diff --git a/examples/webpack-demo-vanilla-bundle/src/app.ts b/examples/webpack-demo-vanilla-bundle/src/app.ts index 5de6b2304..0aeb43062 100644 --- a/examples/webpack-demo-vanilla-bundle/src/app.ts +++ b/examples/webpack-demo-vanilla-bundle/src/app.ts @@ -71,6 +71,7 @@ export class App { if (viewModel && viewModel.attached && this.renderer.className) { this.viewModelObj[this.renderer.className] = viewModel; viewModel.attached(); + this.dropdownToggle(); // rebind bulma dropdown toggle event handlers } // change browser's history state & title @@ -81,6 +82,27 @@ export class App { } } + /** bind bulma all dropdowns toggle event handlers */ + dropdownToggle() { + const $dropdowns = document.querySelectorAll('.dropdown:not(.is-hoverable)'); + + if ($dropdowns.length > 0) { + $dropdowns.forEach(($el) => { + $el.addEventListener('click', (event) => { + event.stopPropagation(); + $el.classList.toggle('is-active'); + }); + }); + + document.addEventListener('click', () => this.closeDropdowns()); + } + } + + closeDropdowns() { + const $dropdowns = document.querySelectorAll('.dropdown:not(.is-hoverable)'); + $dropdowns.forEach($el => $el.classList.remove('is-active')); + } + /** Add event listener for the navbar hamburger menu toggle when menu shows up on mobile */ navbarHamburgerToggle() { document.addEventListener('DOMContentLoaded', () => { diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example11.html b/examples/webpack-demo-vanilla-bundle/src/examples/example11.html index 37c1ba5c4..9333fd45e 100644 --- a/examples/webpack-demo-vanilla-bundle/src/examples/example11.html +++ b/examples/webpack-demo-vanilla-bundle/src/examples/example11.html @@ -3,30 +3,75 @@

(with Salesforce Theme)

-
-
- - - - - -
-
+
+
+
+ + + + + +
+
+ +
+
+ + + + + + +
+
+ +
+ @@ -34,4 +79,4 @@


-
\ No newline at end of file + diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example11.scss b/examples/webpack-demo-vanilla-bundle/src/examples/example11.scss index c20c9abff..70222b4a2 100644 --- a/examples/webpack-demo-vanilla-bundle/src/examples/example11.scss +++ b/examples/webpack-demo-vanilla-bundle/src/examples/example11.scss @@ -7,3 +7,10 @@ $autocomplete-max-height: 250px; .unsaved-editable-field { background-color: #fbfdd1 !important; } +.dropdown-item { + cursor: pointer; +} +.dropdown-item-disabled { + color: #afafaf; + cursor: default; +} diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example11.ts b/examples/webpack-demo-vanilla-bundle/src/examples/example11.ts index 861fe7819..a3369a516 100644 --- a/examples/webpack-demo-vanilla-bundle/src/examples/example11.ts +++ b/examples/webpack-demo-vanilla-bundle/src/examples/example11.ts @@ -1,12 +1,14 @@ import { AutocompleteOption, Column, + CurrentFilter, Editors, FieldType, Filters, Formatter, Formatters, GridOption, + OperatorType, SlickNamespace, SortComparers, @@ -16,6 +18,7 @@ import { } from '@slickgrid-universal/common'; import { ExcelExportService } from '@slickgrid-universal/excel-export'; import { Slicker, SlickerGridInstance, SlickVanillaGridBundle } from '@slickgrid-universal/vanilla-bundle'; +import * as moment from 'moment-mini'; import { ExampleGridOptions } from './example-grid-options'; import { loadComponent } from 'examples/utilities'; @@ -24,9 +27,10 @@ import './example11.scss'; // using external SlickGrid JS libraries declare const Slick: SlickNamespace; +const LOCAL_STORAGE_KEY = 'gridFilterPreset'; // you can create custom validator to pass to an inline editor -const myCustomTitleValidator = (value, args) => { +const myCustomTitleValidator = (value) => { if (value === null || value === undefined || !value.length) { return { valid: false, msg: 'This is a required field' }; } else if (!/^task\s\d+$/i.test(value)) { @@ -35,22 +39,55 @@ const myCustomTitleValidator = (value, args) => { return { valid: true, msg: '' }; }; -const customEditableInputFormatter = (row, cell, value, columnDef, dataContext, grid) => { +const customEditableInputFormatter = (_row, _cell, value, columnDef, _dataContext, grid) => { const gridOptions = grid && grid.getOptions && grid.getOptions(); const isEditableLine = gridOptions.editable && columnDef.editor; value = (value === null || value === undefined) ? '' : value; return isEditableLine ? { text: value, addClasses: 'editable-field', toolTip: 'Click to Edit' } : value; }; +export interface FilterPreset { + label: string; + value: string; + isSelected?: boolean; + isUserDefined?: boolean; + filters: CurrentFilter[]; +} + export class Example11 { columnDefinitions: Column[]; gridOptions: GridOption; dataset: any[] = []; + currentSelectedFilterPreset: FilterPreset; isGridEditable = true; + dropdownDeleteFilterClass = 'dropdown-item dropdown-item-disabled'; + dropdownUpdateFilterClass = 'dropdown-item dropdown-item-disabled'; editQueue = []; editedItems = {}; sgb: SlickVanillaGridBundle; gridContainerElm: HTMLDivElement; + currentYear = moment().year(); + defaultPredefinedPresets = [ + { + label: 'Tasks Finished in Previous Years', + value: 'previousYears', + isSelected: false, + isUserDefined: false, + filters: [ + { columnId: 'finish', operator: OperatorType.lessThanOrEqual, searchTerms: [`${this.currentYear}-01-01`] }, + { columnId: 'completed', operator: OperatorType.equal, searchTerms: [true], }, + { columnId: 'percentComplete', operator: OperatorType.greaterThan, searchTerms: [50] }, + ] as CurrentFilter[] + }, + { + label: 'Tasks Finishing in Future Years', + value: 'greaterCurrentYear', + isSelected: false, + isUserDefined: false, + filters: [{ columnId: 'finish', operator: '>=', searchTerms: [`${this.currentYear + 1}-01-01`] }] + } + ] as FilterPreset[]; + predefinedPresets = [...this.defaultPredefinedPresets]; get slickerGridInstance(): SlickerGridInstance { return this.sgb?.instances; @@ -66,6 +103,7 @@ export class Example11 { // bind any of the grid events this.gridContainerElm.addEventListener('onvalidationerror', this.handleValidationError.bind(this)); this.gridContainerElm.addEventListener('onitemdeleted', this.handleItemDeleted.bind(this)); + this.recreatePredefinedFilters(); } dispose() { @@ -83,7 +121,7 @@ export class Example11 { { id: 'duration', name: 'Duration', field: 'duration', sortable: true, filterable: true, editor: { model: Editors.float, massUpdate: true, decimal: 2, valueStep: 1, maxValue: 10000, alwaysSaveOnEnterKey: true, }, - formatter: (row, cell, value) => { + formatter: (_row, _cell, value) => { if (value === null || value === undefined) { return ''; } @@ -213,7 +251,7 @@ export class Example11 { itemVisibilityOverride: (args) => { return !args.dataContext.completed; }, - action: (event, args) => { + action: (_event, args) => { const dataContext = args.dataContext; if (confirm(`Do you really want to delete row (${args.row + 1}) with "${dataContext.title}"`)) { this.slickerGridInstance.gridService.deleteItemById(dataContext.id); @@ -304,6 +342,21 @@ export class Example11 { onCommand: (e, args) => this.executeCommand(e, args) } }; + + const filterPresets = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY) || null); + if (filterPresets) { + const presetFilter = filterPresets.find(preFilter => preFilter.isSelected); + this.predefinedPresets = filterPresets; + + if (presetFilter && presetFilter.filters) { + this.currentSelectedFilterPreset = presetFilter; + this.dropdownDeleteFilterClass = presetFilter.isUserDefined ? 'dropdown-item' : 'dropdown-item dropdown-item-disabled'; + this.dropdownUpdateFilterClass = this.dropdownDeleteFilterClass; + this.gridOptions.presets = { + filters: presetFilter.filters + }; + } + } } loadData(count: number) { @@ -324,9 +377,9 @@ export class Example11 { duration: Math.floor(Math.random() * 100) + 10, percentComplete: randomPercentComplete > 100 ? 100 : randomPercentComplete, start: new Date(randomYear, randomMonth, randomDay), - finish: (randomFinish < new Date() || i < 3) ? '' : randomFinish, // make sure the random date is earlier than today and it's index is bigger than 3 + finish: (i < 3) ? '' : randomFinish, // make sure the random date is earlier than today and it's index is bigger than 3 cost: (i % 33 === 0) ? null : Math.round(Math.random() * 10000) / 100, - completed: (i % 5 === 0), + completed: (randomFinish < new Date()), product: { id: this.mockProducts()[randomItemId]?.id, itemName: this.mockProducts()[randomItemId]?.itemName, }, countryOfOrigin: (i % 2) ? { code: 'CA', name: 'Canada' } : { code: 'US', name: 'United States' }, }; @@ -352,7 +405,7 @@ export class Example11 { console.log('item deleted with id:', itemId); } - async executeCommand(e, args) { + async executeCommand(_e, args) { const command = args.command; const dataContext = args.dataContext; @@ -449,7 +502,7 @@ export class Example11 { this.gridOptions = this.sgb.gridOptions; } - removeUnsavedStylingFromCell(item: any, column: Column, row: number) { + removeUnsavedStylingFromCell(_item: any, column: Column, row: number) { // remove unsaved css class from that cell this.sgb.slickGrid.removeCellCssStyles(`unsaved_highlight_${[column.field]}${row}`); } @@ -513,6 +566,125 @@ export class Example11 { this.editQueue = []; } + // -- + // PreDefined Filter Methods + // ----------------------------- + + pushNewFilterToSelectPreFilter(predefinedFilters: FilterPreset | FilterPreset[], isOptionSelected = false) { + if (isOptionSelected) { + this.resetPredefinedFilterSelection(this.predefinedPresets); + } + const presetFilters: FilterPreset[] = Array.isArray(predefinedFilters) ? predefinedFilters : [predefinedFilters]; + const filterSelect = document.querySelector('.selected-filter'); + + // empty an empty