{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "site-search", "title": "Site Search", "description": "Cmd/Ctrl-K site search palette on the Base UI autocomplete and dialog primitives — grouped results, keyboard filtering and an onSelect callback for framework routing.", "dependencies": [ "@base-ui/react", "clsx", "tailwind-merge" ], "registryDependencies": [ "https://ui.digital.nsw.gov.au/registry/r/theme.json", "https://ui.digital.nsw.gov.au/registry/r/button.json", "https://ui.digital.nsw.gov.au/registry/r/icons.json" ], "files": [ { "path": "src/components/site-search.tsx", "content": "'use client'\n\nimport { Autocomplete } from '@base-ui/react/autocomplete'\nimport { Dialog } from '@base-ui/react/dialog'\nimport React from 'react'\n\nimport { IconSearch } from '@/icons/search'\nimport { cn } from '@/lib/utils'\n\nimport { Button } from '@/components/button'\n\n/** A single searchable destination. `keywords` extend matching beyond the title. */\ntype SiteSearchItem = {\n title: string\n href: string\n keywords?: string[]\n}\n\n/** A titled section of results, rendered with a muted group heading. */\ntype SiteSearchGroup = {\n title: string\n items: SiteSearchItem[]\n}\n\ntype SiteSearchProps = {\n /** The searchable site map, as titled groups of items. */\n groups: SiteSearchGroup[]\n /**\n * Called with the chosen item on click or Enter, after the palette closes.\n * Navigation is the APP's job — call your router here (e.g. next/navigation's\n * `router.push(item.href)`). The design system ships no framework code, so it\n * never navigates itself; this mirrors the DS-wide framework-free rule\n * (see `Link` / `LinkProvider`).\n */\n onSelect: (item: SiteSearchItem) => void\n /** Whether the palette is open. Use when controlled. */\n open?: boolean\n /** Called whenever the palette asks to open or close. */\n onOpenChange?: (open: boolean) => void\n /** Whether the palette is initially open (uncontrolled). */\n defaultOpen?: boolean\n /**\n * Wire the global Cmd/Ctrl-K toggle on `document`, with cleanup on unmount.\n * Defaults to true. Only one shortcut-enabled SiteSearch should be mounted\n * per page, or each press toggles all of them.\n */\n shortcut?: boolean\n /**\n * Accessible name for the palette, applied as `aria-label` to the dialog\n * panel, the search input and the default trigger. Defaults to\n * `'Search site'`. Mirrors ExpandableSearch's `label` prop — localise it\n * rather than hardcoding English into consumers' pages.\n */\n label?: string\n /** Placeholder for the search input. */\n placeholder?: string\n /** Message shown when no items match the query. */\n emptyMessage?: string\n /**\n * The element that opens the palette. Defaults to a ghost icon `Button`\n * named by `label`. Pass an element to replace it — it is composed via Base\n * UI's `render` prop, so it inherits the trigger behaviour and ARIA — or\n * `null` to render no trigger at all (open via `open` or the shortcut).\n * A custom trigger owns its own accessible name — `label` is applied as\n * `aria-label` to the default icon button only, since overriding a visible\n * label that way fails WCAG 2.2, 2.5.3 Label in Name.\n *\n * Conditional triggers must resolve to `null`, not `false`: write\n * `cond ?