CorrelationAnalysis.vue 20 KB
Newer Older
1
<template>
2
  <div :class="`fjs-correlation-analysis fjs-vm-uid-${this._uid}`">
3
4
5
    <div class="fjs-data-box-container">
      <data-box class="fjs-data-box"
                header="X and Y variables"
6
7
8
                dataType="numerical"
                v-on:update="update_xyData">
      </data-box>
9
10
      <data-box class="fjs-data-box"
                header="Annotations"
11
12
13
                dataType="categorical"
                v-on:update="update_annotationData">
      </data-box>
14
    </div>
15

16
    <div class="fjs-parameter-container">
17
      <span>{{ error }}</span>
18
19
20
21
22
23
24
25
26
      <fieldset class="fjs-correlation-method">
        <legend>Correlation Method</legend>
        <input type="radio" id="fjs-param-method-1" value="pearson" v-model="params.method">
        <label for="fjs-param-method-1">Pearson</label>
        <input type="radio" id="fjs-param-method-2" value="spearman" v-model="params.method">
        <label for="fjs-param-method-2">Spearman</label>
        <input type="radio" id="fjs-param-method-3" value="kendall" v-model="params.method">
        <label for="fjs-param-method-3">Kendall</label>
      </fieldset>
27
    </div>
28

29
    <div class="fjs-vis-container">
30
      <svg :width="width"
31
           :height="height">
32
        <g :transform="`translate(${margin.left}, ${margin.top})`">
33
34
35
36
37
          <g class="fjs-corr-axis fjs-x-axis-1" :transform="`translate(0, ${padded.height})`"></g>
          <g class="fjs-corr-axis fjs-x-axis-2"></g>
          <g class="fjs-corr-axis fjs-y-axis-1"></g>
          <g class="fjs-corr-axis fjs-y-axis-2" :transform="`translate(${padded.width}, 0)`"></g>
          <g class="fjs-brush"></g>
38
39
40
41
42
43
44
45
46
47
48
49
50
          <text :x="padded.width / 2"
                y="-10"
                text-anchor="middle"
                font-size="16">
            {{ shownAnalysisResults.x_label }}
          </text>
          <text :x="padded.width + 10"
                :y="padded.height / 2"
                text-anchor="middle"
                font-size="16"
                :transform="`rotate(90 ${padded.width + 10} ${padded.height / 2})`">
            {{ shownAnalysisResults.y_label }}
          </text>
Sascha Herzinger's avatar
Sascha Herzinger committed
51
          <icon class="fjs-scatterplot-point"
52
                :shape="point.subset"
Sascha Herzinger's avatar
Sascha Herzinger committed
53
54
                :cx="scales.x(point.x)"
                :cy="scales.y(point.y)"
55
                :size="9"
56
57
                v-svgtooltip="point.tooltip"
                :fill="annotationColors[annotations.indexOf(point.annotation) % annotationColors.length]"
58
                :key="`${point.id}-${scales.x(point.x)}-${scales.y(point.y)}`"
59
                v-for="point in shownPoints.all">
Sascha Herzinger's avatar
Sascha Herzinger committed
60
          </icon>
61
          <line class="fjs-lin-reg-line"
62
63
64
                :x1="tweened.regLine.x1"
                :x2="tweened.regLine.x2"
                :y1="tweened.regLine.y1"
Sascha Herzinger's avatar
Sascha Herzinger committed
65
                :y2="tweened.regLine.y2"
66
                v-svgtooltip="regLine.tooltip">
67
          </line>
68
          <rect class="fjs-histogram-rect"
69
70
71
72
73
                :x="attr.x"
                :y="attr.y"
                :width="attr.width"
                :height="attr.height"
                v-for="attr in tweened.histogramAttr.xAttr">
Sascha Herzinger's avatar
Sascha Herzinger committed
74
          </rect>
75
          <rect class="fjs-histogram-rect"
76
77
78
79
80
                :x="attr.x"
                :y="attr.y"
                :width="attr.width"
                :height="attr.height"
                v-for="attr in tweened.histogramAttr.yAttr">
Sascha Herzinger's avatar
Sascha Herzinger committed
81
          </rect>
82
83
        </g>
      </svg>
84
85
86
87
      <div class="fjs-table-container">
        <table class="fjs-stats-table">
          <caption>Selected points</caption>
          <tr>
Sascha Herzinger's avatar
Sascha Herzinger committed
88
            <td>Coefficient</td>
