@x-plat/design-system 0.11.0 → 0.13.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 (63) hide show
  1. package/dist/components/AutoResizeTextArea/index.cjs +2 -2
  2. package/dist/components/AutoResizeTextArea/index.js +2 -2
  3. package/dist/components/ChatInput/index.cjs +2 -2
  4. package/dist/components/ChatInput/index.js +2 -2
  5. package/dist/components/CheckBox/index.cjs +2 -2
  6. package/dist/components/CheckBox/index.js +2 -2
  7. package/dist/components/DatePicker/index.cjs +109 -75
  8. package/dist/components/DatePicker/index.d.cts +19 -0
  9. package/dist/components/DatePicker/index.d.ts +19 -0
  10. package/dist/components/DatePicker/index.js +109 -75
  11. package/dist/components/Dropdown/index.cjs +22 -9
  12. package/dist/components/Dropdown/index.css +3 -0
  13. package/dist/components/Dropdown/index.js +22 -9
  14. package/dist/components/Editor/index.cjs +602 -30
  15. package/dist/components/Editor/index.css +103 -0
  16. package/dist/components/Editor/index.d.cts +1 -1
  17. package/dist/components/Editor/index.d.ts +1 -1
  18. package/dist/components/Editor/index.js +602 -30
  19. package/dist/components/FieldMessage/index.cjs +2 -2
  20. package/dist/components/FieldMessage/index.d.cts +8 -0
  21. package/dist/components/FieldMessage/index.d.ts +8 -0
  22. package/dist/components/FieldMessage/index.js +2 -2
  23. package/dist/components/FileUpload/index.cjs +10 -6
  24. package/dist/components/FileUpload/index.d.cts +15 -2
  25. package/dist/components/FileUpload/index.d.ts +15 -2
  26. package/dist/components/FileUpload/index.js +10 -6
  27. package/dist/components/ImageSelector/index.cjs +7 -6
  28. package/dist/components/ImageSelector/index.d.cts +15 -2
  29. package/dist/components/ImageSelector/index.d.ts +15 -2
  30. package/dist/components/ImageSelector/index.js +7 -6
  31. package/dist/components/Input/index.cjs +2 -2
  32. package/dist/components/Input/index.d.cts +4 -0
  33. package/dist/components/Input/index.d.ts +4 -0
  34. package/dist/components/Input/index.js +2 -2
  35. package/dist/components/PopOver/index.cjs +21 -7
  36. package/dist/components/PopOver/index.css +3 -0
  37. package/dist/components/PopOver/index.js +21 -7
  38. package/dist/components/Radio/index.cjs +2 -2
  39. package/dist/components/Radio/index.js +2 -2
  40. package/dist/components/Select/index.cjs +23 -10
  41. package/dist/components/Select/index.css +3 -0
  42. package/dist/components/Select/index.d.cts +1 -2
  43. package/dist/components/Select/index.d.ts +1 -2
  44. package/dist/components/Select/index.js +23 -10
  45. package/dist/components/Switch/index.cjs +2 -2
  46. package/dist/components/Switch/index.js +2 -2
  47. package/dist/components/TextArea/index.cjs +2 -2
  48. package/dist/components/TextArea/index.js +2 -2
  49. package/dist/components/TimePicker/index.cjs +23 -10
  50. package/dist/components/TimePicker/index.css +3 -0
  51. package/dist/components/TimePicker/index.js +23 -10
  52. package/dist/components/index.cjs +751 -124
  53. package/dist/components/index.css +113 -0
  54. package/dist/components/index.js +751 -124
  55. package/dist/index.cjs +751 -124
  56. package/dist/index.css +113 -0
  57. package/dist/index.js +751 -124
  58. package/guidelines/Guidelines.md +12 -0
  59. package/guidelines/MIGRATION.md +270 -0
  60. package/guidelines/components/datepicker.md +26 -0
  61. package/guidelines/components/editor.md +58 -3
  62. package/guidelines/components/file-media.md +21 -4
  63. package/package.json +1 -1
