@lumx/react 4.11.0-next.7 → 4.11.0-next.9

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.
Files changed (4) hide show
  1. package/index.d.ts +275 -272
  2. package/index.js +857 -790
  3. package/index.js.map +1 -1
  4. package/package.json +3 -3
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Kind as Kind$1, Size as Size$1, ColorPalette as ColorPalette$1, Emphasis as Emphasis$1, ColorVariant as ColorVariant$1, VISUALLY_HIDDEN, Theme as Theme$1, AspectRatio as AspectRatio$1, DOCUMENT, IS_BROWSER as IS_BROWSER$1, WINDOW, DIALOG_TRANSITION_DURATION, Orientation as Orientation$1, NOTIFICATION_TRANSITION_DURATION, Alignment as Alignment$1 } from '@lumx/core/js/constants';
1
+ import { ColorVariant as ColorVariant$1, Size as Size$1, VISUALLY_HIDDEN, Theme as Theme$1, AspectRatio as AspectRatio$1, DOCUMENT, IS_BROWSER as IS_BROWSER$1, Emphasis as Emphasis$1, WINDOW, DIALOG_TRANSITION_DURATION, Orientation as Orientation$1, NOTIFICATION_TRANSITION_DURATION, Kind as Kind$1, Alignment as Alignment$1, ColorPalette as ColorPalette$1 } from '@lumx/core/js/constants';
2
2
  export * from '@lumx/core/js/constants';
3
3
  export * from '@lumx/core/js/types';
4
4
  import * as React from 'react';
