/**
* Picks one string out of a supplied list of options, through a dropdown
* two-way bound to the caller's cell, with the empty string standing for
* nothing picked.
*
* Embed it as `` and
* the host's cell holds whatever is picked; the host reads one field rather
* than a value and a flag.
*
* The `select` stream is there for a host or an agent that sets the choice
* without a click, and it refuses a value that is not on offer.
*
* @hashtags picker, select, options, category, status, dropdown
* @keywords choose, pick one, select an option, dropdown, category, status,
* type, kind, single choice, enum, classification, tag one
*/
import {
action,
computed,
Default,
NAME,
pattern,
Stream,
UI,
type VNode,
Writable,
} from "commonfabric";
export interface OptionPickerInput {
/** The values on offer. */
options?: Writable>;
/** The value picked; the empty string while nothing is. */
selected?: Writable>;
/** Heading shown above the dropdown. */
label?: Writable>;
/** Text for the entry that stands for "nothing picked". */
placeholder?: Writable>;
}
export interface OptionPickerOutput {
[NAME]: string;
[UI]: VNode;
selected: string;
hasSelection: boolean;
options: string[];
select: Stream<{ option: string }>;
clear: Stream;
}
export const OptionPicker = pattern(
({ options, selected, label, placeholder }) => {
// A choice that is not on offer would leave the dropdown showing the
// placeholder while `selected` said otherwise, so it is refused.
const select = action(({ option }: { option: string }) => {
if (!options.get().includes(option)) return;
selected.set(option);
});
const clear = action(() => {
selected.set("");
});
const hasSelection = computed(() => (selected.get() ?? "") !== "");
const items = computed(() => [
{ label: placeholder.get(), value: "" },
...options.get().map((option) => ({ label: option, value: option })),
]);
return {
[NAME]: computed(() =>
`${label.get()}: ${hasSelection ? selected.get() : "none"}`
),
[UI]: (
{label}
),
selected,
hasSelection,
options,
select,
clear,
};
},
);
export default OptionPicker;