@toolbox-web/grid 1.23.3 → 1.24.0

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.
Files changed (236) hide show
  1. package/README.md +35 -6
  2. package/all.d.ts +2 -2
  3. package/all.d.ts.map +1 -1
  4. package/all.js +2 -2
  5. package/all.js.map +1 -1
  6. package/index.js +1 -1
  7. package/index.js.map +1 -1
  8. package/lib/core/grid.d.ts +28 -0
  9. package/lib/core/grid.d.ts.map +1 -1
  10. package/lib/core/internal/feature-hook.d.ts +8 -0
  11. package/lib/core/internal/feature-hook.d.ts.map +1 -0
  12. package/lib/core/plugin/base-plugin.d.ts +14 -2
  13. package/lib/core/plugin/base-plugin.d.ts.map +1 -1
  14. package/lib/core/plugin/types.d.ts +2 -1
  15. package/lib/core/plugin/types.d.ts.map +1 -1
  16. package/lib/core/types.d.ts +68 -2
  17. package/lib/core/types.d.ts.map +1 -1
  18. package/lib/features/clipboard.d.ts +8 -0
  19. package/lib/features/clipboard.d.ts.map +1 -0
  20. package/lib/features/clipboard.js +2 -0
  21. package/lib/features/clipboard.js.map +1 -0
  22. package/lib/features/column-virtualization.d.ts +8 -0
  23. package/lib/features/column-virtualization.d.ts.map +1 -0
  24. package/lib/features/column-virtualization.js +2 -0
  25. package/lib/features/column-virtualization.js.map +1 -0
  26. package/lib/features/context-menu.d.ts +8 -0
  27. package/lib/features/context-menu.d.ts.map +1 -0
  28. package/lib/features/context-menu.js +2 -0
  29. package/lib/features/context-menu.js.map +1 -0
  30. package/lib/features/editing.d.ts +8 -0
  31. package/lib/features/editing.d.ts.map +1 -0
  32. package/lib/features/editing.js +2 -0
  33. package/lib/features/editing.js.map +1 -0
  34. package/lib/features/export.d.ts +8 -0
  35. package/lib/features/export.d.ts.map +1 -0
  36. package/lib/features/export.js +2 -0
  37. package/lib/features/export.js.map +1 -0
  38. package/lib/features/filtering.d.ts +8 -0
  39. package/lib/features/filtering.d.ts.map +1 -0
  40. package/lib/features/filtering.js +2 -0
  41. package/lib/features/filtering.js.map +1 -0
  42. package/lib/features/grouping-columns.d.ts +8 -0
  43. package/lib/features/grouping-columns.d.ts.map +1 -0
  44. package/lib/features/grouping-columns.js +2 -0
  45. package/lib/features/grouping-columns.js.map +1 -0
  46. package/lib/features/grouping-rows.d.ts +8 -0
  47. package/lib/features/grouping-rows.d.ts.map +1 -0
  48. package/lib/features/grouping-rows.js +2 -0
  49. package/lib/features/grouping-rows.js.map +1 -0
  50. package/lib/features/magic-string.es-CkyDP9Ir.mjs.map +1 -0
  51. package/lib/features/master-detail.d.ts +8 -0
  52. package/lib/features/master-detail.d.ts.map +1 -0
  53. package/lib/features/master-detail.js +2 -0
  54. package/lib/features/master-detail.js.map +1 -0
  55. package/lib/features/multi-sort.d.ts +10 -0
  56. package/lib/features/multi-sort.d.ts.map +1 -0
  57. package/lib/features/multi-sort.js +2 -0
  58. package/lib/features/multi-sort.js.map +1 -0
  59. package/lib/features/pinned-columns.d.ts +18 -0
  60. package/lib/features/pinned-columns.d.ts.map +1 -0
  61. package/lib/features/pinned-columns.js +2 -0
  62. package/lib/features/pinned-columns.js.map +1 -0
  63. package/lib/features/pinned-rows.d.ts +8 -0
  64. package/lib/features/pinned-rows.d.ts.map +1 -0
  65. package/lib/features/pinned-rows.js +2 -0
  66. package/lib/features/pinned-rows.js.map +1 -0
  67. package/lib/features/pivot.d.ts +8 -0
  68. package/lib/features/pivot.d.ts.map +1 -0
  69. package/lib/features/pivot.js +2 -0
  70. package/lib/features/pivot.js.map +1 -0
  71. package/lib/features/print.d.ts +8 -0
  72. package/lib/features/print.d.ts.map +1 -0
  73. package/lib/features/print.js +2 -0
  74. package/lib/features/print.js.map +1 -0
  75. package/lib/features/registry.d.ts +50 -0
  76. package/lib/features/registry.d.ts.map +1 -0
  77. package/lib/features/registry.js +2 -0
  78. package/lib/features/registry.js.map +1 -0
  79. package/lib/features/registry.spec.js +5 -0
  80. package/lib/features/registry.spec.js.map +1 -0
  81. package/lib/features/reorder-columns.d.ts +10 -0
  82. package/lib/features/reorder-columns.d.ts.map +1 -0
  83. package/lib/features/reorder-columns.js +2 -0
  84. package/lib/features/reorder-columns.js.map +1 -0
  85. package/lib/features/reorder-rows.d.ts +10 -0
  86. package/lib/features/reorder-rows.d.ts.map +1 -0
  87. package/lib/features/reorder-rows.js +2 -0
  88. package/lib/features/reorder-rows.js.map +1 -0
  89. package/lib/features/responsive.d.ts +8 -0
  90. package/lib/features/responsive.d.ts.map +1 -0
  91. package/lib/features/responsive.js +2 -0
  92. package/lib/features/responsive.js.map +1 -0
  93. package/lib/features/selection.d.ts +8 -0
  94. package/lib/features/selection.d.ts.map +1 -0
  95. package/lib/features/selection.js +2 -0
  96. package/lib/features/selection.js.map +1 -0
  97. package/lib/features/server-side.d.ts +8 -0
  98. package/lib/features/server-side.d.ts.map +1 -0
  99. package/lib/features/server-side.js +2 -0
  100. package/lib/features/server-side.js.map +1 -0
  101. package/lib/features/tree.d.ts +8 -0
  102. package/lib/features/tree.d.ts.map +1 -0
  103. package/lib/features/tree.js +2 -0
  104. package/lib/features/tree.js.map +1 -0
  105. package/lib/features/undo-redo.d.ts +8 -0
  106. package/lib/features/undo-redo.d.ts.map +1 -0
  107. package/lib/features/undo-redo.js +2 -0
  108. package/lib/features/undo-redo.js.map +1 -0
  109. package/lib/features/visibility.d.ts +8 -0
  110. package/lib/features/visibility.d.ts.map +1 -0
  111. package/lib/features/visibility.js +2 -0
  112. package/lib/features/visibility.js.map +1 -0
  113. package/lib/plugins/clipboard/ClipboardPlugin.d.ts +3 -3
  114. package/lib/plugins/clipboard/index.js +1 -1
  115. package/lib/plugins/clipboard/index.js.map +1 -1
  116. package/lib/plugins/clipboard/types.d.ts +1 -1
  117. package/lib/plugins/column-virtualization/index.js +1 -1
  118. package/lib/plugins/column-virtualization/index.js.map +1 -1
  119. package/lib/plugins/column-virtualization/types.d.ts +24 -2
  120. package/lib/plugins/column-virtualization/types.d.ts.map +1 -1
  121. package/lib/plugins/context-menu/index.js +1 -1
  122. package/lib/plugins/context-menu/index.js.map +1 -1
  123. package/lib/plugins/editing/index.js +1 -1
  124. package/lib/plugins/editing/index.js.map +1 -1
  125. package/lib/plugins/editing/types.d.ts +1 -1
  126. package/lib/plugins/export/ExportPlugin.d.ts +1 -1
  127. package/lib/plugins/export/index.js +1 -1
  128. package/lib/plugins/export/index.js.map +1 -1
  129. package/lib/plugins/export/types.d.ts +9 -1
  130. package/lib/plugins/export/types.d.ts.map +1 -1
  131. package/lib/plugins/filtering/index.js +1 -1
  132. package/lib/plugins/filtering/index.js.map +1 -1
  133. package/lib/plugins/filtering/types.d.ts +586 -29
  134. package/lib/plugins/filtering/types.d.ts.map +1 -1
  135. package/lib/plugins/grouping-columns/index.d.ts +1 -1
  136. package/lib/plugins/grouping-columns/index.js +1 -1
  137. package/lib/plugins/grouping-columns/index.js.map +1 -1
  138. package/lib/plugins/grouping-rows/index.d.ts +2 -2
  139. package/lib/plugins/grouping-rows/index.d.ts.map +1 -1
  140. package/lib/plugins/grouping-rows/index.js +1 -1
  141. package/lib/plugins/grouping-rows/index.js.map +1 -1
  142. package/lib/plugins/grouping-rows/types.d.ts +48 -3
  143. package/lib/plugins/grouping-rows/types.d.ts.map +1 -1
  144. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts +2 -2
  145. package/lib/plugins/master-detail/index.js +1 -1
  146. package/lib/plugins/master-detail/index.js.map +1 -1
  147. package/lib/plugins/multi-sort/index.js +1 -1
  148. package/lib/plugins/multi-sort/index.js.map +1 -1
  149. package/lib/plugins/multi-sort/types.d.ts +40 -6
  150. package/lib/plugins/multi-sort/types.d.ts.map +1 -1
  151. package/lib/plugins/pinned-columns/index.js +1 -1
  152. package/lib/plugins/pinned-columns/index.js.map +1 -1
  153. package/lib/plugins/pinned-columns/types.d.ts +3 -3
  154. package/lib/plugins/pinned-rows/index.js +1 -1
  155. package/lib/plugins/pinned-rows/index.js.map +1 -1
  156. package/lib/plugins/pinned-rows/types.d.ts +42 -4
  157. package/lib/plugins/pinned-rows/types.d.ts.map +1 -1
  158. package/lib/plugins/pivot/index.js +1 -1
  159. package/lib/plugins/pivot/index.js.map +1 -1
  160. package/lib/plugins/pivot/types.d.ts +66 -1
  161. package/lib/plugins/pivot/types.d.ts.map +1 -1
  162. package/lib/plugins/print/PrintPlugin.d.ts +1 -1
  163. package/lib/plugins/print/index.js +1 -1
  164. package/lib/plugins/print/index.js.map +1 -1
  165. package/lib/plugins/print/types.d.ts +9 -1
  166. package/lib/plugins/print/types.d.ts.map +1 -1
  167. package/lib/plugins/{reorder → reorder-columns}/ReorderPlugin.d.ts +5 -3
  168. package/lib/plugins/reorder-columns/ReorderPlugin.d.ts.map +1 -0
  169. package/lib/plugins/reorder-columns/column-drag.d.ts.map +1 -0
  170. package/lib/plugins/{reorder → reorder-columns}/index.d.ts +2 -2
  171. package/lib/plugins/reorder-columns/index.d.ts.map +1 -0
  172. package/lib/plugins/reorder-columns/index.js +2 -0
  173. package/lib/plugins/reorder-columns/index.js.map +1 -0
  174. package/lib/plugins/{reorder → reorder-columns}/types.d.ts +17 -1
  175. package/lib/plugins/reorder-columns/types.d.ts.map +1 -0
  176. package/lib/plugins/{row-reorder → reorder-rows}/RowReorderPlugin.d.ts +5 -3
  177. package/lib/plugins/reorder-rows/RowReorderPlugin.d.ts.map +1 -0
  178. package/lib/plugins/{row-reorder → reorder-rows}/index.d.ts +2 -2
  179. package/lib/plugins/reorder-rows/index.d.ts.map +1 -0
  180. package/lib/plugins/reorder-rows/index.js +2 -0
  181. package/lib/plugins/reorder-rows/index.js.map +1 -0
  182. package/lib/plugins/{row-reorder → reorder-rows}/types.d.ts +5 -0
  183. package/lib/plugins/reorder-rows/types.d.ts.map +1 -0
  184. package/lib/plugins/responsive/ResponsivePlugin.d.ts +2 -2
  185. package/lib/plugins/responsive/index.js +1 -1
  186. package/lib/plugins/responsive/index.js.map +1 -1
  187. package/lib/plugins/selection/SelectionPlugin.d.ts +5 -5
  188. package/lib/plugins/selection/index.js +1 -1
  189. package/lib/plugins/selection/index.js.map +1 -1
  190. package/lib/plugins/server-side/index.js +1 -1
  191. package/lib/plugins/server-side/index.js.map +1 -1
  192. package/lib/plugins/server-side/types.d.ts +82 -0
  193. package/lib/plugins/server-side/types.d.ts.map +1 -1
  194. package/lib/plugins/tree/index.js +1 -1
  195. package/lib/plugins/tree/index.js.map +1 -1
  196. package/lib/plugins/undo-redo/index.js +1 -1
  197. package/lib/plugins/undo-redo/index.js.map +1 -1
  198. package/lib/plugins/visibility/VisibilityPlugin.d.ts +2 -2
  199. package/lib/plugins/visibility/index.js +1 -1
  200. package/lib/plugins/visibility/index.js.map +1 -1
  201. package/lib/plugins/visibility/types.d.ts +16 -2
  202. package/lib/plugins/visibility/types.d.ts.map +1 -1
  203. package/package.json +17 -2
  204. package/public.d.ts +9 -2
  205. package/public.d.ts.map +1 -1
  206. package/umd/grid.all.umd.js +1 -1
  207. package/umd/grid.all.umd.js.map +1 -1
  208. package/umd/grid.umd.js +1 -1
  209. package/umd/grid.umd.js.map +1 -1
  210. package/umd/plugins/clipboard.umd.js.map +1 -1
  211. package/umd/plugins/export.umd.js.map +1 -1
  212. package/umd/plugins/master-detail.umd.js.map +1 -1
  213. package/umd/plugins/print.umd.js.map +1 -1
  214. package/umd/plugins/reorder-columns.umd.js +2 -0
  215. package/umd/plugins/reorder-columns.umd.js.map +1 -0
  216. package/umd/plugins/reorder-rows.umd.js +2 -0
  217. package/umd/plugins/reorder-rows.umd.js.map +1 -0
  218. package/umd/plugins/responsive.umd.js.map +1 -1
  219. package/umd/plugins/selection.umd.js.map +1 -1
  220. package/umd/plugins/visibility.umd.js.map +1 -1
  221. package/lib/plugins/reorder/ReorderPlugin.d.ts.map +0 -1
  222. package/lib/plugins/reorder/column-drag.d.ts.map +0 -1
  223. package/lib/plugins/reorder/index.d.ts.map +0 -1
  224. package/lib/plugins/reorder/index.js +0 -2
  225. package/lib/plugins/reorder/index.js.map +0 -1
  226. package/lib/plugins/reorder/types.d.ts.map +0 -1
  227. package/lib/plugins/row-reorder/RowReorderPlugin.d.ts.map +0 -1
  228. package/lib/plugins/row-reorder/index.d.ts.map +0 -1
  229. package/lib/plugins/row-reorder/index.js +0 -2
  230. package/lib/plugins/row-reorder/index.js.map +0 -1
  231. package/lib/plugins/row-reorder/types.d.ts.map +0 -1
  232. package/umd/plugins/reorder.umd.js +0 -2
  233. package/umd/plugins/reorder.umd.js.map +0 -1
  234. package/umd/plugins/row-reorder.umd.js +0 -2
  235. package/umd/plugins/row-reorder.umd.js.map +0 -1
  236. /package/lib/plugins/{reorder → reorder-columns}/column-drag.d.ts +0 -0
