filtercn 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,36 +4,98 @@ The official CLI tool to automatically install the [FilterCN](https://github.com
4
4
 
5
5
  FilterCN is a comprehensive, fully-customizable filter component built on top of [shadcn/ui](https://ui.shadcn.com/). It provides a visual filter builder UI that syncs state to URL search parameters, ideal for REST API-powered data tables.
6
6
 
7
- ## Installation & Usage
7
+ ## Requirements
8
8
 
9
- Run the following command in the root of your Next.js project to install the component:
9
+ Before using this CLI, your project must have **Next.js**, **React**, and **shadcn/ui** installed and configured.
10
10
 
11
+ You also need the following shadcn/ui components installed in your project:
11
12
  ```bash
12
- npx filtercn init
13
+ npx shadcn@latest add button input select popover calendar command badge
13
14
  ```
14
15
 
15
- The CLI will:
16
- 1. Detect your project structure (e.g., `src/components` vs `components`).
17
- 2. Detect your package manager (npm, pnpm, yarn, bun).
18
- 3. Detect your import alias (e.g., `@/`).
19
- 4. Automatically create the necessary folder structure.
20
- 5. Scaffold all 19 component files (UI components, hooks, helpers, types).
21
- 6. Auto-install required peer dependencies (`lucide-react`, `date-fns`).
16
+ ---
22
17
 
23
- ## Force Overwrite
18
+ ## Installation
24
19
 
25
- If you already have the component installed and want to overwrite it with a fresh copy:
20
+ You do not need to install this CLI globally. Instead, run it directly using your preferred package manager's executor:
26
21
 
22
+ **Using npm:**
27
23
  ```bash
28
- npx filtercn init --force
24
+ npx filtercn init
29
25
  ```
30
26
 
31
- ## Prerequisites
27
+ **Using pnpm:**
28
+ ```bash
29
+ pnpm dlx filtercn init
30
+ ```
32
31
 
33
- Since the component is built using shadcn/ui, you must have the following shadcn components installed in your project:
32
+ **Using bun:**
33
+ ```bash
34
+ bunx filtercn init
35
+ ```
34
36
 
37
+ **Using yarn:**
35
38
  ```bash
36
- npx shadcn@latest add button input select popover calendar command badge
39
+ yarn dlx filtercn init
40
+ ```
41
+
42
+ ### What happens when I run this?
43
+
44
+ The CLI will interactively guide you through the setup:
45
+ 1. **Detects Environment:** It automatically finds your `src/components` (or `components`) folder and your TypeScript import aliases (e.g., `@/`).
46
+ 2. **Prompts for Confirmation:** It asks if you want to proceed with writing files.
47
+ 3. **Scaffolds Files:** It creates a `conditional-filter` folder with all 19 necessary files (Types, Hooks, Helpers, Context Provider, and UI components).
48
+ 4. **Installs Dependencies:** It checks if you have `lucide-react` and `date-fns` installed. If you don't, it will ask to automatically install them for you using your detected package manager.
49
+
50
+ ## Post-Installation Usage
51
+
52
+ Once the CLI finishes, you can immediately start using the component in your pages.
53
+
54
+ Here is a quick example of how to wrap your data table with the filter:
55
+
56
+ ```tsx
57
+ "use client";
58
+
59
+ import { FilterProvider, FilterRoot } from "@/components/conditional-filter";
60
+ import type { FilterFieldDefinition } from "@/components/conditional-filter";
61
+
62
+ // 1. Define the fields your users can filter by
63
+ const filterFields: FilterFieldDefinition[] = [
64
+ { name: "title", label: "Project Title", type: "text" },
65
+ {
66
+ name: "status",
67
+ label: "Status",
68
+ type: "select",
69
+ options: [
70
+ { label: "Active", value: "active" },
71
+ { label: "Archived", value: "archived" },
72
+ ]
73
+ },
74
+ { name: "price", label: "Budget", type: "number" },
75
+ ];
76
+
77
+ export default function MyPage() {
78
+ return (
79
+ <div className="p-8 space-y-4">
80
+ <h1 className="text-2xl font-bold">Projects</h1>
81
+
82
+ {/* 2. Wrap the filter root in the provider */}
83
+ <FilterProvider config={{ fields: filterFields, paramStyle: "underscore" }}>
84
+ <FilterRoot />
85
+ </FilterProvider>
86
+
87
+ {/* Your data table goes here */}
88
+ </div>
89
+ );
90
+ }
91
+ ```
92
+
93
+ ## Options
94
+
95
+ If you have modified the component files and want to reset them to their original state, or if the installation failed halfway and you want to try again, use the `--force` flag:
96
+
97
+ ```bash
98
+ npx filtercn init --force
37
99
  ```
38
100
 
39
101
  ## License
@@ -1087,26 +1087,42 @@ export function FilterRowComponent({ rowId }: FilterRowProps) {
1087
1087
  import { Plus, RotateCcw, Check } from "lucide-react";
1088
1088
  import { Button } from "${alias}components/ui/button";
1089
1089
  import { useFilterContext } from "../provider/filter-context";
1090
+ import { PopoverClose } from "${alias}components/ui/popover";
1091
+ import { isValidFilterRow } from "../helpers/validators";
1090
1092
 
1091
1093
  export function FilterFooter() {
1092
1094
  const { config, state, addRow, reset, apply } = useFilterContext();
1093
1095
  const maxRows = config.maxRows || 10;
1094
1096
  const canAdd = state.rows.length < maxRows;
1095
1097
 
1098
+ // Determine if there is at least one valid filter row
1099
+ const hasValidFilters = state.rows.some(isValidFilterRow);
1100
+
1096
1101
  return (
1097
1102
  <div className="flex items-center gap-2 pt-2">
1098
1103
  <Button variant="outline" size="sm" onClick={addRow} disabled={!canAdd}>
1099
1104
  <Plus className="mr-1 h-4 w-4" />
1100
1105
  {config.locale?.addFilter || "+ Add filter"}
1101
1106
  </Button>
1102
- <Button variant="ghost" size="sm" onClick={reset}>
1103
- <RotateCcw className="mr-1 h-4 w-4" />
1104
- {config.locale?.reset || "Reset"}
1105
- </Button>
1106
- <Button size="sm" onClick={apply}>
1107
- <Check className="mr-1 h-4 w-4" />
1108
- {config.locale?.apply || "Apply"}
1109
- </Button>
1107
+ <PopoverClose asChild>
1108
+ <Button variant="ghost" size="sm" onClick={reset}>
1109
+ <RotateCcw className="mr-1 h-4 w-4" />
1110
+ {config.locale?.reset || "Reset"}
1111
+ </Button>
1112
+ </PopoverClose>
1113
+ {hasValidFilters ? (
1114
+ <PopoverClose asChild>
1115
+ <Button size="sm" onClick={apply}>
1116
+ <Check className="mr-1 h-4 w-4" />
1117
+ {config.locale?.apply || "Apply"}
1118
+ </Button>
1119
+ </PopoverClose>
1120
+ ) : (
1121
+ <Button size="sm" disabled>
1122
+ <Check className="mr-1 h-4 w-4" />
1123
+ {config.locale?.apply || "Apply"}
1124
+ </Button>
1125
+ )}
1110
1126
  </div>
1111
1127
  );
1112
1128
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "filtercn",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "CLI to scaffold the FilterCN conditional filter component into your Next.js + shadcn/ui project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1107,26 +1107,42 @@ export function FilterRowComponent({ rowId }: FilterRowProps) {
1107
1107
  import { Plus, RotateCcw, Check } from "lucide-react";
1108
1108
  import { Button } from "${alias}components/ui/button";
1109
1109
  import { useFilterContext } from "../provider/filter-context";
1110
+ import { PopoverClose } from "${alias}components/ui/popover";
1111
+ import { isValidFilterRow } from "../helpers/validators";
1110
1112
 
1111
1113
  export function FilterFooter() {
1112
1114
  const { config, state, addRow, reset, apply } = useFilterContext();
1113
1115
  const maxRows = config.maxRows || 10;
1114
1116
  const canAdd = state.rows.length < maxRows;
1115
1117
 
1118
+ // Determine if there is at least one valid filter row
1119
+ const hasValidFilters = state.rows.some(isValidFilterRow);
1120
+
1116
1121
  return (
1117
1122
  <div className="flex items-center gap-2 pt-2">
1118
1123
  <Button variant="outline" size="sm" onClick={addRow} disabled={!canAdd}>
1119
1124
  <Plus className="mr-1 h-4 w-4" />
1120
1125
  {config.locale?.addFilter || "+ Add filter"}
1121
1126
  </Button>
1122
- <Button variant="ghost" size="sm" onClick={reset}>
1123
- <RotateCcw className="mr-1 h-4 w-4" />
1124
- {config.locale?.reset || "Reset"}
1125
- </Button>
1126
- <Button size="sm" onClick={apply}>
1127
- <Check className="mr-1 h-4 w-4" />
1128
- {config.locale?.apply || "Apply"}
1129
- </Button>
1127
+ <PopoverClose asChild>
1128
+ <Button variant="ghost" size="sm" onClick={reset}>
1129
+ <RotateCcw className="mr-1 h-4 w-4" />
1130
+ {config.locale?.reset || "Reset"}
1131
+ </Button>
1132
+ </PopoverClose>
1133
+ {hasValidFilters ? (
1134
+ <PopoverClose asChild>
1135
+ <Button size="sm" onClick={apply}>
1136
+ <Check className="mr-1 h-4 w-4" />
1137
+ {config.locale?.apply || "Apply"}
1138
+ </Button>
1139
+ </PopoverClose>
1140
+ ) : (
1141
+ <Button size="sm" disabled>
1142
+ <Check className="mr-1 h-4 w-4" />
1143
+ {config.locale?.apply || "Apply"}
1144
+ </Button>
1145
+ )}
1130
1146
  </div>
1131
1147
  );
1132
1148
  }