@geomak/ui 2.0.0 → 4.0.0

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.d.cts CHANGED
@@ -928,34 +928,87 @@ interface ContextMenuPosition {
928
928
  }
929
929
 
930
930
  interface WizardStep {
931
- /** Ref to the DOM element to highlight */
932
- stepRef: React$1.RefObject<HTMLElement>;
931
+ /** Ref to the DOM element to highlight for this step. */
932
+ stepRef: React$1.RefObject<HTMLElement | null>;
933
+ /** Tooltip body content. */
933
934
  description: React$1.ReactNode;
934
- /** 'natural' | 'center' — controls tooltip position relative to target */
935
- positioning?: 'natural' | 'center';
935
+ /**
936
+ * Tooltip placement relative to the highlighted element.
937
+ * - `'right'` (default) — to the right of the highlight
938
+ * - `'left'` — to the left of the highlight
939
+ * - `'top'` — above the highlight
940
+ * - `'bottom'` — below the highlight
941
+ */
942
+ placement?: 'right' | 'left' | 'top' | 'bottom';
943
+ /** Optional heading for the step's tooltip. */
944
+ title?: React$1.ReactNode;
936
945
  }
937
946
  interface WizardProps {
947
+ /** The wrapped subtree — refs in `steps` point into this tree. */
938
948
  children: React$1.ReactNode;
949
+ /** Ordered list of steps to walk the user through. */
939
950
  steps: WizardStep[];
940
- /** localStorage key used to remember dismissal (default: 'oxygen_wizard') */
941
- storageKey?: string;
951
+ /**
952
+ * `localStorage` key used to remember dismissal. When the user clicks
953
+ * "Done" (or "Skip" when dismissible), this key is set so the wizard
954
+ * won't reopen on subsequent mounts.
955
+ *
956
+ * Pass `null` to disable persistence entirely (useful for tests or
957
+ * tours that should always run).
958
+ *
959
+ * Default: `'oxygen.wizard.completed'`.
960
+ */
961
+ storageKey?: string | null;
962
+ /** Show a "Skip tour" button + Esc-to-dismiss. Default `true`. */
963
+ dismissible?: boolean;
964
+ /** Called when the user reaches the final step's "Done" button. */
965
+ onComplete?: () => void;
966
+ /** Called when the user skips (or presses Esc) before completing. */
967
+ onSkip?: () => void;
942
968
  }
943
969
  /**
944
- * Guided-tour overlay wizard.
970
+ * Guided-tour overlay that walks the user through a sequence of UI elements.
945
971
  *
946
- * Highlights a DOM element via a border, then shows a floating tooltip
947
- * adjacent to it. Remembers dismissal via localStorage.
972
+ * Highlights each step's target element with an outlined "spotlight", shows a
973
+ * tooltip with the description, and provides Prev / Next / Done navigation.
974
+ * The wrapped tree is rendered unchanged; the overlay sits on top of it via a
975
+ * portal so it always covers the real viewport regardless of where Wizard
976
+ * lives in the React tree.
977
+ *
978
+ * **What's improved over the previous version**
979
+ * - `localStorage` access is SSR-safe and try/catch-guarded (Safari private
980
+ * mode, quota exceeded, no-storage browsers all degrade silently).
981
+ * - No more `classList.add(...)` on consumer-owned DOM. The spotlight is a
982
+ * portaled outline rectangle that tracks the target's bbox via
983
+ * `ResizeObserver` + scroll/resize listeners. The consumer's DOM is never
984
+ * mutated, so unmounting Wizard mid-tour leaves no orphan classes.
985
+ * - Focus trap inside the tooltip — Tab and Shift+Tab cycle through the
986
+ * tooltip's buttons. Esc dismisses (when `dismissible`).
987
+ * - Backdrop blocks click-through on the rest of the page so the user can't
988
+ * stumble into unrelated UI mid-tour. The highlighted target itself stays
989
+ * click-blocked too (use the "Next" button to advance).
990
+ * - Position recalculates on scroll / resize / target size changes via
991
+ * `ResizeObserver`.
992
+ * - All `dark-cornflower-blue` / `prussian-blue` / `bg-white` swapped for
993
+ * semantic tokens — both light and dark modes work out of the box.
948
994
  *
949
995
  * @example
950
- * const step1Ref = useRef<HTMLDivElement>(null)
951
- * const steps = [
952
- * { stepRef: step1Ref, description: 'Click here to start.', positioning: 'natural' },
953
- * ]
954
- * <Wizard steps={steps}>
955
- * <div ref={step1Ref}>...</div>
996
+ * ```tsx
997
+ * const dashRef = useRef<HTMLDivElement>(null)
998
+ * const sidebarRef = useRef<HTMLElement>(null)
999
+ *
1000
+ * <Wizard
1001
+ * steps={[
1002
+ * { stepRef: dashRef, title: 'Dashboard', description: 'Your fleet at a glance.' },
1003
+ * { stepRef: sidebarRef, title: 'Navigation', description: 'Jump between sections here.', placement: 'right' },
1004
+ * ]}
1005
+ * onComplete={() => track('onboarding_complete')}
1006
+ * >
1007
+ * <Layout dashRef={dashRef} sidebarRef={sidebarRef} />
956
1008
  * </Wizard>
1009
+ * ```
957
1010
  */
