@n-uf/placement 26.11.11 → 26.11.12

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
@@ -3,6 +3,34 @@
3
3
  Versions are calendar versions, `YY.M.R` (year, month of the release, counter within the month), shared with
4
4
  `@n-uf/motion`: both packages carry the same version, and a release bumps both.
5
5
 
6
+ ## 26.11.12
7
+
8
+ - New entry `@n-uf/placement/stack`: the items along one edge of a stage (a column or a row), each a panel or a control
9
+ group with forms from largest to smallest. Pure, and not tied to `./panels` columns; `./panels`, `./dock` and `./desk`
10
+ are unchanged.
11
+ - `fitStack(input)`: one form, kind and rect per item. Every item starts in its largest form, or at `StackItem.from`.
12
+ While the smallest sizes don't fit the length, the item used longest ago (unkept before `kept`, the later in the
13
+ stack on a tie) takes its next form, panel `open` → `header` → `rail` → `menu`, group `row` → `stacked` → `icons`
14
+ → `menu`; it doesn't go into the menu when that only lengthens a stack nothing else can shorten (`overflow`).
15
+ Spare length goes to the forms that `grows`, shared by their natural sizes. The stack's cross size is the widest
16
+ form that `setsCross` (a panel's `open`, by default), clamped, else the rail width (30).
17
+ - Cross-fit: a form that doesn't set the cross size and whose `cross.min` (default `cross.natural`) is over the
18
+ stack's takes its next form: a group alone is a rail of icons, a column with nothing open shrinks to a rail, and a
19
+ header with a small `cross.min` stays in a narrower column. It never steps an item into the menu: the item keeps
20
+ its last drawn form and a `too-wide` note says so. An item with no forms is left out (`no-forms`).
21
+ - Result: `items` (`form` and `from`/`to` in notes are indexes into the item's forms), `cross`, `menu` (the » entry's
22
+ rect and the ids in it) and coded `notes` (`form-down`, `to-menu`, `too-wide`, `no-forms`, `overflow`).
23
+ - `stackTargets(input, dragged)`: a drop target for every gap (before each item and after the last; the dragged id is
24
+ lifted out of the input first), in the shape `@n-uf/motion/drag` takes: `gap:<k>`, priority 2, `place`, `hit` (split
25
+ at the items' middles, across as wide as the larger of the stack and the drop's fit), `slot`, `marker` and the full
26
+ `fit` of the stack with the item inserted, so the preview is exactly the rect after the drop. `place.at` counts the
27
+ input's items, not `fit.items` (which leaves out an item with no forms): look the dragged item up in it by id.
28
+ - `storeStack` / `readStack` (`{ v: 1, order, kept, closed, used, forms }`): never throws, drops what it can't use
29
+ (including the `__proto__` key in `forms`, an object with no prototype) and lists it; an id listed twice in `used`
30
+ counts at its last place.
31
+ - Exports: `fitStack`, `stackTargets`, `withItem`, `formSetsCross`, `storeStack`, `readStack`, `createStack`,
32
+ `DEFAULT_RAIL`, `DEFAULT_MENU` and their types (`StackAxis`, `StackForm`, `StackItem`, `StackInput`, `StackFit`, …).
33
+
6
34
  ## 26.11.11
7
35
 
8
36
  - `@n-uf/placement/panels`, drops into a stacked column at a position (opt-in; without it every target is what it was):
package/README.md CHANGED
@@ -40,6 +40,10 @@ without importing each other: a target list from `@n-uf/placement/targets` is ex
40
40
  room kept clear for a toolbar the app draws; their drop targets; and a stored form.
41
41
  - **`/desk`**: desk presets (`docks`, `docksFull`, `dockTear`, `navigator`) as data on `/panels`: options and a
42
42
  start state.
43
+ - **`/stack`**: the items along one edge, a column or a bar, each a panel or a control group with forms from largest to
44
+ smallest (open, header, rail icon, » menu; row, stacked, icons, » menu): one form and rect per item, giving way by
45
+ age, spare room to the panels that grow, a rail when nothing open sets the width; a drop target for every gap, each
46
+ with the exact solve a drop makes; and a stored form. Not tied to `/panels`.
43
47
 
44
48
  Rendering stays in the app: it draws the rects, measures its groups, and feeds pointer samples to a drag intent.
45
49
 
@@ -402,6 +406,94 @@ console.log(targets.some((t) => t.id === "float")); // false
402
406
  the same way, offering only the places the preset allows (`presetInput` gives that input). `./dock` stays as it is for
403
407
  now.
404
408
 
409
+ ## Edge stacks
410
+
411
+ `@n-uf/placement/stack` lays out the items along one edge of a stage: a column (`axis: "column"`, a side region) or a
412
+ row (`"row"`, a bar). An item is a panel or a control group and declares its forms, largest first. Each form has a
413
+ structural `kind` (a panel's `open`, `header`, `rail`, `menu`; a group's `row`, `stacked`, `icons`, `menu`), a `main`
414
+ size along the stack (`min`, `natural`, `grows`) and a `cross` size. `fitStack(input)` picks one form per item and
415
+ places them; it is not tied to `./panels` columns, and nothing in it is a word.
416
+
417
+ - Every item starts in its largest form, or at `from` (an index into its forms; a panel the user folded by hand
418
+ starts at its header). While the smallest sizes don't fit the `length`, the item used longest ago (`used`: the
419
+ smaller, the older) takes its next form; unkept before `kept`, the later in the stack first on a tie. It keeps giving
420
+ way until its forms end, then the next item does. A `menu` form takes no room: the » entry does (`menu.main`,
421
+ default 28 px), while any item is in it; an item doesn't go into the menu when that only makes the stack longer and
422
+ nothing else can give way (`overflow`).
423
+ - Spare length goes to the forms that `grows`, shared by their natural sizes. With none, the items keep their
424
+ natural sizes from the start of the stack.
425
+ - The stack's cross size is the widest `cross.natural` among the forms that set it (a panel's `open`, by default;
426
+ `setsCross` overrides), clamped to `cross.min` and `max`. With none it is the rail, `cross.rail` (default 30 px).
427
+ - Cross-fit: a form that doesn't set the cross size and whose `cross.min` (default its `natural`) is over the stack's
428
+ takes its next form, so a group alone is a rail of icons and a column with nothing open shrinks to a rail. Give a
429
+ header a small `cross.min` and it stays in a column an open panel made narrower. Cross size never puts an item in
430
+ the menu: an item that is still too wide in its last drawn form keeps it, and `too-wide` says so (the app clips it).
431
+ Cross-fit and give-way repeat until nothing changes, because a panel folding can narrow the column.
432
+ - The solver has no open or closed state: it lays out the items it is given. An item with no forms is left out
433
+ (`no-forms`). Panel and group kinds may be mixed on one item; only `menu` and `open` mean anything to the solver.
434
+
435
+ ```ts
436
+ import { fitStack, readStack, stackTargets, storeStack, type StackForm, type StackItem } from "@n-uf/placement/stack";
437
+
438
+ const form = (kind: StackForm["kind"], min: number, natural: number, cross: number, grows = false, crossMin?: number): StackForm => ({
439
+ kind,
440
+ main: { min, natural, ...(grows ? { grows } : {}) },
441
+ cross: { natural: cross, ...(crossMin === undefined ? {} : { min: crossMin }) },
442
+ });
443
+ const panel = (id: string, used: number): StackItem => ({
444
+ id,
445
+ used,
446
+ forms: [form("open", 90, 160, 240, true), form("header", 28, 28, 240, false, 100), form("rail", 24, 24, 30), form("menu", 0, 0, 0)],
447
+ });
448
+ const tools: StackItem = { id: "tools", used: 3, forms: [form("row", 96, 96, 180), form("stacked", 78, 78, 84), form("icons", 72, 72, 28), form("menu", 0, 0, 0)] };
449
+ const column = { axis: "column", length: 250, cross: { min: 120, max: 360 } } as const;
450
+
451
+ const fit = fitStack({ ...column, items: [panel("pages", 1), panel("findings", 2), tools] });
452
+ console.log(fit.items.map((p) => p.kind), fit.cross, fit.notes);
453
+ // [ 'header', 'open', 'row' ] 240 [ { code: 'form-down', id: 'pages', from: 0, to: 1 } ]
454
+
455
+ const alone = fitStack({ ...column, items: [tools] });
456
+ console.log(alone.items[0]!.kind, alone.cross); // icons 30
457
+ ```
458
+
459
+ The result has, per item, `form` (an index into its forms), `kind` and `rect` (stack-local px: the stack starts at
460
+ 0, 0); `cross`, the stack's size across; `menu` (the » entry's `rect` and the ids in it, or `null`; an item in the
461
+ menu has the entry's rect; `menu.items` are exactly the items whose kind is `menu`); and coded `notes`: `form-down`
462
+ (`id`, `from`, `to`: the item took a smaller form than the one it started in, by length or by cross size, as indexes
463
+ into its forms), `to-menu` (`id`), `too-wide` (`id`), `no-forms` (`id`) and `overflow` (`over` px: no item can give
464
+ more way and the stack is still too long; forms that don't end in a `menu` form can't give more).
465
+
466
+ `stackTargets(input, dragged)` takes the stack (an item of it with the dragged id is lifted out first, so a reorder
467
+ within a stack works) and gives a target for every gap, before each item and after the last, in the shape
468
+ `@n-uf/motion/drag` takes: `id` `gap:<k>`, priority 2, `place` `{ kind: "gap", at: k }`, `hit` (the stack split at
469
+ its items' middles, as wide across as the larger of the stack and the drop's fit; with every item in the menu only
470
+ the last gap has any length) and `slot`, the rect the dragged item gets.
471
+ `fit` is the full solve with the item inserted at k (`input` the stack that was solved), so the preview is exactly the
472
+ rect after the drop; `marker` is the line where the item's leading edge lands (a rect with no thickness across the
473
+ stack). The dragged item counts as just used, so the others give way before it, and a group lands in the form that
474
+ fits.
475
+
476
+ ```ts
477
+ const targets = stackTargets({ ...column, items: [panel("pages", 1), panel("findings", 2)] }, tools);
478
+ console.log(targets.map((t) => t.id), targets[1]!.slot, targets[1]!.fit.items[1]!.kind);
479
+ // [ 'gap:0', 'gap:1', 'gap:2' ] { x: 0, y: 28, width: 240, height: 96 } row (Pages folds to its header to make room)
480
+ ```
481
+
482
+ `storeStack(state)` and `readStack(data, known?)` keep what the user chose, as `{ v: 1, … }`: `order`, `kept`,
483
+ `closed`, `used` (the use order, oldest first, so a reload restores who gives way first) and `forms` (the largest form
484
+ index each item may take, `StackItem.from`; stored only from 1). Reading never throws: anything but an object of
485
+ version 1 is `{ ok: false, error }`; inside one, an id that isn't a string, repeats or (with `known`) isn't one the
486
+ consumer has, a `kept`, `closed`, `used` or `forms` entry for an item not in `order`, a form that isn't a whole number
487
+ from 1, the key `__proto__` in `forms` (`forms` is an object with no prototype; `constructor` and `prototype` are ids
488
+ like any other), and a field this version doesn't know are dropped and listed in `dropped`. An id listed twice in
489
+ `used` counts at its last place (the newest use wins).
490
+
491
+ ```ts
492
+ const saved = JSON.stringify(storeStack({ order: ["pages", "tools"], kept: ["pages"], closed: [], used: ["pages", "tools"], forms: { tools: 1 } }));
493
+ console.log(saved); // {"v":1,"order":["pages","tools"],"kept":["pages"],"closed":[],"used":["pages","tools"],"forms":{"tools":1}}
494
+ console.log(readStack(JSON.parse(saved), ["pages", "tools", "findings"]).ok, readStack({ v: 2 }).ok); // true false
495
+ ```
496
+
405
497
  ## Pane zones
