@farmzone/fz-react-ui 1.0.1 → 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.cjs CHANGED
@@ -943,7 +943,7 @@ var SELECT_BORDER_COLORS = {
943
943
  };
944
944
  function Select2(props) {
945
945
  const {
946
- containerClass,
946
+ containerClassName,
947
947
  options,
948
948
  placeholder = "\uC120\uD0DD\uD574 \uC8FC\uC138\uC694",
949
949
  triggerClassName,
@@ -973,7 +973,7 @@ function Select2(props) {
973
973
  onChange("");
974
974
  onReset?.();
975
975
  };
976
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-0.5", containerClass ?? "w-20"), children: [
976
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-0.5", containerClassName ?? "w-30"), children: [
977
977
  label && /* @__PURE__ */ jsxRuntime.jsx(
978
978
  "label",
979
979
  {
@@ -1056,6 +1056,7 @@ function Radio(props) {
1056
1056
  checked,
1057
1057
  onChange,
1058
1058
  disabled = false,
1059
+ disabledMessage,
1059
1060
  checkColor = DEFAULT_CHECK_COLOR,
1060
1061
  unCheckColor = DEFAULT_UNCHECK_COLOR,
1061
1062
  labelClassName,
@@ -1104,23 +1105,29 @@ function Radio(props) {
1104
1105
  }
1105
1106
  )
1106
1107
  ] });
1108
+ const withTooltip = (node) => {
1109
+ if (!disabled || !disabledMessage) return node;
1110
+ return /* @__PURE__ */ jsxRuntime.jsx(Tooltip2, { content: disabledMessage, position: "bottom", asChild: true, sideOffset: 4, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex", children: node }) });
1111
+ };
1107
1112
  if (onChange !== void 0 && checked !== void 0) {
1108
- return /* @__PURE__ */ jsxRuntime.jsx(
1109
- RadioGroupPrimitive__namespace.Root,
1110
- {
1111
- name,
1112
- value: checked ? value : "",
1113
- onValueChange: (newValue) => {
1114
- if (newValue === value) {
1115
- onChange(value);
1116
- }
1117
- },
1118
- disabled,
1119
- children: control
1120
- }
1113
+ return withTooltip(
1114
+ /* @__PURE__ */ jsxRuntime.jsx(
1115
+ RadioGroupPrimitive__namespace.Root,
1116
+ {
1117
+ name,
1118
+ value: checked ? value : "",
1119
+ onValueChange: (newValue) => {
1120
+ if (newValue === value) {
1121
+ onChange(value);
1122
+ }
1123
+ },
1124
+ disabled,
1125
+ children: control
1126
+ }
1127
+ )
1121
1128
  );
1122
1129
  }
1123
- return control;
1130
+ return withTooltip(control);
1124
1131
  }
1125
1132
  function RadioGroup(props) {
1126
1133
  const {
@@ -1156,6 +1163,7 @@ function RadioGroup(props) {
1156
1163
  value: option.value,
1157
1164
  label: option.label,
1158
1165
  disabled: disabled || option.disabled,
1166
+ disabledMessage: option.disabled ? option.disabledMessage : void 0,
1159
1167
  checkColor,
1160
1168
  unCheckColor,
1161
1169
  labelClassName,
@@ -1960,6 +1968,28 @@ function parseLocalYmd(ymd) {
1960
1968
  if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) return null;
1961
1969
  return dt;
1962
1970
  }
1971
+ function canSelectToday(options) {
1972
+ const today = options.now ?? /* @__PURE__ */ new Date();
1973
+ const todayYmd = formatDate(today);
1974
+ if (options.maxDate && isCalendarDayAfterMax(today, options.maxDate)) {
1975
+ return false;
1976
+ }
1977
+ if (!options.range) {
1978
+ return true;
1979
+ }
1980
+ if (options.isSelectingEnd) {
1981
+ const start = options.rangeStart?.trim() ?? "";
1982
+ if (start.length === 10 && todayYmd < start) {
1983
+ return false;
1984
+ }
1985
+ return true;
1986
+ }
1987
+ const end = options.rangeEnd?.trim() ?? "";
1988
+ if (end.length === 10 && todayYmd > end) {
1989
+ return false;
1990
+ }
1991
+ return true;
1992
+ }
1963
1993
  function isCalendarDayAfterMax(date, maxYmd) {
1964
1994
  const max = parseLocalYmd(maxYmd);
1965
1995
  if (!max) return false;
@@ -2016,6 +2046,105 @@ function validateAndFormatInput(input) {
2016
2046
  }
2017
2047
  return `${year}-${month}-${day}`;
2018
2048
  }
2049
+
2050
+ // src/components/DatePicker/calendar/config/default.ts
2051
+ var DEFAULT_CALENDAR_YEAR_HALF_SPAN = 25;
2052
+ var CALENDAR_WEEKDAY_LABELS = ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"];
2053
+ var CALENDAR_RANGE_HINT = {
2054
+ start: "\uC2DC\uC791 \uB0A0\uC9DC\uB97C \uC120\uD0DD\uD558\uC138\uC694",
2055
+ end: "\uC885\uB8CC \uB0A0\uC9DC\uB97C \uC120\uD0DD\uD558\uC138\uC694"
2056
+ };
2057
+ var CALENDAR_PORTAL_Z_INDEX = Z_INDEX.CALENDAR_PORTAL;
2058
+ var CALENDAR_SELECT_CLASSNAMES = {
2059
+ containerClass: "w-20 shrink-0",
2060
+ triggerClassName: "h-8 px-2 py-1 text-sm cursor-pointer",
2061
+ contentClassName: "min-w-[5rem]"
2062
+ };
2063
+ var CALENDAR_PANEL_BASE_CLASS = "bg-white border border-gray-300 rounded-lg shadow-lg p-3 min-w-[280px]";
2064
+ var CALENDAR_PANEL_ANCHORED_CLASS = `${CALENDAR_PANEL_BASE_CLASS} absolute z-50`;
2065
+ var YEAR_SELECT_CONTENT_EXTRA_CLASS = "year-select-content custom-view-scrollbar max-h-50 overflow-y-auto";
2066
+ var YEAR_SELECT_OPEN_CONTENT_SELECTOR = ".year-select-content[data-state='open']";
2067
+ var SELECT_ITEM_CHECKED_SELECTOR = "[data-slot='select-item'][data-state='checked']";
2068
+
2069
+ // src/components/DatePicker/calendar/config/calendar.ts
2070
+ function parseYmdDateString(value) {
2071
+ if (value.length !== 10) return null;
2072
+ return parseLocalYmd(value);
2073
+ }
2074
+ function resolveCalendarYearBounds(anchorYear, calendarYearRange) {
2075
+ const fallbackMin = anchorYear - DEFAULT_CALENDAR_YEAR_HALF_SPAN;
2076
+ const fallbackMax = anchorYear + DEFAULT_CALENDAR_YEAR_HALF_SPAN;
2077
+ const rawMin = calendarYearRange?.minYear?.trim();
2078
+ const rawMax = calendarYearRange?.maxYear?.trim();
2079
+ if (rawMin === void 0 || rawMin === "" || rawMax === void 0 || rawMax === "") {
2080
+ return { minYear: fallbackMin, maxYear: fallbackMax };
2081
+ }
2082
+ const minParsed = Number.parseInt(rawMin, 10);
2083
+ const maxParsed = Number.parseInt(rawMax, 10);
2084
+ if (Number.isNaN(minParsed) || Number.isNaN(maxParsed) || minParsed > maxParsed) {
2085
+ return { minYear: fallbackMin, maxYear: fallbackMax };
2086
+ }
2087
+ return { minYear: minParsed, maxYear: maxParsed };
2088
+ }
2089
+ function buildCalendarYearSelectOptions(maxDate, anchorYear = (/* @__PURE__ */ new Date()).getFullYear(), calendarYearRange) {
2090
+ const { minYear, maxYear } = resolveCalendarYearBounds(anchorYear, calendarYearRange);
2091
+ const maxSelectableYear = maxDate ? parseLocalYmd(maxDate)?.getFullYear() ?? null : null;
2092
+ const options = [];
2093
+ for (let y = maxYear; y >= minYear; y--) {
2094
+ options.push({
2095
+ value: String(y),
2096
+ label: `${y}\uB144`,
2097
+ disabled: maxSelectableYear !== null && y > maxSelectableYear
2098
+ });
2099
+ }
2100
+ return options;
2101
+ }
2102
+ function buildCalendarMonthSelectOptions(viewYear, maxDate) {
2103
+ const maxDateParsed = maxDate ? parseLocalYmd(maxDate) : null;
2104
+ const maxYear = maxDateParsed?.getFullYear() ?? null;
2105
+ const maxMonth = maxDateParsed !== null && maxDateParsed !== void 0 ? maxDateParsed.getMonth() + 1 : null;
2106
+ const isMaxYear = maxYear !== null && viewYear === maxYear;
2107
+ const options = [];
2108
+ for (let m = 1; m <= 12; m++) {
2109
+ options.push({
2110
+ value: String(m),
2111
+ label: `${m}\uC6D4`,
2112
+ disabled: isMaxYear && maxMonth !== null && m > maxMonth
2113
+ });
2114
+ }
2115
+ return options;
2116
+ }
2117
+ function getCalendarMonthGrid(year, monthIndex) {
2118
+ const firstDay = new Date(year, monthIndex, 1);
2119
+ const daysInMonth = new Date(year, monthIndex + 1, 0).getDate();
2120
+ const leadingEmpty = firstDay.getDay();
2121
+ const totalCells = leadingEmpty + daysInMonth;
2122
+ const trailingEmpty = totalCells % 7 === 0 ? 0 : 7 - totalCells % 7;
2123
+ return { leadingEmpty, daysInMonth, trailingEmpty };
2124
+ }
2125
+ function disableNextCalendarMonth(year, monthIndex, maxDate) {
2126
+ if (maxDate === void 0) return false;
2127
+ return isCalendarDayAfterMax(new Date(year, monthIndex + 1, 1), maxDate);
2128
+ }
2129
+ function scrollOpenYearSelectCheckedIntoView() {
2130
+ requestAnimationFrame(() => {
2131
+ const root2 = document.querySelector(YEAR_SELECT_OPEN_CONTENT_SELECTOR);
2132
+ if (!(root2 instanceof HTMLElement)) return;
2133
+ const checkedItem = root2.querySelector(SELECT_ITEM_CHECKED_SELECTOR);
2134
+ if (!(checkedItem instanceof HTMLElement)) return;
2135
+ checkedItem.scrollIntoView({ block: "start" });
2136
+ });
2137
+ }
2138
+ function dayCellButtonClass(opts) {
2139
+ const { isToday, isSelected, isInRange, isRangeStart, isRangeEnd, interactiveDisabled } = opts;
2140
+ const parts = ["w-8 h-8 text-sm flex items-center justify-center rounded-2xl transition-colors"];
2141
+ if (isToday) parts.push("bg-gray-100");
2142
+ if (isSelected && !isToday) parts.push("bg-neutral-200 text-gray-700");
2143
+ if (isInRange && !isToday) parts.push("bg-gray-100/90");
2144
+ if ((isRangeStart || isRangeEnd) && !isToday) parts.push("bg-neutral-300 text-gray-700 opacity-100");
2145
+ parts.push(interactiveDisabled ? "opacity-20 cursor-not-allowed" : "hover:bg-blue-100 cursor-pointer");
2146
+ return parts.filter(Boolean).join(" ").trim();
2147
+ }
2019
2148
  function RenderCalendar(props) {
2020
2149
  const {
2021
2150
  currentDate,
@@ -2030,133 +2159,163 @@ function RenderCalendar(props) {
2030
2159
  goToNextMonth,
2031
2160
  onYearChange,
2032
2161
  onMonthChange,
2162
+ onSelectOpenChange,
2033
2163
  editDateSelect = true,
2034
2164
  datePosition = "bottom",
2035
- calendarSelectContainerClass
2165
+ maxDate,
2166
+ calendarYearRange,
2167
+ calendarSelectContainerClass,
2168
+ floating = false,
2169
+ showTodayButton = true
2036
2170
  } = props;
2037
2171
  const year = currentDate.getFullYear();
2038
- const month = currentDate.getMonth();
2039
- const firstDay = new Date(year, month, 1);
2040
- const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
2041
- const yearOptions = [];
2042
- for (let i = currentYear - 25; i <= currentYear + 20; i++) {
2043
- yearOptions.push({ value: i.toString(), label: `${i}\uB144` });
2044
- }
2045
- const monthOptions = [];
2046
- for (let i = 1; i <= 12; i++) {
2047
- monthOptions.push({ value: i.toString(), label: `${i}\uC6D4` });
2048
- }
2049
- const lastDay = new Date(year, month + 1, 0);
2050
- const startDate = new Date(firstDay);
2051
- startDate.setDate(startDate.getDate() - firstDay.getDay());
2052
- const endDate = new Date(lastDay);
2053
- endDate.setDate(endDate.getDate() + (6 - endDate.getDay()));
2054
- const totalDays = Math.ceil((endDate.getTime() - startDate.getTime()) / (1e3 * 60 * 60 * 24)) + 1;
2172
+ const monthIndex = currentDate.getMonth();
2173
+ const anchorYear = (/* @__PURE__ */ new Date()).getFullYear();
2174
+ const disableNextMonth = disableNextCalendarMonth(year, monthIndex, maxDate);
2175
+ const { leadingEmpty, daysInMonth, trailingEmpty } = getCalendarMonthGrid(year, monthIndex);
2176
+ const yearOptions = buildCalendarYearSelectOptions(maxDate, anchorYear, calendarYearRange);
2177
+ const monthOptions = buildCalendarMonthSelectOptions(year, maxDate);
2178
+ const todayFormatted = formatDate(/* @__PURE__ */ new Date());
2179
+ const selectedSingleYmd = range ? null : inputValue;
2180
+ const rangeStartYmd = rangeStart.trim();
2181
+ const rangeEndYmd = rangeEnd.trim();
2055
2182
  const days = [];
2056
- const currentDateFormatted = formatDate(/* @__PURE__ */ new Date());
2057
- const selectedDate = range ? null : inputValue;
2058
- const handleStopPropagation = (e) => e.stopPropagation();
2059
- const handleYearChange = (value) => onYearChange(parseInt(value));
2060
- const handleMonthChange = (value) => onMonthChange(parseInt(value) - 1);
2061
- for (let i = 0; i < totalDays; i++) {
2062
- const date = new Date(startDate);
2063
- date.setDate(startDate.getDate() + i);
2183
+ for (let i = 0; i < leadingEmpty; i++) {
2184
+ days.push(/* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-8", "aria-hidden": true }, `lead-${i}`));
2185
+ }
2186
+ for (let d = 1; d <= daysInMonth; d++) {
2187
+ const date = new Date(year, monthIndex, d);
2064
2188
  const dateFormatted = formatDate(date);
2065
- const isToday = dateFormatted === currentDateFormatted;
2066
- const isSelected = dateFormatted === selectedDate;
2189
+ const isToday = dateFormatted === todayFormatted;
2190
+ const isSelected = dateFormatted === selectedSingleYmd;
2067
2191
  const isInRange = isDateInRange(date, range, rangeStart, rangeEnd);
2068
- const isRangeStart = range && dateFormatted === rangeStart;
2069
- const isRangeEnd = range && dateFormatted === rangeEnd;
2070
- const isCurrentMonth = date.getMonth() === month;
2071
- const rangeStartDate = new Date(rangeStart);
2072
- rangeStartDate.setDate(rangeStartDate.getDate() - 1);
2073
- const isDisabled = range && isSelectingEnd && rangeStart && date < rangeStartDate;
2192
+ const isRangeStartFlag = Boolean(range && dateFormatted === rangeStart);
2193
+ const isRangeEndFlag = Boolean(range && dateFormatted === rangeEnd);
2194
+ const isBeforeRangeStart = range && isSelectingEnd && rangeStartYmd.length === 10 && dateFormatted < rangeStartYmd;
2195
+ const isAfterRangeEnd = range && !isSelectingEnd && rangeEndYmd.length === 10 && dateFormatted > rangeEndYmd;
2196
+ const isGreyedForRange = Boolean(isBeforeRangeStart || isAfterRangeEnd);
2197
+ const isPastMax = maxDate ? isCalendarDayAfterMax(date, maxDate) : false;
2074
2198
  days.push(
2075
2199
  /* @__PURE__ */ jsxRuntime.jsx(
2076
2200
  "button",
2077
2201
  {
2202
+ type: "button",
2078
2203
  onClick: () => handleDateSelect(date),
2079
- disabled: isDisabled || false,
2080
- className: `
2081
- w-8 h-8 text-sm flex items-center justify-center rounded-2xl transition-colors
2082
- ${!isCurrentMonth ? "text-gray-300" : ""}
2083
- ${isToday ? "bg-gray-100 " : ""}
2084
- ${isInRange && !isToday ? "bg-gray-100/90" : ""}
2085
- ${isSelected && !isToday ? "bg-neutral-200 text-gray-700" : ""}
2086
- ${(isRangeStart || isRangeEnd) && !isToday ? "bg-neutral-300 text-gray-700 opacity-100" : ""}
2087
- ${isDisabled ? "opacity-20 cursor-not-allowed" : "hover:bg-blue-100 cursor-pointer"}
2088
- `,
2089
- children: date.getDate()
2204
+ disabled: isGreyedForRange || isPastMax,
2205
+ className: dayCellButtonClass({
2206
+ isToday,
2207
+ isSelected: Boolean(isSelected),
2208
+ isInRange,
2209
+ isRangeStart: isRangeStartFlag,
2210
+ isRangeEnd: isRangeEndFlag,
2211
+ interactiveDisabled: isGreyedForRange || isPastMax
2212
+ }),
2213
+ children: d
2090
2214
  },
2091
- i
2215
+ d
2092
2216
  )
2093
2217
  );
2094
2218
  }
2095
- const calendarPositionClass = range && calendarPosition === "end" ? "right-0" : "left-0";
2096
- const datePositionClass = datePosition === "top" ? "bottom-full mb-1" : "top-full mt-1";
2097
- return /* @__PURE__ */ jsxRuntime.jsxs(
2098
- "div",
2099
- {
2100
- className: `${datePositionClass} ${calendarPositionClass} bg-white border border-gray-300 rounded-lg shadow-lg p-3 z-50 min-w-[280px]`,
2101
- onClick: handleStopPropagation,
2102
- onMouseDown: handleStopPropagation,
2103
- children: [
2104
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
2105
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: goToPreviousMonth, className: "p-1 hover:bg-gray-100 rounded cursor-pointer", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "size-4 text-balck" }) }),
2106
- editDateSelect ? /* @__PURE__ */ jsxRuntime.jsxs(
2107
- "div",
2108
- {
2109
- className: "flex items-center gap-2",
2110
- onClick: handleStopPropagation,
2111
- onMouseDown: handleStopPropagation,
2112
- children: [
2113
- /* @__PURE__ */ jsxRuntime.jsx("div", { onClick: handleStopPropagation, onMouseDown: handleStopPropagation, children: /* @__PURE__ */ jsxRuntime.jsx(
2114
- Select2,
2115
- {
2116
- value: year.toString(),
2117
- options: yearOptions,
2118
- onChange: handleYearChange,
2119
- className: "cursor-pointer",
2120
- containerClass: calendarSelectContainerClass
2121
- }
2122
- ) }),
2123
- /* @__PURE__ */ jsxRuntime.jsx("div", { onClick: handleStopPropagation, onMouseDown: handleStopPropagation, children: /* @__PURE__ */ jsxRuntime.jsx(
2124
- Select2,
2125
- {
2126
- value: (month + 1).toString(),
2127
- options: monthOptions,
2128
- onChange: handleMonthChange,
2129
- className: "cursor-pointer",
2130
- containerClass: calendarSelectContainerClass
2131
- }
2132
- ) })
2133
- ]
2134
- }
2135
- ) : /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-lg font-semibold", children: [
2136
- year,
2137
- "\uB144 ",
2138
- month + 1,
2139
- "\uC6D4"
2140
- ] }),
2141
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: goToNextMonth, className: "p-1 hover:bg-gray-100 rounded cursor-pointer", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "size-4" }) })
2142
- ] }),
2143
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1 mb-1", children: ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"].map((day) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center text-sm font-medium text-gray-500 py-2 ", children: day }, day)) }),
2144
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2", children: days })
2145
- ]
2146
- }
2147
- );
2148
- }
2149
-
2150
- // src/components/DatePicker/calendar/config/default.ts
2151
- var CALENDAR_PORTAL_Z_INDEX = Z_INDEX.CALENDAR_PORTAL;
2152
-
2153
- // src/components/DatePicker/calendar/config/calendar.ts
2154
- function parseYmdDateString(value) {
2155
- if (value.length !== 10) return null;
2156
- return parseLocalYmd(value);
2219
+ for (let i = 0; i < trailingEmpty; i++) {
2220
+ days.push(/* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-8", "aria-hidden": true }, `trail-${i}`));
2221
+ }
2222
+ const calendarPositionClass = !floating && range && calendarPosition === "end" ? "right-0" : !floating ? "left-0" : "";
2223
+ const datePositionClass = floating ? "" : datePosition === "top" ? "bottom-full mb-1" : "top-full mt-1";
2224
+ const panelClass = floating ? CALENDAR_PANEL_BASE_CLASS : cn(CALENDAR_PANEL_ANCHORED_CLASS, datePositionClass, calendarPositionClass);
2225
+ const handleYearSelectOpenChange = (open) => {
2226
+ onSelectOpenChange?.(open);
2227
+ if (open) scrollOpenYearSelectCheckedIntoView();
2228
+ };
2229
+ const sel = CALENDAR_SELECT_CLASSNAMES;
2230
+ const containerClass = cn(sel.containerClass, calendarSelectContainerClass);
2231
+ const stopBubble = (e) => e.stopPropagation();
2232
+ const isTodaySelectable = canSelectToday({
2233
+ maxDate,
2234
+ range,
2235
+ isSelectingEnd,
2236
+ rangeStart,
2237
+ rangeEnd
2238
+ });
2239
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: panelClass, onClick: stopBubble, onMouseDown: stopBubble, children: [
2240
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
2241
+ /* @__PURE__ */ jsxRuntime.jsx(
2242
+ "button",
2243
+ {
2244
+ type: "button",
2245
+ onClick: goToPreviousMonth,
2246
+ className: "cursor-pointer rounded p-1 hover:bg-gray-100",
2247
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "size-4" })
2248
+ }
2249
+ ),
2250
+ editDateSelect ? /* @__PURE__ */ jsxRuntime.jsxs(
2251
+ "div",
2252
+ {
2253
+ role: "presentation",
2254
+ className: "flex items-center gap-2",
2255
+ onClick: stopBubble,
2256
+ onMouseDown: stopBubble,
2257
+ children: [
2258
+ /* @__PURE__ */ jsxRuntime.jsx(
2259
+ Select2,
2260
+ {
2261
+ value: year.toString(),
2262
+ options: yearOptions,
2263
+ onChange: (value) => onYearChange(Number.parseInt(value, 10)),
2264
+ containerClassName: containerClass,
2265
+ triggerClassName: sel.triggerClassName,
2266
+ contentClassName: cn(YEAR_SELECT_CONTENT_EXTRA_CLASS, sel.contentClassName),
2267
+ disableScrollButtonEvents: true,
2268
+ onOpenChange: handleYearSelectOpenChange
2269
+ }
2270
+ ),
2271
+ /* @__PURE__ */ jsxRuntime.jsx(
2272
+ Select2,
2273
+ {
2274
+ value: (monthIndex + 1).toString(),
2275
+ options: monthOptions,
2276
+ onChange: (value) => onMonthChange(Number.parseInt(value, 10) - 1),
2277
+ containerClassName: containerClass,
2278
+ triggerClassName: sel.triggerClassName,
2279
+ contentClassName: sel.contentClassName,
2280
+ onOpenChange: (open) => onSelectOpenChange?.(open)
2281
+ }
2282
+ )
2283
+ ]
2284
+ }
2285
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-lg font-semibold", children: [
2286
+ year,
2287
+ "\uB144 ",
2288
+ monthIndex + 1,
2289
+ "\uC6D4"
2290
+ ] }),
2291
+ /* @__PURE__ */ jsxRuntime.jsx(
2292
+ "button",
2293
+ {
2294
+ type: "button",
2295
+ onClick: goToNextMonth,
2296
+ disabled: disableNextMonth,
2297
+ className: cn(
2298
+ "rounded p-1",
2299
+ disableNextMonth ? "cursor-not-allowed opacity-30" : "cursor-pointer hover:bg-gray-100"
2300
+ ),
2301
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "size-4" })
2302
+ }
2303
+ )
2304
+ ] }),
2305
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1 grid grid-cols-7 gap-1", children: CALENDAR_WEEKDAY_LABELS.map((day) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-2 text-center text-sm font-medium text-gray-500", children: day }, day)) }),
2306
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2", children: days }),
2307
+ range && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 text-sm text-gray-600", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center", children: isSelectingEnd ? CALENDAR_RANGE_HINT.end : CALENDAR_RANGE_HINT.start }) }),
2308
+ showTodayButton && isTodaySelectable && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "-mx-3 -mb-3 mt-3 flex items-center justify-center border-t border-gray-200 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(
2309
+ "button",
2310
+ {
2311
+ type: "button",
2312
+ onClick: () => handleDateSelect(/* @__PURE__ */ new Date()),
2313
+ className: "cursor-pointer text-sm text-gray-800 hover:text-main",
2314
+ children: "Today"
2315
+ }
2316
+ ) })
2317
+ ] });
2157
2318
  }
