@enonic/ui 0.15.0 → 0.16.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.
@@ -0,0 +1,346 @@
1
+ import { ComponentPropsWithoutRef, ReactElement, ReactNode } from 'react';
2
+ /**
3
+ * Root component that provides context for the Menubar.
4
+ *
5
+ * Implements the ARIA menubar pattern for horizontal navigation.
6
+ * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/menubar/}
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <Menubar.Root>
11
+ * <Menubar.Content aria-label="Main navigation">
12
+ * <Menubar.Button>New</Menubar.Button>
13
+ * <Menubar.Button>Save</Menubar.Button>
14
+ * </Menubar.Content>
15
+ * </Menubar.Root>
16
+ * ```
17
+ */
18
+ export type MenubarRootProps = {
19
+ /** Default active item ID on initial render */
20
+ defaultActive?: string;
21
+ /** Callback when active item changes */
22
+ onActiveChange?: (active: string | undefined) => void;
23
+ /** Base ID for generating unique IDs */
24
+ id?: string;
25
+ children?: ReactNode;
26
+ };
27
+ /**
28
+ * Container component that renders the menubar element with keyboard navigation.
29
+ *
30
+ * Implements horizontal arrow key navigation (ArrowLeft/Right), Home/End keys,
31
+ * and Tab key to exit the menubar.
32
+ *
33
+ * This is exported as part of the Menubar API but used for the horizontal menubar container,
34
+ * not the dropdown menu content (which is also named MenubarContent but context-aware).
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * <MenubarNav aria-label="Main menu" loop>
39
+ * <Menubar.Button>File</Menubar.Button>
40
+ * <Menubar.Button>Edit</Menubar.Button>
41
+ * </MenubarNav>
42
+ * ```
43
+ */
44
+ export type MenubarNavProps = {
45
+ /** Accessible label for the menubar (required for screen readers) */
46
+ 'aria-label': string;
47
+ /** Whether navigation should loop from last to first item */
48
+ loop?: boolean;
49
+ className?: string;
50
+ children?: ReactNode;
51
+ } & ComponentPropsWithoutRef<'div'>;
52
+ /**
53
+ * A button item within the menubar.
54
+ *
55
+ * Implements ARIA menuitem role and integrates with menubar keyboard navigation.
56
+ * Supports active state animations via Tailwind transitions.
57
+ *
58
+ * @example
59
+ * ```tsx
60
+ * <Menubar.Button onSelect={() => console.log('clicked')}>
61
+ * Save
62
+ * </Menubar.Button>
63
+ *
64
+ * <Menubar.Button disabled>Export</Menubar.Button>
65
+ *
66
+ * <Menubar.Button asChild>
67
+ * <a href="/new">New</a>
68
+ * </Menubar.Button>
69
+ * ```
70
+ */
71
+ export type MenubarButtonProps = {
72
+ /** Unique ID for this button (auto-generated if not provided) */
73
+ id?: string;
74
+ /** Whether the button is disabled */
75
+ disabled?: boolean;
76
+ /** Callback when the button is selected (clicked or activated via keyboard) */
77
+ onSelect?: (event: Event) => void;
78
+ /** Render as a child element using Radix UI Slot */
79
+ asChild?: boolean;
80
+ className?: string;
81
+ children: ReactNode;
82
+ } & Omit<ComponentPropsWithoutRef<'button'>, 'id' | 'children'>;
83
+ /**
84
+ * A visual separator component that adapts based on context.
85
+ *
86
+ * - Within Menubar.Content (menubar): Renders as vertical separator between buttons
87
+ * - Within Menubar.Menu Content (dropdown): Renders as horizontal separator between items
88
+ *
89
+ * Does not participate in keyboard navigation (purely decorative).
90
+ *
91
+ * @example
92
+ * ```tsx
93
+ * // Vertical separator between menubar buttons
94
+ * <Menubar.Content aria-label="Actions">
95
+ * <Menubar.Button>File</Menubar.Button>
96
+ * <Menubar.Separator />
97
+ * <Menubar.Button>Edit</Menubar.Button>
98
+ * </Menubar.Content>
99
+ *
100
+ * // Horizontal separator between menu items
101
+ * <Menubar.Menu>
102
+ * <Menubar.Trigger>File</Menubar.Trigger>
103
+ * <Menubar.Portal>
104
+ * <Menubar.Content>
105
+ * <Menubar.Item>New</Menubar.Item>
106
+ * <Menubar.Separator />
107
+ * <Menubar.Item>Exit</Menubar.Item>
108
+ * </Menubar.Content>
109
+ * </Menubar.Portal>
110
+ * </Menubar.Menu>
111
+ * ```
112
+ */
113
+ export type MenubarSeparatorProps = {
114
+ className?: string;
115
+ } & ComponentPropsWithoutRef<'div'>;
116
+ /**
117
+ * Wrapper component that integrates dropdown menus within a Menubar.
118
+ *
119
+ * Coordinates between the menubar's horizontal navigation and the menu's
120
+ * vertical navigation, implementing the ARIA menubar pattern with submenus.
121
+ *
122
+ * Features:
123
+ * - Registers menu trigger as a menubar item for keyboard navigation
124
+ * - ArrowDown opens the menu from the menubar
125
+ * - ArrowLeft/Right navigate between menus (closing current, opening adjacent)
126
+ * - Automatic conditional hover (hover opens menu when another menu is open)
127
+ * - Tab closes menu and exits menubar entirely
128
+ *
129
+ * @example
130
+ * ```tsx
131
+ * <Menubar.Menu>
132
+ * <Menubar.Trigger>File</Menubar.Trigger>
133
+ * <Menubar.Portal>
134
+ * <Menubar.Content>
135
+ * <Menubar.Item>New</Menubar.Item>
136
+ * <Menubar.Item>Open</Menubar.Item>
137
+ * </Menubar.Content>
138
+ * </Menubar.Portal>
139
+ * </Menubar.Menu>
140
+ * ```
141
+ */
142
+ export type MenubarMenuProps = {
143
+ /** Unique ID for this menu within the menubar */
144
+ id?: string;
145
+ /** Whether the menu is disabled (cannot be opened) */
146
+ disabled?: boolean;
147
+ children?: ReactNode;
148
+ };
149
+ /**
150
+ * Trigger button for a menubar dropdown menu.
151
+ *
152
+ * Opens the menu on click, Enter, Space, or ArrowDown.
153
+ * Implements conditional hover: when another menu is open, hovering this
154
+ * trigger automatically opens its menu and closes the other.
155
+ *
156
+ * Must be used within Menubar.Menu.
157
+ *
158
+ * @example
159
+ * ```tsx
160
+ * <Menubar.Menu>
161
+ * <Menubar.Trigger>File</Menubar.Trigger>
162
+ * <Menubar.Portal>
163
+ * <Menubar.Content>...</Menubar.Content>
164
+ * </Menubar.Portal>
165
+ * </Menubar.Menu>
166
+ *
167
+ * <Menubar.Menu>
168
+ * <Menubar.Trigger asChild>
169
+ * <Button variant="text">Edit</Button>
170
+ * </Menubar.Trigger>
171
+ * <Menubar.Portal>
172
+ * <Menubar.Content>...</Menubar.Content>
173
+ * </Menubar.Portal>
174
+ * </Menubar.Menu>
175
+ * ```
176
+ */
177
+ export type MenubarTriggerProps = {
178
+ /** Render as a child element using Radix UI Slot */
179
+ asChild?: boolean;
180
+ className?: string;
181
+ children: ReactNode;
182
+ } & Omit<ComponentPropsWithoutRef<'button'>, 'children'>;
183
+ /**
184
+ * Portal component that renders menubar dropdown content to document.body.
185
+ *
186
+ * Prevents z-index stacking issues by rendering outside the DOM hierarchy.
187
+ * Only renders when the menu is open unless `forceMount` is true.
188
+ *
189
+ * Must be used within Menubar.Menu.
190
+ *
191
+ * @example
192
+ * ```tsx
193
+ * <Menubar.Menu>
194
+ * <Menubar.Trigger>File</Menubar.Trigger>
195
+ * <Menubar.Portal>
196
+ * <Menubar.Content>
197
+ * <Menubar.Item>New</Menubar.Item>
198
+ * </Menubar.Content>
199
+ * </Menubar.Portal>
200
+ * </Menubar.Menu>
201
+ * ```
202
+ */
203
+ export type MenubarPortalProps = {
204
+ /** Custom container element (defaults to document.body) */
205
+ container?: HTMLElement | null;
206
+ /** Keep content mounted in DOM even when menu is closed */
207
+ forceMount?: boolean;
208
+ children?: ReactNode;
209
+ };
210
+ /**
211
+ * Dropdown content container for a menubar menu.
212
+ *
213
+ * Positioned below the trigger with automatic viewport collision detection.
214
+ * Implements vertical keyboard navigation (ArrowUp/Down) within the menu,
215
+ * plus special ArrowLeft/Right handling to navigate between menus.
216
+ *
217
+ * Must be used within Menubar.Portal and Menubar.Menu.
218
+ *
219
+ * @example
220
+ * ```tsx
221
+ * <Menubar.Menu>
222
+ * <Menubar.Trigger>File</Menubar.Trigger>
223
+ * <Menubar.Portal>
224
+ * <Menubar.Content align="start" loop>
225
+ * <Menubar.Item>New File</Menubar.Item>
226
+ * <Menubar.Item>Open File</Menubar.Item>
227
+ * <Menubar.Separator />
228
+ * <Menubar.Item>Exit</Menubar.Item>
229
+ * </Menubar.Content>
230
+ * </Menubar.Portal>
231
+ * </Menubar.Menu>
232
+ * ```
233
+ */
234
+ export type MenubarContentProps = {
235
+ /** Horizontal alignment relative to trigger */
236
+ align?: 'start' | 'end';
237
+ /** Whether navigation should loop from last to first item */
238
+ loop?: boolean;
239
+ /** Keep content mounted in DOM even when menu is closed */
240
+ forceMount?: boolean;
241
+ /** Callback when Escape key is pressed */
242
+ onEscapeKeyDown?: (event: React.KeyboardEvent<HTMLDivElement>) => void;
243
+ /** Callback when pointer clicks outside the menu */
244
+ onPointerDownOutside?: (event: PointerEvent) => void;
245
+ /** Callback when any outside interaction occurs */
246
+ onInteractOutside?: (event: Event) => void;
247
+ className?: string;
248
+ children?: ReactNode;
249
+ } & ComponentPropsWithoutRef<'div'>;
250
+ /**
251
+ * An interactive item within a menubar dropdown menu.
252
+ *
253
+ * Supports keyboard navigation, hover effects, and the onSelect callback.
254
+ * Automatically closes the menu when selected.
255
+ *
256
+ * Must be used within Menubar.Content.
257
+ *
258
+ * @example
259
+ * ```tsx
260
+ * <Menubar.Content>
261
+ * <Menubar.Item onSelect={() => console.log('New file')}>
262
+ * New File
263
+ * </Menubar.Item>
264
+ * <Menubar.Item disabled>
265
+ * Save (disabled)
266
+ * </Menubar.Item>
267
+ * <Menubar.Item asChild>
268
+ * <a href="/export">Export</a>
269
+ * </Menubar.Item>
270
+ * </Menubar.Content>
271
+ * ```
272
+ */
273
+ export type MenubarItemProps = {
274
+ /** Unique ID for this item (auto-generated if not provided) */
275
+ id?: string;
276
+ /** Whether the item is disabled */
277
+ disabled?: boolean;
278
+ /** Callback when the item is selected (clicked or activated via keyboard) */
279
+ onSelect?: (event: Event) => void;
280
+ /** Render as a child element using Radix UI Slot */
281
+ asChild?: boolean;
282
+ className?: string;
283
+ children: ReactNode;
284
+ } & Omit<ComponentPropsWithoutRef<'div'>, 'id' | 'children'>;
285
+ /**
286
+ * A non-interactive label for grouping items within a menubar dropdown.
287
+ *
288
+ * Used to provide section headings or context within the menu.
289
+ * Does not participate in keyboard navigation.
290
+ *
291
+ * Must be used within Menubar.Content.
292
+ *
293
+ * @example
294
+ * ```tsx
295
+ * <Menubar.Content>
296
+ * <Menubar.Label>File Operations</Menubar.Label>
297
+ * <Menubar.Item>New</Menubar.Item>
298
+ * <Menubar.Item>Open</Menubar.Item>
299
+ * <Menubar.Separator />
300
+ * <Menubar.Label>Recent Files</Menubar.Label>
301
+ * <Menubar.Item>Document.txt</Menubar.Item>
302
+ * </Menubar.Content>
303
+ * ```
304
+ */
305
+ export type MenubarLabelProps = {
306
+ className?: string;
307
+ children?: ReactNode;
308
+ } & ComponentPropsWithoutRef<'div'>;
309
+ export declare const Menubar: {
310
+ ({ defaultActive, onActiveChange, id, children }: MenubarRootProps): ReactElement;
311
+ displayName: string;
312
+ } & {
313
+ Root: {
314
+ ({ defaultActive, onActiveChange, id, children }: MenubarRootProps): ReactElement;
315
+ displayName: string;
316
+ };
317
+ Nav: import('preact').FunctionalComponent<import('preact/compat').PropsWithoutRef<MenubarNavProps> & {
318
+ ref?: import('preact').Ref<HTMLDivElement> | undefined;
319
+ }>;
320
+ Content: import('preact').FunctionalComponent<import('preact/compat').PropsWithoutRef<MenubarContentProps> & {
321
+ ref?: import('preact').Ref<HTMLDivElement> | undefined;
322
+ }>;
323
+ Button: import('preact').FunctionalComponent<import('preact/compat').PropsWithoutRef<MenubarButtonProps> & {
324
+ ref?: import('preact').Ref<HTMLButtonElement> | undefined;
325
+ }>;
326
+ Separator: import('preact').FunctionalComponent<import('preact/compat').PropsWithoutRef<MenubarSeparatorProps> & {
327
+ ref?: import('preact').Ref<HTMLDivElement> | undefined;
328
+ }>;
329
+ Menu: {
330
+ ({ id: providedId, disabled, children }: MenubarMenuProps): ReactElement;
331
+ displayName: string;
332
+ };
333
+ Trigger: import('preact').FunctionalComponent<import('preact/compat').PropsWithoutRef<MenubarTriggerProps> & {
334
+ ref?: import('preact').Ref<HTMLButtonElement> | undefined;
335
+ }>;
336
+ Portal: {
337
+ ({ container, forceMount, children }: MenubarPortalProps): ReactElement | null;
338
+ displayName: string;
339
+ };
340
+ Item: import('preact').FunctionalComponent<import('preact/compat').PropsWithoutRef<MenubarItemProps> & {
341
+ ref?: import('preact').Ref<HTMLDivElement> | undefined;
342
+ }>;
343
+ Label: import('preact').FunctionalComponent<import('preact/compat').PropsWithoutRef<MenubarLabelProps> & {
344
+ ref?: import('preact').Ref<HTMLDivElement> | undefined;
345
+ }>;
346
+ };
@@ -1,4 +1,5 @@
1
1
  export { useScrollLock } from './use-scroll-lock';
