@juv/codego-react-ui 3.3.0 → 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.
@@ -271,7 +271,7 @@ var CodegoUI = (() => {
271
271
  }
272
272
  console.error(error);
273
273
  };
274
- var Children = {
274
+ var Children2 = {
275
275
  map: mapChildren,
276
276
  forEach: function(children, forEachFunc, forEachContext) {
277
277
  mapChildren(
@@ -303,7 +303,7 @@ var CodegoUI = (() => {
303
303
  }
304
304
  };
305
305
  exports.Activity = REACT_ACTIVITY_TYPE;
306
- exports.Children = Children;
306
+ exports.Children = Children2;
307
307
  exports.Component = Component;
308
308
  exports.Fragment = REACT_FRAGMENT_TYPE;
309
309
  exports.Profiler = REACT_PROFILER_TYPE;
@@ -69673,15 +69673,16 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
69673
69673
  }
69674
69674
  function FieldRenderer({ field, value, onChange }) {
69675
69675
  if (field.render) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children: field.render(value, onChange) });
69676
+ const toLabelValue = (o) => {
69677
+ if (typeof o === "string") return { label: o, value: o };
69678
+ if (Array.isArray(o)) return { label: o[0], value: o[1] };
69679
+ return o;
69680
+ };
69676
69681
  const strOptions = (field.options ?? []).map(
69677
- (o) => typeof o === "string" ? o : o.value
69678
- );
69679
- const comboOptions = (field.options ?? []).map(
69680
- (o) => typeof o === "string" ? { label: o, value: o } : o
69681
- );
69682
- const radioOptions = (field.options ?? []).map(
69683
- (o) => typeof o === "string" ? { label: o, value: o } : o
69682
+ (o) => typeof o === "string" ? o : Array.isArray(o) ? o[1] : o.value
69684
69683
  );
69684
+ const comboOptions = (field.options ?? []).map(toLabelValue);
69685
+ const radioOptions = (field.options ?? []).map(toLabelValue);
69685
69686
  switch (field.type) {
69686
69687
  case "textarea":
69687
69688
  return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Textarea, { value: value ?? "", onChange: (e) => onChange(e.target.value), placeholder: field.placeholder, rows: 3 });
@@ -70636,15 +70637,16 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
70636
70637
  }
70637
70638
  function DGFieldRenderer({ field, value, onChange }) {
70638
70639
  if (field.render) return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_jsx_runtime33.Fragment, { children: field.render(value, onChange) });
70640
+ const toLabelValue = (o) => {
70641
+ if (typeof o === "string") return { label: o, value: o };
70642
+ if (Array.isArray(o)) return { label: o[0], value: o[1] };
70643
+ return o;
70644
+ };
70639
70645
  const strOptions = (field.options ?? []).map(
70640
- (o) => typeof o === "string" ? o : o.value
70641
- );
70642
- const comboOptions = (field.options ?? []).map(
70643
- (o) => typeof o === "string" ? { label: o, value: o } : o
70644
- );
70645
- const radioOptions = (field.options ?? []).map(
70646
- (o) => typeof o === "string" ? { label: o, value: o } : o
70646
+ (o) => typeof o === "string" ? o : Array.isArray(o) ? o[1] : o.value
70647
70647
  );
70648
+ const comboOptions = (field.options ?? []).map(toLabelValue);
70649
+ const radioOptions = (field.options ?? []).map(toLabelValue);
70648
70650
  switch (field.type) {
70649
70651
  case "textarea":
70650
70652
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Textarea, { value: value ?? "", onChange: (e) => onChange(e.target.value), placeholder: field.placeholder, rows: 3 });
@@ -74446,6 +74448,8 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74446
74448
  // src/components/ui/panel.tsx
74447
74449
  var import_jsx_runtime46 = __toESM(require_jsx_runtime(), 1);
74448
74450
  var PanelCollapsedContext = React42.createContext(false);
74451
+ var PanelGroupsContext = React42.createContext({ expandedGroups: /* @__PURE__ */ new Set(), onGroupToggle: () => {
74452
+ } });
74449
74453
  function PanelThemeToggle() {
74450
74454
  const { theme, setTheme } = useTheme();
74451
74455
  return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
@@ -74472,15 +74476,109 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74472
74476
  topbar,
74473
74477
  topbarTrailing,
74474
74478
  defaultCollapsed = false,
74479
+ collapsed: controlledCollapsed,
74480
+ onCollapsedChange,
74475
74481
  collapsible = false,
74476
74482
  showThemeToggle = false,
74477
74483
  defaultPage,
74484
+ currentPage: controlledPage,
74485
+ onPageChange,
74478
74486
  height = "h-[520px]",
74479
74487
  children,
74480
- className
74488
+ className,
74489
+ loading = false,
74490
+ emptyState,
74491
+ error = null,
74492
+ showGroupDividers = false,
74493
+ expandedGroups: controlledExpandedGroups,
74494
+ onGroupToggle,
74495
+ theme: themeProp,
74496
+ collapseIcon,
74497
+ expandIcon,
74498
+ meta,
74499
+ actions,
74500
+ keyboardNavigation = false,
74501
+ draggable = false,
74502
+ onSidebarReorder,
74503
+ animationDuration = 200,
74504
+ animationEasing = "ease-in-out",
74505
+ sidebarTooltip,
74506
+ mobileBreakpoint = 768,
74507
+ mobileCollapsed: controlledMobileCollapsed,
74508
+ onMobileCollapseChange
74481
74509
  }) {
74482
- const [collapsed, setCollapsed] = React42.useState(defaultCollapsed);
74483
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PanelCollapsedContext.Provider, { value: collapsed, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
74510
+ const [internalCollapsed, setInternalCollapsed] = React42.useState(defaultCollapsed);
74511
+ const [internalPage, setInternalPage] = React42.useState(defaultPage || "");
74512
+ const [internalExpandedGroups, setInternalExpandedGroups] = React42.useState(
74513
+ new Set(controlledExpandedGroups)
74514
+ );
74515
+ const [isMobile, setIsMobile] = React42.useState(false);
74516
+ const [internalMobileCollapsed, setInternalMobileCollapsed] = React42.useState(true);
74517
+ const isCollapsed = controlledCollapsed !== void 0 ? controlledCollapsed : internalCollapsed;
74518
+ const currentPage = controlledPage !== void 0 ? controlledPage : internalPage;
74519
+ const expandedGroups = controlledExpandedGroups !== void 0 ? new Set(controlledExpandedGroups) : internalExpandedGroups;
74520
+ const mobileCollapsed = controlledMobileCollapsed !== void 0 ? controlledMobileCollapsed : internalMobileCollapsed;
74521
+ const handleCollapsedChange = (value) => {
74522
+ if (controlledCollapsed === void 0) setInternalCollapsed(value);
74523
+ onCollapsedChange?.(value);
74524
+ };
74525
+ const handlePageChange = (page) => {
74526
+ if (controlledPage === void 0) setInternalPage(page);
74527
+ onPageChange?.(page);
74528
+ };
74529
+ const handleGroupToggle = (title, expanded) => {
74530
+ if (controlledExpandedGroups === void 0) {
74531
+ setInternalExpandedGroups((prev) => {
74532
+ const next = new Set(prev);
74533
+ if (expanded) next.add(title);
74534
+ else next.delete(title);
74535
+ return next;
74536
+ });
74537
+ }
74538
+ onGroupToggle?.(title, expanded);
74539
+ };
74540
+ const handleMobileCollapseChange = (value) => {
74541
+ if (controlledMobileCollapsed === void 0) setInternalMobileCollapsed(value);
74542
+ onMobileCollapseChange?.(value);
74543
+ };
74544
+ React42.useEffect(() => {
74545
+ const handleResize = () => {
74546
+ const mobile = window.innerWidth < mobileBreakpoint;
74547
+ setIsMobile(mobile);
74548
+ if (mobile && !internalMobileCollapsed) {
74549
+ handleMobileCollapseChange(true);
74550
+ }
74551
+ };
74552
+ handleResize();
74553
+ window.addEventListener("resize", handleResize);
74554
+ return () => window.removeEventListener("resize", handleResize);
74555
+ }, [mobileBreakpoint, internalMobileCollapsed]);
74556
+ const handleKeyDown = React42.useCallback(
74557
+ (e) => {
74558
+ if (!keyboardNavigation) return;
74559
+ if (e.key === "Escape" && !isCollapsed && collapsible) {
74560
+ handleCollapsedChange(true);
74561
+ }
74562
+ if (e.key === "Enter" && isCollapsed && collapsible) {
74563
+ handleCollapsedChange(false);
74564
+ }
74565
+ },
74566
+ [keyboardNavigation, isCollapsed, collapsible]
74567
+ );
74568
+ React42.useEffect(() => {
74569
+ if (keyboardNavigation) {
74570
+ window.addEventListener("keydown", handleKeyDown);
74571
+ return () => window.removeEventListener("keydown", handleKeyDown);
74572
+ }
74573
+ }, [keyboardNavigation, handleKeyDown]);
74574
+ const effectiveCollapsed = isMobile ? mobileCollapsed : isCollapsed;
74575
+ const animStyle = {
74576
+ transitionDuration: `${animationDuration}ms`,
74577
+ transitionTimingFunction: animationEasing
74578
+ };
74579
+ const hasContent = React42.Children.count(children) > 0;
74580
+ const showEmpty = !loading && !hasContent && emptyState;
74581
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PanelCollapsedContext.Provider, { value: effectiveCollapsed, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PanelGroupsContext.Provider, { value: { expandedGroups, onGroupToggle: handleGroupToggle }, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
74484
74582
  "div",
74485
74583
  {
74486
74584
  className: cn(
@@ -74488,6 +74586,7 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74488
74586
  height,
74489
74587
  className
74490
74588
  ),
74589
+ style: { ...animStyle },
74491
74590
  children: [
74492
74591
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "pointer-events-none absolute inset-0 overflow-hidden", children: [
74493
74592
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "absolute -top-[40%] -left-[20%] h-[80%] w-[60%] rounded-full bg-primary/10 blur-[120px]" }),
@@ -74497,23 +74596,36 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74497
74596
  "aside",
74498
74597
  {
74499
74598
  className: cn(
74500
- "relative z-10 flex flex-col shrink-0 border-r border-border transition-all duration-200",
74501
- collapsed ? "w-14" : sidebarWidth
74599
+ "relative z-10 flex flex-col shrink-0 border-r border-border transition-all",
74600
+ effectiveCollapsed ? "w-14" : sidebarWidth
74502
74601
  ),
74602
+ style: { transitionDuration: `${animationDuration}ms`, transitionTimingFunction: animationEasing },
74503
74603
  children: [
74504
- (sidebarBrand || sidebarHeader) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: cn(
74505
- "shrink-0 border-b border-border",
74506
- collapsed ? "flex items-center justify-center py-3" : "flex items-center gap-2 px-4 py-3"
74507
- ), children: sidebarBrand ? collapsed ? sidebarBrand.image ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("img", { src: sidebarBrand.image, alt: "logo", className: "h-7 w-7 rounded-md object-cover shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarBrand.icon }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
74508
- sidebarBrand.image ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("img", { src: sidebarBrand.image, alt: "logo", className: "h-7 w-7 rounded-md object-cover shrink-0" }) : sidebarBrand.icon && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarBrand.icon }),
74509
- sidebarBrand.title && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "flex-1 truncate text-sm font-semibold", children: sidebarBrand.title }),
74510
- sidebarBrand.trailing && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarBrand.trailing })
74511
- ] }) : !collapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "text-sm font-semibold", children: sidebarHeader }) }),
74604
+ (sidebarBrand || sidebarHeader) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
74605
+ "div",
74606
+ {
74607
+ className: cn(
74608
+ "shrink-0 border-b border-border",
74609
+ effectiveCollapsed ? "flex items-center justify-center py-3" : "flex items-center gap-2 px-4 py-3"
74610
+ ),
74611
+ children: sidebarBrand ? effectiveCollapsed ? sidebarBrand.image ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("img", { src: sidebarBrand.image, alt: "logo", className: "h-7 w-7 rounded-md object-cover shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarBrand.icon }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
74612
+ sidebarBrand.image ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("img", { src: sidebarBrand.image, alt: "logo", className: "h-7 w-7 rounded-md object-cover shrink-0" }) : sidebarBrand.icon && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarBrand.icon }),
74613
+ sidebarBrand.title && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "flex-1 truncate text-sm font-semibold", children: sidebarBrand.title }),
74614
+ sidebarBrand.trailing && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarBrand.trailing })
74615
+ ] }) : !effectiveCollapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "text-sm font-semibold", children: sidebarHeader })
74616
+ }
74617
+ ),
74512
74618
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex-1 overflow-y-auto py-2", children: sidebar }),
74513
- (sidebarProfile || sidebarFooter) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: cn(
74514
- "shrink-0 border-t border-border",
74515
- collapsed ? "flex items-center justify-center py-3" : "px-4 py-3"
74516
- ), children: sidebarProfile ? collapsed ? sidebarProfile.image ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("img", { src: sidebarProfile.image, alt: "profile", className: "h-7 w-7 rounded-full object-cover shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarProfile.icon }) : sidebarProfile.content ?? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex items-center gap-2", children: sidebarProfile.image ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("img", { src: sidebarProfile.image, alt: "profile", className: "h-7 w-7 rounded-full object-cover shrink-0" }) : sidebarProfile.icon && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarProfile.icon }) }) : !collapsed && sidebarFooter })
74619
+ (sidebarProfile || sidebarFooter) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
74620
+ "div",
74621
+ {
74622
+ className: cn(
74623
+ "shrink-0 border-t border-border",
74624
+ effectiveCollapsed ? "flex items-center justify-center py-3" : "px-4 py-3"
74625
+ ),
74626
+ children: sidebarProfile ? effectiveCollapsed ? sidebarProfile.image ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("img", { src: sidebarProfile.image, alt: "profile", className: "h-7 w-7 rounded-full object-cover shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarProfile.icon }) : sidebarProfile.content ?? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex items-center gap-2", children: sidebarProfile.image ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("img", { src: sidebarProfile.image, alt: "profile", className: "h-7 w-7 rounded-full object-cover shrink-0" }) : sidebarProfile.icon && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarProfile.icon }) }) : !effectiveCollapsed && sidebarFooter
74627
+ }
74628
+ )
74517
74629
  ]
