feat(wcctools): Add section-based configuration API for setupWccTools, new Views, and section-aware routing/sidebar

This commit is contained in:
2025-12-28 12:51:55 +00:00
parent dd151bdad8
commit 14e63738b7
14 changed files with 1709 additions and 212 deletions

View File

@@ -1,6 +1,7 @@
import { DeesElement, property, html, customElement, type TemplateResult, queryAsync, render, domtools } from '@design.estate/dees-element';
import { resolveTemplateFactory, getDemoAtIndex, getDemoCount, hasMultipleDemos } from './wcctools.helpers.js';
import type { TTemplateFactory } from './wcctools.helpers.js';
import type { IWccConfig, IWccSection, TElementType } from '../wcctools.interfaces.js';
import * as plugins from '../wcctools.plugins.js';
@@ -9,13 +10,37 @@ import './wcc-frame.js';
import './wcc-sidebar.js';
import './wcc-properties.js';
import { type TTheme } from './wcc-properties.js';
import { type TElementType } from './wcc-sidebar.js';
import { breakpoints } from '@design.estate/dees-domtools';
import { WccFrame } from './wcc-frame.js';
/**
* Get filtered and sorted items from a section
*/
export const getSectionItems = (section: IWccSection): Array<[string, any]> => {
let entries = Object.entries(section.items);
// Apply filter if provided
if (section.filter) {
entries = entries.filter(([name, item]) => section.filter(name, item));
}
// Apply sort if provided
if (section.sort) {
entries.sort(section.sort);
}
return entries;
};
@customElement('wcc-dashboard')
export class WccDashboard extends DeesElement {
@property()
accessor sections: IWccSection[] = [];
@property()
accessor selectedSection: IWccSection | null = null;
@property()
accessor selectedType: TElementType;
@@ -39,36 +64,43 @@ export class WccDashboard extends DeesElement {
return this.selectedViewport === 'native';
}
@property()
accessor pages: Record<string, TTemplateFactory> = {};
@property()
accessor elements: { [key: string]: DeesElement } = {};
@property()
accessor warning: string = null;
private frameScrollY: number = 0;
private sidebarScrollY: number = 0;
private scrollPositionsApplied: boolean = false;
@queryAsync('wcc-frame')
accessor wccFrame: Promise<WccFrame>;
constructor(
elementsArg?: { [key: string]: DeesElement },
pagesArg?: Record<string, TTemplateFactory>
) {
constructor(config?: IWccConfig) {
super();
if (elementsArg) {
this.elements = elementsArg;
console.log('got elements:');
console.log(this.elements);
if (config && config.sections) {
this.sections = config.sections;
console.log('got sections:', this.sections.map(s => s.name));
}
}
if (pagesArg) {
this.pages = pagesArg;
/**
* Find an item by name across all sections, returns the item and its section
*/
public findItemByName(name: string): { item: any; section: IWccSection } | null {
for (const section of this.sections) {
const entries = getSectionItems(section);
const found = entries.find(([itemName]) => itemName === name);
if (found) {
return { item: found[1], section };
}
}
return null;
}
/**
* Find a section by name (URL-decoded)
*/
public findSectionByName(name: string): IWccSection | null {
return this.sections.find(s => s.name === name) || null;
}
public render(): TemplateResult {
@@ -159,19 +191,37 @@ export class WccDashboard extends DeesElement {
this.setupScrollListeners();
}, 500);
// Route with demo index (new format)
// New route format with section name
this.domtools.router.on(
'/wcctools-route/:itemType/:itemName/:demoIndex/:viewport/:theme',
'/wcctools-route/:sectionName/:itemName/:demoIndex/:viewport/:theme',
async (routeInfo) => {
this.selectedType = routeInfo.params.itemType as TElementType;
const sectionName = decodeURIComponent(routeInfo.params.sectionName);
this.selectedSection = this.findSectionByName(sectionName);
this.selectedItemName = routeInfo.params.itemName;
this.selectedDemoIndex = parseInt(routeInfo.params.demoIndex) || 0;
this.selectedViewport = routeInfo.params.viewport as breakpoints.TViewport;
this.selectedTheme = routeInfo.params.theme as TTheme;
if (routeInfo.params.itemType === 'element') {
this.selectedItem = this.elements[routeInfo.params.itemName];
} else if (routeInfo.params.itemType === 'page') {
this.selectedItem = this.pages[routeInfo.params.itemName];
if (this.selectedSection) {
// Find item within the section
const entries = getSectionItems(this.selectedSection);
const found = entries.find(([name]) => name === routeInfo.params.itemName);
if (found) {
this.selectedItem = found[1];
this.selectedType = this.selectedSection.type === 'elements' ? 'element' : 'page';
}
} else {
// Fallback: try legacy format (element/page as section name)
const legacyType = routeInfo.params.sectionName;
if (legacyType === 'element' || legacyType === 'page') {
this.selectedType = legacyType as TElementType;
// Find item in any matching section
const result = this.findItemByName(routeInfo.params.itemName);
if (result) {
this.selectedItem = result.item;
this.selectedSection = result.section;
}
}
}
// Restore scroll positions from query parameters
@@ -201,17 +251,33 @@ export class WccDashboard extends DeesElement {
// Legacy route without demo index (for backwards compatibility)
this.domtools.router.on(
'/wcctools-route/:itemType/:itemName/:viewport/:theme',
'/wcctools-route/:sectionName/:itemName/:viewport/:theme',
async (routeInfo) => {
this.selectedType = routeInfo.params.itemType as TElementType;
const sectionName = decodeURIComponent(routeInfo.params.sectionName);
this.selectedSection = this.findSectionByName(sectionName);
this.selectedItemName = routeInfo.params.itemName;
this.selectedDemoIndex = 0; // Default to first demo
this.selectedDemoIndex = 0;
this.selectedViewport = routeInfo.params.viewport as breakpoints.TViewport;
this.selectedTheme = routeInfo.params.theme as TTheme;
if (routeInfo.params.itemType === 'element') {
this.selectedItem = this.elements[routeInfo.params.itemName];
} else if (routeInfo.params.itemType === 'page') {
this.selectedItem = this.pages[routeInfo.params.itemName];
if (this.selectedSection) {
const entries = getSectionItems(this.selectedSection);
const found = entries.find(([name]) => name === routeInfo.params.itemName);
if (found) {
this.selectedItem = found[1];
this.selectedType = this.selectedSection.type === 'elements' ? 'element' : 'page';
}
} else {
// Fallback: try legacy format
const legacyType = routeInfo.params.sectionName;
if (legacyType === 'element' || legacyType === 'page') {
this.selectedType = legacyType as TElementType;
const result = this.findItemByName(routeInfo.params.itemName);
if (result) {
this.selectedItem = result.item;
this.selectedSection = result.section;
}
}
}
// Restore scroll positions from query parameters
@@ -297,7 +363,10 @@ export class WccDashboard extends DeesElement {
}
public buildUrl() {
const baseUrl = `/wcctools-route/${this.selectedType}/${this.selectedItemName}/${this.selectedDemoIndex}/${this.selectedViewport}/${this.selectedTheme}`;
const sectionName = this.selectedSection
? encodeURIComponent(this.selectedSection.name)
: this.selectedType; // Fallback for legacy
const baseUrl = `/wcctools-route/${sectionName}/${this.selectedItemName}/${this.selectedDemoIndex}/${this.selectedViewport}/${this.selectedTheme}`;
const queryParams = new URLSearchParams();
if (this.frameScrollY > 0) {
@@ -351,7 +420,10 @@ export class WccDashboard extends DeesElement {
}
private updateUrlWithScrollState() {
const baseUrl = `/wcctools-route/${this.selectedType}/${this.selectedItemName}/${this.selectedDemoIndex}/${this.selectedViewport}/${this.selectedTheme}`;
const sectionName = this.selectedSection
? encodeURIComponent(this.selectedSection.name)
: this.selectedType; // Fallback for legacy
const baseUrl = `/wcctools-route/${sectionName}/${this.selectedItemName}/${this.selectedDemoIndex}/${this.selectedViewport}/${this.selectedTheme}`;
const queryParams = new URLSearchParams();
if (this.frameScrollY > 0) {