azure-devops-ui
Version:
React components for building web UI in Azure DevOps
245 lines (244 loc) • 8.8 kB
JavaScript
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
});