Create variants for workflow visualizer nodes (#10006)

Closes https://github.com/twentyhq/core-team-issues/issues/332

- Create the success and failed variants
- Introduce the first responsive color
- Creating stories for the new variants

These components are not yet in use in the source code. If you want to
see them, launch Storybook.

| Success | Failure |
|--------|--------|
| ![CleanShot 2025-02-04 at 16 24
43@2x](https://github.com/user-attachments/assets/0dd68a8f-3914-4b6e-b2d8-43108c2f5e8c)
| ![CleanShot 2025-02-04 at 16 24
59@2x](https://github.com/user-attachments/assets/e4e408d3-29fb-4fbc-a277-044aec9b0f4b)
|
| ![CleanShot 2025-02-04 at 16 24
54@2x](https://github.com/user-attachments/assets/d565ee47-1476-475d-adf6-dadfff9c6719)
| ![CleanShot 2025-02-04 at 16 25
05@2x](https://github.com/user-attachments/assets/9a0aabcc-84d1-41e2-a5a1-7c8cb05f963f)
|
This commit is contained in:
Baptiste Devessier
2025-02-04 18:38:38 +01:00
committed by GitHub
parent 5be22413c9
commit 125a0c3419
14 changed files with 284 additions and 102 deletions

View File

@ -4,14 +4,14 @@ import { NODE_HANDLE_WIDTH_PX } from '@/workflow/workflow-diagram/constants/Node
import { NODE_ICON_LEFT_MARGIN } from '@/workflow/workflow-diagram/constants/NodeIconLeftMargin'; import { NODE_ICON_LEFT_MARGIN } from '@/workflow/workflow-diagram/constants/NodeIconLeftMargin';
import { NODE_ICON_WIDTH } from '@/workflow/workflow-diagram/constants/NodeIconWidth'; import { NODE_ICON_WIDTH } from '@/workflow/workflow-diagram/constants/NodeIconWidth';
import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant';
import { css } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Handle, Position } from '@xyflow/react'; import { Handle, Position } from '@xyflow/react';
import React from 'react'; import React from 'react';
import { capitalize, isDefined } from 'twenty-shared'; import { capitalize, isDefined } from 'twenty-shared';
import { Label, OverflowingTextWithTooltip } from 'twenty-ui'; import { Label, OverflowingTextWithTooltip } from 'twenty-ui';
type Variant = 'placeholder';
const StyledStepNodeContainer = styled.div` const StyledStepNodeContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -19,52 +19,109 @@ const StyledStepNodeContainer = styled.div`
padding-block: ${({ theme }) => theme.spacing(3)}; padding-block: ${({ theme }) => theme.spacing(3)};
`; `;
const StyledStepNodeType = styled(Label)` const StyledStepNodeType = styled.div<{
background-color: ${({ theme }) => theme.background.tertiary}; nodeVariant: WorkflowDiagramNodeVariant;
border-radius: ${({ theme }) => theme.border.radius.sm} }>`
${({ theme }) => theme.border.radius.sm} 0 0; ${({ nodeVariant, theme }) => {
switch (nodeVariant) {
case 'success': {
return css`
background-color: ${theme.tag.background.turquoise};
color: ${theme.tag.text.turquoise};
`;
}
case 'failure': {
return css`
background-color: ${theme.tag.background.red};
color: ${theme.color.red};
`;
}
default: {
return css`
background-color: ${theme.background.tertiary};
`;
}
}
}}
align-self: flex-start;
border-radius: ${({ theme }) =>
`${theme.border.radius.sm} ${theme.border.radius.sm} 0 0`};
margin-left: ${({ theme }) => theme.spacing(2)}; margin-left: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)}; padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)};
align-self: flex-start;
.selectable.selected &, .selectable.selected &,
.selectable:focus &, .selectable:focus &,
.selectable:focus-visible & { .selectable:focus-visible & {
background-color: ${({ theme }) => theme.color.blue}; ${({ nodeVariant, theme }) => {
color: ${({ theme }) => theme.font.color.inverted}; switch (nodeVariant) {
case 'empty':
case 'default': {
return css`
background-color: ${theme.color.blue};
color: ${theme.font.color.inverted};
`;
}
}
}}
} }
`; `.withComponent(Label);
const StyledStepNodeInnerContainer = styled.div<{ variant?: Variant }>` const StyledStepNodeInnerContainer = styled.div<{
variant: WorkflowDiagramNodeVariant;
}>`
background-color: ${({ theme }) => theme.background.secondary}; background-color: ${({ theme }) => theme.background.secondary};
border: ${NODE_BORDER_WIDTH}px solid border-color: ${({ theme }) => theme.border.color.medium};
${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.md}; border-radius: ${({ theme }) => theme.border.radius.md};
border-style: solid;
border-width: ${NODE_BORDER_WIDTH}px;
box-shadow: ${({ variant, theme }) =>
variant === 'empty' ? 'none' : theme.boxShadow.strong};
display: flex; display: flex;
gap: ${({ theme }) => theme.spacing(2)}; gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2)}; padding: ${({ theme }) => theme.spacing(2)};
position: relative; position: relative;
box-shadow: ${({ variant, theme }) =>
variant === 'placeholder' ? 'none' : theme.boxShadow.strong};
.selectable.selected &, .selectable.selected &,
.selectable:focus &, .selectable:focus &,
.selectable:focus-visible & { .selectable:focus-visible & {
background-color: ${({ theme }) => theme.accent.quaternary}; ${({ theme, variant }) => {
border-color: ${({ theme }) => theme.color.blue}; switch (variant) {
case 'success': {
return css`
background-color: ${theme.adaptiveColors.turquoise1};
border-color: ${theme.adaptiveColors.turquoise4};
`;
}
case 'failure': {
return css`
background-color: ${theme.background.danger};
border-color: ${theme.color.red};
`;
}
default: {
return css`
background-color: ${theme.accent.quaternary};
border-color: ${theme.color.blue};
`;
}
}
}}
} }
`; `;
const StyledStepNodeLabel = styled.div<{ variant?: Variant }>` const StyledStepNodeLabel = styled.div<{
variant: WorkflowDiagramNodeVariant;
}>`
align-items: center; align-items: center;
display: flex; display: flex;
font-size: 13px; font-size: 13px;
font-weight: ${({ theme }) => theme.font.weight.medium}; font-weight: ${({ theme }) => theme.font.weight.medium};
column-gap: ${({ theme }) => theme.spacing(2)}; column-gap: ${({ theme }) => theme.spacing(2)};
color: ${({ variant, theme }) => color: ${({ variant, theme }) =>
variant === 'placeholder' variant === 'empty'
? theme.font.color.extraLight ? theme.font.color.extraLight
: theme.font.color.primary}; : theme.font.color.primary};
max-width: 200px; max-width: 200px;
@ -106,7 +163,7 @@ export const WorkflowDiagramBaseStepNode = ({
}: { }: {
nodeType: WorkflowDiagramStepNodeData['nodeType']; nodeType: WorkflowDiagramStepNodeData['nodeType'];
name: string; name: string;
variant?: Variant; variant: WorkflowDiagramNodeVariant;
Icon?: React.ReactNode; Icon?: React.ReactNode;
RightFloatingElement?: React.ReactNode; RightFloatingElement?: React.ReactNode;
}) => { }) => {
@ -116,7 +173,7 @@ export const WorkflowDiagramBaseStepNode = ({
<StyledTargetHandle type="target" position={Position.Top} /> <StyledTargetHandle type="target" position={Position.Top} />
) : null} ) : null}
<StyledStepNodeType variant="small"> <StyledStepNodeType variant="small" nodeVariant={variant}>
{capitalize(nodeType)} {capitalize(nodeType)}
</StyledStepNodeType> </StyledStepNodeType>

