SlotGhost

SlotGhost is a dashed placeholder for an empty slot, with an optional caption. Use it to show where windows can go in a custom window layout.

import { SlotGhost } from 'jarvis-react-ui';

Basic

SlotGhost is absolutely positioned at rect inside its nearest positioned ancestor; the rect is applied through the CSS variables --lib-slot-ghost-x, -y, -w and -h. label adds a small uppercase caption in the middle.

import { SlotGhost } from 'jarvis-react-ui';

export default function Basic() {
    return (
        <div className="relative h-[120px] w-[412px]">
            <SlotGhost rect={{ x: 0, y: 0, w: 200, h: 120 }} label="Empty slot" />
            <SlotGhost rect={{ x: 212, y: 0, w: 200, h: 120 }} />
        </div>
    );
}

Empty slots

Render a SlotGhost for every slot without a window, so the grid stays readable while it is partly empty. Unlike the overlays, SlotGhost is positioned absolute, not fixed, and sits in the same layer as your windows.

import { useEffect, useRef, useState } from 'react';
import { SLOT_IDS, SlotGhost, Window, computeSlot } from 'jarvis-react-ui';
import type { SlotId, SlotRect } from 'jarvis-react-ui';

const ASSIGNED: Partial<Record<SlotId, string>> = { L1: 'System', R2: 'Agenda', B2: 'Transcript' };

// The slot grid is made for a full viewport; to fit the demo frame it is computed at 2× and halved.
function demoSlot(id: SlotId, w: number, h: number): SlotRect {
    const r = computeSlot(id, w * 2, h * 2);
    return { x: r.x / 2, y: r.y / 2, w: r.w / 2, h: r.h / 2 };
}

export default function EmptySlots() {
    const stageRef = useRef<HTMLDivElement>(null);
    const [size, setSize] = useState({ w: 0, h: 0 });

    useEffect(() => {
        const stage = stageRef.current;
        if (!stage) return;
        const observer = new ResizeObserver(() =>
            setSize({ w: stage.clientWidth, h: stage.clientHeight }),
        );
        observer.observe(stage);
        return () => observer.disconnect();
    }, []);

    return (
        <div ref={stageRef} className="relative h-[340px] w-full overflow-hidden">
            {size.w > 0 &&
                SLOT_IDS.map((id) => {
                    const rect = demoSlot(id, size.w, size.h);
                    const title = ASSIGNED[id];
                    return title === undefined ? (
                        <SlotGhost key={id} rect={rect} label={id} />
                    ) : (
                        <Window
                            key={id}
                            id={id}
                            title={title}
                            position={rect}
                            draggable={false}
                            itemRenderer={() => id}
                        />
                    );
                })}
        </div>
    );
}

Note: Not part of WindowManager

WindowManager does not render SlotGhost; its empty slots stay blank. SlotGhost is a building block for layouts you assemble yourself from Window, useSlotDrag and the slot grid helpers.

Accessibility

SlotGhost is decorative: its root is aria-hidden, so the label is not announced, and it takes no pointer events.

API

Prop Type Default Description
rect (required) SlotRect Position and size in pixels, relative to the nearest positioned ancestor.
className string Extra classes for the root <div> element (the dashed placeholder).
label string Small uppercase caption centred in the placeholder, e.g. the slot id.

Type SlotRect

Rectangular geometry (pixel-absolute, viewport-relative).

Field Type Default Description
h (required) number Height in px.
w (required) number Width in px.
x (required) number Left edge in px.
y (required) number Top edge in px.