@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
@@ -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);
@@ -110,6 +125,7 @@
110
125
  .lib-xplat-checkbox:hover > .checkbox.unchecked {
111
126
  background-color: var(--semantic-surface-neutral-disabled);
112
127
  }
128
+ .lib-xplat-checkbox.primary > .checkbox.checked,
113
129
  .lib-xplat-checkbox.brand > .checkbox.checked {
114
130
  background-color: var(--semantic-surface-brand-default);
115
131
  }
@@ -125,6 +141,7 @@
125
141
  .lib-xplat-checkbox.info > .checkbox.checked {
126
142
  background-color: var(--semantic-surface-info-default);
127
143
  }
144
+ .lib-xplat-checkbox:hover.primary > .checkbox.checked,
128
145
  .lib-xplat-checkbox:hover.brand > .checkbox.checked {
129
146
  background-color: var(--semantic-surface-brand-strong);
130
147
  }
@@ -1,17 +1,25 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { I as InputAttributes } from '../../attributes-DJIWir_0.cjs';
3
+ import { L as LegacyStatusType } from '../../status-njWqQWFV.cjs';
3
4
  import { V as Validation } from '../../validation-Dqo1zqhT.cjs';
4
5
  import 'react';
5
6
 
6
7
  type CheckBoxSize = "sm" | "md" | "lg";
7
- type CheckBoxType = "brand" | "success" | "error" | "warning" | "info";
8
+ /** CheckBox 의 상태 색 */
9
+ type CheckBoxType = "primary" | "success" | "error" | "warning" | "info";
10
+ /** 입력으로는 예전 이름 `brand` 도 받는다 (= `primary`) */
11
+ type CheckBoxTypeInput = CheckBoxType | LegacyStatusType;
8
12
  interface CheckBoxProps extends Omit<InputAttributes, "size"> {
13
+ /** 체크박스 옆에 붙는 글자. 클릭하면 체크가 토글된다 */
9
14
  label?: string;
15
+ /** 체크 여부. 주면 controlled 가 되므로 onChange 와 함께 쓴다 */
10
16
  checked?: boolean;
11
17
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
12
18
  validations?: Validation[];
19
+ /** 컨트롤 크기. 글자 크기도 함께 바뀐다 */
13
20
  size?: CheckBoxSize;
14
- type?: CheckBoxType;
21
+ /** 체크됐을 때의 색 */
22
+ type?: CheckBoxTypeInput;
15
23
  }