406
498
 
407
499
  `paneZone(rect, point, previous, options?)` says which of a pane's five zones a point is in: the centre rectangle
package/dist/desk.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { P as PanelsOptions, a as PanelDef, b as PanelsState, c as PanelTargetInput, d as PanelTarget, p as panelDrop, e as PanelDrop, l as layoutPanels, f as PanelsLayout } from './targets-B_U2Zf7x.js';
1
+ import { P as PanelsOptions, a as PanelDef, b as PanelsState, c as PanelTargetInput, d as PanelTarget, p as panelDrop, e as PanelDrop, l as layoutPanels, f as PanelsLayout } from './targets-B6eLC-52.js';
2
2
  import './geometry-mL-GTRti.js';
3
+ import './target-D3PiLg44.js';
3
4
 
4
5
  /**
5
6
  * @n-uf/placement/desk: desk presets as data on `@n-uf/placement/panels`. A preset is options for the panels engine
package/dist/panels.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { b as PanelsState, S as Side, E as Edge, C as Clock, a as PanelDef } from './targets-B_U2Zf7x.js';
2
- export { B as BAND_SIZE, g as Band, h as BandOption, i as COLUMN_REACH, j as Column, D as DEFAULT_PANELS_OPTIONS, k as DockOptions, m as EDGES, F as FLOAT_HEIGHT, n as FLOAT_MIN, o as FloatAt, G as GiveWay, I as IDENTITY_VIEW, e as PanelDrop, q as PanelKind, r as PanelPlace, s as PanelStop, t as PanelStopsOptions, d as PanelTarget, c as PanelTargetInput, f as PanelsLayout, u as PanelsNote, v as PanelsNoteCode, P as PanelsOptions, w as PanelsStart, x as Place, y as PlacedBand, z as PlacedColumn, A as PlacedPanel, H as PlacedRail, R as RAIL_EDGE, J as Reserve, K as ReserveEdge, L as SIDES, V as View, W as Where, M as activate, N as bandHeight, O as bandSize, Q as close, T as columnWidth, U as createPanels, X as defOf, Y as dock, Z as dockBand, _ as float, $ as floatSize, a0 as focus, a1 as giveWayOrder, a2 as homePlace, a3 as keep, l as layoutPanels, a4 as liftPanel, a5 as open, a6 as ownWidth, p as panelDrop, a7 as panelStops, a8 as panelTargets, a9 as pin, aa as placeRank, ab as resizeBand, ac as resizeColumn, ad as resizeFloat, ae as samePlaces, af as showTabs, ag as tick, ah as toPage, ai as toView, aj as toggleFold, ak as whereIs } from './targets-B_U2Zf7x.js';
1
+ import { b as PanelsState, S as Side, E as Edge, C as Clock, a as PanelDef } from './targets-B6eLC-52.js';
2
+ export { B as BAND_SIZE, g as Band, h as BandOption, i as COLUMN_REACH, j as Column, D as DEFAULT_PANELS_OPTIONS, k as DockOptions, m as EDGES, F as FLOAT_HEIGHT, n as FLOAT_MIN, o as FloatAt, G as GiveWay, I as IDENTITY_VIEW, e as PanelDrop, q as PanelKind, r as PanelPlace, s as PanelStop, t as PanelStopsOptions, d as PanelTarget, c as PanelTargetInput, f as PanelsLayout, u as PanelsNote, v as PanelsNoteCode, P as PanelsOptions, w as PanelsStart, x as Place, y as PlacedBand, z as PlacedColumn, A as PlacedPanel, H as PlacedRail, R as RAIL_EDGE, J as Reserve, K as ReserveEdge, L as SIDES, V as View, W as Where, M as activate, N as bandHeight, O as bandSize, Q as close, T as columnWidth, U as createPanels, X as defOf, Y as dock, Z as dockBand, _ as float, $ as floatSize, a0 as focus, a1 as giveWayOrder, a2 as homePlace, a3 as keep, l as layoutPanels, a4 as liftPanel, a5 as open, a6 as ownWidth, p as panelDrop, a7 as panelStops, a8 as panelTargets, a9 as pin, aa as placeRank, ab as resizeBand, ac as resizeColumn, ad as resizeFloat, ae as samePlaces, af as showTabs, ag as tick, ah as toPage, ai as toView, aj as toggleFold, ak as whereIs } from './targets-B6eLC-52.js';
3
3
  import { R as Rect } from './geometry-mL-GTRti.js';
4
+ import './target-D3PiLg44.js';
4
5
 
5
6
  /**
6
7
  * The panels' stored form: `storePanels(state)` gives plain JSON-safe data (`{ v: 1, … }`); `readPanels(data, defs?)`
@@ -0,0 +1,237 @@
1
+ import { R as Rect } from './geometry-mL-GTRti.js';
2
+ import { T as Target } from './target-D3PiLg44.js';
3
+
4
+ /**
5
+ * The edge stack's shapes. A stack is the items along one edge of a stage: a column (vertical, a side region) or a
6
+ * row (horizontal, a bar). An item is a panel or a control group; it declares its forms, largest first, and the
7
+ * solver (`fitStack`) picks one per item. Plain data in and out: no words, no time, no DOM.
8
+ *
9
+ * The form kinds are structural labels the app draws by; the solver reads only `menu` (a form with no room of its own)
10
+ * and, for the cross size, `open`. Panel and group kinds may be mixed on one item.
11
+ */
12
+
13
+ /** `"column"` runs the stack down (its main size is a height, its cross size a width); `"row"` runs it across. */
14
+ type StackAxis = "column" | "row";
15
+ /** How a panel draws: open (header and body), a header only, a rail icon, or an entry in the » menu. */
16
+ type PanelFormKind = "open" | "header" | "rail" | "menu";
17
+ /** How a control group draws: a row of words, the words stacked, icons only, or an entry in the » menu. */
18
+ type GroupFormKind = "row" | "stacked" | "icons" | "menu";
19
+ type StackFormKind = PanelFormKind | GroupFormKind;
20
+ /** Along the stack: `min` is the least it can be drawn, `natural` what it asks for; `grows` takes spare length. */
21
+ interface MainSize {
22
+ readonly min: number;
23
+ readonly natural: number;
24
+ readonly grows?: boolean;
25
+ }
26
+ /** One way to draw an item. A `menu` form takes no room in the stack (the » entry stands for it). */
27
+ interface StackForm {
28
+ readonly kind: StackFormKind;
29
+ readonly main: MainSize;
30
+ /**
31
+ * Across the stack: `natural` is what this form asks for (it sets the stack's cross size when `setsCross`); `min`
32
+ * is the least it can be drawn across (default `natural`). A form that doesn't set the cross size takes its next
33
+ * form only when its `min` is over the stack's cross size, so a header stays in a column an open panel made
34
+ * narrow when it has a small `min`.
35
+ */
36
+ readonly cross: {
37
+ readonly natural: number;
38
+ readonly min?: number;
39
+ };
40
+ /**
41
+ * Whether this form sets the stack's cross size (the column's width, the bar's height). Default: true for a
42
+ * panel's `open`, false for everything else (a group never sets a column's width).
43
+ */
44
+ readonly setsCross?: boolean;
45
+ }
46
+ interface StackItem {
47
+ readonly id: string;
48
+ /** Largest first. The solver starts at `from` and steps down one at a time. An item with none is left out. */
49
+ readonly forms: readonly StackForm[];
50
+ /** How recently it was used: the smaller, the longer ago, the sooner it gives way. */
51
+ readonly used: number;
52
+ /** Kept: gives way after every item that isn't. */
53
+ readonly kept?: boolean;
54
+ /**
55
+ * The first form the item may take, an index into `forms` (default 0; a panel folded by hand starts at its header).
56
+ * The solver never goes above it. `form` in the result and in the notes are indexes into `forms`, not into what is
57
+ * left after `from`.
58
+ */
59
+ readonly from?: number;
60
+ }
61
+ /** The cross size a stack may take. With no form setting it, the stack is a `rail` wide (default {@link DEFAULT_RAIL}). */
62
+ interface CrossLimits {
63
+ readonly min: number;
64
+ readonly max: number;
65
+ readonly rail?: number;
66
+ }
67
+ interface StackInput {
68
+ readonly axis: StackAxis;
69
+ /** The stack's length along the axis. */
70
+ readonly length: number;
71
+ readonly cross: CrossLimits;
72
+ /** In order, from the start of the stack. */
73
+ readonly items: readonly StackItem[];
74
+ /** The » menu entry's size along the stack, taken only while an item is in the menu (default {@link DEFAULT_MENU}). */
75
+ readonly menu?: {
76
+ readonly main: number;
77
+ };
78
+ }
79
+ /** The width of a stack nothing open holds, px. */
80
+ declare const DEFAULT_RAIL = 30;
81
+ /** The » menu entry's size along the stack, px. */
82
+ declare const DEFAULT_MENU = 28;
83
+ /** Whether a form sets the stack's cross size: its own say, else true for a panel's `open`. */
84
+ declare const formSetsCross: (f: StackForm) => boolean;
85
+ /** `input` with `item` inserted at index `at` (clamped to the items' range). */
86
+ declare function withItem(input: StackInput, item: StackItem, at: number): StackInput;
87
+ /** An item drawn: the form it took, its kind and its rect (stack-local px). One in the menu has the menu entry's rect. */
88
+ interface PlacedStackItem {
89
+ readonly id: string;
90
+ /** Index into the item's `forms`. */
91
+ readonly form: number;
92
+ readonly kind: StackFormKind;
93
+ readonly rect: Rect;
94
+ }
95
+ interface StackMenu {
96
+ /** The » entry's rect. */
97
+ readonly rect: Rect;
98
+ /** The items in it, in the stack's order: exactly the items whose kind is `menu`. */
99
+ readonly items: readonly string[];
100
+ }
101
+ /**
102
+ * Why a stack looks as it does:
103
+ * - `form-down`: an item took a smaller form than the one it started in (`from`, an index into its forms), by length
104
+ * or by cross size.
105
+ * - `to-menu`: an item is in the » menu.
106
+ * - `too-wide`: an item's form doesn't set the cross size and its `cross.min` is over the stack's. Cross size never
107
+ * puts an item in the menu, so it keeps its last drawn form and the app clips it.
108
+ * - `no-forms`: an item with no forms, left out of `items`.
109
+ * - `overflow`: no item can give more way and the stack is still `over` px too long (forms that don't end in a `menu`
110
+ * form can't give more; neither can a stack where going into the menu would only make it longer).
111
+ */
112
+ type StackNote = {
113
+ readonly code: "form-down";
114
+ readonly id: string;
115
+ readonly from: number;
116
+ readonly to: number;
117
+ } | {
118
+ readonly code: "to-menu";
119
+ readonly id: string;
120
+ } | {
121
+ readonly code: "too-wide";
122
+ readonly id: string;
123
+ } | {
124
+ readonly code: "no-forms";
125
+ readonly id: string;
126
+ } | {
127
+ readonly code: "overflow";
128
+ readonly over: number;
129
+ };
130
+ type StackNoteCode = StackNote["code"];
131
+ interface StackFit {
132
+ readonly axis: StackAxis;
133
+ readonly length: number;
134
+ /** The stack's size across: the widest `open` (clamped to the limits), or the rail. */
135
+ readonly cross: number;
136
+ /** One per input item that has forms, in order. */
137
+ readonly items: readonly PlacedStackItem[];
138
+ /** The » entry, or null while no item is in the menu. */
139
+ readonly menu: StackMenu | null;
140
+ /** `no-forms` first, then `form-down` per item in the order they gave way, `to-menu`, `too-wide`, and `overflow`. */
141
+ readonly notes: readonly StackNote[];
142
+ }
143
+
144
+ declare function fitStack(input: StackInput): StackFit;
145
+
146
+ /**
147
+ * The drop targets for dragging an item into a stack, in the shape @n-uf/motion/drag takes. `stackTargets(input,
148
+ * dragged)` gives one target for every gap of the stack without the dragged item (an item of the input with its id
149
+ * is lifted out first, so a reorder within a stack works): before each item and after the last, so an empty stack has
150
+ * one. An item with no forms has no place of its own, but its gaps count.
151
+ *
152
+ * - `id` `gap:<k>`, priority 2, `place` `{ kind: "gap", at: k }` (k counts the input's items, the dragged one lifted
153
+ * out, including any with no forms; it is not an index into `fit.items`, which leaves those out: look the dragged
154
+ * item up there by its id, as `slot` already does).
155
+ * - `hit`: the gap's zone along the axis, the stack split at its items' middles (an item in the menu has no place of
156
+ * its own: the gaps next to it split the stretch where the stack's drawn items end; with every item in the menu only
157
+ * the last gap, "at the end", has any length, and the others are a point at the start). Across, it is the larger of
158
+ * the stack's current size and the size the drop gives it, so the pointer over the preview is over the target.
159
+ * - `fit`: the full `fitStack` of the stack with the dragged item inserted at k, and `input` the stack that was
160
+ * solved. The preview is `slot`, the dragged item's rect in `fit`: exactly the rect it gets after the drop, because
161
+ * the drop is that same solve. `marker` is the line where its leading edge lands: a rect with no thickness across
162
+ * the stack's cross size (the app draws it with one).
163
+ *
164
+ * The dragged item counts as just used: it is newer than every item in the stack, so the others give way before it.
165
+ * A group lands in the form that fits; a panel that would set a wider column makes room by that solve too. A dragged
166
+ * item with no forms has no targets.
167
+ */
168
+
169
+ type StackPlace = {
170
+ readonly kind: "gap";
171
+ readonly at: number;
172
+ };
173
+ interface StackTarget extends Target {
174
+ readonly priority: 2;
175
+ /** The item dragged. */
176
+ readonly item: string;
177
+ readonly place: StackPlace;
178
+ /** The line where the dragged item's leading edge lands. */
179
+ readonly marker: Rect;
180
+ /** The stack with the dragged item inserted: what a drop here makes. */
181
+ readonly input: StackInput;
182
+ /** Its fit: the preview and the drop are both its rect for the dragged item. */
183
+ readonly fit: StackFit;
184
+ }
185
+ declare function stackTargets(input: StackInput, dragged: StackItem): StackTarget[];
186
+
187
+ /**
188
+ * The stack's stored form: `storeStack(state)` gives plain JSON-safe data (`{ v: 1, order, kept, closed, used, forms
189
+ * }`); `readStack(data, known?)` reads parsed data back and never throws. Anything but an object of version 1 is
190
+ * `{ ok: false, error }`. Inside one it is lenient, as the panels' reader is: an id that isn't a string, is repeated
191
+ * or (with `known`) isn't one the consumer has is dropped and listed in `dropped`; a `kept`, `closed`, `used` or
192
+ * `forms` entry for an id that isn't in `order` is dropped; a form that isn't a whole number from 1 up is dropped;
193
+ * the key `__proto__` in `forms` is dropped (the other keys, `constructor` and `prototype` included, are ids like any
194
+ * other: `forms` is an object with no prototype, written and read); a field this version doesn't know is skipped and
195
+ * reported as `"<field>"`. An item the consumer has that the stored order lacks is no error: the consumer places it.
196
+ *
197
+ * - `kept`: items kept open (they give way last).
198
+ * - `closed`: items the user closed; their place in `order` is kept for when they open.
199
+ * - `used`: the use order, oldest to newest, so a reload restores who gives way first (an item's `used` is its index in
200
+ * it; one the list lacks is older than all). An id listed twice counts at its last place: the newest use wins.
201
+ * - `forms`: the user's chosen form per item, the index of the largest form it may take (`StackItem.from`). 0, the
202
+ * first, is not stored.
203
+ */
204
+ interface StackState {
205
+ /** The items' order along the stack. */
206
+ readonly order: readonly string[];
207
+ /** Items kept open: they give way last. */
208
+ readonly kept: readonly string[];
209
+ /** Items the user closed. */
210
+ readonly closed: readonly string[];
211
+ /** The use order, oldest first. */
212
+ readonly used: readonly string[];
213
+ /** The form index the user chose per item (only those that chose one; absent means the first). */
214
+ readonly forms: Readonly<Record<string, number>>;
215
+ }
216
+ interface StoredStack {
217
+ readonly v: 1;
218
+ readonly order: string[];
219
+ readonly kept: string[];
220
+ readonly closed: string[];
221
+ readonly used: string[];
222
+ readonly forms: Record<string, number>;
223
+ }
224
+ type StackRead = {
225
+ readonly ok: true;
226
+ readonly state: StackState;
227
+ readonly dropped: readonly string[];
228
+ } | {
229
+ readonly ok: false;
230
+ readonly error: string;
231
+ };
232
+ /** A state with these items in this order, none kept or closed, used in that order, every form the first. */
233
+ declare const createStack: (order: readonly string[]) => StackState;
234
+ declare function storeStack(state: StackState): StoredStack;
235
+ declare function readStack(data: unknown, known?: readonly string[]): StackRead;
236
+
237
+ export { type CrossLimits, DEFAULT_MENU, DEFAULT_RAIL, type GroupFormKind, type MainSize, type PanelFormKind, type PlacedStackItem, type StackAxis, type StackFit, type StackForm, type StackFormKind, type StackInput, type StackItem, type StackMenu, type StackNote, type StackNoteCode, type StackPlace, type StackRead, type StackState, type StackTarget, type StoredStack, createStack, fitStack, formSetsCross, readStack, stackTargets, storeStack, withItem };
package/dist/stack.js ADDED
@@ -0,0 +1,272 @@
1
+ import {
2
+ rect
3
+ } from "./chunk-QMTNB2C3.js";
4
+
5
+ // src/stack/model.ts
6
+ var DEFAULT_RAIL = 30;
7
+ var DEFAULT_MENU = 28;
8
+ var finite = (x, fallback) => Number.isFinite(x) ? x : fallback;
9
+ function limitsOf(input) {
10
+ const min = Math.max(0, finite(input.cross.min, 0));
11
+ const max = Number.isNaN(input.cross.max) ? Infinity : input.cross.max;
12
+ return { min, max: Math.max(min, max), rail: Math.max(0, finite(input.cross.rail ?? DEFAULT_RAIL, DEFAULT_RAIL)) };
13
+ }
14
+ var menuMain = (input) => Math.max(0, finite(input.menu?.main ?? DEFAULT_MENU, DEFAULT_MENU));
15
+ var formSetsCross = (f) => f.setsCross ?? f.kind === "open";
16
+ function withItem(input, item, at) {
17
+ const k = Math.min(Math.max(0, Math.floor(at)), input.items.length);
18
+ return { ...input, items: [...input.items.slice(0, k), item, ...input.items.slice(k)] };
19
+ }
20
+
21
+ // src/stack/layout.ts
22
+ var EPS = 1e-6;
23
+ var isMenu = (f) => f.kind === "menu";
24
+ var finite2 = (x, fallback = 0) => Number.isFinite(x) ? x : fallback;
25
+ var mainOf = (f, which) => isMenu(f) ? 0 : Math.max(0, finite2(f.main[which]));
26
+ var crossMin = (f) => finite2(f.cross.min ?? f.cross.natural);
27
+ function victim(items, at) {
28
+ let best = -1;
29
+ for (let i = 0; i < items.length; i++) {
30
+ if (at[i] + 1 >= items[i].forms.length) continue;
31
+ if (best < 0) {
32
+ best = i;
33
+ continue;
34
+ }
35
+ const a = items[i];
36
+ const b = items[best];
37
+ const ka = a.kept ? 1 : 0;
38
+ const kb = b.kept ? 1 : 0;
39
+ if (ka !== kb ? ka < kb : a.used !== b.used ? a.used < b.used : true) best = i;
40
+ }
41
+ return best;
42
+ }
43
+ function fitStack(input) {
44
+ const length = Math.max(0, finite2(input.length));
45
+ const lim = limitsOf(input);
46
+ const items = input.items.filter((it) => it.forms.length > 0);
47
+ const start = items.map((it) => Math.min(Math.max(0, Math.floor(finite2(it.from ?? 0))), it.forms.length - 1));
48
+ const at = [...start];
49
+ const form = (i) => items[i].forms[at[i]];
50
+ const idx = items.map((_, i) => i);
51
+ const firstStep = [];
52
+ const menuOrder = [];
53
+ const step = (i) => {
54
+ if (!firstStep.includes(i)) firstStep.push(i);
55
+ at[i]++;
56
+ if (isMenu(form(i)) && !menuOrder.includes(i)) menuOrder.push(i);
57
+ };
58
+ for (const i of idx) if (isMenu(form(i))) menuOrder.push(i);
59
+ const crossOf = () => {
60
+ const setters = idx.filter((i) => !isMenu(form(i)) && formSetsCross(form(i)));
61
+ if (setters.length === 0) return lim.rail;
62
+ const want = Math.max(...setters.map((i) => finite2(form(i).cross.natural)));
63
+ return Math.min(Math.max(want, lim.min), lim.max);
64
+ };
65
+ const inMenu = () => idx.some((i) => isMenu(form(i)));
66
+ const minSum = () => idx.reduce((s, i) => s + mainOf(form(i), "min"), 0) + (inMenu() ? menuMain(input) : 0);
67
+ const canStep = (i) => at[i] + 1 < items[i].forms.length;
68
+ const tooWide = (i, cross2) => !isMenu(form(i)) && !formSetsCross(form(i)) && crossMin(form(i)) > cross2 + EPS;
69
+ let cross = crossOf();
70
+ let overflow = 0;
71
+ const guard = items.reduce((s, it) => s + it.forms.length, 0) + 1;
72
+ for (let n = 0; n <= guard; n++) {
73
+ cross = crossOf();
74
+ let stepped = false;
75
+ for (const i of idx) {
76
+ if (tooWide(i, cross) && canStep(i) && !isMenu(items[i].forms[at[i] + 1])) {
77
+ step(i);
78
+ stepped = true;
79
+ }
80
+ }
81
+ if (stepped) continue;
82
+ const over = minSum() - length;
83
+ if (over <= EPS) break;
84
+ const v = victim(items, at);
85
+ if (v < 0) {
86
+ overflow = over;
87
+ break;
88
+ }
89
+ const intoMenu = isMenu(items[v].forms[at[v] + 1]);
90
+ const gain = mainOf(form(v), "min") - (inMenu() ? 0 : menuMain(input));
91
+ if (intoMenu && gain < 0 && idx.filter(canStep).length === 1) {
92
+ overflow = over;
93
+ break;
94
+ }
95
+ step(v);
96
+ }
97
+ cross = crossOf();
98
+ const menuSize = inMenu() ? menuMain(input) : 0;
99
+ const avail = Math.max(0, length - menuSize);
100
+ const drawn = idx.filter((i) => !isMenu(form(i)));
101
+ const min = (i) => mainOf(form(i), "min");
102
+ const nat = (i) => Math.max(min(i), mainOf(form(i), "natural"));
103
+ const sumMin = drawn.reduce((s, i) => s + min(i), 0);
104
+ const sumNat = drawn.reduce((s, i) => s + nat(i), 0);
105
+ const size = /* @__PURE__ */ new Map();
106
+ if (sumNat <= avail + EPS) {
107
+ const growers = drawn.filter((i) => form(i).main.grows);
108
+ const spare = Math.max(0, avail - sumNat);
109
+ const weight = growers.reduce((s, i) => s + nat(i), 0);
110
+ for (const i of drawn) {
111
+ const share = growers.includes(i) ? weight > EPS ? nat(i) / weight : 1 / growers.length : 0;
112
+ size.set(i, nat(i) + spare * share);
113
+ }
114
+ } else {
115
+ const t = avail > sumMin && sumNat > sumMin ? (avail - sumMin) / (sumNat - sumMin) : 0;
116
+ for (const i of drawn) size.set(i, min(i) + (nat(i) - min(i)) * t);
117
+ }
118
+ const rectAt = (from, main) => input.axis === "column" ? { x: 0, y: from, width: cross, height: main } : { x: from, y: 0, width: main, height: cross };
119
+ const rects = /* @__PURE__ */ new Map();
120
+ let cursor = 0;
121
+ for (const i of drawn) {
122
+ const s = size.get(i);
123
+ rects.set(i, rectAt(cursor, s));
124
+ cursor += s;
125
+ }
126
+ const menuRect = rectAt(Math.max(cursor, length - menuSize), menuSize);
127
+ const inside = idx.filter((i) => isMenu(form(i)));
128
+ const menu = inside.length > 0 ? { rect: menuRect, items: inside.map((i) => items[i].id) } : null;
129
+ const placed = idx.map((i) => ({ id: items[i].id, form: at[i], kind: form(i).kind, rect: isMenu(form(i)) ? menuRect : rects.get(i) }));
130
+ const notes = [];
131
+ for (const it of input.items) if (it.forms.length === 0) notes.push({ code: "no-forms", id: it.id });
132
+ for (const i of firstStep) if (at[i] > start[i]) notes.push({ code: "form-down", id: items[i].id, from: start[i], to: at[i] });
133
+ for (const i of menuOrder) if (isMenu(form(i))) notes.push({ code: "to-menu", id: items[i].id });
134
+ for (const i of idx) if (tooWide(i, cross)) notes.push({ code: "too-wide", id: items[i].id });
135
+ if (overflow > EPS) notes.push({ code: "overflow", over: overflow });
136
+ return { axis: input.axis, length, cross, items: placed, menu, notes };
137
+ }
138
+
139
+ // src/stack/targets.ts
140
+ function bounds(input, fit) {
141
+ const column = input.axis === "column";
142
+ const start = (r) => column ? r.y : r.x;
143
+ const size = (r) => column ? r.height : r.width;
144
+ const placed = new Map(fit.items.map((p) => [p.id, p]));
145
+ const b = [0];
146
+ let end = 0;
147
+ for (const it of input.items) {
148
+ const p = placed.get(it.id);
149
+ if (!p || p.kind === "menu") {
150
+ b.push(end);
151
+ continue;
152
+ }
153
+ b.push(start(p.rect) + size(p.rect) / 2);
154
+ end = start(p.rect) + size(p.rect);
155
+ }
156
+ b.push(fit.length);
157
+ for (let i = 1; i < b.length; i++) b[i] = Math.min(Math.max(b[i], b[i - 1]), fit.length);
158
+ return b;
159
+ }
160
+ function stackTargets(input, dragged) {
161
+ if (dragged.forms.length === 0) return [];
162
+ const rest = { ...input, items: input.items.filter((it) => it.id !== dragged.id) };
163
+ const base = fitStack(rest);
164
+ const b = bounds(rest, base);
165
+ const column = rest.axis === "column";
166
+ const newest = rest.items.reduce((m, it) => Math.max(m, it.used), dragged.used) + 1;
167
+ const item = { ...dragged, used: newest };
168
+ const out = [];
169
+ for (let k = 0; k <= rest.items.length; k++) {
170
+ const solved = withItem(rest, item, k);
171
+ const fit = fitStack(solved);
172
+ const slot = fit.items.find((p) => p.id === dragged.id).rect;
173
+ const from = b[k];
174
+ const to = b[k + 1];
175
+ const across = Math.max(base.cross, fit.cross);
176
+ out.push({
177
+ id: `gap:${k}`,
178
+ priority: 2,
179
+ item: dragged.id,
180
+ place: { kind: "gap", at: k },
181
+ hit: column ? rect(0, from, across, to - from) : rect(from, 0, to - from, across),
182
+ slot,
183
+ marker: column ? rect(slot.x, slot.y, slot.width, 0) : rect(slot.x, slot.y, 0, slot.height),
184
+ input: solved,
185
+ fit
186
+ });
187
+ }
188
+ return out;
189
+ }
190
+
191
+ // src/stack/store.ts
192
+ var createStack = (order) => ({ order: [...order], kept: [], closed: [], used: [...order], forms: {} });
193
+ var isForm = (f) => typeof f === "number" && Number.isInteger(f) && f >= 1;
194
+ var uniq = (ids, order) => [...new Set(ids)].filter((id) => order.includes(id));
195
+ var uniqLast = (ids, order) => ids.filter((id, i) => order.includes(id) && ids.lastIndexOf(id) === i);
196
+ var noForms = () => /* @__PURE__ */ Object.create(null);
197
+ function storeStack(state) {
198
+ const forms = noForms();
199
+ for (const id of state.order) {
200
+ const f = state.forms[id];
201
+ if (id !== "__proto__" && Object.hasOwn(state.forms, id) && isForm(f)) forms[id] = f;
202
+ }
203
+ return { v: 1, order: [...state.order], kept: uniq(state.kept, state.order), closed: uniq(state.closed, state.order), used: uniqLast(state.used, state.order), forms };
204
+ }
205
+ var isObject = (x) => typeof x === "object" && x !== null && !Array.isArray(x);
206
+ function readStack(data, known) {
207
+ try {
208
+ return read(data, known);
209
+ } catch (e) {
210
+ return { ok: false, error: `unreadable: ${e instanceof Error ? e.message : typeof e}` };
211
+ }
212
+ }
213
+ var versionWords = (v) => typeof v === "number" ? String(v) : typeof v === "string" ? JSON.stringify(v.length > 24 ? `${v.slice(0, 24)}\u2026` : v) : typeof v;
214
+ function read(data, known) {
215
+ if (!isObject(data)) return { ok: false, error: "not an object" };
216
+ if (data.v !== 1) return { ok: false, error: `unknown version ${versionWords(data.v)} (this reads version 1)` };
217
+ if (!Array.isArray(data.order)) return { ok: false, error: "order is missing" };
218
+ const dropped = [];
219
+ const drop = (what) => {
220
+ if (!dropped.includes(what)) dropped.push(what);
221
+ };
222
+ for (const k of Object.keys(data)) if (!["v", "order", "kept", "closed", "used", "forms"].includes(k)) drop(k);
223
+ const order = [];
224
+ for (const x of data.order) {
225
+ if (typeof x !== "string") drop(`a ${typeof x} where an id goes`);
226
+ else if (known && !known.includes(x) || order.includes(x)) drop(x);
227
+ else order.push(x);
228
+ }
229
+ const list = (key) => {
230
+ const out = [];
231
+ const x = data[key];
232
+ if (x === void 0) return out;
233
+ if (!Array.isArray(x)) {
234
+ drop(key);
235
+ return out;
236
+ }
237
+ for (const id of x) {
238
+ if (typeof id === "string" && order.includes(id)) {
239
+ if (key === "used" && out.includes(id)) out.splice(out.indexOf(id), 1);
240
+ if (!out.includes(id)) out.push(id);
241
+ } else drop(typeof id === "string" ? `${key}:${id}` : `a ${typeof id} in ${key}`);
242
+ }
243
+ return out;
244
+ };
245
+ const kept = list("kept");
246
+ const closed = list("closed");
247
+ const used = list("used");
248
+ const forms = noForms();
249
+ if (data.forms !== void 0) {
250
+ if (!isObject(data.forms)) drop("forms");
251
+ else {
252
+ for (const id of Object.keys(data.forms)) {
253
+ const f = data.forms[id];
254
+ if (id === "__proto__" || !Object.hasOwn(data.forms, id) || !order.includes(id) || !isForm(f)) drop(`forms:${id}`);
255
+ else forms[id] = f;
256
+ }
257
+ }
258
+ }
259
+ return { ok: true, state: { order, kept, closed, used, forms }, dropped };
260
+ }
261
+ export {
262
+ DEFAULT_MENU,
263
+ DEFAULT_RAIL,
264
+ createStack,
265
+ fitStack,
266
+ formSetsCross,
267
+ readStack,
268
+ stackTargets,
269
+ storeStack,
270
+ withItem
271
+ };
272
+ //# sourceMappingURL=stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stack/model.ts","../src/stack/layout.ts","../src/stack/targets.ts","../src/stack/store.ts"],"sourcesContent":["/**\n * The edge stack's shapes. A stack is the items along one edge of a stage: a column (vertical, a side region) or a\n * row (horizontal, a bar). An item is a panel or a control group; it declares its forms, largest first, and the\n * solver (`fitStack`) picks one per item. Plain data in and out: no words, no time, no DOM.\n *\n * The form kinds are structural labels the app draws by; the solver reads only `menu` (a form with no room of its own)\n * and, for the cross size, `open`. Panel and group kinds may be mixed on one item.\n */\nimport type { Rect } from \"../geometry\";\n\n/** `\"column\"` runs the stack down (its main size is a height, its cross size a width); `\"row\"` runs it across. */\nexport type StackAxis = \"column\" | \"row\";\n\n/** How a panel draws: open (header and body), a header only, a rail icon, or an entry in the » menu. */\nexport type PanelFormKind = \"open\" | \"header\" | \"rail\" | \"menu\";\n/** How a control group draws: a row of words, the words stacked, icons only, or an entry in the » menu. */\nexport type GroupFormKind = \"row\" | \"stacked\" | \"icons\" | \"menu\";\nexport type StackFormKind = PanelFormKind | GroupFormKind;\n\n/** Along the stack: `min` is the least it can be drawn, `natural` what it asks for; `grows` takes spare length. */\nexport interface MainSize {\n readonly min: number;\n readonly natural: number;\n readonly grows?: boolean;\n}\n\n/** One way to draw an item. A `menu` form takes no room in the stack (the » entry stands for it). */\nexport interface StackForm {\n readonly kind: StackFormKind;\n readonly main: MainSize;\n /**\n * Across the stack: `natural` is what this form asks for (it sets the stack's cross size when `setsCross`); `min`\n * is the least it can be drawn across (default `natural`). A form that doesn't set the cross size takes its next\n * form only when its `min` is over the stack's cross size, so a header stays in a column an open panel made\n * narrow when it has a small `min`.\n */\n readonly cross: { readonly natural: number; readonly min?: number };\n /**\n * Whether this form sets the stack's cross size (the column's width, the bar's height). Default: true for a\n * panel's `open`, false for everything else (a group never sets a column's width).\n */\n readonly setsCross?: boolean;\n}\n\nexport interface StackItem {\n readonly id: string;\n /** Largest first. The solver starts at `from` and steps down one at a time. An item with none is left out. */\n readonly forms: readonly StackForm[];\n /** How recently it was used: the smaller, the longer ago, the sooner it gives way. */\n readonly used: number;\n /** Kept: gives way after every item that isn't. */\n readonly kept?: boolean;\n /**\n * The first form the item may take, an index into `forms` (default 0; a panel folded by hand starts at its header).\n * The solver never goes above it. `form` in the result and in the notes are indexes into `forms`, not into what is\n * left after `from`.\n */\n readonly from?: number;\n}\n\n/** The cross size a stack may take. With no form setting it, the stack is a `rail` wide (default {@link DEFAULT_RAIL}). */\nexport interface CrossLimits {\n readonly min: number;\n readonly max: number;\n readonly rail?: number;\n}\n\nexport interface StackInput {\n readonly axis: StackAxis;\n /** The stack's length along the axis. */\n readonly length: number;\n readonly cross: CrossLimits;\n /** In order, from the start of the stack. */\n readonly items: readonly StackItem[];\n /** The » menu entry's size along the stack, taken only while an item is in the menu (default {@link DEFAULT_MENU}). */\n readonly menu?: { readonly main: number };\n}\n\n/** The width of a stack nothing open holds, px. */\nexport const DEFAULT_RAIL = 30;\n/** The » menu entry's size along the stack, px. */\nexport const DEFAULT_MENU = 28;\n\nconst finite = (x: number, fallback: number): number => (Number.isFinite(x) ? x : fallback);\n\n/**\n * The cross limits with a non-number replaced: `min` and `rail` that aren't finite become 0 and the default rail, a\n * `max` that is NaN becomes no limit (Infinity is one already), and `max` is never under `min`.\n */\nexport function limitsOf(input: StackInput): { readonly min: number; readonly max: number; readonly rail: number } {\n const min = Math.max(0, finite(input.cross.min, 0));\n const max = Number.isNaN(input.cross.max) ? Infinity : input.cross.max;\n return { min, max: Math.max(min, max), rail: Math.max(0, finite(input.cross.rail ?? DEFAULT_RAIL, DEFAULT_RAIL)) };\n}\nexport const menuMain = (input: StackInput): number => Math.max(0, finite(input.menu?.main ?? DEFAULT_MENU, DEFAULT_MENU));\n\n/** Whether a form sets the stack's cross size: its own say, else true for a panel's `open`. */\nexport const formSetsCross = (f: StackForm): boolean => f.setsCross ?? f.kind === \"open\";\n\n/** `input` with `item` inserted at index `at` (clamped to the items' range). */\nexport function withItem(input: StackInput, item: StackItem, at: number): StackInput {\n const k = Math.min(Math.max(0, Math.floor(at)), input.items.length);\n return { ...input, items: [...input.items.slice(0, k), item, ...input.items.slice(k)] };\n}\n\n/** An item drawn: the form it took, its kind and its rect (stack-local px). One in the menu has the menu entry's rect. */\nexport interface PlacedStackItem {\n readonly id: string;\n /** Index into the item's `forms`. */\n readonly form: number;\n readonly kind: StackFormKind;\n readonly rect: Rect;\n}\n\nexport interface StackMenu {\n /** The » entry's rect. */\n readonly rect: Rect;\n /** The items in it, in the stack's order: exactly the items whose kind is `menu`. */\n readonly items: readonly string[];\n}\n\n/**\n * Why a stack looks as it does:\n * - `form-down`: an item took a smaller form than the one it started in (`from`, an index into its forms), by length\n * or by cross size.\n * - `to-menu`: an item is in the » menu.\n * - `too-wide`: an item's form doesn't set the cross size and its `cross.min` is over the stack's. Cross size never\n * puts an item in the menu, so it keeps its last drawn form and the app clips it.\n * - `no-forms`: an item with no forms, left out of `items`.\n * - `overflow`: no item can give more way and the stack is still `over` px too long (forms that don't end in a `menu`\n * form can't give more; neither can a stack where going into the menu would only make it longer).\n */\nexport type StackNote =\n | { readonly code: \"form-down\"; readonly id: string; readonly from: number; readonly to: number }\n | { readonly code: \"to-menu\"; readonly id: string }\n | { readonly code: \"too-wide\"; readonly id: string }\n | { readonly code: \"no-forms\"; readonly id: string }\n | { readonly code: \"overflow\"; readonly over: number };\nexport type StackNoteCode = StackNote[\"code\"];\n\nexport interface StackFit {\n readonly axis: StackAxis;\n readonly length: number;\n /** The stack's size across: the widest `open` (clamped to the limits), or the rail. */\n readonly cross: number;\n /** One per input item that has forms, in order. */\n readonly items: readonly PlacedStackItem[];\n /** The » entry, or null while no item is in the menu. */\n readonly menu: StackMenu | null;\n /** `no-forms` first, then `form-down` per item in the order they gave way, `to-menu`, `too-wide`, and `overflow`. */\n readonly notes: readonly StackNote[];\n}\n","/**\n * `fitStack`: which form each item of a stack takes, and where it goes.\n *\n * 1. Every item starts in its largest form it may take (`forms[from]`, default the first).\n * 2. The stack's cross size is the largest `cross.natural` among the items whose form sets it (an open panel's, by\n * default), clamped to the limits; with none, the rail width.\n * 3. Cross-fit: an item whose form doesn't set the cross size and whose `cross.min` is over it steps down until it\n * fits (a group never sets a column's width, so a group alone becomes a rail of icons). Cross size never steps\n * an item into the menu: when only the menu is left it keeps its last drawn form and a `too-wide` note says so.\n * 4. Give-way: while the smallest sizes (`main.min`) don't fit the length, the item used longest ago steps down one\n * form; unkept before kept, the later in the stack first on a tie. The same item keeps giving way until its forms\n * end, then the next does. A `menu` form takes no room; the » entry does, while any item is in it (and an item\n * doesn't go into the menu when that would only make the stack longer and nothing else can give way).\n * Steps 2 to 4 repeat until nothing changes: a panel folding can narrow the column, which asks more of the groups.\n * 5. The room the items leave goes to the forms that grow, shared by their natural sizes. With none, the items keep\n * their natural sizes from the start of the stack. Between the minimum and natural sizes every item shrinks by the\n * same share of what it can give.\n *\n * The solver has no open or closed state: it lays out the items it is given, and a consumer closes a panel by not\n * passing it (or by starting it at a later form with `from`). An item with no forms is left out (`no-forms`). Pure:\n * same input, same fit.\n */\nimport type { Rect } from \"../geometry\";\nimport { formSetsCross, limitsOf, menuMain, type PlacedStackItem, type StackFit, type StackForm, type StackInput, type StackItem, type StackMenu, type StackNote } from \"./model\";\n\nconst EPS = 1e-6;\nconst isMenu = (f: StackForm): boolean => f.kind === \"menu\";\nconst finite = (x: number, fallback = 0): number => (Number.isFinite(x) ? x : fallback);\nconst mainOf = (f: StackForm, which: \"min\" | \"natural\"): number => (isMenu(f) ? 0 : Math.max(0, finite(f.main[which])));\n/** The least a form can be drawn across the stack. */\nconst crossMin = (f: StackForm): number => finite(f.cross.min ?? f.cross.natural);\n\n/** The item that gives way next: unkept before kept, used longest ago, the later in the stack on a tie. */\nfunction victim(items: readonly StackItem[], at: readonly number[]): number {\n let best = -1;\n for (let i = 0; i < items.length; i++) {\n if (at[i]! + 1 >= items[i]!.forms.length) continue;\n if (best < 0) {\n best = i;\n continue;\n }\n const a = items[i]!;\n const b = items[best]!;\n const ka = a.kept ? 1 : 0;\n const kb = b.kept ? 1 : 0;\n if (ka !== kb ? ka < kb : a.used !== b.used ? a.used < b.used : true) best = i;\n }\n return best;\n}\n\nexport function fitStack(input: StackInput): StackFit {\n const length = Math.max(0, finite(input.length));\n const lim = limitsOf(input);\n const items = input.items.filter((it) => it.forms.length > 0);\n const start = items.map((it) => Math.min(Math.max(0, Math.floor(finite(it.from ?? 0))), it.forms.length - 1));\n const at = [...start];\n const form = (i: number): StackForm => items[i]!.forms[at[i]!]!;\n const idx = items.map((_, i) => i);\n\n const firstStep: number[] = [];\n const menuOrder: number[] = [];\n const step = (i: number): void => {\n if (!firstStep.includes(i)) firstStep.push(i);\n at[i]!++;\n if (isMenu(form(i)) && !menuOrder.includes(i)) menuOrder.push(i);\n };\n // An item that starts in the menu is in it from the start.\n for (const i of idx) if (isMenu(form(i))) menuOrder.push(i);\n\n const crossOf = (): number => {\n const setters = idx.filter((i) => !isMenu(form(i)) && formSetsCross(form(i)));\n if (setters.length === 0) return lim.rail;\n const want = Math.max(...setters.map((i) => finite(form(i).cross.natural)));\n return Math.min(Math.max(want, lim.min), lim.max);\n };\n const inMenu = (): boolean => idx.some((i) => isMenu(form(i)));\n const minSum = (): number => idx.reduce((s, i) => s + mainOf(form(i), \"min\"), 0) + (inMenu() ? menuMain(input) : 0);\n const canStep = (i: number): boolean => at[i]! + 1 < items[i]!.forms.length;\n const tooWide = (i: number, cross: number): boolean => !isMenu(form(i)) && !formSetsCross(form(i)) && crossMin(form(i)) > cross + EPS;\n\n let cross = crossOf();\n let overflow = 0;\n const guard = items.reduce((s, it) => s + it.forms.length, 0) + 1;\n for (let n = 0; n <= guard; n++) {\n cross = crossOf();\n // Too wide for the stack: step down, but never into the menu (that is the length's call).\n let stepped = false;\n for (const i of idx) {\n if (tooWide(i, cross) && canStep(i) && !isMenu(items[i]!.forms[at[i]! + 1]!)) {\n step(i);\n stepped = true;\n }\n }\n if (stepped) continue;\n const over = minSum() - length;\n if (over <= EPS) break;\n const v = victim(items, at);\n if (v < 0) {\n overflow = over;\n break;\n }\n // Into the menu only when that helps, or when something else can still give way.\n const intoMenu = isMenu(items[v]!.forms[at[v]! + 1]!);\n const gain = mainOf(form(v), \"min\") - (inMenu() ? 0 : menuMain(input));\n if (intoMenu && gain < 0 && idx.filter(canStep).length === 1) {\n overflow = over;\n break;\n }\n step(v);\n }\n cross = crossOf();\n\n // Sizes along the stack.\n const menuSize = inMenu() ? menuMain(input) : 0;\n const avail = Math.max(0, length - menuSize);\n const drawn = idx.filter((i) => !isMenu(form(i)));\n const min = (i: number): number => mainOf(form(i), \"min\");\n const nat = (i: number): number => Math.max(min(i), mainOf(form(i), \"natural\"));\n const sumMin = drawn.reduce((s, i) => s + min(i), 0);\n const sumNat = drawn.reduce((s, i) => s + nat(i), 0);\n const size = new Map<number, number>();\n if (sumNat <= avail + EPS) {\n const growers = drawn.filter((i) => form(i).main.grows);\n const spare = Math.max(0, avail - sumNat);\n const weight = growers.reduce((s, i) => s + nat(i), 0);\n for (const i of drawn) {\n const share = growers.includes(i) ? (weight > EPS ? nat(i) / weight : 1 / growers.length) : 0;\n size.set(i, nat(i) + spare * share);\n }\n } else {\n const t = avail > sumMin && sumNat > sumMin ? (avail - sumMin) / (sumNat - sumMin) : 0;\n for (const i of drawn) size.set(i, min(i) + (nat(i) - min(i)) * t);\n }\n\n const rectAt = (from: number, main: number): Rect => (input.axis === \"column\" ? { x: 0, y: from, width: cross, height: main } : { x: from, y: 0, width: main, height: cross });\n const rects = new Map<number, Rect>();\n let cursor = 0;\n for (const i of drawn) {\n const s = size.get(i)!;\n rects.set(i, rectAt(cursor, s));\n cursor += s;\n }\n const menuRect = rectAt(Math.max(cursor, length - menuSize), menuSize);\n const inside = idx.filter((i) => isMenu(form(i)));\n const menu: StackMenu | null = inside.length > 0 ? { rect: menuRect, items: inside.map((i) => items[i]!.id) } : null;\n\n const placed: PlacedStackItem[] = idx.map((i) => ({ id: items[i]!.id, form: at[i]!, kind: form(i).kind, rect: isMenu(form(i)) ? menuRect : rects.get(i)! }));\n\n const notes: StackNote[] = [];\n for (const it of input.items) if (it.forms.length === 0) notes.push({ code: \"no-forms\", id: it.id });\n for (const i of firstStep) if (at[i]! > start[i]!) notes.push({ code: \"form-down\", id: items[i]!.id, from: start[i]!, to: at[i]! });\n for (const i of menuOrder) if (isMenu(form(i))) notes.push({ code: \"to-menu\", id: items[i]!.id });\n for (const i of idx) if (tooWide(i, cross)) notes.push({ code: \"too-wide\", id: items[i]!.id });\n if (overflow > EPS) notes.push({ code: \"overflow\", over: overflow });\n return { axis: input.axis, length, cross, items: placed, menu, notes };\n}\n","/**\n * The drop targets for dragging an item into a stack, in the shape @n-uf/motion/drag takes. `stackTargets(input,\n * dragged)` gives one target for every gap of the stack without the dragged item (an item of the input with its id\n * is lifted out first, so a reorder within a stack works): before each item and after the last, so an empty stack has\n * one. An item with no forms has no place of its own, but its gaps count.\n *\n * - `id` `gap:<k>`, priority 2, `place` `{ kind: \"gap\", at: k }` (k counts the input's items, the dragged one lifted\n * out, including any with no forms; it is not an index into `fit.items`, which leaves those out: look the dragged\n * item up there by its id, as `slot` already does).\n * - `hit`: the gap's zone along the axis, the stack split at its items' middles (an item in the menu has no place of\n * its own: the gaps next to it split the stretch where the stack's drawn items end; with every item in the menu only\n * the last gap, \"at the end\", has any length, and the others are a point at the start). Across, it is the larger of\n * the stack's current size and the size the drop gives it, so the pointer over the preview is over the target.\n * - `fit`: the full `fitStack` of the stack with the dragged item inserted at k, and `input` the stack that was\n * solved. The preview is `slot`, the dragged item's rect in `fit`: exactly the rect it gets after the drop, because\n * the drop is that same solve. `marker` is the line where its leading edge lands: a rect with no thickness across\n * the stack's cross size (the app draws it with one).\n *\n * The dragged item counts as just used: it is newer than every item in the stack, so the others give way before it.\n * A group lands in the form that fits; a panel that would set a wider column makes room by that solve too. A dragged\n * item with no forms has no targets.\n */\nimport { rect, type Rect } from \"../geometry\";\nimport type { Target } from \"../targets/target\";\nimport { fitStack } from \"./layout\";\nimport { withItem, type StackFit, type StackInput, type StackItem } from \"./model\";\n\nexport type StackPlace = { readonly kind: \"gap\"; readonly at: number };\n\nexport interface StackTarget extends Target {\n readonly priority: 2;\n /** The item dragged. */\n readonly item: string;\n readonly place: StackPlace;\n /** The line where the dragged item's leading edge lands. */\n readonly marker: Rect;\n /** The stack with the dragged item inserted: what a drop here makes. */\n readonly input: StackInput;\n /** Its fit: the preview and the drop are both its rect for the dragged item. */\n readonly fit: StackFit;\n}\n\n/** The gap boundaries along the axis: `b[0]` is the start, `b[k]` the middle of item k - 1, `b[n + 1]` the length. */\nfunction bounds(input: StackInput, fit: StackFit): number[] {\n const column = input.axis === \"column\";\n const start = (r: Rect): number => (column ? r.y : r.x);\n const size = (r: Rect): number => (column ? r.height : r.width);\n const placed = new Map(fit.items.map((p) => [p.id, p]));\n const b = [0];\n // Where the drawn items have ended so far: an item in the menu (or with no forms) sits there.\n let end = 0;\n for (const it of input.items) {\n const p = placed.get(it.id);\n if (!p || p.kind === \"menu\") {\n b.push(end);\n continue;\n }\n b.push(start(p.rect) + size(p.rect) / 2);\n end = start(p.rect) + size(p.rect);\n }\n b.push(fit.length);\n // Monotone, whatever the sizes did (a stack that overflows).\n for (let i = 1; i < b.length; i++) b[i] = Math.min(Math.max(b[i]!, b[i - 1]!), fit.length);\n return b;\n}\n\nexport function stackTargets(input: StackInput, dragged: StackItem): StackTarget[] {\n if (dragged.forms.length === 0) return [];\n const rest: StackInput = { ...input, items: input.items.filter((it) => it.id !== dragged.id) };\n const base = fitStack(rest);\n const b = bounds(rest, base);\n const column = rest.axis === \"column\";\n const newest = rest.items.reduce((m, it) => Math.max(m, it.used), dragged.used) + 1;\n const item: StackItem = { ...dragged, used: newest };\n const out: StackTarget[] = [];\n for (let k = 0; k <= rest.items.length; k++) {\n const solved = withItem(rest, item, k);\n const fit = fitStack(solved);\n const slot = fit.items.find((p) => p.id === dragged.id)!.rect;\n const from = b[k]!;\n const to = b[k + 1]!;\n const across = Math.max(base.cross, fit.cross);\n out.push({\n id: `gap:${k}`,\n priority: 2,\n item: dragged.id,\n place: { kind: \"gap\", at: k },\n hit: column ? rect(0, from, across, to - from) : rect(from, 0, to - from, across),\n slot,\n marker: column ? rect(slot.x, slot.y, slot.width, 0) : rect(slot.x, slot.y, 0, slot.height),\n input: solved,\n fit,\n });\n }\n return out;\n}\n","/**\n * The stack's stored form: `storeStack(state)` gives plain JSON-safe data (`{ v: 1, order, kept, closed, used, forms\n * }`); `readStack(data, known?)` reads parsed data back and never throws. Anything but an object of version 1 is\n * `{ ok: false, error }`. Inside one it is lenient, as the panels' reader is: an id that isn't a string, is repeated\n * or (with `known`) isn't one the consumer has is dropped and listed in `dropped`; a `kept`, `closed`, `used` or\n * `forms` entry for an id that isn't in `order` is dropped; a form that isn't a whole number from 1 up is dropped;\n * the key `__proto__` in `forms` is dropped (the other keys, `constructor` and `prototype` included, are ids like any\n * other: `forms` is an object with no prototype, written and read); a field this version doesn't know is skipped and\n * reported as `\"<field>\"`. An item the consumer has that the stored order lacks is no error: the consumer places it.\n *\n * - `kept`: items kept open (they give way last).\n * - `closed`: items the user closed; their place in `order` is kept for when they open.\n * - `used`: the use order, oldest to newest, so a reload restores who gives way first (an item's `used` is its index in\n * it; one the list lacks is older than all). An id listed twice counts at its last place: the newest use wins.\n * - `forms`: the user's chosen form per item, the index of the largest form it may take (`StackItem.from`). 0, the\n * first, is not stored.\n */\n\nexport interface StackState {\n /** The items' order along the stack. */\n readonly order: readonly string[];\n /** Items kept open: they give way last. */\n readonly kept: readonly string[];\n /** Items the user closed. */\n readonly closed: readonly string[];\n /** The use order, oldest first. */\n readonly used: readonly string[];\n /** The form index the user chose per item (only those that chose one; absent means the first). */\n readonly forms: Readonly<Record<string, number>>;\n}\n\nexport interface StoredStack {\n readonly v: 1;\n readonly order: string[];\n readonly kept: string[];\n readonly closed: string[];\n readonly used: string[];\n readonly forms: Record<string, number>;\n}\n\nexport type StackRead = { readonly ok: true; readonly state: StackState; readonly dropped: readonly string[] } | { readonly ok: false; readonly error: string };\n\n/** A state with these items in this order, none kept or closed, used in that order, every form the first. */\nexport const createStack = (order: readonly string[]): StackState => ({ order: [...order], kept: [], closed: [], used: [...order], forms: {} });\n\nconst isForm = (f: unknown): f is number => typeof f === \"number\" && Number.isInteger(f) && f >= 1;\n/** Ids that are in `order`, each once, at the first place it was listed. */\nconst uniq = (ids: readonly string[], order: readonly string[]): string[] => [...new Set(ids)].filter((id) => order.includes(id));\n/** Ids that are in `order`, each once, at the last place it was listed (the newest use wins). */\nconst uniqLast = (ids: readonly string[], order: readonly string[]): string[] => ids.filter((id, i) => order.includes(id) && ids.lastIndexOf(id) === i);\n/** Forms live in an object with no prototype, so no key (`constructor`, `prototype`) is more than a key; only `__proto__` is refused. */\nconst noForms = (): Record<string, number> => Object.create(null) as Record<string, number>;\n\nexport function storeStack(state: StackState): StoredStack {\n const forms = noForms();\n for (const id of state.order) {\n const f = state.forms[id];\n if (id !== \"__proto__\" && Object.hasOwn(state.forms, id) && isForm(f)) forms[id] = f;\n }\n return { v: 1, order: [...state.order], kept: uniq(state.kept, state.order), closed: uniq(state.closed, state.order), used: uniqLast(state.used, state.order), forms };\n}\n\nconst isObject = (x: unknown): x is Record<string, unknown> => typeof x === \"object\" && x !== null && !Array.isArray(x);\n\nexport function readStack(data: unknown, known?: readonly string[]): StackRead {\n try {\n return read(data, known);\n } catch (e) {\n // A getter that throws, a proxy: still no throw.\n return { ok: false, error: `unreadable: ${e instanceof Error ? e.message : typeof e}` };\n }\n}\n\nconst versionWords = (v: unknown): string => (typeof v === \"number\" ? String(v) : typeof v === \"string\" ? JSON.stringify(v.length > 24 ? `${v.slice(0, 24)}…` : v) : typeof v);\n\nfunction read(data: unknown, known?: readonly string[]): StackRead {\n if (!isObject(data)) return { ok: false, error: \"not an object\" };\n if (data.v !== 1) return { ok: false, error: `unknown version ${versionWords(data.v)} (this reads version 1)` };\n if (!Array.isArray(data.order)) return { ok: false, error: \"order is missing\" };\n const dropped: string[] = [];\n const drop = (what: string): void => {\n if (!dropped.includes(what)) dropped.push(what);\n };\n for (const k of Object.keys(data)) if (![\"v\", \"order\", \"kept\", \"closed\", \"used\", \"forms\"].includes(k)) drop(k);\n const order: string[] = [];\n for (const x of data.order as unknown[]) {\n if (typeof x !== \"string\") drop(`a ${typeof x} where an id goes`);\n else if ((known && !known.includes(x)) || order.includes(x)) drop(x);\n else order.push(x);\n }\n // A list of ids that are in `order`, each once: at the first place it was listed, or for `used` the last (the newest use wins).\n const list = (key: \"kept\" | \"closed\" | \"used\"): string[] => {\n const out: string[] = [];\n const x = data[key];\n if (x === undefined) return out;\n if (!Array.isArray(x)) {\n drop(key);\n return out;\n }\n for (const id of x as unknown[]) {\n if (typeof id === \"string\" && order.includes(id)) {\n if (key === \"used\" && out.includes(id)) out.splice(out.indexOf(id), 1);\n if (!out.includes(id)) out.push(id);\n } else drop(typeof id === \"string\" ? `${key}:${id}` : `a ${typeof id} in ${key}`);\n }\n return out;\n };\n const kept = list(\"kept\");\n const closed = list(\"closed\");\n const used = list(\"used\");\n const forms = noForms();\n if (data.forms !== undefined) {\n if (!isObject(data.forms)) drop(\"forms\");\n else {\n for (const id of Object.keys(data.forms)) {\n const f = data.forms[id];\n if (id === \"__proto__\" || !Object.hasOwn(data.forms, id) || !order.includes(id) || !isForm(f)) drop(`forms:${id}`);\n else forms[id] = f;\n }\n }\n }\n return { ok: true, state: { order, kept, closed, used, forms }, dropped };\n}\n"],"mappings":"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","names":["finite","cross"]}
@@ -0,0 +1,17 @@
1
+ import { R as Rect } from './geometry-mL-GTRti.js';
2
+
3
+ /**
4
+ * A drop target in the shape @n-uf/motion/drag takes: an id, a priority (1 most precise: a rail or a tab strip;
5
+ * 2 a slot; 3 a broad area such as floating anywhere), the zone the pointer hits, and optionally where the item
6
+ * lands, a label and whether it lands freely (no room is made for it).
7
+ */
8
+ interface Target {
9
+ readonly id: string;
10
+ readonly priority: 1 | 2 | 3;
11
+ readonly hit: Rect;
12
+ readonly slot?: Rect;
13
+ readonly label?: string;
14
+ readonly free?: boolean;
15
+ }
16
+
17
+ export type { Target as T };
@@ -1,4 +1,5 @@
1
1
  import { S as Size, R as Rect, P as Point } from './geometry-mL-GTRti.js';
