@internetarchive/collection-browser 4.9.0 → 4.9.1-webdev-9670.1

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.
Files changed (43) hide show
  1. package/README.md +11 -0
  2. package/dist/src/app-root.d.ts +11 -0
  3. package/dist/src/app-root.js +95 -1
  4. package/dist/src/app-root.js.map +1 -1
  5. package/dist/src/collection-browser.d.ts +42 -0
  6. package/dist/src/collection-browser.js +186 -2
  7. package/dist/src/collection-browser.js.map +1 -1
  8. package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +12 -0
  9. package/dist/src/sort-filter-bar/sort-filter-bar.js +64 -0
  10. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
  11. package/dist/src/tiles/grid/collection-tile.js +5 -0
  12. package/dist/src/tiles/grid/collection-tile.js.map +1 -1
  13. package/dist/src/tiles/grid/item-tile.d.ts +4 -0
  14. package/dist/src/tiles/grid/item-tile.js +16 -0
  15. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  16. package/dist/src/tiles/hover/hover-pane-controller.d.ts +2 -1
  17. package/dist/src/tiles/hover/hover-pane-controller.js +1 -1
  18. package/dist/src/tiles/hover/hover-pane-controller.js.map +1 -1
  19. package/dist/src/tiles/image-block.d.ts +5 -0
  20. package/dist/src/tiles/image-block.js +29 -0
  21. package/dist/src/tiles/image-block.js.map +1 -1
  22. package/dist/src/tiles/models.d.ts +2 -1
  23. package/dist/src/tiles/models.js.map +1 -1
  24. package/dist/src/tiles/preview/tile-preview-controller.d.ts +61 -0
  25. package/dist/src/tiles/preview/tile-preview-controller.js +216 -0
  26. package/dist/src/tiles/preview/tile-preview-controller.js.map +1 -0
  27. package/dist/src/tiles/preview/tile-preview-media.d.ts +44 -0
  28. package/dist/src/tiles/preview/tile-preview-media.js +151 -0
  29. package/dist/src/tiles/preview/tile-preview-media.js.map +1 -0
  30. package/dist/src/tiles/preview/tile-preview.d.ts +66 -0
  31. package/dist/src/tiles/preview/tile-preview.js +494 -0
  32. package/dist/src/tiles/preview/tile-preview.js.map +1 -0
  33. package/dist/src/tiles/tile-dispatcher.d.ts +7 -0
  34. package/dist/src/tiles/tile-dispatcher.js +31 -0
  35. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  36. package/dist/src/utils/grid-zoom-controller.d.ts +69 -0
  37. package/dist/src/utils/grid-zoom-controller.js +261 -0
  38. package/dist/src/utils/grid-zoom-controller.js.map +1 -0
  39. package/dist/src/utils/grid-zoom.d.ts +31 -0
  40. package/dist/src/utils/grid-zoom.js +109 -0
  41. package/dist/src/utils/grid-zoom.js.map +1 -0
  42. package/dist/src/utils/log.d.ts +1 -4
  43. package/package.json +5 -1
@@ -3,6 +3,7 @@ import { __decorate } from "tslib";
3
3
  import { html, css, LitElement, nothing, } from 'lit';
4
4
  import { customElement, property, query, state } from 'lit/decorators.js';
5
5
  import { classMap } from 'lit/directives/class-map.js';
6
+ import { styleMap } from 'lit/directives/style-map.js';
6
7
  import { msg } from '@lit/localize';
7
8
  import { SearchType, } from '@internetarchive/search-service';
8
9
  import '@internetarchive/infinite-scroller';
