@x-plat/design-system 0.9.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.
Files changed (156) hide show
  1. package/dist/components/AutoResizeTextArea/index.cjs +14 -5
  2. package/dist/components/AutoResizeTextArea/index.css +15 -0
  3. package/dist/components/AutoResizeTextArea/index.d.cts +5 -0
  4. package/dist/components/AutoResizeTextArea/index.d.ts +5 -0
  5. package/dist/components/AutoResizeTextArea/index.js +14 -5
  6. package/dist/components/Badge/index.cjs +5 -1
  7. package/dist/components/Badge/index.css +1 -0
  8. package/dist/components/Badge/index.d.cts +1 -0
  9. package/dist/components/Badge/index.d.ts +1 -0
  10. package/dist/components/Badge/index.js +5 -1
  11. package/dist/components/Box/index.d.cts +3 -0
  12. package/dist/components/Box/index.d.ts +3 -0
  13. package/dist/components/Chart/index.cjs +7 -6
  14. package/dist/components/Chart/index.d.cts +12 -1
  15. package/dist/components/Chart/index.d.ts +12 -1
  16. package/dist/components/Chart/index.js +7 -6
  17. package/dist/components/ChatInput/index.cjs +20 -7
  18. package/dist/components/ChatInput/index.css +15 -0
  19. package/dist/components/ChatInput/index.d.cts +10 -0
  20. package/dist/components/ChatInput/index.d.ts +10 -0
  21. package/dist/components/ChatInput/index.js +20 -7
  22. package/dist/components/CheckBox/index.cjs +14 -5
  23. package/dist/components/CheckBox/index.css +17 -0
  24. package/dist/components/CheckBox/index.d.cts +10 -2
  25. package/dist/components/CheckBox/index.d.ts +10 -2
  26. package/dist/components/CheckBox/index.js +14 -5
  27. package/dist/components/Chip/index.d.cts +2 -0
  28. package/dist/components/Chip/index.d.ts +2 -0
  29. package/dist/components/DatePicker/index.cjs +181 -65
  30. package/dist/components/DatePicker/index.css +19 -0
  31. package/dist/components/DatePicker/index.js +181 -65
  32. package/dist/components/Drawer/index.cjs +83 -4
  33. package/dist/components/Drawer/index.d.cts +1 -0
  34. package/dist/components/Drawer/index.d.ts +1 -0
  35. package/dist/components/Drawer/index.js +83 -4
  36. package/dist/components/Dropdown/index.cjs +35 -7
  37. package/dist/components/Dropdown/index.js +35 -7
  38. package/dist/components/Editor/index.cjs +178 -21
  39. package/dist/components/Editor/index.js +178 -21
  40. package/dist/components/FieldMessage/index.cjs +8 -3
  41. package/dist/components/FieldMessage/index.css +15 -0
  42. package/dist/components/FieldMessage/index.d.cts +11 -4
  43. package/dist/components/FieldMessage/index.d.ts +11 -4
  44. package/dist/components/FieldMessage/index.js +8 -3
  45. package/dist/components/FileUpload/index.cjs +8 -3
  46. package/dist/components/FileUpload/index.css +15 -0
  47. package/dist/components/FileUpload/index.d.cts +6 -0
  48. package/dist/components/FileUpload/index.d.ts +6 -0
  49. package/dist/components/FileUpload/index.js +8 -3
  50. package/dist/components/ImageSelector/index.cjs +8 -3
  51. package/dist/components/ImageSelector/index.css +15 -0
  52. package/dist/components/ImageSelector/index.d.cts +3 -0
  53. package/dist/components/ImageSelector/index.d.ts +3 -0
  54. package/dist/components/ImageSelector/index.js +8 -3
  55. package/dist/components/Input/index.cjs +15 -5
  56. package/dist/components/Input/index.css +15 -0
  57. package/dist/components/Input/index.d.cts +8 -0
  58. package/dist/components/Input/index.d.ts +8 -0
  59. package/dist/components/Input/index.js +15 -5
  60. package/dist/components/Modal/index.cjs +84 -5
  61. package/dist/components/Modal/index.js +84 -5
  62. package/dist/components/NotificationBadge/index.cjs +5 -1
  63. package/dist/components/NotificationBadge/index.css +1 -0
  64. package/dist/components/NotificationBadge/index.d.cts +6 -2
  65. package/dist/components/NotificationBadge/index.d.ts +6 -2
  66. package/dist/components/NotificationBadge/index.js +5 -1
  67. package/dist/components/Pagination/index.cjs +6 -2
  68. package/dist/components/Pagination/index.css +1 -0
  69. package/dist/components/Pagination/index.d.cts +13 -2
  70. package/dist/components/Pagination/index.d.ts +13 -2
  71. package/dist/components/Pagination/index.js +6 -2
  72. package/dist/components/PopOver/index.cjs +34 -6
  73. package/dist/components/PopOver/index.js +34 -6
  74. package/dist/components/Progress/index.cjs +6 -2
  75. package/dist/components/Progress/index.css +1 -0
  76. package/dist/components/Progress/index.d.cts +11 -2
  77. package/dist/components/Progress/index.d.ts +11 -2
  78. package/dist/components/Progress/index.js +6 -2
  79. package/dist/components/Radio/index.cjs +14 -5
  80. package/dist/components/Radio/index.css +17 -0
  81. package/dist/components/Radio/index.d.cts +11 -2
  82. package/dist/components/Radio/index.d.ts +11 -2
  83. package/dist/components/Radio/index.js +14 -5
  84. package/dist/components/Select/index.cjs +99 -31
  85. package/dist/components/Select/index.css +54 -0
  86. package/dist/components/Select/index.d.cts +16 -1
  87. package/dist/components/Select/index.d.ts +16 -1
  88. package/dist/components/Select/index.js +99 -31
  89. package/dist/components/Skeleton/index.d.cts +3 -0
  90. package/dist/components/Skeleton/index.d.ts +3 -0
  91. package/dist/components/Spinner/index.cjs +6 -2
  92. package/dist/components/Spinner/index.css +1 -0
  93. package/dist/components/Spinner/index.d.cts +8 -2
  94. package/dist/components/Spinner/index.d.ts +8 -2
  95. package/dist/components/Spinner/index.js +6 -2
  96. package/dist/components/Stat/index.cjs +84 -0
  97. package/dist/components/Stat/index.css +96 -0
  98. package/dist/components/Stat/index.d.cts +37 -0
  99. package/dist/components/Stat/index.d.ts +37 -0
  100. package/dist/components/Stat/index.js +57 -0
  101. package/dist/components/Steps/index.cjs +6 -2
  102. package/dist/components/Steps/index.css +11 -0
  103. package/dist/components/Steps/index.d.cts +9 -2
  104. package/dist/components/Steps/index.d.ts +9 -2
  105. package/dist/components/Steps/index.js +6 -2
  106. package/dist/components/Switch/index.cjs +20 -7
  107. package/dist/components/Switch/index.css +17 -0
  108. package/dist/components/Switch/index.d.cts +15 -2
  109. package/dist/components/Switch/index.d.ts +15 -2
  110. package/dist/components/Switch/index.js +20 -7
  111. package/dist/components/Tab/index.cjs +51 -26
  112. package/dist/components/Tab/index.css +4 -0
  113. package/dist/components/Tab/index.d.cts +12 -1
  114. package/dist/components/Tab/index.d.ts +12 -1
  115. package/dist/components/Tab/index.js +51 -26
  116. package/dist/components/Tag/index.cjs +5 -3
  117. package/dist/components/Tag/index.d.cts +12 -2
  118. package/dist/components/Tag/index.d.ts +12 -2
  119. package/dist/components/Tag/index.js +5 -3
  120. package/dist/components/TextArea/index.cjs +14 -4
  121. package/dist/components/TextArea/index.css +15 -0
  122. package/dist/components/TextArea/index.d.cts +7 -0
  123. package/dist/components/TextArea/index.d.ts +7 -0
  124. package/dist/components/TextArea/index.js +14 -4
  125. package/dist/components/TimePicker/index.cjs +448 -0
  126. package/dist/components/TimePicker/index.css +172 -0
  127. package/dist/components/TimePicker/index.d.cts +42 -0
  128. package/dist/components/TimePicker/index.d.ts +42 -0
  129. package/dist/components/TimePicker/index.js +411 -0
  130. package/dist/components/Tooltip/index.cjs +4 -2
  131. package/dist/components/Tooltip/index.d.cts +11 -1
  132. package/dist/components/Tooltip/index.d.ts +11 -1
  133. package/dist/components/Tooltip/index.js +4 -2
  134. package/dist/components/index.cjs +916 -385
  135. package/dist/components/index.css +296 -0
  136. package/dist/components/index.d.cts +3 -0
  137. package/dist/components/index.d.ts +3 -0
  138. package/dist/components/index.js +914 -385
  139. package/dist/index.cjs +940 -409
  140. package/dist/index.css +296 -0
  141. package/dist/index.d.cts +3 -0
  142. package/dist/index.d.ts +3 -0
  143. package/dist/index.js +938 -409
  144. package/dist/status-njWqQWFV.d.cts +20 -0
  145. package/dist/status-njWqQWFV.d.ts +20 -0
  146. package/guidelines/AGENT_PROMPT.md +18 -3
  147. package/guidelines/Guidelines.md +3 -0
  148. package/guidelines/components/card.md +3 -3
  149. package/guidelines/components/editor.md +86 -0
  150. package/guidelines/components/feedback.md +1 -1
  151. package/guidelines/components/form.md +19 -0
  152. package/guidelines/components/navigation.md +16 -15
  153. package/guidelines/components/overlay.md +43 -4
  154. package/guidelines/components/stat.md +31 -0
  155. package/guidelines/components/time-picker.md +49 -0
  156. package/package.json +1 -1
