@enonic/ui 0.48.0 → 0.50.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 +182 -181
- 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/grid-list/grid-list.d.ts +1 -1
- package/dist/types/components/list-item/list-item.d.ts +16 -4
- package/dist/types/components/listbox/listbox.d.ts +8 -2
- package/dist/types/components/search-field/search-field.d.ts +3 -2
- package/dist/types/components/virtualized-tree-list/virtualized-tree-list.d.ts +7 -31
- package/dist/types/hooks/index.d.ts +2 -0
- package/dist/types/hooks/use-active-on-open.d.ts +63 -0
- package/dist/types/hooks/use-click-outside.d.ts +5 -1
- package/dist/types/hooks/use-scroll-active-into-view.d.ts +3 -61
- package/dist/types/hooks/use-scroll-selected-to-virtuoso.d.ts +42 -0
- package/dist/types/providers/combobox-provider.d.ts +6 -0
- package/dist/types/providers/listbox-provider.d.ts +5 -0
- package/dist/types/providers/virtualized-tree-list-provider.d.ts +1 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/scroll.d.ts +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
export type UseActiveOnOpenConfig = {
|
|
3
|
+
/**
|
|
4
|
+
* Whether the dropdown is open. Pass `true` for always-open contexts
|
|
5
|
+
* like a standalone Listbox — the rising edge fires once on mount.
|
|
6
|
+
*/
|
|
7
|
+
open: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Current selection set. The hook picks the first selected item in
|
|
10
|
+
* DOM order (according to `getItems()`) as the activation target.
|
|
11
|
+
*/
|
|
12
|
+
selection: ReadonlySet<string>;
|
|
13
|
+
/** Setter for active item. */
|
|
14
|
+
setActive: (id: string | undefined) => void;
|
|
15
|
+
/** Returns DOM-ordered IDs from the item registry. */
|
|
16
|
+
getItems: () => readonly string[];
|
|
17
|
+
/** Returns whether an item is disabled. */
|
|
18
|
+
isItemDisabled: (id: string) => boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Container element to scroll. The hook centers the activated selection
|
|
21
|
+
* inside this container by querying for the option element and calling
|
|
22
|
+
* `scrollIntoView({ block: 'center' })`. When omitted, only state is
|
|
23
|
+
* updated (useful for tests or virtualized consumers handling scroll
|
|
24
|
+
* through their own adapter).
|
|
25
|
+
*/
|
|
26
|
+
containerRef?: RefObject<HTMLElement> | null;
|
|
27
|
+
/**
|
|
28
|
+
* Build the option element's DOM id from a registry id. Defaults to
|
|
29
|
+
* identity. Match the consumer's `buildElementId` used elsewhere.
|
|
30
|
+
*/
|
|
31
|
+
buildElementId?: (id: string) => string;
|
|
32
|
+
/**
|
|
33
|
+
* When `false`, the hook does nothing. Used by Combobox when `active`
|
|
34
|
+
* is controlled externally — the consumer owns activation.
|
|
35
|
+
* @default true
|
|
36
|
+
*/
|
|
37
|
+
enabled?: boolean;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Activates the selected item (or first enabled fallback) when a dropdown
|
|
41
|
+
* opens, and centers the activated selection in the viewport.
|
|
42
|
+
*
|
|
43
|
+
* Trigger: `open` rising edge. On rising edge, the hook unconditionally
|
|
44
|
+
* rewrites active even if a stale value persists from the previous open.
|
|
45
|
+
* If the item registry is empty at that moment (popup mounts items lazily,
|
|
46
|
+
* common with Portals), the hook re-runs on subsequent renders until items
|
|
47
|
+
* appear — `getItems` callback identity is coupled to the registry version,
|
|
48
|
+
* so listing it as a dep covers lazy-mount cases.
|
|
49
|
+
*
|
|
50
|
+
* Behavior:
|
|
51
|
+
* - Selection has any enabled item present in the registry → activate it
|
|
52
|
+
* and call `scrollIntoView({ block: 'center' })` on the option element.
|
|
53
|
+
* - Otherwise → set active to first enabled item; no scroll (the default
|
|
54
|
+
* `'nearest'` from `useScrollActiveIntoView` will apply on next nav).
|
|
55
|
+
* - No items registered yet → wait; the next `getItems` identity change
|
|
56
|
+
* re-runs the effect (covers the lazy-mount case).
|
|
57
|
+
*
|
|
58
|
+
* The imperative scroll is owned by this hook so the open-transition center
|
|
59
|
+
* behavior is independent of whether `setActive` actually changed the value
|
|
60
|
+
* — if `active` already pointed at the target, `useScrollActiveIntoView`
|
|
61
|
+
* would not re-fire, and a coordination-via-ref scheme would silently fail.
|
|
62
|
+
*/
|
|
63
|
+
export declare function useActiveOnOpen({ open, selection, setActive, getItems, isItemDisabled, containerRef, buildElementId, enabled, }: UseActiveOnOpenConfig): void;
|
|
@@ -13,6 +13,8 @@ export type UseClickOutsideConfig = {
|
|
|
13
13
|
onPointerDownOutside?: (event: PointerEvent) => void;
|
|
14
14
|
/** Callback when any interaction occurs outside */
|
|
15
15
|
onInteractOutside?: (event: Event) => void;
|
|
16
|
+
/** Which event should trigger `onClose`. Outside callbacks still fire on pointerdown. Defaults to pointerdown. */
|
|
17
|
+
closeOn?: 'pointerdown' | 'click';
|
|
16
18
|
/**
|
|
17
19
|
* Callback to close/hide the content.
|
|
18
20
|
* Only called if event.defaultPrevented is false.
|
|
@@ -29,6 +31,8 @@ export type UseClickOutsideConfig = {
|
|
|
29
31
|
* - Ignores clicks inside the content element
|
|
30
32
|
* - Supports excluding additional elements (e.g., trigger buttons)
|
|
31
33
|
* - Ignores clicks inside elements with `data-click-outside-ignore` attribute
|
|
34
|
+
* - Can close immediately on outside pointerdown, or wait for the matching click
|
|
35
|
+
* - Waiting for click helps avoid mobile tap clicks reaching elements behind a closing overlay
|
|
32
36
|
* - Respects event.defaultPrevented to allow custom handling
|
|
33
37
|
* - Only active when enabled
|
|
34
38
|
* - Properly cleans up event listeners
|
|
@@ -97,4 +101,4 @@ export type UseClickOutsideConfig = {
|
|
|
97
101
|
* }
|
|
98
102
|
* ```
|
|
99
103
|
*/
|
|
100
|
-
export declare function useClickOutside({ enabled, contentRef, excludeRefs, onPointerDownOutside, onInteractOutside, onClose, }: UseClickOutsideConfig): void;
|
|
104
|
+
export declare function useClickOutside({ enabled, contentRef, excludeRefs, onPointerDownOutside, onInteractOutside, closeOn, onClose, }: UseClickOutsideConfig): void;
|
|
@@ -21,66 +21,8 @@ export type UseScrollActiveIntoViewConfig = {
|
|
|
21
21
|
buildElementId?: (activeId: string) => string;
|
|
22
22
|
};
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* components to ensure the currently active item is visible when it changes.
|
|
28
|
-
*
|
|
29
|
-
* Features:
|
|
30
|
-
* - Smooth scrolling behavior with 'nearest' algorithm (minimal scroll distance)
|
|
31
|
-
* - Supports both vertical and horizontal scrolling
|
|
32
|
-
* - Supports custom ID construction for prefixed/namespaced IDs
|
|
33
|
-
* - Only scrolls when activeId changes
|
|
34
|
-
*
|
|
35
|
-
* @param config - Configuration object for scroll behavior
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```tsx
|
|
39
|
-
* // Vertical list (Menu)
|
|
40
|
-
* function MenuContent() {
|
|
41
|
-
* const contentRef = useRef<HTMLDivElement>(null);
|
|
42
|
-
* const { active } = useMenu();
|
|
43
|
-
*
|
|
44
|
-
* useScrollActiveIntoView({
|
|
45
|
-
* containerRef: contentRef,
|
|
46
|
-
* activeId: active,
|
|
47
|
-
* orientation: 'vertical',
|
|
48
|
-
* });
|
|
49
|
-
*
|
|
50
|
-
* return <div ref={contentRef}>...</div>;
|
|
51
|
-
* }
|
|
52
|
-
*
|
|
53
|
-
* // Horizontal navigation (Menubar)
|
|
54
|
-
* function Menubar() {
|
|
55
|
-
* const menubarRef = useRef<HTMLDivElement>(null);
|
|
56
|
-
* const { active } = useMenubar();
|
|
57
|
-
*
|
|
58
|
-
* useScrollActiveIntoView({
|
|
59
|
-
* containerRef: menubarRef,
|
|
60
|
-
* activeId: active,
|
|
61
|
-
* orientation: 'horizontal',
|
|
62
|
-
* });
|
|
63
|
-
*
|
|
64
|
-
* return <div ref={menubarRef}>...</div>;
|
|
65
|
-
* }
|
|
66
|
-
*
|
|
67
|
-
* // With ID prefix (Listbox)
|
|
68
|
-
* function ListboxContent({ baseId }: { baseId: string }) {
|
|
69
|
-
* const contentRef = useRef<HTMLDivElement>(null);
|
|
70
|
-
* const { active } = useListbox();
|
|
71
|
-
*
|
|
72
|
-
* // Memoize to keep the effect dependency stable across re-renders
|
|
73
|
-
* const buildElementId = useCallback((id: string) => `${baseId}-listbox-option-${id}`, [baseId]);
|
|
74
|
-
*
|
|
75
|
-
* useScrollActiveIntoView({
|
|
76
|
-
* containerRef: contentRef,
|
|
77
|
-
* activeId: active,
|
|
78
|
-
* orientation: 'vertical',
|
|
79
|
-
* buildElementId,
|
|
80
|
-
* });
|
|
81
|
-
*
|
|
82
|
-
* return <div ref={contentRef}>...</div>;
|
|
83
|
-
* }
|
|
84
|
-
* ```
|
|
24
|
+
* Reactive scroll-into-view for keyboard navigation. Uses `block: 'nearest'`
|
|
25
|
+
* for minimal scroll travel. For the open-transition "center on selected"
|
|
26
|
+
* behavior, see `useActiveOnOpen` which performs an imperative center-scroll.
|
|
85
27
|
*/
|
|
86
28
|
export declare function useScrollActiveIntoView({ containerRef, activeId, orientation, buildElementId, }: UseScrollActiveIntoViewConfig): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
type VirtuosoLikeHandle = {
|
|
3
|
+
scrollToIndex: (location: number | {
|
|
4
|
+
index: number;
|
|
5
|
+
align?: 'start' | 'center' | 'end';
|
|
6
|
+
behavior?: 'auto' | 'smooth';
|
|
7
|
+
}) => void;
|
|
8
|
+
};
|
|
9
|
+
export type UseScrollSelectedToVirtuosoConfig<TItem extends {
|
|
10
|
+
id: string;
|
|
11
|
+
}> = {
|
|
12
|
+
/** Whether the dropdown is open. The hook fires on the rising edge. */
|
|
13
|
+
open: boolean;
|
|
14
|
+
/** The single selected ID to center on open, or undefined to skip. */
|
|
15
|
+
selectedId: string | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Setter called with `selectedId` on the rising edge so navigation
|
|
18
|
+
* resumes from there. Pass the active setter from the wrapped list
|
|
19
|
+
* (e.g., `VirtualizedTreeList`'s `onActiveChange` consumer).
|
|
20
|
+
*/
|
|
21
|
+
setActive: (id: string) => void;
|
|
22
|
+
/** Flat items in render order. Used to look up the index for `scrollToIndex`. */
|
|
23
|
+
items: readonly TItem[];
|
|
24
|
+
/** Ref to the Virtuoso instance for scroll operations. */
|
|
25
|
+
virtuosoRef: RefObject<VirtuosoLikeHandle | null> | undefined;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Centers the selected item in a Virtuoso-backed virtualized list when its
|
|
29
|
+
* containing dropdown opens, and sets the consumer's active state to match.
|
|
30
|
+
*
|
|
31
|
+
* Retries on `requestAnimationFrame` while the Virtuoso ref is still null
|
|
32
|
+
* (Virtuoso typically attaches within a frame of mount). Bails after a small
|
|
33
|
+
* cap to avoid infinite loops.
|
|
34
|
+
*
|
|
35
|
+
* Use inside a consumer that wraps `VirtualizedTreeList` in `Combobox.TreeContent`
|
|
36
|
+
* (or any other open/close container) when the same UX as `Combobox.ListContent`
|
|
37
|
+
* is desired: open with selection → centered, arrow keys → nearest.
|
|
38
|
+
*/
|
|
39
|
+
export declare function useScrollSelectedToVirtuoso<TItem extends {
|
|
40
|
+
id: string;
|
|
41
|
+
}>({ open, selectedId, setActive, items, virtuosoRef, }: UseScrollSelectedToVirtuosoConfig<TItem>): void;
|
|
42
|
+
export {};
|
|
@@ -29,6 +29,12 @@ export type ComboboxContextValue = {
|
|
|
29
29
|
isItemDisabled: (id: string) => boolean;
|
|
30
30
|
hasValue: boolean;
|
|
31
31
|
setHasValue: (has: boolean) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Whether the auto-active-on-open behavior should run inside Listbox-backed
|
|
34
|
+
* content. Set to `false` when active state is consumer-controlled. Tree
|
|
35
|
+
* content doesn't use this — see `useScrollSelectedToVirtuoso` instead.
|
|
36
|
+
*/
|
|
37
|
+
autoActiveOnOpen: boolean;
|
|
32
38
|
};
|
|
33
39
|
export type ComboboxProviderProps = {
|
|
34
40
|
value: ComboboxContextValue;
|
|
@@ -26,6 +26,11 @@ export type ListboxContextValue = {
|
|
|
26
26
|
unregisterItem: (id: string) => void;
|
|
27
27
|
getItems: () => string[];
|
|
28
28
|
isItemDisabled: (id: string) => boolean;
|
|
29
|
+
/**
|
|
30
|
+
* When `false`, `useActiveOnOpen` in `Listbox.Content` is skipped. Set
|
|
31
|
+
* by hosts (e.g., Combobox) when the consumer controls active state.
|
|
32
|
+
*/
|
|
33
|
+
autoActiveOnOpen: boolean;
|
|
29
34
|
};
|
|
30
35
|
export type ListboxProviderProps = {
|
|
31
36
|
value: ListboxContextValue;
|
|
@@ -9,6 +9,7 @@ export type VirtualizedTreeListContextValue = {
|
|
|
9
9
|
toggleSelection: (id: string, index: number) => void;
|
|
10
10
|
selectionMode: 'single' | 'multiple' | 'none';
|
|
11
11
|
isFocused: boolean;
|
|
12
|
+
keyboardActive: boolean;
|
|
12
13
|
items: readonly FlatNodeBase[];
|
|
13
14
|
getItemIndex: (id: string) => number;
|
|
14
15
|
scrollToIndex: (index: number) => void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scrolls an element to the center of its nearest scrollable ancestor without
|
|
3
|
+
* disturbing outer scroll containers (page, dialog, etc.).
|
|
4
|
+
*
|
|
5
|
+
* - Chromium 134+ uses `scrollIntoView({ block: 'center', container: 'nearest' })`
|
|
6
|
+
* and centers the element within the closest scroll container only.
|
|
7
|
+
* - Other browsers fall back to `scrollIntoView({ block: 'nearest' })` — the
|
|
8
|
+
* element lands at the closest edge instead of the center, but ancestors
|
|
9
|
+
* that already show it stay put.
|
|
10
|
+
*
|
|
11
|
+
* Useful for dropdowns: bring the selected option into view on open without
|
|
12
|
+
* scrolling the page underneath.
|
|
13
|
+
*/
|
|
14
|
+
export declare function scrollIntoCenter(element: HTMLElement): void;
|