howl/src/input.tsx
2025-11-29 22:38:15 -08:00

184 lines
5.3 KiB
TypeScript

import { Section } from "./section"
import { H2, H3, H4, H5, Text, SmallText } from "./text"
import "hono/jsx"
import type { JSX, FC } from "hono/jsx"
import { VStack, HStack } from "./stack"
export type InputProps = JSX.IntrinsicElements["input"] & {
labelPosition?: "above" | "left" | "right"
children?: any
}
export const Input: FC<InputProps> = (props) => {
const { labelPosition = "above", children, style, ...inputProps } = props
const inputStyle: JSX.CSSProperties = {
height: "40px",
padding: "8px 12px",
borderRadius: "6px",
border: "1px solid #d1d5db",
backgroundColor: "white",
fontSize: "14px",
outline: "none",
...(style as JSX.CSSProperties),
}
if (!children) {
return <input style={inputStyle} {...inputProps} />
}
const labelStyle: JSX.CSSProperties = {
fontSize: "14px",
fontWeight: "500",
color: "#111827",
}
const labelElement = (
<label for={inputProps.id} style={labelStyle}>
{children}
</label>
)
if (labelPosition === "above") {
return (
<div style={{ display: "flex", flexDirection: "column", gap: "4px", flex: 1, minWidth: 0 }}>
{labelElement}
<input style={inputStyle} {...inputProps} />
</div>
)
}
if (labelPosition === "left") {
return (
<div style={{ display: "flex", alignItems: "center", gap: "4px", flex: 1 }}>
{labelElement}
<input style={{ ...inputStyle, flex: 1 }} {...inputProps} />
</div>
)
}
if (labelPosition === "right") {
return (
<div style={{ display: "flex", alignItems: "center", gap: "4px", flex: 1 }}>
<input style={{ ...inputStyle, flex: 1 }} {...inputProps} />
{labelElement}
</div>
)
}
return null
}
export const Test = () => {
return (
<Section 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" style={{ opacity: 0.5, cursor: "not-allowed" }} />
<Input disabled value="Disabled with value" style={{ opacity: 0.5, cursor: "not-allowed" }} />
</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>
{/* Custom styling */}
<VStack gap={4}>
<H2>Custom Input Styling</H2>
<VStack gap={4}>
<Input style={{ borderColor: "#93c5fd" }} placeholder="Custom styled input">
<span style={{ color: "#2563eb", fontWeight: "bold" }}>Custom Label</span>
</Input>
<Input labelPosition="left" placeholder="Required input">
<span style={{ color: "#dc2626", minWidth: "96px" }}>Required Field</span>
</Input>
</VStack>
</VStack>
</VStack>
)
}