@@ -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
- if (!editorRef.current) return;
144
- if (editorRef.current.innerHTML !== value) {
145
- editorRef.current.innerHTML = sanitizeHtml(value || "");
146
- updateEmpty();
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
- if (!lastRangeRef.current) return;
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
- sel?.removeAllRanges();
166
- sel?.addRange(lastRangeRef.current);
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 editorRect = editorRef.current.getBoundingClientRect();
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
- setBlock("pre");
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 editorRect = editorRef.current.getBoundingClientRect();
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
- [/^###\s$/, () => setBlock("h3")],
351
- [/^##\s$/, () => setBlock("h2")],
352
- [/^#\s$/, () => setBlock("h1")],
353
- [/^-\s$/, () => exec("insertUnorderedList")],
354
- [/^\*\s$/, () => exec("insertUnorderedList")],
355
- [/^1\.\s$/, () => exec("insertOrderedList")],
356
- [/^>\s$/, () => setBlock("blockquote")]
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
- onChange?.(el.innerHTML);
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
- setBlock("pre");
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
- if (!validations || validations.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
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
- if (!validations || validations.length === 0) return /* @__PURE__ */ jsx4(Fragment, { children });
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
- if (!validations || validations.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime295.jsx)(import_jsx_runtime295.Fragment, { children });
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);
@@ -2,13 +2,19 @@ import React from 'react';
2
2
  import { V as Validation } from '../../validation-Dqo1zqhT.cjs';
3
3
 
4
4
  interface FileUploadProps {
5
+ /** 고를 수 있는 파일 형식 (input 의 accept 와 같다) */
5
6
  accept?: string;
7
+ /** 여러 개를 한 번에 고를 수 있게 한다 */
6
8
  multiple?: boolean;
7
9
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
8
10
  validations?: Validation[];
11
+ /** 바이트 단위 최대 크기. **초과분은 조용히 걸러진다** */
9
12
  maxSize?: number;
13
+ /** 고른 파일 목록을 준다. maxSize 로 걸러진 것은 빠져 있다 */
10
14
  onChange?: (files: File[]) => void;
15
+ /** 상자 안 큰 글자 */
11
16
  label?: string;
17
+ /** 상자 안 작은 설명 */
12
18
  description?: string;
13
19
  }
14
20
  declare const FileUpload: React.FC<FileUploadProps>;
@@ -2,13 +2,19 @@ import React from 'react';
2
2
  import { V as Validation } from '../../validation-Dqo1zqhT.js';
3
3
 
4
4
  interface FileUploadProps {
5
+ /** 고를 수 있는 파일 형식 (input 의 accept 와 같다) */
5
6
  accept?: string;
7
+ /** 여러 개를 한 번에 고를 수 있게 한다 */
6
8
  multiple?: boolean;
7
9
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
8
10
  validations?: Validation[];
11
+ /** 바이트 단위 최대 크기. **초과분은 조용히 걸러진다** */
9
12
  maxSize?: number;
13
+ /** 고른 파일 목록을 준다. maxSize 로 걸러진 것은 빠져 있다 */
10
14
  onChange?: (files: File[]) => void;
15
+ /** 상자 안 큰 글자 */
11
16
  label?: string;
17
+ /** 상자 안 작은 설명 */
12
18
  description?: string;
13
19
  }
14
20
  declare const FileUpload: React.FC<FileUploadProps>;
@@ -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
- if (!validations || validations.length === 0) return /* @__PURE__ */ jsx295(Fragment, { children });
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
- if (!validations || validations.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime295.jsx)(import_jsx_runtime295.Fragment, { children });
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);
@@ -2,10 +2,13 @@ import React from 'react';
2
2
  import { V as Validation } from '../../validation-Dqo1zqhT.cjs';
3
3
 
4
4
  interface ImageSelectorProps {
5
+ /** 선택된 이미지 파일. 주면 미리보기가 나온다 */
5
6
  value?: File;
6
7
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
7
8
  validations?: Validation[];
9
+ /** 비어 있을 때 표시할 안내 글자 */
8
10
  label?: string;
11
+ /** 이미지를 고르거나 지우면 호출된다. 지우면 undefined 가 온다 */
9
12
  onChange?: (value: File | undefined) => void;
10
13
  }
11
14
  declare const ImageSelector: React.FC<ImageSelectorProps>;
@@ -2,10 +2,13 @@ import React from 'react';
2
2
  import { V as Validation } from '../../validation-Dqo1zqhT.js';
3
3
 
4
4
  interface ImageSelectorProps {
5
+ /** 선택된 이미지 파일. 주면 미리보기가 나온다 */
5
6
  value?: File;
6
7
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
7
8
  validations?: Validation[];
9
+ /** 비어 있을 때 표시할 안내 글자 */
8
10
  label?: string;
11
+ /** 이미지를 고르거나 지우면 호출된다. 지우면 undefined 가 온다 */
9
12
  onChange?: (value: File | undefined) => void;
10
13
  }
11
14
  declare const ImageSelector: React.FC<ImageSelectorProps>;
@@ -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
- if (!validations || validations.length === 0) return /* @__PURE__ */ jsx295(Fragment, { children });
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";