enhancement
major
minor
major
minor
major
#29622
TL Views: a `<flow-diagram selection="ch">` writes its channel but does not follow it, so a node selected elsewhere is not marked in the diagram
Problem
A <flow-diagram selection="ch"> publishes the user object of the node the user clicks to its selection channel, but it does not observe that channel: a value written to the channel by another element leaves the diagram's own marking where it is.
Observed on the Flow Diagram page of the React demo (demo/flow-diagram-demo.view.xml, a dashboard since #29613), where the diagram, a <table selection="selectedNode"> of the build steps and a <form input="selectedNode"> share one channel. Clicking the node "Querträger" in the diagram selects its row in the table, fills the form and marks the node (tlSelected). Picking the row "Unterboden" in the table then fills the form with Unterboden, but the diagram keeps "Querträger" marked, so two selectors of the same channel show different selections. A <table> bound to the same channel follows it in both directions.
In the React demo app (tl-demo-react) the marking is moreover not visible at all, not even for a node clicked in the diagram: the demo draws its marking as a border and fill with the class tlSelectionMarker, which the React flow stylesheet tl-react-flow.css hides, and the rule showing it on a selected node exists only in the stylesheet tl-blocks.css of the legacy graphic.blocks modules, which only the legacy tl-demo serves. The zoom indicator of the React diagram control is styled by that legacy stylesheet only, too.
Analysis
FlowDiagramElement resolves the selection channel ref and hands it to FlowDiagramControl.setSelectionChannel(), which only stores it. The control calls _selectionChannel.set(...) when the client reports a selection change and after a rebuild of the diagram, but registers no ViewChannel.ChannelListener on the channel, and it has no path from a channel value back to the selected SelectableBox nodes of the diagram on the client. The Config#getSelection() documentation describes the channel as write-only ("to write the selected node's user object to"), which is what the code does, but a channel that other selectors share has to be followed as well.
The transport for the reverse direction exists already: the diagram model is a msgbuf shared graph, the server scope observes every node it serialized, and FlowDiagramControl.pushDiagramChanges() ships recorded changes as a diagramPatch state update that the client applies and redraws node by node (SelectableBoxOperations.draw writes tlSelected). Selection changes made by the user travel the other way as the same kind of patch (update command); the selection command the control also handles is never sent by the client.
Solution
The rules by which a selector shares a channel with other selectors are those already implemented for <table> in TableSelectionBinding: the selector writes the channel only for a selection made in itself and for a displayed selection that vanished after a refresh; a value it merely does not display means "nothing selected here" and leaves the channel alone (it may belong to another selector over a different set of objects); the selector's own echo of an applied value is never written back. These rules are extracted into the generic SelectionChannelBinding in com.top_logic.layout.view.model, with hooks for the displayed selection, for applying a selection and for whether several values can be shown; TableSelectionBinding is a subclass of it. Several selected values are written as a Set by both selectors.
FlowDiagramControl has a selection API (the selected user objects, selecting user objects, a selection listener notified after a client patch, and a model listener notified after a rebuild of the diagram), and DiagramSelectionBinding binds it to the channel: a value set from outside is mapped to the nodes carrying it as user object, the nodes are marked on the server model and the change is pushed to the client as a patch. The nodes are found with WidgetTraversal (com.top_logic.react.flow.common), a generic walk over every widget reachable from the diagram through the model's own property values. After a rebuild of the diagram from its inputs the selection is carried over to the nodes the new diagram still has, the binding re-applies the channel value, and only then is the diagram serialized, so the marking is part of the diagram the client mounts on rather than a patch racing the remount; a displayed node that has no counterpart in the new diagram is dropped from the channel, as before (#29462). The binding is detached when the control is cleaned up. The write-only setSelectionChannel() and the unused selection command handler are removed; no client code changes.
tl-react-flow.css shows the tlSelectionMarker elements of a selected node and styles the zoom indicator (theme tokens, hidden again once the indicator's invisible class is set), so the React flow diagram no longer depends on a legacy stylesheet for either.
The Flow Diagram demo page is the test bed: picking a row in the Build steps table marks that node in the Construction Plan, and clicking a node still selects its row. The demo also draws each build step's image in its node, as the legacy demo does, and its Build steps table has no filter bar any more (it took most of the tile). TestDiagramSelectionBinding covers the channel rules including the client-patch path and the rebuild cases.
The <tree> element follows its channel already (#29502) through its own TreeSelectionBinding, a parallel implementation of the same rules; folding it onto SelectionChannelBinding is a separate work line.