Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(filters): set date filter dynamically not always setting input value #1586

Merged
merged 2 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/vite-demo-vanilla-bundle/src/app-routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Example21 from './examples/example21';
import Example22 from './examples/example22';
import Example23 from './examples/example23';
import Example24 from './examples/example24';
import Example25 from './examples/example25';

export class AppRouting {
constructor(private config: RouterConfig) {
Expand Down Expand Up @@ -55,6 +56,7 @@ export class AppRouting {
{ route: 'example22', name: 'example22', view: './examples/example22.html', viewModel: Example22, title: 'Example22', },
{ route: 'example23', name: 'example23', view: './examples/example23.html', viewModel: Example23, title: 'Example23', },
{ route: 'example24', name: 'example24', view: './examples/example24.html', viewModel: Example24, title: 'Example24', },
{ route: 'example25', name: 'example25', view: './examples/example25.html', viewModel: Example25, title: 'Example25', },
{ route: '', redirect: 'example01' },
{ route: '**', redirect: 'example01' }
];
Expand Down
3 changes: 3 additions & 0 deletions examples/vite-demo-vanilla-bundle/src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ <h4 class="is-size-4 has-text-white">Slickgrid-Universal</h4>
<a class="navbar-item" onclick.delegate="loadRoute('example24')">
Example24 - Footer Totals Row
</a>
<a class="navbar-item" onclick.delegate="loadRoute('example25')">
Example25 - Range Filters
</a>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ <h3 class="title is-3">
</h3>

<div class="columns">
<div class="column is-6">
<div class="column is-12">
<button class="button is-small" onclick.delegate="toggleGridEditReadonly()" data-test="toggle-readonly-btn">
Toggle Edit/Readonly Grid
</button>
Expand Down
46 changes: 46 additions & 0 deletions examples/vite-demo-vanilla-bundle/src/examples/example25.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<h3 class="title is-3">
Example 25 - Range Filters

<div class="subtitle code-link">
<span class="is-size-6">see</span>
<a class="is-size-5" target="_blank"
href="https://github.com/ghiscoding/slickgrid-universal/blob/master/examples/vite-demo-vanilla-bundle/src/examples/example25.ts">
<span class="mdi mdi-link-variant"></span> code
</a>
</div>
</h3>

<span>
<b>Metrics:</b>
<span textcontent.bind="metricsEndTime"></span>
<span textcontent.bind="metricsItemCount"></span> of
<span textcontent.bind="metricsTotalItemCount"></span> items
</span>

<div class="row mb-3">
<button class="button is-small" data-test="clear-filters" onclick.delegate="clearFilters()">
Clear Filters
</button>
<button class="button is-small" data-test="clear-sorting" onclick.delegate="clearSorting()">
Clear Sorting
</button>
<button class="button is-small" data-test="set-dynamic-filter" onclick.delegate="setFiltersDynamically()">
Set Filters Dynamically
</button>
<button class="button is-small" data-test="set-dynamic-sorting" onclick.delegate="setSortingDynamically()">
Set Sorting Dynamically
</button>

<span class="ml-4">
<button class="button is-small" onclick.delegate="switchLanguage()" data-test="language-button">
<span class="mdi mdi-translate"></span>
<span>Switch Language</span>
</button>
<b>Locale:</b>
<span class="text-italic" data-test="selected-locale" textcontent.bind="selectedLanguageFile">
</span>
</span>
</div>

<div class="grid25">
</div>
283 changes: 283 additions & 0 deletions examples/vite-demo-vanilla-bundle/src/examples/example25.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
import { addDay, format } from '@formkit/tempo';

import { SlickCustomTooltip } from '@slickgrid-universal/custom-tooltip-plugin';
import { ExcelExportService } from '@slickgrid-universal/excel-export';
import { Slicker, type SlickVanillaGridBundle } from '@slickgrid-universal/vanilla-bundle';

import {
type Column,
type CurrentFilter,
FieldType,
Filters,
Formatters,
type GridOption,
type GridStateChange,
type MultipleSelectOption,
OperatorType,
type SliderRangeOption,
} from '@slickgrid-universal/common';
import { BindingEventService } from '@slickgrid-universal/binding';

import { ExampleGridOptions } from './example-grid-options';
import { type TranslateService } from '../translate.service';

const NB_ITEMS = 5000;

function randomBetween(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1) + min);
}