2
2
  export { useControlledState } from './use-controlled-state';
3
+ export { useControlledStateWithNull } from './use-controlled-state-with-null';
3
4
  export { useItemRegistry, type ItemMetadata, type UseItemRegistryReturn } from './use-item-registry';
4
5
  export { useKeyboardNavigation, type KeyboardNavigationConfig, type UseKeyboardNavigationReturn, } from './use-keyboard-navigation';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * A variant of `useControlledState` that handles `null` as "no value" in controlled mode.
3
+ *
4
+ * This hook provides a clean external API where `null` represents "no value" while
5
+ * internally using `undefined` for consistency with React/Preact patterns.
6
+ *
7
+ * **External API (Component Props):**
8
+ * - `undefined` = uncontrolled mode (prop not provided)
9
+ * - `null` = controlled mode with no value
10
+ * - `value` = controlled mode with value
11
+ *
12
+ * **Internal State:**
13
+ * - `undefined` = no value (converted from external `null`)
14
+ * - `value` = has value
15
+ *
16
+ * @template T - The type of the state value (typically `string`)
17
+ *
18
+ * @param controlledValue - The controlled value from props (e.g., `active`)
19
+ * @param defaultValue - The default value for uncontrolled mode
20
+ * @param onChange - Callback invoked when the value changes (receives `null` for no value)
21
+ *
22
+ * @returns A tuple containing the current value and a setter function
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * // Component with controlled "no active" support
27
+ * function Listbox({ active, defaultActive, setActive }: {
28
+ * active?: string | null;
29
+ * defaultActive?: string;
30
+ * setActive?: (active: string | null) => void;
31
+ * }) {
32
+ * const [activeInternal, setActiveInternal] = useControlledStateWithNull(
33
+ * active,
34
+ * defaultActive,
35
+ * setActive
36
+ * );
37
+ *
38
+ * // activeInternal is string | undefined internally
39
+ * // But external API uses string | null
40
+ *
41
+ * // active={null} → activeInternal = undefined
42
+ * // active="item" → activeInternal = "item"
43
+ * // active not provided → activeInternal from defaultActive
44
+ * }
45
+ * ```
46
+ */
47
+ export declare function useControlledStateWithNull<T extends string>(controlledValue: T | null | undefined, defaultValue: T | undefined, onChange?: (value: T | null) => void): [T | undefined, (value: T | null | undefined) => void];
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Manages state that can be either controlled or uncontrolled.
3
- * Follows the pattern for controlled/uncontrolled state management.
3
+ * Follows the React pattern for controlled/uncontrolled state management.
4
4
  *
5
- * @template T - The type of the state value
5
+ * @template T - The type of the state value (can include `null` for "no value" state)
6
6
  *
7
7
  * @param controlledValue - The controlled value from props (e.g., `value`, `open`, `checked`)
8
8
  * @param defaultValue - The default value for uncontrolled mode (e.g., `defaultValue`, `defaultOpen`)
@@ -10,6 +10,16 @@
10
10
  *
11
11
  * @returns A tuple containing the current value and a setter function
12
12
  *
13
+ * **Controlled vs Uncontrolled Detection:**
14
+ * - `undefined` = uncontrolled mode (prop not provided)
15
+ * - Any other value (including `null`) = controlled mode
16
+ *
17
+ * **Supporting "No Value" in Controlled Mode:**
18
+ * Use `null` to represent "no value" in controlled mode:
19
+ * - `active={null}` → controlled with no active item
20
+ * - `active="item-1"` → controlled with active item
21
+ * - `active` not provided → uncontrolled mode
22
+ *
13
23
  * @example
14
24
  * ```tsx
