fluency-v8-components 1.5.9 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.css CHANGED
@@ -1 +1 @@
1
- .x-center[data-v-59ba8a5f]{transform:translate(-50%)}.left-tooltip[data-v-59ba8a5f]{transform:translate(-120%)}.right-tooltip[data-v-59ba8a5f]{transform:translate(20%)}.prism-editor-wrapper{width:100%;height:100%;display:flex;align-items:flex-start;overflow:auto;-o-tab-size:1.5em;tab-size:1.5em;-moz-tab-size:1.5em}@media (-ms-high-contrast:active),(-ms-high-contrast:none){.prism-editor-wrapper .prism-editor__textarea{color:transparent!important}.prism-editor-wrapper .prism-editor__textarea::-moz-selection{background-color:#accef7!important;color:transparent!important}.prism-editor-wrapper .prism-editor__textarea::selection{background-color:#accef7!important;color:transparent!important}}.prism-editor-wrapper .prism-editor__container{position:relative;text-align:left;box-sizing:border-box;padding:0;overflow:hidden;width:100%}.prism-editor-wrapper .prism-editor__line-numbers{height:100%;overflow:hidden;flex-shrink:0;padding-top:4px;margin-top:0;margin-right:10px}.prism-editor-wrapper .prism-editor__line-number{text-align:right;white-space:nowrap}.prism-editor-wrapper .prism-editor__textarea{position:absolute;top:0;left:0;height:100%;width:100%;resize:none;color:inherit;overflow:hidden;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;-webkit-text-fill-color:transparent}.prism-editor-wrapper .prism-editor__editor,.prism-editor-wrapper .prism-editor__textarea{margin:0;border:0;background:none;box-sizing:inherit;display:inherit;font-family:inherit;font-size:inherit;font-style:inherit;font-variant-ligatures:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;-moz-tab-size:inherit;-o-tab-size:inherit;tab-size:inherit;text-indent:inherit;text-rendering:inherit;text-transform:inherit;white-space:pre-wrap;word-wrap:keep-all;overflow-wrap:break-word;padding:0}.prism-editor-wrapper .prism-editor__textarea--empty{-webkit-text-fill-color:inherit!important}.prism-editor-wrapper .prism-editor__editor{position:relative;pointer-events:none}pre[class*=language-],code[class*=language-]{color:#d4d4d4;font-size:13px;text-shadow:none;font-family:Menlo,Monaco,Consolas,Andale Mono,Ubuntu Mono,Courier New,monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]::selection,code[class*=language-]::selection,pre[class*=language-] *::selection,code[class*=language-] *::selection{text-shadow:none;background:#264f78}@media print{pre[class*=language-],code[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;background:#1e1e1e}:not(pre)>code[class*=language-]{padding:.1em .3em;border-radius:.3em;color:#db4c69;background:#1e1e1e}.namespace{opacity:.7}.token.doctype .token.doctype-tag{color:#569cd6}.token.doctype .token.name{color:#9cdcfe}.token.comment,.token.prolog,.token.timestamp{color:#6a9955}.token.errors{color:#c10015}.token.punctuation,.language-html .language-css,.language-html .language-javascript{color:#fdbe23}.token.property,.token.tag,.token.boolean,.token.number,.token.constant,.token.symbol,.token.inserted,.token.unit{color:#b5cea8}.token.selector,.token.attr-name,.token.string,.token.char,.token.builtin,.token.deleted{color:#ce9178}.language-css .token.string.url{text-decoration:underline}.token.operator,.token.entity{color:#d4d4d4}.token.operator.arrow{color:#569cd6}.token.atrule{color:#ce9178}.token.atrule .token.rule{color:#c586c0}.token.atrule .token.url{color:#9cdcfe}.token.atrule .token.url .token.function{color:#dcdcaa}.token.atrule .token.url .token.punctuation{color:#d4d4d4}.token.keyword.module,.token.keyword.control-flow{color:#c586c0}.token.function,.token.function .token.maybe-class-name,.token.brackets{color:#e9d299}.token.builtinfunction{color:#ce92d8}.token.keyword,.token.curlybraces{color:#569cd6}.token.regex{color:#d16969}.token.important{color:#569cd6}.token.italic{font-style:italic}.token.constant{color:#9cdcfe}.token.class-name,.token.maybe-class-name{color:#4ec9b0}.token.console,.token.parameter,.token.interpolation{color:#9cdcfe}.token.punctuation.interpolation-punctuation,.token.boolean{color:#569cd6}.token.property,.token.variable,.token.imports .token.maybe-class-name,.token.exports .token.maybe-class-name{color:#9cdcfe}.token.selector,.token.escape{color:#d7ba7d}.token.tag{color:#569cd6}.token.tag .token.punctuation,.token.cdata{color:gray}.token.attr-name{color:#9cdcfe}.token.attr-value,.token.attr-value .token.punctuation{color:#ce9178}.token.attr-value .token.punctuation.attr-equals{color:#d4d4d4}.token.entity{color:#569cd6}.token.namespace{color:#4ec9b0}pre[class*=language-javascript],code[class*=language-javascript],pre[class*=language-jsx],code[class*=language-jsx],pre[class*=language-typescript],code[class*=language-typescript],pre[class*=language-tsx],code[class*=language-tsx]{color:#9cdcfe}pre[class*=language-css],code[class*=language-css]{color:#ce9178}pre[class*=language-html],code[class*=language-html]{color:#d4d4d4}.language-regex .token.anchor{color:#dcdcaa}.language-html .token.punctuation{color:gray}pre[class*=language-]>code[class*=language-]{position:relative;z-index:1}.line-highlight.line-highlight{background:#f7ebc6;box-shadow:inset 5px 0 #f7d87c;z-index:0}.primary[data-v-2890730f]{font-weight:400;padding-top:5px}.primary[data-v-2890730f]:first-child{font-weight:400}
1
+ .x-center[data-v-59ba8a5f]{transform:translate(-50%)}.left-tooltip[data-v-59ba8a5f]{transform:translate(-120%)}.right-tooltip[data-v-59ba8a5f]{transform:translate(20%)}.prism-editor-wrapper{width:100%;height:100%;display:flex;align-items:flex-start;overflow:auto;-o-tab-size:1.5em;tab-size:1.5em;-moz-tab-size:1.5em}@media (-ms-high-contrast:active),(-ms-high-contrast:none){.prism-editor-wrapper .prism-editor__textarea{color:transparent!important}.prism-editor-wrapper .prism-editor__textarea::-moz-selection{background-color:#accef7!important;color:transparent!important}.prism-editor-wrapper .prism-editor__textarea::selection{background-color:#accef7!important;color:transparent!important}}.prism-editor-wrapper .prism-editor__container{position:relative;text-align:left;box-sizing:border-box;padding:0;overflow:hidden;width:100%}.prism-editor-wrapper .prism-editor__line-numbers{height:100%;overflow:hidden;flex-shrink:0;padding-top:4px;margin-top:0;margin-right:10px}.prism-editor-wrapper .prism-editor__line-number{text-align:right;white-space:nowrap}.prism-editor-wrapper .prism-editor__textarea{position:absolute;top:0;left:0;height:100%;width:100%;resize:none;color:inherit;overflow:hidden;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;-webkit-text-fill-color:transparent}.prism-editor-wrapper .prism-editor__editor,.prism-editor-wrapper .prism-editor__textarea{margin:0;border:0;background:none;box-sizing:inherit;display:inherit;font-family:inherit;font-size:inherit;font-style:inherit;font-variant-ligatures:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;-moz-tab-size:inherit;-o-tab-size:inherit;tab-size:inherit;text-indent:inherit;text-rendering:inherit;text-transform:inherit;white-space:pre-wrap;word-wrap:keep-all;overflow-wrap:break-word;padding:0}.prism-editor-wrapper .prism-editor__textarea--empty{-webkit-text-fill-color:inherit!important}.prism-editor-wrapper .prism-editor__editor{position:relative;pointer-events:none}pre[class*=language-],code[class*=language-]{color:#d4d4d4;font-size:13px;text-shadow:none;font-family:Menlo,Monaco,Consolas,Andale Mono,Ubuntu Mono,Courier New,monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]::selection,code[class*=language-]::selection,pre[class*=language-] *::selection,code[class*=language-] *::selection{text-shadow:none;background:#264f78}@media print{pre[class*=language-],code[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;background:#1e1e1e}:not(pre)>code[class*=language-]{padding:.1em .3em;border-radius:.3em;color:#db4c69;background:#1e1e1e}.namespace{opacity:.7}.token.doctype .token.doctype-tag{color:#569cd6}.token.doctype .token.name{color:#9cdcfe}.token.comment,.token.prolog,.token.timestamp{color:#6a9955}.token.errors{color:#c10015}.token.punctuation,.language-html .language-css,.language-html .language-javascript{color:#fdbe23}.token.property,.token.tag,.token.boolean,.token.number,.token.constant,.token.symbol,.token.inserted,.token.unit{color:#b5cea8}.token.selector,.token.attr-name,.token.string,.token.char,.token.builtin,.token.deleted{color:#ce9178}.language-css .token.string.url{text-decoration:underline}.token.operator,.token.entity{color:#d4d4d4}.token.operator.arrow{color:#569cd6}.token.atrule{color:#ce9178}.token.atrule .token.rule{color:#c586c0}.token.atrule .token.url{color:#9cdcfe}.token.atrule .token.url .token.function{color:#dcdcaa}.token.atrule .token.url .token.punctuation{color:#d4d4d4}.token.keyword.module,.token.keyword.control-flow{color:#c586c0}.token.function,.token.function .token.maybe-class-name,.token.brackets{color:#e9d299}.token.builtinfunction{color:#ce92d8}.token.keyword,.token.curlybraces{color:#569cd6}.token.regex{color:#d16969}.token.important{color:#569cd6}.token.italic{font-style:italic}.token.constant{color:#9cdcfe}.token.class-name,.token.maybe-class-name{color:#4ec9b0}.token.console,.token.parameter,.token.interpolation{color:#9cdcfe}.token.punctuation.interpolation-punctuation,.token.boolean{color:#569cd6}.token.property,.token.variable,.token.imports .token.maybe-class-name,.token.exports .token.maybe-class-name{color:#9cdcfe}.token.selector,.token.escape{color:#d7ba7d}.token.tag{color:#569cd6}.token.tag .token.punctuation,.token.cdata{color:gray}.token.attr-name{color:#9cdcfe}.token.attr-value,.token.attr-value .token.punctuation{color:#ce9178}.token.attr-value .token.punctuation.attr-equals{color:#d4d4d4}.token.entity{color:#569cd6}.token.namespace{color:#4ec9b0}pre[class*=language-javascript],code[class*=language-javascript],pre[class*=language-jsx],code[class*=language-jsx],pre[class*=language-typescript],code[class*=language-typescript],pre[class*=language-tsx],code[class*=language-tsx]{color:#9cdcfe}pre[class*=language-css],code[class*=language-css]{color:#ce9178}pre[class*=language-html],code[class*=language-html]{color:#d4d4d4}.language-regex .token.anchor{color:#dcdcaa}.language-html .token.punctuation{color:gray}pre[class*=language-]>code[class*=language-]{position:relative;z-index:1}.line-highlight.line-highlight{background:#f7ebc6;box-shadow:inset 5px 0 #f7d87c;z-index:0}.primary[data-v-2890730f]{font-weight:400;padding-top:5px}.primary[data-v-2890730f]:first-child{font-weight:400}.rangeslider-container .axis path,.rangeslider-container .axis line{fill:none;stroke:#000;shape-rendering:crispEdges}.rangeslider-container .axis text{font:9px sans-serif}.rangeslider-container line.selected{stroke:#ff4800;stroke-width:4}.rangeslider-container .axis.path,.rangeslider-container .axis.line{fill:none;stroke:#000;shape-rendering:crispEdges}.rangeslider-container .axis.text{font:9px sans-serif}.rangeslider-container line.selected{stroke:#027be3;stroke-width:2}.rangeslider-container .brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.rangeslider-container .resize{display:inline!important;fill:#027be3;fill-opacity:1;stroke:gray;stroke-width:0px}.resize:focus-visible{outline:3px solid black}
@@ -1,4 +1,4 @@
1
- import { c as Da, _ as Va, g as il } from "./index-Bhs-DoPS.mjs";
1
+ import { c as Da, _ as Va, g as il } from "./index-BAGLjhJi.mjs";
2
2
  var fn = {}, cn = {}, cr, vn;
3
3
  function Q() {
4
4
  if (vn) return cr;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluency-v8-components",
3
- "version": "1.5.9",
3
+ "version": "1.6.1",
4
4
  "main": "dist/fluency-v8-components.umd.js",
5
5
  "module": "dist/fluency-v8-components.es.js",
6
6
  "types": "dist/index.d.ts",
@@ -144,7 +144,7 @@
144
144
  }
145
145
 
146
146
  @utility std-light {
147
- @apply bg-blue-50;
147
+ @apply bg-blue-100;
148
148
  }
149
149
 
150
150
  @utility std-dark {
@@ -325,6 +325,10 @@
325
325
  @apply w-10 h-10;
326
326
  }
327
327
 
328
+ @utility link {
329
+ @apply text-blue-400 hover:underline;
330
+ }
331
+
328
332
  @font-face {
329
333
  font-family: "Open Sans";
330
334
  src: url("/OpenSans-VariableFont_wdth,wght.ttf");
@@ -510,22 +514,22 @@
510
514
  }
511
515
 
512
516
  .chip {
513
- @apply text-sm font-medium px-2 py-1 rounded-full flex flex-row;
517
+ @apply inline-flex items-center rounded-md px-2 py-1 font-medium ring-1 ring-inset;
514
518
  }
515
519
  .select-chip {
516
520
  @apply px-1 text-sm font-medium rounded-full bg-sky-200 dark:bg-sky-700;
517
521
  }
518
522
  .green-chip {
519
- @apply bg-green-200 text-green-700 dark:bg-green-800 dark:text-green-300;
523
+ @apply bg-green-400/10 ring-green-500/20 text-green-400;
520
524
  }
521
525
  .red-chip {
522
- @apply bg-red-200 text-red-600 dark:bg-red-800 dark:text-red-300;
526
+ @apply bg-red-400/10 ring-red-500/20 text-red-400;
523
527
  }
524
528
  .yellow-chip {
525
- @apply bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300;
529
+ @apply bg-yellow-400/10 ring-yellow-500/20 text-yellow-400;
526
530
  }
527
531
  .blue-chip {
528
- @apply bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300;
532
+ @apply bg-blue-400/10 ring-blue-500/20 text-blue-400;
529
533
  }
530
534
 
531
535
  .image-position-box {
@@ -0,0 +1,513 @@
1
+ <template>
2
+ <div
3
+ :id="chartID"
4
+ class="rangeslider-container"
5
+ style="max-height: 135px; max-width: 1185px;"
6
+ />
7
+ </template>
8
+
9
+ <script>
10
+ import { extent } from 'd3-array'
11
+ import { axisBottom } from 'd3-axis'
12
+ import { brushX } from 'd3-brush'
13
+ import { scaleLinear, scaleTime } from 'd3-scale'
14
+ import { pointer, select } from 'd3-selection'
15
+ import { timeHour } from 'd3-time'
16
+ import { timeFormat } from 'd3-time-format'
17
+
18
+ const ONE_HOUR_MS = 60 * 60 * 1000;
19
+ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
20
+
21
+ export default {
22
+ props: {
23
+ chartData: {
24
+ type: Array,
25
+ default: () => []
26
+ },
27
+ chartID: {
28
+ type: String,
29
+ default: 'range-slider'
30
+ },
31
+ from: {
32
+ type: Number,
33
+ required: true
34
+ },
35
+ to: {
36
+ type: Number,
37
+ required: true
38
+ },
39
+ theme: {
40
+ type: String,
41
+ default: "light"
42
+ }
43
+ },
44
+ data () {
45
+ return {
46
+ currentTS: new Date(),
47
+ handleWidth: 10,
48
+ interval: 4,
49
+ margin: { top: 10, right: 15, bottom: 45, left: 15 },
50
+ maxTo: null,
51
+ minFrom: null,
52
+ previousSelectionValue: null,
53
+ startTimeIsDifferent: false,
54
+ endTimeIsDifferent: false,
55
+ size: { height: 100, width: 1000 },
56
+ week: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
57
+ xScaleTime: null,
58
+ knownFrom: this.from,
59
+ knownTo: this.to
60
+ }
61
+ },
62
+ computed: {
63
+ start () {
64
+ if (this.from < this.minFrom) {
65
+ return this.minFrom
66
+ } else {
67
+ return this.from
68
+ }
69
+ },
70
+ end () {
71
+ if (this.to > this.maxTo) {
72
+ return this.maxTo
73
+ } else {
74
+ return this.to
75
+ }
76
+ },
77
+ isDark () {
78
+ return this.theme === "dark"
79
+ },
80
+ },
81
+ watch: {
82
+ chartData () {
83
+ const canvas = select(`#${this.chartID}`)
84
+ canvas.select('svg').remove()
85
+ this.drawChart()
86
+ },
87
+ from () {
88
+ this.updateSlider()
89
+ },
90
+ to () {
91
+ this.updateSlider()
92
+ },
93
+ theme () {
94
+ const canvas = select(`#${this.chartID}`)
95
+ canvas.select('svg').remove()
96
+ this.drawChart()
97
+ },
98
+ },
99
+ mounted () {
100
+ this.drawChart()
101
+ },
102
+ methods: {
103
+ determineTickTimeIntervalHours (timeDomain) {
104
+ const days = Math.round((timeDomain[1] - timeDomain[0]) / ONE_DAY_MS)
105
+
106
+ let interval = 4
107
+
108
+ if (days > 6 && days <= 12) {
109
+ interval = 8
110
+ } else if (days > 12 && days <= 20) {
111
+ interval = 12
112
+ } else if (days > 20) {
113
+ interval = 24
114
+ }
115
+
116
+ return interval
117
+ },
118
+ roundTimeMillisecends (interval, time) {
119
+ return this.roundTime(interval, new Date(time))
120
+ },
121
+ roundTimeDate (interval, date) {
122
+ return new Date(this.roundTime(interval, date))
123
+ },
124
+ roundTime (interval, date) {
125
+ const newDate = new Date(date)
126
+ const h =
127
+ newDate.getHours() +
128
+ newDate.getMinutes() / 60 +
129
+ newDate.getSeconds() / 3600 +
130
+ newDate.getMilliseconds() / 3600000
131
+
132
+ newDate.setHours(Math.round(h / interval) * interval)
133
+ newDate.setMinutes(0)
134
+ newDate.setSeconds(0)
135
+ newDate.setMilliseconds(0)
136
+
137
+ return newDate.getTime()
138
+ },
139
+ drawChart () {
140
+ const canvas = select(`#${this.chartID}`)
141
+
142
+ let textColor = 'black'
143
+ let bgColor = 'white'
144
+ if (this.isDark) {
145
+ textColor = 'white'
146
+ bgColor = '#303030'
147
+ }
148
+
149
+ console.log('textColor', textColor)
150
+
151
+ const svg = canvas
152
+ .append('svg')
153
+ .attr('preserveAspectRatio', 'xMinYMin meet')
154
+
155
+ .attr('viewBox', `0 0 ${this.size.width} ${this.size.height + 10}`)
156
+ .append('g')
157
+ .attr('background-color', bgColor)
158
+ .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')')
159
+
160
+ const height = this.size.height - this.margin.top - this.margin.bottom
161
+ const width = this.size.width - this.margin.left - this.margin.right
162
+ const flowLine = this.chartData.map((d, i, arr) => {
163
+ const next = arr[i + 1]
164
+ const prev = arr[i - 1]
165
+
166
+ return {
167
+ x: d[0],
168
+ y: d[1],
169
+ x1: d[0],
170
+ y1: d[1],
171
+ x2: next ? next[0] : prev[0],
172
+ y2: next ? next[1] : prev[1]
173
+ }
174
+ })
175
+
176
+ const timeSeries = this.chartData.map(d => d[0])
177
+ const maxDate = timeSeries[timeSeries.length - 1]
178
+
179
+ // add an extra hour to the end since the interval is 1 hour
180
+ // this allows a full selection to the current time
181
+ if (Date.now() - maxDate <= ONE_HOUR_MS) {
182
+ const oneHourAhead = maxDate + ONE_HOUR_MS
183
+ timeSeries.push(oneHourAhead)
184
+ }
185
+
186
+ // scales the time range to pixels
187
+ const xScaleTime = scaleTime()
188
+ .domain(extent(timeSeries))
189
+ .range([0, width])
190
+ this.xScaleTime = xScaleTime
191
+
192
+ const interval = this.determineTickTimeIntervalHours(xScaleTime.domain())
193
+ this.interval = interval
194
+
195
+ this.maxTo = timeSeries[timeSeries.length - 1]
196
+ this.minFrom = timeSeries[0]
197
+ this.currentTS = this.maxTo
198
+
199
+ const timeFormatted = timeFormat('%H')
200
+
201
+ const xAxis = axisBottom(xScaleTime)
202
+ .ticks(timeFormatted.hours, interval)
203
+ .tickFormat(d => {
204
+ const hours = d.getHours()
205
+
206
+ if (hours === 0) {
207
+ return d.toLocaleString('en-us', { month: 'short' }) + ' ' + d.getDate() + ' ' + this.week[d.getDay()]
208
+ } else {
209
+ if (hours >= 12) {
210
+ return hours + ' pm'
211
+ } else {
212
+ return hours < 10 ? '0' + hours + ' am' : hours + ' am'
213
+ }
214
+ }
215
+ })
216
+
217
+ // yScale for upper histogram chart
218
+ const yScaleLinear = scaleLinear()
219
+ .domain(extent(this.chartData, d => d[1]))
220
+ .range([height - 2, 0])
221
+ this.yScaleLinear = yScaleLinear
222
+
223
+ // draws the x-axis in the middle with black/red text labeling
224
+ svg
225
+ .append('g')
226
+ .attr('class', 'xAxis')
227
+ .attr('transform', 'translate(0,' + height + ')')
228
+ .call(xAxis)
229
+ .selectAll('text')
230
+ .style('text-anchor', 'end')
231
+ .attr('dx', '-.8em')
232
+ .attr('dy', '.15em')
233
+ .attr('transform', 'rotate(-35)')
234
+ .style('fill', d => (d.getHours() === 0 ? '#027be3' : textColor))
235
+
236
+ // draws the histogram lines
237
+ const lines = svg
238
+ .append('g')
239
+ .selectAll('line')
240
+ .data(flowLine)
241
+ .enter()
242
+ .append('line')
243
+ .attr('class', 'flow-line')
244
+ .attr('x1', d => this.xScaleTime(d.x1))
245
+ .attr('y1', d => this.yScaleLinear(d.y1))
246
+ .attr('x2', d => this.xScaleTime(d.x2))
247
+ .attr('y2', d => this.yScaleLinear(d.y2))
248
+ .attr('stroke', '#9e9e9e')
249
+ .attr('stroke-width', '1')
250
+
251
+ // create brush handle (blue vertical bars)
252
+ const brushResizePath = (_, i) => {
253
+ return (
254
+ 'M0,0h' +
255
+ (i <= 0 ? '-' : '') +
256
+ this.handleWidth +
257
+ 'v' +
258
+ (height + 10) +
259
+ 'h' +
260
+ (i <= 0 ? '' : '-') +
261
+ this.handleWidth +
262
+ 'z'
263
+ )
264
+ }
265
+
266
+ const brush = brushX()
267
+ .extent([[0, -9], [width, height - 1]])
268
+ .on('start brush', event => brushMoved(event, this))
269
+ .on('end', event => brushEnd(event, this))
270
+ this.brush = brush
271
+
272
+ // create a brush for the draggable range selection / sliding window
273
+ const gBrush = svg
274
+ .append('g')
275
+ .attr('class', 'brush')
276
+ .call(brush)
277
+ .call(g =>
278
+ g
279
+ .select('.overlay')
280
+ .datum({ type: 'selection' })
281
+ .on('mousedown touchstart', beforeBrushStarted)
282
+ )
283
+ this.gBrush = gBrush
284
+
285
+ // adds the blue bars to each end of the slider
286
+ const handle = gBrush
287
+ .selectAll('.handle--custom')
288
+ .data([{ type: 'w' }, { type: 'e' }])
289
+ .enter()
290
+ .append('path')
291
+ .attr('id', (_, index) => index === 0 ? 'handle-left' : 'handle-right')
292
+ .attr('class', 'resize')
293
+ .attr('cursor', 'ew-resize')
294
+ .attr('d', brushResizePath)
295
+ .attr('focusable', 'true')
296
+ .attr('tabindex', 0)
297
+ .attr('role', 'slider')
298
+ .attr('aria-label', (_, index) => index === 0 ? 'from date/time value' : 'to date/time value')
299
+ .on('keydown', (event) => {
300
+ let startTime = new Date(this.from)
301
+ let endTime = new Date(this.to)
302
+
303
+ console.log('keydown event', event)
304
+
305
+ let STEP
306
+ if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') {
307
+ STEP = -1
308
+ } else if (event.key === 'ArrowRight' || event.key === 'ArrowUp') {
309
+ STEP = 1
310
+ } else if (event.key === 'PageUp') {
311
+ STEP = 10
312
+ } else if (event.key === 'PageDown') {
313
+ STEP = -10
314
+ }
315
+
316
+ const isLeftHandle = document.activeElement.id === 'handle-left'
317
+ if (isLeftHandle) {
318
+ startTime = timeHour.offset(startTime, STEP)
319
+ } else {
320
+ endTime = timeHour.offset(endTime, STEP)
321
+ }
322
+
323
+ // only allow updates if the time delta is an hour or more
324
+ const deltaSeconds = (endTime - startTime) / 1000
325
+ if (deltaSeconds > 60 * 60 * Math.abs(STEP)) {
326
+ this.$emit('sliderChanged', { from: startTime, to: endTime })
327
+ }
328
+ })
329
+
330
+ // highlight area between startTime and endTime
331
+ gBrush.call(brush.move, [xScaleTime(this.start), xScaleTime(this.end)])
332
+
333
+ // if chart is clicked outside of selection re-center the selection
334
+ function beforeBrushStarted (event) {
335
+ const dx = xScaleTime(1) - xScaleTime(0)
336
+ const [cx] = pointer(event, this)
337
+
338
+ let [x0, x1] = [cx - dx / 2, cx + dx / 2]
339
+ const [X0, X1] = xScaleTime.range()
340
+
341
+ while (Math.abs(x0 - x1) < 60) {
342
+ if (x0 > X0 && x0 - 1 > X0) {
343
+ x0 -= 1
344
+ } else if (x0 > X0) {
345
+ x0 = X0
346
+ }
347
+
348
+ if (x1 < X1 && x1 + 1 < X1) {
349
+ x1 += 1
350
+ } else if (x1 < X1) {
351
+ x1 = X1
352
+ }
353
+ }
354
+ select(this.parentNode).call(brush.move, x1 > X1 ? [X1 - dx, X1] : x0 < X0 ? [X0, X0 + dx] : [x0, x1])
355
+ }
356
+
357
+ function brushMoved (selection, oldState) {
358
+ // keep blue
359
+ // brush handles aligned with selection ends
360
+
361
+ if (isNaN(selection.selection[0]) || isNaN(selection.selection[1])) {
362
+ selection.selection[0] = oldState.knownFrom
363
+ selection.selection[1] = oldState.knownTo
364
+ } else {
365
+ oldState.knownFrom = selection.selection[0]
366
+ oldState.knownTo = selection.selection[1]
367
+ }
368
+
369
+ handle.attr('display', null).attr('transform', function (_, i) {
370
+ return 'translate(' + [selection.selection[i], -height / 5] + ')'
371
+ })
372
+
373
+ handle.attr('aria-valuenow', (_, index) => index === 0 ? oldState.from / 1000 : oldState.to / 1000) // should be unixEpochTime
374
+ handle.attr('aria-valuemin', oldState.minFrom / 1000) // unixEpochTime
375
+ handle.attr('aria-valuemax', oldState.maxTo / 1000) // unixEpochTime
376
+ handle.attr('aria-valuetext', (_, index) => {
377
+ const formatTime = timeFormat('%Y/%m/%d %H:%M')
378
+ return index === 0 ? formatTime(new Date(oldState.from)) : formatTime(new Date(oldState.to))
379
+ })
380
+
381
+ // changes the line color to red when selected ... looking for true false
382
+
383
+ // lines.classed('selected', false)
384
+
385
+ lines.classed('selected', (data, index, localLines) => {
386
+ const start = selection.selection[0]
387
+ const end = selection.selection[1]
388
+ const line = select(localLines[index])
389
+ const x1 = line.attr('x1'),
390
+ x2 = line.attr('x2')
391
+ return x1 && x1 >= start && x2 <= end
392
+ })
393
+
394
+ // only emit when the slider is moved and not for incoming updates
395
+ if (selection.sourceEvent) {
396
+ const brushEnds = selection.selection.map(oldState.xScaleTime.invert)
397
+ oldState.emitSliderChange(brushEnds)
398
+ }
399
+ }
400
+
401
+ // end event handler
402
+ function brushEnd (event, oldState) {
403
+ if (!event.sourceEvent || !event.selection) {
404
+ return
405
+ }
406
+
407
+ const brushEnds = event.selection.map(oldState.xScaleTime.invert)
408
+ oldState.emitSliderChange(brushEnds)
409
+ }
410
+ },
411
+
412
+ emitSliderChange (brushEnds) {
413
+ let startTime = brushEnds[0]
414
+ let endTime = brushEnds[1]
415
+
416
+ // round the start/end selection time only if it was moved
417
+ if (this.startTimeIsDifferent) {
418
+ startTime = this.roundTimeDate(1, startTime)
419
+ }
420
+
421
+ if (this.endTimeIsDifferent) {
422
+ endTime = this.roundTimeDate(1, endTime)
423
+ }
424
+
425
+ // reach the left border
426
+ if (endTime.getTime() >= this.maxTo) {
427
+ endTime = new Date(this.currentTS)
428
+
429
+ // make sure currentTS is not out of display bound
430
+ if (this.currentTS >= this.maxTo + this.interval * 3600000) {
431
+ endTime = new Date(this.maxTo)
432
+ }
433
+ }
434
+
435
+ // emit selection changed event
436
+ if (this.timeout) {
437
+ clearTimeout(this.timeout)
438
+ }
439
+
440
+ // debounce for performance
441
+ this.timeout = setTimeout(() => {
442
+ this.$emit('sliderChanged', { from: startTime, to: endTime })
443
+ }, 10)
444
+ },
445
+ updateSlider () {
446
+ this.gBrush.call(this.brush.move, [this.xScaleTime(this.start), this.xScaleTime(this.end)])
447
+ }
448
+ }
449
+ }
450
+ </script>
451
+
452
+ <style>
453
+ .rangeslider-container {
454
+ .axis path, .axis line {
455
+ fill: none;
456
+ stroke: #000;
457
+ shape-rendering: crispEdges;
458
+ }
459
+
460
+ .axis text {
461
+ font: 9px sans-serif;
462
+ }
463
+
464
+ line.selected {
465
+ stroke:#ff4800;
466
+ stroke-width: 4;
467
+ }
468
+
469
+ .brush .extent {
470
+ fill-opacity: .125;
471
+ shape-rendering: crispEdges;
472
+ }
473
+
474
+ .resize {
475
+ display: inline !important; /* show when empty */
476
+ fill: #027be3;
477
+ fill-opacity: 1.0;
478
+ stroke: #808080;
479
+ stroke-width: 0px;
480
+ }
481
+ }
482
+
483
+ .rangeslider-container .axis.path{
484
+ fill: none;stroke: #000;
485
+ shape-rendering: crispEdges;
486
+ }
487
+ .rangeslider-container .axis.line{
488
+ fill: none;stroke: #000;
489
+ shape-rendering: crispEdges;
490
+ }
491
+ .rangeslider-container .axis.text{
492
+ font: 9px sans-serif;
493
+ }
494
+ .rangeslider-container line.selected{
495
+ stroke:#027be3;
496
+ stroke-width: 2;
497
+ }
498
+ .rangeslider-container .brush .extent {
499
+ fill-opacity: .125;
500
+ shape-rendering: crispEdges;
501
+ }
502
+ .rangeslider-container .resize{
503
+ display: inline !important; /* show when empty */
504
+ fill: #027be3;
505
+ fill-opacity: 1.0;
506
+ stroke: #808080;
507
+ stroke-width: 0px;
508
+ }
509
+
510
+ .resize:focus-visible {
511
+ outline: 3px solid black;
512
+ }
513
+ </style>
@@ -27,6 +27,14 @@ const props = defineProps({
27
27
  type: Boolean,
28
28
  default: false,
29
29
  },
30
+ width: {
31
+ type: Number,
32
+ default: 1200,
33
+ },
34
+ height: {
35
+ type: Number,
36
+ default: 300,
37
+ },
30
38
  theme: {
31
39
  type: String,
32
40
  default: "light",
@@ -201,8 +209,8 @@ function plotGraph() {
201
209
  const plot = Plot.plot({
202
210
  marginLeft,
203
211
  marginRight,
204
- width: 1200,
205
- height: 320,
212
+ width: props.width,
213
+ height: props.height,
206
214
  marginBottom: 50,
207
215
  inset: 20,
208
216
  style: {