Migrate useSurface to useBg
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
+160
-79
@@ -144,7 +144,6 @@ export const AlertDefinition: {
|
||||
readonly spinner: 'bui-AlertSpinner';
|
||||
readonly actions: 'bui-AlertActions';
|
||||
};
|
||||
readonly surface: 'container';
|
||||
readonly propDefs: {
|
||||
readonly status: {
|
||||
readonly dataAttribute: true;
|
||||
@@ -157,7 +156,6 @@ export const AlertDefinition: {
|
||||
readonly customActions: {};
|
||||
readonly title: {};
|
||||
readonly description: {};
|
||||
readonly surface: {};
|
||||
readonly className: {};
|
||||
readonly style: {};
|
||||
};
|
||||
@@ -165,7 +163,7 @@ export const AlertDefinition: {
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type AlertOwnProps = ContainerSurfaceProps & {
|
||||
export type AlertOwnProps = {
|
||||
status?: Responsive<'info' | 'success' | 'warning' | 'danger'>;
|
||||
icon?: boolean | ReactElement;
|
||||
loading?: boolean;
|
||||
@@ -207,6 +205,33 @@ export interface AvatarProps extends React.ComponentPropsWithoutRef<'div'> {
|
||||
src: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type Bg =
|
||||
| 'neutral-1'
|
||||
| 'neutral-2'
|
||||
| 'neutral-3'
|
||||
| 'neutral-4'
|
||||
| 'danger'
|
||||
| 'warning'
|
||||
| 'success';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface BgContextValue {
|
||||
// (undocumented)
|
||||
bg: Responsive<Bg> | undefined;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const BgProvider: ({ bg, children }: BgProviderProps) => JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface BgProviderProps {
|
||||
// (undocumented)
|
||||
bg: Responsive<Bg>;
|
||||
// (undocumented)
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type Border = 'none' | 'base' | 'error' | 'warning' | 'selected';
|
||||
|
||||
@@ -234,12 +259,12 @@ export const BoxDefinition: {
|
||||
readonly classNames: {
|
||||
readonly root: 'bui-Box';
|
||||
};
|
||||
readonly surface: 'container';
|
||||
readonly bg: 'container';
|
||||
readonly propDefs: {
|
||||
readonly as: {
|
||||
readonly default: 'div';
|
||||
};
|
||||
readonly surface: {
|
||||
readonly bg: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
readonly children: {};
|
||||
@@ -275,7 +300,7 @@ export const BoxDefinition: {
|
||||
// @public (undocumented)
|
||||
export type BoxOwnProps = {
|
||||
as?: keyof JSX.IntrinsicElements;
|
||||
surface?: Responsive<Surface>;
|
||||
bg?: Responsive<Bg>;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
@@ -320,7 +345,7 @@ export const ButtonDefinition: {
|
||||
readonly content: 'bui-ButtonContent';
|
||||
readonly spinner: 'bui-ButtonSpinner';
|
||||
};
|
||||
readonly surface: 'leaf';
|
||||
readonly bg: 'leaf';
|
||||
readonly propDefs: {
|
||||
readonly size: {
|
||||
readonly dataAttribute: true;
|
||||
@@ -338,7 +363,6 @@ export const ButtonDefinition: {
|
||||
};
|
||||
readonly iconStart: {};
|
||||
readonly iconEnd: {};
|
||||
readonly onSurface: {};
|
||||
readonly children: {};
|
||||
readonly className: {};
|
||||
readonly style: {};
|
||||
@@ -360,7 +384,7 @@ export const ButtonIconDefinition: {
|
||||
readonly content: 'bui-ButtonIconContent';
|
||||
readonly spinner: 'bui-ButtonIconSpinner';
|
||||
};
|
||||
readonly surface: 'leaf';
|
||||
readonly bg: 'leaf';
|
||||
readonly propDefs: {
|
||||
readonly size: {
|
||||
readonly dataAttribute: true;
|
||||
@@ -374,14 +398,13 @@ export const ButtonIconDefinition: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
readonly icon: {};
|
||||
readonly onSurface: {};
|
||||
readonly className: {};
|
||||
readonly style: {};
|
||||
};
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type ButtonIconOwnProps = LeafSurfaceProps & {
|
||||
export type ButtonIconOwnProps = {
|
||||
size?: Responsive<'small' | 'medium'>;
|
||||
variant?: Responsive<'primary' | 'secondary' | 'tertiary'>;
|
||||
icon?: ReactElement;
|
||||
@@ -409,7 +432,7 @@ export const ButtonLinkDefinition: {
|
||||
readonly root: 'bui-ButtonLink';
|
||||
readonly content: 'bui-ButtonLinkContent';
|
||||
};
|
||||
readonly surface: 'leaf';
|
||||
readonly bg: 'leaf';
|
||||
readonly propDefs: {
|
||||
readonly size: {
|
||||
readonly dataAttribute: true;
|
||||
@@ -421,7 +444,6 @@ export const ButtonLinkDefinition: {
|
||||
};
|
||||
readonly iconStart: {};
|
||||
readonly iconEnd: {};
|
||||
readonly onSurface: {};
|
||||
readonly children: {};
|
||||
readonly className: {};
|
||||
readonly style: {};
|
||||
@@ -429,7 +451,7 @@ export const ButtonLinkDefinition: {
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type ButtonLinkOwnProps = LeafSurfaceProps & {
|
||||
export type ButtonLinkOwnProps = {
|
||||
size?: Responsive<'small' | 'medium'>;
|
||||
variant?: Responsive<'primary' | 'secondary' | 'tertiary'>;
|
||||
iconStart?: ReactElement;
|
||||
@@ -445,7 +467,7 @@ export interface ButtonLinkProps
|
||||
ButtonLinkOwnProps {}
|
||||
|
||||
// @public (undocumented)
|
||||
export type ButtonOwnProps = LeafSurfaceProps & {
|
||||
export type ButtonOwnProps = {
|
||||
size?: Responsive<'small' | 'medium'>;
|
||||
variant?: Responsive<'primary' | 'secondary' | 'tertiary'>;
|
||||
destructive?: boolean;
|
||||
@@ -473,18 +495,45 @@ export const CardBody: ForwardRefExoticComponent<
|
||||
>;
|
||||
|
||||
// @public
|
||||
export interface CardBodyProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
// (undocumented)
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
export const CardBodyDefinition: {
|
||||
readonly styles: {
|
||||
readonly [key: string]: string;
|
||||
};
|
||||
readonly classNames: {
|
||||
readonly root: 'bui-CardBody';
|
||||
};
|
||||
readonly propDefs: {
|
||||
readonly children: {};
|
||||
readonly className: {};
|
||||
};
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type CardBodyOwnProps = {
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface CardBodyProps
|
||||
extends CardBodyOwnProps,
|
||||
React.HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
// @public
|
||||
export const CardDefinition: {
|
||||
readonly styles: {
|
||||
readonly [key: string]: string;
|
||||
};
|
||||
readonly classNames: {
|
||||
readonly root: 'bui-Card';
|
||||
readonly header: 'bui-CardHeader';
|
||||
readonly body: 'bui-CardBody';
|
||||
readonly footer: 'bui-CardFooter';
|
||||
};
|
||||
readonly bg: 'container';
|
||||
readonly propDefs: {
|
||||
readonly bg: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
readonly children: {};
|
||||
readonly className: {};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -494,10 +543,29 @@ export const CardFooter: ForwardRefExoticComponent<
|
||||
>;
|
||||
|
||||
// @public
|
||||
export interface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
// (undocumented)
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
export const CardFooterDefinition: {
|
||||
readonly styles: {
|
||||
readonly [key: string]: string;
|
||||
};
|
||||
readonly classNames: {
|
||||
readonly root: 'bui-CardFooter';
|
||||
};
|
||||
readonly propDefs: {
|
||||
readonly children: {};
|
||||
readonly className: {};
|
||||
};
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type CardFooterOwnProps = {
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface CardFooterProps
|
||||
extends CardFooterOwnProps,
|
||||
React.HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
// @public
|
||||
export const CardHeader: ForwardRefExoticComponent<
|
||||
@@ -505,16 +573,41 @@ export const CardHeader: ForwardRefExoticComponent<
|
||||
>;
|
||||
|
||||
// @public
|
||||
export interface CardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
// (undocumented)
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
export const CardHeaderDefinition: {
|
||||
readonly styles: {
|
||||
readonly [key: string]: string;
|
||||
};
|
||||
readonly classNames: {
|
||||
readonly root: 'bui-CardHeader';
|
||||
};
|
||||
readonly propDefs: {
|
||||
readonly children: {};
|
||||
readonly className: {};
|
||||
};
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type CardHeaderOwnProps = {
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
// (undocumented)
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
export interface CardHeaderProps
|
||||
extends CardHeaderOwnProps,
|
||||
React.HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
// @public (undocumented)
|
||||
export type CardOwnProps = {
|
||||
bg?: Responsive<Bg>;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface CardProps
|
||||
extends CardOwnProps,
|
||||
React.HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
// @public (undocumented)
|
||||
export const Cell: {
|
||||
@@ -684,12 +777,6 @@ export interface ContainerProps {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ContainerSurfaceProps {
|
||||
// (undocumented)
|
||||
surface?: Responsive<Surface>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CursorParams<TFilter> {
|
||||
// (undocumented)
|
||||
@@ -861,11 +948,11 @@ export const FlexDefinition: {
|
||||
'direction',
|
||||
];
|
||||
readonly dataAttributes: {
|
||||
readonly surface: readonly [
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
readonly bg: readonly [
|
||||
'neutral-1',
|
||||
'neutral-2',
|
||||
'neutral-3',
|
||||
'neutral-4',
|
||||
'danger',
|
||||
'warning',
|
||||
'success',
|
||||
@@ -881,6 +968,8 @@ export interface FlexProps extends SpaceProps {
|
||||
// (undocumented)
|
||||
align?: Responsive<'start' | 'center' | 'end' | 'baseline' | 'stretch'>;
|
||||
// (undocumented)
|
||||
bg?: Responsive<Bg>;
|
||||
// (undocumented)
|
||||
children?: React.ReactNode;
|
||||
// (undocumented)
|
||||
className?: string;
|
||||
@@ -892,8 +981,6 @@ export interface FlexProps extends SpaceProps {
|
||||
justify?: Responsive<'start' | 'center' | 'end' | 'between'>;
|
||||
// (undocumented)
|
||||
style?: React.CSSProperties;
|
||||
// (undocumented)
|
||||
surface?: Responsive<Surface>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -931,11 +1018,11 @@ export const GridDefinition: {
|
||||
'py',
|
||||
];
|
||||
readonly dataAttributes: {
|
||||
readonly surface: readonly [
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
readonly bg: readonly [
|
||||
'neutral-1',
|
||||
'neutral-2',
|
||||
'neutral-3',
|
||||
'neutral-4',
|
||||
'danger',
|
||||
'warning',
|
||||
'success',
|
||||
@@ -950,11 +1037,11 @@ export const GridItemDefinition: {
|
||||
};
|
||||
readonly utilityProps: ['colSpan', 'colEnd', 'colStart', 'rowSpan'];
|
||||
readonly dataAttributes: {
|
||||
readonly surface: readonly [
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
readonly bg: readonly [
|
||||
'neutral-1',
|
||||
'neutral-2',
|
||||
'neutral-3',
|
||||
'neutral-4',
|
||||
'danger',
|
||||
'warning',
|
||||
'success',
|
||||
@@ -964,6 +1051,8 @@ export const GridItemDefinition: {
|
||||
|
||||
// @public (undocumented)
|
||||
export interface GridItemProps {
|
||||
// (undocumented)
|
||||
bg?: Responsive<Bg>;
|
||||
// (undocumented)
|
||||
children?: React.ReactNode;
|
||||
// (undocumented)
|
||||
@@ -978,12 +1067,12 @@ export interface GridItemProps {
|
||||
rowSpan?: Responsive<Columns>;
|
||||
// (undocumented)
|
||||
style?: React.CSSProperties;
|
||||
// (undocumented)
|
||||
surface?: Responsive<Surface>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface GridProps extends SpaceProps {
|
||||
// (undocumented)
|
||||
bg?: Responsive<Bg>;
|
||||
// (undocumented)
|
||||
children?: React.ReactNode;
|
||||
// (undocumented)
|
||||
@@ -994,8 +1083,6 @@ export interface GridProps extends SpaceProps {
|
||||
gap?: Responsive<Space>;
|
||||
// (undocumented)
|
||||
style?: React.CSSProperties;
|
||||
// (undocumented)
|
||||
surface?: Responsive<Surface>;
|
||||
}
|
||||
|
||||
// @public
|
||||
@@ -1088,12 +1175,6 @@ export type JustifyContent =
|
||||
| 'around'
|
||||
| 'between';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface LeafSurfaceProps {
|
||||
// (undocumented)
|
||||
onSurface?: Responsive<Surface>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const Link: ForwardRefExoticComponent<
|
||||
LinkProps & RefAttributes<HTMLAnchorElement>
|
||||
@@ -1655,17 +1736,6 @@ export const SubmenuTrigger: (props: SubmenuTriggerProps) => JSX_2.Element;
|
||||
// @public (undocumented)
|
||||
export interface SubmenuTriggerProps extends SubmenuTriggerProps_2 {}
|
||||
|
||||
// @public
|
||||
export type Surface =
|
||||
| '0'
|
||||
| '1'
|
||||
| '2'
|
||||
| '3'
|
||||
| 'danger'
|
||||
| 'warning'
|
||||
| 'success'
|
||||
| 'auto';
|
||||
|
||||
// @public (undocumented)
|
||||
export const Switch: ForwardRefExoticComponent<
|
||||
SwitchProps & RefAttributes<HTMLLabelElement>
|
||||
@@ -2079,7 +2149,6 @@ export interface ToggleButtonProps extends ToggleButtonProps_2 {
|
||||
iconEnd?: ReactElement;
|
||||
// (undocumented)
|
||||
iconStart?: ReactElement;
|
||||
onSurface?: Responsive<Surface>;
|
||||
// (undocumented)
|
||||
size?: 'small' | 'medium' | Partial<Record<Breakpoint, 'small' | 'medium'>>;
|
||||
}
|
||||
@@ -2108,6 +2177,18 @@ export const TooltipTrigger: (
|
||||
props: TooltipTriggerComponentProps,
|
||||
) => JSX_2.Element;
|
||||
|
||||
// @public
|
||||
export const useBg: (options?: UseBgOptions) => BgContextValue;
|
||||
|
||||
// @public (undocumented)
|
||||
export type UseBgOptions =
|
||||
| {
|
||||
bg: Responsive<Bg> | undefined;
|
||||
}
|
||||
| {
|
||||
leaf: true;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export const useBreakpoint: () => {
|
||||
breakpoint: Breakpoint;
|
||||
|
||||
@@ -284,50 +284,46 @@ export const LongContent = meta.story({
|
||||
),
|
||||
});
|
||||
|
||||
export const OnDifferentSurfaces = meta.story({
|
||||
export const OnDifferentBackgrounds = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Default Surface</Text>
|
||||
<Text>Default</Text>
|
||||
<Flex direction="column" gap="2" p="4">
|
||||
<Alert status="info" icon={true} title="Alert on default surface" />
|
||||
<Alert
|
||||
status="success"
|
||||
icon={true}
|
||||
title="Alert on default surface"
|
||||
/>
|
||||
<Alert status="info" icon={true} title="Alert on default bg" />
|
||||
<Alert status="success" icon={true} title="Alert on default bg" />
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 0</Text>
|
||||
<Flex direction="column" gap="2" surface="0" p="4">
|
||||
<Alert status="info" icon={true} title="Alert on surface 0" />
|
||||
<Alert status="success" icon={true} title="Alert on surface 0" />
|
||||
<Text>On Neutral 1</Text>
|
||||
<Flex direction="column" gap="2" bg="neutral-1" p="4">
|
||||
<Alert status="info" icon={true} title="Alert on neutral-1" />
|
||||
<Alert status="success" icon={true} title="Alert on neutral-1" />
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 1</Text>
|
||||
<Flex direction="column" gap="2" surface="1" p="4">
|
||||
<Alert status="info" icon={true} title="Alert on surface 1" />
|
||||
<Alert status="success" icon={true} title="Alert on surface 1" />
|
||||
<Text>On Neutral 2</Text>
|
||||
<Flex direction="column" gap="2" bg="neutral-2" p="4">
|
||||
<Alert status="info" icon={true} title="Alert on neutral-2" />
|
||||
<Alert status="success" icon={true} title="Alert on neutral-2" />
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 2</Text>
|
||||
<Flex direction="column" gap="2" surface="2" p="4">
|
||||
<Alert status="info" icon={true} title="Alert on surface 2" />
|
||||
<Alert status="success" icon={true} title="Alert on surface 2" />
|
||||
<Text>On Neutral 3</Text>
|
||||
<Flex direction="column" gap="2" bg="neutral-3" p="4">
|
||||
<Alert status="info" icon={true} title="Alert on neutral-3" />
|
||||
<Alert status="success" icon={true} title="Alert on neutral-3" />
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 3</Text>
|
||||
<Flex direction="column" gap="2" surface="3" p="4">
|
||||
<Alert status="info" icon={true} title="Alert on surface 3" />
|
||||
<Alert status="success" icon={true} title="Alert on surface 3" />
|
||||
<Text>On Neutral 4</Text>
|
||||
<Flex direction="column" gap="2" bg="neutral-4" p="4">
|
||||
<Alert status="info" icon={true} title="Alert on neutral-4" />
|
||||
<Alert status="success" icon={true} title="Alert on neutral-4" />
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
@@ -349,7 +345,7 @@ export const Responsive = meta.story({
|
||||
|
||||
export const WithUtilityProps = meta.story({
|
||||
render: () => (
|
||||
<Box surface="1" py="4">
|
||||
<Box bg="neutral-1" py="4">
|
||||
<Alert
|
||||
status="success"
|
||||
icon={true}
|
||||
|
||||
@@ -34,7 +34,6 @@ export const AlertDefinition = defineComponent<AlertOwnProps>()({
|
||||
spinner: 'bui-AlertSpinner',
|
||||
actions: 'bui-AlertActions',
|
||||
},
|
||||
surface: 'container',
|
||||
propDefs: {
|
||||
status: { dataAttribute: true, default: 'info' },
|
||||
loading: { dataAttribute: true },
|
||||
@@ -42,7 +41,6 @@ export const AlertDefinition = defineComponent<AlertOwnProps>()({
|
||||
customActions: {},
|
||||
title: {},
|
||||
description: {},
|
||||
surface: {},
|
||||
className: {},
|
||||
style: {},
|
||||
},
|
||||
|
||||
@@ -15,14 +15,10 @@
|
||||
*/
|
||||
|
||||
import type { ReactElement, ReactNode, CSSProperties } from 'react';
|
||||
import type {
|
||||
ContainerSurfaceProps,
|
||||
Responsive,
|
||||
MarginProps,
|
||||
} from '../../types';
|
||||
import type { Responsive, MarginProps } from '../../types';
|
||||
|
||||
/** @public */
|
||||
export type AlertOwnProps = ContainerSurfaceProps & {
|
||||
export type AlertOwnProps = {
|
||||
status?: Responsive<'info' | 'success' | 'warning' | 'danger'>;
|
||||
icon?: boolean | ReactElement;
|
||||
loading?: boolean;
|
||||
|
||||
@@ -23,31 +23,31 @@
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-Box[data-surface='0'] {
|
||||
background-color: var(--bui-bg-neutral-0);
|
||||
}
|
||||
|
||||
.bui-Box[data-surface='1'] {
|
||||
.bui-Box[data-bg='neutral-1'] {
|
||||
background-color: var(--bui-bg-neutral-1);
|
||||
}
|
||||
|
||||
.bui-Box[data-surface='2'] {
|
||||
.bui-Box[data-bg='neutral-2'] {
|
||||
background-color: var(--bui-bg-neutral-2);
|
||||
}
|
||||
|
||||
.bui-Box[data-surface='3'] {
|
||||
.bui-Box[data-bg='neutral-3'] {
|
||||
background-color: var(--bui-bg-neutral-3);
|
||||
}
|
||||
|
||||
.bui-Box[data-surface='danger'] {
|
||||
.bui-Box[data-bg='neutral-4'] {
|
||||
background-color: var(--bui-bg-neutral-4);
|
||||
}
|
||||
|
||||
.bui-Box[data-bg='danger'] {
|
||||
background-color: var(--bui-bg-danger);
|
||||
}
|
||||
|
||||
.bui-Box[data-surface='warning'] {
|
||||
.bui-Box[data-bg='warning'] {
|
||||
background-color: var(--bui-bg-warning);
|
||||
}
|
||||
|
||||
.bui-Box[data-surface='success'] {
|
||||
.bui-Box[data-bg='success'] {
|
||||
background-color: var(--bui-bg-success);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,90 +346,64 @@ export const Display = meta.story({
|
||||
),
|
||||
});
|
||||
|
||||
export const Surfaces = meta.story({
|
||||
export const BackgroundColors = meta.story({
|
||||
args: { px: '6', py: '4' },
|
||||
render: args => (
|
||||
<Flex align="center" style={{ flexWrap: 'wrap' }}>
|
||||
<Box {...args}>Default</Box>
|
||||
<Box surface="0" {...args}>
|
||||
Surface 0
|
||||
<Box bg="neutral-1" {...args}>
|
||||
Neutral 1
|
||||
</Box>
|
||||
<Box surface="1" {...args}>
|
||||
Surface 1
|
||||
<Box bg="neutral-2" {...args}>
|
||||
Neutral 2
|
||||
</Box>
|
||||
<Box surface="2" {...args}>
|
||||
Surface 2
|
||||
<Box bg="neutral-3" {...args}>
|
||||
Neutral 3
|
||||
</Box>
|
||||
<Box surface="3" {...args}>
|
||||
Surface 3
|
||||
<Box bg={{ initial: 'neutral-1', sm: 'neutral-2' }} {...args}>
|
||||
Responsive Neutral
|
||||
</Box>
|
||||
<Box surface={{ initial: '0', sm: '1' }} {...args}>
|
||||
Responsive Surface
|
||||
<Box bg="danger" {...args}>
|
||||
Danger
|
||||
</Box>
|
||||
<Box surface="danger" {...args}>
|
||||
Surface Danger
|
||||
<Box bg="warning" {...args}>
|
||||
Warning
|
||||
</Box>
|
||||
<Box surface="warning" {...args}>
|
||||
Surface Warning
|
||||
</Box>
|
||||
<Box surface="success" {...args}>
|
||||
Surface Success
|
||||
<Box bg="success" {...args}>
|
||||
Success
|
||||
</Box>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const SurfacesNested = meta.story({
|
||||
export const NestedNeutralColors = meta.story({
|
||||
args: { px: '6', py: '4' },
|
||||
render: args => (
|
||||
<Flex direction="column">
|
||||
<Box style={{ maxWidth: '600px' }} mb="4">
|
||||
In this test, we are nesting boxes and buttons on different surfaces to
|
||||
ensure that the correct surface is applied to each element. If a Button
|
||||
is placed on a surface that doesn't have the surface prop set, it will
|
||||
inherit the surface from the parent.
|
||||
</Box>
|
||||
<Box {...args} surface="1">
|
||||
<Button variant="secondary">Button</Button>
|
||||
<Box {...args} surface="2" mt="4">
|
||||
<Button variant="secondary">Button</Button>
|
||||
<Flex direction="column" gap="6">
|
||||
<Flex direction="column" gap="2">
|
||||
<div>With bg="neutral-1" — nested boxes and buttons auto-increment</div>
|
||||
<Box {...args} bg="neutral-1">
|
||||
<Button variant="secondary">Button (neutral-2)</Button>
|
||||
<Box {...args} mt="4">
|
||||
<Button variant="secondary">Button</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const SurfacesAutoIncrement = meta.story({
|
||||
args: { px: '6', py: '4' },
|
||||
render: args => (
|
||||
<Flex direction="column">
|
||||
<Box style={{ maxWidth: '600px' }} mb="4">
|
||||
Using surface="auto" automatically increments from the parent surface
|
||||
level. This makes components more reusable as they don't need to know
|
||||
their absolute surface level. Notice how each nested Box with
|
||||
surface="auto" automatically increments: 0 → 1 → 2 → 3 (capped at 3).
|
||||
</Box>
|
||||
<Box {...args} surface="0">
|
||||
Surface 0 (explicit)
|
||||
<Box {...args} surface="auto" mt="4">
|
||||
<Box mb="3">Surface auto (becomes 1)</Box>
|
||||
<Button variant="secondary" onSurface="auto">
|
||||
Button auto
|
||||
</Button>
|
||||
<Box {...args} surface="auto" mt="4">
|
||||
Surface auto (becomes 2)
|
||||
<Box {...args} surface="auto" mt="4">
|
||||
Surface auto (becomes 3)
|
||||
<Box {...args} surface="auto" mt="4">
|
||||
Surface auto (stays 3 - capped)
|
||||
</Box>
|
||||
<Button variant="secondary">Button (neutral-3)</Button>
|
||||
<Box {...args} mt="4">
|
||||
<Button variant="secondary">Button (neutral-4)</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="2">
|
||||
<div>Without bg — no context, no auto-increment</div>
|
||||
<Box {...args}>
|
||||
<Button variant="secondary">Button (default)</Button>
|
||||
<Box {...args} mt="4">
|
||||
<Button variant="secondary">Button (default)</Button>
|
||||
<Box {...args} mt="4">
|
||||
<Button variant="secondary">Button (default)</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ export const Box = forwardRef<HTMLDivElement, BoxProps>((props, ref) => {
|
||||
BoxDefinition,
|
||||
props,
|
||||
);
|
||||
const { classes, as, surfaceChildren } = ownProps;
|
||||
const { classes, as, bgChildren } = ownProps;
|
||||
|
||||
return createElement(
|
||||
as,
|
||||
@@ -36,7 +36,7 @@ export const Box = forwardRef<HTMLDivElement, BoxProps>((props, ref) => {
|
||||
...dataAttributes,
|
||||
...restProps,
|
||||
},
|
||||
surfaceChildren,
|
||||
bgChildren,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -27,10 +27,10 @@ export const BoxDefinition = defineComponent<BoxOwnProps>()({
|
||||
classNames: {
|
||||
root: 'bui-Box',
|
||||
},
|
||||
surface: 'container',
|
||||
bg: 'container',
|
||||
propDefs: {
|
||||
as: { default: 'div' },
|
||||
surface: { dataAttribute: true },
|
||||
bg: { dataAttribute: true },
|
||||
children: {},
|
||||
className: {},
|
||||
style: {},
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
|
||||
import type { ReactNode, CSSProperties } from 'react';
|
||||
import type { Responsive, Surface, SpaceProps } from '../../types';
|
||||
import type { Responsive, Bg, SpaceProps } from '../../types';
|
||||
|
||||
/** @public */
|
||||
export type BoxOwnProps = {
|
||||
as?: keyof JSX.IntrinsicElements;
|
||||
surface?: Responsive<Surface>;
|
||||
bg?: Responsive<Bg>;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
|
||||
@@ -118,19 +118,19 @@
|
||||
--bg-active: var(--bui-bg-neutral-1-pressed);
|
||||
--fg: var(--bui-fg-primary);
|
||||
|
||||
&[data-on-surface='1'] {
|
||||
&[data-bg='neutral-2'] {
|
||||
--bg: var(--bui-bg-neutral-2);
|
||||
--bg-hover: var(--bui-bg-neutral-2-hover);
|
||||
--bg-active: var(--bui-bg-neutral-2-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='2'] {
|
||||
&[data-bg='neutral-3'] {
|
||||
--bg: var(--bui-bg-neutral-3);
|
||||
--bg-hover: var(--bui-bg-neutral-3-hover);
|
||||
--bg-active: var(--bui-bg-neutral-3-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='3'] {
|
||||
&[data-bg='neutral-4'] {
|
||||
--bg: var(--bui-bg-neutral-4);
|
||||
--bg-hover: var(--bui-bg-neutral-4-hover);
|
||||
--bg-active: var(--bui-bg-neutral-4-pressed);
|
||||
@@ -182,17 +182,17 @@
|
||||
--bg-active: var(--bui-bg-neutral-1-pressed);
|
||||
--fg: var(--bui-fg-primary);
|
||||
|
||||
&[data-on-surface='1'] {
|
||||
&[data-bg='neutral-2'] {
|
||||
--bg-hover: var(--bui-bg-neutral-2-hover);
|
||||
--bg-active: var(--bui-bg-neutral-2-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='2'] {
|
||||
&[data-bg='neutral-3'] {
|
||||
--bg-hover: var(--bui-bg-neutral-3-hover);
|
||||
--bg-active: var(--bui-bg-neutral-3-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='3'] {
|
||||
&[data-bg='neutral-4'] {
|
||||
--bg-hover: var(--bui-bg-neutral-4-hover);
|
||||
--bg-active: var(--bui-bg-neutral-4-pressed);
|
||||
}
|
||||
|
||||
@@ -81,32 +81,9 @@ export const Variants = meta.story({
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Neutral 0</Text>
|
||||
<Flex align="center" surface="0" p="4">
|
||||
<Button iconStart={<RiCloudLine />} variant="primary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="secondary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="tertiary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="primary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="secondary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="tertiary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Neutral 1</Text>
|
||||
<Flex align="center" surface="1" p="4">
|
||||
<Flex align="center" bg="neutral-1" p="4">
|
||||
<Button iconStart={<RiCloudLine />} variant="primary">
|
||||
Button
|
||||
</Button>
|
||||
@@ -129,63 +106,72 @@ export const Variants = meta.story({
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Neutral 2</Text>
|
||||
<Box surface="1">
|
||||
<Flex align="center" surface="2" p="4">
|
||||
<Button iconStart={<RiCloudLine />} variant="primary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="secondary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="tertiary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="primary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="secondary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="tertiary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Flex align="center" bg="neutral-2" p="4">
|
||||
<Button iconStart={<RiCloudLine />} variant="primary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="secondary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="tertiary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="primary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="secondary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="tertiary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Neutral 3</Text>
|
||||
<Box surface="1">
|
||||
<Box surface="2">
|
||||
<Flex align="center" surface="3" p="4">
|
||||
<Button iconStart={<RiCloudLine />} variant="primary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="secondary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="tertiary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="primary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
<Button
|
||||
iconStart={<RiCloudLine />}
|
||||
variant="secondary"
|
||||
destructive
|
||||
>
|
||||
Button
|
||||
</Button>
|
||||
<Button
|
||||
iconStart={<RiCloudLine />}
|
||||
variant="tertiary"
|
||||
destructive
|
||||
>
|
||||
Button
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Box>
|
||||
<Flex align="center" bg="neutral-3" p="4">
|
||||
<Button iconStart={<RiCloudLine />} variant="primary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="secondary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="tertiary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="primary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="secondary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="tertiary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Neutral 4</Text>
|
||||
<Flex align="center" bg="neutral-4" p="4">
|
||||
<Button iconStart={<RiCloudLine />} variant="primary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="secondary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="tertiary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="primary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="secondary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart={<RiCloudLine />} variant="tertiary" destructive>
|
||||
Button
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
),
|
||||
@@ -246,8 +232,8 @@ export const Destructive = meta.story({
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 1</Text>
|
||||
<Flex align="center" surface="1" p="4" gap="4">
|
||||
<Text>On Neutral 1</Text>
|
||||
<Flex align="center" bg="neutral-1" p="4" gap="4">
|
||||
<Button variant="primary" destructive>
|
||||
Primary
|
||||
</Button>
|
||||
@@ -462,49 +448,33 @@ export const LoadingVariants = meta.story({
|
||||
),
|
||||
});
|
||||
|
||||
export const OnSurfaceAuto = meta.story({
|
||||
export const AutoBg = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<div style={{ maxWidth: '600px' }}>
|
||||
Using onSurface="auto" on buttons inherits their container's surface
|
||||
level, making them reusable. This is equivalent to not specifying
|
||||
onSurface. To override, use explicit surface values like onSurface="0"
|
||||
or onSurface="2".
|
||||
Buttons automatically detect their parent bg context and increment the
|
||||
neutral level by 1. No prop is needed on the button -- it's fully
|
||||
automatic.
|
||||
</div>
|
||||
<Box surface="0" p="4">
|
||||
<Text>Surface 0 container</Text>
|
||||
<Box bg="neutral-1" p="4">
|
||||
<Text>Neutral 1 container</Text>
|
||||
<Flex gap="2" mt="2">
|
||||
<Button variant="secondary">Default (inherits 0)</Button>
|
||||
<Button variant="secondary" onSurface="auto">
|
||||
Auto (inherits 0)
|
||||
</Button>
|
||||
<Button variant="secondary" onSurface="1">
|
||||
Explicit 1
|
||||
</Button>
|
||||
<Button variant="secondary">Auto (neutral-2)</Button>
|
||||
<Button variant="tertiary">Auto (neutral-2)</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box surface="1" p="4">
|
||||
<Text>Surface 1 container</Text>
|
||||
<Box bg="neutral-2" p="4">
|
||||
<Text>Neutral 2 container</Text>
|
||||
<Flex gap="2" mt="2">
|
||||
<Button variant="secondary">Default (inherits 1)</Button>
|
||||
<Button variant="secondary" onSurface="auto">
|
||||
Auto (inherits 1)
|
||||
</Button>
|
||||
<Button variant="secondary" onSurface="2">
|
||||
Explicit 2
|
||||
</Button>
|
||||
<Button variant="secondary">Auto (neutral-3)</Button>
|
||||
<Button variant="tertiary">Auto (neutral-3)</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box surface="2" p="4">
|
||||
<Text>Surface 2 container</Text>
|
||||
<Box bg="neutral-3" p="4">
|
||||
<Text>Neutral 3 container</Text>
|
||||
<Flex gap="2" mt="2">
|
||||
<Button variant="secondary">Default (inherits 2)</Button>
|
||||
<Button variant="secondary" onSurface="auto">
|
||||
Auto (inherits 2)
|
||||
</Button>
|
||||
<Button variant="secondary" onSurface="3">
|
||||
Explicit 3
|
||||
</Button>
|
||||
<Button variant="secondary">Auto (neutral-4)</Button>
|
||||
<Button variant="tertiary">Auto (neutral-4)</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
@@ -29,7 +29,7 @@ export const ButtonDefinition = defineComponent<ButtonOwnProps>()({
|
||||
content: 'bui-ButtonContent',
|
||||
spinner: 'bui-ButtonSpinner',
|
||||
},
|
||||
surface: 'leaf',
|
||||
bg: 'leaf',
|
||||
propDefs: {
|
||||
size: { dataAttribute: true, default: 'small' },
|
||||
variant: { dataAttribute: true, default: 'primary' },
|
||||
@@ -37,7 +37,6 @@ export const ButtonDefinition = defineComponent<ButtonOwnProps>()({
|
||||
loading: { dataAttribute: true },
|
||||
iconStart: {},
|
||||
iconEnd: {},
|
||||
onSurface: {},
|
||||
children: {},
|
||||
className: {},
|
||||
style: {},
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
import type { ReactElement, ReactNode, CSSProperties } from 'react';
|
||||
import type { ButtonProps as RAButtonProps } from 'react-aria-components';
|
||||
import type { LeafSurfaceProps, Responsive } from '../../types';
|
||||
import type { Responsive } from '../../types';
|
||||
|
||||
/** @public */
|
||||
export type ButtonOwnProps = LeafSurfaceProps & {
|
||||
export type ButtonOwnProps = {
|
||||
size?: Responsive<'small' | 'medium'>;
|
||||
variant?: Responsive<'primary' | 'secondary' | 'tertiary'>;
|
||||
destructive?: boolean;
|
||||
|
||||
@@ -85,19 +85,19 @@
|
||||
--bg-active: var(--bui-bg-neutral-1-pressed);
|
||||
--fg: var(--bui-fg-primary);
|
||||
|
||||
&[data-on-surface='1'] {
|
||||
&[data-bg='neutral-2'] {
|
||||
--bg: var(--bui-bg-neutral-2);
|
||||
--bg-hover: var(--bui-bg-neutral-2-hover);
|
||||
--bg-active: var(--bui-bg-neutral-2-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='2'] {
|
||||
&[data-bg='neutral-3'] {
|
||||
--bg: var(--bui-bg-neutral-3);
|
||||
--bg-hover: var(--bui-bg-neutral-3-hover);
|
||||
--bg-active: var(--bui-bg-neutral-3-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='3'] {
|
||||
&[data-bg='neutral-4'] {
|
||||
--bg: var(--bui-bg-neutral-4);
|
||||
--bg-hover: var(--bui-bg-neutral-4-hover);
|
||||
--bg-active: var(--bui-bg-neutral-4-pressed);
|
||||
@@ -122,17 +122,17 @@
|
||||
--bg-active: var(--bui-bg-neutral-1-pressed);
|
||||
--fg: var(--bui-fg-primary);
|
||||
|
||||
&[data-on-surface='1'] {
|
||||
&[data-bg='neutral-2'] {
|
||||
--bg-hover: var(--bui-bg-neutral-2-hover);
|
||||
--bg-active: var(--bui-bg-neutral-2-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='2'] {
|
||||
&[data-bg='neutral-3'] {
|
||||
--bg-hover: var(--bui-bg-neutral-3-hover);
|
||||
--bg-active: var(--bui-bg-neutral-3-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='3'] {
|
||||
&[data-bg='neutral-4'] {
|
||||
--bg-hover: var(--bui-bg-neutral-4-hover);
|
||||
--bg-active: var(--bui-bg-neutral-4-pressed);
|
||||
}
|
||||
|
||||
@@ -29,13 +29,12 @@ export const ButtonIconDefinition = defineComponent<ButtonIconOwnProps>()({
|
||||
content: 'bui-ButtonIconContent',
|
||||
spinner: 'bui-ButtonIconSpinner',
|
||||
},
|
||||
surface: 'leaf',
|
||||
bg: 'leaf',
|
||||
propDefs: {
|
||||
size: { dataAttribute: true, default: 'small' },
|
||||
variant: { dataAttribute: true, default: 'primary' },
|
||||
loading: { dataAttribute: true },
|
||||
icon: {},
|
||||
onSurface: {},
|
||||
className: {},
|
||||
style: {},
|
||||
},
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
import type { ReactElement, CSSProperties } from 'react';
|
||||
import type { ButtonProps as RAButtonProps } from 'react-aria-components';
|
||||
import type { LeafSurfaceProps, Responsive } from '../../types';
|
||||
import type { Responsive } from '../../types';
|
||||
|
||||
/** @public */
|
||||
export type ButtonIconOwnProps = LeafSurfaceProps & {
|
||||
export type ButtonIconOwnProps = {
|
||||
size?: Responsive<'small' | 'medium'>;
|
||||
variant?: Responsive<'primary' | 'secondary' | 'tertiary'>;
|
||||
icon?: ReactElement;
|
||||
|
||||
@@ -79,19 +79,19 @@
|
||||
--bg-active: var(--bui-bg-neutral-1-pressed);
|
||||
--fg: var(--bui-fg-primary);
|
||||
|
||||
&[data-on-surface='1'] {
|
||||
&[data-bg='neutral-2'] {
|
||||
--bg: var(--bui-bg-neutral-2);
|
||||
--bg-hover: var(--bui-bg-neutral-2-hover);
|
||||
--bg-active: var(--bui-bg-neutral-2-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='2'] {
|
||||
&[data-bg='neutral-3'] {
|
||||
--bg: var(--bui-bg-neutral-3);
|
||||
--bg-hover: var(--bui-bg-neutral-3-hover);
|
||||
--bg-active: var(--bui-bg-neutral-3-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='3'] {
|
||||
&[data-bg='neutral-4'] {
|
||||
--bg: var(--bui-bg-neutral-4);
|
||||
--bg-hover: var(--bui-bg-neutral-4-hover);
|
||||
--bg-active: var(--bui-bg-neutral-4-pressed);
|
||||
@@ -115,17 +115,17 @@
|
||||
--bg-active: var(--bui-bg-neutral-1-pressed);
|
||||
--fg: var(--bui-fg-primary);
|
||||
|
||||
&[data-on-surface='1'] {
|
||||
&[data-bg='neutral-2'] {
|
||||
--bg-hover: var(--bui-bg-neutral-2-hover);
|
||||
--bg-active: var(--bui-bg-neutral-2-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='2'] {
|
||||
&[data-bg='neutral-3'] {
|
||||
--bg-hover: var(--bui-bg-neutral-3-hover);
|
||||
--bg-active: var(--bui-bg-neutral-3-pressed);
|
||||
}
|
||||
|
||||
&[data-on-surface='3'] {
|
||||
&[data-bg='neutral-4'] {
|
||||
--bg-hover: var(--bui-bg-neutral-4-hover);
|
||||
--bg-active: var(--bui-bg-neutral-4-pressed);
|
||||
}
|
||||
|
||||
@@ -28,13 +28,12 @@ export const ButtonLinkDefinition = defineComponent<ButtonLinkOwnProps>()({
|
||||
root: 'bui-ButtonLink',
|
||||
content: 'bui-ButtonLinkContent',
|
||||
},
|
||||
surface: 'leaf',
|
||||
bg: 'leaf',
|
||||
propDefs: {
|
||||
size: { dataAttribute: true, default: 'small' },
|
||||
variant: { dataAttribute: true, default: 'primary' },
|
||||
iconStart: {},
|
||||
iconEnd: {},
|
||||
onSurface: {},
|
||||
children: {},
|
||||
className: {},
|
||||
style: {},
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
import type { ReactElement, ReactNode, CSSProperties } from 'react';
|
||||
import type { LinkProps as RALinkProps } from 'react-aria-components';
|
||||
import type { LeafSurfaceProps, Responsive } from '../../types';
|
||||
import type { Responsive } from '../../types';
|
||||
|
||||
/** @public */
|
||||
export type ButtonLinkOwnProps = LeafSurfaceProps & {
|
||||
export type ButtonLinkOwnProps = {
|
||||
size?: Responsive<'small' | 'medium'>;
|
||||
variant?: Responsive<'primary' | 'secondary' | 'tertiary'>;
|
||||
iconStart?: ReactElement;
|
||||
|
||||
@@ -30,31 +30,31 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bui-Card[data-surface='0'] {
|
||||
background-color: var(--bui-bg-surface-0);
|
||||
.bui-Card[data-bg='neutral-1'] {
|
||||
background-color: var(--bui-bg-neutral-1);
|
||||
}
|
||||
|
||||
.bui-Card[data-surface='1'] {
|
||||
background-color: var(--bui-bg-surface-1);
|
||||
.bui-Card[data-bg='neutral-2'] {
|
||||
background-color: var(--bui-bg-neutral-2);
|
||||
}
|
||||
|
||||
.bui-Card[data-surface='2'] {
|
||||
background-color: var(--bui-bg-surface-2);
|
||||
.bui-Card[data-bg='neutral-3'] {
|
||||
background-color: var(--bui-bg-neutral-3);
|
||||
}
|
||||
|
||||
.bui-Card[data-surface='3'] {
|
||||
background-color: var(--bui-bg-surface-3);
|
||||
.bui-Card[data-bg='neutral-4'] {
|
||||
background-color: var(--bui-bg-neutral-4);
|
||||
}
|
||||
|
||||
.bui-Card[data-surface='danger'] {
|
||||
.bui-Card[data-bg='danger'] {
|
||||
background-color: var(--bui-bg-danger);
|
||||
}
|
||||
|
||||
.bui-Card[data-surface='warning'] {
|
||||
.bui-Card[data-bg='warning'] {
|
||||
background-color: var(--bui-bg-warning);
|
||||
}
|
||||
|
||||
.bui-Card[data-surface='success'] {
|
||||
.bui-Card[data-bg='success'] {
|
||||
background-color: var(--bui-bg-success);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,72 +128,71 @@ export const WithListRow = meta.story({
|
||||
),
|
||||
});
|
||||
|
||||
export const Surfaces = meta.story({
|
||||
export const Backgrounds = meta.story({
|
||||
render: args => (
|
||||
<Flex align="start" style={{ flexWrap: 'wrap' }} gap="4">
|
||||
<Card {...args} style={{ width: '200px' }}>
|
||||
<CardHeader>Default</CardHeader>
|
||||
<CardBody>No surface prop</CardBody>
|
||||
<CardBody>No bg prop</CardBody>
|
||||
</Card>
|
||||
<Card {...args} surface="0" style={{ width: '200px' }}>
|
||||
<CardHeader>Surface 0</CardHeader>
|
||||
<CardBody>Explicit surface 0</CardBody>
|
||||
<Card {...args} bg="neutral-1" style={{ width: '200px' }}>
|
||||
<CardHeader>Neutral 1</CardHeader>
|
||||
<CardBody>Explicit neutral-1</CardBody>
|
||||
</Card>
|
||||
<Card {...args} surface="1" style={{ width: '200px' }}>
|
||||
<CardHeader>Surface 1</CardHeader>
|
||||
<CardBody>Explicit surface 1</CardBody>
|
||||
<Card {...args} bg="neutral-2" style={{ width: '200px' }}>
|
||||
<CardHeader>Neutral 2</CardHeader>
|
||||
<CardBody>Explicit neutral-2</CardBody>
|
||||
</Card>
|
||||
<Card {...args} surface="2" style={{ width: '200px' }}>
|
||||
<CardHeader>Surface 2</CardHeader>
|
||||
<CardBody>Explicit surface 2</CardBody>
|
||||
<Card {...args} bg="neutral-3" style={{ width: '200px' }}>
|
||||
<CardHeader>Neutral 3</CardHeader>
|
||||
<CardBody>Explicit neutral-3</CardBody>
|
||||
</Card>
|
||||
<Card {...args} surface="3" style={{ width: '200px' }}>
|
||||
<CardHeader>Surface 3</CardHeader>
|
||||
<CardBody>Explicit surface 3</CardBody>
|
||||
<Card {...args} bg="neutral-4" style={{ width: '200px' }}>
|
||||
<CardHeader>Neutral 4</CardHeader>
|
||||
<CardBody>Explicit neutral-4</CardBody>
|
||||
</Card>
|
||||
<Card
|
||||
{...args}
|
||||
surface={{ initial: '0', sm: '1' }}
|
||||
bg={{ initial: 'neutral-1', sm: 'neutral-2' }}
|
||||
style={{ width: '200px' }}
|
||||
>
|
||||
<CardHeader>Responsive</CardHeader>
|
||||
<CardBody>Surface 0 → 1</CardBody>
|
||||
<CardBody>Neutral 1 → 2</CardBody>
|
||||
</Card>
|
||||
<Card {...args} surface="danger" style={{ width: '200px' }}>
|
||||
<Card {...args} bg="danger" style={{ width: '200px' }}>
|
||||
<CardHeader>Danger</CardHeader>
|
||||
<CardBody>Surface danger</CardBody>
|
||||
<CardBody>Bg danger</CardBody>
|
||||
</Card>
|
||||
<Card {...args} surface="warning" style={{ width: '200px' }}>
|
||||
<Card {...args} bg="warning" style={{ width: '200px' }}>
|
||||
<CardHeader>Warning</CardHeader>
|
||||
<CardBody>Surface warning</CardBody>
|
||||
<CardBody>Bg warning</CardBody>
|
||||
</Card>
|
||||
<Card {...args} surface="success" style={{ width: '200px' }}>
|
||||
<Card {...args} bg="success" style={{ width: '200px' }}>
|
||||
<CardHeader>Success</CardHeader>
|
||||
<CardBody>Surface success</CardBody>
|
||||
<CardBody>Bg success</CardBody>
|
||||
</Card>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const SurfacesNested = meta.story({
|
||||
export const BgNested = meta.story({
|
||||
render: args => (
|
||||
<Flex direction="column">
|
||||
<Box style={{ maxWidth: '600px' }} mb="4">
|
||||
In this test, we are nesting cards on different surfaces to ensure that
|
||||
the correct surface is applied to each element. If a Button is placed on
|
||||
a surface that doesn't have the surface prop set, it will inherit the
|
||||
surface from the parent.
|
||||
In this test, we are nesting cards to ensure that the correct background
|
||||
is applied to each element. Buttons automatically inherit the bg context
|
||||
and increment their neutral level.
|
||||
</Box>
|
||||
<Card {...args} surface="1" style={{ width: '500px' }}>
|
||||
<CardHeader>Surface 1</CardHeader>
|
||||
<Card {...args} bg="neutral-1" style={{ width: '500px' }}>
|
||||
<CardHeader>Neutral 1</CardHeader>
|
||||
<CardBody>
|
||||
<Button variant="secondary">Button</Button>
|
||||
<Card {...args} surface="2" style={{ marginTop: '16px' }}>
|
||||
<CardHeader>Surface 2</CardHeader>
|
||||
<Card {...args} bg="neutral-2" style={{ marginTop: '16px' }}>
|
||||
<CardHeader>Neutral 2</CardHeader>
|
||||
<CardBody>
|
||||
<Button variant="secondary">Button</Button>
|
||||
<Card {...args} style={{ marginTop: '16px' }}>
|
||||
<CardHeader>Inherited</CardHeader>
|
||||
<CardHeader>Auto-incremented</CardHeader>
|
||||
<CardBody>
|
||||
<Button variant="secondary">Button</Button>
|
||||
</CardBody>
|
||||
@@ -206,31 +205,31 @@ export const SurfacesNested = meta.story({
|
||||
),
|
||||
});
|
||||
|
||||
export const SurfacesAutoIncrement = meta.story({
|
||||
export const BgAutoIncrement = meta.story({
|
||||
render: args => (
|
||||
<Flex align="start" style={{ flexWrap: 'wrap' }} gap="4">
|
||||
<Box surface="0" p="4" style={{ borderRadius: '8px' }}>
|
||||
<Card {...args} surface="auto" style={{ width: '200px' }}>
|
||||
<CardHeader>On surface 0</CardHeader>
|
||||
<CardBody>Card auto → 1</CardBody>
|
||||
<Box bg="neutral-1" p="4" style={{ borderRadius: '8px' }}>
|
||||
<Card {...args} style={{ width: '200px' }}>
|
||||
<CardHeader>On neutral-1</CardHeader>
|
||||
<CardBody>Card auto → neutral-2</CardBody>
|
||||
</Card>
|
||||
</Box>
|
||||
<Box surface="1" p="4" style={{ borderRadius: '8px' }}>
|
||||
<Card {...args} surface="auto" style={{ width: '200px' }}>
|
||||
<CardHeader>On surface 1</CardHeader>
|
||||
<CardBody>Card auto → 2</CardBody>
|
||||
<Box bg="neutral-2" p="4" style={{ borderRadius: '8px' }}>
|
||||
<Card {...args} style={{ width: '200px' }}>
|
||||
<CardHeader>On neutral-2</CardHeader>
|
||||
<CardBody>Card auto → neutral-3</CardBody>
|
||||
</Card>
|
||||
</Box>
|
||||
<Box surface="2" p="4" style={{ borderRadius: '8px' }}>
|
||||
<Card {...args} surface="auto" style={{ width: '200px' }}>
|
||||
<CardHeader>On surface 2</CardHeader>
|
||||
<CardBody>Card auto → 3</CardBody>
|
||||
<Box bg="neutral-3" p="4" style={{ borderRadius: '8px' }}>
|
||||
<Card {...args} style={{ width: '200px' }}>
|
||||
<CardHeader>On neutral-3</CardHeader>
|
||||
<CardBody>Card auto → neutral-4</CardBody>
|
||||
</Card>
|
||||
</Box>
|
||||
<Box surface="3" p="4" style={{ borderRadius: '8px' }}>
|
||||
<Card {...args} surface="auto" style={{ width: '200px' }}>
|
||||
<CardHeader>On surface 3</CardHeader>
|
||||
<CardBody>Card auto → 3 (capped)</CardBody>
|
||||
<Box bg="neutral-4" p="4" style={{ borderRadius: '8px' }}>
|
||||
<Card {...args} style={{ width: '200px' }}>
|
||||
<CardHeader>On neutral-4</CardHeader>
|
||||
<CardBody>Card auto → neutral-4 (capped)</CardBody>
|
||||
</Card>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
@@ -39,11 +39,11 @@ export const Card = forwardRef<HTMLDivElement, CardProps>((props, ref) => {
|
||||
CardDefinition,
|
||||
props,
|
||||
);
|
||||
const { classes, surfaceChildren } = ownProps;
|
||||
const { classes, bgChildren } = ownProps;
|
||||
|
||||
return (
|
||||
<div ref={ref} className={classes.root} {...dataAttributes} {...restProps}>
|
||||
{surfaceChildren}
|
||||
{bgChildren}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -32,9 +32,9 @@ export const CardDefinition = defineComponent<CardOwnProps>()({
|
||||
classNames: {
|
||||
root: 'bui-Card',
|
||||
},
|
||||
surface: 'container',
|
||||
bg: 'container',
|
||||
propDefs: {
|
||||
surface: { dataAttribute: true, default: '1' },
|
||||
bg: { dataAttribute: true },
|
||||
children: {},
|
||||
className: {},
|
||||
},
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
import type { Responsive, Surface } from '../../types';
|
||||
import type { Responsive, Bg } from '../../types';
|
||||
|
||||
/** @public */
|
||||
export type CardOwnProps = {
|
||||
surface?: Responsive<Surface>;
|
||||
bg?: Responsive<Bg>;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
@@ -24,31 +24,31 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.bui-Flex[data-surface='0'] {
|
||||
background-color: var(--bui-bg-neutral-0);
|
||||
}
|
||||
|
||||
.bui-Flex[data-surface='1'] {
|
||||
.bui-Flex[data-bg='neutral-1'] {
|
||||
background-color: var(--bui-bg-neutral-1);
|
||||
}
|
||||
|
||||
.bui-Flex[data-surface='2'] {
|
||||
.bui-Flex[data-bg='neutral-2'] {
|
||||
background-color: var(--bui-bg-neutral-2);
|
||||
}
|
||||
|
||||
.bui-Flex[data-surface='3'] {
|
||||
.bui-Flex[data-bg='neutral-3'] {
|
||||
background-color: var(--bui-bg-neutral-3);
|
||||
}
|
||||
|
||||
.bui-Flex[data-surface='danger'] {
|
||||
.bui-Flex[data-bg='neutral-4'] {
|
||||
background-color: var(--bui-bg-neutral-4);
|
||||
}
|
||||
|
||||
.bui-Flex[data-bg='danger'] {
|
||||
background-color: var(--bui-bg-danger);
|
||||
}
|
||||
|
||||
.bui-Flex[data-surface='warning'] {
|
||||
.bui-Flex[data-bg='warning'] {
|
||||
background-color: var(--bui-bg-warning);
|
||||
}
|
||||
|
||||
.bui-Flex[data-surface='success'] {
|
||||
.bui-Flex[data-bg='success'] {
|
||||
background-color: var(--bui-bg-success);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ export const WithTextTruncate = meta.story({
|
||||
),
|
||||
});
|
||||
|
||||
export const Surfaces = meta.story({
|
||||
export const Backgrounds = meta.story({
|
||||
args: {
|
||||
px: '6',
|
||||
py: '4',
|
||||
@@ -252,52 +252,52 @@ export const Surfaces = meta.story({
|
||||
render: args => (
|
||||
<Flex align="center" style={{ flexWrap: 'wrap' }}>
|
||||
<Flex {...args}>Default</Flex>
|
||||
<Flex surface="0" {...args}>
|
||||
Surface 0
|
||||
<Flex bg="neutral-1" {...args}>
|
||||
Neutral 1
|
||||
</Flex>
|
||||
<Flex surface="1" {...args}>
|
||||
Surface 1
|
||||
<Flex bg="neutral-2" {...args}>
|
||||
Neutral 2
|
||||
</Flex>
|
||||
<Flex surface="2" {...args}>
|
||||
Surface 2
|
||||
<Flex bg="neutral-3" {...args}>
|
||||
Neutral 3
|
||||
</Flex>
|
||||
<Flex surface="3" {...args}>
|
||||
Surface 3
|
||||
<Flex bg="neutral-4" {...args}>
|
||||
Neutral 4
|
||||
</Flex>
|
||||
<Flex surface={{ initial: '0', sm: '1' }} {...args}>
|
||||
Responsive Surface
|
||||
<Flex bg={{ initial: 'neutral-1', sm: 'neutral-2' }} {...args}>
|
||||
Responsive Bg
|
||||
</Flex>
|
||||
<Flex surface="danger" {...args}>
|
||||
Surface Danger
|
||||
<Flex bg="danger" {...args}>
|
||||
Danger
|
||||
</Flex>
|
||||
<Flex surface="warning" {...args}>
|
||||
Surface Warning
|
||||
<Flex bg="warning" {...args}>
|
||||
Warning
|
||||
</Flex>
|
||||
<Flex surface="success" {...args}>
|
||||
Surface Success
|
||||
<Flex bg="success" {...args}>
|
||||
Success
|
||||
</Flex>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const SurfacesAutoIncrement = meta.story({
|
||||
export const BgAutoIncrement = meta.story({
|
||||
args: { px: '6', py: '4', gap: '4' },
|
||||
render: args => (
|
||||
<Flex direction="column">
|
||||
<div style={{ maxWidth: '600px', marginBottom: '16px' }}>
|
||||
Using surface="auto" automatically increments from the parent surface.
|
||||
This allows components to be reusable without hardcoding surface levels.
|
||||
Nested Flex components automatically increment their neutral background
|
||||
level. No explicit bg prop is needed on inner Flex components.
|
||||
</div>
|
||||
<Flex {...args} surface="0" direction="column">
|
||||
<div>Surface 0 (explicit)</div>
|
||||
<Flex {...args} surface="auto" direction="column">
|
||||
<div>Surface auto (becomes 1)</div>
|
||||
<Flex {...args} surface="auto" direction="column">
|
||||
<div>Surface auto (becomes 2)</div>
|
||||
<Flex {...args} surface="auto" direction="column">
|
||||
<div>Surface auto (becomes 3)</div>
|
||||
<Flex {...args} surface="auto" direction="column">
|
||||
<div>Surface auto (stays 3 - capped)</div>
|
||||
<Flex {...args} bg="neutral-1" direction="column">
|
||||
<div>Neutral 1 (explicit)</div>
|
||||
<Flex {...args} direction="column">
|
||||
<div>Auto (becomes neutral-2)</div>
|
||||
<Flex {...args} direction="column">
|
||||
<div>Auto (becomes neutral-3)</div>
|
||||
<Flex {...args} direction="column">
|
||||
<div>Auto (becomes neutral-4)</div>
|
||||
<Flex {...args} direction="column">
|
||||
<div>Auto (stays neutral-4 - capped)</div>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -20,24 +20,21 @@ import clsx from 'clsx';
|
||||
import { useStyles } from '../../hooks/useStyles';
|
||||
import { FlexDefinition } from './definition';
|
||||
import styles from './Flex.module.css';
|
||||
import { SurfaceProvider, useSurface } from '../../hooks/useSurface';
|
||||
import { BgProvider, useBg } from '../../hooks/useBg';
|
||||
|
||||
/** @public */
|
||||
export const Flex = forwardRef<HTMLDivElement, FlexProps>((props, ref) => {
|
||||
// Resolve the surface this Flex creates for its children
|
||||
// Using 'surface' parameter = container behavior (auto increments)
|
||||
const { surface: resolvedSurface } = useSurface({
|
||||
surface: props.surface,
|
||||
});
|
||||
// Resolve the bg this Flex creates for its children
|
||||
const { bg: resolvedBg } = useBg({ bg: props.bg });
|
||||
|
||||
const { classNames, dataAttributes, utilityClasses, style, cleanedProps } =
|
||||
useStyles(FlexDefinition, {
|
||||
gap: '4',
|
||||
...props,
|
||||
surface: resolvedSurface, // Use resolved surface for data attribute
|
||||
bg: resolvedBg, // Use resolved bg for data attribute
|
||||
});
|
||||
|
||||
const { className, surface, ...rest } = cleanedProps;
|
||||
const { className, bg, ...rest } = cleanedProps;
|
||||
|
||||
const content = (
|
||||
<div
|
||||
@@ -54,8 +51,8 @@ export const Flex = forwardRef<HTMLDivElement, FlexProps>((props, ref) => {
|
||||
/>
|
||||
);
|
||||
|
||||
return resolvedSurface ? (
|
||||
<SurfaceProvider surface={resolvedSurface}>{content}</SurfaceProvider>
|
||||
return resolvedBg ? (
|
||||
<BgProvider bg={resolvedBg}>{content}</BgProvider>
|
||||
) : (
|
||||
content
|
||||
);
|
||||
|
||||
@@ -45,6 +45,14 @@ export const FlexDefinition = {
|
||||
'direction',
|
||||
],
|
||||
dataAttributes: {
|
||||
surface: ['0', '1', '2', '3', 'danger', 'warning', 'success'] as const,
|
||||
bg: [
|
||||
'neutral-1',
|
||||
'neutral-2',
|
||||
'neutral-3',
|
||||
'neutral-4',
|
||||
'danger',
|
||||
'warning',
|
||||
'success',
|
||||
] as const,
|
||||
},
|
||||
} as const satisfies ComponentDefinition;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Responsive, Space, SpaceProps, Surface } from '../../types';
|
||||
import type { Responsive, Space, SpaceProps, Bg } from '../../types';
|
||||
|
||||
/** @public */
|
||||
export interface FlexProps extends SpaceProps {
|
||||
@@ -25,5 +25,5 @@ export interface FlexProps extends SpaceProps {
|
||||
direction?: Responsive<'row' | 'column' | 'row-reverse' | 'column-reverse'>;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
surface?: Responsive<Surface>;
|
||||
bg?: Responsive<Bg>;
|
||||
}
|
||||
|
||||
@@ -21,38 +21,38 @@
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.bui-Grid[data-surface='0'],
|
||||
.bui-GridItem[data-surface='0'] {
|
||||
background-color: var(--bui-bg-neutral-0);
|
||||
}
|
||||
|
||||
.bui-Grid[data-surface='1'],
|
||||
.bui-GridItem[data-surface='1'] {
|
||||
.bui-Grid[data-bg='neutral-1'],
|
||||
.bui-GridItem[data-bg='neutral-1'] {
|
||||
background-color: var(--bui-bg-neutral-1);
|
||||
}
|
||||
|
||||
.bui-Grid[data-surface='2'],
|
||||
.bui-GridItem[data-surface='2'] {
|
||||
.bui-Grid[data-bg='neutral-2'],
|
||||
.bui-GridItem[data-bg='neutral-2'] {
|
||||
background-color: var(--bui-bg-neutral-2);
|
||||
}
|
||||
|
||||
.bui-Grid[data-surface='3'],
|
||||
.bui-GridItem[data-surface='3'] {
|
||||
.bui-Grid[data-bg='neutral-3'],
|
||||
.bui-GridItem[data-bg='neutral-3'] {
|
||||
background-color: var(--bui-bg-neutral-3);
|
||||
}
|
||||
|
||||
.bui-Grid[data-surface='danger'],
|
||||
.bui-GridItem[data-surface='danger'] {
|
||||
.bui-Grid[data-bg='neutral-4'],
|
||||
.bui-GridItem[data-bg='neutral-4'] {
|
||||
background-color: var(--bui-bg-neutral-4);
|
||||
}
|
||||
|
||||
.bui-Grid[data-bg='danger'],
|
||||
.bui-GridItem[data-bg='danger'] {
|
||||
background-color: var(--bui-bg-danger);
|
||||
}
|
||||
|
||||
.bui-Grid[data-surface='warning'],
|
||||
.bui-GridItem[data-surface='warning'] {
|
||||
.bui-Grid[data-bg='warning'],
|
||||
.bui-GridItem[data-bg='warning'] {
|
||||
background-color: var(--bui-bg-warning);
|
||||
}
|
||||
|
||||
.bui-Grid[data-surface='success'],
|
||||
.bui-GridItem[data-surface='success'] {
|
||||
.bui-Grid[data-bg='success'],
|
||||
.bui-GridItem[data-bg='success'] {
|
||||
background-color: var(--bui-bg-success);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,78 +106,78 @@ export const RowAndColumns = meta.story({
|
||||
),
|
||||
});
|
||||
|
||||
export const Surfaces = meta.story({
|
||||
export const Backgrounds = meta.story({
|
||||
args: { px: '6', py: '4' },
|
||||
render: args => (
|
||||
<Flex direction="column">
|
||||
<Flex style={{ flexWrap: 'wrap' }}>
|
||||
<Grid.Root {...args} surface="0">
|
||||
Surface 0
|
||||
<Grid.Root {...args} bg="neutral-1">
|
||||
Neutral 1
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args} surface="1">
|
||||
Surface 1
|
||||
<Grid.Root {...args} bg="neutral-2">
|
||||
Neutral 2
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args} surface="2">
|
||||
Surface 2
|
||||
<Grid.Root {...args} bg="neutral-3">
|
||||
Neutral 3
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args} surface="3">
|
||||
Surface 3
|
||||
<Grid.Root {...args} bg="neutral-4">
|
||||
Neutral 4
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args} surface={{ initial: '0', sm: '1' }}>
|
||||
Responsive Surface
|
||||
<Grid.Root {...args} bg={{ initial: 'neutral-1', sm: 'neutral-2' }}>
|
||||
Responsive Bg
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args} surface="danger">
|
||||
Surface Danger
|
||||
<Grid.Root {...args} bg="danger">
|
||||
Danger
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args} surface="warning">
|
||||
Surface Warning
|
||||
<Grid.Root {...args} bg="warning">
|
||||
Warning
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args} surface="success">
|
||||
Surface Success
|
||||
<Grid.Root {...args} bg="success">
|
||||
Success
|
||||
</Grid.Root>
|
||||
</Flex>
|
||||
<Flex style={{ flexWrap: 'wrap' }}>
|
||||
<Grid.Root {...args}>
|
||||
<Grid.Item surface="0" style={{ padding: '4px' }}>
|
||||
Surface 0
|
||||
<Grid.Item bg="neutral-1" style={{ padding: '4px' }}>
|
||||
Neutral 1
|
||||
</Grid.Item>
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args}>
|
||||
<Grid.Item surface="1" style={{ padding: '4px' }}>
|
||||
Surface 1
|
||||
<Grid.Item bg="neutral-2" style={{ padding: '4px' }}>
|
||||
Neutral 2
|
||||
</Grid.Item>
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args}>
|
||||
<Grid.Item surface="2" style={{ padding: '4px' }}>
|
||||
Surface 2
|
||||
<Grid.Item bg="neutral-3" style={{ padding: '4px' }}>
|
||||
Neutral 3
|
||||
</Grid.Item>
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args}>
|
||||
<Grid.Item surface="3" style={{ padding: '4px' }}>
|
||||
Surface 3
|
||||
<Grid.Item bg="neutral-4" style={{ padding: '4px' }}>
|
||||
Neutral 4
|
||||
</Grid.Item>
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args}>
|
||||
<Grid.Item
|
||||
surface={{ initial: '0', sm: '1' }}
|
||||
bg={{ initial: 'neutral-1', sm: 'neutral-2' }}
|
||||
style={{ padding: '4px' }}
|
||||
>
|
||||
Responsive Surface
|
||||
Responsive Bg
|
||||
</Grid.Item>
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args}>
|
||||
<Grid.Item surface="danger" style={{ padding: '4px' }}>
|
||||
Surface Danger
|
||||
<Grid.Item bg="danger" style={{ padding: '4px' }}>
|
||||
Danger
|
||||
</Grid.Item>
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args}>
|
||||
<Grid.Item surface="warning" style={{ padding: '4px' }}>
|
||||
Surface Warning
|
||||
<Grid.Item bg="warning" style={{ padding: '4px' }}>
|
||||
Warning
|
||||
</Grid.Item>
|
||||
</Grid.Root>
|
||||
<Grid.Root {...args}>
|
||||
<Grid.Item surface="success" style={{ padding: '4px' }}>
|
||||
Surface Success
|
||||
<Grid.Item bg="success" style={{ padding: '4px' }}>
|
||||
Success
|
||||
</Grid.Item>
|
||||
</Grid.Root>
|
||||
</Flex>
|
||||
@@ -185,23 +185,20 @@ export const Surfaces = meta.story({
|
||||
),
|
||||
});
|
||||
|
||||
export const SurfacesAutoIncrement = meta.story({
|
||||
export const BgAutoIncrement = meta.story({
|
||||
args: { px: '6', py: '4', columns: '2', gap: '4' },
|
||||
render: args => (
|
||||
<Flex direction="column">
|
||||
<div style={{ maxWidth: '600px', marginBottom: '16px' }}>
|
||||
Using surface="auto" automatically increments from the parent surface.
|
||||
Each Grid.Item with auto will be one level above its Grid.Root parent.
|
||||
Nested Grid components automatically increment their neutral background.
|
||||
Each nested Grid.Item inherits and increments from its parent's bg.
|
||||
</div>
|
||||
<Grid.Root {...args} surface="0">
|
||||
<Grid.Item>Surface 0 (Grid.Root)</Grid.Item>
|
||||
<Grid.Item surface="auto">Surface auto (becomes 1)</Grid.Item>
|
||||
<Grid.Root {...args} bg="neutral-1">
|
||||
<Grid.Item>Neutral 1 (Grid.Root)</Grid.Item>
|
||||
<Grid.Item>
|
||||
<Grid.Root {...args} surface="auto">
|
||||
<Grid.Item>Nested: Surface auto (becomes 1)</Grid.Item>
|
||||
<Grid.Item surface="auto">
|
||||
Nested: Surface auto (becomes 2)
|
||||
</Grid.Item>
|
||||
<Grid.Root {...args}>
|
||||
<Grid.Item>Nested: Auto (becomes neutral-2)</Grid.Item>
|
||||
<Grid.Item>Nested: Auto (becomes neutral-2)</Grid.Item>
|
||||
</Grid.Root>
|
||||
</Grid.Item>
|
||||
</Grid.Root>
|
||||
|
||||
@@ -20,24 +20,21 @@ import type { GridItemProps, GridProps } from './types';
|
||||
import { useStyles } from '../../hooks/useStyles';
|
||||
import { GridDefinition, GridItemDefinition } from './definition';
|
||||
import styles from './Grid.module.css';
|
||||
import { SurfaceProvider, useSurface } from '../../hooks/useSurface';
|
||||
import { BgProvider, useBg } from '../../hooks/useBg';
|
||||
|
||||
const GridRoot = forwardRef<HTMLDivElement, GridProps>((props, ref) => {
|
||||
// Resolve the surface this Grid creates for its children
|
||||
// Using 'surface' parameter = container behavior (auto increments)
|
||||
const { surface: resolvedSurface } = useSurface({
|
||||
surface: props.surface,
|
||||
});
|
||||
// Resolve the bg this Grid creates for its children
|
||||
const { bg: resolvedBg } = useBg({ bg: props.bg });
|
||||
|
||||
const { classNames, dataAttributes, utilityClasses, style, cleanedProps } =
|
||||
useStyles(GridDefinition, {
|
||||
columns: 'auto',
|
||||
gap: '4',
|
||||
...props,
|
||||
surface: resolvedSurface, // Use resolved surface for data attribute
|
||||
bg: resolvedBg, // Use resolved bg for data attribute
|
||||
});
|
||||
|
||||
const { className, surface, ...rest } = cleanedProps;
|
||||
const { className, bg, ...rest } = cleanedProps;
|
||||
|
||||
const content = (
|
||||
<div
|
||||
@@ -54,27 +51,24 @@ const GridRoot = forwardRef<HTMLDivElement, GridProps>((props, ref) => {
|
||||
/>
|
||||
);
|
||||
|
||||
return resolvedSurface ? (
|
||||
<SurfaceProvider surface={resolvedSurface}>{content}</SurfaceProvider>
|
||||
return resolvedBg ? (
|
||||
<BgProvider bg={resolvedBg}>{content}</BgProvider>
|
||||
) : (
|
||||
content
|
||||
);
|
||||
});
|
||||
|
||||
const GridItem = forwardRef<HTMLDivElement, GridItemProps>((props, ref) => {
|
||||
// Resolve the surface this GridItem creates for its children
|
||||
// Using 'surface' parameter = container behavior (auto increments)
|
||||
const { surface: resolvedSurface } = useSurface({
|
||||
surface: props.surface,
|
||||
});
|
||||
// Resolve the bg this GridItem creates for its children
|
||||
const { bg: resolvedBg } = useBg({ bg: props.bg });
|
||||
|
||||
const { classNames, dataAttributes, utilityClasses, style, cleanedProps } =
|
||||
useStyles(GridItemDefinition, {
|
||||
...props,
|
||||
surface: resolvedSurface, // Use resolved surface for data attribute
|
||||
bg: resolvedBg, // Use resolved bg for data attribute
|
||||
});
|
||||
|
||||
const { className, surface, ...rest } = cleanedProps;
|
||||
const { className, bg, ...rest } = cleanedProps;
|
||||
|
||||
const content = (
|
||||
<div
|
||||
@@ -91,8 +85,8 @@ const GridItem = forwardRef<HTMLDivElement, GridItemProps>((props, ref) => {
|
||||
/>
|
||||
);
|
||||
|
||||
return resolvedSurface ? (
|
||||
<SurfaceProvider surface={resolvedSurface}>{content}</SurfaceProvider>
|
||||
return resolvedBg ? (
|
||||
<BgProvider bg={resolvedBg}>{content}</BgProvider>
|
||||
) : (
|
||||
content
|
||||
);
|
||||
|
||||
@@ -43,7 +43,15 @@ export const GridDefinition = {
|
||||
'py',
|
||||
],
|
||||
dataAttributes: {
|
||||
surface: ['0', '1', '2', '3', 'danger', 'warning', 'success'] as const,
|
||||
bg: [
|
||||
'neutral-1',
|
||||
'neutral-2',
|
||||
'neutral-3',
|
||||
'neutral-4',
|
||||
'danger',
|
||||
'warning',
|
||||
'success',
|
||||
] as const,
|
||||
},
|
||||
} as const satisfies ComponentDefinition;
|
||||
|
||||
@@ -57,6 +65,14 @@ export const GridItemDefinition = {
|
||||
},
|
||||
utilityProps: ['colSpan', 'colEnd', 'colStart', 'rowSpan'],
|
||||
dataAttributes: {
|
||||
surface: ['0', '1', '2', '3', 'danger', 'warning', 'success'] as const,
|
||||
bg: [
|
||||
'neutral-1',
|
||||
'neutral-2',
|
||||
'neutral-3',
|
||||
'neutral-4',
|
||||
'danger',
|
||||
'warning',
|
||||
'success',
|
||||
] as const,
|
||||
},
|
||||
} as const satisfies ComponentDefinition;
|
||||
|
||||
@@ -14,13 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type {
|
||||
Space,
|
||||
SpaceProps,
|
||||
Responsive,
|
||||
Columns,
|
||||
Surface,
|
||||
} from '../../types';
|
||||
import type { Space, SpaceProps, Responsive, Columns, Bg } from '../../types';
|
||||
|
||||
/** @public */
|
||||
export interface GridProps extends SpaceProps {
|
||||
@@ -29,7 +23,7 @@ export interface GridProps extends SpaceProps {
|
||||
columns?: Responsive<Columns>;
|
||||
gap?: Responsive<Space>;
|
||||
style?: React.CSSProperties;
|
||||
surface?: Responsive<Surface>;
|
||||
bg?: Responsive<Bg>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@@ -41,5 +35,5 @@ export interface GridItemProps {
|
||||
colStart?: Responsive<Columns>;
|
||||
rowSpan?: Responsive<Columns>;
|
||||
style?: React.CSSProperties;
|
||||
surface?: Responsive<Surface>;
|
||||
bg?: Responsive<Bg>;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ export const Default = meta.story({
|
||||
},
|
||||
});
|
||||
|
||||
export const Surfaces = meta.story({
|
||||
export const Backgrounds = meta.story({
|
||||
args: {
|
||||
children: 'Toggle',
|
||||
},
|
||||
@@ -64,26 +64,26 @@ export const Surfaces = meta.story({
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 0</Text>
|
||||
<Flex align="center" surface="0" p="4">
|
||||
<Text>On Neutral 1</Text>
|
||||
<Flex align="center" bg="neutral-1" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 1</Text>
|
||||
<Flex align="center" surface="1" p="4">
|
||||
<Text>On Neutral 2</Text>
|
||||
<Flex align="center" bg="neutral-2" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 2</Text>
|
||||
<Flex align="center" surface="2" p="4">
|
||||
<Text>On Neutral 3</Text>
|
||||
<Flex align="center" bg="neutral-3" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 3</Text>
|
||||
<Flex align="center" surface="3" p="4">
|
||||
<Text>On Neutral 4</Text>
|
||||
<Flex align="center" bg="neutral-4" p="4">
|
||||
<ToggleButton>Toggle</ToggleButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -21,7 +21,7 @@ import type { ToggleButtonProps } from './types';
|
||||
import { useStyles } from '../../hooks/useStyles';
|
||||
import { ToggleButtonDefinition } from './definition';
|
||||
import styles from './ToggleButton.module.css';
|
||||
import { useSurface } from '../../hooks/useSurface';
|
||||
import { useBg } from '../../hooks/useBg';
|
||||
|
||||
/** @public */
|
||||
export const ToggleButton = forwardRef(
|
||||
@@ -34,17 +34,16 @@ export const ToggleButton = forwardRef(
|
||||
},
|
||||
);
|
||||
|
||||
const { children, className, iconStart, iconEnd, onSurface, ...rest } =
|
||||
cleanedProps;
|
||||
const { children, className, iconStart, iconEnd, ...rest } = cleanedProps;
|
||||
|
||||
const { surface } = useSurface({ onSurface });
|
||||
const { bg } = useBg({ leaf: true });
|
||||
|
||||
return (
|
||||
<AriaToggleButton
|
||||
className={clsx(classNames.root, styles[classNames.root], className)}
|
||||
ref={ref}
|
||||
{...dataAttributes}
|
||||
{...(typeof surface === 'string' ? { 'data-on-surface': surface } : {})}
|
||||
{...(typeof bg === 'string' ? { 'data-bg': bg } : {})}
|
||||
{...rest}
|
||||
>
|
||||
{renderProps => {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import type { Breakpoint } from '../..';
|
||||
import type { ReactElement } from 'react';
|
||||
import type { ToggleButtonProps as AriaToggleButtonProps } from 'react-aria-components';
|
||||
import type { Responsive, Surface } from '../../types';
|
||||
|
||||
/**
|
||||
* Properties for {@link ToggleButton}
|
||||
@@ -28,6 +27,4 @@ export interface ToggleButtonProps extends AriaToggleButtonProps {
|
||||
size?: 'small' | 'medium' | Partial<Record<Breakpoint, 'small' | 'medium'>>;
|
||||
iconStart?: ReactElement;
|
||||
iconEnd?: ReactElement;
|
||||
/** Surface the toggle button is placed on. Defaults to context surface if available */
|
||||
onSurface?: Responsive<Surface>;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export const MultipleSelection = meta.story({
|
||||
),
|
||||
});
|
||||
|
||||
export const Surfaces = meta.story({
|
||||
export const Backgrounds = meta.story({
|
||||
args: {
|
||||
selectionMode: 'single',
|
||||
defaultSelectedKeys: ['option1'],
|
||||
@@ -99,8 +99,8 @@ export const Surfaces = meta.story({
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 0</Text>
|
||||
<Flex align="center" surface="0" p="4" gap="4">
|
||||
<Text>On Neutral 1</Text>
|
||||
<Flex align="center" bg="neutral-1" p="4" gap="4">
|
||||
<ToggleButtonGroup
|
||||
selectionMode="single"
|
||||
defaultSelectedKeys={['option1']}
|
||||
@@ -112,8 +112,8 @@ export const Surfaces = meta.story({
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 1</Text>
|
||||
<Flex align="center" surface="1" p="4" gap="4">
|
||||
<Text>On Neutral 2</Text>
|
||||
<Flex align="center" bg="neutral-2" p="4" gap="4">
|
||||
<ToggleButtonGroup
|
||||
selectionMode="single"
|
||||
defaultSelectedKeys={['option1']}
|
||||
@@ -125,8 +125,8 @@ export const Surfaces = meta.story({
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 2</Text>
|
||||
<Flex align="center" surface="2" p="4" gap="4">
|
||||
<Text>On Neutral 3</Text>
|
||||
<Flex align="center" bg="neutral-3" p="4" gap="4">
|
||||
<ToggleButtonGroup
|
||||
selectionMode="single"
|
||||
defaultSelectedKeys={['option1']}
|
||||
@@ -138,8 +138,8 @@ export const Surfaces = meta.story({
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>On Surface 3</Text>
|
||||
<Flex align="center" surface="3" p="4" gap="4">
|
||||
<Text>On Neutral 4</Text>
|
||||
<Flex align="center" bg="neutral-4" p="4" gap="4">
|
||||
<ToggleButtonGroup
|
||||
selectionMode="single"
|
||||
defaultSelectedKeys={['option1']}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useContext, ReactNode } from 'react';
|
||||
import {
|
||||
createVersionedContext,
|
||||
createVersionedValueMap,
|
||||
} from '@backstage/version-bridge';
|
||||
import { Bg, Responsive } from '../types';
|
||||
|
||||
/** @public */
|
||||
export interface BgContextValue {
|
||||
bg: Responsive<Bg> | undefined;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface BgProviderProps {
|
||||
bg: Responsive<Bg>;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export type UseBgOptions =
|
||||
| {
|
||||
/**
|
||||
* Container mode: the explicit bg value from the component's prop.
|
||||
* If undefined, the container auto-increments from parent context.
|
||||
*/
|
||||
bg: Responsive<Bg> | undefined;
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* Leaf mode: automatically reads bg from context and increments by 1.
|
||||
* No prop is needed on the component.
|
||||
*/
|
||||
leaf: true;
|
||||
};
|
||||
|
||||
const BgContext = createVersionedContext<{
|
||||
1: BgContextValue;
|
||||
}>('bg-context');
|
||||
|
||||
/**
|
||||
* Increments a neutral bg level by one, capping at 'neutral-4'.
|
||||
* Intent backgrounds (danger, warning, success) pass through unchanged.
|
||||
*
|
||||
* @param bg - The current bg value
|
||||
* @returns The incremented bg value
|
||||
* @internal
|
||||
*/
|
||||
function incrementNeutralBg(bg: Bg | undefined): Bg | undefined {
|
||||
if (!bg) return undefined;
|
||||
if (bg === 'neutral-1') return 'neutral-2';
|
||||
if (bg === 'neutral-2') return 'neutral-3';
|
||||
if (bg === 'neutral-3') return 'neutral-4';
|
||||
if (bg === 'neutral-4') return 'neutral-4'; // capped
|
||||
// Intent values pass through unchanged
|
||||
return bg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the bg value for a container component.
|
||||
*
|
||||
* - If an explicit bg prop is provided, use that.
|
||||
* - If no bg prop is provided but there is a parent context, auto-increment from parent.
|
||||
* - If no bg prop and no context, return undefined (no bg).
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function resolveBgForContainer(
|
||||
contextBg: Responsive<Bg> | undefined,
|
||||
propBg: Responsive<Bg> | undefined,
|
||||
): Responsive<Bg> | undefined {
|
||||
// Explicit bg prop takes priority
|
||||
if (propBg !== undefined) {
|
||||
return propBg;
|
||||
}
|
||||
|
||||
// No explicit bg: auto-increment from context if available
|
||||
if (contextBg === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If context is a responsive object, we can't auto-increment
|
||||
if (typeof contextBg === 'object') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return incrementNeutralBg(contextBg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the bg value for a leaf component.
|
||||
*
|
||||
* Always auto-increments from parent context. If no context, returns undefined.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function resolveBgForLeaf(
|
||||
contextBg: Responsive<Bg> | undefined,
|
||||
): Responsive<Bg> | undefined {
|
||||
if (contextBg === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If context is a responsive object, we can't auto-increment
|
||||
if (typeof contextBg === 'object') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return incrementNeutralBg(contextBg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider component that establishes the bg context for child components.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const BgProvider = ({ bg, children }: BgProviderProps) => {
|
||||
return (
|
||||
<BgContext.Provider value={createVersionedValueMap({ 1: { bg } })}>
|
||||
{children}
|
||||
</BgContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to access and resolve the current bg context.
|
||||
*
|
||||
* Supports two modes:
|
||||
* - **Container mode** (`{ bg }`) - for components like Box that establish bg context.
|
||||
* If bg prop is provided, uses that value. If bg is undefined but parent context exists,
|
||||
* auto-increments from parent.
|
||||
* - **Leaf mode** (`{ leaf: true }`) - for components like Button that consume bg context.
|
||||
* Always auto-increments from parent context. No prop needed.
|
||||
*
|
||||
* @param options - Configuration for bg resolution
|
||||
* @public
|
||||
*/
|
||||
export const useBg = (options?: UseBgOptions): BgContextValue => {
|
||||
const value = useContext(BgContext)?.atVersion(1);
|
||||
const context = value ?? { bg: undefined };
|
||||
|
||||
if (!options) {
|
||||
return context;
|
||||
}
|
||||
|
||||
if ('leaf' in options) {
|
||||
return { bg: resolveBgForLeaf(context.bg) };
|
||||
}
|
||||
|
||||
return { bg: resolveBgForContainer(context.bg, options.bg) };
|
||||
};
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ComponentConfig, SurfacePropsConstraint } from './types';
|
||||
import type { ComponentConfig, BgPropsConstraint } from './types';
|
||||
|
||||
export function defineComponent<P extends Record<string, any>>() {
|
||||
return <
|
||||
const S extends Record<string, string>,
|
||||
const C extends ComponentConfig<P, S>,
|
||||
>(
|
||||
config: C & SurfacePropsConstraint<P, C['surface']>,
|
||||
config: C & BgPropsConstraint<P, C['bg']>,
|
||||
): C => config;
|
||||
}
|
||||
|
||||
@@ -21,4 +21,5 @@ export type {
|
||||
PropDefConfig,
|
||||
UseDefinitionOptions,
|
||||
UseDefinitionResult,
|
||||
BgPropsConstraint,
|
||||
} from './types';
|
||||
|
||||
@@ -36,25 +36,19 @@ export interface ComponentConfig<
|
||||
propDefs: { [K in keyof P]: PropDefConfig<P[K]> };
|
||||
// readonly for compatibility with const inference from factory
|
||||
utilityProps?: readonly UtilityPropKey[];
|
||||
surface?: 'container' | 'leaf';
|
||||
bg?: 'container' | 'leaf';
|
||||
}
|
||||
|
||||
/**
|
||||
* Type constraint that validates surface props are present in the props type.
|
||||
* - If surface is 'leaf', P must include 'onSurface'
|
||||
* - If surface is 'container', P must include 'surface'
|
||||
* Type constraint that validates bg props are present in the props type.
|
||||
* - If bg is 'container', P must include 'bg'
|
||||
* - If bg is 'leaf', no prop constraint (fully automatic)
|
||||
*/
|
||||
export type SurfacePropsConstraint<P, Surface> = Surface extends 'leaf'
|
||||
? 'onSurface' extends keyof P
|
||||
export type BgPropsConstraint<P, BgMode> = BgMode extends 'container'
|
||||
? 'bg' extends keyof P
|
||||
? {}
|
||||
: {
|
||||
__error: 'Leaf components must include onSurface in props type. Extend LeafProps.';
|
||||
}
|
||||
: Surface extends 'container'
|
||||
? 'surface' extends keyof P
|
||||
? {}
|
||||
: {
|
||||
__error: 'Container components must include surface in props type. Extend ContainerProps.';
|
||||
__error: 'Bg container components must include bg in props type.';
|
||||
}
|
||||
: {};
|
||||
|
||||
@@ -81,12 +75,11 @@ type ResolvedOwnProps<
|
||||
[K in keyof PropDefs & keyof P]: ResolvePropType<P[K], PropDefs[K]>;
|
||||
};
|
||||
|
||||
type ChildrenProps<Surface extends 'container' | 'leaf' | undefined> =
|
||||
Surface extends 'container'
|
||||
? { surfaceChildren: ReactNode; children?: never }
|
||||
: Surface extends 'leaf'
|
||||
? { children: ReactNode; surfaceChildren?: never }
|
||||
: { children: ReactNode };
|
||||
type ChildrenProps<
|
||||
BgMode extends 'container' | 'leaf' | undefined = undefined,
|
||||
> = BgMode extends 'container'
|
||||
? { bgChildren: ReactNode; children?: never }
|
||||
: { children: ReactNode; bgChildren?: never };
|
||||
|
||||
type DataAttributeKeys<PropDefs> = {
|
||||
[K in keyof PropDefs]: PropDefs[K] extends { dataAttribute: true }
|
||||
@@ -98,7 +91,7 @@ type DataAttributes<PropDefs> = {
|
||||
[K in DataAttributeKeys<PropDefs> as `data-${Lowercase<
|
||||
string & K
|
||||
>}`]?: string;
|
||||
} & { 'data-on-surface'?: string };
|
||||
} & { 'data-bg'?: string };
|
||||
|
||||
export type UtilityKeys<D extends ComponentConfig<any, any>> =
|
||||
D['utilityProps'] extends ReadonlyArray<infer K extends string> ? K : never;
|
||||
@@ -127,7 +120,7 @@ export interface UseDefinitionResult<
|
||||
ownProps: {
|
||||
classes: Record<keyof D['classNames'], string>;
|
||||
} & ResolvedOwnProps<P, D['propDefs']> &
|
||||
ChildrenProps<D['surface']>;
|
||||
ChildrenProps<D['bg']>;
|
||||
|
||||
// Rest props excludes both propDefs keys AND utility prop keys
|
||||
restProps: keyof Omit<P, keyof D['propDefs'] | UtilityKeys<D>> extends never
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { ReactNode } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useBreakpoint } from '../useBreakpoint';
|
||||
import { useSurface, SurfaceProvider, UseSurfaceOptions } from '../useSurface';
|
||||
import { useBg, BgProvider, UseBgOptions } from '../useBg';
|
||||
import { resolveResponsiveValue, processUtilityProps } from './helpers';
|
||||
import type {
|
||||
ComponentConfig,
|
||||
@@ -36,14 +36,14 @@ export function useDefinition<
|
||||
): UseDefinitionResult<D, P> {
|
||||
const { breakpoint } = useBreakpoint();
|
||||
|
||||
const surfaceOptions: UseSurfaceOptions | undefined =
|
||||
definition.surface === 'container'
|
||||
? { surface: props.surface }
|
||||
: definition.surface === 'leaf'
|
||||
? { onSurface: props.onSurface }
|
||||
const bgOptions: UseBgOptions | undefined =
|
||||
definition.bg === 'container'
|
||||
? { bg: props.bg }
|
||||
: definition.bg === 'leaf'
|
||||
? { leaf: true }
|
||||
: undefined;
|
||||
|
||||
const { surface: resolvedSurface } = useSurface(surfaceOptions);
|
||||
const { bg: resolvedBg } = useBg(bgOptions);
|
||||
|
||||
const ownPropKeys = new Set(Object.keys(definition.propDefs));
|
||||
const utilityPropKeys = new Set(definition.utilityProps ?? []);
|
||||
@@ -77,31 +77,26 @@ export function useDefinition<
|
||||
}
|
||||
}
|
||||
|
||||
// Override data-surface for container components with the resolved value
|
||||
// This ensures 'auto' is replaced with the actual computed surface level
|
||||
if (
|
||||
definition.surface === 'container' &&
|
||||
resolvedSurface !== undefined &&
|
||||
dataAttributes['data-surface'] !== undefined
|
||||
) {
|
||||
const surfaceValue =
|
||||
typeof resolvedSurface === 'object'
|
||||
? resolveResponsiveValue(resolvedSurface as any, breakpoint)
|
||||
: resolvedSurface;
|
||||
if (surfaceValue !== undefined) {
|
||||
dataAttributes['data-surface'] = String(surfaceValue);
|
||||
// Set data-bg for bg container components with the resolved value
|
||||
// This covers both explicit bg props and auto-incremented values
|
||||
if (definition.bg === 'container' && resolvedBg !== undefined) {
|
||||
const bgValue =
|
||||
typeof resolvedBg === 'object'
|
||||
? resolveResponsiveValue(resolvedBg as any, breakpoint)
|
||||
: resolvedBg;
|
||||
if (bgValue !== undefined) {
|
||||
dataAttributes['data-bg'] = String(bgValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Add data-on-surface for leaf components
|
||||
if (definition.surface === 'leaf' && resolvedSurface !== undefined) {
|
||||
// Handle responsive surface values - for data attributes, use the resolved string
|
||||
const surfaceValue =
|
||||
typeof resolvedSurface === 'object'
|
||||
? resolveResponsiveValue(resolvedSurface as any, breakpoint)
|
||||
: resolvedSurface;
|
||||
if (surfaceValue !== undefined) {
|
||||
dataAttributes['data-on-surface'] = String(surfaceValue);
|
||||
// Add data-bg for bg leaf components (auto-incremented from context)
|
||||
if (definition.bg === 'leaf' && resolvedBg !== undefined) {
|
||||
const bgValue =
|
||||
typeof resolvedBg === 'object'
|
||||
? resolveResponsiveValue(resolvedBg as any, breakpoint)
|
||||
: resolvedBg;
|
||||
if (bgValue !== undefined) {
|
||||
dataAttributes['data-bg'] = String(bgValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,13 +120,11 @@ export function useDefinition<
|
||||
}
|
||||
|
||||
let children: ReactNode | undefined;
|
||||
let surfaceChildren: ReactNode | undefined;
|
||||
let bgChildren: ReactNode | undefined;
|
||||
|
||||
if (definition.surface === 'container') {
|
||||
surfaceChildren = resolvedSurface ? (
|
||||
<SurfaceProvider surface={resolvedSurface}>
|
||||
{props.children}
|
||||
</SurfaceProvider>
|
||||
if (definition.bg === 'container') {
|
||||
bgChildren = resolvedBg ? (
|
||||
<BgProvider bg={resolvedBg}>{props.children}</BgProvider>
|
||||
) : (
|
||||
props.children
|
||||
);
|
||||
@@ -143,9 +136,7 @@ export function useDefinition<
|
||||
ownProps: {
|
||||
classes,
|
||||
...ownPropsResolved,
|
||||
...(definition.surface === 'container'
|
||||
? { surfaceChildren }
|
||||
: { children }),
|
||||
...(definition.bg === 'container' ? { bgChildren } : { children }),
|
||||
},
|
||||
restProps,
|
||||
dataAttributes,
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useContext, ReactNode } from 'react';
|
||||
import {
|
||||
createVersionedContext,
|
||||
createVersionedValueMap,
|
||||
} from '@backstage/version-bridge';
|
||||
import { Surface, Responsive } from '../types';
|
||||
|
||||
/** @public */
|
||||
export interface SurfaceContextValue {
|
||||
surface: Responsive<Surface> | undefined;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface SurfaceProviderProps {
|
||||
surface: Responsive<Surface>;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface UseSurfaceOptions {
|
||||
/**
|
||||
* The surface value this component CREATES for its children (container behavior).
|
||||
* When 'auto', increments from parent surface.
|
||||
*
|
||||
* Use this for components like Box, Flex, Grid that establish surface context.
|
||||
*/
|
||||
surface?: Responsive<Surface>;
|
||||
/**
|
||||
* The surface value this component is ON for styling (leaf behavior).
|
||||
* When 'auto', inherits from current surface.
|
||||
*
|
||||
* Use this for leaf components like Button that consume surface for styling.
|
||||
*/
|
||||
onSurface?: Responsive<Surface>;
|
||||
}
|
||||
|
||||
const SurfaceContext = createVersionedContext<{
|
||||
1: SurfaceContextValue;
|
||||
}>('surface-context');
|
||||
|
||||
/**
|
||||
* Increments a surface level by one, capping at '3'.
|
||||
* Intent surfaces (danger, warning, success) remain unchanged.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function incrementSurface(surface: Surface | undefined): Surface {
|
||||
if (!surface) return '0'; // no context = root level
|
||||
if (surface === '0') return '1';
|
||||
if (surface === '1') return '2';
|
||||
if (surface === '2' || surface === '3') return '3'; // cap at max
|
||||
// Intent surfaces remain unchanged
|
||||
if (surface === 'danger') return 'danger';
|
||||
if (surface === 'warning') return 'warning';
|
||||
if (surface === 'success') return 'success';
|
||||
// 'auto' should not appear here, but handle it defensively
|
||||
if (surface === 'auto') return '1';
|
||||
return surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a surface value for containers (SurfaceProvider).
|
||||
* When 'auto' is used, increments from the parent surface.
|
||||
* For responsive surfaces (objects), returns them as-is without resolution.
|
||||
*
|
||||
* @param contextSurface - The surface from context
|
||||
* @param requestedSurface - The requested surface value (may be 'auto')
|
||||
* @returns The resolved surface value
|
||||
* @internal
|
||||
*/
|
||||
export function resolveSurfaceForProvider(
|
||||
contextSurface: Responsive<Surface> | undefined,
|
||||
requestedSurface: Responsive<Surface> | undefined,
|
||||
): Responsive<Surface> | undefined {
|
||||
if (!requestedSurface) {
|
||||
return contextSurface;
|
||||
}
|
||||
|
||||
// If requestedSurface is a responsive object (breakpoint-based), return as-is
|
||||
if (typeof requestedSurface === 'object') {
|
||||
return requestedSurface;
|
||||
}
|
||||
|
||||
// If contextSurface is a responsive object, we can't auto-increment from it
|
||||
// Return the requested surface as-is or default to '0' for auto
|
||||
if (typeof contextSurface === 'object') {
|
||||
if (requestedSurface === 'auto') {
|
||||
return '0'; // fallback to root when context is responsive
|
||||
}
|
||||
return requestedSurface;
|
||||
}
|
||||
|
||||
// For containers, 'auto' means increment to create a new elevated context
|
||||
if (requestedSurface === 'auto') {
|
||||
return incrementSurface(contextSurface);
|
||||
}
|
||||
|
||||
return requestedSurface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a surface value for leaf components (useSurface hook).
|
||||
* When 'auto' is used, inherits the current surface (doesn't increment).
|
||||
* For responsive surfaces (objects), returns them as-is without resolution.
|
||||
*
|
||||
* @param contextSurface - The surface from context
|
||||
* @param requestedSurface - The requested surface value (may be 'auto')
|
||||
* @returns The resolved surface value
|
||||
* @internal
|
||||
*/
|
||||
function resolveSurfaceForConsumer(
|
||||
contextSurface: Responsive<Surface> | undefined,
|
||||
requestedSurface: Responsive<Surface> | undefined,
|
||||
): Responsive<Surface> | undefined {
|
||||
if (!requestedSurface) {
|
||||
return contextSurface;
|
||||
}
|
||||
|
||||
// If requestedSurface is a responsive object (breakpoint-based), return as-is
|
||||
if (typeof requestedSurface === 'object') {
|
||||
return requestedSurface;
|
||||
}
|
||||
|
||||
// For leaf components, 'auto' means inherit the current surface
|
||||
if (requestedSurface === 'auto') {
|
||||
// If context is responsive, fallback to '0'
|
||||
if (typeof contextSurface === 'object') {
|
||||
return '0';
|
||||
}
|
||||
return contextSurface;
|
||||
}
|
||||
|
||||
return requestedSurface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider component that establishes the surface context for child components.
|
||||
* This allows components to adapt their styling based on their background surface.
|
||||
*
|
||||
* Note: The surface value should already be resolved before passing to this provider.
|
||||
* Container components should use useSurface with the surface parameter.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const SurfaceProvider = ({
|
||||
surface,
|
||||
children,
|
||||
}: SurfaceProviderProps) => {
|
||||
return (
|
||||
<SurfaceContext.Provider
|
||||
value={createVersionedValueMap({ 1: { surface } })}
|
||||
>
|
||||
{children}
|
||||
</SurfaceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to access the current surface context.
|
||||
* Returns the current surface level, or undefined if no provider is present.
|
||||
*
|
||||
* The parameter name determines the behavior:
|
||||
* - `surface`: Container behavior - 'auto' increments from parent
|
||||
* - `onSurface`: Leaf behavior - 'auto' inherits from parent
|
||||
*
|
||||
* @param options - Optional configuration for surface resolution
|
||||
* @internal
|
||||
*/
|
||||
export const useSurface = (
|
||||
options?: UseSurfaceOptions,
|
||||
): SurfaceContextValue => {
|
||||
const value = useContext(SurfaceContext)?.atVersion(1);
|
||||
const context = value ?? { surface: undefined };
|
||||
|
||||
// Infer behavior from which parameter is provided
|
||||
// 'surface' = provider behavior (increment)
|
||||
// 'onSurface' = consumer behavior (inherit)
|
||||
const isProvider = options?.surface !== undefined;
|
||||
const requestedSurface = options?.surface ?? options?.onSurface;
|
||||
|
||||
const resolvedSurface = isProvider
|
||||
? resolveSurfaceForProvider(context.surface, requestedSurface)
|
||||
: resolveSurfaceForConsumer(context.surface, requestedSurface);
|
||||
|
||||
return {
|
||||
surface: resolvedSurface,
|
||||
};
|
||||
};
|
||||
@@ -64,3 +64,9 @@ export * from './types';
|
||||
|
||||
// Hooks
|
||||
export { useBreakpoint } from './hooks/useBreakpoint';
|
||||
export { useBg, BgProvider } from './hooks/useBg';
|
||||
export type {
|
||||
BgContextValue,
|
||||
BgProviderProps,
|
||||
UseBgOptions,
|
||||
} from './hooks/useBg';
|
||||
|
||||
@@ -182,29 +182,18 @@ export interface ComponentDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Surface type
|
||||
* Background type for the neutral bg system.
|
||||
*
|
||||
* Supports absolute levels ('0'-'3'), intent surfaces ('danger', 'warning', 'success'),
|
||||
* and 'auto' which increments from the parent surface context.
|
||||
* Supports neutral levels ('neutral-1' through 'neutral-4') and
|
||||
* intent backgrounds ('danger', 'warning', 'success').
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type Surface =
|
||||
| '0'
|
||||
| '1'
|
||||
| '2'
|
||||
| '3'
|
||||
export type Bg =
|
||||
| 'neutral-1'
|
||||
| 'neutral-2'
|
||||
| 'neutral-3'
|
||||
| 'neutral-4'
|
||||
| 'danger'
|
||||
| 'warning'
|
||||
| 'success'
|
||||
| 'auto';
|
||||
|
||||
/** @public */
|
||||
export interface LeafSurfaceProps {
|
||||
onSurface?: Responsive<Surface>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ContainerSurfaceProps {
|
||||
surface?: Responsive<Surface>;
|
||||
}
|
||||
| 'success';
|
||||
|
||||
Reference in New Issue
Block a user