vintage-frames 0.14.1 → 0.16.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/custom-elements.json +770 -74
- package/dist/chrome.d.ts +10 -1
- package/dist/chrome.js +8 -3
- package/dist/components/vf-button.d.ts +5 -1
- package/dist/components/vf-button.js +1 -1
- package/dist/components/vf-desktop.d.ts +5 -2
- package/dist/components/vf-desktop.js +5 -5
- package/dist/components/vf-dialog.d.ts +13 -2
- package/dist/components/vf-dialog.js +27 -27
- package/dist/components/vf-menu-bar.js +9 -10
- package/dist/components/vf-menu-item.js +18 -13
- package/dist/components/vf-menu.d.ts +8 -3
- package/dist/components/vf-menu.js +85 -61
- package/dist/components/vf-number-field.d.ts +4 -0
- package/dist/components/vf-number-field.js +28 -25
- package/dist/components/vf-paragraph.d.ts +3 -1
- package/dist/components/vf-paragraph.js +7 -0
- package/dist/components/vf-scroll-area.d.ts +16 -1
- package/dist/components/vf-scroll-area.js +39 -29
- package/dist/components/vf-select.d.ts +5 -4
- package/dist/components/vf-select.js +26 -17
- package/dist/components/vf-text-field.d.ts +5 -5
- package/dist/components/vf-text-field.js +1 -1
- package/dist/components/vf-window.d.ts +22 -5
- package/dist/components/vf-window.js +113 -98
- package/dist/cursor.d.ts +8 -0
- package/dist/cursor.js +70 -73
- package/dist/events.d.ts +3 -2
- package/dist/events.js +3 -3
- package/dist/index.d.ts +2 -1
- package/dist/modal-dialog.d.ts +49 -4
- package/dist/modal-dialog.js +115 -87
- package/dist/position.js +25 -20
- package/dist/shell/alert.d.ts +17 -9
- package/dist/shell/alert.js +25 -28
- package/dist/shell/catalog.d.ts +1 -1
- package/dist/shell/catalog.js +32 -18
- package/dist/shell/finder.d.ts +46 -1
- package/dist/shell/finder.js +166 -126
- package/dist/shell/index.d.ts +2 -4
- package/dist/shell/index.js +5 -6
- package/dist/shell/pure.d.ts +10 -0
- package/dist/shell/pure.js +4 -0
- package/dist/shell/shell.d.ts +40 -13
- package/dist/shell/shell.js +87 -51
- package/dist/shell/windows.d.ts +69 -12
- package/dist/shell/windows.js +245 -144
- package/dist/text-control.d.ts +30 -1
- package/dist/text-control.js +27 -6
- package/dist/top-layer.d.ts +13 -0
- package/dist/top-layer.js +16 -0
- package/docs/SPEC.md +25 -24
- package/editor/vscode.html-custom-data.json +22 -12
- package/editor/web-types.json +58 -25
- package/package.json +5 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vintage-frames/shell/pure` — the shell's pure modules on their own: the
|
|
3
|
+
* catalog, the geometry and the saved session. They import nothing from the
|
|
4
|
+
* kit and touch no DOM at import, so a site's own pure modules and their
|
|
5
|
+
* tests import them under Node. `vintage-frames/shell` exports the same
|
|
6
|
+
* names.
|
|
7
|
+
*/
|
|
8
|
+
export * from './catalog.js';
|
|
9
|
+
export * from './geometry.js';
|
|
10
|
+
export * from './state.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { DISK as e, FOLDER as t, TRASH as n, UNTITLED_FOLDER as r, childrenOf as i, containerOf as a, copyName as o, createCatalog as s, descendantsOf as c, enclosingFolders as l, isContainerKind as u, isInside as d, isTrashed as f, isVolume as p, itemCount as m, itemOf as h, memoryStorage as g, nextFolderName as _ } from "./catalog.js";
|
|
2
|
+
import { BAND as v, CASCADE_SLOTS as y, CASCADE_STEP as b, ICON_CELL as x, ICON_COLUMN as S, ICON_INSET as C, ICON_ROW as w, NEAR as T, WINDOW_ORIGIN as E, cascadeFrom as D, cascadeSlot as O, cascadedBox as k, centeredBox as A, cleanUp as j, collisions as M, desktopLattice as N, fieldExtent as P, fillOrder as F, folderLattice as I, frameOf as L, isPin as R, latticeCell as z, latticeSlot as B, nearBox as V, nextFreeCell as H, pinOf as U, pinTo as W, trashCell as G, windowOrigin as K } from "./geometry.js";
|
|
3
|
+
import { localStorageState as q, mergeSession as J, openWindowsOf as Y, readSession as X } from "./state.js";
|
|
4
|
+
export { v as BAND, y as CASCADE_SLOTS, b as CASCADE_STEP, e as DISK, t as FOLDER, x as ICON_CELL, S as ICON_COLUMN, C as ICON_INSET, w as ICON_ROW, T as NEAR, n as TRASH, r as UNTITLED_FOLDER, E as WINDOW_ORIGIN, D as cascadeFrom, O as cascadeSlot, k as cascadedBox, A as centeredBox, i as childrenOf, j as cleanUp, M as collisions, a as containerOf, o as copyName, s as createCatalog, c as descendantsOf, N as desktopLattice, l as enclosingFolders, P as fieldExtent, F as fillOrder, I as folderLattice, L as frameOf, u as isContainerKind, d as isInside, R as isPin, f as isTrashed, p as isVolume, m as itemCount, h as itemOf, z as latticeCell, B as latticeSlot, q as localStorageState, g as memoryStorage, J as mergeSession, V as nearBox, _ as nextFolderName, H as nextFreeCell, Y as openWindowsOf, U as pinOf, W as pinTo, X as readSession, G as trashCell, K as windowOrigin };
|
package/dist/shell/shell.d.ts
CHANGED
|
@@ -7,21 +7,25 @@
|
|
|
7
7
|
* nodes come back each time it is front, so items keep their state. A menu
|
|
8
8
|
* off the bar has no key equivalents, so only the front application's fire.
|
|
9
9
|
* The bar's label is the front application's name.
|
|
10
|
+
* - While an application's held dialog is open, the bar shows that
|
|
11
|
+
* application's name and menus instead; nothing else changes.
|
|
10
12
|
* - Each application's `init(ctx)` gets the desktop, the window manager, its
|
|
11
|
-
* own menus, the system menu, the other applications' actions
|
|
12
|
-
* call), the catalog when a Finder runs, the site's services,
|
|
13
|
-
* `gate()`, `onFront()` and `onDispose()`. Everything it sets up
|
|
14
|
-
* through `onDispose()`, so a hot
|
|
13
|
+
* own menus, dialogs and window markup, the system menu, the other applications' actions
|
|
14
|
+
* (read at the call), the catalog when a Finder runs, the site's services,
|
|
15
|
+
* `ask()`, `gate()`, `onFront()` and `onDispose()`. Everything it sets up
|
|
16
|
+
* comes down through `onDispose()`, and its dialogs with it, so a hot
|
|
17
|
+
* reload starts clean. The shell composes no UI of its own.
|
|
15
18
|
* - A window showing a catalog item follows it: its title follows a rename,
|
|
16
19
|
* and it closes when the item is removed.
|
|
17
|
-
* - The boot
|
|
18
|
-
*
|
|
20
|
+
* - The boot waits for the desktop and its menu bar to render, reads the
|
|
21
|
+
* catalog, seeds it once, reopens the last session's windows deepest
|
|
22
|
+
* first and the active one last, then starts saving.
|
|
19
23
|
*/
|
|
20
|
-
import
|
|
24
|
+
import { VfWindow } from '../index.js';
|
|
25
|
+
import type { VfDesktop, VfDialog, VfMenu, VfMenuItem, VfModalDialog, VfViewportBox } from '../index.js';
|
|
21
26
|
import type { Catalog, CatalogStorage, Item, Volumes } from './catalog.js';
|
|
22
27
|
import type { WindowManager } from './windows.js';
|
|
23
28
|
import type { ShellState } from './state.js';
|
|
24
|
-
import type { AlertOptions } from './alert.js';
|
|
25
29
|
/** A kind of catalog item an application registers: how it looks and opens. */
|
|
26
30
|
export interface KindDefinition {
|
|
27
31
|
/** Its 32×32 art: one URL, or one per item. */
|
|
@@ -57,6 +61,16 @@ export interface AppDefinition<Actions = unknown> {
|
|
|
57
61
|
icon?: string;
|
|
58
62
|
/** A fragment of `vf-menu` elements, each with a `data-menu` name. */
|
|
59
63
|
menus?: string;
|
|
64
|
+
/**
|
|
65
|
+
* A fragment of dialogs, each with a `data-dialog` name: a `vf-dialog`, or
|
|
66
|
+
* an element that renders one inside itself. Held under the application.
|
|
67
|
+
*/
|
|
68
|
+
dialogs?: string;
|
|
69
|
+
/**
|
|
70
|
+
* A fragment of windows, each with a `data-window` name, kept inert:
|
|
71
|
+
* `ctx.window(name)` makes a fresh copy of one on each call.
|
|
72
|
+
*/
|
|
73
|
+
windows?: string;
|
|
60
74
|
/** The catalog kinds it opens. */
|
|
61
75
|
kinds?: Record<string, KindDefinition>;
|
|
62
76
|
/** The Finder's: the catalog every application shares. */
|
|
@@ -98,8 +112,24 @@ export interface AppContext {
|
|
|
98
112
|
onMenu(fn: (value: string, item: VfMenuItem) => void): void;
|
|
99
113
|
/** Put an item in the system menu, which runs `fn` when picked. */
|
|
100
114
|
systemItem(value: string, label: string, fn: () => void): VfMenuItem;
|
|
101
|
-
/**
|
|
102
|
-
|
|
115
|
+
/** One of its dialogs by `data-dialog`; throws when the markup drifts. */
|
|
116
|
+
dialog<T extends HTMLElement = VfDialog>(name: string): T;
|
|
117
|
+
/**
|
|
118
|
+
* A fresh copy of one of its windows by `data-window`, upgraded and not yet
|
|
119
|
+
* appended: `open({ create: () => ctx.window('viewer') })`. Throws when the
|
|
120
|
+
* markup drifts.
|
|
121
|
+
*/
|
|
122
|
+
window<T extends HTMLElement = VfWindow>(name: string): T;
|
|
123
|
+
/** Hold a dialog it made in code: appended to the desktop, held under it, removed with it. Returns it. */
|
|
124
|
+
hold<T extends Element>(dialog: T): T;
|
|
125
|
+
/** Let a dialog it holds go, and remove it. */
|
|
126
|
+
release(dialog: Element): void;
|
|
127
|
+
/**
|
|
128
|
+
* Show a dialog it holds, and resolve its `returnValue`: the value of the
|
|
129
|
+
* button that submitted it, or null when it closed without one. Throws for
|
|
130
|
+
* a dialog it doesn't hold.
|
|
131
|
+
*/
|
|
132
|
+
ask(dialog: VfModalDialog): Promise<string | null>;
|
|
103
133
|
/** Keep `item` disabled while `test` is false: re-run on every press, key, selection change and activation. */
|
|
104
134
|
gate(item: VfMenuItem, test: () => boolean): void;
|
|
105
135
|
/** Whether a text field has keyboard focus, read through shadow roots at the call. */
|
|
@@ -128,8 +158,6 @@ export interface ShellOptions {
|
|
|
128
158
|
services?: Record<string, unknown>;
|
|
129
159
|
/** The clock in the menu bar's `end` slot. Default true. */
|
|
130
160
|
clock?: boolean;
|
|
131
|
-
/** The 32×32 caution art the alerts carry. */
|
|
132
|
-
caution?: string | null;
|
|
133
161
|
}
|
|
134
162
|
/** A running shell. */
|
|
135
163
|
export interface Shell {
|
|
@@ -139,7 +167,6 @@ export interface Shell {
|
|
|
139
167
|
readonly apps: Record<string, any>;
|
|
140
168
|
/** Settles once the boot has read the catalog and reopened the session. */
|
|
141
169
|
readonly ready: Promise<void>;
|
|
142
|
-
alert(message: string, options?: AlertOptions): Promise<string | null>;
|
|
143
170
|
dispose(): void;
|
|
144
171
|
}
|
|
145
172
|
/** The kind the shell registers: an application's icon, which opens it. */
|
package/dist/shell/shell.js
CHANGED
|
@@ -2,10 +2,9 @@ import { onScaleChange as e } from "../scale.js";
|
|
|
2
2
|
import { VfWindow as t } from "../components/vf-window.js";
|
|
3
3
|
import "../index.js";
|
|
4
4
|
import { createCatalog as n } from "./catalog.js";
|
|
5
|
-
import { createWindowManager as r, deepActiveElement as i } from "./windows.js";
|
|
6
|
-
import { openWindowsOf as
|
|
7
|
-
import { startClock as
|
|
8
|
-
import { showAlert as s } from "./alert.js";
|
|
5
|
+
import { createWindowManager as r, deepActiveElement as i, desktopRendered as a } from "./windows.js";
|
|
6
|
+
import { openWindowsOf as o } from "./state.js";
|
|
7
|
+
import { startClock as s } from "./clock.js";
|
|
9
8
|
//#region src/shell/shell.ts
|
|
10
9
|
function c(e) {
|
|
11
10
|
return e;
|
|
@@ -62,22 +61,30 @@ function d(c, l) {
|
|
|
62
61
|
}
|
|
63
62
|
};
|
|
64
63
|
document.addEventListener("pointerdown", A, !0), document.addEventListener("keydown", A, !0), c.addEventListener("vf-select", A), c.addEventListener("vf-activate", A), _.push(() => document.removeEventListener("pointerdown", A, !0), () => document.removeEventListener("keydown", A, !0), () => c.removeEventListener("vf-select", A), () => c.removeEventListener("vf-activate", A), S.onWindows(A), S.onLayout(A)), E && _.push(E.subscribe(A));
|
|
65
|
-
let j = (e
|
|
66
|
-
art: l.caution ?? null,
|
|
67
|
-
...t
|
|
68
|
-
}), M = (e) => {
|
|
64
|
+
let j = (e) => {
|
|
69
65
|
if (!e) return [];
|
|
70
66
|
let t = document.createElement("div");
|
|
71
67
|
t.innerHTML = e, customElements.upgrade(t);
|
|
72
|
-
let n = [...t.children].filter((e) => e
|
|
68
|
+
let n = [...t.children].filter((e) => e instanceof HTMLElement);
|
|
73
69
|
for (let e of n) e.remove();
|
|
74
70
|
return n;
|
|
75
|
-
},
|
|
71
|
+
}, M = d.map((e) => ({
|
|
76
72
|
def: e,
|
|
77
|
-
menus:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
menus: j(e.menus).filter((e) => e.localName === "vf-menu"),
|
|
74
|
+
dialogs: j(e.dialogs).filter((e) => e.dataset.dialog != null),
|
|
75
|
+
windows: (() => {
|
|
76
|
+
let t = document.createElement("template");
|
|
77
|
+
return t.innerHTML = e.windows ?? "", t.content;
|
|
78
|
+
})()
|
|
79
|
+
})), N = [];
|
|
80
|
+
for (let { def: e, menus: t, dialogs: n, windows: r } of M) {
|
|
81
|
+
let i = [], a = `vintage-frames/shell (${e.id})`, o = /* @__PURE__ */ new Set();
|
|
82
|
+
for (let t of n) S.holdDialog(t, { app: e.id }), o.add(t);
|
|
83
|
+
i.push(() => {
|
|
84
|
+
for (let e of o) S.releaseDialog(e), e.remove();
|
|
85
|
+
o.clear();
|
|
86
|
+
});
|
|
87
|
+
let s = {
|
|
81
88
|
desktop: c,
|
|
82
89
|
windows: S,
|
|
83
90
|
menus: t,
|
|
@@ -91,7 +98,7 @@ function d(c, l) {
|
|
|
91
98
|
app: (e) => d.find((t) => t.id === e),
|
|
92
99
|
menu(e) {
|
|
93
100
|
let n = t.find((t) => t.dataset.menu === e);
|
|
94
|
-
if (!n) throw Error(`${
|
|
101
|
+
if (!n) throw Error(`${a}: missing menu ${e}`);
|
|
95
102
|
return n;
|
|
96
103
|
},
|
|
97
104
|
item(e) {
|
|
@@ -99,76 +106,101 @@ function d(c, l) {
|
|
|
99
106
|
let t = n.querySelector(`vf-menu-item[value="${CSS.escape(e)}"]`);
|
|
100
107
|
if (t) return t;
|
|
101
108
|
}
|
|
102
|
-
throw Error(`${
|
|
109
|
+
throw Error(`${a}: missing menu item ${e}`);
|
|
103
110
|
},
|
|
104
111
|
onMenu(e) {
|
|
105
|
-
for (let n of t)
|
|
112
|
+
for (let n of t) s.on(n, "vf-menu-select", (t) => {
|
|
106
113
|
if (D()) return;
|
|
107
114
|
let { value: n, item: r } = t.detail;
|
|
108
115
|
e(n, r);
|
|
109
116
|
});
|
|
110
117
|
},
|
|
111
|
-
systemItem(e, t,
|
|
112
|
-
let
|
|
113
|
-
return
|
|
114
|
-
!D() && t.detail.value === e &&
|
|
115
|
-
}),
|
|
118
|
+
systemItem(e, t, n) {
|
|
119
|
+
let r = document.createElement("vf-menu-item");
|
|
120
|
+
return r.setAttribute("value", e), r.textContent = t, y.append(r), s.on(y, "vf-menu-select", (t) => {
|
|
121
|
+
!D() && t.detail.value === e && n();
|
|
122
|
+
}), i.push(() => r.remove()), r;
|
|
123
|
+
},
|
|
124
|
+
dialog(e) {
|
|
125
|
+
let t = n.find((t) => t.dataset.dialog === e);
|
|
126
|
+
if (!t) throw Error(`${a}: missing dialog ${e}`);
|
|
127
|
+
return t;
|
|
128
|
+
},
|
|
129
|
+
window(e) {
|
|
130
|
+
let t = [...r.children].find((t) => t.dataset.window === e);
|
|
131
|
+
if (!t) throw Error(`${a}: missing window ${e}`);
|
|
132
|
+
let n = document.importNode(t, !0);
|
|
133
|
+
return customElements.upgrade(n), n;
|
|
134
|
+
},
|
|
135
|
+
hold(t) {
|
|
136
|
+
return S.holdDialog(t, { app: e.id }), o.add(t), t;
|
|
137
|
+
},
|
|
138
|
+
release(e) {
|
|
139
|
+
o.delete(e) && (S.releaseDialog(e), e.remove());
|
|
140
|
+
},
|
|
141
|
+
ask(t) {
|
|
142
|
+
if (S.appOf(t) !== e.id) throw Error(`${a}: ask() takes a dialog the application holds`);
|
|
143
|
+
return new Promise((e) => {
|
|
144
|
+
let n = (r) => {
|
|
145
|
+
r.target === t && (t.removeEventListener("vf-close", n), e(r.detail.returnValue));
|
|
146
|
+
};
|
|
147
|
+
t.addEventListener("vf-close", n), t.show();
|
|
148
|
+
});
|
|
116
149
|
},
|
|
117
|
-
alert: j,
|
|
118
150
|
gate(e, t) {
|
|
119
|
-
k.set(e, t),
|
|
120
|
-
let
|
|
121
|
-
e.disabled !==
|
|
151
|
+
k.set(e, t), i.push(() => k.delete(e));
|
|
152
|
+
let n = !t();
|
|
153
|
+
e.disabled !== n && (e.disabled = n);
|
|
122
154
|
},
|
|
123
155
|
typing: O,
|
|
124
156
|
modalOpen: D,
|
|
125
157
|
onFront(t) {
|
|
126
|
-
t(S.front === e.id),
|
|
158
|
+
t(S.front === e.id), i.push(S.onFront((n) => t(n === e.id)));
|
|
127
159
|
},
|
|
128
|
-
on(e, t,
|
|
129
|
-
e.addEventListener(t,
|
|
160
|
+
on(e, t, n, r) {
|
|
161
|
+
e.addEventListener(t, n, r), i.push(() => e.removeEventListener(t, n, r));
|
|
130
162
|
},
|
|
131
163
|
onDispose(e) {
|
|
132
|
-
|
|
164
|
+
i.push(e);
|
|
133
165
|
}
|
|
134
|
-
},
|
|
135
|
-
|
|
136
|
-
for (let e of
|
|
166
|
+
}, l = e.init(s);
|
|
167
|
+
l && (w[e.id] = l), N.push(() => {
|
|
168
|
+
for (let e of i.splice(0).reverse()) e();
|
|
137
169
|
});
|
|
138
170
|
}
|
|
139
|
-
let
|
|
140
|
-
let e =
|
|
141
|
-
if (!(!
|
|
142
|
-
if (
|
|
143
|
-
y.after(...
|
|
171
|
+
let P = null, F = () => {
|
|
172
|
+
let e = S.asking ?? S.front, t = M.find((t) => t.def.id === e) ?? M.find((e) => e.def.id === m) ?? null;
|
|
173
|
+
if (!(!t || t === P)) {
|
|
174
|
+
if (P) for (let e of P.menus) e.remove();
|
|
175
|
+
y.after(...t.menus), v.label = t.def.name, P = t, A();
|
|
144
176
|
}
|
|
145
177
|
};
|
|
146
|
-
if (_.push(S.onFront(
|
|
178
|
+
if (_.push(S.onFront(F), S.onDialogs(F)), F(), l.clock !== !1) {
|
|
147
179
|
let e = v.querySelector(":scope > [slot=\"end\"]"), t = !e;
|
|
148
180
|
e || (e = document.createElement("vf-label"), e.slot = "end", v.append(e));
|
|
149
|
-
let n =
|
|
181
|
+
let n = s(e);
|
|
150
182
|
_.push(() => {
|
|
151
183
|
n(), t && e.remove();
|
|
152
184
|
});
|
|
153
185
|
}
|
|
154
|
-
let
|
|
186
|
+
let I = /* @__PURE__ */ new WeakSet(), L = () => {
|
|
155
187
|
if (E) for (let e of c.stackingOrder) {
|
|
156
188
|
if (!(e instanceof t)) continue;
|
|
157
189
|
let n = S.itemOf(e);
|
|
158
190
|
if (n == null) continue;
|
|
159
191
|
let r = E.item(n);
|
|
160
|
-
r ? (
|
|
192
|
+
r ? (I.add(e), e.heading !== r.name && (e.heading = r.name)) : I.has(e) && E.get().available && S.close(e, { to: null });
|
|
161
193
|
}
|
|
162
194
|
};
|
|
163
|
-
return E && _.push(E.subscribe(
|
|
195
|
+
return E && _.push(E.subscribe(L), S.onWindows(L)), h?.hold(), {
|
|
164
196
|
desktop: c,
|
|
165
197
|
windows: S,
|
|
166
198
|
catalog: E,
|
|
167
199
|
apps: w,
|
|
168
200
|
ready: (async () => {
|
|
169
201
|
try {
|
|
170
|
-
E && (await E.refresh(), p?.catalog?.seed && await E.seed(p.catalog.seed));
|
|
171
|
-
for (let { item: e, app: t } of
|
|
202
|
+
await a(c), E && (await E.refresh(), p?.catalog?.seed && await E.seed(p.catalog.seed));
|
|
203
|
+
for (let { item: e, app: t } of o(h?.saved ?? null)) await w[t]?.open?.({ item: e });
|
|
172
204
|
let e = h?.saved?.active;
|
|
173
205
|
if (e) {
|
|
174
206
|
let t = S.windowFor(e);
|
|
@@ -186,20 +218,24 @@ function d(c, l) {
|
|
|
186
218
|
S.onWindows(e),
|
|
187
219
|
...E ? [E.subscribe(e)] : []
|
|
188
220
|
];
|
|
189
|
-
|
|
221
|
+
c.addEventListener("vf-activate", e);
|
|
222
|
+
let n = new MutationObserver(e);
|
|
223
|
+
return n.observe(c, {
|
|
224
|
+
attributes: !0,
|
|
225
|
+
attributeFilter: ["pattern"]
|
|
226
|
+
}), () => {
|
|
190
227
|
for (let e of t) e();
|
|
191
|
-
c.removeEventListener("vf-activate", e);
|
|
228
|
+
c.removeEventListener("vf-activate", e), n.disconnect();
|
|
192
229
|
};
|
|
193
230
|
});
|
|
194
231
|
_.push(e), h.release();
|
|
195
232
|
}
|
|
196
233
|
}
|
|
197
234
|
})(),
|
|
198
|
-
alert: j,
|
|
199
235
|
dispose() {
|
|
200
|
-
for (let e of
|
|
201
|
-
if (
|
|
202
|
-
|
|
236
|
+
for (let e of N.splice(0).reverse()) e();
|
|
237
|
+
if (P) for (let e of P.menus) e.remove();
|
|
238
|
+
P = null;
|
|
203
239
|
for (let e of _.splice(0)) e();
|
|
204
240
|
S.dispose(), E?.dispose();
|
|
205
241
|
}
|
package/dist/shell/windows.d.ts
CHANGED
|
@@ -29,9 +29,15 @@
|
|
|
29
29
|
* resizable window's pin when its size changed, floors it at the window's
|
|
30
30
|
* own size limits and caps it to the window area, so growing back restores
|
|
31
31
|
* every place.
|
|
32
|
+
* - The zoom box, for a window adopted with `zoom`: the window goes to its
|
|
33
|
+
* zoomed box and back to the box it had, kept as a pin; a zoomed window
|
|
34
|
+
* stays zoomed across a resize.
|
|
35
|
+
* - Dialogs: `holdDialog()` holds an application's dialog under it, in the
|
|
36
|
+
* desktop. The manager follows each held dialog's `open` attribute, and
|
|
37
|
+
* `asking` is the application of the one opened last and still open.
|
|
32
38
|
*/
|
|
33
39
|
import { VfWindow } from '../index.js';
|
|
34
|
-
import type { VfDesktop, VfViewportBox } from '../index.js';
|
|
40
|
+
import type { VfCloseKeys, VfDesktop, VfViewportBox } from '../index.js';
|
|
35
41
|
import type { Bands, Box, Pin, Policy, Size } from './geometry.js';
|
|
36
42
|
/** A box to write, any edge optional. */
|
|
37
43
|
export interface BoxInput {
|
|
@@ -65,6 +71,13 @@ export interface AdoptOptions {
|
|
|
65
71
|
policy?: ((cur: Box) => Policy) | null;
|
|
66
72
|
/** A box held across a raster resize while the window is near it. */
|
|
67
73
|
keep?: ((area: Box) => Box) | null;
|
|
74
|
+
/**
|
|
75
|
+
* Its zoom box: the window's zoomed box on the window area, given its
|
|
76
|
+
* current box (to keep its top-left, say). Set, the manager runs the zoom
|
|
77
|
+
* box: it goes to this box and back to the box the user had, and a window
|
|
78
|
+
* at it stays zoomed across a raster resize.
|
|
79
|
+
*/
|
|
80
|
+
zoom?: ((area: Box, box: Box) => BoxInput) | null;
|
|
68
81
|
/**
|
|
69
82
|
* A palette: shown while its application is front — and while the
|
|
70
83
|
* predicate holds, for one the application hides for its own reasons —
|
|
@@ -74,10 +87,14 @@ export interface AdoptOptions {
|
|
|
74
87
|
/**
|
|
75
88
|
* What the close box means. Unset, it closes the window. Set, the close box
|
|
76
89
|
* asks the application, which calls `close()` once it decides — after a
|
|
77
|
-
* Save prompt, say — or never.
|
|
90
|
+
* Save prompt, say — or never. It gets the modifier keys held on the
|
|
91
|
+
* close box's click; a close from a menu or code holds none. A handler
|
|
92
|
+
* that decides later returns its promise, so `closeAll()` waits for it.
|
|
78
93
|
*/
|
|
79
|
-
close?:
|
|
94
|
+
close?: CloseHandler | null;
|
|
80
95
|
}
|
|
96
|
+
/** A window's close handler: decides whether it closes, and calls `close()` if so. */
|
|
97
|
+
export type CloseHandler = (win: VfWindow, keys: Partial<VfCloseKeys>) => void | Promise<void>;
|
|
81
98
|
/** What opening a window for an item declares. */
|
|
82
99
|
export interface OpenOptions extends Omit<AdoptOptions, 'pin' | 'palette'> {
|
|
83
100
|
/** Makes the window, not yet appended. */
|
|
@@ -105,13 +122,19 @@ export interface WindowManagerOptions {
|
|
|
105
122
|
/** A saved pin by item — the session the boot read. */
|
|
106
123
|
savedPin?: (item: string) => Pin | null;
|
|
107
124
|
}
|
|
125
|
+
/** Settles once the desktop and its menu bar have rendered, so `workArea` measures the bar. */
|
|
126
|
+
export declare function desktopRendered(desktop: VfDesktop): Promise<void>;
|
|
108
127
|
/** The innermost focused element, through open shadow roots. */
|
|
109
128
|
export declare function deepActiveElement(): Element | null;
|
|
110
129
|
/**
|
|
111
|
-
* Move keyboard focus
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
130
|
+
* Move keyboard focus into `win`'s content once that content has rendered —
|
|
131
|
+
* and then only if `when`, read at that point, still holds: to a descendant
|
|
132
|
+
* marked `autofocus`, else to the first thing that takes the focus. An
|
|
133
|
+
* element whose `tabindex` is negative is passed over: a roving cell parked
|
|
134
|
+
* off the tab order, a disabled control. A kit host with no `tabindex` goes
|
|
135
|
+
* through, since it hands the focus on inside itself. Resolves whether the
|
|
136
|
+
* focus moved: a window with nothing focusable in its content keeps the
|
|
137
|
+
* focus where it was.
|
|
115
138
|
*/
|
|
116
139
|
export declare function focusInto(win: Element, { when }?: {
|
|
117
140
|
when?: () => boolean;
|
|
@@ -143,16 +166,40 @@ export declare function createWindowManager(desktop: VfDesktop, options: WindowM
|
|
|
143
166
|
close(win: VfWindow, { to }?: {
|
|
144
167
|
to?: VfViewportBox | null;
|
|
145
168
|
}): Promise<boolean>;
|
|
146
|
-
/**
|
|
147
|
-
|
|
169
|
+
/**
|
|
170
|
+
* The close box's route: the window's own close, given the modifier keys
|
|
171
|
+
* held, or a plain close. Resolves whether the window closed, once its
|
|
172
|
+
* close handler has settled.
|
|
173
|
+
*/
|
|
174
|
+
requestClose(win: VfWindow, keys?: Partial<VfCloseKeys>): Promise<boolean>;
|
|
175
|
+
/**
|
|
176
|
+
* Ask each of an application's windows to close, front to back, palettes
|
|
177
|
+
* aside: a Quit. Resolves false at the first window still open once its
|
|
178
|
+
* handler has settled — a Cancel — and true when all have closed.
|
|
179
|
+
*/
|
|
180
|
+
closeAll(app: string, keys?: Partial<VfCloseKeys>): Promise<boolean>;
|
|
148
181
|
/** The window showing `item`, or null. */
|
|
149
182
|
windowFor(item: string): VfWindow | null;
|
|
150
183
|
/** Every window of an application, back to front. */
|
|
151
184
|
windowsOf(app: string): VfWindow[];
|
|
152
185
|
/** A window's item, or null. */
|
|
153
186
|
itemOf(win: Element | null): string | null;
|
|
154
|
-
/** A window's application, or null when the manager doesn't hold it. */
|
|
155
|
-
appOf(
|
|
187
|
+
/** A window's or a held dialog's application, or null when the manager doesn't hold it. */
|
|
188
|
+
appOf(el: Element | null): string | null;
|
|
189
|
+
/**
|
|
190
|
+
* Hold an application's dialog: a `vf-dialog`, or an element that renders
|
|
191
|
+
* one inside itself. It is appended to the desktop unless it is already
|
|
192
|
+
* in it, and its `open` is followed from then on.
|
|
193
|
+
*/
|
|
194
|
+
holdDialog(dialog: Element, { app }: {
|
|
195
|
+
app: string;
|
|
196
|
+
}): void;
|
|
197
|
+
/** Let a held dialog go. It stays where it is; the caller removes it. */
|
|
198
|
+
releaseDialog(dialog: Element): void;
|
|
199
|
+
/** An application's held dialogs. */
|
|
200
|
+
dialogsOf(app: string): Element[];
|
|
201
|
+
/** The application of the held dialog opened last and still open, or null. */
|
|
202
|
+
readonly asking: string | null;
|
|
156
203
|
/** Whether `win` is a palette. */
|
|
157
204
|
isPalette(win: Element | null): boolean;
|
|
158
205
|
/** Change the item a window shows: an untitled document's first save, say. */
|
|
@@ -163,8 +210,16 @@ export declare function createWindowManager(desktop: VfDesktop, options: WindowM
|
|
|
163
210
|
hasWindows(app: string): boolean;
|
|
164
211
|
/** Re-read every palette's predicate: an application hid or showed one. */
|
|
165
212
|
palettesChanged(): void;
|
|
166
|
-
/** Write a box for a gesture the owner runs itself —
|
|
213
|
+
/** Write a box for a gesture the owner runs itself — its group's arrange, a zoom of its own. */
|
|
167
214
|
write(win: VfWindow, box: Placed): void;
|
|
215
|
+
/**
|
|
216
|
+
* The zoom box, for a window adopted with a `zoom`: away from its zoomed
|
|
217
|
+
* box, the window goes there, its box remembered; at it, the window goes
|
|
218
|
+
* back to that box, carried by its pin through any raster resize since,
|
|
219
|
+
* or to its `place` with none remembered. The zoom box's press runs it;
|
|
220
|
+
* so can a menu command.
|
|
221
|
+
*/
|
|
222
|
+
zoom(win: VfWindow): void;
|
|
168
223
|
/** The box a placement would write, not written. */
|
|
169
224
|
clamped(win: VfWindow, box: BoxInput): Placed;
|
|
170
225
|
/** A window's `place` on the live area, clamped, or null. */
|
|
@@ -209,6 +264,8 @@ export declare function createWindowManager(desktop: VfDesktop, options: WindowM
|
|
|
209
264
|
beforeFront(fn: (win: HTMLElement | null) => void): () => void;
|
|
210
265
|
/** The front application changed. */
|
|
211
266
|
onFront(fn: (app: string) => void): () => void;
|
|
267
|
+
/** A held dialog opened or closed, so `asking` may have changed. */
|
|
268
|
+
onDialogs(fn: () => void): () => void;
|
|
212
269
|
dispose(): void;
|
|
213
270
|
};
|
|
214
271
|
export type WindowManager = ReturnType<typeof createWindowManager>;
|