diff --git a/cypress/integration/column.js b/cypress/integration/column.js index 94f7c97..bab33c4 100644 --- a/cypress/integration/column.js +++ b/cypress/integration/column.js @@ -1,5 +1,5 @@ describe('Column', function () { - before(function () { + beforeEach(function () { cy.visit('/'); }); @@ -68,48 +68,62 @@ describe('Column', function () { .and('match', /9\dpx/); }); - it('keeps sticky columns pinned while scrolling horizontally', function () { - const expectPinned = (actual, expected) => { - expect(actual).to.be.closeTo(expected, 1); - }; + it('pins a column from the dropdown menu', function () { + cy.clickDropdown(2); + cy.clickDropdownItem(2, 'Stick to left'); + + cy.window().then(win => win.datatable.getColumn(2)) + .its('sticky') + .should('eq', true); + + cy.get('.dt-scrollable').then(($scrollable) => { + const scrollable = $scrollable[0]; + const stickyBodyCell = Cypress.$('.dt-cell--2-0')[0]; + const initialStickyBodyLeft = stickyBodyCell.getBoundingClientRect().left; + + scrollable.scrollLeft = 220; + scrollable.dispatchEvent(new Event('scroll')); + + cy.wait(50).then(() => { + const nextStickyBodyLeft = stickyBodyCell.getBoundingClientRect().left; + expect(nextStickyBodyLeft).to.be.closeTo(initialStickyBodyLeft, 1); + }); + }); + }); + it('keeps sticky columns pinned while scrolling horizontally', function () { cy.get('.dt-scrollable').then(($scrollable) => { const scrollable = $scrollable[0]; - const stickyCheckboxBodyCell = Cypress.$('.dt-cell--0-0')[0]; - const stickyCheckboxHeaderCell = Cypress.$('.dt-cell--header-0')[0]; - const stickySerialBodyCell = Cypress.$('.dt-cell--1-0')[0]; - const stickySerialHeaderCell = Cypress.$('.dt-cell--header-1')[0]; - const stickyCustomBodyCell = Cypress.$('.dt-cell--2-0')[0]; - const stickyCustomHeaderCell = Cypress.$('.dt-cell--header-2')[0]; - const regularBodyCell = Cypress.$('.dt-cell--4-0')[0]; - - const initialStickyCheckboxBodyLeft = stickyCheckboxBodyCell.getBoundingClientRect().left; - const initialStickyCheckboxHeaderLeft = stickyCheckboxHeaderCell.getBoundingClientRect().left; - const initialStickySerialBodyLeft = stickySerialBodyCell.getBoundingClientRect().left; - const initialStickySerialHeaderLeft = stickySerialHeaderCell.getBoundingClientRect().left; - const initialStickyCustomBodyLeft = stickyCustomBodyCell.getBoundingClientRect().left; - const initialStickyCustomHeaderLeft = stickyCustomHeaderCell.getBoundingClientRect().left; - const initialRegularBodyLeft = regularBodyCell.getBoundingClientRect().left; + const checkboxBodyCell = Cypress.$('.dt-cell--0-0')[0]; + const checkboxHeaderCell = Cypress.$('.dt-cell--header-0')[0]; + const serialBodyCell = Cypress.$('.dt-cell--1-0')[0]; + const serialHeaderCell = Cypress.$('.dt-cell--header-1')[0]; + const officeBodyCell = Cypress.$('.dt-cell--4-0')[0]; + const officeHeaderCell = Cypress.$('.dt-cell--header-4')[0]; + const nameBodyCell = Cypress.$('.dt-cell--2-0')[0]; + + const initialCheckboxLeft = checkboxBodyCell.getBoundingClientRect().left; + const initialSerialLeft = serialBodyCell.getBoundingClientRect().left; + const initialNameLeft = nameBodyCell.getBoundingClientRect().left; scrollable.scrollLeft = 220; scrollable.dispatchEvent(new Event('scroll')); cy.wait(50).then(() => { - const nextStickyCheckboxBodyLeft = stickyCheckboxBodyCell.getBoundingClientRect().left; - const nextStickyCheckboxHeaderLeft = stickyCheckboxHeaderCell.getBoundingClientRect().left; - const nextStickySerialBodyLeft = stickySerialBodyCell.getBoundingClientRect().left; - const nextStickySerialHeaderLeft = stickySerialHeaderCell.getBoundingClientRect().left; - const nextStickyCustomBodyLeft = stickyCustomBodyCell.getBoundingClientRect().left; - const nextStickyCustomHeaderLeft = stickyCustomHeaderCell.getBoundingClientRect().left; - const nextRegularBodyLeft = regularBodyCell.getBoundingClientRect().left; - - expectPinned(nextStickyCheckboxBodyLeft, initialStickyCheckboxBodyLeft); - expectPinned(nextStickyCheckboxHeaderLeft, initialStickyCheckboxHeaderLeft); - expectPinned(nextStickySerialBodyLeft, initialStickySerialBodyLeft); - expectPinned(nextStickySerialHeaderLeft, initialStickySerialHeaderLeft); - expectPinned(nextStickyCustomBodyLeft, initialStickyCustomBodyLeft); - expectPinned(nextStickyCustomHeaderLeft, initialStickyCustomHeaderLeft); - expect(nextRegularBodyLeft).to.be.lessThan(initialRegularBodyLeft); + const nextCheckboxBodyLeft = checkboxBodyCell.getBoundingClientRect().left; + const nextCheckboxHeaderLeft = checkboxHeaderCell.getBoundingClientRect().left; + const nextSerialBodyLeft = serialBodyCell.getBoundingClientRect().left; + const nextSerialHeaderLeft = serialHeaderCell.getBoundingClientRect().left; + const nextOfficeBodyLeft = officeBodyCell.getBoundingClientRect().left; + const nextOfficeHeaderLeft = officeHeaderCell.getBoundingClientRect().left; + const nextNameLeft = nameBodyCell.getBoundingClientRect().left; + + expect(nextCheckboxBodyLeft).to.be.closeTo(initialCheckboxLeft, 1); + expect(nextSerialBodyLeft).to.be.closeTo(initialSerialLeft, 1); + expect(nextCheckboxHeaderLeft).to.be.closeTo(nextCheckboxBodyLeft, 1); + expect(nextSerialHeaderLeft).to.be.closeTo(nextSerialBodyLeft, 1); + expect(nextOfficeHeaderLeft).to.be.closeTo(nextOfficeBodyLeft, 1); + expect(nextNameLeft).to.be.lessThan(initialNameLeft); }); }); }); diff --git a/index.html b/index.html index bd545dd..a7868c6 100644 --- a/index.html +++ b/index.html @@ -151,9 +151,9 @@

Frappe DataTable

function buildData() { columns = [ - { name: "Name", width: 150, sticky: true }, - { name: "Position", width: 200 }, - { name: "Office", sticky: true }, + { name: "Name", width: 150}, + { name: "Position", width: 200}, + { name: "Office", sticky: true}, { name: "Extn." }, { name: "Start Date", diff --git a/src/columnmanager.js b/src/columnmanager.js index 93344da..d27d9ac 100644 --- a/src/columnmanager.js +++ b/src/columnmanager.js @@ -93,7 +93,7 @@ export default class ColumnManager { }); $.on(this.$dropdownList, 'click', '.dt-dropdown__list-item', (e, $item) => { - if (!this._dropdownActiveColIndex) return; + if (this._dropdownActiveColIndex == null) return; const dropdownItems = this.options.headerDropdown; const { index } = $.data($item); const colIndex = this._dropdownActiveColIndex; @@ -108,6 +108,11 @@ export default class ColumnManager { _this.hideDropdown(); } + this.stickDropdownIndex = this.options.headerDropdown + .findIndex(item => item.stickyAction === 'stick'); + this.unstickDropdownIndex = this.options.headerDropdown + .findIndex(item => item.stickyAction === 'unstick'); + this.hideDropdown(); } @@ -124,6 +129,7 @@ export default class ColumnManager { const $cell = $.closest('.dt-cell', e.target); const { colIndex } = $.data($cell); this._dropdownActiveColIndex = colIndex; + this.updateStickyDropdownItems(this.getColumn(colIndex)); } hideDropdown() { @@ -304,6 +310,20 @@ export default class ColumnManager { }); } + setColumnSticky(colIndex, sticky) { + const column = this.getColumn(colIndex); + if (!column || column.sticky === sticky) { + return; + } + + this.instance.freeze(); + this.datamanager.updateColumn(colIndex, { sticky }); + + this.refreshHeader(); + this.rowmanager.refreshRows() + .then(() => this.instance.unfreeze()); + } + switchColumn(oldIndex, newIndex) { this.instance.freeze(); this.datamanager.switchColumn(oldIndex, newIndex) @@ -493,4 +513,21 @@ export default class ColumnManager { toggleDropdownItem(index) { $('.dt-dropdown__list', this.instance.dropdownContainer).children[index].classList.toggle('dt-hidden'); } + + updateStickyDropdownItems(column) { + if (!column) return; + if (this.stickDropdownIndex === -1 || this.unstickDropdownIndex === -1) return; + + const stickItem = this.$dropdownList.children[this.stickDropdownIndex]; + const unstickItem = this.$dropdownList.children[this.unstickDropdownIndex]; + if (!(stickItem && unstickItem)) return; + + if (column.sticky) { + stickItem.classList.add('dt-hidden'); + unstickItem.classList.remove('dt-hidden'); + } else { + stickItem.classList.remove('dt-hidden'); + unstickItem.classList.add('dt-hidden'); + } + } } diff --git a/src/datatable.js b/src/datatable.js index c34ebc3..6d1f62a 100644 --- a/src/datatable.js +++ b/src/datatable.js @@ -222,6 +222,10 @@ class DataTable { this.columnmanager.removeColumn(colIndex); } + setColumnSticky(colIndex, sticky) { + this.columnmanager.setColumnSticky(colIndex, sticky); + } + scrollToLastColumn() { this.datatableWrapper.scrollLeft = 9999; } diff --git a/src/defaults.js b/src/defaults.js index 359f8e8..b815f8e 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -30,6 +30,22 @@ export default function getDefaultOptions(instance) { action: function (column) { this.removeColumn(column.colIndex); } + }, + { + label: instance.translate('Stick to left'), + stickyAction: 'stick', + display: 'hidden', + action: function (column) { + this.setColumnSticky(column.colIndex, true); + } + }, + { + label: instance.translate('Unstick from left'), + stickyAction: 'unstick', + display: 'hidden', + action: function (column) { + this.setColumnSticky(column.colIndex, false); + } } ], events: { diff --git a/src/translations/de.json b/src/translations/de.json index 0e667df..584e3af 100644 --- a/src/translations/de.json +++ b/src/translations/de.json @@ -3,6 +3,8 @@ "Sort Descending": "Absteigend sortieren", "Reset sorting": "Sortierung zurücksetzen", "Remove column": "Spalte entfernen", + "Stick to left": "Links anheften", + "Unstick from left": "Linke Anheftung lösen", "No Data": "Keine Daten", "{count} cells copied": { "1": "{count} Zelle kopiert", diff --git a/src/translations/en.json b/src/translations/en.json index 8029868..ce674a1 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3,6 +3,8 @@ "Sort Descending": "Sort Descending", "Reset sorting": "Reset sorting", "Remove column": "Remove column", + "Stick to left": "Stick to left", + "Unstick from left": "Unstick from left", "No Data": "No Data", "{count} cells copied": { "1": "{count} cell copied", diff --git a/src/translations/fr.json b/src/translations/fr.json index 194ec10..3540ef9 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -3,6 +3,8 @@ "Sort Descending": "Trier par ordre décroissant", "Reset sorting": "Réinitialiser le tri", "Remove column": "Supprimer colonne", + "Stick to left": "Épingler à gauche", + "Unstick from left": "Désépingler de la gauche", "No Data": "Pas de données", "{count} cells copied": { "1": "{count} cellule copiée", diff --git a/src/translations/it.json b/src/translations/it.json index a7308c1..2454edf 100644 --- a/src/translations/it.json +++ b/src/translations/it.json @@ -3,6 +3,8 @@ "Sort Descending": "Ordinamento decrescente", "Reset sorting": "Azzeramento ordinamento", "Remove column": "Rimuovi colonna", + "Stick to left": "Blocca a sinistra", + "Unstick from left": "Sblocca dalla sinistra", "No Data": "Nessun dato", "{count} cells copied": { "1": "Copiato {count} cella",