Skip to content
Open
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
140 changes: 135 additions & 5 deletions src/js/modules/ResponsiveLayout/ResponsiveLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default class ResponsiveLayout extends Module{
this.collapseFormatter = [];
this.collapseStartOpen = true;
this.collapseHandleColumn = false;
this.deferredRows = new Set();

this.registerTableOption("responsiveLayout", false); //responsive layout flags
this.registerTableOption("responsiveLayoutCollapseStartOpen", true); //start showing collapsed data
Expand All @@ -38,14 +39,28 @@ export default class ResponsiveLayout extends Module{

this.subscribe("table-redrawing", this.tableRedraw.bind(this));

if(this.table.options.responsiveLayout === "collapse"){
//this.mode is not set until initializeResponsivity
if(this.isCollapseMode(this.table.options.responsiveLayout)){
this.subscribe("row-data-changed", this.generateCollapsedRowContent.bind(this));
this.subscribe("row-init", this.initializeRow.bind(this));
this.subscribe("row-layout", this.layoutRow.bind(this));

if(this.isEditableMode(this.table.options.responsiveLayout)){
this.subscribe("row-responsive-toggled", this.rowResponsiveToggled.bind(this));
this.subscribe("edit-editor-clear", this.editorCleared.bind(this));
}
}
}
}

isCollapseMode(mode = this.mode){
return mode === "collapse" || mode === "collapseEditable";
}

isEditableMode(mode = this.mode){
return mode === "collapseEditable";
}

tableRedraw(force){
if(["fitColumns", "fitDataStretch"].indexOf(this.layoutMode()) === -1){
if(!force){
Expand Down Expand Up @@ -73,7 +88,7 @@ export default class ResponsiveLayout extends Module{
column.modules.responsive.index = i;
columns.push(column);

if(!column.visible && this.mode === "collapse"){
if(!column.visible && this.isCollapseMode()){
this.hiddenColumns.push(column);
}
}
Expand All @@ -89,7 +104,7 @@ export default class ResponsiveLayout extends Module{

this.columns = columns;

if(this.mode === "collapse"){
if(this.isCollapseMode()){
this.generateCollapsedContent();
}

Expand Down Expand Up @@ -157,7 +172,7 @@ export default class ResponsiveLayout extends Module{

column.hide(false, true);

if(this.mode === "collapse"){
if(this.isCollapseMode()){
this.hiddenColumns.unshift(column);
this.generateCollapsedContent();

Expand All @@ -170,11 +185,15 @@ export default class ResponsiveLayout extends Module{
showColumn(column){
var index;

if(this.isEditableMode()){
this.restoreColumnCells(column);
}

column.show(false, true);
//set column width to prevent calculation loops on uninitialized columns
column.setWidth(column.getWidth());

if(this.mode === "collapse"){
if(this.isCollapseMode()){
index = this.hiddenColumns.indexOf(column);

if(index > -1){
Expand Down Expand Up @@ -249,8 +268,21 @@ export default class ResponsiveLayout extends Module{
var el, contents;

if(row.modules.responsiveLayout){
//a rebuild would tear an open editor out of the DOM, so defer it
if(this.isEditableMode() && this.rowIsEditing(row)){
this.deferredRows.add(row);
return;
}

this.deferredRows.delete(row);

el = row.modules.responsiveLayout.element;

//restore first, so the teardown below moves cells rather than orphaning them
if(this.isEditableMode()){
this.restoreCollapsedCells(row);
}

while(el.firstChild) el.removeChild(el.firstChild);

contents = this.collapseFormatter(this.generateCollapsedRowData(row));
Expand All @@ -270,6 +302,20 @@ export default class ResponsiveLayout extends Module{
var value = column.getFieldValue(data);

if(column.definition.title && column.field){
if(this.isEditableMode()){
let cell = row.getCell(column.field);

if(cell){
output.push({
field: column.field,
title: column.definition.title,
value: this.collapseCell(cell)
});
}

return;
}

if(column.modules.format && this.table.options.responsiveLayoutCollapseUseFormatters){

mockCellComponent = {
Expand Down Expand Up @@ -320,6 +366,90 @@ export default class ResponsiveLayout extends Module{
return output;
}

editorCleared(){
var rows = this.deferredRows;

if(rows.size){
this.deferredRows = new Set();
rows.forEach((row) => {
this.generateCollapsedRowContent(row);
});
}
}

rowIsEditing(row){
var currentCell = this.table.modExists("edit") ? this.table.modules.edit.currentCell : false;

return !!currentCell && currentCell.row === row;
}

//an editor left open in a closed container would hold the redraw block
rowResponsiveToggled(row, open){
if(!open && this.rowIsEditing(row)){
this.table.modules.edit.cancelEdit();
}
}

//only visibility is touched here; sizing is left to the stylesheet
collapseCell(cell){
var element = cell.getElement();

cell.show();

return element;
}

restoreColumnCells(column){
column.cells.forEach((cell) => {
this.restoreCell(cell);
});
}

restoreCollapsedCells(row){
row.cells.forEach((cell) => {
this.restoreCell(cell);
});
}

restoreCell(cell){
var config = cell.row.modules.responsiveLayout,
cells = cell.row.cells,
anchor = null,
rowEl;

//cell.element, not getElement(), which would force a lazy render
if(!config || !config.element || !cell.element || !config.element.contains(cell.element)){
return;
}

rowEl = cell.row.getElement();

for(let i = cells.indexOf(cell) + 1; i < cells.length; i++){
if(cells[i].element && cells[i].element.parentNode === rowEl){
anchor = cells[i].element;
break;
}
}

//the collapse container is always the row's last child
if(!anchor && config.element.parentNode === rowEl){
anchor = config.element;
}

if(anchor){
rowEl.insertBefore(cell.element, anchor);
}else{
rowEl.appendChild(cell.element);
}

//the column may have been shown while the cell was away
if(cell.column.visible){
cell.show();
}else{
cell.hide();
}
}

formatCollapsedData(data){
var list = document.createElement("table");

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default function(cell, formatterParams, onRendered){
var el = document.createElement("div"),
config = cell.getRow()._row.modules.responsiveLayout;
row = cell.getRow()._row,
config = row.modules.responsiveLayout;

el.classList.add("tabulator-responsive-collapse-toggle");

Expand Down Expand Up @@ -35,6 +36,7 @@ export default function(cell, formatterParams, onRendered){
el.addEventListener("click", function(e){
e.stopImmediatePropagation();
toggleList(!config.open);
row.dispatch("row-responsive-toggled", row, config.open);
cell.getTable().rowManager.adjustTableSize();
});

Expand Down
13 changes: 13 additions & 0 deletions src/scss/tabulator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,19 @@ $rangeHeaderTextHighlightBackground: #000000 !default; //header text color when
}
}
}

//a cell relocated here by the collapseEditable mode
.tabulator-cell{
//the column width and row height are re-applied inline on every pass
width:100% !important;
height:auto !important;

border-right:none;

white-space:normal;
overflow:visible;
text-overflow:clip;
}
}

//cell element
Expand Down
57 changes: 57 additions & 0 deletions test/e2e/responsive-collapse-editable.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Tabulator Responsive Collapse Editable Test</title>
<link rel="stylesheet" href="../../dist/css/tabulator.min.css" />
<script src="../../dist/js/tabulator.js"></script>
<style>
body {
margin: 0;
padding: 10px;
font-family: Arial, sans-serif;
}
#test-table {
width: 100%;
height: 400px;
}
</style>
</head>
<body>
<div id="test-table"></div>

<script>
document.addEventListener("DOMContentLoaded", function () {
// every cellEdited the table emits, so a test can assert the edit went down
// the ordinary Edit path rather than just changing some DOM
window.edits = [];
window.cancels = [];

window.testTable = new Tabulator("#test-table", {
layout: "fitColumns",
responsiveLayout: "collapseEditable",
data: [
{ id: 1, name: "Alice", age: 25, city: "New York", code: "AA" },
{ id: 2, name: "Bob", age: 32, city: "Chicago", code: "BB" },
],
columns: [
{ title: "", formatter: "responsiveCollapse", width: 40, minWidth: 40, hozAlign: "center", resizable: false, headerSort: false, responsive: 0 },
{ title: "ID", field: "id", width: 80, responsive: 0 },
{ title: "Name", field: "name", width: 160, editor: "input", responsive: 0 },
{ title: "Age", field: "age", width: 160, editor: "input", responsive: 2 },
{ title: "City", field: "city", width: 160, editor: "input", responsive: 3 },
{ title: "Code", field: "code", width: 160, editor: "input", validator: "required", responsive: 4 },
],
});

window.testTable.on("cellEdited", function (cell) {
window.edits.push({ field: cell.getField(), value: cell.getValue() });
});

window.testTable.on("cellEditCancelled", function (cell) {
window.cancels.push(cell.getField());
});
});
</script>
</body>
</html>
Loading