Update spacing props display with collapsible groups
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": ["next/core-web-vitals", "next/typescript"],
|
||||
"extends": "next/core-web-vitals",
|
||||
"rules": {
|
||||
"notice/notice": "off",
|
||||
"react/forbid-elements": "off",
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
boxResponsiveSnippet,
|
||||
} from './snippets';
|
||||
import { Default, Surface, Responsive } from './components';
|
||||
import { spacingPropDefs } from '@/utils/propDefs';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { BoxDefinition } from '../../../utils/definitions';
|
||||
@@ -31,12 +30,6 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PropsTable data={boxPropDefs} />
|
||||
|
||||
Padding and margin are used to create space around your component using our
|
||||
predefined spacing tokens. We would recommend to use padding over margin to
|
||||
avoid collapsing margins but both are available.
|
||||
|
||||
<PropsTable data={spacingPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Surface
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
positionPropDefs,
|
||||
stylePropDefs,
|
||||
widthPropDefs,
|
||||
spacingGroupAll,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
import { Chip } from '@/components/Chip';
|
||||
@@ -43,4 +44,5 @@ export const boxPropDefs: Record<string, PropDef> = {
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
spacing: spacingGroupAll,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
spacingValues,
|
||||
createSpacingGroup,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
@@ -11,42 +11,10 @@ export const containerPropDefs: Record<string, PropDef> = {
|
||||
values: ['ReactNode'],
|
||||
description: 'Content to render inside the container.',
|
||||
},
|
||||
my: {
|
||||
type: 'enum | string',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Vertical margin (top and bottom).',
|
||||
},
|
||||
mt: {
|
||||
type: 'enum | string',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Top margin.',
|
||||
},
|
||||
mb: {
|
||||
type: 'enum | string',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Bottom margin.',
|
||||
},
|
||||
py: {
|
||||
type: 'enum | string',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Vertical padding (top and bottom).',
|
||||
},
|
||||
pt: {
|
||||
type: 'enum | string',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Top padding.',
|
||||
},
|
||||
pb: {
|
||||
type: 'enum | string',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Bottom padding.',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
spacing: createSpacingGroup(
|
||||
['my', 'mt', 'mb', 'py', 'pt', 'pb'],
|
||||
'Vertical spacing properties for controlling margin and padding.',
|
||||
),
|
||||
};
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
AlignExample,
|
||||
DirectionExample,
|
||||
} from './components';
|
||||
import { spacingPropDefs } from '@/utils/propDefs';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { FlexDefinition } from '../../../utils/definitions';
|
||||
@@ -37,10 +36,6 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PropsTable data={flexPropDefs} />
|
||||
|
||||
The Flex component also accepts all the spacing props from the Box component.
|
||||
|
||||
<PropsTable data={spacingPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Direction
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
gapPropDefs,
|
||||
spacingGroupAll,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
import { Chip } from '@/components/Chip';
|
||||
@@ -61,4 +62,5 @@ export const flexPropDefs: Record<string, PropDef> = {
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
spacing: spacingGroupAll,
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
gridItemSnippet,
|
||||
} from './snippets';
|
||||
import { Default, ResponsiveExample, GridItemExample } from './components';
|
||||
import { spacingPropDefs } from '@/utils/propDefs';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { GridDefinition } from '../../../utils/definitions';
|
||||
@@ -35,10 +34,6 @@ The grid container. Defines column count and gap between items.
|
||||
|
||||
<PropsTable data={gridPropDefs} />
|
||||
|
||||
Grid.Root also accepts all spacing props from the Box component.
|
||||
|
||||
<PropsTable data={spacingPropDefs} />
|
||||
|
||||
### Grid.Item
|
||||
|
||||
A grid child with column and row spanning control.
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
classNamePropDefs,
|
||||
gapPropDefs,
|
||||
stylePropDefs,
|
||||
spacingGroupAll,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
import { Chip } from '@/components/Chip';
|
||||
@@ -67,6 +68,7 @@ export const gridPropDefs: Record<string, PropDef> = {
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
spacing: spacingGroupAll,
|
||||
};
|
||||
|
||||
export const gridItemPropDefs: Record<string, PropDef> = {
|
||||
|
||||
@@ -8,8 +8,6 @@ import { MobileBottomNav } from '@/components/MobileBottomNav';
|
||||
import styles from './layout.module.css';
|
||||
|
||||
import '../css/globals.css';
|
||||
import '/public/theme-backstage.css';
|
||||
import '/public/theme-spotify.css';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Backstage UI',
|
||||
@@ -49,6 +47,10 @@ export default async function RootLayout({
|
||||
data-theme-name="backstage"
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/theme-backstage.css" />
|
||||
<link rel="stylesheet" href="/theme-spotify.css" />
|
||||
</head>
|
||||
<body>
|
||||
<Providers>
|
||||
<Sidebar />
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
import * as Table from '../Table';
|
||||
import { Chip } from '../Chip';
|
||||
import { TypePopup } from './TypePopup';
|
||||
import { SpacingPopup } from './SpacingPopup';
|
||||
import { SpacingGroupRow } from './SpacingGroupRow';
|
||||
|
||||
import { PropDef } from '@/utils/propDefs';
|
||||
|
||||
@@ -37,8 +39,10 @@ export const PropsTable = <T extends Record<string, PropData>>({
|
||||
data: T;
|
||||
columns?: ColumnConfig[];
|
||||
}) => {
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
|
||||
const renderCell = (
|
||||
propName: string,
|
||||
propData: PropData,
|
||||
@@ -59,12 +63,10 @@ export const PropsTable = <T extends Record<string, PropData>>({
|
||||
{propData.type === 'number' && <Chip>number</Chip>}
|
||||
{propData.type === 'boolean' && <Chip>boolean</Chip>}
|
||||
{propData.type === 'enum' && enumValues}
|
||||
{propData.type === 'spacing' && (
|
||||
<>
|
||||
<Chip>0.5, 1, 1.5, 2, 3, ..., 14</Chip>
|
||||
<Chip>string</Chip>
|
||||
</>
|
||||
)}
|
||||
{propData.type === 'spacing' &&
|
||||
Array.isArray(propData.values) && (
|
||||
<SpacingPopup values={propData.values} />
|
||||
)}
|
||||
{propData.type === 'complex' && propData.complexType && (
|
||||
<TypePopup
|
||||
complexType={propData.complexType}
|
||||
@@ -81,10 +83,12 @@ export const PropsTable = <T extends Record<string, PropData>>({
|
||||
);
|
||||
|
||||
case 'default':
|
||||
return propData.default ? <Chip>{propData.default}</Chip> : null;
|
||||
return propData.default ? <Chip>{propData.default}</Chip> : '-';
|
||||
|
||||
case 'description':
|
||||
return propData.description || null;
|
||||
return propData.description ? (
|
||||
<span style={{ fontSize: '14px' }}>{propData.description}</span>
|
||||
) : null;
|
||||
|
||||
case 'responsive':
|
||||
return <Chip>{propData.responsive ? 'Yes' : 'No'}</Chip>;
|
||||
@@ -106,15 +110,32 @@ export const PropsTable = <T extends Record<string, PropData>>({
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{Object.keys(data).map(n => (
|
||||
<Table.Row key={n}>
|
||||
{columns.map(col => (
|
||||
<Table.Cell key={col.key} style={{ width: col.width }}>
|
||||
{renderCell(n, data[n], col.key)}
|
||||
</Table.Cell>
|
||||
))}
|
||||
</Table.Row>
|
||||
))}
|
||||
{Object.keys(data).map(n => {
|
||||
const propData = data[n];
|
||||
|
||||
// Handle spacing-group type
|
||||
if (propData.type === 'spacing-group' && propData.spacingGroup) {
|
||||
return (
|
||||
<SpacingGroupRow
|
||||
key={n}
|
||||
spacingGroup={propData.spacingGroup}
|
||||
description={propData.description}
|
||||
columns={columns}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Handle regular props
|
||||
return (
|
||||
<Table.Row key={n}>
|
||||
{columns.map(col => (
|
||||
<Table.Cell key={col.key} style={{ width: col.width }}>
|
||||
{renderCell(n, propData, col.key)}
|
||||
</Table.Cell>
|
||||
))}
|
||||
</Table.Row>
|
||||
);
|
||||
})}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
.expandButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.expandButton:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.propName {
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.count {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: var(--text-secondary);
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.expandedContent {
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.twoColumnLayout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.columnTitle {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.25rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.propsGrid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.propRow {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
gap: 1rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.propRowName {
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.propRowDescription {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.propRowDefault {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import * as Table from '../Table';
|
||||
import { Chip } from '../Chip';
|
||||
import { SpacingPopup } from './SpacingPopup';
|
||||
import type { SpacingGroupDef } from '@/utils/propDefs';
|
||||
import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react';
|
||||
import styles from './SpacingGroupRow.module.css';
|
||||
|
||||
interface SpacingGroupRowProps {
|
||||
spacingGroup: SpacingGroupDef;
|
||||
description?: React.ReactNode;
|
||||
columns: Array<{ key: string; width: string }>;
|
||||
}
|
||||
|
||||
export const SpacingGroupRow = ({
|
||||
spacingGroup,
|
||||
description,
|
||||
columns,
|
||||
}: SpacingGroupRowProps) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
// Separate padding and margin props
|
||||
const paddingProps = spacingGroup.props.filter(prop => prop.name.startsWith('p'));
|
||||
const marginProps = spacingGroup.props.filter(prop => prop.name.startsWith('m'));
|
||||
|
||||
const renderCell = (columnKey: string) => {
|
||||
switch (columnKey) {
|
||||
case 'prop':
|
||||
return (
|
||||
<button
|
||||
className={styles.expandButton}
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
aria-expanded={isExpanded}
|
||||
>
|
||||
<span className={styles.propName}>
|
||||
Spacing props <span className={styles.count}>({spacingGroup.props.length})</span>
|
||||
</span>
|
||||
{isExpanded ? (
|
||||
<RiArrowDownSLine className={styles.icon} size={16} />
|
||||
) : (
|
||||
<RiArrowRightSLine className={styles.icon} size={16} />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
|
||||
case 'type':
|
||||
return <SpacingPopup values={spacingGroup.values} />;
|
||||
|
||||
case 'default':
|
||||
return '-';
|
||||
|
||||
case 'description':
|
||||
return description ? (
|
||||
<span style={{ fontSize: '14px' }}>{description}</span>
|
||||
) : null;
|
||||
|
||||
case 'responsive':
|
||||
return <Chip>{spacingGroup.responsive ? 'Yes' : 'No'}</Chip>;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table.Row>
|
||||
{columns.map(col => (
|
||||
<Table.Cell key={col.key} style={{ width: col.width }}>
|
||||
{renderCell(col.key)}
|
||||
</Table.Cell>
|
||||
))}
|
||||
</Table.Row>
|
||||
{isExpanded && (
|
||||
<Table.Row>
|
||||
<Table.Cell colSpan={columns.length}>
|
||||
<div className={styles.expandedContent}>
|
||||
<div className={styles.twoColumnLayout}>
|
||||
{/* Padding Column */}
|
||||
{paddingProps.length > 0 && (
|
||||
<div className={styles.column}>
|
||||
<div className={styles.columnTitle}>Padding</div>
|
||||
<div className={styles.propsGrid}>
|
||||
{paddingProps.map(prop => (
|
||||
<div key={prop.name} className={styles.propRow}>
|
||||
<div className={styles.propRowName}>
|
||||
<Chip head>{prop.name}</Chip>
|
||||
</div>
|
||||
<div className={styles.propRowDescription}>
|
||||
{prop.description}
|
||||
</div>
|
||||
{prop.default && (
|
||||
<div className={styles.propRowDefault}>
|
||||
Default: <Chip>{prop.default}</Chip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Margin Column */}
|
||||
{marginProps.length > 0 && (
|
||||
<div className={styles.column}>
|
||||
<div className={styles.columnTitle}>Margin</div>
|
||||
<div className={styles.propsGrid}>
|
||||
{marginProps.map(prop => (
|
||||
<div key={prop.name} className={styles.propRow}>
|
||||
<div className={styles.propRowName}>
|
||||
<Chip head>{prop.name}</Chip>
|
||||
</div>
|
||||
<div className={styles.propRowDescription}>
|
||||
{prop.description}
|
||||
</div>
|
||||
{prop.default && (
|
||||
<div className={styles.propRowDefault}>
|
||||
Default: <Chip>{prop.default}</Chip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
border-radius: 6px;
|
||||
padding: 0px 8px;
|
||||
height: 24px;
|
||||
margin-right: 4px;
|
||||
background-color: #f0f0f0;
|
||||
color: #5d5d5d;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
transition: background-color 150ms ease;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
[data-theme-mode='dark'] .button {
|
||||
background-color: #2c2c2c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
[data-theme-mode='dark'] .button:hover {
|
||||
background-color: #3c3c3c;
|
||||
}
|
||||
|
||||
.popover {
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 8px 20px rgba(0 0 0 / 0.1);
|
||||
border-radius: 6px;
|
||||
background: var(--bg);
|
||||
color: var(--primary);
|
||||
outline: none;
|
||||
transition: transform 200ms, opacity 200ms;
|
||||
padding: 1rem;
|
||||
margin-top: 6px;
|
||||
max-width: 400px;
|
||||
--origin: translateY(-8px);
|
||||
|
||||
&[data-entering],
|
||||
&[data-exiting] {
|
||||
transform: var(--origin);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow svg {
|
||||
display: block;
|
||||
fill: var(--bg);
|
||||
stroke: var(--border);
|
||||
stroke-width: 1px;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.375rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.note {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
font-style: italic;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Chip } from '../Chip';
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
OverlayArrow,
|
||||
Popover,
|
||||
} from 'react-aria-components';
|
||||
import styles from './SpacingPopup.module.css';
|
||||
|
||||
interface SpacingPopupProps {
|
||||
values: string[];
|
||||
}
|
||||
|
||||
export const SpacingPopup = ({ values }: SpacingPopupProps) => {
|
||||
// Display abbreviated list: first, second, ..., last
|
||||
const firstValue = values[0];
|
||||
const secondValue = values[1];
|
||||
const lastValue = values[values.length - 1];
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.375rem' }}>
|
||||
<Chip>{firstValue}</Chip>
|
||||
<Chip>{secondValue}</Chip>
|
||||
<DialogTrigger>
|
||||
<Button className={styles.button}>...</Button>
|
||||
<Popover className={styles.popover}>
|
||||
<OverlayArrow className={styles.arrow}>
|
||||
<svg width={12} height={12} viewBox="0 0 12 12">
|
||||
<path d="M0 0 L6 6 L12 0" />
|
||||
</svg>
|
||||
</OverlayArrow>
|
||||
<Dialog>
|
||||
<div className={styles.title}>All spacing values</div>
|
||||
<div className={styles.grid}>
|
||||
{values.map(value => (
|
||||
<Chip key={value}>{value}</Chip>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.note}>Also accepts custom string values</div>
|
||||
</Dialog>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
<Chip>{lastValue}</Chip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -45,12 +45,14 @@ export const Row = ({ children }: { children: ReactNode }) => {
|
||||
export const Cell = ({
|
||||
children,
|
||||
style,
|
||||
colSpan,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
style?: CSSProperties;
|
||||
colSpan?: number;
|
||||
}) => {
|
||||
return (
|
||||
<td className={styles.tableCell} style={style}>
|
||||
<td className={styles.tableCell} style={style} colSpan={colSpan}>
|
||||
{children}
|
||||
</td>
|
||||
);
|
||||
|
||||
@@ -15,6 +15,18 @@ export type ComplexTypeDef = {
|
||||
>;
|
||||
};
|
||||
|
||||
export type SpacingProp = {
|
||||
name: string;
|
||||
description: ReactNode;
|
||||
default?: string;
|
||||
};
|
||||
|
||||
export type SpacingGroupDef = {
|
||||
props: SpacingProp[];
|
||||
values: string[];
|
||||
responsive: boolean;
|
||||
};
|
||||
|
||||
export type PropDef = {
|
||||
type:
|
||||
| 'string'
|
||||
@@ -23,9 +35,11 @@ export type PropDef = {
|
||||
| 'number'
|
||||
| 'boolean'
|
||||
| 'spacing'
|
||||
| 'spacing-group'
|
||||
| 'complex';
|
||||
values?: string | string[];
|
||||
complexType?: ComplexTypeDef;
|
||||
spacingGroup?: SpacingGroupDef;
|
||||
default?: string;
|
||||
required?: boolean;
|
||||
responsive?: boolean;
|
||||
@@ -58,43 +72,43 @@ export const paddingPropDefs = (
|
||||
spacingValues: string[],
|
||||
): Record<string, PropDef> => ({
|
||||
p: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Padding on all sides.',
|
||||
},
|
||||
px: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Horizontal padding (left and right).',
|
||||
},
|
||||
py: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Vertical padding (top and bottom).',
|
||||
},
|
||||
pt: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Padding on the top.',
|
||||
},
|
||||
pr: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Padding on the right.',
|
||||
},
|
||||
pb: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Padding on the bottom.',
|
||||
},
|
||||
pl: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Padding on the left.',
|
||||
@@ -105,43 +119,43 @@ export const marginPropDefs = (
|
||||
spacingValues: string[],
|
||||
): Record<string, PropDef> => ({
|
||||
m: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Margin on all sides.',
|
||||
},
|
||||
mx: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Horizontal margin (left and right).',
|
||||
},
|
||||
my: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Vertical margin (top and bottom).',
|
||||
},
|
||||
mt: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Margin on the top.',
|
||||
},
|
||||
mr: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Margin on the right.',
|
||||
},
|
||||
mb: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Margin on the bottom.',
|
||||
},
|
||||
ml: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
description: 'Margin on the left.',
|
||||
@@ -153,6 +167,76 @@ export const spacingPropDefs = {
|
||||
...marginPropDefs(spacingValues),
|
||||
};
|
||||
|
||||
// Spacing prop metadata for creating groups
|
||||
const spacingPropMetadata: Record<string, SpacingProp> = {
|
||||
p: { name: 'p', description: 'Padding on all sides.' },
|
||||
px: { name: 'px', description: 'Horizontal padding (left and right).' },
|
||||
py: { name: 'py', description: 'Vertical padding (top and bottom).' },
|
||||
pt: { name: 'pt', description: 'Padding on the top.' },
|
||||
pr: { name: 'pr', description: 'Padding on the right.' },
|
||||
pb: { name: 'pb', description: 'Padding on the bottom.' },
|
||||
pl: { name: 'pl', description: 'Padding on the left.' },
|
||||
m: { name: 'm', description: 'Margin on all sides.' },
|
||||
mx: { name: 'mx', description: 'Horizontal margin (left and right).' },
|
||||
my: { name: 'my', description: 'Vertical margin (top and bottom).' },
|
||||
mt: { name: 'mt', description: 'Margin on the top.' },
|
||||
mr: { name: 'mr', description: 'Margin on the right.' },
|
||||
mb: { name: 'mb', description: 'Margin on the bottom.' },
|
||||
ml: { name: 'ml', description: 'Margin on the left.' },
|
||||
};
|
||||
|
||||
export const createSpacingGroup = (
|
||||
propNames: string[],
|
||||
description?: ReactNode,
|
||||
): PropDef => {
|
||||
const props = propNames
|
||||
.map(name => spacingPropMetadata[name])
|
||||
.filter(Boolean);
|
||||
|
||||
return {
|
||||
type: 'spacing-group',
|
||||
spacingGroup: {
|
||||
props,
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
},
|
||||
description:
|
||||
description || 'Spacing properties for controlling padding and margin.',
|
||||
responsive: true,
|
||||
};
|
||||
};
|
||||
|
||||
// Pre-built spacing groups
|
||||
export const spacingGroupAll = createSpacingGroup(
|
||||
[
|
||||
'p',
|
||||
'px',
|
||||
'py',
|
||||
'pt',
|
||||
'pr',
|
||||
'pb',
|
||||
'pl',
|
||||
'm',
|
||||
'mx',
|
||||
'my',
|
||||
'mt',
|
||||
'mr',
|
||||
'mb',
|
||||
'ml',
|
||||
],
|
||||
'Padding and margin properties for controlling spacing around the element.',
|
||||
);
|
||||
|
||||
export const spacingGroupPadding = createSpacingGroup(
|
||||
['p', 'px', 'py', 'pt', 'pr', 'pb', 'pl'],
|
||||
'Padding properties for controlling internal spacing.',
|
||||
);
|
||||
|
||||
export const spacingGroupMargin = createSpacingGroup(
|
||||
['m', 'mx', 'my', 'mt', 'mr', 'mb', 'ml'],
|
||||
'Margin properties for controlling external spacing.',
|
||||
);
|
||||
|
||||
export const displayPropDefs: Record<string, PropDef> = {
|
||||
display: {
|
||||
type: 'enum',
|
||||
@@ -164,7 +248,7 @@ export const displayPropDefs: Record<string, PropDef> = {
|
||||
|
||||
export const gapPropDefs: Record<string, PropDef> = {
|
||||
gap: {
|
||||
type: 'enum | string',
|
||||
type: 'spacing',
|
||||
values: spacingValues,
|
||||
responsive: true,
|
||||
default: '4',
|
||||
|
||||
Reference in New Issue
Block a user