@juv/codego-react-ui 3.3.0 → 3.3.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.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 });
@@ -10118,7 +10120,7 @@ function GroupNavigation({
10118
10120
  group.collapsible && "cursor-pointer hover:text-foreground"
10119
10121
  ),
10120
10122
  children: [
10121
- /* @__PURE__ */ jsx43("span", { className: "flex-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: group.label }),
10123
+ /* @__PURE__ */ jsx43("span", { className: "flex-1 text-xs font-semibold uppercase tracking-wider text-primary/50 text-left", children: group.label }),
10122
10124
  group.collapsible && (isOpen ? /* @__PURE__ */ jsx43(ChevronDown6, { className: "h-3 w-3 text-muted-foreground" }) : /* @__PURE__ */ jsx43(ChevronRight10, { className: "h-3 w-3 text-muted-foreground" }))
10123
10125
  ]
10124
10126
  }
@@ -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,42 +10914,61 @@ 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
  ),
10838
10950
  /* @__PURE__ */ jsxs41("div", { className: "relative z-10 flex flex-1 min-w-0 flex-col", children: [
10839
- /* @__PURE__ */ jsxs41("header", { className: "flex h-14 shrink-0 items-center justify-between border-b glass px-4 gap-2", children: [
10951
+ /* @__PURE__ */ jsxs41("header", { className: "flex h-14 shrink-0 items-center justify-between border-b border-b-slate-300/50 px-4 gap-2", children: [
10840
10952
  /* @__PURE__ */ jsxs41("div", { className: "flex items-center gap-2", children: [
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/50 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.3",
8
8
  "description": "Reusable React UI components",
9
9
  "license": "MIT",
10
10
  "main": "dist/index.js",