mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Implement fit-mode rendering contract with container sizing and pane re-fit
This commit is contained in:
@@ -379,9 +379,9 @@ _Live Preview_ spec. The only invariant this doc cares about:
|
||||
> embeds that returned spec. **The user's stored spec is never mutated by
|
||||
> rendering.**
|
||||
|
||||
The container-relative fit modes (Width/Height/Full) depend on `renderer: 'svg'`
|
||||
plus `"container"` sizing to follow the pane; when the pane resizes, re-running
|
||||
`prepareSpecForRender` + re-embedding (a `flush()`) re-fits the chart.
|
||||
The container-relative fit modes (Width/Height/Full) depend on `"container"`
|
||||
sizing to follow the pane. Re-fitting on a **pane resize** is _not_ a re-embed:
|
||||
the existing view is re-measured via a `ResizeObserver`-driven event — see §8.
|
||||
|
||||
### Rules
|
||||
|
||||
@@ -463,14 +463,64 @@ manual retry, no reload.
|
||||
|
||||
---
|
||||
|
||||
## 8. Container Sizing & Pane Resize (two gotchas that cost real time)
|
||||
|
||||
Vega-Lite's `"container"` sizing is responsible for the Width/Height/Full fit
|
||||
modes, and it has **two non-obvious failure modes**. Both were rediscovered the
|
||||
hard way; this section is the shortcut.
|
||||
|
||||
### Gotcha 1 — the embed host shrink-wraps, collapsing `width:"container"`
|
||||
|
||||
`vega-embed` brands the element you embed into with its own
|
||||
`.vega-embed { display: inline-block }`, injected into `<head>` at runtime so it
|
||||
**wins the cascade** over a class you put on that same element. `inline-block`
|
||||
shrink-wraps horizontally, and `"container"` width reads `host.clientWidth` — so
|
||||
the chart collapses to near-zero width. (Height often survives because a tall box
|
||||
keeps `clientHeight`, which is why the symptom is "Width broken, Height fine".)
|
||||
Note also: `vega-embed` only adds its responsive `chart-wrapper` (the element its
|
||||
`width:100%` rule targets) **when `actions` are enabled** — we pass
|
||||
`actions: false`, so that path is dead and the host is branded directly.
|
||||
|
||||
**Fix:** embed into a dedicated **inner host** with a _static_ className (React
|
||||
never re-reconciles it, so Vega's runtime classes survive) nested inside a
|
||||
**React-owned frame** that carries the fit-mode class. Size the host with
|
||||
**two-class selectors** (`.fitWidth .host { width: 100% }`) that out-specify
|
||||
`.vega-embed`. Original mode lets the host stay natural and the pane scrolls.
|
||||
|
||||
### Gotcha 2 — Vega re-measures only on `window:resize`
|
||||
|
||||
The compiled `width`/`height` signals re-evaluate `containerSize()` **only** on
|
||||
`events: "window:resize"`. Consequences: `view.resize()` re-runs layout with the
|
||||
**stale** size (it does _not_ re-measure), and a pane drag fires no window resize,
|
||||
so a responsive chart does **not** follow the pane on its own.
|
||||
|
||||
**Fix:** a `ResizeObserver` on the host → `window.dispatchEvent(new Event('resize'))`
|
||||
(behind `RenderHandle.resize()`, keeping the Vega knowledge in the renderer).
|
||||
`ResizeObserver` callbacks are frame-batched, so this tracks a drag without a
|
||||
debounce. Because only the container-bound dimension carries the resize handler,
|
||||
Width re-fits width and leaves height natural automatically — no fit-mode
|
||||
bookkeeping. Gate the observer to responsive modes (Original needs no re-fit).
|
||||
|
||||
### Rules
|
||||
|
||||
- **Do** give `vega-embed` its own inner host element; never put a React-managed,
|
||||
changing `className` on the element `vega-embed` brands.
|
||||
- **Do** out-specify `.vega-embed` (two-class selectors) when you must size the host.
|
||||
- **Do** bridge pane-resize via a synthetic `window:resize`, not `view.resize()`.
|
||||
- **Don't** assume `actions: false` leaves you the responsive `chart-wrapper` — it doesn't.
|
||||
- **Don't** re-embed just to re-fit a resize; re-measure the existing view.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Concern | Mechanism | Source of truth |
|
||||
| ------------- | ----------------------------------------------------------------------- | ----------------------------------------------- |
|
||||
| Embedding | One `renderSpec` over `vega-embed`, `actions: false`, `renderer: 'svg'` | `src/app/services/chart-renderer.ts` |
|
||||
| View teardown | `view.finalize()` before each re-render and on unmount | the renderer's `RenderHandle` |
|
||||
| Theming | Vega `Config` per UI theme, applied at embed time | `chartConfigFor()` in `src/core/vega-themes.ts` |
|
||||
| Field names | `escapeVegaField` on every data-derived `field:` | `src/core/rendering.ts` |
|
||||
| Debounce | `createDebouncedRenderer`, delay from `renderDebounce` setting | `src/app/services/debounced-renderer.ts` |
|
||||
| Spec prep | `prepareSpecForRender` (pure, on a copy) | `src/core/rendering.ts` (see _Live Preview_) |
|
||||
| Errors | One error field, cleared on success, empty = nothing | `PreviewStore.error` |
|
||||
| Concern | Mechanism | Source of truth |
|
||||
| ------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------- |
|
||||
| Embedding | One `renderSpec` over `vega-embed`, `actions: false`, `renderer: 'svg'` | `src/app/services/chart-renderer.ts` |
|
||||
| View teardown | `view.finalize()` before each re-render and on unmount | the renderer's `RenderHandle` |
|
||||
| Theming | Vega `Config` per UI theme, applied at embed time | `chartConfigFor()` in `src/core/vega-themes.ts` |
|
||||
| Field names | `escapeVegaField` on every data-derived `field:` | `src/core/rendering.ts` |
|
||||
| Debounce | `createDebouncedRenderer`, delay from `renderDebounce` setting | `src/app/services/debounced-renderer.ts` |
|
||||
| Spec prep | `prepareSpecForRender` (pure, on a copy) | `src/core/rendering.ts` (see _Live Preview_) |
|
||||
| Errors | One error field, cleared on success, empty = nothing | `PreviewStore.error` |
|
||||
| Container fit | Inner host + frame (out-specify `.vega-embed`); resize via synthetic `window:resize` | §8 (`LivePreview` + `chart-renderer`) |
|
||||
|
||||
Reference in New Issue
Block a user