@farmzone/fz-react-ui 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -441,6 +441,7 @@ type DatePickerBaseProps = {
441
441
  disabled?: boolean;
442
442
  maxDate?: YmdDateString;
443
443
  calendarYearRange?: CalendarYearRange;
444
+ calendarSelectContainerClass?: string;
444
445
  showTodayButton?: boolean;
445
446
  };
446
447
  type DatePickerSingleMode = DatePickerBaseProps & {
@@ -842,6 +843,9 @@ interface CascadingSelectFieldConfig {
842
843
  separator?: string;
843
844
  }
844
845
  interface SelectWithInputFieldConfig {
846
+ /** 입력 필드 숫자만 허용 여부. 기본: true. */
847
+ numbersOnly?: boolean;
848
+ /** @deprecated numbersOnly를 사용하세요. */
845
849
  inputMode?: "number" | "text";
846
850
  }
847
851
  interface InputWithButtonFieldConfig {
@@ -859,6 +863,7 @@ interface FormFieldConfig<TFieldValues extends FieldValues = FieldValues> {
859
863
  options?: SelectOption$1[];
860
864
  className?: string;
861
865
  allowDecimal?: boolean;
866
+ numbersOnly?: boolean;
862
867
  canReset?: boolean;
863
868
  datePosition?: "top" | "bottom";
864
869
  disableFutureDates?: boolean;
package/dist/index.d.ts CHANGED
@@ -441,6 +441,7 @@ type DatePickerBaseProps = {
441
441
  disabled?: boolean;
442
442
  maxDate?: YmdDateString;
443
443
  calendarYearRange?: CalendarYearRange;
444
+ calendarSelectContainerClass?: string;
444
445
  showTodayButton?: boolean;
445
446
  };
446
447
  type DatePickerSingleMode = DatePickerBaseProps & {
@@ -842,6 +843,9 @@ interface CascadingSelectFieldConfig {
842
843
  separator?: string;
843
844
  }
844
845
  interface SelectWithInputFieldConfig {
846
+ /** 입력 필드 숫자만 허용 여부. 기본: true. */
847
+ numbersOnly?: boolean;
848
+ /** @deprecated numbersOnly를 사용하세요. */
845
849
  inputMode?: "number" | "text";
846
850
  }
847
851
  interface InputWithButtonFieldConfig {
@@ -859,6 +863,7 @@ interface FormFieldConfig<TFieldValues extends FieldValues = FieldValues> {
859
863
  options?: SelectOption$1[];
860
864
  className?: string;
861
865
  allowDecimal?: boolean;
866
+ numbersOnly?: boolean;
862
867
  canReset?: boolean;
863
868
  datePosition?: "top" | "bottom";
864
869
  disableFutureDates?: boolean;
package/dist/index.js CHANGED
@@ -939,7 +939,7 @@ function Select2(props) {
939
939
  onChange("");
940
940
  onReset?.();
941
941
  };
942
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-0.5", containerClass ?? "w-30"), children: [
942
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-0.5", containerClass ?? "w-20"), children: [
943
943
  label && /* @__PURE__ */ jsx(
944
944
  "label",
945
945
  {
@@ -1997,7 +1997,8 @@ function RenderCalendar(props) {
1997
1997
  onYearChange,
1998
1998
  onMonthChange,
1999
1999
  editDateSelect = true,
2000
- datePosition = "bottom"
2000
+ datePosition = "bottom",
2001
+ calendarSelectContainerClass
2001
2002
  } = props;
2002
2003
  const year = currentDate.getFullYear();
2003
2004
  const month = currentDate.getMonth();
@@ -2081,7 +2082,8 @@ function RenderCalendar(props) {
2081
2082
  value: year.toString(),
2082
2083
  options: yearOptions,
2083
2084
  onChange: handleYearChange,
2084
- className: "cursor-pointer"
2085
+ className: "cursor-pointer",
2086
+ containerClass: calendarSelectContainerClass
2085
2087
  }
2086
2088
  ) }),
2087
2089
  /* @__PURE__ */ jsx("div", { onClick: handleStopPropagation, onMouseDown: handleStopPropagation, children: /* @__PURE__ */ jsx(
@@ -2090,7 +2092,8 @@ function RenderCalendar(props) {
2090
2092
  value: (month + 1).toString(),
2091
2093
  options: monthOptions,
2092
2094
  onChange: handleMonthChange,
2093
- className: " cursor-pointer"
2095
+ className: "cursor-pointer",
2096
+ containerClass: calendarSelectContainerClass
2094
2097
  }
2095
2098
  ) })
2096
2099
  ]
@@ -2258,6 +2261,7 @@ function DatePickerRange(props) {
2258
2261
  disabled = false,
2259
2262
  maxDate,
2260
2263
  calendarYearRange,
2264
+ calendarSelectContainerClass,
2261
2265
  showTodayButton = true
2262
2266
  } = props;
2263
2267
  const [inputs, setInputs] = useState({ start: startValue, end: endValue });
@@ -2394,6 +2398,7 @@ function DatePickerRange(props) {
2394
2398
  datePosition: autoDatePosition,
2395
2399
  maxDate,
2396
2400
  calendarYearRange,
2401
+ calendarSelectContainerClass,
2397
2402
  showTodayButton
2398
2403
  }
2399
2404
  )
@@ -2473,6 +2478,7 @@ function DatePickerSingle(props) {
2473
2478
  disabled = false,
2474
2479
  maxDate,
2475
2480
  calendarYearRange,
2481
+ calendarSelectContainerClass,
2476
2482
  onBlur,
2477
2483
  showTodayButton = true
2478
2484
  } = props;
@@ -2565,6 +2571,7 @@ function DatePickerSingle(props) {
2565
2571
  datePosition: autoDatePosition,
2566
2572
  maxDate,
2567
2573
  calendarYearRange,
2574
+ calendarSelectContainerClass,
2568
2575
  showTodayButton
2569
2576
  }
2570
2577
  )
@@ -2623,6 +2630,7 @@ function DatePicker(props) {
2623
2630
  disabled,
2624
2631
  maxDate,
2625
2632
  calendarYearRange,
2633
+ calendarSelectContainerClass,
2626
2634
  showTodayButton
2627
2635
  } = props;
2628
2636
  if (isRange) {
@@ -2640,6 +2648,7 @@ function DatePicker(props) {
2640
2648
  disabled,
2641
2649
  maxDate,
2642
2650
  calendarYearRange,
2651
+ calendarSelectContainerClass,
2643
2652
  showTodayButton
2644
2653
  }
2645
2654
  );
@@ -2659,6 +2668,7 @@ function DatePicker(props) {
2659
2668
  disabled,
2660
2669
  maxDate,
2661
2670
  calendarYearRange,
2671
+ calendarSelectContainerClass,
2662
2672
  showTodayButton,
2663
2673
  onBlur
2664
2674
  }
@@ -3524,7 +3534,7 @@ function TableFooter(props) {
3524
3534
  const { paginationInfo, renderLeft, renderRight } = props;
3525
3535
  return /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between py-4 min-h-16", children: [
3526
3536
  /* @__PURE__ */ jsx("div", { className: "flex justify-start flex-1 min-w-0", children: renderLeft }),
3527
- paginationInfo && /* @__PURE__ */ jsx("div", { className: "flex-1 px-5", children: /* @__PURE__ */ jsx(Pagination, { ...paginationInfo }) }),
3537
+ paginationInfo && /* @__PURE__ */ jsx("div", { className: "px-5", children: /* @__PURE__ */ jsx(Pagination, { ...paginationInfo }) }),
3528
3538
  /* @__PURE__ */ jsx("div", { className: "flex justify-end flex-1 min-w-0", children: renderRight })
3529
3539
  ] }) });
