import { define } from 'forge'
import { theme } from './theme'
import { Section } from './section'
import { H2 } from './text'
import { VStack, HStack } from './stack'
export type SelectOption = {
value: string
label: string
disabled?: boolean
}
// Custom dropdown arrow as base64 SVG
const dropdownArrow = `url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQgNkw4IDEwTDEyIDYiIHN0cm9rZT0iIzZCNzI4MCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+")`
export const Select = define('Select', {
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: 'select',
height: 40,
padding: `${theme('spacing-2')} 32px ${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',
appearance: 'none',
backgroundImage: dropdownArrow,
backgroundRepeat: 'no-repeat',
backgroundPosition: 'right 8px center',
backgroundSize: '16px' as any,
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, options, placeholder, labelPosition = 'above', ...selectProps } = props
// Generate id for label association if needed
const elementId = props.id || (children ? `select-${Math.random().toString(36).slice(2)}` : undefined)
const selectElement = (
{placeholder && (
)}
{options?.map((option: SelectOption) => (
))}
)
if (!children) {
return selectElement
}
return (
{selectElement}
)
},
})
export type SelectProps = Parameters[0]
export const Test = () => {
const months = [
{ value: '01', label: 'January' },
{ value: '02', label: 'February' },
{ value: '03', label: 'March' },
{ value: '04', label: 'April' },
{ value: '05', label: 'May' },
{ value: '06', label: 'June' },
{ value: '07', label: 'July' },
{ value: '08', label: 'August' },
{ value: '09', label: 'September' },
{ value: '10', label: 'October' },
{ value: '11', label: 'November' },
{ value: '12', label: 'December' },
]
const years = Array.from({ length: 10 }, (_, i) => ({
value: String(2024 + i),
label: String(2024 + i),
}))
const countries = [
{ value: 'us', label: 'United States' },
{ value: 'ca', label: 'Canada' },
{ value: 'uk', label: 'United Kingdom' },
{ value: 'de', label: 'Germany' },
{ value: 'fr', label: 'France' },
{ value: 'au', label: 'Australia', disabled: true },
]
return (
{/* Basic selects */}
Basic Selects
{/* With values */}
With Values
{/* Disabled state */}
Disabled State
{/* Label above */}
Label Above
{/* Label to the left */}
Label Left
{/* Label to the right */}
Label Right
{/* Horizontal layout (like card form) */}
Horizontal Layout
)
}