@@ -73,485 +73,135 @@ function useId() {
73
73
  const forwardRef = React__default.forwardRef;
74
74
 
75
75
  /**
76
- * Associative map from message kind to color and icon.
76
+ * Animation duration constants. Take into consideration that if you change one of these variables,
77
+ * you need to update their scss counterpart as well
77
78
  */
78
- const CONFIG$1 = {
79
- [Kind$1.error]: {
80
- color: ColorPalette$1.red,
81
- icon: mdiAlert
82
- },
83
- [Kind$1.info]: {
84
- color: ColorPalette$1.blue,
85
- icon: mdiInformation
86
- },
87
- [Kind$1.success]: {
88
- color: ColorPalette$1.green,
89
- icon: mdiCheckCircle
90
- },
91
- [Kind$1.warning]: {
92
- color: ColorPalette$1.yellow,
93
- icon: mdiAlertCircle
94
- }
95
- };
96
79
 
97
80
  /**
98
- * Component display name.
81
+ * Delay on hover after which we open or close the tooltip.
82
+ * Only applies to devices supporting pointer hover.
99
83
  */
100
- const COMPONENT_NAME$1x = 'AlertDialog';
84
+ const TOOLTIP_HOVER_DELAY = {
85
+ open: 500,
86
+ close: 500
87
+ };
101
88
 
102
89
  /**
103
- * Component default class name and class prefix.
90
+ * Delay on long press after which we open or close the tooltip.
91
+ * Only applies to devices not supporting pointer hover.
104
92
  */
105
- const CLASSNAME$1v = 'lumx-alert-dialog';
106
- const {
107
- block: block$19
108
- } = classNames.bem(CLASSNAME$1v);
93
+ const TOOLTIP_LONG_PRESS_DELAY = {
94
+ open: 250,
95
+ close: 3000
96
+ };
109
97
 
110
98
  /**
111
- * Component default props.
99
+ * Alignments.
112
100
  */
113
- const DEFAULT_PROPS$1g = {
114
- size: Size$1.tiny,
115
- kind: Kind$1.info
101
+ const Alignment = {
102
+ left: 'left'};
103
+ const Theme = {
104
+ light: 'light',
105
+ dark: 'dark'
106
+ };
107
+ const Size = {
108
+ xxs: 'xxs',
109
+ xs: 'xs',
110
+ s: 's',
111
+ m: 'm',
112
+ l: 'l',
113
+ xl: 'xl',
114
+ xxl: 'xxl',
115
+ tiny: 'tiny',
116
+ regular: 'regular',
117
+ medium: 'medium',
118
+ big: 'big',
119
+ huge: 'huge'
120
+ };
121
+ const Orientation = {
122
+ horizontal: 'horizontal',
123
+ vertical: 'vertical'
124
+ };
125
+ const Emphasis = {
126
+ low: 'low',
127
+ medium: 'medium',
128
+ high: 'high'
116
129
  };
117
-
118
130
  /**
119
- * AlertDialog component.
120
- *
121
- * An alert dialog is a modal dialog that interrupts the user's workflow to
122
- * communicate an important message and acquire a response.
123
- *
124
- * It should not have a complex content.
125
- * Children of this component should only be strings, paragraphs or links.
131
+ * List of typographies that can't be customized.
126
132
  */
127
- const AlertDialog = forwardRef((props, ref) => {
128
- const {
129
- id,
130
- title,
131
- className,
132
- cancelProps,
133
- confirmProps,
134
- kind = DEFAULT_PROPS$1g.kind,
135
- size = DEFAULT_PROPS$1g.size,
136
- dialogProps,
137
- children,
138
- ...forwardedProps
139
- } = props;
140
- const cancelButtonRef = React__default.useRef(null);
141
- const confirmationButtonRef = React__default.useRef(null);
142
- const {
143
- color,
144
- icon
145
- } = CONFIG$1[kind] || {};
146
-
147
- // Define a unique ID to target title and description for aria attributes.
148
- const generatedId = useId();
149
- const uniqueId = id || generatedId;
150
- const titleId = `${uniqueId}-title`;
151
- const descriptionId = `${uniqueId}-description`;
152
-
153
- // If content is a string, set in a paragraph.
154
- const DescriptionElement = typeof children === 'string' ? 'p' : 'div';
155
- const {
156
- label: confirmLabel,
157
- onClick: confirmOnClick,
158
- ...forwardedConfirmProps
159
- } = confirmProps;
160
- const {
161
- label: cancelLabel,
162
- onClick: cancelOnClick,
163
- ...forwardedCancelProps
164
- } = cancelProps || {};
165
- return /*#__PURE__*/jsxs(Dialog, {
166
- ref: ref,
167
- focusElement: cancelProps ? cancelButtonRef : confirmationButtonRef,
168
- size: size,
169
- dialogProps: {
170
- id: uniqueId,
171
- role: 'alertdialog',
172
- 'aria-labelledby': titleId,
173
- 'aria-describedby': descriptionId,
174
- ...dialogProps
175
- },
176
- className: classNames.join(className, block$19({
177
- [`kind-${kind}`]: Boolean(kind)
178
- })),
179
- ...forwardedProps,
180
- children: [/*#__PURE__*/jsx("header", {
181
- children: /*#__PURE__*/jsx(Toolbar, {
182
- className: "lumx-spacing-margin-horizontal",
183
- before: /*#__PURE__*/jsx(Icon, {
184
- icon: icon,
185
- size: Size$1.s,
186
- color: color
187
- }),
188
- label: /*#__PURE__*/jsx("h2", {
189
- id: titleId,
190
- className: "lumx-typography-title",
191
- children: title
192
- })
193
- })
194
- }), children && /*#__PURE__*/jsx(DescriptionElement, {
195
- id: descriptionId,
196
- className: "lumx-typography-body2 lumx-spacing-padding-vertical-big lumx-spacing-padding-horizontal-huge",
197
- children: children
198
- }), /*#__PURE__*/jsx("footer", {
199
- children: /*#__PURE__*/jsx(Toolbar, {
200
- className: "lumx-spacing-margin-horizontal",
201
- after: /*#__PURE__*/jsxs(Fragment, {
202
- children: [cancelProps && /*#__PURE__*/jsx(Button, {
203
- ...forwardedCancelProps,
204
- ref: cancelButtonRef,
205
- emphasis: Emphasis$1.medium,
206
- onClick: cancelOnClick,
207
- children: cancelLabel
208
- }), /*#__PURE__*/jsx(Button, {
209
- ...forwardedConfirmProps,
210
- ref: confirmationButtonRef,
211
- color: color,
212
- className: "lumx-spacing-margin-left-regular",
213
- onClick: confirmOnClick,
214
- children: confirmLabel
215
- })]
216
- })
217
- })
218
- })]
219
- });
220
- });
221
- AlertDialog.displayName = COMPONENT_NAME$1x;
222
- AlertDialog.className = CLASSNAME$1v;
223
- AlertDialog.defaultProps = DEFAULT_PROPS$1g;
224
-
133
+ const TypographyInterface = {
134
+ overline: 'overline',
135
+ caption: 'caption',
136
+ body1: 'body1',
137
+ body2: 'body2',
138
+ subtitle1: 'subtitle1',
139
+ subtitle2: 'subtitle2',
140
+ title: 'title',
141
+ headline: 'headline',
142
+ display1: 'display1'
143
+ };
225
144
  /**
226
- * Hook focusing an element when defined and `focus` boolean `true`.
227
- *
228
- * @param element Element to focus.
229
- * @param shouldFocus Boolean flag to trigger the focus
145
+ * List of all typographies.
230
146
  */
231
- function useFocus(element, shouldFocus = true) {
232
- const [wasFocus, setWasFocus] = useState(false);
233
- useEffect(() => {
234
- if (shouldFocus && wasFocus !== shouldFocus && element) {
235
- element.focus();
236
- setWasFocus(shouldFocus);
237
- }
238
- },
239
- // eslint-disable-next-line react-hooks/exhaustive-deps
240
- [element, shouldFocus]);
241
- }
242
-
147
+ const Typography = {
148
+ ...TypographyInterface};
243
149
  /**
244
- * Merge refs into a single function ref.
245
- *
246
- * @param refs React references to merge.
247
- * @return the merged ref.
150
+ * All available aspect ratios.
248
151
  */
249
- function mergeRefs(...refs) {
250
- return value => refs.forEach(ref => {
251
- if (typeof ref === 'function') {
252
- ref(value);
253
- } else if (ref) {
254
- // eslint-disable-next-line no-param-reassign
255
- ref.current = value;
256
- }
257
- });
258
- }
259
-
152
+ const AspectRatio = {
153
+ /** Intrinsic content ratio. */
154
+ original: 'original',
155
+ /** Ratio 3:2 */
156
+ horizontal: 'horizontal',
157
+ /** Ratio 1:1 */
158
+ square: 'square',
159
+ /** Ratio constrained by the parent. */
160
+ free: 'free'
161
+ };
260
162
  /**
261
- * Same as `mergeRefs` but memoized
163
+ * Semantic info about the purpose of the component
262
164
  */
263
- const useMergeRefs = (...refs) => {
264
- return useMemo(() => mergeRefs(...refs),
265
- // eslint-disable-next-line react-hooks/exhaustive-deps
266
- refs);
165
+ const Kind = {
166
+ info: 'info',
167
+ success: 'success',
168
+ warning: 'warning',
169
+ error: 'error'
267
170
  };
268
-
269
- const ThemeContext = /*#__PURE__*/React__default.createContext(undefined);
270
-
271
- /** Provide a theme context to all children. */
272
- const ThemeProvider = ThemeContext.Provider;
273
-
274
- /** Get the theme in the current context. */
275
- function useTheme() {
276
- return React__default.useContext(ThemeContext);
277
- }
278
-
279
171
  /**
280
- * Resolve disabled state from props.
281
- * (handles `disabled`, `isDisabled` and `aria-disabled`)
282
- *
283
- * @params component props
172
+ * See SCSS variable $lumx-color-palette
284
173
  */
285
- function useDisableStateProps(props) {
286
- const {
287
- disabled,
288
- isDisabled,
289
- 'aria-disabled': ariaDisabled,
290
- onClick,
291
- onChange,
292
- ...otherProps
293
- } = props;
294
- const disabledStateContext = useDisabledStateContext();
295
- const disabledStateProps = getDisabledState$1(disabledStateContext, {
296
- disabled,
297
- isDisabled,
298
- 'aria-disabled': ariaDisabled
299
- });
300
- const isAnyDisabled = disabledStateProps['aria-disabled'] || disabledStateProps.disabled || undefined;
301
- if (!isAnyDisabled) {
302
- otherProps.onClick = onClick;
303
- otherProps.onChange = onChange;
304
- }
305
- return {
306
- disabledStateProps,
307
- otherProps: otherProps,
308
- isAnyDisabled
309
- };
310
- }
311
-
312
- /**
313
- * Component display name.
314
- */
315
- const COMPONENT_NAME$1w = 'Autocomplete';
316
-
317
- /**
318
- * Component default class name and class prefix.
319
- */
320
- const CLASSNAME$1u = 'lumx-autocomplete';
321
-
174
+ const ColorPalette = {
175
+ primary: 'primary',
176
+ blue: 'blue',
177
+ dark: 'dark',
178
+ green: 'green',
179
+ yellow: 'yellow',
180
+ red: 'red',
181
+ light: 'light'};
322
182
  /**
323
- * Component default props.
183
+ * See SCSS variable $lumx-color-variants
324
184
  */
325
- const DEFAULT_PROPS$1f = {
326
- anchorToInput: false,
327
- closeOnClick: false,
328
- closeOnClickAway: true,
329
- closeOnEscape: true,
330
- shouldFocusOnClose: false
185
+ const ColorVariant = {
186
+ N: 'N'
331
187
  };
332
188
 
333
- /**
334
- * Autocomplete component.
335
- *
336
- * @param props Component props.
337
- * @param ref Component ref.
338
- * @return React element.
339
- */
340
- const Autocomplete = forwardRef((props, ref) => {
341
- const defaultTheme = useTheme();
342
- const {
343
- disabledStateProps,
344
- otherProps
345
- } = useDisableStateProps(props);
346
- const {
347
- anchorToInput = DEFAULT_PROPS$1f.anchorToInput,
348
- children,
349
- chips,
350
- className,
351
- closeOnClick = DEFAULT_PROPS$1f.closeOnClick,
352
- closeOnClickAway = DEFAULT_PROPS$1f.closeOnClickAway,
353
- closeOnEscape = DEFAULT_PROPS$1f.closeOnEscape,
354
- error,
355
- fitToAnchorWidth,
356
- hasError,
357
- helper,
358
- icon,
359
- inputRef,
360
- clearButtonProps,
361
- isRequired,
362
- isOpen,
363
- isValid,
364
- label,
365
- name,
366
- offset,
367
- onBlur,
368
- onChange,
369
- onClose,
370
- onFocus,
371
- onInfiniteScroll,
372
- placeholder,
373
- placement,
374
- shouldFocusOnClose = DEFAULT_PROPS$1f.shouldFocusOnClose,
375
- theme = defaultTheme,
376
- value,
377
- textFieldProps = {},
378
- focusAnchorOnClose,
379
- ...forwardedProps
380
- } = otherProps;
381
- const inputAnchorRef = useRef(null);
382
- const textFieldRef = useRef(null);
383
- useFocus(inputAnchorRef.current, !isOpen && shouldFocusOnClose);
384
- return /*#__PURE__*/jsxs("div", {
385
- ref: ref,
386
- ...forwardedProps,
387
- className: classNames.join(className, CLASSNAME$1u),
388
- children: [/*#__PURE__*/jsx(TextField, {
389
- ...textFieldProps,
390
- chips: chips,
391
- error: error,
392
- hasError: hasError,
393
- helper: helper,
394
- icon: icon,
395
- inputRef: mergeRefs(inputAnchorRef, inputRef),
396
- clearButtonProps: clearButtonProps,
397
- ...disabledStateProps,
398
- isRequired: isRequired,
399
- isValid: isValid,
400
- label: label,
401
- name: name,
402
- onBlur: onBlur,
403
- onChange: onChange,
404
- onFocus: onFocus,
405
- placeholder: placeholder,
406
- textFieldRef: textFieldRef,
407
- theme: theme,
408
- value: value
409
- }), /*#__PURE__*/jsx(Dropdown, {
410
- anchorRef: anchorToInput ? inputAnchorRef : textFieldRef,
411
- closeOnClick: closeOnClick,
412
- closeOnClickAway: closeOnClickAway,
413
- closeOnEscape: closeOnEscape,
414
- focusAnchorOnClose: focusAnchorOnClose,
415
- fitToAnchorWidth: fitToAnchorWidth,
416
- isOpen: isOpen,
417
- offset: offset,
418
- onClose: onClose,
419
- onInfiniteScroll: onInfiniteScroll,
420
- placement: placement,
421
- shouldFocusOnOpen: false,
422
- theme: theme,
423
- children: children
424
- })]
425
- });
426
- });
427
- Autocomplete.displayName = COMPONENT_NAME$1w;
428
- Autocomplete.className = CLASSNAME$1u;
429
- Autocomplete.defaultProps = DEFAULT_PROPS$1f;
430
-
431
- /**
432
- * Component display name.
433
- */
434
- const COMPONENT_NAME$1v = 'AutocompleteMultiple';
435
-
436
- /**
437
- * Component default class name and class prefix.
438
- */
439
- const CLASSNAME$1t = 'lumx-autocomplete-multiple';
440
-
441
- /**
442
- * Component default props.
443
- */
444
- const DEFAULT_PROPS$1e = {
445
- closeOnClickAway: true,
446
- closeOnEscape: true,
447
- selectedChipRender(choice, index, onClear, isDisabled) {
448
- const onClick = event => onClear && onClear(event, choice);
449
- return /*#__PURE__*/jsx(Chip, {
450
- after: onClear && /*#__PURE__*/jsx(Icon, {
451
- icon: mdiClose,
452
- size: Size$1.xxs
453
- }),
454
- isDisabled: isDisabled,
455
- size: Size$1.s,
456
- onAfterClick: onClick,
457
- onClick: onClick,
458
- children: choice
459
- }, index);
460
- },
461
- values: []
462
- };
189
+ /** ColorPalette with all possible color variant combination */
463
190
 
464
- /**
465
- * AutocompleteMultiple component.
466
- *
467
- * @param props Component props.
468
- * @param ref Component ref.
469
- * @return React element.
470
- */
471
- const AutocompleteMultiple = forwardRef((props, ref) => {
472
- const defaultTheme = useTheme();
473
- const {
474
- disabledStateProps,
475
- otherProps
476
- } = useDisableStateProps(props);
477
- const {
478
- anchorToInput,
479
- children,
480
- // `chipsAlignment` needs to be here to remove it from `forwardedProps`.
481
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
482
- chipsAlignment,
483
- className,
484
- closeOnClickAway = DEFAULT_PROPS$1e.closeOnClickAway,
485
- closeOnEscape = DEFAULT_PROPS$1e.closeOnEscape,
486
- fitToAnchorWidth,
487
- hasError,
488
- helper,
489
- icon,
490
- inputRef,
491
- clearButtonProps,
492
- isRequired,
493
- isOpen,
494
- isValid,
495
- label,
496
- name,
497
- offset,
498
- onBlur,
499
- onChange,
500
- onClear,
501
- onClose,
502
- onFocus,
503
- onInfiniteScroll,
504
- onKeyDown,
505
- placeholder,
506
- placement,
507
- selectedChipRender = DEFAULT_PROPS$1e.selectedChipRender,
508
- shouldFocusOnClose,
509
- theme = defaultTheme,
510
- type,
511
- value,
512
- values = DEFAULT_PROPS$1e.values,
513
- ...forwardedProps
514
- } = otherProps;
515
- return /*#__PURE__*/jsx(Autocomplete, {
516
- ref: ref,
517
- ...forwardedProps,
518
- anchorToInput: anchorToInput,
519
- className: classNames.join(className, CLASSNAME$1t),
520
- name: name,
521
- value: value,
522
- onChange: onChange,
523
- onKeyDown: onKeyDown,
524
- onBlur: onBlur,
525
- shouldFocusOnClose: shouldFocusOnClose,
526
- onFocus: onFocus,
527
- hasError: hasError,
528
- helper: helper,
529
- icon: icon,
530
- inputRef: inputRef,
531
- chips: values && values.map((chip, index) => selectedChipRender?.(chip, index, onClear)),
532
- ...disabledStateProps,
533
- isRequired: isRequired,
534
- clearButtonProps: clearButtonProps,
535
- isValid: isValid,
536
- label: label,
537
- placeholder: placeholder,
538
- theme: theme,
539
- type: type,
540
- isOpen: isOpen,
541
- closeOnClick: false,
542
- closeOnClickAway: closeOnClickAway,
543
- closeOnEscape: closeOnEscape,
544
- onClose: onClose,
545
- offset: offset,
546
- placement: placement,
547
- fitToAnchorWidth: fitToAnchorWidth,
548
- onInfiniteScroll: onInfiniteScroll,
549
- children: children
550
- });
551
- });
552
- AutocompleteMultiple.displayName = COMPONENT_NAME$1v;
553
- AutocompleteMultiple.className = CLASSNAME$1t;
554
- AutocompleteMultiple.defaultProps = DEFAULT_PROPS$1e;
191
+ ({
192
+ [Size.xxs]: 14,
193
+ [Size.xs]: 20,
194
+ [Size.s]: 24,
195
+ [Size.m]: 36,
196
+ [Size.l]: 64,
197
+ [Size.xl]: 128,
198
+ [Size.xxl]: 256
199
+ });
200
+
201
+ /**
202
+ * Check if we are running in a true browser (not SSR and not jsdom test environment).
203
+ */
204
+ const IS_BROWSER = typeof window !== 'undefined' && !window.navigator.userAgent.includes('jsdom');
555
205
 
556
206
  function getDefaultExportFromCjs (x) {
557
207
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
@@ -627,386 +277,790 @@ var classnamesExports = requireClassnames();
627
277
  var classnames = /*@__PURE__*/getDefaultExportFromCjs(classnamesExports);
628
278
 
629
279
  /**
630
- * Modifier
631
- * @example { 'is-disabled': true, 'is-selected': false }
280
+ * Modifier
281
+ * @example { 'is-disabled': true, 'is-selected': false }
282
+ */
283
+
284
+ /**
285
+ * Generates BEM modifier class names.
286
+ *
287
+ * @param baseName The base BEM class to attach modifiers to.
288
+ * @param modifiers Map of modifier names to boolean values.
289
+ * @returns Combined modifier class names string.
290
+ *
291
+ * @example
292
+ * modifier('button', { active: true }); // 'button--active'
293
+ * modifier('button', { active: true, disabled: false }); // 'button--active'
294
+ */
295
+ function modifier$1(baseName, modifiers) {
296
+ const modifierClasses = [];
297
+ for (const [key, value] of Object.entries(modifiers)) {
298
+ if (value) modifierClasses.push(`${baseName}--${key}`);
299
+ }
300
+ return modifierClasses.join(' ');
301
+ }
302
+
303
+ /**
304
+ * Generates a BEM block + modifier class name string.
305
+ * Combines a base class with optional modifiers and additional classes.
306
+ *
307
+ * @param baseName The base BEM class
308
+ * @param modifier Optional modifiers
309
+ * @returns Combined class name string
310
+ *
311
+ * @example
312
+ * block('button'); // 'button'
313
+ * block('button', { active: true, disabled: false }); // 'button button--active'
314
+ */
315
+
316
+ function block$19(baseName, modifiersOrAdditionalClasses, additionalClasses) {
317
+ let modifiers;
318
+ let classes;
319
+ if (Array.isArray(modifiersOrAdditionalClasses)) {
320
+ classes = modifiersOrAdditionalClasses;
321
+ } else {
322
+ modifiers = modifiersOrAdditionalClasses;
323
+ classes = additionalClasses;
324
+ }
325
+ if (!modifiers && !classes) {
326
+ return baseName;
327
+ }
328
+ return classnames(
329
+ // Additional classes
330
+ classes,
331
+ // Base class
332
+ baseName,
333
+ // Modifier(s)
334
+ modifiers ? modifier$1(baseName, modifiers) : null);
335
+ }
336
+
337
+ const PREFIX = 'lumx-action-area';
338
+ const ACTION_ELEMENT = `${PREFIX}__action`;
339
+ /**
340
+ * Action area CSS utility.
341
+ *
342
+ * Apply `actionArea()` to the container element to make it a positioning context
343
+ * for the action's expanded click area.
344
+ *
345
+ * Apply `actionArea.action()` to the primary interactive element (link or button)
346
+ * whose click area should expand to fill the entire container.
347
+ */
348
+ const actionArea = Object.assign(/** Action area container class. Sets `position: relative`. */
349
+ () => PREFIX, {
350
+ /** Action element class. Adds a `::before` pseudo-element with `position: absolute; inset: 0` to expand the click area. */
351
+ action: modifiers => block$19(ACTION_ELEMENT, modifiers)
352
+ });
353
+
354
+ /** Resolve color & color variant from a `ColorWithVariants` and optionally a `ColorVariant`. */
355
+ function resolveColorWithVariants(colorWithVariants, colorVariant) {
356
+ if (!colorWithVariants) return [undefined, colorVariant];
357
+ const [color, baseColorVariant] = colorWithVariants.split('-');
358
+ return [color, colorVariant || baseColorVariant];
359
+ }
360
+
361
+ /**
362
+ * Generates a Lumx color class name for the given type, color and variant.
363
+ * This is the base function used by font() and background() utilities.
364
+ *
365
+ * @param type - The color class type ('font' or 'background')
366
+ * @param propColor - The color palette name (e.g., 'primary', 'dark') with optional variant suffix (e.g., 'primary-L2')
367
+ * @param propColorVariant - Optional color variant override (e.g., 'L1', 'L2', 'D1', 'N')
368
+ * @returns The Lumx color class name or undefined if no color is provided
369
+ *
370
+ * @example
371
+ * color('font', 'dark', 'L2'); // 'lumx-color-font-dark-L2'
372
+ * color('background', 'primary'); // 'lumx-color-background-primary-N'
373
+ * color('font', 'primary-L2'); // 'lumx-color-font-primary-L2'
374
+ * color('font', undefined); // undefined
375
+ */
376
+ function color(type, propColor, propColorVariant) {
377
+ const [cColor, cColorVariant = ColorVariant$1.N] = resolveColorWithVariants(propColor, propColorVariant);
378
+ return `lumx-color-${type}-${cColor}-${cColorVariant}`;
379
+ }
380
+
381
+ /**
382
+ * Generates a Lumx font color class name for the given color and variant.
383
+ *
384
+ * @param propColor - The color palette name (e.g., 'primary', 'dark') with optional variant suffix (e.g., 'primary-L2')
385
+ * @param propColorVariant - Optional color variant override (e.g., 'L1', 'L2', 'D1', 'N')
386
+ * @returns The Lumx font color class name or undefined if no color is provided
387
+ *
388
+ * @example
389
+ * font('dark', 'L2'); // 'lumx-color-font-dark-L2'
390
+ * font('primary-L2'); // 'lumx-color-font-primary-L2'
391
+ * font('primary'); // 'lumx-color-font-primary-N'
392
+ * font(undefined); // undefined
393
+ */
394
+ const font = (propColor, propColorVariant) => color('font', propColor, propColorVariant);
395
+
396
+ /**
397
+ * Returns the classname associated to the given typography. For example, for Typography.title it returns
398
+ * lumx-typography-title
399
+ */
400
+ function typography(typo) {
401
+ return `lumx-typography-${typo}`;
402
+ }
403
+
404
+ /** Set of valid AbstractSize values for runtime detection. */
405
+ const ABSTRACT_SIZES = new Set([Size$1.tiny, Size$1.regular, Size$1.medium, Size$1.big, Size$1.huge]);
406
+ function isAbstractSize(value) {
407
+ return ABSTRACT_SIZES.has(value);
408
+ }
409
+
410
+ /**
411
+ * Returns a lumx classname for the given type, direction and size. For example, for
412
+ * arguments type='padding', direction='right', size='regular' it returns lumx-spacing-padding-right-regular
413
+ * @param type - margin or padding
414
+ * @param direction - Direction
415
+ * @param size - Size
416
+ * @returns string
417
+ */
418
+
419
+ function spacing(type, directionOrSize, size) {
420
+ // Resolve shorthand: spacing(type, size) => spacing(type, undefined, size)
421
+ const isShorthand = isAbstractSize(directionOrSize) || directionOrSize === null;
422
+ const direction = isShorthand ? undefined : directionOrSize;
423
+ const resolvedSize = isShorthand ? directionOrSize : size;
424
+ let baseClass = `lumx-spacing-${type}`;
425
+ if (direction && direction !== 'all') {
426
+ baseClass = `${baseClass}-${direction}`;
427
+ }
428
+ if (resolvedSize) {
429
+ baseClass = `${baseClass}-${resolvedSize}`;
430
+ } else if (resolvedSize === null) {
431
+ baseClass = `${baseClass}-none`;
432
+ }
433
+ return baseClass;
434
+ }
435
+
436
+ /**
437
+ * Returns a lumx padding classname for the given direction and size. For example, for
438
+ * arguments direction='right', size='regular' it returns lumx-spacing-padding-right-regular
439
+ *
440
+ * Can also be called with just a size: padding('regular') is equivalent to padding('all', 'regular').
441
+ * @param direction - Direction
442
+ * @param size - Size
443
+ * @returns string
444
+ */
445
+
446
+ function padding(directionOrSize, size) {
447
+ if (isAbstractSize(directionOrSize) || directionOrSize === null) {
448
+ return spacing('padding', directionOrSize);
449
+ }
450
+ return spacing('padding', directionOrSize, size);
451
+ }
452
+
453
+ /**
454
+ * Visually hidden class name.
455
+ * Used to hide elements from view but keep them readable from screen readers
456
+ */
457
+ const visuallyHidden = () => VISUALLY_HIDDEN;
458
+
459
+ /**
460
+ * Creates a BEM element class generator function for the given base class.
461
+ * Returns a function that generates BEM element class names with optional modifiers.
462
+ *
463
+ * @param baseClass The base BEM block class name (e.g., 'button', 'card')
464
+ * @param elem The BEM element name (e.g., 'icon', 'title')
465
+ * @param modifier Optional BEM modifier ()
466
+ * @returns combined BEM element class name
467
+ *
468
+ * @example
469
+ * element('my-button', 'icon'); // 'my-button__icon'
470
+ * element('my-button', 'icon', { active: true }); // 'my-button__icon my-button__icon--active'
471
+ */
472
+
473
+ function element$R(baseClass, elem, modifiersOrAdditionalClasses, additionalClasses) {
474
+ if (Array.isArray(modifiersOrAdditionalClasses)) {
475
+ return block$19(`${baseClass}__${elem}`, modifiersOrAdditionalClasses);
476
+ }
477
+ return block$19(`${baseClass}__${elem}`, modifiersOrAdditionalClasses, additionalClasses);
478
+ }
479
+
480
+ /**
481
+ * Setup BEM block & element generation for a given base name.
482
+ */
483
+ function bem(baseName) {
484
+ function blockFn(modifiersOrAdditionalClasses, additionalClasses) {
485
+ if (Array.isArray(modifiersOrAdditionalClasses)) {
486
+ return block$19(baseName, modifiersOrAdditionalClasses);
487
+ }
488
+ return block$19(baseName, modifiersOrAdditionalClasses, additionalClasses);
489
+ }
490
+ function elementFn(elem, modifiersOrAdditionalClasses, additionalClasses) {
491
+ if (Array.isArray(modifiersOrAdditionalClasses)) {
492
+ return element$R(baseName, elem, modifiersOrAdditionalClasses);
493
+ }
494
+ return element$R(baseName, elem, modifiersOrAdditionalClasses, additionalClasses);
495
+ }
496
+ return {
497
+ block: blockFn,
498
+ element: elementFn,
499
+ modifier: modifiers => modifier$1(baseName, modifiers)
500
+ };
501
+ }
502
+
503
+ /**
504
+ * Make sure the pressed key is the enter key before calling the callback.
505
+ *
506
+ * @param handler The handler to call on enter/return press.
507
+ * @return The decorated function.
508
+ */
509
+ function onEnterPressed(handler) {
510
+ return evt => {
511
+ if (evt.key !== 'Enter') {
512
+ return;
513
+ }
514
+ handler(evt);
515
+ };
516
+ }
517
+
518
+ /** Get value with a string of function selector */
519
+ const getWithSelector = (selector, object) => {
520
+ // Use the provided selector function
521
+ if (typeof selector === 'function') {
522
+ return selector(object);
523
+ }
524
+ // Use the provided selector as a property name
525
+ if (typeof selector === 'string') {
526
+ return object[selector];
527
+ }
528
+ return String(object);
529
+ };
530
+
531
+ /**
532
+ * Associative map from message kind to color and icon.
533
+ */
534
+ const CONFIG$1 = {
535
+ [Kind.error]: {
536
+ color: ColorPalette.red,
537
+ icon: mdiAlert
538
+ },
539
+ [Kind.info]: {
540
+ color: ColorPalette.blue,
541
+ icon: mdiInformation
542
+ },
543
+ [Kind.success]: {
544
+ color: ColorPalette.green,
545
+ icon: mdiCheckCircle
546
+ },
547
+ [Kind.warning]: {
548
+ color: ColorPalette.yellow,
549
+ icon: mdiAlertCircle
550
+ }
551
+ };
552
+
553
+ /**
554
+ * Component display name.
632
555
  */
556
+ const COMPONENT_NAME$1x = 'AlertDialog';
633
557
 
634
558
  /**
635
- * Generates BEM modifier class names.
636
- *
637
- * @param baseName The base BEM class to attach modifiers to.
638
- * @param modifiers Map of modifier names to boolean values.
639
- * @returns Combined modifier class names string.
640
- *
641
- * @example
642
- * modifier('button', { active: true }); // 'button--active'
643
- * modifier('button', { active: true, disabled: false }); // 'button--active'
559
+ * Component default class name and class prefix.
644
560
  */
645
- function modifier$1(baseName, modifiers) {
646
- const modifierClasses = [];
647
- for (const [key, value] of Object.entries(modifiers)) {
648
- if (value) modifierClasses.push(`${baseName}--${key}`);
649
- }
650
- return modifierClasses.join(' ');
651
- }
561
+ const CLASSNAME$1v = 'lumx-alert-dialog';
562
+ const {
563
+ block: block$18
564
+ } = bem(CLASSNAME$1v);
652
565
 
653
566
  /**
654
- * Generates a BEM block + modifier class name string.
655
- * Combines a base class with optional modifiers and additional classes.
656
- *
657
- * @param baseName The base BEM class
658
- * @param modifier Optional modifiers
659
- * @returns Combined class name string
660
- *
661
- * @example
662
- * block('button'); // 'button'
663
- * block('button', { active: true, disabled: false }); // 'button button--active'
567
+ * Component default props.
664
568
  */
569
+ const DEFAULT_PROPS$1g = {
570
+ size: Size.tiny,
571
+ kind: Kind.info
572
+ };
665
573
 
666
- function block$18(baseName, modifiersOrAdditionalClasses, additionalClasses) {
667
- let modifiers;
668
- let classes;
669
- if (Array.isArray(modifiersOrAdditionalClasses)) {
670
- classes = modifiersOrAdditionalClasses;
671
- } else {
672
- modifiers = modifiersOrAdditionalClasses;
673
- classes = additionalClasses;
674
- }
675
- if (!modifiers && !classes) {
676
- return baseName;
677
- }
678
- return classnames(
679
- // Additional classes
680
- classes,
681
- // Base class
682
- baseName,
683
- // Modifier(s)
684
- modifiers ? modifier$1(baseName, modifiers) : null);
685
- }
686
-
687
- const PREFIX = 'lumx-action-area';
688
- const ACTION_ELEMENT = `${PREFIX}__action`;
689
574
  /**
690
- * Action area CSS utility.
575
+ * AlertDialog component.
691
576
  *
692
- * Apply `actionArea()` to the container element to make it a positioning context
693
- * for the action's expanded click area.
577
+ * An alert dialog is a modal dialog that interrupts the user's workflow to
578
+ * communicate an important message and acquire a response.
694
579
  *
695
- * Apply `actionArea.action()` to the primary interactive element (link or button)
696
- * whose click area should expand to fill the entire container.
580
+ * It should not have a complex content.
581
+ * Children of this component should only be strings, paragraphs or links.
697
582
  */
698
- const actionArea = Object.assign(/** Action area container class. Sets `position: relative`. */
699
- () => PREFIX, {
700
- /** Action element class. Adds a `::before` pseudo-element with `position: absolute; inset: 0` to expand the click area. */
701
- action: modifiers => block$18(ACTION_ELEMENT, modifiers)
702
- });
703
-
704
- /** Resolve color & color variant from a `ColorWithVariants` and optionally a `ColorVariant`. */
705
- function resolveColorWithVariants(colorWithVariants, colorVariant) {
706
- if (!colorWithVariants) return [undefined, colorVariant];
707
- const [color, baseColorVariant] = colorWithVariants.split('-');
708
- return [color, colorVariant || baseColorVariant];
709
- }
583
+ const AlertDialog$1 = props => {
584
+ const {
585
+ id,
586
+ title,
587
+ className,
588
+ cancelProps,
589
+ confirmProps,
590
+ ref,
591
+ kind = DEFAULT_PROPS$1g.kind,
592
+ size = DEFAULT_PROPS$1g.size,
593
+ dialogProps,
594
+ children,
595
+ DescriptionElement,
596
+ cancelButtonRef,
597
+ confirmationButtonRef,
598
+ focusElement,
599
+ Dialog,
600
+ Toolbar,
601
+ Button,
602
+ Icon,
603
+ ...forwardedProps
604
+ } = props;
605
+ const {
606
+ color,
607
+ icon
608
+ } = CONFIG$1[kind] || {};
609
+ const titleId = `${id}-title`;
610
+ const descriptionId = `${id}-description`;
611
+ const {
612
+ label: confirmLabel,
613
+ onClick: confirmOnClick,
614
+ ...forwardedConfirmProps
615
+ } = confirmProps;
616
+ const {
617
+ label: cancelLabel,
618
+ onClick: cancelOnClick,
619
+ ...forwardedCancelProps
620
+ } = cancelProps || {};
621
+ return /*#__PURE__*/jsxs(Dialog, {
622
+ ref: ref,
623
+ focusElement: focusElement ?? (cancelProps ? cancelButtonRef : confirmationButtonRef),
624
+ size: size,
625
+ dialogProps: {
626
+ id,
627
+ role: 'alertdialog',
628
+ 'aria-labelledby': titleId,
629
+ 'aria-describedby': descriptionId,
630
+ ...dialogProps
631
+ },
632
+ className: classnames(className, block$18({
633
+ [`kind-${kind}`]: Boolean(kind)
634
+ })),
635
+ ...forwardedProps,
636
+ children: [/*#__PURE__*/jsx("header", {
637
+ children: /*#__PURE__*/jsx(Toolbar, {
638
+ className: "lumx-spacing-margin-horizontal",
639
+ before: /*#__PURE__*/jsx(Icon, {
640
+ icon: icon,
641
+ size: Size.s,
642
+ color: color
643
+ }),
644
+ label: /*#__PURE__*/jsx("h2", {
645
+ id: titleId,
646
+ className: "lumx-typography-title",
647
+ children: title
648
+ })
649
+ })
650
+ }), children && /*#__PURE__*/jsx(DescriptionElement, {
651
+ id: descriptionId,
652
+ className: "lumx-typography-body2 lumx-spacing-padding-vertical-big lumx-spacing-padding-horizontal-huge",
653
+ children: children
654
+ }), /*#__PURE__*/jsx("footer", {
655
+ children: /*#__PURE__*/jsx(Toolbar, {
656
+ className: "lumx-spacing-margin-horizontal",
657
+ after: /*#__PURE__*/jsxs(Fragment, {
658
+ children: [cancelProps && /*#__PURE__*/jsx(Button, {
659
+ ...forwardedCancelProps,
660
+ ref: cancelButtonRef,
661
+ emphasis: Emphasis.medium,
662
+ onClick: cancelOnClick,
663
+ children: cancelLabel
664
+ }), /*#__PURE__*/jsx(Button, {
665
+ ...forwardedConfirmProps,
666
+ ref: confirmationButtonRef,
667
+ color: color,
668
+ className: "lumx-spacing-margin-left-regular",
669
+ onClick: confirmOnClick,
670
+ children: confirmLabel
671
+ })]
672
+ })
673
+ })
674
+ })]
675
+ });
676
+ };
710
677
 
711
678
  /**
712
- * Generates a Lumx color class name for the given type, color and variant.
713
- * This is the base function used by font() and background() utilities.
679
+ * AlertDialog component.
714
680
  *
715
- * @param type - The color class type ('font' or 'background')
716
- * @param propColor - The color palette name (e.g., 'primary', 'dark') with optional variant suffix (e.g., 'primary-L2')
717
- * @param propColorVariant - Optional color variant override (e.g., 'L1', 'L2', 'D1', 'N')
718
- * @returns The Lumx color class name or undefined if no color is provided
681
+ * An alert dialog is a modal dialog that interrupts the user's workflow to
682
+ * communicate an important message and acquire a response.
719
683
  *
720
- * @example
721
- * color('font', 'dark', 'L2'); // 'lumx-color-font-dark-L2'
722
- * color('background', 'primary'); // 'lumx-color-background-primary-N'
723
- * color('font', 'primary-L2'); // 'lumx-color-font-primary-L2'
724
- * color('font', undefined); // undefined
684
+ * It should not have a complex content.
685
+ * Children of this component should only be strings, paragraphs or links.
725
686
  */
726
- function color(type, propColor, propColorVariant) {
727
- const [cColor, cColorVariant = ColorVariant$1.N] = resolveColorWithVariants(propColor, propColorVariant);
728
- return `lumx-color-${type}-${cColor}-${cColorVariant}`;
729
- }
687
+ const AlertDialog = forwardRef((props, ref) => {
688
+ const {
689
+ id,
690
+ title,
691
+ className,
692
+ cancelProps,
693
+ confirmProps,
694
+ kind,
695
+ size,
696
+ dialogProps,
697
+ children,
698
+ ...forwardedProps
699
+ } = props;
700
+ const cancelButtonRef = React__default.useRef(null);
701
+ const confirmationButtonRef = React__default.useRef(null);
702
+
703
+ // Define a unique ID to target title and description for aria attributes.
704
+ const generatedId = useId();
705
+ const uniqueId = id || generatedId;
706
+
707
+ // If content is a string, set in a paragraph.
708
+ const DescriptionElement = typeof children === 'string' ? 'p' : 'div';
709
+ return AlertDialog$1({
710
+ Button,
711
+ confirmProps,
712
+ DescriptionElement,
713
+ Dialog,
714
+ Icon,
715
+ id: uniqueId,
716
+ Toolbar,
717
+ cancelButtonRef,
718
+ cancelProps,
719
+ children,
720
+ className,
721
+ confirmationButtonRef,
722
+ dialogProps,
723
+ kind,
724
+ ref,
725
+ size,
726
+ title,
727
+ ...forwardedProps
728
+ });
729
+ });
730
+ AlertDialog.displayName = COMPONENT_NAME$1x;
731
+ AlertDialog.className = CLASSNAME$1v;
732
+ AlertDialog.defaultProps = DEFAULT_PROPS$1g;
730
733
 
731
734
  /**
732
- * Generates a Lumx font color class name for the given color and variant.
733
- *
734
- * @param propColor - The color palette name (e.g., 'primary', 'dark') with optional variant suffix (e.g., 'primary-L2')
735
- * @param propColorVariant - Optional color variant override (e.g., 'L1', 'L2', 'D1', 'N')
736
- * @returns The Lumx font color class name or undefined if no color is provided
735
+ * Hook focusing an element when defined and `focus` boolean `true`.
737
736
  *
738
- * @example
739
- * font('dark', 'L2'); // 'lumx-color-font-dark-L2'
740
- * font('primary-L2'); // 'lumx-color-font-primary-L2'
741
- * font('primary'); // 'lumx-color-font-primary-N'
742
- * font(undefined); // undefined
737
+ * @param element Element to focus.
738
+ * @param shouldFocus Boolean flag to trigger the focus
743
739
  */
744
- const font = (propColor, propColorVariant) => color('font', propColor, propColorVariant);
740
+ function useFocus(element, shouldFocus = true) {
741
+ const [wasFocus, setWasFocus] = useState(false);
742
+ useEffect(() => {
743
+ if (shouldFocus && wasFocus !== shouldFocus && element) {
744
+ element.focus();
745
+ setWasFocus(shouldFocus);
746
+ }
747
+ },
748
+ // eslint-disable-next-line react-hooks/exhaustive-deps
749
+ [element, shouldFocus]);
750
+ }
745
751
 
746
752
  /**
747
- * Returns the classname associated to the given typography. For example, for Typography.title it returns
748
- * lumx-typography-title
753
+ * Merge refs into a single function ref.
754
+ *
755
+ * @param refs React references to merge.
756
+ * @return the merged ref.
749
757
  */
750
- function typography(typo) {
751
- return `lumx-typography-${typo}`;
752
- }
753
-
754
- /** Set of valid AbstractSize values for runtime detection. */
755
- const ABSTRACT_SIZES = new Set([Size$1.tiny, Size$1.regular, Size$1.medium, Size$1.big, Size$1.huge]);
756
- function isAbstractSize(value) {
757
- return ABSTRACT_SIZES.has(value);
758
+ function mergeRefs(...refs) {
759
+ return value => refs.forEach(ref => {
760
+ if (typeof ref === 'function') {
761
+ ref(value);
762
+ } else if (ref) {
763
+ // eslint-disable-next-line no-param-reassign
764
+ ref.current = value;
765
+ }
766
+ });
758
767
  }
759
768
 
760
769
  /**
761
- * Returns a lumx classname for the given type, direction and size. For example, for
762
- * arguments type='padding', direction='right', size='regular' it returns lumx-spacing-padding-right-regular
763
- * @param type - margin or padding
764
- * @param direction - Direction
765
- * @param size - Size
766
- * @returns string
770
+ * Same as `mergeRefs` but memoized
767
771
  */
772
+ const useMergeRefs = (...refs) => {
773
+ return useMemo(() => mergeRefs(...refs),
774
+ // eslint-disable-next-line react-hooks/exhaustive-deps
775
+ refs);
776
+ };
768
777
 
769
- function spacing(type, directionOrSize, size) {
770
- // Resolve shorthand: spacing(type, size) => spacing(type, undefined, size)
771
- const isShorthand = isAbstractSize(directionOrSize) || directionOrSize === null;
772
- const direction = isShorthand ? undefined : directionOrSize;
773
- const resolvedSize = isShorthand ? directionOrSize : size;
774
- let baseClass = `lumx-spacing-${type}`;
775
- if (direction && direction !== 'all') {
776
- baseClass = `${baseClass}-${direction}`;
777
- }
778
- if (resolvedSize) {
779
- baseClass = `${baseClass}-${resolvedSize}`;
780
- } else if (resolvedSize === null) {
781
- baseClass = `${baseClass}-none`;
782
- }
783
- return baseClass;
778
+ const ThemeContext = /*#__PURE__*/React__default.createContext(undefined);
779
+
780
+ /** Provide a theme context to all children. */
781
+ const ThemeProvider = ThemeContext.Provider;
782
+
783
+ /** Get the theme in the current context. */
784
+ function useTheme() {
785
+ return React__default.useContext(ThemeContext);
784
786
  }
785
787
 
786
788
  /**
787
- * Returns a lumx padding classname for the given direction and size. For example, for
788
- * arguments direction='right', size='regular' it returns lumx-spacing-padding-right-regular
789
+ * Resolve disabled state from props.
790
+ * (handles `disabled`, `isDisabled` and `aria-disabled`)
789
791
  *
790
- * Can also be called with just a size: padding('regular') is equivalent to padding('all', 'regular').
791
- * @param direction - Direction
792
- * @param size - Size
793
- * @returns string
792
+ * @params component props
794
793
  */
795
-
796
- function padding(directionOrSize, size) {
797
- if (isAbstractSize(directionOrSize) || directionOrSize === null) {
798
- return spacing('padding', directionOrSize);
794
+ function useDisableStateProps(props) {
795
+ const {
796
+ disabled,
797
+ isDisabled,
798
+ 'aria-disabled': ariaDisabled,
799
+ onClick,
800
+ onChange,
801
+ ...otherProps
802
+ } = props;
803
+ const disabledStateContext = useDisabledStateContext();
804
+ const disabledStateProps = getDisabledState$1(disabledStateContext, {
805
+ disabled,
806
+ isDisabled,
807
+ 'aria-disabled': ariaDisabled
808
+ });
809
+ const isAnyDisabled = disabledStateProps['aria-disabled'] || disabledStateProps.disabled || undefined;
810
+ if (!isAnyDisabled) {
811
+ otherProps.onClick = onClick;
812
+ otherProps.onChange = onChange;
799
813
  }
800
- return spacing('padding', directionOrSize, size);
814
+ return {
815
+ disabledStateProps,
816
+ otherProps: otherProps,
817
+ isAnyDisabled
818
+ };
801
819
  }
802
820
 
803
821
  /**
804
- * Visually hidden class name.
805
- * Used to hide elements from view but keep them readable from screen readers
822
+ * Component display name.
806
823
  */
807
- const visuallyHidden = () => VISUALLY_HIDDEN;
824
+ const COMPONENT_NAME$1w = 'Autocomplete';
808
825
 
809
826
  /**
810
- * Creates a BEM element class generator function for the given base class.
811
- * Returns a function that generates BEM element class names with optional modifiers.
812
- *
813
- * @param baseClass The base BEM block class name (e.g., 'button', 'card')
814
- * @param elem The BEM element name (e.g., 'icon', 'title')
815
- * @param modifier Optional BEM modifier ()
816
- * @returns combined BEM element class name
817
- *
818
- * @example
819
- * element('my-button', 'icon'); // 'my-button__icon'
820
- * element('my-button', 'icon', { active: true }); // 'my-button__icon my-button__icon--active'
827
+ * Component default class name and class prefix.
821
828
  */
822
-
823
- function element$R(baseClass, elem, modifiersOrAdditionalClasses, additionalClasses) {
824
- if (Array.isArray(modifiersOrAdditionalClasses)) {
825
- return block$18(`${baseClass}__${elem}`, modifiersOrAdditionalClasses);
826
- }
827
- return block$18(`${baseClass}__${elem}`, modifiersOrAdditionalClasses, additionalClasses);
828
- }
829
+ const CLASSNAME$1u = 'lumx-autocomplete';
829
830
 
830
831
  /**
831
- * Setup BEM block & element generation for a given base name.
832
+ * Component default props.
832
833
  */
833
- function bem(baseName) {
834
- function blockFn(modifiersOrAdditionalClasses, additionalClasses) {
835
- if (Array.isArray(modifiersOrAdditionalClasses)) {
836
- return block$18(baseName, modifiersOrAdditionalClasses);
837
- }
838
- return block$18(baseName, modifiersOrAdditionalClasses, additionalClasses);
839
- }
840
- function elementFn(elem, modifiersOrAdditionalClasses, additionalClasses) {
841
- if (Array.isArray(modifiersOrAdditionalClasses)) {
842
- return element$R(baseName, elem, modifiersOrAdditionalClasses);
843
- }
844
- return element$R(baseName, elem, modifiersOrAdditionalClasses, additionalClasses);
845
- }
846
- return {
847
- block: blockFn,
848
- element: elementFn,
849
- modifier: modifiers => modifier$1(baseName, modifiers)
850
- };
851
- }
834
+ const DEFAULT_PROPS$1f = {
835
+ anchorToInput: false,
836
+ closeOnClick: false,
837
+ closeOnClickAway: true,
838
+ closeOnEscape: true,
839
+ shouldFocusOnClose: false
840
+ };
852
841
 
853
842
  /**
854
- * Make sure the pressed key is the enter key before calling the callback.
843
+ * Autocomplete component.
855
844
  *
856
- * @param handler The handler to call on enter/return press.
857
- * @return The decorated function.
845
+ * @param props Component props.
846
+ * @param ref Component ref.
847
+ * @return React element.
858
848
  */
859
- function onEnterPressed(handler) {
860
- return evt => {
861
- if (evt.key !== 'Enter') {
862
- return;
863
- }
864
- handler(evt);
865
- };
866
- }
867
-
868
- /** Get value with a string of function selector */
869
- const getWithSelector = (selector, object) => {
870
- // Use the provided selector function
871
- if (typeof selector === 'function') {
872
- return selector(object);
873
- }
874
- // Use the provided selector as a property name
875
- if (typeof selector === 'string') {
876
- return object[selector];
877
- }
878
- return String(object);
879
- };
849
+ const Autocomplete = forwardRef((props, ref) => {
850
+ const defaultTheme = useTheme();
851
+ const {
852
+ disabledStateProps,
853
+ otherProps
854
+ } = useDisableStateProps(props);
855
+ const {
856
+ anchorToInput = DEFAULT_PROPS$1f.anchorToInput,
857
+ children,
858
+ chips,
859
+ className,
860
+ closeOnClick = DEFAULT_PROPS$1f.closeOnClick,
861
+ closeOnClickAway = DEFAULT_PROPS$1f.closeOnClickAway,
862
+ closeOnEscape = DEFAULT_PROPS$1f.closeOnEscape,
863
+ error,
864
+ fitToAnchorWidth,
865
+ hasError,
866
+ helper,
867
+ icon,
868
+ inputRef,
869
+ clearButtonProps,
870
+ isRequired,
871
+ isOpen,
872
+ isValid,
873
+ label,
874
+ name,
875
+ offset,
876
+ onBlur,
877
+ onChange,
878
+ onClose,
879
+ onFocus,
880
+ onInfiniteScroll,
881
+ placeholder,
882
+ placement,
883
+ shouldFocusOnClose = DEFAULT_PROPS$1f.shouldFocusOnClose,
884
+ theme = defaultTheme,
885
+ value,
886
+ textFieldProps = {},
887
+ focusAnchorOnClose,
888
+ ...forwardedProps
889
+ } = otherProps;
890
+ const inputAnchorRef = useRef(null);
891
+ const textFieldRef = useRef(null);
892
+ useFocus(inputAnchorRef.current, !isOpen && shouldFocusOnClose);
893
+ return /*#__PURE__*/jsxs("div", {
894
+ ref: ref,
895
+ ...forwardedProps,
896
+ className: classNames.join(className, CLASSNAME$1u),
897
+ children: [/*#__PURE__*/jsx(TextField, {
898
+ ...textFieldProps,
899
+ chips: chips,
900
+ error: error,
901
+ hasError: hasError,
902
+ helper: helper,
903
+ icon: icon,
904
+ inputRef: mergeRefs(inputAnchorRef, inputRef),
905
+ clearButtonProps: clearButtonProps,
906
+ ...disabledStateProps,
907
+ isRequired: isRequired,
908
+ isValid: isValid,
909
+ label: label,
910
+ name: name,
911
+ onBlur: onBlur,
912
+ onChange: onChange,
913
+ onFocus: onFocus,
914
+ placeholder: placeholder,
915
+ textFieldRef: textFieldRef,
916
+ theme: theme,
917
+ value: value
918
+ }), /*#__PURE__*/jsx(Dropdown, {
919
+ anchorRef: anchorToInput ? inputAnchorRef : textFieldRef,
920
+ closeOnClick: closeOnClick,
921
+ closeOnClickAway: closeOnClickAway,
922
+ closeOnEscape: closeOnEscape,
923
+ focusAnchorOnClose: focusAnchorOnClose,
924
+ fitToAnchorWidth: fitToAnchorWidth,
925
+ isOpen: isOpen,
926
+ offset: offset,
927
+ onClose: onClose,
928
+ onInfiniteScroll: onInfiniteScroll,
929
+ placement: placement,
930
+ shouldFocusOnOpen: false,
931
+ theme: theme,
932
+ children: children
933
+ })]
934
+ });
935
+ });
936
+ Autocomplete.displayName = COMPONENT_NAME$1w;
937
+ Autocomplete.className = CLASSNAME$1u;
938
+ Autocomplete.defaultProps = DEFAULT_PROPS$1f;
880
939
 
881
940
  /**
882
- * Animation duration constants. Take into consideration that if you change one of these variables,
883
- * you need to update their scss counterpart as well
941
+ * Component display name.
884
942
  */
943
+ const COMPONENT_NAME$1v = 'AutocompleteMultiple';
885
944
 
886
945
  /**
887
- * Delay on hover after which we open or close the tooltip.
888
- * Only applies to devices supporting pointer hover.
946
+ * Component default class name and class prefix.
889
947
  */
890
- const TOOLTIP_HOVER_DELAY = {
891
- open: 500,
892
- close: 500
893
- };
948
+ const CLASSNAME$1t = 'lumx-autocomplete-multiple';
894
949
 
895
950
  /**
896
- * Delay on long press after which we open or close the tooltip.
897
- * Only applies to devices not supporting pointer hover.
951
+ * Component default props.
898
952
  */
899
- const TOOLTIP_LONG_PRESS_DELAY = {
900
- open: 250,
901
- close: 3000
953
+ const DEFAULT_PROPS$1e = {
954
+ closeOnClickAway: true,
955
+ closeOnEscape: true,
956
+ selectedChipRender(choice, index, onClear, isDisabled) {
957
+ const onClick = event => onClear && onClear(event, choice);
958
+ return /*#__PURE__*/jsx(Chip, {
959
+ after: onClear && /*#__PURE__*/jsx(Icon, {
960
+ icon: mdiClose,
961
+ size: Size$1.xxs
962
+ }),
963
+ isDisabled: isDisabled,
964
+ size: Size$1.s,
965
+ onAfterClick: onClick,
966
+ onClick: onClick,
967
+ children: choice
968
+ }, index);
969
+ },
970
+ values: []
902
971
  };
903
972
 
904
973
  /**
905
- * Alignments.
906
- */
907
- const Alignment = {
908
- left: 'left'};
909
- const Theme = {
910
- light: 'light',
911
- dark: 'dark'
912
- };
913
- const Size = {
914
- xxs: 'xxs',
915
- xs: 'xs',
916
- s: 's',
917
- m: 'm',
918
- l: 'l',
919
- xl: 'xl',
920
- xxl: 'xxl',
921
- tiny: 'tiny',
922
- regular: 'regular',
923
- medium: 'medium',
924
- big: 'big',
925
- huge: 'huge'
926
- };
927
- const Orientation = {
928
- horizontal: 'horizontal',
929
- vertical: 'vertical'
930
- };
931
- const Emphasis = {
932
- low: 'low',
933
- high: 'high'
934
- };
935
- /**
936
- * List of typographies that can't be customized.
937
- */
938
- const TypographyInterface = {
939
- overline: 'overline',
940
- caption: 'caption',
941
- body1: 'body1',
942
- body2: 'body2',
943
- subtitle1: 'subtitle1',
944
- subtitle2: 'subtitle2',
945
- title: 'title',
946
- headline: 'headline',
947
- display1: 'display1'
948
- };
949
- /**
950
- * List of all typographies.
951
- */
952
- const Typography = {
953
- ...TypographyInterface};
954
- /**
955
- * All available aspect ratios.
956
- */
957
- const AspectRatio = {
958
- /** Intrinsic content ratio. */
959
- original: 'original',
960
- /** Ratio 3:2 */
961
- horizontal: 'horizontal',
962
- /** Ratio 1:1 */
963
- square: 'square',
964
- /** Ratio constrained by the parent. */
965
- free: 'free'
966
- };
967
- /**
968
- * Semantic info about the purpose of the component
969
- */
970
- const Kind = {
971
- info: 'info',
972
- success: 'success',
973
- warning: 'warning',
974
- error: 'error'
975
- };
976
- /**
977
- * See SCSS variable $lumx-color-palette
978
- */
979
- const ColorPalette = {
980
- primary: 'primary',
981
- blue: 'blue',
982
- dark: 'dark',
983
- green: 'green',
984
- yellow: 'yellow',
985
- red: 'red',
986
- light: 'light'};
987
- /**
988
- * See SCSS variable $lumx-color-variants
974
+ * AutocompleteMultiple component.
975
+ *
976
+ * @param props Component props.
977
+ * @param ref Component ref.
978
+ * @return React element.
989
979
  */
990
- const ColorVariant = {
991
- N: 'N'
992
- };
993
-
994
- /** ColorPalette with all possible color variant combination */
995
-
996
- ({
997
- [Size.xxs]: 14,
998
- [Size.xs]: 20,
999
- [Size.s]: 24,
1000
- [Size.m]: 36,
1001
- [Size.l]: 64,
1002
- [Size.xl]: 128,
1003
- [Size.xxl]: 256
980
+ const AutocompleteMultiple = forwardRef((props, ref) => {
981
+ const defaultTheme = useTheme();
982
+ const {
983
+ disabledStateProps,
984
+ otherProps
985
+ } = useDisableStateProps(props);
986
+ const {
987
+ anchorToInput,
988
+ children,
989
+ // `chipsAlignment` needs to be here to remove it from `forwardedProps`.
990
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
991
+ chipsAlignment,
992
+ className,
993
+ closeOnClickAway = DEFAULT_PROPS$1e.closeOnClickAway,
994
+ closeOnEscape = DEFAULT_PROPS$1e.closeOnEscape,
995
+ fitToAnchorWidth,
996
+ hasError,
997
+ helper,
998
+ icon,
999
+ inputRef,
1000
+ clearButtonProps,
1001
+ isRequired,
1002
+ isOpen,
1003
+ isValid,
1004
+ label,
1005
+ name,
1006
+ offset,
1007
+ onBlur,
1008
+ onChange,
1009
+ onClear,
1010
+ onClose,
1011
+ onFocus,
1012
+ onInfiniteScroll,
1013
+ onKeyDown,
1014
+ placeholder,
1015
+ placement,
1016
+ selectedChipRender = DEFAULT_PROPS$1e.selectedChipRender,
1017
+ shouldFocusOnClose,
1018
+ theme = defaultTheme,
1019
+ type,
1020
+ value,
1021
+ values = DEFAULT_PROPS$1e.values,
1022
+ ...forwardedProps
1023
+ } = otherProps;
1024
+ return /*#__PURE__*/jsx(Autocomplete, {
1025
+ ref: ref,
1026
+ ...forwardedProps,
1027
+ anchorToInput: anchorToInput,
1028
+ className: classNames.join(className, CLASSNAME$1t),
1029
+ name: name,
1030
+ value: value,
1031
+ onChange: onChange,
1032
+ onKeyDown: onKeyDown,
1033
+ onBlur: onBlur,
1034
+ shouldFocusOnClose: shouldFocusOnClose,
1035
+ onFocus: onFocus,
1036
+ hasError: hasError,
1037
+ helper: helper,
1038
+ icon: icon,
1039
+ inputRef: inputRef,
1040
+ chips: values && values.map((chip, index) => selectedChipRender?.(chip, index, onClear)),
1041
+ ...disabledStateProps,
1042
+ isRequired: isRequired,
1043
+ clearButtonProps: clearButtonProps,
1044
+ isValid: isValid,
1045
+ label: label,
1046
+ placeholder: placeholder,
1047
+ theme: theme,
1048
+ type: type,
1049
+ isOpen: isOpen,
1050
+ closeOnClick: false,
1051
+ closeOnClickAway: closeOnClickAway,
1052
+ closeOnEscape: closeOnEscape,
1053
+ onClose: onClose,
1054
+ offset: offset,
1055
+ placement: placement,
1056
+ fitToAnchorWidth: fitToAnchorWidth,
1057
+ onInfiniteScroll: onInfiniteScroll,
1058
+ children: children
1059
+ });
1004
1060
  });
1005
-
1006
- /**
1007
- * Check if we are running in a true browser (not SSR and not jsdom test environment).
1008
- */
1009
- const IS_BROWSER = typeof window !== 'undefined' && !window.navigator.userAgent.includes('jsdom');
1061
+ AutocompleteMultiple.displayName = COMPONENT_NAME$1v;
1062
+ AutocompleteMultiple.className = CLASSNAME$1t;
1063
+ AutocompleteMultiple.defaultProps = DEFAULT_PROPS$1e;
1010
1064
 
1011
1065
  /**
1012
1066
  * Component display name.
@@ -2380,11 +2434,11 @@ const SelectionChipGroup$1 = (props, {
2380
2434
  const customProps = getChipProps?.(v) || {};
2381
2435
  const chipIsDisabled = customProps.isDisabled || isDisabled;
2382
2436
  const chipName = typeof customProps.children === 'string' ? customProps.children : name;
2383
- const ariaLabel = chipRemoveLabel ? `${chipName} \u2014 ${chipRemoveLabel}` : chipName;
2437
+ const ariaLabel = chipRemoveLabel ? `${chipName} - ${chipRemoveLabel}` : chipName;
2384
2438
  return /*#__PURE__*/jsx(Tooltip, {
2385
2439
  label: !chipIsDisabled ? ariaLabel : undefined,
2386
2440
  children: /*#__PURE__*/jsx(Chip, {
2387
- "aria-label": label,
2441
+ "aria-label": ariaLabel,
2388
2442
  ...customProps,
2389
2443
  size: "s",
2390
2444
  after: /*#__PURE__*/jsx(Icon, {
@@ -6046,6 +6100,12 @@ function useEventCallback(fn) {
6046
6100
  return React__default.useCallback((...args) => ref.current?.(...args), []);
6047
6101
  }
6048
6102
 
6103
+ /** Unref a react ref or element */
6104
+ function unref(maybeElement) {
6105
+ if (maybeElement instanceof HTMLElement) return maybeElement;
6106
+ return maybeElement?.current;
6107
+ }
6108
+
6049
6109
  const useRovingTabIndexContainer = ({
6050
6110
  containerRef,
6051
6111
  itemSelector,
@@ -6056,7 +6116,7 @@ const useRovingTabIndexContainer = ({
6056
6116
  }) => {
6057
6117
  const onItemFocused = useEventCallback(unstableOnItemFocused);
6058
6118
  useIsomorphicLayoutEffect(() => {
6059
- const container = containerRef?.current;
6119
+ const container = unref(containerRef);
6060
6120
  if (!container) {
6061
6121
  return undefined;
6062
6122
  }
@@ -6097,7 +6157,7 @@ const SelectionChipGroup = ({
6097
6157
  chipRemoveLabel,
6098
6158
  ...forwardedProps
6099
6159
  }) => {
6100
- const containerRef = React__default.useRef(null);
6160
+ const [container, setContainer] = React__default.useState(null);
6101
6161
 
6102
6162
  // Store latest values in refs so the event handlers always access current state.
6103
6163
  const valueRef = React__default.useRef(value);
@@ -6105,27 +6165,40 @@ const SelectionChipGroup = ({
6105
6165
  const onChangeRef = React__default.useRef(onChange);
6106
6166
  onChangeRef.current = onChange;
6107
6167
 
6108
- // Attach event listeners
6168
+ // Attach event listeners. Re-runs when the container mounts/unmounts, inputRef or getOptionId change.
6109
6169
  React__default.useEffect(() => {
6110
6170
  return setupSelectionChipGroupEvents({
6111
- getContainer: () => containerRef.current,
6171
+ getContainer: () => container,
6112
6172
  getInput: () => inputRef?.current,
6113
6173
  onChange: newValue => onChangeRef.current?.(newValue),
6114
6174
  getValue: () => valueRef.current,
6115
6175
  getOptionId
6116
6176
  });
6117
- }, [inputRef, getOptionId]);
6177
+ }, [container, inputRef, getOptionId]);
6118
6178
  useRovingTabIndexContainer({
6119
- containerRef,
6179
+ containerRef: container,
6120
6180
  itemSelector: `.${CLASSNAME$1i}`,
6121
6181
  itemDisabledSelector: `.${CLASSNAME$1i}[aria-disabled="true"]`
6122
6182
  });
6123
6183
 
6124
- // Handle renderChip or fallback to getChipProps from props
6125
- const getChipProps = renderChip ? option => {
6126
- const customChip = renderChip(option);
6127
- return isComponentType(Chip)(customChip) && customChip.props || {};
6128
- } : getChipPropsProp;
6184
+ // Merge getChipProps and renderChip: getChipProps provides base props, renderChip overrides them,
6185
+ // and the core JSX template props take final priority (applied in the core component).
6186
+ const getChipProps = option => {
6187
+ const chipProps = getChipPropsProp?.(option) || {};
6188
+ let renderChipProps = {};
6189
+ if (renderChip) {
6190
+ const customChip = renderChip(option);
6191
+ if (isComponentType(Chip)(customChip)) {
6192
+ renderChipProps = customChip.props || {};
6193
+ }
6194
+ }
6195
+ // Filter out undefined values from renderChipProps so they don't override chipProps
6196
+ const definedRenderChipProps = Object.fromEntries(Object.entries(renderChipProps).filter(([, v]) => v !== undefined));
6197
+ return {
6198
+ ...chipProps,
6199
+ ...definedRenderChipProps
6200
+ };
6201
+ };
6129
6202
  return SelectionChipGroup$1({
6130
6203
  ...forwardedProps,
6131
6204
  value,
@@ -6136,7 +6209,7 @@ const SelectionChipGroup = ({
6136
6209
  label,
6137
6210
  chipRemoveLabel,
6138
6211
  getChipProps,
6139
- ref: containerRef
6212
+ ref: setContainer
6140
6213
  }, {
6141
6214
  Chip,
6142
6215
  ChipGroup,
@@ -13495,12 +13568,6 @@ const ImageSlideshow = ({
13495
13568
  });
13496
13569
  };
13497
13570
 
13498
- /** Unref a react ref or element */
13499
- function unref(maybeElement) {
13500
- if (maybeElement instanceof HTMLElement) return maybeElement;
13501
- return maybeElement?.current;
13502
- }
13503
-
13504
13571
  function setupViewTransitionName(elementRef, name) {
13505
13572
  let originalName = null;
13506
13573
  return {