@lumx/core 4.11.0-next.9 → 4.12.0-alpha.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.
@@ -9572,6 +9572,7 @@ table {
9572
9572
  align-items: center;
9573
9573
  margin: calc((var(--lumx-text-field-input-min-height) - var(--lumx-size-s) - 6px) / 2 + 3px) 0;
9574
9574
  gap: 6px;
9575
+ min-width: 0;
9575
9576
  }
9576
9577
  .lumx-select--theme-light .lumx-select__input-native {
9577
9578
  display: block;
@@ -11655,6 +11656,7 @@ table {
11655
11656
  align-items: center;
11656
11657
  margin: calc((var(--lumx-text-field-input-min-height) - var(--lumx-size-s) - 6px) / 2 + 3px) 0;
11657
11658
  gap: 6px;
11659
+ min-width: 0;
11658
11660
  }
11659
11661
  .lumx-text-field__helper {
11660
11662
  margin-top: 8px;
@@ -18,6 +18,14 @@ export interface ComboboxStateProps {
18
18
  * When omitted, the empty state is not shown.
19
19
  */
20
20
  emptyMessage?: string | ((inputValue: string) => string);
21
+ /**
22
+ * Message callback to display the number of available options.
23
+ * Called with the current visible option count and should return a human-readable string
24
+ * (e.g. `(n) => \`${n} result(s) available\``).
25
+ * Displayed when the combobox is open, not empty, not loading, and not in error.
26
+ * When omitted, no option count message is shown.
27
+ */
28
+ nbOptionMessage?: (optionsLength: number) => string;
21
29
  /**
22
30
  * Error state title message.
23
31
  * When provided, the error state is active (takes priority over the empty state).
@@ -39,10 +47,10 @@ export interface ComboboxStateProps {
39
47
  */
40
48
  state?: {
41
49
  /**
42
- * Whether the list currently has no visible options.
43
- * Driven by the framework wrapper via the combobox handle's `emptyChange` event.
50
+ * The number of currently visible options.
51
+ * Driven by the framework wrapper via the combobox handle's `optionsChange` event.
44
52
  */
45
- isEmpty?: boolean;
53
+ optionsLength?: number;
46
54
  /**
47
55
  * The current value of the combobox input.
48
56
  * Passed to `emptyMessage` when it is a function.
@@ -76,12 +84,13 @@ export interface ComboboxStateComponents {
76
84
  }
77
85
  /**
78
86
  * ComboboxState core template.
79
- * Renders empty/error state messages inside the combobox popover.
87
+ * Renders empty/error/option-count state messages inside the combobox popover.
80
88
  * The block itself acts as a screen reader live region (`role="status" aria-live="polite"`).
81
89
  *
82
90
  * Activation rules:
83
91
  * - Error state: active when `errorMessage` is provided (presence-based).
84
- * - Empty state: active when `isEmpty` is true and `emptyMessage` is provided, and there is no error.
92
+ * - Empty state: active when `optionsLength` is 0 and `emptyMessage` is provided, and there is no error.
93
+ * - Option count: active when `nbOptionMessage` is provided, the list is not empty, not loading, and not in error.
85
94
  *
86
95
  * @param props Component props.
87
96
  * @param components Injected framework-specific components.
@@ -21,11 +21,11 @@ export interface ComboboxEventMap {
21
21
  /** Fired when the active descendant changes (visual focus). Payload: the option id or null. */
22
22
  activeDescendantChange: string | null;
23
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.
24
+ * Fired when the visible option count changes.
25
+ * Payload: the number of visible options plus the current input value.
26
26
  */
27
- emptyChange: {
28
- isEmpty?: boolean;
27
+ optionsChange: {
28
+ optionsLength: number;
29
29
  inputValue?: string;
30
30
  } | undefined;
31
31
  /**
@@ -86,6 +86,13 @@ export interface ComboboxHandle {
86
86
  * An empty filter value clears filtering (all options become visible).
87
87
  */
88
88
  setFilter(filterValue: string): void;
89
+ /**
90
+ * Re-evaluate the filter state of a single registered option.
91
+ * Call this after the option's `data-value` or textContent has been updated
92
+ * (e.g. after a framework re-render) to ensure its filtered/visible state
93
+ * is consistent with the current filter value.
94
+ */
95
+ refilterOption(element: HTMLElement): void;
89
96
  /**
90
97
  * Register a section DOM element for state notifications.
91
98
  * The callback is invoked immediately with the current state, and again whenever
@@ -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
+ lightbox: HTMLElement | null;
8
+ wrapper: Partial<import("../../../testing").SetupResult>;
9
+ };
10
+ declare const _default: (renderOptions: SetupOptions<any>) => void;
11
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import type { Selector } from '../../types';
2
+ /**
3
+ * Get the display name for a single option value.
4
+ *
5
+ * Resolves the option's display name by trying `getOptionName` first,
6
+ * then falling back to `getOptionId`, returning `''` for nullish values.
7
+ */
8
+ export declare function getOptionDisplayName<O>(value: O | undefined, getOptionName?: Selector<O, string | undefined | null>, getOptionId?: Selector<O>): string;
@@ -0,0 +1,196 @@
1
+ import type { HasAriaDisabled } from '../../types/HasAriaDisabled';
2
+ import type { HasTheme } from '../../types/HasTheme';
3
+ import type { JSXElement, Selector } from '../../types';
4
+ /**
5
+ * Status of the SelectTextField dropdown list.
6
+ *
7
+ * - `'idle'` — Default state, no loading indicators.
8
+ * - `'loading'` — Full loading: shows skeleton placeholders, hides real options.
9
+ * - `'loadingMore'` — Paginated loading: appends a skeleton after existing options.
10
+ * - `'error'` — Error state: shows an error message in the dropdown.
11
+ */
12
+ export type SelectTextFieldStatus = 'idle' | 'loading' | 'loadingMore' | 'error';
13
+ /**
14
+ * Context passed to the `renderOption` callback alongside the option object.
15
+ * Contains core-computed values that the consumer should forward to `<Combobox.Option>`.
16
+ */
17
+ export interface RenderOptionContext {
18
+ /** Index of the option in the current (possibly section-filtered) list. */
19
+ index: number;
20
+ /** Resolved option id (from `getOptionId`). Should be passed as `value` to `<Combobox.Option>`. */
21
+ value: any;
22
+ /** Whether this option is currently selected. Should be forwarded as `isSelected`. */
23
+ isSelected: boolean;
24
+ /** Resolved description string (from `getOptionDescription`), if any. Should be forwarded as `description`. */
25
+ description?: string | null;
26
+ }
27
+ export interface BaseSelectProps<O> {
28
+ /** List of option objects. */
29
+ options?: Array<O>;
30
+ /** Option object id selector. */
31
+ getOptionId: Selector<O>;
32
+ /** Option object name selector (falls back to id if not defined). */
33
+ getOptionName?: Selector<O, string | undefined | null>;
34
+ /** Option object description selector. */
35
+ getOptionDescription?: Selector<O, string | undefined | null>;
36
+ /**
37
+ * Custom option render function (core/Vue contract).
38
+ * Receives the option object and a `RenderOptionContext` with core-computed props (`value`,
39
+ * `isSelected`, `description`, `index`). The callee must render a `<Combobox.Option>` and
40
+ * forward those context values, including a unique `key`.
41
+ *
42
+ * @example (Vue / core level)
43
+ * renderOption={(fruit, { value, isSelected, description }) => (
44
+ * <Combobox.Option key={value} value={value} isSelected={isSelected} description={description}>
45
+ * <strong>{fruit.name}</strong>
46
+ * </Combobox.Option>
47
+ * )}
48
+ */
49
+ renderOption?: (option: O, context: RenderOptionContext) => JSXElement;
50
+ /**
51
+ * Selector returning a section id string for each option. Options with the same
52
+ * section id are grouped together. The id is also used as the default displayed
53
+ * label unless `renderSectionTitle` is provided.
54
+ */
55
+ getSectionId?: Selector<O, string>;
56
+ /**
57
+ * Custom section title render function. Receives the section id and the options
58
+ * in that section. Returns custom JSX to display as the section header.
59
+ * When not provided, the section id is used as a plain text label.
60
+ */
61
+ renderSectionTitle?: (sectionId: string, options: O[]) => JSXElement;
62
+ }
63
+ export interface BaseSelectComponents {
64
+ /** Combobox compound component. */
65
+ Combobox: {
66
+ Provider: any;
67
+ Button: any;
68
+ Input: any;
69
+ Popover: any;
70
+ List: any;
71
+ Section: any;
72
+ Option: any;
73
+ State: any;
74
+ OptionSkeleton: any;
75
+ };
76
+ /** Framework-specific InfiniteScroll component (handles IntersectionObserver lifecycle). */
77
+ InfiniteScroll?: any;
78
+ }
79
+ /**
80
+ * Props for rendering select options.
81
+ */
82
+ export interface RenderSelectOptionsProps<O> extends BaseSelectProps<O> {
83
+ /** Selected option (single) or options (multiple). */
84
+ selected?: O | O[];
85
+ }
86
+ /**
87
+ * Shared translation labels for SelectTextField wrappers (React and Vue).
88
+ */
89
+ export interface SelectTextFieldTranslations {
90
+ /** Accessible label for the clear button. */
91
+ clearLabel?: string;
92
+ /** Accessible label for the show-suggestions toggle button. When omitted, the toggle button is not rendered. */
93
+ showSuggestionsLabel?: string;
94
+ /** Accessible label for the chip group. */
95
+ chipGroupLabel?: string;
96
+ /** Accessible label for the remove action on chips (used in visually hidden text). */
97
+ chipRemoveLabel?: string;
98
+ /** Screen reader loading announcement (e.g. "Loading…"). */
99
+ loadingMessage?: string;
100
+ /**
101
+ * Message to display when the list has no visible options.
102
+ * Can be a plain string or a function receiving the current input value (for dynamic messages).
103
+ * When omitted, the empty state is not shown.
104
+ */
105
+ emptyMessage?: string | ((inputValue: string) => string);
106
+ /**
107
+ * Message callback to display the number of available options.
108
+ * Called with the current visible option count and should return a human-readable string
109
+ * (e.g. `(n) => \`${n} result(s) available\``).
110
+ * Displayed when the combobox is open, not empty, not loading, and not in error.
111
+ * When omitted, no option count message is shown.
112
+ */
113
+ nbOptionMessage?: (optionsLength: number) => string;
114
+ /** Error title displayed in the dropdown (e.g. "Failed to load"). */
115
+ errorMessage?: string;
116
+ /** Secondary error message (e.g. "Please try again"). */
117
+ errorTryReloadMessage?: string;
118
+ }
119
+ /**
120
+ * Wrapper-level props shared between React and Vue SelectTextField implementations.
121
+ * These are framework-specific concerns (not part of the core template) that both
122
+ * wrappers need — extracted here to avoid duplication.
123
+ */
124
+ export interface BaseSelectTextFieldWrapperProps<O> extends Pick<BaseSelectProps<O>, 'options' | 'getOptionId' | 'getOptionName' | 'getOptionDescription' | 'getSectionId'>, HasAriaDisabled, HasTheme {
125
+ /** Selection type: 'single' or 'multiple'. */
126
+ selectionType: 'single' | 'multiple';
127
+ /**
128
+ * Status of the dropdown list.
129
+ * @default 'idle'
130
+ */
131
+ listStatus?: SelectTextFieldStatus;
132
+ /**
133
+ * Controls how the combobox filters options as the user types.
134
+ *
135
+ * - `'auto'` — Options that do not match the input value are hidden client-side.
136
+ * - `'manual'` — All options remain visible; filtering is the consumer's
137
+ * responsibility (e.g. by updating the `options` prop in response to `onSearch`).
138
+ * - `'off'` — Like `'manual'`, but the input is also set to `readOnly` and
139
+ * `openOnFocus` defaults to `true` (useful for static dropdowns / pure pickers).
140
+ *
141
+ * This prop is independent of `onSearch`: you can use both together (e.g. client-side
142
+ * filtering + tracking search text for a "create" action), or use `filter: 'manual'`
143
+ * without `onSearch` for static dropdowns.
144
+ */
145
+ filter: 'auto' | 'manual' | 'off';
146
+ /**
147
+ * Controlled search input value.
148
+ * When provided, this value seeds (and resets) the visible search text in the input.
149
+ * Setting it to `''` (empty string) resets the input.
150
+ */
151
+ searchInputValue?: string;
152
+ /**
153
+ * Whether to show a clear button when a value is selected.
154
+ * @default true
155
+ */
156
+ hasClearButton?: boolean;
157
+ /**
158
+ * When true, the dropdown opens automatically when the input receives focus.
159
+ * When false (default), the dropdown only opens on click, typing, or keyboard navigation.
160
+ *
161
+ * @default false
162
+ */
163
+ openOnFocus?: boolean;
164
+ /** Field label (required). Also used as aria-label for the listbox. */
165
+ label: string;
166
+ /** Input placeholder text. */
167
+ placeholder?: string;
168
+ /** Leading icon (SVG path). */
169
+ icon?: string;
170
+ /** Disabled state. */
171
+ isDisabled?: boolean;
172
+ /** Required field indicator. */
173
+ isRequired?: boolean;
174
+ /** Error state flag. */
175
+ hasError?: boolean;
176
+ /** Error message text. */
177
+ error?: string;
178
+ /** Helper text below the field. */
179
+ helper?: string;
180
+ /** Native input id attribute. */
181
+ id?: string;
182
+ /** Native input name attribute. */
183
+ name?: string;
184
+ /** Whether displayed with valid style. */
185
+ isValid?: boolean;
186
+ /** Maximum string length (shows character counter). */
187
+ maxLength?: number;
188
+ /** Accessible label for the input element (overrides the visual label for screen readers). */
189
+ ariaLabel?: string;
190
+ /** Additional props forwarded to the Combobox.Input (and ultimately to TextField). */
191
+ inputProps?: Record<string, any>;
192
+ /** Props forwarded to the Combobox.Popover. */
193
+ popoverProps?: Record<string, any>;
194
+ /** Labels for the clear button, toggle button, and chip group. */
195
+ translations: SelectTextFieldTranslations;
196
+ }
package/lumx.css CHANGED
@@ -10253,6 +10253,7 @@ table {
10253
10253
  align-items: center;
10254
10254
  margin: calc((var(--lumx-text-field-input-min-height) - var(--lumx-size-s) - 6px) / 2 + 3px) 0;
10255
10255
  gap: 6px;
10256
+ min-width: 0;
10256
10257
  }
10257
10258
  .lumx-select--theme-light .lumx-select__input-native {
10258
10259
  display: block;
@@ -12336,6 +12337,7 @@ table {
12336
12337
  align-items: center;
12337
12338
  margin: calc((var(--lumx-text-field-input-min-height) - var(--lumx-size-s) - 6px) / 2 + 3px) 0;
12338
12339
  gap: 6px;
12340
+ min-width: 0;
12339
12341
  }
12340
12342
  .lumx-text-field__helper {
12341
12343
  margin-top: 8px;
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.11.0-next.9",
10
+ "@lumx/icons": "^4.12.0-alpha.0",
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.11.0-next.9",
72
+ "version": "4.12.0-alpha.0",
73
73
  "devDependencies": {
74
74
  "@rollup/plugin-typescript": "^12.3.0",
75
75
  "@testing-library/dom": "^10.4.1",
@@ -89,9 +89,9 @@
89
89
  "tinycolor2": "^1.4.1",
90
90
  "typescript": "^5.4.3",
91
91
  "version-changelog": "^3.1.1",
92
- "vite": "^7.3.1",
92
+ "vite": "^7.3.2",
93
93
  "vite-tsconfig-paths": "^5.1.4",
94
94
  "vitest": "^4.0.18"
95
95
  },
96
- "stableVersion": "4.10.0"
96
+ "stableVersion": "4.11.0"
97
97
  }
@@ -140,4 +140,5 @@
140
140
  align-items: center;
141
141
  margin: calc((var(--lumx-text-field-input-min-height) - var(--lumx-size-s) - 6px) / 2 + $lumx-chip-group-spacing) 0;
142
142
  gap: $lumx-chip-group-spacing * 2;
143
+ min-width: 0;
143
144
  }