@farmzone/fz-react-ui 1.0.0 → 1.0.2

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.js CHANGED
@@ -909,7 +909,7 @@ var SELECT_BORDER_COLORS = {
909
909
  };
910
910
  function Select2(props) {
911
911
  const {
912
- containerClass,
912
+ containerClassName,
913
913
  options,
914
914
  placeholder = "\uC120\uD0DD\uD574 \uC8FC\uC138\uC694",
915
915
  triggerClassName,
@@ -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", containerClassName ?? "w-30"), children: [
943
943
  label && /* @__PURE__ */ jsx(
944
944
  "label",
945
945
  {
@@ -1022,6 +1022,7 @@ function Radio(props) {
1022
1022
  checked,
1023
1023
  onChange,
1024
1024
  disabled = false,
1025
+ disabledMessage,
1025
1026
  checkColor = DEFAULT_CHECK_COLOR,
1026
1027
  unCheckColor = DEFAULT_UNCHECK_COLOR,
1027
1028
  labelClassName,
@@ -1070,23 +1071,29 @@ function Radio(props) {
1070
1071
  }
1071
1072
  )
1072
1073
  ] });
1074
+ const withTooltip = (node) => {
1075
+ if (!disabled || !disabledMessage) return node;
1076
+ return /* @__PURE__ */ jsx(Tooltip2, { content: disabledMessage, position: "bottom", asChild: true, sideOffset: 4, children: /* @__PURE__ */ jsx("span", { className: "inline-flex", children: node }) });
1077
+ };
1073
1078
  if (onChange !== void 0 && checked !== void 0) {
1074
- return /* @__PURE__ */ jsx(
1075
- RadioGroupPrimitive.Root,
1076
- {
1077
- name,
1078
- value: checked ? value : "",
1079
- onValueChange: (newValue) => {
1080
- if (newValue === value) {
1081
- onChange(value);
1082
- }
1083
- },
1084
- disabled,
1085
- children: control
1086
- }
1079
+ return withTooltip(
1080
+ /* @__PURE__ */ jsx(
1081
+ RadioGroupPrimitive.Root,
1082
+ {
1083
+ name,
1084
+ value: checked ? value : "",
1085
+ onValueChange: (newValue) => {
1086
+ if (newValue === value) {
1087
+ onChange(value);
1088
+ }
1089
+ },
1090
+ disabled,
1091
+ children: control
1092
+ }
1093
+ )
1087
1094
  );
1088
1095
  }
1089
- return control;
1096
+ return withTooltip(control);
1090
1097
  }
1091
1098
  function RadioGroup(props) {
1092
1099
  const {
@@ -1122,6 +1129,7 @@ function RadioGroup(props) {
1122
1129
  value: option.value,
1123
1130
  label: option.label,
1124
1131
  disabled: disabled || option.disabled,
1132
+ disabledMessage: option.disabled ? option.disabledMessage : void 0,
1125
1133
  checkColor,
1126
1134
  unCheckColor,
1127
1135
  labelClassName,
@@ -1926,6 +1934,28 @@ function parseLocalYmd(ymd) {
1926
1934
  if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) return null;
1927
1935
  return dt;
1928
1936
  }
1937
+ function canSelectToday(options) {
1938
+ const today = options.now ?? /* @__PURE__ */ new Date();
1939
+ const todayYmd = formatDate(today);
1940
+ if (options.maxDate && isCalendarDayAfterMax(today, options.maxDate)) {
1941
+ return false;
1942
+ }
1943
+ if (!options.range) {
1944
+ return true;
1945
+ }
1946
+ if (options.isSelectingEnd) {
1947
+ const start = options.rangeStart?.trim() ?? "";
1948
+ if (start.length === 10 && todayYmd < start) {
1949
+ return false;
1950
+ }
1951
+ return true;
1952
+ }
1953
+ const end = options.rangeEnd?.trim() ?? "";
1954
+ if (end.length === 10 && todayYmd > end) {
1955
+ return false;
1956
+ }
1957
+ return true;
1958
+ }
1929
1959
  function isCalendarDayAfterMax(date, maxYmd) {
1930
1960
  const max = parseLocalYmd(maxYmd);
1931
1961
  if (!max) return false;
@@ -1982,6 +2012,105 @@ function validateAndFormatInput(input) {
1982
2012
  }
1983
2013
  return `${year}-${month}-${day}`;
1984
2014
  }
2015
+
2016
+ // src/components/DatePicker/calendar/config/default.ts
2017
+ var DEFAULT_CALENDAR_YEAR_HALF_SPAN = 25;
2018
+ var CALENDAR_WEEKDAY_LABELS = ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"];
2019
+ var CALENDAR_RANGE_HINT = {
2020
+ start: "\uC2DC\uC791 \uB0A0\uC9DC\uB97C \uC120\uD0DD\uD558\uC138\uC694",
2021
+ end: "\uC885\uB8CC \uB0A0\uC9DC\uB97C \uC120\uD0DD\uD558\uC138\uC694"
2022
+ };
2023
+ var CALENDAR_PORTAL_Z_INDEX = Z_INDEX.CALENDAR_PORTAL;
2024
+ var CALENDAR_SELECT_CLASSNAMES = {
2025
+ containerClass: "w-20 shrink-0",
2026
+ triggerClassName: "h-8 px-2 py-1 text-sm cursor-pointer",
2027
+ contentClassName: "min-w-[5rem]"
2028
+ };
2029
+ var CALENDAR_PANEL_BASE_CLASS = "bg-white border border-gray-300 rounded-lg shadow-lg p-3 min-w-[280px]";
2030
+ var CALENDAR_PANEL_ANCHORED_CLASS = `${CALENDAR_PANEL_BASE_CLASS} absolute z-50`;
2031
+ var YEAR_SELECT_CONTENT_EXTRA_CLASS = "year-select-content custom-view-scrollbar max-h-50 overflow-y-auto";
2032
+ var YEAR_SELECT_OPEN_CONTENT_SELECTOR = ".year-select-content[data-state='open']";
2033
+ var SELECT_ITEM_CHECKED_SELECTOR = "[data-slot='select-item'][data-state='checked']";
2034
+
2035
+ // src/components/DatePicker/calendar/config/calendar.ts
2036
+ function parseYmdDateString(value) {
2037
+ if (value.length !== 10) return null;
2038
+ return parseLocalYmd(value);
2039
+ }
2040
+ function resolveCalendarYearBounds(anchorYear, calendarYearRange) {
2041
+ const fallbackMin = anchorYear - DEFAULT_CALENDAR_YEAR_HALF_SPAN;
2042
+ const fallbackMax = anchorYear + DEFAULT_CALENDAR_YEAR_HALF_SPAN;
2043
+ const rawMin = calendarYearRange?.minYear?.trim();
2044
+ const rawMax = calendarYearRange?.maxYear?.trim();
2045
+ if (rawMin === void 0 || rawMin === "" || rawMax === void 0 || rawMax === "") {
2046
+ return { minYear: fallbackMin, maxYear: fallbackMax };
2047
+ }
2048
+ const minParsed = Number.parseInt(rawMin, 10);
2049
+ const maxParsed = Number.parseInt(rawMax, 10);
2050
+ if (Number.isNaN(minParsed) || Number.isNaN(maxParsed) || minParsed > maxParsed) {
2051
+ return { minYear: fallbackMin, maxYear: fallbackMax };
2052
+ }
2053
+ return { minYear: minParsed, maxYear: maxParsed };
2054
+ }
2055
+ function buildCalendarYearSelectOptions(maxDate, anchorYear = (/* @__PURE__ */ new Date()).getFullYear(), calendarYearRange) {
2056
+ const { minYear, maxYear } = resolveCalendarYearBounds(anchorYear, calendarYearRange);
2057
+ const maxSelectableYear = maxDate ? parseLocalYmd(maxDate)?.getFullYear() ?? null : null;
2058
+ const options = [];
2059
+ for (let y = maxYear; y >= minYear; y--) {
2060
+ options.push({
2061
+ value: String(y),
2062
+ label: `${y}\uB144`,
2063
+ disabled: maxSelectableYear !== null && y > maxSelectableYear
2064
+ });
2065
+ }
2066
+ return options;
2067
+ }
2068
+ function buildCalendarMonthSelectOptions(viewYear, maxDate) {
2069
+ const maxDateParsed = maxDate ? parseLocalYmd(maxDate) : null;
2070
+ const maxYear = maxDateParsed?.getFullYear() ?? null;
2071
+ const maxMonth = maxDateParsed !== null && maxDateParsed !== void 0 ? maxDateParsed.getMonth() + 1 : null;
2072
+ const isMaxYear = maxYear !== null && viewYear === maxYear;
2073
+ const options = [];
2074
+ for (let m = 1; m <= 12; m++) {
2075
+ options.push({
2076
+ value: String(m),
2077
+ label: `${m}\uC6D4`,
2078
+ disabled: isMaxYear && maxMonth !== null && m > maxMonth
2079
+ });
2080
+ }
2081
+ return options;
2082
+ }
2083
+ function getCalendarMonthGrid(year, monthIndex) {
2084
+ const firstDay = new Date(year, monthIndex, 1);
2085
+ const daysInMonth = new Date(year, monthIndex + 1, 0).getDate();
2086
+ const leadingEmpty = firstDay.getDay();
2087
+ const totalCells = leadingEmpty + daysInMonth;
2088
+ const trailingEmpty = totalCells % 7 === 0 ? 0 : 7 - totalCells % 7;
2089
+ return { leadingEmpty, daysInMonth, trailingEmpty };
2090
+ }
2091
+ function disableNextCalendarMonth(year, monthIndex, maxDate) {
2092
+ if (maxDate === void 0) return false;
2093
+ return isCalendarDayAfterMax(new Date(year, monthIndex + 1, 1), maxDate);
2094
+ }
2095
+ function scrollOpenYearSelectCheckedIntoView() {
2096
+ requestAnimationFrame(() => {
2097
+ const root2 = document.querySelector(YEAR_SELECT_OPEN_CONTENT_SELECTOR);
2098
+ if (!(root2 instanceof HTMLElement)) return;
2099
+ const checkedItem = root2.querySelector(SELECT_ITEM_CHECKED_SELECTOR);
2100
+ if (!(checkedItem instanceof HTMLElement)) return;
2101
+ checkedItem.scrollIntoView({ block: "start" });
2102
+ });
2103
+ }
2104
+ function dayCellButtonClass(opts) {
2105
+ const { isToday, isSelected, isInRange, isRangeStart, isRangeEnd, interactiveDisabled } = opts;
2106
+ const parts = ["w-8 h-8 text-sm flex items-center justify-center rounded-2xl transition-colors"];
2107
+ if (isToday) parts.push("bg-gray-100");
2108
+ if (isSelected && !isToday) parts.push("bg-neutral-200 text-gray-700");
2109
+ if (isInRange && !isToday) parts.push("bg-gray-100/90");
2110
+ if ((isRangeStart || isRangeEnd) && !isToday) parts.push("bg-neutral-300 text-gray-700 opacity-100");
2111
+ parts.push(interactiveDisabled ? "opacity-20 cursor-not-allowed" : "hover:bg-blue-100 cursor-pointer");
2112
+ return parts.filter(Boolean).join(" ").trim();
2113
+ }
1985
2114
  function RenderCalendar(props) {
1986
2115
  const {
1987
2116
  currentDate,
@@ -1996,130 +2125,163 @@ function RenderCalendar(props) {
1996
2125
  goToNextMonth,
1997
2126
  onYearChange,
1998
2127
  onMonthChange,
2128
+ onSelectOpenChange,
1999
2129
  editDateSelect = true,
2000
- datePosition = "bottom"
2130
+ datePosition = "bottom",
2131
+ maxDate,
2132
+ calendarYearRange,
2133
+ calendarSelectContainerClass,
2134
+ floating = false,
2135
+ showTodayButton = true
2001
2136
  } = props;
2002
2137
  const year = currentDate.getFullYear();
2003
- const month = currentDate.getMonth();
2004
- const firstDay = new Date(year, month, 1);
2005
- const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
2006
- const yearOptions = [];
2007
- for (let i = currentYear - 25; i <= currentYear + 20; i++) {
2008
- yearOptions.push({ value: i.toString(), label: `${i}\uB144` });
2009
- }
2010
- const monthOptions = [];
2011
- for (let i = 1; i <= 12; i++) {
2012
- monthOptions.push({ value: i.toString(), label: `${i}\uC6D4` });
2013
- }
2014
- const lastDay = new Date(year, month + 1, 0);
2015
- const startDate = new Date(firstDay);
2016
- startDate.setDate(startDate.getDate() - firstDay.getDay());
2017
- const endDate = new Date(lastDay);
2018
- endDate.setDate(endDate.getDate() + (6 - endDate.getDay()));
2019
- const totalDays = Math.ceil((endDate.getTime() - startDate.getTime()) / (1e3 * 60 * 60 * 24)) + 1;
2138
+ const monthIndex = currentDate.getMonth();
2139
+ const anchorYear = (/* @__PURE__ */ new Date()).getFullYear();
2140
+ const disableNextMonth = disableNextCalendarMonth(year, monthIndex, maxDate);
2141
+ const { leadingEmpty, daysInMonth, trailingEmpty } = getCalendarMonthGrid(year, monthIndex);
2142
+ const yearOptions = buildCalendarYearSelectOptions(maxDate, anchorYear, calendarYearRange);
2143
+ const monthOptions = buildCalendarMonthSelectOptions(year, maxDate);
2144
+ const todayFormatted = formatDate(/* @__PURE__ */ new Date());
2145
+ const selectedSingleYmd = range ? null : inputValue;
2146
+ const rangeStartYmd = rangeStart.trim();
2147
+ const rangeEndYmd = rangeEnd.trim();
2020
2148
  const days = [];
2021
- const currentDateFormatted = formatDate(/* @__PURE__ */ new Date());
2022
- const selectedDate = range ? null : inputValue;
2023
- const handleStopPropagation = (e) => e.stopPropagation();
2024
- const handleYearChange = (value) => onYearChange(parseInt(value));
2025
- const handleMonthChange = (value) => onMonthChange(parseInt(value) - 1);
2026
- for (let i = 0; i < totalDays; i++) {
2027
- const date = new Date(startDate);
2028
- date.setDate(startDate.getDate() + i);
2149
+ for (let i = 0; i < leadingEmpty; i++) {
2150
+ days.push(/* @__PURE__ */ jsx("div", { className: "size-8", "aria-hidden": true }, `lead-${i}`));
2151
+ }
2152
+ for (let d = 1; d <= daysInMonth; d++) {
2153
+ const date = new Date(year, monthIndex, d);
2029
2154
  const dateFormatted = formatDate(date);
2030
- const isToday = dateFormatted === currentDateFormatted;
2031
- const isSelected = dateFormatted === selectedDate;
2155
+ const isToday = dateFormatted === todayFormatted;
2156
+ const isSelected = dateFormatted === selectedSingleYmd;
2032
2157
  const isInRange = isDateInRange(date, range, rangeStart, rangeEnd);
2033
- const isRangeStart = range && dateFormatted === rangeStart;
2034
- const isRangeEnd = range && dateFormatted === rangeEnd;
2035
- const isCurrentMonth = date.getMonth() === month;
2036
- const rangeStartDate = new Date(rangeStart);
2037
- rangeStartDate.setDate(rangeStartDate.getDate() - 1);
2038
- const isDisabled = range && isSelectingEnd && rangeStart && date < rangeStartDate;
2158
+ const isRangeStartFlag = Boolean(range && dateFormatted === rangeStart);
2159
+ const isRangeEndFlag = Boolean(range && dateFormatted === rangeEnd);
2160
+ const isBeforeRangeStart = range && isSelectingEnd && rangeStartYmd.length === 10 && dateFormatted < rangeStartYmd;
2161
+ const isAfterRangeEnd = range && !isSelectingEnd && rangeEndYmd.length === 10 && dateFormatted > rangeEndYmd;
2162
+ const isGreyedForRange = Boolean(isBeforeRangeStart || isAfterRangeEnd);
2163
+ const isPastMax = maxDate ? isCalendarDayAfterMax(date, maxDate) : false;
2039
2164
  days.push(
2040
2165
  /* @__PURE__ */ jsx(
2041
2166
  "button",
2042
2167
  {
2168
+ type: "button",
2043
2169
  onClick: () => handleDateSelect(date),
2044
- disabled: isDisabled || false,
2045
- className: `
2046
- w-8 h-8 text-sm flex items-center justify-center rounded-2xl transition-colors
2047
- ${!isCurrentMonth ? "text-gray-300" : ""}
2048
- ${isToday ? "bg-gray-100 " : ""}
2049
- ${isInRange && !isToday ? "bg-gray-100/90" : ""}
2050
- ${isSelected && !isToday ? "bg-neutral-200 text-gray-700" : ""}
2051
- ${(isRangeStart || isRangeEnd) && !isToday ? "bg-neutral-300 text-gray-700 opacity-100" : ""}
2052
- ${isDisabled ? "opacity-20 cursor-not-allowed" : "hover:bg-blue-100 cursor-pointer"}
2053
- `,
2054
- children: date.getDate()
2170
+ disabled: isGreyedForRange || isPastMax,
2171
+ className: dayCellButtonClass({
2172
+ isToday,
2173
+ isSelected: Boolean(isSelected),
2174
+ isInRange,
2175
+ isRangeStart: isRangeStartFlag,
2176
+ isRangeEnd: isRangeEndFlag,
2177
+ interactiveDisabled: isGreyedForRange || isPastMax
2178
+ }),
2179
+ children: d
2055
2180
  },
2056
- i
2181
+ d
2057
2182
  )
2058
2183
  );
2059
2184
  }
2060
- const calendarPositionClass = range && calendarPosition === "end" ? "right-0" : "left-0";
2061
- const datePositionClass = datePosition === "top" ? "bottom-full mb-1" : "top-full mt-1";
2062
- return /* @__PURE__ */ jsxs(
2063
- "div",
2064
- {
2065
- className: `${datePositionClass} ${calendarPositionClass} bg-white border border-gray-300 rounded-lg shadow-lg p-3 z-50 min-w-[280px]`,
2066
- onClick: handleStopPropagation,
2067
- onMouseDown: handleStopPropagation,
2068
- children: [
2069
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [
2070
- /* @__PURE__ */ jsx("button", { onClick: goToPreviousMonth, className: "p-1 hover:bg-gray-100 rounded cursor-pointer", children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4 text-balck" }) }),
2071
- editDateSelect ? /* @__PURE__ */ jsxs(
2072
- "div",
2073
- {
2074
- className: "flex items-center gap-2",
2075
- onClick: handleStopPropagation,
2076
- onMouseDown: handleStopPropagation,
2077
- children: [
2078
- /* @__PURE__ */ jsx("div", { onClick: handleStopPropagation, onMouseDown: handleStopPropagation, children: /* @__PURE__ */ jsx(
2079
- Select2,
2080
- {
2081
- value: year.toString(),
2082
- options: yearOptions,
2083
- onChange: handleYearChange,
2084
- className: "cursor-pointer"
2085
- }
2086
- ) }),
2087
- /* @__PURE__ */ jsx("div", { onClick: handleStopPropagation, onMouseDown: handleStopPropagation, children: /* @__PURE__ */ jsx(
2088
- Select2,
2089
- {
2090
- value: (month + 1).toString(),
2091
- options: monthOptions,
2092
- onChange: handleMonthChange,
2093
- className: " cursor-pointer"
2094
- }
2095
- ) })
2096
- ]
2097
- }
2098
- ) : /* @__PURE__ */ jsxs("h3", { className: "text-lg font-semibold", children: [
2099
- year,
2100
- "\uB144 ",
2101
- month + 1,
2102
- "\uC6D4"
2103
- ] }),
2104
- /* @__PURE__ */ jsx("button", { onClick: goToNextMonth, className: "p-1 hover:bg-gray-100 rounded cursor-pointer", children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" }) })
2105
- ] }),
2106
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1 mb-1", children: ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"].map((day) => /* @__PURE__ */ jsx("div", { className: "text-center text-sm font-medium text-gray-500 py-2 ", children: day }, day)) }),
2107
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: days })
2108
- ]
2109
- }
2110
- );
2111
- }
2112
-
2113
- // src/components/DatePicker/calendar/config/default.ts
2114
- var CALENDAR_PORTAL_Z_INDEX = Z_INDEX.CALENDAR_PORTAL;
2115
-
2116
- // src/components/DatePicker/calendar/config/calendar.ts
2117
- function parseYmdDateString(value) {
2118
- if (value.length !== 10) return null;
2119
- return parseLocalYmd(value);
2185
+ for (let i = 0; i < trailingEmpty; i++) {
2186
+ days.push(/* @__PURE__ */ jsx("div", { className: "size-8", "aria-hidden": true }, `trail-${i}`));
2187
+ }
2188
+ const calendarPositionClass = !floating && range && calendarPosition === "end" ? "right-0" : !floating ? "left-0" : "";
2189
+ const datePositionClass = floating ? "" : datePosition === "top" ? "bottom-full mb-1" : "top-full mt-1";
2190
+ const panelClass = floating ? CALENDAR_PANEL_BASE_CLASS : cn(CALENDAR_PANEL_ANCHORED_CLASS, datePositionClass, calendarPositionClass);
2191
+ const handleYearSelectOpenChange = (open) => {
2192
+ onSelectOpenChange?.(open);
2193
+ if (open) scrollOpenYearSelectCheckedIntoView();
2194
+ };
2195
+ const sel = CALENDAR_SELECT_CLASSNAMES;
2196
+ const containerClass = cn(sel.containerClass, calendarSelectContainerClass);
2197
+ const stopBubble = (e) => e.stopPropagation();
2198
+ const isTodaySelectable = canSelectToday({
2199
+ maxDate,
2200
+ range,
2201
+ isSelectingEnd,
2202
+ rangeStart,
2203
+ rangeEnd
2204
+ });
2205
+ return /* @__PURE__ */ jsxs("div", { className: panelClass, onClick: stopBubble, onMouseDown: stopBubble, children: [
2206
+ /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
2207
+ /* @__PURE__ */ jsx(
2208
+ "button",
2209
+ {
2210
+ type: "button",
2211
+ onClick: goToPreviousMonth,
2212
+ className: "cursor-pointer rounded p-1 hover:bg-gray-100",
2213
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
2214
+ }
2215
+ ),
2216
+ editDateSelect ? /* @__PURE__ */ jsxs(
2217
+ "div",
2218
+ {
2219
+ role: "presentation",
2220
+ className: "flex items-center gap-2",
2221
+ onClick: stopBubble,
2222
+ onMouseDown: stopBubble,
2223
+ children: [
2224
+ /* @__PURE__ */ jsx(
2225
+ Select2,
2226
+ {
2227
+ value: year.toString(),
2228
+ options: yearOptions,
2229
+ onChange: (value) => onYearChange(Number.parseInt(value, 10)),
2230
+ containerClassName: containerClass,
2231
+ triggerClassName: sel.triggerClassName,
2232
+ contentClassName: cn(YEAR_SELECT_CONTENT_EXTRA_CLASS, sel.contentClassName),
2233
+ disableScrollButtonEvents: true,
2234
+ onOpenChange: handleYearSelectOpenChange
2235
+ }
2236
+ ),
2237
+ /* @__PURE__ */ jsx(
2238
+ Select2,
2239
+ {
2240
+ value: (monthIndex + 1).toString(),
2241
+ options: monthOptions,
2242
+ onChange: (value) => onMonthChange(Number.parseInt(value, 10) - 1),
2243
+ containerClassName: containerClass,
2244
+ triggerClassName: sel.triggerClassName,
2245
+ contentClassName: sel.contentClassName,
2246
+ onOpenChange: (open) => onSelectOpenChange?.(open)
2247
+ }
2248
+ )
2249
+ ]
2250
+ }
2251
+ ) : /* @__PURE__ */ jsxs("h3", { className: "text-lg font-semibold", children: [
2252
+ year,
2253
+ "\uB144 ",
2254
+ monthIndex + 1,
2255
+ "\uC6D4"
2256
+ ] }),
2257
+ /* @__PURE__ */ jsx(
2258
+ "button",
2259
+ {
2260
+ type: "button",
2261
+ onClick: goToNextMonth,
2262
+ disabled: disableNextMonth,
2263
+ className: cn(
2264
+ "rounded p-1",
2265
+ disableNextMonth ? "cursor-not-allowed opacity-30" : "cursor-pointer hover:bg-gray-100"
2266
+ ),
2267
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
2268
+ }
2269
+ )
2270
+ ] }),
2271
+ /* @__PURE__ */ jsx("div", { className: "mb-1 grid grid-cols-7 gap-1", children: CALENDAR_WEEKDAY_LABELS.map((day) => /* @__PURE__ */ jsx("div", { className: "py-2 text-center text-sm font-medium text-gray-500", children: day }, day)) }),
2272
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: days }),
2273
+ range && /* @__PURE__ */ jsx("div", { className: "mt-3 text-sm text-gray-600", children: /* @__PURE__ */ jsx("p", { className: "text-center", children: isSelectingEnd ? CALENDAR_RANGE_HINT.end : CALENDAR_RANGE_HINT.start }) }),
2274
+ showTodayButton && isTodaySelectable && /* @__PURE__ */ jsx("div", { className: "-mx-3 -mb-3 mt-3 flex items-center justify-center border-t border-gray-200 py-2", children: /* @__PURE__ */ jsx(
2275
+ "button",
2276
+ {
2277
+ type: "button",
2278
+ onClick: () => handleDateSelect(/* @__PURE__ */ new Date()),
2279
+ className: "cursor-pointer text-sm text-gray-800 hover:text-main",
2280
+ children: "Today"
2281
+ }
2282
+ ) })
2283
+ ] });
2120
2284
  }
