diff --git a/.playwright-mcp/sidebar-check-2.png b/.playwright-mcp/sidebar-check-2.png new file mode 100644 index 0000000..202a95f Binary files /dev/null and b/.playwright-mcp/sidebar-check-2.png differ diff --git a/.playwright-mcp/sidebar-check.png b/.playwright-mcp/sidebar-check.png new file mode 100644 index 0000000..ce65898 Binary files /dev/null and b/.playwright-mcp/sidebar-check.png differ diff --git a/ts_web/elements/wcc-dashboard.ts b/ts_web/elements/wcc-dashboard.ts index e3814d3..72ccc29 100644 --- a/ts_web/elements/wcc-dashboard.ts +++ b/ts_web/elements/wcc-dashboard.ts @@ -66,6 +66,10 @@ export class WccDashboard extends DeesElement { @property({ attribute: false }) accessor pinnedItems: Set = new Set(); + // Sidebar width (resizable) + @property({ type: Number }) + accessor sidebarWidth: number = 200; + // Derived from selectedViewport - no need for separate property public get isNative(): boolean { return this.selectedViewport === 'native'; @@ -127,6 +131,7 @@ export class WccDashboard extends DeesElement { .selectedItem=${this.selectedItem} .searchQuery=${this.searchQuery} .pinnedItems=${this.pinnedItems} + .sidebarWidth=${this.sidebarWidth} .isNative=${this.isNative} @selectedType=${(eventArg) => { this.selectedType = eventArg.detail; @@ -145,6 +150,15 @@ export class WccDashboard extends DeesElement { this.pinnedItems = eventArg.detail; this.updateUrlWithScrollState(); }} + @widthChanged=${async (eventArg: CustomEvent) => { + this.sidebarWidth = eventArg.detail; + this.updateUrlWithScrollState(); + const frame = await this.wccFrame; + if (frame) { + frame.sidebarWidth = eventArg.detail; + frame.requestUpdate(); + } + }} > { this.selectedViewport = eventArg.detail; this.scheduleUpdate(); @@ -171,7 +186,7 @@ export class WccDashboard extends DeesElement { this.toggleNative(); }} > - + `; } @@ -247,6 +262,7 @@ export class WccDashboard extends DeesElement { const frameScrollY = routeInfo.queryParams.frameScrollY; const sidebarScrollY = routeInfo.queryParams.sidebarScrollY; const pinned = routeInfo.queryParams.pinned; + const sidebarWidth = routeInfo.queryParams.sidebarWidth; if (search) { this.searchQuery = search; @@ -269,10 +285,19 @@ export class WccDashboard extends DeesElement { } else if (this.pinnedItems.size > 0) { this.pinnedItems = new Set(); } + if (sidebarWidth) { + this.sidebarWidth = parseInt(sidebarWidth, 10); + } - // Apply scroll positions after a short delay to ensure DOM is ready - setTimeout(() => { + // Apply scroll positions and update frame after a short delay to ensure DOM is ready + setTimeout(async () => { this.applyScrollPositions(); + // Ensure frame gets the sidebarWidth + const frame = await this.wccFrame; + if (frame) { + frame.sidebarWidth = this.sidebarWidth; + frame.requestUpdate(); + } }, 100); } else { this.searchQuery = ''; @@ -326,6 +351,7 @@ export class WccDashboard extends DeesElement { const frameScrollY = routeInfo.queryParams.frameScrollY; const sidebarScrollY = routeInfo.queryParams.sidebarScrollY; const pinned = routeInfo.queryParams.pinned; + const sidebarWidth = routeInfo.queryParams.sidebarWidth; if (search) { this.searchQuery = search; @@ -348,6 +374,9 @@ export class WccDashboard extends DeesElement { } else if (this.pinnedItems.size > 0) { this.pinnedItems = new Set(); } + if (sidebarWidth) { + this.sidebarWidth = parseInt(sidebarWidth, 10); + } // Apply scroll positions after a short delay to ensure DOM is ready setTimeout(() => { @@ -444,6 +473,9 @@ export class WccDashboard extends DeesElement { if (this.pinnedItems.size > 0) { queryParams.set('pinned', Array.from(this.pinnedItems).join(',')); } + if (this.sidebarWidth !== 200) { + queryParams.set('sidebarWidth', this.sidebarWidth.toString()); + } const queryString = queryParams.toString(); const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl; @@ -507,6 +539,9 @@ export class WccDashboard extends DeesElement { if (this.pinnedItems.size > 0) { queryParams.set('pinned', Array.from(this.pinnedItems).join(',')); } + if (this.sidebarWidth !== 200) { + queryParams.set('sidebarWidth', this.sidebarWidth.toString()); + } const queryString = queryParams.toString(); const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl; diff --git a/ts_web/elements/wcc-frame.ts b/ts_web/elements/wcc-frame.ts index 33386c6..e44d106 100644 --- a/ts_web/elements/wcc-frame.ts +++ b/ts_web/elements/wcc-frame.ts @@ -19,13 +19,15 @@ export class WccFrame extends DeesElement { @property({ type: Boolean }) accessor isNative: boolean = false; + @property({ type: Number }) + accessor sidebarWidth: number = 200; + public static styles = [ css` :host { border: 10px solid #ffaeaf; position: absolute; background: ${cssManager.bdTheme('#333', '#000')}; - left: 200px; right: 0px; top: 0px; overflow-y: auto; @@ -55,7 +57,7 @@ export class WccFrame extends DeesElement { ` : ` bottom: ${this.advancedEditorOpen ? '400px' : '100px'}; border: 10px solid #ffaeaf; - left: 200px; + left: ${this.sidebarWidth}px; `} transition: all 0.3s ease; ${this.isNative ? 'padding: 0px;' : (() => { @@ -67,19 +69,19 @@ export class WccFrame extends DeesElement { case 'tablet': return ` padding: 0px ${ - (document.body.clientWidth - 200 - domtools.breakpoints.tablet) / 2 + (document.body.clientWidth - this.sidebarWidth - domtools.breakpoints.tablet) / 2 }px; `; case 'phablet': return ` padding: 0px ${ - (document.body.clientWidth - 200 - domtools.breakpoints.phablet) / 2 + (document.body.clientWidth - this.sidebarWidth - domtools.breakpoints.phablet) / 2 }px; `; case 'phone': return ` padding: 0px ${ - (document.body.clientWidth - 200 - domtools.breakpoints.phone) / 2 + (document.body.clientWidth - this.sidebarWidth - domtools.breakpoints.phone) / 2 }px; `; } diff --git a/ts_web/elements/wcc-properties.ts b/ts_web/elements/wcc-properties.ts index c6987da..760ff0f 100644 --- a/ts_web/elements/wcc-properties.ts +++ b/ts_web/elements/wcc-properties.ts @@ -37,6 +37,9 @@ export class WccProperties extends DeesElement { @property() accessor isNative: boolean = false; + @property({ type: Number }) + accessor sidebarWidth: number = 200; + @state() accessor propertyContent: TemplateResult[] = []; @@ -89,7 +92,7 @@ export class WccProperties extends DeesElement { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif; box-sizing: border-box; position: absolute; - left: 200px; + left: ${this.sidebarWidth}px; height: ${this.editingProperties.length > 0 ? 100 + this.editorHeight : 100}px; bottom: 0px; right: 0px; diff --git a/ts_web/elements/wcc-sidebar.ts b/ts_web/elements/wcc-sidebar.ts index b2439e7..866fd7e 100644 --- a/ts_web/elements/wcc-sidebar.ts +++ b/ts_web/elements/wcc-sidebar.ts @@ -36,6 +36,14 @@ export class WccSidebar extends DeesElement { @property({ attribute: false }) accessor pinnedItems: Set = new Set(); + // Sidebar width (resizable) + @property({ type: Number }) + accessor sidebarWidth: number = 200; + + // Track if currently resizing + @state() + accessor isResizing: boolean = false; + private sectionsInitialized = false; public render(): TemplateResult { @@ -66,7 +74,7 @@ export class WccSidebar extends DeesElement { box-sizing: border-box; position: absolute; left: 0px; - width: 200px; + width: ${this.sidebarWidth}px; top: 0px; bottom: 0px; overflow-y: auto; @@ -360,6 +368,27 @@ export class WccSidebar extends DeesElement { margin-left: 0.25rem; margin-right: 0.25rem; } + + /* Resize handle */ + .resize-handle { + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 4px; + cursor: col-resize; + background: transparent; + transition: background 0.15s ease; + z-index: 10; + } + + .resize-handle:hover { + background: rgba(59, 130, 246, 0.3); + } + + .resize-handle.active { + background: var(--primary); + }
+
`; } @@ -723,6 +756,42 @@ export class WccSidebar extends DeesElement { } } + // ============ Resize functionality ============ + + private startResize = (e: MouseEvent) => { + e.preventDefault(); + this.isResizing = true; + const startX = e.clientX; + const startWidth = this.sidebarWidth; + + // Cache references once at start + const frame = this.dashboardRef?.shadowRoot?.querySelector('wcc-frame') as any; + const properties = this.dashboardRef?.shadowRoot?.querySelector('wcc-properties') as any; + + const onMouseMove = (e: MouseEvent) => { + const newWidth = Math.min(400, Math.max(150, startWidth + (e.clientX - startX))); + this.sidebarWidth = newWidth; + // Update frame and properties directly + if (frame) { + frame.sidebarWidth = newWidth; + } + if (properties) { + properties.sidebarWidth = newWidth; + } + }; + + const onMouseUp = () => { + this.isResizing = false; + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + // Dispatch event on release for URL persistence + this.dispatchEvent(new CustomEvent('widthChanged', { detail: this.sidebarWidth })); + }; + + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + }; + public selectItem( typeArg: TElementType, itemNameArg: string,