@grafloria/element 0.4.64 → 0.4.65

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grafloria/element",
3
- "version": "0.4.64",
3
+ "version": "0.4.65",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
@@ -979,7 +979,7 @@ export function bindDashboardGrid(api, group, options = {}) {
979
979
  }
980
980
  want.forEach((b, i) => {
981
981
  const el = lanesEl.children[i];
982
- el.setAttribute('data-lane', b.side);
982
+ el.setAttribute('data-lane', b.name);
983
983
  el.style.left = `${b.rect.x - c.frame.x}px`;
984
984
  el.style.top = `${b.rect.y - c.frame.y}px`;
985
985
  el.style.width = `${b.rect.width}px`;
@@ -2254,6 +2254,11 @@ export function bindDashboardGrid(api, group, options = {}) {
2254
2254
  // until 216 px of the fluid demo's page meant "above the whole
2255
2255
  // panel" (0.4.62). The sides keep the fifth.
2256
2256
  bandY: layout === 'tabs' ? TAB_STRIP_HEIGHT : 0,
2257
+ // …and the TOP band hangs ABOVE the frame, where a hand looking
2258
+ // for "above this panel" actually goes (0.4.65). A panel holding
2259
+ // the board's first row has no row above it to point at, and the
2260
+ // lane under its header is the last place anyone would try.
2261
+ topOutside: layout === 'tabs' ? TAB_STRIP_HEIGHT : 0,
2257
2262
  inner: innerPeer ? boardRef(innerPeer, depth + 1) : null,
2258
2263
  });
2259
2264
  }
@@ -59,6 +59,17 @@ export interface ZoneContainer {
59
59
  band: number;
60
60
  /** The TOP and BOTTOM depth in world units, when it is a fixed depth rather than `band` of the body. */
61
61
  bandY?: number;
62
+ /**
63
+ * How deep the "above me" band hangs ABOVE the frame's top edge, in world
64
+ * units. 0 or absent: no such band, and no inside top band either.
65
+ *
66
+ * A hand looking for "above this panel" goes above it. It used to have to go
67
+ * BELOW the panel's header instead, into a 30 px lane inside the body, which
68
+ * is the opposite of where anyone points and reachable only by being told —
69
+ * "try dragging nps above the tab panel, won't work" (0.4.65). The band now
70
+ * hangs off the top edge, and the body means the page, all of it.
71
+ */
72
+ topOutside?: number;
62
73
  /** The board a descent enters: a tab container's ACTIVE page, a section's own board. Null: nothing to enter. */
63
74
  inner: ZoneBoard | null;
64
75
  }
@@ -197,6 +208,7 @@ export interface ContainerBands {
197
208
  stripHeight: number;
198
209
  band: number;
199
210
  bandY?: number;
211
+ topOutside?: number;
200
212
  }
201
213
  /**
202
214
  * The deepest container under a pointer whose EDGES mean something — the one
@@ -211,9 +223,15 @@ export interface ContainerBands {
211
223
  * something on top of the tab group?")
212
224
  */
213
225
  export declare function containerUnder(p: ContainerProbe): ContainerBands | null;
214
- /** Where a container's four bands are, in the same world units as its frame. */
226
+ /**
227
+ * Where a container's bands are, in the same world units as its frame. TOP
228
+ * appears twice: hanging above the frame, where a hand looking for "above it"
229
+ * goes, and under the strip, which is the only one a container pressed against
230
+ * the top of its canvas can offer. Both mean the same thing.
231
+ */
215
232
  export declare function bandRects(c: ContainerBands): {
216
233
  side: BesideSide;
234
+ name: string;
217
235
  rect: ZoneRect;
218
236
  }[];
219
237
  export declare function resolve(input: ResolveInput): Zone;
