@grafloria/element 0.4.19 → 0.4.20

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.19",
3
+ "version": "0.4.20",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
@@ -453,7 +453,15 @@ export function bindDashboardGrid(api, group, options = {}) {
453
453
  designHeight: sizing === 'fit' ? frame().height : designH,
454
454
  rtl,
455
455
  });
456
- const rows = () => Math.max(1, engine.rows());
456
+ /**
457
+ * The rows the board is laid out in. A BOUNDED board (a section) keeps its
458
+ * bound's rows even when its content ends higher — empty rows stay empty
459
+ * rows, as in gridstack — instead of stretching the rest to fill the slab:
460
+ * that stretch made a tile pulled shorter shrink FASTER than the pointer
461
+ * (each row it gave up made the remaining rows taller, so the same pixel
462
+ * height quantised to fewer rows on the next move — kit lab L37).
463
+ */
464
+ const rows = () => Math.max(1, engine.rows(), maxRows !== null && maxRows !== void 0 ? maxRows : 0);
457
465
  const htmlLayer = () => api.container.querySelector('.grafloria-html-layer');
458
466
  const hostOf = (id) => {
459
467
  const esc = typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(id) : id.replace(/"/g, '\\"');
@@ -1692,6 +1700,10 @@ export function bindDashboardGrid(api, group, options = {}) {
1692
1700
  touched = true;
1693
1701
  }
1694
1702
  }
1703
+ else if (!E.s) {
1704
+ // A top-edge pull needs the row ABOVE: the anchored resize path
1705
+ // decides it (refused when that row is taken) — never the section.
1706
+ }
1695
1707
  else if (!engine.resizeCheck(g.id, pulled.w, pulled.h + 1).changed) {
1696
1708
  const res = parent.resizeMemberBy(group.id, +1);
1697
1709
  if (res.changed) {
@@ -1720,15 +1732,22 @@ export function bindDashboardGrid(api, group, options = {}) {
1720
1732
  touched = true;
1721
1733
  }
1722
1734
  }
1723
- else if (pulled.h > 1) {
1724
- engine.resizeCheck(g.id, pulled.w, pulled.h - 1);
1735
+ else if (pulled.h > 1 && E.s) {
1736
+ // Shrink to the row the pointer asks for (one row per move lagged
1737
+ // a fast pull and overshot a slow one), then hand every row the
1738
+ // layout no longer needs back to the board.
1739
+ engine.resizeCheck(g.id, pulled.w, Math.max(1, wantRows));
1725
1740
  const floor = Math.max(designRows, extentOf(engine.getItems()));
1726
- if (slabRows > floor) {
1741
+ let slab = slabRows;
1742
+ let bound = inner;
1743
+ while (slab > floor) {
1727
1744
  const res = parent.resizeMemberBy(group.id, -1);
1728
- if (res.changed) {
1729
- record(res, -1);
1730
- setInnerRows(inner - 1);
1731
- }
1745
+ if (!res.changed)
1746
+ break;
1747
+ record(res, -1);
1748
+ slab -= 1;
1749
+ bound -= 1;
1750
+ setInnerRows(bound);
1732
1751
  }
1733
1752
  touched = true;
1734
1753
  }
@@ -1797,9 +1816,23 @@ export function bindDashboardGrid(api, group, options = {}) {
1797
1816
  const moves = tx !== itemNow.x || ty !== itemNow.y;
1798
1817
  const growing = span.w > itemNow.w || span.h > itemNow.h;
1799
1818
  let changed = false;
1800
- if (moves && growing)
1801
- changed = engine.moveCheck(g.id, tx, ty, { gate: false }).changed || changed;
1802
- changed = engine.resizeCheck(g.id, span.w, span.h).changed || changed;
1819
+ // A north or west pull grows by MOVING the anchor first; when that move
1820
+ // is refused (the row above is taken) the growth is refused with it —
1821
+ // the first version fell through to a plain resize and grew the tile at
1822
+ // the OPPOSITE edge, so pulling a control's top edge up made it taller
1823
+ // at the bottom and pushed its section a row (kit lab L37).
1824
+ let anchored = true;
1825
+ if (moves && growing) {
1826
+ // The anchor may move only into FREE cells: a move that pushes would
1827
+ // relocate the tile (a top-edge pull sent a control to row 0 and its
1828
+ // neighbours below it) instead of growing it.
1829
+ const probe = { x: tx, y: ty, w: itemNow.w, h: itemNow.h };
1830
+ const blocked = engine.getItems().some((o) => o.id !== g.id && o.x < probe.x + probe.w && probe.x < o.x + o.w && o.y < probe.y + probe.h && probe.y < o.y + o.h);
1831
+ anchored = !blocked && engine.moveCheck(g.id, tx, ty, { gate: false }).changed;
1832
+ changed = anchored || changed;
1833
+ }
1834
+ if (anchored)
1835
+ changed = engine.resizeCheck(g.id, span.w, span.h).changed || changed;
1803
1836
  if (moves && !growing)
1804
1837
  changed = engine.moveCheck(g.id, tx, ty, { gate: false }).changed || changed;
1805
1838
  if (changed)
@@ -2126,7 +2159,7 @@ export function bindDashboardGrid(api, group, options = {}) {
2126
2159
  return insideMemberGroupFrame(ev.world.x, ev.world.y) || worldInsideBoard(ev.world.x, ev.world.y);
2127
2160
  },
2128
2161
  onPointerDown(ev, hit) {
2129
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
2162
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
2130
2163
  if (gesture)
2131
2164
  return; // mid-palette
2132
2165
  const target = ((_b = (_a = ev.source) === null || _a === void 0 ? void 0 : _a.target) !== null && _b !== void 0 ? _b : null);
@@ -2136,12 +2169,15 @@ export function bindDashboardGrid(api, group, options = {}) {
2136
2169
  const gripHost = gripHostOf(target);
2137
2170
  const gripId = (_c = gripHost === null || gripHost === void 0 ? void 0 : gripHost.getAttribute('data-node-id')) !== null && _c !== void 0 ? _c : null;
2138
2171
  const onGrip = !!gripId && ((_d = group.members) !== null && _d !== void 0 ? _d : new Set()).has(gripId);
2139
- if (!hit.node && !onGrip) {
2172
+ // A SECTION's corner handle sits on top of whatever tile shares that
2173
+ // corner; the DOM target names the section, the hit test the tile.
2174
+ const sectionHandle = (_e = target === null || target === void 0 ? void 0 : target.closest) === null || _e === void 0 ? void 0 : _e.call(target, '.axdb-slab > .axdb-rs');
2175
+ if ((!hit.node && !onGrip) || sectionHandle) {
2140
2176
  // A press on a SECTION — its empty band, its corner handle or its
2141
2177
  // frame edge — selects the section; the handle or an edge resizes it.
2142
- const slabHandle = (_e = target === null || target === void 0 ? void 0 : target.closest) === null || _e === void 0 ? void 0 : _e.call(target, '.axdb-slab > .axdb-rs');
2143
- const slabId = (_g = (_f = slabHandle === null || slabHandle === void 0 ? void 0 : slabHandle.parentElement) === null || _f === void 0 ? void 0 : _f.getAttribute('data-slab-id')) !== null && _g !== void 0 ? _g : memberGroupAt(ev.world.x, ev.world.y);
2144
- const grp = slabId && ((_h = group.members) !== null && _h !== void 0 ? _h : new Set()).has(slabId) ? diagram.getGroup(slabId) : undefined;
2178
+ const slabHandle = (_f = target === null || target === void 0 ? void 0 : target.closest) === null || _f === void 0 ? void 0 : _f.call(target, '.axdb-slab > .axdb-rs');
2179
+ const slabId = (_h = (_g = slabHandle === null || slabHandle === void 0 ? void 0 : slabHandle.parentElement) === null || _g === void 0 ? void 0 : _g.getAttribute('data-slab-id')) !== null && _h !== void 0 ? _h : memberGroupAt(ev.world.x, ev.world.y);
2180
+ const grp = slabId && ((_j = group.members) !== null && _j !== void 0 ? _j : new Set()).has(slabId) ? diagram.getGroup(slabId) : undefined;
2145
2181
  if (slabId && grp) {
2146
2182
  selectWidget(slabId);
2147
2183
  api.render();
@@ -2160,7 +2196,7 @@ export function bindDashboardGrid(api, group, options = {}) {
2160
2196
  // forwards the pointer sequence.
2161
2197
  const parent = parentPeer();
2162
2198
  if ((parent === null || parent === void 0 ? void 0 : parent.selectMember) && worldInsideBoard(ev.world.x, ev.world.y)) {
2163
- const ownHandle = ((_j = slabHandle === null || slabHandle === void 0 ? void 0 : slabHandle.parentElement) === null || _j === void 0 ? void 0 : _j.getAttribute('data-slab-id')) === group.id;
2199
+ const ownHandle = ((_k = slabHandle === null || slabHandle === void 0 ? void 0 : slabHandle.parentElement) === null || _k === void 0 ? void 0 : _k.getAttribute('data-slab-id')) === group.id;
2164
2200
  parent.selectMember(group.id);
2165
2201
  if (!isStatic && parent.beginSlabResize) {
2166
2202
  const edges = ownHandle
@@ -2173,7 +2209,7 @@ export function bindDashboardGrid(api, group, options = {}) {
2173
2209
  }
2174
2210
  // The board's own empty area: a void click. Nothing to drag, and the
2175
2211
  // selection clears exactly as a click outside any board would.
2176
- (_l = (_k = diagram).clearSelection) === null || _l === void 0 ? void 0 : _l.call(_k);
2212
+ (_m = (_l = diagram).clearSelection) === null || _m === void 0 ? void 0 : _m.call(_l);
2177
2213
  selectWidget(undefined);
2178
2214
  api.render();
2179
2215
  return;
@@ -2182,17 +2218,17 @@ export function bindDashboardGrid(api, group, options = {}) {
2182
2218
  if (!node)
2183
2219
  return;
2184
2220
  selectWidget(node.id); // a press selects, whether or not it starts a gesture
2185
- if (((_m = node.state) === null || _m === void 0 ? void 0 : _m.locked) === true)
2221
+ if (((_o = node.state) === null || _o === void 0 ? void 0 : _o.locked) === true)
2186
2222
  return; // pinned: refuse; click still focuses
2187
2223
  if (isStatic)
2188
2224
  return; // a static board: claimed and deadened, click still focuses
2189
2225
  // Which edges did the press take? The corner handle names its own (s+e,
2190
2226
  // or s+w on RTL); a bare press within EDGE_GRIP of the tile's border
2191
2227
  // takes that border; anywhere else is a move. A grip press is a move.
2192
- const onHandle = !!((_o = target === null || target === void 0 ? void 0 : target.closest) === null || _o === void 0 ? void 0 : _o.call(target, '.axdb-rs'));
2228
+ const onHandle = !!((_p = target === null || target === void 0 ? void 0 : target.closest) === null || _p === void 0 ? void 0 : _p.call(target, '.axdb-rs'));
2193
2229
  const hostEl = hostOf(node.id);
2194
- const resizable = ((_p = node.getMetadata) === null || _p === void 0 ? void 0 : _p.call(node, 'widgetResizable')) !== false;
2195
- const movable = ((_q = node.getMetadata) === null || _q === void 0 ? void 0 : _q.call(node, 'widgetMovable')) !== false;
2230
+ const resizable = ((_q = node.getMetadata) === null || _q === void 0 ? void 0 : _q.call(node, 'widgetResizable')) !== false;
2231
+ const movable = ((_r = node.getMetadata) === null || _r === void 0 ? void 0 : _r.call(node, 'widgetMovable')) !== false;
2196
2232
  // `ev.screen` is ELEMENT-LOCAL px; host rects are in client px. Compare
2197
2233
  // like with like — the source event's clientX/Y when there is one, else
2198
2234
  // the container's origin plus the local offset.
@@ -2240,7 +2276,7 @@ export function bindDashboardGrid(api, group, options = {}) {
2240
2276
  startSize: { width: node.size.width, height: node.size.height },
2241
2277
  startPos: { x: node.position.x, y: node.position.y },
2242
2278
  edges,
2243
- spans: { w: (_r = it === null || it === void 0 ? void 0 : it.w) !== null && _r !== void 0 ? _r : 1, h: (_s = it === null || it === void 0 ? void 0 : it.h) !== null && _s !== void 0 ? _s : 1 },
2279
+ spans: { w: (_s = it === null || it === void 0 ? void 0 : it.w) !== null && _s !== void 0 ? _s : 1, h: (_t = it === null || it === void 0 ? void 0 : it.h) !== null && _t !== void 0 ? _t : 1 },
2244
2280
  removedFromBoard: false,
2245
2281
  leg: null,
2246
2282
  lastWorld: null,
@@ -358,9 +358,11 @@ const CSS = `
358
358
  /* SECTION CHROME: a pointer-transparent overlay on every member group. It
359
359
  wears the selection ring and, while selected, the corner handle. */
360
360
  .grafloria-html-layer > .axdb-slab { position: absolute; pointer-events: none; border-radius: var(--axdb-rs-radius, 3px); z-index: 4; }
361
- .grafloria-html-layer > .axdb-slab.axdb-slab--selected { box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)); }
362
- .grafloria-html-layer > .axdb-slab > .axdb-rs { pointer-events: auto; opacity: 0; }
363
- .grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-rs { opacity: 1; }
361
+ /* Selected, the overlay rises above the tiles so ITS corner handle wins a
362
+ corner it shares with a child's; unselected, its handle takes no presses. */
363
+ .grafloria-html-layer > .axdb-slab.axdb-slab--selected { z-index: 6; box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)); }
364
+ .grafloria-html-layer > .axdb-slab > .axdb-rs { pointer-events: none; opacity: 0; }
365
+ .grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-rs { pointer-events: auto; opacity: 1; }
364
366
  .grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-rs { display: none; }
365
367
 
366
368
  /* legend chips, shared by line and donut */