rich-html-editor 0.2.1 → 0.2.2
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 +14 -4
- package/dist/index.js +275 -214
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +273 -214
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# rich-html-editor
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/rich-html-editor)
|
|
4
|
+
|
|
3
5
|
A framework-agnostic, plug-and-play rich HTML editor library for adding WYSIWYG editing capabilities to your web applications.
|
|
4
6
|
|
|
5
7
|
## Features
|
|
@@ -52,9 +54,11 @@ Vanilla JavaScript
|
|
|
52
54
|
```html
|
|
53
55
|
<iframe id="editor" srcdoc="<body><div class=\"editable\">Edit me</div></body>"></iframe>
|
|
54
56
|
<script type="module">
|
|
55
|
-
import { initRichEditor } from './dist/index.mjs';
|
|
57
|
+
import { initRichEditor, getCleanHTML } from './dist/index.mjs';
|
|
56
58
|
const iframe = document.getElementById('editor');
|
|
57
59
|
initRichEditor(iframe);
|
|
60
|
+
// later, retrieve the cleaned HTML from the editor
|
|
61
|
+
const html = getCleanHTML();
|
|
58
62
|
</script>
|
|
59
63
|
```
|
|
60
64
|
|
|
@@ -62,12 +66,14 @@ React (functional component)
|
|
|
62
66
|
|
|
63
67
|
```jsx
|
|
64
68
|
import { useEffect, useRef } from "react";
|
|
65
|
-
import { initRichEditor } from "rich-html-editor";
|
|
69
|
+
import { initRichEditor, getCleanHTML } from "rich-html-editor";
|
|
66
70
|
|
|
67
71
|
export default function Editor() {
|
|
68
72
|
const iframeRef = useRef(null);
|
|
69
73
|
useEffect(() => {
|
|
70
74
|
if (iframeRef.current) initRichEditor(iframeRef.current);
|
|
75
|
+
// later, read cleaned HTML from the editor
|
|
76
|
+
const html = getCleanHTML();
|
|
71
77
|
}, []);
|
|
72
78
|
return (
|
|
73
79
|
<iframe
|
|
@@ -86,12 +92,14 @@ Angular (simple component)
|
|
|
86
92
|
|
|
87
93
|
// in component.ts
|
|
88
94
|
import { AfterViewInit, ViewChild, ElementRef } from "@angular/core";
|
|
89
|
-
import { initRichEditor } from "rich-html-editor";
|
|
95
|
+
import { initRichEditor, getCleanHTML } from "rich-html-editor";
|
|
90
96
|
|
|
91
97
|
export class MyEditorComponent implements AfterViewInit {
|
|
92
98
|
@ViewChild("editor") editor!: ElementRef<HTMLIFrameElement>;
|
|
93
99
|
ngAfterViewInit() {
|
|
94
100
|
initRichEditor(this.editor.nativeElement);
|
|
101
|
+
// later, obtain cleaned HTML
|
|
102
|
+
const html = getCleanHTML();
|
|
95
103
|
}
|
|
96
104
|
}
|
|
97
105
|
```
|
|
@@ -104,12 +112,14 @@ Vue 3 (Composition API)
|
|
|
104
112
|
</template>
|
|
105
113
|
<script setup>
|
|
106
114
|
import { onMounted, ref } from "vue";
|
|
107
|
-
import { initRichEditor } from "rich-html-editor";
|
|
115
|
+
import { initRichEditor, getCleanHTML } from "rich-html-editor";
|
|
108
116
|
|
|
109
117
|
const editor = ref(null);
|
|
110
118
|
const srcdoc = '<body><div class="editable">Edit me</div></body>';
|
|
111
119
|
onMounted(() => {
|
|
112
120
|
if (editor.value) initRichEditor(editor.value);
|
|
121
|
+
// later, read cleaned HTML from the editor
|
|
122
|
+
const html = getCleanHTML();
|
|
113
123
|
});
|
|
114
124
|
</script>
|
|
115
125
|
```
|
package/dist/index.js
CHANGED
|
@@ -761,219 +761,76 @@ function injectStyles(doc) {
|
|
|
761
761
|
styleEl.textContent = css;
|
|
762
762
|
}
|
|
763
763
|
|
|
764
|
-
// src/toolbar/
|
|
764
|
+
// src/toolbar/render.ts
|
|
765
765
|
init_constants();
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
766
|
+
|
|
767
|
+
// src/toolbar/color.ts
|
|
768
|
+
function makeColorInput(doc, options, title, command, initialColor) {
|
|
769
|
+
const input = doc.createElement("input");
|
|
770
|
+
input.type = "color";
|
|
771
|
+
input.className = "toolbar-color-input";
|
|
772
|
+
const wrapper = doc.createElement("label");
|
|
773
|
+
wrapper.className = "color-label";
|
|
774
|
+
wrapper.appendChild(doc.createTextNode(title + " "));
|
|
775
|
+
wrapper.appendChild(input);
|
|
776
|
+
let savedRange = null;
|
|
777
|
+
input.addEventListener("pointerdown", () => {
|
|
778
|
+
const s = doc.getSelection();
|
|
779
|
+
if (s && s.rangeCount) savedRange = s.getRangeAt(0).cloneRange();
|
|
780
|
+
});
|
|
781
|
+
input.onchange = (e) => {
|
|
782
|
+
try {
|
|
783
|
+
const s = doc.getSelection();
|
|
784
|
+
if (savedRange && s) {
|
|
785
|
+
s.removeAllRanges();
|
|
786
|
+
s.addRange(savedRange);
|
|
787
|
+
}
|
|
788
|
+
} catch (err) {
|
|
780
789
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
ev.preventDefault();
|
|
791
|
-
btn.click();
|
|
790
|
+
options.onCommand(command, e.target.value);
|
|
791
|
+
savedRange = null;
|
|
792
|
+
};
|
|
793
|
+
function rgbToHex(input2) {
|
|
794
|
+
if (!input2) return null;
|
|
795
|
+
const v = input2.trim();
|
|
796
|
+
if (v.startsWith("#")) {
|
|
797
|
+
if (v.length === 4) {
|
|
798
|
+
return ("#" + v[1] + v[1] + v[2] + v[2] + v[3] + v[3]).toLowerCase();
|
|
792
799
|
}
|
|
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));
|
|
800
|
+
return v.toLowerCase();
|
|
803
801
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
802
|
+
const rgbMatch = v.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/i);
|
|
803
|
+
if (rgbMatch) {
|
|
804
|
+
const r = Number(rgbMatch[1]);
|
|
805
|
+
const g = Number(rgbMatch[2]);
|
|
806
|
+
const b = Number(rgbMatch[3]);
|
|
807
|
+
const hex = "#" + [r, g, b].map((n) => n.toString(16).padStart(2, "0")).join("").toLowerCase();
|
|
808
|
+
return hex;
|
|
807
809
|
}
|
|
808
|
-
|
|
809
|
-
const val = e.target.value;
|
|
810
|
-
options.onCommand(command, val);
|
|
811
|
-
select.selectedIndex = 0;
|
|
812
|
-
};
|
|
813
|
-
return select;
|
|
810
|
+
return null;
|
|
814
811
|
}
|
|
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;
|
|
812
|
+
const setColor = (val) => {
|
|
813
|
+
if (!val) return;
|
|
814
|
+
const hex = rgbToHex(val) || val;
|
|
815
|
+
try {
|
|
816
|
+
if (hex && hex.startsWith("#") && input.value !== hex) {
|
|
817
|
+
input.value = hex;
|
|
856
818
|
}
|
|
857
|
-
|
|
819
|
+
} catch (e) {
|
|
858
820
|
}
|
|
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);
|
|
821
|
+
};
|
|
822
|
+
if (initialColor) setColor(initialColor);
|
|
823
|
+
input.addEventListener("input", (e) => {
|
|
824
|
+
const val = e.target.value;
|
|
825
|
+
setColor(val);
|
|
826
|
+
});
|
|
827
|
+
input.title = title;
|
|
828
|
+
input.setAttribute("aria-label", title);
|
|
829
|
+
return wrapper;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// src/toolbar/overflow.ts
|
|
833
|
+
function setupOverflow(doc, toolbar, options, format, helpers) {
|
|
977
834
|
const overflowBtn = doc.createElement("button");
|
|
978
835
|
overflowBtn.type = "button";
|
|
979
836
|
overflowBtn.className = "toolbar-overflow-btn";
|
|
@@ -1000,7 +857,7 @@ function injectToolbar(doc, options) {
|
|
|
1000
857
|
overflowBtn.setAttribute("aria-expanded", "false");
|
|
1001
858
|
overflowBtn.focus();
|
|
1002
859
|
}
|
|
1003
|
-
overflowBtn.addEventListener("click", (
|
|
860
|
+
overflowBtn.addEventListener("click", () => {
|
|
1004
861
|
if (overflowMenu.hidden) openOverflow();
|
|
1005
862
|
else closeOverflow();
|
|
1006
863
|
});
|
|
@@ -1027,35 +884,105 @@ function injectToolbar(doc, options) {
|
|
|
1027
884
|
}
|
|
1028
885
|
});
|
|
1029
886
|
overflowMenu.appendChild(
|
|
1030
|
-
makeSelect(
|
|
887
|
+
helpers.makeSelect(
|
|
1031
888
|
"Format",
|
|
1032
889
|
"formatBlock",
|
|
1033
|
-
|
|
890
|
+
window.RHE_FORMAT_OPTIONS || [],
|
|
1034
891
|
format.formatBlock
|
|
1035
892
|
)
|
|
1036
893
|
);
|
|
1037
894
|
overflowMenu.appendChild(
|
|
1038
|
-
makeSelect(
|
|
895
|
+
helpers.makeSelect(
|
|
896
|
+
"Font",
|
|
897
|
+
"fontName",
|
|
898
|
+
window.RHE_FONT_OPTIONS || [],
|
|
899
|
+
format.fontName
|
|
900
|
+
)
|
|
1039
901
|
);
|
|
1040
902
|
overflowMenu.appendChild(
|
|
1041
|
-
makeSelect(
|
|
903
|
+
helpers.makeSelect(
|
|
904
|
+
"Size",
|
|
905
|
+
"fontSize",
|
|
906
|
+
window.RHE_SIZE_OPTIONS || [],
|
|
907
|
+
format.fontSize
|
|
908
|
+
)
|
|
1042
909
|
);
|
|
1043
910
|
overflowMenu.appendChild(
|
|
1044
|
-
makeColorInput("Text color", "foreColor", format.foreColor)
|
|
911
|
+
helpers.makeColorInput("Text color", "foreColor", format.foreColor)
|
|
1045
912
|
);
|
|
1046
913
|
overflowMenu.appendChild(
|
|
1047
|
-
makeColorInput(
|
|
914
|
+
helpers.makeColorInput(
|
|
1048
915
|
"Highlight color",
|
|
1049
916
|
"hiliteColor",
|
|
1050
917
|
format.hiliteColor
|
|
1051
918
|
)
|
|
1052
919
|
);
|
|
1053
|
-
overflowMenu.appendChild(makeButton(
|
|
1054
|
-
const overflowWrap = makeGroup();
|
|
920
|
+
overflowMenu.appendChild(helpers.makeButton("Link", "Insert link", "link"));
|
|
921
|
+
const overflowWrap = helpers.makeGroup();
|
|
1055
922
|
overflowWrap.className = "toolbar-group toolbar-overflow-wrap";
|
|
1056
923
|
overflowWrap.appendChild(overflowBtn);
|
|
1057
924
|
overflowWrap.appendChild(overflowMenu);
|
|
1058
925
|
toolbar.appendChild(overflowWrap);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// src/toolbar/buttons.ts
|
|
929
|
+
function makeButton(doc, options, label, title, command, value, isActive, disabled) {
|
|
930
|
+
const btn = doc.createElement("button");
|
|
931
|
+
btn.type = "button";
|
|
932
|
+
if (label && label.trim().startsWith("<")) {
|
|
933
|
+
btn.innerHTML = label;
|
|
934
|
+
} else {
|
|
935
|
+
btn.textContent = label;
|
|
936
|
+
}
|
|
937
|
+
btn.title = title;
|
|
938
|
+
btn.setAttribute("aria-label", title);
|
|
939
|
+
if (typeof isActive !== "undefined")
|
|
940
|
+
btn.setAttribute("aria-pressed", String(!!isActive));
|
|
941
|
+
btn.tabIndex = 0;
|
|
942
|
+
if (disabled) btn.disabled = true;
|
|
943
|
+
btn.onclick = () => options.onCommand(command, value);
|
|
944
|
+
btn.addEventListener("keydown", (ev) => {
|
|
945
|
+
if (ev.key === "Enter" || ev.key === " ") {
|
|
946
|
+
ev.preventDefault();
|
|
947
|
+
btn.click();
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
return btn;
|
|
951
|
+
}
|
|
952
|
+
function makeGroup(doc) {
|
|
953
|
+
const g = doc.createElement("div");
|
|
954
|
+
g.className = "toolbar-group";
|
|
955
|
+
return g;
|
|
956
|
+
}
|
|
957
|
+
function makeSep(doc) {
|
|
958
|
+
const s = doc.createElement("div");
|
|
959
|
+
s.className = "toolbar-sep";
|
|
960
|
+
return s;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// src/toolbar/selects.ts
|
|
964
|
+
function makeSelect(doc, options, title, command, optionsList, initialValue) {
|
|
965
|
+
const select = doc.createElement("select");
|
|
966
|
+
select.title = title;
|
|
967
|
+
select.setAttribute("aria-label", title);
|
|
968
|
+
select.appendChild(new Option(title, "", true, true));
|
|
969
|
+
for (const opt of optionsList) {
|
|
970
|
+
select.appendChild(new Option(opt.label, opt.value));
|
|
971
|
+
}
|
|
972
|
+
try {
|
|
973
|
+
if (initialValue) select.value = initialValue;
|
|
974
|
+
} catch (e) {
|
|
975
|
+
}
|
|
976
|
+
select.onchange = (e) => {
|
|
977
|
+
const val = e.target.value;
|
|
978
|
+
options.onCommand(command, val);
|
|
979
|
+
select.selectedIndex = 0;
|
|
980
|
+
};
|
|
981
|
+
return select;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// src/toolbar/navigation.ts
|
|
985
|
+
function setupNavigation(toolbar) {
|
|
1059
986
|
toolbar.addEventListener("keydown", (e) => {
|
|
1060
987
|
const focusable = Array.from(
|
|
1061
988
|
toolbar.querySelectorAll("button, select, input, [tabindex]")
|
|
@@ -1078,6 +1005,140 @@ function injectToolbar(doc, options) {
|
|
|
1078
1005
|
focusable[focusable.length - 1].focus();
|
|
1079
1006
|
}
|
|
1080
1007
|
});
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// src/toolbar/render.ts
|
|
1011
|
+
function injectToolbar(doc, options) {
|
|
1012
|
+
const existing = doc.getElementById(TOOLBAR_ID);
|
|
1013
|
+
if (existing) existing.remove();
|
|
1014
|
+
const toolbar = doc.createElement("div");
|
|
1015
|
+
toolbar.id = TOOLBAR_ID;
|
|
1016
|
+
toolbar.setAttribute("role", "toolbar");
|
|
1017
|
+
toolbar.setAttribute("aria-label", "Rich text editor toolbar");
|
|
1018
|
+
const makeButton2 = (label, title, command, value, isActive, disabled) => makeButton(
|
|
1019
|
+
doc,
|
|
1020
|
+
{ onCommand: options.onCommand },
|
|
1021
|
+
label,
|
|
1022
|
+
title,
|
|
1023
|
+
command,
|
|
1024
|
+
value,
|
|
1025
|
+
isActive,
|
|
1026
|
+
disabled
|
|
1027
|
+
);
|
|
1028
|
+
const makeSelect2 = (title, command, optionsList, initialValue) => makeSelect(
|
|
1029
|
+
doc,
|
|
1030
|
+
{ onCommand: options.onCommand },
|
|
1031
|
+
title,
|
|
1032
|
+
command,
|
|
1033
|
+
optionsList,
|
|
1034
|
+
initialValue
|
|
1035
|
+
);
|
|
1036
|
+
const format = options.getFormatState();
|
|
1037
|
+
const makeGroup2 = () => makeGroup(doc);
|
|
1038
|
+
const makeSep2 = () => makeSep(doc);
|
|
1039
|
+
const undoBtn = makeButton2(
|
|
1040
|
+
LABEL_UNDO,
|
|
1041
|
+
"Undo",
|
|
1042
|
+
"undo",
|
|
1043
|
+
void 0,
|
|
1044
|
+
false,
|
|
1045
|
+
!options.canUndo()
|
|
1046
|
+
);
|
|
1047
|
+
undoBtn.onclick = () => options.onUndo();
|
|
1048
|
+
const redoBtn = makeButton2(
|
|
1049
|
+
LABEL_REDO,
|
|
1050
|
+
"Redo",
|
|
1051
|
+
"redo",
|
|
1052
|
+
void 0,
|
|
1053
|
+
false,
|
|
1054
|
+
!options.canRedo()
|
|
1055
|
+
);
|
|
1056
|
+
redoBtn.onclick = () => options.onRedo();
|
|
1057
|
+
const grp1 = makeGroup2();
|
|
1058
|
+
grp1.appendChild(undoBtn);
|
|
1059
|
+
grp1.appendChild(redoBtn);
|
|
1060
|
+
toolbar.appendChild(grp1);
|
|
1061
|
+
toolbar.appendChild(makeSep2());
|
|
1062
|
+
const grp2 = makeGroup2();
|
|
1063
|
+
grp2.className = "toolbar-group collapse-on-small";
|
|
1064
|
+
grp2.appendChild(
|
|
1065
|
+
makeSelect2(
|
|
1066
|
+
"Format",
|
|
1067
|
+
"formatBlock",
|
|
1068
|
+
FORMAT_OPTIONS,
|
|
1069
|
+
format.formatBlock
|
|
1070
|
+
)
|
|
1071
|
+
);
|
|
1072
|
+
grp2.appendChild(
|
|
1073
|
+
makeSelect2("Font", "fontName", FONT_OPTIONS, format.fontName)
|
|
1074
|
+
);
|
|
1075
|
+
grp2.appendChild(
|
|
1076
|
+
makeSelect2("Size", "fontSize", SIZE_OPTIONS, format.fontSize)
|
|
1077
|
+
);
|
|
1078
|
+
toolbar.appendChild(grp2);
|
|
1079
|
+
toolbar.appendChild(makeSep2());
|
|
1080
|
+
const grp3 = makeGroup2();
|
|
1081
|
+
grp3.appendChild(
|
|
1082
|
+
makeButton2(LABEL_BOLD, "Bold", "bold", void 0, format.bold)
|
|
1083
|
+
);
|
|
1084
|
+
grp3.appendChild(
|
|
1085
|
+
makeButton2(LABEL_ITALIC, "Italic", "italic", void 0, format.italic)
|
|
1086
|
+
);
|
|
1087
|
+
grp3.appendChild(
|
|
1088
|
+
makeButton2(
|
|
1089
|
+
LABEL_UNDERLINE,
|
|
1090
|
+
"Underline",
|
|
1091
|
+
"underline",
|
|
1092
|
+
void 0,
|
|
1093
|
+
format.underline
|
|
1094
|
+
)
|
|
1095
|
+
);
|
|
1096
|
+
grp3.appendChild(makeButton2(LABEL_STRIKETHROUGH, "Strikethrough", "strike"));
|
|
1097
|
+
toolbar.appendChild(grp3);
|
|
1098
|
+
toolbar.appendChild(makeSep2());
|
|
1099
|
+
const grp4 = makeGroup2();
|
|
1100
|
+
grp4.appendChild(makeButton2(LABEL_ALIGN_LEFT, "Align left", "align", "left"));
|
|
1101
|
+
grp4.appendChild(
|
|
1102
|
+
makeButton2(LABEL_ALIGN_CENTER, "Align center", "align", "center")
|
|
1103
|
+
);
|
|
1104
|
+
grp4.appendChild(
|
|
1105
|
+
makeButton2(LABEL_ALIGN_RIGHT, "Align right", "align", "right")
|
|
1106
|
+
);
|
|
1107
|
+
toolbar.appendChild(grp4);
|
|
1108
|
+
toolbar.appendChild(makeSep2());
|
|
1109
|
+
const grp5 = makeGroup2();
|
|
1110
|
+
grp5.className = "toolbar-group collapse-on-small";
|
|
1111
|
+
grp5.appendChild(
|
|
1112
|
+
makeColorInput(
|
|
1113
|
+
doc,
|
|
1114
|
+
options,
|
|
1115
|
+
"Text color",
|
|
1116
|
+
"foreColor",
|
|
1117
|
+
format.foreColor
|
|
1118
|
+
)
|
|
1119
|
+
);
|
|
1120
|
+
grp5.appendChild(
|
|
1121
|
+
makeColorInput(
|
|
1122
|
+
doc,
|
|
1123
|
+
options,
|
|
1124
|
+
"Highlight color",
|
|
1125
|
+
"hiliteColor",
|
|
1126
|
+
format.hiliteColor
|
|
1127
|
+
)
|
|
1128
|
+
);
|
|
1129
|
+
toolbar.appendChild(grp5);
|
|
1130
|
+
toolbar.appendChild(makeSep2());
|
|
1131
|
+
const grp6 = makeGroup2();
|
|
1132
|
+
grp6.className = "toolbar-group collapse-on-small";
|
|
1133
|
+
grp6.appendChild(makeButton2(LABEL_LINK, "Insert link", "link"));
|
|
1134
|
+
toolbar.appendChild(grp6);
|
|
1135
|
+
setupOverflow(doc, toolbar, options, format, {
|
|
1136
|
+
makeSelect: makeSelect2,
|
|
1137
|
+
makeColorInput: (title, command, initial) => makeColorInput(doc, options, title, command, initial),
|
|
1138
|
+
makeButton: makeButton2,
|
|
1139
|
+
makeGroup: makeGroup2
|
|
1140
|
+
});
|
|
1141
|
+
setupNavigation(toolbar);
|
|
1081
1142
|
doc.body.insertBefore(toolbar, doc.body.firstChild);
|
|
1082
1143
|
}
|
|
1083
1144
|
|