2121
-
2122
- // src/components/DatePicker/calendar/hooks/useCalendarState.ts
2123
2285
  function useCalendarState(props) {
2124
2286
  const { datePosition = "bottom", disabled = false, calendarRef } = props;
2125
2287
  const [isCalendarOpen, setIsCalendarOpen] = useState(false);
@@ -2216,11 +2378,15 @@ function useFloatingCalendarPosition(anchorRef, isOpen2, placement, align = "sta
2216
2378
  setStyle({ ...base, top: rect.bottom + CALENDAR_GAP });
2217
2379
  }
2218
2380
  };
2381
+ const handleScroll = (e) => {
2382
+ if (e.target?.closest?.('[data-slot="select-content"]')) return;
2383
+ update();
2384
+ };
2219
2385
  update();
2220
- window.addEventListener("scroll", update, true);
2386
+ window.addEventListener("scroll", handleScroll, true);
2221
2387
  window.addEventListener("resize", update);
2222
2388
  return () => {
2223
- window.removeEventListener("scroll", update, true);
2389
+ window.removeEventListener("scroll", handleScroll, true);
2224
2390
  window.removeEventListener("resize", update);
2225
2391
  };
2226
2392
  }, [isOpen2, placement, align, anchorRef]);
@@ -2258,6 +2424,7 @@ function DatePickerRange(props) {
2258
2424
  disabled = false,
2259
2425
  maxDate,
2260
2426
  calendarYearRange,
2427
+ calendarSelectContainerClass,
2261
2428
  showTodayButton = true
2262
2429
  } = props;
