@internetarchive/histogram-date-range 0.0.11-beta → 0.1.2-alpha
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/demo/index.css +23 -0
- package/demo/index.html +51 -42
- package/dist/src/histogram-date-range.d.ts +22 -13
- package/dist/src/histogram-date-range.js +134 -70
- package/dist/src/histogram-date-range.js.map +1 -1
- package/dist/test/histogram-date-range.test.js +130 -34
- package/dist/test/histogram-date-range.test.js.map +1 -1
- package/docs/_snowpack/pkg/@internetarchive/ia-activity-indicator/ia-activity-indicator.js +9 -2534
- package/docs/_snowpack/pkg/common/lit-element-3254fb50.js +22 -0
- package/docs/_snowpack/pkg/common/lit-html-1d707ff6.js +8 -0
- package/docs/_snowpack/pkg/dayjs/esm/index.js +3 -3
- package/docs/_snowpack/pkg/dayjs/esm/plugin/customParseFormat.js +342 -0
- package/docs/_snowpack/pkg/import-map.json +1 -0
- package/docs/_snowpack/pkg/lit/decorators.js +8 -2
- package/docs/_snowpack/pkg/lit/directives/live.js +4 -4
- package/docs/_snowpack/pkg/lit.js +2 -23
- package/docs/demo/index.css +23 -0
- package/docs/demo/index.html +51 -42
- package/docs/dist/src/histogram-date-range.js +95 -51
- package/package.json +21 -20
- package/src/histogram-date-range.ts +176 -76
- package/test/histogram-date-range.test.ts +172 -38
- package/docs/_snowpack/env.js +0 -3
- package/docs/_snowpack/pkg/common/lit-html-e67c9f49.js +0 -8
- package/types/static.d.ts +0 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
css,
|
|
3
3
|
html,
|
|
4
|
+
nothing,
|
|
4
5
|
LitElement,
|
|
5
6
|
PropertyValues,
|
|
6
7
|
svg,
|
|
@@ -10,6 +11,8 @@ import {
|
|
|
10
11
|
import { property, state, customElement } from 'lit/decorators.js';
|
|
11
12
|
import { live } from 'lit/directives/live.js';
|
|
12
13
|
import dayjs from 'dayjs/esm/index.js';
|
|
14
|
+
import customParseFormat from 'dayjs/esm/plugin/customParseFormat';
|
|
15
|
+
dayjs.extend(customParseFormat);
|
|
13
16
|
import '@internetarchive/ia-activity-indicator/ia-activity-indicator';
|
|
14
17
|
|
|
15
18
|
// these values can be overridden via the component's HTML (camelCased) attributes
|
|
@@ -20,23 +23,25 @@ const TOOLTIP_WIDTH = 125;
|
|
|
20
23
|
const TOOLTIP_HEIGHT = 30;
|
|
21
24
|
const DATE_FORMAT = 'YYYY';
|
|
22
25
|
const MISSING_DATA = 'no data';
|
|
23
|
-
const UPDATE_DEBOUNCE_DELAY_MS =
|
|
26
|
+
const UPDATE_DEBOUNCE_DELAY_MS = 0;
|
|
24
27
|
|
|
25
28
|
// this constant is not set up to be overridden
|
|
26
29
|
const SLIDER_CORNER_SIZE = 4;
|
|
27
30
|
|
|
28
31
|
// these CSS custom props can be overridden from the HTML that is invoking this component
|
|
29
|
-
const
|
|
30
|
-
const
|
|
32
|
+
const sliderColor = css`var(--histogramDateRangeSliderColor, #4B65FE)`;
|
|
33
|
+
const selectedRangeColor = css`var(--histogramDateRangeSelectedRangeColor, #DBE0FF)`;
|
|
31
34
|
const barIncludedFill = css`var(--histogramDateRangeBarIncludedFill, #2C2C2C)`;
|
|
32
35
|
const activityIndicatorColor = css`var(--histogramDateRangeActivityIndicator, #2C2C2C)`;
|
|
33
36
|
const barExcludedFill = css`var(--histogramDateRangeBarExcludedFill, #CCCCCC)`;
|
|
34
37
|
const inputBorder = css`var(--histogramDateRangeInputBorder, 0.5px solid #2C2C2C)`;
|
|
35
38
|
const inputWidth = css`var(--histogramDateRangeInputWidth, 35px)`;
|
|
36
39
|
const inputFontSize = css`var(--histogramDateRangeInputFontSize, 1.2rem)`;
|
|
40
|
+
const inputFontFamily = css`var(--histogramDateRangeInputFontFamily, sans-serif)`;
|
|
37
41
|
const tooltipBackgroundColor = css`var(--histogramDateRangeTooltipBackgroundColor, #2C2C2C)`;
|
|
38
42
|
const tooltipTextColor = css`var(--histogramDateRangeTooltipTextColor, #FFFFFF)`;
|
|
39
43
|
const tooltipFontSize = css`var(--histogramDateRangeTooltipFontSize, 1.1rem)`;
|
|
44
|
+
const tooltipFontFamily = css`var(--histogramDateRangeTooltipFontFamily, sans-serif)`;
|
|
40
45
|
|
|
41
46
|
type SliderId = 'slider-min' | 'slider-max';
|
|
42
47
|
|
|
@@ -47,6 +52,12 @@ interface HistogramItem {
|
|
|
47
52
|
binEnd: string;
|
|
48
53
|
}
|
|
49
54
|
|
|
55
|
+
interface BarDataset extends DOMStringMap {
|
|
56
|
+
numItems: string;
|
|
57
|
+
binStart: string;
|
|
58
|
+
binEnd: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
50
61
|
@customElement('histogram-date-range')
|
|
51
62
|
export class HistogramDateRange extends LitElement {
|
|
52
63
|
/* eslint-disable lines-between-class-members */
|
|
@@ -93,7 +104,14 @@ export class HistogramDateRange extends LitElement {
|
|
|
93
104
|
}
|
|
94
105
|
|
|
95
106
|
updated(changedProps: PropertyValues): void {
|
|
96
|
-
|
|
107
|
+
// check for changes that would affect bin data calculations
|
|
108
|
+
if (
|
|
109
|
+
changedProps.has('bins') ||
|
|
110
|
+
changedProps.has('minDate') ||
|
|
111
|
+
changedProps.has('maxDate') ||
|
|
112
|
+
changedProps.has('minSelectedDate') ||
|
|
113
|
+
changedProps.has('maxSelectedDate')
|
|
114
|
+
) {
|
|
97
115
|
this.handleDataUpdate();
|
|
98
116
|
}
|
|
99
117
|
}
|
|
@@ -110,8 +128,8 @@ export class HistogramDateRange extends LitElement {
|
|
|
110
128
|
return;
|
|
111
129
|
}
|
|
112
130
|
this._histWidth = this.width - this.sliderWidth * 2;
|
|
113
|
-
this._minDateMS =
|
|
114
|
-
this._maxDateMS =
|
|
131
|
+
this._minDateMS = this.getMSFromString(this.minDate);
|
|
132
|
+
this._maxDateMS = this.getMSFromString(this.maxDate);
|
|
115
133
|
this._binWidth = this._histWidth / this._numBins;
|
|
116
134
|
this._previousDateRange = this.currentDateRangeString;
|
|
117
135
|
this._histData = this.calculateHistData();
|
|
@@ -127,11 +145,17 @@ export class HistogramDateRange extends LitElement {
|
|
|
127
145
|
private calculateHistData(): HistogramItem[] {
|
|
128
146
|
const minValue = Math.min(...this.bins);
|
|
129
147
|
const maxValue = Math.max(...this.bins);
|
|
130
|
-
|
|
148
|
+
// if there is no difference between the min and max values, use a range of
|
|
149
|
+
// 1 because log scaling will fail if the range is 0
|
|
150
|
+
const valueRange =
|
|
151
|
+
minValue === maxValue ? 1 : Math.log1p(maxValue - minValue);
|
|
152
|
+
const valueScale = this.height / valueRange;
|
|
131
153
|
const dateScale = this.dateRangeMS / this._numBins;
|
|
132
154
|
return this.bins.map((v: number, i: number) => {
|
|
133
155
|
return {
|
|
134
156
|
value: v,
|
|
157
|
+
// use log scaling for the height of the bar to prevent tall bars from
|
|
158
|
+
// making the smaller ones too small to see
|
|
135
159
|
height: Math.floor(Math.log1p(v) * valueScale),
|
|
136
160
|
binStart: `${this.formatDate(i * dateScale + this._minDateMS)}`,
|
|
137
161
|
binEnd: `${this.formatDate((i + 1) * dateScale + this._minDateMS)}`,
|
|
@@ -150,6 +174,14 @@ export class HistogramDateRange extends LitElement {
|
|
|
150
174
|
return this.bins.length;
|
|
151
175
|
}
|
|
152
176
|
|
|
177
|
+
private get histogramLeftEdgeX(): number {
|
|
178
|
+
return this.sliderWidth;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private get histogramRightEdgeX(): number {
|
|
182
|
+
return this.width - this.sliderWidth;
|
|
183
|
+
}
|
|
184
|
+
|
|
153
185
|
/** component's loading (and disabled) state */
|
|
154
186
|
@property({ type: Boolean }) get loading(): boolean {
|
|
155
187
|
return this._isLoading;
|
|
@@ -162,56 +194,62 @@ export class HistogramDateRange extends LitElement {
|
|
|
162
194
|
|
|
163
195
|
/** formatted minimum date of selected date range */
|
|
164
196
|
@property() get minSelectedDate(): string {
|
|
165
|
-
return this.formatDate(this._minSelectedDate);
|
|
197
|
+
return this.formatDate(this.getMSFromString(this._minSelectedDate));
|
|
166
198
|
}
|
|
167
199
|
|
|
200
|
+
/** updates minSelectedDate if new date is valid */
|
|
168
201
|
set minSelectedDate(rawDate: string) {
|
|
169
202
|
if (!this._minSelectedDate) {
|
|
170
203
|
// because the values needed to calculate valid max/min values are not
|
|
171
204
|
// available during the lit init when it's populating properties from
|
|
172
205
|
// attributes, fall back to just the raw date if nothing is already set
|
|
173
206
|
this._minSelectedDate = rawDate;
|
|
207
|
+
return;
|
|
174
208
|
}
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
209
|
+
const proposedDateMS = this.getMSFromString(rawDate);
|
|
210
|
+
const isValidDate = !Number.isNaN(proposedDateMS);
|
|
211
|
+
const isNotTooRecent =
|
|
212
|
+
proposedDateMS <= this.getMSFromString(this.maxSelectedDate);
|
|
213
|
+
if (isValidDate && isNotTooRecent) {
|
|
214
|
+
this._minSelectedDate = this.formatDate(proposedDateMS);
|
|
179
215
|
}
|
|
180
216
|
this.requestUpdate();
|
|
181
217
|
}
|
|
182
218
|
|
|
183
219
|
/** formatted maximum date of selected date range */
|
|
184
220
|
@property() get maxSelectedDate(): string {
|
|
185
|
-
return this.formatDate(this._maxSelectedDate);
|
|
221
|
+
return this.formatDate(this.getMSFromString(this._maxSelectedDate));
|
|
186
222
|
}
|
|
187
223
|
|
|
224
|
+
/** updates maxSelectedDate if new date is valid */
|
|
188
225
|
set maxSelectedDate(rawDate: string) {
|
|
189
226
|
if (!this._maxSelectedDate) {
|
|
190
|
-
//
|
|
227
|
+
// because the values needed to calculate valid max/min values are not
|
|
228
|
+
// available during the lit init when it's populating properties from
|
|
229
|
+
// attributes, fall back to just the raw date if nothing is already set
|
|
191
230
|
this._maxSelectedDate = rawDate;
|
|
231
|
+
return;
|
|
192
232
|
}
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
233
|
+
const proposedDateMS = this.getMSFromString(rawDate);
|
|
234
|
+
const isValidDate = !Number.isNaN(proposedDateMS);
|
|
235
|
+
const isNotTooOld =
|
|
236
|
+
proposedDateMS >= this.getMSFromString(this.minSelectedDate);
|
|
237
|
+
if (isValidDate && isNotTooOld) {
|
|
238
|
+
this._maxSelectedDate = this.formatDate(proposedDateMS);
|
|
197
239
|
}
|
|
198
240
|
this.requestUpdate();
|
|
199
241
|
}
|
|
242
|
+
|
|
200
243
|
/** horizontal position of min date slider */
|
|
201
244
|
get minSliderX(): number {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
this.translateDateToPosition(this.minSelectedDate) ?? this.sliderWidth
|
|
205
|
-
);
|
|
245
|
+
const x = this.translateDateToPosition(this.minSelectedDate);
|
|
246
|
+
return this.validMinSliderX(x);
|
|
206
247
|
}
|
|
207
248
|
|
|
208
249
|
/** horizontal position of max date slider */
|
|
209
250
|
get maxSliderX(): number {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
this.translateDateToPosition(this.maxSelectedDate) ??
|
|
213
|
-
this.width - this.sliderWidth
|
|
214
|
-
);
|
|
251
|
+
const x = this.translateDateToPosition(this.maxSelectedDate);
|
|
252
|
+
return this.validMaxSliderX(x);
|
|
215
253
|
}
|
|
216
254
|
|
|
217
255
|
private get dateRangeMS(): number {
|
|
@@ -224,14 +262,15 @@ export class HistogramDateRange extends LitElement {
|
|
|
224
262
|
}
|
|
225
263
|
const target = e.currentTarget as SVGRectElement;
|
|
226
264
|
const x = target.x.baseVal.value + this.sliderWidth / 2;
|
|
227
|
-
const dataset = target.dataset;
|
|
265
|
+
const dataset = target.dataset as BarDataset;
|
|
228
266
|
const itemsText = `item${dataset.numItems !== '1' ? 's' : ''}`;
|
|
267
|
+
const formattedNumItems = Number(dataset.numItems).toLocaleString();
|
|
229
268
|
|
|
230
269
|
this._tooltipOffset =
|
|
231
270
|
x + (this._binWidth - this.sliderWidth - this.tooltipWidth) / 2;
|
|
232
271
|
|
|
233
272
|
this._tooltipContent = html`
|
|
234
|
-
${
|
|
273
|
+
${formattedNumItems} ${itemsText}<br />
|
|
235
274
|
${dataset.binStart} - ${dataset.binEnd}
|
|
236
275
|
`;
|
|
237
276
|
this._tooltipVisible = true;
|
|
@@ -274,11 +313,14 @@ export class HistogramDateRange extends LitElement {
|
|
|
274
313
|
private move = (e: PointerEvent): void => {
|
|
275
314
|
const newX = e.offsetX - this._dragOffset;
|
|
276
315
|
const slider = this._currentSlider as SVGRectElement;
|
|
277
|
-
const date = this.translatePositionToDate(newX);
|
|
278
316
|
if ((slider.id as SliderId) === 'slider-min') {
|
|
279
|
-
this.minSelectedDate =
|
|
317
|
+
this.minSelectedDate = this.translatePositionToDate(
|
|
318
|
+
this.validMinSliderX(newX)
|
|
319
|
+
);
|
|
280
320
|
} else {
|
|
281
|
-
this.maxSelectedDate =
|
|
321
|
+
this.maxSelectedDate = this.translatePositionToDate(
|
|
322
|
+
this.validMaxSliderX(newX)
|
|
323
|
+
);
|
|
282
324
|
}
|
|
283
325
|
};
|
|
284
326
|
|
|
@@ -286,26 +328,42 @@ export class HistogramDateRange extends LitElement {
|
|
|
286
328
|
* Constrain a proposed value for the minimum (left) slider
|
|
287
329
|
*
|
|
288
330
|
* If the value is less than the leftmost valid position, then set it to the
|
|
289
|
-
* left edge of the
|
|
290
|
-
* the rightmost valid position (the position of the max slider), then
|
|
291
|
-
* to the position of the max slider
|
|
331
|
+
* left edge of the histogram (ie the slider width). If the value is greater
|
|
332
|
+
* than the rightmost valid position (the position of the max slider), then
|
|
333
|
+
* set it to the position of the max slider
|
|
292
334
|
*/
|
|
293
335
|
private validMinSliderX(newX: number): number {
|
|
294
|
-
|
|
295
|
-
|
|
336
|
+
// allow the left slider to go right only to the right slider, even if the
|
|
337
|
+
// max selected date is out of range
|
|
338
|
+
const rightLimit = Math.min(
|
|
339
|
+
this.translateDateToPosition(this.maxSelectedDate),
|
|
340
|
+
this.histogramRightEdgeX
|
|
341
|
+
);
|
|
342
|
+
newX = this.clamp(newX, this.histogramLeftEdgeX, rightLimit);
|
|
343
|
+
const isInvalid =
|
|
344
|
+
Number.isNaN(newX) || rightLimit < this.histogramLeftEdgeX;
|
|
345
|
+
return isInvalid ? this.histogramLeftEdgeX : newX;
|
|
296
346
|
}
|
|
297
347
|
|
|
298
348
|
/**
|
|
299
349
|
* Constrain a proposed value for the maximum (right) slider
|
|
300
350
|
*
|
|
301
351
|
* If the value is greater than the rightmost valid position, then set it to
|
|
302
|
-
* the right edge of the
|
|
303
|
-
* is less than the leftmost valid position (the position of the min
|
|
304
|
-
* then set it to the position of the min slider
|
|
352
|
+
* the right edge of the histogram (ie histogram width - slider width). If the
|
|
353
|
+
* value is less than the leftmost valid position (the position of the min
|
|
354
|
+
* slider), then set it to the position of the min slider
|
|
305
355
|
*/
|
|
306
356
|
private validMaxSliderX(newX: number): number {
|
|
307
|
-
|
|
308
|
-
|
|
357
|
+
// allow the right slider to go left only to the left slider, even if the
|
|
358
|
+
// min selected date is out of range
|
|
359
|
+
const leftLimit = Math.max(
|
|
360
|
+
this.histogramLeftEdgeX,
|
|
361
|
+
this.translateDateToPosition(this.minSelectedDate)
|
|
362
|
+
);
|
|
363
|
+
newX = this.clamp(newX, leftLimit, this.histogramRightEdgeX);
|
|
364
|
+
const isInvalid =
|
|
365
|
+
Number.isNaN(newX) || leftLimit > this.histogramRightEdgeX;
|
|
366
|
+
return isInvalid ? this.histogramRightEdgeX : newX;
|
|
309
367
|
}
|
|
310
368
|
|
|
311
369
|
private addListeners(): void {
|
|
@@ -387,18 +445,22 @@ export class HistogramDateRange extends LitElement {
|
|
|
387
445
|
}
|
|
388
446
|
|
|
389
447
|
/**
|
|
390
|
-
* Returns slider x-position corresponding to given date
|
|
391
|
-
* date)
|
|
448
|
+
* Returns slider x-position corresponding to given date
|
|
392
449
|
*
|
|
393
450
|
* @param date
|
|
394
451
|
* @returns x-position of slider
|
|
395
452
|
*/
|
|
396
|
-
private translateDateToPosition(date: string): number
|
|
397
|
-
const milliseconds =
|
|
398
|
-
|
|
453
|
+
private translateDateToPosition(date: string): number {
|
|
454
|
+
const milliseconds = this.getMSFromString(date);
|
|
455
|
+
return (
|
|
399
456
|
this.sliderWidth +
|
|
400
|
-
((milliseconds - this._minDateMS) * this._histWidth) / this.dateRangeMS
|
|
401
|
-
|
|
457
|
+
((milliseconds - this._minDateMS) * this._histWidth) / this.dateRangeMS
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/** ensure that the returned value is between minValue and maxValue */
|
|
462
|
+
private clamp(x: number, minValue: number, maxValue: number): number {
|
|
463
|
+
return Math.min(Math.max(x, minValue), maxValue);
|
|
402
464
|
}
|
|
403
465
|
|
|
404
466
|
private handleMinDateInput(e: InputEvent): void {
|
|
@@ -413,29 +475,51 @@ export class HistogramDateRange extends LitElement {
|
|
|
413
475
|
this.beginEmitUpdateProcess();
|
|
414
476
|
}
|
|
415
477
|
|
|
416
|
-
private
|
|
417
|
-
|
|
478
|
+
private handleKeyUp(e: KeyboardEvent): void {
|
|
479
|
+
if (e.key === 'Enter') {
|
|
480
|
+
const target = e.currentTarget as HTMLInputElement;
|
|
481
|
+
target.blur();
|
|
482
|
+
}
|
|
418
483
|
}
|
|
419
484
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
return dayjs(this.minSelectedDate).valueOf();
|
|
485
|
+
private get currentDateRangeString(): string {
|
|
486
|
+
return `${this.minSelectedDate}:${this.maxSelectedDate}`;
|
|
423
487
|
}
|
|
424
488
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
489
|
+
private getMSFromString(date: string): number {
|
|
490
|
+
const digitGroupCount = (date.split(/(\d+)/).length - 1) / 2;
|
|
491
|
+
if (digitGroupCount === 1) {
|
|
492
|
+
// if there's just a single set of digits, assume it's a year
|
|
493
|
+
const dateObj = new Date(0, 0); // start at January 1, 1900
|
|
494
|
+
dateObj.setFullYear(Number(date)); // override year (=> 0099-01-01) = 99 CE
|
|
495
|
+
return dateObj.getTime(); // get time in milliseconds
|
|
496
|
+
}
|
|
497
|
+
return dayjs(date, [this.dateFormat, DATE_FORMAT]).valueOf();
|
|
428
498
|
}
|
|
429
499
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
500
|
+
/**
|
|
501
|
+
* expand or narrow the selected range by moving the slider nearest the
|
|
502
|
+
* clicked bar to the outer edge of the clicked bar
|
|
503
|
+
*
|
|
504
|
+
* @param e Event click event from a histogram bar
|
|
505
|
+
*/
|
|
506
|
+
private handleBarClick(e: Event): void {
|
|
507
|
+
const dataset = (e.currentTarget as SVGRectElement).dataset as BarDataset;
|
|
508
|
+
const clickPosition =
|
|
509
|
+
(this.getMSFromString(dataset.binStart) +
|
|
510
|
+
this.getMSFromString(dataset.binEnd)) /
|
|
511
|
+
2;
|
|
512
|
+
const distanceFromMinSlider = Math.abs(
|
|
513
|
+
clickPosition - this.getMSFromString(this.minSelectedDate)
|
|
514
|
+
);
|
|
515
|
+
const distanceFromMaxSlider = Math.abs(
|
|
516
|
+
clickPosition - this.getMSFromString(this.maxSelectedDate)
|
|
517
|
+
);
|
|
518
|
+
// update the selected range by moving the nearer slider
|
|
519
|
+
if (distanceFromMinSlider < distanceFromMaxSlider) {
|
|
520
|
+
this.minSelectedDate = dataset.binStart;
|
|
521
|
+
} else {
|
|
522
|
+
this.maxSelectedDate = dataset.binEnd;
|
|
439
523
|
}
|
|
440
524
|
this.beginEmitUpdateProcess();
|
|
441
525
|
}
|
|
@@ -482,10 +566,12 @@ export class HistogramDateRange extends LitElement {
|
|
|
482
566
|
return svg`
|
|
483
567
|
<svg
|
|
484
568
|
id="${id}"
|
|
485
|
-
class="
|
|
569
|
+
class="
|
|
570
|
+
${this.disabled ? '' : 'draggable'}
|
|
571
|
+
${this._isDragging ? 'dragging' : ''}"
|
|
486
572
|
@pointerdown="${this.drag}"
|
|
487
573
|
>
|
|
488
|
-
<path d="${sliderShape} z" fill="${
|
|
574
|
+
<path d="${sliderShape} z" fill="${sliderColor}" />
|
|
489
575
|
<rect
|
|
490
576
|
x="${
|
|
491
577
|
sliderPositionX - this.sliderWidth * k + this.sliderWidth * 0.4 * k
|
|
@@ -515,7 +601,7 @@ export class HistogramDateRange extends LitElement {
|
|
|
515
601
|
y="0"
|
|
516
602
|
width="${this.maxSliderX - this.minSliderX}"
|
|
517
603
|
height="${this.height}"
|
|
518
|
-
fill="${
|
|
604
|
+
fill="${selectedRangeColor}"
|
|
519
605
|
/>`;
|
|
520
606
|
}
|
|
521
607
|
|
|
@@ -543,7 +629,7 @@ export class HistogramDateRange extends LitElement {
|
|
|
543
629
|
@pointerleave="${this.hideTooltip}"
|
|
544
630
|
@click="${this.handleBarClick}"
|
|
545
631
|
fill="${
|
|
546
|
-
x >= this.minSliderX && x <= this.maxSliderX
|
|
632
|
+
x + barWidth >= this.minSliderX && x <= this.maxSliderX
|
|
547
633
|
? barIncludedFill
|
|
548
634
|
: barExcludedFill
|
|
549
635
|
}"
|
|
@@ -556,9 +642,17 @@ export class HistogramDateRange extends LitElement {
|
|
|
556
642
|
});
|
|
557
643
|
}
|
|
558
644
|
|
|
559
|
-
private formatDate(
|
|
560
|
-
|
|
561
|
-
|
|
645
|
+
private formatDate(dateMS: number): string {
|
|
646
|
+
if (Number.isNaN(dateMS)) {
|
|
647
|
+
return '';
|
|
648
|
+
}
|
|
649
|
+
const date = dayjs(dateMS);
|
|
650
|
+
if (date.year() < 1000) {
|
|
651
|
+
// years before 1000 don't play well with dayjs custom formatting, so fall
|
|
652
|
+
// back to displaying only the year
|
|
653
|
+
return String(date.year());
|
|
654
|
+
}
|
|
655
|
+
return date.format(this.dateFormat);
|
|
562
656
|
}
|
|
563
657
|
|
|
564
658
|
/**
|
|
@@ -575,6 +669,7 @@ export class HistogramDateRange extends LitElement {
|
|
|
575
669
|
type="text"
|
|
576
670
|
@focus="${this.cancelPendingUpdateEvent}"
|
|
577
671
|
@blur="${this.handleMinDateInput}"
|
|
672
|
+
@keyup="${this.handleKeyUp}"
|
|
578
673
|
.value="${live(this.minSelectedDate)}"
|
|
579
674
|
?disabled="${this.disabled}"
|
|
580
675
|
/>
|
|
@@ -589,6 +684,7 @@ export class HistogramDateRange extends LitElement {
|
|
|
589
684
|
type="text"
|
|
590
685
|
@focus="${this.cancelPendingUpdateEvent}"
|
|
591
686
|
@blur="${this.handleMaxDateInput}"
|
|
687
|
+
@keyup="${this.handleKeyUp}"
|
|
592
688
|
.value="${live(this.maxSelectedDate)}"
|
|
593
689
|
?disabled="${this.disabled}"
|
|
594
690
|
/>
|
|
@@ -619,9 +715,9 @@ export class HistogramDateRange extends LitElement {
|
|
|
619
715
|
`;
|
|
620
716
|
}
|
|
621
717
|
|
|
622
|
-
private get activityIndicatorTemplate(): TemplateResult {
|
|
718
|
+
private get activityIndicatorTemplate(): TemplateResult | typeof nothing {
|
|
623
719
|
if (!this.loading) {
|
|
624
|
-
return
|
|
720
|
+
return nothing;
|
|
625
721
|
}
|
|
626
722
|
return html`
|
|
627
723
|
<ia-activity-indicator mode="processing"> </ia-activity-indicator>
|
|
@@ -685,7 +781,7 @@ export class HistogramDateRange extends LitElement {
|
|
|
685
781
|
border-radius: 3px;
|
|
686
782
|
padding: 2px;
|
|
687
783
|
font-size: ${tooltipFontSize};
|
|
688
|
-
font-family:
|
|
784
|
+
font-family: ${tooltipFontFamily};
|
|
689
785
|
touch-action: none;
|
|
690
786
|
pointer-events: none;
|
|
691
787
|
}
|
|
@@ -722,6 +818,7 @@ export class HistogramDateRange extends LitElement {
|
|
|
722
818
|
border-radius: 2px !important;
|
|
723
819
|
text-align: center;
|
|
724
820
|
font-size: ${inputFontSize};
|
|
821
|
+
font-family: ${inputFontFamily};
|
|
725
822
|
}
|
|
726
823
|
`;
|
|
727
824
|
|
|
@@ -762,6 +859,9 @@ export class HistogramDateRange extends LitElement {
|
|
|
762
859
|
`;
|
|
763
860
|
}
|
|
764
861
|
}
|
|
862
|
+
|
|
863
|
+
// help TypeScript provide strong typing when interacting with DOM APIs
|
|
864
|
+
// https://stackoverflow.com/questions/65148695/lit-element-typescript-project-global-interface-declaration-necessary
|
|
765
865
|
declare global {
|
|
766
866
|
interface HTMLElementTagNameMap {
|
|
767
867
|
'histogram-date-range': HistogramDateRange;
|