@@ -127,9 +127,165 @@ export interface FilterParams {
127
127
  /** Placeholder text for text inputs */
128
128
  placeholder?: string;
129
129
  }
130
- /** Supported filter types */
130
+ /**
131
+ * The category of filter applied to a column, which determines the available
132
+ * {@link FilterOperator operators} and the filter panel UI rendered.
133
+ *
134
+ * | Type | Panel UI | Compatible operators |
135
+ * |------|----------|---------------------|
136
+ * | `'text'` | Text input with operator dropdown | `contains`, `notContains`, `equals`, `notEquals`, `startsWith`, `endsWith`, `blank`, `notBlank` |
137
+ * | `'number'` | Range slider with min/max inputs | `lessThan`, `lessThanOrEqual`, `greaterThan`, `greaterThanOrEqual`, `between`, `blank`, `notBlank` |
138
+ * | `'date'` | Date pickers (from/to) | Same as `'number'` |
139
+ * | `'set'` | Checkbox list of unique values | `in`, `notIn`, `blank`, `notBlank` |
140
+ * | `'boolean'` | Checkbox list (`true` / `false` / `(Blank)`) | `in`, `notIn`, `blank`, `notBlank` |
141
+ *
142
+ * The grid auto-detects the filter type from the column's `type` property.
143
+ * Override by setting `filter.type` explicitly in the {@link FilterModel}.
144
+ */
131
145
  export type FilterType = 'text' | 'number' | 'date' | 'set' | 'boolean';