2263
2430
  const [inputs, setInputs] = useState({ start: startValue, end: endValue });
@@ -2394,6 +2561,7 @@ function DatePickerRange(props) {
2394
2561
  datePosition: autoDatePosition,
2395
2562
  maxDate,
2396
2563
  calendarYearRange,
2564
+ calendarSelectContainerClass,
2397
2565
  showTodayButton
2398
2566
  }
2399
2567
  )
@@ -2473,6 +2641,7 @@ function DatePickerSingle(props) {
2473
2641
  disabled = false,
2474
2642
  maxDate,
2475
2643
  calendarYearRange,
2644
+ calendarSelectContainerClass,
2476
2645
  onBlur,
2477
2646
  showTodayButton = true
2478
2647
  } = props;
@@ -2565,6 +2734,7 @@ function DatePickerSingle(props) {
2565
2734
  datePosition: autoDatePosition,
2566
2735
  maxDate,
2567
2736
  calendarYearRange,
2737
+ calendarSelectContainerClass,
2568
2738
  showTodayButton
2569
2739
  }
2570
2740
  )
@@ -2623,6 +2793,7 @@ function DatePicker(props) {
2623
2793
  disabled,
2624
2794
  maxDate,
2625
2795
  calendarYearRange,
2796
+ calendarSelectContainerClass,
2626
2797
  showTodayButton
2627
2798
  } = props;
