@internetarchive/collection-browser 1.14.17 → 2.0.0
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/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/src/app-root.d.ts +0 -2
- package/dist/src/app-root.js +0 -8
- package/dist/src/app-root.js.map +1 -1
- package/dist/src/collection-browser.d.ts +49 -200
- package/dist/src/collection-browser.js +191 -762
- package/dist/src/collection-browser.js.map +1 -1
- package/dist/src/collection-facets/facet-row.d.ts +2 -2
- package/dist/src/collection-facets/facet-row.js +5 -10
- package/dist/src/collection-facets/facet-row.js.map +1 -1
- package/dist/src/collection-facets/facets-template.d.ts +2 -2
- package/dist/src/collection-facets/facets-template.js +2 -2
- package/dist/src/collection-facets/facets-template.js.map +1 -1
- package/dist/src/collection-facets/more-facets-content.d.ts +2 -9
- package/dist/src/collection-facets/more-facets-content.js +13 -18
- package/dist/src/collection-facets/more-facets-content.js.map +1 -1
- package/dist/src/collection-facets.d.ts +2 -3
- package/dist/src/collection-facets.js +12 -13
- package/dist/src/collection-facets.js.map +1 -1
- package/dist/src/data-source/collection-browser-data-source-interface.d.ts +217 -0
- package/dist/src/data-source/collection-browser-data-source-interface.js +2 -0
- package/dist/src/data-source/collection-browser-data-source-interface.js.map +1 -0
- package/dist/src/data-source/collection-browser-data-source.d.ts +352 -0
- package/dist/src/data-source/collection-browser-data-source.js +912 -0
- package/dist/src/data-source/collection-browser-data-source.js.map +1 -0
- package/dist/src/data-source/collection-browser-query-state.d.ts +43 -0
- package/dist/src/data-source/collection-browser-query-state.js +2 -0
- package/dist/src/data-source/collection-browser-query-state.js.map +1 -0
- package/dist/src/data-source/models.d.ts +28 -0
- package/dist/src/data-source/models.js +9 -0
- package/dist/src/data-source/models.js.map +1 -0
- package/dist/src/manage/manage-bar.d.ts +1 -1
- package/dist/src/manage/manage-bar.js.map +1 -1
- package/dist/src/models.d.ts +21 -4
- package/dist/src/models.js +111 -0
- package/dist/src/models.js.map +1 -1
- package/dist/src/sort-filter-bar/sort-filter-bar.js +26 -23
- package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
- package/dist/src/tiles/grid/item-tile.d.ts +1 -0
- package/dist/src/tiles/grid/item-tile.js +28 -1
- package/dist/src/tiles/grid/item-tile.js.map +1 -1
- package/dist/src/tiles/grid/tile-stats.js +13 -8
- package/dist/src/tiles/grid/tile-stats.js.map +1 -1
- package/dist/src/tiles/hover/hover-pane-controller.d.ts +2 -2
- package/dist/src/tiles/hover/hover-pane-controller.js +1 -1
- package/dist/src/tiles/hover/hover-pane-controller.js.map +1 -1
- package/dist/src/tiles/hover/tile-hover-pane.d.ts +2 -2
- package/dist/src/tiles/hover/tile-hover-pane.js +2 -2
- package/dist/src/tiles/hover/tile-hover-pane.js.map +1 -1
- package/dist/src/tiles/item-image.d.ts +3 -0
- package/dist/src/tiles/item-image.js +30 -2
- package/dist/src/tiles/item-image.js.map +1 -1
- package/dist/src/tiles/list/tile-list.d.ts +4 -3
- package/dist/src/tiles/list/tile-list.js +39 -14
- package/dist/src/tiles/list/tile-list.js.map +1 -1
- package/dist/src/tiles/tile-dispatcher.d.ts +2 -2
- package/dist/src/tiles/tile-dispatcher.js +5 -5
- package/dist/src/tiles/tile-dispatcher.js.map +1 -1
- package/dist/src/tiles/tile-display-value-provider.d.ts +6 -2
- package/dist/src/tiles/tile-display-value-provider.js +15 -1
- package/dist/src/tiles/tile-display-value-provider.js.map +1 -1
- package/dist/src/utils/collapse-repeated-quotes.d.ts +11 -0
- package/dist/src/utils/collapse-repeated-quotes.js +14 -0
- package/dist/src/utils/collapse-repeated-quotes.js.map +1 -0
- package/dist/src/utils/log.d.ts +7 -0
- package/dist/src/utils/log.js +16 -0
- package/dist/src/utils/log.js.map +1 -0
- package/dist/src/utils/resolve-mediatype.d.ts +8 -0
- package/dist/src/utils/resolve-mediatype.js +24 -0
- package/dist/src/utils/resolve-mediatype.js.map +1 -0
- package/dist/test/collection-browser.test.js +142 -92
- package/dist/test/collection-browser.test.js.map +1 -1
- package/dist/test/collection-facets/facet-row.test.js +6 -17
- package/dist/test/collection-facets/facet-row.test.js.map +1 -1
- package/dist/test/collection-facets/more-facets-content.test.js +2 -2
- package/dist/test/collection-facets/more-facets-content.test.js.map +1 -1
- package/dist/test/collection-facets.test.js +5 -5
- package/dist/test/collection-facets.test.js.map +1 -1
- package/dist/test/data-source/collection-browser-data-source.test.d.ts +1 -0
- package/dist/test/data-source/collection-browser-data-source.test.js +80 -0
- package/dist/test/data-source/collection-browser-data-source.test.js.map +1 -0
- package/dist/test/item-image.test.js +33 -34
- package/dist/test/item-image.test.js.map +1 -1
- package/dist/test/mocks/mock-search-responses.d.ts +3 -0
- package/dist/test/mocks/mock-search-responses.js +131 -0
- package/dist/test/mocks/mock-search-responses.js.map +1 -1
- package/dist/test/mocks/mock-search-service.js +4 -1
- package/dist/test/mocks/mock-search-service.js.map +1 -1
- package/dist/test/sort-filter-bar/sort-filter-bar.test.js +41 -4
- package/dist/test/sort-filter-bar/sort-filter-bar.test.js.map +1 -1
- package/dist/test/tile-stats.test.js +62 -0
- package/dist/test/tile-stats.test.js.map +1 -1
- package/dist/test/tiles/grid/item-tile.test.js +44 -0
- package/dist/test/tiles/grid/item-tile.test.js.map +1 -1
- package/dist/test/tiles/hover/hover-pane-controller.test.js +18 -17
- package/dist/test/tiles/hover/hover-pane-controller.test.js.map +1 -1
- package/dist/test/tiles/list/tile-list.test.js +44 -5
- package/dist/test/tiles/list/tile-list.test.js.map +1 -1
- package/dist/test/tiles/tile-dispatcher.test.js +12 -0
- package/dist/test/tiles/tile-dispatcher.test.js.map +1 -1
- package/index.ts +4 -1
- package/package.json +3 -4
- package/src/app-root.ts +0 -10
- package/src/collection-browser.ts +219 -935
- package/src/collection-facets/facet-row.ts +4 -9
- package/src/collection-facets/facets-template.ts +3 -3
- package/src/collection-facets/more-facets-content.ts +15 -20
- package/src/collection-facets.ts +11 -14
- package/src/data-source/collection-browser-data-source-interface.ts +272 -0
- package/src/data-source/collection-browser-data-source.ts +1166 -0
- package/src/data-source/collection-browser-query-state.ts +54 -0
- package/src/data-source/models.ts +38 -0
- package/src/manage/manage-bar.ts +1 -1
- package/src/models.ts +164 -3
- package/src/sort-filter-bar/sort-filter-bar.ts +26 -23
- package/src/tiles/grid/item-tile.ts +36 -1
- package/src/tiles/grid/tile-stats.ts +12 -7
- package/src/tiles/hover/hover-pane-controller.ts +3 -3
- package/src/tiles/hover/tile-hover-pane.ts +3 -3
- package/src/tiles/item-image.ts +28 -0
- package/src/tiles/list/tile-list.ts +56 -23
- package/src/tiles/tile-dispatcher.ts +5 -5
- package/src/tiles/tile-display-value-provider.ts +20 -2
- package/src/utils/collapse-repeated-quotes.ts +13 -0
- package/src/utils/log.ts +16 -0
- package/src/utils/resolve-mediatype.ts +26 -0
- package/test/collection-browser.test.ts +213 -106
- package/test/collection-facets/facet-row.test.ts +5 -18
- package/test/collection-facets/more-facets-content.test.ts +4 -2
- package/test/collection-facets.test.ts +5 -5
- package/test/data-source/collection-browser-data-source.test.ts +103 -0
- package/test/item-image.test.ts +34 -36
- package/test/mocks/mock-search-responses.ts +144 -0
- package/test/mocks/mock-search-service.ts +6 -0
- package/test/sort-filter-bar/sort-filter-bar.test.ts +50 -4
- package/test/tile-stats.test.ts +104 -0
- package/test/tiles/grid/item-tile.test.ts +55 -0
- package/test/tiles/hover/hover-pane-controller.test.ts +19 -17
- package/test/tiles/list/tile-list.test.ts +55 -5
- package/test/tiles/tile-dispatcher.test.ts +13 -0
- package/dist/test/mocks/mock-collection-name-cache.d.ts +0 -9
- package/dist/test/mocks/mock-collection-name-cache.js +0 -18
- package/dist/test/mocks/mock-collection-name-cache.js.map +0 -1
- package/test/mocks/mock-collection-name-cache.ts +0 -24
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CollectionExtraInfo,
|
|
3
|
+
PageElementName,
|
|
4
|
+
SearchServiceInterface,
|
|
5
|
+
SearchType,
|
|
6
|
+
SortDirection,
|
|
7
|
+
SortParam,
|
|
8
|
+
} from '@internetarchive/search-service';
|
|
9
|
+
import type { SelectedFacets, SortField } from '../models';
|
|
10
|
+
import type { CollectionBrowserDataSourceInterface } from './collection-browser-data-source-interface';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Properties of collection browser that affect the overall search query
|
|
14
|
+
*/
|
|
15
|
+
export interface CollectionBrowserQueryState {
|
|
16
|
+
baseQuery?: string;
|
|
17
|
+
withinCollection?: string;
|
|
18
|
+
withinProfile?: string;
|
|
19
|
+
profileElement?: PageElementName;
|
|
20
|
+
searchType: SearchType;
|
|
21
|
+
selectedFacets?: SelectedFacets;
|
|
22
|
+
minSelectedDate?: string;
|
|
23
|
+
maxSelectedDate?: string;
|
|
24
|
+
selectedTitleFilter: string | null;
|
|
25
|
+
selectedCreatorFilter: string | null;
|
|
26
|
+
selectedSort?: SortField;
|
|
27
|
+
sortDirection: SortDirection | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Interface representing search-related state and operations required by the
|
|
32
|
+
* data source on its host component.
|
|
33
|
+
*/
|
|
34
|
+
export interface CollectionBrowserSearchInterface
|
|
35
|
+
extends CollectionBrowserQueryState {
|
|
36
|
+
searchService?: SearchServiceInterface;
|
|
37
|
+
readonly sortParam: SortParam | null;
|
|
38
|
+
readonly defaultSortParam: SortParam | null;
|
|
39
|
+
readonly suppressFacets?: boolean;
|
|
40
|
+
readonly initialPageNumber: number;
|
|
41
|
+
readonly currentVisiblePageNumbers: number[];
|
|
42
|
+
readonly clearResultsOnEmptyQuery?: boolean;
|
|
43
|
+
readonly dataSource?: CollectionBrowserDataSourceInterface;
|
|
44
|
+
|
|
45
|
+
getSessionId(): Promise<string>;
|
|
46
|
+
setSearchResultsLoading(loading: boolean): void;
|
|
47
|
+
setFacetsLoading(loading: boolean): void;
|
|
48
|
+
setTotalResultCount(count: number): void;
|
|
49
|
+
setTileCount(count: number): void;
|
|
50
|
+
applyDefaultCollectionSort(collectionInfo?: CollectionExtraInfo): void;
|
|
51
|
+
emitEmptyResults(): void;
|
|
52
|
+
emitQueryStateChanged(): void;
|
|
53
|
+
refreshVisibleResults(): void;
|
|
54
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PageElementName,
|
|
3
|
+
PageType,
|
|
4
|
+
} from '@internetarchive/search-service';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A Map from collection identifiers to their corresponding collection titles.
|
|
8
|
+
*/
|
|
9
|
+
export type CollectionTitles = Map<string, string>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The subset of search service params that uniquely specify the type of results
|
|
13
|
+
* that are sought by an instance of collection browser.
|
|
14
|
+
*/
|
|
15
|
+
export type PageSpecifierParams = {
|
|
16
|
+
/**
|
|
17
|
+
* What high-level type of page is being fetched for (search results, collection, or profile)
|
|
18
|
+
*/
|
|
19
|
+
pageType: PageType;
|
|
20
|
+
/**
|
|
21
|
+
* The target identifier for collection or profile pages (e.g., "prelinger", "@brewster", ...)
|
|
22
|
+
*/
|
|
23
|
+
pageTarget: string;
|
|
24
|
+
/**
|
|
25
|
+
* Which specific elements of a profile page to fetch. Corresponds to individual tab data
|
|
26
|
+
* (e.g., "uploads", "reviews", ...)
|
|
27
|
+
*/
|
|
28
|
+
pageElements?: PageElementName[];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* List of profile page elements that do not currently allow faceting
|
|
33
|
+
*/
|
|
34
|
+
export const FACETLESS_PAGE_ELEMENTS: PageElementName[] = [
|
|
35
|
+
'forum_posts',
|
|
36
|
+
'lending',
|
|
37
|
+
'web_archives',
|
|
38
|
+
];
|
package/src/manage/manage-bar.ts
CHANGED
package/src/models.ts
CHANGED
|
@@ -1,42 +1,193 @@
|
|
|
1
1
|
import type { MediaType } from '@internetarchive/field-parsers';
|
|
2
2
|
import {
|
|
3
3
|
AggregationSortType,
|
|
4
|
+
SearchResult,
|
|
4
5
|
SortDirection,
|
|
5
6
|
} from '@internetarchive/search-service';
|
|
7
|
+
import { collapseRepeatedQuotes } from './utils/collapse-repeated-quotes';
|
|
8
|
+
import { resolveMediatype } from './utils/resolve-mediatype';
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Flags that can affect the visibility of content on a tile
|
|
12
|
+
*/
|
|
13
|
+
interface TileFlags {
|
|
14
|
+
loginRequired: boolean;
|
|
15
|
+
contentWarning: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Class for converting & storing raw search results in the correct format for UI tiles.
|
|
20
|
+
*/
|
|
21
|
+
export class TileModel {
|
|
8
22
|
averageRating?: number;
|
|
23
|
+
|
|
24
|
+
captureDates?: Date[]; // List of capture dates for a URL, used on profile Web Archives tiles
|
|
25
|
+
|
|
9
26
|
checked: boolean; // Whether this tile is currently checked for item management functions
|
|
27
|
+
|
|
10
28
|
collectionIdentifier?: string;
|
|
29
|
+
|
|
11
30
|
collectionName?: string;
|
|
31
|
+
|
|
12
32
|
collectionFilesCount: number;
|
|
33
|
+
|
|
13
34
|
collections: string[];
|
|
35
|
+
|
|
14
36
|
collectionSize: number;
|
|
37
|
+
|
|
15
38
|
commentCount: number;
|
|
39
|
+
|
|
16
40
|
creator?: string;
|
|
41
|
+
|
|
17
42
|
creators: string[];
|
|
43
|
+
|
|
44
|
+
dateStr?: string; // A string representation of the publication date, used strictly for passing preformatted dates to the parent
|
|
45
|
+
|
|
18
46
|
dateAdded?: Date; // Date added to public search (software-defined) [from: addeddate]
|
|
47
|
+
|
|
19
48
|
dateArchived?: Date; // Date archived (software-defined) item created on archive.org [from: publicdate]
|
|
49
|
+
|
|
20
50
|
datePublished?: Date; // Date work published in the world (user-defined) [from: date]
|
|
51
|
+
|
|
21
52
|
dateReviewed?: Date; // Date reviewed (user-created) most recent review [from: reviewdate]
|
|
53
|
+
|
|
22
54
|
description?: string;
|
|
55
|
+
|
|
23
56
|
favCount: number;
|
|
57
|
+
|
|
24
58
|
href?: string;
|
|
25
|
-
|
|
59
|
+
|
|
60
|
+
identifier?: string;
|
|
61
|
+
|
|
26
62
|
issue?: string;
|
|
63
|
+
|
|
27
64
|
itemCount: number;
|
|
65
|
+
|
|
28
66
|
mediatype: MediaType;
|
|
67
|
+
|
|
29
68
|
source?: string;
|
|
69
|
+
|
|
30
70
|
snippets?: string[];
|
|
71
|
+
|
|
31
72
|
subjects: string[];
|
|
73
|
+
|
|
32
74
|
title: string;
|
|
33
|
-
|
|
75
|
+
|
|
76
|
+
viewCount?: number;
|
|
77
|
+
|
|
34
78
|
volume?: string;
|
|
79
|
+
|
|
35
80
|
weeklyViewCount?: number;
|
|
81
|
+
|
|
36
82
|
loginRequired: boolean;
|
|
83
|
+
|
|
37
84
|
contentWarning: boolean;
|
|
85
|
+
|
|
86
|
+
constructor(result: SearchResult) {
|
|
87
|
+
const flags = this.getFlags(result);
|
|
88
|
+
|
|
89
|
+
this.averageRating = result.avg_rating?.value;
|
|
90
|
+
this.captureDates = result.capture_dates?.values;
|
|
91
|
+
this.checked = false;
|
|
92
|
+
this.collections = result.collection?.values ?? [];
|
|
93
|
+
this.collectionFilesCount = result.collection_files_count?.value ?? 0;
|
|
94
|
+
this.collectionSize = result.collection_size?.value ?? 0;
|
|
95
|
+
this.commentCount = result.num_reviews?.value ?? 0;
|
|
96
|
+
this.creator = result.creator?.value;
|
|
97
|
+
this.creators = result.creator?.values ?? [];
|
|
98
|
+
this.dateAdded = result.addeddate?.value;
|
|
99
|
+
this.dateArchived = result.publicdate?.value;
|
|
100
|
+
this.datePublished = result.date?.value;
|
|
101
|
+
this.dateReviewed = result.reviewdate?.value;
|
|
102
|
+
this.description = result.description?.values.join('\n');
|
|
103
|
+
this.favCount = result.num_favorites?.value ?? 0;
|
|
104
|
+
this.href = collapseRepeatedQuotes(result.__href__?.value);
|
|
105
|
+
this.identifier = result.identifier;
|
|
106
|
+
this.issue = result.issue?.value;
|
|
107
|
+
this.itemCount = result.item_count?.value ?? 0;
|
|
108
|
+
this.mediatype = resolveMediatype(result);
|
|
109
|
+
this.snippets = result.highlight?.values ?? [];
|
|
110
|
+
this.source = result.source?.value;
|
|
111
|
+
this.subjects = result.subject?.values ?? [];
|
|
112
|
+
this.title = result.title?.value ?? '';
|
|
113
|
+
this.volume = result.volume?.value;
|
|
114
|
+
this.viewCount = result.downloads?.value;
|
|
115
|
+
this.weeklyViewCount = result.week?.value;
|
|
116
|
+
this.loginRequired = flags.loginRequired;
|
|
117
|
+
this.contentWarning = flags.contentWarning;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Copies the contents of this TileModel onto a new instance
|
|
122
|
+
*/
|
|
123
|
+
clone(): TileModel {
|
|
124
|
+
const cloned = new TileModel({});
|
|
125
|
+
cloned.averageRating = this.averageRating;
|
|
126
|
+
cloned.captureDates = this.captureDates;
|
|
127
|
+
cloned.checked = this.checked;
|
|
128
|
+
cloned.collections = this.collections;
|
|
129
|
+
cloned.collectionFilesCount = this.collectionFilesCount;
|
|
130
|
+
cloned.collectionSize = this.collectionSize;
|
|
131
|
+
cloned.commentCount = this.commentCount;
|
|
132
|
+
cloned.creator = this.creator;
|
|
133
|
+
cloned.creators = this.creators;
|
|
134
|
+
cloned.dateStr = this.dateStr;
|
|
135
|
+
cloned.dateAdded = this.dateAdded;
|
|
136
|
+
cloned.dateArchived = this.dateArchived;
|
|
137
|
+
cloned.datePublished = this.datePublished;
|
|
138
|
+
cloned.dateReviewed = this.dateReviewed;
|
|
139
|
+
cloned.description = this.description;
|
|
140
|
+
cloned.favCount = this.favCount;
|
|
141
|
+
cloned.href = this.href;
|
|
142
|
+
cloned.identifier = this.identifier;
|
|
143
|
+
cloned.issue = this.issue;
|
|
144
|
+
cloned.itemCount = this.itemCount;
|
|
145
|
+
cloned.mediatype = this.mediatype;
|
|
146
|
+
cloned.snippets = this.snippets;
|
|
147
|
+
cloned.source = this.source;
|
|
148
|
+
cloned.subjects = this.subjects;
|
|
149
|
+
cloned.title = this.title;
|
|
150
|
+
cloned.volume = this.volume;
|
|
151
|
+
cloned.viewCount = this.viewCount;
|
|
152
|
+
cloned.weeklyViewCount = this.weeklyViewCount;
|
|
153
|
+
cloned.loginRequired = this.loginRequired;
|
|
154
|
+
cloned.contentWarning = this.contentWarning;
|
|
155
|
+
return cloned;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Determines the appropriate tile flags for the given search result
|
|
160
|
+
* (login required and/or content warning)
|
|
161
|
+
*/
|
|
162
|
+
private getFlags(result: SearchResult): TileFlags {
|
|
163
|
+
const flags: TileFlags = {
|
|
164
|
+
loginRequired: false,
|
|
165
|
+
contentWarning: false,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Check if item and item in "modifying" collection, setting above flags
|
|
169
|
+
if (
|
|
170
|
+
result.collection?.values.length &&
|
|
171
|
+
result.mediatype?.value !== 'collection'
|
|
172
|
+
) {
|
|
173
|
+
for (const collection of result.collection?.values ?? []) {
|
|
174
|
+
if (collection === 'loggedin') {
|
|
175
|
+
flags.loginRequired = true;
|
|
176
|
+
if (flags.contentWarning) break;
|
|
177
|
+
}
|
|
178
|
+
if (collection === 'no-preview') {
|
|
179
|
+
flags.contentWarning = true;
|
|
180
|
+
if (flags.loginRequired) break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return flags;
|
|
186
|
+
}
|
|
38
187
|
}
|
|
39
188
|
|
|
189
|
+
export type RequestKind = 'full' | 'hits' | 'aggregations';
|
|
190
|
+
|
|
40
191
|
export type CollectionDisplayMode = 'grid' | 'list-compact' | 'list-detail';
|
|
41
192
|
|
|
42
193
|
export type TileDisplayMode =
|
|
@@ -280,6 +431,16 @@ export function sortOptionFromAPIString(sortName?: string | null): SortOption {
|
|
|
280
431
|
);
|
|
281
432
|
}
|
|
282
433
|
|
|
434
|
+
export const defaultProfileElementSorts: Record<
|
|
435
|
+
string,
|
|
436
|
+
Exclude<SortField, SortField.default>
|
|
437
|
+
> = {
|
|
438
|
+
uploads: SortField.datearchived,
|
|
439
|
+
reviews: SortField.datereviewed,
|
|
440
|
+
collections: SortField.datearchived,
|
|
441
|
+
web_archives: SortField.datearchived,
|
|
442
|
+
};
|
|
443
|
+
|
|
283
444
|
/** A union of the fields that permit prefix filtering (e.g., alphabetical filtering) */
|
|
284
445
|
export type PrefixFilterType = 'title' | 'creator';
|
|
285
446
|
|
|
@@ -178,7 +178,7 @@ export class SortFilterBar
|
|
|
178
178
|
this.setupEscapeListeners();
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
if (changed.has('resizeObserver')) {
|
|
181
|
+
if (changed.has('resizeObserver') || changed.has('showLoansTopBar')) {
|
|
182
182
|
const oldObserver = changed.get(
|
|
183
183
|
'resizeObserver'
|
|
184
184
|
) as SharedResizeObserverInterface;
|
|
@@ -216,35 +216,37 @@ export class SortFilterBar
|
|
|
216
216
|
private disconnectResizeObserver(
|
|
217
217
|
resizeObserver: SharedResizeObserverInterface
|
|
218
218
|
) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
});
|
|
219
|
+
if (this.sortSelectorContainer) {
|
|
220
|
+
resizeObserver.removeObserver({
|
|
221
|
+
target: this.sortSelectorContainer,
|
|
222
|
+
handler: this,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
226
225
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
226
|
+
if (this.desktopSortContainer) {
|
|
227
|
+
resizeObserver.removeObserver({
|
|
228
|
+
target: this.desktopSortContainer,
|
|
229
|
+
handler: this,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
private setupResizeObserver() {
|
|
234
235
|
if (!this.resizeObserver) return;
|
|
235
236
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
});
|
|
237
|
+
if (this.sortSelectorContainer) {
|
|
238
|
+
this.resizeObserver.addObserver({
|
|
239
|
+
target: this.sortSelectorContainer,
|
|
240
|
+
handler: this,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
243
|
|
|
244
|
-
this.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
244
|
+
if (this.desktopSortContainer) {
|
|
245
|
+
this.resizeObserver.addObserver({
|
|
246
|
+
target: this.desktopSortContainer,
|
|
247
|
+
handler: this,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
248
250
|
}
|
|
249
251
|
|
|
250
252
|
handleResize(entry: ResizeObserverEntry): void {
|
|
@@ -1114,6 +1116,7 @@ export class SortFilterBar
|
|
|
1114
1116
|
height: 100%;
|
|
1115
1117
|
padding-left: 5px;
|
|
1116
1118
|
font-size: 1.4rem;
|
|
1119
|
+
font-family: var(--ia-theme-base-font-family);
|
|
1117
1120
|
line-height: 2;
|
|
1118
1121
|
color: var(--ia-theme-primary-text-color, #2c2c2c);
|
|
1119
1122
|
white-space: nowrap;
|
|
@@ -4,6 +4,7 @@ import { customElement, property } from 'lit/decorators.js';
|
|
|
4
4
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
5
5
|
import { msg } from '@lit/localize';
|
|
6
6
|
|
|
7
|
+
import { map } from 'lit/directives/map.js';
|
|
7
8
|
import { DateFormat, formatDate } from '../../utils/format-date';
|
|
8
9
|
import { isFirstMillisecondOfUTCYear } from '../../utils/local-date-from-utc';
|
|
9
10
|
import { BaseTileComponent } from '../base-tile-component';
|
|
@@ -57,7 +58,7 @@ export class ItemTile extends BaseTileComponent {
|
|
|
57
58
|
${this.isSortedByDate
|
|
58
59
|
? this.sortedDateInfoTemplate
|
|
59
60
|
: this.creatorTemplate}
|
|
60
|
-
${this.textSnippetsTemplate}
|
|
61
|
+
${this.webArchivesCaptureDatesTemplate} ${this.textSnippetsTemplate}
|
|
61
62
|
</div>
|
|
62
63
|
|
|
63
64
|
<tile-stats
|
|
@@ -172,6 +173,26 @@ export class ItemTile extends BaseTileComponent {
|
|
|
172
173
|
`;
|
|
173
174
|
}
|
|
174
175
|
|
|
176
|
+
private get webArchivesCaptureDatesTemplate():
|
|
177
|
+
| TemplateResult
|
|
178
|
+
| typeof nothing {
|
|
179
|
+
if (!this.model?.captureDates || !this.model.title) return nothing;
|
|
180
|
+
|
|
181
|
+
return html`
|
|
182
|
+
<ul class="capture-dates">
|
|
183
|
+
${map(
|
|
184
|
+
this.model.captureDates,
|
|
185
|
+
date => html`<li>
|
|
186
|
+
${this.displayValueProvider.webArchivesCaptureLink(
|
|
187
|
+
this.model!.title,
|
|
188
|
+
date
|
|
189
|
+
)}
|
|
190
|
+
</li>`
|
|
191
|
+
)}
|
|
192
|
+
</ul>
|
|
193
|
+
`;
|
|
194
|
+
}
|
|
195
|
+
|
|
175
196
|
private get isSortedByDate(): boolean {
|
|
176
197
|
return ['date', 'reviewdate', 'addeddate', 'publicdate'].includes(
|
|
177
198
|
this.sortParam?.field as string
|
|
@@ -200,10 +221,24 @@ export class ItemTile extends BaseTileComponent {
|
|
|
200
221
|
return [
|
|
201
222
|
baseTileStyles,
|
|
202
223
|
css`
|
|
224
|
+
a:link {
|
|
225
|
+
text-decoration: none;
|
|
226
|
+
color: var(--ia-theme-link-color, #4b64ff);
|
|
227
|
+
}
|
|
228
|
+
a:hover {
|
|
229
|
+
text-decoration: underline;
|
|
230
|
+
}
|
|
231
|
+
|
|
203
232
|
.container {
|
|
204
233
|
border: 1px solid ${tileBorderColor};
|
|
205
234
|
}
|
|
206
235
|
|
|
236
|
+
.capture-dates {
|
|
237
|
+
margin: 0;
|
|
238
|
+
padding: 0 5px;
|
|
239
|
+
list-style-type: none;
|
|
240
|
+
}
|
|
241
|
+
|
|
207
242
|
text-snippet-block {
|
|
208
243
|
--containerLeftMargin: 5px;
|
|
209
244
|
--containerTopMargin: 5px;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { css, CSSResultGroup, html, LitElement } from 'lit';
|
|
2
2
|
import { customElement, property } from 'lit/decorators.js';
|
|
3
3
|
|
|
4
|
+
import { msg } from '@lit/localize';
|
|
4
5
|
import { favoriteFilledIcon } from '../../assets/img/icons/favorite-filled';
|
|
5
6
|
import { reviewsIcon } from '../../assets/img/icons/reviews';
|
|
6
7
|
import { uploadIcon } from '../../assets/img/icons/upload';
|
|
@@ -33,8 +34,8 @@ export class TileStats extends LitElement {
|
|
|
33
34
|
|
|
34
35
|
const uploadsOrViewsTitle =
|
|
35
36
|
this.mediatype === 'account'
|
|
36
|
-
? `${this.itemCount} uploads`
|
|
37
|
-
: `${this.viewCount} ${this.viewLabel ?? 'all-time views'}`;
|
|
37
|
+
? `${this.itemCount ?? 0} uploads`
|
|
38
|
+
: `${this.viewCount ?? 0} ${this.viewLabel ?? 'all-time views'}`;
|
|
38
39
|
|
|
39
40
|
return html`
|
|
40
41
|
<div class="item-stats">
|
|
@@ -43,17 +44,21 @@ export class TileStats extends LitElement {
|
|
|
43
44
|
</p>
|
|
44
45
|
<ul id="stats-row">
|
|
45
46
|
<li class="col">
|
|
46
|
-
<p class="sr-only"
|
|
47
|
+
<p class="sr-only">${msg('Mediatype:')}</p>
|
|
47
48
|
<mediatype-icon .mediatype=${this.mediatype}></mediatype-icon>
|
|
48
49
|
</li>
|
|
49
50
|
<li class="col" title="${uploadsOrViewsTitle}">
|
|
50
51
|
${this.mediatype === 'account' ? uploadIcon : viewsIcon}
|
|
51
52
|
<p class="status-text">
|
|
52
53
|
<span class="sr-only">
|
|
53
|
-
${this.mediatype === 'account'
|
|
54
|
+
${this.mediatype === 'account'
|
|
55
|
+
? msg('Uploads:')
|
|
56
|
+
: msg('Views:')}
|
|
54
57
|
</span>
|
|
55
58
|
${formatCount(
|
|
56
|
-
this.mediatype === 'account'
|
|
59
|
+
this.mediatype === 'account'
|
|
60
|
+
? this.itemCount ?? 0
|
|
61
|
+
: this.viewCount ?? 0,
|
|
57
62
|
'short',
|
|
58
63
|
'short'
|
|
59
64
|
)}
|
|
@@ -62,14 +67,14 @@ export class TileStats extends LitElement {
|
|
|
62
67
|
<li class="col" title="${formattedFavCount} favorites">
|
|
63
68
|
${favoriteFilledIcon}
|
|
64
69
|
<p class="status-text">
|
|
65
|
-
<span class="sr-only"
|
|
70
|
+
<span class="sr-only">${msg('Favorites:')}</span>
|
|
66
71
|
${formattedFavCount}
|
|
67
72
|
</p>
|
|
68
73
|
</li>
|
|
69
74
|
<li class="col reviews" title="${formattedReviewCount} reviews">
|
|
70
75
|
${reviewsIcon}
|
|
71
76
|
<p class="status-text">
|
|
72
|
-
<span class="sr-only"
|
|
77
|
+
<span class="sr-only">${msg('Reviews:')}</span>
|
|
73
78
|
${formattedReviewCount}
|
|
74
79
|
</p>
|
|
75
80
|
</li>
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { CollectionNameCacheInterface } from '@internetarchive/collection-name-cache';
|
|
2
1
|
import type { SortParam } from '@internetarchive/search-service';
|
|
3
2
|
import {
|
|
4
3
|
html,
|
|
@@ -8,6 +7,7 @@ import {
|
|
|
8
7
|
ReactiveControllerHost,
|
|
9
8
|
} from 'lit';
|
|
10
9
|
import type { TileModel } from '../../models';
|
|
10
|
+
import type { CollectionTitles } from '../../data-source/models';
|
|
11
11
|
|
|
12
12
|
type HoverPaneState = 'hidden' | 'shown' | 'fading-out';
|
|
13
13
|
|
|
@@ -17,7 +17,7 @@ export interface HoverPaneProperties {
|
|
|
17
17
|
baseImageUrl?: string;
|
|
18
18
|
loggedIn: boolean;
|
|
19
19
|
sortParam: SortParam | null;
|
|
20
|
-
|
|
20
|
+
collectionTitles?: CollectionTitles;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export interface HoverPaneControllerOptions {
|
|
@@ -187,7 +187,7 @@ export class HoverPaneController implements HoverPaneControllerInterface {
|
|
|
187
187
|
.baseImageUrl=${this.hoverPaneProps?.baseImageUrl}
|
|
188
188
|
.loggedIn=${this.hoverPaneProps?.loggedIn}
|
|
189
189
|
.sortParam=${this.hoverPaneProps?.sortParam}
|
|
190
|
-
.
|
|
190
|
+
.collectionTitles=${this.hoverPaneProps?.collectionTitles}
|
|
191
191
|
></tile-hover-pane>`
|
|
192
192
|
: nothing;
|
|
193
193
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { CollectionNameCacheInterface } from '@internetarchive/collection-name-cache';
|
|
2
1
|
import type { SortParam } from '@internetarchive/search-service';
|
|
3
2
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from 'lit';
|
|
4
3
|
import { customElement, property } from 'lit/decorators.js';
|
|
5
4
|
import type { TileModel } from '../../models';
|
|
5
|
+
import type { CollectionTitles } from '../../data-source/models';
|
|
6
6
|
import '../list/tile-list';
|
|
7
7
|
|
|
8
8
|
@customElement('tile-hover-pane')
|
|
@@ -18,7 +18,7 @@ export class TileHoverPane extends LitElement {
|
|
|
18
18
|
@property({ type: Object }) sortParam?: SortParam;
|
|
19
19
|
|
|
20
20
|
@property({ type: Object })
|
|
21
|
-
|
|
21
|
+
collectionTitles?: CollectionTitles;
|
|
22
22
|
|
|
23
23
|
protected render(): TemplateResult {
|
|
24
24
|
return html`
|
|
@@ -29,7 +29,7 @@ export class TileHoverPane extends LitElement {
|
|
|
29
29
|
.baseImageUrl=${this.baseImageUrl}
|
|
30
30
|
.loggedIn=${this.loggedIn}
|
|
31
31
|
.sortParam=${this.sortParam}
|
|
32
|
-
.
|
|
32
|
+
.collectionTitles=${this.collectionTitles}
|
|
33
33
|
></tile-list>
|
|
34
34
|
</div>
|
|
35
35
|
`;
|
package/src/tiles/item-image.ts
CHANGED
|
@@ -24,6 +24,8 @@ export class ItemImage extends LitElement {
|
|
|
24
24
|
|
|
25
25
|
@state() private isWaveform = false;
|
|
26
26
|
|
|
27
|
+
@state() private isNotFound = false;
|
|
28
|
+
|
|
27
29
|
@query('img') private baseImage!: HTMLImageElement;
|
|
28
30
|
|
|
29
31
|
render() {
|
|
@@ -43,6 +45,7 @@ export class ItemImage extends LitElement {
|
|
|
43
45
|
src="${this.imageSrc}"
|
|
44
46
|
alt=""
|
|
45
47
|
@load=${this.onLoad}
|
|
48
|
+
@error=${this.onError}
|
|
46
49
|
/>
|
|
47
50
|
`;
|
|
48
51
|
}
|
|
@@ -51,12 +54,33 @@ export class ItemImage extends LitElement {
|
|
|
51
54
|
* Helpers
|
|
52
55
|
*/
|
|
53
56
|
private get imageSrc() {
|
|
57
|
+
if (this.isNotFound) return this.notFoundSrc;
|
|
58
|
+
|
|
59
|
+
// Use the correct image for web capture tiles, if possible
|
|
60
|
+
if (this.model?.captureDates && this.model.identifier) {
|
|
61
|
+
try {
|
|
62
|
+
const url = new URL(this.model.identifier);
|
|
63
|
+
const domain = encodeURIComponent(url.hostname);
|
|
64
|
+
return this.baseImageUrl
|
|
65
|
+
? `https://web.archive.org/thumb/${domain}?generate=1`
|
|
66
|
+
: nothing;
|
|
67
|
+
} catch (err) {
|
|
68
|
+
return `${this.baseImageUrl}/images/notfound.png`;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
54
72
|
// Don't try to load invalid image URLs
|
|
55
73
|
return this.baseImageUrl && this.model?.identifier
|
|
56
74
|
? `${this.baseImageUrl}/services/img/${this.model.identifier}`
|
|
57
75
|
: nothing;
|
|
58
76
|
}
|
|
59
77
|
|
|
78
|
+
private get notFoundSrc() {
|
|
79
|
+
return this.baseImageUrl
|
|
80
|
+
? `${this.baseImageUrl}/images/notfound.png`
|
|
81
|
+
: nothing;
|
|
82
|
+
}
|
|
83
|
+
|
|
60
84
|
private get hashBasedGradient() {
|
|
61
85
|
if (!this.model?.identifier) {
|
|
62
86
|
return 'waveform-grad0';
|
|
@@ -120,6 +144,10 @@ export class ItemImage extends LitElement {
|
|
|
120
144
|
}
|
|
121
145
|
}
|
|
122
146
|
|
|
147
|
+
private onError() {
|
|
148
|
+
this.isNotFound = true;
|
|
149
|
+
}
|
|
150
|
+
|
|
123
151
|
/**
|
|
124
152
|
* CSS
|
|
125
153
|
*/
|