@internetarchive/histogram-date-range 0.1.0-beta → 0.1.3-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 +131 -65
- package/dist/src/histogram-date-range.js.map +1 -1
- package/dist/test/histogram-date-range.test.js +76 -17
- 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 +94 -47
- package/package.json +20 -19
- package/src/histogram-date-range.ts +164 -72
- package/test/histogram-date-range.test.ts +107 -23
- 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
package/demo/index.css
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
html {
|
|
2
|
+
font-size: 10px;
|
|
3
|
+
font-family: sans-serif;
|
|
4
|
+
}
|
|
5
|
+
body {
|
|
6
|
+
background: white;
|
|
7
|
+
}
|
|
8
|
+
.container {
|
|
9
|
+
margin-top: 20px;
|
|
10
|
+
display: grid;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
}
|
|
13
|
+
.description {
|
|
14
|
+
margin: 10px auto;
|
|
15
|
+
}
|
|
16
|
+
.received-events {
|
|
17
|
+
position: absolute;
|
|
18
|
+
top: 0;
|
|
19
|
+
}
|
|
20
|
+
button {
|
|
21
|
+
font-size: 100%;
|
|
22
|
+
margin: 10px auto;
|
|
23
|
+
}
|
package/demo/index.html
CHANGED
|
@@ -3,49 +3,29 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
5
5
|
<meta charset="utf-8" />
|
|
6
|
-
<
|
|
7
|
-
html {
|
|
8
|
-
font-size: 10px;
|
|
9
|
-
font-family: sans-serif;
|
|
10
|
-
}
|
|
11
|
-
body {
|
|
12
|
-
background: white;
|
|
13
|
-
}
|
|
14
|
-
.container {
|
|
15
|
-
margin-top: 50px;
|
|
16
|
-
display: grid;
|
|
17
|
-
justify-content: center;
|
|
18
|
-
}
|
|
19
|
-
.description {
|
|
20
|
-
margin: 10px auto;
|
|
21
|
-
}
|
|
22
|
-
.received-events {
|
|
23
|
-
position: absolute;
|
|
24
|
-
top: 0;
|
|
25
|
-
}
|
|
26
|
-
button {
|
|
27
|
-
font-size: 100%;
|
|
28
|
-
margin: 10px auto;
|
|
29
|
-
}
|
|
30
|
-
</style>
|
|
6
|
+
<link rel="stylesheet" href="index.css">
|
|
31
7
|
</head>
|
|
32
8
|
|
|
33
9
|
<script type="module">
|
|
34
10
|
import '../dist/src/histogram-date-range.js';
|
|
11
|
+
let eventCount = 0;
|
|
35
12
|
// listen to events from the component and display the data received from them
|
|
36
13
|
document.addEventListener('histogramDateRangeUpdated', e => {
|
|
37
|
-
document.querySelector('.received-events').innerHTML
|
|
38
|
-
|
|
14
|
+
document.querySelector('.received-events').innerHTML =
|
|
15
|
+
++eventCount + ': ' + JSON.stringify(e.detail);
|
|
39
16
|
});
|
|
40
17
|
</script>
|
|
41
18
|
<body>
|
|
42
19
|
<pre class="received-events"></pre>
|
|
43
20
|
|
|
44
21
|
<div class="container">
|
|
45
|
-
<div class="description">
|
|
22
|
+
<div class="description">
|
|
23
|
+
pre-selected range with 1000ms debounce delay
|
|
24
|
+
</div>
|
|
46
25
|
<histogram-date-range
|
|
47
26
|
minDate="1400"
|
|
48
27
|
maxDate="2021"
|
|
28
|
+
updateDelay="1000"
|
|
49
29
|
minSelectedDate="1800"
|
|
50
30
|
maxSelectedDate="1900"
|
|
51
31
|
bins="[ 74, 67, 17, 66, 49, 93, 47, 61, 32, 46, 53, 2,
|
|
@@ -57,19 +37,45 @@
|
|
|
57
37
|
</div>
|
|
58
38
|
|
|
59
39
|
<div class="container">
|
|
60
|
-
<div class="description">
|
|
40
|
+
<div class="description">range spanning negative to positive years</div>
|
|
41
|
+
<histogram-date-range
|
|
42
|
+
mindate="-1050" maxdate="2200"
|
|
43
|
+
bins="[ 74, 67, 17, 66, 49, 93, 47, 61, 32, 46, 53, 2,
|
|
44
|
+
13, 45, 28, 1, 8, 70, 37, 74, 67, 17, 66, 49, 93,
|
|
45
|
+
47, 61, 70, 37, 74, 67, 17, 66, 49, 93, 47, 61, 32,
|
|
46
|
+
32, 70, 37, 74, 67, 17, 66, 49, 93, 47, 61, 32
|
|
47
|
+
]"
|
|
48
|
+
></histogram-date-range>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
<div class="container">
|
|
54
|
+
<div class="description">small year range and few bins</div>
|
|
55
|
+
<histogram-date-range width="175" tooltipwidth="120"
|
|
56
|
+
mindate="2008" maxdate="2016" bins="[76104,866978,1151617,986331,218672,107410,3324]">
|
|
57
|
+
</histogram-date-range>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="container">
|
|
61
|
+
<div class="description">
|
|
62
|
+
default range with custom styling and date format
|
|
63
|
+
</div>
|
|
61
64
|
<histogram-date-range
|
|
62
65
|
width="300"
|
|
63
66
|
height="50"
|
|
64
67
|
tooltipWidth="140"
|
|
65
|
-
updateDelay="2000"
|
|
66
68
|
dateFormat="DD MMM YYYY"
|
|
67
69
|
style="
|
|
70
|
+
--histogramDateRangeSliderColor: #d8b384;
|
|
71
|
+
--histogramDateRangeSelectedRangeColor: #f3f0d7;
|
|
72
|
+
--histogramDateRangeTooltipFontFamily: serif;
|
|
73
|
+
--histogramDateRangeInputFontFamily: serif;
|
|
68
74
|
--histogramDateRangeTooltipFontSize: 1rem;
|
|
69
75
|
--histogramDateRangeInputWidth: 85px;
|
|
70
76
|
"
|
|
71
|
-
minDate="May
|
|
72
|
-
maxDate="
|
|
77
|
+
minDate="05 May 1972"
|
|
78
|
+
maxDate="21 Dec 1980"
|
|
73
79
|
bins="[ 85, 25, 200, 0, 0, 34, 0, 2, 5, 10, 0, 56, 10, 45, 100, 70, 50 ]"
|
|
74
80
|
></histogram-date-range>
|
|
75
81
|
</div>
|
|
@@ -99,11 +105,8 @@
|
|
|
99
105
|
</script>
|
|
100
106
|
|
|
101
107
|
<div class="container">
|
|
102
|
-
<div class="description">data set up with js
|
|
103
|
-
<histogram-date-range
|
|
104
|
-
id="js-setup"
|
|
105
|
-
updateDelay="0"
|
|
106
|
-
></histogram-date-range>
|
|
108
|
+
<div class="description">data set up with js</div>
|
|
109
|
+
<histogram-date-range id="js-setup"></histogram-date-range>
|
|
107
110
|
</div>
|
|
108
111
|
<script>
|
|
109
112
|
document.addEventListener('DOMContentLoaded', function () {
|
|
@@ -112,17 +115,23 @@
|
|
|
112
115
|
);
|
|
113
116
|
histogram.minDate = '1950';
|
|
114
117
|
histogram.maxDate = '2000';
|
|
115
|
-
// generate
|
|
118
|
+
// generate array of [0, 1, 2, ... 49]
|
|
116
119
|
histogram.bins = [...Array(50).keys()];
|
|
117
120
|
});
|
|
118
121
|
</script>
|
|
119
122
|
|
|
123
|
+
<div class="container">
|
|
124
|
+
<div class="description">
|
|
125
|
+
single bin
|
|
126
|
+
</div>
|
|
127
|
+
<histogram-date-range mindate="1926" maxdate="1926" bins="[8]">
|
|
128
|
+
</histogram-date-range>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
120
131
|
<div class="container">
|
|
121
132
|
<div class="description">empty data</div>
|
|
122
|
-
<histogram-date-range
|
|
123
|
-
bins=""
|
|
124
|
-
missingDataMessage="no data..."
|
|
125
|
-
></histogram-date-range>
|
|
133
|
+
<histogram-date-range missingDataMessage="no data..."></histoghistogram-date-range>
|
|
126
134
|
</div>
|
|
135
|
+
|
|
127
136
|
</body>
|
|
128
137
|
</html>
|
|
@@ -42,14 +42,18 @@ export declare class HistogramDateRange extends LitElement {
|
|
|
42
42
|
private calculateHistData;
|
|
43
43
|
private get hasBinData();
|
|
44
44
|
private get _numBins();
|
|
45
|
+
private get histogramLeftEdgeX();
|
|
46
|
+
private get histogramRightEdgeX();
|
|
45
47
|
/** component's loading (and disabled) state */
|
|
46
48
|
get loading(): boolean;
|
|
47
49
|
set loading(value: boolean);
|
|
48
50
|
/** formatted minimum date of selected date range */
|
|
49
51
|
get minSelectedDate(): string;
|
|
52
|
+
/** updates minSelectedDate if new date is valid */
|
|
50
53
|
set minSelectedDate(rawDate: string);
|
|
51
54
|
/** formatted maximum date of selected date range */
|
|
52
55
|
get maxSelectedDate(): string;
|
|
56
|
+
/** updates maxSelectedDate if new date is valid */
|
|
53
57
|
set maxSelectedDate(rawDate: string);
|
|
54
58
|
/** horizontal position of min date slider */
|
|
55
59
|
get minSliderX(): number;
|
|
@@ -70,18 +74,18 @@ export declare class HistogramDateRange extends LitElement {
|
|
|
70
74
|
* Constrain a proposed value for the minimum (left) slider
|
|
71
75
|
*
|
|
72
76
|
* If the value is less than the leftmost valid position, then set it to the
|
|
73
|
-
* left edge of the
|
|
74
|
-
* the rightmost valid position (the position of the max slider), then
|
|
75
|
-
* to the position of the max slider
|
|
77
|
+
* left edge of the histogram (ie the slider width). If the value is greater
|
|
78
|
+
* than the rightmost valid position (the position of the max slider), then
|
|
79
|
+
* set it to the position of the max slider
|
|
76
80
|
*/
|
|
77
81
|
private validMinSliderX;
|
|
78
82
|
/**
|
|
79
83
|
* Constrain a proposed value for the maximum (right) slider
|
|
80
84
|
*
|
|
81
85
|
* If the value is greater than the rightmost valid position, then set it to
|
|
82
|
-
* the right edge of the
|
|
83
|
-
* is less than the leftmost valid position (the position of the min
|
|
84
|
-
* then set it to the position of the min slider
|
|
86
|
+
* the right edge of the histogram (ie histogram width - slider width). If the
|
|
87
|
+
* value is less than the leftmost valid position (the position of the min
|
|
88
|
+
* slider), then set it to the position of the min slider
|
|
85
89
|
*/
|
|
86
90
|
private validMaxSliderX;
|
|
87
91
|
private addListeners;
|
|
@@ -103,20 +107,25 @@ export declare class HistogramDateRange extends LitElement {
|
|
|
103
107
|
*/
|
|
104
108
|
private translatePositionToDate;
|
|
105
109
|
/**
|
|
106
|
-
* Returns slider x-position corresponding to given date
|
|
107
|
-
* date)
|
|
110
|
+
* Returns slider x-position corresponding to given date
|
|
108
111
|
*
|
|
109
112
|
* @param date
|
|
110
113
|
* @returns x-position of slider
|
|
111
114
|
*/
|
|
112
115
|
private translateDateToPosition;
|
|
116
|
+
/** ensure that the returned value is between minValue and maxValue */
|
|
117
|
+
private clamp;
|
|
113
118
|
private handleMinDateInput;
|
|
114
119
|
private handleMaxDateInput;
|
|
120
|
+
private handleKeyUp;
|
|
115
121
|
private get currentDateRangeString();
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
private getMSFromString;
|
|
123
|
+
/**
|
|
124
|
+
* expand or narrow the selected range by moving the slider nearest the
|
|
125
|
+
* clicked bar to the outer edge of the clicked bar
|
|
126
|
+
*
|
|
127
|
+
* @param e Event click event from a histogram bar
|
|
128
|
+
*/
|
|
120
129
|
private handleBarClick;
|
|
121
130
|
private get minSliderTemplate();
|
|
122
131
|
private get maxSliderTemplate();
|
|
@@ -135,7 +144,7 @@ export declare class HistogramDateRange extends LitElement {
|
|
|
135
144
|
get tooltipTemplate(): TemplateResult;
|
|
136
145
|
private get noDataTemplate();
|
|
137
146
|
private get activityIndicatorTemplate();
|
|
138
|
-
static styles: import("lit").
|
|
147
|
+
static styles: import("lit").CSSResult;
|
|
139
148
|
render(): TemplateResult;
|
|
140
149
|
}
|
|
141
150
|
declare global {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
-
import { css, html, LitElement, svg, } from 'lit';
|
|
2
|
+
import { css, html, nothing, LitElement, svg, } from 'lit';
|
|
3
3
|
import { property, state, customElement } from 'lit/decorators.js';
|
|
4
4
|
import { live } from 'lit/directives/live.js';
|
|
5
5
|
import dayjs from 'dayjs/esm/index.js';
|
|
6
|
+
import customParseFormat from 'dayjs/esm/plugin/customParseFormat';
|
|
7
|
+
dayjs.extend(customParseFormat);
|
|
6
8
|
import '@internetarchive/ia-activity-indicator/ia-activity-indicator';
|
|
7
9
|
// these values can be overridden via the component's HTML (camelCased) attributes
|
|
8
10
|
const WIDTH = 180;
|
|
@@ -12,21 +14,23 @@ const TOOLTIP_WIDTH = 125;
|
|
|
12
14
|
const TOOLTIP_HEIGHT = 30;
|
|
13
15
|
const DATE_FORMAT = 'YYYY';
|
|
14
16
|
const MISSING_DATA = 'no data';
|
|
15
|
-
const UPDATE_DEBOUNCE_DELAY_MS =
|
|
17
|
+
const UPDATE_DEBOUNCE_DELAY_MS = 0;
|
|
16
18
|
// this constant is not set up to be overridden
|
|
17
19
|
const SLIDER_CORNER_SIZE = 4;
|
|
18
20
|
// these CSS custom props can be overridden from the HTML that is invoking this component
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
+
const sliderColor = css `var(--histogramDateRangeSliderColor, #4B65FE)`;
|
|
22
|
+
const selectedRangeColor = css `var(--histogramDateRangeSelectedRangeColor, #DBE0FF)`;
|
|
21
23
|
const barIncludedFill = css `var(--histogramDateRangeBarIncludedFill, #2C2C2C)`;
|
|
22
24
|
const activityIndicatorColor = css `var(--histogramDateRangeActivityIndicator, #2C2C2C)`;
|
|
23
25
|
const barExcludedFill = css `var(--histogramDateRangeBarExcludedFill, #CCCCCC)`;
|
|
24
26
|
const inputBorder = css `var(--histogramDateRangeInputBorder, 0.5px solid #2C2C2C)`;
|
|
25
27
|
const inputWidth = css `var(--histogramDateRangeInputWidth, 35px)`;
|
|
26
28
|
const inputFontSize = css `var(--histogramDateRangeInputFontSize, 1.2rem)`;
|
|
29
|
+
const inputFontFamily = css `var(--histogramDateRangeInputFontFamily, sans-serif)`;
|
|
27
30
|
const tooltipBackgroundColor = css `var(--histogramDateRangeTooltipBackgroundColor, #2C2C2C)`;
|
|
28
31
|
const tooltipTextColor = css `var(--histogramDateRangeTooltipTextColor, #FFFFFF)`;
|
|
29
32
|
const tooltipFontSize = css `var(--histogramDateRangeTooltipFontSize, 1.1rem)`;
|
|
33
|
+
const tooltipFontFamily = css `var(--histogramDateRangeTooltipFontFamily, sans-serif)`;
|
|
30
34
|
let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
31
35
|
constructor() {
|
|
32
36
|
/* eslint-disable lines-between-class-members */
|
|
@@ -89,12 +93,11 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
89
93
|
this.move = (e) => {
|
|
90
94
|
const newX = e.offsetX - this._dragOffset;
|
|
91
95
|
const slider = this._currentSlider;
|
|
92
|
-
const date = this.translatePositionToDate(newX);
|
|
93
96
|
if (slider.id === 'slider-min') {
|
|
94
|
-
this.minSelectedDate =
|
|
97
|
+
this.minSelectedDate = this.translatePositionToDate(this.validMinSliderX(newX));
|
|
95
98
|
}
|
|
96
99
|
else {
|
|
97
|
-
this.maxSelectedDate =
|
|
100
|
+
this.maxSelectedDate = this.translatePositionToDate(this.validMaxSliderX(newX));
|
|
98
101
|
}
|
|
99
102
|
};
|
|
100
103
|
}
|
|
@@ -125,8 +128,8 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
125
128
|
return;
|
|
126
129
|
}
|
|
127
130
|
this._histWidth = this.width - this.sliderWidth * 2;
|
|
128
|
-
this._minDateMS =
|
|
129
|
-
this._maxDateMS =
|
|
131
|
+
this._minDateMS = this.getMSFromString(this.minDate);
|
|
132
|
+
this._maxDateMS = this.getMSFromString(this.maxDate);
|
|
130
133
|
this._binWidth = this._histWidth / this._numBins;
|
|
131
134
|
this._previousDateRange = this.currentDateRangeString;
|
|
132
135
|
this._histData = this.calculateHistData();
|
|
@@ -141,11 +144,16 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
141
144
|
calculateHistData() {
|
|
142
145
|
const minValue = Math.min(...this.bins);
|
|
143
146
|
const maxValue = Math.max(...this.bins);
|
|
144
|
-
|
|
147
|
+
// if there is no difference between the min and max values, use a range of
|
|
148
|
+
// 1 because log scaling will fail if the range is 0
|
|
149
|
+
const valueRange = minValue === maxValue ? 1 : Math.log1p(maxValue - minValue);
|
|
150
|
+
const valueScale = this.height / valueRange;
|
|
145
151
|
const dateScale = this.dateRangeMS / this._numBins;
|
|
146
152
|
return this.bins.map((v, i) => {
|
|
147
153
|
return {
|
|
148
154
|
value: v,
|
|
155
|
+
// use log scaling for the height of the bar to prevent tall bars from
|
|
156
|
+
// making the smaller ones too small to see
|
|
149
157
|
height: Math.floor(Math.log1p(v) * valueScale),
|
|
150
158
|
binStart: `${this.formatDate(i * dateScale + this._minDateMS)}`,
|
|
151
159
|
binEnd: `${this.formatDate((i + 1) * dateScale + this._minDateMS)}`,
|
|
@@ -161,6 +169,12 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
161
169
|
}
|
|
162
170
|
return this.bins.length;
|
|
163
171
|
}
|
|
172
|
+
get histogramLeftEdgeX() {
|
|
173
|
+
return this.sliderWidth;
|
|
174
|
+
}
|
|
175
|
+
get histogramRightEdgeX() {
|
|
176
|
+
return this.width - this.sliderWidth;
|
|
177
|
+
}
|
|
164
178
|
/** component's loading (and disabled) state */
|
|
165
179
|
get loading() {
|
|
166
180
|
return this._isLoading;
|
|
@@ -171,51 +185,55 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
171
185
|
}
|
|
172
186
|
/** formatted minimum date of selected date range */
|
|
173
187
|
get minSelectedDate() {
|
|
174
|
-
return this.formatDate(this._minSelectedDate);
|
|
188
|
+
return this.formatDate(this.getMSFromString(this._minSelectedDate));
|
|
175
189
|
}
|
|
190
|
+
/** updates minSelectedDate if new date is valid */
|
|
176
191
|
set minSelectedDate(rawDate) {
|
|
177
192
|
if (!this._minSelectedDate) {
|
|
178
193
|
// because the values needed to calculate valid max/min values are not
|
|
179
194
|
// available during the lit init when it's populating properties from
|
|
180
195
|
// attributes, fall back to just the raw date if nothing is already set
|
|
181
196
|
this._minSelectedDate = rawDate;
|
|
197
|
+
return;
|
|
182
198
|
}
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
199
|
+
const proposedDateMS = this.getMSFromString(rawDate);
|
|
200
|
+
const isValidDate = !Number.isNaN(proposedDateMS);
|
|
201
|
+
const isNotTooRecent = proposedDateMS <= this.getMSFromString(this.maxSelectedDate);
|
|
202
|
+
if (isValidDate && isNotTooRecent) {
|
|
203
|
+
this._minSelectedDate = this.formatDate(proposedDateMS);
|
|
187
204
|
}
|
|
188
205
|
this.requestUpdate();
|
|
189
206
|
}
|
|
190
207
|
/** formatted maximum date of selected date range */
|
|
191
208
|
get maxSelectedDate() {
|
|
192
|
-
return this.formatDate(this._maxSelectedDate);
|
|
209
|
+
return this.formatDate(this.getMSFromString(this._maxSelectedDate));
|
|
193
210
|
}
|
|
211
|
+
/** updates maxSelectedDate if new date is valid */
|
|
194
212
|
set maxSelectedDate(rawDate) {
|
|
195
213
|
if (!this._maxSelectedDate) {
|
|
196
|
-
//
|
|
214
|
+
// because the values needed to calculate valid max/min values are not
|
|
215
|
+
// available during the lit init when it's populating properties from
|
|
216
|
+
// attributes, fall back to just the raw date if nothing is already set
|
|
197
217
|
this._maxSelectedDate = rawDate;
|
|
218
|
+
return;
|
|
198
219
|
}
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
220
|
+
const proposedDateMS = this.getMSFromString(rawDate);
|
|
221
|
+
const isValidDate = !Number.isNaN(proposedDateMS);
|
|
222
|
+
const isNotTooOld = proposedDateMS >= this.getMSFromString(this.minSelectedDate);
|
|
223
|
+
if (isValidDate && isNotTooOld) {
|
|
224
|
+
this._maxSelectedDate = this.formatDate(proposedDateMS);
|
|
203
225
|
}
|
|
204
226
|
this.requestUpdate();
|
|
205
227
|
}
|
|
206
228
|
/** horizontal position of min date slider */
|
|
207
229
|
get minSliderX() {
|
|
208
|
-
|
|
209
|
-
return (
|
|
210
|
-
// default to leftmost position if missing or invalid min position
|
|
211
|
-
(_a = this.translateDateToPosition(this.minSelectedDate)) !== null && _a !== void 0 ? _a : this.sliderWidth);
|
|
230
|
+
const x = this.translateDateToPosition(this.minSelectedDate);
|
|
231
|
+
return this.validMinSliderX(x);
|
|
212
232
|
}
|
|
213
233
|
/** horizontal position of max date slider */
|
|
214
234
|
get maxSliderX() {
|
|
215
|
-
|
|
216
|
-
return (
|
|
217
|
-
// default to rightmost position if missing or invalid max position
|
|
218
|
-
(_a = this.translateDateToPosition(this.maxSelectedDate)) !== null && _a !== void 0 ? _a : this.width - this.sliderWidth);
|
|
235
|
+
const x = this.translateDateToPosition(this.maxSelectedDate);
|
|
236
|
+
return this.validMaxSliderX(x);
|
|
219
237
|
}
|
|
220
238
|
get dateRangeMS() {
|
|
221
239
|
return this._maxDateMS - this._minDateMS;
|
|
@@ -228,10 +246,11 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
228
246
|
const x = target.x.baseVal.value + this.sliderWidth / 2;
|
|
229
247
|
const dataset = target.dataset;
|
|
230
248
|
const itemsText = `item${dataset.numItems !== '1' ? 's' : ''}`;
|
|
249
|
+
const formattedNumItems = Number(dataset.numItems).toLocaleString();
|
|
231
250
|
this._tooltipOffset =
|
|
232
251
|
x + (this._binWidth - this.sliderWidth - this.tooltipWidth) / 2;
|
|
233
252
|
this._tooltipContent = html `
|
|
234
|
-
${
|
|
253
|
+
${formattedNumItems} ${itemsText}<br />
|
|
235
254
|
${dataset.binStart} - ${dataset.binEnd}
|
|
236
255
|
`;
|
|
237
256
|
this._tooltipVisible = true;
|
|
@@ -244,25 +263,33 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
244
263
|
* Constrain a proposed value for the minimum (left) slider
|
|
245
264
|
*
|
|
246
265
|
* If the value is less than the leftmost valid position, then set it to the
|
|
247
|
-
* left edge of the
|
|
248
|
-
* the rightmost valid position (the position of the max slider), then
|
|
249
|
-
* to the position of the max slider
|
|
266
|
+
* left edge of the histogram (ie the slider width). If the value is greater
|
|
267
|
+
* than the rightmost valid position (the position of the max slider), then
|
|
268
|
+
* set it to the position of the max slider
|
|
250
269
|
*/
|
|
251
270
|
validMinSliderX(newX) {
|
|
252
|
-
|
|
253
|
-
|
|
271
|
+
// allow the left slider to go right only to the right slider, even if the
|
|
272
|
+
// max selected date is out of range
|
|
273
|
+
const rightLimit = Math.min(this.translateDateToPosition(this.maxSelectedDate), this.histogramRightEdgeX);
|
|
274
|
+
newX = this.clamp(newX, this.histogramLeftEdgeX, rightLimit);
|
|
275
|
+
const isInvalid = Number.isNaN(newX) || rightLimit < this.histogramLeftEdgeX;
|
|
276
|
+
return isInvalid ? this.histogramLeftEdgeX : newX;
|
|
254
277
|
}
|
|
255
278
|
/**
|
|
256
279
|
* Constrain a proposed value for the maximum (right) slider
|
|
257
280
|
*
|
|
258
281
|
* If the value is greater than the rightmost valid position, then set it to
|
|
259
|
-
* the right edge of the
|
|
260
|
-
* is less than the leftmost valid position (the position of the min
|
|
261
|
-
* then set it to the position of the min slider
|
|
282
|
+
* the right edge of the histogram (ie histogram width - slider width). If the
|
|
283
|
+
* value is less than the leftmost valid position (the position of the min
|
|
284
|
+
* slider), then set it to the position of the min slider
|
|
262
285
|
*/
|
|
263
286
|
validMaxSliderX(newX) {
|
|
264
|
-
|
|
265
|
-
|
|
287
|
+
// allow the right slider to go left only to the left slider, even if the
|
|
288
|
+
// min selected date is out of range
|
|
289
|
+
const leftLimit = Math.max(this.histogramLeftEdgeX, this.translateDateToPosition(this.minSelectedDate));
|
|
290
|
+
newX = this.clamp(newX, leftLimit, this.histogramRightEdgeX);
|
|
291
|
+
const isInvalid = Number.isNaN(newX) || leftLimit > this.histogramRightEdgeX;
|
|
292
|
+
return isInvalid ? this.histogramRightEdgeX : newX;
|
|
266
293
|
}
|
|
267
294
|
addListeners() {
|
|
268
295
|
window.addEventListener('pointermove', this.move);
|
|
@@ -332,17 +359,19 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
332
359
|
return this.formatDate(this._minDateMS + milliseconds);
|
|
333
360
|
}
|
|
334
361
|
/**
|
|
335
|
-
* Returns slider x-position corresponding to given date
|
|
336
|
-
* date)
|
|
362
|
+
* Returns slider x-position corresponding to given date
|
|
337
363
|
*
|
|
338
364
|
* @param date
|
|
339
365
|
* @returns x-position of slider
|
|
340
366
|
*/
|
|
341
367
|
translateDateToPosition(date) {
|
|
342
|
-
const milliseconds =
|
|
343
|
-
|
|
344
|
-
((milliseconds - this._minDateMS) * this._histWidth) / this.dateRangeMS;
|
|
345
|
-
|
|
368
|
+
const milliseconds = this.getMSFromString(date);
|
|
369
|
+
return (this.sliderWidth +
|
|
370
|
+
((milliseconds - this._minDateMS) * this._histWidth) / this.dateRangeMS);
|
|
371
|
+
}
|
|
372
|
+
/** ensure that the returned value is between minValue and maxValue */
|
|
373
|
+
clamp(x, minValue, maxValue) {
|
|
374
|
+
return Math.min(Math.max(x, minValue), maxValue);
|
|
346
375
|
}
|
|
347
376
|
handleMinDateInput(e) {
|
|
348
377
|
const target = e.currentTarget;
|
|
@@ -354,25 +383,51 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
354
383
|
this.maxSelectedDate = target.value;
|
|
355
384
|
this.beginEmitUpdateProcess();
|
|
356
385
|
}
|
|
386
|
+
handleKeyUp(e) {
|
|
387
|
+
if (e.key === 'Enter') {
|
|
388
|
+
const target = e.currentTarget;
|
|
389
|
+
target.blur();
|
|
390
|
+
if (target.id === 'date-min') {
|
|
391
|
+
this.handleMinDateInput(e);
|
|
392
|
+
}
|
|
393
|
+
else if (target.id === 'date-max') {
|
|
394
|
+
this.handleMaxDateInput(e);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
357
398
|
get currentDateRangeString() {
|
|
358
399
|
return `${this.minSelectedDate}:${this.maxSelectedDate}`;
|
|
359
400
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
401
|
+
getMSFromString(date) {
|
|
402
|
+
const digitGroupCount = (date.split(/(\d+)/).length - 1) / 2;
|
|
403
|
+
if (digitGroupCount === 1) {
|
|
404
|
+
// if there's just a single set of digits, assume it's a year
|
|
405
|
+
const dateObj = new Date(0, 0); // start at January 1, 1900
|
|
406
|
+
dateObj.setFullYear(Number(date)); // override year
|
|
407
|
+
return dateObj.getTime(); // get time in milliseconds
|
|
408
|
+
}
|
|
409
|
+
return dayjs(date, [this.dateFormat, DATE_FORMAT]).valueOf();
|
|
367
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* expand or narrow the selected range by moving the slider nearest the
|
|
413
|
+
* clicked bar to the outer edge of the clicked bar
|
|
414
|
+
*
|
|
415
|
+
* @param e Event click event from a histogram bar
|
|
416
|
+
*/
|
|
368
417
|
handleBarClick(e) {
|
|
369
418
|
const dataset = e.currentTarget.dataset;
|
|
370
|
-
|
|
371
|
-
|
|
419
|
+
// use the midpoint of the width of the clicked bar to determine which is
|
|
420
|
+
// the nearest slider
|
|
421
|
+
const clickPosition = (this.getMSFromString(dataset.binStart) +
|
|
422
|
+
this.getMSFromString(dataset.binEnd)) /
|
|
423
|
+
2;
|
|
424
|
+
const distanceFromMinSlider = Math.abs(clickPosition - this.getMSFromString(this.minSelectedDate));
|
|
425
|
+
const distanceFromMaxSlider = Math.abs(clickPosition - this.getMSFromString(this.maxSelectedDate));
|
|
426
|
+
// update the selected range by moving the nearer slider
|
|
427
|
+
if (distanceFromMinSlider < distanceFromMaxSlider) {
|
|
372
428
|
this.minSelectedDate = dataset.binStart;
|
|
373
429
|
}
|
|
374
|
-
|
|
375
|
-
if (binEndDateMS > this.maxSelectedDateMS) {
|
|
430
|
+
else {
|
|
376
431
|
this.maxSelectedDate = dataset.binEnd;
|
|
377
432
|
}
|
|
378
433
|
this.beginEmitUpdateProcess();
|
|
@@ -416,7 +471,7 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
416
471
|
${this._isDragging ? 'dragging' : ''}"
|
|
417
472
|
@pointerdown="${this.drag}"
|
|
418
473
|
>
|
|
419
|
-
<path d="${sliderShape} z" fill="${
|
|
474
|
+
<path d="${sliderShape} z" fill="${sliderColor}" />
|
|
420
475
|
<rect
|
|
421
476
|
x="${sliderPositionX - this.sliderWidth * k + this.sliderWidth * 0.4 * k}"
|
|
422
477
|
y="${this.height / 3}"
|
|
@@ -441,7 +496,7 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
441
496
|
y="0"
|
|
442
497
|
width="${this.maxSliderX - this.minSliderX}"
|
|
443
498
|
height="${this.height}"
|
|
444
|
-
fill="${
|
|
499
|
+
fill="${selectedRangeColor}"
|
|
445
500
|
/>`;
|
|
446
501
|
}
|
|
447
502
|
get histogramTemplate() {
|
|
@@ -464,7 +519,7 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
464
519
|
@pointerenter="${this.showTooltip}"
|
|
465
520
|
@pointerleave="${this.hideTooltip}"
|
|
466
521
|
@click="${this.handleBarClick}"
|
|
467
|
-
fill="${x >= this.minSliderX && x <= this.maxSliderX
|
|
522
|
+
fill="${x + barWidth >= this.minSliderX && x <= this.maxSliderX
|
|
468
523
|
? barIncludedFill
|
|
469
524
|
: barExcludedFill}"
|
|
470
525
|
data-num-items="${data.value}"
|
|
@@ -475,9 +530,17 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
475
530
|
return bar;
|
|
476
531
|
});
|
|
477
532
|
}
|
|
478
|
-
formatDate(
|
|
479
|
-
|
|
480
|
-
|
|
533
|
+
formatDate(dateMS) {
|
|
534
|
+
if (Number.isNaN(dateMS)) {
|
|
535
|
+
return '';
|
|
536
|
+
}
|
|
537
|
+
const date = dayjs(dateMS);
|
|
538
|
+
if (date.year() < 1000) {
|
|
539
|
+
// years before 1000 don't play well with dayjs custom formatting, so fall
|
|
540
|
+
// back to displaying only the year
|
|
541
|
+
return String(date.year());
|
|
542
|
+
}
|
|
543
|
+
return date.format(this.dateFormat);
|
|
481
544
|
}
|
|
482
545
|
/**
|
|
483
546
|
* NOTE: we are relying on the lit `live` directive in the template to
|
|
@@ -493,6 +556,7 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
493
556
|
type="text"
|
|
494
557
|
@focus="${this.cancelPendingUpdateEvent}"
|
|
495
558
|
@blur="${this.handleMinDateInput}"
|
|
559
|
+
@keyup="${this.handleKeyUp}"
|
|
496
560
|
.value="${live(this.minSelectedDate)}"
|
|
497
561
|
?disabled="${this.disabled}"
|
|
498
562
|
/>
|
|
@@ -506,6 +570,7 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
506
570
|
type="text"
|
|
507
571
|
@focus="${this.cancelPendingUpdateEvent}"
|
|
508
572
|
@blur="${this.handleMaxDateInput}"
|
|
573
|
+
@keyup="${this.handleKeyUp}"
|
|
509
574
|
.value="${live(this.maxSelectedDate)}"
|
|
510
575
|
?disabled="${this.disabled}"
|
|
511
576
|
/>
|
|
@@ -535,7 +600,7 @@ let HistogramDateRange = class HistogramDateRange extends LitElement {
|
|
|
535
600
|
}
|
|
536
601
|
get activityIndicatorTemplate() {
|
|
537
602
|
if (!this.loading) {
|
|
538
|
-
return
|
|
603
|
+
return nothing;
|
|
539
604
|
}
|
|
540
605
|
return html `
|
|
541
606
|
<ia-activity-indicator mode="processing"> </ia-activity-indicator>
|
|
@@ -635,7 +700,7 @@ HistogramDateRange.styles = css `
|
|
|
635
700
|
border-radius: 3px;
|
|
636
701
|
padding: 2px;
|
|
637
702
|
font-size: ${tooltipFontSize};
|
|
638
|
-
font-family:
|
|
703
|
+
font-family: ${tooltipFontFamily};
|
|
639
704
|
touch-action: none;
|
|
640
705
|
pointer-events: none;
|
|
641
706
|
}
|
|
@@ -672,6 +737,7 @@ HistogramDateRange.styles = css `
|
|
|
672
737
|
border-radius: 2px !important;
|
|
673
738
|
text-align: center;
|
|
674
739
|
font-size: ${inputFontSize};
|
|
740
|
+
font-family: ${inputFontFamily};
|
|
675
741
|
}
|
|
676
742
|
`;
|
|
677
743
|
__decorate([
|