@lumx/react 4.19.1-alpha.1 → 4.19.1-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.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
@@ -20935,6 +20935,7 @@ const TabList$1 = props => {
20935
20935
  className,
20936
20936
  layout = DEFAULT_PROPS$7.layout,
20937
20937
  position = DEFAULT_PROPS$7.position,
20938
+ role = 'tablist',
20938
20939
  theme,
20939
20940
  ref,
20940
20941
  ...forwardedProps
@@ -20949,7 +20950,7 @@ const TabList$1 = props => {
20949
20950
  })),
20950
20951
  children: /*#__PURE__*/jsx("div", {
20951
20952
  className: element$4('links'),
20952
- role: "tablist",
20953
+ role: role,
20953
20954
  "aria-label": ariaLabel,
20954
20955
  children: children
20955
20956
  })
@@ -20961,6 +20962,8 @@ const TabList$1 = props => {
20961
20962
  *
20962
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}
20963
20964
  *
20965
+ * Renders a `role="navigation"` landmark instead when used outside a `TabProvider` (nav-link mode).
20966
+ *
20964
20967
  * @param props Component props.
20965
20968
  * @param ref Component ref.
20966
20969
  * @return React element.
@@ -20972,14 +20975,19 @@ const TabList = forwardRef((props, ref) => {
20972
20975
  ...forwardedProps
20973
20976
  } = props;
20974
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.
20975
20982
  useRovingTabIndexContainer({
20976
- containerRef: tabListRef,
20983
+ containerRef: role === 'navigation' ? null : tabListRef,
20977
20984
  itemSelector: '[role="tab"]'
20978
20985
  });
20979
20986
  return TabList$1({
20987
+ ...forwardedProps,
20980
20988
  theme,
20981
- ref: mergeRefs(ref, tabListRef),
20982
- ...forwardedProps
20989
+ role,
20990
+ ref: mergeRefs(ref, tabListRef)
20983
20991
  });
20984
20992
  });
20985
20993
  TabList.displayName = COMPONENT_NAME$6;
@@ -21005,9 +21013,10 @@ const {
21005
21013
  } = bem(CLASSNAME$6);
21006
21014
 
21007
21015
  /**
21008
- * Tab component.
21009
- *
21010
- * 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.
21011
21020
  *
21012
21021
  * @param props Component props.
21013
21022
  * @param ref Component ref.
@@ -21015,13 +21024,15 @@ const {
21015
21024
  */
21016
21025
  const Tab$1 = props => {
21017
21026
  const {
21027
+ as = 'button',
21018
21028
  className,
21019
21029
  icon,
21020
21030
  iconProps = {},
21021
21031
  isAnyDisabled,
21022
21032
  isDisabled,
21023
21033
  id,
21024
- isActive,
21034
+ isActive: isActiveProp,
21035
+ 'aria-current': ariaCurrent,
21025
21036
  label,
21026
21037
  handleFocus,
21027
21038
  handleKeyPress,
@@ -21037,56 +21048,80 @@ const Tab$1 = props => {
21037
21048
  ref,
21038
21049
  ...forwardedProps
21039
21050
  } = props;
21040
- const changeToCurrentTab = () => {
21041
- if (isAnyDisabled) {
21042
- return;
21043
- }
21044
- changeToTab?.();
21045
- };
21046
- const onFocus = event => {
21047
- handleFocus?.(event);
21048
- 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
+ }
21049
21072
  changeToCurrentTab();
21050
- }
21051
- };
21052
- const onKeyPress = event => {
21053
- handleKeyPress?.(event);
21054
- if (event.key !== 'Enter' || isAnyDisabled) {
21055
- return;
21056
- }
21057
- changeToCurrentTab();
21058
- };
21059
- return /*#__PURE__*/jsxs("button", {
21060
- ref: ref,
21061
- ...forwardedProps,
21062
- 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,
21063
21101
  id: tabId,
21064
21102
  className: classnames(className, block$6({
21065
21103
  'is-active': isActive,
21066
21104
  'is-disabled': isAnyDisabled
21067
21105
  })),
21068
- onClick: changeToCurrentTab,
21069
- [keyPressProp]: onKeyPress,
21070
- onFocus: onFocus,
21071
- role: "tab",
21072
- [tabIndexProp]: isActive ? 0 : tabIndex,
21073
- "aria-disabled": isAnyDisabled,
21074
- "aria-selected": isActive,
21075
- "aria-controls": tabPanelId,
21076
- children: [icon && /*#__PURE__*/jsx(Icon, {
21077
- icon: icon,
21078
- size: Size.xs,
21079
- ...iconProps
21080
- }), label && /*#__PURE__*/jsx(Text, {
21081
- as: "span",
21082
- truncate: true,
21083
- children: label
21084
- })]
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
+ })
21085
21118
  });
21086
21119
  };
21087
21120
 
21088
21121
  /**
21089
- * 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`.
21090
21125
  */
21091
21126
 
21092
21127
  /**
@@ -21094,44 +21129,54 @@ const Tab$1 = props => {
21094
21129
  *
21095
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}
21096
21131
  *
21132
+ * In nav-link mode (`as` set): plain link, no `role="tab"`/roving tabindex/`changeToTab` dispatch.
21133
+ *
21097
21134
  * @param props Component props.
21098
- * @param ref Component ref.
21135
+ * @param ref Component ref (type follows `as`).
21099
21136
  * @return React element.
21100
21137
  */
21101
- const Tab = forwardRef((props, ref) => {
21138
+ const Tab = Object.assign(forwardRefPolymorphic((props, ref) => {
21102
21139
  const {
21103
21140
  isAnyDisabled,
21104
21141
  otherProps
21105
21142
  } = useDisableStateProps(props);
21106
21143
  const {
21144
+ as = 'button',
21107
21145
  isActive: propIsActive,
21108
21146
  id,
21109
21147
  onFocus,
21110
21148
  onKeyPress,
21111
21149
  ...forwardedProps
21112
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.
21113
21154
  const tabState = useTabProviderContext('tab', id);
21114
21155
  const {
21115
21156
  isLazy: _isLazy,
21116
21157
  ...state
21117
21158
  } = tabState ?? {};
21118
- 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;
21119
21161
  return Tab$1({
21162
+ ...forwardedProps,
21120
21163
  id,
21121
21164
  ...state,
21165
+ as,
21122
21166
  Icon,
21123
21167
  Text,
21124
21168
  ref,
21125
21169
  isActive,
21126
21170
  isAnyDisabled,
21171
+ isDisabled: isAnyDisabled,
21127
21172
  handleFocus: onFocus,
21128
- handleKeyPress: onKeyPress,
21129
- ...forwardedProps
21173
+ handleKeyPress: onKeyPress
21130
21174
  });
21175
+ }), {
21176
+ displayName: COMPONENT_NAME$5,
21177
+ className: CLASSNAME$6,
21178
+ defaultProps: DEFAULT_PROPS$6
21131
21179
  });
21132
- Tab.displayName = COMPONENT_NAME$5;
21133
- Tab.className = CLASSNAME$6;
21134
- Tab.defaultProps = DEFAULT_PROPS$6;
21135
21180
 
21136
21181
  /**
21137
21182
  * Component display name.