import type { TableFilterCellProps } from '@servicetitan/design-system'; import { Checkbox } from '@servicetitan/design-system'; import { Component, Fragment, FormEvent } from 'react'; import { observer } from 'mobx-react'; import { observable, computed, runInAction, action, makeObservable } from 'mobx'; import { Input, InputChangeEvent } from '@progress/kendo-react-inputs'; import { DataSource, IdType } from '@servicetitan/data-query'; import { TableState } from '../table-state'; import { renderCustomColumnMenuFilter } from './column-menu-filters'; export function operator(item: T, value: T[]) { return value.includes(item); } export function fieldValuesColumnMenuFilter( tableState: TableState, renderItem: (value: T[keyof T]) => string = defaultRenderItem ) { interface Data { value: T[keyof T]; text: string; } @observer class FieldValuesFilterCell extends Component { @observable data: Data[] = []; @observable searchQuery = ''; @computed get processedData() { const searchQuery = this.searchQuery.toLowerCase(); const filteredData = !searchQuery ? this.data : this.data.filter(v => v.text.toLowerCase().includes(searchQuery)); return filteredData.slice().sort((a, b) => { if (a.text === b.text) { return 0; } return a.text < b.text ? -1 : 1; }); } get field() { const field = this.props.field; if (field === undefined) { throw 'missing field'; } return field as keyof T; } get value(): T[keyof T][] { return this.props.value || []; } constructor(props: TableFilterCellProps) { super(props); makeObservable(this); } @action handleSearchQueryChange = (ev: InputChangeEvent) => { if (typeof ev.target.value === 'string') { this.searchQuery = ev.target.value; } }; componentDidMount() { this.fetchData(tableState.dataSource); } handleChange = (value: Data, checked: boolean, ev: FormEvent) => { const newFilterValue = checked ? this.value.concat(value.value) : this.value.filter((v: T[keyof T]) => v !== value.value); const hasValue = newFilterValue.length > 0; this.props.onChange({ value: hasValue ? newFilterValue : '', operator: hasValue ? operator : '', syntheticEvent: ev, }); }; render() { return ( {this.processedData.map(item => ( ))} ); } private async fetchData(dataSource: DataSource | null) { if (!dataSource) { return; } const items = (await dataSource.getData({})).data as T[]; const values = items.map(i => i[this.field]); const distinctValues = values.filter((v, idx) => values.indexOf(v) === idx); runInAction(() => { this.data = distinctValues.map(value => ({ value, text: renderItem(value) })); }); } } return renderCustomColumnMenuFilter(FieldValuesFilterCell); } function defaultRenderItem(v: any): string { if (v == null) { return ''; } // suppose v has toString(); return v.toString(); }