update
This commit is contained in:
@ -58,6 +58,34 @@ Updated the WCC Dashboard UI components (properties and sidebar) to use shadcn-l
|
|||||||
- Grid layout with 1px gaps creating subtle dividers
|
- Grid layout with 1px gaps creating subtle dividers
|
||||||
- Warning display with backdrop blur and rounded corners
|
- Warning display with backdrop blur and rounded corners
|
||||||
|
|
||||||
|
## Advanced Complex Properties Editor (2025-06-27)
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Implemented an advanced editor for complex properties (Arrays and Objects) that appears between the wcc-properties panel and frame when activated.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
1. **Dynamic Layout**: Frame shrinks by 300px from bottom when editor opens
|
||||||
|
2. **JSON Editor**:
|
||||||
|
- Monospace font for code editing
|
||||||
|
- Tab key support for indentation
|
||||||
|
- Syntax validation with error messages
|
||||||
|
- Live preview of changes
|
||||||
|
3. **Smooth Transitions**: Animated opening/closing with 0.3s ease
|
||||||
|
4. **Error Handling**: Invalid JSON shows clear error messages that disappear on typing
|
||||||
|
|
||||||
|
### Technical Implementation
|
||||||
|
- **State Management**: Added showAdvancedEditor, editingProperty, editorValue, editorError states
|
||||||
|
- **Event System**: Uses custom 'editorStateChanged' event to communicate with parent dashboard
|
||||||
|
- **Dynamic Styling**: wcc-frame's bottom position changes from 100px to 400px when editor is open
|
||||||
|
- **Property Types**: Object and Array properties show "Edit Object/Array" button instead of inline controls
|
||||||
|
|
||||||
|
### User Flow
|
||||||
|
1. Click "Edit Object/Array" button on complex property
|
||||||
|
2. Editor slides up between properties panel and frame
|
||||||
|
3. Edit JSON with live validation
|
||||||
|
4. Save applies changes and refreshes properties, Cancel discards changes
|
||||||
|
5. Frame automatically resizes back when editor closes
|
||||||
|
|
||||||
## Properties Panel Element Detection Issue (Fixed)
|
## Properties Panel Element Detection Issue (Fixed)
|
||||||
|
|
||||||
### Problem
|
### Problem
|
||||||
|
@ -99,6 +99,13 @@ export class WccDashboard extends DeesElement {
|
|||||||
@selectedTheme=${(eventArg) => {
|
@selectedTheme=${(eventArg) => {
|
||||||
this.selectedTheme = eventArg.detail;
|
this.selectedTheme = eventArg.detail;
|
||||||
}}
|
}}
|
||||||
|
@editorStateChanged=${async (eventArg) => {
|
||||||
|
const frame = await this.wccFrame;
|
||||||
|
if (frame) {
|
||||||
|
frame.advancedEditorOpen = eventArg.detail.isOpen;
|
||||||
|
frame.requestUpdate();
|
||||||
|
}
|
||||||
|
}}
|
||||||
></wcc-properties>
|
></wcc-properties>
|
||||||
<wcc-frame id="wccFrame" viewport=${this.selectedViewport}>
|
<wcc-frame id="wccFrame" viewport=${this.selectedViewport}>
|
||||||
</wcc-frame>
|
</wcc-frame>
|
||||||
|
@ -13,6 +13,9 @@ export class WccFrame extends DeesElement {
|
|||||||
@property()
|
@property()
|
||||||
public viewport: string;
|
public viewport: string;
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
public advancedEditorOpen: boolean = false;
|
||||||
|
|
||||||
public static styles = [
|
public static styles = [
|
||||||
css`
|
css`
|
||||||
:host {
|
:host {
|
||||||
@ -22,7 +25,6 @@ export class WccFrame extends DeesElement {
|
|||||||
left: 200px;
|
left: 200px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
bottom: 100px;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overscroll-behavior: contain;
|
overscroll-behavior: contain;
|
||||||
@ -41,6 +43,8 @@ export class WccFrame extends DeesElement {
|
|||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
|
bottom: ${this.advancedEditorOpen ? '400px' : '100px'};
|
||||||
|
transition: bottom 0.3s ease;
|
||||||
${(() => {
|
${(() => {
|
||||||
switch (this.viewport) {
|
switch (this.viewport) {
|
||||||
case 'desktop':
|
case 'desktop':
|
||||||
|
@ -34,6 +34,20 @@ export class WccProperties extends DeesElement {
|
|||||||
@state()
|
@state()
|
||||||
propertyContent: TemplateResult[] = [];
|
propertyContent: TemplateResult[] = [];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
showAdvancedEditor: boolean = false;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
editingProperty: { name: string; value: any; element: HTMLElement } = null;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
editorValue: string = '';
|
||||||
|
|
||||||
|
@state()
|
||||||
|
editorError: string = '';
|
||||||
|
|
||||||
|
public editorHeight: number = 300;
|
||||||
|
|
||||||
public render(): TemplateResult {
|
public render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
@ -62,7 +76,7 @@ export class WccProperties extends DeesElement {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 200px;
|
left: 200px;
|
||||||
height: 100px;
|
height: ${this.showAdvancedEditor ? 100 + this.editorHeight : 100}px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -74,6 +88,10 @@ export class WccProperties extends DeesElement {
|
|||||||
grid-template-columns: 1fr 150px 300px 70px;
|
grid-template-columns: 1fr 150px 300px 70px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main-content .grid {
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
.properties {
|
.properties {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@ -150,6 +168,23 @@ export class WccProperties extends DeesElement {
|
|||||||
accent-color: var(--primary);
|
accent-color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.properties .editor-button {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
background: var(--input);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-size: 0.7rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.properties .editor-button:hover {
|
||||||
|
border-color: var(--primary);
|
||||||
|
background: rgba(59, 130, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.viewportSelector,
|
.viewportSelector,
|
||||||
.themeSelector {
|
.themeSelector {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -254,7 +289,148 @@ export class WccProperties extends DeesElement {
|
|||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.advanced-editor {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
height: ${this.editorHeight}px;
|
||||||
|
background: var(--background);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-header {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: rgba(59, 130, 246, 0.03);
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-title {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-button {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
color: #999;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-button:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-button.primary {
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--primary-foreground);
|
||||||
|
border-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-button.primary:hover {
|
||||||
|
background: rgba(59, 130, 246, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--input);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
resize: none;
|
||||||
|
outline: none;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-textarea:focus {
|
||||||
|
border-color: var(--primary);
|
||||||
|
background: rgba(59, 130, 246, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-error {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
color: #f87171;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 100px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 150px 300px 70px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
${this.showAdvancedEditor ? html`
|
||||||
|
<div class="advanced-editor">
|
||||||
|
<div class="editor-header">
|
||||||
|
<div class="editor-title">Editing: ${this.editingProperty?.name}</div>
|
||||||
|
<div class="editor-actions">
|
||||||
|
<button class="editor-button" @click=${this.handleEditorCancel}>Cancel</button>
|
||||||
|
<button class="editor-button primary" @click=${this.handleEditorSave}>Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="editor-content">
|
||||||
|
<textarea
|
||||||
|
class="editor-textarea"
|
||||||
|
.value=${this.editorValue}
|
||||||
|
@input=${(e: InputEvent) => {
|
||||||
|
this.editorValue = (e.target as HTMLTextAreaElement).value;
|
||||||
|
this.editorError = '';
|
||||||
|
}}
|
||||||
|
@keydown=${(e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Tab') {
|
||||||
|
e.preventDefault();
|
||||||
|
const target = e.target as HTMLTextAreaElement;
|
||||||
|
const start = target.selectionStart;
|
||||||
|
const end = target.selectionEnd;
|
||||||
|
const value = target.value;
|
||||||
|
target.value = value.substring(0, start) + ' ' + value.substring(end);
|
||||||
|
target.selectionStart = target.selectionEnd = start + 2;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></textarea>
|
||||||
|
${this.editorError ? html`
|
||||||
|
<div class="editor-error">${this.editorError}</div>
|
||||||
|
` : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
` : null}
|
||||||
|
<div class="main-content">
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div class="properties">
|
<div class="properties">
|
||||||
${this.propertyContent}
|
${this.propertyContent}
|
||||||
@ -323,6 +499,7 @@ export class WccProperties extends DeesElement {
|
|||||||
<div class="docs">Docs</div>
|
<div class="docs">Docs</div>
|
||||||
</div>
|
</div>
|
||||||
${this.warning ? html`<div class="warning">${this.warning}</div>` : null}
|
${this.warning ? html`<div class="warning">${this.warning}</div>` : null}
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,6 +678,17 @@ export class WccProperties extends DeesElement {
|
|||||||
`;
|
`;
|
||||||
})}
|
})}
|
||||||
</select>`;
|
</select>`;
|
||||||
|
case 'Object':
|
||||||
|
case 'Array':
|
||||||
|
return html`<button
|
||||||
|
class="editor-button"
|
||||||
|
style="width: 100%; margin-top: 0.25rem;"
|
||||||
|
@click="${() => this.openAdvancedEditor(key, firstFoundInstantiatedElement[key], firstFoundInstantiatedElement)}"
|
||||||
|
>
|
||||||
|
Edit ${propertyTypeString}
|
||||||
|
</button>`;
|
||||||
|
default:
|
||||||
|
return html`<div style="color: #666; font-size: 0.7rem;">Unsupported type</div>`;
|
||||||
}
|
}
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
@ -552,4 +740,62 @@ export class WccProperties extends DeesElement {
|
|||||||
);
|
);
|
||||||
this.dashboardRef.buildUrl();
|
this.dashboardRef.buildUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private openAdvancedEditor(propertyName: string, value: any, element: HTMLElement) {
|
||||||
|
this.editingProperty = {
|
||||||
|
name: propertyName,
|
||||||
|
value: value,
|
||||||
|
element: element
|
||||||
|
};
|
||||||
|
this.editorValue = JSON.stringify(value, null, 2);
|
||||||
|
this.editorError = '';
|
||||||
|
this.showAdvancedEditor = true;
|
||||||
|
|
||||||
|
// Notify parent to resize frame
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('editorStateChanged', {
|
||||||
|
detail: { isOpen: true },
|
||||||
|
bubbles: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleEditorSave() {
|
||||||
|
try {
|
||||||
|
const parsedValue = JSON.parse(this.editorValue);
|
||||||
|
if (this.editingProperty) {
|
||||||
|
this.editingProperty.element[this.editingProperty.name] = parsedValue;
|
||||||
|
this.showAdvancedEditor = false;
|
||||||
|
this.editorError = '';
|
||||||
|
|
||||||
|
// Notify parent to resize frame back
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('editorStateChanged', {
|
||||||
|
detail: { isOpen: false },
|
||||||
|
bubbles: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Refresh properties display
|
||||||
|
this.createProperties();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.editorError = `Invalid JSON: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleEditorCancel() {
|
||||||
|
this.showAdvancedEditor = false;
|
||||||
|
this.editingProperty = null;
|
||||||
|
this.editorValue = '';
|
||||||
|
this.editorError = '';
|
||||||
|
|
||||||
|
// Notify parent to resize frame back
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('editorStateChanged', {
|
||||||
|
detail: { isOpen: false },
|
||||||
|
bubbles: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user