Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d48cd063c4 | |||
| bb04895be8 | |||
| 54b34b6faa | |||
| 12c85fa4cb | |||
| d90df9717b | |||
| d4b161437b | |||
| ad033c8104 |
BIN
.playwright-mcp/recording-panel.png
Normal file
BIN
.playwright-mcp/recording-panel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
BIN
.playwright-mcp/wcctools-dashboard.png
Normal file
BIN
.playwright-mcp/wcctools-dashboard.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
.playwright-mcp/wcctools-with-element.png
Normal file
BIN
.playwright-mcp/wcctools-with-element.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
18
changelog.md
18
changelog.md
@@ -1,5 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-12-11 - 2.0.1 - fix(@git.zone/tswatch)
|
||||
Bump @git.zone/tswatch devDependency to ^2.3.12
|
||||
|
||||
- Updated devDependency @git.zone/tswatch from ^2.3.11 to ^2.3.12 in package.json
|
||||
|
||||
## 2025-12-11 - 2.0.0 - BREAKING CHANGE(recorder)
|
||||
Remove FFmpeg-based MP4 conversion; simplify recorder/export to WebM and improve recorder/editor robustness
|
||||
|
||||
- Removed FFmpegService and all client-side MP4 conversion logic — exports are now WebM-only (MP4 conversion and related UI/controls removed).
|
||||
- ts_web/elements/wcc-recording-panel: dropped outputFormat and conversion states/UI; download flow simplified to always export WebM.
|
||||
- ts_web/index.ts: removed FFmpegService exports and conversion types from public API.
|
||||
- package.json: removed @ffmpeg/* dependencies.
|
||||
- RecorderService: handleRecordingComplete is now async and fixes recorded blob assignment and cleanup timing.
|
||||
- wcc-properties: improved element detection and robustness — recursive search through light/shadow DOM with retry/delay, plus an advanced JSON editor for Object/Array props (supports multiple open editors and frame resize events).
|
||||
- wcc-sidebar: force re-render after selecting demos to ensure child demo selection indicators update correctly.
|
||||
- dees-demowrapper: ensure slotted content is rendered before calling runAfterRender (small timing/stability improvements).
|
||||
- Test update: demo definitions can be arrays (multiple demos) — test-demoelement updated to use multiple demo entries.
|
||||
|
||||
## 2025-12-11 - 1.3.0 - feat(recording-panel)
|
||||
Add demo wrapper utilities, improve recording trim behavior, and harden property panel element detection; update documentation
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@design.estate/dees-wcctools",
|
||||
"version": "1.3.0",
|
||||
"version": "2.0.1",
|
||||
"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": {
|
||||
@@ -28,7 +28,7 @@
|
||||
"@git.zone/tsbundle": "^2.6.3",
|
||||
"@git.zone/tsrun": "^2.0.0",
|
||||
"@git.zone/tstest": "^3.1.3",
|
||||
"@git.zone/tswatch": "^2.3.10",
|
||||
"@git.zone/tswatch": "^2.3.12",
|
||||
"@push.rocks/projectinfo": "^5.0.2",
|
||||
"@types/node": "^25.0.0"
|
||||
},
|
||||
|
||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@@ -37,8 +37,8 @@ importers:
|
||||
specifier: ^3.1.3
|
||||
version: 3.1.3(@push.rocks/smartserve@1.4.0)(socks@2.8.7)(typescript@5.9.3)
|
||||
'@git.zone/tswatch':
|
||||
specifier: ^2.3.10
|
||||
version: 2.3.10(@tiptap/pm@2.27.1)
|
||||
specifier: ^2.3.12
|
||||
version: 2.3.12(@tiptap/pm@2.27.1)
|
||||
'@push.rocks/projectinfo':
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2
|
||||
@@ -489,8 +489,8 @@ packages:
|
||||
resolution: {integrity: sha512-t+/cKV21JHK8X7NGAmihs5M/eMm+V+jn4R5rzfwGG97WJFAcP5qE1Os9VYtyZw3tx/NZXA2yA4abo/ELluTuRA==}
|
||||
hasBin: true
|
||||
|
||||
'@git.zone/tswatch@2.3.10':
|
||||
resolution: {integrity: sha512-88bdzD15mYoG0T0AUTg8ATNkV/dN5ecqfiYcQRX1gJHmLrE2yqymFGkb0W0/xWgpcRakc08V+wRbSI7pqg+EOQ==}
|
||||
'@git.zone/tswatch@2.3.12':
|
||||
resolution: {integrity: sha512-0GWh9nxaw/wjN9k9ah07rgFM4gjHgn+aYH28IEHdBuQeP6EuiROqwRHmtt6nj8Hv2MOaWGru9wIHCw5qr6ACWA==}
|
||||
hasBin: true
|
||||
|
||||
'@happy-dom/global-registrator@15.11.7':
|
||||
@@ -912,8 +912,8 @@ packages:
|
||||
resolution: {integrity: sha512-M7rMLdcO423JIF7PbMnqy730h4seAx8lXkP3d7yGhIXep2jizPP+KlkdbdkBdaVp7YupcFZiTnu2HY66SKVtpQ==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@push.rocks/smartwatch@6.2.4':
|
||||
resolution: {integrity: sha512-cxGx/RJXSU45cfyJn0DNgXA1jPwmzraJhy+8J8hL2Bjn0K+DxatQRyeIvRVCSLLgBhVTN6yYaUjUtjs19gJLkA==}
|
||||
'@push.rocks/smartwatch@6.2.5':
|
||||
resolution: {integrity: sha512-i9p2ocg2MJ7O5yk/EeZxfG8qrfBX9OrkBQBVPXuDD/7vsyCk2Mi/XRwsDKXdqEYiCdT5uhXZIF+C57QOwgzKPQ==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@push.rocks/smartxml@2.0.0':
|
||||
@@ -4807,7 +4807,7 @@ snapshots:
|
||||
- utf-8-validate
|
||||
- vue
|
||||
|
||||
'@git.zone/tswatch@2.3.10(@tiptap/pm@2.27.1)':
|
||||
'@git.zone/tswatch@2.3.12(@tiptap/pm@2.27.1)':
|
||||
dependencies:
|
||||
'@api.global/typedserver': 7.11.1(@tiptap/pm@2.27.1)
|
||||
'@git.zone/tsbundle': 2.6.3
|
||||
@@ -4820,7 +4820,7 @@ snapshots:
|
||||
'@push.rocks/smartlog': 3.1.10
|
||||
'@push.rocks/smartlog-destination-local': 9.0.2
|
||||
'@push.rocks/smartshell': 3.3.0
|
||||
'@push.rocks/smartwatch': 6.2.4
|
||||
'@push.rocks/smartwatch': 6.2.5
|
||||
'@push.rocks/taskbuffer': 3.5.0
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
@@ -5787,7 +5787,7 @@ snapshots:
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
picomatch: 4.0.3
|
||||
|
||||
'@push.rocks/smartwatch@6.2.4':
|
||||
'@push.rocks/smartwatch@6.2.5':
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartenv': 6.0.0
|
||||
|
||||
@@ -18,7 +18,11 @@ enum ETestEnum {
|
||||
|
||||
@customElement('test-demoelement')
|
||||
export class TestDemoelement extends DeesElement {
|
||||
public static demo = () => html`<test-demoelement>This is a slot text</test-demoelement>`;
|
||||
public static demo = [
|
||||
() => html`<test-demoelement>This is demo 1</test-demoelement>`,
|
||||
() => html`<test-demoelement>This is demo 2</test-demoelement>`,
|
||||
() => html`<test-demoelement>This is demo 2</test-demoelement>`,
|
||||
]
|
||||
|
||||
@property()
|
||||
accessor notTyped = 'hello';
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-wcctools',
|
||||
version: '1.3.0',
|
||||
version: '2.0.1',
|
||||
description: 'A set of web component tools for creating element catalogues, enabling the structured development and documentation of custom elements and pages.'
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DeesElement, property, html, customElement, type TemplateResult, queryAsync, render, domtools } from '@design.estate/dees-element';
|
||||
import { resolveTemplateFactory } from './wcctools.helpers.js';
|
||||
import { resolveTemplateFactory, getDemoAtIndex, getDemoCount, hasMultipleDemos } from './wcctools.helpers.js';
|
||||
import type { TTemplateFactory } from './wcctools.helpers.js';
|
||||
|
||||
import * as plugins from '../wcctools.plugins.js';
|
||||
@@ -25,6 +25,9 @@ export class WccDashboard extends DeesElement {
|
||||
@property()
|
||||
accessor selectedItem: TTemplateFactory | DeesElement;
|
||||
|
||||
@property({ type: Number })
|
||||
accessor selectedDemoIndex: number = 0;
|
||||
|
||||
@property()
|
||||
accessor selectedViewport: plugins.deesDomtools.breakpoints.TViewport = 'desktop';
|
||||
|
||||
@@ -151,11 +154,13 @@ export class WccDashboard extends DeesElement {
|
||||
this.setupScrollListeners();
|
||||
}, 500);
|
||||
|
||||
// Route with demo index (new format)
|
||||
this.domtools.router.on(
|
||||
'/wcctools-route/:itemType/:itemName/:viewport/:theme',
|
||||
'/wcctools-route/:itemType/:itemName/:demoIndex/:viewport/:theme',
|
||||
async (routeInfo) => {
|
||||
this.selectedType = routeInfo.params.itemType as TElementType;
|
||||
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') {
|
||||
@@ -163,25 +168,65 @@ export class WccDashboard extends DeesElement {
|
||||
} 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();
|
||||
}
|
||||
);
|
||||
|
||||
// Legacy route without demo index (for backwards compatibility)
|
||||
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.selectedDemoIndex = 0; // Default to first demo
|
||||
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()
|
||||
@@ -218,33 +263,48 @@ export class WccDashboard extends DeesElement {
|
||||
this.setWarning(`component ${anonItem.name} does not expose a demo property.`);
|
||||
return;
|
||||
}
|
||||
if (!(typeof anonItem.demo === 'function')) {
|
||||
|
||||
// Support both single demo (function) and multiple demos (array)
|
||||
const isArray = Array.isArray(anonItem.demo);
|
||||
const isFunction = typeof anonItem.demo === 'function';
|
||||
|
||||
if (!isArray && !isFunction) {
|
||||
this.setWarning(
|
||||
`component ${anonItem.name} has demo property, but it is not of type function`
|
||||
`component ${anonItem.name} has demo property, but it is not a function or array of functions`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the specific demo to render
|
||||
const demoFactory = getDemoAtIndex(anonItem.demo, this.selectedDemoIndex);
|
||||
if (!demoFactory) {
|
||||
this.setWarning(
|
||||
`component ${anonItem.name} does not have a demo at index ${this.selectedDemoIndex + 1}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setWarning(null);
|
||||
const viewport = await wccFrame.getViewportElement();
|
||||
const demoTemplate = await resolveTemplateFactory(() => anonItem.demo());
|
||||
const demoTemplate = await resolveTemplateFactory(demoFactory);
|
||||
render(demoTemplate, viewport);
|
||||
}
|
||||
}
|
||||
|
||||
public buildUrl() {
|
||||
const baseUrl = `/wcctools-route/${this.selectedType}/${this.selectedItemName}/${this.selectedViewport}/${this.selectedTheme}`;
|
||||
const baseUrl = `/wcctools-route/${this.selectedType}/${this.selectedItemName}/${this.selectedDemoIndex}/${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);
|
||||
}
|
||||
|
||||
@@ -286,19 +346,19 @@ export class WccDashboard extends DeesElement {
|
||||
}
|
||||
|
||||
private updateUrlWithScrollState() {
|
||||
const baseUrl = `/wcctools-route/${this.selectedType}/${this.selectedItemName}/${this.selectedViewport}/${this.selectedTheme}`;
|
||||
const baseUrl = `/wcctools-route/${this.selectedType}/${this.selectedItemName}/${this.selectedDemoIndex}/${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);
|
||||
}
|
||||
|
||||
@@ -552,6 +552,7 @@ export class WccRecordingPanel extends DeesElement {
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
`
|
||||
];
|
||||
|
||||
@@ -706,6 +707,7 @@ export class WccRecordingPanel extends DeesElement {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="preview-modal-actions">
|
||||
<button class="preview-btn secondary" @click=${() => this.discardRecording()}>Discard</button>
|
||||
@@ -714,7 +716,7 @@ export class WccRecordingPanel extends DeesElement {
|
||||
?disabled=${this.isExporting}
|
||||
@click=${() => this.downloadRecording()}
|
||||
>
|
||||
${this.isExporting ? html`<span class="export-spinner"></span>Exporting...` : 'Download'}
|
||||
${this.isExporting ? html`<span class="export-spinner"></span>Exporting...` : 'Download WebM'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -815,6 +817,7 @@ export class WccRecordingPanel extends DeesElement {
|
||||
try {
|
||||
let blobToDownload: Blob;
|
||||
|
||||
// Handle trimming if needed
|
||||
const needsTrim = this.trimStart > 0.1 || this.trimEnd < this.videoDuration - 0.1;
|
||||
|
||||
if (needsTrim) {
|
||||
@@ -828,6 +831,7 @@ export class WccRecordingPanel extends DeesElement {
|
||||
blobToDownload = recordedBlob;
|
||||
}
|
||||
|
||||
// Trigger download
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
||||
const filename = `wcctools-recording-${timestamp}.webm`;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import * as plugins from '../wcctools.plugins.js';
|
||||
import { DeesElement, property, html, customElement, type TemplateResult } from '@design.estate/dees-element';
|
||||
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';
|
||||
import { getDemoCount, hasMultipleDemos } from './wcctools.helpers.js';
|
||||
|
||||
export type TElementType = 'element' | 'page';
|
||||
|
||||
@@ -19,6 +20,10 @@ export class WccSidebar extends DeesElement {
|
||||
@property()
|
||||
accessor isFullscreen: boolean = false;
|
||||
|
||||
// Track which elements are expanded (for multi-demo elements)
|
||||
@state()
|
||||
accessor expandedElements: Set<string> = new Set();
|
||||
|
||||
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" />
|
||||
@@ -110,7 +115,21 @@ export class WccSidebar extends DeesElement {
|
||||
color: #999;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
.selectOption.folder {
|
||||
grid-template-columns: 16px 20px 1fr;
|
||||
}
|
||||
|
||||
.selectOption .expand-icon {
|
||||
font-size: 14px;
|
||||
opacity: 0.5;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.selectOption.expanded .expand-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.selectOption:hover {
|
||||
background: rgba(59, 130, 246, 0.05);
|
||||
color: #bbb;
|
||||
@@ -143,6 +162,42 @@ export class WccSidebar extends DeesElement {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.demo-children {
|
||||
margin-left: 1rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.demo-child {
|
||||
user-select: none;
|
||||
position: relative;
|
||||
margin: 0.125rem 0.5rem;
|
||||
padding: 0.35rem 0.75rem;
|
||||
transition: all 0.15s ease;
|
||||
display: grid;
|
||||
grid-template-columns: 16px 1fr;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
font-size: 0.7rem;
|
||||
color: #777;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.demo-child:hover {
|
||||
background: rgba(59, 130, 246, 0.05);
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.demo-child.selected {
|
||||
background: rgba(59, 130, 246, 0.15);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.demo-child .material-symbols-outlined {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
@@ -171,7 +226,7 @@ export class WccSidebar extends DeesElement {
|
||||
class="selectOption ${this.selectedItem === item ? 'selected' : null}"
|
||||
@click=${async () => {
|
||||
const domtools = await plugins.deesDomtools.DomTools.setupDomTools();
|
||||
this.selectItem('page', pageName, item);
|
||||
this.selectItem('page', pageName, item, 0);
|
||||
}}
|
||||
>
|
||||
<i class="material-symbols-outlined">insert_drive_file</i>
|
||||
@@ -184,31 +239,83 @@ export class WccSidebar extends DeesElement {
|
||||
${(() => {
|
||||
const elements = Object.keys(this.dashboardRef.elements);
|
||||
return elements.map(elementName => {
|
||||
const item = this.dashboardRef.elements[elementName];
|
||||
return html`
|
||||
<div
|
||||
class="selectOption ${this.selectedItem === item ? 'selected' : null}"
|
||||
@click=${async () => {
|
||||
const domtools = await plugins.deesDomtools.DomTools.setupDomTools();
|
||||
this.selectItem('element', elementName, item);
|
||||
}}
|
||||
>
|
||||
<i class="material-symbols-outlined">featured_video</i>
|
||||
<div class="text">${elementName}</div>
|
||||
</div>
|
||||
`;
|
||||
const item = this.dashboardRef.elements[elementName] as any;
|
||||
const demoCount = item.demo ? getDemoCount(item.demo) : 0;
|
||||
const isMultiDemo = item.demo && hasMultipleDemos(item.demo);
|
||||
const isExpanded = this.expandedElements.has(elementName);
|
||||
const isSelected = this.selectedItem === item;
|
||||
|
||||
if (isMultiDemo) {
|
||||
// Multi-demo element - render as expandable folder
|
||||
return html`
|
||||
<div
|
||||
class="selectOption folder ${isExpanded ? 'expanded' : ''} ${isSelected ? 'selected' : ''}"
|
||||
@click=${() => this.toggleExpanded(elementName)}
|
||||
>
|
||||
<i class="material-symbols-outlined expand-icon">chevron_right</i>
|
||||
<i class="material-symbols-outlined">folder</i>
|
||||
<div class="text">${elementName}</div>
|
||||
</div>
|
||||
${isExpanded ? html`
|
||||
<div class="demo-children">
|
||||
${Array.from({ length: demoCount }, (_, i) => {
|
||||
const demoIndex = i;
|
||||
const isThisDemoSelected = isSelected && this.dashboardRef.selectedDemoIndex === demoIndex;
|
||||
return html`
|
||||
<div
|
||||
class="demo-child ${isThisDemoSelected ? 'selected' : ''}"
|
||||
@click=${async () => {
|
||||
await plugins.deesDomtools.DomTools.setupDomTools();
|
||||
this.selectItem('element', elementName, item, demoIndex);
|
||||
}}
|
||||
>
|
||||
<i class="material-symbols-outlined">play_circle</i>
|
||||
<div class="text">demo${demoIndex + 1}</div>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
` : null}
|
||||
`;
|
||||
} else {
|
||||
// Single demo element - render as normal
|
||||
return html`
|
||||
<div
|
||||
class="selectOption ${isSelected ? 'selected' : null}"
|
||||
@click=${async () => {
|
||||
await plugins.deesDomtools.DomTools.setupDomTools();
|
||||
this.selectItem('element', elementName, item, 0);
|
||||
}}
|
||||
>
|
||||
<i class="material-symbols-outlined">featured_video</i>
|
||||
<div class="text">${elementName}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
});
|
||||
})()}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
public selectItem(typeArg: TElementType, itemNameArg: string, itemArg: TTemplateFactory | DeesElement) {
|
||||
private toggleExpanded(elementName: string) {
|
||||
const newSet = new Set(this.expandedElements);
|
||||
if (newSet.has(elementName)) {
|
||||
newSet.delete(elementName);
|
||||
} else {
|
||||
newSet.add(elementName);
|
||||
}
|
||||
this.expandedElements = newSet;
|
||||
}
|
||||
|
||||
public selectItem(typeArg: TElementType, itemNameArg: string, itemArg: TTemplateFactory | DeesElement, demoIndex: number = 0) {
|
||||
console.log('selected item');
|
||||
console.log(itemNameArg);
|
||||
console.log(itemArg);
|
||||
console.log('demo index:', demoIndex);
|
||||
this.selectedItem = itemArg;
|
||||
this.selectedType = typeArg;
|
||||
this.dashboardRef.selectedDemoIndex = demoIndex;
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('selectedType', {
|
||||
detail: typeArg
|
||||
@@ -224,7 +331,11 @@ export class WccSidebar extends DeesElement {
|
||||
detail: itemArg
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
this.dashboardRef.buildUrl();
|
||||
|
||||
// Force re-render to update demo child selection indicator
|
||||
// (needed when switching between demos of the same element)
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,39 @@ import type { TemplateResult } from 'lit';
|
||||
|
||||
export type TTemplateFactory = () => TemplateResult | Promise<TemplateResult>;
|
||||
|
||||
// Demo can be a single function or an array of functions
|
||||
export type TDemoDefinition = TTemplateFactory | TTemplateFactory[];
|
||||
|
||||
export const resolveTemplateFactory = async (
|
||||
factoryArg: TTemplateFactory
|
||||
): Promise<TemplateResult> => {
|
||||
return await Promise.resolve(factoryArg());
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of demos for an element
|
||||
*/
|
||||
export const getDemoCount = (demo: TDemoDefinition): number => {
|
||||
if (Array.isArray(demo)) {
|
||||
return demo.length;
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a specific demo by index (0-based internally, displayed as 1-based)
|
||||
*/
|
||||
export const getDemoAtIndex = (demo: TDemoDefinition, index: number): TTemplateFactory | null => {
|
||||
if (Array.isArray(demo)) {
|
||||
return demo[index] ?? null;
|
||||
}
|
||||
// Single demo - only index 0 is valid
|
||||
return index === 0 ? demo : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if an element has multiple demos
|
||||
*/
|
||||
export const hasMultipleDemos = (demo: TDemoDefinition): boolean => {
|
||||
return Array.isArray(demo) && demo.length > 1;
|
||||
};
|
||||
|
||||
@@ -235,9 +235,11 @@ export class RecorderService {
|
||||
}
|
||||
}
|
||||
|
||||
private handleRecordingComplete(): void {
|
||||
private async handleRecordingComplete(): Promise<void> {
|
||||
// Create blob from recorded chunks
|
||||
this._recordedBlob = new Blob(this.recordedChunks, { type: 'video/webm' });
|
||||
const blob = new Blob(this.recordedChunks, { type: 'video/webm' });
|
||||
|
||||
this._recordedBlob = blob;
|
||||
|
||||
// Stop all tracks
|
||||
if (this.currentStream) {
|
||||
|
||||
Reference in New Issue
Block a user