mjeditor
Version:
A modern, plugin-extensible rich text editor for React with beautiful custom dialogs, notification system, and comprehensive editing features. Built with Slate.js for maximum flexibility.
1 lines • 209 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../node_modules/style-inject/dist/style-inject.es.js","../src/types/index.ts","../src/core/Toolbar.tsx","../src/core/Editor.tsx","../src/plugins/bold.ts","../src/plugins/italic.ts","../src/plugins/underline.ts","../src/plugins/strikethrough.ts","../src/plugins/font.ts","../src/plugins/color.ts","../src/plugins/list.ts","../src/plugins/image.tsx","../src/plugins/link.tsx","../src/plugins/table.tsx","../src/plugins/codeBlock.ts","../src/plugins/horizontalRule.ts","../src/plugins/emoji.tsx","../src/plugins/checklist.ts","../src/plugins/heading.tsx","../src/utils/serializer.ts","../src/utils/deserializer.ts","../src/utils/markdown.ts","../src/utils/markdownDeserializer.ts","../src/utils/notification.ts","../src/components/Dialog.tsx","../src/components/TableDialog.tsx","../src/components/ImageDialog.tsx","../src/components/LinkDialog.tsx","../src/components/EmojiDialog.tsx","../src/components/HeadingDialog.tsx","../src/components/Notification.tsx","../src/components/NotificationProvider.tsx"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n","import { ReactNode } from 'react';\nimport { BaseEditor, BaseRange, BaseSelection, BaseElement, BaseText, Descendant } from 'slate';\nimport { ReactEditor } from 'slate-react';\nimport { HistoryEditor } from 'slate-history';\n\n// Extend Slate's types to include our custom properties\ndeclare module 'slate' {\n interface CustomTypes {\n Editor: CustomEditor;\n Element: CustomElement;\n Text: CustomText;\n }\n}\n\n// Slate Editor Types\nexport type CustomEditor = BaseEditor & ReactEditor & HistoryEditor;\n\n// Element Types\nexport type CustomElement = \n | ParagraphElement\n | HeadingElement\n | ListItemElement\n | BulletedListElement\n | NumberedListElement\n | ImageElement\n | TableElement\n | TableRowElement\n | TableCellElement\n | CodeBlockElement\n | HorizontalRuleElement\n | ChecklistElement;\n\n// Text Types\nexport type CustomText = BaseText & {\n bold?: boolean;\n italic?: boolean;\n underline?: boolean;\n strikethrough?: boolean;\n color?: string;\n backgroundColor?: string;\n fontSize?: number;\n fontFamily?: string;\n superscript?: boolean;\n subscript?: boolean;\n link?: string;\n};\n\n// Element Definitions\nexport interface ParagraphElement extends BaseElement {\n type: 'paragraph';\n align?: 'left' | 'center' | 'right' | 'justify';\n indent?: number;\n children: CustomText[];\n}\n\nexport interface HeadingElement extends BaseElement {\n type: 'heading';\n level: 1 | 2 | 3 | 4 | 5 | 6;\n align?: 'left' | 'center' | 'right' | 'justify';\n children: CustomText[];\n}\n\nexport interface ListItemElement extends BaseElement {\n type: 'list-item';\n children: CustomText[];\n}\n\nexport interface BulletedListElement extends BaseElement {\n type: 'bulleted-list';\n children: ListItemElement[];\n}\n\nexport interface NumberedListElement extends BaseElement {\n type: 'numbered-list';\n children: ListItemElement[];\n}\n\nexport interface ImageElement extends BaseElement {\n type: 'image';\n url: string;\n alt?: string;\n caption?: string;\n align?: 'left' | 'center' | 'right';\n width?: number;\n height?: number;\n children: CustomText[];\n}\n\nexport interface TableElement extends BaseElement {\n type: 'table';\n rows: number;\n cols: number;\n children: TableRowElement[];\n}\n\nexport interface TableRowElement extends BaseElement {\n type: 'table-row';\n children: TableCellElement[];\n}\n\nexport interface TableCellElement extends BaseElement {\n type: 'table-cell';\n header?: boolean;\n align?: 'left' | 'center' | 'right';\n children: CustomText[];\n}\n\nexport interface CodeBlockElement extends BaseElement {\n type: 'code-block';\n language?: string;\n children: CustomText[];\n}\n\nexport interface HorizontalRuleElement extends BaseElement {\n type: 'horizontal-rule';\n style?: 'solid' | 'dashed' | 'dotted';\n thickness?: number;\n color?: string;\n children: CustomText[];\n}\n\nexport interface ChecklistElement extends BaseElement {\n type: 'checklist';\n checked: boolean;\n children: CustomText[];\n}\n\n// Plugin Types\nexport interface Plugin {\n name: string;\n renderElement?: (props: any) => ReactNode | undefined;\n renderLeaf?: (props: any) => ReactNode | undefined;\n onKeyDown?: (event: KeyboardEvent, editor: CustomEditor) => boolean;\n toolbarButton?: () => ReactNode;\n commands?: Record<string, (editor: CustomEditor, ...args: any[]) => void>;\n}\n\n// Editor Props\nexport interface MJEditorProps {\n value?: CustomElement[];\n onChange?: (value: CustomElement[]) => void;\n placeholder?: string;\n readOnly?: boolean;\n plugins?: Plugin[];\n theme?: 'light' | 'dark';\n className?: string;\n style?: React.CSSProperties;\n autoFocus?: boolean;\n autoSave?: boolean;\n autoSaveInterval?: number;\n onSave?: (value: CustomElement[]) => void;\n toolbarConfig?: ToolbarConfig;\n maxLength?: number;\n minHeight?: string;\n maxHeight?: string;\n}\n\n// Toolbar Configuration\nexport interface ToolbarConfig {\n showTextFormatting?: boolean;\n showFontControls?: boolean;\n showTextStyling?: boolean;\n showTextAlignment?: boolean;\n showIndentation?: boolean;\n showLists?: boolean;\n showTextPositioning?: boolean;\n showInsertOptions?: boolean;\n showAdvancedFeatures?: boolean;\n showUndoRedo?: boolean;\n disabledTools?: string[];\n customButtons?: ReactNode[];\n}\n\n// Color Options\nexport interface ColorOption {\n name: string;\n value: string;\n label: string;\n}\n\n// Font Options\nexport interface FontOption {\n name: string;\n value: string;\n label: string;\n}\n\n// Size Options\nexport interface SizeOption {\n name: string;\n value: number;\n label: string;\n}\n\n// Export Types\nexport interface ExportOptions {\n format: 'html' | 'markdown' | 'plain-text' | 'json';\n includeStyles?: boolean;\n preserveFormatting?: boolean;\n}\n\n// History Types\nexport interface HistoryState {\n undos: any[];\n redos: any[];\n}\n\n// Selection Types\nexport type CustomSelection = BaseSelection;\nexport type CustomRange = BaseRange;\n\n// Utility Types\nexport type ElementType = CustomElement['type'];\nexport type MarkType = keyof Omit<CustomText, 'text'>;\n\n// Event Types\nexport interface EditorEvent {\n type: string;\n data?: any;\n timestamp: number;\n}\n\n// Configuration Types\nexport interface EditorConfig {\n plugins: Plugin[];\n theme: 'light' | 'dark';\n toolbar: ToolbarConfig;\n autoSave: boolean;\n autoSaveInterval: number;\n maxLength?: number;\n placeholder?: string;\n}\n\n// Default Values\nexport const DEFAULT_FONTS: FontOption[] = [\n { name: 'Lato Regular', value: 'Lato, sans-serif', label: 'Lato Regular' },\n { name: 'Arial', value: 'Arial, sans-serif', label: 'Arial' },\n { name: 'Times New Roman', value: 'Times New Roman, serif', label: 'Times New Roman' },\n { name: 'Helvetica', value: 'Helvetica, sans-serif', label: 'Helvetica' },\n { name: 'Georgia', value: 'Georgia, serif', label: 'Georgia' },\n { name: 'Verdana', value: 'Verdana, sans-serif', label: 'Verdana' },\n { name: 'Courier New', value: 'Courier New, monospace', label: 'Courier New' }\n];\n\nexport const DEFAULT_SIZES: SizeOption[] = [\n { name: '8pt', value: 8, label: '8' },\n { name: '9pt', value: 9, label: '9' },\n { name: '10pt', value: 10, label: '10' },\n { name: '11pt', value: 11, label: '11' },\n { name: '12pt', value: 12, label: '12' },\n { name: '14pt', value: 14, label: '14' },\n { name: '16pt', value: 16, label: '16' },\n { name: '18pt', value: 18, label: '18' },\n { name: '20pt', value: 20, label: '20' },\n { name: '22pt', value: 22, label: '22' },\n { name: '24pt', value: 24, label: '24' },\n { name: '26pt', value: 26, label: '26' },\n { name: '28pt', value: 28, label: '28' },\n { name: '36pt', value: 36, label: '36' },\n { name: '48pt', value: 48, label: '48' },\n { name: '72pt', value: 72, label: '72' }\n];\n\nexport const DEFAULT_COLORS: ColorOption[] = [\n { name: 'Black', value: '#000000', label: 'Black' },\n { name: 'Red', value: '#FF0000', label: 'Red' },\n { name: 'Green', value: '#00FF00', label: 'Green' },\n { name: 'Blue', value: '#0000FF', label: 'Blue' },\n { name: 'Yellow', value: '#FFFF00', label: 'Yellow' },\n { name: 'Orange', value: '#FFA500', label: 'Orange' },\n { name: 'Purple', value: '#800080', label: 'Purple' },\n { name: 'Pink', value: '#FFC0CB', label: 'Pink' },\n { name: 'Brown', value: '#A52A2A', label: 'Brown' },\n { name: 'Gray', value: '#808080', label: 'Gray' }\n];\n\nexport const DEFAULT_HIGHLIGHT_COLORS: ColorOption[] = [\n { name: 'Yellow', value: '#FFFF00', label: 'Yellow' },\n { name: 'Green', value: '#90EE90', label: 'Green' },\n { name: 'Blue', value: '#87CEEB', label: 'Blue' },\n { name: 'Pink', value: '#FFB6C1', label: 'Pink' },\n { name: 'Orange', value: '#FFD700', label: 'Orange' },\n { name: 'Purple', value: '#DDA0DD', label: 'Purple' },\n { name: 'Red', value: '#FFB6C1', label: 'Red' },\n { name: 'Gray', value: '#D3D3D3', label: 'Gray' }\n]; ","import React, { useState, useCallback, useEffect, useRef } from 'react';\nimport { useSlate } from 'slate-react';\nimport { Editor, Transforms, Element as SlateElement } from 'slate';\nimport { \n ToolbarConfig, \n CustomEditor, \n CustomElement,\n CustomText,\n DEFAULT_FONTS,\n DEFAULT_SIZES,\n DEFAULT_COLORS,\n DEFAULT_HIGHLIGHT_COLORS\n} from '../types';\nimport { ImageDialog, LinkDialog, EmojiDialog } from '../components';\n\n// Premium Icons Component\nconst Icons = {\n Bold: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12.499,4.00006 C15.3222,4.00006 17.25,6.34 17.25,8.75006 C17.25,9.68113 16.9624,10.602 16.4465,11.3815 C17.3691,12.2394 18,13.4704 18,15.0001 C18,18.1334 15.3234,20.0001 13,20.0001 L7.75,20.0001 C6.7835,20.0001 6,19.2166 6,18.2501 L6,5.75006 C6,4.78356 6.7835,4.00006 7.75,4.00006 L12.499,4.00006 Z M13,13.5 L9.5,13.5 L9.5,16.5001 L13,16.5001 C13.3118,16.5001 13.7105,16.3622 14.0242,16.0787 C14.3122,15.8183 14.5,15.4643 14.5,15.0001 C14.5,14.1124 13.7547,13.5 13,13.5 Z M12.499,7.50006 L9.5,7.50006 L9.5,10 L12.5,10 C13.215,10 13.75,9.4079 13.75,8.75006 C13.75,8.09326 13.2143,7.50006 12.499,7.50006 Z\"/>\n </svg>\n ),\n Italic: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 256 256\" fill=\"currentColor\">\n <path d=\"M203.99414,55.99512a12,12,0,0,1-12,12H160.64331l-40,120h23.35083a12,12,0,0,1,0,24h-39.918c-.027,0-.05359.00293-.08056.00293-.02649,0-.05323-.00293-.07984-.00293H63.99414a12,12,0,0,1,0-24H95.345l40-120H111.99414a12,12,0,0,1,0-24H152.2002c.01489,0,.02954-.00049.04443,0h39.74951A12,12,0,0,1,203.99414,55.99512Z\"/>\n </svg>\n ),\n Underline: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M6 19H18M8 5V11C8 13.2091 9.79086 15 12 15C14.2091 15 16 13.2091 16 11V5\"/>\n </svg>\n ),\n Strikethrough: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M14.348 12H21v2h-4.613c.24.515.368 1.094.368 1.748 0 1.317-.474 2.355-1.423 3.114-.947.76-2.266 1.138-3.956 1.138-1.557 0-2.934-.293-4.132-.878v-2.874c.985.44 1.818.75 2.5.928.682.18 1.306.27 1.872.27.68 0 1.2-.13 1.562-.39.363-.26.545-.644.545-1.158 0-.285-.08-.54-.24-.763-.16-.222-.394-.437-.704-.643-.18-.12-.483-.287-.88-.49H3v-2H14.347zm-3.528-2c-.073-.077-.143-.155-.193-.235-.126-.202-.19-.44-.19-.713 0-.44.157-.795.47-1.068.313-.273.762-.41 1.348-.41.492 0 .993.064 1.502.19.51.127 1.153.35 1.93.67l1-2.405c-.753-.327-1.473-.58-2.16-.76-.69-.18-1.414-.27-2.173-.27-1.544 0-2.753.37-3.628 1.108-.874.738-1.312 1.753-1.312 3.044 0 .302.036.58.088.848h3.318z\"/>\n </svg>\n ),\n AlignLeft: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M3 4C2.44772 4 2 4.44772 2 5V5.5C2 6.05228 2.44772 6.5 3 6.5H21C21.5523 6.5 22 6.05228 22 5.5V5C22 4.44772 21.5523 4 21 4H3Z\"/>\n <path d=\"M3 13C2.44772 13 2 13.4477 2 14V14.5C2 15.0523 2.44772 15.5 3 15.5H21C21.5523 15.5 22 15.0523 22 14.5V14C22 13.4477 21.5523 13 21 13H3Z\"/>\n <path d=\"M2 9.5C2 8.94772 2.44772 8.5 3 8.5H15C15.5523 8.5 16 8.94772 16 9.5V10C16 10.5523 15.5523 11 15 11H3C2.44772 11 2 10.5523 2 10V9.5Z\"/>\n <path d=\"M3 17.5C2.44772 17.5 2 17.9477 2 18.5V19C2 19.5523 2.44772 20 3 20H15C15.5523 20 16 19.5523 16 19V18.5C16 17.9477 15.5523 17.5 15 17.5H3Z\"/>\n </svg>\n ),\n AlignCenter: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <line x1=\"5\" y1=\"6\" x2=\"19\" y2=\"6\"/>\n <line x1=\"5\" y1=\"14\" x2=\"19\" y2=\"14\"/>\n <line x1=\"8\" y1=\"10\" x2=\"16\" y2=\"10\"/>\n <line x1=\"8\" y1=\"18\" x2=\"16\" y2=\"18\"/>\n </svg>\n ),\n AlignRight: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 256 256\" fill=\"currentColor\">\n <path d=\"M28,68A12.00028,12.00028,0,0,1,40,56H216a12,12,0,0,1,0,24H40A12.00028,12.00028,0,0,1,28,68ZM216,96H88a12,12,0,0,0,0,24H216a12,12,0,0,0,0-24Zm0,40H40.00586a12,12,0,1,0,0,24H216a12,12,0,0,0,0-24Zm0,40H88.00586a12,12,0,1,0,0,24H216a12,12,0,0,0,0-24Z\"/>\n </svg>\n ),\n AlignJustify: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M5 6h14M5 10h14M5 14h14M5 18h14\"/>\n </svg>\n ),\n BulletList: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\">\n <path d=\"M2.5,4.5 C3.32843,4.5 4,3.82843 4,3 C4,2.17157 3.32843,1.5 2.5,1.5 C1.67157,1.5 1,2.17157 1,3 C1,3.82843 1.67157,4.5 2.5,4.5 Z M7,2 C6.44772,2 6,2.44772 6,3 C6,3.55228 6.44772,4 7,4 L14,4 C14.5523,4 15,3.55228 15,3 C15,2.44772 14.5523,2 14,2 L7,2 Z M6,8 C6,7.44772 6.44772,7 7,7 L14,7 C14.5523,7 15,7.44772 15,8 C15,8.55229 14.5523,9 14,9 L7,9 C6.44772,9 6,8.55229 6,8 Z M2.5,9.5 C3.32843,9.5 4,8.82843 4,8 C4,7.17157 3.32843,6.5 2.5,6.5 C1.67157,6.5 1,7.17157 1,8 C1,8.82843 1.67157,9.5 2.5,9.5 Z M6,13 C6,12.4477 6.44772,12 7,12 L14,12 C14.5523,12 15,12.4477 15,13 C15,13.5523 14.5523,14 14,14 L7,14 C6.44772,14 6,13.5523 6,13 Z M2.5,14.5 C3.32843,14.5 4,13.8284 4,13 C4,12.1716 3.32843,11.5 2.5,11.5 C1.67157,11.5 1,12.1716 1,13 C1,13.8284 1.67157,14.5 2.5,14.5 Z\"/>\n </svg>\n ),\n NumberedList: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\">\n <path d=\"M2.75,11 C3.44036,11 4,11.5596 4,12.25 C4,12.5314 3.90701,12.7911 3.75009,13 C3.90701,13.2089 4,13.4686 4,13.75 C4,14.4404 3.44036,15 2.75,15 L1,15 L1,14 L2.75,14 C2.88807,14 3,13.8881 3,13.75 C3,13.6119 2.88807,13.5 2.75,13.5 L2,13.5 L2,12.5 L2.75,12.5 C2.88807,12.5 3,12.3881 3,12.25 C3,12.1119 2.88807,12 2.75,12 L1,12 L1,11 L2.75,11 Z M14,12 C14.5523,12 15,12.4477 15,13 C15,13.5523 14.5523,14 14,14 L7,14 C6.44772,14 6,13.5523 6,13 C6,12.4477 6.44772,12 7,12 L14,12 Z M1.00004,6.00781 L2.63675,6.00781 C3.00139,6.00781 3.35125,6.15193 3.6101,6.40875 C4.18216588,6.97635471 4.14383564,7.89999429 3.55640337,8.42152942 L3.43984,8.51442 L2.75985,9 L3.99995,9 L4.00002,10 L1,10 L1,9.02789 L2.85871,7.70061 C3.0513,7.56309 3.07376,7.28531 2.90577,7.11863 C2.852115,7.0653875 2.7843075,7.0296725 2.71124062,7.01514266 L2.63675,7.00781 L1.00004,7.00781 L1.00004,6.00781 Z M14,7 C14.5523,7 15,7.44772 15,8 C15,8.51283143 14.613973,8.93550653 14.1166239,8.9932722 L14,9 L7,9 C6.44772,9 6,8.55228 6,8 C6,7.48716857 6.38604429,7.06449347 6.88337975,7.0067278 L7,7 L14,7 Z M3,1 L3,4 L4,4 L4,5 L1,5 L1,4 L2,4 L2,2 L1,2 L1,1 L3,1 Z M14,2 C14.5523,2 15,2.44772 15,3 C15,3.51283143 14.613973,3.93550653 14.1166239,3.9932722 L14,4 L7,4 C6.44772,4 6,3.55228 6,3 C6,2.48716857 6.38604429,2.06449347 6.88337975,2.0067278 L7,2 L14,2 Z\"/>\n </svg>\n ),\n Indent: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M20,17.5 C20.8284,17.5 21.5,18.1716 21.5,19 C21.5,19.7796706 20.9050879,20.4204457 20.1444558,20.4931332 L20,20.5 L4,20.5 C3.17157,20.5 2.5,19.8284 2.5,19 C2.5,18.2203294 3.09488554,17.5795543 3.85553954,17.5068668 L4,17.5 L20,17.5 Z M20,12.5 C20.8284,12.5 21.5,13.1716 21.5,14 C21.5,14.8284 20.8284,15.5 20,15.5 L11,15.5 C10.1716,15.5 9.5,14.8284 9.5,14 C9.5,13.1716 10.1716,12.5 11,12.5 L20,12.5 Z M3.64296,9.17291 L3.93311976,9.32458624 L3.93311976,9.32458624 L4.27740768,9.51291912 C4.3390124,9.547389 4.402657,9.583406 4.46823375,9.62098 L4.88401821,9.86518704 L4.88401821,9.86518704 L5.34100323,10.1472363 C5.4203135,10.1974244 5.501125,10.249209 5.58333,10.3026 C5.839569,10.46904 6.0770223,10.630089 6.2942886,10.7824206 L6.70149,11.074475 C6.7647325,11.120885 6.8256282,11.16608 6.8841252,11.2099368 L7.344432,11.5651902 C7.3880697,11.599909 7.429153,11.63292 7.46763,11.6641 C7.6824,11.8382 7.67298,12.1852 7.44716,12.3674 L7.1866432,12.5735648 L7.1866432,12.5735648 L6.8680296,12.8162144 L6.8680296,12.8162144 L6.4932104,13.0901216 L6.4932104,13.0901216 L6.0640768,13.3900592 C5.9881334,13.4419276 5.9100057,13.4946628 5.8297331,13.5481559 L5.58252,13.7108 C5.41359,13.82052 5.2508624,13.923344 5.09516256,14.0193992 L4.64997168,14.2873856 L4.64997168,14.2873856 L4.25189952,14.5155224 L4.25189952,14.5155224 L3.90589824,14.7045728 L3.90589824,14.7045728 L3.61692,14.8553 L3.61692,14.8553 C3.36348,14.9844 3.09132,14.8195 3.0649,14.5229 L3.02512622,14.0126967 L3.02512622,14.0126967 L2.98895109,13.3626736 L2.98895109,13.3626736 L2.97096882,12.8587215 L2.97096882,12.8587215 L2.96078881,12.3032388 L2.96078881,12.3032388 L2.96095553,11.6945042 L2.96095553,11.6945042 L2.97229331,11.1090706 L2.97229331,11.1090706 L2.99214125,10.5819212 L2.99214125,10.5819212 L3.01754879,10.1181043 L3.01754879,10.1181043 L3.05963,9.55217 L3.05963,9.55217 C3.08695,9.23553 3.38546,9.04139 3.64296,9.17291 Z M20,7.5 C20.8284,7.5 21.5,8.17157 21.5,9 C21.5,9.82843 20.8284,10.5 20,10.5 L11,10.5 C10.1716,10.5 9.5,9.82843 9.5,9 C9.5,8.17157 10.1716,7.5 11,7.5 L20,7.5 Z M20,2.5 C20.8284,2.5 21.5,3.17157 21.5,4 C21.5,4.82843 20.8284,5.5 20,5.5 L4,5.5 C3.17157,5.5 2.5,4.82843 2.5,4 C2.5,3.17157 3.17157,2.5 4,2.5 L20,2.5 Z\"/>\n </svg>\n ),\n Outdent: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M11 17h10v-2H11v2zm-8-5l4 4V8l-4 4zm0 9h18v-2H3v2zM3 3v2h18V3H3z\"/>\n </svg>\n ),\n Undo: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z\"/>\n </svg>\n ),\n Redo: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M18.4 10.6C16.55 8.99 14.15 8 11.5 8c-4.65 0-8.58 3.03-9.96 7.22L3.9 16c1.05-3.19 4.05-5.5 7.6-5.5 1.95 0 3.73.72 5.12 1.88L13 16h9V7l-3.6 3.6z\"/>\n </svg>\n ),\n Image: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z\"/>\n </svg>\n ),\n Link: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M11,9 L7,9 C5.34314575,9 4,10.3431458 4,12 C4,13.6568542 5.34314575,15 7,15 L11,15 L11,17 L7,17 C4.23857625,17 2,14.7614237 2,12 C2,9.23857625 4.23857625,7 7,7 L11,7 L11,9 Z M13,15 L17,15 C18.6568542,15 20,13.6568542 20,12 C20,10.3431458 18.6568542,9 17,9 L13,9 L13,7 L17,7 C19.7614237,7 22,9.23857625 22,12 C22,14.7614237 19.7614237,17 17,17 L13,17 L13,15 Z M9,11 L15,11 C15.5522847,11 16,11.4477153 16,12 C16,12.5522847 15.5522847,13 15,13 L9,13 C8.44771525,13 8,12.5522847 8,12 C8,11.4477153 8.44771525,11 9,11 Z\"/>\n </svg>\n ),\n Table: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 48 48\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"4\">\n <path d=\"M40.15 5H7.85C6.27599 5 5 6.27599 5 7.85V40.15C5 41.7241 6.27599 43 7.85 43H40.15C41.7241 43 43 41.7241 43 40.15V7.85C43 6.27599 41.7241 5 40.15 5Z\"/>\n <path d=\"M17 5V43\" strokeLinecap=\"round\"/>\n <path d=\"M31 5V43\" strokeLinecap=\"round\"/>\n <path d=\"M5 17H43\" strokeLinecap=\"round\"/>\n <path d=\"M5 31H43\" strokeLinecap=\"round\"/>\n </svg>\n ),\n Code: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M3 6H3.01919M3.01919 6H20.9809M3.01919 6C3 6.31438 3 6.70191 3 7.2002V16.8002C3 17.9203 3 18.4796 3.21799 18.9074C3.40973 19.2837 3.71547 19.5905 4.0918 19.7822C4.51921 20 5.079 20 6.19694 20L17.8031 20C18.921 20 19.48 20 19.9074 19.7822C20.2837 19.5905 20.5905 19.2837 20.7822 18.9074C21 18.48 21 17.921 21 16.8031L21 7.19691C21 6.70021 21 6.31368 20.9809 6M3.01919 6C3.04314 5.60768 3.09697 5.3293 3.21799 5.0918C3.40973 4.71547 3.71547 4.40973 4.0918 4.21799C4.51962 4 5.08009 4 6.2002 4H17.8002C18.9203 4 19.4796 4 19.9074 4.21799C20.2837 4.40973 20.5905 4.71547 20.7822 5.0918C20.9032 5.3293 20.957 5.60768 20.9809 6M20.9809 6H21M14 11L16 13L14 15M10 15L8 13L10 11\"/>\n </svg>\n ),\n HorizontalRule: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M2 12.75a.75.75 0 01.75-.75h18.5a.75.75 0 010 1.5H2.75a.75.75 0 01-.75-.75z\"/>\n </svg>\n ),\n Color: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z\"/>\n </svg>\n ),\n BackgroundColor: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 22C6.49 22 2 17.51 2 12S6.49 2 12 2s10 4.49 10 10-4.49 10-10 10zm-1-17.93c-3.94.49-7 3.85-7 7.93 0 4.42 3.58 8 8 8 3.07 0 5.7-1.73 7.37-4.25.27-.43.09-1.02-.44-1.29-.43-.27-1.02-.09-1.29.44C16.48 17.62 14.44 19 12 19c-3.31 0-6-2.69-6-6 0-2.96 2.16-5.43 5-5.91V4.07z\"/>\n </svg>\n ),\n Emoji: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 18 18\" fill=\"currentColor\">\n <path d=\"M9 2c3.9 0 7 3.1 7 7s-3.1 7-7 7-7-3.1-7-7 3.1-7 7-7m0-2C4 0 0 4 0 9s4 9 9 9 9-4 9-9-4-9-9-9z\"/>\n <circle cx=\"6.5\" cy=\"7.5\" r=\"1.5\"/>\n <path d=\"M6.5 6C5.7 6 5 6.7 5 7.5S5.7 9 6.5 9 8 8.3 8 7.5 7.3 6 6.5 6z\"/>\n <circle cx=\"11.5\" cy=\"7.5\" r=\"1.5\"/>\n <path d=\"M11.5 6c-.8 0-1.5.7-1.5 1.5S10.7 9 11.5 9 13 8.3 13 7.5 12.3 6 11.5 6zM14 11c0 1.6-2.8 3-5 3s-5-1.4-5-3c0-.8 2 1 5 1s5-1.8 5-1z\"/>\n </svg>\n ),\n Checklist: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M7.06089,16.8357 C7.54905,16.3476 8.3405,16.3476 8.82866,16.8357 C9.28426667,17.2913533 9.31464044,18.0111276 8.91978133,18.501997 L8.82866,18.6035 L6.53056,20.9016 C5.98138187,21.4507875 5.11232582,21.4851117 4.52316295,21.0045727 L4.40924,20.9016 L3.17178,19.6641 C2.68363,19.176 2.68363,18.3845 3.17178,17.8964 C3.627396,17.4407467 4.34721129,17.4103698 4.83805386,17.8052693 L4.93955,17.8964 L5.4699,18.4267 L7.06089,16.8357 Z M20.0002,17.4999 C20.8286,17.4999 21.5002,18.1715 21.5002,18.9999 C21.5002,19.8284 20.8286,20.4999 20.0002,20.4999 L12.0002,20.4999 C11.1718,20.4999 10.5002,19.8284 10.5002,18.9999 C10.5002,18.1715 11.1718,17.4999 12.0002,17.4999 L20.0002,17.4999 Z M8.82866,9.83572 C9.31681,10.3239 9.31681,11.1153 8.82866,11.6035 L6.53056,13.9016 C5.94477,14.4874 4.99503,14.4874 4.40924,13.9016 L3.17178,12.6641 C2.68363,12.176 2.68363,11.3845 3.17178,10.8964 C3.65994,10.4082 4.45139,10.4082 4.93955,10.8964 L5.4699,11.4267 L7.06089,9.83572 C7.54905,9.34757 8.3405,9.34757 8.82866,9.83572 Z M20.0002,10.4999 C20.8286,10.4999 21.5002,11.1715 21.5002,11.9999 C21.5002,12.7796647 20.9052879,13.4203567 20.1446558,13.4930341 L20.0002,13.4999 L12.0002,13.4999 C11.1718,13.4999 10.5002,12.8284 10.5002,11.9999 C10.5002,11.2202294 11.0951121,10.5794543 11.8557442,10.5067668 L12.0002,10.4999 L20.0002,10.4999 Z M7.06089,2.83572 C7.54905,2.34757 8.3405,2.34757 8.82866,2.83572 C9.28426667,3.291336 9.31464044,4.01115129 8.91978133,4.50199386 L8.82866,4.60349 L6.53056,6.90159 C5.98138187,7.45075875 5.11232582,7.4850818 4.52316295,7.00455914 L4.40924,6.90159 L3.17178,5.66413 C2.68363,5.17597 2.68363,4.38451 3.17178,3.89636 C3.627396,3.440744 4.34721129,3.4103696 4.83805386,3.8052368 L4.93955,3.89636 L5.4699,4.42671 L7.06089,2.83572 Z M20.0002,3.49995 C20.8286,3.49995 21.5002,4.17152 21.5002,4.99995 C21.5002,5.77964882 20.9052879,6.420399 20.1446558,6.49308345 L20.0002,6.49995 L12.0002,6.49995 C11.1718,6.49995 10.5002,5.82838 10.5002,4.99995 C10.5002,4.22025118 11.0951121,3.579501 11.8557442,3.50681655 L12.0002,3.49995 L20.0002,3.49995 Z\"/>\n </svg>\n ),\n Quote: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z\"/>\n </svg>\n ),\n Superscript: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M22 7h-2v1h3v1h-4V7c0-.55.45-1 1-1h2V5h-3V4h3c.55 0 1 .45 1 1v1c0 .55-.45 1-1 1zM5.88 20h2.53l1.28-3.88H13.1l1.28 3.88h2.53L15.6 4.12h-2.34L5.88 20zm6.33-5.88L12.5 11.5h-.05l-.71 2.62h2.47z\"/>\n </svg>\n ),\n More: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M7 14C8.10457 14 9 13.1046 9 12C9 10.8954 8.10457 10 7 10C5.89543 10 5 10.8954 5 12C5 13.1046 5.89543 14 7 14Z\"/>\n <path d=\"M14 12C14 13.1046 13.1046 14 12 14C10.8954 14 10 13.1046 10 12C10 10.8954 10.8954 10 12 10C13.1046 10 14 10.8954 14 12Z\"/>\n <path d=\"M17 14C18.1046 14 19 13.1046 19 12C19 10.8954 18.1046 10 17 10C15.8954 10 15 10.8954 15 12C15 13.1046 15.8954 14 17 14Z\"/>\n </svg>\n ),\n ClearFormat: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M3.27 3L2 4.27l5 5V13h3v9l3.58-6.14L17.73 20 19 18.73 3.27 3zM17 10h-4l4-8H7v2.18l8.46 8.46L17 10z\"/>\n </svg>\n )\n};\n\n// Premium Toolbar Button Component\ninterface ToolbarButtonProps {\n icon: React.ReactNode;\n label: string;\n isActive?: boolean;\n onClick: () => void;\n disabled?: boolean;\n className?: string;\n hasDropdown?: boolean;\n}\n\nconst ToolbarButton: React.FC<ToolbarButtonProps> = ({\n icon,\n label,\n isActive = false,\n onClick,\n disabled = false,\n className = '',\n hasDropdown = false\n}) => (\n <button\n type=\"button\"\n className={`mj-toolbar-button ${isActive ? 'active' : ''} ${className}`}\n onClick={onClick}\n disabled={disabled}\n title={label}\n aria-label={label}\n >\n <span className=\"button-icon\">{icon}</span>\n {hasDropdown && <span className=\"dropdown-arrow\">▼</span>}\n </button>\n);\n\n// Premium Dropdown Component\ninterface DropdownProps {\n label: string;\n value: string;\n options: Array<{ label: string; value: string }>;\n onChange: (value: string) => void;\n className?: string;\n icon?: React.ReactNode;\n}\n\nconst Dropdown: React.FC<DropdownProps> = ({\n label,\n value,\n options,\n onChange,\n className = '',\n icon\n}) => (\n <div className={`mj-toolbar-dropdown-container ${className}`}>\n <select\n className=\"mj-toolbar-dropdown\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n title={label}\n aria-label={label}\n >\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n {icon && <span className=\"dropdown-icon\">{icon}</span>}\n <span className=\"dropdown-arrow\">▼</span>\n </div>\n);\n\n// Premium Color Picker Component\ninterface ColorPickerProps {\n label: string;\n value: string;\n colors: Array<{ name: string; value: string; label: string }>;\n onChange: (color: string) => void;\n className?: string;\n icon?: React.ReactNode;\n}\n\nconst ColorPicker: React.FC<ColorPickerProps> = ({\n label,\n value,\n colors,\n onChange,\n className = '',\n icon\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const pickerRef = useRef<HTMLDivElement>(null);\n\n // Close picker when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (pickerRef.current && !pickerRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [isOpen]);\n\n return (\n <div className={`mj-color-picker ${className}`} ref={pickerRef}>\n <button\n type=\"button\"\n className=\"mj-color-picker-button\"\n onClick={() => setIsOpen(!isOpen)}\n title={label}\n aria-label={label}\n >\n {icon && <span className=\"color-icon\">{icon}</span>}\n <span className=\"dropdown-arrow\">▼</span>\n </button>\n {isOpen && (\n <div className=\"mj-color-picker-dropdown\">\n {colors.map((color) => (\n <button\n key={color.value}\n type=\"button\"\n className=\"mj-color-option\"\n style={{ backgroundColor: color.value }}\n onClick={() => {\n onChange(color.value);\n setIsOpen(false);\n }}\n title={color.label}\n aria-label={color.label}\n />\n ))}\n </div>\n )}\n </div>\n );\n};\n\n// Premium Toolbar Component\ninterface ToolbarProps {\n config?: ToolbarConfig;\n className?: string;\n}\n\nexport const Toolbar: React.FC<ToolbarProps> = ({\n config = {},\n className = ''\n}) => {\n const [showTableModal, setShowTableModal] = useState(false);\n const [tableRows, setTableRows] = useState('3');\n const [tableCols, setTableCols] = useState('3');\n const [showImageDialog, setShowImageDialog] = useState(false);\n const [showLinkDialog, setShowLinkDialog] = useState(false);\n const [showEmojiDialog, setShowEmojiDialog] = useState(false);\n const editor = useSlate();\n\n // Helper functions\n const isMarkActive = (format: string) => {\n const marks = Editor.marks(editor);\n return marks ? (marks as any)[format] === true : false;\n };\n\n const toggleMark = (format: string) => {\n const isActive = isMarkActive(format);\n if (isActive) {\n Editor.removeMark(editor, format);\n } else {\n Editor.addMark(editor, format, true);\n }\n };\n\n const isBlockActive = (format: string) => {\n const { selection } = editor;\n if (!selection) return false;\n\n const [match] = Array.from(\n Editor.nodes(editor, {\n at: Editor.unhangRange(editor, selection),\n match: (n) =>\n !Editor.isEditor(n) &&\n SlateElement.isElement(n) &&\n (n as any).type === format,\n })\n );\n\n return !!match;\n };\n\n const toggleBlock = (format: string) => {\n const isActive = isBlockActive(format);\n if (isActive) {\n Transforms.unwrapNodes(editor, {\n match: (n) =>\n !Editor.isEditor(n) &&\n SlateElement.isElement(n) &&\n (n as any).type === format,\n });\n } else {\n const newProperties: Partial<CustomElement> = { type: format as any };\n Transforms.setNodes<CustomElement>(editor, newProperties);\n }\n };\n\n // Undo/Redo functions\n const canUndo = () => {\n return editor.history.undos.length > 0;\n };\n\n const canRedo = () => {\n return editor.history.redos.length > 0;\n };\n\n const handleUndo = () => {\n if (canUndo()) {\n editor.undo();\n }\n };\n\n const handleRedo = () => {\n if (canRedo()) {\n editor.redo();\n }\n };\n\n // Check if a tool is enabled\n const isToolEnabled = (toolName: string) => {\n const enabled = config.disabledTools ? !config.disabledTools.includes(toolName) : true;\n console.log(`Tool ${toolName} enabled:`, enabled, 'disabledTools:', config.disabledTools);\n return enabled;\n };\n\n // Undo/Redo Section\n const renderUndoRedo = () => {\n if (!config.showUndoRedo) return null;\n\n return (\n <div className=\"mj-toolbar-section\">\n {isToolEnabled('undo') && (\n <ToolbarButton\n icon={<Icons.Undo />}\n label=\"Undo\"\n onClick={handleUndo}\n disabled={!canUndo()}\n />\n )}\n {isToolEnabled('redo') && (\n <ToolbarButton\n icon={<Icons.Redo />}\n label=\"Redo\"\n onClick={handleRedo}\n disabled={!canRedo()}\n />\n )}\n </div>\n );\n };\n\n // Text Formatting Section\n const renderTextFormatting = () => {\n if (!config.showTextFormatting) return null;\n\n return (\n <div className=\"mj-toolbar-section\">\n {isToolEnabled('bold') && (\n <ToolbarButton\n icon={<Icons.Bold />}\n label=\"Bold\"\n isActive={isMarkActive('bold')}\n onClick={() => toggleMark('bold')}\n />\n )}\n {isToolEnabled('italic') && (\n <ToolbarButton\n icon={<Icons.Italic />}\n label=\"Italic\"\n isActive={isMarkActive('italic')}\n onClick={() => toggleMark('italic')}\n />\n )}\n {isToolEnabled('underline') && (\n <ToolbarButton\n icon={<Icons.Underline />}\n label=\"Underline\"\n isActive={isMarkActive('underline')}\n onClick={() => toggleMark('underline')}\n />\n )}\n {isToolEnabled('strikethrough') && (\n <ToolbarButton\n icon={<Icons.Strikethrough />}\n label=\"Strikethrough\"\n isActive={isMarkActive('strikethrough')}\n onClick={() => toggleMark('strikethrough')}\n />\n )}\n </div>\n );\n };\n\n // Font Controls Section\n const renderFontControls = () => {\n if (!config.showFontControls) return null;\n\n const currentFont = Editor.marks(editor)?.fontFamily || 'Lato';\n const currentSize = Editor.marks(editor)?.fontSize || 12;\n const currentColor = Editor.marks(editor)?.color || '#000000';\n const currentBgColor = Editor.marks(editor)?.backgroundColor || 'transparent';\n\n return (\n <div className=\"mj-toolbar-section\">\n <Dropdown\n label=\"Font Family\"\n value={currentFont}\n options={DEFAULT_FONTS}\n onChange={(font) => Editor.addMark(editor, 'fontFamily', font)}\n icon=\"A\"\n />\n <Dropdown\n label=\"Font Size\"\n value={currentSize.toString()}\n options={DEFAULT_SIZES.map(size => ({ label: size.name, value: size.value.toString() }))}\n onChange={(size) => Editor.addMark(editor, 'fontSize', parseInt(size))}\n icon=\"12\"\n />\n <ColorPicker\n label=\"Text Color\"\n value={currentColor}\n colors={DEFAULT_COLORS}\n onChange={(color) => Editor.addMark(editor, 'color', color)}\n icon={<Icons.Color />}\n />\n <ColorPicker\n label=\"Background Color\"\n value={currentBgColor}\n colors={DEFAULT_HIGHLIGHT_COLORS}\n onChange={(color) => Editor.addMark(editor, 'backgroundColor', color)}\n icon={<Icons.BackgroundColor />}\n />\n </div>\n );\n };\n\n // Text Alignment Section\n const renderTextAlignment = () => {\n if (!config.showTextAlignment) return null;\n\n const getCurrentAlignment = () => {\n const { selection } = editor;\n if (!selection) return 'left';\n\n const [match] = Array.from(\n Editor.nodes(editor, {\n at: Editor.unhangRange(editor, selection),\n match: (n) =>\n !Editor.isEditor(n) && SlateElement.isElement(n) && ['paragraph', 'heading'].includes((n as any).type),\n })\n );\n\n return match ? (match[0] as any).align || 'left' : 'left';\n };\n\n const setAlignment = (align: string) => {\n const { selection } = editor;\n if (!selection) return;\n\n const [match] = Array.from(\n Editor.nodes(editor, {\n at: Editor.unhangRange(editor, selection),\n match: (n) =>\n !Editor.isEditor(n) && SlateElement.isElement(n) && ['paragraph', 'heading'].includes((n as any).type),\n })\n );\n\n if (match) {\n Transforms.setNodes(\n editor,\n { align: align as any },\n { at: match[1] }\n );\n }\n };\n\n const currentAlign = getCurrentAlignment();\n\n return (\n <div className=\"mj-toolbar-section\">\n {isToolEnabled('alignLeft') && (\n <ToolbarButton\n icon={<Icons.AlignLeft />}\n label=\"Align Left\"\n isActive={currentAlign === 'left'}\n onClick={() => setAlignment('left')}\n />\n )}\n {isToolEnabled('alignCenter') && (\n <ToolbarButton\n icon={<Icons.AlignCenter />}\n label=\"Align Center\"\n isActive={currentAlign === 'center'}\n onClick={() => setAlignment('center')}\n />\n )}\n {isToolEnabled('alignRight') && (\n <ToolbarButton\n icon={<Icons.AlignRight />}\n label=\"Align Right\"\n isActive={currentAlign === 'right'}\n onClick={() => setAlignment('right')}\n />\n )}\n {isToolEnabled('alignJustify') && (\n <ToolbarButton\n icon={<Icons.AlignJustify />}\n label=\"Justify\"\n isActive={currentAlign === 'justify'}\n onClick={() => setAlignment('justify')}\n />\n )}\n </div>\n );\n };\n\n // Lists Section\n const renderLists = () => {\n if (!config.showLists) return null;\n\n return (\n <div className=\"mj-toolbar-section\">\n {isToolEnabled('bulletedList') && (\n <ToolbarButton\n icon={<Icons.BulletList />}\n label=\"Bulleted List\"\n isActive={isBlockActive('bulleted-list')}\n onClick={() => toggleBlock('bulleted-list')}\n />\n )}\n {isToolEnabled('numberedList') && (\n <ToolbarButton\n icon={<Icons.NumberedList />}\n label=\"Numbered List\"\n isActive={isBlockActive('numbered-list')}\n onClick={() => toggleBlock('numbered-list')}\n />\n )}\n </div>\n );\n };\n\n // Indentation Section\n const renderIndentation = () => {\n if (!config.showIndentation) return null;\n\n const increaseIndent = () => {\n const { selection } = editor;\n if (!selection) return;\n\n const [match] = Array.from(\n Editor.nodes(editor, {\n at: Editor.unhangRange(editor, selection),\n match: (n) =>\n !Editor.isEditor(n) && SlateElement.isElement(n) && ['paragraph', 'heading'].includes((n as any).type),\n })\n );\n\n if (match) {\n const currentIndent = (match[0] as any).indent || 0;\n Transforms.setNodes(\n editor,\n { indent: Math.min(currentIndent + 1, 5) },\n { at: match[1] }\n );\n }\n };\n\n return (\n <div className=\"mj-toolbar-section\">\n {isToolEnabled('indent') && (\n <ToolbarButton\n icon={<Icons.Indent />}\n label=\"Increase Indent\"\n onClick={increaseIndent}\n />\n )}\n {isToolEnabled('outdent') && (\n <ToolbarButton\n icon={<Icons.Outdent />}\n label=\"Decrease Indent\"\n onClick={() => {\n const { selection } = editor;\n if (!selection) return;\n\n const [match] = Array.from(\n Editor.nodes(editor, {\n at: Editor.unhangRange(editor, selection),\n match: (n) =>\n !Editor.isEditor(n) && SlateElement.isElement(n) && ['paragraph', 'heading'].includes((n as any).type),\n })\n );\n\n if (match) {\n const currentIndent = (match[0] as any).indent || 0;\n Transforms.setNodes(\n editor,\n { indent: Math.max(currentIndent - 1, 0) },\n { at: match[1] }\n );\n }\n }}\n />\n )}\n </div>\n );\n };\n\n // Text Positioning Section\n const renderTextPositioning = () => {\n if (!config.showTextPositioning) return null;\n\n return (\n <div className=\"mj-toolbar-section\">\n <ToolbarButton\n icon={<Icons.Superscript />}\n label=\"Superscript\"\n onClick={() => console.log('Superscript')}\n />\n </div>\n );\n };\n\n // Insert Options Section\n const renderInsertOptions = () => {\n if (!config.showInsertOptions) return null;\n\n return (\n <div className=\"mj-toolbar-section\">\n {isToolEnabled('image') && (\n <ToolbarButton\n icon={<Icons.Image />}\n label=\"Insert Image\"\n onClick={() => setShowImageDialog(true)}\n />\n )}\n {isToolEnabled('link') && (\n <ToolbarButton\n icon={<Icons.Link />}\n label=\"Insert Link\"\n onClick={() => setShowLinkDialog(true)}\n />\n )}\n </div>\n );\n };\n\n // Code and Special Features Section\n const renderCodeFeatures = () => {\n return (\n <div className=\"mj-toolbar-section\">\n {isToolEnabled('codeBlock') && (\n <ToolbarButton\n icon={<Icons.Code />}\n label=\"Code Block\"\n isActive={isBlockActive('code-block')}\n onClick={() => toggleBlock('code-block')}\n />\n )}\n {isToolEnabled('quote') && (\n <ToolbarButton\n icon={<Icons.Quote />}\n label=\"Quote\"\n isActive={isBlockActive('blockquote')}\n onClick={() => toggleBlock('blockquote')}\n />\n )}\n {isToolEnabled('clearFormat') && (\n <ToolbarButton\n icon={<Icons.ClearFormat />}\n label=\"Clear Formatting\"\n onClick={() => {\n const { selection } = editor;\n if (selection) {\n Transforms.unsetNodes(editor, ['bold', 'italic', 'underline', 'strikethrough', 'color', 'backgroundColor', 'fontFamily', 'fontSize'], { at: selection });\n // Clear marks by setting them to undefined\n ['bold', 'italic', 'underline', 'strikethrough', 'color', 'backgroundColor', 'fontFamily', 'fontSize'].forEach(mark => {\n Editor.removeMark(editor, mark);\n });\n }\n }}\n />\n )}\n {isToolEnabled('horizontalRule') && (\n <ToolbarButton\n icon={<Icons.HorizontalRule />}\n label=\"Horizontal Rule\"\n onClick={() => toggleBlock('horizontal-rule')}\n />\n )}\n </div>\n );\n };\n\n // Tables and Advanced Features Section\n const renderTablesAndAdvanced = () => {\n return (\n <div className=\"mj-toolbar-section\">\n {isToolEnabled('table') && (\n <ToolbarButton\n icon={<Icons.Table />}\n label=\"Insert Table\"\n onClick={() => setShowTableModal(true)}\n />\n )}\n {isToolEnabled('emoji') && (\n <ToolbarButton\n icon={<Icons.Emoji />}\n label=\"Insert Emoji\"\n onClick={() => setShowEmojiDialog(true)}\n />\n )}\n {isToolEnabled('checklist') && (\n <ToolbarButton\n icon={<Icons.Checklist />}\n label=\"Checklist\"\n isActive={isBlockActive('checklist')}\n onClick={() => toggleBlock('checklist')}\n />\n )}\n <ToolbarButton\n icon={<Icons.More />}\n label=\"More Options\"\n onClick={() => console.log('More options')}\n hasDropdown\n />\n </div>\n );\n };\n\n return (\n <div className={`mj-toolbar ${className}`}>\n {renderUndoRedo()}\n {renderTextFormatting()}\n {renderFontControls()}\n {renderTextAlignment()}\n {renderLists()}\n {renderIndentation()}\n {renderTextPositioning()}\n {renderInsertOptions()}\n {renderCodeFeatures()}\n {renderTablesAndAdvanced()}\n {config.customButtons && (\n <div className=\"mj-toolbar-section\">\n {config.customButtons}\n </div>\n )}\n \n {/* Table Insertion Modal */}\n {showTableModal && (\n <div className=\"mj-table-modal-overlay\" onClick={() => setShowTableModal(false)}>\n <div className=\"mj-table-modal\" onClick={(e) => e.stopPropagation()}>\n <div className=\"mj-table-modal-header\">\n <h3>Insert Table</h3>\n <button \n className=\"mj-table-modal-close\"\n onClick={() => setShowTableModal(false)}\n >\n ×\n </button>\n </div>\n <div className=\"mj-table-modal-content\">\n <div className=\"mj-table-input-group\">\n <label htmlFor=\"table-rows\">Rows:</label>\n <input\n id=\"table-rows\"\n type=\"number\"\n min=\"1\"\n max=\"10\"\n value={tableRows}\n onChange={(e) => setTableRows(e.target.value)}\n className=\"mj-table-input\"\n />\n </div>\n <div className=\"mj-table-input-group\">\n <label htmlFor=\"table-cols\">Columns:</label>\n <input\n id=\"table-cols\"\n type=\"number\"\n min=\"1\"\n max=\"10\"\n value={tableCols}\n onChange={(e) => setTableCols(e.target.value)}\n className=\"mj-table-input\"\n />\n </div>\n </div>\n <div className=\"mj-table-modal-footer\">\n <button \n className=\"mj-table-modal-btn mj-table-modal-btn-cancel\"\n onClick={() => setShowTableModal(false)}\n >\n Cancel\n </button>\n <button \n className=\"mj-table-modal-btn mj-table-modal-btn-insert\"\n onClick={() => {\n const rows = parseInt(tableRows);\n const cols = parseInt(tableCols);\n if (rows > 0 && cols > 0) {\n const tableNode = {\n type: 'table' as const,\n rows: rows,\n cols: cols,\n children: Array.from({ length: rows }, () => ({\n type: 'table-row' as const,\n children: Array.from({ length: cols }, () => ({\n type: 'table-cell' as const,\n children: [{ text: '' }],\n })),\n })),\n };\n Transforms.insertNodes(editor, tableNode);\n setShowTableModal(false);\n setTableRows('3');\n setTableCols('3');\n }\n }}\n >\n Insert Table\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Image Insertion Modal */}\n {showImageDialog && (\n <div className=\"mj-table-modal-overlay\" onClick={() => setShowImageDialog(false)}>\n <div className=\"mj-table-modal\" onClick={(e) => e.stopPropagation()}>\n <div className=\"mj-table-modal-header\">\n <h3>Insert Image</h3>\n <button \n className=\"mj-table-modal-close\"\n onC