@pythia-software/query-table-ui 0.5.0 → 0.7.0

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/README.md CHANGED
@@ -1,15 +1,239 @@
1
1
  # @pythia-software/query-table-ui
2
2
 
3
+ `ObjectInspector`, `ObjectProperties`, `PropertyViewControls`, and
4
+ `PropertyViewEditor` provide customizable read-only object details. See
5
+ [object property views](../../docs/object-properties.md) for setup and composition.
6
+
7
+ `ObjectInspector` supplies a compact card with a saved-view toolbar, property/value
8
+ rows, and a read-only footer. The host owns visible identity. Its property editor
9
+ opens a modal workbench with an ordered list, display settings and a live preview.
10
+
11
+ Enable `useQueryTable({ schema, itemDetails: true })` and pass
12
+ `itemDetails={api.itemDetails}` to `DataTable` for click popovers, resizable item modals
13
+ and row expansion. Detail is a regular draggable/resizable column, defaulting to Modal. Page links
14
+ support URL templates and same-tab/new-tab navigation. Item toolbars include Share. Each
15
+ modality has independent saved property views; shared links carry the query,
16
+ typed item ID, modality and layouts. Property label widths are saved with the
17
+ view; values support double click or focused Enter to copy raw data.
18
+ The Detail chip's right caret opens its settings in the column selector.
19
+ Popovers use compact icon controls and scroll properties within the card;
20
+ expanded rows keep Collapse in the card toolbar. Mode labels appear only in
21
+ the properties editor's context strip.
3
22
  Opinionated, themeable React components for query-table, including the data
4
23
  table, query builder, field picker, metrics panel, saved queries, cell menus,
5
24
  and selection toolbar.
6
25
 
26
+ ## Grouped rows
27
+
28
+ `useQueryTable({ schema, clientRows })` enables one row-producing grouping stage
29
+ for complete local datasets. The standard query builder supplies GROUP BY,
30
+ HAVING, aggregate column editing and representative-row ordering. Use
31
+ `api.schema`, `api.visibleFields` and `api.rowId` for the active result shape;
32
+ set `grouping: false` to opt out. Backend transports currently reject grouped
33
+ row queries. See [grouping integration](../../docs/grouping.md) for the query
34
+ shape, source-stage controls, saved state and execution boundaries.
35
+
7
36
  ## Install
8
37
 
9
38
  ```bash
10
39
  npm install @pythia-software/query-table-core @pythia-software/query-table-react @pythia-software/query-table-ui react react-dom
11
40
  ```
12
41
 
