Zum Inhalt springen

Eigene Toolbox

Die eigene Toolbox ersetzt Blocklys eingebautes Flyout durch eine HTML-Toolbox aus Kacheln der Morphic Blocks: keine Off-Screen-Blockly-Workspaces, nur DOM, das du gestalten kannst. Gib mount() einen Container dafür:

engine.mount({
workspaceContainer: document.getElementById("workspace")!,
toolboxContainer: document.getElementById("toolbox")!,
});

Die Kategorien kommen aus den Definitionen. Um die Toolbox später oder mit Optionen einzurichten, nutze stattdessen engine.mountToolbox():

engine.mountToolbox(document.getElementById("toolbox")!, {
categories: definitions.categories,
});

Eine Kachel in den Workspace zu ziehen — oder auf den Codespace — erzeugt den tatsächlichen Blockly-Block im Modell.

Option Standard Zweck
modeLabel true Kopfzeile oben in der Toolbox: true zeigt Mode: <name>, false keine, ein String diesen Text, eine Funktion (mode) => text ihr Ergebnis
blocks alle Nur eine Teilmenge der Blöcke zeigen (Liste von Identifiern)
categories — Kategorie-Gruppierung; ohne diese und ohne toolbox.categories der Mount-Konfiguration rendern Blöcke als flache Liste
highlight true Code, der auf Kacheln als Text steht (render: "text-based"), mit dem Highlighting seines Elements färben, wie der Codespace; false lässt ihn schlicht
touch true Kacheln lassen sich mit Finger oder Stift ziehen; siehe Touch und Tastatur
keyboard true Kacheln sind mit Tab erreichbar und werden mit Enter oder Leertaste hinzugefügt; siehe Touch und Tastatur
layout "stacked" "stacked": jede Kategorie unter ihrer Überschrift; "tabs": eine Liste der Kategorienamen und die Kacheln der gewählten; siehe Kategorien
categoryHeadings true Eine Überschrift mit dem Namen der Kategorie über ihren Kacheln
collapsible true Ein Klick auf eine Überschrift klappt ihre Kategorie zu oder auf
collapsed alle offen true klappt zu Beginn alle zu, eine Liste von Namen nur diese
onCategoryToggle — (name, collapsed) => …, etwa um den Zustand zu merken
scrollbar false false blendet die Scrollbar aus und lässt die Ränder auslaufen, wo weitere Kacheln warten; true zeigt sie; siehe Scrollen
custom — Wie jedes custom-Element gezeichnet wird, nach Element-Name; siehe Eigene Elemente
label was die Kachel zeigt Das Element, dessen Text jede Kachel für Screenreader benennt, oder (mode) => element; siehe Gesprochene Namen

Alle lassen sich auch in mount() unter toolbox setzen, sodass auch eine über toolboxContainer eingerichtete Toolbox sie bekommt:

engine.mount({
workspaceContainer,
toolboxContainer,
toolbox: { modeLabel: false, blocks: ["text_print", "loop_for"] },
});

Optionen, die an mountToolbox() übergeben werden, haben Vorrang vor denen in mount().

Kategorie-Einträge sind { name, color?, blocks? } — lässt eine Kategorie ihre blocks-Liste weg, leitet das Framework sie aus den Block-Definitionen ab, deren category-Feld zum Namen passt.

Jede Kachel rendert alle Elements eines Blocks; das CSS des aktiven Toolbox-Mode entscheidet, welche sichtbar sind (siehe Blocks & Elements für das Markup). Die Toolbox rendert neu, wenn sich der Toolbox-Mode ändert, über show({ toolbox }) oder einen Preset-Wechsel, und respektiert das render des Modes, um Code-Elemente als Blöcke oder als Quelltext zu zeigen.

Mit Kategorien bekommt jede eine Überschrift mit ihrem Namen. Ein Klick darauf, oder Enter oder Leertaste, klappt die Kategorie zu oder auf, und der Zustand bleibt, wenn die Kacheln neu gezeichnet werden (Mode- oder Preset-Wechsel). Das Markup:

<div class="morphic-toolbox morphic-toolbox-stacked">
<div data-category="Output" data-collapsed style="--morphic-category-color: #5C81A6">
<button class="morphic-category-heading" aria-expanded="false">Output</button>
<div class="morphic-category-tiles" hidden>…Kacheln…</div>
</div>
</div>

layout: "tabs" zeigt eine Kategorie auf einmal, wie Blocklys eigene Toolbox: eine Liste der Namen (.morphic-category-tabs, Buttons .morphic-category-tab mit aria-selected) und die Kacheln der gewählten Kategorie. Zu Beginn ist die erste gewählt; Kacheln ohne Kategorie bleiben sichtbar. Wo die Liste sitzt, bestimmt dein CSS, etwa neben den Kacheln:

.morphic-toolbox-tabs { display: grid; grid-template-columns: auto 1fr; gap: 0.5rem; }
.morphic-category-tabs { display: flex; flex-direction: column; gap: 4px; }
.morphic-category-tab[aria-selected="true"] { background: var(--morphic-category-color); color: white; }

Die Breite der Toolbox zählt jede Kategorie, ob zugeklappt oder nicht, das Aufklappen verbreitert sie also nie. Ohne Kategorien sind die Kacheln eine flache Liste, und "tabs" warnt über onWarning.

Standardmäßig blendet die Toolbox ihre Scrollbar aus und lässt den Rand auslaufen, wo oben oder unten weitere Kacheln warten. Der Container bekommt Klassen, um das selbst zu gestalten:

Klasse Solange
morphic-toolbox immer
morphic-toolbox-more-above oben Kacheln verborgen sind
morphic-toolbox-more-below unten Kacheln verborgen sind