@@ -102,7 +102,15 @@ export function stripUnder(p) {
102
102
  const ty = atRest ? y : y + dy;
103
103
  const held = c.id === p.held;
104
104
  const pad = held ? p.stay : 0;
105
- if (c.stripHeight > 0 && inRect({ x: frame.x, y: frame.y, width: frame.width, height: c.stripHeight }, tx, ty, pad)) {
105
+ // The stay reaches DOWN into the body and out to the sides, never UP:
106
+ // above the strip is the band that means "above this container" (0.4.65),
107
+ // and a strip that kept the hand 9 px into it would eat a third of it.
108
+ const onStrip = c.stripHeight > 0 &&
109
+ tx >= frame.x - pad &&
110
+ tx <= frame.x + frame.width + pad &&
111
+ ty >= frame.y &&
112
+ ty <= frame.y + c.stripHeight + pad;
113
+ if (onStrip) {
106
114
  if (bestId === null || held || (!bestHeld && b.depth >= bestDepth)) {
107
115
  bestId = c.id;
108
116
  bestDepth = b.depth;
@@ -134,7 +142,7 @@ export function containerUnder(p) {
134
142
  let best = null;
135
143
  let bestDepth = -1;
136
144
  const visit = (b, dx, dy) => {
137
- var _a, _b, _c;
145
+ var _a, _b, _c, _d;
138
146
  for (const c of b.children()) {
139
147
  if ((_a = p.ghostSubtree) === null || _a === void 0 ? void 0 : _a.has(c.id))
140
148
  continue;
@@ -143,10 +151,12 @@ export function containerUnder(p) {
143
151
  const frame = atRest !== null && atRest !== void 0 ? atRest : c.frame;
144
152
  const tx = atRest ? x : x + dx;
145
153
  const ty = atRest ? y : y + dy;
146
- if (!inRect(frame, tx, ty))
154
+ const up = (_c = c.topOutside) !== null && _c !== void 0 ? _c : 0;
155
+ const inOverhang = up > 0 && tx >= frame.x && tx <= frame.x + frame.width && ty < frame.y && ty >= frame.y - up;
156
+ if (!inRect(frame, tx, ty) && !inOverhang)
147
157
  continue;
148
- if (c.band > 0 && !((_c = p.homeChain) === null || _c === void 0 ? void 0 : _c.has(c.id)) && b.depth >= bestDepth) {
149
- best = Object.assign({ containerId: c.id, frame, stripHeight: c.stripHeight, band: c.band }, (c.bandY === undefined ? {} : { bandY: c.bandY }));
158
+ if (c.band > 0 && !((_d = p.homeChain) === null || _d === void 0 ? void 0 : _d.has(c.id)) && b.depth >= bestDepth) {
159
+ best = Object.assign(Object.assign({ containerId: c.id, frame, stripHeight: c.stripHeight, band: c.band }, (c.bandY === undefined ? {} : { bandY: c.bandY })), (c.topOutside === undefined ? {} : { topOutside: c.topOutside }));
150
160
  bestDepth = b.depth;
151
161
  }
152
162
  if (c.inner)
@@ -157,21 +167,41 @@ export function containerUnder(p) {
157
167
  visit(r, 0, 0);
158
168
  return best;
159
169
  }
160
- /** Where a container's four bands are, in the same world units as its frame. */
170
+ /**
171
+ * Where a container's bands are, in the same world units as its frame. TOP
172
+ * appears twice: hanging above the frame, where a hand looking for "above it"
173
+ * goes, and under the strip, which is the only one a container pressed against
174
+ * the top of its canvas can offer. Both mean the same thing.
175
+ */
161
176
  export function bandRects(c) {
177
+ var _a;
162
178
  const bodyY = c.frame.y + c.stripHeight;
163
179
  const bodyH = Math.max(1, c.frame.height - c.stripHeight);
164
180
  const w = Math.max(1, c.frame.width);
165
181
  const side = w * c.band;
166
182
  const depth = c.bandY !== undefined && c.bandY > 0 ? Math.min(c.bandY, bodyH / 2) : c.band * bodyH;
183
+ const up = (_a = c.topOutside) !== null && _a !== void 0 ? _a : 0;
167
184
  return [
168
- { side: 'left', rect: { x: c.frame.x, y: bodyY, width: side, height: bodyH } },
169
- { side: 'right', rect: { x: c.frame.x + w - side, y: bodyY, width: side, height: bodyH } },
170
- // the sides take the corners (bandOf tests rx first), so top and bottom stop short of them
171
- { side: 'top', rect: { x: c.frame.x + side, y: bodyY, width: w - 2 * side, height: depth } },
172
- { side: 'bottom', rect: { x: c.frame.x + side, y: bodyY + bodyH - depth, width: w - 2 * side, height: depth } },
185
+ { side: 'left', name: 'left', rect: { x: c.frame.x, y: bodyY, width: side, height: bodyH } },
186
+ { side: 'right', name: 'right', rect: { x: c.frame.x + w - side, y: bodyY, width: side, height: bodyH } },
187
+ // above the frame: the whole width, since no side band is up there to take the corners
188
+ ...(up > 0 ? [{ side: 'top', name: 'top', rect: { x: c.frame.x, y: c.frame.y - up, width: w, height: up } }] : []),
189
+ // and under the strip; the sides take the corners here (bandOf tests rx first)
190
+ { side: 'top', name: 'top-inside', rect: { x: c.frame.x + side, y: bodyY, width: w - 2 * side, height: depth } },
191
+ { side: 'bottom', name: 'bottom', rect: { x: c.frame.x + side, y: bodyY + bodyH - depth, width: w - 2 * side, height: depth } },
173
192
  ];
174
193
  }
194
+ /**
195
+ * The band a point is in for a TILE drag: the sides and the bottom as
196
+ * `bandOf` gives them, and "top" ONLY from the band hanging above the frame.
197
+ * The rows under the strip are the page.
198
+ */
199
+ function tileBand(f, stripHeight, x, y, band, bandY, topOutside, grow = 1) {
200
+ const up = (topOutside !== null && topOutside !== void 0 ? topOutside : 0) * grow;
201
+ if (up > 0 && x >= f.x && x <= f.x + f.width && y < f.y && y >= f.y - up)
202
+ return 'top';
203
+ return bandOf(f, stripHeight, x, y, band, bandY === undefined ? undefined : bandY * grow);
204
+ }
175
205
  /** The board a container sits on, found by id through the tree. */
176
206
  function boardOf(roots, containerId) {
177
207
  const visit = (b) => {
@@ -206,8 +236,9 @@ export function resolve(input) {
206
236
  const c0 = containerOf(input.roots, p.containerId);
207
237
  const stripH = (_a = c0 === null || c0 === void 0 ? void 0 : c0.stripHeight) !== null && _a !== void 0 ? _a : 0;
208
238
  const depth = c0 === null || c0 === void 0 ? void 0 : c0.bandY;
209
- const stay = bandOf(p.frame0, stripH, x, y, BESIDE_BAND + BESIDE_STAY, depth === undefined ? undefined : depth * (1 + BESIDE_STAY / BESIDE_BAND));
210
- const other = bandOf(p.frame0, stripH, x, y, BESIDE_BAND, depth);
239
+ const grow = 1 + BESIDE_STAY / BESIDE_BAND;
240
+ const stay = tileBand(p.frame0, stripH, x, y, BESIDE_BAND + BESIDE_STAY, depth, c0 === null || c0 === void 0 ? void 0 : c0.topOutside, grow);
241
+ const other = tileBand(p.frame0, stripH, x, y, BESIDE_BAND, depth, c0 === null || c0 === void 0 ? void 0 : c0.topOutside);
211
242
  const onVacated = !!p.vacated && inRect(p.vacated, x, y, input.gap);
212
243
  const held = stay === p.side || (!(other !== null && other !== p.side) && onVacated);
213
244
  if (held) {
@@ -251,7 +282,7 @@ export function resolve(input) {
251
282
  const ndx0 = atRest ? c.frame.x - atRest.x : dx;
252
283
  const ndy0 = atRest ? c.frame.y - atRest.y : dy;
253
284
  const overNested = !!c.inner && c.inner.children().some((cc) => inRect(cc.frame, x + ndx0, y + ndy0));
254
- const side = overNested || input.homeChain.has(c.id) ? null : bandOf(frame, c.stripHeight, tx, ty, c.band, c.bandY);
285
+ const side = overNested || input.homeChain.has(c.id) ? null : tileBand(frame, c.stripHeight, tx, ty, c.band, c.bandY, c.topOutside);
255
286
  if (side)
256
287
  return { kind: 'beside', board, containerId: c.id, side, kept: false };
257
288
  if (opaque(board, c) || !c.inner)
@@ -260,11 +291,44 @@ export function resolve(input) {
260
291
  return { kind: 'plain', board, grace: false }; // the margin: the container's own frame
261
292
  return descend(c.inner, ndx0, ndy0);
262
293
  }
263
- return { kind: 'plain', board, grace: false };
294
+ const over = overhang(board, dx, dy);
295
+ return over !== null && over !== void 0 ? over : { kind: 'plain', board, grace: false };
264
296
  };
297
+ /**
298
+ * The bands that hang ABOVE a container's top edge, once nothing on the
299
+ * board holds the point. Always a second pass: a container whose frame
300
+ * actually holds the hand must never lose to a neighbour's overhang. The
301
+ * band of a container at the board's first row hangs off the board
302
+ * altogether, so the grace path asks for it too.
303
+ */
304
+ function overhang(board, dx, dy) {
305
+ var _a;
306
+ for (const c of board.children()) {
307
+ if (input.ghostSubtree.has(c.id) || input.homeChain.has(c.id) || c.band <= 0)
308
+ continue;
309
+ const rest = (_a = input.restFrames) === null || _a === void 0 ? void 0 : _a.get(c.id);
310
+ const atRest = rest && inRect(rest, x, y) ? rest : null;
311
+ const frame = atRest !== null && atRest !== void 0 ? atRest : c.frame;
312
+ const tx = atRest ? x : x + dx;
313
+ const ty = atRest ? y : y + dy;
314
+ if (tileBand(frame, c.stripHeight, tx, ty, c.band, c.bandY, c.topOutside) === 'top')
315
+ return { kind: 'beside', board, containerId: c.id, side: 'top', kept: false };
316
+ const deeper = c.inner ? overhang(c.inner, atRest ? c.frame.x - atRest.x : dx, atRest ? c.frame.y - atRest.y : dy) : null;
317
+ if (deeper)
318
+ return deeper;
319
+ }
320
+ return null;
321
+ }
265
322
  for (const root of input.roots)
266
323
  if (root.contains(x, y))
267
324
  return descend(root, 0, 0);
325
+ // Off every board — but a container against the board's first row hangs its
326
+ // "above me" band into the margin above it, which is off the board too.
327
+ for (const root of input.roots) {
328
+ const over = overhang(root, 0, 0);
329
+ if (over)
330
+ return over;
331
+ }
268
332
  // Nothing strict matched: the deepest board whose grace row holds the point.
269
333
  let deepest = null;
270
334
  const visit = (b) => {