@design.estate/dees-wcctools 3.3.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 +735 -231
- 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 +2 -0
- package/dist_ts_web/elements/wcc-dashboard.js +95 -4
- package/dist_ts_web/elements/wcc-sidebar.d.ts +17 -0
- package/dist_ts_web/elements/wcc-sidebar.js +341 -64
- package/dist_watch/bundle.js +1144 -383
- 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 +77 -2
- package/ts_web/elements/wcc-sidebar.ts +350 -62
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
|
+
}
|
|
@@ -59,6 +59,13 @@ export class WccDashboard extends DeesElement {
|
|
|
59
59
|
@property()
|
|
60
60
|
accessor selectedTheme: TTheme = 'dark';
|
|
61
61
|
|
|
62
|
+
@property()
|
|
63
|
+
accessor searchQuery: string = '';
|
|
64
|
+
|
|
65
|
+
// Pinned items as Set of "sectionName::itemName"
|
|
66
|
+
@property({ attribute: false })
|
|
67
|
+
accessor pinnedItems: Set<string> = new Set();
|
|
68
|
+
|
|
62
69
|
// Derived from selectedViewport - no need for separate property
|
|
63
70
|
public get isNative(): boolean {
|
|
64
71
|
return this.selectedViewport === 'native';
|
|
@@ -118,6 +125,8 @@ export class WccDashboard extends DeesElement {
|
|
|
118
125
|
<wcc-sidebar
|
|
119
126
|
.dashboardRef=${this}
|
|
120
127
|
.selectedItem=${this.selectedItem}
|
|
128
|
+
.searchQuery=${this.searchQuery}
|
|
129
|
+
.pinnedItems=${this.pinnedItems}
|
|
121
130
|
.isNative=${this.isNative}
|
|
122
131
|
@selectedType=${(eventArg) => {
|
|
123
132
|
this.selectedType = eventArg.detail;
|
|
@@ -128,6 +137,14 @@ export class WccDashboard extends DeesElement {
|
|
|
128
137
|
@selectedItem=${(eventArg) => {
|
|
129
138
|
this.selectedItem = eventArg.detail;
|
|
130
139
|
}}
|
|
140
|
+
@searchChanged=${(eventArg: CustomEvent) => {
|
|
141
|
+
this.searchQuery = eventArg.detail;
|
|
142
|
+
this.updateUrlWithScrollState();
|
|
143
|
+
}}
|
|
144
|
+
@pinnedChanged=${(eventArg: CustomEvent) => {
|
|
145
|
+
this.pinnedItems = eventArg.detail;
|
|
146
|
+
this.updateUrlWithScrollState();
|
|
147
|
+
}}
|
|
131
148
|
></wcc-sidebar>
|
|
132
149
|
<wcc-properties
|
|
133
150
|
.dashboardRef=${this}
|
|
@@ -224,22 +241,45 @@ export class WccDashboard extends DeesElement {
|
|
|
224
241
|
}
|
|
225
242
|
}
|
|
226
243
|
|
|
227
|
-
// Restore
|
|
244
|
+
// Restore state from query parameters
|
|
228
245
|
if (routeInfo.queryParams) {
|
|
246
|
+
const search = routeInfo.queryParams.search;
|
|
229
247
|
const frameScrollY = routeInfo.queryParams.frameScrollY;
|
|
230
248
|
const sidebarScrollY = routeInfo.queryParams.sidebarScrollY;
|
|
249
|
+
const pinned = routeInfo.queryParams.pinned;
|
|
231
250
|
|
|
251
|
+
if (search) {
|
|
252
|
+
this.searchQuery = search;
|
|
253
|
+
} else {
|
|
254
|
+
this.searchQuery = '';
|
|
255
|
+
}
|
|
232
256
|
if (frameScrollY) {
|
|
233
257
|
this.frameScrollY = parseInt(frameScrollY);
|
|
234
258
|
}
|
|
235
259
|
if (sidebarScrollY) {
|
|
236
260
|
this.sidebarScrollY = parseInt(sidebarScrollY);
|
|
237
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
|
+
}
|
|
238
272
|
|
|
239
273
|
// Apply scroll positions after a short delay to ensure DOM is ready
|
|
240
274
|
setTimeout(() => {
|
|
241
275
|
this.applyScrollPositions();
|
|
242
276
|
}, 100);
|
|
277
|
+
} else {
|
|
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
|
+
}
|
|
243
283
|
}
|
|
244
284
|
|
|
245
285
|
const domtoolsInstance = await plugins.deesDomtools.elementBasic.setup();
|
|
@@ -280,22 +320,45 @@ export class WccDashboard extends DeesElement {
|
|
|
280
320
|
}
|
|
281
321
|
}
|
|
282
322
|
|
|
283
|
-
// Restore
|
|
323
|
+
// Restore state from query parameters
|
|
284
324
|
if (routeInfo.queryParams) {
|
|
325
|
+
const search = routeInfo.queryParams.search;
|
|
285
326
|
const frameScrollY = routeInfo.queryParams.frameScrollY;
|
|
286
327
|
const sidebarScrollY = routeInfo.queryParams.sidebarScrollY;
|
|
328
|
+
const pinned = routeInfo.queryParams.pinned;
|
|
287
329
|
|
|
330
|
+
if (search) {
|
|
331
|
+
this.searchQuery = search;
|
|
332
|
+
} else {
|
|
333
|
+
this.searchQuery = '';
|
|
334
|
+
}
|
|
288
335
|
if (frameScrollY) {
|
|
289
336
|
this.frameScrollY = parseInt(frameScrollY);
|
|
290
337
|
}
|
|
291
338
|
if (sidebarScrollY) {
|
|
292
339
|
this.sidebarScrollY = parseInt(sidebarScrollY);
|
|
293
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
|
+
}
|
|
294
351
|
|
|
295
352
|
// Apply scroll positions after a short delay to ensure DOM is ready
|
|
296
353
|
setTimeout(() => {
|
|
297
354
|
this.applyScrollPositions();
|
|
298
355
|
}, 100);
|
|
356
|
+
} else {
|
|
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
|
+
}
|
|
299
362
|
}
|
|
300
363
|
|
|
301
364
|
const domtoolsInstance = await plugins.deesDomtools.elementBasic.setup();
|
|
@@ -369,12 +432,18 @@ export class WccDashboard extends DeesElement {
|
|
|
369
432
|
const baseUrl = `/wcctools-route/${sectionName}/${this.selectedItemName}/${this.selectedDemoIndex}/${this.selectedViewport}/${this.selectedTheme}`;
|
|
370
433
|
const queryParams = new URLSearchParams();
|
|
371
434
|
|
|
435
|
+
if (this.searchQuery) {
|
|
436
|
+
queryParams.set('search', this.searchQuery);
|
|
437
|
+
}
|
|
372
438
|
if (this.frameScrollY > 0) {
|
|
373
439
|
queryParams.set('frameScrollY', this.frameScrollY.toString());
|
|
374
440
|
}
|
|
375
441
|
if (this.sidebarScrollY > 0) {
|
|
376
442
|
queryParams.set('sidebarScrollY', this.sidebarScrollY.toString());
|
|
377
443
|
}
|
|
444
|
+
if (this.pinnedItems.size > 0) {
|
|
445
|
+
queryParams.set('pinned', Array.from(this.pinnedItems).join(','));
|
|
446
|
+
}
|
|
378
447
|
|
|
379
448
|
const queryString = queryParams.toString();
|
|
380
449
|
const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl;
|
|
@@ -426,12 +495,18 @@ export class WccDashboard extends DeesElement {
|
|
|
426
495
|
const baseUrl = `/wcctools-route/${sectionName}/${this.selectedItemName}/${this.selectedDemoIndex}/${this.selectedViewport}/${this.selectedTheme}`;
|
|
427
496
|
const queryParams = new URLSearchParams();
|
|
428
497
|
|
|
498
|
+
if (this.searchQuery) {
|
|
499
|
+
queryParams.set('search', this.searchQuery);
|
|
500
|
+
}
|
|
429
501
|
if (this.frameScrollY > 0) {
|
|
430
502
|
queryParams.set('frameScrollY', this.frameScrollY.toString());
|
|
431
503
|
}
|
|
432
504
|
if (this.sidebarScrollY > 0) {
|
|
433
505
|
queryParams.set('sidebarScrollY', this.sidebarScrollY.toString());
|
|
434
506
|
}
|
|
507
|
+
if (this.pinnedItems.size > 0) {
|
|
508
|
+
queryParams.set('pinned', Array.from(this.pinnedItems).join(','));
|
|
509
|
+
}
|
|
435
510
|
|
|
436
511
|
const queryString = queryParams.toString();
|
|
437
512
|
const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl;
|