One aspect related to building out signal support in Flow is how to show different components based on the value of some signal. As a simple example, let’s assume we have a widthSignal and want to show one out of three different alternative components depending on the available width.
Assuming bindVisible from Thoughts about conventions and naming for binding signals to components?, the straightforward way without any additional help would be to add all three components to the parent layout and manually create separate computed signals for the visibility of each component so that only one is visible.
layout.add(big, medium, small);
big.bindVisible(widthSignal.map(width -> width > 1000));
medium.bindVisible(widthSignal.map(width -> width > 500 && width <= 1000));
small.bindVisible(widthSignal.map(width -> width <= 500));
There are two limitations with this simple approach. First, you have to spend CPU and memory on instances that might never be used. Second, the rules are fragile since each boundary is defined in two separate places and mixing up exclusive and inclusive comparisons might lead to showing two components or no component at all for some specific widths.
There could be a wrapper component that selects the content to show based on a signal and instantiates components on demand. The logic to determine what to show could be exclusive so that it evaluates the condition for each potential content component and uses the first one that matches while ignoring any subsequent matches. It could select and show multiple components simultaneously if some options are not defined as exclusive.
Selector<Integer> selector = new Selector<>(widthSignal);
layout.add(selector);
selector.addExclusive(Big::new, width -> width > 1000);
selector.addExclusive(Medium::new, width -> width > 500);
selector.addFallback(Small::new);
There could also be overloads that take a single trigger value rather than a dynamically evaluated condition. This could be used as-is in many cases whereas this case with width ranges would require a separate type to define the option to render.
enum Size { BIG, MEDIUM, SMALL }
Signal<Size> sizeSignal = widthSignal.map(width -> {
if (width > 1000) return Size.BIG;
else if (width > 500) return Size.MEDIUM;
else return Size.SMALL;
});
Selector<Size> selector = new Selector<>(sizeSignal);
layout.add(selector);
selector.add(Big::new, Size.BIG);
selector.add(Medium::new, Size.MEDIUM);
selector.add(Small::new, Size.SMALL);
Does this seems like a sensible approach in general?
Do you see any scenarios that are not covered by the relatively simple example I’ve used?
Is Selector the right name?