import { DeesElement, property, html, customElement, type TemplateResult, queryAsync, render, domtools } from '@design.estate/dees-element'; import * as plugins from '../wcctools.plugins.js'; // wcc tools 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'; @customElement('wcc-dashboard') export class WccDashboard extends DeesElement { @property() public selectedType: TElementType; @property() public selectedItemName: string; @property() public selectedItem: (() => TemplateResult) | DeesElement; @property() public selectedViewport: plugins.deesDomtools.breakpoints.TViewport = 'desktop'; @property() public selectedTheme: TTheme = 'dark'; @property() public pages: { [key: string]: () => TemplateResult } = {}; @property() public elements: { [key: string]: DeesElement } = {}; @property() public warning: string = null; @property() public frameScrollY: number = 0; @property() public sidebarScrollY: number = 0; private scrollPositionsApplied: boolean = false; @queryAsync('wcc-frame') public wccFrame: Promise; constructor( elementsArg?: { [key: string]: DeesElement }, pagesArg?: { [key: string]: () => TemplateResult } ) { super(); if (elementsArg) { this.elements = elementsArg; console.log('got elements:'); console.log(this.elements); } if (pagesArg) { this.pages = pagesArg; } } public render(): TemplateResult { return html` { this.selectedType = eventArg.detail; }} @selectedItemName=${(eventArg) => { this.selectedItemName = eventArg.detail; }} @selectedItem=${(eventArg) => { this.selectedItem = eventArg.detail; }} > { this.selectedViewport = eventArg.detail; this.scheduleUpdate(); }} @selectedTheme=${(eventArg) => { this.selectedTheme = eventArg.detail; }} > `; } public setWarning(warningTextArg: string) { if (this.warning !== warningTextArg) { console.log(warningTextArg); this.warning = warningTextArg; setTimeout(() => { this.scheduleUpdate(); }, 0); } } public async firstUpdated() { this.domtools = await plugins.deesDomtools.DomTools.setupDomTools(); // Set up scroll listeners after DOM is ready setTimeout(() => { this.setupScrollListeners(); }, 500); this.domtools.router.on( '/wcctools-route/:itemType/:itemName/:viewport/:theme', async (routeInfo) => { this.selectedType = routeInfo.params.itemType as TElementType; this.selectedItemName = routeInfo.params.itemName; 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]; } // Restore scroll positions from query parameters if (routeInfo.queryParams) { const frameScrollY = routeInfo.queryParams.frameScrollY; const sidebarScrollY = routeInfo.queryParams.sidebarScrollY; if (frameScrollY) { this.frameScrollY = parseInt(frameScrollY); } if (sidebarScrollY) { this.sidebarScrollY = parseInt(sidebarScrollY); } // Apply scroll positions after a short delay to ensure DOM is ready setTimeout(() => { this.applyScrollPositions(); }, 100); } const domtoolsInstance = await plugins.deesDomtools.elementBasic.setup(); this.selectedTheme === 'bright' ? domtoolsInstance.themeManager.goBright() : domtoolsInstance.themeManager.goDark(); } ); } public async updated(changedPropertiesArg: Map) { this.domtools = await plugins.deesDomtools.DomTools.setupDomTools(); await this.domtools.router._handleRouteState(); const wccFrame: WccFrame = this.shadowRoot.querySelector('wcc-frame'); if (changedPropertiesArg.has('selectedItemName')) { document.title = this.selectedItemName; }; if (this.selectedType === 'page' && this.selectedItem) { if (typeof this.selectedItem === 'function') { console.log('slotting page.'); const viewport = await wccFrame.getViewportElement(); render(this.selectedItem(), viewport); console.log('rendered page.'); } else { console.error('The selected item looks strange:'); console.log(this.selectedItem); } } else if (this.selectedType === 'element' && this.selectedItem) { console.log('slotting element.'); const anonItem: any = this.selectedItem; if (!anonItem.demo) { this.setWarning(`component ${anonItem.name} does not expose a demo property.`); return; } if (!(typeof anonItem.demo === 'function')) { this.setWarning( `component ${anonItem.name} has demo property, but it is not of type function` ); return; } this.setWarning(null); const viewport = await wccFrame.getViewportElement(); render(anonItem.demo(), viewport);; } } public buildUrl() { const baseUrl = `/wcctools-route/${this.selectedType}/${this.selectedItemName}/${this.selectedViewport}/${this.selectedTheme}`; const queryParams = new URLSearchParams(); if (this.frameScrollY > 0) { queryParams.set('frameScrollY', this.frameScrollY.toString()); } if (this.sidebarScrollY > 0) { queryParams.set('sidebarScrollY', this.sidebarScrollY.toString()); } const queryString = queryParams.toString(); const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl; this.domtools.router.pushUrl(fullUrl); } private scrollUpdateTimeout: NodeJS.Timeout; public async setupScrollListeners() { const wccFrame = await this.wccFrame; const wccSidebar = this.shadowRoot.querySelector('wcc-sidebar'); if (wccFrame) { // The frame element itself is the scrollable container wccFrame.addEventListener('scroll', () => { this.frameScrollY = wccFrame.scrollTop; this.debouncedScrollUpdate(); }); } if (wccSidebar) { // The sidebar element itself is the scrollable container wccSidebar.addEventListener('scroll', () => { this.sidebarScrollY = wccSidebar.scrollTop; this.debouncedScrollUpdate(); }); } } private debouncedScrollUpdate() { clearTimeout(this.scrollUpdateTimeout); this.scrollUpdateTimeout = setTimeout(() => { this.updateUrlWithScrollState(); }, 300); } private updateUrlWithScrollState() { const baseUrl = `/wcctools-route/${this.selectedType}/${this.selectedItemName}/${this.selectedViewport}/${this.selectedTheme}`; const queryParams = new URLSearchParams(); if (this.frameScrollY > 0) { queryParams.set('frameScrollY', this.frameScrollY.toString()); } if (this.sidebarScrollY > 0) { queryParams.set('sidebarScrollY', this.sidebarScrollY.toString()); } const queryString = queryParams.toString(); const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl; // Use replaceState to update URL without navigation window.history.replaceState(null, '', fullUrl); } public async applyScrollPositions() { // Only apply scroll positions once to avoid interfering with user scrolling if (this.scrollPositionsApplied) { return; } const wccFrame = await this.wccFrame; const wccSidebar = this.shadowRoot.querySelector('wcc-sidebar'); if (wccFrame && this.frameScrollY > 0) { // The frame element itself is the scrollable container wccFrame.scrollTop = this.frameScrollY; } if (wccSidebar && this.sidebarScrollY > 0) { // The sidebar element itself is the scrollable container wccSidebar.scrollTop = this.sidebarScrollY; } this.scrollPositionsApplied = true; } }