@ssa-ui-kit/core 3.26.1 → 3.26.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.
@@ -1584,9 +1584,11 @@ The app sidebar: a logo, a menu, and three layouts.
1584
1584
  - `subMenuMaxWidth?: Property.MaxWidth<string | number>` — Max width for submenu panels (e.g. 220).
1585
1585
  - `showIconTooltip?: boolean` — When true, show tooltips on nav icons when sidebar is collapsed.
1586
1586
  - `useMatchPattern?: (prefix: string) => string | PathPattern<string>` — Custom route matching for active state (e.g. for React Router): given a group's `prefix`, return the pattern…
1587
- - `onChange?: (isChecked: boolean) => void` — Called when the sidebar expand/collapse state changes.
1587
+ - `onChange?: (isChecked: boolean) => void` — Called when the sidebar expand/collapse state changes — on user toggles, the collapse on viewport resize, and…
1588
+ - `onToggle?: (isOpen: boolean) => void` — Called when the user opens or closes the panel with its toggle. Unlike `onChange`, it does not fire on mount,…
1588
1589
  - `exactMatch?: boolean` `= false` — If true, only exact path matches will be considered active. If false, sub-routes will also be considered…
1589
1590
  - `defaultExpanded?: boolean` `= false` — Initial expanded state (sidebar shows icon + text). When false, only icons are shown until the user toggles.
1591
+ - `isOpen?: boolean` — Controlled expanded state. When passed, the sidebar renders from it and keeps no state of its own; toggles…
1590
1592
 
1591
1593
  #### CollapsibleNavBarCustomIconSVG
1592
1594
 
@@ -34,5 +34,8 @@ import { CollapsibleNavBarExtendedProps } from './types';
34
34
  * onChange={(isExpanded) => console.log(isExpanded)}
35
35
  * />
36
36
  * ```
37
+ *
38
+ * Pass `isOpen` to own the expanded state (e.g. to persist it): the bar then
39
+ * renders from it and reports toggles through `onChange` for you to apply.
37
40
  */
38
- export declare const CollapsibleNavBar: ({ items, renderLogo, header, theme, activeColor, subMenuMaxWidth, showIconTooltip, className, useMatchPattern, onChange, exactMatch, defaultExpanded, }: CollapsibleNavBarExtendedProps) => import("@emotion/react/jsx-runtime").JSX.Element;
41
+ export declare const CollapsibleNavBar: (props: CollapsibleNavBarExtendedProps) => import("@emotion/react/jsx-runtime").JSX.Element;
@@ -63,8 +63,22 @@ export interface CollapsibleNavBarExtendedProps<T extends NavBarExtendedSubItem
63
63
  * loses a group.
64
64
  */
65
65
  useMatchPattern?: (prefix: string) => string | PathPattern<string>;
66
- /** Called when the sidebar expand/collapse state changes. */
66
+ /**
67
+ * Called when the sidebar expand/collapse state changes — on user toggles,
68
+ * the collapse on viewport resize, and the mobile overlay closing on
69
+ * navigation.
70
+ *
71
+ * Uncontrolled, it also fires once on mount with the initial state.
72
+ * Controlled (`isOpen` passed), it never fires on mount and only reports a
73
+ * value that differs from `isOpen`.
74
+ */
67
75
  onChange?: (isChecked: boolean) => void;
76
+ /**
77
+ * Called when the user opens or closes the panel with its toggle. Unlike
78
+ * `onChange`, it does not fire on mount, on the resize collapse, on mobile
79
+ * navigation, or when `isOpen` changes from outside.
80
+ */
81
+ onToggle?: (isOpen: boolean) => void;
68
82
  /**
69
83
  * If true, only exact path matches will be considered active.
70
84
  * If false, sub-routes will also be considered active.
@@ -76,4 +90,13 @@ export interface CollapsibleNavBarExtendedProps<T extends NavBarExtendedSubItem
76
90
  * @default false
77
91
  */
78
92
  defaultExpanded?: boolean;
93
+ /**
94
+ * Controlled expanded state. When passed, the sidebar renders from it and
95
+ * keeps no state of its own; toggles are reported through `onChange` for the
96
+ * caller to apply. Omit it for the uncontrolled `defaultExpanded` behaviour.
97
+ *
98
+ * The bar still asks to collapse (`onChange(false)`) when the viewport
99
+ * resizes, as it does uncontrolled.
100
+ */
101
+ isOpen?: boolean;
79
102
  }
package/dist/index.js CHANGED
@@ -70721,27 +70721,45 @@ const SCREEN_SIZES = {
70721
70721
  * onChange={(isExpanded) => console.log(isExpanded)}
70722
70722
  * />
70723
70723
  * ```
