├── .github
└── workflows
│ ├── latest.yml
│ └── release.yml
├── .gitignore
├── .npmignore
├── .vscode
└── launch.json
├── LICENSE
├── README.md
├── dev
├── assets
│ ├── theme.html
│ └── theme2.html
├── index.html
└── sample.json
├── examples
├── cdn
│ └── basic.html
└── npm
│ └── basic.html
├── index.js
├── index.min.js
├── package.json
├── src
└── wc-sortable-table.js
└── themes
└── basic.html
/.github/workflows/latest.yml:
--------------------------------------------------------------------------------
1 | name: Latest
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | verify:
7 | name: Verify
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v1
12 | - name: Setup
13 | uses: actions/setup-node@v1
14 | with:
15 | node-version: 14
16 | - name: Cache
17 | uses: actions/cache@v1
18 | with:
19 | path: node_modules
20 | key: ${{ runner.OS }}-npm-cache-${{ hashFiles('**/package.json') }}
21 | restore-keys: |
22 | ${{ runner.OS }}-npm-cache-
23 | - name: Install
24 | run: npm i
25 | - name: Test
26 | run: npm run test --if-present
27 | - name: Lint
28 | run: npm run lint --if-present
29 | - name: Types
30 | run: npm run types --if-present
31 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | jobs:
9 | check:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@master
14 | - name: Setup
15 | uses: actions/setup-node@v1
16 | with:
17 | node-version: 14
18 | - name: Cache
19 | uses: actions/cache@v1
20 | with:
21 | path: node_modules
22 | key: ${{ runner.OS }}-npm-cache-${{ hashFiles('**/package.json') }}
23 | restore-keys: |
24 | ${{ runner.OS }}-npm-cache-
25 | - name: Verify
26 | run: |
27 | npm i
28 | npm run preversion
29 | npm:
30 | runs-on: ubuntu-latest
31 | needs: check
32 | steps:
33 | - name: Checkout
34 | uses: actions/checkout@master
35 | - name: Publish
36 | run: |
37 | echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_AUTH_TOKEN }}" > ~/.npmrc
38 | npm publish --access public
39 | gh:
40 | runs-on: ubuntu-latest
41 | needs: check
42 | steps:
43 | - name: Checkout
44 | uses: actions/checkout@master
45 | - name: Publish
46 | run: |
47 | echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> ~/.npmrc
48 | ORG="$(echo '${{ github.repository }}' | cut -d'/' -f1)"
49 | echo "registry=https://npm.pkg.github.com/$ORG" > .npmrc
50 | npm publish
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | package/
3 | *.tgz
4 | package-lock.json
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .github/
2 | .vscode/
3 | dev/
4 | *.tgz
5 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "type": "chrome",
6 | "request": "launch",
7 | "name": "Chrome",
8 | "url": "http://localhost:5500/dev",
9 | "webRoot": "${workspaceFolder}",
10 | "pathMapping": {
11 | "/dev/": "${workspaceFolder}/dev/"
12 | }
13 | },
14 | {
15 | "type": "browser-preview",
16 | "request": "launch",
17 | "name": "Browser Preview",
18 | "url": "http://localhost:5500/dev/",
19 | "webRoot": "${workspaceFolder}",
20 | "pathMapping": {
21 | "/dev/": "${workspaceFolder}/dev/"
22 | }
23 | }
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 VanillaWC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | 'Software'), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
<wc-sortable-table> A HTML Table with Sort
2 |
3 |
4 |

5 |

6 |

7 |

8 |

9 |
10 |

11 |

