{
  "aliases": [
    "more-filters",
    "many-filters",
    "filter-sheet",
    "advanced-filters",
    "add-filter"
  ],
  "code": "// Research basis: Shopify Polaris Filters (\"include no more than 2 or 3 promoted filters\"; a\n// More button opens a sheet with the rest), Linear (one Filter menu), IBM Carbon filtering\n// (filters hidden in a drawer/menu show how many are applied), WCAG 2.2 SC 1.4.10 Reflow\n// (search fields and controls are not in the 2-D exception: they reflow, they do not scroll).\n//\n// RULE #51: a filter bar WRAPS — stacked below 640px, extra rows above — at every width.\n// ❌ FilterBar overflow=\"scroll\" (removed in 33.0.0), <ScrollArea orientation=\"horizontal\"> or an\n//    overflow-x-auto box around filters: the strip runs off-screen and clips its first label.\n// ❌ your own <Flex>/<Card> of FormFields above the table, worst of all justify=\"between\" (filters\n//    pushed to the far ends): ui-audit hand-rolled-filter-bar. Filters pack at the START; the\n//    only far-end element is FilterBar's `actions` slot.\n// ✅ the common filters inline + ONE \"More filters\" Sheet for the rest + wrapping chips.\nimport { useState } from \"react\";\nimport { SlidersHorizontal } from \"lucide-react\";\nimport { Button } from \"@godxjp/ui/general\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@godxjp/ui/data-entry\";\nimport {\n  Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader,\n  SheetTitle, SheetTrigger,\n} from \"@godxjp/ui/feedback\";\nimport { Flex } from \"@godxjp/ui/layout\";\nimport { FilterBar, FilterBarGroup } from \"@godxjp/ui/navigation\";\n\ntype Option = { value: string; label: string };\ntype Filters = Record<\"q\" | \"status\" | \"owner\" | \"region\" | \"channel\" | \"period\", string>;\nexport const NO_FILTERS: Filters = { q: \"\", status: \"\", owner: \"\", region: \"\", channel: \"\", period: \"\" };\n\nconst STATUS: Option[] = [{ value: \"open\", label: \"Open\" }, { value: \"paid\", label: \"Paid\" }, { value: \"void\", label: \"Void\" }];\nconst OWNER: Option[] = [{ value: \"me\", label: \"Me\" }, { value: \"team\", label: \"My team\" }];\n// Everything people reach for less often. Order = the order of the sheet.\nconst MORE: { key: \"region\" | \"channel\" | \"period\"; label: string; options: Option[] }[] = [\n  { key: \"region\", label: \"Region\", options: [{ value: \"kanto\", label: \"Kanto\" }, { value: \"kansai\", label: \"Kansai\" }] },\n  { key: \"channel\", label: \"Sales channel\", options: [{ value: \"web\", label: \"Web\" }, { value: \"store\", label: \"Store\" }] },\n  { key: \"period\", label: \"Period\", options: [{ value: \"month\", label: \"This month\" }, { value: \"quarter\", label: \"This quarter\" }] },\n];\nconst LABELS: Record<string, { label: string; options: Option[] }> = {\n  status: { label: \"Status\", options: STATUS },\n  owner: { label: \"Owner\", options: OWNER },\n  ...Object.fromEntries(MORE.map((f) => [f.key, f])),\n};\n\nexport function OrderFilters({ value, onValueChange, resultCount }: {\n  value: Filters; onValueChange: (next: Filters) => void; resultCount: number;\n}) {\n  const [moreOpen, setMoreOpen] = useState(false);\n  const set = (key: keyof Filters, v: string) => onValueChange({ ...value, [key]: v });\n\n  // ONE chip per applied filter — inline AND hidden ones, so nothing applied is out of sight.\n  const chips = Object.entries(LABELS)\n    .filter(([key]) => value[key as keyof Filters] !== \"\")\n    .map(([key, f]) => ({\n      value: key,\n      label: `${f.label}: ${f.options.find((o) => o.value === value[key as keyof Filters])?.label ?? \"\"}`,\n    }));\n  if (value.q) chips.unshift({ value: \"q\", label: `Search: ${value.q}` });\n  const hiddenApplied = MORE.filter((f) => value[f.key] !== \"\").length;\n\n  return (\n    <FilterBar\n      search={{ value: value.q, onValueChange: (v) => set(\"q\", v), placeholder: \"Search orders\", ariaLabel: \"Search orders\" }}\n      // The 2–4 most-used filters, INLINE. Typed filters wire each caption as the real <label>.\n      filters={[\n        { value: \"status\", label: \"Status\", options: STATUS, selected: value.status, onSelectedChange: (v) => set(\"status\", v), placeholder: \"All\" },\n        { value: \"owner\", label: \"Owner\", options: OWNER, selected: value.owner, onSelectedChange: (v) => set(\"owner\", v), placeholder: \"Anyone\" },\n      ]}\n      chips={chips}\n      onChipRemove={(key) => set(key as keyof Filters, \"\")}\n      hasActiveFilters={chips.length > 0}\n      onClear={() => onValueChange(NO_FILTERS)}\n      resultCount={resultCount}\n    >\n      {/* Children render after the typed filters, before the reset: ONE \"More filters\" control.\n          count = hidden filters applied, so the closed sheet still says so. Button paints a 0 pill by\n          default (showZero = true) — showZero={false} hides it when nothing hidden is applied (#1333). */}\n      <Sheet open={moreOpen} onOpenChange={setMoreOpen}>\n        <SheetTrigger asChild>\n          <Button variant=\"outline\" count={hiddenApplied} showZero={false} countLabel=\"applied\">\n            <SlidersHorizontal aria-hidden=\"true\" />\n            More filters\n          </Button>\n        </SheetTrigger>\n        <SheetContent side=\"right\">\n          <SheetHeader>\n            <SheetTitle>More filters</SheetTitle>\n            <SheetDescription>Applied filters also show as chips above the list.</SheetDescription>\n          </SheetHeader>\n          <SheetBody>\n            {/* A Sheet scrolls VERTICALLY; the fields stack. The same FilterBarGroup labels them. */}\n            <Flex direction=\"col\" gap=\"md\">\n              {MORE.map((f) => (\n                <FilterBarGroup key={f.key} label={f.label} controlId={`more-${f.key}`}>\n                  <Select value={value[f.key]} onValueChange={(v: string) => set(f.key, v)}>\n                    <SelectTrigger id={`more-${f.key}`}><SelectValue placeholder=\"All\" /></SelectTrigger>\n                    <SelectContent>\n                      {f.options.map((o) => <SelectItem key={o.value} value={o.value}>{o.label}</SelectItem>)}\n                    </SelectContent>\n                  </Select>\n                </FilterBarGroup>\n              ))}\n            </Flex>\n          </SheetBody>\n          <SheetFooter>\n            <SheetClose asChild><Button>Show results</Button></SheetClose>\n          </SheetFooter>\n        </SheetContent>\n      </Sheet>\n    </FilterBar>\n  );\n}\n// Phone width: the bar stacks (search, each filter, More filters, reset) and the chips wrap.\n// A row of filter CHIPS (ToggleGroup) takes `wrap` — it does not scroll sideways either.\n// Few filters (≤ 4)? Drop the Sheet: the plain bar in the data-table-page pattern is enough.",
  "name": "list-filters",
  "tagline": "A list page with MANY filters (cardinal rule #51) — FilterBar WRAPS; the 2–4 most-used filters stay inline, the rest sit behind ONE 'More filters' Button (count = hidden filters applied) that opens a Sheet; every applied filter is a removable chip. Never a sideways-scrolling strip.",
  "tags": [
    "filter",
    "filters",
    "list",
    "toolbar",
    "filter-bar",
    "sheet",
    "chips",
    "search",
    "responsive",
    "datatable"
  ]
}
