Skip to content

05customization

Settings dialog

The settings module — the optional preferences dialog, its KloqPreferences model, and the account helpers behind it.

@/components/kloq/settings is its own module because it is genuinely optional: the calendar works with no settings surface at all, and a host with its own preferences screen wants the KloqPreferences model without pulling in a dialog it will never render.

The dialog owns no settings

SettingsDialog is a value / onChange pair over a plain KloqPreferences object and nothing else — no context, no localStorage, no writes into <Kloq>. That is forced by the architecture rather than chosen: "show weekends" and "start week on" are props on the calendar, so the only honest thing a settings surface can do is report intent upward and let the host pass it back down. A dialog that secretly mutated the calendar would be a second source of truth, and the two would drift the first time the host changed a prop.

app/calendar.tsx
Note

Fully controlled means exactly that: a host that ignores onChange gets a UI that visibly refuses to move, rather than one that looks like it worked and didn't.

The KloqPreferences model

Flat and serializable on purpose: persist the whole object under one key. Every field maps to either a <Kloq> prop or a host behaviour, and nothing is optional — a settings screen with tri-state values renders switches that are neither on nor off. DEFAULT_PREFERENCES matches the calendar's own defaults.

proptype · description
showWeekends
boolean

Choose the seven-day week or Monday–Friday workweek view.

showDeclinedEvents
boolean

Maps to the showDeclinedEvents prop.

showWeekNumbers
boolean

ISO week numbers in the gutter.

weekStartsOn
WeekStart

0 Sunday, 1 Monday, 6 Saturday.

navigationKeys
NavigationKeys

"range" — ←/→ step the visible range; "day" — always one day.

language
string

BCP 47 tag, or "" for "follow the system". LANGUAGE_OPTIONS is the built-in list; pass your own.

desktopNotifications
boolean

The host may show desktop notifications at all.

eventReminders
boolean

Fire an alert before each event — delivery is the host's job.

defaultReminderMinutes
number

A default reminder offset your app can stamp on new events.

dailyAgenda
boolean

A once-a-morning summary of the day.

SettingsDialogProps

Four panes — "general", "accounts", "conferencing", "notifications" (SettingsSection; defaultSection picks which opens first). Beyond the value / onChange pair, the props follow one honesty rule: a button with nothing behind it isn't rendered. Omit onAddAccount and there is no "Add calendar account" button — the package owns no OAuth flow, so a connect button that couldn't connect would be a lie.

proptype · description
open / onOpenChange?
boolean / (open) => void

Standard dialog control.

value
KloqPreferences

The whole preferences object, fully controlled.

onChange
(next: KloqPreferences) => void

The next preferences object on every change.

accounts / sources?
CalendarAccount[] / CalendarSource[]

Rows to render. Omit to read the calendar-source registry.

conferencing?
readonly ConferencingProvider[]

Options offered; default CONFERENCING_PROVIDERS.

languages?
readonly {id; label}[]

Default LANGUAGE_OPTIONS.

onAddAccount?
() => void

Omit and the button isn't rendered.

onDisconnectAccount / onSetAccountConferencing?
(accountId, …) => void

Forwarded to the account rows. Omit → the registry handles it.

defaultSection?
SettingsSection

Which pane opens first; default "general".

Accounts as first-class

Accounts are the one exception to "the dialog owns nothing" — they already have a registry (registerCalendarSources), and the account rows write through it unless the host passes handlers: the same controlled/uncontrolled split as everything else. The module re-exports the grouping's helpers so a host's own settings screen can build the same rows: findAccount, defaultAccount, setDefaultAccount, disconnectAccount, providerLabel, conferencingLabel, accountDefaultCalendar, setAccountDefaultCalendar, resolveDefaultCalendar, and setAccountConferencing.

SegmentedChoice

Every "pick one of a few" control in the dialog is SegmentedChoice — a segmented control that is a real radio group: one tab stop, arrow keys move the choice, and assistive tech hears "radio, 2 of 3". It is exported for hosts building matching settings UI. For 2–5 options it is the better control: every choice is visible and it costs one click, not two.