Add column and field editor
+ at the end of the header row creates a custom field and adds it to this View. Settings › Fields uses the same form.
Playground
Change the controls to try each prop. The code under the preview lists only the props you changed.
import { AddColumnButton } from '@/ui/add-column-and-field-editor';<AddColumnButton />Add a column
- sits in the header row's last cell, through the table's
headerEnd. It opens the field form in a popover; a created field joins this View only and confirms with a toast.
| Candidate | Company | |
|---|---|---|
| Maya Chen | Halcyon Bank |
import { useState } from 'react';
import { AddColumnButton, FieldEditor } from '@/ui/add-column-and-field-editor';
import { Popover, PopoverContent, PopoverHeader, PopoverTitle, PopoverTrigger } from '@/ui/popover';
import { Table, type TableColumn } from '@/ui/table';
import { toast } from '@/ui/toast';
type Candidate = { id: string; name: string; company: string };
const base: TableColumn<Candidate>[] = [
{ key: 'name', label: 'Candidate', cell: (row) => row.name },
{ key: 'company', label: 'Company', width: 180, cell: (row) => row.company },
];
// Press + at the end of the header row, name the field and create it: it joins this table.
export default function AddColumnInTheHeader() {
const [columns, setColumns] = useState(base);
const [open, setOpen] = useState(false);
const [pending, setPending] = useState(false);
return (
<Table
label="Candidates"
columns={columns}
rows={[{ id: 'c1', name: 'Maya Chen', company: 'Halcyon Bank' }]}
headerEnd={
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger render={<AddColumnButton />} />
<PopoverContent align="end">
<PopoverHeader>
<PopoverTitle>New field on Candidates</PopoverTitle>
</PopoverHeader>
<FieldEditor
noun="candidate"
autoFocus
pending={pending}
onCancel={() => setOpen(false)}
onSubmit={(field) => {
setPending(true);
setTimeout(() => {
setPending(false);
setOpen(false);
setColumns((all) => [...all, { key: field.name, label: field.name, cell: () => null }]);
toast.add({ type: 'success', title: `${field.name} added` });
}, 700);
}}
/>
</PopoverContent>
</Popover>
}
/>
);
}Locked
Only Owners and Admins add fields. Everyone else sees a lock that stays focusable, with a tooltip that names who can.
import { AddColumnButton } from '@/ui/add-column-and-field-editor';
// A member who can't add fields sees a lock; its tooltip names who can.
export default function AddColumnLocked() {
return (
<div className="flex items-center gap-200">
<AddColumnButton />
<AddColumnButton locked="Only Owners and Admins can add fields. Ask Joseph." />
</div>
);
}The form
Name and Type first; the form grows only for the type picked: options for selects, format and decimal places for numbers, a default currency for currency. Each option is a lozenge; press it to change its colour. Enter in Add option adds the option without submitting. Create field with no name says what to do. In Settings › Fields the form joins no View.
import { FieldEditor } from '@/ui/add-column-and-field-editor';
// The same form inline in Settings › Fields: the field joins no View. Press Create field with no name to see the message.
// Pick Single select or Number to see the form grow.
export default function AddColumnSettingsForm() {
return (
<div className="w-80 rounded-large bg-surface p-150 shadow-raised">
<FieldEditor noun="candidate" attach="none" onCancel={() => {}} onSubmit={() => {}} />
</div>
);
}When the create fails
The form keeps what was typed and says what happened and what to do.
import { FieldEditor } from '@/ui/add-column-and-field-editor';
// A create that fails keeps what was typed and says what to do.
export default function AddColumnCreateFails() {
return (
<div className="w-80 rounded-large bg-surface p-150 shadow-raised">
<FieldEditor noun="company" onCancel={() => {}} onSubmit={() => {}} error="Couldn't create the field. Check your connection and try again." />
</div>
);
}Value diff
A field's value before and after a change: the old value struck through, an arrow, the new one. Values draw as the table draws them, so a stage stays a pill and money stays money.
Bulk action bar
While rows are selected, a 44px bar covers the toolbar: how many are selected, the actions that apply to all of them, then Clear.