Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 29a4e8c2ee |
@@ -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",
|
||||
|
||||
@@ -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 `<Link>`.',
|
||||
control: 'text',
|
||||
@@ -80,6 +86,7 @@ const meta = {
|
||||
type: 'solid',
|
||||
variant: 'primary',
|
||||
size: 'md',
|
||||
width: 'md',
|
||||
label: 'Save',
|
||||
},
|
||||
} satisfies Meta<typeof Button>;
|
||||
@@ -132,7 +139,7 @@ export const SizeMatrix: Story = {
|
||||
<Button {...args} size="sm" label="Small" />
|
||||
<Button {...args} size="md" label="Medium" />
|
||||
<Button {...args} size="lg" label="Large" />
|
||||
<Button {...args} size="full" label="Full width" />
|
||||
<Button {...args} size="md" width="full" label="Full width" />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ type ButtonProps = {
|
||||
type: ButtonType;
|
||||
variant?: ButtonVariant;
|
||||
size?: ComponentSize;
|
||||
width?: ComponentSize;
|
||||
to?: string;
|
||||
htmlType?: NativeButtonType;
|
||||
onClick?: MouseEventHandler<HTMLElement>;
|
||||
@@ -24,14 +25,14 @@ const SIZE_CLASS: Record<ComponentSize, string> = {
|
||||
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<ComponentSize, string> = {
|
||||
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<ComponentSize, string> = {
|
||||
@@ -48,6 +49,13 @@ const ICON_ONLY_CLASS: Record<ComponentSize, string> = {
|
||||
full: 'h-5 w-5',
|
||||
};
|
||||
|
||||
const WIDTH_CLASS: Record<ComponentSize, string> = {
|
||||
sm: 'max-w-xs',
|
||||
md: '',
|
||||
lg: 'max-w-md',
|
||||
full: 'w-full max-w-none',
|
||||
};
|
||||
|
||||
const TYPE_CLASS: Record<ButtonType, string> = {
|
||||
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,
|
||||
|
||||
@@ -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<typeof DatePicker>;
|
||||
@@ -224,6 +231,7 @@ export const SizeMatrix: Story = {
|
||||
{...args}
|
||||
value={value}
|
||||
size="full"
|
||||
width="full"
|
||||
onChange={(event) => setValue(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -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<DatePickerProps>) {
|
||||
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
|
||||
className={`${wrapperClass} text-sm font-medium ${disabled ? 'ui-label-disabled' : 'ui-label'} ${containerSizeClass} ${className}`.trim()}
|
||||
className={`${wrapperClass} text-sm font-medium ${disabled ? 'ui-label-disabled' : 'ui-label'} ${containerWidthClass} ${className}`.trim()}
|
||||
>
|
||||
{label ? <span className={labelClass}>{label}</span> : null}
|
||||
<div className={inputWrapperClass}>
|
||||
|
||||
@@ -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<typeof Dropdown>;
|
||||
@@ -147,7 +154,14 @@ export const SizeMatrix: Story = {
|
||||
<Dropdown {...args} value={value} size="sm" label="Small" onChange={setValue} />
|
||||
<Dropdown {...args} value={value} size="md" label="Medium" onChange={setValue} />
|
||||
<Dropdown {...args} value={value} size="lg" label="Large" onChange={setValue} />
|
||||
<Dropdown {...args} value={value} size="full" label="Full" onChange={setValue} />
|
||||
<Dropdown
|
||||
{...args}
|
||||
value={value}
|
||||
size="full"
|
||||
width="full"
|
||||
label="Full"
|
||||
onChange={setValue}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -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<DropdownProps>) {
|
||||
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
|
||||
className={`${wrapperClass} text-sm font-medium ${disabled ? 'ui-label-disabled' : 'ui-label'} ${containerSizeClass} ${className}`.trim()}
|
||||
className={`${wrapperClass} text-sm font-medium ${disabled ? 'ui-label-disabled' : 'ui-label'} ${containerWidthClass} ${className}`.trim()}
|
||||
>
|
||||
{label ? <span className={labelClass}>{label}</span> : null}
|
||||
<div className={selectWrapperClass}>
|
||||
|
||||
@@ -80,6 +80,7 @@ export const WithActions: Story = {
|
||||
value={title}
|
||||
onChange={(event) => setTitle(event.target.value)}
|
||||
size="full"
|
||||
width="full"
|
||||
/>
|
||||
</div>
|
||||
<Dropdown
|
||||
|
||||
@@ -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'],
|
||||
@@ -107,6 +113,7 @@ const meta = {
|
||||
placeholder: 'name@example.com',
|
||||
value: '',
|
||||
size: 'md',
|
||||
width: 'md',
|
||||
layout: 'stacked',
|
||||
},
|
||||
} satisfies Meta<typeof InputField>;
|
||||
@@ -229,6 +236,7 @@ export const SizeMatrix: Story = {
|
||||
{...args}
|
||||
value={value}
|
||||
size="full"
|
||||
width="full"
|
||||
onChange={(event) => setValue(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -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<InputFieldProps>) {
|
||||
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
|
||||
className={`${wrapperClass} text-sm font-medium ${disabled ? 'ui-label-disabled' : 'ui-label'} ${containerSizeClass} ${className}`.trim()}
|
||||
className={`${wrapperClass} text-sm font-medium ${disabled ? 'ui-label-disabled' : 'ui-label'} ${containerWidthClass} ${className}`.trim()}
|
||||
>
|
||||
{label ? <span className={labelClass}>{label}</span> : null}
|
||||
<div className={inputWrapperClass}>
|
||||
|
||||
Reference in New Issue
Block a user