@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.cjs CHANGED
@@ -242,6 +242,32 @@ function TooltipContent({
242
242
  }
243
243
  ) });
244
244
  }
245
+
246
+ // src/config/zIndex.ts
247
+ var Z_INDEX = {
248
+ TABLE_COL_STICKY: 1,
249
+ TABLE_FIXED_CELL: 5,
250
+ TABLE_FIXED_HEADER_CELL: 10,
251
+ LOCAL_OVERLAY: 10,
252
+ LAYOUT_HEADER: 20,
253
+ DROPDOWN_LOCAL: 20,
254
+ SIDEBAR_CONTAINER: 30,
255
+ SIDEBAR_BACKDROP: 40,
256
+ SIDEBAR: 50,
257
+ DROPDOWN: 50,
258
+ CALENDAR_ANCHORED: 50,
259
+ LOADING_OVERLAY_DEFAULT: 50,
260
+ DRAWING_CANVAS_1: 51,
261
+ DRAWING_CANVAS_2: 52,
262
+ DRAWING: 53,
263
+ MODAL_OVERLAY: 54,
264
+ MODAL: 55,
265
+ CALENDAR_PORTAL: 60,
266
+ TOOLTIP: 70,
267
+ SELECT_CONTENT: 100,
268
+ TOP: 9999,
269
+ MODAL_Z_STEP: 10
270
+ };
245
271
  function Tooltip2(props) {
246
272
  const {
247
273
  children,
@@ -253,6 +279,7 @@ function Tooltip2(props) {
253
279
  arrowClassName = "",
254
280
  contentClassName = "",
255
281
  contentStyle = {},
282
+ containerStyle,
256
283
  offsetX = 0,
257
284
  offsetY = 2,
258
285
  delayDuration = 0,
@@ -272,7 +299,9 @@ function Tooltip2(props) {
272
299
  sideOffset,
273
300
  collisionPadding: 8,
274
301
  style: {
275
- transform: `translateX(${offsetX}px) translateY(${translateY}px)`
302
+ zIndex: Z_INDEX.TOOLTIP,
303
+ transform: `translateX(${offsetX}px) translateY(${translateY}px)`,
304
+ ...containerStyle
276
305
  },
277
306
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-[5px] break-words whitespace-normal", style: contentStyle, children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: contentClassName, children: [
278
307
  content && /* @__PURE__ */ jsxRuntime.jsx("span", { children: content }),
@@ -943,7 +972,7 @@ var SELECT_BORDER_COLORS = {
943
972
  };
944
973
  function Select2(props) {
945
974
  const {
946
- containerClass,
975
+ containerClassName,
947
976
  options,
948
977
  placeholder = "\uC120\uD0DD\uD574 \uC8FC\uC138\uC694",
949
978
  triggerClassName,
@@ -973,7 +1002,7 @@ function Select2(props) {
973
1002
  onChange("");
974
1003
  onReset?.();
975
1004
  };
976
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-0.5", containerClass ?? "w-20"), children: [
1005
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-0.5", containerClassName ?? "w-30"), children: [
977
1006
  label && /* @__PURE__ */ jsxRuntime.jsx(
978
1007
  "label",
979
1008
  {
@@ -1056,6 +1085,7 @@ function Radio(props) {
1056
1085
  checked,
1057
1086
  onChange,
1058
1087
  disabled = false,
1088
+ disabledMessage,
1059
1089
  checkColor = DEFAULT_CHECK_COLOR,
1060
1090
  unCheckColor = DEFAULT_UNCHECK_COLOR,
1061
1091
  labelClassName,
@@ -1104,23 +1134,29 @@ function Radio(props) {
1104
1134
  }
1105
1135
  )
1106
1136
  ] });
1137
+ const withTooltip = (node) => {
1138
+ if (!disabled || !disabledMessage) return node;
1139
+ return /* @__PURE__ */ jsxRuntime.jsx(Tooltip2, { content: disabledMessage, position: "bottom", asChild: true, sideOffset: 4, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex", children: node }) });
1140
+ };
1107
1141
  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
- }
1142
+ return withTooltip(
1143
+ /* @__PURE__ */ jsxRuntime.jsx(
1144
+ RadioGroupPrimitive__namespace.Root,
1145
+ {
1146
+ name,
1147
+ value: checked ? value : "",
1148
+ onValueChange: (newValue) => {
1149
+ if (newValue === value) {
1150
+ onChange(value);
1151
+ }
1152
+ },
1153
+ disabled,
1154
+ children: control
1155
+ }
1156
+ )
1121
1157
  );
1122
1158
  }
1123
- return control;
1159
+ return withTooltip(control);
1124
1160
  }
1125
1161
  function RadioGroup(props) {
1126
1162
  const {
@@ -1156,6 +1192,7 @@ function RadioGroup(props) {
1156
1192
  value: option.value,
1157
1193
  label: option.label,
1158
1194
  disabled: disabled || option.disabled,
1195
+ disabledMessage: option.disabled ? option.disabledMessage : void 0,
1159
1196
  checkColor,
1160
1197
  unCheckColor,
1161
1198
  labelClassName,
@@ -1311,31 +1348,6 @@ function Spinner(props) {
1311
1348
  }
1312
1349
  );
1313
1350
  }
1314
-
1315
- // src/config/zIndex.ts
1316
- var Z_INDEX = {
1317
- TABLE_COL_STICKY: 1,
1318
- TABLE_FIXED_CELL: 5,
1319
- TABLE_FIXED_HEADER_CELL: 10,
1320
- LOCAL_OVERLAY: 10,
1321
- LAYOUT_HEADER: 20,
1322
- DROPDOWN_LOCAL: 20,
1323
- SIDEBAR_CONTAINER: 30,
1324
- SIDEBAR_BACKDROP: 40,
1325
- SIDEBAR: 50,
1326
- DROPDOWN: 50,
1327
- CALENDAR_ANCHORED: 50,
1328
- LOADING_OVERLAY_DEFAULT: 50,
1329
- DRAWING_CANVAS_1: 51,
1330
- DRAWING_CANVAS_2: 52,
1331
- DRAWING: 53,
1332
- MODAL_OVERLAY: 54,
1333
- MODAL: 55,
1334
- CALENDAR_PORTAL: 60,
1335
- SELECT_CONTENT: 100,
1336
- TOP: 9999,
1337
- MODAL_Z_STEP: 10
1338
- };
1339
1351
  function LoadingOverlay(props) {
1340
1352
  const {
1341
1353
  isVisible,
@@ -1950,16 +1962,43 @@ function ToastProvider() {
1950
1962
  }
1951
1963
 
1952
1964
  // src/components/DatePicker/config/utils.ts
1965
+ function makeLocalDate(year, monthIndex, day) {
1966
+ const d = /* @__PURE__ */ new Date(0);
1967
+ d.setFullYear(year, monthIndex, day);
1968
+ return d;
1969
+ }
1953
1970
  function parseLocalYmd(ymd) {
1954
1971
  const m = ymd.trim().match(/^(\d{4})-(\d{2})-(\d{2})$/);
1955
1972
  if (!m) return null;
1956
1973
  const y = Number(m[1]);
1957
1974
  const mo = Number(m[2]) - 1;
1958
1975
  const d = Number(m[3]);
1959
- const dt = new Date(y, mo, d);
1976
+ const dt = makeLocalDate(y, mo, d);
1960
1977
  if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) return null;
1961
1978
  return dt;
1962
1979
  }
1980
+ function canSelectToday(options) {
1981
+ const today = options.now ?? /* @__PURE__ */ new Date();
1982
+ const todayYmd = formatDate(today);
1983
+ if (options.maxDate && isCalendarDayAfterMax(today, options.maxDate)) {
1984
+ return false;
1985
+ }
1986
+ if (!options.range) {
1987
+ return true;
1988
+ }
1989
+ if (options.isSelectingEnd) {
1990
+ const start = options.rangeStart?.trim() ?? "";
1991
+ if (start.length === 10 && todayYmd < start) {
1992
+ return false;
1993
+ }
1994
+ return true;
1995
+ }
1996
+ const end = options.rangeEnd?.trim() ?? "";
1997
+ if (end.length === 10 && todayYmd > end) {
1998
+ return false;
1999
+ }
2000
+ return true;
2001
+ }
1963
2002
  function isCalendarDayAfterMax(date, maxYmd) {
1964
2003
  const max = parseLocalYmd(maxYmd);
1965
2004
  if (!max) return false;
@@ -1968,7 +2007,7 @@ function isCalendarDayAfterMax(date, maxYmd) {
1968
2007
  return ax > bx;
1969
2008
  }
1970
2009
  function formatDate(date) {
1971
- const year = date.getFullYear();
2010
+ const year = String(date.getFullYear()).padStart(4, "0");
1972
2011
  const month = String(date.getMonth() + 1).padStart(2, "0");
1973
2012
  const day = String(date.getDate()).padStart(2, "0");
1974
2013
  return `${year}-${month}-${day}`;
@@ -1987,7 +2026,6 @@ function validateAndFormatInput(input) {
1987
2026
  if (numbers.length === 0) return "";
1988
2027
  const clampYear = (raw) => {
1989
2028
  const n = parseInt(raw, 10);
1990
- if (n < 1) return "0001";
1991
2029
  if (n > 9999) return "9999";
1992
2030
  return raw;
1993
2031
  };
@@ -2010,12 +2048,111 @@ function validateAndFormatInput(input) {
2010
2048
  let day = dayRaw;
2011
2049
  if (day.length === 2) {
2012
2050
  const dayNum = parseInt(day, 10);
2013
- const lastDayOfMonth = new Date(parseInt(year, 10), parseInt(month, 10), 0).getDate();
2051
+ const lastDayOfMonth = makeLocalDate(parseInt(year, 10), parseInt(month, 10), 0).getDate();
2014
2052
  if (dayNum < 1) day = "01";
2015
2053
  else if (dayNum > lastDayOfMonth) day = lastDayOfMonth.toString().padStart(2, "0");
2016
2054
  }
2017
2055
  return `${year}-${month}-${day}`;
2018
2056
  }
2057
+
2058
+ // src/components/DatePicker/calendar/config/default.ts
2059
+ var DEFAULT_CALENDAR_YEAR_HALF_SPAN = 50;
2060
+ var CALENDAR_WEEKDAY_LABELS = ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"];
2061
+ var CALENDAR_RANGE_HINT = {
2062
+ start: "\uC2DC\uC791 \uB0A0\uC9DC\uB97C \uC120\uD0DD\uD558\uC138\uC694",
2063
+ end: "\uC885\uB8CC \uB0A0\uC9DC\uB97C \uC120\uD0DD\uD558\uC138\uC694"
2064
+ };
2065
+ var CALENDAR_PORTAL_Z_INDEX = Z_INDEX.CALENDAR_PORTAL;
2066
+ var CALENDAR_SELECT_CLASSNAMES = {
2067
+ containerClass: "w-20 shrink-0",
2068
+ triggerClassName: "h-8 px-2 py-1 text-sm cursor-pointer",
2069
+ contentClassName: "min-w-[5rem]"
2070
+ };
2071
+ var CALENDAR_PANEL_BASE_CLASS = "bg-white border border-gray-300 rounded-lg shadow-lg p-3 min-w-[280px]";
2072
+ var CALENDAR_PANEL_ANCHORED_CLASS = `${CALENDAR_PANEL_BASE_CLASS} absolute z-50`;
2073
+ var YEAR_SELECT_CONTENT_EXTRA_CLASS = "year-select-content custom-view-scrollbar max-h-50 overflow-y-auto";
2074
+ var YEAR_SELECT_OPEN_CONTENT_SELECTOR = ".year-select-content[data-state='open']";
2075
+ var SELECT_ITEM_CHECKED_SELECTOR = "[data-slot='select-item'][data-state='checked']";
2076
+
2077
+ // src/components/DatePicker/calendar/config/calendar.ts
2078
+ function parseYmdDateString(value) {
2079
+ if (value.length !== 10) return null;
2080
+ return parseLocalYmd(value);
2081
+ }
2082
+ function resolveCalendarYearBounds(anchorYear, calendarYearRange) {
2083
+ const fallbackMin = Math.max(0, anchorYear - DEFAULT_CALENDAR_YEAR_HALF_SPAN);
2084
+ const fallbackMax = anchorYear + DEFAULT_CALENDAR_YEAR_HALF_SPAN;
2085
+ const rawMin = calendarYearRange?.minYear?.trim();
2086
+ const rawMax = calendarYearRange?.maxYear?.trim();
2087
+ if (rawMin === void 0 || rawMin === "" || rawMax === void 0 || rawMax === "") {
2088
+ return { minYear: fallbackMin, maxYear: fallbackMax };
2089
+ }
2090
+ const minParsed = Number.parseInt(rawMin, 10);
2091
+ const maxParsed = Number.parseInt(rawMax, 10);
2092
+ if (Number.isNaN(minParsed) || Number.isNaN(maxParsed) || minParsed > maxParsed) {
2093
+ return { minYear: fallbackMin, maxYear: fallbackMax };
2094
+ }
2095
+ return { minYear: Math.max(0, minParsed), maxYear: maxParsed };
2096
+ }
2097
+ function buildCalendarYearSelectOptions(maxDate, anchorYear = (/* @__PURE__ */ new Date()).getFullYear(), calendarYearRange) {
2098
+ const { minYear, maxYear } = resolveCalendarYearBounds(anchorYear, calendarYearRange);
2099
+ const maxSelectableYear = maxDate ? parseLocalYmd(maxDate)?.getFullYear() ?? null : null;
2100
+ const options = [];
2101
+ for (let y = maxYear; y >= minYear; y--) {
2102
+ options.push({
2103
+ value: String(y),
2104
+ label: `${String(y).padStart(4, "0")}\uB144`,
2105
+ disabled: maxSelectableYear !== null && y > maxSelectableYear
2106
+ });
2107
+ }
2108
+ return options;
2109
+ }
2110
+ function buildCalendarMonthSelectOptions(viewYear, maxDate) {
2111
+ const maxDateParsed = maxDate ? parseLocalYmd(maxDate) : null;
2112
+ const maxYear = maxDateParsed?.getFullYear() ?? null;
2113
+ const maxMonth = maxDateParsed !== null && maxDateParsed !== void 0 ? maxDateParsed.getMonth() + 1 : null;
2114
+ const isMaxYear = maxYear !== null && viewYear === maxYear;
2115
+ const options = [];
2116
+ for (let m = 1; m <= 12; m++) {
2117
+ options.push({
2118
+ value: String(m),
2119
+ label: `${m}\uC6D4`,
2120
+ disabled: isMaxYear && maxMonth !== null && m > maxMonth
2121
+ });
2122
+ }
2123
+ return options;
2124
+ }
2125
+ function getCalendarMonthGrid(year, monthIndex) {
2126
+ const firstDay = makeLocalDate(year, monthIndex, 1);
2127
+ const daysInMonth = makeLocalDate(year, monthIndex + 1, 0).getDate();
2128
+ const leadingEmpty = firstDay.getDay();
2129
+ const totalCells = leadingEmpty + daysInMonth;
2130
+ const trailingEmpty = totalCells % 7 === 0 ? 0 : 7 - totalCells % 7;
2131
+ return { leadingEmpty, daysInMonth, trailingEmpty };
2132
+ }
2133
+ function disableNextCalendarMonth(year, monthIndex, maxDate) {
2134
+ if (maxDate === void 0) return false;
2135
+ return isCalendarDayAfterMax(makeLocalDate(year, monthIndex + 1, 1), maxDate);
2136
+ }
2137
+ function scrollOpenYearSelectCheckedIntoView() {
2138
+ requestAnimationFrame(() => {
2139
+ const root2 = document.querySelector(YEAR_SELECT_OPEN_CONTENT_SELECTOR);
2140
+ if (!(root2 instanceof HTMLElement)) return;
2141
+ const checkedItem = root2.querySelector(SELECT_ITEM_CHECKED_SELECTOR);
2142
+ if (!(checkedItem instanceof HTMLElement)) return;
2143
+ checkedItem.scrollIntoView({ block: "start" });
2144
+ });
2145
+ }
2146
+ function dayCellButtonClass(opts) {
2147
+ const { isToday, isSelected, isInRange, isRangeStart, isRangeEnd, interactiveDisabled } = opts;
2148
+ const parts = ["w-8 h-8 text-sm flex items-center justify-center rounded-2xl transition-colors"];
2149
+ if (isToday) parts.push("bg-gray-100");
2150
+ if (isSelected && !isToday) parts.push("bg-neutral-200 text-gray-700");
2151
+ if (isInRange && !isToday) parts.push("bg-gray-100/90");
2152
+ if ((isRangeStart || isRangeEnd) && !isToday) parts.push("bg-neutral-300 text-gray-700 opacity-100");
2153
+ parts.push(interactiveDisabled ? "opacity-20 cursor-not-allowed" : "hover:bg-blue-100 cursor-pointer");
2154
+ return parts.filter(Boolean).join(" ").trim();
2155
+ }
2019
2156
  function RenderCalendar(props) {
2020
2157
  const {
2021
2158
  currentDate,
@@ -2030,133 +2167,163 @@ function RenderCalendar(props) {
2030
2167
  goToNextMonth,
2031
2168
  onYearChange,
2032
2169
  onMonthChange,
2170
+ onSelectOpenChange,
2033
2171
  editDateSelect = true,
2034
2172
  datePosition = "bottom",
2035
- calendarSelectContainerClass
2173
+ maxDate,
2174
+ calendarYearRange,
2175
+ calendarSelectContainerClass,
2176
+ floating = false,
2177
+ showTodayButton = true
2036
2178
  } = props;
2037
2179
  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;
2180
+ const monthIndex = currentDate.getMonth();
2181
+ const anchorYear = year;
2182
+ const disableNextMonth = disableNextCalendarMonth(year, monthIndex, maxDate);
2183
+ const { leadingEmpty, daysInMonth, trailingEmpty } = getCalendarMonthGrid(year, monthIndex);
2184
+ const yearOptions = buildCalendarYearSelectOptions(maxDate, anchorYear, calendarYearRange);
2185
+ const monthOptions = buildCalendarMonthSelectOptions(year, maxDate);
2186
+ const todayFormatted = formatDate(/* @__PURE__ */ new Date());
2187
+ const selectedSingleYmd = range ? null : inputValue;
2188
+ const rangeStartYmd = rangeStart.trim();
2189
+ const rangeEndYmd = rangeEnd.trim();
2055
2190
  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);
2191
+ for (let i = 0; i < leadingEmpty; i++) {
2192
+ days.push(/* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-8", "aria-hidden": true }, `lead-${i}`));
2193
+ }
2194
+ for (let d = 1; d <= daysInMonth; d++) {
2195
+ const date = makeLocalDate(year, monthIndex, d);
2064
2196
  const dateFormatted = formatDate(date);
2065
- const isToday = dateFormatted === currentDateFormatted;
2066
- const isSelected = dateFormatted === selectedDate;
2197
+ const isToday = dateFormatted === todayFormatted;
2198
+ const isSelected = dateFormatted === selectedSingleYmd;
2067
2199
  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;
2200
+ const isRangeStartFlag = Boolean(range && dateFormatted === rangeStart);
2201
+ const isRangeEndFlag = Boolean(range && dateFormatted === rangeEnd);
2202
+ const isBeforeRangeStart = range && isSelectingEnd && rangeStartYmd.length === 10 && dateFormatted < rangeStartYmd;
2203
+ const isAfterRangeEnd = range && !isSelectingEnd && rangeEndYmd.length === 10 && dateFormatted > rangeEndYmd;
2204
+ const isGreyedForRange = Boolean(isBeforeRangeStart || isAfterRangeEnd);
2205
+ const isPastMax = maxDate ? isCalendarDayAfterMax(date, maxDate) : false;
2074
2206
  days.push(
2075
2207
  /* @__PURE__ */ jsxRuntime.jsx(
2076
2208
  "button",
2077
2209
  {
2210
+ type: "button",
2078
2211
  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()
2212
+ disabled: isGreyedForRange || isPastMax,
2213
+ className: dayCellButtonClass({
2214
+ isToday,
2215
+ isSelected: Boolean(isSelected),
2216
+ isInRange,
2217
+ isRangeStart: isRangeStartFlag,
2218
+ isRangeEnd: isRangeEndFlag,
2219
+ interactiveDisabled: isGreyedForRange || isPastMax
2220
+ }),
2221
+ children: d
2090
2222
  },
2091
- i
2223
+ d
2092
2224
  )
2093
2225
  );
2094
2226
  }
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);
2227
+ for (let i = 0; i < trailingEmpty; i++) {
2228
+ days.push(/* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-8", "aria-hidden": true }, `trail-${i}`));
2229
+ }
2230
+ const calendarPositionClass = !floating && range && calendarPosition === "end" ? "right-0" : !floating ? "left-0" : "";
2231
+ const datePositionClass = floating ? "" : datePosition === "top" ? "bottom-full mb-1" : "top-full mt-1";
2232
+ const panelClass = floating ? CALENDAR_PANEL_BASE_CLASS : cn(CALENDAR_PANEL_ANCHORED_CLASS, datePositionClass, calendarPositionClass);
2233
+ const handleYearSelectOpenChange = (open) => {
2234
+ onSelectOpenChange?.(open);
2235
+ if (open) scrollOpenYearSelectCheckedIntoView();
2236
+ };
2237
+ const sel = CALENDAR_SELECT_CLASSNAMES;
2238
+ const containerClass = cn(sel.containerClass, calendarSelectContainerClass);
2239
+ const stopBubble = (e) => e.stopPropagation();
2240
+ const isTodaySelectable = canSelectToday({
2241
+ maxDate,
2242
+ range,
2243
+ isSelectingEnd,
2244
+ rangeStart,
2245
+ rangeEnd
2246
+ });
2247
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: panelClass, onClick: stopBubble, onMouseDown: stopBubble, children: [
2248
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
2249
+ /* @__PURE__ */ jsxRuntime.jsx(
2250
+ "button",
2251
+ {
2252
+ type: "button",
2253
+ onClick: goToPreviousMonth,
2254
+ className: "cursor-pointer rounded p-1 hover:bg-gray-100",
2255
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "size-4" })
2256
+ }
2257
+ ),
2258
+ editDateSelect ? /* @__PURE__ */ jsxRuntime.jsxs(
2259
+ "div",
2260
+ {
2261
+ role: "presentation",
2262
+ className: "flex items-center gap-2",
2263
+ onClick: stopBubble,
2264
+ onMouseDown: stopBubble,
2265
+ children: [
2266
+ /* @__PURE__ */ jsxRuntime.jsx(
2267
+ Select2,
2268
+ {
2269
+ value: year.toString(),
2270
+ options: yearOptions,
2271
+ onChange: (value) => onYearChange(Number.parseInt(value, 10)),
2272
+ containerClassName: containerClass,
2273
+ triggerClassName: sel.triggerClassName,
2274
+ contentClassName: cn(YEAR_SELECT_CONTENT_EXTRA_CLASS, sel.contentClassName),
2275
+ disableScrollButtonEvents: true,
2276
+ onOpenChange: handleYearSelectOpenChange
2277
+ }
2278
+ ),
2279
+ /* @__PURE__ */ jsxRuntime.jsx(
2280
+ Select2,
2281
+ {
2282
+ value: (monthIndex + 1).toString(),
2283
+ options: monthOptions,
2284
+ onChange: (value) => onMonthChange(Number.parseInt(value, 10) - 1),
2285
+ containerClassName: containerClass,
2286
+ triggerClassName: sel.triggerClassName,
2287
+ contentClassName: sel.contentClassName,
2288
+ onOpenChange: (open) => onSelectOpenChange?.(open)
2289
+ }
2290
+ )
2291
+ ]
2292
+ }
2293
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-lg font-semibold", children: [
2294
+ String(year).padStart(4, "0"),
2295
+ "\uB144 ",
2296
+ monthIndex + 1,
2297
+ "\uC6D4"
2298
+ ] }),
2299
+ /* @__PURE__ */ jsxRuntime.jsx(
2300
+ "button",
2301
+ {
2302
+ type: "button",
2303
+ onClick: goToNextMonth,
2304
+ disabled: disableNextMonth,
2305
+ className: cn(
2306
+ "rounded p-1",
2307
+ disableNextMonth ? "cursor-not-allowed opacity-30" : "cursor-pointer hover:bg-gray-100"
2308
+ ),
2309
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "size-4" })
2310
+ }
2311
+ )
2312
+ ] }),
2313
+ /* @__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)) }),
2314
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2", children: days }),
2315
+ 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 }) }),
2316
+ 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(
2317
+ "button",
2318
+ {
2319
+ type: "button",
2320
+ onClick: () => handleDateSelect(/* @__PURE__ */ new Date()),
2321
+ className: "cursor-pointer text-sm text-gray-800 hover:text-main",
2322
+ children: "Today"
2323
+ }
2324
+ ) })
2325
+ ] });
2157
2326
  }
