@polymer/polymer
Version:
The Polymer library makes it easy to create your own web components. Give your element some markup and properties, and then use it on a site. Polymer provides features like dynamic templates and data binding to reduce the amount of boilerplate you need to
379 lines (370 loc) • 11.9 kB
HTML
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
<title>data-table</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="../../polymer.html">
</head>
<body>
<script>
window.emojiByType = {
a: '😎',
b: '💰',
c: '🚔',
d: '💕',
e: '👍',
f: '🍌'
};
</script>
<dom-module id="data-popup">
<template strip-whitespace>
<style>
:host {
display: inline-block;
background: lightblue;
border-radius: 10px;
border-bottom-left-radius: 0;
border: 1px solid #aaa;
box-shadow: 3px 3px 5px #535353;
padding: 5px;
z-index: 1;
}
</style>
<slot></slot>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'data-popup'
});
});
</script>
</dom-module>
<dom-module id="data-cell">
<template strip-whitespace>
<style>
:host {
flex: 1;
border: 2px solid #aaa;
padding: 5px;
position: relative;
background: #eee;
}
data-popup {
display: none;
position: absolute;
bottom: calc(100% - 15px);
left: calc(100% - 15px);
}
:host(:hover) data-popup {
display: inline-block;
}
:host([change=up]) {
background: #afa;
}
:host([change=down]) {
background: #faa;
}
.type {
display: inline-block;
border-radius: 100%;
box-sizing: border-box;
padding: 3px 0;
background: #aaa;
margin-right: 5px;
height: 30px;
width: 30px;
text-align: center;
}
</style>
<div class="type">{{computeTypeIcon(type)}}</div>
{{toFixed(value)}}
<data-popup>{{value}}</data-popup>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'data-cell',
properties: {
type: {
type: String,
notify: true
},
value: {
type: Number,
notify: true
},
change: {
reflectToAttribute: true,
computed: 'computeChange(value)'
}
},
attached: function() {
this.fire('register-cell');
},
computeChange: function(value) {
var prev = this._prev;
this._prev = value;
return value > prev ? 'up' : (value < prev ? 'down' : 'none');
},
computeTypeIcon: function(type) {
return emojiByType[type];
},
toFixed: function(value) {
return Number(value).toFixed(2);
}
});
});
</script>
</dom-module>
<dom-module id="data-row">
<template strip-whitespace>
<style>
:host {
display: flex;
flex-direction: row;
}
.title-cell {
flex: 1;
}
.title {
font-weight: bold;
}
.dominant {
font-size: 0.7em;
}
</style>
<div class="title-cell">
<div class="title">{{title}}</div>
<div class="dominant">Dominant: {{computeDominant(cells.*)}}</div>
</div>
<template is="dom-repeat" items="{{cells}}" as="cell">
<data-cell type="{{cell.type}}" value="{{cell.value}}"></data-cell>
</template>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'data-row',
properties: {
title: {
type: String,
notify: true
},
cells: {
type: Array,
notify: true
}
},
computeDominant: function(info) {
var types = this.types = info.base.reduce(function(map, i) {
return map[i.type] = (map[i.type] || 0) + 1, map;
}, {})
return emojiByType[Object.keys(types).sort(function(a, b) {
return types[b] - types[a];
})[0]];
}
});
});
</script>
</dom-module>
<dom-module id="data-table">
<template strip-whitespace>
<style>
:host {
display: block;
}
</style>
Polymer: {{version}}<br>
<button on-tap="toggleRunning">{{computeRunning(running)}}</button>
<input value-as-number="{{tables::input}}" type="number" style="width:35px;" placeholder="tables">
<input value-as-number="{{rows::input}}" type="number" style="width:35px;" placeholder="rows">
<input value-as-number="{{columns::input}}" type="number" style="width:35px;" placeholder="columns">
<br><br>
<select value="{{strategy::change}}">
<option value="reset">(Mutate in place x n), reset</option>
<option value="reset-each">(Mutate in place, reset) x n</option>
<option value="path">(Set path from top) x n</option>
<option value="path-leaf">(Set path from leaf) x n</option>
</select>
n = <input value-as-number="{{mutations::input}}" type="number" style="width:35px;">
<br><br>
<h3>FPS: {{fps}}</h3>
<h3>Op time: {{toFixed(optime, 2)}}</h3>
<div xhidden>
<template is="dom-repeat" items="{{tableData}}">
<h3>Table {{index}}:</h3>
<template is="dom-repeat" items="{{data}}" as="row">
<data-row title="{{row.title}}" cells="{{row.cells}}"></data-row>
</template>
</template>
</div>
</template>
<script>
HTMLImports.whenReady(function() {
var params = document.location.search.substring(1).split('&').map(p=>p.split('='))
.reduce((m, p)=>{return m[p[0]] = p[1], m;}, {});
Polymer({
is: 'data-table',
properties: {
rows: {
type: Number,
value: Number(params.rows) || 6
},
columns: {
type: Number,
value: Number(params.columns) || 10
},
tables: {
type: Number,
value: Number(params.tables) || 2
},
mutations: {
type: Number,
value: Number(params.mutations) || 30
},
strategy: {
type: String,
value: params.strategy || 'reset'
},
running: {
type: Number,
value: params.running === 'false' ? false : true
},
tableData: {
computed: 'computeTableData(tables)'
},
optime: {
value: 0
}
},
observers: ['computeData(rows, columns)'],
created: function() {
this._fps = 0;
this.end = performance.now() + 1000;
var cells = this.cells = [];
this.addEventListener('register-cell', function(e) {
var cell = e.composedPath()[0];
cell.id = 'cell-' + cells.length;
cells.push(cell);
});
},
toggleRunning: function() {
this.running = !this.running;
if (this.running) {
this.go();
}
},
computeRunning: function(running) {
return running ? 'Pause' : 'Run';
},
computeData: function(rows, columns) {
var r = [];
for (var i=0; i<rows; i++) {
var c = [];
for (var j=0; j<columns; j++) {
c.push({
value: Math.random() * 10,
type: ['a','b','c','d','e','f'][Math.floor(Math.random()*6)]
});
}
r.push({
cells: c,
title: 'Row ' + i
})
}
this.data = r;
},
computeTableData: function(tables) {
return new Array(Number(tables));
},
toFixed: function(value, decimals) {
return value.toFixed(decimals);
},
ready: function() {
this.version = Polymer.version || 'alacarte';
if (!this.setProperties) {
this.setProperties = function(props) {
for (var p in props) {
this.set(p, props[p]);
}
}
}
this.go();
},
go: function() {
var optime = 0;
for (var i=0; i<this.mutations; i++) {
var type = ['a','b','c','d','e','f'][Math.floor(Math.random()*6)];
var delta = (Math.random() > 0.5 ? -1 : 1) * (Math.random() * 5);
var row = Math.floor(Math.random() * this.rows);
var column = Math.floor(Math.random() * this.columns);
var d = this.data[row].cells[column];
if (this.strategy == 'reset' || this.strategy == 'reset-each') {
d.type = type;
d.value += delta;
if (this.strategy == 'reset-each') {
var start = performance.now();
this.set('data', this.data);
optime += (performance.now() - start);
}
} else if (this.strategy == 'path') {
var path = ['data', row, 'cells', column].join('.');
var props = {};
props[path + '.type'] = type;
props[path + '.value'] = d.value + delta;
var start = performance.now();
this.setProperties(props);
optime += (performance.now() - start);
} else if (this.strategy == 'path-leaf') {
var c = this.cells[Math.floor(this.cells.length * Math.random())];
var props = {
type: type,
value: c.value + delta
};
var start = performance.now();
c.setProperties(props);
optime += (performance.now() - start);
}
}
if (optime) {
this.optime = (this.optime * 9 + (optime / this.mutations)) / 10;
// this.optime = optime / this.mutations;
}
if (this.strategy == 'reset') {
var start = performance.now();
this.set('data', this.data);
this.optime = (this.optime * 9 + (performance.now() - start)) / 10;
// this.optime = performance.now() - start;
}
this._fps++;
if (performance.now() > this.end) {
this.fps = this._fps;
this._fps = 0;
this.end = performance.now() + 1000;
}
var self = this;
if (this.running) {
requestAnimationFrame(function() {
self.go();
});
}
}
});
});
</script>
</dom-module>
<data-table id="table"></data-table>
</body>
</html>