vintage-frames 0.14.0 → 0.15.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/README.md +16 -0
- package/custom-elements.json +271 -27
- package/dist/components/vf-button.d.ts +5 -1
- package/dist/components/vf-button.js +1 -1
- package/dist/components/vf-container.d.ts +3 -1
- package/dist/components/vf-container.js +34 -16
- package/dist/components/vf-desktop.d.ts +114 -12
- package/dist/components/vf-desktop.js +119 -58
- package/dist/components/vf-dialog.d.ts +7 -1
- package/dist/components/vf-icon-field.d.ts +1 -1
- package/dist/components/vf-icon.js +68 -61
- package/dist/components/vf-menu-bar.d.ts +5 -0
- package/dist/components/vf-menu-bar.js +67 -43
- package/dist/components/vf-window.d.ts +13 -0
- package/dist/components/vf-window.js +10 -1
- package/dist/events.d.ts +3 -2
- package/dist/events.js +3 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +44 -43
- package/dist/modal-dialog.d.ts +31 -4
- package/dist/modal-dialog.js +98 -82
- package/dist/pattern-fill.d.ts +11 -8
- package/dist/pattern-fill.js +2 -4
- package/dist/position.d.ts +25 -7
- package/dist/position.js +31 -23
- package/dist/shell/alert.d.ts +40 -0
- package/dist/shell/alert.js +41 -0
- package/dist/shell/catalog.d.ts +168 -0
- package/dist/shell/catalog.js +278 -0
- package/dist/shell/clock.d.ts +16 -0
- package/dist/shell/clock.js +36 -0
- package/dist/shell/filing.d.ts +39 -0
- package/dist/shell/filing.js +68 -0
- package/dist/shell/finder.d.ts +156 -0
- package/dist/shell/finder.js +598 -0
- package/dist/shell/geometry.d.ts +196 -0
- package/dist/shell/geometry.js +241 -0
- package/dist/shell/index.d.ts +27 -0
- package/dist/shell/index.js +10 -0
- package/dist/shell/shell.d.ts +164 -0
- package/dist/shell/shell.js +234 -0
- package/dist/shell/state.d.ts +88 -0
- package/dist/shell/state.js +103 -0
- package/dist/shell/storage.d.ts +14 -0
- package/dist/shell/storage.js +41 -0
- package/dist/shell/windows.d.ts +234 -0
- package/dist/shell/windows.js +387 -0
- package/dist/text-control.d.ts +6 -0
- package/dist/text-control.js +2 -2
- package/dist/window-chrome.d.ts +43 -0
- package/dist/window-chrome.js +13 -0
- package/docs/SPEC.md +17 -16
- package/editor/vscode.html-custom-data.json +11 -6
- package/editor/web-types.json +55 -14
- package/package.json +10 -2
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The window manager. Applications make their windows; the manager places
|
|
3
|
+
* them, opens and closes them, follows the front application, carries them
|
|
4
|
+
* through a raster resize and arranges them.
|
|
5
|
+
*
|
|
6
|
+
* - `adopt()` takes a window an application made and appended: its
|
|
7
|
+
* application, the item it shows, how to place it, its resize policy, and
|
|
8
|
+
* what its close box means. `open()` raises the window for an item if one
|
|
9
|
+
* is open, and otherwise makes, appends, adopts and places one, and shows
|
|
10
|
+
* it out of `from`. `close()` hides a window into `to` — by default its
|
|
11
|
+
* home box, which the Finder provides: the item's icon, or the
|
|
12
|
+
* application's — then releases and removes it.
|
|
13
|
+
* - Placement, in order: this session's pin for the item, the saved pin,
|
|
14
|
+
* then the `place` policy — by default the first cascade slot no open
|
|
15
|
+
* window holds. The result is clamped into the desktop's window area.
|
|
16
|
+
* - The front application is the active window's, or the default one while
|
|
17
|
+
* none is active. `beforeFront` runs before a change, `onFront` reports it.
|
|
18
|
+
* - Palettes are an application's utility windows: shown while it is front
|
|
19
|
+
* and hidden otherwise, never closed or removed.
|
|
20
|
+
* - Opening a window moves keyboard focus into it; closing the active one
|
|
21
|
+
* moves focus to the window that becomes active, else to the closed
|
|
22
|
+
* window's icon, else to the desktop's icons. Focus left behind in a
|
|
23
|
+
* window that is no longer active — a title-bar press moves none — goes
|
|
24
|
+
* to the active window too.
|
|
25
|
+
* - `onWindows` reports a window opened or closed and a close's zoom rects
|
|
26
|
+
* landing, palettes aside; `isOpen()` and `hasWindows()` hold until they
|
|
27
|
+
* land, which is what the Finder's open ghost follows.
|
|
28
|
+
* - The resize rule re-pins every window with the nine-slice pin, re-reads a
|
|
29
|
+
* resizable window's pin when its size changed, floors it at the window's
|
|
30
|
+
* own size limits and caps it to the window area, so growing back restores
|
|
31
|
+
* every place.
|
|
32
|
+
* - Dialogs: `holdDialog()` holds an application's dialog under it, in the
|
|
33
|
+
* desktop. The manager follows each held dialog's `open` attribute, and
|
|
34
|
+
* `asking` is the application of the one opened last and still open.
|
|
35
|
+
*/
|
|
36
|
+
import { VfWindow } from '../index.js';
|
|
37
|
+
import type { VfDesktop, VfViewportBox } from '../index.js';
|
|
38
|
+
import type { Bands, Box, Pin, Policy, Size } from './geometry.js';
|
|
39
|
+
/** A box to write, any edge optional. */
|
|
40
|
+
export interface BoxInput {
|
|
41
|
+
left?: number;
|
|
42
|
+
top?: number;
|
|
43
|
+
width?: number;
|
|
44
|
+
height?: number;
|
|
45
|
+
}
|
|
46
|
+
/** A placement: a position, and a size where one is stated. */
|
|
47
|
+
export interface Placed extends BoxInput {
|
|
48
|
+
left: number;
|
|
49
|
+
top: number;
|
|
50
|
+
}
|
|
51
|
+
/** How a window is placed: its box on the desktop's live window area. */
|
|
52
|
+
export type PlacePolicy = (area: Box) => BoxInput & Point;
|
|
53
|
+
type Point = {
|
|
54
|
+
left: number;
|
|
55
|
+
top: number;
|
|
56
|
+
};
|
|
57
|
+
/** What adopting a window declares. */
|
|
58
|
+
export interface AdoptOptions {
|
|
59
|
+
/** The application shown while the window is active. */
|
|
60
|
+
app: string;
|
|
61
|
+
/** The item it shows: a catalog id, or an application's own key. */
|
|
62
|
+
item?: string | null;
|
|
63
|
+
/** Its placement on the live window area; adopt() and arrange() apply it. Unset, the owner places it. */
|
|
64
|
+
place?: PlacePolicy | null;
|
|
65
|
+
/** A saved pin to open at instead. */
|
|
66
|
+
pin?: Pin | null;
|
|
67
|
+
/** Its resize policy for the re-pin. Default: a resizable window floored at its size limits, else fixed. */
|
|
68
|
+
policy?: ((cur: Box) => Policy) | null;
|
|
69
|
+
/** A box held across a raster resize while the window is near it. */
|
|
70
|
+
keep?: ((area: Box) => Box) | null;
|
|
71
|
+
/**
|
|
72
|
+
* A palette: shown while its application is front — and while the
|
|
73
|
+
* predicate holds, for one the application hides for its own reasons —
|
|
74
|
+
* and hidden otherwise. Never closed by the manager.
|
|
75
|
+
*/
|
|
76
|
+
palette?: boolean | (() => boolean);
|
|
77
|
+
/**
|
|
78
|
+
* What the close box means. Unset, it closes the window. Set, the close box
|
|
79
|
+
* asks the application, which calls `close()` once it decides — after a
|
|
80
|
+
* Save prompt, say — or never.
|
|
81
|
+
*/
|
|
82
|
+
close?: ((win: VfWindow) => void) | null;
|
|
83
|
+
}
|
|
84
|
+
/** What opening a window for an item declares. */
|
|
85
|
+
export interface OpenOptions extends Omit<AdoptOptions, 'pin' | 'palette'> {
|
|
86
|
+
/** Makes the window, not yet appended. */
|
|
87
|
+
create: () => VfWindow;
|
|
88
|
+
/** The box it opens out of, in viewport CSS px: an icon's `cellRect()`. */
|
|
89
|
+
from?: VfViewportBox | null;
|
|
90
|
+
}
|
|
91
|
+
/** An application's arrangement group, for windows it places itself. */
|
|
92
|
+
export interface ArrangeGroup {
|
|
93
|
+
arrange(): void;
|
|
94
|
+
arranged(): boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The box a closing window shrinks into, in viewport CSS px: `item` is the
|
|
98
|
+
* window's item, or null, and `app` its application. The Finder answers with
|
|
99
|
+
* the item's icon, the nearest open folder around it, or the application's
|
|
100
|
+
* icon.
|
|
101
|
+
*/
|
|
102
|
+
export type HomeBox = (item: string | null, app: string) => VfViewportBox | null;
|
|
103
|
+
/** Move the focus to the closed window's icon (`item` null: the desktop's first); resolves whether it moved. */
|
|
104
|
+
export type FocusHome = (item: string | null, app: string | null) => boolean;
|
|
105
|
+
export interface WindowManagerOptions {
|
|
106
|
+
/** The application front while no window is active. */
|
|
107
|
+
defaultApp: string;
|
|
108
|
+
/** A saved pin by item — the session the boot read. */
|
|
109
|
+
savedPin?: (item: string) => Pin | null;
|
|
110
|
+
}
|
|
111
|
+
/** The innermost focused element, through open shadow roots. */
|
|
112
|
+
export declare function deepActiveElement(): Element | null;
|
|
113
|
+
/**
|
|
114
|
+
* Move keyboard focus to the first thing in `win`'s content that takes it,
|
|
115
|
+
* once that content has rendered — and then only if `when`, read at that
|
|
116
|
+
* point, still holds. Resolves whether it did: a window with nothing
|
|
117
|
+
* focusable in its content keeps the focus where it was.
|
|
118
|
+
*/
|
|
119
|
+
export declare function focusInto(win: Element, { when }?: {
|
|
120
|
+
when?: () => boolean;
|
|
121
|
+
}): Promise<boolean>;
|
|
122
|
+
/** The window manager over a desktop. */
|
|
123
|
+
export declare function createWindowManager(desktop: VfDesktop, options: WindowManagerOptions): {
|
|
124
|
+
/** The desktop's window area, system px. */
|
|
125
|
+
readonly area: Box;
|
|
126
|
+
/** The front application's id. */
|
|
127
|
+
readonly front: string;
|
|
128
|
+
/**
|
|
129
|
+
* Take a window an application made and appended (a slotted child of the
|
|
130
|
+
* desktop: the clamp reads its lattice). It opens at the saved `pin`, or
|
|
131
|
+
* at its `place`; without either the owner placed it.
|
|
132
|
+
*/
|
|
133
|
+
adopt(win: VfWindow, opts: AdoptOptions): void;
|
|
134
|
+
/**
|
|
135
|
+
* Open the window for `item`: raise it if one is open, else make it,
|
|
136
|
+
* append it, adopt it and place it — this session's pin, then the saved
|
|
137
|
+
* one, then `place`, by default the first free cascade slot — and show it
|
|
138
|
+
* out of `from`. Keyboard focus moves into it. Returns the window.
|
|
139
|
+
*/
|
|
140
|
+
open(opts: OpenOptions): VfWindow;
|
|
141
|
+
/**
|
|
142
|
+
* Close a window into `to` — by default its home box, the Finder's icon
|
|
143
|
+
* for it — then release and remove it. The item's box is remembered for
|
|
144
|
+
* the session. Resolves when the zoom rects have landed.
|
|
145
|
+
*/
|
|
146
|
+
close(win: VfWindow, { to }?: {
|
|
147
|
+
to?: VfViewportBox | null;
|
|
148
|
+
}): Promise<boolean>;
|
|
149
|
+
/** The close box's route: the window's own close, or a plain close. */
|
|
150
|
+
requestClose(win: VfWindow): void;
|
|
151
|
+
/** The window showing `item`, or null. */
|
|
152
|
+
windowFor(item: string): VfWindow | null;
|
|
153
|
+
/** Every window of an application, back to front. */
|
|
154
|
+
windowsOf(app: string): VfWindow[];
|
|
155
|
+
/** A window's item, or null. */
|
|
156
|
+
itemOf(win: Element | null): string | null;
|
|
157
|
+
/** A window's or a held dialog's application, or null when the manager doesn't hold it. */
|
|
158
|
+
appOf(el: Element | null): string | null;
|
|
159
|
+
/**
|
|
160
|
+
* Hold an application's dialog: a `vf-dialog`, or an element that renders
|
|
161
|
+
* one inside itself. It is appended to the desktop unless it is already
|
|
162
|
+
* in it, and its `open` is followed from then on.
|
|
163
|
+
*/
|
|
164
|
+
holdDialog(dialog: Element, { app }: {
|
|
165
|
+
app: string;
|
|
166
|
+
}): void;
|
|
167
|
+
/** Let a held dialog go. It stays where it is; the caller removes it. */
|
|
168
|
+
releaseDialog(dialog: Element): void;
|
|
169
|
+
/** An application's held dialogs. */
|
|
170
|
+
dialogsOf(app: string): Element[];
|
|
171
|
+
/** The application of the held dialog opened last and still open, or null. */
|
|
172
|
+
readonly asking: string | null;
|
|
173
|
+
/** Whether `win` is a palette. */
|
|
174
|
+
isPalette(win: Element | null): boolean;
|
|
175
|
+
/** Change the item a window shows: an untitled document's first save, say. */
|
|
176
|
+
setItem(win: VfWindow, item: string | null): void;
|
|
177
|
+
/** Whether a window shows `item`, or is still closing into its icon. */
|
|
178
|
+
isOpen(item: string): boolean;
|
|
179
|
+
/** Whether an application has a window open, palettes aside, or one still closing. */
|
|
180
|
+
hasWindows(app: string): boolean;
|
|
181
|
+
/** Re-read every palette's predicate: an application hid or showed one. */
|
|
182
|
+
palettesChanged(): void;
|
|
183
|
+
/** Write a box for a gesture the owner runs itself — a zoom, its group's arrange. */
|
|
184
|
+
write(win: VfWindow, box: Placed): void;
|
|
185
|
+
/** The box a placement would write, not written. */
|
|
186
|
+
clamped(win: VfWindow, box: BoxInput): Placed;
|
|
187
|
+
/** A window's `place` on the live area, clamped, or null. */
|
|
188
|
+
placed(win: VfWindow): Placed | null;
|
|
189
|
+
/** A window's pin on the current raster, for an owner that saves it. */
|
|
190
|
+
pinOf(win: VfWindow): Pin;
|
|
191
|
+
/** A pin on the live raster, clamped, under the window's policy or `policy`. */
|
|
192
|
+
fromPin(win: VfWindow, pin: Pin, policy?: Policy): Placed;
|
|
193
|
+
/** Widen the frame's bands, for an application whose windows dock along the edges. */
|
|
194
|
+
setFrameBands(next: Partial<Bands>): void;
|
|
195
|
+
/** Register an application's arrangement group. Returns the unregister. */
|
|
196
|
+
arrangeWith(app: string, group: ArrangeGroup): () => void;
|
|
197
|
+
/** View → Arrange Windows: every group's arrange, then every `place`. Nothing is raised. */
|
|
198
|
+
arrange(): void;
|
|
199
|
+
/** Whether every group is arranged and every shown window sits at its `place`. */
|
|
200
|
+
arranged(): boolean;
|
|
201
|
+
/**
|
|
202
|
+
* Carry every window from the `before` raster to the live one with the
|
|
203
|
+
* nine-slice pin, then tell the onRaster listeners. Nothing clamps a
|
|
204
|
+
* window's position, so growing back restores it.
|
|
205
|
+
*/
|
|
206
|
+
resized(before: Size): void;
|
|
207
|
+
/** The windows' saved boxes: each open one with its depth, the palettes as boxes alone. */
|
|
208
|
+
snapshot(): Record<string, {
|
|
209
|
+
app: string;
|
|
210
|
+
pin: Pin;
|
|
211
|
+
z?: number;
|
|
212
|
+
}>;
|
|
213
|
+
/** This session's pin for an item whose window closed, or null. */
|
|
214
|
+
rememberedPin(item: string): Pin | null;
|
|
215
|
+
/** Notify layout of a change the owner made itself. */
|
|
216
|
+
layoutChanged(): void;
|
|
217
|
+
/** Set where a window closes into and where focus goes after it: the Finder's. */
|
|
218
|
+
setHome(box: HomeBox, focus: FocusHome): () => void;
|
|
219
|
+
/** The layout signal: any geometry write, drag, grow or arrange. Returns the unsubscribe. */
|
|
220
|
+
onLayout(fn: () => void): () => void;
|
|
221
|
+
/** A window opened or closed, or finished closing, palettes aside; a window's item changed. */
|
|
222
|
+
onWindows(fn: () => void): () => void;
|
|
223
|
+
/** A raster resize, after every window has re-pinned. */
|
|
224
|
+
onRaster(fn: (before: Size, after: Size) => void): () => void;
|
|
225
|
+
/** An activation, before the front application changes: `fn(win)`, the new active window or null. */
|
|
226
|
+
beforeFront(fn: (win: HTMLElement | null) => void): () => void;
|
|
227
|
+
/** The front application changed. */
|
|
228
|
+
onFront(fn: (app: string) => void): () => void;
|
|
229
|
+
/** A held dialog opened or closed, so `asking` may have changed. */
|
|
230
|
+
onDialogs(fn: () => void): () => void;
|
|
231
|
+
dispose(): void;
|
|
232
|
+
};
|
|
233
|
+
export type WindowManager = ReturnType<typeof createWindowManager>;
|
|
234
|
+
export {};
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { snapSys as e, systemPxQuantum as t } from "../scale.js";
|
|
2
|
+
import { VfWindow as n } from "../components/vf-window.js";
|
|
3
|
+
import "../index.js";
|
|
4
|
+
import { cascadeFrom as r, cascadedBox as i, frameOf as a, nearBox as o, pinOf as s, pinTo as c, windowOrigin as l } from "./geometry.js";
|
|
5
|
+
//#region src/shell/windows.ts
|
|
6
|
+
var u = (e, t) => e.set(t, (e.get(t) ?? 0) + 1), d = (e, t) => {
|
|
7
|
+
let n = e.get(t) ?? 1;
|
|
8
|
+
n > 1 ? e.set(t, n - 1) : e.delete(t);
|
|
9
|
+
}, f = (e, t, n) => Math.min(Math.max(e, t), n), p = (e) => ({
|
|
10
|
+
left: e.left ?? 0,
|
|
11
|
+
top: e.top ?? 0,
|
|
12
|
+
width: e.width ?? 0,
|
|
13
|
+
height: e.height ?? 0
|
|
14
|
+
});
|
|
15
|
+
function m() {
|
|
16
|
+
let e = document.activeElement;
|
|
17
|
+
for (; e?.shadowRoot?.activeElement;) e = e.shadowRoot.activeElement;
|
|
18
|
+
return e;
|
|
19
|
+
}
|
|
20
|
+
var h = (e) => {
|
|
21
|
+
let t = document.activeElement;
|
|
22
|
+
return !!t && (t === e || e.contains(t));
|
|
23
|
+
};
|
|
24
|
+
async function g(e, { when: t } = {}) {
|
|
25
|
+
let n = [...e.querySelectorAll("*")];
|
|
26
|
+
if (await Promise.all([e, ...n].map((e) => e.updateComplete)), !e.isConnected || t && !t()) return !1;
|
|
27
|
+
for (let t of n) if (!(t.closest("[hidden]") || !t.checkVisibility()) && !(t.tabIndex < 0 && !t.localName.startsWith("vf-")) && (t.focus({ preventScroll: !0 }), h(e))) return !0;
|
|
28
|
+
return !1;
|
|
29
|
+
}
|
|
30
|
+
function ee(m, ee) {
|
|
31
|
+
let { defaultApp: te, savedPin: ne = () => null } = ee, _ = [], v = (e, t, n) => {
|
|
32
|
+
e.addEventListener(t, n), _.push(() => e.removeEventListener(t, n));
|
|
33
|
+
}, y = () => ({
|
|
34
|
+
width: m.width,
|
|
35
|
+
height: m.height
|
|
36
|
+
}), b = () => m.windowArea, x = /* @__PURE__ */ new Map(), S = /* @__PURE__ */ new Map(), C = /* @__PURE__ */ new Map(), w = /* @__PURE__ */ new Map(), T = /* @__PURE__ */ new Map(), E = /* @__PURE__ */ new WeakMap(), D = {}, O = () => a(b().top, D), k = /* @__PURE__ */ new Set(), A = !1, j = () => {
|
|
37
|
+
if (!A) for (let e of [...k]) e();
|
|
38
|
+
}, M = /* @__PURE__ */ new Set(), N = () => {
|
|
39
|
+
for (let e of [...M]) e();
|
|
40
|
+
}, P = /* @__PURE__ */ new Set(), F = /* @__PURE__ */ new Set(), I = /* @__PURE__ */ new Set(), L = /* @__PURE__ */ new Set(), R = /* @__PURE__ */ new Map(), z = [], re = (e) => {
|
|
41
|
+
for (let t = e; t; t = t.parentElement) if (R.has(t)) return t;
|
|
42
|
+
return null;
|
|
43
|
+
}, B = (e) => e.isConnected && (e.matches("vf-dialog[open]") || !!e.querySelector("vf-dialog[open]")), V = () => {
|
|
44
|
+
let e = z.filter((e) => R.has(e) && B(e));
|
|
45
|
+
for (let t of R.keys()) !e.includes(t) && B(t) && e.push(t);
|
|
46
|
+
if (!(e.length === z.length && e.every((e, t) => e === z[t]))) {
|
|
47
|
+
z = e;
|
|
48
|
+
for (let e of [...L]) e();
|
|
49
|
+
}
|
|
50
|
+
}, H = () => null, U = () => !1;
|
|
51
|
+
function W(n, r) {
|
|
52
|
+
let i = b(), a = t(n), o = (e) => Math.floor(e / a) * a, s = Math.ceil(i.top / a) * a, c = i.left + i.width, l = i.top + i.height, { width: u, height: d } = r;
|
|
53
|
+
n.resizable && (u != null && Number.isFinite(u) && (u = Math.min(u, o(i.width))), d != null && Number.isFinite(d) && (d = Math.min(d, o(Math.max(0, l - s)))));
|
|
54
|
+
let p = u ?? n.width ?? 0, m = d ?? n.height ?? 0;
|
|
55
|
+
return {
|
|
56
|
+
left: f(e(r.left ?? i.left, n), i.left, Math.max(i.left, o(c - p))),
|
|
57
|
+
top: f(e(r.top ?? s, n), s, Math.max(s, o(l - m))),
|
|
58
|
+
width: u,
|
|
59
|
+
height: d
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
let G = (t, n) => {
|
|
63
|
+
t.left = e(n.left, t), t.top = e(n.top, t), n.width != null && Number.isFinite(n.width) && (t.width = n.width), n.height != null && Number.isFinite(n.height) && (t.height = n.height), E.delete(t);
|
|
64
|
+
}, K = (e) => {
|
|
65
|
+
let t = x.get(e)?.place;
|
|
66
|
+
return t ? W(e, t(b())) : null;
|
|
67
|
+
}, q = (e, t) => {
|
|
68
|
+
let n = x.get(e)?.policy;
|
|
69
|
+
if (n) return n(t);
|
|
70
|
+
if (!e.resizable) return { size: {
|
|
71
|
+
width: t.width,
|
|
72
|
+
height: t.height
|
|
73
|
+
} };
|
|
74
|
+
let { minWidth: r, minHeight: i } = e.sizeLimits;
|
|
75
|
+
return { min: {
|
|
76
|
+
width: r,
|
|
77
|
+
height: i
|
|
78
|
+
} };
|
|
79
|
+
}, J = (e, t, n = q(e, p(e))) => W(e, c(t, y(), O(), n)), ie = (e, t) => {
|
|
80
|
+
let n = p(e);
|
|
81
|
+
return [
|
|
82
|
+
"left",
|
|
83
|
+
"top",
|
|
84
|
+
"width",
|
|
85
|
+
"height"
|
|
86
|
+
].every((e) => {
|
|
87
|
+
let r = t[e];
|
|
88
|
+
return r == null || !Number.isFinite(r) || n[e] === r;
|
|
89
|
+
});
|
|
90
|
+
}, ae = (e) => {
|
|
91
|
+
let t = [...x].filter(([t, n]) => t !== e && !t.hidden && !n.palette).map(([e]) => p(e));
|
|
92
|
+
return r(l(b()), t).slot;
|
|
93
|
+
}, Y = te, X = (e) => e instanceof n && x.get(e)?.app || te;
|
|
94
|
+
function oe() {
|
|
95
|
+
let e = !1;
|
|
96
|
+
for (let [t, n] of x) {
|
|
97
|
+
if (!n.palette) continue;
|
|
98
|
+
let r = n.app === Y && n.palette();
|
|
99
|
+
t.hidden !== !r && (t.hidden = !r, r && m.bringToFront(t), e = !0);
|
|
100
|
+
}
|
|
101
|
+
e && j();
|
|
102
|
+
}
|
|
103
|
+
let se = (e) => {
|
|
104
|
+
let t = document.activeElement?.closest("vf-window");
|
|
105
|
+
!(t instanceof n) || t === e || !x.has(t) || x.get(t).palette || (e ? Z(e) : U(null, null));
|
|
106
|
+
}, Z = (e) => void g(e, { when: () => m.activeWindow === e }), ce = (e) => {
|
|
107
|
+
se(e);
|
|
108
|
+
for (let t of [...F]) t(e);
|
|
109
|
+
let t = X(e);
|
|
110
|
+
if (t !== Y) {
|
|
111
|
+
Y = t, oe();
|
|
112
|
+
for (let e of [...I]) e(Y);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
v(m, "vf-activate", (e) => ce(e.detail.window)), v(m, "vf-close", (e) => {
|
|
116
|
+
let t = e.target;
|
|
117
|
+
t instanceof n && x.has(t) && $.requestClose(t);
|
|
118
|
+
});
|
|
119
|
+
let Q = 0, le = (e) => {
|
|
120
|
+
!(e.target instanceof n) || !x.has(e.target) || Q || (Q = window.setTimeout(() => {
|
|
121
|
+
Q = 0, j();
|
|
122
|
+
}, 0));
|
|
123
|
+
};
|
|
124
|
+
m.addEventListener("vf-placement-change", le), _.push(() => m.removeEventListener("vf-placement-change", le)), v(m, "vf-resize", (e) => {
|
|
125
|
+
e.detail?.commit && j();
|
|
126
|
+
});
|
|
127
|
+
let ue = (e) => {
|
|
128
|
+
let t = x.get(e);
|
|
129
|
+
t && (x.delete(e), E.delete(e), t.palette || N(), j());
|
|
130
|
+
}, de = (n, r, i, a) => {
|
|
131
|
+
let l = p(n), u = x.get(n);
|
|
132
|
+
if (u?.keep && o(l, u.keep(a))) {
|
|
133
|
+
G(n, W(n, u.keep(b())));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
let d = E.get(n), f = !!d && n.resizable && (d.width !== l.width || d.height !== l.height), m = !d || d.left !== l.left || d.top !== l.top || f || !d ? s(l, r, O()) : d.pin, h = c(m, i, O(), q(n, l));
|
|
137
|
+
if (n.left = e(h.left, n), n.top = e(h.top, n), n.resizable) {
|
|
138
|
+
let r = t(n), i = b(), a = Math.ceil(i.top / r) * r, o = Math.floor(i.width / r) * r, s = Math.floor(Math.max(0, i.top + i.height - a) / r) * r;
|
|
139
|
+
n.width = Math.min(e(h.width, n), o), n.height = Math.min(e(h.height, n), s);
|
|
140
|
+
}
|
|
141
|
+
E.set(n, {
|
|
142
|
+
pin: m,
|
|
143
|
+
...p(n)
|
|
144
|
+
});
|
|
145
|
+
}, $ = {
|
|
146
|
+
get area() {
|
|
147
|
+
return b();
|
|
148
|
+
},
|
|
149
|
+
get front() {
|
|
150
|
+
return Y;
|
|
151
|
+
},
|
|
152
|
+
adopt(e, t) {
|
|
153
|
+
let n = t.palette;
|
|
154
|
+
if (x.set(e, {
|
|
155
|
+
app: t.app,
|
|
156
|
+
item: t.item ?? null,
|
|
157
|
+
place: t.place ?? null,
|
|
158
|
+
policy: t.policy ?? null,
|
|
159
|
+
keep: t.keep ?? null,
|
|
160
|
+
palette: n ? n === !0 ? () => !0 : n : null,
|
|
161
|
+
close: t.close ?? null
|
|
162
|
+
}), t.pin) G(e, J(e, t.pin));
|
|
163
|
+
else {
|
|
164
|
+
let t = K(e);
|
|
165
|
+
t && G(e, t);
|
|
166
|
+
}
|
|
167
|
+
if (n) {
|
|
168
|
+
let t = x.get(e);
|
|
169
|
+
e.hidden = !(t.app === Y && t.palette());
|
|
170
|
+
}
|
|
171
|
+
m.activeWindow === e && ce(e), n || N(), j();
|
|
172
|
+
},
|
|
173
|
+
open(e) {
|
|
174
|
+
let t = e.item == null ? null : $.windowFor(e.item);
|
|
175
|
+
if (t) return m.bringToFront(t), Z(t), t;
|
|
176
|
+
let n = e.create(), r = {
|
|
177
|
+
width: n.width ?? 0,
|
|
178
|
+
height: n.height ?? 0
|
|
179
|
+
}, a = ae();
|
|
180
|
+
m.append(n);
|
|
181
|
+
let o = e.item ?? null;
|
|
182
|
+
return $.adopt(n, {
|
|
183
|
+
app: e.app,
|
|
184
|
+
item: o,
|
|
185
|
+
place: e.place ?? ((e) => i(e, r, l(e), a)),
|
|
186
|
+
pin: o == null ? null : C.get(o)?.pin ?? ne(o),
|
|
187
|
+
policy: e.policy ?? null,
|
|
188
|
+
keep: e.keep ?? null,
|
|
189
|
+
close: e.close ?? null
|
|
190
|
+
}), o != null && C.delete(o), m.bringToFront(n), e.from && n.show({ from: e.from }), Z(n), n;
|
|
191
|
+
},
|
|
192
|
+
close(e, { to: t } = {}) {
|
|
193
|
+
let n = x.get(e), r = n?.item ?? null, i = n?.app ?? null, a = t === void 0 ? i == null ? null : H(r, i) : t, o = m.activeWindow === e || h(e);
|
|
194
|
+
r != null && i != null && C.set(r, {
|
|
195
|
+
app: i,
|
|
196
|
+
pin: s(p(e), y(), O())
|
|
197
|
+
});
|
|
198
|
+
let c = e.hide({ to: a });
|
|
199
|
+
r != null && u(w, r), i != null && !n?.palette && u(T, i);
|
|
200
|
+
let l = () => {
|
|
201
|
+
r != null && d(w, r), i != null && !n?.palette && d(T, i), N();
|
|
202
|
+
};
|
|
203
|
+
return c.then(l, l), ue(e), e.remove(), o && queueMicrotask(() => {
|
|
204
|
+
let e = m.activeWindow;
|
|
205
|
+
e ? Z(e) : U(r, i) || U(null, null);
|
|
206
|
+
}), c;
|
|
207
|
+
},
|
|
208
|
+
requestClose(e) {
|
|
209
|
+
let t = x.get(e)?.close;
|
|
210
|
+
t ? t(e) : $.close(e);
|
|
211
|
+
},
|
|
212
|
+
windowFor(e) {
|
|
213
|
+
for (let [t, n] of x) if (n.item === e) return t;
|
|
214
|
+
return null;
|
|
215
|
+
},
|
|
216
|
+
windowsOf(e) {
|
|
217
|
+
return m.stackingOrder.filter((t) => t instanceof n && x.get(t)?.app === e);
|
|
218
|
+
},
|
|
219
|
+
itemOf(e) {
|
|
220
|
+
return e instanceof n ? x.get(e)?.item ?? null : null;
|
|
221
|
+
},
|
|
222
|
+
appOf(e) {
|
|
223
|
+
if (e instanceof n) return x.get(e)?.app ?? null;
|
|
224
|
+
let t = re(e);
|
|
225
|
+
return t ? R.get(t).app : null;
|
|
226
|
+
},
|
|
227
|
+
holdDialog(e, { app: t }) {
|
|
228
|
+
let n = R.get(e);
|
|
229
|
+
if (n) n.app = t;
|
|
230
|
+
else {
|
|
231
|
+
let n = new MutationObserver(V);
|
|
232
|
+
n.observe(e, {
|
|
233
|
+
attributes: !0,
|
|
234
|
+
attributeFilter: ["open"],
|
|
235
|
+
subtree: !0
|
|
236
|
+
}), R.set(e, {
|
|
237
|
+
app: t,
|
|
238
|
+
observer: n
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
m.contains(e) || m.append(e), V();
|
|
242
|
+
},
|
|
243
|
+
releaseDialog(e) {
|
|
244
|
+
let t = R.get(e);
|
|
245
|
+
t && (t.observer.disconnect(), R.delete(e), V());
|
|
246
|
+
},
|
|
247
|
+
dialogsOf(e) {
|
|
248
|
+
return [...R].filter(([, t]) => t.app === e).map(([e]) => e);
|
|
249
|
+
},
|
|
250
|
+
get asking() {
|
|
251
|
+
let e = z[z.length - 1];
|
|
252
|
+
return e ? R.get(e).app : null;
|
|
253
|
+
},
|
|
254
|
+
isPalette(e) {
|
|
255
|
+
return e instanceof n && !!x.get(e)?.palette;
|
|
256
|
+
},
|
|
257
|
+
setItem(e, t) {
|
|
258
|
+
let n = x.get(e);
|
|
259
|
+
!n || n.item === t || (n.item = t, N());
|
|
260
|
+
},
|
|
261
|
+
isOpen(e) {
|
|
262
|
+
return w.has(e) || $.windowFor(e) !== null;
|
|
263
|
+
},
|
|
264
|
+
hasWindows(e) {
|
|
265
|
+
if (T.has(e)) return !0;
|
|
266
|
+
for (let [, t] of x) if (t.app === e && !t.palette) return !0;
|
|
267
|
+
return !1;
|
|
268
|
+
},
|
|
269
|
+
palettesChanged() {
|
|
270
|
+
oe();
|
|
271
|
+
},
|
|
272
|
+
write(e, t) {
|
|
273
|
+
G(e, t), j();
|
|
274
|
+
},
|
|
275
|
+
clamped(e, t) {
|
|
276
|
+
return W(e, t);
|
|
277
|
+
},
|
|
278
|
+
placed(e) {
|
|
279
|
+
return K(e);
|
|
280
|
+
},
|
|
281
|
+
pinOf(e) {
|
|
282
|
+
return s(p(e), y(), O());
|
|
283
|
+
},
|
|
284
|
+
fromPin(e, t, n) {
|
|
285
|
+
return J(e, t, n);
|
|
286
|
+
},
|
|
287
|
+
setFrameBands(e) {
|
|
288
|
+
D = { ...e }, E = /* @__PURE__ */ new WeakMap();
|
|
289
|
+
},
|
|
290
|
+
arrangeWith(e, t) {
|
|
291
|
+
return S.set(e, t), j(), () => {
|
|
292
|
+
S.get(e) === t && S.delete(e), j();
|
|
293
|
+
};
|
|
294
|
+
},
|
|
295
|
+
arrange() {
|
|
296
|
+
A = !0;
|
|
297
|
+
try {
|
|
298
|
+
for (let e of S.values()) e.arrange();
|
|
299
|
+
for (let e of x.keys()) {
|
|
300
|
+
let t = K(e);
|
|
301
|
+
t && G(e, t);
|
|
302
|
+
}
|
|
303
|
+
} finally {
|
|
304
|
+
A = !1;
|
|
305
|
+
}
|
|
306
|
+
j();
|
|
307
|
+
},
|
|
308
|
+
arranged() {
|
|
309
|
+
for (let e of S.values()) if (!e.arranged()) return !1;
|
|
310
|
+
for (let e of x.keys()) {
|
|
311
|
+
let t = K(e);
|
|
312
|
+
if (t && !e.hidden && !ie(e, t)) return !1;
|
|
313
|
+
}
|
|
314
|
+
return !0;
|
|
315
|
+
},
|
|
316
|
+
resized(e) {
|
|
317
|
+
let t = y();
|
|
318
|
+
if (e.width === t.width && e.height === t.height) return;
|
|
319
|
+
let n = b(), r = {
|
|
320
|
+
left: n.left,
|
|
321
|
+
top: n.top,
|
|
322
|
+
width: e.width - n.left,
|
|
323
|
+
height: Math.max(0, e.height - n.top)
|
|
324
|
+
};
|
|
325
|
+
for (let n of x.keys()) de(n, e, t, r);
|
|
326
|
+
for (let n of [...P]) n(e, t);
|
|
327
|
+
j();
|
|
328
|
+
},
|
|
329
|
+
snapshot() {
|
|
330
|
+
let e = {};
|
|
331
|
+
for (let [t, n] of C) e[t] = { ...n };
|
|
332
|
+
let t = m.stackingOrder;
|
|
333
|
+
for (let [n, r] of x) {
|
|
334
|
+
if (r.item == null) continue;
|
|
335
|
+
let i = s(p(n), y(), O());
|
|
336
|
+
e[r.item] = r.palette ? {
|
|
337
|
+
app: r.app,
|
|
338
|
+
pin: i
|
|
339
|
+
} : {
|
|
340
|
+
app: r.app,
|
|
341
|
+
pin: i,
|
|
342
|
+
z: t.indexOf(n)
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
return e;
|
|
346
|
+
},
|
|
347
|
+
rememberedPin(e) {
|
|
348
|
+
return C.get(e)?.pin ?? null;
|
|
349
|
+
},
|
|
350
|
+
layoutChanged() {
|
|
351
|
+
j();
|
|
352
|
+
},
|
|
353
|
+
setHome(e, t) {
|
|
354
|
+
return H = e, U = t, () => {
|
|
355
|
+
H = () => null, U = () => !1;
|
|
356
|
+
};
|
|
357
|
+
},
|
|
358
|
+
onLayout(e) {
|
|
359
|
+
return k.add(e), () => void k.delete(e);
|
|
360
|
+
},
|
|
361
|
+
onWindows(e) {
|
|
362
|
+
return M.add(e), () => void M.delete(e);
|
|
363
|
+
},
|
|
364
|
+
onRaster(e) {
|
|
365
|
+
return P.add(e), () => void P.delete(e);
|
|
366
|
+
},
|
|
367
|
+
beforeFront(e) {
|
|
368
|
+
return F.add(e), () => void F.delete(e);
|
|
369
|
+
},
|
|
370
|
+
onFront(e) {
|
|
371
|
+
return I.add(e), () => void I.delete(e);
|
|
372
|
+
},
|
|
373
|
+
onDialogs(e) {
|
|
374
|
+
return L.add(e), () => void L.delete(e);
|
|
375
|
+
},
|
|
376
|
+
dispose() {
|
|
377
|
+
clearTimeout(Q);
|
|
378
|
+
for (let e of _) e();
|
|
379
|
+
k.clear(), M.clear(), P.clear(), F.clear(), I.clear(), L.clear();
|
|
380
|
+
for (let { observer: e } of R.values()) e.disconnect();
|
|
381
|
+
R.clear(), z = [], S.clear(), x.clear();
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
return Y = X(m.activeWindow), $;
|
|
385
|
+
}
|
|
386
|
+
//#endregion
|
|
387
|
+
export { ee as createWindowManager, m as deepActiveElement, g as focusInto };
|
package/dist/text-control.d.ts
CHANGED
|
@@ -117,6 +117,12 @@ export declare class VfTextControlBase extends VfShadowRoleControl {
|
|
|
117
117
|
* disabled default button means no submission at all, as in HTML; a form
|
|
118
118
|
* with no submit button falls back to the bare call, also as in HTML.
|
|
119
119
|
*
|
|
120
|
+
* One departure: a submit `vf-button variant="default"`, the button with
|
|
121
|
+
* the ring, is the default button when the form has one, ahead of the
|
|
122
|
+
* first in tree order. A Cancel/OK row puts Cancel first, and Return
|
|
123
|
+
* should press the button the ring marks — the one a modal's own Return
|
|
124
|
+
* rule presses (`VfModalDialog`).
|
|
125
|
+
*
|
|
120
126
|
* Returns whether a form owner took the press — true even when a disabled
|
|
121
127
|
* default button meant no submission, since HTML gave the press to the form
|
|
122
128
|
* either way. The callers cancel the keydown on true, so a modal shell
|
package/dist/text-control.js
CHANGED
|
@@ -57,8 +57,8 @@ var l = class extends a {
|
|
|
57
57
|
requestImplicitSubmit() {
|
|
58
58
|
let e = this.internals.form;
|
|
59
59
|
if (!e) return !1;
|
|
60
|
-
let t = [...e.elements].
|
|
61
|
-
return
|
|
60
|
+
let t = [...e.elements].filter((e) => e instanceof HTMLButtonElement && e.type === "submit" || e instanceof HTMLInputElement && (e.type === "submit" || e.type === "image") || e.localName === "vf-button" && !e.href && e.type?.toLowerCase() === "submit"), n = t.find((e) => e.localName === "vf-button" && e.variant === "default") ?? t[0];
|
|
61
|
+
return n ? (n.matches(":disabled") || n.click(), !0) : (e.requestSubmit(), !0);
|
|
62
62
|
}
|
|
63
63
|
emitValue(t, n = { value: this.value }) {
|
|
64
64
|
e(this, t, n);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A window's chrome as numbers: the insets between its outer box and the
|
|
3
|
+
* region its content lays out in, and the floor its grow box stops at.
|
|
4
|
+
*
|
|
5
|
+
* A page sizing a window to its content, or content to its window, needs the
|
|
6
|
+
* chrome between the two — a folder window's field sized to the scrolled
|
|
7
|
+
* plane, a palette sized to hold its cells exactly. Restating `1 + 18 + 20 +
|
|
8
|
+
* 15 + 1` in the page was the alternative, with a note to re-derive it if
|
|
9
|
+
* the kit's chrome ever changed.
|
|
10
|
+
*/
|
|
11
|
+
/** The grow box's default floors, in system px: a window smaller than this can't be worked. */
|
|
12
|
+
export declare const WINDOW_MIN_WIDTH = 80;
|
|
13
|
+
export declare const WINDOW_MIN_HEIGHT = 54;
|
|
14
|
+
/** The declared options that decide a window's chrome — `vf-window`'s own. */
|
|
15
|
+
export interface VfWindowChromeOptions {
|
|
16
|
+
/** `'utility'` for the windoid's 12px bar; anything else is the 18px one. */
|
|
17
|
+
variant?: 'utility' | null;
|
|
18
|
+
/** The edge scroll rails, as on `vf-window`. */
|
|
19
|
+
scrollbars?: 'vertical' | 'horizontal' | 'both' | null;
|
|
20
|
+
/** The header's height, rule included, while the `header` slot is populated. */
|
|
21
|
+
headerHeight?: number | null;
|
|
22
|
+
/** Whether the `status` slot is populated. */
|
|
23
|
+
status?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Four insets in system px. */
|
|
26
|
+
export interface VfInsets {
|
|
27
|
+
top: number;
|
|
28
|
+
right: number;
|
|
29
|
+
bottom: number;
|
|
30
|
+
left: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The insets, in system px, between a window's outer box (its `width` and
|
|
34
|
+
* `height`) and the region its content lays out in: the body, or under
|
|
35
|
+
* `scrollbars` the scrolled plane's viewport. They go both ways — the content
|
|
36
|
+
* of a window `W` wide is `W − left − right`, and a window that holds content
|
|
37
|
+
* `w` wide is `w + left + right`. The frame's 1px stroke on every side, the
|
|
38
|
+
* title bar (18, or 12 for a utility window) and the header above, the status
|
|
39
|
+
* strip (15) and a horizontal rail (15) below, a vertical rail (15) on the
|
|
40
|
+
* right. The numbers are the default tokens': a page that restyles
|
|
41
|
+
* `--vf-titlebar-height` or `--vf-status-bar-height` restates them.
|
|
42
|
+
*/
|
|
43
|
+
export declare function windowChrome({ variant, scrollbars, headerHeight, status, }?: VfWindowChromeOptions): VfInsets;
|