@postxl/ui-components 1.5.3 → 1.5.5

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
@@ -96,6 +96,7 @@ import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
96
96
  import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
97
97
  import * as ResizablePrimitive from "react-resizable-panels";
98
98
  import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
99
+ import useLocalStorageState from "use-local-storage-state";
99
100
  import * as SliderPrimitive from "@radix-ui/react-slider";
100
101
  import { useDirection } from "@radix-ui/react-direction";
101
102
  import * as SwitchPrimitives from "@radix-ui/react-switch";
@@ -5086,7 +5087,7 @@ function NumberCell({ cell, table, rowIndex, columnId, isFocused, isEditing, isS
5086
5087
  return typeof v === "function" ? v : () => Boolean(v);
5087
5088
  }, [colMeta?.editable]);
5088
5089
  const isEditable = editableResolver(cell.row.original);
5089
- const { min, max, step, prefix, suffix, fallbackValue = "" } = cellOptions?.variant === "number" ? cellOptions : {};
5090
+ const { min, max, step, prefix, suffix, fallbackValue = "", formatter } = cellOptions?.variant === "number" ? cellOptions : {};
5090
5091
  const resolvedPrefix = React$28.useMemo(() => {
5091
5092
  if (typeof prefix === "function") return prefix(cell.row.original, initialValue);
5092
5093
  return prefix ?? null;
@@ -5218,7 +5219,7 @@ function NumberCell({ cell, table, rowIndex, columnId, isFocused, isEditing, isS
5218
5219
  className: cn("tabular-nums", colMeta?.align ?? "text-right"),
5219
5220
  children: initialValue === null || initialValue === void 0 ? fallbackValue : /* @__PURE__ */ jsxs(Fragment, { children: [
5220
5221
  resolvedPrefix,
5221
- initialValue.toLocaleString(),
5222
+ formatter ? formatter(initialValue) : initialValue.toLocaleString(),
5222
5223
  resolvedSuffix
5223
5224
  ] })
5224
5225
  })
@@ -9075,6 +9076,21 @@ SheetDescription.displayName = SheetPrimitive.Description.displayName;
9075
9076
 
9076
9077
  //#endregion
9077
9078
  //#region src/sidebar/sidebar-context-provider.tsx
9079
+ /**
9080
+ * Stabilises a small object prop by structural equality so that inline objects
9081
+ * (e.g. `defaultOpenSidebars={{ left: true }}`) don't trigger unnecessary
9082
+ * re-renders. Uses JSON serialisation — suitable for small, plain objects only.
9083
+ */
9084
+ function useStableValue(value) {
9085
+ const ref = React$13.useRef(value);
9086
+ const serialized = JSON.stringify(value);
9087
+ const prevSerialized = React$13.useRef(serialized);
9088
+ if (prevSerialized.current !== serialized) {
9089
+ prevSerialized.current = serialized;
9090
+ ref.current = value;
9091
+ }
9092
+ return ref.current;
9093
+ }
9078
9094
  const SIDEBAR_WIDTH = "16rem";
9079
9095
  const SIDEBAR_WIDTH_MOBILE = "18rem";
9080
9096
  const SIDEBAR_WIDTH_ICON = "3rem";
@@ -9089,34 +9105,34 @@ const SIDEBAR_MAX_WIDTH = 600;
9089
9105
  const SidebarContext = React$13.createContext(null);
9090
9106
  /**
9091
9107
  * Context that identifies which sidebar a component belongs to.
9092
- * Set by `<Sidebar sidebarId="...">`, consumed by `useSidebar()`.
9108
+ * Set by `<Sidebar side="...">`, consumed by `useSidebar()`.
9093
9109
  */
9094
- const SidebarIdContext = React$13.createContext("default");
9110
+ const SidebarSideContext = React$13.createContext(null);
9095
9111
  /**
9096
9112
  * Returns the state and controls for a specific sidebar.
9097
9113
  *
9098
- * @param sidebarId - Optional explicit sidebar ID. Falls back to the nearest
9099
- * `SidebarIdContext` (set by the parent `<Sidebar>`), then to `"default"`.
9114
+ * @param side - Optional explicit sidebar side. Falls back to the nearest
9115
+ * `SidebarSideContext` (set by the parent `<Sidebar>`), then to `"left"`.
9100
9116
  */
9101
- function useSidebar(sidebarId) {
9117
+ function useSidebar(side) {
9102
9118
  const store = React$13.useContext(SidebarContext);
9103
- const ctxId = React$13.useContext(SidebarIdContext);
9104
- const id = sidebarId ?? ctxId;
9119
+ const ctxSide = React$13.useContext(SidebarSideContext);
9120
+ const resolvedSide = side ?? ctxSide ?? "left";
9105
9121
  if (!store) throw new Error("useSidebar must be used within a SidebarProvider.");
9106
- const open = store.openStates[id] ?? store.defaultOpen;
9107
- const openMobile = store.mobileStates[id] ?? false;
9108
- const width = store.widthStates[id];
9122
+ const open = store.openStates[resolvedSide] ?? store.defaultOpen;
9123
+ const openMobile = store.mobileStates[resolvedSide] ?? false;
9124
+ const width = store.widthStates[resolvedSide];
9109
9125
  const { isMobile, isResizing } = store;
9110
- const registry = store.sidebarRegistry[id];
9126
+ const registry = store.sidebarRegistry[resolvedSide];
9111
9127
  const minWidth = registry?.minWidth ?? store.providerMinWidth;
9112
9128
  const maxWidth = registry?.maxWidth ?? store.providerMaxWidth;
9113
- const setOpen = React$13.useCallback((value) => store.setOpen(id, value), [store.setOpen, id]);
9114
- const setOpenMobile = React$13.useCallback((value) => store.setOpenMobile(id, value), [store.setOpenMobile, id]);
9115
- const setWidth = React$13.useCallback((value) => store.setWidth(id, value), [store.setWidth, id]);
9129
+ const setOpen = React$13.useCallback((value) => store.setOpen(resolvedSide, value), [store.setOpen, resolvedSide]);
9130
+ const setOpenMobile = React$13.useCallback((value) => store.setOpenMobile(resolvedSide, value), [store.setOpenMobile, resolvedSide]);
9131
+ const setWidth = React$13.useCallback((value) => store.setWidth(resolvedSide, value), [store.setWidth, resolvedSide]);
9116
9132
  const toggleSidebar = React$13.useCallback(() => {
9117
- if (store.isMobile) store.setOpenMobile(id, !(store.mobileStates[id] ?? false));
9118
- else store.setOpen(id, !(store.openStates[id] ?? store.defaultOpen));
9119
- }, [store, id]);
9133
+ if (store.isMobile) store.setOpenMobile(resolvedSide, !(store.mobileStates[resolvedSide] ?? false));
9134
+ else store.setOpen(resolvedSide, !(store.openStates[resolvedSide] ?? store.defaultOpen));
9135
+ }, [store, resolvedSide]);
9120
9136
  const state = open ? "expanded" : "collapsed";
9121
9137
  return React$13.useMemo(() => ({
9122
9138
  state,
@@ -9147,118 +9163,119 @@ function useSidebar(sidebarId) {
9147
9163
  ]);
9148
9164
  }
9149
9165
  /**
9150
- * Returns a list of all registered sidebars with their IDs and sides.
9166
+ * Returns a list of all registered sidebars with their sides.
9151
9167
  */
9152
9168
  function useRegisteredSidebars() {
9153
9169
  const store = React$13.useContext(SidebarContext);
9154
9170
  if (!store) throw new Error("useRegisteredSidebars must be used within a SidebarProvider.");
9155
- return React$13.useMemo(() => Object.entries(store.sidebarRegistry).map(([sidebarId, entry]) => ({
9156
- sidebarId,
9157
- side: entry.side
9158
- })), [store.sidebarRegistry]);
9171
+ return React$13.useMemo(() => Object.keys(store.sidebarRegistry).map((side) => ({ side })), [store.sidebarRegistry]);
9159
9172
  }
9160
- function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: setOpenProp, width: widthProp, onWidthChange, minWidth = SIDEBAR_MIN_WIDTH, maxWidth = SIDEBAR_MAX_WIDTH, storageKey, defaultOpenSidebars, defaultWidths, keyboardShortcuts, className, style, children,...props }) {
9173
+ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: setOpenProp, controlledSide = "left", width: widthProp, onWidthChange, minWidth = SIDEBAR_MIN_WIDTH, maxWidth = SIDEBAR_MAX_WIDTH, storageKey, defaultOpenSidebars, defaultWidths, keyboardShortcuts, className, style, children,...props }) {
9161
9174
  const isMobile = useIsMobile();
9162
9175
  const [isResizing, setIsResizing] = React$13.useState(false);
9163
- const storedRef = React$13.useRef(null);
9164
- if (storedRef.current === null && storageKey) try {
9165
- storedRef.current = JSON.parse(localStorage.getItem(storageKey) ?? "{}");
9166
- } catch {
9167
- storedRef.current = {};
9168
- }
9169
- const [openStates, setOpenStates] = React$13.useState(() => {
9170
- const states = { default: defaultOpen };
9171
- if (defaultOpenSidebars) Object.assign(states, defaultOpenSidebars);
9172
- if (storedRef.current) {
9173
- for (const [id, data] of Object.entries(storedRef.current)) if (data && typeof data === "object" && "open" in data) states[id] = data.open;
9174
- }
9175
- return states;
9176
+ const fallbackKey = React$13.useId();
9177
+ const stableDefaultOpenSidebars = useStableValue(defaultOpenSidebars);
9178
+ const stableDefaultWidths = useStableValue(defaultWidths);
9179
+ const [storedStates, setStoredStates] = useLocalStorageState(storageKey ?? fallbackKey, {
9180
+ defaultValue: () => {
9181
+ const states = {};
9182
+ if (stableDefaultOpenSidebars) for (const [side, open] of Object.entries(stableDefaultOpenSidebars)) states[side] = {
9183
+ ...states[side] ?? { open: defaultOpen },
9184
+ open
9185
+ };
9186
+ if (stableDefaultWidths) for (const [side, width] of Object.entries(stableDefaultWidths)) states[side] = {
9187
+ ...states[side] ?? { open: defaultOpen },
9188
+ width
9189
+ };
9190
+ return states;
9191
+ },
9192
+ storageSync: !!storageKey
9176
9193
  });
9194
+ const openStates = React$13.useMemo(() => {
9195
+ const states = {};
9196
+ if (stableDefaultOpenSidebars) Object.assign(states, stableDefaultOpenSidebars);
9197
+ for (const [side, data] of Object.entries(storedStates)) if (data && typeof data === "object" && "open" in data) states[side] = data.open;
9198
+ return states;
9199
+ }, [storedStates, stableDefaultOpenSidebars]);
9177
9200
  const [mobileStates, setMobileStates] = React$13.useState({});
9178
- React$13.useEffect(() => {
9179
- if (openProp !== void 0) setOpenStates((prev) => prev["default"] === openProp ? prev : {
9180
- ...prev,
9181
- default: openProp
9182
- });
9183
- }, [openProp]);
9184
9201
  const effectiveOpenStates = React$13.useMemo(() => {
9185
9202
  if (openProp !== void 0) return {
9186
9203
  ...openStates,
9187
- default: openProp
9204
+ [controlledSide]: openProp
9188
9205
  };
9189
9206
  return openStates;
9190
- }, [openStates, openProp]);
9191
- const [widthStates, setWidthStates] = React$13.useState(() => {
9207
+ }, [
9208
+ openStates,
9209
+ openProp,
9210
+ controlledSide
9211
+ ]);
9212
+ const widthStates = React$13.useMemo(() => {
9192
9213
  const states = {};
9193
- if (widthProp !== void 0) states["default"] = widthProp;
9194
- if (defaultWidths) Object.assign(states, defaultWidths);
9195
- if (storedRef.current) {
9196
- for (const [id, data] of Object.entries(storedRef.current)) if (data && typeof data === "object" && typeof data.width === "number") states[id] = data.width;
9197
- }
9214
+ if (stableDefaultWidths) Object.assign(states, stableDefaultWidths);
9215
+ for (const [side, data] of Object.entries(storedStates)) if (data && typeof data === "object" && typeof data.width === "number") states[side] = data.width;
9198
9216
  return states;
9199
- });
9200
- React$13.useEffect(() => {
9201
- if (widthProp !== void 0) setWidthStates((prev) => prev["default"] === widthProp ? prev : {
9202
- ...prev,
9203
- default: widthProp
9204
- });
9205
- }, [widthProp]);
9217
+ }, [storedStates, stableDefaultWidths]);
9206
9218
  const effectiveWidthStates = React$13.useMemo(() => {
9207
9219
  if (widthProp !== void 0) return {
9208
9220
  ...widthStates,
9209
- default: widthProp
9221
+ [controlledSide]: widthProp
9210
9222
  };
9211
9223
  return widthStates;
9212
- }, [widthStates, widthProp]);
9213
- React$13.useEffect(() => {
9214
- if (!storageKey) return;
9215
- const toStore = {};
9216
- const allIds = new Set([...Object.keys(openStates), ...Object.keys(widthStates)]);
9217
- for (const id of allIds) toStore[id] = {
9218
- open: openStates[id] ?? defaultOpen,
9219
- ...widthStates[id] === void 0 ? {} : { width: widthStates[id] }
9220
- };
9221
- localStorage.setItem(storageKey, JSON.stringify(toStore));
9222
9224
  }, [
9223
- storageKey,
9224
- openStates,
9225
9225
  widthStates,
9226
- defaultOpen
9226
+ widthProp,
9227
+ controlledSide
9227
9228
  ]);
9228
- const setOpen = React$13.useCallback((id, open) => {
9229
- if (id === "default" && setOpenProp) setOpenProp(open);
9230
- setOpenStates((prev) => ({
9229
+ const setOpen = React$13.useCallback((side, open) => {
9230
+ if (side === controlledSide && setOpenProp) setOpenProp(open);
9231
+ setStoredStates((prev) => ({
9231
9232
  ...prev,
9232
- [id]: open
9233
+ [side]: {
9234
+ ...prev[side] ?? { open: defaultOpen },
9235
+ open
9236
+ }
9233
9237
  }));
9234
- }, [setOpenProp]);
9235
- const setOpenMobile = React$13.useCallback((id, open) => {
9238
+ }, [
9239
+ controlledSide,
9240
+ setOpenProp,
9241
+ setStoredStates,
9242
+ defaultOpen
9243
+ ]);
9244
+ const setOpenMobile = React$13.useCallback((side, open) => {
9236
9245
  setMobileStates((prev) => ({
9237
9246
  ...prev,
9238
- [id]: open
9247
+ [side]: open
9239
9248
  }));
9240
9249
  }, []);
9241
- const setWidth = React$13.useCallback((id, width) => {
9242
- if (id === "default" && onWidthChange) onWidthChange(width);
9243
- setWidthStates((prev) => ({
9250
+ const setWidth = React$13.useCallback((side, width) => {
9251
+ if (side === controlledSide && onWidthChange) onWidthChange(width);
9252
+ setStoredStates((prev) => ({
9244
9253
  ...prev,
9245
- [id]: width
9254
+ [side]: {
9255
+ ...prev[side] ?? { open: defaultOpen },
9256
+ width
9257
+ }
9246
9258
  }));
9247
- }, [onWidthChange]);
9259
+ }, [
9260
+ controlledSide,
9261
+ onWidthChange,
9262
+ setStoredStates,
9263
+ defaultOpen
9264
+ ]);
9248
9265
  const [sidebarRegistry, setSidebarRegistry] = React$13.useState({});
9249
- const registerSidebar = React$13.useCallback((id, config) => {
9266
+ const registerSidebar = React$13.useCallback((side, config) => {
9250
9267
  setSidebarRegistry((prev) => {
9251
- const existing = prev[id];
9252
- if (existing && existing.side === config.side && existing.minWidth === config.minWidth && existing.maxWidth === config.maxWidth) return prev;
9268
+ const existing = prev[side];
9269
+ if (existing && existing.minWidth === config.minWidth && existing.maxWidth === config.maxWidth) return prev;
9253
9270
  return {
9254
9271
  ...prev,
9255
- [id]: config
9272
+ [side]: config
9256
9273
  };
9257
9274
  });
9258
9275
  }, []);
9259
- const unregisterSidebar = React$13.useCallback((id) => {
9276
+ const unregisterSidebar = React$13.useCallback((side) => {
9260
9277
  setSidebarRegistry((prev) => {
9261
- const { [id]: _,...rest } = prev;
9278
+ const { [side]: _,...rest } = prev;
9262
9279
  return rest;
9263
9280
  });
9264
9281
  }, []);
@@ -9266,22 +9283,22 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: set
9266
9283
  effectiveOpenStatesRef.current = effectiveOpenStates;
9267
9284
  const resolvedShortcuts = React$13.useMemo(() => {
9268
9285
  if (keyboardShortcuts === false) return {};
9269
- return keyboardShortcuts ?? { [SIDEBAR_KEYBOARD_SHORTCUT]: "default" };
9286
+ return keyboardShortcuts ?? { [SIDEBAR_KEYBOARD_SHORTCUT]: "left" };
9270
9287
  }, [keyboardShortcuts]);
9271
9288
  React$13.useEffect(() => {
9272
9289
  const entries = Object.entries(resolvedShortcuts);
9273
9290
  if (entries.length === 0) return;
9274
9291
  const handleKeyDown = (event) => {
9275
9292
  if (!(event.metaKey || event.ctrlKey)) return;
9276
- for (const [key, sidebarId] of entries) if (event.key === key) {
9293
+ for (const [key, side] of entries) if (event.key === key) {
9277
9294
  event.preventDefault();
9278
9295
  if (isMobile) setMobileStates((prev) => ({
9279
9296
  ...prev,
9280
- [sidebarId]: !(prev[sidebarId] ?? false)
9297
+ [side]: !(prev[side] ?? false)
9281
9298
  }));
9282
9299
  else {
9283
- const currentOpen = effectiveOpenStatesRef.current[sidebarId] ?? defaultOpen;
9284
- setOpen(sidebarId, !currentOpen);
9300
+ const currentOpen = effectiveOpenStatesRef.current[side] ?? defaultOpen;
9301
+ setOpen(side, !currentOpen);
9285
9302
  }
9286
9303
  return;
9287
9304
  }
@@ -9326,7 +9343,7 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: set
9326
9343
  minWidth,
9327
9344
  maxWidth
9328
9345
  ]);
9329
- const defaultWidth = effectiveWidthStates["default"];
9346
+ const defaultWidth = effectiveWidthStates[controlledSide];
9330
9347
  const sidebarWidthValue = defaultWidth ? `${defaultWidth}px` : SIDEBAR_WIDTH;
9331
9348
  return /* @__PURE__ */ jsx(SidebarContext.Provider, {
9332
9349
  value: storeValue,
@@ -9349,22 +9366,20 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: set
9349
9366
 
9350
9367
  //#endregion
9351
9368
  //#region src/sidebar/sidebar.tsx
9352
- function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas", sidebarId = "default", minWidth, maxWidth, className, children,...props }) {
9353
- const { isMobile, state, openMobile, setOpenMobile, width, isResizing } = useSidebar(sidebarId);
9369
+ function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas", minWidth, maxWidth, className, children,...props }) {
9370
+ const { isMobile, state, openMobile, setOpenMobile, width, isResizing } = useSidebar(side);
9354
9371
  const store = React$12.useContext(SidebarContext);
9355
9372
  const storeRef = React$12.useRef(store);
9356
9373
  storeRef.current = store;
9357
9374
  const effectiveMinWidth = minWidth ?? store?.providerMinWidth ?? SIDEBAR_MIN_WIDTH;
9358
9375
  const effectiveMaxWidth = maxWidth ?? store?.providerMaxWidth ?? SIDEBAR_MAX_WIDTH;
9359
9376
  React$12.useEffect(() => {
9360
- storeRef.current?.registerSidebar(sidebarId, {
9361
- side,
9377
+ storeRef.current?.registerSidebar(side, {
9362
9378
  minWidth: effectiveMinWidth,
9363
9379
  maxWidth: effectiveMaxWidth
9364
9380
  });
9365
- return () => storeRef.current?.unregisterSidebar(sidebarId);
9381
+ return () => storeRef.current?.unregisterSidebar(side);
9366
9382
  }, [
9367
- sidebarId,
9368
9383
  side,
9369
9384
  effectiveMinWidth,
9370
9385
  effectiveMaxWidth
@@ -9374,8 +9389,8 @@ function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas"
9374
9389
  "--sidebar-width": sidebarWidthValue,
9375
9390
  "--sidebar-width-icon": SIDEBAR_WIDTH_ICON
9376
9391
  };
9377
- const wrappedChildren = /* @__PURE__ */ jsx(SidebarIdContext.Provider, {
9378
- value: sidebarId,
9392
+ const wrappedChildren = /* @__PURE__ */ jsx(SidebarSideContext.Provider, {
9393
+ value: side,
9379
9394
  children
9380
9395
  });
9381
9396
  if (collapsible === "none") return /* @__PURE__ */ jsx("div", {
@@ -9436,13 +9451,11 @@ function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas"
9436
9451
  })]
9437
9452
  });
9438
9453
  }
9439
- function SidebarTrigger({ className, onClick, sidebarId, children,...props }) {
9440
- const { toggleSidebar } = useSidebar(sidebarId);
9441
- const store = React$12.useContext(SidebarContext);
9442
- const ctxId = React$12.useContext(SidebarIdContext);
9443
- const resolvedId = sidebarId ?? ctxId;
9444
- const side = store?.sidebarRegistry[resolvedId]?.side;
9445
- const Icon = side === "right" ? PanelRightIcon : PanelLeftIcon;
9454
+ function SidebarTrigger({ className, onClick, side, children,...props }) {
9455
+ const ctxSide = React$12.useContext(SidebarSideContext);
9456
+ const resolvedSide = side ?? ctxSide ?? "left";
9457
+ const { toggleSidebar } = useSidebar(resolvedSide);
9458
+ const Icon = resolvedSide === "right" ? PanelRightIcon : PanelLeftIcon;
9446
9459
  return /* @__PURE__ */ jsx(Button, {
9447
9460
  "data-sidebar": "trigger",
9448
9461
  "data-slot": "sidebar-trigger",
@@ -9465,8 +9478,8 @@ function SidebarRail({ className,...props }) {
9465
9478
  const store = React$12.useContext(SidebarContext);
9466
9479
  const startXRef = React$12.useRef(0);
9467
9480
  const hasDraggedRef = React$12.useRef(false);
9468
- const sidebarId = React$12.useContext(SidebarIdContext);
9469
- const isResizable = !!store?.sidebarRegistry[sidebarId];
9481
+ const side = React$12.useContext(SidebarSideContext);
9482
+ const isResizable = !!(side && store?.sidebarRegistry[side]);
9470
9483
  const handleMouseDown = React$12.useCallback((e) => {
9471
9484
  if (!isResizable) return;
9472
9485
  e.preventDefault();
@@ -9475,11 +9488,11 @@ function SidebarRail({ className,...props }) {
9475
9488
  store?.setIsResizing(true);
9476
9489
  const sidebarEl = e.target.closest("[data-slot=\"sidebar-container\"]");
9477
9490
  const sidebarLeft = sidebarEl?.getBoundingClientRect().left ?? 0;
9478
- const side = e.target.closest("[data-side]")?.getAttribute("data-side");
9491
+ const side$1 = e.target.closest("[data-side]")?.getAttribute("data-side");
9479
9492
  const handleMouseMove = (moveEvent) => {
9480
9493
  if (Math.abs(moveEvent.clientX - startXRef.current) > 3) hasDraggedRef.current = true;
9481
9494
  let newWidth;
9482
- if (side === "right") {
9495
+ if (side$1 === "right") {
9483
9496
  const sidebarRight = sidebarEl?.getBoundingClientRect().right ?? globalThis.innerWidth;
9484
9497
  newWidth = sidebarRight - moveEvent.clientX;
9485
9498
  } else newWidth = moveEvent.clientX - sidebarLeft;
@@ -9741,9 +9754,14 @@ function SidebarMenuSubButton({ asChild = false, size = "md", isActive = false,
9741
9754
  //#endregion
9742
9755
  //#region src/sidebar/sidebar-tab-context-provider.tsx
9743
9756
  const SidebarTabsContext = React$11.createContext(null);
9744
- function SidebarTabsProvider({ children }) {
9757
+ function SidebarTabsProvider({ children, storageKey }) {
9745
9758
  const [tabsMap, setTabsMap] = React$11.useState({});
9746
- const [activeTab, setActiveTabState] = React$11.useState({});
9759
+ const fallbackKey = React$11.useId();
9760
+ const [storedActiveTabs, setStoredActiveTabs] = useLocalStorageState(storageKey ?? fallbackKey, {
9761
+ defaultValue: {},
9762
+ storageSync: !!storageKey
9763
+ });
9764
+ const [activeTab, setActiveTabState] = React$11.useState(storedActiveTabs);
9747
9765
  const register = React$11.useCallback((side, tab) => {
9748
9766
  setTabsMap((prev) => {
9749
9767
  const next = new Map(prev[side]);
@@ -9753,13 +9771,6 @@ function SidebarTabsProvider({ children }) {
9753
9771
  [side]: next
9754
9772
  };
9755
9773
  });
9756
- setActiveTabState((prev) => {
9757
- if (prev[side] === void 0 || prev[side] === null) return {
9758
- ...prev,
9759
- [side]: tab.id
9760
- };
9761
- return prev;
9762
- });
9763
9774
  }, []);
9764
9775
  const unregister = React$11.useCallback((side, tabId) => {
9765
9776
  setTabsMap((prev) => {
@@ -9770,13 +9781,6 @@ function SidebarTabsProvider({ children }) {
9770
9781
  [side]: next
9771
9782
  };
9772
9783
  });
9773
- setActiveTabState((prev) => {
9774
- if (prev[side] === tabId) return {
9775
- ...prev,
9776
- [side]: null
9777
- };
9778
- return prev;
9779
- });
9780
9784
  }, []);
9781
9785
  const setActiveTab = React$11.useCallback((side, tabId) => {
9782
9786
  setActiveTabState((prev) => {
@@ -9793,15 +9797,28 @@ function SidebarTabsProvider({ children }) {
9793
9797
  for (const [side, map] of Object.entries(tabsMap)) result[side] = sort(map);
9794
9798
  return result;
9795
9799
  }, [tabsMap]);
9800
+ const resolvedActiveTab = React$11.useMemo(() => {
9801
+ const result = {};
9802
+ for (const [side, tabs] of Object.entries(sortedTabs)) {
9803
+ const desired = activeTab[side];
9804
+ if (desired && tabs.some((t) => t.id === desired)) result[side] = desired;
9805
+ else if (tabs.length > 0) result[side] = tabs[0].id;
9806
+ else result[side] = null;
9807
+ }
9808
+ return result;
9809
+ }, [sortedTabs, activeTab]);
9810
+ React$11.useEffect(() => {
9811
+ setStoredActiveTabs(resolvedActiveTab);
9812
+ }, [resolvedActiveTab, setStoredActiveTabs]);
9796
9813
  const value = React$11.useMemo(() => ({
9797
9814
  tabs: sortedTabs,
9798
- activeTab,
9815
+ activeTab: resolvedActiveTab,
9799
9816
  register,
9800
9817
  unregister,
9801
9818
  setActiveTab
9802
9819
  }), [
9803
9820
  sortedTabs,
9804
- activeTab,
9821
+ resolvedActiveTab,
9805
9822
  register,
9806
9823
  unregister,
9807
9824
  setActiveTab
@@ -9867,34 +9884,25 @@ function SidebarTab({ side, id, icon, label, render, order, badge }) {
9867
9884
  function DynamicTabbedSidebar({ side, orientation = "horizontal", collapsible = "offcanvas", className,...sidebarProps }) {
9868
9885
  const ctx = React$10.useContext(SidebarTabsContext);
9869
9886
  if (!ctx) throw new Error("DynamicTabbedSidebar must be used within a SidebarTabsProvider.");
9870
- const { setActiveTab } = ctx;
9871
9887
  const tabs = ctx.tabs[side] ?? [];
9872
9888
  const activeTabId = ctx.activeTab[side] ?? null;
9873
9889
  const activeTab = tabs.find((t) => t.id === activeTabId);
9874
- React$10.useEffect(() => {
9875
- if (tabs.length > 0 && tabs[0] && !activeTab) setActiveTab(side, tabs[0].id);
9876
- }, [
9877
- tabs,
9878
- activeTab,
9879
- side,
9880
- setActiveTab
9881
- ]);
9882
9890
  const isVertical = orientation === "vertical";
9883
9891
  const effectiveCollapsible = isVertical && collapsible === "offcanvas" ? "icon" : collapsible;
9884
- return /* @__PURE__ */ jsxs(Sidebar, {
9892
+ if (tabs.length === 0) return null;
9893
+ return /* @__PURE__ */ jsx(Sidebar, {
9885
9894
  side,
9886
- sidebarId: side,
9887
9895
  collapsible: effectiveCollapsible,
9888
9896
  className,
9889
9897
  ...sidebarProps,
9890
- children: [/* @__PURE__ */ jsx(TabbedSidebarContent, {
9898
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(TabbedSidebarContent, {
9891
9899
  side,
9892
9900
  tabs,
9893
9901
  activeTabId,
9894
9902
  activeTab,
9895
9903
  isVertical,
9896
9904
  collapsible: effectiveCollapsible
9897
- }), /* @__PURE__ */ jsx(SidebarRail, {})]
9905
+ }), /* @__PURE__ */ jsx(SidebarRail, {})] })
9898
9906
  });
9899
9907
  }
9900
9908
  function TabbedSidebarContent({ side, tabs, activeTabId, activeTab, isVertical, collapsible }) {
@@ -9915,10 +9923,7 @@ function TabbedSidebarContent({ side, tabs, activeTabId, activeTab, isVertical,
9915
9923
  side,
9916
9924
  tabs,
9917
9925
  activeTabId
9918
- }), /* @__PURE__ */ jsx("div", {
9919
- className: "flex min-w-0 flex-1 flex-col",
9920
- children: /* @__PURE__ */ jsx(SidebarContent, { children: activeTab?.render() })
9921
- })]
9926
+ }), /* @__PURE__ */ jsx(SidebarContent, { children: activeTab?.render() })]
9922
9927
  });
9923
9928
  return /* @__PURE__ */ jsxs("div", {
9924
9929
  className: "flex h-full flex-col",
@@ -9929,10 +9934,7 @@ function TabbedSidebarContent({ side, tabs, activeTabId, activeTab, isVertical,
9929
9934
  activeTabId
9930
9935
  }),
9931
9936
  tabs.length > 0 && /* @__PURE__ */ jsx(SidebarSeparator, {}),
9932
- /* @__PURE__ */ jsx("div", {
9933
- className: "flex min-w-0 flex-1 flex-col",
9934
- children: /* @__PURE__ */ jsx(SidebarContent, { children: activeTab?.render() })
9935
- })
9937
+ /* @__PURE__ */ jsx(SidebarContent, { children: activeTab?.render() })
9936
9938
  ]
9937
9939
  });
9938
9940
  }
@@ -11395,5 +11397,5 @@ const TreeView = React$1.forwardRef(({ data, onNodeSelect, onGroupSelect, onTogg
11395
11397
  TreeView.displayName = "TreeView";
11396
11398
 
11397
11399
  //#endregion
11398
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, Avatar, AvatarFallback, AvatarImage, Badge, KanbanBoard as Board, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Calendar, CalendarDayButton, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, Checkbox, CheckboxCell, Collapse, CollapseContent, CollapseTrigger, KanbanColumn as Column, KanbanColumnHandle as ColumnHandle, ComboboxDemo, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandPalette, CommandPaletteDialog, CommandPaletteEmpty, CommandPaletteGroup, CommandPaletteInput, CommandPaletteItem, CommandPaletteList, CommandPaletteSeparator, CommandPaletteShortcut, CommandSeparator, CommandShortcut, CommentCreate, CommentList, CommentThread, ContentFrame, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DataGrid, DataGridCell, DataGridCellWrapper, DataGridColumnHeader, DataGridContextMenu, DataGridRow, DataGridSearch, DataGridViewMenu, DateCell, DatePickerDemo, DeferredInput, DeferredNumberInput, DeferredTextarea, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, DynamicTabbedSidebar, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, GanttCell, GanttTimeline, GanttTimerangePicker, HeaderComponents, HierarchyCell, HierarchyItem, HoverCard, HoverCardContent, HoverCardTrigger, InfoCard, Input, KanbanItem as Item, KanbanItemHandle as ItemHandle, KanbanRoot as Kanban, KanbanBoard, KanbanColumn, KanbanColumnHandle, KanbanItem, KanbanItemHandle, KanbanOverlay, Label, Loader, LongTextCell, MarkValueRenderer, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, Modal, MultiSelectCell, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NumberCell, NumberInput, KanbanOverlay as Overlay, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, ReactNodeCell, ResizableHandle, ResizablePanel, ResizablePanelGroup, KanbanRoot as Root, SIDEBAR_KEYBOARD_SHORTCUT, SIDEBAR_KEYBOARD_SHORTCUT_RIGHT, SIDEBAR_MAX_WIDTH, SIDEBAR_MIN_WIDTH, SIDEBAR_WIDTH, SIDEBAR_WIDTH_ICON, SIDEBAR_WIDTH_MOBILE, ScrollArea, ScrollBar, Select, SelectCell, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, ShortTextCell, Sidebar, SidebarContent, SidebarContext, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarIdContext, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTab, SidebarTabsContext, SidebarTabsProvider, SidebarTrigger, Skeleton, Slicer, SlicerHierarchyItem, Slider, Spinner, Stepper, StepperContent, StepperDescription, StepperIndicator, StepperItem, StepperList, StepperNext, StepperPrev, StepperSeparator, StepperTitle, StepperTrigger, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TreeBranch, TreeView, badgeVariants, buildLabelMap, buttonVariants, checkboxVariants, cn, commandInputVariants, createSelectColumn, findOptionById, getAllDescendantIds, getAllDescendantValues, getAllIds, getAncestorIds, getCellKey, getCommonPinningStyles, getInitialExpandedIds, getLabelPath, getLineCount, getRowHeightValue, inputVariants, isoToLocalDate, knobVariants, matchesSearch, navigationMenuTriggerStyle, parseCellKey, sliderVariants, testId, toast, toggleVariants, useCallbackRef, useDataGrid, useDebouncedCallback, useIsMobile, useRegisteredSidebars, useSidebar, useSidebarTabs, useStore as useStepper };
11400
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, Avatar, AvatarFallback, AvatarImage, Badge, KanbanBoard as Board, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Calendar, CalendarDayButton, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, Checkbox, CheckboxCell, Collapse, CollapseContent, CollapseTrigger, KanbanColumn as Column, KanbanColumnHandle as ColumnHandle, ComboboxDemo, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandPalette, CommandPaletteDialog, CommandPaletteEmpty, CommandPaletteGroup, CommandPaletteInput, CommandPaletteItem, CommandPaletteList, CommandPaletteSeparator, CommandPaletteShortcut, CommandSeparator, CommandShortcut, CommentCreate, CommentList, CommentThread, ContentFrame, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DataGrid, DataGridCell, DataGridCellWrapper, DataGridColumnHeader, DataGridContextMenu, DataGridRow, DataGridSearch, DataGridViewMenu, DateCell, DatePickerDemo, DeferredInput, DeferredNumberInput, DeferredTextarea, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, DynamicTabbedSidebar, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, GanttCell, GanttTimeline, GanttTimerangePicker, HeaderComponents, HierarchyCell, HierarchyItem, HoverCard, HoverCardContent, HoverCardTrigger, InfoCard, Input, KanbanItem as Item, KanbanItemHandle as ItemHandle, KanbanRoot as Kanban, KanbanBoard, KanbanColumn, KanbanColumnHandle, KanbanItem, KanbanItemHandle, KanbanOverlay, Label, Loader, LongTextCell, MarkValueRenderer, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, Modal, MultiSelectCell, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NumberCell, NumberInput, KanbanOverlay as Overlay, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, ReactNodeCell, ResizableHandle, ResizablePanel, ResizablePanelGroup, KanbanRoot as Root, SIDEBAR_KEYBOARD_SHORTCUT, SIDEBAR_KEYBOARD_SHORTCUT_RIGHT, SIDEBAR_MAX_WIDTH, SIDEBAR_MIN_WIDTH, SIDEBAR_WIDTH, SIDEBAR_WIDTH_ICON, SIDEBAR_WIDTH_MOBILE, ScrollArea, ScrollBar, Select, SelectCell, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, ShortTextCell, Sidebar, SidebarContent, SidebarContext, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarSideContext, SidebarTab, SidebarTabsContext, SidebarTabsProvider, SidebarTrigger, Skeleton, Slicer, SlicerHierarchyItem, Slider, Spinner, Stepper, StepperContent, StepperDescription, StepperIndicator, StepperItem, StepperList, StepperNext, StepperPrev, StepperSeparator, StepperTitle, StepperTrigger, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TreeBranch, TreeView, badgeVariants, buildLabelMap, buttonVariants, checkboxVariants, cn, commandInputVariants, createSelectColumn, findOptionById, getAllDescendantIds, getAllDescendantValues, getAllIds, getAncestorIds, getCellKey, getCommonPinningStyles, getInitialExpandedIds, getLabelPath, getLineCount, getRowHeightValue, inputVariants, isoToLocalDate, knobVariants, matchesSearch, navigationMenuTriggerStyle, parseCellKey, sliderVariants, testId, toast, toggleVariants, useCallbackRef, useDataGrid, useDebouncedCallback, useIsMobile, useRegisteredSidebars, useSidebar, useSidebarTabs, useStore as useStepper };
11399
11401
  //# sourceMappingURL=index.js.map