mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Chart builder: data-aware defaults, one-click hint fixes, canvas preview, fullscreen modal
This commit is contained in:
@@ -11,7 +11,9 @@ authoritative architecture for adding, opening, closing, and rendering modals.
|
||||
- **At most one modal open at a time** (mandated by the product spec). Opening a
|
||||
modal closes any other; the two never overlap.
|
||||
- **Uniform dismissal**: close button, `Escape`, or backdrop click — never a
|
||||
click inside the body.
|
||||
click inside the body. A modal holding in-progress work can opt out of the
|
||||
**backdrop** click (`dismissOnBackdrop: false`) so a stray click can't discard it
|
||||
(the Chart Builder does); close button and `Escape` still dismiss.
|
||||
- **Accessible by default**: focus moves into the modal on open and returns to
|
||||
the trigger on close.
|
||||
- **Unsaved-change safety** for editing modals, with an explicit opt-out for
|
||||
@@ -492,6 +494,13 @@ export function useFocusTrap<T extends HTMLElement = HTMLDivElement>(
|
||||
- Don't dismiss on clicks inside the body, and don't let Escape fire when no
|
||||
modal is open (the handler only exists while a modal renders).
|
||||
|
||||
**Sizing & backdrop opt-out.** The shell picks a **size tier** by modal: a small form
|
||||
(Extract), a large two-pane manager (Datasets), or a near-fullscreen **work surface**
|
||||
(Chart Builder — a config pane plus a chart that wants room). The two larger tiers have a
|
||||
definite height so their inner panes scroll **internally** rather than the modal growing
|
||||
past the viewport. A modal opts a backdrop click out of dismissal with the registry's
|
||||
`dismissOnBackdrop: false` (above).
|
||||
|
||||
---
|
||||
|
||||
## Confirmation & alert dialogs
|
||||
|
||||
@@ -101,6 +101,23 @@ async function rerender(node: HTMLElement, spec: TopLevelSpec, config: Config) {
|
||||
|
||||
- **Do** pass `actions: false`. Astrolabe owns its own export/copy affordances;
|
||||
the library's overlay menu does not belong on the preview.
|
||||
- **SVG is the default renderer, canvas is an opt-in for many-mark previews.** SVG is
|
||||
crisp/inspectable/copyable and stays the default for the editor's LivePreview. But an
|
||||
SVG chart renders one DOM node per mark, so a many-mark chart (e.g. the Chart Builder's
|
||||
default one-bar-per-row on a 10k-row dataset) costs **seconds** of main-thread
|
||||
layout/paint per render (measured ~6.5s on 9994 rows; the chart paints _after_ it first
|
||||
appears, freezing the tab). The **Chart Builder preview** therefore passes
|
||||
`renderSpec(…, { renderer: 'canvas' })` — canvas is a single node and paints in
|
||||
milliseconds. The raster trade-off is invisible for an ephemeral preview, and image
|
||||
export (`view.toImageURL`) is renderer-agnostic.
|
||||
- **Canvas has a hard max dimension; SVG doesn't.** A canvas larger than the browser's
|
||||
limit (~32k px/side, less on Safari) fails to allocate and draws _nothing_ — silently.
|
||||
So for canvas, `renderSpec` first runs a headless (`'none'`) layout probe, reads the
|
||||
resolved height, and throws `ChartTooLargeError(heightPx, limitPx)` when it exceeds
|
||||
`MAX_CANVAS_PX ÷ devicePixelRatio`, so the caller can show the real cause. This is a
|
||||
**render-size** limit (the chart is physically too big), distinct from the readability
|
||||
cardinality warnings — don't conflate them. Only an _unbounded_ axis overflows: a
|
||||
`width: 'container'` axis is bounded, so it's the deleted (natural-height) axis to watch.
|
||||
- **Do** call `view.finalize()` on every previous view before rendering a new
|
||||
one, and on component unmount.
|
||||
- **Do** keep exactly one live view per preview node.
|
||||
|
||||
@@ -301,8 +301,10 @@ trivially testable. The caller passes `null` for URL and non-tabular datasets.
|
||||
### 3.3 Column stats: cardinality + numeric extent
|
||||
|
||||
Alongside the display type, each column carries the two data-shape signals the
|
||||
**Chart Builder** needs for its data-aware Tier-B hints (spec §06; see
|
||||
`chart-builder.ts` `builderWarnings`):
|
||||
**Chart Builder** needs for its data-aware Tier-B hints (`chart-builder.ts`
|
||||
`builderWarnings`) **and** for its default pre-population (`smartDefaultEncodings`
|
||||
prefers a low-cardinality category over a high-cardinality key, so the builder never
|
||||
opens on a degenerate chart; spec §06):
|
||||
|
||||
- **`distinct`** — the count of distinct non-empty values **in the sample**,
|
||||
counted only up to `DISTINCT_CAP` (50). Past the cap the exact number stops
|
||||
|
||||
@@ -167,6 +167,18 @@ lives in [04 · Routing & Global Events](04-routing-and-events.md).
|
||||
uses `alert`/`status` roles by severity. Don't invent keyboard models; adopt the
|
||||
documented one.
|
||||
|
||||
**Resolved — a control that removes its own container.** When activating a control deletes
|
||||
the element it lives in (e.g. a Chart Builder guidance hint's one-click **fix** button —
|
||||
the hint re-derives away once applied), focus must not fall to `<body>`. The rule (council:
|
||||
Carbon _Actionable notification_ + APG _Alert_): **announce the change politely and move
|
||||
focus to a stable neighbour.** Concretely, the builder writes "Applied: `<label>`." to a
|
||||
visually-hidden `role="status" aria-live="polite"` node and moves focus to the guidance
|
||||
region if any hints remain, else the surrounding pane (`tabIndex={-1}` anchors, focused only
|
||||
programmatically — no visible ring). Advisory hints themselves don't _grab_ focus (APG: an
|
||||
alert "must not affect keyboard focus"); the fix's remedy lives in a **low-emphasis ghost
|
||||
button** beside the advice (Carbon: inline actionable → ghost button, wraps under the body
|
||||
on narrow widths), an _offer_, never a forced change.
|
||||
|
||||
**Resolved — pane resize handle (window splitter).** A `ResizeHandle` is a focusable
|
||||
`role="separator"` that **reports the controlled pane's size**, per APG → Window Splitter:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user