JSFiddle - React, Tailwind, and code Playground

by Nicholas Berlette

TypeScript

export interface Position {
  x: number;
  y: number;
  z?: number;
}

export interface Size {
  width: number;
  height: number;
}

export interface HistoryEntry { position: Position; size: Size }

class NWindow extends HTMLElement {
  protected _isMinimized = false;
  protected _isMaximized = false;
  protected _isDragging = false;
  protected _isResizing = false;
  protected _fullscreen = false;
  protected _resizable = true;
  protected _zIndex = 1;
  protected _position: Position = { x: 0, y: 0 };
  protected _size: Size = { width: 600, height: 400 };
  protected _stateHistory: HistoryEntry[] = [];

  get isMinimized() {
    return this._isMinimized;
  }

  get id() {
    return this.getAttribute('id') || '';
  }
  
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }

  connectedCallback() {
    this.render();
    this.setupEventListeners();
  }

  updateSize() {
    this.style.width = `${this._size.width}px`;
    this.style.height = `${this._size.height}px`;
  }
  
  getZIndex(): number {
    const z = parseInt(this.style.zIndex ?? "0");
    return this._zIndex = isFinite(z) ? z : this._zIndex;
  }

  setZIndex(zIndex: number) {
    this._zIndex = zIndex;
    this.style.zIndex = zIndex.toString();
    return this;
  }


  render() {
    if (!this.shadowRoot) return;

    this.shadowRoot.innerHTML = `
      <style>
        :host {
          display: ${this._isMinimized ? 'none' : 'block'};
          position: absolute;
          left: ${this._position.x}px;
          top: ${this._position.y}px;
          width: ${this._size.width}px;
          height: ${this._size.height}px;
          z-index: ${this._zIndex};
          background: white;
          border-radius: 8px;
          box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
          overflow: hidden;
        }
        .title-bar {
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 8px;
          background: #f0f0f0;
       ...