@lumx/react 4.19.1-next.0 → 4.20.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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`).
5297
5303
  */
5298
- interface TabProps$1 extends HasClassName {
5304
+ type TabElement = 'a' | 'button' | ElementType;
5305
+ /**
5306
+ * Defines the props of the component (independent of the `as` element).
5307
+ */
5308
+ interface CommonTabProps extends HasClassName {
5299
5309
  /** Children are not supported. */
5300
5310
  children?: never;
5301
5311
  /** Icon (SVG path). */
@@ -5306,6 +5316,11 @@ interface TabProps$1 extends HasClassName {
5306
5316
  id?: string;
5307
5317
  /** Whether the tab is active or not. */
5308
5318
  isActive?: boolean;
5319
+ /**
5320
+ * WAI-ARIA `tab` role, resolved by the framework wrapper from `TabProvider` presence:
5321
+ * `'tab'` inside a provider (classic tab mode), `undefined` outside (nav-link mode).
5322
+ */
5323
+ role?: 'tab';
5309
5324
  /** Whether the component is disabled or not. */
5310
5325
  isDisabled?: boolean;
5311
5326
  /** Label content. */
@@ -5326,23 +5341,38 @@ interface TabProps$1 extends HasClassName {
5326
5341
  tabIndexProp?: string;
5327
5342
  /** Name of the prop used to attach the keypress event (framework-dependent). */
5328
5343
  keyPressProp?: string;
5329
- /** ID applied to the tab button element (for aria-labelledby on the panel). */
5330
- tabId?: string;
5331
5344
  /** ID of the associated tab panel (for aria-controls). */
5332
5345
  tabPanelId?: string;
5333
5346
  /** Icon component injected by the framework wrapper. */
5334
5347
  Icon: any;
5335
5348
  /** Text component injected by the framework wrapper. */
5336
5349
  Text: any;
5337
- /** Forward ref to the underlying button element. */
5350
+ /** Forward ref to the underlying element. */
5338
5351
  ref?: CommonRef;
5339
5352
  }
5340
- type TabPropsToOverride = 'isAnyDisabled' | 'shouldActivateOnFocus' | 'changeToTab' | 'tabIndex' | 'tabIndexProp' | 'keyPressProp' | 'tabId' | 'tabPanelId' | 'Icon' | 'Text';
5353
+ /**
5354
+ * Polymorphic `as` surface for `Tab`. Not `HasPolymorphicAs<E>`: core JSX is type-checked under
5355
+ * both the React and Vue namespaces, so spreading React DOM prop types would break the Vue
5356
+ * cross-check. `href` is required at compile time when `as === 'a'` (via {@link HasRequiredLinkHref}).
5357
+ */
5358
+ type TabAsProps<E extends TabElement = 'button'> = {
5359
+ as?: E;
5360
+ } & (E extends 'a' ? HasRequiredLinkHref<'a'> : unknown);
5361
+ /**
5362
+ * Props of the `Tab` component. Polymorphic on `as` (element selection only): `as` picks the
5363
+ * rendered element, while **mode is driven by `TabProvider` presence** (via the wrapper-resolved
5364
+ * `role`), not by `as`. The `href` conditional's false branch is `unknown` (not a record) to avoid
5365
+ * widening `keyof TabProps`.
5366
+ */
5367
+ type TabProps$1<E extends TabElement = 'button'> = CommonTabProps & TabAsProps<E>;
5368
+ type TabPropsToOverride = 'as' | 'role' | 'isAnyDisabled' | 'shouldActivateOnFocus' | 'changeToTab' | 'tabIndex' | 'tabIndexProp' | 'keyPressProp' | 'tabPanelId' | 'Icon' | 'Text';
5341
5369
 
5342
5370
  /**
5343
- * Defines the props of the component.
5371
+ * Defines the props of the component. Polymorphic on `as` (element selection only): `as` picks the
5372
+ * rendered element (`as="a"` with required `href`, or `as={RouterLink}`), while the mode
5373
+ * (`role="tab"` vs nav-link) is driven by `TabProvider` presence. The forwarded ref type follows `as`.
5344
5374
  */
5345
- interface TabProps extends GenericProps$1, ReactToJSX<TabProps$1, TabPropsToOverride> {
5375
+ type TabProps<E extends ElementType$1 = 'button'> = GenericProps$1 & HasPolymorphicAs$1<E> & HasRequiredLinkHref$1<E> & ReactToJSX<TabProps$1, TabPropsToOverride> & {
5346
5376
  /** Children are not supported. */
5347
5377
  children?: never;
5348
5378
  /** Icon (SVG path). */
@@ -5357,17 +5387,44 @@ interface TabProps extends GenericProps$1, ReactToJSX<TabProps$1, TabPropsToOver
5357
5387
  isDisabled?: boolean;
5358
5388
  /** Label content. */
5359
5389
  label: string | ReactNode;
5360
- }
5390
+ };
5361
5391
  /**
5362
5392
  * Tab component.
5363
5393
  *
5364
5394
  * Implements WAI-ARIA `tab` role {@see https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-1/tabs.html#rps_label}
5395
+ * when rendered inside a `TabProvider`.
5396
+ *
5397
+ * Outside a `TabProvider` (nav-link mode): plain link, no `role="tab"`/roving tabindex/`changeToTab`
5398
+ * dispatch — only `aria-current` marks the active item.
5365
5399
  *
5366
5400
  * @param props Component props.
5367
- * @param ref Component ref.
5401
+ * @param ref Component ref (type follows `as`).
5368
5402
  * @return React element.
5369
5403
  */
5370
- declare const Tab: Comp<TabProps, HTMLButtonElement>;
5404
+ declare const Tab: (<E extends ElementType$1 = "button">(props: GenericProps$1 & React$1.PropsWithoutRef<React$1.ComponentProps<E>> & {
5405
+ as?: E | undefined;
5406
+ } & HasRequiredLinkHref$1<E> & ReactToJSX<TabProps$1, TabPropsToOverride> & {
5407
+ /** Children are not supported. */
5408
+ children?: never;
5409
+ /** Icon (SVG path). */
5410
+ icon?: IconProps["icon"];
5411
+ /** Icon component properties. */
5412
+ iconProps?: Omit<IconProps, "icon">;
5413
+ /** Native id property. */
5414
+ id?: string;
5415
+ /** Whether the tab is active or not. */
5416
+ isActive?: boolean;
5417
+ /** Whether the component is disabled or not. */
5418
+ isDisabled?: boolean;
5419
+ /** Label content. */
5420
+ label: string | ReactNode;
5421
+ } & React$1.ComponentProps<E> & {
5422
+ ref?: ComponentRef<E> | undefined;
5423
+ }) => React.JSX.Element) & {
5424
+ displayName: string;
5425
+ className: string;
5426
+ defaultProps: Partial<TabProps$1<"button">>;
5427
+ };
5371
5428
 
5372
5429
  /**
5373
5430
  * Defines the props of the component.
package/index.js CHANGED
@@ -20935,10 +20935,12 @@ 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
20941
20942
  } = props;
20943
+ const LinkWrapper = role === 'navigation' ? 'nav' : 'div';
20942
20944
  return /*#__PURE__*/jsx("div", {
20943
20945
  ref: ref,
20944
20946
  ...forwardedProps,
@@ -20947,9 +20949,9 @@ const TabList$1 = props => {
20947
20949
  [`position-${position}`]: Boolean(position),
20948
20950
  [`theme-${theme}`]: Boolean(theme)
20949
20951
  })),
20950
- children: /*#__PURE__*/jsx("div", {
20952
+ children: /*#__PURE__*/jsx(LinkWrapper, {
20951
20953
  className: element$4('links'),
20952
- role: "tablist",
20954
+ role: role,
20953
20955
  "aria-label": ariaLabel,
20954
20956
  children: children
20955
20957
  })
@@ -20961,6 +20963,8 @@ const TabList$1 = props => {
20961
20963
  *
20962
20964
  * 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
20965
  *
20966
+ * Renders a `role="navigation"` landmark instead when used outside a `TabProvider` (nav-link mode).
20967
+ *
20964
20968
  * @param props Component props.
20965
20969
  * @param ref Component ref.
20966
20970
  * @return React element.
@@ -20972,14 +20976,19 @@ const TabList = forwardRef((props, ref) => {
20972
20976
  ...forwardedProps
20973
20977
  } = props;
20974
20978
  const tabListRef = React__default.useRef(null);
20979
+ const role = useTabProviderContextState() === undefined ? 'navigation' : 'tablist';
20980
+
20981
+ // Classic roving tabindex (role="tab"). Disabled in nav-link mode: nav links keep their
20982
+ // native per-link Tab stops, so this path is never engaged there.
20975
20983
  useRovingTabIndexContainer({
20976
- containerRef: tabListRef,
20984
+ containerRef: role === 'navigation' ? null : tabListRef,
20977
20985
  itemSelector: '[role="tab"]'
20978
20986
  });
20979
20987
  return TabList$1({
20988
+ ...forwardedProps,
20980
20989
  theme,
20981
- ref: mergeRefs(ref, tabListRef),
20982
- ...forwardedProps
20990
+ role,
20991
+ ref: mergeRefs(ref, tabListRef)
20983
20992
  });
20984
20993
  });
20985
20994
  TabList.displayName = COMPONENT_NAME$6;
@@ -21005,9 +21014,11 @@ const {
21005
21014
  } = bem(CLASSNAME$6);
21006
21015
 
21007
21016
  /**
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}
21017
+ * Tab component. Two modes keyed on `TabProvider` presence (via the wrapper-resolved `role`):
21018
+ * tab mode (`role === 'tab'`, inside a provider) implements the WAI-ARIA `tab` role
21019
+ * {@see https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-1/tabs.html#rps_label};
21020
+ * nav-link mode (no provider) renders plain link semantics (`aria-current`, no tab role/aria).
21021
+ * `as` only selects the rendered element and applies in both modes.
21011
21022
  *
21012
21023
  * @param props Component props.
21013
21024
  * @param ref Component ref.
@@ -21015,6 +21026,7 @@ const {
21015
21026
  */
21016
21027
  const Tab$1 = props => {
21017
21028
  const {
21029
+ as = 'button',
21018
21030
  className,
21019
21031
  icon,
21020
21032
  iconProps = {},
@@ -21022,6 +21034,7 @@ const Tab$1 = props => {
21022
21034
  isDisabled,
21023
21035
  id,
21024
21036
  isActive,
21037
+ role,
21025
21038
  label,
21026
21039
  handleFocus,
21027
21040
  handleKeyPress,
@@ -21031,107 +21044,145 @@ const Tab$1 = props => {
21031
21044
  changeToTab,
21032
21045
  tabPanelId,
21033
21046
  shouldActivateOnFocus,
21034
- tabId,
21035
21047
  Icon,
21036
21048
  Text,
21037
21049
  ref,
21038
21050
  ...forwardedProps
21039
21051
  } = props;
21040
- const changeToCurrentTab = () => {
21041
- if (isAnyDisabled) {
21042
- return;
21043
- }
21044
- changeToTab?.();
21045
- };
21046
- const onFocus = event => {
21047
- handleFocus?.(event);
21048
- if (shouldActivateOnFocus) {
21052
+ let rawClickableProps;
21053
+ if (role === 'tab') {
21054
+ // Mode: WAI-ARIA `tab` role (inside a TabProvider)
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",
21063
- id: tabId,
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,
21101
+ id,
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` (element selection only): `as` picks the
21123
+ * rendered element (`as="a"` with required `href`, or `as={RouterLink}`), while the mode
21124
+ * (`role="tab"` vs nav-link) is driven by `TabProvider` presence. The forwarded ref type follows `as`.
21090
21125
  */
