import type { HotInstance } from '../../core/types'; import { BasePlugin } from '../base'; import ConditionCollection from './conditionCollection'; import DataFilter from './dataFilter'; import ConditionUpdateObserver from './conditionUpdateObserver'; import type { DropdownMenu } from '../dropdownMenu/dropdownMenu'; import { TrimmingMap } from '../../translations'; import type { BaseComponent } from './component/_base'; export type OperationType = 'conjunction' | 'disjunction' | 'disjunctionWithExtraCondition'; export interface ConditionId { name: string; args: unknown[]; } export interface ColumnConditions { column: number; conditions: ConditionId[]; operation: OperationType; } export declare const PLUGIN_KEY = "filters"; export declare const PLUGIN_PRIORITY = 250; /** * @plugin Filters * @class Filters * * @description * The plugin allows filtering the table data either by the built-in component or with the API. * * See [the filtering demo](@/guides/columns/column-filter/column-filter.md) for examples. * * @example * ::: only-for javascript * ```js * const container = document.getElementById('example'); * const hot = new Handsontable(container, { * data: getData(), * colHeaders: true, * rowHeaders: true, * dropdownMenu: true, * filters: true * }); * ``` * ::: * * ::: only-for react * ```jsx * * ``` * ::: * * ::: only-for angular * ```ts * settings = { * data: getData(), * colHeaders: true, * rowHeaders: true, * dropdownMenu: true, * filters: true, * }; * ``` * * ```html * * ``` * ::: */ export declare class Filters extends BasePlugin { #private; /** * Returns the plugin key used to identify this plugin in Handsontable settings. */ static get PLUGIN_KEY(): string; /** * Returns the priority order used to determine the order in which plugins are initialized. */ static get PLUGIN_PRIORITY(): number; /** * Returns the default settings applied when the plugin is enabled without explicit configuration. */ static get DEFAULT_SETTINGS(): { searchMode: string; }; /** * Returns validator functions for each plugin setting to verify their values are valid before applying them. */ static get SETTINGS_VALIDATORS(): { searchMode: (value: unknown) => boolean; }; /** * Returns the list of plugin dependencies required before this plugin can be initialized. */ static get PLUGIN_DEPS(): string[]; /** * Instance of {@link DropdownMenu}. * * @private * @type {DropdownMenu} */ dropdownMenuPlugin: DropdownMenu | null; /** * Instance of {@link ConditionCollection}. * * @private * @type {ConditionCollection} */ conditionCollection: ConditionCollection | null; /** * Instance of {@link ConditionUpdateObserver}. * * @private * @type {ConditionUpdateObserver} */ conditionUpdateObserver: ConditionUpdateObserver | null; /** * Map, where key is component identifier and value represent `BaseComponent` element or it derivatives. * * @private * @type {Map} */ components: Map; /** * Map of skipped rows by plugin. * * @private * @type {null|TrimmingMap} */ filtersRowsMap: TrimmingMap | null; /** * Initializes the plugin and registers the column header hook needed to inject the filter UI. */ constructor(hotInstance: HotInstance); /** * Checks if the plugin is enabled in the handsontable settings. This method is executed in {@link Hooks#beforeInit} * hook and if it returns `true` then the {@link Filters#enablePlugin} method is called. * * @returns {boolean} */ isEnabled(): boolean; /** * Enables the plugin functionality for this Handsontable instance. */ enablePlugin(): void; /** * Update plugin state after Handsontable settings update. */ updatePlugin(): void; /** * Disables the plugin functionality for this Handsontable instance. */ disablePlugin(): void; /** * Register shortcuts responsible for clearing the filters. * * @private */ registerShortcuts(): void; /** * Unregister shortcuts responsible for clearing the filters. * * @private */ unregisterShortcuts(): void; /** * @memberof Filters# * @function addCondition * @description * Adds condition to the conditions collection at specified column index. * * Possible predefined conditions: * * | Condition | Description | Expected `args` | * |---|---|---| * | `begins_with` | Begins with | `[value: string]`, e.g. `['de']` | * | `between` | Between | `[from: number\|string, to: number\|string]`, e.g. `[10, 50]` | * | `by_value` | By value | `[[...values: Array]]`, e.g. `[['ing', 'ed', 'as']]`. The outer array wraps a single inner array that contains all values to **keep** (show) after filtering. | * | `contains` | Contains | `[value: string]`, e.g. `['ing']` | * | `date_after` | After a date (exclusive) | `[dateString: string]`, e.g. `['1/1/2023']`. The format must match the column's `dateFormat` option. | * | `date_after_or_equal` | After or equal to a date (inclusive) | `[dateString: string]`, e.g. `['1/1/2023']`. The format must match the column's `dateFormat` option. | * | `date_before` | Before a date (exclusive) | `[dateString: string]`, e.g. `['1/1/2023']`. The format must match the column's `dateFormat` option. | * | `date_before_or_equal` | Before or equal to a date (inclusive) | `[dateString: string]`, e.g. `['1/1/2023']`. The format must match the column's `dateFormat` option. | * | `date_today` | Today | `[]` | * | `date_tomorrow` | Tomorrow | `[]` | * | `date_yesterday` | Yesterday | `[]` | * | `empty` | Empty | `[]` | * | `ends_with` | Ends with | `[value: string]`, e.g. `['ing']` | * | `eq` | Equal | `[value: string\|number]`, e.g. `['John']` | * | `gt` | Greater than | `[value: number]`, e.g. `[95]` | * | `gte` | Greater than or equal | `[value: number]`, e.g. `[95]` | * | `intl_date_after` | After a date, exclusive (locale-aware) | `[dateString: string]`, e.g. `['2023-01-01']` | * | `intl_date_after_or_equal` | After or equal to a date, inclusive (locale-aware) | `[dateString: string]`, e.g. `['2023-01-01']` | * | `intl_date_before` | Before a date, exclusive (locale-aware) | `[dateString: string]`, e.g. `['2023-01-01']` | * | `intl_date_before_or_equal` | Before or equal to a date, inclusive (locale-aware) | `[dateString: string]`, e.g. `['2023-01-01']` | * | `intl_date_between` | Between dates (locale-aware) | `[fromDateString: string, toDateString: string]`, e.g. `['2023-01-01', '2023-12-31']` | * | `intl_date_today` | Today (locale-aware) | `[]` | * | `intl_date_tomorrow` | Tomorrow (locale-aware) | `[]` | * | `intl_date_yesterday` | Yesterday (locale-aware) | `[]` | * | `intl_time_after` | After a time (locale-aware) | `[timeString: string]`, e.g. `['12:00']` | * | `intl_time_before` | Before a time (locale-aware) | `[timeString: string]`, e.g. `['08:00']` | * | `intl_time_between` | Between times (locale-aware) | `[fromTimeString: string, toTimeString: string]`, e.g. `['08:00', '12:00']` | * | `lt` | Less than | `[value: number]`, e.g. `[10]` | * | `lte` | Less than or equal | `[value: number]`, e.g. `[10]` | * | `none` | None (no filter) | `[]` | * | `not_between` | Not between | `[from: number\|string, to: number\|string]`, e.g. `[10, 50]` | * | `not_contains` | Not contains | `[value: string]`, e.g. `['ing']` | * | `not_empty` | Not empty | `[]` | * | `neq` | Not equal | `[value: string\|number]`, e.g. `['John']` | * * Possible operations on collection of conditions: * * `conjunction` - [**Conjunction**](https://en.wikipedia.org/wiki/Logical_conjunction) on conditions collection (by default), i.e. for such operation:
c1 AND c2 AND c3 AND c4 ... AND cn === TRUE, where c1 ... cn are conditions. * * `disjunction` - [**Disjunction**](https://en.wikipedia.org/wiki/Logical_disjunction) on conditions collection, i.e. for such operation:
c1 OR c2 OR c3 OR c4 ... OR cn === TRUE, where c1, c2, c3, c4 ... cn are conditions. * * `disjunctionWithExtraCondition` - **Disjunction** on first `n - 1`\* conditions from collection with an extra requirement computed from the last condition, i.e. for such operation:
c1 OR c2 OR c3 OR c4 ... OR cn-1 AND cn === TRUE, where c1, c2, c3, c4 ... cn are conditions. * * \* when `n` is collection size; it's used i.e. for one operation introduced from UI (when choosing from filter's drop-down menu two conditions with OR operator between them, mixed with choosing values from the multiple choice select) * * **Note**: Mind that you cannot mix different types of operations (for instance, if you use `conjunction`, use it consequently for a particular column). * * **Note**: If the number of conditions added programmatically via `addCondition()` exceeds the capacity of the * filter's dropdown UI (at most 2 regular conditions and 1 `by_value` condition per column), the extra conditions * will be applied to the data but will not be visible or editable in the dropdown menu. * * @example * ::: only-for javascript * ```js * const container = document.getElementById('example'); * const hot = new Handsontable(container, { * data: getData(), * filters: true * }); * * // access to filters plugin instance * const filtersPlugin = hot.getPlugin('filters'); * * // add filter "Begins with" with value "de" to column at index 1 * filtersPlugin.addCondition(1, 'begins_with', ['de']); * filtersPlugin.filter(); * * // add filter "Between" 10 and 50 to column at index 1 * filtersPlugin.addCondition(1, 'between', [10, 50]); * filtersPlugin.filter(); * * // add filter "By value" to column at index 1 * // in this case all values that don't match will be filtered * filtersPlugin.addCondition(1, 'by_value', [['ing', 'ed', 'as', 'on']]); * filtersPlugin.filter(); * * // add filter "Contains" with value "ing" to column at index 1 * filtersPlugin.addCondition(1, 'contains', ['ing']); * filtersPlugin.filter(); * * // add filter "After a date" with value "1/1/2023" to column at index 1 * filtersPlugin.addCondition(1, 'date_after', ['1/1/2023']); * filtersPlugin.filter(); * * // add filter "Before a date" with value "1/1/2023" to column at index 1 * filtersPlugin.addCondition(1, 'date_before', ['1/1/2023']); * filtersPlugin.filter(); * * // add filter "Today" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'date_today', []); * filtersPlugin.filter(); * * // add filter "Tomorrow" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'date_tomorrow', []); * filtersPlugin.filter(); * * // add filter "Yesterday" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'date_yesterday', []); * filtersPlugin.filter(); * * // add filter "Empty" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'empty', []); * filtersPlugin.filter(); * * // add filter "Ends with" with value "ing" to column at index 1 * filtersPlugin.addCondition(1, 'ends_with', ['ing']); * filtersPlugin.filter(); * * // add filter "Equal" with value "John" to column at index 1 * filtersPlugin.addCondition(1, 'eq', ['John']); * filtersPlugin.filter(); * * // add filter "Greater than" 95 to column at index 1 * filtersPlugin.addCondition(1, 'gt', [95]); * filtersPlugin.filter(); * * // add filter "Greater than or equal" 95 to column at index 1 * filtersPlugin.addCondition(1, 'gte', [95]); * filtersPlugin.filter(); * * // add filter "Less than" 10 to column at index 1 * filtersPlugin.addCondition(1, 'lt', [10]); * filtersPlugin.filter(); * * // add filter "Less than or equal" 10 to column at index 1 * filtersPlugin.addCondition(1, 'lte', [10]); * filtersPlugin.filter(); * * // add filter "None" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'none', []); * filtersPlugin.filter(); * * // add filter "Not between" 10 and 50 to column at index 1 * filtersPlugin.addCondition(1, 'not_between', [10, 50]); * filtersPlugin.filter(); * * // add filter "Not contains" with value "ing" to column at index 1 * filtersPlugin.addCondition(1, 'not_contains', ['ing']); * filtersPlugin.filter(); * * // add filter "Not empty" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'not_empty', []); * filtersPlugin.filter(); * * // add filter "Not equal" with value "John" to column at index 1 * filtersPlugin.addCondition(1, 'neq', ['John']); * filtersPlugin.filter(); * ``` * ::: * * ::: only-for react * ```jsx * const hotRef = useRef(null); * * ... * * * * // access to filters plugin instance * const hot = hotRef.current.hotInstance; * const filtersPlugin = hot.getPlugin('filters'); * * // add filter "Begins with" with value "de" to column at index 1 * filtersPlugin.addCondition(1, 'begins_with', ['de']); * filtersPlugin.filter(); * * // add filter "Between" 10 and 50 to column at index 1 * filtersPlugin.addCondition(1, 'between', [10, 50]); * filtersPlugin.filter(); * * // add filter "By value" to column at index 1 * // in this case all values that don't match will be filtered * filtersPlugin.addCondition(1, 'by_value', [['ing', 'ed', 'as', 'on']]); * filtersPlugin.filter(); * * // add filter "Contains" with value "ing" to column at index 1 * filtersPlugin.addCondition(1, 'contains', ['ing']); * filtersPlugin.filter(); * * // add filter "After a date" with value "1/1/2023" to column at index 1 * filtersPlugin.addCondition(1, 'date_after', ['1/1/2023']); * filtersPlugin.filter(); * * // add filter "Before a date" with value "1/1/2023" to column at index 1 * filtersPlugin.addCondition(1, 'date_before', ['1/1/2023']); * filtersPlugin.filter(); * * // add filter "Today" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'date_today', []); * filtersPlugin.filter(); * * // add filter "Tomorrow" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'date_tomorrow', []); * filtersPlugin.filter(); * * // add filter "Yesterday" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'date_yesterday', []); * filtersPlugin.filter(); * * // add filter "Empty" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'empty', []); * filtersPlugin.filter(); * * // add filter "Ends with" with value "ing" to column at index 1 * filtersPlugin.addCondition(1, 'ends_with', ['ing']); * filtersPlugin.filter(); * * // add filter "Equal" with value "John" to column at index 1 * filtersPlugin.addCondition(1, 'eq', ['John']); * filtersPlugin.filter(); * * // add filter "Greater than" 95 to column at index 1 * filtersPlugin.addCondition(1, 'gt', [95]); * filtersPlugin.filter(); * * // add filter "Greater than or equal" 95 to column at index 1 * filtersPlugin.addCondition(1, 'gte', [95]); * filtersPlugin.filter(); * * // add filter "Less than" 10 to column at index 1 * filtersPlugin.addCondition(1, 'lt', [10]); * filtersPlugin.filter(); * * // add filter "Less than or equal" 10 to column at index 1 * filtersPlugin.addCondition(1, 'lte', [10]); * filtersPlugin.filter(); * * // add filter "None" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'none', []); * filtersPlugin.filter(); * * // add filter "Not between" 10 and 50 to column at index 1 * filtersPlugin.addCondition(1, 'not_between', [10, 50]); * filtersPlugin.filter(); * * // add filter "Not contains" with value "ing" to column at index 1 * filtersPlugin.addCondition(1, 'not_contains', ['ing']); * filtersPlugin.filter(); * * // add filter "Not empty" with no arguments to column at index 1 * filtersPlugin.addCondition(1, 'not_empty', []); * filtersPlugin.filter(); * * // add filter "Not equal" with value "John" to column at index 1 * filtersPlugin.addCondition(1, 'neq', ['John']); * filtersPlugin.filter(); * ``` * ::: * * ::: only-for angular * ```ts * import { AfterViewInit, Component, ViewChild } from "@angular/core"; * import { * GridSettings, * HotTableModule, * HotTableComponent, * } from "@handsontable/angular-wrapper"; * * `@Component`({ * selector: "app-example", * standalone: true, * imports: [HotTableModule], * template: `
* *
`, * }) * export class ExampleComponent implements AfterViewInit { * `@ViewChild`(HotTableComponent, { static: false }) * readonly hotTable!: HotTableComponent; * * readonly gridSettings = { * data: this.getData(), * filters: true, * }; * * ngAfterViewInit(): void { * // Access to filters plugin instance * const hot = this.hotTable.hotInstance; * const filtersPlugin = hot.getPlugin("filters"); * * // Add filter "Begins with" with value "de" to column at index 1 * filtersPlugin.addCondition(1, "begins_with", ["de"]); * filtersPlugin.filter(); * * // Add filter "Between" 10 and 50 to column at index 1 * filtersPlugin.addCondition(1, "between", [10, 50]); * filtersPlugin.filter(); * * // Add filter "By value" to column at index 1 * // In this case, all values that don't match will be filtered. * filtersPlugin.addCondition(1, "by_value", [["ing", "ed", "as", "on"]]); * filtersPlugin.filter(); * * // Add filter "Contains" with value "ing" to column at index 1 * filtersPlugin.addCondition(1, "contains", ["ing"]); * filtersPlugin.filter(); * * // Add filter "After a date" with value "1/1/2023" to column at index 1 * filtersPlugin.addCondition(1, "date_after", ["1/1/2023"]); * filtersPlugin.filter(); * * // Add filter "Before a date" with value "1/1/2023" to column at index 1 * filtersPlugin.addCondition(1, "date_before", ["1/1/2023"]); * filtersPlugin.filter(); * * // Add filter "Today" with no arguments to column at index 1 * filtersPlugin.addCondition(1, "date_today", []); * filtersPlugin.filter(); * * // Add filter "Tomorrow" with no arguments to column at index 1 * filtersPlugin.addCondition(1, "date_tomorrow", []); * filtersPlugin.filter(); * * // Add filter "Yesterday" with no arguments to column at index 1 * filtersPlugin.addCondition(1, "date_yesterday", []); * filtersPlugin.filter(); * * // Add filter "Empty" with no arguments to column at index 1 * filtersPlugin.addCondition(1, "empty", []); * filtersPlugin.filter(); * * // Add filter "Ends with" with value "ing" to column at index 1 * filtersPlugin.addCondition(1, "ends_with", ["ing"]); * filtersPlugin.filter(); * * // Add filter "Equal" with value "John" to column at index 1 * filtersPlugin.addCondition(1, "eq", ["John"]); * filtersPlugin.filter(); * * // Add filter "Greater than" 95 to column at index 1 * filtersPlugin.addCondition(1, "gt", [95]); * filtersPlugin.filter(); * * // Add filter "Greater than or equal" 95 to column at index 1 * filtersPlugin.addCondition(1, "gte", [95]); * filtersPlugin.filter(); * * // Add filter "Less than" 10 to column at index 1 * filtersPlugin.addCondition(1, "lt", [10]); * filtersPlugin.filter(); * * // Add filter "Less than or equal" 10 to column at index 1 * filtersPlugin.addCondition(1, "lte", [10]); * filtersPlugin.filter(); * * // Add filter "None" with no arguments to column at index 1 * filtersPlugin.addCondition(1, "none", []); * filtersPlugin.filter(); * * // Add filter "Not between" 10 and 50 to column at index 1 * filtersPlugin.addCondition(1, "not_between", [10, 50]); * filtersPlugin.filter(); * * // Add filter "Not contains" with value "ing" to column at index 1 * filtersPlugin.addCondition(1, "not_contains", ["ing"]); * filtersPlugin.filter(); * * // Add filter "Not empty" with no arguments to column at index 1 * filtersPlugin.addCondition(1, "not_empty", []); * filtersPlugin.filter(); * * // Add filter "Not equal" with value "John" to column at index 1 * filtersPlugin.addCondition(1, "neq", ["John"]); * filtersPlugin.filter(); * } * * private getData(): Array<*> { * // Get some data * } * } * ``` * ::: * * @param {number} column Visual column index. * @param {string} name Condition short name. * @param {Array} args Condition arguments. The expected format depends on the condition - see the table above for details. * @param {string} [operationId=conjunction] `id` of operation which is performed on the column. */ addCondition(column: number, name: string, args: unknown[], operationId?: string): void; /** * Removes conditions at specified column index. * * @param {number} column Visual column index. */ removeConditions(column: number): void; /** * Clears all conditions previously added to the collection for the specified column index or, if the column index * was not passed, clear the conditions for all columns. * * @param {number} [column] Visual column index. */ clearConditions(column?: number): void; /** * Imports filter conditions to all columns to the plugin. The method accepts * the array of conditions with the same structure as the {@link Filters#exportConditions} method returns. * Importing conditions will replace the current conditions. Once replaced, the state of the condition * will be reflected in the UI. To apply the changes and filter the table, call * the {@link Filters#filter} method eventually. * * @param {Array} conditions Array of conditions. */ importConditions(conditions: ColumnConditions[]): void; /** * Exports filter conditions for all columns from the plugin. * The array represents the filter state for each column. For example: * * ```js * [ * { * column: 1, * operation: 'conjunction', * conditions: [ * { name: 'gt', args: [95] }, * ] * }, * { * column: 7, * operation: 'conjunction', * conditions: [ * { name: 'contains', args: ['mike'] }, * { name: 'begins_with', args: ['m'] }, * ] * }, * ] * ``` * * @returns {Array} */ exportConditions(): ColumnConditions[]; /** * Filters data based on added filter conditions. * * @fires Hooks#beforeFilter * @fires Hooks#afterFilter */ filter(): void; /** * Gets last selected column index. * * @returns {{visualIndex: number, physicalIndex: number} | null} Returns `null` when a column is * not selected. Otherwise, returns an object with `visualIndex` and `physicalIndex` properties containing * the index of the column. */ getSelectedColumn(): { physicalIndex: number; visualIndex: number; } | null; /** * Returns the full dataset for a column with cell meta for each row. The dataset is independent of * any index mapper - no matter if the data is filtered, sorted, or otherwise transformed all rows * are included. * * @param {number} physicalColumn The physical column index. * @returns {Array<{meta: CellProperties, value: *}>} Array of objects with `meta` and `value`, one per source row. */ getDataMapAtColumn(physicalColumn: number): Record[]; /** * Update the condition of ValueComponent, based on the handled changes. * * @private * @param {number} columnIndex Physical column index of handled ValueComponent condition. */ updateValueComponentCondition(columnIndex: number): void; /** * Restores components to its saved state. * * @private * @param {Array} components List of components. */ restoreComponents(components: BaseComponent[]): void; /** * Get an operation, based on the number and types of arguments (where arguments are states of components). * * @param {string} suggestedOperation Operation which was chosen by user from UI. * @param {object} byConditionState1 State of first condition component. * @param {object} byConditionState2 State of second condition component. * @param {object} byValueState State of value component. * @private * @returns {string} */ getOperationBasedOnArguments(suggestedOperation: string, byConditionState1: Record, byConditionState2: Record, byValueState: Record): string; /** * Listen to the keyboard input on document body and forward events to instance of Handsontable * created by DropdownMenu plugin. * * @private */ setListeningDropdownMenu(): void; /** * Updates visibility of some of the components, based on the state of the parent component. * * @private */ updateDependentComponentsVisibility(): void; /** * Creates DataFilter instance based on condition collection. * * @private * @param {ConditionCollection} conditionCollection Condition collection object. * @returns {DataFilter} */ _createDataFilter(conditionCollection?: ConditionCollection | null): DataFilter; /** * Returns indexes of passed components inside list of `dropdownMenu` items. * * @private * @param {...BaseComponent} components List of components. * @returns {Array} */ getIndexesOfComponents(...components: BaseComponent[]): number[]; /** * Changes visibility of component. * * @private * @param {boolean} visible Determine if components should be visible. * @param {...BaseComponent} components List of components. */ changeComponentsVisibility(visible?: boolean, ...components: BaseComponent[]): void; /** * Hides components of filters `dropdownMenu`. * * @private * @param {...BaseComponent} components List of components. */ hideComponents(...components: BaseComponent[]): void; /** * Shows components of filters `dropdownMenu`. * * @private * @param {...BaseComponent} components List of components. */ showComponents(...components: BaseComponent[]): void; /** * Destroys the plugin instance. */ destroy(): void; }