Window Management UI
by Nicholas Berlette
HTML
<window-component id="window1">
<tab-view-component slot="views">
<tab-component slot="tabs">Explorer</tab-component>
<tab-component slot="tabs">Search</tab-component>
<tab-component slot="tabs">Extensions</tab-component>
<div slot="content" name="panel-0">
<tab-view-component>
<tab-component slot="tabs">File 1</tab-component>
<tab-component slot="tabs">File 2</tab-component>
<div slot="content" name="panel-0">Content for File 1</div>
<div slot="content" name="panel-1">Content for File 2</div>
</tab-view-component>
</div>
<div slot="content" name="panel-1">
<tab-view-component>
<tab-component slot="tabs">Match 1</tab-component>
<tab-component slot="tabs">Match 2</tab-component>
<div slot="content" name="panel-0">Search Result 1</div>
<div slot="content" name="panel-1">Search Result 2</div>
</tab-view-component>
</div>
<div slot="content" name="panel-2">
<tab-view-component>
<tab-component slot="tabs">Installed</tab-component>
<tab-component slot="tabs">Disabled</tab-component>
<tab-component slot="tabs">Built-in</tab-component>
<div slot="content" name="panel-0">Installed Extensions</div>
<div slot="content" name="panel-1">Disabled Extensions</div>
<div slot="content" name="panel-2">Built-in Extensions</div>
</tab-view-component>
</div>
</tab-view-component>
</window-component>
<window-component id="window2">
<tab-view-component slot="views">
<tab-component slot="tabs">Home</tab-component>
<tab-component slot="tabs">Settings</tab-component>
<div slot="content" name="panel-0">Home Content</div>
<div slot="content" name="panel-1">Settings Content</div>
...
Tailwind CSS
body {
margin: 0;
height: 100vh;
width: 100vw;
position: relative;
background: #111827;
overflow: hidden;
--window-border-color: #000;
--window-focused-shadow: 0 4px 25px -8px rgba(0,0,0,0.2);
--window-blurred-filter: blur(2px);
font-family: var(--font-sans);
color: #ddd;
}
JavaScript
// BaseComponent.js
class BaseComponent extends HTMLElement {
static tagName = 'base-component';
static styles = `
:host {
--background-color: white;
--border-color: #ccc;
--focused-shadow: 0 4px 20px rgba(0,0,0,0.3);
--blurred-filter: blur(2px);
--transition-duration: 0.3s;
--transition-ease: ease;
display: block;
}
`;
static template = `<slot></slot>`;
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>${this.constructor.styles}</style>
${this.constructor.template}
`;
}
/**
* Shorthand for querySelector within shadow DOM
* @param {string} selector
* @returns {Element}
*/
$(selector) {
return this.shadowRoot.querySelector(selector);
}
/**
* Shorthand for querySelectorAll within shadow DOM
* @param {string} selector
* @returns {NodeList}
*/
$$(selector) {
return this.shadowRoot.querySelectorAll(selector);
}
/**
* Set multiple attributes at once
* @param {Object} attributes
* @returns {BaseComponent} for chaining
*/
setAttributes(attributes) {
for (let key in attributes) {
this.setAttribute(key, attributes[key]);
}
return this;
}
/**
* Emit a custom event with optional detail
* @param {string} eventName
* @param {Object} detail
* @returns {BaseComponent} for chaining
*/
emit(eventName, detail = {}) {
this.dispatchEvent(new CustomEvent(eventName, { detail, bubbles: true, composed: true }));
return this;
}
/**
* Add an event listener
* @param {string} eventName
* @param {Function} callback
* @returns {BaseComponent} for chaining
*/
on(eventName, callback) {
this.addEventListener(eventName,...