42
+ ## Client palettes and column roles
43
+
44
+ Import `theme.css` and override `--qt-*` properties on a wrapper around your
45
+ query table. Each instance can have its own palette; the library does not choose
46
+ one from the OS appearance. `color-scheme` can match the host's native controls,
47
+ but surfaces and role colors come from explicit tokens. Scoped tokens and
48
+ typography also follow pickers and dialogs into mobile portals, including live
49
+ changes to the wrapper's `class`, `style`, or `data-theme`.
50
+
51
+ The general palette uses `--qt-bg`, `--qt-text`, `--qt-muted`, `--qt-header-bg`,
52
+ `--qt-border`, and `--qt-border-subtle`. Actions use `--qt-accent` and
53
+ `--qt-accent-bg`; filled buttons additionally accept `--qt-accent-fg` (default
54
+ white) and `--qt-accent-hover` (default a darker accent).
55
+
56
+ Grouping roles have their own tokens, independent of actions and metric/chart
57
+ category colors:
58
+
59
+ | Prefix | Meaning | Default text color |
60
+ | --- | --- | --- |
61
+ | `--qt-column-key` | GROUP BY key | `#956000` |
62
+ | `--qt-column-aggregate` | Aggregated value, including custom formulas | `#7942a8` |
63
+ | `--qt-column-row` | Value from the group's representative row | `#08786d` |
64
+
65
+ Each prefix supports these suffixes:
66
+
67
+ | Suffix | Controls | Default |
68
+ | --- | --- | --- |
69
+ | `-color` | Role text, label, and badge | Color above |
70
+ | `-bg` | Badge and compact option background | 9% role color over `--qt-bg` |
71
+ | `-border` | Role selection border | 45% role color over `--qt-border` |
72
+ | `-hover-bg` | Hovered role option/control | 15% role color over `--qt-bg` |
73
+ | `-selected-bg` | Selected role option/column | Role `-bg` |
74
+
75
+ For example, a host adapter can map its own palette directly:
76
+
77
+ ```css
78
+ .client-query-table {
79
+ --qt-bg: var(--client-panel);
80
+ --qt-text: var(--client-ink);
81
+ --qt-accent: var(--client-accent);
82
+ --qt-accent-fg: var(--client-panel);
83
+ --qt-column-key-color: var(--client-key-label);
84
+ --qt-column-aggregate-color: var(--client-aggregate-label);
85
+ --qt-column-row-color: var(--client-row-label);
86
+ /* Optional: choose a badge surface rather than deriving a tint. */
87
+ --qt-column-key-bg: var(--client-key-surface);
88
+ }
89
+ ```
90
+
91
+ `ColumnRoleBadge` accepts presentation metadata
92
+ `{ kind: "key" | "aggregate" | "row", badge: string, title: string }` and an
93
+ optional `className`. Use it with `DataTable.headerAdornment` and
94
+ `QueryBuilder.selectPresentation.renderChip`; a `.qt-column-label` wrapper
95
+ with the same `data-column-role` colors the field label too. Pass `undefined`
96
+ for ordinary ungrouped fields to omit the badge. The host determines provenance;
97
+ the component does not infer a role from a field's name or type. Role tokens
98
+ also style the grouped column options and column workbench.
99
+
100
+ The role text remains visible alongside color. Choose readable foregrounds
101
+ against your surfaces; you can use neutral colors without losing the KEY,
102
+ ROW, and aggregate labels. These presentation settings do not enter saved
103
+ queries. Metric category assignments remain in the separate `MetricTheme` API.
104
+ The `/grouping` demo includes adapters for Pharos' cream and forest palettes.
105
+
106
+ ## Column widths
107
+
108
+ Drag a header edge to resize one column (arrow keys nudge, Enter auto-fits).
109
+ Click anywhere in a column header to open its actions menu, including padding
110
+ and blank space. Resize handles remain dedicated to resizing.
111
+ Open **Column Widths** to resize in one step:
112
+
113
+ - **Fit This Column** sizes the clicked column to its widest rendered value.
114
+ - **Fit All to Content** does the same for every column.
115
+ - **Fit All to Screen** fills the viewport; when content overflows, narrow
116
+ columns keep their content width and only the widest ones are trimmed. It
117
+ sets `query.fitToScreen`, so shared links and saved queries fit each viewer's
118
+ available table area after loading and when that area changes.
119
+ - **Equal Widths** splits the viewport evenly.
120
+ - **Reset Widths** drops explicit widths back to schema defaults.
121
+
122
+ Widths live in `query.select`, so presets are shareable and undoable.
123
+ The fit-to-screen preference travels in the existing encoded `?q=` parameter
124
+ (`fs: true` in its compact payload). Manual column resizing, fitting a single
125
+ column, another width preset, or resetting widths clears it. Set
126
+ `fitToScreen: true` on an initial query to enable the same behavior directly.
127
+
128
+ ## Cell quick filters
129
+
130
+ Click a text cell to open paired keep/exclude filters. Length comparisons use
131
+ `length > / ≤`, `length < / ≥`, and `length = / ≠`, prefilled with the clicked
132
+ value's Unicode code-point count.
133
+
134
+ ## Reusable menus
135
+
136
+ `Menu`, `MenuButton`, `MenuGroup`, `MenuItem`, `MenuSubmenu`, and `MenuSeparator`
137
+ serve table header/cell actions, condition choices, and metric actions. They also
138
+ work independently of a table.
139
+ Mount a `Menu` while open, place it beneath your theme provider, and supply a
140
+ trigger element or viewport `{ x, y }` coordinates as its `anchor`:
141
+
142
+ ```tsx
143
+ import { useState } from "react";
144
+ import { Menu, MenuItem, MenuSubmenu, MenuSeparator } from "@pythia-software/query-table-ui";
145
+
146
+ function ViewActions() {
147
+ const [anchor, setAnchor] = useState<HTMLButtonElement | null>(null);
148
+ return <>
149
+ <button aria-haspopup="menu" aria-expanded={!!anchor}
150
+ onClick={event => { const trigger = event.currentTarget; setAnchor(previous => previous ? null : trigger); }}>
151
+ Actions
152
+ </button>
153
+ {anchor && <Menu label="View actions" anchor={anchor} onClose={() => setAnchor(null)}>
154
+ <MenuItem onSelect={() => copyView()}>Copy view</MenuItem>
155
+ <MenuSubmenu label="Export">
156
+ <MenuItem onSelect={() => exportCsv()}>CSV</MenuItem>
157
+ <MenuItem disabled>Image</MenuItem>
158
+ </MenuSubmenu>
159
+ <MenuSeparator />
160
+ <MenuItem tone="danger" onSelect={() => removeView()}>Remove view</MenuItem>
161
+ </Menu>}
162
+ </>;
163
+ }
164
+ ```
165
+
166
+ For a button trigger, `MenuButton` owns open/close state, click toggling, and
167
+ ArrowDown/ArrowUp opening at the first/last enabled action:
168
+
169
+ ```tsx
170
+ <MenuButton label="Metric actions" trigger="⋯" width={200}
171
+ buttonProps={{ className: "my-trigger" }}>
172
+ <MenuItem onSelect={duplicate}>Duplicate</MenuItem>
173
+ <MenuItem disabled={!canMove} onSelect={move}>Move up</MenuItem>
174
+ <MenuSeparator />
175
+ <MenuItem tone="danger" onSelect={remove}>Remove</MenuItem>
176
+ </MenuButton>
177
+ ```
178
+
179
+ `MenuGroup` adds a labeled group. Set `columns={2}` for paired actions: horizontal
180
+ arrows move within a row, vertical arrows follow a column, disabled cells are
181
+ skipped, and vertical boundaries continue to adjacent actions. Direct action
182
+ children should follow visual row order. Set `checked` with
183
+ `selectionType="radio"` for exclusive choices; the consumer owns selected state.
184
+
185
+ Items accept `icon`, `shortcut`, `disabled`, and `textValue` for rich-content
186
+ typeahead labels. Set `checked` for checkbox semantics and `closeOnSelect={false}`
187
+ for toggles that should keep the menu open. Submenus can nest and accept `width`.
188
+ Actions close the whole tree; Escape and the back arrow close one level. Up/Down,
189
+ Home/End, typeahead, and direction-aware submenu arrows handle keyboard navigation.
190
+ Desktop Tab closes the menu and continues from its trigger. `returnFocus` supplies
191
+ a destination for coordinate menus; outside interactions keep their own focus.
192
+
193
+ Desktop menus follow element anchors, flip/shift at viewport edges, scroll when
194
+ long, and portal outside clipping containers. Within a dialog, they remain inside
195
+ its focus trap. Coordinate menus dismiss on external scrolling. Mouse travel across
196
+ submenu gaps uses a safe corridor; only one child submenu opens at each level.
197
+ Phones use the shared focus-trapping sheet with expandable inline submenus.
198
+
199
+ Scoped `--qt-*` variables, font family, color scheme, and text direction propagate
200
+ through portals, including theme changes while open. Optional tokens are
201
+ `--qt-menu-bg`, `--qt-menu-border`, `--qt-menu-radius`, `--qt-menu-shadow`, and
202
+ `--qt-menu-hover`; defaults use the existing palette. `classNames` supports
203
+ `popover`, `submenu`, `item`, and `separator`; existing `qt-cm-*` CSS hooks remain.
204
+ Import `theme.css` for the standard visual and responsive behavior.
205
+
206
+ Visit the demo's `/menus` playground for nested, themed, RTL, clipped, long, and
207
+ dialog examples. With the demo running, `npm run test:menus` checks those behaviors
208
+ in Chromium and WebKit (`QT_DEMO_URL` can point to a different server).
209
+
210
+ ### Forms and searchable pickers
211
+
212
+ Use `Popover` for inputs and settings. It shares menu positioning, theme,
213
+ dismissal, portals, and mobile sheets, with regular form Tab behavior and dialog
214
+ semantics. Desktop focus starts at the first enabled control; `initialFocus` can
215
+ supply a selector scoped to the surface. Escape restores the trigger, leaving
216
+ focus dismisses the popover, and outside clicks retain the clicked destination.
217
+ It accepts `anchor`, `label`, `onClose`, `returnFocus`, `width`, `placement`,
218
+ `className`, and arbitrary children. Mount it only while open:
219
+
220
+ ```tsx
221
+ {open && trigger.current && <Popover label="View settings" anchor={trigger.current}
222
+ placement="bottom-end" width={280} onClose={() => setOpen(false)}>
223
+ <label>Name <input value={name} onChange={event => setName(event.target.value)} /></label>
224
+ <button onClick={() => { save(name); setOpen(false); }}>Save</button>
225
+ </Popover>}
226
+ ```
227
+
228
+ Add column/filter/sort field pickers, rich filter-value pickers, and auto-refresh
229
+ settings use this surface. `FieldPicker` accepts an optional `anchor` (or
230
+ `PickerAnchor` wrapper) for floating desktop use; otherwise it stays inline on
231
+ desktop. With `onClose`, it becomes a dismissible sheet on mobile. Omit `onClose`
232
+ to keep a standalone picker inline on all screen sizes. Tag multi-select editors and
233
+ full workbenches use the shared modal/sheet foundation with Apply/Cancel and focus
234
+ containment. Native selects, expandable sections, and formula autocomplete keep
235
+ their respective form, disclosure, and combobox interactions.
236
+
13
237
  ## Tag/set filters