2
+ import { T as Target } from './target-D3PiLg44.js';
2
3
 
3
4
  /**
4
5
  * The panels model: plain data and the pure changes on it.
@@ -492,20 +493,6 @@ declare function giveWayOrder<C extends {
492
493
  }>(cols: readonly C[], order: "oldest" | "newest"): C[];
493
494
  declare function layoutPanels(defs: readonly PanelDef[], state: PanelsState, stage: Size, options?: Partial<PanelsOptions>): PanelsLayout;
494
495
 
495
- /**
496
- * A drop target in the shape @n-uf/motion/drag takes: an id, a priority (1 most precise: a rail or a tab strip;
497
- * 2 a slot; 3 a broad area such as floating anywhere), the zone the pointer hits, and optionally where the item
498
- * lands, a label and whether it lands freely (no room is made for it).
499
- */
500
- interface Target {
501
- readonly id: string;
502
- readonly priority: 1 | 2 | 3;
503
- readonly hit: Rect;
504
- readonly slot?: Rect;
505
- readonly label?: string;
506
- readonly free?: boolean;
507
- }
508
-
509
496
  /**
510
497
  * The drop targets for dragging a panel, in the shape @n-uf/motion/drag takes (`{ id, priority, hit, slot?, free? }`),
511
498
  * worked out from the state with the panel lifted out:
@@ -633,4 +620,4 @@ interface PanelStopsOptions {
633
620
  /** The panel's places in reading order, for the arrow keys and a "Move to" menu (`keyboardStops` calls this). */
