Migrate Tabs component from useStyles to useDefinition
Split monolithic TabsDefinition into 5 per-sub-component definitions (Tabs, TabList, Tab, TabPanel, TabsIndicators). Each sub-component now uses useDefinition with its own OwnProps type. Removed clsx and direct CSS module imports from component files. Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
+45
-13
@@ -2355,40 +2355,72 @@ export interface TableSelection {
|
||||
export const TabList: (props: TabListProps) => JSX_2.Element;
|
||||
|
||||
// @public
|
||||
export interface TabListProps extends Omit<TabListProps_2<object>, 'items'> {}
|
||||
export type TabListOwnProps = {
|
||||
className?: string;
|
||||
children?: TabListProps_2<object>['children'];
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface TabListProps
|
||||
extends TabListOwnProps,
|
||||
Omit<TabListProps_2<object>, 'items' | keyof TabListOwnProps> {}
|
||||
|
||||
// @public
|
||||
export type TabMatchStrategy = 'exact' | 'prefix';
|
||||
|
||||
// @public
|
||||
export type TabOwnProps = {
|
||||
className?: string;
|
||||
matchStrategy?: TabMatchStrategy;
|
||||
href?: TabProps_2['href'];
|
||||
id?: TabProps_2['id'];
|
||||
};
|
||||
|
||||
// @public
|
||||
export const TabPanel: (props: TabPanelProps) => JSX_2.Element;
|
||||
|
||||
// @public
|
||||
export interface TabPanelProps extends TabPanelProps_2 {}
|
||||
export type TabPanelOwnProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface TabProps extends TabProps_2 {
|
||||
matchStrategy?: 'exact' | 'prefix';
|
||||
}
|
||||
export interface TabPanelProps
|
||||
extends TabPanelOwnProps,
|
||||
Omit<TabPanelProps_2, keyof TabPanelOwnProps> {}
|
||||
|
||||
// @public
|
||||
export interface TabProps
|
||||
extends TabOwnProps,
|
||||
Omit<TabProps_2, keyof TabOwnProps> {}
|
||||
|
||||
// @public
|
||||
export const Tabs: (props: TabsProps) => JSX_2.Element | null;
|
||||
|
||||
// @public
|
||||
export const TabsDefinition: {
|
||||
readonly styles: {
|
||||
readonly [key: string]: string;
|
||||
};
|
||||
readonly classNames: {
|
||||
readonly tabs: 'bui-Tabs';
|
||||
readonly tabList: 'bui-TabList';
|
||||
readonly tabListWrapper: 'bui-TabListWrapper';
|
||||
readonly tab: 'bui-Tab';
|
||||
readonly tabActive: 'bui-TabActive';
|
||||
readonly tabHovered: 'bui-TabHovered';
|
||||
readonly panel: 'bui-TabPanel';
|
||||
readonly root: 'bui-Tabs';
|
||||
};
|
||||
readonly propDefs: {
|
||||
readonly className: {};
|
||||
readonly children: {};
|
||||
};
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface TabsProps extends TabsProps_2 {}
|
||||
export type TabsOwnProps = {
|
||||
className?: string;
|
||||
children?: TabsProps_2['children'];
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface TabsProps
|
||||
extends TabsOwnProps,
|
||||
Omit<TabsProps_2, keyof TabsOwnProps> {}
|
||||
|
||||
// @public
|
||||
export const Tag: ForwardRefExoticComponent<
|
||||
|
||||
@@ -43,14 +43,17 @@ import {
|
||||
TabPanel as AriaTabPanel,
|
||||
TabProps as AriaTabProps,
|
||||
} from 'react-aria-components';
|
||||
import { useStyles } from '../../hooks/useStyles';
|
||||
import { TabsDefinition } from './definition';
|
||||
import { useDefinition } from '../../hooks/useDefinition';
|
||||
import {
|
||||
TabsDefinition,
|
||||
TabListDefinition,
|
||||
TabDefinition,
|
||||
TabPanelDefinition,
|
||||
} from './definition';
|
||||
import {
|
||||
isInternalLink,
|
||||
createRoutingRegistration,
|
||||
} from '../InternalLinkProvider';
|
||||
import styles from './Tabs.module.css';
|
||||
import clsx from 'clsx';
|
||||
|
||||
const { RoutingProvider, useRoutingRegistrationEffect } =
|
||||
createRoutingRegistration();
|
||||
@@ -105,8 +108,8 @@ const isTabActive = (
|
||||
* @public
|
||||
*/
|
||||
export const Tabs = (props: TabsProps) => {
|
||||
const { classNames, cleanedProps } = useStyles(TabsDefinition, props);
|
||||
const { className, children, ...rest } = cleanedProps;
|
||||
const { ownProps, restProps } = useDefinition(TabsDefinition, props);
|
||||
const { classes, children } = ownProps;
|
||||
const tabsRef = useRef<HTMLDivElement>(null);
|
||||
const tabRefs = useRef<Map<string, HTMLDivElement>>(new Map());
|
||||
const [hoveredKey, setHoveredKey] = useState<string | null>(null);
|
||||
@@ -210,15 +213,11 @@ export const Tabs = (props: TabsProps) => {
|
||||
<TabsContext.Provider value={tabsContextValue}>
|
||||
<TabSelectionContext.Provider value={selectionContextValue}>
|
||||
<AriaTabs
|
||||
className={clsx(
|
||||
classNames.tabs,
|
||||
styles[classNames.tabs],
|
||||
className,
|
||||
)}
|
||||
className={classes.root}
|
||||
keyboardActivation="manual"
|
||||
selectedKey={selectedTabId}
|
||||
ref={tabsRef}
|
||||
{...rest}
|
||||
{...restProps}
|
||||
>
|
||||
{children as ReactNode}
|
||||
</AriaTabs>
|
||||
@@ -234,8 +233,8 @@ export const Tabs = (props: TabsProps) => {
|
||||
* @public
|
||||
*/
|
||||
export const TabList = (props: TabListProps) => {
|
||||
const { classNames, cleanedProps } = useStyles(TabsDefinition, props);
|
||||
const { className, children, ...rest } = cleanedProps;
|
||||
const { ownProps, restProps } = useDefinition(TabListDefinition, props);
|
||||
const { classes, children } = ownProps;
|
||||
const { setHoveredKey, tabRefs, tabsRef, hoveredKey, prevHoveredKey } =
|
||||
useTabsContext();
|
||||
|
||||
@@ -255,17 +254,11 @@ export const TabList = (props: TabListProps) => {
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
classNames.tabListWrapper,
|
||||
styles[classNames.tabListWrapper],
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className={classes.root}>
|
||||
<AriaTabList
|
||||
className={clsx(classNames.tabList, styles[classNames.tabList])}
|
||||
className={classes.tabList}
|
||||
aria-label="Toolbar tabs"
|
||||
{...rest}
|
||||
{...restProps}
|
||||
>
|
||||
{enhancedChildren}
|
||||
</AriaTabList>
|
||||
@@ -330,9 +323,8 @@ function RoutedTabEffects({
|
||||
* @public
|
||||
*/
|
||||
export const Tab = (props: TabProps) => {
|
||||
const { classNames, cleanedProps } = useStyles(TabsDefinition, props);
|
||||
const { className, href, children, id, matchStrategy, ...rest } =
|
||||
cleanedProps;
|
||||
const { ownProps, restProps } = useDefinition(TabDefinition, props);
|
||||
const { classes, matchStrategy, href, id } = ownProps;
|
||||
const { setTabRef } = useTabsContext();
|
||||
|
||||
return (
|
||||
@@ -346,13 +338,11 @@ export const Tab = (props: TabProps) => {
|
||||
)}
|
||||
<AriaTab
|
||||
id={id}
|
||||
className={clsx(classNames.tab, styles[classNames.tab], className)}
|
||||
className={classes.root}
|
||||
ref={el => setTabRef(id as string, el as HTMLDivElement)}
|
||||
href={href}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</AriaTab>
|
||||
{...restProps}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -363,15 +353,7 @@ export const Tab = (props: TabProps) => {
|
||||
* @public
|
||||
*/
|
||||
export const TabPanel = (props: TabPanelProps) => {
|
||||
const { classNames, cleanedProps } = useStyles(TabsDefinition, props);
|
||||
const { className, children, ...rest } = cleanedProps;
|
||||
const { ownProps, restProps } = useDefinition(TabPanelDefinition, props);
|
||||
|
||||
return (
|
||||
<AriaTabPanel
|
||||
className={clsx(classNames.panel, styles[classNames.panel], className)}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</AriaTabPanel>
|
||||
);
|
||||
return <AriaTabPanel className={ownProps.classes.root} {...restProps} />;
|
||||
};
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
*/
|
||||
|
||||
import { TabListStateContext } from 'react-aria-components';
|
||||
import { useStyles } from '../../hooks/useStyles';
|
||||
import { TabsDefinition } from './definition';
|
||||
import { useDefinition } from '../../hooks/useDefinition';
|
||||
import { TabsIndicatorsDefinition } from './definition';
|
||||
import { useContext, useEffect, useCallback, useRef } from 'react';
|
||||
import type { TabsIndicatorsProps } from './types';
|
||||
import styles from './Tabs.module.css';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/**
|
||||
* A component that renders the indicators for the toolbar.
|
||||
@@ -28,8 +26,8 @@ import clsx from 'clsx';
|
||||
* @internal
|
||||
*/
|
||||
export const TabsIndicators = (props: TabsIndicatorsProps) => {
|
||||
const { tabRefs, tabsRef, hoveredKey, prevHoveredKey } = props;
|
||||
const { classNames } = useStyles(TabsDefinition);
|
||||
const { ownProps } = useDefinition(TabsIndicatorsDefinition, props);
|
||||
const { classes, tabRefs, tabsRef, hoveredKey, prevHoveredKey } = ownProps;
|
||||
const state = useContext(TabListStateContext);
|
||||
const prevSelectedKey = useRef<string | null>(null);
|
||||
|
||||
@@ -187,12 +185,8 @@ export const TabsIndicators = (props: TabsIndicatorsProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={clsx(classNames.tabActive, styles[classNames.tabActive])}
|
||||
/>
|
||||
<div
|
||||
className={clsx(classNames.tabHovered, styles[classNames.tabHovered])}
|
||||
/>
|
||||
<div className={classes.root} />
|
||||
<div className={classes.hovered} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,20 +14,81 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ComponentDefinition } from '../../types';
|
||||
import { defineComponent } from '../../hooks/useDefinition';
|
||||
import type {
|
||||
TabsOwnProps,
|
||||
TabListOwnProps,
|
||||
TabOwnProps,
|
||||
TabPanelOwnProps,
|
||||
TabsIndicatorsOwnProps,
|
||||
} from './types';
|
||||
import styles from './Tabs.module.css';
|
||||
|
||||
/**
|
||||
* Component definition for Tabs
|
||||
* @public
|
||||
*/
|
||||
export const TabsDefinition = {
|
||||
export const TabsDefinition = defineComponent<TabsOwnProps>()({
|
||||
styles,
|
||||
classNames: {
|
||||
tabs: 'bui-Tabs',
|
||||
tabList: 'bui-TabList',
|
||||
tabListWrapper: 'bui-TabListWrapper',
|
||||
tab: 'bui-Tab',
|
||||
tabActive: 'bui-TabActive',
|
||||
tabHovered: 'bui-TabHovered',
|
||||
panel: 'bui-TabPanel',
|
||||
root: 'bui-Tabs',
|
||||
},
|
||||
} as const satisfies ComponentDefinition;
|
||||
propDefs: {
|
||||
className: {},
|
||||
children: {},
|
||||
},
|
||||
});
|
||||
|
||||
/** @internal */
|
||||
export const TabListDefinition = defineComponent<TabListOwnProps>()({
|
||||
styles,
|
||||
classNames: {
|
||||
root: 'bui-TabListWrapper',
|
||||
tabList: 'bui-TabList',
|
||||
},
|
||||
propDefs: {
|
||||
className: {},
|
||||
children: {},
|
||||
},
|
||||
});
|
||||
|
||||
/** @internal */
|
||||
export const TabDefinition = defineComponent<TabOwnProps>()({
|
||||
styles,
|
||||
classNames: {
|
||||
root: 'bui-Tab',
|
||||
},
|
||||
propDefs: {
|
||||
className: {},
|
||||
matchStrategy: {},
|
||||
href: {},
|
||||
id: {},
|
||||
},
|
||||
});
|
||||
|
||||
/** @internal */
|
||||
export const TabPanelDefinition = defineComponent<TabPanelOwnProps>()({
|
||||
styles,
|
||||
classNames: {
|
||||
root: 'bui-TabPanel',
|
||||
},
|
||||
propDefs: {
|
||||
className: {},
|
||||
},
|
||||
});
|
||||
|
||||
/** @internal */
|
||||
export const TabsIndicatorsDefinition =
|
||||
defineComponent<TabsIndicatorsOwnProps>()({
|
||||
styles,
|
||||
classNames: {
|
||||
root: 'bui-TabActive',
|
||||
hovered: 'bui-TabHovered',
|
||||
},
|
||||
propDefs: {
|
||||
tabRefs: {},
|
||||
tabsRef: {},
|
||||
hoveredKey: {},
|
||||
prevHoveredKey: {},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -17,9 +17,13 @@
|
||||
export { Tabs, TabList, Tab, TabPanel } from './Tabs';
|
||||
export type {
|
||||
TabsProps,
|
||||
TabsOwnProps,
|
||||
TabListProps,
|
||||
TabListOwnProps,
|
||||
TabPanelProps,
|
||||
TabPanelOwnProps,
|
||||
TabProps,
|
||||
TabOwnProps,
|
||||
TabMatchStrategy,
|
||||
} from './types';
|
||||
export { TabsDefinition } from './definition';
|
||||
|
||||
@@ -29,33 +29,69 @@ import { MutableRefObject } from 'react';
|
||||
*/
|
||||
export type TabMatchStrategy = 'exact' | 'prefix';
|
||||
|
||||
/**
|
||||
* Own props for the Tabs component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type TabsOwnProps = {
|
||||
className?: string;
|
||||
children?: AriaTabsProps['children'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Props for the Tabs component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface TabsProps extends AriaTabsProps {}
|
||||
export interface TabsProps
|
||||
extends TabsOwnProps,
|
||||
Omit<AriaTabsProps, keyof TabsOwnProps> {}
|
||||
|
||||
/**
|
||||
* Own props for the TabList component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type TabListOwnProps = {
|
||||
className?: string;
|
||||
children?: AriaTabListProps<object>['children'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Props for the TabList component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface TabListProps extends Omit<AriaTabListProps<object>, 'items'> {}
|
||||
export interface TabListProps
|
||||
extends TabListOwnProps,
|
||||
Omit<AriaTabListProps<object>, 'items' | keyof TabListOwnProps> {}
|
||||
|
||||
/**
|
||||
* Own props for the Tab component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type TabOwnProps = {
|
||||
className?: string;
|
||||
/**
|
||||
* Strategy for matching the current route to determine if this tab should be active.
|
||||
* - 'exact': Tab href must exactly match the current pathname (default)
|
||||
* - 'prefix': Tab is active if current pathname starts with tab href
|
||||
*/
|
||||
matchStrategy?: TabMatchStrategy;
|
||||
href?: AriaTabProps['href'];
|
||||
id?: AriaTabProps['id'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Props for the Tab component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface TabProps extends AriaTabProps {
|
||||
/**
|
||||
* Strategy for matching the current route to determine if this tab should be active.
|
||||
* - 'exact': Tab href must exactly match the current pathname (default)
|
||||
* - 'prefix': Tab is active if current pathname starts with tab href
|
||||
*/
|
||||
matchStrategy?: 'exact' | 'prefix';
|
||||
}
|
||||
export interface TabProps
|
||||
extends TabOwnProps,
|
||||
Omit<AriaTabProps, keyof TabOwnProps> {}
|
||||
|
||||
/** Context for sharing refs between Tabs and TabList
|
||||
*
|
||||
@@ -70,21 +106,39 @@ export interface TabsContextValue {
|
||||
setTabRef: (key: string, element: HTMLDivElement | null) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Own props for the TabPanel component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type TabPanelOwnProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Props for the TabPanel component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface TabPanelProps extends AriaTabPanelProps {}
|
||||
export interface TabPanelProps
|
||||
extends TabPanelOwnProps,
|
||||
Omit<AriaTabPanelProps, keyof TabPanelOwnProps> {}
|
||||
|
||||
/**
|
||||
* Own props for the TabsIndicators component.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export type TabsIndicatorsOwnProps = {
|
||||
tabRefs: MutableRefObject<Map<string, HTMLDivElement>>;
|
||||
tabsRef: MutableRefObject<HTMLDivElement | null>;
|
||||
hoveredKey: string | null;
|
||||
prevHoveredKey: MutableRefObject<string | null>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Props for the TabsIndicators component.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export interface TabsIndicatorsProps {
|
||||
tabRefs: MutableRefObject<Map<string, HTMLDivElement>>;
|
||||
tabsRef: MutableRefObject<HTMLDivElement | null>;
|
||||
hoveredKey: string | null;
|
||||
prevHoveredKey: MutableRefObject<string | null>;
|
||||
}
|
||||
export interface TabsIndicatorsProps extends TabsIndicatorsOwnProps {}
|
||||
|
||||
Reference in New Issue
Block a user