89
90
91
92
93
94
95
            <td>{{ tmpAnalysisResults.coef }}</td>
          </tr>
          <tr>
            <td>p-value</td>
            <td>{{ tmpAnalysisResults.p_value }}</td>
          </tr>
          <tr>
Sascha Herzinger's avatar
Sascha Herzinger committed
96
            <td>Method</td>
97
98
99
100
101
102
103
104
            <td>{{ tmpAnalysisResults.method }}</td>
          </tr>
          <tr>
            <td>#Points</td>
            <td>{{ tmpPoints.all.length }}</td>
          </tr>
        </table>
        <table class="fjs-stats-table"
Sascha Herzinger's avatar
Sascha Herzinger committed
105
               v-for="(stats, i) in tmpAnalysisResults.subsets">
106
107
          <caption>Subset: {{ i + 1 }}</caption>
          <tr>
Sascha Herzinger's avatar
Sascha Herzinger committed
108
            <td>Coefficient</td>
109
110
111
112
113
114
115
            <td>{{ stats.coef }}</td>
          </tr>
          <tr>
            <td>p-value</td>
            <td>{{ stats.p_value }}</td>
          </tr>
          <tr>
Sascha Herzinger's avatar
Sascha Herzinger committed
116
            <td>Method</td>
117
118
119
120
121
122
123
124
            <td>{{ tmpAnalysisResults.method }}</td>
          </tr>
          <tr>
            <td>#Points</td>
            <td>{{ tmpPoints.subsets.filter(d => d === i).length }}</td>
          </tr>
        </table>
      </div>
125
    </div>
Sascha Herzinger's avatar
Sascha Herzinger committed
126
    <task-view></task-view>
127
  </div>
128
129
130
131
</template>


<script>
132
  import DataBox from '../DataBox.vue'
Sascha Herzinger's avatar
Sascha Herzinger committed
133
  import Icon from '../Icon.vue'
134
  import store from '../../store/store'
135
  import requestHandling from '../methods/run-analysis'
136
  import * as d3 from 'd3'
Sascha Herzinger's avatar
Sascha Herzinger committed
137
  import svgtooltip from '../directives/v-svgtooltip'
138
  import { TweenLite } from 'gsap'
Sascha Herzinger's avatar
Sascha Herzinger committed
139
  import TaskView from '../TaskView.vue'