21091
21126
 
21092
21127
  /**
21093
21128
  * Tab component.
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}
21131
+ * when rendered inside a `TabProvider`.
21132
+ *
21133
+ * Outside a `TabProvider` (nav-link mode): plain link, no `role="tab"`/roving tabindex/`changeToTab`
21134
+ * dispatch — only `aria-current` marks the active item.
21096
21135
  *
21097
21136
  * @param props Component props.
21098
- * @param ref Component ref.
21137
+ * @param ref Component ref (type follows `as`).
21099
21138
  * @return React element.
21100
21139
  */
21101
- const Tab = forwardRef((props, ref) => {
21140
+ const Tab = Object.assign(forwardRefPolymorphic((props, ref) => {
21102
21141
  const {
21103
21142
  isAnyDisabled,
21104
21143
  otherProps
21105
21144
  } = useDisableStateProps(props);
21106
21145
  const {
21146
+ as = 'button',
21107
21147
  isActive: propIsActive,
21108
21148
  id,
21109
21149
  onFocus,
21110
21150
  onKeyPress,
21111
21151
  ...forwardedProps
21112
21152
  } = otherProps;
21153
+
21154
+ // Register into the provider (if any) so it can track this tab's index / active state.
21113
21155
  const tabState = useTabProviderContext('tab', id);
21156
+ // In a provider => tab mode (role="tab"); outside => nav-link mode. Mirrors TabList's role.
21157
+ const inProvider = tabState !== undefined;
21114
21158
  const {
21115
21159
  isLazy: _isLazy,
21160
+ tabId,
21116
21161
  ...state
21117
21162
  } = tabState ?? {};
21163
+ // Without a provider there's no active index, so this collapses to the caller-provided value.
21118
21164
  const isActive = propIsActive || state?.isActive;
21119
21165
  return Tab$1({
21120
- id,
21166
+ ...forwardedProps,
21121
21167
  ...state,
21168
+ as,
21169
+ role: inProvider ? 'tab' : undefined,
21122
21170
  Icon,
21123
21171
  Text,
21124
21172
  ref,
21173
+ // Falls back to the caller-provided `id` when there's no TabProvider to generate one.
21174
+ id: tabId || id,
21125
21175
  isActive,
21126
21176
  isAnyDisabled,
21177
+ isDisabled: isAnyDisabled,
21127
21178
  handleFocus: onFocus,
21128
- handleKeyPress: onKeyPress,
21129
- ...forwardedProps
21179
+ handleKeyPress: onKeyPress
21130
21180
  });
21181
+ }), {
21182
+ displayName: COMPONENT_NAME$5,
21183
+ className: CLASSNAME$6,
21184
+ defaultProps: DEFAULT_PROPS$6
21131
21185
  });
21132
- Tab.displayName = COMPONENT_NAME$5;
21133
- Tab.className = CLASSNAME$6;
21134
- Tab.defaultProps = DEFAULT_PROPS$6;
21135
21186
 
21136
21187
  /**
21137
21188
  * Component display name.