Skip to content

Instantly share code, notes, and snippets.

@davismj
Last active April 1, 2019 22:02
Show Gist options
  • Save davismj/1aa5983bde49715e95174211fa5ce174 to your computer and use it in GitHub Desktop.
Save davismj/1aa5983bde49715e95174211fa5ce174 to your computer and use it in GitHub Desktop.
Table example
<template>
<require from="fancy-table.css"></require>
<require from="table-custom-element"></require>
<require from="value-converters"></require>
<require from="sortable-custom-attribute"></require>
<require from="reorderable-custom-attribute"></require>
<require from="resizable-custom-attribute"></require>
<require from="table-custom-element"></require>
<require from="row-custom-element"></require>
<style>
table { width: 100%; table-layout: fixed; }
.align-right { text-align: right; }
.align-center { text-align: center; }
.actions { margin-bottom: 0.5em; }
.action { cursor: pointer; }
.button { border: 1px solid; margin: 0.25em; padding: 0.25em; cursor: pointer; }
.button.active { background: skyblue; }
/*.arrow::after { content: "►"; }*/
.rotate { display: inline-block; transition: transform 0.25s ease-in-out }
.rotate.ninety { transform: rotate(90deg); }
</style>
<compose view="details.html"></compose>
<!--<div class="actions">-->
<!-- <button click.delegate="increaseProfitByFactor(2)">double profits</button>-->
<!-- <button click.delegate="increaseProfitByFactor(0.5)">halve profits</button>-->
<!--</div>-->
<!--<div class="filters"></div>-->
<!--
Selectable adds checkboxes.
All columns are resizable, reodrerable by default.
resizable="false", reorderable="false" to opt out
paged adds $page, paged="5" sets the page size
-->
<!--<ess-table selectable paged sort-change.delegate="updateSort($event.detail)">-->
<table mousedown.delegate="startResize($event)" mousemove.delegate="resize($event)">
<thead><tr sort-change.delegate="updateSort($event.detail)">
<td style="width: 24px;"><!-- expand --></td>
<td style="width: 24px;">
<input type="checkbox" change.delegate="toggleAll()" element.ref="allCheckbox" />
</td>
<th sortable="name" resizable reorderable>Name</th>
<th sortable="industry" resizable reorderable>Industry</th>
<th sortable="revenue" resizable reorderable>Revenue</th>
<th sortable="profit" resizable reorderable>Profit</th>
<th sortable="employees" resizable reorderable>Employees</th>
<td><!-- actions --></td>
</tr></thead>
<template repeat.for="company of data">
<tr class="row">
<td class="action">
<i class="arrow rotate ${expanded ? 'ninety' : ''}" click.delegate="expanded = !expanded">►</i>
</td>
<td>
<input type="checkbox" checked.bind="selected" model.bind="company" change.delegate="toggleCheckbox()" />
</td>
<td>${company.name}</td>
<td>${company.industry}</td>
<td class="align-right">${company.revenue | cash}</td>
<td class="align-right">${company.profit | cash}</td>
<td class="align-right">${company.employees | integer}</td>
<td>
<a class="action">save</a>
<a class="action">delete</a>
<a class="action">edit</a>
</td>
</tr>
<tr if.bind="expanded">
<td></td>
<td colspan="6">
<table class="table" style="width: 100%">
<tr>
<th>Name again</th>
<th>More profits</th>
</tr>
<tr class="row" repeat.for="i of 2">
<td>${company.name} again</td>
<td>${company.profit * (i + 0.5) | cash}</td>
</tr>
</table>
</td>
</tr>
</template>
</table>
</template>
const DATA = [{"id":1,"name":"Great Haperdohl LLC","industry":"Energy","revenue":7463223593,"profit":285235056,"employees":4538421,"stockType":"Private"},{"id":2,"name":"Flaberdune Cartel","industry":"Healthcare","revenue":8247505676,"profit":459460530,"employees":6258914,"stockType":"Public"},{"id":3,"name":"Ittabito LLC","industry":"Retail","revenue":1114822061,"profit":79793676,"employees":1069455,"stockType":"Public"},{"id":4,"name":"Mangus Company","industry":"Professional Services","revenue":8340589421,"profit":771667109,"employees":694463,"stockType":"Public"},{"id":5,"name":"Big Haperdohl Inc","industry":"Energy","revenue":490059500,"profit":-42947038,"employees":350512,"stockType":"Private"},{"id":6,"name":"Peders and Niptune GmbH","industry":"Professional Services","revenue":162387372,"profit":8181461,"employees":121730,"stockType":"Public"},{"id":7,"name":"Big Kantorset LLP","industry":"Energy","revenue":4055472550,"profit":75701215,"employees":2790938,"stockType":"Private"},{"id":8,"name":"The Mangus Company","industry":"Healthcare","revenue":373545503,"profit":30470421,"employees":331892,"stockType":"Private"},{"id":9,"name":"Frick, Hangdi, and Xu Company","industry":"Healthcare","revenue":9805204865,"profit":643754088,"employees":1149239,"stockType":"Private"},{"id":10,"name":"Flaberdune Cartel","industry":"Professional Services","revenue":4249188928,"profit":354068669,"employees":3843675,"stockType":"Private"},{"id":11,"name":"Silamoon LLC","industry":"Healthcare","revenue":3421276636,"profit":148162077,"employees":321634,"stockType":"Private"},{"id":12,"name":"Mots and Pots Inc","industry":"Professional Services","revenue":4340435672,"profit":-114223324,"employees":4043964,"stockType":"Public"},{"id":13,"name":"Wat Mangus Cartel","industry":"Retail","revenue":3339881999,"profit":-22380287,"employees":1206667,"stockType":"Private"},{"id":14,"name":"Oranges Mangus LLC","industry":"Energy","revenue":6286356219,"profit":-351413220,"employees":131026,"stockType":"Private"},{"id":15,"name":"Getchoo LLC","industry":"Technology","revenue":7678922123,"profit":136010373,"employees":279408,"stockType":"Public"},{"id":16,"name":"Rapsure GmbH","industry":"Finance","revenue":698889498,"profit":10756224,"employees":526695,"stockType":"Public"},{"id":17,"name":"Candzer GmbH","industry":"Retail","revenue":120560040,"profit":-8007905,"employees":82564,"stockType":"Private"},{"id":18,"name":"Frick, Hangdi, and Xu Company","industry":"Energy","revenue":2371535427,"profit":-112806171,"employees":938876,"stockType":"Private"},{"id":19,"name":"Ittabito Inc","industry":"Energy","revenue":2144384009,"profit":-117252392,"employees":1129640,"stockType":"Private"},{"id":20,"name":"Peders and Niptune LLC","industry":"Healthcare","revenue":7303398308,"profit":129639389,"employees":2240585,"stockType":"Private"},{"id":21,"name":"Great Cangrue Inc","industry":"Technology","revenue":3636652542,"profit":-44434312,"employees":1022928,"stockType":"Public"},{"id":22,"name":"Mangus GmbH","industry":"Technology","revenue":2900647213,"profit":-237141150,"employees":1702733,"stockType":"Public"},{"id":23,"name":"Zimmerman Company","industry":"Finance","revenue":1001198218,"profit":-85353111,"employees":352132,"stockType":"Public"},{"id":24,"name":"Womp Company","industry":"Healthcare","revenue":3438414715,"profit":-322919836,"employees":2461858,"stockType":"Private"},{"id":25,"name":"Floor So Clean Cartel","industry":"Energy","revenue":4238467958,"profit":138732159,"employees":156166,"stockType":"Private"},{"id":26,"name":"Zimmerman Inc","industry":"Professional Services","revenue":7357175505,"profit":287755105,"employees":5676009,"stockType":"Public"},{"id":27,"name":"Big Womp LLC","industry":"Professional Services","revenue":4771430545,"profit":304224653,"employees":4427484,"stockType":"Public"},{"id":28,"name":"Ignis Pati LLC","industry":"Finance","revenue":9941703051,"profit":616489181,"employees":2498462,"stockType":"Public"},{"id":29,"name":"Wat Ignis Pati GmbH","industry":"Professional Services","revenue":174481683,"profit":-13104758,"employees":151642,"stockType":"Private"},{"id":30,"name":"Floor So Clean Company","industry":"Retail","revenue":9649291625,"profit":-131008101,"employees":8204372,"stockType":"Public"},{"id":31,"name":"Getchoo Company","industry":"Retail","revenue":4451349341,"profit":-52022004,"employees":2406274,"stockType":"Public"},{"id":32,"name":"The Kantorset LLC","industry":"Healthcare","revenue":3888584251,"profit":-71043859,"employees":1820945,"stockType":"Public"},{"id":33,"name":"Big Frick, Hangdi, and Xu Inc","industry":"Technology","revenue":604589367,"profit":53479734,"employees":428306,"stockType":"Private"},{"id":34,"name":"Hearty GmbH","industry":"Finance","revenue":8106593659,"profit":128248000,"employees":5038297,"stockType":"Public"},{"id":35,"name":"Haperdohl Inc","industry":"Energy","revenue":3297825689,"profit":640745,"employees":2873213,"stockType":"Private"},{"id":36,"name":"Silamoon LLC","industry":"Healthcare","revenue":5392488359,"profit":330650370,"employees":3306660,"stockType":"Private"},{"id":37,"name":"Piperdoodle LLP","industry":"Finance","revenue":7296209823,"profit":-538813402,"employees":174872,"stockType":"Private"},{"id":38,"name":"Great Silamoon GmbH","industry":"Retail","revenue":6231676938,"profit":247010311,"employees":2656258,"stockType":"Public"},{"id":39,"name":"Candzer Company","industry":"Finance","revenue":9410542678,"profit":-331736614,"employees":2776780,"stockType":"Public"},{"id":40,"name":"Getchoo Company","industry":"Finance","revenue":9792999127,"profit":569734102,"employees":7281382,"stockType":"Private"},{"id":41,"name":"Wat Getchoo Company","industry":"Energy","revenue":3299755916,"profit":-128685420,"employees":161743,"stockType":"Public"},{"id":42,"name":"The King Fantastic LLP","industry":"Healthcare","revenue":7572331205,"profit":105483529,"employees":1074510,"stockType":"Private"},{"id":43,"name":"The King Fantastic Cartel","industry":"Energy","revenue":8022791039,"profit":-633801512,"employees":494130,"stockType":"Private"},{"id":44,"name":"Great Cangrue Company","industry":"Healthcare","revenue":7510077193,"profit":160131345,"employees":3076648,"stockType":"Public"},{"id":45,"name":"Silamoon GmbH","industry":"Finance","revenue":5305266880,"profit":213164715,"employees":419699,"stockType":"Private"},{"id":46,"name":"Hearty LLP","industry":"Retail","revenue":6728822897,"profit":495128896,"employees":2374175,"stockType":"Public"},{"id":47,"name":"Great Cangrue GmbH","industry":"Professional Services","revenue":2422821081,"profit":-16366750,"employees":1245701,"stockType":"Private"},{"id":48,"name":"Zimmerman Inc","industry":"Energy","revenue":1703558834,"profit":-120234329,"employees":803013,"stockType":"Public"},{"id":49,"name":"Big Silamoon GmbH","industry":"Retail","revenue":9170148057,"profit":-458072837,"employees":4456824,"stockType":"Public"},{"id":50,"name":"Piperdoodle Company","industry":"Healthcare","revenue":4011156171,"profit":-135574071,"employees":2334006,"stockType":"Public"},{"id":51,"name":"Womp GmbH","industry":"Technology","revenue":4838827336,"profit":-244319393,"employees":1266332,"stockType":"Private"},{"id":52,"name":"Big Womp LLC","industry":"Energy","revenue":8832195965,"profit":147898282,"employees":4865141,"stockType":"Public"},{"id":53,"name":"Ignis Pati LLC","industry":"Healthcare","revenue":1183441467,"profit":27578833,"employees":879324,"stockType":"Public"},{"id":54,"name":"Haperdohl LLP","industry":"Professional Services","revenue":169604533,"profit":8502283,"employees":165033,"stockType":"Public"},{"id":55,"name":"King Fantastic LLP","industry":"Retail","revenue":2235648104,"profit":-132165307,"employees":685697,"stockType":"Public"},{"id":56,"name":"Flaberdune Cartel","industry":"Technology","revenue":7095514270,"profit":-679682123,"employees":1803033,"stockType":"Private"},{"id":57,"name":"King Fantastic GmbH","industry":"Technology","revenue":1956840532,"profit":180526916,"employees":1632932,"stockType":"Public"},{"id":58,"name":"Peders and Niptune LLC","industry":"Finance","revenue":842888169,"profit":53615200,"employees":203997,"stockType":"Private"},{"id":59,"name":"Mangus Company","industry":"Technology","revenue":492148100,"profit":-24071097,"employees":192729,"stockType":"Private"},{"id":60,"name":"Mangus LLC","industry":"Healthcare","revenue":7322298157,"profit":327578895,"employees":1722482,"stockType":"Private"},{"id":61,"name":"Candzer GmbH","industry":"Professional Services","revenue":5649051234,"profit":329111062,"employees":3616350,"stockType":"Private"},{"id":62,"name":"Piperdoodle Company","industry":"Professional Services","revenue":2918793589,"profit":-270705812,"employees":2471837,"stockType":"Private"},{"id":63,"name":"The Getchoo Cartel","industry":"Technology","revenue":2039084921,"profit":-159996954,"employees":175981,"stockType":"Private"},{"id":64,"name":"King Fantastic GmbH","industry":"Healthcare","revenue":7954465372,"profit":-57155707,"employees":1358609,"stockType":"Public"},{"id":65,"name":"Rapsure Cartel","industry":"Technology","revenue":132331482,"profit":1016025,"employees":132237,"stockType":"Private"},{"id":66,"name":"Getchoo LLC","industry":"Energy","revenue":8924185236,"profit":492226842,"employees":6056595,"stockType":"Public"},{"id":67,"name":"Ignis Pati LLP","industry":"Energy","revenue":2335131094,"profit":-186654888,"employees":2012776,"stockType":"Public"},{"id":68,"name":"Rapsure Inc","industry":"Technology","revenue":2960057029,"profit":262547631,"employees":94620,"stockType":"Private"},{"id":69,"name":"Flaberdune Company","industry":"Technology","revenue":4384485834,"profit":335437900,"employees":4296098,"stockType":"Private"},{"id":70,"name":"Wat Hearty Cartel","industry":"Energy","revenue":6038941840,"profit":-123289504,"employees":3607309,"stockType":"Private"},{"id":71,"name":"The Zimmerman Company","industry":"Healthcare","revenue":9818258474,"profit":790437232,"employees":512591,"stockType":"Public"},{"id":72,"name":"Wat Ittabito LLC","industry":"Finance","revenue":7574500151,"profit":172655006,"employees":2991242,"stockType":"Private"},{"id":73,"name":"Candzer LLP","industry":"Technology","revenue":9356692941,"profit":919266001,"employees":8408063,"stockType":"Private"},{"id":74,"name":"King Fantastic Company","industry":"Technology","revenue":5215405800,"profit":153687063,"employees":3845172,"stockType":"Public"},{"id":75,"name":"Ignis Pati LLC","industry":"Energy","revenue":2245136505,"profit":141471584,"employees":731537,"stockType":"Public"},{"id":76,"name":"Oranges Rapsure Inc","industry":"Energy","revenue":3942699594,"profit":132858250,"employees":3737869,"stockType":"Private"},{"id":77,"name":"Candzer Inc","industry":"Professional Services","revenue":7154405999,"profit":-434519708,"employees":5254072,"stockType":"Private"},{"id":78,"name":"Wat Haperdohl LLC","industry":"Healthcare","revenue":9796004524,"profit":580314820,"employees":1675503,"stockType":"Public"},{"id":79,"name":"Hearty LLC","industry":"Healthcare","revenue":2505779177,"profit":-71478939,"employees":283905,"stockType":"Private"},{"id":80,"name":"Zimmerman Cartel","industry":"Energy","revenue":6789437387,"profit":-424834943,"employees":4112823,"stockType":"Public"},{"id":81,"name":"Wat Getchoo GmbH","industry":"Professional Services","revenue":7994720840,"profit":547784112,"employees":7694058,"stockType":"Private"},{"id":82,"name":"Rapsure Company","industry":"Retail","revenue":5625018710,"profit":-196916829,"employees":1703942,"stockType":"Private"},{"id":83,"name":"King Fantastic LLC","industry":"Healthcare","revenue":2258164807,"profit":-78141393,"employees":1495958,"stockType":"Public"},{"id":84,"name":"Flaberdune Cartel","industry":"Finance","revenue":9965090221,"profit":-184324222,"employees":9368819,"stockType":"Private"},{"id":85,"name":"Kantorset Inc","industry":"Finance","revenue":6119440819,"profit":381614905,"employees":5906024,"stockType":"Public"},{"id":86,"name":"Kantorset Inc","industry":"Professional Services","revenue":8736268747,"profit":789782735,"employees":2892410,"stockType":"Public"},{"id":87,"name":"Peders and Niptune LLP","industry":"Professional Services","revenue":5382053789,"profit":117453642,"employees":767198,"stockType":"Public"},{"id":88,"name":"Hearty LLC","industry":"Finance","revenue":5289313634,"profit":-111456091,"employees":5270825,"stockType":"Public"},{"id":89,"name":"Floor So Clean LLC","industry":"Healthcare","revenue":5069411670,"profit":390950918,"employees":544575,"stockType":"Public"},{"id":90,"name":"Getchoo Cartel","industry":"Energy","revenue":2733883180,"profit":-173507636,"employees":867753,"stockType":"Private"},{"id":91,"name":"Silamoon Cartel","industry":"Finance","revenue":5465300140,"profit":-519423445,"employees":3417287,"stockType":"Public"},{"id":92,"name":"Womp Company","industry":"Technology","revenue":8556887131,"profit":-425066747,"employees":4546350,"stockType":"Private"},{"id":93,"name":"Wat Ittabito Cartel","industry":"Energy","revenue":4013235453,"profit":-87674586,"employees":213018,"stockType":"Public"},{"id":94,"name":"Wat Hearty GmbH","industry":"Professional Services","revenue":249513915,"profit":17011352,"employees":125438,"stockType":"Public"},{"id":95,"name":"The Candzer LLC","industry":"Professional Services","revenue":1842955767,"profit":-141851809,"employees":994072,"stockType":"Private"},{"id":96,"name":"Piperdoodle GmbH","industry":"Technology","revenue":1667899196,"profit":13001807,"employees":885172,"stockType":"Public"},{"id":97,"name":"Zimmerman LLC","industry":"Professional Services","revenue":8033493573,"profit":774916979,"employees":2765446,"stockType":"Private"},{"id":98,"name":"Hearty LLC","industry":"Professional Services","revenue":9504728500,"profit":-246514623,"employees":4773786,"stockType":"Private"},{"id":99,"name":"Peders and Niptune Inc","industry":"Finance","revenue":7952550485,"profit":-330785855,"employees":4164307,"stockType":"Private"},{"id":100,"name":"Cangrue Cartel","industry":"Healthcare","revenue":8379023796,"profit":472987607,"employees":305778,"stockType":"Public"}]
export class App {
sorters = [];
selected = [];
page = 0;
pages = DATA.length / 10;
attached() {
const resize = () => {
const { resizing, target, move } = this;
if (resizing) {
debugger;
target.clientWidth += move;
this.move = 0;
}
requestAnimationFrame(resize);
};
resize();
}
target = null;
resizing = false;
move = 0;
startResize(event) {
if (event.offsetX < 0) {
this.resizing = true;
this.target = event.target;
}
}
resize(event) {
if (this.resizing) {
this.move += event.movementX;
}
}
activate() {
const { sorters, page } = this;
return Promise.all([
this.getData({ sorters, page }).then((data) => this.data = data),
this.loadConfig()
]);
}
getData({ sorters, page } = {}) {
sorters = sorters || [];
page = page || 0;
let data = DATA.slice();
data = SortValueConverter.prototype.toView(DATA, sorters);
data = data.slice(10 * page, 10 * (page + 1));
return Promise.resolve(data);
}
loadConfig() {
return Promise.resolve({
columns: []
});
}
increaseProfitByFactor(k) {
this.selected.forEach(s => s.profit *= k);
}
toggleAll() {
if (this.selected.length < this.data.length) {
this.selected = this.data.slice();
} else {
this.selected = [];
}
}
toggleCheckbox() {
const { length } = this.selected;
if (length === 0) {
this.allCheckbox.checked = false;
this.allCheckbox.indeterminate = false;
} else if (length === this.data.length) {
this.allCheckbox.checked = true;
this.allCheckbox.indeterminate = false;
} else {
this.allCheckbox.checked = false;
this.allCheckbox.indeterminate = true;
}
}
updateSort(newSort) {
const idx = this.getSortIndex(newSort[0]);
if (idx !== null) {
this.sorters.splice(idx, 1);
}
if (newSort[1]) {
this.sorters.push(newSort);
}
this.activate();
}
updatePage(page) {
this.page = page;
this.activate();
}
getSortIndex(property) {
const idx = this.sorters.findIndex((s) => s[0] === property);
return idx > -1 ? idx : null;
}
}
export class SortValueConverter {
toView(arr, sorters) {
arr = arr.slice();
sorters.forEach(([prop, dir]) => {
const s = dir === 'desc' ? -1 : 1;
arr = arr.sort((a, b) => {
const aProp = a[prop];
const bProp = b[prop];
if (aProp < bProp) {
return -s;
} else if (aProp > bProp) {
return s;
} else {
return 0;
}
});
});
return arr;
}
}
export class DoubleValueConverter {
toView(arr) {
return arr.reduce((a, i) => a = a.concat([i, i]), []);
}
}
<template>
<style>em::before { content: "NOTE: "; font-size: 0.8em; font-weight: bold; }</style>
<section style="margin-bottom: 1em;">
<style>label { display: block; }</style>
<p>This example will demonstrate how to build Aurelia table-oriented tools that achieve the following:</p>
<label>
<input type="checkbox" checked />
<span>declarative column definition</span>
<b>(44535)</b>
<em>through default templating</em>
</label>
<label>
<input type="checkbox" checked />
<span>action columns (select, cancel, save, etc), including multi-select column with checkboxes for bulk actions</span>
<b>(44535)</b>
</label>
<label>
<input type="checkbox" checked />
<span>(server side) cascading sort</span>
<b>(44535)</b>
</label>
<label>
<input type="checkbox" checked />
<span>(server side) pagination</span>
<b>(44535)</b>
</label>
<label>
<input type="checkbox" checked />
<span>hierarchy (detail tables)</span>
<b>(44535)</b>
<em>no ie11 support!</em>
</label>
<label>
<input type="checkbox" />
<span>(server side) filter</span>
<b>(46554)</b>
</label>
<label>
<input type="checkbox" checked />
<span>resizable columns</span>
<b>(46554)</b>
</label>
<label>
<input type="checkbox" checked />
<span>auto-fit widths</span>
<b>(46554)</b>
</label>
<label>
<input type="checkbox" />
<span>column visibility</span>
<b>(46554)</b>
</label>
<label>
<input type="checkbox" />
<span>column reordering</span>
<b>(46554)</b>
</label>
<label>
<input type="checkbox" />
<span>persist table settings</span>
<b>(46554)</b>
</label>
<label>
<input type="checkbox" />
<span>"freeze" column header row at top when scrolling</span>
<b>(46554)</b>
</label>
<label>
<input type="checkbox" />
<span>(server side) filter</span>
</label>
<label>
<input type="checkbox" />
<span>(client side) cascading sort</span>
<em>not demoed here, but follows trivially from this construction</em>
</label>
<label>
<input type="checkbox" />
<span>(client side) pagination</span>
<em>not demoed here, but follows trivially from this construction</em>
</label>
<label>
<input type="checkbox" />
<span>(client side) filter</span>
</label>
</section>
</template>
/* Resizable */
.resize-handle {
position: absolute;
right: -4px;
top: 0;
background: red;
height: 100%;
width: 8px;
cursor: col-resize;
}
[resizable] {
position: relative;
box-sizing: content-box;
padding: 1vh 1vw;
transition: width 0.05s;
}
/* Reorderable */
.reorder-handle {
height: 0.8em;
width: 0.8em;
border-radius: 50%;
background: green;
float: right;
cursor: grab;
}
[reorderable] {
}
td:not(.dragging), th:not(.dragging) {
transition: transform 0.25s ease-in-out;
}
/* Sortable */
[sortable] {
cursor: pointer;
}
.sort-asc:before, .sort-desc:before {
content: attr(data-sort-index);
margin-right: 0.5rem;
font-size: 0.8em;
}
.sort-asc:after {
content: '▲';
margin-left: 0.5rem;
}
.sort-desc:after {
content: '▼';
margin-left: 0.5rem;
}
.table {
display: grid;
grid-template-columns: repeat(auto-fit);
max-width: 100%;
}
thead td,
thead th {
}
.row {
}
.cell {
padding: 0.2em;
}
.action {
font-weight: bold;
font-size: 0.8em;
}
<!doctype html>
<html>
<head>
<title>Table Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body aurelia-app="main">
<h1>Loading...</h1>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.6/system.js"></script>
<script src="https://rawgit.com/aurelia-ui-toolkits/aurelia-materialize-bundles/0.30.0/config2.js"></script>
<script>
System.import('aurelia-bootstrapper');
</script>
</body>
</html>
/*******************************************************************************
* The following two lines enable async/await without using babel's
* "runtime" transformer. Uncomment the lines if you intend to use async/await.
*
* More info here: https://github.com/jdanyow/aurelia-plunker/issues/2
*/
//import regeneratorRuntime from 'babel-runtime/regenerator';
//window.regeneratorRuntime = regeneratorRuntime;
/******************************************************************************/
import 'materialize';
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging()
.plugin('aurelia-materialize-bridge', bridge => bridge.useAll() );
aurelia.start().then(a => a.setRoot());
}
<template>
<div>
<a class="button ${page === i ? 'active' : ''}" repeat.for="i of pages" click.delegate="updatePage(i)">${i + 1}</a>
</div>
</template>
function midpoints(el) {
const { top, left, height, width } = el.getBoundingClientRect();
return {
x: left + width / 2,
y: top + height / 2
};
}
function indexOf(el) {
return Array.from(el.parentElement.children).indexOf(el);
}
const handle = document.createElement('div');
handle.classList.add('reorder-handle');
export class ReorderableCustomAttribute {
static inject = [Element];
constructor(element) {
this.element = element;
this.handle = handle.cloneNode();
}
attached() {
this.element.appendChild(this.handle);
const index = this.index = indexOf(this.element);
const table = this.element.closest('table');
this.column = Array.from(table.querySelectorAll(`tr > :nth-child(${index+1}`));
const midpoint = midpoints(this.element).x;
const swap = (event) => {
if (this.dragging) return;
const sgn = event.detail.index < index ? -1 : 1;
if (sgn * (midpoint - event.detail.x) > 0) {
this.move(sgn * event.detail.width, 0);
event.detail.dragged.index = this.index
this.index = event.detail.index
}
};
const drag = ({ clientX, clientY, pageX }) => {
if (this.dragging) {
const dx = clientX - this.x;
const dy = clientY - this.y;
this.move(dx, dy);
table.dispatchEvent(new CustomEvent('dragover', { detail: {
index: this.index,
x: clientX,
width: this.element.clientWidth,
dragged: this
}}));
}
}
const cancel = () => {
this.dragging = false;
this.column.forEach((cell) => cell.classList.remove('dragging'));
document.removeEventListener('mousemove', drag);
document.removeEventListener('mouseup', cancel);
table.dispatchEvent(new CustomEvent('dragend', {
detail: { index: this.index, dragged: this }
}));
}
table.addEventListener('dragover', swap);
table.addEventListener('dragend', (event) => {
if (index === event.detail.index) {
for (let i = this.column.length-1; i >= 0; i--) {
const shifted = this.column[i];
const dragged = event.detail.dragged.column[i];
const parent = shifted.parentElement;
parent.insertBefore(dragged, shifted.nextSibling);
}
}
if (this !== event.detail.dragged) {
}
this.move(0, 0);
});
this.handle.addEventListener('mousedown', ({ clientX, clientY }) => {
this.dragging = true;
this.column.forEach((cell) => cell.classList.add('dragging'));
this.x = clientX;
this.y = clientY;
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', cancel);
});
this.handle.addEventListener('click', (event) => event.stopPropagation());
}
move(dx, dy) {
this.column.forEach((cell) => cell.style.transform = `translate(${dx}px, ${dy}px)`);
}
reorder(dx, pageX) {
// if dx < 0
// for each header from index-1 to 0
// if pagex is less than right for a header element
// then select each idx-th element and insert after the next element
// if dx > 0
// for each header from index+1 to length
// if pagex is more than left for a header element
// then select each idx-th element and insert it before the previouse element
}
}
function isResizable(el) {
return el && el.hasAttribute('resizable');
}
function midpoint(el) {
const { left, width } = el.getBoundingClientRect();
return left + width / 2;
}
const handle = document.createElement('div');
handle.classList.add('resize-handle');
export class ResizableCustomAttribute {
static inject = [Element];
constructor(element) {
this.element = element;
this.handle = handle.cloneNode();
}
attached() {
this.element.appendChild(this.handle);
this.autofit();
const resize = ({ clientX }) => {
if (this.resizing) {
const el = this.element;
const dx = clientX - this.x;
console.log(el.clientWidth, dx,);
this.resize(el, this.width + dx);
const next = el.nextSibling;
if (next) {
this.resize(next, next.clientWidth - dx);
}
}
};
const cancel = () => {
this.resizing = false;
document.removeEventListener('mouseup', cancel);
document.removeEventListener('mouseup', resize);
};
const autofit = () => this.autofit();
this.handle.addEventListener('mousedown', (event) => {
this.x = midpoint(this.handle);
this.width = this.element.clientWidth;
this.resizing = true;
document.addEventListener('mouseup', cancel);
document.addEventListener('mousemove', resize);
});
this.handle.addEventListener('click', (event) => event.stopPropagation());
this.handle.addEventListener('dblclick', autofit);
}
resize(el, width) {
el.style.width = `${width}px`;
}
autofit() {
const table = this.element.closest('table');
const parent = this.element.parentElement;
const index = Array.from(parent.children).indexOf(this.element);
const range = document.createRange();
const width = Math.max(
...Array.from(table.querySelectorAll(`tr > :nth-child(${index+1})`))
.map((cell) => {
const range = document.createRange();
range.selectNodeContents(cell.childNodes[0]);
return range.getBoundingClientRect().width;
})
);
this.resize(this.element, width);
}
}
import { containerless, inlineView } from 'aurelia-framework';
@containerless
@inlineView('<template><slot></slot></template>')
export class EssRowCustomElement {
}
import { bindable, inject } from 'aurelia-framework';
const DIRS = {
0: null,
1: 'asc',
2: 'desc'
}
@inject(Element)
export class SortableCustomAttribute {
sort = Object.freeze([this.value, DIRS[this.dir]]);
dir = 0;
constructor(element) {
this.element = element;
}
attached() {
this.element.addEventListener('click', (event) => {
this.dir = (this.dir + 1) % 3;
this.sort = Object.freeze([this.value, DIRS[this.dir]]);
this.element.dispatchEvent(new CustomEvent('sort-change', {
bubbles: true,
detail: this.sort
}));
this.updateClass();
});
}
updateClass() {
const dir = DIRS[this.dir];
if (dir === 'asc') {
this.element.classList.add('sort-asc');
}
else if (dir === 'desc') {
this.element.classList.remove('sort-asc');
this.element.classList.add('sort-desc');
} else {
this.element.classList.remove('sort-desc');
}
}
}
<template>
<style>
ess-table {
display: grid;
grid-auto-columns: min-content;
}
ess-cell, ess-header {
padding: 1vh 1vw;
}
</style>
<slot></slot>
</template>
export class EssTableCustomElement {
}
// export class PagedCustomAttribute {
// pageLength = 10;
// page = 0;
// bind(bindingContext, overrideContext) {
// this.context = overrideContext;
// this.setPage(0);
// }
// setPage(page) {
// this.page = this.context.$page = page;
// }
// }
// export class PagedValueConverter {
// }
<table mousedown.delegate="startResize($event)" mousemove.delegate="resize($event)">
<thead><tr sort-change.delegate="updateSort($event.detail)">
<td style="width: 24px;"><!-- expand --></td>
<td style="width: 24px;">
<input type="checkbox" change.delegate="toggleAll()" element.ref="allCheckbox" />
</td>
<th sortable="name" resizable reorderable>Name</th>
<th sortable="industry" resizable reorderable>Industry</th>
<th sortable="revenue" resizable reorderable>Revenue</th>
<th sortable="profit" resizable reorderable>Profit</th>
<th sortable="employees" resizable reorderable>Employees</th>
<td><!-- actions --></td>
</tr></thead>
<template repeat.for="company of data">
<tr class="row">
<td class="action">
<i class="arrow rotate ${expanded ? 'ninety' : ''}" click.delegate="expanded = !expanded">►</i>
</td>
<td>
<input type="checkbox" checked.bind="selected" model.bind="company" change.delegate="toggleCheckbox()" />
</td>
<td>${company.name}</td>
<td>${company.industry}</td>
<td class="align-right">${company.revenue | cash}</td>
<td class="align-right">${company.profit | cash}</td>
<td class="align-right">${company.employees | integer}</td>
<td>
<a class="action">save</a>
<a class="action">delete</a>
<a class="action">edit</a>
</td>
</tr>
<tr if.bind="expanded">
<td></td>
<td colspan="6">
<table class="table" style="width: 100%">
<tr>
<th>Name again</th>
<th>More profits</th>
</tr>
<tr class="row" repeat.for="i of 2">
<td>${company.name} again</td>
<td>${company.profit * (i + 0.5) | cash}</td>
</tr>
</table>
</td>
</tr>
</template>
</table>
function pad(num, digits = 3) {
num = num.toString();
while (num.length < digits) {
num = '0' + num;
}
return num;
}
export class CashValueConverter {
toView(val) {
if (typeof(val) !== 'number') {
return val;
}
const sign = Math.sign(val) < 0 ? '-' : '';
const decimal = val % 1;
const places = [];
val = Math.abs(Math.trunc(val));
while (val > 1) {
places.unshift(val % 1000);
val = Math.floor(val / 1000)
}
return `${sign}\$${places.map((p, i) => pad(p, i > 0 ? 3 : 0)).join(',')}.${decimal.toFixed(2).slice(2)}`
}
}
export class IntegerValueConverter {
toView(val) {
if (typeof(val) !== 'number') {
return val;
}
const places = [];
while (val > 1) {
places.unshift(val % 1000);
val = Math.floor(val / 1000)
}
return `${places.map((p, i) => pad(p, i > 0 ? 3 : 0)).join(',')}`
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment