diff --git a/src-web/components/core/PairEditor.tsx b/src-web/components/core/PairEditor.tsx index b0526c484..1c1523a2e 100644 --- a/src-web/components/core/PairEditor.tsx +++ b/src-web/components/core/PairEditor.tsx @@ -78,6 +78,60 @@ export type PairWithId = Pair & { /** Max number of pairs to show before prompting the user to reveal the rest */ const MAX_INITIAL_PAIRS = 50; +interface SelectAllHeaderProps { + pairs: PairWithId[]; + setPairsAndSave: (fn: (pairs: PairWithId[]) => PairWithId[]) => void; +} + +function SelectAllHeader({ pairs, setPairsAndSave }: SelectAllHeaderProps) { + // INFO: excludes the empty "placeholder" pair until filled + const activePairs = pairs.slice(0, -1); + + const enabledCount = activePairs.filter((pair) => pair.enabled).length; + const selectAllState = + !activePairs.length || !enabledCount + ? false + : enabledCount === activePairs.length + ? true + : ('indeterminate' as const); + + const statusLabel = + selectAllState === true + ? 'All selected' + : selectAllState === 'indeterminate' + ? `${enabledCount}/${activePairs.length} selected` + : 'None selected'; + + const handleToggleAll = () => { + setPairsAndSave((oldPairs) => { + // INFO: excludes the empty "placeholder" pair until filled + const lastPair = oldPairs[oldPairs.length - 1]; + const activePairs = oldPairs.slice(0, -1); + + const shouldEnable = selectAllState === false; + + const updatedPairs = activePairs.map((pair) => ({ ...pair, enabled: shouldEnable })); + return lastPair ? [...updatedPairs, lastPair] : updatedPairs; + }); + }; + + // INFO: excludes the empty "placeholder" pair until filled + if (pairs.length <= 1) return null; + + return ( +
+ + {statusLabel} +
+ ); +} + export const PairEditor = forwardRef(function PairEditor( { allowFileValues, @@ -244,6 +298,7 @@ export const PairEditor = forwardRef(function Pa 'pt-0.5', )} > + {pairs.map((p, i) => { if (!showAll && i > MAX_INITIAL_PAIRS) return null;