@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.cjs CHANGED
@@ -6527,15 +6527,16 @@ function validateField(field, value) {
6527
6527
  }
6528
6528
  function FieldRenderer({ field, value, onChange }) {
6529
6529
  if (field.render) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children: field.render(value, onChange) });
6530
+ const toLabelValue = (o) => {
6531
+ if (typeof o === "string") return { label: o, value: o };
6532
+ if (Array.isArray(o)) return { label: o[0], value: o[1] };
6533
+ return o;
6534
+ };
6530
6535
  const strOptions = (field.options ?? []).map(
6531
- (o) => typeof o === "string" ? o : o.value
6532
- );
6533
- const comboOptions = (field.options ?? []).map(
6534
- (o) => typeof o === "string" ? { label: o, value: o } : o
6535
- );
6536
- const radioOptions = (field.options ?? []).map(
6537
- (o) => typeof o === "string" ? { label: o, value: o } : o
6536
+ (o) => typeof o === "string" ? o : Array.isArray(o) ? o[1] : o.value
6538
6537
  );
6538
+ const comboOptions = (field.options ?? []).map(toLabelValue);
6539
+ const radioOptions = (field.options ?? []).map(toLabelValue);
6539
6540
  switch (field.type) {
6540
6541
  case "textarea":
6541
6542
  return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Textarea, { value: value ?? "", onChange: (e) => onChange(e.target.value), placeholder: field.placeholder, rows: 3 });
@@ -7490,15 +7491,16 @@ function DGModalShell({ title, onClose, children, footer, width = "lg" }) {
7490
7491
  }
7491
7492
  function DGFieldRenderer({ field, value, onChange }) {
7492
7493
  if (field.render) return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_jsx_runtime33.Fragment, { children: field.render(value, onChange) });
7494
+ const toLabelValue = (o) => {
7495
+ if (typeof o === "string") return { label: o, value: o };
7496
+ if (Array.isArray(o)) return { label: o[0], value: o[1] };
7497
+ return o;
7498
+ };
7493
7499
  const strOptions = (field.options ?? []).map(
7494
- (o) => typeof o === "string" ? o : o.value
7495
- );
7496
- const comboOptions = (field.options ?? []).map(
7497
- (o) => typeof o === "string" ? { label: o, value: o } : o
7498
- );
7499
- const radioOptions = (field.options ?? []).map(
7500
- (o) => typeof o === "string" ? { label: o, value: o } : o
7500
+ (o) => typeof o === "string" ? o : Array.isArray(o) ? o[1] : o.value
7501
7501
  );
7502
+ const comboOptions = (field.options ?? []).map(toLabelValue);
7503
+ const radioOptions = (field.options ?? []).map(toLabelValue);
7502
7504
  switch (field.type) {
7503
7505
  case "textarea":
7504
7506
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Textarea, { value: value ?? "", onChange: (e) => onChange(e.target.value), placeholder: field.placeholder, rows: 3 });
@@ -10236,7 +10238,7 @@ function GroupNavigation({
10236
10238
  group.collapsible && "cursor-pointer hover:text-foreground"
10237
10239
  ),
10238
10240
  children: [
10239
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: group.label }),
10241
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex-1 text-xs font-semibold uppercase tracking-wider text-primary/50 text-left", children: group.label }),
10240
10242
  group.collapsible && (isOpen ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react24.ChevronDown, { className: "h-3 w-3 text-muted-foreground" }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react24.ChevronRight, { className: "h-3 w-3 text-muted-foreground" }))
10241
10243
  ]
10242
10244
  }
