@flood-md/agent 0.2.11 → 0.2.12
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/DESIGN.md +1 -1
- package/dist/catalog.json +106 -50
- package/dist/materials/component-app-shell.md +2 -0
- package/dist/materials/component-collection-toolbar.md +2 -1
- package/dist/materials/component-data-table.md +21 -3
- package/dist/materials/component-editable.md +4 -0
- package/dist/materials/component-heading.md +1 -1
- package/dist/materials/component-section.md +1 -1
- package/dist/materials/component-sheet.md +49 -0
- package/dist/materials/component-sparkline.md +41 -0
- package/dist/materials/component-table.md +2 -0
- package/dist/materials/component-toolbar.md +1 -0
- package/dist/materials/guide-agent-knowledge.md +5 -5
- package/dist/materials/guide-foundations.md +2 -2
- package/dist/materials/guide-installation.md +6 -6
- package/dist/materials/guide-motion.md +7 -10
- package/dist/materials/recipe-collection-views.md +69 -47
- package/dist/materials/recipe-record-management.md +18 -4
- package/lib/inspect.mjs +11 -0
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/references/component-families.md +4 -0
- package/references/composition.md +12 -0
- package/references/flood-data-display/SKILL.md +29 -0
- package/references/flood-feedback/SKILL.md +12 -0
- package/references/flood-hierarchy/SKILL.md +4 -0
- package/references/flood-interface-content/SKILL.md +7 -0
- package/references/flood-motion/SKILL.md +3 -0
- package/references/flood-overlays/SKILL.md +6 -0
- package/references/flood-records/SKILL.md +23 -0
- package/references/flood-spacing/SKILL.md +7 -0
- package/rules.json +4 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Installation · 0.2.
|
|
1
|
+
# Installation · 0.2.12
|
|
2
2
|
|
|
3
|
-
The commands below target version 0.2.
|
|
3
|
+
The commands below target version 0.2.12. Verify registry availability before installation; for local source evaluation, see the tarball instructions at the end of this guide.
|
|
4
4
|
|
|
5
5
|
Use Node.js 22.12 or newer and an existing npm-based React/Next.js project.
|
|
6
6
|
React 18.3/19 is supported; React 19 and Next.js App Router are integration targets.
|
|
@@ -15,8 +15,8 @@ repeat installation when you start using another component.
|
|
|
15
15
|
## Connect with the CLI
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
|
-
npx @flood-md/cli@0.2.
|
|
19
|
-
npx @flood-md/cli@0.2.
|
|
18
|
+
npx @flood-md/cli@0.2.12 init --dry-run
|
|
19
|
+
npx @flood-md/cli@0.2.12 init
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
The installer adds exact matching tokens, React and agent packages. It creates
|
|
@@ -40,7 +40,7 @@ No Tailwind configuration or font CDN is needed. The foundations profile is dark
|
|
|
40
40
|
only; legacy theme behavior remains available outside `FloodRoot`.
|
|
41
41
|
|
|
42
42
|
```sh
|
|
43
|
-
npx @flood-md/cli@0.2.
|
|
43
|
+
npx @flood-md/cli@0.2.12 doctor
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Doctor checks package versions and guide presence, not stylesheet imports or
|
|
@@ -50,7 +50,7 @@ reference `flood/DESIGN.md` from its existing instructions.
|
|
|
50
50
|
## Manual package installation
|
|
51
51
|
|
|
52
52
|
```sh
|
|
53
|
-
npm install --save-exact @flood-md/tokens@0.2.
|
|
53
|
+
npm install --save-exact @flood-md/tokens@0.2.12 @flood-md/react@0.2.12 @flood-md/agent@0.2.12
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
Import the stylesheet and use `FloodRoot` as above. Manual installation does not
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
# Motion
|
|
2
2
|
|
|
3
|
-
`PageHeader` stays static. Its actions keep their own Flood interaction feedback,
|
|
4
|
-
and container reflow does not animate the title or move focus.
|
|
5
|
-
`AppShell` and `AppTopBar` do not animate layout or scroll position. Sidebar
|
|
6
|
-
and Drawer own their own transitions.
|
|
7
|
-
|
|
8
3
|
Flood uses short motion to show where an interface changed. Motion follows the
|
|
9
4
|
component's own state and respects the operating system's reduced-motion preference.
|
|
10
5
|
|
|
@@ -43,10 +38,11 @@ behavior of their children. State updates and focus take effect immediately.
|
|
|
43
38
|
| `FloodRoot` | Applies the motion preference to its scope. Preference changes are immediate. |
|
|
44
39
|
| `Text`, `Heading`, `Kbd`, `VisuallyHidden`, `Blockquote`, `InlineCode`, `IconSlot`, `Label` | Static text and artwork. The containing control owns feedback. |
|
|
45
40
|
| `Stack`, `Inline`, `Surface`, `Divider`, `Container`, `Grid`, `AspectRatio`, `Section`, `Split`, `Center`, `Fieldset` | Stable layout. Children own any appearance or interaction feedback. |
|
|
41
|
+
| `PageHeader`, `AppShell`, `AppTopBar` | Titles, layout and scroll position stay stable during reflow. Actions, Sidebar and Drawer own their interaction feedback. |
|
|
46
42
|
| `ScrollArea`, `ResizablePanels`, `Slider` | Direct pointer tracking. Only scrollbar hover paint eases. Resize, scroll and slider positions stay under the pointer. |
|
|
47
43
|
| `Button`, `IconButton`, `Toggle`, `CopyButton` | Hover paint and short press compression. Toggle holds selected paint. Copy shows its result glyph without resizing the button. |
|
|
48
44
|
| `ButtonGroup`, `ToggleGroup`, `Toolbar`, `OverflowActions`, `BulkActionBar`, `Menubar` | Actions inherit Button and Menu feedback. Joined button surfaces keep their geometry. Bulk actions fade in when available. |
|
|
49
|
-
| `CommandList`, `ListBox`, `Tree`, `NavigationItem`, `NavigationList`, `NavigationTree`, `NavigationGroup`, `Sidebar` | Row paint, disclosure glyphs and optional flyouts. NavigationTree fades in branch content in place and removes it immediately on collapse. Sidebar eases between expanded and rail widths. Focus, route and tree reflow update immediately. |
|
|
45
|
+
| `CommandList`, `ListBox`, `Tree`, `NavigationItem`, `NavigationList`, `NavigationTree`, `NavigationGroup`, `Sidebar` | Row paint, disclosure glyphs and optional flyouts. NavigationTree fades in branch content in place and removes it immediately on collapse. Sidebar eases between expanded and rail widths and truncates its row text to keep it on one line. Its toggle shows a filled keyboard focus state, with the system outline in forced colors. Focus, route and tree reflow update immediately. |
|
|
50
46
|
| `Link`, `Breadcrumb`, `Pagination`, `SkipLink`, `Steps` | Navigation is immediate. Button-style actions and step rows can ease their paint. Skip links appear immediately on focus. |
|
|
51
47
|
| `Tabs`, `SegmentedControl` | Selected paint and, for line tabs, an arriving indicator. Tabs reveal the visible panel with a short fade while selection and focus update immediately. |
|
|
52
48
|
| `Field`, `Input`, `Textarea`, `InputGroup`, `SearchInput`, `PasswordInput`, `NumberInput`, `PinInput`, `DateField`, `TimeField`, `DateRangeField`, `Editable`, `CharacterCount` | Field hover paint and inherited action feedback. Typing, caret, counts, validation and edit-mode focus are immediate. |
|
|
@@ -57,13 +53,14 @@ behavior of their children. State updates and focus take effect immediately.
|
|
|
57
53
|
| `Collapsible`, `Accordion` | Keep the header flush on press, rotate the centered disclosure mark and reveal panel content. Closing immediately hides the panel and restores focus where needed. |
|
|
58
54
|
| `CodeSnippet`, `Card`, `DataList`, `DataListItem`, `FileItem`, `FileList`, `AvatarGroup` | Embedded actions and avatars own motion. Reading order and record layout remain stable. |
|
|
59
55
|
| `Badge`, `DescriptionList`, `Table`, `Image`, `List`, `Figure`, `Metric`, `Timeline`, `ColorSwatch`, `FileMeta` | Static content. Changes to data, labels and supporting media render immediately. |
|
|
56
|
+
| `Sparkline` | Hover briefly strengthens the latest point and area. Data changes render immediately. |
|
|
60
57
|
| `Avatar` | Loaded image eases in within a fixed frame. Fallback text is immediately available. |
|
|
61
|
-
| `DataTable` | Selected row paint and
|
|
62
|
-
| `Tooltip`, `Popover`, `Menu`, `ContextMenu`, `HoverCard`, `CommandPalette`, `Dialog`, `AlertDialog`, `Drawer`, `ImagePreview` | Anchored entrances follow placement. Modal and edge panels have their own timing. Controls, focus, close and positioning retain their native behavior. |
|
|
58
|
+
| `DataTable` | Selected row paint, chevron rotation and a short height transition for row details. Sorting, pagination and column resize update geometry immediately. |
|
|
59
|
+
| `Tooltip`, `Popover`, `Menu`, `ContextMenu`, `HoverCard`, `CommandPalette`, `Dialog`, `AlertDialog`, `Drawer`, `Sheet`, `ImagePreview` | Anchored entrances follow placement. Modal and edge panels have their own timing. Sheet uses the bottom Drawer entrance. Controls, focus, close and positioning retain their native behavior. |
|
|
60
|
+
| `Spinner`, `Skeleton`, `Progress`, `Meter`, `ToastRegion`, `UploadItem`, `UploadList` | Busy indicators and value fill. A floating toast enters as a card from its placement edge; an inline toast fades in place. Upload compositions inherit the progress and action recipes. Announcements and final values are immediate. |
|
|
61
|
+
| `Alert`, `EmptyState`, `StatusIndicator`, `ErrorSummary` | State and message changes are immediate. Embedded actions keep Button feedback. |
|
|
63
62
|
|
|
64
63
|
Menu, ContextMenu and title-only navigation flyouts fade in and out where
|
|
65
64
|
discrete top-layer transitions are supported. Dismissal releases focus and
|
|
66
65
|
pointer interaction immediately. When a sibling navigation flyout takes over
|
|
67
66
|
the same rail position, the outgoing panel yields immediately.
|
|
68
|
-
| `Spinner`, `Skeleton`, `Progress`, `Meter`, `ToastRegion`, `UploadItem`, `UploadList` | Busy indicators, value fill and floating toast arrivals. Upload compositions inherit the progress and action recipes. Announcements and final values are immediate. |
|
|
69
|
-
| `Alert`, `EmptyState`, `StatusIndicator`, `ErrorSummary` | State and message changes are immediate. Embedded actions keep Button feedback. |
|
|
@@ -61,7 +61,9 @@ checkboxes with no resulting action.
|
|
|
61
61
|
|
|
62
62
|
Selecting projects reveals one shared action bar floating at the viewport bottom. It shows the
|
|
63
63
|
selected count, changes status, confirms archiving and clears the
|
|
64
|
-
selection.
|
|
64
|
+
selection. DataTable uses `showSelection={false}` to keep its result range and
|
|
65
|
+
pagination while announcing the selected count without displaying it again.
|
|
66
|
+
Selection spans the filtered client collection across its pages.
|
|
65
67
|
Changing the query resets it. Archiving captures IDs and names before opening
|
|
66
68
|
the confirmation. Cancel keeps the selection and records intact.
|
|
67
69
|
|
|
@@ -70,6 +72,10 @@ records after the optional onUpdateStatus callback accepts the whole operation.
|
|
|
70
72
|
Reload returns to the declared fixtures. A rejection keeps accepted records and
|
|
71
73
|
selection intact. Retry uses the failed operation's captured IDs and status.
|
|
72
74
|
Dismiss or a deliberate query/selection change abandons its feedback.
|
|
75
|
+
Report results in a floating ToastRegion so the table stays in place.
|
|
76
|
+
Use the default bottom-end placement. Success expires automatically. A failed
|
|
77
|
+
update offers a persistent Retry action. While the selection rail is visible,
|
|
78
|
+
raise the toast above it without changing the table's scroll clearance.
|
|
73
79
|
|
|
74
80
|
Only one update can run at a time. Pending actions are disabled. If the user
|
|
75
81
|
changes the query or selection while an update is pending, its result still
|
|
@@ -113,7 +119,7 @@ export function ProjectSelectionActions({ projects, pending, onUpdate, onClear }
|
|
|
113
119
|
<VisuallyHidden role="status">{pending ? 'Updating…' : ''}</VisuallyHidden>
|
|
114
120
|
{archive && <AlertDialog
|
|
115
121
|
title={`Archive ${archive.length} ${archive.length === 1 ? 'project' : 'projects'}?`}
|
|
116
|
-
description={`${archive.map(project => project.name).join(', ')} will move to Archived.`}
|
|
122
|
+
description={`${archive.map(project => project.name).join(', ')}\n${archive.length === 1 ? 'It' : 'They'} will move to Archived.`}
|
|
117
123
|
confirmLabel="Archive projects"
|
|
118
124
|
cancelLabel="Keep projects"
|
|
119
125
|
open
|
|
@@ -129,29 +135,52 @@ export function ProjectSelectionActions({ projects, pending, onUpdate, onClear }
|
|
|
129
135
|
|
|
130
136
|
The panel uses the existing floating-surface tokens and a viewport-safe inset.
|
|
131
137
|
It leaves normal document flow and stays centered when the document scrolls.
|
|
138
|
+
Check the table header position and scroll dimensions with zero, one and
|
|
139
|
+
several selected records. A collection that fits should keep fitting on selection.
|
|
132
140
|
BulkActionBar keeps a single pill-shaped row with at most two visible commands
|
|
133
141
|
by default. Lower-priority commands move into More as space shrinks; the host
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
scroll
|
|
142
|
+
keeps the commands on one row. Budget bottom clearance in the base layout
|
|
143
|
+
using the control height, the panel chrome and the viewport inset. At the end
|
|
144
|
+
of a scroll, the final rows and pagination should be reachable above the panel.
|
|
145
|
+
Keep the scroll area inside the bordered Surface and clip the frame at its
|
|
146
|
+
rounded corners so the table footer cannot cover the outline.
|
|
137
147
|
|
|
138
148
|
```css
|
|
139
149
|
.collection-examples { padding-block:var(--flood-sys-space-8); min-inline-size:0; }
|
|
140
150
|
.collection-view-controls { min-inline-size:0; }
|
|
141
151
|
.flood-root .collection-filter-editor { inline-size:min(42rem,calc(100vw - 2rem)); }
|
|
142
152
|
|
|
153
|
+
.collection-projects {
|
|
154
|
+
--collection-selection-inset: max(var(--flood-sys-space-4), env(safe-area-inset-bottom));
|
|
155
|
+
--collection-selection-control-height: var(--flood-sys-control-sm-height);
|
|
156
|
+
--collection-selection-height: calc(var(--collection-selection-control-height) + 2 * (var(--flood-sys-space-1) + var(--flood-sys-stroke-width)));
|
|
157
|
+
padding-block-end: calc(var(--collection-selection-inset) + var(--collection-selection-height) + var(--flood-sys-space-2));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@media (pointer: coarse) {
|
|
161
|
+
.collection-projects {
|
|
162
|
+
--collection-selection-control-height: max(var(--flood-sys-control-sm-height), var(--flood-sys-size-touch));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.collection-table-frame {
|
|
167
|
+
border: var(--flood-sys-stroke-width) solid var(--flood-sys-color-border-decorative);
|
|
168
|
+
overflow: clip;
|
|
169
|
+
}
|
|
170
|
+
|
|
143
171
|
.collection-selection-panel {
|
|
144
172
|
position: fixed;
|
|
145
173
|
inset-inline: 0;
|
|
146
|
-
inset-block-end:
|
|
174
|
+
inset-block-end: var(--collection-selection-inset);
|
|
147
175
|
margin-inline: auto;
|
|
148
176
|
inline-size: min(26rem, calc(100% - 2 * var(--flood-sys-space-4)));
|
|
149
177
|
max-inline-size: calc(100% - 2 * var(--flood-sys-space-4));
|
|
150
178
|
z-index: var(--flood-sys-layer-sticky);
|
|
151
179
|
}
|
|
152
180
|
|
|
153
|
-
.collection-
|
|
154
|
-
|
|
181
|
+
.flood-root .collection-selection-panel + .flood-toast-region {
|
|
182
|
+
inset-block-end: calc(var(--collection-selection-inset) + var(--collection-selection-height) + var(--flood-sys-space-2));
|
|
183
|
+
max-block-size: calc(100dvh - var(--collection-selection-inset) - var(--collection-selection-height) - var(--flood-sys-space-6));
|
|
155
184
|
}
|
|
156
185
|
```
|
|
157
186
|
|
|
@@ -210,6 +239,9 @@ defines reset scope and supplies canClear for any state held by quickFilters.
|
|
|
210
239
|
At most two task-important quick properties remain inline; additional properties
|
|
211
240
|
belong inside Filters. Summaries outside the panel name the property and preserve
|
|
212
241
|
any-match meaning. Container width, rather than viewport size, governs reflow.
|
|
242
|
+
The Projects table places its Columns control in CollectionToolbar's `actions`
|
|
243
|
+
slot via DataTable's `toolbar` render function. It shares the tool row with
|
|
244
|
+
search and filters, and the commands wrap as a group in a narrow container.
|
|
213
245
|
|
|
214
246
|
CollectionViewSwitcher owns the separate accepted-view choice and adaptive
|
|
215
247
|
commands. It reuses Field/Select and Button/OverflowActions with the same size.
|
|
@@ -222,7 +254,7 @@ SearchInput, Select, Menu, MultiSelect and SegmentedControl own input/choice beh
|
|
|
222
254
|
Popover contains the condition draft and Tag removes applied conditions. Button
|
|
223
255
|
and IconButton perform actions. Several view commands use OverflowActions; a
|
|
224
256
|
single command uses Button directly. Field/Fieldset, Grid/Stack/Inline, Dialog,
|
|
225
|
-
AlertDialog and
|
|
257
|
+
AlertDialog and ToastRegion retain their own roles. DataTable/StatusIndicator and
|
|
226
258
|
FileList/FileItem/FileMeta demonstrate two result structures. No owner is inserted
|
|
227
259
|
merely to increase its usage count, and no new package atom is needed for layout.
|
|
228
260
|
|
|
@@ -293,7 +325,7 @@ export function readCollectionViews(key:string,schema:CollectionSchema,storage:V
|
|
|
293
325
|
|
|
294
326
|
```tsx
|
|
295
327
|
"use client";
|
|
296
|
-
import {useEffect,useRef,useState,type FormEvent} from 'react';
|
|
328
|
+
import {useEffect,useRef,useState,type FormEvent,type ReactNode} from 'react';
|
|
297
329
|
import {Alert,AlertDialog,Button,CollectionToolbar,CollectionViewSwitcher,Dialog,Field,FilterGroup,IconSlot,Inline,Input,Menu,Select,Stack,Tag,type OverflowAction} from '@flood-md/react';
|
|
298
330
|
import {collectionQueryKey,copyCollectionQuery,parseCollectionQuery,readCollectionViews,type CollectionQuery,type CollectionSchema,type CollectionView,type FilterProperty,type ViewRead,type ViewStorage} from './collection-views-model';
|
|
299
331
|
|
|
@@ -340,13 +372,13 @@ export function AppliedFilterControl({ property, value, onChange }: {
|
|
|
340
372
|
|
|
341
373
|
interface CollectionViewControlsProps {
|
|
342
374
|
label:string;schema:CollectionSchema;presets:readonly CollectionView[];
|
|
343
|
-
value:CollectionQuery;onValueChange:(query:CollectionQuery)=>void;storageKey:string;storage?:ViewStorage;
|
|
375
|
+
value:CollectionQuery;onValueChange:(query:CollectionQuery)=>void;storageKey:string;storage?:ViewStorage;toolbarActions?:ReactNode;
|
|
344
376
|
}
|
|
345
377
|
type NameDraft={kind:'create'|'rename';id?:string;name:string;snapshot:CollectionQuery;error?:string;invalid?:string};
|
|
346
378
|
const inlineFilterLimit = 2;
|
|
347
379
|
|
|
348
380
|
/** Host recipe: shared behaviour, not a second Flood component implementation. */
|
|
349
|
-
export function CollectionViewControls({label,schema,presets,value,onValueChange,storageKey,storage}:CollectionViewControlsProps){
|
|
381
|
+
export function CollectionViewControls({label,schema,presets,value,onValueChange,storageKey,storage,toolbarActions}:CollectionViewControlsProps){
|
|
350
382
|
const [saved,setSaved]=useState<ViewRead|{state:'loading';views:[]}>({state:'loading',views:[]}),[selectedId,setSelectedId]=useState(presets[0].id);
|
|
351
383
|
const [editing,setEditing]=useState(false),[draft,setDraft]=useState(()=>copyCollectionQuery(value)),[draftError,setDraftError]=useState('');
|
|
352
384
|
const [advancedEditor, setAdvancedEditor] = useState(false);
|
|
@@ -435,6 +467,7 @@ export function CollectionViewControls({label,schema,presets,value,onValueChange
|
|
|
435
467
|
{saved.state==='error'&&<Stack gap="3"><Alert tone="error">Saved views could not be read.</Alert><Inline gap="3"><Button size="sm" variant="subtle" onClick={()=>setReadAttempt(attempt=>attempt+1)}>Retry saved views</Button>{saved.raw!==null&&<Button size="sm" variant="subtle" onClick={exportSnapshot}>Export snapshot</Button>}<Button size="sm" variant="subtle" tone="danger" onClick={()=>{viewInput.current?.focus({preventScroll:true});setConfirmation({kind:'replace'});}}>Replace saved views</Button></Inline></Stack>}
|
|
436
468
|
{operationError&&<Alert tone="error" announcement="polite"><Inline gap="3">{operationError.message}<Button size="sm" variant="ghost" onClick={()=>setOperationError(null)}>Dismiss</Button></Inline></Alert>}
|
|
437
469
|
<CollectionToolbar label={label+' collection toolbar'} searchValue={value.search}
|
|
470
|
+
actions={toolbarActions}
|
|
438
471
|
onSearchValueChange={search=>onValueChange({...value,search})}
|
|
439
472
|
searchLabel={'Search '+label.toLowerCase()} searchPlaceholder={'Search '+label.toLowerCase()+'…'} clearSearchLabel={'Clear '+label.toLowerCase()+' search'}
|
|
440
473
|
appliedFiltersLabel={label+' applied filters'}
|
|
@@ -496,8 +529,8 @@ chosen consumer with actual data and their own chrome.
|
|
|
496
529
|
|
|
497
530
|
```tsx
|
|
498
531
|
"use client";
|
|
499
|
-
import {
|
|
500
|
-
import {
|
|
532
|
+
import {useRef,useState} from 'react';
|
|
533
|
+
import {Button,DataTable,EmptyState,Field,FileItem,FileList,FloodRoot,Heading,Select,Stack,StatusIndicator,Surface,Tabs,ToastRegion,type ToastItem} from '@flood-md/react';
|
|
501
534
|
import {SiteHeader} from './site-header';
|
|
502
535
|
import {SiteFooter} from './site-footer';
|
|
503
536
|
import {CollectionViewControls} from './recipes/collection-views';
|
|
@@ -532,10 +565,8 @@ export function ProjectCollection({storage,onUpdateStatus}: {
|
|
|
532
565
|
const [page, setPage] = useState(1);
|
|
533
566
|
const [selection, setSelection] = useState<readonly string[]>([]);
|
|
534
567
|
const [pending, setPending] = useState(false);
|
|
535
|
-
const [feedback, setFeedback] = useState<
|
|
536
|
-
|
|
537
|
-
{ kind: 'error'; update: ProjectStatusUpdate; message: string } | null
|
|
538
|
-
>(null);
|
|
568
|
+
const [feedback, setFeedback] = useState<ToastItem | null>(null);
|
|
569
|
+
const feedbackSequence = useRef(0);
|
|
539
570
|
const inFlight = useRef(false);
|
|
540
571
|
const collection = useRef<HTMLElement>(null);
|
|
541
572
|
const selectionPanel = useRef<HTMLDivElement>(null);
|
|
@@ -549,20 +580,6 @@ export function ProjectCollection({storage,onUpdateStatus}: {
|
|
|
549
580
|
const selectedProjects=rows.filter(project=>selection.includes(project.id));
|
|
550
581
|
const hasSelection = selectedProjects.length > 0;
|
|
551
582
|
|
|
552
|
-
useEffect(() => {
|
|
553
|
-
const panel = selectionPanel.current;
|
|
554
|
-
const host = collection.current;
|
|
555
|
-
if (!hasSelection || !panel || !host) return;
|
|
556
|
-
const measure = () => host.style.setProperty('--collection-selection-height', `${panel.getBoundingClientRect().height}px`);
|
|
557
|
-
measure();
|
|
558
|
-
const observer = new ResizeObserver(measure);
|
|
559
|
-
observer.observe(panel);
|
|
560
|
-
return () => {
|
|
561
|
-
observer.disconnect();
|
|
562
|
-
host.style.removeProperty('--collection-selection-height');
|
|
563
|
-
};
|
|
564
|
-
}, [hasSelection]);
|
|
565
|
-
|
|
566
583
|
async function updateStatus(proposal: ProjectStatusUpdate) {
|
|
567
584
|
if (inFlight.current) return;
|
|
568
585
|
const update = { ids: [...new Set(proposal.ids)], status: proposal.status };
|
|
@@ -580,33 +597,38 @@ export function ProjectCollection({storage,onUpdateStatus}: {
|
|
|
580
597
|
setSelection(current => current.filter(id => !ids.has(id)));
|
|
581
598
|
setPage(1);
|
|
582
599
|
const result = update.status === 'active' ? 'set active' : update.status;
|
|
583
|
-
setFeedback({
|
|
600
|
+
setFeedback({ id: String(++feedbackSequence.current), status: 'success', title: `${ids.size} ${ids.size === 1 ? 'project' : 'projects'} ${result}.` });
|
|
584
601
|
} catch {
|
|
585
|
-
setFeedback({
|
|
602
|
+
setFeedback({
|
|
603
|
+
id: String(++feedbackSequence.current),
|
|
604
|
+
status: 'error',
|
|
605
|
+
title: `Couldn’t update ${update.ids.length} ${update.ids.length === 1 ? 'project' : 'projects'}.`,
|
|
606
|
+
action: { label: 'Retry update', onClick: () => {
|
|
607
|
+
collection.current?.focus({ preventScroll: true });
|
|
608
|
+
void updateStatus(update);
|
|
609
|
+
}},
|
|
610
|
+
});
|
|
586
611
|
} finally {
|
|
587
612
|
inFlight.current = false;
|
|
588
613
|
setPending(false);
|
|
589
614
|
}
|
|
590
615
|
}
|
|
591
616
|
|
|
592
|
-
return <section ref={collection} tabIndex={-1} className="collection-projects"
|
|
593
|
-
<CollectionViewControls label="Projects" schema={projectSchema} presets={projectPresets} value={query} onValueChange={change} storageKey="flood-project-views-v1" storage={storage}/>
|
|
617
|
+
return <section ref={collection} tabIndex={-1} className="collection-projects" aria-label="Projects collection"><Stack gap="6">
|
|
594
618
|
{hasSelection&&<div ref={selectionPanel} className="collection-selection-panel"><ProjectSelectionActions projects={selectedProjects} pending={pending} onUpdate={update=>{void updateStatus(update);}} onClear={()=>{
|
|
595
619
|
collection.current?.focus({preventScroll:true});setSelection([]);setFeedback(null);
|
|
596
620
|
}}/></div>}
|
|
597
|
-
|
|
598
|
-
{
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
<
|
|
603
|
-
|
|
604
|
-
}}>Dismiss</Button>
|
|
605
|
-
</Inline></Alert>}
|
|
606
|
-
<DataTable caption="Projects" hideCaption minWidth="36rem" selectable pageSize={5} page={page} onPageChange={setPage} selectedIds={selection} onSelectionChange={ids=>{setSelection(ids);setFeedback(null);}} sort={query.sort} onSortChange={sort=>change({...query,sort})}
|
|
621
|
+
<ToastRegion label="Project updates" items={feedback ? [feedback] : []}
|
|
622
|
+
onDismiss={id=>setFeedback(current=>current?.id===id?null:current)}/>
|
|
623
|
+
<Surface padding="0" className="collection-table-frame">
|
|
624
|
+
<DataTable caption="Projects" hideCaption minWidth="36rem" selectable showSelection={false} pageSize={5} page={page} onPageChange={setPage} selectedIds={selection} onSelectionChange={ids=>{setSelection(ids);setFeedback(null);}} sort={query.sort} onSortChange={sort=>change({...query,sort})}
|
|
625
|
+
inset="roomy" showColumnVisibility
|
|
626
|
+
toolbar={({columns}) => <CollectionViewControls label="Projects" schema={projectSchema} presets={projectPresets}
|
|
627
|
+
value={query} onValueChange={change} storageKey="flood-project-views-v1" storage={storage} toolbarActions={columns}/>}
|
|
607
628
|
emptyContent={<EmptyState title="No matching projects" actions={<Button variant="subtle" onClick={()=>{change({...query,search:'',conditions:[]});collection.current?.querySelector<HTMLInputElement>('.flood-collection-toolbar input')?.focus({preventScroll:true});}}>Reset filters</Button>}/>}
|
|
608
|
-
columns={[{id:'name',label:'Project',sortable:true},{id:'owner',label:'Owner',sortable:true},{id:'status',label:'Status',sortable:true},{id:'priority',label:'Priority',sortable:true}]}
|
|
629
|
+
columns={[{id:'name',label:'Project',sortable:true,hideable:false},{id:'owner',label:'Owner',sortable:true},{id:'status',label:'Status',sortable:true},{id:'priority',label:'Priority',sortable:true}]}
|
|
609
630
|
rows={rows.map(project=>({id:project.id,label:project.name,cells:{name:project.name,owner:owners.find(owner=>owner.value===project.owner)!.label,status:<StatusIndicator label={projectSchema.properties[0].options.find(option=>option.value===project.status)!.label} tone={project.status==='active'?'success':project.status==='paused'?'warning':'neutral'}/>,priority:project.priority==='urgent'?'Urgent':'Normal'},sortValues:{name:project.name,owner:project.owner,status:project.status,priority:project.priority==='urgent'?1:0}}))}/>
|
|
631
|
+
</Surface>
|
|
610
632
|
</Stack></section>;
|
|
611
633
|
}
|
|
612
634
|
|
|
@@ -36,11 +36,11 @@ and filters that share the table surface.
|
|
|
36
36
|
},
|
|
37
37
|
}]} />
|
|
38
38
|
)}>
|
|
39
|
-
<Stack gap="
|
|
40
|
-
<Section title="Customer message">
|
|
39
|
+
<Stack gap="8">
|
|
40
|
+
<Section title="Customer message" headingLevel={3} headingSize="group">
|
|
41
41
|
<Text as="p">Confirm who should manage member permissions.</Text>
|
|
42
42
|
</Section>
|
|
43
|
-
<Section title="Assignment">
|
|
43
|
+
<Section title="Assignment" headingLevel={3} headingSize="group">
|
|
44
44
|
<Field label="Owner">{field => <Input {...field} />}</Field>
|
|
45
45
|
</Section>
|
|
46
46
|
</Stack>
|
|
@@ -51,6 +51,20 @@ Use `size="lg"` when long messages and several field groups need the wider
|
|
|
51
51
|
reading area. Compose the body from Section for separately named content,
|
|
52
52
|
DescriptionList for facts and Fieldset for related controls. Check wrapping and
|
|
53
53
|
scrolling with the longest title and a real footer action before selecting a size.
|
|
54
|
+
Use the group heading size inside a detail Drawer to keep the record name prominent.
|
|
55
|
+
|
|
56
|
+
When the detail contains line items and a total, use the Table footer to mark
|
|
57
|
+
the summary and keep its amount aligned with the line-item amounts. A row header
|
|
58
|
+
labels the total, and the Table footer supplies the separator and emphasis.
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
<tfoot>
|
|
62
|
+
<tr>
|
|
63
|
+
<th scope="row" colSpan={2}>Total</th>
|
|
64
|
+
<td data-numeric>{formattedTotal}</td>
|
|
65
|
+
</tr>
|
|
66
|
+
</tfoot>
|
|
67
|
+
```
|
|
54
68
|
|
|
55
69
|
## Keep browsing state and edit state separate
|
|
56
70
|
|
|
@@ -235,7 +249,7 @@ export function RecordManagement() {
|
|
|
235
249
|
<Surface elevation="raised" padding="4">
|
|
236
250
|
<ScrollArea ref={list} role="region" aria-label="Request results" style={{maxBlockSize: "22rem"}}>
|
|
237
251
|
{results.length > 0
|
|
238
|
-
? <DataList aria-label="Requests">
|
|
252
|
+
? <DataList aria-label="Requests" className="request-results">
|
|
239
253
|
{results.map(record => <DataListItem
|
|
240
254
|
key={record.id}
|
|
241
255
|
title={record.title}
|
package/lib/inspect.mjs
CHANGED
|
@@ -51,6 +51,17 @@ export function inspectRenderedUI(options = {}) {
|
|
|
51
51
|
return matches.slice(0, limit);
|
|
52
52
|
};
|
|
53
53
|
if(root===document&&document.documentElement.scrollWidth>innerWidth+1)addIssue({id:'layout.page-overflow',target:{tag:'html',id:document.documentElement.id||null,className:document.documentElement.getAttribute('class')||'',label:'Document'},offset:document.documentElement.scrollWidth-innerWidth,message:'The document extends beyond the viewport. Review the actual overflow owner at this text size; dense content can scroll locally. Hiding page overflow does not repair the layout.'});
|
|
54
|
+
const documentGutter = innerWidth - document.documentElement.clientWidth;
|
|
55
|
+
const scrollOwner = document.scrollingElement;
|
|
56
|
+
if (documentGutter > 1 && scrollOwner && scrollOwner.scrollHeight > scrollOwner.clientHeight + 1) {
|
|
57
|
+
const table = select('.flood-data-table').find(visible);
|
|
58
|
+
if (table) addIssue({
|
|
59
|
+
id: 'layout.record-scroll-owner',
|
|
60
|
+
target: target(table),
|
|
61
|
+
offset: documentGutter,
|
|
62
|
+
message: 'A document scrollbar consumes width on this record view. Compare full and filtered views with classic scrollbars. For a full-window workspace, use AppShell content scrolling so changes in row count keep the table edges in place.',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
54
65
|
for (const button of select('button.flood-button, button.flood-icon-button')) {
|
|
55
66
|
// Text is inspected outside real icon slots, SVGs and visually hidden names.
|
|
56
67
|
const walker = document.createTreeWalker(button, NodeFilter.SHOW_TEXT);
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -89,6 +89,10 @@ override leaves fields and composed controls inconsistent. Keep absolute
|
|
|
89
89
|
material colors separate from contextual control roles and verify the nearest
|
|
90
90
|
nested owner resets those roles. Preserve accent focus and destructive meaning.
|
|
91
91
|
|
|
92
|
+
Vertical pill Toolbar keeps one column on a shared center axis. Use named
|
|
93
|
+
icon-only actions and a consistent control size for a compact rail. Use a
|
|
94
|
+
rounded vertical Toolbar with consistently labeled controls when text is needed.
|
|
95
|
+
|
|
92
96
|
BulkActionBar is a one-row capsule for a small set of selection commands. Keep
|
|
93
97
|
one to three visible commands, prefer two, and disclose the remainder through
|
|
94
98
|
its overflow owner. Do not wrap this floating rail into a second row or use it
|
|
@@ -43,6 +43,18 @@ adapter such as @/ui/button may re-export Flood, but does not change React APIs.
|
|
|
43
43
|
Before handing off, inspect a real composed screen at narrow and wide widths,
|
|
44
44
|
not only isolated controls. See [verification](verification.md).
|
|
45
45
|
|
|
46
|
+
Choose the scroll owner when laying out a screen. In an application workspace
|
|
47
|
+
with fixed chrome or floating collection actions, use AppShell with
|
|
48
|
+
`scroll="content"`. For a padded workspace, set `scrollContentInset="none"`
|
|
49
|
+
when that prop is available and the existing edge padding clears the rails.
|
|
50
|
+
Use the document as scroll owner for pages designed to flow as one document.
|
|
51
|
+
Apply the workspace recipe to filterable or pageable record queues with floating
|
|
52
|
+
actions even when they have no persistent top bar. Keep viewport-fixed actions
|
|
53
|
+
outside the scrolling content. Table's horizontal ScrollArea handles wide
|
|
54
|
+
columns independently.
|
|
55
|
+
When testing a floating selection bar, select the first row and check that the
|
|
56
|
+
viewport and the table keep the same inline size as the overlay rail appears.
|
|
57
|
+
|
|
46
58
|
For constrained overflow, use installed ScrollArea when available. Set height or
|
|
47
59
|
maxHeight on its viewport, choose the needed axis, and provide a useful accessible
|
|
48
60
|
name. Rails overlay content with insets; keep essential content out of the rail
|
|
@@ -60,6 +60,35 @@ the interface-content workflow for contextual placement.
|
|
|
60
60
|
Keep identifiers, such as postal codes and account numbers, aligned as text
|
|
61
61
|
even when they contain only digits. Preserve formatted units and use raw sort
|
|
62
62
|
values when localized text or rich cells would sort differently.
|
|
63
|
+
- Offer column visibility when users need different subsets of fields for real
|
|
64
|
+
tasks in the same table. Leave it off for compact tables with a fixed task.
|
|
65
|
+
Use one layout control per table. If the screen already has View options or
|
|
66
|
+
Display settings, put column visibility there and pass `hiddenColumnIds`.
|
|
67
|
+
Otherwise enable `showColumnVisibility`. If the table already has search and
|
|
68
|
+
filters, use the `toolbar` render function and put its Columns control in
|
|
69
|
+
`CollectionToolbar.actions` to share the existing tool row. Keep the record
|
|
70
|
+
identity visible with `hideable: false` and save preferences by column ID in
|
|
71
|
+
the host. Keep layout preferences independent of the data query. Verify that
|
|
72
|
+
visibility changes preserve page, selection and sort.
|
|
73
|
+
- Enable resizing for columns with variable-length content when readers need
|
|
74
|
+
control over width. Use logical start pinning for record identity and logical
|
|
75
|
+
end for recurring actions when horizontal scrolling would hide them. Persist
|
|
76
|
+
widths by column ID with visibility, and verify keyboard resize, reset, RTL
|
|
77
|
+
and access to the unpinned middle at the smallest intended width.
|
|
78
|
+
- Choose short one-line column labels and use DataTable's `unit` badge for
|
|
79
|
+
a shared short unit. Preserve the complete label when a narrow column
|
|
80
|
+
truncates it. Compare the label, sort icon and resize grip on the same row.
|
|
81
|
+
- Use DataTable's row disclosure for short details the reader needs while
|
|
82
|
+
scanning parent records. Compose a named nested Table for related line items.
|
|
83
|
+
Keep the disclosure and any record-opening action independent. Use a Drawer
|
|
84
|
+
or separate page for longer detail or editing tasks.
|
|
85
|
+
- Use Sparkline when the shape of a series helps a comparison. Supply a label
|
|
86
|
+
that describes the trend, use a shared scale for comparing magnitudes, and
|
|
87
|
+
show decision-critical numbers as text. If a rich trend cell is sortable,
|
|
88
|
+
define the metric it sorts by and supply its raw value.
|
|
89
|
+
Keep the same tone across rows of one metric. A soft area fill supports the
|
|
90
|
+
stroke and follows the same gaps. Reserve semantic tones for meaning and
|
|
91
|
+
keep numbers and labels readable independently of color.
|
|
63
92
|
- DescriptionList expresses named values; DataList/DataListItem express records.
|
|
64
93
|
Card/Section/Surface organize groups without inventing a second typography or
|
|
65
94
|
spacing system. A heading's semantic level does not prescribe its visual size.
|
|
@@ -35,6 +35,14 @@ visible even when reducing decorative emphasis.
|
|
|
35
35
|
|
|
36
36
|
## Scope feedback to its operation
|
|
37
37
|
|
|
38
|
+
Use a floating ToastRegion for the result of a discrete action in a collection
|
|
39
|
+
that is still usable. Keep a failed action's toast available while its retry
|
|
40
|
+
is offered, and preserve the operation's captured targets. Start at bottom-end
|
|
41
|
+
and keep the toast clear of selection actions. Use an inline Alert when the message belongs to
|
|
42
|
+
the affected section, for example when a read failure blocks the table itself.
|
|
43
|
+
Keep its action on the same visual axis as the message in a short titleless
|
|
44
|
+
row. Use Flood Button variants so hover and press inherit the Alert tone.
|
|
45
|
+
|
|
38
46
|
Associate a message with the operation and record that produced it. Preserve
|
|
39
47
|
entered data after refusal and keep a useful error while that operation remains
|
|
40
48
|
unresolved. A successful retry clears/replaces its failure. Explicitly discarding
|
|
@@ -48,6 +56,10 @@ that distinction; the feedback primitive only presents the supplied state.
|
|
|
48
56
|
|
|
49
57
|
## Evidence
|
|
50
58
|
|
|
59
|
+
After a collection action, check that its result is visible at the current
|
|
60
|
+
scroll position and that success, refusal, retry and dismissal leave the
|
|
61
|
+
table header and scroll height in place.
|
|
62
|
+
|
|
51
63
|
Trigger the operation and observe each applicable transition, including failure
|
|
52
64
|
and retry. Also follow failure → explicit discard → list → reopen a saved record;
|
|
53
65
|
the abandoned draft's failure must be gone and the saved data unchanged. Distinguish
|
|
@@ -21,6 +21,10 @@ this workflow makes that meaning visible.
|
|
|
21
21
|
|
|
22
22
|
- Dialog owns its task heading and the body inset. Its dark title uses the
|
|
23
23
|
dialog-title role (1.25rem / 600); it is distinct from compact popup captions.
|
|
24
|
+
- For independent blocks inside a record Drawer, use Section with
|
|
25
|
+
`headingSize="group"` when installed. Keep the Drawer title one level stronger
|
|
26
|
+
visually. Set the semantic heading level separately. Start with a tighter gap
|
|
27
|
+
from each heading to its content and space-8 between independent sections.
|
|
24
28
|
- Field owns the label-to-control relationship: space-1 (.25rem) in the dark
|
|
25
29
|
profile. DateField/TimeField already use that relationship. Leave room between
|
|
26
30
|
independent fields instead of increasing the gap inside every label/control pair.
|
|
@@ -38,6 +38,13 @@ Do not hide essential instructions or risks behind a tooltip merely
|
|
|
38
38
|
to make a screen quieter. Preserve accessible names and associations when reducing
|
|
39
39
|
visible copy; use the component's documented hideLabel API where appropriate.
|
|
40
40
|
|
|
41
|
+
For a bulk confirmation, put the action and its scope first. Use the title for
|
|
42
|
+
the count when it reads naturally there. If the count belongs in the body, give
|
|
43
|
+
it the first sentence. Put a meaningful consequence on a separate line or in a
|
|
44
|
+
separate paragraph. Let each thought wrap naturally on narrow screens. Preserve
|
|
45
|
+
the dialog's accessible description and check singular and plural copy.
|
|
46
|
+
AlertDialog preserves a newline in its string description.
|
|
47
|
+
|
|
41
48
|
## Separate prototype provenance from product state
|
|
42
49
|
|
|
43
50
|
An explicitly requested local prototype or fixture-backed test app does not need
|
|
@@ -22,6 +22,9 @@ opacity and transform on bounded visual parts. Keep an anchored overlay's
|
|
|
22
22
|
positioning root stable while a child moves. Animate color only where the old and
|
|
23
23
|
new colors keep required information legible. Keep focus outlines and error
|
|
24
24
|
messages immediate. Apply hover treatment only to devices that can hover.
|
|
25
|
+
For ToastRegion, animate each arriving card as a unit from its placement edge.
|
|
26
|
+
Keep the region anchored and its announcement available as soon as it appears.
|
|
27
|
+
Inline toasts can fade without moving surrounding content.
|
|
25
28
|
Keep compression off disabled controls, text editors and joined control
|
|
26
29
|
surfaces. Verify release and cancellation without a layout shift.
|
|
27
30
|
Treat disclosure headers as part of their enclosing surface. Keep their
|
|
@@ -56,6 +56,12 @@ DescriptionList or Fieldset as appropriate. Put the next action in the footer
|
|
|
56
56
|
when it must stay reachable during scrolling. Avoid repeating the heading
|
|
57
57
|
as a section name or showing two equivalent dismiss actions in a clean state.
|
|
58
58
|
|
|
59
|
+
Use Sheet when a short touch task, navigation list or option list needs the
|
|
60
|
+
width of the phone. Keep the current choice visible on its trigger and compose
|
|
61
|
+
single-choice options with ListBox. Commit on selection, keep the previous value
|
|
62
|
+
on dismissal, and return focus to the trigger. Use one scrolling body and keep
|
|
63
|
+
long content, the close action and safe-area padding reachable.
|
|
64
|
+
|
|
59
65
|
Overlay material establishes the nested neutral control context automatically.
|
|
60
66
|
Compare its fields, secondary actions, selection and supporting chips with their
|
|
61
67
|
canvas counterparts. Avoid opaque dark action islands or hiding the entire
|
|
@@ -69,6 +69,29 @@ button provides the keyboard entry. Keep checkbox, links and menus independent.
|
|
|
69
69
|
For a real destination, preserve a Link. Use the table toolbar and matching
|
|
70
70
|
edge inset when the heading, filters, cells and footer share one surface.
|
|
71
71
|
|
|
72
|
+
For transient selection commands, look up `recipe:collection-views` in the
|
|
73
|
+
installed catalog. For a full-window record workspace with a changing row count,
|
|
74
|
+
use `AppShell scroll="content" scrollOrientation="vertical"` as the vertical
|
|
75
|
+
scroll owner, including when the page has no app chrome. Keep fixed actions
|
|
76
|
+
outside that ScrollArea. Test a populated view and a filtered or empty view in a
|
|
77
|
+
browser with space-reserving scrollbars. Compare the table's inline edges
|
|
78
|
+
and the document's client width across those views.
|
|
79
|
+
|
|
80
|
+
Place BulkActionBar in a fixed host centered near the bottom
|
|
81
|
+
of the viewport. Keep the host outside the table toolbar. Budget any needed
|
|
82
|
+
bottom clearance in the base layout so selecting rows preserves both the table
|
|
83
|
+
position and the scroll height. Use `showSelection={false}` when BulkActionBar displays the count.
|
|
84
|
+
The table still announces selection and can show its result range and pagination.
|
|
85
|
+
When a bordered Surface wraps a flush table, apply `overflow: clip` to that
|
|
86
|
+
frame. Keep horizontal scrolling on the table's own ScrollArea. Verify the
|
|
87
|
+
outline at both lower corners with a visible footer and check keyboard focus at
|
|
88
|
+
the frame's edges.
|
|
89
|
+
|
|
90
|
+
If a detail table's total summarizes its rows, put it in Table's `tfoot`. Use a
|
|
91
|
+
row header for the label and align the amount with the corresponding numeric
|
|
92
|
+
column. The existing footer border separates it from the last item. Use an
|
|
93
|
+
external summary when its scope includes data beyond the table.
|
|
94
|
+
|
|
72
95
|
Give a queue's summaries only the prominence they earn in the task. If a
|
|
73
96
|
count changes which work to pick up, make the relevant scope clear. If the
|
|
74
97
|
table already exposes the decision, compare a quiet inline count with a
|
|
@@ -74,6 +74,13 @@ and compact states. Center an icon-only rail against the full rail, not the
|
|
|
74
74
|
remainder after a one-sided gutter. A scrollbar appearing must not shift body
|
|
75
75
|
tracks relative to the fixed header/footer. Full-viewport workspaces without
|
|
76
76
|
document overflow must not reserve a document scrollbar strip.
|
|
77
|
+
For a floating action bar, size its host to the actual count, commands and close
|
|
78
|
+
control. Keep the count slot stable across digit changes and bound the panel to
|
|
79
|
+
the viewport. Measure the clearance below the last row with the panel open.
|
|
80
|
+
Compare scroll height, available content width and table position before and
|
|
81
|
+
after selection. Check a viewport where all rows fit and one where the last
|
|
82
|
+
row needs scrolling. Put required clearance in the base layout so selection
|
|
83
|
+
keeps the same scroll geometry.
|
|
77
84
|
Test expanded/compact, missing icons, badges/disclosures, long labels, RTL and
|
|
78
85
|
enlarged text. Measure rendered glyph centers, text starts and action centers
|
|
79
86
|
across distant rows; include the header and footer. Correct the owning primitive
|
package/rules.json
CHANGED
|
@@ -131,6 +131,7 @@
|
|
|
131
131
|
"verify": [
|
|
132
132
|
"320px width",
|
|
133
133
|
"Root 24px and increased text spacing",
|
|
134
|
+
"Record workspace keeps table edges and document width stable across full, filtered and empty views with classic scrollbars",
|
|
134
135
|
"No glyph shrink or unintended page overflow"
|
|
135
136
|
]
|
|
136
137
|
},
|
|
@@ -418,6 +419,9 @@
|
|
|
418
419
|
"verify": [
|
|
419
420
|
"Caption, headers and named-value relationships preserved",
|
|
420
421
|
"Row actions remain independent named controls",
|
|
422
|
+
"When users need column visibility, the table has one layout control in its tool row or existing view settings",
|
|
423
|
+
"If enabled, column resizing works by pointer and keyboard, pinned edges leave the middle reachable, and resetting layout preserves the query",
|
|
424
|
+
"Expanded detail remains associated with its parent record and preserves native table semantics",
|
|
421
425
|
"File metadata/status/progress reuse their existing primitives",
|
|
422
426
|
"Test one supplied server page without local slicing/sorting; declare and validate page/dataset selection scope."
|
|
423
427
|
]
|