3530
3540
  }
@@ -3680,7 +3690,7 @@ function useTableFixed(columns, isHeader = false) {
3680
3690
  window.addEventListener("resize", onResize);
3681
3691
  const firstKey = columns[0]?.key;
3682
3692
  const cell = firstKey ? document.getElementById(`${firstKey}${isHeader ? "-header" : "-body"}`) : null;
3683
- const tableEl = cell?.closest("table") || document.querySelector("table.table-layout");
3693
+ const tableEl = cell?.closest("table") || document.querySelector("table.table-fixed");
3684
3694
  if (tableEl && "ResizeObserver" in window) {
3685
3695
  resizeObserverRef.current = new ResizeObserver(handleResize);
3686
3696
  resizeObserverRef.current.observe(tableEl);
@@ -3767,19 +3777,14 @@ function TableHeader(props) {
3767
3777
  const columns = useMemo(() => {
3768
3778
  return customColumns.map((column) => {
3769
3779
  const { key } = column;
3770
- const getWidth = () => {
3771
- if (key === "__checkbox__") {
3772
- return "40px";
3773
- }
3774
- return column.width ?? "calc(100%/4)";
3775
- };
3776
- const getAlign = () => {
3777
- if (key === "__checkbox__") {
3778
- return "center";
3779
- }
3780
- return column.align ?? "left";
3780
+ const isCheckbox = key === "__checkbox__";
3781
+ return {
3782
+ ...column,
3783
+ width: isCheckbox ? "40px" : column.width,
3784
+ minWidth: isCheckbox ? "40px" : column.minWidth ?? column.width,
3785
+ maxWidth: isCheckbox ? "40px" : column.maxWidth,
3786
+ align: isCheckbox ? "center" : column.align ?? "left"
3781
3787
  };
3782
- return { ...column, width: getWidth(), align: getAlign() };
3783
3788
  });
3784
3789
  }, [customColumns]);
3785
3790
  const { getColumnFixedStyle } = useTableFixed(columns, true);
@@ -4252,11 +4257,11 @@ function SkeletonTd(props) {
4252
4257
  return /* @__PURE__ */ jsx(
4253
4258
  "td",
4254
4259
  {
4255
- className: "px-2 py-[6.8px] whitespace-nowrap text-sm border-r border-b border-gray-200/70 last:border-r-0",
4260
+ className: `${col.key === "__checkbox__" ? "p-0" : "px-2 py-[6.8px]"} whitespace-nowrap text-sm border-r border-b border-gray-200/70 last:border-r-0`,
4256
4261
  style: {
4257
4262
  width: col.width,
4258
4263
  minWidth: col.minWidth || col.width,
4259
- maxWidth: col.maxWidth || col.width
4264
+ maxWidth: col.key === "__checkbox__" ? col.maxWidth || col.width : void 0
4260
4265
  },
4261
4266
  children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-4", children: showSquare ? /* @__PURE__ */ jsx(Skeleton, { className: "size-4 rounded-xs" }) : /* @__PURE__ */ jsx(Skeleton, { className: "h-1.5 rounded-sm", style: { width: "calc(100% - 20px)" } }) })
4262
4267
  }
@@ -4337,7 +4342,7 @@ function getAlignStyle(align) {
4337
4342
  return align === "left" ? "flex-start" : align === "center" ? "center" : "flex-end";
4338
4343
  }
4339
4344
  function applyColDefaults(col) {
4340
- const width = col.key === "__checkbox__" ? "40px" : col.width ?? col.minWidth ?? "100px";
4345
+ const width = col.key === "__checkbox__" ? "40px" : col.width ?? col.minWidth;
4341
4346
  const align = col.key === "__checkbox__" ? "center" : col.align ?? "left";
4342
4347
  return { ...col, width, align };
4343
4348
  }
@@ -4786,17 +4791,18 @@ function Table(props) {
4786
4791
  const { width, minWidth, maxWidth, align } = col;
4787
4792
  const fixedStyle = isPinned ? getColumnFixedStyleRef.current(col) : void 0;
4788
4793
  const cellContent = col.render ? col.render(record[col.key], record, rowIdx) : String(record[col.key] ?? "");
4794
+ const isCheckbox = col.key === "__checkbox__";
4789
4795
  return /* @__PURE__ */ jsx(
4790
4796
  "td",
4791
4797
  {
4792
4798
  id: `${col.key}-body`,
4793
4799
  "data-row-cell": "true",
4794
4800
  "data-col-key": col.key,
4795
- className: "px-2 py-[6.8px] whitespace-nowrap text-sm text-gray-900 border-r border-b border-gray-200 last:border-r-0 bg-inherit group-hover:bg-gray-100 transition-all",
4801
+ className: `${isCheckbox ? "p-0" : "px-2 py-[6.8px]"} whitespace-nowrap text-sm text-gray-900 border-r border-b border-gray-200 last:border-r-0 bg-inherit group-hover:bg-gray-100 transition-all`,
4796
4802
  style: {
4797
4803
  width,
4798
4804
  minWidth: minWidth || width,
4799
- maxWidth: maxWidth || width,
4805
+ maxWidth: isCheckbox ? maxWidth || width : void 0,
4800
4806
  ...fixedStyle
4801
4807
  },
4802
4808
  onClick: (event) => {
@@ -4892,7 +4898,7 @@ function Table(props) {
4892
4898
  ref: scrollRef,
4893
4899
  className: "relative w-full overflow-y-auto overflow-x-auto custom-view-scrollbar bg-white",
4894
4900
  style: { height: `${tableHeight}px` },
4895
- children: /* @__PURE__ */ jsxs("table", { className: "w-full table-layout", children: [
4901
+ children: /* @__PURE__ */ jsxs("table", { className: "w-full table-fixed", children: [
4896
4902
  /* @__PURE__ */ jsx(
4897
4903
  TableHeader,
4898
4904
  {
@@ -5992,6 +5998,7 @@ var FORM_CONTROL_CLASS = "h-8 w-full rounded border border-gray-300 bg-white px-
5992
5998
  function formControlClass(hasError, extra) {
5993
5999
  return cn2(FORM_CONTROL_CLASS, FORM_CONTROL_FOCUS_CLASS, hasError && FORM_FIELD_ERROR_BORDER_CLASS, extra);
5994
6000
  }
6001
+ var FIELD_READONLY_TEXT_CLASS = "text-sm text-gray-800 leading-tight";
5995
6002
  function fieldControlWrapClass(className) {
5996
6003
  return className ? cn2("min-w-0 shrink-0", className) : "min-w-0 w-full";
5997
6004
  }
@@ -6338,6 +6345,7 @@ function AddressField(props) {
6338
6345
  const {
6339
6346
  placeholder = "\uC8FC\uC18C\uB97C \uAC80\uC0C9\uD558\uC138\uC694",
6340
6347
  disabled = false,
6348
+ readOnly = false,
6341
6349
  className = "",
6342
6350
  addressDetailKey,
6343
6351
  addressClassNames
@@ -6352,6 +6360,9 @@ function AddressField(props) {
6352
6360
  searchButtonRef.current?.click();
6353
6361
  }
6354
6362
  }, [isFocused]);
6363
+ if (readOnly) {
6364
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: String(value ?? "") || "\u2014" }) });
6365
+ }
6355
6366
  const handleComplete = (data) => {
6356
6367
  isAddressSelectedRef.current = true;
6357
6368
  const nextAddress = data.roadAddress || data.address;
@@ -6470,12 +6481,12 @@ function CascadingSelectField(props) {
6470
6481
  }
6471
6482
  function CascadingSelectFieldInner(props) {
6472
6483
  const {
6473
- field: { onChange, value },
6484
+ field: { onChange, onBlur, value },
6474
6485
  config,
6475
6486
  hasError = false,
6476
6487
  cascadingSelect
6477
6488
  } = props;
6478
- const { disabled = false, className = "", cascadingSelectClassNames } = config;
6489
+ const { disabled = false, readOnly = false, className = "", cascadingSelectClassNames } = config;
6479
6490
  const {
6480
6491
  primaryOptions,
6481
6492
  getSecondaryOptions,
@@ -6489,10 +6500,17 @@ function CascadingSelectFieldInner(props) {
6489
6500
  [formValue, getSecondaryOptions, separator]
6490
6501
  );
6491
6502
  useLayoutEffect(() => {
6503
+ if (readOnly) return;
6492
6504
  if (normalizedFormValue !== formValue) {
6493
6505
  onChange(normalizedFormValue);
6494
6506
  }
6495
- }, [formValue, normalizedFormValue, onChange]);
6507
+ }, [formValue, normalizedFormValue, onChange, readOnly]);
6508
+ if (readOnly) {
6509
+ const primaryLabel = primaryOptions.find((o) => String(o.value) === primary)?.label ?? primary;
6510
+ const secondaryLabel = secondary ? secondaryOptions.find((o) => String(o.value) === secondary)?.label ?? secondary : "";
6511
+ const displayText = secondaryLabel ? `${primaryLabel} ${secondaryLabel}` : primaryLabel || "\u2014";
6512
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: displayText }) });
6513
+ }
6496
6514
  const updateValue = (nextPrimary, nextSecondary) => {
6497
6515
  onChange(createCascadingSelectValue(nextPrimary, nextSecondary, separator));
6498
6516
  };
@@ -6518,6 +6536,7 @@ function CascadingSelectFieldInner(props) {
6518
6536
  canReset: true,
6519
6537
  onChange: (nextPrimary) => {
6520
6538
  updateValue(nextPrimary, "");
6539
+ if (!nextPrimary) onBlur();
6521
6540
  }
6522
6541
  }
6523
6542
  ),
@@ -6534,6 +6553,7 @@ function CascadingSelectFieldInner(props) {
6534
6553
  canReset: true,
6535
6554
  onChange: (nextSecondary) => {
6536
6555
  updateValue(primary, nextSecondary);
6556
+ onBlur();
6537
6557
  }
6538
6558
  }
6539
6559
  )
@@ -6544,7 +6564,11 @@ function CheckboxField(props) {
6544
6564
  field: { onChange, value },
6545
6565
  config
6546
6566
  } = props;
6547
- const { disabled = false, checkboxLabel, className = "" } = config;
6567
+ const { disabled = false, readOnly = false, checkboxLabel, className = "" } = config;
6568
+ if (readOnly) {
6569
+ const text = value ? checkboxLabel ?? "\uC120\uD0DD\uB428" : "\u2014";
6570
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: text }) });
6571
+ }
6548
6572
  return /* @__PURE__ */ jsx(
6549
6573
  Checkbox,
6550
6574
  {
@@ -6565,11 +6589,15 @@ function DateField(props) {
6565
6589
  const {
6566
6590
  placeholder = "YYYY-MM-DD",
6567
6591
  disabled = false,
6592
+ readOnly = false,
6568
6593
  className = "w-40",
6569
6594
  datePosition = "bottom",
6570
6595
  disableFutureDates = false
6571
6596
  } = config;
6572
6597
  const maxDate = disableFutureDates ? formatDate(/* @__PURE__ */ new Date()) : void 0;
6598
+ if (readOnly) {
6599
+ return /* @__PURE__ */ jsx("div", { className: cn2("flex items-center", fieldControlWrapClass(className)), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: String(value ?? "") || "\u2014" }) });
6600
+ }
6573
6601
  return /* @__PURE__ */ jsx("div", { className: cn2("flex items-center", fieldControlWrapClass(className)), children: /* @__PURE__ */ jsx(
6574
6602
  DatePicker,
6575
6603
  {
@@ -6597,11 +6625,17 @@ function InputField(props) {
6597
6625
  readOnly = false,
6598
6626
  maxLength,
6599
6627
  className = "",
6600
- type = "input"
6628
+ type = "input",
6629
+ numbersOnly = false
6601
6630
  } = config;
6602
6631
  const inputType = type === "email" ? "email" : type === "password" ? "password" : "text";
6632
+ if (readOnly) {
6633
+ const text = type === "password" ? value ? "**********" : "" : String(value ?? "") || "";
6634
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: text }) });
6635
+ }
6603
6636
  const handleChange = (e) => {
6604
- onChange(e.target.value);
6637
+ const value2 = numbersOnly ? e.target.value.replace(/\D/g, "") : e.target.value;
6638
+ onChange(value2);
6605
6639
  };
6606
6640
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(
6607
6641
  Input2,
@@ -6632,7 +6666,7 @@ function InputWithButtonField(props) {
6632
6666
  placeholder = "\uC870\uD68C \uBC84\uD2BC\uC73C\uB85C \uC120\uD0DD",
6633
6667
  disabled = false,
6634
6668
  className = "",
6635
- readOnly = true,
6669
+ readOnly = false,
6636
6670
  inputWithButton,
6637
6671
  onClickInputWithButton
6638
6672
  } = config;
@@ -6641,13 +6675,16 @@ function InputWithButtonField(props) {
6641
6675
  if (disabled) return;
6642
6676
  onClickInputWithButton?.();
6643
6677
  };
6678
+ if (readOnly) {
6679
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: String(value ?? "") || "\u2014" }) });
6680
+ }
6644
6681
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsxs("div", { className: "flex w-full min-w-0 items-center gap-2", children: [
6645
6682
  /* @__PURE__ */ jsx(
6646
6683
  Input2,
6647
6684
  {
6648
6685
  placeholder,
6649
6686
  value: String(value ?? ""),
6650
- readOnly,
6687
+ readOnly: true,
6651
6688
  disabled,
6652
6689
  bare: true,
6653
6690
  className: formControlClass(hasError),
@@ -6687,6 +6724,9 @@ function NumberField(props) {
6687
6724
  }
6688
6725
  };
6689
6726
  const displayValue = value != null && value !== "" ? isMoney ? formatNumberWithCommas(value) : String(value) : "";
6727
+ if (readOnly) {
6728
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: displayValue || "\u2014" }) });
6729
+ }
6690
6730
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(
6691
6731
  Input2,
6692
6732
  {
@@ -6716,6 +6756,9 @@ function PhoneField(props) {
6716
6756
  const handleChange = (e) => {
6717
6757
  onChange(formatPhoneNumber(e.target.value));
6718
6758
  };
6759
+ if (readOnly) {
6760
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: String(value ?? "") || "\u2014" }) });
6761
+ }
6719
6762
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(
6720
6763
  Input2,
6721
6764
  {
@@ -6740,7 +6783,11 @@ function RadioField(props) {
6740
6783
  field: { onChange, value, name },
6741
6784
  config
6742
6785
  } = props;
6743
- const { disabled = false, options = [], className = "" } = config;
6786
+ const { disabled = false, readOnly = false, options = [], className = "" } = config;
6787
+ if (readOnly) {
6788
+ const label = options.find((o) => String(o.value) === String(value ?? ""))?.label ?? String(value ?? "");
6789
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: label || "\u2014" }) });
6790
+ }
6744
6791
  return /* @__PURE__ */ jsx(
6745
6792
  RadioGroup,
6746
6793
  {
@@ -6772,11 +6819,16 @@ function SelectField(props) {
6772
6819
  const {
6773
6820
  placeholder = "\uC120\uD0DD\uD558\uC138\uC694",
6774
6821
  disabled = false,
6822
+ readOnly = false,
6775
6823
  options = [],
6776
6824
  className = "",
6777
6825
  canReset = false
6778
6826
  } = config;
6779
6827
  const selectOptions = toSelectOptions2(options);
6828
+ if (readOnly) {
6829
+ const label = selectOptions.find((o) => o.value === String(value ?? ""))?.label ?? String(value ?? "");
6830
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: label || "\u2014" }) });
6831
+ }
6780
6832
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(
6781
6833
  Select2,
6782
6834
  {
@@ -6823,16 +6875,6 @@ function combineSelectWithInputValue(prefix, input) {
6823
6875
  return `${prefix}${input}`;
6824
6876
  }
6825
6877
  var DEFAULT_SELECT_TRIGGER = "h-8 rounded border bg-white px-2 text-sm shadow-none min-w-[5rem]";
6826
- function resolveSelectWithInputState(formValue, defaultPrefix, options) {
6827
- const parsed = parseSelectWithInputValue(formValue, defaultPrefix);
6828
- const validPrefixes = new Set(options.map((opt) => String(opt.value)));
6829
- const prefix = validPrefixes.has(parsed.prefix) ? parsed.prefix : defaultPrefix;
6830
- return {
6831
- prefix,
6832
- inputPart: parsed.input,
6833
- normalizedFormValue: combineSelectWithInputValue(prefix, parsed.input)
6834
- };
6835
- }
6836
6878
  function SelectWithInputField(props) {
6837
6879
  const {
6838
6880
  field: { onChange, onBlur, value },
@@ -6841,37 +6883,58 @@ function SelectWithInputField(props) {
6841
6883
  } = props;
6842
6884
  const {
6843
6885
  disabled = false,
6886
+ readOnly = false,
6844
6887
  options = [],
6845
6888
  maxLength,
6846
6889
  className = "",
6847
6890
  selectWithInput,
6848
6891
  selectWithInputClassNames
6849
6892
  } = config;
6850
- const inputMode = selectWithInput?.inputMode ?? "number";
6893
+ const validationCtx = useSubmitFormValidation();
6894
+ const numbersOnly = selectWithInput?.numbersOnly ?? selectWithInput?.inputMode !== "text";
6851
6895
  const defaultPrefix = String(options[0]?.value ?? "");
6852
6896
  const numberInputRef = useRef(null);
6853
6897
  const formValue = String(value ?? "");
6854
- const { prefix, inputPart, normalizedFormValue } = useMemo(
6855
- () => resolveSelectWithInputState(formValue, defaultPrefix, options),
6856
- [defaultPrefix, formValue, options]
6857
- );
6898
+ const { prefix, inputPart, normalizedFormValue } = useMemo(() => {
6899
+ const parsed = parseSelectWithInputValue(formValue, defaultPrefix);
6900
+ const validPrefixes = new Set(options.map((opt) => String(opt.value)));
6901
+ const resolvedPrefix = validPrefixes.has(parsed.prefix) ? parsed.prefix : defaultPrefix;
6902
+ return {
6903
+ prefix: resolvedPrefix,
6904
+ inputPart: parsed.input,
6905
+ normalizedFormValue: combineSelectWithInputValue(resolvedPrefix, parsed.input)
6906
+ };
6907
+ }, [defaultPrefix, formValue, options]);
6908
+ const [selectedPrefix, setSelectedPrefix] = useState(prefix);
6909
+ const [prevFormValue, setPrevFormValue] = useState(formValue);
6910
+ if (prevFormValue !== formValue) {
6911
+ setPrevFormValue(formValue);
6912
+ setSelectedPrefix(formValue ? prefix : defaultPrefix);
6913
+ }
6858
6914
  useLayoutEffect(() => {
6859
- if (normalizedFormValue !== formValue) {
6915
+ if (readOnly) return;
6916
+ if (formValue && normalizedFormValue !== formValue) {
6860
6917
  onChange(normalizedFormValue);
6861
6918
  }
6862
- }, [formValue, normalizedFormValue, onChange]);
6919
+ }, [formValue, normalizedFormValue, onChange, readOnly]);
6920
+ if (readOnly) {
6921
+ const prefixLabel = options.find((o) => String(o.value) === prefix)?.label ?? prefix;
6922
+ const displayText = inputPart ? `${prefixLabel} ${inputPart}` : prefix ? prefixLabel : "\u2014";
6923
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: displayText || "\u2014" }) });
6924
+ }
6863
6925
  const updateCombined = (nextPrefix, nextInput) => {
6864
6926
  onChange(combineSelectWithInputValue(nextPrefix, nextInput));
6865
6927
  };
