Skip to content

Commit dd6d4f6

Browse files
authored
Merge pull request #20 from mutablelogic/dev2
Updating table support
2 parents 14a1a51 + 971192e commit dd6d4f6

14 files changed

+268
-37
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ jspm_packages/
2626
.next
2727
.nuxt
2828
dist
29+
build
2930
.cache/
3031
.vuepress/dist
3132
.serverless/

.vscode/settings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"eslint.experimental.useFlatConfig": true,
2+
"eslint.useFlatConfig": true,
33
"eslint.options": { "overrideConfigFile": "./config/eslint.config.mjs" },
44
}

README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,11 @@ npm install
99
Development mode,
1010

1111
```bash
12-
npm run dev
12+
npm run dev
13+
```
14+
15+
Build,
16+
17+
```bash
18+
npm run build
19+
```

config/esbuild.table.mjs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import esbuild from 'esbuild';
2+
3+
const commonOptions = {
4+
outdir: 'dist',
5+
format: 'esm',
6+
bundle: true,
7+
loader: {
8+
'.svg': 'file',
9+
'.woff': 'file',
10+
'.woff2': 'file',
11+
'.ttf': 'file',
12+
'.otf': 'file',
13+
'.html': 'copy',
14+
'.json': 'copy',
15+
},
16+
logLevel: 'info',
17+
entryPoints: [],
18+
};
19+
20+
// Add the table
21+
commonOptions.entryPoints.push('example/table/index.js', 'example/table/index.html');
22+
23+
// Build the table, and optionally serve it in development
24+
if (process.env.NODE_ENV === 'production') {
25+
await esbuild.build({
26+
...commonOptions,
27+
minify: true,
28+
sourcemap: false,
29+
}).catch(() => process.exit(1));
30+
} else if (process.env.NODE_ENV === 'development') {
31+
let ctx = await esbuild.context({
32+
...commonOptions,
33+
minify: false,
34+
sourcemap: true,
35+
})
36+
let { host, port } = await ctx.serve({
37+
servedir: commonOptions.outdir,
38+
});
39+
await ctx.watch();
40+
}

example/esbuild.js

-5
This file was deleted.

example/table/index.html

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Nanoradio</title>
8+
<script type="module" src="index.js" defer></script>
9+
<link href="index.css" rel="stylesheet">
10+
<style type="text/css">
11+
.content-scroll {
12+
overflow-y: scroll;
13+
}
14+
body {
15+
overflow: hidden;
16+
}
17+
</style>
18+
</head>
19+
20+
<body>
21+
<js-canvas>
22+
<js-nav>
23+
<js-navitem>Home</js-navitem>
24+
</js-nav>
25+
<js-content class="content-scroll">
26+
<js-table id="items-table" data="#items-data">
27+
<!-- Table columns -->
28+
<js-itemcol name="title"></js-itemcol>
29+
30+
<!-- Hide other columns by default -->
31+
<js-tablecol hidden></js-tablecol>
32+
</js-table>
33+
</js-content>
34+
<js-nav>
35+
<!-- data source and model for items -->
36+
<js-provider id="items-provider" path="http://cm5.lan:8000/feed/v1/document?limit=50"></js-provider>
37+
<js-array id="items-data" provider="#items-provider" select="body"></js-array>
38+
</js-nav>
39+
</js-canvas>
40+
</body>
41+
42+
</html>

example/table/index.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// This file defines all the styles and elements used for the web components
2+
import '../../src/index';
3+
import './item.js'
4+
5+
/* Code to reload in the esbuild serve development environment */
6+
window.addEventListener('load', () => {
7+
// eslint-disable-next-line no-restricted-globals
8+
new EventSource('/esbuild').addEventListener('change', () => location.reload());
9+
});

