Fixed Inline + Stack

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2024-11-28 21:39:25 +00:00
parent ff47196321
commit 3d7fab9a25
9 changed files with 199 additions and 54 deletions
@@ -42,7 +42,7 @@ export const Padding = () => {
paddingY="xl"
style={{ border: '1px solid #e7e7e7' }}
>
<Inline align="center" gap="xl">
<Inline align="center" alignY="center" gap="xl">
<Box
alignItems="center"
borderRadius="small"
@@ -68,7 +68,7 @@ export const Padding = () => {
<FakeBox>paddingY</FakeBox>
</Box>
</Inline>
<Inline align="center" gap="xl">
<Inline align="center" alignY="center" gap="xl">
<Box
alignItems="center"
borderRadius="small"
@@ -18,38 +18,108 @@ import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { Inline } from './Inline';
import { Box } from '../Box/Box';
import { argTypesSpacing, argTypesColor } from '../../../docs/utils/argTypes';
const meta = {
title: 'Components/Inline',
component: Inline,
argTypes: {
...argTypesSpacing,
...argTypesColor,
align: {
control: 'inline-radio',
options: ['left', 'center', 'right'],
},
alignY: {
control: 'inline-radio',
options: ['top', 'center', 'bottom'],
},
children: {
control: false,
},
as: {
control: false,
},
className: {
control: 'text',
},
},
} satisfies Meta<typeof Inline>;
export default meta;
type Story = StoryObj<typeof meta>;
const FakeBox = () => (
const FakeBox = ({
width = 120,
height = 80,
}: {
width?: number;
height?: number;
}) => (
<Box
paddingX="xl"
paddingY="md"
borderRadius="small"
style={{ background: '#1f47ff', color: 'white' }}
>
Fake Box
</Box>
style={{ background: '#1f47ff', color: 'white', width, height }}
/>
);
export const Default: Story = {
args: {
children: (
<>
<FakeBox />
<FakeBox />
<FakeBox />
{Array.from({ length: 32 }).map((_, index) => (
<FakeBox
key={index}
width={Math.floor(Math.random() * (160 - 40 + 1)) + 40}
height={Math.floor(Math.random() * (80 - 40 + 1)) + 40}
/>
))}
</>
),
},
};
export const AlignLeft: Story = {
args: {
...Default.args,
align: 'left',
},
};
export const AlignCenter: Story = {
args: {
...Default.args,
align: 'center',
},
};
export const AlignRight: Story = {
args: {
...Default.args,
align: 'right',
},
};
export const VerticalAlignTop: Story = {
args: {
...Default.args,
alignY: 'top',
},
};
export const VerticalAlignCenter: Story = {
args: {
...Default.args,
alignY: 'center',
},
};
export const VerticalAlignBottom: Story = {
args: {
...Default.args,
alignY: 'bottom',
},
};
export const LargeGap: Story = {
args: {
...Default.args,
+37 -32
View File
@@ -14,45 +14,50 @@
* limitations under the License.
*/
import React from 'react';
import { Box } from '../Box/Box';
import type { BoxProps } from '../Box/types';
import { createElement } from 'react';
import { inlineSprinkles } from './sprinkles.css';
import type { InlineProps } from './types';
export const validInlineComponents = [
'div',
'span',
'p',
'nav',
'ul',
'ol',
'li',
] as const;
const alignYToFlexAlign = (alignY: InlineProps['alignY']) => {
if (alignY === 'top') return 'flex-start';
if (alignY === 'center') return 'center';
if (alignY === 'bottom') return 'flex-end';
return undefined;
};
export interface InlineProps extends Omit<BoxProps, 'alignItems'> {
as?: (typeof validInlineComponents)[number];
children: React.ReactNode;
align?: 'left' | 'center' | 'right';
gap?: BoxProps['gap'];
}
const alignToFlexAlignY = (align: InlineProps['align']) => {
if (align === 'left') return 'flex-start';
if (align === 'center') return 'center';
if (align === 'right') return 'flex-end';
return undefined;
};
export const Inline = ({
align,
as = 'div',
children,
align = 'left',
alignY = 'top',
gap = 'xs',
className,
style,
...restProps
}: InlineProps) => {
return (
<Box
as={as}
display="flex"
// alignItems={align !== 'left' ? alignToFlexAlign(align) : undefined}
justifyContent="flex-start"
flexWrap="wrap"
gap={gap}
{...restProps}
>
{children}
</Box>
);
// Generate the list of class names
const sprinklesClassName = inlineSprinkles({
...restProps,
gap,
alignItems: alignYToFlexAlign(alignY),
justifyContent: alignToFlexAlignY(align),
});
// Combine the base class name, the sprinkles class name, and any additional class names
const classNames = ['inline', sprinklesClassName, className]
.filter(Boolean)
.join(' ');
return createElement(as, {
className: classNames,
style,
children,
});
};
@@ -0,0 +1,34 @@
/*
* Copyright 2024 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 { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles';
import { breakpoints } from '../../layout/properties';
import { colorProperties, spacingProperties } from '../../layout/sprinkles.css';
const inlineProperties = defineProperties({
conditions: breakpoints,
defaultCondition: 'xs',
properties: {
alignItems: ['flex-start', 'center', 'flex-end'],
justifyContent: ['flex-start', 'center', 'flex-end'],
},
});
export const inlineSprinkles = createSprinkles(
spacingProperties,
colorProperties,
inlineProperties,
);
@@ -0,0 +1,4 @@
.inline {
display: flex;
flex-wrap: wrap;
}
@@ -0,0 +1,35 @@
/*
* Copyright 2024 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 { AsProps, Breakpoint, ColorProps } from '../../layout/types';
import { SpaceProps } from '../../layout/types';
export interface InlineProps extends SpaceProps, ColorProps {
children: React.ReactNode;
as?: AsProps;
align?:
| 'left'
| 'center'
| 'right'
| Partial<Record<Breakpoint, 'left' | 'center' | 'right'>>;
alignY?:
| 'top'
| 'center'
| 'bottom'
| Partial<Record<Breakpoint, 'top' | 'center' | 'bottom'>>;
className?: string;
style?: React.CSSProperties;
}
@@ -18,8 +18,7 @@ import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { Stack } from './Stack';
import { Box } from '../Box/Box';
import { argTypesSpacing } from '../../../docs/utils/argTypes';
import { argTypesColor } from '../../../docs/utils/argTypes';
import { argTypesSpacing, argTypesColor } from '../../../docs/utils/argTypes';
const meta = {
title: 'Components/Stack',
+5 -8
View File
@@ -16,17 +16,14 @@
import { AsProps, Breakpoint, ColorProps } from '../../layout/types';
import { SpaceProps } from '../../layout/types';
/** @public */
export type AlignProps =
| 'left'
| 'center'
| 'right'
| Partial<Record<Breakpoint, 'left' | 'center' | 'right'>>;
export interface StackProps extends SpaceProps, ColorProps {
children: React.ReactNode;
as?: AsProps;
align?: AlignProps;
align?:
| 'left'
| 'center'
| 'right'
| Partial<Record<Breakpoint, 'left' | 'center' | 'right'>>;
className?: string;
style?: React.CSSProperties;
}
+1
View File
@@ -24,3 +24,4 @@
/* Components */
@import '../components/Button/styles.css';
@import '../components/Stack/styles.css';
@import '../components/Inline/styles.css';