Skip to content
Recipes

Settings menus

Build nested, toggleable settings that update in place with ctx.render.

Settings are a great fit for the bar: users can change a preference without visiting a settings page. The trick is ctx.render, which re-renders the current view after a change so the checkmarks stay accurate.

import { grid, list, type ListView } from '@qikbar/qikbar'

function settingsView(): ListView {
  const s = store.settings
  const toggle = (key: keyof typeof s, label: string, icon: string) => ({
    id: key,
    title: label,
    icon,
    section: 'Preferences',
    accessories: [{ tag: s[key] ? { value: 'On', color: 'green' } : { value: 'Off', color: 'gray' } }],
    run: (ctx) => {
      store.update({ [key]: !s[key] })
      ctx.render(settingsView())   // re-render in place
      return false                  // keep the bar open
    },
  })

  return list({
    title: 'Settings',
    items: [
      toggle('compactMode', 'Compact mode', 'list'),
      toggle('emailDigest', 'Weekly email digest', 'mail'),
      toggle('sounds', 'Sounds', 'bell'),
      {
        title: 'Language',
        subtitle: s.language,
        icon: 'languages',
        section: 'Region',
        view: grid({
          title: 'Language',
          columns: 4,
          items: LANGUAGES.map((l) => ({
            title: l.name,
            subtitle: l.native,
            icon: l.flag, // an emoji
            run: (ctx) => {
              store.update({ language: l.code })
              ctx.pop()
              return false
            },
          })),
        }),
      },
    ],
  })
}

export const settingsCommand = { title: 'Settings', icon: 'settings', shortcut: 'mod+,', view: settingsView }

Because ctx.render keeps the query and selection, users can type “sound”, press ↵ repeatedly, and watch the toggle flip.

ctx.pop() followed by return false goes back one level and keeps the bar open. Omit return false to close the bar after the change.

Edit this page on GitHub