@internetarchive/histogram-date-range 0.1.5 → 0.1.6-alpha1

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.
@@ -1,807 +0,0 @@
1
- import { __decorate } from "tslib";
2
- import { css, html, nothing, LitElement, svg, } from 'lit';
3
- import { property, state, customElement } from 'lit/decorators.js';
4
- import { live } from 'lit/directives/live.js';
5
- import dayjs from 'dayjs/esm/index.js';
6
- import customParseFormat from 'dayjs/esm/plugin/customParseFormat';
7
- dayjs.extend(customParseFormat);
8
- import '@internetarchive/ia-activity-indicator/ia-activity-indicator';
9
- // these values can be overridden via the component's HTML (camelCased) attributes
10
- const WIDTH = 180;
11
- const HEIGHT = 40;
12
- const SLIDER_WIDTH = 10;
13
- const TOOLTIP_WIDTH = 125;
14
- const TOOLTIP_HEIGHT = 30;
15
- const DATE_FORMAT = 'YYYY';
16
- const MISSING_DATA = 'no data';
17
- const UPDATE_DEBOUNCE_DELAY_MS = 0;
18
- // this constant is not set up to be overridden
19
- const SLIDER_CORNER_SIZE = 4;
20
- // these CSS custom props can be overridden from the HTML that is invoking this component
21
- const sliderColor = css `var(--histogramDateRangeSliderColor, #4B65FE)`;
22
- const selectedRangeColor = css `var(--histogramDateRangeSelectedRangeColor, #DBE0FF)`;
23
- const barIncludedFill = css `var(--histogramDateRangeBarIncludedFill, #2C2C2C)`;
24
- const activityIndicatorColor = css `var(--histogramDateRangeActivityIndicator, #2C2C2C)`;
25
- const barExcludedFill = css `var(--histogramDateRangeBarExcludedFill, #CCCCCC)`;
26
- const inputBorder = css `var(--histogramDateRangeInputBorder, 0.5px solid #2C2C2C)`;
27
- const inputWidth = css `var(--histogramDateRangeInputWidth, 35px)`;
28
- const inputFontSize = css `var(--histogramDateRangeInputFontSize, 1.2rem)`;
29
- const inputFontFamily = css `var(--histogramDateRangeInputFontFamily, sans-serif)`;
30
- const tooltipBackgroundColor = css `var(--histogramDateRangeTooltipBackgroundColor, #2C2C2C)`;
31
- const tooltipTextColor = css `var(--histogramDateRangeTooltipTextColor, #FFFFFF)`;
32
- const tooltipFontSize = css `var(--histogramDateRangeTooltipFontSize, 1.1rem)`;
33
- const tooltipFontFamily = css `var(--histogramDateRangeTooltipFontFamily, sans-serif)`;
34
- let HistogramDateRange = class HistogramDateRange extends LitElement {
35
- constructor() {
36
- /* eslint-disable lines-between-class-members */
37
- super(...arguments);
38
- // public reactive properties that can be set via HTML attributes
39
- this.width = WIDTH;
40
- this.height = HEIGHT;
41
- this.sliderWidth = SLIDER_WIDTH;
42
- this.tooltipWidth = TOOLTIP_WIDTH;
43
- this.tooltipHeight = TOOLTIP_HEIGHT;
44
- this.updateDelay = UPDATE_DEBOUNCE_DELAY_MS;
45
- this.dateFormat = DATE_FORMAT;
46
- this.missingDataMessage = MISSING_DATA;
47
- this.minDate = '';
48
- this.maxDate = '';
49
- this.disabled = false;
50
- this.bins = [];
51
- // internal reactive properties not exposed as attributes
52
- this._tooltipOffset = 0;
53
- this._tooltipVisible = false;
54
- this._isDragging = false;
55
- this._isLoading = false;
56
- // non-reactive properties (changes don't auto-trigger re-rendering)
57
- this._minSelectedDate = '';
58
- this._maxSelectedDate = '';
59
- this._minDateMS = 0;
60
- this._maxDateMS = 0;
61
- this._dragOffset = 0;
62
- this._histWidth = 0;
63
- this._binWidth = 0;
64
- this._histData = [];
65
- this._previousDateRange = '';
66
- // use arrow functions (rather than standard JS class instance methods) so
67
- // that `this` is bound to the histogramDateRange object and not the event
68
- // target. for more info see
69
- // https://lit-element.polymer-project.org/guide/events#using-this-in-event-listeners
70
- this.drag = (e) => {
71
- // prevent selecting text or other ranges while dragging, especially in Safari
72
- e.preventDefault();
73
- if (this.disabled) {
74
- return;
75
- }
76
- this.setDragOffset(e);
77
- this._isDragging = true;
78
- this.addListeners();
79
- this.cancelPendingUpdateEvent();
80
- };
81
- this.drop = () => {
82
- if (this._isDragging) {
83
- this.removeListeners();
84
- this.beginEmitUpdateProcess();
85
- }
86
- this._isDragging = false;
87
- };
88
- /**
89
- * Adjust the date range based on slider movement
90
- *
91
- * @param e PointerEvent from the slider being moved
92
- */
93
- this.move = (e) => {
94
- const newX = e.offsetX - this._dragOffset;
95
- const slider = this._currentSlider;
96
- if (slider.id === 'slider-min') {
97
- this.minSelectedDate = this.translatePositionToDate(this.validMinSliderX(newX));
98
- }
99
- else {
100
- this.maxSelectedDate = this.translatePositionToDate(this.validMaxSliderX(newX));
101
- }
102
- };
103
- }
104
- /* eslint-enable lines-between-class-members */
105
- disconnectedCallback() {
106
- this.removeListeners();
107
- super.disconnectedCallback();
108
- }
109
- updated(changedProps) {
110
- // check for changes that would affect bin data calculations
111
- if (changedProps.has('bins') ||
112
- changedProps.has('minDate') ||
113
- changedProps.has('maxDate') ||
114
- changedProps.has('minSelectedDate') ||
115
- changedProps.has('maxSelectedDate')) {
116
- this.handleDataUpdate();
117
- }
118
- }
119
- /**
120
- * Set private properties that depend on the attribute bin data
121
- *
122
- * We're caching these values and not using getters to avoid recalculating all
123
- * of the hist data every time the user drags a slider or hovers over a bar
124
- * creating a tooltip.
125
- */
126
- handleDataUpdate() {
127
- if (!this.hasBinData) {
128
- return;
129
- }
130
- this._histWidth = this.width - this.sliderWidth * 2;
131
- this._minDateMS = this.getMSFromString(this.minDate);
132
- this._maxDateMS = this.getMSFromString(this.maxDate);
133
- this._binWidth = this._histWidth / this._numBins;
134
- this._previousDateRange = this.currentDateRangeString;
135
- this._histData = this.calculateHistData();
136
- this.minSelectedDate = this.minSelectedDate
137
- ? this.minSelectedDate
138
- : this.minDate;
139
- this.maxSelectedDate = this.maxSelectedDate
140
- ? this.maxSelectedDate
141
- : this.maxDate;
142
- this.requestUpdate();
143
- }
144
- calculateHistData() {
145
- const minValue = Math.min(...this.bins);
146
- const maxValue = Math.max(...this.bins);
147
- // if there is no difference between the min and max values, use a range of
148
- // 1 because log scaling will fail if the range is 0
149
- const valueRange = minValue === maxValue ? 1 : Math.log1p(maxValue);
150
- const valueScale = this.height / valueRange;
151
- const dateScale = this.dateRangeMS / this._numBins;
152
- return this.bins.map((v, i) => {
153
- return {
154
- value: v,
155
- // use log scaling for the height of the bar to prevent tall bars from
156
- // making the smaller ones too small to see
157
- height: Math.floor(Math.log1p(v) * valueScale),
158
- binStart: `${this.formatDate(i * dateScale + this._minDateMS)}`,
159
- binEnd: `${this.formatDate((i + 1) * dateScale + this._minDateMS)}`,
160
- };
161
- });
162
- }
163
- get hasBinData() {
164
- return this._numBins > 0;
165
- }
166
- get _numBins() {
167
- if (!this.bins || !this.bins.length) {
168
- return 0;
169
- }
170
- return this.bins.length;
171
- }
172
- get histogramLeftEdgeX() {
173
- return this.sliderWidth;
174
- }
175
- get histogramRightEdgeX() {
176
- return this.width - this.sliderWidth;
177
- }
178
- /** component's loading (and disabled) state */
179
- get loading() {
180
- return this._isLoading;
181
- }
182
- set loading(value) {
183
- this.disabled = value;
184
- this._isLoading = value;
185
- }
186
- /** formatted minimum date of selected date range */
187
- get minSelectedDate() {
188
- return this.formatDate(this.getMSFromString(this._minSelectedDate));
189
- }
190
- /** updates minSelectedDate if new date is valid */
191
- set minSelectedDate(rawDate) {
192
- if (!this._minSelectedDate) {
193
- // because the values needed to calculate valid max/min values are not
194
- // available during the lit init when it's populating properties from
195
- // attributes, fall back to just the raw date if nothing is already set
196
- this._minSelectedDate = rawDate;
197
- return;
198
- }
199
- const proposedDateMS = this.getMSFromString(rawDate);
200
- const isValidDate = !Number.isNaN(proposedDateMS);
201
- const isNotTooRecent = proposedDateMS <= this.getMSFromString(this.maxSelectedDate);
202
- if (isValidDate && isNotTooRecent) {
203
- this._minSelectedDate = this.formatDate(proposedDateMS);
204
- }
205
- this.requestUpdate();
206
- }
207
- /** formatted maximum date of selected date range */
208
- get maxSelectedDate() {
209
- return this.formatDate(this.getMSFromString(this._maxSelectedDate));
210
- }
211
- /** updates maxSelectedDate if new date is valid */
212
- set maxSelectedDate(rawDate) {
213
- if (!this._maxSelectedDate) {
214
- // because the values needed to calculate valid max/min values are not
215
- // available during the lit init when it's populating properties from
216
- // attributes, fall back to just the raw date if nothing is already set
217
- this._maxSelectedDate = rawDate;
218
- return;
219
- }
220
- const proposedDateMS = this.getMSFromString(rawDate);
221
- const isValidDate = !Number.isNaN(proposedDateMS);
222
- const isNotTooOld = proposedDateMS >= this.getMSFromString(this.minSelectedDate);
223
- if (isValidDate && isNotTooOld) {
224
- this._maxSelectedDate = this.formatDate(proposedDateMS);
225
- }
226
- this.requestUpdate();
227
- }
228
- /** horizontal position of min date slider */
229
- get minSliderX() {
230
- const x = this.translateDateToPosition(this.minSelectedDate);
231
- return this.validMinSliderX(x);
232
- }
233
- /** horizontal position of max date slider */
234
- get maxSliderX() {
235
- const x = this.translateDateToPosition(this.maxSelectedDate);
236
- return this.validMaxSliderX(x);
237
- }
238
- get dateRangeMS() {
239
- return this._maxDateMS - this._minDateMS;
240
- }
241
- showTooltip(e) {
242
- if (this._isDragging || this.disabled) {
243
- return;
244
- }
245
- const target = e.currentTarget;
246
- const x = target.x.baseVal.value + this.sliderWidth / 2;
247
- const dataset = target.dataset;
248
- const itemsText = `item${dataset.numItems !== '1' ? 's' : ''}`;
249
- const formattedNumItems = Number(dataset.numItems).toLocaleString();
250
- this._tooltipOffset =
251
- x + (this._binWidth - this.sliderWidth - this.tooltipWidth) / 2;
252
- this._tooltipContent = html `
253
- ${formattedNumItems} ${itemsText}<br />
254
- ${dataset.binStart} - ${dataset.binEnd}
255
- `;
256
- this._tooltipVisible = true;
257
- }
258
- hideTooltip() {
259
- this._tooltipContent = undefined;
260
- this._tooltipVisible = false;
261
- }
262
- /**
263
- * Constrain a proposed value for the minimum (left) slider
264
- *
265
- * If the value is less than the leftmost valid position, then set it to the
266
- * left edge of the histogram (ie the slider width). If the value is greater
267
- * than the rightmost valid position (the position of the max slider), then
268
- * set it to the position of the max slider
269
- */
270
- validMinSliderX(newX) {
271
- // allow the left slider to go right only to the right slider, even if the
272
- // max selected date is out of range
273
- const rightLimit = Math.min(this.translateDateToPosition(this.maxSelectedDate), this.histogramRightEdgeX);
274
- newX = this.clamp(newX, this.histogramLeftEdgeX, rightLimit);
275
- const isInvalid = Number.isNaN(newX) || rightLimit < this.histogramLeftEdgeX;
276
- return isInvalid ? this.histogramLeftEdgeX : newX;
277
- }
278
- /**
279
- * Constrain a proposed value for the maximum (right) slider
280
- *
281
- * If the value is greater than the rightmost valid position, then set it to
282
- * the right edge of the histogram (ie histogram width - slider width). If the
283
- * value is less than the leftmost valid position (the position of the min
284
- * slider), then set it to the position of the min slider
285
- */
286
- validMaxSliderX(newX) {
287
- // allow the right slider to go left only to the left slider, even if the
288
- // min selected date is out of range
289
- const leftLimit = Math.max(this.histogramLeftEdgeX, this.translateDateToPosition(this.minSelectedDate));
290
- newX = this.clamp(newX, leftLimit, this.histogramRightEdgeX);
291
- const isInvalid = Number.isNaN(newX) || leftLimit > this.histogramRightEdgeX;
292
- return isInvalid ? this.histogramRightEdgeX : newX;
293
- }
294
- addListeners() {
295
- window.addEventListener('pointermove', this.move);
296
- window.addEventListener('pointerup', this.drop);
297
- window.addEventListener('pointercancel', this.drop);
298
- }
299
- removeListeners() {
300
- window.removeEventListener('pointermove', this.move);
301
- window.removeEventListener('pointerup', this.drop);
302
- window.removeEventListener('pointercancel', this.drop);
303
- }
304
- /**
305
- * start a timer to emit an update event. this timer can be canceled (and the
306
- * event not emitted) if user drags a slider or focuses a date input within
307
- * the update delay
308
- */
309
- beginEmitUpdateProcess() {
310
- this.cancelPendingUpdateEvent();
311
- this._emitUpdatedEventTimer = setTimeout(() => {
312
- if (this.currentDateRangeString === this._previousDateRange) {
313
- // don't emit duplicate event if no change since last emitted event
314
- return;
315
- }
316
- this._previousDateRange = this.currentDateRangeString;
317
- const options = {
318
- detail: {
319
- minDate: this.minSelectedDate,
320
- maxDate: this.maxSelectedDate,
321
- },
322
- bubbles: true,
323
- composed: true,
324
- };
325
- this.dispatchEvent(new CustomEvent('histogramDateRangeUpdated', options));
326
- }, this.updateDelay);
327
- }
328
- cancelPendingUpdateEvent() {
329
- if (this._emitUpdatedEventTimer === undefined) {
330
- return;
331
- }
332
- clearTimeout(this._emitUpdatedEventTimer);
333
- this._emitUpdatedEventTimer = undefined;
334
- }
335
- /**
336
- * find position of pointer in relation to the current slider
337
- */
338
- setDragOffset(e) {
339
- this._currentSlider = e.currentTarget;
340
- const sliderX = this._currentSlider.id === 'slider-min'
341
- ? this.minSliderX
342
- : this.maxSliderX;
343
- this._dragOffset = e.offsetX - sliderX;
344
- // work around Firefox issue where e.offsetX seems to be not based on current
345
- // element but on background element
346
- if (this._dragOffset > this.sliderWidth ||
347
- this._dragOffset < -this.sliderWidth) {
348
- this._dragOffset = 0;
349
- }
350
- }
351
- /**
352
- * @param x horizontal position of slider
353
- * @returns string representation of date
354
- */
355
- translatePositionToDate(x) {
356
- // use Math.ceil to round up to fix case where input like 1/1/2010 would get
357
- // translated to 12/31/2009
358
- const milliseconds = Math.ceil(((x - this.sliderWidth) * this.dateRangeMS) / this._histWidth);
359
- return this.formatDate(this._minDateMS + milliseconds);
360
- }
361
- /**
362
- * Returns slider x-position corresponding to given date
363
- *
364
- * @param date
365
- * @returns x-position of slider
366
- */
367
- translateDateToPosition(date) {
368
- const milliseconds = this.getMSFromString(date);
369
- return (this.sliderWidth +
370
- ((milliseconds - this._minDateMS) * this._histWidth) / this.dateRangeMS);
371
- }
372
- /** ensure that the returned value is between minValue and maxValue */
373
- clamp(x, minValue, maxValue) {
374
- return Math.min(Math.max(x, minValue), maxValue);
375
- }
376
- handleMinDateInput(e) {
377
- const target = e.currentTarget;
378
- this.minSelectedDate = target.value;
379
- this.beginEmitUpdateProcess();
380
- }
381
- handleMaxDateInput(e) {
382
- const target = e.currentTarget;
383
- this.maxSelectedDate = target.value;
384
- this.beginEmitUpdateProcess();
385
- }
386
- handleKeyUp(e) {
387
- if (e.key === 'Enter') {
388
- const target = e.currentTarget;
389
- target.blur();
390
- if (target.id === 'date-min') {
391
- this.handleMinDateInput(e);
392
- }
393
- else if (target.id === 'date-max') {
394
- this.handleMaxDateInput(e);
395
- }
396
- }
397
- }
398
- get currentDateRangeString() {
399
- return `${this.minSelectedDate}:${this.maxSelectedDate}`;
400
- }
401
- getMSFromString(date) {
402
- const digitGroupCount = (date.split(/(\d+)/).length - 1) / 2;
403
- if (digitGroupCount === 1) {
404
- // if there's just a single set of digits, assume it's a year
405
- const dateObj = new Date(0, 0); // start at January 1, 1900
406
- dateObj.setFullYear(Number(date)); // override year
407
- return dateObj.getTime(); // get time in milliseconds
408
- }
409
- return dayjs(date, [this.dateFormat, DATE_FORMAT]).valueOf();
410
- }
411
- /**
412
- * expand or narrow the selected range by moving the slider nearest the
413
- * clicked bar to the outer edge of the clicked bar
414
- *
415
- * @param e Event click event from a histogram bar
416
- */
417
- handleBarClick(e) {
418
- const dataset = e.currentTarget.dataset;
419
- // use the midpoint of the width of the clicked bar to determine which is
420
- // the nearest slider
421
- const clickPosition = (this.getMSFromString(dataset.binStart) +
422
- this.getMSFromString(dataset.binEnd)) /
423
- 2;
424
- const distanceFromMinSlider = Math.abs(clickPosition - this.getMSFromString(this.minSelectedDate));
425
- const distanceFromMaxSlider = Math.abs(clickPosition - this.getMSFromString(this.maxSelectedDate));
426
- // update the selected range by moving the nearer slider
427
- if (distanceFromMinSlider < distanceFromMaxSlider) {
428
- this.minSelectedDate = dataset.binStart;
429
- }
430
- else {
431
- this.maxSelectedDate = dataset.binEnd;
432
- }
433
- this.beginEmitUpdateProcess();
434
- }
435
- get minSliderTemplate() {
436
- // width/height in pixels of curved part of the sliders (like
437
- // border-radius); used as part of a SVG quadratic curve. see
438
- // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#curve_commands
439
- const cs = SLIDER_CORNER_SIZE;
440
- const sliderShape = `
441
- M${this.minSliderX},0
442
- h-${this.sliderWidth - cs}
443
- q-${cs},0 -${cs},${cs}
444
- v${this.height - cs * 2}
445
- q0,${cs} ${cs},${cs}
446
- h${this.sliderWidth - cs}
447
- `;
448
- return this.generateSliderSVG(this.minSliderX, 'slider-min', sliderShape);
449
- }
450
- get maxSliderTemplate() {
451
- const cs = SLIDER_CORNER_SIZE;
452
- const sliderShape = `
453
- M${this.maxSliderX},0
454
- h${this.sliderWidth - cs}
455
- q${cs},0 ${cs},${cs}
456
- v${this.height - cs * 2}
457
- q0,${cs} -${cs},${cs}
458
- h-${this.sliderWidth - cs}
459
- `;
460
- return this.generateSliderSVG(this.maxSliderX, 'slider-max', sliderShape);
461
- }
462
- generateSliderSVG(sliderPositionX, id, sliderShape) {
463
- // whether the curved part of the slider is facing towards the left (1), ie
464
- // minimum, or facing towards the right (-1), ie maximum
465
- const k = id === 'slider-min' ? 1 : -1;
466
- return svg `
467
- <svg
468
- id="${id}"
469
- class="
470
- ${this.disabled ? '' : 'draggable'}
471
- ${this._isDragging ? 'dragging' : ''}"
472
- @pointerdown="${this.drag}"
473
- >
474
- <path d="${sliderShape} z" fill="${sliderColor}" />
475
- <rect
476
- x="${sliderPositionX - this.sliderWidth * k + this.sliderWidth * 0.4 * k}"
477
- y="${this.height / 3}"
478
- width="1"
479
- height="${this.height / 3}"
480
- fill="white"
481
- />
482
- <rect
483
- x="${sliderPositionX - this.sliderWidth * k + this.sliderWidth * 0.6 * k}"
484
- y="${this.height / 3}"
485
- width="1"
486
- height="${this.height / 3}"
487
- fill="white"
488
- />
489
- </svg>
490
- `;
491
- }
492
- get selectedRangeTemplate() {
493
- return svg `
494
- <rect
495
- x="${this.minSliderX}"
496
- y="0"
497
- width="${this.maxSliderX - this.minSliderX}"
498
- height="${this.height}"
499
- fill="${selectedRangeColor}"
500
- />`;
501
- }
502
- get histogramTemplate() {
503
- const xScale = this._histWidth / this._numBins;
504
- const barWidth = xScale - 1;
505
- let x = this.sliderWidth; // start at the left edge of the histogram
506
- // the stroke-dasharray style below creates a transparent border around the
507
- // right edge of the bar, which prevents user from encountering a gap
508
- // between adjacent bars (eg when viewing the tooltips or when trying to
509
- // extend the range by clicking on a bar)
510
- return this._histData.map(data => {
511
- const bar = svg `
512
- <rect
513
- class="bar"
514
- style='stroke-dasharray: 0 ${barWidth} ${data.height} ${barWidth} 0 ${data.height};'
515
- x="${x}"
516
- y="${this.height - data.height}"
517
- width="${barWidth}"
518
- height="${data.height}"
519
- @pointerenter="${this.showTooltip}"
520
- @pointerleave="${this.hideTooltip}"
521
- @click="${this.handleBarClick}"
522
- fill="${x + barWidth >= this.minSliderX && x <= this.maxSliderX
523
- ? barIncludedFill
524
- : barExcludedFill}"
525
- data-num-items="${data.value}"
526
- data-bin-start="${data.binStart}"
527
- data-bin-end="${data.binEnd}"
528
- />`;
529
- x += xScale;
530
- return bar;
531
- });
532
- }
533
- formatDate(dateMS) {
534
- if (Number.isNaN(dateMS)) {
535
- return '';
536
- }
537
- const date = dayjs(dateMS);
538
- if (date.year() < 1000) {
539
- // years before 1000 don't play well with dayjs custom formatting, so fall
540
- // back to displaying only the year
541
- return String(date.year());
542
- }
543
- return date.format(this.dateFormat);
544
- }
545
- /**
546
- * NOTE: we are relying on the lit `live` directive in the template to
547
- * ensure that the change to minSelectedDate is noticed and the input value
548
- * gets properly re-rendered. see
549
- * https://lit.dev/docs/templates/directives/#live
550
- */
551
- get minInputTemplate() {
552
- return html `
553
- <input
554
- id="date-min"
555
- placeholder="${this.dateFormat}"
556
- type="text"
557
- @focus="${this.cancelPendingUpdateEvent}"
558
- @blur="${this.handleMinDateInput}"
559
- @keyup="${this.handleKeyUp}"
560
- .value="${live(this.minSelectedDate)}"
561
- ?disabled="${this.disabled}"
562
- />
563
- `;
564
- }
565
- get maxInputTemplate() {
566
- return html `
567
- <input
568
- id="date-max"
569
- placeholder="${this.dateFormat}"
570
- type="text"
571
- @focus="${this.cancelPendingUpdateEvent}"
572
- @blur="${this.handleMaxDateInput}"
573
- @keyup="${this.handleKeyUp}"
574
- .value="${live(this.maxSelectedDate)}"
575
- ?disabled="${this.disabled}"
576
- />
577
- `;
578
- }
579
- get tooltipTemplate() {
580
- return html `
581
- <style>
582
- #tooltip {
583
- width: ${this.tooltipWidth}px;
584
- height: ${this.tooltipHeight}px;
585
- top: ${-9 - this.tooltipHeight}px;
586
- left: ${this._tooltipOffset}px;
587
- display: ${this._tooltipVisible ? 'block' : 'none'};
588
- }
589
- #tooltip:after {
590
- left: ${this.tooltipWidth / 2}px;
591
- }
592
- </style>
593
- <div id="tooltip">${this._tooltipContent}</div>
594
- `;
595
- }
596
- get noDataTemplate() {
597
- return html `
598
- <div class="missing-data-message">${this.missingDataMessage}</div>
599
- `;
600
- }
601
- get activityIndicatorTemplate() {
602
- if (!this.loading) {
603
- return nothing;
604
- }
605
- return html `
606
- <ia-activity-indicator mode="processing"> </ia-activity-indicator>
607
- `;
608
- }
609
- render() {
610
- if (!this.hasBinData) {
611
- return this.noDataTemplate;
612
- }
613
- return html `
614
- <div
615
- id="container"
616
- class="
617
- noselect
618
- ${this._isDragging ? 'dragging' : ''}
619
- "
620
- style="width: ${this.width}px"
621
- >
622
- ${this.activityIndicatorTemplate} ${this.tooltipTemplate}
623
- <div
624
- class="inner-container
625
- ${this.disabled ? 'disabled' : ''}"
626
- >
627
- <svg
628
- width="${this.width}"
629
- height="${this.height}"
630
- @pointerleave="${this.drop}"
631
- >
632
- ${this.selectedRangeTemplate}
633
- <svg id="histogram">${this.histogramTemplate}</svg>
634
- ${this.minSliderTemplate} ${this.maxSliderTemplate}
635
- </svg>
636
- <div id="inputs">
637
- ${this.minInputTemplate}
638
- <div class="dash">-</div>
639
- ${this.maxInputTemplate}
640
- </div>
641
- </div>
642
- </div>
643
- `;
644
- }
645
- };
646
- HistogramDateRange.styles = css `
647
- .missing-data-message {
648
- text-align: center;
649
- }
650
- #container {
651
- margin: 0;
652
- touch-action: none;
653
- position: relative;
654
- }
655
- .disabled {
656
- opacity: 0.3;
657
- }
658
- ia-activity-indicator {
659
- position: absolute;
660
- left: calc(50% - 10px);
661
- top: 10px;
662
- width: 20px;
663
- height: 20px;
664
- --activityIndicatorLoadingDotColor: rgba(0, 0, 0, 0);
665
- --activityIndicatorLoadingRingColor: ${activityIndicatorColor};
666
- }
667
-
668
- /* prevent selection from interfering with tooltip, especially on mobile */
669
- /* https://stackoverflow.com/a/4407335/1163042 */
670
- .noselect {
671
- -webkit-touch-callout: none; /* iOS Safari */
672
- -webkit-user-select: none; /* Safari */
673
- -moz-user-select: none; /* Old versions of Firefox */
674
- -ms-user-select: none; /* Internet Explorer/Edge */
675
- user-select: none; /* current Chrome, Edge, Opera and Firefox */
676
- }
677
- .bar {
678
- /* create a transparent border around the hist bars to prevent "gaps" and
679
- flickering when moving around between bars. this also helps with handling
680
- clicks on the bars, preventing users from being able to click in between
681
- bars */
682
- stroke: rgba(0, 0, 0, 0);
683
- /* ensure transparent stroke wide enough to cover gap between bars */
684
- stroke-width: 2px;
685
- }
686
- .bar:hover {
687
- /* highlight currently hovered bar */
688
- fill-opacity: 0.7;
689
- }
690
- .disabled .bar:hover {
691
- /* ensure no visual hover interaction when disabled */
692
- fill-opacity: 1;
693
- }
694
- /****** histogram ********/
695
- #tooltip {
696
- position: absolute;
697
- background: ${tooltipBackgroundColor};
698
- color: ${tooltipTextColor};
699
- text-align: center;
700
- border-radius: 3px;
701
- padding: 2px;
702
- font-size: ${tooltipFontSize};
703
- font-family: ${tooltipFontFamily};
704
- touch-action: none;
705
- pointer-events: none;
706
- }
707
- #tooltip:after {
708
- content: '';
709
- position: absolute;
710
- margin-left: -5px;
711
- top: 100%;
712
- /* arrow */
713
- border: 5px solid ${tooltipTextColor};
714
- border-color: ${tooltipBackgroundColor} transparent transparent
715
- transparent;
716
- }
717
- /****** slider ********/
718
- .draggable:hover {
719
- cursor: grab;
720
- }
721
- .dragging {
722
- cursor: grabbing !important;
723
- }
724
- /****** inputs ********/
725
- #inputs {
726
- display: flex;
727
- justify-content: center;
728
- }
729
- #inputs .dash {
730
- position: relative;
731
- bottom: -1px;
732
- }
733
- input {
734
- width: ${inputWidth};
735
- margin: 0 3px;
736
- border: ${inputBorder};
737
- border-radius: 2px !important;
738
- text-align: center;
739
- font-size: ${inputFontSize};
740
- font-family: ${inputFontFamily};
741
- }
742
- `;
743
- __decorate([
744
- property({ type: Number })
745
- ], HistogramDateRange.prototype, "width", void 0);
746
- __decorate([
747
- property({ type: Number })
748
- ], HistogramDateRange.prototype, "height", void 0);
749
- __decorate([
750
- property({ type: Number })
751
- ], HistogramDateRange.prototype, "sliderWidth", void 0);
752
- __decorate([
753
- property({ type: Number })
754
- ], HistogramDateRange.prototype, "tooltipWidth", void 0);
755
- __decorate([
756
- property({ type: Number })
757
- ], HistogramDateRange.prototype, "tooltipHeight", void 0);
758
- __decorate([
759
- property({ type: Number })
760
- ], HistogramDateRange.prototype, "updateDelay", void 0);
761
- __decorate([
762
- property()
763
- ], HistogramDateRange.prototype, "dateFormat", void 0);
764
- __decorate([
765
- property()
766
- ], HistogramDateRange.prototype, "missingDataMessage", void 0);
767
- __decorate([
768
- property()
769
- ], HistogramDateRange.prototype, "minDate", void 0);
770
- __decorate([
771
- property()
772
- ], HistogramDateRange.prototype, "maxDate", void 0);
773
- __decorate([
774
- property({ type: Boolean })
775
- ], HistogramDateRange.prototype, "disabled", void 0);
776
- __decorate([
777
- property({ type: Object })
778
- ], HistogramDateRange.prototype, "bins", void 0);
779
- __decorate([
780
- state()
781
- ], HistogramDateRange.prototype, "_tooltipOffset", void 0);
782
- __decorate([
783
- state()
784
- ], HistogramDateRange.prototype, "_tooltipContent", void 0);
785
- __decorate([
786
- state()
787
- ], HistogramDateRange.prototype, "_tooltipVisible", void 0);
788
- __decorate([
789
- state()
790
- ], HistogramDateRange.prototype, "_isDragging", void 0);
791
- __decorate([
792
- state()
793
- ], HistogramDateRange.prototype, "_isLoading", void 0);
794
- __decorate([
795
- property({ type: Boolean })
796
- ], HistogramDateRange.prototype, "loading", null);
797
- __decorate([
798
- property()
799
- ], HistogramDateRange.prototype, "minSelectedDate", null);
800
- __decorate([
801
- property()
802
- ], HistogramDateRange.prototype, "maxSelectedDate", null);
803
- HistogramDateRange = __decorate([
804
- customElement('histogram-date-range')
805
- ], HistogramDateRange);
806
- export { HistogramDateRange };
807
- //# sourceMappingURL=histogram-date-range.js.map