2158
-
2159
- // src/components/DatePicker/calendar/hooks/useCalendarState.ts
2160
2319
  function useCalendarState(props) {
2161
2320
  const { datePosition = "bottom", disabled = false, calendarRef } = props;
2162
2321
  const [isCalendarOpen, setIsCalendarOpen] = React6.useState(false);
@@ -2253,11 +2412,15 @@ function useFloatingCalendarPosition(anchorRef, isOpen2, placement, align = "sta
2253
2412
  setStyle({ ...base, top: rect.bottom + CALENDAR_GAP });
2254
2413
  }
2255
2414
  };
2415
+ const handleScroll = (e) => {
2416
+ if (e.target?.closest?.('[data-slot="select-content"]')) return;
2417
+ update();
2418
+ };
2256
2419
  update();
2257
- window.addEventListener("scroll", update, true);
2420
+ window.addEventListener("scroll", handleScroll, true);
2258
2421
  window.addEventListener("resize", update);
2259
2422
  return () => {
2260
- window.removeEventListener("scroll", update, true);
2423
+ window.removeEventListener("scroll", handleScroll, true);
2261
2424
  window.removeEventListener("resize", update);
2262
2425
  };
2263
2426
  }, [isOpen2, placement, align, anchorRef]);
@@ -6565,7 +6728,7 @@ function CascadingSelectFieldInner(props) {
6565
6728
  disabled,
6566
6729
  hasError,
6567
6730
  options: toSelectOptions(primaryOptions),
6568
- containerClass: firstSelectClass.containerClassName,
6731
+ containerClassName: firstSelectClass.containerClassName,
6569
6732
  triggerClassName: firstSelectClass.triggerClassName,
6570
6733
  canReset: true,
6571
6734
  onChange: (nextPrimary) => {
@@ -6582,7 +6745,7 @@ function CascadingSelectFieldInner(props) {
6582
6745
  disabled: disabled || !primary || secondaryOptions.length === 0,
6583
6746
  hasError,
6584
6747
  options: toSelectOptions(secondaryOptions),
6585
- containerClass: secondSelectClass.containerClassName,
6748
+ containerClassName: secondSelectClass.containerClassName,
6586
6749
  triggerClassName: secondSelectClass.triggerClassName,
6587
6750
  canReset: true,
6588
6751
  onChange: (nextSecondary) => {
@@ -6626,7 +6789,8 @@ function DateField(props) {
6626
6789
  readOnly = false,
6627
6790
  className = "w-40",
6628
6791
  datePosition = "bottom",
6629
- disableFutureDates = false
6792
+ disableFutureDates = false,
6793
+ calendarSelectContainerClass = "w-20"
6630
6794
  } = config;
6631
6795
  const maxDate = disableFutureDates ? formatDate(/* @__PURE__ */ new Date()) : void 0;
6632
6796
  if (readOnly) {
@@ -6643,7 +6807,8 @@ function DateField(props) {
6643
6807
  className: "w-full",
6644
6808
  validation: hasError,
6645
6809
  datePosition,
6646
- maxDate
6810
+ maxDate,
6811
+ calendarSelectContainerClass
6647
6812
  }
6648
6813
  ) });
6649
6814
  }
@@ -6833,7 +6998,9 @@ function RadioField(props) {
6833
6998
  containerClassName: cn2("justify-start", fieldControlWrapClass(className)),
6834
6999
  options: options.map((option) => ({
6835
7000
  label: option.label,
6836
- value: String(option.value)
7001
+ value: String(option.value),
7002
+ disabled: option.disabled,
7003
+ disabledMessage: option.disabledMessage
6837
7004
  }))
6838
7005
  }
6839
7006
  );