@@ -10882,6 +10884,8 @@ var useTheme = () => {
10882
10884
  // src/components/ui/panel.tsx
10883
10885
  var import_jsx_runtime46 = require("react/jsx-runtime");
10884
10886
  var PanelCollapsedContext = React40.createContext(false);
10887
+ var PanelGroupsContext = React40.createContext({ expandedGroups: /* @__PURE__ */ new Set(), onGroupToggle: () => {
10888
+ } });
10885
10889
  function PanelThemeToggle() {
10886
10890
  const { theme, setTheme } = useTheme();
10887
10891
  return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
@@ -10908,15 +10912,109 @@ function Panel({
10908
10912
  topbar,
10909
10913
  topbarTrailing,
10910
10914
  defaultCollapsed = false,
10915
+ collapsed: controlledCollapsed,
10916
+ onCollapsedChange,
10911
10917
  collapsible = false,
10912
10918
  showThemeToggle = false,
10913
10919
  defaultPage,
10920
+ currentPage: controlledPage,
10921
+ onPageChange,
10914
10922
  height = "h-[520px]",
10915
10923
  children,
10916
- className
10924
+ className,
10925
+ loading = false,
10926
+ emptyState,
10927
+ error = null,
10928
+ showGroupDividers = false,
10929
+ expandedGroups: controlledExpandedGroups,
10930
+ onGroupToggle,
10931
+ theme: themeProp,
10932
+ collapseIcon,
10933
+ expandIcon,
10934
+ meta,
10935
+ actions,
10936
+ keyboardNavigation = false,
10937
+ draggable = false,
10938
+ onSidebarReorder,
10939
+ animationDuration = 200,
10940
+ animationEasing = "ease-in-out",
10941
+ sidebarTooltip,
10942
+ mobileBreakpoint = 768,
10943
+ mobileCollapsed: controlledMobileCollapsed,
10944
+ onMobileCollapseChange
10917
10945
  }) {
10918
- const [collapsed, setCollapsed] = React40.useState(defaultCollapsed);
10919
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PanelCollapsedContext.Provider, { value: collapsed, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
10946
+ const [internalCollapsed, setInternalCollapsed] = React40.useState(defaultCollapsed);
10947
+ const [internalPage, setInternalPage] = React40.useState(defaultPage || "");
10948
+ const [internalExpandedGroups, setInternalExpandedGroups] = React40.useState(
10949
+ new Set(controlledExpandedGroups)
10950
+ );
10951
+ const [isMobile, setIsMobile] = React40.useState(false);
10952
+ const [internalMobileCollapsed, setInternalMobileCollapsed] = React40.useState(true);
10953
+ const isCollapsed = controlledCollapsed !== void 0 ? controlledCollapsed : internalCollapsed;
10954
+ const currentPage = controlledPage !== void 0 ? controlledPage : internalPage;
10955
+ const expandedGroups = controlledExpandedGroups !== void 0 ? new Set(controlledExpandedGroups) : internalExpandedGroups;
10956
+ const mobileCollapsed = controlledMobileCollapsed !== void 0 ? controlledMobileCollapsed : internalMobileCollapsed;
10957
+ const handleCollapsedChange = (value) => {
10958
+ if (controlledCollapsed === void 0) setInternalCollapsed(value);
10959
+ onCollapsedChange?.(value);
10960
+ };
10961
+ const handlePageChange = (page) => {
10962
+ if (controlledPage === void 0) setInternalPage(page);
10963
+ onPageChange?.(page);
10964
+ };
10965
+ const handleGroupToggle = (title, expanded) => {
10966
+ if (controlledExpandedGroups === void 0) {
10967
+ setInternalExpandedGroups((prev) => {
10968
+ const next = new Set(prev);
10969
+ if (expanded) next.add(title);
10970
+ else next.delete(title);
10971
+ return next;
10972
+ });
10973
+ }
10974
+ onGroupToggle?.(title, expanded);
10975
+ };
10976
+ const handleMobileCollapseChange = (value) => {
10977
+ if (controlledMobileCollapsed === void 0) setInternalMobileCollapsed(value);
10978
+ onMobileCollapseChange?.(value);
10979
+ };
10980
+ React40.useEffect(() => {
10981
+ const handleResize = () => {
10982
+ const mobile = window.innerWidth < mobileBreakpoint;
10983
+ setIsMobile(mobile);
10984
+ if (mobile && !internalMobileCollapsed) {
10985
+ handleMobileCollapseChange(true);
10986
+ }
10987
+ };
10988
+ handleResize();
10989
+ window.addEventListener("resize", handleResize);
10990
+ return () => window.removeEventListener("resize", handleResize);
10991
+ }, [mobileBreakpoint, internalMobileCollapsed]);
10992
+ const handleKeyDown = React40.useCallback(
10993
+ (e) => {
10994
+ if (!keyboardNavigation) return;
10995
+ if (e.key === "Escape" && !isCollapsed && collapsible) {
10996
+ handleCollapsedChange(true);
10997
+ }
10998
+ if (e.key === "Enter" && isCollapsed && collapsible) {
10999
+ handleCollapsedChange(false);
11000
+ }
11001
+ },
11002
+ [keyboardNavigation, isCollapsed, collapsible]
11003
+ );
11004
+ React40.useEffect(() => {
11005
+ if (keyboardNavigation) {
11006
+ window.addEventListener("keydown", handleKeyDown);
11007
+ return () => window.removeEventListener("keydown", handleKeyDown);
11008
+ }
11009
+ }, [keyboardNavigation, handleKeyDown]);
11010
+ const effectiveCollapsed = isMobile ? mobileCollapsed : isCollapsed;
11011
+ const animStyle = {
11012
+ transitionDuration: `${animationDuration}ms`,
11013
+ transitionTimingFunction: animationEasing
11014
+ };
11015
+ const hasContent = React40.Children.count(children) > 0;
11016
+ const showEmpty = !loading && !hasContent && emptyState;
11017
+ 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)(
10920
11018
  "div",
10921
11019
  {
10922
11020
  className: cn(
@@ -10924,6 +11022,7 @@ function Panel({
10924
11022
  height,
10925
11023
  className
10926
11024
  ),
11025
+ style: { ...animStyle },
10927
11026
  children: [
10928
11027
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "pointer-events-none absolute inset-0 overflow-hidden", children: [
10929
11028
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "absolute -top-[40%] -left-[20%] h-[80%] w-[60%] rounded-full bg-primary/10 blur-[120px]" }),
@@ -10933,42 +11032,61 @@ function Panel({
10933
11032
  "aside",
10934
11033
  {
10935
11034
  className: cn(
10936
- "relative z-10 flex flex-col shrink-0 border-r border-border transition-all duration-200",
10937
- collapsed ? "w-14" : sidebarWidth
11035
+ "relative z-10 flex flex-col shrink-0 border-r border-border transition-all",
11036
+ effectiveCollapsed ? "w-14" : sidebarWidth
10938
11037
  ),
11038
+ style: { transitionDuration: `${animationDuration}ms`, transitionTimingFunction: animationEasing },
10939
11039
  children: [
10940
- (sidebarBrand || sidebarHeader) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: cn(
10941
- "shrink-0 border-b border-border",
10942
- collapsed ? "flex items-center justify-center py-3" : "flex items-center gap-2 px-4 py-3"
10943
- ), 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: [
10944
- 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 }),
10945
- sidebarBrand.title && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "flex-1 truncate text-sm font-semibold", children: sidebarBrand.title }),
10946
- sidebarBrand.trailing && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarBrand.trailing })
10947
- ] }) : !collapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "text-sm font-semibold", children: sidebarHeader }) }),
11040
+ (sidebarBrand || sidebarHeader) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
11041
+ "div",
11042
+ {
11043
+ className: cn(
11044
+ "shrink-0 border-b border-border",
11045
+ effectiveCollapsed ? "flex items-center justify-center py-3" : "flex items-center gap-2 px-4 py-3"
11046
+ ),
11047
+ 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: [
11048
+ 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 }),
11049
+ sidebarBrand.title && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "flex-1 truncate text-sm font-semibold", children: sidebarBrand.title }),
11050
+ sidebarBrand.trailing && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "shrink-0", children: sidebarBrand.trailing })
11051
+ ] }) : !effectiveCollapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "text-sm font-semibold", children: sidebarHeader })
11052
+ }
11053
+ ),
10948
11054
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex-1 overflow-y-auto py-2", children: sidebar }),
10949
- (sidebarProfile || sidebarFooter) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: cn(
10950
- "shrink-0 border-t border-border",
10951
- collapsed ? "flex items-center justify-center py-3" : "px-4 py-3"
10952
- ), 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 })
11055
+ (sidebarProfile || sidebarFooter) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
11056
+ "div",
11057
+ {
11058
+ className: cn(
11059
+ "shrink-0 border-t border-border",
11060
+ effectiveCollapsed ? "flex items-center justify-center py-3" : "px-4 py-3"
11061
+ ),
11062
+ 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
11063
+ }
11064
+ )
10953
11065
  ]
