swarm-ui: shared form-field kit (TextField, SelectField, Button)
Closes #3448. New ui/ primitives: FormField (shared label+control wrapper), TextField, SelectField, Button — each with a min-height touch target (2.75em ~= 44px, WCAG 2.5.5) per mara's #3447 ask, and a max-width instead of a fixed width so the control caps on desktop without overflowing a narrow/touch viewport. CreateAgentPage's name field + submit button now come from the kit instead of page-scoped CSS; ComponentsPage gets a section for each new primitive with an editable sample.
This commit is contained in:
parent
3643eccf22
commit
ba873926fa
9 changed files with 288 additions and 57 deletions
|
|
@ -5,10 +5,14 @@
|
|||
// primitive gets a section here the same day it's added. Sample data
|
||||
// only, no network calls — this page must render the same whether
|
||||
// swarm-controller's API is up or not.
|
||||
import { useState } from 'preact/hooks';
|
||||
import type { ComponentChildren } from 'preact';
|
||||
import { Panel } from '../ui/panel/Panel.js';
|
||||
import { StatusChip, type ChipTone } from '../ui/status-chip/StatusChip.js';
|
||||
import { Table, type TableColumn } from '../ui/table/Table.js';
|
||||
import { TextField } from '../ui/text-field/TextField.js';
|
||||
import { SelectField, type SelectOption } from '../ui/select-field/SelectField.js';
|
||||
import { Button, type ButtonVariant } from '../ui/button/Button.js';
|
||||
import './ComponentsPage.css';
|
||||
|
||||
function Section({ title, children }: { title: string; children: ComponentChildren }) {
|
||||
|
|
@ -47,6 +51,34 @@ const TABLE_ROWS: Row[] = [
|
|||
{ name: 'beta', detail: 'sample row two' },
|
||||
];
|
||||
|
||||
const SELECT_OPTIONS: SelectOption[] = [
|
||||
{ value: 'alpha', label: 'alpha' },
|
||||
{ value: 'beta', label: 'beta' },
|
||||
];
|
||||
|
||||
const BUTTON_VARIANTS: ButtonVariant[] = ['primary', 'default'];
|
||||
|
||||
// Controlled samples need their own state to actually type/select into —
|
||||
// module-level consts can't do that, hence these two small wrappers
|
||||
// rather than inline JSX in the page body below.
|
||||
function TextFieldSample() {
|
||||
const [value, setValue] = useState('');
|
||||
return <TextField id="sample-text-field" label="agent name" value={value} onInput={setValue} />;
|
||||
}
|
||||
|
||||
function SelectFieldSample() {
|
||||
const [value, setValue] = useState('alpha');
|
||||
return (
|
||||
<SelectField
|
||||
id="sample-select-field"
|
||||
label="hive"
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
options={SELECT_OPTIONS}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function ComponentsPage() {
|
||||
return (
|
||||
<Panel title="components">
|
||||
|
|
@ -82,6 +114,31 @@ export function ComponentsPage() {
|
|||
<Table columns={TABLE_COLUMNS} rows={[]} rowKey={(r) => r.name} />
|
||||
</Sample>
|
||||
</Section>
|
||||
|
||||
<Section title="TextField">
|
||||
<Sample label="editable">
|
||||
<TextFieldSample />
|
||||
</Sample>
|
||||
</Section>
|
||||
|
||||
<Section title="SelectField">
|
||||
<Sample label="editable">
|
||||
<SelectFieldSample />
|
||||
</Sample>
|
||||
</Section>
|
||||
|
||||
<Section title="Button">
|
||||
<div class="components-chip-row">
|
||||
{BUTTON_VARIANTS.map((variant) => (
|
||||
<Sample key={variant} label={variant}>
|
||||
<Button variant={variant}>{variant}</Button>
|
||||
</Sample>
|
||||
))}
|
||||
<Sample label="disabled">
|
||||
<Button disabled>disabled</Button>
|
||||
</Sample>
|
||||
</div>
|
||||
</Section>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue