@farmzone/fz-react-ui 1.0.1 → 1.0.3

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
@@ -208,6 +208,32 @@ function TooltipContent({
208
208
  }
209
209
  ) });
210
210
  }
211
+
212
+ // src/config/zIndex.ts
213
+ var Z_INDEX = {
214
+ TABLE_COL_STICKY: 1,
215
+ TABLE_FIXED_CELL: 5,
216
+ TABLE_FIXED_HEADER_CELL: 10,
217
+ LOCAL_OVERLAY: 10,
218
+ LAYOUT_HEADER: 20,
219
+ DROPDOWN_LOCAL: 20,
220
+ SIDEBAR_CONTAINER: 30,
221
+ SIDEBAR_BACKDROP: 40,
222
+ SIDEBAR: 50,
223
+ DROPDOWN: 50,
224
+ CALENDAR_ANCHORED: 50,
225
+ LOADING_OVERLAY_DEFAULT: 50,
226
+ DRAWING_CANVAS_1: 51,
227
+ DRAWING_CANVAS_2: 52,
228
+ DRAWING: 53,
229
+ MODAL_OVERLAY: 54,
230
+ MODAL: 55,
231
+ CALENDAR_PORTAL: 60,
232
+ TOOLTIP: 70,
233
+ SELECT_CONTENT: 100,
234
+ TOP: 9999,
235
+ MODAL_Z_STEP: 10
236
+ };
211
237
  function Tooltip2(props) {
212
238
  const {
213
239
  children,
@@ -219,6 +245,7 @@ function Tooltip2(props) {
219
245
  arrowClassName = "",
220
246
  contentClassName = "",
221
247
  contentStyle = {},
248
+ containerStyle,
222
249
  offsetX = 0,
223
250
  offsetY = 2,
224
251
  delayDuration = 0,
@@ -238,7 +265,9 @@ function Tooltip2(props) {
238
265
  sideOffset,
239
266
  collisionPadding: 8,
240
267
  style: {
241
- transform: `translateX(${offsetX}px) translateY(${translateY}px)`
268
+ zIndex: Z_INDEX.TOOLTIP,
269
+ transform: `translateX(${offsetX}px) translateY(${translateY}px)`,
270
+ ...containerStyle
242
271
  },
243
272
  children: /* @__PURE__ */ jsx("div", { className: "px-3 py-[5px] break-words whitespace-normal", style: contentStyle, children: /* @__PURE__ */ jsxs("p", { className: contentClassName, children: [
244
273
  content && /* @__PURE__ */ jsx("span", { children: content }),
@@ -909,7 +938,7 @@ var SELECT_BORDER_COLORS = {
909
938
  };
910
939
  function Select2(props) {
911
940
  const {
912
- containerClass,
941
+ containerClassName,
913
942
  options,
914
943
  placeholder = "\uC120\uD0DD\uD574 \uC8FC\uC138\uC694",
915
944
  triggerClassName,
@@ -939,7 +968,7 @@ function Select2(props) {
939
968
  onChange("");
940
969
  onReset?.();
941
970
  };
942
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-0.5", containerClass ?? "w-20"), children: [
971
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-0.5", containerClassName ?? "w-30"), children: [
943
972
  label && /* @__PURE__ */ jsx(
944
973
  "label",
945
974
  {
@@ -1022,6 +1051,7 @@ function Radio(props) {
1022
1051
  checked,
1023
1052
  onChange,
1024
1053
  disabled = false,
1054
+ disabledMessage,
1025
1055
  checkColor = DEFAULT_CHECK_COLOR,
1026
1056
  unCheckColor = DEFAULT_UNCHECK_COLOR,
1027
1057
  labelClassName,
@@ -1070,23 +1100,29 @@ function Radio(props) {
1070
1100
  }
1071
1101
  )
1072
1102
  ] });
1103
+ const withTooltip = (node) => {
1104
+ if (!disabled || !disabledMessage) return node;
1105
+ return /* @__PURE__ */ jsx(Tooltip2, { content: disabledMessage, position: "bottom", asChild: true, sideOffset: 4, children: /* @__PURE__ */ jsx("span", { className: "inline-flex", children: node }) });
1106
+ };
1073
1107
  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
- }
1108
+ return withTooltip(
1109
+ /* @__PURE__ */ jsx(
1110
+ RadioGroupPrimitive.Root,
1111
+ {
1112
+ name,
1113
+ value: checked ? value : "",
1114
+ onValueChange: (newValue) => {
1115
+ if (newValue === value) {
1116
+ onChange(value);
1117
+ }
1118
+ },
1119
+ disabled,
1120
+ children: control
1121
+ }
1122
+ )
1087
1123
  );
1088
1124
  }
1089
- return control;
1125
+ return withTooltip(control);
1090
1126
  }
1091
1127
  function RadioGroup(props) {
1092
1128
  const {
@@ -1122,6 +1158,7 @@ function RadioGroup(props) {
1122
1158
  value: option.value,
1123
1159
  label: option.label,
1124
1160
  disabled: disabled || option.disabled,
1161
+ disabledMessage: option.disabled ? option.disabledMessage : void 0,
1125
1162
  checkColor,
1126
1163
  unCheckColor,
1127
1164
  labelClassName,
@@ -1277,31 +1314,6 @@ function Spinner(props) {
1277
1314
  }
1278
1315
  );
1279
1316
  }
1280
-
1281
- // src/config/zIndex.ts
1282
- var Z_INDEX = {
1283
- TABLE_COL_STICKY: 1,
1284
- TABLE_FIXED_CELL: 5,
1285
- TABLE_FIXED_HEADER_CELL: 10,
1286
- LOCAL_OVERLAY: 10,
1287
- LAYOUT_HEADER: 20,
1288
- DROPDOWN_LOCAL: 20,
1289
- SIDEBAR_CONTAINER: 30,
1290
- SIDEBAR_BACKDROP: 40,
1291
- SIDEBAR: 50,
1292
- DROPDOWN: 50,
1293
- CALENDAR_ANCHORED: 50,
1294
- LOADING_OVERLAY_DEFAULT: 50,
1295
- DRAWING_CANVAS_1: 51,
1296
- DRAWING_CANVAS_2: 52,
1297
- DRAWING: 53,
1298
- MODAL_OVERLAY: 54,
1299
- MODAL: 55,
1300
- CALENDAR_PORTAL: 60,
1301
- SELECT_CONTENT: 100,
1302
- TOP: 9999,
1303
- MODAL_Z_STEP: 10
1304
- };
1305
1317
  function LoadingOverlay(props) {
1306
1318
  const {
1307
1319
  isVisible,
@@ -1916,16 +1928,43 @@ function ToastProvider() {
1916
1928
  }
1917
1929
 
1918
1930
  // src/components/DatePicker/config/utils.ts
1931
+ function makeLocalDate(year, monthIndex, day) {
1932
+ const d = /* @__PURE__ */ new Date(0);
1933
+ d.setFullYear(year, monthIndex, day);
1934
+ return d;
1935
+ }
1919
1936
  function parseLocalYmd(ymd) {
1920
1937
  const m = ymd.trim().match(/^(\d{4})-(\d{2})-(\d{2})$/);
1921
1938
  if (!m) return null;
1922
1939
  const y = Number(m[1]);
1923
1940
  const mo = Number(m[2]) - 1;
1924
1941
  const d = Number(m[3]);
1925
- const dt = new Date(y, mo, d);
1942
+ const dt = makeLocalDate(y, mo, d);
1926
1943
  if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) return null;
1927
1944
  return dt;
1928
1945
  }
1946
+ function canSelectToday(options) {
1947
+ const today = options.now ?? /* @__PURE__ */ new Date();
1948
+ const todayYmd = formatDate(today);
1949
+ if (options.maxDate && isCalendarDayAfterMax(today, options.maxDate)) {
1950
+ return false;
1951
+ }
1952
+ if (!options.range) {
1953
+ return true;
1954
+ }
1955
+ if (options.isSelectingEnd) {
1956
+ const start = options.rangeStart?.trim() ?? "";
1957
+ if (start.length === 10 && todayYmd < start) {
1958
+ return false;
1959
+ }
1960
+ return true;
1961
+ }
1962
+ const end = options.rangeEnd?.trim() ?? "";
1963
+ if (end.length === 10 && todayYmd > end) {
1964
+ return false;
1965
+ }
1966
+ return true;
1967
+ }
1929
1968
  function isCalendarDayAfterMax(date, maxYmd) {
1930
1969
  const max = parseLocalYmd(maxYmd);
1931
1970
  if (!max) return false;
@@ -1934,7 +1973,7 @@ function isCalendarDayAfterMax(date, maxYmd) {
1934
1973
  return ax > bx;
1935
1974
  }
1936
1975
  function formatDate(date) {
1937
- const year = date.getFullYear();
1976
+ const year = String(date.getFullYear()).padStart(4, "0");
1938
1977
  const month = String(date.getMonth() + 1).padStart(2, "0");
1939
1978
  const day = String(date.getDate()).padStart(2, "0");
1940
1979
  return `${year}-${month}-${day}`;
@@ -1953,7 +1992,6 @@ function validateAndFormatInput(input) {
1953
1992
  if (numbers.length === 0) return "";
1954
1993
  const clampYear = (raw) => {
1955
1994
  const n = parseInt(raw, 10);
1956
- if (n < 1) return "0001";
1957
1995
  if (n > 9999) return "9999";
1958
1996
  return raw;
1959
1997
  };
@@ -1976,12 +2014,111 @@ function validateAndFormatInput(input) {
1976
2014
  let day = dayRaw;
1977
2015
  if (day.length === 2) {
1978
2016
  const dayNum = parseInt(day, 10);
1979
- const lastDayOfMonth = new Date(parseInt(year, 10), parseInt(month, 10), 0).getDate();
2017
+ const lastDayOfMonth = makeLocalDate(parseInt(year, 10), parseInt(month, 10), 0).getDate();
1980
2018
  if (dayNum < 1) day = "01";
1981
2019
  else if (dayNum > lastDayOfMonth) day = lastDayOfMonth.toString().padStart(2, "0");
1982
2020
  }
1983
2021
  return `${year}-${month}-${day}`;
1984
2022
  }
2023
+
2024
+ // src/components/DatePicker/calendar/config/default.ts
2025
+ var DEFAULT_CALENDAR_YEAR_HALF_SPAN = 50;
2026
+ var CALENDAR_WEEKDAY_LABELS = ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"];
2027
+ var CALENDAR_RANGE_HINT = {
2028
+ start: "\uC2DC\uC791 \uB0A0\uC9DC\uB97C \uC120\uD0DD\uD558\uC138\uC694",
2029
+ end: "\uC885\uB8CC \uB0A0\uC9DC\uB97C \uC120\uD0DD\uD558\uC138\uC694"
2030
+ };
2031
+ var CALENDAR_PORTAL_Z_INDEX = Z_INDEX.CALENDAR_PORTAL;
2032
+ var CALENDAR_SELECT_CLASSNAMES = {
2033
+ containerClass: "w-20 shrink-0",
2034
+ triggerClassName: "h-8 px-2 py-1 text-sm cursor-pointer",
2035
+ contentClassName: "min-w-[5rem]"
2036
+ };
2037
+ var CALENDAR_PANEL_BASE_CLASS = "bg-white border border-gray-300 rounded-lg shadow-lg p-3 min-w-[280px]";
2038
+ var CALENDAR_PANEL_ANCHORED_CLASS = `${CALENDAR_PANEL_BASE_CLASS} absolute z-50`;
2039
+ var YEAR_SELECT_CONTENT_EXTRA_CLASS = "year-select-content custom-view-scrollbar max-h-50 overflow-y-auto";
2040
+ var YEAR_SELECT_OPEN_CONTENT_SELECTOR = ".year-select-content[data-state='open']";
2041
+ var SELECT_ITEM_CHECKED_SELECTOR = "[data-slot='select-item'][data-state='checked']";
2042
+
2043
+ // src/components/DatePicker/calendar/config/calendar.ts
2044
+ function parseYmdDateString(value) {
2045
+ if (value.length !== 10) return null;
2046
+ return parseLocalYmd(value);
2047
+ }
2048
+ function resolveCalendarYearBounds(anchorYear, calendarYearRange) {
2049
+ const fallbackMin = Math.max(0, anchorYear - DEFAULT_CALENDAR_YEAR_HALF_SPAN);
2050
+ const fallbackMax = anchorYear + DEFAULT_CALENDAR_YEAR_HALF_SPAN;
2051
+ const rawMin = calendarYearRange?.minYear?.trim();
2052
+ const rawMax = calendarYearRange?.maxYear?.trim();
2053
+ if (rawMin === void 0 || rawMin === "" || rawMax === void 0 || rawMax === "") {
2054
+ return { minYear: fallbackMin, maxYear: fallbackMax };
2055
+ }
2056
+ const minParsed = Number.parseInt(rawMin, 10);
2057
+ const maxParsed = Number.parseInt(rawMax, 10);
2058
+ if (Number.isNaN(minParsed) || Number.isNaN(maxParsed) || minParsed > maxParsed) {
2059
+ return { minYear: fallbackMin, maxYear: fallbackMax };
2060
+ }
2061
+ return { minYear: Math.max(0, minParsed), maxYear: maxParsed };
2062
+ }
2063
+ function buildCalendarYearSelectOptions(maxDate, anchorYear = (/* @__PURE__ */ new Date()).getFullYear(), calendarYearRange) {
2064
+ const { minYear, maxYear } = resolveCalendarYearBounds(anchorYear, calendarYearRange);
2065
+ const maxSelectableYear = maxDate ? parseLocalYmd(maxDate)?.getFullYear() ?? null : null;
2066
+ const options = [];
2067
+ for (let y = maxYear; y >= minYear; y--) {
2068
+ options.push({
2069
+ value: String(y),
2070
+ label: `${String(y).padStart(4, "0")}\uB144`,
2071
+ disabled: maxSelectableYear !== null && y > maxSelectableYear
2072
+ });
2073
+ }
2074
+ return options;
2075
+ }
2076
+ function buildCalendarMonthSelectOptions(viewYear, maxDate) {
2077
+ const maxDateParsed = maxDate ? parseLocalYmd(maxDate) : null;
2078
+ const maxYear = maxDateParsed?.getFullYear() ?? null;
2079
+ const maxMonth = maxDateParsed !== null && maxDateParsed !== void 0 ? maxDateParsed.getMonth() + 1 : null;
2080
+ const isMaxYear = maxYear !== null && viewYear === maxYear;
2081
+ const options = [];
2082
+ for (let m = 1; m <= 12; m++) {
2083
+ options.push({
2084
+ value: String(m),
2085
+ label: `${m}\uC6D4`,
2086
+ disabled: isMaxYear && maxMonth !== null && m > maxMonth
2087
+ });
2088
+ }
2089
+ return options;
2090
+ }
2091
+ function getCalendarMonthGrid(year, monthIndex) {
2092
+ const firstDay = makeLocalDate(year, monthIndex, 1);
2093
+ const daysInMonth = makeLocalDate(year, monthIndex + 1, 0).getDate();
2094
+ const leadingEmpty = firstDay.getDay();
2095
+ const totalCells = leadingEmpty + daysInMonth;
2096
+ const trailingEmpty = totalCells % 7 === 0 ? 0 : 7 - totalCells % 7;
2097
+ return { leadingEmpty, daysInMonth, trailingEmpty };
2098
+ }
2099
+ function disableNextCalendarMonth(year, monthIndex, maxDate) {
2100
+ if (maxDate === void 0) return false;
2101
+ return isCalendarDayAfterMax(makeLocalDate(year, monthIndex + 1, 1), maxDate);
2102
+ }
2103
+ function scrollOpenYearSelectCheckedIntoView() {
2104
+ requestAnimationFrame(() => {
2105
+ const root2 = document.querySelector(YEAR_SELECT_OPEN_CONTENT_SELECTOR);
2106
+ if (!(root2 instanceof HTMLElement)) return;
2107
+ const checkedItem = root2.querySelector(SELECT_ITEM_CHECKED_SELECTOR);
2108
+ if (!(checkedItem instanceof HTMLElement)) return;
2109
+ checkedItem.scrollIntoView({ block: "start" });
2110
+ });
2111
+ }
2112
+ function dayCellButtonClass(opts) {
2113
+ const { isToday, isSelected, isInRange, isRangeStart, isRangeEnd, interactiveDisabled } = opts;
2114
+ const parts = ["w-8 h-8 text-sm flex items-center justify-center rounded-2xl transition-colors"];
2115
+ if (isToday) parts.push("bg-gray-100");
2116
+ if (isSelected && !isToday) parts.push("bg-neutral-200 text-gray-700");
2117
+ if (isInRange && !isToday) parts.push("bg-gray-100/90");
2118
+ if ((isRangeStart || isRangeEnd) && !isToday) parts.push("bg-neutral-300 text-gray-700 opacity-100");
2119
+ parts.push(interactiveDisabled ? "opacity-20 cursor-not-allowed" : "hover:bg-blue-100 cursor-pointer");
2120
+ return parts.filter(Boolean).join(" ").trim();
2121
+ }
1985
2122
  function RenderCalendar(props) {
1986
2123
  const {
1987
2124
  currentDate,
@@ -1996,133 +2133,163 @@ function RenderCalendar(props) {
1996
2133
  goToNextMonth,
1997
2134
  onYearChange,
1998
2135
  onMonthChange,
2136
+ onSelectOpenChange,
1999
2137
  editDateSelect = true,
2000
2138
  datePosition = "bottom",
2001
- calendarSelectContainerClass
2139
+ maxDate,
2140
+ calendarYearRange,
2141
+ calendarSelectContainerClass,
2142
+ floating = false,
2143
+ showTodayButton = true
2002
2144
  } = props;
2003
2145
  const year = currentDate.getFullYear();
2004
- const month = currentDate.getMonth();
2005
- const firstDay = new Date(year, month, 1);
2006
- const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
2007
- const yearOptions = [];
2008
- for (let i = currentYear - 25; i <= currentYear + 20; i++) {
2009
- yearOptions.push({ value: i.toString(), label: `${i}\uB144` });
2010
- }
2011
- const monthOptions = [];
2012
- for (let i = 1; i <= 12; i++) {
2013
- monthOptions.push({ value: i.toString(), label: `${i}\uC6D4` });
2014
- }
2015
- const lastDay = new Date(year, month + 1, 0);
2016
- const startDate = new Date(firstDay);
2017
- startDate.setDate(startDate.getDate() - firstDay.getDay());
2018
- const endDate = new Date(lastDay);
2019
- endDate.setDate(endDate.getDate() + (6 - endDate.getDay()));
2020
- const totalDays = Math.ceil((endDate.getTime() - startDate.getTime()) / (1e3 * 60 * 60 * 24)) + 1;
2146
+ const monthIndex = currentDate.getMonth();
2147
+ const anchorYear = year;
2148
+ const disableNextMonth = disableNextCalendarMonth(year, monthIndex, maxDate);
2149
+ const { leadingEmpty, daysInMonth, trailingEmpty } = getCalendarMonthGrid(year, monthIndex);
2150
+ const yearOptions = buildCalendarYearSelectOptions(maxDate, anchorYear, calendarYearRange);
2151
+ const monthOptions = buildCalendarMonthSelectOptions(year, maxDate);
2152
+ const todayFormatted = formatDate(/* @__PURE__ */ new Date());
2153
+ const selectedSingleYmd = range ? null : inputValue;
2154
+ const rangeStartYmd = rangeStart.trim();
2155
+ const rangeEndYmd = rangeEnd.trim();
2021
2156
  const days = [];
2022
- const currentDateFormatted = formatDate(/* @__PURE__ */ new Date());
2023
- const selectedDate = range ? null : inputValue;
2024
- const handleStopPropagation = (e) => e.stopPropagation();
2025
- const handleYearChange = (value) => onYearChange(parseInt(value));
2026
- const handleMonthChange = (value) => onMonthChange(parseInt(value) - 1);
2027
- for (let i = 0; i < totalDays; i++) {
2028
- const date = new Date(startDate);
2029
- date.setDate(startDate.getDate() + i);
2157
+ for (let i = 0; i < leadingEmpty; i++) {
2158
+ days.push(/* @__PURE__ */ jsx("div", { className: "size-8", "aria-hidden": true }, `lead-${i}`));
2159
+ }
2160
+ for (let d = 1; d <= daysInMonth; d++) {
2161
+ const date = makeLocalDate(year, monthIndex, d);
2030
2162
  const dateFormatted = formatDate(date);
2031
- const isToday = dateFormatted === currentDateFormatted;
2032
- const isSelected = dateFormatted === selectedDate;
2163
+ const isToday = dateFormatted === todayFormatted;
2164
+ const isSelected = dateFormatted === selectedSingleYmd;
2033
2165
  const isInRange = isDateInRange(date, range, rangeStart, rangeEnd);
2034
- const isRangeStart = range && dateFormatted === rangeStart;
2035
- const isRangeEnd = range && dateFormatted === rangeEnd;
2036
- const isCurrentMonth = date.getMonth() === month;
2037
- const rangeStartDate = new Date(rangeStart);
2038
- rangeStartDate.setDate(rangeStartDate.getDate() - 1);
2039
- const isDisabled = range && isSelectingEnd && rangeStart && date < rangeStartDate;
2166
+ const isRangeStartFlag = Boolean(range && dateFormatted === rangeStart);
2167
+ const isRangeEndFlag = Boolean(range && dateFormatted === rangeEnd);
2168
+ const isBeforeRangeStart = range && isSelectingEnd && rangeStartYmd.length === 10 && dateFormatted < rangeStartYmd;
2169
+ const isAfterRangeEnd = range && !isSelectingEnd && rangeEndYmd.length === 10 && dateFormatted > rangeEndYmd;
2170
+ const isGreyedForRange = Boolean(isBeforeRangeStart || isAfterRangeEnd);
2171
+ const isPastMax = maxDate ? isCalendarDayAfterMax(date, maxDate) : false;
2040
2172
  days.push(
2041
2173
  /* @__PURE__ */ jsx(
2042
2174
  "button",
2043
2175
  {
2176
+ type: "button",
2044
2177
  onClick: () => handleDateSelect(date),
2045
- disabled: isDisabled || false,
2046
- className: `
2047
- w-8 h-8 text-sm flex items-center justify-center rounded-2xl transition-colors
2048
- ${!isCurrentMonth ? "text-gray-300" : ""}
2049
- ${isToday ? "bg-gray-100 " : ""}
2050
- ${isInRange && !isToday ? "bg-gray-100/90" : ""}
2051
- ${isSelected && !isToday ? "bg-neutral-200 text-gray-700" : ""}
2052
- ${(isRangeStart || isRangeEnd) && !isToday ? "bg-neutral-300 text-gray-700 opacity-100" : ""}
2053
- ${isDisabled ? "opacity-20 cursor-not-allowed" : "hover:bg-blue-100 cursor-pointer"}
2054
- `,
2055
- children: date.getDate()
2178
+ disabled: isGreyedForRange || isPastMax,
2179
+ className: dayCellButtonClass({
2180
+ isToday,
2181
+ isSelected: Boolean(isSelected),
2182
+ isInRange,
2183
+ isRangeStart: isRangeStartFlag,
2184
+ isRangeEnd: isRangeEndFlag,
2185
+ interactiveDisabled: isGreyedForRange || isPastMax
2186
+ }),
2187
+ children: d
2056
2188
  },
2057
- i
2189
+ d
2058
2190
  )
2059
2191
  );
2060
2192
  }
2061
- const calendarPositionClass = range && calendarPosition === "end" ? "right-0" : "left-0";
2062
- const datePositionClass = datePosition === "top" ? "bottom-full mb-1" : "top-full mt-1";
2063
- return /* @__PURE__ */ jsxs(
2064
- "div",
2065
- {
2066
- className: `${datePositionClass} ${calendarPositionClass} bg-white border border-gray-300 rounded-lg shadow-lg p-3 z-50 min-w-[280px]`,
2067
- onClick: handleStopPropagation,
2068
- onMouseDown: handleStopPropagation,
2069
- children: [
2070
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [
2071
- /* @__PURE__ */ jsx("button", { onClick: goToPreviousMonth, className: "p-1 hover:bg-gray-100 rounded cursor-pointer", children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4 text-balck" }) }),
2072
- editDateSelect ? /* @__PURE__ */ jsxs(
2073
- "div",
2074
- {
2075
- className: "flex items-center gap-2",
2076
- onClick: handleStopPropagation,
2077
- onMouseDown: handleStopPropagation,
2078
- children: [
2079
- /* @__PURE__ */ jsx("div", { onClick: handleStopPropagation, onMouseDown: handleStopPropagation, children: /* @__PURE__ */ jsx(
2080
- Select2,
2081
- {
2082
- value: year.toString(),
2083
- options: yearOptions,
2084
- onChange: handleYearChange,
2085
- className: "cursor-pointer",
2086
- containerClass: calendarSelectContainerClass
2087
- }
2088
- ) }),
2089
- /* @__PURE__ */ jsx("div", { onClick: handleStopPropagation, onMouseDown: handleStopPropagation, children: /* @__PURE__ */ jsx(
2090
- Select2,
2091
- {
2092
- value: (month + 1).toString(),
2093
- options: monthOptions,
2094
- onChange: handleMonthChange,
2095
- className: "cursor-pointer",
2096
- containerClass: calendarSelectContainerClass
2097
- }
2098
- ) })
2099
- ]
2100
- }
2101
- ) : /* @__PURE__ */ jsxs("h3", { className: "text-lg font-semibold", children: [
2102
- year,
2103
- "\uB144 ",
2104
- month + 1,
2105
- "\uC6D4"
2106
- ] }),
2107
- /* @__PURE__ */ jsx("button", { onClick: goToNextMonth, className: "p-1 hover:bg-gray-100 rounded cursor-pointer", children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" }) })
2108
- ] }),
2109
- /* @__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)) }),
2110
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: days })
2111
- ]
2112
- }
2113
- );
2114
- }
2115
-
2116
- // src/components/DatePicker/calendar/config/default.ts
2117
- var CALENDAR_PORTAL_Z_INDEX = Z_INDEX.CALENDAR_PORTAL;
2118
-
2119
- // src/components/DatePicker/calendar/config/calendar.ts
2120
- function parseYmdDateString(value) {
2121
- if (value.length !== 10) return null;
2122
- return parseLocalYmd(value);
2193
+ for (let i = 0; i < trailingEmpty; i++) {
2194
+ days.push(/* @__PURE__ */ jsx("div", { className: "size-8", "aria-hidden": true }, `trail-${i}`));
2195
+ }
2196
+ const calendarPositionClass = !floating && range && calendarPosition === "end" ? "right-0" : !floating ? "left-0" : "";
2197
+ const datePositionClass = floating ? "" : datePosition === "top" ? "bottom-full mb-1" : "top-full mt-1";
2198
+ const panelClass = floating ? CALENDAR_PANEL_BASE_CLASS : cn(CALENDAR_PANEL_ANCHORED_CLASS, datePositionClass, calendarPositionClass);
2199
+ const handleYearSelectOpenChange = (open) => {
2200
+ onSelectOpenChange?.(open);
2201
+ if (open) scrollOpenYearSelectCheckedIntoView();
2202
+ };
2203
+ const sel = CALENDAR_SELECT_CLASSNAMES;
2204
+ const containerClass = cn(sel.containerClass, calendarSelectContainerClass);
2205
+ const stopBubble = (e) => e.stopPropagation();
2206
+ const isTodaySelectable = canSelectToday({
2207
+ maxDate,
2208
+ range,
2209
+ isSelectingEnd,
2210
+ rangeStart,
2211
+ rangeEnd
2212
+ });
2213
+ return /* @__PURE__ */ jsxs("div", { className: panelClass, onClick: stopBubble, onMouseDown: stopBubble, children: [
2214
+ /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
2215
+ /* @__PURE__ */ jsx(
2216
+ "button",
2217
+ {
2218
+ type: "button",
2219
+ onClick: goToPreviousMonth,
2220
+ className: "cursor-pointer rounded p-1 hover:bg-gray-100",
2221
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
2222
+ }
2223
+ ),
2224
+ editDateSelect ? /* @__PURE__ */ jsxs(
2225
+ "div",
2226
+ {
2227
+ role: "presentation",
2228
+ className: "flex items-center gap-2",
2229
+ onClick: stopBubble,
2230
+ onMouseDown: stopBubble,
2231
+ children: [
2232
+ /* @__PURE__ */ jsx(
2233
+ Select2,
2234
+ {
2235
+ value: year.toString(),
2236
+ options: yearOptions,
2237
+ onChange: (value) => onYearChange(Number.parseInt(value, 10)),
2238
+ containerClassName: containerClass,
2239
+ triggerClassName: sel.triggerClassName,
2240
+ contentClassName: cn(YEAR_SELECT_CONTENT_EXTRA_CLASS, sel.contentClassName),
2241
+ disableScrollButtonEvents: true,
2242
+ onOpenChange: handleYearSelectOpenChange
2243
+ }
2244
+ ),
2245
+ /* @__PURE__ */ jsx(
2246
+ Select2,
2247
+ {
2248
+ value: (monthIndex + 1).toString(),
2249
+ options: monthOptions,
2250
+ onChange: (value) => onMonthChange(Number.parseInt(value, 10) - 1),
2251
+ containerClassName: containerClass,
2252
+ triggerClassName: sel.triggerClassName,
2253
+ contentClassName: sel.contentClassName,
2254
+ onOpenChange: (open) => onSelectOpenChange?.(open)
2255
+ }
2256
+ )
2257
+ ]
2258
+ }
2259
+ ) : /* @__PURE__ */ jsxs("h3", { className: "text-lg font-semibold", children: [
2260
+ String(year).padStart(4, "0"),
2261
+ "\uB144 ",
2262
+ monthIndex + 1,
2263
+ "\uC6D4"
2264
+ ] }),
2265
+ /* @__PURE__ */ jsx(
2266
+ "button",
2267
+ {
2268
+ type: "button",
2269
+ onClick: goToNextMonth,
2270
+ disabled: disableNextMonth,
2271
+ className: cn(
2272
+ "rounded p-1",
2273
+ disableNextMonth ? "cursor-not-allowed opacity-30" : "cursor-pointer hover:bg-gray-100"
2274
+ ),
2275
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
2276
+ }
2277
+ )
2278
+ ] }),
2279
+ /* @__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)) }),
2280
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: days }),
2281
+ 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 }) }),
2282
+ 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(
2283
+ "button",
2284
+ {
2285
+ type: "button",
2286
+ onClick: () => handleDateSelect(/* @__PURE__ */ new Date()),
2287
+ className: "cursor-pointer text-sm text-gray-800 hover:text-main",
2288
+ children: "Today"
2289
+ }
2290
+ ) })
2291
+ ] });
2123
2292
  }
2124
-
2125
- // src/components/DatePicker/calendar/hooks/useCalendarState.ts
2126
2293
  function useCalendarState(props) {
2127
2294
  const { datePosition = "bottom", disabled = false, calendarRef } = props;
2128
2295
  const [isCalendarOpen, setIsCalendarOpen] = useState(false);
@@ -2171,16 +2338,16 @@ function useCalendarState(props) {
2171
2338
  setIsCalendarOpen(false);
2172
2339
  };
2173
2340
  const goToPreviousMonth = () => {
2174
- setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
2341
+ setCurrentDate(makeLocalDate(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
2175
2342
  };
2176
2343
  const goToNextMonth = () => {
2177
- setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1));
2344
+ setCurrentDate(makeLocalDate(currentDate.getFullYear(), currentDate.getMonth() + 1, 1));
2178
2345
  };
2179
2346
  const handleYearChange = (year) => {
2180
- setCurrentDate(new Date(year, currentDate.getMonth(), 1));
2347
+ setCurrentDate(makeLocalDate(year, currentDate.getMonth(), 1));
2181
2348
  };
2182
2349
  const handleMonthChange = (month) => {
2183
- setCurrentDate(new Date(currentDate.getFullYear(), month, 1));
2350
+ setCurrentDate(makeLocalDate(currentDate.getFullYear(), month, 1));
2184
2351
  };
2185
2352
  return {
2186
2353
  isCalendarOpen,
@@ -2219,11 +2386,15 @@ function useFloatingCalendarPosition(anchorRef, isOpen2, placement, align = "sta
2219
2386
  setStyle({ ...base, top: rect.bottom + CALENDAR_GAP });
2220
2387
  }
2221
2388
  };
2389
+ const handleScroll = (e) => {
2390
+ if (e.target?.closest?.('[data-slot="select-content"]')) return;
2391
+ update();
2392
+ };
2222
2393
  update();
2223
- window.addEventListener("scroll", update, true);
2394
+ window.addEventListener("scroll", handleScroll, true);
2224
2395
  window.addEventListener("resize", update);
2225
2396
  return () => {
2226
- window.removeEventListener("scroll", update, true);
2397
+ window.removeEventListener("scroll", handleScroll, true);
2227
2398
  window.removeEventListener("resize", update);
2228
2399
  };
2229
2400
  }, [isOpen2, placement, align, anchorRef]);
@@ -2303,6 +2474,7 @@ function DatePickerRange(props) {
2303
2474
  return;
2304
2475
  }
2305
2476
  onRangeChange?.(formatted, endInputValue);
2477
+ if (isCalendarOpen && calendarPosition === "start") navigateToDateValue(formatted);
2306
2478
  } else if (formatted.length === 0) {
2307
2479
  onRangeChange?.(formatted, endInputValue);
2308
2480
  }
@@ -2316,6 +2488,7 @@ function DatePickerRange(props) {
2316
2488
  return;
2317
2489
  }
2318
2490
  onRangeChange?.(startInputValue, formatted);
2491
+ if (isCalendarOpen && calendarPosition === "end") navigateToDateValue(formatted);
2319
2492
  } else if (formatted.length === 0) {
2320
2493
  onRangeChange?.(startInputValue, formatted);
2321
2494
  }
@@ -2490,6 +2663,7 @@ function DatePickerSingle(props) {
2490
2663
  autoDatePosition,
2491
2664
  containerRef,
2492
2665
  toggleCalendar,
2666
+ navigateToDateValue,
2493
2667
  closeCalendar,
2494
2668
  goToPreviousMonth,
2495
2669
  goToNextMonth,
@@ -2507,7 +2681,7 @@ function DatePickerSingle(props) {
2507
2681
  const dateMatch = formatted.match(/^(\d{4})-(\d{2})-(\d{2})$/);
2508
2682
  if (dateMatch) {
2509
2683
  const [, year, month, day] = dateMatch;
2510
- const date = new Date(
2684
+ const date = makeLocalDate(
2511
2685
  Number.parseInt(year, 10),
2512
2686
  Number.parseInt(month, 10) - 1,
2513
2687
  Number.parseInt(day, 10)
@@ -2519,6 +2693,7 @@ function DatePickerSingle(props) {
2519
2693
  return;
2520
2694
  }
2521
2695
  onChange?.(formatted);
2696
+ if (isCalendarOpen) navigateToDateValue(formatted);
2522
2697
  }
2523
2698
  }
2524
2699
  } else {
@@ -3455,9 +3630,10 @@ function ConfirmModal(props) {
3455
3630
  ] }),
3456
3631
  /* @__PURE__ */ jsxs("div", { className: "flex h-8 items-center justify-end gap-2", children: [
3457
3632
  !isHideOkButton && /* @__PURE__ */ jsx(
3458
- Button,
3633
+ DebouncedButton,
3459
3634
  {
3460
3635
  ref: okButtonRef,
3636
+ debounceDelay: 500,
3461
3637
  variant: "outline",
3462
3638
  onClick: handleOkClick,
3463
3639
  className: "focus-visible:ring-2 focus-visible:ring-blue-400 focus-visible:ring-offset-1",
@@ -6531,7 +6707,7 @@ function CascadingSelectFieldInner(props) {
6531
6707
  disabled,
6532
6708
  hasError,
6533
6709
  options: toSelectOptions(primaryOptions),
6534
- containerClass: firstSelectClass.containerClassName,
6710
+ containerClassName: firstSelectClass.containerClassName,
6535
6711
  triggerClassName: firstSelectClass.triggerClassName,
6536
6712
  canReset: true,
6537
6713
  onChange: (nextPrimary) => {
@@ -6548,7 +6724,7 @@ function CascadingSelectFieldInner(props) {
6548
6724
  disabled: disabled || !primary || secondaryOptions.length === 0,
6549
6725
  hasError,
6550
6726
  options: toSelectOptions(secondaryOptions),
6551
- containerClass: secondSelectClass.containerClassName,
6727
+ containerClassName: secondSelectClass.containerClassName,
6552
6728
  triggerClassName: secondSelectClass.triggerClassName,
6553
6729
  canReset: true,
6554
6730
  onChange: (nextSecondary) => {
@@ -6592,7 +6768,8 @@ function DateField(props) {
6592
6768
  readOnly = false,
6593
6769
  className = "w-40",
6594
6770
  datePosition = "bottom",
6595
- disableFutureDates = false
6771
+ disableFutureDates = false,
6772
+ calendarSelectContainerClass = "w-20"
6596
6773
  } = config;
6597
6774
  const maxDate = disableFutureDates ? formatDate(/* @__PURE__ */ new Date()) : void 0;
6598
6775
  if (readOnly) {
@@ -6609,7 +6786,8 @@ function DateField(props) {
6609
6786
  className: "w-full",
6610
6787
  validation: hasError,
6611
6788
  datePosition,
6612
- maxDate
6789
+ maxDate,
6790
+ calendarSelectContainerClass
6613
6791
  }
6614
6792
  ) });
6615
6793
  }
@@ -6799,7 +6977,9 @@ function RadioField(props) {
6799
6977
  containerClassName: cn2("justify-start", fieldControlWrapClass(className)),
6800
6978
  options: options.map((option) => ({
6801
6979
  label: option.label,
6802
- value: String(option.value)
6980
+ value: String(option.value),
6981
+ disabled: option.disabled,
6982
+ disabledMessage: option.disabledMessage
6803
6983
  }))
6804
6984
  }
6805
6985
  );
@@ -6837,7 +7017,7 @@ function SelectField(props) {
6837
7017
  disabled,
6838
7018
  hasError,
6839
7019
  options: selectOptions,
6840
- containerClass: "flex w-full min-w-0 max-w-full items-center",
7020
+ containerClassName: "flex w-full min-w-0 max-w-full items-center",
6841
7021
  className: "w-full min-w-0",
6842
7022
  triggerClassName: cn2(
6843
7023
  "h-8 w-full min-w-0 max-w-full rounded border bg-white px-2 py-1 text-sm shadow-none",
@@ -6949,11 +7129,12 @@ function SelectWithInputField(props) {
6949
7129
  disabled,
6950
7130
  hasError,
6951
7131
  options: toSelectOptions(options),
6952
- containerClass: selectClass.containerClassName,
7132
+ containerClassName: selectClass.containerClassName,
6953
7133
  triggerClassName: selectClass.triggerClassName,
6954
7134
  onChange: (nextPrefix) => {
6955
7135
  setSelectedPrefix(nextPrefix);
6956
7136
  updateCombined(nextPrefix, inputPart);
7137
+ onBlur();
6957
7138
  }
6958
7139
  }
6959
7140
  ),
@@ -7088,7 +7269,7 @@ function useBlurValidation(name, options) {
7088
7269
  (value, fieldOnChange) => {
7089
7270
  markHadValueIfFilled(value);
7090
7271
  fieldOnChange(value);
7091
- if (options.revalidateOnChange || shouldValidateEmptyOnChange(value)) {
7272
+ if (options.revalidateOnChange || options.validateEmptyOnChange && shouldValidateEmptyOnChange(value)) {
7092
7273
  void trigger(name);
7093
7274
  }
7094
7275
  void triggerRelatedFields();
@@ -7096,6 +7277,7 @@ function useBlurValidation(name, options) {
7096
7277
  [
7097
7278
  markHadValueIfFilled,
7098
7279
  options.revalidateOnChange,
7280
+ options.validateEmptyOnChange,
7099
7281
  shouldValidateEmptyOnChange,
7100
7282
  trigger,
7101
7283
  name,
@@ -7116,9 +7298,7 @@ var REVALIDATE_ON_CHANGE_TYPES = /* @__PURE__ */ new Set([
7116
7298
  "radio",
7117
7299
  "switch",
7118
7300
  "select",
7119
- "date",
7120
7301
  "cascading-select",
7121
- "select-with-input",
7122
7302
  "input-with-button",
7123
7303
  "address"
7124
7304
  ]);
@@ -7139,14 +7319,15 @@ function FormFieldRow(props) {
7139
7319
  const { control } = useFormContext();
7140
7320
  const { isSubmitted } = useFormState({ control });
7141
7321
  const { name, label, type = "input", required = false, renderCustomField, contentAlign = "left" } = config;
7322
+ const revalidateOnChange = REVALIDATE_ON_CHANGE_TYPES.has(type);
7142
7323
  const { handleBlur, handleChange } = useBlurValidation(name, {
7143
7324
  required,
7144
- revalidateOnChange: REVALIDATE_ON_CHANGE_TYPES.has(type),
7325
+ revalidateOnChange,
7326
+ validateEmptyOnChange: revalidateOnChange,
7145
7327
  revalidateFields: config.revalidateFields
7146
7328
  });
7147
7329
  const showError = Boolean(fieldState.error?.message) && (isSubmitted || fieldState.isTouched);
7148
7330
  const isTextarea = type === "textarea";
7149
- const revalidateOnChange = REVALIDATE_ON_CHANGE_TYPES.has(type);
7150
7331
  const wrappedField = wrapFieldHandlers(field, {
7151
7332
  onBlur: () => handleBlur(() => field.onBlur()),
7152
7333
  onChange: (value) => {
@@ -7251,6 +7432,7 @@ function rowPropsToFieldConfig(props) {
7251
7432
  canReset,
7252
7433
  datePosition,
7253
7434
  disableFutureDates,
7435
+ calendarSelectContainerClass,
7254
7436
  revalidateFields,
7255
7437
  renderCustomField,
7256
7438
  colspan,
@@ -7281,6 +7463,7 @@ function rowPropsToFieldConfig(props) {
7281
7463
  canReset,
7282
7464
  datePosition,
7283
7465
  disableFutureDates,
7466
+ calendarSelectContainerClass,
7284
7467
  revalidateFields,
7285
7468
  renderCustomField,
7286
7469
  colspan,
@@ -7445,7 +7628,10 @@ function PageFilter(props) {
7445
7628
  {
7446
7629
  placeholder: option.placeholder,
7447
7630
  value: currentValue,
7448
- onChange: handleInputChange(optionKey)
7631
+ onChange: handleInputChange(optionKey),
7632
+ onKeyDown: (e) => {
7633
+ if (e.key === "Enter") handleSubmit();
7634
+ }
7449
7635
  }
7450
7636
  ) }, String(optionKey));
7451
7637
  case "select":
@@ -7455,7 +7641,8 @@ function PageFilter(props) {
7455
7641
  value: currentValue,
7456
7642
  onChange: handleSelectChange(optionKey),
7457
7643
  options: option.options,
7458
- placeholder: "\uC120\uD0DD\uD574\uC8FC\uC138\uC694"
7644
+ placeholder: "\uC120\uD0DD",
7645
+ containerClassName: option.containerClassName ?? "w-30"
7459
7646
  }
7460
7647
  ) }, String(optionKey));
7461
7648
  case "radio":
@@ -7520,9 +7707,84 @@ function PageFilter(props) {
7520
7707
  }
7521
7708
  );
7522
7709
  }
7710
+ var VARIANT_STYLES = {
7711
+ light: {
7712
+ container: "bg-white border-b border-gray-200",
7713
+ activeTab: "bg-gray-50 border-gray-200 text-gray-800 shadow-sm",
7714
+ inactiveTab: "bg-white border-gray-100 text-gray-400 hover:text-gray-600 hover:border-gray-200 hover:bg-gray-50",
7715
+ activeText: "font-semibold text-gray-800",
7716
+ inactiveText: "font-normal",
7717
+ indicator: "bg-blue-500",
7718
+ closeBtn: "hover:bg-gray-300/60",
7719
+ plusBtnArea: "border-l border-gray-100 bg-white",
7720
+ plusBtn: "text-gray-400 hover:text-gray-600 hover:bg-gray-100",
7721
+ fadeOpacity: 0.12,
7722
+ tabPy: "py-1.5",
7723
+ tabPx: "px-3",
7724
+ tabTextSize: "text-xs",
7725
+ scrollPadding: "px-3 pt-1.5",
7726
+ rounded: "rounded-t-md"
7727
+ },
7728
+ dark: {
7729
+ container: "bg-gray-900 border-b border-gray-700",
7730
+ activeTab: "bg-gray-800 border-gray-600 text-white shadow-sm",
7731
+ inactiveTab: "bg-gray-900 border-gray-700/50 text-gray-500 hover:text-gray-300 hover:border-gray-600 hover:bg-gray-800",
7732
+ activeText: "font-semibold text-white",
7733
+ inactiveText: "font-normal",
7734
+ indicator: "bg-indigo-400",
7735
+ closeBtn: "hover:bg-gray-600/60",
7736
+ plusBtnArea: "border-l border-gray-700 bg-gray-900",
7737
+ plusBtn: "text-gray-500 hover:text-gray-300 hover:bg-gray-700",
7738
+ fadeOpacity: 0.4,
7739
+ tabPy: "py-1.5",
7740
+ tabPx: "px-3",
7741
+ tabTextSize: "text-xs",
7742
+ scrollPadding: "px-3 pt-1.5",
7743
+ rounded: "rounded-t-md"
7744
+ },
7745
+ "system-light": {
7746
+ container: "bg-gray-200 border-b border-gray-300",
7747
+ activeTab: "bg-white border-gray-300 text-slate-800 shadow-sm",
7748
+ inactiveTab: "bg-transparent border-gray-300/80 text-slate-500 hover:text-slate-700 hover:bg-gray-200 hover:border-gray-300",
7749
+ activeText: "font-semibold text-slate-800",
7750
+ inactiveText: "font-medium",
7751
+ indicator: "bg-[#2b2b2b]",
7752
+ closeBtn: "hover:bg-gray-300",
7753
+ plusBtnArea: "border-l border-gray-300 bg-gray-200",
7754
+ plusBtn: "text-slate-400 hover:text-slate-600 hover:bg-gray-300",
7755
+ fadeOpacity: 0.15,
7756
+ tabPy: "py-2.5",
7757
+ tabPx: "px-5",
7758
+ tabTextSize: "text-sm",
7759
+ scrollPadding: "px-3 pt-2",
7760
+ rounded: "rounded-t-none"
7761
+ },
7762
+ "system-dark": {
7763
+ container: "bg-slate-900 border-b border-slate-700",
7764
+ activeTab: "bg-slate-800 border-slate-600 text-slate-100 shadow-sm",
7765
+ inactiveTab: "bg-transparent border-slate-700/50 text-slate-400 hover:text-slate-200 hover:bg-slate-800/60 hover:border-slate-700",
7766
+ activeText: "font-semibold text-slate-100",
7767
+ inactiveText: "font-medium",
7768
+ indicator: "bg-indigo-400",
7769
+ closeBtn: "hover:bg-slate-600/60",
7770
+ plusBtnArea: "border-l border-slate-700 bg-slate-900",
7771
+ plusBtn: "text-slate-400 hover:text-slate-200 hover:bg-slate-700",
7772
+ fadeOpacity: 0.45,
7773
+ tabPy: "py-2.5",
7774
+ tabPx: "px-5",
7775
+ tabTextSize: "text-sm",
7776
+ scrollPadding: "px-4 pt-2",
7777
+ rounded: "rounded-t-none"
7778
+ }
7779
+ };
7523
7780
  var PLUS_BTN_WIDTH = 36;
7781
+ function easedFade(dir, opacity) {
7782
+ const c = (t) => `rgba(0,0,0,${(opacity * t).toFixed(3)})`;
7783
+ return `linear-gradient(${dir}, ${c(1)} 0%, ${c(0.72)} 20%, ${c(0.38)} 45%, ${c(0.1)} 70%, transparent 100%)`;
7784
+ }
7524
7785
  function MultiTabBar(props) {
7525
- const { tabs, activeTabId, onTabClick, onTabClose, onTabAdd } = props;
7786
+ const { tabs, activeTabId, onTabClick, onTabClose, onTabAdd, variant = "light", primaryColor } = props;
7787
+ const s = VARIANT_STYLES[variant];
7526
7788
  const scrollRef = useRef(null);
7527
7789
  const tabRefs = useRef(/* @__PURE__ */ new Map());
7528
7790
  const [showLeftFade, setShowLeftFade] = useState(false);
@@ -7575,13 +7837,23 @@ function MultiTabBar(props) {
7575
7837
  const handleStopDrag = () => {
7576
7838
  isDragging.current = false;
7577
7839
  };
7840
+ useEffect(() => {
7841
+ const el = scrollRef.current;
7842
+ if (!el) return;
7843
+ const onWheel = (e) => {
7844
+ e.preventDefault();
7845
+ el.scrollLeft += e.deltaY || e.deltaX;
7846
+ };
7847
+ el.addEventListener("wheel", onWheel, { passive: false });
7848
+ return () => el.removeEventListener("wheel", onWheel);
7849
+ }, []);
7578
7850
  if (tabs.length === 0) return null;
7579
- return /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 w-full overflow-hidden bg-white border-b border-gray-200", children: /* @__PURE__ */ jsxs("div", { className: "relative flex items-stretch", children: [
7851
+ return /* @__PURE__ */ jsx("div", { className: `flex-shrink-0 w-full overflow-hidden ${s.container}`, children: /* @__PURE__ */ jsxs("div", { className: "relative flex items-stretch", children: [
7580
7852
  showLeftFade && /* @__PURE__ */ jsx(
7581
7853
  "div",
7582
7854
  {
7583
7855
  className: "absolute left-0 top-0 bottom-0 w-10 pointer-events-none z-10",
7584
- style: { background: "linear-gradient(to right, white 30%, transparent)" }
7856
+ style: { background: easedFade("to right", s.fadeOpacity) }
7585
7857
  }
7586
7858
  ),
7587
7859
  showRightFade && /* @__PURE__ */ jsx(
@@ -7590,7 +7862,7 @@ function MultiTabBar(props) {
7590
7862
  className: "absolute top-0 bottom-0 w-10 pointer-events-none z-10",
7591
7863
  style: {
7592
7864
  right: PLUS_BTN_WIDTH,
7593
- background: "linear-gradient(to left, white 30%, transparent)"
7865
+ background: easedFade("to left", s.fadeOpacity)
7594
7866
  }
7595
7867
  }
7596
7868
  ),
@@ -7598,7 +7870,7 @@ function MultiTabBar(props) {
7598
7870
  "div",
7599
7871
  {
7600
7872
  ref: scrollRef,
7601
- className: "flex-1 min-w-0 flex items-end gap-0.5 px-3 pt-1.5 overflow-x-auto [&::-webkit-scrollbar]:hidden",
7873
+ className: `flex-1 min-w-0 flex items-end gap-0.5 ${s.scrollPadding} overflow-x-auto [&::-webkit-scrollbar]:hidden`,
7602
7874
  style: { scrollbarWidth: "none" },
7603
7875
  onScroll: updateFades,
7604
7876
  onMouseDown: handleMouseDown,
@@ -7619,17 +7891,23 @@ function MultiTabBar(props) {
7619
7891
  if (!isActive) onTabClick(tab.id);
7620
7892
  },
7621
7893
  className: `
7622
- group relative flex items-center gap-1.5 px-3 py-1.5 flex-shrink-0
7623
- rounded-t-md border border-b-0 cursor-pointer select-none
7894
+ group relative flex items-center gap-1.5 ${s.tabPx} ${s.tabPy} flex-shrink-0
7895
+ ${s.rounded} border border-b-0 cursor-pointer select-none
7624
7896
  transition-colors duration-100
7625
- ${isActive ? "bg-gray-50 border-gray-200 text-gray-800 shadow-sm" : "bg-white border-gray-100 text-gray-400 hover:text-gray-600 hover:border-gray-200 hover:bg-gray-50"}
7897
+ ${isActive ? s.activeTab : s.inactiveTab}
7626
7898
  `,
7627
7899
  children: [
7628
- isActive && /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 right-0 h-0.5 rounded-t-md bg-blue-500" }),
7900
+ isActive && /* @__PURE__ */ jsx(
7901
+ "div",
7902
+ {
7903
+ className: `absolute top-0 left-0 right-0 h-0.5 ${s.rounded} ${s.indicator}`,
7904
+ style: primaryColor ? { backgroundColor: primaryColor } : void 0
7905
+ }
7906
+ ),
7629
7907
  /* @__PURE__ */ jsx(
7630
7908
  "span",
7631
7909
  {
7632
- className: `text-xs max-w-35 truncate leading-none ${isActive ? "font-semibold text-gray-800" : "font-normal"}`,
7910
+ className: `${s.tabTextSize} max-w-35 truncate leading-none ${isActive ? s.activeText : s.inactiveText}`,
7633
7911
  children: tab.label
7634
7912
  }
7635
7913
  ),
@@ -7642,7 +7920,8 @@ function MultiTabBar(props) {
7642
7920
  },
7643
7921
  className: `
7644
7922
  flex-shrink-0 w-3.5 h-3.5 flex items-center justify-center
7645
- rounded hover:bg-gray-300/60 transition-all duration-100 cursor-pointer
7923
+ rounded transition-all duration-100 cursor-pointer
7924
+ ${s.closeBtn}
7646
7925
  ${isActive ? "opacity-100" : "opacity-40 group-hover:opacity-60"}
7647
7926
  `,
7648
7927
  children: /* @__PURE__ */ jsx(X, { size: 10, strokeWidth: 2.5 })
@@ -7658,14 +7937,14 @@ function MultiTabBar(props) {
7658
7937
  onTabAdd && /* @__PURE__ */ jsx(
7659
7938
  "div",
7660
7939
  {
7661
- className: "flex-shrink-0 flex items-center justify-center border-l border-gray-100 bg-white",
7940
+ className: `flex-shrink-0 flex items-center justify-center ${s.plusBtnArea}`,
7662
7941
  style: { width: PLUS_BTN_WIDTH },
7663
7942
  children: /* @__PURE__ */ jsx(
7664
7943
  "button",
7665
7944
  {
7666
7945
  onClick: onTabAdd,
7667
7946
  title: "\uD604\uC7AC \uD398\uC774\uC9C0\uB97C \uC0C8 \uD0ED\uC73C\uB85C \uC5F4\uAE30",
7668
- className: "w-6 h-6 flex items-center justify-center rounded text-gray-400 cursor-pointer hover:text-gray-600 hover:bg-gray-100 transition-colors",
7947
+ className: `w-6 h-6 flex items-center justify-center rounded cursor-pointer transition-colors ${s.plusBtn}`,
7669
7948
  children: /* @__PURE__ */ jsx(Plus, { size: 13, strokeWidth: 2 })
7670
7949
  }
7671
7950
  )
@@ -8450,19 +8729,212 @@ function FileUploader(props) {
8450
8729
  /* @__PURE__ */ jsx(FilePreviewViewer, { ...viewer.viewerProps, fileTypes, fileNames: files.map((f) => f.name) })
8451
8730
  ] });
8452
8731
  }
8732
+ var VARIANT_STYLES2 = {
8733
+ light: {
8734
+ aside: "bg-white border-r border-gray-200",
8735
+ sectionTitle: "text-xs font-semibold text-gray-500 tracking-wider mb-3",
8736
+ itemActive: "bg-blue-50 text-blue-600",
8737
+ itemInactive: "text-gray-700 hover:bg-gray-100",
8738
+ subItemActive: "text-blue-600 font-medium",
8739
+ subItemInactive: "text-gray-600 hover:bg-gray-50 hover:text-gray-900",
8740
+ childGroupBorder: "border-gray-100",
8741
+ childActive: "bg-blue-50 text-blue-600 font-medium",
8742
+ childInactive: "text-gray-600 hover:bg-gray-50 hover:text-gray-900",
8743
+ grandchildActive: "bg-blue-50 text-blue-600 font-medium",
8744
+ grandchildInactive: "text-gray-500 hover:bg-gray-50 hover:text-gray-900",
8745
+ collapseBtn: "bg-gray-50 hover:bg-gray-100",
8746
+ collapseIcon: "var(--color-sub-darkgray)",
8747
+ rounded: "rounded-lg",
8748
+ itemPy: "py-2",
8749
+ childPy: "py-1.5",
8750
+ grandchildPy: "py-1",
8751
+ itemFont: "font-semibold",
8752
+ itemTextSize: "text-sm",
8753
+ grandchildTextSize: "text-xs",
8754
+ iconClass: "w-4 h-4 flex-shrink-0",
8755
+ activeItemStyle: {},
8756
+ activeChildStyle: {},
8757
+ activeGrandchildStyle: {}
8758
+ },
8759
+ dark: {
8760
+ aside: "bg-gray-900 border-r border-gray-700",
8761
+ sectionTitle: "text-xs font-semibold text-gray-400 tracking-wider mb-3",
8762
+ itemActive: "bg-gray-700 text-white",
8763
+ itemInactive: "text-gray-300 hover:bg-gray-800",
8764
+ subItemActive: "text-blue-400 font-medium",
8765
+ subItemInactive: "text-gray-400 hover:bg-gray-800 hover:text-gray-100",
8766
+ childGroupBorder: "border-gray-700",
8767
+ childActive: "bg-gray-700 text-white font-medium",
8768
+ childInactive: "text-gray-400 hover:bg-gray-800 hover:text-gray-100",
8769
+ grandchildActive: "bg-gray-700 text-blue-400 font-medium",
8770
+ grandchildInactive: "text-gray-500 hover:bg-gray-800 hover:text-gray-100",
8771
+ collapseBtn: "bg-gray-800 hover:bg-gray-700",
8772
+ collapseIcon: "#9ca3af",
8773
+ rounded: "rounded-lg",
8774
+ itemPy: "py-2",
8775
+ childPy: "py-1.5",
8776
+ grandchildPy: "py-1",
8777
+ itemFont: "font-semibold",
8778
+ itemTextSize: "text-sm",
8779
+ grandchildTextSize: "text-xs",
8780
+ iconClass: "w-4 h-4 flex-shrink-0",
8781
+ activeItemStyle: {},
8782
+ activeChildStyle: {},
8783
+ activeGrandchildStyle: {}
8784
+ },
8785
+ "system-dark": {
8786
+ aside: "bg-slate-900 border-r border-slate-700",
8787
+ sectionTitle: "text-[10px] font-bold text-slate-500 uppercase tracking-[0.12em] mb-2 pb-2 border-b border-slate-700/70",
8788
+ itemActive: "bg-slate-800 text-white",
8789
+ itemInactive: "text-slate-300 hover:bg-slate-800/60 hover:text-white",
8790
+ subItemActive: "text-slate-100 font-medium",
8791
+ subItemInactive: "text-slate-400 hover:bg-slate-800/50 hover:text-slate-200",
8792
+ childGroupBorder: "border-slate-700/60",
8793
+ childActive: "bg-slate-800/70 text-slate-100 font-medium",
8794
+ childInactive: "text-slate-400 hover:bg-slate-800/50 hover:text-slate-200",
8795
+ grandchildActive: "text-slate-100 font-medium",
8796
+ grandchildInactive: "text-slate-500 hover:bg-slate-800/50 hover:text-slate-200",
8797
+ collapseBtn: "bg-slate-800 hover:bg-slate-700",
8798
+ collapseIcon: "#94a3b8",
8799
+ rounded: "rounded-none",
8800
+ itemPy: "py-3",
8801
+ childPy: "py-[10px]",
8802
+ grandchildPy: "py-2",
8803
+ itemFont: "font-medium",
8804
+ itemTextSize: "text-sm",
8805
+ grandchildTextSize: "text-xs",
8806
+ iconClass: "w-4.5 h-4.5 flex-shrink-0",
8807
+ activeItemStyle: { borderLeft: "3px solid #818cf8", paddingLeft: "9px" },
8808
+ activeChildStyle: { borderLeft: "2px solid #818cf8", paddingLeft: "14px" },
8809
+ activeGrandchildStyle: { borderLeft: "2px solid #818cf8", paddingLeft: "10px" }
8810
+ },
8811
+ "system-light": {
8812
+ aside: "bg-gray-50 border-r border-gray-200",
8813
+ sectionTitle: "text-[10px] font-bold text-slate-400 uppercase tracking-[0.12em] mb-2 pb-2 border-b border-gray-200",
8814
+ itemActive: "font-medium",
8815
+ itemInactive: "text-slate-700 hover:bg-gray-100",
8816
+ subItemActive: "text-[#2b2b2b] font-medium",
8817
+ subItemInactive: "text-slate-500 hover:bg-gray-100 hover:text-slate-800",
8818
+ childGroupBorder: "border-gray-200",
8819
+ childActive: "font-medium",
8820
+ childInactive: "text-slate-500 hover:bg-gray-100 hover:text-slate-800",
8821
+ grandchildActive: "text-[#2b2b2b] font-medium",
8822
+ grandchildInactive: "text-slate-500 hover:bg-gray-100 hover:text-slate-800",
8823
+ collapseBtn: "bg-gray-100 hover:bg-gray-200",
8824
+ collapseIcon: "#64748b",
8825
+ rounded: "rounded-none",
8826
+ itemPy: "py-3",
8827
+ childPy: "py-[10px]",
8828
+ grandchildPy: "py-2",
8829
+ itemFont: "font-medium",
8830
+ itemTextSize: "text-[15px]",
8831
+ grandchildTextSize: "text-sm",
8832
+ iconClass: "w-4.5 h-4.5 flex-shrink-0",
8833
+ activeItemStyle: {
8834
+ color: "#2b2b2b",
8835
+ backgroundColor: "color-mix(in srgb, #2b2b2b 10%, transparent)",
8836
+ borderLeft: "3px solid #2b2b2b",
8837
+ paddingLeft: "9px"
8838
+ },
8839
+ activeChildStyle: {
8840
+ color: "#2b2b2b",
8841
+ backgroundColor: "color-mix(in srgb, #2b2b2b 10%, transparent)",
8842
+ borderLeft: "2px solid #2b2b2b",
8843
+ paddingLeft: "14px"
8844
+ },
8845
+ activeGrandchildStyle: {
8846
+ color: "#2b2b2b",
8847
+ backgroundColor: "color-mix(in srgb, #2b2b2b 10%, transparent)",
8848
+ borderLeft: "2px solid #2b2b2b",
8849
+ paddingLeft: "10px"
8850
+ }
8851
+ }
8852
+ };
8853
+ function tintedStyle(color) {
8854
+ return {
8855
+ color,
8856
+ backgroundColor: `color-mix(in srgb, ${color} 12%, transparent)`
8857
+ };
8858
+ }
8859
+ function textStyle(color) {
8860
+ return { color };
8861
+ }
8862
+ function solidStyle(color) {
8863
+ return {
8864
+ backgroundColor: `color-mix(in srgb, ${color} 25%, transparent)`,
8865
+ color: "#ffffff",
8866
+ boxShadow: `inset 3px 0 0 ${color}`
8867
+ };
8868
+ }
8869
+ var ACCENT_APPLIERS = {
8870
+ light: {
8871
+ itemActive: tintedStyle,
8872
+ subItemActive: textStyle,
8873
+ childActive: tintedStyle,
8874
+ grandchildActive: textStyle
8875
+ },
8876
+ dark: {
8877
+ itemActive: textStyle,
8878
+ subItemActive: textStyle,
8879
+ childActive: textStyle,
8880
+ grandchildActive: textStyle
8881
+ },
8882
+ "system-dark": {
8883
+ itemActive: (c) => ({
8884
+ ...solidStyle(c),
8885
+ boxShadow: void 0,
8886
+ borderLeft: `3px solid ${c}`,
8887
+ paddingLeft: "9px"
8888
+ }),
8889
+ subItemActive: textStyle,
8890
+ childActive: (c) => ({
8891
+ ...solidStyle(c),
8892
+ boxShadow: void 0,
8893
+ borderLeft: `2px solid ${c}`,
8894
+ paddingLeft: "14px"
8895
+ }),
8896
+ grandchildActive: (c) => ({
8897
+ ...textStyle(c),
8898
+ borderLeft: `2px solid ${c}`,
8899
+ paddingLeft: "10px"
8900
+ })
8901
+ },
8902
+ "system-light": {
8903
+ itemActive: (c) => ({
8904
+ ...tintedStyle(c),
8905
+ borderLeft: `3px solid ${c}`,
8906
+ paddingLeft: "9px"
8907
+ }),
8908
+ subItemActive: textStyle,
8909
+ childActive: (c) => ({
8910
+ ...tintedStyle(c),
8911
+ borderLeft: `2px solid ${c}`,
8912
+ paddingLeft: "14px"
8913
+ }),
8914
+ grandchildActive: (c) => ({
8915
+ ...tintedStyle(c),
8916
+ borderLeft: `2px solid ${c}`,
8917
+ paddingLeft: "10px"
8918
+ })
8919
+ }
8920
+ };
8453
8921
  function Sidebar(props) {
8454
8922
  const navigate = useNavigate();
8455
- const { isOpen: isOpen2, onClose, menuSections, header, className, showCollapseButton = false } = props;
8923
+ const {
8924
+ onClose,
8925
+ menuSections,
8926
+ header,
8927
+ className,
8928
+ showCollapseButton = false,
8929
+ variant = "light",
8930
+ primaryColor
8931
+ } = props;
8456
8932
  const { pathname } = useLocation();
8457
8933
  const [isCollapsed, setIsCollapsed] = useState(false);
8458
- const [isContentVisible, setIsContentVisible] = useState(true);
8459
8934
  const [expandedItems, setExpandedItems] = useState(/* @__PURE__ */ new Set());
8460
8935
  const [expandedSubItems, setExpandedSubItems] = useState(/* @__PURE__ */ new Set());
8461
- useEffect(() => {
8462
- if (isCollapsed) return;
8463
- const timer = setTimeout(() => setIsContentVisible(true), 300);
8464
- return () => clearTimeout(timer);
8465
- }, [isCollapsed]);
8936
+ const s = VARIANT_STYLES2[variant];
8937
+ const applier = ACCENT_APPLIERS[variant];
8466
8938
  useEffect(() => {
8467
8939
  menuSections.forEach((section) => {
8468
8940
  section.items.forEach((item) => {
@@ -8513,167 +8985,178 @@ function Sidebar(props) {
8513
8985
  window.open(path, "_blank", "noopener,noreferrer");
8514
8986
  } else if (path !== "") {
8515
8987
  navigate(path);
8516
- onClose();
8988
+ onClose?.();
8517
8989
  }
8518
8990
  };
8519
- return /* @__PURE__ */ jsxs(Fragment, { children: [
8520
- isOpen2 && /* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black/10 bg-opacity-50 z-40 lg:hidden", onClick: onClose }),
8521
- /* @__PURE__ */ jsxs(
8522
- "aside",
8523
- {
8524
- className: `
8525
- ${isCollapsed ? "w-11 min-w-11" : "min-w-60 w-62"}
8526
- ${isOpen2 ? "translate-x-0" : "-translate-x-full lg:translate-x-0"}
8527
- hide-scrollbar fixed lg:relative top-0 left-0 h-screen bg-white border-r border-gray-200 z-50 shrink-0 overflow-y-auto
8528
- transition-transform duration-300 ease-in-out
8529
- ${className ?? ""}
8530
- `,
8531
- children: [
8532
- showCollapseButton && /* @__PURE__ */ jsx(
8533
- "button",
8534
- {
8535
- onClick: () => {
8536
- if (!isCollapsed) setIsContentVisible(false);
8537
- setIsCollapsed((prev) => !prev);
8538
- },
8539
- className: "absolute top-4 right-2 z-20 w-6.5 h-6.5 bg-gray-50 rounded-full flex items-center justify-center hover:bg-gray-50 transition-colors cursor-pointer hover:bg-gray-100",
8540
- children: isCollapsed ? /* @__PURE__ */ jsx(PanelRightClose, { size: 18, color: "var(--color-sub-darkgray)" }) : /* @__PURE__ */ jsx(PanelLeftClose, { size: 18, color: "var(--color-sub-darkgray)" })
8541
- }
8542
- ),
8543
- isContentVisible && /* @__PURE__ */ jsxs("div", { className: "py-6 pl-6 pr-4", children: [
8544
- /* @__PURE__ */ jsx(
8545
- "button",
8546
- {
8547
- onClick: onClose,
8548
- className: "lg:hidden absolute top-4 right-4 p-2 hover:bg-gray-100 rounded-lg transition-colors text-2xl leading-none",
8549
- "aria-label": "Close menu",
8550
- children: "x"
8551
- }
8552
- ),
8553
- header && /* @__PURE__ */ jsx("div", { className: "mb-10 mt-2", children: header }),
8554
- /* @__PURE__ */ jsx("nav", { className: "space-y-6", children: menuSections.map((section, sectionIndex) => /* @__PURE__ */ jsxs("div", { children: [
8555
- section.title && /* @__PURE__ */ jsx("div", { className: "text-xs font-semibold text-gray-500 tracking-wider mb-3", children: section.title }),
8556
- /* @__PURE__ */ jsx("div", { className: "space-y-1", children: section.items.map((item) => /* @__PURE__ */ jsxs("div", { children: [
8557
- /* @__PURE__ */ jsxs(
8558
- "button",
8559
- {
8560
- onClick: () => {
8561
- if (item.children) {
8562
- toggleExpand(item.label);
8563
- } else if (item.path) {
8564
- handleItemClick(item.path);
8565
- }
8566
- },
8567
- className: `
8568
- w-full text-left flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors
8569
- ${!item.children && pathname === item.path ? "bg-blue-50 text-blue-600" : "text-gray-700 hover:bg-gray-100"}
8570
- cursor-pointer
8571
- `,
8572
- children: [
8573
- /* @__PURE__ */ jsx(item.icon, { className: "w-4 h-4 flex-shrink-0" }),
8574
- /* @__PURE__ */ jsx("span", { className: "flex-1", children: item.label }),
8575
- item.children && /* @__PURE__ */ jsx(
8576
- "svg",
8577
- {
8578
- className: `w-4 h-4 transition-transform ${expandedItems.has(item.label) ? "rotate-90" : ""}`,
8579
- fill: "none",
8580
- stroke: "currentColor",
8581
- viewBox: "0 0 24 24",
8582
- children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" })
8583
- }
8584
- ),
8585
- item.path?.includes("https") && /* @__PURE__ */ jsx(
8586
- "svg",
8587
- {
8588
- className: "w-4 h-4 flex-shrink-0 text-gray-400",
8589
- fill: "none",
8590
- stroke: "currentColor",
8591
- viewBox: "0 0 24 24",
8592
- children: /* @__PURE__ */ jsx(
8593
- "path",
8991
+ return /* @__PURE__ */ jsxs(
8992
+ "aside",
8993
+ {
8994
+ className: `
8995
+ ${isCollapsed ? "w-11 min-w-11" : "min-w-60 w-62"}
8996
+ transition-[width,min-width] duration-300 ease-in-out
8997
+ hide-scrollbar relative h-screen shrink-0 overflow-y-auto overflow-x-hidden
8998
+ ${s.aside}
8999
+ ${className ?? ""}
9000
+ `,
9001
+ children: [
9002
+ showCollapseButton && /* @__PURE__ */ jsx(
9003
+ "button",
9004
+ {
9005
+ onClick: () => setIsCollapsed((prev) => !prev),
9006
+ className: `absolute top-4 right-2 z-20 w-6.5 h-6.5 rounded-full flex items-center justify-center transition-colors cursor-pointer ${s.collapseBtn}`,
9007
+ children: isCollapsed ? /* @__PURE__ */ jsx(PanelRightClose, { size: 18, color: primaryColor ?? s.collapseIcon }) : /* @__PURE__ */ jsx(PanelLeftClose, { size: 18, color: primaryColor ?? s.collapseIcon })
9008
+ }
9009
+ ),
9010
+ /* @__PURE__ */ jsxs(
9011
+ "div",
9012
+ {
9013
+ className: `transition-opacity duration-200 ease-in-out ${isCollapsed ? "opacity-0 pointer-events-none select-none" : "opacity-100"} ${variant === "system-dark" || variant === "system-light" ? "px-1 py-6" : "px-4 py-6"}`,
9014
+ style: { transitionDelay: isCollapsed ? "0ms" : "150ms" },
9015
+ children: [
9016
+ header && /* @__PURE__ */ jsx("div", { className: "mb-6 mt-2", children: header }),
9017
+ /* @__PURE__ */ jsx("nav", { className: "space-y-6", children: menuSections.map((section, sectionIndex) => /* @__PURE__ */ jsxs("div", { children: [
9018
+ section.title && /* @__PURE__ */ jsx("div", { className: s.sectionTitle, children: section.title }),
9019
+ /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: section.items.map((item) => {
9020
+ const isActive = !item.children && pathname === item.path;
9021
+ return /* @__PURE__ */ jsxs("div", { children: [
9022
+ /* @__PURE__ */ jsxs(
9023
+ "button",
9024
+ {
9025
+ onClick: () => {
9026
+ if (item.children) {
9027
+ toggleExpand(item.label);
9028
+ } else if (item.path) {
9029
+ handleItemClick(item.path);
9030
+ }
9031
+ },
9032
+ className: `
9033
+ w-full text-left flex items-center gap-2 px-3 ${s.itemPy} ${s.rounded} ${s.itemTextSize} ${s.itemFont} transition-colors cursor-pointer
9034
+ ${isActive ? s.itemActive : s.itemInactive}
9035
+ `,
9036
+ style: {
9037
+ ...isActive ? s.activeItemStyle : {},
9038
+ ...isActive && primaryColor ? applier.itemActive(primaryColor) : {}
9039
+ },
9040
+ children: [
9041
+ /* @__PURE__ */ jsx(item.icon, { className: s.iconClass }),
9042
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: item.label }),
9043
+ item.children && /* @__PURE__ */ jsx(
9044
+ "svg",
8594
9045
  {
8595
- strokeLinecap: "round",
8596
- strokeLinejoin: "round",
8597
- strokeWidth: 2,
8598
- d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
9046
+ className: `w-4 h-4 transition-transform ${expandedItems.has(item.label) ? "rotate-90" : ""}`,
9047
+ fill: "none",
9048
+ stroke: "currentColor",
9049
+ viewBox: "0 0 24 24",
9050
+ children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" })
9051
+ }
9052
+ ),
9053
+ item.path?.includes("https") && /* @__PURE__ */ jsx(
9054
+ "svg",
9055
+ {
9056
+ className: "w-4 h-4 flex-shrink-0 opacity-50",
9057
+ fill: "none",
9058
+ stroke: "currentColor",
9059
+ viewBox: "0 0 24 24",
9060
+ children: /* @__PURE__ */ jsx(
9061
+ "path",
9062
+ {
9063
+ strokeLinecap: "round",
9064
+ strokeLinejoin: "round",
9065
+ strokeWidth: 2,
9066
+ d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
9067
+ }
9068
+ )
8599
9069
  }
8600
9070
  )
8601
- }
8602
- )
8603
- ]
8604
- }
8605
- ),
8606
- item.children && expandedItems.has(item.label) && /* @__PURE__ */ jsx("div", { className: "ml-[25px] mt-1 space-y-1", children: item.children.map((child, childIndex) => {
8607
- if (child.children && child.children.length > 0) {
8608
- const subKey = `${item.label}::${child.label}`;
8609
- const isSubExpanded = expandedSubItems.has(subKey);
8610
- const isSubActive = child.children.some((gc) => gc.path === pathname);
8611
- return /* @__PURE__ */ jsxs("div", { children: [
8612
- /* @__PURE__ */ jsxs(
8613
- "button",
8614
- {
8615
- onClick: () => toggleSubExpand(subKey),
8616
- className: `
8617
- w-full text-left flex items-center justify-between px-3 py-1.5 rounded-lg text-sm transition-colors cursor-pointer
8618
- ${isSubActive ? "text-blue-600 font-medium" : "text-gray-600 hover:bg-gray-50 hover:text-gray-900"}
8619
- `,
8620
- children: [
8621
- /* @__PURE__ */ jsx("span", { children: child.label }),
8622
- /* @__PURE__ */ jsx(
8623
- "svg",
8624
- {
8625
- className: `w-3 h-3 transition-transform ${isSubExpanded ? "rotate-90" : ""}`,
8626
- fill: "none",
8627
- stroke: "currentColor",
8628
- viewBox: "0 0 24 24",
8629
- children: /* @__PURE__ */ jsx(
8630
- "path",
9071
+ ]
9072
+ }
9073
+ ),
9074
+ item.children && expandedItems.has(item.label) && /* @__PURE__ */ jsx("div", { className: `ml-5 mt-0.5 space-y-0.5 border-l ${s.childGroupBorder}`, children: item.children.map((child, childIndex) => {
9075
+ if (child.children && child.children.length > 0) {
9076
+ const subKey = `${item.label}::${child.label}`;
9077
+ const isSubExpanded = expandedSubItems.has(subKey);
9078
+ const isSubActive = child.children.some((gc) => gc.path === pathname);
9079
+ return /* @__PURE__ */ jsxs("div", { children: [
9080
+ /* @__PURE__ */ jsxs(
9081
+ "button",
9082
+ {
9083
+ onClick: () => toggleSubExpand(subKey),
9084
+ className: `
9085
+ w-full text-left flex items-center justify-between pl-4 pr-3 ${s.childPy} ${s.rounded} ${s.itemTextSize} transition-colors cursor-pointer
9086
+ ${isSubActive ? s.subItemActive : s.subItemInactive}
9087
+ `,
9088
+ style: isSubActive && primaryColor ? applier.subItemActive(primaryColor) : void 0,
9089
+ children: [
9090
+ /* @__PURE__ */ jsx("span", { children: child.label }),
9091
+ /* @__PURE__ */ jsx(
9092
+ "svg",
8631
9093
  {
8632
- strokeLinecap: "round",
8633
- strokeLinejoin: "round",
8634
- strokeWidth: 2,
8635
- d: "M9 5l7 7-7 7"
9094
+ className: `w-3 h-3 transition-transform ${isSubExpanded ? "rotate-90" : ""}`,
9095
+ fill: "none",
9096
+ stroke: "currentColor",
9097
+ viewBox: "0 0 24 24",
9098
+ children: /* @__PURE__ */ jsx(
9099
+ "path",
9100
+ {
9101
+ strokeLinecap: "round",
9102
+ strokeLinejoin: "round",
9103
+ strokeWidth: 2,
9104
+ d: "M9 5l7 7-7 7"
9105
+ }
9106
+ )
8636
9107
  }
8637
9108
  )
8638
- }
8639
- )
8640
- ]
8641
- }
8642
- ),
8643
- isSubExpanded && /* @__PURE__ */ jsx("div", { className: "ml-3 mt-0.5 space-y-0.5 border-l border-gray-200 pl-2", children: child.children.map((grandchild) => /* @__PURE__ */ jsx(
9109
+ ]
9110
+ }
9111
+ ),
9112
+ isSubExpanded && /* @__PURE__ */ jsx("div", { className: "ml-3 mt-0.5 space-y-0.5 pl-2", children: child.children.map((grandchild) => {
9113
+ const isGrandchildActive = pathname === grandchild.path;
9114
+ return /* @__PURE__ */ jsx(
9115
+ "button",
9116
+ {
9117
+ onClick: () => handleItemClick(grandchild.path),
9118
+ className: `
9119
+ w-full text-left px-3 ${s.grandchildPy} ${s.rounded} ${s.grandchildTextSize} transition-colors cursor-pointer
9120
+ ${isGrandchildActive ? s.grandchildActive : s.grandchildInactive}
9121
+ `,
9122
+ style: {
9123
+ ...isGrandchildActive ? s.activeGrandchildStyle : {},
9124
+ ...isGrandchildActive && primaryColor ? applier.grandchildActive(primaryColor) : {}
9125
+ },
9126
+ children: grandchild.label
9127
+ },
9128
+ grandchild.path
9129
+ );
9130
+ }) })
9131
+ ] }, child.label);
9132
+ }
9133
+ const isChildActive = pathname === child.path;
9134
+ return /* @__PURE__ */ jsx(
8644
9135
  "button",
8645
9136
  {
8646
- onClick: () => handleItemClick(grandchild.path),
9137
+ onClick: () => handleItemClick(child.path ?? ""),
8647
9138
  className: `
8648
- w-full text-left px-3 py-1 rounded-lg text-sm transition-colors cursor-pointer
8649
- ${pathname === grandchild.path ? "text-blue-600 font-medium" : "text-gray-500 hover:bg-gray-50 hover:text-gray-900"}
8650
- `,
8651
- children: grandchild.label
9139
+ w-full text-left pl-4 pr-3 ${s.childPy} ${s.rounded} ${s.itemTextSize} transition-colors cursor-pointer
9140
+ ${isChildActive ? s.childActive : s.childInactive}
9141
+ `,
9142
+ style: {
9143
+ ...isChildActive ? s.activeChildStyle : {},
9144
+ ...isChildActive && primaryColor ? applier.childActive(primaryColor) : {}
9145
+ },
9146
+ children: child.label
8652
9147
  },
8653
- grandchild.path
8654
- )) })
8655
- ] }, child.label);
8656
- }
8657
- return /* @__PURE__ */ jsx(
8658
- "button",
8659
- {
8660
- onClick: () => handleItemClick(child.path ?? ""),
8661
- className: `
8662
- w-full text-left px-3 py-1.5 rounded-lg text-sm transition-colors cursor-pointer
8663
- ${pathname === child.path ? "bg-blue-50 text-blue-600 font-medium" : "text-gray-600 hover:bg-gray-50 hover:text-gray-900"}
8664
- `,
8665
- children: child.label
8666
- },
8667
- childIndex
8668
- );
9148
+ childIndex
9149
+ );
9150
+ }) })
9151
+ ] }, item.label);
8669
9152
  }) })
8670
- ] }, item.label)) })
8671
- ] }, section.title ?? sectionIndex)) })
8672
- ] })
8673
- ]
8674
- }
8675
- )
8676
- ] });
9153
+ ] }, section.title ?? sectionIndex)) })
9154
+ ]
9155
+ }
9156
+ )
9157
+ ]
9158
+ }
9159
+ );
8677
9160
  }
8678
9161
  function useDetailController(params = {}) {
8679
9162
  const { mode: controlledMode, onModeChange } = params;
@@ -8970,10 +9453,7 @@ function DetailModalFrame(props) {
8970
9453
  const isEditMode = controller.mode === "edit";
8971
9454
  const canEdit = Boolean(editSchema && (editFields || renderEditBody));
8972
9455
  return /* @__PURE__ */ jsxs(Modal, { isOpen: open, onClose: handleClose, contentClassName: cn("max-w-2xl", contentClassName), children: [
8973
- /* @__PURE__ */ jsxs(ModalHeader, { className: "flex flex-row items-center justify-between", children: [
8974
- /* @__PURE__ */ jsx("span", { className: "text-base font-semibold text-gray-900", children: title }),
8975
- !isEditMode && canEdit && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleEdit, children: "\uC218\uC815" })
8976
- ] }),
9456
+ /* @__PURE__ */ jsx(ModalHeader, { className: "flex flex-row items-center justify-between", children: /* @__PURE__ */ jsx("span", { className: "text-base font-semibold text-gray-900", children: title }) }),
8977
9457
  /* @__PURE__ */ jsxs(ModalBody, { className: "flex-1 min-h-0 overflow-hidden p-0 flex flex-col", children: [
8978
9458
  /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: /* @__PURE__ */ jsx(
8979
9459
  DetailContent,
@@ -8993,8 +9473,9 @@ function DetailModalFrame(props) {
8993
9473
  ] }),
8994
9474
  /* @__PURE__ */ jsx(ModalFooter, { className: "flex justify-end gap-2", children: isEditMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
8995
9475
  /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleCancelEdit, disabled: controller.isSaving, children: "\uCDE8\uC18C" }),
8996
- /* @__PURE__ */ jsx(Button, { type: "submit", form: controller.formId, variant: "save", disabled: controller.isSaving, children: controller.isSaving ? "\uC800\uC7A5 \uC911..." : "\uC800\uC7A5" })
9476
+ /* @__PURE__ */ jsx(Button, { type: "submit", form: controller.formId, variant: "save", disabled: controller.isSaving, children: "\uC800\uC7A5" })
8997
9477
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
9478
+ canEdit && /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleEdit, children: "\uC218\uC815" }),
8998
9479
  renderFooterExtra,
8999
9480
  /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleClose, children: "\uB2EB\uAE30" })
9000
9481
  ] }) })