10954
11066
  }
10955
11067
  ),
10956
11068
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "relative z-10 flex flex-1 min-w-0 flex-col", children: [
10957
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("header", { className: "flex h-14 shrink-0 items-center justify-between border-b glass px-4 gap-2", children: [
11069
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("header", { className: "flex h-14 shrink-0 items-center justify-between border-b border-b-slate-300/50 px-4 gap-2", children: [
10958
11070
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
10959
11071
  collapsible && sidebar && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
10960
11072
  Tooltip,
10961
11073
  {
10962
- content: collapsed ? "Expand sidebar" : "Collapse sidebar",
11074
+ content: effectiveCollapsed ? "Expand sidebar" : "Collapse sidebar",
10963
11075
  side: "bottom",
10964
11076
  children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
10965
11077
  "button",
10966
11078
  {
10967
11079
  type: "button",
10968
- onClick: () => setCollapsed((c) => !c),
11080
+ onClick: () => {
11081
+ if (isMobile) {
11082
+ handleMobileCollapseChange(!mobileCollapsed);
11083
+ } else {
11084
+ handleCollapsedChange(!isCollapsed);
11085
+ }
11086
+ },
10969
11087
  className: "text-muted-foreground hover:text-foreground transition-colors",
10970
- "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
10971
- children: collapsed ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react25.PanelLeftOpen, { className: "h-5 w-5" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react25.PanelLeftClose, { className: "h-5 w-5" })
11088
+ "aria-label": effectiveCollapsed ? "Expand sidebar" : "Collapse sidebar",
11089
+ children: effectiveCollapsed ? expandIcon || /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react25.PanelLeftOpen, { className: "h-5 w-5" }) : collapseIcon || /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react25.PanelLeftClose, { className: "h-5 w-5" })
10972
11090
  }
10973
11091
  )
10974
11092
  }
@@ -10980,11 +11098,16 @@ function Panel({
10980
11098
  showThemeToggle && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PanelThemeToggle, {})
10981
11099
  ] })
10982
11100
  ] }),
