@farmzone/fz-react-ui 1.0.2 → 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 }),
@@ -1319,31 +1348,6 @@ function Spinner(props) {
1319
1348
  }
1320
1349
  );
1321
1350
  }
1322
-
1323
- // src/config/zIndex.ts
1324
- var Z_INDEX = {
1325
- TABLE_COL_STICKY: 1,
1326
- TABLE_FIXED_CELL: 5,
1327
- TABLE_FIXED_HEADER_CELL: 10,
1328
- LOCAL_OVERLAY: 10,
1329
- LAYOUT_HEADER: 20,
1330
- DROPDOWN_LOCAL: 20,
1331
- SIDEBAR_CONTAINER: 30,
1332
- SIDEBAR_BACKDROP: 40,
1333
- SIDEBAR: 50,
1334
- DROPDOWN: 50,
1335
- CALENDAR_ANCHORED: 50,
1336
- LOADING_OVERLAY_DEFAULT: 50,
1337
- DRAWING_CANVAS_1: 51,
1338
- DRAWING_CANVAS_2: 52,
1339
- DRAWING: 53,
1340
- MODAL_OVERLAY: 54,
1341
- MODAL: 55,
1342
- CALENDAR_PORTAL: 60,
1343
- SELECT_CONTENT: 100,
1344
- TOP: 9999,
1345
- MODAL_Z_STEP: 10
1346
- };
1347
1351
  function LoadingOverlay(props) {
1348
1352
  const {
1349
1353
  isVisible,
@@ -1958,13 +1962,18 @@ function ToastProvider() {
1958
1962
  }
1959
1963
 
1960
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
+ }
1961
1970
  function parseLocalYmd(ymd) {
1962
1971
  const m = ymd.trim().match(/^(\d{4})-(\d{2})-(\d{2})$/);
1963
1972
  if (!m) return null;
1964
1973
  const y = Number(m[1]);
1965
1974
  const mo = Number(m[2]) - 1;
1966
1975
  const d = Number(m[3]);
1967
- const dt = new Date(y, mo, d);
1976
+ const dt = makeLocalDate(y, mo, d);
1968
1977
  if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) return null;
1969
1978
  return dt;
1970
1979
  }
@@ -1998,7 +2007,7 @@ function isCalendarDayAfterMax(date, maxYmd) {
1998
2007
  return ax > bx;
1999
2008
  }