2628
2799
  if (isRange) {
@@ -2640,6 +2811,7 @@ function DatePicker(props) {
2640
2811
  disabled,
2641
2812
  maxDate,
2642
2813
  calendarYearRange,
2814
+ calendarSelectContainerClass,
2643
2815
  showTodayButton
2644
2816
  }
2645
2817
  );
@@ -2659,6 +2831,7 @@ function DatePicker(props) {
2659
2831
  disabled,
2660
2832
  maxDate,
2661
2833
  calendarYearRange,
2834
+ calendarSelectContainerClass,
2662
2835
  showTodayButton,
2663
2836
  onBlur
2664
2837
  }
@@ -3524,7 +3697,7 @@ function TableFooter(props) {
3524
3697
  const { paginationInfo, renderLeft, renderRight } = props;
3525
3698
  return /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between py-4 min-h-16", children: [
3526
3699
  /* @__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 }) }),
3700
+ paginationInfo && /* @__PURE__ */ jsx("div", { className: "px-5", children: /* @__PURE__ */ jsx(Pagination, { ...paginationInfo }) }),
3528
3701
  /* @__PURE__ */ jsx("div", { className: "flex justify-end flex-1 min-w-0", children: renderRight })
3529
3702
  ] }) });
3530
3703
  }
@@ -3680,7 +3853,7 @@ function useTableFixed(columns, isHeader = false) {
3680
3853
  window.addEventListener("resize", onResize);
3681
3854
  const firstKey = columns[0]?.key;
3682
3855
  const cell = firstKey ? document.getElementById(`${firstKey}${isHeader ? "-header" : "-body"}`) : null;
3683
- const tableEl = cell?.closest("table") || document.querySelector("table.table-layout");
3856
+ const tableEl = cell?.closest("table") || document.querySelector("table.table-fixed");
3684
3857
  if (tableEl && "ResizeObserver" in window) {
3685
3858
  resizeObserverRef.current = new ResizeObserver(handleResize);
3686
3859
  resizeObserverRef.current.observe(tableEl);
@@ -3767,19 +3940,14 @@ function TableHeader(props) {
3767
3940
  const columns = useMemo(() => {
3768
3941
  return customColumns.map((column) => {
3769
3942
  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";
3943
+ const isCheckbox = key === "__checkbox__";
3944
+ return {
3945
+ ...column,
3946
+ width: isCheckbox ? "40px" : column.width,
3947
+ minWidth: isCheckbox ? "40px" : column.minWidth ?? column.width,
3948
+ maxWidth: isCheckbox ? "40px" : column.maxWidth,
3949
+ align: isCheckbox ? "center" : column.align ?? "left"
3781
3950
  };
3782
- return { ...column, width: getWidth(), align: getAlign() };
3783
3951
  });
3784
3952
  }, [customColumns]);
3785
3953
  const { getColumnFixedStyle } = useTableFixed(columns, true);
@@ -4252,11 +4420,11 @@ function SkeletonTd(props) {
4252
4420
  return /* @__PURE__ */ jsx(
4253
4421
  "td",
4254
4422
  {
4255
- className: "px-2 py-[6.8px] whitespace-nowrap text-sm border-r border-b border-gray-200/70 last:border-r-0",
4423
+ 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
4424
  style: {
4257
4425
  width: col.width,
4258
4426
  minWidth: col.minWidth || col.width,
4259
- maxWidth: col.maxWidth || col.width
4427
+ maxWidth: col.key === "__checkbox__" ? col.maxWidth || col.width : void 0
4260
4428
  },
4261
4429
  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
4430
  }
@@ -4337,7 +4505,7 @@ function getAlignStyle(align) {
4337
4505
  return align === "left" ? "flex-start" : align === "center" ? "center" : "flex-end";
4338
4506
  }
4339
4507
  function applyColDefaults(col) {
4340
- const width = col.key === "__checkbox__" ? "40px" : col.width ?? col.minWidth ?? "100px";
4508
+ const width = col.key === "__checkbox__" ? "40px" : col.width ?? col.minWidth;
4341
4509
  const align = col.key === "__checkbox__" ? "center" : col.align ?? "left";
4342
4510
  return { ...col, width, align };
4343
4511
  }
@@ -4786,17 +4954,18 @@ function Table(props) {
4786
4954
  const { width, minWidth, maxWidth, align } = col;
4787
4955
  const fixedStyle = isPinned ? getColumnFixedStyleRef.current(col) : void 0;
4788
4956
  const cellContent = col.render ? col.render(record[col.key], record, rowIdx) : String(record[col.key] ?? "");
4957
+ const isCheckbox = col.key === "__checkbox__";
4789
4958
  return /* @__PURE__ */ jsx(
4790
4959
  "td",
4791
4960
  {
4792
4961
  id: `${col.key}-body`,
4793
4962
  "data-row-cell": "true",
4794
4963
  "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",
4964
+ 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
4965
  style: {
4797
4966
  width,
4798
4967
  minWidth: minWidth || width,
4799
- maxWidth: maxWidth || width,
4968
+ maxWidth: isCheckbox ? maxWidth || width : void 0,
4800
4969
  ...fixedStyle
4801
4970
  },
4802
4971
  onClick: (event) => {
@@ -4892,7 +5061,7 @@ function Table(props) {
4892
5061
  ref: scrollRef,
4893
5062
  className: "relative w-full overflow-y-auto overflow-x-auto custom-view-scrollbar bg-white",
4894
5063
  style: { height: `${tableHeight}px` },
4895
- children: /* @__PURE__ */ jsxs("table", { className: "w-full table-layout", children: [
5064
+ children: /* @__PURE__ */ jsxs("table", { className: "w-full table-fixed", children: [
4896
5065
  /* @__PURE__ */ jsx(
4897
5066
  TableHeader,
4898
5067
  {
@@ -5992,6 +6161,7 @@ var FORM_CONTROL_CLASS = "h-8 w-full rounded border border-gray-300 bg-white px-
5992
6161
  function formControlClass(hasError, extra) {
5993
6162
  return cn2(FORM_CONTROL_CLASS, FORM_CONTROL_FOCUS_CLASS, hasError && FORM_FIELD_ERROR_BORDER_CLASS, extra);
5994
6163
  }
6164
+ var FIELD_READONLY_TEXT_CLASS = "text-sm text-gray-800 leading-tight";
5995
6165
  function fieldControlWrapClass(className) {
5996
6166
  return className ? cn2("min-w-0 shrink-0", className) : "min-w-0 w-full";
5997
6167
  }
@@ -6338,6 +6508,7 @@ function AddressField(props) {
6338
6508
  const {
6339
6509
  placeholder = "\uC8FC\uC18C\uB97C \uAC80\uC0C9\uD558\uC138\uC694",
6340
6510
  disabled = false,
6511
+ readOnly = false,
6341
6512
  className = "",
6342
6513
  addressDetailKey,
6343
6514
  addressClassNames
@@ -6352,6 +6523,9 @@ function AddressField(props) {
6352
6523
  searchButtonRef.current?.click();
6353
6524
  }
6354
6525
  }, [isFocused]);
6526
+ if (readOnly) {
6527
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: String(value ?? "") || "\u2014" }) });
6528
+ }
6355
6529
  const handleComplete = (data) => {
6356
6530
  isAddressSelectedRef.current = true;
6357
6531
  const nextAddress = data.roadAddress || data.address;
@@ -6470,12 +6644,12 @@ function CascadingSelectField(props) {
6470
6644
  }
6471
6645
  function CascadingSelectFieldInner(props) {
6472
6646
  const {
6473
- field: { onChange, value },
6647
+ field: { onChange, onBlur, value },
6474
6648
  config,
6475
6649
  hasError = false,
6476
6650
  cascadingSelect
6477
6651
  } = props;
6478
- const { disabled = false, className = "", cascadingSelectClassNames } = config;
6652
+ const { disabled = false, readOnly = false, className = "", cascadingSelectClassNames } = config;
6479
6653
  const {
6480
6654
  primaryOptions,
6481
6655
  getSecondaryOptions,
@@ -6489,10 +6663,17 @@ function CascadingSelectFieldInner(props) {
6489
6663
  [formValue, getSecondaryOptions, separator]
6490
6664
  );
6491
6665
  useLayoutEffect(() => {
6666
+ if (readOnly) return;
6492
6667
  if (normalizedFormValue !== formValue) {
6493
6668
  onChange(normalizedFormValue);
6494
6669
  }
6495
- }, [formValue, normalizedFormValue, onChange]);
6670
+ }, [formValue, normalizedFormValue, onChange, readOnly]);
6671
+ if (readOnly) {
6672
+ const primaryLabel = primaryOptions.find((o) => String(o.value) === primary)?.label ?? primary;
6673
+ const secondaryLabel = secondary ? secondaryOptions.find((o) => String(o.value) === secondary)?.label ?? secondary : "";
6674
+ const displayText = secondaryLabel ? `${primaryLabel} ${secondaryLabel}` : primaryLabel || "\u2014";
6675
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: displayText }) });
6676
+ }
6496
6677
  const updateValue = (nextPrimary, nextSecondary) => {
6497
6678
  onChange(createCascadingSelectValue(nextPrimary, nextSecondary, separator));
6498
6679
  };
@@ -6513,11 +6694,12 @@ function CascadingSelectFieldInner(props) {
6513
6694
  disabled,
6514
6695
  hasError,
6515
6696
  options: toSelectOptions(primaryOptions),
6516
- containerClass: firstSelectClass.containerClassName,
6697
+ containerClassName: firstSelectClass.containerClassName,
6517
6698
  triggerClassName: firstSelectClass.triggerClassName,
6518
6699
  canReset: true,
6519
6700
  onChange: (nextPrimary) => {
6520
6701
  updateValue(nextPrimary, "");
6702
+ if (!nextPrimary) onBlur();
6521
6703
  }
6522
6704
  }
6523
6705
  ),
@@ -6529,11 +6711,12 @@ function CascadingSelectFieldInner(props) {
6529
6711
  disabled: disabled || !primary || secondaryOptions.length === 0,
6530
6712
  hasError,
6531
6713
  options: toSelectOptions(secondaryOptions),
6532
- containerClass: secondSelectClass.containerClassName,
6714
+ containerClassName: secondSelectClass.containerClassName,
6533
6715
  triggerClassName: secondSelectClass.triggerClassName,
6534
6716
  canReset: true,
6535
6717
  onChange: (nextSecondary) => {
6536
6718
  updateValue(primary, nextSecondary);
6719
+ onBlur();
6537
6720
  }
6538
6721
  }
6539
6722
  )
@@ -6544,7 +6727,11 @@ function CheckboxField(props) {
6544
6727
  field: { onChange, value },
6545
6728
  config
6546
6729
  } = props;
6547
- const { disabled = false, checkboxLabel, className = "" } = config;
6730
+ const { disabled = false, readOnly = false, checkboxLabel, className = "" } = config;
6731
+ if (readOnly) {
6732
+ const text = value ? checkboxLabel ?? "\uC120\uD0DD\uB428" : "\u2014";
6733
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: text }) });
6734
+ }
6548
6735
  return /* @__PURE__ */ jsx(
6549
6736
  Checkbox,
6550
6737
  {
@@ -6565,11 +6752,16 @@ function DateField(props) {
6565
6752
  const {
6566
6753
  placeholder = "YYYY-MM-DD",
6567
6754
  disabled = false,
6755
+ readOnly = false,
6568
6756
  className = "w-40",
6569
6757
  datePosition = "bottom",
6570
- disableFutureDates = false
6758
+ disableFutureDates = false,
6759
+ calendarSelectContainerClass = "w-20"
6571
6760
  } = config;
6572
6761
  const maxDate = disableFutureDates ? formatDate(/* @__PURE__ */ new Date()) : void 0;
6762
+ if (readOnly) {
6763
+ return /* @__PURE__ */ jsx("div", { className: cn2("flex items-center", fieldControlWrapClass(className)), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: String(value ?? "") || "\u2014" }) });
6764
+ }
6573
6765
  return /* @__PURE__ */ jsx("div", { className: cn2("flex items-center", fieldControlWrapClass(className)), children: /* @__PURE__ */ jsx(
6574
6766
  DatePicker,
6575
6767
  {
@@ -6581,7 +6773,8 @@ function DateField(props) {
6581
6773
  className: "w-full",
6582
6774
  validation: hasError,
6583
6775
  datePosition,
6584
- maxDate
6776
+ maxDate,
6777
+ calendarSelectContainerClass
6585
6778
  }
6586
6779
  ) });
6587
6780
  }
@@ -6597,11 +6790,17 @@ function InputField(props) {
6597
6790
  readOnly = false,
6598
6791
  maxLength,
6599
6792
  className = "",
6600
- type = "input"
6793
+ type = "input",
6794
+ numbersOnly = false
6601
6795
  } = config;
6602
6796
  const inputType = type === "email" ? "email" : type === "password" ? "password" : "text";
6797
+ if (readOnly) {
6798
+ const text = type === "password" ? value ? "**********" : "" : String(value ?? "") || "";
6799
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: text }) });
6800
+ }
6603
6801
  const handleChange = (e) => {
6604
- onChange(e.target.value);
6802
+ const value2 = numbersOnly ? e.target.value.replace(/\D/g, "") : e.target.value;
6803
+ onChange(value2);
6605
6804
  };
6606
6805
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(
6607
6806
  Input2,
@@ -6632,7 +6831,7 @@ function InputWithButtonField(props) {
6632
6831
  placeholder = "\uC870\uD68C \uBC84\uD2BC\uC73C\uB85C \uC120\uD0DD",
6633
6832
  disabled = false,
6634
6833
  className = "",
6635
- readOnly = true,
6834
+ readOnly = false,
6636
6835
  inputWithButton,
6637
6836
  onClickInputWithButton
6638
6837
  } = config;
@@ -6641,13 +6840,16 @@ function InputWithButtonField(props) {
6641
6840
  if (disabled) return;
6642
6841
  onClickInputWithButton?.();
6643
6842
  };
6843
+ if (readOnly) {
6844
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: String(value ?? "") || "\u2014" }) });
6845
+ }
6644
6846
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsxs("div", { className: "flex w-full min-w-0 items-center gap-2", children: [
6645
6847
  /* @__PURE__ */ jsx(
6646
6848
  Input2,
6647
6849
  {
6648
6850
  placeholder,
6649
6851
  value: String(value ?? ""),
6650
- readOnly,
6852
+ readOnly: true,
6651
6853
  disabled,
6652
6854
  bare: true,
6653
6855
  className: formControlClass(hasError),
@@ -6687,6 +6889,9 @@ function NumberField(props) {
6687
6889
  }
6688
6890
  };
6689
6891
  const displayValue = value != null && value !== "" ? isMoney ? formatNumberWithCommas(value) : String(value) : "";
6892
+ if (readOnly) {
6893
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: displayValue || "\u2014" }) });
6894
+ }
6690
6895
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(
6691
6896
  Input2,
6692
6897
  {
@@ -6716,6 +6921,9 @@ function PhoneField(props) {
6716
6921
  const handleChange = (e) => {
6717
6922
  onChange(formatPhoneNumber(e.target.value));
6718
6923
  };
6924
+ if (readOnly) {
6925
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: String(value ?? "") || "\u2014" }) });
6926
+ }
6719
6927
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(
6720
6928
  Input2,
6721
6929
  {
@@ -6740,7 +6948,11 @@ function RadioField(props) {
6740
6948
  field: { onChange, value, name },
6741
6949
  config
6742
6950
  } = props;
6743
- const { disabled = false, options = [], className = "" } = config;
6951
+ const { disabled = false, readOnly = false, options = [], className = "" } = config;
6952
+ if (readOnly) {
6953
+ const label = options.find((o) => String(o.value) === String(value ?? ""))?.label ?? String(value ?? "");
6954
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: label || "\u2014" }) });
6955
+ }
6744
6956
  return /* @__PURE__ */ jsx(
6745
6957
  RadioGroup,
6746
6958
  {
@@ -6752,7 +6964,9 @@ function RadioField(props) {
6752
6964
  containerClassName: cn2("justify-start", fieldControlWrapClass(className)),
6753
6965
  options: options.map((option) => ({
6754
6966
  label: option.label,
6755
- value: String(option.value)
6967
+ value: String(option.value),
6968
+ disabled: option.disabled,
6969
+ disabledMessage: option.disabledMessage
6756
6970
  }))
6757
6971
  }
6758
6972
  );
@@ -6772,11 +6986,16 @@ function SelectField(props) {
6772
6986
  const {
6773
6987
  placeholder = "\uC120\uD0DD\uD558\uC138\uC694",
6774
6988
  disabled = false,
6989
+ readOnly = false,
6775
6990
  options = [],
6776
6991
  className = "",
6777
6992
  canReset = false
6778
6993
  } = config;
6779
6994
  const selectOptions = toSelectOptions2(options);
6995
+ if (readOnly) {
6996
+ const label = selectOptions.find((o) => o.value === String(value ?? ""))?.label ?? String(value ?? "");
6997
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: label || "\u2014" }) });
6998
+ }
6780
6999
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(
6781
7000
  Select2,
6782
7001
  {
@@ -6785,7 +7004,7 @@ function SelectField(props) {
6785
7004
  disabled,
6786
7005
  hasError,
6787
7006
  options: selectOptions,
6788
- containerClass: "flex w-full min-w-0 max-w-full items-center",
7007
+ containerClassName: "flex w-full min-w-0 max-w-full items-center",
6789
7008
  className: "w-full min-w-0",
6790
7009
  triggerClassName: cn2(
6791
7010
  "h-8 w-full min-w-0 max-w-full rounded border bg-white px-2 py-1 text-sm shadow-none",
@@ -6823,16 +7042,6 @@ function combineSelectWithInputValue(prefix, input) {
6823
7042
  return `${prefix}${input}`;
6824
7043
  }
6825
7044
  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
7045
  function SelectWithInputField(props) {
6837
7046
  const {
6838
7047
  field: { onChange, onBlur, value },
@@ -6841,37 +7050,58 @@ function SelectWithInputField(props) {
6841
7050
  } = props;
6842
7051
  const {
6843
7052
  disabled = false,
7053
+ readOnly = false,
6844
7054
  options = [],
6845
7055
  maxLength,
6846
7056
  className = "",
6847
7057
  selectWithInput,
6848
7058
  selectWithInputClassNames
6849
7059
  } = config;
6850
- const inputMode = selectWithInput?.inputMode ?? "number";
7060
+ const validationCtx = useSubmitFormValidation();
7061
+ const numbersOnly = selectWithInput?.numbersOnly ?? selectWithInput?.inputMode !== "text";
6851
7062
  const defaultPrefix = String(options[0]?.value ?? "");
6852
7063
  const numberInputRef = useRef(null);
6853
7064
  const formValue = String(value ?? "");
6854
- const { prefix, inputPart, normalizedFormValue } = useMemo(
6855
- () => resolveSelectWithInputState(formValue, defaultPrefix, options),
6856
- [defaultPrefix, formValue, options]
6857
- );
7065
+ const { prefix, inputPart, normalizedFormValue } = useMemo(() => {
7066
+ const parsed = parseSelectWithInputValue(formValue, defaultPrefix);
7067
+ const validPrefixes = new Set(options.map((opt) => String(opt.value)));
7068
+ const resolvedPrefix = validPrefixes.has(parsed.prefix) ? parsed.prefix : defaultPrefix;
7069
+ return {
7070
+ prefix: resolvedPrefix,
7071
+ inputPart: parsed.input,
7072
+ normalizedFormValue: combineSelectWithInputValue(resolvedPrefix, parsed.input)
7073
+ };
7074
+ }, [defaultPrefix, formValue, options]);
7075
+ const [selectedPrefix, setSelectedPrefix] = useState(prefix);
7076
+ const [prevFormValue, setPrevFormValue] = useState(formValue);
7077
+ if (prevFormValue !== formValue) {
7078
+ setPrevFormValue(formValue);
7079
+ setSelectedPrefix(formValue ? prefix : defaultPrefix);
7080
+ }
6858
7081
  useLayoutEffect(() => {
6859
- if (normalizedFormValue !== formValue) {
7082
+ if (readOnly) return;
7083
+ if (formValue && normalizedFormValue !== formValue) {
6860
7084
  onChange(normalizedFormValue);
6861
7085
  }
6862
- }, [formValue, normalizedFormValue, onChange]);
7086
+ }, [formValue, normalizedFormValue, onChange, readOnly]);
7087
+ if (readOnly) {
7088
+ const prefixLabel = options.find((o) => String(o.value) === prefix)?.label ?? prefix;
7089
+ const displayText = inputPart ? `${prefixLabel} ${inputPart}` : prefix ? prefixLabel : "\u2014";
7090
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: displayText || "\u2014" }) });
7091
+ }
6863
7092
  const updateCombined = (nextPrefix, nextInput) => {
6864
7093
  onChange(combineSelectWithInputValue(nextPrefix, nextInput));
6865
7094
  };
6866
7095
  const normalizeInput = (raw) => {
6867
- if (inputMode === "number") {
6868
- return raw.replace(/\D+/g, "");
6869
- }
7096
+ if (numbersOnly) return raw.replace(/\D+/g, "");
6870
7097
  return raw;
6871
7098
  };
6872
7099
  const handleInputChange = (raw) => {
6873
7100
  const normalized = normalizeInput(raw.normalize("NFKC"));
6874
- updateCombined(prefix, normalized);
7101
+ if (normalized) {
7102
+ validationCtx?.markFieldHadValue(String(config.name));
7103
+ }
7104
+ updateCombined(selectedPrefix, normalized);
6875
7105
  };
6876
7106
  const selectClass = resolveSelectClassNameProp(selectWithInputClassNames?.selectClassName, {
6877
7107
  containerClassName: "shrink-0",
@@ -6881,15 +7111,17 @@ function SelectWithInputField(props) {
6881
7111
  /* @__PURE__ */ jsx(
6882
7112
  Select2,
6883
7113
  {
6884
- value: prefix,
7114
+ value: selectedPrefix,
6885
7115
  placeholder: "\uC120\uD0DD",
6886
7116
  disabled,
6887
7117
  hasError,
6888
7118
  options: toSelectOptions(options),
6889
- containerClass: selectClass.containerClassName,
7119
+ containerClassName: selectClass.containerClassName,
6890
7120
  triggerClassName: selectClass.triggerClassName,
6891
7121
  onChange: (nextPrefix) => {
7122
+ setSelectedPrefix(nextPrefix);
6892
7123
  updateCombined(nextPrefix, inputPart);
7124
+ onBlur();
6893
7125
  }
6894
7126
  }
6895
7127
  ),
@@ -6907,7 +7139,7 @@ function SelectWithInputField(props) {
6907
7139
  },
6908
7140
  onBlur: (e) => {
6909
7141
  const normalized = normalizeInput(e.target.value.normalize("NFKC"));
6910
- handleInputChange(normalized);
7142
+ updateCombined(selectedPrefix, normalized);
6911
7143
  onBlur();
6912
7144
  }
6913
7145
  }
@@ -6919,7 +7151,10 @@ function SwitchField(props) {
6919
7151
  field: { onChange, value },
6920
7152
  config
6921
7153
  } = props;
6922
- const { disabled = false, className = "" } = config;
7154
+ const { disabled = false, readOnly = false, className = "" } = config;
7155
+ if (readOnly) {
7156
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: FIELD_READONLY_TEXT_CLASS, children: value ? "\uCF1C\uC9D0" : "\uAEBC\uC9D0" }) });
7157
+ }
6923
7158
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(BaseSwitch, { checked: Boolean(value), onCheckedChange: onChange, disabled }) });
6924
7159
  }