example/table/item.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { html } from 'lit';
2+
import { TableColumnElement } from '../../src/element/TableColumnElement';
3+
4+
export class ItemColumn extends TableColumnElement {
5+
static get localName() {
6+
return 'js-itemcol';
7+
}
8+
9+
// eslint-disable-next-line class-methods-use-this
10+
#text(value, key) {
11+
return value instanceof Object ? value[key] : value;
12+
}
13+
14+
render(value, key) {
15+
const cell = value instanceof Object ? value[key] : value;
16+
switch (key) {
17+
case 'title':
18+
return html`
19+
<div style="padding: 5px;">
20+
<js-tag size="small">${this.#text(value, 'author')}</js-tag>
21+
<h4>${this.#text(value, 'title')}</h4>
22+
<small><strong>${this.#text(value, 'pubdate')}</strong></small>
23+
<p>${this.#text(value, 'desc')}</p>
24+
${value.media ? this.#renderAudio(value.media[0]) : ''}
25+
</div>
26+
`;
27+
default:
28+
}
29+
return html`${cell}`;
30+
}
31+
32+
// eslint-disable-next-line class-methods-use-this
33+
#renderAudio(media) {
34+
return html`
35+
<audio controls>
36+
<source src="${media.url}" type="${media.type}">
37+
Your browser does not support the audio element.
38+
</audio>
39+
`;
40+
}
41+
}
42+
43+
customElements.define(ItemColumn.localName, ItemColumn); // js-itemcol

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"description": "Javascript Framework",
55
"main": "dist/index.js",
66
"scripts": {
7-
"geojson-dev": "rm -fr dist && install -d dist && NODE_ENV=development node config/esbuild.geojson.mjs",
8-
"build": "rm -fr dist && install -d dist && NODE_ENV=production node config/esbuild.geojson.mjs",
7+
"dev": "NODE_ENV=development node config/esbuild.table.mjs",
8+
"build": "rm -fr dist && install -d dist && NODE_ENV=production node esbuild.table.mjs",
99
"lint": "ESLINT_USE_FLAT_CONFIG=true eslint -c config/eslint.config.mjs --cache --fix ./src/**/*.js",
1010
"docs": "jsdoc -c config/jsdoc.config.json"
1111
},

src/element/TableColumnElement.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { LitElement, html } from 'lit';
2+
3+
/**
4+
* @class TableColumnElement
5+
*
6+
* This class provides a table column element, for rendering
7+
* a table cell. It also provides properties for the column.
8+
* The name property is used to identify the column in the
9+
* table, and the hidden property is used to hide the column.
10+
*
11+
* @example
12+
* <js-tablecol name="id">ID</js-tablecol>
13+
*/
14+
export class TableColumnElement extends LitElement {
15+
static get localName() {
16+
return 'js-tablecol';
17+
}
18+
19+
static get properties() {
20+
return {
21+
name: { type: String, reflect: true },
22+
hidden: { type: Boolean, reflect: true },
23+
};
24+
}
25+
26+
/**
27+
* Get the column title.
28+
*
29+
* @returns {string}
30+
*/
31+
get title() {
32+
return this.textContent;
33+
}
34+
35+
// eslint-disable-next-line class-methods-use-this
36+
render(value, key) {
37+
const cell = value instanceof Object ? value[key] : value;
38+
if (cell instanceof Object) {
39+
return html`<code>${JSON.stringify(cell)}</code>`;
40+
}
41+
return html`${cell}`;
42+
}
43+
}

src/element/TableBodyElement.js src/element/TableElement.js

+67-20
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,32 @@
11
import { LitElement, html, css } from 'lit';
22
import { EventType } from '../core/EventType';
33
import { TableHeadElement } from './TableHeadElement';
4+
import { TableColumnElement } from './TableColumnElement';
45

