rich-html-editor 0.2.1 → 1.0.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 +17 -5
- package/dist/{chunk-ZDGUOGND.mjs → chunk-W3ULZ2LR.mjs} +23 -1
- package/dist/chunk-W3ULZ2LR.mjs.map +1 -0
- package/dist/index.js +413 -218
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +394 -219
- package/dist/index.mjs.map +1 -1
- package/dist/{state-CZIMHTJ3.mjs → state-DA3PGFTN.mjs} +2 -2
- package/package.json +2 -2
- package/dist/chunk-ZDGUOGND.mjs.map +0 -1
- /package/dist/{state-CZIMHTJ3.mjs.map → state-DA3PGFTN.mjs.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -91,7 +91,7 @@ var init_events = __esm({
|
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
// src/core/constants.ts
|
|
94
|
-
var TOOLBAR_ID, STYLE_ID, CLASS_EDITABLE, CLASS_ACTIVE, DEFAULT_MAX_STACK, TOOLBAR_BG, TOOLBAR_BORDER, BUTTON_BORDER, BUTTON_ACTIVE_BG, BUTTON_BG, BUTTON_COLOR, INFO_COLOR, HOVER_OUTLINE, ACTIVE_OUTLINE, LABEL_BOLD, LABEL_ITALIC, LABEL_UNDERLINE, LABEL_STRIKETHROUGH, LABEL_UNDO, LABEL_REDO, LABEL_LINK, LABEL_ALIGN_LEFT, LABEL_ALIGN_CENTER, LABEL_ALIGN_RIGHT, FONT_OPTIONS, SIZE_OPTIONS, FORMAT_OPTIONS;
|
|
94
|
+
var TOOLBAR_ID, STYLE_ID, CLASS_EDITABLE, CLASS_ACTIVE, DEFAULT_MAX_STACK, TOOLBAR_BG, TOOLBAR_BORDER, BUTTON_BORDER, BUTTON_ACTIVE_BG, BUTTON_BG, BUTTON_COLOR, INFO_COLOR, HOVER_OUTLINE, ACTIVE_OUTLINE, LABEL_BOLD, LABEL_ITALIC, LABEL_UNDERLINE, LABEL_STRIKETHROUGH, LABEL_UNDO, LABEL_REDO, LABEL_LINK, LABEL_UNORDERED_LIST, LABEL_ORDERED_LIST, LABEL_ALIGN_LEFT, LABEL_ALIGN_CENTER, LABEL_ALIGN_RIGHT, FONT_OPTIONS, SIZE_OPTIONS, FORMAT_OPTIONS;
|
|
95
95
|
var init_constants = __esm({
|
|
96
96
|
"src/core/constants.ts"() {
|
|
97
97
|
"use strict";
|
|
@@ -116,6 +116,26 @@ var init_constants = __esm({
|
|
|
116
116
|
LABEL_UNDO = "\u21BA";
|
|
117
117
|
LABEL_REDO = "\u21BB";
|
|
118
118
|
LABEL_LINK = "\u{1F517}";
|
|
119
|
+
LABEL_UNORDERED_LIST = `
|
|
120
|
+
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
121
|
+
<circle cx="3" cy="4" r="1" fill="currentColor" />
|
|
122
|
+
<rect x="6" y="3" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
123
|
+
<circle cx="3" cy="8" r="1" fill="currentColor" />
|
|
124
|
+
<rect x="6" y="7" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
125
|
+
<circle cx="3" cy="12" r="1" fill="currentColor" />
|
|
126
|
+
<rect x="6" y="11" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
127
|
+
</svg>
|
|
128
|
+
`;
|
|
129
|
+
LABEL_ORDERED_LIST = `
|
|
130
|
+
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
131
|
+
<text x="1" y="4" font-size="4" fill="currentColor">1.</text>
|
|
132
|
+
<rect x="6" y="3" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
133
|
+
<text x="1" y="8" font-size="4" fill="currentColor">2.</text>
|
|
134
|
+
<rect x="6" y="7" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
135
|
+
<text x="1" y="12" font-size="4" fill="currentColor">3.</text>
|
|
136
|
+
<rect x="6" y="11" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
137
|
+
</svg>
|
|
138
|
+
`;
|
|
119
139
|
LABEL_ALIGN_LEFT = `
|
|
120
140
|
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
121
141
|
<rect x="1" y="2" width="10" height="2" rx="0.5" fill="currentColor" />
|
|
@@ -761,219 +781,76 @@ function injectStyles(doc) {
|
|
|
761
781
|
styleEl.textContent = css;
|
|
762
782
|
}
|
|
763
783
|
|
|
764
|
-
// src/toolbar/
|
|
784
|
+
// src/toolbar/render.ts
|
|
765
785
|
init_constants();
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
786
|
+
|
|
787
|
+
// src/toolbar/color.ts
|
|
788
|
+
function makeColorInput(doc, options, title, command, initialColor) {
|
|
789
|
+
const input = doc.createElement("input");
|
|
790
|
+
input.type = "color";
|
|
791
|
+
input.className = "toolbar-color-input";
|
|
792
|
+
const wrapper = doc.createElement("label");
|
|
793
|
+
wrapper.className = "color-label";
|
|
794
|
+
wrapper.appendChild(doc.createTextNode(title + " "));
|
|
795
|
+
wrapper.appendChild(input);
|
|
796
|
+
let savedRange = null;
|
|
797
|
+
input.addEventListener("pointerdown", () => {
|
|
798
|
+
const s = doc.getSelection();
|
|
799
|
+
if (s && s.rangeCount) savedRange = s.getRangeAt(0).cloneRange();
|
|
800
|
+
});
|
|
801
|
+
input.onchange = (e) => {
|
|
802
|
+
try {
|
|
803
|
+
const s = doc.getSelection();
|
|
804
|
+
if (savedRange && s) {
|
|
805
|
+
s.removeAllRanges();
|
|
806
|
+
s.addRange(savedRange);
|
|
807
|
+
}
|
|
808
|
+
} catch (err) {
|
|
780
809
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
ev.preventDefault();
|
|
791
|
-
btn.click();
|
|
810
|
+
options.onCommand(command, e.target.value);
|
|
811
|
+
savedRange = null;
|
|
812
|
+
};
|
|
813
|
+
function rgbToHex(input2) {
|
|
814
|
+
if (!input2) return null;
|
|
815
|
+
const v = input2.trim();
|
|
816
|
+
if (v.startsWith("#")) {
|
|
817
|
+
if (v.length === 4) {
|
|
818
|
+
return ("#" + v[1] + v[1] + v[2] + v[2] + v[3] + v[3]).toLowerCase();
|
|
792
819
|
}
|
|
793
|
-
|
|
794
|
-
return btn;
|
|
795
|
-
}
|
|
796
|
-
function makeSelect(title, command, optionsList, initialValue) {
|
|
797
|
-
const select = doc.createElement("select");
|
|
798
|
-
select.title = title;
|
|
799
|
-
select.setAttribute("aria-label", title);
|
|
800
|
-
select.appendChild(new Option(title, "", true, true));
|
|
801
|
-
for (const opt of optionsList) {
|
|
802
|
-
select.appendChild(new Option(opt.label, opt.value));
|
|
820
|
+
return v.toLowerCase();
|
|
803
821
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
822
|
+
const rgbMatch = v.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/i);
|
|
823
|
+
if (rgbMatch) {
|
|
824
|
+
const r = Number(rgbMatch[1]);
|
|
825
|
+
const g = Number(rgbMatch[2]);
|
|
826
|
+
const b = Number(rgbMatch[3]);
|
|
827
|
+
const hex = "#" + [r, g, b].map((n) => n.toString(16).padStart(2, "0")).join("").toLowerCase();
|
|
828
|
+
return hex;
|
|
807
829
|
}
|
|
808
|
-
|
|
809
|
-
const val = e.target.value;
|
|
810
|
-
options.onCommand(command, val);
|
|
811
|
-
select.selectedIndex = 0;
|
|
812
|
-
};
|
|
813
|
-
return select;
|
|
830
|
+
return null;
|
|
814
831
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
wrapper.appendChild(doc.createTextNode(title + " "));
|
|
822
|
-
wrapper.appendChild(input);
|
|
823
|
-
let savedRange = null;
|
|
824
|
-
input.addEventListener("pointerdown", () => {
|
|
825
|
-
const s = doc.getSelection();
|
|
826
|
-
if (s && s.rangeCount) savedRange = s.getRangeAt(0).cloneRange();
|
|
827
|
-
});
|
|
828
|
-
input.onchange = (e) => {
|
|
829
|
-
try {
|
|
830
|
-
const s = doc.getSelection();
|
|
831
|
-
if (savedRange && s) {
|
|
832
|
-
s.removeAllRanges();
|
|
833
|
-
s.addRange(savedRange);
|
|
834
|
-
}
|
|
835
|
-
} catch (err) {
|
|
836
|
-
}
|
|
837
|
-
options.onCommand(command, e.target.value);
|
|
838
|
-
savedRange = null;
|
|
839
|
-
};
|
|
840
|
-
function rgbToHex(input2) {
|
|
841
|
-
if (!input2) return null;
|
|
842
|
-
const v = input2.trim();
|
|
843
|
-
if (v.startsWith("#")) {
|
|
844
|
-
if (v.length === 4) {
|
|
845
|
-
return ("#" + v[1] + v[1] + v[2] + v[2] + v[3] + v[3]).toLowerCase();
|
|
846
|
-
}
|
|
847
|
-
return v.toLowerCase();
|
|
848
|
-
}
|
|
849
|
-
const rgbMatch = v.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/i);
|
|
850
|
-
if (rgbMatch) {
|
|
851
|
-
const r = Number(rgbMatch[1]);
|
|
852
|
-
const g = Number(rgbMatch[2]);
|
|
853
|
-
const b = Number(rgbMatch[3]);
|
|
854
|
-
const hex = "#" + [r, g, b].map((n) => n.toString(16).padStart(2, "0")).join("").toLowerCase();
|
|
855
|
-
return hex;
|
|
832
|
+
const setColor = (val) => {
|
|
833
|
+
if (!val) return;
|
|
834
|
+
const hex = rgbToHex(val) || val;
|
|
835
|
+
try {
|
|
836
|
+
if (hex && hex.startsWith("#") && input.value !== hex) {
|
|
837
|
+
input.value = hex;
|
|
856
838
|
}
|
|
857
|
-
|
|
839
|
+
} catch (e) {
|
|
858
840
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
setColor(val);
|
|
873
|
-
});
|
|
874
|
-
input.title = title;
|
|
875
|
-
input.setAttribute("aria-label", title);
|
|
876
|
-
return wrapper;
|
|
877
|
-
}
|
|
878
|
-
const format = options.getFormatState();
|
|
879
|
-
function makeGroup() {
|
|
880
|
-
const g = doc.createElement("div");
|
|
881
|
-
g.className = "toolbar-group";
|
|
882
|
-
return g;
|
|
883
|
-
}
|
|
884
|
-
function makeSep() {
|
|
885
|
-
const s = doc.createElement("div");
|
|
886
|
-
s.className = "toolbar-sep";
|
|
887
|
-
return s;
|
|
888
|
-
}
|
|
889
|
-
const undoBtn = makeButton(
|
|
890
|
-
LABEL_UNDO,
|
|
891
|
-
"Undo",
|
|
892
|
-
"undo",
|
|
893
|
-
void 0,
|
|
894
|
-
false,
|
|
895
|
-
!options.canUndo()
|
|
896
|
-
);
|
|
897
|
-
undoBtn.onclick = () => options.onUndo();
|
|
898
|
-
const redoBtn = makeButton(
|
|
899
|
-
LABEL_REDO,
|
|
900
|
-
"Redo",
|
|
901
|
-
"redo",
|
|
902
|
-
void 0,
|
|
903
|
-
false,
|
|
904
|
-
!options.canRedo()
|
|
905
|
-
);
|
|
906
|
-
redoBtn.onclick = () => options.onRedo();
|
|
907
|
-
const grp1 = makeGroup();
|
|
908
|
-
grp1.appendChild(undoBtn);
|
|
909
|
-
grp1.appendChild(redoBtn);
|
|
910
|
-
toolbar.appendChild(grp1);
|
|
911
|
-
toolbar.appendChild(makeSep());
|
|
912
|
-
const grp2 = makeGroup();
|
|
913
|
-
grp2.className = "toolbar-group collapse-on-small";
|
|
914
|
-
grp2.appendChild(
|
|
915
|
-
makeSelect(
|
|
916
|
-
"Format",
|
|
917
|
-
"formatBlock",
|
|
918
|
-
FORMAT_OPTIONS,
|
|
919
|
-
format.formatBlock
|
|
920
|
-
)
|
|
921
|
-
);
|
|
922
|
-
grp2.appendChild(
|
|
923
|
-
makeSelect("Font", "fontName", FONT_OPTIONS, format.fontName)
|
|
924
|
-
);
|
|
925
|
-
grp2.appendChild(
|
|
926
|
-
makeSelect("Size", "fontSize", SIZE_OPTIONS, format.fontSize)
|
|
927
|
-
);
|
|
928
|
-
toolbar.appendChild(grp2);
|
|
929
|
-
toolbar.appendChild(makeSep());
|
|
930
|
-
const grp3 = makeGroup();
|
|
931
|
-
grp3.appendChild(
|
|
932
|
-
makeButton(LABEL_BOLD, "Bold", "bold", void 0, format.bold)
|
|
933
|
-
);
|
|
934
|
-
grp3.appendChild(
|
|
935
|
-
makeButton(LABEL_ITALIC, "Italic", "italic", void 0, format.italic)
|
|
936
|
-
);
|
|
937
|
-
grp3.appendChild(
|
|
938
|
-
makeButton(
|
|
939
|
-
LABEL_UNDERLINE,
|
|
940
|
-
"Underline",
|
|
941
|
-
"underline",
|
|
942
|
-
void 0,
|
|
943
|
-
format.underline
|
|
944
|
-
)
|
|
945
|
-
);
|
|
946
|
-
grp3.appendChild(makeButton(LABEL_STRIKETHROUGH, "Strikethrough", "strike"));
|
|
947
|
-
toolbar.appendChild(grp3);
|
|
948
|
-
toolbar.appendChild(makeSep());
|
|
949
|
-
const grp4 = makeGroup();
|
|
950
|
-
grp4.appendChild(makeButton(LABEL_ALIGN_LEFT, "Align left", "align", "left"));
|
|
951
|
-
grp4.appendChild(
|
|
952
|
-
makeButton(LABEL_ALIGN_CENTER, "Align center", "align", "center")
|
|
953
|
-
);
|
|
954
|
-
grp4.appendChild(
|
|
955
|
-
makeButton(LABEL_ALIGN_RIGHT, "Align right", "align", "right")
|
|
956
|
-
);
|
|
957
|
-
toolbar.appendChild(grp4);
|
|
958
|
-
toolbar.appendChild(makeSep());
|
|
959
|
-
const grp5 = makeGroup();
|
|
960
|
-
grp5.className = "toolbar-group collapse-on-small";
|
|
961
|
-
grp5.appendChild(
|
|
962
|
-
makeColorInput("Text color", "foreColor", format.foreColor)
|
|
963
|
-
);
|
|
964
|
-
grp5.appendChild(
|
|
965
|
-
makeColorInput(
|
|
966
|
-
"Highlight color",
|
|
967
|
-
"hiliteColor",
|
|
968
|
-
format.hiliteColor
|
|
969
|
-
)
|
|
970
|
-
);
|
|
971
|
-
toolbar.appendChild(grp5);
|
|
972
|
-
toolbar.appendChild(makeSep());
|
|
973
|
-
const grp6 = makeGroup();
|
|
974
|
-
grp6.className = "toolbar-group collapse-on-small";
|
|
975
|
-
grp6.appendChild(makeButton(LABEL_LINK, "Insert link", "link"));
|
|
976
|
-
toolbar.appendChild(grp6);
|
|
841
|
+
};
|
|
842
|
+
if (initialColor) setColor(initialColor);
|
|
843
|
+
input.addEventListener("input", (e) => {
|
|
844
|
+
const val = e.target.value;
|
|
845
|
+
setColor(val);
|
|
846
|
+
});
|
|
847
|
+
input.title = title;
|
|
848
|
+
input.setAttribute("aria-label", title);
|
|
849
|
+
return wrapper;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// src/toolbar/overflow.ts
|
|
853
|
+
function setupOverflow(doc, toolbar, options, format, helpers) {
|
|
977
854
|
const overflowBtn = doc.createElement("button");
|
|
978
855
|
overflowBtn.type = "button";
|
|
979
856
|
overflowBtn.className = "toolbar-overflow-btn";
|
|
@@ -1000,7 +877,7 @@ function injectToolbar(doc, options) {
|
|
|
1000
877
|
overflowBtn.setAttribute("aria-expanded", "false");
|
|
1001
878
|
overflowBtn.focus();
|
|
1002
879
|
}
|
|
1003
|
-
overflowBtn.addEventListener("click", (
|
|
880
|
+
overflowBtn.addEventListener("click", () => {
|
|
1004
881
|
if (overflowMenu.hidden) openOverflow();
|
|
1005
882
|
else closeOverflow();
|
|
1006
883
|
});
|
|
@@ -1027,35 +904,105 @@ function injectToolbar(doc, options) {
|
|
|
1027
904
|
}
|
|
1028
905
|
});
|
|
1029
906
|
overflowMenu.appendChild(
|
|
1030
|
-
makeSelect(
|
|
907
|
+
helpers.makeSelect(
|
|
1031
908
|
"Format",
|
|
1032
909
|
"formatBlock",
|
|
1033
|
-
|
|
910
|
+
window.RHE_FORMAT_OPTIONS || [],
|
|
1034
911
|
format.formatBlock
|
|
1035
912
|
)
|
|
1036
913
|
);
|
|
1037
914
|
overflowMenu.appendChild(
|
|
1038
|
-
makeSelect(
|
|
915
|
+
helpers.makeSelect(
|
|
916
|
+
"Font",
|
|
917
|
+
"fontName",
|
|
918
|
+
window.RHE_FONT_OPTIONS || [],
|
|
919
|
+
format.fontName
|
|
920
|
+
)
|
|
1039
921
|
);
|
|
1040
922
|
overflowMenu.appendChild(
|
|
1041
|
-
makeSelect(
|
|
923
|
+
helpers.makeSelect(
|
|
924
|
+
"Size",
|
|
925
|
+
"fontSize",
|
|
926
|
+
window.RHE_SIZE_OPTIONS || [],
|
|
927
|
+
format.fontSize
|
|
928
|
+
)
|
|
1042
929
|
);
|
|
1043
930
|
overflowMenu.appendChild(
|
|
1044
|
-
makeColorInput("Text color", "foreColor", format.foreColor)
|
|
931
|
+
helpers.makeColorInput("Text color", "foreColor", format.foreColor)
|
|
1045
932
|
);
|
|
1046
933
|
overflowMenu.appendChild(
|
|
1047
|
-
makeColorInput(
|
|
934
|
+
helpers.makeColorInput(
|
|
1048
935
|
"Highlight color",
|
|
1049
936
|
"hiliteColor",
|
|
1050
937
|
format.hiliteColor
|
|
1051
938
|
)
|
|
1052
939
|
);
|
|
1053
|
-
overflowMenu.appendChild(makeButton(
|
|
1054
|
-
const overflowWrap = makeGroup();
|
|
940
|
+
overflowMenu.appendChild(helpers.makeButton("Link", "Insert link", "link"));
|
|
941
|
+
const overflowWrap = helpers.makeGroup();
|
|
1055
942
|
overflowWrap.className = "toolbar-group toolbar-overflow-wrap";
|
|
1056
943
|
overflowWrap.appendChild(overflowBtn);
|
|
1057
944
|
overflowWrap.appendChild(overflowMenu);
|
|
1058
945
|
toolbar.appendChild(overflowWrap);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// src/toolbar/buttons.ts
|
|
949
|
+
function makeButton(doc, options, label, title, command, value, isActive, disabled) {
|
|
950
|
+
const btn = doc.createElement("button");
|
|
951
|
+
btn.type = "button";
|
|
952
|
+
if (label && label.trim().startsWith("<")) {
|
|
953
|
+
btn.innerHTML = label;
|
|
954
|
+
} else {
|
|
955
|
+
btn.textContent = label;
|
|
956
|
+
}
|
|
957
|
+
btn.title = title;
|
|
958
|
+
btn.setAttribute("aria-label", title);
|
|
959
|
+
if (typeof isActive !== "undefined")
|
|
960
|
+
btn.setAttribute("aria-pressed", String(!!isActive));
|
|
961
|
+
btn.tabIndex = 0;
|
|
962
|
+
if (disabled) btn.disabled = true;
|
|
963
|
+
btn.onclick = () => options.onCommand(command, value);
|
|
964
|
+
btn.addEventListener("keydown", (ev) => {
|
|
965
|
+
if (ev.key === "Enter" || ev.key === " ") {
|
|
966
|
+
ev.preventDefault();
|
|
967
|
+
btn.click();
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
return btn;
|
|
971
|
+
}
|
|
972
|
+
function makeGroup(doc) {
|
|
973
|
+
const g = doc.createElement("div");
|
|
974
|
+
g.className = "toolbar-group";
|
|
975
|
+
return g;
|
|
976
|
+
}
|
|
977
|
+
function makeSep(doc) {
|
|
978
|
+
const s = doc.createElement("div");
|
|
979
|
+
s.className = "toolbar-sep";
|
|
980
|
+
return s;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// src/toolbar/selects.ts
|
|
984
|
+
function makeSelect(doc, options, title, command, optionsList, initialValue) {
|
|
985
|
+
const select = doc.createElement("select");
|
|
986
|
+
select.title = title;
|
|
987
|
+
select.setAttribute("aria-label", title);
|
|
988
|
+
select.appendChild(new Option(title, "", true, true));
|
|
989
|
+
for (const opt of optionsList) {
|
|
990
|
+
select.appendChild(new Option(opt.label, opt.value));
|
|
991
|
+
}
|
|
992
|
+
try {
|
|
993
|
+
if (initialValue) select.value = initialValue;
|
|
994
|
+
} catch (e) {
|
|
995
|
+
}
|
|
996
|
+
select.onchange = (e) => {
|
|
997
|
+
const val = e.target.value;
|
|
998
|
+
options.onCommand(command, val);
|
|
999
|
+
select.selectedIndex = 0;
|
|
1000
|
+
};
|
|
1001
|
+
return select;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// src/toolbar/navigation.ts
|
|
1005
|
+
function setupNavigation(toolbar) {
|
|
1059
1006
|
toolbar.addEventListener("keydown", (e) => {
|
|
1060
1007
|
const focusable = Array.from(
|
|
1061
1008
|
toolbar.querySelectorAll("button, select, input, [tabindex]")
|
|
@@ -1078,6 +1025,158 @@ function injectToolbar(doc, options) {
|
|
|
1078
1025
|
focusable[focusable.length - 1].focus();
|
|
1079
1026
|
}
|
|
1080
1027
|
});
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// src/toolbar/render.ts
|
|
1031
|
+
function injectToolbar(doc, options) {
|
|
1032
|
+
const existing = doc.getElementById(TOOLBAR_ID);
|
|
1033
|
+
if (existing) existing.remove();
|
|
1034
|
+
const toolbar = doc.createElement("div");
|
|
1035
|
+
toolbar.id = TOOLBAR_ID;
|
|
1036
|
+
toolbar.setAttribute("role", "toolbar");
|
|
1037
|
+
toolbar.setAttribute("aria-label", "Rich text editor toolbar");
|
|
1038
|
+
const makeButton2 = (label, title, command, value, isActive, disabled) => makeButton(
|
|
1039
|
+
doc,
|
|
1040
|
+
{ onCommand: options.onCommand },
|
|
1041
|
+
label,
|
|
1042
|
+
title,
|
|
1043
|
+
command,
|
|
1044
|
+
value,
|
|
1045
|
+
isActive,
|
|
1046
|
+
disabled
|
|
1047
|
+
);
|
|
1048
|
+
const makeSelect2 = (title, command, optionsList, initialValue) => makeSelect(
|
|
1049
|
+
doc,
|
|
1050
|
+
{ onCommand: options.onCommand },
|
|
1051
|
+
title,
|
|
1052
|
+
command,
|
|
1053
|
+
optionsList,
|
|
1054
|
+
initialValue
|
|
1055
|
+
);
|
|
1056
|
+
const format = options.getFormatState();
|
|
1057
|
+
const makeGroup2 = () => makeGroup(doc);
|
|
1058
|
+
const makeSep2 = () => makeSep(doc);
|
|
1059
|
+
const undoBtn = makeButton2(
|
|
1060
|
+
LABEL_UNDO,
|
|
1061
|
+
"Undo",
|
|
1062
|
+
"undo",
|
|
1063
|
+
void 0,
|
|
1064
|
+
false,
|
|
1065
|
+
!options.canUndo()
|
|
1066
|
+
);
|
|
1067
|
+
undoBtn.onclick = () => options.onUndo();
|
|
1068
|
+
const redoBtn = makeButton2(
|
|
1069
|
+
LABEL_REDO,
|
|
1070
|
+
"Redo",
|
|
1071
|
+
"redo",
|
|
1072
|
+
void 0,
|
|
1073
|
+
false,
|
|
1074
|
+
!options.canRedo()
|
|
1075
|
+
);
|
|
1076
|
+
redoBtn.onclick = () => options.onRedo();
|
|
1077
|
+
const grp1 = makeGroup2();
|
|
1078
|
+
grp1.appendChild(undoBtn);
|
|
1079
|
+
grp1.appendChild(redoBtn);
|
|
1080
|
+
toolbar.appendChild(grp1);
|
|
1081
|
+
toolbar.appendChild(makeSep2());
|
|
1082
|
+
const grp2 = makeGroup2();
|
|
1083
|
+
grp2.className = "toolbar-group collapse-on-small";
|
|
1084
|
+
grp2.appendChild(
|
|
1085
|
+
makeSelect2(
|
|
1086
|
+
"Format",
|
|
1087
|
+
"formatBlock",
|
|
1088
|
+
FORMAT_OPTIONS,
|
|
1089
|
+
format.formatBlock
|
|
1090
|
+
)
|
|
1091
|
+
);
|
|
1092
|
+
grp2.appendChild(
|
|
1093
|
+
makeSelect2("Font", "fontName", FONT_OPTIONS, format.fontName)
|
|
1094
|
+
);
|
|
1095
|
+
grp2.appendChild(
|
|
1096
|
+
makeSelect2("Size", "fontSize", SIZE_OPTIONS, format.fontSize)
|
|
1097
|
+
);
|
|
1098
|
+
toolbar.appendChild(grp2);
|
|
1099
|
+
toolbar.appendChild(makeSep2());
|
|
1100
|
+
const grp3 = makeGroup2();
|
|
1101
|
+
grp3.appendChild(
|
|
1102
|
+
makeButton2(LABEL_BOLD, "Bold", "bold", void 0, format.bold)
|
|
1103
|
+
);
|
|
1104
|
+
grp3.appendChild(
|
|
1105
|
+
makeButton2(LABEL_ITALIC, "Italic", "italic", void 0, format.italic)
|
|
1106
|
+
);
|
|
1107
|
+
grp3.appendChild(
|
|
1108
|
+
makeButton2(
|
|
1109
|
+
LABEL_UNDERLINE,
|
|
1110
|
+
"Underline",
|
|
1111
|
+
"underline",
|
|
1112
|
+
void 0,
|
|
1113
|
+
format.underline
|
|
1114
|
+
)
|
|
1115
|
+
);
|
|
1116
|
+
grp3.appendChild(makeButton2(LABEL_STRIKETHROUGH, "Strikethrough", "strike"));
|
|
1117
|
+
grp3.appendChild(
|
|
1118
|
+
makeButton2(
|
|
1119
|
+
LABEL_UNORDERED_LIST,
|
|
1120
|
+
"Unordered list",
|
|
1121
|
+
"unorderedList",
|
|
1122
|
+
void 0,
|
|
1123
|
+
format.listType === "ul"
|
|
1124
|
+
)
|
|
1125
|
+
);
|
|
1126
|
+
grp3.appendChild(
|
|
1127
|
+
makeButton2(
|
|
1128
|
+
LABEL_ORDERED_LIST,
|
|
1129
|
+
"Ordered list",
|
|
1130
|
+
"orderedList",
|
|
1131
|
+
void 0,
|
|
1132
|
+
format.listType === "ol"
|
|
1133
|
+
)
|
|
1134
|
+
);
|
|
1135
|
+
toolbar.appendChild(grp3);
|
|
1136
|
+
toolbar.appendChild(makeSep2());
|
|
1137
|
+
const grp4 = makeGroup2();
|
|
1138
|
+
grp4.appendChild(makeButton2(LABEL_ALIGN_LEFT, "Align left", "align", "left"));
|
|
1139
|
+
grp4.appendChild(
|
|
1140
|
+
makeButton2(LABEL_ALIGN_CENTER, "Align center", "align", "center")
|
|
1141
|
+
);
|
|
1142
|
+
grp4.appendChild(
|
|
1143
|
+
makeButton2(LABEL_ALIGN_RIGHT, "Align right", "align", "right")
|
|
1144
|
+
);
|
|
1145
|
+
toolbar.appendChild(grp4);
|
|
1146
|
+
toolbar.appendChild(makeSep2());
|
|
1147
|
+
const grp5 = makeGroup2();
|
|
1148
|
+
grp5.className = "toolbar-group collapse-on-small";
|
|
1149
|
+
grp5.appendChild(
|
|
1150
|
+
makeColorInput(
|
|
1151
|
+
doc,
|
|
1152
|
+
options,
|
|
1153
|
+
"Text color",
|
|
1154
|
+
"foreColor",
|
|
1155
|
+
format.foreColor
|
|
1156
|
+
)
|
|
1157
|
+
);
|
|
1158
|
+
grp5.appendChild(
|
|
1159
|
+
makeColorInput(
|
|
1160
|
+
doc,
|
|
1161
|
+
options,
|
|
1162
|
+
"Highlight color",
|
|
1163
|
+
"hiliteColor",
|
|
1164
|
+
format.hiliteColor
|
|
1165
|
+
)
|
|
1166
|
+
);
|
|
1167
|
+
toolbar.appendChild(grp5);
|
|
1168
|
+
toolbar.appendChild(makeSep2());
|
|
1169
|
+
const grp6 = makeGroup2();
|
|
1170
|
+
grp6.className = "toolbar-group collapse-on-small";
|
|
1171
|
+
grp6.appendChild(makeButton2(LABEL_LINK, "Insert link", "link"));
|
|
1172
|
+
toolbar.appendChild(grp6);
|
|
1173
|
+
setupOverflow(doc, toolbar, options, format, {
|
|
1174
|
+
makeSelect: makeSelect2,
|
|
1175
|
+
makeColorInput: (title, command, initial) => makeColorInput(doc, options, title, command, initial),
|
|
1176
|
+
makeButton: makeButton2,
|
|
1177
|
+
makeGroup: makeGroup2
|
|
1178
|
+
});
|
|
1179
|
+
setupNavigation(toolbar);
|
|
1081
1180
|
doc.body.insertBefore(toolbar, doc.body.firstChild);
|
|
1082
1181
|
}
|
|
1083
1182
|
|
|
@@ -1120,7 +1219,8 @@ function computeFormatState(doc) {
|
|
|
1120
1219
|
hiliteColor: null,
|
|
1121
1220
|
fontName: null,
|
|
1122
1221
|
fontSize: null,
|
|
1123
|
-
formatBlock: null
|
|
1222
|
+
formatBlock: null,
|
|
1223
|
+
listType: null
|
|
1124
1224
|
};
|
|
1125
1225
|
const computed = (_a = doc.defaultView) == null ? void 0 : _a.getComputedStyle(el);
|
|
1126
1226
|
const bold = !!(el.closest("strong, b") || computed && (computed.fontWeight === "700" || Number(computed.fontWeight) >= 700));
|
|
@@ -1156,6 +1256,18 @@ function computeFormatState(doc) {
|
|
|
1156
1256
|
blockEl = blockEl.parentElement;
|
|
1157
1257
|
}
|
|
1158
1258
|
const formatBlock = blockEl ? blockEl.tagName.toLowerCase() : null;
|
|
1259
|
+
let listType = null;
|
|
1260
|
+
try {
|
|
1261
|
+
if (blockEl && blockEl.tagName === "LI") {
|
|
1262
|
+
const list = blockEl.closest("ul,ol");
|
|
1263
|
+
if (list) listType = list.tagName.toLowerCase();
|
|
1264
|
+
} else {
|
|
1265
|
+
const possible = el.closest("ul,ol");
|
|
1266
|
+
if (possible) listType = possible.tagName.toLowerCase();
|
|
1267
|
+
}
|
|
1268
|
+
} catch (e) {
|
|
1269
|
+
listType = null;
|
|
1270
|
+
}
|
|
1159
1271
|
return {
|
|
1160
1272
|
bold,
|
|
1161
1273
|
italic,
|
|
@@ -1164,7 +1276,8 @@ function computeFormatState(doc) {
|
|
|
1164
1276
|
hiliteColor,
|
|
1165
1277
|
fontName,
|
|
1166
1278
|
fontSize,
|
|
1167
|
-
formatBlock
|
|
1279
|
+
formatBlock,
|
|
1280
|
+
listType
|
|
1168
1281
|
};
|
|
1169
1282
|
} catch (err) {
|
|
1170
1283
|
return {
|
|
@@ -1175,7 +1288,8 @@ function computeFormatState(doc) {
|
|
|
1175
1288
|
hiliteColor: null,
|
|
1176
1289
|
fontName: null,
|
|
1177
1290
|
fontSize: null,
|
|
1178
|
-
formatBlock: null
|
|
1291
|
+
formatBlock: null,
|
|
1292
|
+
listType: null
|
|
1179
1293
|
};
|
|
1180
1294
|
}
|
|
1181
1295
|
}
|
|
@@ -1571,6 +1685,9 @@ function applyStandaloneCommand(command, value) {
|
|
|
1571
1685
|
} else {
|
|
1572
1686
|
wrapSelectionWithElement(doc, tag);
|
|
1573
1687
|
}
|
|
1688
|
+
} else if (command === "unorderedList" || command === "orderedList") {
|
|
1689
|
+
const tag = command === "unorderedList" ? "ul" : "ol";
|
|
1690
|
+
toggleList(doc, tag);
|
|
1574
1691
|
}
|
|
1575
1692
|
pushStandaloneSnapshot();
|
|
1576
1693
|
} catch (error) {
|
|
@@ -1606,6 +1723,57 @@ function wrapSelectionWithElement(doc, tagName, style) {
|
|
|
1606
1723
|
newRange.selectNodeContents(wrapper);
|
|
1607
1724
|
sel.addRange(newRange);
|
|
1608
1725
|
}
|
|
1726
|
+
function toggleList(doc, listTag) {
|
|
1727
|
+
var _a, _b;
|
|
1728
|
+
const sel = doc.getSelection();
|
|
1729
|
+
if (!sel || !sel.rangeCount) return;
|
|
1730
|
+
const node = sel.anchorNode || null;
|
|
1731
|
+
const block = findBlockAncestor(node);
|
|
1732
|
+
if (block && block.tagName === "LI" && block.parentElement) {
|
|
1733
|
+
const parentList = block.parentElement;
|
|
1734
|
+
const parentTag = parentList.tagName.toLowerCase();
|
|
1735
|
+
if (parentTag === listTag) {
|
|
1736
|
+
const frag = doc.createDocumentFragment();
|
|
1737
|
+
Array.from(parentList.children).forEach((li2) => {
|
|
1738
|
+
const p = doc.createElement("p");
|
|
1739
|
+
while (li2.firstChild) p.appendChild(li2.firstChild);
|
|
1740
|
+
frag.appendChild(p);
|
|
1741
|
+
});
|
|
1742
|
+
(_a = parentList.parentElement) == null ? void 0 : _a.replaceChild(frag, parentList);
|
|
1743
|
+
return;
|
|
1744
|
+
} else {
|
|
1745
|
+
const newList = doc.createElement(listTag);
|
|
1746
|
+
while (parentList.firstChild) newList.appendChild(parentList.firstChild);
|
|
1747
|
+
(_b = parentList.parentElement) == null ? void 0 : _b.replaceChild(newList, parentList);
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
const range = sel.getRangeAt(0);
|
|
1752
|
+
if (range.collapsed) {
|
|
1753
|
+
const list2 = doc.createElement(listTag);
|
|
1754
|
+
const li2 = doc.createElement("li");
|
|
1755
|
+
const zw = doc.createTextNode("\u200B");
|
|
1756
|
+
li2.appendChild(zw);
|
|
1757
|
+
list2.appendChild(li2);
|
|
1758
|
+
range.insertNode(list2);
|
|
1759
|
+
const newRange2 = doc.createRange();
|
|
1760
|
+
newRange2.setStart(zw, 1);
|
|
1761
|
+
newRange2.collapse(true);
|
|
1762
|
+
sel.removeAllRanges();
|
|
1763
|
+
sel.addRange(newRange2);
|
|
1764
|
+
return;
|
|
1765
|
+
}
|
|
1766
|
+
const content = range.extractContents();
|
|
1767
|
+
const list = doc.createElement(listTag);
|
|
1768
|
+
const li = doc.createElement("li");
|
|
1769
|
+
li.appendChild(content);
|
|
1770
|
+
list.appendChild(li);
|
|
1771
|
+
range.insertNode(list);
|
|
1772
|
+
sel.removeAllRanges();
|
|
1773
|
+
const newRange = doc.createRange();
|
|
1774
|
+
newRange.selectNodeContents(li);
|
|
1775
|
+
sel.addRange(newRange);
|
|
1776
|
+
}
|
|
1609
1777
|
function findBlockAncestor(node) {
|
|
1610
1778
|
let n = node;
|
|
1611
1779
|
const BLOCKS = [
|
|
@@ -1748,6 +1916,33 @@ function attachStandaloneHandlers(doc) {
|
|
|
1748
1916
|
},
|
|
1749
1917
|
true
|
|
1750
1918
|
);
|
|
1919
|
+
doc.addEventListener(
|
|
1920
|
+
"keydown",
|
|
1921
|
+
(e) => {
|
|
1922
|
+
if (e.key !== "Enter") return;
|
|
1923
|
+
if (e.shiftKey) return;
|
|
1924
|
+
const sel = doc.getSelection();
|
|
1925
|
+
if (!sel || !sel.rangeCount) return;
|
|
1926
|
+
const node = sel.anchorNode;
|
|
1927
|
+
const el = node && node.nodeType === Node.ELEMENT_NODE ? node : node && node.parentElement || null;
|
|
1928
|
+
if (!el) return;
|
|
1929
|
+
const li = el.closest("li");
|
|
1930
|
+
if (!li || !li.parentElement) return;
|
|
1931
|
+
e.preventDefault();
|
|
1932
|
+
const list = li.parentElement;
|
|
1933
|
+
const newLi = doc.createElement("li");
|
|
1934
|
+
const zw = doc.createTextNode("\u200B");
|
|
1935
|
+
newLi.appendChild(zw);
|
|
1936
|
+
if (li.nextSibling) list.insertBefore(newLi, li.nextSibling);
|
|
1937
|
+
else list.appendChild(newLi);
|
|
1938
|
+
const range = doc.createRange();
|
|
1939
|
+
range.setStart(zw, 1);
|
|
1940
|
+
range.collapse(true);
|
|
1941
|
+
sel.removeAllRanges();
|
|
1942
|
+
sel.addRange(range);
|
|
1943
|
+
},
|
|
1944
|
+
true
|
|
1945
|
+
);
|
|
1751
1946
|
}
|
|
1752
1947
|
|
|
1753
1948
|
// src/core/editor.ts
|