74518
74630
  }
74519
74631
  ),
@@ -74523,16 +74635,22 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74523
74635
  collapsible && sidebar && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
74524
74636
  Tooltip,
74525
74637
  {
74526
- content: collapsed ? "Expand sidebar" : "Collapse sidebar",
74638
+ content: effectiveCollapsed ? "Expand sidebar" : "Collapse sidebar",
74527
74639
  side: "bottom",
74528
74640
  children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
74529
74641
  "button",
74530
74642
  {
74531
74643
  type: "button",
74532
- onClick: () => setCollapsed((c) => !c),
74644
+ onClick: () => {
74645
+ if (isMobile) {
74646
+ handleMobileCollapseChange(!mobileCollapsed);
74647
+ } else {
74648
+ handleCollapsedChange(!isCollapsed);
74649
+ }
74650
+ },
74533
74651
  className: "text-muted-foreground hover:text-foreground transition-colors",
74534
- "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
74535
- children: collapsed ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PanelLeftOpen, { className: "h-5 w-5" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PanelLeftClose, { className: "h-5 w-5" })
74652
+ "aria-label": effectiveCollapsed ? "Expand sidebar" : "Collapse sidebar",
74653
+ children: effectiveCollapsed ? expandIcon || /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PanelLeftOpen, { className: "h-5 w-5" }) : collapseIcon || /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PanelLeftClose, { className: "h-5 w-5" })
74536
74654
  }