6925
7160
  function TextareaField(props) {
@@ -6932,6 +7167,9 @@ function TextareaField(props) {
6932
7167
  const handleChange = (e) => {
6933
7168
  onChange(e.target.value);
6934
7169
  };
7170
+ if (readOnly) {
7171
+ return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx("span", { className: cn2(FIELD_READONLY_TEXT_CLASS, "whitespace-pre-wrap"), children: String(value ?? "") || "\u2014" }) });
7172
+ }
6935
7173
  return /* @__PURE__ */ jsx("div", { className: fieldControlWrapClass(className), children: /* @__PURE__ */ jsx(
6936
7174
  Textarea,
6937
7175
  {
@@ -7018,7 +7256,7 @@ function useBlurValidation(name, options) {
7018
7256
  (value, fieldOnChange) => {
7019
7257
  markHadValueIfFilled(value);
7020
7258
  fieldOnChange(value);
7021
- if (options.revalidateOnChange || shouldValidateEmptyOnChange(value)) {
7259
+ if (options.revalidateOnChange || options.validateEmptyOnChange && shouldValidateEmptyOnChange(value)) {
7022
7260
  void trigger(name);
7023
7261
  }
7024
7262
  void triggerRelatedFields();
@@ -7026,6 +7264,7 @@ function useBlurValidation(name, options) {
7026
7264
  [
7027
7265
  markHadValueIfFilled,
7028
7266
  options.revalidateOnChange,
7267
+ options.validateEmptyOnChange,
7029
7268
  shouldValidateEmptyOnChange,
7030
7269
  trigger,
7031
7270
  name,
@@ -7046,9 +7285,7 @@ var REVALIDATE_ON_CHANGE_TYPES = /* @__PURE__ */ new Set([
7046
7285
  "radio",
7047
7286
  "switch",
7048
7287
  "select",
7049
- "date",
7050
7288
  "cascading-select",
7051
- "select-with-input",
7052
7289
  "input-with-button",
7053
7290
  "address"
7054
7291
  ]);
@@ -7069,14 +7306,15 @@ function FormFieldRow(props) {
7069
7306
  const { control } = useFormContext();
7070
7307
  const { isSubmitted } = useFormState({ control });
7071
7308
  const { name, label, type = "input", required = false, renderCustomField, contentAlign = "left" } = config;
7309
+ const revalidateOnChange = REVALIDATE_ON_CHANGE_TYPES.has(type);
7072
7310
  const { handleBlur, handleChange } = useBlurValidation(name, {
7073
7311
  required,
7074
- revalidateOnChange: REVALIDATE_ON_CHANGE_TYPES.has(type),
7312
+ revalidateOnChange,
7313
+ validateEmptyOnChange: revalidateOnChange,
7075
7314
  revalidateFields: config.revalidateFields
7076
7315
  });
7077
7316
  const showError = Boolean(fieldState.error?.message) && (isSubmitted || fieldState.isTouched);
7078
7317
  const isTextarea = type === "textarea";
7079
- const revalidateOnChange = REVALIDATE_ON_CHANGE_TYPES.has(type);
7080
7318
  const wrappedField = wrapFieldHandlers(field, {
7081
7319
  onBlur: () => handleBlur(() => field.onBlur()),
7082
7320
  onChange: (value) => {
@@ -7177,9 +7415,11 @@ function rowPropsToFieldConfig(props) {
7177
7415
  options,
7178
7416
  className,
7179
7417
  allowDecimal,
7418
+ numbersOnly,
7180
7419
  canReset,
7181
7420
  datePosition,
7182
7421
  disableFutureDates,
7422
+ calendarSelectContainerClass,
7183
7423
  revalidateFields,
7184
7424
  renderCustomField,
7185
7425
  colspan,
@@ -7206,9 +7446,11 @@ function rowPropsToFieldConfig(props) {
7206
7446
  options,
7207
7447
  className,
7208
7448
  allowDecimal,
7449
+ numbersOnly,
7209
7450
  canReset,
7210
7451
  datePosition,
7211
7452
  disableFutureDates,
7453
+ calendarSelectContainerClass,
7212
7454
  revalidateFields,
7213
7455
  renderCustomField,
7214
7456
  colspan,
@@ -7368,14 +7610,7 @@ function PageFilter(props) {
7368
7610
  const rowGap2 = index !== 0 ? option.label ? gap : 5 : 0;
7369
7611
  switch (option.type) {
7370
7612
  case "input":
7371
- return /* @__PURE__ */ jsx(LabeledFilterOption, { label: option.label, gap: rowGap2, children: /* @__PURE__ */ jsx(
7372
- Input2,
7373
- {
7374
- placeholder: option.placeholder,
7375
- value: currentValue,
7376
- onChange: handleInputChange(optionKey)
7377
- }
7378
- ) }, String(optionKey));
7613
+ return /* @__PURE__ */ jsx(LabeledFilterOption, { label: option.label, gap: rowGap2, children: /* @__PURE__ */ jsx(Input2, { placeholder: option.placeholder, value: currentValue, onChange: handleInputChange(optionKey) }) }, String(optionKey));
7379
7614
  case "select":
7380
7615
  return /* @__PURE__ */ jsx(LabeledFilterOption, { label: option.label, gap: rowGap2, children: /* @__PURE__ */ jsx(
7381
7616
  Select2,
@@ -7383,7 +7618,8 @@ function PageFilter(props) {
7383
7618
  value: currentValue,
7384
7619
  onChange: handleSelectChange(optionKey),
7385
7620
  options: option.options,
7386
- placeholder: "\uC120\uD0DD\uD574\uC8FC\uC138\uC694"
7621
+ placeholder: "\uC120\uD0DD",
7622
+ containerClassName: option.containerClassName ?? "w-30"
7387
7623
  }
7388
7624
  ) }, String(optionKey));
7389
7625
  case "radio":