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.
Files changed (46) hide show
  1. package/README.md +83 -15
  2. package/dist/components/ClassicEditor.d.ts +32 -1
  3. package/dist/components/ClassicEditor.js +949 -251
  4. package/dist/components/MediaManager.d.ts +7 -0
  5. package/dist/components/MediaManager.js +85 -29
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +1 -0
  8. package/dist/standalone/classic-editor-embed.js +1 -1
  9. package/dist/theme.d.ts +3 -0
  10. package/dist/theme.js +78 -0
  11. package/package.json +13 -12
  12. package/dist/QuillEditor.d.ts +0 -8
  13. package/dist/QuillEditor.js +0 -34
  14. package/dist/app.d.ts +0 -6
  15. package/dist/app.js +0 -6
  16. package/dist/blots/CommentBlot.d.ts +0 -8
  17. package/dist/blots/CommentBlot.js +0 -17
  18. package/dist/blots/FormulaBlot.d.ts +0 -12
  19. package/dist/blots/FormulaBlot.js +0 -36
  20. package/dist/blots/MediaBlot.d.ts +0 -11
  21. package/dist/blots/MediaBlot.js +0 -37
  22. package/dist/blots/TableBlot.d.ts +0 -10
  23. package/dist/blots/TableBlot.js +0 -54
  24. package/dist/blots/index.d.ts +0 -5
  25. package/dist/blots/index.js +0 -12
  26. package/dist/components/DiagramEditor.d.ts +0 -5
  27. package/dist/components/DiagramEditor.js +0 -73
  28. package/dist/components/FormulaEditor.d.ts +0 -6
  29. package/dist/components/FormulaEditor.js +0 -86
  30. package/dist/components/InfoBox.d.ts +0 -7
  31. package/dist/components/InfoBox.js +0 -18
  32. package/dist/components/MCQBlock.d.ts +0 -13
  33. package/dist/components/MCQBlock.js +0 -29
  34. package/dist/components/SmartEditor.d.ts +0 -0
  35. package/dist/components/SmartEditor.js +0 -1
  36. package/dist/components/SmartTable.d.ts +0 -22
  37. package/dist/components/SmartTable.js +0 -629
  38. package/dist/components/TableContextMenu.d.ts +0 -11
  39. package/dist/components/TableContextMenu.js +0 -15
  40. package/dist/components/TableInsertDialog.d.ts +0 -7
  41. package/dist/components/TableInsertDialog.js +0 -42
  42. package/dist/hooks/useEditorSync.d.ts +0 -5
  43. package/dist/hooks/useEditorSync.js +0 -53
  44. package/dist/smart-editor.d.ts +0 -0
  45. package/dist/smart-editor.js +0 -1
  46. 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: "rgba(0,0,0,0.35)",
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: "#fff",
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: "0 12px 32px rgba(0,0,0,0.22)",
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 #eee",
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 #ddd",
113
- background: activeTab === "upload" ? "#f2f2f2" : "#fff",
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 #ddd",
118
- background: activeTab === "library" ? "#f2f2f2" : "#fff",
119
- }, children: "Library" })] }), _jsx("button", { onClick: onClose, children: "\u2715" })] }), error && (_jsx("div", { style: { color: "#b00020", 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
+ 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 #bbb",
129
+ border: "2px dashed var(--srte-border)",
126
130
  borderRadius: 10,
127
131
  padding: 24,
128
132
  textAlign: "center",
129
- color: "#333",
130
- background: "#fafafa",
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 #ddd",
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("button", { onClick: () => {
150
- onSelect(it);
151
- onClose();
152
- }, title: it.title || it.url, style: {
153
- display: "block",
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: "#fff",
158
- textAlign: "center",
159
- }, children: [_jsx("img", { src: it.url, alt: it.alt || "", style: {
160
- maxWidth: "100%",
161
- maxHeight: 100,
162
- display: "block",
163
- margin: "0 auto",
164
- objectFit: "cover",
165
- borderRadius: 6,
166
- } }), _jsx("div", { style: { fontSize: 11, marginTop: 6, color: "#333" }, children: it.width && it.height ? `${it.width}×${it.height}` : "" })] }, it.id || it.url))) })] }))] }) }));
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
@@ -1,2 +1,4 @@
1
1
  export { ClassicEditor } from './components/ClassicEditor';
2
2
  export type { MediaManagerAdapter, MediaItem, MediaSearchQuery } from './components/MediaManager';
3
+ export type { SrteTheme } from './theme';
4
+ export { SRTE_DEFAULT_CSS, ensureStyleSheet } from './theme';
package/dist/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export { ClassicEditor } from './components/ClassicEditor';
2
+ export { SRTE_DEFAULT_CSS, ensureStyleSheet } from './theme';
@@ -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) {
@@ -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.1.18",
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
- "runkitExampleFilename": "runkit-example.js",
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
+ }
@@ -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 {};
@@ -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
@@ -1,6 +0,0 @@
1
- import React from "react";
2
- export default function App(): React.DetailedReactHTMLElement<{
3
- style: {
4
- padding: number;
5
- };
6
- }, HTMLElement>;
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,8 +0,0 @@
1
- declare const Inline: any;
2
- export declare class CommentBlot extends Inline {
3
- static blotName: string;
4
- static tagName: string;
5
- static create(value: any): HTMLElement;
6
- static value(domNode: HTMLElement): string;
7
- }
8
- export {};
@@ -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 {};
@@ -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";
@@ -1,10 +0,0 @@
1
- declare const BlockEmbed: any;
2
- export declare class TableBlot extends BlockEmbed {
3
- static blotName: string;
4
- static tagName: string;
5
- static create(value: any): HTMLElement;
6
- static value(domNode: HTMLElement): {
7
- rows: any[];
8
- };
9
- }
10
- export {};
@@ -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";
@@ -1,5 +0,0 @@
1
- import { TableBlot } from "./TableBlot";
2
- import { FormulaBlot } from "./FormulaBlot";
3
- import { MediaBlot } from "./MediaBlot";
4
- import { CommentBlot } from "./CommentBlot";
5
- export { TableBlot, FormulaBlot, MediaBlot, CommentBlot };
@@ -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 };
@@ -1,5 +0,0 @@
1
- export declare function DiagramEditor({ open, onClose, onInsert, }: {
2
- open: boolean;
3
- onClose: () => void;
4
- onInsert: (dataUrl: string) => void;
5
- }): import("react/jsx-runtime").JSX.Element;