@juv/codego-react-ui 3.2.8 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6409,15 +6409,16 @@ function validateField(field, value) {
6409
6409
  }
6410
6410
  function FieldRenderer({ field, value, onChange }) {
6411
6411
  if (field.render) return /* @__PURE__ */ jsx32(Fragment11, { children: field.render(value, onChange) });
6412
+ const toLabelValue = (o) => {
6413
+ if (typeof o === "string") return { label: o, value: o };
6414
+ if (Array.isArray(o)) return { label: o[0], value: o[1] };
6415
+ return o;
6416
+ };
6412
6417
  const strOptions = (field.options ?? []).map(
6413
- (o) => typeof o === "string" ? o : o.value
6414
- );
6415
- const comboOptions = (field.options ?? []).map(
6416
- (o) => typeof o === "string" ? { label: o, value: o } : o
6417
- );
6418
- const radioOptions = (field.options ?? []).map(
6419
- (o) => typeof o === "string" ? { label: o, value: o } : o
6418
+ (o) => typeof o === "string" ? o : Array.isArray(o) ? o[1] : o.value
6420
6419
  );
6420
+ const comboOptions = (field.options ?? []).map(toLabelValue);
6421
+ const radioOptions = (field.options ?? []).map(toLabelValue);
6421
6422
  switch (field.type) {
6422
6423
  case "textarea":
6423
6424
  return /* @__PURE__ */ jsx32(Textarea, { value: value ?? "", onChange: (e) => onChange(e.target.value), placeholder: field.placeholder, rows: 3 });
@@ -6679,8 +6680,7 @@ function DeleteModal({
6679
6680
  itemId,
6680
6681
  onClose,
6681
6682
  onSuccess,
6682
- notif,
6683
- width
6683
+ notif
6684
6684
  }) {
6685
6685
  const [loading, setLoading] = React28.useState(false);
6686
6686
  const [error, setError] = React28.useState(null);
@@ -6702,7 +6702,7 @@ function DeleteModal({
6702
6702
  {
6703
6703
  title: "Confirm Delete",
6704
6704
  onClose,
6705
- width,
6705
+ width: "lg",
6706
6706
  footer: /* @__PURE__ */ jsxs30(Fragment11, { children: [
6707
6707
  /* @__PURE__ */ jsx32(Button, { variant: "outline", size: "sm", onClick: onClose, disabled: loading, children: "Cancel" }),
6708
6708
  /* @__PURE__ */ jsxs30(Button, { variant: "danger", size: "sm", onClick: handleDelete, disabled: loading, children: [
@@ -6722,17 +6722,19 @@ function ActionBtn({
6722
6722
  defaultIcon,
6723
6723
  defaultLabel,
6724
6724
  defaultVariant,
6725
+ defaultSize,
6725
6726
  onClick
6726
6727
  }) {
6727
6728
  const mode = cfg?.displayMode ?? "icon";
6728
6729
  const icon = cfg?.icon ?? defaultIcon;
6729
6730
  const label = cfg?.label ?? defaultLabel;
6731
+ const buttonSize = cfg?.size ?? defaultSize ?? "xs";
6730
6732
  return /* @__PURE__ */ jsx32(
6731
6733
  Button,
6732
6734
  {
6733
6735
  type: "button",
6734
6736
  title: label,
6735
- size: cfg?.size ?? "xs",
6737
+ size: buttonSize,
6736
6738
  variant: cfg?.variant ?? defaultVariant,
6737
6739
  rounded: cfg?.rounded ?? "lg",
6738
6740
  gradientFrom: cfg?.gradientFrom,
@@ -6841,6 +6843,7 @@ function Table({
6841
6843
  defaultIcon: extra.icon,
6842
6844
  defaultLabel: extra.label ?? extra.key,
6843
6845
  defaultVariant: extra.variant ?? "outline",
6846
+ defaultSize: defaultActions.actionsSize,
6844
6847
  onClick: () => extra.onClick(item)
6845
6848
  },
6846
6849
  extra.key
@@ -7219,7 +7222,6 @@ function Table({
7219
7222
  baseUrl: defaultActions.baseUrl,
7220
7223
  itemId: String(deleteItem[actionIdKey] ?? ""),
7221
7224
  notif: defaultActions.onSuccessNotif,
7222
- width: defaultActions.modalWidth,
7223
7225
  onClose: () => setDeleteItem(null),
7224
7226
  onSuccess: (deleted) => {
7225
7227
  setTableData(
@@ -7315,17 +7317,19 @@ function ActionBtn2({
7315
7317
  defaultIcon,
7316
7318
  defaultLabel,
7317
7319
  defaultVariant,
7320
+ defaultSize,
7318
7321
  onClick
7319
7322
  }) {
7320
7323
  const mode = cfg?.displayMode ?? "icon";
7321
7324
  const icon = cfg?.icon ?? defaultIcon;
7322
7325
  const label = cfg?.label ?? defaultLabel;
7326
+ const buttonSize = cfg?.size ?? defaultSize ?? "xs";
7323
7327
  return /* @__PURE__ */ jsx33(
7324
7328
  Button,
7325
7329
  {
7326
7330
  type: "button",
7327
7331
  title: label,
7328
- size: cfg?.size ?? "xs",
7332
+ size: buttonSize,
7329
7333
  variant: cfg?.variant ?? defaultVariant,
7330
7334
  rounded: cfg?.rounded ?? "lg",
7331
7335
  gradientFrom: cfg?.gradientFrom,
@@ -7369,15 +7373,16 @@ function DGModalShell({ title, onClose, children, footer, width = "lg" }) {
7369
7373
  }
7370
7374
  function DGFieldRenderer({ field, value, onChange }) {
7371
7375
  if (field.render) return /* @__PURE__ */ jsx33(Fragment12, { children: field.render(value, onChange) });
7376
+ const toLabelValue = (o) => {
7377
+ if (typeof o === "string") return { label: o, value: o };
7378
+ if (Array.isArray(o)) return { label: o[0], value: o[1] };
7379
+ return o;
7380
+ };
7372
7381
  const strOptions = (field.options ?? []).map(
7373
- (o) => typeof o === "string" ? o : o.value
7374
- );
7375
- const comboOptions = (field.options ?? []).map(
7376
- (o) => typeof o === "string" ? { label: o, value: o } : o
7377
- );
7378
- const radioOptions = (field.options ?? []).map(
7379
- (o) => typeof o === "string" ? { label: o, value: o } : o
7382
+ (o) => typeof o === "string" ? o : Array.isArray(o) ? o[1] : o.value
7380
7383
  );
7384
+ const comboOptions = (field.options ?? []).map(toLabelValue);
7385
+ const radioOptions = (field.options ?? []).map(toLabelValue);
7381
7386
  switch (field.type) {
7382
7387
  case "textarea":
7383
7388
  return /* @__PURE__ */ jsx33(Textarea, { value: value ?? "", onChange: (e) => onChange(e.target.value), placeholder: field.placeholder, rows: 3 });
@@ -7548,8 +7553,7 @@ function DGDeleteModal({
7548
7553
  itemId,
7549
7554
  onClose,
7550
7555
  onSuccess,
7551
- notif,
7552
- width
7556
+ notif
7553
7557
  }) {
7554
7558
  const [loading, setLoading] = React29.useState(false);
7555
7559
  const [error, setError] = React29.useState(null);
@@ -7571,7 +7575,7 @@ function DGDeleteModal({
7571
7575
  {
7572
7576
  title: "Confirm Delete",
7573
7577
  onClose,
7574
- width,
7578
+ width: "lg",
7575
7579
  footer: /* @__PURE__ */ jsxs31(Fragment12, { children: [
7576
7580
  /* @__PURE__ */ jsx33("button", { onClick: onClose, disabled: loading, className: "px-4 py-1.5 text-sm rounded-xl border border-border hover:bg-accent transition-colors", children: "Cancel" }),
7577
7581
  /* @__PURE__ */ jsxs31("button", { onClick: handleDelete, disabled: loading, className: "px-4 py-1.5 text-sm rounded-xl bg-danger text-danger-foreground hover:bg-danger-hover transition-colors flex items-center gap-1.5", children: [
@@ -7692,6 +7696,7 @@ function DataGrid({
7692
7696
  defaultIcon: /* @__PURE__ */ jsx33(Eye3, { className: "h-3.5 w-3.5" }),
7693
7697
  defaultLabel: "View",
7694
7698
  defaultVariant: "outline",
7699
+ defaultSize: defaultActions.actionsSize,
7695
7700
  onClick: () => setViewItem(row)
7696
7701
  }
7697
7702
  ),
@@ -7702,6 +7707,7 @@ function DataGrid({
7702
7707
  defaultIcon: /* @__PURE__ */ jsx33(Pencil3, { className: "h-3.5 w-3.5" }),
7703
7708
  defaultLabel: "Edit",
7704
7709
  defaultVariant: "outline",
7710
+ defaultSize: defaultActions.actionsSize,
7705
7711
  onClick: () => setEditItem(row)
7706
7712
  }
7707
7713
  ),
@@ -7712,6 +7718,7 @@ function DataGrid({
7712
7718
  defaultIcon: /* @__PURE__ */ jsx33(Trash4, { className: "h-3.5 w-3.5" }),
7713
7719
  defaultLabel: "Delete",
7714
7720
  defaultVariant: "danger",
7721
+ defaultSize: defaultActions.actionsSize,
7715
7722
  onClick: () => setDeleteItem(row)
7716
7723
  }
7717
7724
  ),
@@ -7722,6 +7729,7 @@ function DataGrid({
7722
7729
  defaultIcon: extra.icon,
7723
7730
  defaultLabel: extra.label ?? extra.key,
7724
7731
  defaultVariant: extra.variant ?? "outline",
7732
+ defaultSize: defaultActions.actionsSize,
7725
7733
  onClick: () => extra.onClick(row)
7726
7734
  },
7727
7735
  extra.key
@@ -7955,7 +7963,6 @@ function DataGrid({
7955
7963
  baseUrl: defaultActions.baseUrl,
7956
7964
  itemId: String(deleteItem[actionIdKey] ?? ""),
7957
7965
  notif: defaultActions.onSuccessNotif,
7958
- width: defaultActions.modalWidth,
7959
7966
  onClose: () => setDeleteItem(null),
7960
7967
  onSuccess: (deleted) => {
7961
7968
  setTableData((prev) => prev.filter((r) => String(r[actionIdKey]) !== String(deleted[actionIdKey])));
@@ -10144,7 +10151,7 @@ function GroupNavigation({
10144
10151
 
10145
10152
  // src/components/ui/panel.tsx
10146
10153
  import * as React40 from "react";
10147
- import { PanelLeftClose, PanelLeftOpen, Sun as Sun3, Moon as Moon2 } from "lucide-react";
10154
+ import { PanelLeftClose, PanelLeftOpen, Sun as Sun3, Moon as Moon2, Loader2 as Loader24 } from "lucide-react";
10148
10155
 
10149
10156
  // src/components/ui/tooltip.tsx
10150
10157
  import * as React38 from "react";
@@ -10759,6 +10766,8 @@ var useTheme = () => {
10759
10766
  // src/components/ui/panel.tsx
10760
10767
  import { Fragment as Fragment17, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
10761
10768
  var PanelCollapsedContext = React40.createContext(false);
10769
+ var PanelGroupsContext = React40.createContext({ expandedGroups: /* @__PURE__ */ new Set(), onGroupToggle: () => {
10770
+ } });
10762
10771
  function PanelThemeToggle() {
10763
10772
  const { theme, setTheme } = useTheme();
10764
10773
  return /* @__PURE__ */ jsxs41(
@@ -10785,15 +10794,109 @@ function Panel({
10785
10794
  topbar,
10786
10795
  topbarTrailing,
10787
10796
  defaultCollapsed = false,
10797
+ collapsed: controlledCollapsed,
10798
+ onCollapsedChange,
10788
10799
  collapsible = false,
10789
10800
  showThemeToggle = false,
10790
10801
  defaultPage,
10802
+ currentPage: controlledPage,
10803
+ onPageChange,
10791
10804
  height = "h-[520px]",
10792
10805
  children,
10793
- className
10806
+ className,
10807
+ loading = false,
10808
+ emptyState,
10809
+ error = null,
10810
+ showGroupDividers = false,
10811
+ expandedGroups: controlledExpandedGroups,
10812
+ onGroupToggle,
10813
+ theme: themeProp,
10814
+ collapseIcon,
10815
+ expandIcon,
10816
+ meta,
10817
+ actions,
10818
+ keyboardNavigation = false,
10819
+ draggable = false,
10820
+ onSidebarReorder,
10821
+ animationDuration = 200,
10822
+ animationEasing = "ease-in-out",
10823
+ sidebarTooltip,
10824
+ mobileBreakpoint = 768,
10825
+ mobileCollapsed: controlledMobileCollapsed,
10826
+ onMobileCollapseChange
10794
10827
  }) {
10795
- const [collapsed, setCollapsed] = React40.useState(defaultCollapsed);
10796
- return /* @__PURE__ */ jsx46(PanelCollapsedContext.Provider, { value: collapsed, children: /* @__PURE__ */ jsxs41(
10828
+ const [internalCollapsed, setInternalCollapsed] = React40.useState(defaultCollapsed);
10829
+ const [internalPage, setInternalPage] = React40.useState(defaultPage || "");
10830
+ const [internalExpandedGroups, setInternalExpandedGroups] = React40.useState(
10831
+ new Set(controlledExpandedGroups)
10832
+ );
10833
+ const [isMobile, setIsMobile] = React40.useState(false);
10834
+ const [internalMobileCollapsed, setInternalMobileCollapsed] = React40.useState(true);
10835
+ const isCollapsed = controlledCollapsed !== void 0 ? controlledCollapsed : internalCollapsed;
10836
+ const currentPage = controlledPage !== void 0 ? controlledPage : internalPage;
10837
+ const expandedGroups = controlledExpandedGroups !== void 0 ? new Set(controlledExpandedGroups) : internalExpandedGroups;
10838
+ const mobileCollapsed = controlledMobileCollapsed !== void 0 ? controlledMobileCollapsed : internalMobileCollapsed;
10839
+ const handleCollapsedChange = (value) => {
10840
+ if (controlledCollapsed === void 0) setInternalCollapsed(value);
10841
+ onCollapsedChange?.(value);
10842
+ };
10843
+ const handlePageChange = (page) => {
10844
+ if (controlledPage === void 0) setInternalPage(page);
10845
+ onPageChange?.(page);
10846
+ };
10847
+ const handleGroupToggle = (title, expanded) => {
10848
+ if (controlledExpandedGroups === void 0) {
10849
+ setInternalExpandedGroups((prev) => {
10850
+ const next = new Set(prev);
10851
+ if (expanded) next.add(title);
10852
+ else next.delete(title);
10853
+ return next;
10854
+ });
10855
+ }
10856
+ onGroupToggle?.(title, expanded);
10857
+ };
10858
+ const handleMobileCollapseChange = (value) => {
10859
+ if (controlledMobileCollapsed === void 0) setInternalMobileCollapsed(value);
10860
+ onMobileCollapseChange?.(value);
10861
+ };
10862
+ React40.useEffect(() => {
10863
+ const handleResize = () => {
10864
+ const mobile = window.innerWidth < mobileBreakpoint;
10865
+ setIsMobile(mobile);
10866
+ if (mobile && !internalMobileCollapsed) {
10867
+ handleMobileCollapseChange(true);
10868
+ }
10869
+ };
10870
+ handleResize();
10871
+ window.addEventListener("resize", handleResize);
10872
+ return () => window.removeEventListener("resize", handleResize);
10873
+ }, [mobileBreakpoint, internalMobileCollapsed]);
10874
+ const handleKeyDown = React40.useCallback(
10875
+ (e) => {
10876
+ if (!keyboardNavigation) return;
10877
+ if (e.key === "Escape" && !isCollapsed && collapsible) {
10878
+ handleCollapsedChange(true);
10879
+ }
10880
+ if (e.key === "Enter" && isCollapsed && collapsible) {
10881
+ handleCollapsedChange(false);
10882
+ }
10883
+ },
10884
+ [keyboardNavigation, isCollapsed, collapsible]
10885
+ );
10886
+ React40.useEffect(() => {
10887
+ if (keyboardNavigation) {
10888
+ window.addEventListener("keydown", handleKeyDown);
10889
+ return () => window.removeEventListener("keydown", handleKeyDown);
10890
+ }
10891
+ }, [keyboardNavigation, handleKeyDown]);
10892
+ const effectiveCollapsed = isMobile ? mobileCollapsed : isCollapsed;
10893
+ const animStyle = {
10894
+ transitionDuration: `${animationDuration}ms`,
10895
+ transitionTimingFunction: animationEasing
10896
+ };
10897
+ const hasContent = React40.Children.count(children) > 0;
10898
+ const showEmpty = !loading && !hasContent && emptyState;
10899
+ return /* @__PURE__ */ jsx46(PanelCollapsedContext.Provider, { value: effectiveCollapsed, children: /* @__PURE__ */ jsx46(PanelGroupsContext.Provider, { value: { expandedGroups, onGroupToggle: handleGroupToggle }, children: /* @__PURE__ */ jsxs41(
10797
10900
  "div",
10798
10901
  {
10799
10902
  className: cn(
@@ -10801,6 +10904,7 @@ function Panel({
10801
10904
  height,
10802
10905
  className
10803
10906
  ),
10907
+ style: { ...animStyle },
10804
10908
  children: [
10805
10909
  /* @__PURE__ */ jsxs41("div", { className: "pointer-events-none absolute inset-0 overflow-hidden", children: [
10806
10910
  /* @__PURE__ */ jsx46("div", { className: "absolute -top-[40%] -left-[20%] h-[80%] w-[60%] rounded-full bg-primary/10 blur-[120px]" }),
@@ -10810,23 +10914,36 @@ function Panel({
10810
10914
  "aside",
10811
10915
  {
10812
10916
  className: cn(
10813
- "relative z-10 flex flex-col shrink-0 border-r border-border transition-all duration-200",
10814
- collapsed ? "w-14" : sidebarWidth
10917
+ "relative z-10 flex flex-col shrink-0 border-r border-border transition-all",
10918
+ effectiveCollapsed ? "w-14" : sidebarWidth
10815
10919
  ),
10920
+ style: { transitionDuration: `${animationDuration}ms`, transitionTimingFunction: animationEasing },
10816
10921
  children: [
10817
- (sidebarBrand || sidebarHeader) && /* @__PURE__ */ jsx46("div", { className: cn(
10818
- "shrink-0 border-b border-border",
10819
- collapsed ? "flex items-center justify-center py-3" : "flex items-center gap-2 px-4 py-3"
10820
- ), children: sidebarBrand ? collapsed ? sidebarBrand.image ? /* @__PURE__ */ jsx46("img", { src: sidebarBrand.image, alt: "logo", className: "h-7 w-7 rounded-md object-cover shrink-0" }) : /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.icon }) : /* @__PURE__ */ jsxs41(Fragment17, { children: [
10821
- sidebarBrand.image ? /* @__PURE__ */ jsx46("img", { src: sidebarBrand.image, alt: "logo", className: "h-7 w-7 rounded-md object-cover shrink-0" }) : sidebarBrand.icon && /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.icon }),
10822
- sidebarBrand.title && /* @__PURE__ */ jsx46("span", { className: "flex-1 truncate text-sm font-semibold", children: sidebarBrand.title }),
10823
- sidebarBrand.trailing && /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.trailing })
10824
- ] }) : !collapsed && /* @__PURE__ */ jsx46("div", { className: "text-sm font-semibold", children: sidebarHeader }) }),
10922
+ (sidebarBrand || sidebarHeader) && /* @__PURE__ */ jsx46(
10923
+ "div",
10924
+ {
10925
+ className: cn(
10926
+ "shrink-0 border-b border-border",
10927
+ effectiveCollapsed ? "flex items-center justify-center py-3" : "flex items-center gap-2 px-4 py-3"
10928
+ ),
10929
+ children: sidebarBrand ? effectiveCollapsed ? sidebarBrand.image ? /* @__PURE__ */ jsx46("img", { src: sidebarBrand.image, alt: "logo", className: "h-7 w-7 rounded-md object-cover shrink-0" }) : /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.icon }) : /* @__PURE__ */ jsxs41(Fragment17, { children: [
10930
+ sidebarBrand.image ? /* @__PURE__ */ jsx46("img", { src: sidebarBrand.image, alt: "logo", className: "h-7 w-7 rounded-md object-cover shrink-0" }) : sidebarBrand.icon && /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.icon }),
10931
+ sidebarBrand.title && /* @__PURE__ */ jsx46("span", { className: "flex-1 truncate text-sm font-semibold", children: sidebarBrand.title }),
10932
+ sidebarBrand.trailing && /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.trailing })
10933
+ ] }) : !effectiveCollapsed && /* @__PURE__ */ jsx46("div", { className: "text-sm font-semibold", children: sidebarHeader })
10934
+ }
10935
+ ),
10825
10936
  /* @__PURE__ */ jsx46("div", { className: "flex-1 overflow-y-auto py-2", children: sidebar }),
10826
- (sidebarProfile || sidebarFooter) && /* @__PURE__ */ jsx46("div", { className: cn(
10827
- "shrink-0 border-t border-border",
10828
- collapsed ? "flex items-center justify-center py-3" : "px-4 py-3"
10829
- ), children: sidebarProfile ? collapsed ? sidebarProfile.image ? /* @__PURE__ */ jsx46("img", { src: sidebarProfile.image, alt: "profile", className: "h-7 w-7 rounded-full object-cover shrink-0" }) : /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarProfile.icon }) : sidebarProfile.content ?? /* @__PURE__ */ jsx46("div", { className: "flex items-center gap-2", children: sidebarProfile.image ? /* @__PURE__ */ jsx46("img", { src: sidebarProfile.image, alt: "profile", className: "h-7 w-7 rounded-full object-cover shrink-0" }) : sidebarProfile.icon && /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarProfile.icon }) }) : !collapsed && sidebarFooter })
10937
+ (sidebarProfile || sidebarFooter) && /* @__PURE__ */ jsx46(
10938
+ "div",
10939
+ {
10940
+ className: cn(
10941
+ "shrink-0 border-t border-border",
10942
+ effectiveCollapsed ? "flex items-center justify-center py-3" : "px-4 py-3"
10943
+ ),
10944
+ children: sidebarProfile ? effectiveCollapsed ? sidebarProfile.image ? /* @__PURE__ */ jsx46("img", { src: sidebarProfile.image, alt: "profile", className: "h-7 w-7 rounded-full object-cover shrink-0" }) : /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarProfile.icon }) : sidebarProfile.content ?? /* @__PURE__ */ jsx46("div", { className: "flex items-center gap-2", children: sidebarProfile.image ? /* @__PURE__ */ jsx46("img", { src: sidebarProfile.image, alt: "profile", className: "h-7 w-7 rounded-full object-cover shrink-0" }) : sidebarProfile.icon && /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarProfile.icon }) }) : !effectiveCollapsed && sidebarFooter
10945
+ }
10946
+ )
10830
10947
  ]
10831
10948
  }
10832
10949
  ),
@@ -10836,16 +10953,22 @@ function Panel({
10836
10953
  collapsible && sidebar && /* @__PURE__ */ jsx46(
10837
10954
  Tooltip,
10838
10955
  {
10839
- content: collapsed ? "Expand sidebar" : "Collapse sidebar",
10956
+ content: effectiveCollapsed ? "Expand sidebar" : "Collapse sidebar",
10840
10957
  side: "bottom",
10841
10958
  children: /* @__PURE__ */ jsx46(
10842
10959
  "button",
10843
10960
  {
10844
10961
  type: "button",
10845
- onClick: () => setCollapsed((c) => !c),
10962
+ onClick: () => {
10963
+ if (isMobile) {
10964
+ handleMobileCollapseChange(!mobileCollapsed);
10965
+ } else {
10966
+ handleCollapsedChange(!isCollapsed);
10967
+ }
10968
+ },
10846
10969
  className: "text-muted-foreground hover:text-foreground transition-colors",
10847
- "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
10848
- children: collapsed ? /* @__PURE__ */ jsx46(PanelLeftOpen, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx46(PanelLeftClose, { className: "h-5 w-5" })
10970
+ "aria-label": effectiveCollapsed ? "Expand sidebar" : "Collapse sidebar",
10971
+ children: effectiveCollapsed ? expandIcon || /* @__PURE__ */ jsx46(PanelLeftOpen, { className: "h-5 w-5" }) : collapseIcon || /* @__PURE__ */ jsx46(PanelLeftClose, { className: "h-5 w-5" })
10849
10972
  }
10850
10973
  )
10851
10974
  }
@@ -10857,11 +10980,16 @@ function Panel({
10857
10980
  showThemeToggle && /* @__PURE__ */ jsx46(PanelThemeToggle, {})
10858
10981
  ] })
10859
10982
  ] }),
10860
- /* @__PURE__ */ jsx46("main", { className: "flex-1 overflow-y-auto p-4", children })
10983
+ /* @__PURE__ */ jsxs41("main", { className: "flex-1 overflow-y-auto p-4", children: [
10984
+ error && /* @__PURE__ */ jsx46("div", { className: "mb-4 p-3 rounded-md bg-destructive/10 text-destructive text-sm", children: error }),
10985
+ loading && /* @__PURE__ */ jsx46("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsx46(Loader24, { className: "h-6 w-6 animate-spin text-muted-foreground" }) }),
10986
+ showEmpty && /* @__PURE__ */ jsx46("div", { className: "flex items-center justify-center h-full text-muted-foreground", children: emptyState }),
10987
+ !loading && !showEmpty && children
10988
+ ] })
10861
10989
  ] })
10862
10990
  ]
10863
10991
  }
10864
- ) });
10992
+ ) }) });
10865
10993
  }
10866
10994
  function PanelSidebarItem({
10867
10995
  icon: Icon,
@@ -10892,10 +11020,20 @@ function PanelSidebarGroup({
10892
11020
  children
10893
11021
  }) {
10894
11022
  const collapsed = React40.useContext(PanelCollapsedContext);
11023
+ const { expandedGroups, onGroupToggle } = React40.useContext(PanelGroupsContext);
11024
+ const isExpanded = title ? expandedGroups.has(title) : true;
10895
11025
  return /* @__PURE__ */ jsxs41("div", { className: "px-2 py-1", children: [
10896
- title && !collapsed && /* @__PURE__ */ jsx46("p", { className: "mb-1 px-2 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground", children: title }),
11026
+ title && !collapsed && /* @__PURE__ */ jsx46(
11027
+ "button",
11028
+ {
11029
+ type: "button",
11030
+ onClick: () => onGroupToggle(title, !isExpanded),
11031
+ className: "mb-1 px-2 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground hover:text-foreground transition-colors w-full text-left",
11032
+ children: title
11033
+ }
11034
+ ),
10897
11035
  title && collapsed && /* @__PURE__ */ jsx46("div", { className: "mx-1 mb-1 h-px bg-border" }),
10898
- /* @__PURE__ */ jsx46("main", { className: "space-y-0.5", children })
11036
+ (!title || isExpanded) && /* @__PURE__ */ jsx46("main", { className: "space-y-0.5", children })
10899
11037
  ] });
10900
11038
  }
10901
11039
 
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "registry": "https://registry.npmjs.org/",
5
5
  "access": "public"
6
6
  },
7
- "version": "3.2.8",
7
+ "version": "3.3.1",
8
8
  "description": "Reusable React UI components",
9
9
  "license": "MIT",
10
10
  "main": "dist/index.js",