@enonic/ui 0.47.12 → 0.49.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/enonic-ui.cjs +1 -1
- package/dist/enonic-ui.es.js +181 -177
- package/dist/styles/preset.css +1 -1
- package/dist/styles/style.css +1 -1
- package/dist/styles/utilities.css +1 -1
- package/dist/types/components/context-menu/context-menu.d.ts +39 -0
- package/dist/types/components/context-menu/index.d.ts +1 -1
- package/dist/types/components/list-item/list-item.d.ts +16 -4
- package/dist/types/components/search-field/search-field.d.ts +3 -2
- package/dist/types/hooks/index.d.ts +2 -1
- package/dist/types/hooks/use-click-outside.d.ts +5 -1
- package/dist/types/hooks/use-floating-position.d.ts +21 -39
- package/dist/types/hooks/use-submenu-safe-area.d.ts +38 -0
- package/dist/types/providers/context-menu-content-provider.d.ts +29 -0
- package/dist/types/providers/context-menu-provider.d.ts +8 -6
- package/dist/types/providers/context-menu-sub-provider.d.ts +28 -0
- package/dist/types/providers/index.d.ts +2 -0
- package/package.json +1 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
|
+
export type FloatingSide = 'top' | 'bottom' | 'left' | 'right';
|
|
2
3
|
export type FloatingPosition = {
|
|
3
4
|
top: number;
|
|
4
5
|
left?: number;
|
|
5
6
|
right?: number;
|
|
6
|
-
/** Which side the popup is positioned relative to the anchor */
|
|
7
|
-
side:
|
|
7
|
+
/** Which side the popup is positioned relative to the anchor (post-flip) */
|
|
8
|
+
side: FloatingSide;
|
|
8
9
|
};
|
|
9
10
|
export type UseFloatingPositionConfig = {
|
|
10
11
|
/** Whether the floating element is open/visible */
|
|
@@ -13,48 +14,29 @@ export type UseFloatingPositionConfig = {
|
|
|
13
14
|
anchorRef: RefObject<HTMLElement> | null;
|
|
14
15
|
/** Reference to the floating content element */
|
|
15
16
|
contentRef: RefObject<HTMLElement> | null;
|
|
16
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Preferred side relative to the anchor. Flips to the opposite side when
|
|
19
|
+
* the content would overflow the viewport.
|
|
20
|
+
* @default 'bottom'
|
|
21
|
+
*/
|
|
22
|
+
side?: FloatingSide;
|
|
23
|
+
/**
|
|
24
|
+
* Alignment along the perpendicular axis:
|
|
25
|
+
* - For `side` top/bottom: horizontal alignment ('start' = left, 'end' = right)
|
|
26
|
+
* - For `side` left/right: vertical alignment ('start' = top, 'end' = bottom)
|
|
27
|
+
* @default 'start'
|
|
28
|
+
*/
|
|
17
29
|
align?: 'start' | 'end';
|
|
18
30
|
};
|
|
19
31
|
/**
|
|
20
|
-
* Calculates optimal position for floating elements (menus, dropdowns) relative
|
|
21
|
-
* with automatic viewport collision detection and flip behavior.
|
|
32
|
+
* Calculates optimal position for floating elements (menus, dropdowns, submenus) relative
|
|
33
|
+
* to an anchor, with automatic viewport collision detection and flip behavior.
|
|
22
34
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* - Horizontal adjustment to stay within viewport bounds
|
|
27
|
-
* - Automatic repositioning on window resize and scroll
|
|
35
|
+
* Supports four preferred sides (top/bottom/left/right). When the content would overflow
|
|
36
|
+
* the viewport on the preferred side, it flips to the opposite side. Perpendicular-axis
|
|
37
|
+
* alignment is adjusted to keep the content within the viewport.
|
|
28
38
|
*
|
|
29
39
|
* @param config - Configuration object for positioning behavior
|
|
30
40
|
* @returns Position object with top and left/right coordinates, or null if not yet calculated
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```tsx
|
|
34
|
-
* function Menu() {
|
|
35
|
-
* const anchorRef = useRef<HTMLButtonElement>(null);
|
|
36
|
-
* const contentRef = useRef<HTMLDivElement>(null);
|
|
37
|
-
* const position = useFloatingPosition({
|
|
38
|
-
* enabled: open,
|
|
39
|
-
* anchorRef,
|
|
40
|
-
* contentRef,
|
|
41
|
-
* align: 'start'
|
|
42
|
-
* });
|
|
43
|
-
*
|
|
44
|
-
* return (
|
|
45
|
-
* <div
|
|
46
|
-
* ref={contentRef}
|
|
47
|
-
* style={{
|
|
48
|
-
* position: 'fixed',
|
|
49
|
-
* top: position ? `${position.top}px` : '0',
|
|
50
|
-
* left: position?.left !== undefined ? `${position.left}px` : undefined,
|
|
51
|
-
* right: position?.right !== undefined ? `${position.right}px` : undefined,
|
|
52
|
-
* }}
|
|
53
|
-
* >
|
|
54
|
-
* Menu content
|
|
55
|
-
* </div>
|
|
56
|
-
* );
|
|
57
|
-
* }
|
|
58
|
-
* ```
|
|
59
41
|
*/
|
|
60
|
-
export declare function useFloatingPosition({ enabled, anchorRef, contentRef, align, }: UseFloatingPositionConfig): FloatingPosition | null;
|
|
42
|
+
export declare function useFloatingPosition({ enabled, anchorRef, contentRef, side: preferredSide, align, }: UseFloatingPositionConfig): FloatingPosition | null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
export type SafeAreaSide = 'left' | 'right';
|
|
3
|
+
export type UseSubmenuSafeAreaConfig = {
|
|
4
|
+
/** Whether the submenu is open (tracking is active) */
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
/** Reference to the submenu content element — used to compute its near edge */
|
|
7
|
+
submenuRef: RefObject<HTMLElement>;
|
|
8
|
+
/**
|
|
9
|
+
* Which side the submenu sits on relative to its trigger.
|
|
10
|
+
* - 'right': near edge is the submenu's LEFT edge; pointer travels right toward it
|
|
11
|
+
* - 'left': near edge is the submenu's RIGHT edge; pointer travels left toward it
|
|
12
|
+
*/
|
|
13
|
+
side: SafeAreaSide;
|
|
14
|
+
/**
|
|
15
|
+
* How long (ms) after the submenu opens the safe zone remains valid without
|
|
16
|
+
* a confirming pointer sample. Prevents a stale history from locking the zone.
|
|
17
|
+
* @default 300
|
|
18
|
+
*/
|
|
19
|
+
graceMs?: number;
|
|
20
|
+
};
|
|
21
|
+
export type UseSubmenuSafeAreaReturn = {
|
|
22
|
+
/**
|
|
23
|
+
* Returns true if the pointer at (x, y) is currently traveling toward the submenu's
|
|
24
|
+
* near edge — i.e. inside the triangle formed by the previous pointer sample and
|
|
25
|
+
* the two corners of that edge. Callers should skip sibling hover handling while
|
|
26
|
+
* this is true.
|
|
27
|
+
*/
|
|
28
|
+
isInSafeArea: (x: number, y: number) => boolean;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Tracks pointer history while a submenu is open and computes whether the pointer
|
|
32
|
+
* is inside the "safe triangle" — the region between the previous pointer position
|
|
33
|
+
* and the submenu's near edge. Callers use this to suppress sibling-item hover
|
|
34
|
+
* handling while the user is diagonally moving toward the submenu.
|
|
35
|
+
*
|
|
36
|
+
* Implements the classic Amazon.com menu heuristic popularized by Radix UI.
|
|
37
|
+
*/
|
|
38
|
+
export declare function useSubmenuSafeArea({ enabled, submenuRef, side, graceMs, }: UseSubmenuSafeAreaConfig): UseSubmenuSafeAreaReturn;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { MutableRefObject, ReactElement, ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Navigation-related state scoped to a single `ContextMenu.Content` (or `SubContent`).
|
|
4
|
+
*
|
|
5
|
+
* Each menu level owns its own item registry and active-item state so nested submenus
|
|
6
|
+
* don't share navigation state with their parent. Selection-related state like `setOpen`
|
|
7
|
+
* (which closes the entire menu) still lives on the root `ContextMenuContextValue`.
|
|
8
|
+
*/
|
|
9
|
+
export type ContextMenuContentContextValue = {
|
|
10
|
+
active: string | undefined;
|
|
11
|
+
setActive: (id: string | undefined) => void;
|
|
12
|
+
registerItem: (id: string, disabled?: boolean, element?: HTMLElement | null) => void;
|
|
13
|
+
unregisterItem: (id: string) => void;
|
|
14
|
+
getItems: () => string[];
|
|
15
|
+
isItemDisabled: (id: string) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Safe-triangle check registered by a currently-open child `Sub`. When non-null,
|
|
18
|
+
* the Content's pointer-move capture handler consults it to decide whether to
|
|
19
|
+
* suppress sibling hover activation while the user is traveling toward the submenu.
|
|
20
|
+
* Null when no submenu is open at this level.
|
|
21
|
+
*/
|
|
22
|
+
safeAreaCheckRef: MutableRefObject<((x: number, y: number) => boolean) | null>;
|
|
23
|
+
};
|
|
24
|
+
export type ContextMenuContentProviderProps = {
|
|
25
|
+
value: ContextMenuContentContextValue;
|
|
26
|
+
children?: ReactNode;
|
|
27
|
+
};
|
|
28
|
+
export declare const ContextMenuContentProvider: ({ value, children }: ContextMenuContentProviderProps) => ReactElement;
|
|
29
|
+
export declare const useContextMenuContent: () => ContextMenuContentContextValue;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { ReactElement, ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Root-level context for ContextMenu. Holds menu-wide state that every level
|
|
4
|
+
* (top content, submenus) needs access to.
|
|
5
|
+
*
|
|
6
|
+
* Navigation state (active item, item registry) lives in a per-level
|
|
7
|
+
* `ContextMenuContentContextValue` — each Content and SubContent owns its own.
|
|
8
|
+
*/
|
|
2
9
|
export type ContextMenuContextValue = {
|
|
3
10
|
baseId: string;
|
|
4
11
|
open: boolean;
|
|
5
12
|
setOpen: (open: boolean) => void;
|
|
6
|
-
|
|
7
|
-
setActive: (id: string | undefined) => void;
|
|
8
|
-
registerItem: (id: string, disabled?: boolean, element?: HTMLElement | null) => void;
|
|
9
|
-
unregisterItem: (id: string) => void;
|
|
10
|
-
getItems: () => string[];
|
|
11
|
-
isItemDisabled: (id: string) => boolean;
|
|
13
|
+
/** Pointer coordinates where the menu was opened (set by Trigger onContextMenu) */
|
|
12
14
|
position: {
|
|
13
15
|
x: number;
|
|
14
16
|
y: number;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
import { ContextMenuContentContextValue } from './context-menu-content-provider';
|
|
3
|
+
/**
|
|
4
|
+
* Context value for a single `ContextMenu.Sub` wrapper.
|
|
5
|
+
*
|
|
6
|
+
* Coordinates between the submenu's local open state and the parent content's
|
|
7
|
+
* navigation state (so ArrowLeft from a submenu can restore active on its trigger).
|
|
8
|
+
*/
|
|
9
|
+
export type ContextMenuSubContextValue = {
|
|
10
|
+
/** Unique ID used by the SubTrigger (also the registry key in the parent content) */
|
|
11
|
+
subId: string;
|
|
12
|
+
/** ID of the submenu content element (for aria-controls) */
|
|
13
|
+
contentId: string;
|
|
14
|
+
/** Whether the submenu is currently open */
|
|
15
|
+
open: boolean;
|
|
16
|
+
/** Open/close the submenu */
|
|
17
|
+
setOpen: (open: boolean) => void;
|
|
18
|
+
/** Ref to the SubTrigger DOM node (for ArrowLeft refocus and collision anchoring) */
|
|
19
|
+
triggerRef: RefObject<HTMLDivElement> | null;
|
|
20
|
+
/**
|
|
21
|
+
* Snapshot of the parent content context captured at mount time.
|
|
22
|
+
* Used by SubContent's ArrowLeft handler to set parent `active` back to the trigger.
|
|
23
|
+
*/
|
|
24
|
+
parentContent: ContextMenuContentContextValue;
|
|
25
|
+
};
|
|
26
|
+
export declare const ContextMenuSubProvider: import('preact').Provider<ContextMenuSubContextValue | undefined>;
|
|
27
|
+
export declare const useContextMenuSub: () => ContextMenuSubContextValue;
|
|
28
|
+
export declare const useContextMenuSubOptional: () => ContextMenuSubContextValue | undefined;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from './avatar-provider';
|
|
2
2
|
export * from './combobox-provider';
|
|
3
|
+
export * from './context-menu-content-provider';
|
|
3
4
|
export * from './context-menu-provider';
|
|
5
|
+
export * from './context-menu-sub-provider';
|
|
4
6
|
export * from './date-picker-provider';
|
|
5
7
|
export * from './dialog-provider';
|
|
6
8
|
export * from './focus-container-provider';
|