@miragon/modeler-shell 0.1.0 → 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 +1051 -84
- package/README.md +162 -31
- package/dist/appearance-BBaM_4bD.d.ts +323 -0
- package/dist/appearance-XX3JC3pI.d.cts +323 -0
- package/dist/{chunk-6LQOFACK.js → chunk-66HCSJNY.js} +1247 -31
- package/dist/chunk-66HCSJNY.js.map +1 -0
- package/dist/{chunk-RJRFR6N3.js → chunk-KYDG5DMX.js} +2 -2
- package/dist/{chunk-5JSZ4VGA.js → chunk-OADW3GPA.js} +240 -6
- package/dist/chunk-OADW3GPA.js.map +1 -0
- package/dist/core.cjs +260 -6
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +81 -6
- package/dist/core.d.ts +81 -6
- package/dist/core.js +43 -3
- package/dist/diagram-js.cjs +34 -2
- package/dist/diagram-js.cjs.map +1 -1
- package/dist/diagram-js.d.cts +10 -3
- package/dist/diagram-js.d.ts +10 -3
- package/dist/diagram-js.js +14 -2
- package/dist/diagram-js.js.map +1 -1
- package/dist/index.cjs +1654 -197
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -9
- package/dist/index.d.ts +18 -9
- package/dist/index.js +44 -4
- package/dist/shell.css +692 -3
- package/dist/testing-vscode-stub.js +2 -2
- package/dist/testing.cjs +2849 -550
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +75 -9
- package/dist/testing.d.ts +75 -9
- package/dist/testing.js +2744 -558
- package/dist/testing.js.map +1 -1
- package/dist/toasts-Bp0REqdq.d.cts +142 -0
- package/dist/toasts-Bp0REqdq.d.ts +142 -0
- package/dist/{types-OuFlqZp0.d.cts → types-CnRrINRs.d.cts} +170 -9
- package/dist/{types-DnZn9CPw.d.ts → types-PuDZczzy.d.ts} +170 -9
- package/dist/vscode-extension.js +2 -2
- package/dist/vscode-webview.cjs +1541 -178
- package/dist/vscode-webview.cjs.map +1 -1
- package/dist/vscode-webview.d.cts +6 -4
- package/dist/vscode-webview.d.ts +6 -4
- package/dist/vscode-webview.js +11 -2
- package/dist/vscode-webview.js.map +1 -1
- package/package.json +2 -2
- 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 +18 -3
- package/src/core/index.ts +43 -0
- package/src/core/keymap.ts +20 -0
- package/src/core/messages.ts +71 -0
- package/src/core/types.ts +24 -2
- 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/diagram-js/service.ts +4 -1
- package/src/diagram-js/viewport.ts +19 -0
- package/src/dom/buttons.css +124 -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/index.ts +13 -0
- package/src/dom/keys.ts +8 -0
- package/src/dom/menu.ts +28 -17
- package/src/dom/mount.ts +290 -20
- package/src/dom/problems.ts +5 -4
- package/src/dom/shell-dom.css +7 -4
- package/src/dom/types.ts +188 -6
- 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/shell.css +3 -0
- package/src/testing/case-tools.ts +164 -0
- package/src/testing/cases.ts +209 -144
- package/src/testing/checks.ts +51 -15
- package/src/testing/facts.ts +56 -2
- package/src/testing/geometry.ts +206 -26
- package/src/testing/harness.ts +56 -7
- package/src/testing/index.ts +4 -0
- package/src/testing/page-tools.ts +24 -1
- package/src/testing/page.ts +10 -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/vscode/webview/mount.ts +13 -1
- package/dist/chunk-5JSZ4VGA.js.map +0 -1
- package/dist/chunk-6LQOFACK.js.map +0 -1
- package/dist/keymap-CObQE6q6.d.cts +0 -151
- package/dist/keymap-DORzHnK-.d.ts +0 -151
- package/dist/types-B6NfkiUG.d.cts +0 -66
- package/dist/types-B6NfkiUG.d.ts +0 -66
- /package/dist/{chunk-RJRFR6N3.js.map → chunk-KYDG5DMX.js.map} +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { parseAppearanceChoice, type ShellMessages } from "../../core/index.js";
|
|
2
|
+
import type { ShellIconName } from "../icons.js";
|
|
3
|
+
import type { AppearanceHandle, FileMenuAction, FileMenuOptions, MenuEntry } from "../types.js";
|
|
4
|
+
|
|
5
|
+
interface FileEntrySpec {
|
|
6
|
+
option: keyof FileMenuOptions;
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
icon: ShellIconName | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function fileEntry(spec: FileEntrySpec, action: FileMenuAction): MenuEntry {
|
|
13
|
+
const common = {
|
|
14
|
+
id: spec.id,
|
|
15
|
+
label: spec.label,
|
|
16
|
+
...(spec.icon ? { icon: spec.icon } : {}),
|
|
17
|
+
...(action.disabled ? { disabled: action.disabled } : {}),
|
|
18
|
+
...(action.shortcut ? { shortcut: action.shortcut } : {}),
|
|
19
|
+
onSelect: () => action.onSelect(),
|
|
20
|
+
};
|
|
21
|
+
return action.dialog ? { kind: "dialog", ...common } : { kind: "action", ...common };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The File group (guide §5.4, §2), first in the menu: "New diagram" (`file-plus`, id
|
|
26
|
+
* `new-diagram`), "Open…" (`folder-open`, `open`), "Show example" (no icon, `show-example`),
|
|
27
|
+
* "Import…" (`import`, `import`), in this order and only those `options` gives. `action` entries
|
|
28
|
+
* (`dialog` with `FileMenuAction.dialog`) with the action's `shortcut` and `disabled`. Never
|
|
29
|
+
* locked on a read-only canvas.
|
|
30
|
+
*/
|
|
31
|
+
export function fileMenuEntries(options: FileMenuOptions, messages: ShellMessages): readonly MenuEntry[] {
|
|
32
|
+
const specs: readonly FileEntrySpec[] = [
|
|
33
|
+
{ option: "newDiagram", id: "new-diagram", label: messages.newDiagram, icon: "file-plus" },
|
|
34
|
+
{ option: "open", id: "open", label: messages.openFile, icon: "folder-open" },
|
|
35
|
+
// §5.14 gives "Show example" no icon.
|
|
36
|
+
{ option: "showExample", id: "show-example", label: messages.showExample, icon: undefined },
|
|
37
|
+
{ option: "import", id: "import", label: messages.importFile, icon: "import" },
|
|
38
|
+
];
|
|
39
|
+
return specs.flatMap((spec) => {
|
|
40
|
+
const action = options[spec.option];
|
|
41
|
+
return action ? [fileEntry(spec, action)] : [];
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The Appearance group (guide §5.4, §13.3) between Export and Help: one radio group, id
|
|
47
|
+
* `appearance`, label `messages.appearance`, options `system`, `light`, `dark` labelled
|
|
48
|
+
* `messages.appearanceSystem` / `Light` / `Dark`, value `appearance.choice()`, selecting calls
|
|
49
|
+
* `appearance.setChoice`. Never locked. The shell asks for it only when `themes` includes `"dark"`.
|
|
50
|
+
*/
|
|
51
|
+
export function appearanceMenuEntries(
|
|
52
|
+
appearance: AppearanceHandle,
|
|
53
|
+
messages: ShellMessages,
|
|
54
|
+
): readonly MenuEntry[] {
|
|
55
|
+
return [
|
|
56
|
+
{
|
|
57
|
+
kind: "radio",
|
|
58
|
+
id: "appearance",
|
|
59
|
+
label: messages.appearance,
|
|
60
|
+
options: [
|
|
61
|
+
{ value: "system", label: messages.appearanceSystem },
|
|
62
|
+
{ value: "light", label: messages.appearanceLight },
|
|
63
|
+
{ value: "dark", label: messages.appearanceDark },
|
|
64
|
+
],
|
|
65
|
+
value: () => appearance.choice(),
|
|
66
|
+
onSelect: (value) => appearance.setChoice(parseAppearanceChoice(value)),
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { SHELL, type PrimaryWidths, type ShellMessages } from "../../core/index.js";
|
|
2
|
+
import { createElement, toggleAttribute } from "../element.js";
|
|
3
|
+
import { iconElement } from "../icons.js";
|
|
4
|
+
import type { TooltipController } from "../tooltip.js";
|
|
5
|
+
import type { PrimaryActionHandle, PrimaryActionOptions } from "../types.js";
|
|
6
|
+
|
|
7
|
+
export interface PrimaryActionContext {
|
|
8
|
+
ownerDocument: Document;
|
|
9
|
+
messages: ShellMessages;
|
|
10
|
+
tooltip: TooltipController;
|
|
11
|
+
options: PrimaryActionOptions;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface PrimaryActionController extends PrimaryActionHandle {
|
|
15
|
+
/**
|
|
16
|
+
* Border-box widths with and without the label for `decideTopRow`, measured also while the label
|
|
17
|
+
* is collapsed (the label's width does not change with the state), cached until `refresh()`.
|
|
18
|
+
*/
|
|
19
|
+
widths(): PrimaryWidths;
|
|
20
|
+
/** Icon only (`true`) or icon and label; the `aria-label` stays, the tooltip shows only while collapsed. */
|
|
21
|
+
setCollapsed(collapsed: boolean): void;
|
|
22
|
+
destroy(): void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** On the button while it shows its icon only: the stylesheet's 40 × 40 (the root mirrors it as `data-primary`). */
|
|
26
|
+
const COLLAPSED_ATTRIBUTE = "data-collapsed";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* `<button type="button" class="cd-shell-primary cd-shell-button" data-variant="primary"
|
|
30
|
+
* data-chrome="primary" aria-label="Share">` with the 16px icon and `<span
|
|
31
|
+
* class="cd-shell-primary-label">` (guide §5.7): 40px high at `top: 12px; right: 12px`, layer 30,
|
|
32
|
+
* blue (`dom/buttons.css`), hover lift. Collapsed: 40 × 40, label hidden, chrome tooltip with the
|
|
33
|
+
* label (no `aria-describedby`, it repeats the name). `disabled()` → `aria-disabled`, no action.
|
|
34
|
+
*/
|
|
35
|
+
export function createPrimaryAction(context: PrimaryActionContext): PrimaryActionController {
|
|
36
|
+
const { ownerDocument, messages, tooltip, options } = context;
|
|
37
|
+
const label = options.label ?? messages.share;
|
|
38
|
+
const labelElement = createElement(ownerDocument, "span", { class: "cd-shell-primary-label" }, label);
|
|
39
|
+
const element = createElement(
|
|
40
|
+
ownerDocument,
|
|
41
|
+
"button",
|
|
42
|
+
{
|
|
43
|
+
type: "button",
|
|
44
|
+
class: "cd-shell-primary cd-shell-button",
|
|
45
|
+
"data-variant": "primary",
|
|
46
|
+
"data-chrome": "primary",
|
|
47
|
+
"aria-label": label,
|
|
48
|
+
},
|
|
49
|
+
iconElement(options.icon ?? "share-2"),
|
|
50
|
+
labelElement,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
let collapsed = false;
|
|
54
|
+
let disabled = false;
|
|
55
|
+
let labelledWidth: number | undefined;
|
|
56
|
+
|
|
57
|
+
function readDisabled(): void {
|
|
58
|
+
disabled = options.disabled?.() ?? false;
|
|
59
|
+
toggleAttribute(element, "aria-disabled", disabled ? "true" : null);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function showCollapsed(state: boolean): void {
|
|
63
|
+
labelElement.hidden = state;
|
|
64
|
+
toggleAttribute(element, COLLAPSED_ATTRIBUTE, state ? "" : null);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The labelled width; while collapsed the label shows for one synchronous layout, never painted. */
|
|
68
|
+
function measureLabelledWidth(): number {
|
|
69
|
+
if (!collapsed) return element.offsetWidth;
|
|
70
|
+
showCollapsed(false);
|
|
71
|
+
const width = element.offsetWidth;
|
|
72
|
+
showCollapsed(true);
|
|
73
|
+
return width;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function isEngaged(): boolean {
|
|
77
|
+
if (ownerDocument.activeElement === element) return true;
|
|
78
|
+
try {
|
|
79
|
+
return element.matches(":hover");
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const onClick = (event: MouseEvent): void => {
|
|
86
|
+
if (disabled) {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
options.onSelect();
|
|
91
|
+
};
|
|
92
|
+
element.addEventListener("click", onClick);
|
|
93
|
+
const detachTooltip = tooltip.attach(element, () => (collapsed ? { label } : null));
|
|
94
|
+
readDisabled();
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
element,
|
|
98
|
+
isCollapsed: () => collapsed,
|
|
99
|
+
refresh() {
|
|
100
|
+
readDisabled();
|
|
101
|
+
labelledWidth = undefined;
|
|
102
|
+
},
|
|
103
|
+
widths() {
|
|
104
|
+
// Measured fresh while the label shows (a late web font widens it), from the cache while collapsed.
|
|
105
|
+
if (!collapsed || labelledWidth === undefined) {
|
|
106
|
+
const measured = measureLabelledWidth();
|
|
107
|
+
if (measured > 0) labelledWidth = measured;
|
|
108
|
+
}
|
|
109
|
+
return { withLabel: labelledWidth ?? 0, iconOnly: SHELL.row };
|
|
110
|
+
},
|
|
111
|
+
setCollapsed(next) {
|
|
112
|
+
if (next === collapsed) return;
|
|
113
|
+
collapsed = next;
|
|
114
|
+
showCollapsed(next);
|
|
115
|
+
// An expanded button names itself; a tooltip still showing for it would only repeat the label.
|
|
116
|
+
if (!next && isEngaged()) tooltip.hide();
|
|
117
|
+
},
|
|
118
|
+
destroy() {
|
|
119
|
+
detachTooltip();
|
|
120
|
+
element.removeEventListener("click", onClick);
|
|
121
|
+
element.remove();
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Web-app chrome of @miragon/modeler-shell (owned by src/dom/web): primary action, legal line,
|
|
3
|
+
* empty-state card, drop zone, the start screen hiding the work chrome and the raised zoom row
|
|
4
|
+
* (guide §5.7, §5.10, §5.15, §8, §9). Colours only from var(--cd-ui-*), sizes from
|
|
5
|
+
* var(--cd-shell-*); the primary fill comes from ../buttons.css. Every rule is scoped to
|
|
6
|
+
* .cd-shell[data-cd-shell-host="web"] or to classes only the web host renders.
|
|
7
|
+
*
|
|
8
|
+
* 20px is the legal line's height (13px text on a 20px line, guide §5.10); it has no token. Where a
|
|
9
|
+
* narrow container wraps the line, the shell measures it into --modeler-shell-legal-height on the
|
|
10
|
+
* root, and everything stacked above the line stands on that height.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/* The app's own element rules (h1 { font-size: 2em }, a { text-transform … }) stay out. */
|
|
14
|
+
:where(.cd-shell)
|
|
15
|
+
:is(
|
|
16
|
+
.cd-shell-primary,
|
|
17
|
+
.cd-shell-primary-label,
|
|
18
|
+
.cd-shell-legal,
|
|
19
|
+
.cd-shell-legal a,
|
|
20
|
+
.cd-shell-empty-state,
|
|
21
|
+
.cd-shell-empty-state-title,
|
|
22
|
+
.cd-shell-empty-state-text,
|
|
23
|
+
.cd-shell-empty-state-hint,
|
|
24
|
+
.cd-shell-empty-state-open,
|
|
25
|
+
.cd-shell-empty-state-key,
|
|
26
|
+
.cd-shell-empty-state .cd-shell-button,
|
|
27
|
+
.cd-shell-drop-zone-chip
|
|
28
|
+
) {
|
|
29
|
+
letter-spacing: normal;
|
|
30
|
+
word-spacing: normal;
|
|
31
|
+
text-transform: none;
|
|
32
|
+
text-indent: 0;
|
|
33
|
+
text-shadow: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* ---- Primary action, top right (guide §5.7, §5.15) ------------------------------------------ */
|
|
37
|
+
|
|
38
|
+
.cd-shell .cd-shell-primary {
|
|
39
|
+
position: absolute;
|
|
40
|
+
top: var(--cd-shell-rand);
|
|
41
|
+
right: var(--cd-shell-rand);
|
|
42
|
+
z-index: var(--cd-shell-ebene-reihe);
|
|
43
|
+
height: var(--cd-shell-zeile);
|
|
44
|
+
padding: 0 16px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Icon only: 40 × 40, the label gone, the aria-label unchanged. */
|
|
48
|
+
.cd-shell .cd-shell-primary[data-collapsed] {
|
|
49
|
+
width: var(--cd-shell-zeile);
|
|
50
|
+
padding: 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.cd-shell-primary-label[hidden] {
|
|
54
|
+
display: none;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* ---- Legal line, bottom left (guide §5.10, §2) ---------------------------------------------- */
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* One line where it fits; in a narrower container it wraps between its entries instead of being
|
|
61
|
+
* cut off (Datenschutz must stay reachable, WCAG 1.4.10), and grows upwards from bottom 12.
|
|
62
|
+
*/
|
|
63
|
+
.cd-shell-legal {
|
|
64
|
+
position: absolute;
|
|
65
|
+
left: var(--cd-shell-rand);
|
|
66
|
+
bottom: var(--cd-shell-rand);
|
|
67
|
+
z-index: var(--cd-shell-ebene-inseln);
|
|
68
|
+
box-sizing: border-box;
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-wrap: wrap;
|
|
71
|
+
align-items: center;
|
|
72
|
+
column-gap: 6px;
|
|
73
|
+
row-gap: 0;
|
|
74
|
+
min-height: 20px;
|
|
75
|
+
max-width: calc(100cqw - 2 * var(--cd-shell-rand));
|
|
76
|
+
margin: 0;
|
|
77
|
+
padding: 0 8px;
|
|
78
|
+
color: var(--cd-ui-text-leise);
|
|
79
|
+
font-size: 13px;
|
|
80
|
+
font-weight: 400;
|
|
81
|
+
line-height: 20px;
|
|
82
|
+
white-space: nowrap;
|
|
83
|
+
background: color-mix(in srgb, var(--cd-ui-flaeche-hoch) var(--cd-ui-chrome-deckkraft), transparent);
|
|
84
|
+
-webkit-backdrop-filter: blur(8px);
|
|
85
|
+
backdrop-filter: blur(8px);
|
|
86
|
+
border: 0;
|
|
87
|
+
border-radius: var(--cd-radius-sm);
|
|
88
|
+
box-shadow: none;
|
|
89
|
+
user-select: none;
|
|
90
|
+
-webkit-user-select: none;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.cd-shell-legal[hidden] {
|
|
94
|
+
display: none;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.cd-shell-legal > * {
|
|
98
|
+
flex: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* A separator and the link after it: a wrap never leaves a separator alone at a line's end. */
|
|
102
|
+
.cd-shell-legal-item {
|
|
103
|
+
display: inline-flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
gap: 6px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.cd-shell .cd-shell-legal a,
|
|
109
|
+
.cd-shell .cd-shell-legal a:visited {
|
|
110
|
+
color: var(--cd-ui-link);
|
|
111
|
+
font: inherit;
|
|
112
|
+
text-decoration: none;
|
|
113
|
+
border-radius: 2px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.cd-shell .cd-shell-legal a:hover {
|
|
117
|
+
color: var(--cd-ui-link);
|
|
118
|
+
text-decoration: underline;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.cd-shell .cd-shell-legal a:focus {
|
|
122
|
+
outline: none;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.cd-shell .cd-shell-legal a:focus-visible {
|
|
126
|
+
text-decoration: underline;
|
|
127
|
+
outline: 2px solid var(--cd-ui-fokus);
|
|
128
|
+
outline-offset: 2px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* ---- Empty state, centre (guide §8) --------------------------------------------------------- */
|
|
132
|
+
|
|
133
|
+
.cd-shell-empty-state {
|
|
134
|
+
position: absolute;
|
|
135
|
+
top: 50%;
|
|
136
|
+
left: 50%;
|
|
137
|
+
translate: -50% -50%;
|
|
138
|
+
z-index: var(--cd-shell-ebene-inseln);
|
|
139
|
+
box-sizing: border-box;
|
|
140
|
+
display: flex;
|
|
141
|
+
flex-direction: column;
|
|
142
|
+
align-items: center;
|
|
143
|
+
gap: 16px;
|
|
144
|
+
width: min(400px, 100cqw - 2 * var(--cd-shell-rand));
|
|
145
|
+
max-height: calc(100cqh - 2 * var(--cd-shell-rand));
|
|
146
|
+
overflow-y: auto;
|
|
147
|
+
overscroll-behavior: contain;
|
|
148
|
+
margin: 0;
|
|
149
|
+
padding: 32px 40px;
|
|
150
|
+
color: var(--cd-ui-text);
|
|
151
|
+
font-size: 13px;
|
|
152
|
+
line-height: 1.5;
|
|
153
|
+
text-align: center;
|
|
154
|
+
background: color-mix(in srgb, var(--cd-ui-flaeche-hoch) var(--cd-ui-chrome-deckkraft), transparent);
|
|
155
|
+
-webkit-backdrop-filter: blur(8px);
|
|
156
|
+
backdrop-filter: blur(8px);
|
|
157
|
+
border: 1px solid var(--cd-ui-linie);
|
|
158
|
+
border-radius: var(--cd-radius-lg);
|
|
159
|
+
box-shadow: var(--cd-shadow-2);
|
|
160
|
+
user-select: none;
|
|
161
|
+
-webkit-user-select: none;
|
|
162
|
+
animation: cd-shell-rise-in var(--cd-motion-base) var(--cd-ease-out);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.cd-shell-empty-state[hidden] {
|
|
166
|
+
display: none;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.cd-shell-empty-state-mark {
|
|
170
|
+
display: block;
|
|
171
|
+
flex: none;
|
|
172
|
+
width: 64px;
|
|
173
|
+
height: 64px;
|
|
174
|
+
margin: 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.cd-shell .cd-shell-empty-state-title {
|
|
178
|
+
margin: 0;
|
|
179
|
+
color: var(--cd-ui-text);
|
|
180
|
+
font-size: 21px;
|
|
181
|
+
font-weight: 600;
|
|
182
|
+
line-height: 1.3;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.cd-shell .cd-shell-empty-state-text {
|
|
186
|
+
margin: 0;
|
|
187
|
+
color: var(--cd-ui-text-leise);
|
|
188
|
+
font-size: 16px;
|
|
189
|
+
font-weight: 400;
|
|
190
|
+
line-height: 1.6;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.cd-shell-empty-state-actions {
|
|
194
|
+
display: flex;
|
|
195
|
+
flex-wrap: wrap;
|
|
196
|
+
justify-content: center;
|
|
197
|
+
gap: var(--cd-shell-luecke);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.cd-shell .cd-shell-empty-state-hint {
|
|
201
|
+
margin: 0;
|
|
202
|
+
color: var(--cd-ui-text-leise);
|
|
203
|
+
font-size: 13px;
|
|
204
|
+
font-weight: 400;
|
|
205
|
+
line-height: 1.5;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* The File group's "Open…" as a text link in the hint: the keyboard's way to a file (guide §8). */
|
|
209
|
+
.cd-shell .cd-shell-empty-state-open {
|
|
210
|
+
display: inline;
|
|
211
|
+
margin: 0;
|
|
212
|
+
padding: 0;
|
|
213
|
+
color: var(--cd-ui-link);
|
|
214
|
+
font: inherit;
|
|
215
|
+
text-decoration: none;
|
|
216
|
+
background: transparent;
|
|
217
|
+
border: 0;
|
|
218
|
+
border-radius: 2px;
|
|
219
|
+
cursor: pointer;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.cd-shell .cd-shell-empty-state-open:hover {
|
|
223
|
+
text-decoration: underline;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.cd-shell .cd-shell-empty-state-open:focus {
|
|
227
|
+
outline: none;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.cd-shell .cd-shell-empty-state-open:focus-visible {
|
|
231
|
+
text-decoration: underline;
|
|
232
|
+
outline: 2px solid var(--cd-ui-fokus);
|
|
233
|
+
outline-offset: 2px;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.cd-shell-empty-state-open-item {
|
|
237
|
+
white-space: nowrap;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.cd-shell .cd-shell-empty-state-key {
|
|
241
|
+
color: var(--cd-ui-text-leise);
|
|
242
|
+
font-family: var(--modeler-shell-font-mono);
|
|
243
|
+
font-size: 13px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* ---- Start screen: only the card and the legal line compete (guide §8) ---------------------- */
|
|
247
|
+
|
|
248
|
+
.cd-shell[data-cd-shell-host="web"][data-empty-state="shown"]
|
|
249
|
+
:is(
|
|
250
|
+
.cd-shell-menu-trigger,
|
|
251
|
+
.cd-shell-primary,
|
|
252
|
+
.djs-palette,
|
|
253
|
+
.cd-shell-zoom,
|
|
254
|
+
.cd-shell-problems,
|
|
255
|
+
.cd-shell-legend,
|
|
256
|
+
.cd-shell-status,
|
|
257
|
+
.cd-shell-hint,
|
|
258
|
+
.cd-shell-inspector
|
|
259
|
+
) {
|
|
260
|
+
visibility: hidden;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* ---- Bottom row: the zoom panel above the legal line where they meet (shell rule) ----------- */
|
|
264
|
+
|
|
265
|
+
.cd-shell[data-cd-shell-host="web"][data-zoom-row="raised"] .cd-shell-zoom {
|
|
266
|
+
bottom: calc(var(--cd-shell-rand) + var(--modeler-shell-legal-height, 20px) + var(--cd-shell-luecke));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* Up to 440px the legend stands above the raised zoom panel too: 40 + 40 + 8 = 88. */
|
|
270
|
+
@container cd-shell (max-width: 440px) {
|
|
271
|
+
.cd-shell[data-cd-shell-host="web"][data-zoom-row="raised"] .cd-shell-legend {
|
|
272
|
+
--modeler-shell-legend-min-bottom: calc(
|
|
273
|
+
var(--cd-shell-rand) + var(--modeler-shell-legal-height, 20px) + var(--cd-shell-luecke) +
|
|
274
|
+
var(--cd-shell-zeile) + var(--cd-shell-luecke)
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/* ---- Drop zone, whole page (guide §9 Drag-over, Drop forbidden) ----------------------------- */
|
|
280
|
+
|
|
281
|
+
.cd-shell-drop-zone {
|
|
282
|
+
position: absolute;
|
|
283
|
+
inset: var(--cd-shell-rand);
|
|
284
|
+
box-sizing: border-box;
|
|
285
|
+
display: grid;
|
|
286
|
+
place-items: center;
|
|
287
|
+
margin: 0;
|
|
288
|
+
padding: var(--cd-shell-rand);
|
|
289
|
+
background: var(--cd-ui-hover);
|
|
290
|
+
border: 2px dashed var(--cd-ui-interaktion);
|
|
291
|
+
border-radius: var(--cd-radius-lg);
|
|
292
|
+
/* Fills the 12px around it, so no island corner shows beside the rounded frame. */
|
|
293
|
+
box-shadow: 0 0 0 var(--cd-shell-rand) var(--cd-ui-grund);
|
|
294
|
+
pointer-events: none;
|
|
295
|
+
user-select: none;
|
|
296
|
+
-webkit-user-select: none;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.cd-shell-drop-zone[hidden] {
|
|
300
|
+
display: none;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.cd-shell-drop-zone[data-state="forbidden"] {
|
|
304
|
+
background: var(--cd-ui-danger-flaeche);
|
|
305
|
+
border-color: var(--cd-ui-danger);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.cd-shell-drop-zone-chip {
|
|
309
|
+
box-sizing: border-box;
|
|
310
|
+
max-width: 100%;
|
|
311
|
+
padding: 7px 16px;
|
|
312
|
+
color: var(--cd-ui-text);
|
|
313
|
+
font-size: 13px;
|
|
314
|
+
font-weight: 600;
|
|
315
|
+
line-height: 20px;
|
|
316
|
+
text-align: center;
|
|
317
|
+
background: var(--cd-ui-flaeche-hoch);
|
|
318
|
+
border: 1px solid var(--cd-ui-linie);
|
|
319
|
+
border-radius: var(--cd-radius-md);
|
|
320
|
+
box-shadow: var(--cd-shadow-2);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* ---- Motion (guide §5.14, §7): no rise under reduced motion (the buttons' lift: ../buttons.css) */
|
|
324
|
+
|
|
325
|
+
@media (prefers-reduced-motion: reduce) {
|
|
326
|
+
.cd-shell .cd-shell-empty-state {
|
|
327
|
+
animation: none;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
html.cd-shell-reduce-motion .cd-shell .cd-shell-empty-state {
|
|
332
|
+
animation: none;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* ---- High contrast (guide §5.3): opaque glass through the role, no shadows, 3px focus -------- */
|
|
336
|
+
|
|
337
|
+
.cd-shell:is([data-cd-kontrast="hoch"], [data-cd-kontrast="hoch"] *)
|
|
338
|
+
:is(.cd-shell-empty-state, .cd-shell-drop-zone-chip) {
|
|
339
|
+
box-shadow: none;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.cd-shell:is([data-cd-kontrast="hoch"], [data-cd-kontrast="hoch"] *)
|
|
343
|
+
:is(.cd-shell-legal a, .cd-shell-empty-state-open) {
|
|
344
|
+
text-decoration: underline;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.cd-shell:is([data-cd-kontrast="hoch"], [data-cd-kontrast="hoch"] *)
|
|
348
|
+
:is(.cd-shell-legal a, .cd-shell-empty-state-open):focus-visible {
|
|
349
|
+
outline-width: 3px;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* ---- Forced colours (guide §5.14, §12): nothing disappears ---------------------------------- */
|
|
353
|
+
|
|
354
|
+
@media (forced-colors: active) {
|
|
355
|
+
.cd-shell .cd-shell-empty-state,
|
|
356
|
+
.cd-shell .cd-shell-drop-zone-chip {
|
|
357
|
+
border-color: CanvasText;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.cd-shell :is(.cd-shell-legal a, .cd-shell-empty-state-open) {
|
|
361
|
+
text-decoration: underline;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.cd-shell :is(.cd-shell-legal a, .cd-shell-empty-state-open):focus-visible {
|
|
365
|
+
outline-color: Highlight;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* Over and forbidden keep their own dashed frame; the chip's words name the state as well. */
|
|
369
|
+
.cd-shell .cd-shell-drop-zone {
|
|
370
|
+
forced-color-adjust: none;
|
|
371
|
+
background: Canvas;
|
|
372
|
+
border-color: Highlight;
|
|
373
|
+
box-shadow: 0 0 0 var(--cd-shell-rand) Canvas;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.cd-shell .cd-shell-drop-zone[data-state="forbidden"] {
|
|
377
|
+
border-color: CanvasText;
|
|
378
|
+
border-style: dotted;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.cd-shell .cd-shell-drop-zone-chip {
|
|
382
|
+
color: CanvasText;
|
|
383
|
+
background: Canvas;
|
|
384
|
+
box-shadow: none;
|
|
385
|
+
}
|
|
386
|
+
}
|
package/src/shell.css
CHANGED
|
@@ -3,4 +3,7 @@
|
|
|
3
3
|
* diagram-js.css (and the renderer's CSS), so the palette skin wins regardless of its specificity.
|
|
4
4
|
*/
|
|
5
5
|
@import "./dom/shell-dom.css";
|
|
6
|
+
@import "./dom/buttons.css";
|
|
7
|
+
@import "./dom/web/web.css";
|
|
8
|
+
@import "./dom/feedback/feedback.css";
|
|
6
9
|
@import "./diagram-js/palette.css";
|