132
- /** Filter operators for different filter types */
146
+ /**
147
+ * Filter operators used in {@link FilterModel} to define how a cell value is compared
148
+ * against the filter value. Operators are grouped by the column types they apply to.
149
+ *
150
+ * **Multiple filters** on different columns use AND logic — a row must match all active filters.
151
+ *
152
+ * ---
153
+ *
154
+ * ## Text operators (`FilterType: 'text'`)
155
+ *
156
+ * Compare cell values as strings. **Case-insensitive by default** (controlled by `FilterConfig.caseSensitive`).
157
+ * Non-string cell values are coerced via `String()` before comparison.
158
+ *
159
+ * | Operator | Matches when | Example: filter = `"lic"` |
160
+ * |--|--|--|
161
+ * | `contains` | Cell value includes the filter as a substring | `"Alice"` ✓, `"Bob"` ✗ |
162
+ * | `notContains` | Cell value does **not** include the filter substring | `"Bob"` ✓, `"Alice"` ✗ |
163
+ * | `equals` | Cell value exactly equals the filter (after case normalization) | `"lic"` ✓, `"Alice"` ✗ |
164
+ * | `notEquals` | Cell value does **not** equal the filter | `"Alice"` ✓, `"lic"` ✗ |
165
+ * | `startsWith` | Cell value begins with the filter | filter `"Al"` → `"Alice"` ✓ |
166
+ * | `endsWith` | Cell value ends with the filter | filter `"ce"` → `"Alice"` ✓ |
167
+ *
168
+ * **When to use:**
169
+ * - `contains` — the default for free-text search fields; most intuitive for users
170
+ * - `equals` — when filtering on exact known values (e.g. status codes)
171
+ * - `startsWith` / `endsWith` — for prefix/suffix matching (e.g. file extensions, area codes)
172
+ * - `notContains` / `notEquals` — exclusion filters ("show everything except...")
173
+ *
174
+ * ---
175
+ *
176
+ * ## Blank operators (`FilterType: all`)
177
+ *
178
+ * These work universally across all filter types and check for **empty** values.
179
+ * They are evaluated first, before any type-specific logic.
180
+ *
181
+ * | Operator | Matches when | Does NOT match |
182
+ * |--|--|--|
183
+ * | `blank` | Cell is `null`, `undefined`, or `""` (empty string) | `0`, `false`, `NaN` |
184
+ * | `notBlank` | Cell has any non-null, non-empty value | `null`, `undefined`, `""` |
185
+ *
186
+ * **When to use:**
187
+ * - `blank` — find rows with missing data (e.g. "show incomplete records")
188
+ * - `notBlank` — exclude rows with missing data (e.g. "show only filled records")
189
+ *
190
+ * ---
191
+ *
192
+ * ## Numeric / date operators (`FilterType: 'number' | 'date'`)
193
+ *
194
+ * Compare values numerically. An internal `toNumeric()` conversion handles:
195
+ * - Numbers → used directly
196
+ * - `Date` objects → converted via `.getTime()` (milliseconds since epoch)
197
+ * - ISO date strings (e.g. `"2025-03-11"`) → parsed as `Date`, then `.getTime()`
198
+ * - Unparseable values → `NaN`, which fails all comparisons (row excluded)
199
+ *
200
+ * | Operator | Matches when (`cell` vs `filter.value`) |
201
+ * |--|--|
202
+ * | `lessThan` | `cell < value` |
203
+ * | `lessThanOrEqual` | `cell <= value` |
204
+ * | `greaterThan` | `cell > value` |
205
+ * | `greaterThanOrEqual` | `cell >= value` |
206
+ * | `between` | `value <= cell <= valueTo` (inclusive both ends) |
207
+ *
208
+ * The `between` operator requires both `filter.value` (min) and `filter.valueTo` (max).
209
+ * In the built-in UI:
210
+ * - **Number panels** render a dual-thumb range slider with min/max inputs
211
+ * - **Date panels** render "From" and "To" date pickers
212
+ *
213
+ * **When to use:**
214
+ * - `between` — range filters (age 25–35, dates in Q1, prices $10–$50)
215
+ * - `greaterThan` / `lessThan` — open-ended thresholds ("salary above 100k")
216
+ * - `greaterThanOrEqual` / `lessThanOrEqual` — inclusive thresholds
217
+ *
218
+ * ---
219
+ *
220
+ * ## Set operators (`FilterType: 'set' | 'boolean'`)
221
+ *
222
+ * Filter by inclusion/exclusion against a set of discrete values. The built-in filter panel
223
+ * shows a checkbox list of unique values; unchecked items form the excluded set.
224
+ *
225
+ * `filter.value` is an `unknown[]` array containing the set of values.
226
+ *
227
+ * | Operator | Matches when | Typical use |
228
+ * |--|--|--|
229
+ * | `notIn` | Cell value is **not** in the excluded array | Default for checkbox lists — unchecked items are excluded |
230
+ * | `in` | Cell value **is** in the included array | Explicit inclusion ("show only these") |
231
+ *
232
+ * **Blank handling:** Blank cells (`null`, `undefined`, `""`) are represented by the
233
+ * sentinel `BLANK_FILTER_VALUE` (`"(Blank)"`) in the values array. The panel renders a
234
+ * "(Blank)" checkbox; its checked/unchecked state controls whether blank rows are shown.
235
+ *
236
+ * **With `filterValue` extractor:** When a column defines `filterValue` to extract multiple
237
+ * values from a complex cell (e.g. an array of objects):
238
+ * - `notIn` — row is hidden if **any** extracted value is in the excluded set
239
+ * - `in` — row passes if **any** extracted value is in the included set
240
+ * - Empty extraction (no values) is treated as a blank cell
241
+ *
242
+ * **When to use:**
243
+ * - `notIn` — the default for set/boolean filters; maps naturally to "uncheck to hide"
244
+ * - `in` — when programmatically setting a filter to show only specific values
245
+ *
246
+ * ---
247
+ *
248
+ * ## Operator–type compatibility quick reference
249
+ *
250
+ * | Operator | text | number | date | set | boolean |
251
+ * |--|:--:|:--:|:--:|:--:|:--:|
252
+ * | `contains` | ✓ | | | | |
253
+ * | `notContains` | ✓ | | | | |
254
+ * | `equals` | ✓ | | | | |
255
+ * | `notEquals` | ✓ | | | | |
256
+ * | `startsWith` | ✓ | | | | |
257
+ * | `endsWith` | ✓ | | | | |
258
+ * | `blank` | ✓ | ✓ | ✓ | ✓ | ✓ |
259
+ * | `notBlank` | ✓ | ✓ | ✓ | ✓ | ✓ |
260
+ * | `lessThan` | | ✓ | ✓ | | |
261
+ * | `lessThanOrEqual` | | ✓ | ✓ | | |
262
+ * | `greaterThan` | | ✓ | ✓ | | |
263
+ * | `greaterThanOrEqual` | | ✓ | ✓ | | |
264
+ * | `between` | | ✓ | ✓ | | |
265
+ * | `in` | | | | ✓ | ✓ |
266
+ * | `notIn` | | | | ✓ | ✓ |
267
+ *
268
+ * @example
269
+ * ```typescript
270
+ * // Text: free-text search on name column
271
+ * { field: 'name', type: 'text', operator: 'contains', value: 'alice' }
272
+ *
273
+ * // Number: salary above 100k
274
+ * { field: 'salary', type: 'number', operator: 'greaterThan', value: 100000 }
275
+ *
276
+ * // Date: hired in Q1 2025
277
+ * { field: 'hireDate', type: 'date', operator: 'between', value: '2025-01-01', valueTo: '2025-03-31' }
278
+ *
279
+ * // Set: show only Engineering and Sales departments
280
+ * { field: 'department', type: 'set', operator: 'in', value: ['Engineering', 'Sales'] }
281
+ *
282
+ * // Set: hide specific statuses (checkbox-style exclusion)
283
+ * { field: 'status', type: 'set', operator: 'notIn', value: ['Inactive', 'Archived'] }
284
+ *
285
+ * // Blank: find rows missing an email
286
+ * { field: 'email', type: 'text', operator: 'blank', value: '' }
287
+ * ```
288
+ */
133
289
  export type FilterOperator = 'contains' | 'notContains' | 'equals' | 'notEquals' | 'startsWith' | 'endsWith' | 'blank' | 'notBlank' | 'lessThan' | 'lessThanOrEqual' | 'greaterThan' | 'greaterThanOrEqual' | 'between' | 'in' | 'notIn';
