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.
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.
| prop | type · description |
|---|---|
showWeekends | booleanChoose the seven-day week or Monday–Friday workweek view. |
showDeclinedEvents | booleanMaps to the |
showWeekNumbers | booleanISO week numbers in the gutter. |
weekStartsOn | WeekStart
|
navigationKeys | NavigationKeys
|
language | stringBCP 47 tag, or |
desktopNotifications | booleanThe host may show desktop notifications at all. |
eventReminders | booleanFire an alert before each event — delivery is the host's job. |
defaultReminderMinutes | numberA default reminder offset your app can stamp on new events. |
dailyAgenda | booleanA 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.
| prop | type · description |
|---|---|
open / onOpenChange? | boolean / (open) => voidStandard dialog control. |
value | KloqPreferencesThe whole preferences object, fully controlled. |
onChange | (next: KloqPreferences) => voidThe 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 |
languages? | readonly {id; label}[]Default |
onAddAccount? | () => voidOmit and the button isn't rendered. |
onDisconnectAccount / onSetAccountConferencing? | (accountId, …) => voidForwarded to the account rows. Omit → the registry handles it. |
defaultSection? | SettingsSectionWhich pane opens first; default |
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.