@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
|
@@ -4,6 +4,7 @@ import { WccDashboard, getSectionItems } from './wcc-dashboard.js';
|
|
|
4
4
|
import type { TTemplateFactory } from './wcctools.helpers.js';
|
|
5
5
|
import { getDemoCount, hasMultipleDemos } from './wcctools.helpers.js';
|
|
6
6
|
import type { IWccSection, TElementType } from '../wcctools.interfaces.js';
|
|
7
|
+
import { WccContextmenu } from './wcc-contextmenu.js';
|
|
7
8
|
|
|
8
9
|
@customElement('wcc-sidebar')
|
|
9
10
|
export class WccSidebar extends DeesElement {
|
|
@@ -27,6 +28,14 @@ export class WccSidebar extends DeesElement {
|
|
|
27
28
|
@state()
|
|
28
29
|
accessor collapsedSections: Set<string> = new Set();
|
|
29
30
|
|
|
31
|
+
// Search query for filtering sidebar items
|
|
32
|
+
@property()
|
|
33
|
+
accessor searchQuery: string = '';
|
|
34
|
+
|
|
35
|
+
// Pinned items as Set of "sectionName::itemName"
|
|
36
|
+
@property({ attribute: false })
|
|
37
|
+
accessor pinnedItems: Set<string> = new Set();
|
|
38
|
+
|
|
30
39
|
private sectionsInitialized = false;
|
|
31
40
|
|
|
32
41
|
public render(): TemplateResult {
|
|
@@ -155,7 +164,7 @@ export class WccSidebar extends DeesElement {
|
|
|
155
164
|
}
|
|
156
165
|
|
|
157
166
|
.selectOption.folder {
|
|
158
|
-
grid-template-columns: 16px
|
|
167
|
+
grid-template-columns: 16px 1fr;
|
|
159
168
|
}
|
|
160
169
|
|
|
161
170
|
.selectOption .expand-icon {
|
|
@@ -252,8 +261,109 @@ export class WccSidebar extends DeesElement {
|
|
|
252
261
|
::-webkit-scrollbar-thumb:hover {
|
|
253
262
|
background: rgba(255, 255, 255, 0.2);
|
|
254
263
|
}
|
|
264
|
+
|
|
265
|
+
.search-container {
|
|
266
|
+
padding: 0.5rem;
|
|
267
|
+
border-bottom: 1px solid var(--border);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.search-input {
|
|
271
|
+
width: 100%;
|
|
272
|
+
box-sizing: border-box;
|
|
273
|
+
background: var(--input);
|
|
274
|
+
border: 1px solid var(--border);
|
|
275
|
+
border-radius: var(--radius);
|
|
276
|
+
padding: 0.5rem 0.75rem;
|
|
277
|
+
color: var(--foreground);
|
|
278
|
+
font-size: 0.75rem;
|
|
279
|
+
font-family: inherit;
|
|
280
|
+
outline: none;
|
|
281
|
+
transition: border-color 0.15s ease;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.search-input:focus {
|
|
285
|
+
border-color: var(--primary);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.search-input::placeholder {
|
|
289
|
+
color: var(--muted-foreground);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.highlight {
|
|
293
|
+
background: rgba(59, 130, 246, 0.3);
|
|
294
|
+
border-radius: 2px;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/* Pinned item highlight in original section */
|
|
298
|
+
.selectOption.pinned {
|
|
299
|
+
background: rgba(245, 158, 11, 0.08);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.selectOption.pinned:hover {
|
|
303
|
+
background: rgba(245, 158, 11, 0.12);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.selectOption.pinned.selected {
|
|
307
|
+
background: rgba(245, 158, 11, 0.18);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/* Pinned section styling */
|
|
311
|
+
.section-header.pinned-section {
|
|
312
|
+
background: rgba(245, 158, 11, 0.08);
|
|
313
|
+
color: #f59e0b;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.section-header.pinned-section:hover {
|
|
317
|
+
background: rgba(245, 158, 11, 0.12);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.section-header.pinned-section .section-icon {
|
|
321
|
+
opacity: 0.8;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/* Section tag for pinned items */
|
|
325
|
+
.section-tag {
|
|
326
|
+
font-size: 0.55rem;
|
|
327
|
+
color: #555;
|
|
328
|
+
margin-left: auto;
|
|
329
|
+
text-transform: uppercase;
|
|
330
|
+
letter-spacing: 0.03em;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* Group container */
|
|
334
|
+
.item-group {
|
|
335
|
+
margin: 0.375rem 0.375rem;
|
|
336
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
337
|
+
border-radius: 6px;
|
|
338
|
+
padding: 0.25rem 0;
|
|
339
|
+
background: rgba(255, 255, 255, 0.01);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.item-group-legend {
|
|
343
|
+
font-size: 0.55rem;
|
|
344
|
+
text-transform: uppercase;
|
|
345
|
+
letter-spacing: 0.05em;
|
|
346
|
+
color: #555;
|
|
347
|
+
padding: 0.125rem 0.625rem 0.25rem;
|
|
348
|
+
display: block;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.item-group .selectOption {
|
|
352
|
+
margin-left: 0.25rem;
|
|
353
|
+
margin-right: 0.25rem;
|
|
354
|
+
}
|
|
255
355
|
</style>
|
|
356
|
+
<div class="search-container">
|
|
357
|
+
<input
|
|
358
|
+
type="text"
|
|
359
|
+
class="search-input"
|
|
360
|
+
placeholder="Search..."
|
|
361
|
+
.value=${this.searchQuery}
|
|
362
|
+
@input=${this.handleSearchInput}
|
|
363
|
+
/>
|
|
364
|
+
</div>
|
|
256
365
|
<div class="menu">
|
|
366
|
+
${this.renderPinnedSection()}
|
|
257
367
|
${this.renderSections()}
|
|
258
368
|
</div>
|
|
259
369
|
`;
|
|
@@ -263,7 +373,7 @@ export class WccSidebar extends DeesElement {
|
|
|
263
373
|
* Initialize collapsed sections from section config
|
|
264
374
|
*/
|
|
265
375
|
private initCollapsedSections() {
|
|
266
|
-
if (this.sectionsInitialized) return;
|
|
376
|
+
if (this.sectionsInitialized || !this.dashboardRef?.sections) return;
|
|
267
377
|
|
|
268
378
|
const collapsed = new Set<string>();
|
|
269
379
|
for (const section of this.dashboardRef.sections) {
|
|
@@ -275,13 +385,125 @@ export class WccSidebar extends DeesElement {
|
|
|
275
385
|
this.sectionsInitialized = true;
|
|
276
386
|
}
|
|
277
387
|
|
|
388
|
+
// ============ Pinning helpers ============
|
|
389
|
+
|
|
390
|
+
private getPinKey(sectionName: string, itemName: string): string {
|
|
391
|
+
return `${sectionName}::${itemName}`;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
private isPinned(sectionName: string, itemName: string): boolean {
|
|
395
|
+
return this.pinnedItems.has(this.getPinKey(sectionName, itemName));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
private togglePin(sectionName: string, itemName: string) {
|
|
399
|
+
const key = this.getPinKey(sectionName, itemName);
|
|
400
|
+
const newPinned = new Set(this.pinnedItems);
|
|
401
|
+
if (newPinned.has(key)) {
|
|
402
|
+
newPinned.delete(key);
|
|
403
|
+
} else {
|
|
404
|
+
newPinned.add(key);
|
|
405
|
+
}
|
|
406
|
+
this.pinnedItems = newPinned;
|
|
407
|
+
this.dispatchEvent(new CustomEvent('pinnedChanged', { detail: newPinned }));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
private showContextMenu(e: MouseEvent, sectionName: string, itemName: string) {
|
|
411
|
+
const isPinned = this.isPinned(sectionName, itemName);
|
|
412
|
+
WccContextmenu.show(e, [
|
|
413
|
+
{
|
|
414
|
+
name: isPinned ? 'Unpin' : 'Pin',
|
|
415
|
+
iconName: isPinned ? 'push_pin' : 'push_pin',
|
|
416
|
+
action: () => this.togglePin(sectionName, itemName),
|
|
417
|
+
},
|
|
418
|
+
]);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Render the PINNED section (only if there are pinned items)
|
|
423
|
+
*/
|
|
424
|
+
private renderPinnedSection() {
|
|
425
|
+
if (!this.dashboardRef?.sections || this.pinnedItems.size === 0) {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const isCollapsed = this.collapsedSections.has('__pinned__');
|
|
430
|
+
|
|
431
|
+
// Collect pinned items with their original section info
|
|
432
|
+
const pinnedEntries: Array<{ sectionName: string; itemName: string; item: any; section: IWccSection }> = [];
|
|
433
|
+
|
|
434
|
+
for (const key of this.pinnedItems) {
|
|
435
|
+
const [sectionName, itemName] = key.split('::');
|
|
436
|
+
const section = this.dashboardRef.sections.find(s => s.name === sectionName);
|
|
437
|
+
if (section) {
|
|
438
|
+
const entries = getSectionItems(section);
|
|
439
|
+
const found = entries.find(([name]) => name === itemName);
|
|
440
|
+
if (found) {
|
|
441
|
+
pinnedEntries.push({ sectionName, itemName, item: found[1], section });
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Filter by search
|
|
447
|
+
const filteredEntries = pinnedEntries.filter(e => this.matchesSearch(e.itemName));
|
|
448
|
+
|
|
449
|
+
if (filteredEntries.length === 0 && this.searchQuery) {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return html`
|
|
454
|
+
<div
|
|
455
|
+
class="section-header pinned-section ${isCollapsed ? 'collapsed' : ''}"
|
|
456
|
+
@click=${() => this.toggleSectionCollapsed('__pinned__')}
|
|
457
|
+
>
|
|
458
|
+
<i class="material-symbols-outlined expand-icon">expand_more</i>
|
|
459
|
+
<i class="material-symbols-outlined section-icon">push_pin</i>
|
|
460
|
+
<span>Pinned</span>
|
|
461
|
+
</div>
|
|
462
|
+
<div class="section-content ${isCollapsed ? 'collapsed' : ''}">
|
|
463
|
+
${filteredEntries.map(({ sectionName, itemName, item, section }) => {
|
|
464
|
+
const isSelected = this.selectedItem === item;
|
|
465
|
+
const type = section.type === 'elements' ? 'element' : 'page';
|
|
466
|
+
const icon = section.type === 'elements' ? 'featured_video' : 'insert_drive_file';
|
|
467
|
+
|
|
468
|
+
return html`
|
|
469
|
+
<div
|
|
470
|
+
class="selectOption ${isSelected ? 'selected' : ''}"
|
|
471
|
+
@click=${async () => {
|
|
472
|
+
await plugins.deesDomtools.DomTools.setupDomTools();
|
|
473
|
+
this.selectItem(type, itemName, item, 0, section);
|
|
474
|
+
}}
|
|
475
|
+
@contextmenu=${(e: MouseEvent) => this.showContextMenu(e, sectionName, itemName)}
|
|
476
|
+
>
|
|
477
|
+
<i class="material-symbols-outlined">${icon}</i>
|
|
478
|
+
<div class="text">${this.highlightMatch(itemName)}</div>
|
|
479
|
+
<span class="section-tag">${sectionName}</span>
|
|
480
|
+
</div>
|
|
481
|
+
`;
|
|
482
|
+
})}
|
|
483
|
+
</div>
|
|
484
|
+
`;
|
|
485
|
+
}
|
|
486
|
+
|
|
278
487
|
/**
|
|
279
488
|
* Render all sections
|
|
280
489
|
*/
|
|
281
490
|
private renderSections() {
|
|
491
|
+
if (!this.dashboardRef?.sections) {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
|
|
282
495
|
this.initCollapsedSections();
|
|
283
496
|
|
|
284
|
-
return this.dashboardRef.sections.map((section
|
|
497
|
+
return this.dashboardRef.sections.map((section) => {
|
|
498
|
+
// Check if section has any matching items
|
|
499
|
+
const entries = getSectionItems(section);
|
|
500
|
+
const filteredEntries = entries.filter(([name]) => this.matchesSearch(name));
|
|
501
|
+
|
|
502
|
+
// Hide section if no items match the search
|
|
503
|
+
if (filteredEntries.length === 0 && this.searchQuery) {
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
|
|
285
507
|
const isCollapsed = this.collapsedSections.has(section.name);
|
|
286
508
|
const sectionIcon = section.icon || (section.type === 'pages' ? 'insert_drive_file' : 'widgets');
|
|
287
509
|
|
|
@@ -306,79 +528,122 @@ export class WccSidebar extends DeesElement {
|
|
|
306
528
|
*/
|
|
307
529
|
private renderSectionItems(section: IWccSection) {
|
|
308
530
|
const entries = getSectionItems(section);
|
|
531
|
+
// Filter entries by search query
|
|
532
|
+
const filteredEntries = entries.filter(([name]) => this.matchesSearch(name));
|
|
309
533
|
|
|
310
534
|
if (section.type === 'pages') {
|
|
311
|
-
return
|
|
535
|
+
return filteredEntries.map(([pageName, item]) => {
|
|
536
|
+
const isPinned = this.isPinned(section.name, pageName);
|
|
312
537
|
return html`
|
|
313
538
|
<div
|
|
314
|
-
class="selectOption ${this.selectedItem === item ? 'selected' : ''}"
|
|
539
|
+
class="selectOption ${this.selectedItem === item ? 'selected' : ''} ${isPinned ? 'pinned' : ''}"
|
|
315
540
|
@click=${async () => {
|
|
316
541
|
await plugins.deesDomtools.DomTools.setupDomTools();
|
|
317
542
|
this.selectItem('page', pageName, item, 0, section);
|
|
318
543
|
}}
|
|
544
|
+
@contextmenu=${(e: MouseEvent) => this.showContextMenu(e, section.name, pageName)}
|
|
319
545
|
>
|
|
320
546
|
<i class="material-symbols-outlined">insert_drive_file</i>
|
|
321
|
-
<div class="text">${pageName}</div>
|
|
547
|
+
<div class="text">${this.highlightMatch(pageName)}</div>
|
|
322
548
|
</div>
|
|
323
549
|
`;
|
|
324
550
|
});
|
|
325
551
|
} else {
|
|
326
|
-
// type === 'elements'
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (isMultiDemo) {
|
|
335
|
-
// Multi-demo element - render as expandable folder
|
|
336
|
-
return html`
|
|
337
|
-
<div
|
|
338
|
-
class="selectOption folder ${isExpanded ? 'expanded' : ''} ${isSelected ? 'selected' : ''}"
|
|
339
|
-
@click=${() => this.toggleExpanded(elementName)}
|
|
340
|
-
>
|
|
341
|
-
<i class="material-symbols-outlined expand-icon">chevron_right</i>
|
|
342
|
-
<i class="material-symbols-outlined">folder</i>
|
|
343
|
-
<div class="text">${elementName}</div>
|
|
344
|
-
</div>
|
|
345
|
-
${isExpanded ? html`
|
|
346
|
-
<div class="demo-children">
|
|
347
|
-
${Array.from({ length: demoCount }, (_, i) => {
|
|
348
|
-
const demoIndex = i;
|
|
349
|
-
const isThisDemoSelected = isSelected && this.dashboardRef.selectedDemoIndex === demoIndex;
|
|
350
|
-
return html`
|
|
351
|
-
<div
|
|
352
|
-
class="demo-child ${isThisDemoSelected ? 'selected' : ''}"
|
|
353
|
-
@click=${async () => {
|
|
354
|
-
await plugins.deesDomtools.DomTools.setupDomTools();
|
|
355
|
-
this.selectItem('element', elementName, item, demoIndex, section);
|
|
356
|
-
}}
|
|
357
|
-
>
|
|
358
|
-
<i class="material-symbols-outlined">play_circle</i>
|
|
359
|
-
<div class="text">demo${demoIndex + 1}</div>
|
|
360
|
-
</div>
|
|
361
|
-
`;
|
|
362
|
-
})}
|
|
363
|
-
</div>
|
|
364
|
-
` : null}
|
|
365
|
-
`;
|
|
366
|
-
} else {
|
|
367
|
-
// Single demo element
|
|
368
|
-
return html`
|
|
369
|
-
<div
|
|
370
|
-
class="selectOption ${isSelected ? 'selected' : ''}"
|
|
371
|
-
@click=${async () => {
|
|
372
|
-
await plugins.deesDomtools.DomTools.setupDomTools();
|
|
373
|
-
this.selectItem('element', elementName, item, 0, section);
|
|
374
|
-
}}
|
|
375
|
-
>
|
|
376
|
-
<i class="material-symbols-outlined">featured_video</i>
|
|
377
|
-
<div class="text">${elementName}</div>
|
|
378
|
-
</div>
|
|
379
|
-
`;
|
|
552
|
+
// type === 'elements' - group by demoGroup
|
|
553
|
+
const groupedItems = new Map<string | null, Array<[string, any]>>();
|
|
554
|
+
|
|
555
|
+
for (const entry of filteredEntries) {
|
|
556
|
+
const [, item] = entry;
|
|
557
|
+
const group = (item as any).demoGroup || null;
|
|
558
|
+
if (!groupedItems.has(group)) {
|
|
559
|
+
groupedItems.set(group, []);
|
|
380
560
|
}
|
|
381
|
-
|
|
561
|
+
groupedItems.get(group)!.push(entry);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const result: TemplateResult[] = [];
|
|
565
|
+
|
|
566
|
+
// Render ungrouped items first
|
|
567
|
+
const ungrouped = groupedItems.get(null) || [];
|
|
568
|
+
for (const entry of ungrouped) {
|
|
569
|
+
result.push(this.renderElementItem(entry, section));
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Render grouped items
|
|
573
|
+
for (const [groupName, items] of groupedItems) {
|
|
574
|
+
if (groupName === null) continue;
|
|
575
|
+
|
|
576
|
+
result.push(html`
|
|
577
|
+
<div class="item-group">
|
|
578
|
+
<span class="item-group-legend">${groupName}</span>
|
|
579
|
+
${items.map((entry) => this.renderElementItem(entry, section))}
|
|
580
|
+
</div>
|
|
581
|
+
`);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
return result;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Render a single element item (used by renderSectionItems)
|
|
590
|
+
*/
|
|
591
|
+
private renderElementItem(entry: [string, any], section: IWccSection): TemplateResult {
|
|
592
|
+
const [elementName, item] = entry;
|
|
593
|
+
const anonItem = item as any;
|
|
594
|
+
const demoCount = anonItem.demo ? getDemoCount(anonItem.demo) : 0;
|
|
595
|
+
const isMultiDemo = anonItem.demo && hasMultipleDemos(anonItem.demo);
|
|
596
|
+
const isExpanded = this.expandedElements.has(elementName);
|
|
597
|
+
const isSelected = this.selectedItem === item;
|
|
598
|
+
const isPinned = this.isPinned(section.name, elementName);
|
|
599
|
+
|
|
600
|
+
if (isMultiDemo) {
|
|
601
|
+
// Multi-demo element - render as expandable folder
|
|
602
|
+
return html`
|
|
603
|
+
<div
|
|
604
|
+
class="selectOption folder ${isExpanded ? 'expanded' : ''} ${isSelected ? 'selected' : ''} ${isPinned ? 'pinned' : ''}"
|
|
605
|
+
@click=${() => this.toggleExpanded(elementName)}
|
|
606
|
+
@contextmenu=${(e: MouseEvent) => this.showContextMenu(e, section.name, elementName)}
|
|
607
|
+
>
|
|
608
|
+
<i class="material-symbols-outlined expand-icon">chevron_right</i>
|
|
609
|
+
<div class="text">${this.highlightMatch(elementName)}</div>
|
|
610
|
+
</div>
|
|
611
|
+
${isExpanded ? html`
|
|
612
|
+
<div class="demo-children">
|
|
613
|
+
${Array.from({ length: demoCount }, (_, i) => {
|
|
614
|
+
const demoIndex = i;
|
|
615
|
+
const isThisDemoSelected = isSelected && this.dashboardRef.selectedDemoIndex === demoIndex;
|
|
616
|
+
return html`
|
|
617
|
+
<div
|
|
618
|
+
class="demo-child ${isThisDemoSelected ? 'selected' : ''}"
|
|
619
|
+
@click=${async () => {
|
|
620
|
+
await plugins.deesDomtools.DomTools.setupDomTools();
|
|
621
|
+
this.selectItem('element', elementName, item, demoIndex, section);
|
|
622
|
+
}}
|
|
623
|
+
>
|
|
624
|
+
<i class="material-symbols-outlined">play_circle</i>
|
|
625
|
+
<div class="text">demo${demoIndex + 1}</div>
|
|
626
|
+
</div>
|
|
627
|
+
`;
|
|
628
|
+
})}
|
|
629
|
+
</div>
|
|
630
|
+
` : null}
|
|
631
|
+
`;
|
|
632
|
+
} else {
|
|
633
|
+
// Single demo element
|
|
634
|
+
return html`
|
|
635
|
+
<div
|
|
636
|
+
class="selectOption ${isSelected ? 'selected' : ''} ${isPinned ? 'pinned' : ''}"
|
|
637
|
+
@click=${async () => {
|
|
638
|
+
await plugins.deesDomtools.DomTools.setupDomTools();
|
|
639
|
+
this.selectItem('element', elementName, item, 0, section);
|
|
640
|
+
}}
|
|
641
|
+
@contextmenu=${(e: MouseEvent) => this.showContextMenu(e, section.name, elementName)}
|
|
642
|
+
>
|
|
643
|
+
<i class="material-symbols-outlined">featured_video</i>
|
|
644
|
+
<div class="text">${this.highlightMatch(elementName)}</div>
|
|
645
|
+
</div>
|
|
646
|
+
`;
|
|
382
647
|
}
|
|
383
648
|
}
|
|
384
649
|
|
|
@@ -402,11 +667,34 @@ export class WccSidebar extends DeesElement {
|
|
|
402
667
|
this.expandedElements = newSet;
|
|
403
668
|
}
|
|
404
669
|
|
|
670
|
+
private handleSearchInput(e: Event) {
|
|
671
|
+
const input = e.target as HTMLInputElement;
|
|
672
|
+
this.searchQuery = input.value;
|
|
673
|
+
this.dispatchEvent(new CustomEvent('searchChanged', { detail: this.searchQuery }));
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
private matchesSearch(name: string): boolean {
|
|
677
|
+
if (!this.searchQuery) return true;
|
|
678
|
+
return name.toLowerCase().includes(this.searchQuery.toLowerCase());
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
private highlightMatch(text: string): TemplateResult {
|
|
682
|
+
if (!this.searchQuery) return html`${text}`;
|
|
683
|
+
const lowerText = text.toLowerCase();
|
|
684
|
+
const lowerQuery = this.searchQuery.toLowerCase();
|
|
685
|
+
const index = lowerText.indexOf(lowerQuery);
|
|
686
|
+
if (index === -1) return html`${text}`;
|
|
687
|
+
const before = text.slice(0, index);
|
|
688
|
+
const match = text.slice(index, index + this.searchQuery.length);
|
|
689
|
+
const after = text.slice(index + this.searchQuery.length);
|
|
690
|
+
return html`${before}<span class="highlight">${match}</span>${after}`;
|
|
691
|
+
}
|
|
692
|
+
|
|
405
693
|
protected updated(changedProperties: Map<string, unknown>) {
|
|
406
694
|
super.updated(changedProperties);
|
|
407
695
|
|
|
408
696
|
// Auto-expand folder when a multi-demo element is selected
|
|
409
|
-
if (changedProperties.has('selectedItem') && this.selectedItem) {
|
|
697
|
+
if (changedProperties.has('selectedItem') && this.selectedItem && this.dashboardRef?.sections) {
|
|
410
698
|
// Find the element in any section
|
|
411
699
|
for (const section of this.dashboardRef.sections) {
|
|
412
700
|
if (section.type !== 'elements') continue;
|