2000
2009
  function formatDate(date) {
2001
- const year = date.getFullYear();
2010
+ const year = String(date.getFullYear()).padStart(4, "0");
2002
2011
  const month = String(date.getMonth() + 1).padStart(2, "0");
2003
2012
  const day = String(date.getDate()).padStart(2, "0");
2004
2013
  return `${year}-${month}-${day}`;
@@ -2017,7 +2026,6 @@ function validateAndFormatInput(input) {
2017
2026
  if (numbers.length === 0) return "";
2018
2027
  const clampYear = (raw) => {
2019
2028
  const n = parseInt(raw, 10);
2020
- if (n < 1) return "0001";
2021
2029
  if (n > 9999) return "9999";
2022
2030
  return raw;
2023
2031
  };
@@ -2040,7 +2048,7 @@ function validateAndFormatInput(input) {
2040
2048
  let day = dayRaw;
2041
2049
  if (day.length === 2) {
2042
2050
  const dayNum = parseInt(day, 10);
2043
- const lastDayOfMonth = new Date(parseInt(year, 10), parseInt(month, 10), 0).getDate();
2051
+ const lastDayOfMonth = makeLocalDate(parseInt(year, 10), parseInt(month, 10), 0).getDate();
2044
2052
  if (dayNum < 1) day = "01";
2045
2053
  else if (dayNum > lastDayOfMonth) day = lastDayOfMonth.toString().padStart(2, "0");
2046
2054
  }
@@ -2048,7 +2056,7 @@ function validateAndFormatInput(input) {
2048
2056
  }
2049
2057
 
2050
2058
  // src/components/DatePicker/calendar/config/default.ts
2051
- var DEFAULT_CALENDAR_YEAR_HALF_SPAN = 25;
2059
+ var DEFAULT_CALENDAR_YEAR_HALF_SPAN = 50;
2052
2060
  var CALENDAR_WEEKDAY_LABELS = ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"];
2053
2061
  var CALENDAR_RANGE_HINT = {
2054
2062
  start: "\uC2DC\uC791 \uB0A0\uC9DC\uB97C \uC120\uD0DD\uD558\uC138\uC694",
@@ -2072,7 +2080,7 @@ function parseYmdDateString(value) {
2072
2080
  return parseLocalYmd(value);
2073
2081
  }
2074
2082
  function resolveCalendarYearBounds(anchorYear, calendarYearRange) {
2075
- const fallbackMin = anchorYear - DEFAULT_CALENDAR_YEAR_HALF_SPAN;
2083
+ const fallbackMin = Math.max(0, anchorYear - DEFAULT_CALENDAR_YEAR_HALF_SPAN);
2076
2084
  const fallbackMax = anchorYear + DEFAULT_CALENDAR_YEAR_HALF_SPAN;
2077
2085
  const rawMin = calendarYearRange?.minYear?.trim();
2078
2086
  const rawMax = calendarYearRange?.maxYear?.trim();
@@ -2084,7 +2092,7 @@ function resolveCalendarYearBounds(anchorYear, calendarYearRange) {
2084
2092
  if (Number.isNaN(minParsed) || Number.isNaN(maxParsed) || minParsed > maxParsed) {
2085
2093
  return { minYear: fallbackMin, maxYear: fallbackMax };
2086
2094
  }
2087
- return { minYear: minParsed, maxYear: maxParsed };
2095
+ return { minYear: Math.max(0, minParsed), maxYear: maxParsed };
2088
2096
  }
2089
2097
  function buildCalendarYearSelectOptions(maxDate, anchorYear = (/* @__PURE__ */ new Date()).getFullYear(), calendarYearRange) {
2090
2098
  const { minYear, maxYear } = resolveCalendarYearBounds(anchorYear, calendarYearRange);
@@ -2093,7 +2101,7 @@ function buildCalendarYearSelectOptions(maxDate, anchorYear = (/* @__PURE__ */ n
2093
2101
  for (let y = maxYear; y >= minYear; y--) {
2094
2102
  options.push({
2095
2103
  value: String(y),
2096
- label: `${y}\uB144`,
2104
+ label: `${String(y).padStart(4, "0")}\uB144`,
2097
2105
  disabled: maxSelectableYear !== null && y > maxSelectableYear
2098
2106
  });
2099
2107
  }
@@ -2115,8 +2123,8 @@ function buildCalendarMonthSelectOptions(viewYear, maxDate) {
2115
2123
  return options;
2116
2124
  }
2117
2125
  function getCalendarMonthGrid(year, monthIndex) {
2118
- const firstDay = new Date(year, monthIndex, 1);
2119
- const daysInMonth = new Date(year, monthIndex + 1, 0).getDate();
2126
+ const firstDay = makeLocalDate(year, monthIndex, 1);
2127
+ const daysInMonth = makeLocalDate(year, monthIndex + 1, 0).getDate();
2120
2128
  const leadingEmpty = firstDay.getDay();
2121
2129
  const totalCells = leadingEmpty + daysInMonth;
2122
2130
  const trailingEmpty = totalCells % 7 === 0 ? 0 : 7 - totalCells % 7;
@@ -2124,7 +2132,7 @@ function getCalendarMonthGrid(year, monthIndex) {
2124
2132
  }
2125
2133
  function disableNextCalendarMonth(year, monthIndex, maxDate) {
2126
2134
  if (maxDate === void 0) return false;
2127
- return isCalendarDayAfterMax(new Date(year, monthIndex + 1, 1), maxDate);
2135
+ return isCalendarDayAfterMax(makeLocalDate(year, monthIndex + 1, 1), maxDate);
2128
2136
  }
2129
2137
  function scrollOpenYearSelectCheckedIntoView() {
2130
2138
  requestAnimationFrame(() => {
@@ -2170,7 +2178,7 @@ function RenderCalendar(props) {
2170
2178
  } = props;
2171
2179
  const year = currentDate.getFullYear();
2172
2180
  const monthIndex = currentDate.getMonth();
2173
- const anchorYear = (/* @__PURE__ */ new Date()).getFullYear();
2181
+ const anchorYear = year;
2174
2182
  const disableNextMonth = disableNextCalendarMonth(year, monthIndex, maxDate);
2175
2183
  const { leadingEmpty, daysInMonth, trailingEmpty } = getCalendarMonthGrid(year, monthIndex);
2176
2184
  const yearOptions = buildCalendarYearSelectOptions(maxDate, anchorYear, calendarYearRange);
@@ -2184,7 +2192,7 @@ function RenderCalendar(props) {
2184
2192
  days.push(/* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-8", "aria-hidden": true }, `lead-${i}`));
2185
2193
  }
2186
2194
  for (let d = 1; d <= daysInMonth; d++) {
2187
- const date = new Date(year, monthIndex, d);
2195
+ const date = makeLocalDate(year, monthIndex, d);
2188
2196
  const dateFormatted = formatDate(date);
2189
2197
  const isToday = dateFormatted === todayFormatted;
2190
2198
  const isSelected = dateFormatted === selectedSingleYmd;
@@ -2283,7 +2291,7 @@ function RenderCalendar(props) {
2283
2291
  ]
2284
2292
  }
2285
2293
  ) : /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-lg font-semibold", children: [
2286
- year,
2294
+ String(year).padStart(4, "0"),
2287
2295
  "\uB144 ",
2288
2296
  monthIndex + 1,
2289
2297
  "\uC6D4"
@@ -2364,16 +2372,16 @@ function useCalendarState(props) {
2364
2372
  setIsCalendarOpen(false);
2365
2373
  };
2366
2374
  const goToPreviousMonth = () => {
2367
- setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
2375
+ setCurrentDate(makeLocalDate(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
2368
2376
  };
2369
2377
  const goToNextMonth = () => {
2370
- setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1));
2378
+ setCurrentDate(makeLocalDate(currentDate.getFullYear(), currentDate.getMonth() + 1, 1));
2371
2379
  };
2372
2380
  const handleYearChange = (year) => {
2373
- setCurrentDate(new Date(year, currentDate.getMonth(), 1));
2381
+ setCurrentDate(makeLocalDate(year, currentDate.getMonth(), 1));
2374
2382
  };
2375
2383
  const handleMonthChange = (month) => {
2376
- setCurrentDate(new Date(currentDate.getFullYear(), month, 1));
2384
+ setCurrentDate(makeLocalDate(currentDate.getFullYear(), month, 1));
2377
2385
  };
2378
2386
  return {
2379
2387
  isCalendarOpen,
@@ -2500,6 +2508,7 @@ function DatePickerRange(props) {
2500
2508
  return;
2501
2509
  }
2502
2510
  onRangeChange?.(formatted, endInputValue);
2511
+ if (isCalendarOpen && calendarPosition === "start") navigateToDateValue(formatted);
2503
2512
  } else if (formatted.length === 0) {
2504
2513
  onRangeChange?.(formatted, endInputValue);
2505
2514
  }
@@ -2513,6 +2522,7 @@ function DatePickerRange(props) {
2513
2522
  return;
2514
2523
  }
2515
2524
  onRangeChange?.(startInputValue, formatted);
2525
+ if (isCalendarOpen && calendarPosition === "end") navigateToDateValue(formatted);
2516
2526
  } else if (formatted.length === 0) {
2517
2527
  onRangeChange?.(startInputValue, formatted);
2518
2528
  }
@@ -2687,6 +2697,7 @@ function DatePickerSingle(props) {
2687
2697
  autoDatePosition,
2688
2698
  containerRef,
2689
2699
  toggleCalendar,
2700
+ navigateToDateValue,
2690
2701
  closeCalendar,
2691
2702
  goToPreviousMonth,
2692
2703
  goToNextMonth,
@@ -2704,7 +2715,7 @@ function DatePickerSingle(props) {
2704
2715
  const dateMatch = formatted.match(/^(\d{4})-(\d{2})-(\d{2})$/);
2705
2716
  if (dateMatch) {
2706
2717
  const [, year, month, day] = dateMatch;
2707
- const date = new Date(
2718
+ const date = makeLocalDate(
2708
2719
  Number.parseInt(year, 10),
2709
2720
  Number.parseInt(month, 10) - 1,
2710
2721
  Number.parseInt(day, 10)
@@ -2716,6 +2727,7 @@ function DatePickerSingle(props) {
2716
2727
  return;
2717
2728
  }
2718
2729
  onChange?.(formatted);
2730
+ if (isCalendarOpen) navigateToDateValue(formatted);
2719
2731
  }
2720
2732
  }
2721
2733
  } else {
@@ -3652,9 +3664,10 @@ function ConfirmModal(props) {
3652
3664
  ] }),
3653
3665
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-8 items-center justify-end gap-2", children: [
3654
3666
  !isHideOkButton && /* @__PURE__ */ jsxRuntime.jsx(
3655
- Button,
3667
+ DebouncedButton,
3656
3668
  {
3657
3669
  ref: okButtonRef,
3670
+ debounceDelay: 500,
3658
3671
  variant: "outline",
3659
3672
  onClick: handleOkClick,
3660
3673
  className: "focus-visible:ring-2 focus-visible:ring-blue-400 focus-visible:ring-offset-1",
@@ -7644,7 +7657,17 @@ function PageFilter(props) {
7644
7657
  const rowGap2 = index !== 0 ? option.label ? gap : 5 : 0;
7645
7658
  switch (option.type) {
7646
7659
  case "input":
7647
- return /* @__PURE__ */ jsxRuntime.jsx(LabeledFilterOption, { label: option.label, gap: rowGap2, children: /* @__PURE__ */ jsxRuntime.jsx(Input2, { placeholder: option.placeholder, value: currentValue, onChange: handleInputChange(optionKey) }) }, String(optionKey));
7660
+ return /* @__PURE__ */ jsxRuntime.jsx(LabeledFilterOption, { label: option.label, gap: rowGap2, children: /* @__PURE__ */ jsxRuntime.jsx(
7661
+ Input2,
7662
+ {
7663
+ placeholder: option.placeholder,
7664
+ value: currentValue,
7665
+ onChange: handleInputChange(optionKey),
7666
+ onKeyDown: (e) => {
7667
+ if (e.key === "Enter") handleSubmit();
7668
+ }
7669
+ }
7670
+ ) }, String(optionKey));
7648
7671
  case "select":
7649
7672
  return /* @__PURE__ */ jsxRuntime.jsx(LabeledFilterOption, { label: option.label, gap: rowGap2, children: /* @__PURE__ */ jsxRuntime.jsx(
7650
7673
  Select2,
@@ -7718,9 +7741,84 @@ function PageFilter(props) {
7718
7741
  }
7719
7742
  );
7720
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
+ };
7721
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
+ }
7722
7819
  function MultiTabBar(props) {
7723
- const { tabs, activeTabId, onTabClick, onTabClose, onTabAdd } = props;
7820
+ const { tabs, activeTabId, onTabClick, onTabClose, onTabAdd, variant = "light", primaryColor } = props;
7821
+ const s = VARIANT_STYLES[variant];
7724
7822
  const scrollRef = React6.useRef(null);
7725
7823
  const tabRefs = React6.useRef(/* @__PURE__ */ new Map());
7726
7824
  const [showLeftFade, setShowLeftFade] = React6.useState(false);
@@ -7773,13 +7871,23 @@ function MultiTabBar(props) {
7773
7871
  const handleStopDrag = () => {
7774
7872
  isDragging.current = false;
7775
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
+ }, []);
7776
7884
  if (tabs.length === 0) return null;
7777
- 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: [
7778
7886
  showLeftFade && /* @__PURE__ */ jsxRuntime.jsx(
7779
7887
  "div",
7780
7888
  {
7781
7889
  className: "absolute left-0 top-0 bottom-0 w-10 pointer-events-none z-10",
7782
- style: { background: "linear-gradient(to right, white 30%, transparent)" }
7890
+ style: { background: easedFade("to right", s.fadeOpacity) }
7783
7891
  }
7784
7892
  ),
7785
7893
  showRightFade && /* @__PURE__ */ jsxRuntime.jsx(
@@ -7788,7 +7896,7 @@ function MultiTabBar(props) {
7788
7896
  className: "absolute top-0 bottom-0 w-10 pointer-events-none z-10",
7789
7897
  style: {
7790
7898
  right: PLUS_BTN_WIDTH,
7791
- background: "linear-gradient(to left, white 30%, transparent)"
7899
+ background: easedFade("to left", s.fadeOpacity)
7792
7900
  }
7793
7901
  }
7794
7902
  ),
@@ -7796,7 +7904,7 @@ function MultiTabBar(props) {
7796
7904
  "div",
7797
7905
  {
7798
7906
  ref: scrollRef,
7799
- 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`,
7800
7908
  style: { scrollbarWidth: "none" },
7801
7909
  onScroll: updateFades,
7802
7910
  onMouseDown: handleMouseDown,
@@ -7817,17 +7925,23 @@ function MultiTabBar(props) {
7817
7925
  if (!isActive) onTabClick(tab.id);
7818
7926
  },
7819
7927
  className: `
7820
- group relative flex items-center gap-1.5 px-3 py-1.5 flex-shrink-0
7821
- 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
7822
7930
  transition-colors duration-100
7823
- ${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}
7824
7932
  `,
7825
7933
  children: [
7826
- 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
+ ),
7827
7941
  /* @__PURE__ */ jsxRuntime.jsx(
7828
7942
  "span",
7829
7943
  {
7830
- 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}`,
7831
7945
  children: tab.label
7832
7946
  }
7833
7947
  ),
@@ -7840,7 +7954,8 @@ function MultiTabBar(props) {
7840
7954
  },
7841
7955
  className: `
7842
7956
  flex-shrink-0 w-3.5 h-3.5 flex items-center justify-center
7843
- rounded hover:bg-gray-300/60 transition-all duration-100 cursor-pointer
7957
+ rounded transition-all duration-100 cursor-pointer
7958
+ ${s.closeBtn}
7844
7959
  ${isActive ? "opacity-100" : "opacity-40 group-hover:opacity-60"}
7845
7960
  `,
7846
7961
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 10, strokeWidth: 2.5 })
@@ -7856,14 +7971,14 @@ function MultiTabBar(props) {
7856
7971
  onTabAdd && /* @__PURE__ */ jsxRuntime.jsx(
7857
7972
  "div",
7858
7973
  {
7859
- 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}`,
7860
7975
  style: { width: PLUS_BTN_WIDTH },
7861
7976
  children: /* @__PURE__ */ jsxRuntime.jsx(
7862
7977
  "button",
7863
7978
  {
7864
7979
  onClick: onTabAdd,
7865
7980
  title: "\uD604\uC7AC \uD398\uC774\uC9C0\uB97C \uC0C8 \uD0ED\uC73C\uB85C \uC5F4\uAE30",
7866
- 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}`,
7867
7982
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 13, strokeWidth: 2 })
7868
7983
  }
7869
7984
  )
@@ -8648,19 +8763,212 @@ function FileUploader(props) {
8648
8763
  /* @__PURE__ */ jsxRuntime.jsx(FilePreviewViewer, { ...viewer.viewerProps, fileTypes, fileNames: files.map((f) => f.name) })
8649
8764
  ] });
8650
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
+ };
8651
8955
  function Sidebar(props) {
8652
8956
  const navigate = reactRouter.useNavigate();
8653
- 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;
8654
8966
  const { pathname } = reactRouter.useLocation();
8655
8967
  const [isCollapsed, setIsCollapsed] = React6.useState(false);
8656
- const [isContentVisible, setIsContentVisible] = React6.useState(true);
8657
8968
  const [expandedItems, setExpandedItems] = React6.useState(/* @__PURE__ */ new Set());
8658
8969
  const [expandedSubItems, setExpandedSubItems] = React6.useState(/* @__PURE__ */ new Set());
8659
- React6.useEffect(() => {
8660
- if (isCollapsed) return;
8661
- const timer = setTimeout(() => setIsContentVisible(true), 300);
8662
- return () => clearTimeout(timer);
8663
- }, [isCollapsed]);
8970
+ const s = VARIANT_STYLES2[variant];
8971
+ const applier = ACCENT_APPLIERS[variant];
8664
8972
  React6.useEffect(() => {
8665
8973
  menuSections.forEach((section) => {
8666
8974
  section.items.forEach((item) => {
@@ -8711,167 +9019,178 @@ function Sidebar(props) {
8711
9019
  window.open(path, "_blank", "noopener,noreferrer");
8712
9020
  } else if (path !== "") {
8713
9021
  navigate(path);
8714
- onClose();
9022
+ onClose?.();
8715
9023
  }
8716
9024
  };
8717
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8718
- isOpen2 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 bg-black/10 bg-opacity-50 z-40 lg:hidden", onClick: onClose }),
8719
- /* @__PURE__ */ jsxRuntime.jsxs(
8720
- "aside",
8721
- {
8722
- className: `
8723
- ${isCollapsed ? "w-11 min-w-11" : "min-w-60 w-62"}
8724
- ${isOpen2 ? "translate-x-0" : "-translate-x-full lg:translate-x-0"}
8725
- 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
8726
- transition-transform duration-300 ease-in-out
8727
- ${className ?? ""}
8728
- `,
8729
- children: [
8730
- showCollapseButton && /* @__PURE__ */ jsxRuntime.jsx(
8731
- "button",
8732
- {
8733
- onClick: () => {
8734
- if (!isCollapsed) setIsContentVisible(false);
8735
- setIsCollapsed((prev) => !prev);
8736
- },
8737
- 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",
8738
- 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)" })
8739
- }
8740
- ),
8741
- isContentVisible && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-6 pl-6 pr-4", children: [
8742
- /* @__PURE__ */ jsxRuntime.jsx(
8743
- "button",
8744
- {
8745
- onClick: onClose,
8746
- className: "lg:hidden absolute top-4 right-4 p-2 hover:bg-gray-100 rounded-lg transition-colors text-2xl leading-none",
8747
- "aria-label": "Close menu",
8748
- children: "x"
8749
- }
8750
- ),
8751
- header && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-10 mt-2", children: header }),
8752
- /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "space-y-6", children: menuSections.map((section, sectionIndex) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8753
- section.title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-semibold text-gray-500 tracking-wider mb-3", children: section.title }),
8754
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1", children: section.items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8755
- /* @__PURE__ */ jsxRuntime.jsxs(
8756
- "button",
8757
- {
8758
- onClick: () => {
8759
- if (item.children) {
8760
- toggleExpand(item.label);
8761
- } else if (item.path) {
8762
- handleItemClick(item.path);
8763
- }
8764
- },
8765
- className: `
8766
- w-full text-left flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors
8767
- ${!item.children && pathname === item.path ? "bg-blue-50 text-blue-600" : "text-gray-700 hover:bg-gray-100"}
8768
- cursor-pointer
8769
- `,
8770
- children: [
8771
- /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: "w-4 h-4 flex-shrink-0" }),
8772
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: item.label }),
8773
- item.children && /* @__PURE__ */ jsxRuntime.jsx(
8774
- "svg",
8775
- {
8776
- className: `w-4 h-4 transition-transform ${expandedItems.has(item.label) ? "rotate-90" : ""}`,
8777
- fill: "none",
8778
- stroke: "currentColor",
8779
- viewBox: "0 0 24 24",
8780
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" })
8781
- }
8782
- ),
8783
- item.path?.includes("https") && /* @__PURE__ */ jsxRuntime.jsx(
8784
- "svg",
8785
- {
8786
- className: "w-4 h-4 flex-shrink-0 text-gray-400",
8787
- fill: "none",
8788
- stroke: "currentColor",
8789
- viewBox: "0 0 24 24",
8790
- children: /* @__PURE__ */ jsxRuntime.jsx(
8791
- "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",
9079
+ {
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",
8792
9089
  {
8793
- strokeLinecap: "round",
8794
- strokeLinejoin: "round",
8795
- strokeWidth: 2,
8796
- d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
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
+ )
8797
9103
  }
8798
9104
  )
8799
- }
8800
- )
8801
- ]
8802
- }
8803
- ),
8804
- item.children && expandedItems.has(item.label) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-[25px] mt-1 space-y-1", children: item.children.map((child, childIndex) => {
8805
- if (child.children && child.children.length > 0) {
8806
- const subKey = `${item.label}::${child.label}`;
8807
- const isSubExpanded = expandedSubItems.has(subKey);
8808
- const isSubActive = child.children.some((gc) => gc.path === pathname);
8809
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8810
- /* @__PURE__ */ jsxRuntime.jsxs(
8811
- "button",
8812
- {
8813
- onClick: () => toggleSubExpand(subKey),
8814
- className: `
8815
- w-full text-left flex items-center justify-between px-3 py-1.5 rounded-lg text-sm transition-colors cursor-pointer
8816
- ${isSubActive ? "text-blue-600 font-medium" : "text-gray-600 hover:bg-gray-50 hover:text-gray-900"}
8817
- `,
8818
- children: [
8819
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: child.label }),
8820
- /* @__PURE__ */ jsxRuntime.jsx(
8821
- "svg",
8822
- {
8823
- className: `w-3 h-3 transition-transform ${isSubExpanded ? "rotate-90" : ""}`,
8824
- fill: "none",
8825
- stroke: "currentColor",
8826
- viewBox: "0 0 24 24",
8827
- children: /* @__PURE__ */ jsxRuntime.jsx(
8828
- "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",
8829
9127
  {
8830
- strokeLinecap: "round",
8831
- strokeLinejoin: "round",
8832
- strokeWidth: 2,
8833
- 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
+ )
8834
9141
  }
8835
9142
  )
8836
- }
8837
- )
8838
- ]
8839
- }
8840
- ),
8841
- 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(
8842
9169
  "button",
8843
9170
  {
8844
- onClick: () => handleItemClick(grandchild.path),
9171
+ onClick: () => handleItemClick(child.path ?? ""),
8845
9172
  className: `
8846
- w-full text-left px-3 py-1 rounded-lg text-sm transition-colors cursor-pointer
8847
- ${pathname === grandchild.path ? "text-blue-600 font-medium" : "text-gray-500 hover:bg-gray-50 hover:text-gray-900"}
8848
- `,
8849
- 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
8850
9181
  },
8851
- grandchild.path
8852
- )) })
8853
- ] }, child.label);
8854
- }
8855
- return /* @__PURE__ */ jsxRuntime.jsx(
8856
- "button",
8857
- {
8858
- onClick: () => handleItemClick(child.path ?? ""),
8859
- className: `
8860
- w-full text-left px-3 py-1.5 rounded-lg text-sm transition-colors cursor-pointer
8861
- ${pathname === child.path ? "bg-blue-50 text-blue-600 font-medium" : "text-gray-600 hover:bg-gray-50 hover:text-gray-900"}
8862
- `,
8863
- children: child.label
8864
- },
8865
- childIndex
8866
- );
9182
+ childIndex
9183
+ );
9184
+ }) })
9185
+ ] }, item.label);
8867
9186
  }) })
