Improve Stack
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -29,7 +29,6 @@ import { displayPropDefs } from '../../props/display.props';
|
||||
export const Box = forwardRef<HTMLDivElement, BoxProps>((props, ref) => {
|
||||
const { as = 'div', children } = props;
|
||||
|
||||
// Extract utility class names and styles
|
||||
const propDefs = {
|
||||
...spacingPropDefs,
|
||||
...widthPropDefs,
|
||||
|
||||
@@ -25,7 +25,6 @@ export const Container = forwardRef<HTMLDivElement, ContainerProps>(
|
||||
(props, ref) => {
|
||||
const { children } = props;
|
||||
|
||||
// Extract utility class names and styles
|
||||
const propDefs = {
|
||||
...displayPropDefs,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 type { PropDef, GetPropDefTypes } from '../../props/prop-def';
|
||||
|
||||
const alignValues = ['start', 'center', 'end', 'baseline', 'stretch'] as const;
|
||||
|
||||
/** @public */
|
||||
const stackPropDefs = {
|
||||
align: {
|
||||
type: 'enum',
|
||||
className: 'cu-align',
|
||||
values: alignValues,
|
||||
responsive: true,
|
||||
default: 'stretch',
|
||||
},
|
||||
} satisfies {
|
||||
align: PropDef<(typeof alignValues)[number]>;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
type StackOwnProps = GetPropDefTypes<typeof stackPropDefs>;
|
||||
|
||||
export { stackPropDefs };
|
||||
export type { StackOwnProps };
|
||||
@@ -29,16 +29,13 @@ const meta = {
|
||||
children: {
|
||||
control: false,
|
||||
},
|
||||
as: {
|
||||
control: false,
|
||||
},
|
||||
className: {
|
||||
control: 'text',
|
||||
},
|
||||
},
|
||||
args: {
|
||||
align: 'left',
|
||||
gap: 'xs',
|
||||
align: 'stretch',
|
||||
gap: '4',
|
||||
children: 'hello world',
|
||||
},
|
||||
} satisfies Meta<typeof Stack>;
|
||||
@@ -63,20 +60,14 @@ const DecorativeBox = () => {
|
||||
};
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => (
|
||||
<div style={{ width: '320px' }}>
|
||||
<Stack>
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
</Stack>
|
||||
</div>
|
||||
),
|
||||
args: {
|
||||
children: [<DecorativeBox />, <DecorativeBox />, <DecorativeBox />],
|
||||
},
|
||||
};
|
||||
|
||||
export const AlignLeft: Story = {
|
||||
render: () => (
|
||||
<Stack align="left">
|
||||
<Stack align="start">
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
@@ -96,7 +87,7 @@ export const AlignCenter: Story = {
|
||||
|
||||
export const AlignRight: Story = {
|
||||
render: () => (
|
||||
<Stack align="right">
|
||||
<Stack align="end">
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
@@ -106,7 +97,7 @@ export const AlignRight: Story = {
|
||||
|
||||
export const ResponsiveAlign: Story = {
|
||||
render: () => (
|
||||
<Stack align={{ xs: 'left', md: 'center', lg: 'right' }}>
|
||||
<Stack align={{ xs: 'start', md: 'center', lg: 'end' }}>
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
@@ -116,7 +107,7 @@ export const ResponsiveAlign: Story = {
|
||||
|
||||
export const ResponsiveGap: Story = {
|
||||
render: () => (
|
||||
<Stack gap={{ xs: 'xs', md: 'md', lg: '2xl' }}>
|
||||
<Stack gap={{ xs: '4', md: '8', lg: '12' }}>
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
@@ -126,7 +117,7 @@ export const ResponsiveGap: Story = {
|
||||
|
||||
export const LargeGap: Story = {
|
||||
render: () => (
|
||||
<Stack gap="xl">
|
||||
<Stack gap="8">
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
|
||||
@@ -16,51 +16,24 @@
|
||||
|
||||
import { createElement, forwardRef } from 'react';
|
||||
import { StackProps } from './types';
|
||||
import { getClassNames } from '../../utils/getClassNames';
|
||||
import type { AlignItems, Breakpoint } from '../../types';
|
||||
import clsx from 'clsx';
|
||||
|
||||
// Function to map align values
|
||||
const mapAlignValue = (value?: StackProps['align']) => {
|
||||
if (typeof value === 'string') {
|
||||
let returnedValue: AlignItems = 'stretch';
|
||||
if (value === 'left') returnedValue = 'stretch';
|
||||
if (value === 'center') returnedValue = 'center';
|
||||
if (value === 'right') returnedValue = 'end';
|
||||
return returnedValue;
|
||||
} else if (typeof value === 'object') {
|
||||
const returnedValue: Partial<Record<Breakpoint, AlignItems>> = {};
|
||||
for (const [key, val] of Object.entries(value)) {
|
||||
returnedValue[key as Breakpoint] = mapAlignValue(val) as AlignItems;
|
||||
}
|
||||
return returnedValue;
|
||||
}
|
||||
return 'stretch';
|
||||
};
|
||||
import { stackPropDefs } from './Stack.props';
|
||||
import { extractProps } from '../../utils/extractProps';
|
||||
import { gapPropDefs } from '../../props/gap-props';
|
||||
|
||||
/** @public */
|
||||
export const Stack = forwardRef<HTMLDivElement, StackProps>((props, ref) => {
|
||||
const {
|
||||
as = 'div',
|
||||
children,
|
||||
align = 'left',
|
||||
gap = 'xs',
|
||||
className,
|
||||
style,
|
||||
...restProps
|
||||
} = props;
|
||||
const propDefs = {
|
||||
...gapPropDefs,
|
||||
...stackPropDefs,
|
||||
};
|
||||
|
||||
// Generate utility class names
|
||||
const utilityClassNames = getClassNames({
|
||||
gap,
|
||||
alignItems: mapAlignValue(align),
|
||||
...restProps,
|
||||
});
|
||||
const { className, style } = extractProps(props, propDefs);
|
||||
|
||||
return createElement(as, {
|
||||
return createElement('div', {
|
||||
ref,
|
||||
className: clsx('canon-Stack', utilityClassNames, className),
|
||||
className: clsx('canon-Stack', className),
|
||||
style,
|
||||
children,
|
||||
children: props.children,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,23 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type {
|
||||
SpaceProps,
|
||||
UtilityProps,
|
||||
AsProps,
|
||||
Breakpoint,
|
||||
} from '../../types';
|
||||
import type { SpaceProps } from '../../types';
|
||||
import { StackOwnProps } from './Stack.props';
|
||||
import type { GapProps } from '../../props/gap-props';
|
||||
|
||||
/** @public */
|
||||
export interface StackProps extends SpaceProps {
|
||||
children: React.ReactNode;
|
||||
as?: AsProps;
|
||||
gap?: UtilityProps['gap'];
|
||||
align?:
|
||||
| 'left'
|
||||
| 'center'
|
||||
| 'right'
|
||||
| Partial<Record<Breakpoint, 'left' | 'center' | 'right'>>;
|
||||
gap?: GapProps['gap'];
|
||||
align?: StackOwnProps['align'];
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
@@ -67,3 +67,6 @@
|
||||
|
||||
/* Gap */
|
||||
@import './utilities/gap.css';
|
||||
|
||||
/* Flex */
|
||||
@import './utilities/flex.css';
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
.cu-align-start {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.cu-align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cu-align-end {
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
/* Breakpoint xs */
|
||||
@media (min-width: 640px) {
|
||||
.xs\:cu-align-start {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.xs\:cu-align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.xs\:cu-align-end {
|
||||
align-items: end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Breakpoint sm */
|
||||
@media (min-width: 768px) {
|
||||
.sm\:cu-align-start {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.sm\:cu-align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sm\:cu-align-end {
|
||||
align-items: end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Breakpoint md */
|
||||
@media (min-width: 1024px) {
|
||||
.md\:cu-align-start {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.md\:cu-align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.md\:cu-align-end {
|
||||
align-items: end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Breakpoint lg */
|
||||
@media (min-width: 1280px) {
|
||||
.lg\:cu-align-start {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.lg\:cu-align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.lg\:cu-align-end {
|
||||
align-items: end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Breakpoint xl */
|
||||
@media (min-width: 1536px) {
|
||||
.xl\:cu-align-start {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.xl\:cu-align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.xl\:cu-align-end {
|
||||
align-items: end;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user