958
- declare function Wizard({ children, steps, storageKey }: WizardProps): react_jsx_runtime.JSX.Element;
1011
+ declare function Wizard({ children, steps, storageKey, dismissible, onComplete, onSkip, }: WizardProps): react_jsx_runtime.JSX.Element;
959
1012
 
960
1013
  /** ─────────────────── types ─────────────────── */
961
1014
  /**
@@ -1759,40 +1812,84 @@ interface AutoCompleteProps {
1759
1812
  */
1760
1813
  declare function AutoComplete({ disabled, label, placeholder, name, inputStyle, style, layout, items, onItemClick, emptyText, }: AutoCompleteProps): react_jsx_runtime.JSX.Element;
1761
1814
 
1762
- interface TreeSelectItem {
1815
+ interface TreeSelectNode {
1763
1816
  key: string | number;
1764
1817
  label: React$1.ReactNode;
1765
1818
  icon?: React$1.ReactNode;
1819
+ /** Nested children. If present, this node is treated as a parent (branch). */
1820
+ children?: TreeSelectNode[];
1821
+ /** Render the node disabled — visible but not selectable. */
1822
+ disabled?: boolean;
1766
1823
  }
1767
1824
  interface TreeSelectProps {
1768
- hasSearch?: boolean;
1769
- label?: React$1.ReactNode;
1770
- name?: string;
1771
- value?: any;
1825
+ /** Tree data. Each node may optionally carry `children` for sub-branches. */
1826
+ items: TreeSelectNode[];
1827
+ /** Currently selected key (single-select). Pass `undefined`/`null` for unset. */
1828
+ value?: string | number | null;
1829
+ /** Fires with the next selected key (and the corresponding event-like target). */
1772
1830
  onChange?: (e: {
1773
1831
  target: {
1774
- value: any;
1832
+ value: string | number;
1775
1833
  id?: string;
1776
1834
  name?: string;
1777
1835
  };
1778
1836
  }) => void;
1779
- onBlur?: React$1.FocusEventHandler;
1837
+ label?: React$1.ReactNode;
1838
+ placeholder?: string;
1839
+ /** Form control id linkage. */
1840
+ htmlFor?: string;
1841
+ name?: string;
1842
+ /** Label/trigger orientation. Defaults to `'horizontal'`. */
1843
+ layout?: 'horizontal' | 'vertical';
1780
1844
  disabled?: boolean;
1781
- /** 'horizontal' | 'vertical' */
1782
- layout?: string;
1783
1845
  errorMessage?: React$1.ReactNode;
1784
1846
  style?: React$1.CSSProperties;
1785
- htmlFor?: string;
1786
- items?: TreeSelectItem[];
1847
+ /**
1848
+ * Whether parent (branch) nodes can be selected. When `false`, parents
1849
+ * only expand/collapse and only leaves are picked. Default `true`.
1850
+ */
1851
+ parentsSelectable?: boolean;
1852
+ /** Keys of nodes that should start expanded. */
1853
+ defaultExpandedKeys?: (string | number)[];
1787
1854
  }
1788
1855
  /**
1789
- * Single-value select with a flat list, powered by Radix Popover.
1790
- * Functionally similar to Dropdown (single-select only).
1856
+ * Hierarchical single-select with expandable branches.
1857
+ *
1858
+ * Built on `@radix-ui/react-popover` for portal positioning and click-outside.
1859
+ * Tree state (expanded keys) is local; selected value is controlled by the
1860
+ * parent.
1861
+ *
1862
+ * **Keyboard model** (focus is on the trigger or any item):
1863
+ * - `Enter` / `Space` / `↓` / `↑` on the trigger — open the popover
1864
+ * - `↓` / `↑` — move active item through the visible (un-collapsed) list
1865
+ * - `→` — expand a branch (or move into it if already expanded)
1866
+ * - `←` — collapse a branch (or move to its parent if already collapsed)
1867
+ * - `Enter` / `Space` — select the active item (if selectable)
1868
+ * - `Esc` — close the popover, return focus to the trigger
1791
1869
  *
1792
1870
  * @example
1793
- * <TreeSelect label="Fleet" items={fleets} value={form.fleet} onChange={handleChange} htmlFor="fleet" />
1871
+ * ```tsx
1872
+ * type FleetKey = number
1873
+ * const [fleet, setFleet] = useState<FleetKey | null>(null)
1874
+ *
1875
+ * const tree: TreeSelectNode[] = [
1876
+ * { key: 'eu', label: 'Europe', children: [
1877
+ * { key: 1, label: 'Aegean Fleet' },
1878
+ * { key: 2, label: 'Adriatic Fleet' },
1879
+ * ]},
1880
+ * { key: 'asia', label: 'Asia', children: [{ key: 3, label: 'Pacific Fleet' }] },
1881
+ * ]
1882
+ *
1883
+ * <TreeSelect
1884
+ * label="Fleet"
1885
+ * items={tree}
1886
+ * value={fleet}
1887
+ * onChange={({ target }) => setFleet(target.value as FleetKey)}
1888
+ * parentsSelectable={false}
1889
+ * />
1890
+ * ```
1794
1891
  */
1795
- declare function TreeSelect({ label, name, value, onChange, disabled, layout, errorMessage, style, htmlFor, items, }: TreeSelectProps): react_jsx_runtime.JSX.Element;
1892
+ declare function TreeSelect({ label, name, value, onChange, disabled, layout, errorMessage, style, htmlFor, items, placeholder, parentsSelectable, defaultExpandedKeys, }: TreeSelectProps): react_jsx_runtime.JSX.Element;
1796
1893
 
1797
1894
  interface FileInputProps {
1798
1895
  allowMultiple?: boolean;
package/dist/index.d.ts CHANGED
@@ -928,34 +928,87 @@ interface ContextMenuPosition {
928
928
  }
929
929
 
930
930
  interface WizardStep {
931
- /** Ref to the DOM element to highlight */
932
- stepRef: React$1.RefObject<HTMLElement>;
931
+ /** Ref to the DOM element to highlight for this step. */
932
+ stepRef: React$1.RefObject<HTMLElement | null>;
933
+ /** Tooltip body content. */
933
934
  description: React$1.ReactNode;
934
- /** 'natural' | 'center' — controls tooltip position relative to target */
935
- positioning?: 'natural' | 'center';
935
+ /**
936
+ * Tooltip placement relative to the highlighted element.
937
+ * - `'right'` (default) — to the right of the highlight
938
+ * - `'left'` — to the left of the highlight
939
+ * - `'top'` — above the highlight
940
+ * - `'bottom'` — below the highlight
941
+ */
942
+ placement?: 'right' | 'left' | 'top' | 'bottom';
943
+ /** Optional heading for the step's tooltip. */
944
+ title?: React$1.ReactNode;
936
945
  }
937
946
  interface WizardProps {
947
+ /** The wrapped subtree — refs in `steps` point into this tree. */
938
948
  children: React$1.ReactNode;
949
+ /** Ordered list of steps to walk the user through. */
939
950
  steps: WizardStep[];
940
- /** localStorage key used to remember dismissal (default: 'oxygen_wizard') */
941
- storageKey?: string;
951
+ /**
952
+ * `localStorage` key used to remember dismissal. When the user clicks
953
+ * "Done" (or "Skip" when dismissible), this key is set so the wizard
954
+ * won't reopen on subsequent mounts.
955
+ *
956
+ * Pass `null` to disable persistence entirely (useful for tests or
957
+ * tours that should always run).
958
+ *
959
+ * Default: `'oxygen.wizard.completed'`.
960
+ */
961
+ storageKey?: string | null;
962
+ /** Show a "Skip tour" button + Esc-to-dismiss. Default `true`. */
963
+ dismissible?: boolean;
964
+ /** Called when the user reaches the final step's "Done" button. */
965
+ onComplete?: () => void;
966
+ /** Called when the user skips (or presses Esc) before completing. */
967
+ onSkip?: () => void;
942
968
  }
943
969
  /**
944
- * Guided-tour overlay wizard.
970
+ * Guided-tour overlay that walks the user through a sequence of UI elements.
945
971
  *
946
- * Highlights a DOM element via a border, then shows a floating tooltip
947
- * adjacent to it. Remembers dismissal via localStorage.
972
+ * Highlights each step's target element with an outlined "spotlight", shows a
973
+ * tooltip with the description, and provides Prev / Next / Done navigation.
974
+ * The wrapped tree is rendered unchanged; the overlay sits on top of it via a
975
+ * portal so it always covers the real viewport regardless of where Wizard
976
+ * lives in the React tree.
977
+ *
978
+ * **What's improved over the previous version**
979
+ * - `localStorage` access is SSR-safe and try/catch-guarded (Safari private
980
+ * mode, quota exceeded, no-storage browsers all degrade silently).
981
+ * - No more `classList.add(...)` on consumer-owned DOM. The spotlight is a
982
+ * portaled outline rectangle that tracks the target's bbox via
983
+ * `ResizeObserver` + scroll/resize listeners. The consumer's DOM is never
984
+ * mutated, so unmounting Wizard mid-tour leaves no orphan classes.
985
+ * - Focus trap inside the tooltip — Tab and Shift+Tab cycle through the
986
+ * tooltip's buttons. Esc dismisses (when `dismissible`).
987
+ * - Backdrop blocks click-through on the rest of the page so the user can't
988
+ * stumble into unrelated UI mid-tour. The highlighted target itself stays
989
+ * click-blocked too (use the "Next" button to advance).
990
+ * - Position recalculates on scroll / resize / target size changes via
991
+ * `ResizeObserver`.
992
+ * - All `dark-cornflower-blue` / `prussian-blue` / `bg-white` swapped for
993
+ * semantic tokens — both light and dark modes work out of the box.
948
994
  *
949
995
  * @example
950
- * const step1Ref = useRef<HTMLDivElement>(null)
951
- * const steps = [
952
- * { stepRef: step1Ref, description: 'Click here to start.', positioning: 'natural' },
953
- * ]
954
- * <Wizard steps={steps}>
955
- * <div ref={step1Ref}>...</div>
996
+ * ```tsx
997
+ * const dashRef = useRef<HTMLDivElement>(null)
998
+ * const sidebarRef = useRef<HTMLElement>(null)
999
+ *
1000
+ * <Wizard
1001
+ * steps={[
1002
+ * { stepRef: dashRef, title: 'Dashboard', description: 'Your fleet at a glance.' },
1003
+ * { stepRef: sidebarRef, title: 'Navigation', description: 'Jump between sections here.', placement: 'right' },
1004
+ * ]}
1005
+ * onComplete={() => track('onboarding_complete')}
1006
+ * >
1007
+ * <Layout dashRef={dashRef} sidebarRef={sidebarRef} />
956
1008
  * </Wizard>
1009
+ * ```
957
1010
  */
958
- declare function Wizard({ children, steps, storageKey }: WizardProps): react_jsx_runtime.JSX.Element;
1011
+ declare function Wizard({ children, steps, storageKey, dismissible, onComplete, onSkip, }: WizardProps): react_jsx_runtime.JSX.Element;
959
1012
 
960
1013
  /** ─────────────────── types ─────────────────── */
961
1014
  /**
@@ -1759,40 +1812,84 @@ interface AutoCompleteProps {
1759
1812
  */
1760
1813
  declare function AutoComplete({ disabled, label, placeholder, name, inputStyle, style, layout, items, onItemClick, emptyText, }: AutoCompleteProps): react_jsx_runtime.JSX.Element;
1761
1814
 
1762
- interface TreeSelectItem {
1815
+ interface TreeSelectNode {
1763
1816
  key: string | number;
1764
1817
  label: React$1.ReactNode;
1765
1818
  icon?: React$1.ReactNode;
1819
+ /** Nested children. If present, this node is treated as a parent (branch). */
1820
+ children?: TreeSelectNode[];
1821
+ /** Render the node disabled — visible but not selectable. */
1822
+ disabled?: boolean;
1766
1823
  }
1767
1824
  interface TreeSelectProps {
1768
- hasSearch?: boolean;
1769
- label?: React$1.ReactNode;
1770
- name?: string;
1771
- value?: any;
1825
+ /** Tree data. Each node may optionally carry `children` for sub-branches. */
1826
+ items: TreeSelectNode[];
1827
+ /** Currently selected key (single-select). Pass `undefined`/`null` for unset. */
1828
+ value?: string | number | null;
1829
+ /** Fires with the next selected key (and the corresponding event-like target). */
1772
1830
  onChange?: (e: {
1773
1831
  target: {
1774
- value: any;
1832
+ value: string | number;
1775
1833
  id?: string;
1776
1834
  name?: string;
1777
1835
  };
1778
1836
  }) => void;
1779
- onBlur?: React$1.FocusEventHandler;
1837
+ label?: React$1.ReactNode;
1838
+ placeholder?: string;
1839
+ /** Form control id linkage. */
1840
+ htmlFor?: string;
1841
+ name?: string;
1842
+ /** Label/trigger orientation. Defaults to `'horizontal'`. */
1843
+ layout?: 'horizontal' | 'vertical';
1780
1844
  disabled?: boolean;
1781
- /** 'horizontal' | 'vertical' */
1782
- layout?: string;
1783
1845
  errorMessage?: React$1.ReactNode;
1784
1846
  style?: React$1.CSSProperties;
1785
- htmlFor?: string;
1786
- items?: TreeSelectItem[];
1847
+ /**
1848
+ * Whether parent (branch) nodes can be selected. When `false`, parents
1849
+ * only expand/collapse and only leaves are picked. Default `true`.
1850
+ */
1851
+ parentsSelectable?: boolean;
1852
+ /** Keys of nodes that should start expanded. */
1853
+ defaultExpandedKeys?: (string | number)[];
1787
1854
  }
1788
1855
  /**
1789
- * Single-value select with a flat list, powered by Radix Popover.
1790
- * Functionally similar to Dropdown (single-select only).
1856
+ * Hierarchical single-select with expandable branches.
1857
+ *
1858
+ * Built on `@radix-ui/react-popover` for portal positioning and click-outside.
1859
+ * Tree state (expanded keys) is local; selected value is controlled by the
1860
+ * parent.
1861
+ *
1862
+ * **Keyboard model** (focus is on the trigger or any item):
1863
+ * - `Enter` / `Space` / `↓` / `↑` on the trigger — open the popover
1864
+ * - `↓` / `↑` — move active item through the visible (un-collapsed) list
1865
+ * - `→` — expand a branch (or move into it if already expanded)
1866
+ * - `←` — collapse a branch (or move to its parent if already collapsed)
1867
+ * - `Enter` / `Space` — select the active item (if selectable)
1868
+ * - `Esc` — close the popover, return focus to the trigger
1791
1869
  *
1792
1870
  * @example
1793
- * <TreeSelect label="Fleet" items={fleets} value={form.fleet} onChange={handleChange} htmlFor="fleet" />
1871
+ * ```tsx
1872
+ * type FleetKey = number
1873
+ * const [fleet, setFleet] = useState<FleetKey | null>(null)
1874
+ *
1875
+ * const tree: TreeSelectNode[] = [
1876
+ * { key: 'eu', label: 'Europe', children: [
1877
+ * { key: 1, label: 'Aegean Fleet' },
1878
+ * { key: 2, label: 'Adriatic Fleet' },
1879
+ * ]},
1880
+ * { key: 'asia', label: 'Asia', children: [{ key: 3, label: 'Pacific Fleet' }] },
1881
+ * ]
1882
+ *
1883
+ * <TreeSelect
1884
+ * label="Fleet"
1885
+ * items={tree}
1886
+ * value={fleet}
1887
+ * onChange={({ target }) => setFleet(target.value as FleetKey)}
1888
+ * parentsSelectable={false}
1889
+ * />
1890
+ * ```
1794
1891
  */
1795
- declare function TreeSelect({ label, name, value, onChange, disabled, layout, errorMessage, style, htmlFor, items, }: TreeSelectProps): react_jsx_runtime.JSX.Element;
1892
+ declare function TreeSelect({ label, name, value, onChange, disabled, layout, errorMessage, style, htmlFor, items, placeholder, parentsSelectable, defaultExpandedKeys, }: TreeSelectProps): react_jsx_runtime.JSX.Element;
1796
1893
 
1797
1894
  interface FileInputProps {
1798
1895
  allowMultiple?: boolean;