howl/src/input.tsx
Chris Wanstrath 0cad100197 ________ ______ _______ ______ ________
/        |/      \ /       \  /      \ /        |
$$$$$$$$//$$$$$$  |$$$$$$$  |/$$$$$$  |$$$$$$$$/
$$ |__   $$ |  $$ |$$ |__$$ |$$ | _$$/ $$ |__
$$    |  $$ |  $$ |$$    $$< $$ |/    |$$    |
$$$$$/   $$ |  $$ |$$$$$$$  |$$ |$$$$ |$$$$$/
$$ |     $$ \__$$ |$$ |  $$ |$$ \__$$ |$$ |_____
$$ |     $$    $$/ $$ |  $$ |$$    $$/ $$       |
$$/       $$$$$$/  $$/   $$/  $$$$$$/  $$$$$$$$/
2026-01-16 08:33:38 -08:00

193 lines
5.0 KiB
TypeScript

import { define } from 'forge'
import { theme } from './theme'
import { Section } from './section'
import { H2 } from './text'
import { VStack, HStack } from './stack'
export const Input = define('Input', {
parts: {
Wrapper: {
display: 'flex',
flexDirection: 'column',
gap: theme('spacing-1'),
flex: 1,
minWidth: 0,
},
Label: {
base: 'label',
fontSize: theme('fontSize-sm'),
fontWeight: 500,
color: theme('colors-fg'),
},
Field: {
base: 'input',
height: 40,
padding: `${theme('spacing-2')} ${theme('spacing-3')}`,
borderRadius: theme('radius-md'),
border: `1px solid ${theme('colors-border')}`,
background: theme('colors-bg'),
fontSize: theme('fontSize-sm'),
outline: 'none',
flex: 1,
states: {
':focus': {
borderColor: theme('colors-borderActive'),
},
':disabled': {
opacity: 0.5,
cursor: 'not-allowed',
},
},
},
},
variants: {
labelPosition: {
above: {
parts: {
Wrapper: {
flexDirection: 'column',
gap: theme('spacing-1'),
},
},
},
left: {
parts: {
Wrapper: {
flexDirection: 'row',
alignItems: 'center',
gap: theme('spacing-1'),
},
},
},
right: {
parts: {
Wrapper: {
flexDirection: 'row-reverse',
alignItems: 'center',
gap: theme('spacing-1'),
},
},
},
},
},
render({ props, parts: { Root, Wrapper, Label, Field } }) {
const { children, labelPosition = 'above', ...inputProps } = props
if (!children) {
return <Field {...inputProps} />
}
return (
<Wrapper labelPosition={labelPosition}>
<Label for={props.id}>{children}</Label>
<Field {...inputProps} />
</Wrapper>
)
},
})
export type InputProps = Parameters<typeof Input>[0]
export const Test = () => {
return (
<Section style={{ maxWidth: '448px' }}>
{/* Basic inputs */}
<VStack gap={4}>
<H2>Basic Inputs</H2>
<VStack gap={4}>
<Input placeholder="Enter your name" />
<Input type="email" placeholder="Enter your email" />
<Input type="password" placeholder="Enter your password" />
</VStack>
</VStack>
{/* Custom styling */}
<VStack gap={4}>
<H2>Custom Styling</H2>
<VStack gap={4}>
<Input style={{ height: '32px', fontSize: '12px' }} placeholder="Small input" />
<Input placeholder="Default input" />
<Input style={{ height: '48px', fontSize: '16px' }} placeholder="Large input" />
</VStack>
</VStack>
{/* With values */}
<VStack gap={4}>
<H2>With Values</H2>
<VStack gap={4}>
<Input value="John Doe" placeholder="Name" />
<Input type="email" value="john@example.com" placeholder="Email" />
</VStack>
</VStack>
{/* Disabled state */}
<VStack gap={4}>
<H2>Disabled State</H2>
<VStack gap={4}>
<Input disabled placeholder="Disabled input" />
<Input disabled value="Disabled with value" />
</VStack>
</VStack>
{/* Label above */}
<VStack gap={4}>
<H2>Label Above</H2>
<VStack gap={4}>
<Input placeholder="Enter your name">Name</Input>
<Input type="email" placeholder="Enter your email">
Email
</Input>
<Input type="password" placeholder="Enter your password">
Password
</Input>
</VStack>
</VStack>
{/* Label to the left */}
<VStack gap={4}>
<H2>Label Left</H2>
<VStack gap={4}>
<Input labelPosition="left" placeholder="Enter your name">
Name
</Input>
<Input labelPosition="left" type="email" placeholder="Enter your email">
Email
</Input>
<Input labelPosition="left" type="password" placeholder="Enter your password">
Password
</Input>
</VStack>
</VStack>
{/* Label to the right */}
<VStack gap={4}>
<H2>Label Right</H2>
<VStack gap={4}>
<Input labelPosition="right" placeholder="Enter your name">
Name
</Input>
<Input labelPosition="right" type="email" placeholder="Enter your email">
Email
</Input>
<Input labelPosition="right" type="password" placeholder="Enter your password">
Password
</Input>
</VStack>
</VStack>
{/* Horizontal layout */}
<VStack gap={4}>
<H2>Horizontal Layout</H2>
<HStack gap={4}>
<Input placeholder="First name">First</Input>
<Input placeholder="Last name">Last</Input>
<Input placeholder="Age">Age</Input>
</HStack>
</VStack>
</Section>
)
}