Back to Black Site Map Editor
SOURCE / PINNED RELEASE

Made of little things.

Black Site Map Editor

Release
32a57733b7e9…
Author-recorded commit
3f54890f542f…
License
LICENSE
Author’s source reference
nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/black-site-map-editor

Archive hash verified: 7a4b1abbac7db5b0…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/components/bs-workspace.ts
import { html } from 'lit';
import { editor } from '../editor-controller';
import { mountPaneResizers } from '../pane-resizer';
import type { WorkspaceTab } from '../state/editor-state';
import { LightElement } from './light-element';
import './bs-tool-palette';
import './bs-map-canvas';
import './bs-inspector';

const tabs: [WorkspaceTab, string][] = [
    ['map', 'Map'],
    ['tools', 'Tools'],
    ['details', 'Details'],
];

/** Mobile tabs plus the three-column workspace with resizable side panes. */
export class BsWorkspace extends LightElement {
    constructor() {
        super();
        this.watch((s) => [s.workspaceTab, s.busy]);
    }
    render() {
        const { workspaceTab, busy } = editor.state;
        return html`<nav class="tabs" aria-label="Workspace">
                ${tabs.map(
                    ([tab, label]) =>
                        html`<button
                            data-tab=${tab}
                            class=${tab === workspaceTab ? 'active' : ''}
                            @click=${() => editor.setWorkspaceTab(tab)}
                        >
                            ${label}
                        </button>`,
                )}
            </nav>
            <div class="workspace" data-tab=${workspaceTab} ?inert=${busy}>
                <aside id="tool-panel" class="pane left"><bs-tool-palette></bs-tool-palette></aside>
                <section class="canvas-pane" aria-label="Map workspace">
                    <bs-map-canvas></bs-map-canvas>
                    <div
                        id="resizer-left"
                        class="pane-resizer left"
                        role="separator"
                        aria-orientation="vertical"
                        tabindex="0"
                        aria-label="Resize map tools panel"
                    ></div>
                    <div
                        id="resizer-right"
                        class="pane-resizer right"
                        role="separator"
                        aria-orientation="vertical"
                        tabindex="0"
                        aria-label="Resize details panel"
                    ></div>
                </section>
                <aside id="inspector" class="pane right"><bs-inspector></bs-inspector></aside>
            </div>`;
    }
    firstUpdated() {
        // Side panes resize by dragging their handles on the canvas edges, arrow keys on a
        // focused handle, or a double-click reset. The canvas ResizeObserver refits the map.
        const $ = (selector: string) => this.querySelector<HTMLElement>(selector)!;
        mountPaneResizers($('.workspace'), {
            left: {
                handle: $('#resizer-left'),
                pane: $('#tool-panel'),
                defaultWidth: 174,
                min: 120,
                max: 420,
            },
            right: {
                handle: $('#resizer-right'),
                pane: $('#inspector'),
                defaultWidth: 260,
                min: 200,
                max: 560,
            },
        });
    }
    updated() {
        // Switching mobile tabs reveals the canvas; redraw once layout settles.
        requestAnimationFrame(() => editor.ui.canvas?.draw());
    }
}
customElements.define('bs-workspace', BsWorkspace);