Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
4c23739d9a | |||
dd048d42a8 | |||
ca28dbd9db | |||
7148b12066 | |||
309d708830 |
11
changelog.md
11
changelog.md
@@ -1,5 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-09-19 - 1.2.0 - feat(wcc-properties)
|
||||
Add advanced property editors, recursive element detection, demo wrapper, UI refresh and test fixtures
|
||||
|
||||
- Advanced JSON property editor: multiple side-by-side editors with save/cancel, syntax validation and inline error display; editors affect frame layout (frame bottom increases when editors open).
|
||||
- Improved properties panel element detection: recursive search through nested children and shadow DOM, initial delay and retry mechanism to handle async Lit rendering.
|
||||
- Add dees-demowrapper component in ts_demotools to run post-render callbacks and support async demo setup and DOM access for demos.
|
||||
- UI refresh with shadcn-like styles: CSS variables for theming, redesigned properties panel and sidebar, improved form controls, theme and viewport selectors.
|
||||
- Viewport and frame improvements: responsive padding based on viewport type, theme-aware background rendering, and scroll position tracking with URL/state restoration for frame and sidebar.
|
||||
- Add test fixtures and demo elements/pages under test/ to exercise properties, complex types, nested elements and scroll restoration; include node test for resolveTemplateFactory.
|
||||
- Expose setupWccTools entry point and plugin wiring (wcctools.plugins exports for dees-domtools and smartdelay) for easier integration.
|
||||
|
||||
## 2025-06-27 - 1.1.0 - feat(wcctools)
|
||||
Enhance component tools with an advanced property editor, improved element detection and modernized UI styling for a more responsive dashboard experience.
|
||||
|
||||
|
20
package.json
20
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@design.estate/dees-wcctools",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"private": false,
|
||||
"description": "A set of web component tools for creating element catalogues, enabling the structured development and documentation of custom elements and pages.",
|
||||
"exports": {
|
||||
@@ -17,18 +17,20 @@
|
||||
"author": "Lossless GmbH",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@design.estate/dees-domtools": "^2.0.57",
|
||||
"@design.estate/dees-element": "^2.0.34",
|
||||
"@design.estate/dees-domtools": "^2.3.3",
|
||||
"@design.estate/dees-element": "^2.1.2",
|
||||
"@push.rocks/smartdelay": "^3.0.5",
|
||||
"lit": "^3.1.3"
|
||||
"lit": "^3.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@api.global/typedserver": "^3.0.29",
|
||||
"@git.zone/tsbuild": "^2.1.72",
|
||||
"@git.zone/tsbundle": "^2.0.15",
|
||||
"@api.global/typedserver": "^3.0.79",
|
||||
"@git.zone/tsbuild": "^2.6.8",
|
||||
"@git.zone/tsbundle": "^2.5.1",
|
||||
"@git.zone/tsrun": "^1.2.44",
|
||||
"@git.zone/tswatch": "^2.0.23",
|
||||
"@push.rocks/projectinfo": "^5.0.2"
|
||||
"@git.zone/tstest": "^2.3.8",
|
||||
"@git.zone/tswatch": "^2.2.1",
|
||||
"@push.rocks/projectinfo": "^5.0.2",
|
||||
"@types/node": "^22.18.6"
|
||||
},
|
||||
"files": [
|
||||
"ts/**/*",
|
||||
|
5624
pnpm-lock.yaml
generated
5624
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
4
pnpm-workspace.yaml
Normal file
4
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
- mongodb-memory-server
|
||||
- puppeteer
|
15
readme.md
15
readme.md
@@ -211,6 +211,19 @@ export class MyComponent extends DeesElement {
|
||||
}
|
||||
```
|
||||
|
||||
### ⏳ Async Demos
|
||||
|
||||
If your catalogue needs additional setup before rendering, return a `Promise` from the `demo` function. The dashboard waits for the result before inserting it into the viewport:
|
||||
|
||||
```typescript
|
||||
public static demo = async () => {
|
||||
await Promise.resolve(); // e.g. fetch data, load fixtures, or await wrappers
|
||||
return html`<my-component .value=${'Loaded asynchronously'}></my-component>`;
|
||||
};
|
||||
```
|
||||
|
||||
The same pattern works for page factories you pass into `setupWccTools`, enabling asynchronous data preparation across the entire demo surface.
|
||||
|
||||
### 🎭 Container Queries Support
|
||||
|
||||
Components can respond to their container size:
|
||||
@@ -389,4 +402,4 @@ Registered at District court Bremen HRB 35230 HB, Germany
|
||||
|
||||
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
||||
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||
|
@@ -97,4 +97,11 @@ Properties panel was overwriting values set by demo functions
|
||||
2. This prevents browser from firing input events during initialization
|
||||
3. Added proper number parsing for number inputs
|
||||
4. Increased initial wait to 200ms for demo wrappers to complete
|
||||
5. Simplified select element handling to use property binding
|
||||
5. Simplified select element handling to use property binding
|
||||
# Async Demo Support (IN PROGRESS)
|
||||
|
||||
## Tasks
|
||||
- [ ] Allow dashboard-selected items to return Promise-based TemplateResults
|
||||
- [ ] Await async demos/pages before rendering them into the viewport
|
||||
- [ ] Add regression test covering async demo usage
|
||||
- [ ] Document async demo pattern in README and verify with pnpm scripts
|
||||
|
22
test/test.demoresolver.node.ts
Normal file
22
test/test.demoresolver.node.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import { resolveTemplateFactory } from '../ts_web/elements/wcctools.helpers.js';
|
||||
import { html } from 'lit';
|
||||
|
||||
const waitFor = (durationMs: number) => new Promise(resolve => setTimeout(resolve, durationMs));
|
||||
|
||||
tap.test('resolveTemplateFactory returns sync TemplateResult', async () => {
|
||||
const template = html`<p>sync demo</p>`;
|
||||
const resolvedTemplate = await resolveTemplateFactory(() => template);
|
||||
expect(resolvedTemplate).toEqual(template);
|
||||
});
|
||||
|
||||
tap.test('resolveTemplateFactory awaits async TemplateResult', async () => {
|
||||
const template = html`<p>async demo</p>`;
|
||||
const resolvedTemplate = await resolveTemplateFactory(async () => {
|
||||
await waitFor(5);
|
||||
return template;
|
||||
});
|
||||
expect(resolvedTemplate).toEqual(template);
|
||||
});
|
||||
|
||||
export default tap.start();
|
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-wcctools',
|
||||
version: '1.1.0',
|
||||
version: '1.2.0',
|
||||
description: 'A set of web component tools for creating element catalogues, enabling the structured development and documentation of custom elements and pages.'
|
||||
}
|
||||
|
@@ -1,4 +1,6 @@
|
||||
import { DeesElement, property, html, customElement, type TemplateResult, queryAsync, render, domtools } from '@design.estate/dees-element';
|
||||
import { resolveTemplateFactory } from './wcctools.helpers.js';
|
||||
import type { TTemplateFactory } from './wcctools.helpers.js';
|
||||
|
||||
import * as plugins from '../wcctools.plugins.js';
|
||||
|
||||
@@ -21,7 +23,7 @@ export class WccDashboard extends DeesElement {
|
||||
public selectedItemName: string;
|
||||
|
||||
@property()
|
||||
public selectedItem: (() => TemplateResult) | DeesElement;
|
||||
public selectedItem: TTemplateFactory | DeesElement;
|
||||
|
||||
@property()
|
||||
public selectedViewport: plugins.deesDomtools.breakpoints.TViewport = 'desktop';
|
||||
@@ -30,7 +32,10 @@ export class WccDashboard extends DeesElement {
|
||||
public selectedTheme: TTheme = 'dark';
|
||||
|
||||
@property()
|
||||
public pages: { [key: string]: () => TemplateResult } = {};
|
||||
public isFullscreen: boolean = false;
|
||||
|
||||
@property()
|
||||
public pages: Record<string, TTemplateFactory> = {};
|
||||
|
||||
@property()
|
||||
public elements: { [key: string]: DeesElement } = {};
|
||||
@@ -47,7 +52,7 @@ export class WccDashboard extends DeesElement {
|
||||
|
||||
constructor(
|
||||
elementsArg?: { [key: string]: DeesElement },
|
||||
pagesArg?: { [key: string]: () => TemplateResult }
|
||||
pagesArg?: Record<string, TTemplateFactory>
|
||||
) {
|
||||
super();
|
||||
if (elementsArg) {
|
||||
@@ -76,6 +81,7 @@ export class WccDashboard extends DeesElement {
|
||||
<wcc-sidebar
|
||||
.dashboardRef=${this}
|
||||
.selectedItem=${this.selectedItem}
|
||||
.isFullscreen=${this.isFullscreen}
|
||||
@selectedType=${(eventArg) => {
|
||||
this.selectedType = eventArg.detail;
|
||||
}}
|
||||
@@ -92,6 +98,7 @@ export class WccDashboard extends DeesElement {
|
||||
.selectedItem=${this.selectedItem}
|
||||
.selectedViewport=${this.selectedViewport}
|
||||
.selectedTheme=${this.selectedTheme}
|
||||
.isFullscreen=${this.isFullscreen}
|
||||
@selectedViewport=${(eventArg) => {
|
||||
this.selectedViewport = eventArg.detail;
|
||||
this.scheduleUpdate();
|
||||
@@ -106,8 +113,11 @@ export class WccDashboard extends DeesElement {
|
||||
frame.requestUpdate();
|
||||
}
|
||||
}}
|
||||
@toggleFullscreen=${() => {
|
||||
this.toggleFullscreen();
|
||||
}}
|
||||
></wcc-properties>
|
||||
<wcc-frame id="wccFrame" viewport=${this.selectedViewport}>
|
||||
<wcc-frame id="wccFrame" viewport=${this.selectedViewport} .isFullscreen=${this.isFullscreen}>
|
||||
</wcc-frame>
|
||||
`;
|
||||
}
|
||||
@@ -122,9 +132,20 @@ export class WccDashboard extends DeesElement {
|
||||
}
|
||||
}
|
||||
|
||||
public toggleFullscreen() {
|
||||
this.isFullscreen = !this.isFullscreen;
|
||||
}
|
||||
|
||||
public async firstUpdated() {
|
||||
this.domtools = await plugins.deesDomtools.DomTools.setupDomTools();
|
||||
|
||||
// Add ESC key handler for fullscreen mode
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape' && this.isFullscreen) {
|
||||
this.isFullscreen = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Set up scroll listeners after DOM is ready
|
||||
setTimeout(() => {
|
||||
this.setupScrollListeners();
|
||||
@@ -182,7 +203,9 @@ export class WccDashboard extends DeesElement {
|
||||
if (typeof this.selectedItem === 'function') {
|
||||
console.log('slotting page.');
|
||||
const viewport = await wccFrame.getViewportElement();
|
||||
render(this.selectedItem(), viewport);
|
||||
const pageFactory = this.selectedItem as TTemplateFactory;
|
||||
const pageTemplate = await resolveTemplateFactory(pageFactory);
|
||||
render(pageTemplate, viewport);
|
||||
console.log('rendered page.');
|
||||
} else {
|
||||
console.error('The selected item looks strange:');
|
||||
@@ -203,7 +226,8 @@ export class WccDashboard extends DeesElement {
|
||||
}
|
||||
this.setWarning(null);
|
||||
const viewport = await wccFrame.getViewportElement();
|
||||
render(anonItem.demo(), viewport);;
|
||||
const demoTemplate = await resolveTemplateFactory(() => anonItem.demo());
|
||||
render(demoTemplate, viewport);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -16,6 +16,9 @@ export class WccFrame extends DeesElement {
|
||||
@property({ type: Boolean })
|
||||
public advancedEditorOpen: boolean = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public isFullscreen: boolean = false;
|
||||
|
||||
public static styles = [
|
||||
css`
|
||||
:host {
|
||||
@@ -43,9 +46,19 @@ export class WccFrame extends DeesElement {
|
||||
return html`
|
||||
<style>
|
||||
:host {
|
||||
bottom: ${this.advancedEditorOpen ? '400px' : '100px'};
|
||||
transition: bottom 0.3s ease;
|
||||
${(() => {
|
||||
${this.isFullscreen ? `
|
||||
border: none !important;
|
||||
left: 0px !important;
|
||||
right: 0px !important;
|
||||
top: 0px !important;
|
||||
bottom: 0px !important;
|
||||
` : `
|
||||
bottom: ${this.advancedEditorOpen ? '400px' : '100px'};
|
||||
border: 10px solid #ffaeaf;
|
||||
left: 200px;
|
||||
`}
|
||||
transition: all 0.3s ease;
|
||||
${this.isFullscreen ? 'padding: 0px;' : (() => {
|
||||
switch (this.viewport) {
|
||||
case 'desktop':
|
||||
return `
|
||||
@@ -74,7 +87,7 @@ export class WccFrame extends DeesElement {
|
||||
}
|
||||
|
||||
.viewport {
|
||||
${this.viewport !== 'desktop'
|
||||
${!this.isFullscreen && this.viewport !== 'desktop'
|
||||
? html` border-right: 1px dotted #444; border-left: 1px dotted #444; `
|
||||
: html``
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { DeesElement, property, html, customElement, type TemplateResult, state } from '@design.estate/dees-element';
|
||||
import { WccDashboard } from './wcc-dashboard.js';
|
||||
import type { TTemplateFactory } from './wcctools.helpers.js';
|
||||
|
||||
export type TPropertyType = 'String' | 'Number' | 'Boolean' | 'Object' | 'Enum' | 'Array';
|
||||
|
||||
@@ -20,7 +21,7 @@ export class WccProperties extends DeesElement {
|
||||
public dashboardRef: WccDashboard;
|
||||
|
||||
@property()
|
||||
public selectedItem: (() => TemplateResult) | DeesElement;
|
||||
public selectedItem: TTemplateFactory | DeesElement;
|
||||
|
||||
@property()
|
||||
public selectedViewport: TEnvironment = 'native';
|
||||
@@ -31,6 +32,9 @@ export class WccProperties extends DeesElement {
|
||||
@property()
|
||||
public warning: string = null;
|
||||
|
||||
@property()
|
||||
public isFullscreen: boolean = false;
|
||||
|
||||
@state()
|
||||
propertyContent: TemplateResult[] = [];
|
||||
|
||||
@@ -80,6 +84,7 @@ export class WccProperties extends DeesElement {
|
||||
overflow: hidden;
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
display: ${this.isFullscreen ? 'none' : 'block'};
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
@@ -644,7 +649,11 @@ export class WccProperties extends DeesElement {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="docs">Docs</div>
|
||||
<div class="docs" @click=${() => this.toggleFullscreen()}>
|
||||
<i class="material-symbols-outlined" style="font-size: 20px;">
|
||||
${this.isFullscreen ? 'fullscreen_exit' : 'fullscreen'}
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
${this.warning ? html`<div class="warning">${this.warning}</div>` : null}
|
||||
</div>
|
||||
@@ -977,4 +986,12 @@ export class WccProperties extends DeesElement {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private toggleFullscreen() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('toggleFullscreen', {
|
||||
bubbles: true
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,14 @@
|
||||
import * as plugins from '../wcctools.plugins.js';
|
||||
import { DeesElement, property, html, customElement, type TemplateResult } from '@design.estate/dees-element';
|
||||
import { WccDashboard } from './wcc-dashboard.js';
|
||||
import type { TTemplateFactory } from './wcctools.helpers.js';
|
||||
|
||||
export type TElementType = 'element' | 'page';
|
||||
|
||||
@customElement('wcc-sidebar')
|
||||
export class WccSidebar extends DeesElement {
|
||||
@property({ attribute: false })
|
||||
public selectedItem: DeesElement | (() => TemplateResult);
|
||||
public selectedItem: DeesElement | TTemplateFactory;
|
||||
|
||||
@property({ attribute: false })
|
||||
public selectedType: TElementType;
|
||||
@@ -15,6 +16,9 @@ export class WccSidebar extends DeesElement {
|
||||
@property()
|
||||
public dashboardRef: WccDashboard;
|
||||
|
||||
@property()
|
||||
public isFullscreen: boolean = false;
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet" />
|
||||
@@ -36,7 +40,7 @@ export class WccSidebar extends DeesElement {
|
||||
--ring: #3b82f6;
|
||||
--radius: 4px;
|
||||
|
||||
display: block;
|
||||
display: ${this.isFullscreen ? 'none' : 'block'};
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.08);
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||
font-size: 14px;
|
||||
@@ -199,7 +203,7 @@ export class WccSidebar extends DeesElement {
|
||||
`;
|
||||
}
|
||||
|
||||
public selectItem(typeArg: TElementType, itemNameArg: string, itemArg: (() => TemplateResult) | DeesElement) {
|
||||
public selectItem(typeArg: TElementType, itemNameArg: string, itemArg: TTemplateFactory | DeesElement) {
|
||||
console.log('selected item');
|
||||
console.log(itemNameArg);
|
||||
console.log(itemArg);
|
||||
|
9
ts_web/elements/wcctools.helpers.ts
Normal file
9
ts_web/elements/wcctools.helpers.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { TemplateResult } from 'lit';
|
||||
|
||||
export type TTemplateFactory = () => TemplateResult | Promise<TemplateResult>;
|
||||
|
||||
export const resolveTemplateFactory = async (
|
||||
factoryArg: TTemplateFactory
|
||||
): Promise<TemplateResult> => {
|
||||
return await Promise.resolve(factoryArg());
|
||||
};
|
@@ -1,7 +1,11 @@
|
||||
import { WccDashboard } from './elements/wcc-dashboard.js';
|
||||
import { LitElement, type TemplateResult } from 'lit';
|
||||
import { LitElement } from 'lit';
|
||||
import type { TTemplateFactory } from './elements/wcctools.helpers.js';
|
||||
|
||||
const setupWccTools = (elementsArg?: { [key: string]: LitElement }, pagesArg?: { [key: string]: () => TemplateResult }) => {
|
||||
const setupWccTools = (
|
||||
elementsArg?: { [key: string]: LitElement },
|
||||
pagesArg?: Record<string, TTemplateFactory>
|
||||
) => {
|
||||
let hasRun = false;
|
||||
const runWccToolsSetup = async () => {
|
||||
if (document.readyState === 'complete' && !hasRun) {
|
||||
|
Reference in New Issue
Block a user