aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/tensorboard/components/tf-multi-checkbox/tf-multi-checkbox.html
blob: a5447e8f5e1fdc09663b5ddd9548115d52030c11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
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>