fix(properties): enhance element detection in properties panel with recursive search and retry mechanism

This commit is contained in:
Juergen Kunz
2025-06-16 13:10:12 +00:00
parent fca47b87fb
commit 6bdb8c78b7
7 changed files with 107 additions and 11 deletions

View File

@ -3,12 +3,14 @@
"version": "1.0.90", "version": "1.0.90",
"private": false, "private": false,
"description": "A set of web component tools for creating element catalogues, enabling the structured development and documentation of custom elements and pages.", "description": "A set of web component tools for creating element catalogues, enabling the structured development and documentation of custom elements and pages.",
"main": "dist_ts_web/index.js", "exports": {
"typings": "dist_ts_web/index.d.ts", ".": "./dist_ts_web/index.js",
"./demoTools": "./dist_ts_demotools"
},
"type": "module", "type": "module",
"scripts": { "scripts": {
"test": "(npm run build)", "test": "(npm run build)",
"build": "(tsbuild element --web --allowimplicitany && tsbundle element)", "build": "(tsbuild tsfolders --allowimplicitany && tsbundle element)",
"watch": "tswatch element", "watch": "tswatch element",
"buildDocs": "tsdoc" "buildDocs": "tsdoc"
}, },

View File

@ -1 +1,24 @@
# Project Hints and Findings
## Properties Panel Element Detection Issue (Fixed)
### Problem
The properties panel had timing issues detecting rendered elements because:
1. Elements are rendered asynchronously via lit's `render()` function in the dashboard component
2. The properties panel tried to find elements immediately without waiting for render completion
3. Element search only looked at direct children of the viewport, missing nested elements or those inside shadow DOM
### Solution Implemented
1. Added a 100ms initial delay to allow render completion
2. Implemented recursive element search that:
- Searches through nested children up to 5 levels deep
- Checks shadow roots of elements
- Handles complex DOM structures
3. Added retry mechanism with up to 5 attempts (200ms between retries)
4. Improved error messages to show retry count
### Code Flow
1. Dashboard renders element demo into viewport using `render(anonItem.demo(), viewport)`
2. Properties panel waits, then searches recursively for the element instance
3. If not found, retries with delays to handle async rendering
4. Once found, extracts and displays element properties

30
readme.plan.md Normal file
View File

@ -0,0 +1,30 @@
# Fix Properties Panel Element Detection
To fix the element detection issue, reread CLAUDE.md first.
## Problem Analysis
The properties panel has timing issues detecting rendered elements because:
1. Elements are rendered asynchronously via lit's `render()` in the dashboard
2. Properties panel tries to find elements immediately without waiting for render completion
3. Element search only looks at direct children, missing nested/shadow DOM elements
## Implementation Plan
### 1. Add proper synchronization
- Add a delay or await render completion before element detection
- Use MutationObserver or lit's updateComplete promises
### 2. Improve element search algorithm
- Search recursively through all descendants, not just direct children
- Handle shadow DOM boundaries properly
- Support elements wrapped in containers
### 3. Add retry mechanism
- If element not found, retry after a delay
- Add maximum retry attempts to prevent infinite loops
- Clear error state when element is eventually found
## Code Changes Required
1. Modify `wcc-properties.ts` createProperties() method
2. Add element search utility function
3. Improve error handling and user feedback

View File

0
ts_demotools/index.ts Normal file
View File

1
ts_demotools/plugins.ts Normal file
View File

@ -0,0 +1 @@
import {} from '@design.estate/dees-element';

View File

@ -226,6 +226,31 @@ export class WccProperties extends DeesElement {
`; `;
} }
private async findElementRecursively(container: Element, elementClass: any, maxDepth: number = 5): Promise<HTMLElement | null> {
if (maxDepth <= 0) return null;
// Check direct children
for (const child of Array.from(container.children)) {
if (child instanceof elementClass) {
return child as HTMLElement;
}
}
// Check shadow roots of children
for (const child of Array.from(container.children)) {
if (child.shadowRoot) {
const found = await this.findElementRecursively(child.shadowRoot as any, elementClass, maxDepth - 1);
if (found) return found;
}
// Also check nested children
const found = await this.findElementRecursively(child, elementClass, maxDepth - 1);
if (found) return found;
}
return null;
}
public async createProperties() { public async createProperties() {
console.log('creating properties for:'); console.log('creating properties for:');
console.log(this.selectedItem); console.log(this.selectedItem);
@ -275,15 +300,30 @@ export class WccProperties extends DeesElement {
} }
console.log(anonItem.elementProperties); console.log(anonItem.elementProperties);
const wccFrame = await this.dashboardRef.wccFrame; const wccFrame = await this.dashboardRef.wccFrame;
let firstFoundInstantiatedElement: HTMLElement;
for (const element of Array.from((await wccFrame.getViewportElement()).children)) { // Wait for render to complete
if (element instanceof (this.selectedItem as any)) { await new Promise(resolve => setTimeout(resolve, 100));
firstFoundInstantiatedElement = element as HTMLElement;
break; // Try to find the element with recursive search
} const viewport = await wccFrame.getViewportElement();
let firstFoundInstantiatedElement: HTMLElement = await this.findElementRecursively(
viewport,
this.selectedItem as any
);
// Retry logic if element not found
let retries = 0;
while (!firstFoundInstantiatedElement && retries < 5) {
await new Promise(resolve => setTimeout(resolve, 200));
firstFoundInstantiatedElement = await this.findElementRecursively(
viewport,
this.selectedItem as any
);
retries++;
} }
if (!firstFoundInstantiatedElement) { if (!firstFoundInstantiatedElement) {
this.warning = `no first instantiated element found for >>${anonItem.name}<<`; this.warning = `no first instantiated element found for >>${anonItem.name}<< after ${retries} retries`;
return; return;
} }
const classProperties: Map<string, any> = anonItem.elementProperties; const classProperties: Map<string, any> = anonItem.elementProperties;