/** * A value the options do not contain is still the value. * * Any control that offers a list faces this: a form holds `"fr"`, the options arrive without it, and * the control has to show something. Erasing the value loses data the person could have fixed; hiding * it shows a control that disagrees with the form. So the value stays and the list gains an entry for * it, marked as unrecognised. * * Neutral ground, deliberately. It lived under `select/` and the multiselect — which is in `field/` — * reached across for it, while `select/` reached the other way for the field shell. Two directions * between two folders is one module with two names. */ import type { MdySelectOption } from "@modyra/core"; export interface MdySelectReconciliationState { readonly value: TValue | null; /** * Where a value used to be set aside while its option was missing. Always `null` on the way out: * an unrecognised value now stays in the model, so there is nothing to hold anywhere else. */ readonly parkedValue: TValue | null; } /** * Whether a value and an option's value are the same choice. * * Loose between primitives, because a value read from JSON arrives as `"1"` where the option holds * `1` and they are the same choice. * * Between objects the question is the same one and the answer is not `String()`: every plain object * renders as `[object Object]` through it, which would call two different entities one. The answer * is the key an option is identified by — the rule `oneOf` uses and the rule a part id is built from * (ADR 0051) — so an option that came back as a fresh object, which is what a restored draft, a * refetch and an import all produce, is the option it is a copy of. Compared by reference instead, * the list gained a second entry for a choice already in it, labelled with its own JSON and sharing * a key with the entry below it: a keyboard pointing at whichever the DOM found first. */ export declare function sameChoice(value: unknown, optionValue: unknown): boolean; /** * Reconciles a programmatic value against a changing option set. * * Loading or filtering options is not a user edit, and neither is a value the option set does not * recognise. **The widget does not write to the model to make itself consistent**: a value outside * the list is a value the form holds and the rules can judge — `oneOf()` is how a select says it is * wrong — and erasing it destroys the one thing that would let the user fix it. That matters most * where the value came from outside: an import that carries the name of a category that does not * exist yet is exactly the row a person has to see in order to resolve it. * * What is still repaired is the *representation*: a value that matches an option loosely — `"1"` * against `1`, as a value read from JSON does — is replaced by the option's own value, so the model * holds what the list holds and identity comparisons work. */ export declare function reconcileSelectValue(state: MdySelectReconciliationState, options: readonly MdySelectOption[]): MdySelectReconciliationState; /** * The option list a select renders, with a place for a value the list does not contain. * * A value the model holds and the list cannot show is invisible: the control appears empty, the * user is told nothing, and the only clue is a validation message about a value they cannot see. So * the unrecognised value is rendered as an option of its own, selected, and labelled by `label` * — by default the value itself, which is the only honest thing to call something the list has no * name for. * * Nothing is added while the list is empty: options that have not loaded yet are not a list that * refuses the value, and a placeholder would flash on every load. */ export declare function optionsWithUnrecognizedValue(options: readonly MdySelectOption[], value: TValue | null, labelFor?: (value: TValue) => string): readonly MdySelectOption[]; /** * The multi-value form of {@link optionsWithUnrecognizedValue}. * * A widget that holds several values has the same duty as one that holds one: what it will not * erase, it has to show. Unrecognised values come first, in the order the value holds them, and are * named by themselves — supply an option to name one differently. * * A value that is not a list is one value, as its singular sibling has always treated it. The two * ask the same question of the same kind of input, and this one guarded emptiness while the other * guarded shape: given a string, a number or an object — all of which `patchValue` accepts and the * model holds — it threw from inside the effect that draws the widget, and an effect that throws * stops running. The control kept whatever it was showing before the write, reported itself valid, * and there was nothing on the page to read or correct. */ export declare function optionsWithUnrecognizedValues(options: readonly MdySelectOption[], values: readonly TValue[] | TValue | null | undefined, labelFor?: (value: TValue) => string): readonly MdySelectOption[];