@@ -6871,7 +7038,7 @@ function SelectField(props) {
6871
7038
  disabled,
6872
7039
  hasError,
6873
7040
  options: selectOptions,
6874
- containerClass: "flex w-full min-w-0 max-w-full items-center",
7041
+ containerClassName: "flex w-full min-w-0 max-w-full items-center",
6875
7042
  className: "w-full min-w-0",
6876
7043
  triggerClassName: cn2(
6877
7044
  "h-8 w-full min-w-0 max-w-full rounded border bg-white px-2 py-1 text-sm shadow-none",
@@ -6983,11 +7150,12 @@ function SelectWithInputField(props) {
6983
7150
  disabled,
6984
7151
  hasError,
6985
7152
  options: toSelectOptions(options),
6986
- containerClass: selectClass.containerClassName,
7153
+ containerClassName: selectClass.containerClassName,
6987
7154
  triggerClassName: selectClass.triggerClassName,
6988
7155
  onChange: (nextPrefix) => {
6989
7156
  setSelectedPrefix(nextPrefix);
6990
7157
  updateCombined(nextPrefix, inputPart);
7158
+ onBlur();
6991
7159
  }
6992
7160
  }
6993
7161
  ),
@@ -7122,7 +7290,7 @@ function useBlurValidation(name, options) {
7122
7290
  (value, fieldOnChange) => {
7123
7291
  markHadValueIfFilled(value);
7124
7292
  fieldOnChange(value);
7125
- if (options.revalidateOnChange || shouldValidateEmptyOnChange(value)) {
7293
+ if (options.revalidateOnChange || options.validateEmptyOnChange && shouldValidateEmptyOnChange(value)) {
7126
7294
  void trigger(name);
7127
7295
  }
7128
7296
  void triggerRelatedFields();
@@ -7130,6 +7298,7 @@ function useBlurValidation(name, options) {
7130
7298
  [
7131
7299
  markHadValueIfFilled,
7132
7300
  options.revalidateOnChange,
7301
+ options.validateEmptyOnChange,
7133
7302
  shouldValidateEmptyOnChange,
7134
7303
  trigger,
7135
7304
  name,
@@ -7150,9 +7319,7 @@ var REVALIDATE_ON_CHANGE_TYPES = /* @__PURE__ */ new Set([
7150
7319
  "radio",
7151
7320
  "switch",
7152
7321
  "select",
7153
- "date",
7154
7322
  "cascading-select",
7155
- "select-with-input",
7156
7323
  "input-with-button",
7157
7324
  "address"
7158
7325
  ]);
@@ -7173,14 +7340,15 @@ function FormFieldRow(props) {
7173
7340
  const { control } = reactHookForm.useFormContext();
7174
7341
  const { isSubmitted } = reactHookForm.useFormState({ control });
7175
7342
  const { name, label, type = "input", required = false, renderCustomField, contentAlign = "left" } = config;
7343
+ const revalidateOnChange = REVALIDATE_ON_CHANGE_TYPES.has(type);
7176
7344
  const { handleBlur, handleChange } = useBlurValidation(name, {
7177
7345
  required,
7178
- revalidateOnChange: REVALIDATE_ON_CHANGE_TYPES.has(type),
7346
+ revalidateOnChange,
7347
+ validateEmptyOnChange: revalidateOnChange,
7179
7348
  revalidateFields: config.revalidateFields
7180
7349
  });
7181
7350
  const showError = Boolean(fieldState.error?.message) && (isSubmitted || fieldState.isTouched);
7182
7351
  const isTextarea = type === "textarea";
7183
- const revalidateOnChange = REVALIDATE_ON_CHANGE_TYPES.has(type);
7184
7352
  const wrappedField = wrapFieldHandlers(field, {
7185
7353
  onBlur: () => handleBlur(() => field.onBlur()),
7186
7354
  onChange: (value) => {
@@ -7285,6 +7453,7 @@ function rowPropsToFieldConfig(props) {
7285
7453
  canReset,
7286
7454
  datePosition,
7287
7455
  disableFutureDates,
7456
+ calendarSelectContainerClass,
7288
7457
  revalidateFields,
7289
7458
  renderCustomField,
7290
7459
  colspan,
@@ -7315,6 +7484,7 @@ function rowPropsToFieldConfig(props) {
7315
7484
  canReset,
7316
7485
  datePosition,
7317
7486
  disableFutureDates,
7487
+ calendarSelectContainerClass,
7318
7488
  revalidateFields,
7319
7489
  renderCustomField,
7320
7490
  colspan,
@@ -7474,14 +7644,7 @@ function PageFilter(props) {
7474
7644
  const rowGap2 = index !== 0 ? option.label ? gap : 5 : 0;
7475
7645
  switch (option.type) {
7476
7646
  case "input":
7477
- return /* @__PURE__ */ jsxRuntime.jsx(LabeledFilterOption, { label: option.label, gap: rowGap2, children: /* @__PURE__ */ jsxRuntime.jsx(
7478
- Input2,
7479
- {
7480
- placeholder: option.placeholder,
7481
- value: currentValue,
7482
- onChange: handleInputChange(optionKey)
7483
- }
7484
- ) }, String(optionKey));
7647
+ return /* @__PURE__ */ jsxRuntime.jsx(LabeledFilterOption, { label: option.label, gap: rowGap2, children: /* @__PURE__ */ jsxRuntime.jsx(Input2, { placeholder: option.placeholder, value: currentValue, onChange: handleInputChange(optionKey) }) }, String(optionKey));
7485
7648
  case "select":
7486
7649
  return /* @__PURE__ */ jsxRuntime.jsx(LabeledFilterOption, { label: option.label, gap: rowGap2, children: /* @__PURE__ */ jsxRuntime.jsx(
7487
7650
  Select2,
@@ -7489,7 +7652,8 @@ function PageFilter(props) {
7489
7652
  value: currentValue,
7490
7653
  onChange: handleSelectChange(optionKey),
7491
7654
  options: option.options,
7492
- placeholder: "\uC120\uD0DD\uD574\uC8FC\uC138\uC694"
7655
+ placeholder: "\uC120\uD0DD",
7656
+ containerClassName: option.containerClassName ?? "w-30"
7493
7657
  }
7494
7658
  ) }, String(optionKey));
7495
7659
  case "radio":