createAnnouncementPhrase
projects/core/utils/announcement-phrase.ts
Description#
Declarative live-region phrase with transition arming and expiry - the
shared shape behind "voice the change, not the standing state". A real
transition (arm) sets the phrase; an expiry transition (spend)
clears an already-voiced phrase so a later content change cannot
re-announce it; everything else keeps the previous phrase.
Fully derived (linkedSignal over source snapshots) - no effect, no
imperative previous-value tracking.
Eager-read caveat. linkedSignal only observes source snapshots it
is actually read under. A consumer that renders this phrase behind a
short-circuiting arm (busy() ? busyPhrase : phrase()) MUST read the
phrase eagerly before branching, otherwise the phrase never observes
the busy-start snapshot and the spend transition is skipped.
private readonly selectionPhrase = createAnnouncementPhrase({
source: () => ({ selected: this.selected(), loading: this.loading() }),
arm: (curr, prev) =>
curr.selected !== prev.selected ? (curr.selected ? 'Selected' : 'Deselected') : null,
spend: (curr, prev) => curr.loading && !prev.loading,
});