diff options
Diffstat (limited to 'tensorflow/tensorboard/components/tf-multi-checkbox')
-rw-r--r-- | tensorflow/tensorboard/components/tf-multi-checkbox/demo/index.html | 162 | ||||
-rw-r--r-- | tensorflow/tensorboard/components/tf-multi-checkbox/tf-multi-checkbox.html | 228 |
2 files changed, 390 insertions, 0 deletions
diff --git a/tensorflow/tensorboard/components/tf-multi-checkbox/demo/index.html b/tensorflow/tensorboard/components/tf-multi-checkbox/demo/index.html new file mode 100644 index 0000000000..e5661b98bc --- /dev/null +++ b/tensorflow/tensorboard/components/tf-multi-checkbox/demo/index.html @@ -0,0 +1,162 @@ +<!DOCTYPE html> +<html> +<head> +<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> +<script src="../../../bower_components/d3/d3.js"></script> +<link rel="import" href="../tf-multi-checkbox.html"> +<link rel="import" href="../../tf-event-dashboard/tf-color-scale.html"> +<link rel="stylesheet" type="text/css" href="../../../lib/css/global.css"> + +</head> +<body> +<script> +var seed = 1; +function random() { + var x = Math.sin(seed++) * 10000; + return x - Math.floor(x); +} +</script> +<style> +</style> + +<dom-module id="mc-demo"> + <template> + <tf-multi-checkbox + id="multiCheckbox" + names="[[names]]" + tooltips="[[_tooltips]]" + class-scale="[[classScale]]" + highlights="[[highlights]]" + ></tf-multi-checkbox> + <tf-color-scale + id="colorScale" + runs="[[names]]" + out-class-scale="{{classScale}}" + ></tf-color-scale> + <style> + </style> + </template> + <script> + + function randomTooltip() { + var s = ""; + while (random() < 0.8) { + s += String(10*random())[0]; + } + return s; + } + Polymer({ + is: "mc-demo", + properties: { + names: Array, + tooltips: Object, + autoGenerateTooltips: {value: true}, + _tooltips: Object, + classScale: Function, + highlights: Array, + }, + observers: [ + 'autogenerate(names, autoGenerateTooltips)', + 'randomHighlights(names)' + ], + autogenerate: function(names, autoGenerateTooltips) { + if (autoGenerateTooltips) { + var tooltips = {}; + names.forEach(function(n) { + if (random() > 0.5) { + tooltips[n] = randomTooltip(); + } + }); + this._tooltips = tooltips; + } + }, + randomHighlights: function(names) { + var h = []; + names.forEach(function(n) { + if (random() > 0.6) { + h.push(n); + } + }); + this.highlights = h; + } + }); + </script> +</dom-module> + +<dom-module id="x-demo"> +<style> +.small { + width: 200px; + height: 500px; +} +.large { + width: 500px; + height: 900px; +} +html,body { + height: 100%; +} +mc-demo { + padding: 5px; + border: 1px solid var(--paper-red-500); + display: inline-block; +} +</style> +<template> + <div class="demo-block"> + <mc-demo id="demo1" class="small" names="[[long_names]]"></mc-demo> + <mc-demo class="small" names="[[many_names]]"></mc-demo> + <mc-demo class="small" names="[[many_long_names]]"></mc-demo> + </div> + + <div class="demo-block"> + <mc-demo class="large" names="[[long_names]]"></mc-demo> + <mc-demo class="large" names="[[many_names]]"></mc-demo> + <mc-demo class="large" names="[[many_long_names]]"></mc-demo> + </div> + +</template> +<script> + +function long_names() { + return [ + "foo_bar very long name with spaces", + "the quick brown fox jumped over the lazy dog", + "supercalifragilisticexpialodcious/bar/foo/zod/longer/longer", + ]; +} + +function many_names() { + var out = []; + for (var i=0; i<20; i++) { + out.push("foo_bar-" + i); + out.push("bar_zod_bing-" + i); + out.push("lol-" + i); + } + return out; +} + +function many_long_names() { + var out = []; + for (var i=0; i<20; i++) { + out.push("foo_bar very very very long some spaces though-" + i); + out.push("bar_zod_bing_bas_womp_wub_wub_dub_wub_wub-" + i); + out.push("rightly_to_be_great_is_not_to_stir_without_great_argument_but_greatly_to_find_quarrel_in_a_straw_when_honors_at_the_stake-" + i); + } + return out; +} + +Polymer({ + is: "x-demo", + properties: { + long_names: {type: Array, value: long_names}, + many_names: {type: Array, value: many_names}, + many_long_names: {type: Array, value: many_long_names}, +}, +}); +</script> +</dom-module> + +<x-demo id="demo"></x-demo> +</body> +</html> diff --git a/tensorflow/tensorboard/components/tf-multi-checkbox/tf-multi-checkbox.html b/tensorflow/tensorboard/components/tf-multi-checkbox/tf-multi-checkbox.html new file mode 100644 index 0000000000..a5447e8f5e --- /dev/null +++ b/tensorflow/tensorboard/components/tf-multi-checkbox/tf-multi-checkbox.html @@ -0,0 +1,228 @@ +<link rel="import" href="../../bower_components/polymer/polymer.html"> +<link rel="import" href="../../bower_components/paper-checkbox/paper-checkbox.html"> +<link rel="import" href="../imports/lodash.html"> +<link rel="import" href="../tf-dashboard-common/scrollbar-style.html"> +<link rel="import" href="../tf-dashboard-common/run-color-style.html"> +<!-- +tf-multi-checkbox creates a list of checkboxes that can be used to toggle on or off +a large number of values. Each checkbox displays a name, and may also have an +assosciated tooltip value. Checkboxes can be highlighted, hidden, and re-ordered. + +tf-multi-checkbox assumes that the names may be very long compared to the width +of the checkbox, and the number of names may also be very large, and works to +handle these situations gracefully. + +API: + +Properties in: +names: The string names to associate with checkboxes. +tooltips: An object mapping from name to tooltip value. +tooltipOrderer: A function that is used to compute how to order the names based +on tooltip values (when available). If tooltip values and a tooltip orderer are +present, the tooltipOrderer computes a numeric value for each tooltip, tooltips +with higher values are ordered first, tooltips with equal values are ordered +lexicogrpahically, and tooltips without a value are placed last. If the +tooltipOrderer is set to a falsey value (eg null), then the names are not +re-ordered based on tooltip value. +classScale: A function that maps from name to class name, which is applied as +the special property color-class. This is intended to be used to color the names. +hideMissingTooltips: If set, then when tooltips are present, any names that do +not have an associate non-empty tooltip value will be hidden. + +Properties out: +outSelected: An array of names that the user has checked. +If the user does not interact, everything will be checked. + +--> + +<dom-module id="tf-multi-checkbox"> + <style include="scrollbar-style"></style> + <style include="run-color-style"></style> + + <template> + <div id="outer-container" class="scrollbar"> + <template + is="dom-repeat" + items="[[names]]" + sort="[[_tooltipComparator(tooltips, tooltipOrderer)]]" + > + <div + class="run-row" + color-class$="[[_applyColorClass(item, classScale)]]" + null-tooltip$="[[_isNullTooltip(item, tooltips)]]" + highlight$="[[_isHighlighted(item, highlights.*)]]" + > + <div class="checkbox-container vertical-align-container"> + <paper-checkbox + class="checkbox vertical-align-center" + name="[[item]]" + checked$="[[_isChecked(item,outSelected.*)]]" + on-change="_checkboxChange" + ></paper-checkbox> + </div> + <div class="item-label-container"> + <span>[[item]]</span> + </div> + <div class="tooltip-value-container vertical-align-container"> + <span class="vertical-align-top">[[_lookupTooltip(item,tooltips)]]</span> + </div> + </div> + </template> + </div> + <style> + :host { + display: flex; + flex-direction: column; + height: 100%; + } + #outer-container { + overflow-y: scroll; + overflow-x: hidden; + width: 100%; + flex-grow: 1; + flex-shrink: 1; + word-wrap: break-word; + } + .run-row { + padding-top: 5px; + padding-bottom: 5px; + display: flex; + flex-direction: row; + font-size: 13px; + } + .checkbox-container { + flex-grow: 0; + flex-shrink: 0; + } + .checkbox { + padding-left: 2px; + width: 32px; + } + .item-label-container { + flex-grow: 1; + flex-shrink: 1; + width: 0px; /* hack to get the flex-grow to work properly */ + } + .tooltip-value-container { + display: flex; + justify-content: center; + flex-grow: 0; + flex-shrink: 0; + text-align:right; + padding-left: 2px; + } + .vertical-align-container { + display: flex; + justify-content: center; + } + .vertical-align-container .vertical-align-center { + align-self: center; + } + .vertical-align-container .vertical-align-top { + align-self: start; + } + [null-tooltip] { + display: none; + } + [highlight] { + font-weight: bold; + } + </style> + </template> + + <script> + Polymer({ + is: "tf-multi-checkbox", + properties: { + names: Array, + tooltipOrderer: { + /* Used to compute how to order the tooltips based on the tooltip value. + * By default, it parses the tooltip strings as numbers. + * If set to a falsey value, tooltips are always ordered lexicographically. + */ + type: Function, + value: function() { + return function(x) {return +x;} + }, + }, + tooltips: Object, + highlights: Array, + outSelected: { + type: Array, + notify: true, + value: function() { + return []; + }, + }, + hideMissingTooltips: { + // If we have tooltips, but some names are missing, do we hide them? + type: Boolean, + value: true, + }, + classScale: Function, // map from run name to css class + }, + observers: [ + "_initializeOutSelected(names.*)", + ], + _lookupTooltip: function(item, tooltips) { + return tooltips != null ? tooltips[item] : null; + }, + _isNullTooltip: function(item, tooltips) { + if (!this.hideMissingTooltips) { + return true; + } + if (tooltips == null) { + return false; + } + return tooltips[item] == null; + }, + _initializeOutSelected: function(change) { + this.outSelected = change.base.slice(); + }, + _tooltipComparator: function(tooltips, tooltipOrderer) { + return function(a, b) { + if (!tooltips || !tooltipOrderer) { + // if we're missing tooltips or orderer, do lexicogrpahic sort + return a.localeCompare(b); + } + function getValue(x) { + var value = tooltipOrderer(tooltips[x]); + return value == null || _.isNaN(value) ? -Infinity : value; + } + var aValue = getValue(a); + var bValue = getValue(b); + return aValue === bValue ? a.localeCompare(b) : bValue - aValue; + } + }, + _checkboxChange: function(e) { + var name = e.srcElement.name; + var idx = this.outSelected.indexOf(name); + var checked = e.srcElement.checked; + if (checked && idx === -1) { + this.push("outSelected", name); + } else if (!checked && idx !== -1) { + this.splice("outSelected", idx, 1); + } + }, + _isChecked: function(item, outSelectedChange) { + var outSelected = outSelectedChange.base; + return outSelected.indexOf(item) !== -1; + }, + _initializeRuns: function(change) { + this.outSelected = change.base.slice(); + }, + _applyColorClass: function(item, classScale) { + // TODO: Update style just on the element that changes + // and apply at microtask timing + this.debounce("restyle", function (){ + this.updateStyles(); + }, 16); + return classScale(item); + }, + _isHighlighted: function(item, highlights) { + return highlights.base.indexOf(item) !== -1; + }, + }); + </script> + +</dom-module> |