@internetarchive/histogram-date-range 0.0.10-beta → 0.1.1-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.
@@ -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
- <style>
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
- '\n' + JSON.stringify(e.detail);
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">pre-selected range with default date format</div>
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">default range with custom date format</div>
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 1, 1972"
72
- maxDate="12/21/1980"
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; no debounce delay</div>
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 an array like [0, 1, 2, ... 49]
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>
126
- </div>
133
+ <histogram-date-range missingDataMessage="no data..."></histoghistogram-date-range>
134
+ </div> -->
135
+
127
136
  </body>
128
137
  </html>
@@ -12,12 +12,15 @@ var __decorate = (decorators, target, key, kind) => {
12
12
  import {
13
13
  css,
14
14
  html,
15
+ nothing,
15
16
  LitElement,
16
17
  svg
17
18
  } from "../../_snowpack/pkg/lit.js";
18
19
  import {property, state, customElement} from "../../_snowpack/pkg/lit/decorators.js";
19
20
  import {live} from "../../_snowpack/pkg/lit/directives/live.js";
20
21
  import dayjs from "../../_snowpack/pkg/dayjs/esm/index.js";
22
+ import customParseFormat from "../../_snowpack/pkg/dayjs/esm/plugin/customParseFormat.js";
23
+ dayjs.extend(customParseFormat);
21
24
  import "../../_snowpack/pkg/@internetarchive/ia-activity-indicator/ia-activity-indicator.js";
22
25
  const WIDTH = 180;
23
26
  const HEIGHT = 40;
@@ -26,19 +29,21 @@ const TOOLTIP_WIDTH = 125;
26
29
  const TOOLTIP_HEIGHT = 30;
27
30
  const DATE_FORMAT = "YYYY";
28
31
  const MISSING_DATA = "no data";
29
- const UPDATE_DEBOUNCE_DELAY_MS = 1e3;
32
+ const UPDATE_DEBOUNCE_DELAY_MS = 0;
30
33
  const SLIDER_CORNER_SIZE = 4;
31
- const sliderFill = css`var(--histogramDateRangeSliderFill, #4B65FE)`;
32
- const selectedRangeFill = css`var(--histogramDateRangeSelectedRangeFill, #DBE0FF)`;
34
+ const sliderColor = css`var(--histogramDateRangeSliderColor, #4B65FE)`;
35
+ const selectedRangeColor = css`var(--histogramDateRangeSelectedRangeColor, #DBE0FF)`;
33
36
  const barIncludedFill = css`var(--histogramDateRangeBarIncludedFill, #2C2C2C)`;
34
37
  const activityIndicatorColor = css`var(--histogramDateRangeActivityIndicator, #2C2C2C)`;
35
38
  const barExcludedFill = css`var(--histogramDateRangeBarExcludedFill, #CCCCCC)`;
36
39
  const inputBorder = css`var(--histogramDateRangeInputBorder, 0.5px solid #2C2C2C)`;
37
40
  const inputWidth = css`var(--histogramDateRangeInputWidth, 35px)`;
38
41
  const inputFontSize = css`var(--histogramDateRangeInputFontSize, 1.2rem)`;
42
+ const inputFontFamily = css`var(--histogramDateRangeInputFontFamily, sans-serif)`;
39
43
  const tooltipBackgroundColor = css`var(--histogramDateRangeTooltipBackgroundColor, #2C2C2C)`;
40
44
  const tooltipTextColor = css`var(--histogramDateRangeTooltipTextColor, #FFFFFF)`;
41
45
  const tooltipFontSize = css`var(--histogramDateRangeTooltipFontSize, 1.1rem)`;
46
+ const tooltipFontFamily = css`var(--histogramDateRangeTooltipFontFamily, sans-serif)`;
42
47
  export let HistogramDateRange = class extends LitElement {
43
48
  constructor() {
44
49
  super(...arguments);
@@ -87,11 +92,10 @@ export let HistogramDateRange = class extends LitElement {
87
92
  this.move = (e) => {
88
93
  const newX = e.offsetX - this._dragOffset;
89
94
  const slider = this._currentSlider;
90
- const date = this.translatePositionToDate(newX);
91
95
  if (slider.id === "slider-min") {
92
- this.minSelectedDate = date;
96
+ this.minSelectedDate = this.translatePositionToDate(this.validMinSliderX(newX));
93
97
  } else {
94
- this.maxSelectedDate = date;
98
+ this.maxSelectedDate = this.translatePositionToDate(this.validMaxSliderX(newX));
95
99
  }
96
100
  };
97
101
  }
@@ -100,7 +104,7 @@ export let HistogramDateRange = class extends LitElement {
100
104
  super.disconnectedCallback();
101
105
  }
102
106
  updated(changedProps) {
103
- if (changedProps.has("bins") || changedProps.has("Date")) {
107
+ if (changedProps.has("bins") || changedProps.has("minDate") || changedProps.has("maxDate") || changedProps.has("minSelectedDate") || changedProps.has("maxSelectedDate")) {
104
108
  this.handleDataUpdate();
105
109
  }
106
110
  }
@@ -109,8 +113,8 @@ export let HistogramDateRange = class extends LitElement {
109
113
  return;
110
114
  }
111
115
  this._histWidth = this.width - this.sliderWidth * 2;
112
- this._minDateMS = dayjs(this.minDate).valueOf();
113
- this._maxDateMS = dayjs(this.maxDate).valueOf();
116
+ this._minDateMS = this.getMSFromString(this.minDate);
117
+ this._maxDateMS = this.getMSFromString(this.maxDate);
114
118
  this._binWidth = this._histWidth / this._numBins;
115
119
  this._previousDateRange = this.currentDateRangeString;
116
120
  this._histData = this.calculateHistData();
@@ -121,7 +125,8 @@ export let HistogramDateRange = class extends LitElement {
121
125
  calculateHistData() {
122
126
  const minValue = Math.min(...this.bins);
123
127
  const maxValue = Math.max(...this.bins);
124
- const valueScale = this.height / Math.log1p(maxValue - minValue);
128
+ const valueRange = minValue === maxValue ? 1 : Math.log1p(maxValue - minValue);
129
+ const valueScale = this.height / valueRange;
125
130
  const dateScale = this.dateRangeMS / this._numBins;
126
131
  return this.bins.map((v, i) => {
127
132
  return {
@@ -149,38 +154,42 @@ export let HistogramDateRange = class extends LitElement {
149
154
  this._isLoading = value;
150
155
  }
151
156
  get minSelectedDate() {
152
- return this.formatDate(this._minSelectedDate);
157
+ return this.formatDate(this.getMSFromString(this._minSelectedDate));
153
158
  }
154
159
  set minSelectedDate(rawDate) {
155
160
  if (!this._minSelectedDate) {
156
161
  this._minSelectedDate = rawDate;
162
+ return;
157
163
  }
158
- const x = this.translateDateToPosition(rawDate);
159
- if (x) {
160
- const validX = this.validMinSliderX(x);
161
- this._minSelectedDate = this.translatePositionToDate(validX);
164
+ let ms = this.getMSFromString(rawDate);
165
+ if (!Number.isNaN(ms)) {
166
+ ms = Math.min(ms, this.getMSFromString(this._maxSelectedDate));
167
+ this._minSelectedDate = this.formatDate(ms);
162
168
  }
163
169
  this.requestUpdate();
164
170
  }
165
171
  get maxSelectedDate() {
166
- return this.formatDate(this._maxSelectedDate);
172
+ return this.formatDate(this.getMSFromString(this._maxSelectedDate));
167
173
  }
168
174
  set maxSelectedDate(rawDate) {
169
175
  if (!this._maxSelectedDate) {
170
176
  this._maxSelectedDate = rawDate;
177
+ return;
171
178
  }
172
- const x = this.translateDateToPosition(rawDate);
173
- if (x) {
174
- const validX = this.validMaxSliderX(x);
175
- this._maxSelectedDate = this.translatePositionToDate(validX);
179
+ let ms = this.getMSFromString(rawDate);
180
+ if (!Number.isNaN(ms)) {
181
+ ms = Math.max(this.getMSFromString(this._minSelectedDate), ms);
182
+ this._maxSelectedDate = this.formatDate(ms);
176
183
  }
177
184
  this.requestUpdate();
178
185
  }
179
186
  get minSliderX() {
180
- return this.translateDateToPosition(this.minSelectedDate) ?? this.sliderWidth;
187
+ const x = this.translateDateToPosition(this.minSelectedDate);
188
+ return this.validMinSliderX(x);
181
189
  }
182
190
  get maxSliderX() {
183
- return this.translateDateToPosition(this.maxSelectedDate) ?? this.width - this.sliderWidth;
191
+ const x = this.translateDateToPosition(this.maxSelectedDate);
192
+ return this.validMaxSliderX(x);
184
193
  }
185
194
  get dateRangeMS() {
186
195
  return this._maxDateMS - this._minDateMS;
@@ -193,9 +202,10 @@ export let HistogramDateRange = class extends LitElement {
193
202
  const x = target.x.baseVal.value + this.sliderWidth / 2;
194
203
  const dataset = target.dataset;
195
204
  const itemsText = `item${dataset.numItems !== "1" ? "s" : ""}`;
205
+ const formattedNumItems = Number(dataset.numItems).toLocaleString();
196
206
  this._tooltipOffset = x + (this._binWidth - this.sliderWidth - this.tooltipWidth) / 2;
197
207
  this._tooltipContent = html`
198
- ${dataset.numItems} ${itemsText}<br />
208
+ ${formattedNumItems} ${itemsText}<br />
199
209
  ${dataset.binStart} - ${dataset.binEnd}
200
210
  `;
201
211
  this._tooltipVisible = true;
@@ -205,12 +215,16 @@ export let HistogramDateRange = class extends LitElement {
205
215
  this._tooltipVisible = false;
206
216
  }
207
217
  validMinSliderX(newX) {
208
- const validX = Math.max(newX, this.sliderWidth);
209
- return Math.min(validX, this.maxSliderX);
218
+ if (Number.isNaN(newX)) {
219
+ return this.sliderWidth;
220
+ }
221
+ return this.clamp(newX, this.sliderWidth, this.translateDateToPosition(this.maxSelectedDate));
210
222
  }
211
223
  validMaxSliderX(newX) {
212
- const validX = Math.max(newX, this.minSliderX);
213
- return Math.min(validX, this.width - this.sliderWidth);
224
+ if (Number.isNaN(newX)) {
225
+ return this.width - this.sliderWidth;
226
+ }
227
+ return this.clamp(newX, this.translateDateToPosition(this.minSelectedDate), this.width - this.sliderWidth);
214
228
  }
215
229
  addListeners() {
216
230
  window.addEventListener("pointermove", this.move);
@@ -260,9 +274,11 @@ export let HistogramDateRange = class extends LitElement {
260
274
  return this.formatDate(this._minDateMS + milliseconds);
261
275
  }
262
276
  translateDateToPosition(date) {
263
- const milliseconds = dayjs(date).valueOf();
264
- const xPosition = this.sliderWidth + (milliseconds - this._minDateMS) * this._histWidth / this.dateRangeMS;
265
- return isNaN(milliseconds) || isNaN(xPosition) ? null : xPosition;
277
+ const milliseconds = this.getMSFromString(date);
278
+ return this.sliderWidth + (milliseconds - this._minDateMS) * this._histWidth / this.dateRangeMS;
279
+ }
280
+ clamp(x, minValue, maxValue) {
281
+ return Math.min(Math.max(x, minValue), maxValue);
266
282
  }
267
283
  handleMinDateInput(e) {
268
284
  const target = e.currentTarget;
@@ -274,24 +290,32 @@ export let HistogramDateRange = class extends LitElement {
274
290
  this.maxSelectedDate = target.value;
275
291
  this.beginEmitUpdateProcess();
276
292
  }
293
+ handleKeyUp(e) {
294
+ if (e.key === "Enter") {
295
+ const target = e.currentTarget;
296
+ target.blur();
297
+ }
298
+ }
277
299
  get currentDateRangeString() {
278
300
  return `${this.minSelectedDate}:${this.maxSelectedDate}`;
279
301
  }
280
- get minSelectedDateMS() {
281
- return dayjs(this.minSelectedDate).valueOf();
282
- }
283
- get maxSelectedDateMS() {
284
- return dayjs(this.maxSelectedDate).valueOf();
302
+ getMSFromString(date) {
303
+ const digitGroupCount = (date.split(/(\d+)/).length - 1) / 2;
304
+ if (digitGroupCount === 1) {
305
+ const dateObj = new Date(0, 0);
306
+ dateObj.setFullYear(Number(date));
307
+ return dateObj.getTime();
308
+ }
309
+ return dayjs(date, [this.dateFormat, DATE_FORMAT]).valueOf();
285
310
  }
286
311
  handleBarClick(e) {
287
312
  const dataset = e.currentTarget.dataset;
288
- const binStartDateMS = dayjs(dataset.binStart).valueOf();
289
- if (binStartDateMS < this.minSelectedDateMS) {
290
- this.minSelectedDate = dataset.binStart ?? "";
291
- }
292
- const binEndDateMS = dayjs(dataset.binEnd).valueOf();
293
- if (binEndDateMS > this.maxSelectedDateMS) {
294
- this.maxSelectedDate = dataset.binEnd ?? "";
313
+ const distanceFromMinSlider = this.getMSFromString(dataset.binStart) - this.getMSFromString(this.minSelectedDate);
314
+ const distanceFromMaxSlider = this.getMSFromString(this.maxSelectedDate) - this.getMSFromString(dataset.binEnd);
315
+ if (distanceFromMinSlider < distanceFromMaxSlider) {
316
+ this.minSelectedDate = dataset.binStart;
317
+ } else {
318
+ this.maxSelectedDate = dataset.binEnd;
295
319
  }
296
320
  this.beginEmitUpdateProcess();
297
321
  }
@@ -324,9 +348,12 @@ export let HistogramDateRange = class extends LitElement {
324
348
  return svg`
325
349
  <svg
326
350
  id="${id}"
351
+ class="
352
+ ${this.disabled ? "" : "draggable"}
353
+ ${this._isDragging ? "dragging" : ""}"
327
354
  @pointerdown="${this.drag}"
328
355
  >
329
- <path d="${sliderShape} z" fill="${sliderFill}" />
356
+ <path d="${sliderShape} z" fill="${sliderColor}" />
330
357
  <rect
331
358
  x="${sliderPositionX - this.sliderWidth * k + this.sliderWidth * 0.4 * k}"
332
359
  y="${this.height / 3}"
@@ -351,7 +378,7 @@ export let HistogramDateRange = class extends LitElement {
351
378
  y="0"
352
379
  width="${this.maxSliderX - this.minSliderX}"
353
380
  height="${this.height}"
354
- fill="${selectedRangeFill}"
381
+ fill="${selectedRangeColor}"
355
382
  />`;
356
383
  }
357
384
  get histogramTemplate() {
@@ -370,7 +397,7 @@ export let HistogramDateRange = class extends LitElement {
370
397
  @pointerenter="${this.showTooltip}"
371
398
  @pointerleave="${this.hideTooltip}"
372
399
  @click="${this.handleBarClick}"
373
- fill="${x >= this.minSliderX && x <= this.maxSliderX ? barIncludedFill : barExcludedFill}"
400
+ fill="${x + barWidth >= this.minSliderX && x <= this.maxSliderX ? barIncludedFill : barExcludedFill}"
374
401
  data-num-items="${data.value}"
375
402
  data-bin-start="${data.binStart}"
376
403
  data-bin-end="${data.binEnd}"
@@ -379,9 +406,15 @@ export let HistogramDateRange = class extends LitElement {
379
406
  return bar;
380
407
  });
381
408
  }
382
- formatDate(rawDate) {
383
- const date = dayjs(rawDate);
384
- return date.isValid() ? date.format(this.dateFormat) : "";
409
+ formatDate(dateMS) {
410
+ if (Number.isNaN(dateMS)) {
411
+ return "";
412
+ }
413
+ const date = dayjs(dateMS);
414
+ if (date.year() < 1e3) {
415
+ return String(date.year());
416
+ }
417
+ return date.format(this.dateFormat);
385
418
  }
386
419
  get minInputTemplate() {
387
420
  return html`
@@ -391,6 +424,7 @@ export let HistogramDateRange = class extends LitElement {
391
424
  type="text"
392
425
  @focus="${this.cancelPendingUpdateEvent}"
393
426
  @blur="${this.handleMinDateInput}"
427
+ @keyup="${this.handleKeyUp}"
394
428
  .value="${live(this.minSelectedDate)}"
395
429
  ?disabled="${this.disabled}"
396
430
  />
@@ -404,6 +438,7 @@ export let HistogramDateRange = class extends LitElement {
404
438
  type="text"
405
439
  @focus="${this.cancelPendingUpdateEvent}"
406
440
  @blur="${this.handleMaxDateInput}"
441
+ @keyup="${this.handleKeyUp}"
407
442
  .value="${live(this.maxSelectedDate)}"
408
443
  ?disabled="${this.disabled}"
409
444
  />
@@ -433,7 +468,7 @@ export let HistogramDateRange = class extends LitElement {
433
468
  }
434
469
  get activityIndicatorTemplate() {
435
470
  if (!this.loading) {
436
- return html``;
471
+ return nothing;
437
472
  }
438
473
  return html`
439
474
  <ia-activity-indicator mode="processing"> </ia-activity-indicator>
@@ -513,11 +548,17 @@ HistogramDateRange.styles = css`
513
548
  clicks on the bars, preventing users from being able to click in between
514
549
  bars */
515
550
  stroke: rgba(0, 0, 0, 0);
551
+ /* ensure transparent stroke wide enough to cover gap between bars */
516
552
  stroke-width: 2px;
517
553
  }
518
554
  .bar:hover {
555
+ /* highlight currently hovered bar */
519
556
  fill-opacity: 0.7;
520
557
  }
558
+ .disabled .bar:hover {
559
+ /* ensure no visual hover interaction when disabled */
560
+ fill-opacity: 1;
561
+ }
521
562
  /****** histogram ********/
522
563
  #tooltip {
523
564
  position: absolute;
@@ -527,7 +568,7 @@ HistogramDateRange.styles = css`
527
568
  border-radius: 3px;
528
569
  padding: 2px;
529
570
  font-size: ${tooltipFontSize};
530
- font-family: sans-serif;
571
+ font-family: ${tooltipFontFamily};
531
572
  touch-action: none;
532
573
  pointer-events: none;
533
574
  }
@@ -564,6 +605,7 @@ HistogramDateRange.styles = css`
564
605
  border-radius: 2px !important;
565
606
  text-align: center;
566
607
  font-size: ${inputFontSize};
608
+ font-family: ${inputFontFamily};
567
609
  }
568
610
  `;
569
611
  __decorate([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/histogram-date-range",
3
- "version": "0.0.10-beta",
3
+ "version": "0.1.1-alpha",
4
4
  "description": "Internet Archive histogram date range picker",
5
5
  "license": "AGPL-3.0-only",
6
6
  "main": "dist/index.js",
@@ -16,12 +16,12 @@
16
16
  "prepare": "npm run build",
17
17
  "lint": "eslint --ext .ts,.html . --ignore-path .gitignore && prettier \"**/*.ts\" --check --ignore-path .gitignore",
18
18
  "format": "eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \"**/*.ts\" --write --ignore-path .gitignore",
19
- "test": "tsc && wtr --coverage",
19
+ "test": "tsc && wtr",
20
20
  "test:watch": "tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wtr --watch\""
21
21
  },
22
22
  "dependencies": {
23
23
  "dayjs": "^1.9.7",
24
- "lit": "^2.0.0-rc.2"
24
+ "lit": "^2.1.1"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@internetarchive/ia-activity-indicator": "^0.0.1",