Complexity Tiers
These progressive-complexity components let a section of the UI expose a Beginner / Intermediate / Advanced tier and share that state with its descendants. They are reusable components under $ui/ (source: packages/praxrr-app/src/lib/client/ui/), built around a Svelte context so a provider, a selector, and any consuming form can stay in sync without prop drilling.
ComplexityTierProvider
Section titled “ComplexityTierProvider”Wraps a section of UI and establishes the complexity-tier Svelte context for its descendants. It instantiates a per-section user tier store (keyed by sectionKey) and exposes tier state plus activity/suggestion callbacks via context, cleaning up the store on destroy.
Import:
import ComplexityTierProvider from'$ui/complexity/ComplexityTierProvider.svelte';Props:
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
sectionKey |
SectionKey (from $shared/complexity/tiers.ts) |
— |
Yes | Identifier for the section whose complexity tier state is managed. Passed to getUserComplexityTierSectionStore to scope the store. |
initialTier |
ComplexityTier ('beginner' | 'intermediate' | 'advanced') |
'beginner' |
No | Starting tier used when the section has no persisted tier yet. Provided as the initial value to the section store. |
Usage:
<ComplexityTierProvider sectionKey={CF_CONDITIONS} initialTier={sectionTiers[CF_CONDITIONS] ?? 'beginner'}> {#if enableComplexityTiers} <ComplexityTierSelector /> {/if} <!-- section content that reads the tier context --></ComplexityTierProvider>Notes:
- Uses the classic Svelte slot API (no runes; declares props with
export let). The default slot renders child content (for example section forms and aComplexityTierSelector) inside the provider so descendants can consume the tier context. - On mount it calls
setComplexityTierContext({ tier, advancedToggleCount, lastSuggestedTier, suggestionDismissedAt, recordActivity, dismissSuggestion, tierToDefaultMode }), sourced from the section store plus the importedtierToDefaultModehelper. - The store comes from
getUserComplexityTierSectionStore(sectionKey, initialTier)in$stores/userComplexityTiers, and the provider registersonDestroy(() => sectionStore.cleanup()). - It exposes no event or callback props directly; callbacks (
recordActivityanddismissSuggestion) are reached through the context object. - Real usage in
routes/custom-formats/[databaseId]/components/GeneralForm.sveltenests multiple providers (CF_CONDITIONS,CF_SCORING,CF_NEGATION_AND_GROUPS).
ComplexityTierSelector
Section titled “ComplexityTierSelector”Segmented button control that lets the user switch the active complexity tier (Beginner / Intermediate / Advanced) for the surrounding section, plus a Reset button that returns the tier to 'beginner'. Renders nothing unless a complexity-tier context is present.
Import:
import ComplexityTierSelector from'$ui/complexity/ComplexityTierSelector.svelte';Props:
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
— |
— |
— |
No | Takes no props. It reads all state from getComplexityTierContext(). |
Usage:
<ComplexityTierProvider sectionKey={CF_SCORING} initialTier={'beginner'}> <ComplexityTierSelector /></ComplexityTierProvider>Variants:
- Tier buttons render for each of
COMPLEXITY_TIERS('beginner' | 'intermediate' | 'advanced') with labels Beginner / Intermediate / Advanced. - The active tier button is highlighted (dark fill) and gets
aria-pressed={true}. - The Reset button (a
RotateCcwicon) is disabled when the active tier is already'beginner'.
Notes:
- It exposes no event or callback props. Internal
onclickhandlers callcontext.tier.set(tier)to change the tier andresetTier()to reset to'beginner'. - If no context exists (rendered outside a
ComplexityTierProvider), the whole markup is guarded by{#if context}and renders nothing. - It subscribes to
context.tierto track the active tier and unsubscribes inonDestroy. - The source notes that the UI Reset only sets the tier to
'beginner'; clearing suggestion metadata is a separate server-side concern (userComplexityTiersQueries.reset()). The icon is imported asRotateCcwfromlucide-svelte.
complexityTierContext (module)
Section titled “complexityTierContext (module)”Svelte context helpers and the typed contract for sharing complexity-tier state between ComplexityTierProvider and consumers like ComplexityTierSelector. It defines the ComplexityTierContext interface and set/get functions keyed by a private Symbol, and re-exports tierToDefaultMode.
Import:
import { setComplexityTierContext, getComplexityTierContext, tierToDefaultMode, type ComplexityTierContext} from '$ui/complexity/complexityTierContext';Exports:
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
ComplexityTierContext |
interface (see fields below) |
— |
No | Shape of the value stored in context. recordActivity has signature (activity: { interaction?: number; advancedToggle?: number }) => Promise<void>; dismissSuggestion has signature (suggestedTier: ComplexityTier) => Promise<void>. |
setComplexityTierContext |
(value: ComplexityTierContext) => void |
— |
No | Stores the given context value under the private COMPLEXITY_TIER_CONTEXT Symbol via Svelte’s setContext. Called by ComplexityTierProvider during init. |
getComplexityTierContext |
() => ComplexityTierContext | undefined |
— |
No | Retrieves the context via getContext; returns undefined if called outside component initialization (the getContext call is wrapped in try/catch). Used by ComplexityTierSelector. |
tierToDefaultMode |
(tier: ComplexityTier) => UiPreferenceMode |
— |
No | Re-exported from $shared/complexity/tiers.ts. Returns 'advanced' when tier === 'advanced', otherwise 'basic'. |
The ComplexityTierContext interface fields:
tier: Writable<ComplexityTier>advancedToggleCount: Readable<number>lastSuggestedTier: Readable<ComplexityTier | null>suggestionDismissedAt: Readable<string | null>recordActivity: UserComplexityTierSectionStore['recordActivity']dismissSuggestion: UserComplexityTierSectionStore['dismissSuggestion']tierToDefaultMode: (tier: ComplexityTier) => UiPreferenceMode
Usage:
import { setComplexityTierContext, getComplexityTierContext, tierToDefaultMode, type ComplexityTierContext,} from '$ui/complexity/complexityTierContext';
// in a provider componentsetComplexityTierContext({ tier, advancedToggleCount, lastSuggestedTier, suggestionDismissedAt, recordActivity, dismissSuggestion, tierToDefaultMode,});
// in a consumer componentconst context = getComplexityTierContext();if (context) context.tier.set('advanced');Notes:
- The context key is a module-private
Symbol('complexity-tier-context'), so the context is only reachable through these exported helpers. ComplexityTier('beginner' | 'intermediate' | 'advanced') andtierToDefaultModeoriginate in$shared/complexity/tiers.ts;UiPreferenceModeandSectionKeycome from$shared/disclosure/sectionKeys.ts.- The store types (
recordActivity/dismissSuggestion) are indexed fromUserComplexityTierSectionStorein$stores/userComplexityTiers.