10983
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("main", { className: "flex-1 overflow-y-auto p-4", children })
11101
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("main", { className: "flex-1 overflow-y-auto p-4", children: [
11102
+ error && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "mb-4 p-3 rounded-md bg-destructive/50 text-destructive text-sm", children: error }),
11103
+ loading && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react25.Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) }),
11104
+ showEmpty && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex items-center justify-center h-full text-muted-foreground", children: emptyState }),
11105
+ !loading && !showEmpty && children
11106
+ ] })
10984
11107
  ] })
10985
11108
  ]
10986
11109
  }
10987
- ) });
11110
+ ) }) });
10988
11111
  }
10989
11112
  function PanelSidebarItem({
10990
11113
  icon: Icon,
@@ -11015,10 +11138,20 @@ function PanelSidebarGroup({
11015
11138
  children
11016
11139
  }) {
11017
11140
  const collapsed = React40.useContext(PanelCollapsedContext);
11141
+ const { expandedGroups, onGroupToggle } = React40.useContext(PanelGroupsContext);
11142
+ const isExpanded = title ? expandedGroups.has(title) : true;
11018
11143
  return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "px-2 py-1", children: [
11019
- 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 }),
11144
+ title && !collapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
11145
+ "button",
11146
+ {
11147
+ type: "button",
11148
+ onClick: () => onGroupToggle(title, !isExpanded),
11149
+ className: "mb-1 px-2 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground hover:text-foreground transition-colors w-full text-left",
11150
+ children: title
11151
+ }
11152
+ ),
11020
11153
  title && collapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "mx-1 mb-1 h-px bg-border" }),
11021
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("main", { className: "space-y-0.5", children })
11154
+ (!title || isExpanded) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("main", { className: "space-y-0.5", children })
11022
11155
  ] });
11023
11156
  }
11024
11157