diff --git a/vite-demo/src/example4.html b/vite-demo/src/example4.html
index 5e80ae16..8c2fe1ff 100644
--- a/vite-demo/src/example4.html
+++ b/vite-demo/src/example4.html
@@ -24,7 +24,10 @@
-
+
Load Data
+
+
+
Demonstrates:
diff --git a/vite-demo/src/example4.js b/vite-demo/src/example4.js
index be3f536d..432a6c3f 100644
--- a/vite-demo/src/example4.js
+++ b/vite-demo/src/example4.js
@@ -11,279 +11,275 @@ import {
Utils,
} from 'slickgrid';
-import Example4 from './example4.html?raw';
+import example4Html from './example4.html?raw';
import './example4.scss'
-/** render Example HTML code */
-export function render() {
- return Example4;
-}
-
-/** initialize Example JS code */
-export function init() {
- let dataView;
- let grid;
- let data = [];
- let columns = [
- { id: "sel", name: "#", field: "num", behavior: "select", cssClass: "cell-selection", width: 45, cannotTriggerInsert: true, resizable: false, selectable: false, excludeFromColumnPicker: true },
- { id: "title", name: "Title", field: "title", width: 110, minWidth: 100, cssClass: "cell-title", editor: Editors.LongText, validator: requiredFieldValidator, sortable: true },
- { id: "duration", name: "Duration", field: "duration", editor: Editors.Text, sortable: true },
- { id: "%", defaultSortAsc: false, name: "% Complete", field: "percentComplete", width: 95, formatter: Formatters.PercentCompleteBar, editor: Editors.PercentComplete, sortable: true },
- { id: "start", name: "Start", field: "start", minWidth: 60, editor: Editors.Flatpickr, sortable: true },
- { id: "finish", name: "Finish", field: "finish", minWidth: 60, editor: Editors.Flatpickr, sortable: true },
- { id: "effort-driven", name: "Effort Driven", width: 120, minWidth: 20, cssClass: "cell-effort-driven", field: "effortDriven", formatter: Formatters.Checkbox, editor: Editors.Checkbox, cannotTriggerInsert: true, sortable: true }
- ];
-
- let options = {
- columnPicker: {
- columnTitle: "Columns",
- hideForceFitButton: false,
- hideSyncResizeButton: false,
- forceFitTitle: "Force fit columns",
- syncResizeTitle: "Synchronous resize",
- },
- gridMenu: {
- iconCssClass: "sgi sgi-menu sgi-17px",
- columnTitle: "Columns",
- hideForceFitButton: false,
- hideSyncResizeButton: false,
- forceFitTitle: "Force fit columns",
- syncResizeTitle: "Synchronous resize",
- },
- editable: true,
- enableAddRow: true,
- enableCellNavigation: true,
- asyncEditorLoading: true,
- forceFitColumns: false,
- topPanelHeight: 35,
- rowHeight: 28
- };
-
- let sortcol = "title";
- let sortdir = 1;
- let percentCompleteThreshold = 0;
- let searchString = "";
-
- function toggleTheme(theme) {
- const gridElm = document.querySelector('#myGrid');
-
- if (theme === 'alpine') {
- changeCSS('../dist/styles/css/slick.grid.css', '../dist/styles/css/slick-alpine-theme.css');
- changeCSS('examples.css', '../dist/styles/css/example-demo.css');
- changeCSS('examples-unicode-icons.css', '../dist/styles/css/slick-icons.css');
- gridElm.classList.add('alpine-theme');
- gridElm.classList.remove('classic-theme');
- } else {
- changeCSS('../dist/styles/css/slick-alpine-theme.css', '../dist/styles/css/slick.grid.css');
- changeCSS('../dist/styles/css/example-demo.css', 'examples.css');
- changeCSS('../dist/styles/css/slick-icons.css', 'examples-unicode-icons.css');
- gridElm.classList.add('classic-theme');
- gridElm.classList.remove('alpine-theme');
- }
+export class Example4 {
+ dataView;
+ grid;
+ data;
+ columns;
+ options;
+ sortcol = 'title';
+ sortdir = 1;
+ percentCompleteThreshold = 0;
+ searchString = '';
+
+ /** render Example HTML code */
+ render() {
+ return example4Html;
}
- function changeCSS(prevFilePath, newFilePath) {
- let headerIndex = 0;
- let previousCssElm = document.getElementsByTagName("head")[0].querySelector(`link[href="${prevFilePath}"]`);
- if (previousCssElm) {
- previousCssElm.setAttribute('href', newFilePath);
- }
- }
+ /** initialize Example JS code */
+ init() {
+ this.dataView;
+ this.grid;
+ this.data = [];
+ this.columns = [
+ { id: "sel", name: "#", field: "num", behavior: "select", cssClass: "cell-selection", width: 45, cannotTriggerInsert: true, resizable: false, selectable: false, excludeFromColumnPicker: true },
+ { id: "title", name: "Title", field: "title", width: 110, minWidth: 100, cssClass: "cell-title", editor: Editors.LongText, validator: this.requiredFieldValidator, sortable: true },
+ { id: "duration", name: "Duration", field: "duration", editor: Editors.Text, sortable: true },
+ { id: "%", defaultSortAsc: false, name: "% Complete", field: "percentComplete", width: 95, formatter: Formatters.PercentCompleteBar, editor: Editors.PercentComplete, sortable: true },
+ { id: "start", name: "Start", field: "start", minWidth: 60, editor: Editors.Flatpickr, sortable: true },
+ { id: "finish", name: "Finish", field: "finish", minWidth: 60, editor: Editors.Flatpickr, sortable: true },
+ { id: "effort-driven", name: "Effort Driven", width: 120, minWidth: 20, cssClass: "cell-effort-driven", field: "effortDriven", formatter: Formatters.Checkbox, editor: Editors.Checkbox, cannotTriggerInsert: true, sortable: true }
+ ];
+
+ this.options = {
+ columnPicker: {
+ columnTitle: "Columns",
+ hideForceFitButton: false,
+ hideSyncResizeButton: false,
+ forceFitTitle: "Force fit columns",
+ syncResizeTitle: "Synchronous resize",
+ },
+ gridMenu: {
+ iconCssClass: "sgi sgi-menu sgi-17px",
+ columnTitle: "Columns",
+ hideForceFitButton: false,
+ hideSyncResizeButton: false,
+ forceFitTitle: "Force fit columns",
+ syncResizeTitle: "Synchronous resize",
+ },
+ editable: true,
+ enableAddRow: true,
+ enableCellNavigation: true,
+ asyncEditorLoading: true,
+ forceFitColumns: false,
+ topPanelHeight: 35,
+ rowHeight: 28
+ };
+
+ // get some mocked data
+ this.data = this.getData(50000);
+
+ // instantiate SlickGrid and SlickDataView
+ this.dataView = new SlickDataView({ inlineFilters: true });
+ this.grid = new SlickGrid("#myGrid", this.dataView, this.columns, this.options);
+ this.grid.setSelectionModel(new SlickRowSelectionModel());
+
+ // instantiate a few Controls
+ new SlickGridPager(this.dataView, this.grid, "#pager");
+ new SlickColumnPicker(this.columns, this.grid, this.options);
+ new SlickGridMenu(this.columns, this.grid, this.options);
+
+ // move the filter panel defined in a hidden div into grid top panel
+ let topPanel = this.grid.getTopPanel();
+ const topPanelLeftElm = document.querySelector("#inlineFilterPanel");
+ topPanel.appendChild(topPanelLeftElm);
+ topPanelLeftElm.style.display = 'block';
+
+ this.grid.onCellChange.subscribe((e, args) => {
+ this.dataView.updateItem(args.item.id, args.item);
+ });
- function requiredFieldValidator(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)) {
- return { valid: false, msg: 'Your title is invalid, it must start with "Task" followed by a number.' };
- }
- else {
- return { valid: true, msg: null };
- }
- }
+ this.grid.onAddNewRow.subscribe((e, args) => {
+ let item = { "num": this.data.length, "id": "new_" + (Math.round(Math.random() * 10000)), "title": "New task", "duration": "1 day", "percentComplete": 0, "start": "01/01/2009", "finish": "01/01/2009", "effortDriven": false };
+ Utils.extend(item, args.item);
+ this.dataView.addItem(item);
+ });
- function percentCompleteSort(a, b) {
- return a["percentComplete"] - b["percentComplete"];
- }
+ this.grid.onKeyDown.subscribe(function (e) {
+ // select all rows on ctrl-a
+ if (e.which != 65 || !e.ctrlKey) {
+ return false;
+ }
- function comparer(a, b) {
- let x = a[sortcol], y = b[sortcol];
- return (x == y ? 0 : (x > y ? 1 : -1));
- }
+ let rows = [];
+ for (let i = 0; i < this.dataView.getLength(); i++) {
+ rows.push(i);
+ }
- function toggleFilterRow() {
- grid.setTopPanelVisibility(!grid.getOptions().showTopPanel);
- }
+ this.grid.setSelectedRows(rows);
+ e.preventDefault();
+ });
- // prepare the data
- for (let i = 0; i < 50000; i++) {
- let d = (data[i] = {});
-
- d["id"] = "id_" + i;
- d["num"] = i;
- d["title"] = "Task " + i;
- d["duration"] = "5 days";
- d["percentComplete"] = Math.round(Math.random() * 100);
- d["start"] = "01/01/2009";
- d["finish"] = "01/05/2009";
- d["effortDriven"] = (i % 5 == 0);
- }
+ this.grid.onSort.subscribe((e, args) => {
+ this.sortdir = args.sortAsc ? 1 : -1;
+ this.sortcol = args.sortCol.field;
- // instantiate SlickGrid and SlickDataView
- dataView = new SlickDataView({ inlineFilters: true });
- grid = new SlickGrid("#myGrid", dataView, columns, options);
- grid.setSelectionModel(new SlickRowSelectionModel());
-
- let pager = new SlickGridPager(dataView, grid, "#pager");
- let columnpicker = new SlickColumnPicker(columns, grid, options);
- let gridMenu = new SlickGridMenu(columns, grid, options);
-
- // move the filter panel defined in a hidden div into grid top panel
- let topPanel = grid.getTopPanel();
- const topPanelLeftElm = document.querySelector("#inlineFilterPanel");
- topPanel.appendChild(topPanelLeftElm);
- topPanelLeftElm.style.display = 'block';
-
- grid.onCellChange.subscribe((e, args) => {
- dataView.updateItem(args.item.id, args.item);
- });
-
- grid.onAddNewRow.subscribe((e, args) => {
- let item = { "num": data.length, "id": "new_" + (Math.round(Math.random() * 10000)), "title": "New task", "duration": "1 day", "percentComplete": 0, "start": "01/01/2009", "finish": "01/01/2009", "effortDriven": false };
- Utils.extend(item, args.item);
- dataView.addItem(item);
- });
-
- grid.onKeyDown.subscribe(function (e) {
- // select all rows on ctrl-a
- if (e.which != 65 || !e.ctrlKey) {
- return false;
- }
+ // using native sort with comparer
+ this.dataView.sort(this.comparer.bind(this), args.sortAsc);
+ });
- let rows = [];
- for (let i = 0; i < dataView.getLength(); i++) {
- rows.push(i);
- }
+ this.grid.onBeforeEditCell.subscribe((e, args) => {
+ if (args.item.title === 'Task 18') {
+ return false;
+ }
+ });
- grid.setSelectedRows(rows);
- e.preventDefault();
- });
+ // wire up model events to drive the grid
+ // !! both this.dataView.onRowCountChanged and this.dataView.onRowsChanged MUST be wired to correctly update the grid
+ // see Issue#91
+ this.dataView.onRowCountChanged.subscribe((e, args) => {
+ this.grid.updateRowCount();
+ this.grid.render();
+ });
- grid.onSort.subscribe((e, args) => {
- sortdir = args.sortAsc ? 1 : -1;
- sortcol = args.sortCol.field;
+ this.dataView.onRowsChanged.subscribe((e, args) => {
+ this.grid.invalidateRows(args.rows);
+ this.grid.render();
+ });
- // using native sort with comparer
- dataView.sort(comparer, args.sortAsc);
- });
+ this.dataView.onPagingInfoChanged.subscribe((e, pagingInfo) => {
+ this.grid.updatePagingStatusFromView(pagingInfo);
+ // show the pagingInfo but remove the dataView from the object, just for the Cypress E2E test
+ delete pagingInfo.dataView;
+ console.log('on After Paging Info Changed - New Paging:: ', pagingInfo);
+ });
- grid.onBeforeEditCell.subscribe((e, args) => {
- if (args.item.title === 'Task 18') {
- return false;
- }
- });
-
- // wire up model events to drive the grid
- // !! both dataView.onRowCountChanged and dataView.onRowsChanged MUST be wired to correctly update the grid
- // see Issue#91
- dataView.onRowCountChanged.subscribe((e, args) => {
- grid.updateRowCount();
- grid.render();
- });
-
- dataView.onRowsChanged.subscribe((e, args) => {
- grid.invalidateRows(args.rows);
- grid.render();
- });
-
- dataView.onPagingInfoChanged.subscribe((e, pagingInfo) => {
- grid.updatePagingStatusFromView(pagingInfo);
- // show the pagingInfo but remove the dataView from the object, just for the Cypress E2E test
- delete pagingInfo.dataView;
- console.log('on After Paging Info Changed - New Paging:: ', pagingInfo);
- });
-
- dataView.onBeforePagingInfoChanged.subscribe((e, previousPagingInfo) => {
- // show the previous pagingInfo but remove the dataView from the object, just for the Cypress E2E test
- delete previousPagingInfo.dataView;
- console.log('on Before Paging Info Changed - Previous Paging:: ', previousPagingInfo);
- });
-
- let h_runfilters = null;
-
- // wire up the slider to apply the filter to the model
- let slider = document.getElementById("pcSlider");
- let slider2 = document.getElementById("pcSlider2");
- let sliderCallback = function () {
- SlickGlobalEditorLock.cancelCurrentEdit();
-
- if (percentCompleteThreshold != this.value) {
- window.clearTimeout(h_runfilters);
- h_runfilters = window.setTimeout(updateFilter, 10);
- percentCompleteThreshold = this.value;
+ this.dataView.onBeforePagingInfoChanged.subscribe((e, previousPagingInfo) => {
+ // show the previous pagingInfo but remove the dataView from the object, just for the Cypress E2E test
+ delete previousPagingInfo.dataView;
+ console.log('on Before Paging Info Changed - Previous Paging:: ', previousPagingInfo);
+ });
+
+ let h_runfilters = null;
+
+ // wire up the slider to apply the filter to the model
+ let slider = document.getElementById("pcSlider");
+ let slider2 = document.getElementById("pcSlider2");
+ let sliderCallback = (e) => {
+ const value = e.target.value;
+ SlickGlobalEditorLock.cancelCurrentEdit();
+ if (this.percentCompleteThreshold != value) {
+ window.clearTimeout(h_runfilters);
+ h_runfilters = window.setTimeout(this.updateFilter(), 10);
+ this.percentCompleteThreshold = value;
+ }
}
- }
- slider.oninput = sliderCallback;
- slider2.oninput = sliderCallback;
+ slider.oninput = sliderCallback.bind(this);
+ slider2.oninput = sliderCallback.bind(this);
+
+ // wire up the search textbox to apply the filter to the model
+ document.querySelectorAll("#txtSearch,#txtSearch2").forEach(elm => elm.addEventListener('keyup', (e) => {
+ SlickGlobalEditorLock.cancelCurrentEdit();
- // wire up the search textbox to apply the filter to the model
- document.querySelectorAll("#txtSearch,#txtSearch2").forEach(elm => elm.addEventListener('keyup', (e) => {
- SlickGlobalEditorLock.cancelCurrentEdit();
+ // clear on Esc
+ if (e.which == 27) e.target.value = '';
- // clear on Esc
- if (e.which == 27) e.target.value = '';
+ this.searchString = (e.target.value || '').trim();
+ this.updateFilter();
+ this.dataView.refresh();
+ }));
- searchString = (e.target.value || '').trim();
- updateFilter();
- dataView.refresh();
- }));
+ document.querySelector("#btnSelectRows").addEventListener('click', () => {
+ if (!SlickGlobalEditorLock.commitCurrentEdit()) {
+ return;
+ }
+ let rows = [];
- function updateFilter() {
- dataView.setFilterArgs({
- percentCompleteThreshold,
- searchString
+ for (let i = 0; i < 10 && i < this.dataView.getLength(); i++) {
+ rows.push(i);
+ }
+ this.grid.setSelectedRows(rows);
});
- dataView.refresh();
+
+ // initialize the model after all the events have been hooked up
+ this.dataView.beginUpdate();
+ this.dataView.setItems(this.data);
+ this.dataView.setFilterArgs({
+ percentCompleteThreshold: this.percentCompleteThreshold,
+ searchString: this.searchString
+ });
+ // `inlineFilters` can be a regular function or an ES6 arrow function like below
+ // this.dataView.setFilter((item, args) => item['percentComplete'] > args.percentCompleteThreshold);
+ this.dataView.setFilter((item, args) => {
+ if (item['percentComplete'] < args.percentCompleteThreshold) {
+ return false;
+ }
+
+ const searchString = args.searchString.toLowerCase();
+ if (args.searchString != '' && !item['title'].toLowerCase().includes(searchString)) {
+ return false;
+ }
+
+ return true;
+ });
+ this.dataView.endUpdate();
+
+ // if you don't want the items that are not visible (due to being filtered out
+ // or being on a different page) to stay selected, pass 'false' to the second arg
+ this.dataView.syncGridSelection(this.grid, true);
+
+ document.querySelector('.sgi-search').addEventListener('click', this.toggleFilterRow.bind(this));
+ document.querySelector('[data-test="add-50k-rows-btn"]').addEventListener('click', () => this.setData(this.getData(50000)));
+ document.querySelector('[data-test="add-500k-rows-btn"]').addEventListener('click', () => this.setData(this.getData(500000)));
+ document.querySelector('[data-test="add-1M-rows-btn"]').addEventListener('click', () => this.setData(this.getData(1000000)));
}
- document.querySelector("#btnSelectRows").addEventListener('click', () => {
- if (!SlickGlobalEditorLock.commitCurrentEdit()) {
- return;
- }
- let rows = [];
+ comparer(a, b) {
+ let x = a[this.sortcol], y = b[this.sortcol];
+ return (x == y ? 0 : (x > y ? 1 : -1));
+ }
- for (let i = 0; i < 10 && i < dataView.getLength(); i++) {
- rows.push(i);
- }
- grid.setSelectedRows(rows);
- });
-
- // initialize the model after all the events have been hooked up
- dataView.beginUpdate();
- dataView.setItems(data);
- dataView.setFilterArgs({
- percentCompleteThreshold,
- searchString
- });
- // `inlineFilters` can be a regular function or an ES6 arrow function like below
- // dataView.setFilter((item, args) => item['percentComplete'] > args.percentCompleteThreshold);
- dataView.setFilter((item, args) => {
- if (item['percentComplete'] < args.percentCompleteThreshold) {
- return false;
- }
+ setData(data) {
+ this.dataView.setItems(data);
+ }
- const searchString = args.searchString.toLowerCase();
- if (args.searchString != '' && !item['title'].toLowerCase().includes(searchString)) {
- return false;
+ getData(count) {
+ // prepare the data
+ let tmpData = [];
+ for (let i = 0; i < count; i++) {
+ let d = (tmpData[i] = {});
+
+ d["id"] = "id_" + i;
+ d["num"] = i;
+ d["title"] = "Task " + i;
+ d["duration"] = "5 days";
+ d["percentComplete"] = Math.round(Math.random() * 100);
+ d["start"] = "01/01/2009";
+ d["finish"] = "01/05/2009";
+ d["effortDriven"] = (i % 5 == 0);
}
+ return tmpData;
+ }
- return true;
- });
- dataView.endUpdate();
+ percentCompleteSort(a, b) {
+ return a["percentComplete"] - b["percentComplete"];
+ }
- // if you don't want the items that are not visible (due to being filtered out
- // or being on a different page) to stay selected, pass 'false' to the second arg
- dataView.syncGridSelection(grid, true);
+ requiredFieldValidator(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)) {
+ return { valid: false, msg: 'Your title is invalid, it must start with "Task" followed by a number.' };
+ }
+ else {
+ return { valid: true, msg: null };
+ }
+ }
- document.querySelector('.sgi-search').addEventListener('click', toggleFilterRow);
+ toggleFilterRow() {
+ this.grid.setTopPanelVisibility(!this.grid.getOptions().showTopPanel);
+ }
+
+ updateFilter() {
+ this.dataView.setFilterArgs({
+ percentCompleteThreshold: this.percentCompleteThreshold,
+ searchString: this.searchString
+ });
+ this.dataView.refresh();
+ }
}
diff --git a/vite-demo/src/main.js b/vite-demo/src/main.js
index ff4b3948..1f301363 100644
--- a/vite-demo/src/main.js
+++ b/vite-demo/src/main.js
@@ -2,12 +2,13 @@ import 'flatpickr';
import 'flatpickr/dist/flatpickr.css';
import Sortable from 'sortablejs';
-import { init, render } from './example4.js';
+import { Example4 } from './example4.js';
import './style.scss';
// assign SortableJS to the Window object
window.Sortable = Sortable;
// load example html, assign it to App and then init the JS code
-document.querySelector('#app').innerHTML = render();
-init();
\ No newline at end of file
+const demo4 = new Example4();
+document.querySelector('#app').innerHTML = demo4.render();
+demo4.init();
\ No newline at end of file