@liberation-data/desk 0.1.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/LICENSE +201 -0
- package/NOTICE +4 -0
- package/README.md +436 -0
- package/dist/core/commands.d.ts +40 -0
- package/dist/core/commands.d.ts.map +1 -0
- package/dist/core/commands.js +92 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/desk.d.ts +46 -0
- package/dist/core/desk.d.ts.map +1 -0
- package/dist/core/desk.js +178 -0
- package/dist/core/desk.js.map +1 -0
- package/dist/core/events.d.ts +42 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +54 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +7 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/location.d.ts +30 -0
- package/dist/core/location.d.ts.map +1 -0
- package/dist/core/location.js +99 -0
- package/dist/core/location.js.map +1 -0
- package/dist/core/shortcuts.d.ts +36 -0
- package/dist/core/shortcuts.d.ts.map +1 -0
- package/dist/core/shortcuts.js +73 -0
- package/dist/core/shortcuts.js.map +1 -0
- package/dist/core/titles.d.ts +3 -0
- package/dist/core/titles.d.ts.map +1 -0
- package/dist/core/titles.js +26 -0
- package/dist/core/titles.js.map +1 -0
- package/dist/core/types.d.ts +51 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/desk.css +2550 -0
- package/dist/react/Desktop.d.ts +24 -0
- package/dist/react/Desktop.d.ts.map +1 -0
- package/dist/react/Desktop.js +212 -0
- package/dist/react/Desktop.js.map +1 -0
- package/dist/react/Dock.d.ts +42 -0
- package/dist/react/Dock.d.ts.map +1 -0
- package/dist/react/Dock.js +103 -0
- package/dist/react/Dock.js.map +1 -0
- package/dist/react/MenuBar.d.ts +64 -0
- package/dist/react/MenuBar.d.ts.map +1 -0
- package/dist/react/MenuBar.js +245 -0
- package/dist/react/MenuBar.js.map +1 -0
- package/dist/react/appFrame.d.ts +37 -0
- package/dist/react/appFrame.d.ts.map +1 -0
- package/dist/react/appFrame.js +119 -0
- package/dist/react/appFrame.js.map +1 -0
- package/dist/react/arrange.d.ts +24 -0
- package/dist/react/arrange.d.ts.map +1 -0
- package/dist/react/arrange.js +61 -0
- package/dist/react/arrange.js.map +1 -0
- package/dist/react/commands.d.ts +23 -0
- package/dist/react/commands.d.ts.map +1 -0
- package/dist/react/commands.js +41 -0
- package/dist/react/commands.js.map +1 -0
- package/dist/react/context.d.ts +25 -0
- package/dist/react/context.d.ts.map +1 -0
- package/dist/react/context.js +28 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/controls.d.ts +111 -0
- package/dist/react/controls.d.ts.map +1 -0
- package/dist/react/controls.js +83 -0
- package/dist/react/controls.js.map +1 -0
- package/dist/react/conversation.d.ts +41 -0
- package/dist/react/conversation.d.ts.map +1 -0
- package/dist/react/conversation.js +73 -0
- package/dist/react/conversation.js.map +1 -0
- package/dist/react/dnd.d.ts +51 -0
- package/dist/react/dnd.d.ts.map +1 -0
- package/dist/react/dnd.js +138 -0
- package/dist/react/dnd.js.map +1 -0
- package/dist/react/dragContext.d.ts +32 -0
- package/dist/react/dragContext.d.ts.map +1 -0
- package/dist/react/dragContext.js +14 -0
- package/dist/react/dragContext.js.map +1 -0
- package/dist/react/events.d.ts +41 -0
- package/dist/react/events.d.ts.map +1 -0
- package/dist/react/events.js +91 -0
- package/dist/react/events.js.map +1 -0
- package/dist/react/index.d.ts +43 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +22 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/layers.d.ts +5 -0
- package/dist/react/layers.d.ts.map +1 -0
- package/dist/react/layers.js +65 -0
- package/dist/react/layers.js.map +1 -0
- package/dist/react/overlays.d.ts +67 -0
- package/dist/react/overlays.d.ts.map +1 -0
- package/dist/react/overlays.js +98 -0
- package/dist/react/overlays.js.map +1 -0
- package/dist/react/popup.d.ts +21 -0
- package/dist/react/popup.d.ts.map +1 -0
- package/dist/react/popup.js +94 -0
- package/dist/react/popup.js.map +1 -0
- package/dist/react/search.d.ts +33 -0
- package/dist/react/search.d.ts.map +1 -0
- package/dist/react/search.js +82 -0
- package/dist/react/search.js.map +1 -0
- package/dist/react/setupProgress.d.ts +35 -0
- package/dist/react/setupProgress.d.ts.map +1 -0
- package/dist/react/setupProgress.js +68 -0
- package/dist/react/setupProgress.js.map +1 -0
- package/dist/react/shell.d.ts +17 -0
- package/dist/react/shell.d.ts.map +1 -0
- package/dist/react/shell.js +13 -0
- package/dist/react/shell.js.map +1 -0
- package/dist/react/sidebar.d.ts +23 -0
- package/dist/react/sidebar.d.ts.map +1 -0
- package/dist/react/sidebar.js +26 -0
- package/dist/react/sidebar.js.map +1 -0
- package/dist/react/table.d.ts +39 -0
- package/dist/react/table.d.ts.map +1 -0
- package/dist/react/table.js +86 -0
- package/dist/react/table.js.map +1 -0
- package/dist/react/tasks.d.ts +41 -0
- package/dist/react/tasks.d.ts.map +1 -0
- package/dist/react/tasks.js +79 -0
- package/dist/react/tasks.js.map +1 -0
- package/dist/react/tour.d.ts +42 -0
- package/dist/react/tour.d.ts.map +1 -0
- package/dist/react/tour.js +68 -0
- package/dist/react/tour.js.map +1 -0
- package/dist/react/windowBoundary.d.ts +22 -0
- package/dist/react/windowBoundary.d.ts.map +1 -0
- package/dist/react/windowBoundary.js +50 -0
- package/dist/react/windowBoundary.js.map +1 -0
- package/dist/react/wizard.d.ts +41 -0
- package/dist/react/wizard.d.ts.map +1 -0
- package/dist/react/wizard.js +60 -0
- package/dist/react/wizard.js.map +1 -0
- package/llms.txt +410 -0
- package/package.json +64 -0
- package/src/core/commands.ts +142 -0
- package/src/core/desk.ts +229 -0
- package/src/core/events.ts +99 -0
- package/src/core/index.ts +43 -0
- package/src/core/location.ts +127 -0
- package/src/core/shortcuts.ts +119 -0
- package/src/core/titles.ts +28 -0
- package/src/core/types.ts +49 -0
- package/src/desk.css +2550 -0
- package/src/react/Desktop.tsx +349 -0
- package/src/react/Dock.tsx +258 -0
- package/src/react/MenuBar.tsx +350 -0
- package/src/react/appFrame.tsx +183 -0
- package/src/react/arrange.ts +82 -0
- package/src/react/commands.ts +60 -0
- package/src/react/context.tsx +55 -0
- package/src/react/controls.tsx +364 -0
- package/src/react/conversation.tsx +208 -0
- package/src/react/dnd.tsx +189 -0
- package/src/react/dragContext.tsx +60 -0
- package/src/react/events.tsx +149 -0
- package/src/react/index.ts +64 -0
- package/src/react/layers.ts +66 -0
- package/src/react/overlays.tsx +254 -0
- package/src/react/popup.tsx +176 -0
- package/src/react/search.tsx +177 -0
- package/src/react/setupProgress.ts +113 -0
- package/src/react/shell.tsx +27 -0
- package/src/react/sidebar.tsx +84 -0
- package/src/react/table.tsx +176 -0
- package/src/react/tasks.tsx +176 -0
- package/src/react/tour.tsx +183 -0
- package/src/react/windowBoundary.tsx +110 -0
- package/src/react/wizard.tsx +150 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { canPerform, perform } from '../core/commands.js';
|
|
4
|
+
import { focusedId } from '../core/desk.js';
|
|
5
|
+
import { bindShortcuts, formatShortcut, isApplePlatform } from '../core/shortcuts.js';
|
|
6
|
+
import { useDesk } from './context.js';
|
|
7
|
+
export const menuCommand = (label, command, options = {}) => ({
|
|
8
|
+
type: 'command',
|
|
9
|
+
label,
|
|
10
|
+
command,
|
|
11
|
+
...options,
|
|
12
|
+
});
|
|
13
|
+
export const menuAction = (label, onSelect, options = {}) => ({
|
|
14
|
+
type: 'action',
|
|
15
|
+
label,
|
|
16
|
+
onSelect,
|
|
17
|
+
...options,
|
|
18
|
+
});
|
|
19
|
+
export const menuSeparator = () => ({ type: 'separator' });
|
|
20
|
+
export const menuHeader = (label) => ({ type: 'header', label });
|
|
21
|
+
/** One item per open window, the key window checked — what a Window menu lists at its foot. */
|
|
22
|
+
export function windowMenuItems(state, focus, title) {
|
|
23
|
+
const key = focusedId(state);
|
|
24
|
+
return state.windows.map(w => menuAction(title(w.id), () => focus(w.id), { checked: w.id === key }));
|
|
25
|
+
}
|
|
26
|
+
const read = (flag) => (typeof flag === 'function' ? flag() : flag);
|
|
27
|
+
const selectable = (r) => r.enabled && (r.item.type === 'command' || r.item.type === 'action');
|
|
28
|
+
export function MenuBar({ menus, status = [], leading, trailing, label = 'Menu bar', shortcuts = true, className }) {
|
|
29
|
+
const desk = useDesk();
|
|
30
|
+
const all = useMemo(() => [...menus, ...status], [menus, status]);
|
|
31
|
+
const [open, setOpen] = useState(null);
|
|
32
|
+
const [roving, setRoving] = useState(0);
|
|
33
|
+
const bar = useRef(null);
|
|
34
|
+
const titles = useRef(new Map());
|
|
35
|
+
// Where focus was before the keyboard reached the menu bar, so a chosen command starts from there.
|
|
36
|
+
const returnTo = useRef(null);
|
|
37
|
+
const baseId = useId();
|
|
38
|
+
const apple = useMemo(isApplePlatform, []);
|
|
39
|
+
const resolve = (menu) => (typeof menu.items === 'function' ? menu.items() : menu.items).map(item => ({
|
|
40
|
+
item,
|
|
41
|
+
enabled: item.type === 'command' ? canPerform(desk, item.command)
|
|
42
|
+
: item.type === 'action' ? !read(item.disabled)
|
|
43
|
+
: false,
|
|
44
|
+
checked: item.type === 'command' || item.type === 'action' ? read(item.checked) : undefined,
|
|
45
|
+
}));
|
|
46
|
+
const step = (items, from, delta) => {
|
|
47
|
+
for (let i = 1; i <= items.length; i++) {
|
|
48
|
+
const index = (from + delta * i + items.length * 2) % items.length;
|
|
49
|
+
if (items[index] && selectable(items[index]))
|
|
50
|
+
return index;
|
|
51
|
+
}
|
|
52
|
+
return -1;
|
|
53
|
+
};
|
|
54
|
+
const openMenu = (index, via, active = 'first') => {
|
|
55
|
+
const menu = all[(index + all.length) % all.length];
|
|
56
|
+
if (!menu)
|
|
57
|
+
return;
|
|
58
|
+
const items = resolve(menu);
|
|
59
|
+
setRoving(all.indexOf(menu));
|
|
60
|
+
setOpen({
|
|
61
|
+
id: menu.id,
|
|
62
|
+
items,
|
|
63
|
+
via,
|
|
64
|
+
active: active === 'none' ? -1 : active === 'first' ? step(items, -1, 1) : step(items, items.length, -1),
|
|
65
|
+
});
|
|
66
|
+
if (via === 'keyboard')
|
|
67
|
+
titles.current.get(menu.id)?.focus();
|
|
68
|
+
};
|
|
69
|
+
const close = (returnFocus) => {
|
|
70
|
+
const was = open;
|
|
71
|
+
setOpen(null);
|
|
72
|
+
if (returnFocus && was)
|
|
73
|
+
titles.current.get(was.id)?.focus();
|
|
74
|
+
};
|
|
75
|
+
const activate = (resolved) => {
|
|
76
|
+
if (!resolved || !selectable(resolved))
|
|
77
|
+
return;
|
|
78
|
+
const wasKeyboard = open?.via === 'keyboard';
|
|
79
|
+
setOpen(null);
|
|
80
|
+
// Keyboard use put focus on the menu bar; send it back so the command starts where the person was working.
|
|
81
|
+
if (wasKeyboard && bar.current?.contains(document.activeElement)) {
|
|
82
|
+
if (returnTo.current?.isConnected)
|
|
83
|
+
returnTo.current.focus();
|
|
84
|
+
else
|
|
85
|
+
document.activeElement.blur();
|
|
86
|
+
}
|
|
87
|
+
const { item } = resolved;
|
|
88
|
+
if (item.type === 'command')
|
|
89
|
+
perform(desk, item.command, item.args);
|
|
90
|
+
else if (item.type === 'action')
|
|
91
|
+
item.onSelect();
|
|
92
|
+
};
|
|
93
|
+
// While a menu is open, the keyboard drives it without it ever holding focus.
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (!open)
|
|
96
|
+
return;
|
|
97
|
+
const menuIndex = all.findIndex(m => m.id === open.id);
|
|
98
|
+
const onKey = (event) => {
|
|
99
|
+
const handled = (() => {
|
|
100
|
+
switch (event.key) {
|
|
101
|
+
case 'ArrowDown':
|
|
102
|
+
setOpen({ ...open, active: step(open.items, open.active, 1) });
|
|
103
|
+
return true;
|
|
104
|
+
case 'ArrowUp':
|
|
105
|
+
setOpen({ ...open, active: step(open.items, open.active < 0 ? open.items.length : open.active, -1) });
|
|
106
|
+
return true;
|
|
107
|
+
case 'Home':
|
|
108
|
+
setOpen({ ...open, active: step(open.items, -1, 1) });
|
|
109
|
+
return true;
|
|
110
|
+
case 'End':
|
|
111
|
+
setOpen({ ...open, active: step(open.items, open.items.length, -1) });
|
|
112
|
+
return true;
|
|
113
|
+
case 'ArrowRight':
|
|
114
|
+
openMenu(menuIndex + 1, open.via);
|
|
115
|
+
return true;
|
|
116
|
+
case 'ArrowLeft':
|
|
117
|
+
openMenu(menuIndex - 1, open.via);
|
|
118
|
+
return true;
|
|
119
|
+
case 'Enter':
|
|
120
|
+
case ' ':
|
|
121
|
+
activate(open.items[open.active]);
|
|
122
|
+
return true;
|
|
123
|
+
case 'Escape':
|
|
124
|
+
close(open.via === 'keyboard');
|
|
125
|
+
return true;
|
|
126
|
+
case 'Tab':
|
|
127
|
+
close(false);
|
|
128
|
+
return false;
|
|
129
|
+
default: {
|
|
130
|
+
if (event.key.length !== 1 || event.metaKey || event.ctrlKey || event.altKey)
|
|
131
|
+
return false;
|
|
132
|
+
const letter = event.key.toLowerCase();
|
|
133
|
+
const start = open.active;
|
|
134
|
+
for (let i = 1; i <= open.items.length; i++) {
|
|
135
|
+
const index = (start + i) % open.items.length;
|
|
136
|
+
const r = open.items[index];
|
|
137
|
+
if (r && selectable(r) && 'label' in r.item && r.item.label.toLowerCase().startsWith(letter)) {
|
|
138
|
+
setOpen({ ...open, active: index });
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
})();
|
|
146
|
+
if (handled) {
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
event.stopPropagation();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const onPointerDown = (event) => {
|
|
152
|
+
if (!bar.current?.contains(event.target))
|
|
153
|
+
setOpen(null);
|
|
154
|
+
};
|
|
155
|
+
document.addEventListener('keydown', onKey, true);
|
|
156
|
+
document.addEventListener('pointerdown', onPointerDown, true);
|
|
157
|
+
return () => {
|
|
158
|
+
document.removeEventListener('keydown', onKey, true);
|
|
159
|
+
document.removeEventListener('pointerdown', onPointerDown, true);
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
// Menus own their key equivalents, as in AppKit. Menus that build their items when opened are
|
|
163
|
+
// read when a key is pressed, so their shortcuts work before the menu has ever been opened.
|
|
164
|
+
const menusNow = useRef(all);
|
|
165
|
+
menusNow.current = all;
|
|
166
|
+
useEffect(() => shortcuts
|
|
167
|
+
? bindShortcuts(desk, () => Object.fromEntries(menusNow.current
|
|
168
|
+
.flatMap(m => (typeof m.items === 'function' ? m.items() : m.items))
|
|
169
|
+
.flatMap(i => (i.type === 'command' && i.shortcut ? [[i.shortcut, i.command]] : []))), { apple })
|
|
170
|
+
: undefined, [desk, shortcuts, apple]);
|
|
171
|
+
const onBarKeyDown = (event) => {
|
|
172
|
+
if (open)
|
|
173
|
+
return;
|
|
174
|
+
const index = all.findIndex(m => titles.current.get(m.id) === document.activeElement);
|
|
175
|
+
if (index < 0)
|
|
176
|
+
return;
|
|
177
|
+
const move = (to) => {
|
|
178
|
+
const next = all[(to + all.length) % all.length];
|
|
179
|
+
if (next) {
|
|
180
|
+
setRoving(all.indexOf(next));
|
|
181
|
+
titles.current.get(next.id)?.focus();
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
switch (event.key) {
|
|
185
|
+
case 'ArrowRight':
|
|
186
|
+
move(index + 1);
|
|
187
|
+
break;
|
|
188
|
+
case 'ArrowLeft':
|
|
189
|
+
move(index - 1);
|
|
190
|
+
break;
|
|
191
|
+
case 'Home':
|
|
192
|
+
move(0);
|
|
193
|
+
break;
|
|
194
|
+
case 'End':
|
|
195
|
+
move(all.length - 1);
|
|
196
|
+
break;
|
|
197
|
+
case 'ArrowDown':
|
|
198
|
+
case 'Enter':
|
|
199
|
+
case ' ':
|
|
200
|
+
openMenu(index, 'keyboard', 'first');
|
|
201
|
+
break;
|
|
202
|
+
case 'ArrowUp':
|
|
203
|
+
openMenu(index, 'keyboard', 'last');
|
|
204
|
+
break;
|
|
205
|
+
default: return;
|
|
206
|
+
}
|
|
207
|
+
event.preventDefault();
|
|
208
|
+
};
|
|
209
|
+
const renderMenu = (menu, index) => {
|
|
210
|
+
const isOpen = open?.id === menu.id;
|
|
211
|
+
const titleId = `${baseId}-title-${menu.id}`;
|
|
212
|
+
const menuId = `${baseId}-menu-${menu.id}`;
|
|
213
|
+
return (_jsxs("div", { className: "desk-menubar-slot", "data-align": index >= menus.length ? 'end' : undefined, children: [_jsx("button", { ref: el => {
|
|
214
|
+
if (el)
|
|
215
|
+
titles.current.set(menu.id, el);
|
|
216
|
+
else
|
|
217
|
+
titles.current.delete(menu.id);
|
|
218
|
+
}, id: titleId, type: "button", role: "menuitem", className: "desk-menubar-title", "aria-haspopup": "menu", "aria-expanded": isOpen, "aria-controls": isOpen ? menuId : undefined, "aria-label": menu.title ? menu.label : undefined, "data-emphasis": menu.emphasis || undefined, tabIndex: index === roving ? 0 : -1,
|
|
219
|
+
// Opening a menu with the pointer must not move focus out of the window being worked in.
|
|
220
|
+
onMouseDown: event => event.preventDefault(), onClick: () => (isOpen ? close(false) : openMenu(index, 'pointer', 'none')), onPointerEnter: () => {
|
|
221
|
+
if (open && !isOpen)
|
|
222
|
+
openMenu(index, open.via, open.via === 'keyboard' ? 'first' : 'none');
|
|
223
|
+
}, children: menu.title ?? menu.label }), isOpen && open && (_jsx("div", { id: menuId, role: "menu", "aria-labelledby": titleId, className: "desk-menu", "aria-activedescendant": open.active >= 0 ? `${menuId}-${open.active}` : undefined, children: open.items.map((resolved, i) => {
|
|
224
|
+
const { item } = resolved;
|
|
225
|
+
if (item.type === 'separator')
|
|
226
|
+
return _jsx("div", { role: "separator", className: "desk-menu-separator" }, i);
|
|
227
|
+
if (item.type === 'header')
|
|
228
|
+
return _jsx("div", { role: "presentation", className: "desk-menu-header", children: item.label }, i);
|
|
229
|
+
const checkable = resolved.checked !== undefined;
|
|
230
|
+
return (_jsxs("div", { id: `${menuId}-${i}`, role: checkable ? 'menuitemcheckbox' : 'menuitem', "aria-checked": checkable ? resolved.checked : undefined, "aria-disabled": !resolved.enabled || undefined, className: "desk-menu-item", "data-active": i === open.active || undefined, onMouseDown: event => event.preventDefault(), onPointerMove: () => {
|
|
231
|
+
if (open.active !== i && selectable(resolved))
|
|
232
|
+
setOpen({ ...open, active: i });
|
|
233
|
+
}, onPointerLeave: () => {
|
|
234
|
+
if (open.via === 'pointer' && open.active === i)
|
|
235
|
+
setOpen({ ...open, active: -1 });
|
|
236
|
+
}, onClick: () => activate(resolved), children: [_jsx("span", { className: "desk-menu-check", "aria-hidden": "true", children: resolved.checked ? '✓' : '' }), _jsx("span", { className: "desk-menu-label", children: item.label }), item.detail && _jsx("span", { className: "desk-menu-detail", children: item.detail }), item.shortcut && _jsx("kbd", { className: "desk-menu-shortcut", children: formatShortcut(item.shortcut, apple) })] }, i));
|
|
237
|
+
}) }))] }, menu.id));
|
|
238
|
+
};
|
|
239
|
+
return (_jsxs("div", { ref: bar, role: "menubar", "aria-label": label, className: ['desk-menubar', className].filter(Boolean).join(' '), onKeyDown: onBarKeyDown, onFocus: event => {
|
|
240
|
+
const from = event.relatedTarget;
|
|
241
|
+
if (from instanceof HTMLElement && !bar.current?.contains(from))
|
|
242
|
+
returnTo.current = from;
|
|
243
|
+
}, children: [leading && _jsx("div", { className: "desk-menubar-leading", children: leading }), menus.map((menu, i) => renderMenu(menu, i)), _jsx("span", { className: "desk-menubar-spacer" }), status.map((menu, i) => renderMenu(menu, menus.length + i)), trailing && _jsx("div", { className: "desk-menubar-trailing", children: trailing })] }));
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=MenuBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MenuBar.js","sourceRoot":"","sources":["../../src/react/MenuBar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Accepts } from './dnd.js';
|
|
2
|
+
export interface AppFrameProps {
|
|
3
|
+
/** The accessible name of the frame: what the app is. */
|
|
4
|
+
readonly title: string;
|
|
5
|
+
/** The app's page, as a URL or as the HTML itself. Give one. */
|
|
6
|
+
readonly src?: string;
|
|
7
|
+
readonly srcDoc?: string;
|
|
8
|
+
/** Topics the app may hear, e.g. `['ride.selected']` or `['ride.*']`. Default: none. */
|
|
9
|
+
readonly listens?: readonly string[];
|
|
10
|
+
/** Topics the app may say. Anything else it publishes is dropped. Default: none. */
|
|
11
|
+
readonly says?: readonly string[];
|
|
12
|
+
/** What may be dropped on it. Default: nothing. */
|
|
13
|
+
readonly accepts?: Accepts;
|
|
14
|
+
/** Windows the app may ask to open. Default: none. */
|
|
15
|
+
readonly opens?: readonly string[];
|
|
16
|
+
/**
|
|
17
|
+
* The frame's sandbox. The default lets the app run its own scripts and forms, and
|
|
18
|
+
* nothing more: no same-origin access, no top-level navigation, no pop-ups.
|
|
19
|
+
*/
|
|
20
|
+
readonly sandbox?: string;
|
|
21
|
+
readonly className?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function AppFrame({ title, src, srcDoc, listens, says, accepts, opens, sandbox, className, }: AppFrameProps): import("react").JSX.Element;
|
|
24
|
+
/**
|
|
25
|
+
* The app's half of the bridge, as a script to put in its page. It gives the page a
|
|
26
|
+
* small `desk` object:
|
|
27
|
+
*
|
|
28
|
+
* desk.on('ride.selected', event => …) hear a granted topic
|
|
29
|
+
* desk.onDrop(drop => …) take what is dropped on the window
|
|
30
|
+
* desk.publish('ride.selected', ride) say something, if it was granted
|
|
31
|
+
* desk.open('map') ask for a window, if it was granted
|
|
32
|
+
* desk.window which window this is, once connected
|
|
33
|
+
*/
|
|
34
|
+
export declare const APP_BRIDGE_SCRIPT = "(() => {\n const handlers = new Map();\n const drops = [];\n const send = message => parent.postMessage(Object.assign({ desk: 1 }, message), '*');\n const desk = {\n window: null,\n listens: [],\n says: [],\n on(topic, handler) { (handlers.get(topic) || handlers.set(topic, []).get(topic)).push(handler); },\n onDrop(handler) { drops.push(handler); },\n publish(topic, payload) { send({ kind: 'publish', topic, payload }); },\n open(window) { send({ kind: 'open', window }); },\n };\n const matches = (pattern, topic) =>\n pattern === '*' || pattern === topic || (pattern.endsWith('.*') && (topic === pattern.slice(0, -2) || topic.startsWith(pattern.slice(0, -1))));\n addEventListener('message', event => {\n if (event.source !== parent || !event.data || event.data.desk !== 1) return;\n const message = event.data;\n if (message.kind === 'hello') { desk.window = message.window; desk.listens = message.listens; desk.says = message.says; }\n if (message.kind === 'event') for (const [pattern, list] of handlers) if (matches(pattern, message.topic)) list.forEach(h => h(message));\n if (message.kind === 'drop') drops.forEach(h => h(message));\n });\n window.desk = desk;\n send({ kind: 'ready' });\n})();";
|
|
35
|
+
/** Puts the bridge into a page of HTML, so a generated app needs nothing added by hand. */
|
|
36
|
+
export declare function withAppBridge(html: string): string;
|
|
37
|
+
//# sourceMappingURL=appFrame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appFrame.d.ts","sourceRoot":"","sources":["../../src/react/appFrame.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AA8BvC,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,gEAAgE;IAChE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,wFAAwF;IACxF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,oFAAoF;IACpF,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,mDAAmD;IACnD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B,sDAAsD;IACtD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,GAAG,EACH,MAAM,EACN,OAAY,EACZ,IAAS,EACT,OAAO,EACP,KAAU,EACV,OAAqC,EACrC,SAAS,GACV,EAAE,aAAa,+BAyEf;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,2uCAwBxB,CAAA;AAEN,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGlD"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { topicMatches } from '../core/events.js';
|
|
4
|
+
import { useDragging, useDropTarget } from './dnd.js';
|
|
5
|
+
import { useDesk, useWindowId } from './context.js';
|
|
6
|
+
import { useBus } from './events.js';
|
|
7
|
+
/*
|
|
8
|
+
* A generated app — a self-contained page — running inside a window, and taking
|
|
9
|
+
* part in the desk as a native window does: hearing events, saying things, and
|
|
10
|
+
* taking what is dropped on it.
|
|
11
|
+
*
|
|
12
|
+
* It runs in a sandboxed frame with no access to the host's origin, and talks
|
|
13
|
+
* only through messages. The host decides, per app, which topics it may hear,
|
|
14
|
+
* which it may say, and what may be dropped on it. Nothing else crosses.
|
|
15
|
+
*/
|
|
16
|
+
/** The protocol version. Every message carries it, so a mismatch is ignored rather than misread. */
|
|
17
|
+
const PROTOCOL = 1;
|
|
18
|
+
const isAppMessage = (data) => typeof data === 'object' && data !== null && data.desk === PROTOCOL && typeof data.kind === 'string';
|
|
19
|
+
export function AppFrame({ title, src, srcDoc, listens = [], says = [], accepts, opens = [], sandbox = 'allow-scripts allow-forms', className, }) {
|
|
20
|
+
const desk = useDesk();
|
|
21
|
+
const bus = useBus();
|
|
22
|
+
const windowId = useWindowId();
|
|
23
|
+
const frame = useRef(null);
|
|
24
|
+
const dragging = useDragging();
|
|
25
|
+
const post = (message) => {
|
|
26
|
+
// A sandboxed frame without same-origin has an opaque origin, so '*' is the only
|
|
27
|
+
// target that reaches it. It is safe here: the message goes to this frame's
|
|
28
|
+
// window and no other.
|
|
29
|
+
frame.current?.contentWindow?.postMessage(message, '*');
|
|
30
|
+
};
|
|
31
|
+
const latest = useRef({ listens, says, opens, windowId });
|
|
32
|
+
latest.current = { listens, says, opens, windowId };
|
|
33
|
+
// What the app says: only from this frame, only on topics it was granted.
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const onMessage = (event) => {
|
|
36
|
+
if (!frame.current || event.source !== frame.current.contentWindow || !isAppMessage(event.data))
|
|
37
|
+
return;
|
|
38
|
+
const message = event.data;
|
|
39
|
+
const granted = latest.current;
|
|
40
|
+
if (message.kind === 'ready') {
|
|
41
|
+
post({ desk: PROTOCOL, kind: 'hello', window: granted.windowId, listens: granted.listens, says: granted.says });
|
|
42
|
+
}
|
|
43
|
+
else if (message.kind === 'publish') {
|
|
44
|
+
if (typeof message.topic === 'string' && granted.says.some(pattern => topicMatches(pattern, message.topic))) {
|
|
45
|
+
bus.publish(message.topic, message.payload, { from: granted.windowId });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (message.kind === 'open') {
|
|
49
|
+
if (typeof message.window === 'string' && granted.opens.includes(message.window))
|
|
50
|
+
desk.open(message.window);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
window.addEventListener('message', onMessage);
|
|
54
|
+
return () => window.removeEventListener('message', onMessage);
|
|
55
|
+
}, [bus, desk]);
|
|
56
|
+
// What the app hears: only the topics it was granted, and never its own words back.
|
|
57
|
+
const listenKey = listens.join('|');
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!listens.length)
|
|
60
|
+
return;
|
|
61
|
+
const stops = listens.map(pattern => bus.subscribe(pattern, (event) => {
|
|
62
|
+
if (event.from !== null && event.from === latest.current.windowId)
|
|
63
|
+
return;
|
|
64
|
+
post({ desk: PROTOCOL, kind: 'event', topic: event.topic, payload: event.payload, from: event.from });
|
|
65
|
+
}));
|
|
66
|
+
return () => stops.forEach(stop => stop());
|
|
67
|
+
// listenKey stands for the list's contents; the array itself changes identity every render.
|
|
68
|
+
}, [bus, listenKey]);
|
|
69
|
+
const { dropProps } = useDropTarget({
|
|
70
|
+
accepts: accepts ?? (() => false),
|
|
71
|
+
disabled: accepts === undefined,
|
|
72
|
+
onDrop: drag => post({ desk: PROTOCOL, kind: 'drop', type: drag.type, payload: drag.payload, from: drag.from }),
|
|
73
|
+
});
|
|
74
|
+
// While something is being carried, the frame must not swallow the pointer, or the
|
|
75
|
+
// desk can never see that it is over this app.
|
|
76
|
+
const frameStyle = { pointerEvents: dragging ? 'none' : 'auto' };
|
|
77
|
+
return (_jsx("div", { className: ['desk-app-frame', className].filter(Boolean).join(' '), ...dropProps, children: _jsx("iframe", { ref: frame, title: title, className: "desk-app-frame-page", sandbox: sandbox, ...(srcDoc !== undefined ? { srcDoc } : src !== undefined ? { src } : {}), style: frameStyle }) }));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The app's half of the bridge, as a script to put in its page. It gives the page a
|
|
81
|
+
* small `desk` object:
|
|
82
|
+
*
|
|
83
|
+
* desk.on('ride.selected', event => …) hear a granted topic
|
|
84
|
+
* desk.onDrop(drop => …) take what is dropped on the window
|
|
85
|
+
* desk.publish('ride.selected', ride) say something, if it was granted
|
|
86
|
+
* desk.open('map') ask for a window, if it was granted
|
|
87
|
+
* desk.window which window this is, once connected
|
|
88
|
+
*/
|
|
89
|
+
export const APP_BRIDGE_SCRIPT = `(() => {
|
|
90
|
+
const handlers = new Map();
|
|
91
|
+
const drops = [];
|
|
92
|
+
const send = message => parent.postMessage(Object.assign({ desk: ${PROTOCOL} }, message), '*');
|
|
93
|
+
const desk = {
|
|
94
|
+
window: null,
|
|
95
|
+
listens: [],
|
|
96
|
+
says: [],
|
|
97
|
+
on(topic, handler) { (handlers.get(topic) || handlers.set(topic, []).get(topic)).push(handler); },
|
|
98
|
+
onDrop(handler) { drops.push(handler); },
|
|
99
|
+
publish(topic, payload) { send({ kind: 'publish', topic, payload }); },
|
|
100
|
+
open(window) { send({ kind: 'open', window }); },
|
|
101
|
+
};
|
|
102
|
+
const matches = (pattern, topic) =>
|
|
103
|
+
pattern === '*' || pattern === topic || (pattern.endsWith('.*') && (topic === pattern.slice(0, -2) || topic.startsWith(pattern.slice(0, -1))));
|
|
104
|
+
addEventListener('message', event => {
|
|
105
|
+
if (event.source !== parent || !event.data || event.data.desk !== ${PROTOCOL}) return;
|
|
106
|
+
const message = event.data;
|
|
107
|
+
if (message.kind === 'hello') { desk.window = message.window; desk.listens = message.listens; desk.says = message.says; }
|
|
108
|
+
if (message.kind === 'event') for (const [pattern, list] of handlers) if (matches(pattern, message.topic)) list.forEach(h => h(message));
|
|
109
|
+
if (message.kind === 'drop') drops.forEach(h => h(message));
|
|
110
|
+
});
|
|
111
|
+
window.desk = desk;
|
|
112
|
+
send({ kind: 'ready' });
|
|
113
|
+
})();`;
|
|
114
|
+
/** Puts the bridge into a page of HTML, so a generated app needs nothing added by hand. */
|
|
115
|
+
export function withAppBridge(html) {
|
|
116
|
+
const script = `<script>${APP_BRIDGE_SCRIPT}</script>`;
|
|
117
|
+
return /<head[^>]*>/i.test(html) ? html.replace(/<head[^>]*>/i, match => `${match}${script}`) : `${script}${html}`;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=appFrame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appFrame.js","sourceRoot":"","sources":["../../src/react/appFrame.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Frame, WindowId } from '../core/types.js';
|
|
2
|
+
/** The part of the stage windows are laid out in, and the gap between them. */
|
|
3
|
+
export interface Area extends Frame {
|
|
4
|
+
readonly gap: number;
|
|
5
|
+
}
|
|
6
|
+
export interface ArrangeLimits {
|
|
7
|
+
readonly minWidth: number;
|
|
8
|
+
readonly minHeight: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Where Arrange puts each window. The same windows always land in the same places:
|
|
12
|
+
* the focused window takes the first and largest place, the rest follow in the order
|
|
13
|
+
* they were opened. Nothing here looks at where windows are now, so a small drag
|
|
14
|
+
* never swaps two windows the next time.
|
|
15
|
+
*
|
|
16
|
+
* 1 fills the area · 2 side by side · 3 one tall on the left, two stacked on the right
|
|
17
|
+
* 4 a 2 × 2 grid · more a grid, while every cell stays usable
|
|
18
|
+
*
|
|
19
|
+
* When a grid cell would be too small to use, the focused window and the one used
|
|
20
|
+
* before it share the area and the rest cascade over the second half, so the focused
|
|
21
|
+
* window, which stays in front, covers none of them.
|
|
22
|
+
*/
|
|
23
|
+
export declare function arrangement(ids: readonly WindowId[], focused: WindowId | null, previous: WindowId | null, area: Area, limits: ArrangeLimits): Record<WindowId, Frame>;
|
|
24
|
+
//# sourceMappingURL=arrange.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arrange.d.ts","sourceRoot":"","sources":["../../src/react/arrange.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAEvD,+EAA+E;AAC/E,MAAM,WAAW,IAAK,SAAQ,KAAK;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,SAAS,QAAQ,EAAE,EACxB,OAAO,EAAE,QAAQ,GAAG,IAAI,EACxB,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,aAAa,GACpB,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,CAkDzB"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where Arrange puts each window. The same windows always land in the same places:
|
|
3
|
+
* the focused window takes the first and largest place, the rest follow in the order
|
|
4
|
+
* they were opened. Nothing here looks at where windows are now, so a small drag
|
|
5
|
+
* never swaps two windows the next time.
|
|
6
|
+
*
|
|
7
|
+
* 1 fills the area · 2 side by side · 3 one tall on the left, two stacked on the right
|
|
8
|
+
* 4 a 2 × 2 grid · more a grid, while every cell stays usable
|
|
9
|
+
*
|
|
10
|
+
* When a grid cell would be too small to use, the focused window and the one used
|
|
11
|
+
* before it share the area and the rest cascade over the second half, so the focused
|
|
12
|
+
* window, which stays in front, covers none of them.
|
|
13
|
+
*/
|
|
14
|
+
export function arrangement(ids, focused, previous, area, limits) {
|
|
15
|
+
const first = focused && ids.includes(focused) ? focused : ids[0];
|
|
16
|
+
if (first === undefined)
|
|
17
|
+
return {};
|
|
18
|
+
const order = [first, ...ids.filter(id => id !== first)];
|
|
19
|
+
const { x, y, width, height, gap } = area;
|
|
20
|
+
const half = (width - gap) / 2;
|
|
21
|
+
const place = (frames) => Object.fromEntries(order.map((id, i) => [id, frames[i]]));
|
|
22
|
+
if (order.length === 1)
|
|
23
|
+
return place([{ x, y, width, height }]);
|
|
24
|
+
if (order.length === 2)
|
|
25
|
+
return place([{ x, y, width: half, height }, { x: x + half + gap, y, width: half, height }]);
|
|
26
|
+
if (order.length === 3) {
|
|
27
|
+
const stacked = (height - gap) / 2;
|
|
28
|
+
return place([
|
|
29
|
+
{ x, y, width: half, height },
|
|
30
|
+
{ x: x + half + gap, y, width: half, height: stacked },
|
|
31
|
+
{ x: x + half + gap, y: y + stacked + gap, width: half, height: stacked },
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
const columns = Math.ceil(Math.sqrt(order.length));
|
|
35
|
+
const rows = Math.ceil(order.length / columns);
|
|
36
|
+
const cellWidth = (width - gap * (columns - 1)) / columns;
|
|
37
|
+
const cellHeight = (height - gap * (rows - 1)) / rows;
|
|
38
|
+
if (cellWidth >= limits.minWidth && cellHeight >= limits.minHeight) {
|
|
39
|
+
return place(order.map((_, i) => ({
|
|
40
|
+
x: x + (i % columns) * (cellWidth + gap),
|
|
41
|
+
y: y + Math.floor(i / columns) * (cellHeight + gap),
|
|
42
|
+
width: cellWidth,
|
|
43
|
+
height: cellHeight,
|
|
44
|
+
})));
|
|
45
|
+
}
|
|
46
|
+
// Too many to grid: the two most recent share the area, the rest cascade over the second half.
|
|
47
|
+
const second = previous && previous !== first && ids.includes(previous) ? previous : order[1];
|
|
48
|
+
const rest = order.filter(id => id !== first && id !== second);
|
|
49
|
+
const step = 28;
|
|
50
|
+
const floatWidth = Math.max(limits.minWidth, Math.min(720, half - step * 2));
|
|
51
|
+
const floatHeight = Math.max(limits.minHeight, Math.min(520, height - step * 2));
|
|
52
|
+
return {
|
|
53
|
+
[first]: { x, y, width: half, height },
|
|
54
|
+
[second]: { x: x + half + gap, y, width: half, height },
|
|
55
|
+
...Object.fromEntries(rest.map((id, i) => {
|
|
56
|
+
const n = i % 6;
|
|
57
|
+
return [id, { x: x + half + gap + step + n * 32, y: y + step + n * step, width: floatWidth, height: floatHeight }];
|
|
58
|
+
})),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=arrange.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arrange.js","sourceRoot":"","sources":["../../src/react/arrange.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,WAAW,CACzB,GAAwB,EACxB,OAAwB,EACxB,QAAyB,EACzB,IAAU,EACV,MAAqB;IAErB,MAAM,KAAK,GAAG,OAAO,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IACjE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAA;IAClC,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,CAAA;IACxD,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;IACzC,MAAM,IAAI,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;IAC9B,MAAM,KAAK,GAAG,CAAC,MAAwB,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAU,CAAC,CAAC,CAAC,CAAA;IAE9G,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;IAC/D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;IACpH,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;QAClC,OAAO,KAAK,CAAC;YACX,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE;YAC7B,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;YACtD,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,GAAG,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;SAC1E,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;IAClD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,CAAA;IAC9C,MAAM,SAAS,GAAG,CAAC,KAAK,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAA;IACzD,MAAM,UAAU,GAAG,CAAC,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;IACrD,IAAI,SAAS,IAAI,MAAM,CAAC,QAAQ,IAAI,UAAU,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACnE,OAAO,KAAK,CACV,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YACnB,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC;YACxC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACnD,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,UAAU;SACnB,CAAC,CAAC,CACJ,CAAA;IACH,CAAC;IAED,+FAA+F;IAC/F,MAAM,MAAM,GAAG,QAAQ,IAAI,QAAQ,KAAK,KAAK,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAE,KAAK,CAAC,CAAC,CAAc,CAAA;IAC3G,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,MAAM,CAAC,CAAA;IAC9D,MAAM,IAAI,GAAG,EAAE,CAAA;IACf,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAA;IAC5E,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAA;IAChF,OAAO;QACL,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE;QACtC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE;QACvD,GAAG,MAAM,CAAC,WAAW,CACnB,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE;YACjB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YACf,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAA;QACpH,CAAC,CAAC,CACH;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
import type { CommandHandler, CommandId } from '../core/commands.js';
|
|
3
|
+
import type { Keymap, ShortcutOptions } from '../core/shortcuts.js';
|
|
4
|
+
export interface UseCommandOptions {
|
|
5
|
+
/** Whether the handler can act right now. Read when the command arrives and when a menu opens. */
|
|
6
|
+
readonly enabled?: boolean | (() => boolean);
|
|
7
|
+
/**
|
|
8
|
+
* Where in the chain this responder sits.
|
|
9
|
+
* - `'window'` (the default inside a window): the window this component is rendered in.
|
|
10
|
+
* - `'app'` (the default outside windows): the top of the chain.
|
|
11
|
+
* - a ref: that element, e.g. an editor that answers Copy only while it has focus.
|
|
12
|
+
*/
|
|
13
|
+
readonly at?: 'window' | 'app' | RefObject<Element | null>;
|
|
14
|
+
}
|
|
15
|
+
/** Answers a command when it reaches this component's place in the responder chain. */
|
|
16
|
+
export declare function useCommand<Args = unknown>(command: CommandId, handler: CommandHandler<Args>, options?: UseCommandOptions): void;
|
|
17
|
+
/** Returns a function that sends a command along the chain, and reports whether anything handled it. */
|
|
18
|
+
export declare function usePerform(): <Args = unknown>(command: CommandId, args?: Args) => boolean;
|
|
19
|
+
/** Returns a function that asks whether a command would be handled right now — what a menu calls as it opens. */
|
|
20
|
+
export declare function useCanPerform(): (command: CommandId) => boolean;
|
|
21
|
+
/** Performs commands from keyboard shortcuts while mounted. Pass a stable keymap. */
|
|
22
|
+
export declare function useShortcuts(keymap: Keymap, options?: ShortcutOptions): void;
|
|
23
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/react/commands.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAEpE,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAGnE,MAAM,WAAW,iBAAiB;IAChC,kGAAkG;IAClG,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAA;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;CAC3D;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,OAAO,GAAE,iBAAsB,GAAG,IAAI,CAmBnI;AAED,wGAAwG;AACxG,wBAAgB,UAAU,IAAI,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,IAAI,KAAK,OAAO,CAGzF;AAED,iHAAiH;AACjH,wBAAgB,aAAa,IAAI,CAAC,OAAO,EAAE,SAAS,KAAK,OAAO,CAG/D;AAED,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,IAAI,CAIhF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useCallback, useContext, useEffect, useRef } from 'react';
|
|
2
|
+
import { addCommandHandler, canPerform, perform } from '../core/commands.js';
|
|
3
|
+
import { bindShortcuts } from '../core/shortcuts.js';
|
|
4
|
+
import { useDesk, WindowContext } from './context.js';
|
|
5
|
+
/** Answers a command when it reaches this component's place in the responder chain. */
|
|
6
|
+
export function useCommand(command, handler, options = {}) {
|
|
7
|
+
const window = useContext(WindowContext);
|
|
8
|
+
// Latest handler and enablement without re-registering on every render.
|
|
9
|
+
const latest = useRef({ handler, enabled: options.enabled });
|
|
10
|
+
latest.current = { handler, enabled: options.enabled };
|
|
11
|
+
const at = options.at ?? (window ? 'window' : 'app');
|
|
12
|
+
const ref = typeof at === 'object' ? at : at === 'window' ? window?.element : null;
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const target = at === 'app' ? document : ref?.current;
|
|
15
|
+
if (!target)
|
|
16
|
+
return;
|
|
17
|
+
return addCommandHandler(target, command, args => latest.current.handler(args), {
|
|
18
|
+
enabled: () => {
|
|
19
|
+
const enabled = latest.current.enabled;
|
|
20
|
+
return typeof enabled === 'function' ? enabled() : (enabled ?? true);
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}, [command, at, ref]);
|
|
24
|
+
}
|
|
25
|
+
/** Returns a function that sends a command along the chain, and reports whether anything handled it. */
|
|
26
|
+
export function usePerform() {
|
|
27
|
+
const desk = useDesk();
|
|
28
|
+
return useCallback((command, args) => perform(desk, command, args), [desk]);
|
|
29
|
+
}
|
|
30
|
+
/** Returns a function that asks whether a command would be handled right now — what a menu calls as it opens. */
|
|
31
|
+
export function useCanPerform() {
|
|
32
|
+
const desk = useDesk();
|
|
33
|
+
return useCallback(command => canPerform(desk, command), [desk]);
|
|
34
|
+
}
|
|
35
|
+
/** Performs commands from keyboard shortcuts while mounted. Pass a stable keymap. */
|
|
36
|
+
export function useShortcuts(keymap, options = {}) {
|
|
37
|
+
const desk = useDesk();
|
|
38
|
+
const { target, apple } = options;
|
|
39
|
+
useEffect(() => bindShortcuts(desk, keymap, { ...(target ? { target } : {}), ...(apple === undefined ? {} : { apple }) }), [desk, keymap, target, apple]);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/react/commands.ts"],"names":[],"mappings":"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"}
|