@twoux/ui 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +61 -0
- package/LICENSES/Apache-2.0.txt +202 -0
- package/NOTICE.md +64 -0
- package/README.md +211 -2
- package/SHADCN-DIFF.md +228 -0
- package/agent/AGENTS.md +358 -0
- package/agent/docs-md/accessibility.md +49 -0
- package/agent/docs-md/action-catalogue.md +81 -0
- package/agent/docs-md/actions.md +74 -0
- package/agent/docs-md/agent-activity-log.md +70 -0
- package/agent/docs-md/agent-permissions.md +82 -0
- package/agent/docs-md/agent-run.md +90 -0
- package/agent/docs-md/agents-on-behalf.md +74 -0
- package/agent/docs-md/ai-column.md +85 -0
- package/agent/docs-md/ai-origin-label.md +70 -0
- package/agent/docs-md/announcing-features.md +73 -0
- package/agent/docs-md/answer-actions.md +81 -0
- package/agent/docs-md/app-shell.md +114 -0
- package/agent/docs-md/artifact-card.md +89 -0
- package/agent/docs-md/ask-on-screen.md +72 -0
- package/agent/docs-md/assistant-page.md +100 -0
- package/agent/docs-md/assistant-panel.md +142 -0
- package/agent/docs-md/avatar.md +82 -0
- package/agent/docs-md/banner.md +106 -0
- package/agent/docs-md/board.md +163 -0
- package/agent/docs-md/breadcrumb.md +89 -0
- package/agent/docs-md/breakdown-table.md +97 -0
- package/agent/docs-md/bulk-action-bar.md +112 -0
- package/agent/docs-md/button.md +293 -0
- package/agent/docs-md/card.md +115 -0
- package/agent/docs-md/change-list.md +99 -0
- package/agent/docs-md/change-request.md +166 -0
- package/agent/docs-md/chart-area.md +87 -0
- package/agent/docs-md/chart-bar.md +100 -0
- package/agent/docs-md/chart-box-plot.md +88 -0
- package/agent/docs-md/chart-bullet.md +89 -0
- package/agent/docs-md/chart-candlestick.md +112 -0
- package/agent/docs-md/chart-card-line.md +120 -0
- package/agent/docs-md/chart-donut.md +90 -0
- package/agent/docs-md/chart-drill-down.md +72 -0
- package/agent/docs-md/chart-funnel.md +71 -0
- package/agent/docs-md/chart-heatmap.md +76 -0
- package/agent/docs-md/chart-histogram.md +81 -0
- package/agent/docs-md/chart-map.md +165 -0
- package/agent/docs-md/chart-mixed.md +88 -0
- package/agent/docs-md/chart-price.md +92 -0
- package/agent/docs-md/chart-radial-bars.md +109 -0
- package/agent/docs-md/chart-radial-lines.md +111 -0
- package/agent/docs-md/chart-sankey.md +104 -0
- package/agent/docs-md/chart-scatter.md +87 -0
- package/agent/docs-md/chart-share-bar.md +89 -0
- package/agent/docs-md/chart-treemap.md +98 -0
- package/agent/docs-md/chart-waterfall.md +93 -0
- package/agent/docs-md/checkbox.md +131 -0
- package/agent/docs-md/choosing-selection-control.md +71 -0
- package/agent/docs-md/citations-sources.md +79 -0
- package/agent/docs-md/citing-sources.md +63 -0
- package/agent/docs-md/clarify-before-acting.md +69 -0
- package/agent/docs-md/clarifying-question.md +92 -0
- package/agent/docs-md/code-block.md +174 -0
- package/agent/docs-md/colours.md +87 -0
- package/agent/docs-md/columns-menu.md +140 -0
- package/agent/docs-md/combobox.md +161 -0
- package/agent/docs-md/command-palette.md +126 -0
- package/agent/docs-md/confidence-note.md +87 -0
- package/agent/docs-md/configurable-dashboard.md +76 -0
- package/agent/docs-md/confirmation-dialog.md +93 -0
- package/agent/docs-md/connected-tools.md +80 -0
- package/agent/docs-md/connector-picker.md +127 -0
- package/agent/docs-md/consistency.md +53 -0
- package/agent/docs-md/content-density.md +61 -0
- package/agent/docs-md/conversation-history.md +78 -0
- package/agent/docs-md/create-record.md +80 -0
- package/agent/docs-md/csv-import.md +88 -0
- package/agent/docs-md/dashboard-items.md +78 -0
- package/agent/docs-md/data-export.md +61 -0
- package/agent/docs-md/data-grid.md +137 -0
- package/agent/docs-md/data-vis-colours.md +76 -0
- package/agent/docs-md/data-visualisation.md +76 -0
- package/agent/docs-md/date-picker.md +178 -0
- package/agent/docs-md/delete-record.md +75 -0
- package/agent/docs-md/dense-table.md +495 -0
- package/agent/docs-md/design-tokens.md +53 -0
- package/agent/docs-md/disabled-read-only.md +75 -0
- package/agent/docs-md/drag-and-drop.md +74 -0
- package/agent/docs-md/dropdown-menu.md +137 -0
- package/agent/docs-md/edit-record.md +73 -0
- package/agent/docs-md/empty-loading-error.md +78 -0
- package/agent/docs-md/empty-state.md +128 -0
- package/agent/docs-md/error-messages.md +75 -0
- package/agent/docs-md/error-state.md +123 -0
- package/agent/docs-md/export-menu.md +86 -0
- package/agent/docs-md/feedback-prompt.md +88 -0
- package/agent/docs-md/field-mapping.md +78 -0
- package/agent/docs-md/file-drop.md +110 -0
- package/agent/docs-md/filter-bar.md +138 -0
- package/agent/docs-md/filter-panel.md +103 -0
- package/agent/docs-md/filtering-search.md +79 -0
- package/agent/docs-md/follow-ups-starters.md +79 -0
- package/agent/docs-md/form-validation.md +95 -0
- package/agent/docs-md/generated-columns.md +65 -0
- package/agent/docs-md/generated-drafts-files.md +71 -0
- package/agent/docs-md/grouped-list.md +89 -0
- package/agent/docs-md/guide-cue.md +170 -0
- package/agent/docs-md/help-panel.md +103 -0
- package/agent/docs-md/help-system.md +74 -0
- package/agent/docs-md/iconography.md +49 -0
- package/agent/docs-md/inbox.md +108 -0
- package/agent/docs-md/inline-editing-validation.md +80 -0
- package/agent/docs-md/inline-input-in-answer.md +70 -0
- package/agent/docs-md/items-palette.md +116 -0
- package/agent/docs-md/keyboard-model.md +47 -0
- package/agent/docs-md/keyboard-navigation.md +68 -0
- package/agent/docs-md/keyboard-shortcut-hint.md +87 -0
- package/agent/docs-md/keyboard-shortcut-sheet.md +90 -0
- package/agent/docs-md/labelling-ai-output.md +66 -0
- package/agent/docs-md/layout.md +47 -0
- package/agent/docs-md/list-table.md +254 -0
- package/agent/docs-md/loading-skeleton.md +91 -0
- package/agent/docs-md/managing-agents.md +71 -0
- package/agent/docs-md/message-composer.md +105 -0
- package/agent/docs-md/migrating.md +49 -0
- package/agent/docs-md/motion.md +93 -0
- package/agent/docs-md/navigation.md +76 -0
- package/agent/docs-md/notification-channels.md +65 -0
- package/agent/docs-md/notifications-decision.md +100 -0
- package/agent/docs-md/onboarding-step.md +114 -0
- package/agent/docs-md/order-book.md +108 -0
- package/agent/docs-md/page-header.md +96 -0
- package/agent/docs-md/pagination.md +88 -0
- package/agent/docs-md/pane-layout.md +105 -0
- package/agent/docs-md/period-comparison.md +66 -0
- package/agent/docs-md/plan-card.md +120 -0
- package/agent/docs-md/popover-rules.md +88 -0
- package/agent/docs-md/prompt-editor.md +90 -0
- package/agent/docs-md/propose-apply.md +65 -0
- package/agent/docs-md/provider-logo.md +97 -0
- package/agent/docs-md/radio.md +83 -0
- package/agent/docs-md/ranked-list.md +102 -0
- package/agent/docs-md/reasoning-progress.md +72 -0
- package/agent/docs-md/record-detail.md +71 -0
- package/agent/docs-md/regenerating-answers.md +70 -0
- package/agent/docs-md/run-status-bar.md +94 -0
- package/agent/docs-md/saved-tasks-schedules.md +63 -0
- package/agent/docs-md/saved-views.md +149 -0
- package/agent/docs-md/saving-views.md +68 -0
- package/agent/docs-md/scheduler.md +258 -0
- package/agent/docs-md/secondary-panels.md +72 -0
- package/agent/docs-md/segmented-control.md +88 -0
- package/agent/docs-md/selection-bulk-actions.md +70 -0
- package/agent/docs-md/separator.md +75 -0
- package/agent/docs-md/setup-checklist.md +92 -0
- package/agent/docs-md/shape-elevation.md +55 -0
- package/agent/docs-md/sheet-grid.md +211 -0
- package/agent/docs-md/side-panel.md +134 -0
- package/agent/docs-md/sign-in.md +114 -0
- package/agent/docs-md/spacing.md +55 -0
- package/agent/docs-md/split-button.md +100 -0
- package/agent/docs-md/stat-compare.md +82 -0
- package/agent/docs-md/static-dashboard.md +75 -0
- package/agent/docs-md/status-badge.md +140 -0
- package/agent/docs-md/step-card.md +75 -0
- package/agent/docs-md/stepper.md +92 -0
- package/agent/docs-md/suggested-changes.md +91 -0
- package/agent/docs-md/switch.md +78 -0
- package/agent/docs-md/table-settings.md +81 -0
- package/agent/docs-md/tabs.md +85 -0
- package/agent/docs-md/tag-input.md +108 -0
- package/agent/docs-md/tag.md +79 -0
- package/agent/docs-md/task-builder.md +87 -0
- package/agent/docs-md/team-instructions.md +72 -0
- package/agent/docs-md/templates-gallery.md +85 -0
- package/agent/docs-md/text-input.md +160 -0
- package/agent/docs-md/thinking-steps.md +89 -0
- package/agent/docs-md/timeline.md +189 -0
- package/agent/docs-md/timestamp.md +84 -0
- package/agent/docs-md/timestamps.md +70 -0
- package/agent/docs-md/toast.md +117 -0
- package/agent/docs-md/tooltip.md +90 -0
- package/agent/docs-md/typography.md +62 -0
- package/agent/docs-md/undo-destructive.md +66 -0
- package/agent/docs-md/unsaved-changes.md +71 -0
- package/agent/docs-md/usage-meter.md +80 -0
- package/agent/docs-md/user-feedback.md +71 -0
- package/agent/docs-md/voice-and-tone.md +60 -0
- package/agent/docs-md/why-choice.md +93 -0
- package/agent/docs-md/workspace-onboarding.md +74 -0
- package/agent/docs-md/writing.md +48 -0
- package/agent/llms-full.txt +18369 -0
- package/agent/llms.txt +198 -0
- package/agent/skills/twoux-audit/SKILL.md +49 -0
- package/agent/skills/twoux-build/SKILL.md +39 -0
- package/agent/skills/twoux-design/SKILL.md +54 -0
- package/agent/skills/twoux-write/SKILL.md +32 -0
- package/bin/check.mjs +123 -0
- package/bin/docs.mjs +24 -0
- package/bin/guard.mjs +27 -0
- package/bin/init.mjs +113 -0
- package/bin/knowledge.mjs +87 -0
- package/bin/mcp.mjs +116 -0
- package/bin/theme.mjs +168 -0
- package/bin/twoux.mjs +25 -0
- package/package.json +80 -3
- package/registry/jobs.json +1028 -0
- package/registry/palette.json +770 -0
- package/registry/rules.json +913 -0
- package/src/components/agent-review.tsx +364 -0
- package/src/components/ai-basics.tsx +212 -0
- package/src/components/ai-work.tsx +304 -0
- package/src/components/ai.tsx +227 -0
- package/src/components/alert-dialog.tsx +203 -0
- package/src/components/alert.tsx +86 -0
- package/src/components/avatar.tsx +114 -0
- package/src/components/badge.tsx +59 -0
- package/src/components/board.tsx +397 -0
- package/src/components/breadcrumb.tsx +114 -0
- package/src/components/button-group.tsx +90 -0
- package/src/components/button.tsx +109 -0
- package/src/components/calendar.tsx +247 -0
- package/src/components/card.tsx +102 -0
- package/src/components/chart-map.tsx +336 -0
- package/src/components/charts-more.tsx +1331 -0
- package/src/components/charts-radial.tsx +328 -0
- package/src/components/charts.tsx +390 -0
- package/src/components/checkbox.tsx +40 -0
- package/src/components/code.tsx +139 -0
- package/src/components/collapsible.tsx +37 -0
- package/src/components/combobox.tsx +339 -0
- package/src/components/command-extras.tsx +11 -0
- package/src/components/command.tsx +196 -0
- package/src/components/data-grid.tsx +213 -0
- package/src/components/data-table.tsx +334 -0
- package/src/components/data-views.tsx +740 -0
- package/src/components/date-picker.tsx +686 -0
- package/src/components/dense-table.tsx +207 -0
- package/src/components/dialog.tsx +162 -0
- package/src/components/dropdown-menu.tsx +264 -0
- package/src/components/empty.tsx +114 -0
- package/src/components/feedback-prompt.tsx +80 -0
- package/src/components/field.tsx +254 -0
- package/src/components/fields.tsx +112 -0
- package/src/components/flow.tsx +840 -0
- package/src/components/guide-cue.tsx +273 -0
- package/src/components/inbox.tsx +113 -0
- package/src/components/input-group.tsx +176 -0
- package/src/components/input.tsx +26 -0
- package/src/components/kbd.tsx +34 -0
- package/src/components/label.tsx +27 -0
- package/src/components/labels.tsx +176 -0
- package/src/components/motion.tsx +136 -0
- package/src/components/onboarding.tsx +454 -0
- package/src/components/order-book.tsx +185 -0
- package/src/components/pane-layout.tsx +417 -0
- package/src/components/popover.tsx +96 -0
- package/src/components/provider-logos.data.ts +49 -0
- package/src/components/radio-group.tsx +52 -0
- package/src/components/scheduler.tsx +997 -0
- package/src/components/segmented-control.tsx +28 -0
- package/src/components/select.tsx +195 -0
- package/src/components/separator.tsx +31 -0
- package/src/components/sheet.tsx +148 -0
- package/src/components/shell.tsx +426 -0
- package/src/components/shortcut-sheet.tsx +83 -0
- package/src/components/skeleton.tsx +18 -0
- package/src/components/sonner.tsx +107 -0
- package/src/components/spinner.tsx +13 -0
- package/src/components/split-button.tsx +58 -0
- package/src/components/stepper.tsx +78 -0
- package/src/components/switch.tsx +43 -0
- package/src/components/table.tsx +126 -0
- package/src/components/tabs.tsx +118 -0
- package/src/components/tag-input.tsx +253 -0
- package/src/components/textarea.tsx +21 -0
- package/src/components/theme.tsx +80 -0
- package/src/components/timeline.tsx +408 -0
- package/src/components/toggle-group.tsx +87 -0
- package/src/components/toggle.tsx +54 -0
- package/src/components/tooltip.tsx +65 -0
- package/src/components/workspace-actions.tsx +294 -0
- package/src/components/workspace-import.tsx +465 -0
- package/src/components/workspace.tsx +1884 -0
- package/src/index.ts +80 -0
- package/src/lib/active-indicator.tsx +53 -0
- package/src/lib/chart-kit.tsx +72 -0
- package/src/lib/cn.ts +6 -0
- package/src/lib/shortcuts.tsx +61 -0
- package/src/lib/workspace.tsx +10 -0
- package/src/shadcn/alert-dialog.tsx +196 -0
- package/src/shadcn/alert.tsx +65 -0
- package/src/shadcn/avatar.tsx +108 -0
- package/src/shadcn/badge.tsx +47 -0
- package/src/shadcn/breadcrumb.tsx +108 -0
- package/src/shadcn/button-group.tsx +83 -0
- package/src/shadcn/button.tsx +63 -0
- package/src/shadcn/calendar.tsx +220 -0
- package/src/shadcn/card.tsx +91 -0
- package/src/shadcn/checkbox.tsx +31 -0
- package/src/shadcn/collapsible.tsx +33 -0
- package/src/shadcn/command.tsx +184 -0
- package/src/shadcn/dialog.tsx +158 -0
- package/src/shadcn/dropdown-menu.tsx +256 -0
- package/src/shadcn/empty.tsx +103 -0
- package/src/shadcn/field.tsx +248 -0
- package/src/shadcn/input-group.tsx +170 -0
- package/src/shadcn/input.tsx +20 -0
- package/src/shadcn/kbd.tsx +28 -0
- package/src/shadcn/label.tsx +23 -0
- package/src/shadcn/popover.tsx +88 -0
- package/src/shadcn/radio-group.tsx +44 -0
- package/src/shadcn/select.tsx +189 -0
- package/src/shadcn/separator.tsx +27 -0
- package/src/shadcn/sheet.tsx +142 -0
- package/src/shadcn/skeleton.tsx +13 -0
- package/src/shadcn/sonner.tsx +40 -0
- package/src/shadcn/switch.tsx +34 -0
- package/src/shadcn/table.tsx +115 -0
- package/src/shadcn/tabs.tsx +90 -0
- package/src/shadcn/textarea.tsx +17 -0
- package/src/shadcn/toggle-group.tsx +83 -0
- package/src/shadcn/toggle.tsx +46 -0
- package/src/shadcn/tooltip.tsx +56 -0
- package/src/styles/palette.css +127 -0
- package/src/styles/theme.css +35 -0
- package/src/styles/twoux.css +236 -0
- package/theme.json +14 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Filter panel
|
|
2
|
+
|
|
3
|
+
A side panel of collapsible filter sections for searches with many fields, with live counts per option and a main action that says how many rows it will act on.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: Data · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- Search across many dimensions at once, such as candidates by location, team, experience and salary. That is faceted navigation.
|
|
10
|
+
- People set filters across several categories, which work best placed vertically on the left.
|
|
11
|
+
- People need counts per option to avoid zero-result combinations.
|
|
12
|
+
|
|
13
|
+
## When not to use
|
|
14
|
+
|
|
15
|
+
- For two or three filters on a table. Use the TwoUX Filter bar so filters stay inline.
|
|
16
|
+
- Inside a dropdown or menu when there are several categories; multiple categories should never go in a menu.
|
|
17
|
+
- For saved combinations people reuse. Use TwoUX Saved views.
|
|
18
|
+
|
|
19
|
+
## Structure
|
|
20
|
+
|
|
21
|
+
1. Header (Filters, count of active filters, Clear all)
|
|
22
|
+
2. Collapsible sections with set-value count
|
|
23
|
+
3. Options with result counts
|
|
24
|
+
4. Results table
|
|
25
|
+
5. Footer ("Showing 5 of 412 candidates", Save search, main action with count)
|
|
26
|
+
|
|
27
|
+
## Guidelines
|
|
28
|
+
|
|
29
|
+
- Sections collapse, and each closed section shows how many values are set.
|
|
30
|
+
- Show the most used sections open and the rest collapsed.
|
|
31
|
+
- Each option shows how many results it matches.
|
|
32
|
+
- The result count updates live and the main action states its scope, "Add 412 to pipeline".
|
|
33
|
+
- Offer Clear all for the panel and a clear per section.
|
|
34
|
+
|
|
35
|
+
## Content
|
|
36
|
+
|
|
37
|
+
- Section titles are the field names people use, not database names.
|
|
38
|
+
- Write "Any team" or "Any time" when a section has no value, so the default is explicit.
|
|
39
|
+
- Keep counts in the same place on every option so they scan down the column.
|
|
40
|
+
|
|
41
|
+
## Accessibility
|
|
42
|
+
|
|
43
|
+
- APG disclosure pattern: each section header is a button with aria-expanded; Enter or Space toggles it.
|
|
44
|
+
- 4.1.3 Status Messages: the live result count is announced as a full phrase, "412 candidates".
|
|
45
|
+
- 2.1.1 Keyboard: every section, option and Clear all can be reached and used from the keyboard.
|
|
46
|
+
- 1.3.1 Info and Relationships: options are grouped under their section with a group label.
|
|
47
|
+
|
|
48
|
+
## Keyboard
|
|
49
|
+
|
|
50
|
+
| Keys | Action |
|
|
51
|
+
|---|---|
|
|
52
|
+
| Tab / ⇧Tab | Move between sections and controls |
|
|
53
|
+
| Enter / Space (section) | Expand or collapse the section |
|
|
54
|
+
| Space (option) | Tick or untick an option |
|
|
55
|
+
|
|
56
|
+
## Examples
|
|
57
|
+
|
|
58
|
+
### candidates
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { Button, FilterPanel, type FilterSection, Table, TableBody, TableCell, TableRow, toast } from "@twoux/ui";
|
|
62
|
+
import { useState } from "react";
|
|
63
|
+
|
|
64
|
+
export const meta = { title: "Search candidates by many fields", description: "Sections collapse and show how many values are set. Each option shows its count. The footer says what the main action will affect.", order: 1 };
|
|
65
|
+
|
|
66
|
+
const PEOPLE = [
|
|
67
|
+
{ name: "Amara Okafor", stage: "Screen", source: "Referral", location: "Leeds" },
|
|
68
|
+
{ name: "Tom Becker", stage: "Applied", source: "Job board", location: "Manchester" },
|
|
69
|
+
{ name: "Priya Shah", stage: "Interview", source: "Careers page", location: "London" },
|
|
70
|
+
{ name: "Liam Walsh", stage: "Applied", source: "Job board", location: "Leeds" },
|
|
71
|
+
{ name: "Sofia Romano", stage: "Offer", source: "Agency", location: "Remote (UK)" },
|
|
72
|
+
{ name: "Hana Kim", stage: "Screen", source: "Job board", location: "Glasgow" },
|
|
73
|
+
];
|
|
74
|
+
type K = "stage" | "source" | "location";
|
|
75
|
+
|
|
76
|
+
export default function Example() {
|
|
77
|
+
const [value, setValue] = useState<Record<string, string[]>>({ stage: ["Applied", "Screen"] });
|
|
78
|
+
const match = (p: (typeof PEOPLE)[number], skip?: K) => (Object.entries(value) as [K, string[]][]).every(([k, v]) => k === skip || !v.length || v.includes(p[k]));
|
|
79
|
+
const rows = PEOPLE.filter((p) => match(p));
|
|
80
|
+
const section = (id: K, title: string, open?: boolean): FilterSection => ({ id, title, defaultOpen: open, options: [...new Set(PEOPLE.map((p) => p[id]))].sort().map((v) => ({ value: v, label: v, count: PEOPLE.filter((p) => match(p, id) && p[id] === v).length })) });
|
|
81
|
+
return (
|
|
82
|
+
<div className="grid h-96 w-full grid-cols-[240px_1fr] overflow-hidden rounded-sm border border-line bg-surface max-md:grid-cols-1">
|
|
83
|
+
<FilterPanel className="border-r border-line" value={value} onChange={setValue} sections={[section("stage", "Stage", true), section("source", "Source", true), section("location", "Location")]} />
|
|
84
|
+
<div className="flex min-h-0 flex-col">
|
|
85
|
+
<div className="min-h-0 flex-1 overflow-auto">
|
|
86
|
+
<Table aria-label="Candidates"><TableBody>{rows.map((p) => <TableRow key={p.name}><TableCell className="font-medium text-ink">{p.name}</TableCell><TableCell className="text-ink-2">{p.stage}</TableCell><TableCell className="text-ink-2">{p.location}</TableCell></TableRow>)}</TableBody></Table>
|
|
87
|
+
</div>
|
|
88
|
+
<div className="flex items-center justify-between gap-2 border-t border-line px-3 py-2">
|
|
89
|
+
<span className="tabular text-sm text-ink-2" aria-live="polite">Showing {rows.length} of {PEOPLE.length} candidates</span>
|
|
90
|
+
<Button size="sm" disabled={!rows.length} onClick={() => toast.success(`Added ${rows.length} to the pipeline`)}>Add {rows.length} to pipeline</Button>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Related
|
|
99
|
+
|
|
100
|
+
- Filter pills and filter bar
|
|
101
|
+
- Saved views
|
|
102
|
+
- List table
|
|
103
|
+
- Bulk action bar
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Filtering and search
|
|
2
|
+
|
|
3
|
+
Narrow a list to the rows that matter with filter pills that apply as you tick, show counts, and live in the URL.
|
|
4
|
+
|
|
5
|
+
Kind: pattern · Group: Patterns: general
|
|
6
|
+
|
|
7
|
+
**Problem:** Hiring teams work through hundreds of listings and candidates and need to narrow them quickly. If every screen filters differently, people relearn the controls each time and links to a filtered list break.
|
|
8
|
+
|
|
9
|
+
**Solution:** Use the filter bar (pills) for list pages and the filter panel only for search across many fields. List values apply on tick with match counts, typed values apply on Enter, and the whole query is stored in the URL.
|
|
10
|
+
|
|
11
|
+
**Built from:** Filter pills and filter bar, Filter panel, List table, Data grid, Pagination and counts, Empty state, Saved views
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
1. Show one or two of the most used fields as dashed pills next to Filter (F). Everything else sits behind Filter in a type-ahead list grouped into most used and all fields.
|
|
16
|
+
2. Opening a pill shows its values with the number of rows each one matches. Ticking a list value applies it at once; numbers and dates apply on Enter.
|
|
17
|
+
3. An applied filter becomes a solid pill that reads as a sentence: field, operator, value, for example "Status is any of Live, Expiring". Its remove button sits on the left.
|
|
18
|
+
4. Clear and the result count sit straight after the pills, so the person sees what is applied and how many rows match without opening anything.
|
|
19
|
+
5. Every change to a filter updates the URL, so reload, back and a shared link all reproduce the same list.
|
|
20
|
+
6. If nothing matches, the table is replaced by a zero results state with Clear filters as the action.
|
|
21
|
+
|
|
22
|
+
## When to use
|
|
23
|
+
|
|
24
|
+
- On any list page with more than one screen of rows: listings, candidates, pipeline, inbox.
|
|
25
|
+
- When people mostly narrow by two or three known fields such as status, location or team.
|
|
26
|
+
- Use the filter panel instead of pills when there are many fields to filter by, as in candidate search, and the panel can stay open beside the results.
|
|
27
|
+
|
|
28
|
+
## When not to use
|
|
29
|
+
|
|
30
|
+
- To find one known record by name or ID. Use search in the command palette (⌘K) or the view's search (/) instead.
|
|
31
|
+
- For view settings that always have a value, such as a chart's date range or Daily vs Weekly. Pills are removable, so these use a rectangle (segmented control or dropdown) instead.
|
|
32
|
+
- When several tables share one page. Filters in the URL become ambiguous, so filter each table locally and do not persist.
|
|
33
|
+
|
|
34
|
+
## Guidelines
|
|
35
|
+
|
|
36
|
+
- Filters act on the whole collection, not just the rows loaded on screen. The count says so: "Showing 5 of 412 candidates".
|
|
37
|
+
- Apply on tick because TwoUX's queries return fast; filter interactively when results come back almost instantly and in a batch when they are slow.
|
|
38
|
+
- If a query will take more than about a second, dim the results and show a progress indicator rather than redrawing after every tick.
|
|
39
|
+
- Show match counts on every list option so people avoid combinations that return nothing.
|
|
40
|
+
- Never put a filter row inside each column header. The pill bar keeps the whole query in one visible, shareable place.
|
|
41
|
+
- Validate filter values read from the URL and fall back to defaults when they are invalid. Never put personal data in the URL.
|
|
42
|
+
- Offer a plain search box alone when people know the exact name, such as a candidate's surname. Add pills when they choose among known values, such as stage or location.
|
|
43
|
+
- Show and/or wording only once two or more filters are applied; one filter reads as a plain sentence.
|
|
44
|
+
- Returning to a list by Back, the breadcrumb or the sidebar restores the filters the person left with.
|
|
45
|
+
- When a page has two filterable lists, only the main one writes its filters to the URL; the other keeps its filters on screen only.
|
|
46
|
+
|
|
47
|
+
## Content
|
|
48
|
+
|
|
49
|
+
- Pill text follows field, operator, value in sentence case: "Posted is in the last 30 days".
|
|
50
|
+
- Zero results heading: "No listings match these filters", then one sentence suggesting the likeliest fix, then a Clear filters button.
|
|
51
|
+
- Counts use the noun of the list: "14 listings", not "14 results".
|
|
52
|
+
|
|
53
|
+
## Accessibility
|
|
54
|
+
|
|
55
|
+
- Announce the new result count in a polite live region after each change, without moving focus.
|
|
56
|
+
- Each pill's remove button is its own Tab stop, named after its filter: "Remove Status filter".
|
|
57
|
+
- The value list is a combobox with a listbox: arrow keys move, Space ticks, Enter applies a typed value, Esc closes and returns focus to the pill.
|
|
58
|
+
- F to open Filter is a single-key shortcut, so it can be turned off in the shortcut sheet.
|
|
59
|
+
|
|
60
|
+
## Do
|
|
61
|
+
|
|
62
|
+
- Do keep the active query visible as pills at all times.
|
|
63
|
+
- Do show how many rows each value matches.
|
|
64
|
+
- Do write filters to the URL on every change.
|
|
65
|
+
|
|
66
|
+
## Don't
|
|
67
|
+
|
|
68
|
+
- Don't hide applied filters behind a single "3 filters" badge on desktop.
|
|
69
|
+
- Don't add an Apply button to a fast list filter; it adds a step for nothing.
|
|
70
|
+
- Don't use the empty state illustration and copy for a failed load. That is an error state.
|
|
71
|
+
|
|
72
|
+
## Related
|
|
73
|
+
|
|
74
|
+
- Saved views
|
|
75
|
+
- Selection and bulk actions
|
|
76
|
+
- Empty, loading and error states
|
|
77
|
+
- Keyboard-first navigation
|
|
78
|
+
- Filter pills and filter bar
|
|
79
|
+
- Filter panel
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Follow-ups and starters
|
|
2
|
+
|
|
3
|
+
Follow-ups continue an answer as muted rows under a "Follow-ups" label; starters appear only in the empty state as suggested tasks based on what is on screen.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: AI and agents · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- In the empty state, to show two or three tasks based on the selection, instead of a blank "How can I help?".
|
|
10
|
+
- After an answer, to offer one or two ways to go deeper and one to widen.
|
|
11
|
+
- To prompt checking, such as "Show who viewed these but didn't apply", not only moving on.
|
|
12
|
+
|
|
13
|
+
## When not to use
|
|
14
|
+
|
|
15
|
+
- Asking for missing details. Use the Clarifying question card (clarifying-question).
|
|
16
|
+
- Reusable workflows. Use the Templates gallery (templates-gallery) or saved tasks via / (message-composer).
|
|
17
|
+
- Inside an answer's body. Follow-ups sit after the answer actions, never inside the answer.
|
|
18
|
+
|
|
19
|
+
## Structure
|
|
20
|
+
|
|
21
|
+
1. Starters: "Try asking" label, bulb icon, two or three chips, Hide
|
|
22
|
+
2. Follow-ups: "Follow-ups" label, rows with a ↳ icon, muted text, full width
|
|
23
|
+
|
|
24
|
+
## Guidelines
|
|
25
|
+
|
|
26
|
+
- Starters are based on what is selected ("Why are these 2 doing badly?") or, with nothing selected, on common tasks.
|
|
27
|
+
- Show three or fewer. Too many options cause scanning fatigue.
|
|
28
|
+
- Anchor follow-ups in the last answer and keep them visually apart from it.
|
|
29
|
+
- Selecting a follow-up sends it; selecting a starter that needs editing fills the box instead.
|
|
30
|
+
- Starters can be hidden. Do not show them everywhere.
|
|
31
|
+
- Starters fill the composer for editing and show an edit icon; slots in them are dashed until filled. Follow-ups send at once.
|
|
32
|
+
- A prompt has no state: picking it inserts or sends once, and it doesn't stay selected.
|
|
33
|
+
- Follow-ups that narrow an answer offer up to five options, most likely first, each under 72 characters.
|
|
34
|
+
|
|
35
|
+
## Content
|
|
36
|
+
|
|
37
|
+
- Write them as the person would ask, specific to the data: "Which of these close this week?"
|
|
38
|
+
- Sentence case, no exclamation marks, no trailing arrow.
|
|
39
|
+
- Include at least one prompt that helps check the answer, not only extend it.
|
|
40
|
+
|
|
41
|
+
## Accessibility
|
|
42
|
+
|
|
43
|
+
- Each follow-up and starter is a button with its full text as the name (SC 4.1.2).
|
|
44
|
+
- Group them under a visible label ("Follow-ups", "Try asking") that is programmatically associated (SC 1.3.1).
|
|
45
|
+
- Rows are full width and at least 24 by 24 px targets (SC 2.5.8).
|
|
46
|
+
- Starters and follow-ups are buttons whose names say what happens: "Insert: Which listings close this week?" or "Ask: Compare with last month".
|
|
47
|
+
|
|
48
|
+
## Keyboard
|
|
49
|
+
|
|
50
|
+
| Keys | Action |
|
|
51
|
+
|---|---|
|
|
52
|
+
| Tab | Move to the next suggestion |
|
|
53
|
+
| Enter / Space | Send the follow-up, or fill the box with the starter |
|
|
54
|
+
|
|
55
|
+
## Examples
|
|
56
|
+
|
|
57
|
+
### suggestions
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { FollowUps, Starters, toast } from "@twoux/ui";
|
|
61
|
+
|
|
62
|
+
export const meta = { title: "Starters and follow-ups", description: "Starters come from what is selected; follow-ups continue the last answer, muted and apart from it. Three at most.", order: 1 };
|
|
63
|
+
|
|
64
|
+
export default function Example() {
|
|
65
|
+
return (
|
|
66
|
+
<div className="grid w-full max-w-lg gap-6">
|
|
67
|
+
<Starters items={["Why are these 2 doing badly?", "Compare applicants with last quarter", "Draft a shorter description"]} onPick={(q) => toast(q)} onHide={() => {}} />
|
|
68
|
+
<FollowUps items={["Which job boards are they on?", "Show the same for Design", "Extend both by 30 days"]} onPick={(q) => toast(q)} />
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Related
|
|
75
|
+
|
|
76
|
+
- Assistant side panel
|
|
77
|
+
- Message composer
|
|
78
|
+
- Templates gallery
|
|
79
|
+
- Clarifying question card
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Form validation
|
|
2
|
+
|
|
3
|
+
Tells people which fields need fixing and how, at the field and in a summary at the top on submit.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: Actions and inputs · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- On any form where a value can be wrong or missing: a new listing, a salary, a closing date.
|
|
10
|
+
- Field errors as soon as the person leaves a field with a wrong value.
|
|
11
|
+
- The summary strip when someone submits and one or more fields fail.
|
|
12
|
+
|
|
13
|
+
## When not to use
|
|
14
|
+
|
|
15
|
+
- For errors not tied to a field, such as a server failure. Use a banner above the form or the table error state.
|
|
16
|
+
- To confirm success on every field. Use a Toast for the completed action instead.
|
|
17
|
+
- Before the person has touched the field. Leave untouched fields neutral until submit.
|
|
18
|
+
|
|
19
|
+
## Structure
|
|
20
|
+
|
|
21
|
+
1. Error summary strip (on submit)
|
|
22
|
+
2. Count of problems
|
|
23
|
+
3. Go to first link
|
|
24
|
+
4. Field with red border
|
|
25
|
+
5. Inline error message
|
|
26
|
+
|
|
27
|
+
## Guidelines
|
|
28
|
+
|
|
29
|
+
- Check a field when the person leaves it. Recheck on each keystroke once it is in error, and clear the error when fixed.
|
|
30
|
+
- On submit, check the whole form. Show the red strip at the top: 2 fields need fixing before you can publish.
|
|
31
|
+
- The strip has a Go to first link that scrolls to and focuses the first field in error.
|
|
32
|
+
- The summary adds to inline errors; it never replaces them.
|
|
33
|
+
- Message sits directly under the field, replacing the hint, in 12px red text.
|
|
34
|
+
- Keep everything the person typed. Never clear a form on error.
|
|
35
|
+
|
|
36
|
+
## Content
|
|
37
|
+
|
|
38
|
+
- Say what to do, not what went wrong in system terms: Pick a date after today.
|
|
39
|
+
- For empty fields, instruct: Enter a job title. For wrong values, describe the limit: Enter at least £23,400.
|
|
40
|
+
- No please, sorry, oops, invalid or blaming words.
|
|
41
|
+
- Use the field label in the message so it reads as one thing, and use the same words in the summary.
|
|
42
|
+
- The summary counts problems in plain words: 2 fields need fixing before you can publish.
|
|
43
|
+
|
|
44
|
+
## Accessibility
|
|
45
|
+
|
|
46
|
+
- Each failed field has aria-invalid="true" and aria-describedby pointing at its message.
|
|
47
|
+
- Errors are in text, not only red. The border colour is a second cue.
|
|
48
|
+
- The strip uses role="alert" so it is announced at once; Go to first moves focus to the field.
|
|
49
|
+
- Messages suggest a fix whenever the system knows one.
|
|
50
|
+
- Red text and the red border meet 4.5:1 and 3:1 on white.
|
|
51
|
+
|
|
52
|
+
## Keyboard
|
|
53
|
+
|
|
54
|
+
| Keys | Action |
|
|
55
|
+
|---|---|
|
|
56
|
+
| Tab | Leave a field and trigger its check |
|
|
57
|
+
| Enter on Go to first | Move focus to the first field in error |
|
|
58
|
+
|
|
59
|
+
## Examples
|
|
60
|
+
|
|
61
|
+
### summary
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { Button, ErrorSummary, TextField } from "@twoux/ui";
|
|
65
|
+
import { useState } from "react";
|
|
66
|
+
export const meta = {"title": "Validate on submit, then summarise", "description": "Press Publish. On submit, each field shows its own error and the summary at the top counts the problems and links to each field.", "order": 1};
|
|
67
|
+
|
|
68
|
+
export default function Example() {
|
|
69
|
+
const [title, setTitle] = useState("");
|
|
70
|
+
const [salary, setSalary] = useState("18000");
|
|
71
|
+
const [sent, setSent] = useState(false);
|
|
72
|
+
const errs = [
|
|
73
|
+
!title.trim() && { field: "Job title", message: "Enter a job title.", href: "#fv-title" },
|
|
74
|
+
Number(salary.replace(/\D/g, "")) < 23400 && { field: "Salary", message: "Enter at least £23,400.", href: "#fv-salary" },
|
|
75
|
+
].filter(Boolean) as { field: string; message: string; href: string }[];
|
|
76
|
+
const show = sent ? errs : [];
|
|
77
|
+
return (
|
|
78
|
+
<form className="grid w-96 gap-4" noValidate onSubmit={(e) => { e.preventDefault(); setSent(true); }}>
|
|
79
|
+
<ErrorSummary errors={show} />
|
|
80
|
+
<TextField id="fv-title" label="Job title" required error={show.find((e) => e.field === "Job title")?.message} value={title} onChange={(e) => setTitle(e.target.value)} />
|
|
81
|
+
<TextField id="fv-salary" label="Salary" required hint="Per year, before tax." error={show.find((e) => e.field === "Salary")?.message} prefix="£" value={salary} onChange={(e) => setSalary(e.target.value)} inputMode="numeric" />
|
|
82
|
+
<div><Button type="submit" variant="default">Publish listing</Button></div>
|
|
83
|
+
</form>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Related
|
|
89
|
+
|
|
90
|
+
- Text input
|
|
91
|
+
- Button
|
|
92
|
+
- Checkbox
|
|
93
|
+
- Radio
|
|
94
|
+
- Sign in
|
|
95
|
+
- File drop
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Generated table columns and enrichment
|
|
2
|
+
|
|
3
|
+
Add an AI column, test it on three rows, see the cost per row, then run it with progress and Stop. Each cell shows its own state and cites its record.
|
|
4
|
+
|
|
5
|
+
Kind: pattern · Group: Patterns: AI
|
|
6
|
+
|
|
7
|
+
**Problem:** Running a prompt across 1,000 rows is expensive and hard to undo, and wrong answers hide among right ones. Without a shared flow, each table runs AI differently and overwrites real data.
|
|
8
|
+
|
|
9
|
+
**Solution:** Generated columns are separate from real data, carry the origin mark, and run on demand: test on a few rows first, show cost, then run with a progress bar and Stop. Cells fill one by one; failures say why; stale cells are marked out of date.
|
|
10
|
+
|
|
11
|
+
**Built from:** AI column, Data grid, Prompt editor with fields, AI origin label, Citations and sources, Toast
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
1. Add a column from a short list ("Suggested title", "Risk of closing empty") or describe your own in the prompt editor, with fields from the row as removable chips.
|
|
16
|
+
2. Test on 3 rows before anything else; results appear in place for review.
|
|
17
|
+
3. Before running on all rows, show the cost per row and in total, and the number of rows.
|
|
18
|
+
4. Run fills cells one by one with a visible state (queued, writing, done, failed) and a progress bar with Stop.
|
|
19
|
+
5. A failed cell says why ("The listing has no description") with Try again; "Not found" means the model looked and found nothing; a dash means nothing to do.
|
|
20
|
+
6. When source data changes, mark the column out of date instead of rewriting it silently.
|
|
21
|
+
|
|
22
|
+
## When to use
|
|
23
|
+
|
|
24
|
+
- To add a judgement or summary to many rows at once in the data grid.
|
|
25
|
+
|
|
26
|
+
## When not to use
|
|
27
|
+
|
|
28
|
+
- In the list table. Generated columns live only in the data grid.
|
|
29
|
+
- To change real fields. A generated column never overwrites real data; applying a suggestion to a real field goes through Propose then apply.
|
|
30
|
+
|
|
31
|
+
## Guidelines
|
|
32
|
+
|
|
33
|
+
- Default to testing when a run is expensive or affects many rows.
|
|
34
|
+
- Show cost where the decision is made, on the Run button, in credits with the unit stated.
|
|
35
|
+
- Each cell cites the record it used and shows when it was written.
|
|
36
|
+
- Cells can be corrected one at a time or in bulk.
|
|
37
|
+
- Runs keep a history so people can see and undo earlier runs.
|
|
38
|
+
|
|
39
|
+
## Content
|
|
40
|
+
|
|
41
|
+
- Run button: "Run on 412 rows · about 412 credits".
|
|
42
|
+
- Column header shows the origin mark and the time written: "Likely reason · 2 min ago".
|
|
43
|
+
|
|
44
|
+
## Accessibility
|
|
45
|
+
|
|
46
|
+
- Cell states are text ("Writing", "Failed"), not only spinners or colours.
|
|
47
|
+
|
|
48
|
+
## Do
|
|
49
|
+
|
|
50
|
+
- Do test on a few rows first.
|
|
51
|
+
- Do show cost before running.
|
|
52
|
+
|
|
53
|
+
## Don't
|
|
54
|
+
|
|
55
|
+
- Don't auto-run a new column on every row.
|
|
56
|
+
- Don't rewrite stale cells silently.
|
|
57
|
+
|
|
58
|
+
## Related
|
|
59
|
+
|
|
60
|
+
- Labelling AI output
|
|
61
|
+
- Citing sources
|
|
62
|
+
- Showing reasoning and progress
|
|
63
|
+
- Data export
|
|
64
|
+
- AI column
|
|
65
|
+
- Prompt editor with fields
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Generated drafts and files
|
|
2
|
+
|
|
3
|
+
Show what the assistant makes (a job description, a CSV, a chart) as a card under the answer. Short things are read and copied in place; long drafts open on the assistant page. Anything that would change a record goes through Suggested changes.
|
|
4
|
+
|
|
5
|
+
Kind: pattern · Group: Patterns: AI
|
|
6
|
+
|
|
7
|
+
**Problem:** Hiring teams ask the assistant for things they will use elsewhere: a job description for a new role, a CSV of expiring listings, a chart of applications by week. Pasted into the answer text, these are hard to copy, easy to lose and easy to mistake for saved data.
|
|
8
|
+
|
|
9
|
+
**Solution:** Each output becomes a generated file card stacked under the answer, with name, type, size and origin label, and its own actions. Short, self-contained output stays inline. Long drafts open beside the conversation on the assistant page. Saving a draft into a listing is a suggested change the person applies.
|
|
10
|
+
|
|
11
|
+
**Built from:** Generated file card, Assistant side panel, Assistant page, Suggested changes, Code block, AI origin label, Dropdown menu
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
1. When an answer produces something to keep, place it as a card below the answer text, one card per output, never inside a paragraph.
|
|
16
|
+
2. Choose inline or open by length and intent: a short snippet, a CSV or a chart image stays inline with Copy or Download; a full job description opens to read and edit.
|
|
17
|
+
3. Open shows the draft on the assistant page in the Answer area, with the conversation still beside it.
|
|
18
|
+
4. If the draft belongs on a record (the description of LST-1044), the card's main action is "Suggest for LST-1044", which adds it to Suggested changes with before and after.
|
|
19
|
+
5. Applying works like every proposal: step card, toast with Undo.
|
|
20
|
+
|
|
21
|
+
## When to use
|
|
22
|
+
|
|
23
|
+
- For drafts people will edit or reuse: job descriptions, interview question sets, rejection email drafts for a recruiter to send themselves.
|
|
24
|
+
- For files to download: CSV of listings, a chart as PNG.
|
|
25
|
+
- For a chart the assistant drew from listings data, shown inline with Download and a data table on Open.
|
|
26
|
+
|
|
27
|
+
## When not to use
|
|
28
|
+
|
|
29
|
+
- For a figure or a short table that answers the question. Keep it in the answer.
|
|
30
|
+
- For direct edits to a record field. Use Suggested changes.
|
|
31
|
+
- For exports of a table view. Use the Export menu on the table.
|
|
32
|
+
|
|
33
|
+
## Guidelines
|
|
34
|
+
|
|
35
|
+
- Keep file actions in the card and answer actions in the answer row, so Copy on the card copies the file and Copy under the answer copies the answer.
|
|
36
|
+
- Two visible actions at most on a card; the rest go in a More menu.
|
|
37
|
+
- Every card carries the origin label and the records it drew on, as the answer does.
|
|
38
|
+
- A draft is never saved anywhere until the person downloads, copies or applies it. Say so on the card.
|
|
39
|
+
- Drafts about candidates are for a person to review and send. The assistant never sends or decides on its own.
|
|
40
|
+
|
|
41
|
+
## Content
|
|
42
|
+
|
|
43
|
+
- Card title is the file name or what it is: "Job description, Junior Product Designer", "expiring-listings.csv".
|
|
44
|
+
- Metadata uses standard units and plain types: "CSV · 14 KB · 38 rows".
|
|
45
|
+
- Main action verbs: "Open", "Download", "Copy", "Suggest for LST-1044".
|
|
46
|
+
|
|
47
|
+
## Accessibility
|
|
48
|
+
|
|
49
|
+
- Each card is a group named by its title; its actions are named with the file, such as "Download expiring-listings.csv".
|
|
50
|
+
- Generated charts include a text summary and the data as a table on Open.
|
|
51
|
+
- Opening a draft moves focus to its heading; closing returns focus to the card.
|
|
52
|
+
|
|
53
|
+
## Do
|
|
54
|
+
|
|
55
|
+
- Do stack each output in its own card under the answer.
|
|
56
|
+
- Do route record changes through Suggested changes.
|
|
57
|
+
- Do show size and row counts before download.
|
|
58
|
+
|
|
59
|
+
## Don't
|
|
60
|
+
|
|
61
|
+
- Don't nest file cards inside answer text.
|
|
62
|
+
- Don't put a Save to record button on a draft.
|
|
63
|
+
- Don't mix file actions into the answer actions row.
|
|
64
|
+
|
|
65
|
+
## Related
|
|
66
|
+
|
|
67
|
+
- Propose, then apply
|
|
68
|
+
- Citing sources
|
|
69
|
+
- Labelling AI output
|
|
70
|
+
- Data export
|
|
71
|
+
- Generated file card
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Grouped list
|
|
2
|
+
|
|
3
|
+
A compact list of records split under group headers (such as candidates by stage), each header showing its count.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: Data · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- Records fall into a small number of meaningful groups that people scan one at a time, such as pipeline stages.
|
|
10
|
+
- A short list where a full table would be heavy; simple vertical content reads better as a simple list.
|
|
11
|
+
|
|
12
|
+
## When not to use
|
|
13
|
+
|
|
14
|
+
- When people sort or compare many attributes across all records. Use the TwoUX List table.
|
|
15
|
+
- For nested hierarchies more than one level deep. Use a tree.
|
|
16
|
+
- For more than about seven groups at once; split with a filter or Saved view instead.
|
|
17
|
+
|
|
18
|
+
## Structure
|
|
19
|
+
|
|
20
|
+
1. Group header (name, count, optional action)
|
|
21
|
+
2. Rows: ID, title, secondary text, owner avatar, date
|
|
22
|
+
3. Divider between rows
|
|
23
|
+
|
|
24
|
+
## Guidelines
|
|
25
|
+
|
|
26
|
+
- Each group header has a tinted background so rows read as belonging to it.
|
|
27
|
+
- Show the count beside every group name.
|
|
28
|
+
- Keep the same row layout in every group so columns line up down the list.
|
|
29
|
+
- Allow groups to collapse when lists are long.
|
|
30
|
+
|
|
31
|
+
## Content
|
|
32
|
+
|
|
33
|
+
- Group names are the states people use, "Interviewing", "Screening", "Offer accepted".
|
|
34
|
+
- Order groups by workflow, not alphabetically.
|
|
35
|
+
- Keep row text to the ID, name, role, owner and date; open the row for more.
|
|
36
|
+
|
|
37
|
+
## Accessibility
|
|
38
|
+
|
|
39
|
+
- 1.3.1 Info and Relationships: each group is a list with a heading, so the grouping is available to assistive technology.
|
|
40
|
+
- APG disclosure pattern: collapsible headers are buttons with aria-expanded.
|
|
41
|
+
- 2.1.1 Keyboard: J and K move through rows across groups, as in the List table.
|
|
42
|
+
|
|
43
|
+
## Keyboard
|
|
44
|
+
|
|
45
|
+
| Keys | Action |
|
|
46
|
+
|---|---|
|
|
47
|
+
| J / K | Move to the next or previous row |
|
|
48
|
+
| Enter | Open the row |
|
|
49
|
+
| Enter / Space (header) | Collapse or expand the group |
|
|
50
|
+
|
|
51
|
+
## Examples
|
|
52
|
+
|
|
53
|
+
### by-stage
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { Badge, Button, GroupedList, ListGroup } from "@twoux/ui";
|
|
57
|
+
|
|
58
|
+
export const meta = { title: "Candidates by stage", description: "Each tinted header names the group and counts it. Every row keeps the same layout so columns line up. Headers collapse with Enter or Space.", order: 1 };
|
|
59
|
+
|
|
60
|
+
const STAGES = [
|
|
61
|
+
{ stage: "Interview", people: [["Priya Shah", "Frontend Engineer", "28 Oct"], ["Sofia Romano", "Junior Product Designer", "20 Oct"]] },
|
|
62
|
+
{ stage: "Screen", people: [["Amara Okafor", "Junior Product Designer", "2 Nov"]] },
|
|
63
|
+
{ stage: "Applied", people: [["Tom Becker", "Frontend Engineer", "4 Nov"], ["Liam Walsh", "Data Analyst", "5 Nov"], ["Hana Kim", "QA Engineer", "5 Nov"]] },
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
export default function Example() {
|
|
67
|
+
return (
|
|
68
|
+
<GroupedList className="w-full max-w-xl" aria-label="Candidates by stage">
|
|
69
|
+
{STAGES.map((g) => (
|
|
70
|
+
<ListGroup key={g.stage} title={g.stage} count={g.people.length} defaultOpen={g.stage !== "Applied"} action={g.stage === "Applied" ? <Button variant="ghost" size="xs">Screen all</Button> : undefined}>
|
|
71
|
+
{g.people.map(([name, role, date]) => (
|
|
72
|
+
<li key={name} className="grid h-9 grid-cols-[1fr_auto_auto] items-center gap-3 px-3 text-base hover:bg-hover">
|
|
73
|
+
<span className="min-w-0 truncate"><span className="font-medium text-ink">{name}</span> <span className="text-ink-3">· {role}</span></span>
|
|
74
|
+
<Badge variant="secondary">{g.stage}</Badge>
|
|
75
|
+
<span className="tabular w-14 text-right text-sm text-ink-3">{date}</span>
|
|
76
|
+
</li>
|
|
77
|
+
))}
|
|
78
|
+
</ListGroup>
|
|
79
|
+
))}
|
|
80
|
+
</GroupedList>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Related
|
|
86
|
+
|
|
87
|
+
- List table
|
|
88
|
+
- Saved views
|
|
89
|
+
- Filter pills and filter bar
|