diff --git a/package.json b/package.json index 71a5710..05d995e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@panic/web-ui", - "version": "0.1.9", + "version": "0.1.10", "license": "AGPL-3.0-only", "description": "Core components for panic.haus web applications", "type": "module", diff --git a/src/components/Button.stories.tsx b/src/components/Button.stories.tsx index ae0119c..c8eddf1 100644 --- a/src/components/Button.stories.tsx +++ b/src/components/Button.stories.tsx @@ -39,6 +39,12 @@ const meta = { control: 'inline-radio', table: { type: { summary: "'sm' | 'md' | 'lg' | 'full'" } }, }, + width: { + description: 'Button width behavior.', + options: ['sm', 'md', 'lg', 'full'], + control: 'inline-radio', + table: { type: { summary: "'sm' | 'md' | 'lg' | 'full'" } }, + }, to: { description: 'Navigation path. When set, the component renders a ``.', control: 'text', @@ -80,6 +86,7 @@ const meta = { type: 'solid', variant: 'primary', size: 'md', + width: 'md', label: 'Save', }, } satisfies Meta; @@ -132,7 +139,7 @@ export const SizeMatrix: Story = { - + ), }; diff --git a/src/components/Button.tsx b/src/components/Button.tsx index b160cb1..d10fdb0 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -11,6 +11,7 @@ type ButtonProps = { type: ButtonType; variant?: ButtonVariant; size?: ComponentSize; + width?: ComponentSize; to?: string; htmlType?: NativeButtonType; onClick?: MouseEventHandler; @@ -24,14 +25,14 @@ const SIZE_CLASS: Record = { sm: 'h-8 px-3 text-xs', md: 'h-10 px-4 text-sm', lg: 'h-12 px-5 text-base', - full: 'h-10 w-full px-4 text-sm', + full: 'h-10 px-4 text-sm', }; const ICON_ONLY_SIZE_CLASS: Record = { sm: 'h-8 w-8 !p-0', md: 'h-10 w-10 !p-0', lg: 'h-12 w-12 !p-0', - full: 'h-10 w-full !p-0', + full: 'h-10 w-10 !p-0', }; const ICON_CLASS: Record = { @@ -48,6 +49,13 @@ const ICON_ONLY_CLASS: Record = { full: 'h-5 w-5', }; +const WIDTH_CLASS: Record = { + sm: 'max-w-xs', + md: '', + lg: 'max-w-md', + full: 'w-full max-w-none', +}; + const TYPE_CLASS: Record = { solid: 'btn-solid', outlined: 'btn-outlined', @@ -73,6 +81,7 @@ export function Button({ type, variant, size = 'md', + width = 'md', to, htmlType = 'button', onClick, @@ -87,6 +96,7 @@ export function Button({ TYPE_CLASS[type], VARIANT_CLASS[resolvedVariant], isIconOnly ? ICON_ONLY_SIZE_CLASS[size] : SIZE_CLASS[size], + WIDTH_CLASS[width], Icon && label ? 'gap-1.5' : '', disabled ? 'pointer-events-none cursor-not-allowed opacity-45 saturate-50' : '', className, diff --git a/src/components/DatePicker.stories.tsx b/src/components/DatePicker.stories.tsx index 372975d..e853cb1 100644 --- a/src/components/DatePicker.stories.tsx +++ b/src/components/DatePicker.stories.tsx @@ -38,6 +38,12 @@ const meta = { control: 'inline-radio', table: { type: { summary: "'sm' | 'md' | 'lg' | 'full'" } }, }, + width: { + description: 'Input width constraint.', + options: ['sm', 'md', 'lg', 'full'], + control: 'inline-radio', + table: { type: { summary: "'sm' | 'md' | 'lg' | 'full'" } }, + }, layout: { description: 'Label/input layout mode.', options: ['stacked', 'inline'], @@ -105,6 +111,7 @@ const meta = { type: 'datetime-local', value: '', size: 'md', + width: 'md', layout: 'stacked', }, } satisfies Meta; @@ -224,6 +231,7 @@ export const SizeMatrix: Story = { {...args} value={value} size="full" + width="full" onChange={(event) => setValue(event.target.value)} /> diff --git a/src/components/DatePicker.tsx b/src/components/DatePicker.tsx index e8185ee..48583be 100644 --- a/src/components/DatePicker.tsx +++ b/src/components/DatePicker.tsx @@ -9,6 +9,7 @@ export type DatePickerProps = { placeholder?: string; type: DatePickerKind; size?: ComponentSize; + width?: ComponentSize; layout?: Layout; value: string; name?: string; @@ -28,6 +29,7 @@ export function DatePicker({ placeholder = '', type, size = 'md', + width = 'md', layout = 'stacked', value, name, @@ -41,12 +43,12 @@ export function DatePicker({ className = '', inputClassName = '', }: Readonly) { - const containerSizeClass = { + const containerWidthClass = { sm: 'max-w-xs', md: 'max-w-sm', lg: 'max-w-md', full: 'max-w-none', - }[size]; + }[width]; const inputSizeClass = { sm: 'h-8 !text-xs', @@ -63,7 +65,7 @@ export function DatePicker({ return ( {label ? {label} : null} diff --git a/src/components/Dropdown.stories.tsx b/src/components/Dropdown.stories.tsx index 945c497..bba5158 100644 --- a/src/components/Dropdown.stories.tsx +++ b/src/components/Dropdown.stories.tsx @@ -42,6 +42,12 @@ const meta = { control: 'inline-radio', table: { type: { summary: "'sm' | 'md' | 'lg' | 'full'" } }, }, + width: { + description: 'Control width constraint.', + options: ['sm', 'md', 'lg', 'full'], + control: 'inline-radio', + table: { type: { summary: "'sm' | 'md' | 'lg' | 'full'" } }, + }, layout: { description: 'Label/control layout mode.', options: ['stacked', 'inline'], @@ -84,6 +90,7 @@ const meta = { value: 'draft', choices, size: 'md', + width: 'md', layout: 'stacked', }, } satisfies Meta; @@ -147,7 +154,14 @@ export const SizeMatrix: Story = { - + ); }, diff --git a/src/components/Dropdown.tsx b/src/components/Dropdown.tsx index bd910f0..b207547 100644 --- a/src/components/Dropdown.tsx +++ b/src/components/Dropdown.tsx @@ -14,6 +14,7 @@ type DropdownProps = { value: string; choices: DropdownChoice[]; size?: ComponentSize; + width?: ComponentSize; layout?: DropdownLayout; disabled?: boolean; required?: boolean; @@ -28,6 +29,7 @@ export function Dropdown({ value, choices, size = 'md', + width = 'md', layout = 'stacked', disabled = false, required = false, @@ -36,12 +38,12 @@ export function Dropdown({ className = '', selectClassName = '', }: Readonly) { - const containerSizeClass = { + const containerWidthClass = { sm: 'max-w-xs', md: 'max-w-sm', lg: 'max-w-md', full: 'max-w-none', - }[size]; + }[width]; const selectSizeClass = { sm: 'h-8 !text-xs', @@ -62,7 +64,7 @@ export function Dropdown({ return ( {label ? {label} : null} diff --git a/src/components/Form.stories.tsx b/src/components/Form.stories.tsx index efa234e..9973034 100644 --- a/src/components/Form.stories.tsx +++ b/src/components/Form.stories.tsx @@ -80,6 +80,7 @@ export const WithActions: Story = { value={title} onChange={(event) => setTitle(event.target.value)} size="full" + width="full" /> ; @@ -229,6 +236,7 @@ export const SizeMatrix: Story = { {...args} value={value} size="full" + width="full" onChange={(event) => setValue(event.target.value)} /> diff --git a/src/components/InputField.tsx b/src/components/InputField.tsx index 332755d..76105aa 100644 --- a/src/components/InputField.tsx +++ b/src/components/InputField.tsx @@ -12,6 +12,7 @@ type InputFieldProps = { placeholder?: string; type: InputKind; size?: ComponentSize; + width?: ComponentSize; layout?: Layout; value: string; name?: string; @@ -31,6 +32,7 @@ export function InputField({ placeholder = '', type, size = 'md', + width = 'md', layout = 'stacked', value, name, @@ -45,12 +47,12 @@ export function InputField({ inputClassName = '', }: Readonly) { const [showPassword, setShowPassword] = useState(false); - const containerSizeClass = { + const containerWidthClass = { sm: 'max-w-xs', md: 'max-w-sm', lg: 'max-w-md', full: 'max-w-none', - }[size]; + }[width]; const inputSizeClass = { sm: 'h-8 !text-xs', @@ -69,7 +71,7 @@ export function InputField({ return ( {label ? {label} : null}