1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
import type { ColumnType, Filter, FilterOperator, } from "@/types/table"
import { type Column } from "@tanstack/react-table"
import { dataTableConfig } from "@/config/data-table"
import { FilterFn, Row } from "@tanstack/react-table"
/**
* Generate common pinning styles for a table column.
*
* This function calculates and returns CSS properties for pinned columns in a data table.
* It handles both left and right pinning, applying appropriate styles for positioning,
* shadows, and z-index. The function also considers whether the column is the last left-pinned
* or first right-pinned column to apply specific shadow effects.
*
* @param options - The options for generating pinning styles.
* @param options.column - The column object for which to generate styles.
* @param options.withBorder - Whether to show a box shadow between pinned and scrollable columns.
* @returns A React.CSSProperties object containing the calculated styles.
*/
export function getCommonPinningStyles<TData>({
column,
withBorder = false,
}: {
column: Column<TData>
/**
* Show box shadow between pinned and scrollable columns.
* @default false
*/
withBorder?: boolean
}): React.CSSProperties {
const isPinned = column.getIsPinned()
const isLastLeftPinnedColumn =
isPinned === "left" && column.getIsLastColumn("left")
const isFirstRightPinnedColumn =
isPinned === "right" && column.getIsFirstColumn("right")
return {
boxShadow: withBorder
? isLastLeftPinnedColumn
? "-4px 0 4px -4px hsl(var(--border)) inset"
: isFirstRightPinnedColumn
? "4px 0 4px -4px hsl(var(--border)) inset"
: undefined
: undefined,
left: isPinned === "left" ? `${column.getStart("left")}px` : undefined,
right: isPinned === "right" ? `${column.getAfter("right")}px` : undefined,
opacity: isPinned ? 0.97 : 1,
position: isPinned ? "sticky" : "relative",
background: isPinned ? "hsl(var(--background))" : "hsl(var(--background))",
width: column.getSize(),
zIndex: isPinned ? 1 : 0,
}
}
/**
* Determine the default filter operator for a given column type.
*
* This function returns the most appropriate default filter operator based on the
* column's data type. For text columns, it returns 'iLike' (case-insensitive like),
* while for all other types, it returns 'eq' (equality).
*
* @param columnType - The type of the column (e.g., 'text', 'number', 'date', etc.).
* @returns The default FilterOperator for the given column type.
*/
export function getDefaultFilterOperator(
columnType: ColumnType
): FilterOperator {
if (columnType === "text") {
return "iLike"
}
return "eq"
}
/**
* Retrieve the list of applicable filter operators for a given column type.
*
* This function returns an array of filter operators that are relevant and applicable
* to the specified column type. It uses a predefined mapping of column types to
* operator lists, falling back to text operators if an unknown column type is provided.
*
* @param columnType - The type of the column for which to get filter operators.
* @returns An array of objects, each containing a label and value for a filter operator.
*/
export function getFilterOperators(columnType: ColumnType) {
const operatorMap: Record<
ColumnType,
{ label: string; value: FilterOperator }[]
> = {
text: dataTableConfig.textOperators,
number: dataTableConfig.numericOperators,
select: dataTableConfig.selectOperators,
"multi-select": dataTableConfig.selectOperators,
boolean: dataTableConfig.booleanOperators,
date: dataTableConfig.dateOperators,
}
return operatorMap[columnType] ?? dataTableConfig.textOperators
}
/**
* Filters out invalid or empty filters from an array of filters.
*
* This function processes an array of filters and returns a new array
* containing only the valid filters. A filter is considered valid if:
* - It has an 'isEmpty' or 'isNotEmpty' operator, or
* - Its value is not empty (for array values, at least one element must be present;
* for other types, the value must not be an empty string, null, or undefined)
*
* @param filters - An array of Filter objects to be validated.
* @returns A new array containing only the valid filters.
*/
export function getValidFilters<TData>(
filters: Filter<TData>[]
): Filter<TData>[] {
return filters?.filter(
(filter) =>
filter.operator === "isEmpty" ||
filter.operator === "isNotEmpty" ||
(Array.isArray(filter.value)
? filter.value.length > 0
: filter.value !== "" &&
filter.value !== null &&
filter.value !== undefined)
)
}
interface NumericFilterValue {
operator: string
inputValue?: number
}
export const numericFilter: FilterFn<any> = (
row: Row<any>,
columnId: string,
filterValue: NumericFilterValue
) => {
const rowValue = row.getValue(columnId)
// handle "isEmpty" / "isNotEmpty"
if (filterValue.operator === "isEmpty") {
return rowValue == null || rowValue === ""
} else if (filterValue.operator === "isNotEmpty") {
return !(rowValue == null || rowValue === "")
}
// parse rowValue → numeric
const numericRowVal =
typeof rowValue === "number" ? rowValue : parseFloat(String(rowValue))
if (isNaN(numericRowVal)) {
// rowValue not a number
return false
}
// parse filterValue.inputValue
const filterNum = filterValue.inputValue
if (filterNum == null || isNaN(filterNum)) {
// if user didn’t actually type a number, match everything or nothing (your choice)
return true
}
// compare based on operator
switch (filterValue.operator) {
case "eq":
return numericRowVal === filterNum
case "ne":
return numericRowVal !== filterNum
case "lt":
return numericRowVal < filterNum
case "lte":
return numericRowVal <= filterNum
case "gt":
return numericRowVal > filterNum
case "gte":
return numericRowVal >= filterNum
default:
return true
}
}
|