enhancement
major
minor
major
minor
major
#29626
TL Views: a panel toolbar has no overflow mode; at 400 px width the ticket page's six commands run 908 px wide, three of them unreachable, and the panel title is pushed out
Problem
The header <panel> of the ticket page in the issue tracker tl-dev (ticket.view.xml) carries six toolbar commands (Back, Accept, Resolve, New subtask, Delete, plus the form's Edit), each rendered as icon plus label.
Measured at 400×800 (engine snapshot build 37 of 2026-09-16): the toolbar is 908 px wide inside a 400 px .tlPanel__header (scrollWidth 932, clientWidth 400, overflow-x: visible). "Zurück" (24..137 px) and "Übernehmen" (145..298 px) are visible, "Abschließen" (306..457 px) is cut in half, "Neue Teilaufgabe" (465..650 px), "Löschen" (658..781 px) and "Bearbeiten" (788..932 px) are unreachable - nothing scrolls them in, and there is no overflow menu. The panel title "Ticket" is not rendered at all, pushed out by the toolbar.
The ReactToolbarControl has group displays (ToolbarGroupDisplay.INLINE, …) but no rule for the case that the inline buttons do not fit.
The same applies to the footer of a dialog window: the <actions> of a <window> (typically Cancel) and the buttons of the Java-built dialogs (unsaved-changes confirmation with Cancel/Discard/Save, confirm dialog, I18N editor, table column dialogs, password prompts) are plain siblings in a right-aligned flex row and never collapse. Once they are wider than the footer, the footer overflows at its left edge, so the first thing cut off is exactly the overflow trigger of the collapsed button bar beside them.
Request
A panel toolbar that fits its header at any width: buttons that do not fit move into an overflow menu ("…") in order, labels give way to icons before that where command-display allows it, and the panel title keeps its place. The app bar's <app-bar> actions have the same need on a phone, and so has the footer of a dialog window.
Lösung
Die Toolbar der TL Views (TLToolbar, das Client-Gegenstück von ReactToolbarControl) passt sich der verfügbaren Breite an. Sie gibt ihre natürliche Breite als eigene Breite an (daran bemisst ihr Host seinen Platz), misst, was der Host ihr tatsächlich gewährt (ResizeObserver), und klappt in zwei Stufen zusammen:
- Schaltflächen mit Icon und Beschriftung zeigen nur noch ihr Icon (Modifikator tlToolbar--compact; die Beschriftung bleibt zugänglicher Name und Tooltip). Schaltflächen ohne Icon behalten ihre Beschriftung.
- Einträge, die danach immer noch nicht passen, wandern in ein Überlaufmenü ("…", zugänglicher Name js.toolbar.overflow), in ihrer ursprünglichen Reihenfolge. Eine bereits als Menü dargestellte Clique (View, Export, More) wird als eigener, durch Trennlinie abgesetzter Abschnitt übernommen; verborgene Befehle erzeugen keine leeren Menüeinträge.
Wird wieder mehr Platz verfügbar, kehren die Einträge in umgekehrter Reihenfolge zurück.
Die Richtung des Zusammenklappens ist Zustand der Toolbar (ReactToolbarControl#OVERFLOW, Enum ToolbarOverflow: NONE, TRAILING, LEADING) und wird vom ToolbarBuilder aus der Platzierung der Kommandos abgeleitet:
- TOOLBAR (Panel-Kopf, Fenster-Titelzeile, Vollseite): Die Toolbar klappt von rechts nach links zusammen, das Überlaufmenü steht am rechten Ende. Der Host gibt der Toolbar den Vorrang beim Schrumpfen (--tl-toolbar-shrink-priority) und lässt sie nie unter die Breite des Menü-Auslösers (--tl-toolbar-trigger-size) fallen; der Paneltitel wird erst gekürzt, wenn die Toolbar auf den Auslöser geschrumpft ist.
- BUTTON_BAR (Dialog- und Panel-Fußzeile): Die Leiste klappt von links nach rechts zusammen, das Überlaufmenü steht am linken Ende. Die primäre Aktion am rechten Ende bleibt so am längsten sichtbar.
- Toolbars, die nicht über den ToolbarBuilder entstehen, behalten NONE.
Die Aktionen der <app-bar> werden über denselben Mechanismus dargestellt: AppBarElement baut seine Aktionen wie die anderen Kommando-Scopes über ToolbarBuilder als ein ReactToolbarControl auf, das bei Änderungen des Scopes an Ort und Stelle neu befüllt wird (replaceGroups); ReactAppBarControl ist kein ToolbarControl mehr. Die Media-Query, die Beschriftungen unterhalb von 768 px ausblendete, entfällt zugunsten der Anpassung an die tatsächlich verfügbare Breite.
Fußzeile eines Dialogfensters. Die Fußzeile eines ReactWindowControl ist eine einzige zusammenklappbare Schaltflächenleiste (ToolbarOverflow.LEADING), die das Fenster selbst besitzt. Die <actions> des <window> (bzw. die per setActions übergebenen Schaltflächen der Java-Dialoge) bilden darin die vorderste Gruppe, dahinter folgen die BUTTON_BAR-Kommandos des Scopes. Die Schaltflächen lesen sich damit als "Abbrechen, …, primäre Aktion" – die Reihenfolge, die Carbon, Material und die Apple-Richtlinien vorgeben (die primäre Aktion steht rechts) – und die zuerst eingeklappte Schaltfläche ist Abbrechen, das über Escape und das X der Titelzeile erreichbar bleibt. setActions und setButtonBar behalten ihre Signatur; ein Neuaufbau des Scopes ersetzt nur die Kommandogruppen, die Aktionsgruppe bleibt. Der Client rendert in der Fußzeile nur noch dieses eine Toolbar-Kind; den Zustandsschlüssel actions des Fensters gibt es nicht mehr.
Auf dem Weg behoben: Der Wrapper, in dem ein <form> seine eigenen <commands> dem umgebenden Scope anbietet (FormElement.FormScopedCommandModel), delegierte nur einen Teil der CommandModel-Zugriffe; Tastenkürzel (key="ENTER"), Tooltip, Clique, Anzeigemodus und CSS-Klassen eines Formular-Kommandos gingen verloren. Er delegiert jetzt vollständig (abgesichert durch TestFormScopedCommands).
Demo: Im Dialog "Neues Ticket" von tl-demo-react gibt es ein zweites Fußzeilen-Kommando ("Geschlossen anlegen"), damit das Überlaufverhalten einer Schaltflächenleiste sichtbar ist; "Anlegen" trägt key="ENTER".