@design.estate/dees-wcctools 3.4.0 → 3.5.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/dist_bundle/bundle.js +637 -233
- package/dist_bundle/bundle.js.map +4 -4
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/wcc-contextmenu.d.ts +25 -0
- package/dist_ts_web/elements/wcc-contextmenu.js +257 -0
- package/dist_ts_web/elements/wcc-dashboard.d.ts +1 -0
- package/dist_ts_web/elements/wcc-dashboard.js +54 -2
- package/dist_ts_web/elements/wcc-sidebar.d.ts +13 -0
- package/dist_ts_web/elements/wcc-sidebar.js +257 -62
- package/dist_watch/bundle.js +1046 -385
- package/dist_watch/bundle.js.map +4 -4
- package/package.json +1 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/wcc-contextmenu.ts +211 -0
- package/ts_web/elements/wcc-dashboard.ts +45 -0
- package/ts_web/elements/wcc-sidebar.ts +269 -60
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@design.estate/dees-wcctools",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A set of web component tools for creating element catalogues, enabling the structured development and documentation of custom elements and pages.",
|
|
6
6
|
"exports": {
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@design.estate/dees-wcctools',
|
|
6
|
-
version: '3.
|
|
6
|
+
version: '3.5.0',
|
|
7
7
|
description: 'A set of web component tools for creating element catalogues, enabling the structured development and documentation of custom elements and pages.'
|
|
8
8
|
}
|
|
@@ -0,0 +1,211 @@
|
|
|
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
|
+
@customElement('wcc-contextmenu')
|
|
11
|
+
export class WccContextmenu extends DeesElement {
|
|
12
|
+
// Static method to show context menu at position
|
|
13
|
+
public static async show(
|
|
14
|
+
event: MouseEvent,
|
|
15
|
+
menuItems: IContextMenuItem[]
|
|
16
|
+
): Promise<void> {
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
event.stopPropagation();
|
|
19
|
+
|
|
20
|
+
// Remove any existing context menu
|
|
21
|
+
const existing = document.querySelector('wcc-contextmenu');
|
|
22
|
+
if (existing) {
|
|
23
|
+
existing.remove();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const menu = new WccContextmenu();
|
|
27
|
+
menu.menuItems = menuItems;
|
|
28
|
+
menu.x = event.clientX;
|
|
29
|
+
menu.y = event.clientY;
|
|
30
|
+
|
|
31
|
+
document.body.appendChild(menu);
|
|
32
|
+
|
|
33
|
+
// Wait for render then adjust position if needed
|
|
34
|
+
await menu.updateComplete;
|
|
35
|
+
menu.adjustPosition();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@property({ type: Array })
|
|
39
|
+
accessor menuItems: IContextMenuItem[] = [];
|
|
40
|
+
|
|
41
|
+
@property({ type: Number })
|
|
42
|
+
accessor x: number = 0;
|
|
43
|
+
|
|
44
|
+
@property({ type: Number })
|
|
45
|
+
accessor y: number = 0;
|
|
46
|
+
|
|
47
|
+
@state()
|
|
48
|
+
accessor visible: boolean = false;
|
|
49
|
+
|
|
50
|
+
private boundHandleOutsideClick = this.handleOutsideClick.bind(this);
|
|
51
|
+
private boundHandleKeydown = this.handleKeydown.bind(this);
|
|
52
|
+
|
|
53
|
+
public static styles = [
|
|
54
|
+
css`
|
|
55
|
+
:host {
|
|
56
|
+
position: fixed;
|
|
57
|
+
z-index: 10000;
|
|
58
|
+
opacity: 0;
|
|
59
|
+
transform: scale(0.95) translateY(-5px);
|
|
60
|
+
transition: opacity 0.15s ease, transform 0.15s ease;
|
|
61
|
+
pointer-events: none;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
:host(.visible) {
|
|
65
|
+
opacity: 1;
|
|
66
|
+
transform: scale(1) translateY(0);
|
|
67
|
+
pointer-events: auto;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.menu {
|
|
71
|
+
min-width: 160px;
|
|
72
|
+
background: #0f0f0f;
|
|
73
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
74
|
+
border-radius: 6px;
|
|
75
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
76
|
+
padding: 4px 0;
|
|
77
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
|
78
|
+
font-size: 12px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.menu-item {
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
gap: 8px;
|
|
85
|
+
padding: 8px 12px;
|
|
86
|
+
color: #ccc;
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
transition: background 0.1s ease;
|
|
89
|
+
user-select: none;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.menu-item:hover {
|
|
93
|
+
background: rgba(59, 130, 246, 0.15);
|
|
94
|
+
color: #fff;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.menu-item.disabled {
|
|
98
|
+
opacity: 0.4;
|
|
99
|
+
cursor: not-allowed;
|
|
100
|
+
pointer-events: none;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.menu-item .icon {
|
|
104
|
+
font-family: 'Material Symbols Outlined';
|
|
105
|
+
font-size: 16px;
|
|
106
|
+
font-weight: normal;
|
|
107
|
+
font-style: normal;
|
|
108
|
+
line-height: 1;
|
|
109
|
+
letter-spacing: normal;
|
|
110
|
+
text-transform: none;
|
|
111
|
+
white-space: nowrap;
|
|
112
|
+
word-wrap: normal;
|
|
113
|
+
direction: ltr;
|
|
114
|
+
font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 24;
|
|
115
|
+
opacity: 0.7;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.menu-item:hover .icon {
|
|
119
|
+
opacity: 1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.menu-item .label {
|
|
123
|
+
flex: 1;
|
|
124
|
+
}
|
|
125
|
+
`
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
public render(): TemplateResult {
|
|
129
|
+
return html`
|
|
130
|
+
<div class="menu">
|
|
131
|
+
${this.menuItems.map(item => html`
|
|
132
|
+
<div
|
|
133
|
+
class="menu-item ${item.disabled ? 'disabled' : ''}"
|
|
134
|
+
@click=${() => this.handleItemClick(item)}
|
|
135
|
+
>
|
|
136
|
+
${item.iconName ? html`<span class="icon">${item.iconName}</span>` : null}
|
|
137
|
+
<span class="label">${item.name}</span>
|
|
138
|
+
</div>
|
|
139
|
+
`)}
|
|
140
|
+
</div>
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async connectedCallback() {
|
|
145
|
+
await super.connectedCallback();
|
|
146
|
+
// Delay adding listeners to avoid immediate close
|
|
147
|
+
requestAnimationFrame(() => {
|
|
148
|
+
document.addEventListener('click', this.boundHandleOutsideClick);
|
|
149
|
+
document.addEventListener('contextmenu', this.boundHandleOutsideClick);
|
|
150
|
+
document.addEventListener('keydown', this.boundHandleKeydown);
|
|
151
|
+
this.classList.add('visible');
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async disconnectedCallback() {
|
|
156
|
+
await super.disconnectedCallback();
|
|
157
|
+
document.removeEventListener('click', this.boundHandleOutsideClick);
|
|
158
|
+
document.removeEventListener('contextmenu', this.boundHandleOutsideClick);
|
|
159
|
+
document.removeEventListener('keydown', this.boundHandleKeydown);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private adjustPosition() {
|
|
163
|
+
const rect = this.getBoundingClientRect();
|
|
164
|
+
const windowWidth = window.innerWidth;
|
|
165
|
+
const windowHeight = window.innerHeight;
|
|
166
|
+
|
|
167
|
+
let x = this.x;
|
|
168
|
+
let y = this.y;
|
|
169
|
+
|
|
170
|
+
// Adjust if menu goes off right edge
|
|
171
|
+
if (x + rect.width > windowWidth - 10) {
|
|
172
|
+
x = windowWidth - rect.width - 10;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Adjust if menu goes off bottom edge
|
|
176
|
+
if (y + rect.height > windowHeight - 10) {
|
|
177
|
+
y = windowHeight - rect.height - 10;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Ensure not off left or top
|
|
181
|
+
if (x < 10) x = 10;
|
|
182
|
+
if (y < 10) y = 10;
|
|
183
|
+
|
|
184
|
+
this.style.left = `${x}px`;
|
|
185
|
+
this.style.top = `${y}px`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private handleOutsideClick(e: Event) {
|
|
189
|
+
const path = e.composedPath();
|
|
190
|
+
if (!path.includes(this)) {
|
|
191
|
+
this.close();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private handleKeydown(e: KeyboardEvent) {
|
|
196
|
+
if (e.key === 'Escape') {
|
|
197
|
+
this.close();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private async handleItemClick(item: IContextMenuItem) {
|
|
202
|
+
if (item.disabled) return;
|
|
203
|
+
await item.action();
|
|
204
|
+
this.close();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private close() {
|
|
208
|
+
this.classList.remove('visible');
|
|
209
|
+
setTimeout(() => this.remove(), 150);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -62,6 +62,10 @@ export class WccDashboard extends DeesElement {
|
|
|
62
62
|
@property()
|
|
63
63
|
accessor searchQuery: string = '';
|
|
64
64
|
|
|
65
|
+
// Pinned items as Set of "sectionName::itemName"
|
|
66
|
+
@property({ attribute: false })
|
|
67
|
+
accessor pinnedItems: Set<string> = new Set();
|
|
68
|
+
|
|
65
69
|
// Derived from selectedViewport - no need for separate property
|
|
66
70
|
public get isNative(): boolean {
|
|
67
71
|
return this.selectedViewport === 'native';
|
|
@@ -122,6 +126,7 @@ export class WccDashboard extends DeesElement {
|
|
|
122
126
|
.dashboardRef=${this}
|
|
123
127
|
.selectedItem=${this.selectedItem}
|
|
124
128
|
.searchQuery=${this.searchQuery}
|
|
129
|
+
.pinnedItems=${this.pinnedItems}
|
|
125
130
|
.isNative=${this.isNative}
|
|
126
131
|
@selectedType=${(eventArg) => {
|
|
127
132
|
this.selectedType = eventArg.detail;
|
|
@@ -136,6 +141,10 @@ export class WccDashboard extends DeesElement {
|
|
|
136
141
|
this.searchQuery = eventArg.detail;
|
|
137
142
|
this.updateUrlWithScrollState();
|
|
138
143
|
}}
|
|
144
|
+
@pinnedChanged=${(eventArg: CustomEvent) => {
|
|
145
|
+
this.pinnedItems = eventArg.detail;
|
|
146
|
+
this.updateUrlWithScrollState();
|
|
147
|
+
}}
|
|
139
148
|
></wcc-sidebar>
|
|
140
149
|
<wcc-properties
|
|
141
150
|
.dashboardRef=${this}
|
|
@@ -237,6 +246,7 @@ export class WccDashboard extends DeesElement {
|
|
|
237
246
|
const search = routeInfo.queryParams.search;
|
|
238
247
|
const frameScrollY = routeInfo.queryParams.frameScrollY;
|
|
239
248
|
const sidebarScrollY = routeInfo.queryParams.sidebarScrollY;
|
|
249
|
+
const pinned = routeInfo.queryParams.pinned;
|
|
240
250
|
|
|
241
251
|
if (search) {
|
|
242
252
|
this.searchQuery = search;
|
|
@@ -249,6 +259,16 @@ export class WccDashboard extends DeesElement {
|
|
|
249
259
|
if (sidebarScrollY) {
|
|
250
260
|
this.sidebarScrollY = parseInt(sidebarScrollY);
|
|
251
261
|
}
|
|
262
|
+
if (pinned) {
|
|
263
|
+
const newPinned = new Set(pinned.split(',').filter(Boolean));
|
|
264
|
+
// Only update if actually different to avoid update loops
|
|
265
|
+
if (this.pinnedItems.size !== newPinned.size ||
|
|
266
|
+
![...newPinned].every(k => this.pinnedItems.has(k))) {
|
|
267
|
+
this.pinnedItems = newPinned;
|
|
268
|
+
}
|
|
269
|
+
} else if (this.pinnedItems.size > 0) {
|
|
270
|
+
this.pinnedItems = new Set();
|
|
271
|
+
}
|
|
252
272
|
|
|
253
273
|
// Apply scroll positions after a short delay to ensure DOM is ready
|
|
254
274
|
setTimeout(() => {
|
|
@@ -256,6 +276,10 @@ export class WccDashboard extends DeesElement {
|
|
|
256
276
|
}, 100);
|
|
257
277
|
} else {
|
|
258
278
|
this.searchQuery = '';
|
|
279
|
+
// Only clear if not already empty to avoid update loops
|
|
280
|
+
if (this.pinnedItems.size > 0) {
|
|
281
|
+
this.pinnedItems = new Set();
|
|
282
|
+
}
|
|
259
283
|
}
|
|
260
284
|
|
|
261
285
|
const domtoolsInstance = await plugins.deesDomtools.elementBasic.setup();
|
|
@@ -301,6 +325,7 @@ export class WccDashboard extends DeesElement {
|
|
|
301
325
|
const search = routeInfo.queryParams.search;
|
|
302
326
|
const frameScrollY = routeInfo.queryParams.frameScrollY;
|
|
303
327
|
const sidebarScrollY = routeInfo.queryParams.sidebarScrollY;
|
|
328
|
+
const pinned = routeInfo.queryParams.pinned;
|
|
304
329
|
|
|
305
330
|
if (search) {
|
|
306
331
|
this.searchQuery = search;
|
|
@@ -313,6 +338,16 @@ export class WccDashboard extends DeesElement {
|
|
|
313
338
|
if (sidebarScrollY) {
|
|
314
339
|
this.sidebarScrollY = parseInt(sidebarScrollY);
|
|
315
340
|
}
|
|
341
|
+
if (pinned) {
|
|
342
|
+
const newPinned = new Set(pinned.split(',').filter(Boolean));
|
|
343
|
+
// Only update if actually different to avoid update loops
|
|
344
|
+
if (this.pinnedItems.size !== newPinned.size ||
|
|
345
|
+
![...newPinned].every(k => this.pinnedItems.has(k))) {
|
|
346
|
+
this.pinnedItems = newPinned;
|
|
347
|
+
}
|
|
348
|
+
} else if (this.pinnedItems.size > 0) {
|
|
349
|
+
this.pinnedItems = new Set();
|
|
350
|
+
}
|
|
316
351
|
|
|
317
352
|
// Apply scroll positions after a short delay to ensure DOM is ready
|
|
318
353
|
setTimeout(() => {
|
|
@@ -320,6 +355,10 @@ export class WccDashboard extends DeesElement {
|
|
|
320
355
|
}, 100);
|
|
321
356
|
} else {
|
|
322
357
|
this.searchQuery = '';
|
|
358
|
+
// Only clear if not already empty to avoid update loops
|
|
359
|
+
if (this.pinnedItems.size > 0) {
|
|
360
|
+
this.pinnedItems = new Set();
|
|
361
|
+
}
|
|
323
362
|
}
|
|
324
363
|
|
|
325
364
|
const domtoolsInstance = await plugins.deesDomtools.elementBasic.setup();
|
|
@@ -402,6 +441,9 @@ export class WccDashboard extends DeesElement {
|
|
|
402
441
|
if (this.sidebarScrollY > 0) {
|
|
403
442
|
queryParams.set('sidebarScrollY', this.sidebarScrollY.toString());
|
|
404
443
|
}
|
|
444
|
+
if (this.pinnedItems.size > 0) {
|
|
445
|
+
queryParams.set('pinned', Array.from(this.pinnedItems).join(','));
|
|
446
|
+
}
|
|
405
447
|
|
|
406
448
|
const queryString = queryParams.toString();
|
|
407
449
|
const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl;
|
|
@@ -462,6 +504,9 @@ export class WccDashboard extends DeesElement {
|
|
|
462
504
|
if (this.sidebarScrollY > 0) {
|
|
463
505
|
queryParams.set('sidebarScrollY', this.sidebarScrollY.toString());
|
|
464
506
|
}
|
|
507
|
+
if (this.pinnedItems.size > 0) {
|
|
508
|
+
queryParams.set('pinned', Array.from(this.pinnedItems).join(','));
|
|
509
|
+
}
|
|
465
510
|
|
|
466
511
|
const queryString = queryParams.toString();
|
|
467
512
|
const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl;
|