smartrte-react 0.1.18 → 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 +83 -15
- package/dist/components/ClassicEditor.d.ts +32 -1
- package/dist/components/ClassicEditor.js +949 -251
- package/dist/components/MediaManager.d.ts +7 -0
- package/dist/components/MediaManager.js +85 -29
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/standalone/classic-editor-embed.js +1 -1
- package/dist/theme.d.ts +3 -0
- package/dist/theme.js +78 -0
- package/package.json +13 -12
- package/dist/QuillEditor.d.ts +0 -8
- package/dist/QuillEditor.js +0 -34
- package/dist/app.d.ts +0 -6
- package/dist/app.js +0 -6
- package/dist/blots/CommentBlot.d.ts +0 -8
- package/dist/blots/CommentBlot.js +0 -17
- package/dist/blots/FormulaBlot.d.ts +0 -12
- package/dist/blots/FormulaBlot.js +0 -36
- package/dist/blots/MediaBlot.d.ts +0 -11
- package/dist/blots/MediaBlot.js +0 -37
- package/dist/blots/TableBlot.d.ts +0 -10
- package/dist/blots/TableBlot.js +0 -54
- package/dist/blots/index.d.ts +0 -5
- package/dist/blots/index.js +0 -12
- package/dist/components/DiagramEditor.d.ts +0 -5
- package/dist/components/DiagramEditor.js +0 -73
- package/dist/components/FormulaEditor.d.ts +0 -6
- package/dist/components/FormulaEditor.js +0 -86
- package/dist/components/InfoBox.d.ts +0 -7
- package/dist/components/InfoBox.js +0 -18
- package/dist/components/MCQBlock.d.ts +0 -13
- package/dist/components/MCQBlock.js +0 -29
- package/dist/components/SmartEditor.d.ts +0 -0
- package/dist/components/SmartEditor.js +0 -1
- package/dist/components/SmartTable.d.ts +0 -22
- package/dist/components/SmartTable.js +0 -629
- package/dist/components/TableContextMenu.d.ts +0 -11
- package/dist/components/TableContextMenu.js +0 -15
- package/dist/components/TableInsertDialog.d.ts +0 -7
- package/dist/components/TableInsertDialog.js +0 -42
- package/dist/hooks/useEditorSync.d.ts +0 -5
- package/dist/hooks/useEditorSync.js +0 -53
- package/dist/smart-editor.d.ts +0 -0
- package/dist/smart-editor.js +0 -1
- package/dist/standalone/editor.js +0 -241
|
@@ -10,6 +10,13 @@ export type MediaItem = {
|
|
|
10
10
|
title?: string;
|
|
11
11
|
alt?: string;
|
|
12
12
|
tags?: string[];
|
|
13
|
+
license?: {
|
|
14
|
+
author?: string;
|
|
15
|
+
licenseType?: string;
|
|
16
|
+
licenseText?: string;
|
|
17
|
+
sourceUrl?: string;
|
|
18
|
+
workName?: string;
|
|
19
|
+
};
|
|
13
20
|
};
|
|
14
21
|
export type MediaSearchQuery = {
|
|
15
22
|
q?: string;
|
|
@@ -7,6 +7,7 @@ export function MediaManager(props) {
|
|
|
7
7
|
const [error, setError] = useState(null);
|
|
8
8
|
const [query, setQuery] = useState("");
|
|
9
9
|
const [results, setResults] = useState([]);
|
|
10
|
+
const [infoItem, setInfoItem] = useState(null);
|
|
10
11
|
const fileInputRef = useRef(null);
|
|
11
12
|
useEffect(() => {
|
|
12
13
|
if (!open)
|
|
@@ -86,18 +87,19 @@ export function MediaManager(props) {
|
|
|
86
87
|
return (_jsx("div", { style: {
|
|
87
88
|
position: "fixed",
|
|
88
89
|
inset: 0,
|
|
89
|
-
background: "
|
|
90
|
+
background: "var(--srte-modal-backdrop)",
|
|
90
91
|
display: "flex",
|
|
91
92
|
alignItems: "center",
|
|
92
93
|
justifyContent: "center",
|
|
93
94
|
zIndex: 80,
|
|
94
95
|
}, onClick: onClose, children: _jsxs("div", { style: {
|
|
95
|
-
background: "
|
|
96
|
+
background: "var(--srte-modal-bg)",
|
|
97
|
+
color: "var(--srte-modal-text)",
|
|
96
98
|
width: 820,
|
|
97
99
|
maxWidth: "90vw",
|
|
98
100
|
maxHeight: "86vh",
|
|
99
101
|
borderRadius: 10,
|
|
100
|
-
boxShadow: "
|
|
102
|
+
boxShadow: "var(--srte-menu-shadow)",
|
|
101
103
|
display: "flex",
|
|
102
104
|
flexDirection: "column",
|
|
103
105
|
}, onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { style: {
|
|
@@ -105,29 +107,31 @@ export function MediaManager(props) {
|
|
|
105
107
|
alignItems: "center",
|
|
106
108
|
justifyContent: "space-between",
|
|
107
109
|
padding: "10px 14px",
|
|
108
|
-
borderBottom: "1px solid
|
|
110
|
+
borderBottom: "1px solid var(--srte-border-light)",
|
|
109
111
|
}, children: [_jsxs("div", { style: { display: "flex", gap: 8 }, children: [_jsx("button", { onClick: () => setActiveTab("upload"), style: {
|
|
110
112
|
padding: "6px 10px",
|
|
111
113
|
borderRadius: 6,
|
|
112
|
-
border: "1px solid
|
|
113
|
-
background: activeTab === "upload" ? "
|
|
114
|
+
border: "1px solid var(--srte-border)",
|
|
115
|
+
background: activeTab === "upload" ? "var(--srte-surface-subtle)" : "var(--srte-input-bg)",
|
|
116
|
+
color: "var(--srte-input-text)",
|
|
114
117
|
}, children: "Upload" }), _jsx("button", { onClick: () => setActiveTab("library"), style: {
|
|
115
118
|
padding: "6px 10px",
|
|
116
119
|
borderRadius: 6,
|
|
117
|
-
border: "1px solid
|
|
118
|
-
background: activeTab === "library" ? "
|
|
119
|
-
|
|
120
|
+
border: "1px solid var(--srte-border)",
|
|
121
|
+
background: activeTab === "library" ? "var(--srte-surface-subtle)" : "var(--srte-input-bg)",
|
|
122
|
+
color: "var(--srte-input-text)",
|
|
123
|
+
}, children: "Library" })] }), _jsx("button", { onClick: onClose, children: "\u2715" })] }), error && (_jsx("div", { style: { color: "var(--srte-danger)", padding: "8px 14px" }, children: error })), activeTab === "upload" ? (_jsxs("div", { style: { padding: 16 }, children: [_jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", multiple: true, style: { display: "none" }, onChange: (e) => handleUploadFiles(e.currentTarget.files) }), _jsx("div", { onClick: () => fileInputRef.current?.click(), onDragOver: (e) => {
|
|
120
124
|
e.preventDefault();
|
|
121
125
|
}, onDrop: (e) => {
|
|
122
126
|
e.preventDefault();
|
|
123
127
|
handleUploadFiles(e.dataTransfer.files);
|
|
124
128
|
}, style: {
|
|
125
|
-
border: "2px dashed
|
|
129
|
+
border: "2px dashed var(--srte-border)",
|
|
126
130
|
borderRadius: 10,
|
|
127
131
|
padding: 24,
|
|
128
132
|
textAlign: "center",
|
|
129
|
-
color: "
|
|
130
|
-
background: "
|
|
133
|
+
color: "var(--srte-text-muted)",
|
|
134
|
+
background: "var(--srte-surface-subtle)",
|
|
131
135
|
cursor: uploading ? "default" : "pointer",
|
|
132
136
|
opacity: uploading ? 0.7 : 1,
|
|
133
137
|
}, children: uploading ? "Uploading…" : "Click or drag images to upload" })] })) : (_jsxs("div", { style: {
|
|
@@ -138,30 +142,82 @@ export function MediaManager(props) {
|
|
|
138
142
|
}, children: [_jsxs("div", { style: { display: "flex", gap: 8 }, children: [_jsx("input", { value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Search images by name, tag, etc.", style: {
|
|
139
143
|
flex: 1,
|
|
140
144
|
padding: "6px 8px",
|
|
141
|
-
border: "1px solid
|
|
145
|
+
border: "1px solid var(--srte-border)",
|
|
142
146
|
borderRadius: 6,
|
|
147
|
+
background: "var(--srte-input-bg)",
|
|
148
|
+
color: "var(--srte-input-text)",
|
|
143
149
|
} }), _jsx("button", { onClick: performSearch, children: "Search" })] }), _jsx("div", { style: {
|
|
144
150
|
display: "grid",
|
|
145
151
|
gridTemplateColumns: "repeat(auto-fill, minmax(120px, 1fr))",
|
|
146
152
|
gap: 12,
|
|
147
153
|
overflowY: "auto",
|
|
148
154
|
paddingBottom: 16,
|
|
149
|
-
}, children: results.map((it) => (_jsxs("
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
border: "1px solid #eee",
|
|
155
|
+
}, children: results.map((it) => (_jsxs("div", { title: it.title || it.url, style: {
|
|
156
|
+
display: "flex",
|
|
157
|
+
flexDirection: "column",
|
|
158
|
+
gap: 6,
|
|
159
|
+
border: "1px solid var(--srte-border-light)",
|
|
155
160
|
borderRadius: 8,
|
|
156
161
|
padding: 6,
|
|
157
|
-
background: "
|
|
158
|
-
|
|
159
|
-
}, children: [_jsx("
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
162
|
+
background: "var(--srte-input-bg)",
|
|
163
|
+
color: "var(--srte-input-text)",
|
|
164
|
+
}, children: [_jsx("button", { type: "button", onClick: () => {
|
|
165
|
+
onSelect(it);
|
|
166
|
+
onClose();
|
|
167
|
+
}, style: {
|
|
168
|
+
border: "none",
|
|
169
|
+
padding: 0,
|
|
170
|
+
background: "transparent",
|
|
171
|
+
cursor: "pointer",
|
|
172
|
+
}, children: _jsx("img", { src: it.url, alt: it.alt || "", style: {
|
|
173
|
+
maxWidth: "100%",
|
|
174
|
+
maxHeight: 100,
|
|
175
|
+
display: "block",
|
|
176
|
+
margin: "0 auto",
|
|
177
|
+
objectFit: "cover",
|
|
178
|
+
borderRadius: 6,
|
|
179
|
+
} }) }), _jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 6 }, children: [_jsx("div", { style: { fontSize: 11, color: "var(--srte-text-muted)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: it.title || it.alt || (it.width && it.height ? `${it.width}×${it.height}` : "Image") }), _jsx("button", { type: "button", onClick: () => setInfoItem(it), title: "Image info", style: {
|
|
180
|
+
width: 24,
|
|
181
|
+
height: 24,
|
|
182
|
+
border: "1px solid var(--srte-border)",
|
|
183
|
+
borderRadius: 999,
|
|
184
|
+
background: "var(--srte-surface-subtle)",
|
|
185
|
+
color: "var(--srte-input-text)",
|
|
186
|
+
cursor: "pointer",
|
|
187
|
+
flex: "0 0 auto",
|
|
188
|
+
}, children: "i" })] }), it.tags && it.tags.length > 0 && (_jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 4 }, children: it.tags.slice(0, 3).map((tag) => (_jsx("span", { style: {
|
|
189
|
+
fontSize: 10,
|
|
190
|
+
padding: "1px 5px",
|
|
191
|
+
borderRadius: 999,
|
|
192
|
+
background: "var(--srte-surface-subtle)",
|
|
193
|
+
color: "var(--srte-text-muted)",
|
|
194
|
+
}, children: tag }, tag))) }))] }, it.id || it.url))) })] })), infoItem && (_jsx("div", { style: {
|
|
195
|
+
position: "fixed",
|
|
196
|
+
inset: 0,
|
|
197
|
+
background: "var(--srte-modal-backdrop)",
|
|
198
|
+
display: "flex",
|
|
199
|
+
alignItems: "center",
|
|
200
|
+
justifyContent: "center",
|
|
201
|
+
zIndex: 90,
|
|
202
|
+
}, onClick: () => setInfoItem(null), children: _jsxs("div", { style: {
|
|
203
|
+
width: 420,
|
|
204
|
+
maxWidth: "90vw",
|
|
205
|
+
background: "var(--srte-modal-bg)",
|
|
206
|
+
color: "var(--srte-modal-text)",
|
|
207
|
+
borderRadius: 10,
|
|
208
|
+
boxShadow: "var(--srte-menu-shadow)",
|
|
209
|
+
padding: 16,
|
|
210
|
+
}, onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { style: { display: "flex", justifyContent: "space-between", gap: 12, marginBottom: 12 }, children: [_jsx("div", { style: { fontWeight: 600 }, children: "Image info" }), _jsx("button", { type: "button", onClick: () => setInfoItem(null), children: "\u2715" })] }), _jsx("img", { src: infoItem.url, alt: infoItem.alt || "", style: { maxWidth: "100%", maxHeight: 180, display: "block", margin: "0 auto 12px", borderRadius: 8 } }), [
|
|
211
|
+
["Title", infoItem.title],
|
|
212
|
+
["Alt text", infoItem.alt],
|
|
213
|
+
["Dimensions", infoItem.width && infoItem.height ? `${infoItem.width}×${infoItem.height}` : undefined],
|
|
214
|
+
["MIME type", infoItem.mimeType],
|
|
215
|
+
["Size", infoItem.sizeBytes ? `${Math.round(infoItem.sizeBytes / 1024)} KB` : undefined],
|
|
216
|
+
["Created", infoItem.createdAt],
|
|
217
|
+
["Tags", infoItem.tags?.join(", ")],
|
|
218
|
+
["Work", infoItem.license?.workName],
|
|
219
|
+
["Author", infoItem.license?.author],
|
|
220
|
+
["License", [infoItem.license?.licenseType, infoItem.license?.licenseText].filter(Boolean).join(" - ")],
|
|
221
|
+
["Source", infoItem.license?.sourceUrl],
|
|
222
|
+
].filter(([, value]) => value).map(([label, value]) => (_jsxs("div", { style: { display: "grid", gridTemplateColumns: "92px 1fr", gap: 8, fontSize: 12, marginBottom: 6 }, children: [_jsx("div", { style: { color: "var(--srte-text-muted)" }, children: label }), _jsx("div", { style: { overflowWrap: "anywhere" }, children: value })] }, label)))] }) }))] }) }));
|
|
167
223
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ function ClassicEditorHost(props, ref) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
catch { }
|
|
40
|
-
}, placeholder: props.placeholder, minHeight: props.minHeight, maxHeight: props.maxHeight, readOnly: props.readOnly, table: props.table, media: props.media, formula: props.formula, mediaManager: props.mediaManager }) }));
|
|
40
|
+
}, placeholder: props.placeholder, minHeight: props.minHeight, maxHeight: props.maxHeight, readOnly: props.readOnly, table: props.table, media: props.media, formula: props.formula, mediaManager: props.mediaManager, theme: props.theme, className: props.className }) }));
|
|
41
41
|
}
|
|
42
42
|
const ClassicEditorHostWithRef = React.forwardRef(ClassicEditorHost);
|
|
43
43
|
function initClassicEditor(opts) {
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type SrteTheme = 'light' | 'dark';
|
|
2
|
+
export declare const SRTE_DEFAULT_CSS = "\n.srte-editor {\n --srte-bg: #ffffff;\n --srte-text: #111111;\n --srte-text-muted: #4b5563;\n --srte-border: #dddddd;\n --srte-border-light: #eeeeee;\n --srte-toolbar-bg: #ffffff;\n --srte-input-bg: #ffffff;\n --srte-input-text: #111111;\n --srte-input-border: #e5e7eb;\n --srte-modal-backdrop: rgba(0, 0, 0, 0.35);\n --srte-modal-bg: #ffffff;\n --srte-modal-text: #000000;\n --srte-menu-bg: #ffffff;\n --srte-menu-text: #111111;\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n --srte-accent: #1e90ff;\n --srte-accent-bg: rgba(30, 144, 255, 0.15);\n --srte-danger: #dc2626;\n --srte-primary: #2563eb;\n --srte-surface-subtle: #f3f4f6;\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #f3f4f6;\n}\n.srte-editor.srte-dark {\n --srte-bg: #1e1e1e;\n --srte-text: #e0e0e0;\n --srte-text-muted: #9ca3af;\n --srte-border: #3a3a3a;\n --srte-border-light: #2e2e2e;\n --srte-toolbar-bg: #252525;\n --srte-input-bg: #2a2a2a;\n --srte-input-text: #e0e0e0;\n --srte-input-border: #444444;\n --srte-modal-backdrop: rgba(0, 0, 0, 0.6);\n --srte-modal-bg: #252525;\n --srte-modal-text: #e0e0e0;\n --srte-menu-bg: #2a2a2a;\n --srte-menu-text: #e0e0e0;\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);\n --srte-accent: #3b9eff;\n --srte-accent-bg: rgba(59, 158, 255, 0.2);\n --srte-danger: #ef4444;\n --srte-primary: #3b82f6;\n --srte-surface-subtle: #333333;\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #333333;\n}\n.srte-editor [contenteditable] blockquote {\n border-left: 4px solid var(--srte-accent);\n margin: 0.75em 0;\n padding: 0.5em 1em;\n background: var(--srte-surface-subtle);\n color: var(--srte-text);\n}\n.srte-editor.srte-dark [contenteditable] [style*=\"color\"]:not(.srte-preserve-colors):not(.srte-preserve-colors *),\n.srte-editor.srte-dark [contenteditable] [style*=\"background\"]:not(.srte-preserve-colors):not(.srte-preserve-colors *) {\n color: var(--srte-text) !important;\n background: transparent !important;\n background-color: transparent !important;\n}\n.srte-editor [contenteditable] sub,\n.srte-editor [contenteditable] sup {\n line-height: 0;\n}\n";
|
|
3
|
+
export declare function ensureStyleSheet(): void;
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export const SRTE_DEFAULT_CSS = `
|
|
2
|
+
.srte-editor {
|
|
3
|
+
--srte-bg: #ffffff;
|
|
4
|
+
--srte-text: #111111;
|
|
5
|
+
--srte-text-muted: #4b5563;
|
|
6
|
+
--srte-border: #dddddd;
|
|
7
|
+
--srte-border-light: #eeeeee;
|
|
8
|
+
--srte-toolbar-bg: #ffffff;
|
|
9
|
+
--srte-input-bg: #ffffff;
|
|
10
|
+
--srte-input-text: #111111;
|
|
11
|
+
--srte-input-border: #e5e7eb;
|
|
12
|
+
--srte-modal-backdrop: rgba(0, 0, 0, 0.35);
|
|
13
|
+
--srte-modal-bg: #ffffff;
|
|
14
|
+
--srte-modal-text: #000000;
|
|
15
|
+
--srte-menu-bg: #ffffff;
|
|
16
|
+
--srte-menu-text: #111111;
|
|
17
|
+
--srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
|
|
18
|
+
--srte-accent: #1e90ff;
|
|
19
|
+
--srte-accent-bg: rgba(30, 144, 255, 0.15);
|
|
20
|
+
--srte-danger: #dc2626;
|
|
21
|
+
--srte-primary: #2563eb;
|
|
22
|
+
--srte-surface-subtle: #f3f4f6;
|
|
23
|
+
--srte-on-primary: #ffffff;
|
|
24
|
+
--srte-cancel-bg: #f3f4f6;
|
|
25
|
+
}
|
|
26
|
+
.srte-editor.srte-dark {
|
|
27
|
+
--srte-bg: #1e1e1e;
|
|
28
|
+
--srte-text: #e0e0e0;
|
|
29
|
+
--srte-text-muted: #9ca3af;
|
|
30
|
+
--srte-border: #3a3a3a;
|
|
31
|
+
--srte-border-light: #2e2e2e;
|
|
32
|
+
--srte-toolbar-bg: #252525;
|
|
33
|
+
--srte-input-bg: #2a2a2a;
|
|
34
|
+
--srte-input-text: #e0e0e0;
|
|
35
|
+
--srte-input-border: #444444;
|
|
36
|
+
--srte-modal-backdrop: rgba(0, 0, 0, 0.6);
|
|
37
|
+
--srte-modal-bg: #252525;
|
|
38
|
+
--srte-modal-text: #e0e0e0;
|
|
39
|
+
--srte-menu-bg: #2a2a2a;
|
|
40
|
+
--srte-menu-text: #e0e0e0;
|
|
41
|
+
--srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
|
42
|
+
--srte-accent: #3b9eff;
|
|
43
|
+
--srte-accent-bg: rgba(59, 158, 255, 0.2);
|
|
44
|
+
--srte-danger: #ef4444;
|
|
45
|
+
--srte-primary: #3b82f6;
|
|
46
|
+
--srte-surface-subtle: #333333;
|
|
47
|
+
--srte-on-primary: #ffffff;
|
|
48
|
+
--srte-cancel-bg: #333333;
|
|
49
|
+
}
|
|
50
|
+
.srte-editor [contenteditable] blockquote {
|
|
51
|
+
border-left: 4px solid var(--srte-accent);
|
|
52
|
+
margin: 0.75em 0;
|
|
53
|
+
padding: 0.5em 1em;
|
|
54
|
+
background: var(--srte-surface-subtle);
|
|
55
|
+
color: var(--srte-text);
|
|
56
|
+
}
|
|
57
|
+
.srte-editor.srte-dark [contenteditable] [style*="color"]:not(.srte-preserve-colors):not(.srte-preserve-colors *),
|
|
58
|
+
.srte-editor.srte-dark [contenteditable] [style*="background"]:not(.srte-preserve-colors):not(.srte-preserve-colors *) {
|
|
59
|
+
color: var(--srte-text) !important;
|
|
60
|
+
background: transparent !important;
|
|
61
|
+
background-color: transparent !important;
|
|
62
|
+
}
|
|
63
|
+
.srte-editor [contenteditable] sub,
|
|
64
|
+
.srte-editor [contenteditable] sup {
|
|
65
|
+
line-height: 0;
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
const SRTE_STYLE_ID = 'srte-theme-defaults';
|
|
69
|
+
export function ensureStyleSheet() {
|
|
70
|
+
if (typeof document === 'undefined')
|
|
71
|
+
return;
|
|
72
|
+
if (document.getElementById(SRTE_STYLE_ID))
|
|
73
|
+
return;
|
|
74
|
+
const style = document.createElement('style');
|
|
75
|
+
style.id = SRTE_STYLE_ID;
|
|
76
|
+
style.textContent = SRTE_DEFAULT_CSS;
|
|
77
|
+
document.head.appendChild(style);
|
|
78
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smartrte-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "A powerful, feature-rich Rich Text Editor for React with support for tables, mathematical formulas (LaTeX/KaTeX), and media management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -38,7 +38,16 @@
|
|
|
38
38
|
},
|
|
39
39
|
"author": "Smart RTE Contributors",
|
|
40
40
|
"license": "MIT",
|
|
41
|
-
"
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc -p tsconfig.json",
|
|
43
|
+
"prepublishOnly": "pnpm run build",
|
|
44
|
+
"dev": "pnpm build",
|
|
45
|
+
"lint": "eslint . || true",
|
|
46
|
+
"test": "vitest run || true",
|
|
47
|
+
"storybook": "storybook dev -p 6006",
|
|
48
|
+
"build-storybook": "storybook build",
|
|
49
|
+
"e2e": "playwright test || true"
|
|
50
|
+
},
|
|
42
51
|
"publishConfig": {
|
|
43
52
|
"access": "public"
|
|
44
53
|
},
|
|
@@ -63,16 +72,8 @@
|
|
|
63
72
|
"vitest": "^2.1.4"
|
|
64
73
|
},
|
|
65
74
|
"dependencies": {
|
|
75
|
+
"jszip": "^3.10.1",
|
|
66
76
|
"mammoth": "^1.11.0",
|
|
67
77
|
"pdfjs-dist": "^5.4.530"
|
|
68
|
-
},
|
|
69
|
-
"scripts": {
|
|
70
|
-
"build": "tsc -p tsconfig.json",
|
|
71
|
-
"dev": "pnpm build",
|
|
72
|
-
"lint": "eslint . || true",
|
|
73
|
-
"test": "vitest run || true",
|
|
74
|
-
"storybook": "storybook dev -p 6006",
|
|
75
|
-
"build-storybook": "storybook build",
|
|
76
|
-
"e2e": "playwright test || true"
|
|
77
78
|
}
|
|
78
|
-
}
|
|
79
|
+
}
|
package/dist/QuillEditor.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import "quill/dist/quill.snow.css";
|
|
2
|
-
import "./blots";
|
|
3
|
-
type QuillEditorProps = {
|
|
4
|
-
value?: any;
|
|
5
|
-
onChange?: (delta: any) => void;
|
|
6
|
-
};
|
|
7
|
-
export declare function QuillEditor({ value, onChange }: QuillEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
export {};
|
package/dist/QuillEditor.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef } from "react";
|
|
3
|
-
import Quill from "quill";
|
|
4
|
-
import "quill/dist/quill.snow.css";
|
|
5
|
-
import "./blots"; // registers custom blots
|
|
6
|
-
export function QuillEditor({ value, onChange }) {
|
|
7
|
-
const ref = useRef(null);
|
|
8
|
-
const quillRef = useRef();
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
if (!ref.current)
|
|
11
|
-
return;
|
|
12
|
-
if (quillRef.current)
|
|
13
|
-
return;
|
|
14
|
-
quillRef.current = new Quill(ref.current, {
|
|
15
|
-
theme: "snow",
|
|
16
|
-
modules: {
|
|
17
|
-
toolbar: [
|
|
18
|
-
["bold", "italic", "underline"],
|
|
19
|
-
[{ header: [1, 2, 3, false] }],
|
|
20
|
-
["formula", "image"],
|
|
21
|
-
["clean"],
|
|
22
|
-
],
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
quillRef.current.on("text-change", () => {
|
|
26
|
-
const delta = quillRef.current?.getContents();
|
|
27
|
-
onChange?.(delta);
|
|
28
|
-
});
|
|
29
|
-
if (value) {
|
|
30
|
-
quillRef.current.setContents(value);
|
|
31
|
-
}
|
|
32
|
-
}, [value, onChange]);
|
|
33
|
-
return _jsx("div", { ref: ref, style: { minHeight: 300 } });
|
|
34
|
-
}
|
package/dist/app.d.ts
DELETED
package/dist/app.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
2
|
-
import { QuillEditor } from "./QuillEditor";
|
|
3
|
-
export default function App() {
|
|
4
|
-
const [delta, setDelta] = useState(null);
|
|
5
|
-
return React.createElement("div", { style: { padding: 20 } }, React.createElement("h1", null, "Smart RTE – Quill Demo"), React.createElement(QuillEditor, { value: delta, onChange: setDelta }), React.createElement("pre", null, JSON.stringify(delta, null, 2)));
|
|
6
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import Quill from "quill";
|
|
2
|
-
const Inline = Quill.import("blots/inline");
|
|
3
|
-
export class CommentBlot extends Inline {
|
|
4
|
-
static create(value) {
|
|
5
|
-
const node = super.create();
|
|
6
|
-
node.setAttribute("data-comment", value);
|
|
7
|
-
node.classList.add("comment-anchor");
|
|
8
|
-
node.style.background = "rgba(255, 229, 100, 0.6)";
|
|
9
|
-
node.innerText = "💬"; // you can replace with thread number or icon
|
|
10
|
-
return node;
|
|
11
|
-
}
|
|
12
|
-
static value(domNode) {
|
|
13
|
-
return domNode.getAttribute("data-comment") || "";
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
CommentBlot.blotName = "comment";
|
|
17
|
-
CommentBlot.tagName = "sup";
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
declare const Embed: any;
|
|
2
|
-
export declare class FormulaBlot extends Embed {
|
|
3
|
-
static blotName: string;
|
|
4
|
-
static tagName: string;
|
|
5
|
-
static className: string;
|
|
6
|
-
static create(value: any): any;
|
|
7
|
-
static value(node: HTMLElement): {
|
|
8
|
-
tex: string;
|
|
9
|
-
displayMode: boolean;
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
export {};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import Quill from "quill";
|
|
2
|
-
const Embed = Quill.import("blots/embed");
|
|
3
|
-
export class FormulaBlot extends Embed {
|
|
4
|
-
static create(value) {
|
|
5
|
-
const node = super.create();
|
|
6
|
-
node.setAttribute("data-formula", value.tex);
|
|
7
|
-
try {
|
|
8
|
-
// @ts-ignore
|
|
9
|
-
const katex = window.katex;
|
|
10
|
-
if (katex) {
|
|
11
|
-
node.innerHTML = "";
|
|
12
|
-
katex.render(value.tex, node, {
|
|
13
|
-
displayMode: !!value.displayMode,
|
|
14
|
-
throwOnError: false,
|
|
15
|
-
strict: "ignore",
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
node.textContent = value.tex;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
node.textContent = value.tex;
|
|
24
|
-
}
|
|
25
|
-
return node;
|
|
26
|
-
}
|
|
27
|
-
static value(node) {
|
|
28
|
-
return {
|
|
29
|
-
tex: node.getAttribute("data-formula") || "",
|
|
30
|
-
displayMode: node.classList.contains("formula-block"),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
FormulaBlot.blotName = "formula";
|
|
35
|
-
FormulaBlot.tagName = "span";
|
|
36
|
-
FormulaBlot.className = "formula-inline";
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
declare const BlockEmbed: any;
|
|
2
|
-
export declare class MediaBlot extends BlockEmbed {
|
|
3
|
-
static blotName: string;
|
|
4
|
-
static tagName: string;
|
|
5
|
-
static create(value: any): HTMLElement;
|
|
6
|
-
static value(domNode: HTMLElement): {
|
|
7
|
-
key: string;
|
|
8
|
-
content_type: string;
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
export {};
|
package/dist/blots/MediaBlot.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import Quill from "quill";
|
|
2
|
-
const BlockEmbed = Quill.import("blots/block/embed");
|
|
3
|
-
export class MediaBlot extends BlockEmbed {
|
|
4
|
-
static create(value) {
|
|
5
|
-
const node = super.create();
|
|
6
|
-
node.setAttribute("data-media-key", value.key);
|
|
7
|
-
node.setAttribute("data-type", value.content_type);
|
|
8
|
-
node.classList.add("smart-media");
|
|
9
|
-
node.innerHTML = `<div style="border:1px dashed #aaa;padding:4px;">Media: ${value.key}</div>`;
|
|
10
|
-
if (value.content_type.startsWith("image/")) {
|
|
11
|
-
const img = document.createElement("img");
|
|
12
|
-
img.src = value.url || `/media/${value.key}`;
|
|
13
|
-
node.appendChild(img);
|
|
14
|
-
}
|
|
15
|
-
else if (value.content_type.startsWith("video/")) {
|
|
16
|
-
const video = document.createElement("video");
|
|
17
|
-
video.controls = true;
|
|
18
|
-
video.src = value.url || `/media/${value.key}`;
|
|
19
|
-
node.appendChild(video);
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
const link = document.createElement("a");
|
|
23
|
-
link.href = value.url || `/media/${value.key}`;
|
|
24
|
-
link.innerText = `Download ${value.key}`;
|
|
25
|
-
node.appendChild(link);
|
|
26
|
-
}
|
|
27
|
-
return node;
|
|
28
|
-
}
|
|
29
|
-
static value(domNode) {
|
|
30
|
-
return {
|
|
31
|
-
key: domNode.getAttribute("data-media-key"),
|
|
32
|
-
content_type: domNode.getAttribute("data-type") || "application/octet-stream",
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
MediaBlot.blotName = "media";
|
|
37
|
-
MediaBlot.tagName = "div";
|
package/dist/blots/TableBlot.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import Quill from "quill";
|
|
2
|
-
const BlockEmbed = Quill.import("blots/block/embed");
|
|
3
|
-
export class TableBlot extends BlockEmbed {
|
|
4
|
-
static create(value) {
|
|
5
|
-
const node = super.create();
|
|
6
|
-
node.setAttribute("contenteditable", "false");
|
|
7
|
-
node.classList.add("smart-table");
|
|
8
|
-
// Render rows & cells
|
|
9
|
-
if (value && value.rows) {
|
|
10
|
-
value.rows.forEach((row) => {
|
|
11
|
-
const tr = document.createElement("tr");
|
|
12
|
-
row.cells.forEach((cell) => {
|
|
13
|
-
if (cell.placeholder)
|
|
14
|
-
return;
|
|
15
|
-
const td = document.createElement("td");
|
|
16
|
-
if (cell.colspan && cell.colspan > 1)
|
|
17
|
-
td.colSpan = cell.colspan;
|
|
18
|
-
if (cell.rowspan && cell.rowspan > 1)
|
|
19
|
-
td.rowSpan = cell.rowspan;
|
|
20
|
-
if (cell.style?.background)
|
|
21
|
-
td.style.background = cell.style.background;
|
|
22
|
-
if (cell.style?.border)
|
|
23
|
-
td.style.border = `${cell.style.border.width_px}px solid ${cell.style.border.color}`;
|
|
24
|
-
td.innerText = cell.text;
|
|
25
|
-
tr.appendChild(td);
|
|
26
|
-
});
|
|
27
|
-
node.appendChild(tr);
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
return node;
|
|
31
|
-
}
|
|
32
|
-
static value(domNode) {
|
|
33
|
-
// Store the JSON representation in Delta
|
|
34
|
-
const rows = [];
|
|
35
|
-
domNode.querySelectorAll("tr").forEach((tr) => {
|
|
36
|
-
const cells = [];
|
|
37
|
-
tr.querySelectorAll("td").forEach((td) => {
|
|
38
|
-
cells.push({
|
|
39
|
-
text: td.innerText,
|
|
40
|
-
colspan: td.colSpan || 1,
|
|
41
|
-
rowspan: td.rowSpan || 1,
|
|
42
|
-
style: td.style.background
|
|
43
|
-
? { background: td.style.background }
|
|
44
|
-
: {},
|
|
45
|
-
placeholder: false,
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
rows.push({ cells });
|
|
49
|
-
});
|
|
50
|
-
return { rows };
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
TableBlot.blotName = "table";
|
|
54
|
-
TableBlot.tagName = "TABLE";
|
package/dist/blots/index.d.ts
DELETED
package/dist/blots/index.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import Quill from "quill";
|
|
2
|
-
import { TableBlot } from "./TableBlot";
|
|
3
|
-
import { FormulaBlot } from "./FormulaBlot";
|
|
4
|
-
import { MediaBlot } from "./MediaBlot";
|
|
5
|
-
import { CommentBlot } from "./CommentBlot";
|
|
6
|
-
Quill.register({
|
|
7
|
-
"formats/table": TableBlot,
|
|
8
|
-
"formats/formula": FormulaBlot,
|
|
9
|
-
"formats/media": MediaBlot,
|
|
10
|
-
"formats/comment": CommentBlot,
|
|
11
|
-
});
|
|
12
|
-
export { TableBlot, FormulaBlot, MediaBlot, CommentBlot };
|