74537
74655
  )
74538
74656
  }
@@ -74544,11 +74662,16 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74544
74662
  showThemeToggle && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PanelThemeToggle, {})
74545
74663
  ] })
74546
74664
  ] }),
74547
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("main", { className: "flex-1 overflow-y-auto p-4", children })
74665
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("main", { className: "flex-1 overflow-y-auto p-4", children: [
74666
+ error && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "mb-4 p-3 rounded-md bg-destructive/10 text-destructive text-sm", children: error }),
74667
+ loading && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(LoaderCircle, { className: "h-6 w-6 animate-spin text-muted-foreground" }) }),
74668
+ showEmpty && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex items-center justify-center h-full text-muted-foreground", children: emptyState }),
74669
+ !loading && !showEmpty && children
74670
+ ] })
74548
74671
  ] })
74549
74672
  ]
74550
74673
  }
74551
- ) });
74674
+ ) }) });
74552
74675
  }
74553
74676
  function PanelSidebarItem({
74554
74677
  icon: Icon2,
@@ -74579,10 +74702,20 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74579
74702
  children
74580
74703
  }) {
74581
74704
  const collapsed = React42.useContext(PanelCollapsedContext);
74705
+ const { expandedGroups, onGroupToggle } = React42.useContext(PanelGroupsContext);
74706
+ const isExpanded = title ? expandedGroups.has(title) : true;
74582
74707
  return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "px-2 py-1", children: [
74583
- title && !collapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "mb-1 px-2 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground", children: title }),
74708
+ title && !collapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
74709
+ "button",
74710
+ {
74711
+ type: "button",
74712
+ onClick: () => onGroupToggle(title, !isExpanded),
74713
+ className: "mb-1 px-2 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground hover:text-foreground transition-colors w-full text-left",
74714
+ children: title
74715
+ }
74716
+ ),
74584
74717
  title && collapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "mx-1 mb-1 h-px bg-border" }),
