diff --git a/cypress/integration/column.js b/cypress/integration/column.js index 3467e8e5..c9a3bbde 100644 --- a/cypress/integration/column.js +++ b/cypress/integration/column.js @@ -29,7 +29,7 @@ describe('Column', function () { .its('currentSort.colIndex') .should('eq', 2); - cy.get('.dt-scrollable .dt-row:first div:nth-of-type(3)') + cy.get('.dt-scrollable .dt-body .dt-row:first div:nth-of-type(3)') .contains('Airi Satou'); cy.clickDropdownItem(2, 'Reset sorting'); @@ -63,8 +63,8 @@ describe('Column', function () { it('resize column using double click', function () { cy.get('.dt-cell--header-4 .dt-cell__resize-handle').trigger('dblclick'); cy.getColumnCell(4).should('have.css', 'width') - .and('match', /9\dpx/); + .and('match', /10\dpx/); cy.getCell(4, 1).should('have.css', 'width') - .and('match', /9\dpx/); + .and('match', /10\dpx/); }); }); diff --git a/cypress/integration/inline_filters.js b/cypress/integration/inline_filters.js index c18e94a2..8964a558 100644 --- a/cypress/integration/inline_filters.js +++ b/cypress/integration/inline_filters.js @@ -81,7 +81,7 @@ describe('Inline Filters', function () { cy.getCell(5, 24).click().type('{ctrl}f'); cy.get('@filterInput5').type('>3000', {delay: 100}); - cy.get('.dt-scrollable .dt-row:first') + cy.get('.dt-body .dt-row:first') .should('contain', 'Angelica') .should('have.class', 'dt-row-24'); cy.get('@filterInput5').clear(); diff --git a/cypress/integration/row.js b/cypress/integration/row.js index 1278e79c..9cd7fcdd 100644 --- a/cypress/integration/row.js +++ b/cypress/integration/row.js @@ -4,7 +4,7 @@ describe('Row', function () { }); it('check / uncheck row', function () { - cy.get('.dt-scrollable .dt-row:first') + cy.get('.dt-body .dt-row:first') .find('input[type="checkbox"]') .click(); diff --git a/index.html b/index.html index bd545dda..58d69e32 100644 --- a/index.html +++ b/index.html @@ -151,9 +151,9 @@

Frappe DataTable

function buildData() { columns = [ - { name: "Name", width: 150, sticky: true }, + { name: "Name", width: 150 }, { name: "Position", width: 200 }, - { name: "Office", sticky: true }, + { name: "Office" }, { name: "Extn." }, { name: "Start Date", diff --git a/src/body-renderer.js b/src/body-renderer.js index 7a26486f..ea65d68c 100644 --- a/src/body-renderer.js +++ b/src/body-renderer.js @@ -7,7 +7,9 @@ export default class BodyRenderer { this.rowmanager = instance.rowmanager; this.cellmanager = instance.cellmanager; this.bodyScrollable = instance.bodyScrollable; + this.bodyContainer = instance.bodyContainer; this.footer = this.instance.footer; + this.header = this.instance.header; this.log = instance.log; } @@ -32,11 +34,7 @@ export default class BodyRenderer { return null; }).filter(index => index !== null); - const computedStyle = getComputedStyle(this.bodyScrollable); - let config = { - width: computedStyle.width, - height: computedStyle.height, itemHeight: this.options.cellHeight, total: rows.length, generate: (index) => { @@ -53,9 +51,9 @@ export default class BodyRenderer { }; if (!this.hyperlist) { - this.hyperlist = new HyperList(this.bodyScrollable, config); + this.hyperlist = new HyperList(this.bodyContainer, config); } else { - this.hyperlist.refresh(this.bodyScrollable, config); + this.hyperlist.refresh(this.bodyContainer, config); } this.renderFooter(); @@ -123,6 +121,24 @@ export default class BodyRenderer { this.rowmanager.highlightCheckedRows(); this.cellmanager.selectAreaOnClusterChanged(); this.cellmanager.focusCellOnClusterChanged(); + this.bodyContainer.style.removeProperty('overflow'); + this.setbodyWidth(); + } + + setbodyWidth() { + // change width of dt-body once the all operations are done + setTimeout(() => { + const computedStyle = getComputedStyle(this.bodyScrollable); + + const cells = this.header.querySelectorAll('.dt-cell--header'); + let totalColWidth = 0; + cells.forEach((cell, index) => { + totalColWidth += cell.clientWidth + 1; + }); + this.bodyContainer.style.width = `${totalColWidth + 5}px`; + this.bodyContainer.style.height = `calc(${computedStyle.height} - 80px)`; + this.bodyScrollable.style.height = computedStyle.height; + }, 100); } showToastMessage(message, hideAfter) { diff --git a/src/cellmanager.js b/src/cellmanager.js index bf312171..ea3d8af1 100644 --- a/src/cellmanager.js +++ b/src/cellmanager.js @@ -17,6 +17,7 @@ export default class CellManager { 'style', 'header', 'bodyScrollable', + 'bodyContainer', 'columnmanager', 'rowmanager', 'datamanager', @@ -25,8 +26,6 @@ export default class CellManager { ]); this.bindEvents(); - this.stickyRowWidth = 0; - this.stickyColWitdh = []; } bindEvents() { @@ -45,7 +44,7 @@ export default class CellManager { bindEditCell() { this.$editingCell = null; - $.on(this.bodyScrollable, 'dblclick', '.dt-cell', (e, cell) => { + $.on(this.bodyContainer, 'dblclick', '.dt-cell', (e, cell) => { this.activateEditing(cell); }); @@ -102,6 +101,7 @@ export default class CellManager { const $cell = $.closest('.dt-cell', e.target); const { colIndex } = $.data($cell); + console.log(colIndex, "colIndex", colIndex); this.activateFilter(colIndex); return true; }); @@ -166,12 +166,12 @@ export default class CellManager { bindMouseEvents() { let mouseDown = null; - $.on(this.bodyScrollable, 'mousedown', '.dt-cell', (e) => { + $.on(this.bodyContainer, 'mousedown', '.dt-cell', (e) => { mouseDown = true; this.focusCell($(e.delegatedTarget)); }); - $.on(this.bodyScrollable, 'mouseup', () => { + $.on(this.bodyContainer, 'mouseup', () => { mouseDown = false; }); @@ -188,11 +188,11 @@ export default class CellManager { this.selectArea($(e.delegatedTarget)); }; - $.on(this.bodyScrollable, 'mousemove', '.dt-cell', throttle(selectArea, 50)); + $.on(this.bodyContainer, 'mousemove', '.dt-cell', throttle(selectArea, 50)); } bindTreeEvents() { - $.on(this.bodyScrollable, 'click', '.dt-tree-node__toggle', (e, $toggle) => { + $.on(this.bodyContainer, 'click', '.dt-tree-node__toggle', (e, $toggle) => { const $cell = $.closest('.dt-cell', $toggle); const { rowIndex } = $.data($cell); @@ -648,7 +648,7 @@ export default class CellManager { } refreshCell(cell, refreshHtml = false) { - const $cell = $(this.selector(cell.colIndex, cell.rowIndex), this.bodyScrollable); + const $cell = $(this.selector(cell.colIndex, cell.rowIndex), this.bodyContainer); $cell.innerHTML = this.getCellContent(cell, refreshHtml); } @@ -709,7 +709,7 @@ export default class CellManager { } getCell$(colIndex, rowIndex) { - return $(this.selector(colIndex, rowIndex), this.bodyScrollable); + return $(this.selector(colIndex, rowIndex), this.bodyContainer); } getAboveCell$($cell) { @@ -769,11 +769,11 @@ export default class CellManager { } getRowHeight() { - return $.style($('.dt-row', this.bodyScrollable), 'height'); + return $.style($('.dt-row', this.bodyContainer), 'height'); } scrollToCell($cell) { - if ($.inViewport($cell, this.bodyScrollable) || $.inViewport($cell, this.footer)) return false; + if ($.inViewport($cell, this.bodyContainer) || $.inViewport($cell, this.footer)) return false; const { rowIndex @@ -802,44 +802,10 @@ export default class CellManager { isTotalRow }); - let styles = ''; - const row = this.datamanager.getRow(rowIndex); const isBodyCell = !(isHeader || isFilter || isTotalRow); - const serialNoColIndex = !this.options.checkboxColumn && this.options.serialNoColumn ? 0 : 1; - - let sticky = false; - - let checkboxserialNoclass = ''; - - if (colIndex === 0 && this.options.checkboxColumn) { - if (cell.isHeader && !(cell.id in this.stickyColWitdh)) this.stickyRowWidth = 34; - checkboxserialNoclass = 'dt-cell-checkbox'; - sticky = true; - } else if (colIndex === serialNoColIndex && this.options.serialNoColumn) { - if (cell.isHeader && !(cell.id in this.stickyColWitdh)) { - this.stickyColWitdh[cell.id] = this.stickyRowWidth; - this.stickyRowWidth += (cell.width || 37); - checkboxserialNoclass = 'dt-cell-serial-no'; - } - styles = `left:${this.stickyColWitdh[isBodyCell ? cell.column.id : cell.id]}px;`; - sticky = true; - - } else if (cell.sticky) { - if (cell.isHeader && !(cell.id in this.stickyColWitdh)) { - this.stickyColWitdh[cell.id] = this.stickyRowWidth; - this.stickyRowWidth += ((cell.width || 100) + 1); - } - styles = `left:${this.stickyColWitdh[cell.id]}px;`; - sticky = true; - - } else if ((isBodyCell || isTotalRow) && cell.column.sticky) { - styles = `left:${this.stickyColWitdh[cell.column.id]}px;`; - sticky = true; - } - const className = [ 'dt-cell', 'dt-cell--col-' + colIndex, @@ -849,12 +815,10 @@ export default class CellManager { isHeader ? `dt-cell--header-${colIndex}` : '', isFilter ? 'dt-cell--filter' : '', isBodyCell && (row && row.meta.isTreeNodeClose) ? 'dt-cell--tree-close' : '', - sticky ? 'dt-sticky-col' : '', - checkboxserialNoclass, ].join(' '); return ` -
+
${this.getCellContent(cell)}
`; diff --git a/src/columnmanager.js b/src/columnmanager.js index fabfbe72..83309944 100644 --- a/src/columnmanager.js +++ b/src/columnmanager.js @@ -166,6 +166,7 @@ export default class ColumnManager { this.setColumnWidth(colIndex); this.style.setBodyStyle(); $resizingCell = null; + this.bodyRenderer.setbodyWidth(); }; $.on(document.body, 'mouseup', onMouseup); this.instance.on('onDestroy', () => { @@ -270,6 +271,11 @@ export default class ColumnManager { }); } + pinColumn(colIndex) { + this.instance.freeze(); + this.instance.unfreeze(); + } + removeColumn(colIndex) { const removedCol = this.getColumn(colIndex); this.instance.freeze(); diff --git a/src/datatable.js b/src/datatable.js index b2655117..314fa4d6 100644 --- a/src/datatable.js +++ b/src/datatable.js @@ -110,9 +110,11 @@ class DataTable { this.wrapper.innerHTML = `
-
-
- +
+
+
+ +
@@ -127,6 +129,7 @@ class DataTable { this.datatableWrapper = $('.datatable', this.wrapper); this.header = $('.dt-header', this.wrapper); + this.bodyContainer = $('.dt-body', this.wrapper); this.footer = $('.dt-footer', this.wrapper); this.bodyScrollable = $('.dt-scrollable', this.wrapper); this.freezeContainer = $('.dt-freeze', this.wrapper); @@ -213,6 +216,10 @@ class DataTable { this.columnmanager.sortColumn(colIndex, sortOrder); } + pinColumn(colIndex) { + this.columnmanager.pinColumn(colIndex); + } + removeColumn(colIndex) { this.columnmanager.removeColumn(colIndex); } diff --git a/src/defaults.js b/src/defaults.js index 359f8e8a..8c453e9d 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -25,6 +25,12 @@ export default function getDefaultOptions(instance) { this.sortColumn(column.colIndex, 'none'); } }, + // { + // label: instance.translate('Pin column'), + // action: function (column) { + // this.pinColumn(column.colIndex); + // } + // }, { label: instance.translate('Remove column'), action: function (column) { diff --git a/src/style.css b/src/style.css index 673ad6cc..e931924a 100644 --- a/src/style.css +++ b/src/style.css @@ -33,18 +33,28 @@ } } -.dt-header{ - border-bottom: 2px solid var(--dt-border-color); +.dt-header { + top: 0; } -.datatable-content{ - .dt-header{ - display: flex; - } +.dt-header, +.dt-footer { + position: sticky; + background: white; +} + +.dt-footer { + bottom: 0; +} +.dt-body { + overflow-y: auto; + overflow-x: clip; } .dt-scrollable { height: 40vw; + border-top: 2px solid var(--dt-border-color); + overflow-y: hidden; &--highlight-all { background-color: var(--dt-selection-highlight-color); @@ -81,7 +91,7 @@ display: none; } - &:last-child:not(.dt-row-filter) { + &:last-child:not(.dt-row-filter) .dt-cell { border-bottom: 1px solid var(--dt-border-color); } } @@ -154,6 +164,7 @@ &--header { background-color: var(--dt-header-cell-bg); + border-bottom: 2px solid var(--dt-border-color); } &--header:last-child { @@ -309,9 +320,4 @@ body.dt-resize { cursor: col-resize; -} -.dt-sticky-col { - position: sticky; - z-index: 1; - left: 0; } \ No newline at end of file diff --git a/src/style.js b/src/style.js index 0ec37d80..a82681bc 100644 --- a/src/style.js +++ b/src/style.js @@ -23,7 +23,6 @@ export default class Style { this.styleEl = styleEl; this.bindResizeWindow(); - this.bindScrollHeader(); } get stylesheet() { @@ -39,66 +38,6 @@ export default class Style { } } - bindScrollHeader() { - this._settingHeaderPosition = false; - - $.on(this.bodyScrollable, 'scroll', (e) => { - - if (this._settingHeaderPosition) return; - - this._settingHeaderPosition = true; - - requestAnimationFrame(() => { - - const scrollLeft = e.target.scrollLeft; - - // Move non-sticky header and footer cells normally - const nonStickyHeaderCells = this.header.querySelectorAll('.dt-cell:not(.dt-sticky-col)'); - const nonStickyFooterCells = this.footer.querySelectorAll('.dt-cell:not(.dt-sticky-col)'); - - nonStickyHeaderCells.forEach(cell => { - $.style(cell, { transform: `translateX(${-scrollLeft}px)` }); - }); - - nonStickyFooterCells.forEach(cell => { - $.style(cell, { transform: `translateX(${-scrollLeft}px)` }); - }); - - const stickyHeaderCells = this.header.querySelectorAll( - '.dt-cell.dt-sticky-col:not(.dt-cell-serial-no):not(.dt-cell-checkbox)' - ); - - stickyHeaderCells.forEach((headerCell) => { - - const colIndex = headerCell.getAttribute('data-col-index'); - const bodyCell = this.bodyScrollable.querySelector(`.dt-cell[data-col-index="${colIndex}"]`); - const colLeft = parseFloat(headerCell.style.left) || 0; // get left position of the column - - // Find corresponding footer cell - const footerCell = this.footer.querySelector(`.dt-cell[data-col-index="${colIndex}"]`); - - if (bodyCell && ~~(bodyCell.offsetLeft - scrollLeft) > colLeft) { - headerCell.style.transform = `translateX(${-scrollLeft - 1}px)`; - if (footerCell) { - footerCell.style.transform = `translateX(${scrollLeft ? -scrollLeft - 1 : 0}px)`; - } - } else { - headerCell.style.transform = `translateX(${colLeft - headerCell.offsetLeft}px)`; - if (footerCell) footerCell.style.transform = `translateX(${colLeft - footerCell.offsetLeft}px)`; - } - }); - - this._settingHeaderPosition = false; - if (this.instance.noData) { - $.style($('.no-data-message'), { - left: `${this.instance.wrapper.clientWidth / 2 - (-scrollLeft)}px` - }); - } - this._settingHeaderPosition = false; - }); - }); - } - onWindowResize() { this.distributeRemainingWidth(); this.refreshColumnWidth(); diff --git a/src/translations/de.json b/src/translations/de.json index 0e667df5..1a74b4c3 100644 --- a/src/translations/de.json +++ b/src/translations/de.json @@ -2,6 +2,7 @@ "Sort Ascending": "Aufsteigend sortieren", "Sort Descending": "Absteigend sortieren", "Reset sorting": "Sortierung zurücksetzen", + "Pin column": "Spalte anheften", "Remove column": "Spalte entfernen", "No Data": "Keine Daten", "{count} cells copied": { diff --git a/src/translations/en.json b/src/translations/en.json index 80298682..16ac80a5 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2,6 +2,7 @@ "Sort Ascending": "Sort Ascending", "Sort Descending": "Sort Descending", "Reset sorting": "Reset sorting", + "Pin column": "Pin column", "Remove column": "Remove column", "No Data": "No Data", "{count} cells copied": { diff --git a/src/translations/fr.json b/src/translations/fr.json index 194ec108..80bd0f14 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -2,6 +2,7 @@ "Sort Ascending": "Trier par ordre croissant", "Sort Descending": "Trier par ordre décroissant", "Reset sorting": "Réinitialiser le tri", + "Pin column": "Épingler la colonne", "Remove column": "Supprimer colonne", "No Data": "Pas de données", "{count} cells copied": { diff --git a/src/translations/it.json b/src/translations/it.json index a7308c15..1f19b41b 100644 --- a/src/translations/it.json +++ b/src/translations/it.json @@ -2,6 +2,7 @@ "Sort Ascending": "Ordinamento ascendente", "Sort Descending": "Ordinamento decrescente", "Reset sorting": "Azzeramento ordinamento", + "Pin column": "Colonna perno", "Remove column": "Rimuovi colonna", "No Data": "Nessun dato", "{count} cells copied": {