flexdesk 0.17.14 → 0.17.16
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/CHANGELOG.md +39 -0
- package/css/base.css +237 -8
- package/css/flexdesk.css +344 -17
- package/css/overrides.css +6 -0
- package/dist/charts.js +104 -34
- package/dist/charts.js.map +3 -3
- package/dist/{chunk-2PM6ZMNS.js → chunk-2Z2ENHR3.js} +2 -2
- package/dist/{chunk-IG74S3ZH.js → chunk-7JI7HHSY.js} +2 -2
- package/dist/{chunk-TLIV6QSU.js → chunk-7OL6F2AM.js} +3 -3
- package/dist/{chunk-MADMGRX2.js → chunk-PGZ4EHSE.js} +156 -9
- package/dist/chunk-PGZ4EHSE.js.map +7 -0
- package/dist/{chunk-ZMH3OC7H.js → chunk-XJNADRGR.js} +4 -2
- package/dist/chunk-XJNADRGR.js.map +7 -0
- package/dist/flexdesk.css +344 -17
- package/dist/flow.js +3 -3
- package/dist/tiles.js +84 -9
- package/dist/tiles.js.map +2 -2
- package/dist/widgets.js +448 -5
- package/dist/widgets.js.map +4 -4
- package/dist/wm.js +110 -19
- package/dist/wm.js.map +2 -2
- package/docs/README.md +1 -1
- package/docs/components/charts.md +70 -3
- package/docs/components/forms-and-dialogs.md +80 -2
- package/docs/components/menus.md +85 -1
- package/docs/components/shell.md +52 -0
- package/docs/components/slide-out-panel.md +15 -0
- package/docs/components/tiles.md +37 -0
- package/package.json +1 -1
- package/src/charts/plot_config_editor.js +125 -29
- package/src/tiles/tile_base.js +48 -2
- package/src/tiles/tile_grid.js +42 -1
- package/src/tiling/kind_taxonomy.js +45 -0
- package/src/tiling/tile_breadcrumb.js +12 -4
- package/src/tiling/wm.js +86 -11
- package/src/ui/components/category_picker.js +556 -0
- package/src/ui/components/computing_status_window.js +3 -0
- package/src/ui/components/confirm_dialog.js +3 -0
- package/src/ui/components/managed_window.js +204 -9
- package/src/ui/components/modal.js +11 -2
- package/src/ui/components/wizard.js +4 -1
- package/widgets.js +2 -1
- package/dist/chunk-MADMGRX2.js.map +0 -7
- package/dist/chunk-ZMH3OC7H.js.map +0 -7
- /package/dist/{chunk-2PM6ZMNS.js.map → chunk-2Z2ENHR3.js.map} +0 -0
- /package/dist/{chunk-IG74S3ZH.js.map → chunk-7JI7HHSY.js.map} +0 -0
- /package/dist/{chunk-TLIV6QSU.js.map → chunk-7OL6F2AM.js.map} +0 -0
package/docs/README.md
CHANGED
|
@@ -49,7 +49,7 @@ FlexDesk is nine entry points that share their code, so you load only what you u
|
|
|
49
49
|
- [Settings page](components/settings-view.md): a settings store's schema drawn as a page.
|
|
50
50
|
- [Wizard](components/wizard.md): a multi-step flow, inline or in a window.
|
|
51
51
|
- [Input fields: slider and tags](components/input-fields.md): `SliderField` and `createTagInput`.
|
|
52
|
-
- [Menus](components/menus.md): `createMenuBar` and `
|
|
52
|
+
- [Menus](components/menus.md): `createMenuBar`, `ActionDropdown` and `CategoryPicker`.
|
|
53
53
|
|
|
54
54
|
### Layout, content and feedback
|
|
55
55
|
|
|
@@ -4,6 +4,70 @@
|
|
|
4
4
|
|
|
5
5
|
Plotly-backed charts: the wrapper, the chart types, subplot layouts, downsampling, the plot grid, the plot-config editor and the two plot windows. Plotly itself is not in the bundle; call `setPlotlySource()` with the path to your copy. The plot window's `streamEvents` option is described with the [other application-neutral options](../concepts/domain-neutrality.md#no-application-globals-the-gallery-picker-and-plot-window-events-0170).
|
|
6
6
|
|
|
7
|
+
## A plot config series is one block, and an embedder's fields share it (0.17.16)
|
|
8
|
+
|
|
9
|
+
[`renderPlotConfigEditor`](#editing-a-plot-config-renderplotconfigeditor-0170)
|
|
10
|
+
drew each series as one wrapped line: the series picker, the legend label, the
|
|
11
|
+
colour swatch with a wide hex box, the width, the dash and the stack group,
|
|
12
|
+
then the embedder's `extraSeriesFields` and the overlay select on label
|
|
13
|
+
columns of their own. Each series is now one block:
|
|
14
|
+
|
|
15
|
+
- **A header line**: the series picker, the legend label and the remove
|
|
16
|
+
button. It wraps when the window is narrow.
|
|
17
|
+
- **Its fields, one per line, on the editor's label column** (the same 96px
|
|
18
|
+
column as the subplot's and the axes' fields): **Colour** (a `ColorField`:
|
|
19
|
+
the swatch and a hex box sized to `#rrggbb`), **Width**, **Dash**,
|
|
20
|
+
**Stack group**, then the embedder's fields, then **Overlay** (with
|
|
21
|
+
`overlaySources`). An overlay's own label, colour, dash, width and mode sit
|
|
22
|
+
under its source, set in by a rule, with their controls still on the
|
|
23
|
+
series' control column.
|
|
24
|
+
|
|
25
|
+
It reads at the usual ~600px config window and down to 360px without
|
|
26
|
+
overflowing. The classes are `.twm-plot-config__series-head` and
|
|
27
|
+
`.twm-plot-config__series-fields`; `.twm-plot-config__series-main` is gone.
|
|
28
|
+
|
|
29
|
+
`extraSeriesFields(el, series, ctx)` can now put its fields on that same
|
|
30
|
+
column, in either of two ways, and the 0.17.0 form keeps working:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
renderPlotConfigEditor(el, plotConfig, {
|
|
34
|
+
// 1. return field specs: the editor lays them out
|
|
35
|
+
extraSeriesFields: (el, series, { onChange }) => [
|
|
36
|
+
{ label: 'Scenario', control: scenarioSelect(series, onChange),
|
|
37
|
+
hint: 'The run this series is read from' },
|
|
38
|
+
{ label: 'Note', render: (slot) => mountMyWidget(slot, series, onChange) }, // may return { dispose }
|
|
39
|
+
],
|
|
40
|
+
// 2. or draw rows with the editor's own helper
|
|
41
|
+
// extraSeriesFields: (el, series, { onChange, field }) => {
|
|
42
|
+
// el.appendChild(field('Scenario', scenarioSelect(series, onChange), { hint: '…' }));
|
|
43
|
+
// },
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- `ctx.field(label, control, { hint })` returns a field line (a `<label>`
|
|
48
|
+
when the control is a single input, select or textarea). A hint is drawn
|
|
49
|
+
under the control, outside the label: the line is then a `<div>`, its label
|
|
50
|
+
text is the `<label for>` of a single control, and the hint is the control's
|
|
51
|
+
`aria-describedby`, so it describes the control without joining its name.
|
|
52
|
+
- Only an ARRAY return is field specs. A spec is
|
|
53
|
+
`{ label: string, control: HTMLElement, hint? }` or
|
|
54
|
+
`{ label: string, render(slot), hint? }`; `render` draws into an empty slot
|
|
55
|
+
on the field line and may return `{ dispose }`. Specs are laid out after
|
|
56
|
+
anything drawn into `el`. A spec without a string `label`, with a `control`
|
|
57
|
+
that is not an Element, with neither `control` nor `render`, or whose
|
|
58
|
+
`render` throws is skipped with a warning (`logger.warn`, else
|
|
59
|
+
`console.warn`); the rest of the row is drawn. Any other return is a handle
|
|
60
|
+
as in 0.17.0 (`{ dispose }`), even one that has a `fields` property.
|
|
61
|
+
- `el` sits in the field column (`display: contents`), so rows appended to it
|
|
62
|
+
line up with the editor's; content drawn there any other way shows as
|
|
63
|
+
before. A returned `{ dispose }` is still disposed on every redraw.
|
|
64
|
+
|
|
65
|
+
The config the editor reads and writes is unchanged: no key was added, renamed
|
|
66
|
+
or defaulted differently, opening a series writes nothing, and each control
|
|
67
|
+
writes its own key only.
|
|
68
|
+
|
|
69
|
+
`tests/plot_config_editor.test.mjs` §12 pins it.
|
|
70
|
+
|
|
7
71
|
## An empty cell can name what is missing: `emptyMessage` as a function (0.17.12)
|
|
8
72
|
|
|
9
73
|
[`renderPlotGrid`](#a-plot-config-in-any-container-renderplotgrid-0170),
|
|
@@ -303,7 +367,7 @@ const editor = renderPlotConfigEditor(el, plotConfig, {
|
|
|
303
367
|
seriesProvider: ({ value, role, subplot }) => names(value), // items, or a promise of them
|
|
304
368
|
seriesTransforms, // their toggles, per subplot
|
|
305
369
|
overlaySources: () => mySources(), // optional
|
|
306
|
-
extraSeriesFields: (el, series, { subplot, axis, onChange }) => { /* your fields */ },
|
|
370
|
+
extraSeriesFields: (el, series, { subplot, axis, onChange, field }) => { /* your fields */ },
|
|
307
371
|
});
|
|
308
372
|
editor.dispose();
|
|
309
373
|
```
|
|
@@ -341,8 +405,11 @@ config it was given and then calls `onChange(config)`.
|
|
|
341
405
|
dash, width and mode. `renderPlotGrid` draws it from the data you pass under
|
|
342
406
|
the series' id in `overlays`. **None** removes `series.overlay`. Without
|
|
343
407
|
`overlaySources` there are no overlay controls.
|
|
344
|
-
- `extraSeriesFields(el, series, { subplot, axis, onChange })` puts fields of
|
|
345
|
-
your own in each series row; call `onChange()` after you edit the series.
|
|
408
|
+
- `extraSeriesFields(el, series, { subplot, axis, onChange, field })` puts fields of
|
|
409
|
+
your own in each series row; call `onChange()` after you edit the series.
|
|
410
|
+
Since 0.17.16 they can share the series' label column (`field`, or returned
|
|
411
|
+
field specs; see
|
|
412
|
+
[above](#a-plot-config-series-is-one-block-and-an-embedders-fields-share-it-01716)). What
|
|
346
413
|
it returns is disposed (`dispose()`) when the row is redrawn or the editor
|
|
347
414
|
is disposed. A `dispose()` that throws is reported to `logger.warn` and does
|
|
348
415
|
not stop the rest of the editor being disposed.
|
|
@@ -230,18 +230,35 @@ openForm({
|
|
|
230
230
|
row hidden by `setVisible` drops its bounds until it is shown again, as it
|
|
231
231
|
drops `required`. As with any `<input type="number">`, a `min` is also where
|
|
232
232
|
`step` counts from.
|
|
233
|
-
- **`{ row: [field, ...] }`** puts its fields on one line
|
|
233
|
+
- **`{ row: [field, ...] }`** puts its fields on one line; their inputs share
|
|
234
|
+
what the labels leave equally (see below).
|
|
234
235
|
The row is layout only and has no name: each field in it is an ordinary
|
|
235
236
|
field with its own label, hint, error, `setVisible`, value and submitted
|
|
236
237
|
key, so hiding one hides only that one. The first keeps the form's label
|
|
237
238
|
column; the others size their label to its text. A form narrower than
|
|
238
239
|
360px stacks them (a container query, `twm-form`, on a form that has a row;
|
|
239
240
|
a form without one is not made a container). A form with a row opens at the
|
|
240
|
-
wide (640px) layout
|
|
241
|
+
wide (640px) layout, or wider for a row of three or more fields (below).
|
|
242
|
+
The row is `.twm-modal__field-group`.
|
|
241
243
|
|
|
242
244
|
A form with neither renders exactly as before.
|
|
243
245
|
`tests/open_form_rows_bounds.test.mjs` covers both in jsdom.
|
|
244
246
|
|
|
247
|
+
### The inputs of a row are equally wide (0.17.15)
|
|
248
|
+
|
|
249
|
+
Every input in a `{ row }` gets the same width: each field takes its label's
|
|
250
|
+
width (the first one the form's 130px label column), and what is left of the
|
|
251
|
+
row is shared equally between the inputs. The share is equal up to each
|
|
252
|
+
control's own padding and border: a native `<input>` or `<textarea>` keeps its
|
|
253
|
+
~18px, a select, toggle or picker (which sit in a padding-less slot) does not,
|
|
254
|
+
so in a row that mixes the two the native input is that much wider. A form
|
|
255
|
+
whose widest row has three or more fields opens wider: `170 + 230 × fields` px
|
|
256
|
+
(860px for three, 1090px for four), at least 640px, otherwise at most 90% of the
|
|
257
|
+
window; forms without such a row open as before. Until 0.17.14 every field was
|
|
258
|
+
an equal share of the row, including the first one's label column, so its input
|
|
259
|
+
was a sliver beside the others: about 15px for the first of four number fields
|
|
260
|
+
in a 640px form (BUG-0050).
|
|
261
|
+
|
|
245
262
|
### A switch: `ToggleField` and `type: 'toggle'` (0.17.0)
|
|
246
263
|
|
|
247
264
|
```js
|
|
@@ -292,3 +309,64 @@ the caller's own DOM.
|
|
|
292
309
|
|
|
293
310
|
`tests/confirm_escaping.test.mjs` opens each of them in jsdom with hostile
|
|
294
311
|
names and labels.
|
|
312
|
+
|
|
313
|
+
## Windows: closing, focus and Esc (0.17.16)
|
|
314
|
+
|
|
315
|
+
Every dialog above is a `ManagedWindow`, and so is any window you build
|
|
316
|
+
yourself with `new ManagedWindow({ id, title, content, ... })`. Three things
|
|
317
|
+
changed for all of them (BUG-0053):
|
|
318
|
+
|
|
319
|
+
- **A closed window is gone.** `close()` (once `beforeClose` allows it) runs
|
|
320
|
+
`onClose`, removes the window from the registry and releases its content,
|
|
321
|
+
its DOM and every callback it was given. `ManagedWindow.get(id)` then answers
|
|
322
|
+
`null`, `ManagedWindow.all()` no longer lists it and `win.isClosed` is
|
|
323
|
+
`true`. A closed window cannot be shown again: `show()` on it warns and does
|
|
324
|
+
nothing. To reopen, build a new `ManagedWindow` with the same id; it restores
|
|
325
|
+
the geometry the last one saved under that id. Until 0.17.15 a closed window
|
|
326
|
+
stayed registered, holding its content and whatever its `onClose` captured,
|
|
327
|
+
until the same id opened again.
|
|
328
|
+
- **Saved geometry is bounded.** Position and size are still saved per id
|
|
329
|
+
under `flexdesk.managedWindows.v1` (or your `storageKey`), but only for the
|
|
330
|
+
50 most recently saved ids; older entries are dropped.
|
|
331
|
+
`ManagedWindow.configure({ maxSavedWindows })` changes the cap
|
|
332
|
+
(`DEFAULT_MAX_SAVED_WINDOWS` is 50). Entries saved by an earlier version
|
|
333
|
+
carry no use stamp: those under the window manager's minted ids
|
|
334
|
+
(`twm-mw-…`, which never open again) are purged when the entry is read, and
|
|
335
|
+
the others are stamped in the order they were stored, as older than anything
|
|
336
|
+
saved since, so your stable ids keep their geometry. A window whose id is
|
|
337
|
+
minted per opening passes `persistGeometry: false` and neither saves nor
|
|
338
|
+
restores geometry; the window manager's own windows do. Modal windows never
|
|
339
|
+
saved geometry and still do not.
|
|
340
|
+
- **Focus moves in and back.** With `focusOnOpen` (default `true`), opening a
|
|
341
|
+
window focuses the first element of its content that takes focus (a link,
|
|
342
|
+
button, input, select, textarea, `contenteditable` or `tabindex` element
|
|
343
|
+
that is not disabled or hidden), or the window itself when there is none.
|
|
344
|
+
`focusOnOpen: 'window'` always focuses the window itself, for a dialog whose
|
|
345
|
+
first button must not catch a stray Enter (`showRunProgressDialog` uses it,
|
|
346
|
+
so Stop is never focused). Closing gives focus back to the element that had
|
|
347
|
+
it before the window opened, if that element is still in the document and
|
|
348
|
+
focus is still in the window or nowhere (on `body`); a window closed while
|
|
349
|
+
the user works elsewhere does not pull focus back. Restoring a minimised
|
|
350
|
+
window is not an open and moves nothing. A dialog that focuses a field of
|
|
351
|
+
its own a frame later, as `openForm` and `openConfirm` do, still wins, and
|
|
352
|
+
`Wizard.openInWindow` renders its first step before it shows the window, so
|
|
353
|
+
focus lands on the step's first field. Pass `focusOnOpen: false` for a
|
|
354
|
+
window that must not take focus when it appears; `showComputingWindow`
|
|
355
|
+
does.
|
|
356
|
+
- **The window itself holds focus.** Every window element has
|
|
357
|
+
`tabindex="-1"`: never a Tab stop, but a press on its title bar or on empty
|
|
358
|
+
content moves focus into the window (when it was not there already), so the
|
|
359
|
+
window's Esc still works after the user clicked it.
|
|
360
|
+
- **Esc closes a non-modal window only from inside it.** Which window an Esc
|
|
361
|
+
closes:
|
|
362
|
+
1. a modal window on top, wherever focus is (as before);
|
|
363
|
+
2. otherwise the window holding focus, modal or not — so a modal under a
|
|
364
|
+
non-modal window still closes on an Esc from inside it;
|
|
365
|
+
3. otherwise (focus in no window) the topmost modal.
|
|
366
|
+
A non-modal window that does not hold focus never closes on Esc, so an Esc
|
|
367
|
+
meant for an editor beside it is left alone. An Esc something inside the
|
|
368
|
+
window already handled with `preventDefault()` (an autocomplete closing its
|
|
369
|
+
list) or one ending an IME composition (`isComposing`) closes nothing. One
|
|
370
|
+
Esc closes one window, even when the close hands focus to another window.
|
|
371
|
+
|
|
372
|
+
`tests/managed_window_lifecycle.test.mjs` holds all of these.
|
package/docs/components/menus.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Package:** `@flexdesk/widgets` · [All docs](../README.md)
|
|
4
4
|
|
|
5
|
-
`createMenuBar` draws File / Edit / View menus from one declarative model, with pointer and keyboard behaviour and the WAI-ARIA menubar roles, and plugs into the shell. `ActionDropdown` draws a dropdown of action buttons from a trigger.
|
|
5
|
+
`createMenuBar` draws File / Edit / View menus from one declarative model, with pointer and keyboard behaviour and the WAI-ARIA menubar roles, and plugs into the shell. `ActionDropdown` draws a dropdown of action buttons from a trigger. `CategoryPicker` draws a categorised picker (search, categories, items, a detail pane) from a trigger.
|
|
6
6
|
|
|
7
7
|
## Context menus stay inside the window (0.17.9)
|
|
8
8
|
|
|
@@ -188,3 +188,87 @@ its right edge. Either flips to the other edge when the menu would leave the
|
|
|
188
188
|
window. A tile's ⋯ menu uses `'end'`
|
|
189
189
|
([Dashboard tiles](tiles.md#the--menu-is-drawn-over-the-page-0179)).
|
|
190
190
|
|
|
191
|
+
## A categorised picker: `CategoryPicker` (0.17.16)
|
|
192
|
+
|
|
193
|
+
An `ActionDropdown` is a flat list. When the choices fall into categories and
|
|
194
|
+
each needs a sentence (and perhaps a sample) before it is chosen, use
|
|
195
|
+
`CategoryPicker`: a search field over three columns — the categories (a
|
|
196
|
+
colour dot, the label, the item count), the active category's items (an icon
|
|
197
|
+
and the label), and a detail pane for the highlighted item (its title,
|
|
198
|
+
description, an optional monospace preview and an Add button).
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
import { CategoryPicker } from '@flexdesk/widgets';
|
|
202
|
+
|
|
203
|
+
const picker = new CategoryPicker({
|
|
204
|
+
trigger: addCellButton,
|
|
205
|
+
categories: [
|
|
206
|
+
{ id: 'text', label: 'Text', color: 'var(--accent-light)', items: [
|
|
207
|
+
{ id: 'heading', label: 'Heading', badge: 'H',
|
|
208
|
+
description: 'A section heading; it shapes the outline.',
|
|
209
|
+
preview: '## Debt dynamics' },
|
|
210
|
+
{ id: 'doc', label: 'Documentation', icon: 'description',
|
|
211
|
+
description: 'Markdown with LaTeX math.', keywords: ['markdown', 'latex'] },
|
|
212
|
+
] },
|
|
213
|
+
{ id: 'functions', label: 'Functions', color: 'var(--color-warning)', items: [
|
|
214
|
+
{ id: 'lag', label: 'Delay / Lag', badge: 'Δt',
|
|
215
|
+
description: 'The value of an expression some time ago.',
|
|
216
|
+
preview: 'expected = lag(inflation, 2)', keywords: 'delay history' },
|
|
217
|
+
] },
|
|
218
|
+
],
|
|
219
|
+
onSelect: (item, category) => insertCell(category.id, item.id),
|
|
220
|
+
placeholder: 'Search cells…', // the search field's placeholder (default 'Search…')
|
|
221
|
+
addLabel: 'Add cell', // the detail pane's button (default 'Add')
|
|
222
|
+
previewLabel: 'Writes', // a caption over the preview (none by default)
|
|
223
|
+
align: 'start', // or 'end', as ActionDropdown.position takes it
|
|
224
|
+
label: 'Add a cell', // the popover's accessible name (default 'Pick')
|
|
225
|
+
});
|
|
226
|
+
// The trigger's click toggles it; or drive it:
|
|
227
|
+
picker.open(); picker.close(); picker.toggle(); picker.position();
|
|
228
|
+
picker.setCategories(next); // finds the active category and the highlight again by id
|
|
229
|
+
picker.destroy();
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
An item has `id` and `label`, and optionally `icon` (a Material Symbols
|
|
233
|
+
ligature) or `badge` (a short text drawn as a monospace badge in the
|
|
234
|
+
category's colour), `description`, `preview` (text, shown monospace and
|
|
235
|
+
pre-wrapped) and `keywords` (an array or a string; searched, never shown). A
|
|
236
|
+
category's `color` is any CSS colour; it reaches the rows as
|
|
237
|
+
`--twm-category-picker-color`, and without one the dot and badge are muted.
|
|
238
|
+
`onSelect(item, category)` gets your own objects back.
|
|
239
|
+
|
|
240
|
+
- **Anchored like ActionDropdown.** It is appended to `body`, placed by
|
|
241
|
+
`ActionDropdown.position` (`position: fixed`, under the trigger when it fits,
|
|
242
|
+
flipped above it when it does not, kept 8px inside the window, capped to the
|
|
243
|
+
room there), and drawn on `--z-over-windows`, over the managed window it
|
|
244
|
+
opened from. Its columns scroll inside; the popover itself does not. The
|
|
245
|
+
trigger gets `aria-haspopup="dialog"`, `aria-expanded` and `twm-is-open`.
|
|
246
|
+
- **Search.** Focus is in the search field on open (the query starts empty).
|
|
247
|
+
Typing filters every category's items by label, description and keywords:
|
|
248
|
+
each word must appear, case-insensitively. While a query is set the items
|
|
249
|
+
column is one hit list grouped under the category labels, the counts are hit
|
|
250
|
+
counts (a category with none is dimmed), and the first hit is highlighted.
|
|
251
|
+
Clicking a category then jumps to its first hit.
|
|
252
|
+
- **Keyboard.** With no query, Up/Down move within the current column (the
|
|
253
|
+
categories, or the items) and wrap; Left/Right move between the two. With a
|
|
254
|
+
query, Up/Down walk the hit list across categories and Left/Right stay the
|
|
255
|
+
caret's. Enter picks the highlighted item. A printable key typed while a row
|
|
256
|
+
has focus goes to the search field. Tab and Shift+Tab stay inside: they
|
|
257
|
+
cycle the search field and Add (when it is enabled), as a modal
|
|
258
|
+
ManagedWindow's focus trap does. Keys an IME is composing with are left to
|
|
259
|
+
it. Escape closes the picker and nothing
|
|
260
|
+
else (it is caught first and stopped, so a ManagedWindow around it stays
|
|
261
|
+
open). Escape and a pick give focus back to the trigger.
|
|
262
|
+
- **Pointer.** Hovering or clicking an item shows it in the detail pane;
|
|
263
|
+
double-clicking it, or Add, picks it. A click outside closes the picker.
|
|
264
|
+
Pressing anywhere inside but the search field keeps focus where it is, so
|
|
265
|
+
the keyboard keeps working (the preview's text cannot be drag-selected).
|
|
266
|
+
- **Accessibility.** The popover is a named `role="dialog"`. The search field
|
|
267
|
+
is a combobox whose `aria-activedescendant` is the active category while the
|
|
268
|
+
categories column is current, else the highlighted item. While a query is
|
|
269
|
+
set, each category's hits are a `role="group"` named by its header.
|
|
270
|
+
|
|
271
|
+
The Add button is FlexDesk's `twm-btn twm-btn--primary`; everything else is
|
|
272
|
+
styled from the tokens (`tests/category_picker.test.mjs` checks there is no
|
|
273
|
+
colour literal in its rules, and that base.css and flexdesk.css carry the same
|
|
274
|
+
block).
|
package/docs/components/shell.md
CHANGED
|
@@ -155,6 +155,58 @@ content: {
|
|
|
155
155
|
chrome shows only — the tree, the tab labels and the saved layout are left
|
|
156
156
|
alone — and a call from content whose pane has since been rebuilt does nothing.
|
|
157
157
|
|
|
158
|
+
## A tile breadcrumb of one segment is not drawn (0.17.15)
|
|
159
|
+
|
|
160
|
+
`mountTileBreadcrumb` (and the page shell around a content, which mounts one)
|
|
161
|
+
draws the path to where a tile is: an optional root crumb, the top-nav page,
|
|
162
|
+
the trail, the entity's ancestors and the entity. When that path is a single
|
|
163
|
+
segment (or none), for instance a top-nav page with no root crumb, no trail and
|
|
164
|
+
no entity, or an `appGlobal` kind, the strip says nothing the tile's own title
|
|
165
|
+
bar does not already say. It is then `hidden`, and
|
|
166
|
+
`.twm-page-shell__breadcrumb` around it takes no room, so the tile's content
|
|
167
|
+
starts right under its title. `refresh()` shows the strip again as soon as the
|
|
168
|
+
path has two segments; a path of two or more is drawn as before. Until 0.17.14
|
|
169
|
+
a top-level page showed its name twice, in the title bar and in a one-segment
|
|
170
|
+
breadcrumb under it (STORY-0124). `renderBreadcrumb`, which draws segments a
|
|
171
|
+
content routes itself, is unchanged.
|
|
172
|
+
|
|
173
|
+
## A kind that lives in a window: `windowOnly` and `window` (0.17.16)
|
|
174
|
+
|
|
175
|
+
A kind can be a tool rather than a page: it opens in a managed window, never in
|
|
176
|
+
a tile, and it can say how big that window is.
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
const taxonomy = createTaxonomy({
|
|
180
|
+
root: 'start',
|
|
181
|
+
kinds: {
|
|
182
|
+
start: { label: 'Start', icon: 'apps' },
|
|
183
|
+
estimation: { label: 'Parameter Estimation', icon: 'tune',
|
|
184
|
+
single: true, windowOnly: true, window: { width: 1100, height: 700 } },
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
wm.navigate('estimation'); // a window, whatever `dest` says
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- **`window: { width?, height? }`** is the initial size, in px, of a window the
|
|
191
|
+
kind opens in, by `navigate(…, { dest: 'window' })` or, for a window-only
|
|
192
|
+
kind, always. ManagedWindow clamps it to the screen and to its minimum
|
|
193
|
+
(400 × 300), so a smaller size opens at the minimum. Without it a window
|
|
194
|
+
opens at ManagedWindow's default (600 × 400).
|
|
195
|
+
- **`windowOnly: true`** sends every open path to a window: `navigate` with
|
|
196
|
+
any `dest`, `openInPrimary`, `openInLeaf`, the tab verbs and
|
|
197
|
+
`splitLeafWith`, `navigateActiveTab` (crumbs, Backspace) and
|
|
198
|
+
`openInWindow`. With `single`, opening it again reveals the window that is
|
|
199
|
+
open. Its window never goes into a tile: it has no *Back to tile*, its
|
|
200
|
+
maximise button maximises it, and it does not snap onto a tile when dragged.
|
|
201
|
+
A restored layout that still holds a tab of the kind in a tile (or archived
|
|
202
|
+
under a page) drops it and warns once, naming the kinds; the rest of the
|
|
203
|
+
layout stays, and the next open makes a window. An entry of it in a tab's
|
|
204
|
+
back history (a layout saved before the kind was window-only) opens its
|
|
205
|
+
window when Backspace reaches it. Neither the root kind nor a top-nav
|
|
206
|
+
page can be window-only (`createTaxonomy` refuses).
|
|
207
|
+
- `taxonomy.isWindowOnly(kind)` and `taxonomy.windowSize(kind)` answer both
|
|
208
|
+
(STORY-0127).
|
|
209
|
+
|
|
158
210
|
## Buttons that change with the pane: `ctx.setChromeActions` (0.17.0)
|
|
159
211
|
|
|
160
212
|
A factory may return `chromeActions` — `[{icon, title, onClick}]`, drawn in the
|
|
@@ -66,3 +66,18 @@ Until 0.17.4 a panel in a narrow tile reached past the tile's left edge across
|
|
|
66
66
|
the neighbouring pane, and neither icon had a size of its own: both took
|
|
67
67
|
whatever the page's global icon size was. `tests/slide_out_panel_clamp.test.mjs`
|
|
68
68
|
holds it.
|
|
69
|
+
|
|
70
|
+
## Never drawn outside its box (0.17.15)
|
|
71
|
+
|
|
72
|
+
While it is open the panel is `position: fixed`, pinned to its host's right
|
|
73
|
+
edge, so the host's `overflow: hidden` no longer clips it. Until 0.17.14 it
|
|
74
|
+
slid in (and out) by `translateX(100%)` and, on the way, was drawn across
|
|
75
|
+
whatever lies right of the host: a dock panel, an inspector. It no longer
|
|
76
|
+
moves: a clip alone carries the motion, revealing it from its right edge
|
|
77
|
+
leftwards (`clip-path: inset(0 0 0 100%)` closed, `inset(0 0 0 -16px)` open,
|
|
78
|
+
200ms ease-out), so no frame paints outside its final box, which lies inside
|
|
79
|
+
the host. No transform is animated beside the clip: the two could drift apart,
|
|
80
|
+
the transform often running on the compositor. The left inset of -16px keeps
|
|
81
|
+
the panel's shadow; when the panel is as wide as its host (BUG-0035's cap)
|
|
82
|
+
that shadow falls on the neighbouring pane, as before. Nothing changes for
|
|
83
|
+
callers (BUG-0051).
|
package/docs/components/tiles.md
CHANGED
|
@@ -233,3 +233,40 @@ Until 0.17.8 the menu was `position: absolute` inside the tile, so in a tile
|
|
|
233
233
|
narrower than about 220px its left part and its labels were cut off
|
|
234
234
|
(BUG-0043). `tests/tile_menu_unclipped.test.mjs` holds it.
|
|
235
235
|
|
|
236
|
+
|
|
237
|
+
## A double-click on the title strip configures the tile (0.17.16)
|
|
238
|
+
|
|
239
|
+
A double-click on a tile's header (`.tile-header`: its title, its drag grip or
|
|
240
|
+
the strip between them) does what the cogwheel (`.tile-config-btn`) does: it
|
|
241
|
+
calls the same `_onConfigClick`, so the tile is selected and
|
|
242
|
+
`tile:config-request` goes out on the event bus and as a DOM event exactly as
|
|
243
|
+
for a click on the cogwheel. A double-click on one of the strip's buttons
|
|
244
|
+
(cogwheel, ⋯, info, expand, remove) or in an editable field you put in the
|
|
245
|
+
strip (`input`, `textarea`, `select`, `contenteditable`) does not. A read-only
|
|
246
|
+
tile has no cogwheel and opens nothing.
|
|
247
|
+
|
|
248
|
+
`new TileGrid({ configOnDoubleClick: false })` turns it off for every tile of
|
|
249
|
+
the grid; a tile takes the same option (`tile.configOnDoubleClick`, read at the
|
|
250
|
+
double-click). `tests/tile_title_dblclick_config.test.mjs` holds it.
|
|
251
|
+
|
|
252
|
+
## Several tiles marked as being configured: `grid.setConfiguring` (0.17.16)
|
|
253
|
+
|
|
254
|
+
A config request (cogwheel or double-click) highlights the requesting tile with
|
|
255
|
+
`tile--selected` and takes that highlight off every other tile of the grid:
|
|
256
|
+
one selection per grid, which suits one config panel. An embedder that keeps a
|
|
257
|
+
config window open per tile marks each of those tiles on the grid:
|
|
258
|
+
|
|
259
|
+
```js
|
|
260
|
+
grid.setConfiguring(tileId, true); // when its config window opens
|
|
261
|
+
grid.setConfiguring(tileId, false); // when its config window closes
|
|
262
|
+
grid.isConfiguring(tileId); // true while marked
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The mark is the class `tile--configuring`, drawn exactly like `tile--selected`
|
|
266
|
+
(same rules in `flexdesk.css`). It is the embedder's alone: no config request,
|
|
267
|
+
on that tile or another, clears it. The grid holds it **by tile id**, so a tile
|
|
268
|
+
that `setLayout` disposes and rebuilds (or an id added later) is marked again,
|
|
269
|
+
and clearing it after a rebuild reaches the live tile. `tile.setConfiguring(on)`
|
|
270
|
+
and `tile.isConfiguring` remain as conveniences: on a tile in a grid they go
|
|
271
|
+
through the grid. Do not add or remove `tile--selected` from outside; it
|
|
272
|
+
belongs to the config request.
|