@x-plat/design-system 0.10.0 → 0.11.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/dist/components/AutoResizeTextArea/index.cjs +14 -5
- package/dist/components/AutoResizeTextArea/index.css +15 -0
- package/dist/components/AutoResizeTextArea/index.d.cts +4 -0
- package/dist/components/AutoResizeTextArea/index.d.ts +4 -0
- package/dist/components/AutoResizeTextArea/index.js +14 -5
- package/dist/components/Badge/index.d.cts +1 -1
- package/dist/components/Badge/index.d.ts +1 -1
- package/dist/components/ChatInput/index.cjs +20 -7
- package/dist/components/ChatInput/index.css +15 -0
- package/dist/components/ChatInput/index.d.cts +4 -0
- package/dist/components/ChatInput/index.d.ts +4 -0
- package/dist/components/ChatInput/index.js +20 -7
- package/dist/components/CheckBox/index.cjs +8 -3
- package/dist/components/CheckBox/index.css +15 -0
- package/dist/components/CheckBox/index.d.cts +1 -1
- package/dist/components/CheckBox/index.d.ts +1 -1
- package/dist/components/CheckBox/index.js +8 -3
- package/dist/components/DatePicker/index.cjs +179 -64
- package/dist/components/DatePicker/index.css +19 -0
- package/dist/components/DatePicker/index.js +179 -64
- package/dist/components/Drawer/index.cjs +83 -4
- package/dist/components/Drawer/index.js +83 -4
- package/dist/components/Dropdown/index.cjs +35 -7
- package/dist/components/Dropdown/index.js +35 -7
- package/dist/components/Editor/index.cjs +178 -21
- package/dist/components/Editor/index.js +178 -21
- package/dist/components/FieldMessage/index.cjs +8 -3
- package/dist/components/FieldMessage/index.css +15 -0
- package/dist/components/FieldMessage/index.d.cts +11 -4
- package/dist/components/FieldMessage/index.d.ts +11 -4
- package/dist/components/FieldMessage/index.js +8 -3
- package/dist/components/FileUpload/index.cjs +8 -3
- package/dist/components/FileUpload/index.css +15 -0
- package/dist/components/FileUpload/index.js +8 -3
- package/dist/components/ImageSelector/index.cjs +8 -3
- package/dist/components/ImageSelector/index.css +15 -0
- package/dist/components/ImageSelector/index.js +8 -3
- package/dist/components/Input/index.cjs +15 -5
- package/dist/components/Input/index.css +15 -0
- package/dist/components/Input/index.d.cts +4 -0
- package/dist/components/Input/index.d.ts +4 -0
- package/dist/components/Input/index.js +15 -5
- package/dist/components/Modal/index.cjs +84 -5
- package/dist/components/Modal/index.js +84 -5
- package/dist/components/NotificationBadge/index.d.cts +1 -1
- package/dist/components/NotificationBadge/index.d.ts +1 -1
- package/dist/components/Pagination/index.d.cts +1 -1
- package/dist/components/Pagination/index.d.ts +1 -1
- package/dist/components/PopOver/index.cjs +34 -6
- package/dist/components/PopOver/index.js +34 -6
- package/dist/components/Progress/index.d.cts +1 -1
- package/dist/components/Progress/index.d.ts +1 -1
- package/dist/components/Radio/index.cjs +8 -3
- package/dist/components/Radio/index.css +15 -0
- package/dist/components/Radio/index.d.cts +1 -1
- package/dist/components/Radio/index.d.ts +1 -1
- package/dist/components/Radio/index.js +8 -3
- package/dist/components/Select/index.cjs +99 -31
- package/dist/components/Select/index.css +54 -0
- package/dist/components/Select/index.d.cts +11 -0
- package/dist/components/Select/index.d.ts +11 -0
- package/dist/components/Select/index.js +99 -31
- package/dist/components/Spinner/index.d.cts +1 -1
- package/dist/components/Spinner/index.d.ts +1 -1
- package/dist/components/Stat/index.cjs +84 -0
- package/dist/components/Stat/index.css +96 -0
- package/dist/components/Stat/index.d.cts +37 -0
- package/dist/components/Stat/index.d.ts +37 -0
- package/dist/components/Stat/index.js +57 -0
- package/dist/components/Steps/index.d.cts +1 -1
- package/dist/components/Steps/index.d.ts +1 -1
- package/dist/components/Switch/index.cjs +14 -5
- package/dist/components/Switch/index.css +15 -0
- package/dist/components/Switch/index.d.cts +6 -1
- package/dist/components/Switch/index.d.ts +6 -1
- package/dist/components/Switch/index.js +14 -5
- package/dist/components/Tab/index.cjs +49 -25
- package/dist/components/Tab/index.css +4 -0
- package/dist/components/Tab/index.js +49 -25
- package/dist/components/TextArea/index.cjs +14 -4
- package/dist/components/TextArea/index.css +15 -0
- package/dist/components/TextArea/index.d.cts +4 -0
- package/dist/components/TextArea/index.d.ts +4 -0
- package/dist/components/TextArea/index.js +14 -4
- package/dist/components/TimePicker/index.cjs +448 -0
- package/dist/components/TimePicker/index.css +172 -0
- package/dist/components/TimePicker/index.d.cts +42 -0
- package/dist/components/TimePicker/index.d.ts +42 -0
- package/dist/components/TimePicker/index.js +411 -0
- package/dist/components/index.cjs +873 -359
- package/dist/components/index.css +275 -0
- package/dist/components/index.d.cts +3 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.js +871 -359
- package/dist/index.cjs +897 -383
- package/dist/index.css +275 -0
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +895 -383
- package/dist/status-njWqQWFV.d.cts +20 -0
- package/dist/status-njWqQWFV.d.ts +20 -0
- package/guidelines/AGENT_PROMPT.md +3 -3
- package/guidelines/Guidelines.md +3 -0
- package/guidelines/components/editor.md +86 -0
- package/guidelines/components/form.md +19 -0
- package/guidelines/components/stat.md +31 -0
- package/guidelines/components/time-picker.md +49 -0
- package/package.json +1 -1
- package/dist/status-g_KP3qT6.d.cts +0 -8
- package/dist/status-g_KP3qT6.d.ts +0 -8
|
@@ -35,7 +35,7 @@ __export(Dropdown_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(Dropdown_exports);
|
|
36
36
|
|
|
37
37
|
// src/components/Dropdown/Dropdown.tsx
|
|
38
|
-
var
|
|
38
|
+
var import_react4 = __toESM(require("react"), 1);
|
|
39
39
|
|
|
40
40
|
// src/tokens/hooks/Portal.tsx
|
|
41
41
|
var import_react_dom = __toESM(require("react-dom"), 1);
|
|
@@ -132,6 +132,30 @@ var useClickOutside = (refs, handler, enabled = true) => {
|
|
|
132
132
|
};
|
|
133
133
|
var useClickOutside_default = useClickOutside;
|
|
134
134
|
|
|
135
|
+
// src/tokens/hooks/useOverlayKeyboard.ts
|
|
136
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
137
|
+
var FOCUSABLE = [
|
|
138
|
+
"a[href]",
|
|
139
|
+
"button:not([disabled])",
|
|
140
|
+
"input:not([disabled]):not([type='hidden'])",
|
|
141
|
+
"select:not([disabled])",
|
|
142
|
+
"textarea:not([disabled])",
|
|
143
|
+
"[tabindex]:not([tabindex='-1'])"
|
|
144
|
+
].join(",");
|
|
145
|
+
var useEscapeKey = (enabled, onEscape) => {
|
|
146
|
+
import_react3.default.useEffect(() => {
|
|
147
|
+
if (!enabled) return;
|
|
148
|
+
const onKey = (e) => {
|
|
149
|
+
if (e.key !== "Escape") return;
|
|
150
|
+
if (e.defaultPrevented) return;
|
|
151
|
+
e.preventDefault();
|
|
152
|
+
onEscape();
|
|
153
|
+
};
|
|
154
|
+
document.addEventListener("keydown", onKey);
|
|
155
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
156
|
+
}, [enabled, onEscape]);
|
|
157
|
+
};
|
|
158
|
+
|
|
135
159
|
// ../../node_modules/clsx/dist/clsx.mjs
|
|
136
160
|
function r(e) {
|
|
137
161
|
var t, f, n = "";
|
|
@@ -152,14 +176,18 @@ var clsx_default = clsx;
|
|
|
152
176
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
153
177
|
var Dropdown = (props) => {
|
|
154
178
|
const { items, children } = props;
|
|
155
|
-
const [isOpen, setIsOpen] =
|
|
156
|
-
const [mounted, setMounted] =
|
|
157
|
-
const [visible, setVisible] =
|
|
158
|
-
const triggerRef =
|
|
159
|
-
const menuRef =
|
|
179
|
+
const [isOpen, setIsOpen] = import_react4.default.useState(false);
|
|
180
|
+
const [mounted, setMounted] = import_react4.default.useState(false);
|
|
181
|
+
const [visible, setVisible] = import_react4.default.useState(false);
|
|
182
|
+
const triggerRef = import_react4.default.useRef(null);
|
|
183
|
+
const menuRef = import_react4.default.useRef(null);
|
|
160
184
|
const { position, direction } = useAutoPosition_default(triggerRef, menuRef, mounted);
|
|
161
185
|
useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
|
|
162
|
-
|
|
186
|
+
useEscapeKey(isOpen, () => {
|
|
187
|
+
setIsOpen(false);
|
|
188
|
+
triggerRef.current?.focus({ preventScroll: true });
|
|
189
|
+
});
|
|
190
|
+
import_react4.default.useEffect(() => {
|
|
163
191
|
if (isOpen) {
|
|
164
192
|
setMounted(true);
|
|
165
193
|
const t2 = setTimeout(() => setVisible(true), 1);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/Dropdown/Dropdown.tsx
|
|
2
|
-
import
|
|
2
|
+
import React4 from "react";
|
|
3
3
|
|
|
4
4
|
// src/tokens/hooks/Portal.tsx
|
|
5
5
|
import ReactDOM from "react-dom";
|
|
@@ -96,6 +96,30 @@ var useClickOutside = (refs, handler, enabled = true) => {
|
|
|
96
96
|
};
|
|
97
97
|
var useClickOutside_default = useClickOutside;
|
|
98
98
|
|
|
99
|
+
// src/tokens/hooks/useOverlayKeyboard.ts
|
|
100
|
+
import React3 from "react";
|
|
101
|
+
var FOCUSABLE = [
|
|
102
|
+
"a[href]",
|
|
103
|
+
"button:not([disabled])",
|
|
104
|
+
"input:not([disabled]):not([type='hidden'])",
|
|
105
|
+
"select:not([disabled])",
|
|
106
|
+
"textarea:not([disabled])",
|
|
107
|
+
"[tabindex]:not([tabindex='-1'])"
|
|
108
|
+
].join(",");
|
|
109
|
+
var useEscapeKey = (enabled, onEscape) => {
|
|
110
|
+
React3.useEffect(() => {
|
|
111
|
+
if (!enabled) return;
|
|
112
|
+
const onKey = (e) => {
|
|
113
|
+
if (e.key !== "Escape") return;
|
|
114
|
+
if (e.defaultPrevented) return;
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
onEscape();
|
|
117
|
+
};
|
|
118
|
+
document.addEventListener("keydown", onKey);
|
|
119
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
120
|
+
}, [enabled, onEscape]);
|
|
121
|
+
};
|
|
122
|
+
|
|
99
123
|
// ../../node_modules/clsx/dist/clsx.mjs
|
|
100
124
|
function r(e) {
|
|
101
125
|
var t, f, n = "";
|
|
@@ -116,14 +140,18 @@ var clsx_default = clsx;
|
|
|
116
140
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
117
141
|
var Dropdown = (props) => {
|
|
118
142
|
const { items, children } = props;
|
|
119
|
-
const [isOpen, setIsOpen] =
|
|
120
|
-
const [mounted, setMounted] =
|
|
121
|
-
const [visible, setVisible] =
|
|
122
|
-
const triggerRef =
|
|
123
|
-
const menuRef =
|
|
143
|
+
const [isOpen, setIsOpen] = React4.useState(false);
|
|
144
|
+
const [mounted, setMounted] = React4.useState(false);
|
|
145
|
+
const [visible, setVisible] = React4.useState(false);
|
|
146
|
+
const triggerRef = React4.useRef(null);
|
|
147
|
+
const menuRef = React4.useRef(null);
|
|
124
148
|
const { position, direction } = useAutoPosition_default(triggerRef, menuRef, mounted);
|
|
125
149
|
useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
|
|
126
|
-
|
|
150
|
+
useEscapeKey(isOpen, () => {
|
|
151
|
+
setIsOpen(false);
|
|
152
|
+
triggerRef.current?.focus({ preventScroll: true });
|
|
153
|
+
});
|
|
154
|
+
React4.useEffect(() => {
|
|
127
155
|
if (isOpen) {
|
|
128
156
|
setMounted(true);
|
|
129
157
|
const t2 = setTimeout(() => setVisible(true), 1);
|
|
@@ -103,6 +103,15 @@ var ALLOWED_ATTRS = {
|
|
|
103
103
|
a: ["href", "target", "rel"],
|
|
104
104
|
img: ["src", "alt"]
|
|
105
105
|
};
|
|
106
|
+
var DROP_WITH_CONTENT = /* @__PURE__ */ new Set([
|
|
107
|
+
"script",
|
|
108
|
+
"style",
|
|
109
|
+
"noscript",
|
|
110
|
+
"iframe",
|
|
111
|
+
"object",
|
|
112
|
+
"embed",
|
|
113
|
+
"template"
|
|
114
|
+
]);
|
|
106
115
|
var sanitizeHtml = (input) => {
|
|
107
116
|
const doc = new DOMParser().parseFromString(`<div>${input}</div>`, "text/html");
|
|
108
117
|
const root = doc.body.firstElementChild;
|
|
@@ -111,6 +120,10 @@ var sanitizeHtml = (input) => {
|
|
|
111
120
|
const children = Array.from(node.children);
|
|
112
121
|
for (const child of children) {
|
|
113
122
|
const tag = child.tagName.toLowerCase();
|
|
123
|
+
if (DROP_WITH_CONTENT.has(tag)) {
|
|
124
|
+
node.removeChild(child);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
114
127
|
if (!ALLOWED_TAGS.has(tag)) {
|
|
115
128
|
while (child.firstChild) node.insertBefore(child.firstChild, child);
|
|
116
129
|
node.removeChild(child);
|
|
@@ -175,12 +188,16 @@ var Editor = (props) => {
|
|
|
175
188
|
const [linkOpen, setLinkOpen] = import_react.default.useState(false);
|
|
176
189
|
const [linkValue, setLinkValue] = import_react.default.useState("");
|
|
177
190
|
const [linkPos, setLinkPos] = import_react.default.useState({ top: 0, left: 0 });
|
|
191
|
+
const lastEmittedRef = import_react.default.useRef(null);
|
|
178
192
|
import_react.default.useEffect(() => {
|
|
179
|
-
|
|
180
|
-
if (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
193
|
+
const el = editorRef.current;
|
|
194
|
+
if (!el) return;
|
|
195
|
+
if (value === lastEmittedRef.current) return;
|
|
196
|
+
if (el.innerHTML === value) return;
|
|
197
|
+
el.innerHTML = sanitizeHtml(value || "");
|
|
198
|
+
if (!el.firstChild) el.innerHTML = "<p><br></p>";
|
|
199
|
+
lastEmittedRef.current = null;
|
|
200
|
+
updateEmpty();
|
|
184
201
|
}, [value]);
|
|
185
202
|
const updateEmpty = () => {
|
|
186
203
|
const el = editorRef.current;
|
|
@@ -196,10 +213,19 @@ var Editor = (props) => {
|
|
|
196
213
|
}
|
|
197
214
|
};
|
|
198
215
|
const restoreSelection = () => {
|
|
199
|
-
|
|
216
|
+
const range = lastRangeRef.current;
|
|
217
|
+
if (!range) return;
|
|
218
|
+
if (!editorRef.current?.contains(range.startContainer)) {
|
|
219
|
+
lastRangeRef.current = null;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
200
222
|
const sel = window.getSelection();
|
|
201
|
-
|
|
202
|
-
|
|
223
|
+
try {
|
|
224
|
+
sel?.removeAllRanges();
|
|
225
|
+
sel?.addRange(range);
|
|
226
|
+
} catch {
|
|
227
|
+
lastRangeRef.current = null;
|
|
228
|
+
}
|
|
203
229
|
};
|
|
204
230
|
const updateActiveFormats = () => {
|
|
205
231
|
if (!editorRef.current) return;
|
|
@@ -228,6 +254,96 @@ var Editor = (props) => {
|
|
|
228
254
|
const setBlock = (tag) => {
|
|
229
255
|
exec("formatBlock", tag);
|
|
230
256
|
};
|
|
257
|
+
const closestPre = () => {
|
|
258
|
+
const sel = window.getSelection();
|
|
259
|
+
const node = sel?.anchorNode;
|
|
260
|
+
if (!node || !editorRef.current?.contains(node)) return null;
|
|
261
|
+
const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
|
|
262
|
+
return el?.closest("pre") ?? null;
|
|
263
|
+
};
|
|
264
|
+
const insertCodeBlock = () => {
|
|
265
|
+
const el = editorRef.current;
|
|
266
|
+
const sel = window.getSelection();
|
|
267
|
+
if (!el || !sel || sel.rangeCount === 0) return;
|
|
268
|
+
const existing = closestPre();
|
|
269
|
+
if (existing) {
|
|
270
|
+
const p = document.createElement("p");
|
|
271
|
+
p.textContent = existing.textContent || "";
|
|
272
|
+
if (!p.textContent) p.innerHTML = "<br>";
|
|
273
|
+
existing.replaceWith(p);
|
|
274
|
+
placeCaretAtEnd(p);
|
|
275
|
+
handleInput();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const range = sel.getRangeAt(0);
|
|
279
|
+
const frag = range.cloneContents();
|
|
280
|
+
const holder = document.createElement("div");
|
|
281
|
+
holder.appendChild(frag);
|
|
282
|
+
const BLOCK = /^(p|div|h[1-6]|li|blockquote|pre|tr)$/i;
|
|
283
|
+
const linesOf = (node) => {
|
|
284
|
+
const out = [];
|
|
285
|
+
let cur = "";
|
|
286
|
+
for (const child of Array.from(node.childNodes)) {
|
|
287
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
288
|
+
cur += child.textContent || "";
|
|
289
|
+
} else if (child.nodeName === "BR") {
|
|
290
|
+
out.push(cur);
|
|
291
|
+
cur = "";
|
|
292
|
+
} else if (BLOCK.test(child.nodeName)) {
|
|
293
|
+
if (cur) {
|
|
294
|
+
out.push(cur);
|
|
295
|
+
cur = "";
|
|
296
|
+
}
|
|
297
|
+
const sub = linesOf(child);
|
|
298
|
+
out.push(...sub.length > 0 ? sub : [""]);
|
|
299
|
+
} else {
|
|
300
|
+
cur += linesOf(child).join("\n");
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (cur) out.push(cur);
|
|
304
|
+
return out;
|
|
305
|
+
};
|
|
306
|
+
const text = linesOf(holder).join("\n").replace(/\n+$/, "");
|
|
307
|
+
const pre = document.createElement("pre");
|
|
308
|
+
const code = document.createElement("code");
|
|
309
|
+
code.textContent = text || "";
|
|
310
|
+
pre.appendChild(code);
|
|
311
|
+
range.deleteContents();
|
|
312
|
+
range.insertNode(pre);
|
|
313
|
+
ensureParagraphAfter(pre);
|
|
314
|
+
placeCaretAtEnd(code);
|
|
315
|
+
handleInput();
|
|
316
|
+
};
|
|
317
|
+
const insertTextAtCaret = (text) => {
|
|
318
|
+
const sel = window.getSelection();
|
|
319
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
320
|
+
const range = sel.getRangeAt(0);
|
|
321
|
+
range.deleteContents();
|
|
322
|
+
const node = document.createTextNode(text);
|
|
323
|
+
range.insertNode(node);
|
|
324
|
+
range.setStartAfter(node);
|
|
325
|
+
range.collapse(true);
|
|
326
|
+
sel.removeAllRanges();
|
|
327
|
+
sel.addRange(range);
|
|
328
|
+
handleInput();
|
|
329
|
+
};
|
|
330
|
+
const ensureParagraphAfter = (node) => {
|
|
331
|
+
const next = node.nextElementSibling;
|
|
332
|
+
if (next && next.tagName === "P") return;
|
|
333
|
+
const p = document.createElement("p");
|
|
334
|
+
p.innerHTML = "<br>";
|
|
335
|
+
node.after(p);
|
|
336
|
+
};
|
|
337
|
+
const placeCaretAtEnd = (node) => {
|
|
338
|
+
const sel = window.getSelection();
|
|
339
|
+
if (!sel) return;
|
|
340
|
+
const r2 = document.createRange();
|
|
341
|
+
r2.selectNodeContents(node);
|
|
342
|
+
r2.collapse(false);
|
|
343
|
+
sel.removeAllRanges();
|
|
344
|
+
sel.addRange(r2);
|
|
345
|
+
editorRef.current?.focus();
|
|
346
|
+
};
|
|
231
347
|
const insertDivider = () => {
|
|
232
348
|
exec("insertHorizontalRule");
|
|
233
349
|
const el = editorRef.current;
|
|
@@ -274,7 +390,9 @@ var Editor = (props) => {
|
|
|
274
390
|
const range = sel.getRangeAt(0);
|
|
275
391
|
slashStartRangeRef.current = range.cloneRange();
|
|
276
392
|
const rect = range.getBoundingClientRect();
|
|
277
|
-
const
|
|
393
|
+
const editorEl = editorRef.current;
|
|
394
|
+
if (!editorEl) return;
|
|
395
|
+
const editorRect = editorEl.getBoundingClientRect();
|
|
278
396
|
setSlashPos({
|
|
279
397
|
top: rect.bottom - editorRect.top + 4,
|
|
280
398
|
left: rect.left - editorRect.left
|
|
@@ -332,7 +450,7 @@ var Editor = (props) => {
|
|
|
332
450
|
setBlock("blockquote");
|
|
333
451
|
break;
|
|
334
452
|
case "code-block":
|
|
335
|
-
|
|
453
|
+
insertCodeBlock();
|
|
336
454
|
break;
|
|
337
455
|
case "divider":
|
|
338
456
|
insertDivider();
|
|
@@ -350,7 +468,9 @@ var Editor = (props) => {
|
|
|
350
468
|
if (!sel || sel.rangeCount === 0) return;
|
|
351
469
|
const range = sel.getRangeAt(0);
|
|
352
470
|
const rect = range.getBoundingClientRect();
|
|
353
|
-
const
|
|
471
|
+
const editorEl2 = editorRef.current;
|
|
472
|
+
if (!editorEl2) return;
|
|
473
|
+
const editorRect = editorEl2.getBoundingClientRect();
|
|
354
474
|
setLinkPos({
|
|
355
475
|
top: rect.bottom - editorRect.top + 4,
|
|
356
476
|
left: rect.left - editorRect.left
|
|
@@ -383,13 +503,13 @@ var Editor = (props) => {
|
|
|
383
503
|
if (node.nodeType !== Node.TEXT_NODE) return false;
|
|
384
504
|
const text = (node.textContent || "").slice(0, range.startOffset);
|
|
385
505
|
const patterns = [
|
|
386
|
-
[
|
|
387
|
-
[
|
|
388
|
-
[
|
|
389
|
-
[
|
|
390
|
-
[
|
|
391
|
-
[/^1
|
|
392
|
-
[
|
|
506
|
+
[/^###$/, () => setBlock("h3")],
|
|
507
|
+
[/^##$/, () => setBlock("h2")],
|
|
508
|
+
[/^#$/, () => setBlock("h1")],
|
|
509
|
+
[/^-$/, () => exec("insertUnorderedList")],
|
|
510
|
+
[/^\*$/, () => exec("insertUnorderedList")],
|
|
511
|
+
[/^1\.$/, () => exec("insertOrderedList")],
|
|
512
|
+
[/^>$/, () => setBlock("blockquote")]
|
|
393
513
|
];
|
|
394
514
|
for (const [re, fn] of patterns) {
|
|
395
515
|
if (re.test(text)) {
|
|
@@ -409,7 +529,9 @@ var Editor = (props) => {
|
|
|
409
529
|
if (composingRef.current) return;
|
|
410
530
|
const el = editorRef.current;
|
|
411
531
|
if (!el) return;
|
|
412
|
-
|
|
532
|
+
const html = el.innerHTML;
|
|
533
|
+
lastEmittedRef.current = html;
|
|
534
|
+
onChange?.(html);
|
|
413
535
|
updateEmpty();
|
|
414
536
|
updateActiveFormats();
|
|
415
537
|
if (slashOpen) {
|
|
@@ -437,6 +559,31 @@ var Editor = (props) => {
|
|
|
437
559
|
};
|
|
438
560
|
const handleKeyDown = (e) => {
|
|
439
561
|
if (readOnly) return;
|
|
562
|
+
if (composingRef.current || e.nativeEvent.isComposing || e.keyCode === 229) return;
|
|
563
|
+
const pre = closestPre();
|
|
564
|
+
if (pre && !slashOpen) {
|
|
565
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
566
|
+
const text = pre.textContent || "";
|
|
567
|
+
if (text.endsWith("\n\n")) {
|
|
568
|
+
e.preventDefault();
|
|
569
|
+
const code = pre.querySelector("code") ?? pre;
|
|
570
|
+
code.textContent = text.replace(/\n\n$/, "");
|
|
571
|
+
ensureParagraphAfter(pre);
|
|
572
|
+
const next = pre.nextElementSibling;
|
|
573
|
+
if (next) placeCaretAtEnd(next);
|
|
574
|
+
handleInput();
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
e.preventDefault();
|
|
578
|
+
insertTextAtCaret("\n");
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
if (e.key === "Tab") {
|
|
582
|
+
e.preventDefault();
|
|
583
|
+
insertTextAtCaret(" ");
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
440
587
|
if (slashOpen) {
|
|
441
588
|
if (e.key === "ArrowDown") {
|
|
442
589
|
e.preventDefault();
|
|
@@ -483,6 +630,7 @@ var Editor = (props) => {
|
|
|
483
630
|
return;
|
|
484
631
|
}
|
|
485
632
|
}
|
|
633
|
+
if (pre) return;
|
|
486
634
|
if (enableSlashCommand && e.key === "/") {
|
|
487
635
|
setTimeout(openSlashMenu, 0);
|
|
488
636
|
}
|
|
@@ -495,8 +643,12 @@ var Editor = (props) => {
|
|
|
495
643
|
const handlePaste = (e) => {
|
|
496
644
|
if (readOnly) return;
|
|
497
645
|
e.preventDefault();
|
|
498
|
-
const html = e.clipboardData.getData("text/html");
|
|
499
646
|
const text = e.clipboardData.getData("text/plain");
|
|
647
|
+
if (closestPre()) {
|
|
648
|
+
insertTextAtCaret(text);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const html = e.clipboardData.getData("text/html");
|
|
500
652
|
const clean = html ? sanitizeHtml(html) : escapeHtml(text);
|
|
501
653
|
document.execCommand("insertHTML", false, clean);
|
|
502
654
|
handleInput();
|
|
@@ -520,6 +672,7 @@ var Editor = (props) => {
|
|
|
520
672
|
updateActiveFormats();
|
|
521
673
|
};
|
|
522
674
|
const onToolbarAction = (item) => {
|
|
675
|
+
if (closestPre() && item !== "code-block") return;
|
|
523
676
|
switch (item) {
|
|
524
677
|
case "bold":
|
|
525
678
|
exec("bold");
|
|
@@ -561,7 +714,7 @@ var Editor = (props) => {
|
|
|
561
714
|
setBlock("blockquote");
|
|
562
715
|
break;
|
|
563
716
|
case "code-block":
|
|
564
|
-
|
|
717
|
+
insertCodeBlock();
|
|
565
718
|
break;
|
|
566
719
|
case "link":
|
|
567
720
|
openLinkEditor();
|
|
@@ -591,6 +744,10 @@ var Editor = (props) => {
|
|
|
591
744
|
ref: editorRef,
|
|
592
745
|
contentEditable: !readOnly,
|
|
593
746
|
suppressContentEditableWarning: true,
|
|
747
|
+
role: "textbox",
|
|
748
|
+
"aria-multiline": "true",
|
|
749
|
+
"aria-readonly": readOnly || void 0,
|
|
750
|
+
"aria-label": placeholder,
|
|
594
751
|
className: "lib-xplat-editor__editable",
|
|
595
752
|
onInput: handleInput,
|
|
596
753
|
onKeyDown: handleKeyDown,
|