634
621
  declare function panelStops(targets: readonly PanelTarget[], options?: PanelStopsOptions): PanelStop[];
635
622
 
636
- export { floatSize as $, type PlacedPanel as A, BAND_SIZE as B, type Clock as C, DEFAULT_PANELS_OPTIONS as D, type Edge as E, FLOAT_HEIGHT as F, type GiveWay as G, type PlacedRail as H, IDENTITY_VIEW as I, type Reserve as J, type ReserveEdge as K, SIDES as L, activate as M, bandHeight as N, bandSize as O, type PanelsOptions as P, close as Q, RAIL_EDGE as R, type Side as S, columnWidth as T, createPanels as U, type View as V, type Where as W, defOf as X, dock as Y, dockBand as Z, float as _, type PanelDef as a, focus as a0, giveWayOrder as a1, homePlace as a2, keep as a3, liftPanel as a4, open as a5, ownWidth as a6, panelStops as a7, panelTargets as a8, pin as a9, placeRank as aa, resizeBand as ab, resizeColumn as ac, resizeFloat as ad, samePlaces as ae, showTabs as af, tick as ag, toPage as ah, toView as ai, toggleFold as aj, whereIs as ak, type Target as al, type PanelsState as b, type PanelTargetInput as c, type PanelTarget as d, type PanelDrop as e, type PanelsLayout as f, type Band as g, type BandOption as h, COLUMN_REACH as i, type Column as j, type DockOptions as k, layoutPanels as l, EDGES as m, FLOAT_MIN as n, type FloatAt as o, panelDrop as p, type PanelKind as q, type PanelPlace as r, type PanelStop as s, type PanelStopsOptions as t, type PanelsNote as u, type PanelsNoteCode as v, type PanelsStart as w, type Place as x, type PlacedBand as y, type PlacedColumn as z };
623
+ export { floatSize as $, type PlacedPanel as A, BAND_SIZE as B, type Clock as C, DEFAULT_PANELS_OPTIONS as D, type Edge as E, FLOAT_HEIGHT as F, type GiveWay as G, type PlacedRail as H, IDENTITY_VIEW as I, type Reserve as J, type ReserveEdge as K, SIDES as L, activate as M, bandHeight as N, bandSize as O, type PanelsOptions as P, close as Q, RAIL_EDGE as R, type Side as S, columnWidth as T, createPanels as U, type View as V, type Where as W, defOf as X, dock as Y, dockBand as Z, float as _, type PanelDef as a, focus as a0, giveWayOrder as a1, homePlace as a2, keep as a3, liftPanel as a4, open as a5, ownWidth as a6, panelStops as a7, panelTargets as a8, pin as a9, placeRank as aa, resizeBand as ab, resizeColumn as ac, resizeFloat as ad, samePlaces as ae, showTabs as af, tick as ag, toPage as ah, toView as ai, toggleFold as aj, whereIs as ak, type PanelsState as b, type PanelTargetInput as c, type PanelTarget as d, type PanelDrop as e, type PanelsLayout as f, type Band as g, type BandOption as h, COLUMN_REACH as i, type Column as j, type DockOptions as k, layoutPanels as l, EDGES as m, FLOAT_MIN as n, type FloatAt as o, panelDrop as p, type PanelKind as q, type PanelPlace as r, type PanelStop as s, type PanelStopsOptions as t, type PanelsNote as u, type PanelsNoteCode as v, type PanelsStart as w, type Place as x, type PlacedBand as y, type PlacedColumn as z };
package/dist/targets.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { al as Target, d as PanelTarget, t as PanelStopsOptions, s as PanelStop } from './targets-B_U2Zf7x.js';
1
+ import { T as Target } from './target-D3PiLg44.js';
2
2
  import { R as Rect, P as Point, S as Size } from './geometry-mL-GTRti.js';
3
3
  import { D as DeskState, g as Layout, a as Construction, O as Options } from './constructions-VDK_mwmz.js';
4
4
  import { Place, Arrangement, HostId, HostSpec, GroupSize } from './toolbar.js';
5
+ import { d as PanelTarget, t as PanelStopsOptions, s as PanelStop } from './targets-B6eLC-52.js';
5
6
 
6
7
  /**
7
8
  * Each desk construction's drop targets, as pure functions: from the state with the dragged item lifted out, a list
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@n-uf/placement",
3
- "version": "26.11.11",
3
+ "version": "26.11.12",
4
4
  "description": "Where things go: desk docks, rails and palettes, panel columns, toolbar groups, and the drop targets for each. Pure state in, rects out. No DOM, no framework, no motion.",
5
5
  "keywords": [
6
6
  "layout",
@@ -52,6 +52,10 @@
52
52
  "types": "./dist/desk.d.ts",
53
53
  "default": "./dist/desk.js"
54
54
  },
55
+ "./stack": {
56
+ "types": "./dist/stack.d.ts",
57
+ "default": "./dist/stack.js"
58
+ },
55
59
  "./package.json": "./package.json"
56
60
  },
57
61
  "publishConfig": {