@internetarchive/histogram-date-range 1.3.1 → 1.3.2
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 +6 -0
- package/demo/index.html +15 -0
- package/dist/demo/js/app-root.d.ts +19 -19
- package/dist/demo/js/app-root.js +46 -46
- package/dist/demo/js/app-root.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/src/histogram-date-range copy.d.ts +214 -0
- package/dist/src/histogram-date-range copy.js +1018 -0
- package/dist/src/histogram-date-range copy.js.map +1 -0
- package/dist/src/histogram-date-range.d.ts +214 -213
- package/dist/src/histogram-date-range.js +790 -774
- package/dist/src/histogram-date-range.js.map +1 -1
- package/dist/src/plugins/fix-first-century-years.d.ts +23 -23
- package/dist/src/plugins/fix-first-century-years.js +42 -42
- package/dist/src/plugins/fix-first-century-years.js.map +1 -1
- package/dist/test/histogram-date-range.test.d.ts +1 -1
- package/dist/test/histogram-date-range.test.js +543 -542
- package/dist/test/histogram-date-range.test.js.map +1 -1
- package/docs/_snowpack/pkg/import-map.json +2 -1
- package/docs/_snowpack/pkg/lit/decorators.js +14 -1
- package/docs/_snowpack/pkg/lit/directives/style-map.js +10 -0
- package/docs/demo/index.css +6 -0
- package/docs/demo/index.html +15 -0
- package/docs/dist/src/histogram-date-range.js +31 -22
- package/package.json +11 -11
- package/src/histogram-date-range.ts +36 -20
- package/src/plugins/fix-first-century-years.ts +52 -52
- package/test/histogram-date-range.test.ts +1 -0
- package/types/dayjs.d.ts +10 -10
|
@@ -12,9 +12,10 @@ import {
|
|
|
12
12
|
SVGTemplateResult,
|
|
13
13
|
TemplateResult,
|
|
14
14
|
} from 'lit';
|
|
15
|
-
import { customElement, property, state } from 'lit/decorators.js';
|
|
15
|
+
import { customElement, property, state, query } from 'lit/decorators.js';
|
|
16
16
|
import { live } from 'lit/directives/live.js';
|
|
17
17
|
import { classMap } from 'lit/directives/class-map.js';
|
|
18
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
18
19
|
|
|
19
20
|
dayjs.extend(customParseFormat);
|
|
20
21
|
dayjs.extend(fixFirstCenturyYears);
|
|
@@ -100,13 +101,15 @@ export class HistogramDateRange extends LitElement {
|
|
|
100
101
|
@property({ type: String }) binSnapping: BinSnappingInterval = 'none';
|
|
101
102
|
|
|
102
103
|
// internal reactive properties not exposed as attributes
|
|
103
|
-
@state() private
|
|
104
|
+
@state() private _tooltipOffsetX = 0;
|
|
105
|
+
@state() private _tooltipOffsetY = 0;
|
|
104
106
|
@state() private _tooltipContent?: TemplateResult;
|
|
105
|
-
@state() private _tooltipVisible = false;
|
|
106
107
|
@state() private _tooltipDateFormat?: string;
|
|
107
108
|
@state() private _isDragging = false;
|
|
108
109
|
@state() private _isLoading = false;
|
|
109
110
|
|
|
111
|
+
@query('#tooltip') private _tooltip!: HTMLDivElement;
|
|
112
|
+
|
|
110
113
|
// non-reactive properties (changes don't auto-trigger re-rendering)
|
|
111
114
|
private _minSelectedDate = '';
|
|
112
115
|
private _maxSelectedDate = '';
|
|
@@ -412,25 +415,39 @@ export class HistogramDateRange extends LitElement {
|
|
|
412
415
|
if (this._isDragging || this.disabled) {
|
|
413
416
|
return;
|
|
414
417
|
}
|
|
418
|
+
|
|
415
419
|
const target = e.currentTarget as SVGRectElement;
|
|
416
420
|
const x = target.x.baseVal.value + this.sliderWidth / 2;
|
|
417
421
|
const dataset = target.dataset as BarDataset;
|
|
418
422
|
const itemsText = `item${dataset.numItems !== '1' ? 's' : ''}`;
|
|
419
423
|
const formattedNumItems = Number(dataset.numItems).toLocaleString();
|
|
420
424
|
|
|
421
|
-
|
|
422
|
-
|
|
425
|
+
const tooltipPadding = 2;
|
|
426
|
+
const bufferHeight = 9;
|
|
427
|
+
const heightAboveHistogram = bufferHeight + this.tooltipHeight;
|
|
428
|
+
const histogramBounds = this.getBoundingClientRect();
|
|
429
|
+
const barX = histogramBounds.x + x;
|
|
430
|
+
const histogramY = histogramBounds.y;
|
|
431
|
+
|
|
432
|
+
// Center the tooltip horizontally along the bar
|
|
433
|
+
this._tooltipOffsetX =
|
|
434
|
+
barX -
|
|
435
|
+
tooltipPadding +
|
|
436
|
+
(this._binWidth - this.sliderWidth - this.tooltipWidth) / 2 +
|
|
437
|
+
window.scrollX;
|
|
438
|
+
// Place the tooltip (with arrow) just above the top of the histogram bars
|
|
439
|
+
this._tooltipOffsetY = histogramY - heightAboveHistogram + window.scrollY;
|
|
423
440
|
|
|
424
441
|
this._tooltipContent = html`
|
|
425
442
|
${formattedNumItems} ${itemsText}<br />
|
|
426
443
|
${dataset.tooltip}
|
|
427
444
|
`;
|
|
428
|
-
this.
|
|
445
|
+
this._tooltip.showPopover?.();
|
|
429
446
|
}
|
|
430
447
|
|
|
431
448
|
private hideTooltip(): void {
|
|
432
449
|
this._tooltipContent = undefined;
|
|
433
|
-
this.
|
|
450
|
+
this._tooltip.hidePopover?.();
|
|
434
451
|
}
|
|
435
452
|
|
|
436
453
|
// use arrow functions (rather than standard JS class instance methods) so
|
|
@@ -898,20 +915,15 @@ export class HistogramDateRange extends LitElement {
|
|
|
898
915
|
}
|
|
899
916
|
|
|
900
917
|
get tooltipTemplate(): TemplateResult {
|
|
918
|
+
const styles = styleMap({
|
|
919
|
+
width: `${this.tooltipWidth}px`,
|
|
920
|
+
height: `${this.tooltipHeight}px`,
|
|
921
|
+
top: `${this._tooltipOffsetY}px`,
|
|
922
|
+
left: `${this._tooltipOffsetX}px`,
|
|
923
|
+
});
|
|
924
|
+
|
|
901
925
|
return html`
|
|
902
|
-
<style>
|
|
903
|
-
#tooltip {
|
|
904
|
-
width: ${this.tooltipWidth}px;
|
|
905
|
-
height: ${this.tooltipHeight}px;
|
|
906
|
-
top: ${-9 - this.tooltipHeight}px;
|
|
907
|
-
left: ${this._tooltipOffset}px;
|
|
908
|
-
display: ${this._tooltipVisible ? 'block' : 'none'};
|
|
909
|
-
}
|
|
910
|
-
#tooltip:after {
|
|
911
|
-
left: ${this.tooltipWidth / 2}px;
|
|
912
|
-
}
|
|
913
|
-
</style>
|
|
914
|
-
<div id="tooltip">${this._tooltipContent}</div>
|
|
926
|
+
<div id="tooltip" style=${styles} popover>${this._tooltipContent}</div>
|
|
915
927
|
`;
|
|
916
928
|
}
|
|
917
929
|
|
|
@@ -982,6 +994,8 @@ export class HistogramDateRange extends LitElement {
|
|
|
982
994
|
#tooltip {
|
|
983
995
|
position: absolute;
|
|
984
996
|
background: ${tooltipBackgroundColor};
|
|
997
|
+
margin: 0;
|
|
998
|
+
border: 0;
|
|
985
999
|
color: ${tooltipTextColor};
|
|
986
1000
|
text-align: center;
|
|
987
1001
|
border-radius: 3px;
|
|
@@ -990,12 +1004,14 @@ export class HistogramDateRange extends LitElement {
|
|
|
990
1004
|
font-family: ${tooltipFontFamily};
|
|
991
1005
|
touch-action: none;
|
|
992
1006
|
pointer-events: none;
|
|
1007
|
+
overflow: visible;
|
|
993
1008
|
}
|
|
994
1009
|
#tooltip:after {
|
|
995
1010
|
content: '';
|
|
996
1011
|
position: absolute;
|
|
997
1012
|
margin-left: -5px;
|
|
998
1013
|
top: 100%;
|
|
1014
|
+
left: 50%;
|
|
999
1015
|
/* arrow */
|
|
1000
1016
|
border: 5px solid ${tooltipTextColor};
|
|
1001
1017
|
border-color: ${tooltipBackgroundColor} transparent transparent
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
import type dayjs from 'dayjs/esm';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* As with the Date(y, m, ...) constructor, dayjs interprets years 0-99 as offsets
|
|
5
|
-
* from the year 1900 instead of the actual first-century years.
|
|
6
|
-
* We don't want that weird legacy behavior; we want years parsed literally.
|
|
7
|
-
*
|
|
8
|
-
* The maintainer of dayjs apparently refuses to address this:
|
|
9
|
-
* - https://github.com/iamkun/dayjs/pull/548#issuecomment-477660947
|
|
10
|
-
* - https://github.com/iamkun/dayjs/issues/1237
|
|
11
|
-
*
|
|
12
|
-
* So this plugin tries to detect the anomalous cases where the date format
|
|
13
|
-
* contains a YYYY block and the parsed date has a year in the 1900-1999 range,
|
|
14
|
-
* by checking whether the parsed year actually occurred in the original string.
|
|
15
|
-
* If not, then we assume it was parsed incorrectly as an offset, and adjust.
|
|
16
|
-
*
|
|
17
|
-
* In practice this assumption could fail if the input date is invalid in some
|
|
18
|
-
* way (e.g. having overflow, like a YYYY-MM-DD of "1950-22-33", which might be
|
|
19
|
-
* converted to 1951-11-02 and produce a false positive). Essentially, we trade away
|
|
20
|
-
* leniency for overflow dates to ensure that we handle all valid ones correctly.
|
|
21
|
-
* This seems a reasonable tradeoff for our present use cases. But realistically we
|
|
22
|
-
* should probably explore moving to a date lib that handles these cases properly.
|
|
23
|
-
*/
|
|
24
|
-
export default function fixFirstCenturyYears(
|
|
25
|
-
_: unknown,
|
|
26
|
-
dayjsClass: typeof dayjs.Dayjs
|
|
27
|
-
) {
|
|
28
|
-
const proto = dayjsClass.prototype;
|
|
29
|
-
const oldParse = proto.parse;
|
|
30
|
-
proto.parse = function (cfg) {
|
|
31
|
-
const inputDate = cfg.date;
|
|
32
|
-
const format = cfg.args[1];
|
|
33
|
-
oldParse.call(this, cfg);
|
|
34
|
-
|
|
35
|
-
const year = this.year();
|
|
36
|
-
const isProblemDateRange = year >= 1900 && year < 2000;
|
|
37
|
-
const isProblemStringFormat =
|
|
38
|
-
typeof format === 'string' && format.includes('YYYY');
|
|
39
|
-
const isProblemArrayFormat =
|
|
40
|
-
Array.isArray(format) &&
|
|
41
|
-
typeof format[0] === 'string' &&
|
|
42
|
-
format[0].includes('YYYY');
|
|
43
|
-
const isProblemFormat = isProblemStringFormat || isProblemArrayFormat;
|
|
44
|
-
const missingParsedYear =
|
|
45
|
-
typeof inputDate === 'string' && !inputDate.includes(`${year}`);
|
|
46
|
-
|
|
47
|
-
if (isProblemDateRange && isProblemFormat && missingParsedYear) {
|
|
48
|
-
this.$d.setFullYear(year - 1900);
|
|
49
|
-
this.init(); // Re-initialize with the new date
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
1
|
+
import type dayjs from 'dayjs/esm';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* As with the Date(y, m, ...) constructor, dayjs interprets years 0-99 as offsets
|
|
5
|
+
* from the year 1900 instead of the actual first-century years.
|
|
6
|
+
* We don't want that weird legacy behavior; we want years parsed literally.
|
|
7
|
+
*
|
|
8
|
+
* The maintainer of dayjs apparently refuses to address this:
|
|
9
|
+
* - https://github.com/iamkun/dayjs/pull/548#issuecomment-477660947
|
|
10
|
+
* - https://github.com/iamkun/dayjs/issues/1237
|
|
11
|
+
*
|
|
12
|
+
* So this plugin tries to detect the anomalous cases where the date format
|
|
13
|
+
* contains a YYYY block and the parsed date has a year in the 1900-1999 range,
|
|
14
|
+
* by checking whether the parsed year actually occurred in the original string.
|
|
15
|
+
* If not, then we assume it was parsed incorrectly as an offset, and adjust.
|
|
16
|
+
*
|
|
17
|
+
* In practice this assumption could fail if the input date is invalid in some
|
|
18
|
+
* way (e.g. having overflow, like a YYYY-MM-DD of "1950-22-33", which might be
|
|
19
|
+
* converted to 1951-11-02 and produce a false positive). Essentially, we trade away
|
|
20
|
+
* leniency for overflow dates to ensure that we handle all valid ones correctly.
|
|
21
|
+
* This seems a reasonable tradeoff for our present use cases. But realistically we
|
|
22
|
+
* should probably explore moving to a date lib that handles these cases properly.
|
|
23
|
+
*/
|
|
24
|
+
export default function fixFirstCenturyYears(
|
|
25
|
+
_: unknown,
|
|
26
|
+
dayjsClass: typeof dayjs.Dayjs
|
|
27
|
+
) {
|
|
28
|
+
const proto = dayjsClass.prototype;
|
|
29
|
+
const oldParse = proto.parse;
|
|
30
|
+
proto.parse = function (cfg) {
|
|
31
|
+
const inputDate = cfg.date;
|
|
32
|
+
const format = cfg.args[1];
|
|
33
|
+
oldParse.call(this, cfg);
|
|
34
|
+
|
|
35
|
+
const year = this.year();
|
|
36
|
+
const isProblemDateRange = year >= 1900 && year < 2000;
|
|
37
|
+
const isProblemStringFormat =
|
|
38
|
+
typeof format === 'string' && format.includes('YYYY');
|
|
39
|
+
const isProblemArrayFormat =
|
|
40
|
+
Array.isArray(format) &&
|
|
41
|
+
typeof format[0] === 'string' &&
|
|
42
|
+
format[0].includes('YYYY');
|
|
43
|
+
const isProblemFormat = isProblemStringFormat || isProblemArrayFormat;
|
|
44
|
+
const missingParsedYear =
|
|
45
|
+
typeof inputDate === 'string' && !inputDate.includes(`${year}`);
|
|
46
|
+
|
|
47
|
+
if (isProblemDateRange && isProblemFormat && missingParsedYear) {
|
|
48
|
+
this.$d.setFullYear(year - 1900);
|
|
49
|
+
this.init(); // Re-initialize with the new date
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
package/types/dayjs.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export { default } from 'dayjs/esm';
|
|
2
|
-
|
|
3
|
-
declare module 'dayjs/esm' {
|
|
4
|
-
// Widening the Dayjs interface so that we can properly extend it via plugin
|
|
5
|
-
interface Dayjs {
|
|
6
|
-
$d: Date;
|
|
7
|
-
parse(cfg: { date: unknown; args: unknown[] }): void;
|
|
8
|
-
init(): void;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
1
|
+
export { default } from 'dayjs/esm';
|
|
2
|
+
|
|
3
|
+
declare module 'dayjs/esm' {
|
|
4
|
+
// Widening the Dayjs interface so that we can properly extend it via plugin
|
|
5
|
+
interface Dayjs {
|
|
6
|
+
$d: Date;
|
|
7
|
+
parse(cfg: { date: unknown; args: unknown[] }): void;
|
|
8
|
+
init(): void;
|
|
9
|
+
}
|
|
10
|
+
}
|