Migrate Dialog component from useStyles to useDefinition

Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
Johan Persson
2026-02-25 17:31:52 +01:00
parent 54d5ba4cff
commit 08bc4dbcfe
10 changed files with 274 additions and 122 deletions
+93 -33
View File
@@ -13,7 +13,6 @@ import type { ComponentProps } from 'react';
import type { ComponentPropsWithoutRef } from 'react';
import type { ComponentPropsWithRef } from 'react';
import type { CSSProperties } from 'react';
import { DetailedHTMLProps } from 'react';
import type { DialogTriggerProps as DialogTriggerProps_2 } from 'react-aria-components';
import type { DisclosureGroupProps } from 'react-aria-components';
import type { DisclosurePanelProps } from 'react-aria-components';
@@ -21,7 +20,6 @@ import type { DisclosureProps } from 'react-aria-components';
import type { ElementType } from 'react';
import { ForwardRefExoticComponent } from 'react';
import type { HeadingProps } from 'react-aria-components';
import { HTMLAttributes } from 'react';
import { JSX as JSX_2 } from 'react/jsx-runtime';
import type { LinkProps as LinkProps_2 } from 'react-aria-components';
import type { ListBoxItemProps } from 'react-aria-components';
@@ -939,59 +937,121 @@ export const DialogBody: ForwardRefExoticComponent<
>;
// @public
export interface DialogBodyProps {
// (undocumented)
children?: React.ReactNode;
// (undocumented)
export const DialogBodyDefinition: {
readonly styles: {
readonly [key: string]: string;
};
readonly classNames: {
readonly root: 'bui-DialogBody';
};
readonly propDefs: {
readonly children: {};
readonly className: {};
};
};
// @public (undocumented)
export type DialogBodyOwnProps = {
children?: ReactNode;
className?: string;
}
};
// @public
export interface DialogBodyProps extends DialogBodyOwnProps {}
// @public
export const DialogDefinition: {
readonly styles: {
readonly [key: string]: string;
};
readonly classNames: {
readonly overlay: 'bui-DialogOverlay';
readonly root: 'bui-DialogOverlay';
readonly dialog: 'bui-Dialog';
readonly content: 'bui-DialogContent';
readonly header: 'bui-DialogHeader';
readonly headerTitle: 'bui-DialogHeaderTitle';
readonly body: 'bui-DialogBody';
readonly footer: 'bui-DialogFooter';
};
readonly propDefs: {
readonly children: {};
readonly className: {};
readonly width: {};
readonly height: {};
readonly style: {};
};
};
// @public (undocumented)
export const DialogFooter: ForwardRefExoticComponent<
Omit<
DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>,
'ref'
> &
RefAttributes<HTMLDivElement>
DialogFooterProps & RefAttributes<HTMLDivElement>
>;
// @public
export const DialogFooterDefinition: {
readonly styles: {
readonly [key: string]: string;
};
readonly classNames: {
readonly root: 'bui-DialogFooter';
};
readonly propDefs: {
readonly children: {};
readonly className: {};
};
};
// @public (undocumented)
export type DialogFooterOwnProps = {
children?: ReactNode;
className?: string;
};
// @public
export interface DialogFooterProps
extends DialogFooterOwnProps,
Omit<React.ComponentPropsWithoutRef<'div'>, keyof DialogFooterOwnProps> {}
// @public (undocumented)
export const DialogHeader: ForwardRefExoticComponent<
DialogHeaderProps & RefAttributes<HTMLDivElement>
>;
// @public
export interface DialogHeaderProps extends HeadingProps {
// (undocumented)
children?: React.ReactNode;
// (undocumented)
export const DialogHeaderDefinition: {
readonly styles: {
readonly [key: string]: string;
};
readonly classNames: {
readonly root: 'bui-DialogHeader';
readonly title: 'bui-DialogHeaderTitle';
};
readonly propDefs: {
readonly children: {};
readonly className: {};
};
};
// @public (undocumented)
export type DialogHeaderOwnProps = {
children?: ReactNode;
className?: string;
}
};
// @public
export interface DialogProps extends ModalOverlayProps {
// (undocumented)
children?: React.ReactNode;
// (undocumented)
export interface DialogHeaderProps
extends DialogHeaderOwnProps,
Omit<HeadingProps, keyof DialogHeaderOwnProps> {}
// @public (undocumented)
export type DialogOwnProps = {
children?: ReactNode;
className?: string;
// (undocumented)
height?: number | string;
// (undocumented)
width?: number | string;
}
height?: number | string;
style?: React.CSSProperties;
};
// @public
export interface DialogProps
extends DialogOwnProps,
Omit<ModalOverlayProps, keyof DialogOwnProps> {}
// @public (undocumented)
export const DialogTrigger: (props: DialogTriggerProps) => JSX_2.Element;
@@ -1098,7 +1158,7 @@ export type FlexDirection = 'row' | 'column';
// @public (undocumented)
export type FlexOwnProps = {
children?: ReactNode;
children: ReactNode;
className?: string;
style?: CSSProperties;
bg?: Responsive<ProviderBg>;
@@ -1214,7 +1274,7 @@ export const GridItemDefinition: {
// @public (undocumented)
export type GridItemOwnProps = {
children?: ReactNode;
children: ReactNode;
className?: string;
style?: CSSProperties;
bg?: Responsive<ProviderBg>;
@@ -1234,7 +1294,7 @@ export interface GridItemProps extends GridItemOwnProps {
// @public (undocumented)
export type GridOwnProps = {
children?: ReactNode;
children: ReactNode;
className?: string;
style?: CSSProperties;
bg?: Responsive<ProviderBg>;
+28 -44
View File
@@ -21,21 +21,25 @@ import {
Modal,
Heading,
} from 'react-aria-components';
import clsx from 'clsx';
import type {
DialogTriggerProps,
DialogHeaderProps,
DialogProps,
DialogBodyProps,
DialogFooterProps,
} from './types';
import { RiCloseLine } from '@remixicon/react';
import { Button } from '../Button';
import { useStyles } from '../../hooks/useStyles';
import { DialogDefinition } from './definition';
import { Flex } from '../Flex';
import { useDefinition } from '../../hooks/useDefinition';
import {
DialogDefinition,
DialogHeaderDefinition,
DialogBodyDefinition,
DialogFooterDefinition,
} from './definition';
import { Box } from '../Box';
import { BgReset } from '../../hooks/useBg';
import styles from './Dialog.module.css';
import { Flex } from '../Flex';
/** @public */
export const DialogTrigger = (props: DialogTriggerProps) => {
@@ -45,23 +49,21 @@ export const DialogTrigger = (props: DialogTriggerProps) => {
/** @public */
export const Dialog = forwardRef<React.ElementRef<typeof Modal>, DialogProps>(
(props, ref) => {
const { classNames, cleanedProps } = useStyles(DialogDefinition, props);
const { className, children, width, height, style, ...rest } = cleanedProps;
const { ownProps, restProps } = useDefinition(DialogDefinition, props, {
classNameTarget: 'dialog',
});
const { classes, children, width, height, style } = ownProps;
return (
<Modal
ref={ref}
className={clsx(classNames.overlay, styles[classNames.overlay])}
className={classes.root}
isDismissable
isKeyboardDismissDisabled={false}
{...rest}
{...restProps}
>
<RADialog
className={clsx(
classNames.dialog,
styles[classNames.dialog],
className,
)}
className={classes.dialog}
style={{
['--bui-dialog-min-width' as keyof React.CSSProperties]:
typeof width === 'number' ? `${width}px` : width || '400px',
@@ -74,10 +76,7 @@ export const Dialog = forwardRef<React.ElementRef<typeof Modal>, DialogProps>(
}}
>
<BgReset>
<Box
bg="neutral"
className={clsx(classNames.content, styles[classNames.content])}
>
<Box bg="neutral" className={classes.content}>
{children}
</Box>
</BgReset>
@@ -94,19 +93,12 @@ export const DialogHeader = forwardRef<
React.ElementRef<'div'>,
DialogHeaderProps
>((props, ref) => {
const { classNames, cleanedProps } = useStyles(DialogDefinition, props);
const { className, children, ...rest } = cleanedProps;
const { ownProps, restProps } = useDefinition(DialogHeaderDefinition, props);
const { classes, children } = ownProps;
return (
<Flex
ref={ref}
className={clsx(classNames.header, styles[classNames.header], className)}
{...rest}
>
<Heading
slot="title"
className={clsx(classNames.headerTitle, styles[classNames.headerTitle])}
>
<Flex ref={ref} className={classes.root} {...restProps}>
<Heading slot="title" className={classes.title}>
{children}
</Heading>
<Button name="close" aria-label="Close" variant="tertiary" slot="close">
@@ -120,15 +112,11 @@ DialogHeader.displayName = 'DialogHeader';
/** @public */
export const DialogBody = forwardRef<React.ElementRef<'div'>, DialogBodyProps>(
(props, ref) => {
const { classNames, cleanedProps } = useStyles(DialogDefinition, props);
const { className, children, ...rest } = cleanedProps;
const { ownProps, restProps } = useDefinition(DialogBodyDefinition, props);
const { classes, children } = ownProps;
return (
<div
className={clsx(classNames.body, styles[classNames.body], className)}
ref={ref}
{...rest}
>
<div className={classes.root} ref={ref} {...restProps}>
{children}
</div>
);
@@ -140,17 +128,13 @@ DialogBody.displayName = 'DialogBody';
/** @public */
export const DialogFooter = forwardRef<
React.ElementRef<'div'>,
React.ComponentPropsWithoutRef<'div'>
DialogFooterProps
>((props, ref) => {
const { classNames, cleanedProps } = useStyles(DialogDefinition, props);
const { className, children, ...rest } = cleanedProps;
const { ownProps, restProps } = useDefinition(DialogFooterDefinition, props);
const { classes, children } = ownProps;
return (
<div
ref={ref}
className={clsx(classNames.footer, styles[classNames.footer], className)}
{...rest}
>
<div ref={ref} className={classes.root} {...restProps}>
{children}
</div>
);
@@ -14,20 +14,77 @@
* limitations under the License.
*/
import type { ComponentDefinition } from '../../types';
import { defineComponent } from '../../hooks/useDefinition';
import type {
DialogOwnProps,
DialogHeaderOwnProps,
DialogBodyOwnProps,
DialogFooterOwnProps,
} from './types';
import styles from './Dialog.module.css';
/**
* Component definition for Dialog
* @public
*/
export const DialogDefinition = {
export const DialogDefinition = defineComponent<DialogOwnProps>()({
styles,
classNames: {
overlay: 'bui-DialogOverlay',
root: 'bui-DialogOverlay',
dialog: 'bui-Dialog',
content: 'bui-DialogContent',
header: 'bui-DialogHeader',
headerTitle: 'bui-DialogHeaderTitle',
body: 'bui-DialogBody',
footer: 'bui-DialogFooter',
},
} as const satisfies ComponentDefinition;
propDefs: {
children: {},
className: {},
width: {},
height: {},
style: {},
},
});
/**
* Component definition for DialogHeader
* @public
*/
export const DialogHeaderDefinition = defineComponent<DialogHeaderOwnProps>()({
styles,
classNames: {
root: 'bui-DialogHeader',
title: 'bui-DialogHeaderTitle',
},
propDefs: {
children: {},
className: {},
},
});
/**
* Component definition for DialogBody
* @public
*/
export const DialogBodyDefinition = defineComponent<DialogBodyOwnProps>()({
styles,
classNames: {
root: 'bui-DialogBody',
},
propDefs: {
children: {},
className: {},
},
});
/**
* Component definition for DialogFooter
* @public
*/
export const DialogFooterDefinition = defineComponent<DialogFooterOwnProps>()({
styles,
classNames: {
root: 'bui-DialogFooter',
},
propDefs: {
children: {},
className: {},
},
});
+6 -1
View File
@@ -14,5 +14,10 @@
* limitations under the License.
*/
export * from './Dialog';
export { DialogDefinition } from './definition';
export {
DialogDefinition,
DialogHeaderDefinition,
DialogBodyDefinition,
DialogFooterDefinition,
} from './definition';
export * from './types';
+40 -11
View File
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import type { ReactNode } from 'react';
import type {
DialogTriggerProps as RADialogTriggerProps,
ModalOverlayProps as RAModalProps,
@@ -26,31 +27,59 @@ import type {
*/
export interface DialogTriggerProps extends RADialogTriggerProps {}
/** @public */
export type DialogOwnProps = {
children?: ReactNode;
className?: string;
width?: number | string;
height?: number | string;
style?: React.CSSProperties;
};
/**
* Props for the Dialog component.
* @public
*/
export interface DialogProps extends RAModalProps {
export interface DialogProps
extends DialogOwnProps,
Omit<RAModalProps, keyof DialogOwnProps> {}
/** @public */
export type DialogHeaderOwnProps = {
children?: ReactNode;
className?: string;
children?: React.ReactNode;
width?: number | string;
height?: number | string;
}
};
/**
* Props for the DialogHeader component.
* @public
*/
export interface DialogHeaderProps extends RAHeadingProps {
children?: React.ReactNode;
export interface DialogHeaderProps
extends DialogHeaderOwnProps,
Omit<RAHeadingProps, keyof DialogHeaderOwnProps> {}
/** @public */
export type DialogBodyOwnProps = {
children?: ReactNode;
className?: string;
}
};
/**
* Props for the DialogBody component.
* @public
*/
export interface DialogBodyProps {
children?: React.ReactNode;
export interface DialogBodyProps extends DialogBodyOwnProps {}
/** @public */
export type DialogFooterOwnProps = {
children?: ReactNode;
className?: string;
}
};
/**
* Props for the DialogFooter component.
* @public
*/
export interface DialogFooterProps
extends DialogFooterOwnProps,
Omit<React.ComponentPropsWithoutRef<'div'>, keyof DialogFooterOwnProps> {}
@@ -35,6 +35,7 @@ const meta = preview.meta({
options: ['row', 'column', 'row-reverse', 'column-reverse'],
},
},
args: { children: null },
});
const DecorativeBox = ({
+1 -1
View File
@@ -19,7 +19,7 @@ import type { Responsive, Space, SpaceProps, ProviderBg } from '../../types';
/** @public */
export type FlexOwnProps = {
children?: ReactNode;
children: ReactNode;
className?: string;
style?: CSSProperties;
bg?: Responsive<ProviderBg>;
@@ -24,6 +24,7 @@ const meta = preview.meta({
component: Grid.Root,
// We will add this story in the manifest when the component is not composed.
tags: ['!manifest'],
args: { children: null },
});
const FakeBox = () => (
+2 -2
View File
@@ -25,7 +25,7 @@ import type {
/** @public */
export type GridOwnProps = {
children?: ReactNode;
children: ReactNode;
className?: string;
style?: CSSProperties;
bg?: Responsive<ProviderBg>;
@@ -39,7 +39,7 @@ export interface GridProps extends SpaceProps, GridOwnProps {
/** @public */
export type GridItemOwnProps = {
children?: ReactNode;
children: ReactNode;
className?: string;
style?: CSSProperties;
bg?: Responsive<ProviderBg>;