@lumx/react 4.19.1-alpha.0 → 4.19.1-alpha.2

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.d.ts CHANGED
@@ -5271,14 +5271,17 @@ interface TabListProps$1 extends HasClassName, HasTheme {
5271
5271
  layout?: TabListLayout;
5272
5272
  /** Position of the tabs in the list (requires 'clustered' layout). */
5273
5273
  position?: Alignment;
5274
+ /** `role` applied to the inner `__links` container */
5275
+ role?: 'navigation' | 'tablist';
5274
5276
  /** ref to the wrapper element */
5275
5277
  ref?: CommonRef;
5276
5278
  }
5279
+ type TabListPropsToOverride = 'role';
5277
5280
 
5278
5281
  /**
5279
5282
  * Defines the props of the component.
5280
5283
  */
5281
- interface TabListProps extends GenericProps$1, ReactToJSX<TabListProps$1> {
5284
+ interface TabListProps extends GenericProps$1, ReactToJSX<TabListProps$1, TabListPropsToOverride> {
5282
5285
  }
5283
5286
 
5284
5287
  /**
@@ -5286,6 +5289,8 @@ interface TabListProps extends GenericProps$1, ReactToJSX<TabListProps$1> {
5286
5289
  *
5287
5290
  * Implements WAI-ARIA `tablist` role {@see https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-1/tabs.html#rps_label}
5288
5291
  *
5292
+ * Renders a `role="navigation"` landmark instead when used outside a `TabProvider` (nav-link mode).
5293
+ *
5289
5294
  * @param props Component props.
5290
5295
  * @param ref Component ref.
5291
5296
  * @return React element.
@@ -5293,9 +5298,14 @@ interface TabListProps extends GenericProps$1, ReactToJSX<TabListProps$1> {
5293
5298
  declare const TabList: Comp<TabListProps, HTMLDivElement>;
5294
5299
 
5295
5300
  /**
5296
- * Defines the props of the component.
5301
+ * Element a `Tab` can render as: `'button'` (default, classic tab), `'a'` (nav-link, `href`
5302
+ * required via {@link HasRequiredLinkHref}), or any `ElementType` (nav-link, e.g. a router `Link`).
5303
+ */
5304
+ type TabElement = 'a' | 'button' | ElementType;
5305
+ /**
5306
+ * Defines the props of the component (independent of the `as` element).
5297
5307
  */
5298
- interface TabProps$1 extends HasClassName {
5308
+ interface CommonTabProps extends HasClassName {
5299
5309
  /** Children are not supported. */
5300
5310
  children?: never;
5301
5311
  /** Icon (SVG path). */
@@ -5306,6 +5316,8 @@ interface TabProps$1 extends HasClassName {
5306
5316
  id?: string;
5307
5317
  /** Whether the tab is active or not. */
5308
5318
  isActive?: boolean;
5319
+ /** Synonym for `isActive` — `'page'` marks the tab as active (WAI-ARIA nav-link convention). */
5320
+ 'aria-current'?: 'page' | boolean;
5309
5321
  /** Whether the component is disabled or not. */
5310
5322
  isDisabled?: boolean;
5311
5323
  /** Label content. */
@@ -5334,15 +5346,31 @@ interface TabProps$1 extends HasClassName {
5334
5346
  Icon: any;
5335
5347
  /** Text component injected by the framework wrapper. */
5336
5348
  Text: any;
5337
- /** Forward ref to the underlying button element. */
5349
+ /** Forward ref to the underlying element. */
5338
5350
  ref?: CommonRef;
5339
5351
  }
5340
- type TabPropsToOverride = 'isAnyDisabled' | 'shouldActivateOnFocus' | 'changeToTab' | 'tabIndex' | 'tabIndexProp' | 'keyPressProp' | 'tabId' | 'tabPanelId' | 'Icon' | 'Text';
5352
+ /**
5353
+ * Polymorphic `as` surface for `Tab`. Not `HasPolymorphicAs<E>`: core JSX is type-checked under
5354
+ * both the React and Vue namespaces, so spreading React DOM prop types would break the Vue
5355
+ * cross-check. `href` is required at compile time when `as === 'a'` (via {@link HasRequiredLinkHref}).
5356
+ */
5357
+ type TabAsProps<E extends TabElement = 'button'> = {
5358
+ as?: E;
5359
+ } & (E extends 'a' ? HasRequiredLinkHref<'a'> : unknown);
5360
+ /**
5361
+ * Props of the `Tab` component. Polymorphic on `as`: setting it enters **nav-link mode**,
5362
+ * rendering that element instead of a `<button role="tab">`. The `href` conditional's false
5363
+ * branch is `unknown` (not a record) to avoid widening `keyof TabProps`.
5364
+ */
5365
+ type TabProps$1<E extends TabElement = 'button'> = CommonTabProps & TabAsProps<E>;
5366
+ type TabPropsToOverride = 'as' | 'isAnyDisabled' | 'shouldActivateOnFocus' | 'changeToTab' | 'tabIndex' | 'tabIndexProp' | 'keyPressProp' | 'tabId' | 'tabPanelId' | 'Icon' | 'Text';
5341
5367
 
5342
5368
  /**
5343
- * Defines the props of the component.
5369
+ * Defines the props of the component. Polymorphic on `as`: unset it's a classic
5370
+ * `<button role="tab">`; set it (`as="a"` with required `href`, or `as={RouterLink}`) for
5371
+ * nav-link mode. The forwarded ref type follows `as`.
5344
5372
  */
5345
- interface TabProps extends GenericProps$1, ReactToJSX<TabProps$1, TabPropsToOverride> {
5373
+ type TabProps<E extends ElementType$1 = 'button'> = GenericProps$1 & HasPolymorphicAs$1<E> & HasRequiredLinkHref$1<E> & ReactToJSX<TabProps$1, TabPropsToOverride> & {
5346
5374
  /** Children are not supported. */
5347
5375
  children?: never;
5348
5376
  /** Icon (SVG path). */
@@ -5357,17 +5385,42 @@ interface TabProps extends GenericProps$1, ReactToJSX<TabProps$1, TabPropsToOver
5357
5385
  isDisabled?: boolean;
5358
5386
  /** Label content. */
5359
5387
  label: string | ReactNode;
5360
- }
5388
+ };
5361
5389
  /**
5362
5390
  * Tab component.
5363
5391
  *
5364
5392
  * Implements WAI-ARIA `tab` role {@see https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-1/tabs.html#rps_label}
5365
5393
  *
5394
+ * In nav-link mode (`as` set): plain link, no `role="tab"`/roving tabindex/`changeToTab` dispatch.
5395
+ *
5366
5396
  * @param props Component props.
5367
- * @param ref Component ref.
5397
+ * @param ref Component ref (type follows `as`).
5368
5398
  * @return React element.
5369
5399
  */
5370
- declare const Tab: Comp<TabProps, HTMLButtonElement>;
5400
+ declare const Tab: (<E extends ElementType$1 = "button">(props: GenericProps$1 & React$1.PropsWithoutRef<React$1.ComponentProps<E>> & {
5401
+ as?: E | undefined;
5402
+ } & HasRequiredLinkHref$1<E> & ReactToJSX<TabProps$1, TabPropsToOverride> & {
5403
+ /** Children are not supported. */
5404
+ children?: never;
5405
+ /** Icon (SVG path). */
5406
+ icon?: IconProps["icon"];
5407
+ /** Icon component properties. */
5408
+ iconProps?: Omit<IconProps, "icon">;
5409
+ /** Native id property. */
5410
+ id?: string;
5411
+ /** Whether the tab is active or not. */
5412
+ isActive?: boolean;
5413
+ /** Whether the component is disabled or not. */
5414
+ isDisabled?: boolean;
5415
+ /** Label content. */
5416
+ label: string | ReactNode;
5417
+ } & React$1.ComponentProps<E> & {
5418
+ ref?: ComponentRef<E> | undefined;
5419
+ }) => React.JSX.Element) & {
5420
+ displayName: string;
5421
+ className: string;
5422
+ defaultProps: Partial<TabProps$1<"button">>;
5423
+ };
5371
5424
 
5372
5425
  /**
5373
5426
  * Defines the props of the component.
package/index.js CHANGED
@@ -6421,15 +6421,21 @@ function setupListbox(handle, signal, notify, options) {
6421
6421
  */
6422
6422
  function debounceMicrotask(fn) {
6423
6423
  let running = false;
6424
- return () => {
6424
+ const debounced = () => {
6425
6425
  if (!running) {
6426
6426
  running = true;
6427
6427
  queueMicrotask(() => {
6428
- fn();
6429
- running = false;
6428
+ if (running) {
6429
+ running = false;
6430
+ fn();
6431
+ }
6430
6432
  });
6431
6433
  }
6432
6434
  };
6435
+ debounced.cancel = () => {
6436
+ running = false;
6437
+ };
6438
+ return debounced;
6433
6439
  }
6434
6440
 
6435
6441
  /** Options for configuring the shared combobox behavior. */
@@ -6936,6 +6942,7 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
6936
6942
  optionRegistrations.clear();
6937
6943
  sectionRegistrations.clear();
6938
6944
  skeletonCount = 0;
6945
+ notifyVisibilityChange.cancel();
6939
6946
  clearTimeout(loadingTimer);
6940
6947
  announcementSent = false;
6941
6948
  // Clear all subscribers
@@ -20928,6 +20935,7 @@ const TabList$1 = props => {
20928
20935
  className,
20929
20936
  layout = DEFAULT_PROPS$7.layout,
20930
20937
  position = DEFAULT_PROPS$7.position,
20938
+ role = 'tablist',
20931
20939
  theme,
20932
20940
  ref,
20933
20941
  ...forwardedProps
@@ -20942,7 +20950,7 @@ const TabList$1 = props => {
20942
20950
  })),
20943
20951
  children: /*#__PURE__*/jsx("div", {
20944
20952
  className: element$4('links'),
20945
- role: "tablist",
20953
+ role: role,
20946
20954
  "aria-label": ariaLabel,
20947
20955
  children: children
20948
20956
  })
@@ -20954,6 +20962,8 @@ const TabList$1 = props => {
20954
20962
  *
20955
20963
  * Implements WAI-ARIA `tablist` role {@see https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-1/tabs.html#rps_label}
20956
20964
  *
20965
+ * Renders a `role="navigation"` landmark instead when used outside a `TabProvider` (nav-link mode).
20966
+ *
20957
20967
  * @param props Component props.
20958
20968
  * @param ref Component ref.
20959
20969
  * @return React element.
@@ -20965,14 +20975,19 @@ const TabList = forwardRef((props, ref) => {
20965
20975
  ...forwardedProps
20966
20976
  } = props;
20967
20977
  const tabListRef = React__default.useRef(null);
20978
+ const role = useTabProviderContextState() === undefined ? 'navigation' : 'tablist';
20979
+
20980
+ // Classic roving tabindex (role="tab"). Disabled in nav-link mode: nav links keep their
20981
+ // native per-link Tab stops, so this path is never engaged there.
20968
20982
  useRovingTabIndexContainer({
20969
- containerRef: tabListRef,
20983
+ containerRef: role === 'navigation' ? null : tabListRef,
20970
20984
  itemSelector: '[role="tab"]'
20971
20985
  });
20972
20986
  return TabList$1({
20987
+ ...forwardedProps,
20973
20988
  theme,
20974
- ref: mergeRefs(ref, tabListRef),
20975
- ...forwardedProps
20989
+ role,
20990
+ ref: mergeRefs(ref, tabListRef)
20976
20991
  });
20977
20992
  });
20978
20993
  TabList.displayName = COMPONENT_NAME$6;
@@ -20998,9 +21013,10 @@ const {
20998
21013
  } = bem(CLASSNAME$6);
20999
21014
 
21000
21015
  /**
21001
- * Tab component.
21002
- *
21003
- * Implements WAI-ARIA `tab` role {@see https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-1/tabs.html#rps_label}
21016
+ * Tab component. Two modes keyed on `as`: classic (no `as`) implements the WAI-ARIA `tab` role
21017
+ * {@see https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-1/tabs.html#rps_label};
21018
+ * nav-link (`as` set) renders plain link semantics (`aria-current`, no tab role/aria), decoupled
21019
+ * from the TabProvider.
21004
21020
  *
21005
21021
  * @param props Component props.
21006
21022
  * @param ref Component ref.
@@ -21008,13 +21024,15 @@ const {
21008
21024
  */
21009
21025
  const Tab$1 = props => {
21010
21026
  const {
21027
+ as = 'button',
21011
21028
  className,
21012
21029
  icon,
21013
21030
  iconProps = {},
21014
21031
  isAnyDisabled,
21015
21032
  isDisabled,
21016
21033
  id,
21017
- isActive,
21034
+ isActive: isActiveProp,
21035
+ 'aria-current': ariaCurrent,
21018
21036
  label,
21019
21037
  handleFocus,
21020
21038
  handleKeyPress,
@@ -21030,56 +21048,80 @@ const Tab$1 = props => {
21030
21048
  ref,
21031
21049
  ...forwardedProps
21032
21050
  } = props;
21033
- const changeToCurrentTab = () => {
21034
- if (isAnyDisabled) {
21035
- return;
21036
- }
21037
- changeToTab?.();
21038
- };
21039
- const onFocus = event => {
21040
- handleFocus?.(event);
21041
- if (shouldActivateOnFocus) {
21051
+ const isActive = isActiveProp ?? ariaCurrent === 'page';
21052
+ let rawClickableProps;
21053
+ if (as === 'button') {
21054
+ // Mode: WAI-ARIA `tab` role
21055
+ const changeToCurrentTab = () => {
21056
+ if (isAnyDisabled) {
21057
+ return;
21058
+ }
21059
+ changeToTab?.();
21060
+ };
21061
+ const onFocus = event => {
21062
+ handleFocus?.(event);
21063
+ if (shouldActivateOnFocus) {
21064
+ changeToCurrentTab();
21065
+ }
21066
+ };
21067
+ const onKeyPress = event => {
21068
+ handleKeyPress?.(event);
21069
+ if (event.key !== 'Enter' || isAnyDisabled) {
21070
+ return;
21071
+ }
21042
21072
  changeToCurrentTab();
21043
- }
21044
- };
21045
- const onKeyPress = event => {
21046
- handleKeyPress?.(event);
21047
- if (event.key !== 'Enter' || isAnyDisabled) {
21048
- return;
21049
- }
21050
- changeToCurrentTab();
21051
- };
21052
- return /*#__PURE__*/jsxs("button", {
21053
- ref: ref,
21054
- ...forwardedProps,
21055
- type: "button",
21073
+ };
21074
+ rawClickableProps = {
21075
+ ...forwardedProps,
21076
+ 'aria-disabled': isAnyDisabled ? 'true' : 'false',
21077
+ handleClick: changeToCurrentTab,
21078
+ onFocus,
21079
+ role: 'tab',
21080
+ 'aria-selected': isActive,
21081
+ 'aria-controls': tabPanelId,
21082
+ [keyPressProp]: onKeyPress,
21083
+ [tabIndexProp]: isActive ? 0 : tabIndex
21084
+ };
21085
+ } else {
21086
+ // Mode: nav link
21087
+ const {
21088
+ onClick,
21089
+ ...rest
21090
+ } = forwardedProps;
21091
+ rawClickableProps = {
21092
+ ...rest,
21093
+ isDisabled,
21094
+ handleClick: onClick,
21095
+ 'aria-current': isActive ? 'page' : undefined
21096
+ };
21097
+ }
21098
+ return RawClickable({
21099
+ ...rawClickableProps,
21100
+ as,
21056
21101
  id: tabId,
21057
21102
  className: classnames(className, block$6({
21058
21103
  'is-active': isActive,
21059
21104
  'is-disabled': isAnyDisabled
21060
21105
  })),
21061
- onClick: changeToCurrentTab,
21062
- [keyPressProp]: onKeyPress,
21063
- onFocus: onFocus,
21064
- role: "tab",
21065
- [tabIndexProp]: isActive ? 0 : tabIndex,
21066
- "aria-disabled": isAnyDisabled,
21067
- "aria-selected": isActive,
21068
- "aria-controls": tabPanelId,
21069
- children: [icon && /*#__PURE__*/jsx(Icon, {
21070
- icon: icon,
21071
- size: Size.xs,
21072
- ...iconProps
21073
- }), label && /*#__PURE__*/jsx(Text, {
21074
- as: "span",
21075
- truncate: true,
21076
- children: label
21077
- })]
21106
+ ref: ref,
21107
+ children: /*#__PURE__*/jsxs(Fragment, {
21108
+ children: [icon && /*#__PURE__*/jsx(Icon, {
21109
+ icon: icon,
21110
+ size: Size.xs,
21111
+ ...iconProps
21112
+ }), label && /*#__PURE__*/jsx(Text, {
21113
+ as: "span",
21114
+ truncate: true,
21115
+ children: label
21116
+ })]
21117
+ })
21078
21118
  });
21079
21119
  };
21080
21120
 
21081
21121
  /**
21082
- * Defines the props of the component.
21122
+ * Defines the props of the component. Polymorphic on `as`: unset it's a classic
21123
+ * `<button role="tab">`; set it (`as="a"` with required `href`, or `as={RouterLink}`) for
21124
+ * nav-link mode. The forwarded ref type follows `as`.
21083
21125
  */
21084
21126
 
21085
21127
  /**
@@ -21087,44 +21129,54 @@ const Tab$1 = props => {
21087
21129
  *
21088
21130
  * Implements WAI-ARIA `tab` role {@see https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-1/tabs.html#rps_label}
21089
21131
  *
21132
+ * In nav-link mode (`as` set): plain link, no `role="tab"`/roving tabindex/`changeToTab` dispatch.
21133
+ *
21090
21134
  * @param props Component props.
21091
- * @param ref Component ref.
21135
+ * @param ref Component ref (type follows `as`).
21092
21136
  * @return React element.
21093
21137
  */
21094
- const Tab = forwardRef((props, ref) => {
21138
+ const Tab = Object.assign(forwardRefPolymorphic((props, ref) => {
21095
21139
  const {
21096
21140
  isAnyDisabled,
21097
21141
  otherProps
21098
21142
  } = useDisableStateProps(props);
21099
21143
  const {
21144
+ as = 'button',
21100
21145
  isActive: propIsActive,
21101
21146
  id,
21102
21147
  onFocus,
21103
21148
  onKeyPress,
21104
21149
  ...forwardedProps
21105
21150
  } = otherProps;
21151
+ const isNavLink = as != 'button';
21152
+
21153
+ // Register into the provider (if any) so it can track this tab's index / active state.
21106
21154
  const tabState = useTabProviderContext('tab', id);
21107
21155
  const {
21108
21156
  isLazy: _isLazy,
21109
21157
  ...state
21110
21158
  } = tabState ?? {};
21111
- const isActive = propIsActive || state?.isActive;
21159
+ // Nav-link tabs ignore the provider's active index — active state is caller-controlled only.
21160
+ const isActive = isNavLink ? propIsActive : propIsActive || state?.isActive;
21112
21161
  return Tab$1({
21162
+ ...forwardedProps,
21113
21163
  id,
21114
21164
  ...state,
21165
+ as,
21115
21166
  Icon,
21116
21167
  Text,
21117
21168
  ref,
21118
21169
  isActive,
21119
21170
  isAnyDisabled,
21171
+ isDisabled: isAnyDisabled,
21120
21172
  handleFocus: onFocus,
21121
- handleKeyPress: onKeyPress,
21122
- ...forwardedProps
21173
+ handleKeyPress: onKeyPress
21123
21174
  });
21175
+ }), {
21176
+ displayName: COMPONENT_NAME$5,
21177
+ className: CLASSNAME$6,
21178
+ defaultProps: DEFAULT_PROPS$6
21124
21179
  });
21125
- Tab.displayName = COMPONENT_NAME$5;
21126
- Tab.className = CLASSNAME$6;
21127
- Tab.defaultProps = DEFAULT_PROPS$6;
21128
21180
 
21129
21181
  /**
21130
21182
  * Component display name.