leksy-editor 1.0.21 → 1.1.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 +4 -0
- package/constant.js +7 -2
- package/index.js +68 -17
- package/package.json +1 -1
- package/plugin.js +48 -10
- package/utilities.js +90 -42
package/README.md
CHANGED
|
@@ -98,6 +98,9 @@ const app = createApp({
|
|
|
98
98
|
| `pexelsApiKey` | API key for Pexels integration. |
|
|
99
99
|
| `tenorApiKey` | API key for Tenor integration. |
|
|
100
100
|
| `cssVariables` | Custom CSS styling variables for multiple themes and modes. |
|
|
101
|
+
| `autoHeight` | Automatically adjusts height based on content. |
|
|
102
|
+
| `maxHeight` | Maximum height for the editor. |
|
|
103
|
+
| `minHeight` | Minimum height for the editor. |
|
|
101
104
|
|
|
102
105
|
### CSS Customization
|
|
103
106
|
|
|
@@ -118,6 +121,7 @@ cssVariables: {
|
|
|
118
121
|
textColor: "hsl(216, 10%, 20%)",
|
|
119
122
|
resizerPosition: "#fff",
|
|
120
123
|
resizerPositionBackground: "#333",
|
|
124
|
+
linkColor: "#0000ff",
|
|
121
125
|
}
|
|
122
126
|
```
|
|
123
127
|
|
package/constant.js
CHANGED
|
@@ -167,13 +167,14 @@ const CSS_VARIABLES = {
|
|
|
167
167
|
resizerBackground: "hsl(211, 100%, 62%, 0.3)",
|
|
168
168
|
shadow: "rgba(0, 0, 0, 0.3)",
|
|
169
169
|
mention: " #efefef",
|
|
170
|
-
mentionHighlight: "
|
|
170
|
+
mentionHighlight: "#ddd",
|
|
171
171
|
dashedBorder: "gray",
|
|
172
172
|
tableResizer: "cornsilk",
|
|
173
173
|
tableResizerOutline: "darkblue",
|
|
174
174
|
textColor: "hsl(216, 10%, 20%)",
|
|
175
175
|
resizerPosition: "#fff",
|
|
176
|
-
resizerPositionBackground: "
|
|
176
|
+
resizerPositionBackground: "#333",
|
|
177
|
+
linkColor: "#0000ff",
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
const CSS = {
|
|
@@ -194,11 +195,15 @@ const CSS = {
|
|
|
194
195
|
--text-color: #000000;
|
|
195
196
|
--resizer-position: #ffffff;
|
|
196
197
|
--resizer-position-background: #333;
|
|
198
|
+
--link-color: blue;
|
|
197
199
|
}
|
|
198
200
|
`,
|
|
199
201
|
IFRAME_TRIBUTE: `.tribute{height:auto;max-height:300px;max-width:500px;overflow:auto;display:block;z-index:100;border:1px solid var(--base-white-dark)}.tribute ul{margin:2px 0 0;padding:0;list-style:none;background-color:var(--mention); color:var(--text-color)}.tribute .category{color:var(--text-color); padding:5px;color:var(--primary);background-color:var(--base-white)}.tribute .item{padding:5px;cursor:pointer}.tribute .highlight{background-color:var(--mention-highlight)}`,
|
|
200
202
|
IFRAME_BODY: `
|
|
201
203
|
body { margin: 0}
|
|
204
|
+
a {
|
|
205
|
+
color: var(--link-color);
|
|
206
|
+
}
|
|
202
207
|
::-webkit-scrollbar {
|
|
203
208
|
width: 6px;
|
|
204
209
|
height: 6px;
|
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
3
|
import PLUGINS, { applyOrderList, 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 } from './utilities';
|
|
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 } from './utilities';
|
|
5
5
|
|
|
6
6
|
class LeksyEditor {
|
|
7
7
|
|
|
@@ -21,6 +21,9 @@ class LeksyEditor {
|
|
|
21
21
|
* @property {String} tenorApiKey
|
|
22
22
|
* @property {Object} cssVariables
|
|
23
23
|
* @property {Boolean} disablePastedColorStyles
|
|
24
|
+
* @property {Boolean} autoHeight
|
|
25
|
+
* @property {String} maxHeight
|
|
26
|
+
* @property {String} minHeight
|
|
24
27
|
*/
|
|
25
28
|
/**
|
|
26
29
|
*
|
|
@@ -46,7 +49,7 @@ class LeksyEditor {
|
|
|
46
49
|
base: baseElement,
|
|
47
50
|
toolbar: {},
|
|
48
51
|
},
|
|
49
|
-
state: { isCodeViewOpen: false, page: 1, totalPages: null, next: null },
|
|
52
|
+
state: { isCodeViewOpen: false, page: 1, totalPages: null, next: null, tribute: null },
|
|
50
53
|
html: options.value || '<div><br></div>',
|
|
51
54
|
cursor: {
|
|
52
55
|
startIndex: 0,
|
|
@@ -127,6 +130,7 @@ class LeksyEditor {
|
|
|
127
130
|
}
|
|
128
131
|
},
|
|
129
132
|
onChange: (html = core.elements.editor.innerHTML) => {
|
|
133
|
+
updateHeight(core, options)
|
|
130
134
|
updateTableResizerPosition(options, core)
|
|
131
135
|
if (html === '<br>' || html === '<div><br></div>') html = ''
|
|
132
136
|
core.html = html;
|
|
@@ -243,6 +247,7 @@ class LeksyEditor {
|
|
|
243
247
|
setContents: (html) => {
|
|
244
248
|
core.html = html
|
|
245
249
|
core.elements.editor.innerHTML = html
|
|
250
|
+
updateHeight(core, options)
|
|
246
251
|
},
|
|
247
252
|
getContents: () => core.html,
|
|
248
253
|
onChange: () => { },
|
|
@@ -253,6 +258,46 @@ class LeksyEditor {
|
|
|
253
258
|
uploadVideo: async () => { },
|
|
254
259
|
focus: () => { core.elements.editor.focus() },
|
|
255
260
|
getCore: () => core,
|
|
261
|
+
updateCssVariables: (newVariables) => {
|
|
262
|
+
const variableMap = {
|
|
263
|
+
primary: '--primary',
|
|
264
|
+
midDarker: '--white-mid-darker',
|
|
265
|
+
baseWhite: '--base-white',
|
|
266
|
+
whiteDark: '--base-white-dark',
|
|
267
|
+
shadow: '--shadow',
|
|
268
|
+
resizer: '--resizer',
|
|
269
|
+
resizerBackground: '--resizer-background',
|
|
270
|
+
mention: '--mention',
|
|
271
|
+
mentionHighlight: '--mention-highlight',
|
|
272
|
+
dashedBorder: '--dashed-border',
|
|
273
|
+
tableResizer: '--table-resizer',
|
|
274
|
+
tableResizerOutline: '--table-resizer-outline',
|
|
275
|
+
textColor: '--text-color',
|
|
276
|
+
resizerPosition: '--resizer-position',
|
|
277
|
+
resizerPositionBackground: '--resizer-position-background',
|
|
278
|
+
linkColor: '--link-color',
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const mergedVariables = {
|
|
282
|
+
...CSS_VARIABLES,
|
|
283
|
+
...newVariables
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
Object.keys(variableMap).forEach(key => {
|
|
287
|
+
const cssVar = variableMap[key];
|
|
288
|
+
const value = mergedVariables[key];
|
|
289
|
+
|
|
290
|
+
if (value !== undefined) {
|
|
291
|
+
core.elements.iframeWindow.documentElement.style.setProperty(cssVar, value);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
updateLabels: (labels = []) => {
|
|
296
|
+
if (!core.state.tribute) return;
|
|
297
|
+
|
|
298
|
+
const values = buildTributeValues(labels);
|
|
299
|
+
core.state.tribute.append(0, values, true);
|
|
300
|
+
},
|
|
256
301
|
getLocalCursor: () => core.cursor,
|
|
257
302
|
setRemoteCursor: (user, cursor) => showRemoteCursor(core, user, cursor),
|
|
258
303
|
onLocalCursorChange: () => { },
|
|
@@ -477,10 +522,15 @@ class LeksyEditor {
|
|
|
477
522
|
childList: true,
|
|
478
523
|
subtree: true
|
|
479
524
|
});
|
|
525
|
+
|
|
526
|
+
// font / layout reflow
|
|
527
|
+
const ro = new ResizeObserver(() => {
|
|
528
|
+
updateHeight(core, options)
|
|
529
|
+
});
|
|
530
|
+
ro.observe(contentEditableDiv);
|
|
480
531
|
}
|
|
481
532
|
|
|
482
533
|
/***************************** for css theme variables ********************************* */
|
|
483
|
-
|
|
484
534
|
if (options.cssVariables) {
|
|
485
535
|
const cssVariable = options.cssVariables;
|
|
486
536
|
const _CSS_VARIABLES = structuredClone(CSS_VARIABLES)
|
|
@@ -507,13 +557,19 @@ class LeksyEditor {
|
|
|
507
557
|
--text-color: ${_CSS_VARIABLES.textColor};
|
|
508
558
|
--resizer-position: ${_CSS_VARIABLES.resizerPosition};
|
|
509
559
|
--resizer-position-background: ${_CSS_VARIABLES.resizerPositionBackground};
|
|
560
|
+
--link-color: ${_CSS_VARIABLES.linkColor};
|
|
510
561
|
}
|
|
511
562
|
`;
|
|
512
563
|
}
|
|
513
564
|
|
|
514
565
|
const iframe = document.createElement('iframe');
|
|
515
566
|
iframe.style.flex = '1 1 auto';
|
|
516
|
-
|
|
567
|
+
|
|
568
|
+
if (options.autoHeight) iframe.style.height = 'auto';
|
|
569
|
+
else iframe.style.height = options.minHeight ?? '250px';
|
|
570
|
+
iframe.style.minHeight = options.minHeight ?? '250px';
|
|
571
|
+
if (options.maxHeight) iframe.style.maxHeight = options.maxHeight;
|
|
572
|
+
|
|
517
573
|
iframe.style.border = '0';
|
|
518
574
|
core.elements.base.appendChild(iframe)
|
|
519
575
|
|
|
@@ -554,6 +610,11 @@ class LeksyEditor {
|
|
|
554
610
|
contentEditableDiv.spellcheck = !!options.spellcheck
|
|
555
611
|
contentEditableDiv.innerHTML = core.html;
|
|
556
612
|
contentEditableDiv.className = 'content-editable';
|
|
613
|
+
if (options.autoHeight) contentEditableDiv.style.height = 'auto';
|
|
614
|
+
else contentEditableDiv.style.height = options.minHeight ?? '250px';
|
|
615
|
+
contentEditableDiv.style.minHeight = options.minHeight ?? '250px';
|
|
616
|
+
if (options.maxHeight) contentEditableDiv.style.maxHeight = options.maxHeight;
|
|
617
|
+
if (options.autoHeight) iframe.contentDocument.body.style.overflow = 'hidden';
|
|
557
618
|
|
|
558
619
|
contentEditableDiv.oninput = (e) => {
|
|
559
620
|
core.onChange(e.target.innerHTML)
|
|
@@ -718,18 +779,8 @@ class LeksyEditor {
|
|
|
718
779
|
script.src = "https://cdnjs.cloudflare.com/ajax/libs/tributejs/5.1.3/tribute.js";
|
|
719
780
|
|
|
720
781
|
script.onload = () => {
|
|
721
|
-
|
|
722
|
-
values: options.labels
|
|
723
|
-
const fields = [
|
|
724
|
-
{ key: category.name, isCategory: true },
|
|
725
|
-
...category.fields.map(field => ({
|
|
726
|
-
key: field.name,
|
|
727
|
-
value: field.value,
|
|
728
|
-
}))
|
|
729
|
-
];
|
|
730
|
-
|
|
731
|
-
return fields
|
|
732
|
-
}),
|
|
782
|
+
core.state.tribute = new iframe.contentWindow.Tribute({
|
|
783
|
+
values: buildTributeValues(options.labels),
|
|
733
784
|
noMatchTemplate: () => '<li>No match found</li>',
|
|
734
785
|
menuItemTemplate: (item) => {
|
|
735
786
|
if (item.original.isCategory) return `<div class="category">${item.original.key}</div>`;
|
|
@@ -741,7 +792,7 @@ class LeksyEditor {
|
|
|
741
792
|
return `<span spellcheck="false" contentEditable="false" data-id="${item.original.value}">${item.original.key}</span>`;
|
|
742
793
|
},
|
|
743
794
|
});
|
|
744
|
-
tribute.attach(contentEditableDiv);
|
|
795
|
+
core.state.tribute.attach(contentEditableDiv);
|
|
745
796
|
makeObserver()
|
|
746
797
|
};
|
|
747
798
|
iframeDoc.body.appendChild(script);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leksy-editor",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.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
|
@@ -542,22 +542,32 @@ const PLUGINS = {
|
|
|
542
542
|
}
|
|
543
543
|
}
|
|
544
544
|
const body = document.createElement('div')
|
|
545
|
+
const linkLabel = document.createElement("label");
|
|
546
|
+
linkLabel.innerText = "Link";
|
|
547
|
+
linkLabel.style.display = "block";
|
|
548
|
+
linkLabel.style.paddingLeft = "4px";
|
|
549
|
+
|
|
545
550
|
const inputLink = document.createElement('input');
|
|
546
551
|
inputLink.value = anchorLink
|
|
547
552
|
inputLink.type = 'text'
|
|
548
553
|
inputLink.placeholder = 'Insert link, mail, phone no'
|
|
549
554
|
inputLink.addEventListener('keydown', onInputKeydown);
|
|
550
555
|
|
|
556
|
+
const textLabel = document.createElement("label");
|
|
557
|
+
textLabel.innerText = "Link Text";
|
|
558
|
+
textLabel.style.display = "block";
|
|
559
|
+
textLabel.style.paddingLeft = "4px";
|
|
560
|
+
textLabel.style.marginTop = "8px";
|
|
561
|
+
|
|
551
562
|
const inputText = document.createElement('input');
|
|
552
563
|
inputText.value = anchorText
|
|
553
564
|
inputText.type = 'text'
|
|
554
|
-
inputText.style.marginTop = '8px'
|
|
555
565
|
inputText.placeholder = 'Link text'
|
|
556
566
|
inputText.addEventListener('keydown', onInputKeydown);
|
|
557
567
|
|
|
558
568
|
const span = document.createElement('span');
|
|
559
569
|
span.className = 'warning'
|
|
560
|
-
body.append(inputLink,
|
|
570
|
+
body.append(linkLabel, inputLink, span, textLabel, inputText);
|
|
561
571
|
|
|
562
572
|
const footer = document.createElement('div')
|
|
563
573
|
const button = document.createElement('button')
|
|
@@ -622,7 +632,7 @@ const PLUGINS = {
|
|
|
622
632
|
modal.close()
|
|
623
633
|
core.elements.editor.focus();
|
|
624
634
|
} else {
|
|
625
|
-
span.innerText = 'Invalid
|
|
635
|
+
span.innerText = 'Invalid URL'
|
|
626
636
|
}
|
|
627
637
|
};
|
|
628
638
|
core.updateCaretPosition()
|
|
@@ -740,9 +750,9 @@ const PLUGINS = {
|
|
|
740
750
|
const uploadBtn = document.createElement('button');
|
|
741
751
|
uploadBtn.innerText = 'Choose Image';
|
|
742
752
|
uploadBtn.type = 'button';
|
|
743
|
-
uploadBtn.style.border = '
|
|
753
|
+
uploadBtn.style.border = '1px solid grey';
|
|
744
754
|
uploadBtn.style.padding = '4px';
|
|
745
|
-
uploadBtn.style.borderRadius = '
|
|
755
|
+
uploadBtn.style.borderRadius = '6px';
|
|
746
756
|
|
|
747
757
|
|
|
748
758
|
// Span to show selected file name
|
|
@@ -754,17 +764,21 @@ const PLUGINS = {
|
|
|
754
764
|
fileInput.click(); // Trigger file input
|
|
755
765
|
});
|
|
756
766
|
|
|
767
|
+
const altLabel = document.createElement("label");
|
|
768
|
+
altLabel.innerText = "Alternative Text";
|
|
769
|
+
altLabel.style.display = "block";
|
|
770
|
+
altLabel.style.paddingLeft = "4px";
|
|
771
|
+
altLabel.style.marginTop = '10px';
|
|
757
772
|
|
|
758
773
|
const altInput = document.createElement('input');
|
|
759
774
|
altInput.type = 'text';
|
|
760
775
|
altInput.placeholder = 'Alternative Text';
|
|
761
|
-
altInput.style.marginTop = '10px';
|
|
762
776
|
altInput.addEventListener('keydown', onInputKeydown);
|
|
763
777
|
|
|
764
778
|
const span = document.createElement('span');
|
|
765
779
|
span.className = 'warning';
|
|
766
780
|
|
|
767
|
-
body.append(fileInput, uploadBtn, fileNameSpan, altInput, span);
|
|
781
|
+
body.append(fileInput, uploadBtn, fileNameSpan, altLabel, altInput, span);
|
|
768
782
|
|
|
769
783
|
const footer = document.createElement('div');
|
|
770
784
|
const button = document.createElement('button');
|
|
@@ -835,22 +849,34 @@ const PLUGINS = {
|
|
|
835
849
|
}
|
|
836
850
|
|
|
837
851
|
const body = document.createElement('div')
|
|
852
|
+
|
|
853
|
+
const linkLabel = document.createElement("label");
|
|
854
|
+
linkLabel.innerText = "Link";
|
|
855
|
+
linkLabel.style.display = "block";
|
|
856
|
+
linkLabel.style.paddingLeft = "4px";
|
|
857
|
+
|
|
838
858
|
const input = document.createElement('input');
|
|
839
859
|
input.value = ''
|
|
840
860
|
input.type = 'text'
|
|
841
861
|
input.placeholder = 'Insert the link'
|
|
842
862
|
input.addEventListener('keydown', onInputKeydown);
|
|
843
863
|
|
|
864
|
+
const textLabel = document.createElement("label");
|
|
865
|
+
textLabel.innerText = "Alternative Text";
|
|
866
|
+
textLabel.style.display = "block";
|
|
867
|
+
textLabel.style.marginTop = "8px";
|
|
868
|
+
textLabel.style.paddingLeft = "4px";
|
|
869
|
+
|
|
844
870
|
const altInput = document.createElement('input');
|
|
845
871
|
altInput.value = ''
|
|
846
|
-
altInput.style.marginTop = '
|
|
872
|
+
altInput.style.marginTop = '4px'
|
|
847
873
|
altInput.type = 'text'
|
|
848
874
|
altInput.placeholder = 'Alternative Text'
|
|
849
875
|
altInput.addEventListener('keydown', onInputKeydown);
|
|
850
876
|
|
|
851
877
|
const span = document.createElement('span');
|
|
852
878
|
span.className = 'warning'
|
|
853
|
-
body.append(input,
|
|
879
|
+
body.append(linkLabel, input, span, textLabel, altInput);
|
|
854
880
|
|
|
855
881
|
const footer = document.createElement('div')
|
|
856
882
|
const button = document.createElement('button')
|
|
@@ -1085,6 +1111,10 @@ const PLUGINS = {
|
|
|
1085
1111
|
body.className = "link"
|
|
1086
1112
|
|
|
1087
1113
|
// Input for Embed URL
|
|
1114
|
+
const embedLabel = document.createElement("label");
|
|
1115
|
+
embedLabel.innerText = "Embed URL";
|
|
1116
|
+
embedLabel.style.display = "block";
|
|
1117
|
+
embedLabel.style.paddingLeft = "4px";
|
|
1088
1118
|
const urlInput = document.createElement('input');
|
|
1089
1119
|
urlInput.type = 'text';
|
|
1090
1120
|
urlInput.placeholder = 'Enter Embed URL';
|
|
@@ -1092,6 +1122,10 @@ const PLUGINS = {
|
|
|
1092
1122
|
urlInput.addEventListener('keydown', onInputKeydown);
|
|
1093
1123
|
|
|
1094
1124
|
// Input for Width (%)
|
|
1125
|
+
const widthLabel = document.createElement("label");
|
|
1126
|
+
widthLabel.innerText = "Width";
|
|
1127
|
+
widthLabel.style.display = "block";
|
|
1128
|
+
widthLabel.style.paddingLeft = "4px";
|
|
1095
1129
|
const widthInput = document.createElement('input');
|
|
1096
1130
|
widthInput.type = 'text';
|
|
1097
1131
|
widthInput.placeholder = 'Width (px)';
|
|
@@ -1099,6 +1133,10 @@ const PLUGINS = {
|
|
|
1099
1133
|
widthInput.value = "400"; // Default to 400px
|
|
1100
1134
|
|
|
1101
1135
|
// Input for Height (%)
|
|
1136
|
+
const heightLabel = document.createElement("label");
|
|
1137
|
+
heightLabel.innerText = "Height";
|
|
1138
|
+
heightLabel.style.display = "block";
|
|
1139
|
+
heightLabel.style.paddingLeft = "4px";
|
|
1102
1140
|
const heightInput = document.createElement('input');
|
|
1103
1141
|
heightInput.type = 'text';
|
|
1104
1142
|
heightInput.placeholder = 'Height (px)';
|
|
@@ -1109,7 +1147,7 @@ const PLUGINS = {
|
|
|
1109
1147
|
const span = document.createElement('span');
|
|
1110
1148
|
span.className = 'warning';
|
|
1111
1149
|
|
|
1112
|
-
body.append(urlInput, widthInput, heightInput, span);
|
|
1150
|
+
body.append(embedLabel, urlInput, widthLabel, widthInput, heightLabel, heightInput, span);
|
|
1113
1151
|
|
|
1114
1152
|
const footer = document.createElement('div');
|
|
1115
1153
|
const button = document.createElement('button');
|
package/utilities.js
CHANGED
|
@@ -1859,23 +1859,33 @@ const editAnchorTag = (event, core, options) => {
|
|
|
1859
1859
|
|
|
1860
1860
|
const body = document.createElement("div");
|
|
1861
1861
|
|
|
1862
|
+
const linkLabel = document.createElement("label");
|
|
1863
|
+
linkLabel.innerText = "Link";
|
|
1864
|
+
linkLabel.style.display = "block";
|
|
1865
|
+
linkLabel.style.paddingLeft = "4px";
|
|
1866
|
+
|
|
1862
1867
|
// Link Input
|
|
1863
1868
|
const inputLink = document.createElement("input");
|
|
1864
1869
|
inputLink.value = anchorLink;
|
|
1865
1870
|
inputLink.type = "text";
|
|
1866
|
-
inputLink.placeholder = "Insert
|
|
1871
|
+
inputLink.placeholder = "Insert link, mail, phone no";
|
|
1872
|
+
|
|
1873
|
+
const textLabel = document.createElement("label");
|
|
1874
|
+
textLabel.innerText = "Link Text";
|
|
1875
|
+
textLabel.style.display = "block";
|
|
1876
|
+
textLabel.style.marginTop = "8px";
|
|
1877
|
+
textLabel.style.paddingLeft = "4px";
|
|
1867
1878
|
|
|
1868
1879
|
// Text Input
|
|
1869
1880
|
const inputText = document.createElement("input");
|
|
1870
1881
|
inputText.value = anchorText;
|
|
1871
1882
|
inputText.type = "text";
|
|
1872
|
-
inputText.style.marginTop = "8px";
|
|
1873
1883
|
inputText.placeholder = "Link text";
|
|
1874
1884
|
|
|
1875
1885
|
const span = document.createElement("span");
|
|
1876
1886
|
span.className = "warning";
|
|
1877
1887
|
|
|
1878
|
-
body.append(inputLink,
|
|
1888
|
+
body.append(linkLabel, inputLink, span, textLabel, inputText);
|
|
1879
1889
|
|
|
1880
1890
|
// Footer (Buttons)
|
|
1881
1891
|
const footer = document.createElement("div");
|
|
@@ -1944,10 +1954,10 @@ const destroyAnchorPopover = (core) => {
|
|
|
1944
1954
|
};
|
|
1945
1955
|
|
|
1946
1956
|
function getCharIndex(root, node, offset) {
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1957
|
+
const range = document.createRange();
|
|
1958
|
+
range.selectNodeContents(root);
|
|
1959
|
+
range.setEnd(node, offset);
|
|
1960
|
+
return range.toString().length;
|
|
1951
1961
|
}
|
|
1952
1962
|
|
|
1953
1963
|
const getNodeAndOffsetFromIndex = (root, index) => {
|
|
@@ -2155,43 +2165,43 @@ const syncRemoteChanges = (core) => {
|
|
|
2155
2165
|
const syncRemoteChangesDebounce = debounce(syncRemoteChanges, 50)
|
|
2156
2166
|
|
|
2157
2167
|
const applyRemoteChanges = (core, diffs) => {
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
|
|
2176
|
-
requestAnimationFrame(() => {
|
|
2177
|
-
dd.apply(editor, diffs.diffs); // Apply patch
|
|
2178
|
-
core.state.previousDOM = editor.cloneNode(true); // Update snapshot
|
|
2179
|
-
|
|
2180
|
-
if (saved) {
|
|
2181
|
-
const newStart = getNodeAndOffsetFromIndex(editor, saved.start);
|
|
2182
|
-
const newEnd = getNodeAndOffsetFromIndex(editor, saved.end);
|
|
2183
|
-
if (newStart && newEnd) {
|
|
2184
|
-
const newRange = iframeWindow.createRange();
|
|
2185
|
-
newRange.setStart(newStart.node, newStart.offset);
|
|
2186
|
-
newRange.setEnd(newEnd.node, newEnd.offset);
|
|
2187
|
-
const sel = iframeWindow.getSelection();
|
|
2188
|
-
if (sel) {
|
|
2189
|
-
sel.removeAllRanges();
|
|
2190
|
-
sel.addRange(newRange);
|
|
2168
|
+
const editor = core.elements.editor;
|
|
2169
|
+
const iframeWindow = core.elements.iframeWindow;
|
|
2170
|
+
if (!editor || !diffs?.diffs) return;
|
|
2171
|
+
|
|
2172
|
+
const selection = iframeWindow.getSelection();
|
|
2173
|
+
let saved = null;
|
|
2174
|
+
|
|
2175
|
+
if (selection && selection.rangeCount > 0) {
|
|
2176
|
+
const range = selection.getRangeAt(0);
|
|
2177
|
+
if (editor.contains(range.startContainer) && editor.contains(range.endContainer)) {
|
|
2178
|
+
// Save selection as indexes (pre-change)
|
|
2179
|
+
saved = {
|
|
2180
|
+
start: getCharIndex(editor, range.startContainer, range.startOffset),
|
|
2181
|
+
end: getCharIndex(editor, range.endContainer, range.endOffset),
|
|
2182
|
+
};
|
|
2191
2183
|
}
|
|
2192
|
-
}
|
|
2193
2184
|
}
|
|
2194
|
-
|
|
2185
|
+
|
|
2186
|
+
requestAnimationFrame(() => {
|
|
2187
|
+
dd.apply(editor, diffs.diffs); // Apply patch
|
|
2188
|
+
core.state.previousDOM = editor.cloneNode(true); // Update snapshot
|
|
2189
|
+
|
|
2190
|
+
if (saved) {
|
|
2191
|
+
const newStart = getNodeAndOffsetFromIndex(editor, saved.start);
|
|
2192
|
+
const newEnd = getNodeAndOffsetFromIndex(editor, saved.end);
|
|
2193
|
+
if (newStart && newEnd) {
|
|
2194
|
+
const newRange = iframeWindow.createRange();
|
|
2195
|
+
newRange.setStart(newStart.node, newStart.offset);
|
|
2196
|
+
newRange.setEnd(newEnd.node, newEnd.offset);
|
|
2197
|
+
const sel = iframeWindow.getSelection();
|
|
2198
|
+
if (sel) {
|
|
2199
|
+
sel.removeAllRanges();
|
|
2200
|
+
sel.addRange(newRange);
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
});
|
|
2195
2205
|
}
|
|
2196
2206
|
|
|
2197
2207
|
const isLinkValid = (link) => {
|
|
@@ -2215,6 +2225,41 @@ const formatLink = (value) => {
|
|
|
2215
2225
|
return trimmed;
|
|
2216
2226
|
};
|
|
2217
2227
|
|
|
2228
|
+
const buildTributeValues = (labels = []) => {
|
|
2229
|
+
return labels.flatMap(category => ([
|
|
2230
|
+
{ key: category.name, isCategory: true },
|
|
2231
|
+
...category.fields.map(field => ({
|
|
2232
|
+
key: field.name,
|
|
2233
|
+
value: field.value,
|
|
2234
|
+
}))
|
|
2235
|
+
]));
|
|
2236
|
+
};
|
|
2237
|
+
|
|
2238
|
+
function toPx(value, el = document.body) {
|
|
2239
|
+
if (!value) return null;
|
|
2240
|
+
if (typeof value === 'number') return value;
|
|
2241
|
+
|
|
2242
|
+
const tmp = document.createElement('div');
|
|
2243
|
+
tmp.style.width = value;
|
|
2244
|
+
tmp.style.position = 'absolute';
|
|
2245
|
+
tmp.style.visibility = 'hidden';
|
|
2246
|
+
el.appendChild(tmp);
|
|
2247
|
+
const px = tmp.getBoundingClientRect().width;
|
|
2248
|
+
tmp.remove();
|
|
2249
|
+
return px;
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
const updateHeight = (core, options) => {
|
|
2253
|
+
if (options.autoHeight) {
|
|
2254
|
+
let h = core.elements.editor.scrollHeight;
|
|
2255
|
+
const minH = toPx(options.minHeight, core.elements.iframeContainer.parentElement);
|
|
2256
|
+
const maxH = toPx(options.maxHeight, core.elements.iframeContainer.parentElement);
|
|
2257
|
+
if (minH != null) h = Math.max(h, minH);
|
|
2258
|
+
if (maxH != null) h = Math.min(h, maxH);
|
|
2259
|
+
core.elements.iframeContainer.style.height = h + 'px';
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2218
2263
|
export {
|
|
2219
2264
|
cleanHTML,
|
|
2220
2265
|
debounce,
|
|
@@ -2243,4 +2288,7 @@ export {
|
|
|
2243
2288
|
updateCursorPositionDebounce,
|
|
2244
2289
|
isLinkValid,
|
|
2245
2290
|
formatLink,
|
|
2291
|
+
buildTributeValues,
|
|
2292
|
+
toPx,
|
|
2293
|
+
updateHeight,
|
|
2246
2294
|
}
|