{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tutorial-filters",
  "type": "registry:component",
  "title": "Tutorial Filters",
  "description": "Filter controls for browsing tutorials by category or difficulty.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/tutorial-filters/tutorial-filters.tsx",
      "content": "\"use client\";\n\nimport { memo } from \"react\";\n\nimport { Badge } from \"@vllnt/ui\";\n\nexport type TutorialFiltersLabels = {\n  activeFilters: string;\n  clear: string;\n  clearAll: string;\n  difficulty: Record<string, string>;\n  difficultyLabel: string;\n  searchFilter: string;\n  searchLabel: string;\n  searchPlaceholder: string;\n  tagsLabel: string;\n};\n\nexport type FilterUpdates = {\n  difficulty?: string;\n  search?: string;\n  tags?: string[];\n};\n\nexport type TutorialFiltersProps = {\n  currentDifficulty: string;\n  currentTags: string[];\n  difficultyOptions?: string[];\n  isPending?: boolean;\n  labels: TutorialFiltersLabels;\n  onFilterChange: (updates: FilterUpdates) => void;\n  searchQuery: string;\n  tags: string[];\n};\n\nfunction SearchInput({\n  isPending,\n  labels,\n  onSearchChange,\n  searchQuery,\n}: {\n  isPending: boolean;\n  labels: TutorialFiltersLabels;\n  onSearchChange: (event: React.ChangeEvent<HTMLInputElement>) => void;\n  searchQuery: string;\n}): React.ReactNode {\n  return (\n    <div>\n      <label className=\"sr-only\" htmlFor=\"tutorial-search\">\n        {labels.searchLabel}\n      </label>\n      <input\n        className=\"w-full px-4 py-2 border border-border rounded-lg bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\"\n        defaultValue={searchQuery}\n        disabled={isPending}\n        id=\"tutorial-search\"\n        onChange={onSearchChange}\n        placeholder={labels.searchPlaceholder}\n        type=\"text\"\n      />\n    </div>\n  );\n}\n\nfunction DifficultyFilter({\n  activeDifficulty,\n  difficultyOptions,\n  isPending,\n  labels,\n  onDifficultyChange,\n}: {\n  activeDifficulty: string;\n  difficultyOptions: string[];\n  isPending: boolean;\n  labels: TutorialFiltersLabels;\n  onDifficultyChange: (difficulty: string) => void;\n}): React.ReactNode {\n  return (\n    <div>\n      <div className=\"flex items-center gap-2 mb-2\">\n        <span className=\"text-sm font-medium\">{labels.difficultyLabel}</span>\n      </div>\n      <div className=\"flex flex-wrap gap-2\">\n        {difficultyOptions.map((difficulty) => {\n          const isActive = difficulty === activeDifficulty;\n          return (\n            <button\n              className={`px-3 py-1 text-sm rounded-lg border transition-colors ${\n                isActive\n                  ? \"bg-primary text-primary-foreground border-transparent\"\n                  : \"bg-background text-foreground border-border hover:bg-muted\"\n              }`}\n              disabled={isPending}\n              key={difficulty}\n              onClick={() => {\n                onDifficultyChange(difficulty);\n              }}\n              type=\"button\"\n            >\n              <span className=\"capitalize\">\n                {labels.difficulty[difficulty] || difficulty}\n              </span>\n            </button>\n          );\n        })}\n      </div>\n    </div>\n  );\n}\n\nfunction TagFilter({\n  currentTags,\n  labels,\n  onClearTags,\n  onTagToggle,\n  tags,\n}: {\n  currentTags: string[];\n  labels: TutorialFiltersLabels;\n  onClearTags: () => void;\n  onTagToggle: (tag: string) => void;\n  tags: string[];\n}): React.ReactNode {\n  if (tags.length === 0) {\n    return null;\n  }\n\n  return (\n    <div>\n      <div className=\"flex items-center gap-2 mb-2\">\n        <span className=\"text-sm font-medium\">{labels.tagsLabel}</span>\n        {currentTags.length > 0 ? (\n          <button\n            className=\"text-xs text-muted-foreground hover:text-foreground transition-colors\"\n            onClick={onClearTags}\n            type=\"button\"\n          >\n            {labels.clear}\n          </button>\n        ) : null}\n      </div>\n      <div className=\"flex flex-wrap gap-2\">\n        {tags.map((tag) => {\n          const isActive = currentTags.includes(tag);\n          return (\n            <Badge\n              className={`cursor-pointer transition-all ${\n                isActive\n                  ? \"bg-primary text-primary-foreground border-transparent\"\n                  : \"hover:bg-muted\"\n              }`}\n              key={tag}\n              onClick={() => {\n                onTagToggle(tag);\n              }}\n              variant={isActive ? \"default\" : \"outline\"}\n            >\n              {tag}\n            </Badge>\n          );\n        })}\n      </div>\n    </div>\n  );\n}\n\nfunction ActiveFiltersSummary({\n  currentDifficulty,\n  currentTags,\n  labels,\n  onClearAll,\n  searchQuery,\n}: {\n  currentDifficulty: string;\n  currentTags: string[];\n  labels: TutorialFiltersLabels;\n  onClearAll: () => void;\n  searchQuery: string;\n}): React.ReactNode {\n  if (!currentDifficulty && currentTags.length === 0 && !searchQuery) {\n    return null;\n  }\n\n  return (\n    <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n      <span>{labels.activeFilters}</span>\n      {currentDifficulty ? (\n        <Badge className=\"capitalize\" variant=\"secondary\">\n          {labels.difficulty[currentDifficulty] || currentDifficulty}\n        </Badge>\n      ) : null}\n      {currentTags.map((tag) => (\n        <Badge key={tag} variant=\"secondary\">\n          {tag}\n        </Badge>\n      ))}\n      {searchQuery ? (\n        <Badge variant=\"secondary\">\n          {labels.searchFilter} &quot;{searchQuery}&quot;\n        </Badge>\n      ) : null}\n      <button\n        className=\"text-xs hover:underline\"\n        onClick={onClearAll}\n        type=\"button\"\n      >\n        {labels.clearAll}\n      </button>\n    </div>\n  );\n}\n\nconst DEFAULT_DIFFICULTY_OPTIONS = [\n  \"all\",\n  \"beginner\",\n  \"intermediate\",\n  \"advanced\",\n];\n\nfunction TutorialFiltersImpl({\n  currentDifficulty,\n  currentTags,\n  difficultyOptions = DEFAULT_DIFFICULTY_OPTIONS,\n  isPending = false,\n  labels,\n  onFilterChange,\n  searchQuery,\n  tags,\n}: TutorialFiltersProps): React.ReactNode {\n  const activeDifficulty = currentDifficulty || \"all\";\n\n  const handleDifficultyChange = (difficulty: string): void => {\n    onFilterChange({ difficulty });\n  };\n\n  const handleSearchChange = (\n    event: React.ChangeEvent<HTMLInputElement>,\n  ): void => {\n    onFilterChange({ search: event.target.value });\n  };\n\n  const handleTagToggle = (tag: string): void => {\n    const newTags = currentTags.includes(tag)\n      ? currentTags.filter((t) => t !== tag)\n      : [...currentTags, tag];\n    onFilterChange({ tags: newTags });\n  };\n\n  const handleClearAll = (): void => {\n    onFilterChange({ difficulty: \"all\", search: \"\", tags: [] });\n    const input = document.querySelector<HTMLInputElement>(\"#tutorial-search\");\n    if (input) input.value = \"\";\n  };\n\n  return (\n    <div className=\"space-y-4 mb-8\">\n      <SearchInput\n        isPending={isPending}\n        labels={labels}\n        onSearchChange={handleSearchChange}\n        searchQuery={searchQuery}\n      />\n      <DifficultyFilter\n        activeDifficulty={activeDifficulty}\n        difficultyOptions={difficultyOptions}\n        isPending={isPending}\n        labels={labels}\n        onDifficultyChange={handleDifficultyChange}\n      />\n      <TagFilter\n        currentTags={currentTags}\n        labels={labels}\n        onClearTags={() => {\n          onFilterChange({ tags: [] });\n        }}\n        onTagToggle={handleTagToggle}\n        tags={tags}\n      />\n      <ActiveFiltersSummary\n        currentDifficulty={currentDifficulty}\n        currentTags={currentTags}\n        labels={labels}\n        onClearAll={handleClearAll}\n        searchQuery={searchQuery}\n      />\n    </div>\n  );\n}\n\nexport const TutorialFilters = memo(TutorialFiltersImpl);\nTutorialFilters.displayName = \"TutorialFilters\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
