"use client"
import { useRender } from "@base-ui/react/use-render"
import type { CSSProperties, ElementType, ReactNode } from "react"
import * as stylex from "@stylexjs/stylex"
import type { StyleXStyles } from "@stylexjs/stylex"
import { clsx } from "clsx"
import { space } from "@/theme/tokens.stylex"
export type SpacingStep = 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12
const gapStyles = stylex.create({
0: { columnGap: space.s0, rowGap: space.s0 },
0.5: { columnGap: space.s0_5, rowGap: space.s0_5 },
1: { columnGap: space.s1, rowGap: space.s1 },
1.5: { columnGap: space.s1_5, rowGap: space.s1_5 },
2: { columnGap: space.s2, rowGap: space.s2 },
3: { columnGap: space.s3, rowGap: space.s3 },
4: { columnGap: space.s4, rowGap: space.s4 },
5: { columnGap: space.s5, rowGap: space.s5 },
6: { columnGap: space.s6, rowGap: space.s6 },
8: { columnGap: space.s8, rowGap: space.s8 },
10: { columnGap: space.s10, rowGap: space.s10 },
12: { columnGap: space.s12, rowGap: space.s12 },
})
const paddingInlineStartStyles = stylex.create({
0: { paddingInlineStart: space.s0 },
0.5: { paddingInlineStart: space.s0_5 },
1: { paddingInlineStart: space.s1 },
1.5: { paddingInlineStart: space.s1_5 },
2: { paddingInlineStart: space.s2 },
3: { paddingInlineStart: space.s3 },
4: { paddingInlineStart: space.s4 },
5: { paddingInlineStart: space.s5 },
6: { paddingInlineStart: space.s6 },
8: { paddingInlineStart: space.s8 },
10: { paddingInlineStart: space.s10 },
12: { paddingInlineStart: space.s12 },
})
const paddingInlineEndStyles = stylex.create({
0: { paddingInlineEnd: space.s0 },
0.5: { paddingInlineEnd: space.s0_5 },
1: { paddingInlineEnd: space.s1 },
1.5: { paddingInlineEnd: space.s1_5 },
2: { paddingInlineEnd: space.s2 },
3: { paddingInlineEnd: space.s3 },
4: { paddingInlineEnd: space.s4 },
5: { paddingInlineEnd: space.s5 },
6: { paddingInlineEnd: space.s6 },
8: { paddingInlineEnd: space.s8 },
10: { paddingInlineEnd: space.s10 },
12: { paddingInlineEnd: space.s12 },
})
const paddingBlockStartStyles = stylex.create({
0: { paddingBlockStart: space.s0 },
0.5: { paddingBlockStart: space.s0_5 },
1: { paddingBlockStart: space.s1 },
1.5: { paddingBlockStart: space.s1_5 },
2: { paddingBlockStart: space.s2 },
3: { paddingBlockStart: space.s3 },
4: { paddingBlockStart: space.s4 },
5: { paddingBlockStart: space.s5 },
6: { paddingBlockStart: space.s6 },
8: { paddingBlockStart: space.s8 },
10: { paddingBlockStart: space.s10 },
12: { paddingBlockStart: space.s12 },
})
const paddingBlockEndStyles = stylex.create({
0: { paddingBlockEnd: space.s0 },
0.5: { paddingBlockEnd: space.s0_5 },
1: { paddingBlockEnd: space.s1 },
1.5: { paddingBlockEnd: space.s1_5 },
2: { paddingBlockEnd: space.s2 },
3: { paddingBlockEnd: space.s3 },
4: { paddingBlockEnd: space.s4 },
5: { paddingBlockEnd: space.s5 },
6: { paddingBlockEnd: space.s6 },
8: { paddingBlockEnd: space.s8 },
10: { paddingBlockEnd: space.s10 },
12: { paddingBlockEnd: space.s12 },
})
const styles = stylex.create({
base: { display: "flex", minWidth: 0 },
horizontal: { flexDirection: "row" },
vertical: { flexDirection: "column" },
grow: { flexGrow: 1 },
scrollable: { overflow: "auto", minHeight: 0 },
})
const wrapStyles = stylex.create({
nowrap: { flexWrap: "nowrap" },
wrap: { flexWrap: "wrap" },
"wrap-reverse": { flexWrap: "wrap-reverse" },
})
const justifyStyles = stylex.create({
start: { justifyContent: "flex-start" },
center: { justifyContent: "center" },
end: { justifyContent: "flex-end" },
between: { justifyContent: "space-between" },
around: { justifyContent: "space-around" },
evenly: { justifyContent: "space-evenly" },
})
const alignStyles = stylex.create({
start: { alignItems: "flex-start" },
center: { alignItems: "center" },
end: { alignItems: "flex-end" },
stretch: { alignItems: "stretch" },
baseline: { alignItems: "baseline" },
})
type SizeValue = number | string
const toSize = (value: SizeValue) => (typeof value === "number" ? `${value}px` : value)
type StackOwnProps = {
direction?: "horizontal" | "vertical"
justify?: keyof typeof justifyStyles
align?: keyof typeof alignStyles
gap?: SpacingStep
padding?: SpacingStep
paddingInline?: SpacingStep
paddingInlineStart?: SpacingStep
paddingInlineEnd?: SpacingStep
paddingBlock?: SpacingStep
paddingBlockStart?: SpacingStep
paddingBlockEnd?: SpacingStep
wrap?: keyof typeof wrapStyles
grow?: boolean
scrollable?: boolean
width?: SizeValue
height?: SizeValue
maxWidth?: SizeValue
minHeight?: SizeValue
xstyle?: StyleXStyles
style?: CSSProperties
className?: string
children?: ReactNode
}
type StackProps<T extends ElementType = "div"> = StackOwnProps &
Omit<useRender.ComponentProps<T>, keyof StackOwnProps>
function Stack<T extends ElementType = "div">({
render,
ref,
direction = "vertical",
justify,
align,
gap,
padding,
paddingInline,
paddingInlineStart,
paddingInlineEnd,
paddingBlock,
paddingBlockStart,
paddingBlockEnd,
wrap,
grow,
scrollable,
width,
height,
maxWidth,
minHeight,
xstyle,
style,
className,
...props
}: StackProps<T>) {
const inlineStart = paddingInlineStart ?? paddingInline ?? padding
const inlineEnd = paddingInlineEnd ?? paddingInline ?? padding
const blockStart = paddingBlockStart ?? paddingBlock ?? padding
const blockEnd = paddingBlockEnd ?? paddingBlock ?? padding
const sx = stylex.props(
styles.base,
direction === "vertical" ? styles.vertical : styles.horizontal,
justify && justifyStyles[justify],
align && alignStyles[align],
gap != null && gapStyles[gap],
inlineStart != null && paddingInlineStartStyles[inlineStart],
inlineEnd != null && paddingInlineEndStyles[inlineEnd],
blockStart != null && paddingBlockStartStyles[blockStart],
blockEnd != null && paddingBlockEndStyles[blockEnd],
wrap != null && wrapStyles[wrap],
grow && styles.grow,
scrollable && styles.scrollable,
xstyle,
)
const sizing: CSSProperties = {
...sx.style,
...(width != null && { width: toSize(width) }),
...(height != null && { height: toSize(height) }),
...(maxWidth != null && { maxWidth: toSize(maxWidth) }),
...(minHeight != null && { minHeight: toSize(minHeight) }),
...style,
}
return useRender({
defaultTagName: "div",
render,
ref,
props: {
"data-slot": "stack",
"data-orientation": direction,
...sx,
...props,
className: clsx(sx.className, className),
style: Object.keys(sizing).length > 0 ? sizing : undefined,
},
})
}
function HStack<T extends ElementType = "div">(props: Omit<StackProps<T>, "direction">) {
return <Stack<T> {...(props as StackProps<T>)} direction="horizontal" />
}
function VStack<T extends ElementType = "div">(props: Omit<StackProps<T>, "direction">) {
return <Stack<T> {...(props as StackProps<T>)} direction="vertical" />
}
export { HStack, Stack, VStack }