feat(recording-panel): Add demo wrapper utilities, improve recording trim behavior, and harden property panel element detection; update documentation
This commit is contained in:
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 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
|
||||
|
||||
- Add dees-demowrapper (ts_demotools) with runAfterRender callback to run post-render demo logic (supports async callbacks).
|
||||
- Improve recording UI and trimming: handle WebM files with Infinity/NaN durations by falling back to tracked recording duration; replace numeric handle positioning with CSS calc strings for responsive trim handles.
|
||||
- Harden property extraction: implement recursive element search (including shadowRoots), add an initial delay and retry loop to wait for demo rendering, and add an advanced JSON editor for Object/Array properties with open/save/cancel and per-editor error reporting.
|
||||
- Add and expand documentation: new ts_web/ and ts_demotools/ READMEs, reorganized main README with clearer feature list, usage examples, and API reference.
|
||||
- Minor exports and module/docs housekeeping (index exports, readme reorder, examples updated to import classes).
|
||||
|
||||
## 2025-11-16 - 1.2.1 - fix(dependencies)
|
||||
Bump dependencies and developer tooling versions
|
||||
|
||||
|
||||
246
readme.md
246
readme.md
@@ -1,14 +1,18 @@
|
||||
# @design.estate/dees-wcctools
|
||||
Web Component Development Tools - A powerful framework for building, testing, and documenting web components
|
||||
|
||||
🛠️ **Web Component Development Tools** — A powerful framework for building, testing, documenting, and recording web components
|
||||
|
||||
## Overview
|
||||
|
||||
`@design.estate/dees-wcctools` provides a comprehensive development environment for web components, featuring:
|
||||
- 🎨 Interactive component catalogue with live preview
|
||||
- 🔧 Real-time property editing
|
||||
- 🌓 Theme switching (light/dark modes)
|
||||
- 📱 Responsive viewport testing
|
||||
- 🧪 Advanced demo tools for component testing
|
||||
- 🚀 Zero-config setup with TypeScript and Lit support
|
||||
|
||||
- 🎨 **Interactive Component Catalogue** — Live preview with sidebar navigation
|
||||
- 🔧 **Real-time Property Editing** — Modify component props on the fly with auto-detected editors
|
||||
- 🌓 **Theme Switching** — Test light/dark modes instantly
|
||||
- 📱 **Responsive Viewport Testing** — Phone, phablet, tablet, and desktop views
|
||||
- 🎬 **Screen Recording** — Record component demos with audio support and video trimming
|
||||
- 🧪 **Advanced Demo Tools** — Post-render hooks for interactive testing
|
||||
- 🚀 **Zero-config Setup** — TypeScript and Lit support out of the box
|
||||
|
||||
## Issue Reporting and Security
|
||||
|
||||
@@ -17,11 +21,11 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Using npm
|
||||
npm install @design.estate/dees-wcctools --save-dev
|
||||
|
||||
# Using pnpm (recommended)
|
||||
pnpm add -D @design.estate/dees-wcctools
|
||||
|
||||
# Using npm
|
||||
npm install @design.estate/dees-wcctools --save-dev
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
@@ -89,8 +93,8 @@ import { setupWccTools } from '@design.estate/dees-wcctools';
|
||||
import { html } from 'lit';
|
||||
|
||||
// Import your components
|
||||
import './components/my-button.js';
|
||||
import './components/my-card.js';
|
||||
import { MyButton } from './components/my-button.js';
|
||||
import { MyCard } from './components/my-card.js';
|
||||
|
||||
// Define elements for the catalogue
|
||||
const elements = {
|
||||
@@ -136,21 +140,28 @@ setupWccTools(elements, pages);
|
||||
## Features
|
||||
|
||||
### 🎯 Live Property Editing
|
||||
|
||||
The properties panel automatically detects and allows editing of:
|
||||
- **String** properties with text inputs
|
||||
- **Number** properties with number inputs
|
||||
- **Boolean** properties with checkboxes
|
||||
- **Enum** properties with select dropdowns
|
||||
- **Object** and **Array** properties (read-only display)
|
||||
|
||||
| Property Type | Editor |
|
||||
|--------------|--------|
|
||||
| **String** | Text input |
|
||||
| **Number** | Number input |
|
||||
| **Boolean** | Checkbox |
|
||||
| **Enum** | Select dropdown |
|
||||
| **Object/Array** | JSON editor modal |
|
||||
|
||||
### 📱 Viewport Testing
|
||||
|
||||
Test your components across different screen sizes:
|
||||
- **Phone** (320px width)
|
||||
- **Phablet** (600px width)
|
||||
- **Tablet** (768px width)
|
||||
- **Desktop** (full width)
|
||||
|
||||
- **Phone** — 320px width
|
||||
- **Phablet** — 600px width
|
||||
- **Tablet** — 768px width
|
||||
- **Desktop** — Full width (native)
|
||||
|
||||
### 🌓 Theme Support
|
||||
|
||||
Components automatically adapt to light/dark themes using the `goBright` property:
|
||||
|
||||
```typescript
|
||||
@@ -163,7 +174,8 @@ public render() {
|
||||
}
|
||||
```
|
||||
|
||||
Or use CSS custom properties:
|
||||
Or use CSS custom properties with the theme manager:
|
||||
|
||||
```typescript
|
||||
import { cssManager } from '@design.estate/dees-element';
|
||||
|
||||
@@ -177,39 +189,44 @@ public static styles = [
|
||||
];
|
||||
```
|
||||
|
||||
### 🧪 Advanced Demo Tools
|
||||
### 🎬 Screen Recording
|
||||
|
||||
The demo tools provide enhanced testing capabilities:
|
||||
Record component demos directly from the catalogue! The built-in recorder supports:
|
||||
|
||||
- **Viewport Recording** — Record just the component viewport
|
||||
- **Full Screen Recording** — Capture the entire screen
|
||||
- **Audio Support** — Add microphone commentary with live level monitoring
|
||||
- **Video Trimming** — Trim start/end before export with visual timeline
|
||||
- **WebM Export** — High-quality video output
|
||||
|
||||
Click the red record button in the bottom toolbar to start.
|
||||
|
||||
### 🧪 Demo Tools
|
||||
|
||||
The demotools module provides enhanced testing capabilities with `dees-demowrapper`:
|
||||
|
||||
```typescript
|
||||
import * as demoTools from '@design.estate/dees-wcctools/demotools';
|
||||
import '@design.estate/dees-wcctools/demotools';
|
||||
|
||||
@customElement('my-component')
|
||||
export class MyComponent extends DeesElement {
|
||||
public static demo = () => html`
|
||||
<dees-demowrapper .runAfterRender=${async (wrapper) => {
|
||||
// Use querySelector to find specific elements
|
||||
const myComponent = wrapper.querySelector('my-component') as MyComponent;
|
||||
console.log('Component found:', myComponent);
|
||||
|
||||
// Access all children via wrapper.children
|
||||
console.log('Total children:', wrapper.children.length);
|
||||
|
||||
// Use querySelectorAll for multiple elements
|
||||
const allDivs = wrapper.querySelectorAll('div');
|
||||
console.log('Found divs:', allDivs.length);
|
||||
|
||||
// Find elements using standard DOM APIs
|
||||
const myComponent = wrapper.querySelector('my-component');
|
||||
|
||||
// Simulate user interactions
|
||||
myComponent.value = 'Test value';
|
||||
await myComponent.updateComplete;
|
||||
|
||||
// Work with all children
|
||||
Array.from(wrapper.children).forEach((child, index) => {
|
||||
console.log(`Child ${index}:`, child.tagName);
|
||||
|
||||
// Work with multiple elements
|
||||
wrapper.querySelectorAll('.item').forEach((el, i) => {
|
||||
console.log(`Item ${i}:`, el.textContent);
|
||||
});
|
||||
}}>
|
||||
<my-component></my-component>
|
||||
<div>Additional content</div>
|
||||
<div class="item">Item 1</div>
|
||||
<div class="item">Item 2</div>
|
||||
</dees-demowrapper>
|
||||
`;
|
||||
}
|
||||
@@ -217,20 +234,18 @@ 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:
|
||||
Return a `Promise` from `demo` for async setup. The dashboard waits for resolution:
|
||||
|
||||
```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>`;
|
||||
const data = await fetchSomeData();
|
||||
return html`<my-component .data=${data}></my-component>`;
|
||||
};
|
||||
```
|
||||
|
||||
The same pattern works for page factories you pass into `setupWccTools`, enabling asynchronous data preparation across the entire demo surface.
|
||||
### 🎭 Container Queries
|
||||
|
||||
### 🎭 Container Queries Support
|
||||
|
||||
Components can respond to their container size:
|
||||
Components can respond to their container size using the `wccToolsViewport` container:
|
||||
|
||||
```typescript
|
||||
public static styles = [
|
||||
@@ -240,7 +255,7 @@ public static styles = [
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@container wccToolsViewport (max-width: 767px) {
|
||||
:host {
|
||||
flex-direction: column;
|
||||
@@ -253,11 +268,13 @@ public static styles = [
|
||||
## Component Guidelines
|
||||
|
||||
### Required for Catalogue Display
|
||||
|
||||
1. Components must expose a static `demo` property returning a Lit template
|
||||
2. Use `@property()` decorators for properties you want to be editable
|
||||
2. Use `@property()` decorators with the `accessor` keyword for editable properties
|
||||
3. Export component classes for proper detection
|
||||
|
||||
### Best Practices
|
||||
|
||||
```typescript
|
||||
@customElement('best-practice-component')
|
||||
export class BestPracticeComponent extends DeesElement {
|
||||
@@ -269,7 +286,7 @@ export class BestPracticeComponent extends DeesElement {
|
||||
></best-practice-component>
|
||||
`;
|
||||
|
||||
// ✅ Typed properties with defaults (TC39 decorator syntax with accessor)
|
||||
// ✅ Typed properties with defaults (TC39 decorators)
|
||||
@property({ type: String })
|
||||
accessor title: string = 'Default Title';
|
||||
|
||||
@@ -286,28 +303,59 @@ export class BestPracticeComponent extends DeesElement {
|
||||
## URL Routing
|
||||
|
||||
The catalogue uses URL routing for deep linking:
|
||||
|
||||
```
|
||||
/wcctools-route/:type/:name/:viewport/:theme
|
||||
|
||||
Example:
|
||||
Examples:
|
||||
/wcctools-route/element/my-button/desktop/dark
|
||||
/wcctools-route/page/home/tablet/bright
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
## API Reference
|
||||
|
||||
### Build and Watch
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build": "tsbuild tsfolders --allowimplicitany && tsbundle element",
|
||||
"watch": "tswatch element",
|
||||
"serve": "http-server ./dist"
|
||||
}
|
||||
}
|
||||
### `setupWccTools(elements, pages?)`
|
||||
|
||||
Initialize the WCC Tools dashboard.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `elements` | `Record<string, typeof LitElement>` | Map of element names to classes |
|
||||
| `pages` | `Record<string, TTemplateFactory>` | Optional map of page names to template functions |
|
||||
|
||||
### `DeesDemoWrapper`
|
||||
|
||||
Component for wrapping demos with post-render logic.
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `runAfterRender` | `(wrapper) => void \| Promise<void>` | Callback after wrapped elements render |
|
||||
|
||||
The wrapper provides full DOM API access:
|
||||
- `wrapper.querySelector()` — Find single element
|
||||
- `wrapper.querySelectorAll()` — Find multiple elements
|
||||
- `wrapper.children` — Access child elements directly
|
||||
|
||||
### Recording Components (Advanced)
|
||||
|
||||
For custom recording integrations:
|
||||
|
||||
```typescript
|
||||
import { RecorderService } from '@design.estate/dees-wcctools';
|
||||
|
||||
const recorder = new RecorderService({
|
||||
onDurationUpdate: (duration) => console.log(`${duration}s`),
|
||||
onRecordingComplete: (blob) => console.log('Recording done!', blob),
|
||||
onAudioLevelUpdate: (level) => console.log(`Audio: ${level}%`),
|
||||
});
|
||||
|
||||
await recorder.startRecording({ mode: 'viewport' });
|
||||
// ... later
|
||||
recorder.stopRecording();
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
my-components/
|
||||
├── src/
|
||||
@@ -320,74 +368,12 @@ my-components/
|
||||
└── package.json
|
||||
```
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Custom Property Handlers
|
||||
For complex property types, implement custom logic in your demo:
|
||||
|
||||
```typescript
|
||||
public static demo = () => html`
|
||||
<dees-demowrapper .runAfterRender=${(wrapper) => {
|
||||
// Use querySelector to target specific elements
|
||||
const component = wrapper.querySelector('my-component');
|
||||
if (component) {
|
||||
component.addEventListener('property-change', (e) => {
|
||||
console.log('Property changed:', e.detail);
|
||||
});
|
||||
}
|
||||
|
||||
// Or handle all elements of a type
|
||||
wrapper.querySelectorAll('my-component').forEach(el => {
|
||||
el.addEventListener('click', () => console.log('Clicked!'));
|
||||
});
|
||||
}}>
|
||||
<my-component></my-component>
|
||||
</dees-demowrapper>
|
||||
`;
|
||||
```
|
||||
|
||||
### Responsive Testing Helpers
|
||||
```typescript
|
||||
import * as domtools from '@design.estate/dees-domtools';
|
||||
|
||||
public static styles = [
|
||||
// Media query helpers
|
||||
domtools.breakpoints.cssForPhone(css`
|
||||
:host { font-size: 14px; }
|
||||
`),
|
||||
|
||||
domtools.breakpoints.cssForTablet(css`
|
||||
:host { font-size: 16px; }
|
||||
`),
|
||||
|
||||
domtools.breakpoints.cssForDesktop(css`
|
||||
:host { font-size: 18px; }
|
||||
`)
|
||||
];
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### setupWccTools(elements, pages?)
|
||||
Initialize the WCC Tools dashboard.
|
||||
|
||||
- `elements`: Object mapping element names to element classes
|
||||
- `pages`: Optional object mapping page names to template functions
|
||||
|
||||
### DeesDemoWrapper
|
||||
Component for wrapping demos with post-render logic.
|
||||
|
||||
- `runAfterRender`: Function called after the wrapped elements render
|
||||
- Receives the wrapper element itself, providing full DOM API access
|
||||
- Use `wrapper.querySelector()` and `wrapper.querySelectorAll()` for element selection
|
||||
- Access children via `wrapper.children` property
|
||||
- Supports async operations
|
||||
|
||||
## Browser Support
|
||||
- Chrome/Edge (latest)
|
||||
- Firefox (latest)
|
||||
- Safari (latest)
|
||||
- Mobile browsers with Web Components support
|
||||
|
||||
- ✅ Chrome/Edge (latest)
|
||||
- ✅ Firefox (latest)
|
||||
- ✅ Safari (latest)
|
||||
- ✅ Mobile browsers with Web Components support
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
|
||||
147
ts_demotools/readme.md
Normal file
147
ts_demotools/readme.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# @design.estate/dees-wcctools/demotools
|
||||
|
||||
🧪 **Demo Wrapper Utilities** — Enhanced testing tools for web component demos
|
||||
|
||||
## Overview
|
||||
|
||||
The demotools module provides `dees-demowrapper`, a utility component for executing post-render logic in component demos. Perfect for simulating user interactions, setting up test data, or validating component state.
|
||||
|
||||
## Installation
|
||||
|
||||
This module is included with `@design.estate/dees-wcctools`:
|
||||
|
||||
```bash
|
||||
pnpm add -D @design.estate/dees-wcctools
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Import the demotools subpath:
|
||||
|
||||
```typescript
|
||||
import '@design.estate/dees-wcctools/demotools';
|
||||
```
|
||||
|
||||
## DeesDemoWrapper
|
||||
|
||||
A wrapper component that executes a callback after its slotted content renders.
|
||||
|
||||
### Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `runAfterRender` | `(wrapper: DeesDemoWrapper) => void \| Promise<void>` | Callback executed after content renders |
|
||||
|
||||
### Example: Basic Usage
|
||||
|
||||
```typescript
|
||||
import { html } from 'lit';
|
||||
import '@design.estate/dees-wcctools/demotools';
|
||||
|
||||
public static demo = () => html`
|
||||
<dees-demowrapper .runAfterRender=${(wrapper) => {
|
||||
const button = wrapper.querySelector('my-button');
|
||||
console.log('Button found:', button);
|
||||
}}>
|
||||
<my-button>Click Me</my-button>
|
||||
</dees-demowrapper>
|
||||
`;
|
||||
```
|
||||
|
||||
### Example: Async Operations
|
||||
|
||||
```typescript
|
||||
public static demo = () => html`
|
||||
<dees-demowrapper .runAfterRender=${async (wrapper) => {
|
||||
const form = wrapper.querySelector('my-form');
|
||||
|
||||
// Wait for component initialization
|
||||
await form.updateComplete;
|
||||
|
||||
// Simulate user input
|
||||
form.values = { name: 'Test User', email: 'test@example.com' };
|
||||
|
||||
// Trigger validation
|
||||
await form.validate();
|
||||
|
||||
console.log('Form state:', form.isValid);
|
||||
}}>
|
||||
<my-form></my-form>
|
||||
</dees-demowrapper>
|
||||
`;
|
||||
```
|
||||
|
||||
### Example: Multiple Elements
|
||||
|
||||
```typescript
|
||||
public static demo = () => html`
|
||||
<dees-demowrapper .runAfterRender=${(wrapper) => {
|
||||
// Find all cards
|
||||
const cards = wrapper.querySelectorAll('my-card');
|
||||
console.log(`Found ${cards.length} cards`);
|
||||
|
||||
// Access by index
|
||||
Array.from(wrapper.children).forEach((child, i) => {
|
||||
console.log(`Child ${i}:`, child.tagName);
|
||||
});
|
||||
|
||||
// Add event listeners
|
||||
wrapper.querySelectorAll('button').forEach(btn => {
|
||||
btn.addEventListener('click', () => console.log('Clicked!'));
|
||||
});
|
||||
}}>
|
||||
<my-card title="Card 1"></my-card>
|
||||
<my-card title="Card 2"></my-card>
|
||||
<my-card title="Card 3"></my-card>
|
||||
</dees-demowrapper>
|
||||
`;
|
||||
```
|
||||
|
||||
### Example: Component State Manipulation
|
||||
|
||||
```typescript
|
||||
public static demo = () => html`
|
||||
<dees-demowrapper .runAfterRender=${async (wrapper) => {
|
||||
const tabs = wrapper.querySelector('my-tabs');
|
||||
|
||||
// Programmatically switch tabs
|
||||
tabs.activeTab = 'settings';
|
||||
await tabs.updateComplete;
|
||||
|
||||
// Verify content updated
|
||||
const content = tabs.shadowRoot.querySelector('.tab-content');
|
||||
console.log('Active content:', content.textContent);
|
||||
}}>
|
||||
<my-tabs>
|
||||
<div slot="home">Home Content</div>
|
||||
<div slot="settings">Settings Content</div>
|
||||
</my-tabs>
|
||||
</dees-demowrapper>
|
||||
`;
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. The wrapper renders its slot content immediately
|
||||
2. After a brief delay (50ms) to allow slotted content to initialize
|
||||
3. The `runAfterRender` callback is invoked with the wrapper element
|
||||
4. You have full DOM API access to query and manipulate children
|
||||
|
||||
## Key Features
|
||||
|
||||
- 📦 **Light DOM Access** — Slotted elements remain accessible via standard DOM APIs
|
||||
- ⏱️ **Async Support** — Return a Promise for async operations
|
||||
- 🎯 **Full DOM API** — Use `querySelector`, `querySelectorAll`, `children`, etc.
|
||||
- 🛡️ **Error Handling** — Errors in callbacks are caught and logged
|
||||
|
||||
## CSS Behavior
|
||||
|
||||
The wrapper uses `display: contents` so it doesn't affect layout:
|
||||
|
||||
```css
|
||||
:host {
|
||||
display: contents;
|
||||
}
|
||||
```
|
||||
|
||||
This means the wrapper is "invisible" in the layout — its children render as if they were direct children of the wrapper's parent.
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-wcctools',
|
||||
version: '1.2.1',
|
||||
version: '1.3.0',
|
||||
description: 'A set of web component tools for creating element catalogues, enabling the structured development and documentation of custom elements and pages.'
|
||||
}
|
||||
|
||||
@@ -678,16 +678,16 @@ export class WccRecordingPanel extends DeesElement {
|
||||
<div class="trim-track"></div>
|
||||
<div
|
||||
class="trim-selected"
|
||||
style="left: ${this.getHandlePosition(this.trimStart)}px; right: ${this.getHandlePositionFromEnd(this.trimEnd)}px;"
|
||||
style="left: ${this.getHandlePositionStyle(this.trimStart)}; right: ${this.getHandlePositionFromEndStyle(this.trimEnd)};"
|
||||
></div>
|
||||
<div
|
||||
class="trim-handle start-handle"
|
||||
style="left: ${this.getHandlePosition(this.trimStart)}px;"
|
||||
style="left: ${this.getHandlePositionStyle(this.trimStart)};"
|
||||
@mousedown=${(e: MouseEvent) => { e.stopPropagation(); this.isDraggingTrim = 'start'; }}
|
||||
></div>
|
||||
<div
|
||||
class="trim-handle end-handle"
|
||||
style="left: ${this.getHandlePosition(this.trimEnd)}px;"
|
||||
style="left: ${this.getHandlePositionStyle(this.trimEnd)};"
|
||||
@mousedown=${(e: MouseEvent) => { e.stopPropagation(); this.isDraggingTrim = 'end'; }}
|
||||
></div>
|
||||
</div>
|
||||
@@ -850,9 +850,12 @@ export class WccRecordingPanel extends DeesElement {
|
||||
// ==================== Trim Methods ====================
|
||||
|
||||
private handleVideoLoaded(video: HTMLVideoElement): void {
|
||||
this.videoDuration = video.duration;
|
||||
// WebM files from MediaRecorder may have Infinity/NaN duration
|
||||
// Fall back to the tracked recording duration
|
||||
const duration = Number.isFinite(video.duration) ? video.duration : this.recordingDuration;
|
||||
this.videoDuration = duration;
|
||||
this.trimStart = 0;
|
||||
this.trimEnd = video.duration;
|
||||
this.trimEnd = duration;
|
||||
}
|
||||
|
||||
private formatDuration(seconds: number): string {
|
||||
@@ -861,18 +864,23 @@ export class WccRecordingPanel extends DeesElement {
|
||||
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
private getHandlePosition(time: number): number {
|
||||
if (this.videoDuration === 0) return 12;
|
||||
private getHandlePositionStyle(time: number): string {
|
||||
if (this.videoDuration === 0) return '12px';
|
||||
const percentage = time / this.videoDuration;
|
||||
const trackWidth = 336;
|
||||
return 12 + (percentage * trackWidth);
|
||||
// Formula: 12px padding + percentage of remaining width (total - 24px padding)
|
||||
// At 0%: 12px (left edge of track)
|
||||
// At 100%: calc(100% - 12px) (right edge of track)
|
||||
return `calc(12px + ${(percentage * 100).toFixed(2)}% - ${(percentage * 24).toFixed(2)}px)`;
|
||||
}
|
||||
|
||||
private getHandlePositionFromEnd(time: number): number {
|
||||
if (this.videoDuration === 0) return 12;
|
||||
const percentage = (this.videoDuration - time) / this.videoDuration;
|
||||
const trackWidth = 336;
|
||||
return 12 + (percentage * trackWidth);
|
||||
private getHandlePositionFromEndStyle(time: number): string {
|
||||
if (this.videoDuration === 0) return '12px';
|
||||
const percentage = time / this.videoDuration;
|
||||
const remainingPercentage = 1 - percentage;
|
||||
// For CSS 'right' property: distance from right edge
|
||||
// At trimEnd = 100%: right = 12px (at right edge of track)
|
||||
// At trimEnd = 0%: right = calc(100% - 12px) (at left edge of track)
|
||||
return `calc(12px + ${(remainingPercentage * 100).toFixed(2)}% - ${(remainingPercentage * 24).toFixed(2)}px)`;
|
||||
}
|
||||
|
||||
private handleTimelineClick(e: MouseEvent): void {
|
||||
|
||||
123
ts_web/readme.md
Normal file
123
ts_web/readme.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# @design.estate/dees-wcctools
|
||||
|
||||
🛠️ **Web Component Catalogue Tools** — The core dashboard and UI components for building interactive component catalogues
|
||||
|
||||
## Overview
|
||||
|
||||
This is the main module of `@design.estate/dees-wcctools`, providing the complete dashboard experience for developing, testing, and documenting web components.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pnpm add -D @design.estate/dees-wcctools
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { setupWccTools } from '@design.estate/dees-wcctools';
|
||||
import { MyButton } from './components/my-button.js';
|
||||
|
||||
setupWccTools({
|
||||
'my-button': MyButton,
|
||||
});
|
||||
```
|
||||
|
||||
## Exports
|
||||
|
||||
### Main Entry Point
|
||||
|
||||
| Export | Description |
|
||||
|--------|-------------|
|
||||
| `setupWccTools` | Initialize the component catalogue dashboard |
|
||||
|
||||
### Recording Components
|
||||
|
||||
| Export | Description |
|
||||
|--------|-------------|
|
||||
| `RecorderService` | Service class for screen/viewport recording |
|
||||
| `WccRecordButton` | Record button UI component |
|
||||
| `WccRecordingPanel` | Recording options and preview panel |
|
||||
| `IRecorderEvents` | TypeScript interface for recorder callbacks |
|
||||
| `IRecordingOptions` | TypeScript interface for recording options |
|
||||
|
||||
## Internal Components
|
||||
|
||||
The module includes these internal web components:
|
||||
|
||||
| Component | Description |
|
||||
|-----------|-------------|
|
||||
| `wcc-dashboard` | Main dashboard container with routing |
|
||||
| `wcc-sidebar` | Navigation sidebar with element/page listing |
|
||||
| `wcc-frame` | Iframe viewport with responsive sizing |
|
||||
| `wcc-properties` | Property panel with live editing |
|
||||
| `wcc-record-button` | Recording state indicator button |
|
||||
| `wcc-recording-panel` | Recording workflow UI |
|
||||
|
||||
## RecorderService API
|
||||
|
||||
For programmatic recording control:
|
||||
|
||||
```typescript
|
||||
import { RecorderService, type IRecorderEvents } from '@design.estate/dees-wcctools';
|
||||
|
||||
const events: IRecorderEvents = {
|
||||
onDurationUpdate: (duration) => console.log(`Recording: ${duration}s`),
|
||||
onRecordingComplete: (blob) => saveBlob(blob),
|
||||
onAudioLevelUpdate: (level) => updateMeter(level),
|
||||
onError: (error) => console.error(error),
|
||||
onStreamEnded: () => console.log('User stopped sharing'),
|
||||
};
|
||||
|
||||
const recorder = new RecorderService(events);
|
||||
|
||||
// Load available microphones
|
||||
const mics = await recorder.loadMicrophones(true); // true = request permission
|
||||
|
||||
// Start audio level monitoring
|
||||
await recorder.startAudioMonitoring(mics[0].deviceId);
|
||||
|
||||
// Start recording
|
||||
await recorder.startRecording({
|
||||
mode: 'viewport', // or 'screen'
|
||||
audioDeviceId: mics[0].deviceId,
|
||||
viewportElement: document.querySelector('.viewport'),
|
||||
});
|
||||
|
||||
// Stop recording
|
||||
recorder.stopRecording();
|
||||
|
||||
// Export trimmed video
|
||||
const trimmedBlob = await recorder.exportTrimmedVideo(videoElement, startTime, endTime);
|
||||
|
||||
// Cleanup
|
||||
recorder.dispose();
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
ts_web/
|
||||
├── index.ts # Main exports
|
||||
├── elements/
|
||||
│ ├── wcc-dashboard.ts # Root dashboard component
|
||||
│ ├── wcc-sidebar.ts # Navigation sidebar
|
||||
│ ├── wcc-frame.ts # Responsive iframe viewport
|
||||
│ ├── wcc-properties.ts # Property editing panel
|
||||
│ ├── wcc-record-button.ts # Recording button
|
||||
│ ├── wcc-recording-panel.ts # Recording options/preview
|
||||
│ └── wcctools.helpers.ts # Shared utilities
|
||||
├── services/
|
||||
│ └── recorder.service.ts # MediaRecorder abstraction
|
||||
└── pages/
|
||||
└── index.ts # Built-in pages
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- 🎨 Interactive component preview
|
||||
- 🔧 Real-time property editing with type detection
|
||||
- 🌓 Theme switching (light/dark)
|
||||
- 📱 Responsive viewport testing
|
||||
- 🎬 Screen recording with trimming
|
||||
- 🔗 URL-based deep linking
|
||||
Reference in New Issue
Block a user