-
Notifications
You must be signed in to change notification settings - Fork 41
Add support for fixed column widths #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| <!-- | ||
|
|
||
| Copyright (c) 2020, the Regular Table Authors. | ||
|
|
||
| This file is part of the Regular Table library, distributed under the terms of | ||
| the Apache License 2.0. The full license can be found in the LICENSE file. | ||
|
|
||
| --> | ||
|
|
||
| <!-- | ||
| This is an example of how to set up columns with fixed width just using | ||
| Javascript and CSS rules. | ||
|
|
||
| The default behaviour that regular-table implements for this feature includes: | ||
| * The default width of the cells corresponding to a column is calculated | ||
| taking into account the size of the content of the visible cells for that column. | ||
| * It is only possible to increase the width of a cell up to the size of the | ||
| content currently visible for that column. | ||
| * It is possible to manually shrink the column width up to the limit of 10 pixels. | ||
| * Column widths are calculated by the library using the max-width css rule. Which | ||
| means that settings the max-width from a css rule will lead to a fixed width | ||
| behavior for the cells of that column. | ||
| --> | ||
|
|
||
| <!DOCTYPE html> | ||
| <html> | ||
|
|
||
| <head> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"> | ||
| <script src="../dist/umd/regular-table.js"></script> | ||
| <link rel='stylesheet' href="../dist/css/material.css"> | ||
| </head> | ||
|
|
||
| <body> | ||
|
|
||
| <regular-table></regular-table> | ||
|
|
||
| <script> | ||
| class CreateDataModel { | ||
| constructor(baseColumns, columnCount, cellCount) { | ||
| this._baseColumns = baseColumns; | ||
| this._columnCount = columnCount; | ||
| this._cellCount = cellCount; | ||
| this._dataset = this._createDataset(); | ||
| this.columns = this._createColumns(); | ||
| this._data = this.columns.map(({key}) => this._dataset[key]); | ||
| this._columnHeaders = this.columns.map(({value}) => [value]); | ||
| } | ||
|
|
||
| _createTextCells(text) { | ||
| return Array.from(Array(this._cellCount).keys()).map((idx) => `${text} ${idx}`); | ||
| } | ||
|
|
||
| _createColumns() { | ||
| return Array.from(Array(this._columnCount)).map((_, idx) => { | ||
| const key = this._baseColumns[idx % this._baseColumns.length]; | ||
| return { | ||
| key, | ||
| value: `${key} Column ${idx}`, | ||
| }; | ||
| }); | ||
| } | ||
|
|
||
| _createDataset() { | ||
| return this._baseColumns.reduce( | ||
| (prev, curr) => ({ | ||
| ...prev, | ||
| [curr]: this._createTextCells(curr), | ||
| }), | ||
| {} | ||
| ); | ||
| } | ||
|
|
||
| dataListener = (x0, y0, x1, y1) => { | ||
| const data = this._data.slice(x0, x1).map((col) => col.slice(y0, y1)); | ||
| const column_headers = this._columnHeaders.slice(x0, x1); | ||
| const num_columns = this._data.length; | ||
| const num_rows = this._data[0].length; | ||
| return { | ||
| num_rows, | ||
| num_columns, | ||
| column_headers, | ||
| data, | ||
| }; | ||
| }; | ||
| } | ||
|
|
||
| const baseColumns = ["Fixed", "Not Set"]; | ||
| const columnCount = 20; | ||
| const cellCount = 1000; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think these are also CONSTANTS and belong at the top of the script |
||
|
|
||
| // Create data model that returns a dataListener function and a columns array. | ||
| const dataModel = new CreateDataModel(baseColumns, columnCount, cellCount); | ||
|
|
||
| // Get Regular Table element. | ||
| const tableApi = document.getElementsByTagName("regular-table")[0]; | ||
| const FIXED_CLASS = "fixed"; | ||
|
|
||
| // Clear previous cell manipulations done by this api. | ||
| function clear(cellElement) { | ||
| cellElement.classList.remove(FIXED_CLASS); | ||
| cellElement.style.minWidth = ""; | ||
| } | ||
|
|
||
| function getColumnName(index) { | ||
| return dataModel.columns[index].key; | ||
| } | ||
|
|
||
| // Check if cell should apply fixed min-width. | ||
| function isFixed(cellElement) { | ||
| // Use regular-table api to get cell metadata. | ||
| const metadata = tableApi.getMeta(cellElement); | ||
| const name = getColumnName(metadata.cidx); | ||
| return name.includes("Fixed"); | ||
| } | ||
|
|
||
| // Add "fixed" class to cell element. | ||
| function setFixedClass(cellElement) { | ||
| cellElement.classList.add(FIXED_CLASS); | ||
| } | ||
|
|
||
| // Set fixed min-width to cells when appropiate. | ||
| function styleListener() { | ||
| const ths = tableApi.querySelectorAll("thead th"); | ||
| const tds = tableApi.querySelectorAll("tbody td"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be const elems = tableApi.querySelectorAll("thead th, tbody td");The DOM API is very expressive already! |
||
| // Iterate over all rendered cells. | ||
| for (const cellElement of [...ths, ...tds]) { | ||
| clear(cellElement); | ||
| if (isFixed(cellElement)) setFixedClass(cellElement); | ||
| } | ||
| } | ||
|
|
||
| window.addEventListener("DOMContentLoaded", async () => { | ||
| // Pass dataListener function to regular-table api. | ||
| tableApi.setDataListener(dataModel.dataListener); | ||
|
|
||
| // Attach styleListener function to a listener to apply changes | ||
| // after the grid is updated. | ||
| tableApi.addStyleListener(styleListener); | ||
|
|
||
| // Trigger table draw method to make listeners run. | ||
| await tableApi.draw(); | ||
| }); | ||
| </script> | ||
| <style> | ||
| /* Set fixed width for cells. */ | ||
| .fixed { | ||
| min-width: 100px !important; | ||
| max-width: 100px !important; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems important - I'd put it at the top! |
||
| } | ||
| /* Disable default header text selection. */ | ||
| thead tr th { | ||
| user-select: none; | ||
| } | ||
| /* Do not allow content to overflow cell limit. */ | ||
| tr th, tr td { | ||
| overflow: hidden; | ||
| white-space: nowrap; | ||
| text-overflow: ellipsis; | ||
| } | ||
| </style> | ||
| </body> | ||
| </html> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| /****************************************************************************** | ||
| * | ||
| * Copyright (c) 2020, the Regular Table Authors. | ||
| * | ||
| * This file is part of the Regular Table library, distributed under the terms | ||
| * of the Apache License 2.0. The full license can be found in the LICENSE | ||
| * file. | ||
| * | ||
| */ | ||
|
|
||
| describe("fixed_column_width.html", () => { | ||
| beforeAll(async () => { | ||
| await page.setViewport({width: 400, height: 100}); | ||
| }); | ||
|
|
||
| describe("creates a `<table>` with fixed column widths", () => { | ||
| beforeAll(async () => { | ||
| await page.goto("http://localhost:8081/examples/fixed_column_width.html"); | ||
| await page.waitFor("regular-table table tbody tr td"); | ||
| }); | ||
|
|
||
| test("fixed th has min-width", async () => { | ||
| const first_tr = await page.$("regular-table thead tr:first-child"); | ||
| const minWidths = await page.evaluate((first_tr) => Array.from(first_tr.children) | ||
| .map((x) => getComputedStyle(x).getPropertyValue('min-width')), first_tr); | ||
| const fixedWidth = minWidths[0]; | ||
| const notSetWidth = minWidths[1]; | ||
| expect(fixedWidth).toEqual("100px"); | ||
| expect(notSetWidth).toEqual("0px"); | ||
| }); | ||
|
|
||
| test("fixed th has max-width", async () => { | ||
| const first_tr = await page.$("regular-table thead tr:first-child"); | ||
| const max_widths = await page.evaluate((first_tr) => Array.from(first_tr.children) | ||
| .map((x) => getComputedStyle(x).getPropertyValue('max-width')), first_tr); | ||
| const fixed_width = max_widths[0]; | ||
| const not_set_width = max_widths[1]; | ||
| expect(fixed_width).toEqual("100px"); | ||
| expect(not_set_width).toEqual("none"); | ||
| }); | ||
|
|
||
|
|
||
| test("fixed td has min-width", async () => { | ||
| const first_tr = await page.$("regular-table tbody tr:first-child"); | ||
| const minWidths = await page.evaluate((first_tr) => Array.from(first_tr.children) | ||
| .map((x) => getComputedStyle(x).getPropertyValue('min-width')), first_tr); | ||
| const fixedWidth = minWidths[0]; | ||
| const notSetWidth = minWidths[1]; | ||
| expect(fixedWidth).toEqual("100px"); | ||
| expect(notSetWidth).toEqual("0px"); | ||
| }); | ||
|
|
||
| test("fixed td has max-width", async () => { | ||
| const first_tr = await page.$("regular-table tbody tr:first-child"); | ||
| const max_widths = await page.evaluate((first_tr) => Array.from(first_tr.children) | ||
| .map((x) => getComputedStyle(x).getPropertyValue('max-width')), first_tr); | ||
| const fixed_width = max_widths[0]; | ||
| const not_set_width = max_widths[1]; | ||
| expect(fixed_width).toEqual("100px"); | ||
| expect(not_set_width).toEqual("none"); | ||
| }); | ||
|
|
||
| test("cell value do not overflow", async () => { | ||
| const first_td = await page.$("regular-table tbody tr td:first-child"); | ||
| const { text_overflow, overflow, white_space } = await page.evaluate((first_td) => { | ||
| first_td.text_content = 'ABCDEFGHABCDEFGHABCDEFGHABCDEFGH'; | ||
| const styles = getComputedStyle(first_td); | ||
| return { | ||
| text_overflow: styles.getPropertyValue('text-overflow'), | ||
| overflow: styles.getPropertyValue('overflow'), | ||
| white_space: styles.getPropertyValue('white-space') | ||
| }; | ||
| }, first_td); | ||
| expect(text_overflow).toEqual("ellipsis"); | ||
| expect(overflow).toEqual("hidden"); | ||
| expect(white_space).toEqual("nowrap"); | ||
| }); | ||
|
|
||
| test("ths do not allow text selection", async () => { | ||
| const first_tr = await page.$("regular-table thead tr:first-child"); | ||
| const user_selects = await page.evaluate((first_tr) => Array.from(first_tr.children) | ||
| .map((x) => getComputedStyle(x).getPropertyValue('user-select')), first_tr); | ||
| expect(user_selects).toEqual(["none", "none", "none", "none"]); | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe these list-item hard wraps need to be indented https://www.markdownguide.org/basic-syntax/#adding-elements-in-lists