70724
+ *
70725
+ * Pass `isOpen` to own the expanded state (e.g. to persist it): the bar then
70726
+ * renders from it and reports toggles through `onChange` for you to apply.
70724
70727
  */
70725
70728
 
70726
- const CollapsibleNavBar = ({
70727
- items,
70728
- renderLogo,
70729
- header,
70730
- theme = 'default',
70731
- activeColor,
70732
- subMenuMaxWidth,
70733
- showIconTooltip = false,
70734
- className,
70735
- useMatchPattern,
70736
- onChange,
70737
- exactMatch = false,
70738
- defaultExpanded = false
70739
- }) => {
70729
+ const CollapsibleNavBar = props => {
70730
+ const {
70731
+ items,
70732
+ renderLogo,
70733
+ header,
70734
+ theme = 'default',
70735
+ activeColor,
70736
+ subMenuMaxWidth,
70737
+ showIconTooltip = false,
70738
+ className,
70739
+ useMatchPattern,
70740
+ onChange,
70741
+ onToggle,
70742
+ exactMatch = false,
70743
+ defaultExpanded = false,
70744
+ isOpen
70745
+ } = props;
70740
70746
  const toggleId = (0,external_react_.useId)();
70741
70747
  const {
70742
70748
  width
70743
70749
  } = (0,hooks_namespaceObject.useWindowSize)();
70744
- const [isChecked, onToggle] = (0,external_react_.useState)(defaultExpanded);
70750
+ // Controlled by the prop's presence, per the kit convention — see
70751
+ // `useControllableState`. Uncontrolled mode reports through the effect below
70752
+ // (which also fires on mount, as it always has), so the hook only notifies
70753
+ // when controlled.
70754
+ const controlled = 'isOpen' in props;
70755
+ const [rawChecked, setChecked] = (0,hooks_namespaceObject.useControllableState)({
70756
+ controlled,
70757
+ value: isOpen,
70758
+ defaultValue: defaultExpanded,
70759
+ finalValue: false,
70760
+ onChange: controlled ? onChange : undefined
70761
+ });
70762
+ const isChecked = Boolean(rawChecked);
70745
70763
  const isMobile = width < SCREEN_SIZES['900'].width;
70746
70764
  const treeItems = (0,external_react_.useMemo)(() => toTreeItems(items), [items]);
70747
70765
  const [expandedIds, setExpandedIds] = (0,external_react_.useState)([]);
@@ -70750,21 +70768,24 @@ const CollapsibleNavBar = ({
70750
70768
  const showHeader = hasHeaderContent(header);
70751
70769
 
70752
70770
  // Collapse whenever the viewport changes size — but not on mount, which
70753
- // would immediately undo `defaultExpanded`.
70754
- const hasMeasured = (0,external_react_.useRef)(false);
70771
+ // would immediately undo `defaultExpanded`. Compared against the last width
70772
+ // rather than a run-once flag, so StrictMode's repeated mount run is a no-op.
70773
+ const lastWidth = (0,external_react_.useRef)(width);
70755
70774
  (0,external_react_.useEffect)(() => {
70756
- if (!hasMeasured.current) {
70757
- hasMeasured.current = true;
70775
+ if (lastWidth.current === width) {
70758
70776
  return;
70759
70777
  }
70760
- onToggle(false);
70778
+ lastWidth.current = width;
70779
+ setChecked(false);
70761
70780
  }, [width]);
70762
70781
  (0,external_react_.useEffect)(() => {
70763
- onChange?.(isChecked);
70782
+ if (!controlled) {
70783
+ onChange?.(isChecked);
70784
+ }
70764
70785
  }, [isChecked]);
70765
70786
  const handleCloseMobileMenu = () => {
70766
70787
  if (isMobile) {
70767
- onToggle(!isChecked);
70788
+ setChecked(!isChecked);
70768
70789
  }
70769
70790
  };
70770
70791
  return (0,jsx_runtime_namespaceObject.jsx)(CollapsibleNavBarProvider, {
@@ -70780,7 +70801,8 @@ const CollapsibleNavBar = ({
70780
70801
  id: toggleId,
70781
70802
  checked: isChecked,
70782
70803
  onChange: () => {
70783
- onToggle(!isChecked);
70804
+ setChecked(!isChecked);
70805
+ onToggle?.(!isChecked);
70784
70806
  }
70785
70807
  }), (0,jsx_runtime_namespaceObject.jsx)(CollapsibleNavToggle, {
70786
70808
  id: toggleId
package/dist/index.mjs CHANGED
@@ -70381,27 +70381,45 @@ const SCREEN_SIZES = {
70381
70381
  * onChange={(isExpanded) => console.log(isExpanded)}
70382
70382
  * />
70383
70383
  * ```
70384
+ *
70385
+ * Pass `isOpen` to own the expanded state (e.g. to persist it): the bar then
70386
+ * renders from it and reports toggles through `onChange` for you to apply.
70384
70387
  */
70385
70388
 
70386
- const CollapsibleNavBar = ({
70387
- items,
70388
- renderLogo,
70389
- header,
70390
- theme = 'default',
70391
- activeColor,
70392
- subMenuMaxWidth,
70393
- showIconTooltip = false,
70394
- className,
70395
- useMatchPattern,
70396
- onChange,
70397
- exactMatch = false,
70398
- defaultExpanded = false
70399
- }) => {
70389
+ const CollapsibleNavBar = props => {
70390
+ const {
70391
+ items,
70392
+ renderLogo,
70393
+ header,
70394
+ theme = 'default',
70395
+ activeColor,
70396
+ subMenuMaxWidth,
70397
+ showIconTooltip = false,
70398
+ className,
70399
+ useMatchPattern,
70400
+ onChange,
70401
+ onToggle,
70402
+ exactMatch = false,
70403
+ defaultExpanded = false,
70404
+ isOpen
70405
+ } = props;
70400
70406
  const toggleId = (0,external_react_.useId)();
70401
70407
  const {
70402
70408
  width
70403
70409
  } = __WEBPACK_EXTERNAL_MODULE__ssa_ui_kit_hooks_615bc432_useWindowSize__();
70404
- const [isChecked, onToggle] = (0,external_react_.useState)(defaultExpanded);
70410
+ // Controlled by the prop's presence, per the kit convention — see
70411
+ // `useControllableState`. Uncontrolled mode reports through the effect below
70412
+ // (which also fires on mount, as it always has), so the hook only notifies
70413
+ // when controlled.
70414
+ const controlled = 'isOpen' in props;
70415
+ const [rawChecked, setChecked] = __WEBPACK_EXTERNAL_MODULE__ssa_ui_kit_hooks_615bc432_useControllableState__({
70416
+ controlled,
70417
+ value: isOpen,
70418
+ defaultValue: defaultExpanded,
70419
+ finalValue: false,
70420
+ onChange: controlled ? onChange : undefined
70421
+ });
70422
+ const isChecked = Boolean(rawChecked);
70405
70423
  const isMobile = width < SCREEN_SIZES['900'].width;
70406
70424
  const treeItems = (0,external_react_.useMemo)(() => toTreeItems(items), [items]);
70407
70425
  const [expandedIds, setExpandedIds] = (0,external_react_.useState)([]);
@@ -70410,21 +70428,24 @@ const CollapsibleNavBar = ({
70410
70428
  const showHeader = hasHeaderContent(header);
70411
70429
 
70412
70430
  // Collapse whenever the viewport changes size — but not on mount, which
70413
- // would immediately undo `defaultExpanded`.
70414
- const hasMeasured = (0,external_react_.useRef)(false);
70431
+ // would immediately undo `defaultExpanded`. Compared against the last width
70432
+ // rather than a run-once flag, so StrictMode's repeated mount run is a no-op.
70433
+ const lastWidth = (0,external_react_.useRef)(width);
70415
70434
  (0,external_react_.useEffect)(() => {
70416
- if (!hasMeasured.current) {
70417
- hasMeasured.current = true;
70435
+ if (lastWidth.current === width) {
70418
70436
  return;
70419
70437
  }
70420
- onToggle(false);
70438
+ lastWidth.current = width;
70439
+ setChecked(false);
70421
70440
  }, [width]);
70422
70441
  (0,external_react_.useEffect)(() => {
70423
- onChange?.(isChecked);
70442
+ if (!controlled) {
70443
+ onChange?.(isChecked);
70444
+ }
70424
70445
  }, [isChecked]);
70425
70446
  const handleCloseMobileMenu = () => {
70426
70447
  if (isMobile) {
70427
- onToggle(!isChecked);
70448
+ setChecked(!isChecked);
70428
70449
  }
70429
70450
  };
70430
70451
  return __WEBPACK_EXTERNAL_MODULE__emotion_react_jsx_runtime_ea06233c_jsx__(CollapsibleNavBarProvider, {
@@ -70440,7 +70461,8 @@ const CollapsibleNavBar = ({
70440
70461
  id: toggleId,
70441
70462
  checked: isChecked,
70442
70463
  onChange: () => {
70443
- onToggle(!isChecked);
70464
+ setChecked(!isChecked);
70465
+ onToggle?.(!isChecked);
70444
70466
  }
70445
70467
  }), __WEBPACK_EXTERNAL_MODULE__emotion_react_jsx_runtime_ea06233c_jsx__(CollapsibleNavToggle, {
70446
70468
  id: toggleId