6866
6928
  const normalizeInput = (raw) => {
6867
- if (inputMode === "number") {
6868
- return raw.replace(/\D+/g, "");
6869
- }
6929
+ if (numbersOnly) return raw.replace(/\D+/g, "");
6870
6930
  return raw;
6871
6931
  };
6872
6932
  const handleInputChange = (raw) => {
6873
6933
  const normalized = normalizeInput(raw.normalize("NFKC"));
6874
- updateCombined(prefix, normalized);
6934
+ if (normalized) {
6935
+ validationCtx?.markFieldHadValue(String(config.name));
6936
+ }
6937
+ updateCombined(selectedPrefix, normalized);
6875
6938
  };
6876
6939
  const selectClass = resolveSelectClassNameProp(selectWithInputClassNames?.selectClassName, {
6877
6940
  containerClassName: "shrink-0",
@@ -6881,7 +6944,7 @@ function SelectWithInputField(props) {
6881
6944
  /* @__PURE__ */ jsx(
6882
6945
  Select2,
6883
6946
  {
6884
- value: prefix,
6947
+ value: selectedPrefix,
6885
6948
  placeholder: "\uC120\uD0DD",
6886
6949
  disabled,
6887
6950
  hasError,
@@ -6889,6 +6952,7 @@ function SelectWithInputField(props) {
6889
6952
  containerClass: selectClass.containerClassName,
6890
6953
  triggerClassName: selectClass.triggerClassName,
6891
6954
  onChange: (nextPrefix) => {
6955
+ setSelectedPrefix(nextPrefix);
6892
6956
  updateCombined(nextPrefix, inputPart);
6893
6957
  }
6894
6958
  }
@@ -6907,7 +6971,7 @@ function SelectWithInputField(props) {
6907
6971
  },
6908
6972
  onBlur: (e) => {
6909
6973
  const normalized = normalizeInput(e.target.value.normalize("NFKC"));
6910
- handleInputChange(normalized);
6974
+ updateCombined(selectedPrefix, normalized);
6911
6975
  onBlur();
6912
6976
  }
6913
6977
  }
@@ -6919,7 +6983,10 @@ function SwitchField(props) {
6919
6983
  field: { onChange, value },
6920
6984
  config
6921
6985
  } = props;
6922
- const { disabled = false, className = "" } = config;
6986
+ const { disabled = false, readOnly = false, className = "" } = config;
6987
+ if (readOnly) {
6988
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: value ? "\uCF1C\uC9D0" : "\uAEBC\uC9D0" }) });
6989
+ }
6923
6990
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(BaseSwitch, { checked: Boolean(value), onCheckedChange: onChange, disabled }) });
6924
6991
  }
6925
6992
  function TextareaField(props) {
@@ -6932,6 +6999,9 @@ function TextareaField(props) {
6932
6999
  const handleChange = (e) => {
6933
7000
  onChange(e.target.value);
6934
7001
  };
7002
+ if (readOnly) {
7003
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: cn2(FIELD_READONLY_TEXT_CLASS, "whitespace-pre-wrap"), children: String(value ?? "") || "\u2014" }) });
7004
+ }
6935
7005
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(
6936
7006
  Textarea,
6937
7007
  {
@@ -7177,6 +7247,7 @@ function rowPropsToFieldConfig(props) {
7177
7247
  options,
7178
7248
  className,
7179
7249
  allowDecimal,
7250
+ numbersOnly,
7180
7251
  canReset,
7181
7252
  datePosition,
7182
7253
  disableFutureDates,
@@ -7206,6 +7277,7 @@ function rowPropsToFieldConfig(props) {
7206
7277
  options,
7207
7278
  className,
7208
7279
  allowDecimal,
7280
+ numbersOnly,
7209
7281
  canReset,
7210
7282
  datePosition,
7211
7283
  disableFutureDates,