@miragon/modeler-shell 0.0.0-stage → 0.2.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/API.md +2869 -0
- package/LICENSE +21 -0
- package/README.md +566 -2
- package/THIRD_PARTY_NOTICES.md +52 -0
- package/dist/appearance-BBaM_4bD.d.ts +323 -0
- package/dist/appearance-D3FUcbu6.d.cts +53 -0
- package/dist/appearance-D3FUcbu6.d.ts +53 -0
- package/dist/appearance-XX3JC3pI.d.cts +323 -0
- package/dist/chunk-66HCSJNY.js +2792 -0
- package/dist/chunk-66HCSJNY.js.map +1 -0
- package/dist/chunk-KYDG5DMX.js +136 -0
- package/dist/chunk-KYDG5DMX.js.map +1 -0
- package/dist/chunk-N4QXFF3A.js +23 -0
- package/dist/chunk-N4QXFF3A.js.map +1 -0
- package/dist/chunk-OADW3GPA.js +785 -0
- package/dist/chunk-OADW3GPA.js.map +1 -0
- package/dist/core.cjs +873 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +246 -0
- package/dist/core.d.ts +246 -0
- package/dist/core.js +131 -0
- package/dist/core.js.map +1 -0
- package/dist/diagram-js.cjs +1147 -0
- package/dist/diagram-js.cjs.map +1 -0
- package/dist/diagram-js.d.cts +170 -0
- package/dist/diagram-js.d.ts +170 -0
- package/dist/diagram-js.js +879 -0
- package/dist/diagram-js.js.map +1 -0
- package/dist/index.cjs +3632 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +143 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol-DhYBakWL.d.cts +15 -0
- package/dist/protocol-IeW8bf4f.d.ts +15 -0
- package/dist/shell.css +1603 -0
- package/dist/testing-vscode-stub.cjs +332 -0
- package/dist/testing-vscode-stub.cjs.map +1 -0
- package/dist/testing-vscode-stub.d.cts +65 -0
- package/dist/testing-vscode-stub.d.ts +65 -0
- package/dist/testing-vscode-stub.js +154 -0
- package/dist/testing-vscode-stub.js.map +1 -0
- package/dist/testing.cjs +4663 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +231 -0
- package/dist/testing.d.ts +231 -0
- package/dist/testing.js +4483 -0
- package/dist/testing.js.map +1 -0
- package/dist/toasts-Bp0REqdq.d.cts +142 -0
- package/dist/toasts-Bp0REqdq.d.ts +142 -0
- package/dist/types-CnRrINRs.d.cts +445 -0
- package/dist/types-PuDZczzy.d.ts +445 -0
- package/dist/vscode-extension.cjs +215 -0
- package/dist/vscode-extension.cjs.map +1 -0
- package/dist/vscode-extension.d.cts +103 -0
- package/dist/vscode-extension.d.ts +103 -0
- package/dist/vscode-extension.js +24 -0
- package/dist/vscode-extension.js.map +1 -0
- package/dist/vscode-webview.cjs +3561 -0
- package/dist/vscode-webview.cjs.map +1 -0
- package/dist/vscode-webview.d.cts +84 -0
- package/dist/vscode-webview.d.ts +84 -0
- package/dist/vscode-webview.js +259 -0
- package/dist/vscode-webview.js.map +1 -0
- package/package.json +145 -4
- package/src/core/appearance.ts +104 -0
- package/src/core/feedback/appearance.ts +141 -0
- package/src/core/feedback/problems.ts +23 -0
- package/src/core/feedback/toasts.ts +118 -0
- package/src/core/fit.ts +220 -0
- package/src/core/index.ts +127 -0
- package/src/core/keymap.ts +295 -0
- package/src/core/layout.ts +69 -0
- package/src/core/messages.ts +159 -0
- package/src/core/sizes.ts +85 -0
- package/src/core/types.ts +106 -0
- package/src/core/web-chrome/bottom-row.ts +37 -0
- package/src/core/web-chrome/drop.ts +78 -0
- package/src/core/web-chrome/top-row.ts +66 -0
- package/src/core/zoom.ts +74 -0
- package/src/core.ts +2 -0
- package/src/diagram-js/connect.ts +75 -0
- package/src/diagram-js/index.ts +16 -0
- package/src/diagram-js/module.ts +20 -0
- package/src/diagram-js/palette-skin.ts +267 -0
- package/src/diagram-js/palette.css +269 -0
- package/src/diagram-js/service.ts +243 -0
- package/src/diagram-js/types.ts +67 -0
- package/src/diagram-js/viewport.ts +583 -0
- package/src/diagram-js.ts +2 -0
- package/src/dom/buttons.css +124 -0
- package/src/dom/element.ts +51 -0
- package/src/dom/feedback/appearance.ts +184 -0
- package/src/dom/feedback/feedback.css +435 -0
- package/src/dom/feedback/problems-popover.ts +287 -0
- package/src/dom/feedback/toasts.ts +431 -0
- package/src/dom/hint.ts +51 -0
- package/src/dom/icons.ts +309 -0
- package/src/dom/index.ts +42 -0
- package/src/dom/inspector.ts +35 -0
- package/src/dom/keys.ts +67 -0
- package/src/dom/ledger.ts +87 -0
- package/src/dom/legend.ts +85 -0
- package/src/dom/menu.ts +404 -0
- package/src/dom/mount.ts +731 -0
- package/src/dom/problems.ts +73 -0
- package/src/dom/shell-dom.css +969 -0
- package/src/dom/status.ts +72 -0
- package/src/dom/tooltip.ts +237 -0
- package/src/dom/types.ts +459 -0
- package/src/dom/web/drop-zone.ts +181 -0
- package/src/dom/web/empty-state.ts +144 -0
- package/src/dom/web/legal.ts +75 -0
- package/src/dom/web/menu-groups.ts +69 -0
- package/src/dom/web/primary.ts +124 -0
- package/src/dom/web/web.css +386 -0
- package/src/dom/zoom-panel.ts +154 -0
- package/src/index.ts +3 -0
- package/src/shell.css +9 -0
- package/src/testing/case-tools.ts +164 -0
- package/src/testing/cases.ts +1077 -0
- package/src/testing/checks.ts +868 -0
- package/src/testing/facts.ts +641 -0
- package/src/testing/geometry.ts +652 -0
- package/src/testing/harness.ts +187 -0
- package/src/testing/index.ts +19 -0
- package/src/testing/page-tools.ts +457 -0
- package/src/testing/page.ts +27 -0
- package/src/testing/vscode-stub.ts +237 -0
- package/src/testing/web-cases.ts +913 -0
- package/src/testing/web-checks.ts +1049 -0
- package/src/testing/web-facts.ts +713 -0
- package/src/testing-vscode-stub.ts +2 -0
- package/src/testing.ts +2 -0
- package/src/vscode/extension/html.ts +107 -0
- package/src/vscode/extension/index.ts +8 -0
- package/src/vscode/extension/reveal.ts +9 -0
- package/src/vscode/extension/theme-bridge.ts +85 -0
- package/src/vscode/extension/types.ts +36 -0
- package/src/vscode/protocol.ts +31 -0
- package/src/vscode/webview/appearance.ts +141 -0
- package/src/vscode/webview/index.ts +8 -0
- package/src/vscode/webview/mount.ts +88 -0
- package/src/vscode/webview/state.ts +116 -0
- package/src/vscode-extension.ts +2 -0
- package/src/vscode-webview.ts +2 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createElement } from "./element.js";
|
|
2
|
+
import { iconElement } from "./icons.js";
|
|
3
|
+
import type { StatusMessage } from "./types.js";
|
|
4
|
+
|
|
5
|
+
export interface StatusBox {
|
|
6
|
+
readonly element: HTMLElement;
|
|
7
|
+
set(status: StatusMessage | null, delayMs: number): void;
|
|
8
|
+
destroy(): void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createStatusBox(ownerDocument: Document): StatusBox {
|
|
12
|
+
const icon = createElement(ownerDocument, "span", { class: "cd-shell-status-icon" });
|
|
13
|
+
const text = createElement(ownerDocument, "p", { class: "cd-shell-status-text" });
|
|
14
|
+
const element = createElement(
|
|
15
|
+
ownerDocument,
|
|
16
|
+
"div",
|
|
17
|
+
{ class: "cd-shell-status", "data-chrome": "status" },
|
|
18
|
+
icon,
|
|
19
|
+
text,
|
|
20
|
+
);
|
|
21
|
+
element.hidden = true;
|
|
22
|
+
|
|
23
|
+
let pending: { status: StatusMessage; timer: ReturnType<typeof setTimeout> } | undefined;
|
|
24
|
+
|
|
25
|
+
function cancelPending(): void {
|
|
26
|
+
if (pending) clearTimeout(pending.timer);
|
|
27
|
+
pending = undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function show(status: StatusMessage): void {
|
|
31
|
+
pending = undefined;
|
|
32
|
+
if (element.dataset.severity !== status.severity) {
|
|
33
|
+
element.dataset.severity = status.severity;
|
|
34
|
+
element.setAttribute("role", status.severity === "danger" ? "alert" : "status");
|
|
35
|
+
icon.replaceChildren(iconElement(status.severity === "danger" ? "circle-x" : "triangle-alert"));
|
|
36
|
+
}
|
|
37
|
+
// An unchanged text is not written again, so role="alert" does not announce it again.
|
|
38
|
+
if (text.textContent !== status.text) text.textContent = status.text;
|
|
39
|
+
element.hidden = false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
element,
|
|
44
|
+
set(status, delayMs) {
|
|
45
|
+
if (status === null) {
|
|
46
|
+
cancelPending();
|
|
47
|
+
element.hidden = true;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!element.hidden) {
|
|
51
|
+
show(status);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Repeated invalid imports while typing keep the first timer: the box shows once the text
|
|
55
|
+
// stayed invalid for the delay, not the delay after the last keystroke (guide §5.12).
|
|
56
|
+
if (pending) {
|
|
57
|
+
pending.status = status;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (delayMs > 0) {
|
|
61
|
+
const entry = { status, timer: setTimeout(() => show(entry.status), delayMs) };
|
|
62
|
+
pending = entry;
|
|
63
|
+
} else {
|
|
64
|
+
show(status);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
destroy() {
|
|
68
|
+
cancelPending();
|
|
69
|
+
element.remove();
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { TOOLTIP_DELAY_MS, placeTooltip } from "../core/index.js";
|
|
2
|
+
import { accessibleName, boxRelativeTo, createElement, hasVisibleFocus } from "./element.js";
|
|
3
|
+
import type { TooltipContent, TooltipHandle } from "./types.js";
|
|
4
|
+
|
|
5
|
+
type ContentSource = TooltipContent | (() => TooltipContent | null);
|
|
6
|
+
|
|
7
|
+
interface Shown {
|
|
8
|
+
control: HTMLElement;
|
|
9
|
+
describedBy: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface TooltipController extends TooltipHandle {
|
|
13
|
+
readonly element: HTMLElement;
|
|
14
|
+
/** One tooltip for every `selector` match inside `container` (palette entries are rebuilt often). */
|
|
15
|
+
delegate(
|
|
16
|
+
container: HTMLElement,
|
|
17
|
+
selector: string,
|
|
18
|
+
content: (control: HTMLElement) => TooltipContent | null,
|
|
19
|
+
): () => void;
|
|
20
|
+
destroy(): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TooltipContext {
|
|
24
|
+
root: HTMLElement;
|
|
25
|
+
layer: HTMLElement;
|
|
26
|
+
id: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createTooltip({ root, layer, id }: TooltipContext): TooltipController {
|
|
30
|
+
const ownerDocument = root.ownerDocument;
|
|
31
|
+
const label = createElement(ownerDocument, "span", { class: "cd-shell-tooltip-label" });
|
|
32
|
+
const shortcut = createElement(ownerDocument, "kbd", { class: "cd-shell-kbd" });
|
|
33
|
+
const element = createElement(ownerDocument, "div", { class: "cd-shell-tooltip", role: "tooltip", id });
|
|
34
|
+
element.hidden = true;
|
|
35
|
+
element.append(label, shortcut);
|
|
36
|
+
layer.append(element);
|
|
37
|
+
|
|
38
|
+
let shown: Shown | undefined;
|
|
39
|
+
let pending: { control: HTMLElement; timer: ReturnType<typeof setTimeout> } | undefined;
|
|
40
|
+
// After a pointer leaves a control the next one shows at once, so moving along a row of
|
|
41
|
+
// buttons does not wait 300ms for every neighbour (guide §5.6).
|
|
42
|
+
let warmUntil = 0;
|
|
43
|
+
// A clicked control keeps its tooltip hidden until the pointer leaves it.
|
|
44
|
+
let pressed: HTMLElement | undefined;
|
|
45
|
+
const detachers = new Set<() => void>();
|
|
46
|
+
|
|
47
|
+
function cancelPending(): void {
|
|
48
|
+
if (pending) clearTimeout(pending.timer);
|
|
49
|
+
pending = undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function resolve(source: ContentSource): TooltipContent | null {
|
|
53
|
+
return typeof source === "function" ? source() : source;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function place(control: HTMLElement): void {
|
|
57
|
+
const island = control.closest<HTMLElement>("[data-chrome]") ?? control;
|
|
58
|
+
const point = placeTooltip({
|
|
59
|
+
anchor: boxRelativeTo(control, root),
|
|
60
|
+
island: boxRelativeTo(island, root),
|
|
61
|
+
tooltip: { width: element.offsetWidth, height: element.offsetHeight },
|
|
62
|
+
container: { width: root.clientWidth, height: root.clientHeight },
|
|
63
|
+
});
|
|
64
|
+
element.style.left = `${point.x}px`;
|
|
65
|
+
element.style.top = `${point.y}px`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function hide(): void {
|
|
69
|
+
cancelPending();
|
|
70
|
+
if (!shown) return;
|
|
71
|
+
const { control, describedBy } = shown;
|
|
72
|
+
shown = undefined;
|
|
73
|
+
element.hidden = true;
|
|
74
|
+
if (describedBy === null) control.removeAttribute("aria-describedby");
|
|
75
|
+
else control.setAttribute("aria-describedby", describedBy);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function show(control: HTMLElement, source: ContentSource): void {
|
|
79
|
+
cancelPending();
|
|
80
|
+
const content = resolve(source);
|
|
81
|
+
if (!content || !control.isConnected) {
|
|
82
|
+
if (shown?.control === control) hide();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (shown && shown.control !== control) hide();
|
|
86
|
+
label.textContent = content.label;
|
|
87
|
+
shortcut.textContent = content.shortcut ?? "";
|
|
88
|
+
shortcut.hidden = !content.shortcut;
|
|
89
|
+
element.hidden = false;
|
|
90
|
+
if (!shown) {
|
|
91
|
+
const describedBy = control.getAttribute("aria-describedby");
|
|
92
|
+
shown = { control, describedBy };
|
|
93
|
+
// Only a tooltip that says more than the control's name describes it (guide §5.6); a shortcut
|
|
94
|
+
// chip does, unless the control names its keys in aria-keyshortcuts.
|
|
95
|
+
const saysMore =
|
|
96
|
+
!accessibleName(control).includes(content.label) ||
|
|
97
|
+
(Boolean(content.shortcut) && !control.hasAttribute("aria-keyshortcuts"));
|
|
98
|
+
if (saysMore) {
|
|
99
|
+
control.setAttribute("aria-describedby", [describedBy, id].filter(Boolean).join(" "));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
place(control);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function onEnter(control: HTMLElement, source: ContentSource, event: PointerEvent): void {
|
|
106
|
+
if (event.pointerType === "touch" || pressed === control) return;
|
|
107
|
+
if (shown || Date.now() < warmUntil) {
|
|
108
|
+
show(control, source);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
cancelPending();
|
|
112
|
+
pending = { control, timer: setTimeout(() => show(control, source), TOOLTIP_DELAY_MS) };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function onLeave(control: HTMLElement): void {
|
|
116
|
+
if (pressed === control) pressed = undefined;
|
|
117
|
+
if (pending?.control === control) cancelPending();
|
|
118
|
+
if (shown?.control === control) {
|
|
119
|
+
hide();
|
|
120
|
+
warmUntil = Date.now() + TOOLTIP_DELAY_MS;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function onFocus(control: HTMLElement, source: ContentSource): void {
|
|
125
|
+
if (hasVisibleFocus(control)) show(control, source);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function onBlur(control: HTMLElement): void {
|
|
129
|
+
if (pending?.control === control) cancelPending();
|
|
130
|
+
if (shown?.control === control) hide();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function onPress(control: HTMLElement, event: Event): void {
|
|
134
|
+
if (event.type === "pointerdown") pressed = control;
|
|
135
|
+
if (pending?.control === control) cancelPending();
|
|
136
|
+
if (shown?.control === control) hide();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function attach(control: HTMLElement, source: ContentSource): () => void {
|
|
140
|
+
const enter = (event: PointerEvent) => onEnter(control, source, event);
|
|
141
|
+
const leave = () => onLeave(control);
|
|
142
|
+
const focus = () => onFocus(control, source);
|
|
143
|
+
const blur = () => onBlur(control);
|
|
144
|
+
const press = (event: Event) => onPress(control, event);
|
|
145
|
+
control.addEventListener("pointerenter", enter);
|
|
146
|
+
control.addEventListener("pointerleave", leave);
|
|
147
|
+
control.addEventListener("focus", focus);
|
|
148
|
+
control.addEventListener("blur", blur);
|
|
149
|
+
control.addEventListener("pointerdown", press);
|
|
150
|
+
control.addEventListener("click", press);
|
|
151
|
+
const detach = () => {
|
|
152
|
+
if (!detachers.delete(detach)) return;
|
|
153
|
+
control.removeEventListener("pointerenter", enter);
|
|
154
|
+
control.removeEventListener("pointerleave", leave);
|
|
155
|
+
control.removeEventListener("focus", focus);
|
|
156
|
+
control.removeEventListener("blur", blur);
|
|
157
|
+
control.removeEventListener("pointerdown", press);
|
|
158
|
+
control.removeEventListener("click", press);
|
|
159
|
+
if (shown?.control === control || pending?.control === control) hide();
|
|
160
|
+
if (pressed === control) pressed = undefined;
|
|
161
|
+
};
|
|
162
|
+
detachers.add(detach);
|
|
163
|
+
return detach;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function delegate(
|
|
167
|
+
container: HTMLElement,
|
|
168
|
+
selector: string,
|
|
169
|
+
content: (control: HTMLElement) => TooltipContent | null,
|
|
170
|
+
): () => void {
|
|
171
|
+
let hovered: HTMLElement | undefined;
|
|
172
|
+
const controlOf = (target: EventTarget | null): HTMLElement | undefined => {
|
|
173
|
+
if (!target || typeof (target as Element).closest !== "function") return undefined;
|
|
174
|
+
const match = (target as Element).closest<HTMLElement>(selector);
|
|
175
|
+
return match && container.contains(match) ? match : undefined;
|
|
176
|
+
};
|
|
177
|
+
const sourceOf = (control: HTMLElement) => () => content(control);
|
|
178
|
+
const over = (event: PointerEvent) => {
|
|
179
|
+
const control = controlOf(event.target);
|
|
180
|
+
if (control === hovered) return;
|
|
181
|
+
if (hovered) onLeave(hovered);
|
|
182
|
+
hovered = control;
|
|
183
|
+
if (control) onEnter(control, sourceOf(control), event);
|
|
184
|
+
};
|
|
185
|
+
const out = (event: PointerEvent) => {
|
|
186
|
+
if (!hovered) return;
|
|
187
|
+
if (controlOf(event.relatedTarget) === hovered) return;
|
|
188
|
+
onLeave(hovered);
|
|
189
|
+
hovered = undefined;
|
|
190
|
+
};
|
|
191
|
+
const focusIn = (event: FocusEvent) => {
|
|
192
|
+
const control = controlOf(event.target);
|
|
193
|
+
if (control) onFocus(control, sourceOf(control));
|
|
194
|
+
};
|
|
195
|
+
const focusOut = (event: FocusEvent) => {
|
|
196
|
+
const control = controlOf(event.target);
|
|
197
|
+
if (control) onBlur(control);
|
|
198
|
+
};
|
|
199
|
+
const press = (event: Event) => {
|
|
200
|
+
const control = controlOf(event.target);
|
|
201
|
+
if (control) onPress(control, event);
|
|
202
|
+
};
|
|
203
|
+
container.addEventListener("pointerover", over);
|
|
204
|
+
container.addEventListener("pointerout", out);
|
|
205
|
+
container.addEventListener("focusin", focusIn);
|
|
206
|
+
container.addEventListener("focusout", focusOut);
|
|
207
|
+
container.addEventListener("pointerdown", press);
|
|
208
|
+
container.addEventListener("click", press);
|
|
209
|
+
const detach = () => {
|
|
210
|
+
if (!detachers.delete(detach)) return;
|
|
211
|
+
container.removeEventListener("pointerover", over);
|
|
212
|
+
container.removeEventListener("pointerout", out);
|
|
213
|
+
container.removeEventListener("focusin", focusIn);
|
|
214
|
+
container.removeEventListener("focusout", focusOut);
|
|
215
|
+
container.removeEventListener("pointerdown", press);
|
|
216
|
+
container.removeEventListener("click", press);
|
|
217
|
+
if ((shown && container.contains(shown.control)) || (pending && container.contains(pending.control))) {
|
|
218
|
+
hide();
|
|
219
|
+
}
|
|
220
|
+
hovered = undefined;
|
|
221
|
+
};
|
|
222
|
+
detachers.add(detach);
|
|
223
|
+
return detach;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
element,
|
|
228
|
+
attach,
|
|
229
|
+
delegate,
|
|
230
|
+
hide,
|
|
231
|
+
destroy() {
|
|
232
|
+
hide();
|
|
233
|
+
for (const detach of [...detachers]) detach();
|
|
234
|
+
element.remove();
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|