74585
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("main", { className: "space-y-0.5", children })
74718
+ (!title || isExpanded) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("main", { className: "space-y-0.5", children })
74586
74719
  ] });
74587
74720
  }
74588
74721
 
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 });
@@ -7372,15 +7373,16 @@ function DGModalShell({ title, onClose, children, footer, width = "lg" }) {
7372
7373
  }
7373
7374
  function DGFieldRenderer({ field, value, onChange }) {
7374
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
+ };
7375
7381
  const strOptions = (field.options ?? []).map(
7376
- (o) => typeof o === "string" ? o : o.value
7377
- );
7378
- const comboOptions = (field.options ?? []).map(
7379
- (o) => typeof o === "string" ? { label: o, value: o } : o
7380
- );
7381
- const radioOptions = (field.options ?? []).map(
7382
- (o) => typeof o === "string" ? { label: o, value: o } : o
7382
+ (o) => typeof o === "string" ? o : Array.isArray(o) ? o[1] : o.value
7383
7383
  );
7384
+ const comboOptions = (field.options ?? []).map(toLabelValue);
7385
+ const radioOptions = (field.options ?? []).map(toLabelValue);
7384
7386
  switch (field.type) {
7385
7387
  case "textarea":
7386
7388
  return /* @__PURE__ */ jsx33(Textarea, { value: value ?? "", onChange: (e) => onChange(e.target.value), placeholder: field.placeholder, rows: 3 });
@@ -10149,7 +10151,7 @@ function GroupNavigation({
10149
10151
 
10150
10152
  // src/components/ui/panel.tsx
10151
10153
  import * as React40 from "react";
10152
- 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";
10153
10155
 
10154
10156
  // src/components/ui/tooltip.tsx
10155
10157
  import * as React38 from "react";
@@ -10764,6 +10766,8 @@ var useTheme = () => {
10764
10766
  // src/components/ui/panel.tsx
10765
10767
  import { Fragment as Fragment17, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
10766
10768
  var PanelCollapsedContext = React40.createContext(false);
10769
+ var PanelGroupsContext = React40.createContext({ expandedGroups: /* @__PURE__ */ new Set(), onGroupToggle: () => {
10770
+ } });
10767
10771
  function PanelThemeToggle() {
10768
10772
  const { theme, setTheme } = useTheme();
10769
10773
  return /* @__PURE__ */ jsxs41(
@@ -10790,15 +10794,109 @@ function Panel({
10790
10794
  topbar,
10791
10795
  topbarTrailing,
10792
10796
  defaultCollapsed = false,
10797
+ collapsed: controlledCollapsed,
10798
+ onCollapsedChange,
10793
10799
  collapsible = false,
10794
10800
  showThemeToggle = false,
10795
10801
  defaultPage,
10802
+ currentPage: controlledPage,
10803
+ onPageChange,
10796
10804
  height = "h-[520px]",
10797
10805
  children,
10798
- 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
10799
10827
  }) {
10800
- const [collapsed, setCollapsed] = React40.useState(defaultCollapsed);
10801
- 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(
10802
10900
  "div",
10803
10901
  {
10804
10902
  className: cn(
@@ -10806,6 +10904,7 @@ function Panel({
10806
10904
  height,
10807
10905
  className
10808
10906
  ),
10907
+ style: { ...animStyle },
10809
10908
  children: [
10810
10909
  /* @__PURE__ */ jsxs41("div", { className: "pointer-events-none absolute inset-0 overflow-hidden", children: [
10811
10910
  /* @__PURE__ */ jsx46("div", { className: "absolute -top-[40%] -left-[20%] h-[80%] w-[60%] rounded-full bg-primary/10 blur-[120px]" }),
@@ -10815,23 +10914,36 @@ function Panel({
10815
10914
  "aside",
10816
10915
  {
10817
10916
  className: cn(
10818
- "relative z-10 flex flex-col shrink-0 border-r border-border transition-all duration-200",
10819
- collapsed ? "w-14" : sidebarWidth
10917
+ "relative z-10 flex flex-col shrink-0 border-r border-border transition-all",
10918
+ effectiveCollapsed ? "w-14" : sidebarWidth
10820
10919
  ),
10920
+ style: { transitionDuration: `${animationDuration}ms`, transitionTimingFunction: animationEasing },
10821
10921
  children: [
10822
- (sidebarBrand || sidebarHeader) && /* @__PURE__ */ jsx46("div", { className: cn(
10823
- "shrink-0 border-b border-border",
10824
- collapsed ? "flex items-center justify-center py-3" : "flex items-center gap-2 px-4 py-3"
10825
- ), 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: [
10826
- 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 }),
10827
- sidebarBrand.title && /* @__PURE__ */ jsx46("span", { className: "flex-1 truncate text-sm font-semibold", children: sidebarBrand.title }),
10828
- sidebarBrand.trailing && /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.trailing })
10829
- ] }) : !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
+ ),
10830
10936
  /* @__PURE__ */ jsx46("div", { className: "flex-1 overflow-y-auto py-2", children: sidebar }),
10831
- (sidebarProfile || sidebarFooter) && /* @__PURE__ */ jsx46("div", { className: cn(
10832
- "shrink-0 border-t border-border",
10833
- collapsed ? "flex items-center justify-center py-3" : "px-4 py-3"
10834
- ), 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
+ )
10835
10947
  ]
10836
10948
  }
10837
10949
  ),
@@ -10841,16 +10953,22 @@ function Panel({
10841
10953
  collapsible && sidebar && /* @__PURE__ */ jsx46(
10842
10954
  Tooltip,
10843
10955
  {
10844
- content: collapsed ? "Expand sidebar" : "Collapse sidebar",
10956
+ content: effectiveCollapsed ? "Expand sidebar" : "Collapse sidebar",
10845
10957
  side: "bottom",
10846
10958
  children: /* @__PURE__ */ jsx46(
10847
10959
  "button",
10848
10960
  {
10849
10961
  type: "button",
10850
- onClick: () => setCollapsed((c) => !c),
10962
+ onClick: () => {
10963
+ if (isMobile) {
10964
+ handleMobileCollapseChange(!mobileCollapsed);
10965
+ } else {
10966
+ handleCollapsedChange(!isCollapsed);
10967
+ }
10968
+ },
10851
10969
  className: "text-muted-foreground hover:text-foreground transition-colors",
10852
- "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
10853
- 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" })
10854
10972
  }
10855
10973
  )
10856
10974
  }
@@ -10862,11 +10980,16 @@ function Panel({
10862
10980
  showThemeToggle && /* @__PURE__ */ jsx46(PanelThemeToggle, {})
10863
10981
  ] })
10864
10982
  ] }),
10865
- /* @__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
+ ] })
10866
10989
  ] })
10867
10990
  ]
10868
10991
  }
10869
- ) });
10992
+ ) }) });
10870
10993
  }
10871
10994
  function PanelSidebarItem({
10872
10995
  icon: Icon,
@@ -10897,10 +11020,20 @@ function PanelSidebarGroup({
10897
11020
  children
10898
11021
  }) {
10899
11022
  const collapsed = React40.useContext(PanelCollapsedContext);
11023
+ const { expandedGroups, onGroupToggle } = React40.useContext(PanelGroupsContext);
11024
+ const isExpanded = title ? expandedGroups.has(title) : true;
10900
11025
  return /* @__PURE__ */ jsxs41("div", { className: "px-2 py-1", children: [
10901
- 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
+ ),
10902
11035
  title && collapsed && /* @__PURE__ */ jsx46("div", { className: "mx-1 mb-1 h-px bg-border" }),
10903
- /* @__PURE__ */ jsx46("main", { className: "space-y-0.5", children })
11036
+ (!title || isExpanded) && /* @__PURE__ */ jsx46("main", { className: "space-y-0.5", children })
10904
11037
  ] });
10905
11038
  }
10906
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.3.0",
7
+ "version": "3.3.1",
8
8
  "description": "Reusable React UI components",
9
9
  "license": "MIT",
10
10
  "main": "dist/index.js",