16
24
  declare const CheckBox: {
17
25
  (props: CheckBoxProps): react_jsx_runtime.JSX.Element;
@@ -1,17 +1,25 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { I as InputAttributes } from '../../attributes-DJIWir_0.js';
3
+ import { L as LegacyStatusType } from '../../status-njWqQWFV.js';
3
4
  import { V as Validation } from '../../validation-Dqo1zqhT.js';
4
5
  import 'react';
5
6
 
6
7
  type CheckBoxSize = "sm" | "md" | "lg";
7
- type CheckBoxType = "brand" | "success" | "error" | "warning" | "info";
8
+ /** CheckBox 의 상태 색 */
9
+ type CheckBoxType = "primary" | "success" | "error" | "warning" | "info";
10
+ /** 입력으로는 예전 이름 `brand` 도 받는다 (= `primary`) */
11
+ type CheckBoxTypeInput = CheckBoxType | LegacyStatusType;
8
12
  interface CheckBoxProps extends Omit<InputAttributes, "size"> {
13
+ /** 체크박스 옆에 붙는 글자. 클릭하면 체크가 토글된다 */
9
14
  label?: string;
15
+ /** 체크 여부. 주면 controlled 가 되므로 onChange 와 함께 쓴다 */
10
16
  checked?: boolean;
11
17
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
12
18
  validations?: Validation[];
19
+ /** 컨트롤 크기. 글자 크기도 함께 바뀐다 */
13
20
  size?: CheckBoxSize;
14
- type?: CheckBoxType;
21
+ /** 체크됐을 때의 색 */
22
+ type?: CheckBoxTypeInput;
15
23
  }
16
24
  declare const CheckBox: {
17
25
  (props: CheckBoxProps): react_jsx_runtime.JSX.Element;
@@ -993,6 +993,9 @@ import { jsx as jsx294, jsxs as jsxs188 } from "react/jsx-runtime";
993
993
  // src/tokens/index.ts
994
994
  import { cssVar } from "@x-plat/tokens-core";
995
995
 
996
+ // src/tokens/interface/status.ts
997
+ var normalizeStatus = (value) => value === "brand" ? "primary" : value;
998
+
996
999
  // ../../node_modules/clsx/dist/clsx.mjs
997
1000
  function r(e) {
998
1001
  var t, f, n = "";
@@ -1034,11 +1037,16 @@ var FieldMessage = (props) => {
1034
1037
  };
1035
1038
  FieldMessage.displayName = "FieldMessage";
1036
1039
  var Field = (props) => {
1037
- const { validations, children } = props;
1038
- if (!validations || validations.length === 0) return /* @__PURE__ */ jsx295(Fragment, { children });
1040
+ const { label, required = false, htmlFor, validations, children } = props;
1041
+ const hasMessages = !!validations && validations.length > 0;
1042
+ if (!label && !hasMessages) return /* @__PURE__ */ jsx295(Fragment, { children });
1039
1043
  return /* @__PURE__ */ jsxs189("div", { className: "lib-xplat-field", children: [
1044
+ label && /* @__PURE__ */ jsxs189("label", { className: "lib-xplat-field-label", htmlFor, children: [
1045
+ label,
1046
+ required && /* @__PURE__ */ jsx295("span", { className: "required", "aria-hidden": "true", children: "*" })
1047
+ ] }),
1040
1048
  children,
1041
- /* @__PURE__ */ jsx295(FieldMessage, { validations })
1049
+ hasMessages && /* @__PURE__ */ jsx295(FieldMessage, { validations })
1042
1050
  ] });
1043
1051
  };
1044
1052
  Field.displayName = "Field";
@@ -1054,10 +1062,11 @@ var CheckBox = (props) => {
1054
1062
  onChange,
1055
1063
  disabled,
1056
1064
  size = "md",
1057
- type = "brand",
1065
+ type = "primary",
1058
1066
  validations,
1059
1067
  ...rest
1060
1068
  } = props;
1069
+ const statusType = normalizeStatus(type);
1061
1070
  const handleChange = (e) => {
1062
1071
  if (onChange) onChange(e);
1063
1072
  };
@@ -1065,7 +1074,7 @@ var CheckBox = (props) => {
1065
1074
  const checkedClasses = `checked`;
1066
1075
  const disabledClasses = "disabled";
1067
1076
  const boxClasses = disabled ? disabledClasses : checked ? checkedClasses : uncheckedClasses;
1068
- const control = /* @__PURE__ */ jsxs190("label", { className: clsx_default("lib-xplat-checkbox", size, type, className), children: [
1077
+ const control = /* @__PURE__ */ jsxs190("label", { className: clsx_default("lib-xplat-checkbox", size, statusType, className), children: [
1069
1078
  /* @__PURE__ */ jsx296(
1070
1079
  "input",
1071
1080
  {
@@ -5,7 +5,9 @@ type ChipType = "primary" | "secondary" | "neutral" | "success" | "error" | "war
5
5
  type ChipSize = "sm" | "md" | "lg";
6
6
  interface ChipProps {
7
7
  children: React.ReactNode;
8
+ /** 칩 색. 상태를 나타낸다 */
8
9
  type?: ChipType;
10
+ /** 칩 크기 */
9
11
  size?: ChipSize;
10
12
  }
11
13
  declare const Chip: {
@@ -5,7 +5,9 @@ type ChipType = "primary" | "secondary" | "neutral" | "success" | "error" | "war
5
5
  type ChipSize = "sm" | "md" | "lg";
6
6
  interface ChipProps {
7
7
  children: React.ReactNode;
8
+ /** 칩 색. 상태를 나타낸다 */
8
9
  type?: ChipType;
10
+ /** 칩 크기 */
9
11
  size?: ChipSize;
10
12
  }
11
13
  declare const Chip: {
@@ -38,7 +38,7 @@ __export(DatePicker_exports, {
38
38
  module.exports = __toCommonJS(DatePicker_exports);
39
39
 
40
40
  // src/components/DatePicker/InputDatePicker/index.tsx
41
- var import_react6 = __toESM(require("react"), 1);
41
+ var import_react7 = __toESM(require("react"), 1);
42
42
 
43
43
  // ../../node_modules/clsx/dist/clsx.mjs
44
44
  function r(e) {
@@ -206,11 +206,16 @@ var FieldMessage = (props) => {
206
206
  FieldMessage.displayName = "FieldMessage";
207
207
  var FieldMessage_default = FieldMessage;
208
208
  var Field = (props) => {
209
- const { validations, children } = props;
210
- if (!validations || validations.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
209
+ const { label, required = false, htmlFor, validations, children } = props;
210
+ const hasMessages = !!validations && validations.length > 0;
211
+ if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
211
212
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lib-xplat-field", children: [
213
+ label && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "lib-xplat-field-label", htmlFor, children: [
214
+ label,
215
+ required && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
216
+ ] }),
212
217
  children,
213
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FieldMessage, { validations })
218
+ hasMessages && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FieldMessage, { validations })
214
219
  ] });
215
220
  };
216
221
  Field.displayName = "Field";
@@ -309,8 +314,12 @@ var Input = import_react.default.forwardRef((props, ref) => {
309
314
  suffix,
310
315
  disabled,
311
316
  validations,
317
+ label,
318
+ required,
312
319
  ...inputProps
313
320
  } = props;
321
+ const autoId = import_react.default.useId();
322
+ const fieldId = inputProps.id ?? autoId;
314
323
  const localValue = formatValue(type, value);
315
324
  const handleChange = (e) => {
316
325
  let val = e.target.value;
@@ -326,7 +335,7 @@ var Input = import_react.default.forwardRef((props, ref) => {
326
335
  onChange(event);
327
336
  }
328
337
  };
329
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: clsx_default("lib-xplat-input-wrap", className), children: [
338
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Field, { label, required, htmlFor: fieldId, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: clsx_default("lib-xplat-input-wrap", className), children: [
330
339
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
331
340
  "div",
332
341
  {
@@ -337,6 +346,7 @@ var Input = import_react.default.forwardRef((props, ref) => {
337
346
  {
338
347
  ...inputProps,
339
348
  ref,
349
+ id: fieldId,
340
350
  disabled,
341
351
  type: type === "number" ? "text" : type,
342
352
  value: localValue,
@@ -349,7 +359,7 @@ var Input = import_react.default.forwardRef((props, ref) => {
349
359
  }
350
360
  ),
351
361
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FieldMessage_default, { validations })
352
- ] });
362
+ ] }) });
353
363
  });
354
364
  Input.displayName = "Input";
355
365
  var Input_default = Input;
@@ -446,16 +456,96 @@ var PasswordInput = import_react2.default.forwardRef(
446
456
  PasswordInput.displayName = "PasswordInput";
447
457
 
448
458
  // src/components/Modal/Modal.tsx
449
- var import_react3 = __toESM(require("react"), 1);
459
+ var import_react4 = __toESM(require("react"), 1);
450
460
  var import_react_dom = require("react-dom");
461
+
462
+ // src/tokens/hooks/useOverlayKeyboard.ts
463
+ var import_react3 = __toESM(require("react"), 1);
464
+ var FOCUSABLE = [
465
+ "a[href]",
466
+ "button:not([disabled])",
467
+ "input:not([disabled]):not([type='hidden'])",
468
+ "select:not([disabled])",
469
+ "textarea:not([disabled])",
470
+ "[tabindex]:not([tabindex='-1'])"
471
+ ].join(",");
472
+ var focusableIn = (el) => Array.from(el.querySelectorAll(FOCUSABLE)).filter(
473
+ // 화면에 없는 것은 제외 (display:none 등)
474
+ (n) => n.offsetWidth > 0 || n.offsetHeight > 0 || n === document.activeElement
475
+ );
476
+ var useEscapeKey = (enabled, onEscape) => {
477
+ import_react3.default.useEffect(() => {
478
+ if (!enabled) return;
479
+ const onKey = (e) => {
480
+ if (e.key !== "Escape") return;
481
+ if (e.defaultPrevented) return;
482
+ e.preventDefault();
483
+ onEscape();
484
+ };
485
+ document.addEventListener("keydown", onKey);
486
+ return () => document.removeEventListener("keydown", onKey);
487
+ }, [enabled, onEscape]);
488
+ };
489
+ var useFocusTrap = (containerRef, enabled, options = {}) => {
490
+ const { trap = false, initialFocus } = options;
491
+ const restoreRef = import_react3.default.useRef(null);
492
+ import_react3.default.useEffect(() => {
493
+ if (!enabled) return;
494
+ restoreRef.current = document.activeElement;
495
+ const raf = requestAnimationFrame(() => {
496
+ const el = containerRef.current;
497
+ if (!el) return;
498
+ const target = initialFocus?.current ?? focusableIn(el)[0] ?? el;
499
+ if (!target.hasAttribute("tabindex") && !focusableIn(el).includes(target)) {
500
+ target.setAttribute("tabindex", "-1");
501
+ }
502
+ target.focus({ preventScroll: true });
503
+ });
504
+ return () => {
505
+ cancelAnimationFrame(raf);
506
+ const prev = restoreRef.current;
507
+ if (prev && document.contains(prev)) prev.focus({ preventScroll: true });
508
+ };
509
+ }, [enabled, containerRef, initialFocus]);
510
+ import_react3.default.useEffect(() => {
511
+ if (!enabled || !trap) return;
512
+ const onKey = (e) => {
513
+ if (e.key !== "Tab") return;
514
+ const el = containerRef.current;
515
+ if (!el) return;
516
+ const items = focusableIn(el);
517
+ if (items.length === 0) {
518
+ e.preventDefault();
519
+ return;
520
+ }
521
+ const first = items[0];
522
+ const last = items[items.length - 1];
523
+ const active = document.activeElement;
524
+ if (e.shiftKey && (active === first || !el.contains(active))) {
525
+ e.preventDefault();
526
+ last.focus();
527
+ } else if (!e.shiftKey && (active === last || !el.contains(active))) {
528
+ e.preventDefault();
529
+ first.focus();
530
+ }
531
+ };
532
+ document.addEventListener("keydown", onKey);
533
+ return () => document.removeEventListener("keydown", onKey);
534
+ }, [enabled, trap, containerRef]);
535
+ };
536
+
537
+ // src/components/Modal/Modal.tsx
451
538
  var import_jsx_runtime10 = require("react/jsx-runtime");
452
539
  var ANIMATION_DURATION_MS = 200;
453
540
  var Modal = (props) => {
454
541
  const { isOpen, onClose, children } = props;
455
- const [mounted, setMounted] = import_react3.default.useState(false);
456
- const [visible, setVisible] = import_react3.default.useState(false);
457
- const [settled, setSettled] = import_react3.default.useState(false);
458
- import_react3.default.useEffect(() => {
542
+ const [mounted, setMounted] = import_react4.default.useState(false);
543
+ const [visible, setVisible] = import_react4.default.useState(false);
544
+ const [settled, setSettled] = import_react4.default.useState(false);
545
+ const boxRef = import_react4.default.useRef(null);
546
+ useEscapeKey(isOpen, onClose);
547
+ useFocusTrap(boxRef, isOpen && mounted, { trap: true });
548
+ import_react4.default.useEffect(() => {
459
549
  if (isOpen) {
460
550
  setMounted(true);
461
551
  const show = setTimeout(() => setVisible(true), 1);
@@ -482,6 +572,7 @@ var Modal = (props) => {
482
572
  children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
483
573
  "div",
484
574
  {
575
+ ref: boxRef,
485
576
  className: clsx_default("lib-xplat-modal", "modal-box", stateClass, settled && "settled"),
486
577
  role: "dialog",
487
578
  "aria-modal": "true",
@@ -1409,15 +1500,15 @@ var import_jsx_runtime298 = require("react/jsx-runtime");
1409
1500
  var import_jsx_runtime299 = require("react/jsx-runtime");
1410
1501
 
1411
1502
  // src/components/DatePicker/SingleDatePicker/index.tsx
1412
- var import_react5 = __toESM(require("react"), 1);
1503
+ var import_react6 = __toESM(require("react"), 1);
1413
1504
 
1414
1505
  // src/components/Calendar/useCalendar.ts
1415
- var import_react4 = __toESM(require("react"), 1);
1506
+ var import_react5 = __toESM(require("react"), 1);
1416
1507
  var useCalendar = (initialYear, initialMonth) => {
1417
1508
  const today = /* @__PURE__ */ new Date();
1418
- const [year, setYear] = import_react4.default.useState(initialYear ?? today.getFullYear());
1419
- const [month, setMonth] = import_react4.default.useState(initialMonth ?? today.getMonth());
1420
- const days = import_react4.default.useMemo(() => {
1509
+ const [year, setYear] = import_react5.default.useState(initialYear ?? today.getFullYear());
1510
+ const [month, setMonth] = import_react5.default.useState(initialMonth ?? today.getMonth());
1511
+ const days = import_react5.default.useMemo(() => {
1421
1512
  const firstDay = new Date(year, month, 1);
1422
1513
  const lastDay = new Date(year, month + 1, 0);
1423
1514
  const startDayOfWeek = firstDay.getDay();
@@ -1460,7 +1551,7 @@ var useCalendar = (initialYear, initialMonth) => {
1460
1551
  }
1461
1552
  return result;
1462
1553
  }, [year, month]);
1463
- const goToPrevMonth = import_react4.default.useCallback(() => {
1554
+ const goToPrevMonth = import_react5.default.useCallback(() => {
1464
1555
  setMonth((prev) => {
1465
1556
  if (prev === 0) {
1466
1557
  setYear((y) => y - 1);
@@ -1469,7 +1560,7 @@ var useCalendar = (initialYear, initialMonth) => {
1469
1560
  return prev - 1;
1470
1561
  });
1471
1562
  }, []);
1472
- const goToNextMonth = import_react4.default.useCallback(() => {
1563
+ const goToNextMonth = import_react5.default.useCallback(() => {
1473
1564
  setMonth((prev) => {
1474
1565
  if (prev === 11) {
1475
1566
  setYear((y) => y + 1);
@@ -1478,7 +1569,7 @@ var useCalendar = (initialYear, initialMonth) => {
1478
1569
  return prev + 1;
1479
1570
  });
1480
1571
  }, []);
1481
- const goToToday = import_react4.default.useCallback(() => {
1572
+ const goToToday = import_react5.default.useCallback(() => {
1482
1573
  setYear(today.getFullYear());
1483
1574
  setMonth(today.getMonth());
1484
1575
  }, []);
@@ -1513,7 +1604,7 @@ var MONTH_LABELS = {
1513
1604
 
1514
1605
  // src/components/DatePicker/SingleDatePicker/index.tsx
1515
1606
  var import_jsx_runtime300 = require("react/jsx-runtime");
1516
- var DayCell = import_react5.default.memo(
1607
+ var DayCell = import_react6.default.memo(
1517
1608
  ({
1518
1609
  day,
1519
1610
  disabled,
@@ -1565,26 +1656,26 @@ var SingleDatePicker = (props) => {
1565
1656
  initialYear,
1566
1657
  initialMonth
1567
1658
  );
1568
- const [pickerMode, setPickerMode] = import_react5.default.useState("days");
1569
- const [yearRangeStart, setYearRangeStart] = import_react5.default.useState(
1659
+ const [pickerMode, setPickerMode] = import_react6.default.useState("days");
1660
+ const [yearRangeStart, setYearRangeStart] = import_react6.default.useState(
1570
1661
  Math.floor((initialYear ?? (/* @__PURE__ */ new Date()).getFullYear()) / 12) * 12
1571
1662
  );
1572
- const minTime = import_react5.default.useMemo(
1663
+ const minTime = import_react6.default.useMemo(
1573
1664
  () => minDate ? new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()).getTime() : -Infinity,
1574
1665
  [minDate]
1575
1666
  );
1576
- const maxTime = import_react5.default.useMemo(
1667
+ const maxTime = import_react6.default.useMemo(
1577
1668
  () => maxDate ? new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate()).getTime() : Infinity,
1578
1669
  [maxDate]
1579
1670
  );
1580
- const highlightSet = import_react5.default.useMemo(() => {
1671
+ const highlightSet = import_react6.default.useMemo(() => {
1581
1672
  const set = /* @__PURE__ */ new Set();
1582
1673
  for (const h of highlightDates) {
1583
1674
  set.add(`${h.getFullYear()}-${h.getMonth()}-${h.getDate()}`);
1584
1675
  }
1585
1676
  return set;
1586
1677
  }, [highlightDates]);
1587
- const handleSelect = import_react5.default.useCallback(
1678
+ const handleSelect = import_react6.default.useCallback(
1588
1679
  (date) => {
1589
1680
  onChange?.(date);
1590
1681
  },
@@ -1713,8 +1804,8 @@ var formatDate = (date) => {
1713
1804
  };
1714
1805
  var InputDatePicker = (props) => {
1715
1806
  const { value, onChange, minDate, maxDate, disabled, locale = "ko", placeholder } = props;
1716
- const [isOpen, setIsOpen] = import_react6.default.useState(false);
1717
- const [tempDate, setTempDate] = import_react6.default.useState(value ?? /* @__PURE__ */ new Date());
1807
+ const [isOpen, setIsOpen] = import_react7.default.useState(false);
1808
+ const [tempDate, setTempDate] = import_react7.default.useState(value ?? /* @__PURE__ */ new Date());
1718
1809
  const handleOpen = () => {
1719
1810
  if (disabled) return;
1720
1811
  setTempDate(value ?? /* @__PURE__ */ new Date());
@@ -1763,23 +1854,27 @@ InputDatePicker.displayName = "InputDatePicker";
1763
1854
  var InputDatePicker_default = InputDatePicker;
1764
1855
 
1765
1856
  // src/components/DatePicker/PopupPicker/index.tsx
1766
- var import_react10 = __toESM(require("react"), 1);
1857
+ var import_react11 = __toESM(require("react"), 1);
1767
1858
 
1768
1859
  // src/components/DatePicker/RangePicker/index.tsx
1769
- var import_react9 = __toESM(require("react"), 1);
1860
+ var import_react10 = __toESM(require("react"), 1);
1770
1861
 
1771
1862
  // src/components/Tab/Tab.tsx
1772
- var import_react8 = __toESM(require("react"), 1);
1863
+ var import_react9 = __toESM(require("react"), 1);
1773
1864
 
1774
1865
  // src/components/Tab/TabItem.tsx
1775
- var import_react7 = __toESM(require("react"), 1);
1866
+ var import_react8 = __toESM(require("react"), 1);
1776
1867
  var import_jsx_runtime302 = require("react/jsx-runtime");
1777
- var TabItem = import_react7.default.forwardRef((props, ref) => {
1868
+ var TabItem = import_react8.default.forwardRef((props, ref) => {
1778
1869
  const { isActive, title, onClick } = props;
1779
1870
  return /* @__PURE__ */ (0, import_jsx_runtime302.jsx)(
1780
- "div",
1871
+ "button",
1781
1872
  {
1782
1873
  ref,
1874
+ type: "button",
1875
+ role: "tab",
1876
+ "aria-selected": isActive,
1877
+ tabIndex: isActive ? 0 : -1,
1783
1878
  className: clsx_default("tab-item", isActive ? "active" : null),
1784
1879
  onClick,
1785
1880
  children: title
@@ -1792,45 +1887,66 @@ var TabItem_default = TabItem;
1792
1887
  // src/components/Tab/Tab.tsx
1793
1888
  var import_jsx_runtime303 = require("react/jsx-runtime");
1794
1889
  var Tab = (props) => {
1795
- const { activeIndex, onChange, tabs, type, size = "md" } = props;
1796
- const [underlineStyle, setUnderlineStyle] = import_react8.default.useState({
1890
+ const { activeIndex, onChange, tabs, variant, type, size = "md" } = props;
1891
+ const shape = variant ?? type ?? "default";
1892
+ const [underlineStyle, setUnderlineStyle] = import_react9.default.useState({
1797
1893
  left: 0,
1798
1894
  width: 0
1799
1895
  });
1800
- const itemRefs = import_react8.default.useRef([]);
1896
+ const itemRefs = import_react9.default.useRef([]);
1897
+ const handleKeyDown = (e) => {
1898
+ const last = tabs.length - 1;
1899
+ let next = null;
1900
+ if (e.key === "ArrowRight") next = activeIndex >= last ? 0 : activeIndex + 1;
1901
+ else if (e.key === "ArrowLeft") next = activeIndex <= 0 ? last : activeIndex - 1;
1902
+ else if (e.key === "Home") next = 0;
1903
+ else if (e.key === "End") next = last;
1904
+ if (next === null) return;
1905
+ e.preventDefault();
1906
+ onChange(tabs[next], next);
1907
+ itemRefs.current[next]?.focus();
1908
+ };
1801
1909
  const handleChangeActiveTab = (tabItem, tabIdx) => {
1802
1910
  onChange(tabItem, tabIdx);
1803
1911
  };
1804
- import_react8.default.useEffect(() => {
1912
+ import_react9.default.useEffect(() => {
1805
1913
  const el = itemRefs.current[activeIndex];
1806
1914
  if (el) {
1807
1915
  setUnderlineStyle({ left: el.offsetLeft, width: el.offsetWidth });
1808
1916
  }
1809
1917
  }, [activeIndex, tabs.length]);
1810
- return /* @__PURE__ */ (0, import_jsx_runtime303.jsxs)("div", { className: clsx_default("lib-xplat-tab", `type-${type}`, size), children: [
1811
- tabs.map((tab, idx) => /* @__PURE__ */ (0, import_jsx_runtime303.jsx)(
1812
- TabItem_default,
1813
- {
1814
- onClick: () => handleChangeActiveTab(tab, idx),
1815
- isActive: activeIndex === idx,
1816
- ref: (el) => {
1817
- itemRefs.current[idx] = el;
1818
- },
1819
- title: tab.title
1820
- },
1821
- `${tab.value}_${idx}`
1822
- )),
1823
- type === "toggle" && /* @__PURE__ */ (0, import_jsx_runtime303.jsx)(
1824
- "div",
1825
- {
1826
- className: "tab-toggle-underline",
1827
- style: {
1828
- left: underlineStyle.left,
1829
- width: underlineStyle.width
1830
- }
1831
- }
1832
- )
1833
- ] });
1918
+ return /* @__PURE__ */ (0, import_jsx_runtime303.jsxs)(
1919
+ "div",
1920
+ {
1921
+ className: clsx_default("lib-xplat-tab", `type-${shape}`, size),
1922
+ role: "tablist",
1923
+ onKeyDown: handleKeyDown,
1924
+ children: [
1925
+ tabs.map((tab, idx) => /* @__PURE__ */ (0, import_jsx_runtime303.jsx)(
1926
+ TabItem_default,
1927
+ {
1928
+ onClick: () => handleChangeActiveTab(tab, idx),
1929
+ isActive: activeIndex === idx,
1930
+ ref: (el) => {
1931
+ itemRefs.current[idx] = el;
1932
+ },
1933
+ title: tab.title
1934
+ },
1935
+ `${tab.value}_${idx}`
1936
+ )),
1937
+ shape === "toggle" && /* @__PURE__ */ (0, import_jsx_runtime303.jsx)(
1938
+ "div",
1939
+ {
1940
+ className: "tab-toggle-underline",
1941
+ style: {
1942
+ left: underlineStyle.left,
1943
+ width: underlineStyle.width
1944
+ }
1945
+ }
1946
+ )
1947
+ ]
1948
+ }
1949
+ );
1834
1950
  };
1835
1951
  Tab.displayName = "Tab";
1836
1952
  var Tab_default = Tab;
@@ -1846,7 +1962,7 @@ var RangePicker = (props) => {
1846
1962
  maxDate,
1847
1963
  locale = "ko"
1848
1964
  } = props;
1849
- const [activeTab, setActiveTab] = import_react9.default.useState("start");
1965
+ const [activeTab, setActiveTab] = import_react10.default.useState("start");
1850
1966
  const handleStartChange = (date) => {
1851
1967
  if (!date) return;
1852
1968
  const newStart = date > endDate ? endDate : date;
@@ -1931,7 +2047,7 @@ var RangePicker_default = RangePicker;
1931
2047
  var import_jsx_runtime305 = require("react/jsx-runtime");
1932
2048
  var PopupPicker = (props) => {
1933
2049
  const { component, type, locale } = props;
1934
- const [isOpen, setIsOpen] = import_react10.default.useState(false);
2050
+ const [isOpen, setIsOpen] = import_react11.default.useState(false);
1935
2051
  const handleClick = () => setIsOpen(true);
1936
2052
  const handleClose = () => setIsOpen(false);
1937
2053
  const handleSingleChange = (date) => {
@@ -1940,7 +2056,7 @@ var PopupPicker = (props) => {
1940
2056
  handleClose();
1941
2057
  };
1942
2058
  return /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("div", { className: "lib-xplat-popup-datepicker", children: [
1943
- import_react10.default.cloneElement(component, { onClick: handleClick }),
2059
+ import_react11.default.cloneElement(component, { onClick: handleClick }),
1944
2060
  /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(Modal_default, { isOpen, onClose: handleClose, children: /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("div", { className: clsx_default("lib-xplat-popup-datepicker-card", type === "range" && "range-mode"), children: [
1945
2061
  /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("div", { className: "popup-datepicker-content", children: [
1946
2062
  type === "single" && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
@@ -111,6 +111,21 @@
111
111
  flex-direction: column;
112
112
  gap: var(--spacing-space-2);
113
113
  }
114
+ .lib-xplat-field-label {
115
+ font-family: var(--semantic-typo-label-2-m-font);
116
+ font-size: var(--semantic-typo-label-2-m-size);
117
+ line-height: var(--semantic-typo-label-2-m-lh);
118
+ font-weight: var(--semantic-typo-label-2-m-weight);
119
+ letter-spacing: var(--semantic-typo-label-2-m-ls);
120
+ color: var(--semantic-text-secondary);
121
+ display: inline-flex;
122
+ align-items: center;
123
+ gap: var(--spacing-space-1);
124
+ cursor: pointer;
125
+ }
126
+ .lib-xplat-field-label > .required {
127
+ color: var(--semantic-text-error);
128
+ }
114
129
  .lib-xplat-field-message-wrap {
115
130
  display: flex;
116
131
  gap: var(--spacing-space-2);
@@ -574,6 +589,10 @@
574
589
  border-radius: var(--spacing-radius-md);
575
590
  }
576
591
  .lib-xplat-tab.type-default > .tab-item {
592
+ appearance: none;
593
+ font: inherit;
594
+ border: none;
595
+ background: none;
577
596
  flex: 1;
578
597
  padding: var(--spacing-space-1) var(--spacing-space-6);
579
598
  height: 100%;