Die Standard-Styles haben kein Gewicht (:where()), jede deiner Regeln gewinnt also, etwa ein Schatten statt des Auslaufens:

.morphic-toolbox-more-below { mask-image: none; box-shadow: inset 0 -12px 12px -12px rgb(0 0 0 / 0.3); }

scrollbar: true behält die normale Scrollbar (gestalte sie mit scrollbar-width und scrollbar-color); die Klassen bleiben. Das funktioniert, wenn der Toolbox-Container das Element ist, das scrollt (overflow-y: auto).

Die Toolbox funktioniert ohne Maus. Beides ist standardmäßig an und lässt sich in den Toolbox-Optionen abschalten (touch: false, keyboard: false).

Touch und Stift. Eine Kachel bewegt sich, wenn sie seitwärts gezogen oder lange gedrückt wird; eine Wischbewegung nach oben oder unten scrollt weiter die Toolbox. Sie lässt sich auf dem Workspace und auf jedem Codespace ablegen. Eine seitwärts angeordnete Toolbox gibt ihren Kacheln touch-action: pan-x, sodass Wischen entlang der Toolbox scrollt und Wischen nach oben oder unten zieht.

Tastatur. Jede Kachel ist ein Button in der Tab-Reihenfolge, für Screenreader benannt nach dem, was sie zeigt: Code so, wie der Codespace ihn schreiben würde, Text wie er ist und Bilder nach ihrem Alt-Text. Enter oder Leertaste fügt den Block hinter dem ausgewählten ein, wenn er dort passt, sonst als neuen Stapel, und wählt ihn aus, sodass erneutes Drücken eine Kette baut. Der Fokus bleibt auf der Kachel. Gib dem Fokus einen sichtbaren Stil:

.morphic-block:focus-visible { outline: 2px solid; }

Was eine Kachel zeigt, ist nicht immer das, was ein Screenreader sagen soll: ein Icon-Modus zeigt ein Bild, ein Code-Modus liest print(%1) vor. Ein Element, das kein Modus aufführt, ist auf jeder Kachel versteckt, gehört aber zum Block und kann so den gesprochenen Namen enthalten. label sagt der Toolbox, die Kacheln danach zu benennen:

{
"elementTypes": { "icon": "image", "title": "text", "spoken": "text" },
"blocks": [
{ "identifier": "print", "elements": {
"icon": "icons/print.svg", "title": "Ausgabe", "spoken": "Ausgabe-Block: schreibt eine Nachricht" } }
]
}
engine.mount({ workspaceContainer, toolboxContainer, toolbox: { label: "spoken" } });

Ein Block ohne dieses Element behält den Standardnamen. Für mehrere Sprachen bekommt jede ihr eigenes Element, und du übergibst eine Funktion; sie wird bei jedem Neuzeichnen erneut gefragt, also zeichne die Toolbox nach einem Sprachwechsel neu:

let language = "en";
engine.mount({ /* … */ toolbox: { label: () => `spoken-${language}` } });
language = "de";
engine.show({ toolbox: engine.getToolboxMode() });

Ein Vorlese-Button auf den Kacheln gehört der App: ein eigenes Element kann denselben Text mit der Sprachausgabe des Browsers vorlesen.

Der Workspace selbst gehört Blockly. Um Blöcke dort mit der Tastatur zu bewegen und zu bearbeiten, füge deiner App Blocklys Plugin für die Tastaturnavigation hinzu; das Framework überlässt dir diese Wahl.

Die Toolbox ist über strukturelle Klassen hinaus bewusst unstilisiert. Zielklassen:

.morphic-block { /* jede Kachel */ }
.morphic-mode-icon-tiles .morphic-element-title { /* pro Mode, pro Element */ }
[data-category="Output"] { /* Kategorie-Wrapper */ }

Siehe Modes mit CSS gestalten für den vollständigen CSS-Vertrag.

Die Toolbox nimmt die Breite, die dein Layout ihr gibt; ein breiterer Block oder eine breitere Codezeile wird abgeschnitten. Das Framework veröffentlicht den breitesten Block oder als Text gezeigten Code (render: "text-based") einer Kachel als --morphic-toolbox-block-width am Toolbox-Container, aktualisiert bei jedem neuen Zeichnen der Kacheln (Mode- oder Schriftwechsel). Die Variable steht am Container, nutze sie also dort, plus deinem eigenen Kachel-Innenabstand, und lass die Spalte darum auf seine Mindestbreite wachsen:

#toolbox {
min-width: calc(var(--morphic-toolbox-block-width) + 40px);
}
.layout {
grid-template-columns: minmax(250px, min-content) 1fr;
}

Nur Blöcke und Code zählen, da beide nicht umbrechen; lange Beschreibungen, die umbrechen, verbreitern ihn nicht. Als Text gezeigter Code wird auf der Seite gemessen, eine Toolbox, die beim Zeichnen ihrer Kacheln verborgen ist, zählt also bis zum nächsten Neuzeichnen nur ihre Blöcke. Ohne eine solche Regel ändert sich nichts.

Eine Kachel hat keinen eigenen Hintergrund; jeder Rahmen um den Block kommt aus deinem CSS. Um nur den Blockly-Block zu zeigen, gib der Toolbox einen Mode, der nur das Code-Element auflistet, und lass sein CSS die Kachel genau so groß wie den Block machen:

"modes": [{ "name": "block-only-tiles", "elements": ["concept"] }]
.morphic-block.morphic-mode-block-only-tiles {
background: none;
border: 0;
padding: 0;
width: fit-content;
}