@umriss-ui/dashboard 0.41.1 → 0.43.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/CHANGELOG.md CHANGED
@@ -4,20 +4,52 @@ This document describes what changes for **callers** of the package: exports,
4
4
  props, behaviour.
5
5
 
6
6
  **The numbers.** The middle digit rises when something is added; the last one
7
- when something is repaired. As long as the first digit is `0`, no number
8
- promises compatibility — which is why a rule stronger than the figure applies as
9
- well: **whatever changes existing behaviour stands under a heading "Changed" of
10
- its own**, no matter which digit rose. Whoever reads only one section before an
11
- upgrade reads that one.
7
+ when something is repaired. The six packages are released in step, and a rename
8
+ keeps its old name for one further minor, with a warning, before the minor after
9
+ it removes the name ([the upgrade promise, ADR-0066](../../docs/adr/0066-one-number-peers-over-the-minor-a-rename-lives-one-minor.md)).
10
+ **Whatever changes existing behaviour stands under a heading "Changed" of its
11
+ own**, no matter which digit rose, with a `Migration:` line. Whoever reads only
12
+ one section before an upgrade reads that one.
12
13
 
13
14
  The dashboard joins the other five packages' one number at its first version: they are released together, and a package without a change of its own still gets a heading for it.
14
15
 
16
+ ## 0.43.0 – What fills a widget (Oct. 2026)
17
+
18
+ ### Added
19
+
20
+ - **`Dashboard` takes a `ref`**, typed at its root `role="group"` (ADR-0068); at runtime it reached that element before.
21
+ - **The README says what fills a widget and who hears of a failure:** a chart and a schedule fill the body, a table stays within it with its toolbar and pager in view, a wrapper passes the height on with `height: 100%`; a widget whose content throws reaches React's `onCaughtError`, keeps its frame, and tries again when its size or options change.
22
+
23
+ ### Changed
24
+
25
+ - **The peer range on core moves to `^0.43.0`.**
26
+ - Migration: install the 0.43 version of `@umriss-ui/core` beside it.
27
+
28
+ ## 0.42.0 – A widget scrolls what exceeds its size (Oct. 2026)
29
+
30
+ ### Added
31
+
32
+ - **Loadable in Vitest and Jest** with core's lines: the `.` entry carries a `default` condition, the README's "Testing" section points at core's, and the release check runs a test of a widget under both runners.
33
+ - **The mode label reads core's `--u-transform-caps`.**
34
+ - **TypeScript 5.4 or newer**, stated in the Install section (the declarations use `NoInfer`); the release check compiles a strict consumer under 5.4 as well as the current TypeScript.
35
+
36
+ ### Changed
37
+
38
+ - **The peer range on core covers the minor: `^0.42.0`.** It named the exact patch before (`^0.41.1`), so npm refused an install beside an earlier patch of core with `ERESOLVE`; the six packages move under one number and a minor moves the range (ADR-0066).
39
+ - Migration: nothing to change; any `@umriss-ui/core` 0.42 patch satisfies the peer.
40
+ - **Content beyond a widget's size scrolls inside its body**, on both axes, where it was cut. While it overflows, the body is a tab stop named by the widget's title, so the keyboard reaches what the wheel reaches; in the editing mode the body stays inert and keeps its scroll position (ADR-0064, amended).
41
+ - Migration: nothing to change; a widget whose content was cut now scrolls - look at a size that was chosen too small, and at a test that tabs through the dashboard.
42
+ - **The editing mode keeps every body at its height.** The mode's band took about 4 px from each body, so content that fitted at rest was cut on "Edit"; a card's head now has one height at rest and in the mode, under any spacing tokens, and the band's controls are centred in it.
43
+ - Migration: nothing to change; a screenshot of the editing mode moves by a few pixels.
44
+
15
45
  ## 0.41.1 – The documentation runs where the code is (Oct. 2026)
16
46
 
17
47
  ### Changed
18
48
 
19
49
  - **The documentation is no longer hosted.** The site on GitHub Pages is gone, and the package's `homepage` now names its README on GitHub. The running documentation of this version opens from a checkout of the tag `dashboard-v0.41.1` with `pnpm install` and `pnpm start`, at `http://localhost:4170/umriss-ui/` - the repository README says how, under "See it running".
50
+ - Migration: nothing to change; a bookmark or link to the old hosted site no longer resolves - open the documentation from a checkout of the tag, as described above.
20
51
  - **`docs/llms-full.md` in the package stands on its own:** a link to another page points at that page's heading in the same file, a link to a place on the site that the file does not hold keeps only its text, and ADR-0032 is linked at this version's tag.
52
+ - Migration: nothing to change; look at any tool that read `docs/llms-full.md` and followed its links to the site, which now find the heading in the same file.
21
53
 
22
54
  ## 0.41.0 – Stored views that never crash, and widgets that wait alone (Oct. 2026)
23
55
 
package/README.md CHANGED
@@ -15,7 +15,7 @@ pnpm add @umriss-ui/dashboard @umriss-ui/core
15
15
 
16
16
  `@umriss-ui/core` is a **peer dependency**: the dashboard takes its styling
17
17
  regime, its frames, menus and drawer, its wording and its announcements from
18
- there. React 19 or newer as a peer as well. The dashboard depends on `core`
18
+ there. React 19 or newer as a peer as well. TypeScript 5.4 or newer (the declarations use `NoInfer`). The dashboard depends on `core`
19
19
  alone; nothing depends on the dashboard, and it takes no chart, table or
20
20
  schedule - what stands in a widget is the application's.
21
21
 
@@ -23,8 +23,10 @@ The package ships ES2022 syntax; an application that serves older browsers
23
23
  transpiles it itself.
24
24
 
25
25
  Every entry begins with `"use client"`, the directive a framework with Server
26
- Components reads to treat a module as client code. The dashboard renders to a
27
- string on the server and in a test, without a layout engine.
26
+ Components reads to treat a module as client code. A dashboard is declared by its hook,
27
+ `useDashboard`, so the declaration lives in a Client Component file: a Server Component renders that
28
+ file and hands it the data its widgets show. The dashboard renders to a string on the server and in
29
+ a test, without a layout engine.
28
30
 
29
31
  ## Declared like a table
30
32
 
