flexdesk 0.6.2 → 0.7.0

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": "flexdesk",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "A tiling window manager and widget set for desktop-class web applications. Zero runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,7 +27,9 @@
27
27
  * ══ GESTURES ════════════════════════════════════════════════════════════
28
28
  *
29
29
  * A press SELECTS — it toggles classes and fills the settings panel, and
30
- * rebuilds no row, so the click lands on the node the press went down on.
30
+ * rebuilds no row, so the click lands on the node the press went down on. In
31
+ * a narrow editor, where the panel is stacked under the list, a press (like
32
+ * Enter, Go to it and `select()`) scrolls the panel on screen when it is off it.
31
33
  * "+" between rows (on hover AND on focus), the add rows and a row's menu
32
34
  * open the step picker and the step menu, both the kit's popovers. A step is
33
35
  * dragged by its grip with POINTER EVENTS and a ghost on `document.body`
@@ -528,7 +530,7 @@ export function createOutlineEditor(host, options = {}) {
528
530
  if (fs.length) row.appendChild(badge(fs));
529
531
  item.appendChild(row);
530
532
  item.setAttribute('aria-label', [map.startLabel(S), sum].filter(Boolean).join(', '));
531
- row.addEventListener('click', () => select('flow', { focus: true }));
533
+ row.addEventListener('click', () => select('flow', { focus: true, picked: true }));
532
534
  if (filter) item.classList.add('twm-flow-outline__item--context');
533
535
  itemEls.set('flow', item);
534
536
  return item;
@@ -645,7 +647,7 @@ export function createOutlineEditor(host, options = {}) {
645
647
  row.appendChild(menu);
646
648
  row.addEventListener('contextmenu', (ev) => { ev.preventDefault(); select(item.id); openMenu(item.id, menu); });
647
649
  }
648
- row.addEventListener('click', () => select(item.id, { focus: true }));
650
+ row.addEventListener('click', () => select(item.id, { focus: true, picked: true }));
649
651
  return { row, label: [name, type?.label !== name ? type?.label : null, summary].filter(Boolean).join(', ') };
650
652
  }
651
653
 
@@ -836,7 +838,13 @@ export function createOutlineEditor(host, options = {}) {
836
838
  }
837
839
  }
838
840
 
839
- function select(id, { focus = false, silent = false } = {}) {
841
+ /**
842
+ * `picked`: the step was PICKED — a press on its row, Go to it, `select()`
843
+ * — so its settings are what the person looks at next, and they are
844
+ * brought on screen (`revealSettings`). ↑ ↓ ← → only move the selection
845
+ * through the list and leave the list where it is.
846
+ */
847
+ function select(id, { focus = false, silent = false, picked = false } = {}) {
840
848
  const next = id === null || id === undefined ? 'flow' : id;
841
849
  const changed = next !== selected;
842
850
  selected = next;
@@ -844,9 +852,33 @@ export function createOutlineEditor(host, options = {}) {
844
852
  if (changed) { showPanel(); drawAside(); }
845
853
  void silent;
846
854
  if (focus) itemEls.get(selected)?.focus({ preventScroll: false });
855
+ if (picked) revealSettings();
847
856
  if (changed) onSelect?.({ kind: selected === 'flow' ? 'flow' : 'step', id: selected === 'flow' ? null : selected });
848
857
  }
849
858
 
859
+ /**
860
+ * BRING THE SETTINGS ON SCREEN when the layout has put them off it — the
861
+ * one rule for Enter, a press on a row, Go to it and `select()`. Under
862
+ * 1000px the columns wrap and the body scrolls as one (the container query
863
+ * in the sheet), so the panel sits under the whole list: a step picked
864
+ * there filled a panel ~860px below with nothing scrolled, and the press
865
+ * looked like it did nothing. A panel wholly inside the body's visible box
866
+ * — always, side by side, where the panel IS the body's height — or one
867
+ * that covers it is left alone, so nothing moves, the page included;
868
+ * otherwise it is scrolled into view, 'nearest' (all of it when it fits,
869
+ * its top when it does not).
870
+ */
871
+ function revealSettings() {
872
+ if (destroyed) return;
873
+ const box = body.getBoundingClientRect();
874
+ if (!box.height) return; // not laid out: hidden, detached
875
+ const r = settingsHost.getBoundingClientRect();
876
+ const inside = r.top >= box.top - 1 && r.bottom <= box.bottom + 1;
877
+ const covers = r.top <= box.top + 1 && r.bottom >= box.bottom - 1;
878
+ if (inside || covers) return;
879
+ settingsHost.scrollIntoView?.({ block: 'nearest' });
880
+ }
881
+
850
882
  function focusSelected() {
851
883
  const li = itemEls.get(selected) ?? itemEls.get('flow');
852
884
  if (li) li.focus({ preventScroll: true });
@@ -1152,7 +1184,8 @@ export function createOutlineEditor(host, options = {}) {
1152
1184
  }
1153
1185
 
1154
1186
  function rename(id) {
1155
- select(id);
1187
+ // The title is focused without scrolling, so it is brought on screen first.
1188
+ select(id, { picked: true });
1156
1189
  const input = settingsHost.querySelector('[data-twm-flow-title]');
1157
1190
  if (input) { input.focus({ preventScroll: true }); input.select?.(); }
1158
1191
  }
@@ -1168,7 +1201,7 @@ export function createOutlineEditor(host, options = {}) {
1168
1201
  function goTo(f) {
1169
1202
  const id = f?.node_id && nodeOf(f.node_id) ? f.node_id : 'flow';
1170
1203
  if (reveal(id)) render();
1171
- select(id, { focus: true });
1204
+ select(id, { focus: true, picked: true });
1172
1205
  if (f?.field) panel.focusField(f.field);
1173
1206
  }
1174
1207
 
@@ -1208,6 +1241,7 @@ export function createOutlineEditor(host, options = {}) {
1208
1241
  const f = settingsHost.querySelector('.twm-flow-panel__fields input, .twm-flow-panel__fields select, '
1209
1242
  + '.twm-flow-panel__fields textarea, .twm-flow-panel__fields [contenteditable="true"], .twm-flow-panel__fields button')
1210
1243
  || settingsHost.querySelector('[data-twm-flow-title]');
1244
+ if (f) revealSettings();
1211
1245
  f?.focus({ preventScroll: false });
1212
1246
  return Boolean(f);
1213
1247
  },
@@ -1477,11 +1511,15 @@ export function createOutlineEditor(host, options = {}) {
1477
1511
  updateTools();
1478
1512
  },
1479
1513
  setStatus(text) { statusEl.textContent = text ?? ''; },
1480
- /** Select a step (unfolding the blocks around it, so it is on the screen), the flow, or nothing. */
1514
+ /**
1515
+ * Select a step (unfolding the blocks around it, so it is on the
1516
+ * screen), the flow, or nothing — and, in a narrow editor whose panel
1517
+ * is scrolled off, bring its settings on screen as a press does.
1518
+ */
1481
1519
  select(id) {
1482
1520
  const want = id === null || id === undefined ? 'flow' : id;
1483
1521
  if (want !== 'flow' && reveal(want)) render();
1484
- select(want);
1522
+ select(want, { picked: true });
1485
1523
  },
1486
1524
  focus() { focusSelected(); },
1487
1525
  undo() { if (editable() && history.undo()) emit('undo'); },