140
141
  export default {
    name: 'correlation-analysis',
142
    data () {
143
      return {
144
        error: '',
145
146
        width: 0,
        height: 0,
147
148
        xyData: [],
        annotationData: [],
149
        annotationColors: d3.schemeCategory10,
150
151
152
        params: {
          method: 'pearson'
        },
153
        shownAnalysisResults: {
154
          init: true,  // will disappear after being initially set
155
156
157
158
159
160
161
          coef: 0,
          p_value: 0,
          slope: 0,
          intercept: 0,
          method: '',
          x_label: '',
          y_label: '',
Sascha Herzinger's avatar
Sascha Herzinger committed
162
          get data () {
163
164
165
166
167
168
            return {
              id: [],
              [this.x_label]: [],
              [this.y_label]: []
            }
          }
169
        },
170
        tmpAnalysisResults: {
171
          init: true,  // will disappear after being initially set
172
173
174
175
176
177
178
          coef: 0,
          p_value: 0,
          slope: 0,
          intercept: 0,
          method: '',
          x_label: '',
          y_label: '',
Sascha Herzinger's avatar
Sascha Herzinger committed
179
          get data () {
180
181
182
183
184
185
186
            return {
              id: [],
              [this.x_label]: [],
              [this.y_label]: []
            }
          }
        },
187
        selectedPoints: [],
188
189
190
191
192
        tweened: {
          regLine: {},
          histogramAttr: {
            xAttr: [],
            yAttr: []
193
          }
194
        }
195
196
      }
    },
197
    computed: {
198
199
200
      idFilter () {
        return store.getters.filter('ids')
      },
201
202
      disabled () {
        return this.xyData.length !== 2
203
      },
204
205
206
207
      args () {
        return {
          x: `$${this.xyData[0]}$`,
          y: `$${this.xyData[1]}$`,
208
          id_filter: this.selectedPoints.map(d => d.id),
209
210
          method: this.params.method,
          subsets: store.getters.subsets,
211
          annotations: this.annotationData.map(d => `$${d}$`)
212
213
        }
      },
Sascha Herzinger's avatar
Sascha Herzinger committed
214
215
      margin () {
        const left = this.width / 3
216
217
        const top = 50
        const right = 50
Sascha Herzinger's avatar
Sascha Herzinger committed
218
219
220
        const bottom = this.height / 3
        return { left, top, right, bottom }
      },
221
222
223
224
      padded () {
        const width = this.width - this.margin.left - this.margin.right
        const height = this.height - this.margin.top - this.margin.bottom
        return { width, height }
225
      },
226
227
228
      annotations () {
        return this.shownPoints.annotations.filter((d, i, arr) => arr.indexOf(d) === i)  // make unique
      },
229
      shownPoints () {
230
231
232
        const xs = []
        const ys = []
        const ids = []
233
        const subsets = []
234
        const annotations = []
235
        let all = []
236
        if (!this.shownAnalysisResults.init) {
237
238
239
240
241
242
          all = this.shownAnalysisResults.data.map(d => {
            const x = d[this.shownAnalysisResults.x_label]
            const y = d[this.shownAnalysisResults.y_label]
            const id = d.id
            const subset = d.subset
            const annotation = d.annotation
243
244
245
            const tooltip = {
              [this.shownAnalysisResults.x_label]: x,
              [this.shownAnalysisResults.y_label]: y,
246
247
248
249
              subset
            }
            if (typeof annotation !== 'undefined') {
              tooltip.annotation = annotation
250
            }
251
252
253
            xs.push(x)
            ys.push(y)
            ids.push(id)
254
            subsets.push(subset)
255
256
            annotations.push(annotation)
            return {x, y, id, subset, annotation, tooltip}
257
258
          })
        }
259
        return { xs, ys, ids, subsets, annotations, all }
260
      },
Sascha Herzinger's avatar
Sascha Herzinger committed
261
      tmpPoints () {
262
263
        const xs = []
        const ys = []
264
265
266
267
        const ids = []
        const subsets = []
        const annotations = []
        let all = []
268
        if (!this.tmpAnalysisResults.init) {
269
270
271
272
273
274
          all = this.tmpAnalysisResults.data.map(d => {
            const x = d[this.tmpAnalysisResults.x_label]
            const y = d[this.tmpAnalysisResults.y_label]
            const id = d.id
            const subset = d.subset
            const annotation = d.annotation
275
276
            xs.push(x)
            ys.push(y)
277
278
279
280
            ids.push(id)
            subsets.push(subset)
            annotations.push(annotation)
            return {x, y, id, subset, annotation}
281
282
          })
        }
283
        return { xs, ys, ids, subsets, annotations, all }
284
      },
285
286
      scales () {
        const x = d3.scaleLinear()
Sascha Herzinger's avatar
Sascha Herzinger committed
287
288
289
290
291
          .domain((() => {
            const xExtent = d3.extent(this.shownPoints.xs)
            const xPadding = (xExtent[1] - xExtent[0]) / 10
            return [xExtent[0] - xPadding, xExtent[1] + xPadding]
          })())
292
293
          .range([0, this.padded.width])
        const y = d3.scaleLinear()
Sascha Herzinger's avatar
Sascha Herzinger committed
294
295
296
297
298
          .domain((() => {
            const yExtent = d3.extent(this.shownPoints.ys)
            const yPadding = (yExtent[1] - yExtent[0]) / 10
            return [yExtent[0] - yPadding, yExtent[1] + yPadding]
          })())
299
300
301
302
303
304
305
306
307
308
309
310
311
312
          .range([this.padded.height, 0])
        return { x, y }
      },
      axis () {
        const x1 = d3.axisTop(this.scales.x)
        const y1 = d3.axisRight(this.scales.y)
        const x2 = d3.axisBottom(this.scales.x)
          .tickSizeInner(this.padded.height - 23)
          .tickFormat('')
        const y2 = d3.axisLeft(this.scales.y)
          .tickSizeInner(this.padded.width - 23)
          .tickFormat('')
        return { x1, x2, y1, y2 }
      },
313
      regLine () {
314
        if (this.tmpAnalysisResults.init) {
315
316
          return { x1: 0, x2: 0, y1: 0, y2: 0 }
        }
317
318
        const minX = d3.min(this.tmpPoints.xs)
        const maxX = d3.max(this.tmpPoints.xs)
319
        let x1 = this.scales.x(minX)
320
        let y1 = this.scales.y(this.tmpAnalysisResults.intercept + this.tmpAnalysisResults.slope * minX)
321
        let x2 = this.scales.x(maxX)
322
        let y2 = this.scales.y(this.tmpAnalysisResults.intercept + this.tmpAnalysisResults.slope * maxX)
323

324
325
        x1 = x1 < 0 ? 0 : x1
        x1 = x1 > this.width ? this.width : x1
326

327
328
        x2 = x2 < 0 ? 0 : x2
        x2 = x2 > this.width ? this.width : x2
329

330
331
        y1 = y1 < 0 ? 0 : y1
        y1 = y1 > this.height ? this.height : y1
332

333
334
        y2 = y2 < 0 ? 0 : y2
        y2 = y2 > this.height ? this.height : y2
335

Sascha Herzinger's avatar
Sascha Herzinger committed
336
337
338
        const tooltip = {Slope: this.tmpAnalysisResults.slope, Intercept: this.tmpAnalysisResults.intercept}

        return { x1, x2, y1, y2, tooltip }
339
340
341
342
      },
      brush () {
        return d3.brush()
          .extent([[0, 0], [this.padded.width, this.padded.height]])
343
          .on('end', () => {
344
            this.error = ''
345
            if (!d3.event.selection) {
346
              this.selectedPoints = []
347
            } else {
348
349
350
351
352
353
354
355
              const [[x0, y0], [x1, y1]] = d3.event.selection
              this.selectedPoints = this.shownPoints.all.filter(d => {
                const x = this.scales.x(d.x)
                const y = this.scales.y(d.y)
                return x0 <= x && x <= x1 && y0 <= y && y <= y1
              })
              if (this.selectedPoints.length > 0 && this.selectedPoints.length < 3) {
                this.error = 'Selection must be zero (everything is selected) or greater than two.'
Sascha Herzinger's avatar
Sascha Herzinger committed
356
                return
357
              }
358
            }
Sascha Herzinger's avatar
Sascha Herzinger committed
359
            store.dispatch('setFilter', {filter: 'ids', value: this.selectedPoints.map(d => d.id)})
360
          })
Sascha Herzinger's avatar
Sascha Herzinger committed
361
362
      },
      histograms () {
363
        const BINS = 14
364
365
366
        let xBins = []
        let yBins = []
        if (!this.tmpAnalysisResults.init) {
367
368
          const [xMin, xMax] = d3.extent(this.tmpPoints.xs)
          const [yMin, yMax] = d3.extent(this.tmpPoints.ys)
369
370
371
          const xThresholds = d3.range(xMin, xMax, (xMax - xMin) / BINS)
          const yThresholds = d3.range(yMin, yMax, (yMax - yMin) / BINS)
          xBins = d3.histogram()
372
            .domain([xMin, xMax])
373
374
            .thresholds(xThresholds)(this.tmpPoints.xs)
          yBins = d3.histogram()
375
            .domain([yMin, yMax])
376
377
            .thresholds(yThresholds)(this.tmpPoints.ys)
        }
Sascha Herzinger's avatar
Sascha Herzinger committed
378
379
380
        return { xBins, yBins }
      },
      histogramScales () {
381
382
        const xExtent = d3.extent(this.histograms.xBins.map(d => d.length))
        const yExtent = d3.extent(this.histograms.yBins.map(d => d.length))
383
        // no, I didn't mix up x and y.
Sascha Herzinger's avatar
Sascha Herzinger committed
384
        const x = d3.scaleLinear()
385
386
          .domain(yExtent)
          .range([yExtent[0] ? 10 : 0, this.margin.left])
Sascha Herzinger's avatar
Sascha Herzinger committed
387
        const y = d3.scaleLinear()
388
389
          .domain(xExtent)
          .range([xExtent[0] ? 10 : 0, this.margin.bottom])
Sascha Herzinger's avatar
Sascha Herzinger committed
390
391
        return { x, y }
      },
392
393
394
395
      histogramAttr () {
        const xAttr = this.histograms.xBins.map(d => {
          return {
            x: this.scales.x(d.x0),
Sascha Herzinger's avatar
Sascha Herzinger committed
396
            y: this.padded.height + 1,
397
398
399
400
401
402
            width: this.scales.x(d.x1) - this.scales.x(d.x0),
            height: this.histogramScales.y(d.length)
          }
        })
        const yAttr = this.histograms.yBins.map(d => {
          return {
403
            x: -this.histogramScales.x(d.length),
404
405
406
407
408
409
410
411
            y: this.scales.y(d.x1),
            width: this.histogramScales.x(d.length),
            height: this.scales.y(d.x0) - this.scales.y(d.x1)
          }
        })

        return { xAttr, yAttr }
      }
412
413
    },
    watch: {
414
415
      'args': {
        handler: function (newArgs, oldArgs) {
Sascha Herzinger's avatar
Sascha Herzinger committed
416
          // if our data selection change we will want to re-initialize the current view
417
418
419
          const init = newArgs.x !== oldArgs.x ||
            newArgs.y !== oldArgs.y ||
            JSON.stringify(newArgs.annotations) !== JSON.stringify(oldArgs.annotations)
Sascha Herzinger's avatar
Sascha Herzinger committed
420
421
          const args = this.args
          args.id_filter = init ? [] : args.id_filter
422
          if (!this.disabled) {
Sascha Herzinger's avatar
Sascha Herzinger committed
423
            this.runAnalysisWrapper({init, args})
424
425
426
          }
        }
      },
427
      'axis': {
428
        handler: function (newAxis) {
429
430
431
432
          d3.select(`.fjs-vm-uid-${this._uid} .fjs-x-axis-1`).call(newAxis.x1)
          d3.select(`.fjs-vm-uid-${this._uid} .fjs-x-axis-2`).call(newAxis.x2)
          d3.select(`.fjs-vm-uid-${this._uid} .fjs-y-axis-1`).call(newAxis.y1)
          d3.select(`.fjs-vm-uid-${this._uid} .fjs-y-axis-2`).call(newAxis.y2)
433
        }
434
435
      },
      'brush': {
436
        handler: function (newBrush) {
437
          d3.select(`.fjs-vm-uid-${this._uid} .fjs-brush`).call(newBrush)
438
        }
439
440
      },
      'regLine': {
441
        handler: function (newRegLine, oldRegLine) {
442
443
444
445
446
447
448
          const coords = oldRegLine
          const targetCoords = newRegLine
          targetCoords.onUpdate = () => { this.tweened.regLine = coords }
          TweenLite.to(coords, 0.5, targetCoords)
        }
      },
      'histogramAttr': {
449
        handler: function (newHistogramAttr, oldHistogramAttr) {
450
451
452
          let i = Math.max.apply(null, [newHistogramAttr.xAttr.length, oldHistogramAttr.xAttr.length])
          let j = Math.max.apply(null, [newHistogramAttr.yAttr.length, oldHistogramAttr.yAttr.length])

453
          while (i--) {
454
            const ii = i
455
            let xAttr = oldHistogramAttr.xAttr[i] ? oldHistogramAttr.xAttr[i]
Sascha Herzinger's avatar
Sascha Herzinger committed
456
              : { x: this.padded.width / 2, y: this.padded.height, width: 0, height: 0 }
457
458
459
            const xAttrTarget = newHistogramAttr.xAttr[i] ? newHistogramAttr.xAttr[i] : { width: 0 }
            xAttrTarget.onUpdate = () => { this.tweened.histogramAttr.xAttr[ii] = xAttr }
            TweenLite.to(xAttr, 0.5, xAttrTarget)
460
461
          }

462
          while (j--) {
463
            const jj = j
464
            const yAttr = oldHistogramAttr.yAttr[j] ? oldHistogramAttr.yAttr[j]
Sascha Herzinger's avatar
Sascha Herzinger committed
465
              : { x: 0, y: this.padded.height / 2, width: 0, height: 0 }
466
467
468
            const yAttrTarget = newHistogramAttr.yAttr[j] ? newHistogramAttr.yAttr[j] : { height: 0 }
            yAttrTarget.onUpdate = () => { this.tweened.histogramAttr.yAttr[jj] = yAttr }
            TweenLite.to(yAttr, 0.5, yAttrTarget)
469
470
          }
        }
471
472
473
474
475
476
      },
      'idFilter': {
        handler: function (newIDFilter) {
          const isFiltered = (newIDFilter.length === this.selectedPoints.length) &&
            this.selectedPoints.map(d => d.id).every(id => newIDFilter.indexOf(id) !== -1)
          if (!isFiltered) {
Sascha Herzinger's avatar
Sascha Herzinger committed
477
478
            // FIXME: This will probably not work because args is a computed property, not static data
            this.args.id_filter = newIDFilter
479
480
          }
        }
481
482
      }
    },
Sascha Herzinger's avatar
Sascha Herzinger committed
483
    mounted () {
484
      window.addEventListener('resize', this.handleResize)
485
      this.handleResize()
486
    },
Sascha Herzinger's avatar
Sascha Herzinger committed
487
    beforeDestroy () {
488
      window.removeEventListener('resize', this.handleResize)
489
    },
490
    components: {
Sascha Herzinger's avatar
Sascha Herzinger committed
491
      DataBox,
Sascha Herzinger's avatar
Sascha Herzinger committed
492
493
      Icon,
      TaskView
494
495
    },
    mixins: [
Sascha Herzinger's avatar
Sascha Herzinger committed
496
      requestHandling,
497
      svgtooltip
498
499
    ],
    methods: {
500
      runAnalysisWrapper ({init, args}) {
501
        // function made available via requestHandling mixin
502
        this.runAnalysis({task_name: 'compute-correlation', args})
503
          .then(response => {
504
            const results = JSON.parse(response)
505
506
            const data = JSON.parse(results.data)
            results.data = Object.keys(data).map(key => data[key])
507
508
509
510
511
512
            if (init) {
              this.shownAnalysisResults = results
              this.tmpAnalysisResults = results
            } else {
              this.tmpAnalysisResults = results
            }
513
          })
514
          .catch(error => console.error(error))
515
          .then(this.handleResize)
516
      },
517
      handleResize () {
518
        const container = this.$el.querySelector(`.fjs-vm-uid-${this._uid} .fjs-vis-container svg`)
519
        // noinspection JSSuspiciousNameCombination
520
521
        this.height = container.getBoundingClientRect().width
        this.width = container.getBoundingClientRect().width
522
523
524
525
526
527
      },
      update_xyData (ids) {
        this.xyData = ids
      },
      update_annotationData (ids) {
        this.annotationData = ids
528
      }
529
530
531
532
533
    }
  }