View File

@ -15,7 +15,7 @@ export const WorkflowDiagramEmptyTrigger = () => {
<WorkflowDiagramBaseStepNode <WorkflowDiagramBaseStepNode
name="Add a Trigger" name="Add a Trigger"
nodeType="trigger" nodeType="trigger"
variant="placeholder" variant="empty"
Icon={<StyledStepNodeLabelIconContainer />} Icon={<StyledStepNodeLabelIconContainer />}
/> />
); );

View File

@ -1,6 +1,7 @@
import { assertUnreachable } from '@/workflow/utils/assertUnreachable'; import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
import { WorkflowDiagramBaseStepNode } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode'; import { WorkflowDiagramBaseStepNode } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode';
import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant';
import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey'; import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
@ -17,9 +18,11 @@ const StyledStepNodeLabelIconContainer = styled.div`
export const WorkflowDiagramStepNodeBase = ({ export const WorkflowDiagramStepNodeBase = ({
data, data,
variant,
RightFloatingElement, RightFloatingElement,
}: { }: {
data: WorkflowDiagramStepNodeData; data: WorkflowDiagramStepNodeData;
variant: WorkflowDiagramNodeVariant;
RightFloatingElement?: React.ReactNode; RightFloatingElement?: React.ReactNode;
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
@ -93,6 +96,7 @@ export const WorkflowDiagramStepNodeBase = ({
return ( return (
<WorkflowDiagramBaseStepNode <WorkflowDiagramBaseStepNode
name={data.name} name={data.name}
variant={variant}
nodeType={data.nodeType} nodeType={data.nodeType}
Icon={renderStepIcon()} Icon={renderStepIcon()}
RightFloatingElement={RightFloatingElement} RightFloatingElement={RightFloatingElement}

View File

@ -27,6 +27,7 @@ export const WorkflowDiagramStepNodeEditable = ({
return ( return (
<WorkflowDiagramStepNodeEditableContent <WorkflowDiagramStepNodeEditableContent
data={data} data={data}
variant="default"
selected={selected ?? false} selected={selected ?? false}
onDelete={() => { onDelete={() => {
deleteStep(id); deleteStep(id);

View File

@ -1,19 +1,23 @@
import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase'; import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase';
import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant';
import { FloatingIconButton, IconTrash } from 'twenty-ui'; import { FloatingIconButton, IconTrash } from 'twenty-ui';
export const WorkflowDiagramStepNodeEditableContent = ({ export const WorkflowDiagramStepNodeEditableContent = ({
data, data,
selected, selected,
variant,
onDelete, onDelete,
}: { }: {
data: WorkflowDiagramStepNodeData; data: WorkflowDiagramStepNodeData;
variant: WorkflowDiagramNodeVariant;
selected: boolean; selected: boolean;
onDelete: () => void; onDelete: () => void;
}) => { }) => {
return ( return (
<WorkflowDiagramStepNodeBase <WorkflowDiagramStepNodeBase
data={data} data={data}
variant={variant}
RightFloatingElement={ RightFloatingElement={
selected ? ( selected ? (
<FloatingIconButton <FloatingIconButton

View File

@ -6,5 +6,5 @@ export const WorkflowDiagramStepNodeReadonly = ({
}: { }: {
data: WorkflowDiagramStepNodeData; data: WorkflowDiagramStepNodeData;
}) => { }) => {
return <WorkflowDiagramStepNodeBase data={data} />; return <WorkflowDiagramStepNodeBase variant="default" data={data} />;
}; };

View File

@ -2,9 +2,9 @@ import { Meta, StoryObj } from '@storybook/react';
import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { fn } from '@storybook/test'; import { fn } from '@storybook/test';
import { ReactFlowProvider } from '@xyflow/react';
import '@xyflow/react/dist/style.css'; import '@xyflow/react/dist/style.css';
import { CatalogDecorator, CatalogStory } from 'twenty-ui'; import { CatalogDecorator, CatalogStory } from 'twenty-ui';
import { ReactflowDecorator } from '~/testing/decorators/ReactflowDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks'; import { graphqlMocks } from '~/testing/graphqlMocks';
import { WorkflowDiagramStepNodeEditableContent } from '../WorkflowDiagramStepNodeEditableContent'; import { WorkflowDiagramStepNodeEditableContent } from '../WorkflowDiagramStepNodeEditableContent';
@ -17,12 +17,43 @@ export default meta;
type Story = StoryObj<typeof WorkflowDiagramStepNodeEditableContent>; type Story = StoryObj<typeof WorkflowDiagramStepNodeEditableContent>;
const ALL_STEPS = [
{
nodeType: 'trigger',
triggerType: 'DATABASE_EVENT',
name: 'Record is Created',
},
{ nodeType: 'trigger', triggerType: 'MANUAL', name: 'Manual' },
{
nodeType: 'action',
actionType: 'CREATE_RECORD',
name: 'Create Record',
},
{
nodeType: 'action',
actionType: 'UPDATE_RECORD',
name: 'Update Record',
},
{
nodeType: 'action',
actionType: 'DELETE_RECORD',
name: 'Delete Record',
},
{
nodeType: 'action',
actionType: 'SEND_EMAIL',
name: 'Send Email',
},
{ nodeType: 'action', actionType: 'CODE', name: 'Code' },
] satisfies WorkflowDiagramStepNodeData[];
export const All: CatalogStory< export const All: CatalogStory<
Story, Story,
typeof WorkflowDiagramStepNodeEditableContent typeof WorkflowDiagramStepNodeEditableContent
> = { > = {
args: { args: {
onDelete: fn(), onDelete: fn(),
variant: 'default',
selected: false, selected: false,
}, },
parameters: { parameters: {
@ -37,57 +68,22 @@ export const All: CatalogStory<
dimensions: [ dimensions: [
{ {
name: 'step type', name: 'step type',
values: [ values: ALL_STEPS,
{
nodeType: 'trigger',
triggerType: 'DATABASE_EVENT',
name: 'Record is Created',
},
{ nodeType: 'trigger', triggerType: 'MANUAL', name: 'Manual' },
{
nodeType: 'action',
actionType: 'CREATE_RECORD',
name: 'Create Record',
},
{
nodeType: 'action',
actionType: 'UPDATE_RECORD',
name: 'Update Record',
},
{
nodeType: 'action',
actionType: 'DELETE_RECORD',
name: 'Delete Record',
},
{
nodeType: 'action',
actionType: 'SEND_EMAIL',
name: 'Send Email',
},
{ nodeType: 'action', actionType: 'CODE', name: 'Code' },
] satisfies WorkflowDiagramStepNodeData[],
props: (data: WorkflowDiagramStepNodeData) => ({ data }), props: (data: WorkflowDiagramStepNodeData) => ({ data }),
}, },
], ],
}, },
}, },
decorators: [ decorators: [CatalogDecorator, ReactflowDecorator],
CatalogDecorator,
(Story) => {
return (
<ReactFlowProvider>
<Story />
</ReactFlowProvider>
);
},
],
}; };
export const AllSelected: CatalogStory< export const AllSelected: CatalogStory<
Story, Story,
typeof WorkflowDiagramStepNodeEditableContent typeof WorkflowDiagramStepNodeEditableContent
> = { > = {
args: { args: {
onDelete: fn(), onDelete: fn(),
variant: 'default',
selected: true, selected: true,
}, },
parameters: { parameters: {
@ -103,48 +99,131 @@ export const AllSelected: CatalogStory<
dimensions: [ dimensions: [
{ {
name: 'step type', name: 'step type',
values: [ values: ALL_STEPS,
{
nodeType: 'trigger',
triggerType: 'DATABASE_EVENT',
name: 'Record is Created',
},
{ nodeType: 'trigger', triggerType: 'MANUAL', name: 'Manual' },
{
nodeType: 'action',
actionType: 'CREATE_RECORD',
name: 'Create Record',
},
{
nodeType: 'action',
actionType: 'UPDATE_RECORD',
name: 'Update Record',
},
{
nodeType: 'action',
actionType: 'DELETE_RECORD',
name: 'Delete Record',
},
{
nodeType: 'action',
actionType: 'SEND_EMAIL',
name: 'Send Email',
},
{ nodeType: 'action', actionType: 'CODE', name: 'Code' },
] satisfies WorkflowDiagramStepNodeData[],
props: (data: WorkflowDiagramStepNodeData) => ({ data }), props: (data: WorkflowDiagramStepNodeData) => ({ data }),
}, },
], ],
}, },
}, },
decorators: [ decorators: [CatalogDecorator, ReactflowDecorator],
CatalogDecorator, };
(Story) => {
return ( export const AllSuccess: CatalogStory<
<ReactFlowProvider> Story,
<Story /> typeof WorkflowDiagramStepNodeEditableContent
</ReactFlowProvider> > = {
); args: {
}, onDelete: fn(),
], variant: 'success',
},
parameters: {
msw: graphqlMocks,
catalog: {
options: {
elementContainer: {
width: 250,
style: { position: 'relative' },
},
},
dimensions: [
{
name: 'step type',
values: ALL_STEPS,
props: (data: WorkflowDiagramStepNodeData) => ({ data }),
},
],
},
},
decorators: [CatalogDecorator, ReactflowDecorator],
};
export const AllSuccessSelected: CatalogStory<
Story,
typeof WorkflowDiagramStepNodeEditableContent
> = {
args: {
onDelete: fn(),
variant: 'success',
selected: true,
},
parameters: {
msw: graphqlMocks,
catalog: {
options: {
elementContainer: {
width: 250,
style: { position: 'relative' },
className: 'selectable selected',
},
},
dimensions: [
{
name: 'step type',
values: ALL_STEPS,
props: (data: WorkflowDiagramStepNodeData) => ({ data }),
},
],
},
},
decorators: [CatalogDecorator, ReactflowDecorator],
};
export const AllFailure: CatalogStory<
Story,
typeof WorkflowDiagramStepNodeEditableContent
> = {
args: {
onDelete: fn(),
variant: 'failure',
},
parameters: {
msw: graphqlMocks,
catalog: {
options: {
elementContainer: {
width: 250,
style: { position: 'relative' },
},
},
dimensions: [
{
name: 'step type',
values: ALL_STEPS,
props: (data: WorkflowDiagramStepNodeData) => ({ data }),
},
],
},
},
decorators: [CatalogDecorator, ReactflowDecorator],
};
export const AllFailureSelected: CatalogStory<
Story,
typeof WorkflowDiagramStepNodeEditableContent
> = {
args: {
onDelete: fn(),
variant: 'failure',
selected: true,
},
parameters: {
msw: graphqlMocks,
catalog: {
options: {
elementContainer: {
width: 250,
style: { position: 'relative' },
className: 'selectable selected',
},
},
dimensions: [
{
name: 'step type',
values: ALL_STEPS,
props: (data: WorkflowDiagramStepNodeData) => ({ data }),
},
],
},
},
decorators: [CatalogDecorator, ReactflowDecorator],
}; };

View File

@ -0,0 +1,5 @@
export type WorkflowDiagramNodeVariant =
| 'default'
| 'success'
| 'failure'
| 'empty';

View File

@ -0,0 +1,10 @@
import { Decorator } from '@storybook/react';
import { ReactFlowProvider } from '@xyflow/react';
export const ReactflowDecorator: Decorator = (Story) => {
return (
<ReactFlowProvider>
<Story />
</ReactFlowProvider>
);
};

View File

@ -0,0 +1,8 @@
import { THEME_COMMON } from '@ui/theme/constants/ThemeCommon';
export const ADAPTIVE_COLORS_DARK = {
turquoise1: THEME_COMMON.color.turquoise80,
turquoise2: THEME_COMMON.color.turquoise70,
turquoise3: THEME_COMMON.color.turquoise60,
turquoise4: THEME_COMMON.color.turquoise50,
};

View File

@ -0,0 +1,8 @@
import { THEME_COMMON } from '@ui/theme/constants/ThemeCommon';
export const ADAPTIVE_COLORS_LIGHT = {
turquoise1: THEME_COMMON.color.turquoise10,
turquoise2: THEME_COMMON.color.turquoise20,
turquoise3: THEME_COMMON.color.turquoise30,
turquoise4: THEME_COMMON.color.turquoise40,
};

View File

@ -1,3 +1,4 @@
import { ADAPTIVE_COLORS_DARK } from '@ui/theme/constants/AdaptiveColorsDark';
import { BLUR_DARK } from '@ui/theme/constants/BlurDark'; import { BLUR_DARK } from '@ui/theme/constants/BlurDark';
import { ILLUSTRATION_ICON_DARK } from '@ui/theme/constants/IllustrationIconDark'; import { ILLUSTRATION_ICON_DARK } from '@ui/theme/constants/IllustrationIconDark';
import { SNACK_BAR_DARK, ThemeType } from '..'; import { SNACK_BAR_DARK, ThemeType } from '..';
@ -24,5 +25,6 @@ export const THEME_DARK: ThemeType = {
tag: TAG_DARK, tag: TAG_DARK,
code: CODE_DARK, code: CODE_DARK,
IllustrationIcon: ILLUSTRATION_ICON_DARK, IllustrationIcon: ILLUSTRATION_ICON_DARK,
adaptiveColors: ADAPTIVE_COLORS_DARK,
}, },
}; };

View File

@ -1,3 +1,4 @@
import { ADAPTIVE_COLORS_LIGHT } from '@ui/theme/constants/AdaptiveColorsLight';
import { BLUR_LIGHT } from '@ui/theme/constants/BlurLight'; import { BLUR_LIGHT } from '@ui/theme/constants/BlurLight';
import { ILLUSTRATION_ICON_LIGHT } from '@ui/theme/constants/IllustrationIconLight'; import { ILLUSTRATION_ICON_LIGHT } from '@ui/theme/constants/IllustrationIconLight';
import { SNACK_BAR_LIGHT } from '@ui/theme/constants/SnackBarLight'; import { SNACK_BAR_LIGHT } from '@ui/theme/constants/SnackBarLight';
@ -24,5 +25,6 @@ export const THEME_LIGHT = {
tag: TAG_LIGHT, tag: TAG_LIGHT,
code: CODE_LIGHT, code: CODE_LIGHT,
IllustrationIcon: ILLUSTRATION_ICON_LIGHT, IllustrationIcon: ILLUSTRATION_ICON_LIGHT,
adaptiveColors: ADAPTIVE_COLORS_LIGHT,
}, },
}; };

View File

@ -1,5 +1,7 @@
export * from './constants/AccentDark'; export * from './constants/AccentDark';
export * from './constants/AccentLight'; export * from './constants/AccentLight';
export * from './constants/AdaptiveColorsDark';
export * from './constants/AdaptiveColorsLight';
export * from './constants/Animation'; export * from './constants/Animation';
export * from './constants/BackgroundDark'; export * from './constants/BackgroundDark';
export * from './constants/BackgroundLight'; export * from './constants/BackgroundLight';