@@ -166,11 +166,11 @@ var FieldMessage = (props) => {
166
166
  FieldMessage.displayName = "FieldMessage";
167
167
  var FieldMessage_default = FieldMessage;
168
168
  var Field = (props) => {
169
- const { label, required = false, htmlFor, validations, children } = props;
169
+ const { label, required = false, htmlFor, labelId, validations, children } = props;
170
170
  const hasMessages = !!validations && validations.length > 0;
171
171
  if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
172
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: [
173
+ label && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "lib-xplat-field-label", id: labelId, htmlFor, children: [
174
174
  label,
175
175
  required && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
176
176
  ] }),
@@ -31,6 +31,14 @@ interface FieldProps {
31
31
  required?: boolean;
32
32
  /** 라벨의 htmlFor 와 컨트롤의 id 를 잇는 값 */
33
33
  htmlFor?: string;
34
+ /**
35
+ * 라벨 자체에 붙일 id.
36
+ *
37
+ * `htmlFor` 로 이을 수 없는 컨트롤 — 달력처럼 포커스 대상이 여럿이라
38
+ * 하나를 고를 수 없는 것 — 이 `role="group"` + `aria-labelledby` 로
39
+ * 라벨을 가리킬 때 쓴다.
40
+ */
41
+ labelId?: string;
34
42
  validations?: Validation[];
35
43
  children: React.ReactNode;
36
44
  }
@@ -31,6 +31,14 @@ interface FieldProps {
31
31
  required?: boolean;
32
32
  /** 라벨의 htmlFor 와 컨트롤의 id 를 잇는 값 */
33
33
  htmlFor?: string;
34
+ /**
35
+ * 라벨 자체에 붙일 id.
36
+ *
37
+ * `htmlFor` 로 이을 수 없는 컨트롤 — 달력처럼 포커스 대상이 여럿이라
38
+ * 하나를 고를 수 없는 것 — 이 `role="group"` + `aria-labelledby` 로
39
+ * 라벨을 가리킬 때 쓴다.
40
+ */
41
+ labelId?: string;
34
42
  validations?: Validation[];
35
43
  children: React.ReactNode;
36
44
  }
@@ -138,11 +138,11 @@ var FieldMessage = (props) => {
138
138
  FieldMessage.displayName = "FieldMessage";
139
139
  var FieldMessage_default = FieldMessage;
140
140
  var Field = (props) => {
141
- const { label, required = false, htmlFor, validations, children } = props;
141
+ const { label, required = false, htmlFor, labelId, validations, children } = props;
142
142
  const hasMessages = !!validations && validations.length > 0;
143
143
  if (!label && !hasMessages) return /* @__PURE__ */ jsx4(Fragment, { children });
144
144
  return /* @__PURE__ */ jsxs3("div", { className: "lib-xplat-field", children: [
145
- label && /* @__PURE__ */ jsxs3("label", { className: "lib-xplat-field-label", htmlFor, children: [
145
+ label && /* @__PURE__ */ jsxs3("label", { className: "lib-xplat-field-label", id: labelId, htmlFor, children: [
146
146
  label,
147
147
  required && /* @__PURE__ */ jsx4("span", { className: "required", "aria-hidden": "true", children: "*" })
148
148
  ] }),
@@ -1076,11 +1076,11 @@ var FieldMessage = (props) => {
1076
1076
  };
1077
1077
  FieldMessage.displayName = "FieldMessage";
1078
1078
  var Field = (props) => {
1079
- const { label, required = false, htmlFor, validations, children } = props;
1079
+ const { label, required = false, htmlFor, labelId, validations, children } = props;
1080
1080
  const hasMessages = !!validations && validations.length > 0;
1081
1081
  if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime295.jsx)(import_jsx_runtime295.Fragment, { children });
1082
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: [
1083
+ label && /* @__PURE__ */ (0, import_jsx_runtime295.jsxs)("label", { className: "lib-xplat-field-label", id: labelId, htmlFor, children: [
1084
1084
  label,
1085
1085
  required && /* @__PURE__ */ (0, import_jsx_runtime295.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
1086
1086
  ] }),
@@ -1099,10 +1099,13 @@ var FileUpload = (props) => {
1099
1099
  multiple = false,
1100
1100
  maxSize,
1101
1101
  onChange,
1102
- label = "\uD30C\uC77C\uC744 \uB4DC\uB798\uADF8\uD558\uAC70\uB098 \uD074\uB9AD\uD558\uC5EC \uC5C5\uB85C\uB4DC",
1102
+ placeholder = "\uD30C\uC77C\uC744 \uB4DC\uB798\uADF8\uD558\uAC70\uB098 \uD074\uB9AD\uD558\uC5EC \uC5C5\uB85C\uB4DC",
1103
1103
  description,
1104
- validations
1104
+ validations,
1105
+ label,
1106
+ required
1105
1107
  } = props;
1108
+ const fieldId = import_react.default.useId();
1106
1109
  const [isDragOver, setIsDragOver] = import_react.default.useState(false);
1107
1110
  const inputRef = import_react.default.useRef(null);
1108
1111
  const handleFiles = (fileList) => {
@@ -1147,6 +1150,7 @@ var FileUpload = (props) => {
1147
1150
  "input",
1148
1151
  {
1149
1152
  ref: inputRef,
1153
+ id: fieldId,
1150
1154
  type: "file",
1151
1155
  accept,
1152
1156
  multiple,
@@ -1155,12 +1159,12 @@ var FileUpload = (props) => {
1155
1159
  }
1156
1160
  ),
1157
1161
  /* @__PURE__ */ (0, import_jsx_runtime296.jsx)("div", { className: "upload-icon", children: /* @__PURE__ */ (0, import_jsx_runtime296.jsx)(UploadIcon_default, {}) }),
1158
- /* @__PURE__ */ (0, import_jsx_runtime296.jsx)("p", { className: "upload-label", children: label }),
1162
+ /* @__PURE__ */ (0, import_jsx_runtime296.jsx)("p", { className: "upload-label", children: placeholder }),
1159
1163
  description && /* @__PURE__ */ (0, import_jsx_runtime296.jsx)("p", { className: "upload-description", children: description })
1160
1164
  ]
1161
1165
  }
1162
1166
  );
1163
- return /* @__PURE__ */ (0, import_jsx_runtime296.jsx)(Field, { validations, children: control });
1167
+ return /* @__PURE__ */ (0, import_jsx_runtime296.jsx)(Field, { label, required, htmlFor: fieldId, validations, children: control });
1164
1168
  };
1165
1169
  FileUpload.displayName = "FileUpload";
1166
1170
  var FileUpload_default = FileUpload;
@@ -12,10 +12,23 @@ interface FileUploadProps {
12
12
  maxSize?: number;
13
13
  /** 고른 파일 목록을 준다. maxSize 로 걸러진 것은 빠져 있다 */
14
14
  onChange?: (files: File[]) => void;
15
- /** 상자 안 큰 글자 */
16
- label?: string;
15
+ /**
16
+ * 상자 안 큰 글자.
17
+ *
18
+ * **0.13.0 에서 `label` 이 필드 라벨로 바뀌면서 이 이름으로 옮겨졌다.**
19
+ * DS 전체에서 `placeholder` 는 "빈 컨트롤 안에 보이는 글자" 를 뜻한다.
20
+ */
21
+ placeholder?: string;
17
22
  /** 상자 안 작은 설명 */
18
23
  description?: string;
24
+ /**
25
+ * 상자 **위에** 붙는 필드 라벨. 클릭하면 파일 선택창이 열린다.
26
+ *
27
+ * **0.13.0 이전에는 상자 안 글자를 뜻했다.** 그 용도는 `placeholder` 로 옮겼다.
28
+ */
29
+ label?: React.ReactNode;
30
+ /** 라벨 뒤에 필수 표시(*)를 붙인다 */
31
+ required?: boolean;
19
32
  }
20
33
  declare const FileUpload: React.FC<FileUploadProps>;
21
34
 
@@ -12,10 +12,23 @@ interface FileUploadProps {
12
12
  maxSize?: number;
13
13
  /** 고른 파일 목록을 준다. maxSize 로 걸러진 것은 빠져 있다 */
14
14
  onChange?: (files: File[]) => void;
15
- /** 상자 안 큰 글자 */
16
- label?: string;
15
+ /**
16
+ * 상자 안 큰 글자.
17
+ *
18
+ * **0.13.0 에서 `label` 이 필드 라벨로 바뀌면서 이 이름으로 옮겨졌다.**
19
+ * DS 전체에서 `placeholder` 는 "빈 컨트롤 안에 보이는 글자" 를 뜻한다.
20
+ */
21
+ placeholder?: string;
17
22
  /** 상자 안 작은 설명 */
18
23
  description?: string;
24
+ /**
25
+ * 상자 **위에** 붙는 필드 라벨. 클릭하면 파일 선택창이 열린다.
26
+ *
27
+ * **0.13.0 이전에는 상자 안 글자를 뜻했다.** 그 용도는 `placeholder` 로 옮겼다.
28
+ */
29
+ label?: React.ReactNode;
30
+ /** 라벨 뒤에 필수 표시(*)를 붙인다 */
31
+ required?: boolean;
19
32
  }
20
33
  declare const FileUpload: React.FC<FileUploadProps>;
21
34
 
@@ -1040,11 +1040,11 @@ var FieldMessage = (props) => {
1040
1040
  };
1041
1041
  FieldMessage.displayName = "FieldMessage";
1042
1042
  var Field = (props) => {
1043
- const { label, required = false, htmlFor, validations, children } = props;
1043
+ const { label, required = false, htmlFor, labelId, validations, children } = props;
1044
1044
  const hasMessages = !!validations && validations.length > 0;
1045
1045
  if (!label && !hasMessages) return /* @__PURE__ */ jsx295(Fragment, { children });
1046
1046
  return /* @__PURE__ */ jsxs189("div", { className: "lib-xplat-field", children: [
1047
- label && /* @__PURE__ */ jsxs189("label", { className: "lib-xplat-field-label", htmlFor, children: [
1047
+ label && /* @__PURE__ */ jsxs189("label", { className: "lib-xplat-field-label", id: labelId, htmlFor, children: [
1048
1048
  label,
1049
1049
  required && /* @__PURE__ */ jsx295("span", { className: "required", "aria-hidden": "true", children: "*" })
1050
1050
  ] }),
@@ -1063,10 +1063,13 @@ var FileUpload = (props) => {
1063
1063
  multiple = false,
1064
1064
  maxSize,
1065
1065
  onChange,
1066
- label = "\uD30C\uC77C\uC744 \uB4DC\uB798\uADF8\uD558\uAC70\uB098 \uD074\uB9AD\uD558\uC5EC \uC5C5\uB85C\uB4DC",
1066
+ placeholder = "\uD30C\uC77C\uC744 \uB4DC\uB798\uADF8\uD558\uAC70\uB098 \uD074\uB9AD\uD558\uC5EC \uC5C5\uB85C\uB4DC",
1067
1067
  description,
1068
- validations
1068
+ validations,
1069
+ label,
1070
+ required
1069
1071
  } = props;
1072
+ const fieldId = React.useId();
1070
1073
  const [isDragOver, setIsDragOver] = React.useState(false);
1071
1074
  const inputRef = React.useRef(null);
1072
1075
  const handleFiles = (fileList) => {
@@ -1111,6 +1114,7 @@ var FileUpload = (props) => {
1111
1114
  "input",
1112
1115
  {
1113
1116
  ref: inputRef,
1117
+ id: fieldId,
1114
1118
  type: "file",
1115
1119
  accept,
1116
1120
  multiple,
@@ -1119,12 +1123,12 @@ var FileUpload = (props) => {
1119
1123
  }
1120
1124
  ),
1121
1125
  /* @__PURE__ */ jsx296("div", { className: "upload-icon", children: /* @__PURE__ */ jsx296(UploadIcon_default, {}) }),
1122
- /* @__PURE__ */ jsx296("p", { className: "upload-label", children: label }),
1126
+ /* @__PURE__ */ jsx296("p", { className: "upload-label", children: placeholder }),
1123
1127
  description && /* @__PURE__ */ jsx296("p", { className: "upload-description", children: description })
1124
1128
  ]
1125
1129
  }
1126
1130
  );
1127
- return /* @__PURE__ */ jsx296(Field, { validations, children: control });
1131
+ return /* @__PURE__ */ jsx296(Field, { label, required, htmlFor: fieldId, validations, children: control });
1128
1132
  };
1129
1133
  FileUpload.displayName = "FileUpload";
1130
1134
  var FileUpload_default = FileUpload;
@@ -1143,11 +1143,11 @@ var FieldMessage = (props) => {
1143
1143
  };
1144
1144
  FieldMessage.displayName = "FieldMessage";
1145
1145
  var Field = (props) => {
1146
- const { label, required = false, htmlFor, validations, children } = props;
1146
+ const { label, required = false, htmlFor, labelId, validations, children } = props;
1147
1147
  const hasMessages = !!validations && validations.length > 0;
1148
1148
  if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime295.jsx)(import_jsx_runtime295.Fragment, { children });
1149
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: [
1150
+ label && /* @__PURE__ */ (0, import_jsx_runtime295.jsxs)("label", { className: "lib-xplat-field-label", id: labelId, htmlFor, children: [
1151
1151
  label,
1152
1152
  required && /* @__PURE__ */ (0, import_jsx_runtime295.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
1153
1153
  ] }),
@@ -1161,7 +1161,8 @@ var hasError = (validations) => !!validations?.some((v) => v.status === "error")
1161
1161
  // src/components/ImageSelector/ImageSelector.tsx
1162
1162
  var import_jsx_runtime296 = require("react/jsx-runtime");
1163
1163
  var ImageSelector = (props) => {
1164
- const { value, label, onChange, validations } = props;
1164
+ const { value, placeholder, onChange, validations, label, required } = props;
1165
+ const fieldId = import_react.default.useId();
1165
1166
  const [previewUrl, setPreviewUrl] = import_react.default.useState();
1166
1167
  import_react.default.useEffect(() => {
1167
1168
  if (!value) {
@@ -1191,7 +1192,7 @@ var ImageSelector = (props) => {
1191
1192
  {
1192
1193
  type: "file",
1193
1194
  accept: "image/*",
1194
- id: "image-input",
1195
+ id: fieldId,
1195
1196
  onChange: handleFileChange,
1196
1197
  ref: inputRef,
1197
1198
  "aria-invalid": hasError(validations) || void 0
@@ -1203,10 +1204,10 @@ var ImageSelector = (props) => {
1203
1204
  ] }),
1204
1205
  /* @__PURE__ */ (0, import_jsx_runtime296.jsx)("div", { className: "content", children: previewUrl ? /* @__PURE__ */ (0, import_jsx_runtime296.jsx)("img", { src: previewUrl, alt: "preview" }) : /* @__PURE__ */ (0, import_jsx_runtime296.jsxs)("div", { className: "skeleton", onClick: handleOpenFileDialog, children: [
1205
1206
  /* @__PURE__ */ (0, import_jsx_runtime296.jsx)("div", { className: "icon-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime296.jsx)(ImageIcon_default, {}) }),
1206
- /* @__PURE__ */ (0, import_jsx_runtime296.jsx)("div", { className: "label", children: label || "\uC774\uBBF8\uC9C0 \uCD94\uAC00\uD558\uAE30" })
1207
+ /* @__PURE__ */ (0, import_jsx_runtime296.jsx)("div", { className: "label", children: placeholder || "\uC774\uBBF8\uC9C0 \uCD94\uAC00\uD558\uAE30" })
1207
1208
  ] }) })
1208
1209
  ] });
1209
- return /* @__PURE__ */ (0, import_jsx_runtime296.jsx)(Field, { validations, children: control });
1210
+ return /* @__PURE__ */ (0, import_jsx_runtime296.jsx)(Field, { label, required, htmlFor: fieldId, validations, children: control });
1210
1211
  };
1211
1212
  ImageSelector.displayName = "ImageSelector";
1212
1213
  var ImageSelector_default = ImageSelector;
@@ -6,8 +6,21 @@ interface ImageSelectorProps {
6
6
  value?: File;
7
7
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
8
8
  validations?: Validation[];
9
- /** 비어 있을 때 표시할 안내 글자 */
10
- label?: string;
9
+ /**
10
+ * 비어 있을 때 상자 안에 표시할 안내 글자.
11
+ *
12
+ * **0.13.0 에서 `label` 이 필드 라벨로 바뀌면서 이 이름으로 옮겨졌다.**
13
+ * DS 전체에서 `placeholder` 는 "빈 컨트롤 안에 보이는 글자" 를 뜻한다.
14
+ */
15
+ placeholder?: string;
16
+ /**
17
+ * 상자 **위에** 붙는 필드 라벨. 클릭하면 파일 선택창이 열린다.
18
+ *
19
+ * **0.13.0 이전에는 상자 안 글자를 뜻했다.** 그 용도는 `placeholder` 로 옮겼다.
20
+ */
21
+ label?: React.ReactNode;
22
+ /** 라벨 뒤에 필수 표시(*)를 붙인다 */
23
+ required?: boolean;
11
24
  /** 이미지를 고르거나 지우면 호출된다. 지우면 undefined 가 온다 */
12
25
  onChange?: (value: File | undefined) => void;
13
26
  }
@@ -6,8 +6,21 @@ interface ImageSelectorProps {
6
6
  value?: File;
7
7
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
8
8
  validations?: Validation[];
9
- /** 비어 있을 때 표시할 안내 글자 */
10
- label?: string;
9
+ /**
10
+ * 비어 있을 때 상자 안에 표시할 안내 글자.
11
+ *
12
+ * **0.13.0 에서 `label` 이 필드 라벨로 바뀌면서 이 이름으로 옮겨졌다.**
13
+ * DS 전체에서 `placeholder` 는 "빈 컨트롤 안에 보이는 글자" 를 뜻한다.
14
+ */
15
+ placeholder?: string;
16
+ /**
17
+ * 상자 **위에** 붙는 필드 라벨. 클릭하면 파일 선택창이 열린다.
18
+ *
19
+ * **0.13.0 이전에는 상자 안 글자를 뜻했다.** 그 용도는 `placeholder` 로 옮겼다.
20
+ */
21
+ label?: React.ReactNode;
22
+ /** 라벨 뒤에 필수 표시(*)를 붙인다 */
23
+ required?: boolean;
11
24
  /** 이미지를 고르거나 지우면 호출된다. 지우면 undefined 가 온다 */
12
25
  onChange?: (value: File | undefined) => void;
13
26
  }
@@ -1107,11 +1107,11 @@ var FieldMessage = (props) => {
1107
1107
  };
1108
1108
  FieldMessage.displayName = "FieldMessage";
1109
1109
  var Field = (props) => {
1110
- const { label, required = false, htmlFor, validations, children } = props;
1110
+ const { label, required = false, htmlFor, labelId, validations, children } = props;
1111
1111
  const hasMessages = !!validations && validations.length > 0;
1112
1112
  if (!label && !hasMessages) return /* @__PURE__ */ jsx295(Fragment, { children });
1113
1113
  return /* @__PURE__ */ jsxs189("div", { className: "lib-xplat-field", children: [
1114
- label && /* @__PURE__ */ jsxs189("label", { className: "lib-xplat-field-label", htmlFor, children: [
1114
+ label && /* @__PURE__ */ jsxs189("label", { className: "lib-xplat-field-label", id: labelId, htmlFor, children: [
1115
1115
  label,
1116
1116
  required && /* @__PURE__ */ jsx295("span", { className: "required", "aria-hidden": "true", children: "*" })
1117
1117
  ] }),
@@ -1125,7 +1125,8 @@ var hasError = (validations) => !!validations?.some((v) => v.status === "error")
1125
1125
  // src/components/ImageSelector/ImageSelector.tsx
1126
1126
  import { jsx as jsx296, jsxs as jsxs190 } from "react/jsx-runtime";
1127
1127
  var ImageSelector = (props) => {
1128
- const { value, label, onChange, validations } = props;
1128
+ const { value, placeholder, onChange, validations, label, required } = props;
1129
+ const fieldId = React.useId();
1129
1130
  const [previewUrl, setPreviewUrl] = React.useState();
1130
1131
  React.useEffect(() => {
1131
1132
  if (!value) {
@@ -1155,7 +1156,7 @@ var ImageSelector = (props) => {
1155
1156
  {
1156
1157
  type: "file",
1157
1158
  accept: "image/*",
1158
- id: "image-input",
1159
+ id: fieldId,
1159
1160
  onChange: handleFileChange,
1160
1161
  ref: inputRef,
1161
1162
  "aria-invalid": hasError(validations) || void 0
@@ -1167,10 +1168,10 @@ var ImageSelector = (props) => {
1167
1168
  ] }),
1168
1169
  /* @__PURE__ */ jsx296("div", { className: "content", children: previewUrl ? /* @__PURE__ */ jsx296("img", { src: previewUrl, alt: "preview" }) : /* @__PURE__ */ jsxs190("div", { className: "skeleton", onClick: handleOpenFileDialog, children: [
1169
1170
  /* @__PURE__ */ jsx296("div", { className: "icon-wrapper", children: /* @__PURE__ */ jsx296(ImageIcon_default, {}) }),
1170
- /* @__PURE__ */ jsx296("div", { className: "label", children: label || "\uC774\uBBF8\uC9C0 \uCD94\uAC00\uD558\uAE30" })
1171
+ /* @__PURE__ */ jsx296("div", { className: "label", children: placeholder || "\uC774\uBBF8\uC9C0 \uCD94\uAC00\uD558\uAE30" })
1171
1172
  ] }) })
1172
1173
  ] });
1173
- return /* @__PURE__ */ jsx296(Field, { validations, children: control });
1174
+ return /* @__PURE__ */ jsx296(Field, { label, required, htmlFor: fieldId, validations, children: control });
1174
1175
  };
1175
1176
  ImageSelector.displayName = "ImageSelector";
1176
1177
  var ImageSelector_default = ImageSelector;
@@ -178,11 +178,11 @@ var FieldMessage = (props) => {
178
178
  FieldMessage.displayName = "FieldMessage";
179
179
  var FieldMessage_default = FieldMessage;
180
180
  var Field = (props) => {
181
- const { label, required = false, htmlFor, validations, children } = props;
181
+ const { label, required = false, htmlFor, labelId, validations, children } = props;
182
182
  const hasMessages = !!validations && validations.length > 0;
183
183
  if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
184
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: [
185
+ label && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "lib-xplat-field-label", id: labelId, htmlFor, children: [
186
186
  label,
187
187
  required && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
188
188
  ] }),
@@ -23,6 +23,10 @@ interface PasswordInputProps extends Omit<InputAttributes, "size"> {
23
23
  validations?: Validation[];
24
24
  /** 높이와 글자 크기 */
25
25
  size?: InputSize;
26
+ /** 컨트롤 위에 붙는 라벨. 클릭하면 입력칸에 포커스가 간다 */
27
+ label?: React.ReactNode;
28
+ /** 라벨 뒤에 필수 표시(*)를 붙인다 */
29
+ required?: boolean;
26
30
  reg?: RegExp;
27
31
  }
28
32
  declare const PasswordInput: React.ForwardRefExoticComponent<PasswordInputProps & React.RefAttributes<HTMLInputElement>>;
@@ -23,6 +23,10 @@ interface PasswordInputProps extends Omit<InputAttributes, "size"> {
23
23
  validations?: Validation[];
24
24
  /** 높이와 글자 크기 */
25
25
  size?: InputSize;
26
+ /** 컨트롤 위에 붙는 라벨. 클릭하면 입력칸에 포커스가 간다 */
27
+ label?: React.ReactNode;
28
+ /** 라벨 뒤에 필수 표시(*)를 붙인다 */
29
+ required?: boolean;
26
30
  reg?: RegExp;
27
31
  }
28
32
  declare const PasswordInput: React.ForwardRefExoticComponent<PasswordInputProps & React.RefAttributes<HTMLInputElement>>;
@@ -141,11 +141,11 @@ var FieldMessage = (props) => {
141
141
  FieldMessage.displayName = "FieldMessage";
142
142
  var FieldMessage_default = FieldMessage;
143
143
  var Field = (props) => {
144
- const { label, required = false, htmlFor, validations, children } = props;
144
+ const { label, required = false, htmlFor, labelId, validations, children } = props;
145
145
  const hasMessages = !!validations && validations.length > 0;
146
146
  if (!label && !hasMessages) return /* @__PURE__ */ jsx4(Fragment, { children });
147
147
  return /* @__PURE__ */ jsxs3("div", { className: "lib-xplat-field", children: [
148
- label && /* @__PURE__ */ jsxs3("label", { className: "lib-xplat-field-label", htmlFor, children: [
148
+ label && /* @__PURE__ */ jsxs3("label", { className: "lib-xplat-field-label", id: labelId, htmlFor, children: [
149
149
  label,
150
150
  required && /* @__PURE__ */ jsx4("span", { className: "required", "aria-hidden": "true", children: "*" })
151
151
  ] }),
@@ -51,8 +51,10 @@ var import_react = __toESM(require("react"), 1);
51
51
  var useAutoPosition = (triggerRef, popRef, enabled = true) => {
52
52
  const [position, setPosition] = import_react.default.useState({
53
53
  position: {},
54
- direction: "bottom"
54
+ direction: "bottom",
55
+ repositioning: false
55
56
  });
57
+ const placedRef = import_react.default.useRef(false);
56
58
  const calculatePosition = import_react.default.useCallback(() => {
57
59
  if (!triggerRef.current || !popRef.current) return;
58
60
  const triggerRect = triggerRef.current.getBoundingClientRect();
@@ -77,11 +79,16 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
77
79
  if (left < 8) left = 8;
78
80
  setPosition({
79
81
  position: { top, left },
80
- direction
82
+ direction,
83
+ repositioning: placedRef.current
81
84
  });
85
+ placedRef.current = true;
82
86
  }, [triggerRef, popRef]);
83
87
  import_react.default.useLayoutEffect(() => {
84
- if (!enabled) return;
88
+ if (!enabled) {
89
+ placedRef.current = false;
90
+ return;
91
+ }
85
92
  calculatePosition();
86
93
  const raf = requestAnimationFrame(calculatePosition);
87
94
  return () => cancelAnimationFrame(raf);
@@ -96,11 +103,17 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
96
103
  }, [calculatePosition, enabled, popRef]);
97
104
  import_react.default.useEffect(() => {
98
105
  if (!enabled) return;
99
- window.addEventListener("resize", calculatePosition);
100
- window.addEventListener("scroll", calculatePosition, true);
106
+ let raf = 0;
107
+ const onScrollOrResize = () => {
108
+ cancelAnimationFrame(raf);
109
+ raf = requestAnimationFrame(calculatePosition);
110
+ };
111
+ window.addEventListener("resize", onScrollOrResize, { passive: true });
112
+ window.addEventListener("scroll", onScrollOrResize, { passive: true, capture: true });
101
113
  return () => {
102
- window.removeEventListener("resize", calculatePosition);
103
- window.removeEventListener("scroll", calculatePosition, true);
114
+ cancelAnimationFrame(raf);
115
+ window.removeEventListener("resize", onScrollOrResize);
116
+ window.removeEventListener("scroll", onScrollOrResize, true);
104
117
  };
105
118
  }, [calculatePosition, enabled]);
106
119
  return position;
@@ -215,6 +228,7 @@ var PopOver = (props) => {
215
228
  className: clsx_default(
216
229
  "lib-xplat-pop-over-content",
217
230
  position.direction,
231
+ position.repositioning && "repositioning",
218
232
  eventTrigger && "visible"
219
233
  ),
220
234
  ref: popRef,
@@ -27,3 +27,6 @@
27
27
  opacity: 1;
28
28
  transform: scale(1);
29
29
  }
30
+ .lib-xplat-pop-over-content.repositioning {
31
+ transition: none;
32
+ }
@@ -15,8 +15,10 @@ import React from "react";
15
15
  var useAutoPosition = (triggerRef, popRef, enabled = true) => {
16
16
  const [position, setPosition] = React.useState({
17
17
  position: {},
18
- direction: "bottom"
18
+ direction: "bottom",
19
+ repositioning: false
19
20
  });
21
+ const placedRef = React.useRef(false);
20
22
  const calculatePosition = React.useCallback(() => {
21
23
  if (!triggerRef.current || !popRef.current) return;
22
24
  const triggerRect = triggerRef.current.getBoundingClientRect();
@@ -41,11 +43,16 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
41
43
  if (left < 8) left = 8;
42
44
  setPosition({
43
45
  position: { top, left },
44
- direction
46
+ direction,
47
+ repositioning: placedRef.current
45
48
  });
49
+ placedRef.current = true;
46
50
  }, [triggerRef, popRef]);
47
51
  React.useLayoutEffect(() => {
48
- if (!enabled) return;
52
+ if (!enabled) {
53
+ placedRef.current = false;
54
+ return;
55
+ }
49
56
  calculatePosition();
50
57
  const raf = requestAnimationFrame(calculatePosition);
51
58
  return () => cancelAnimationFrame(raf);
@@ -60,11 +67,17 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
60
67
  }, [calculatePosition, enabled, popRef]);
61
68
  React.useEffect(() => {
62
69
  if (!enabled) return;
63
- window.addEventListener("resize", calculatePosition);
64
- window.addEventListener("scroll", calculatePosition, true);
70
+ let raf = 0;
71
+ const onScrollOrResize = () => {
72
+ cancelAnimationFrame(raf);
73
+ raf = requestAnimationFrame(calculatePosition);
74
+ };
75
+ window.addEventListener("resize", onScrollOrResize, { passive: true });
76
+ window.addEventListener("scroll", onScrollOrResize, { passive: true, capture: true });
65
77
  return () => {
66
- window.removeEventListener("resize", calculatePosition);
67
- window.removeEventListener("scroll", calculatePosition, true);
78
+ cancelAnimationFrame(raf);
79
+ window.removeEventListener("resize", onScrollOrResize);
80
+ window.removeEventListener("scroll", onScrollOrResize, true);
68
81
  };
69
82
  }, [calculatePosition, enabled]);
70
83
  return position;
@@ -179,6 +192,7 @@ var PopOver = (props) => {
179
192
  className: clsx_default(
180
193
  "lib-xplat-pop-over-content",
181
194
  position.direction,
195
+ position.repositioning && "repositioning",
182
196
  eventTrigger && "visible"
183
197
  ),
184
198
  ref: popRef,
@@ -177,11 +177,11 @@ var FieldMessage = (props) => {
177
177
  };
178
178
  FieldMessage.displayName = "FieldMessage";
179
179
  var Field = (props) => {
180
- const { label, required = false, htmlFor, validations, children } = props;
180
+ const { label, required = false, htmlFor, labelId, validations, children } = props;
181
181
  const hasMessages = !!validations && validations.length > 0;
182
182
  if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
183
183
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lib-xplat-field", children: [
184
- label && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "lib-xplat-field-label", htmlFor, children: [
184
+ label && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "lib-xplat-field-label", id: labelId, htmlFor, children: [
185
185
  label,
186
186
  required && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
187
187
  ] }),
@@ -140,11 +140,11 @@ var FieldMessage = (props) => {
140
140
  };
141
141
  FieldMessage.displayName = "FieldMessage";
142
142
  var Field = (props) => {
143
- const { label, required = false, htmlFor, validations, children } = props;
143
+ const { label, required = false, htmlFor, labelId, validations, children } = props;
144
144
  const hasMessages = !!validations && validations.length > 0;
145
145
  if (!label && !hasMessages) return /* @__PURE__ */ jsx4(Fragment, { children });
146
146
  return /* @__PURE__ */ jsxs3("div", { className: "lib-xplat-field", children: [
147
- label && /* @__PURE__ */ jsxs3("label", { className: "lib-xplat-field-label", htmlFor, children: [
147
+ label && /* @__PURE__ */ jsxs3("label", { className: "lib-xplat-field-label", id: labelId, htmlFor, children: [
148
148
  label,
149
149
  required && /* @__PURE__ */ jsx4("span", { className: "required", "aria-hidden": "true", children: "*" })
150
150
  ] }),