@lumx/react 4.9.0-alpha.1 → 4.9.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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, 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, Alignment, NOTIFICATION_TRANSITION_DURATION } from '@lumx/core/js/constants';
1
+ import { Kind as Kind$1, Size as Size$1, ColorPalette as ColorPalette$1, Emphasis as Emphasis$1, ColorVariant, 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, Alignment as Alignment$1, NOTIFICATION_TRANSITION_DURATION } 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';
@@ -9,7 +9,7 @@ import ReactDOM__default from 'react-dom';
9
9
  import { classNames, onEscapePressed, onEnterPressed as onEnterPressed$1, detectHorizontalSwipe } from '@lumx/core/js/utils';
10
10
  import last from 'lodash/last.js';
11
11
  import pull from 'lodash/pull.js';
12
- import { u as useDisabledStateContext, P as Portal, C as ClickAwayProvider } from './_internal/C9YCH96e.js';
12
+ import { u as useDisabledStateContext, P as Portal, C as ClickAwayProvider } from './_internal/Dpulze-1.js';
13
13
  import isEmpty from 'lodash/isEmpty.js';
14
14
  import get from 'lodash/get.js';
15
15
  import { getDisabledState } from '@lumx/core/js/utils/disabledState';
@@ -892,6 +892,8 @@ const TOOLTIP_LONG_PRESS_DELAY = {
892
892
  /**
893
893
  * Alignments.
894
894
  */
895
+ const Alignment = {
896
+ left: 'left'};
895
897
  const Theme = {
896
898
  light: 'light',
897
899
  dark: 'dark'
@@ -946,7 +948,10 @@ const AspectRatio = {
946
948
  /** Ratio 3:2 */
947
949
  horizontal: 'horizontal',
948
950
  /** Ratio 1:1 */
949
- square: 'square'};
951
+ square: 'square',
952
+ /** Ratio constrained by the parent. */
953
+ free: 'free'
954
+ };
950
955
  /**
951
956
  * Semantic info about the purpose of the component
952
957
  */
@@ -1637,6 +1642,7 @@ const IconButton = forwardRef((props, ref) => {
1637
1642
  tooltipProps,
1638
1643
  hideTooltip,
1639
1644
  label,
1645
+ theme = defaultTheme,
1640
1646
  ...forwardedProps
1641
1647
  } = props;
1642
1648
  const {
@@ -1654,7 +1660,7 @@ const IconButton = forwardRef((props, ref) => {
1654
1660
  ...tooltipProps,
1655
1661
  children: IconButton$1({
1656
1662
  ref,
1657
- theme: defaultTheme,
1663
+ theme,
1658
1664
  ...disabledStateProps,
1659
1665
  ...restOfOtherProps,
1660
1666
  handleClick: onClick,
@@ -3207,7 +3213,7 @@ function setupListbox(handle, signal, notify) {
3207
3213
  *
3208
3214
  * @see https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
3209
3215
  *
3210
- * @param callbacks Callbacks for select and open/close events.
3216
+ * @param callbacks Callbacks invoked on combobox events (e.g. option selection).
3211
3217
  * @param options Options for configuring the shared combobox behavior.
3212
3218
  * @param onTriggerAttach Optional callback invoked when the trigger is registered and the signal is ready.
3213
3219
  * Used by mode-specific wrappers (setupComboboxInput/Button) to automatically
@@ -3238,6 +3244,9 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
3238
3244
  /** Last notified isEmpty state, to avoid redundant `emptyChange` notifications. */
3239
3245
  let lastIsEmpty = true;
3240
3246
 
3247
+ /** Last notified input value, to re-fire `emptyChange` when the user keeps typing while empty. */
3248
+ let lastInputValue = '';
3249
+
3241
3250
  /** Event subscribers managed by the handle. */
3242
3251
  const subscribers = {
3243
3252
  open: new Set(),
@@ -3253,7 +3262,9 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
3253
3262
  }
3254
3263
 
3255
3264
  /**
3256
- * Notify all registered sections and fire `emptyChange` if the visible option count changed.
3265
+ * Notify all registered sections and fire `emptyChange` if the visible option count changed
3266
+ * or if the input value changed while the list is empty (so `emptyMessage` callbacks get
3267
+ * the updated query string).
3257
3268
  * Called whenever the set of visible options may have changed (option register/unregister, filter change).
3258
3269
  */
3259
3270
  function notifyVisibilityChange() {
@@ -3265,9 +3276,10 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
3265
3276
  if (!reg.lastFiltered) visibleCount += 1;
3266
3277
  }
3267
3278
  const isEmpty = visibleCount === 0;
3268
- if (isEmpty !== lastIsEmpty) {
3279
+ const inputValue = trigger?.value ?? '';
3280
+ if (isEmpty !== lastIsEmpty || isEmpty && inputValue !== lastInputValue) {
3269
3281
  lastIsEmpty = isEmpty;
3270
- const inputValue = trigger?.value ?? '';
3282
+ lastInputValue = inputValue;
3271
3283
  notify('emptyChange', {
3272
3284
  isEmpty,
3273
3285
  inputValue
@@ -3286,6 +3298,22 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
3286
3298
  /** Timer for debounced loading announcement. */
3287
3299
  let loadingTimer;
3288
3300
 
3301
+ /** Whether a loading announcement has been sent since the last open. */
3302
+ let announcementSent = false;
3303
+
3304
+ /** Start or restart the debounced loading announcement timer if conditions are met. */
3305
+ function startLoadingAnnouncementTimer() {
3306
+ clearTimeout(loadingTimer);
3307
+ if (skeletonCount > 0 && isOpenState) {
3308
+ loadingTimer = setTimeout(() => {
3309
+ if (skeletonCount > 0 && isOpenState) {
3310
+ announcementSent = true;
3311
+ notify('loadingAnnouncement', true);
3312
+ }
3313
+ }, LOADING_ANNOUNCEMENT_DELAY);
3314
+ }
3315
+ }
3316
+
3289
3317
  /**
3290
3318
  * Called when the skeleton count transitions between 0 and >0 (or vice versa).
3291
3319
  * Fires `loadingChange` immediately and manages the debounced `loadingAnnouncement`.
@@ -3293,15 +3321,14 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
3293
3321
  function onSkeletonCountChange() {
3294
3322
  const isLoading = skeletonCount > 0;
3295
3323
  notify('loadingChange', isLoading);
3296
- clearTimeout(loadingTimer);
3297
3324
  if (isLoading) {
3298
- loadingTimer = setTimeout(() => {
3299
- if (skeletonCount > 0) {
3300
- notify('loadingAnnouncement', true);
3301
- }
3302
- }, LOADING_ANNOUNCEMENT_DELAY);
3325
+ startLoadingAnnouncementTimer();
3303
3326
  } else {
3304
- notify('loadingAnnouncement', false);
3327
+ clearTimeout(loadingTimer);
3328
+ if (announcementSent) {
3329
+ announcementSent = false;
3330
+ notify('loadingAnnouncement', false);
3331
+ }
3305
3332
  }
3306
3333
  }
3307
3334
 
@@ -3343,17 +3370,19 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
3343
3370
  case 'Enter':
3344
3371
  if (handle.isOpen && nav?.hasActiveItem && nav.activeItem) {
3345
3372
  if (!isOptionDisabled(nav.activeItem)) {
3346
- if (nav.type === 'grid' && isActionCell(nav.activeItem)) {
3347
- // Action cell: programmatically click it.
3348
- nav.activeItem.click();
3349
- } else {
3350
- // Option cell: select the option.
3351
- handle.select(nav.activeItem);
3352
- }
3373
+ // Click the active item. For option cells, the delegated click handler
3374
+ // on the listbox will call handle.select() and handle closing.
3375
+ // For action cells and link options, the native click fires too.
3376
+ nav.activeItem.click();
3353
3377
  }
3354
- }
3355
- // In multi-select mode, keep open after selection; otherwise toggle.
3356
- if (!handle.isMultiSelect) {
3378
+ // Close for single-select. For option cells the delegated handler
3379
+ // already closed, but setIsOpen(false) is idempotent. For action cells
3380
+ // and disabled options, the delegated handler did NOT close, so this is needed.
3381
+ if (!handle.isMultiSelect) {
3382
+ handle.setIsOpen(false);
3383
+ }
3384
+ } else if (!handle.isMultiSelect) {
3385
+ // No active item — toggle open/close.
3357
3386
  handle.setIsOpen(!handle.isOpen);
3358
3387
  }
3359
3388
  flag = true;
@@ -3406,10 +3435,12 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
3406
3435
  flag = true;
3407
3436
  break;
3408
3437
  case 'Tab':
3409
- // Select the active option (if any) and close. Let Tab propagate.
3438
+ // Click the active option (if any) and close. Let Tab propagate.
3410
3439
  if (nav?.hasActiveItem && nav.activeItem && !isOptionDisabled(nav.activeItem)) {
3411
- handle.select(nav.activeItem);
3440
+ nav.activeItem.click();
3412
3441
  }
3442
+ // The delegated click handler closes for single-select, but for multi-select
3443
+ // or when no item is active, we still need to explicitly close.
3413
3444
  handle.setIsOpen(false);
3414
3445
  break;
3415
3446
  case 'PageUp':
@@ -3484,16 +3515,38 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
3484
3515
  isOpenState = isOpen;
3485
3516
  if (!isOpen) {
3486
3517
  focusNav?.clear();
3518
+ // Reset announcement state so it retriggers on next open
3519
+ clearTimeout(loadingTimer);
3520
+ if (announcementSent) {
3521
+ announcementSent = false;
3522
+ notify('loadingAnnouncement', false);
3523
+ }
3524
+ } else if (skeletonCount > 0) {
3525
+ // Opening while already loading — start the announcement timer
3526
+ startLoadingAnnouncementTimer();
3487
3527
  }
3488
3528
 
3489
3529
  // Update aria-expanded on trigger
3490
3530
  trigger?.setAttribute('aria-expanded', String(isOpen));
3491
3531
  notify('open', isOpen);
3532
+
3533
+ // When opening, always notify the current empty state so that
3534
+ // subscribers (ComboboxState) get the correct initial value.
3535
+ // Without this, a list that starts empty never fires `emptyChange`
3536
+ // because `lastIsEmpty` is initialized to `true` and `notifyVisibilityChange`
3537
+ // only fires on *changes*.
3538
+ if (isOpen) {
3539
+ const inputValue = trigger?.value ?? '';
3540
+ notify('emptyChange', {
3541
+ isEmpty: lastIsEmpty,
3542
+ inputValue
3543
+ });
3544
+ }
3492
3545
  },
3493
3546
  select(option) {
3494
3547
  callbacks.onSelect({
3495
3548
  value: option ? getOptionValue(option) : ''
3496
- });
3549
+ }, handle);
3497
3550
  },
3498
3551
  registerOption(element, callback) {
3499
3552
  const filterLower = filterValue.toLowerCase();
@@ -3605,10 +3658,12 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
3605
3658
  listbox = null;
3606
3659
  filterValue = '';
3607
3660
  lastIsEmpty = true;
3661
+ lastInputValue = '';
3608
3662
  optionRegistrations.clear();
3609
3663
  sectionRegistrations.clear();
3610
3664
  skeletonCount = 0;
3611
3665
  clearTimeout(loadingTimer);
3666
+ announcementSent = false;
3612
3667
  // Clear all subscribers
3613
3668
  for (const set of Object.values(subscribers)) {
3614
3669
  set.clear();
@@ -3775,13 +3830,8 @@ function setupComboboxButton(button, callbacks) {
3775
3830
  case ' ':
3776
3831
  // Space acts like Enter in button mode.
3777
3832
  if (combobox.isOpen && nav?.hasActiveItem && nav.activeItem) {
3778
- if (nav.type === 'grid' && isActionCell(nav.activeItem)) {
3779
- // Action cell: programmatically click it.
3780
- nav.activeItem.click();
3781
- } else {
3782
- combobox.select(nav.activeItem);
3783
- combobox.setIsOpen(false);
3784
- }
3833
+ // Click the active item — delegated handler handles select + close.
3834
+ nav.activeItem.click();
3785
3835
  } else {
3786
3836
  combobox.setIsOpen(true);
3787
3837
  }
@@ -6563,44 +6613,58 @@ const ComboboxButton = Object.assign(forwardRefPolymorphic((props, ref) => {
6563
6613
  * Handles: Home/End (text cursor), ArrowLeft/Right (clear active descendant),
6564
6614
  * filtering (on input and on open), and focus behavior.
6565
6615
  *
6566
- * @param input The input element to use as the combobox trigger.
6567
- * @param callbacks Callbacks for select and open/close events.
6568
- * @param options Options for configuring the input-mode controller.
6616
+ * @param input The input element to use as the combobox trigger.
6617
+ * @param options Options and callbacks for configuring the input-mode controller.
6569
6618
  * @returns A ComboboxHandle for interacting with the combobox.
6570
6619
  */
6571
- function setupComboboxInput(input, callbacks, options = {}) {
6620
+ function setupComboboxInput(input, options) {
6572
6621
  const {
6573
- autoFilter = true
6622
+ autoFilter = true,
6623
+ onSelect: optionOnSelect
6574
6624
  } = options;
6575
- const handle = setupCombobox(callbacks, {
6625
+
6626
+ /**
6627
+ * True when the current input value came from user typing (real InputEvent).
6628
+ * False when the value was set programmatically (select, clear, etc.).
6629
+ * Used to decide whether to re-apply the filter when the combobox opens.
6630
+ */
6631
+ let userHasTyped = false;
6632
+
6633
+ /**
6634
+ * Wraps the consumer's onSelect to perform input-mode side effects after selection:
6635
+ * clears the active descendant, resets the filter, and re-opens the popup.
6636
+ */
6637
+ const onSelect = (option, combobox) => {
6638
+ optionOnSelect(option, combobox);
6639
+
6640
+ // Clear the active item. In multi-select, keep visual focus so the
6641
+ // user can continue navigating after selection.
6642
+ if (!combobox.isMultiSelect) {
6643
+ combobox.focusNav?.clear();
6644
+ }
6645
+ userHasTyped = false;
6646
+ combobox.setIsOpen(true);
6647
+ if (autoFilter) {
6648
+ combobox.setFilter('');
6649
+ }
6650
+ };
6651
+ const handle = setupCombobox({
6652
+ onSelect
6653
+ }, {
6576
6654
  wrapNavigation: true
6577
6655
  }, (combobox, signal) => {
6578
- /**
6579
- * True when the current input value came from user typing (real InputEvent).
6580
- * False when the value was set programmatically (select, clear, etc.).
6581
- * Used to decide whether to re-apply the filter when the combobox opens.
6582
- */
6583
- let userHasTyped = false;
6584
6656
  signal.addEventListener('abort', () => {
6585
6657
  userHasTyped = false;
6586
6658
  });
6587
6659
 
6588
- // Filter on user typing; reset filter on programmatic input changes.
6589
- // Real user input fires an `InputEvent` (with `inputType`), while
6590
- // programmatic changes (selection bridge, clear, etc.) dispatch a
6591
- // plain `Event('input')` — we use this to distinguish the two.
6660
+ // Filter on real user typing (InputEvent with `inputType`).
6592
6661
  input.addEventListener('input', event => {
6593
- const isUserTyping = event instanceof InputEvent;
6594
-
6595
- // Clear the active item. In multi-select with a programmatic change, keep
6596
- // visual focus so the user can continue navigating after selection.
6597
- if (isUserTyping || !combobox.isMultiSelect) {
6598
- combobox.focusNav?.clear();
6599
- }
6600
- userHasTyped = isUserTyping;
6662
+ if (!(event instanceof InputEvent)) return;
6663
+ combobox.focusNav?.clear();
6664
+ userHasTyped = true;
6601
6665
  combobox.setIsOpen(true);
6602
6666
  if (autoFilter) {
6603
- combobox.setFilter(isUserTyping ? input.value : '');
6667
+ combobox.setFilter(input.value);
6604
6668
  }
6605
6669
  }, {
6606
6670
  signal
@@ -6694,6 +6758,7 @@ const ComboboxInput$1 = (props, {
6694
6758
  textFieldRef,
6695
6759
  toggleButtonProps,
6696
6760
  handleToggle,
6761
+ theme,
6697
6762
  ...forwardedProps
6698
6763
  } = props;
6699
6764
  return /*#__PURE__*/jsx(TextField, {
@@ -6706,8 +6771,10 @@ const ComboboxInput$1 = (props, {
6706
6771
  inputRef: inputRef,
6707
6772
  textFieldRef: textFieldRef,
6708
6773
  autoComplete: "off",
6774
+ theme: theme,
6709
6775
  afterElement: toggleButtonProps ? /*#__PURE__*/jsx(IconButton, {
6710
6776
  ...toggleButtonProps,
6777
+ theme: theme,
6711
6778
  emphasis: "low",
6712
6779
  size: "s",
6713
6780
  icon: isOpen ? mdiChevronUp : mdiChevronDown,
@@ -7229,32 +7296,6 @@ TextField.displayName = COMPONENT_NAME$1d;
7229
7296
  TextField.className = CLASSNAME$1c;
7230
7297
  TextField.defaultProps = DEFAULT_PROPS$Z;
7231
7298
 
7232
- /**
7233
- * Set an input's value using the native HTMLInputElement.prototype.value setter,
7234
- * bypassing React's controlled input value tracker, then dispatch an `input` event
7235
- * so React's onChange fires.
7236
- *
7237
- * React wraps the `value` property setter on controlled inputs to track the "last
7238
- * known value." If you set `input.value` through React's wrapper with the same value
7239
- * React last rendered, the subsequent `input` event is suppressed. Using the native
7240
- * setter bypasses this tracker, ensuring React detects a value change and fires onChange.
7241
- */
7242
- function setNativeInputValue(input, value) {
7243
- const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;
7244
- if (nativeSetter) {
7245
- nativeSetter.call(input, value);
7246
- } else {
7247
- // Fallback: set directly (may not trigger React onChange on controlled inputs)
7248
- // eslint-disable-next-line no-param-reassign
7249
- input.value = value;
7250
- }
7251
-
7252
- // Dispatch input event so React's onChange fires
7253
- input.dispatchEvent(new Event('input', {
7254
- bubbles: true
7255
- }));
7256
- }
7257
-
7258
7299
  /**
7259
7300
  * Props for Combobox.Input component.
7260
7301
  * Note: role, aria-autocomplete, aria-controls, aria-expanded are set internally and cannot be overridden.
@@ -7285,9 +7326,11 @@ const ComboboxInput = forwardRef((props, ref) => {
7285
7326
  const internalInputRef = useRef(null);
7286
7327
  const mergedInputRef = useMergeRefs(externalInputRef, internalInputRef);
7287
7328
 
7288
- // Keep onSelect in a ref to avoid re-creating the handle on every render
7329
+ // Keep callbacks in refs to avoid re-creating the handle on every render
7289
7330
  const onSelectRef = useRef(onSelect);
7290
7331
  onSelectRef.current = onSelect;
7332
+ const onChangeRef = useRef(otherProps.onChange);
7333
+ onChangeRef.current = otherProps.onChange;
7291
7334
 
7292
7335
  // Create the combobox handle with input-mode controller on mount
7293
7336
  useEffect(() => {
@@ -7295,11 +7338,10 @@ const ComboboxInput = forwardRef((props, ref) => {
7295
7338
  if (!input) return undefined;
7296
7339
  const handle = setupComboboxInput(input, {
7297
7340
  onSelect(option) {
7298
- // Bridge to React's controlled input flow.
7299
- setNativeInputValue(input, option.value);
7341
+ // Update controlled value through React's normal onChange flow.
7342
+ onChangeRef.current?.(option.value);
7300
7343
  onSelectRef.current?.(option);
7301
- }
7302
- }, {
7344
+ },
7303
7345
  autoFilter
7304
7346
  });
7305
7347
  setHandle(handle);
@@ -7737,6 +7779,7 @@ const ComboboxOption$1 = (props, {
7737
7779
  isGrid,
7738
7780
  isSelected,
7739
7781
  handleClick,
7782
+ actionProps,
7740
7783
  ref,
7741
7784
  tooltipProps,
7742
7785
  value,
@@ -7749,7 +7792,8 @@ const ComboboxOption$1 = (props, {
7749
7792
  itemRole = isGrid ? 'row' : 'none';
7750
7793
  }
7751
7794
  const actionElement = ListItemAction$1({
7752
- as: 'p',
7795
+ as: 'button',
7796
+ ...actionProps,
7753
7797
  id,
7754
7798
  className: element$H('trigger'),
7755
7799
  handleClick,
@@ -7832,6 +7876,7 @@ const ComboboxOption = forwardRef((props, ref) => {
7832
7876
  before,
7833
7877
  after,
7834
7878
  tooltipProps,
7879
+ actionProps,
7835
7880
  onClick,
7836
7881
  ...forwardedProps
7837
7882
  } = props;
@@ -7865,6 +7910,7 @@ const ComboboxOption = forwardRef((props, ref) => {
7865
7910
  return ComboboxOption$1({
7866
7911
  ...forwardedProps,
7867
7912
  ref: mergedRef,
7913
+ actionProps,
7868
7914
  hidden: isFiltered,
7869
7915
  value,
7870
7916
  description,
@@ -8011,14 +8057,16 @@ const ComboboxOptionMoreInfo$1 = (props, {
8011
8057
  popoverId,
8012
8058
  ref,
8013
8059
  onMouseEnter,
8014
- onMouseLeave
8060
+ onMouseLeave,
8061
+ buttonProps
8015
8062
  } = props;
8016
8063
  return /*#__PURE__*/jsxs(Fragment, {
8017
8064
  children: [/*#__PURE__*/jsx(IconButton, {
8018
8065
  ref: ref,
8019
- className: block$R([className]),
8020
8066
  icon: mdiInformationOutline,
8021
8067
  size: "s",
8068
+ ...buttonProps,
8069
+ className: block$R([className, buttonProps?.className]),
8022
8070
  emphasis: "low",
8023
8071
  onMouseEnter: onMouseEnter,
8024
8072
  onMouseLeave: onMouseLeave
@@ -8036,6 +8084,7 @@ const ComboboxOptionMoreInfo$1 = (props, {
8036
8084
  closeOnEscape: true,
8037
8085
  closeOnClickAway: true,
8038
8086
  placement: "bottom-end",
8087
+ hasArrow: true,
8039
8088
  children: children
8040
8089
  })]
8041
8090
  });
@@ -8720,6 +8769,7 @@ const ComboboxOptionMoreInfo = props => {
8720
8769
  const {
8721
8770
  children,
8722
8771
  onToggle,
8772
+ buttonProps,
8723
8773
  ...forwardedProps
8724
8774
  } = props;
8725
8775
  const ref = useRef(null);
@@ -8750,6 +8800,7 @@ const ComboboxOptionMoreInfo = props => {
8750
8800
  isOpen,
8751
8801
  popoverId,
8752
8802
  children,
8803
+ buttonProps,
8753
8804
  onMouseEnter: () => setIsHovered(true),
8754
8805
  onMouseLeave: () => setIsHovered(false)
8755
8806
  }, {
@@ -8835,9 +8886,10 @@ const ComboboxOptionSkeleton$1 = props => {
8835
8886
  children,
8836
8887
  className,
8837
8888
  ref,
8889
+ count = 1,
8838
8890
  ...forwardedProps
8839
8891
  } = props;
8840
- return ListItem$1({
8892
+ const itemProps = {
8841
8893
  ref,
8842
8894
  size: 'tiny',
8843
8895
  role: 'none',
@@ -8852,6 +8904,13 @@ const ComboboxOptionSkeleton$1 = props => {
8852
8904
  theme: "light"
8853
8905
  })]
8854
8906
  })
8907
+ };
8908
+ return /*#__PURE__*/jsx(Fragment, {
8909
+ children: Array.from({
8910
+ length: count
8911
+ }, (_, i) => /*#__PURE__*/jsx(ListItem$1, {
8912
+ ...itemProps
8913
+ }, i))
8855
8914
  });
8856
8915
  };
8857
8916
 
@@ -8880,19 +8939,13 @@ const ComboboxOptionSkeleton$1 = props => {
8880
8939
  * @return React element(s).
8881
8940
  */
8882
8941
  const ComboboxOptionSkeleton = props => {
8883
- const {
8884
- count = 1,
8885
- ...itemProps
8886
- } = props;
8887
8942
  const {
8888
8943
  handle
8889
8944
  } = useComboboxContext();
8890
8945
  useEffect(() => handle?.registerSkeleton(), [handle]);
8891
- return Array.from({
8892
- length: count
8893
- }, (_, i) => /*#__PURE__*/jsx(ComboboxOptionSkeleton$1, {
8894
- ...itemProps
8895
- }, i));
8946
+ return /*#__PURE__*/jsx(ComboboxOptionSkeleton$1, {
8947
+ ...props
8948
+ });
8896
8949
  };
8897
8950
  ComboboxOptionSkeleton.displayName = COMPONENT_NAME$12;
8898
8951
  ComboboxOptionSkeleton.className = CLASSNAME$11;
@@ -9443,11 +9496,12 @@ const ListSection$1 = props => {
9443
9496
  ...forwardedProps
9444
9497
  } = props;
9445
9498
  const labelId = `${id}-label`;
9499
+ const hasHeader = !!label;
9446
9500
  return /*#__PURE__*/jsxs("li", {
9447
9501
  ref: ref,
9448
9502
  ...forwardedProps,
9449
9503
  className: classnames(className, block$O()),
9450
- children: [label && /*#__PURE__*/jsxs(Text, {
9504
+ children: [hasHeader && /*#__PURE__*/jsxs(Text, {
9451
9505
  as: "p",
9452
9506
  typography: "overline",
9453
9507
  className: element$D('title'),
@@ -9458,7 +9512,7 @@ const ListSection$1 = props => {
9458
9512
  }), /*#__PURE__*/jsx("ul", {
9459
9513
  ...itemsWrapperProps,
9460
9514
  className: element$D('items'),
9461
- "aria-labelledby": label ? labelId : undefined,
9515
+ "aria-labelledby": hasHeader ? labelId : undefined,
9462
9516
  children: children
9463
9517
  })]
9464
9518
  });
@@ -9619,6 +9673,7 @@ const ComboboxState$1 = (props, {
9619
9673
  loadingMessage,
9620
9674
  state
9621
9675
  } = props;
9676
+ const isOpen = state?.isOpen ?? true;
9622
9677
  const showError = !!errorMessage;
9623
9678
  const resolvedEmptyMessage = typeof emptyMessage === 'function' ? emptyMessage(state?.inputValue || '') : emptyMessage;
9624
9679
  // Suppress empty while loading (immediate flag prevents false "no results" before data arrives)
@@ -9632,6 +9687,12 @@ const ComboboxState$1 = (props, {
9632
9687
  hAlign: 'center',
9633
9688
  vAlign: 'center'
9634
9689
  };
9690
+
9691
+ // Gate message content behind isOpen so that content is *inserted* into the
9692
+ // aria-live region when the popover opens, triggering screen reader announcements.
9693
+ // Without this gate, content is already present (just hidden via display:none from
9694
+ // the popover's closeMode="hide") and revealing it doesn't trigger announcements.
9695
+ const renderContent = isOpen;
9635
9696
  return /*#__PURE__*/jsxs(GenericBlock, {
9636
9697
  className: classnames(!show && visuallyHidden(), block$N(), padding('regular')),
9637
9698
  orientation: "vertical",
@@ -9639,17 +9700,17 @@ const ComboboxState$1 = (props, {
9639
9700
  role: "status",
9640
9701
  "aria-live": "polite",
9641
9702
  "aria-atomic": true,
9642
- children: [showEmpty && /*#__PURE__*/jsx(Text, {
9703
+ children: [renderContent && showEmpty && /*#__PURE__*/jsx(Text, {
9643
9704
  as: "p",
9644
9705
  typography: "body1",
9645
9706
  color: "dark-L2",
9646
9707
  children: resolvedEmptyMessage
9647
- }), showLoading && /*#__PURE__*/jsx(Text, {
9708
+ }), renderContent && showLoading && /*#__PURE__*/jsx(Text, {
9648
9709
  as: "p",
9649
9710
  typography: "body1",
9650
9711
  color: "dark-L2",
9651
9712
  children: loadingMessage
9652
- }), !!errorMessage && /*#__PURE__*/jsxs(Fragment, {
9713
+ }), renderContent && !!errorMessage && /*#__PURE__*/jsxs(Fragment, {
9653
9714
  children: [/*#__PURE__*/jsx(Text, {
9654
9715
  as: "p",
9655
9716
  typography: "subtitle2",
@@ -9664,6 +9725,62 @@ const ComboboxState$1 = (props, {
9664
9725
  });
9665
9726
  };
9666
9727
 
9728
+ /**
9729
+ * Delay before inserting content into the aria-live region after the popover opens (ms).
9730
+ *
9731
+ * The popover uses `closeMode="hide"` (`display:none` when closed), so the live region
9732
+ * container is not in the accessibility tree until the popover becomes visible.
9733
+ * Screen readers only detect content *changes* in live regions that are already visible.
9734
+ * This delay ensures the popover's `display:none` is removed and the accessibility tree
9735
+ * is updated before content is inserted, so screen readers reliably announce it.
9736
+ */
9737
+ const OPEN_ANNOUNCEMENT_DELAY = 100;
9738
+
9739
+ /** Setters invoked by `subscribeComboboxState` when handle events fire. */
9740
+
9741
+ /**
9742
+ * Subscribe to the combobox handle events needed by `ComboboxState`.
9743
+ *
9744
+ * Manages three subscriptions:
9745
+ * - `loadingChange` → `setIsLoading` (+ synchronous initial read of `handle.isLoading`)
9746
+ * - `loadingAnnouncement` → `setShouldAnnounce`
9747
+ * - `open` → `setIsOpen` (deferred by {@link OPEN_ANNOUNCEMENT_DELAY}ms on open, immediate on close)
9748
+ *
9749
+ * @param handle The combobox handle to subscribe to.
9750
+ * @param setters Framework-specific state setters.
9751
+ * @returns A cleanup function that unsubscribes all events and clears timers.
9752
+ */
9753
+ function subscribeComboboxState(handle, setters) {
9754
+ const {
9755
+ setIsLoading,
9756
+ setShouldAnnounce,
9757
+ setIsOpen
9758
+ } = setters;
9759
+
9760
+ // Read current loading state synchronously
9761
+ setIsLoading(handle.isLoading);
9762
+ const unsubLoadingChange = handle.subscribe('loadingChange', setIsLoading);
9763
+ const unsubLoadingAnnouncement = handle.subscribe('loadingAnnouncement', setShouldAnnounce);
9764
+ let openTimer;
9765
+ const unsubOpen = handle.subscribe('open', open => {
9766
+ clearTimeout(openTimer);
9767
+ if (open) {
9768
+ // Delay content insertion so the popover is visible in the
9769
+ // accessibility tree before the live region content changes.
9770
+ openTimer = setTimeout(() => setIsOpen(true), OPEN_ANNOUNCEMENT_DELAY);
9771
+ } else {
9772
+ // Reset immediately on close
9773
+ setIsOpen(false);
9774
+ }
9775
+ });
9776
+ return () => {
9777
+ unsubLoadingChange();
9778
+ unsubLoadingAnnouncement();
9779
+ unsubOpen();
9780
+ clearTimeout(openTimer);
9781
+ };
9782
+ }
9783
+
9667
9784
  /**
9668
9785
  * Similar to lodash `partition` function but working with multiple predicates.
9669
9786
  *
@@ -9967,25 +10084,21 @@ const ComboboxState = props => {
9967
10084
  handle
9968
10085
  } = useComboboxContext();
9969
10086
  const emptyState = useComboboxEvent('emptyChange', undefined);
9970
-
9971
- // Track loading state with both initial read and event subscription
9972
- // (same pattern as ComboboxList for aria-busy)
9973
10087
  const [isLoading, setIsLoading] = useState(false);
9974
10088
  const [shouldAnnounce, setShouldAnnounce] = useState(false);
10089
+ const [isOpen, setIsOpen] = useState(false);
9975
10090
  useEffect(() => {
9976
10091
  if (!handle) return undefined;
9977
- // Read current state synchronously
9978
- setIsLoading(handle.isLoading);
9979
- const unsub1 = handle.subscribe('loadingChange', setIsLoading);
9980
- const unsub2 = handle.subscribe('loadingAnnouncement', setShouldAnnounce);
9981
- return () => {
9982
- unsub1();
9983
- unsub2();
9984
- };
10092
+ return subscribeComboboxState(handle, {
10093
+ setIsLoading,
10094
+ setShouldAnnounce,
10095
+ setIsOpen
10096
+ });
9985
10097
  }, [handle]);
9986
10098
  const state = {
9987
10099
  ...emptyState,
9988
- isLoading
10100
+ isLoading,
10101
+ isOpen
9989
10102
  };
9990
10103
 
9991
10104
  // Only pass loadingMessage to core after the 500ms debounce threshold
@@ -10006,19 +10119,32 @@ ComboboxState.className = CLASSNAME$X;
10006
10119
  * Combobox compound component namespace.
10007
10120
  */
10008
10121
  const Combobox = {
10122
+ /** Provides shared combobox context (handle, listbox ID, anchor ref) to all sub-components. */
10009
10123
  Provider: ComboboxProvider,
10124
+ /** Button trigger for select-only combobox mode with keyboard navigation and typeahead. */
10010
10125
  Button: ComboboxButton,
10126
+ /** Text input trigger for autocomplete combobox mode with optional toggle button and filtering. */
10011
10127
  Input: ComboboxInput,
10128
+ /** Listbox container that registers with the combobox handle and tracks loading state. */
10012
10129
  List: ComboboxList,
10130
+ /** Selectable option item with filtering and keyboard navigation support. */
10013
10131
  Option: ComboboxOption,
10132
+ /** Secondary action button within a grid-mode option row, rendered as an independent gridcell. */
10014
10133
  OptionAction: ComboboxOptionAction,
10134
+ /** Info button on an option that shows a popover on hover or keyboard highlight. */
10015
10135
  OptionMoreInfo: ComboboxOptionMoreInfo,
10136
+ /** Loading placeholder skeleton(s) that auto-register loading state with the combobox handle. */
10016
10137
  OptionSkeleton: ComboboxOptionSkeleton,
10138
+ /** Floating popover container that auto-binds to the combobox anchor and open/close state. */
10017
10139
  Popover: ComboboxPopover,
10140
+ /** Labelled group of options that auto-hides when all its child options are filtered out. */
10018
10141
  Section: ComboboxSection,
10142
+ /** Displays empty, error, and loading state messages for the combobox list. */
10019
10143
  State: ComboboxState,
10020
10144
  /** Visual separator between option groups (alias for ListDivider). Purely decorative — invisible to screen readers. */
10021
- Divider: ListDivider
10145
+ Divider: ListDivider,
10146
+ /** Hook to subscribe to combobox events. Must be used within a Combobox.Provider. */
10147
+ useComboboxEvent
10022
10148
  };
10023
10149
 
10024
10150
  /**
@@ -11523,7 +11649,7 @@ const CLASSNAME$P = 'lumx-expansion-panel';
11523
11649
  const {
11524
11650
  block: block$G,
11525
11651
  element: element$x
11526
- } = classNames.bem(CLASSNAME$P);
11652
+ } = bem(CLASSNAME$P);
11527
11653
 
11528
11654
  /**
11529
11655
  * Component default props.
@@ -11531,9 +11657,6 @@ const {
11531
11657
  const DEFAULT_PROPS$L = {
11532
11658
  closeMode: 'unmount'
11533
11659
  };
11534
- const isDragHandle = isComponent(DragHandle);
11535
- const isHeader = isComponent('header');
11536
- const isFooter = isComponent('footer');
11537
11660
 
11538
11661
  /**
11539
11662
  * ExpansionPanel component.
@@ -11542,48 +11665,45 @@ const isFooter = isComponent('footer');
11542
11665
  * @param ref Component ref.
11543
11666
  * @return React element.
11544
11667
  */
11545
- const ExpansionPanel = forwardRef((props, ref) => {
11546
- const defaultTheme = useTheme() || Theme$1.light;
11668
+ const ExpansionPanel$1 = props => {
11547
11669
  const {
11548
11670
  className,
11549
- closeMode = DEFAULT_PROPS$L.closeMode,
11550
11671
  children: anyChildren,
11551
11672
  hasBackground,
11673
+ ref,
11552
11674
  hasHeaderDivider,
11553
11675
  isOpen,
11554
11676
  label,
11555
- onClose,
11556
- onOpen,
11557
- onToggleOpen,
11558
- theme = defaultTheme,
11677
+ handleClose,
11678
+ handleOpen,
11679
+ handleToggleOpen,
11680
+ theme,
11559
11681
  toggleButtonProps,
11682
+ headerProps,
11683
+ headerContent,
11684
+ dragHandle,
11685
+ wrapperRef,
11686
+ content,
11687
+ isChildrenVisible,
11688
+ IconButton,
11689
+ footer,
11690
+ closeMode,
11560
11691
  ...forwardedProps
11561
11692
  } = props;
11562
- const children = Children.toArray(anyChildren);
11563
-
11564
- // Partition children by types.
11565
- const [[dragHandle], [header], [footer], content] = partitionMulti(children, [isDragHandle, isHeader, isFooter]);
11566
-
11567
- // Either take the header in children or create one with the label.
11568
- const headerProps = /*#__PURE__*/React__default.isValidElement(header) ? header.props : {};
11569
- const headerContent = !isEmpty(headerProps.children) ? headerProps.children : /*#__PURE__*/jsx("span", {
11570
- className: element$x('label'),
11571
- children: label
11572
- });
11573
11693
  const toggleOpen = event => {
11574
11694
  const shouldOpen = !isOpen;
11575
- if (onOpen && shouldOpen) {
11576
- onOpen(event);
11695
+ if (handleOpen && shouldOpen) {
11696
+ handleOpen(event);
11577
11697
  }
11578
- if (onClose && !shouldOpen) {
11579
- onClose(event);
11698
+ if (handleClose && !shouldOpen) {
11699
+ handleClose(event);
11580
11700
  }
11581
- if (onToggleOpen) {
11582
- onToggleOpen(shouldOpen, event);
11701
+ if (handleToggleOpen) {
11702
+ handleToggleOpen(shouldOpen, event);
11583
11703
  }
11584
11704
  };
11585
- const color = theme === Theme$1.dark ? ColorPalette$1.light : ColorPalette$1.dark;
11586
- const rootClassName = classNames.join(className, block$G({
11705
+ const color = theme === Theme.dark ? ColorPalette.light : ColorPalette.dark;
11706
+ const rootClassName = classnames(className, block$G({
11587
11707
  'has-background': hasBackground,
11588
11708
  'has-header': Boolean(!isEmpty(headerProps.children)),
11589
11709
  'has-header-divider': hasHeaderDivider,
@@ -11592,35 +11712,6 @@ const ExpansionPanel = forwardRef((props, ref) => {
11592
11712
  'is-open': isOpen,
11593
11713
  [`theme-${theme}`]: Boolean(theme)
11594
11714
  }));
11595
- const wrapperRef = useRef(null);
11596
-
11597
- // Children stay visible while the open/close transition is running
11598
- const [isChildrenVisible, setChildrenVisible] = React__default.useState(isOpen);
11599
- const isOpenRef = React__default.useRef(isOpen);
11600
- React__default.useEffect(() => {
11601
- if (isOpen || closeMode === 'hide') {
11602
- setChildrenVisible(true);
11603
- } else if (!IS_BROWSER$1) {
11604
- // Outside a browser we can't wait for the transition
11605
- setChildrenVisible(false);
11606
- }
11607
- isOpenRef.current = isOpen;
11608
- }, [closeMode, isOpen]);
11609
-
11610
- // Change children's visibility on the transition end
11611
- React__default.useEffect(() => {
11612
- const {
11613
- current: wrapper
11614
- } = wrapperRef;
11615
- if (!IS_BROWSER$1 || !wrapper) {
11616
- return undefined;
11617
- }
11618
- const onTransitionEnd = () => {
11619
- setChildrenVisible(isOpenRef.current || closeMode === 'hide');
11620
- };
11621
- wrapper.addEventListener('transitionend', onTransitionEnd);
11622
- return () => wrapper.removeEventListener('transitionend', onTransitionEnd);
11623
- }, [closeMode]);
11624
11715
  return /*#__PURE__*/jsxs("section", {
11625
11716
  ref: ref,
11626
11717
  ...forwardedProps,
@@ -11640,7 +11731,7 @@ const ExpansionPanel = forwardRef((props, ref) => {
11640
11731
  children: /*#__PURE__*/jsx(IconButton, {
11641
11732
  ...toggleButtonProps,
11642
11733
  color: color,
11643
- emphasis: Emphasis$1.low,
11734
+ emphasis: Emphasis.low,
11644
11735
  icon: isOpen ? mdiChevronUp : mdiChevronDown,
11645
11736
  "aria-expanded": isOpen || 'false'
11646
11737
  })
@@ -11660,6 +11751,92 @@ const ExpansionPanel = forwardRef((props, ref) => {
11660
11751
  })
11661
11752
  })]
11662
11753
  });
11754
+ };
11755
+
11756
+ const isDragHandle = isComponent(DragHandle);
11757
+ const isHeader = isComponent('header');
11758
+ const isFooter = isComponent('footer');
11759
+
11760
+ /**
11761
+ * ExpansionPanel component.
11762
+ *
11763
+ * @param props Component props.
11764
+ * @param ref Component ref.
11765
+ * @return React element.
11766
+ */
11767
+ const ExpansionPanel = forwardRef((props, ref) => {
11768
+ const defaultTheme = useTheme() || Theme$1.light;
11769
+ const {
11770
+ closeMode = DEFAULT_PROPS$L.closeMode,
11771
+ children: anyChildren,
11772
+ isOpen,
11773
+ label,
11774
+ onClose,
11775
+ onOpen,
11776
+ onToggleOpen,
11777
+ theme = defaultTheme,
11778
+ ...forwardedProps
11779
+ } = props;
11780
+ const children = Children.toArray(anyChildren);
11781
+
11782
+ // Partition children by types.
11783
+ const [[dragHandle], [header], [footer], content] = partitionMulti(children, [isDragHandle, isHeader, isFooter]);
11784
+
11785
+ // Either take the header in children or create one with the label.
11786
+ const headerProps = /*#__PURE__*/React__default.isValidElement(header) ? header.props : {};
11787
+ const headerContent = !isEmpty(headerProps.children) ? headerProps.children : /*#__PURE__*/jsx("span", {
11788
+ className: element$x('label'),
11789
+ children: label
11790
+ });
11791
+ const wrapperRef = useRef(null);
11792
+
11793
+ // Children stay visible while the open/close transition is running
11794
+ const [isChildrenVisible, setChildrenVisible] = React__default.useState(isOpen);
11795
+ const isOpenRef = React__default.useRef(isOpen);
11796
+ React__default.useEffect(() => {
11797
+ if (isOpen || closeMode === 'hide') {
11798
+ setChildrenVisible(true);
11799
+ } else if (!IS_BROWSER$1) {
11800
+ // Outside a browser we can't wait for the transition
11801
+ setChildrenVisible(false);
11802
+ }
11803
+ isOpenRef.current = isOpen;
11804
+ }, [closeMode, isOpen]);
11805
+
11806
+ // Change children's visibility on the transition end
11807
+ React__default.useEffect(() => {
11808
+ const {
11809
+ current: wrapper
11810
+ } = wrapperRef;
11811
+ if (!IS_BROWSER$1 || !wrapper) {
11812
+ return undefined;
11813
+ }
11814
+ const onTransitionEnd = () => {
11815
+ setChildrenVisible(isOpenRef.current || closeMode === 'hide');
11816
+ };
11817
+ wrapper.addEventListener('transitionend', onTransitionEnd);
11818
+ return () => wrapper.removeEventListener('transitionend', onTransitionEnd);
11819
+ }, [closeMode]);
11820
+ return ExpansionPanel$1({
11821
+ content,
11822
+ dragHandle,
11823
+ footer,
11824
+ headerContent,
11825
+ ref,
11826
+ headerProps,
11827
+ wrapperRef,
11828
+ IconButton,
11829
+ isOpen,
11830
+ handleClose: onClose,
11831
+ handleToggleOpen: onToggleOpen,
11832
+ handleOpen: onOpen,
11833
+ theme,
11834
+ isChildrenVisible,
11835
+ children,
11836
+ closeMode,
11837
+ label,
11838
+ ...forwardedProps
11839
+ });
11663
11840
  });
11664
11841
  ExpansionPanel.displayName = COMPONENT_NAME$O;
11665
11842
  ExpansionPanel.className = CLASSNAME$P;
@@ -12237,7 +12414,7 @@ const {
12237
12414
  */
12238
12415
  const DEFAULT_PROPS$F = {
12239
12416
  captionPosition: ImageBlockCaptionPosition.below,
12240
- align: Alignment.left
12417
+ align: Alignment$1.left
12241
12418
  };
12242
12419
 
12243
12420
  /**
@@ -13651,7 +13828,7 @@ const CLASSNAME$B = 'lumx-mosaic';
13651
13828
  const {
13652
13829
  block: block$v,
13653
13830
  element: element$n
13654
- } = classNames.bem(CLASSNAME$B);
13831
+ } = bem(CLASSNAME$B);
13655
13832
 
13656
13833
  /**
13657
13834
  * Component default props.
@@ -13665,26 +13842,27 @@ const DEFAULT_PROPS$C = {};
13665
13842
  * @param ref Component ref.
13666
13843
  * @return React element.
13667
13844
  */
13668
- const Mosaic = forwardRef((props, ref) => {
13669
- const defaultTheme = useTheme() || Theme$1.light;
13845
+ const Mosaic$1 = props => {
13670
13846
  const {
13671
13847
  className,
13672
- theme = defaultTheme,
13848
+ theme,
13673
13849
  thumbnails,
13674
- onImageClick,
13850
+ handleClick,
13851
+ Thumbnail,
13852
+ ref,
13675
13853
  ...forwardedProps
13676
13854
  } = props;
13677
- const handleImageClick = useMemo(() => {
13678
- if (!onImageClick) return undefined;
13855
+ const onImageClick = () => {
13856
+ if (!handleClick) return undefined;
13679
13857
  return (index, onClick) => event => {
13680
13858
  onClick?.(event);
13681
- onImageClick?.(index);
13859
+ handleClick?.(index);
13682
13860
  };
13683
- }, [onImageClick]);
13861
+ };
13684
13862
  return /*#__PURE__*/jsx("div", {
13685
13863
  ref: ref,
13686
13864
  ...forwardedProps,
13687
- className: classNames.join(className, block$v({
13865
+ className: classnames(className, block$v({
13688
13866
  [`theme-${theme}`]: Boolean(theme),
13689
13867
  'has-1-thumbnail': thumbnails?.length === 1,
13690
13868
  'has-2-thumbnails': thumbnails?.length === 2,
@@ -13707,9 +13885,9 @@ const Mosaic = forwardRef((props, ref) => {
13707
13885
  align: align || Alignment.left,
13708
13886
  image: image,
13709
13887
  theme: theme,
13710
- aspectRatio: AspectRatio$1.free,
13888
+ aspectRatio: AspectRatio.free,
13711
13889
  fillHeight: true,
13712
- onClick: handleImageClick?.(index, onClick) || onClick
13890
+ onClick: onImageClick()?.(index, onClick) || onClick
13713
13891
  }), thumbnails.length > 4 && index === 3 && /*#__PURE__*/jsx("div", {
13714
13892
  className: element$n('overlay'),
13715
13893
  children: /*#__PURE__*/jsxs("span", {
@@ -13720,6 +13898,33 @@ const Mosaic = forwardRef((props, ref) => {
13720
13898
  })
13721
13899
  })
13722
13900
  });
13901
+ };
13902
+
13903
+ /**
13904
+ * Defines the props of the component.
13905
+ */
13906
+
13907
+ /**
13908
+ * Mosaic component.
13909
+ *
13910
+ * @param props Component props.
13911
+ * @param ref Component ref.
13912
+ * @return React element.
13913
+ */
13914
+ const Mosaic = forwardRef((props, ref) => {
13915
+ const defaultTheme = useTheme() || Theme$1.light;
13916
+ const {
13917
+ theme = defaultTheme,
13918
+ onImageClick,
13919
+ ...forwardedProps
13920
+ } = props;
13921
+ return Mosaic$1({
13922
+ ref,
13923
+ theme,
13924
+ Thumbnail,
13925
+ handleClick: onImageClick,
13926
+ ...forwardedProps
13927
+ });
13723
13928
  });
13724
13929
  Mosaic.displayName = COMPONENT_NAME$A;
13725
13930
  Mosaic.className = CLASSNAME$B;
@@ -14519,7 +14724,6 @@ const reducer = (state, action) => {
14519
14724
  if (state.activeTabIndex === action.payload) {
14520
14725
  return state;
14521
14726
  }
14522
- // Change active tab index.
14523
14727
  return {
14524
14728
  ...state,
14525
14729
  activeTabIndex: action.payload
@@ -14531,7 +14735,6 @@ const reducer = (state, action) => {
14531
14735
  type,
14532
14736
  id
14533
14737
  } = action.payload;
14534
- // Append tab/tabPanel id in state.
14535
14738
  return {
14536
14739
  ...state,
14537
14740
  ids: {
@@ -14548,7 +14751,6 @@ const reducer = (state, action) => {
14548
14751
  } = action.payload;
14549
14752
  const index = state.ids[type].indexOf(id);
14550
14753
  if (index === -1) return state;
14551
- // Remove tab & tab panel at index.
14552
14754
  const tabIds = [...state.ids.tab];
14553
14755
  tabIds.splice(index, 1);
14554
14756
  const tabPanelIds = [...state.ids.tabPanel];
@@ -14565,7 +14767,9 @@ const reducer = (state, action) => {
14565
14767
  return state;
14566
14768
  }
14567
14769
  };
14770
+
14568
14771
  const TabProviderContext = /*#__PURE__*/createContext(null);
14772
+
14569
14773
  /* eslint-disable react-hooks/rules-of-hooks */
14570
14774
  const useTabProviderContext = (type, originalId) => {
14571
14775
  const context = useContext(TabProviderContext);
@@ -17378,7 +17582,7 @@ const Switch$1 = props => {
17378
17582
  * Component default props.
17379
17583
  */
17380
17584
  const DEFAULT_PROPS$c = {
17381
- position: Alignment.left
17585
+ position: Alignment$1.left
17382
17586
  };
17383
17587
 
17384
17588
  /**
@@ -17937,7 +18141,7 @@ const TABS_CLASSNAME = `lumx-tabs`;
17937
18141
  const {
17938
18142
  block: block$6,
17939
18143
  element: element$4
17940
- } = classNames.bem(TABS_CLASSNAME);
18144
+ } = bem(TABS_CLASSNAME);
17941
18145
  let TabListLayout = /*#__PURE__*/function (TabListLayout) {
17942
18146
  TabListLayout["clustered"] = "clustered";
17943
18147
  TabListLayout["fixed"] = "fixed";
@@ -17970,26 +18174,21 @@ const DEFAULT_PROPS$6 = {
17970
18174
  * @param ref Component ref.
17971
18175
  * @return React element.
17972
18176
  */
17973
- const TabList = forwardRef((props, ref) => {
17974
- const defaultTheme = useTheme() || Theme$1.light;
18177
+ const TabList$1 = props => {
17975
18178
  const {
17976
18179
  'aria-label': ariaLabel,
17977
18180
  children,
17978
18181
  className,
17979
18182
  layout = DEFAULT_PROPS$6.layout,
17980
18183
  position = DEFAULT_PROPS$6.position,
17981
- theme = defaultTheme,
18184
+ theme,
18185
+ ref,
17982
18186
  ...forwardedProps
17983
18187
  } = props;
17984
- const tabListRef = React__default.useRef(null);
17985
- useRovingTabIndexContainer({
17986
- containerRef: tabListRef,
17987
- itemSelector: '[role="tab"]'
17988
- });
17989
18188
  return /*#__PURE__*/jsx("div", {
17990
- ref: mergeRefs(ref, tabListRef),
18189
+ ref: ref,
17991
18190
  ...forwardedProps,
17992
- className: classNames.join(className, block$6({
18191
+ className: classnames(className, block$6({
17993
18192
  [`layout-${layout}`]: Boolean(layout),
17994
18193
  [`position-${position}`]: Boolean(position),
17995
18194
  [`theme-${theme}`]: Boolean(theme)
@@ -18001,6 +18200,33 @@ const TabList = forwardRef((props, ref) => {
18001
18200
  children: children
18002
18201
  })
18003
18202
  });
18203
+ };
18204
+
18205
+ /**
18206
+ * TabList component.
18207
+ *
18208
+ * Implements WAI-ARIA `tablist` role {@see https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-1/tabs.html#rps_label}
18209
+ *
18210
+ * @param props Component props.
18211
+ * @param ref Component ref.
18212
+ * @return React element.
18213
+ */
18214
+ const TabList = forwardRef((props, ref) => {
18215
+ const defaultTheme = useTheme() || Theme$1.light;
18216
+ const {
18217
+ theme = defaultTheme,
18218
+ ...forwardedProps
18219
+ } = props;
18220
+ const tabListRef = React__default.useRef(null);
18221
+ useRovingTabIndexContainer({
18222
+ containerRef: tabListRef,
18223
+ itemSelector: '[role="tab"]'
18224
+ });
18225
+ return TabList$1({
18226
+ theme,
18227
+ ref: mergeRefs(ref, tabListRef),
18228
+ ...forwardedProps
18229
+ });
18004
18230
  });
18005
18231
  TabList.displayName = COMPONENT_NAME$5;
18006
18232
  TabList.className = TABS_CLASSNAME;
@@ -18039,12 +18265,15 @@ const Tab$1 = props => {
18039
18265
  icon,
18040
18266
  iconProps = {},
18041
18267
  isAnyDisabled,
18268
+ isDisabled,
18042
18269
  id,
18043
18270
  isActive,
18044
18271
  label,
18045
18272
  handleFocus,
18046
18273
  handleKeyPress,
18047
18274
  tabIndex = -1,
18275
+ tabIndexProp = 'tabIndex',
18276
+ keyPressProp = 'onKeyPress',
18048
18277
  changeToTab,
18049
18278
  tabPanelId,
18050
18279
  shouldActivateOnFocus,
@@ -18083,10 +18312,10 @@ const Tab$1 = props => {
18083
18312
  'is-disabled': isAnyDisabled
18084
18313
  })),
18085
18314
  onClick: changeToCurrentTab,
18086
- onKeyPress: onKeyPress,
18315
+ [keyPressProp]: onKeyPress,
18087
18316
  onFocus: onFocus,
18088
18317
  role: "tab",
18089
- tabIndex: isActive ? 0 : tabIndex,
18318
+ [tabIndexProp]: isActive ? 0 : tabIndex,
18090
18319
  "aria-disabled": isAnyDisabled,
18091
18320
  "aria-selected": isActive,
18092
18321
  "aria-controls": tabPanelId,
@@ -18157,7 +18386,7 @@ const COMPONENT_NAME$3 = 'TabPanel';
18157
18386
  const CLASSNAME$4 = `lumx-tab-panel`;
18158
18387
  const {
18159
18388
  block: block$4
18160
- } = classNames.bem(CLASSNAME$4);
18389
+ } = bem(CLASSNAME$4);
18161
18390
 
18162
18391
  /**
18163
18392
  * Component default props.
@@ -18173,27 +18402,60 @@ const DEFAULT_PROPS$4 = {};
18173
18402
  * @param ref Component ref.
18174
18403
  * @return React element.
18175
18404
  */
18176
- const TabPanel = forwardRef((props, ref) => {
18405
+ const TabPanel$1 = props => {
18177
18406
  const {
18178
18407
  children,
18179
- id,
18180
18408
  className,
18181
- isActive: propIsActive,
18409
+ isActive,
18410
+ id,
18411
+ tabId,
18412
+ isLazy,
18413
+ tabIndexProp = 'tabIndex',
18414
+ ref,
18182
18415
  ...forwardedProps
18183
18416
  } = props;
18184
- const state = useTabProviderContext('tabPanel', id);
18185
- const isActive = propIsActive || state?.isActive;
18186
18417
  return /*#__PURE__*/jsx("div", {
18187
18418
  ref: ref,
18188
18419
  ...forwardedProps,
18189
- id: state?.tabPanelId,
18190
- className: classNames.join(className, block$4({
18420
+ id: id,
18421
+ className: classnames(className, block$4({
18191
18422
  'is-active': isActive
18192
18423
  })),
18193
18424
  role: "tabpanel",
18194
- tabIndex: isActive ? 0 : -1,
18195
- "aria-labelledby": state?.tabId,
18196
- children: (!state?.isLazy || isActive) && children
18425
+ [tabIndexProp]: isActive ? 0 : -1,
18426
+ "aria-labelledby": tabId,
18427
+ children: (!isLazy || isActive) && children
18428
+ });
18429
+ };
18430
+
18431
+ /**
18432
+ * Defines the props of the component.
18433
+ */
18434
+
18435
+ /**
18436
+ * TabPanel component.
18437
+ *
18438
+ * Implements WAI-ARIA `tabpanel` role {@see https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-1/tabs.html#rps_label}
18439
+ *
18440
+ * @param props Component props.
18441
+ * @param ref Component ref.
18442
+ * @return React element.
18443
+ */
18444
+ const TabPanel = forwardRef((props, ref) => {
18445
+ const {
18446
+ id,
18447
+ isActive: propIsActive,
18448
+ ...forwardedProps
18449
+ } = props;
18450
+ const state = useTabProviderContext('tabPanel', id);
18451
+ const isActive = propIsActive || state?.isActive;
18452
+ return TabPanel$1({
18453
+ ref,
18454
+ isActive,
18455
+ id: state?.tabPanelId,
18456
+ isLazy: state?.isLazy,
18457
+ tabId: state?.tabId,
18458
+ ...forwardedProps
18197
18459
  });
18198
18460
  });
18199
18461
  TabPanel.displayName = COMPONENT_NAME$3;