@n-uf/placement 26.11.6 → 26.11.8
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 +48 -0
- package/README.md +117 -11
- package/dist/chunk-QR7MGDX6.js +448 -0
- package/dist/chunk-QR7MGDX6.js.map +1 -0
- package/dist/panels.d.ts +45 -0
- package/dist/panels.js +195 -0
- package/dist/panels.js.map +1 -0
- package/dist/targets-DqhZf3XZ.d.ts +454 -0
- package/dist/targets.d.ts +7 -15
- package/dist/targets.js +13 -5
- package/dist/targets.js.map +1 -1
- package/package.json +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,54 @@
|
|
|
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.8
|
|
7
|
+
|
|
8
|
+
- `@n-uf/placement/panels`:
|
|
9
|
+
- Folded floats: a float folded by hand lays out as a title bar (`kind` `"bar"`, `folded` `"hand"`) at its own x, y
|
|
10
|
+
and width, `options.bar` tall, clamped as a bar, pinned or not. It is still a float to targets and drags; a drop
|
|
11
|
+
moves it and keeps its full size, and unfolding brings the full rect back (the stored rect never changes). A
|
|
12
|
+
folded docked panel dropped on the float target stays folded: it lands as a title bar at its natural float size.
|
|
13
|
+
- `options.fold`: `"rail"` (default, as before) or `"ride"`. Riding, a column folded by hand on a side that still
|
|
14
|
+
has an open column becomes title bars stacked on top of the side's first open column (`rides`: its index), as
|
|
15
|
+
wide as it; the open columns lie as if the folded one were gone, and its stored place and width are untouched.
|
|
16
|
+
When every column on the side is folded, the side shows its rail as before. Give-way's folds always go to the rail.
|
|
17
|
+
- `PlacedPanel` gains `floating` (open or folded float) and `rides`; `PlacedColumn` gains `rides`.
|
|
18
|
+
- `pin(…, shown)` takes only the position of `shown` and keeps the stored size, so pinning a folded or clamped
|
|
19
|
+
float keeps its full size.
|
|
20
|
+
- A column too short for its bars gives its open panels a height of 0, never a negative one.
|
|
21
|
+
|
|
22
|
+
## 26.11.7
|
|
23
|
+
|
|
24
|
+
- New entry `@n-uf/placement/panels`: tool panels with string ids, as an app's side columns work. `./dock` is
|
|
25
|
+
unchanged.
|
|
26
|
+
- Model: `PanelDef` (width limits, float size limits, the sides it may dock on, whether it may float; no title:
|
|
27
|
+
words are the consumer's),
|
|
28
|
+
`PanelsState` (`sides` of full-height `Column`s from the outer edge inward, `floats`, `folded`, `kept`,
|
|
29
|
+
`closed`, a logical `clock` of opened and used), `createPanels(defs, start)`.
|
|
30
|
+
- `layoutPanels(defs, state, stage, options?)`: geometry and structure only. Each panel's rect, layer, `kind`
|
|
31
|
+
(`column`, `bar`, `segment`, `float`), folded, kept and pinned flags; the columns; the rails; the pages' rect;
|
|
32
|
+
notes as a `code` plus data (`column-shrunk`, `column-folded`, `kept-folded`, `pages-under-min`,
|
|
33
|
+
`float-resized`, `float-clamped`). Options: `giveWay` (`{ order: "oldest" | "newest", by: "used" | "opened" }`,
|
|
34
|
+
default oldest by use), `floats` (`"clamp"` or `"free"`), `floatVisible`, `pagesMin`, `view` (the page's offset
|
|
35
|
+
and scale), and the chrome sizes `bar`, `rail`, `segment`, `railInset`.
|
|
36
|
+
- Give-way shrinks every column toward its minimum, then folds whole columns to the rail: unkept columns first, in
|
|
37
|
+
policy order across both sides, then columns holding a kept panel, only when the pages still don't fit. It never
|
|
38
|
+
writes `folded`, so a wider stage brings a column back.
|
|
39
|
+
- Floats clamp at layout time (the state keeps the stored rect) so their screen rect stays in the view at any
|
|
40
|
+
scale: a pinned float where it is, an unpinned one as it shows at pan 0, so it still scrolls with the page.
|
|
41
|
+
- Changes: `open`, `close`, `focus`, `toggleFold`, `keep`, `dock`, `float`, `pin` (with `shown`, a clamped float
|
|
42
|
+
pins where it shows), `resizeColumn`, `resizeFloat`, `liftPanel`. Opening, focusing, moving, resizing and
|
|
43
|
+
unfolding are uses (they move the `used` clock). `samePlaces(a, b, defs?)` compares widths as laid out;
|
|
44
|
+
`columnWidth(defs, column)`.
|
|
45
|
+
- Targets: `panelTargets(input)` (`column:<side>:<i>`, `into:<side>:<i>`, `rail:<side>`, `float`) in the shape
|
|
46
|
+
`@n-uf/motion/drag` takes, each with a structured `place` and no words; a place the panel may not take and its
|
|
47
|
+
own place are no targets. `panelDrop(input, target, point, grab)` lays out a drop. `homePlace`, `panelStops`.
|
|
48
|
+
- `storePanels(state)` / `readPanels(data, defs?)`: a versioned stored form (`{ v: 1, … }`); reading never throws,
|
|
49
|
+
drops unknown or repeated ids and clamps to the defs. Version 1 only gains optional fields: a field the reader
|
|
50
|
+
doesn't know is skipped and listed in `dropped`.
|
|
51
|
+
- `@n-uf/placement/targets`: `keyboardStops` also takes panel targets (with `{ home: { place, state }, floats? }`)
|
|
52
|
+
and gives panel stops in reading order.
|
|
53
|
+
|
|
6
54
|
## 26.11.6
|
|
7
55
|
|
|
8
56
|
- `@n-uf/placement/targets`:
|
package/README.md
CHANGED
|
@@ -3,14 +3,16 @@
|
|
|
3
3
|
> [!CAUTION]
|
|
4
4
|
> ## Not yet stable
|
|
5
5
|
>
|
|
6
|
-
> **@n-uf/placement is young and still changing. Its API (the desk constructions, the toolbar model and the drop targets) is not final.**
|
|
6
|
+
> **@n-uf/placement is young and still changing. Its API (the desk constructions, the panel columns, the toolbar model and the drop targets) is not final.**
|
|
7
7
|
>
|
|
8
8
|
> Version numbers are dates (YY.M.R), not semantic versions, so any release may break things. Pin an exact version and read the [CHANGELOG](https://github.com/n-uf/motion/blob/main/packages/placement/CHANGELOG.md) before you upgrade.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
Site and live demos: [motion.n-uf.com](https://motion.n-uf.com)
|
|
11
|
+
|
|
12
|
+
Where things go. State in, rects and drop targets out: a desk of docks, rails and palettes, panels in columns per
|
|
13
|
+
side that give way by age, toolbar groups in hosts and zones, and the drop targets for each. A tiling entry (a split
|
|
14
|
+
tree with ratios, groups and workspaces) is planned. Pure functions over plain data: no DOM, no framework, no
|
|
15
|
+
motion, no dependencies.
|
|
14
16
|
|
|
15
17
|
```sh
|
|
16
18
|
npm i @n-uf/placement
|
|
@@ -20,17 +22,21 @@ Its sibling [`@n-uf/motion`](https://www.npmjs.com/package/@n-uf/motion) owns ho
|
|
|
20
22
|
without importing each other: a target list from `@n-uf/placement/targets` is exactly what
|
|
21
23
|
`@n-uf/motion/drag`'s drag intent takes, and a rect from any layout here is what `@n-uf/motion/layout` glides to.
|
|
22
24
|
|
|
23
|
-
- **`@n-uf/placement`**: the shapes (`Rect`, `Size`, `Point`) and a popup positioner
|
|
24
|
-
then flip, slide
|
|
25
|
+
- **`@n-uf/placement`**: the shapes (`Rect`, `Size`, `Point`) and a popup positioner, as Wayland's xdg-positioner: an anchor
|
|
26
|
+
point, gravity and offset, then flip, slide and resize, each only when the rule allows it.
|
|
25
27
|
- **`/dock`**: a desk of documents and tool panels. Side docks and a bottom dock (between the side docks or across
|
|
26
28
|
the full width), rails, natural-height sections whose last one takes the rest, fold and unfold, the give-way
|
|
27
29
|
rules (sections fold least recently used first, docks fold to their rails), floating palettes with corner stacks,
|
|
28
30
|
tear-off and dock-back; and nine constructions built from these parts, as presets (`createDesk`).
|
|
29
31
|
- **`/toolbar`**: toolbar groups in five hosts (title, top, bottom, left, right) and three zones (start, centre,
|
|
30
|
-
end), floating groups, insertion points (before all, between, after all, an empty zone),
|
|
31
|
-
and "own place is home".
|
|
32
|
-
- **`/targets`**: drop targets (`{ id, priority, hit, slot?, label?, free? }`) for the desk's constructions
|
|
33
|
-
toolbar hosts
|
|
32
|
+
end), floating groups, insertion points (before all, between, after all, an empty zone), collapse when a host
|
|
33
|
+
runs out of room (lowest keep first, trailing groups first among equal keeps), and "own place is home".
|
|
34
|
+
- **`/targets`**: drop targets (`{ id, priority, hit, slot?, label?, free? }`) for the desk's constructions,
|
|
35
|
+
toolbar hosts (each with its `slot`, and `allows` to leave out places a group may not take) and pane zones; and
|
|
36
|
+
`keyboardStops`, one ordered list of places for the arrow keys and a "Move to" menu.
|
|
37
|
+
- **`/panels`**: tool panels with string ids: full-height columns per side, each with its own width; folding to a
|
|
38
|
+
title bar or a rail; give-way by age when the pages run out of room; resizable floats, some pinned to the view;
|
|
39
|
+
their drop targets; and a stored form.
|
|
34
40
|
|
|
35
41
|
Rendering stays in the app: it draws the rects, measures its groups, and feeds pointer samples to a drag intent.
|
|
36
42
|
|
|
@@ -149,6 +155,106 @@ const stops = keyboardStops(targets, { home: { place: hostOf(a, "share")!, arran
|
|
|
149
155
|
console.log(stops.map((s) => (s.home ? "home" : s.id))); // [ 'g:top:start:0', 'g:top:start:1', 'g:top:start:2', 'g:top:center:0', 'home' ]
|
|
150
156
|
```
|
|
151
157
|
|
|
158
|
+
## Panels
|
|
159
|
+
|
|
160
|
+
`@n-uf/placement/panels` lays out tool panels the way an app's side columns work: each side holds full-height
|
|
161
|
+
columns from the outer edge inward, each column one or more panels and its own width (clamped to its panels'
|
|
162
|
+
`min` and `max`). A panel folded by hand is a title bar; a column whose panels are all folded becomes segments of
|
|
163
|
+
its side's rail, or with `fold: "ride"` bars on the side's first open column. Panels may float instead, in page space
|
|
164
|
+
(they move with the page's offset and scale) or pinned to the view; a folded float is a title bar where it floats, and
|
|
165
|
+
a folded panel torn off stays folded. The output is geometry and structure only: each panel's rect, layer, `kind`
|
|
166
|
+
(`column`, `bar`, `segment`, `float`; a folded float is a `bar`) and its folded, kept, pinned, floating and rides
|
|
167
|
+
flags, the columns, the rails, the pages' rect and coded notes. Chrome sizes
|
|
168
|
+
(`bar`, `rail`, `segment`) are options.
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { createPanels, keep, layoutPanels, readPanels, storePanels, type PanelDef } from "@n-uf/placement/panels";
|
|
172
|
+
|
|
173
|
+
const defs: PanelDef[] = [
|
|
174
|
+
{ id: "files", width: { min: 180, natural: 220, max: 360 } },
|
|
175
|
+
{ id: "outline", width: { min: 160, natural: 200, max: 320 } },
|
|
176
|
+
{ id: "notes", width: { min: 200, natural: 260, max: 420 } },
|
|
177
|
+
];
|
|
178
|
+
// Files was used first, so it is the oldest.
|
|
179
|
+
const state = createPanels(defs, { left: [["files"], ["outline"]], right: [["notes"]] });
|
|
180
|
+
const narrow = layoutPanels(defs, state, { width: 780, height: 600 });
|
|
181
|
+
console.log(narrow.notes.map((n) => n.code), narrow.items.get("files")!.kind); // [ 'column-shrunk', 'column-folded' ] segment
|
|
182
|
+
const kept = layoutPanels(defs, keep(state, "files", true), { width: 780, height: 600 });
|
|
183
|
+
console.log(kept.items.get("files")!.kind, kept.items.get("outline")!.kind); // column segment
|
|
184
|
+
const read = readPanels(JSON.parse(JSON.stringify(storePanels(state))), defs);
|
|
185
|
+
console.log(read.ok); // true
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Give-way** (`options.giveWay`, default `{ order: "oldest", by: "used" }`): when the open columns, the rails and
|
|
189
|
+
`pagesMin` (default 320) don't fit the stage width, columns shrink toward their `min`, then whole columns fold to the
|
|
190
|
+
rail, in the policy's order, until the pages fit. A column's age is its newest panel's (`used` counts opening,
|
|
191
|
+
focusing, moving, resizing and unfolding; `opened` only opening). Ties go to the outer column, then the left side.
|
|
192
|
+
Every column shrinks before any folds; then the unkept columns fold before any column holding a kept panel
|
|
193
|
+
(`keep(state, id, true)`), each group in the policy's order across both sides. When only kept columns are left and the
|
|
194
|
+
pages still don't fit, they fold too. Give-way never writes `folded`, so widening the stage brings a column back.
|
|
195
|
+
|
|
196
|
+
**Floats** (`options.floats`, default `"clamp"`): a float that fits the view stays wholly inside it; one that doesn't
|
|
197
|
+
even at its minimum size keeps its title bar and `floatVisible` (default 48) px of its width in the view. A float's
|
|
198
|
+
size is screen px and never scales. A pinned float is clamped where it is; an unpinned one has its page-space position
|
|
199
|
+
clamped so that its screen rect at pan 0 fits the view, so it still scrolls with the page. The clamp runs at layout
|
|
200
|
+
time and the state keeps the stored rect, so a larger view brings a float back where it was. `"free"` never moves a
|
|
201
|
+
float. A float folded by hand (`toggleFold`) lays out as a title bar: `kind` `"bar"`, `folded` `"hand"`, at its own x,
|
|
202
|
+
y and width and `options.bar` tall, clamped as a bar. It is still a float (`floating: true`) to its targets and drags,
|
|
203
|
+
pinned or not, and unfolding brings its full rect back: the stored rect never changes.
|
|
204
|
+
|
|
205
|
+
**Folded columns** (`options.fold`, default `"rail"`): a column folded by hand goes to its side's rail. With `"ride"`,
|
|
206
|
+
it rides instead while its side still has an open column: its panels become title bars (`kind` `"bar"`, `rides`: the
|
|
207
|
+
host's index) stacked in column order on top of the side's first open column (outer edge first), as wide as it, and
|
|
208
|
+
the host's open panels share the rest of the height. The open columns lie as if the folded one were gone (the pages
|
|
209
|
+
gain its room), and its stored place and width are untouched, so unfolding puts it back as its own column. Only when
|
|
210
|
+
every column on a side is folded does the side show its rail. Give-way's folds (`column-folded`, `kept-folded`) go to
|
|
211
|
+
the rail in both modes.
|
|
212
|
+
|
|
213
|
+
**Changes** are pure: `open`, `close`, `focus`, `toggleFold`, `keep`, `dock(state, id, side, index, { into?, width? })`,
|
|
214
|
+
`float`, `pin(state, id, on, view?, shown?)` (pass the layout's rect as `shown` so a clamped float pins where it
|
|
215
|
+
shows), `resizeColumn`, `resizeFloat`.
|
|
216
|
+
|
|
217
|
+
**Drop targets**: `panelTargets({ defs, state, id, stage, options?, allows? })` gives `column:<side>:<i>` (a new
|
|
218
|
+
column as wide as the panel's own width), `into:<side>:<i>` (stack into a column), `rail:<side>` (dock folded) and
|
|
219
|
+
`float` (free), in the shape `@n-uf/motion/drag`'s `createDragIntent` takes. Each carries a structured `place`, not
|
|
220
|
+
words. A place the panel may not take (its def's `sides` and `floats`, or `allows`) and its own place are no targets.
|
|
221
|
+
`panelDrop(input, target, point, grab)` lays out the drop: its `slot` is where the panel lands. `keyboardStops` from
|
|
222
|
+
`@n-uf/placement/targets` puts panel targets in reading order, with `homePlace(state, id)` as `home`:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { createPanels, homePlace, panelDrop, panelTargets, type PanelDef } from "@n-uf/placement/panels";
|
|
226
|
+
import { keyboardStops } from "@n-uf/placement/targets";
|
|
227
|
+
|
|
228
|
+
const defs: PanelDef[] = [
|
|
229
|
+
{ id: "files", width: { min: 180, natural: 220, max: 360 } },
|
|
230
|
+
{ id: "outline", width: { min: 160, natural: 200, max: 320 }, floats: false },
|
|
231
|
+
];
|
|
232
|
+
const state = createPanels(defs, { left: [["files"], ["outline"]] });
|
|
233
|
+
const input = { defs, state, id: "outline", stage: { width: 1200, height: 600 } };
|
|
234
|
+
const targets = panelTargets(input); // hand these to createDragIntent
|
|
235
|
+
console.log(targets.map((t) => t.id)); // [ 'rail:left', 'column:left:0', 'into:left:0', 'column:right:0', 'rail:right' ]
|
|
236
|
+
const drop = panelDrop(input, targets[3]!, { x: 1190, y: 300 }, { x: 0, y: 0 });
|
|
237
|
+
console.log(drop.slot); // { x: 1000, y: 0, width: 200, height: 600 }
|
|
238
|
+
const stops = keyboardStops(targets, { home: { place: homePlace(state, "outline")!, state } });
|
|
239
|
+
console.log(stops.map((s) => (s.home ? "home" : s.id))); // [ 'rail:left', 'column:left:0', 'into:left:0', 'home', 'column:right:0', 'rail:right' ]
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`storePanels(state)` gives `{ v: 1, sides, floats, folded, kept, closed, clock }`; `readPanels(data, defs?)` reads
|
|
243
|
+
parsed data back and never throws. Anything but an object of version 1 is `{ ok: false, error }`; inside one, unknown
|
|
244
|
+
or repeated ids and malformed entries are dropped and listed in `dropped`, and widths and float sizes are clamped to
|
|
245
|
+
the defs. Version 1 only gains optional fields: a field this reader doesn't know is skipped and listed in `dropped`
|
|
246
|
+
too (`"theme"`, `"sides.bottom"`, `"column.<field>"`, `"float.<field>"`, `"clock.<id>"`). The note codes
|
|
247
|
+
(`PanelsNoteCode`):
|
|
248
|
+
|
|
249
|
+
| Code | Says | Also |
|
|
250
|
+
|---|---|---|
|
|
251
|
+
| `column-shrunk` | a column narrowed toward its minimum | `side`, `column`, `items`, `from`, `to` |
|
|
252
|
+
| `column-folded` | a column folded to its side's rail | `side`, `column`, `items` |
|
|
253
|
+
| `kept-folded` | a kept column folded: the pages needed the room | `side`, `column`, `items` |
|
|
254
|
+
| `pages-under-min` | the pages are under `pagesMin` with every column folded | `width`, `min` |
|
|
255
|
+
| `float-resized` | a float shrank to fit the view | `items` |
|
|
256
|
+
| `float-clamped` | a float moved to stay in the view | `items` |
|
|
257
|
+
|
|
152
258
|
## Pane zones
|
|
153
259
|
|
|
154
260
|
`paneZone(rect, point, previous, options?)` says which of a pane's five zones a point is in: the centre rectangle
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import {
|
|
2
|
+
rect
|
|
3
|
+
} from "./chunk-QMTNB2C3.js";
|
|
4
|
+
|
|
5
|
+
// src/panels/model.ts
|
|
6
|
+
var SIDES = ["left", "right"];
|
|
7
|
+
var IDENTITY_VIEW = { x: 0, y: 0, scale: 1 };
|
|
8
|
+
var DEFAULT_PANELS_OPTIONS = {
|
|
9
|
+
giveWay: { order: "oldest", by: "used" },
|
|
10
|
+
floats: "clamp",
|
|
11
|
+
fold: "rail",
|
|
12
|
+
floatVisible: 48,
|
|
13
|
+
pagesMin: 320,
|
|
14
|
+
view: IDENTITY_VIEW,
|
|
15
|
+
bar: 28,
|
|
16
|
+
rail: 36,
|
|
17
|
+
segment: 36,
|
|
18
|
+
railInset: 4
|
|
19
|
+
};
|
|
20
|
+
var FLOAT_HEIGHT = 360;
|
|
21
|
+
var FLOAT_MIN = { width: 160, height: 120 };
|
|
22
|
+
var without = (list, x) => list.filter((y) => y !== x);
|
|
23
|
+
var withOnce = (list, x) => list.includes(x) ? [...list] : [...list, x];
|
|
24
|
+
function whereIs(state, id) {
|
|
25
|
+
for (const side of SIDES) {
|
|
26
|
+
const index = state.sides[side].findIndex((c) => c.panels.includes(id));
|
|
27
|
+
if (index >= 0) return { kind: "column", side, index };
|
|
28
|
+
}
|
|
29
|
+
const float2 = state.floats.find((f) => f.id === id);
|
|
30
|
+
return float2 ? { kind: "float", float: float2 } : { kind: "none" };
|
|
31
|
+
}
|
|
32
|
+
var tick = (state) => Object.values(state.clock).reduce((m, c) => Math.max(m, c.opened, c.used), 0) + 1;
|
|
33
|
+
var bump = (state, id, opened) => {
|
|
34
|
+
const t = tick(state);
|
|
35
|
+
const was = state.clock[id] ?? { opened: 0, used: 0 };
|
|
36
|
+
return { ...state, clock: { ...state.clock, [id]: { opened: opened ? t : was.opened, used: t } } };
|
|
37
|
+
};
|
|
38
|
+
var defOf = (defs, id) => defs.find((d) => d.id === id);
|
|
39
|
+
function floatSize(def) {
|
|
40
|
+
if (def?.float) return { min: def.float.min, natural: def.float.natural };
|
|
41
|
+
const width = Math.max(FLOAT_MIN.width, def?.width.natural ?? FLOAT_MIN.width);
|
|
42
|
+
return { min: FLOAT_MIN, natural: { width, height: FLOAT_HEIGHT } };
|
|
43
|
+
}
|
|
44
|
+
function ownWidth(defs, state, id) {
|
|
45
|
+
const def = defOf(defs, id);
|
|
46
|
+
const w = whereIs(state, id);
|
|
47
|
+
const lo = def?.width.min ?? 0;
|
|
48
|
+
const hi = Math.max(lo, def?.width.max ?? Infinity);
|
|
49
|
+
if (w.kind === "column") return Math.min(Math.max(state.sides[w.side][w.index].width, lo), hi);
|
|
50
|
+
return def?.width.natural ?? lo;
|
|
51
|
+
}
|
|
52
|
+
function createPanels(defs, start) {
|
|
53
|
+
const column = (ids) => {
|
|
54
|
+
const ds = ids.map((id) => defOf(defs, id));
|
|
55
|
+
const lo = Math.max(0, ...ds.map((d) => d?.width.min ?? 0));
|
|
56
|
+
const hi = Math.max(lo, Math.min(...ds.map((d) => d?.width.max ?? Infinity)));
|
|
57
|
+
const natural = Math.max(0, ...ds.map((d) => d?.width.natural ?? 0));
|
|
58
|
+
return { panels: [...ids], width: Math.min(Math.max(natural, lo), hi) };
|
|
59
|
+
};
|
|
60
|
+
const floats = (start.floats ?? []).map((f) => {
|
|
61
|
+
const n = floatSize(defOf(defs, f.id)).natural;
|
|
62
|
+
return { id: f.id, rect: { x: f.x, y: f.y, width: f.width ?? n.width, height: f.height ?? n.height }, ...f.pinned ? { pinned: true } : {} };
|
|
63
|
+
});
|
|
64
|
+
const sides = { left: (start.left ?? []).map(column), right: (start.right ?? []).map(column) };
|
|
65
|
+
const order = [...sides.left, ...sides.right].flatMap((c) => c.panels).concat(floats.map((f) => f.id));
|
|
66
|
+
const clock = {};
|
|
67
|
+
order.forEach((id, i) => clock[id] = { opened: i + 1, used: i + 1 });
|
|
68
|
+
return { sides, floats, folded: [...start.folded ?? []], kept: [...start.kept ?? []], closed: [...start.closed ?? []], clock };
|
|
69
|
+
}
|
|
70
|
+
function liftPanel(state, id) {
|
|
71
|
+
const lift = (cols) => cols.map((c) => ({ ...c, panels: without(c.panels, id) })).filter((c) => c.panels.length > 0);
|
|
72
|
+
return { ...state, sides: { left: lift(state.sides.left), right: lift(state.sides.right) }, floats: state.floats.filter((f) => f.id !== id) };
|
|
73
|
+
}
|
|
74
|
+
function open(state, id, at) {
|
|
75
|
+
let s = { ...state, closed: without(state.closed, id) };
|
|
76
|
+
if (at && whereIs(s, id).kind === "none") s = { ...s, sides: { ...s.sides, [at.side]: [...s.sides[at.side], { panels: [id], width: at.width }] } };
|
|
77
|
+
return bump(s, id, true);
|
|
78
|
+
}
|
|
79
|
+
var close = (state, id) => ({ ...state, closed: withOnce(state.closed, id), kept: without(state.kept, id) });
|
|
80
|
+
function focus(state, id) {
|
|
81
|
+
const f = state.floats.find((x) => x.id === id);
|
|
82
|
+
const s = f ? { ...state, floats: [...state.floats.filter((x) => x !== f), f] } : state;
|
|
83
|
+
return bump(s, id, false);
|
|
84
|
+
}
|
|
85
|
+
function toggleFold(state, id) {
|
|
86
|
+
if (state.folded.includes(id)) return bump({ ...state, folded: without(state.folded, id) }, id, false);
|
|
87
|
+
return { ...state, folded: [...state.folded, id] };
|
|
88
|
+
}
|
|
89
|
+
function keep(state, id, on) {
|
|
90
|
+
if (!on) return { ...state, kept: without(state.kept, id) };
|
|
91
|
+
return whereIs(state, id).kind === "column" ? { ...state, kept: withOnce(state.kept, id) } : state;
|
|
92
|
+
}
|
|
93
|
+
function dock(state, id, side, index, options = {}) {
|
|
94
|
+
const w = whereIs(state, id);
|
|
95
|
+
const from = w.kind === "column" ? state.sides[w.side][w.index].width : 0;
|
|
96
|
+
const s = liftPanel(state, id);
|
|
97
|
+
const cols = [...s.sides[side]];
|
|
98
|
+
if (options.into && cols[index]) cols[index] = { ...cols[index], panels: [...cols[index].panels, id] };
|
|
99
|
+
else {
|
|
100
|
+
const at = Math.max(0, Math.min(index, cols.length));
|
|
101
|
+
cols.splice(at, 0, { panels: [id], width: options.width ?? from });
|
|
102
|
+
}
|
|
103
|
+
return bump({ ...s, sides: { ...s.sides, [side]: cols }, closed: without(s.closed, id) }, id, false);
|
|
104
|
+
}
|
|
105
|
+
function float(state, id, rect2, pinned) {
|
|
106
|
+
const was = state.floats.find((f2) => f2.id === id);
|
|
107
|
+
const on = pinned ?? !!was?.pinned;
|
|
108
|
+
const s = liftPanel(state, id);
|
|
109
|
+
const f = { id, rect: { ...rect2 }, ...on ? { pinned: true } : {} };
|
|
110
|
+
return bump({ ...s, floats: [...s.floats, f], kept: without(s.kept, id), closed: without(s.closed, id) }, id, false);
|
|
111
|
+
}
|
|
112
|
+
var toView = (r, v) => ({ ...r, x: v.x + r.x * v.scale, y: v.y + r.y * v.scale });
|
|
113
|
+
var toPage = (r, v) => ({ ...r, x: (r.x - v.x) / v.scale, y: (r.y - v.y) / v.scale });
|
|
114
|
+
function pin(state, id, on, view = IDENTITY_VIEW, shown) {
|
|
115
|
+
return {
|
|
116
|
+
...state,
|
|
117
|
+
floats: state.floats.map((f) => {
|
|
118
|
+
if (f.id !== id || !!f.pinned === on) return f;
|
|
119
|
+
const seen = shown ? { ...f.rect, x: shown.x, y: shown.y } : f.pinned ? { ...f.rect } : toView(f.rect, view);
|
|
120
|
+
return on ? { id, rect: seen, pinned: true } : { id, rect: toPage(seen, view) };
|
|
121
|
+
})
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function resizeColumn(state, side, index, width) {
|
|
125
|
+
const col = state.sides[side][index];
|
|
126
|
+
if (!col || !Number.isFinite(width)) return state;
|
|
127
|
+
const t = tick(state);
|
|
128
|
+
const clock = { ...state.clock };
|
|
129
|
+
for (const id of col.panels) clock[id] = { opened: clock[id]?.opened ?? 0, used: t };
|
|
130
|
+
return { ...state, sides: { ...state.sides, [side]: state.sides[side].map((c, i) => i === index ? { ...c, width: Math.max(0, width) } : c) }, clock };
|
|
131
|
+
}
|
|
132
|
+
function resizeFloat(state, id, rect2) {
|
|
133
|
+
if (!state.floats.some((f) => f.id === id)) return state;
|
|
134
|
+
return bump({ ...state, floats: state.floats.map((f) => f.id === id ? { ...f, rect: { ...rect2 } } : f) }, id, false);
|
|
135
|
+
}
|
|
136
|
+
function columnWidth(defs, col) {
|
|
137
|
+
if (!defs) return col.width;
|
|
138
|
+
const ds = col.panels.map((id) => defOf(defs, id));
|
|
139
|
+
const lo = Math.max(0, ...ds.map((d) => d?.width.min ?? 0));
|
|
140
|
+
const hi = Math.max(lo, Math.min(...ds.map((d) => d?.width.max ?? Infinity)));
|
|
141
|
+
return Math.min(Math.max(Number.isFinite(col.width) ? col.width : lo, lo), hi);
|
|
142
|
+
}
|
|
143
|
+
function samePlaces(a, b, defs) {
|
|
144
|
+
const cols = (s) => SIDES.map((side) => s.sides[side].map((c) => `${c.panels.join(",")}@${Math.round(columnWidth(defs, c))}`).join("|")).join("\u2016");
|
|
145
|
+
const fl = (s) => s.floats.map((f) => `${f.id}@${Math.round(f.rect.x)},${Math.round(f.rect.y)},${Math.round(f.rect.width)},${Math.round(f.rect.height)}${f.pinned ? "p" : ""}`).join("|");
|
|
146
|
+
const set = (l) => [...l].sort().join(",");
|
|
147
|
+
return cols(a) === cols(b) && fl(a) === fl(b) && set(a.folded) === set(b.folded) && set(a.kept) === set(b.kept) && set(a.closed) === set(b.closed);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/panels/layout.ts
|
|
151
|
+
var clamp = (v, lo, hi) => Math.min(Math.max(v, lo), Math.max(lo, hi));
|
|
152
|
+
function giveWayOrder(cols, order) {
|
|
153
|
+
return [...cols].sort((a, b) => Number(a.kept) - Number(b.kept) || (order === "oldest" ? a.age - b.age : b.age - a.age) || a.index - b.index || SIDES.indexOf(a.side) - SIDES.indexOf(b.side));
|
|
154
|
+
}
|
|
155
|
+
function layoutPanels(defs, state, stage, options = {}) {
|
|
156
|
+
const opt = { ...DEFAULT_PANELS_OPTIONS, ...options };
|
|
157
|
+
const W = stage.width;
|
|
158
|
+
const H = stage.height;
|
|
159
|
+
const closed = new Set(state.closed);
|
|
160
|
+
const folded = new Set(state.folded);
|
|
161
|
+
const kept = new Set(state.kept);
|
|
162
|
+
const by = opt.giveWay.by;
|
|
163
|
+
const ride = opt.fold === "ride";
|
|
164
|
+
const cols = [];
|
|
165
|
+
for (const side of SIDES) {
|
|
166
|
+
state.sides[side].forEach((c, index) => {
|
|
167
|
+
const panels = c.panels.filter((id) => !closed.has(id));
|
|
168
|
+
if (panels.length === 0) return;
|
|
169
|
+
const ds = panels.map((id) => defOf(defs, id));
|
|
170
|
+
const min = Math.max(0, ...ds.map((d) => d?.width.min ?? 0));
|
|
171
|
+
const max = Math.max(min, Math.min(...ds.map((d) => d?.width.max ?? Infinity)));
|
|
172
|
+
const width = clamp(Number.isFinite(c.width) ? c.width : min, min, max);
|
|
173
|
+
const age = Math.max(...panels.map((id) => state.clock[id]?.[by] ?? 0));
|
|
174
|
+
cols.push({ side, index, panels, min, max, width, hand: panels.every((id) => folded.has(id)), kept: panels.some((id) => kept.has(id)), age });
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const order = giveWayOrder(
|
|
178
|
+
cols.filter((c) => !c.hand),
|
|
179
|
+
opt.giveWay.order
|
|
180
|
+
);
|
|
181
|
+
const fit = (k) => {
|
|
182
|
+
const auto = new Set(order.slice(0, k));
|
|
183
|
+
const open2 = (side) => cols.some((c) => c.side === side && !c.hand && !auto.has(c));
|
|
184
|
+
const railed = (side) => cols.some((c) => c.side === side && (auto.has(c) || c.hand && !(ride && open2(side))));
|
|
185
|
+
const widths = new Map(order.filter((c) => !auto.has(c)).map((c) => [c, c.width]));
|
|
186
|
+
let excess = [...widths.values()].reduce((a, b) => a + b, 0) + SIDES.filter(railed).length * opt.rail + opt.pagesMin - W;
|
|
187
|
+
for (const c of order) {
|
|
188
|
+
if (excess <= 0 || auto.has(c)) continue;
|
|
189
|
+
const take = Math.min(excess, c.width - c.min);
|
|
190
|
+
if (take > 0) {
|
|
191
|
+
widths.set(c, c.width - take);
|
|
192
|
+
excess -= take;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return { auto, widths, excess };
|
|
196
|
+
};
|
|
197
|
+
let g = fit(0);
|
|
198
|
+
for (let k = 1; g.excess > 0 && k <= order.length; k++) g = fit(k);
|
|
199
|
+
const notes = [];
|
|
200
|
+
for (const c of order) {
|
|
201
|
+
const w = g.widths.get(c);
|
|
202
|
+
if (w !== void 0 && w < c.width) notes.push({ code: "column-shrunk", side: c.side, column: c.index, items: c.panels, from: c.width, to: w });
|
|
203
|
+
}
|
|
204
|
+
for (const c of order) if (g.auto.has(c)) notes.push({ code: c.kept ? "kept-folded" : "column-folded", side: c.side, column: c.index, items: c.panels });
|
|
205
|
+
const items = /* @__PURE__ */ new Map();
|
|
206
|
+
const placedCols = [];
|
|
207
|
+
const rails = {};
|
|
208
|
+
let x0 = 0;
|
|
209
|
+
let x1 = W;
|
|
210
|
+
for (const side of SIDES) {
|
|
211
|
+
const mine = cols.filter((c) => c.side === side);
|
|
212
|
+
const open2 = mine.filter((c) => !c.hand && !g.auto.has(c));
|
|
213
|
+
const host = ride ? open2[0] : void 0;
|
|
214
|
+
const riders = host ? mine.filter((c) => c.hand) : [];
|
|
215
|
+
const inRail = mine.filter((c) => c.hand && !riders.includes(c) || g.auto.has(c));
|
|
216
|
+
if (inRail.length) {
|
|
217
|
+
const r = side === "left" ? rect(0, 0, opt.rail, H) : rect(W - opt.rail, 0, opt.rail, H);
|
|
218
|
+
const segments = inRail.flatMap((c) => c.panels).map((id, i) => ({ id, rect: rect(r.x, opt.railInset + i * opt.segment, opt.rail, opt.segment) }));
|
|
219
|
+
rails[side] = { side, rect: r, segments };
|
|
220
|
+
for (const s of segments) {
|
|
221
|
+
const c = inRail.find((x) => x.panels.includes(s.id));
|
|
222
|
+
items.set(s.id, { rect: s.rect, kind: "segment", layer: 0, hidden: true, folded: c.hand ? "hand" : "auto", kept: kept.has(s.id), pinned: false, floating: false, side, column: c.index });
|
|
223
|
+
}
|
|
224
|
+
if (side === "left") x0 = opt.rail;
|
|
225
|
+
else x1 = W - opt.rail;
|
|
226
|
+
}
|
|
227
|
+
const at = /* @__PURE__ */ new Map();
|
|
228
|
+
for (const c of mine) {
|
|
229
|
+
const w = g.widths.get(c);
|
|
230
|
+
if (w === void 0 || riders.includes(c)) continue;
|
|
231
|
+
if (side === "left") {
|
|
232
|
+
at.set(c, rect(x0, 0, w, H));
|
|
233
|
+
x0 += w;
|
|
234
|
+
} else {
|
|
235
|
+
x1 -= w;
|
|
236
|
+
at.set(c, rect(x1, 0, w, H));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const riding = riders.flatMap((c) => c.panels.map((id) => ({ id, c })));
|
|
240
|
+
const top = riding.length * opt.bar;
|
|
241
|
+
for (const c of mine) {
|
|
242
|
+
const w = g.widths.get(c);
|
|
243
|
+
const folded1 = c.hand ? "hand" : g.auto.has(c) ? "auto" : null;
|
|
244
|
+
const base = { side, index: c.index, width: c.width, min: c.min, max: c.max, folded: folded1, kept: c.kept, panels: c.panels };
|
|
245
|
+
if (riders.includes(c)) {
|
|
246
|
+
const hr = at.get(host);
|
|
247
|
+
const first = riding.findIndex((x) => x.c === c);
|
|
248
|
+
c.panels.forEach(
|
|
249
|
+
(id, i) => items.set(id, { rect: rect(hr.x, (first + i) * opt.bar, hr.width, opt.bar), kind: "bar", layer: 0, hidden: false, folded: "hand", kept: kept.has(id), pinned: false, floating: false, side, column: c.index, rides: host.index })
|
|
250
|
+
);
|
|
251
|
+
placedCols.push({ ...base, rect: rect(hr.x, first * opt.bar, hr.width, c.panels.length * opt.bar), shrunk: false, rides: host.index });
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const r = at.get(c) ?? rect(side === "left" ? x0 : x1, 0, 0, H);
|
|
255
|
+
placedCols.push({ ...base, rect: r, shrunk: w !== void 0 && w < c.width });
|
|
256
|
+
if (w === void 0) continue;
|
|
257
|
+
const y0 = c === host ? top : 0;
|
|
258
|
+
const bars = c.panels.filter((id) => folded.has(id)).length;
|
|
259
|
+
const share = Math.max(0, (H - y0 - bars * opt.bar) / (c.panels.length - bars));
|
|
260
|
+
let y = y0;
|
|
261
|
+
for (const id of c.panels) {
|
|
262
|
+
const bar = folded.has(id);
|
|
263
|
+
const h = bar ? opt.bar : share;
|
|
264
|
+
items.set(id, { rect: rect(r.x, y, r.width, h), kind: bar ? "bar" : "column", layer: 0, hidden: false, folded: bar ? "hand" : null, kept: kept.has(id), pinned: false, floating: false, side, column: c.index });
|
|
265
|
+
y += h;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const pages = rect(x0, 0, Math.max(0, x1 - x0), H);
|
|
270
|
+
if (pages.width < opt.pagesMin) notes.push({ code: "pages-under-min", width: pages.width, min: opt.pagesMin });
|
|
271
|
+
state.floats.forEach((f, i) => {
|
|
272
|
+
if (closed.has(f.id)) return;
|
|
273
|
+
const shaded = folded.has(f.id);
|
|
274
|
+
const at = shaded ? { ...f.rect, height: opt.bar } : f.rect;
|
|
275
|
+
const k = f.pinned ? 1 : opt.view.scale;
|
|
276
|
+
const r = { ...at };
|
|
277
|
+
if (opt.floats === "clamp") {
|
|
278
|
+
const lim = floatSize(defOf(defs, f.id)).min;
|
|
279
|
+
r.width = Math.max(lim.width, Math.min(r.width, W));
|
|
280
|
+
if (!shaded) r.height = Math.max(lim.height, Math.min(r.height, H));
|
|
281
|
+
if (r.width !== at.width || r.height !== at.height) notes.push({ code: "float-resized", items: [f.id] });
|
|
282
|
+
r.x = r.width <= W ? clamp(r.x, 0, (W - r.width) / k) : clamp(r.x, (opt.floatVisible - r.width) / k, (W - opt.floatVisible) / k);
|
|
283
|
+
r.y = r.height <= H ? clamp(r.y, 0, (H - r.height) / k) : clamp(r.y, 0, (H - opt.bar) / k);
|
|
284
|
+
if (r.x !== at.x || r.y !== at.y) notes.push({ code: "float-clamped", items: [f.id] });
|
|
285
|
+
}
|
|
286
|
+
const clamped = r.x !== at.x || r.y !== at.y || r.width !== at.width || r.height !== at.height;
|
|
287
|
+
items.set(f.id, {
|
|
288
|
+
rect: f.pinned ? r : toView(r, opt.view),
|
|
289
|
+
kind: shaded ? "bar" : "float",
|
|
290
|
+
layer: 10 + i,
|
|
291
|
+
hidden: false,
|
|
292
|
+
folded: shaded ? "hand" : null,
|
|
293
|
+
kept: false,
|
|
294
|
+
pinned: !!f.pinned,
|
|
295
|
+
floating: true,
|
|
296
|
+
...clamped ? { clamped: true } : {}
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
return { items, columns: placedCols, rails, notes, pages };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/panels/targets.ts
|
|
303
|
+
var COLUMN_REACH = 22;
|
|
304
|
+
var RAIL_EDGE = 10;
|
|
305
|
+
function placeRank(place) {
|
|
306
|
+
switch (place.kind) {
|
|
307
|
+
case "float":
|
|
308
|
+
return 3e3;
|
|
309
|
+
case "rail":
|
|
310
|
+
return place.side === "left" ? 0 : 1999;
|
|
311
|
+
case "column":
|
|
312
|
+
return place.side === "left" ? 1 + 2 * place.index : 1998 - 2 * place.index;
|
|
313
|
+
case "into":
|
|
314
|
+
return place.side === "left" ? 2 + 2 * place.index : 1997 - 2 * place.index;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function homePlace(state, id) {
|
|
318
|
+
const w = whereIs(state, id);
|
|
319
|
+
if (w.kind === "float") return { kind: "float" };
|
|
320
|
+
if (w.kind === "none") return null;
|
|
321
|
+
return state.sides[w.side][w.index].panels.length === 1 ? { kind: "column", side: w.side, index: w.index } : { kind: "into", side: w.side, index: w.index };
|
|
322
|
+
}
|
|
323
|
+
var cut = (x0, x1, lo, hi) => [Math.max(x0, lo), Math.min(x1, hi)];
|
|
324
|
+
function panelTargets(input) {
|
|
325
|
+
const { defs, state, id, stage } = input;
|
|
326
|
+
const opt = { ...DEFAULT_PANELS_OPTIONS, ...input.options };
|
|
327
|
+
const def = defOf(defs, id);
|
|
328
|
+
const s0 = liftPanel(state, id);
|
|
329
|
+
const l0 = layoutPanels(defs, s0, stage, opt);
|
|
330
|
+
const width = ownWidth(defs, state, id);
|
|
331
|
+
const W = stage.width;
|
|
332
|
+
const H = stage.height;
|
|
333
|
+
const R = COLUMN_REACH;
|
|
334
|
+
const out = [];
|
|
335
|
+
const add = (place, priority, hit, make, free = false) => {
|
|
336
|
+
if (hit.width <= 0 || hit.height <= 0) return;
|
|
337
|
+
if (input.allows && !input.allows(id, place)) return;
|
|
338
|
+
if (place.kind !== "float" && samePlaces(make({ x: 0, y: 0 }, { x: 0, y: 0 }), state, defs)) return;
|
|
339
|
+
out.push({ id: place.kind === "float" ? "float" : place.kind === "rail" ? `rail:${place.side}` : `${place.kind}:${place.side}:${place.index}`, priority, hit, panel: id, place, order: placeRank(place), make, ...free ? { free: true } : {} });
|
|
340
|
+
};
|
|
341
|
+
for (const side of SIDES) {
|
|
342
|
+
if (def?.sides && !def.sides.includes(side)) continue;
|
|
343
|
+
const left = side === "left";
|
|
344
|
+
const rail = l0.rails[side];
|
|
345
|
+
const edge = rail ? rail.rect.width : RAIL_EDGE;
|
|
346
|
+
const railHit = rail ? rail.rect : left ? rect(0, 0, RAIL_EDGE, H) : rect(W - RAIL_EDGE, 0, RAIL_EDGE, H);
|
|
347
|
+
add({ kind: "rail", side }, 1, railHit, () => {
|
|
348
|
+
const s = dock(state, id, side, 0, { width });
|
|
349
|
+
return { ...s, folded: [...s.folded.filter((x) => x !== id), id] };
|
|
350
|
+
});
|
|
351
|
+
const open2 = l0.columns.filter((c) => c.side === side && c.folded === null);
|
|
352
|
+
const lo = left ? edge : 0;
|
|
353
|
+
const hi = left ? W : W - edge;
|
|
354
|
+
const band = (x, outer, i) => {
|
|
355
|
+
const [a, b] = outer ? left ? cut(x, x + 2 * R, lo, hi) : cut(x - 2 * R, x, lo, hi) : cut(x - R, x + R, lo, hi);
|
|
356
|
+
add({ kind: "column", side, index: i }, 2, rect(a, 0, b - a, H), () => dock(state, id, side, i, { width }));
|
|
357
|
+
};
|
|
358
|
+
if (open2.length === 0) band(left ? l0.pages.x : l0.pages.x + l0.pages.width, true, s0.sides[side].length);
|
|
359
|
+
open2.forEach((c, k) => {
|
|
360
|
+
band(left ? c.rect.x : c.rect.x + c.rect.width, k === 0, c.index);
|
|
361
|
+
add({ kind: "into", side, index: c.index }, 2, c.rect, () => dock(state, id, side, c.index, { into: true }));
|
|
362
|
+
});
|
|
363
|
+
const last = open2.at(-1);
|
|
364
|
+
if (last) band(left ? last.rect.x + last.rect.width : last.rect.x, false, last.index + 1);
|
|
365
|
+
}
|
|
366
|
+
if (def?.floats !== false) {
|
|
367
|
+
const was = state.floats.find((f) => f.id === id);
|
|
368
|
+
const size = was ? { width: was.rect.width, height: was.rect.height } : floatSize(def).natural;
|
|
369
|
+
const pinned = !!was?.pinned;
|
|
370
|
+
add(
|
|
371
|
+
{ kind: "float" },
|
|
372
|
+
3,
|
|
373
|
+
rect(0, 0, W, H),
|
|
374
|
+
(p, grab) => {
|
|
375
|
+
const at = rect(p.x - grab.x, p.y - grab.y, size.width, size.height);
|
|
376
|
+
const s = float(state, id, pinned ? at : toPage(at, opt.view), pinned);
|
|
377
|
+
const laid = layoutPanels(defs, s, stage, opt).items.get(id).rect;
|
|
378
|
+
const shown = s.folded.includes(id) ? { ...laid, width: at.width, height: at.height } : laid;
|
|
379
|
+
return float(state, id, pinned ? shown : toPage(shown, opt.view), pinned);
|
|
380
|
+
},
|
|
381
|
+
true
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
return out.sort((a, b) => a.order - b.order);
|
|
385
|
+
}
|
|
386
|
+
function panelDrop(input, target, p, grab) {
|
|
387
|
+
const opt = { ...DEFAULT_PANELS_OPTIONS, ...input.options };
|
|
388
|
+
const state = target.make(p, grab);
|
|
389
|
+
const layout = layoutPanels(input.defs, state, input.stage, opt);
|
|
390
|
+
const placed = layout.items.get(input.id);
|
|
391
|
+
const key = target.free ? `float:${Math.round(placed.rect.x)},${Math.round(placed.rect.y)}` : target.id;
|
|
392
|
+
return { target: target.id, place: target.place, key, state, layout, slot: placed.rect, hidden: placed.hidden, free: !!target.free };
|
|
393
|
+
}
|
|
394
|
+
function panelStops(targets, options = {}) {
|
|
395
|
+
const stops = [];
|
|
396
|
+
for (const t of targets) {
|
|
397
|
+
if (t.place.kind === "float") {
|
|
398
|
+
if (!options.floats) continue;
|
|
399
|
+
const at = options.floats === true ? { x: t.hit.x + t.hit.width / 2, y: t.hit.y + t.hit.height / 2 } : options.floats.at;
|
|
400
|
+
const grab = options.floats === true ? { x: 0, y: 0 } : options.floats.grab ?? { x: 0, y: 0 };
|
|
401
|
+
stops.push({ id: t.id, place: t.place, state: t.make(at, grab), home: false, order: t.order });
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
stops.push({ id: t.id, place: t.place, state: t.make({ x: 0, y: 0 }, { x: 0, y: 0 }), home: false, order: t.order });
|
|
405
|
+
}
|
|
406
|
+
const h = options.home;
|
|
407
|
+
if (h) stops.push({ id: "home", place: h.place, state: h.state, home: true, order: placeRank(h.place) + (h.place.kind === "float" ? 1 : 0.5) });
|
|
408
|
+
return stops.sort((a, b) => a.order - b.order).map(({ order: _order, ...s }) => s);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export {
|
|
412
|
+
SIDES,
|
|
413
|
+
IDENTITY_VIEW,
|
|
414
|
+
DEFAULT_PANELS_OPTIONS,
|
|
415
|
+
FLOAT_HEIGHT,
|
|
416
|
+
FLOAT_MIN,
|
|
417
|
+
whereIs,
|
|
418
|
+
tick,
|
|
419
|
+
defOf,
|
|
420
|
+
floatSize,
|
|
421
|
+
ownWidth,
|
|
422
|
+
createPanels,
|
|
423
|
+
liftPanel,
|
|
424
|
+
open,
|
|
425
|
+
close,
|
|
426
|
+
focus,
|
|
427
|
+
toggleFold,
|
|
428
|
+
keep,
|
|
429
|
+
dock,
|
|
430
|
+
float,
|
|
431
|
+
toView,
|
|
432
|
+
toPage,
|
|
433
|
+
pin,
|
|
434
|
+
resizeColumn,
|
|
435
|
+
resizeFloat,
|
|
436
|
+
columnWidth,
|
|
437
|
+
samePlaces,
|
|
438
|
+
giveWayOrder,
|
|
439
|
+
layoutPanels,
|
|
440
|
+
COLUMN_REACH,
|
|
441
|
+
RAIL_EDGE,
|
|
442
|
+
placeRank,
|
|
443
|
+
homePlace,
|
|
444
|
+
panelTargets,
|
|
445
|
+
panelDrop,
|
|
446
|
+
panelStops
|
|
447
|
+
};
|
|
448
|
+
//# sourceMappingURL=chunk-QR7MGDX6.js.map
|