@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,170 @@
|
|
|
1
|
+
# Guide cue
|
|
2
|
+
|
|
3
|
+
A short guide of one to three popovers that point at the real controls for a task, opened only when the person asks to be shown.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: Feedback and overlays · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- Showing where the parts of a task live after the person chose it, from a setup checklist Start, a Help menu entry, a help panel link or a feature label's Show me.
|
|
10
|
+
- A task spread over two or three controls in a dense toolbar, such as filter, change the layout, then save a view.
|
|
11
|
+
|
|
12
|
+
## When not to use
|
|
13
|
+
|
|
14
|
+
- On page load, first login, a timer or hover. TwoUX has no push tours; use a setup checklist (setup-checklist) and let people start the guide.
|
|
15
|
+
- Announcing a feature. Use a feature label (feature-label) and its popover; offer Show me inside it only if the feature spans several controls.
|
|
16
|
+
- Explaining a field or concept. Use contextual help (help-system) and the help panel (help-panel).
|
|
17
|
+
- More than three steps, or steps that depend on what was entered. Use a help article, or a stepper (stepper) for the task itself.
|
|
18
|
+
- Explaining an icon-only button. Use a tooltip (tooltip).
|
|
19
|
+
|
|
20
|
+
## Structure
|
|
21
|
+
|
|
22
|
+
1. Target highlight (the focus ring round the real control)
|
|
23
|
+
2. Popover anchored to the target
|
|
24
|
+
3. Heading (what the person does here)
|
|
25
|
+
4. Body (one or two sentences)
|
|
26
|
+
5. Step count as text ("Step 2 of 3")
|
|
27
|
+
6. Back, then Next or Done (the one primary in the cue)
|
|
28
|
+
7. Close ("Close guide", Esc)
|
|
29
|
+
|
|
30
|
+
## Guidelines
|
|
31
|
+
|
|
32
|
+
- Open the guide only from something the person pressed, and return focus there when it closes.
|
|
33
|
+
- Keep it to one to three steps, each on a real control people will use for the task.
|
|
34
|
+
- Point at elements by ref or a data attribute. A step whose target is missing or hidden is skipped, and the count counts only the steps shown; with no targets the guide does not open.
|
|
35
|
+
- Leave the page usable: no dimmed overlay or spotlight. Clicking the page keeps the cue open so people can try the control.
|
|
36
|
+
- Show one guide cue at a time, and close any other popover first.
|
|
37
|
+
- Offer Show me only where the guide can point at something; check with isGuideTargetAvailable.
|
|
38
|
+
|
|
39
|
+
## Content
|
|
40
|
+
|
|
41
|
+
- Heading names what the person does, sentence case: "Filter the list", "Save it as a view". Not "Step 1" or "Welcome".
|
|
42
|
+
- Body is one or two sentences on what the control does for them.
|
|
43
|
+
- Progress is text: "Step 2 of 3". Buttons are Back, Next and Done; the close button is named "Close guide".
|
|
44
|
+
|
|
45
|
+
## Accessibility
|
|
46
|
+
|
|
47
|
+
- The cue is a non-modal dialog: role="dialog", aria-modal="false", named by its heading and described by its body (SC 4.1.2).
|
|
48
|
+
- Focus moves to the heading on open and returns to the control that started the guide on close (SC 2.4.3).
|
|
49
|
+
- Each step change is announced in a polite live region: "Saved views guide, step 2 of 3: Choose a layout" (SC 4.1.3).
|
|
50
|
+
- It never opens unrequested (SC 2.2.4). Nothing pulses; the popover's entrance stops under reduced motion (SC 2.3.3).
|
|
51
|
+
- The highlight uses the focus colour, 3:1 or more against the page, and the cue sits beside the target so it never covers it (SC 1.4.11, 2.4.11).
|
|
52
|
+
|
|
53
|
+
## Keyboard
|
|
54
|
+
|
|
55
|
+
| Keys | Action |
|
|
56
|
+
|---|---|
|
|
57
|
+
| Enter or Space | Press Next, Back, Done or Close |
|
|
58
|
+
| Tab, Shift+Tab | Move between the cue's buttons and on to the page; focus is not trapped |
|
|
59
|
+
| Esc | Close the guide and return focus to what started it |
|
|
60
|
+
|
|
61
|
+
## Examples
|
|
62
|
+
|
|
63
|
+
### saved-view-guide
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { FilterBar, FilterPill, GuideCue, SavedViews, Segment, SegmentedControl, SetupChecklist, toast, useGuide, type GuideStep, type SetupItem } from "@twoux/ui";
|
|
67
|
+
import { useMemo, useRef, useState } from "react";
|
|
68
|
+
|
|
69
|
+
export const meta = {
|
|
70
|
+
title: "Show me from the setup checklist",
|
|
71
|
+
description: "A three-step guide over the real toolbar, shown open here as a preview. In a product it opens only from Start on “Create your first saved view”; Esc or Close ends it and Start brings it back.",
|
|
72
|
+
order: 1,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const LISTINGS = [
|
|
76
|
+
{ status: "Open", location: "London" },
|
|
77
|
+
{ status: "Open", location: "Leeds" },
|
|
78
|
+
{ status: "Open", location: "Remote" },
|
|
79
|
+
{ status: "Closing soon", location: "London" },
|
|
80
|
+
{ status: "Closing soon", location: "Manchester" },
|
|
81
|
+
{ status: "Draft", location: "Leeds" },
|
|
82
|
+
{ status: "Closed", location: "London" },
|
|
83
|
+
{ status: "Closed", location: "Remote" },
|
|
84
|
+
];
|
|
85
|
+
const STATUS = ["Open", "Closing soon", "Draft", "Closed"].map((v) => ({ value: v, label: v, count: LISTINGS.filter((l) => l.status === v).length }));
|
|
86
|
+
const LOCATION = ["London", "Leeds", "Manchester", "Remote"].map((v) => ({ value: v, label: v, count: LISTINGS.filter((l) => l.location === v).length }));
|
|
87
|
+
|
|
88
|
+
export default function Example() {
|
|
89
|
+
const root = useRef<HTMLElement>(null);
|
|
90
|
+
const filtersRef = useRef<HTMLDivElement>(null);
|
|
91
|
+
const layoutRef = useRef<HTMLDivElement>(null);
|
|
92
|
+
const [views, setViews] = useState([{ id: "all", name: "All listings", count: LISTINGS.length }]);
|
|
93
|
+
const [view, setView] = useState("all");
|
|
94
|
+
const [status, setStatus] = useState<string[]>([]);
|
|
95
|
+
const [location, setLocation] = useState<string[]>([]);
|
|
96
|
+
const [layout, setLayout] = useState("table");
|
|
97
|
+
const [savedOne, setSavedOne] = useState(false);
|
|
98
|
+
|
|
99
|
+
const shown = LISTINGS.filter((l) => (status.length === 0 || status.includes(l.status)) && (location.length === 0 || location.includes(l.location))).length;
|
|
100
|
+
|
|
101
|
+
const steps: GuideStep[] = useMemo(() => [
|
|
102
|
+
{ target: filtersRef, title: "Filter the list", body: "Pick a status or a location. The count updates as you tick, and the filters stay when you save." },
|
|
103
|
+
{ target: layoutRef, title: "Choose a layout", body: "Table suits scanning many listings; Board groups them by status. The view remembers which you chose.", align: "end" },
|
|
104
|
+
{
|
|
105
|
+
// A function target: the Save view button inside SavedViews. If it isn't on screen, this step is skipped.
|
|
106
|
+
target: () => root.current?.querySelector<HTMLElement>("[data-slot='saved-views'] [aria-haspopup='dialog']"),
|
|
107
|
+
title: "Save it as a view",
|
|
108
|
+
body: "Save view keeps these filters and the layout under a name, so you and your team return to them in one click.",
|
|
109
|
+
align: "end",
|
|
110
|
+
},
|
|
111
|
+
], []);
|
|
112
|
+
|
|
113
|
+
// Shown open here so you can see the cue straight away. In a product, never pass `preview`: the guide opens only from Start.
|
|
114
|
+
const guide = useGuide(steps, { label: "Saved views guide", preview: true });
|
|
115
|
+
|
|
116
|
+
const items: SetupItem[] = [
|
|
117
|
+
{ id: "slack", label: "Connect Slack", done: true },
|
|
118
|
+
{ id: "view", label: "Create your first saved view", done: savedOne, onStart: guide.start },
|
|
119
|
+
{ id: "invite", label: "Invite a teammate", done: false },
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<div className="flex w-full flex-wrap items-start gap-4">
|
|
124
|
+
<SetupChecklist items={items} className="w-full max-w-64" />
|
|
125
|
+
<section ref={root} aria-label="Listings" className="grid min-w-0 flex-1 basis-80 gap-3 rounded-sm border border-line bg-surface p-3">
|
|
126
|
+
<SavedViews
|
|
127
|
+
views={views}
|
|
128
|
+
value={view}
|
|
129
|
+
onValueChange={setView}
|
|
130
|
+
dirty={status.length + location.length > 0 && view === "all"}
|
|
131
|
+
onSaveAs={(name) => {
|
|
132
|
+
const id = name.toLowerCase().replace(/\W+/g, "-");
|
|
133
|
+
setViews((v) => [...v, { id, name, count: shown }]);
|
|
134
|
+
setView(id);
|
|
135
|
+
setSavedOne(true);
|
|
136
|
+
toast.success(`Saved “${name}”`);
|
|
137
|
+
}}
|
|
138
|
+
/>
|
|
139
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
140
|
+
<div ref={filtersRef} className="min-w-0 rounded-sm">
|
|
141
|
+
<FilterBar>
|
|
142
|
+
<FilterPill field="Status" options={STATUS} selected={status} onChange={setStatus} />
|
|
143
|
+
<FilterPill field="Location" options={LOCATION} selected={location} onChange={setLocation} />
|
|
144
|
+
</FilterBar>
|
|
145
|
+
</div>
|
|
146
|
+
<span className="flex-1" />
|
|
147
|
+
<div ref={layoutRef} className="rounded-sm">
|
|
148
|
+
<SegmentedControl value={layout} onValueChange={setLayout} size="sm" aria-label="Layout">
|
|
149
|
+
<Segment value="table">Table</Segment>
|
|
150
|
+
<Segment value="board">Board</Segment>
|
|
151
|
+
</SegmentedControl>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
<p className="tabular text-sm text-ink-2" aria-live="polite">{shown} of {LISTINGS.length} listings</p>
|
|
155
|
+
</section>
|
|
156
|
+
<GuideCue {...guide.cueProps} />
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Related
|
|
163
|
+
|
|
164
|
+
- Setup checklist
|
|
165
|
+
- feature-label
|
|
166
|
+
- Help panel
|
|
167
|
+
- Contextual help
|
|
168
|
+
- Announcing new and beta features
|
|
169
|
+
- Onboarding to an empty workspace
|
|
170
|
+
- Popover rules
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Help panel
|
|
2
|
+
|
|
3
|
+
Read-only help in the right-hand panel slot, opened from an Info link next to a heading or label, with the topic heading, short guidance and Learn more links.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: Feedback and overlays · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- To explain a page, section or field whose meaning or consequences aren't obvious from its label, opened from an Info link.
|
|
10
|
+
- On each step of a multi-step create flow, such as Create listing.
|
|
11
|
+
- As the page-level help for any page with decisions, opened from the Help button when no Info link was used.
|
|
12
|
+
|
|
13
|
+
## When not to use
|
|
14
|
+
|
|
15
|
+
- To name an icon or show a shortcut. Use a tooltip.
|
|
16
|
+
- For forms or actions. Use the side panel or the page.
|
|
17
|
+
- For questions about the person's data. Use the assistant panel (⌘J).
|
|
18
|
+
|
|
19
|
+
## Structure
|
|
20
|
+
|
|
21
|
+
1. Header: topic heading (matches the heading or label it opened from) and close button
|
|
22
|
+
2. Body: two to five short paragraphs or lists answering what this is, why it matters and how to choose
|
|
23
|
+
3. Optional: a short example, such as a well-written job title
|
|
24
|
+
4. Learn more: up to three help centre links, each opening in a new tab
|
|
25
|
+
5. Feedback prompt: "Was this helpful?" at the foot
|
|
26
|
+
|
|
27
|
+
## Guidelines
|
|
28
|
+
|
|
29
|
+
- Opens in TwoUX's single right-hand slot at 440px, or the width the person last chose, replacing any open panel, and restores it on close.
|
|
30
|
+
- Without an Info link pressed, shows the page topic. After an Info link, shows that topic until another is chosen or the page changes.
|
|
31
|
+
- Reopening on the same page shows the last topic, not the page default.
|
|
32
|
+
- Never opens empty: pages with no help content don't show the Help trigger.
|
|
33
|
+
- Below 860px it takes the full width of the canvas.
|
|
34
|
+
|
|
35
|
+
## Content
|
|
36
|
+
|
|
37
|
+
- Heading matches the on-screen heading or label exactly: "Visa sponsorship".
|
|
38
|
+
- Plain UK English, short paragraphs, no marketing. Explain consequences: "Candidates who need sponsorship can apply only if this is on."
|
|
39
|
+
- Learn more links say what the reader will learn: "How closing dates affect applications".
|
|
40
|
+
|
|
41
|
+
## Accessibility
|
|
42
|
+
|
|
43
|
+
- A complementary landmark labelled "Help"; it does not trap focus.
|
|
44
|
+
- Opening from an Info link moves focus to the topic heading; Esc or close returns focus to that Info link.
|
|
45
|
+
- New-tab links say so in their accessible name.
|
|
46
|
+
- Content reflows to 320px wide without horizontal scrolling.
|
|
47
|
+
|
|
48
|
+
## Keyboard
|
|
49
|
+
|
|
50
|
+
| Keys | Action |
|
|
51
|
+
|---|---|
|
|
52
|
+
| Enter / Space (Info link) | Open the help panel at that topic |
|
|
53
|
+
| Tab | Move through links in the panel, then back to the page |
|
|
54
|
+
| Esc | Close the panel and return focus to the Info link |
|
|
55
|
+
|
|
56
|
+
## Examples
|
|
57
|
+
|
|
58
|
+
### salary
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { FeedbackPrompt, HelpPanel, type HelpTopic, InfoLink, TextField } from "@twoux/ui";
|
|
62
|
+
import { useState } from "react";
|
|
63
|
+
|
|
64
|
+
export const meta = { title: "Help for one field", description: "The Info link opens written help for that topic in the right-hand slot. Esc closes it and focus returns to the link. It ends with up to three Learn more links and a feedback prompt.", order: 1 };
|
|
65
|
+
|
|
66
|
+
const TOPICS: Record<string, HelpTopic> = {
|
|
67
|
+
salary: {
|
|
68
|
+
id: "salary",
|
|
69
|
+
title: "Salary range",
|
|
70
|
+
body: (
|
|
71
|
+
<>
|
|
72
|
+
<p>Listings with a salary range get more applicants, and UK job boards rank them higher.</p>
|
|
73
|
+
<p><strong>Show a real range.</strong> Use the band you would actually pay, not a range so wide it says nothing. For example £30,000 to £36,000.</p>
|
|
74
|
+
<p><strong>Check the minimum.</strong> Full-time roles must pay at least the National Living Wage for the hours worked.</p>
|
|
75
|
+
</>
|
|
76
|
+
),
|
|
77
|
+
links: [{ label: "National Minimum Wage and Living Wage rates (GOV.UK)", href: "https://www.gov.uk/national-minimum-wage-rates" }],
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default function Example() {
|
|
82
|
+
const [open, setOpen] = useState(false);
|
|
83
|
+
const [topic, setTopic] = useState<HelpTopic | null>(null);
|
|
84
|
+
return (
|
|
85
|
+
<div className="flex h-96 w-full overflow-hidden rounded-sm border border-line bg-surface">
|
|
86
|
+
<div className="grid min-w-0 flex-1 content-start gap-1.5 p-5">
|
|
87
|
+
<div className="flex items-center gap-2"><span className="text-base font-medium text-ink">Salary</span><InfoLink topic="salary" onOpen={(t) => { setTopic(TOPICS[t]); setOpen(true); }} /></div>
|
|
88
|
+
<TextField id="help-salary" label="Salary from" prefix="£" placeholder="30,000" />
|
|
89
|
+
</div>
|
|
90
|
+
<HelpPanel open={open} onOpenChange={setOpen} topic={topic} footer={<FeedbackPrompt onVote={() => new Promise((r) => setTimeout(r, 500))} />} />
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Related
|
|
97
|
+
|
|
98
|
+
- Contextual help
|
|
99
|
+
- Secondary panels: which right-hand panel
|
|
100
|
+
- Side panel (drawer)
|
|
101
|
+
- Assistant side panel
|
|
102
|
+
- Feedback prompt
|
|
103
|
+
- Tooltip
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Contextual help
|
|
2
|
+
|
|
3
|
+
Answer "what is this, why does it matter, how do I choose" in three steps of depth: the words on the screen, a help panel opened from an Info link, and a help centre article in a new tab.
|
|
4
|
+
|
|
5
|
+
Kind: pattern · Group: Patterns: general
|
|
6
|
+
|
|
7
|
+
**Problem:** Help that lives only in a separate help centre isn't found at the moment of need, and help crammed into the page as long descriptions slows everyone down. Tooltips stuffed with paragraphs disappear before they are read.
|
|
8
|
+
|
|
9
|
+
**Solution:** Write the screen first: clear headings, labels and short descriptions. For anything that needs more, put an Info link next to the heading or label that opens the help panel on the right with page, section or field help. Each help topic ends with links to the full article.
|
|
10
|
+
|
|
11
|
+
**Built from:** Help panel, Page header, Text input, Tooltip, Side panel (drawer), Shortcut sheet (?)
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
1. Small: headings, labels, one or two lines of description, and constraint text under fields. Most questions end here.
|
|
16
|
+
2. Medium: an Info link beside a page heading, section heading or field label opens the help panel at that topic.
|
|
17
|
+
3. With no Info link pressed, the help panel shows help for the whole page.
|
|
18
|
+
4. Large: the panel's Learn more list links to help centre articles, which open in a new tab so the task is not lost.
|
|
19
|
+
5. Closing and reopening the panel on the same page shows the last topic read, not the page default.
|
|
20
|
+
|
|
21
|
+
## When to use
|
|
22
|
+
|
|
23
|
+
- Decisions with consequences people may not know, such as what Visa sponsorship does to who can apply, or what Closing date means for applications in progress.
|
|
24
|
+
- Concepts specific to the product, such as how pipeline stages map to candidate emails.
|
|
25
|
+
- Every step of a multi-step create flow, such as Create listing.
|
|
26
|
+
|
|
27
|
+
## When not to use
|
|
28
|
+
|
|
29
|
+
- To name an icon button or show its shortcut. Use a tooltip.
|
|
30
|
+
- For questions about the records on screen, such as "which listings close this week". Use the assistant (⌘J).
|
|
31
|
+
- On the home page or any page with nothing to explain. Never show an empty help panel.
|
|
32
|
+
|
|
33
|
+
## Guidelines
|
|
34
|
+
|
|
35
|
+
- Place Info links next to headings and field labels only, never inside descriptions or body text.
|
|
36
|
+
- The help topic's heading matches the heading or label it was opened from, word for word.
|
|
37
|
+
- Help panel content is read-only reference. Forms and actions belong in the page or the side panel.
|
|
38
|
+
- Requirements go in constraint text under the field before any error, not only in the help panel and never only in a placeholder.
|
|
39
|
+
- Don't add standalone Learn more links on a page that has help panel content, except inside a banner or dialog.
|
|
40
|
+
- Keep descriptions to one or two lines; longer explanation moves to the help panel.
|
|
41
|
+
|
|
42
|
+
## Content
|
|
43
|
+
|
|
44
|
+
- Answer three questions in order: what is this, why does it matter, how do I choose.
|
|
45
|
+
- Page headings start with a noun for views ("Listings") and a verb for tasks ("Create listing").
|
|
46
|
+
- Info link text is "Info"; its accessible name adds the topic: "Info about Visa sponsorship".
|
|
47
|
+
- Help centre links say where they go: "Learn how pipeline stages work", not "Click here".
|
|
48
|
+
|
|
49
|
+
## Accessibility
|
|
50
|
+
|
|
51
|
+
- The help panel is a labelled complementary region; opening it from an Info link moves focus to the topic heading, and closing it returns focus to the link.
|
|
52
|
+
- Info links are buttons (they open a panel), not links, and say which topic they open.
|
|
53
|
+
- External help links are marked as opening in a new tab, in text and in the accessible name.
|
|
54
|
+
|
|
55
|
+
## Do
|
|
56
|
+
|
|
57
|
+
- Do write the label and description first; add help only for what they can't carry.
|
|
58
|
+
- Do give every page that has decisions a page-level help topic.
|
|
59
|
+
- Do open help centre articles in a new tab.
|
|
60
|
+
|
|
61
|
+
## Don't
|
|
62
|
+
|
|
63
|
+
- Don't put paragraphs in tooltips.
|
|
64
|
+
- Don't switch the panel back to the default topic when it is reopened.
|
|
65
|
+
- Don't use the help panel as a form.
|
|
66
|
+
|
|
67
|
+
## Related
|
|
68
|
+
|
|
69
|
+
- Secondary panels: which right-hand panel
|
|
70
|
+
- Onboarding to an empty workspace
|
|
71
|
+
- Help panel
|
|
72
|
+
- Tooltip
|
|
73
|
+
- Shortcut sheet (?)
|
|
74
|
+
- Collecting product feedback
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Iconography
|
|
2
|
+
|
|
3
|
+
One line icon set, 16px with a 1.7 stroke in currentColor, and a fixed meaning for every icon. An icon that means one thing in one place means that thing everywhere.
|
|
4
|
+
|
|
5
|
+
Kind: foundation · Group: Visual foundation
|
|
6
|
+
|
|
7
|
+
## Principles
|
|
8
|
+
|
|
9
|
+
- One meaning per icon. WCAG requires components with the same function to be identified consistently, and people cannot rely on icons whose function varies.
|
|
10
|
+
- Words first. Few icons are universally understood, so icons sit beside a text label; icon-only buttons are kept to well-known actions and always have an accessible name and tooltip.
|
|
11
|
+
- Reuse before you draw. Use existing icons rather than making new ones.
|
|
12
|
+
|
|
13
|
+
## Guidelines
|
|
14
|
+
|
|
15
|
+
- ↗ external (i-ext): the link leaves TwoUX, for example 'View public page' on the job board. Never used for in-app navigation.
|
|
16
|
+
- ↳ follow-up (i-reply): a suggested follow-up question that continues an AI answer, under a 'Follow-ups' label.
|
|
17
|
+
- Bulb (i-bulb): a starter idea, a suggested first prompt in an empty assistant. Starters can be hidden.
|
|
18
|
+
- Expand (i-expand): open this record or conversation as a full page. Not used for 'show more'; that is a chevron.
|
|
19
|
+
- Panel (i-panel): show the side panel side by side (push).
|
|
20
|
+
- Origin mark (AiMark, a small speech bubble): content the assistant wrote, beside that content, and the type icon of AI columns in a sheet.
|
|
21
|
+
- Sizes: 16px default, 14px (.sm) inside small controls and tabs, the smaller size mainly for chevrons and badges.
|
|
22
|
+
- Colour: icons inherit text colour. Status icons take the status text token; the origin mark is muted (text-ink-3) like any supporting icon, never an accent or AI colour.
|
|
23
|
+
- AI is marked in one place only: the origin mark next to generated output. Buttons that start AI work (Ask, Suggest titles) use their normal icon or none.
|
|
24
|
+
- The origin mark is not a sparkle and is never added to other icons to mean "AI-enhanced". Search that uses a model is still Search.
|
|
25
|
+
- The assistant's identity is its name, "Assistant", in text. If several agents speak in one thread, each has its own avatar and name; the origin mark is never used as an avatar.
|
|
26
|
+
- Match the strength of the marking to the stage: the entry point looks like any button, work in progress shows step text and a timer, finished output carries the small mark and "Assistant · from …".
|
|
27
|
+
|
|
28
|
+
## Accessibility
|
|
29
|
+
|
|
30
|
+
- Icons that carry meaning reach 3:1 against their background.
|
|
31
|
+
- Decorative icons beside a text label are hidden from assistive technology; icon-only buttons get an aria-label that matches the tooltip.
|
|
32
|
+
- Never use an icon as the only signal of status; pair it with text.
|
|
33
|
+
|
|
34
|
+
## Do
|
|
35
|
+
|
|
36
|
+
- Use the origin mark only beside output the assistant produced, and as the AI column type in sheets.
|
|
37
|
+
- Keep the same icon and the same accessible name for Save, Close or Share everywhere.
|
|
38
|
+
|
|
39
|
+
## Don't
|
|
40
|
+
|
|
41
|
+
- Do not use ↗ for links that stay inside TwoUX.
|
|
42
|
+
- Do not use a sparkle for AI anywhere, and do not put the origin mark on buttons, menu items or sidebar items that start AI work.
|
|
43
|
+
- Do not draw a new icon when an existing one already has that meaning.
|
|
44
|
+
|
|
45
|
+
## Related
|
|
46
|
+
|
|
47
|
+
- Writing
|
|
48
|
+
- Consistency
|
|
49
|
+
- Accessibility
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Inbox (triage)
|
|
2
|
+
|
|
3
|
+
The one place for things that need the person, shown as a list beside the selected item's detail, cleared by doing, marking done or snoozing.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: Navigation · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- Agent approvals and questions that block a run.
|
|
10
|
+
- Things about to need action, such as listings closing soon, mentions and new assignments.
|
|
11
|
+
- Anything the person may need to come back to later, so it must persist rather than flash past.
|
|
12
|
+
|
|
13
|
+
## When not to use
|
|
14
|
+
|
|
15
|
+
- Confirming the person's own action. Use a Toast.
|
|
16
|
+
- Events that need nothing from anyone. Record them in the record's Activity tab.
|
|
17
|
+
- A condition on the page being viewed. Use a Banner.
|
|
18
|
+
|
|
19
|
+
## Structure
|
|
20
|
+
|
|
21
|
+
1. Header with key hints (J K, E done, ⇧H snooze)
|
|
22
|
+
2. Tabs: Inbox, Snoozed, Done, each with a count
|
|
23
|
+
3. List (listbox): items that block an agent first, marked "Needs you", then by time
|
|
24
|
+
4. Detail: the item with its actions in place (approve, extend, reply, answer 1 to 3)
|
|
25
|
+
5. Snooze menu: quick times plus a typed time
|
|
26
|
+
6. Empty state per tab
|
|
27
|
+
|
|
28
|
+
## Guidelines
|
|
29
|
+
|
|
30
|
+
- List and detail side by side; acting in the detail never leaves the Inbox, and the next item opens after Done.
|
|
31
|
+
- Items that block an agent sort first and carry a "Needs you" badge with a word, not only a colour.
|
|
32
|
+
- Items stay until done or snoozed; snoozed items return at the chosen time.
|
|
33
|
+
- Snooze offers four quick times (an hour from now, tomorrow, next week, a month from now) and a typed time such as "4 pm" or "2 days"; an unparseable time shows an error and keeps the menu open.
|
|
34
|
+
- Every action (done, snooze, approve, reply) shows a toast with Undo.
|
|
35
|
+
- Only things that matter reach the Inbox; filter for significance so the count stays trustworthy.
|
|
36
|
+
- The unread count shows on the sidebar item; below 860px, when the sidebar hides, an inbox button with the same count appears in the top bar.
|
|
37
|
+
|
|
38
|
+
## Content
|
|
39
|
+
|
|
40
|
+
- Item titles say what needs doing and to what: "Approve 3 rejections for Graduate Data Analyst".
|
|
41
|
+
- Empty tabs say the state and what happens next: "All caught up. New items appear here when something needs you."
|
|
42
|
+
- Snooze placeholder shows examples: "Try 4 pm, tomorrow, 2 days or in 5 weeks."
|
|
43
|
+
- Snoozed items show when they come back: "Until Mon 9:00".
|
|
44
|
+
|
|
45
|
+
## Accessibility
|
|
46
|
+
|
|
47
|
+
- The list is a listbox with aria-selected on the current item; arrow keys and J/K move through it.
|
|
48
|
+
- Tabs follow the APG tabs pattern and expose their counts in the accessible name.
|
|
49
|
+
- The detail region is a polite live region, so the newly opened item is announced (4.1.3).
|
|
50
|
+
- The compact inbox button's label includes the count: "Inbox, 3 unread" (4.1.2).
|
|
51
|
+
- Single-key triage keys (E, ⇧H, 1 to 3) follow the global single-key switch (2.1.4).
|
|
52
|
+
- New items never interrupt; they appear in the list and count, and the person reads them when ready (2.2.4).
|
|
53
|
+
|
|
54
|
+
## Keyboard
|
|
55
|
+
|
|
56
|
+
| Keys | Action |
|
|
57
|
+
|---|---|
|
|
58
|
+
| G I | Go to Inbox |
|
|
59
|
+
| J / K | Next or previous item |
|
|
60
|
+
| E | Mark done |
|
|
61
|
+
| ⇧H | Snooze (quick times or type a time, Enter to apply) |
|
|
62
|
+
| 1 to 3 | Answer an agent's question |
|
|
63
|
+
| ⌘↵ | Send a reply from the detail |
|
|
64
|
+
| Esc | Close the snooze menu |
|
|
65
|
+
|
|
66
|
+
## Examples
|
|
67
|
+
|
|
68
|
+
### inbox
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { Button, Inbox, type InboxItem, toast } from "@twoux/ui";
|
|
72
|
+
import { useState } from "react";
|
|
73
|
+
|
|
74
|
+
export const meta = { title: "Things that need you", description: "Items that block an agent come first, marked Needs you in words. Focus the list: J and K move, E marks done and the next item opens, ⇧H snoozes. Every action offers Undo.", order: 1 };
|
|
75
|
+
|
|
76
|
+
const START: InboxItem[] = [
|
|
77
|
+
{ id: "1", title: "Approve: extend 3 listings by 30 days", source: "Agent · Weekly listing check", time: "09:12", needsYou: true, state: "inbox" },
|
|
78
|
+
{ id: "2", title: "Priya Shah mentioned you on Frontend Engineer", source: "Comment", time: "Yesterday", state: "inbox" },
|
|
79
|
+
{ id: "3", title: "Data Analyst closes in 2 days with 4 applicants", source: "Listing alert", time: "Mon", state: "inbox" },
|
|
80
|
+
{ id: "4", title: "Export of 1,262 listings is ready", source: "Export", time: "Sun", state: "done" },
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
export default function Example() {
|
|
84
|
+
const [items, setItems] = useState(START);
|
|
85
|
+
const set = (id: string, patch: Partial<InboxItem>) => setItems((x) => x.map((i) => (i.id === id ? { ...i, ...patch } : i)));
|
|
86
|
+
return (
|
|
87
|
+
<Inbox
|
|
88
|
+
className="h-96 w-full"
|
|
89
|
+
items={items}
|
|
90
|
+
onDone={(id) => { set(id, { state: "done" }); toast.success("Marked done", { action: { label: "Undo", onClick: () => set(id, { state: "inbox" }) } }); }}
|
|
91
|
+
onSnooze={(id, until) => { set(id, { state: "snoozed", snoozedUntil: until }); toast.success(`Snoozed until ${until.toLowerCase()}`, { action: { label: "Undo", onClick: () => set(id, { state: "inbox" }) } }); }}
|
|
92
|
+
renderDetail={(it) => (
|
|
93
|
+
<div className="grid gap-3 text-base text-ink-2">
|
|
94
|
+
<p>{it.source} · {it.time}</p>
|
|
95
|
+
{it.needsYou && <div className="flex gap-2"><Button size="sm">Review changes</Button><Button size="sm" variant="outline">Reject</Button></div>}
|
|
96
|
+
</div>
|
|
97
|
+
)}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Related
|
|
104
|
+
|
|
105
|
+
- Notifications vs inbox vs toast
|
|
106
|
+
- Toast
|
|
107
|
+
- App shell and sidebar navigation
|
|
108
|
+
- Empty state
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Inline editing and validation
|
|
2
|
+
|
|
3
|
+
Edit values where they are shown, check each field when the person leaves it, and summarise every problem on submit.
|
|
4
|
+
|
|
5
|
+
Kind: pattern · Group: Patterns: general
|
|
6
|
+
|
|
7
|
+
**Problem:** Errors that appear while typing feel like nagging, and errors that only appear after submit are hard to find. Each form in a product tends to validate at a different moment.
|
|
8
|
+
|
|
9
|
+
**Solution:** Validate a field when the person leaves it. In the data grid, Enter edits, Enter saves and Esc cancels, and invalid values stay in edit. On submit, a summary counts the problems and moves focus to the first.
|
|
10
|
+
|
|
11
|
+
**Built from:** Data grid, Text input, Form validation, Banner (inline alert), Side panel (drawer), Toast
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
1. Show which values are editable: an edit affordance on hover in the side panel, cell focus in the data grid.
|
|
16
|
+
2. Enter starts editing; Enter saves and Esc cancels.
|
|
17
|
+
3. Check the value when the person leaves the field, never on the first keystroke.
|
|
18
|
+
4. If invalid, keep the cell or field in edit with a red outline, an icon and a message under it that says how to fix it.
|
|
19
|
+
5. Once the person is fixing an error, re-check on each keystroke so the message clears as soon as the value is valid.
|
|
20
|
+
6. On submit of a longer form, show a summary at the top that counts the problems and links to each, and move focus to the first field in error.
|
|
21
|
+
|
|
22
|
+
## When to use
|
|
23
|
+
|
|
24
|
+
- For values people change often, one at a time, such as a listing's closing date or owner.
|
|
25
|
+
- Across many rows in the data grid, where the same property is edited on many records.
|
|
26
|
+
|
|
27
|
+
## When not to use
|
|
28
|
+
|
|
29
|
+
- For creating a record or changing several dependent fields at once. Use a form in the side panel or a full page.
|
|
30
|
+
- In the list table. Only the data grid supports cell editing.
|
|
31
|
+
|
|
32
|
+
## Guidelines
|
|
33
|
+
|
|
34
|
+
- Read and edit states use the same type size and weight so text does not jump.
|
|
35
|
+
- In a filtered or sorted table, keep an edited row in place until the person refreshes, so it does not vanish under their cursor.
|
|
36
|
+
- Keep the submit button enabled on longer forms; pressing it runs validation and shows the summary.
|
|
37
|
+
- Mark required fields. If most fields are required, mark the optional ones instead.
|
|
38
|
+
- Server errors that belong to a field appear on that field; errors that belong to no field appear in a banner above the form actions.
|
|
39
|
+
- The error summary is in addition to field messages, never instead of them.
|
|
40
|
+
- Show requirements before anyone gets them wrong: constraint text under the field ("3 to 80 characters") and a format example. An error should never be the first time people see a rule.
|
|
41
|
+
- Warnings are not errors: a field warning ("Salary is below the range for similar roles") is shown in amber text and does not block saving.
|
|
42
|
+
- After submit, stay on the form until the server answers, with a spinner in the Save button. Never redirect first and lose the input.
|
|
43
|
+
- If a slow submit must leave the page, the result message links back to the form with the values and errors restored.
|
|
44
|
+
- A server error that belongs to a field, such as "A listing with this reference already exists", goes on that field, not in the banner.
|
|
45
|
+
|
|
46
|
+
## Content
|
|
47
|
+
|
|
48
|
+
- Required message: "[Field] is required", for example "Closing date is required".
|
|
49
|
+
- Say what is wrong and how to fix it in one sentence: "Salary must be a number, like 32000".
|
|
50
|
+
- Summary heading: "2 problems to fix", each a link to its field.
|
|
51
|
+
- Format errors start with Enter: "Enter a valid email address, like name@example.co.uk".
|
|
52
|
+
- Length errors give the count: "Job title has too many characters. 92 of 80." Counters read "Character count: 12/80".
|
|
53
|
+
- Don't use a generic top message such as "Fix the errors on this page"; the summary lists each problem.
|
|
54
|
+
|
|
55
|
+
## Accessibility
|
|
56
|
+
|
|
57
|
+
- Errors are text, not just a red border; colour is a second cue.
|
|
58
|
+
- Link each message to its field with aria-describedby and set aria-invalid on the field.
|
|
59
|
+
- On submit, move focus to the first field in error.
|
|
60
|
+
- In the grid, Enter or F2 enters edit, Esc restores grid navigation and may undo the edit.
|
|
61
|
+
- Placeholders are examples only; labels and constraint text carry the instructions, because placeholder text disappears and often fails contrast.
|
|
62
|
+
|
|
63
|
+
## Do
|
|
64
|
+
|
|
65
|
+
- Do validate on blur and clear the error as soon as it is fixed.
|
|
66
|
+
- Do keep save and cancel visible while editing.
|
|
67
|
+
|
|
68
|
+
## Don't
|
|
69
|
+
|
|
70
|
+
- Don't show errors on fields the person hasn't reached yet.
|
|
71
|
+
- Don't rely on a top summary alone.
|
|
72
|
+
- Don't report a failed save in a toast; keep the field in error with Retry.
|
|
73
|
+
|
|
74
|
+
## Related
|
|
75
|
+
|
|
76
|
+
- Empty, loading and error states
|
|
77
|
+
- Undo and destructive actions
|
|
78
|
+
- Record detail: side panel, overlay or full page
|
|
79
|
+
- Form validation
|
|
80
|
+
- Data grid
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Asking for details inside the answer
|
|
2
|
+
|
|
3
|
+
When the assistant knows what to do but needs a few values to do it, it asks with a short form card in the thread, prefilled from context. Submitting the form produces a proposal, not a change.
|
|
4
|
+
|
|
5
|
+
Kind: pattern · Group: Patterns: AI
|
|
6
|
+
|
|
7
|
+
**Problem:** Some requests need specific values before anything can be prepared: "Draft a new listing like LST-1044 for Manchester" needs a salary band and a closing date. Asking in prose makes people type values in free text, which the assistant then has to parse and may get wrong.
|
|
8
|
+
|
|
9
|
+
**Solution:** Show an input card in the conversation with only the required fields, at most five, prefilled with what can be inferred and a line saying why the values are needed. Completed cards collapse to a read-only summary with Edit. The result is a Suggested changes card the person applies.
|
|
10
|
+
|
|
11
|
+
**Built from:** Card, Text input, Form validation, Suggested changes, Clarifying question card, Assistant side panel
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
1. Ask only once the intent is clear. If it isn't, use the clarifying question card first.
|
|
16
|
+
2. Show one card per thing being set up, titled after it: "Closing date and salary for the Manchester listing".
|
|
17
|
+
3. Prefill from context: location from the request, salary band from the team file, closing date from the usual 30 days. Mark what was prefilled and from where.
|
|
18
|
+
4. Steps that depend on each other appear one at a time; independent ones appear together as separate cards that can be done in any order.
|
|
19
|
+
5. On Continue, the card collapses to a summary line with Edit, and the assistant prepares the proposal from the values.
|
|
20
|
+
|
|
21
|
+
## When to use
|
|
22
|
+
|
|
23
|
+
- When an action needs values with a known shape: dates, locations, salary bands, a choice of listing.
|
|
24
|
+
- When the person must check a value the assistant chose before it is used, shown as an editable field with an edit icon.
|
|
25
|
+
- When an agent run reaches a step that needs one value from the person, such as which listings to include.
|
|
26
|
+
|
|
27
|
+
## When not to use
|
|
28
|
+
|
|
29
|
+
- To find out what the person means. Use the clarifying question card with options.
|
|
30
|
+
- For long setup with many fields, such as creating a listing from scratch. Open the listing form instead.
|
|
31
|
+
- For decisions about candidates. The assistant does not collect inputs to decide on people.
|
|
32
|
+
|
|
33
|
+
## Guidelines
|
|
34
|
+
|
|
35
|
+
- Five fields at most, all required. If a field is optional, leave it out.
|
|
36
|
+
- Say why the values are needed in one line above the fields.
|
|
37
|
+
- Use the same inputs as the rest of TwoUX: date picker for dates, select for known lists. Validate on Continue like any form.
|
|
38
|
+
- Never ask for something the workspace already holds; prefill it instead.
|
|
39
|
+
- A card left unanswered doesn't block the thread: the composer stays usable and the card can be dismissed.
|
|
40
|
+
|
|
41
|
+
## Content
|
|
42
|
+
|
|
43
|
+
- Card titles name the thing, not the need: "Closing date for LST-1061", not "Information required".
|
|
44
|
+
- Prefill note: "From team file Salary bands 2026".
|
|
45
|
+
- Collapsed summary: "Manchester · £28,000 to £32,000 · closes 6 Dec" then Edit.
|
|
46
|
+
|
|
47
|
+
## Accessibility
|
|
48
|
+
|
|
49
|
+
- When the card appears, announce it and move focus to its heading, not to the first field or Continue.
|
|
50
|
+
- The card is not a dialog and doesn't trap focus; the thread and composer stay reachable.
|
|
51
|
+
- Each field has a visible label; prefilled values are announced with their source.
|
|
52
|
+
|
|
53
|
+
## Do
|
|
54
|
+
|
|
55
|
+
- Do prefill from context and say where values came from.
|
|
56
|
+
- Do collapse completed cards to a summary with Edit.
|
|
57
|
+
- Do turn the values into a proposal the person applies.
|
|
58
|
+
|
|
59
|
+
## Don't
|
|
60
|
+
|
|
61
|
+
- Don't ask for optional fields.
|
|
62
|
+
- Don't use a form to clarify intent.
|
|
63
|
+
- Don't show several dependent steps at once.
|
|
64
|
+
|
|
65
|
+
## Related
|
|
66
|
+
|
|
67
|
+
- Clarifying before acting
|
|
68
|
+
- Propose, then apply
|
|
69
|
+
- Ask about what's on screen
|
|
70
|
+
- Clarifying question card
|