export default class Example25 {
private _bindingEventService: BindingEventService;
columnDefinitions: Column[] = [];
gridContainerElm: HTMLDivElement;
gridOptions!: GridOption;
dataset: any[] = [];
metricsEndTime = '';
metricsStartTime = '';
metricsItemCount = 0;
metricsTotalItemCount = 0;
filterList = [
{ value: '', label: '' },
{ value: 'currentYearTasks', label: 'Current Year Completed Tasks' },
{ value: 'nextYearTasks', label: 'Next Year Active Tasks' }
];
selectedLanguage = 'en';
selectedLanguageFile = 'en.json';
sgb: SlickVanillaGridBundle;
translateService: TranslateService;

constructor() {
this._bindingEventService = new BindingEventService();

// get the Translate Service from the window object,
// it might be better with proper Dependency Injection but this project doesn't have any at this point
this.translateService = (<any>window).TranslateService;
}

attached() {
// define the grid options & columns and then create the grid itself
this.defineGrid();

// mock some data (different in each dataset)
this.dataset = this.mockData(NB_ITEMS);
this.gridContainerElm = document.querySelector<HTMLDivElement>('.grid25') as HTMLDivElement;

this.sgb = new Slicker.GridBundle(this.gridContainerElm, this.columnDefinitions, { ...ExampleGridOptions, ...this.gridOptions }, this.dataset);

// bind any of the grid events
this._bindingEventService.bind(this.gridContainerElm, 'ongridstatechanged', this.gridStateChanged.bind(this));
this._bindingEventService.bind(this.gridContainerElm, 'onrowcountchanged', this.refreshMetrics.bind(this));

this.metricsEndTime = format(new Date(), 'DD MMM, h:mm:ss a');
this.metricsStartTime = format(new Date(), 'DD MMM, h:mm:ss a');
this.metricsItemCount = this.sgb.dataView?.getFilteredItemCount() || 0;
this.metricsTotalItemCount = this.dataset.length || 0;

document.body.classList.add('material-theme');
}

dispose() {
this.saveCurrentGridState();
document.body.classList.remove('material-theme');
}

/* Define grid Options and Columns */
defineGrid() {
this.columnDefinitions = [
{
id: 'title', name: 'Title', field: 'id', nameKey: 'TITLE', minWidth: 100,
formatter: (_row, _cell, value) => {
return this.translateService.translate('TASK_X', { x: value }) ?? '';
},
sortable: true,
filterable: true,
params: { useFormatterOuputToFilter: true }
},
{
id: 'description', name: 'Description', field: 'description', filterable: true, sortable: true, minWidth: 80,
type: FieldType.string,
},
{
id: 'percentComplete', name: '% Complete', field: 'percentComplete', nameKey: 'PERCENT_COMPLETE', minWidth: 120,
sortable: true,
customTooltip: { position: 'center' },
formatter: Formatters.progressBar,
type: FieldType.number,
filterable: true,
filter: {
model: Filters.sliderRange,
maxValue: 100, // or you can use the filterOptions as well
operator: OperatorType.rangeInclusive, // defaults to inclusive
filterOptions: {
hideSliderNumbers: false, // you can hide/show the slider numbers on both side
min: 0, step: 5
} as SliderRangeOption
}
},
{
id: 'start', name: 'Start', field: 'start', nameKey: 'START', formatter: Formatters.dateIso, sortable: true, minWidth: 75, width: 100, exportWithFormatter: true,
type: FieldType.date, filterable: true, filter: { model: Filters.compoundDate }
},
{
id: 'finish', name: 'Finish', field: 'finish', nameKey: 'FINISH', formatter: Formatters.dateIso, sortable: true, minWidth: 75, width: 120, exportWithFormatter: true,
type: FieldType.date,
filterable: true,
filter: {
model: Filters.dateRange,
}
},
{
id: 'duration', field: 'duration', nameKey: 'DURATION', maxWidth: 90,
type: FieldType.number,
sortable: true,
filterable: true, filter: {
model: Filters.input,
operator: OperatorType.rangeExclusive // defaults to exclusive
}
},
{
id: 'completed', name: 'Completed', field: 'completed', nameKey: 'COMPLETED', minWidth: 85, maxWidth: 90,
formatter: Formatters.checkmarkMaterial,
exportWithFormatter: true, // you can set this property in the column definition OR in the grid options, column def has priority over grid options
filterable: true,
filter: {
collection: [{ value: '', label: '' }, { value: true, label: 'True' }, { value: false, label: 'False' }],
model: Filters.singleSelect,
filterOptions: { autoAdjustDropHeight: true } as MultipleSelectOption
}
}
];

const today = new Date();
const presetLowestDay = format(addDay(new Date(), -2), 'YYYY-MM-DD');
const presetHighestDay = format(addDay(new Date(), today.getDate() < 14 ? 30 : 25), 'YYYY-MM-DD');

this.gridOptions = {
autoResize: {
container: '.demo-container',
},
enableExcelCopyBuffer: true,
enableFiltering: true,
enableTranslate: true,
translater: this.translateService, // pass the TranslateService instance to the grid

// use columnDef searchTerms OR use presets as shown below
presets: {
filters: [
// you can use the 2 dots separator on all Filters which support ranges
{ columnId: 'duration', searchTerms: ['4..88'] },
// { columnId: 'percentComplete', searchTerms: ['5..80'] }, // without operator will default to 'RangeExclusive'
// { columnId: 'finish', operator: 'RangeInclusive', searchTerms: [`${presetLowestDay}..${presetHighestDay}`] },

// or you could also use 2 searchTerms values, instead of using the 2 dots (only works with SliderRange & DateRange Filters)
// BUT make sure to provide the operator, else the filter service won't know that this is really a range
{ columnId: 'percentComplete', operator: 'RangeInclusive', searchTerms: [5, 80] }, // same result with searchTerms: ['5..80']
{ columnId: 'finish', operator: 'RangeInclusive', searchTerms: [presetLowestDay, presetHighestDay] },
],
sorters: [
{ columnId: 'percentComplete', direction: 'DESC' },
{ columnId: 'duration', direction: 'ASC' },
],
},
externalResources: [new SlickCustomTooltip(), new ExcelExportService()],
};
}

mockData(itemCount: number, startingIndex = 0): any[] {
// mock a dataset
const tempDataset: any[] = [];
for (let i = startingIndex; i < (startingIndex + itemCount); i++) {
const randomDuration = randomBetween(0, 365);
const randomYear = randomBetween(new Date().getFullYear(), new Date().getFullYear() + 1);
const randomMonth = randomBetween(0, 12);
const randomDay = randomBetween(10, 28);
const randomPercent = randomBetween(0, 100);

tempDataset.push({
id: i,
title: 'Task ' + i,
description: (i % 5) ? 'desc ' + i : null, // also add some random to test NULL field
duration: randomDuration,
percentComplete: randomPercent,
percentCompleteNumber: randomPercent,
start: (i % 4) ? null : new Date(randomYear, randomMonth, randomDay), // provide a Date format
finish: new Date(randomYear, randomMonth, randomDay),
completed: (randomPercent === 100) ? true : false,
});
}

return tempDataset;
}

clearFilters() {
this.sgb?.filterService.clearFilters();
}

/** Dispatched event of a Grid State Changed event */
gridStateChanged(event) {
if (event?.detail) {
console.log('Client sample, Grid State changed:: ', event.detail as GridStateChange);
}
}

/** Save current Filters, Sorters in LocaleStorage or DB */
saveCurrentGridState() {
console.log('Client sample, current Grid State:: ', this.sgb?.gridStateService.getCurrentGridState());
}

refreshMetrics(event) {
const args = event?.detail?.args;
if (args?.current >= 0) {
this.metricsStartTime = format(new Date(), 'DD MMM, h:mm:ss a');
this.metricsItemCount = args?.current || 0;
this.metricsTotalItemCount = this.dataset.length || 0;
}
}

setFiltersDynamically() {
const presetLowestDay = format(addDay(new Date(), -5), 'YYYY-MM-DD');
const presetHighestDay = format(addDay(new Date(), 25), 'YYYY-MM-DD');

// we can Set Filters Dynamically (or different filters) afterward through the FilterService
this.sgb.filterService.updateFilters([
{ columnId: 'duration', searchTerms: ['14..78'], operator: 'RangeInclusive' },
{ columnId: 'percentComplete', operator: 'RangeExclusive', searchTerms: [15, 85] },
{ columnId: 'start', operator: '<=', searchTerms: [presetHighestDay] },
{ columnId: 'finish', operator: 'RangeInclusive', searchTerms: [presetLowestDay, presetHighestDay] },
]);
}

setSortingDynamically() {
this.sgb?.sortService.updateSorting([
// orders matter, whichever is first in array will be the first sorted column
{ columnId: 'finish', direction: 'DESC' },
{ columnId: 'percentComplete', direction: 'ASC' },
]);
}

predefinedFilterChanged(newPredefinedFilter: string) {
let filters: CurrentFilter[] = [];
const currentYear = new Date().getFullYear();

switch (newPredefinedFilter) {
case 'currentYearTasks':
filters = [
{ columnId: 'finish', operator: OperatorType.rangeInclusive, searchTerms: [`${currentYear}-01-01`, `${currentYear}-12-31`] },
{ columnId: 'completed', operator: OperatorType.equal, searchTerms: [true] },
];
break;
case 'nextYearTasks':
filters = [{ columnId: 'start', operator: '>=', searchTerms: [`${currentYear + 1}-01-01`] }];
break;
}
this.sgb?.filterService.updateFilters(filters);
}

async switchLanguage() {
const nextLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en';
await this.translateService.use(nextLanguage);
this.selectedLanguage = nextLanguage;
this.selectedLanguageFile = `${this.selectedLanguage}.json`;
}
}
1 change: 0 additions & 1 deletion packages/common/src/filters/dateFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,6 @@ export class DateFilter implements Filter {
if (this.calendarInstance && pickerValues !== undefined) {
setPickerDates(this.columnFilter, this._dateInputElm, this.calendarInstance, {
columnDef: this.columnDef,
oldVal: this._currentDateOrDates,
newVal: pickerValues,
updatePickerUI: true
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ export class SlickVanillaGridBundle<TData = any> {
) {
// make sure that the grid container doesn't already have the "slickgrid-container" css class
// if it does then we won't create yet another grid, just stop there
if (gridParentContainerElm.querySelectorAll('.slickgrid-container').length !== 0) {
if (!gridParentContainerElm || gridParentContainerElm.querySelectorAll('.slickgrid-container').length !== 0) {
return;
}

Expand Down
Loading
Loading