UNPKG

azure-devops-ui

Version:

React components for building web UI in Azure DevOps

245 lines (244 loc) 8.8 kB
import * as React from "react"; import { Observable } from '../Core/Observable'; export var FilterOperatorType; (function (FilterOperatorType) { /** * The filter's value should be treated as having the AND operator * e.g. If the value is ['a', 'c', 'd'] any item which does not contain * 'a' AND 'c' AND 'd' should not be included */ FilterOperatorType["and"] = "and"; /** * The filter's value should be treated as having the OR operator * e.g. If the value is ['a', 'c', 'd'] any item which contains * 'a' OR 'c' OR 'd' should be included */ FilterOperatorType["or"] = "or"; })(FilterOperatorType || (FilterOperatorType = {})); export const FILTER_CHANGE_EVENT = "filter-changed"; export const FILTER_APPLIED_EVENT = "filter-applied"; export const FILTER_RESET_EVENT = "reset-filters"; /** * Store for a set of filter values * * Events: * * FILTER_CHANGE_EVENT: IFilterState * Fired whenever a filter value changes. The event object contains the changed items. * When using explicit apply-mode, this event is fired when the filter is changed (i.e. by user action) * but not yet applied. * * FILTER_APPLIED_EVENT: IFilterState * Fired when new filter changes have been applied. The event object contains the changed items. * When not using explicit apply-mode, this event is fired on every state change */ export class Filter { /** * Create a new Filter store * @param options Options for the filter store */ constructor(options = {}, observable) { this.observable = observable || new Observable(); this.applyMode = !!options.useApplyMode; this.defaultState = Object.assign({}, options.defaultState); this.currentState = Object.assign({}, this.defaultState); this.customValueComparers = Object.assign({}, options.customValueComparers); if (this.applyMode) { this.appliedState = Object.assign({}, this.currentState); } else { this.appliedState = this.currentState; } } subscribe(observer, action) { return this.observable.subscribe(observer, action); } unsubscribe(observer, action) { this.observable.unsubscribe(observer, action); } /** * Gets a dictionary containing the current values of all filter items */ getState() { return Object.assign({}, this.currentState); } /** * Gets a dictionary containing the applied values of all filter items. * When useApplyMode is false, this always matches the value returned by getState. */ getAppliedState() { return Object.assign({}, this.appliedState); } /** * Gets a dictionary containing the default values of all filter items */ getDefaultState() { return this.defaultState; } /** * Update what the filter considers as its default state * @param defaultState The new default state */ setDefaultState(defaultState) { this.defaultState = defaultState; } /** * Sets the current values of all filter items * @param state Dictionary of all current filter item values * @param supressChangeEvent If true, don't invoke the onFilterChanged callback at this time */ setState(state, supressChangeEvent = false) { const prevState = this.currentState; this.currentState = Object.assign({}, state); if (!this.applyMode) { this.appliedState = this.currentState; } if (!supressChangeEvent) { const changedState = Object.assign({}, state); for (const key in prevState) { // Add current filters that are cleared by the new state // (i.e. these changed from "something" to "nothing") if (!state.hasOwnProperty(key)) { changedState[key] = null; } } this._triggerStateChange(changedState); } } /** * Gets the value of the specified filter item * @param key Filter item key */ getFilterItemState(key) { return this.currentState[key]; } /** * Gets the applied value of the specified filter item. When applyMode is false, * this is equivalent to getFilterItemState. * @param key Filter item key */ getAppliedFilterItemState(key) { return this.appliedState[key]; } /** * Gets the value property for the filter item with the given key. * @param key The filter item's key */ getFilterItemValue(key) { const item = this.currentState[key]; if (item) { return item.value; } else { return undefined; } } /** * Sets the value of the specified filter item * @param key Filter item key * @param value Filter value */ setFilterItemState(key, value) { this.currentState[key] = value; const changeEvent = {}; changeEvent[key] = value; this._triggerStateChange(changeEvent); } /** * Resets the filter values to their default state * @param suppressChangeEvent If true, don't invoke the onFilterChanged callback at this time */ reset(suppressChangeEvent = false) { this.setState(this.defaultState, suppressChangeEvent); if (!suppressChangeEvent) { this._raiseEventAndCallListeners(FILTER_RESET_EVENT, {}); } } resetFilterItemState(key) { const value = this.defaultState[key]; this.setFilterItemState(key, value); } applyChanges() { this.appliedState = Object.assign({}, this.currentState); this._raiseEventAndCallListeners(FILTER_APPLIED_EVENT, this.appliedState); } usesApplyMode() { return this.applyMode; } hasChangesToApply() { return !this.statesAreEqual(this.appliedState, this.currentState); } hasChangesToReset() { return !this.statesAreEqual(this.defaultState, this.currentState); } statesAreEqual(state1, state2) { return this._checkStateEquality(state1, state2) && this._checkStateEquality(state2, state1); } filterItemStatesAreEqual(item, state1, state2) { return this._checkFilterItemStateEquality(item, state1, state2); } _triggerStateChange(changedState) { this._raiseEventAndCallListeners(FILTER_CHANGE_EVENT, changedState); if (!this.applyMode) { this._raiseEventAndCallListeners(FILTER_APPLIED_EVENT, changedState); } } _raiseEventAndCallListeners(eventName, changedState) { this.observable.notify(changedState, eventName); } _checkStateEquality(state1, state2) { for (const item in state1) { const filterItemStateEqual = this._checkFilterItemStateEquality(item, state1[item], state2[item]); if (!filterItemStateEqual) { return false; } } return true; } _checkFilterItemStateEquality(item, item1State, item2State) { const item1Value = item1State && item1State.value; const item2Value = item2State && item2State.value; if (Array.isArray(item1Value)) { if (Array.isArray(item2Value)) { if (item1Value.length !== item2Value.length) { return false; } for (let index = 0; index < item1Value.length; index++) { if (!this._checkValueEquality(item, item1Value[index], item2Value[index])) { return false; } } } else { if (item1Value.length !== 0 || !!item2Value) { return false; } } } else if (!this._checkValueEquality(item, item1Value, item2Value)) { return false; } else if (Array.isArray(item2Value)) { if (item2Value.length !== 0) { return false; } } return true; } _checkValueEquality(key, item1, item2) { if (this.customValueComparers[key]) { return this.customValueComparers[key](item1, item2); } if (item1 && item1 !== item2) { return false; } else if (!!item1 !== !!item2) { return false; } return true; } } export const FilterContext = React.createContext({ filter: null, filterToggled: null });