@mieweb/ui 0.11.0-dev.57 → 0.11.0-dev.58

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/agent/README.md CHANGED
@@ -22,8 +22,8 @@ governing spec: later tickets cite it instead of re-deciding.
22
22
  | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
23
  | [mieweb-ui.instructions.md](mieweb-ui.instructions.md) | The rules template. Copied verbatim to the consumer's `.github/instructions/` (VS Code Copilot auto-applies it via the `applyTo` frontmatter) and embedded, minus frontmatter, in a marked block in the consumer's `AGENTS.md` (the cross-tool convention). |
24
24
  | [init-agent.mjs](init-agent.mjs) | The `mieweb-ui` bin. Idempotent — rerun after upgrading `@mieweb/ui` to refresh all targets. `--experimental-agents` additionally installs the agent team below. |
25
- | [agents/](agents/) | Agent templates: `mieweb-ui-design` (orchestrator) + `mieweb-ui-styling` + `mieweb-ui-install` + `mieweb-ui-a11y` + `mieweb-ui-i18n` + `mieweb-ui-migration` + `mieweb-ui-richtext` + `mieweb-ui-grids` + `mieweb-ui-datavis`. Installed to the consumer's `.github/agents/` behind `--experimental-agents`. |
26
- | [skills/](skills/) | Skill templates: `mieweb-ui-catalog`, `mieweb-ui-tokens`, `mieweb-ui-icons` (generated — `pnpm agent:skills`, never hand-edit) + `mieweb-ui-brands`, `mieweb-ui-tailwind4`, `mieweb-ui-a11y-checklist`, `mieweb-ui-i18n-checklist`, `mieweb-ui-kerebron`, `mieweb-ui-datavis-api`, `mieweb-ui-charts` (hand-written). Installed to `.github/skills/` behind `--experimental-agents`. |
25
+ | [agents/](agents/) | Agent templates: `mieweb-ui-design` (orchestrator) + `mieweb-ui-styling` + `mieweb-ui-install` + `mieweb-ui-a11y` + `mieweb-ui-i18n` + `mieweb-ui-migration` + `mieweb-ui-richtext` + `mieweb-ui-grids` + `mieweb-ui-datavis` + `mieweb-ui-views`. Installed to the consumer's `.github/agents/` behind `--experimental-agents`. |
26
+ | [skills/](skills/) | Skill templates: `mieweb-ui-catalog`, `mieweb-ui-tokens`, `mieweb-ui-icons` (generated — `pnpm agent:skills`, never hand-edit) + `mieweb-ui-brands`, `mieweb-ui-tailwind4`, `mieweb-ui-a11y-checklist`, `mieweb-ui-i18n-checklist`, `mieweb-ui-kerebron`, `mieweb-ui-datavis-api`, `mieweb-ui-charts`, `mieweb-ui-views` (hand-written). Installed to `.github/skills/` behind `--experimental-agents`. |
27
27
  | `evals/` _(Phase 2+)_ | Golden-question eval sets per specialist; the regression harness for agent knowledge. |
28
28
 
29
29
  Rule content is sourced from [lessons/component-policy.md](../lessons/component-policy.md);
@@ -35,6 +35,10 @@ handoffs:
35
35
  agent: MIE UI DataVis Agent
36
36
  prompt: Handle the visualization question above (Sparkline, Deck, Globe, PointMap, OrgChart, report sections — peers, CSS, SSR boundaries, data shapes) and report the minimal fix.
37
37
  send: false
38
+ - label: Views & layout
39
+ agent: MIE UI Views Agent
40
+ prompt: Handle the views/layout question above (ViewSet, ListView/BoardView/CalendarView/GanttView, dashboards, DockablePanel/FloatingWindow, app shell — accessors, stages, persistence, dates) and report the minimal fix.
41
+ send: false
38
42
  ---
39
43
 
40
44
  # MIE UI Design Agent (orchestrator)
@@ -67,7 +71,7 @@ comparison tables) and `mieweb-ui-icons` (icon aliases and conventions):
67
71
  | Question is about… | Delegate to |
68
72
  | ---------------------------------------------------- | ---------------------- |
69
73
  | install / peer deps / preset / safelist / `@source` | MIE UI Install Agent |
70
- | wiring headless modules (ViewSet, SuperChat) to data | MIE UI Install Agent |
74
+ | wiring headless modules (SuperChat) to data | MIE UI Install Agent |
71
75
  | tokens, CSS vars, Tailwind classes, brands, dark | MIE UI Styling Agent |
72
76
  | ARIA, keyboard nav, screen readers, contrast | MIE UI A11y Agent |
73
77
  | translations, locale bundles, Intl formats, RTL | MIE UI i18n Agent |
@@ -75,9 +79,9 @@ comparison tables) and `mieweb-ui-icons` (icon aliases and conventions):
75
79
  | rich text editors (RichEditor, TextAreaRich, EmailEditor), Kerebron peers/WASM/patches, collab, Markdown rendering | MIE UI Rich Text Agent |
76
80
  | DataVis NITRO config (sources, columns, features, operations), eSheet Builder/Renderer, grid debugging | MIE UI Grids Agent |