</script>


534
535
536
537
538
539
<style lang="sass" scoped>
  @import './src/assets/base.sass'

  *
    font-family: Roboto, sans-serif

540
  .fjs-correlation-analysis
541
542
    height: 100%
    width: 100%
543
544
    display: flex
    flex-direction: column
545

546
547
    .fjs-data-box-container
      height: 160px
Sascha Herzinger's avatar
Sascha Herzinger committed
548
549
      display: flex
      justify-content: space-around
550

551
    .fjs-parameter-container
552
      text-align: center
553
554
555
      .fjs-correlation-method
        width: 0
        white-space: nowrap
Sascha Herzinger's avatar
Sascha Herzinger committed
556
557
558
559
        border: solid 1px #bbb
        text-align: left
        border-radius: 8px
        margin: 10px
560

561

562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
    .fjs-vis-container
      flex: 1
      display: flex
      svg
        flex: 1
        .fjs-lin-reg-line
          stroke: #ff5e00
          stroke-width: 4px
        .fjs-lin-reg-line:hover
          opacity: 0.4
        .fjs-histogram-rect
          stroke: #fff
          shape-rendering: crispEdges
          stroke-width: 0px
          fill: #ffd100
        .fjs-scatterplot-point
          stroke-width: 0
          shape-rendering: crispEdges
        .fjs-scatterplot-point:hover
          fill: #f00
        .fjs-brush
          stroke-width: 0
      .fjs-table-container
Sascha Herzinger's avatar
Sascha Herzinger committed
585
        width: 200px
586
587
588
589
590
591
592
        display: flex
        flex-direction: column
        .fjs-stats-table
          margin: 5px
          border-spacing: 0
          border-collapse: collapse
          font-size: 14px
Sascha Herzinger's avatar
Sascha Herzinger committed
593
594
595
596
597
598
599
600
601
602
          tr:nth-child(even)
            background-color: #ddd
          td, th
            max-width: 100px
            overflow: hidden
            text-overflow: ellipsis
            white-space: nowrap
            border: 1px #ccc solid
            border-collapse: collapse
            padding: 5px
603
604
605
</style>

<!--CSS for dynamically created components-->
606
607
608
609
610
611
612
613
614
<style lang="sass">
  @import './src/assets/svgtooltip.sass'

  .fjs-corr-axis
    shape-rendering: crispEdges
    .tick
      shape-rendering: crispEdges
    line
      stroke: #999
615
</style>