fix(map): canonicalize facet visibility and compact header
This commit is contained in:
@@ -105,42 +105,56 @@ export function mapPresentationBindingIsAll(bindingId: string, filters: MapPrese
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply one interactive facet-chip transition without collapsing the storage
|
||||
* contract. A missing field means that facet is not part of the current union,
|
||||
* while one explicitly empty field represents the intentional match-nothing
|
||||
* view after the final selected chip is switched off.
|
||||
* A missing facet is the compact canonical representation of every configured
|
||||
* value being enabled. An explicit array is the exact enabled subset; an empty
|
||||
* array therefore remains an intentional match-nothing state.
|
||||
*/
|
||||
export function toggleMapPresentationFacetSelection(
|
||||
export function mapPresentationFacetValueIsEnabled(
|
||||
facets: Record<string, string[]>,
|
||||
field: string,
|
||||
value: string,
|
||||
) {
|
||||
const selected = facets[field];
|
||||
if (!selected?.includes(value)) {
|
||||
// A saved empty view can contain one or more empty facet arrays. They are
|
||||
// a global zero-match sentinel, not active constraints, so selecting the
|
||||
// first chip must replace them instead of leaving a hidden empty array that
|
||||
// would continue to suppress every fact.
|
||||
const activeFacets = Object.fromEntries(
|
||||
Object.entries(facets).filter(([, values]) => values.length > 0),
|
||||
);
|
||||
return { ...activeFacets, [field]: [...(activeFacets[field] ?? []), value] };
|
||||
}
|
||||
return selected === undefined || selected.includes(value);
|
||||
}
|
||||
|
||||
const nextSelected = selected.filter((item) => item !== value);
|
||||
if (nextSelected.length > 0) return { ...facets, [field]: nextSelected };
|
||||
export function normalizeMapPresentationFacetSelections(
|
||||
facets: Record<string, string[]>,
|
||||
profile: MapPresentationProfile,
|
||||
) {
|
||||
return Object.fromEntries(profile.facets.flatMap((facet) => {
|
||||
const selected = facets[facet.field];
|
||||
if (selected === undefined) return [];
|
||||
const availableValues = [...new Set(facet.values.map((item) => item.value))];
|
||||
const enabledValues = availableValues.filter((value) => selected.includes(value));
|
||||
// Legacy layouts could persist every value explicitly. Canonicalize that
|
||||
// shape to an unconstrained facet so scoped facets (for example movement
|
||||
// on online subjects) cannot accidentally suppress unrelated subjects.
|
||||
return availableValues.length > 0 && enabledValues.length === availableValues.length
|
||||
? []
|
||||
: [[facet.field, enabledValues] as const];
|
||||
}));
|
||||
}
|
||||
|
||||
const { [field]: _removed, ...remaining } = facets;
|
||||
const activeFacets = Object.fromEntries(
|
||||
Object.entries(remaining).filter(([, values]) => values.length > 0),
|
||||
);
|
||||
if (Object.keys(activeFacets).length > 0) return activeFacets;
|
||||
|
||||
// Never collapse the last interactive deselection to `{}`: the renderer
|
||||
// correctly interprets `{}` as unconstrained/all. Retain an explicit empty
|
||||
// facet so the zero-selection state remains zero matches through any toggle
|
||||
// cycle and through a later Application Save.
|
||||
return { [field]: [] };
|
||||
export function toggleMapPresentationFacetSelection(
|
||||
facets: Record<string, string[]>,
|
||||
field: string,
|
||||
value: string,
|
||||
availableValues: string[],
|
||||
) {
|
||||
const values = [...new Set(availableValues)];
|
||||
if (!values.includes(value)) return facets;
|
||||
const selected = facets[field];
|
||||
const enabled = new Set(selected === undefined
|
||||
? values
|
||||
: values.filter((item) => selected.includes(item)));
|
||||
if (enabled.has(value)) enabled.delete(value);
|
||||
else enabled.add(value);
|
||||
const nextEnabled = values.filter((item) => enabled.has(item));
|
||||
const next = { ...facets };
|
||||
if (nextEnabled.length === values.length) delete next[field];
|
||||
else next[field] = nextEnabled;
|
||||
return next;
|
||||
}
|
||||
|
||||
export function mapPresentationProfileForFact(
|
||||
|
||||
Reference in New Issue
Block a user