@design.estate/wcctools 6.0.0 → 6.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/changelog.md +33 -0
- package/dist_shell/bundle.js +405 -368
- package/dist_shell/bundle.js.map +1 -1
- package/dist_shell/bundle.js.third-party-notices.json +52 -0
- package/dist_shell/bundle.js.third-party-notices.txt +107 -1
- package/dist_shell/index.html +1 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.bundlestatus.d.ts +36 -0
- package/dist_ts/classes.bundlestatus.js +110 -0
- package/dist_ts/classes.devapi.d.ts +3 -0
- package/dist_ts/classes.devapi.js +8 -1
- package/dist_ts/classes.devserver.d.ts +45 -10
- package/dist_ts/classes.devserver.js +123 -49
- package/dist_ts/cli.js +12 -3
- package/dist_ts/index.d.ts +0 -2
- package/dist_ts/index.js +1 -3
- package/dist_ts/plugins.d.ts +7 -2
- package/dist_ts/plugins.js +9 -3
- package/dist_ts/standards/baselines.d.ts +27 -0
- package/dist_ts/standards/baselines.js +83 -0
- package/dist_ts/standards/checker.d.ts +21 -0
- package/dist_ts/standards/checker.js +88 -0
- package/dist_ts/standards/classes.gitrepo.d.ts +13 -0
- package/dist_ts/standards/classes.gitrepo.js +43 -0
- package/dist_ts/standards/classes.sourceedits.d.ts +15 -0
- package/dist_ts/standards/classes.sourceedits.js +54 -0
- package/dist_ts/standards/classes.standardfixer.d.ts +40 -0
- package/dist_ts/standards/classes.standardfixer.js +210 -0
- package/dist_ts/standards/classes.standardproject.d.ts +80 -0
- package/dist_ts/standards/classes.standardproject.js +195 -0
- package/dist_ts/standards/classes.standardtree.d.ts +56 -0
- package/dist_ts/standards/classes.standardtree.js +207 -0
- package/dist_ts/standards/cli.d.ts +7 -0
- package/dist_ts/standards/cli.js +89 -0
- package/dist_ts/standards/config.d.ts +33 -0
- package/dist_ts/standards/config.js +139 -0
- package/dist_ts/standards/errors.d.ts +9 -0
- package/dist_ts/standards/errors.js +17 -0
- package/dist_ts/standards/helpers.ast.d.ts +56 -0
- package/dist_ts/standards/helpers.ast.js +172 -0
- package/dist_ts/standards/helpers.paths.d.ts +36 -0
- package/dist_ts/standards/helpers.paths.js +86 -0
- package/dist_ts/standards/index.d.ts +14 -0
- package/dist_ts/standards/index.js +15 -0
- package/dist_ts/standards/output.d.ts +7 -0
- package/dist_ts/standards/output.js +57 -0
- package/dist_ts/standards/ratchet.d.ts +15 -0
- package/dist_ts/standards/ratchet.js +45 -0
- package/dist_ts/standards/rule.d.ts +50 -0
- package/dist_ts/standards/rule.js +9 -0
- package/dist_ts/standards/rules/barrel.element-index.d.ts +7 -0
- package/dist_ts/standards/rules/barrel.element-index.js +108 -0
- package/dist_ts/standards/rules/barrel.no-demo-export.d.ts +3 -0
- package/dist_ts/standards/rules/barrel.no-demo-export.js +29 -0
- package/dist_ts/standards/rules/demo.fixture-not-exported.d.ts +3 -0
- package/dist_ts/standards/rules/demo.fixture-not-exported.js +29 -0
- package/dist_ts/standards/rules/demo.groups.d.ts +6 -0
- package/dist_ts/standards/rules/demo.groups.js +72 -0
- package/dist_ts/standards/rules/demo.required.d.ts +3 -0
- package/dist_ts/standards/rules/demo.required.js +48 -0
- package/dist_ts/standards/rules/demo.separate-file.d.ts +3 -0
- package/dist_ts/standards/rules/demo.separate-file.js +38 -0
- package/dist_ts/standards/rules/demo.shape.d.ts +3 -0
- package/dist_ts/standards/rules/demo.shape.js +38 -0
- package/dist_ts/standards/rules/docs.component-index.d.ts +8 -0
- package/dist_ts/standards/rules/docs.component-index.js +84 -0
- package/dist_ts/standards/rules/element.exported.d.ts +3 -0
- package/dist_ts/standards/rules/element.exported.js +33 -0
- package/dist_ts/standards/rules/element.literal-tag.d.ts +3 -0
- package/dist_ts/standards/rules/element.literal-tag.js +30 -0
- package/dist_ts/standards/rules/element.reachable.d.ts +3 -0
- package/dist_ts/standards/rules/element.reachable.js +48 -0
- package/dist_ts/standards/rules/element.tag-map.d.ts +3 -0
- package/dist_ts/standards/rules/element.tag-map.js +83 -0
- package/dist_ts/standards/rules/i18n.literals.d.ts +3 -0
- package/dist_ts/standards/rules/i18n.literals.js +52 -0
- package/dist_ts/standards/rules/index.d.ts +12 -0
- package/dist_ts/standards/rules/index.js +83 -0
- package/dist_ts/standards/rules/layout.element-folder.d.ts +3 -0
- package/dist_ts/standards/rules/layout.element-folder.js +47 -0
- package/dist_ts/standards/rules/layout.entry-file-name.d.ts +3 -0
- package/dist_ts/standards/rules/layout.entry-file-name.js +71 -0
- package/dist_ts/standards/rules/layout.entry.d.ts +3 -0
- package/dist_ts/standards/rules/layout.entry.js +38 -0
- package/dist_ts/standards/rules/layout.group-folder.d.ts +3 -0
- package/dist_ts/standards/rules/layout.group-folder.js +22 -0
- package/dist_ts/standards/rules/layout.shared-00.d.ts +3 -0
- package/dist_ts/standards/rules/layout.shared-00.js +36 -0
- package/dist_ts/standards/rules/naming.class-matches-tag.d.ts +3 -0
- package/dist_ts/standards/rules/naming.class-matches-tag.js +26 -0
- package/dist_ts/standards/rules/naming.lowercase-files.d.ts +3 -0
- package/dist_ts/standards/rules/naming.lowercase-files.js +26 -0
- package/dist_ts/standards/rules/naming.tag-prefix.d.ts +3 -0
- package/dist_ts/standards/rules/naming.tag-prefix.js +23 -0
- package/dist_ts/standards/rules/props.accessor.d.ts +3 -0
- package/dist_ts/standards/rules/props.accessor.js +35 -0
- package/dist_ts/standards/rules/props.object-no-attribute.d.ts +3 -0
- package/dist_ts/standards/rules/props.object-no-attribute.js +42 -0
- package/dist_ts/standards/rules/props.reflect-primitive.d.ts +5 -0
- package/dist_ts/standards/rules/props.reflect-primitive.js +41 -0
- package/dist_ts/standards/rules/props.typed.d.ts +6 -0
- package/dist_ts/standards/rules/props.typed.js +64 -0
- package/dist_ts/standards/rules/shared.d.ts +38 -0
- package/dist_ts/standards/rules/shared.js +103 -0
- package/dist_ts/standards/rules/style.bdtheme.d.ts +5 -0
- package/dist_ts/standards/rules/style.bdtheme.js +28 -0
- package/dist_ts_interfaces/index.d.ts +1 -0
- package/dist_ts_interfaces/index.js +2 -1
- package/dist_ts_interfaces/requests.d.ts +35 -0
- package/dist_ts_interfaces/standard.d.ts +93 -0
- package/dist_ts_interfaces/standard.js +6 -0
- package/dist_ts_shell/bundlestatus.d.ts +15 -0
- package/dist_ts_shell/bundlestatus.js +64 -0
- package/dist_ts_shell/elements/wcc-contextmenu.d.ts +3 -0
- package/dist_ts_shell/elements/wcc-contextmenu.js +18 -3
- package/dist_ts_shell/elements/wcc-preview-frame.d.ts +6 -0
- package/dist_ts_shell/elements/wcc-preview-frame.js +50 -2
- package/dist_ts_shell/elements/wcc-shell.d.ts +15 -0
- package/dist_ts_shell/elements/wcc-shell.js +45 -3
- package/dist_ts_shell/plugins.d.ts +2 -1
- package/dist_ts_shell/plugins.js +3 -2
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/package.json +8 -3
- package/readme.md +40 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.bundlestatus.ts +123 -0
- package/ts/classes.devapi.ts +9 -0
- package/ts/classes.devserver.ts +127 -47
- package/ts/cli.ts +12 -2
- package/ts/index.ts +0 -2
- package/ts/plugins.ts +10 -2
- package/ts/standards/baselines.ts +100 -0
- package/ts/standards/checker.ts +114 -0
- package/ts/standards/classes.gitrepo.ts +48 -0
- package/ts/standards/classes.sourceedits.ts +58 -0
- package/ts/standards/classes.standardfixer.ts +234 -0
- package/ts/standards/classes.standardproject.ts +250 -0
- package/ts/standards/classes.standardtree.ts +220 -0
- package/ts/standards/cli.ts +86 -0
- package/ts/standards/config.ts +160 -0
- package/ts/standards/errors.ts +18 -0
- package/ts/standards/helpers.ast.ts +208 -0
- package/ts/standards/helpers.paths.ts +119 -0
- package/ts/standards/index.ts +22 -0
- package/ts/standards/output.ts +65 -0
- package/ts/standards/ratchet.ts +63 -0
- package/ts/standards/rule.ts +58 -0
- package/ts/standards/rules/barrel.element-index.ts +112 -0
- package/ts/standards/rules/barrel.no-demo-export.ts +30 -0
- package/ts/standards/rules/demo.fixture-not-exported.ts +30 -0
- package/ts/standards/rules/demo.groups.ts +74 -0
- package/ts/standards/rules/demo.required.ts +49 -0
- package/ts/standards/rules/demo.separate-file.ts +40 -0
- package/ts/standards/rules/demo.shape.ts +42 -0
- package/ts/standards/rules/docs.component-index.ts +88 -0
- package/ts/standards/rules/element.exported.ts +35 -0
- package/ts/standards/rules/element.literal-tag.ts +30 -0
- package/ts/standards/rules/element.reachable.ts +49 -0
- package/ts/standards/rules/element.tag-map.ts +87 -0
- package/ts/standards/rules/i18n.literals.ts +58 -0
- package/ts/standards/rules/index.ts +87 -0
- package/ts/standards/rules/layout.element-folder.ts +49 -0
- package/ts/standards/rules/layout.entry-file-name.ts +73 -0
- package/ts/standards/rules/layout.entry.ts +40 -0
- package/ts/standards/rules/layout.group-folder.ts +23 -0
- package/ts/standards/rules/layout.shared-00.ts +37 -0
- package/ts/standards/rules/naming.class-matches-tag.ts +27 -0
- package/ts/standards/rules/naming.lowercase-files.ts +27 -0
- package/ts/standards/rules/naming.tag-prefix.ts +24 -0
- package/ts/standards/rules/props.accessor.ts +37 -0
- package/ts/standards/rules/props.object-no-attribute.ts +45 -0
- package/ts/standards/rules/props.reflect-primitive.ts +44 -0
- package/ts/standards/rules/props.typed.ts +70 -0
- package/ts/standards/rules/shared.ts +127 -0
- package/ts/standards/rules/style.bdtheme.ts +31 -0
- package/ts_interfaces/bridge.ts +95 -0
- package/ts_interfaces/catalog.ts +37 -0
- package/ts_interfaces/index.ts +4 -0
- package/ts_interfaces/plugins.ts +3 -0
- package/ts_interfaces/requests.ts +65 -0
- package/ts_interfaces/standard.ts +103 -0
- package/ts_shared/index.ts +1 -0
- package/ts_shared/plugins.ts +3 -0
- package/ts_shared/previewroute.ts +58 -0
- package/ts_shell/bundlestatus.ts +68 -0
- package/ts_shell/elements/wcc-contextmenu.ts +306 -0
- package/ts_shell/elements/wcc-preview-frame.ts +167 -0
- package/ts_shell/elements/wcc-properties.ts +998 -0
- package/ts_shell/elements/wcc-record-button.ts +108 -0
- package/ts_shell/elements/wcc-recording-panel.ts +1005 -0
- package/ts_shell/elements/wcc-shell.ts +485 -0
- package/ts_shell/elements/wcc-sidebar.ts +1419 -0
- package/ts_shell/index.html +21 -0
- package/ts_shell/index.ts +4 -0
- package/ts_shell/plugins.ts +12 -0
- package/ts_shell/previewconnection.ts +103 -0
- package/ts_shell/services/recorder.service.ts +451 -0
- package/ts_shell/types/dom-mediacapture-stub/index.d.ts +12 -0
- package/ts_shell/types/dom-mediacapture-stub/package.json +6 -0
- package/ts_shell/types/dom-webcodecs-stub/index.d.ts +2 -0
- package/ts_shell/types/dom-webcodecs-stub/package.json +6 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { DeesElement, property, html, customElement, type TemplateResult, state, css, cssManager } from '@design.estate/dees-element';
|
|
2
|
+
|
|
3
|
+
export interface IContextMenuItem {
|
|
4
|
+
name: string;
|
|
5
|
+
iconName?: string;
|
|
6
|
+
action: () => void | Promise<void>;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface IContextMenuOptions {
|
|
11
|
+
/** The element the menu belongs to: focus returns to it when the menu closes */
|
|
12
|
+
ownerElement?: HTMLElement;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@customElement('wcc-contextmenu')
|
|
16
|
+
export class WccContextmenu extends DeesElement {
|
|
17
|
+
/**
|
|
18
|
+
* Show a context menu at the event's position. The menu takes focus (its first item),
|
|
19
|
+
* is operated with the arrow keys, Home/End, Enter/Space and Escape/Tab, and returns
|
|
20
|
+
* focus to `ownerElement` when it closes from the keyboard or after an action.
|
|
21
|
+
*/
|
|
22
|
+
public static async show(
|
|
23
|
+
event: MouseEvent,
|
|
24
|
+
menuItems: IContextMenuItem[],
|
|
25
|
+
options: IContextMenuOptions = {}
|
|
26
|
+
): Promise<void> {
|
|
27
|
+
event.preventDefault();
|
|
28
|
+
event.stopPropagation();
|
|
29
|
+
|
|
30
|
+
// Remove any existing context menu
|
|
31
|
+
const existing = document.querySelector('wcc-contextmenu');
|
|
32
|
+
if (existing) {
|
|
33
|
+
existing.remove();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const menu = new WccContextmenu();
|
|
37
|
+
menu.menuItems = menuItems;
|
|
38
|
+
menu.x = event.clientX;
|
|
39
|
+
menu.y = event.clientY;
|
|
40
|
+
menu.ownerElement = options.ownerElement ?? null;
|
|
41
|
+
|
|
42
|
+
document.body.appendChild(menu);
|
|
43
|
+
|
|
44
|
+
// Wait for render then adjust position if needed
|
|
45
|
+
await menu.updateComplete;
|
|
46
|
+
menu.adjustPosition();
|
|
47
|
+
menu.focusItem(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Focus returns here when the menu closes from the keyboard or after an action */
|
|
51
|
+
private ownerElement: HTMLElement | null = null;
|
|
52
|
+
private closing = false;
|
|
53
|
+
|
|
54
|
+
@property({ type: Array })
|
|
55
|
+
accessor menuItems: IContextMenuItem[] = [];
|
|
56
|
+
|
|
57
|
+
@property({ type: Number })
|
|
58
|
+
accessor x: number = 0;
|
|
59
|
+
|
|
60
|
+
@property({ type: Number })
|
|
61
|
+
accessor y: number = 0;
|
|
62
|
+
|
|
63
|
+
@state()
|
|
64
|
+
accessor visible: boolean = false;
|
|
65
|
+
|
|
66
|
+
private boundHandleOutsideClick = this.handleOutsideClick.bind(this);
|
|
67
|
+
private boundHandleKeydown = this.handleKeydown.bind(this);
|
|
68
|
+
/** The pending frame that adds the document listeners; cancelled on removal */
|
|
69
|
+
private listenerFrame: number | null = null;
|
|
70
|
+
|
|
71
|
+
public static styles = [
|
|
72
|
+
css`
|
|
73
|
+
:host {
|
|
74
|
+
position: fixed;
|
|
75
|
+
z-index: 10000;
|
|
76
|
+
opacity: 0;
|
|
77
|
+
transform: scale(0.95) translateY(-5px);
|
|
78
|
+
transition: opacity 0.15s ease, transform 0.15s ease;
|
|
79
|
+
pointer-events: none;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
:host(.visible) {
|
|
83
|
+
opacity: 1;
|
|
84
|
+
transform: scale(1) translateY(0);
|
|
85
|
+
pointer-events: auto;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.menu {
|
|
89
|
+
min-width: 160px;
|
|
90
|
+
background: #0f0f0f;
|
|
91
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
92
|
+
border-radius: 6px;
|
|
93
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
94
|
+
padding: 4px 0;
|
|
95
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
|
96
|
+
font-size: 12px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.menu-item {
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
gap: 8px;
|
|
103
|
+
padding: 8px 12px;
|
|
104
|
+
color: #ccc;
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
transition: background 0.1s ease;
|
|
107
|
+
user-select: none;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.menu {
|
|
111
|
+
outline: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.menu-item:hover,
|
|
115
|
+
.menu-item:focus {
|
|
116
|
+
background: rgba(59, 130, 246, 0.15);
|
|
117
|
+
color: #fff;
|
|
118
|
+
outline: none;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.menu-item:focus-visible {
|
|
122
|
+
box-shadow: inset 0 0 0 2px #3b82f6;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.menu-item.disabled {
|
|
126
|
+
opacity: 0.4;
|
|
127
|
+
cursor: not-allowed;
|
|
128
|
+
pointer-events: none;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.menu-item .icon {
|
|
132
|
+
font-family: 'Material Symbols Outlined';
|
|
133
|
+
font-size: 16px;
|
|
134
|
+
font-weight: normal;
|
|
135
|
+
font-style: normal;
|
|
136
|
+
line-height: 1;
|
|
137
|
+
letter-spacing: normal;
|
|
138
|
+
text-transform: none;
|
|
139
|
+
white-space: nowrap;
|
|
140
|
+
word-wrap: normal;
|
|
141
|
+
direction: ltr;
|
|
142
|
+
font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 24;
|
|
143
|
+
opacity: 0.7;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.menu-item:hover .icon {
|
|
147
|
+
opacity: 1;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.menu-item .label {
|
|
151
|
+
flex: 1;
|
|
152
|
+
}
|
|
153
|
+
`
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
public render(): TemplateResult {
|
|
157
|
+
return html`
|
|
158
|
+
<div class="menu" role="menu" tabindex="-1" @keydown=${this.handleMenuKeydown}>
|
|
159
|
+
${this.menuItems.map(item => html`
|
|
160
|
+
<div
|
|
161
|
+
class="menu-item ${item.disabled ? 'disabled' : ''}"
|
|
162
|
+
role="menuitem"
|
|
163
|
+
tabindex="-1"
|
|
164
|
+
aria-disabled=${item.disabled ? 'true' : 'false'}
|
|
165
|
+
@click=${() => this.handleItemClick(item)}
|
|
166
|
+
>
|
|
167
|
+
${item.iconName ? html`<span class="icon" aria-hidden="true">${item.iconName}</span>` : null}
|
|
168
|
+
<span class="label">${item.name}</span>
|
|
169
|
+
</div>
|
|
170
|
+
`)}
|
|
171
|
+
</div>
|
|
172
|
+
`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async connectedCallback() {
|
|
176
|
+
await super.connectedCallback();
|
|
177
|
+
// Removed again while connecting: there is nothing to listen for
|
|
178
|
+
if (!this.isConnected) return;
|
|
179
|
+
// Delay adding listeners to avoid immediate close
|
|
180
|
+
this.cancelListenerFrame();
|
|
181
|
+
this.listenerFrame = requestAnimationFrame(() => {
|
|
182
|
+
this.listenerFrame = null;
|
|
183
|
+
document.addEventListener('click', this.boundHandleOutsideClick);
|
|
184
|
+
document.addEventListener('contextmenu', this.boundHandleOutsideClick);
|
|
185
|
+
document.addEventListener('keydown', this.boundHandleKeydown);
|
|
186
|
+
this.classList.add('visible');
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async disconnectedCallback() {
|
|
191
|
+
// A frame still pending would add the listeners after removal
|
|
192
|
+
this.cancelListenerFrame();
|
|
193
|
+
document.removeEventListener('click', this.boundHandleOutsideClick);
|
|
194
|
+
document.removeEventListener('contextmenu', this.boundHandleOutsideClick);
|
|
195
|
+
document.removeEventListener('keydown', this.boundHandleKeydown);
|
|
196
|
+
await super.disconnectedCallback();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private cancelListenerFrame() {
|
|
200
|
+
if (this.listenerFrame !== null) {
|
|
201
|
+
cancelAnimationFrame(this.listenerFrame);
|
|
202
|
+
this.listenerFrame = null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private adjustPosition() {
|
|
207
|
+
const rect = this.getBoundingClientRect();
|
|
208
|
+
const windowWidth = window.innerWidth;
|
|
209
|
+
const windowHeight = window.innerHeight;
|
|
210
|
+
|
|
211
|
+
let x = this.x;
|
|
212
|
+
let y = this.y;
|
|
213
|
+
|
|
214
|
+
// Adjust if menu goes off right edge
|
|
215
|
+
if (x + rect.width > windowWidth - 10) {
|
|
216
|
+
x = windowWidth - rect.width - 10;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Adjust if menu goes off bottom edge
|
|
220
|
+
if (y + rect.height > windowHeight - 10) {
|
|
221
|
+
y = windowHeight - rect.height - 10;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Ensure not off left or top
|
|
225
|
+
if (x < 10) x = 10;
|
|
226
|
+
if (y < 10) y = 10;
|
|
227
|
+
|
|
228
|
+
this.style.left = `${x}px`;
|
|
229
|
+
this.style.top = `${y}px`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private handleOutsideClick(e: Event) {
|
|
233
|
+
const path = e.composedPath();
|
|
234
|
+
if (!path.includes(this)) {
|
|
235
|
+
this.close();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private handleKeydown(e: KeyboardEvent) {
|
|
240
|
+
if (e.key === 'Escape') {
|
|
241
|
+
this.close(true);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private getEnabledItems(): HTMLElement[] {
|
|
246
|
+
return Array.from(this.shadowRoot?.querySelectorAll<HTMLElement>('.menu-item:not(.disabled)') ?? []);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Focus an enabled item by index (wrapping); the menu itself if there is none */
|
|
250
|
+
private focusItem(indexArg: number) {
|
|
251
|
+
const items = this.getEnabledItems();
|
|
252
|
+
if (items.length === 0) {
|
|
253
|
+
(this.shadowRoot?.querySelector('.menu') as HTMLElement | null)?.focus({ preventScroll: true });
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const index = ((indexArg % items.length) + items.length) % items.length;
|
|
257
|
+
items[index].focus({ preventScroll: true });
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private handleMenuKeydown = (e: KeyboardEvent) => {
|
|
261
|
+
const items = this.getEnabledItems();
|
|
262
|
+
const current = items.indexOf(this.shadowRoot?.activeElement as HTMLElement);
|
|
263
|
+
switch (e.key) {
|
|
264
|
+
case 'ArrowDown':
|
|
265
|
+
this.focusItem(current + 1);
|
|
266
|
+
break;
|
|
267
|
+
case 'ArrowUp':
|
|
268
|
+
this.focusItem(current < 0 ? -1 : current - 1);
|
|
269
|
+
break;
|
|
270
|
+
case 'Home':
|
|
271
|
+
this.focusItem(0);
|
|
272
|
+
break;
|
|
273
|
+
case 'End':
|
|
274
|
+
this.focusItem(-1);
|
|
275
|
+
break;
|
|
276
|
+
case 'Enter':
|
|
277
|
+
case ' ':
|
|
278
|
+
if (current >= 0) items[current].click();
|
|
279
|
+
break;
|
|
280
|
+
case 'Escape':
|
|
281
|
+
case 'Tab':
|
|
282
|
+
this.close(true);
|
|
283
|
+
break;
|
|
284
|
+
default:
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
e.preventDefault();
|
|
288
|
+
e.stopPropagation();
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
private async handleItemClick(item: IContextMenuItem) {
|
|
292
|
+
if (item.disabled) return;
|
|
293
|
+
await item.action();
|
|
294
|
+
this.close(true);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private close(returnFocus = false) {
|
|
298
|
+
if (this.closing) return;
|
|
299
|
+
this.closing = true;
|
|
300
|
+
if (returnFocus && this.ownerElement?.isConnected) {
|
|
301
|
+
this.ownerElement.focus({ preventScroll: true });
|
|
302
|
+
}
|
|
303
|
+
this.classList.remove('visible');
|
|
304
|
+
setTimeout(() => this.remove(), 150);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { DeesElement, property, html, customElement, type TemplateResult, css, cssManager } from '@design.estate/dees-element';
|
|
2
|
+
|
|
3
|
+
import * as plugins from '../plugins.js';
|
|
4
|
+
|
|
5
|
+
export type TWccViewport = 'native' | 'desktop' | 'tablet' | 'phablet' | 'phone';
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
interface HTMLElementTagNameMap {
|
|
9
|
+
'wcc-preview-frame': WccPreviewFrame;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** The width of the preview document per viewport; desktop and native fill the frame. */
|
|
14
|
+
const getViewportWidth = (viewportArg: TWccViewport): string => {
|
|
15
|
+
switch (viewportArg) {
|
|
16
|
+
case 'tablet':
|
|
17
|
+
return `${plugins.deesDomtools.breakpoints.tablet}px`;
|
|
18
|
+
case 'phablet':
|
|
19
|
+
return `${plugins.deesDomtools.breakpoints.phablet}px`;
|
|
20
|
+
case 'phone':
|
|
21
|
+
return `${plugins.deesDomtools.breakpoints.phone}px`;
|
|
22
|
+
default:
|
|
23
|
+
return '100%';
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Hosts the preview document in an iframe as wide as the selected viewport, so the preview's
|
|
29
|
+
* width media queries respond to it.
|
|
30
|
+
*/
|
|
31
|
+
@customElement('wcc-preview-frame')
|
|
32
|
+
export class WccPreviewFrame extends DeesElement {
|
|
33
|
+
@property()
|
|
34
|
+
accessor viewport: TWccViewport = 'desktop';
|
|
35
|
+
|
|
36
|
+
/** The preview document's address. Set once: changing it navigates the preview. */
|
|
37
|
+
@property()
|
|
38
|
+
accessor previewSrc: string = '';
|
|
39
|
+
|
|
40
|
+
@property({ type: Boolean })
|
|
41
|
+
accessor advancedEditorOpen: boolean = false;
|
|
42
|
+
|
|
43
|
+
@property({ type: Boolean })
|
|
44
|
+
accessor isNative: boolean = false;
|
|
45
|
+
|
|
46
|
+
@property({ type: Number })
|
|
47
|
+
accessor sidebarWidth: number = 200;
|
|
48
|
+
|
|
49
|
+
@property({ type: Boolean })
|
|
50
|
+
accessor isResizing: boolean = false;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Catalog bundles whose latest run failed: shown over the preview, which shows an outdated
|
|
54
|
+
* bundle or none, until a run finishes and the preview reloads.
|
|
55
|
+
*/
|
|
56
|
+
@property({ attribute: false })
|
|
57
|
+
accessor bundleFailures: plugins.interfaces.IWccBundleStatus[] = [];
|
|
58
|
+
|
|
59
|
+
public static styles = [
|
|
60
|
+
css`
|
|
61
|
+
:host {
|
|
62
|
+
border: 10px solid rgba(255, 174, 175, 1);
|
|
63
|
+
box-sizing: border-box;
|
|
64
|
+
position: absolute;
|
|
65
|
+
background: ${cssManager.bdTheme('#333', '#000')};
|
|
66
|
+
right: 0px;
|
|
67
|
+
overflow-x: auto;
|
|
68
|
+
overflow-y: hidden;
|
|
69
|
+
overscroll-behavior: contain;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.preview {
|
|
73
|
+
display: block;
|
|
74
|
+
box-sizing: border-box;
|
|
75
|
+
height: 100%;
|
|
76
|
+
margin: 0 auto;
|
|
77
|
+
border: 0;
|
|
78
|
+
background: transparent;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.bundle-failure {
|
|
82
|
+
position: absolute;
|
|
83
|
+
inset: 0;
|
|
84
|
+
box-sizing: border-box;
|
|
85
|
+
overflow: auto;
|
|
86
|
+
padding: 24px;
|
|
87
|
+
background: rgba(20, 0, 0, 0.92);
|
|
88
|
+
color: #ffd7d7;
|
|
89
|
+
font-family: 'Geist Sans', 'Inter', sans-serif;
|
|
90
|
+
font-size: 14px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.bundle-failure h2 {
|
|
94
|
+
margin: 0 0 8px;
|
|
95
|
+
font-size: 16px;
|
|
96
|
+
font-weight: 600;
|
|
97
|
+
color: #ff8a8a;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.bundle-failure pre {
|
|
101
|
+
margin: 0 0 24px;
|
|
102
|
+
white-space: pre-wrap;
|
|
103
|
+
overflow-wrap: anywhere;
|
|
104
|
+
font-family: 'Geist Mono', 'Intel One Mono', monospace;
|
|
105
|
+
font-size: 13px;
|
|
106
|
+
line-height: 1.5;
|
|
107
|
+
}
|
|
108
|
+
`,
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
public get iframe(): HTMLIFrameElement | null {
|
|
112
|
+
return this.shadowRoot?.querySelector('iframe') ?? null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public render(): TemplateResult {
|
|
116
|
+
return html`
|
|
117
|
+
<style>
|
|
118
|
+
:host {
|
|
119
|
+
${this.isNative ? `
|
|
120
|
+
border: 0px solid rgba(255, 174, 175, 0) !important;
|
|
121
|
+
left: 0px !important;
|
|
122
|
+
right: 0px !important;
|
|
123
|
+
top: 0px !important;
|
|
124
|
+
bottom: 0px !important;
|
|
125
|
+
` : `
|
|
126
|
+
bottom: ${this.advancedEditorOpen ? '400px' : '100px'};
|
|
127
|
+
border: 10px solid rgba(255, 174, 175, 1);
|
|
128
|
+
left: ${this.sidebarWidth}px;
|
|
129
|
+
top: 0;
|
|
130
|
+
`}
|
|
131
|
+
transition: ${this.isResizing ? 'none' : 'all 0.3s ease'};
|
|
132
|
+
padding: 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.preview {
|
|
136
|
+
width: ${this.isNative ? '100%' : getViewportWidth(this.viewport)};
|
|
137
|
+
pointer-events: ${this.isResizing ? 'none' : 'auto'};
|
|
138
|
+
${!this.isNative && this.viewport !== 'desktop'
|
|
139
|
+
? 'border-right: 1px dotted #444; border-left: 1px dotted #444;'
|
|
140
|
+
: ''
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
${!this.isNative ? `
|
|
145
|
+
@container wccShell (max-width: 600px) {
|
|
146
|
+
:host {
|
|
147
|
+
left: 0;
|
|
148
|
+
top: 120px;
|
|
149
|
+
bottom: ${this.advancedEditorOpen ? '460px' : '160px'};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
` : ''}
|
|
153
|
+
</style>
|
|
154
|
+
${this.previewSrc ? html`
|
|
155
|
+
<iframe class="preview" title="Preview" src=${this.previewSrc}></iframe>
|
|
156
|
+
` : null}
|
|
157
|
+
${this.bundleFailures.length > 0 ? html`
|
|
158
|
+
<div class="bundle-failure" role="alert">
|
|
159
|
+
${this.bundleFailures.map((bundleArg) => html`
|
|
160
|
+
<h2>bundle failed: ${bundleArg.name}</h2>
|
|
161
|
+
<pre>${bundleArg.errorMessage}</pre>
|
|
162
|
+
`)}
|
|
163
|
+
</div>
|
|
164
|
+
` : null}
|
|
165
|
+
`;
|
|
166
|
+
}
|
|
167
|
+
}
|