15
25
  * // Uncontrolled usage
@@ -23,6 +33,14 @@
23
33
  * const [value, setValue] = useControlledState(open, false, onOpenChange);
24
34
  * return <div>{value ? 'Open' : 'Closed'}</div>;
25
35
  * }
36
+ *
37
+ * // Controlled with null support (for "no value" state)
38
+ * function Component({ active, onActiveChange }: { active?: string | null }) {
39
+ * const [value, setValue] = useControlledState(active, undefined, onActiveChange);
40
+ * // active={null} → controlled, value = null
41
+ * // active="item" → controlled, value = "item"
42
+ * // active not provided → uncontrolled, value from defaultActive
43
+ * }
26
44
  * ```
27
45
  */
28
46
  export declare function useControlledState<T>(controlledValue: T | undefined, defaultValue: T, onChange?: (value: T) => void): [T, (value: T) => void];
@@ -4,4 +4,6 @@ export * from './dialog-provider';
4
4
  export * from './id-provider';
5
5
  export * from './listbox-provider';
6
6
  export * from './menu-provider';
7
+ export * from './menubar-menu-provider';
8
+ export * from './menubar-provider';
7
9
  export * from './search-field-provider';
@@ -1,12 +1,20 @@
1
1
  import { ReactElement, ReactNode } from 'react';
2
2
  export type ListboxContextValue = {
3
3
  baseId: string;
4
+ /**
5
+ * Active item ID (`undefined` when no item is active).
6
+ * Note: Never `null` - the hook converts `null` to `undefined` internally.
7
+ */
4
8
  active?: string;
5
9
  selection: ReadonlySet<string>;
6
10
  selectionMode: 'single' | 'multiple';
7
11
  disabled?: boolean;
8
12
  focusable?: boolean;
9
- setActive: (id?: string) => void;
13
+ /**
14
+ * Set active item.
15
+ * Accepts `null` for compatibility with controlled prop API, but converts it to `undefined` internally.
16
+ */
17
+ setActive: (id?: string | null) => void;
10
18
  toggleValue: (value: string) => void;
11
19
  keyHandler?: (e: React.KeyboardEvent<HTMLElement>) => void;
12
20
  registerItem: (id: string, disabled?: boolean) => void;
@@ -0,0 +1,36 @@
1
+ import { RefObject } from 'react';
2
+ import { MenubarContextValue } from './menubar-provider';
3
+ /**
4
+ * Context value for individual menu within a menubar.
5
+ *
6
+ * Coordinates between the menu's state and the parent menubar's state.
7
+ */
8
+ export type MenubarMenuContextValue = {
9
+ /** Unique ID for this menu */
10
+ menuId: string;
11
+ /** ID for the menu content element */
12
+ contentId: string;
13
+ /** Whether this menu is currently open */
14
+ open: boolean;
15
+ /** Open/close this menu */
16
+ setOpen: (open: boolean) => void;
17
+ /** Reference to the parent menubar context */
18
+ menubarContext: MenubarContextValue;
19
+ /** Reference to the trigger element */
20
+ triggerRef: RefObject<HTMLButtonElement> | null;
21
+ };
22
+ export declare const MenubarMenuContext: import('preact').Context<MenubarMenuContextValue | undefined>;
23
+ export declare const MenubarMenuProvider: import('preact').Provider<MenubarMenuContextValue | undefined>;
24
+ /**
25
+ * Access the menubar menu context.
26
+ *
27
+ * @throws Error if used outside MenubarMenuProvider
28
+ */
29
+ export declare function useMenubarMenu(): MenubarMenuContextValue;
30
+ /**
31
+ * Optionally access the menubar menu context.
32
+ *
33
+ * Returns undefined if used outside MenubarMenuProvider.
34
+ * Useful for components that can work both inside and outside a menubar.
35
+ */
36
+ export declare function useMenubarMenuOptional(): MenubarMenuContextValue | undefined;
@@ -0,0 +1,75 @@
1
+ import { ReactElement, ReactNode, RefObject } from 'react';
2
+ /**
3
+ * Context value for Menubar component.
4
+ * Manages horizontal navigation between menubar items (buttons and menu triggers).
5
+ *
6
+ * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/menubar/} - ARIA Menubar Pattern
7
+ */
8
+ export type MenubarContextValue = {
9
+ /**
10
+ * Currently active menubar item ID (keyboard navigation focus).
11
+ * This tracks which button or menu trigger is currently highlighted.
12
+ * `undefined` when no item is active.
13
+ */
14
+ active: string | undefined;
15
+ setActive: (id: string | undefined) => void;
16
+ /**
17
+ * Registry for menubar items to enable keyboard navigation.
18
+ * Items are tracked in insertion order for left/right arrow navigation.
19
+ */
20
+ registerItem: (id: string, disabled?: boolean) => void;
21
+ unregisterItem: (id: string) => void;
22
+ getItems: () => string[];
23
+ isItemDisabled: (id: string) => boolean;
24
+ /**
25
+ * ID of the currently open menu (if any).
26
+ * Used in Phase 2 to track which menu is expanded in the menubar.
27
+ *
28
+ * @phase2
29
+ */
30
+ openMenuId: string | undefined;
31
+ setOpenMenuId: (id: string | undefined) => void;
32
+ /**
33
+ * Base ID for the menubar container.
34
+ */
35
+ menubarId: string;
36
+ /**
37
+ * Ref to the menubar container element for focus management.
38
+ */
39
+ menubarRef: RefObject<HTMLDivElement> | null;
40
+ };
41
+ export type MenubarProviderProps = {
42
+ value: MenubarContextValue;
43
+ children?: ReactNode;
44
+ };
45
+ /**
46
+ * Provider component that supplies menubar context to child components.
47
+ * Should wrap all Menubar.* components.
48
+ */
49
+ export declare const MenubarProvider: {
50
+ ({ value, children }: MenubarProviderProps): ReactElement;
51
+ displayName: string;
52
+ };
53
+ /**
54
+ * Hook to access menubar context.
55
+ * Must be used within a MenubarProvider.
56
+ *
57
+ * @throws {Error} If used outside of MenubarProvider
58
+ * @returns {MenubarContextValue} The menubar context
59
+ *
60
+ * @example
61
+ * ```tsx
62
+ * const { active, setActive, registerItem } = useMenubar();
63
+ * ```
64
+ */
65
+ export declare const useMenubar: () => MenubarContextValue;
66
+ /**
67
+ * Hook to optionally access menubar context.
68
+ * Returns undefined if not within a MenubarProvider.
69
+ *
70
+ * Useful for components that can work both inside and outside a menubar (e.g., Menu.Trigger).
71
+ *
72
+ * @phase2 Used by Menu components to detect if they're within a Menubar
73
+ * @returns {MenubarContextValue | undefined} The menubar context or undefined
74
+ */
75
+ export declare const useMenubarOptional: () => MenubarContextValue | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enonic/ui",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Enonic UI Component Library",
5
5
  "author": "Enonic",
6
6
  "license": "MIT",