leksy-editor 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/constant.js +26 -1
- package/index.js +7 -9
- package/package.json +1 -1
- package/plugin.js +167 -43
- package/style.css +56 -5
- package/utilities.js +140 -9
package/README.md
CHANGED
|
@@ -105,6 +105,7 @@ const app = createApp({
|
|
|
105
105
|
| `minHeight` | Minimum height for the editor. |
|
|
106
106
|
| `iframeStyle` | To design inside iframe, pass your css here |
|
|
107
107
|
| `disabled` | To disabled editor |
|
|
108
|
+
| `onFullScreen` | To open custom preview |
|
|
108
109
|
|
|
109
110
|
### CSS Customization
|
|
110
111
|
|
package/constant.js
CHANGED
|
@@ -27,6 +27,7 @@ const CLASSES = {
|
|
|
27
27
|
TOOLBAR_TABLE: '-toolbar-table',
|
|
28
28
|
TOOLBAR_ITEM: '-toolbar-item',
|
|
29
29
|
TOOLBAR_ITEM_SELECT: 'select',
|
|
30
|
+
TOOLBAR_ITEM_BUTTON_SELECT: 'button-select',
|
|
30
31
|
TOOLBAR_ITEM_COLOR: 'color',
|
|
31
32
|
TOOLBAR_ITEMS: '-toolbar-items',
|
|
32
33
|
PLACEHOLDER: '-placeholder',
|
|
@@ -66,6 +67,13 @@ const SVG = {
|
|
|
66
67
|
ATTACHMENT: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8.38 15.68"><g><path d="M15.23,6h1v9.78a3.88,3.88,0,0,1-1.31,2.45,4,4,0,0,1-6.57-2.45V7A3,3,0,0,1,9.2,4.89a3,3,0,0,1,5,2.09v8.31a1.92,1.92,0,0,1-.58,1.39,2,2,0,0,1-1.39.58,1.92,1.92,0,0,1-1.39-.58,2,2,0,0,1-.58-1.39V8h1v7.32a1,1,0,0,0,.29.69,1,1,0,0,0,.69.28A.9.9,0,0,0,13,16a1,1,0,0,0,.29-.69V7a1.92,1.92,0,0,0-.58-1.39A2,2,0,0,0,11.27,5a1.92,1.92,0,0,0-1.39.58A2,2,0,0,0,9.33,7v8.31a3,3,0,1,0,5.9,0V6Z" transform="translate(-8.08 -3.78)"/></g></svg>',
|
|
67
68
|
LIST_BULLETS: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.74 12.37"><g><path d="M7.77,16.12a1.59,1.59,0,0,0-.49-1.18,1.62,1.62,0,0,0-1.19-.49,1.68,1.68,0,1,0,0,3.36,1.67,1.67,0,0,0,1.68-1.69Zm0-4.48A1.67,1.67,0,0,0,6.09,10,1.68,1.68,0,0,0,4.9,12.82a1.62,1.62,0,0,0,1.19.49,1.67,1.67,0,0,0,1.68-1.67Zm12.38,3.64a.27.27,0,0,0-.08-.19.28.28,0,0,0-.2-.09H9.19a.28.28,0,0,0-.2.08.29.29,0,0,0-.08.19V17a.27.27,0,0,0,.28.28H19.87a.27.27,0,0,0,.19-.08.24.24,0,0,0,.08-.2V15.28ZM7.77,7.13a1.63,1.63,0,0,0-.49-1.2,1.61,1.61,0,0,0-1.19-.49,1.61,1.61,0,0,0-1.19.49,1.71,1.71,0,0,0,0,2.4,1.62,1.62,0,0,0,1.19.49,1.61,1.61,0,0,0,1.19-.49,1.63,1.63,0,0,0,.49-1.2Zm12.38,3.66a.28.28,0,0,0-.08-.2.29.29,0,0,0-.19-.08H9.19a.27.27,0,0,0-.28.28v1.69a.27.27,0,0,0,.08.19.24.24,0,0,0,.2.08H19.87a.27.27,0,0,0,.19-.08.25.25,0,0,0,.08-.19V10.79Zm0-4.5a.27.27,0,0,0-.08-.19A.25.25,0,0,0,19.88,6H9.19A.28.28,0,0,0,9,6.1a.26.26,0,0,0-.08.19V8A.27.27,0,0,0,9,8.17a.24.24,0,0,0,.2.08H19.87a.27.27,0,0,0,.19-.08A.25.25,0,0,0,20.14,8V6.29Z" transform="translate(-4.41 -5.44)"/></g></svg>',
|
|
68
69
|
LIST_NUMBER: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.69 15.74"><g><path d="M7.66,18a1.24,1.24,0,0,0-.26-.78,1.17,1.17,0,0,0-.72-.42l.85-1V15H4.58v1.34h.94v-.46l.85,0h0c-.11.11-.22.23-.32.35s-.23.27-.37.47L5.39,17l.23.51c.61-.05.92.11.92.49a.42.42,0,0,1-.18.37.79.79,0,0,1-.45.12A1.41,1.41,0,0,1,5,18.15l-.51.77A2.06,2.06,0,0,0,6,19.5a1.8,1.8,0,0,0,1.2-.41A1.38,1.38,0,0,0,7.66,18Zm0-5.54H6.75V13H5.63A.72.72,0,0,1,6,12.51a5.45,5.45,0,0,1,.66-.45,2.71,2.71,0,0,0,.67-.57,1.19,1.19,0,0,0,.31-.81,1.29,1.29,0,0,0-.45-1,1.86,1.86,0,0,0-2-.11,1.51,1.51,0,0,0-.62.7l.74.52A.87.87,0,0,1,6,10.28a.51.51,0,0,1,.35.12.42.42,0,0,1,.13.33.55.55,0,0,1-.21.4,3,3,0,0,1-.5.38c-.19.13-.39.27-.58.42a2,2,0,0,0-.5.6,1.63,1.63,0,0,0-.21.81,3.89,3.89,0,0,0,.05.48h3.2V12.44Zm12.45,2.82a.27.27,0,0,0-.08-.19.28.28,0,0,0-.21-.08H9.1a.32.32,0,0,0-.21.08.24.24,0,0,0-.08.2V17a.27.27,0,0,0,.08.19.3.3,0,0,0,.21.08H19.83a.32.32,0,0,0,.21-.08.25.25,0,0,0,.08-.19V15.26ZM7.69,7.32h-1V3.76H5.8L4.6,4.88l.63.68a1.85,1.85,0,0,0,.43-.48h0l0,2.24H4.74V8.2h3V7.32Zm12.43,3.42a.27.27,0,0,0-.08-.19.28.28,0,0,0-.21-.08H9.1a.32.32,0,0,0-.21.08.24.24,0,0,0-.08.2v1.71a.27.27,0,0,0,.08.19.3.3,0,0,0,.21.08H19.83a.32.32,0,0,0,.21-.08.25.25,0,0,0,.08-.19V10.74Zm0-4.52A.27.27,0,0,0,20,6,.28.28,0,0,0,19.83,6H9.1A.32.32,0,0,0,8.89,6a.24.24,0,0,0-.08.19V7.93a.27.27,0,0,0,.08.19.32.32,0,0,0,.21.08H19.83A.32.32,0,0,0,20,8.12a.26.26,0,0,0,.08-.2V6.22Z" transform="translate(-4.43 -3.76)"/></g></svg>',
|
|
70
|
+
NEW_LIST_NUMBER: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path d="M144,48V208a8,8,0,0,1-16,0V62.13L100.12,78.86a8,8,0,1,1-8.24-13.72l40-24A8,8,0,0,1,144,48Z"></path></svg>',
|
|
71
|
+
LIST_SQUARE: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path d="M224,48V208a16,16,0,0,1-16,16H48a16,16,0,0,1-16-16V48A16,16,0,0,1,48,32H208A16,16,0,0,1,224,48Z"></path></svg>',
|
|
72
|
+
LIST_DISC: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><rect x="32" y="32" width="192" height="192" rx="16"/></svg>',
|
|
73
|
+
LIST_CIRCLE: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><circle cx="128" cy="128" r="96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>',
|
|
74
|
+
LIST_LOWER_ALPHA: '<svg viewBox="-6 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg"><title>a</title><path d="M15.969 12.094v-2.938h2.25v16.438h-2.25v-2.313c-1.563 1.719-3.875 2.844-6.438 2.844-4.75 0-8.906-3.75-8.906-8.438s4.156-8.438 8.906-8.438c2.563 0 4.875 1.125 6.438 2.844zM15.969 17.875v-0.375c-0.125-3.438-2.969-6.188-6.469-6.188-3.594 0-6.719 2.844-6.719 6.375s3.125 6.375 6.719 6.375c3.5 0 6.344-2.75 6.469-6.188z"></path></svg>',
|
|
75
|
+
LIST_LOWER_ROMAN: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M272 112C272 85.5 293.5 64 320 64C346.5 64 368 85.5 368 112C368 138.5 346.5 160 320 160C293.5 160 272 138.5 272 112zM224 256C224 238.3 238.3 224 256 224L320 224C337.7 224 352 238.3 352 256L352 512L384 512C401.7 512 416 526.3 416 544C416 561.7 401.7 576 384 576L256 576C238.3 576 224 561.7 224 544C224 526.3 238.3 512 256 512L288 512L288 288L256 288C238.3 288 224 273.7 224 256z"/></svg>',
|
|
76
|
+
LIST_UPPER_ALPHA: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M349.5 115.7C344.6 103.8 332.9 96 320 96C307.1 96 295.4 103.8 290.5 115.7C197.2 339.7 143.8 467.7 130.5 499.7C123.7 516 131.4 534.7 147.7 541.5C164 548.3 182.7 540.6 189.5 524.3L221.3 448L418.6 448L450.4 524.3C457.2 540.6 475.9 548.3 492.2 541.5C508.5 534.7 516.2 516 509.4 499.7C496.1 467.7 442.7 339.7 349.4 115.7zM392 384L248 384L320 211.2L392 384z"/></svg>',
|
|
69
77
|
SUBSCRIPT: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.75 14.61"><g><path d="M15.38,4.33H12.74L11.19,7c-.28.46-.51.87-.69,1.21L10.07,9h0l-.44-.8c-.22-.4-.45-.81-.71-1.23L7.34,4.33H4.68L8.26,10,4.4,16.08H7.1l1.69-2.83c.38-.63.72-1.22,1-1.78l.25-.46h0l.49.92c.24.45.48.89.74,1.32L13,16.08h2.61L11.84,10l1.77-2.84,1.77-2.85Zm4.77,13.75H17v-.15c0-.4.05-.64.16-.72a4.42,4.42,0,0,1,1.16-.31,3.3,3.3,0,0,0,1.54-.56A1.84,1.84,0,0,0,20.15,15a1.78,1.78,0,0,0-.44-1.41A2.8,2.8,0,0,0,18,13.25a2.71,2.71,0,0,0-1.69.37,1.83,1.83,0,0,0-.44,1.43v.23H17v-.23q0-.63.18-.78a1.62,1.62,0,0,1,.88-.15,1.59,1.59,0,0,1,.88.15q.18.15.18.75t-.18.75a3.58,3.58,0,0,1-1.18.33,3.33,3.33,0,0,0-1.52.51,1.57,1.57,0,0,0-.32,1.18v1.15h4.27v-.86Z" transform="translate(-4.4 -4.33)"/></g></svg>',
|
|
70
78
|
SUPERSCRIPT: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.75 15.42"><g><path d="M12,13.14l3.61-5.81H12.94L11.33,10c-.28.46-.51.88-.69,1.25l-.45.83h0l-.45-.85c-.22-.41-.45-.82-.71-1.24L7.4,7.33H4.68l3.66,5.81L4.4,19.33H7.14l1.74-2.87q.58-1,1-1.83l.25-.48h0l.51.94.75,1.37,1.72,2.87h2.67l-1.92-3.09c-1.12-1.8-1.76-2.83-1.92-3.1Zm4.84-4.41h0l0,.15h3.27v.86H15.77V8.58a1.66,1.66,0,0,1,.33-1.22,3.51,3.51,0,0,1,1.56-.51,3.68,3.68,0,0,0,1.21-.34c.13-.1.19-.36.19-.77S19,5.07,18.87,5A1.63,1.63,0,0,0,18,4.8a1.58,1.58,0,0,0-.91.17c-.13.11-.19.38-.19.8V6H15.78V5.76a1.87,1.87,0,0,1,.45-1.47A2.84,2.84,0,0,1,18,3.91a2.8,2.8,0,0,1,1.72.38,1.84,1.84,0,0,1,.45,1.44,1.91,1.91,0,0,1-.34,1.35,3.24,3.24,0,0,1-1.58.57A3.69,3.69,0,0,0,17,8c-.12.1-.17.35-.17.76Z" transform="translate(-4.4 -3.91)"/></g></svg>',
|
|
71
79
|
INDENT: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.74 12.36"><g><path d="M4.68,14.45a.27.27,0,0,1-.19-.08.3.3,0,0,1-.08-.21V9.1a.27.27,0,0,1,.08-.19.28.28,0,0,1,.2-.08.25.25,0,0,1,.19.07l2.54,2.54a.29.29,0,0,1,0,.4L4.88,14.36a.24.24,0,0,1-.2.09Zm15.19,1.12a.27.27,0,0,1,.19.08.25.25,0,0,1,.08.19v1.69a.27.27,0,0,1-.08.19.25.25,0,0,1-.19.08H4.68a.27.27,0,0,1-.19-.08.25.25,0,0,1-.08-.19V15.84a.27.27,0,0,1,.27-.27H19.87Zm0-3.38a.27.27,0,0,1,.19.08.28.28,0,0,1,.08.21v1.68a.32.32,0,0,1-.08.21.25.25,0,0,1-.19.08H10.31a.27.27,0,0,1-.19-.08.3.3,0,0,1-.08-.21V12.48a.32.32,0,0,1,.08-.21.24.24,0,0,1,.19-.08h9.56Zm0-3.37a.27.27,0,0,1,.19.08.25.25,0,0,1,.08.19v1.69a.27.27,0,0,1-.08.19.25.25,0,0,1-.19.08H10.31a.27.27,0,0,1-.27-.27V9.1a.27.27,0,0,1,.27-.27h9.56Zm.2-3.29a.28.28,0,0,1,.08.2V7.41a.32.32,0,0,1-.08.21.25.25,0,0,1-.19.08H4.68a.27.27,0,0,1-.19-.08.3.3,0,0,1-.08-.21V5.73a.32.32,0,0,1,.08-.21.25.25,0,0,1,.19-.08H19.87a.28.28,0,0,1,.2.09Z" transform="translate(-4.41 -5.44)"/></g></svg>',
|
|
@@ -88,6 +96,7 @@ const SVG = {
|
|
|
88
96
|
REVERT: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.76 14.69"><g><path d="M18.26,15V12.3l1.89-2V15a2.58,2.58,0,0,1-.24,1c-.2.58-.75.92-1.65,1H7.56v2L4.41,15.63,7.56,13v2h10.7ZM6.3,8.28V11L4.41,13V8.28a2.58,2.58,0,0,1,.24-1c.2-.58.75-.92,1.65-1H17v-2l3.15,3.34L17,10.3v-2H6.3Z" transform="translate(-4.4 -4.28)"/></g></svg>',
|
|
89
97
|
AUTO_SIZE: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.74 15.74"><g><path d="M6.71,17.19,6.89,16l1.21-.15A6,6,0,0,1,6.81,13.9a5.78,5.78,0,0,1-.45-2.27A6,6,0,0,1,8.1,7.45a5.83,5.83,0,0,1,4.17-1.73l1-1-1-1A7.89,7.89,0,0,0,5,14.64a7.73,7.73,0,0,0,1.71,2.55Zm5.57,2.31h0A7.86,7.86,0,0,0,17.85,6.07L17.67,7.3l-1.21.15a5.9,5.9,0,0,1,1.29,1.92,5.81,5.81,0,0,1,.45,2.26,5.91,5.91,0,0,1-5.9,5.9l-1,1,.49.49.47.5Z" transform="translate(-4.41 -3.76)"/></g></svg>',
|
|
90
98
|
ARROW_DOWN: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.73 8.67"><g><path d="M18.79,7.52a.8.8,0,0,1,.56-.23.82.82,0,0,1,.79.79.8.8,0,0,1-.23.56l-7.07,7.07a.79.79,0,0,1-.57.25.77.77,0,0,1-.57-.25h0L4.64,8.65a.8.8,0,0,1-.23-.57.82.82,0,0,1,.79-.79.8.8,0,0,1,.56.23L12.28,14l3.26-3.26,3.25-3.26Z" transform="translate(-4.41 -7.29)"/></g></svg>',
|
|
99
|
+
ARROW_DROP_DOWN_FILL: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" ><path d="M12 14L8 10H16L12 14Z"></path></svg>',
|
|
91
100
|
GIPHY_SVG: '<svg xmlns="http://www.w3.org/2000/svg" width="12px" viewBox="0 0 28 35"><g fill-rule="evenodd" clip-rule="evenodd"><path fill="#00ff99" d="M0 3h4v29H0z"></path><path fill="#9933ff" d="M24 11h4v21h-4z"></path><path fill="#00ccff" d="M0 31h28v4H0z"></path><path fill="#fff35c" d="M0 0h16v4H0z"></path><path fill="#ff6666" d="M24 8V4h-4V0h-4v12h12V8"></path><path class="shadow" d="M24 16v-4h4M16 0v4h-4"></path></g></svg>',
|
|
92
101
|
PEXELS: '<svg viewBox="0 0 17 22" fill="none" xmlns="http://www.w3.org/2000/svg"><g> <path fill-rule="evenodd" clip-rule="evenodd" d="M12 5C12.7111 5 13.3875 5.14845 14 5.41604C15.7659 6.1876 17 7.94968 17 10C17 12.0503 15.7659 13.8124 14 14.584C13.3875 14.8516 12.7111 15 12 15V19H6V5H12ZM8 7V17H10V13H12L12.0032 12.9988C13.6427 13.0303 15.0746 11.6934 15.0443 9.95469L15.0375 9.56529C15.0121 8.10183 13.7882 6.94549 12.3257 7.00299L12.0203 7.00762L12 7H8Z" fill="#07a081"></path> </g></svg>',
|
|
93
102
|
EMOJI: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10.5199 19.8634C10.5955 18.6615 10.8833 17.5172 11.3463 16.4676C9.81124 16.3252 8.41864 15.6867 7.33309 14.7151L8.66691 13.2248C9.55217 14.0172 10.7188 14.4978 12 14.4978C12.1763 14.4978 12.3501 14.4887 12.5211 14.471C14.227 12.2169 16.8661 10.7083 19.8634 10.5199C19.1692 6.80877 15.9126 4 12 4C7.58172 4 4 7.58172 4 12C4 15.9126 6.80877 19.1692 10.5199 19.8634ZM19.0233 12.636C15.7891 13.2396 13.2396 15.7891 12.636 19.0233L19.0233 12.636ZM22 12C22 12.1677 21.9959 12.3344 21.9877 12.5L12.5 21.9877C12.3344 21.9959 12.1677 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12ZM10 10C10 10.8284 9.32843 11.5 8.5 11.5C7.67157 11.5 7 10.8284 7 10C7 9.17157 7.67157 8.5 8.5 8.5C9.32843 8.5 10 9.17157 10 10ZM17 10C17 10.8284 16.3284 11.5 15.5 11.5C14.6716 11.5 14 10.8284 14 10C14 9.17157 14.6716 8.5 15.5 8.5C16.3284 8.5 17 9.17157 17 10Z"></path></svg>',
|
|
@@ -230,7 +239,8 @@ const CSS = {
|
|
|
230
239
|
`,
|
|
231
240
|
IFRAME_EDITOR: `
|
|
232
241
|
.content-editable {
|
|
233
|
-
height: 100vh;
|
|
242
|
+
min-height: 100vh;
|
|
243
|
+
height: 100%;
|
|
234
244
|
outline: 0;
|
|
235
245
|
padding: 4px;
|
|
236
246
|
box-sizing: border-box;
|
|
@@ -651,6 +661,19 @@ const TAB_CATEGORIES = {
|
|
|
651
661
|
}
|
|
652
662
|
};
|
|
653
663
|
|
|
664
|
+
const ORDERED_LIST_OPTIONS = {
|
|
665
|
+
'1': `${SVG.NEW_LIST_NUMBER} Number`,
|
|
666
|
+
'a': `${SVG.LIST_LOWER_ALPHA} Lower Alpha`,
|
|
667
|
+
'i': `${SVG.LIST_LOWER_ROMAN} Lower Roman`,
|
|
668
|
+
'A': `${SVG.LIST_UPPER_ALPHA} Upper Alpha`,
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const UNORDERED_LIST_OPTIONS = {
|
|
672
|
+
'Disc': `${SVG.LIST_DISC} Disc`,
|
|
673
|
+
'Circle': `${SVG.LIST_CIRCLE} Circle`,
|
|
674
|
+
'Square': `${SVG.LIST_SQUARE} Square`,
|
|
675
|
+
}
|
|
676
|
+
|
|
654
677
|
const RESIZE_MARGIN = 10;
|
|
655
678
|
|
|
656
679
|
const LIST_STYLES_BY_LEVEL = ['1', 'a', 'i', 'A'];
|
|
@@ -681,4 +704,6 @@ export {
|
|
|
681
704
|
RESIZE_MARGIN,
|
|
682
705
|
LIST_STYLES_BY_LEVEL,
|
|
683
706
|
UNORDERED_LIST_STYLES_BY_LEVEL,
|
|
707
|
+
UNORDERED_LIST_OPTIONS,
|
|
708
|
+
ORDERED_LIST_OPTIONS,
|
|
684
709
|
}
|
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import './style.css'
|
|
2
2
|
import { CLASSES, CSS, CSS_VARIABLES, ERRORS, REGEX, SVG } from "./constant"
|
|
3
|
-
import PLUGINS, {
|
|
4
|
-
import { showAnchorPopover, changeAllToolbarState, changeToolbarStateByName, changeToolbarValueByName, cleanHTML, debounce, destroyImageResizer, destroyTableEditPlugin, initImageResizer, initTableEditPlugin, makeToolbarButton, makeToolbarColor, makeToolbarDropdown, makeToolbarSelect, rgbToHex, updateTableResizerPosition, destroyAnchorPopover, changeToolbarHtmlByName, showRemoteCursor, syncRemoteChangesDebounce, applyRemoteChanges, updateCursorPositionDebounce, buildTributeValues, updateHeight, getTableGrid, getCellPosition, makeUnorderedList, makeOrderedList, makeHeading, makeBlockQuote, makeStrikethrough, makeCodeBlock, makeSublist } from './utilities';
|
|
3
|
+
import PLUGINS, { applyTextFormat } from './plugin';
|
|
4
|
+
import { showAnchorPopover, changeAllToolbarState, changeToolbarStateByName, changeToolbarValueByName, cleanHTML, debounce, destroyImageResizer, destroyTableEditPlugin, initImageResizer, initTableEditPlugin, makeToolbarButton, makeToolbarColor, makeToolbarDropdown, makeToolbarSelect, rgbToHex, updateTableResizerPosition, destroyAnchorPopover, changeToolbarHtmlByName, showRemoteCursor, syncRemoteChangesDebounce, applyRemoteChanges, updateCursorPositionDebounce, buildTributeValues, updateHeight, getTableGrid, getCellPosition, makeUnorderedList, makeOrderedList, makeHeading, makeBlockQuote, makeStrikethrough, makeCodeBlock, makeSublist, showTooltip, makeToolbarButtonSelect } from './utilities';
|
|
5
5
|
|
|
6
6
|
class LeksyEditor {
|
|
7
7
|
|
|
@@ -27,6 +27,7 @@ class LeksyEditor {
|
|
|
27
27
|
* @property {String} minHeight
|
|
28
28
|
* @property {String} iframeStyle
|
|
29
29
|
* @property {Boolean} disabled
|
|
30
|
+
* @property {Function} onFullScreen
|
|
30
31
|
*/
|
|
31
32
|
/**
|
|
32
33
|
*
|
|
@@ -263,7 +264,6 @@ class LeksyEditor {
|
|
|
263
264
|
getCore: () => core,
|
|
264
265
|
updateHeight: (height) => {
|
|
265
266
|
core.elements.iframeContainer.style.setProperty('height', height);
|
|
266
|
-
core.elements.editor.style.setProperty('height', height);
|
|
267
267
|
},
|
|
268
268
|
setDisabled: (disabled) => {
|
|
269
269
|
options.disabled = disabled;
|
|
@@ -371,6 +371,8 @@ class LeksyEditor {
|
|
|
371
371
|
toolbarPlugin = makeToolbarSelect(_plugin, options, core)
|
|
372
372
|
} else if (_plugin.type === 'color') {
|
|
373
373
|
toolbarPlugin = makeToolbarColor(_plugin, options, core)
|
|
374
|
+
} else if (_plugin.type === 'button-select') {
|
|
375
|
+
toolbarPlugin = makeToolbarButtonSelect(_plugin, options, core)
|
|
374
376
|
}
|
|
375
377
|
if (core.elements.toolbar[plugin])
|
|
376
378
|
core.elements.toolbar[plugin].push(toolbarPlugin)
|
|
@@ -386,6 +388,7 @@ class LeksyEditor {
|
|
|
386
388
|
})
|
|
387
389
|
|
|
388
390
|
if (options.disabled) changeAllToolbarState(core, 'disabled')
|
|
391
|
+
showTooltip(options)
|
|
389
392
|
|
|
390
393
|
core.elements.toolbarContainer = toolbarContainer;
|
|
391
394
|
core.elements.base.appendChild(toolbarContainer);
|
|
@@ -608,11 +611,6 @@ class LeksyEditor {
|
|
|
608
611
|
contentEditableDiv.spellcheck = !!options.spellcheck
|
|
609
612
|
contentEditableDiv.innerHTML = core.html;
|
|
610
613
|
contentEditableDiv.className = 'content-editable';
|
|
611
|
-
if (options.autoHeight) contentEditableDiv.style.height = 'auto';
|
|
612
|
-
else contentEditableDiv.style.height = options.minHeight ?? options.height ?? '250px';
|
|
613
|
-
contentEditableDiv.style.minHeight = options.minHeight ?? options.height ?? '250px';
|
|
614
|
-
if (options.maxHeight) contentEditableDiv.style.maxHeight = options.maxHeight;
|
|
615
|
-
if (options.autoHeight) iframe.contentDocument.body.style.overflow = 'hidden';
|
|
616
614
|
|
|
617
615
|
contentEditableDiv.oninput = (e) => {
|
|
618
616
|
core.onChange(e.target.innerHTML)
|
|
@@ -819,7 +817,7 @@ class LeksyEditor {
|
|
|
819
817
|
|
|
820
818
|
if (e.target.closest("a")?.tagName === 'A') {
|
|
821
819
|
const anchor = e.target.closest("a");
|
|
822
|
-
if (anchor) {
|
|
820
|
+
if (anchor && element.tagName !== 'IMG') {
|
|
823
821
|
core.elements.selectedElement = anchor;
|
|
824
822
|
showAnchorPopover(anchor, e.pageX, e.pageY, options, core);
|
|
825
823
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leksy-editor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Leksy Editor is an alternative to traditional WYSIWYG editors, designed primarily for creating mail templates, blogs, and documents without any content manipulation.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
package/plugin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { EMOJI_CATEGORIES, FONT_SIZE_OPTIONS, FONTS, FORMAT_OPTIONS, MIMETYPE, REGEX, SPECIAL_CHARACTERS, SVG } from "./constant"
|
|
1
|
+
import { EMOJI_CATEGORIES, FONT_SIZE_OPTIONS, FONTS, FORMAT_OPTIONS, MIMETYPE, REGEX, SPECIAL_CHARACTERS, SVG, UNORDERED_LIST_OPTIONS, ORDERED_LIST_OPTIONS } from "./constant"
|
|
2
2
|
import { giphy, pexels, tenor } from "./gallery";
|
|
3
|
-
import { formatLink, changeAllToolbarState, changeToolbarHtmlByName, changeToolbarStateByName, changeToolbarValueByName, cleanHTML, constructEmbedUrl, extractSocialMediaId, isLinkValid, openModal } from "./utilities";
|
|
3
|
+
import { formatLink, changeAllToolbarState, changeToolbarHtmlByName, changeToolbarStateByName, changeToolbarValueByName, cleanHTML, constructEmbedUrl, extractSocialMediaId, isLinkValid, openModal, transformTextStyle } from "./utilities";
|
|
4
4
|
|
|
5
5
|
const applyTextFormat = (core, command) => {
|
|
6
6
|
core.elements.editor.focus();
|
|
@@ -233,7 +233,7 @@ const PLUGINS = {
|
|
|
233
233
|
}
|
|
234
234
|
},
|
|
235
235
|
'font-size': {
|
|
236
|
-
title: 'Font',
|
|
236
|
+
title: 'Font Size',
|
|
237
237
|
icon: 'Font Size',
|
|
238
238
|
type: 'select',
|
|
239
239
|
options: Object.keys(FONT_SIZE_OPTIONS).map(size => ({
|
|
@@ -267,23 +267,62 @@ const PLUGINS = {
|
|
|
267
267
|
'ordered_list': {
|
|
268
268
|
title: "Ordered List",
|
|
269
269
|
icon: SVG.LIST_NUMBER,
|
|
270
|
-
type: 'button',
|
|
271
|
-
|
|
270
|
+
type: 'button-select',
|
|
271
|
+
options: Object.keys(ORDERED_LIST_OPTIONS).map(list => ({
|
|
272
|
+
label: ORDERED_LIST_OPTIONS[list], value: list
|
|
273
|
+
})),
|
|
274
|
+
click: (value, core, options) => {
|
|
272
275
|
core.elements.editor.focus();
|
|
273
|
-
core.elements.iframeWindow.execCommand('insertOrderedList');
|
|
274
|
-
core.updateCaretPosition()
|
|
275
|
-
|
|
276
276
|
const isActive = core.elements.iframeWindow.queryCommandState('insertOrderedList');
|
|
277
|
-
if (isActive)
|
|
278
|
-
|
|
277
|
+
if (!isActive) {
|
|
278
|
+
core.elements.iframeWindow.execCommand('insertOrderedList');
|
|
279
|
+
}
|
|
280
|
+
const selection = core.elements.iframeWindow.getSelection();
|
|
281
|
+
if (selection.rangeCount > 0) {
|
|
282
|
+
let node = selection.anchorNode;
|
|
283
|
+
while (node && node.nodeName !== 'OL' && node !== core.elements.editor) {
|
|
284
|
+
node = node.parentNode;
|
|
285
|
+
}
|
|
286
|
+
if (node?.nodeName === 'OL') {
|
|
287
|
+
node.setAttribute('type', value);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
core.updateCaretPosition()
|
|
291
|
+
changeToolbarStateByName(core, 'active', ['ordered_list'])
|
|
279
292
|
changeToolbarStateByName(core, 'inactive', ['unordered_list'])
|
|
293
|
+
},
|
|
294
|
+
mainClick: (event, core, options) => {
|
|
295
|
+
applyOrderList(core)
|
|
280
296
|
}
|
|
281
297
|
},
|
|
282
298
|
'unordered_list': {
|
|
283
299
|
title: "Unordered List",
|
|
284
300
|
icon: SVG.LIST_BULLETS,
|
|
285
|
-
type: 'button',
|
|
286
|
-
|
|
301
|
+
type: 'button-select',
|
|
302
|
+
options: Object.keys(UNORDERED_LIST_OPTIONS).map(list => ({
|
|
303
|
+
label: UNORDERED_LIST_OPTIONS[list], value: list
|
|
304
|
+
})),
|
|
305
|
+
click: (value, core, options) => {
|
|
306
|
+
core.elements.editor.focus();
|
|
307
|
+
const isActive = core.elements.iframeWindow.queryCommandState('insertUnorderedList');
|
|
308
|
+
if (!isActive) {
|
|
309
|
+
core.elements.iframeWindow.execCommand('insertUnorderedList');
|
|
310
|
+
}
|
|
311
|
+
const selection = core.elements.iframeWindow.getSelection();
|
|
312
|
+
if (selection.rangeCount > 0) {
|
|
313
|
+
let node = selection.anchorNode;
|
|
314
|
+
while (node && node.nodeName !== 'UL' && node !== core.elements.editor) {
|
|
315
|
+
node = node.parentNode;
|
|
316
|
+
}
|
|
317
|
+
if (node?.nodeName === 'UL') {
|
|
318
|
+
node.style.listStyleType = value;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
core.updateCaretPosition()
|
|
322
|
+
changeToolbarStateByName(core, 'active', ['unordered_list'])
|
|
323
|
+
changeToolbarStateByName(core, 'inactive', ['ordered_list'])
|
|
324
|
+
},
|
|
325
|
+
mainClick: (event, core, options) => {
|
|
287
326
|
applyUnorderedList(core)
|
|
288
327
|
}
|
|
289
328
|
},
|
|
@@ -388,7 +427,12 @@ const PLUGINS = {
|
|
|
388
427
|
{ label: 'Block Quote', value: 'blockquote' },
|
|
389
428
|
{ label: 'Code', value: 'code' },
|
|
390
429
|
{ label: 'Translucent', value: 'translucent' },
|
|
391
|
-
{ label: 'Shadow', value: 'shadow' }
|
|
430
|
+
{ label: 'Shadow', value: 'shadow' },
|
|
431
|
+
{ label: 'Upper Case', value: 'upper_case' },
|
|
432
|
+
{ label: 'Lower Case', value: 'lower_case' },
|
|
433
|
+
{ label: 'Title Case', value: 'title_case' },
|
|
434
|
+
{ label: 'Button (Green)', value: 'button_green' },
|
|
435
|
+
{ label: 'Button (Black)', value: 'button_black' }
|
|
392
436
|
],
|
|
393
437
|
click: (event, core, options) => {
|
|
394
438
|
if (!core.state.range) return
|
|
@@ -423,6 +467,50 @@ const PLUGINS = {
|
|
|
423
467
|
core.state.range.insertNode(code);
|
|
424
468
|
break;
|
|
425
469
|
}
|
|
470
|
+
case 'upper_case': {
|
|
471
|
+
transformTextStyle(core, 'upper_case');
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
case 'lower_case': {
|
|
475
|
+
transformTextStyle(core, 'lower_case');
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
case 'title_case': {
|
|
479
|
+
transformTextStyle(core, 'title_case');
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
case 'button_green': {
|
|
483
|
+
core.updateCaretPosition()
|
|
484
|
+
const span = document.createElement('span');
|
|
485
|
+
span.textContent = core.state.range.toString();
|
|
486
|
+
span.style.cssText = 'background-color: #28a745; color: white; font-weight: bold; padding: 8px; border-radius: 4px; text-decoration: none; display: inline-block; cursor: pointer;';
|
|
487
|
+
span.contentEditable = 'false';
|
|
488
|
+
let element = core.state.range.commonAncestorContainer;
|
|
489
|
+
if (element.nodeType === Node.TEXT_NODE) element = element.parentElement;
|
|
490
|
+
if (element.nodeName === 'A') {
|
|
491
|
+
element.style.cssText = 'background-color: #28a745; color: white; font-weight: bold; padding: 8px; border-radius: 4px; text-decoration: none; display: inline-block; cursor: pointer;';
|
|
492
|
+
} else if (span.textContent && !['TR', 'TBODY', 'THEAD', 'TABLE'].includes(element.nodeName)) {
|
|
493
|
+
core.state.range.deleteContents();
|
|
494
|
+
core.state.range.insertNode(span);
|
|
495
|
+
}
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
case 'button_black': {
|
|
499
|
+
core.updateCaretPosition()
|
|
500
|
+
const span = document.createElement('span');
|
|
501
|
+
span.textContent = core.state.range.toString();
|
|
502
|
+
span.style.cssText = 'background-color: black; color: white; font-weight: bold; padding: 8px; border-radius: 4px; text-decoration: none; display: inline-block; cursor: pointer;';
|
|
503
|
+
span.contentEditable = 'false';
|
|
504
|
+
let element = core.state.range.commonAncestorContainer;
|
|
505
|
+
if (element.nodeType === Node.TEXT_NODE) element = element.parentElement;
|
|
506
|
+
if (element.nodeName === 'A') {
|
|
507
|
+
element.style.cssText = 'background-color: black; color: white; font-weight: bold; padding: 8px; border-radius: 4px; text-decoration: none; display: inline-block; cursor: pointer;';
|
|
508
|
+
} else if (span.textContent && !['TR', 'TBODY', 'THEAD', 'TABLE'].includes(element.nodeName)) {
|
|
509
|
+
core.state.range.deleteContents();
|
|
510
|
+
core.state.range.insertNode(span);
|
|
511
|
+
}
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
426
514
|
}
|
|
427
515
|
core.elements.editor.focus();
|
|
428
516
|
core.updateCaretPosition()
|
|
@@ -742,53 +830,85 @@ const PLUGINS = {
|
|
|
742
830
|
button.click();
|
|
743
831
|
}
|
|
744
832
|
};
|
|
745
|
-
const body = document.createElement('div');
|
|
746
833
|
|
|
834
|
+
// Top
|
|
835
|
+
const top = document.createElement('div');
|
|
836
|
+
top.style.display = 'flex';
|
|
837
|
+
top.style.gap = '16px';
|
|
838
|
+
|
|
839
|
+
// Top - left
|
|
840
|
+
const left = document.createElement('div');
|
|
841
|
+
left.style.display = 'flex';
|
|
842
|
+
left.style.flexDirection = 'column';
|
|
843
|
+
left.style.gap = '16px';
|
|
844
|
+
left.style.flex = '1';
|
|
845
|
+
|
|
846
|
+
const uploadBox = document.createElement('div');
|
|
847
|
+
uploadBox.className = `${options.classPrefix}-upload-img-box`;
|
|
848
|
+
uploadBox.innerHTML = `
|
|
849
|
+
<div style="text-align: center; color: #6b7280;">
|
|
850
|
+
<div style="font-size: 32px; font-weight: 500;">+</div>
|
|
851
|
+
<div style="font-size: 14px;">Click to browse files</div>
|
|
852
|
+
</div>
|
|
853
|
+
`;
|
|
747
854
|
|
|
748
855
|
const fileInput = document.createElement('input');
|
|
749
856
|
fileInput.type = 'file';
|
|
750
857
|
fileInput.accept = MIMETYPE.IMAGE;
|
|
751
858
|
fileInput.style.display = 'none';
|
|
859
|
+
uploadBox.addEventListener('click', () => fileInput.click());
|
|
752
860
|
|
|
753
|
-
|
|
754
|
-
const uploadBtn = document.createElement('button');
|
|
755
|
-
uploadBtn.innerText = 'Choose Image';
|
|
756
|
-
uploadBtn.type = 'button';
|
|
757
|
-
uploadBtn.style.border = '1px solid grey';
|
|
758
|
-
uploadBtn.style.padding = '4px';
|
|
759
|
-
uploadBtn.style.borderRadius = '6px';
|
|
861
|
+
left.append(uploadBox, fileInput);
|
|
760
862
|
|
|
863
|
+
// Top - right
|
|
864
|
+
const right = document.createElement('div');
|
|
865
|
+
right.style.display = 'flex';
|
|
866
|
+
right.style.flexDirection = 'column';
|
|
867
|
+
right.style.flex = '1';
|
|
761
868
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
fileNameSpan.style.marginLeft = '10px';
|
|
765
|
-
fileNameSpan.innerText = 'No file selected';
|
|
869
|
+
const previewBox = document.createElement('div');
|
|
870
|
+
previewBox.className = `${options.classPrefix}-upload-img-preview-box`;
|
|
766
871
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
872
|
+
const imgPreview = document.createElement('img');
|
|
873
|
+
imgPreview.className = `${options.classPrefix}-upload-img-preview`;
|
|
874
|
+
imgPreview.style.display = 'none';
|
|
875
|
+
|
|
876
|
+
previewBox.append(imgPreview);
|
|
877
|
+
right.append(previewBox);
|
|
878
|
+
|
|
879
|
+
// Complete top
|
|
880
|
+
top.append(left, right)
|
|
881
|
+
|
|
882
|
+
// other fields
|
|
883
|
+
const span = document.createElement('div');
|
|
884
|
+
span.className = 'warning';
|
|
770
885
|
|
|
771
|
-
const altLabel = document.createElement(
|
|
772
|
-
altLabel.innerText =
|
|
886
|
+
const altLabel = document.createElement('label');
|
|
887
|
+
altLabel.innerText = 'Image Description (Alt Text)';
|
|
773
888
|
altLabel.style.display = "block";
|
|
774
889
|
altLabel.style.paddingLeft = "4px";
|
|
775
|
-
altLabel.style.marginTop = '
|
|
890
|
+
altLabel.style.marginTop = '4px';
|
|
891
|
+
altLabel.style.marginBottom = '4px';
|
|
776
892
|
|
|
777
893
|
const altInput = document.createElement('input');
|
|
778
894
|
altInput.type = 'text';
|
|
779
895
|
altInput.placeholder = 'Alternative Text';
|
|
780
896
|
altInput.addEventListener('keydown', onInputKeydown);
|
|
781
897
|
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
body.append(fileInput, uploadBtn, fileNameSpan, altLabel, altInput, span);
|
|
898
|
+
const body = document.createElement('div');
|
|
899
|
+
body.append(top, span, altLabel, altInput);
|
|
786
900
|
|
|
901
|
+
// Footer
|
|
787
902
|
const footer = document.createElement('div');
|
|
903
|
+
footer.style.display = 'flex';
|
|
904
|
+
footer.style.justifyContent = 'space-between';
|
|
905
|
+
footer.style.alignItems = 'center';
|
|
906
|
+
|
|
788
907
|
const button = document.createElement('button');
|
|
789
908
|
button.type = 'button';
|
|
790
909
|
button.className = 'submit';
|
|
791
910
|
button.innerText = 'Add';
|
|
911
|
+
|
|
792
912
|
footer.append(button);
|
|
793
913
|
|
|
794
914
|
const modal = openModal(
|
|
@@ -800,33 +920,35 @@ const PLUGINS = {
|
|
|
800
920
|
core,
|
|
801
921
|
options
|
|
802
922
|
);
|
|
803
|
-
let base64String =
|
|
923
|
+
let base64String = '';
|
|
804
924
|
|
|
805
925
|
fileInput.addEventListener('change', (event) => {
|
|
806
926
|
const file = event.target.files[0];
|
|
807
|
-
|
|
808
927
|
if (file && MIMETYPE.IMAGE.includes(file.type)) {
|
|
809
928
|
const reader = new FileReader();
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
929
|
+
reader.onload = () => {
|
|
930
|
+
base64String = reader.result;
|
|
931
|
+
imgPreview.src = base64String;
|
|
932
|
+
imgPreview.style.display = 'block';
|
|
813
933
|
};
|
|
814
|
-
|
|
815
934
|
reader.readAsDataURL(file);
|
|
816
|
-
|
|
817
|
-
span.innerText = ""; // Clear warning if file is selected
|
|
935
|
+
span.innerText = "";
|
|
818
936
|
}
|
|
819
937
|
});
|
|
938
|
+
|
|
820
939
|
fileInput.addEventListener('click', async () => {
|
|
821
940
|
let fileFromNative = await core.handleFilePicker(MIMETYPE.IMAGE.split(','), 'image');
|
|
822
941
|
|
|
823
942
|
if (fileFromNative) {
|
|
824
943
|
base64String = fileFromNative.data;
|
|
825
944
|
fileNameSpan.innerText = fileFromNative.name;
|
|
945
|
+
imgPreview.src = base64String;
|
|
946
|
+
imgPreview.style.display = 'block';
|
|
826
947
|
span.innerText = ""; // Clear warning if file is selected
|
|
827
948
|
|
|
828
949
|
}
|
|
829
950
|
});
|
|
951
|
+
|
|
830
952
|
button.onclick = async () => {
|
|
831
953
|
if (!base64String) {
|
|
832
954
|
span.innerText = 'Image not selected';
|
|
@@ -843,6 +965,7 @@ const PLUGINS = {
|
|
|
843
965
|
core.insertNode(img);
|
|
844
966
|
modal.close();
|
|
845
967
|
};
|
|
968
|
+
|
|
846
969
|
}
|
|
847
970
|
else if (event.target.getAttribute('data-value') === 'link') {
|
|
848
971
|
const onInputKeydown = (event) => {
|
|
@@ -1204,6 +1327,7 @@ const PLUGINS = {
|
|
|
1204
1327
|
core.updateCaretPosition()
|
|
1205
1328
|
}
|
|
1206
1329
|
}
|
|
1330
|
+
|
|
1207
1331
|
}
|
|
1208
1332
|
|
|
1209
1333
|
export default PLUGINS
|
package/style.css
CHANGED
|
@@ -119,6 +119,23 @@
|
|
|
119
119
|
width: unset;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
.leksy-editor-toolbar-item.button-select {
|
|
123
|
+
border-radius: 4px 0 0 4px;
|
|
124
|
+
margin-right: 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.leksy-editor-toolbar-item.select.button-select {
|
|
128
|
+
padding: 0;
|
|
129
|
+
border-radius: 0 4px 4px 0;
|
|
130
|
+
margin-left: 0;
|
|
131
|
+
margin-right: 1px;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.leksy-editor-toolbar-item.select.button-select svg {
|
|
135
|
+
width: 18px;
|
|
136
|
+
height: 18px;
|
|
137
|
+
}
|
|
138
|
+
|
|
122
139
|
.leksy-editor-toolbar-item.reset-color {
|
|
123
140
|
width: unset;
|
|
124
141
|
padding: 4px 8px;
|
|
@@ -152,7 +169,9 @@
|
|
|
152
169
|
|
|
153
170
|
.leksy-editor-toolbar-item::after {
|
|
154
171
|
content: attr(data-title);
|
|
155
|
-
position:
|
|
172
|
+
position: fixed;
|
|
173
|
+
top: var(--leksy-editor-top, 0);
|
|
174
|
+
left: var(--leksy-editor-left, 0);
|
|
156
175
|
opacity: 0;
|
|
157
176
|
visibility: hidden;
|
|
158
177
|
transition: opacity 0.3s, visibility 0.3s;
|
|
@@ -161,8 +180,6 @@
|
|
|
161
180
|
color: black;
|
|
162
181
|
padding: 5px 10px;
|
|
163
182
|
border-radius: 5px;
|
|
164
|
-
bottom: -90%;
|
|
165
|
-
left: 60%;
|
|
166
183
|
transform: translateX(-50%);
|
|
167
184
|
white-space: nowrap;
|
|
168
185
|
z-index: 1;
|
|
@@ -277,6 +294,11 @@
|
|
|
277
294
|
border-radius: 6px;
|
|
278
295
|
}
|
|
279
296
|
|
|
297
|
+
.leksy-editor-dropdown-content>button svg {
|
|
298
|
+
width: 10px;
|
|
299
|
+
margin-right: 2px;
|
|
300
|
+
}
|
|
301
|
+
|
|
280
302
|
.leksy-editor-dropdown-content>button:hover {
|
|
281
303
|
background-color: #28a745;
|
|
282
304
|
color: #fff;
|
|
@@ -516,7 +538,6 @@ button.leksy-editor-popover-tab.active {
|
|
|
516
538
|
height: 2rem;
|
|
517
539
|
padding: 0.75rem 1rem;
|
|
518
540
|
background-color: hsl(160, 100%, 35%);
|
|
519
|
-
border-color: hsl(160, 75%, 50%);
|
|
520
541
|
display: flex;
|
|
521
542
|
align-items: center;
|
|
522
543
|
justify-content: center;
|
|
@@ -530,7 +551,6 @@ button.leksy-editor-popover-tab.active {
|
|
|
530
551
|
white-space: nowrap;
|
|
531
552
|
gap: 0.5rem;
|
|
532
553
|
transition: 500ms;
|
|
533
|
-
text-transform: uppercase;
|
|
534
554
|
font-size: 1rem;
|
|
535
555
|
}
|
|
536
556
|
|
|
@@ -601,6 +621,37 @@ button.leksy-editor-popover-tab.active {
|
|
|
601
621
|
animation: leksy-editor-dash 1.5s ease-in-out infinite;
|
|
602
622
|
}
|
|
603
623
|
|
|
624
|
+
.leksy-editor-upload-img-box {
|
|
625
|
+
height: 180px;
|
|
626
|
+
border: 2px dashed #d1d5db;
|
|
627
|
+
border-radius: 12px;
|
|
628
|
+
background: #f9fafb;
|
|
629
|
+
display: flex;
|
|
630
|
+
align-items: center;
|
|
631
|
+
justify-content: center;
|
|
632
|
+
cursor: pointer;
|
|
633
|
+
transition: border-color 0.2s ease;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.leksy-editor-upload-img-box:hover {
|
|
637
|
+
border-color: gray;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.leksy-editor-upload-img-preview-box {
|
|
641
|
+
height: 180px;
|
|
642
|
+
border: 2px solid grey;
|
|
643
|
+
margin-bottom: 8px;
|
|
644
|
+
border-radius: 8px;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.leksy-editor-upload-img-preview {
|
|
648
|
+
width: 100%;
|
|
649
|
+
height: 100%;
|
|
650
|
+
border-radius: 8px;
|
|
651
|
+
object-fit: cover;
|
|
652
|
+
object-position: top;
|
|
653
|
+
}
|
|
654
|
+
|
|
604
655
|
@keyframes leksy-editor-rotate {
|
|
605
656
|
100% {
|
|
606
657
|
transform: rotate(360deg);
|
package/utilities.js
CHANGED
|
@@ -161,6 +161,24 @@ function getScrollParent(element) {
|
|
|
161
161
|
return document.body;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
const showTooltip = (options) => {
|
|
165
|
+
document.addEventListener('mouseover', (e) => {
|
|
166
|
+
const el = e.target.closest(`.${options.classPrefix}${CLASSES.TOOLBAR_ITEM}`);
|
|
167
|
+
if (!el) return;
|
|
168
|
+
|
|
169
|
+
const rect = el.getBoundingClientRect();
|
|
170
|
+
|
|
171
|
+
let top = rect.bottom + 2;
|
|
172
|
+
let left = rect.left + rect.width / 2;
|
|
173
|
+
|
|
174
|
+
left = Math.max(68, Math.min(left, window.innerWidth - 68));
|
|
175
|
+
|
|
176
|
+
el.style.setProperty('--leksy-editor-top', `${top}px`);
|
|
177
|
+
el.style.setProperty('--leksy-editor-left', `${left}px`);
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
|
|
164
182
|
const makeToolbarButton = (_plugin, options, core) => {
|
|
165
183
|
const pluginButton = document.createElement('button');
|
|
166
184
|
pluginButton.type = "button"
|
|
@@ -179,11 +197,38 @@ const makeToolbarButton = (_plugin, options, core) => {
|
|
|
179
197
|
return pluginButton
|
|
180
198
|
}
|
|
181
199
|
|
|
200
|
+
|
|
201
|
+
const makeToolbarButtonSelect = (_plugin, options, core) => {
|
|
202
|
+
const pluginButton = document.createElement('button');
|
|
203
|
+
pluginButton.type = "button"
|
|
204
|
+
pluginButton.className = `${options.classPrefix}${CLASSES.TOOLBAR_ITEM} ${_plugin.type === 'button-select' ? CLASSES.TOOLBAR_ITEM_BUTTON_SELECT : ''}`
|
|
205
|
+
|
|
206
|
+
pluginButton.dataset.title = _plugin.title
|
|
207
|
+
pluginButton.dataset.type = 'button'
|
|
208
|
+
pluginButton.onclick = (e) => {
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
_plugin.mainClick(e, core, options);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const pluginIcon = document.createElement('div');
|
|
214
|
+
pluginIcon.innerHTML = _plugin.icon;
|
|
215
|
+
pluginButton.appendChild(pluginIcon)
|
|
216
|
+
const pluginSelect = makeToolbarSelect({ ..._plugin, icon: '' }, options, core);
|
|
217
|
+
|
|
218
|
+
const pluginButtonSelect = document.createElement('div');
|
|
219
|
+
pluginButtonSelect.dataset.type = 'button-select'
|
|
220
|
+
pluginButtonSelect.style.display = 'flex'
|
|
221
|
+
pluginButtonSelect.appendChild(pluginButton);
|
|
222
|
+
pluginButtonSelect.appendChild(pluginSelect);
|
|
223
|
+
return pluginButtonSelect
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
182
227
|
const makeToolbarColor = (_plugin, options, core) => {
|
|
183
228
|
const pluginContainer = document.createElement('div');
|
|
184
229
|
pluginContainer.className = `${options.classPrefix}${CLASSES.TOOLBAR_ITEM} ${CLASSES.TOOLBAR_ITEM_COLOR}`
|
|
185
|
-
pluginContainer.
|
|
186
|
-
pluginContainer.
|
|
230
|
+
pluginContainer.dataset.title = _plugin.title
|
|
231
|
+
pluginContainer.dataset.type = 'color'
|
|
187
232
|
|
|
188
233
|
const pluginButton = document.createElement('input');
|
|
189
234
|
pluginButton.type = "color"
|
|
@@ -513,23 +558,29 @@ const makeToolbarSelect = (_plugin, options, core) => {
|
|
|
513
558
|
}
|
|
514
559
|
|
|
515
560
|
const selectPlugin = document.createElement('div');
|
|
516
|
-
selectPlugin.
|
|
561
|
+
selectPlugin.dataset.type = 'select'
|
|
517
562
|
|
|
518
563
|
const dropdownButton = document.createElement('button');
|
|
519
564
|
dropdownButton.type = "button"
|
|
520
|
-
dropdownButton.className = `${options.classPrefix}${CLASSES.TOOLBAR_ITEM} ${CLASSES.TOOLBAR_ITEM_SELECT}`
|
|
521
|
-
dropdownButton.
|
|
565
|
+
dropdownButton.className = `${options.classPrefix}${CLASSES.TOOLBAR_ITEM} ${CLASSES.TOOLBAR_ITEM_SELECT} ${_plugin.type === 'button-select' ? CLASSES.TOOLBAR_ITEM_BUTTON_SELECT : ''}`
|
|
566
|
+
dropdownButton.dataset.title = _plugin.type == 'button-select' ? `Options` : _plugin.title
|
|
522
567
|
|
|
523
568
|
const pluginIcon = document.createElement('div');
|
|
524
569
|
pluginIcon.style.width = _plugin.width
|
|
525
570
|
pluginIcon.style.textAlign = 'left'
|
|
526
571
|
pluginIcon.style.display = 'flex'
|
|
527
572
|
pluginIcon.style.justifyContent = 'space-between'
|
|
573
|
+
|
|
528
574
|
const iconName = document.createElement('span');
|
|
529
575
|
iconName.innerHTML = _plugin.icon;
|
|
576
|
+
|
|
530
577
|
const arrow = document.createElement('span');
|
|
531
|
-
|
|
532
|
-
|
|
578
|
+
if (_plugin.type == 'button-select') {
|
|
579
|
+
arrow.innerHTML = SVG.ARROW_DROP_DOWN_FILL;
|
|
580
|
+
} else {
|
|
581
|
+
arrow.innerHTML = SVG.ARROW_DOWN;
|
|
582
|
+
arrow.style.paddingLeft = '8px';
|
|
583
|
+
}
|
|
533
584
|
pluginIcon.append(iconName, arrow)
|
|
534
585
|
|
|
535
586
|
dropdownButton.append(pluginIcon)
|
|
@@ -890,7 +941,7 @@ const makeEditToolbar = (options, core, { type, td, image, updateImage, updateTa
|
|
|
890
941
|
title.textContent = plugin.title;
|
|
891
942
|
|
|
892
943
|
const input = document.createElement('input');
|
|
893
|
-
input.type = "
|
|
944
|
+
input.type = "number";
|
|
894
945
|
input.placeholder = "auto";
|
|
895
946
|
input.style.width = '60px';
|
|
896
947
|
input.style.padding = '2px';
|
|
@@ -1110,7 +1161,11 @@ const initImageResizer = (type, image, options, core) => {
|
|
|
1110
1161
|
src = href;
|
|
1111
1162
|
}
|
|
1112
1163
|
}
|
|
1113
|
-
|
|
1164
|
+
if (options.onFullScreen instanceof Function) {
|
|
1165
|
+
const type = isVideo ? 'video/mp4' : 'image/png';
|
|
1166
|
+
options.onFullScreen(src, type);
|
|
1167
|
+
}
|
|
1168
|
+
else createPreviewModal(src, core, options, isVideo);
|
|
1114
1169
|
});
|
|
1115
1170
|
|
|
1116
1171
|
resizer.appendChild(fullscreenButton);
|
|
@@ -2000,6 +2055,11 @@ const changeToolbarStatByPlugin = (plugin, action, value) => {
|
|
|
2000
2055
|
} else if (plugin.getAttribute('data-type') === 'color') {
|
|
2001
2056
|
plugin.childNodes[0].disabled = true
|
|
2002
2057
|
plugin.classList.add('disabled')
|
|
2058
|
+
} else if (plugin.getAttribute('data-type') === 'button-select') {
|
|
2059
|
+
plugin.querySelectorAll('button').forEach(btn => {
|
|
2060
|
+
btn.disabled = true
|
|
2061
|
+
btn.classList.add('disabled')
|
|
2062
|
+
})
|
|
2003
2063
|
}
|
|
2004
2064
|
break;
|
|
2005
2065
|
case 'enabled':
|
|
@@ -2012,6 +2072,11 @@ const changeToolbarStatByPlugin = (plugin, action, value) => {
|
|
|
2012
2072
|
} else if (plugin.getAttribute('data-type') === 'color') {
|
|
2013
2073
|
plugin.childNodes[0].disabled = false
|
|
2014
2074
|
plugin.classList.remove('disabled')
|
|
2075
|
+
} else if (plugin.getAttribute('data-type') === 'button-select') {
|
|
2076
|
+
plugin.querySelectorAll('button').forEach(btn => {
|
|
2077
|
+
btn.disabled = false
|
|
2078
|
+
btn.classList.remove('disabled')
|
|
2079
|
+
})
|
|
2015
2080
|
}
|
|
2016
2081
|
break;
|
|
2017
2082
|
case 'active':
|
|
@@ -2021,6 +2086,8 @@ const changeToolbarStatByPlugin = (plugin, action, value) => {
|
|
|
2021
2086
|
plugin.childNodes[0].classList.add('active')
|
|
2022
2087
|
} else if (plugin.getAttribute('data-type') === 'color') {
|
|
2023
2088
|
plugin.classList.add('active')
|
|
2089
|
+
} else if (plugin.getAttribute('data-type') === 'button-select') {
|
|
2090
|
+
plugin.querySelector('button[data-type="button"]')?.classList.add('active')
|
|
2024
2091
|
}
|
|
2025
2092
|
break;
|
|
2026
2093
|
case 'inactive':
|
|
@@ -2030,6 +2097,8 @@ const changeToolbarStatByPlugin = (plugin, action, value) => {
|
|
|
2030
2097
|
plugin.childNodes[0].classList.remove('active')
|
|
2031
2098
|
} else if (plugin.getAttribute('data-type') === 'color') {
|
|
2032
2099
|
plugin.classList.remove('active')
|
|
2100
|
+
} else if (plugin.getAttribute('data-type') === 'button-select') {
|
|
2101
|
+
plugin.querySelector('button[data-type="button"]')?.classList.remove('active')
|
|
2033
2102
|
}
|
|
2034
2103
|
break;
|
|
2035
2104
|
case 'font':
|
|
@@ -3055,6 +3124,65 @@ const makeSublist = (event, core) => {
|
|
|
3055
3124
|
}
|
|
3056
3125
|
};
|
|
3057
3126
|
|
|
3127
|
+
const transformCase = (text, type) => {
|
|
3128
|
+
if (type === 'upper_case') return text.toUpperCase();
|
|
3129
|
+
if (type === 'lower_case') return text.toLowerCase();
|
|
3130
|
+
if (type === 'title_case') return text.toLowerCase().replace(/\b\w/g, s => s.toUpperCase());
|
|
3131
|
+
return text;
|
|
3132
|
+
};
|
|
3133
|
+
|
|
3134
|
+
const transformTextStyle = (core, type) => {
|
|
3135
|
+
const range = core.state.range;
|
|
3136
|
+
if (!range) return;
|
|
3137
|
+
const startContainer = range.startContainer;
|
|
3138
|
+
const startOffset = range.startOffset;
|
|
3139
|
+
const endContainer = range.endContainer;
|
|
3140
|
+
let endOffset = range.endOffset;
|
|
3141
|
+
|
|
3142
|
+
const processTextNode = (node, start, end) => {
|
|
3143
|
+
const text = node.nodeValue;
|
|
3144
|
+
const transformedText = transformCase(text.slice(start, end), type);
|
|
3145
|
+
node.nodeValue = text.slice(0, start) + transformedText + text.slice(end);
|
|
3146
|
+
if (node === endContainer) endOffset = start + transformedText.length;
|
|
3147
|
+
};
|
|
3148
|
+
|
|
3149
|
+
if (range.commonAncestorContainer.nodeType === Node.TEXT_NODE) {
|
|
3150
|
+
processTextNode(range.commonAncestorContainer, range.startOffset, range.endOffset);
|
|
3151
|
+
} else {
|
|
3152
|
+
const iterator = core.elements.iframeWindow.createNodeIterator(
|
|
3153
|
+
range.commonAncestorContainer,
|
|
3154
|
+
NodeFilter.SHOW_TEXT,
|
|
3155
|
+
{
|
|
3156
|
+
acceptNode: (node) => {
|
|
3157
|
+
return range.intersectsNode(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
);
|
|
3161
|
+
|
|
3162
|
+
const nodes = [];
|
|
3163
|
+
let node;
|
|
3164
|
+
while ((node = iterator.nextNode())) {
|
|
3165
|
+
nodes.push(node);
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
nodes.forEach(node => {
|
|
3169
|
+
let start = 0;
|
|
3170
|
+
let end = node.nodeValue.length;
|
|
3171
|
+
|
|
3172
|
+
if (node === range.startContainer) start = range.startOffset;
|
|
3173
|
+
if (node === range.endContainer) end = range.endOffset;
|
|
3174
|
+
|
|
3175
|
+
processTextNode(node, start, end);
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
const newRange = core.elements.iframeWindow.createRange();
|
|
3180
|
+
newRange.setStart(startContainer, startOffset);
|
|
3181
|
+
newRange.setEnd(endContainer, endOffset);
|
|
3182
|
+
const selection = core.elements.iframeWindow.getSelection();
|
|
3183
|
+
selection.removeAllRanges();
|
|
3184
|
+
selection.addRange(newRange);
|
|
3185
|
+
}
|
|
3058
3186
|
export {
|
|
3059
3187
|
cleanHTML,
|
|
3060
3188
|
debounce,
|
|
@@ -3090,9 +3218,12 @@ export {
|
|
|
3090
3218
|
getCellPosition,
|
|
3091
3219
|
makeUnorderedList,
|
|
3092
3220
|
makeOrderedList,
|
|
3221
|
+
showTooltip,
|
|
3093
3222
|
makeHeading,
|
|
3094
3223
|
makeBlockQuote,
|
|
3095
3224
|
makeStrikethrough,
|
|
3096
3225
|
makeCodeBlock,
|
|
3097
3226
|
makeSublist,
|
|
3227
|
+
transformTextStyle,
|
|
3228
|
+
makeToolbarButtonSelect,
|
|
3098
3229
|
}
|