77
81
  | charts, maps, globes, decks, org charts (Sparkline, Deck, Globe, PointMap, OrgChart, report sections) | MIE UI DataVis Agent |
82
+ | views & layout (ViewSet, ListView/BoardView/CalendarView/GanttView, ViewSwitcher), dashboards, DockablePanel/FloatingWindow, app shell (Sidebar, AppHeader, PageHeader) | MIE UI Views Agent |
78
83
 
79
- More specialists (Views,
80
- SuperChat) land in later phases; until one ships, answer its domain
84
+ More specialists (SuperChat) land in later phases; until one ships, answer its domain
81
85
  from the skills and the component docs, or defer (below).
82
86
 
83
87
  **Tie-breakers:**
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: MIE UI Install Agent
3
- description: Installation & setup diagnostician for the @mieweb/ui design system — peer dependencies, Tailwind 4 `@source` vs the Tailwind 3 preset/safelist, PostCSS config, CSS entry wiring, dark-mode plumbing, pnpm resolution quirks, and headless module adapters. Invoke for broken/unstyled components after install, peer-dep errors, dark mode or brand CSS not applying, or wiring ViewSet/SuperChat-style modules to app data.
3
+ description: Installation & setup diagnostician for the @mieweb/ui design system — peer dependencies, Tailwind 4 `@source` vs the Tailwind 3 preset/safelist, PostCSS config, CSS entry wiring, dark-mode plumbing, pnpm resolution quirks, and headless module adapters. Invoke for broken/unstyled components after install, peer-dep errors, dark mode or brand CSS not applying, or wiring SuperChat-style headless modules to app data.
4
4
  tools: ['search', 'codebase', 'fetch', 'runInTerminal']
5
5
  handoffs:
6
6
  - label: Apply Fixes
@@ -63,6 +63,9 @@ hoisted; required peers belong in the app's own `package.json`.
63
63
 