@@ -49,7 +51,7 @@ const { Dashboard, WidgetType } = useDashboard({
49
51
  name="figure"
50
52
  label="Figure"
51
53
  sentence="One number."
52
- sizes={{ small: { columns: 3, rows: 2 }, medium: { columns: 4, rows: 3 } }}
54
+ sizes={{ small: { columns: 3, rows: 3 }, medium: { columns: 4, rows: 3 } }}
53
55
  start="small"
54
56
  defaultOptions={{ name: "Orders" }}
55
57
  >
@@ -58,6 +60,8 @@ const { Dashboard, WidgetType } = useDashboard({
58
60
  </Dashboard>;
59
61
  ```
60
62
 
63
+ `Dashboard` is an element: `ref`, `className`, `style` and the rest reach its root `role="group"`.
64
+
61
65
  - **A sequence, never coordinates (ADR-0064).** One stored arrangement is right
62
66
  at every width. A widget that does not fit its row starts the next, and the
63
67
  room it leaves stays free. Below the width at which a column would be too
@@ -65,7 +69,14 @@ const { Dashboard, WidgetType } = useDashboard({
65
69
  stacks of widgets sharing a place and a layout per breakpoint are not built.
66
70
  - **Sizes have names.** `small`, `medium`, `wide`, `tall`, `large` and `full`,
67
71
  worded in English and German by the library; a type offers some of them. The
68
- height follows the size, never the content.
72
+ height follows the size, never the content: what the content needs beyond
73
+ the size scrolls inside the widget, and the keyboard reaches it.
74
+ - **A view fills its widget's body.** A chart and a schedule fill the body and
75
+ need no height of their own; a table stays within it, its toolbar and pager
76
+ in view, and its rows scroll. A wrapper of the application's own between the
77
+ body and the view passes the height on with `height: 100%`. A view whose floor
78
+ is taller than a size (a chart's 300 px, a schedule's 400 px) takes a lower one
79
+ through `style`'s `minHeight`, as the dispatcher's trends do.
69
80
  - **Editing is a mode and a draft.** "Edit" opens it; every change is a step
70
81
  that can be undone and redone; "Done" reports the whole arrangement once
71
82
  through `onViewChange`, "Cancel" puts it back, "Reset" steps to the default.
@@ -77,10 +88,23 @@ const { Dashboard, WidgetType } = useDashboard({
77
88
  - **A kept arrangement is read leniently.** A widget of a type that is gone
78
89
  falls out, a size a type no longer offers falls back to its starting size.
79
90
  A widget whose content throws keeps its frame and says so in a sentence.
91
+ - **A failed widget is reported and may be retried.** React's `onCaughtError`,
92
+ given to `createRoot` or to the application's root, receives the error of a
93
+ widget whose content throws; the other widgets render. A retry is an error
94
+ boundary of the application's own inside the content: it catches first, so the
95
+ dashboard's boundary never sees the error, and it can reset the application's
96
+ data layer. Without one, a widget gets another try by itself when its size or
97
+ its options change.
80
98
  - **The application's, not the library's:** where an arrangement is stored,
81
99
  which default a role gets, several dashboards, sharing, and what stands in a
82
100
  widget.
83
101
 
102
+ ## Testing
103
+
104
+ Tested under Vitest 5 and Jest 30 on jsdom. The runner lines and the setup file are in
105
+ [`@umriss-ui/core`'s Testing section](../core/README.md#testing); `check:dist` runs a test of the
106
+ installed dashboard (a widget and its title) with exactly those files.
107
+
84
108
  ## Documentation
85
109
 
86
110
  The demo is the documentation; in a checkout, `pnpm dev:dashboard` (port 4178)
@@ -1 +1 @@
1
- @layer umriss.tokens,umriss.base,umriss.components;@layer umriss.components{._root_10mvp_17,._root_10mvp_17:before,._root_10mvp_17:after{box-sizing:border-box}._bar_10mvp_34,._bar_10mvp_34:before,._bar_10mvp_34:after{box-sizing:border-box}._mode_10mvp_43,._mode_10mvp_43:before,._mode_10mvp_43:after{box-sizing:border-box}._head_10mvp_58,._head_10mvp_58:before,._head_10mvp_58:after{box-sizing:border-box}._editingHead_10mvp_68,._editingHead_10mvp_68:before,._editingHead_10mvp_68:after{box-sizing:border-box}._grip_10mvp_76,._grip_10mvp_76:before,._grip_10mvp_76:after{box-sizing:border-box}._title_10mvp_81,._title_10mvp_81:before,._title_10mvp_81:after{box-sizing:border-box}._size_10mvp_92,._size_10mvp_92:before,._size_10mvp_92:after{box-sizing:border-box}._item_10mvp_100,._item_10mvp_100:before,._item_10mvp_100:after{box-sizing:border-box}._placeholder_10mvp_110,._placeholder_10mvp_110:before,._placeholder_10mvp_110:after{box-sizing:border-box}._grid_10mvp_116,._grid_10mvp_116:before,._grid_10mvp_116:after{box-sizing:border-box}._headingRow_10mvp_133,._headingRow_10mvp_133:before,._headingRow_10mvp_133:after{box-sizing:border-box}._headingHead_10mvp_138,._headingHead_10mvp_138:before,._headingHead_10mvp_138:after{box-sizing:border-box}._heading_10mvp_133,._heading_10mvp_133:before,._heading_10mvp_133:after{box-sizing:border-box}._body_10mvp_163,._body_10mvp_163:before,._body_10mvp_163:after{box-sizing:border-box}._options_10mvp_189,._options_10mvp_189:before,._options_10mvp_189:after{box-sizing:border-box}._catalogue_10mvp_194,._catalogue_10mvp_194:before,._catalogue_10mvp_194:after{box-sizing:border-box}._entry_10mvp_202,._entry_10mvp_202:before,._entry_10mvp_202:after{box-sizing:border-box}._footprint_10mvp_209,._footprint_10mvp_209:before,._footprint_10mvp_209:after{box-sizing:border-box}._entryText_10mvp_221,._entryText_10mvp_221:before,._entryText_10mvp_221:after{box-sizing:border-box}._root_10mvp_17{container-type:inline-size;width:100%;padding:0 var(--u-space-4) var(--u-space-4);border-radius:var(--u-radius-lg);corner-shape:squircle;--_dashboard-row-unit: var(--u-dashboard-row, round(max(50px, 3.125rem), 1px));--_dashboard-gap: var(--u-dashboard-gap, 12px)}._root_10mvp_17[data-editing]{box-shadow:inset 0 0 0 2px var(--u-color-accent);outline:2px solid transparent;outline-offset:-2px}._bar_10mvp_34{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:var(--u-space-2);padding-block:var(--u-space-3)}._mode_10mvp_43{margin-inline-end:auto;padding:5px var(--u-space-2);border-radius:var(--u-radius-xs);corner-shape:squircle;background:var(--u-color-accent);color:var(--u-color-on-accent);font-family:var(--u-font-mono);font-size:var(--u-text-xs);font-weight:var(--u-weight-medium);line-height:var(--u-leading-none);letter-spacing:var(--u-tracking-caps);text-transform:uppercase}._head_10mvp_58{display:flex;align-items:center;gap:6px;min-width:0;min-height:calc(var(--u-control-height) + 2px);padding:var(--u-space-2) var(--u-space-2) 0 var(--u-space-3);border-radius:var(--u-radius-lg) var(--u-radius-lg) 0 0;corner-shape:squircle}._editingHead_10mvp_68{padding-block-end:var(--u-space-1);background:var(--u-color-surface-pressed);touch-action:none;user-select:none;cursor:grab}._grip_10mvp_76{flex:none;color:var(--u-color-text-muted)}._title_10mvp_81{flex:1 1 auto;min-width:0;margin:0;overflow:hidden;font-size:var(--u-text-sm);font-weight:var(--u-weight-medium);white-space:nowrap;text-overflow:ellipsis}._size_10mvp_92{font-family:var(--u-font-mono);font-size:var(--u-text-xs);line-height:var(--u-leading-none);color:var(--u-color-text-muted);white-space:nowrap}._item_10mvp_100[data-dragging]{position:relative;z-index:5;box-shadow:var(--u-shadow-overlay)}._item_10mvp_100[data-dragging] ._editingHead_10mvp_68{cursor:grabbing}._placeholder_10mvp_110{border:2px dashed var(--u-color-accent);border-radius:var(--u-radius-lg);corner-shape:squircle;background:var(--u-color-accent-subtle)}._grid_10mvp_116{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));grid-auto-rows:var(--_dashboard-row-unit);gap:var(--_dashboard-gap);max-width:var(--u-dashboard-max-width, 1560px)}._item_10mvp_100{grid-column:var(--_dashboard-column) / span var(--_dashboard-columns);grid-row:var(--_dashboard-row) / span var(--_dashboard-rows);min-height:0;min-width:0}._headingRow_10mvp_133{display:flex;align-items:stretch}._headingHead_10mvp_138{flex:1 1 auto;align-items:flex-end;padding:0 var(--u-space-2) 0 2px;border-radius:var(--u-radius-md);corner-shape:squircle}._headingRow_10mvp_133[data-editing] ._headingHead_10mvp_138{align-items:center;padding-inline-start:var(--u-space-3);padding-block-end:0}._heading_10mvp_133{flex:1 1 auto;min-width:0;margin:0;padding-block-end:calc(var(--u-space-1) + 2px);font-size:var(--u-text-lg)}._headingRow_10mvp_133[data-editing] ._heading_10mvp_133{padding-block-end:0}._body_10mvp_163{flex:1 1 auto;min-height:0;padding:2px var(--u-space-3) 10px;overflow:hidden}._item_10mvp_100[data-editing] ._body_10mvp_163{padding-block-start:var(--u-space-2)}@container (max-width: 755px){._grid_10mvp_116{grid-template-columns:minmax(0,1fr);grid-auto-rows:auto}._item_10mvp_100{grid-column:1;grid-row:auto;order:var(--_dashboard-order, 0);height:calc(var(--_dashboard-rows) * var(--_dashboard-row-unit) + (var(--_dashboard-rows) - 1) * var(--_dashboard-gap))}}._options_10mvp_189{min-width:16rem;padding:var(--u-space-3)}._catalogue_10mvp_194{list-style:none;margin:0;padding:0;display:grid;gap:var(--u-space-3, 12px)}._entry_10mvp_202{display:grid;grid-template-columns:4.5rem 1fr auto;align-items:center;gap:var(--u-space-3, 12px)}._footprint_10mvp_209{aspect-ratio:3 / 1;border-radius:2px;corner-shape:squircle;box-shadow:var(--u-edge)}._footprint_10mvp_209>span{box-sizing:border-box}._footprint_10mvp_209>span{display:block;background:var(--u-color-accent, currentColor);opacity:.5}._entryText_10mvp_221{display:grid}}
1
+ @layer umriss.tokens,umriss.base,umriss.components;@layer umriss.components{._root_2b0q1_17,._root_2b0q1_17:before,._root_2b0q1_17:after{box-sizing:border-box}._bar_2b0q1_34,._bar_2b0q1_34:before,._bar_2b0q1_34:after{box-sizing:border-box}._mode_2b0q1_43,._mode_2b0q1_43:before,._mode_2b0q1_43:after{box-sizing:border-box}._head_2b0q1_58,._head_2b0q1_58:before,._head_2b0q1_58:after{box-sizing:border-box}._editingHead_2b0q1_77,._editingHead_2b0q1_77:before,._editingHead_2b0q1_77:after{box-sizing:border-box}._grip_2b0q1_85,._grip_2b0q1_85:before,._grip_2b0q1_85:after{box-sizing:border-box}._title_2b0q1_90,._title_2b0q1_90:before,._title_2b0q1_90:after{box-sizing:border-box}._size_2b0q1_102,._size_2b0q1_102:before,._size_2b0q1_102:after{box-sizing:border-box}._item_2b0q1_110,._item_2b0q1_110:before,._item_2b0q1_110:after{box-sizing:border-box}._placeholder_2b0q1_120,._placeholder_2b0q1_120:before,._placeholder_2b0q1_120:after{box-sizing:border-box}._grid_2b0q1_126,._grid_2b0q1_126:before,._grid_2b0q1_126:after{box-sizing:border-box}._headingRow_2b0q1_143,._headingRow_2b0q1_143:before,._headingRow_2b0q1_143:after{box-sizing:border-box}._headingHead_2b0q1_73,._headingHead_2b0q1_73:before,._headingHead_2b0q1_73:after{box-sizing:border-box}._heading_2b0q1_73,._heading_2b0q1_73:before,._heading_2b0q1_73:after{box-sizing:border-box}._body_2b0q1_173,._body_2b0q1_173:before,._body_2b0q1_173:after{box-sizing:border-box}._options_2b0q1_201,._options_2b0q1_201:before,._options_2b0q1_201:after{box-sizing:border-box}._catalogue_2b0q1_206,._catalogue_2b0q1_206:before,._catalogue_2b0q1_206:after{box-sizing:border-box}._entry_2b0q1_214,._entry_2b0q1_214:before,._entry_2b0q1_214:after{box-sizing:border-box}._footprint_2b0q1_221,._footprint_2b0q1_221:before,._footprint_2b0q1_221:after{box-sizing:border-box}._entryText_2b0q1_233,._entryText_2b0q1_233:before,._entryText_2b0q1_233:after{box-sizing:border-box}._root_2b0q1_17{container-type:inline-size;width:100%;padding:0 var(--u-space-4) var(--u-space-4);border-radius:var(--u-radius-lg);corner-shape:squircle;--_dashboard-row-unit: var(--u-dashboard-row, round(max(50px, 3.125rem), 1px));--_dashboard-gap: var(--u-dashboard-gap, 12px)}._root_2b0q1_17[data-editing]{box-shadow:inset 0 0 0 2px var(--u-color-accent);outline:2px solid transparent;outline-offset:-2px}._bar_2b0q1_34{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:var(--u-space-2);padding-block:var(--u-space-3)}._mode_2b0q1_43{margin-inline-end:auto;padding:5px var(--u-space-2);border-radius:var(--u-radius-xs);corner-shape:squircle;background:var(--u-color-accent);color:var(--u-color-on-accent);font-family:var(--u-font-mono);font-size:var(--u-text-xs);font-weight:var(--u-weight-medium);line-height:var(--u-leading-none);letter-spacing:var(--u-tracking-caps);text-transform:var(--u-transform-caps)}._head_2b0q1_58{display:flex;align-items:center;gap:6px;min-width:0;min-height:calc(var(--u-control-height) + 2px);padding:var(--u-space-2) var(--u-space-2) 0 var(--u-space-3);border-radius:var(--u-radius-lg) var(--u-radius-lg) 0 0;corner-shape:squircle}._head_2b0q1_58:not(._headingHead_2b0q1_73){height:max(calc(var(--u-control-height) + 2px),calc(var(--u-space-2) + var(--u-text-sm) * var(--u-leading-normal)))}._editingHead_2b0q1_77{padding-block:0;background:var(--u-color-surface-pressed);touch-action:none;user-select:none;cursor:grab}._grip_2b0q1_85{flex:none;color:var(--u-color-text-muted)}._title_2b0q1_90{flex:1 1 auto;min-width:0;margin:0;overflow:hidden;font-size:var(--u-text-sm);font-weight:var(--u-weight-medium);line-height:var(--u-leading-normal);white-space:nowrap;text-overflow:ellipsis}._size_2b0q1_102{font-family:var(--u-font-mono);font-size:var(--u-text-xs);line-height:var(--u-leading-none);color:var(--u-color-text-muted);white-space:nowrap}._item_2b0q1_110[data-dragging]{position:relative;z-index:5;box-shadow:var(--u-shadow-overlay)}._item_2b0q1_110[data-dragging] ._editingHead_2b0q1_77{cursor:grabbing}._placeholder_2b0q1_120{border:2px dashed var(--u-color-accent);border-radius:var(--u-radius-lg);corner-shape:squircle;background:var(--u-color-accent-subtle)}._grid_2b0q1_126{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));grid-auto-rows:var(--_dashboard-row-unit);gap:var(--_dashboard-gap);max-width:var(--u-dashboard-max-width, 1560px)}._item_2b0q1_110{grid-column:var(--_dashboard-column) / span var(--_dashboard-columns);grid-row:var(--_dashboard-row) / span var(--_dashboard-rows);min-height:0;min-width:0}._headingRow_2b0q1_143{display:flex;align-items:stretch}._headingHead_2b0q1_73{flex:1 1 auto;align-items:flex-end;padding:0 var(--u-space-2) 0 2px;border-radius:var(--u-radius-md);corner-shape:squircle}._headingRow_2b0q1_143[data-editing] ._headingHead_2b0q1_73{align-items:center;padding-inline-start:var(--u-space-3);padding-block-end:0}._heading_2b0q1_73{flex:1 1 auto;min-width:0;margin:0;padding-block-end:calc(var(--u-space-1) + 2px);font-size:var(--u-text-lg)}._headingRow_2b0q1_143[data-editing] ._heading_2b0q1_73{padding-block-end:0}._body_2b0q1_173{flex:1 1 auto;min-height:0;padding:2px var(--u-space-3) 10px;overflow:auto}._body_2b0q1_173:focus-visible{outline:2px solid var(--u-color-accent);outline-offset:-2px}@container (max-width: 755px){._grid_2b0q1_126{grid-template-columns:minmax(0,1fr);grid-auto-rows:auto}._item_2b0q1_110{grid-column:1;grid-row:auto;order:var(--_dashboard-order, 0);height:calc(var(--_dashboard-rows) * var(--_dashboard-row-unit) + (var(--_dashboard-rows) - 1) * var(--_dashboard-gap))}}._options_2b0q1_201{min-width:16rem;padding:var(--u-space-3)}._catalogue_2b0q1_206{list-style:none;margin:0;padding:0;display:grid;gap:var(--u-space-3, 12px)}._entry_2b0q1_214{display:grid;grid-template-columns:4.5rem 1fr auto;align-items:center;gap:var(--u-space-3, 12px)}._footprint_2b0q1_221{aspect-ratio:3 / 1;border-radius:2px;corner-shape:squircle;box-shadow:var(--u-edge)}._footprint_2b0q1_221>span{box-sizing:border-box}._footprint_2b0q1_221>span{display:block;background:var(--u-color-accent, currentColor);opacity:.5}._entryText_2b0q1_233{display:grid}}
package/dist/dashboard.js CHANGED
@@ -1,26 +1,26 @@
1
1
  "use client";
2
2
  import "./dashboard.css";
3
- import { Component as Me, useState as Z, useRef as O, useLayoutEffect as ie, useMemo as oe, Children as Ce, isValidElement as ce, useEffect as ne, Suspense as Se, useSyncExternalStore as le, createElement as ke } from "react";
4
- import { useWording as ae, FormField as Ne, Input as He, Button as $, Text as K, GripGlyph as ue, Heading as Ee, Card as Oe, Skeleton as Re, Drawer as Te, ModalHeader as De, ModalBody as Ae, Popover as Ie, announce as F, Menu as Le, MenuItem as V, MenuSeparator as se } from "@umriss-ui/core";
5
- import { jsx as a, jsxs as E, Fragment as re } from "react/jsx-runtime";
6
- const $e = (s, t) => (d) => {
7
- const i = d.widgets.findIndex((n) => n.id === s), C = Math.max(0, Math.min(d.widgets.length - 1, t));
8
- if (i < 0 || i === C) return d;
9
- const x = [...d.widgets];
10
- return x.splice(C, 0, ...x.splice(i, 1)), { ...d, widgets: x };
11
- }, Fe = (s, t) => (d) => d.widgets.some((i) => i.id === s && i.size !== t) ? { ...d, widgets: d.widgets.map((i) => i.id === s ? { ...i, size: t } : i) } : d, Pe = (s) => (t) => t.widgets.some((d) => d.id === s) ? { ...t, widgets: t.widgets.filter((d) => d.id !== s) } : t, Be = (s, t) => {
12
- let d = 1;
13
- for (; s.widgets.some((i) => i.id === `${t}-${d}`); ) d++;
14
- return `${t}-${d}`;
15
- }, Ue = (s) => (t) => ({ ...t, widgets: [...t.widgets, s] }), Ve = (s, t) => (d) => ({ ...d, widgets: d.widgets.map((i) => i.id === s ? { ...i, options: t } : i) }), je = "_root_10mvp_17", We = "_bar_10mvp_34", Ge = "_mode_10mvp_43", Ye = "_head_10mvp_58", Xe = "_editingHead_10mvp_68", Je = "_grip_10mvp_76", Ke = "_title_10mvp_81", qe = "_size_10mvp_92", Qe = "_item_10mvp_100", Ze = "_placeholder_10mvp_110", et = "_grid_10mvp_116", tt = "_headingRow_10mvp_133", nt = "_headingHead_10mvp_138", st = "_heading_10mvp_133", rt = "_body_10mvp_163", ot = "_options_10mvp_189", at = "_catalogue_10mvp_194", dt = "_entry_10mvp_202", it = "_footprint_10mvp_209", ct = "_entryText_10mvp_221", v = {
16
- root: je,
3
+ import { Component as ve, useState as J, useRef as N, useLayoutEffect as de, useMemo as oe, Children as Se, isValidElement as ce, useEffect as te, Suspense as Ce, useSyncExternalStore as le, createElement as ke } from "react";
4
+ import { useWording as ae, FormField as Ne, Input as He, Button as L, Text as K, GripGlyph as ue, Heading as Oe, Card as Ee, Skeleton as Re, Drawer as qe, ModalHeader as Te, ModalBody as De, Popover as Ae, announce as $, Menu as Ie, MenuItem as U, MenuSeparator as se } from "@umriss-ui/core";
5
+ import { jsx as a, jsxs as R, Fragment as re } from "react/jsx-runtime";
6
+ const Le = (s, e) => (i) => {
7
+ const d = i.widgets.findIndex((n) => n.id === s), v = Math.max(0, Math.min(i.widgets.length - 1, e));
8
+ if (d < 0 || d === v) return i;
9
+ const y = [...i.widgets];
10
+ return y.splice(v, 0, ...y.splice(d, 1)), { ...i, widgets: y };
11
+ }, $e = (s, e) => (i) => i.widgets.some((d) => d.id === s && d.size !== e) ? { ...i, widgets: i.widgets.map((d) => d.id === s ? { ...d, size: e } : d) } : i, Fe = (s) => (e) => e.widgets.some((i) => i.id === s) ? { ...e, widgets: e.widgets.filter((i) => i.id !== s) } : e, Be = (s, e) => {
12
+ let i = 1;
13
+ for (; s.widgets.some((d) => d.id === `${e}-${i}`); ) i++;
14
+ return `${e}-${i}`;
15
+ }, Pe = (s) => (e) => ({ ...e, widgets: [...e.widgets, s] }), Ue = (s, e) => (i) => ({ ...i, widgets: i.widgets.map((d) => d.id === s ? { ...d, options: e } : d) }), Ve = "_root_2b0q1_17", We = "_bar_2b0q1_34", je = "_mode_2b0q1_43", Ge = "_head_2b0q1_58", Ye = "_editingHead_2b0q1_77", Xe = "_grip_2b0q1_85", Je = "_title_2b0q1_90", Ke = "_size_2b0q1_102", Qe = "_item_2b0q1_110", Ze = "_placeholder_2b0q1_120", et = "_grid_2b0q1_126", tt = "_headingRow_2b0q1_143", nt = "_headingHead_2b0q1_73", st = "_heading_2b0q1_73", rt = "_body_2b0q1_173", ot = "_options_2b0q1_201", at = "_catalogue_2b0q1_206", it = "_entry_2b0q1_214", dt = "_footprint_2b0q1_221", ct = "_entryText_2b0q1_233", z = {
16
+ root: Ve,
17
17
  bar: We,
18
- mode: Ge,
19
- head: Ye,
20
- editingHead: Xe,
21
- grip: Je,
22
- title: Ke,
23
- size: qe,
18
+ mode: je,
19
+ head: Ge,
20
+ editingHead: Ye,
21
+ grip: Xe,
22
+ title: Je,
23
+ size: Ke,
24
24
  item: Qe,
25
25
  placeholder: Ze,
26
26
  grid: et,
@@ -30,140 +30,140 @@ const $e = (s, t) => (d) => {
30
30
  body: rt,
31
31
  options: ot,
32
32
  catalogue: at,
33
- entry: dt,
34
- footprint: it,
33
+ entry: it,
34
+ footprint: dt,
35
35
  entryText: ct
36
36
  };
37
- class lt extends Me {
37
+ class lt extends ve {
38
38
  state = { failed: !1 };
39
39
  static getDerivedStateFromError() {
40
40
  return { failed: !0 };
41
41
  }
42
- componentDidUpdate(t) {
43
- this.state.failed && (t.size !== this.props.size || t.options !== this.props.options) && this.setState({ failed: !1 });
42
+ componentDidUpdate(e) {
43
+ this.state.failed && (e.size !== this.props.size || e.options !== this.props.options) && this.setState({ failed: !1 });
44
44
  }
45
45
  render() {
46
46
  return this.state.failed ? this.props.fallback : this.props.children;
47
47
  }
48
48
  }
49
- const be = (s) => typeof s == "object" && s !== null && !Array.isArray(s), he = (s) => be(s) && Array.isArray(s.widgets), de = (s) => s.widgets.filter((t) => be(t) && typeof t.id == "string" && typeof t.type == "string"), ut = (s) => ({ ...s, widgets: de(s) }), ht = (s, t) => {
50
- const d = new Set(t.widgets.map((i) => i.id));
51
- return de(s).filter((i) => !d.has(i.id));
49
+ const me = (s) => typeof s == "object" && s !== null && !Array.isArray(s), he = (s) => me(s) && Array.isArray(s.widgets), ie = (s) => s.widgets.filter((e) => me(e) && typeof e.id == "string" && typeof e.type == "string"), ut = (s) => ({ ...s, widgets: ie(s) }), ht = (s, e) => {
50
+ const i = new Set(e.widgets.map((d) => d.id));
51
+ return ie(s).filter((d) => !i.has(d.id));
52
52
  };
53
- function we(s, t) {
54
- const d = /* @__PURE__ */ new Set();
53
+ function we(s, e) {
54
+ const i = /* @__PURE__ */ new Set();
55
55
  return {
56
56
  ...s,
57
- widgets: de(s).flatMap((i) => {
58
- const C = t.get(i.type);
59
- return !C || d.has(i.id) ? [] : (d.add(i.id), [C.sizes[i.size] ? i : { ...i, size: C.start }]);
57
+ widgets: ie(s).flatMap((d) => {
58
+ const v = e.get(d.type);
59
+ return !v || i.has(d.id) ? [] : (i.add(d.id), [v.sizes[d.size] ? d : { ...d, size: v.start }]);
60
60
  })
61
61
  };
62
62
  }
63
- function _e(s, t) {
64
- const d = /* @__PURE__ */ new Set(), i = (x, n, w, c) => {
65
- for (let h = x; h < x + c; h++) for (let m = n; m < n + w; m++) if (d.has(h * t + m)) return !1;
63
+ function ye(s, e) {
64
+ const i = /* @__PURE__ */ new Set(), d = (y, n, h, c) => {
65
+ for (let l = y; l < y + c; l++) for (let b = n; b < n + h; b++) if (i.has(l * e + b)) return !1;
66
66
  return !0;
67
67
  };
68
- let C = 0;
69
- return s.map((x) => {
70
- const n = Math.min(x.columns, t);
71
- for (let w = C; ; w++) {
72
- const c = Math.floor(w / t), h = w % t;
73
- if (!(h + n > t || !i(c, h, n, x.rows))) {
74
- for (let m = c; m < c + x.rows; m++) for (let p = h; p < h + n; p++) d.add(m * t + p);
75
- return C = w, { column: h, row: c, columns: n, rows: x.rows };
68
+ let v = 0;
69
+ return s.map((y) => {
70
+ const n = Math.min(y.columns, e);
71
+ for (let h = v; ; h++) {
72
+ const c = Math.floor(h / e), l = h % e;
73
+ if (!(l + n > e || !d(c, l, n, y.rows))) {
74
+ for (let b = c; b < c + y.rows; b++) for (let p = l; p < l + n; p++) i.add(b * e + p);
75
+ return v = h, { column: l, row: c, columns: n, rows: y.rows };
76
76
  }
77
77
  }
78
78
  });
79
79
  }
80
- function ft(s, t, d, i, C, { cell: x, unit: n, gap: w }) {
81
- let c = d, h = 1 / 0;
82
- for (let m = 0; m < s.length; m++) {
80
+ function ft(s, e, i, d, v, { cell: y, unit: n, gap: h }) {
81
+ let c = i, l = 1 / 0;
82
+ for (let b = 0; b < s.length; b++) {
83
83
  const p = [...s];
84
- p.splice(m, 0, ...p.splice(d, 1));
85
- const D = _e(p, t)[m], N = (D.column * (x + w) - i) ** 2 + (D.row * (n + w) - C) ** 2;
86
- N < h && (h = N, c = m);
84
+ p.splice(b, 0, ...p.splice(i, 1));
85
+ const H = ye(p, e)[b], O = (H.column * (y + h) - d) ** 2 + (H.row * (n + h) - v) ** 2;
86
+ O < l && (l = O, c = b);
87
87
  }
88
88
  return c;
89
89
  }
90
90
  const pt = { 3: "¼", 4: "⅓", 6: "½", 8: "⅔", 9: "¾" }, gt = ({ columns: s }) => s >= 12 ? "" : pt[s] ?? `${s}/12`;
91
- function mt({ grid: s, cards: t, shown: d, drop: i, cancelled: C }) {
92
- const [x, n] = Z(null), w = O(null), c = O({}), h = O(/* @__PURE__ */ new Map()), m = O(null), p = O({ update: () => {
91
+ function bt({ grid: s, cards: e, shown: i, drop: d, cancelled: v }) {
92
+ const [y, n] = J(null), h = N(null), c = N({}), l = N(/* @__PURE__ */ new Map()), b = N(null), p = N({ update: () => {
93
93
  }, finish: () => {
94
- } }), D = () => {
95
- const r = w.current, o = r && t.get(r.id);
96
- if (!r || !m.current || !o) return;
97
- const l = m.current.getBoundingClientRect();
98
- o.style.transform = `translate(${r.x - r.dx - l.left}px, ${r.y - r.dy - l.top}px)`;
94
+ } }), H = () => {
95
+ const r = h.current, o = r && e.get(r.id);
96
+ if (!r || !b.current || !o) return;
97
+ const u = b.current.getBoundingClientRect();
98
+ o.style.transform = `translate(${r.x - r.dx - u.left}px, ${r.y - r.dy - u.top}px)`;
99
99
  };
100
- ie(() => {
100
+ de(() => {
101
101
  const r = /* @__PURE__ */ new Map(), o = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
102
- for (const [l, g] of t) {
103
- const z = h.current.get(l);
104
- r.set(l, [g.offsetLeft, g.offsetTop]), x && !o && z && l !== x.id && (z[0] !== g.offsetLeft || z[1] !== g.offsetTop) && g.animate([{ transform: `translate(${z[0] - g.offsetLeft}px, ${z[1] - g.offsetTop}px)` }, { transform: "none" }], { duration: 150, easing: "ease-out" });
102
+ for (const [u, m] of e) {
103
+ const S = l.current.get(u);
104
+ r.set(u, [m.offsetLeft, m.offsetTop]), y && !o && S && u !== y.id && (S[0] !== m.offsetLeft || S[1] !== m.offsetTop) && m.animate([{ transform: `translate(${S[0] - m.offsetLeft}px, ${S[1] - m.offsetTop}px)` }, { transform: "none" }], { duration: 150, easing: "ease-out" });
105
105
  }
106
- h.current = r, x && D();
106
+ l.current = r, y && H();
107
107
  });
108
- const N = () => {
109
- const r = w.current, o = s.current, l = r && t.get(r.id);
110
- if (!r || !o || !l) return;
111
- const g = getComputedStyle(o), z = parseFloat(g.columnGap), B = g.gridTemplateColumns.split(" ").length, R = o.getBoundingClientRect(), T = d[r.from].span, Y = { cell: (R.width - (B - 1) * z) / B, gap: z, unit: (l.offsetHeight - (T.rows - 1) * z) / T.rows }, L = ft(d.map((W) => W.span), B, r.from, r.x - r.dx - R.left, r.y - r.dy - R.top, Y);
112
- if (L === r.target) return D();
113
- r.target = L, n({ id: r.id, target: L });
114
- }, G = (r) => {
115
- const o = w.current;
116
- if (w.current = null, window.clearInterval(c.current.timer), c.current.escape && document.removeEventListener("keydown", c.current.escape), o && t.get(o.id)?.style.removeProperty("transform"), n(null), !!o?.started) {
117
- if (!r) return C(d[o.from].title);
118
- i(o.id, d[o.target].id);
108
+ const O = () => {
109
+ const r = h.current, o = s.current, u = r && e.get(r.id);
110
+ if (!r || !o || !u) return;
111
+ const m = getComputedStyle(o), S = parseFloat(m.columnGap), B = m.gridTemplateColumns.split(" ").length, q = o.getBoundingClientRect(), T = i[r.from].span, G = { cell: (q.width - (B - 1) * S) / B, gap: S, unit: (u.offsetHeight - (T.rows - 1) * S) / T.rows }, I = ft(i.map((W) => W.span), B, r.from, r.x - r.dx - q.left, r.y - r.dy - q.top, G);
112
+ if (I === r.target) return H();
113
+ r.target = I, n({ id: r.id, target: I });
114
+ }, j = (r) => {
115
+ const o = h.current;
116
+ if (h.current = null, window.clearInterval(c.current.timer), c.current.escape && document.removeEventListener("keydown", c.current.escape), o && e.get(o.id)?.style.removeProperty("transform"), n(null), !!o?.started) {
117
+ if (!r) return v(i[o.from].title);
118
+ d(o.id, i[o.target].id);
119
119
  }
120
120
  };
121
- ie(() => {
122
- p.current = { update: N, finish: G };
121
+ de(() => {
122
+ p.current = { update: O, finish: j };
123
123
  });
124
- const j = (r, o) => (l) => {
125
- const g = l.target;
126
- if (l.button > 0 || g.closest("button") || !l.currentTarget.contains(g)) return;
127
- const z = t.get(r).getBoundingClientRect();
128
- w.current = { id: r, from: o, x0: l.clientX, y0: l.clientY, x: l.clientX, y: l.clientY, dx: l.clientX - z.left, dy: l.clientY - z.top, started: !1, target: o }, l.currentTarget.setPointerCapture(l.pointerId);
129
- }, P = (r) => {
130
- const o = w.current;
124
+ const V = (r, o) => (u) => {
125
+ const m = u.target;
126
+ if (u.button > 0 || m.closest("button") || !u.currentTarget.contains(m)) return;
127
+ const S = e.get(r).getBoundingClientRect();
128
+ h.current = { id: r, from: o, x0: u.clientX, y0: u.clientY, x: u.clientX, y: u.clientY, dx: u.clientX - S.left, dy: u.clientY - S.top, started: !1, target: o }, u.currentTarget.setPointerCapture(u.pointerId);
129
+ }, F = (r) => {
130
+ const o = h.current;
131
131
  if (o) {
132
132
  if (o.x = r.clientX, o.y = r.clientY, !o.started) {
133
133
  if (Math.hypot(o.x - o.x0, o.y - o.y0) <= 4) return;
134
- o.started = !0, n({ id: o.id, target: o.from }), c.current.escape = (l) => l.key === "Escape" && p.current.finish(!1), document.addEventListener("keydown", c.current.escape), c.current.timer = window.setInterval(() => {
135
- const g = w.current?.y ?? window.innerHeight / 2, z = Math.ceil((64 - Math.min(g, window.innerHeight - g)) / 4);
136
- z <= 0 || (window.scrollBy(0, g < window.innerHeight / 2 ? -z : z), p.current.update());
134
+ o.started = !0, n({ id: o.id, target: o.from }), c.current.escape = (u) => u.key === "Escape" && p.current.finish(!1), document.addEventListener("keydown", c.current.escape), c.current.timer = window.setInterval(() => {
135
+ const m = h.current?.y ?? window.innerHeight / 2, S = Math.ceil((64 - Math.min(m, window.innerHeight - m)) / 4);
136
+ S <= 0 || (window.scrollBy(0, m < window.innerHeight / 2 ? -S : S), p.current.update());
137
137
  }, 16);
138
138
  }
139
139
  p.current.update();
140
140
  }
141
141
  };
142
142
  return {
143
- drag: x,
143
+ drag: y,
144
144
  /** To go on the placeholder. */
145
- placeholder: m,
145
+ placeholder: b,
146
146
  /** The pointer handlers of a widget's head. */
147
147
  handlers: (r, o) => ({
148
- onPointerDown: j(r, o),
149
- onPointerMove: P,
148
+ onPointerDown: V(r, o),
149
+ onPointerMove: F,
150
150
  onPointerUp: () => p.current.finish(!0),
151
151
  onPointerCancel: () => p.current.finish(!1)
152
152
  })
153
153
  };
154
154
  }
155
- function ye(s) {
155
+ function _e(s) {
156
156
  return null;
157
157
  }
158
- function bt() {
158
+ function mt() {
159
159
  return null;
160
160
  }
161
- const wt = "heading", q = (s, t) => s.title?.(t) ?? s.label;
162
- function _t({ options: s, change: t }) {
163
- const d = ae();
164
- return /* @__PURE__ */ a(Ne, { label: d.dashboardHeadingField, children: /* @__PURE__ */ a(He, { value: s.text, onChange: (i) => t({ text: i.target.value }) }) });
161
+ const wt = "heading", Q = (s, e) => s.title?.(e) ?? s.label;
162
+ function yt({ options: s, change: e }) {
163
+ const i = ae();
164
+ return /* @__PURE__ */ a(Ne, { label: i.dashboardHeadingField, children: /* @__PURE__ */ a(He, { value: s.text, onChange: (d) => e({ text: d.target.value }) }) });
165
165
  }
166
- function yt(s) {
166
+ function _t(s) {
167
167
  return {
168
168
  name: wt,
169
169
  label: s.dashboardHeadingLabel,
@@ -171,98 +171,116 @@ function yt(s) {
171
171
  sizes: { full: { columns: 12, rows: 1 } },
172
172
  start: "full",
173
173
  defaultOptions: { text: s.dashboardHeadingText },
174
- title: (t) => t.text || s.dashboardHeadingLabel,
175
- form: _t,
174
+ title: (e) => e.text || s.dashboardHeadingLabel,
175
+ form: yt,
176
176
  children: () => null
177
177
  };
178
178
  }
179
- const Q = (...s) => s.filter(Boolean).join(" "), fe = 12;
180
- function vt({ render: s, ...t }) {
181
- return s(t);
179
+ const Z = (...s) => s.filter(Boolean).join(" "), fe = 12;
180
+ function xt({ render: s, ...e }) {
181
+ return s(e);
182
182
  }
183
- const pe = (s, t) => ({ "--_dashboard-column": s.column + 1, "--_dashboard-row": s.row + 1, "--_dashboard-columns": s.columns, "--_dashboard-rows": s.rows, "--_dashboard-order": t }), ge = (s) => s.columns * s.rows, ee = (s, t) => (d) => {
184
- d ? s.set(t, d) : s.delete(t);
183
+ const pe = (s, e) => ({ "--_dashboard-column": s.column + 1, "--_dashboard-row": s.row + 1, "--_dashboard-columns": s.columns, "--_dashboard-rows": s.rows, "--_dashboard-order": e }), ge = (s) => s.columns * s.rows;
184
+ function zt({ title: s, editing: e, children: i }) {
185
+ const d = N(null), [v, y] = J(!1);
186
+ te(() => {
187
+ const h = d.current;
188
+ if (!h) return;
189
+ const c = () => y(h.scrollHeight > h.clientHeight || h.scrollWidth > h.clientWidth), l = new ResizeObserver(c), b = () => {
190
+ l.disconnect(), l.observe(h);
191
+ for (const H of h.children) l.observe(H);
192
+ c();
193
+ }, p = new MutationObserver(b);
194
+ return p.observe(h, { childList: !0 }), b(), () => {
195
+ l.disconnect(), p.disconnect();
196
+ };
197
+ }, []);
198
+ const n = v && !e;
199
+ return /* @__PURE__ */ a("div", { ref: d, className: z.body, inert: e, ...n ? { role: "group", tabIndex: 0, "aria-label": s } : {}, children: i });
200
+ }
201
+ const ee = (s, e) => (i) => {
202
+ i ? s.set(e, i) : s.delete(e);
185
203
  };
186
- function xt({ view: s, editor: t, className: d, children: i, catalogue: C, ...x }) {
187
- const n = ae(), w = oe(() => yt(n), [n]), c = /* @__PURE__ */ new Map();
188
- for (const e of Ce.toArray(i))
189
- ce(e) && e.type === ye ? c.set(e.props.name, e.props) : ce(e) && e.type === bt && c.set(w.name, w);
190
- const { register: h } = t;
191
- ne(() => h(c));
192
- const m = we(s, c), p = m.widgets.flatMap((e) => {
193
- const u = c.get(e.type), y = u?.sizes[e.size];
194
- return u && y ? [{ widget: e, type: u, span: y }] : [];
195
- }), D = O(null), [N] = Z(() => /* @__PURE__ */ new Map()), [G] = Z(() => /* @__PURE__ */ new Map()), [j] = Z(() => /* @__PURE__ */ new Map()), P = O(null), { editing: r } = t, o = m.widgets.length, l = (e, u, y) => {
196
- const S = m.widgets.findIndex((I) => I.id === e), M = Math.max(0, Math.min(o - 1, y));
197
- S !== M && (t.step($e(e, M)), F(n.dashboardMoved(u, S + 1, M + 1, o)));
198
- }, { drag: g, placeholder: z, handlers: B } = mt({
199
- grid: D,
200
- cards: N,
201
- shown: p.map(({ widget: e, type: u, span: y }) => ({ id: e.id, span: y, title: q(u, e.options) })),
202
- drop: (e, u) => {
203
- const y = p.find((S) => S.widget.id === e);
204
- l(e, q(y.type, y.widget.options), m.widgets.findIndex((S) => S.id === u));
204
+ function Mt({ view: s, editor: e, className: i, children: d, catalogue: v, ...y }) {
205
+ const n = ae(), h = oe(() => _t(n), [n]), c = /* @__PURE__ */ new Map();
206
+ for (const t of Se.toArray(d))
207
+ ce(t) && t.type === _e ? c.set(t.props.name, t.props) : ce(t) && t.type === mt && c.set(h.name, h);
208
+ const { register: l } = e;
209
+ te(() => l(c));
210
+ const b = we(s, c), p = b.widgets.flatMap((t) => {
211
+ const f = c.get(t.type), x = f?.sizes[t.size];
212
+ return f && x ? [{ widget: t, type: f, span: x }] : [];
213
+ }), H = N(null), [O] = J(() => /* @__PURE__ */ new Map()), [j] = J(() => /* @__PURE__ */ new Map()), [V] = J(() => /* @__PURE__ */ new Map()), F = N(null), { editing: r } = e, o = b.widgets.length, u = (t, f, x) => {
214
+ const C = b.widgets.findIndex((A) => A.id === t), M = Math.max(0, Math.min(o - 1, x));
215
+ C !== M && (e.step(Le(t, M)), $(n.dashboardMoved(f, C + 1, M + 1, o)));
216
+ }, { drag: m, placeholder: S, handlers: B } = bt({
217
+ grid: H,
218
+ cards: O,
219
+ shown: p.map(({ widget: t, type: f, span: x }) => ({ id: t.id, span: x, title: Q(f, t.options) })),
220
+ drop: (t, f) => {
221
+ const x = p.find((C) => C.widget.id === t);
222
+ u(t, Q(x.type, x.widget.options), b.widgets.findIndex((C) => C.id === f));
205
223
  },
206
- cancelled: (e) => F(n.dashboardDragCancelled(e))
207
- }), R = g ? p.findIndex((e) => e.widget.id === g.id) : -1, T = p.map((e, u) => u);
208
- R >= 0 && T.splice(g.target, 0, ...T.splice(R, 1));
209
- const Y = _e(T.map((e) => p[e].span), fe), L = p.map((e, u) => Y[T.indexOf(u)]), [W, b] = Z(null), f = O(null), _ = O(null);
210
- ne(() => {
211
- _.current && (N.get(_.current)?.scrollIntoView?.({ block: "nearest" }), _.current = null);
224
+ cancelled: (t) => $(n.dashboardDragCancelled(t))
225
+ }), q = m ? p.findIndex((t) => t.widget.id === m.id) : -1, T = p.map((t, f) => f);
226
+ q >= 0 && T.splice(m.target, 0, ...T.splice(q, 1));
227
+ const G = ye(T.map((t) => p[t].span), fe), I = p.map((t, f) => G[T.indexOf(f)]), [W, w] = J(null), g = N(null), _ = N(null);
228
+ te(() => {
229
+ _.current && (O.get(_.current)?.scrollIntoView?.({ block: "nearest" }), _.current = null);
212
230
  });
213
- const k = O(!1);
214
- ne(() => {
215
- k.current !== !1 && ((k.current === null ? P.current : G.get(k.current))?.focus(), k.current = !1);
231
+ const k = N(!1);
232
+ te(() => {
233
+ k.current !== !1 && ((k.current === null ? F.current : j.get(k.current))?.focus(), k.current = !1);
216
234
  });
217
- const U = [...c.values()].filter((e) => !C || C.includes(e.name)), X = (e) => {
218
- const u = { id: Be(m, e.name), type: e.name, size: e.start, options: e.defaultOptions };
219
- _.current = u.id, t.step(Ue(u)), F(n.dashboardAdded(q(e, u.options), o + 1, o + 1));
220
- }, A = W ? c.get(m.widgets.find((e) => e.id === W)?.type ?? "") : void 0, te = m.widgets.find((e) => e.id === W), ve = (e, u, y, S) => r ? {
221
- ref: ee(G, e.id),
222
- className: Q(v.head, v.editingHead),
223
- ...B(e.id, y),
235
+ const P = [...c.values()].filter((t) => !v || v.includes(t.name)), Y = (t) => {
236
+ const f = { id: Be(b, t.name), type: t.name, size: t.start, options: t.defaultOptions };
237
+ _.current = f.id, e.step(Pe(f)), $(n.dashboardAdded(Q(t, f.options), o + 1, o + 1));
238
+ }, D = W ? c.get(b.widgets.find((t) => t.id === W)?.type ?? "") : void 0, ne = b.widgets.find((t) => t.id === W), xe = (t, f, x, C) => r ? {
239
+ ref: ee(j, t.id),
240
+ className: Z(z.head, z.editingHead),
241
+ ...B(t.id, x),
224
242
  role: "group",
225
243
  tabIndex: 0,
226
- "aria-label": n.dashboardHead(u, S + 1, o),
244
+ "aria-label": n.dashboardHead(f, C + 1, o),
227
245
  onKeyDown: (M) => {
228
246
  if (M.target !== M.currentTarget) return;
229
- const I = { ArrowLeft: S - 1, ArrowUp: S - 1, ArrowRight: S + 1, ArrowDown: S + 1, Home: 0, End: o - 1 }[M.key];
230
- I !== void 0 && (M.preventDefault(), l(e.id, u, I));
247
+ const A = { ArrowLeft: C - 1, ArrowUp: C - 1, ArrowRight: C + 1, ArrowDown: C + 1, Home: 0, End: o - 1 }[M.key];
248
+ A !== void 0 && (M.preventDefault(), u(t.id, f, A));
231
249
  }
232
- } : { className: v.head }, xe = (e, u, y, S, M) => {
233
- const I = Object.entries(u.sizes).sort(([, H], [, J]) => ge(H) - ge(J));
234
- return /* @__PURE__ */ E(
235
- Le,
250
+ } : { className: z.head }, ze = (t, f, x, C, M) => {
251
+ const A = Object.entries(f.sizes).sort(([, E], [, X]) => ge(E) - ge(X));
252
+ return /* @__PURE__ */ R(
253
+ Ie,
236
254
  {
237
255
  align: "end",
238
- trigger: /* @__PURE__ */ a($, { ref: ee(j, e.id), size: "sm", variant: "ghost", "aria-label": n.dashboardMenu(y), children: /* @__PURE__ */ a("svg", { viewBox: "0 0 10 10", width: "10", height: "10", "aria-hidden": "true", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", children: /* @__PURE__ */ a("path", { d: "M1.5 5h.01M5 5h.01M8.5 5h.01" }) }) }),
256
+ trigger: /* @__PURE__ */ a(L, { ref: ee(V, t.id), size: "sm", variant: "ghost", "aria-label": n.dashboardMenu(x), children: /* @__PURE__ */ a("svg", { viewBox: "0 0 10 10", width: "10", height: "10", "aria-hidden": "true", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", children: /* @__PURE__ */ a("path", { d: "M1.5 5h.01M5 5h.01M8.5 5h.01" }) }) }),
239
257
  children: [
240
- /* @__PURE__ */ a(V, { disabled: !0, children: n.dashboardPlace(M + 1, o) }),
241
- /* @__PURE__ */ a(V, { disabled: M === 0, onSelect: () => l(e.id, y, 0), children: n.dashboardMoveStart }),
242
- /* @__PURE__ */ a(V, { disabled: M === 0, onSelect: () => l(e.id, y, M - 1), children: n.dashboardMoveEarlier }),
243
- /* @__PURE__ */ a(V, { disabled: M === o - 1, onSelect: () => l(e.id, y, M + 1), children: n.dashboardMoveLater }),
244
- /* @__PURE__ */ a(V, { disabled: M === o - 1, onSelect: () => l(e.id, y, o - 1), children: n.dashboardMoveEnd }),
245
- I.length > 1 && /* @__PURE__ */ E(re, { children: [
258
+ /* @__PURE__ */ a(U, { disabled: !0, children: n.dashboardPlace(M + 1, o) }),
259
+ /* @__PURE__ */ a(U, { disabled: M === 0, onSelect: () => u(t.id, x, 0), children: n.dashboardMoveStart }),
260
+ /* @__PURE__ */ a(U, { disabled: M === 0, onSelect: () => u(t.id, x, M - 1), children: n.dashboardMoveEarlier }),
261
+ /* @__PURE__ */ a(U, { disabled: M === o - 1, onSelect: () => u(t.id, x, M + 1), children: n.dashboardMoveLater }),
262
+ /* @__PURE__ */ a(U, { disabled: M === o - 1, onSelect: () => u(t.id, x, o - 1), children: n.dashboardMoveEnd }),
263
+ A.length > 1 && /* @__PURE__ */ R(re, { children: [
246
264
  /* @__PURE__ */ a(se, {}),
247
- I.map(([H, J]) => /* @__PURE__ */ a(
248
- V,
265
+ A.map(([E, X]) => /* @__PURE__ */ a(
266
+ U,
249
267
  {
250
- "aria-current": H === e.size || void 0,
268
+ "aria-current": E === t.size || void 0,
251
269
  onSelect: () => {
252
- t.step(Fe(e.id, H)), F(n.dashboardResized(y, n.dashboardSizes[H]));
270
+ e.step($e(t.id, E)), $(n.dashboardResized(x, n.dashboardSizes[E]));
253
271
  },
254
- children: n.dashboardSizeEntry(n.dashboardSizes[H], gt(J), H === e.size)
272
+ children: n.dashboardSizeEntry(n.dashboardSizes[E], gt(X), E === t.size)
255
273
  },
256
- H
274
+ E
257
275
  ))
258
276
  ] }),
259
- u.form && /* @__PURE__ */ E(re, { children: [
277
+ f.form && /* @__PURE__ */ R(re, { children: [
260
278
  /* @__PURE__ */ a(se, {}),
261
279
  /* @__PURE__ */ a(
262
- V,
280
+ U,
263
281
  {
264
282
  onSelect: () => {
265
- f.current = j.get(e.id) ?? null, b(e.id);
283
+ g.current = V.get(t.id) ?? null, w(t.id);
266
284
  },
267
285
  children: n.dashboardOptions
268
286
  }
@@ -270,11 +288,11 @@ function xt({ view: s, editor: t, className: d, children: i, catalogue: C, ...x
270
288
  ] }),
271
289
  /* @__PURE__ */ a(se, {}),
272
290
  /* @__PURE__ */ a(
273
- V,
291
+ U,
274
292
  {
275
293
  tone: "danger",
276
294
  onSelect: () => {
277
- k.current = (p[S + 1] ?? p[S - 1])?.widget.id ?? null, t.step(Pe(e.id)), F(n.dashboardRemoved(y));
295
+ k.current = (p[C + 1] ?? p[C - 1])?.widget.id ?? null, e.step(Fe(t.id)), $(n.dashboardRemoved(x));
278
296
  },
279
297
  children: n.dashboardRemove
280
298
  }
@@ -283,148 +301,148 @@ function xt({ view: s, editor: t, className: d, children: i, catalogue: C, ...x
283
301
  }
284
302
  );
285
303
  };
286
- return /* @__PURE__ */ E("div", { role: "group", className: Q(v.root, d), "data-editing": r || void 0, ...x, children: [
287
- t.allowed && /* @__PURE__ */ a("div", { className: v.bar, children: r ? /* @__PURE__ */ E(re, { children: [
288
- /* @__PURE__ */ a("span", { className: v.mode, children: n.dashboardEditing }),
289
- /* @__PURE__ */ a($, { size: "sm", disabled: !t.canUndo, onClick: t.undo, children: n.dashboardUndo }),
290
- /* @__PURE__ */ a($, { size: "sm", disabled: !t.canRedo, onClick: t.redo, children: n.dashboardRedo }),
291
- /* @__PURE__ */ a($, { size: "sm", disabled: !t.canReset, onClick: t.reset, children: n.dashboardReset }),
292
- /* @__PURE__ */ a($, { ref: P, size: "sm", onClick: () => t.setCatalogue(!0), children: n.dashboardAddWidget }),
293
- /* @__PURE__ */ a($, { size: "sm", onClick: t.cancel, children: n.dashboardCancel }),
294
- /* @__PURE__ */ a($, { size: "sm", variant: "primary", onClick: t.done, children: n.dashboardDone })
295
- ] }) : /* @__PURE__ */ a($, { size: "sm", onClick: t.edit, children: n.dashboardEdit }) }),
304
+ return /* @__PURE__ */ R("div", { role: "group", className: Z(z.root, i), "data-editing": r || void 0, ...y, children: [
305
+ e.allowed && /* @__PURE__ */ a("div", { className: z.bar, children: r ? /* @__PURE__ */ R(re, { children: [
306
+ /* @__PURE__ */ a("span", { className: z.mode, children: n.dashboardEditing }),
307
+ /* @__PURE__ */ a(L, { size: "sm", disabled: !e.canUndo, onClick: e.undo, children: n.dashboardUndo }),
308
+ /* @__PURE__ */ a(L, { size: "sm", disabled: !e.canRedo, onClick: e.redo, children: n.dashboardRedo }),
309
+ /* @__PURE__ */ a(L, { size: "sm", disabled: !e.canReset, onClick: e.reset, children: n.dashboardReset }),
310
+ /* @__PURE__ */ a(L, { ref: F, size: "sm", onClick: () => e.setCatalogue(!0), children: n.dashboardAddWidget }),
311
+ /* @__PURE__ */ a(L, { size: "sm", onClick: e.cancel, children: n.dashboardCancel }),
312
+ /* @__PURE__ */ a(L, { size: "sm", variant: "primary", onClick: e.done, children: n.dashboardDone })
313
+ ] }) : /* @__PURE__ */ a(L, { size: "sm", onClick: e.edit, children: n.dashboardEdit }) }),
296
314
  r && o === 0 && /* @__PURE__ */ a(K, { children: n.dashboardEmptyEditing }),
297
315
  !r && o === 0 && /* @__PURE__ */ a(K, { children: n.dashboardEmpty }),
298
- /* @__PURE__ */ E("div", { className: v.grid, ref: D, children: [
299
- p.map(({ widget: e, type: u }, y) => {
300
- const S = pe(L[y], T.indexOf(y)), M = q(u, e.options), I = m.widgets.findIndex((ze) => ze.id === e.id), H = ve(e, M, y, I), J = r && xe(e, u, M, y, I);
301
- return u === w ? /* @__PURE__ */ a("div", { ref: ee(N, e.id), className: Q(v.item, v.headingRow), style: S, "data-widget": e.id, "data-editing": r || void 0, "data-dragging": g?.id === e.id || void 0, children: /* @__PURE__ */ E("div", { ...H, className: Q(H.className, v.headingHead), children: [
302
- r && /* @__PURE__ */ a(ue, { className: v.grip }),
303
- /* @__PURE__ */ a(Ee, { level: 2, size: "lg", className: v.heading, children: e.options.text }),
304
- J
305
- ] }) }, e.id) : /* @__PURE__ */ E(Oe, { ref: ee(N, e.id), className: v.item, style: S, "data-widget": e.id, "data-editing": r || void 0, "data-dragging": g?.id === e.id || void 0, "aria-label": M, children: [
306
- /* @__PURE__ */ E("div", { ...H, children: [
307
- r && /* @__PURE__ */ a(ue, { className: v.grip }),
308
- /* @__PURE__ */ a("h2", { className: v.title, children: M }),
309
- r && /* @__PURE__ */ a("span", { className: v.size, children: n.dashboardSizes[e.size] }),
310
- J
316
+ /* @__PURE__ */ R("div", { className: z.grid, ref: H, children: [
317
+ p.map(({ widget: t, type: f }, x) => {
318
+ const C = pe(I[x], T.indexOf(x)), M = Q(f, t.options), A = b.widgets.findIndex((Me) => Me.id === t.id), E = xe(t, M, x, A), X = r && ze(t, f, M, x, A);
319
+ return f === h ? /* @__PURE__ */ a("div", { ref: ee(O, t.id), className: Z(z.item, z.headingRow), style: C, "data-widget": t.id, "data-editing": r || void 0, "data-dragging": m?.id === t.id || void 0, children: /* @__PURE__ */ R("div", { ...E, className: Z(E.className, z.headingHead), children: [
320
+ r && /* @__PURE__ */ a(ue, { className: z.grip }),
321
+ /* @__PURE__ */ a(Oe, { level: 2, size: "lg", className: z.heading, children: t.options.text }),
322
+ X
323
+ ] }) }, t.id) : /* @__PURE__ */ R(Ee, { ref: ee(O, t.id), className: z.item, style: C, "data-widget": t.id, "data-editing": r || void 0, "data-dragging": m?.id === t.id || void 0, "aria-label": M, children: [
324
+ /* @__PURE__ */ R("div", { ...E, children: [
325
+ r && /* @__PURE__ */ a(ue, { className: z.grip }),
326
+ /* @__PURE__ */ a("h2", { className: z.title, children: M }),
327
+ r && /* @__PURE__ */ a("span", { className: z.size, children: n.dashboardSizes[t.size] }),
328
+ X
311
329
  ] }),
312
- /* @__PURE__ */ a("div", { className: v.body, inert: r, children: /* @__PURE__ */ a(lt, { size: e.size, options: e.options, fallback: /* @__PURE__ */ a(K, { tone: "secondary", children: n.dashboardFailed }), children: /* @__PURE__ */ a(Se, { fallback: /* @__PURE__ */ a(Re, { width: "100%", height: "100%" }), children: /* @__PURE__ */ a(vt, { render: u.children, size: e.size, options: e.options }) }) }) })
313
- ] }, e.id);
330
+ /* @__PURE__ */ a(zt, { title: M, editing: r, children: /* @__PURE__ */ a(lt, { size: t.size, options: t.options, fallback: /* @__PURE__ */ a(K, { tone: "secondary", children: n.dashboardFailed }), children: /* @__PURE__ */ a(Ce, { fallback: /* @__PURE__ */ a(Re, { width: "100%", height: "100%" }), children: /* @__PURE__ */ a(xt, { render: f.children, size: t.size, options: t.options }) }) }) })
331
+ ] }, t.id);
314
332
  }),
315
- R >= 0 && /* @__PURE__ */ a("div", { ref: z, "aria-hidden": "true", "data-placeholder": !0, className: Q(v.item, v.placeholder), style: pe(L[R], g.target) })
333
+ q >= 0 && /* @__PURE__ */ a("div", { ref: S, "aria-hidden": "true", "data-placeholder": !0, className: Z(z.item, z.placeholder), style: pe(I[q], m.target) })
316
334
  ] }),
317
- /* @__PURE__ */ E(Te, { open: t.catalogue, onClose: () => t.setCatalogue(!1), children: [
318
- /* @__PURE__ */ a(De, { title: n.dashboardCatalogue, description: n.dashboardCatalogueSentence }),
319
- /* @__PURE__ */ a(Ae, { children: /* @__PURE__ */ a("ul", { className: v.catalogue, children: U.map((e) => {
320
- const u = e.sizes[e.start];
321
- return /* @__PURE__ */ E("li", { className: v.entry, children: [
322
- /* @__PURE__ */ a("div", { className: v.footprint, role: "img", "aria-label": n.dashboardFootprint(u.columns, u.rows), children: /* @__PURE__ */ a("span", { style: { width: `${u.columns / fe * 100}%`, height: `${Math.min(1, u.rows / 4) * 100}%` } }) }),
323
- /* @__PURE__ */ E("div", { className: v.entryText, children: [
324
- /* @__PURE__ */ a(K, { as: "strong", weight: "semibold", children: e.label }),
325
- /* @__PURE__ */ a(K, { as: "span", size: "sm", tone: "secondary", children: e.sentence })
335
+ /* @__PURE__ */ R(qe, { open: e.catalogue, onClose: () => e.setCatalogue(!1), children: [
336
+ /* @__PURE__ */ a(Te, { title: n.dashboardCatalogue, description: n.dashboardCatalogueSentence }),
337
+ /* @__PURE__ */ a(De, { children: /* @__PURE__ */ a("ul", { className: z.catalogue, children: P.map((t) => {
338
+ const f = t.sizes[t.start];
339
+ return /* @__PURE__ */ R("li", { className: z.entry, children: [
340
+ /* @__PURE__ */ a("div", { className: z.footprint, role: "img", "aria-label": n.dashboardFootprint(f.columns, f.rows), children: /* @__PURE__ */ a("span", { style: { width: `${f.columns / fe * 100}%`, height: `${Math.min(1, f.rows / 4) * 100}%` } }) }),
341
+ /* @__PURE__ */ R("div", { className: z.entryText, children: [
342
+ /* @__PURE__ */ a(K, { as: "strong", weight: "semibold", children: t.label }),
343
+ /* @__PURE__ */ a(K, { as: "span", size: "sm", tone: "secondary", children: t.sentence })
326
344
  ] }),
327
- /* @__PURE__ */ a($, { size: "sm", "aria-label": n.dashboardAddType(e.label), onClick: () => X(e), children: n.dashboardAdd })
328
- ] }, e.name);
345
+ /* @__PURE__ */ a(L, { size: "sm", "aria-label": n.dashboardAddType(t.label), onClick: () => Y(t), children: n.dashboardAdd })
346
+ ] }, t.name);
329
347
  }) }) })
330
348
  ] }),
331
- A?.form && te && /* @__PURE__ */ a(Ie, { open: !0, anchorRef: f, align: "end", onOpenChange: () => b(null), role: "dialog", ariaLabel: n.dashboardOptions, children: /* @__PURE__ */ a("div", { className: v.options, children: A.form({
332
- options: te.options,
333
- change: (e) => {
334
- t.step(Ve(te.id, e)), F(n.dashboardOptionsChanged(q(A, e)));
349
+ D?.form && ne && /* @__PURE__ */ a(Ae, { open: !0, anchorRef: g, align: "end", onOpenChange: () => w(null), role: "dialog", "aria-label": n.dashboardOptions, children: /* @__PURE__ */ a("div", { className: z.options, children: D.form({
350
+ options: ne.options,
351
+ change: (t) => {
352
+ e.step(Ue(ne.id, t)), $(n.dashboardOptionsChanged(Q(D, t)));
335
353
  }
336
354
  }) }) })
337
355
  ] });
338
356
  }
339
- const me = (s, t) => JSON.stringify(s) === JSON.stringify(t);
340
- function St({ defaultView: s, initialView: t, allowEdit: d = !0, onViewChange: i, onDraftChange: C }) {
341
- const x = O(null);
342
- x.current ??= { state: { view: he(t) ? t : s, draft: null, known: 0 }, listeners: /* @__PURE__ */ new Set(), types: /* @__PURE__ */ new Map(), signature: "" };
343
- const n = x.current, w = ae(), c = O({ defaultView: s, allowEdit: d, onViewChange: i, onDraftChange: C, wording: w });
344
- c.current = { defaultView: s, allowEdit: d, onViewChange: i, onDraftChange: C, wording: w };
345
- const h = oe(() => {
346
- const j = (b) => (n.listeners.add(b), () => void n.listeners.delete(b)), P = () => n.state, r = (b) => {
347
- n.state = b, n.listeners.forEach((f) => f());
348
- }, o = (b) => n.types.size ? we(b, n.types) : ut(b), l = () => {
357
+ const be = (s, e) => JSON.stringify(s) === JSON.stringify(e);
358
+ function kt({ defaultView: s, initialView: e, allowEdit: i = !0, onViewChange: d, onDraftChange: v }) {
359
+ const y = N(null);
360
+ y.current ??= { state: { view: he(e) ? e : s, draft: null, known: 0 }, listeners: /* @__PURE__ */ new Set(), types: /* @__PURE__ */ new Map(), signature: "" };
361
+ const n = y.current, h = ae(), c = N({ defaultView: s, allowEdit: i, onViewChange: d, onDraftChange: v, wording: h });
362
+ c.current = { defaultView: s, allowEdit: i, onViewChange: d, onDraftChange: v, wording: h };
363
+ const l = oe(() => {
364
+ const V = (w) => (n.listeners.add(w), () => void n.listeners.delete(w)), F = () => n.state, r = (w) => {
365
+ n.state = w, n.listeners.forEach((g) => g());
366
+ }, o = (w) => n.types.size ? we(w, n.types) : ut(w), u = () => {
349
367
  !c.current.allowEdit || n.state.draft || (r({ ...n.state, draft: { steps: [o(n.state.view)], at: 0 } }), c.current.onDraftChange?.(!0));
350
- }, g = (b) => {
351
- const { view: f, draft: _ } = n.state;
368
+ }, m = (w) => {
369
+ const { view: g, draft: _ } = n.state;
352
370
  if (!_) return;
353
- const k = _.steps[_.at], U = b && !me(k, o(f)), X = { ...k, widgets: [...k.widgets, ...ht(f, _.steps[0])] };
354
- r({ ...n.state, view: U ? X : f, draft: null, catalogue: !1 }), c.current.onDraftChange?.(!1);
355
- const { wording: A } = c.current;
356
- F(b ? U ? A.dashboardKept : A.dashboardLeft : A.dashboardCancelled), U && c.current.onViewChange?.(X);
357
- }, z = (b) => {
358
- n.state.draft && r({ ...n.state, catalogue: b });
359
- }, B = (b) => {
360
- const { draft: f } = n.state;
361
- if (!f) return;
362
- const _ = f.steps[f.at], k = b(_);
363
- k !== _ && r({ ...n.state, draft: { steps: [...f.steps.slice(0, f.at + 1), k], at: f.at + 1 } });
364
- }, R = (b, f) => {
371
+ const k = _.steps[_.at], P = w && !be(k, o(g)), Y = { ...k, widgets: [...k.widgets, ...ht(g, _.steps[0])] };
372
+ r({ ...n.state, view: P ? Y : g, draft: null, catalogue: !1 }), c.current.onDraftChange?.(!1);
373
+ const { wording: D } = c.current;
374
+ $(w ? P ? D.dashboardKept : D.dashboardLeft : D.dashboardCancelled), P && c.current.onViewChange?.(Y);
375
+ }, S = (w) => {
376
+ n.state.draft && r({ ...n.state, catalogue: w });
377
+ }, B = (w) => {
378
+ const { draft: g } = n.state;
379
+ if (!g) return;
380
+ const _ = g.steps[g.at], k = w(_);
381
+ k !== _ && r({ ...n.state, draft: { steps: [...g.steps.slice(0, g.at + 1), k], at: g.at + 1 } });
382
+ }, q = (w, g) => {
365
383
  const { draft: _ } = n.state;
366
- _?.steps[_.at + b] && (r({ ...n.state, draft: { ..._, at: _.at + b } }), F(f));
367
- }, T = () => R(-1, c.current.wording.dashboardUndone), Y = () => R(1, c.current.wording.dashboardRedone), L = () => {
368
- n.state.draft && (B(() => o(c.current.defaultView)), F(c.current.wording.dashboardResetDone));
369
- }, W = (b, f, _) => ({
370
- allowed: b,
371
- editing: f !== null,
372
- edit: l,
373
- done: () => g(!0),
374
- cancel: () => g(!1),
384
+ _?.steps[_.at + w] && (r({ ...n.state, draft: { ..._, at: _.at + w } }), $(g));
385
+ }, T = () => q(-1, c.current.wording.dashboardUndone), G = () => q(1, c.current.wording.dashboardRedone), I = () => {
386
+ n.state.draft && (B(() => o(c.current.defaultView)), $(c.current.wording.dashboardResetDone));
387
+ }, W = (w, g, _) => ({
388
+ allowed: w,
389
+ editing: g !== null,
390
+ edit: u,
391
+ done: () => m(!0),
392
+ cancel: () => m(!1),
375
393
  catalogue: _,
376
- setCatalogue: z,
394
+ setCatalogue: S,
377
395
  register: (k) => {
378
396
  n.types = k;
379
- const U = [...k].map(([X, A]) => `${X}:${A.start}:${JSON.stringify(A.sizes)}`).join("|");
380
- U !== n.signature && (n.signature = U, r({ ...n.state, known: n.state.known + 1 }));
397
+ const P = [...k].map(([Y, D]) => `${Y}:${D.start}:${JSON.stringify(D.sizes)}`).join("|");
398
+ P !== n.signature && (n.signature = P, r({ ...n.state, known: n.state.known + 1 }));
381
399
  },
382
400
  step: B,
383
401
  undo: T,
384
- redo: Y,
385
- reset: L,
386
- canUndo: !!f && f.at > 0,
387
- canRedo: !!f && f.at < f.steps.length - 1,
388
- canReset: !!f && !me(f.steps[f.at], o(c.current.defaultView))
402
+ redo: G,
403
+ reset: I,
404
+ canUndo: !!g && g.at > 0,
405
+ canRedo: !!g && g.at < g.steps.length - 1,
406
+ canReset: !!g && !be(g.steps[g.at], o(c.current.defaultView))
389
407
  });
390
408
  return {
391
- get: P,
392
- subscribe: j,
393
- edit: l,
394
- done: () => g(!0),
395
- cancel: () => g(!1),
409
+ get: F,
410
+ subscribe: V,
411
+ edit: u,
412
+ done: () => m(!0),
413
+ cancel: () => m(!1),
396
414
  undo: T,
397
- redo: Y,
398
- reset: L,
399
- catalogue: () => z(!0),
415
+ redo: G,
416
+ reset: I,
417
+ catalogue: () => S(!0),
400
418
  read: o,
401
- setView: (b) => {
402
- const f = he(b) ? b : c.current.defaultView;
403
- r({ ...n.state, view: f, draft: n.state.draft ? { steps: [o(f)], at: 0 } : null });
419
+ setView: (w) => {
420
+ const g = he(w) ? w : c.current.defaultView;
421
+ r({ ...n.state, view: g, draft: n.state.draft ? { steps: [o(g)], at: 0 } : null });
404
422
  },
405
- Dashboard: (b) => {
406
- const { view: f, draft: _, catalogue: k } = le(j, P, P);
407
- return ke(xt, { ...b, view: _ ? _.steps[_.at] : f, editor: W(c.current.allowEdit, _, !!k) });
423
+ Dashboard: (w) => {
424
+ const { view: g, draft: _, catalogue: k } = le(V, F, F);
425
+ return ke(Mt, { ...w, view: _ ? _.steps[_.at] : g, editor: W(c.current.allowEdit, _, !!k) });
408
426
  }
409
427
  };
410
- }, [n]), { view: m, draft: p, known: D } = le(h.subscribe, h.get, h.get), N = p ? p.steps[p.at] : m, G = oe(() => h.read(N), [h, N, D]);
428
+ }, [n]), { view: b, draft: p, known: H } = le(l.subscribe, l.get, l.get), O = p ? p.steps[p.at] : b, j = oe(() => l.read(O), [l, O, H]);
411
429
  return {
412
- Dashboard: h.Dashboard,
413
- WidgetType: ye,
414
- view: G,
415
- setView: h.setView,
430
+ Dashboard: l.Dashboard,
431
+ WidgetType: _e,
432
+ view: j,
433
+ setView: l.setView,
416
434
  editing: p !== null,
417
- edit: h.edit,
418
- done: h.done,
419
- cancel: h.cancel,
420
- catalogue: h.catalogue,
421
- undo: h.undo,
422
- redo: h.redo,
423
- reset: h.reset
435
+ edit: l.edit,
436
+ done: l.done,
437
+ cancel: l.cancel,
438
+ catalogue: l.catalogue,
439
+ undo: l.undo,
440
+ redo: l.redo,
441
+ reset: l.reset
424
442
  };
425
443
  }
426
444
  export {
427
- bt as HeadingType,
428
- ye as WidgetType,
429
- St as useDashboard
445
+ mt as HeadingType,
446
+ _e as WidgetType,
447
+ kt as useDashboard
430
448
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { HTMLAttributes } from 'react';
2
2
  import { ReactNode } from 'react';
3
+ import { RefProp } from '@umriss-ui/core';
3
4
 
4
5
  /** What `useDashboard` takes. */
5
6
  export declare interface DashboardOptions {
@@ -48,7 +49,7 @@ export declare interface DashboardParts {
48
49
  }
49
50
 
50
51
  /** The props of `Dashboard`. */
51
- export declare interface DashboardProps extends HTMLAttributes<HTMLDivElement> {
52
+ export declare interface DashboardProps extends HTMLAttributes<HTMLDivElement>, RefProp<HTMLDivElement> {
52
53
  /** The `WidgetType`s of this dashboard. */
53
54
  children?: ReactNode;
54
55
  /** The `name`s of the widget types the catalogue lists.
package/docs/llms-full.md CHANGED
@@ -1,4 +1,4 @@
1
- # @umriss-ui/dashboard 0.41.1
1
+ # @umriss-ui/dashboard 0.43.0
2
2
 
3
3
  > React dashboard for data-dense applications - widgets a reader arranges, packed in order on twelve columns.
4
4
 
@@ -76,7 +76,7 @@ function TrendChart({ data, previous, labels = ["this week", "last week"] }: { d
76
76
  </Text>
77
77
  )}
78
78
  <div style={{ flex: "1 1 0", minHeight: 0 }}>
79
- <Chart ariaLabel={previous ? `${labels[0]} and ${labels[1]}` : labels[0]} padding={4} style={{ minHeight: 0, height: "100%" }}>
79
+ <Chart aria-label={previous ? `${labels[0]} and ${labels[1]}` : labels[0]} padding={4} style={{ minHeight: 0, height: "100%" }}>
80
80
  <XAxis value="i" domain="data" grid={false} tickFormat={() => ""} />
81
81
  <YAxis grid tickFormat={() => ""} />
82
82
  {previous && <Line value="before" name={labels[1]} color="var(--u-color-text-muted)" strokeWidth={1.25} dash={[3, 3]} markers="never" />}
@@ -468,7 +468,7 @@ function TrendChart({ data, previous, labels = ["this week", "last week"] }: { d
468
468
  </Text>
469
469
  )}
470
470
  <div style={{ flex: "1 1 0", minHeight: 0 }}>
471
- <Chart ariaLabel={previous ? `${labels[0]} and ${labels[1]}` : labels[0]} padding={4} style={{ minHeight: 0, height: "100%" }}>
471
+ <Chart aria-label={previous ? `${labels[0]} and ${labels[1]}` : labels[0]} padding={4} style={{ minHeight: 0, height: "100%" }}>
472
472
  <XAxis value="i" domain="data" grid={false} tickFormat={() => ""} />
473
473
  <YAxis grid tickFormat={() => ""} />
474
474
  {previous && <Line value="before" name={labels[1]} color="var(--u-color-text-muted)" strokeWidth={1.25} dash={[3, 3]} markers="never" />}
@@ -535,14 +535,19 @@ const ALERTS = [
535
535
  { verdict: "bad", text: "Tour 14 is 25 min late", meta: "12 min ago" },
536
536
  { verdict: "warn", text: "Dock 3 is out of service", meta: "34 min ago" },
537
537
  { verdict: "warn", text: "Call: Oakridge Pharmacy", meta: "1 h ago" },
538
+ { verdict: "warn", text: "Tour 9 left without a sign-off", meta: "1 h ago" },
539
+ { verdict: "bad", text: "Cold chain: bay 2 above 8 degrees", meta: "2 h ago" },
540
+ { verdict: "warn", text: "Pallet A-2043 has no label", meta: "2 h ago" },
538
541
  { verdict: "ok", text: "Maintenance on line 3 finished", meta: "2 h ago" },
542
+ { verdict: "ok", text: "Tour 6 is back, 10 min early", meta: "3 h ago" },
543
+ { verdict: "warn", text: "Call: Elmstead Surgery", meta: "3 h ago" },
539
544
  ] as const satisfies ReadonlyArray<{ verdict: Verdict; text: string; meta: string }>;
540
545
 
541
546
  const ORDERS = [
542
- ["A-2041", "Holloway Garden Supplies", "120", "Oct 14", "Packed"],
543
- ["A-2042", "Marlow & Finch Books", "490", "Oct 15", "Picking"],
544
- ["A-2043", "Brixley Cycles", "860", "Oct 16", "Late"],
545
- ["A-2044", "Ferrow Bakery", "75", "Oct 16", "Planned"],
547
+ ["A-2041", "Holloway Co", "120", "Packed"],
548
+ ["A-2042", "Marlow Books", "490", "Picking"],
549
+ ["A-2043", "Brixley Bikes", "860", "Late"],
550
+ ["A-2044", "Ferrow Bakery", "75", "Planned"],
546
551
  ];
547
552
 
548
553
  const heading = (id: string, text: string) => ({ id, type: "heading", size: "full" as const, options: { text } satisfies HeadingOptions });
@@ -610,7 +615,7 @@ export default function Dispatcher() {
610
615
  {() => <Rows rows={ALERTS} />}
611
616
  </WidgetType>
612
617
  <WidgetType name="orders" label="Orders" sentence="The work in hand." sizes={{ wide: { columns: 8, rows: 4 }, full: { columns: 12, rows: 4 } }} start="wide" defaultOptions={{}}>
613
- {() => <DataTable head={["No.", "Customer", "Quantity", "Due", "Status"]} numeric={[2]} rows={ORDERS} />}
618
+ {() => <DataTable head={["No.", "Customer", "Qty", "Status"]} numeric={[2]} rows={ORDERS} />}
614
619
  </WidgetType>
615
620
  </Dashboard>
616
621
  );
@@ -795,6 +800,7 @@ Every move, size, removal, addition, undo, redo, reset, "Done" and "Cancel" is a
795
800
  |---|---|---|---|
796
801
  | `children` | `ReactNode` | — | The `WidgetType`s of this dashboard.<br>Shown in: [The start screen of a dispatcher](#dashboard), [Your own component as a widget](#dashboard), [Headings that divide the dashboard](#dashboard) and 1 more |
797
802
  | `catalogue` | `ReadonlyArray<string>` | every declared type | The `name`s of the widget types the catalogue lists.<br>Shown in: [The start screen of a dispatcher](#dashboard) |
803
+ | `ref` | `Ref<HTMLDivElement> \| undefined` | — | Receives the component's root element. From `RefProp`. |
798
804
 
799
805
  Also takes every attribute of `<div>`.
800
806
 
@@ -865,7 +871,7 @@ Declares no props of its own.
865
871
  - Never: stacks of widgets that share one place, coordinates and free placement, a layout per breakpoint, dense packing ([ADR-0064](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0064-a-dashboard-a-user-arranges-is-a-sequence-on-fixed-columns.md)).
866
872
  - Where an arrangement is stored, which default a role gets, several dashboards and what stands in a widget are the application's.
867
873
 
868
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/dashboard-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
874
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/dashboard-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
869
875
 
870
876
  ## API index
871
877
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umriss-ui/dashboard",
3
- "version": "0.41.1",
3
+ "version": "0.43.0",
4
4
  "description": "React dashboard for data-dense applications - widgets a reader arranges, packed in order on twelve columns.",
5
5
  "license": "MIT",
6
6
  "author": "Roman Händler",
@@ -45,7 +45,8 @@
45
45
  "exports": {
46
46
  ".": {
47
47
  "types": "./dist/index.d.ts",
48
- "import": "./dist/dashboard.js"
48
+ "import": "./dist/dashboard.js",
49
+ "default": "./dist/dashboard.js"
49
50
  },
50
51
  "./styles.css": {
51
52
  "types": "./dist/styles.d.ts",
@@ -53,9 +54,9 @@
53
54
  }
54
55
  },
55
56
  "peerDependencies": {
57
+ "@umriss-ui/core": "^0.43.0",
56
58
  "react": ">=19",
57
- "react-dom": ">=19",
58
- "@umriss-ui/core": "^0.41.1"
59
+ "react-dom": ">=19"
59
60
  },
60
61
  "devDependencies": {
61
62
  "@fontsource/geist-mono": "^5.3.0",
@@ -69,7 +70,7 @@
69
70
  "typescript": "^5.6.3",
70
71
  "vite": "^6.0.0",
71
72
  "vite-plugin-dts": "^4.3.0",
72
- "@umriss-ui/core": "0.41.1",
73
+ "@umriss-ui/core": "0.43.0",
73
74
  "@umriss-ui/demo": "0.0.0"
74
75
  },
75
76
  "scripts": {