56
/**
6-
* @class TableBodyElement
7+
* @class TableElement
78
*
8-
* This class provides a table body element.
9+
* This class provides a table element, in which the header, footer
10+
* and columns are rendered.
911
*
1012
* @example
11-
* <js-tablebody data="#data-source-id"></js-tablebody>
13+
* <js-table data="#data-source-id"><!-- .... --></js-table>
1214
*/
13-
export class TableBodyElement extends LitElement {
15+
export class TableElement extends LitElement {
16+
// Data source node
1417
#data = null;
1518

19+
// Table header node
1620
#head = null;
1721

22+
// Table column renderers
23+
#renderer = {};
24+
25+
// Default renderer
26+
#default;
27+
1828
static get localName() {
19-
return 'js-tablebody';
29+
return 'js-table';
2030
}
2131

2232
static get properties() {
@@ -48,8 +58,7 @@ export class TableBodyElement extends LitElement {
4858
th {
4959
text-transform: capitalize;
5060
}
51-
.wrap {
52-
max-height: 40px;
61+
.cell {
5362
overflow: hidden;
5463
}
5564
code, pre {
@@ -92,6 +101,27 @@ export class TableBodyElement extends LitElement {
92101
firstUpdated() {
93102
// Set the table header
94103
this.#head = this.querySelector(TableHeadElement.localName);
104+
105+
// Get the table columns
106+
const elements = this.childNodes;
107+
for (let i = 0; i < elements.length; i += 1) {
108+
if (elements[i] instanceof TableColumnElement) {
109+
// Column name and title
110+
const name = elements[i].getAttribute('name');
111+
// If the name is not empty, add it to the column list
112+
if (name && name !== '') {
113+
// Append the column to the list
114+
if (this.columns.indexOf(name) === -1) {
115+
this.columns.push(elements[i].getAttribute('name'));
116+
}
117+
// Set column renderer
118+
this.#renderer[name] = elements[i];
119+
} else {
120+
// Set the default renderer
121+
this.#default = elements[i];
122+
}
123+
}
124+
}
95125
}
96126

97127
render() {
@@ -111,29 +141,46 @@ export class TableBodyElement extends LitElement {
111141
return rows;
112142
}
113143

114-
#renderColumns(row) {
115-
const columns = [];
144+
#rendererFor(key) {
145+
const renderer = this.#renderer[key];
146+
if (renderer) {
147+
return renderer;
148+
}
149+
return this.#default;
150+
}
151+
152+
#hidden(key) {
153+
return this.#rendererFor(key).hidden;
154+
}
116155

156+
#renderColumns(row) {
157+
const cells = [];
117158
if (row instanceof Object) {
118159
Object.keys(row).forEach((key) => {
119-
if (this.columns.indexOf(key) === -1) {
120-
this.columns.push(key);
160+
if (!this.#hidden(key)) {
161+
if (this.columns.indexOf(key) === -1) {
162+
this.columns.push(key);
163+
}
164+
cells[this.columns.indexOf(key)] = html`<td><div class="cell">${this.#renderCell(row, key)}</div></td>`;
121165
}
122-
columns.push(html`<td><div class="wrap">${this.#renderCell(row[key])}</div></td>`);
123166
});
124167
} else {
125168
this.columns.push('value');
126-
columns.push(html`<td>${this.#renderCell(row)}</td>`);
169+
cells.push(html`<td>${this.#renderCell(row)}</td>`);
127170
}
128171

129-
return columns;
172+
// Any missing columns we fill
173+
for (let i = 0; i < this.columns.length; i += 1) {
174+
if (!cells[i]) {
175+
cells[i] = html`<td></td>`;
176+
}
177+
}
178+
179+
// Return cells for rendering in a row
180+
return cells;
130181
}
131182

132-
// eslint-disable-next-line class-methods-use-this
133-
#renderCell(cell) {
134-
if (cell instanceof Object) {
135-
return html`<code>${JSON.stringify(cell)}</code>`;
136-
}
137-
return html`${cell}`;
183+
#renderCell(value, key) {
184+
return this.#rendererFor(key).render(value, key);
138185
}
139186
}

src/element/TableHeadElement.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ export class TableHeadElement extends LitElement {
5656

5757
#renderColumns(row) {
5858
const columns = [];
59-
for (const cell in row) {
60-
columns.push(html`<th>${this.#renderCell(row[cell])}</th>`);
61-
}
59+
Object.keys(row).forEach((key) => {
60+
columns.push(html`<th>${this.#renderCell(row[key])}</th>`);
61+
});
6262
return columns;
6363
}
6464

0 commit comments

Comments
 (0)