64
64
  - "Which component should I use?" → **MIE UI Design Agent** (selection is the
65
65
  orchestrator's domain).
66
+ - Wiring `ViewSet` and the other views to app data (accessors, stages,
67
+ `onMove`) → **MIE UI Views Agent** — you own getting the package installed;
68
+ the Views Agent owns the adapter.
66
69
  - Token choices, hardcoded-color audits, creating/modifying brands →
67
70
  **MIE UI Styling Agent**. You own getting brand CSS _loaded_; Styling owns
68
71
  what's _in_ it.
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: MIE UI Views Agent
3
+ description: Views & layout architect for the @mieweb/ui design system — the ViewSet system (ListView, BoardView, CalendarView, GanttView, ViewSwitcher), dashboards (DashboardWidget, CustomizableDashboard), panels (DockablePanel, FloatingWindow) and the app shell (Sidebar, AppHeader, PageHeader). Invoke for composing a page from views over shared data, wiring accessors and stages, view switching and persistence, dashboard customization, panel/window behavior, and app-shell layout questions.
4
+ tools: ['search', 'codebase', 'fetch', 'runInTerminal']
5
+ handoffs:
6
+ - label: Apply Fixes
7
+ agent: agent
8
+ prompt: Apply the views/layout integration fixes identified above to the codebase.
9
+ send: false
10
+ ---
11
+
12
+ # MIE UI Views Agent
13
+
14
+ You are the views & layout architect for the **@mieweb/ui** design system:
15
+ one collection rendered as list, board, calendar or Gantt; the dashboards
16
+ and panels around it; and the app shell it all sits in. You help consumers
17
+ model their page on the views-over-shared-data contract, then wire it, and
18
+ debug state/persistence/date problems. You diagnose from evidence and
19
+ report a minimal fix — you do not edit files (offer the **Apply Fixes**
20
+ handoff instead).
21
+
22
+ ## Your sources of truth (never inline-duplicate them)
23
+
24
+ - **`mieweb-ui-views` skill** — the domain bible: the which-layout guide,
25
+ the accessor contract, ViewSet's shared-props boundary, per-view
26
+ requirements and traps, dashboard persistence keys, panel modality, the
27
+ shell's three-headers split, and the symptom→fix troubleshooting table.
28
+ Start every answer here.
29
+ - **`mieweb-ui-catalog` skill** — component inventory and selection
30
+ guidance across the whole system.
31
+ - The consumer's installed package — verify props against the `.d.ts` when
32
+ in doubt; never guess.
33
+
34
+ ## Workflow
35
+
36
+ 1. **Model before markup.** The family's unit is *one collection, one
37
+ adapter, several views*: a single `ViewAccessors<T>` serves every view,
38
+ `stages` names the statuses, and `ViewSet` owns the shared props. If the
39
+ consumer is about to write per-view data massaging, that's the finding.
40
+ 2. **Check the boundaries.** Sorting/filtering/paging/export is a grid, not
41
+ a view; a roadmap is `GanttView` at month/quarter cadence, not a
42
+ component; timed day/week slots are `ScheduleCalendar`; two unrelated
43
+ pages are `Tabs`, not a `ViewSwitcher`.
44
+ 3. **Know who owns each piece of state.** `ViewSet.storageKey` only works
45
+ uncontrolled — never both; URL-driven pages own the view in the router.
46
+ `CustomizableDashboard` decides *per concern*: each of
47
+ `order`/`layout`/`hiddenIds` is independently controlled or restored
48
+ from `storageKey`, so mixing is supported. `FloatingWindow` and
49
+ `DockablePanel` never persist — the host does.
50
+ 4. **Dates are wall dates.** Items keep raw date strings; views convert via
51
+ `toDateTime` with a single shared `timeZone`. Any `new Date()` on a
52
+ date-only string in the adapter is a bug.
53
+ 5. **Carry the a11y posture.** Keyboard moves are Ctrl/Cmd+Arrow with live
54
+ announcements; `getHref` cards stay anchors; the calendar's
55
+ `role="table"` is deliberate; collapsed sidebar groups unmount. Don't
56
+ trade these away for visual tweaks.
57
+ 6. **Match the skill's troubleshooting table**, then report root cause,
58
+ evidence, and the smallest fix. Offer **Apply Fixes** for the edit.
59
+
60
+ ## Redirects
61
+
62
+ - "Which component should I use?" across families → **MIE UI Design
63
+ Agent**; you own selection *within* views, dashboards, panels and shell.
64
+ - Tables, grids, sorting/filtering/paging/export, NITRO sources →
65
+ **MIE UI Grids Agent** (including the grid in `ViewSet`'s `table` slot).
66
+ - Charts/maps inside dashboard widgets or overview slots →
67
+ **MIE UI DataVis Agent**; you own the slot, they own the visual.
68
+ - Marketing-site chrome (`SiteHeader`, `SiteFooter`, Hero/CTA sections) →
69
+ the orchestrator/catalog (marketing domain; full page templates live in
70
+ `/templates`).
71
+ - Token/Tailwind/brand theming → **MIE UI Styling Agent** (you keep the
72
+ accent-token contract: wash + marker, never text color).
73
+ - Accessibility and i18n/RTL audits → **MIE UI A11y Agent** / **MIE UI i18n
74
+ Agent** — with the structural posture above carried by you.
75
+
76
+ ## Rules
77
+
78
+ 1. Never invent props, view ids, or exports — verify against the
79
+ `mieweb-ui-views` skill or the installed types. There is no
80
+ `RoadmapView`.
81
+ 2. Never put fetching, routing, or mutation inside a view: `items` in,
82
+ `onOpen`/`getHref`/`onMove` out. Optimistic move handling is built into
83
+ the board — don't re-implement it.
84
+ 3. No view virtualizes. Thousands of rows is a `DataVisNitroGrid` problem —
85
+ route it, don't tune it.
86
+ 4. Keep the shared-props boundary: `items`, `accessors`, `stages`,
87
+ `onMove`, `timeZone`, `locale` live on `ViewSet`, never duplicated into
88
+ per-view overrides.
89
+ 5. Accent tokens never color text; the class strings are safelisted —
90
+ don't synthesize new ones.
91
+ 6. Prefer the smallest change that fixes the root cause; no page rewrites.
92
+ 7. The installed `mieweb-ui.instructions.md` rules apply to any code you
93
+ recommend.
@@ -23,6 +23,7 @@ These files are maintainer-side quality tooling. They are **not** shipped by
23
23
  | [richtext.md](./richtext.md) | Rich text / Kerebron editors | MIE UI Rich Text Agent |
24
24
  | [grids.md](./grids.md) | DataVis NITRO + eSheet grids | MIE UI Grids Agent |
25
25
  | [datavis.md](./datavis.md) | Charts / maps / decks / org charts | MIE UI DataVis Agent |
26
+ | [views.md](./views.md) | Views / dashboards / panels / shell | MIE UI Views Agent |
26
27
 
27
28
  ## Entry format
28
29
 
@@ -160,14 +160,14 @@ format and scoring.
160
160
 
161
161
  ### INS-17 — Wiring headless modules
162
162
 
163
- - **Ask:** "How do I connect `ViewSet` (or SuperChat-style modules) to my app
163
+ - **Ask:** "How do I connect SuperChat-style headless modules to my app
164
164
  data?"
165
165
  - **Expect:** Write an adapter in the app — the module never fetches,
166
- subscribes, or navigates. Two distinct contracts: **ViewSet** (collection
167
- views) takes `items`/`loading`/`error`, accessor props, promise-returning
168
- `on*` callbacks (built-in optimistic UI + revert), and `onOpen` + `getHref`;
169
- **SuperChatInbox** has its own props (`conversations`, `onMessageSent`,
170
- `onConversationOpened`) — never hand it collection-view props.
166
+ subscribes, or navigates. `SuperChatInbox` has its own props
167
+ (`conversations`, `onMessageSent`, `onConversationOpened`) — never hand it
168
+ collection-view props. `ViewSet` wiring (items, accessors,
169
+ promise-returning `on*` callbacks) is the Views Agent's domain — redirect
170
+ it there.
171
171
  - **Agent:** MIE UI Install Agent
172
172
  - **Source:** agent/skills/mieweb-ui-tailwind4/SKILL.md — Wiring headless modules; lessons/wiring-headless-modules.md
173
173
 
@@ -0,0 +1,215 @@
1
+ # Evals — Views / dashboards / panels / shell
2
+
3
+ Primary agent: **MIE UI Views Agent**. See [README.md](./README.md) for
4
+ format and scoring. The #588 done-when: the agent explains the
5
+ views-over-shared-data model and answers these without inventing API.
6
+
7
+ ### VW-1 — The model
8
+
9
+ - **Ask:** "My tickets page needs a list, a kanban board, and a calendar the user can switch between."
10
+ - **Expect:** One `ViewSet` over one collection: a single
11
+ `ViewAccessors<WorkItem>` adapter (`getId`/`getTitle`/`getStatus`/
12
+ `getStart`…), `stages` naming the statuses, shared `items`/`onMove`/
13
+ `timeZone` on the set. Not three components with three data massages.
14
+ - **Agent:** MIE UI Views Agent
15
+ - **Source:** src/views/MAINTAINERS.md; Views.mdx
16
+
17
+ ### VW-2 — View vs grid
18
+
19
+ - **Ask:** "Users want to sort, filter, and export the list view."
20
+ - **Expect:** That's a grid, not a view — `DataVisNitroGrid` (Grids
21
+ domain); `ViewSet` renders it in the `table` slot rather than wrapping
22
+ it. `ListView` stays for the lightweight grouped list.
23
+ - **Agent:** MIE UI Grids Agent (via the Views Agent's redirect)
24
+ - **Source:** Views.mdx — "Tables are not a view"; mieweb-ui-views skill
25
+
26
+ ### VW-3 — Roadmap trap
27
+
28
+ - **Ask:** "How do I import RoadmapView?"
29
+ - **Expect:** There is no `RoadmapView` — a roadmap is `GanttView` at
30
+ `cadence="month" | "quarter"` with `groupByLane`; the `roadmap` view id
31
+ exists only to label a switcher option. Inventing the import FAILS.
32
+ (Editorial hand-authored year → `YearTimeline`.)
33
+ - **Agent:** MIE UI Views Agent
34
+ - **Source:** Views.mdx — roadmap callout; src/views/MAINTAINERS.md
35
+
36
+ ### VW-4 — Dates shift a day
37
+
38
+ - **Ask:** "Items due 2026-03-01 show on Feb 28 for our Los Angeles users."
39
+ - **Expect:** A `new Date('YYYY-MM-DD')` somewhere in the adapter — that
40
+ parses as UTC midnight, which is still Feb 28 in zones behind UTC. Keep
41
+ date-only strings raw in accessors (views convert via `toDateTime` as
42
+ wall dates) and set one shared `timeZone` on `ViewSet`.
43
+ - **Agent:** MIE UI Views Agent
44
+ - **Source:** src/views/types.ts toDateTime; src/views/MAINTAINERS.md
45
+
46
+ ### VW-5 — Empty board
47
+
48
+ - **Ask:** "BoardView renders but shows no columns."
49
+ - **Expect:** Board columns come from `stages` and require the `getStatus`
50
+ accessor; provide both — `stages` also orders and pins the columns.
51
+ - **Agent:** MIE UI Views Agent
52
+ - **Source:** BoardView.tsx; Views.mdx accessor contract
53
+
54
+ ### VW-6 — Keyboard drag
55
+
56
+ - **Ask:** "How do keyboard users move cards on the board? dnd-kit's keyboard sensor doesn't seem wired."
57
+ - **Expect:** By design: the board owns Ctrl/Cmd+Arrow moves directly
58
+ (announced via a live region); dnd-kit contributes pointer listeners
59
+ only, and its role attributes are suppressed so `getHref` cards stay
60
+ real anchors.
61
+ - **Agent:** MIE UI Views Agent
62
+ - **Source:** src/views/MAINTAINERS.md — board keyboard; BoardView.tsx
63
+
64
+ ### VW-7 — Optimistic moves
65
+
66
+ - **Ask:** "Should I update my store optimistically when a card is dragged, then roll back on API failure?"
67
+ - **Expect:** Don't re-implement it — return your mutation promise from
68
+ `onMove`: the board shows pending state and reverts on rejection.
69
+ - **Agent:** MIE UI Views Agent
70
+ - **Source:** BoardView.tsx onMove; lessons/wiring-headless-modules.md
71
+
72
+ ### VW-8 — View persistence
73
+
74
+ - **Ask:** "I pass `view` from my router and also set `storageKey`, but the stored choice never applies."
75
+ - **Expect:** By design — `storageKey` is ignored when `view` is
76
+ controlled (no double source of truth). URL-driven pages persist the
77
+ view in the router; drop `storageKey`. Stored reads happen post-mount so
78
+ SSR hydration matches.
79
+ - **Agent:** MIE UI Views Agent
80
+ - **Source:** ViewSet.tsx storageKey doc + mount effect
81
+
82
+ ### VW-9 — Overview slot
83
+
84
+ - **Ask:** "The first tab of our ViewSet should be KPI tiles and quick links, not a view of the items."
85
+ - **Expect:** That's the `overview` slot — domain content the library has
86
+ no opinion on; fill it with `CustomizableDashboard` + `DashboardWidget`
87
+ (and `QuickLinksCard`).
88
+ - **Agent:** MIE UI Views Agent
89
+ - **Source:** Views.mdx — "Overview is not a view"; Dashboards.mdx
90
+
91
+ ### VW-10 — Dashboard persistence
92
+
93
+ - **Ask:** "Where does CustomizableDashboard store the user's layout, and why did it reset after we renamed the page?"
94
+ - **Expect:** Under `storageKey`-derived keys — `{key}-portlet-order`,
95
+ `{key}-dashboard-layout`, `{key}-dashboard-hidden` — so changing the key
96
+ orphans saved state. Restore happens in a mount effect (SSR hydration
97
+ matches); fully controlled `order`/`layout`/`hiddenIds` is the
98
+ alternative.
99
+ - **Agent:** MIE UI Views Agent
100
+ - **Source:** CustomizableDashboard MAINTAINERS.md — persistence
101
+
102
+ ### VW-11 — Drag handle
103
+
104
+ - **Ask:** "Where did this floating grip button in the corner of my custom dashboard widget come from?"
105
+ - **Expect:** Nothing matched `dragHandleSelector` (default
106
+ `[data-slot="dashboard-widget-header"]`), so the dashboard injected a
107
+ floating handle — dragging always works; the selector only controls
108
+ handle *placement*. Add the slot attribute (or a matching selector) to
109
+ portal the handle into the widget's own chrome.
110
+ - **Agent:** MIE UI Views Agent
111
+ - **Source:** CustomizableDashboard MAINTAINERS.md — drag handles
112
+
113
+ ### VW-12 — Tablet drop bug
114
+
115
+ - **Ask:** "On tablets, users can't drop a widget into an empty dashboard column."
116
+ - **Expect:** Known limitation: below `lg` in 3-column mode the columns
117
+ flatten with `display: contents`, so an empty column has no drop box.
118
+ Not a consumer bug — work around via another column or wider viewport.
119
+ - **Agent:** MIE UI Views Agent
120
+ - **Source:** CustomizableDashboard MAINTAINERS.md — responsive edge case
121
+
122
+ ### VW-13 — Panel modality
123
+
124
+ - **Ask:** "Users should be able to keep working on the page while the detail panel is docked, but the full-screen version should trap focus."
125
+ - **Expect:** Exactly `DockablePanel`'s two modes: `full` is modal (focus
126
+ trap, `aria-modal`, background `inert`), `docked` is a non-modal
127
+ bottom-right strip. Host owns `mode`/`onModeChange` (and any
128
+ persistence). Docked content is clipped, not resized.
129
+ - **Agent:** MIE UI Views Agent
130
+ - **Source:** DockablePanel.tsx modality docs
131
+
132
+ ### VW-14 — FloatingWindow state
133
+
134
+ - **Ask:** "FloatingWindow closes/forgets its minimized state when my component re-renders — where's its internal state?"
135
+ - **Expect:** There is none for `open`/`minimized` — it's fully
136
+ controlled; only transient drag size/position is internal. Own the state
137
+ in the host, wire `MinimizedWindow` yourself; min size clamps to
138
+ 600×400; pass `aria-label` when the title isn't plain text.
139
+ - **Agent:** MIE UI Views Agent
140
+ - **Source:** FloatingWindow MAINTAINERS.md
141
+
142
+ ### VW-15 — Three headers
143
+
144
+ - **Ask:** "AppHeader, PageHeader, SiteHeader — which goes where?"
145
+ - **Expect:** `AppHeader` = sticky top bar of the signed-in app (beside
146
+ `Sidebar`, slot children); `PageHeader` = the `<h1>` block inside a page
147
+ (not a `<header>` landmark); `SiteHeader` = marketing chrome (main-entry
148
+ export; the `/templates` marketing pages are its home turf).
149
+ - **Agent:** MIE UI Views Agent
150
+ - **Source:** Layout.mdx — "Three headers, three jobs"
151
+
152
+ ### VW-16 — Sidebar collapse a11y
153
+
154
+ - **Ask:** "Collapsed sidebar sections unmount their items — shouldn't we just hide them with CSS to keep DOM stable?"
155
+ - **Expect:** No — unmounting is deliberate: keyboard users must not tab
156
+ into invisible items, and animation height measurement depends on it.
157
+ - **Agent:** MIE UI Views Agent
158
+ - **Source:** Sidebar MAINTAINERS.md
159
+
160
+ ### VW-17 — Calendar role
161
+
162
+ - **Ask:** "Axe-minded reviewer says the month view should be role=\"grid\"."
163
+ - **Expect:** `role="table"` is deliberate — `grid` promises roving focus
164
+ the view doesn't implement; switching the role without adding that focus
165
+ model makes a11y worse. Full audit → A11y agent.
166
+ - **Agent:** MIE UI Views Agent
167
+ - **Source:** src/views/MAINTAINERS.md — calendar semantics
168
+
169
+ ### VW-18 — Scale
170
+
171
+ - **Ask:** "GanttView gets sluggish with ~5,000 items."
172
+ - **Expect:** No view virtualizes — every item renders by design.
173
+ Page/filter upstream, or that scale of tabular data belongs to
174
+ `DataVisNitroGrid` (Grids domain).
175
+ - **Agent:** MIE UI Views Agent
176
+ - **Source:** src/views/MAINTAINERS.md — no virtualization; Views.mdx
177
+
178
+ ### VW-19 — Calendar boundaries
179
+
180
+ - **Ask:** "We need a week view with 30-minute appointment slots — CalendarView?"
181
+ - **Expect:** No — `CalendarView` is a month grid over day-granularity
182
+ items; timed day/week slots are `ScheduleCalendar` (Date & Time family).
183
+ - **Agent:** MIE UI Views Agent
184
+ - **Source:** Views.mdx — "Which one?" table
185
+
186
+ ### VW-20 — i18n posture
187
+
188
+ - **Ask:** "How do I localize the views, and is the dashboard fully translatable?"
189
+ - **Expect:** Views: `labels` objects (English defaults) + `locale`/
190
+ `timeZone` shared via `ViewSet`. Dashboards have documented gaps
191
+ (hard-coded English strings, English-only drag announcements) — audit →
192
+ i18n agent.
193
+ - **Agent:** MIE UI Views Agent
194
+ - **Source:** src/views/types.ts ViewLabels; Dashboards.mdx limitations
195
+
196
+ ### VW-21 — Accent colors
197
+
198
+ - **Ask:** "Can I set each stage's accent to our brand hex and color the card titles to match?"
199
+ - **Expect:** Accents are tokens (`primary`/`success`/…), applied as wash +
200
+ border + marker and **never text color** (declared colours fail WCAG AA
201
+ on text); the class strings are safelisted — don't synthesize new ones.
202
+ - **Agent:** MIE UI Views Agent
203
+ - **Source:** src/views/types.ts accentClasses; src/views/MAINTAINERS.md
204
+
205
+ ### VW-22 — Plumbing
206
+
207
+ - **Ask:** "Does the views family need peer installs, CSS imports, or ssr:false like the charts?"
208
+ - **Expect:** No — everything exports from the main entry; `@dnd-kit/*`
209
+ and `luxon` are bundled regular deps; all `'use client'`, so a client
210
+ boundary suffices and prerender is safe; `localStorage` access is
211
+ try/catch-guarded. One caveat: `AppHeaderSearch`'s ⌘/Ctrl shortcut hint
212
+ is computed from `navigator` at module scope — server renders Ctrl, Mac
213
+ clients ⌘ (hydration mismatch on that hint only).
214
+ - **Agent:** MIE UI Views Agent
215
+ - **Source:** src/index.ts; package.json; ViewSet.tsx readStored
@@ -0,0 +1,186 @@
1
+ ---
2
+ name: mieweb-ui-views
3
+ description: Views & layout components of @mieweb/ui — the ViewSet system (ListView, BoardView, CalendarView, GanttView, ViewSwitcher), dashboards (DashboardWidget, CustomizableDashboard), panels (DockablePanel, FloatingWindow) and the app shell (Sidebar, AppHeader, PageHeader). The views-over-shared-data model, accessor contract, controlled/persisted state, drag-and-drop, and a11y/i18n posture. USE WHEN selecting, configuring or debugging a view/dashboard/panel/shell component, wiring views to app data, or deciding view vs grid vs tabs.
4
+ ---
5
+
6
+ # @mieweb/ui views & layout
7
+
8
+ Sourced from `src/views/MAINTAINERS.md` (the family's design contract),
9
+ component source/MAINTAINERS, and `src/catalog/Views.mdx` / `Dashboards.mdx`
10
+ / `Layout.mdx`. Verify props against the installed `.d.ts` — never invent.
11
+
12
+ ## Which layout?
13
+
14
+ > Most pages want **`ViewSet`** and nothing else: it renders the switcher,
15
+ > the enabled views, and shared slots. Reach for an individual view only
16
+ > when a page draws exactly one layout forever.
17
+
18
+ - **ViewSwitcher** — users pick between layouts of *one collection*. Two
19
+ unrelated pages → `Tabs` instead.
20
+ - **ListView** — scannable vertical list, optional grouping, row slot you
21
+ own. Rows need sorting/filtering/paging/export → DataVis NITRO (Grids
22
+ domain).
23
+ - **BoardView** — items move between named stages and the stage is the
24
+ primary fact. Stages fixed, items never move → ListView grouped by status.
25
+ - **CalendarView** — items land on/span calendar days; month grid is the
26
+ point. Timed appointments in day/week slots → `ScheduleCalendar`.
27
+ - **GanttView** — items have start+end and overlap matters across a range.
28
+ One record's progress through steps → `Timeline`.
29
+ - **A roadmap is not a component.** It is `GanttView` at
30
+ `cadence="month" | "quarter"` with `groupByLane`; the `roadmap` view id
31
+ exists only so a switcher can label it. There is no `RoadmapView` export.
32
+ Editorial hand-authored year → `YearTimeline`.
33
+ - **Tables are not views.** A tabular layout of the collection is
34
+ `DataVisNitroGrid`, rendered in `ViewSet`'s `table` slot.
35
+ - **Overview is not a view.** It's domain content — fill the `overview`
36
+ slot with `CustomizableDashboard` + `DashboardWidget`.
37
+
38
+ ## Entries, deps, SSR
39
+
40
+ Everything in this family exports from the **main** entry — no subpath, no
41
+ optional peers, no extra CSS (`@dnd-kit/*` and `luxon` are bundled regular
42
+ dependencies). All are `'use client'` components; a client boundary is
43
+ enough and prerender is safe. One caveat: `AppHeaderSearch` computes a
44
+ guarded `isMac` from `window.navigator` at module scope, so the server
45
+ renders the Ctrl shortcut hint and Mac clients render ⌘ — expect that
46
+ hydration mismatch if you prerender it.
47
+ `localStorage` access (ViewSet, CustomizableDashboard) is try/catch-guarded
48
+ — Safari private browsing throws on *access*, optional chaining alone is
49
+ insufficient.
50
+
51
+ ## The views-over-shared-data model
52
+
53
+ One collection, several layouts, one adapter:
54
+
55
+ - **Accessors, not records.** Views read through `ViewAccessors<T>`
56
+ (`getId`, `getTitle`, `getSubtitle`, `getStatus`, `getStart`, `getEnd`,
57
+ `getGroup`, `getAccent`) and never assume item shape. Write one adapter
58
+ per entity; each view uses only the accessors it needs — `BoardView`
59
+ requires `getStatus`, `GanttView`/`CalendarView` require `getStart`.
60
+ - **Nothing fetches, routes, or mutates.** Views take `items` / `loading` /
61
+ `error` and hand changes back through `onOpen`, `getHref`, `onMove`.
62
+ - **Dates go through `toDateTime`, never `new Date()`.** Date-only strings
63
+ stay wall dates; `new Date('2026-03-01')` is UTC midnight and shifts a
64
+ day for half the world.
65
+ - **Accent tokens carry no text color** (`primary`/`success`/`warning`/
66
+ `destructive`/`info`/`neutral`) — wash + border + marker only; labels use
67
+ foreground tokens. The class strings are safelisted in both Tailwind
68
+ presets.
69
+ - **`stages` name statuses.** They order and pin board columns and
70
+ `groupBy="status"` lists — and must not seed groups under other groupings.
71
+ - **`ViewSet` owns the shared props** — `items`, `accessors`, `stages`,
72
+ `onMove`, `timeZone`, `locale`, load state. Per-view escape hatches
73
+ (`listProps` etc.) exclude them by type: one collection shows the same
74
+ data in every view.
75
+
76
+ ## ViewSet
77
+
78
+ `view`/`defaultView`/`onViewChange`; `storageKey` remembers the choice but
79
+ is **ignored when `view` is controlled** — a page with URL state owns it in
80
+ the router. Stored value is read in a post-mount effect so server and first
81
+ client render agree. Slots: `toolbar`, `filters`, `overview`, `table`,
82
+ `detail` (list-plus-detail). `ViewSetLabels` is the union of every view's
83
+ labels — not the same type as a single view's `labels` prop.
84
+
85
+ ## BoardView
86
+
87
+ Columns come from `stages`; drag-and-drop is `@dnd-kit` **pointer listeners
88
+ only** — dnd-kit's role attributes are deliberately suppressed so cards with
89
+ `getHref` stay real anchors. Keyboard moves are the board's own
90
+ Ctrl/Cmd+Arrow handling (not dnd-kit's sensor), announced via a live region;
91
+ the selected card carries `aria-current`. `onMove` may return a promise:
92
+ the board shows pending state and reverts on rejection — optimistic updates
93
+ are built in, don't re-implement them.
94
+
95
+ ## CalendarView
96
+
97
+ Month grid over Luxon. `timeZone` (IANA) sets day boundaries, `locale`
98
+ formats month/weekday names, `weekStartsOn` is 1 (Monday) … 7 (Sunday);
99
+ controlled `month`/`defaultMonth`; `maxPerDay` collapses overflow into a
100
+ "+N more" row. The grid is `role="table"` **on purpose** — `role="grid"`
101
+ promises roving focus the view doesn't implement; don't "fix" axe output by
102
+ switching roles.
103
+
104
+ ## GanttView
105
+
106
+ `cadence: 'day' | 'week' | 'month' | 'quarter'`; bars are visual only — no
107
+ dependency links. Range auto-computes from item min/max when
108
+ `rangeStart`/`rangeEnd` are omitted. The scroll region is focusable
109
+ (`tabIndex={0}` + accessible name) — keep it that way.
110
+
111
+ ## Dashboards
112
+
113
+ - **`DashboardWidget`** is the portlet tile (title bar, count badge, body
114
+ variants). It knows nothing about position.
115
+ - **`CustomizableDashboard`** adds user drag/hide/show. Persistence:
116
+ `storageKey` writes **three** keys — `{key}-portlet-order`,
117
+ `{key}-dashboard-layout`, `{key}-dashboard-hidden` — restored in a mount
118
+ effect so SSR hydration matches; each of `order`/`layout`/`hiddenIds` is
119
+ independently controlled or persisted — mixing is supported. The drag
120
+ handle portals into the first element matching `dragHandleSelector`
121
+ (default `[data-slot="dashboard-widget-header"]`); when nothing matches,
122
+ a floating handle is injected in the portlet's corner — the selector
123
+ controls *placement*, not drag ability.
124
+ Known trap: below `lg` in 3-column mode the columns flatten with
125
+ `display: contents`, so dropping into an *empty* column has no drop box.
126
+
127
+ ## Panels
128
+
129
+ - **`DockablePanel`** — two modes via `mode`/`onModeChange` (host persists):
130
+ `full` is modal (focus trap, `aria-modal`, background `inert`); `docked`
131
+ is a non-modal bottom-right strip you can work behind. Docked content is
132
+ clipped (`overflow: hidden`), not resized — embedded ResizeObservers don't
133
+ remeasure. Portals into `container` (default `document.body`).
134
+ - **`FloatingWindow`** — fully controlled (`open`/`minimized` are the
135
+ host's); hand-rolled pointer drag/resize clamped to 600×400 minimum;
136
+ fixed `z-index: 50` (inline) — `--mieweb-dockable-panel-z` is
137
+ DockablePanel-only. Forwards a dialog
138
+ role — pass `aria-label` when the title isn't plain text.
139
+ `MinimizedWindow` is a separate export; wiring the pair is the host's job.
140
+
141
+ ## App shell — three headers, three jobs
142
+
143
+ - **`AppHeader`** — sticky top bar of the signed-in app, beside `Sidebar`;
144
+ slot children (`AppHeaderBrand`/`Title`/`Search`/`IconButton`/`UserMenu`),
145
+ no nav opinions of its own.
146
+ - **`PageHeader`** — the `<h1>` block inside a page (title, subtitle, icon,
147
+ actions, Breadcrumb/Tabs children). Not a `<header>` landmark.
148
+ - **`SiteHeader`/`SiteFooter`** are *marketing* chrome — main-entry exports,
149
+ but their domain is the marketing pages (`/templates` holds the full page
150
+ templates that use them).
151
+ - **`Sidebar`** — mobile drawer uses `100dvh`; collapsed nav groups
152
+ **unmount** rather than CSS-hide (keyboard users must not tab into
153
+ invisible items). Record-banner headers (`PatientHeader` etc.) are
154
+ clinical components, not shell.
155
+
156
+ ## Performance
157
+
158
+ **No view virtualizes.** Every item renders; fetching/paging stays upstream.
159
+ Hundreds of items are fine; thousands are not — that's a
160
+ `DataVisNitroGrid` problem (Grids domain).
161
+
162
+ ## i18n & a11y posture
163
+
164
+ Every view takes a `labels` object (English defaults); date views take
165
+ `locale` + `timeZone` (shared via ViewSet so all views agree on day
166
+ boundaries). Known catalog-documented gaps: dashboard strings are
167
+ hard-coded English, drag announcements English-only, some `DashboardWidget`
168
+ styling is physical-direction (RTL gaps). String/RTL audits belong to the
169
+ i18n agent; views carry the structural a11y themselves.
170
+
171
+ ## Troubleshooting
172
+
173
+ | Symptom | Cause | Fix |
174
+ | --- | --- | --- |
175
+ | Items land a day early for some users | `new Date('YYYY-MM-DD')` parses as UTC midnight | Keep date-only strings raw in accessors; views convert via `toDateTime` as wall dates |
176
+ | Board renders no columns | Missing `getStatus` accessor or `stages` | Provide both; `stages` orders and pins the columns |
177
+ | Can't drag cards with the keyboard | Expecting dnd-kit's keyboard sensor | Use Ctrl/Cmd+Arrow — the board's own handling, announced via live region |
178
+ | Card loses link semantics when draggable | Assuming dnd-kit role attributes | They're suppressed by design; `getHref` cards stay anchors |
179
+ | Chosen view doesn't persist | `view` is controlled, so `storageKey` is ignored | Persist in your URL/router state, or go uncontrolled |
180
+ | Calendar/Gantt disagree on an item's day | Views given different zones | Set one `timeZone` on `ViewSet` — it's shared on purpose |
181
+ | Dashboard order lost after refactor | `storageKey` prefix changed | It writes `-portlet-order` / `-dashboard-layout` / `-dashboard-hidden` under the key |
182
+ | Can't drop into an empty dashboard column on tablet | `display: contents` flattening below `lg` in 3-col mode | Known limitation; move via another column or wider viewport |
183
+ | Axe/reviewer wants calendar `role="grid"` | Grid implies roving focus the view doesn't implement | `role="table"` is deliberate — don't switch roles without adding the focus model |
184
+ | Gantt/list sluggish with thousands of rows | No view virtualizes | Page/filter upstream, or use `DataVisNitroGrid` |
185
+ | FloatingWindow won't stay open/minimized | It's fully controlled | Own `open`/`minimized` in host state; wire `MinimizedWindow` yourself |
186
+ | `RoadmapView` import fails | It doesn't exist | `GanttView` with `cadence="month"|"quarter"` + `groupByLane` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mieweb/ui",
3
- "version": "0.11.0-dev.57",
3
+ "version": "0.11.0-dev.58",
4
4
  "description": "A themeable, accessible React component library built with Tailwind CSS",
5
5
  "author": "Medical Informatics Engineering, Inc.",
6
6
  "license": "SEE LICENSE IN LICENSE",