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