@lumx/core 4.9.0-next.4 → 4.9.0-next.6
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/components-and-utils.css +36 -4
- package/js/components/Combobox/ComboboxButton.d.ts +54 -0
- package/js/components/Combobox/ComboboxInput.d.ts +49 -0
- package/js/components/Combobox/ComboboxList.d.ts +47 -0
- package/js/components/Combobox/ComboboxOption.d.ts +72 -0
- package/js/components/Combobox/ComboboxOptionAction.d.ts +35 -0
- package/js/components/Combobox/ComboboxOptionMoreInfo.d.ts +52 -0
- package/js/components/Combobox/ComboboxOptionSkeleton.d.ts +41 -0
- package/js/components/Combobox/ComboboxPopover.d.ts +50 -0
- package/js/components/Combobox/ComboboxSection.d.ts +58 -0
- package/js/components/Combobox/ComboboxState.d.ts +84 -0
- package/js/components/Combobox/index.d.ts +23 -0
- package/js/components/Combobox/setupCombobox.d.ts +24 -0
- package/js/components/Combobox/setupComboboxButton.d.ts +16 -0
- package/js/components/Combobox/setupComboboxInput.d.ts +29 -0
- package/js/components/Combobox/setupListbox.d.ts +21 -0
- package/js/components/Combobox/types.d.ts +124 -0
- package/js/components/Combobox/utils.d.ts +27 -0
- package/js/components/List/ListItem.d.ts +58 -0
- package/js/components/List/ListItemAction.d.ts +24 -0
- package/js/components/List/index.d.ts +39 -0
- package/js/components/Tabs/Tests.d.ts +11 -0
- package/js/components/Tabs/constants.d.ts +4 -0
- package/js/components/Text/index.d.ts +1 -1
- package/js/types/jsx/PropsToOverride.d.ts +1 -1
- package/js/utils/browser/createSelectorTreeWalker.d.ts +13 -0
- package/js/utils/focusNavigation/createGridFocusNavigation.d.ts +13 -0
- package/js/utils/focusNavigation/index.d.ts +2 -1
- package/js/utils/focusNavigation/types.d.ts +28 -7
- package/js/utils/typeahead/index.d.ts +29 -0
- package/lumx.css +36 -4
- package/package.json +2 -2
- package/scss/_components_classes.scss +2 -1
- package/scss/components/combobox/_index.scss +44 -0
- package/scss/components/list/_mixins.scss +44 -32
- package/stories/types.d.ts +2 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ComboboxCallbacks, ComboboxHandle } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Set up a combobox with a button trigger (select-only pattern).
|
|
4
|
+
*
|
|
5
|
+
* Creates a full combobox handle with the button-mode controller automatically
|
|
6
|
+
* wired in and the trigger registered. The consumer only needs to call
|
|
7
|
+
* `handle.registerListbox(listbox)`.
|
|
8
|
+
*
|
|
9
|
+
* Handles: Space (select/open), Home/End (listbox navigation),
|
|
10
|
+
* printable characters (typeahead), and click (toggle).
|
|
11
|
+
*
|
|
12
|
+
* @param button The button element to use as the combobox trigger.
|
|
13
|
+
* @param callbacks Callbacks for select and open/close events.
|
|
14
|
+
* @returns A ComboboxHandle for interacting with the combobox.
|
|
15
|
+
*/
|
|
16
|
+
export declare function setupComboboxButton(button: HTMLButtonElement, callbacks: ComboboxCallbacks): ComboboxHandle;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ComboboxCallbacks, ComboboxHandle } from './types';
|
|
2
|
+
/** Options for configuring the input-mode combobox controller. */
|
|
3
|
+
export interface SetupComboboxInputOptions {
|
|
4
|
+
/**
|
|
5
|
+
* When true (default), the combobox automatically filters options as the user types.
|
|
6
|
+
* Each registered `Combobox.Option` receives filter state updates and hides itself
|
|
7
|
+
* when it does not match the current input value.
|
|
8
|
+
*
|
|
9
|
+
* Set to false when you want to handle filtering yourself (e.g. async search,
|
|
10
|
+
* consumer-side pre-filtering). Options will not be registered for auto-filtering.
|
|
11
|
+
*/
|
|
12
|
+
autoFilter?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Set up a combobox with an input trigger (autocomplete/filter pattern).
|
|
16
|
+
*
|
|
17
|
+
* Creates a full combobox handle with the input-mode controller automatically
|
|
18
|
+
* wired in and the trigger registered. The consumer only needs to call
|
|
19
|
+
* `handle.registerListbox(listbox)`.
|
|
20
|
+
*
|
|
21
|
+
* Handles: Home/End (text cursor), ArrowLeft/Right (clear active descendant),
|
|
22
|
+
* filtering (on input and on open), and focus behavior.
|
|
23
|
+
*
|
|
24
|
+
* @param input The input element to use as the combobox trigger.
|
|
25
|
+
* @param callbacks Callbacks for select and open/close events.
|
|
26
|
+
* @param options Options for configuring the input-mode controller.
|
|
27
|
+
* @returns A ComboboxHandle for interacting with the combobox.
|
|
28
|
+
*/
|
|
29
|
+
export declare function setupComboboxInput(input: HTMLInputElement, callbacks: ComboboxCallbacks, options?: SetupComboboxInputOptions): ComboboxHandle;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type FocusNavigationController } from '../../utils/focusNavigation';
|
|
2
|
+
import type { ComboboxEventMap, ComboboxHandle } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Set up focus navigation and delegated event listeners on a listbox element.
|
|
5
|
+
*
|
|
6
|
+
* This merges two concerns:
|
|
7
|
+
* 1. Creating the appropriate {@link FocusNavigationController} (grid or list mode)
|
|
8
|
+
* with callbacks that manage `aria-activedescendant` and visual focus indicators.
|
|
9
|
+
* 2. Attaching delegated `click` and `mousedown` listeners to the listbox for
|
|
10
|
+
* option selection and blur prevention.
|
|
11
|
+
*
|
|
12
|
+
* The caller is responsible for guarding against duplicate calls (i.e., checking
|
|
13
|
+
* that a focus navigation controller does not already exist before calling)
|
|
14
|
+
* and ensuring `handle.trigger` and `handle.listbox` are non-null.
|
|
15
|
+
*
|
|
16
|
+
* @param handle The combobox handle (provides trigger, listbox, select, etc.).
|
|
17
|
+
* @param signal Abort signal used to clean up all attached listeners.
|
|
18
|
+
* @param notify Notify subscribers of combobox events.
|
|
19
|
+
* @returns The created focus navigation controller.
|
|
20
|
+
*/
|
|
21
|
+
export declare function setupListbox(handle: ComboboxHandle, signal: AbortSignal, notify: <K extends keyof ComboboxEventMap>(event: K, value: ComboboxEventMap[K]) => void): FocusNavigationController;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { FocusNavigationController } from '../../utils/focusNavigation';
|
|
2
|
+
/** Section visibility state tracked per registration. */
|
|
3
|
+
export interface SectionState {
|
|
4
|
+
hidden: boolean;
|
|
5
|
+
'aria-hidden': boolean;
|
|
6
|
+
}
|
|
7
|
+
/** Registration entry for a section element. */
|
|
8
|
+
export interface SectionRegistration {
|
|
9
|
+
callback: (state: SectionState) => void;
|
|
10
|
+
last: SectionState;
|
|
11
|
+
}
|
|
12
|
+
/** Registration entry for an option element. */
|
|
13
|
+
export interface OptionRegistration {
|
|
14
|
+
callback: (isFiltered: boolean) => void;
|
|
15
|
+
lastFiltered: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Map of combobox event names to their payload types. */
|
|
18
|
+
export interface ComboboxEventMap {
|
|
19
|
+
/** Fired when the combobox open state changes. Payload: whether the combobox is open. */
|
|
20
|
+
open: boolean;
|
|
21
|
+
/** Fired when the active descendant changes (visual focus). Payload: the option id or null. */
|
|
22
|
+
activeDescendantChange: string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Fired when the visible option count transitions between empty and non-empty.
|
|
25
|
+
* Payload: whether the list is empty plus the current input value.
|
|
26
|
+
*/
|
|
27
|
+
emptyChange: {
|
|
28
|
+
isEmpty?: boolean;
|
|
29
|
+
inputValue?: string;
|
|
30
|
+
} | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Fired immediately when the aggregate loading state changes (skeleton count transitions
|
|
33
|
+
* between 0 and >0). Used for empty suppression in ComboboxState and for aria-busy on the listbox.
|
|
34
|
+
*/
|
|
35
|
+
loadingChange: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Fired after a 500ms debounce when loading persists, or immediately when loading ends.
|
|
38
|
+
* Used to control the loading message text in the live region (ComboboxState).
|
|
39
|
+
*/
|
|
40
|
+
loadingAnnouncement: boolean;
|
|
41
|
+
}
|
|
42
|
+
/** Callbacks provided by the consumer (React/Vue) to react to combobox state changes. */
|
|
43
|
+
export interface ComboboxCallbacks {
|
|
44
|
+
/** Called when an option is selected (click or keyboard). */
|
|
45
|
+
onSelect(option: {
|
|
46
|
+
value: string;
|
|
47
|
+
}): void;
|
|
48
|
+
}
|
|
49
|
+
/** Handle returned by `setupCombobox`. Used by framework wrappers and mode controllers. */
|
|
50
|
+
export interface ComboboxHandle {
|
|
51
|
+
/** Register the trigger element. Returns a cleanup function. */
|
|
52
|
+
registerTrigger(trigger: HTMLInputElement | HTMLButtonElement): () => void;
|
|
53
|
+
/** Register the listbox/grid element. Returns a cleanup function. */
|
|
54
|
+
registerListbox(listbox: HTMLElement): () => void;
|
|
55
|
+
/** Tear down all listeners and state. */
|
|
56
|
+
destroy(): void;
|
|
57
|
+
/** Subscribe to a combobox event. Returns an unsubscribe function. */
|
|
58
|
+
subscribe<K extends keyof ComboboxEventMap>(event: K, callback: (value: ComboboxEventMap[K]) => void): () => void;
|
|
59
|
+
/** The current trigger element (may be null before registration). */
|
|
60
|
+
readonly trigger: HTMLInputElement | HTMLButtonElement | null;
|
|
61
|
+
/** The current listbox/grid element (may be null before registration). */
|
|
62
|
+
readonly listbox: HTMLElement | null;
|
|
63
|
+
/** The focus navigation controller. */
|
|
64
|
+
readonly focusNav: FocusNavigationController | null;
|
|
65
|
+
/** Whether the popup is open. */
|
|
66
|
+
readonly isOpen: boolean;
|
|
67
|
+
/** Whether multi-select mode. */
|
|
68
|
+
readonly isMultiSelect: boolean;
|
|
69
|
+
/** Whether any skeleton placeholders are currently registered (loading). */
|
|
70
|
+
readonly isLoading: boolean;
|
|
71
|
+
/** Set the open state, update ARIA, fire callback. */
|
|
72
|
+
setIsOpen(isOpen: boolean): void;
|
|
73
|
+
/** Select an option (or null to clear), fire callback. */
|
|
74
|
+
select(option: HTMLElement | null): void;
|
|
75
|
+
/**
|
|
76
|
+
* Register an option DOM element for filter notifications.
|
|
77
|
+
* The element's textContent is used as the searchable text.
|
|
78
|
+
* The callback is invoked immediately with the current filter state,
|
|
79
|
+
* and again whenever the filter changes.
|
|
80
|
+
* Returns a cleanup function that unregisters the option.
|
|
81
|
+
*/
|
|
82
|
+
registerOption(element: HTMLElement, onFilterChange: (isFiltered: boolean) => void): () => void;
|
|
83
|
+
/**
|
|
84
|
+
* Set the current filter value and notify all registered options of their match state.
|
|
85
|
+
* Options whose text does not start with the filter value are notified with isFiltered=true.
|
|
86
|
+
* An empty filter value clears filtering (all options become visible).
|
|
87
|
+
*/
|
|
88
|
+
setFilter(filterValue: string): void;
|
|
89
|
+
/**
|
|
90
|
+
* Register a section DOM element for state notifications.
|
|
91
|
+
* The callback is invoked immediately with the current state, and again whenever
|
|
92
|
+
* the state changes after a filter update or option un/registration.
|
|
93
|
+
*
|
|
94
|
+
* - `hidden`: true when all registered options are filtered out (keeps children mounted
|
|
95
|
+
* but invisible to the user and screen readers).
|
|
96
|
+
* - `aria-hidden`: true when the section has no registered options at all (skeleton-only).
|
|
97
|
+
* The section stays visually rendered but is hidden from assistive technology —
|
|
98
|
+
* the live region (`ComboboxState`) handles the loading announcement instead.
|
|
99
|
+
*
|
|
100
|
+
* At most one of `hidden` / `aria-hidden` is true at a time.
|
|
101
|
+
*
|
|
102
|
+
* Returns a cleanup function that unregisters the section.
|
|
103
|
+
*/
|
|
104
|
+
registerSection(element: HTMLElement, onChange: (state: {
|
|
105
|
+
hidden: boolean;
|
|
106
|
+
'aria-hidden': boolean;
|
|
107
|
+
}) => void): () => void;
|
|
108
|
+
/**
|
|
109
|
+
* Register a skeleton placeholder. Increments the internal skeleton counter.
|
|
110
|
+
* When the counter transitions from 0 to >0, fires `loadingChange` immediately
|
|
111
|
+
* and schedules `loadingAnnouncement` after 500ms. Returns a cleanup function
|
|
112
|
+
* that decrements the counter (and fires the reverse transitions when reaching 0).
|
|
113
|
+
*/
|
|
114
|
+
registerSkeleton(): () => void;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Callback invoked when the trigger is attached and the abort controller is ready.
|
|
118
|
+
*
|
|
119
|
+
* The callback can optionally return a mode-specific keydown hook. When returned,
|
|
120
|
+
* it is called before the shared keydown handler; return `true` to indicate the
|
|
121
|
+
* event was handled (the caller will call `stopPropagation`/`preventDefault`)
|
|
122
|
+
* and skip the shared logic.
|
|
123
|
+
*/
|
|
124
|
+
export type OnTriggerAttach = (handle: ComboboxHandle, signal: AbortSignal) => ((event: KeyboardEvent) => boolean) | void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { FocusNavigationController } from '../../utils/focusNavigation';
|
|
2
|
+
import type { OptionRegistration, SectionRegistration } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Get the value for a combobox option element.
|
|
5
|
+
* Uses `data-value` when set; falls back to the element's trimmed `textContent`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getOptionValue(option: HTMLElement): string;
|
|
8
|
+
/** Returns true when an option carries aria-disabled="true". */
|
|
9
|
+
export declare function isOptionDisabled(option: HTMLElement): boolean;
|
|
10
|
+
/** Returns true when the cell is NOT the first gridcell in its row (i.e., it's an action cell). */
|
|
11
|
+
export declare function isActionCell(cell: HTMLElement): boolean;
|
|
12
|
+
/** Predicate matching an option element that carries `aria-selected="true"`. */
|
|
13
|
+
export declare const isSelected: (el: Element) => boolean;
|
|
14
|
+
/** Navigate to the selected option, or to the first option if none is selected. */
|
|
15
|
+
export declare function goToSelectedOrFirst(nav: FocusNavigationController): void;
|
|
16
|
+
/** Navigate to the selected option, or to the last option if none is selected. */
|
|
17
|
+
export declare function goToSelectedOrLast(nav: FocusNavigationController): void;
|
|
18
|
+
/**
|
|
19
|
+
* Compute the current state of a section and notify when it changed.
|
|
20
|
+
*
|
|
21
|
+
* Section state:
|
|
22
|
+
* - `hidden`: true when the section has registered options but all are filtered out.
|
|
23
|
+
* - `aria-hidden`: true when the section has no registered options at all (skeleton-only).
|
|
24
|
+
*
|
|
25
|
+
* At most one of `hidden` / `aria-hidden` is true at a time.
|
|
26
|
+
*/
|
|
27
|
+
export declare function notifySection(sectionElement: HTMLElement, sectionRegistrations: Map<HTMLElement, SectionRegistration>, optionRegistrations: Map<HTMLElement, OptionRegistration>, force?: boolean): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Size } from '../../constants';
|
|
2
|
+
import type { CommonRef, GenericProps, HasAriaDisabled, HasClassName, JSXElement, LumxClassName } from '../../types';
|
|
3
|
+
/** ListItem size variants. */
|
|
4
|
+
export type ListItemSize = Extract<Size, 'tiny' | 'regular' | 'big' | 'huge'>;
|
|
5
|
+
/**
|
|
6
|
+
* Defines the props of the component.
|
|
7
|
+
*/
|
|
8
|
+
export interface ListItemProps extends HasClassName, HasAriaDisabled {
|
|
9
|
+
/** A component to be rendered after the content. */
|
|
10
|
+
after?: JSXElement;
|
|
11
|
+
/** A component to be rendered before the content. */
|
|
12
|
+
before?: JSXElement;
|
|
13
|
+
/** Content. */
|
|
14
|
+
children?: JSXElement;
|
|
15
|
+
/** Whether the list item should be highlighted or not. */
|
|
16
|
+
isHighlighted?: boolean;
|
|
17
|
+
/** Whether the component is selected or not. */
|
|
18
|
+
isSelected?: boolean;
|
|
19
|
+
/** Whether link/button is disabled or not. */
|
|
20
|
+
isDisabled?: boolean;
|
|
21
|
+
/** Custom component for the link (can be used to inject router Link). */
|
|
22
|
+
linkAs?: 'a' | any;
|
|
23
|
+
/** Props that will be passed on to the Link. */
|
|
24
|
+
linkProps?: GenericProps;
|
|
25
|
+
/** Reference to the link element. */
|
|
26
|
+
linkRef?: CommonRef;
|
|
27
|
+
/** Size variant. */
|
|
28
|
+
size?: ListItemSize;
|
|
29
|
+
/** ref to the root <li> element */
|
|
30
|
+
ref?: CommonRef;
|
|
31
|
+
/** On click callback. */
|
|
32
|
+
handleClick?: (event: any) => void;
|
|
33
|
+
}
|
|
34
|
+
export type ListItemPropsToOverride = 'after' | 'before' | 'children' | 'handleClick';
|
|
35
|
+
/**
|
|
36
|
+
* Component display name.
|
|
37
|
+
*/
|
|
38
|
+
export declare const COMPONENT_NAME = "ListItem";
|
|
39
|
+
/**
|
|
40
|
+
* Component default class name and class prefix.
|
|
41
|
+
*/
|
|
42
|
+
export declare const CLASSNAME: LumxClassName<typeof COMPONENT_NAME>;
|
|
43
|
+
/**
|
|
44
|
+
* Component default props.
|
|
45
|
+
*/
|
|
46
|
+
export declare const DEFAULT_PROPS: Partial<ListItemProps>;
|
|
47
|
+
/**
|
|
48
|
+
* ListItem component.
|
|
49
|
+
*
|
|
50
|
+
* @param props Component props.
|
|
51
|
+
* @return JSX element.
|
|
52
|
+
*/
|
|
53
|
+
export declare const ListItem: {
|
|
54
|
+
(props: ListItemProps): import("react").JSX.Element;
|
|
55
|
+
displayName: string;
|
|
56
|
+
className: "lumx-list-item";
|
|
57
|
+
defaultProps: Partial<ListItemProps>;
|
|
58
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { HasClassName } from '../../types';
|
|
2
|
+
import { ClickableElement, RawClickableProps } from '../RawClickable';
|
|
3
|
+
/**
|
|
4
|
+
* ListItemAction props.
|
|
5
|
+
*/
|
|
6
|
+
export type ListItemActionProps<E extends ClickableElement = 'button'> = RawClickableProps<E> & HasClassName;
|
|
7
|
+
/**
|
|
8
|
+
* Component display name.
|
|
9
|
+
*/
|
|
10
|
+
export declare const COMPONENT_NAME = "ListItemAction";
|
|
11
|
+
/**
|
|
12
|
+
* Component classname (used by action area CSS pattern).
|
|
13
|
+
*/
|
|
14
|
+
export declare const CLASSNAME = "lumx-action-area__action";
|
|
15
|
+
export declare const DEFAULT_PROPS: Partial<ListItemActionProps>;
|
|
16
|
+
/**
|
|
17
|
+
* ListItemAction component.
|
|
18
|
+
*
|
|
19
|
+
* Renders a button or link with action area classes.
|
|
20
|
+
* When placed as a child of ListItem, it activates the action area pattern:
|
|
21
|
+
* the entire list item becomes visually clickable, while other interactive
|
|
22
|
+
* elements (in `before`/`after` slots) remain independently clickable.
|
|
23
|
+
*/
|
|
24
|
+
export declare const ListItemAction: <E extends ClickableElement = "button">(props: ListItemActionProps<E>) => import("react").JSX.Element;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Size } from '../../constants';
|
|
2
|
+
import type { CommonRef, HasClassName, JSXElement, LumxClassName } from '../../types';
|
|
3
|
+
/** List item padding size. */
|
|
4
|
+
export type ListItemPadding = Extract<Size, 'big' | 'huge'>;
|
|
5
|
+
/**
|
|
6
|
+
* Defines the props of the component.
|
|
7
|
+
*/
|
|
8
|
+
export interface ListProps extends HasClassName {
|
|
9
|
+
/** List content (should be ListItem, ListDivider, etc.). */
|
|
10
|
+
children?: JSXElement;
|
|
11
|
+
/** Item padding size. */
|
|
12
|
+
itemPadding?: ListItemPadding;
|
|
13
|
+
/** ref to the root element */
|
|
14
|
+
ref?: CommonRef;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Component display name.
|
|
18
|
+
*/
|
|
19
|
+
export declare const COMPONENT_NAME = "List";
|
|
20
|
+
/**
|
|
21
|
+
* Component default class name and class prefix.
|
|
22
|
+
*/
|
|
23
|
+
export declare const CLASSNAME: LumxClassName<typeof COMPONENT_NAME>;
|
|
24
|
+
/**
|
|
25
|
+
* Component default props.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DEFAULT_PROPS: Partial<ListProps>;
|
|
28
|
+
/**
|
|
29
|
+
* List component.
|
|
30
|
+
*
|
|
31
|
+
* @param props Component props.
|
|
32
|
+
* @return JSX element.
|
|
33
|
+
*/
|
|
34
|
+
export declare const List: {
|
|
35
|
+
(props: ListProps): import("react").JSX.Element;
|
|
36
|
+
displayName: string;
|
|
37
|
+
className: "lumx-list";
|
|
38
|
+
defaultProps: Partial<ListProps>;
|
|
39
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SetupOptions } from '../../../testing';
|
|
2
|
+
/**
|
|
3
|
+
* Mounts the component and returns common DOM elements / data needed in multiple tests further down.
|
|
4
|
+
*/
|
|
5
|
+
export declare const setup: (propsOverride: any | undefined, { render, ...options }: SetupOptions<any>) => {
|
|
6
|
+
props: any;
|
|
7
|
+
tab: HTMLElement;
|
|
8
|
+
wrapper: Partial<import("../../../testing").SetupResult>;
|
|
9
|
+
};
|
|
10
|
+
declare const _default: (renderOptions: SetupOptions<any>) => void;
|
|
11
|
+
export default _default;
|
|
@@ -69,7 +69,7 @@ export declare const DEFAULT_PROPS: {};
|
|
|
69
69
|
* @param props Component props.
|
|
70
70
|
* @return Common Props
|
|
71
71
|
*/
|
|
72
|
-
export declare const getTextProps: (props: TextProps) => {
|
|
72
|
+
export declare const getTextProps: (props: Omit<TextProps, "as">) => {
|
|
73
73
|
className: string;
|
|
74
74
|
style: {
|
|
75
75
|
accentColor?: import("csstype").Property.AccentColor | undefined;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** list of generic props defined on JSX that need to be redefined for each framework */
|
|
2
|
-
export type PropsToOverride = 'ref' | 'handleClick' | 'handleChange' | 'handleKeyPress' | 'handleClose' | 'handleBeforeClick' | 'handleAfterClick' | 'handleMouseEnter' | 'handleMouseLeave' | 'handleKeyDown';
|
|
2
|
+
export type PropsToOverride = 'ref' | 'handleClick' | 'handleChange' | 'handleKeyPress' | 'handleClose' | 'handleFocus' | 'handleBeforeClick' | 'handleAfterClick' | 'handleMouseEnter' | 'handleMouseLeave' | 'handleKeyDown';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a TreeWalker that iterates over elements matching a CSS selector
|
|
3
|
+
* within a container.
|
|
4
|
+
*
|
|
5
|
+
* Uses `NodeFilter.SHOW_ELEMENT` and accepts nodes that match the given
|
|
6
|
+
* selector, skipping everything else. The returned walker can be used with
|
|
7
|
+
* `nextNode()` / `previousNode()` for lazy, sequential DOM traversal.
|
|
8
|
+
*
|
|
9
|
+
* @param container The root element to walk within.
|
|
10
|
+
* @param selector CSS selector that items must match.
|
|
11
|
+
* @returns A TreeWalker scoped to the container and filtered by the selector.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createSelectorTreeWalker(container: HTMLElement, selector: string): TreeWalker;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FocusNavigationCallbacks, FocusNavigationController, GridNavigationOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Create a focus navigation controller for a 2D grid.
|
|
4
|
+
*
|
|
5
|
+
* Supports Up/Down between rows (with column memory) and Left/Right between cells
|
|
6
|
+
* (with wrapping across rows).
|
|
7
|
+
*
|
|
8
|
+
* @param options Grid navigation options (container, rowSelector, cellSelector, isRowVisible, wrap).
|
|
9
|
+
* @param callbacks Callbacks for focus state changes.
|
|
10
|
+
* @param signal AbortSignal for cleanup.
|
|
11
|
+
* @returns FocusNavigationController instance.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createGridFocusNavigation(options: GridNavigationOptions, callbacks: FocusNavigationCallbacks, signal: AbortSignal): FocusNavigationController;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export type { FocusNavigationCallbacks, FocusNavigationController, ListNavigationOptions } from './types';
|
|
1
|
+
export type { FocusNavigationCallbacks, FocusNavigationController, GridNavigationOptions, ListNavigationOptions, NavigationOptions, } from './types';
|
|
2
2
|
export type { RovingTabIndexOptions } from './setupRovingTabIndex';
|
|
3
3
|
export { createListFocusNavigation } from './createListFocusNavigation';
|
|
4
|
+
export { createGridFocusNavigation } from './createGridFocusNavigation';
|
|
4
5
|
export { setupRovingTabIndex } from './setupRovingTabIndex';
|
|
@@ -39,10 +39,30 @@ export interface ListNavigationOptions {
|
|
|
39
39
|
*/
|
|
40
40
|
itemActiveSelector?: string;
|
|
41
41
|
}
|
|
42
|
-
/**
|
|
42
|
+
/** Options for 2D grid navigation. */
|
|
43
|
+
export interface GridNavigationOptions {
|
|
44
|
+
type: 'grid';
|
|
45
|
+
/** The container element (grid root) to scan for rows and cells. */
|
|
46
|
+
container: HTMLElement;
|
|
47
|
+
/** CSS selector to identify row elements within the container. */
|
|
48
|
+
rowSelector: string;
|
|
49
|
+
/** CSS selector to identify cell elements within each row. */
|
|
50
|
+
cellSelector: string;
|
|
51
|
+
/**
|
|
52
|
+
* Predicate to determine if a row should be included in navigation.
|
|
53
|
+
* Rows for which this returns `false` are skipped.
|
|
54
|
+
* Default: all rows are visible.
|
|
55
|
+
*/
|
|
56
|
+
isRowVisible?: (row: HTMLElement) => boolean;
|
|
57
|
+
/** Whether navigation wraps at boundaries. Default: false. */
|
|
58
|
+
wrap?: boolean;
|
|
59
|
+
}
|
|
60
|
+
/** Union of all navigation option types. */
|
|
61
|
+
export type NavigationOptions = ListNavigationOptions | GridNavigationOptions;
|
|
62
|
+
/** Focus navigation controller interface — works for both 1D lists and 2D grids. */
|
|
43
63
|
export interface FocusNavigationController {
|
|
44
64
|
/** The navigation structure type. */
|
|
45
|
-
readonly type: 'list';
|
|
65
|
+
readonly type: 'list' | 'grid';
|
|
46
66
|
/** The currently active item, or null if no item is active. */
|
|
47
67
|
readonly activeItem: HTMLElement | null;
|
|
48
68
|
/** Whether an item is currently active. */
|
|
@@ -60,7 +80,8 @@ export interface FocusNavigationController {
|
|
|
60
80
|
goToItem(item: HTMLElement): boolean;
|
|
61
81
|
/**
|
|
62
82
|
* Navigate by offset from the current item.
|
|
63
|
-
*
|
|
83
|
+
* In list mode, moves by items. In grid mode, moves by rows.
|
|
84
|
+
* Positive offsets move forward/down, negative move backward/up.
|
|
64
85
|
*/
|
|
65
86
|
goToOffset(offset: number): boolean;
|
|
66
87
|
/**
|
|
@@ -70,12 +91,12 @@ export interface FocusNavigationController {
|
|
|
70
91
|
goToItemMatching(predicate: (item: HTMLElement) => boolean): boolean;
|
|
71
92
|
/** Clear the active item — no item is active after this call. */
|
|
72
93
|
clear(): void;
|
|
73
|
-
/** Navigate up (previous item in vertical list). */
|
|
94
|
+
/** Navigate up (previous item in vertical list, previous row in grid). */
|
|
74
95
|
goUp(): boolean;
|
|
75
|
-
/** Navigate down (next item in vertical list). */
|
|
96
|
+
/** Navigate down (next item in vertical list, next row in grid). */
|
|
76
97
|
goDown(): boolean;
|
|
77
|
-
/** Navigate left (previous item in horizontal list). */
|
|
98
|
+
/** Navigate left (previous item in horizontal list, previous cell in grid). */
|
|
78
99
|
goLeft(): boolean;
|
|
79
|
-
/** Navigate right (next item in horizontal list). */
|
|
100
|
+
/** Navigate right (next item in horizontal list, next cell in grid). */
|
|
80
101
|
goRight(): boolean;
|
|
81
102
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Typeahead interface. */
|
|
2
|
+
export interface Typeahead {
|
|
3
|
+
/**
|
|
4
|
+
* Handle a printable character keypress.
|
|
5
|
+
* @param key The character typed.
|
|
6
|
+
* @param currentItem The currently active item.
|
|
7
|
+
* @returns The matched item, or null.
|
|
8
|
+
*/
|
|
9
|
+
handle(key: string, currentItem: HTMLElement | null): HTMLElement | null;
|
|
10
|
+
/** Reset the accumulated search string. */
|
|
11
|
+
reset(): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a typeahead controller for keyboard navigation in list-like widgets
|
|
15
|
+
* (combobox, menu, listbox, tree, etc.).
|
|
16
|
+
*
|
|
17
|
+
* Accumulates typed characters and matches them against item labels.
|
|
18
|
+
* Supports single-char cycling (e.g. pressing "a" repeatedly cycles through
|
|
19
|
+
* items starting with "a") and multi-char prefix matching.
|
|
20
|
+
*
|
|
21
|
+
* Uses a {@link TreeWalker} for lazy DOM traversal — items are visited one
|
|
22
|
+
* at a time without materializing the full list.
|
|
23
|
+
*
|
|
24
|
+
* @param getWalker Callback returning a fresh TreeWalker over navigable items, or null if unavailable.
|
|
25
|
+
* @param getItemValue Callback extracting the text label from an item element.
|
|
26
|
+
* @param signal AbortSignal for cleanup.
|
|
27
|
+
* @returns Typeahead instance.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createTypeahead(getWalker: () => TreeWalker | null, getItemValue: (item: HTMLElement) => string, signal: AbortSignal): Typeahead;
|
package/lumx.css
CHANGED
|
@@ -5686,6 +5686,38 @@ table {
|
|
|
5686
5686
|
text-overflow: ellipsis;
|
|
5687
5687
|
}
|
|
5688
5688
|
|
|
5689
|
+
/* ==========================================================================
|
|
5690
|
+
Combobox
|
|
5691
|
+
========================================================================== */
|
|
5692
|
+
.lumx-combobox-popover {
|
|
5693
|
+
overflow-y: auto;
|
|
5694
|
+
}
|
|
5695
|
+
|
|
5696
|
+
.lumx-combobox-popover:empty, .lumx-combobox-popover:not(:has(.lumx-combobox-option)):not(:has(.lumx-combobox-state)):not(:has(.lumx-combobox-option-skeleton)),
|
|
5697
|
+
.lumx-combobox-list:empty,
|
|
5698
|
+
.lumx-combobox-list:not(:has(.lumx-combobox-option)):not(:has(.lumx-combobox-state)):not(:has(.lumx-combobox-option-skeleton)) {
|
|
5699
|
+
display: none;
|
|
5700
|
+
}
|
|
5701
|
+
|
|
5702
|
+
.lumx-combobox-state {
|
|
5703
|
+
text-align: center;
|
|
5704
|
+
}
|
|
5705
|
+
|
|
5706
|
+
.lumx-combobox-option-skeleton .lumx-skeleton-typography {
|
|
5707
|
+
width: min(65%, 200px);
|
|
5708
|
+
}
|
|
5709
|
+
.lumx-combobox-option-skeleton:nth-child(3n+1) .lumx-skeleton-typography {
|
|
5710
|
+
width: min(70%, 230px);
|
|
5711
|
+
}
|
|
5712
|
+
.lumx-combobox-option-skeleton:nth-child(3n+2) .lumx-skeleton-typography {
|
|
5713
|
+
width: min(55%, 170px);
|
|
5714
|
+
}
|
|
5715
|
+
|
|
5716
|
+
.lumx-combobox-option-more-info__popover {
|
|
5717
|
+
max-width: 256px;
|
|
5718
|
+
padding: 16px;
|
|
5719
|
+
}
|
|
5720
|
+
|
|
5689
5721
|
/* ==========================================================================
|
|
5690
5722
|
Comment block
|
|
5691
5723
|
========================================================================== */
|
|
@@ -8785,7 +8817,7 @@ table {
|
|
|
8785
8817
|
color: var(--lumx-color-dark-N);
|
|
8786
8818
|
background-color: transparent;
|
|
8787
8819
|
}
|
|
8788
|
-
.lumx-list-item__link[data-focus-visible-added] {
|
|
8820
|
+
.lumx-list-item__link[data-focus-visible-added], .lumx-list-item__link:has([data-focus-visible-added]) {
|
|
8789
8821
|
outline: 2px solid var(--lumx-color-dark-N);
|
|
8790
8822
|
outline-offset: -2px;
|
|
8791
8823
|
}
|
|
@@ -8882,14 +8914,14 @@ table {
|
|
|
8882
8914
|
|
|
8883
8915
|
/* Section
|
|
8884
8916
|
========================================================================== */
|
|
8885
|
-
.lumx-list-section:not(:first-child):not(.lumx-list-section + .lumx-list-section):not(.lumx-list-divider + .lumx-list-section)::before {
|
|
8917
|
+
.lumx-list-section:not(:first-child):not(.lumx-list-section:not([hidden]) + .lumx-list-section):not(.lumx-list-divider + .lumx-list-section)::before {
|
|
8886
8918
|
content: "";
|
|
8887
8919
|
display: block;
|
|
8888
8920
|
height: 1px;
|
|
8889
8921
|
margin: 8px 0;
|
|
8890
8922
|
background-color: var(--lumx-color-dark-L5);
|
|
8891
8923
|
}
|
|
8892
|
-
.lumx-list-section:not(:last-child):not(:has(+ .lumx-list-divider))::after {
|
|
8924
|
+
.lumx-list-section:not(:last-child):not(:has(+ .lumx-list-divider)):not(:has(+ [hidden]))::after {
|
|
8893
8925
|
content: "";
|
|
8894
8926
|
display: block;
|
|
8895
8927
|
height: 1px;
|
|
@@ -10536,7 +10568,7 @@ table {
|
|
|
10536
10568
|
color: var(--lumx-color-dark-N);
|
|
10537
10569
|
background-color: transparent;
|
|
10538
10570
|
}
|
|
10539
|
-
.lumx-side-navigation-item__link[data-focus-visible-added] {
|
|
10571
|
+
.lumx-side-navigation-item__link[data-focus-visible-added], .lumx-side-navigation-item__link:has([data-focus-visible-added]) {
|
|
10540
10572
|
outline: 2px solid var(--lumx-color-dark-N);
|
|
10541
10573
|
outline-offset: -2px;
|
|
10542
10574
|
}
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@floating-ui/dom": "^1.7.5",
|
|
10
|
-
"@lumx/icons": "^4.9.0-next.
|
|
10
|
+
"@lumx/icons": "^4.9.0-next.6",
|
|
11
11
|
"classnames": "^2.3.2",
|
|
12
12
|
"focus-visible": "^5.0.2",
|
|
13
13
|
"lodash": "4.17.23",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"update-version-changelog": "yarn version-changelog ../../CHANGELOG.md"
|
|
70
70
|
},
|
|
71
71
|
"sideEffects": false,
|
|
72
|
-
"version": "4.9.0-next.
|
|
72
|
+
"version": "4.9.0-next.6",
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
75
75
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
@import "./components/button/index";
|
|
4
4
|
@import "./components/checkbox/index";
|
|
5
5
|
@import "./components/chip/index";
|
|
6
|
+
@import "./components/combobox/index";
|
|
6
7
|
@import "./components/comment-block/index";
|
|
7
8
|
@import "./components/date-picker/index";
|
|
8
9
|
@import "./components/dialog/index";
|
|
@@ -49,4 +50,4 @@
|
|
|
49
50
|
@import "./components/toolbar/index";
|
|
50
51
|
@import "./components/tooltip/index";
|
|
51
52
|
@import "./components/uploader/index";
|
|
52
|
-
@import "./components/user-block/index";
|
|
53
|
+
@import "./components/user-block/index";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
Combobox
|
|
3
|
+
========================================================================== */
|
|
4
|
+
|
|
5
|
+
.#{$lumx-base-prefix}-combobox-popover {
|
|
6
|
+
overflow-y: auto;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Hide popover and list when there is no option, no state, and no skeleton
|
|
10
|
+
.#{$lumx-base-prefix}-combobox-popover,
|
|
11
|
+
.#{$lumx-base-prefix}-combobox-list {
|
|
12
|
+
&:empty,
|
|
13
|
+
&:not(:has(.#{$lumx-base-prefix}-combobox-option)):not(:has(.#{$lumx-base-prefix}-combobox-state)):not(
|
|
14
|
+
:has(.#{$lumx-base-prefix}-combobox-option-skeleton)
|
|
15
|
+
) {
|
|
16
|
+
display: none;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.#{$lumx-base-prefix}-combobox-state {
|
|
21
|
+
text-align: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Skeleton option placeholders — width variation via :nth-child cycling
|
|
25
|
+
.#{$lumx-base-prefix}-combobox-option-skeleton {
|
|
26
|
+
.#{$lumx-base-prefix}-skeleton-typography {
|
|
27
|
+
width: min(65%, 200px);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&:nth-child(3n + 1) .#{$lumx-base-prefix}-skeleton-typography {
|
|
31
|
+
width: min(70%, 230px);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&:nth-child(3n + 2) .#{$lumx-base-prefix}-skeleton-typography {
|
|
35
|
+
width: min(55%, 170px);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.#{$lumx-base-prefix}-combobox-option-more-info {
|
|
40
|
+
&__popover {
|
|
41
|
+
max-width: $lumx-size-xxl;
|
|
42
|
+
padding: $lumx-spacing-unit-big;
|
|
43
|
+
}
|
|
44
|
+
}
|