{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "search-results-page",
  "title": "Search Results Page",
  "type": "registry:page",
  "files": [
    {
      "path": "packages/registry-angular/blocks/search-results-page/search-results-page.component.ts",
      "content": "// Full search results page: pre-filled search input with result count, removable\n// filter chips with clear-all, a left facet sidebar, six result rows, and a\n// Previous/Next pagination footer. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport {\n  BookOpen,\n  FileText,\n  GitBranch,\n  LucideAngularModule,\n  MessageSquare,\n  Search,\n  Users,\n  Video,\n  type LucideIconData,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiCheckboxComponent } from '@/ui/checkbox/checkbox.component'\nimport { UiChipComponent } from '@/ui/chip/chip.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\n\ninterface SearchResult {\n  id: string\n  icon: LucideIconData\n  title: string\n  path: string\n  snippet: string\n  type: string\n  updated: string\n}\n\ninterface FacetOption {\n  id: string\n  label: string\n  count: number\n}\n\ninterface FacetGroup {\n  id: string\n  label: string\n  options: FacetOption[]\n}\n\ninterface FilterChip {\n  id: string\n  label: string\n}\n\nconst RESULTS: SearchResult[] = [\n  {\n    id: 'r1',\n    icon: BookOpen,\n    title: 'Onboarding flows: design your first-run experience',\n    path: 'docs.acme.dev / guides / onboarding-flows',\n    snippet:\n      'Learn how to compose onboarding flows from checklists, tours, and progressive disclosure so new users reach their first aha moment faster.',\n    type: 'Docs',\n    updated: '2 days ago',\n  },\n  {\n    id: 'r2',\n    icon: FileText,\n    title: 'Checklist API reference for onboarding flows',\n    path: 'docs.acme.dev / api / checklist',\n    snippet:\n      'Full reference for the Checklist primitive used to drive onboarding flows, including step completion events, persistence, and theming.',\n    type: 'Reference',\n    updated: '5 hours ago',\n  },\n  {\n    id: 'r3',\n    icon: Video,\n    title: 'Video walkthrough: building onboarding flows in 20 minutes',\n    path: 'learn.acme.dev / videos / onboarding-flows-walkthrough',\n    snippet:\n      'A hands-on screencast that builds a complete onboarding flow with branching steps, skippable tours, and analytics instrumentation.',\n    type: 'Video',\n    updated: '1 week ago',\n  },\n  {\n    id: 'r4',\n    icon: MessageSquare,\n    title: 'How do you measure completion of onboarding flows?',\n    path: 'community.acme.dev / discussions / 4821',\n    snippet:\n      'Community thread comparing activation-rate funnels, step-drop-off charts, and qualitative surveys for measuring onboarding flow success.',\n    type: 'Discussion',\n    updated: '3 days ago',\n  },\n  {\n    id: 'r5',\n    icon: GitBranch,\n    title: 'Example repo: onboarding flows with A/B-tested variants',\n    path: 'github.com / acme / examples / onboarding-ab-test',\n    snippet:\n      'Runnable example that splits new signups into two onboarding flow variants and reports which one converts better to first project creation.',\n    type: 'Example',\n    updated: '2 weeks ago',\n  },\n  {\n    id: 'r6',\n    icon: Users,\n    title: 'Playbook: personalizing onboarding flows by team role',\n    path: 'docs.acme.dev / playbooks / role-based-onboarding',\n    snippet:\n      'Patterns for tailoring onboarding flows to admins, editors, and viewers, with sample copy, suggested defaults, and rollout checklists.',\n    type: 'Guide',\n    updated: '4 days ago',\n  },\n]\n\nconst FACET_GROUPS: FacetGroup[] = [\n  {\n    id: 'type',\n    label: 'Type',\n    options: [\n      { id: 'type-docs', label: 'Docs', count: 18 },\n      { id: 'type-guides', label: 'Guides', count: 11 },\n      { id: 'type-video', label: 'Video', count: 7 },\n      { id: 'type-discussion', label: 'Discussion', count: 6 },\n    ],\n  },\n  {\n    id: 'team',\n    label: 'Team',\n    options: [\n      { id: 'team-product', label: 'Product', count: 14 },\n      { id: 'team-engineering', label: 'Engineering', count: 16 },\n      { id: 'team-design', label: 'Design', count: 8 },\n      { id: 'team-support', label: 'Support', count: 4 },\n    ],\n  },\n]\n\nconst TOTAL_PAGES = 3\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-search-results-page, [ui-search-results-page]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCheckboxComponent,\n    UiChipComponent,\n    UiInputComponent,\n  ],\n  template: `\n    <div data-slot=\"search-results-page\" [class]=\"rootClass\">\n      <div class=\"space-y-3\">\n        <div class=\"relative max-w-xl\">\n          <lucide-icon\n            [img]=\"Search\"\n            aria-hidden=\"true\"\n            class=\"text-muted-foreground pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2\"\n          />\n          <ui-input\n            type=\"search\"\n            [value]=\"query()\"\n            (valueChange)=\"query.set($event)\"\n            placeholder=\"Search...\"\n            class=\"h-11 pl-9 text-base\"\n          />\n        </div>\n        <p class=\"text-muted-foreground text-sm\">{{ resultRange() }} of 42 results for &ldquo;{{ query() }}&rdquo;</p>\n      </div>\n\n      @if (chips().length > 0) {\n        <div class=\"flex flex-wrap items-center gap-2\">\n          @for (chip of chips(); track chip.id) {\n            <span ui-chip variant=\"outline\" size=\"sm\" closable (close)=\"removeChip(chip.id)\">\n              {{ chip.label }}\n            </span>\n          }\n          <button\n            ui-button\n            variant=\"link\"\n            size=\"sm\"\n            class=\"text-muted-foreground h-auto min-h-6 px-2\"\n            (click)=\"clearAll()\"\n          >\n            Clear all\n          </button>\n        </div>\n      }\n\n      <div class=\"flex gap-8\">\n        <aside class=\"hidden w-48 shrink-0 space-y-6 lg:block\" aria-label=\"Filters\">\n          @for (group of facetGroups; track group.id) {\n            <div>\n              <h3 class=\"text-muted-foreground mb-3 text-xs font-medium tracking-wider uppercase\">\n                {{ group.label }}\n              </h3>\n              <ul class=\"space-y-2.5\">\n                @for (option of group.options; track option.id) {\n                  <li>\n                    <label [for]=\"group.id + '-' + option.id\" class=\"flex cursor-pointer items-center gap-2 text-sm\">\n                      <ui-checkbox\n                        [id]=\"group.id + '-' + option.id\"\n                        [checked]=\"activeFacets().has(option.id)\"\n                        (checkedChange)=\"toggleFacet(option.id, !!$event)\"\n                      />\n                      <span>{{ option.label }}</span>\n                      <span class=\"text-muted-foreground ml-auto text-xs tabular-nums\">{{ option.count }}</span>\n                    </label>\n                  </li>\n                }\n              </ul>\n            </div>\n          }\n        </aside>\n\n        <div class=\"min-w-0 flex-1\">\n          <ol class=\"divide-border divide-y\">\n            @for (result of resultsList; track result.id) {\n              <li>\n                <a\n                  href=\"#\"\n                  class=\"hover:bg-muted/50 focus-visible:ring-ring block rounded-lg p-4 transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                >\n                  <div class=\"flex gap-4\">\n                    <div\n                      class=\"bg-muted/50 text-muted-foreground flex size-10 shrink-0 items-center justify-center rounded-md border\"\n                    >\n                      <lucide-icon [img]=\"result.icon\" aria-hidden=\"true\" class=\"size-5\" />\n                    </div>\n                    <div class=\"min-w-0 space-y-1\">\n                      <p class=\"truncate font-medium\">{{ result.title }}</p>\n                      <p class=\"text-muted-foreground truncate text-xs\">{{ result.path }}</p>\n                      <p class=\"text-muted-foreground line-clamp-2 text-sm\">{{ result.snippet }}</p>\n                      <div class=\"flex items-center gap-3 pt-1\">\n                        <span ui-badge variant=\"secondary\">{{ result.type }}</span>\n                        <span class=\"text-muted-foreground text-xs\">Updated {{ result.updated }}</span>\n                      </div>\n                    </div>\n                  </div>\n                </a>\n              </li>\n            }\n          </ol>\n\n          <nav class=\"flex items-center justify-between pt-4\" aria-label=\"Pagination\">\n            <button ui-button variant=\"outline\" size=\"sm\" [disabled]=\"page() === 1\" (click)=\"prevPage()\">\n              Previous\n            </button>\n            <div class=\"flex items-center gap-1\">\n              @for (n of pageNumbers; track n) {\n                <button\n                  ui-button\n                  [variant]=\"n === page() ? 'outline' : 'ghost'\"\n                  size=\"icon-sm\"\n                  [attr.aria-current]=\"n === page() ? 'page' : null\"\n                  (click)=\"page.set(n)\"\n                >\n                  {{ n }}\n                </button>\n              }\n            </div>\n            <button ui-button variant=\"outline\" size=\"sm\" [disabled]=\"page() === totalPages\" (click)=\"nextPage()\">\n              Next\n            </button>\n          </nav>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiSearchResultsPageComponent {\n  protected readonly Search = Search\n\n  protected readonly facetGroups = FACET_GROUPS\n  protected readonly resultsList = RESULTS\n  protected readonly pageNumbers = Array.from({ length: TOTAL_PAGES }, (_, i) => i + 1)\n  protected readonly totalPages = TOTAL_PAGES\n\n  @Input() set initialQuery(v: string | undefined) {\n    if (v !== undefined) this.query.set(v)\n  }\n  @Input('class') className?: string\n\n  readonly query = signal('onboarding flows')\n  readonly chips = signal<FilterChip[]>([\n    { id: 'type-docs', label: 'Type: Docs' },\n    { id: 'team-product', label: 'Team: Product' },\n  ])\n  readonly activeFacets = signal<Set<string>>(new Set(['type-docs', 'team-product']))\n  readonly page = signal(1)\n\n  readonly resultRange = computed(() => {\n    const start = (this.page() - 1) * this.resultsList.length + 1\n    return `${start}-${start + this.resultsList.length - 1}`\n  })\n\n  get rootClass(): string {\n    return cn('w-full space-y-6', this.className)\n  }\n\n  removeChip(id: string): void {\n    this.chips.update((prev) => prev.filter((chip) => chip.id !== id))\n  }\n\n  clearAll(): void {\n    this.chips.set([])\n  }\n\n  toggleFacet(id: string, checked: boolean): void {\n    this.activeFacets.update((prev) => {\n      const next = new Set(prev)\n      if (checked) next.add(id)\n      else next.delete(id)\n      return next\n    })\n  }\n\n  prevPage(): void {\n    this.page.update((p) => Math.max(1, p - 1))\n  }\n\n  nextPage(): void {\n    this.page.update((p) => Math.min(TOTAL_PAGES, p + 1))\n  }\n}\n",
      "type": "registry:page",
      "target": "~/src/app/components/blocks/search-results-page/search-results-page.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/search-results-page/index.ts",
      "content": "export { UiSearchResultsPageComponent } from './search-results-page.component'\n",
      "type": "registry:page",
      "target": "~/src/app/components/blocks/search-results-page/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/checkbox.json",
    "https://uipkge.dev/r/angular/chip.json",
    "https://uipkge.dev/r/angular/input.json"
  ],
  "description": "Full search results page: large pre-filled search input with result count, removable filter chips with clear-all, a left facet sidebar with Type and Team checkbox groups (hidden below lg), six result rows with icon tiles, titles, paths, snippets, and type/updated meta, plus a Previous/Next pagination footer. Chips and facets are interactive; swap the stub rows for your search source.",
  "categories": [
    "layout",
    "app"
  ]
}