flexdesk 0.4.0 → 0.4.1
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/README.md +10 -1
- package/dist/wm.js +67 -1
- package/dist/wm.js.map +2 -2
- package/package.json +1 -1
- package/src/tiling/shell.js +7 -0
- package/src/tiling/wm.js +78 -2
package/package.json
CHANGED
package/src/tiling/shell.js
CHANGED
|
@@ -86,6 +86,11 @@ import { openForm } from '../ui/components/modal.js';
|
|
|
86
86
|
* window onto a tile puts it back in the
|
|
87
87
|
* tree. Default off — it changes what a
|
|
88
88
|
* drag to an edge does.
|
|
89
|
+
* @param {boolean} [cfg.backToOpenList] C32. Whether Backspace on a record
|
|
90
|
+
* tab closes it and returns to a list of
|
|
91
|
+
* its section that is already open, rather
|
|
92
|
+
* than rewriting it into a second copy of
|
|
93
|
+
* that list. Default off.
|
|
89
94
|
* @param {'top'|'bottom'} [cfg.tabLayout] C22. Where a multi-tab leaf draws its
|
|
90
95
|
* tabs. `'bottom'` is the framework's own
|
|
91
96
|
* spreadsheet strip under the tile body
|
|
@@ -122,6 +127,7 @@ export async function createShell({
|
|
|
122
127
|
panels = null,
|
|
123
128
|
snapPromotion = false,
|
|
124
129
|
promoteInPlace = false,
|
|
130
|
+
backToOpenList = false,
|
|
125
131
|
tabLayout = null,
|
|
126
132
|
chrome = {},
|
|
127
133
|
// An embedder that moved its sections out of the top bar — into an icon
|
|
@@ -163,6 +169,7 @@ export async function createShell({
|
|
|
163
169
|
panelDefaults: panels,
|
|
164
170
|
snapPromotion,
|
|
165
171
|
promoteInPlace,
|
|
172
|
+
backToOpenList,
|
|
166
173
|
tabLayout,
|
|
167
174
|
// `ctx` is the delivery vehicle for leaf-mounted chrome: tile_renderer
|
|
168
175
|
// spreads it into every content factory, which is how the breadcrumb
|
package/src/tiling/wm.js
CHANGED
|
@@ -34,7 +34,7 @@ const PANEL_TITLES = {
|
|
|
34
34
|
export class WindowManager {
|
|
35
35
|
constructor({ rootEl, api, ctx, onChange, eventBus, host, taxonomy, events, content,
|
|
36
36
|
panelDefaults = null, snapPromotion = false,
|
|
37
|
-
promoteInPlace = false, tabLayout = null }) {
|
|
37
|
+
promoteInPlace = false, tabLayout = null, backToOpenList = false }) {
|
|
38
38
|
if (!taxonomy) throw new Error('WindowManager: a taxonomy is required');
|
|
39
39
|
if (!content || typeof content.mount !== 'function') {
|
|
40
40
|
throw new Error('WindowManager: a content registry is required '
|
|
@@ -83,6 +83,12 @@ export class WindowManager {
|
|
|
83
83
|
* changes where a promoted window can be dragged, and an existing
|
|
84
84
|
* embedder upgrading must not find its windows suddenly clipped. */
|
|
85
85
|
this.promoteInPlace = !!promoteInPlace;
|
|
86
|
+
/** C32. Whether Backspace on an ENTITY tab closes it and returns to a list
|
|
87
|
+
* that is already open, instead of turning the tab into a second copy of
|
|
88
|
+
* that list. See `_backToOpenList`. Default off: it changes what the key
|
|
89
|
+
* does, and an existing embedder upgrading must not find tabs closing
|
|
90
|
+
* where they used to navigate. */
|
|
91
|
+
this.backToOpenList = !!backToOpenList;
|
|
86
92
|
this._snapCtl = null;
|
|
87
93
|
this.desktops = new DesktopManager({
|
|
88
94
|
seed: this._rootLeaf, panelDefaults: this.panelDefaults });
|
|
@@ -285,7 +291,10 @@ export class WindowManager {
|
|
|
285
291
|
this._notifyChange('navigate-back');
|
|
286
292
|
return;
|
|
287
293
|
}
|
|
288
|
-
// (2)
|
|
294
|
+
// (2) C32. An entity tab with nowhere left to go back to, beside a list
|
|
295
|
+
// of its own section that is already open: close it and land there.
|
|
296
|
+
if (this.backToOpenList && this._backToOpenList(id)) return;
|
|
297
|
+
// (3) Taxonomy fallback — the user's path is gone, so walk the
|
|
289
298
|
// canonical parent instead. An ENTITY-level parent wins (the
|
|
290
299
|
// instance one level up, e.g. the parent an id names); failing
|
|
291
300
|
// that, the KIND-level parent (this page's section).
|
|
@@ -347,6 +356,68 @@ export class WindowManager {
|
|
|
347
356
|
/** Navigate inside the current tab — preserves every other tab in
|
|
348
357
|
* the leaf. Used by Backspace + breadcrumb segments + anything
|
|
349
358
|
* else that should walk WITHIN the tile rather than reset it. */
|
|
359
|
+
/**
|
|
360
|
+
* C32. BACK FROM AN OPENED RECORD RETURNS TO THE LIST, IT DOES NOT MAKE ONE.
|
|
361
|
+
*
|
|
362
|
+
* The gesture this is for: a list tab, a record opened from it into a NEW
|
|
363
|
+
* tab, and Backspace in that record. With nothing left in the record tab's
|
|
364
|
+
* own history, step (3) of `navigateBack` walks the taxonomy up and REWRITES
|
|
365
|
+
* the record tab into its parent — so the tile ends up showing the list
|
|
366
|
+
* twice, once in the tab you started from and once in the tab you just left.
|
|
367
|
+
* The user's model is a browser's: you came from the list, the list is still
|
|
368
|
+
* open, so going back means closing this and being on the list again.
|
|
369
|
+
*
|
|
370
|
+
* So: when the active tab is an ENTITY — its props carry an `id`, the same
|
|
371
|
+
* test `swapToPage` uses for "the caller asked for a specific entity" — look
|
|
372
|
+
* for an open tab of the SAME section that is NOT one. That is the list or
|
|
373
|
+
* landing page the record belongs to. Close the record and activate it.
|
|
374
|
+
*
|
|
375
|
+
* NEAREST TO THE LEFT FIRST. New tabs are appended at the end, so the tab a
|
|
376
|
+
* record was opened from sits to its left; when several lists of the section
|
|
377
|
+
* are open, the nearest one on that side is the one it most plausibly came
|
|
378
|
+
* from. The right side is searched only after the left has nothing.
|
|
379
|
+
*
|
|
380
|
+
* DECLINES, and leaves step (3) to walk up in place, whenever there is no
|
|
381
|
+
* such tab: a record that is the tile's only tab, or one whose section has no
|
|
382
|
+
* list open. That keeps the one guarantee the whole rule exists for — Back
|
|
383
|
+
* never leaves two copies of a list — without ever closing a record into a
|
|
384
|
+
* tile that has nothing of its own to show.
|
|
385
|
+
*
|
|
386
|
+
* Matching is by SECTION (`topNavFor`) and not by kind, because the list a
|
|
387
|
+
* record was opened from is often not the record's taxonomy parent: a root
|
|
388
|
+
* landing page that renders the section, or a list that a flattened taxonomy
|
|
389
|
+
* files beside the record rather than above it.
|
|
390
|
+
*
|
|
391
|
+
* @returns {boolean} whether it closed a tab
|
|
392
|
+
*/
|
|
393
|
+
_backToOpenList(leafId) {
|
|
394
|
+
const tree = this._tree();
|
|
395
|
+
const leaf = tree.get(leafId);
|
|
396
|
+
if (!leaf || leaf.kind !== 'leaf') return false;
|
|
397
|
+
const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];
|
|
398
|
+
if (tabs.length < 2) return false;
|
|
399
|
+
const activeIdx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));
|
|
400
|
+
const active = tabs[activeIdx];
|
|
401
|
+
if (!_isEntityTab(active)) return false;
|
|
402
|
+
const section = this.taxonomy.topNavFor(active.kind);
|
|
403
|
+
if (!section) return false;
|
|
404
|
+
|
|
405
|
+
const isList = (t) => t && !_isEntityTab(t) && this.taxonomy.topNavFor(t.kind) === section;
|
|
406
|
+
let target = -1;
|
|
407
|
+
for (let i = activeIdx - 1; i >= 0 && target < 0; i--) if (isList(tabs[i])) target = i;
|
|
408
|
+
for (let i = activeIdx + 1; i < tabs.length && target < 0; i++) if (isList(tabs[i])) target = i;
|
|
409
|
+
if (target < 0) return false;
|
|
410
|
+
|
|
411
|
+
tree.removeLeafTab(leafId, activeIdx);
|
|
412
|
+
// Removing the active tab shifts everything after it one to the left.
|
|
413
|
+
tree.setActiveLeafTab(leafId, target > activeIdx ? target - 1 : target);
|
|
414
|
+
tree.focus(leafId);
|
|
415
|
+
this.renderer.render();
|
|
416
|
+
this._persist();
|
|
417
|
+
this._notifyChange('navigate-back-close');
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
|
|
350
421
|
navigateActiveTab(leafId, kind, props = {}) {
|
|
351
422
|
const tree = this._tree();
|
|
352
423
|
const leaf = tree.get(leafId);
|
|
@@ -3641,3 +3712,8 @@ function _swapSiblings(tree, aId, bId) {
|
|
|
3641
3712
|
return true;
|
|
3642
3713
|
}
|
|
3643
3714
|
|
|
3715
|
+
/** A tab showing ONE record rather than a list: its props name an `id`. The same
|
|
3716
|
+
* test `swapToPage` applies to "the caller asked for a specific entity". */
|
|
3717
|
+
function _isEntityTab(tab) {
|
|
3718
|
+
return !!tab && tab.props != null && tab.props.id != null && tab.props.id !== '';
|
|
3719
|
+
}
|