2158
-
2159
- // src/components/DatePicker/calendar/hooks/useCalendarState.ts
2160
2327
  function useCalendarState(props) {
2161
2328
  const { datePosition = "bottom", disabled = false, calendarRef } = props;
2162
2329
  const [isCalendarOpen, setIsCalendarOpen] = React6.useState(false);
@@ -2205,16 +2372,16 @@ function useCalendarState(props) {
2205
2372
  setIsCalendarOpen(false);
2206
2373
  };
2207
2374
  const goToPreviousMonth = () => {
2208
- setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
2375
+ setCurrentDate(makeLocalDate(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
2209
2376
  };
2210
2377
  const goToNextMonth = () => {
2211
- setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1));
2378
+ setCurrentDate(makeLocalDate(currentDate.getFullYear(), currentDate.getMonth() + 1, 1));
2212
2379
  };
2213
2380
  const handleYearChange = (year) => {
2214
- setCurrentDate(new Date(year, currentDate.getMonth(), 1));
2381
+ setCurrentDate(makeLocalDate(year, currentDate.getMonth(), 1));
2215
2382
  };
2216
2383
  const handleMonthChange = (month) => {
2217
- setCurrentDate(new Date(currentDate.getFullYear(), month, 1));
2384
+ setCurrentDate(makeLocalDate(currentDate.getFullYear(), month, 1));
2218
2385
  };
2219
2386
  return {
2220
2387
  isCalendarOpen,
@@ -2253,11 +2420,15 @@ function useFloatingCalendarPosition(anchorRef, isOpen2, placement, align = "sta
2253
2420
  setStyle({ ...base, top: rect.bottom + CALENDAR_GAP });
2254
2421
  }
2255
2422
  };
2423
+ const handleScroll = (e) => {
2424
+ if (e.target?.closest?.('[data-slot="select-content"]')) return;
2425
+ update();
2426
+ };
2256
2427
  update();
2257
- window.addEventListener("scroll", update, true);
2428
+ window.addEventListener("scroll", handleScroll, true);
2258
2429
  window.addEventListener("resize", update);
2259
2430
  return () => {
2260
- window.removeEventListener("scroll", update, true);
2431
+ window.removeEventListener("scroll", handleScroll, true);
2261
2432
  window.removeEventListener("resize", update);
2262
2433
  };
2263
2434
  }, [isOpen2, placement, align, anchorRef]);