134
290
  /** Filter model representing a single filter condition */
135
291
  export interface FilterModel {
@@ -144,27 +300,191 @@ export interface FilterModel {
144
300
  /** Secondary value for 'between' operator */
145
301
  valueTo?: unknown;
146
302
  }
147
- /** Parameters passed to custom filter panel renderer */
303
+ /**
304
+ * Parameters passed to a custom {@link FilterPanelRenderer} when the filter panel
305
+ * opens for a column. Provides all the state and action callbacks needed to build
306
+ * a fully custom filter UI.
307
+ *
308
+ * The object is created fresh each time the panel opens and captures the current
309
+ * filter state for the column. Use the action methods (`applySetFilter`,
310
+ * `applyTextFilter`, `clearFilter`, `closePanel`) to drive filtering — they
311
+ * handle state updates, re-rendering, and panel lifecycle automatically.
312
+ *
313
+ * **Resolution priority** for filter panel renderers:
314
+ * 1. Plugin-level `filterPanelRenderer` (in `FilterConfig`)
315
+ * 2. Type-level `filterPanelRenderer` (in `typeDefaults`)
316
+ * 3. Built-in default panel (checkbox set filter, number range, or date range)
317
+ *
318
+ * Returning `undefined` from a plugin-level renderer falls through to the next
319
+ * level, so you can override only specific columns/fields while keeping defaults
320
+ * for the rest.
321
+ *
322
+ * **Framework adapters** wrap this for idiomatic usage:
323
+ * - **Angular**: Extend `BaseFilterPanel` — params are available as a signal input.
324
+ * - **React**: Use a single-argument `(params) => ReactNode` signature.
325
+ * - **Vue**: Use a single-argument `(params) => VNode` signature.
326
+ *
327
+ * @example
328
+ * ```typescript
329
+ * // Vanilla: radio-button filter for a "status" column, default for everything else
330
+ * new FilteringPlugin({
331
+ * filterPanelRenderer: (container, params) => {
332
+ * if (params.field !== 'status') return undefined; // fall through to default
333
+ *
334
+ * const options = ['All', ...params.uniqueValues.map(String)];
335
+ * options.forEach(opt => {
336
+ * const label = document.createElement('label');
337
+ * label.style.display = 'block';
338
+ * const radio = document.createElement('input');
339
+ * radio.type = 'radio';
340
+ * radio.name = 'status';
341
+ * radio.checked = opt === 'All' && params.excludedValues.size === 0;
342
+ * radio.addEventListener('change', () => {
343
+ * if (opt === 'All') params.clearFilter();
344
+ * else params.applySetFilter(
345
+ * params.uniqueValues.filter(v => String(v) !== opt) as unknown[]
346
+ * );
347
+ * });
348
+ * label.append(radio, ` ${opt}`);
349
+ * container.appendChild(label);
350
+ * });
351
+ * },
352
+ * });
353
+ * ```
354
+ *
355
+ * @example
356
+ * ```typescript
357
+ * // React: custom slider filter via single-argument signature
358
+ * <DataGrid
359
+ * filtering={{
360
+ * filterPanelRenderer: (params) => (
361
+ * <MySliderFilter
362
+ * min={0} max={100}
363
+ * currentFilter={params.currentFilter}
364
+ * onApply={(min, max) => params.applyTextFilter('between', min, max)}
365
+ * onClear={() => params.clearFilter()}
366
+ * />
367
+ * ),
368
+ * }}
369
+ * />
370
+ * ```
371
+ */
148
372
  export interface FilterPanelParams {
149
- /** The field being filtered */
373
+ /**
374
+ * The field name (column key) being filtered.
375
+ * Matches {@link ColumnConfig.field} — use it to conditionally render
376
+ * different UIs for different columns in a shared renderer.
377
+ */
150
378
  field: string;
151
- /** The column configuration */
379
+ /**
380
+ * The full column configuration for the filtered column.
381
+ * Useful for reading `column.type`, `column.filterParams`, `column.header`,
382
+ * or any other column metadata to tailor the filter panel UI.
383
+ */
152
384
  column: ColumnConfig;
153
- /** All unique values for this field */
385
+ /**
386
+ * All unique values present in the current dataset for this field,
387
+ * sorted and de-duplicated. For columns with a `filterValue` extractor,
388
+ * these are the extracted/flattened values (not the raw cell values).
389
+ *
390
+ * When a `valuesHandler` is provided in the plugin config, this array
391
+ * contains the values returned by the handler instead of locally-extracted ones.
392
+ *
393
+ * Typical use: render checkboxes, radio buttons, or a searchable list.
394
+ */
154
395
  uniqueValues: unknown[];
155
- /** Currently excluded values (for set filter) */
396
+ /**
397
+ * Currently excluded values for set-type (`notIn`) filters.
398
+ * An empty `Set` means no values are excluded (i.e., all values are shown).
399
+ *
400
+ * Use this to restore checkbox/toggle states when the panel re-opens.
401
+ * A value present in this set should appear **unchecked** in a set filter UI.
402
+ */
156
403
  excludedValues: Set<unknown>;
157
- /** Current search text */
404
+ /**
405
+ * The current search text the user has typed into the filter panel's
406
+ * search input (if any). Persisted across panel open/close cycles for
407
+ * the same field. Defaults to `''` when no search has been performed.
408
+ *
409
+ * Use this to pre-populate a search box if your custom panel includes one.
410
+ */
158
411
  searchText: string;
159
- /** Current active filter model for this field, if any */
412
+ /**
413
+ * The currently active {@link FilterModel} for this field, or `undefined`
414
+ * if no filter is applied. Inspect this to reflect the active filter state
415
+ * in your UI (e.g., highlight the active operator, show the current value).
416
+ *
417
+ * @example
418
+ * ```typescript
419
+ * if (params.currentFilter?.operator === 'between') {
420
+ * minInput.value = String(params.currentFilter.value);
421
+ * maxInput.value = String(params.currentFilter.valueTo);
422
+ * }
423
+ * ```
424
+ */
160
425
  currentFilter?: FilterModel;
161
- /** Apply a set filter (exclude these values). Pass optional `valueTo` metadata (e.g. a selected range) to store alongside the filter. */
426
+ /**
427
+ * Apply a **set filter** (`notIn` operator) that excludes the given values.
428
+ * Rows whose field value is in `excludedValues` will be hidden.
429
+ *
430
+ * Calling this automatically closes the panel and triggers a filter-change event.
431
+ *
432
+ * Pass an empty array to clear the set filter (show all values).
433
+ *
434
+ * @param excludedValues - Array of values to exclude.
435
+ * @param valueTo - Optional metadata stored alongside the filter
436
+ * (e.g., a label, date range, or selected category). Accessible later
437
+ * via `FilterModel.valueTo` in the `filter-change` event or `currentFilter`.
438
+ *
439
+ * @example
440
+ * ```typescript
441
+ * // Exclude "Inactive" and "Archived" statuses
442
+ * params.applySetFilter(['Inactive', 'Archived']);
443
+ *
444
+ * // Exclude everything except the selected value
445
+ * const excluded = params.uniqueValues.filter(v => v !== selectedValue);
446
+ * params.applySetFilter(excluded as unknown[]);
447
+ * ```
448
+ */
162
449
  applySetFilter: (excludedValues: unknown[], valueTo?: unknown) => void;
163
- /** Apply a text/number/date filter */
450
+ /**
451
+ * Apply a **text, number, or date filter** with the given operator and value(s).
452
+ *
453
+ * Calling this automatically closes the panel and triggers a filter-change event.
454
+ *
455
+ * @param operator - The filter operator to apply (e.g., `'contains'`,
456
+ * `'greaterThan'`, `'between'`). See {@link FilterOperator} for all options.
457
+ * @param value - The primary filter value.
458
+ * @param valueTo - Secondary value required by the `'between'` operator
459
+ * (defines the upper bound of the range).
460
+ *
461
+ * @example
462
+ * ```typescript
463
+ * // Text: contains search
464
+ * params.applyTextFilter('contains', searchInput.value);
465
+ *
466
+ * // Number: range between 10 and 100
467
+ * params.applyTextFilter('between', 10, 100);
468
+ *
469
+ * // Date: after a specific date
470
+ * params.applyTextFilter('greaterThan', '2025-01-01');
471
+ * ```
472
+ */
164
473
  applyTextFilter: (operator: FilterOperator, value: string | number, valueTo?: string | number) => void;
165
- /** Clear the filter for this field */
474
+ /**
475
+ * Clear the active filter for this field entirely and close the panel.
476
+ * After calling, the column will show all rows (as if no filter was ever applied).
477
+ *
478
+ * Equivalent to removing the field's entry from the filter model.
479
+ */
166
480
  clearFilter: () => void;
167
- /** Close the filter panel */
481
+ /**
482
+ * Close the filter panel **without** applying or clearing any filter.
483
+ * Use this for a "Cancel" / dismiss action where the user abandons changes.
484
+ *
485
+ * Note: `applySetFilter`, `applyTextFilter`, and `clearFilter` already close
486
+ * the panel automatically — you only need `closePanel` for explicit dismiss.
487
+ */
168
488
  closePanel: () => void;
169
489
  }
170
490
  /** Custom filter panel renderer function. Return undefined to use default panel for this column. */
@@ -211,17 +531,163 @@ export type FilterValuesHandler = (field: string, column: ColumnConfig) => Promi
211
531
  * ```
212
532
  */
213
533
  export type FilterHandler<TRow = unknown> = (filters: FilterModel[], currentRows: TRow[]) => TRow[] | Promise<TRow[]>;
214
- /** Configuration options for the filtering plugin */
534
+ /**
535
+ * Configuration options for the {@link FilteringPlugin}.
536
+ *
537
+ * Pass this object to the `FilteringPlugin` constructor to customize filtering
538
+ * behavior, panel rendering, server-side integration, and state persistence.
539
+ *
540
+ * @typeParam TRow - The row data type. Inferred when using a typed `filterHandler`.
541
+ *
542
+ * @example
543
+ * ```typescript
544
+ * // Basic usage with defaults
545
+ * new FilteringPlugin()
546
+ *
547
+ * // Customized local filtering
548
+ * new FilteringPlugin({
549
+ * debounceMs: 200,
550
+ * caseSensitive: true,
551
+ * trackColumnState: true,
552
+ * })
553
+ *
554
+ * // Server-side filtering with custom values
555
+ * new FilteringPlugin<Employee>({
556
+ * valuesHandler: async (field) => {
557
+ * const res = await fetch(`/api/employees/distinct/${field}`);
558
+ * return res.json();
559
+ * },
560
+ * filterHandler: async (filters) => {
561
+ * const params = new URLSearchParams();
562
+ * filters.forEach(f => params.append(f.field, `${f.operator}:${f.value}`));
563
+ * const res = await fetch(`/api/employees?${params}`);
564
+ * return res.json();
565
+ * },
566
+ * })
567
+ * ```
568
+ */
215
569
  export interface FilterConfig<TRow = unknown> {
216
- /** Debounce delay in ms for filter input (default: 300) */
570
+ /**
571
+ * Debounce delay in milliseconds for the search input inside the default
572
+ * filter panel. Controls how long the panel waits after the user stops
573
+ * typing before re-filtering the unique values list.
574
+ *
575
+ * Lower values feel more responsive but cause more DOM updates.
576
+ * Higher values reduce work for columns with many unique values.
577
+ *
578
+ * @default 300
579
+ *
580
+ * @example
581
+ * ```typescript
582
+ * // Faster response for small datasets
583
+ * new FilteringPlugin({ debounceMs: 100 })
584
+ *
585
+ * // Slower debounce for columns with thousands of unique values
586
+ * new FilteringPlugin({ debounceMs: 500 })
587
+ * ```
588
+ */
217
589
  debounceMs?: number;
218
- /** Whether text filtering is case sensitive (default: false) */
590
+ /**
591
+ * Whether text-based filtering comparisons are case-sensitive.
592
+ *
593
+ * When `false` (default), `"alice"` matches `"Alice"`, `"ALICE"`, etc.
594
+ * When `true`, only exact case matches pass the filter.
595
+ *
596
+ * Affects both:
597
+ * - The core `filterRows()` logic (text operators like `contains`, `equals`, etc.)
598
+ * - The search input inside the default filter panel (value list filtering)
599
+ *
600
+ * @default false
601
+ *
602
+ * @example
603
+ * ```typescript
604
+ * // Enable case-sensitive filtering
605
+ * new FilteringPlugin({ caseSensitive: true })
606
+ * ```
607
+ */
219
608
  caseSensitive?: boolean;
220
- /** Whether to trim whitespace from filter input (default: true) */
609
+ /**
610
+ * Whether to trim leading/trailing whitespace from filter input values
611
+ * before applying them.
612
+ *
613
+ * @default true
614
+ *
615
+ * @remarks
616
+ * **Reserved for future use.** This option is accepted in configuration
617
+ * but not yet applied in the current filtering implementation.
618
+ */
221
619
  trimInput?: boolean;
222
- /** Use Web Worker for filtering large datasets >1000 rows (default: true) */
620
+ /**
621
+ * Whether to offload filtering to a Web Worker for large datasets.
622
+ *
623
+ * @default true
624
+ *
625
+ * @remarks
626
+ * **Reserved for future use.** This option is accepted in configuration
627
+ * but not yet implemented. Filtering currently runs synchronously on
628
+ * the main thread for all dataset sizes. Performance is excellent for
629
+ * most use cases — the grid handles 10,000+ rows without noticeable delay
630
+ * thanks to result caching and debounced input.
631
+ *
632
+ * When implemented, this will automatically offload `filterRows()` to a
633
+ * Web Worker when the row count exceeds an internal threshold, keeping
634
+ * the main thread responsive during heavy filtering operations.
635
+ */
223
636
  useWorker?: boolean;
224
- /** Custom filter panel renderer (replaces default panel content) */
637
+ /**
638
+ * Custom filter panel renderer that replaces the built-in panel content
639
+ * for **all** columns (unless you return `undefined` for specific columns
640
+ * to fall through to the next level).
641
+ *
642
+ * **Resolution priority** (first non-empty result wins):
643
+ * 1. This plugin-level `filterPanelRenderer`
644
+ * 2. Type-level `filterPanelRenderer` (in `gridConfig.typeDefaults[type]`)
645
+ * 3. Built-in panel (checkbox set filter, number range slider, or date picker)
646
+ *
647
+ * The renderer receives the panel container element and a {@link FilterPanelParams}
648
+ * object with state and action callbacks. Append your UI to the container.
649
+ *
650
+ * **Return `undefined`** (or leave the container empty) to fall through to
651
+ * the next resolution level. This lets you override only specific fields.
652
+ *
653
+ * @example
654
+ * ```typescript
655
+ * // Override only the "status" column, use defaults for everything else
656
+ * new FilteringPlugin({
657
+ * filterPanelRenderer: (container, params) => {
658
+ * if (params.field !== 'status') return undefined; // fall through
659
+ *
660
+ * params.uniqueValues.forEach(val => {
661
+ * const btn = document.createElement('button');
662
+ * btn.textContent = String(val);
663
+ * btn.onclick = () => {
664
+ * const excluded = params.uniqueValues.filter(v => v !== val);
665
+ * params.applySetFilter(excluded as unknown[]);
666
+ * };
667
+ * container.appendChild(btn);
668
+ * });
669
+ * },
670
+ * })
671
+ * ```
672
+ *
673
+ * @example
674
+ * ```typescript
675
+ * // Replace ALL filter panels with a custom component
676
+ * new FilteringPlugin({
677
+ * filterPanelRenderer: (container, params) => {
678
+ * const myFilter = new MyCustomFilterElement();
679
+ * myFilter.field = params.field;
680
+ * myFilter.values = params.uniqueValues;
681
+ * myFilter.onApply = (excluded) => params.applySetFilter(excluded);
682
+ * myFilter.onClear = () => params.clearFilter();
683
+ * container.appendChild(myFilter);
684
+ * },
685
+ * })
686
+ * ```
687
+ *
688
+ * @see {@link FilterPanelParams} for all available state and action callbacks.
689
+ * @see {@link FilterPanelRenderer} for the function signature.
690
+ */
225
691
  filterPanelRenderer?: FilterPanelRenderer;
226
692
  /**
227
693
  * Whether filter state should be included in column state persistence.
@@ -229,28 +695,119 @@ export interface FilterConfig<TRow = unknown> {
229
695
  * When `true`:
230
696
  * - `getColumnState()` includes filter data for each column
231
697
  * - Filter changes fire the `column-state-change` event (debounced)
232
- * - `applyColumnState()` restores filter state
698
+ * - `applyColumnState()` restores filter state from a saved snapshot
233
699
  *
234
700
  * When `false` (default):
235
701
  * - Filters are excluded from column state entirely
236
- * - Filter changes do not fire `column-state-change`
702
+ * - Filter changes do **not** fire `column-state-change`
703
+ *
704
+ * Enable this when you persist column state (e.g., to localStorage or a server)
705
+ * and want filter selections to survive page reloads.
237
706
  *
238
707
  * @default false
708
+ *
709
+ * @example
710
+ * ```typescript
711
+ * // Persist filters alongside column order, widths, and sort state
712
+ * new FilteringPlugin({ trackColumnState: true })
713
+ *
714
+ * // Save/restore cycle:
715
+ * const state = grid.getColumnState(); // includes filter data
716
+ * localStorage.setItem('grid-state', JSON.stringify(state));
717
+ * // ... later ...
718
+ * grid.applyColumnState(JSON.parse(localStorage.getItem('grid-state')!));
719
+ * ```
239
720
  */
240
721
  trackColumnState?: boolean;
241
722
  /**
242
- * Async handler for fetching unique values from a server.
243
- * When provided, this is called instead of extracting values from local rows.
244
- * Useful for server-side datasets where not all data is loaded.
723
+ * Async handler for fetching unique filter values from a server.
724
+ *
725
+ * When provided, this handler is called **each time a filter panel opens**
726
+ * instead of extracting unique values from the locally-loaded rows. The panel
727
+ * shows a loading indicator while the handler resolves.
728
+ *
729
+ * Use this for server-side or paginated datasets where the client only holds
730
+ * a subset of the data and the local unique values would be incomplete.
731
+ *
732
+ * The returned values populate `FilterPanelParams.uniqueValues` and appear
733
+ * in the checkbox list (or are passed to your custom `filterPanelRenderer`).
734
+ *
735
+ * @example
736
+ * ```typescript
737
+ * new FilteringPlugin({
738
+ * valuesHandler: async (field, column) => {
739
+ * const res = await fetch(`/api/data/distinct/${field}`);
740
+ * return res.json(); // ['Engineering', 'Marketing', 'Sales', ...]
741
+ * },
742
+ * })
743
+ * ```
744
+ *
745
+ * @example
746
+ * ```typescript
747
+ * // Combine with filterHandler for full server-side filtering
748
+ * new FilteringPlugin<Employee>({
749
+ * valuesHandler: async (field) => {
750
+ * const res = await fetch(`/api/employees/distinct/${field}`);
751
+ * return res.json();
752
+ * },
753
+ * filterHandler: async (filters) => {
754
+ * const body = JSON.stringify(filters);
755
+ * const res = await fetch('/api/employees/filter', { method: 'POST', body });
756
+ * return res.json();
757
+ * },
758
+ * })
759
+ * ```
760
+ *
761
+ * @see {@link FilterValuesHandler} for the full type signature.
245
762
  */
246
763
  valuesHandler?: FilterValuesHandler;
247
764
  /**
248
- * Async handler for applying filters on a server.
249
- * When provided, filtering is delegated to the server instead of local filtering.
250
- * Should return the filtered rows from the server.
765
+ * Async handler for delegating filtering to a server.
766
+ *
767
+ * When provided, the plugin's `processRows()` hook becomes a **passthrough**
768
+ * (returns rows unfiltered) and instead calls this handler whenever the
769
+ * active filters change. The handler should return the filtered rows, which
770
+ * replace the grid's current data.
771
+ *
772
+ * This enables full server-side filtering for large datasets that can't be
773
+ * loaded into the browser. The handler receives the complete list of active
774
+ * {@link FilterModel} objects and the current row array (useful for optimistic
775
+ * updates or reference).
776
+ *
777
+ * The handler may return rows synchronously (plain array) or asynchronously
778
+ * (Promise). While an async handler is pending, the grid retains its current
779
+ * rows until the new data arrives.
780
+ *
781
+ * @example
782
+ * ```typescript
783
+ * // Server-side filtering with query params
784
+ * new FilteringPlugin<Employee>({
785
+ * filterHandler: async (filters, currentRows) => {
786
+ * const params = new URLSearchParams();
787
+ * filters.forEach(f => params.append(f.field, `${f.operator}:${f.value}`));
788
+ * const res = await fetch(`/api/employees?${params}`);
789
+ * return res.json();
790
+ * },
791
+ * })
792
+ * ```
793
+ *
794
+ * @example
795
+ * ```typescript
796
+ * // POST-based filtering with full filter models
797
+ * new FilteringPlugin<Product>({
798
+ * filterHandler: async (filters) => {
799
+ * const res = await fetch('/api/products/filter', {
800
+ * method: 'POST',
801
+ * headers: { 'Content-Type': 'application/json' },
802
+ * body: JSON.stringify({ filters }),
803
+ * });
804
+ * return res.json();
805
+ * },
806
+ * })
807
+ * ```
251
808
  *
252
- * Note: When using filterHandler, processRows() becomes a passthrough
253
- * and the returned rows replace the grid's data.
809
+ * @see {@link FilterHandler} for the full type signature.
810
+ * @see {@link FilterChangeDetail} for the event emitted after filter changes.
254
811
  */
255
812
  filterHandler?: FilterHandler<TRow>;
256
813
  }