=> ({
freeform: msg("Freeform"),
flow: msg("Flow"),
flex: "Flex",
grid: "Grid",
});
const getToolbarQuickItems = () => [
{ type: "h1", glyph: wbH1Icon, label: "H1" },
{ type: "paragraph", glyph: wbTextIcon, label: msg("Text") },
{ type: "image", glyph: wbImageIcon, label: msg("Image") },
{ type: "icon", glyph: wbIconIcon, label: msg("Icon") },
{ type: "button", glyph: wbButtonIcon, label: msg("Button") },
{ type: "divider", glyph: wbDividerIcon, label: msg("Divider") },
];
export function renderFloatingToolbar(host: WebwriterWebsiteBuilder) {
const isAuthor = host.isContentEditable;
const showAdd = isAuthor ? host.showAddButton : host.showToolbarInStudent;
const showLayout = isAuthor
? host.showLayoutDropdown
: host.showToolbarInStudent;
if (!showAdd && !showLayout) return null;
const hidden = host.toolbarKeyHidden;
const visibleModes = (
Object.keys(host.visibleLayoutModes) as LayoutMode[]
).filter((m) => host.visibleLayoutModes[m]);
return html`
e.stopPropagation()}
>
${showAdd ? renderAddRow(host) : null}
${showLayout ? renderLayoutDropdown(host, visibleModes) : null}
${renderGroupToolbar(host)}
`;
}
function renderAddRow(host: WebwriterWebsiteBuilder) {
return html`
`;
}
function renderLayoutDropdown(
host: WebwriterWebsiteBuilder,
visibleModes: LayoutMode[],
) {
return html`
${host.layoutDropdownOpen
? html`
${visibleModes.map(
(mode) => html`
`,
)}
`
: null}
`;
}
export function renderGroupToolbar(host: WebwriterWebsiteBuilder) {
if (host.selectedIds.size < 2) return null;
if (!host.isContentEditable) return null;
return html`
e.stopPropagation()}>
${host.selectedIds.size} ${msg("selected")}
{
host.groupTemplateId = (e.target as HTMLSelectElement).value;
}}
>
${getContainerTemplates().map(
(t) =>
html`
${t.label}
`,
)}
`;
}
// ─── Layout button (segmented control) ───────────────────────────────────────
export function renderLayoutBtn(
host: WebwriterWebsiteBuilder,
mode: LayoutMode,
label: string,
) {
const active = host.layoutMode === mode;
return html`
`;
}
// ─── Palette tile (drag + click for info popup) ───────────────────────────────
export function renderPaletteTile(
host: WebwriterWebsiteBuilder,
type: string,
opts: { compact: boolean },
) {
const comp = ComponentRegistry[type];
const label = comp?.label?.() ?? type;
const onDragStart = (e: DragEvent) => {
host.suppressNextClick = true;
setTimeout(() => (host.suppressNextClick = false), 0);
host.drag.onDragStart(e);
requestAnimationFrame(() => {
host.trayOpen = false;
host.requestUpdate();
});
};
const onClick = (e: MouseEvent) => {
if (host.suppressNextClick) return;
e.stopPropagation();
host.infoForType = type;
host.infoAnchorEl = e.currentTarget as HTMLElement;
host.requestUpdate();
};
return html`
${tileGlyph(type)}
${label}
`;
}
// ─── Info popup (shown when clicking a palette tile) ──────────────────────────
export function renderInfoPopup(host: WebwriterWebsiteBuilder) {
if (!host.infoForType || !host.infoAnchorEl) return null;
const comp = ComponentRegistry[host.infoForType];
const label = comp?.label?.() ?? host.infoForType;
const syntax = componentSyntaxHint(host.infoForType);
const insert = () => {
host.quickAdd(host.infoForType!);
host.infoForType = null;
host.infoAnchorEl = null;
host.trayOpen = false;
host.requestUpdate();
};
return html`
e.stopPropagation()}
>
${label}
${msg("Insert")}
${msg("Drag to place on canvas. Click Insert to add at default position.")}
${msg("Syntax (preview)")}
${syntax}
`;
}