14
238
 
15
239
  Set `filter.editor: "set"` on a `textarray` field to use the native WHERE
@@ -68,6 +292,14 @@ export function OrdersTable({ table, schema }) {
68
292
  }
69
293
  ```
70
294
 
295
+ `QueryBuilder` has an inline **Query** label and chevron in its toolbar to fold
296
+ the editor. Set `defaultCollapsed` for its initial state, or use `collapsed` and
297
+ `onCollapsedChange` to control it from a parent. Folding keeps the current query,
298
+ its summary, saved-query controls, history, and run actions available; pass
299
+ `summary` to replace the default folded summary. The demo's Metrics and Table
300
+ sections fold independently through their inline labels, with metric and row
301
+ counts visible in their headers and 44px touch targets on small screens.
302
+
71
303
  `DataTable` virtualizes body rows and uses a 600px maximum-height scroll
72
304
  viewport by default. Set `maxHeight` to match the surrounding layout;
73
305
  `estimateRowHeight` (37px by default) and `overscan` (8 rows) are available for
@@ -78,11 +310,34 @@ The bundled `link` renderer allows only HTTP(S), email, telephone, relative,
78
310
  and fragment URLs. Applications remain responsible for validating URLs emitted
79
311
  by custom renderers.
80
312
 
313
+ Pass `savedQueryControls` to `QueryBuilder` to replace its built-in saved-query
314
+ name/star, Update action, and Saved menu with host-owned React controls; omitting
315
+ the prop keeps the default interface. Use `api.saved` for persistence and loading,
316
+ and handle selection, save feedback, and unsaved-edit protection in your controls.
317
+ Share and query history remain available. The demo's `/saved-queries` route shows
318
+ an integrated prototype of this slot.
319
+
81
320
  The query builder's **Share** button copies a URL containing the complete
82
321
  current query, including a saved default query. Configure `useQueryTable` with
83
322
  `syncUrl: true` so recipients load shared query URLs. Query tokens are encoded,
84
323
  not encrypted, and may contain raw filter values.
85
324
 
325
+ Pass `selection={table.selection}` to enable row checkboxes with editable ranges:
326
+
327
+ - Click a checkbox to toggle it and start a new range, preserving prior checks.
328
+ - Shift-click to grow or shrink the range from its fixed starting row. Starting
329
+ by unchecking a row makes Shift-click uncheck the range instead.
330
+ - Ctrl/Cmd-click toggles one checkbox; Ctrl/Cmd+Shift-click adds the span while
331
+ preserving every existing check.
332
+ - Space toggles the focused checkbox; Shift+Space adjusts the range, with
333
+ Ctrl/Cmd+Shift+Space available for an additive span.
334
+
335
+ For example, click 2, Shift-click 4, click 8, Shift-click 12, then Shift-click 10
336
+ to select rows 2–4 and 8–10. Shrinking an overlapping range preserves checks that
337
+ existed before it began. Ranges follow the current page's displayed order,
338
+ including virtualized rows, and reset when that order changes. The header
339
+ checkbox selects or clears the current page and starts a fresh range context.
340
+
86
341
  `SelectionToolbar` action callbacks receive `(selectedIds, selection)`, so
87
342
  consumer-owned bulk actions can call `selection.replace(ids)` or
88
343
  `selection.retain(ids)` atomically. Existing callbacks that use only the first
@@ -90,6 +345,9 @@ argument remain compatible.
90
345
 
91
346
  ## Icons
92
347
 
348
+ The bundled theme aligns table-header sort arrows and priority indicators with
349
+ their labels for both single-column and multi-column sorts.
350
+
93
351
  `Icon` is the shared, hand-drawn SVG set used throughout the UI and demo. Its
94
352
  24px grid, rounded 1.8px strokes, and `currentColor` styling keep controls
95
353
  cohesive without forcing every shape into the same silhouette. Drag handles
@@ -116,8 +374,9 @@ touch-based landscape viewports use a mobile interaction model:
116
374
  - **Tap to edit or delete:** filters, sorts, and metrics become readable summaries
117
375
  with pencil buttons. Remove actions live inside the editor, including removal
118
376
  of an entire OR group, and support Undo. There are no inline trash buttons.
119
- Adding a filter, sort, or metric immediately
120
- opens its editor. Filters can be combined with OR without relying on drag-and-drop.
377
+ Adding a filter or metric opens its editor. Add sort lets you choose a field,
378
+ then ascending or descending before committing; Back returns to the same field search.
379
+ Filters can be combined with OR without relying on drag-and-drop.
121
380
  - **Direct ordering:** columns, sorts, and metrics have leading drag handles
122
381
  directly in the query builder, with no Order buttons. Column chips stay
123
382
  wrapped and can move horizontally or between rows. Keyboard arrow keys reorder
@@ -138,20 +397,20 @@ touch-based landscape viewports use a mobile interaction model:
138
397
  padding and redundant headings. Query chips, their buttons, and inline handles
139
398
  use a 32px minimum height with zero vertical padding; long summaries can wrap.
140
399
  Filter, sort, and metric inputs use 36px controls. Keyword sections have extra breathing room
141
- and solid horizontal rules on desktop and mobile.
400
+ without divider lines between clauses on desktop and mobile.
142
401
  - **Dense query setup:** filter, sort, and metric actions share a section header.
143
402
  Summaries use one line when space allows and wrap without truncating values.
144
403
  Labeled condition/value and function/measure pairs replace wrapping desktop
145
404
  chips. Sort direction and null-value placement use native selects; regex
146
- extraction stays optional. Its configuration opens a dialog (a bottom sheet on small screens) with a live, worker-isolated preview of up to 100 matching rows. The preview shows original values, extracted sort values, and no-match/null states; Apply commits any syntactically valid pattern even when the preview is unavailable or still loading, while Cancel leaves the sort unchanged. Grouping uses removable tags and a field picker.
405
+ extraction stays optional. It is available from Add sort's field options or its detailed-sort caret, with direction configurable in the dialog. Plain desktop sort chips omit the extraction action; configured extractions remain editable. Its configuration opens a dialog (a bottom sheet on small screens) with a live, worker-isolated preview of up to 100 matching rows. The preview shows original values, extracted sort values, and no-match/null states; Apply commits any syntactically valid pattern even when the preview is unavailable or still loading, while Cancel leaves the sort unchanged. Grouping uses removable tags and a field picker.
147
406
  Simple metric results share two-column rows; grouped results use the full width.
148
407
  - **Touch-first controls:** dedicated drag handles, 16px text inputs,
149
408
  sticky bulk actions, a horizontal table scroll cue, and the device's native
150
409
  share sheet when available (clipboard fallback otherwise).
151
410
 
152
- Edits continue to update the query immediately, as on desktop; **Done** dismisses
153
- the sheet. The column workbench retains its explicit **Apply columns** draft
154
- workflow. Desktop chips, popovers, and drag-and-drop remain available.
411
+ Filter and sort edits update the query immediately; **Done** dismisses the sheet. The column and metric workbenches retain explicit **Apply** draft workflows. Desktop chips, popovers, and drag-and-drop remain available.
412
+
413
+ In compact mode, **Add offset** reveals and focuses the inline offset input without changing the query. Enter or blur commits the value; the clear action returns to **Add offset**.
155
414
 
156
415
  Keep the standard viewport meta tag in the host page:
157
416
 
@@ -173,7 +432,37 @@ for customization and backend integration details.
173
432
 
174
433
  ## SELECT editor
175
434
 
176
- `QueryBuilder` includes a split **Add** button and **Reset** action for Columns: Add opens the field picker, while its caret opens the column customizer. Actions follow the selected chips on desktop and sit in the section header on small screens. The exported `SelectColumnEditor` also accepts `{ api, onClose }` for standalone use. It supports draft column ordering, a searchable catalogue, sampled value frequencies, and a lazy-loaded CodeMirror formula editor with grouped live previews and regex inspection. Shared-definition saves and query-layout application are separate actions.
435
+ `QueryBuilder` includes a split **Add** button and **Reset** action for Columns: Add opens the field picker, while its caret opens the column customizer. Actions follow the selected chips on desktop and sit in the section header on small screens. The exported `SelectColumnEditor` also accepts `{ api, onClose }` for standalone use. It supports draft column ordering, a searchable catalogue, sampled value frequencies, and a lazy-loaded, dependency-free textarea formula editor with field/function suggestions, signature help, and compiler diagnostics with grouped live previews and regex inspection. A Function library beside the editor supports search by name or intent, browsing by concept and return type, and insertion at the cursor or around selected text; it stacks below the editor on narrow screens. Shared-definition saves and query-layout application are separate actions.
436
+
437
+ ### Shared editor components
438
+
439
+ `EditorModal`, `useEditorWorkbench`, `EditorPanelToggle`, and
440
+ `EditorPanelSeparator` provide the shared shell and panel geometry used by the
441
+ metric editor and the grouped column editor. The hook accepts a
442
+ fixed list of `{ width, minWidth, defaultVisible? }` panel sizes in pixels. Attach `root` to the
443
+ workspace, build its grid from `visibleIndices` and `widths` with 6px separators,
444
+ and hide panels with `visiblePanes`. The modal wires the header and Reset layout;
445
+ its `panelControls` slot places panel toggles beside Reset layout. Pass the same
446
+ workbench to panel toggles and separators. Reset restores dialog/pane sizes and
447
+ each panel's current `defaultVisible` setting (true when omitted). Defaults can
448
+ follow the selected column; changing them alone does not override manual toggles.
449
+ Separators support
450
+ pointer drag, arrow keys (Shift for larger steps), Home/double-click reset, and
451
+ Escape to cancel a drag. Toggles keep at least one panel open. Geometry stays
452
+ separate from query drafts; the consumer owns draft validation, Apply/Cancel,
453
+ panel contents, and responsive section navigation.
454
+
455
+ `FormulaEditor` is also exported for reuse. Supply `value`, `onChange`, `fields`,
456
+ and `compile`; an optional `functions` catalogue drives completions, signature
457
+ help, and the Function library. Row formulas use `FORMULA_FUNCTIONS` by default.
458
+ Aggregate editors can combine core's `METRIC_FUNCTIONS` and `FORMULA_FUNCTIONS`
459
+ with `compileMetric`, including `MEDIAN`. A catalogue describes available
460
+ functions; the supplied compiler determines which expressions are valid.
461
+ For a separate function panel, set `showLibrary={false}` and connect the exported
462
+ `FormulaFunctionBrowser` with `variant="panel"` to the editor's
463
+ `actionsRef.current.insertFunction(name)`. This preserves cursor/selection and
464
+ native text undo. Omitting the browser's `onInsert` makes it a reference-only
465
+ library when no formula is selected.
177
466
 
178
467
  ### Filter value presentation
179
468
 
@@ -196,3 +485,88 @@ The searchable static picker matches both labels and keys. Selection emits the
196
485
  canonical `value` only; presentation is never serialized into queries. A resolver
197
486
  can present a current value even when it is absent from the options. Strings without
198
487
  a presentation provider retain the existing select control.
488
+
489
+ ## Metric workbench and dashboards
490
+
491
+ QueryBuilder offers **Add metric** while the query has no metrics. Once metrics exist, editing moves to the always-visible **Edit** button in `MetricsPanel`, which opens the resizable four-pane `MetricsEditor`. Apply commits drafts together; Cancel discards them. Dashboard layout provides draggable canvas widths, whole-rem card sizing through corner handles or inputs, canvas presets, pointer/keyboard reordering, and inline names. Metrics use their own workbench on desktop and compact viewports; their changes commit on Apply.
492
+
493
+ Use the icon buttons beside **Panels** in the modal header to show or hide the metric list, definition, references, and preview. At least one panel stays visible; the metric picker and Previous/Next controls remain available when the list is hidden. **See in dashboard** reveals the selected card in the same draft; **Back to metric**, the card’s pencil action, or a double-click returns to editing. Open settings and panel preferences survive these transitions.
494
+
495
+ Drag the dashboard canvas’s right edge to test responsive wrapping. Card corner handles adjust preferred width and height in whole rems; arrow keys resize and Shift increases the step. Escape or a cancelled pointer gesture restores the dimensions at drag start. Canvas and panel geometry are temporary preferences; card geometry commits on Apply. Removal offers **Undo remove**, and **Review issues** opens the metric needing attention while valid metrics continue to preview.
496
+
497
+ Use **Dialog size** for keyboard-editable pixel width/height controls; the native resize corner remains available. **Reset layout** restores the modal and all pane widths without changing metric drafts. Card minimum widths apply equally in the layout canvas and saved dashboard; narrower containers scroll horizontally.
498
+
499
+ **Value** requires no grouping. Incompatible drafts show an actionable diagnostic and block Apply; standalone `MetricCard` also rejects grouped or multiple scalar buckets. Lists suppress magnitude bars for signed results. Charts visibly disclose missing/error points and drawing limits while keeping ordinary captions compact. Malformed remote box/histogram summaries produce a card-local diagnostic instead of throwing during rendering.
500
+
501
+ `MetricsPanel` renders values, lists, pivots/flat tables, bars, lines, pie/ring, scatter, box, and histogram cards through native SVG and accessible data tables/tooltips. Pass a `MetricTheme` as `metricTheme` to QueryBuilder and `theme` to MetricsPanel for consistent typed-category palette assignments. Labels, temporal units/patterns, legends, list styling, axis labels, and card dimensions are query-local. `MetricCard` and `MetricsEditor` are also exported for standalone integration.
502
+
503
+ See [the production metrics guide](../../docs/metrics.md). Run `npm run test:metrics` against the demo for authoring/layout/persistence and chart interactions.
504
+
505
+ ## Cell displays
506
+
507
+ Set `FieldDef.display` for built-in JSON, date/time, duration, number/percent,
508
+ text, and chip presentation. JSON cells open a searchable tree with nested paths
509
+ and original/decoded copying; objects, JSON strings and escaped strings share
510
+ the same inspector. Custom `render` functions and registered keys take precedence.
511
+ Detail properties inherit these displays and embed initially collapsed JSON
512
+ trees in popovers, modals, and expanded rows. A button beside the property label
513
+ opens the full viewer with filter/highlight search, match navigation, and copy
514
+ controls. JSON property options can be edited, saved, and shared through the
515
+ existing property-view controls.
516
+ See the [cell display guide](../../docs/cell-displays.md) for typed configuration,
517
+ units, examples, and inspection limits. `JsonCell` and the embedded `JsonValueView`
518
+ are also exported for standalone use.
519
+
520
+ ## Compact metric covers
521
+
522
+ `<MetricCard compact clause={clause} result={result} fields={fields} />` uses
523
+ sparse round numeric ticks and short unit labels for a small chart. Calendar
524
+ charts keep the computation timezone and use sparse local calendar boundaries;
525
+ bar timelines render chronologically. This is a rendering option, so saved query
526
+ formats and full data values are unchanged. Explicit axis bounds are respected.
527
+
528
+ Compact Cartesian charts put both axis titles below the plot on one line,
529
+ with ↑ for the vertical axis and → for the horizontal axis. The compact plot
530
+ reserves only the tick gutter, allowing more width for data. Full charts retain
531
+ their usual title placement. Metric loading uses an accessible spinner and
532
+ respects reduced-motion preferences.
533
+
534
+ Compact box plots label each row above its glyph and show its median. Each row
535
+ uses an independent linear scale focused on its whiskers, omitting outlier
536
+ markers. A visible guide explains that the box spans the middle 50%, the line
537
+ marks the median, and scales differ between rows. Full statistics and outlier
538
+ counts remain in accessible mark descriptions and the data table. Allow at least
539
+ 52px per plotted row plus the guide; constrain the supplied bucket count when
540
+ using a fixed-height cover. The default full chart retains its shared scale.
541
+
542
+ ### Underline list bars
543
+
544
+ Use `display: { kind: "list", list: { barPosition: "below" } }` to put a thin
545
+ bar under each label and value. Labels wrap across the available row instead of
546
+ being ellipsized in a narrow column. The default `"inline"` layout keeps the bar
547
+ beside the label. The metric editor exposes both under **Bar placement**. This
548
+ setting survives query links and saved views; it does not change computation,
549
+ sort order, values, or the `showBars` / `showValues` switches.
550
+
551
+ Set `display.list.maxLabelLines` to a positive integer to cap the wrapped label
552
+ height when `barPosition` is `"below"`, for example
553
+ `list: { barPosition: "below", maxLabelLines: 2 }`. Omit it for unlimited lines;
554
+ the editor's **Maximum label lines** input accepts a blank value for unlimited.
555
+ The limit has no effect on inline bars. Truncated labels keep their full text in
556
+ the tooltip, and the limit survives query links and saved views.
557
+
558
+ ### Visible groups and remainder
559
+
560
+ `display.maxGroups` caps displayed groups in result order without changing the
561
+ metric computation or its server `groupLimit`. It is a positive integer, and
562
+ the editor exposes it as **Maximum displayed groups**. Omission retains the
563
+ automatic layout limits (for example eight pie slices and six histogram series).
564
+
565
+ `display.showOther` controls the synthetic remainder in pie/donut charts
566
+ (default true) and lists (default false). Lists combine only exact additive
567
+ counts or sums, with complete results or an exact server remainder. Averages,
568
+ rates, missing values, and partial results never produce an invented remainder.
569
+ Hiding a pie remainder retains the full denominator and leaves its angular
570
+ share empty; it does not rescale the selected groups to 100%. A real group named
571
+ Other remains visible. Both settings survive query links and saved views.
572
+ Use `display.legendPosition: "none"` to hide the chart key.
@@ -0,0 +1,7 @@
1
+ import {
2
+ FormulaEditor
3
+ } from "./chunk-TC5KDYUF.js";
4
+ export {
5
+ FormulaEditor as default
6
+ };
7
+ //# sourceMappingURL=FormulaEditor-NZWSPFJQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}