@farmzone/fz-react-ui 1.0.14 → 1.0.16

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.d.cts CHANGED
@@ -331,7 +331,7 @@ interface PaginationProps {
331
331
  onPageChange?: (page: number) => void;
332
332
  showTotalCount?: boolean;
333
333
  }
334
- declare function Pagination(props: PaginationProps): react.JSX.Element | null;
334
+ declare function Pagination(props: PaginationProps): react.JSX.Element;
335
335
 
336
336
  type BreadCrumbItem = {
337
337
  label: string;
@@ -1152,9 +1152,19 @@ interface MenuSection {
1152
1152
  items: Array<MenuItem>;
1153
1153
  }
1154
1154
  type SidebarVariant = "light" | "dark" | "system-dark" | "system-light";
1155
+ interface SidebarHeaderRenderProps {
1156
+ isCollapsed: boolean;
1157
+ toggleCollapse: () => void;
1158
+ }
1155
1159
  interface SidebarProps {
1156
1160
  menuSections: Array<MenuSection>;
1157
- header?: ReactNode;
1161
+ /**
1162
+ * ReactNode로 넘기면 기존과 동일하게 nav와 함께 collapse 시 fade-out 됨.
1163
+ * 함수로 넘기면 collapse 상태와 무관하게 항상 렌더링되며, { isCollapsed, toggleCollapse }를
1164
+ * 전달받아 헤더 내부에 자체 collapse 토글 UI를 둘 수 있음 — 이 경우 showCollapseButton은 무시되고
1165
+ * 기본 floating 버튼은 렌더링되지 않음.
1166
+ */
1167
+ header?: ReactNode | ((props: SidebarHeaderRenderProps) => ReactNode);
1158
1168
  footer?: ReactNode;
1159
1169
  className?: string;
1160
1170
  showCollapseButton?: boolean;
@@ -1415,4 +1425,4 @@ declare const Z_INDEX: {
1415
1425
  };
1416
1426
  type ZIndexKey = keyof typeof Z_INDEX;
1417
1427
 
1418
- export { ArrowIcon, Badge, type BadgeProps, type BaseParams, BaseUploader, type BaseUploaderProps, BreadCrumb, type BreadCrumbItem, type BreadCrumbMenuItem, type BreadCrumbProps, type BreadCrumbRoot, Button, type ButtonProps, type ButtonSize, type ButtonVariant, type CalendarYearRange, Carousel, type CarouselProps, type CarouselRef, Checkbox, type CheckboxProps, type Column, type ConfirmOptions, DatePicker, DebouncedButton, type DebouncedButtonProps, DetailContent, type DetailContentProps, type DetailField, type DetailFieldOrSection, DetailModalFrame, type DetailMode, DetailPageFrame, Empty, ExcelUploader, FilePreviewViewer, FileUploader, type FilterRow, type FormFieldConfig, Input, type InputProps, Loading, LoadingOverlay, type LoadingOverlayProps, type LoadingProps, type MenuChild, type MenuGrandchild, type MenuItem, type MenuSection, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalIconHeader, type ModalIconHeaderProps, ModalOverlay, type ModalProps, type ModalType, MultiTabBar as MultiTab, type MultiTab as MultiTabItem, PageFilter, type PageParams, Pagination, type PaginationProps, Popover, type PopoverAlign, PopoverAnchor, PopoverClose, PopoverContent, type PopoverContentProps, PopoverDescription, PopoverFooter, PopoverHeader, type PopoverPosition, PopoverTitle, PopoverTrigger, Radio, RadioGroup, type RadioGroupProps, type RadioOptionType, type RadioProps, ScrollArea, ScrollBar, SearchAddress, type SearchAddressProps, SearchBar, type SearchBarProps, Select, Select$1 as SelectBase, SelectContent, SelectGroup, SelectItem, SelectLabel, type SelectOptionType, type SelectProps, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Sidebar, type SidebarProps, SimplePopover, type SimplePopoverProps, Skeleton, type SkeletonProps, Slider, type SliderProps, type SortOption, type SortOrder, type SortParams, Spinner, type SpinnerColor, type SpinnerProps, type SpinnerSize, SubmitForm, Switch, type SwitchProps, Tab, type TabItem, type TabProps, Table, Textarea, type TextareaProps, type ToastPosition, type ToastProps, ToastProvider, type ToastType, ToggleSwitch, Tooltip, type TooltipProps, type UseParams, type YmdDateString, type ZIndexKey, Z_INDEX, buildBreadCrumbItems, buttonVariants, cn, confirmModal, findParentMenuItem, findParentMenuName, formatDate, getFileLabel, getPreviewFileType, isCalendarDayAfterMax, isDateInRange, switchTrackSizes, textVariants, toast, useBlockModalConfirm, useBreadCrumbItems, useCustomBlocker, useDetailController, useFilePreviewViewer, useModal, useMultiTabStore, useParams, useScrollToTop, useStableImageSrc, useToast, validateAndFormatInput };
1428
+ export { ArrowIcon, Badge, type BadgeProps, type BaseParams, BaseUploader, type BaseUploaderProps, BreadCrumb, type BreadCrumbItem, type BreadCrumbMenuItem, type BreadCrumbProps, type BreadCrumbRoot, Button, type ButtonProps, type ButtonSize, type ButtonVariant, type CalendarYearRange, Carousel, type CarouselProps, type CarouselRef, Checkbox, type CheckboxProps, type Column, type ConfirmOptions, DatePicker, DebouncedButton, type DebouncedButtonProps, DetailContent, type DetailContentProps, type DetailField, type DetailFieldOrSection, DetailModalFrame, type DetailMode, DetailPageFrame, Empty, ExcelUploader, FilePreviewViewer, FileUploader, type FilterRow, type FormFieldConfig, Input, type InputProps, Loading, LoadingOverlay, type LoadingOverlayProps, type LoadingProps, type MenuChild, type MenuGrandchild, type MenuItem, type MenuSection, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalIconHeader, type ModalIconHeaderProps, ModalOverlay, type ModalProps, type ModalType, MultiTabBar as MultiTab, type MultiTab as MultiTabItem, PageFilter, type PageParams, Pagination, type PaginationProps, Popover, type PopoverAlign, PopoverAnchor, PopoverClose, PopoverContent, type PopoverContentProps, PopoverDescription, PopoverFooter, PopoverHeader, type PopoverPosition, PopoverTitle, PopoverTrigger, Radio, RadioGroup, type RadioGroupProps, type RadioOptionType, type RadioProps, ScrollArea, ScrollBar, SearchAddress, type SearchAddressProps, SearchBar, type SearchBarProps, Select, Select$1 as SelectBase, SelectContent, SelectGroup, SelectItem, SelectLabel, type SelectOptionType, type SelectProps, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Sidebar, type SidebarHeaderRenderProps, type SidebarProps, SimplePopover, type SimplePopoverProps, Skeleton, type SkeletonProps, Slider, type SliderProps, type SortOption, type SortOrder, type SortParams, Spinner, type SpinnerColor, type SpinnerProps, type SpinnerSize, SubmitForm, Switch, type SwitchProps, Tab, type TabItem, type TabProps, Table, Textarea, type TextareaProps, type ToastPosition, type ToastProps, ToastProvider, type ToastType, ToggleSwitch, Tooltip, type TooltipProps, type UseParams, type YmdDateString, type ZIndexKey, Z_INDEX, buildBreadCrumbItems, buttonVariants, cn, confirmModal, findParentMenuItem, findParentMenuName, formatDate, getFileLabel, getPreviewFileType, isCalendarDayAfterMax, isDateInRange, switchTrackSizes, textVariants, toast, useBlockModalConfirm, useBreadCrumbItems, useCustomBlocker, useDetailController, useFilePreviewViewer, useModal, useMultiTabStore, useParams, useScrollToTop, useStableImageSrc, useToast, validateAndFormatInput };
package/dist/index.d.ts CHANGED
@@ -331,7 +331,7 @@ interface PaginationProps {
331
331
  onPageChange?: (page: number) => void;
332
332
  showTotalCount?: boolean;
333
333
  }
334
- declare function Pagination(props: PaginationProps): react.JSX.Element | null;
334
+ declare function Pagination(props: PaginationProps): react.JSX.Element;
335
335
 
336
336
  type BreadCrumbItem = {
337
337
  label: string;
@@ -1152,9 +1152,19 @@ interface MenuSection {
1152
1152
  items: Array<MenuItem>;
1153
1153
  }
1154
1154
  type SidebarVariant = "light" | "dark" | "system-dark" | "system-light";
1155
+ interface SidebarHeaderRenderProps {
1156
+ isCollapsed: boolean;
1157
+ toggleCollapse: () => void;
1158
+ }
1155
1159
  interface SidebarProps {
1156
1160
  menuSections: Array<MenuSection>;
1157
- header?: ReactNode;
1161
+ /**
1162
+ * ReactNode로 넘기면 기존과 동일하게 nav와 함께 collapse 시 fade-out 됨.
1163
+ * 함수로 넘기면 collapse 상태와 무관하게 항상 렌더링되며, { isCollapsed, toggleCollapse }를
1164
+ * 전달받아 헤더 내부에 자체 collapse 토글 UI를 둘 수 있음 — 이 경우 showCollapseButton은 무시되고
1165
+ * 기본 floating 버튼은 렌더링되지 않음.
1166
+ */
1167
+ header?: ReactNode | ((props: SidebarHeaderRenderProps) => ReactNode);
1158
1168
  footer?: ReactNode;
1159
1169
  className?: string;
1160
1170
  showCollapseButton?: boolean;
@@ -1415,4 +1425,4 @@ declare const Z_INDEX: {
1415
1425
  };
1416
1426
  type ZIndexKey = keyof typeof Z_INDEX;
1417
1427
 
1418
- export { ArrowIcon, Badge, type BadgeProps, type BaseParams, BaseUploader, type BaseUploaderProps, BreadCrumb, type BreadCrumbItem, type BreadCrumbMenuItem, type BreadCrumbProps, type BreadCrumbRoot, Button, type ButtonProps, type ButtonSize, type ButtonVariant, type CalendarYearRange, Carousel, type CarouselProps, type CarouselRef, Checkbox, type CheckboxProps, type Column, type ConfirmOptions, DatePicker, DebouncedButton, type DebouncedButtonProps, DetailContent, type DetailContentProps, type DetailField, type DetailFieldOrSection, DetailModalFrame, type DetailMode, DetailPageFrame, Empty, ExcelUploader, FilePreviewViewer, FileUploader, type FilterRow, type FormFieldConfig, Input, type InputProps, Loading, LoadingOverlay, type LoadingOverlayProps, type LoadingProps, type MenuChild, type MenuGrandchild, type MenuItem, type MenuSection, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalIconHeader, type ModalIconHeaderProps, ModalOverlay, type ModalProps, type ModalType, MultiTabBar as MultiTab, type MultiTab as MultiTabItem, PageFilter, type PageParams, Pagination, type PaginationProps, Popover, type PopoverAlign, PopoverAnchor, PopoverClose, PopoverContent, type PopoverContentProps, PopoverDescription, PopoverFooter, PopoverHeader, type PopoverPosition, PopoverTitle, PopoverTrigger, Radio, RadioGroup, type RadioGroupProps, type RadioOptionType, type RadioProps, ScrollArea, ScrollBar, SearchAddress, type SearchAddressProps, SearchBar, type SearchBarProps, Select, Select$1 as SelectBase, SelectContent, SelectGroup, SelectItem, SelectLabel, type SelectOptionType, type SelectProps, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Sidebar, type SidebarProps, SimplePopover, type SimplePopoverProps, Skeleton, type SkeletonProps, Slider, type SliderProps, type SortOption, type SortOrder, type SortParams, Spinner, type SpinnerColor, type SpinnerProps, type SpinnerSize, SubmitForm, Switch, type SwitchProps, Tab, type TabItem, type TabProps, Table, Textarea, type TextareaProps, type ToastPosition, type ToastProps, ToastProvider, type ToastType, ToggleSwitch, Tooltip, type TooltipProps, type UseParams, type YmdDateString, type ZIndexKey, Z_INDEX, buildBreadCrumbItems, buttonVariants, cn, confirmModal, findParentMenuItem, findParentMenuName, formatDate, getFileLabel, getPreviewFileType, isCalendarDayAfterMax, isDateInRange, switchTrackSizes, textVariants, toast, useBlockModalConfirm, useBreadCrumbItems, useCustomBlocker, useDetailController, useFilePreviewViewer, useModal, useMultiTabStore, useParams, useScrollToTop, useStableImageSrc, useToast, validateAndFormatInput };
1428
+ export { ArrowIcon, Badge, type BadgeProps, type BaseParams, BaseUploader, type BaseUploaderProps, BreadCrumb, type BreadCrumbItem, type BreadCrumbMenuItem, type BreadCrumbProps, type BreadCrumbRoot, Button, type ButtonProps, type ButtonSize, type ButtonVariant, type CalendarYearRange, Carousel, type CarouselProps, type CarouselRef, Checkbox, type CheckboxProps, type Column, type ConfirmOptions, DatePicker, DebouncedButton, type DebouncedButtonProps, DetailContent, type DetailContentProps, type DetailField, type DetailFieldOrSection, DetailModalFrame, type DetailMode, DetailPageFrame, Empty, ExcelUploader, FilePreviewViewer, FileUploader, type FilterRow, type FormFieldConfig, Input, type InputProps, Loading, LoadingOverlay, type LoadingOverlayProps, type LoadingProps, type MenuChild, type MenuGrandchild, type MenuItem, type MenuSection, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalIconHeader, type ModalIconHeaderProps, ModalOverlay, type ModalProps, type ModalType, MultiTabBar as MultiTab, type MultiTab as MultiTabItem, PageFilter, type PageParams, Pagination, type PaginationProps, Popover, type PopoverAlign, PopoverAnchor, PopoverClose, PopoverContent, type PopoverContentProps, PopoverDescription, PopoverFooter, PopoverHeader, type PopoverPosition, PopoverTitle, PopoverTrigger, Radio, RadioGroup, type RadioGroupProps, type RadioOptionType, type RadioProps, ScrollArea, ScrollBar, SearchAddress, type SearchAddressProps, SearchBar, type SearchBarProps, Select, Select$1 as SelectBase, SelectContent, SelectGroup, SelectItem, SelectLabel, type SelectOptionType, type SelectProps, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Sidebar, type SidebarHeaderRenderProps, type SidebarProps, SimplePopover, type SimplePopoverProps, Skeleton, type SkeletonProps, Slider, type SliderProps, type SortOption, type SortOrder, type SortParams, Spinner, type SpinnerColor, type SpinnerProps, type SpinnerSize, SubmitForm, Switch, type SwitchProps, Tab, type TabItem, type TabProps, Table, Textarea, type TextareaProps, type ToastPosition, type ToastProps, ToastProvider, type ToastType, ToggleSwitch, Tooltip, type TooltipProps, type UseParams, type YmdDateString, type ZIndexKey, Z_INDEX, buildBreadCrumbItems, buttonVariants, cn, confirmModal, findParentMenuItem, findParentMenuName, formatDate, getFileLabel, getPreviewFileType, isCalendarDayAfterMax, isDateInRange, switchTrackSizes, textVariants, toast, useBlockModalConfirm, useBreadCrumbItems, useCustomBlocker, useDetailController, useFilePreviewViewer, useModal, useMultiTabStore, useParams, useScrollToTop, useStableImageSrc, useToast, validateAndFormatInput };
package/dist/index.js CHANGED
@@ -1062,43 +1062,50 @@ function Radio(props) {
1062
1062
  "--radio-checked": checkColor,
1063
1063
  "--radio-unchecked": unCheckColor
1064
1064
  };
1065
- const control = /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-1.5", wrapperClassName), style: colorVars, children: [
1066
- /* @__PURE__ */ jsx(
1067
- RadioGroupPrimitive.Item,
1068
- {
1069
- value,
1070
- disabled,
1071
- id: inputId,
1072
- className: cn(
1073
- "group relative size-4 shrink-0 rounded-full border box-border transition-colors duration-200 outline-none",
1074
- "border-(--radio-unchecked) data-[state=checked]:border-(--radio-checked)",
1075
- "data-[state=unchecked]:hover:opacity-80",
1076
- disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
1077
- ),
1078
- children: /* @__PURE__ */ jsx(RadioGroupPrimitive.Indicator, { className: "relative flex items-center justify-center", children: /* @__PURE__ */ jsx(
1079
- CircleIcon,
1065
+ const control = /* @__PURE__ */ jsxs(
1066
+ "label",
1067
+ {
1068
+ htmlFor: inputId,
1069
+ className: cn("flex items-center gap-1.5", disabled ? "cursor-not-allowed" : "cursor-pointer", wrapperClassName),
1070
+ style: colorVars,
1071
+ children: [
1072
+ /* @__PURE__ */ jsx(
1073
+ RadioGroupPrimitive.Item,
1080
1074
  {
1075
+ value,
1076
+ disabled,
1077
+ id: inputId,
1081
1078
  className: cn(
1082
- "absolute top-1/2 left-1/2 size-2.5 -translate-x-1/2 -translate-y-1/2 fill-(--radio-checked) text-(--radio-checked) transition-opacity duration-200",
1083
- indicatorClassName
1084
- )
1079
+ "group relative size-4 shrink-0 rounded-full border box-border transition-colors duration-200 outline-none",
1080
+ "border-(--radio-unchecked) data-[state=checked]:border-(--radio-checked)",
1081
+ "data-[state=unchecked]:hover:opacity-80",
1082
+ disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
1083
+ ),
1084
+ children: /* @__PURE__ */ jsx(RadioGroupPrimitive.Indicator, { className: "relative flex items-center justify-center", children: /* @__PURE__ */ jsx(
1085
+ CircleIcon,
1086
+ {
1087
+ className: cn(
1088
+ "absolute top-1/2 left-1/2 size-2.5 -translate-x-1/2 -translate-y-1/2 fill-(--radio-checked) text-(--radio-checked) transition-opacity duration-200",
1089
+ indicatorClassName
1090
+ )
1091
+ }
1092
+ ) })
1085
1093
  }
1086
- ) })
1087
- }
1088
- ),
1089
- /* @__PURE__ */ jsx(
1090
- "label",
1091
- {
1092
- htmlFor: inputId,
1093
- className: cn(
1094
- "text-sm font-medium cursor-pointer text-black select-none",
1095
- disabled && "cursor-not-allowed text-gray-400 opacity-50",
1096
- labelClassName
1097
1094
  ),
1098
- children: label
1099
- }
1100
- )
1101
- ] });
1095
+ /* @__PURE__ */ jsx(
1096
+ "span",
1097
+ {
1098
+ className: cn(
1099
+ "text-sm font-medium text-black select-none",
1100
+ disabled && "text-gray-400 opacity-50",
1101
+ labelClassName
1102
+ ),
1103
+ children: label
1104
+ }
1105
+ )
1106
+ ]
1107
+ }
1108
+ );
1102
1109
  const withTooltip = (node) => {
1103
1110
  if (!disabled || !disabledMessage) return node;
1104
1111
  return /* @__PURE__ */ jsx(Tooltip2, { content: disabledMessage, position: "bottom", asChild: true, sideOffset: 4, children: /* @__PURE__ */ jsx("span", { className: "inline-flex", children: node }) });
@@ -1145,11 +1152,7 @@ function RadioGroup(props) {
1145
1152
  value,
1146
1153
  onValueChange: (newValue) => onChange(newValue),
1147
1154
  disabled,
1148
- className: cn(
1149
- "flex flex-wrap gap-3",
1150
- direction === "horizontal" ? "flex-row" : "flex-col",
1151
- containerClassName
1152
- ),
1155
+ className: cn("flex flex-wrap gap-3", direction === "horizontal" ? "flex-row" : "flex-col", containerClassName),
1153
1156
  children: options.map((option) => /* @__PURE__ */ jsx(
1154
1157
  Radio,
1155
1158
  {
@@ -1394,7 +1397,8 @@ function getVisiblePageRange(currentPage, totalPages) {
1394
1397
  return { start, end };
1395
1398
  }
1396
1399
  function Pagination(props) {
1397
- const { page = 0, totalPages = 50, onPageChange } = props;
1400
+ const { page = 0, totalPages: rawTotalPages = 0, onPageChange } = props;
1401
+ const totalPages = Math.max(1, rawTotalPages);
1398
1402
  const [displayPage, setDisplayPage] = useState(page);
1399
1403
  useEffect(() => {
1400
1404
  setDisplayPage(page);
@@ -1406,6 +1410,7 @@ function Pagination(props) {
1406
1410
  },
1407
1411
  [onPageChange]
1408
1412
  );
1413
+ const isEmpty = rawTotalPages === 0;
1409
1414
  const pageButtons = useMemo(() => {
1410
1415
  const { start, end } = getVisiblePageRange(displayPage, totalPages);
1411
1416
  const buttons = [];
@@ -1417,9 +1422,10 @@ function Pagination(props) {
1417
1422
  "button",
1418
1423
  {
1419
1424
  type: "button",
1425
+ disabled: isEmpty,
1420
1426
  className: cn(
1421
- "min-w-7 min-h-7 rounded-[4px] text-[14px] px-[5px] font-medium cursor-pointer",
1422
- isActive ? "bg-[#2675e2] text-white font-bold" : "bg-white text-[#000000] font-normal hover:bg-gray-50"
1427
+ "min-w-7 min-h-7 rounded-[4px] text-[14px] px-[5px] font-medium",
1428
+ isEmpty ? "bg-[#2675e2] text-white font-bold cursor-default" : isActive ? "bg-[#2675e2] text-white font-bold cursor-pointer" : "bg-white text-[#000000] font-normal hover:bg-gray-50 cursor-pointer"
1423
1429
  ),
1424
1430
  onClick: () => handlePageChange(pageIndex),
1425
1431
  children: displayPageNumber
@@ -1429,7 +1435,7 @@ function Pagination(props) {
1429
1435
  );
1430
1436
  }
1431
1437
  return buttons;
1432
- }, [displayPage, totalPages, handlePageChange]);
1438
+ }, [displayPage, totalPages, handlePageChange, isEmpty]);
1433
1439
  const isFirstDisabled = displayPage <= 0;
1434
1440
  const isPreviousDisabled = displayPage <= 0;
1435
1441
  const isNextDisabled = displayPage >= totalPages - 1;
@@ -1449,9 +1455,6 @@ function Pagination(props) {
1449
1455
  children
1450
1456
  }
1451
1457
  );
1452
- if (!totalPages || totalPages === 0) {
1453
- return null;
1454
- }
1455
1458
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-row items-center gap-1.5", children: [
1456
1459
  renderNavButton(
1457
1460
  () => handlePageChange(0),
@@ -2791,7 +2794,7 @@ function DatePickerSingle(props) {
2791
2794
  "div",
2792
2795
  {
2793
2796
  role: "button",
2794
- tabIndex: disabled ? -1 : 0,
2797
+ tabIndex: -1,
2795
2798
  "aria-hidden": disabled,
2796
2799
  onClick: () => {
2797
2800
  if (!disabled) toggleCalendar(inputValue);
@@ -5225,7 +5228,7 @@ function Table(props) {
5225
5228
  ] })
5226
5229
  }
5227
5230
  ),
5228
- !showEmpty && !showSkeleton && (paginationInfo || renderFooter?.renderLeft || renderFooter?.renderRight) && /* @__PURE__ */ jsx("div", { ref: footerRef, children: /* @__PURE__ */ jsx(
5231
+ !showSkeleton && (paginationInfo || renderFooter?.renderLeft || renderFooter?.renderRight) && /* @__PURE__ */ jsx("div", { ref: footerRef, children: /* @__PURE__ */ jsx(
5229
5232
  TableFooter,
5230
5233
  {
5231
5234
  paginationInfo,
@@ -9208,6 +9211,9 @@ function Sidebar(props) {
9208
9211
  const [overflowVisible, setOverflowVisible] = useState(true);
9209
9212
  const [expandedItems, setExpandedItems] = useState(/* @__PURE__ */ new Set());
9210
9213
  const [expandedSubItems, setExpandedSubItems] = useState(/* @__PURE__ */ new Set());
9214
+ const toggleCollapse = () => setIsCollapsed((prev) => !prev);
9215
+ const isHeaderRenderProp = typeof header === "function";
9216
+ const headerContent = isHeaderRenderProp ? header({ isCollapsed, toggleCollapse }) : header;
9211
9217
  useEffect(() => {
9212
9218
  if (isCollapsed) {
9213
9219
  setOverflowVisible(false);
@@ -9282,21 +9288,22 @@ function Sidebar(props) {
9282
9288
  ${className ?? ""}
9283
9289
  `,
9284
9290
  children: [
9285
- showCollapseButton && /* @__PURE__ */ jsx(
9291
+ showCollapseButton && !isHeaderRenderProp && /* @__PURE__ */ jsx(
9286
9292
  "button",
9287
9293
  {
9288
- onClick: () => setIsCollapsed((prev) => !prev),
9294
+ onClick: toggleCollapse,
9289
9295
  className: `absolute top-4 z-20 w-6.5 h-6.5 rounded-full flex items-center justify-center transition-colors cursor-pointer ${s.collapseBtn} ${collapseButtonPlacement === "left" ? "left-2" : "right-2"}`,
9290
9296
  children: isCollapsed ? /* @__PURE__ */ jsx(PanelRightClose, { size: 18, color: primaryColor ?? s.collapseIcon }) : /* @__PURE__ */ jsx(PanelLeftClose, { size: 18, color: primaryColor ?? s.collapseIcon })
9291
9297
  }
9292
9298
  ),
9299
+ isHeaderRenderProp && headerContent,
9293
9300
  /* @__PURE__ */ jsx("div", { className: `flex-1 min-h-0 hide-scrollbar ${overflowVisible ? "overflow-y-auto" : "overflow-hidden"}`, children: /* @__PURE__ */ jsxs(
9294
9301
  "div",
9295
9302
  {
9296
9303
  className: `transition-opacity duration-200 ease-in-out ${isCollapsed ? "opacity-0 pointer-events-none select-none" : "opacity-100"} ${variant === "system-dark" || variant === "system-light" ? "pb-6" : "px-4 pb-6"}`,
9297
9304
  style: { transitionDelay: isCollapsed ? "0ms" : "150ms" },
9298
9305
  children: [
9299
- header && header,
9306
+ !isHeaderRenderProp && headerContent,
9300
9307
  /* @__PURE__ */ jsx("nav", { className: `space-y-6 ${variant === "system-dark" || variant === "system-light" ? "px-1" : ""}`, children: menuSections.map((section, sectionIndex) => /* @__PURE__ */ jsxs("div", { children: [
9301
9308
  section.title && /* @__PURE__ */ jsx("div", { className: s.sectionTitle, children: section.title }),
9302
9309
  /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: section.items.map((item) => {
@@ -9776,7 +9783,16 @@ function DetailModalFrame(props) {
9776
9783
  ] }),
9777
9784
  /* @__PURE__ */ jsx(ModalFooter, { className: "flex justify-end", children: isEditMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
9778
9785
  /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleCancelEdit, disabled: controller.isSaving, children: "\uCDE8\uC18C" }),
9779
- /* @__PURE__ */ jsx(Button, { type: "submit", form: controller.formId, variant: "save", disabled: controller.isSaving, children: "\uC800\uC7A5" })
9786
+ /* @__PURE__ */ jsx(
9787
+ Button,
9788
+ {
9789
+ type: "submit",
9790
+ form: controller.formId,
9791
+ variant: "save",
9792
+ disabled: controller.isSaving || !controller.isDirty,
9793
+ children: "\uC800\uC7A5"
9794
+ }
9795
+ )
9780
9796
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
9781
9797
  canEdit && /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleEdit, children: "\uC218\uC815" }),
9782
9798
  renderFooterExtra,