{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "action-bar",
  "title": "Action Bar",
  "description": "Action Bar from the Get IT Done design system.",
  "dependencies": [
    "lucide-react@^0.379.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/button.json",
    "https://zero-design.itdone.de/r/checkbox.json",
    "https://zero-design.itdone.de/r/chip.json",
    "https://zero-design.itdone.de/r/dropdown-menu.json",
    "https://zero-design.itdone.de/r/input.json",
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/recipes.json",
    "https://zero-design.itdone.de/r/select.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/action-bar.tsx",
      "content": "'use client';\n\nimport { MoreHorizontal } from 'lucide-react';\nimport * as React from 'react';\n\nimport { focusRing, transitionBase } from '@/lib/recipes';\nimport { cn } from '@/lib/utils';\nimport { Button } from '@/components/ui/button';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport { Chip } from '@/components/ui/chip';\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';\nimport { useLabels, usePreset } from '@/components/ui/labels-context';\nimport { Input, type InputProps } from '@/components/ui/input';\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';\n\n// Section header action bar family (Figma \"Section header\": Action bar list / bulk / overflow).\n// ActionBar stacks its rows vertically (gap 8): an optional tab row, the toolbar row, the filter row\n// and the BatchBar that replaces the toolbar while rows are selected.\n\nconst ActionBar = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => <div ref={ref} className={cn('flex w-full flex-col gap-2', className)} {...props} />,\n);\nActionBar.displayName = 'ActionBar';\n\n// One horizontal row of the bar. Put tabs, search, filters or actions inside; `ActionBarSpacer` pushes the rest to the end.\nconst ActionBarRow = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => (\n    <div ref={ref} className={cn('flex min-h-action-bar-row w-full flex-wrap items-center gap-2', className)} {...props} />\n  ),\n);\nActionBarRow.displayName = 'ActionBarRow';\n\nconst ActionBarSpacer = () => <span aria-hidden className=\"flex-1\" />;\n\nexport type ActionBarSearchProps = Omit<InputProps, 'variant' | 'size' | 'aria-label'> & {\n  // Required accessible name, for example `Search`.\n  'aria-label': string;\n};\n\n// Search field of the action bar (Figma Input Variant=Search sm): control-sm height in a row of control-sm controls, 200 wide. The width reads --size-search once the token exists and falls back to 200.\nconst ActionBarSearch = React.forwardRef<HTMLInputElement, ActionBarSearchProps>(({ className, ...props }, ref) => (\n  <Input ref={ref} variant=\"search\" size=\"sm\" className={cn('w-[var(--size-search,12.5rem)] shrink-0', className)} {...props} />\n));\nActionBarSearch.displayName = 'ActionBarSearch';\n\n/* -------------------------------------------------------------------------------------------------\n * Filter tags\n * -----------------------------------------------------------------------------------------------*/\n\nexport interface ActionBarFilterTag {\n  id: string;\n  label: string;\n  onRemove: () => void;\n}\n\nexport interface ActionBarFilterTagsProps extends React.HTMLAttributes<HTMLUListElement> {\n  tags: ActionBarFilterTag[];\n  /** Renders the Reset link when provided and at least one tag exists. */\n  onReset?: () => void;\n  resetLabel?: string;\n}\n\nconst ActionBarFilterTags = React.forwardRef<HTMLUListElement, ActionBarFilterTagsProps>(\n  ({ className, tags, onReset, resetLabel: resetLabelProp, 'aria-label': ariaLabelProp, ...props }, ref) => {\n    const l = useLabels();\n    const resetLabel = resetLabelProp ?? l.reset;\n    const ariaLabel = ariaLabelProp ?? l.activeFilters;\n    const zero = usePreset() === 'zero';\n    if (tags.length === 0) return null;\n    return (\n      <ul ref={ref} aria-label={ariaLabel} className={cn('m-0 flex list-none flex-wrap items-center gap-2 p-0', className)} {...props}>\n        {tags.map((tag) => (\n          <li key={tag.id}>\n            <Chip variant=\"outline\" size=\"control\" label={tag.label} onRemove={tag.onRemove} />\n          </li>\n        ))}\n        {onReset && (\n          <li>\n            <button\n              type=\"button\"\n              onClick={onReset}\n              className={cn(\n                'text-text-subtle',\n                zero\n                  ? 'typography-link-sm min-h-[var(--size-control-xs)] rounded-item hover:text-text-default'\n                  : 'typography-ui-medium h-control-sm rounded-item px-2 hover:bg-muted hover:text-text-default',\n                transitionBase,\n                focusRing,\n              )}\n            >\n              {resetLabel}\n            </button>\n          </li>\n        )}\n      </ul>\n    );\n  },\n);\nActionBarFilterTags.displayName = 'ActionBarFilterTags';\n\n/* -------------------------------------------------------------------------------------------------\n * Batch bar (bulk actions)\n * -----------------------------------------------------------------------------------------------*/\n\nexport interface BatchBarAction {\n  id: string;\n  label: string;\n  icon?: React.ReactNode;\n  // Not needed when `render` opens a popover instead.\n  onSelect?: () => void;\n  destructive?: boolean;\n  disabled?: boolean;\n  // Render slot for inline actions. Receives the action button, returns what to render in its place, e.g. a `Popover` whose `PopoverTrigger asChild` wraps the button. Actions in the More menu ignore it.\n  render?: (button: React.ReactElement) => React.ReactNode;\n}\n\nexport interface BatchBarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n  /** Number of selected rows. Mount the bar only while at least one row is selected. */\n  selectedCount: number;\n  /** Called by Clear and by the leading checkbox. */\n  onClear: () => void;\n  /** Inline actions, shown as outlined buttons up to `maxInline`; the rest moves into the More menu. */\n  actions?: BatchBarAction[];\n  /** Always in the More menu. */\n  moreActions?: BatchBarAction[];\n  /** Inline actions shown before overflowing into the More menu. Default 3. */\n  maxInline?: number;\n  /** Text of the count, `{count} selected` by default. */\n  countLabel?: (count: number) => string;\n  clearLabel?: string;\n  moreLabel?: string;\n  /** Chip slot after the count, for example a `Chip` naming the selection scope (\"All 120 matches\") or its status. Pass visible text. */\n  chip?: React.ReactNode;\n}\n\nconst BatchBar = React.forwardRef<HTMLDivElement, BatchBarProps>(\n  (\n    {\n      className,\n      selectedCount,\n      onClear,\n      actions = [],\n      moreActions = [],\n      maxInline = 3,\n      countLabel: countLabelProp,\n      clearLabel: clearLabelProp,\n      moreLabel: moreLabelProp,\n      chip,\n      'aria-label': ariaLabelProp,\n      ...props\n    },\n    ref,\n  ) => {\n    const l = useLabels();\n    const countLabel = countLabelProp ?? l.selectedCount;\n    const clearLabel = clearLabelProp ?? l.clear;\n    const moreLabel = moreLabelProp ?? l.moreActions;\n    const ariaLabel = ariaLabelProp ?? l.bulkActions;\n    const inline = actions.slice(0, Math.max(0, maxInline));\n    const menu = [...actions.slice(Math.max(0, maxInline)), ...moreActions];\n    return (\n      <div\n        ref={ref}\n        role=\"toolbar\"\n        aria-label={ariaLabel}\n        className={cn(\n          'flex min-h-action-bar-row w-full flex-wrap items-center gap-2 rounded-surface border border-solid border-border-brand bg-background-brand-subtle py-1 pl-3 pr-2 text-text-default',\n          className,\n        )}\n        {...props}\n      >\n        <Checkbox checked aria-label={l.deselectAll} onCheckedChange={() => onClear()} />\n        <span role=\"status\" aria-live=\"polite\" className=\"typography-ui-semibold\">\n          {countLabel(selectedCount)}\n        </span>\n        {chip != null && chip !== false ? (\n          <span data-batch-chip=\"\" className=\"flex shrink-0 items-center\">\n            {chip}\n          </span>\n        ) : null}\n        <button\n          type=\"button\"\n          onClick={onClear}\n          className={cn(\n            'typography-ui-medium h-control-sm rounded-item px-2 text-text-default hover:bg-background-brand-subtle-hover',\n            transitionBase,\n            focusRing,\n          )}\n        >\n          {clearLabel}\n        </button>\n        <ActionBarSpacer />\n        <div className=\"flex flex-wrap items-center justify-end gap-2\">\n          {inline.map((action) => {\n            const button = (\n              <Button\n                key={action.id}\n                type=\"button\"\n                variant=\"outline\"\n                size=\"control-sm\"\n                icon={action.icon as React.ReactElement | undefined}\n                disabled={action.disabled}\n                onClick={action.onSelect}\n                className={cn(\n                  action.destructive &&\n                    'border-border-destructive-subtle text-text-destructive-on-subtle hover:bg-background-destructive-subtle',\n                )}\n              >\n                {action.label}\n              </Button>\n            );\n            return action.render ? <React.Fragment key={action.id}>{action.render(button)}</React.Fragment> : button;\n          })}\n          {menu.length > 0 && (\n            <DropdownMenu>\n              <DropdownMenuTrigger asChild>\n                <button\n                  type=\"button\"\n                  aria-label={moreLabel}\n                  className={cn(\n                    'inline-flex h-control-sm w-control-sm items-center justify-center rounded-control text-icon-default hover:bg-background-brand-subtle-hover',\n                    transitionBase,\n                    focusRing,\n                  )}\n                >\n                  <MoreHorizontal className=\"h-icon-sm w-icon-sm\" aria-hidden />\n                </button>\n              </DropdownMenuTrigger>\n              <DropdownMenuContent align=\"end\">\n                {menu.map((action) => (\n                  <DropdownMenuItem\n                    key={action.id}\n                    disabled={action.disabled}\n                    onSelect={action.onSelect}\n                    className={cn(action.destructive && 'text-text-destructive')}\n                  >\n                    {action.icon}\n                    {action.label}\n                  </DropdownMenuItem>\n                ))}\n              </DropdownMenuContent>\n            </DropdownMenu>\n          )}\n        </div>\n      </div>\n    );\n  },\n);\nBatchBar.displayName = 'BatchBar';\n\nexport interface ActionBarDataSwitchOption {\n  value: string;\n  label: string;\n}\n\nexport interface ActionBarDataSwitchProps {\n  options: ActionBarDataSwitchOption[];\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  // Leading icon (icon-xs), for example a building for the data context.\n  icon?: React.ReactNode;\n  // Required accessible name, for example `Data context`.\n  'aria-label': string;\n  className?: string;\n}\n\n// Data context switch of the action bar (Figma Section header, Data switch): a control-sm Select trigger with a leading icon.\nconst ActionBarDataSwitch = ({ options, value, defaultValue, onValueChange, icon, 'aria-label': ariaLabel, className }: ActionBarDataSwitchProps) => (\n  <Select value={value} defaultValue={defaultValue} onValueChange={onValueChange}>\n    <SelectTrigger\n      aria-label={ariaLabel}\n      leading={icon}\n      className={cn('typography-detail h-control-sm w-auto gap-gap-control px-[var(--size-control-padding-x-sm)] py-0', className)}\n    >\n      <SelectValue />\n    </SelectTrigger>\n    <SelectContent>\n      {options.map((option) => (\n        <SelectItem key={option.value} value={option.value}>\n          {option.label}\n        </SelectItem>\n      ))}\n    </SelectContent>\n  </Select>\n);\nActionBarDataSwitch.displayName = 'ActionBarDataSwitch';\n\nexport { ActionBar, ActionBarDataSwitch, ActionBarFilterTags, ActionBarRow, ActionBarSearch, ActionBarSpacer, BatchBar };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}