howl/src/grid.tsx

170 lines
6.3 KiB
TypeScript

import type { TailwindSize } from "./types"
import "hono/jsx"
import type { FC, PropsWithChildren, JSX } from "hono/jsx"
import { VStack } from "./stack"
import { Button } from "./button"
import { Section } from "./section"
import { H2, H3 } from "./text"
import { CodeExamples } from "./code"
import { cn } from "./cn"
type GridProps = PropsWithChildren & {
cols?: GridCols
gap?: TailwindSize
v?: keyof typeof alignItemsMap
h?: keyof typeof justifyItemsMap
class?: string
style?: JSX.CSSProperties
id?: string
ref?: any
}
type GridCols = number | { sm?: number; md?: number; lg?: number; xl?: number }
export const Grid: FC<GridProps> = (props) => {
const { cols = 2, gap = 4, v, h, class: className, style, id, ref, children } = props
const gapPx = gap * 4
const baseStyles: JSX.CSSProperties = {
display: "grid",
gridTemplateColumns: getColumnsValue(cols),
gap: `${gapPx}px`,
}
if (v) {
baseStyles.alignItems = alignItemsMap[v]
}
if (h) {
baseStyles.justifyItems = justifyItemsMap[h]
}
const combinedStyles = {
...baseStyles,
...style,
}
return <div class={cn("Grid", className)} style={combinedStyles} id={id} ref={ref}>{children}</div>
}
function getColumnsValue(cols: GridCols): string {
if (typeof cols === "number") {
return `repeat(${cols}, minmax(0, 1fr))`
}
// For responsive grids, we'll use the largest value
// In a real implementation, you'd want media queries which require CSS
// For now, let's use the largest value specified
const largestCols = cols.xl || cols.lg || cols.md || cols.sm || 1
return `repeat(${largestCols}, minmax(0, 1fr))`
}
const alignItemsMap = {
start: "start",
center: "center",
end: "end",
stretch: "stretch",
} as const
const justifyItemsMap = {
start: "start",
center: "center",
end: "end",
stretch: "stretch",
} as const
export const Test = () => {
return (
<Section gap={4} style={{ padding: "16px" }}>
{/* API Usage Examples */}
<CodeExamples
examples={[
'<Grid cols={3}>...</Grid>',
'<Grid cols={4} gap={6}>...</Grid>',
'<Grid cols={{ sm: 1, md: 2, lg: 3 }}>...</Grid>',
'<Grid cols={2} v="center" h="center">...</Grid>',
]}
/>
<VStack gap={6}>
<H2>Grid Examples</H2>
{/* Simple 3-column grid */}
<VStack gap={2}>
<H3>Simple 3 columns: cols=3</H3>
<Grid cols={3} gap={4}>
<div style={{ backgroundColor: "#fecaca", padding: "16px", textAlign: "center" }}>Item 1</div>
<div style={{ backgroundColor: "#bbf7d0", padding: "16px", textAlign: "center" }}>Item 2</div>
<div style={{ backgroundColor: "#bfdbfe", padding: "16px", textAlign: "center" }}>Item 3</div>
<div style={{ backgroundColor: "#fef08a", padding: "16px", textAlign: "center" }}>Item 4</div>
<div style={{ backgroundColor: "#e9d5ff", padding: "16px", textAlign: "center" }}>Item 5</div>
<div style={{ backgroundColor: "#fbcfe8", padding: "16px", textAlign: "center" }}>Item 6</div>
</Grid>
</VStack>
{/* Responsive grid */}
<VStack gap={2}>
<H3>Responsive: cols=&#123;sm: 1, md: 2, lg: 3&#125;</H3>
<Grid cols={{ sm: 1, md: 2, lg: 3 }} gap={4}>
<div style={{ backgroundColor: "#fecaca", padding: "16px", textAlign: "center" }}>Card 1</div>
<div style={{ backgroundColor: "#bbf7d0", padding: "16px", textAlign: "center" }}>Card 2</div>
<div style={{ backgroundColor: "#bfdbfe", padding: "16px", textAlign: "center" }}>Card 3</div>
<div style={{ backgroundColor: "#fef08a", padding: "16px", textAlign: "center" }}>Card 4</div>
</Grid>
</VStack>
{/* More responsive examples */}
<VStack gap={2}>
<H3>More responsive: cols=&#123;sm: 2, lg: 4, xl: 6&#125;</H3>
<Grid cols={{ sm: 2, lg: 4, xl: 6 }} gap={4}>
<div style={{ backgroundColor: "#fecaca", padding: "16px", textAlign: "center" }}>Item A</div>
<div style={{ backgroundColor: "#bbf7d0", padding: "16px", textAlign: "center" }}>Item B</div>
<div style={{ backgroundColor: "#bfdbfe", padding: "16px", textAlign: "center" }}>Item C</div>
<div style={{ backgroundColor: "#fef08a", padding: "16px", textAlign: "center" }}>Item D</div>
<div style={{ backgroundColor: "#e9d5ff", padding: "16px", textAlign: "center" }}>Item E</div>
<div style={{ backgroundColor: "#fbcfe8", padding: "16px", textAlign: "center" }}>Item F</div>
</Grid>
</VStack>
{/* Payment method example */}
<VStack gap={2}>
<H3>Payment buttons example</H3>
<Grid cols={3} gap={4}>
<Button variant="outline" style={{ height: "80px", flexDirection: "column" }}>
<div style={{ fontSize: "24px" }}>💳</div>
<span style={{ fontSize: "12px" }}>Card</span>
</Button>
<Button variant="outline" style={{ height: "80px", flexDirection: "column" }}>
<div style={{ fontSize: "24px" }}>🍎</div>
<span style={{ fontSize: "12px" }}>Apple</span>
</Button>
<Button variant="outline" style={{ height: "80px", flexDirection: "column" }}>
<div style={{ fontSize: "24px" }}>💰</div>
<span style={{ fontSize: "12px" }}>PayPal</span>
</Button>
</Grid>
</VStack>
{/* Alignment examples */}
<VStack gap={2}>
<H3>Alignment: v="center" h="center"</H3>
<Grid cols={3} gap={4} v="center" h="center" style={{ height: "128px", backgroundColor: "#f3f4f6" }}>
<div style={{ backgroundColor: "#fecaca", padding: "8px" }}>Item 1</div>
<div style={{ backgroundColor: "#bbf7d0", padding: "8px" }}>Item 2</div>
<div style={{ backgroundColor: "#bfdbfe", padding: "8px" }}>Item 3</div>
</Grid>
</VStack>
<VStack gap={2}>
<H3>Alignment: v="start" h="end"</H3>
<Grid cols={2} gap={4} v="start" h="end" style={{ height: "96px", backgroundColor: "#f3f4f6" }}>
<div style={{ backgroundColor: "#e9d5ff", padding: "8px" }}>Left</div>
<div style={{ backgroundColor: "#fed7aa", padding: "8px" }}>Right</div>
</Grid>
</VStack>
</VStack>
</Section>
)
}