@@ -2337,6 +2508,7 @@ function DatePickerRange(props) {
2337
2508
  return;
2338
2509
  }
2339
2510
  onRangeChange?.(formatted, endInputValue);
2511
+ if (isCalendarOpen && calendarPosition === "start") navigateToDateValue(formatted);
2340
2512
  } else if (formatted.length === 0) {
2341
2513
  onRangeChange?.(formatted, endInputValue);
2342
2514
  }
@@ -2350,6 +2522,7 @@ function DatePickerRange(props) {
2350
2522
  return;
2351
2523
  }
2352
2524
  onRangeChange?.(startInputValue, formatted);
2525
+ if (isCalendarOpen && calendarPosition === "end") navigateToDateValue(formatted);
2353
2526
  } else if (formatted.length === 0) {
2354
2527
  onRangeChange?.(startInputValue, formatted);
2355
2528
  }
@@ -2524,6 +2697,7 @@ function DatePickerSingle(props) {
2524
2697
  autoDatePosition,
2525
2698
  containerRef,
2526
2699
  toggleCalendar,
2700
+ navigateToDateValue,
2527
2701
  closeCalendar,
2528
2702
  goToPreviousMonth,
2529
2703
  goToNextMonth,
@@ -2541,7 +2715,7 @@ function DatePickerSingle(props) {
2541
2715
  const dateMatch = formatted.match(/^(\d{4})-(\d{2})-(\d{2})$/);
2542
2716
  if (dateMatch) {
2543
2717
  const [, year, month, day] = dateMatch;
2544
- const date = new Date(
2718
+ const date = makeLocalDate(
2545
2719
  Number.parseInt(year, 10),
2546
2720
  Number.parseInt(month, 10) - 1,
2547
2721
  Number.parseInt(day, 10)
@@ -2553,6 +2727,7 @@ function DatePickerSingle(props) {
2553
2727
  return;
2554
2728
  }
2555
2729
  onChange?.(formatted);
2730
+ if (isCalendarOpen) navigateToDateValue(formatted);
2556
2731
  }
2557
2732
  }
2558
2733
  } else {
@@ -3489,9 +3664,10 @@ function ConfirmModal(props) {
3489
3664
  ] }),
3490
3665
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-8 items-center justify-end gap-2", children: [
3491
3666
  !isHideOkButton && /* @__PURE__ */ jsxRuntime.jsx(
3492
- Button,
3667
+ DebouncedButton,
3493
3668
  {
3494
3669
  ref: okButtonRef,
3670
+ debounceDelay: 500,
3495
3671
  variant: "outline",
3496
3672
  onClick: handleOkClick,
3497
3673
  className: "focus-visible:ring-2 focus-visible:ring-blue-400 focus-visible:ring-offset-1",
@@ -6565,7 +6741,7 @@ function CascadingSelectFieldInner(props) {
6565
6741
  disabled,
6566
6742
  hasError,
6567
6743
  options: toSelectOptions(primaryOptions),
6568
- containerClass: firstSelectClass.containerClassName,
6744
+ containerClassName: firstSelectClass.containerClassName,
6569
6745
  triggerClassName: firstSelectClass.triggerClassName,
6570
6746
  canReset: true,
6571
6747
  onChange: (nextPrimary) => {
@@ -6582,7 +6758,7 @@ function CascadingSelectFieldInner(props) {
6582
6758
  disabled: disabled || !primary || secondaryOptions.length === 0,
6583
6759
  hasError,
6584
6760
  options: toSelectOptions(secondaryOptions),
6585
- containerClass: secondSelectClass.containerClassName,
6761
+ containerClassName: secondSelectClass.containerClassName,
6586
6762
  triggerClassName: secondSelectClass.triggerClassName,
6587
6763
  canReset: true,
6588
6764
  onChange: (nextSecondary) => {
@@ -6626,7 +6802,8 @@ function DateField(props) {
6626
6802
  readOnly = false,
6627
6803
  className = "w-40",
6628
6804
  datePosition = "bottom",
6629
- disableFutureDates = false
6805
+ disableFutureDates = false,
6806
+ calendarSelectContainerClass = "w-20"
6630
6807
  } = config;
6631
6808
  const maxDate = disableFutureDates ? formatDate(/* @__PURE__ */ new Date()) : void 0;
6632
6809
  if (readOnly) {
@@ -6643,7 +6820,8 @@ function DateField(props) {
6643
6820
  className: "w-full",
6644
6821
  validation: hasError,
6645
6822
  datePosition,
6646
- maxDate
6823
+ maxDate,
6824
+ calendarSelectContainerClass
6647
6825
  }
6648
6826
  ) });
6649
6827
  }
@@ -6833,7 +7011,9 @@ function RadioField(props) {
6833
7011
  containerClassName: cn2("justify-start", fieldControlWrapClass(className)),
6834
7012
  options: options.map((option) => ({
6835
7013
  label: option.label,
6836
- value: String(option.value)
7014
+ value: String(option.value),
7015
+ disabled: option.disabled,
7016
+ disabledMessage: option.disabledMessage
6837
7017
  }))
6838
7018
  }
6839
7019
  );
@@ -6871,7 +7051,7 @@ function SelectField(props) {
6871
7051
  disabled,
6872
7052
  hasError,
6873
7053
  options: selectOptions,
6874
- containerClass: "flex w-full min-w-0 max-w-full items-center",
7054
+ containerClassName: "flex w-full min-w-0 max-w-full items-center",
6875
7055
  className: "w-full min-w-0",
6876
7056
  triggerClassName: cn2(
6877
7057
  "h-8 w-full min-w-0 max-w-full rounded border bg-white px-2 py-1 text-sm shadow-none",
@@ -6983,11 +7163,12 @@ function SelectWithInputField(props) {
6983
7163
  disabled,
6984
7164
  hasError,
6985
7165
  options: toSelectOptions(options),
6986
- containerClass: selectClass.containerClassName,
7166
+ containerClassName: selectClass.containerClassName,
6987
7167
  triggerClassName: selectClass.triggerClassName,
6988
7168
  onChange: (nextPrefix) => {
6989
7169
  setSelectedPrefix(nextPrefix);
6990
7170
  updateCombined(nextPrefix, inputPart);
7171
+ onBlur();
6991
7172
  }
6992
7173
  }
6993
7174
  ),
@@ -7122,7 +7303,7 @@ function useBlurValidation(name, options) {
7122
7303
  (value, fieldOnChange) => {
7123
7304
  markHadValueIfFilled(value);
7124
7305
  fieldOnChange(value);
7125
- if (options.revalidateOnChange || shouldValidateEmptyOnChange(value)) {
7306
+ if (options.revalidateOnChange || options.validateEmptyOnChange && shouldValidateEmptyOnChange(value)) {
7126
7307
  void trigger(name);
7127
7308
  }
7128
7309
  void triggerRelatedFields();
@@ -7130,6 +7311,7 @@ function useBlurValidation(name, options) {
7130
7311
  [
7131
7312
  markHadValueIfFilled,
7132
7313
  options.revalidateOnChange,
7314
+ options.validateEmptyOnChange,
7133
7315
  shouldValidateEmptyOnChange,
7134
7316
  trigger,
7135
7317
  name,
@@ -7150,9 +7332,7 @@ var REVALIDATE_ON_CHANGE_TYPES = /* @__PURE__ */ new Set([
7150
7332
  "radio",
7151
7333
  "switch",
7152
7334
  "select",
7153
- "date",
7154
7335
  "cascading-select",
7155
- "select-with-input",
7156
7336
  "input-with-button",
7157
7337
  "address"
7158
7338
  ]);
@@ -7173,14 +7353,15 @@ function FormFieldRow(props) {
7173
7353
  const { control } = reactHookForm.useFormContext();
7174
7354
  const { isSubmitted } = reactHookForm.useFormState({ control });
7175
7355
  const { name, label, type = "input", required = false, renderCustomField, contentAlign = "left" } = config;
7356
+ const revalidateOnChange = REVALIDATE_ON_CHANGE_TYPES.has(type);
7176
7357
  const { handleBlur, handleChange } = useBlurValidation(name, {
7177
7358
  required,
7178
- revalidateOnChange: REVALIDATE_ON_CHANGE_TYPES.has(type),
7359
+ revalidateOnChange,
7360
+ validateEmptyOnChange: revalidateOnChange,
7179
7361
  revalidateFields: config.revalidateFields
7180
7362
  });
7181
7363
  const showError = Boolean(fieldState.error?.message) && (isSubmitted || fieldState.isTouched);
7182
7364
  const isTextarea = type === "textarea";
7183
- const revalidateOnChange = REVALIDATE_ON_CHANGE_TYPES.has(type);
7184
7365
  const wrappedField = wrapFieldHandlers(field, {
7185
7366
  onBlur: () => handleBlur(() => field.onBlur()),
7186
7367
  onChange: (value) => {
@@ -7285,6 +7466,7 @@ function rowPropsToFieldConfig(props) {
7285
7466
  canReset,
7286
7467
  datePosition,
7287
7468
  disableFutureDates,
7469
+ calendarSelectContainerClass,
7288
7470
  revalidateFields,
7289
7471
  renderCustomField,
7290
7472
  colspan,
@@ -7315,6 +7497,7 @@ function rowPropsToFieldConfig(props) {
7315
7497
  canReset,
7316
7498
  datePosition,
7317
7499
  disableFutureDates,
7500
+ calendarSelectContainerClass,
7318
7501
  revalidateFields,
7319
7502
  renderCustomField,
7320
7503
  colspan,
@@ -7479,7 +7662,10 @@ function PageFilter(props) {
7479
7662
  {
7480
7663
  placeholder: option.placeholder,
7481
7664
  value: currentValue,
7482
- onChange: handleInputChange(optionKey)
7665
+ onChange: handleInputChange(optionKey),
7666
+ onKeyDown: (e) => {
7667
+ if (e.key === "Enter") handleSubmit();
7668
+ }
7483
7669
  }
7484
7670
  ) }, String(optionKey));
7485
7671
  case "select":
@@ -7489,7 +7675,8 @@ function PageFilter(props) {
7489
7675
  value: currentValue,
7490
7676
  onChange: handleSelectChange(optionKey),
7491
7677
  options: option.options,
7492
- placeholder: "\uC120\uD0DD\uD574\uC8FC\uC138\uC694"
7678
+ placeholder: "\uC120\uD0DD",
7679
+ containerClassName: option.containerClassName ?? "w-30"
7493
7680
  }
7494
7681
  ) }, String(optionKey));
7495
7682
  case "radio":
@@ -7554,9 +7741,84 @@ function PageFilter(props) {
7554
7741
  }
7555
7742
  );
7556
7743
  }
7744
+ var VARIANT_STYLES = {
7745
+ light: {
7746
+ container: "bg-white border-b border-gray-200",
7747
+ activeTab: "bg-gray-50 border-gray-200 text-gray-800 shadow-sm",
7748
+ inactiveTab: "bg-white border-gray-100 text-gray-400 hover:text-gray-600 hover:border-gray-200 hover:bg-gray-50",
7749
+ activeText: "font-semibold text-gray-800",
7750
+ inactiveText: "font-normal",
7751
+ indicator: "bg-blue-500",
7752
+ closeBtn: "hover:bg-gray-300/60",
7753
+ plusBtnArea: "border-l border-gray-100 bg-white",
7754
+ plusBtn: "text-gray-400 hover:text-gray-600 hover:bg-gray-100",
7755
+ fadeOpacity: 0.12,
7756
+ tabPy: "py-1.5",
7757
+ tabPx: "px-3",
7758
+ tabTextSize: "text-xs",
7759
+ scrollPadding: "px-3 pt-1.5",
7760
+ rounded: "rounded-t-md"
7761
+ },
7762
+ dark: {
7763
+ container: "bg-gray-900 border-b border-gray-700",
7764
+ activeTab: "bg-gray-800 border-gray-600 text-white shadow-sm",
7765
+ inactiveTab: "bg-gray-900 border-gray-700/50 text-gray-500 hover:text-gray-300 hover:border-gray-600 hover:bg-gray-800",
7766
+ activeText: "font-semibold text-white",
7767
+ inactiveText: "font-normal",
7768
+ indicator: "bg-indigo-400",
7769
+ closeBtn: "hover:bg-gray-600/60",
7770
+ plusBtnArea: "border-l border-gray-700 bg-gray-900",
7771
+ plusBtn: "text-gray-500 hover:text-gray-300 hover:bg-gray-700",
7772
+ fadeOpacity: 0.4,
7773
+ tabPy: "py-1.5",
7774
+ tabPx: "px-3",
7775
+ tabTextSize: "text-xs",
7776
+ scrollPadding: "px-3 pt-1.5",
7777
+ rounded: "rounded-t-md"
7778
+ },
7779
+ "system-light": {
7780
+ container: "bg-gray-200 border-b border-gray-300",
7781
+ activeTab: "bg-white border-gray-300 text-slate-800 shadow-sm",
7782
+ inactiveTab: "bg-transparent border-gray-300/80 text-slate-500 hover:text-slate-700 hover:bg-gray-200 hover:border-gray-300",
7783
+ activeText: "font-semibold text-slate-800",
7784
+ inactiveText: "font-medium",
7785
+ indicator: "bg-[#2b2b2b]",
7786
+ closeBtn: "hover:bg-gray-300",
7787
+ plusBtnArea: "border-l border-gray-300 bg-gray-200",
7788
+ plusBtn: "text-slate-400 hover:text-slate-600 hover:bg-gray-300",
7789
+ fadeOpacity: 0.15,
7790
+ tabPy: "py-2.5",
7791
+ tabPx: "px-5",
7792
+ tabTextSize: "text-sm",
7793
+ scrollPadding: "px-3 pt-2",
7794
+ rounded: "rounded-t-none"
7795
+ },
7796
+ "system-dark": {
7797
+ container: "bg-slate-900 border-b border-slate-700",
7798
+ activeTab: "bg-slate-800 border-slate-600 text-slate-100 shadow-sm",
7799
+ inactiveTab: "bg-transparent border-slate-700/50 text-slate-400 hover:text-slate-200 hover:bg-slate-800/60 hover:border-slate-700",
7800
+ activeText: "font-semibold text-slate-100",
7801
+ inactiveText: "font-medium",
7802
+ indicator: "bg-indigo-400",
7803
+ closeBtn: "hover:bg-slate-600/60",
7804
+ plusBtnArea: "border-l border-slate-700 bg-slate-900",
7805
+ plusBtn: "text-slate-400 hover:text-slate-200 hover:bg-slate-700",
7806
+ fadeOpacity: 0.45,
7807
+ tabPy: "py-2.5",
7808
+ tabPx: "px-5",
7809
+ tabTextSize: "text-sm",
7810
+ scrollPadding: "px-4 pt-2",
7811
+ rounded: "rounded-t-none"
7812
+ }
7813
+ };
7557
7814
  var PLUS_BTN_WIDTH = 36;
7815
+ function easedFade(dir, opacity) {
7816
+ const c = (t) => `rgba(0,0,0,${(opacity * t).toFixed(3)})`;
7817
+ return `linear-gradient(${dir}, ${c(1)} 0%, ${c(0.72)} 20%, ${c(0.38)} 45%, ${c(0.1)} 70%, transparent 100%)`;
7818
+ }
7558
7819
  function MultiTabBar(props) {
7559
- const { tabs, activeTabId, onTabClick, onTabClose, onTabAdd } = props;
7820
+ const { tabs, activeTabId, onTabClick, onTabClose, onTabAdd, variant = "light", primaryColor } = props;
7821
+ const s = VARIANT_STYLES[variant];
7560
7822
  const scrollRef = React6.useRef(null);
7561
7823
  const tabRefs = React6.useRef(/* @__PURE__ */ new Map());
7562
7824
  const [showLeftFade, setShowLeftFade] = React6.useState(false);
@@ -7609,13 +7871,23 @@ function MultiTabBar(props) {
7609
7871
  const handleStopDrag = () => {
7610
7872
  isDragging.current = false;
7611
7873
  };
7874
+ React6.useEffect(() => {
7875
+ const el = scrollRef.current;
7876
+ if (!el) return;
7877
+ const onWheel = (e) => {
7878
+ e.preventDefault();
7879
+ el.scrollLeft += e.deltaY || e.deltaX;
7880
+ };
7881
+ el.addEventListener("wheel", onWheel, { passive: false });
7882
+ return () => el.removeEventListener("wheel", onWheel);
7883
+ }, []);
7612
7884
  if (tabs.length === 0) return null;
7613
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 w-full overflow-hidden bg-white border-b border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-stretch", children: [
7885
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex-shrink-0 w-full overflow-hidden ${s.container}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-stretch", children: [
7614
7886
  showLeftFade && /* @__PURE__ */ jsxRuntime.jsx(
7615
7887
  "div",
7616
7888
  {
7617
7889
  className: "absolute left-0 top-0 bottom-0 w-10 pointer-events-none z-10",
7618
- style: { background: "linear-gradient(to right, white 30%, transparent)" }
7890
+ style: { background: easedFade("to right", s.fadeOpacity) }
7619
7891
  }
7620
7892
  ),
7621
7893
  showRightFade && /* @__PURE__ */ jsxRuntime.jsx(
@@ -7624,7 +7896,7 @@ function MultiTabBar(props) {
7624
7896
  className: "absolute top-0 bottom-0 w-10 pointer-events-none z-10",
7625
7897
  style: {
7626
7898
  right: PLUS_BTN_WIDTH,
7627
- background: "linear-gradient(to left, white 30%, transparent)"
7899
+ background: easedFade("to left", s.fadeOpacity)
7628
7900
  }
7629
7901
  }
7630
7902
  ),
@@ -7632,7 +7904,7 @@ function MultiTabBar(props) {
7632
7904
  "div",
7633
7905
  {
7634
7906
  ref: scrollRef,
7635
- className: "flex-1 min-w-0 flex items-end gap-0.5 px-3 pt-1.5 overflow-x-auto [&::-webkit-scrollbar]:hidden",
7907
+ className: `flex-1 min-w-0 flex items-end gap-0.5 ${s.scrollPadding} overflow-x-auto [&::-webkit-scrollbar]:hidden`,
7636
7908
  style: { scrollbarWidth: "none" },
7637
7909
  onScroll: updateFades,
7638
7910
  onMouseDown: handleMouseDown,
@@ -7653,17 +7925,23 @@ function MultiTabBar(props) {
7653
7925
  if (!isActive) onTabClick(tab.id);
7654
7926
  },
7655
7927
  className: `
7656
- group relative flex items-center gap-1.5 px-3 py-1.5 flex-shrink-0
7657
- rounded-t-md border border-b-0 cursor-pointer select-none
7928
+ group relative flex items-center gap-1.5 ${s.tabPx} ${s.tabPy} flex-shrink-0
7929
+ ${s.rounded} border border-b-0 cursor-pointer select-none
7658
7930
  transition-colors duration-100
7659
- ${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"}
7931
+ ${isActive ? s.activeTab : s.inactiveTab}
7660
7932
  `,
7661
7933
  children: [
7662
- isActive && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 left-0 right-0 h-0.5 rounded-t-md bg-blue-500" }),
7934
+ isActive && /* @__PURE__ */ jsxRuntime.jsx(
7935
+ "div",
7936
+ {
7937
+ className: `absolute top-0 left-0 right-0 h-0.5 ${s.rounded} ${s.indicator}`,
7938
+ style: primaryColor ? { backgroundColor: primaryColor } : void 0
7939
+ }
7940
+ ),
7663
7941
  /* @__PURE__ */ jsxRuntime.jsx(
7664
7942
  "span",
7665
7943
  {
7666
- className: `text-xs max-w-35 truncate leading-none ${isActive ? "font-semibold text-gray-800" : "font-normal"}`,
7944
+ className: `${s.tabTextSize} max-w-35 truncate leading-none ${isActive ? s.activeText : s.inactiveText}`,
7667
7945
  children: tab.label
7668
7946
  }
7669
7947
  ),
@@ -7676,7 +7954,8 @@ function MultiTabBar(props) {
7676
7954
  },
7677
7955
  className: `
7678
7956
  flex-shrink-0 w-3.5 h-3.5 flex items-center justify-center
7679
- rounded hover:bg-gray-300/60 transition-all duration-100 cursor-pointer
7957
+ rounded transition-all duration-100 cursor-pointer
7958
+ ${s.closeBtn}
7680
7959
  ${isActive ? "opacity-100" : "opacity-40 group-hover:opacity-60"}
7681
7960
  `,
7682
7961
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 10, strokeWidth: 2.5 })
@@ -7692,14 +7971,14 @@ function MultiTabBar(props) {
7692
7971
  onTabAdd && /* @__PURE__ */ jsxRuntime.jsx(
7693
7972
  "div",
7694
7973
  {
7695
- className: "flex-shrink-0 flex items-center justify-center border-l border-gray-100 bg-white",
7974
+ className: `flex-shrink-0 flex items-center justify-center ${s.plusBtnArea}`,
7696
7975
  style: { width: PLUS_BTN_WIDTH },
7697
7976
  children: /* @__PURE__ */ jsxRuntime.jsx(
7698
7977
  "button",
7699
7978
  {
7700
7979
  onClick: onTabAdd,
7701
7980
  title: "\uD604\uC7AC \uD398\uC774\uC9C0\uB97C \uC0C8 \uD0ED\uC73C\uB85C \uC5F4\uAE30",
7702
- 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",
7981
+ className: `w-6 h-6 flex items-center justify-center rounded cursor-pointer transition-colors ${s.plusBtn}`,
7703
7982
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 13, strokeWidth: 2 })
7704
7983
  }
7705
7984
  )
@@ -8484,19 +8763,212 @@ function FileUploader(props) {
8484
8763
  /* @__PURE__ */ jsxRuntime.jsx(FilePreviewViewer, { ...viewer.viewerProps, fileTypes, fileNames: files.map((f) => f.name) })
8485
8764
  ] });
8486
8765
  }
8766
+ var VARIANT_STYLES2 = {
8767
+ light: {
8768
+ aside: "bg-white border-r border-gray-200",
8769
+ sectionTitle: "text-xs font-semibold text-gray-500 tracking-wider mb-3",
8770
+ itemActive: "bg-blue-50 text-blue-600",
8771
+ itemInactive: "text-gray-700 hover:bg-gray-100",
8772
+ subItemActive: "text-blue-600 font-medium",
8773
+ subItemInactive: "text-gray-600 hover:bg-gray-50 hover:text-gray-900",
8774
+ childGroupBorder: "border-gray-100",
8775
+ childActive: "bg-blue-50 text-blue-600 font-medium",
8776
+ childInactive: "text-gray-600 hover:bg-gray-50 hover:text-gray-900",
8777
+ grandchildActive: "bg-blue-50 text-blue-600 font-medium",
8778
+ grandchildInactive: "text-gray-500 hover:bg-gray-50 hover:text-gray-900",
8779
+ collapseBtn: "bg-gray-50 hover:bg-gray-100",
8780
+ collapseIcon: "var(--color-sub-darkgray)",
8781
+ rounded: "rounded-lg",
8782
+ itemPy: "py-2",
8783
+ childPy: "py-1.5",
8784
+ grandchildPy: "py-1",
8785
+ itemFont: "font-semibold",
8786
+ itemTextSize: "text-sm",
8787
+ grandchildTextSize: "text-xs",
8788
+ iconClass: "w-4 h-4 flex-shrink-0",
8789
+ activeItemStyle: {},
8790
+ activeChildStyle: {},
8791
+ activeGrandchildStyle: {}
8792
+ },
8793
+ dark: {
8794
+ aside: "bg-gray-900 border-r border-gray-700",
8795
+ sectionTitle: "text-xs font-semibold text-gray-400 tracking-wider mb-3",
8796
+ itemActive: "bg-gray-700 text-white",
8797
+ itemInactive: "text-gray-300 hover:bg-gray-800",
8798
+ subItemActive: "text-blue-400 font-medium",
8799
+ subItemInactive: "text-gray-400 hover:bg-gray-800 hover:text-gray-100",
8800
+ childGroupBorder: "border-gray-700",
8801
+ childActive: "bg-gray-700 text-white font-medium",
8802
+ childInactive: "text-gray-400 hover:bg-gray-800 hover:text-gray-100",
8803
+ grandchildActive: "bg-gray-700 text-blue-400 font-medium",
8804
+ grandchildInactive: "text-gray-500 hover:bg-gray-800 hover:text-gray-100",
8805
+ collapseBtn: "bg-gray-800 hover:bg-gray-700",
8806
+ collapseIcon: "#9ca3af",
8807
+ rounded: "rounded-lg",
8808
+ itemPy: "py-2",
8809
+ childPy: "py-1.5",
8810
+ grandchildPy: "py-1",
8811
+ itemFont: "font-semibold",
8812
+ itemTextSize: "text-sm",
8813
+ grandchildTextSize: "text-xs",
8814
+ iconClass: "w-4 h-4 flex-shrink-0",
8815
+ activeItemStyle: {},
8816
+ activeChildStyle: {},
8817
+ activeGrandchildStyle: {}
8818
+ },
8819
+ "system-dark": {
8820
+ aside: "bg-slate-900 border-r border-slate-700",
8821
+ sectionTitle: "text-[10px] font-bold text-slate-500 uppercase tracking-[0.12em] mb-2 pb-2 border-b border-slate-700/70",
8822
+ itemActive: "bg-slate-800 text-white",
8823
+ itemInactive: "text-slate-300 hover:bg-slate-800/60 hover:text-white",
8824
+ subItemActive: "text-slate-100 font-medium",
8825
+ subItemInactive: "text-slate-400 hover:bg-slate-800/50 hover:text-slate-200",
8826
+ childGroupBorder: "border-slate-700/60",
8827
+ childActive: "bg-slate-800/70 text-slate-100 font-medium",
8828
+ childInactive: "text-slate-400 hover:bg-slate-800/50 hover:text-slate-200",
8829
+ grandchildActive: "text-slate-100 font-medium",
8830
+ grandchildInactive: "text-slate-500 hover:bg-slate-800/50 hover:text-slate-200",
8831
+ collapseBtn: "bg-slate-800 hover:bg-slate-700",
8832
+ collapseIcon: "#94a3b8",
8833
+ rounded: "rounded-none",
8834
+ itemPy: "py-3",
8835
+ childPy: "py-[10px]",
8836
+ grandchildPy: "py-2",
8837
+ itemFont: "font-medium",
8838
+ itemTextSize: "text-sm",
8839
+ grandchildTextSize: "text-xs",
8840
+ iconClass: "w-4.5 h-4.5 flex-shrink-0",
8841
+ activeItemStyle: { borderLeft: "3px solid #818cf8", paddingLeft: "9px" },
8842
+ activeChildStyle: { borderLeft: "2px solid #818cf8", paddingLeft: "14px" },
8843
+ activeGrandchildStyle: { borderLeft: "2px solid #818cf8", paddingLeft: "10px" }
8844
+ },
8845
+ "system-light": {
8846
+ aside: "bg-gray-50 border-r border-gray-200",
8847
+ sectionTitle: "text-[10px] font-bold text-slate-400 uppercase tracking-[0.12em] mb-2 pb-2 border-b border-gray-200",
8848
+ itemActive: "font-medium",
8849
+ itemInactive: "text-slate-700 hover:bg-gray-100",
8850
+ subItemActive: "text-[#2b2b2b] font-medium",
8851
+ subItemInactive: "text-slate-500 hover:bg-gray-100 hover:text-slate-800",
8852
+ childGroupBorder: "border-gray-200",
8853
+ childActive: "font-medium",
8854
+ childInactive: "text-slate-500 hover:bg-gray-100 hover:text-slate-800",
8855
+ grandchildActive: "text-[#2b2b2b] font-medium",
8856
+ grandchildInactive: "text-slate-500 hover:bg-gray-100 hover:text-slate-800",
8857
+ collapseBtn: "bg-gray-100 hover:bg-gray-200",
8858
+ collapseIcon: "#64748b",
8859
+ rounded: "rounded-none",
8860
+ itemPy: "py-3",
8861
+ childPy: "py-[10px]",
8862
+ grandchildPy: "py-2",
8863
+ itemFont: "font-medium",
8864
+ itemTextSize: "text-[15px]",
8865
+ grandchildTextSize: "text-sm",
8866
+ iconClass: "w-4.5 h-4.5 flex-shrink-0",
8867
+ activeItemStyle: {
8868
+ color: "#2b2b2b",
8869
+ backgroundColor: "color-mix(in srgb, #2b2b2b 10%, transparent)",
8870
+ borderLeft: "3px solid #2b2b2b",
8871
+ paddingLeft: "9px"
8872
+ },
8873
+ activeChildStyle: {
8874
+ color: "#2b2b2b",
8875
+ backgroundColor: "color-mix(in srgb, #2b2b2b 10%, transparent)",
8876
+ borderLeft: "2px solid #2b2b2b",
8877
+ paddingLeft: "14px"
8878
+ },
8879
+ activeGrandchildStyle: {
8880
+ color: "#2b2b2b",
8881
+ backgroundColor: "color-mix(in srgb, #2b2b2b 10%, transparent)",
8882
+ borderLeft: "2px solid #2b2b2b",
8883
+ paddingLeft: "10px"
8884
+ }
8885
+ }
8886
+ };
8887
+ function tintedStyle(color) {
8888
+ return {
8889
+ color,
8890
+ backgroundColor: `color-mix(in srgb, ${color} 12%, transparent)`
8891
+ };
8892
+ }
8893
+ function textStyle(color) {
8894
+ return { color };
8895
+ }
8896
+ function solidStyle(color) {
8897
+ return {
8898
+ backgroundColor: `color-mix(in srgb, ${color} 25%, transparent)`,
8899
+ color: "#ffffff",
8900
+ boxShadow: `inset 3px 0 0 ${color}`
8901
+ };
8902
+ }
8903
+ var ACCENT_APPLIERS = {
8904
+ light: {
8905
+ itemActive: tintedStyle,
8906
+ subItemActive: textStyle,
8907
+ childActive: tintedStyle,
8908
+ grandchildActive: textStyle
8909
+ },
8910
+ dark: {
8911
+ itemActive: textStyle,
8912
+ subItemActive: textStyle,
8913
+ childActive: textStyle,
8914
+ grandchildActive: textStyle
8915
+ },
8916
+ "system-dark": {
8917
+ itemActive: (c) => ({
8918
+ ...solidStyle(c),
8919
+ boxShadow: void 0,
8920
+ borderLeft: `3px solid ${c}`,
8921
+ paddingLeft: "9px"
8922
+ }),
8923
+ subItemActive: textStyle,
8924
+ childActive: (c) => ({
8925
+ ...solidStyle(c),
8926
+ boxShadow: void 0,
8927
+ borderLeft: `2px solid ${c}`,
8928
+ paddingLeft: "14px"
8929
+ }),
8930
+ grandchildActive: (c) => ({
8931
+ ...textStyle(c),
8932
+ borderLeft: `2px solid ${c}`,
8933
+ paddingLeft: "10px"
8934
+ })
8935
+ },
8936
+ "system-light": {
8937
+ itemActive: (c) => ({
8938
+ ...tintedStyle(c),
8939
+ borderLeft: `3px solid ${c}`,
8940
+ paddingLeft: "9px"
8941
+ }),
8942
+ subItemActive: textStyle,
8943
+ childActive: (c) => ({
8944
+ ...tintedStyle(c),
8945
+ borderLeft: `2px solid ${c}`,
8946
+ paddingLeft: "14px"
8947
+ }),
8948
+ grandchildActive: (c) => ({
8949
+ ...tintedStyle(c),
8950
+ borderLeft: `2px solid ${c}`,
8951
+ paddingLeft: "10px"
8952
+ })
8953
+ }
8954
+ };
8487
8955
  function Sidebar(props) {
8488
8956
  const navigate = reactRouter.useNavigate();
8489
- const { isOpen: isOpen2, onClose, menuSections, header, className, showCollapseButton = false } = props;
8957
+ const {
8958
+ onClose,
8959
+ menuSections,
8960
+ header,
8961
+ className,
8962
+ showCollapseButton = false,
8963
+ variant = "light",
8964
+ primaryColor
8965
+ } = props;
8490
8966
  const { pathname } = reactRouter.useLocation();
8491
8967
  const [isCollapsed, setIsCollapsed] = React6.useState(false);
8492
- const [isContentVisible, setIsContentVisible] = React6.useState(true);
8493
8968
  const [expandedItems, setExpandedItems] = React6.useState(/* @__PURE__ */ new Set());
8494
8969
  const [expandedSubItems, setExpandedSubItems] = React6.useState(/* @__PURE__ */ new Set());
8495
- React6.useEffect(() => {
8496
- if (isCollapsed) return;
8497
- const timer = setTimeout(() => setIsContentVisible(true), 300);
8498
- return () => clearTimeout(timer);
8499
- }, [isCollapsed]);
8970
+ const s = VARIANT_STYLES2[variant];
8971
+ const applier = ACCENT_APPLIERS[variant];
8500
8972
  React6.useEffect(() => {
8501
8973
  menuSections.forEach((section) => {
8502
8974
  section.items.forEach((item) => {
@@ -8547,167 +9019,178 @@ function Sidebar(props) {
8547
9019
  window.open(path, "_blank", "noopener,noreferrer");
8548
9020
  } else if (path !== "") {
8549
9021
  navigate(path);
8550
- onClose();
9022
+ onClose?.();
8551
9023
  }
8552
9024
  };
8553
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8554
- isOpen2 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 bg-black/10 bg-opacity-50 z-40 lg:hidden", onClick: onClose }),
8555
- /* @__PURE__ */ jsxRuntime.jsxs(
8556
- "aside",
8557
- {
8558
- className: `
8559
- ${isCollapsed ? "w-11 min-w-11" : "min-w-60 w-62"}
8560
- ${isOpen2 ? "translate-x-0" : "-translate-x-full lg:translate-x-0"}
8561
- 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
8562
- transition-transform duration-300 ease-in-out
8563
- ${className ?? ""}
8564
- `,
8565
- children: [
8566
- showCollapseButton && /* @__PURE__ */ jsxRuntime.jsx(
8567
- "button",
8568
- {
8569
- onClick: () => {
8570
- if (!isCollapsed) setIsContentVisible(false);
8571
- setIsCollapsed((prev) => !prev);
8572
- },
8573
- 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",
8574
- children: isCollapsed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelRightClose, { size: 18, color: "var(--color-sub-darkgray)" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelLeftClose, { size: 18, color: "var(--color-sub-darkgray)" })
8575
- }
8576
- ),
8577
- isContentVisible && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-6 pl-6 pr-4", children: [
8578
- /* @__PURE__ */ jsxRuntime.jsx(
8579
- "button",
8580
- {
8581
- onClick: onClose,
8582
- className: "lg:hidden absolute top-4 right-4 p-2 hover:bg-gray-100 rounded-lg transition-colors text-2xl leading-none",
8583
- "aria-label": "Close menu",
8584
- children: "x"
8585
- }
8586
- ),
8587
- header && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-10 mt-2", children: header }),
8588
- /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "space-y-6", children: menuSections.map((section, sectionIndex) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8589
- section.title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-semibold text-gray-500 tracking-wider mb-3", children: section.title }),
8590
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1", children: section.items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8591
- /* @__PURE__ */ jsxRuntime.jsxs(
8592
- "button",
8593
- {
8594
- onClick: () => {
8595
- if (item.children) {
8596
- toggleExpand(item.label);
8597
- } else if (item.path) {
8598
- handleItemClick(item.path);
8599
- }
8600
- },
8601
- className: `
8602
- w-full text-left flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors
8603
- ${!item.children && pathname === item.path ? "bg-blue-50 text-blue-600" : "text-gray-700 hover:bg-gray-100"}
8604
- cursor-pointer
8605
- `,
8606
- children: [
8607
- /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: "w-4 h-4 flex-shrink-0" }),
8608
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: item.label }),
8609
- item.children && /* @__PURE__ */ jsxRuntime.jsx(
8610
- "svg",
8611
- {
8612
- className: `w-4 h-4 transition-transform ${expandedItems.has(item.label) ? "rotate-90" : ""}`,
8613
- fill: "none",
8614
- stroke: "currentColor",
8615
- viewBox: "0 0 24 24",
8616
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" })
8617
- }
8618
- ),
8619
- item.path?.includes("https") && /* @__PURE__ */ jsxRuntime.jsx(
8620
- "svg",
8621
- {
8622
- className: "w-4 h-4 flex-shrink-0 text-gray-400",
8623
- fill: "none",
8624
- stroke: "currentColor",
8625
- viewBox: "0 0 24 24",
8626
- children: /* @__PURE__ */ jsxRuntime.jsx(
8627
- "path",
9025
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9026
+ "aside",
9027
+ {
9028
+ className: `
9029
+ ${isCollapsed ? "w-11 min-w-11" : "min-w-60 w-62"}
9030
+ transition-[width,min-width] duration-300 ease-in-out
9031
+ hide-scrollbar relative h-screen shrink-0 overflow-y-auto overflow-x-hidden
9032
+ ${s.aside}
9033
+ ${className ?? ""}
9034
+ `,
9035
+ children: [
9036
+ showCollapseButton && /* @__PURE__ */ jsxRuntime.jsx(
9037
+ "button",
9038
+ {
9039
+ onClick: () => setIsCollapsed((prev) => !prev),
9040
+ 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}`,
9041
+ children: isCollapsed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelRightClose, { size: 18, color: primaryColor ?? s.collapseIcon }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelLeftClose, { size: 18, color: primaryColor ?? s.collapseIcon })
9042
+ }
9043
+ ),
9044
+ /* @__PURE__ */ jsxRuntime.jsxs(
9045
+ "div",
9046
+ {
9047
+ 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"}`,
9048
+ style: { transitionDelay: isCollapsed ? "0ms" : "150ms" },
9049
+ children: [
9050
+ header && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6 mt-2", children: header }),
9051
+ /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "space-y-6", children: menuSections.map((section, sectionIndex) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
9052
+ section.title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: s.sectionTitle, children: section.title }),
9053
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-0.5", children: section.items.map((item) => {
9054
+ const isActive = !item.children && pathname === item.path;
9055
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
9056
+ /* @__PURE__ */ jsxRuntime.jsxs(
9057
+ "button",
9058
+ {
9059
+ onClick: () => {
9060
+ if (item.children) {
9061
+ toggleExpand(item.label);
9062
+ } else if (item.path) {
9063
+ handleItemClick(item.path);
9064
+ }
9065
+ },
9066
+ className: `
9067
+ w-full text-left flex items-center gap-2 px-3 ${s.itemPy} ${s.rounded} ${s.itemTextSize} ${s.itemFont} transition-colors cursor-pointer
9068
+ ${isActive ? s.itemActive : s.itemInactive}
9069
+ `,
9070
+ style: {
9071
+ ...isActive ? s.activeItemStyle : {},
9072
+ ...isActive && primaryColor ? applier.itemActive(primaryColor) : {}
9073
+ },
9074
+ children: [
9075
+ /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: s.iconClass }),
9076
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: item.label }),
9077
+ item.children && /* @__PURE__ */ jsxRuntime.jsx(
9078
+ "svg",
8628
9079
  {
8629
- strokeLinecap: "round",
8630
- strokeLinejoin: "round",
8631
- strokeWidth: 2,
8632
- d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
9080
+ className: `w-4 h-4 transition-transform ${expandedItems.has(item.label) ? "rotate-90" : ""}`,
9081
+ fill: "none",
9082
+ stroke: "currentColor",
9083
+ viewBox: "0 0 24 24",
9084
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" })
9085
+ }
9086
+ ),
9087
+ item.path?.includes("https") && /* @__PURE__ */ jsxRuntime.jsx(
9088
+ "svg",
9089
+ {
9090
+ className: "w-4 h-4 flex-shrink-0 opacity-50",
9091
+ fill: "none",
9092
+ stroke: "currentColor",
9093
+ viewBox: "0 0 24 24",
9094
+ children: /* @__PURE__ */ jsxRuntime.jsx(
9095
+ "path",
9096
+ {
9097
+ strokeLinecap: "round",
9098
+ strokeLinejoin: "round",
9099
+ strokeWidth: 2,
9100
+ d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
9101
+ }
9102
+ )
8633
9103
  }
8634
9104
  )
8635
- }
8636
- )
8637
- ]
8638
- }
8639
- ),
8640
- item.children && expandedItems.has(item.label) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-[25px] mt-1 space-y-1", children: item.children.map((child, childIndex) => {
8641
- if (child.children && child.children.length > 0) {
8642
- const subKey = `${item.label}::${child.label}`;
8643
- const isSubExpanded = expandedSubItems.has(subKey);
8644
- const isSubActive = child.children.some((gc) => gc.path === pathname);
8645
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8646
- /* @__PURE__ */ jsxRuntime.jsxs(
8647
- "button",
8648
- {
8649
- onClick: () => toggleSubExpand(subKey),
8650
- className: `
8651
- w-full text-left flex items-center justify-between px-3 py-1.5 rounded-lg text-sm transition-colors cursor-pointer
8652
- ${isSubActive ? "text-blue-600 font-medium" : "text-gray-600 hover:bg-gray-50 hover:text-gray-900"}
8653
- `,
8654
- children: [
8655
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: child.label }),
8656
- /* @__PURE__ */ jsxRuntime.jsx(
8657
- "svg",
8658
- {
8659
- className: `w-3 h-3 transition-transform ${isSubExpanded ? "rotate-90" : ""}`,
8660
- fill: "none",
8661
- stroke: "currentColor",
8662
- viewBox: "0 0 24 24",
8663
- children: /* @__PURE__ */ jsxRuntime.jsx(
8664
- "path",
9105
+ ]
9106
+ }
9107
+ ),
9108
+ item.children && expandedItems.has(item.label) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `ml-5 mt-0.5 space-y-0.5 border-l ${s.childGroupBorder}`, children: item.children.map((child, childIndex) => {
9109
+ if (child.children && child.children.length > 0) {
9110
+ const subKey = `${item.label}::${child.label}`;
9111
+ const isSubExpanded = expandedSubItems.has(subKey);
9112
+ const isSubActive = child.children.some((gc) => gc.path === pathname);
9113
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
9114
+ /* @__PURE__ */ jsxRuntime.jsxs(
9115
+ "button",
9116
+ {
9117
+ onClick: () => toggleSubExpand(subKey),
9118
+ className: `
9119
+ w-full text-left flex items-center justify-between pl-4 pr-3 ${s.childPy} ${s.rounded} ${s.itemTextSize} transition-colors cursor-pointer
9120
+ ${isSubActive ? s.subItemActive : s.subItemInactive}
9121
+ `,
9122
+ style: isSubActive && primaryColor ? applier.subItemActive(primaryColor) : void 0,
9123
+ children: [
9124
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: child.label }),
9125
+ /* @__PURE__ */ jsxRuntime.jsx(
9126
+ "svg",
8665
9127
  {
8666
- strokeLinecap: "round",
8667
- strokeLinejoin: "round",
8668
- strokeWidth: 2,
8669
- d: "M9 5l7 7-7 7"
9128
+ className: `w-3 h-3 transition-transform ${isSubExpanded ? "rotate-90" : ""}`,
9129
+ fill: "none",
9130
+ stroke: "currentColor",
9131
+ viewBox: "0 0 24 24",
9132
+ children: /* @__PURE__ */ jsxRuntime.jsx(
9133
+ "path",
9134
+ {
9135
+ strokeLinecap: "round",
9136
+ strokeLinejoin: "round",
9137
+ strokeWidth: 2,
9138
+ d: "M9 5l7 7-7 7"
9139
+ }
9140
+ )
8670
9141
  }
8671
9142
  )
8672
- }
8673
- )
8674
- ]
8675
- }
8676
- ),
8677
- isSubExpanded && /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(
9143
+ ]
9144
+ }
9145
+ ),
9146
+ isSubExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-3 mt-0.5 space-y-0.5 pl-2", children: child.children.map((grandchild) => {
9147
+ const isGrandchildActive = pathname === grandchild.path;
9148
+ return /* @__PURE__ */ jsxRuntime.jsx(
9149
+ "button",
9150
+ {
9151
+ onClick: () => handleItemClick(grandchild.path),
9152
+ className: `
9153
+ w-full text-left px-3 ${s.grandchildPy} ${s.rounded} ${s.grandchildTextSize} transition-colors cursor-pointer
9154
+ ${isGrandchildActive ? s.grandchildActive : s.grandchildInactive}
9155
+ `,
9156
+ style: {
9157
+ ...isGrandchildActive ? s.activeGrandchildStyle : {},
9158
+ ...isGrandchildActive && primaryColor ? applier.grandchildActive(primaryColor) : {}
9159
+ },
9160
+ children: grandchild.label
9161
+ },
9162
+ grandchild.path
9163
+ );
9164
+ }) })
9165
+ ] }, child.label);
9166
+ }
9167
+ const isChildActive = pathname === child.path;
9168
+ return /* @__PURE__ */ jsxRuntime.jsx(
8678
9169
  "button",
8679
9170
  {
8680
- onClick: () => handleItemClick(grandchild.path),
9171
+ onClick: () => handleItemClick(child.path ?? ""),
8681
9172
  className: `
8682
- w-full text-left px-3 py-1 rounded-lg text-sm transition-colors cursor-pointer
8683
- ${pathname === grandchild.path ? "text-blue-600 font-medium" : "text-gray-500 hover:bg-gray-50 hover:text-gray-900"}
8684
- `,
8685
- children: grandchild.label
9173
+ w-full text-left pl-4 pr-3 ${s.childPy} ${s.rounded} ${s.itemTextSize} transition-colors cursor-pointer
9174
+ ${isChildActive ? s.childActive : s.childInactive}
9175
+ `,
9176
+ style: {
9177
+ ...isChildActive ? s.activeChildStyle : {},
9178
+ ...isChildActive && primaryColor ? applier.childActive(primaryColor) : {}
9179
+ },
9180
+ children: child.label
8686
9181
  },
8687
- grandchild.path
8688
- )) })
8689
- ] }, child.label);
8690
- }
8691
- return /* @__PURE__ */ jsxRuntime.jsx(
8692
- "button",
8693
- {
8694
- onClick: () => handleItemClick(child.path ?? ""),
8695
- className: `
8696
- w-full text-left px-3 py-1.5 rounded-lg text-sm transition-colors cursor-pointer
8697
- ${pathname === child.path ? "bg-blue-50 text-blue-600 font-medium" : "text-gray-600 hover:bg-gray-50 hover:text-gray-900"}
8698
- `,
8699
- children: child.label
8700
- },
8701
- childIndex
8702
- );
9182
+ childIndex
9183
+ );
9184
+ }) })
9185
+ ] }, item.label);
8703
9186
  }) })
8704
- ] }, item.label)) })
8705
- ] }, section.title ?? sectionIndex)) })
8706
- ] })
8707
- ]
8708
- }
8709
- )
8710
- ] });
9187
+ ] }, section.title ?? sectionIndex)) })
9188
+ ]
9189
+ }
9190
+ )
9191
+ ]
9192
+ }
9193
+ );
8711
9194
  }
8712
9195
  function useDetailController(params = {}) {
8713
9196
  const { mode: controlledMode, onModeChange } = params;
@@ -9004,10 +9487,7 @@ function DetailModalFrame(props) {
9004
9487
  const isEditMode = controller.mode === "edit";
9005
9488
  const canEdit = Boolean(editSchema && (editFields || renderEditBody));
9006
9489
  return /* @__PURE__ */ jsxRuntime.jsxs(Modal, { isOpen: open, onClose: handleClose, contentClassName: cn("max-w-2xl", contentClassName), children: [
9007
- /* @__PURE__ */ jsxRuntime.jsxs(ModalHeader, { className: "flex flex-row items-center justify-between", children: [
9008
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold text-gray-900", children: title }),
9009
- !isEditMode && canEdit && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", size: "sm", onClick: handleEdit, children: "\uC218\uC815" })
9010
- ] }),
9490
+ /* @__PURE__ */ jsxRuntime.jsx(ModalHeader, { className: "flex flex-row items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold text-gray-900", children: title }) }),
9011
9491
  /* @__PURE__ */ jsxRuntime.jsxs(ModalBody, { className: "flex-1 min-h-0 overflow-hidden p-0 flex flex-col", children: [
9012
9492
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(
9013
9493
  DetailContent,
@@ -9027,8 +9507,9 @@ function DetailModalFrame(props) {
9027
9507
  ] }),
9028
9508
  /* @__PURE__ */ jsxRuntime.jsx(ModalFooter, { className: "flex justify-end gap-2", children: isEditMode ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9029
9509
  /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: handleCancelEdit, disabled: controller.isSaving, children: "\uCDE8\uC18C" }),
9030
- /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", form: controller.formId, variant: "save", disabled: controller.isSaving, children: controller.isSaving ? "\uC800\uC7A5 \uC911..." : "\uC800\uC7A5" })
9510
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", form: controller.formId, variant: "save", disabled: controller.isSaving, children: "\uC800\uC7A5" })
9031
9511
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9512
+ canEdit && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: handleEdit, children: "\uC218\uC815" }),
9032
9513
  renderFooterExtra,
9033
9514
  /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: handleClose, children: "\uB2EB\uAE30" })
9034
9515
  ] }) })