├── .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 | GitHub Releases 5 | NPM Releases 6 | Bundlephobia 7 | Latest Status 8 | Release Status 9 | 10 | Discord 11 | Published on WebComponents.org 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 | --------------------------------------------------------------------------------