@grafloria/element 0.4.58 → 0.4.60
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/package.json +1 -1
- package/src/lib/dashboard-kit/dashboard.js +7 -11
- package/src/lib/dashboard-kit/grid-binder.d.ts +19 -6
- package/src/lib/dashboard-kit/grid-binder.js +57 -8
- package/src/lib/dashboard-kit/tear-out.js +6 -2
- package/src/lib/dashboard-kit/zones.d.ts +42 -7
- package/src/lib/dashboard-kit/zones.js +53 -0
package/package.json
CHANGED
|
@@ -1067,15 +1067,11 @@ export function createDashboardHandle(ctx) {
|
|
|
1067
1067
|
return [new RegisterWidgetCommand({ register: () => apply(after), unregister: () => apply(before) }, 'register')];
|
|
1068
1068
|
};
|
|
1069
1069
|
ctx.tabDrop = {
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
if (r.width > 0 && cx >= r.left && cx <= r.right && cy >= r.top && cy <= r.bottom)
|
|
1076
|
-
return { containerId: id, index: indexInStrip(el, cx) };
|
|
1077
|
-
}
|
|
1078
|
-
return null;
|
|
1070
|
+
tabIndexAt: (containerId, cx) => {
|
|
1071
|
+
const el = ctx.tabStrips.get(containerId);
|
|
1072
|
+
if (!el || !ctx.boardGroups.has(containerId))
|
|
1073
|
+
return null;
|
|
1074
|
+
return indexInStrip(el, cx);
|
|
1079
1075
|
},
|
|
1080
1076
|
markDrop: markTabDrop,
|
|
1081
1077
|
dropIntoStrip: (widgetId, containerId, index, sourceBoardId, displaced) => moveWidgetToTabCommands(widgetId, containerId, index, sourceBoardId, displaced),
|
|
@@ -1211,12 +1207,12 @@ export function createDashboardHandle(ctx) {
|
|
|
1211
1207
|
joinTargets: [...ctx.layoutOf.keys()].filter((id) => { var _a, _b; return id !== containerId && id !== pageId && ((_a = ctx.layoutOf.get(id)) !== null && _a !== void 0 ? _a : (_b = specById.get(id)) === null || _b === void 0 ? void 0 : _b.layout) === 'tabs' && !!model.getGroup(id) && !insidePage(id); }),
|
|
1212
1208
|
stripHeight: (targetId) => tabStripReserve(ctx.tabsOf.get(targetId), Math.max(1, liveCount(targetId))),
|
|
1213
1209
|
ownBoards: [pageId, ...[...ctx.boardGroups.keys()].filter((id) => insidePage(id))],
|
|
1214
|
-
stripIndex: (targetId, cx, cy) => {
|
|
1210
|
+
stripIndex: (targetId, cx, cy, grace = 0) => {
|
|
1215
1211
|
const strip = ctx.tabStrips.get(targetId);
|
|
1216
1212
|
if (!strip)
|
|
1217
1213
|
return null;
|
|
1218
1214
|
const r = strip.getBoundingClientRect();
|
|
1219
|
-
if (r.width === 0 || cx < r.left || cx > r.right || cy < r.top || cy > r.bottom)
|
|
1215
|
+
if (r.width === 0 || cx < r.left - grace || cx > r.right + grace || cy < r.top - grace || cy > r.bottom + grace)
|
|
1220
1216
|
return null;
|
|
1221
1217
|
return indexInStrip(strip, cx);
|
|
1222
1218
|
},
|
|
@@ -558,7 +558,7 @@ export interface TearOutPlan {
|
|
|
558
558
|
/** Paint (or, with null, clear) the insertion mark on `targetId`'s strip. */
|
|
559
559
|
markDrop(targetId: string | null, index: number | null): void;
|
|
560
560
|
/** The tab index a CLIENT point means along `targetId`'s strip — null when the point is not over that strip. */
|
|
561
|
-
stripIndex(targetId: string, clientX: number, clientY: number): number | null;
|
|
561
|
+
stripIndex(targetId: string, clientX: number, clientY: number, grace?: number): number | null;
|
|
562
562
|
/** The strip's height on `targetId`, px — the band above its body. */
|
|
563
563
|
stripHeight(targetId: string): number;
|
|
564
564
|
/** The page itself and every board inside it — never a board the page can land on. */
|
|
@@ -581,13 +581,16 @@ export interface TearOutPlan {
|
|
|
581
581
|
* handle owns the strips and the registry; the binder owns the gesture.
|
|
582
582
|
*/
|
|
583
583
|
export interface TabDropHooks {
|
|
584
|
-
/** The strip under a CLIENT point, and the tab index that point means along it. */
|
|
585
|
-
stripAt(clientX: number, clientY: number): {
|
|
586
|
-
containerId: string;
|
|
587
|
-
index: number;
|
|
588
|
-
} | null;
|
|
589
584
|
/** Paint (or, with null, clear) the insertion mark on a strip. */
|
|
590
585
|
markDrop(containerId: string | null, index: number | null): void;
|
|
586
|
+
/**
|
|
587
|
+
* Which tab slot a CLIENT x means along one named strip — the x alone, with
|
|
588
|
+
* no hit test. WHETHER the hand is on a strip at all is the zone walk's
|
|
589
|
+
* answer, from the container's frame in the model at rest: a strip element
|
|
590
|
+
* reports the box it is painted in, and a strip that has been pushed — or
|
|
591
|
+
* is gliding home from a push — is a box the hand never asked for.
|
|
592
|
+
*/
|
|
593
|
+
tabIndexAt?(containerId: string, clientX: number): number | null;
|
|
591
594
|
/** The commands that make `widgetId` a new tab of `containerId` at `index`; `displaced` are this board's survivors. Empty = refused. */
|
|
592
595
|
dropIntoStrip(widgetId: string, containerId: string, index: number, sourceBoardId: string, displaced: Command[]): Command[];
|
|
593
596
|
}
|
|
@@ -680,4 +683,14 @@ export declare function parentPeerOf(container: HTMLElement, groupId: string): B
|
|
|
680
683
|
export declare function clearOtherSelections(container: HTMLElement, self: BinderPeer | null): void;
|
|
681
684
|
export declare function registerBoardPeer(container: HTMLElement, peer: BinderPeer): () => void;
|
|
682
685
|
export type { BinderPeer };
|
|
686
|
+
/**
|
|
687
|
+
* THE STRIP KEEPS THE HAND IT HAS (CSS px). A tab strip is ~30 px tall and
|
|
688
|
+
* the zone right under it moves the whole container out of the way, so at the
|
|
689
|
+
* seam a wobble of a pixel or two toggled a 90-px animated push — the user,
|
|
690
|
+
* aiming a widget at the tabs: "it's switching so fast between having it above
|
|
691
|
+
* the entire tab group or inside as a tab". Once a drag is over a strip, that
|
|
692
|
+
* strip's box counts as this much larger until the hand really leaves. A strip
|
|
693
|
+
* the drag has NOT reached gets no extra room: it never grows under the hand.
|
|
694
|
+
*/
|
|
695
|
+
export declare const STRIP_STAY = 9;
|
|
683
696
|
export declare function bindDashboardGrid(api: DashboardGridApi, group: GroupModel, options?: DashboardGridOptions): DashboardGridHandle;
|
|
@@ -56,7 +56,7 @@ import { buildCommitCommands, cellFromGridItem, cellToRect, columnUnitFor, gridI
|
|
|
56
56
|
import { ensureDashboardKitStyles } from './styles.js';
|
|
57
57
|
import { captionOfGroup, captionPassThrough, sectionCaptionReserve } from './caption.js';
|
|
58
58
|
import { TAB_STRIP_HEIGHT } from './tabs.js';
|
|
59
|
-
import { BESIDE_BAND, resolve as resolveZone } from './zones.js';
|
|
59
|
+
import { BESIDE_BAND, resolve as resolveZone, stripUnder } from './zones.js';
|
|
60
60
|
import { SequenceCommand, SetGroupCellCommand, tileCommands } from './commit.js';
|
|
61
61
|
import { EDGE_GRACE } from './board-ctx.js';
|
|
62
62
|
import { createProjection } from './project.js';
|
|
@@ -149,6 +149,16 @@ export function registerBoardPeer(container, peer) {
|
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
151
|
const DRAG_THRESHOLD = 4;
|
|
152
|
+
/**
|
|
153
|
+
* THE STRIP KEEPS THE HAND IT HAS (CSS px). A tab strip is ~30 px tall and
|
|
154
|
+
* the zone right under it moves the whole container out of the way, so at the
|
|
155
|
+
* seam a wobble of a pixel or two toggled a 90-px animated push — the user,
|
|
156
|
+
* aiming a widget at the tabs: "it's switching so fast between having it above
|
|
157
|
+
* the entire tab group or inside as a tab". Once a drag is over a strip, that
|
|
158
|
+
* strip's box counts as this much larger until the hand really leaves. A strip
|
|
159
|
+
* the drag has NOT reached gets no extra room: it never grows under the hand.
|
|
160
|
+
*/
|
|
161
|
+
export const STRIP_STAY = 9;
|
|
152
162
|
/** The node a gesture holds — node-only paths (the pixel ghost, a resize, a strip drop, a removal) ask through this. */
|
|
153
163
|
const nodeOf = (g) => g.entity;
|
|
154
164
|
let binderSeq = 0;
|
|
@@ -2165,19 +2175,50 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2165
2175
|
};
|
|
2166
2176
|
/** What the pointer means for the dragged tile: the resolve over the live tree, with the beside the hand holds. */
|
|
2167
2177
|
const resolveTileZone = (g, ev) => {
|
|
2168
|
-
var _a, _b;
|
|
2178
|
+
var _a, _b, _c, _d, _e;
|
|
2179
|
+
const roots = zoneRoots();
|
|
2180
|
+
// THE CONTAINERS THIS GESTURE HAS MOVED, at the frames they rest in. A
|
|
2181
|
+
// drag's own pushes must never change what the hand means — settled for a
|
|
2182
|
+
// group ghost in 0.4.44, and true of a widget for the same reason: the
|
|
2183
|
+
// beside that a band previews SHOVES the container out from under the
|
|
2184
|
+
// pointer, so read live it would answer about a container that is only
|
|
2185
|
+
// there because of the answer.
|
|
2186
|
+
const rests = restFramesOf(g);
|
|
2169
2187
|
let strip = null;
|
|
2170
|
-
if (options.tabDrop && !isStatic && g.kind !== 'palette' && g.subject === 'node') {
|
|
2188
|
+
if (((_a = options.tabDrop) === null || _a === void 0 ? void 0 : _a.tabIndexAt) && !isStatic && g.kind !== 'palette' && g.subject === 'node') {
|
|
2171
2189
|
// A group never becomes a tab: over a container's strip it is over the
|
|
2172
2190
|
// container's margin — a cell on the parent board, pushing with intent.
|
|
2173
|
-
const
|
|
2174
|
-
|
|
2191
|
+
const hit = stripUnder({ x: ev.world.x, y: ev.world.y, roots, held: (_c = (_b = g.strip) === null || _b === void 0 ? void 0 : _b.containerId) !== null && _c !== void 0 ? _c : null, stay: STRIP_STAY / (clientPerWorld().y || 1), restFrames: rests });
|
|
2192
|
+
if (hit) {
|
|
2193
|
+
// The SLOT is the painted strip's business — its tabs are laid out by
|
|
2194
|
+
// the browser. A container the gesture shifted sideways is painted
|
|
2195
|
+
// that far from where it was measured, so the client x is carried
|
|
2196
|
+
// across with it.
|
|
2197
|
+
const grp = diagram.getGroup(hit.containerId);
|
|
2198
|
+
const rest = rests.get(hit.containerId);
|
|
2199
|
+
const dx = grp && rest ? (grp.position.x - rest.x) * clientPerWorld().x : 0;
|
|
2200
|
+
const idx = options.tabDrop.tabIndexAt(hit.containerId, api.container.getBoundingClientRect().left + ev.screen.x + dx);
|
|
2201
|
+
if (idx !== null)
|
|
2202
|
+
strip = { containerId: hit.containerId, index: idx };
|
|
2203
|
+
}
|
|
2175
2204
|
}
|
|
2176
|
-
return resolveZone(
|
|
2205
|
+
return resolveZone({
|
|
2206
|
+
x: ev.world.x,
|
|
2207
|
+
y: ev.world.y,
|
|
2208
|
+
roots,
|
|
2209
|
+
strip,
|
|
2210
|
+
prev: beside
|
|
2177
2211
|
? { containerId: beside.id, side: beside.side, frame0: beside.frame0, vacated: cellToRect({ x: beside.vacated.x, y: beside.vacated.y, w: g.spans.w, h: g.spans.h }, frame(), geom(), rows()) }
|
|
2178
|
-
: ((
|
|
2212
|
+
: ((_e = (_d = g.leg) === null || _d === void 0 ? void 0 : _d.adopted.besideState()) !== null && _e !== void 0 ? _e : null), // a beside another board holds for the ghost, through its leg
|
|
2213
|
+
maxDepth: nesting,
|
|
2214
|
+
ghostDepth: g.subject === 'group' ? 1 + levelsInside(g.id) : 0, // a group's widgets sit one board deeper than wherever it lands
|
|
2215
|
+
restFrames: rests,
|
|
2216
|
+
ghostSubtree: g.subject === 'group' ? descendantGroups(g.id) : EMPTY_SUBTREE,
|
|
2217
|
+
gap,
|
|
2218
|
+
homeChain: homeChain(),
|
|
2219
|
+
});
|
|
2179
2220
|
};
|
|
2180
|
-
/** This board's containers as they stood at the press: a
|
|
2221
|
+
/** This board's containers as they stood at the press: a gesture's own pushes never change what the hand means. */
|
|
2181
2222
|
const restFramesOf = (g) => {
|
|
2182
2223
|
const out = new Map();
|
|
2183
2224
|
for (const [id, snap] of g.startGeom) {
|
|
@@ -2227,6 +2268,14 @@ export function bindDashboardGrid(api, group, options = {}) {
|
|
|
2227
2268
|
}
|
|
2228
2269
|
return deepest;
|
|
2229
2270
|
};
|
|
2271
|
+
/** Client pixels per world unit — the camera's scale, measured the way the tear-out measures its bands. */
|
|
2272
|
+
const clientPerWorld = () => {
|
|
2273
|
+
const rect = api.container.getBoundingClientRect();
|
|
2274
|
+
const toWorld = (cx, cy) => { var _a; return ((_a = api.viewport) === null || _a === void 0 ? void 0 : _a.clientToWorld) ? api.viewport.clientToWorld(cx, cy, rect) : { x: cx - rect.left, y: cy - rect.top }; };
|
|
2275
|
+
const o = toWorld(rect.left, rect.top);
|
|
2276
|
+
const u = toWorld(rect.left + 100, rect.top + 100);
|
|
2277
|
+
return { x: 100 / (u.x - o.x || 100), y: 100 / (u.y - o.y || 100) };
|
|
2278
|
+
};
|
|
2230
2279
|
/** The groups this board sits in, all the way up: their bands never apply to a tile of this board. */
|
|
2231
2280
|
const homeChain = () => {
|
|
2232
2281
|
const out = new Set();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cellToRect, rowHeightFor, sizeToSpan } from './grid-mapping.js';
|
|
2
2
|
import { BESIDE_BAND, resolveTabZone } from './zones.js';
|
|
3
3
|
import { EDGE_GRACE } from './board-ctx.js';
|
|
4
|
+
import { STRIP_STAY } from './grid-binder.js';
|
|
4
5
|
/** A torn-out page never arrives shorter than this: a strip with no room under it is not a group. */
|
|
5
6
|
export const TEAR_OUT_MIN_ROWS = 2;
|
|
6
7
|
export function createTearOut(ctx, deps) {
|
|
@@ -278,8 +279,11 @@ export function createTearOut(ctx, deps) {
|
|
|
278
279
|
x: world.x,
|
|
279
280
|
y: world.y,
|
|
280
281
|
clientInside,
|
|
281
|
-
|
|
282
|
-
|
|
282
|
+
// A page dragged along its own strip, or over another's, keeps that
|
|
283
|
+
// strip through a small overshoot too (STRIP_STAY): the zone under a
|
|
284
|
+
// strip moves the container, and a pixel must not toggle it.
|
|
285
|
+
ownStrip: plan.stripIndex(fromGroupId, cx, cy, zone.kind === 'reorder' ? STRIP_STAY : 0),
|
|
286
|
+
stripOf: (id) => plan.stripIndex(id, cx, cy, zone.kind === 'strip' && zone.target.id === id ? STRIP_STAY : 0),
|
|
283
287
|
root: root && root.kind === 'root' ? { side: root.side } : null,
|
|
284
288
|
target: target
|
|
285
289
|
? {
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
* descendant). The strip rows of a tab container are nobody's band, and its
|
|
22
22
|
* margin (inside the frame, outside the page) is a plain cell on the parent.
|
|
23
23
|
*
|
|
24
|
+
* `stripUnder` walks the same tree for the strip itself, so a tab slot and a
|
|
25
|
+
* band are decided on one set of frames — the ones the containers REST in,
|
|
26
|
+
* never the ones a preview pushed them to.
|
|
27
|
+
*
|
|
24
28
|
* This module is pure geometry: no DOM, no engine, no model. The binder
|
|
25
29
|
* builds the tree for each move from its peers and the model, and acts on
|
|
26
30
|
* the answer.
|
|
@@ -89,13 +93,13 @@ export interface ResolveInput {
|
|
|
89
93
|
*/
|
|
90
94
|
homeChain: ReadonlySet<string>;
|
|
91
95
|
/**
|
|
92
|
-
* Containers of the source board as they stood when the gesture began
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* leaves the board.
|
|
96
|
+
* Containers of the source board as they stood when the gesture began. The
|
|
97
|
+
* hand over a pushed container's rest frame still means that container —
|
|
98
|
+
* its zones, its page tested at the displacement — or the target flees the
|
|
99
|
+
* hand that is aiming at it: a group could never enter a container its own
|
|
100
|
+
* body reaches first (0.4.44), and a widget aimed at a tab strip chased a
|
|
101
|
+
* strip its own preview kept shoving away (0.4.60). The engine's memory
|
|
102
|
+
* brings the pushed container home when the ghost leaves the board.
|
|
99
103
|
*/
|
|
100
104
|
restFrames?: ReadonlyMap<string, ZoneRect>;
|
|
101
105
|
}
|
|
@@ -127,6 +131,37 @@ export declare const BESIDE_STAY = 0.05;
|
|
|
127
131
|
* to its far right means "after it", not "above it".
|
|
128
132
|
*/
|
|
129
133
|
export declare function bandOf(f: ZoneRect, stripHeight: number, x: number, y: number, band?: number): BesideSide | null;
|
|
134
|
+
export interface StripProbe {
|
|
135
|
+
x: number;
|
|
136
|
+
y: number;
|
|
137
|
+
roots: ZoneBoard[];
|
|
138
|
+
/** The container whose strip the gesture already holds: its rows are widened by `stay`, and it wins over a neighbour's. */
|
|
139
|
+
held: string | null;
|
|
140
|
+
/** The stickiness around the held strip, in world units. */
|
|
141
|
+
stay: number;
|
|
142
|
+
/** Containers the gesture has displaced, at the frames they rest in. */
|
|
143
|
+
restFrames?: ReadonlyMap<string, ZoneRect>;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The tab strip under a pointer — WHERE THE CONTAINER RESTS, read from the
|
|
147
|
+
* same tree at the same frames as `resolve`.
|
|
148
|
+
*
|
|
149
|
+
* A strip is painted at the top of its container, so it travels with it: a
|
|
150
|
+
* beside shifts the container, a refusal pushes it, and it GLIDES home
|
|
151
|
+
* afterwards. Hit-testing the painted box therefore makes the strip a target
|
|
152
|
+
* at wherever the preview happened to put it — and a box in the air sweeps
|
|
153
|
+
* under a hand that is not moving at all and steals the zone. That is a
|
|
154
|
+
* closed loop: the tab claims the hand, the container comes home, the band
|
|
155
|
+
* under the hand claims it back, the container is pushed away, and the strip
|
|
156
|
+
* flies through the pointer again. The user met it as "it flickers between a
|
|
157
|
+
* tab and above the group; I have to go very slowly".
|
|
158
|
+
*
|
|
159
|
+
* So the rows are the container's own, at rest, on the model's frames. What
|
|
160
|
+
* a hand means never depends on what the preview did with it.
|
|
161
|
+
*/
|
|
162
|
+
export declare function stripUnder(p: StripProbe): {
|
|
163
|
+
containerId: string;
|
|
164
|
+
} | null;
|
|
130
165
|
export declare function resolve(input: ResolveInput): Zone;
|
|
131
166
|
/** A join target as the walk sees it: another tab container, its frame anchored by the binder while the pointer is inside it. */
|
|
132
167
|
export interface TabTarget {
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
* descendant). The strip rows of a tab container are nobody's band, and its
|
|
22
22
|
* margin (inside the frame, outside the page) is a plain cell on the parent.
|
|
23
23
|
*
|
|
24
|
+
* `stripUnder` walks the same tree for the strip itself, so a tab slot and a
|
|
25
|
+
* band are decided on one set of frames — the ones the containers REST in,
|
|
26
|
+
* never the ones a preview pushed them to.
|
|
27
|
+
*
|
|
24
28
|
* This module is pure geometry: no DOM, no engine, no model. The binder
|
|
25
29
|
* builds the tree for each move from its peers and the model, and acts on
|
|
26
30
|
* the answer.
|
|
@@ -55,6 +59,55 @@ export function bandOf(f, stripHeight, x, y, band = BESIDE_BAND) {
|
|
|
55
59
|
return 'bottom';
|
|
56
60
|
return null;
|
|
57
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* The tab strip under a pointer — WHERE THE CONTAINER RESTS, read from the
|
|
64
|
+
* same tree at the same frames as `resolve`.
|
|
65
|
+
*
|
|
66
|
+
* A strip is painted at the top of its container, so it travels with it: a
|
|
67
|
+
* beside shifts the container, a refusal pushes it, and it GLIDES home
|
|
68
|
+
* afterwards. Hit-testing the painted box therefore makes the strip a target
|
|
69
|
+
* at wherever the preview happened to put it — and a box in the air sweeps
|
|
70
|
+
* under a hand that is not moving at all and steals the zone. That is a
|
|
71
|
+
* closed loop: the tab claims the hand, the container comes home, the band
|
|
72
|
+
* under the hand claims it back, the container is pushed away, and the strip
|
|
73
|
+
* flies through the pointer again. The user met it as "it flickers between a
|
|
74
|
+
* tab and above the group; I have to go very slowly".
|
|
75
|
+
*
|
|
76
|
+
* So the rows are the container's own, at rest, on the model's frames. What
|
|
77
|
+
* a hand means never depends on what the preview did with it.
|
|
78
|
+
*/
|
|
79
|
+
export function stripUnder(p) {
|
|
80
|
+
const { x, y } = p;
|
|
81
|
+
// Plain locals, not one object: a `let` assigned only inside the closure
|
|
82
|
+
// narrows to `never` at the return.
|
|
83
|
+
let bestId = null;
|
|
84
|
+
let bestDepth = -1;
|
|
85
|
+
let bestHeld = false;
|
|
86
|
+
const visit = (b, dx, dy) => {
|
|
87
|
+
var _a;
|
|
88
|
+
for (const c of b.children()) {
|
|
89
|
+
const rest = (_a = p.restFrames) === null || _a === void 0 ? void 0 : _a.get(c.id);
|
|
90
|
+
const atRest = rest && inRect(rest, x, y) ? rest : null;
|
|
91
|
+
const frame = atRest !== null && atRest !== void 0 ? atRest : c.frame;
|
|
92
|
+
const tx = atRest ? x : x + dx;
|
|
93
|
+
const ty = atRest ? y : y + dy;
|
|
94
|
+
const held = c.id === p.held;
|
|
95
|
+
const pad = held ? p.stay : 0;
|
|
96
|
+
if (c.stripHeight > 0 && inRect({ x: frame.x, y: frame.y, width: frame.width, height: c.stripHeight }, tx, ty, pad)) {
|
|
97
|
+
if (bestId === null || held || (!bestHeld && b.depth >= bestDepth)) {
|
|
98
|
+
bestId = c.id;
|
|
99
|
+
bestDepth = b.depth;
|
|
100
|
+
bestHeld = held;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (c.inner && inRect(frame, tx, ty, pad))
|
|
104
|
+
visit(c.inner, atRest ? c.frame.x - atRest.x : dx, atRest ? c.frame.y - atRest.y : dy);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
for (const r of p.roots)
|
|
108
|
+
visit(r, 0, 0);
|
|
109
|
+
return bestId === null ? null : { containerId: bestId };
|
|
110
|
+
}
|
|
58
111
|
/** The board a container sits on, found by id through the tree. */
|
|
59
112
|
function boardOf(roots, containerId) {
|
|
60
113
|
const visit = (b) => {
|