@lumx/core 4.10.0 → 4.11.0-alpha.1

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.
@@ -4988,21 +4988,10 @@ table {
4988
4988
  /* Selection Chip group
4989
4989
  ========================================================================== */
4990
4990
  .lumx-selection-chip-group {
4991
- max-width: 100%;
4992
- }
4993
- .lumx-selection-chip-group:empty {
4994
- display: none;
4995
- }
4996
- .lumx-selection-chip-group__item {
4997
- display: flex;
4998
- overflow: hidden;
4991
+ align-content: flex-start;
4999
4992
  }
5000
- .lumx-selection-chip-group__chip {
5001
- overflow: hidden;
5002
- }
5003
- .lumx-selection-chip-group__chip .lumx-chip__label {
5004
- overflow: hidden;
5005
- text-overflow: ellipsis;
4993
+ .lumx-selection-chip-group, .lumx-selection-chip-group .lumx-chip, .lumx-selection-chip-group .lumx-chip__label {
4994
+ min-width: 0;
5006
4995
  }
5007
4996
 
5008
4997
  /* ==========================================================================
@@ -5020,6 +5009,22 @@ table {
5020
5009
  }
5021
5010
  .lumx-combobox-popover__scroll {
5022
5011
  overflow-y: auto;
5012
+ scroll-snap-type: y proximity;
5013
+ }
5014
+ @media (prefers-reduced-motion: no-preference) {
5015
+ .lumx-combobox-popover__scroll {
5016
+ scroll-behavior: smooth;
5017
+ }
5018
+ }
5019
+ .lumx-combobox-popover__scroll::before {
5020
+ content: "";
5021
+ display: block;
5022
+ scroll-snap-align: start;
5023
+ }
5024
+ .lumx-combobox-popover__scroll::after {
5025
+ content: "";
5026
+ display: block;
5027
+ scroll-snap-align: end;
5023
5028
  }
5024
5029
 
5025
5030
  .lumx-combobox-popover:empty, .lumx-combobox-popover:not(:has(.lumx-combobox-option)):not(:has(.lumx-combobox-state)):not(:has(.lumx-combobox-option-skeleton)),
@@ -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
+ alertDialog: HTMLElement | null;
8
+ wrapper: Partial<import("../../../testing").SetupResult>;
9
+ };
10
+ declare const _default: (renderOptions: SetupOptions<any>) => void;
11
+ export default _default;
@@ -0,0 +1,56 @@
1
+ import type { LumxClassName, HasClassName, HasTheme } from '../../types';
2
+ import type { Selector } from '../../types/Selector';
3
+ import type { ChipProps } from '.';
4
+ /**
5
+ * Framework-specific components injected by React/Vue wrappers.
6
+ */
7
+ export interface SelectionChipGroupComponents {
8
+ Chip: any;
9
+ ChipGroup: any;
10
+ Icon: any;
11
+ Text: any;
12
+ Tooltip: any;
13
+ }
14
+ /**
15
+ * Props that consumers can override per chip via getChipProps.
16
+ * Excludes internal/framework props that the core always controls.
17
+ */
18
+ export type ChipOverrideProps = Pick<ChipProps, 'isDisabled' | 'color' | 'isHighlighted' | 'isSelected' | 'className' | 'before' | 'after'>;
19
+ /**
20
+ * Defines the props of the component.
21
+ */
22
+ export interface SelectionChipGroupProps<O> extends HasClassName, HasTheme {
23
+ /** Option object id selector (either a property name or a function to get the id) */
24
+ getOptionId: Selector<O>;
25
+ /** Option object name selector. Falls back to the id if not defined */
26
+ getOptionName?: Selector<O, string | undefined | null>;
27
+ /** Selected options array */
28
+ value?: O[];
29
+ /** Label for accessibility (aria-label on the listbox) */
30
+ label: string;
31
+ /** Label for the remove action (visually hidden text appended to each chip) */
32
+ chipRemoveLabel?: string;
33
+ /** Disabled state for all chips */
34
+ isDisabled?: boolean;
35
+ /** Customize chip props per option */
36
+ getChipProps?: (option: O) => Partial<ChipProps>;
37
+ /** Ref for the container element */
38
+ ref?: any;
39
+ }
40
+ /**
41
+ * Component display name.
42
+ */
43
+ export declare const COMPONENT_NAME = "SelectionChipGroup";
44
+ /**
45
+ * Component default class name and class prefix.
46
+ */
47
+ export declare const CLASSNAME: LumxClassName<typeof COMPONENT_NAME>;
48
+ /**
49
+ * SelectionChipGroup component.
50
+ * Renders a listbox of removable selection chips using component injection for framework-agnostic rendering.
51
+ *
52
+ * @param props Component props.
53
+ * @param components Framework-specific components injected by the wrapper (Chip, ChipGroup, Icon, Tooltip).
54
+ * @return JSX element, or null when value is empty.
55
+ */
56
+ export declare const SelectionChipGroup: <O>(props: SelectionChipGroupProps<O>, { Chip, ChipGroup, Icon, Text, Tooltip }: SelectionChipGroupComponents) => import("react").JSX.Element | null;
@@ -0,0 +1,18 @@
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
+ selectionChipGroup: HTMLElement | null;
8
+ wrapper: Partial<import("../../../testing").SetupResult>;
9
+ };
10
+ interface CoreTestOptions extends SetupOptions<any> {
11
+ /**
12
+ * Render a stateful SelectionChipGroup that updates its own value on change.
13
+ * When provided, enables roving tabindex recovery tests.
14
+ */
15
+ renderStateful?: () => void;
16
+ }
17
+ declare const _default: (renderOptions: CoreTestOptions) => void;
18
+ export default _default;
@@ -0,0 +1,81 @@
1
+ import { ColorPalette, Size } from '../../constants';
2
+ import { HasTheme, HasAriaDisabled, JSXElement, LumxClassName, HasClassName, HasDisabled, CommonRef, GenericProps } from '../../types';
3
+ /**
4
+ * Chip sizes.
5
+ */
6
+ export type ChipSize = Extract<Size, 's' | 'm'>;
7
+ /**
8
+ * Defines the props of the component.
9
+ */
10
+ export interface ChipProps extends HasClassName, HasDisabled, HasTheme, HasAriaDisabled {
11
+ /** A component to be rendered after the content. */
12
+ after?: JSXElement;
13
+ /** A component to be rendered before the content. */
14
+ before?: JSXElement;
15
+ /** Color variant. */
16
+ color?: ColorPalette;
17
+ /** Whether the component is clickable or not. */
18
+ isClickable?: boolean;
19
+ /** Whether the chip is currently in a highlighted state or not. */
20
+ isHighlighted?: boolean;
21
+ /** Whether the component is selected or not. */
22
+ isSelected?: boolean;
23
+ /** Size variant. */
24
+ size?: ChipSize;
25
+ /** href for the chip if it is a link */
26
+ href?: string;
27
+ /** reference to the root element */
28
+ ref?: CommonRef;
29
+ /** On "after" element clicked callback. */
30
+ handleAfterClick?: (event: any) => void;
31
+ /** On element key down callback. */
32
+ handleKeyDown?: (event: any) => void;
33
+ /** On "before" element clicked callback. */
34
+ handleBeforeClick?: (event: any) => void;
35
+ /** On element clicked callback. */
36
+ handleClick?: (event: any) => void;
37
+ /** name of the prop for handling key down events */
38
+ keyDownProp?: string;
39
+ /** name of the prop for tab index */
40
+ tabIndexProp?: string;
41
+ /** Children */
42
+ children?: JSXElement;
43
+ /** Props to apply when the component is in a disabled state. */
44
+ disabledStateProps: GenericProps;
45
+ /** Whether the "after" element has a click handler. */
46
+ hasAfterClick?: boolean;
47
+ /** Whether the "before" element has a click handler. */
48
+ hasBeforeClick?: boolean;
49
+ /** Whether the component has a main click handler. */
50
+ hasOnClick?: boolean;
51
+ /** Whether any part of the component is disabled. */
52
+ isAnyDisabled?: boolean;
53
+ }
54
+ export type ChipPropsToOverride = 'disabledStateProps' | 'hasOnClick' | 'hasBeforeClick' | 'hasAfterClick' | 'before' | 'after' | 'keyDownProp' | 'tabIndexProp' | 'isAnyDisabled';
55
+ /**
56
+ * Component display name.
57
+ */
58
+ export declare const COMPONENT_NAME = "Chip";
59
+ /**
60
+ * Component default class name and class prefix.
61
+ */
62
+ export declare const CLASSNAME: LumxClassName<typeof COMPONENT_NAME>;
63
+ export declare const block: {
64
+ (additionalClasses: import("classnames/types").ClassValue[]): string;
65
+ (modifiers?: import("../../utils/classNames/bem/modifier").Modifier, additionalClasses?: import("classnames/types").ClassValue[]): string;
66
+ }, element: {
67
+ (elem: string, additionalClasses: import("classnames/types").ClassValue[]): string;
68
+ (elem: string, modifiers?: import("../../utils/classNames/bem/modifier").Modifier, additionalClasses?: import("classnames/types").ClassValue[]): string;
69
+ };
70
+ /**
71
+ * Component default props.
72
+ */
73
+ export declare const DEFAULT_PROPS: Partial<ChipProps>;
74
+ /**
75
+ * Chip component.
76
+ *
77
+ * @param props Component props.
78
+ * @param ref Component ref.
79
+ * @return React element.
80
+ */
81
+ export declare const Chip: (props: ChipProps) => import("react").JSX.Element;
@@ -0,0 +1,28 @@
1
+ import type { Selector } from '../../types/Selector';
2
+ /**
3
+ * Options for setting up selection chip group event handlers.
4
+ * All option accessors are wrapped in getters so that React/Vue can provide
5
+ * reactive values without the core needing to know about reactivity systems.
6
+ */
7
+ export interface SetupSelectionChipGroupEventsOptions<O> {
8
+ /** Getter for the current selected options array. */
9
+ getValue: () => O[] | undefined;
10
+ /** Getter for the option id selector. */
11
+ getOptionId: Selector<O>;
12
+ /** Callback when the option array changes. */
13
+ onChange: (newValue?: O[]) => void;
14
+ /** Getter for the chip group container element. */
15
+ getContainer: () => HTMLElement | null | undefined;
16
+ /** Getter for the associated input element (optional). */
17
+ getInput?: () => HTMLInputElement | null | undefined;
18
+ }
19
+ /**
20
+ * Attach delegated click and keydown event listeners on the chip group container,
21
+ * and optionally an input backspace handler.
22
+ *
23
+ * Uses addEventListener on the container element. Events bubble up from individual
24
+ * chips, so there is no need to re-attach listeners when chips change.
25
+ *
26
+ * @returns A cleanup function that removes all attached event listeners.
27
+ */
28
+ export declare function setupSelectionChipGroupEvents<O>(options: SetupSelectionChipGroupEventsOptions<O>): () => void;
@@ -17,6 +17,11 @@ export interface ComboboxInputProps extends HasClassName, HasTheme {
17
17
  toggleButtonProps?: Record<string, any>;
18
18
  /** Toggle callback for the chevron button. */
19
19
  handleToggle?(): void;
20
+ /**
21
+ * Controls how the combobox filters options as the user types.
22
+ * When `'off'`, the input is rendered as `readOnly`.
23
+ */
24
+ filter?: 'auto' | 'manual' | 'off';
20
25
  }
21
26
  /**
22
27
  * Injected framework-specific components for ComboboxInput rendering.
@@ -2,14 +2,22 @@ import type { ComboboxCallbacks, ComboboxHandle } from './types';
2
2
  /** Options for configuring the input-mode combobox controller. */
3
3
  export interface SetupComboboxInputOptions extends ComboboxCallbacks {
4
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.
5
+ * Controls how the combobox filters options as the user types.
8
6
  *
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.
7
+ * - `'auto'` (default) Options are automatically filtered client-side.
8
+ * - `'manual'` Filtering is the consumer's responsibility.
9
+ * - `'off'` — Like `'manual'`, and `openOnFocus` defaults to `true`.
10
+ * The core template renders the input as `readOnly`.
11
11
  */
12
- autoFilter?: boolean;
12
+ filter?: 'auto' | 'manual' | 'off';
13
+ /**
14
+ * When true, the combobox opens automatically when the input receives focus.
15
+ * When false (default, unless `filter` is `'off'`), the combobox only opens
16
+ * on click, typing, or keyboard navigation.
17
+ *
18
+ * @default false (true when filter is 'off')
19
+ */
20
+ openOnFocus?: boolean;
13
21
  }
14
22
  /**
15
23
  * Set up a combobox with an input trigger (autocomplete/filter pattern).
@@ -0,0 +1,12 @@
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
+ dialog: HTMLElement | null;
8
+ container: HTMLElement | null;
9
+ wrapper: Partial<import("../../../testing").SetupResult>;
10
+ };
11
+ declare const _default: (renderOptions: SetupOptions<any>) => void;
12
+ export default _default;
@@ -9,6 +9,8 @@ export interface InputLabelProps extends HasClassName, HasTheme {
9
9
  children: JSXElement;
10
10
  /** Native htmlFor property. */
11
11
  htmlFor: string;
12
+ /** Native id property. */
13
+ id?: string;
12
14
  /** Whether the component is required or not. */
13
15
  isRequired?: boolean;
14
16
  /** ref to the root element */
@@ -0,0 +1,8 @@
1
+ type EventCallback = (evt?: Event) => void;
2
+ /**
3
+ * Sets up an IntersectionObserver on the given element.
4
+ * Calls `callback` when at least one observed entry is intersecting.
5
+ * Returns a cleanup function that unobserves the element.
6
+ */
7
+ export declare function setupInfiniteScrollObserver(element: Element, callback: EventCallback, options?: IntersectionObserverInit): () => void;
8
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Like `querySelectorAll`, but also tests the root node itself.
3
+ *
4
+ * Yields the root element first (if it matches), then all matching descendants
5
+ * in document order. Being a generator, callers that only need the first match
6
+ * can break early without collecting the full list.
7
+ *
8
+ * @param node The starting DOM node.
9
+ * @param selector CSS selector to match against.
10
+ */
11
+ export declare function querySelectorInclusive(node: Node, selector: string): Generator<HTMLElement>;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Track whether the container currently has focus.
3
+ *
4
+ * We can't rely on `document.activeElement` inside MutationObserver callbacks because
5
+ * the browser moves focus to `<body>` before they fire. focusout with `relatedTarget === null`
6
+ * (element removed from DOM) keeps the flag true so the observer can move focus to a fallback.
7
+ */
8
+ export declare function trackContainerFocus(container: HTMLElement, signal: AbortSignal): {
9
+ readonly hasFocus: boolean;
10
+ reset(): void;
11
+ };
@@ -1,10 +1,15 @@
1
- import type { FocusNavigationCallbacks, FocusNavigationController, ListNavigationOptions } from './types';
1
+ import type { FocusNavigationCallbacks, ListFocusNavigationController, ListNavigationOptions } from './types';
2
2
  /**
3
3
  * Create a focus navigation controller for a 1D list.
4
4
  *
5
- * @param options List navigation options (container, itemSelector, direction, wrap).
5
+ * This controller is **stateless** it does not maintain an internal reference to
6
+ * the active item. Instead it reads the active item from the DOM each time via the
7
+ * `getActiveItem` callback provided in the options. This avoids any desync between
8
+ * the controller's internal state and the actual DOM.
9
+ *
10
+ * @param options List navigation options (container, itemSelector, direction, wrap, getActiveItem).
6
11
  * @param callbacks Callbacks for focus state changes.
7
12
  * @param signal AbortSignal for cleanup.
8
- * @returns FocusNavigationController instance.
13
+ * @returns ListFocusNavigationController instance.
9
14
  */
10
- export declare function createListFocusNavigation(options: ListNavigationOptions, callbacks: FocusNavigationCallbacks, signal: AbortSignal): FocusNavigationController;
15
+ export declare function createListFocusNavigation(options: ListNavigationOptions, callbacks: FocusNavigationCallbacks, signal: AbortSignal): ListFocusNavigationController;
@@ -1,4 +1,4 @@
1
- export type { FocusNavigationCallbacks, FocusNavigationController, GridNavigationOptions, ListNavigationOptions, NavigationOptions, } from './types';
1
+ export type { FocusNavigationCallbacks, FocusNavigationController, GridNavigationOptions, ListFocusNavigationController, ListNavigationOptions, NavigationOptions, } from './types';
2
2
  export type { RovingTabIndexOptions } from './setupRovingTabIndex';
3
3
  export { createListFocusNavigation } from './createListFocusNavigation';
4
4
  export { createGridFocusNavigation } from './createGridFocusNavigation';
@@ -1,42 +1,37 @@
1
1
  import type { FocusNavigationController } from './types';
2
- /**
3
- * Options for the roving tabindex setup.
4
- */
2
+ /** Options for {@link setupRovingTabIndex}. */
5
3
  export interface RovingTabIndexOptions {
6
- /** The container element holding the focusable items. */
4
+ /** Container element holding the focusable items. */
7
5
  container: HTMLElement;
8
- /** CSS selector to identify focusable items within the container. */
6
+ /** CSS selector identifying focusable items within the container. */
9
7
  itemSelector: string;
10
8
  /**
11
- * Primary navigation axis — determines which arrow keys navigate.
12
- * - `'horizontal'` (default): Left/Right navigate, Up/Down are no-ops.
13
- * - `'vertical'`: Up/Down navigate, Left/Right are no-ops.
9
+ * Navigation axis — determines which arrow keys navigate.
10
+ * - `'horizontal'` (default): Left/Right navigate.
11
+ * - `'vertical'`: Up/Down navigate.
14
12
  */
15
13
  direction?: 'horizontal' | 'vertical';
14
+ /** CSS selector matching disabled items (skipped during navigation). */
15
+ itemDisabledSelector?: string;
16
16
  /**
17
- * CSS selector matching disabled items within the container.
18
- * Disabled items are skipped during keyboard navigation.
17
+ * Attribute name indicating the selected item (e.g. `'aria-selected'`, `'aria-checked'`).
18
+ * When set, the roving tabindex will observe changes to this attribute and keep
19
+ * `tabindex="0"` in sync with the item whose attribute value is `"true"`.
20
+ * Default: `'aria-selected'`.
19
21
  */
20
- itemDisabledSelector?: string;
21
- /** Callback invoked when an item receives focus via keyboard navigation. */
22
+ itemSelectedAttr?: string;
23
+ /** Called when an item receives focus via keyboard navigation. */
22
24
  onItemFocused?: (item: HTMLElement) => void;
23
25
  }
24
26
  /**
25
27
  * Set up the roving tabindex pattern on a container element.
26
28
  *
27
- * Handles:
28
- * - Keyboard navigation (Arrow keys, Home, End) via a keydown listener on the container
29
- * - tabindex management on focus changes (`0` on active, `-1` on inactive)
30
- * - Calling `.focus()` on the newly active item
31
- *
32
- * The consumer is responsible for setting the initial tabindex values on items
33
- * (`tabindex="0"` on the active item, `tabindex="-1"` on the rest). On setup, the item
34
- * with `tabindex="0"` is silently adopted as the initial active item.
35
- *
36
- * The setup is torn down when the provided `signal` is aborted.
29
+ * - Keyboard navigation (Arrow keys, Home, End)
30
+ * - tabindex management (`0` on active, `-1` on others)
31
+ * - Mount normalization (single-tabstop invariant)
32
+ * - DOM mutation handling via MutationObserver:
33
+ * removal recovery, insertion normalization, disabled-state changes
37
34
  *
38
- * @param options Roving tabindex configuration.
39
- * @param signal AbortSignal for teardown.
40
- * @returns The underlying {@link FocusNavigationController} for programmatic access.
35
+ * Torn down when `signal` is aborted.
41
36
  */
42
37
  export declare function setupRovingTabIndex(options: RovingTabIndexOptions, signal: AbortSignal): FocusNavigationController;
@@ -33,11 +33,18 @@ export interface ListNavigationOptions {
33
33
  */
34
34
  itemDisabledSelector?: string;
35
35
  /**
36
- * CSS selector identifying the initially active item within the container.
37
- * If an item matching this selector is found on setup, it becomes the active item
38
- * without triggering focus or activation callbacks (silent init from DOM state).
36
+ * Callback returning the currently active item from the DOM.
37
+ *
38
+ * The list navigation controller does **not** maintain an internal active-item
39
+ * reference; instead it delegates to this callback every time it needs the current
40
+ * active item (e.g. before navigating by offset).
41
+ *
42
+ * The callback is expected to read from the DOM (e.g. query `[tabindex="0"]` for
43
+ * roving tabindex, or read `aria-activedescendant` for combobox patterns).
44
+ *
45
+ * Default: `() => null` (no active item).
39
46
  */
40
- itemActiveSelector?: string;
47
+ getActiveItem?: () => HTMLElement | null;
41
48
  }
42
49
  /** Options for 2D grid navigation. */
43
50
  export interface GridNavigationOptions {
@@ -100,3 +107,20 @@ export interface FocusNavigationController {
100
107
  /** Navigate right (next item in horizontal list, next cell in grid). */
101
108
  goRight(): boolean;
102
109
  }
110
+ /**
111
+ * Extended controller for 1D list navigation.
112
+ * Adds list-specific methods that don't apply to grid navigation.
113
+ */
114
+ export interface ListFocusNavigationController extends FocusNavigationController {
115
+ /** Combined CSS selector matching enabled (non-disabled) items. */
116
+ readonly enabledItemSelector: string;
117
+ /**
118
+ * Find the nearest enabled item to the given anchor node.
119
+ * The anchor does not need to be an item or even an HTMLElement — it's used
120
+ * as a positional reference to find the closest enabled item in DOM order.
121
+ * Prefers the next item; falls back to the previous item.
122
+ *
123
+ * @returns The nearest enabled item, or null if no enabled items exist.
124
+ */
125
+ findNearestEnabled(anchor: Node): HTMLElement | null;
126
+ }
package/lumx.css CHANGED
@@ -5669,21 +5669,10 @@ table {
5669
5669
  /* Selection Chip group
5670
5670
  ========================================================================== */
5671
5671
  .lumx-selection-chip-group {
5672
- max-width: 100%;
5673
- }
5674
- .lumx-selection-chip-group:empty {
5675
- display: none;
5676
- }
5677
- .lumx-selection-chip-group__item {
5678
- display: flex;
5679
- overflow: hidden;
5672
+ align-content: flex-start;
5680
5673
  }
5681
- .lumx-selection-chip-group__chip {
5682
- overflow: hidden;
5683
- }
5684
- .lumx-selection-chip-group__chip .lumx-chip__label {
5685
- overflow: hidden;
5686
- text-overflow: ellipsis;
5674
+ .lumx-selection-chip-group, .lumx-selection-chip-group .lumx-chip, .lumx-selection-chip-group .lumx-chip__label {
5675
+ min-width: 0;
5687
5676
  }
5688
5677
 
5689
5678
  /* ==========================================================================
@@ -5701,6 +5690,22 @@ table {
5701
5690
  }
5702
5691
  .lumx-combobox-popover__scroll {
5703
5692
  overflow-y: auto;
5693
+ scroll-snap-type: y proximity;
5694
+ }
5695
+ @media (prefers-reduced-motion: no-preference) {
5696
+ .lumx-combobox-popover__scroll {
5697
+ scroll-behavior: smooth;
5698
+ }
5699
+ }
5700
+ .lumx-combobox-popover__scroll::before {
5701
+ content: "";
5702
+ display: block;
5703
+ scroll-snap-align: start;
5704
+ }
5705
+ .lumx-combobox-popover__scroll::after {
5706
+ content: "";
5707
+ display: block;
5708
+ scroll-snap-align: end;
5704
5709
  }
5705
5710
 
5706
5711
  .lumx-combobox-popover:empty, .lumx-combobox-popover:not(:has(.lumx-combobox-option)):not(:has(.lumx-combobox-state)):not(:has(.lumx-combobox-option-skeleton)),
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.10.0",
10
+ "@lumx/icons": "^4.11.0-alpha.1",
11
11
  "classnames": "^2.3.2",
12
12
  "focus-visible": "^5.0.2",
13
13
  "lodash": "4.18.1",
@@ -69,7 +69,7 @@
69
69
  "update-version-changelog": "yarn version-changelog ../../CHANGELOG.md"
70
70
  },
71
71
  "sideEffects": false,
72
- "version": "4.10.0",
72
+ "version": "4.11.0-alpha.1",
73
73
  "devDependencies": {
74
74
  "@rollup/plugin-typescript": "^12.3.0",
75
75
  "@testing-library/dom": "^10.4.1",
@@ -92,5 +92,6 @@
92
92
  "vite": "^7.3.1",
93
93
  "vite-tsconfig-paths": "^5.1.4",
94
94
  "vitest": "^4.0.18"
95
- }
95
+ },
96
+ "stableVersion": "4.10.0"
96
97
  }
@@ -191,23 +191,10 @@
191
191
  ========================================================================== */
192
192
 
193
193
  .#{$lumx-base-prefix}-selection-chip-group {
194
- max-width: 100%;
194
+ align-content: flex-start;
195
195
 
196
- &:empty {
197
- display: none;
198
- }
199
-
200
- &__item {
201
- display: flex;
202
- overflow: hidden;
203
- }
204
-
205
- &__chip {
206
- overflow: hidden;
207
-
208
- .#{$lumx-base-prefix}-chip__label {
209
- overflow: hidden;
210
- text-overflow: ellipsis;
196
+ // Shrink chips in constrained space
197
+ &, & .#{$lumx-base-prefix}-chip, & .#{$lumx-base-prefix}-chip__label {
198
+ min-width: 0;
211
199
  }
212
- }
213
200
  }
@@ -18,6 +18,21 @@
18
18
 
19
19
  &__scroll {
20
20
  overflow-y: auto;
21
+ scroll-snap-type: y proximity;
22
+ @media (prefers-reduced-motion: no-preference) {
23
+ scroll-behavior: smooth;
24
+ }
25
+
26
+ &::before {
27
+ content: '';
28
+ display: block;
29
+ scroll-snap-align: start;
30
+ }
31
+ &::after {
32
+ content: '';
33
+ display: block;
34
+ scroll-snap-align: end;
35
+ }
21
36
  }
22
37
  }
23
38