12 |
13 |
14 | ## Installation
15 |
16 | *Installation*
17 | ```sh
18 | npm i @vanillawc/wc-sortable-table
19 | ```
20 |
21 | *Import from NPM*
22 | ```html
23 |
24 | ```
25 |
26 | *Import from CDN*
27 | ```html
28 |
29 | ```
30 |
31 | ## Demo
32 |
33 | Try it on [WebComponents.dev](https://webcomponents.dev/edit/f9IfeJX878gJZibfBcPO?sv=1&pm=1)
34 |
35 | ## Usage
36 |
37 | **Attributes**
38 |
39 | - `src` - load an external source file
40 | - `theme` - an external theme file
41 |
42 | **Properties**
43 |
44 | - `value` - get/set the table data
45 |
46 | ### Basic Usage
47 |
48 | ```html
49 |
50 | ```
51 |
52 | ## Styling
53 |
54 | By default, `` contains an un-styled `` element in the lightDOM. That means, it will inherit any global CSS styles present on the site and can be styled directly using CSS.
55 |
56 | When a sort is applied, the `` element selected receives either a `.asc` or `.desc` class depending on the direction of the sort.
57 |
58 | ## Theming
59 |
60 | Alternatively, a theme for `` can be specified. This will encapsulate the element in a shadowDOM -- to prevent style bleed -- and apply the style provided.
61 |
62 | ```html
63 |
64 | ```
65 |
66 | ## Creating Themes
67 |
68 | A theme contains a `
94 | ```
95 |
96 | ## Contributing
97 |
98 | See [CONTRIBUTING.md](https://github.com/vanillawc/vanillawc/blob/main/CONTRIBUTING.md)
99 |
--------------------------------------------------------------------------------
/dev/assets/theme.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dev/assets/theme2.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dev/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WC-Sortable-Table - Demo
5 |
6 |
7 |
8 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/dev/sample.json:
--------------------------------------------------------------------------------
1 | [
2 | ["header1", "header2", "header3", "header4", "header5"],
3 | ["AA", "BB", "CC", "DD", "EE"],
4 | ["BB", "CC", "DD", "EE", "AA"],
5 | ["CC", "DD", "EE", "AA", "BB"],
6 | ["DD", "EE", "AA", "BB", "CC"],
7 | ["EE", "AA", "BB", "CC", "DD"]
8 | ]
--------------------------------------------------------------------------------
/examples/cdn/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/npm/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // node_modules/@vanillaes/interpolate/index.js
2 | function interpolate(template, tags = {}) {
3 | const keys = Object.keys(tags);
4 | const values = Object.values(tags);
5 | try {
6 | return new Function(...keys, `return \`${template}\`;`)(...values);
7 | } catch (e) {
8 | throw new TemplateException(template, tags, e);
9 | }
10 | }
11 | var TemplateException = class extends Error {
12 | constructor(template, tags, message) {
13 | super();
14 | this.name = "TemplateError";
15 | let msg = "\n------------------\n";
16 | msg += `Template: \`${template}\``;
17 | msg += "\n------------------\n";
18 | msg += `Tags: ${JSON.stringify(tags, null, 2)}`;
19 | msg += "\n------------------\n";
20 | msg += message;
21 | this.message = msg;
22 | }
23 | };
24 |
25 | // src/wc-sortable-table.js
26 | var WCSortableTable = class extends HTMLElement {
27 | static get observedAttributes() {
28 | return ["src"];
29 | }
30 | attributeChangedCallback(name, oldValue, newValue) {
31 | if (!this.__initialized) {
32 | return;
33 | }
34 | if (oldValue !== newValue) {
35 | this[name] = newValue;
36 | }
37 | }
38 | get src() {
39 | return this.getAttribute("src");
40 | }
41 | set src(value) {
42 | this.setAttribute("src", value);
43 | this.setSrc(value);
44 | }
45 | get value() {
46 | return this.__data;
47 | }
48 | set value(value) {
49 | this.setValue(value);
50 | }
51 | constructor() {
52 | super();
53 | this.__initialized = false;
54 | this.__theme = null;
55 | this.__data = null;
56 | this.__table = null;
57 | this.__column = null;
58 | this.__headers = null;
59 | this.__selected = null;
60 | }
61 | async connectedCallback() {
62 | this.__table = document.createElement("table");
63 | if (this.hasAttribute("theme")) {
64 | this.__theme = document.createElement("template");
65 | this.__theme.innerHTML = await this.getTheme();
66 | this.attachShadow({mode: "open"});
67 | this.shadowRoot.appendChild(this.__theme.content.cloneNode(true));
68 | this.shadowRoot.appendChild(this.__table);
69 | } else {
70 | this.appendChild(this.__table);
71 | }
72 | if (this.hasAttribute("src")) {
73 | this.setSrc();
74 | }
75 | this.__initialized = true;
76 | }
77 | async getTheme() {
78 | const path = this.getAttribute("theme");
79 | const contents = await this.fetchTheme(path);
80 | return interpolate(contents);
81 | }
82 | async fetchTheme(src) {
83 | const response = await fetch(src);
84 | if (response.status !== 200)
85 | throw Error(`ERR ${response.status}: ${response.statusText}`);
86 | return response.text();
87 | }
88 | async setSrc() {
89 | if (this.hasAttribute("src")) {
90 | this.__data = await this.fetchSrc(this.src);
91 | this.render();
92 | }
93 | }
94 | async fetchSrc(src) {
95 | const response = await fetch(src);
96 | if (response.status !== 200)
97 | throw Error(`ERR ${response.status}: ${response.statusText}`);
98 | return response.json();
99 | }
100 | setValue(value) {
101 | this.__data = value;
102 | this.render();
103 | }
104 | sortData(data) {
105 | if (this.__column === null) {
106 | return data;
107 | }
108 | const col = this.__column;
109 | const dir = this.__direction;
110 | const comp = dir ? (a, b) => a < b : (a, b) => a > b;
111 | return data.sort((a, b) => {
112 | return comp(a[col], b[col]) ? -1 : 1;
113 | });
114 | }
115 | headerClicked(e) {
116 | this.__selected = e.target.cellIndex;
117 | const column = e.target.cellIndex;
118 | this.__direction = column !== this.__column ? true : !this.__direction;
119 | this.__column = column;
120 | this.render();
121 | }
122 | styleHeaders() {
123 | const dir = this.__direction ? "asc" : "desc";
124 | const notDir = this.__direction ? "asc" : "desc";
125 | for (const th of this.__headers.children) {
126 | if (th.cellIndex === this.__selected) {
127 | if (th.classList.contains(notDir)) {
128 | th.classList.remove(notDir);
129 | } else {
130 | th.classList.add(dir);
131 | }
132 | } else {
133 | if (th.classList.contains(dir)) {
134 | th.classList.remove(dir);
135 | }
136 | if (th.classList.contains(notDir)) {
137 | th.classList.remove(notDir);
138 | }
139 | }
140 | }
141 | }
142 | render() {
143 | let data = [...this.__data];
144 | const table = document.createElement("table");
145 | this.__headers = document.createElement("tr");
146 | this.__headers.addEventListener("click", (e) => this.headerClicked(e));
147 | const headers = data.shift();
148 | headers.forEach((header) => {
149 | const th = document.createElement("th");
150 | th.innerText = header;
151 | this.__headers.appendChild(th);
152 | });
153 | this.styleHeaders();
154 | const thead = document.createElement("thead");
155 | thead.appendChild(this.__headers);
156 | table.appendChild(thead);
157 | data = this.sortData(data);
158 | const tbody = document.createElement("tbody");
159 | data.forEach((row) => {
160 | const tr = document.createElement("tr");
161 | row.forEach((cell) => {
162 | const td = document.createElement("td");
163 | td.innerText = cell;
164 | tr.appendChild(td);
165 | });
166 | tbody.appendChild(tr);
167 | });
168 | table.appendChild(tbody);
169 | if (this.__theme) {
170 | this.shadowRoot.removeChild(this.__table);
171 | this.__table = table;
172 | this.shadowRoot.appendChild(this.__table);
173 | } else {
174 | this.removeChild(this.__table);
175 | this.__table = table;
176 | this.appendChild(this.__table);
177 | }
178 | }
179 | };
180 | customElements.define("wc-sortable-table", WCSortableTable);
181 | export {
182 | WCSortableTable
183 | };
184 |
--------------------------------------------------------------------------------
/index.min.js:
--------------------------------------------------------------------------------
1 | function o(h,t={}){let e=Object.keys(t),s=Object.values(t);try{return new Function(...e,`return \`${h}\`;`)(...s)}catch(i){throw new l(h,t,i)}}var l=class extends Error{constructor(t,e,s){super();this.name="TemplateError";let i=`
2 | ------------------
3 | `;i+=`Template: \`${t}\``,i+=`
4 | ------------------
5 | `,i+=`Tags: ${JSON.stringify(e,null,2)}`,i+=`
6 | ------------------
7 | `,i+=s,this.message=i}};var d=class extends HTMLElement{static get observedAttributes(){return["src"]}attributeChangedCallback(t,e,s){!this.__initialized||e!==s&&(this[t]=s)}get src(){return this.getAttribute("src")}set src(t){this.setAttribute("src",t),this.setSrc(t)}get value(){return this.__data}set value(t){this.setValue(t)}constructor(){super();this.__initialized=!1,this.__theme=null,this.__data=null,this.__table=null,this.__column=null,this.__headers=null,this.__selected=null}async connectedCallback(){this.__table=document.createElement("table"),this.hasAttribute("theme")?(this.__theme=document.createElement("template"),this.__theme.innerHTML=await this.getTheme(),this.attachShadow({mode:"open"}),this.shadowRoot.appendChild(this.__theme.content.cloneNode(!0)),this.shadowRoot.appendChild(this.__table)):this.appendChild(this.__table),this.hasAttribute("src")&&this.setSrc(),this.__initialized=!0}async getTheme(){let t=this.getAttribute("theme"),e=await this.fetchTheme(t);return o(e)}async fetchTheme(t){let e=await fetch(t);if(e.status!==200)throw Error(`ERR ${e.status}: ${e.statusText}`);return e.text()}async setSrc(){this.hasAttribute("src")&&(this.__data=await this.fetchSrc(this.src),this.render())}async fetchSrc(t){let e=await fetch(t);if(e.status!==200)throw Error(`ERR ${e.status}: ${e.statusText}`);return e.json()}setValue(t){this.__data=t,this.render()}sortData(t){if(this.__column===null)return t;let e=this.__column,i=this.__direction?(r,n)=>rr>n;return t.sort((r,n)=>i(r[e],n[e])?-1:1)}headerClicked(t){this.__selected=t.target.cellIndex;let e=t.target.cellIndex;this.__direction=e!==this.__column?!0:!this.__direction,this.__column=e,this.render()}styleHeaders(){let t=this.__direction?"asc":"desc",e=this.__direction?"asc":"desc";for(let s of this.__headers.children)s.cellIndex===this.__selected?s.classList.contains(e)?s.classList.remove(e):s.classList.add(t):(s.classList.contains(t)&&s.classList.remove(t),s.classList.contains(e)&&s.classList.remove(e))}render(){let t=[...this.__data],e=document.createElement("table");this.__headers=document.createElement("tr"),this.__headers.addEventListener("click",n=>this.headerClicked(n)),t.shift().forEach(n=>{let a=document.createElement("th");a.innerText=n,this.__headers.appendChild(a)}),this.styleHeaders();let i=document.createElement("thead");i.appendChild(this.__headers),e.appendChild(i),t=this.sortData(t);let r=document.createElement("tbody");t.forEach(n=>{let a=document.createElement("tr");n.forEach(_=>{let c=document.createElement("td");c.innerText=_,a.appendChild(c)}),r.appendChild(a)}),e.appendChild(r),this.__theme?(this.shadowRoot.removeChild(this.__table),this.__table=e,this.shadowRoot.appendChild(this.__table)):(this.removeChild(this.__table),this.__table=e,this.appendChild(this.__table))}};customElements.define("wc-sortable-table",d);export{d as WCSortableTable};
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vanillawc/wc-sortable-table",
3 | "version": "1.3.33",
4 | "license": "MIT",
5 | "author": "Evan Plaice (https://evanplaice.com/)",
6 | "description": "A sortable table vanilla web component",
7 | "keywords": [
8 | "web-components",
9 | "vanilla",
10 | "table",
11 | "sort"
12 | ],
13 | "repository": "https://github.com/vanillawc/wc-sortable-table/",
14 | "main": "index.js",
15 | "scripts": {
16 | "start": "npx live-server --no-browser --port=5500 --open=dev",
17 | "lint": "esmtk lint",
18 | "build": "npm run build:esm && npm run build:min",
19 | "build:esm": "esmtk bundle src/wc-sortable-table.js index.js",
20 | "build:min": "esmtk minify src/wc-sortable-table.js index.min.js",
21 | "package": "npx rimraf package && npm pack | tail -n 1 | xargs tar -xf",
22 | "preversion": "npm run lint",
23 | "postversion": "git push --follow-tags"
24 | },
25 | "devDependencies": {
26 | "@vanillaes/interpolate": "^2.0.4",
27 | "esmtk": "^0.5.6"
28 | },
29 | "standard": {
30 | "ignore": [
31 | "index.js"
32 | ]
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/wc-sortable-table.js:
--------------------------------------------------------------------------------
1 | /* eslint no-undef: 0 */
2 | import { interpolate } from '../node_modules/@vanillaes/interpolate/index.js'
3 |
4 | export class WCSortableTable extends HTMLElement {
5 | static get observedAttributes () {
6 | return ['src']
7 | }
8 |
9 | attributeChangedCallback (name, oldValue, newValue) {
10 | if (!this.__initialized) { return }
11 | if (oldValue !== newValue) {
12 | this[name] = newValue
13 | }
14 | }
15 |
16 | get src () { return this.getAttribute('src') }
17 | set src (value) {
18 | this.setAttribute('src', value)
19 | this.setSrc(value)
20 | }
21 |
22 | get value () { return this.__data }
23 | set value (value) {
24 | this.setValue(value)
25 | }
26 |
27 | constructor () {
28 | super()
29 | this.__initialized = false
30 | this.__theme = null
31 | this.__data = null
32 | this.__table = null
33 | this.__column = null
34 | this.__headers = null
35 | this.__selected = null
36 | }
37 |
38 | async connectedCallback () {
39 | this.__table = document.createElement('table')
40 |
41 | if (this.hasAttribute('theme')) {
42 | this.__theme = document.createElement('template')
43 | this.__theme.innerHTML = await this.getTheme()
44 | this.attachShadow({ mode: 'open' })
45 | this.shadowRoot.appendChild(this.__theme.content.cloneNode(true))
46 | this.shadowRoot.appendChild(this.__table)
47 | } else {
48 | this.appendChild(this.__table)
49 | }
50 |
51 | if (this.hasAttribute('src')) {
52 | this.setSrc()
53 | }
54 |
55 | this.__initialized = true
56 | }
57 |
58 | async getTheme () {
59 | const path = this.getAttribute('theme')
60 | const contents = await this.fetchTheme(path)
61 | return interpolate(contents)
62 | }
63 |
64 | async fetchTheme (src) {
65 | const response = await fetch(src)
66 | if (response.status !== 200) throw Error(`ERR ${response.status}: ${response.statusText}`)
67 | return response.text()
68 | }
69 |
70 | async setSrc () {
71 | if (this.hasAttribute('src')) {
72 | this.__data = await this.fetchSrc(this.src)
73 | this.render()
74 | }
75 | }
76 |
77 | async fetchSrc (src) {
78 | const response = await fetch(src)
79 | if (response.status !== 200) throw Error(`ERR ${response.status}: ${response.statusText}`)
80 | return response.json()
81 | }
82 |
83 | setValue (value) {
84 | this.__data = value
85 | this.render()
86 | }
87 |
88 | sortData (data) {
89 | if (this.__column === null) { return data }
90 | const col = this.__column
91 | const dir = this.__direction
92 | const comp = dir
93 | ? (a, b) => a < b
94 | : (a, b) => a > b
95 | return data.sort((a, b) => {
96 | return comp(a[col], b[col]) ? -1 : 1
97 | })
98 | }
99 |
100 | headerClicked (e) {
101 | this.__selected = e.target.cellIndex
102 | const column = e.target.cellIndex
103 | this.__direction = (column !== this.__column)
104 | ? true
105 | : !this.__direction
106 | this.__column = column
107 | this.render()
108 | }
109 |
110 | styleHeaders () {
111 | const dir = this.__direction ? 'asc' : 'desc'
112 | const notDir = this.__direction ? 'asc' : 'desc'
113 | for (const th of this.__headers.children) {
114 | if (th.cellIndex === this.__selected) {
115 | if (th.classList.contains(notDir)) {
116 | th.classList.remove(notDir)
117 | } else {
118 | th.classList.add(dir)
119 | }
120 | } else {
121 | if (th.classList.contains(dir)) {
122 | th.classList.remove(dir)
123 | }
124 | if (th.classList.contains(notDir)) {
125 | th.classList.remove(notDir)
126 | }
127 | }
128 | }
129 | }
130 |
131 | render () {
132 | let data = [...this.__data]
133 | const table = document.createElement('table')
134 | this.__headers = document.createElement('tr')
135 | this.__headers.addEventListener('click', e => this.headerClicked(e))
136 |
137 | // build the headers row
138 | const headers = data.shift()
139 | headers.forEach(header => {
140 | const th = document.createElement('th')
141 | th.innerText = header
142 | this.__headers.appendChild(th)
143 | })
144 | this.styleHeaders()
145 | const thead = document.createElement('thead')
146 | thead.appendChild(this.__headers)
147 | table.appendChild(thead)
148 |
149 | // sort the values
150 | data = this.sortData(data)
151 |
152 | // build the data rows
153 | const tbody = document.createElement('tbody')
154 | data.forEach(row => {
155 | const tr = document.createElement('tr')
156 | row.forEach(cell => {
157 | const td = document.createElement('td')
158 | td.innerText = cell
159 | tr.appendChild(td)
160 | })
161 | tbody.appendChild(tr)
162 | })
163 | table.appendChild(tbody)
164 |
165 | if (this.__theme) {
166 | this.shadowRoot.removeChild(this.__table)
167 | this.__table = table
168 | this.shadowRoot.appendChild(this.__table)
169 | } else {
170 | this.removeChild(this.__table)
171 | this.__table = table
172 | this.appendChild(this.__table)
173 | }
174 | }
175 | }
176 |
177 | customElements.define('wc-sortable-table', WCSortableTable)
178 |
--------------------------------------------------------------------------------
/themes/basic.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
|