@handled-ai/design-system 0.16.0 → 0.16.1
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/dist/components/badge.d.ts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/data-table-condition-filter.d.ts +37 -0
- package/dist/components/data-table-condition-filter.js +407 -0
- package/dist/components/data-table-condition-filter.js.map +1 -0
- package/dist/components/data-table-filter.d.ts +12 -1
- package/dist/components/data-table-filter.js +92 -10
- package/dist/components/data-table-filter.js.map +1 -1
- package/dist/components/data-table-toolbar.d.ts +1 -0
- package/dist/components/data-table.d.ts +1 -0
- package/dist/components/tabs.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/prototype/index.d.ts +1 -0
- package/dist/prototype/prototype-accounts-view.d.ts +1 -0
- package/dist/prototype/prototype-admin-view.d.ts +1 -0
- package/dist/prototype/prototype-config.d.ts +1 -0
- package/dist/prototype/prototype-inbox-view.d.ts +4 -1
- package/dist/prototype/prototype-inbox-view.js +6 -1
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/dist/prototype/prototype-insights-view.d.ts +1 -0
- package/dist/prototype/prototype-shell.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/__tests__/contact-list.test.tsx +1 -1
- package/src/components/__tests__/data-table-condition-filter.test.tsx +397 -0
- package/src/components/__tests__/data-table-filter-presets.test.tsx +1 -1
- package/src/components/__tests__/data-table-filter.test.tsx +270 -3
- package/src/components/data-table-condition-filter.tsx +513 -0
- package/src/components/data-table-filter.tsx +102 -4
- package/src/index.ts +1 -0
- package/src/prototype/__tests__/detail-view-attention.test.tsx +101 -0
- package/src/prototype/prototype-inbox-view.tsx +8 -0
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const badgeVariants: (props?: ({
|
|
6
|
-
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" |
|
|
6
|
+
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
|
|
7
7
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
8
8
|
declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
|
|
9
9
|
asChild?: boolean;
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const buttonVariants: (props?: ({
|
|
6
|
-
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" |
|
|
6
|
+
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
|
|
7
7
|
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
type ConditionOperator = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "is_null" | "is_not_null";
|
|
4
|
+
interface ConditionFieldDef {
|
|
5
|
+
/** Unique field key (e.g., "Account_Balance__c") */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Display label (e.g., "Account Balance") */
|
|
8
|
+
label: string;
|
|
9
|
+
/** Field data type — determines which operators are available and how the value input renders */
|
|
10
|
+
type: "text" | "number" | "currency" | "date";
|
|
11
|
+
/** Allowed operators for this field. Defaults based on type if not provided. */
|
|
12
|
+
operators?: ConditionOperator[];
|
|
13
|
+
}
|
|
14
|
+
interface ConditionFilterValue {
|
|
15
|
+
/** Stable identity — used as React key to avoid stale-state bugs on removal */
|
|
16
|
+
id: string;
|
|
17
|
+
field: string;
|
|
18
|
+
operator: ConditionOperator;
|
|
19
|
+
value: string | number | null;
|
|
20
|
+
}
|
|
21
|
+
interface DataTableConditionFilterProps {
|
|
22
|
+
/** Available fields the user can filter on */
|
|
23
|
+
fields: ConditionFieldDef[];
|
|
24
|
+
/** Current active conditions */
|
|
25
|
+
conditions: ConditionFilterValue[];
|
|
26
|
+
/** Called when conditions change (add, update, remove) */
|
|
27
|
+
onConditionsChange: (conditions: ConditionFilterValue[]) => void;
|
|
28
|
+
className?: string;
|
|
29
|
+
}
|
|
30
|
+
declare const OPERATOR_LABELS: Record<ConditionOperator, string>;
|
|
31
|
+
declare const DEFAULT_OPERATORS: Record<ConditionFieldDef["type"], ConditionOperator[]>;
|
|
32
|
+
/** Generate a stable unique ID for a new condition row. */
|
|
33
|
+
declare function generateConditionId(): string;
|
|
34
|
+
declare function getOperators(field: ConditionFieldDef): ConditionOperator[];
|
|
35
|
+
declare function DataTableConditionFilter({ fields, conditions, onConditionsChange, className, }: DataTableConditionFilterProps): React.JSX.Element;
|
|
36
|
+
|
|
37
|
+
export { type ConditionFieldDef, type ConditionFilterValue, type ConditionOperator, DEFAULT_OPERATORS, DataTableConditionFilter, type DataTableConditionFilterProps, OPERATOR_LABELS, generateConditionId, getOperators };
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __defProps = Object.defineProperties;
|
|
6
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
7
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
+
var __spreadValues = (a, b) => {
|
|
12
|
+
for (var prop in b || (b = {}))
|
|
13
|
+
if (__hasOwnProp.call(b, prop))
|
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
|
15
|
+
if (__getOwnPropSymbols)
|
|
16
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
17
|
+
if (__propIsEnum.call(b, prop))
|
|
18
|
+
__defNormalProp(a, prop, b[prop]);
|
|
19
|
+
}
|
|
20
|
+
return a;
|
|
21
|
+
};
|
|
22
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
23
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
24
|
+
import * as React from "react";
|
|
25
|
+
import {
|
|
26
|
+
CalendarDays,
|
|
27
|
+
DollarSign,
|
|
28
|
+
Eye,
|
|
29
|
+
Hash,
|
|
30
|
+
MoreHorizontal,
|
|
31
|
+
Plus,
|
|
32
|
+
Trash2,
|
|
33
|
+
Type
|
|
34
|
+
} from "lucide-react";
|
|
35
|
+
import { cn } from "../lib/utils.js";
|
|
36
|
+
import { Button } from "./button.js";
|
|
37
|
+
import { Input } from "./input.js";
|
|
38
|
+
import {
|
|
39
|
+
Select,
|
|
40
|
+
SelectContent,
|
|
41
|
+
SelectItem,
|
|
42
|
+
SelectTrigger,
|
|
43
|
+
SelectValue
|
|
44
|
+
} from "./select.js";
|
|
45
|
+
const OPERATOR_LABELS = {
|
|
46
|
+
eq: "=",
|
|
47
|
+
neq: "\u2260",
|
|
48
|
+
gt: ">",
|
|
49
|
+
gte: "\u2265",
|
|
50
|
+
lt: "<",
|
|
51
|
+
lte: "\u2264",
|
|
52
|
+
in: "contains",
|
|
53
|
+
is_null: "is empty",
|
|
54
|
+
is_not_null: "is not empty"
|
|
55
|
+
};
|
|
56
|
+
const NUMERIC_OPERATORS = [
|
|
57
|
+
"eq",
|
|
58
|
+
"neq",
|
|
59
|
+
"gt",
|
|
60
|
+
"gte",
|
|
61
|
+
"lt",
|
|
62
|
+
"lte",
|
|
63
|
+
"is_null",
|
|
64
|
+
"is_not_null"
|
|
65
|
+
];
|
|
66
|
+
const DEFAULT_OPERATORS = {
|
|
67
|
+
text: ["eq", "neq", "is_null", "is_not_null"],
|
|
68
|
+
number: NUMERIC_OPERATORS,
|
|
69
|
+
currency: NUMERIC_OPERATORS,
|
|
70
|
+
date: ["gt", "gte", "lt", "lte", "eq", "neq", "is_null", "is_not_null"]
|
|
71
|
+
};
|
|
72
|
+
function generateConditionId() {
|
|
73
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
74
|
+
return crypto.randomUUID();
|
|
75
|
+
}
|
|
76
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
77
|
+
}
|
|
78
|
+
function getOperators(field) {
|
|
79
|
+
var _a;
|
|
80
|
+
return ((_a = field.operators) != null ? _a : DEFAULT_OPERATORS[field.type]).filter((op) => op !== "in");
|
|
81
|
+
}
|
|
82
|
+
function isUnaryOperator(op) {
|
|
83
|
+
return op === "is_null" || op === "is_not_null";
|
|
84
|
+
}
|
|
85
|
+
function getDefaultOperator(field) {
|
|
86
|
+
var _a;
|
|
87
|
+
return (_a = getOperators(field)[0]) != null ? _a : "eq";
|
|
88
|
+
}
|
|
89
|
+
function createDraftCondition(field) {
|
|
90
|
+
return {
|
|
91
|
+
id: generateConditionId(),
|
|
92
|
+
field: field.id,
|
|
93
|
+
operator: getDefaultOperator(field),
|
|
94
|
+
value: null
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function normalizeCondition(condition, fields) {
|
|
98
|
+
var _a;
|
|
99
|
+
const field = (_a = fields.find((f) => f.id === condition.field)) != null ? _a : fields[0];
|
|
100
|
+
if (!field) return condition;
|
|
101
|
+
const operators = getOperators(field);
|
|
102
|
+
const operator = operators.includes(condition.operator) ? condition.operator : getDefaultOperator(field);
|
|
103
|
+
return __spreadProps(__spreadValues({}, condition), {
|
|
104
|
+
field: field.id,
|
|
105
|
+
operator,
|
|
106
|
+
value: isUnaryOperator(operator) ? null : condition.value
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function parseConditionValue(raw, fieldType) {
|
|
110
|
+
if (raw === "") return null;
|
|
111
|
+
if (fieldType === "number" || fieldType === "currency") {
|
|
112
|
+
const parsed = Number(raw);
|
|
113
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
114
|
+
}
|
|
115
|
+
return raw;
|
|
116
|
+
}
|
|
117
|
+
function isCompleteCondition(condition, fields) {
|
|
118
|
+
const field = fields.find((f) => f.id === condition.field);
|
|
119
|
+
if (!field) return false;
|
|
120
|
+
if (!getOperators(field).includes(condition.operator)) return false;
|
|
121
|
+
if (isUnaryOperator(condition.operator)) return true;
|
|
122
|
+
return condition.value !== null && condition.value !== "";
|
|
123
|
+
}
|
|
124
|
+
function getFieldIcon(type) {
|
|
125
|
+
if (type === "currency") return DollarSign;
|
|
126
|
+
if (type === "number") return Hash;
|
|
127
|
+
if (type === "date") return CalendarDays;
|
|
128
|
+
return Type;
|
|
129
|
+
}
|
|
130
|
+
function ConditionRow({
|
|
131
|
+
condition,
|
|
132
|
+
fields,
|
|
133
|
+
index,
|
|
134
|
+
onChange,
|
|
135
|
+
onRemove,
|
|
136
|
+
onCommit
|
|
137
|
+
}) {
|
|
138
|
+
var _a;
|
|
139
|
+
const fieldDef = (_a = fields.find((f) => f.id === condition.field)) != null ? _a : fields[0];
|
|
140
|
+
const operators = getOperators(fieldDef);
|
|
141
|
+
const isUnary = isUnaryOperator(condition.operator);
|
|
142
|
+
const FieldIcon = getFieldIcon(fieldDef.type);
|
|
143
|
+
const handleFieldChange = (newFieldId) => {
|
|
144
|
+
var _a2;
|
|
145
|
+
const newFieldDef = (_a2 = fields.find((f) => f.id === newFieldId)) != null ? _a2 : fields[0];
|
|
146
|
+
if (!newFieldDef) return;
|
|
147
|
+
onChange(__spreadProps(__spreadValues({}, condition), {
|
|
148
|
+
field: newFieldDef.id,
|
|
149
|
+
operator: getDefaultOperator(newFieldDef),
|
|
150
|
+
value: null
|
|
151
|
+
}));
|
|
152
|
+
};
|
|
153
|
+
const handleOperatorChange = (newOp) => {
|
|
154
|
+
onChange(__spreadProps(__spreadValues({}, condition), {
|
|
155
|
+
operator: newOp,
|
|
156
|
+
value: isUnaryOperator(newOp) ? null : condition.value
|
|
157
|
+
}));
|
|
158
|
+
};
|
|
159
|
+
const handleValueChange = (event) => {
|
|
160
|
+
onChange(__spreadProps(__spreadValues({}, condition), {
|
|
161
|
+
value: parseConditionValue(event.target.value, fieldDef.type)
|
|
162
|
+
}));
|
|
163
|
+
};
|
|
164
|
+
return /* @__PURE__ */ jsxs(
|
|
165
|
+
"div",
|
|
166
|
+
{
|
|
167
|
+
className: "grid grid-cols-[52px_minmax(150px,1fr)_120px_minmax(140px,1fr)_auto] items-center gap-2 rounded-lg border border-border/70 bg-background p-2 shadow-sm",
|
|
168
|
+
"data-slot": "condition-row",
|
|
169
|
+
children: [
|
|
170
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground", children: index === 0 ? "Where" : "And" }),
|
|
171
|
+
/* @__PURE__ */ jsxs(Select, { value: condition.field, onValueChange: handleFieldChange, children: [
|
|
172
|
+
/* @__PURE__ */ jsxs(SelectTrigger, { className: "h-8 w-full justify-start gap-2", size: "sm", children: [
|
|
173
|
+
/* @__PURE__ */ jsx(FieldIcon, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
174
|
+
/* @__PURE__ */ jsx(SelectValue, { placeholder: fieldDef.label })
|
|
175
|
+
] }),
|
|
176
|
+
/* @__PURE__ */ jsx(SelectContent, { children: fields.map((field) => {
|
|
177
|
+
const Icon = getFieldIcon(field.type);
|
|
178
|
+
return /* @__PURE__ */ jsx(SelectItem, { value: field.id, children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
|
|
179
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
180
|
+
field.label
|
|
181
|
+
] }) }, field.id);
|
|
182
|
+
}) })
|
|
183
|
+
] }),
|
|
184
|
+
/* @__PURE__ */ jsxs(
|
|
185
|
+
Select,
|
|
186
|
+
{
|
|
187
|
+
value: condition.operator,
|
|
188
|
+
onValueChange: (value) => handleOperatorChange(value),
|
|
189
|
+
children: [
|
|
190
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-full", size: "sm", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Operator" }) }),
|
|
191
|
+
/* @__PURE__ */ jsx(SelectContent, { children: operators.map((op) => /* @__PURE__ */ jsx(SelectItem, { value: op, children: OPERATOR_LABELS[op] }, op)) })
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
),
|
|
195
|
+
isUnary ? /* @__PURE__ */ jsx("div", { className: "h-8 rounded-md border border-dashed border-border/70 bg-muted/30 px-3 py-1.5 text-xs text-muted-foreground", children: "No value needed" }) : /* @__PURE__ */ jsxs("div", { className: "relative flex items-center", children: [
|
|
196
|
+
fieldDef.type === "currency" ? /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-2 text-sm text-muted-foreground", children: "$" }) : null,
|
|
197
|
+
/* @__PURE__ */ jsx(
|
|
198
|
+
Input,
|
|
199
|
+
{
|
|
200
|
+
type: fieldDef.type === "number" || fieldDef.type === "currency" ? "number" : fieldDef.type === "date" ? "date" : "text",
|
|
201
|
+
value: condition.value != null ? String(condition.value) : "",
|
|
202
|
+
onChange: handleValueChange,
|
|
203
|
+
onClick: (event) => event.stopPropagation(),
|
|
204
|
+
onKeyDown: (event) => {
|
|
205
|
+
event.stopPropagation();
|
|
206
|
+
if (event.key === "Enter") {
|
|
207
|
+
onCommit();
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
placeholder: fieldDef.type === "currency" ? "Amount" : fieldDef.type === "number" ? "Enter number..." : fieldDef.type === "date" ? "" : "Enter value...",
|
|
211
|
+
className: cn("h-8", fieldDef.type === "currency" && "pl-6")
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
] }),
|
|
215
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
216
|
+
/* @__PURE__ */ jsx(
|
|
217
|
+
Button,
|
|
218
|
+
{
|
|
219
|
+
type: "button",
|
|
220
|
+
variant: "ghost",
|
|
221
|
+
size: "icon",
|
|
222
|
+
className: "h-8 w-8 text-muted-foreground",
|
|
223
|
+
"aria-label": "Toggle condition visibility",
|
|
224
|
+
onClick: (event) => event.preventDefault(),
|
|
225
|
+
children: /* @__PURE__ */ jsx(Eye, { className: "size-4" })
|
|
226
|
+
}
|
|
227
|
+
),
|
|
228
|
+
/* @__PURE__ */ jsx(
|
|
229
|
+
Button,
|
|
230
|
+
{
|
|
231
|
+
type: "button",
|
|
232
|
+
variant: "ghost",
|
|
233
|
+
size: "icon",
|
|
234
|
+
className: "h-8 w-8 text-muted-foreground",
|
|
235
|
+
"aria-label": "More condition actions",
|
|
236
|
+
onClick: (event) => event.preventDefault(),
|
|
237
|
+
children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4" })
|
|
238
|
+
}
|
|
239
|
+
),
|
|
240
|
+
/* @__PURE__ */ jsx(
|
|
241
|
+
Button,
|
|
242
|
+
{
|
|
243
|
+
type: "button",
|
|
244
|
+
variant: "ghost",
|
|
245
|
+
size: "icon",
|
|
246
|
+
className: "h-8 w-8 text-muted-foreground hover:text-destructive",
|
|
247
|
+
onClick: onRemove,
|
|
248
|
+
"aria-label": "Remove condition",
|
|
249
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "size-4" })
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
] })
|
|
253
|
+
]
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
function DataTableConditionFilter({
|
|
258
|
+
fields,
|
|
259
|
+
conditions,
|
|
260
|
+
onConditionsChange,
|
|
261
|
+
className
|
|
262
|
+
}) {
|
|
263
|
+
const [drafts, setDrafts] = React.useState(
|
|
264
|
+
() => conditions.map((condition) => normalizeCondition(condition, fields))
|
|
265
|
+
);
|
|
266
|
+
React.useEffect(() => {
|
|
267
|
+
setDrafts(conditions.map((condition) => normalizeCondition(condition, fields)));
|
|
268
|
+
}, [conditions, fields]);
|
|
269
|
+
const commitDrafts = React.useCallback(
|
|
270
|
+
(nextDrafts = drafts) => {
|
|
271
|
+
onConditionsChange(
|
|
272
|
+
nextDrafts.map((condition) => normalizeCondition(condition, fields)).filter((condition) => isCompleteCondition(condition, fields))
|
|
273
|
+
);
|
|
274
|
+
},
|
|
275
|
+
[drafts, fields, onConditionsChange]
|
|
276
|
+
);
|
|
277
|
+
const handleAdd = () => {
|
|
278
|
+
const firstField = fields[0];
|
|
279
|
+
if (!firstField) return;
|
|
280
|
+
const committedDrafts = drafts.filter(
|
|
281
|
+
(condition) => isCompleteCondition(condition, fields)
|
|
282
|
+
);
|
|
283
|
+
const nextDrafts = [...committedDrafts, createDraftCondition(firstField)];
|
|
284
|
+
setDrafts(nextDrafts);
|
|
285
|
+
commitDrafts(committedDrafts);
|
|
286
|
+
};
|
|
287
|
+
const handleUpdate = (index, updated) => {
|
|
288
|
+
setDrafts((current) => {
|
|
289
|
+
const next = [...current];
|
|
290
|
+
next[index] = normalizeCondition(updated, fields);
|
|
291
|
+
return next;
|
|
292
|
+
});
|
|
293
|
+
};
|
|
294
|
+
const handleRemove = (index) => {
|
|
295
|
+
setDrafts((current) => {
|
|
296
|
+
const next = current.filter((_, currentIndex) => currentIndex !== index);
|
|
297
|
+
commitDrafts(next);
|
|
298
|
+
return next;
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
const handleClear = () => {
|
|
302
|
+
setDrafts([]);
|
|
303
|
+
onConditionsChange([]);
|
|
304
|
+
};
|
|
305
|
+
const hasAppliedConditions = conditions.length > 0;
|
|
306
|
+
const hasDrafts = drafts.length > 0;
|
|
307
|
+
return /* @__PURE__ */ jsxs(
|
|
308
|
+
"div",
|
|
309
|
+
{
|
|
310
|
+
className: cn(
|
|
311
|
+
"w-[min(760px,calc(100vw-2rem))] rounded-xl border border-border bg-background p-3 text-foreground shadow-xl",
|
|
312
|
+
className
|
|
313
|
+
),
|
|
314
|
+
"data-slot": "condition-filter",
|
|
315
|
+
onClick: (event) => event.stopPropagation(),
|
|
316
|
+
onKeyDown: (event) => {
|
|
317
|
+
if (event.key !== "Escape") {
|
|
318
|
+
event.stopPropagation();
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
children: [
|
|
322
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between gap-3 border-b border-border/70 pb-3", children: [
|
|
323
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
324
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold", children: "Filter builder" }),
|
|
325
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: "Build field, operator, and value conditions." })
|
|
326
|
+
] }),
|
|
327
|
+
/* @__PURE__ */ jsx(
|
|
328
|
+
Button,
|
|
329
|
+
{
|
|
330
|
+
type: "button",
|
|
331
|
+
variant: "ghost",
|
|
332
|
+
size: "sm",
|
|
333
|
+
className: "h-8 text-xs text-destructive hover:text-destructive",
|
|
334
|
+
onClick: handleClear,
|
|
335
|
+
disabled: !hasAppliedConditions && !hasDrafts,
|
|
336
|
+
children: "Clear filters"
|
|
337
|
+
}
|
|
338
|
+
)
|
|
339
|
+
] }),
|
|
340
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: drafts.map((condition, index) => /* @__PURE__ */ jsx(
|
|
341
|
+
ConditionRow,
|
|
342
|
+
{
|
|
343
|
+
condition,
|
|
344
|
+
fields,
|
|
345
|
+
index,
|
|
346
|
+
onChange: (updated) => handleUpdate(index, updated),
|
|
347
|
+
onRemove: () => handleRemove(index),
|
|
348
|
+
onCommit: () => commitDrafts()
|
|
349
|
+
},
|
|
350
|
+
condition.id
|
|
351
|
+
)) }),
|
|
352
|
+
!hasDrafts ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-border/80 bg-muted/20 px-3 py-5 text-center text-xs text-muted-foreground", children: "No builder filters yet. Add a filter to start a condition row." }) : null,
|
|
353
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center justify-between gap-2 border-t border-border/70 pt-3", children: [
|
|
354
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
355
|
+
/* @__PURE__ */ jsxs(
|
|
356
|
+
Button,
|
|
357
|
+
{
|
|
358
|
+
type: "button",
|
|
359
|
+
variant: "ghost",
|
|
360
|
+
size: "sm",
|
|
361
|
+
className: "h-8 text-xs text-muted-foreground",
|
|
362
|
+
onClick: handleAdd,
|
|
363
|
+
children: [
|
|
364
|
+
/* @__PURE__ */ jsx(Plus, { className: "mr-1 size-3.5" }),
|
|
365
|
+
"Add filter"
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
),
|
|
369
|
+
/* @__PURE__ */ jsxs(
|
|
370
|
+
Button,
|
|
371
|
+
{
|
|
372
|
+
type: "button",
|
|
373
|
+
variant: "ghost",
|
|
374
|
+
size: "sm",
|
|
375
|
+
className: "h-8 text-xs text-muted-foreground opacity-60",
|
|
376
|
+
disabled: true,
|
|
377
|
+
"aria-disabled": "true",
|
|
378
|
+
children: [
|
|
379
|
+
/* @__PURE__ */ jsx(Plus, { className: "mr-1 size-3.5" }),
|
|
380
|
+
"Add filter group"
|
|
381
|
+
]
|
|
382
|
+
}
|
|
383
|
+
)
|
|
384
|
+
] }),
|
|
385
|
+
/* @__PURE__ */ jsx(
|
|
386
|
+
Button,
|
|
387
|
+
{
|
|
388
|
+
type: "button",
|
|
389
|
+
size: "sm",
|
|
390
|
+
className: "h-8 text-xs",
|
|
391
|
+
onClick: () => commitDrafts(),
|
|
392
|
+
children: "Apply"
|
|
393
|
+
}
|
|
394
|
+
)
|
|
395
|
+
] })
|
|
396
|
+
]
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
export {
|
|
401
|
+
DEFAULT_OPERATORS,
|
|
402
|
+
DataTableConditionFilter,
|
|
403
|
+
OPERATOR_LABELS,
|
|
404
|
+
generateConditionId,
|
|
405
|
+
getOperators
|
|
406
|
+
};
|
|
407
|
+
//# sourceMappingURL=data-table-condition-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/data-table-condition-filter.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n CalendarDays,\n DollarSign,\n Eye,\n Hash,\n MoreHorizontal,\n Plus,\n Trash2,\n Type,\n} from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport { Input } from \"./input\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"./select\"\n\n// ── Types ──────────────────────────────────────────────────────\n\nexport type ConditionOperator =\n | \"eq\"\n | \"neq\"\n | \"gt\"\n | \"gte\"\n | \"lt\"\n | \"lte\"\n | \"in\"\n | \"is_null\"\n | \"is_not_null\"\n\nexport interface ConditionFieldDef {\n /** Unique field key (e.g., \"Account_Balance__c\") */\n id: string\n /** Display label (e.g., \"Account Balance\") */\n label: string\n /** Field data type — determines which operators are available and how the value input renders */\n type: \"text\" | \"number\" | \"currency\" | \"date\"\n /** Allowed operators for this field. Defaults based on type if not provided. */\n operators?: ConditionOperator[]\n}\n\nexport interface ConditionFilterValue {\n /** Stable identity — used as React key to avoid stale-state bugs on removal */\n id: string\n field: string\n operator: ConditionOperator\n value: string | number | null\n}\n\ninterface DataTableConditionFilterProps {\n /** Available fields the user can filter on */\n fields: ConditionFieldDef[]\n /** Current active conditions */\n conditions: ConditionFilterValue[]\n /** Called when conditions change (add, update, remove) */\n onConditionsChange: (conditions: ConditionFilterValue[]) => void\n className?: string\n}\n\n// ── Constants ──────────────────────────────────────────────────\n\nconst OPERATOR_LABELS: Record<ConditionOperator, string> = {\n eq: \"=\",\n neq: \"≠\",\n gt: \">\",\n gte: \"≥\",\n lt: \"<\",\n lte: \"≤\",\n in: \"contains\",\n is_null: \"is empty\",\n is_not_null: \"is not empty\",\n}\n\nconst NUMERIC_OPERATORS: ConditionOperator[] = [\n \"eq\",\n \"neq\",\n \"gt\",\n \"gte\",\n \"lt\",\n \"lte\",\n \"is_null\",\n \"is_not_null\",\n]\n\nconst DEFAULT_OPERATORS: Record<ConditionFieldDef[\"type\"], ConditionOperator[]> = {\n text: [\"eq\", \"neq\", \"is_null\", \"is_not_null\"],\n number: NUMERIC_OPERATORS,\n currency: NUMERIC_OPERATORS,\n date: [\"gt\", \"gte\", \"lt\", \"lte\", \"eq\", \"neq\", \"is_null\", \"is_not_null\"],\n}\n\n/** Generate a stable unique ID for a new condition row. */\nfunction generateConditionId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n// ── Helpers ────────────────────────────────────────────────────\n\nfunction getOperators(field: ConditionFieldDef): ConditionOperator[] {\n return (field.operators ?? DEFAULT_OPERATORS[field.type]).filter((op) => op !== \"in\")\n}\n\nfunction isUnaryOperator(op: ConditionOperator): boolean {\n return op === \"is_null\" || op === \"is_not_null\"\n}\n\nfunction getDefaultOperator(field: ConditionFieldDef): ConditionOperator {\n return getOperators(field)[0] ?? \"eq\"\n}\n\nfunction createDraftCondition(field: ConditionFieldDef): ConditionFilterValue {\n return {\n id: generateConditionId(),\n field: field.id,\n operator: getDefaultOperator(field),\n value: null,\n }\n}\n\nfunction normalizeCondition(\n condition: ConditionFilterValue,\n fields: ConditionFieldDef[],\n): ConditionFilterValue {\n const field = fields.find((f) => f.id === condition.field) ?? fields[0]\n if (!field) return condition\n\n const operators = getOperators(field)\n const operator = operators.includes(condition.operator)\n ? condition.operator\n : getDefaultOperator(field)\n\n return {\n ...condition,\n field: field.id,\n operator,\n value: isUnaryOperator(operator) ? null : condition.value,\n }\n}\n\nfunction parseConditionValue(\n raw: string,\n fieldType: ConditionFieldDef[\"type\"],\n): string | number | null {\n if (raw === \"\") return null\n if (fieldType === \"number\" || fieldType === \"currency\") {\n const parsed = Number(raw)\n return Number.isNaN(parsed) ? null : parsed\n }\n return raw\n}\n\nfunction isCompleteCondition(\n condition: ConditionFilterValue,\n fields: ConditionFieldDef[],\n): boolean {\n const field = fields.find((f) => f.id === condition.field)\n if (!field) return false\n if (!getOperators(field).includes(condition.operator)) return false\n if (isUnaryOperator(condition.operator)) return true\n return condition.value !== null && condition.value !== \"\"\n}\n\nfunction getFieldIcon(type: ConditionFieldDef[\"type\"]) {\n if (type === \"currency\") return DollarSign\n if (type === \"number\") return Hash\n if (type === \"date\") return CalendarDays\n return Type\n}\n\n// ── Condition Row ──────────────────────────────────────────────\n\ninterface ConditionRowProps {\n condition: ConditionFilterValue\n fields: ConditionFieldDef[]\n index: number\n onChange: (updated: ConditionFilterValue) => void\n onRemove: () => void\n onCommit: () => void\n}\n\nfunction ConditionRow({\n condition,\n fields,\n index,\n onChange,\n onRemove,\n onCommit,\n}: ConditionRowProps) {\n const fieldDef = fields.find((f) => f.id === condition.field) ?? fields[0]\n const operators = getOperators(fieldDef)\n const isUnary = isUnaryOperator(condition.operator)\n const FieldIcon = getFieldIcon(fieldDef.type)\n\n const handleFieldChange = (newFieldId: string) => {\n const newFieldDef = fields.find((f) => f.id === newFieldId) ?? fields[0]\n if (!newFieldDef) return\n onChange({\n ...condition,\n field: newFieldDef.id,\n operator: getDefaultOperator(newFieldDef),\n value: null,\n })\n }\n\n const handleOperatorChange = (newOp: ConditionOperator) => {\n onChange({\n ...condition,\n operator: newOp,\n value: isUnaryOperator(newOp) ? null : condition.value,\n })\n }\n\n const handleValueChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n onChange({\n ...condition,\n value: parseConditionValue(event.target.value, fieldDef.type),\n })\n }\n\n return (\n <div\n className=\"grid grid-cols-[52px_minmax(150px,1fr)_120px_minmax(140px,1fr)_auto] items-center gap-2 rounded-lg border border-border/70 bg-background p-2 shadow-sm\"\n data-slot=\"condition-row\"\n >\n <div className=\"text-xs font-medium text-muted-foreground\">\n {index === 0 ? \"Where\" : \"And\"}\n </div>\n\n <Select value={condition.field} onValueChange={handleFieldChange}>\n <SelectTrigger className=\"h-8 w-full justify-start gap-2\" size=\"sm\">\n <FieldIcon className=\"h-3.5 w-3.5 text-muted-foreground\" />\n <SelectValue placeholder={fieldDef.label} />\n </SelectTrigger>\n <SelectContent>\n {fields.map((field) => {\n const Icon = getFieldIcon(field.type)\n return (\n <SelectItem key={field.id} value={field.id}>\n <span className=\"inline-flex items-center gap-2\">\n <Icon className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {field.label}\n </span>\n </SelectItem>\n )\n })}\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(value) => handleOperatorChange(value as ConditionOperator)}\n >\n <SelectTrigger className=\"h-8 w-full\" size=\"sm\">\n <SelectValue placeholder=\"Operator\" />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {isUnary ? (\n <div className=\"h-8 rounded-md border border-dashed border-border/70 bg-muted/30 px-3 py-1.5 text-xs text-muted-foreground\">\n No value needed\n </div>\n ) : (\n <div className=\"relative flex items-center\">\n {fieldDef.type === \"currency\" ? (\n <span className=\"pointer-events-none absolute left-2 text-sm text-muted-foreground\">\n $\n </span>\n ) : null}\n <Input\n type={\n fieldDef.type === \"number\" || fieldDef.type === \"currency\"\n ? \"number\"\n : fieldDef.type === \"date\"\n ? \"date\"\n : \"text\"\n }\n value={condition.value != null ? String(condition.value) : \"\"}\n onChange={handleValueChange}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === \"Enter\") {\n onCommit()\n }\n }}\n placeholder={\n fieldDef.type === \"currency\"\n ? \"Amount\"\n : fieldDef.type === \"number\"\n ? \"Enter number...\"\n : fieldDef.type === \"date\"\n ? \"\"\n : \"Enter value...\"\n }\n className={cn(\"h-8\", fieldDef.type === \"currency\" && \"pl-6\")}\n />\n </div>\n )}\n\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 text-muted-foreground\"\n aria-label=\"Toggle condition visibility\"\n onClick={(event) => event.preventDefault()}\n >\n <Eye className=\"size-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 text-muted-foreground\"\n aria-label=\"More condition actions\"\n onClick={(event) => event.preventDefault()}\n >\n <MoreHorizontal className=\"size-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 text-muted-foreground hover:text-destructive\"\n onClick={onRemove}\n aria-label=\"Remove condition\"\n >\n <Trash2 className=\"size-4\" />\n </Button>\n </div>\n </div>\n )\n}\n\n// ── Main Component ─────────────────────────────────────────────\n\nfunction DataTableConditionFilter({\n fields,\n conditions,\n onConditionsChange,\n className,\n}: DataTableConditionFilterProps) {\n const [drafts, setDrafts] = React.useState<ConditionFilterValue[]>(() =>\n conditions.map((condition) => normalizeCondition(condition, fields)),\n )\n\n React.useEffect(() => {\n setDrafts(conditions.map((condition) => normalizeCondition(condition, fields)))\n }, [conditions, fields])\n\n const commitDrafts = React.useCallback(\n (nextDrafts: ConditionFilterValue[] = drafts) => {\n onConditionsChange(\n nextDrafts\n .map((condition) => normalizeCondition(condition, fields))\n .filter((condition) => isCompleteCondition(condition, fields)),\n )\n },\n [drafts, fields, onConditionsChange],\n )\n\n const handleAdd = () => {\n const firstField = fields[0]\n if (!firstField) return\n const committedDrafts = drafts.filter((condition) =>\n isCompleteCondition(condition, fields),\n )\n const nextDrafts = [...committedDrafts, createDraftCondition(firstField)]\n setDrafts(nextDrafts)\n commitDrafts(committedDrafts)\n }\n\n const handleUpdate = (index: number, updated: ConditionFilterValue) => {\n setDrafts((current) => {\n const next = [...current]\n next[index] = normalizeCondition(updated, fields)\n return next\n })\n }\n\n const handleRemove = (index: number) => {\n setDrafts((current) => {\n const next = current.filter((_, currentIndex) => currentIndex !== index)\n commitDrafts(next)\n return next\n })\n }\n\n const handleClear = () => {\n setDrafts([])\n onConditionsChange([])\n }\n\n const hasAppliedConditions = conditions.length > 0\n const hasDrafts = drafts.length > 0\n\n return (\n <div\n className={cn(\n \"w-[min(760px,calc(100vw-2rem))] rounded-xl border border-border bg-background p-3 text-foreground shadow-xl\",\n className,\n )}\n data-slot=\"condition-filter\"\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n if (event.key !== \"Escape\") {\n event.stopPropagation()\n }\n }}\n >\n <div className=\"mb-3 flex items-center justify-between gap-3 border-b border-border/70 pb-3\">\n <div>\n <div className=\"text-sm font-semibold\">Filter builder</div>\n <div className=\"text-xs text-muted-foreground\">\n Build field, operator, and value conditions.\n </div>\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 text-xs text-destructive hover:text-destructive\"\n onClick={handleClear}\n disabled={!hasAppliedConditions && !hasDrafts}\n >\n Clear filters\n </Button>\n </div>\n\n <div className=\"flex flex-col gap-2\">\n {drafts.map((condition, index) => (\n <ConditionRow\n key={condition.id}\n condition={condition}\n fields={fields}\n index={index}\n onChange={(updated) => handleUpdate(index, updated)}\n onRemove={() => handleRemove(index)}\n onCommit={() => commitDrafts()}\n />\n ))}\n </div>\n\n {!hasDrafts ? (\n <div className=\"rounded-lg border border-dashed border-border/80 bg-muted/20 px-3 py-5 text-center text-xs text-muted-foreground\">\n No builder filters yet. Add a filter to start a condition row.\n </div>\n ) : null}\n\n <div className=\"mt-3 flex flex-wrap items-center justify-between gap-2 border-t border-border/70 pt-3\">\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 text-xs text-muted-foreground\"\n onClick={handleAdd}\n >\n <Plus className=\"mr-1 size-3.5\" />\n Add filter\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 text-xs text-muted-foreground opacity-60\"\n disabled\n aria-disabled=\"true\"\n >\n <Plus className=\"mr-1 size-3.5\" />\n Add filter group\n </Button>\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"h-8 text-xs\"\n onClick={() => commitDrafts()}\n >\n Apply\n </Button>\n </div>\n </div>\n )\n}\n\nexport {\n DataTableConditionFilter,\n OPERATOR_LABELS,\n DEFAULT_OPERATORS,\n generateConditionId,\n getOperators,\n}\nexport type { DataTableConditionFilterProps }\n"],"mappings":"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","names":["_a"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { ConditionFieldDef, ConditionFilterValue } from './data-table-condition-filter.js';
|
|
2
3
|
|
|
3
4
|
interface FilterOption {
|
|
4
5
|
label: string;
|
|
@@ -11,6 +12,8 @@ interface DataTableFilterCategory {
|
|
|
11
12
|
className?: string;
|
|
12
13
|
}>;
|
|
13
14
|
options: (string | FilterOption)[];
|
|
15
|
+
/** Filter behavior. Defaults to "multi" (checkbox multi-select). */
|
|
16
|
+
type?: "multi" | "single" | "boolean";
|
|
14
17
|
}
|
|
15
18
|
interface DataTableFilterProps {
|
|
16
19
|
categories: DataTableFilterCategory[];
|
|
@@ -25,7 +28,15 @@ interface DataTableFilterProps {
|
|
|
25
28
|
onTogglePreset?: (categoryId: string, option: string) => void;
|
|
26
29
|
/** Label shown on preset chips to distinguish from user-applied filters. Default: "Default". */
|
|
27
30
|
presetLabel?: string;
|
|
31
|
+
/** Fields exposed in the unified condition-builder panel. */
|
|
32
|
+
conditionFields?: ConditionFieldDef[];
|
|
33
|
+
/** Active builder-managed field/operator/value conditions. */
|
|
34
|
+
conditionFilters?: ConditionFilterValue[];
|
|
35
|
+
/** Callback when builder-managed conditions are applied, removed, or cleared. */
|
|
36
|
+
onConditionFiltersChange?: (conditions: ConditionFilterValue[]) => void;
|
|
37
|
+
/** Dropdown entry label for the condition-builder panel. Default: "Add filter". */
|
|
38
|
+
conditionBuilderLabel?: string;
|
|
28
39
|
}
|
|
29
|
-
declare function DataTableFilter({ categories, selectedFilters, onToggleFilter, className, optionSearchThreshold, presetFilters, onTogglePreset, presetLabel, }: DataTableFilterProps): React.JSX.Element;
|
|
40
|
+
declare function DataTableFilter({ categories, selectedFilters, onToggleFilter, className, optionSearchThreshold, presetFilters, onTogglePreset, presetLabel, conditionFields, conditionFilters, onConditionFiltersChange, conditionBuilderLabel, }: DataTableFilterProps): React.JSX.Element;
|
|
30
41
|
|
|
31
42
|
export { DataTableFilter, type DataTableFilterCategory, type DataTableFilterProps, type FilterOption };
|