@internetarchive/histogram-date-range 0.1.3-alpha → 0.1.6

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,447 +0,0 @@
1
- import { __decorate } from "tslib";
2
- import { html, svg, css, internalProperty, LitElement, property, query, } from 'lit-element';
3
- import dayjs from 'dayjs/esm/index.js';
4
- // these values can be overridden via the component's HTML (camelCased) attributes
5
- const WIDTH = 180;
6
- const HEIGHT = 40;
7
- const SLIDER_WIDTH = 10;
8
- const TOOLTIP_WIDTH = 125;
9
- const TOOLTIP_HEIGHT = 30;
10
- const DATE_FORMAT = 'M/D/YYYY';
11
- // this constant is not set up to be overridden
12
- const SLIDER_CORNER_SIZE = 4;
13
- // these CSS custom props can be overridden from the HTML that is invoking this component
14
- const sliderFill = 'var(--histogramDateRangeSliderFill, #4B65FE)';
15
- const selectedRangeFill = 'var(--histogramDateRangeSelectedRangeFill, #DBE0FF)';
16
- const barIncludedFill = 'var(--histogramDateRangeBarIncludedFill, #2C2C2C)';
17
- const barExcludedFill = 'var(--histogramDateRangeBarExcludedFill, #CCCCCC)';
18
- const inputBorder = css `var(--histogramDateRangeInputBorder, 0.5px solid #2C2C2C)`;
19
- const inputWidth = css `var(--histogramDateRangeInputWidth, 70px)`;
20
- const inputFontSize = css `var(--histogramDateRangeInputFontSize, 1.2rem)`;
21
- const tooltipBackgroundColor = css `var(--histogramDateRangeTooltipBackgroundColor, #2C2C2C)`;
22
- const tooltipTextColor = css `var(--histogramDateRangeTooltipTextColor, #FFFFFF)`;
23
- const tooltipFontSize = css `var(--histogramDateRangeTooltipFontSize, 1.1rem)`;
24
- export class HistogramDateRange extends LitElement {
25
- constructor() {
26
- /* eslint-disable lines-between-class-members */
27
- super(...arguments);
28
- // these properties are intended to be passed in as attributes
29
- this.width = WIDTH;
30
- this.height = HEIGHT;
31
- this.sliderWidth = SLIDER_WIDTH;
32
- this.tooltipWidth = TOOLTIP_WIDTH;
33
- this.tooltipHeight = TOOLTIP_HEIGHT;
34
- this.dateFormat = DATE_FORMAT;
35
- this.minSliderX = 0;
36
- this.maxSliderX = 0;
37
- this.tooltipOffset = 0;
38
- this.tooltipContent = '';
39
- this.tooltipDisplay = 'none';
40
- // these properties don't need to be tracked for changes
41
- this._minDate = 0;
42
- this._maxDate = 0;
43
- this._dragOffset = 0;
44
- this._histWidth = 0;
45
- this._numBins = 0;
46
- this._binWidth = 0;
47
- this._histData = [];
48
- // use arrow functions (rather than standard JS class instance methods) so
49
- // that `this` is bound to the histogramDateRange object and not the event
50
- // target. for more info see
51
- // https://lit-element.polymer-project.org/guide/events#using-this-in-event-listeners
52
- this.drag = (e) => {
53
- // prevent selecting text or other ranges while dragging, especially in Safari
54
- e.preventDefault();
55
- this.setDragOffset(e);
56
- this.container.classList.add('dragging');
57
- window.addEventListener('pointermove', this.move);
58
- window.addEventListener('pointerup', this.drop);
59
- window.addEventListener('pointercancel', this.drop);
60
- };
61
- this.drop = () => {
62
- this.container.classList.remove('dragging');
63
- window.removeEventListener('pointermove', this.move);
64
- window.removeEventListener('pointerup', this.drop);
65
- window.removeEventListener('pointercancel', this.drop);
66
- };
67
- this.move = (e) => {
68
- const newX = e.offsetX - this._dragOffset;
69
- const slider = this._currentSlider;
70
- return slider.id === 'slider-min'
71
- ? this.setMinSlider(newX)
72
- : this.setMaxSlider(newX);
73
- };
74
- }
75
- /* eslint-enable lines-between-class-members */
76
- firstUpdated() {
77
- var _a, _b, _c, _d, _e;
78
- this.minSliderX = this.sliderWidth;
79
- this.maxSliderX = this.width - this.sliderWidth;
80
- this._minDate = dayjs((_a = this.data) === null || _a === void 0 ? void 0 : _a.minDate).valueOf();
81
- this._maxDate = dayjs((_b = this.data) === null || _b === void 0 ? void 0 : _b.maxDate).valueOf();
82
- this._histWidth = this.width - this.sliderWidth * 2;
83
- this._numBins = (_e = (_d = (_c = this.data) === null || _c === void 0 ? void 0 : _c.bins) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 1;
84
- this._binWidth = this._histWidth / this._numBins;
85
- this._histData = this.generateHistData();
86
- }
87
- generateHistData() {
88
- if (!this.data) {
89
- return [];
90
- }
91
- const minValue = Math.min(...this.data.bins);
92
- const maxValue = Math.max(...this.data.bins);
93
- const valueScale = this.height / Math.log1p(maxValue - minValue);
94
- const dateScale = this.dateRange / this._numBins;
95
- return this.data.bins.map((v, i) => {
96
- return {
97
- value: v,
98
- height: Math.floor(Math.log1p(v) * valueScale),
99
- binStart: `${dayjs(i * dateScale + this._minDate).format(this.dateFormat)}`,
100
- binEnd: `${dayjs((i + 1) * dateScale + this._minDate).format(this.dateFormat)}`,
101
- };
102
- });
103
- }
104
- get dateRange() {
105
- return this._maxDate - this._minDate;
106
- }
107
- showTooltip(e) {
108
- if (Array.from(this.container.classList).includes('dragging')) {
109
- return;
110
- }
111
- const target = e.currentTarget;
112
- const x = target.x.baseVal.value + this.sliderWidth / 2;
113
- const data = target.dataset;
114
- const itemsText = `item${data.numItems !== '1' ? 's' : ''}`;
115
- this.tooltipOffset =
116
- x + (this._binWidth - this.sliderWidth - this.tooltipWidth) / 2;
117
- this.tooltipContent = html `
118
- ${data.numItems} ${itemsText}<br />
119
- ${data.binStart} - ${data.binEnd}
120
- `;
121
- this.tooltipDisplay = 'block';
122
- }
123
- hideTooltip() {
124
- this.tooltipContent = '';
125
- this.tooltipDisplay = 'none';
126
- }
127
- // find position of pointer in relation to the current slider
128
- setDragOffset(e) {
129
- this._currentSlider = e.currentTarget;
130
- const sliderX = this._currentSlider.id === 'slider-min'
131
- ? this.minSliderX
132
- : this.maxSliderX;
133
- this._dragOffset = e.offsetX - sliderX;
134
- // work around Firefox issue where e.offsetX seems to be not based on current
135
- // element but on background element
136
- if (this._dragOffset > this.sliderWidth ||
137
- this._dragOffset < -this.sliderWidth) {
138
- this._dragOffset = 0;
139
- }
140
- }
141
- setMinSlider(newX) {
142
- const toSet = Math.max(newX, this.sliderWidth);
143
- this.minSliderX = Math.min(toSet, this.maxSliderX);
144
- }
145
- setMaxSlider(newX) {
146
- const toSet = Math.max(newX, this.minSliderX);
147
- this.maxSliderX = Math.min(toSet, this.width - this.sliderWidth);
148
- }
149
- translatePositionToDate(x) {
150
- const milliseconds = ((x - this.sliderWidth) * this.dateRange) / this._histWidth;
151
- const date = dayjs(this._minDate + milliseconds);
152
- return date.format(this.dateFormat);
153
- }
154
- translateDateToPosition(date) {
155
- const milliseconds = dayjs(date).valueOf();
156
- if (!milliseconds) {
157
- return null;
158
- }
159
- // translate where we are within the date range into what the new x-position
160
- // of the slider should be
161
- return (this.sliderWidth +
162
- ((milliseconds - this._minDate) * this._histWidth) / this.dateRange);
163
- }
164
- handleMinDateInput(e) {
165
- const target = e.currentTarget;
166
- const newX = this.translateDateToPosition(target.value);
167
- if (newX) {
168
- this.setMinSlider(newX);
169
- }
170
- target.value = this.minInputValue;
171
- }
172
- handleMaxDateInput(e) {
173
- const target = e.currentTarget;
174
- const newX = this.translateDateToPosition(target.value);
175
- if (newX) {
176
- this.setMaxSlider(newX);
177
- }
178
- target.value = this.maxInputValue;
179
- }
180
- get minInputValue() {
181
- return this.translatePositionToDate(this.minSliderX);
182
- }
183
- get maxInputValue() {
184
- return this.translatePositionToDate(this.maxSliderX);
185
- }
186
- get minSliderTemplate() {
187
- // width/height in pixels of curved part of the sliders (like
188
- // border-radius); used as part of a SVG quadratic curve. see
189
- // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#curve_commands
190
- const c = SLIDER_CORNER_SIZE;
191
- const sliderShape = `
192
- M${this.minSliderX},0
193
- h-${this.sliderWidth - c}
194
- q-${c},0 -${c},${c}
195
- v${this.height - c * 2}
196
- q0,${c} ${c},${c}
197
- h${this.sliderWidth - c}
198
- `;
199
- return this.generateSliderSVG(this.minSliderX, 'slider-min', sliderShape);
200
- }
201
- get maxSliderTemplate() {
202
- const c = SLIDER_CORNER_SIZE;
203
- const sliderShape = `
204
- M${this.maxSliderX},0
205
- h${this.sliderWidth - c}
206
- q${c},0 ${c},${c}
207
- v${this.height - c * 2}
208
- q0,${c} -${c},${c}
209
- h-${this.sliderWidth - c}
210
- `;
211
- return this.generateSliderSVG(this.maxSliderX, 'slider-max', sliderShape);
212
- }
213
- generateSliderSVG(sliderPositionX, id, sliderShape) {
214
- // whether the curved part of the slider is facing towards the left (1), ie
215
- // minimum, or facing towards the right (-1), ie maximum
216
- const k = id === 'slider-min' ? 1 : -1;
217
- return svg `
218
- <svg
219
- id="${id}"
220
- @pointerdown="${this.drag}"
221
- >
222
- <path d="${sliderShape} z" fill="${sliderFill}" />
223
- <rect
224
- x="${sliderPositionX - this.sliderWidth * k + this.sliderWidth * 0.4 * k}"
225
- y="${this.height / 3}"
226
- width="1"
227
- height="${this.height / 3}"
228
- fill="white"
229
- />
230
- <rect
231
- x="${sliderPositionX - this.sliderWidth * k + this.sliderWidth * 0.6 * k}"
232
- y="${this.height / 3}"
233
- width="1"
234
- height="${this.height / 3}"
235
- fill="white"
236
- />
237
- </svg>
238
- `;
239
- }
240
- get selectedRangeTemplate() {
241
- return svg `
242
- <rect
243
- x="${this.minSliderX}"
244
- y="0"
245
- width="${this.maxSliderX - this.minSliderX}"
246
- height="${this.height}"
247
- fill="${selectedRangeFill}"
248
- />`;
249
- }
250
- get histogramTemplate() {
251
- const xScale = this._histWidth / this._numBins;
252
- const barWidth = xScale - 1;
253
- let x = this.sliderWidth; // start at the left edge of the histogram
254
- return this._histData.map(data => {
255
- const bar = svg `
256
- <rect
257
- class="bar"
258
- x="${x}"
259
- y="${this.height - data.height}"
260
- width="${barWidth}"
261
- height="${data.height}"
262
- @pointerenter="${this.showTooltip}"
263
- @pointerleave="${this.hideTooltip}"
264
- fill="${x >= this.minSliderX && x <= this.maxSliderX
265
- ? barIncludedFill
266
- : barExcludedFill}"
267
- data-num-items="${data.value}"
268
- data-bin-start="${data.binStart}"
269
- data-bin-end="${data.binEnd}"
270
- />`;
271
- x += xScale;
272
- return bar;
273
- });
274
- }
275
- get minInputTemplate() {
276
- return html `
277
- <input
278
- id="date-min"
279
- placeholder="${DATE_FORMAT}"
280
- type="text"
281
- @change="${this.handleMinDateInput}"
282
- .value="${this.minInputValue}"
283
- />
284
- `;
285
- }
286
- get maxInputTemplate() {
287
- return html `
288
- <input
289
- id="date-max"
290
- placeholder="${DATE_FORMAT}"
291
- type="text"
292
- @change="${this.handleMaxDateInput}"
293
- .value="${this.maxInputValue}"
294
- />
295
- `;
296
- }
297
- get tooltipTemplate() {
298
- return html `
299
- <style>
300
- #tooltip {
301
- width: ${this.tooltipWidth}px;
302
- height: ${this.tooltipHeight}px;
303
- top: ${-9 - this.tooltipHeight}px;
304
- left: ${this.tooltipOffset}px;
305
- display: ${this.tooltipDisplay};
306
- }
307
- #tooltip:after {
308
- left: ${this.tooltipWidth / 2}px;
309
- }
310
- </style>
311
- <div id="tooltip">${this.tooltipContent}</div>
312
- `;
313
- }
314
- render() {
315
- if (!this.data || !this._histData) {
316
- return html `no data`;
317
- }
318
- return html `
319
- <div id="container" class="noselect" style="width: ${this.width}px">
320
- ${this.tooltipTemplate}
321
- <svg
322
- width="${this.width}"
323
- height="${this.height}"
324
- @pointerleave="${this.drop}"
325
- >
326
- ${this.selectedRangeTemplate}
327
- <svg id="histogram">${this.histogramTemplate}</svg>
328
- ${this.minSliderTemplate} ${this.maxSliderTemplate}
329
- </svg>
330
- <div id="inputs">
331
- ${this.minInputTemplate}
332
- <div class="dash">-</div>
333
- ${this.maxInputTemplate}
334
- </div>
335
- </div>
336
- `;
337
- }
338
- }
339
- HistogramDateRange.styles = css `
340
- #container {
341
- margin: 0;
342
- touch-action: none;
343
- position: relative;
344
- }
345
- /* prevent selection from interfering with tooltip, especially on mobile */
346
- /* https://stackoverflow.com/a/4407335/1163042 */
347
- .noselect {
348
- -webkit-touch-callout: none; /* iOS Safari */
349
- -webkit-user-select: none; /* Safari */
350
- -moz-user-select: none; /* Old versions of Firefox */
351
- -ms-user-select: none; /* Internet Explorer/Edge */
352
- user-select: none; /* current Chrome, Edge, Opera and Firefox */
353
- }
354
- .bar:hover {
355
- fill-opacity: 0.7;
356
- }
357
- /****** histogram ********/
358
- #tooltip {
359
- position: absolute;
360
- background: ${tooltipBackgroundColor};
361
- color: ${tooltipTextColor};
362
- text-align: center;
363
- border-radius: 3px;
364
- padding: 2px;
365
- font-size: ${tooltipFontSize};
366
- font-family: sans-serif;
367
- touch-action: none;
368
- pointer-events: none;
369
- }
370
- #tooltip:after {
371
- content: '';
372
- position: absolute;
373
- margin-left: -5px;
374
- top: 100%;
375
- /* arrow */
376
- border: 5px solid ${tooltipTextColor};
377
- border-color: ${tooltipBackgroundColor} transparent transparent
378
- transparent;
379
- }
380
- /****** slider ********/
381
- .draggable:hover {
382
- cursor: grab;
383
- }
384
- .dragging {
385
- cursor: grabbing !important;
386
- }
387
- /****** inputs ********/
388
- #inputs {
389
- display: flex;
390
- justify-content: center;
391
- }
392
- #inputs .dash {
393
- position: relative;
394
- bottom: -1px;
395
- }
396
- input {
397
- width: ${inputWidth};
398
- margin: 0 3px;
399
- border: ${inputBorder};
400
- border-radius: 2px !important;
401
- text-align: center;
402
- font-size: ${inputFontSize};
403
- }
404
- `;
405
- __decorate([
406
- property({ type: Number })
407
- ], HistogramDateRange.prototype, "width", void 0);
408
- __decorate([
409
- property({ type: Number })
410
- ], HistogramDateRange.prototype, "height", void 0);
411
- __decorate([
412
- property({ type: Number })
413
- ], HistogramDateRange.prototype, "sliderWidth", void 0);
414
- __decorate([
415
- property({ type: Number })
416
- ], HistogramDateRange.prototype, "tooltipWidth", void 0);
417
- __decorate([
418
- property({ type: Number })
419
- ], HistogramDateRange.prototype, "tooltipHeight", void 0);
420
- __decorate([
421
- property({ type: String })
422
- ], HistogramDateRange.prototype, "dateFormat", void 0);
423
- __decorate([
424
- property({ type: Object })
425
- ], HistogramDateRange.prototype, "data", void 0);
426
- __decorate([
427
- internalProperty()
428
- ], HistogramDateRange.prototype, "minSliderX", void 0);
429
- __decorate([
430
- internalProperty()
431
- ], HistogramDateRange.prototype, "maxSliderX", void 0);
432
- __decorate([
433
- internalProperty()
434
- ], HistogramDateRange.prototype, "tooltipOffset", void 0);
435
- __decorate([
436
- internalProperty()
437
- ], HistogramDateRange.prototype, "tooltipContent", void 0);
438
- __decorate([
439
- internalProperty()
440
- ], HistogramDateRange.prototype, "tooltipDisplay", void 0);
441
- __decorate([
442
- query('#tooltip')
443
- ], HistogramDateRange.prototype, "tooltip", void 0);
444
- __decorate([
445
- query('#container')
446
- ], HistogramDateRange.prototype, "container", void 0);
447
- //# sourceMappingURL=HistogramDateRange.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"HistogramDateRange.js","sourceRoot":"","sources":["../../src/HistogramDateRange.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n html,\n svg,\n css,\n internalProperty,\n LitElement,\n TemplateResult,\n SVGTemplateResult,\n property,\n query,\n} from 'lit-element';\nimport dayjs from 'dayjs/esm/index.js';\n\n// these values can be overridden via the component's HTML (camelCased) attributes\nconst WIDTH = 180;\nconst HEIGHT = 40;\nconst SLIDER_WIDTH = 10;\nconst TOOLTIP_WIDTH = 125;\nconst TOOLTIP_HEIGHT = 30;\nconst DATE_FORMAT = 'M/D/YYYY';\n\n// this constant is not set up to be overridden\nconst SLIDER_CORNER_SIZE = 4;\n\n// these CSS custom props can be overridden from the HTML that is invoking this component\nconst sliderFill = 'var(--histogramDateRangeSliderFill, #4B65FE)';\nconst selectedRangeFill = 'var(--histogramDateRangeSelectedRangeFill, #DBE0FF)';\nconst barIncludedFill = 'var(--histogramDateRangeBarIncludedFill, #2C2C2C)';\nconst barExcludedFill = 'var(--histogramDateRangeBarExcludedFill, #CCCCCC)';\nconst inputBorder = css`var(--histogramDateRangeInputBorder, 0.5px solid #2C2C2C)`;\nconst inputWidth = css`var(--histogramDateRangeInputWidth, 70px)`;\nconst inputFontSize = css`var(--histogramDateRangeInputFontSize, 1.2rem)`;\nconst tooltipBackgroundColor = css`var(--histogramDateRangeTooltipBackgroundColor, #2C2C2C)`;\nconst tooltipTextColor = css`var(--histogramDateRangeTooltipTextColor, #FFFFFF)`;\nconst tooltipFontSize = css`var(--histogramDateRangeTooltipFontSize, 1.1rem)`;\n\ntype SliderIds = 'slider-min' | 'slider-max';\n\ninterface HistogramInputData {\n minDate: string;\n maxDate: string;\n bins: number[];\n}\n\ninterface HistogramItem {\n value: number;\n height: number;\n binStart: string;\n binEnd: string;\n}\n\nexport class HistogramDateRange extends LitElement {\n /* eslint-disable lines-between-class-members */\n\n // these properties are intended to be passed in as attributes\n @property({ type: Number }) width = WIDTH;\n @property({ type: Number }) height = HEIGHT;\n @property({ type: Number }) sliderWidth = SLIDER_WIDTH;\n @property({ type: Number }) tooltipWidth = TOOLTIP_WIDTH;\n @property({ type: Number }) tooltipHeight = TOOLTIP_HEIGHT;\n @property({ type: String }) dateFormat = DATE_FORMAT;\n @property({ type: Object }) data?: HistogramInputData;\n\n @internalProperty() minSliderX: number = 0;\n @internalProperty() maxSliderX: number = 0;\n @internalProperty() tooltipOffset = 0;\n @internalProperty() tooltipContent: TemplateResult | '' = '';\n @internalProperty() tooltipDisplay: 'block' | 'none' = 'none';\n\n @query('#tooltip') tooltip!: HTMLDivElement;\n @query('#container') container!: HTMLDivElement;\n\n // these properties don't need to be tracked for changes\n private _minDate: number = 0;\n private _maxDate: number = 0;\n private _dragOffset: number = 0;\n private _histWidth: number = 0;\n private _numBins: number = 0;\n private _binWidth: number = 0;\n private _currentSlider?: SVGRectElement;\n private _histData: HistogramItem[] = [];\n\n /* eslint-enable lines-between-class-members */\n\n firstUpdated(): void {\n this.minSliderX = this.sliderWidth;\n this.maxSliderX = this.width - this.sliderWidth;\n this._minDate = dayjs(this.data?.minDate).valueOf();\n this._maxDate = dayjs(this.data?.maxDate).valueOf();\n this._histWidth = this.width - this.sliderWidth * 2;\n this._numBins = this.data?.bins?.length ?? 1;\n this._binWidth = this._histWidth / this._numBins;\n this._histData = this.generateHistData();\n }\n\n private generateHistData(): HistogramItem[] {\n if (!this.data) {\n return [];\n }\n const minValue = Math.min(...this.data.bins);\n const maxValue = Math.max(...this.data.bins);\n const valueScale = this.height / Math.log1p(maxValue - minValue);\n const dateScale = this.dateRange / this._numBins;\n return this.data.bins.map((v: number, i: number) => {\n return {\n value: v,\n height: Math.floor(Math.log1p(v) * valueScale),\n binStart: `${dayjs(i * dateScale + this._minDate).format(\n this.dateFormat\n )}`,\n binEnd: `${dayjs((i + 1) * dateScale + this._minDate).format(\n this.dateFormat\n )}`,\n };\n });\n }\n\n private get dateRange(): number {\n return this._maxDate - this._minDate;\n }\n\n private showTooltip(e: PointerEvent): void {\n if (Array.from(this.container.classList).includes('dragging')) {\n return;\n }\n const target = e.currentTarget as SVGRectElement;\n const x = target.x.baseVal.value + this.sliderWidth / 2;\n const data = target.dataset;\n const itemsText = `item${data.numItems !== '1' ? 's' : ''}`;\n\n this.tooltipOffset =\n x + (this._binWidth - this.sliderWidth - this.tooltipWidth) / 2;\n\n this.tooltipContent = html`\n ${data.numItems} ${itemsText}<br />\n ${data.binStart} - ${data.binEnd}\n `;\n this.tooltipDisplay = 'block';\n }\n\n private hideTooltip(): void {\n this.tooltipContent = '';\n this.tooltipDisplay = 'none';\n }\n\n // use arrow functions (rather than standard JS class instance methods) so\n // that `this` is bound to the histogramDateRange object and not the event\n // target. for more info see\n // https://lit-element.polymer-project.org/guide/events#using-this-in-event-listeners\n private drag = (e: PointerEvent): void => {\n // prevent selecting text or other ranges while dragging, especially in Safari\n e.preventDefault();\n\n this.setDragOffset(e);\n this.container.classList.add('dragging');\n\n window.addEventListener('pointermove', this.move);\n window.addEventListener('pointerup', this.drop);\n window.addEventListener('pointercancel', this.drop);\n };\n\n private drop = (): void => {\n this.container.classList.remove('dragging');\n\n window.removeEventListener('pointermove', this.move);\n window.removeEventListener('pointerup', this.drop);\n window.removeEventListener('pointercancel', this.drop);\n };\n\n private move = (e: PointerEvent): void => {\n const newX = e.offsetX - this._dragOffset;\n const slider = this._currentSlider as SVGRectElement;\n return (slider.id as SliderIds) === 'slider-min'\n ? this.setMinSlider(newX)\n : this.setMaxSlider(newX);\n };\n\n // find position of pointer in relation to the current slider\n private setDragOffset(e: PointerEvent): void {\n this._currentSlider = e.currentTarget as SVGRectElement;\n const sliderX =\n (this._currentSlider.id as SliderIds) === 'slider-min'\n ? this.minSliderX\n : this.maxSliderX;\n this._dragOffset = e.offsetX - sliderX;\n // work around Firefox issue where e.offsetX seems to be not based on current\n // element but on background element\n if (\n this._dragOffset > this.sliderWidth ||\n this._dragOffset < -this.sliderWidth\n ) {\n this._dragOffset = 0;\n }\n }\n\n private setMinSlider(newX: number): void {\n const toSet = Math.max(newX, this.sliderWidth);\n this.minSliderX = Math.min(toSet, this.maxSliderX);\n }\n\n private setMaxSlider(newX: number): void {\n const toSet = Math.max(newX, this.minSliderX);\n this.maxSliderX = Math.min(toSet, this.width - this.sliderWidth);\n }\n\n private translatePositionToDate(x: number): string {\n const milliseconds =\n ((x - this.sliderWidth) * this.dateRange) / this._histWidth;\n const date = dayjs(this._minDate + milliseconds);\n return date.format(this.dateFormat);\n }\n\n private translateDateToPosition(date: string): number | null {\n const milliseconds: number | undefined = dayjs(date).valueOf();\n if (!milliseconds) {\n return null;\n }\n // translate where we are within the date range into what the new x-position\n // of the slider should be\n return (\n this.sliderWidth +\n ((milliseconds - this._minDate) * this._histWidth) / this.dateRange\n );\n }\n\n private handleMinDateInput(e: InputEvent): void {\n const target = e.currentTarget as HTMLInputElement;\n const newX = this.translateDateToPosition(target.value);\n if (newX) {\n this.setMinSlider(newX);\n }\n target.value = this.minInputValue;\n }\n\n private handleMaxDateInput(e: InputEvent): void {\n const target = e.currentTarget as HTMLInputElement;\n const newX = this.translateDateToPosition(target.value);\n if (newX) {\n this.setMaxSlider(newX);\n }\n target.value = this.maxInputValue;\n }\n\n private get minInputValue() {\n return this.translatePositionToDate(this.minSliderX);\n }\n\n private get maxInputValue() {\n return this.translatePositionToDate(this.maxSliderX);\n }\n\n private get minSliderTemplate(): SVGTemplateResult {\n // width/height in pixels of curved part of the sliders (like\n // border-radius); used as part of a SVG quadratic curve. see\n // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#curve_commands\n const c = SLIDER_CORNER_SIZE;\n\n const sliderShape = `\n M${this.minSliderX},0\n h-${this.sliderWidth - c}\n q-${c},0 -${c},${c}\n v${this.height - c * 2}\n q0,${c} ${c},${c}\n h${this.sliderWidth - c}\n `;\n return this.generateSliderSVG(this.minSliderX, 'slider-min', sliderShape);\n }\n\n private get maxSliderTemplate(): SVGTemplateResult {\n const c = SLIDER_CORNER_SIZE;\n const sliderShape = `\n M${this.maxSliderX},0\n h${this.sliderWidth - c}\n q${c},0 ${c},${c}\n v${this.height - c * 2}\n q0,${c} -${c},${c}\n h-${this.sliderWidth - c}\n `;\n return this.generateSliderSVG(this.maxSliderX, 'slider-max', sliderShape);\n }\n\n private generateSliderSVG(\n sliderPositionX: number,\n id: SliderIds,\n sliderShape: string\n ): SVGTemplateResult {\n // whether the curved part of the slider is facing towards the left (1), ie\n // minimum, or facing towards the right (-1), ie maximum\n const k = id === 'slider-min' ? 1 : -1;\n\n return svg`\n <svg\n id=\"${id}\"\n @pointerdown=\"${this.drag}\"\n >\n <path d=\"${sliderShape} z\" fill=\"${sliderFill}\" />\n <rect\n x=\"${\n sliderPositionX - this.sliderWidth * k + this.sliderWidth * 0.4 * k\n }\"\n y=\"${this.height / 3}\"\n width=\"1\"\n height=\"${this.height / 3}\"\n fill=\"white\"\n />\n <rect\n x=\"${\n sliderPositionX - this.sliderWidth * k + this.sliderWidth * 0.6 * k\n }\"\n y=\"${this.height / 3}\"\n width=\"1\"\n height=\"${this.height / 3}\"\n fill=\"white\"\n />\n </svg>\n `;\n }\n\n get selectedRangeTemplate(): SVGTemplateResult {\n return svg`\n <rect\n x=\"${this.minSliderX}\"\n y=\"0\"\n width=\"${this.maxSliderX - this.minSliderX}\"\n height=\"${this.height}\"\n fill=\"${selectedRangeFill}\"\n />`;\n }\n\n get histogramTemplate(): SVGTemplateResult[] {\n const xScale = this._histWidth / this._numBins;\n const barWidth = xScale - 1;\n let x = this.sliderWidth; // start at the left edge of the histogram\n return this._histData.map(data => {\n const bar = svg`\n <rect\n class=\"bar\"\n x=\"${x}\"\n y=\"${this.height - data.height}\"\n width=\"${barWidth}\"\n height=\"${data.height}\"\n @pointerenter=\"${this.showTooltip}\"\n @pointerleave=\"${this.hideTooltip}\"\n fill=\"${\n x >= this.minSliderX && x <= this.maxSliderX\n ? barIncludedFill\n : barExcludedFill\n }\"\n data-num-items=\"${data.value}\"\n data-bin-start=\"${data.binStart}\"\n data-bin-end=\"${data.binEnd}\"\n />`;\n x += xScale;\n return bar;\n });\n }\n\n get minInputTemplate(): TemplateResult {\n return html`\n <input\n id=\"date-min\"\n placeholder=\"${DATE_FORMAT}\"\n type=\"text\"\n @change=\"${this.handleMinDateInput}\"\n .value=\"${this.minInputValue}\"\n />\n `;\n }\n\n get maxInputTemplate(): TemplateResult {\n return html`\n <input\n id=\"date-max\"\n placeholder=\"${DATE_FORMAT}\"\n type=\"text\"\n @change=\"${this.handleMaxDateInput}\"\n .value=\"${this.maxInputValue}\"\n />\n `;\n }\n\n get tooltipTemplate(): TemplateResult {\n return html`\n <style>\n #tooltip {\n width: ${this.tooltipWidth}px;\n height: ${this.tooltipHeight}px;\n top: ${-9 - this.tooltipHeight}px;\n left: ${this.tooltipOffset}px;\n display: ${this.tooltipDisplay};\n }\n #tooltip:after {\n left: ${this.tooltipWidth / 2}px;\n }\n </style>\n <div id=\"tooltip\">${this.tooltipContent}</div>\n `;\n }\n\n static styles = css`\n #container {\n margin: 0;\n touch-action: none;\n position: relative;\n }\n /* prevent selection from interfering with tooltip, especially on mobile */\n /* https://stackoverflow.com/a/4407335/1163042 */\n .noselect {\n -webkit-touch-callout: none; /* iOS Safari */\n -webkit-user-select: none; /* Safari */\n -moz-user-select: none; /* Old versions of Firefox */\n -ms-user-select: none; /* Internet Explorer/Edge */\n user-select: none; /* current Chrome, Edge, Opera and Firefox */\n }\n .bar:hover {\n fill-opacity: 0.7;\n }\n /****** histogram ********/\n #tooltip {\n position: absolute;\n background: ${tooltipBackgroundColor};\n color: ${tooltipTextColor};\n text-align: center;\n border-radius: 3px;\n padding: 2px;\n font-size: ${tooltipFontSize};\n font-family: sans-serif;\n touch-action: none;\n pointer-events: none;\n }\n #tooltip:after {\n content: '';\n position: absolute;\n margin-left: -5px;\n top: 100%;\n /* arrow */\n border: 5px solid ${tooltipTextColor};\n border-color: ${tooltipBackgroundColor} transparent transparent\n transparent;\n }\n /****** slider ********/\n .draggable:hover {\n cursor: grab;\n }\n .dragging {\n cursor: grabbing !important;\n }\n /****** inputs ********/\n #inputs {\n display: flex;\n justify-content: center;\n }\n #inputs .dash {\n position: relative;\n bottom: -1px;\n }\n input {\n width: ${inputWidth};\n margin: 0 3px;\n border: ${inputBorder};\n border-radius: 2px !important;\n text-align: center;\n font-size: ${inputFontSize};\n }\n `;\n\n render(): TemplateResult {\n if (!this.data || !this._histData) {\n return html`no data`;\n }\n return html`\n <div id=\"container\" class=\"noselect\" style=\"width: ${this.width}px\">\n ${this.tooltipTemplate}\n <svg\n width=\"${this.width}\"\n height=\"${this.height}\"\n @pointerleave=\"${this.drop}\"\n >\n ${this.selectedRangeTemplate}\n <svg id=\"histogram\">${this.histogramTemplate}</svg>\n ${this.minSliderTemplate} ${this.maxSliderTemplate}\n </svg>\n <div id=\"inputs\">\n ${this.minInputTemplate}\n <div class=\"dash\">-</div>\n ${this.maxInputTemplate}\n </div>\n </div>\n `;\n }\n}\n"]}