@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
|
@@ -67,6 +67,15 @@ var ALLOWED_ATTRS = {
|
|
|
67
67
|
a: ["href", "target", "rel"],
|
|
68
68
|
img: ["src", "alt"]
|
|
69
69
|
};
|
|
70
|
+
var DROP_WITH_CONTENT = /* @__PURE__ */ new Set([
|
|
71
|
+
"script",
|
|
72
|
+
"style",
|
|
73
|
+
"noscript",
|
|
74
|
+
"iframe",
|
|
75
|
+
"object",
|
|
76
|
+
"embed",
|
|
77
|
+
"template"
|
|
78
|
+
]);
|
|
70
79
|
var sanitizeHtml = (input) => {
|
|
71
80
|
const doc = new DOMParser().parseFromString(`<div>${input}</div>`, "text/html");
|
|
72
81
|
const root = doc.body.firstElementChild;
|
|
@@ -75,6 +84,10 @@ var sanitizeHtml = (input) => {
|
|
|
75
84
|
const children = Array.from(node.children);
|
|
76
85
|
for (const child of children) {
|
|
77
86
|
const tag = child.tagName.toLowerCase();
|
|
87
|
+
if (DROP_WITH_CONTENT.has(tag)) {
|
|
88
|
+
node.removeChild(child);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
78
91
|
if (!ALLOWED_TAGS.has(tag)) {
|
|
79
92
|
while (child.firstChild) node.insertBefore(child.firstChild, child);
|
|
80
93
|
node.removeChild(child);
|
|
@@ -139,12 +152,16 @@ var Editor = (props) => {
|
|
|
139
152
|
const [linkOpen, setLinkOpen] = React.useState(false);
|
|
140
153
|
const [linkValue, setLinkValue] = React.useState("");
|
|
141
154
|
const [linkPos, setLinkPos] = React.useState({ top: 0, left: 0 });
|
|
155
|
+
const lastEmittedRef = React.useRef(null);
|
|
142
156
|
React.useEffect(() => {
|
|
143
|
-
|
|
144
|
-
if (
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
const el = editorRef.current;
|
|
158
|
+
if (!el) return;
|
|
159
|
+
if (value === lastEmittedRef.current) return;
|
|
160
|
+
if (el.innerHTML === value) return;
|
|
161
|
+
el.innerHTML = sanitizeHtml(value || "");
|
|
162
|
+
if (!el.firstChild) el.innerHTML = "<p><br></p>";
|
|
163
|
+
lastEmittedRef.current = null;
|
|
164
|
+
updateEmpty();
|
|
148
165
|
}, [value]);
|
|
149
166
|
const updateEmpty = () => {
|
|
150
167
|
const el = editorRef.current;
|
|
@@ -160,10 +177,19 @@ var Editor = (props) => {
|
|
|
160
177
|
}
|
|
161
178
|
};
|
|
162
179
|
const restoreSelection = () => {
|
|
163
|
-
|
|
180
|
+
const range = lastRangeRef.current;
|
|
181
|
+
if (!range) return;
|
|
182
|
+
if (!editorRef.current?.contains(range.startContainer)) {
|
|
183
|
+
lastRangeRef.current = null;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
164
186
|
const sel = window.getSelection();
|
|
165
|
-
|
|
166
|
-
|
|
187
|
+
try {
|
|
188
|
+
sel?.removeAllRanges();
|
|
189
|
+
sel?.addRange(range);
|
|
190
|
+
} catch {
|
|
191
|
+
lastRangeRef.current = null;
|
|
192
|
+
}
|
|
167
193
|
};
|
|
168
194
|
const updateActiveFormats = () => {
|
|
169
195
|
if (!editorRef.current) return;
|
|
@@ -192,6 +218,96 @@ var Editor = (props) => {
|
|
|
192
218
|
const setBlock = (tag) => {
|
|
193
219
|
exec("formatBlock", tag);
|
|
194
220
|
};
|
|
221
|
+
const closestPre = () => {
|
|
222
|
+
const sel = window.getSelection();
|
|
223
|
+
const node = sel?.anchorNode;
|
|
224
|
+
if (!node || !editorRef.current?.contains(node)) return null;
|
|
225
|
+
const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
|
|
226
|
+
return el?.closest("pre") ?? null;
|
|
227
|
+
};
|
|
228
|
+
const insertCodeBlock = () => {
|
|
229
|
+
const el = editorRef.current;
|
|
230
|
+
const sel = window.getSelection();
|
|
231
|
+
if (!el || !sel || sel.rangeCount === 0) return;
|
|
232
|
+
const existing = closestPre();
|
|
233
|
+
if (existing) {
|
|
234
|
+
const p = document.createElement("p");
|
|
235
|
+
p.textContent = existing.textContent || "";
|
|
236
|
+
if (!p.textContent) p.innerHTML = "<br>";
|
|
237
|
+
existing.replaceWith(p);
|
|
238
|
+
placeCaretAtEnd(p);
|
|
239
|
+
handleInput();
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const range = sel.getRangeAt(0);
|
|
243
|
+
const frag = range.cloneContents();
|
|
244
|
+
const holder = document.createElement("div");
|
|
245
|
+
holder.appendChild(frag);
|
|
246
|
+
const BLOCK = /^(p|div|h[1-6]|li|blockquote|pre|tr)$/i;
|
|
247
|
+
const linesOf = (node) => {
|
|
248
|
+
const out = [];
|
|
249
|
+
let cur = "";
|
|
250
|
+
for (const child of Array.from(node.childNodes)) {
|
|
251
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
252
|
+
cur += child.textContent || "";
|
|
253
|
+
} else if (child.nodeName === "BR") {
|
|
254
|
+
out.push(cur);
|
|
255
|
+
cur = "";
|
|
256
|
+
} else if (BLOCK.test(child.nodeName)) {
|
|
257
|
+
if (cur) {
|
|
258
|
+
out.push(cur);
|
|
259
|
+
cur = "";
|
|
260
|
+
}
|
|
261
|
+
const sub = linesOf(child);
|
|
262
|
+
out.push(...sub.length > 0 ? sub : [""]);
|
|
263
|
+
} else {
|
|
264
|
+
cur += linesOf(child).join("\n");
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (cur) out.push(cur);
|
|
268
|
+
return out;
|
|
269
|
+
};
|
|
270
|
+
const text = linesOf(holder).join("\n").replace(/\n+$/, "");
|
|
271
|
+
const pre = document.createElement("pre");
|
|
272
|
+
const code = document.createElement("code");
|
|
273
|
+
code.textContent = text || "";
|
|
274
|
+
pre.appendChild(code);
|
|
275
|
+
range.deleteContents();
|
|
276
|
+
range.insertNode(pre);
|
|
277
|
+
ensureParagraphAfter(pre);
|
|
278
|
+
placeCaretAtEnd(code);
|
|
279
|
+
handleInput();
|
|
280
|
+
};
|
|
281
|
+
const insertTextAtCaret = (text) => {
|
|
282
|
+
const sel = window.getSelection();
|
|
283
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
284
|
+
const range = sel.getRangeAt(0);
|
|
285
|
+
range.deleteContents();
|
|
286
|
+
const node = document.createTextNode(text);
|
|
287
|
+
range.insertNode(node);
|
|
288
|
+
range.setStartAfter(node);
|
|
289
|
+
range.collapse(true);
|
|
290
|
+
sel.removeAllRanges();
|
|
291
|
+
sel.addRange(range);
|
|
292
|
+
handleInput();
|
|
293
|
+
};
|
|
294
|
+
const ensureParagraphAfter = (node) => {
|
|
295
|
+
const next = node.nextElementSibling;
|
|
296
|
+
if (next && next.tagName === "P") return;
|
|
297
|
+
const p = document.createElement("p");
|
|
298
|
+
p.innerHTML = "<br>";
|
|
299
|
+
node.after(p);
|
|
300
|
+
};
|
|
301
|
+
const placeCaretAtEnd = (node) => {
|
|
302
|
+
const sel = window.getSelection();
|
|
303
|
+
if (!sel) return;
|
|
304
|
+
const r2 = document.createRange();
|
|
305
|
+
r2.selectNodeContents(node);
|
|
306
|
+
r2.collapse(false);
|
|
307
|
+
sel.removeAllRanges();
|
|
308
|
+
sel.addRange(r2);
|
|
309
|
+
editorRef.current?.focus();
|
|
310
|
+
};
|
|
195
311
|
const insertDivider = () => {
|
|
196
312
|
exec("insertHorizontalRule");
|
|
197
313
|
const el = editorRef.current;
|
|
@@ -238,7 +354,9 @@ var Editor = (props) => {
|
|
|
238
354
|
const range = sel.getRangeAt(0);
|
|
239
355
|
slashStartRangeRef.current = range.cloneRange();
|
|
240
356
|
const rect = range.getBoundingClientRect();
|
|
241
|
-
const
|
|
357
|
+
const editorEl = editorRef.current;
|
|
358
|
+
if (!editorEl) return;
|
|
359
|
+
const editorRect = editorEl.getBoundingClientRect();
|
|
242
360
|
setSlashPos({
|
|
243
361
|
top: rect.bottom - editorRect.top + 4,
|
|
244
362
|
left: rect.left - editorRect.left
|
|
@@ -296,7 +414,7 @@ var Editor = (props) => {
|
|
|
296
414
|
setBlock("blockquote");
|
|
297
415
|
break;
|
|
298
416
|
case "code-block":
|
|
299
|
-
|
|
417
|
+
insertCodeBlock();
|
|
300
418
|
break;
|
|
301
419
|
case "divider":
|
|
302
420
|
insertDivider();
|
|
@@ -314,7 +432,9 @@ var Editor = (props) => {
|
|
|
314
432
|
if (!sel || sel.rangeCount === 0) return;
|
|
315
433
|
const range = sel.getRangeAt(0);
|
|
316
434
|
const rect = range.getBoundingClientRect();
|
|
317
|
-
const
|
|
435
|
+
const editorEl2 = editorRef.current;
|
|
436
|
+
if (!editorEl2) return;
|
|
437
|
+
const editorRect = editorEl2.getBoundingClientRect();
|
|
318
438
|
setLinkPos({
|
|
319
439
|
top: rect.bottom - editorRect.top + 4,
|
|
320
440
|
left: rect.left - editorRect.left
|
|
@@ -347,13 +467,13 @@ var Editor = (props) => {
|
|
|
347
467
|
if (node.nodeType !== Node.TEXT_NODE) return false;
|
|
348
468
|
const text = (node.textContent || "").slice(0, range.startOffset);
|
|
349
469
|
const patterns = [
|
|
350
|
-
[
|
|
351
|
-
[
|
|
352
|
-
[
|
|
353
|
-
[
|
|
354
|
-
[
|
|
355
|
-
[/^1
|
|
356
|
-
[
|
|
470
|
+
[/^###$/, () => setBlock("h3")],
|
|
471
|
+
[/^##$/, () => setBlock("h2")],
|
|
472
|
+
[/^#$/, () => setBlock("h1")],
|
|
473
|
+
[/^-$/, () => exec("insertUnorderedList")],
|
|
474
|
+
[/^\*$/, () => exec("insertUnorderedList")],
|
|
475
|
+
[/^1\.$/, () => exec("insertOrderedList")],
|
|
476
|
+
[/^>$/, () => setBlock("blockquote")]
|
|
357
477
|
];
|
|
358
478
|
for (const [re, fn] of patterns) {
|
|
359
479
|
if (re.test(text)) {
|
|
@@ -373,7 +493,9 @@ var Editor = (props) => {
|
|
|
373
493
|
if (composingRef.current) return;
|
|
374
494
|
const el = editorRef.current;
|
|
375
495
|
if (!el) return;
|
|
376
|
-
|
|
496
|
+
const html = el.innerHTML;
|
|
497
|
+
lastEmittedRef.current = html;
|
|
498
|
+
onChange?.(html);
|
|
377
499
|
updateEmpty();
|
|
378
500
|
updateActiveFormats();
|
|
379
501
|
if (slashOpen) {
|
|
@@ -401,6 +523,31 @@ var Editor = (props) => {
|
|
|
401
523
|
};
|
|
402
524
|
const handleKeyDown = (e) => {
|
|
403
525
|
if (readOnly) return;
|
|
526
|
+
if (composingRef.current || e.nativeEvent.isComposing || e.keyCode === 229) return;
|
|
527
|
+
const pre = closestPre();
|
|
528
|
+
if (pre && !slashOpen) {
|
|
529
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
530
|
+
const text = pre.textContent || "";
|
|
531
|
+
if (text.endsWith("\n\n")) {
|
|
532
|
+
e.preventDefault();
|
|
533
|
+
const code = pre.querySelector("code") ?? pre;
|
|
534
|
+
code.textContent = text.replace(/\n\n$/, "");
|
|
535
|
+
ensureParagraphAfter(pre);
|
|
536
|
+
const next = pre.nextElementSibling;
|
|
537
|
+
if (next) placeCaretAtEnd(next);
|
|
538
|
+
handleInput();
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
e.preventDefault();
|
|
542
|
+
insertTextAtCaret("\n");
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
if (e.key === "Tab") {
|
|
546
|
+
e.preventDefault();
|
|
547
|
+
insertTextAtCaret(" ");
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
404
551
|
if (slashOpen) {
|
|
405
552
|
if (e.key === "ArrowDown") {
|
|
406
553
|
e.preventDefault();
|
|
@@ -447,6 +594,7 @@ var Editor = (props) => {
|
|
|
447
594
|
return;
|
|
448
595
|
}
|
|
449
596
|
}
|
|
597
|
+
if (pre) return;
|
|
450
598
|
if (enableSlashCommand && e.key === "/") {
|
|
451
599
|
setTimeout(openSlashMenu, 0);
|
|
452
600
|
}
|
|
@@ -459,8 +607,12 @@ var Editor = (props) => {
|
|
|
459
607
|
const handlePaste = (e) => {
|
|
460
608
|
if (readOnly) return;
|
|
461
609
|
e.preventDefault();
|
|
462
|
-
const html = e.clipboardData.getData("text/html");
|
|
463
610
|
const text = e.clipboardData.getData("text/plain");
|
|
611
|
+
if (closestPre()) {
|
|
612
|
+
insertTextAtCaret(text);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
const html = e.clipboardData.getData("text/html");
|
|
464
616
|
const clean = html ? sanitizeHtml(html) : escapeHtml(text);
|
|
465
617
|
document.execCommand("insertHTML", false, clean);
|
|
466
618
|
handleInput();
|
|
@@ -484,6 +636,7 @@ var Editor = (props) => {
|
|
|
484
636
|
updateActiveFormats();
|
|
485
637
|
};
|
|
486
638
|
const onToolbarAction = (item) => {
|
|
639
|
+
if (closestPre() && item !== "code-block") return;
|
|
487
640
|
switch (item) {
|
|
488
641
|
case "bold":
|
|
489
642
|
exec("bold");
|
|
@@ -525,7 +678,7 @@ var Editor = (props) => {
|
|
|
525
678
|
setBlock("blockquote");
|
|
526
679
|
break;
|
|
527
680
|
case "code-block":
|
|
528
|
-
|
|
681
|
+
insertCodeBlock();
|
|
529
682
|
break;
|
|
530
683
|
case "link":
|
|
531
684
|
openLinkEditor();
|
|
@@ -555,6 +708,10 @@ var Editor = (props) => {
|
|
|
555
708
|
ref: editorRef,
|
|
556
709
|
contentEditable: !readOnly,
|
|
557
710
|
suppressContentEditableWarning: true,
|
|
711
|
+
role: "textbox",
|
|
712
|
+
"aria-multiline": "true",
|
|
713
|
+
"aria-readonly": readOnly || void 0,
|
|
714
|
+
"aria-label": placeholder,
|
|
558
715
|
className: "lib-xplat-editor__editable",
|
|
559
716
|
onInput: handleInput,
|
|
560
717
|
onKeyDown: handleKeyDown,
|
|
@@ -166,11 +166,16 @@ var FieldMessage = (props) => {
|
|
|
166
166
|
FieldMessage.displayName = "FieldMessage";
|
|
167
167
|
var FieldMessage_default = FieldMessage;
|
|
168
168
|
var Field = (props) => {
|
|
169
|
-
const { validations, children } = props;
|
|
170
|
-
|
|
169
|
+
const { label, required = false, htmlFor, validations, children } = props;
|
|
170
|
+
const hasMessages = !!validations && validations.length > 0;
|
|
171
|
+
if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
|
|
171
172
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lib-xplat-field", children: [
|
|
173
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "lib-xplat-field-label", htmlFor, children: [
|
|
174
|
+
label,
|
|
175
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
|
|
176
|
+
] }),
|
|
172
177
|
children,
|
|
173
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FieldMessage, { validations })
|
|
178
|
+
hasMessages && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FieldMessage, { validations })
|
|
174
179
|
] });
|
|
175
180
|
};
|
|
176
181
|
Field.displayName = "Field";
|
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
flex-direction: column;
|
|
5
5
|
gap: var(--spacing-space-2);
|
|
6
6
|
}
|
|
7
|
+
.lib-xplat-field-label {
|
|
8
|
+
font-family: var(--semantic-typo-label-2-m-font);
|
|
9
|
+
font-size: var(--semantic-typo-label-2-m-size);
|
|
10
|
+
line-height: var(--semantic-typo-label-2-m-lh);
|
|
11
|
+
font-weight: var(--semantic-typo-label-2-m-weight);
|
|
12
|
+
letter-spacing: var(--semantic-typo-label-2-m-ls);
|
|
13
|
+
color: var(--semantic-text-secondary);
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: var(--spacing-space-1);
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
}
|
|
19
|
+
.lib-xplat-field-label > .required {
|
|
20
|
+
color: var(--semantic-text-error);
|
|
21
|
+
}
|
|
7
22
|
.lib-xplat-field-message-wrap {
|
|
8
23
|
display: flex;
|
|
9
24
|
gap: var(--spacing-space-2);
|
|
@@ -24,11 +24,18 @@ declare const FieldMessage: {
|
|
|
24
24
|
* 기존 사용처의 DOM 구조와 레이아웃이 바뀌지 않도록 하기 위해서다.
|
|
25
25
|
* (폼 컨트롤들의 루트가 label·div·textarea 로 제각각이라 무조건 감싸면 깨진다.)
|
|
26
26
|
*/
|
|
27
|
+
interface FieldProps {
|
|
28
|
+
/** 컨트롤 위에 붙는 라벨. htmlFor 로 컨트롤과 연결된다 */
|
|
29
|
+
label?: React.ReactNode;
|
|
30
|
+
/** 라벨 뒤에 필수 표시(*)를 붙인다 */
|
|
31
|
+
required?: boolean;
|
|
32
|
+
/** 라벨의 htmlFor 와 컨트롤의 id 를 잇는 값 */
|
|
33
|
+
htmlFor?: string;
|
|
34
|
+
validations?: Validation[];
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
}
|
|
27
37
|
declare const Field: {
|
|
28
|
-
(props:
|
|
29
|
-
validations?: Validation[];
|
|
30
|
-
children: React.ReactNode;
|
|
31
|
-
}): react_jsx_runtime.JSX.Element;
|
|
38
|
+
(props: FieldProps): react_jsx_runtime.JSX.Element;
|
|
32
39
|
displayName: string;
|
|
33
40
|
};
|
|
34
41
|
/** validations 중 하나라도 error 면 true. aria-invalid 판정에 쓴다 */
|
|
@@ -24,11 +24,18 @@ declare const FieldMessage: {
|
|
|
24
24
|
* 기존 사용처의 DOM 구조와 레이아웃이 바뀌지 않도록 하기 위해서다.
|
|
25
25
|
* (폼 컨트롤들의 루트가 label·div·textarea 로 제각각이라 무조건 감싸면 깨진다.)
|
|
26
26
|
*/
|
|
27
|
+
interface FieldProps {
|
|
28
|
+
/** 컨트롤 위에 붙는 라벨. htmlFor 로 컨트롤과 연결된다 */
|
|
29
|
+
label?: React.ReactNode;
|
|
30
|
+
/** 라벨 뒤에 필수 표시(*)를 붙인다 */
|
|
31
|
+
required?: boolean;
|
|
32
|
+
/** 라벨의 htmlFor 와 컨트롤의 id 를 잇는 값 */
|
|
33
|
+
htmlFor?: string;
|
|
34
|
+
validations?: Validation[];
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
}
|
|
27
37
|
declare const Field: {
|
|
28
|
-
(props:
|
|
29
|
-
validations?: Validation[];
|
|
30
|
-
children: React.ReactNode;
|
|
31
|
-
}): react_jsx_runtime.JSX.Element;
|
|
38
|
+
(props: FieldProps): react_jsx_runtime.JSX.Element;
|
|
32
39
|
displayName: string;
|
|
33
40
|
};
|
|
34
41
|
/** validations 중 하나라도 error 면 true. aria-invalid 판정에 쓴다 */
|
|
@@ -138,11 +138,16 @@ var FieldMessage = (props) => {
|
|
|
138
138
|
FieldMessage.displayName = "FieldMessage";
|
|
139
139
|
var FieldMessage_default = FieldMessage;
|
|
140
140
|
var Field = (props) => {
|
|
141
|
-
const { validations, children } = props;
|
|
142
|
-
|
|
141
|
+
const { label, required = false, htmlFor, validations, children } = props;
|
|
142
|
+
const hasMessages = !!validations && validations.length > 0;
|
|
143
|
+
if (!label && !hasMessages) return /* @__PURE__ */ jsx4(Fragment, { children });
|
|
143
144
|
return /* @__PURE__ */ jsxs3("div", { className: "lib-xplat-field", children: [
|
|
145
|
+
label && /* @__PURE__ */ jsxs3("label", { className: "lib-xplat-field-label", htmlFor, children: [
|
|
146
|
+
label,
|
|
147
|
+
required && /* @__PURE__ */ jsx4("span", { className: "required", "aria-hidden": "true", children: "*" })
|
|
148
|
+
] }),
|
|
144
149
|
children,
|
|
145
|
-
/* @__PURE__ */ jsx4(FieldMessage, { validations })
|
|
150
|
+
hasMessages && /* @__PURE__ */ jsx4(FieldMessage, { validations })
|
|
146
151
|
] });
|
|
147
152
|
};
|
|
148
153
|
Field.displayName = "Field";
|
|
@@ -1076,11 +1076,16 @@ var FieldMessage = (props) => {
|
|
|
1076
1076
|
};
|
|
1077
1077
|
FieldMessage.displayName = "FieldMessage";
|
|
1078
1078
|
var Field = (props) => {
|
|
1079
|
-
const { validations, children } = props;
|
|
1080
|
-
|
|
1079
|
+
const { label, required = false, htmlFor, validations, children } = props;
|
|
1080
|
+
const hasMessages = !!validations && validations.length > 0;
|
|
1081
|
+
if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime295.jsx)(import_jsx_runtime295.Fragment, { children });
|
|
1081
1082
|
return /* @__PURE__ */ (0, import_jsx_runtime295.jsxs)("div", { className: "lib-xplat-field", children: [
|
|
1083
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime295.jsxs)("label", { className: "lib-xplat-field-label", htmlFor, children: [
|
|
1084
|
+
label,
|
|
1085
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime295.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
|
|
1086
|
+
] }),
|
|
1082
1087
|
children,
|
|
1083
|
-
/* @__PURE__ */ (0, import_jsx_runtime295.jsx)(FieldMessage, { validations })
|
|
1088
|
+
hasMessages && /* @__PURE__ */ (0, import_jsx_runtime295.jsx)(FieldMessage, { validations })
|
|
1084
1089
|
] });
|
|
1085
1090
|
};
|
|
1086
1091
|
Field.displayName = "Field";
|
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
flex-direction: column;
|
|
5
5
|
gap: var(--spacing-space-2);
|
|
6
6
|
}
|
|
7
|
+
.lib-xplat-field-label {
|
|
8
|
+
font-family: var(--semantic-typo-label-2-m-font);
|
|
9
|
+
font-size: var(--semantic-typo-label-2-m-size);
|
|
10
|
+
line-height: var(--semantic-typo-label-2-m-lh);
|
|
11
|
+
font-weight: var(--semantic-typo-label-2-m-weight);
|
|
12
|
+
letter-spacing: var(--semantic-typo-label-2-m-ls);
|
|
13
|
+
color: var(--semantic-text-secondary);
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: var(--spacing-space-1);
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
}
|
|
19
|
+
.lib-xplat-field-label > .required {
|
|
20
|
+
color: var(--semantic-text-error);
|
|
21
|
+
}
|
|
7
22
|
.lib-xplat-field-message-wrap {
|
|
8
23
|
display: flex;
|
|
9
24
|
gap: var(--spacing-space-2);
|
|
@@ -1040,11 +1040,16 @@ var FieldMessage = (props) => {
|
|
|
1040
1040
|
};
|
|
1041
1041
|
FieldMessage.displayName = "FieldMessage";
|
|
1042
1042
|
var Field = (props) => {
|
|
1043
|
-
const { validations, children } = props;
|
|
1044
|
-
|
|
1043
|
+
const { label, required = false, htmlFor, validations, children } = props;
|
|
1044
|
+
const hasMessages = !!validations && validations.length > 0;
|
|
1045
|
+
if (!label && !hasMessages) return /* @__PURE__ */ jsx295(Fragment, { children });
|
|
1045
1046
|
return /* @__PURE__ */ jsxs189("div", { className: "lib-xplat-field", children: [
|
|
1047
|
+
label && /* @__PURE__ */ jsxs189("label", { className: "lib-xplat-field-label", htmlFor, children: [
|
|
1048
|
+
label,
|
|
1049
|
+
required && /* @__PURE__ */ jsx295("span", { className: "required", "aria-hidden": "true", children: "*" })
|
|
1050
|
+
] }),
|
|
1046
1051
|
children,
|
|
1047
|
-
/* @__PURE__ */ jsx295(FieldMessage, { validations })
|
|
1052
|
+
hasMessages && /* @__PURE__ */ jsx295(FieldMessage, { validations })
|
|
1048
1053
|
] });
|
|
1049
1054
|
};
|
|
1050
1055
|
Field.displayName = "Field";
|
|
@@ -1143,11 +1143,16 @@ var FieldMessage = (props) => {
|
|
|
1143
1143
|
};
|
|
1144
1144
|
FieldMessage.displayName = "FieldMessage";
|
|
1145
1145
|
var Field = (props) => {
|
|
1146
|
-
const { validations, children } = props;
|
|
1147
|
-
|
|
1146
|
+
const { label, required = false, htmlFor, validations, children } = props;
|
|
1147
|
+
const hasMessages = !!validations && validations.length > 0;
|
|
1148
|
+
if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime295.jsx)(import_jsx_runtime295.Fragment, { children });
|
|
1148
1149
|
return /* @__PURE__ */ (0, import_jsx_runtime295.jsxs)("div", { className: "lib-xplat-field", children: [
|
|
1150
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime295.jsxs)("label", { className: "lib-xplat-field-label", htmlFor, children: [
|
|
1151
|
+
label,
|
|
1152
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime295.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
|
|
1153
|
+
] }),
|
|
1149
1154
|
children,
|
|
1150
|
-
/* @__PURE__ */ (0, import_jsx_runtime295.jsx)(FieldMessage, { validations })
|
|
1155
|
+
hasMessages && /* @__PURE__ */ (0, import_jsx_runtime295.jsx)(FieldMessage, { validations })
|
|
1151
1156
|
] });
|
|
1152
1157
|
};
|
|
1153
1158
|
Field.displayName = "Field";
|
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
flex-direction: column;
|
|
5
5
|
gap: var(--spacing-space-2);
|
|
6
6
|
}
|
|
7
|
+
.lib-xplat-field-label {
|
|
8
|
+
font-family: var(--semantic-typo-label-2-m-font);
|
|
9
|
+
font-size: var(--semantic-typo-label-2-m-size);
|
|
10
|
+
line-height: var(--semantic-typo-label-2-m-lh);
|
|
11
|
+
font-weight: var(--semantic-typo-label-2-m-weight);
|
|
12
|
+
letter-spacing: var(--semantic-typo-label-2-m-ls);
|
|
13
|
+
color: var(--semantic-text-secondary);
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: var(--spacing-space-1);
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
}
|
|
19
|
+
.lib-xplat-field-label > .required {
|
|
20
|
+
color: var(--semantic-text-error);
|
|
21
|
+
}
|
|
7
22
|
.lib-xplat-field-message-wrap {
|
|
8
23
|
display: flex;
|
|
9
24
|
gap: var(--spacing-space-2);
|
|
@@ -1107,11 +1107,16 @@ var FieldMessage = (props) => {
|
|
|
1107
1107
|
};
|
|
1108
1108
|
FieldMessage.displayName = "FieldMessage";
|
|
1109
1109
|
var Field = (props) => {
|
|
1110
|
-
const { validations, children } = props;
|
|
1111
|
-
|
|
1110
|
+
const { label, required = false, htmlFor, validations, children } = props;
|
|
1111
|
+
const hasMessages = !!validations && validations.length > 0;
|
|
1112
|
+
if (!label && !hasMessages) return /* @__PURE__ */ jsx295(Fragment, { children });
|
|
1112
1113
|
return /* @__PURE__ */ jsxs189("div", { className: "lib-xplat-field", children: [
|
|
1114
|
+
label && /* @__PURE__ */ jsxs189("label", { className: "lib-xplat-field-label", htmlFor, children: [
|
|
1115
|
+
label,
|
|
1116
|
+
required && /* @__PURE__ */ jsx295("span", { className: "required", "aria-hidden": "true", children: "*" })
|
|
1117
|
+
] }),
|
|
1113
1118
|
children,
|
|
1114
|
-
/* @__PURE__ */ jsx295(FieldMessage, { validations })
|
|
1119
|
+
hasMessages && /* @__PURE__ */ jsx295(FieldMessage, { validations })
|
|
1115
1120
|
] });
|
|
1116
1121
|
};
|
|
1117
1122
|
Field.displayName = "Field";
|
|
@@ -178,11 +178,16 @@ var FieldMessage = (props) => {
|
|
|
178
178
|
FieldMessage.displayName = "FieldMessage";
|
|
179
179
|
var FieldMessage_default = FieldMessage;
|
|
180
180
|
var Field = (props) => {
|
|
181
|
-
const { validations, children } = props;
|
|
182
|
-
|
|
181
|
+
const { label, required = false, htmlFor, validations, children } = props;
|
|
182
|
+
const hasMessages = !!validations && validations.length > 0;
|
|
183
|
+
if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
|
|
183
184
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lib-xplat-field", children: [
|
|
185
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "lib-xplat-field-label", htmlFor, children: [
|
|
186
|
+
label,
|
|
187
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
|
|
188
|
+
] }),
|
|
184
189
|
children,
|
|
185
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FieldMessage, { validations })
|
|
190
|
+
hasMessages && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FieldMessage, { validations })
|
|
186
191
|
] });
|
|
187
192
|
};
|
|
188
193
|
Field.displayName = "Field";
|
|
@@ -281,8 +286,12 @@ var Input = import_react.default.forwardRef((props, ref) => {
|
|
|
281
286
|
suffix,
|
|
282
287
|
disabled,
|
|
283
288
|
validations,
|
|
289
|
+
label,
|
|
290
|
+
required,
|
|
284
291
|
...inputProps
|
|
285
292
|
} = props;
|
|
293
|
+
const autoId = import_react.default.useId();
|
|
294
|
+
const fieldId = inputProps.id ?? autoId;
|
|
286
295
|
const localValue = formatValue(type, value);
|
|
287
296
|
const handleChange = (e) => {
|
|
288
297
|
let val = e.target.value;
|
|
@@ -298,7 +307,7 @@ var Input = import_react.default.forwardRef((props, ref) => {
|
|
|
298
307
|
onChange(event);
|
|
299
308
|
}
|
|
300
309
|
};
|
|
301
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: clsx_default("lib-xplat-input-wrap", className), children: [
|
|
310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label, required, htmlFor: fieldId, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: clsx_default("lib-xplat-input-wrap", className), children: [
|
|
302
311
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
303
312
|
"div",
|
|
304
313
|
{
|
|
@@ -309,6 +318,7 @@ var Input = import_react.default.forwardRef((props, ref) => {
|
|
|
309
318
|
{
|
|
310
319
|
...inputProps,
|
|
311
320
|
ref,
|
|
321
|
+
id: fieldId,
|
|
312
322
|
disabled,
|
|
313
323
|
type: type === "number" ? "text" : type,
|
|
314
324
|
value: localValue,
|
|
@@ -321,7 +331,7 @@ var Input = import_react.default.forwardRef((props, ref) => {
|
|
|
321
331
|
}
|
|
322
332
|
),
|
|
323
333
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FieldMessage_default, { validations })
|
|
324
|
-
] });
|
|
334
|
+
] }) });
|
|
325
335
|
});
|
|
326
336
|
Input.displayName = "Input";
|
|
327
337
|
var Input_default = Input;
|
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
flex-direction: column;
|
|
5
5
|
gap: var(--spacing-space-2);
|
|
6
6
|
}
|
|
7
|
+
.lib-xplat-field-label {
|
|
8
|
+
font-family: var(--semantic-typo-label-2-m-font);
|
|
9
|
+
font-size: var(--semantic-typo-label-2-m-size);
|
|
10
|
+
line-height: var(--semantic-typo-label-2-m-lh);
|
|
11
|
+
font-weight: var(--semantic-typo-label-2-m-weight);
|
|
12
|
+
letter-spacing: var(--semantic-typo-label-2-m-ls);
|
|
13
|
+
color: var(--semantic-text-secondary);
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: var(--spacing-space-1);
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
}
|
|
19
|
+
.lib-xplat-field-label > .required {
|
|
20
|
+
color: var(--semantic-text-error);
|
|
21
|
+
}
|
|
7
22
|
.lib-xplat-field-message-wrap {
|
|
8
23
|
display: flex;
|
|
9
24
|
gap: var(--spacing-space-2);
|
|
@@ -9,6 +9,10 @@ interface InputProps extends Omit<InputAttributes, "size"> {
|
|
|
9
9
|
/** 높이와 글자 크기 */
|
|
10
10
|
size?: InputSize$1;
|
|
11
11
|
validations?: Validation[];
|
|
12
|
+
/** 컨트롤 위에 붙는 라벨. 클릭하면 입력칸에 포커스가 간다 */
|
|
13
|
+
label?: React.ReactNode;
|
|
14
|
+
/** 라벨 뒤에 필수 표시(*)를 붙인다 */
|
|
15
|
+
required?: boolean;
|
|
12
16
|
/** 입력칸 오른쪽에 붙일 요소 (단위·아이콘 등) */
|
|
13
17
|
suffix?: React.ReactNode;
|
|
14
18
|
}
|
|
@@ -9,6 +9,10 @@ interface InputProps extends Omit<InputAttributes, "size"> {
|
|
|
9
9
|
/** 높이와 글자 크기 */
|
|
10
10
|
size?: InputSize$1;
|
|
11
11
|
validations?: Validation[];
|
|
12
|
+
/** 컨트롤 위에 붙는 라벨. 클릭하면 입력칸에 포커스가 간다 */
|
|
13
|
+
label?: React.ReactNode;
|
|
14
|
+
/** 라벨 뒤에 필수 표시(*)를 붙인다 */
|
|
15
|
+
required?: boolean;
|
|
12
16
|
/** 입력칸 오른쪽에 붙일 요소 (단위·아이콘 등) */
|
|
13
17
|
suffix?: React.ReactNode;
|
|
14
18
|
}
|