@@ -10,6 +11,8 @@ import { SortField, getDefaultSelectedFacets, sortOptionFromAPIString, SORT_OPTI
10
11
  import { RestorationStateHandler, } from './restoration-state-handler';
11
12
  import { CollectionBrowserDataSource } from './data-source/collection-browser-data-source';
12
13
  import { FACETLESS_PAGE_ELEMENTS } from './data-source/models';
14
+ import { GRID_ZOOM_COLUMNS, loadStoredZoomLevel, nearestZoomLevel, storeZoomLevel, zoomLayoutType, zoomLevelStyles, } from './utils/grid-zoom';
15
+ import { GridZoomController } from './utils/grid-zoom-controller';
13
16
  import { analyticsActions, analyticsCategories, } from './utils/analytics-events';
14
17
  import { updateSelectedFacetBucket } from './utils/facet-utils';
15
18
  import chevronIcon from './assets/img/icons/chevron';
@@ -24,11 +27,22 @@ import './manage/manage-bar';
24
27
  import './collection-facets';
25
28
  import './circular-activity-indicator';
26
29
  import './collection-facets/smart-facets/smart-facet-bar';
30
+ /** How long to keep a zoomed tile lined up with the focal point after a layout change. */
31
+ const GRID_ZOOM_SETTLE_MS = 600;
32
+ /** How long the grid stays hidden while the tile is lined up after a layout change. */
33
+ const GRID_ZOOM_HIDDEN_MS = 250;
27
34
  let CollectionBrowser = class CollectionBrowser extends LitElement {
28
35
  static { CollectionBrowser_1 = this; }
29
36
  constructor() {
30
37
  super();
31
38
  this.baseImageUrl = 'https://archive.org';
39
+ /**
40
+ * Prototype: show a live preview of an item when its tile is hovered,
41
+ * focused with the keyboard or long-pressed. Off by default.
42
+ */
43
+ this.enableTilePreviews = false;
44
+ /** Prototype: what tile previews may fetch and play (video clips, audio sound). */
45
+ this.tilePreviewOptions = {};
32
46
  /**
33
47
  * Which backend should be targeted by searches (e.g., metadata or FTS)
34
48
  */
@@ -131,6 +145,11 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
131
145
  * `LayoutType` for available options. Has no effect on list display modes.
132
146
  */
133
147
  this.tileLayoutType = 'default';
148
+ /**
149
+ * Prototype: lets viewers zoom the tile grid with a pinch, ctrl/cmd +/-,
150
+ * or a slider next to the display mode buttons. Off by default.
151
+ */
152
+ this.enablePinchZoom = false;
134
153
  this.manageViewLabel = 'Select items to remove';
135
154
  /** Whether to replace the default sort options with a slot for customization (default: false) */
136
155
  this.enableSortOptionsSlot = false;
@@ -166,6 +185,17 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
166
185
  this.searchResultsLoading = false;
167
186
  this.facetsLoading = false;
168
187
  this.mobileView = false;
188
+ /** The zoom level closest to the layout the grid has when no zoom level is chosen. */
189
+ this.defaultGridZoomLevel = nearestZoomLevel(4);
190
+ /** Identifies the latest zoom, so that an earlier one stops lining up its tile. */
191
+ this.gridZoomAlignment = 0;
192
+ this.gridZoomController = new GridZoomController({
193
+ getSurface: () => this.infiniteScroller,
194
+ getColumns: () => this.measureGridColumns(),
195
+ getLevel: () => this.effectiveGridZoomLevel,
196
+ captureAnchor: focal => this.tileAtPoint(focal),
197
+ commitLevel: (level, focal, anchor) => this.commitGridZoomLevel(level, focal, anchor),
198
+ });
169
199
  /**
170
200
  * Any temporarily overridden `on`/`off` state for the user's tile blurring preference if they
171
201
  * have chosen to override it, or `no-override` if they have not.
@@ -673,6 +703,7 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
673
703
  itemCount=${this.placeholderType ? 0 : nothing}
674
704
  ariaLandmarkLabel="Search results"
675
705
  .estimatedCellHeight=${this.estimatedTileHeight}
706
+ style=${styleMap(this.gridZoomStyles)}
676
707
  .minBufferMarginCells=${this.pageSize}
677
708
  .cellProvider=${this}
678
709
  .placeholderCellTemplate=${this.placeholderCellTemplate}
@@ -683,6 +714,127 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
683
714
  : nothing}
684
715
  </infinite-scroller>`;
685
716
  }
717
+ /** Whether the grid zoom prototype is on and applies to what's showing. */
718
+ get pinchZoomActive() {
719
+ return this.enablePinchZoom && this.displayMode === 'grid';
720
+ }
721
+ get effectiveGridZoomLevel() {
722
+ return this.gridZoomLevel ?? this.defaultGridZoomLevel;
723
+ }
724
+ /** Inline CSS variables for the chosen zoom level, if the viewer has chosen one. */
725
+ get gridZoomStyles() {
726
+ if (!this.pinchZoomActive || this.gridZoomLevel === undefined)
727
+ return {};
728
+ return zoomLevelStyles(this.gridZoomLevel, this.infiniteScroller?.clientWidth ?? 0);
729
+ }
730
+ get effectiveTileLayoutType() {
731
+ if (!this.pinchZoomActive || this.gridZoomLevel === undefined) {
732
+ return this.tileLayoutType;
733
+ }
734
+ return zoomLayoutType(this.gridZoomLevel) ?? this.tileLayoutType;
735
+ }
736
+ /** How many columns the scroller's grid currently has. */
737
+ measureGridColumns() {
738
+ const cells = this.infiniteScroller?.shadowRoot?.querySelectorAll('.cell-container');
739
+ if (!cells?.length)
740
+ return GRID_ZOOM_COLUMNS[this.effectiveGridZoomLevel];
741
+ const firstTop = cells[0].getBoundingClientRect().top;
742
+ let columns = 0;
743
+ for (const cell of cells) {
744
+ if (cell.getBoundingClientRect().top !== firstTop)
745
+ break;
746
+ columns += 1;
747
+ }
748
+ return columns;
749
+ }
750
+ /** Connects/disconnects the gesture handling and tracks the default zoom level. */
751
+ updateGridZoom(changed) {
752
+ if (changed.has('enablePinchZoom') && this.enablePinchZoom) {
753
+ this.gridZoomLevel ??= loadStoredZoomLevel();
754
+ }
755
+ if (this.pinchZoomActive && this.infiniteScroller) {
756
+ this.gridZoomController.connect();
757
+ if (this.gridZoomLevel === undefined) {
758
+ this.defaultGridZoomLevel = nearestZoomLevel(this.measureGridColumns());
759
+ }
760
+ }
761
+ else {
762
+ this.gridZoomAlignment += 1;
763
+ this.gridZoomController.disconnect();
764
+ }
765
+ }
766
+ gridZoomSliderChanged(e) {
767
+ this.gridZoomController.zoomToLevel(e.detail.level);
768
+ }
769
+ /**
770
+ * Switches the grid to `level`, then scrolls so that the tile that was under
771
+ * `focal` (viewport coordinates) is still under it.
772
+ */
773
+ async commitGridZoomLevel(level, focal, anchor) {
774
+ const scroller = this.infiniteScroller;
775
+ if (!scroller)
776
+ return;
777
+ this.gridZoomLevel = level;
778
+ storeZoomLevel(level);
779
+ await this.updateComplete;
780
+ await scroller.bufferStabilized;
781
+ if (!anchor)
782
+ return;
783
+ // The scroller keeps re-measuring rows for a moment after a layout change.
784
+ // The grid stays hidden for the first part of that, and the rest is corrected
785
+ // in the background.
786
+ this.gridZoomAlignment += 1;
787
+ const alignment = this.gridZoomAlignment;
788
+ await this.alignTile(scroller, anchor, focal, GRID_ZOOM_HIDDEN_MS, alignment);
789
+ this.alignTile(scroller, anchor, focal, GRID_ZOOM_SETTLE_MS, alignment).catch(() => { });
790
+ }
791
+ /** Keeps `anchor`'s tile lined up with `focal` for `duration` ms. */
792
+ async alignTile(scroller, anchor, focal, duration, alignment) {
793
+ const deadline = performance.now() + duration;
794
+ while (performance.now() < deadline &&
795
+ alignment === this.gridZoomAlignment) {
796
+ const cell = scroller.shadowRoot?.querySelector(`[data-cell-index="${anchor.index}"]`);
797
+ if (cell) {
798
+ const rect = cell.getBoundingClientRect();
799
+ const delta = rect.top + rect.height * anchor.fractionY - focal.y;
800
+ if (Math.abs(delta) > 1)
801
+ this.scrollByWithin(scroller, delta);
802
+ }
803
+ else {
804
+ await scroller.scrollToCell(anchor.index, false);
805
+ }
806
+ await new Promise(requestAnimationFrame);
807
+ }
808
+ }
809
+ /** Scrolls the nearest scrolling ancestor of `el` (or the page) by `delta` px. */
810
+ scrollByWithin(el, delta) {
811
+ let node = el;
812
+ while (node) {
813
+ const parent = node.parentElement ?? node.getRootNode().host ?? null;
814
+ if (!parent)
815
+ break;
816
+ const { overflowY } = getComputedStyle(parent);
817
+ if (overflowY === 'auto' || overflowY === 'scroll') {
818
+ parent.scrollTop += delta;
819
+ return;
820
+ }
821
+ node = parent;
822
+ }
823
+ window.scrollBy(0, delta);
824
+ }
825
+ /** Finds the tile under a viewport point and where in the tile the point falls. */
826
+ tileAtPoint(point) {
827
+ const cell = this.infiniteScroller?.shadowRoot
828
+ ?.elementsFromPoint(point.x, point.y)
829
+ .find(el => el.classList.contains('cell-container'));
830
+ if (!cell)
831
+ return undefined;
832
+ const rect = cell.getBoundingClientRect();
833
+ return {
834
+ index: Number(cell.dataset.cellIndex),
835
+ fractionY: (point.y - rect.top) / rect.height,
836
+ };
837
+ }
686
838
  /**
687
839
  * Produces a `classMap` indicating which classes the infinite scroller should have
688
840
  * given the current display mode & placeholder case.
@@ -745,6 +897,9 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
745
897
  .prefixFilterCountMap=${this.dataSource.prefixFilterCountMap}
746
898
  .enableSortOptionsSlot=${this.enableSortOptionsSlot}
747
899
  .suppressDisplayModes=${this.suppressDisplayModes}
900
+ .showGridZoomSlider=${this.pinchZoomActive}
901
+ .gridZoomLevel=${this.effectiveGridZoomLevel}
902
+ @gridZoomLevelChanged=${this.gridZoomSliderChanged}
748
903
  @sortChanged=${this.userChangedSort}
749
904
  @displayModeChanged=${this.displayModeChanged}
750
905
  @titleLetterChanged=${this.titleLetterSelected}
@@ -1420,9 +1575,18 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
1420
1575
  changed.has('baseImageUrl') ||
1421
1576
  changed.has('loggedIn') ||
1422
1577
  changed.has('tileActions') ||
1423
- changed.has('tileLayoutType')) {
1578
+ changed.has('tileLayoutType') ||
1579
+ changed.has('gridZoomLevel') ||
1580
+ changed.has('enableTilePreviews') ||
1581
+ changed.has('tilePreviewOptions')) {
1424
1582
  this.infiniteScroller?.reload();
1425
1583
  }
1584
+ this.updateGridZoom(changed);
1585
+ if (changed.has('contentWidth') &&
1586
+ this.gridZoomStyles['--imgBlockHeight']) {
1587
+ // Thumbnail sizes come from the scroller's width, which has only just changed.
1588
+ requestAnimationFrame(() => this.requestUpdate());
1589
+ }
1426
1590
  if (changed.has('profileElement')) {
1427
1591
  this.applyDefaultProfileSort();
1428
1592
  }
@@ -1523,8 +1687,11 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
1523
1687
  super.connectedCallback?.();
1524
1688
  this.setupStateRestorationObserver();
1525
1689
  this.setupResizeObserver();
1690
+ if (this.hasUpdated)
1691
+ this.updateGridZoom(new Map());
1526
1692
  }
1527
1693
  disconnectedCallback() {
1694
+ this.gridZoomController.disconnect();
1528
1695
  if (this.resizeObserver) {
1529
1696
  this.disconnectResizeObserver(this.resizeObserver);
1530
1697
  }
@@ -2075,9 +2242,11 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
2075
2242
  .suppressBlurring=${this.shouldSuppressTileBlurring}
2076
2243
  .isManageView=${this.isManageView}
2077
2244
  .tileActions=${this.tileActions}
2078
- .layoutType=${this.tileLayoutType}
2245
+ .layoutType=${this.effectiveTileLayoutType}
2079
2246
  ?showTvClips=${isTVSearch || isTVCollection}
2080
2247
  ?enableHoverPane=${true}
2248
+ ?enableTilePreview=${this.enableTilePreviews}
2249
+ .tilePreviewOptions=${this.tilePreviewOptions}
2081
2250
  ?useLocalTime=${shouldUseLocalTime}
2082
2251
  @resultSelected=${(e) => this.resultSelected(e)}
2083
2252
  >
@@ -2616,6 +2785,12 @@ __decorate([
2616
2785
  __decorate([
2617
2786
  property({ type: String })
2618
2787
  ], CollectionBrowser.prototype, "baseImageUrl", void 0);
2788
+ __decorate([
2789
+ property({ type: Boolean })
2790
+ ], CollectionBrowser.prototype, "enableTilePreviews", void 0);
2791
+ __decorate([
2792
+ property({ type: Object })
2793
+ ], CollectionBrowser.prototype, "tilePreviewOptions", void 0);
2619
2794
  __decorate([
2620
2795
  property({ type: Object })
2621
2796
  ], CollectionBrowser.prototype, "searchService", void 0);
@@ -2760,6 +2935,9 @@ __decorate([
2760
2935
  __decorate([
2761
2936
  property({ type: String })
2762
2937
  ], CollectionBrowser.prototype, "tileLayoutType", void 0);
2938
+ __decorate([
2939
+ property({ type: Boolean, attribute: 'enable-pinch-zoom' })
2940
+ ], CollectionBrowser.prototype, "enablePinchZoom", void 0);
2763
2941
  __decorate([
2764
2942
  property({ type: String })
2765
2943
  ], CollectionBrowser.prototype, "manageViewLabel", void 0);
@@ -2790,6 +2968,12 @@ __decorate([
2790
2968
  __decorate([
2791
2969
  state()
2792
2970
  ], CollectionBrowser.prototype, "mobileView", void 0);
2971
+ __decorate([
2972
+ state()
2973
+ ], CollectionBrowser.prototype, "gridZoomLevel", void 0);
2974
+ __decorate([
2975
+ state()
2976
+ ], CollectionBrowser.prototype, "defaultGridZoomLevel", void 0);
2793
2977
  __decorate([
2794
2978
  state()
2795
2979
  ], CollectionBrowser.prototype, "tileBlurOverrideState", void 0);