8868
- ] }, item.label)) })
8869
- ] }, section.title ?? sectionIndex)) })
8870
- ] })
8871
- ]
8872
- }
8873
- )
8874
- ] });
9187
+ ] }, section.title ?? sectionIndex)) })
9188
+ ]
9189
+ }
9190
+ )
9191
+ ]
9192
+ }
9193
+ );
8875
9194
  }
8876
9195
  function useDetailController(params = {}) {
8877
9196
  const { mode: controlledMode, onModeChange } = params;
@@ -9168,10 +9487,7 @@ function DetailModalFrame(props) {
9168
9487
  const isEditMode = controller.mode === "edit";
9169
9488
  const canEdit = Boolean(editSchema && (editFields || renderEditBody));
9170
9489
  return /* @__PURE__ */ jsxRuntime.jsxs(Modal, { isOpen: open, onClose: handleClose, contentClassName: cn("max-w-2xl", contentClassName), children: [
9171
- /* @__PURE__ */ jsxRuntime.jsxs(ModalHeader, { className: "flex flex-row items-center justify-between", children: [
9172
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold text-gray-900", children: title }),
9173
- !isEditMode && canEdit && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", size: "sm", onClick: handleEdit, children: "\uC218\uC815" })
9174
- ] }),
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 }) }),
9175
9491
  /* @__PURE__ */ jsxRuntime.jsxs(ModalBody, { className: "flex-1 min-h-0 overflow-hidden p-0 flex flex-col", children: [
9176
9492
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(
9177
9493
  DetailContent,
@@ -9191,8 +9507,9 @@ function DetailModalFrame(props) {
9191
9507
  ] }),
9192
9508
  /* @__PURE__ */ jsxRuntime.jsx(ModalFooter, { className: "flex justify-end gap-2", children: isEditMode ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9193
9509
  /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: handleCancelEdit, disabled: controller.isSaving, children: "\uCDE8\uC18C" }),
9194
- /* @__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" })
9195
9511
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9512
+ canEdit && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: handleEdit, children: "\uC218\uC815" }),
9196
9513
  renderFooterExtra,
9197
9514
  /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: handleClose, children: "\uB2EB\uAE30" })
9198
9515
  ] }) })