@internetarchive/collection-browser 0.0.1-alpha.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.
- package/.editorconfig +29 -0
- package/.github/workflows/ci.yml +26 -0
- package/.husky/pre-commit +4 -0
- package/LICENSE +661 -0
- package/README.md +68 -0
- package/demo/app-root.ts +414 -0
- package/demo/index.html +26 -0
- package/dist/demo/app-root.d.ts +43 -0
- package/dist/demo/app-root.js +385 -0
- package/dist/demo/app-root.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/src/assets/img/icons/audio.d.ts +1 -0
- package/dist/src/assets/img/icons/audio.js +9 -0
- package/dist/src/assets/img/icons/audio.js.map +1 -0
- package/dist/src/assets/img/icons/collection.d.ts +1 -0
- package/dist/src/assets/img/icons/collection.js +9 -0
- package/dist/src/assets/img/icons/collection.js.map +1 -0
- package/dist/src/assets/img/icons/etree.d.ts +1 -0
- package/dist/src/assets/img/icons/etree.js +9 -0
- package/dist/src/assets/img/icons/etree.js.map +1 -0
- package/dist/src/assets/img/icons/images.d.ts +1 -0
- package/dist/src/assets/img/icons/images.js +10 -0
- package/dist/src/assets/img/icons/images.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/account.d.ts +2 -0
- package/dist/src/assets/img/icons/mediatype/account.js +12 -0
- package/dist/src/assets/img/icons/mediatype/account.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/audio.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/audio.js +11 -0
- package/dist/src/assets/img/icons/mediatype/audio.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/collection.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/collection.js +9 -0
- package/dist/src/assets/img/icons/mediatype/collection.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/etree copy.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/etree copy.js +9 -0
- package/dist/src/assets/img/icons/mediatype/etree copy.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/etree.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/etree.js +9 -0
- package/dist/src/assets/img/icons/mediatype/etree.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/film.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/film.js +13 -0
- package/dist/src/assets/img/icons/mediatype/film.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/images.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/images.js +10 -0
- package/dist/src/assets/img/icons/mediatype/images.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/livemusic.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/livemusic.js +7 -0
- package/dist/src/assets/img/icons/mediatype/livemusic.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/photos.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/photos.js +7 -0
- package/dist/src/assets/img/icons/mediatype/photos.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/software.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/software.js +10 -0
- package/dist/src/assets/img/icons/mediatype/software.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/texts.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/texts.js +10 -0
- package/dist/src/assets/img/icons/mediatype/texts.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/tv.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/tv.js +9 -0
- package/dist/src/assets/img/icons/mediatype/tv.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/video.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/video.js +10 -0
- package/dist/src/assets/img/icons/mediatype/video.js.map +1 -0
- package/dist/src/assets/img/icons/mediatype/web.d.ts +1 -0
- package/dist/src/assets/img/icons/mediatype/web.js +10 -0
- package/dist/src/assets/img/icons/mediatype/web.js.map +1 -0
- package/dist/src/assets/img/icons/software.d.ts +1 -0
- package/dist/src/assets/img/icons/software.js +10 -0
- package/dist/src/assets/img/icons/software.js.map +1 -0
- package/dist/src/assets/img/icons/texts.d.ts +1 -0
- package/dist/src/assets/img/icons/texts.js +10 -0
- package/dist/src/assets/img/icons/texts.js.map +1 -0
- package/dist/src/assets/img/icons/tv.d.ts +1 -0
- package/dist/src/assets/img/icons/tv.js +9 -0
- package/dist/src/assets/img/icons/tv.js.map +1 -0
- package/dist/src/assets/img/icons/video.d.ts +1 -0
- package/dist/src/assets/img/icons/video.js +10 -0
- package/dist/src/assets/img/icons/video.js.map +1 -0
- package/dist/src/assets/img/icons/web.d.ts +1 -0
- package/dist/src/assets/img/icons/web.js +10 -0
- package/dist/src/assets/img/icons/web.js.map +1 -0
- package/dist/src/circular-activity-indicator.d.ts +5 -0
- package/dist/src/circular-activity-indicator.js +66 -0
- package/dist/src/circular-activity-indicator.js.map +1 -0
- package/dist/src/collection-browser.d.ts +151 -0
- package/dist/src/collection-browser.js +697 -0
- package/dist/src/collection-browser.js.map +1 -0
- package/dist/src/collection-facets.d.ts +34 -0
- package/dist/src/collection-facets.js +245 -0
- package/dist/src/collection-facets.js.map +1 -0
- package/dist/src/mediatype-icon.d.ts +9 -0
- package/dist/src/mediatype-icon.js +89 -0
- package/dist/src/mediatype-icon.js.map +1 -0
- package/dist/src/models.d.ts +23 -0
- package/dist/src/models.js +2 -0
- package/dist/src/models.js.map +1 -0
- package/dist/src/sort-filter-bar/alpha-bar.d.ts +10 -0
- package/dist/src/sort-filter-bar/alpha-bar.js +88 -0
- package/dist/src/sort-filter-bar/alpha-bar.js.map +1 -0
- package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +24 -0
- package/dist/src/sort-filter-bar/sort-filter-bar.js +257 -0
- package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -0
- package/dist/src/tiles/grid/account-tile.d.ts +7 -0
- package/dist/src/tiles/grid/account-tile.js +144 -0
- package/dist/src/tiles/grid/account-tile.js.map +1 -0
- package/dist/src/tiles/grid/collection-tile.d.ts +7 -0
- package/dist/src/tiles/grid/collection-tile.js +160 -0
- package/dist/src/tiles/grid/collection-tile.js.map +1 -0
- package/dist/src/tiles/grid/icons/account.d.ts +1 -0
- package/dist/src/tiles/grid/icons/account.js +12 -0
- package/dist/src/tiles/grid/icons/account.js.map +1 -0
- package/dist/src/tiles/grid/icons/favorite-filled.d.ts +1 -0
- package/dist/src/tiles/grid/icons/favorite-filled.js +11 -0
- package/dist/src/tiles/grid/icons/favorite-filled.js.map +1 -0
- package/dist/src/tiles/grid/icons/reviews.d.ts +1 -0
- package/dist/src/tiles/grid/icons/reviews.js +11 -0
- package/dist/src/tiles/grid/icons/reviews.js.map +1 -0
- package/dist/src/tiles/grid/icons/upload.d.ts +1 -0
- package/dist/src/tiles/grid/icons/upload.js +12 -0
- package/dist/src/tiles/grid/icons/upload.js.map +1 -0
- package/dist/src/tiles/grid/icons/views.d.ts +2 -0
- package/dist/src/tiles/grid/icons/views.js +11 -0
- package/dist/src/tiles/grid/icons/views.js.map +1 -0
- package/dist/src/tiles/grid/item-tile.d.ts +9 -0
- package/dist/src/tiles/grid/item-tile.js +240 -0
- package/dist/src/tiles/grid/item-tile.js.map +1 -0
- package/dist/src/tiles/list/tile-list-compact.d.ts +16 -0
- package/dist/src/tiles/list/tile-list-compact.js +125 -0
- package/dist/src/tiles/list/tile-list-compact.js.map +1 -0
- package/dist/src/tiles/list/tile-list-detail.d.ts +17 -0
- package/dist/src/tiles/list/tile-list-detail.js +181 -0
- package/dist/src/tiles/list/tile-list-detail.js.map +1 -0
- package/dist/src/tiles/list/tile-list.d.ts +21 -0
- package/dist/src/tiles/list/tile-list.js +229 -0
- package/dist/src/tiles/list/tile-list.js.map +1 -0
- package/dist/src/tiles/loading-tile.d.ts +5 -0
- package/dist/src/tiles/loading-tile.js +73 -0
- package/dist/src/tiles/loading-tile.js.map +1 -0
- package/dist/src/tiles/tile-dispatcher.d.ts +27 -0
- package/dist/src/tiles/tile-dispatcher.js +160 -0
- package/dist/src/tiles/tile-dispatcher.js.map +1 -0
- package/dist/src/utils/format-count.d.ts +7 -0
- package/dist/src/utils/format-count.js +76 -0
- package/dist/src/utils/format-count.js.map +1 -0
- package/dist/src/utils/format-date.d.ts +2 -0
- package/dist/src/utils/format-date.js +24 -0
- package/dist/src/utils/format-date.js.map +1 -0
- package/dist/src/utils/format-string.d.ts +2 -0
- package/dist/src/utils/format-string.js +7 -0
- package/dist/src/utils/format-string.js.map +1 -0
- package/dist/test/collection-browser.test.d.ts +0 -0
- package/dist/test/collection-browser.test.js +3 -0
- package/dist/test/collection-browser.test.js.map +1 -0
- package/dist/test/utils/format-count.test.d.ts +1 -0
- package/dist/test/utils/format-count.test.js +24 -0
- package/dist/test/utils/format-count.test.js.map +1 -0
- package/dist/test/utils/format-date.test.d.ts +1 -0
- package/dist/test/utils/format-date.test.js +18 -0
- package/dist/test/utils/format-date.test.js.map +1 -0
- package/dist/test/utils/format-string.test.d.ts +1 -0
- package/dist/test/utils/format-string.test.js +17 -0
- package/dist/test/utils/format-string.test.js.map +1 -0
- package/index.ts +3 -0
- package/package.json +95 -0
- package/src/assets/img/icons/mediatype/account.ts +12 -0
- package/src/assets/img/icons/mediatype/audio.ts +11 -0
- package/src/assets/img/icons/mediatype/collection.ts +9 -0
- package/src/assets/img/icons/mediatype/etree.ts +9 -0
- package/src/assets/img/icons/mediatype/film.ts +13 -0
- package/src/assets/img/icons/mediatype/foo.svg +5 -0
- package/src/assets/img/icons/mediatype/images.ts +10 -0
- package/src/assets/img/icons/mediatype/software.ts +10 -0
- package/src/assets/img/icons/mediatype/texts.ts +10 -0
- package/src/assets/img/icons/mediatype/tv.ts +9 -0
- package/src/assets/img/icons/mediatype/video.ts +10 -0
- package/src/assets/img/icons/mediatype/web.ts +10 -0
- package/src/circular-activity-indicator.ts +64 -0
- package/src/collection-browser.ts +756 -0
- package/src/collection-facets.ts +285 -0
- package/src/mediatype-icon.ts +83 -0
- package/src/models.ts +25 -0
- package/src/sort-filter-bar/alpha-bar.ts +86 -0
- package/src/sort-filter-bar/sort-filter-bar.ts +256 -0
- package/src/tiles/grid/account-tile.ts +141 -0
- package/src/tiles/grid/collection-tile.ts +157 -0
- package/src/tiles/grid/icons/account.ts +12 -0
- package/src/tiles/grid/icons/favorite-filled.ts +11 -0
- package/src/tiles/grid/icons/reviews.ts +11 -0
- package/src/tiles/grid/icons/upload.ts +12 -0
- package/src/tiles/grid/icons/views.ts +11 -0
- package/src/tiles/grid/item-tile.ts +254 -0
- package/src/tiles/list/tile-list.ts +227 -0
- package/src/tiles/loading-tile.ts +70 -0
- package/src/tiles/tile-dispatcher.ts +160 -0
- package/src/utils/format-count.ts +95 -0
- package/src/utils/format-date.ts +36 -0
- package/test/collection-browser.test.ts +1 -0
- package/test/utils/format-count.test.ts +32 -0
- package/test/utils/format-date.test.ts +22 -0
- package/tsconfig.json +20 -0
- package/web-dev-server.config.mjs +28 -0
- package/web-test-runner.config.mjs +41 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { css, html, LitElement } from 'lit';
|
|
2
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
3
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
4
|
+
import { Aggregation } from '@internetarchive/search-service';
|
|
5
|
+
|
|
6
|
+
type FacetOption =
|
|
7
|
+
| 'subject'
|
|
8
|
+
| 'mediatype'
|
|
9
|
+
| 'language'
|
|
10
|
+
| 'creator'
|
|
11
|
+
| 'collection'
|
|
12
|
+
| 'year';
|
|
13
|
+
|
|
14
|
+
const facetDisplayOrder: FacetOption[] = [
|
|
15
|
+
'mediatype',
|
|
16
|
+
'year',
|
|
17
|
+
'subject',
|
|
18
|
+
'collection',
|
|
19
|
+
'creator',
|
|
20
|
+
'language',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const aggregationToFacetOption: Record<string, FacetOption> = {
|
|
24
|
+
subjectSorter: 'subject',
|
|
25
|
+
mediatypeSorter: 'mediatype',
|
|
26
|
+
languageSorter: 'language',
|
|
27
|
+
creatorSorter: 'creator',
|
|
28
|
+
collection: 'collection',
|
|
29
|
+
year: 'year',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const facetTitles: Record<FacetOption, string> = {
|
|
33
|
+
subject: 'Subject',
|
|
34
|
+
mediatype: 'Media Type',
|
|
35
|
+
language: 'Language',
|
|
36
|
+
creator: 'Creator',
|
|
37
|
+
collection: 'Collection',
|
|
38
|
+
year: 'Year',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
interface FacetBucket {
|
|
42
|
+
// for some facets, we augment the key with a display value
|
|
43
|
+
displayText?: string;
|
|
44
|
+
key: string;
|
|
45
|
+
count: number;
|
|
46
|
+
selected: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface FacetGroup {
|
|
50
|
+
title: string;
|
|
51
|
+
key: string;
|
|
52
|
+
buckets: FacetBucket[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@customElement('collection-facets')
|
|
56
|
+
export class CollectionFacets extends LitElement {
|
|
57
|
+
@property({ type: Object }) aggregations?: Record<string, Aggregation>;
|
|
58
|
+
|
|
59
|
+
@property({ type: Object }) selectedFacets: Record<string, string[]> = {};
|
|
60
|
+
|
|
61
|
+
private get hydratedSelectedFacets(): Record<string, string[]> {
|
|
62
|
+
const { selectedFacets } = this;
|
|
63
|
+
const hydratedSelectedFacets: Record<string, string[]> = {};
|
|
64
|
+
Object.entries(selectedFacets).forEach(([key]) => {
|
|
65
|
+
const values = hydratedSelectedFacets[key];
|
|
66
|
+
const title = facetTitles[key as FacetOption];
|
|
67
|
+
hydratedSelectedFacets[title] = values || [];
|
|
68
|
+
delete hydratedSelectedFacets[key];
|
|
69
|
+
});
|
|
70
|
+
return hydratedSelectedFacets;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
render() {
|
|
74
|
+
return html`
|
|
75
|
+
<h1>Facets</h1>
|
|
76
|
+
|
|
77
|
+
${this.mergedFacets.map(facetGroup => this.getFacetTemplate(facetGroup))}
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Combines the selected facets with the aggregations to create a single list of facets
|
|
83
|
+
*/
|
|
84
|
+
private get mergedFacets(): FacetGroup[] {
|
|
85
|
+
const facetGroups: FacetGroup[] = [];
|
|
86
|
+
|
|
87
|
+
facetDisplayOrder.forEach(facetKey => {
|
|
88
|
+
const selectedFacetGroup = this.selectedFacetGroups.find(
|
|
89
|
+
group => group.key === facetKey
|
|
90
|
+
);
|
|
91
|
+
const aggregateFacetGroup = this.aggregationFacetGroups.find(
|
|
92
|
+
group => group.key === facetKey
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// if the user selected a facet, but it's not in the aggregation, we add it as-is
|
|
96
|
+
if (selectedFacetGroup && !aggregateFacetGroup) {
|
|
97
|
+
facetGroups.push(selectedFacetGroup);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// if we don't have an aggregate facet group, don't add this to the list
|
|
102
|
+
if (!aggregateFacetGroup) return;
|
|
103
|
+
|
|
104
|
+
// start with either the selected group if we have one, or the aggregate group
|
|
105
|
+
const facetGroup = selectedFacetGroup ?? aggregateFacetGroup;
|
|
106
|
+
|
|
107
|
+
// attach the counts to the selected buckets
|
|
108
|
+
const bucketsWithCount =
|
|
109
|
+
selectedFacetGroup?.buckets.map(bucket => {
|
|
110
|
+
const selectedBucket = aggregateFacetGroup.buckets.find(
|
|
111
|
+
b => b.key === bucket.key
|
|
112
|
+
);
|
|
113
|
+
if (selectedBucket) {
|
|
114
|
+
return {
|
|
115
|
+
...bucket,
|
|
116
|
+
count: selectedBucket.count,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return bucket;
|
|
120
|
+
}) ?? [];
|
|
121
|
+
|
|
122
|
+
// append any additional buckets that were not selected
|
|
123
|
+
aggregateFacetGroup.buckets.forEach(bucket => {
|
|
124
|
+
const existingBucket = bucketsWithCount.find(b => b.key === bucket.key);
|
|
125
|
+
if (existingBucket) return;
|
|
126
|
+
bucketsWithCount.push(bucket);
|
|
127
|
+
});
|
|
128
|
+
facetGroup.buckets = bucketsWithCount.splice(0, 5);
|
|
129
|
+
|
|
130
|
+
if (facetGroup.buckets.length === 0) return;
|
|
131
|
+
|
|
132
|
+
facetGroups.push(facetGroup);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return facetGroups;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Converts the raw `selectedFacets` to `FacetGroups`, which are easier to use
|
|
140
|
+
*/
|
|
141
|
+
private get selectedFacetGroups(): FacetGroup[] {
|
|
142
|
+
const selectedFacetGroups: FacetGroup[] = [];
|
|
143
|
+
Object.entries(this.selectedFacets).forEach(([key, buckets]) => {
|
|
144
|
+
const title = facetTitles[key as FacetOption];
|
|
145
|
+
const group = {
|
|
146
|
+
title,
|
|
147
|
+
key,
|
|
148
|
+
buckets: buckets.map(bucket => ({
|
|
149
|
+
key: bucket,
|
|
150
|
+
count: 0,
|
|
151
|
+
selected: true,
|
|
152
|
+
})),
|
|
153
|
+
};
|
|
154
|
+
selectedFacetGroups.push(group);
|
|
155
|
+
});
|
|
156
|
+
return selectedFacetGroups;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Converts the raw `aggregations` to `FacetGroups`, which are easier to use
|
|
161
|
+
*/
|
|
162
|
+
private get aggregationFacetGroups(): FacetGroup[] {
|
|
163
|
+
const facetGroups: FacetGroup[] = [];
|
|
164
|
+
Object.entries(this.aggregations ?? []).forEach(([key, buckets]) => {
|
|
165
|
+
if (key === 'year_histogram') return;
|
|
166
|
+
const option = this.getFacetOptionFromKey(key);
|
|
167
|
+
const title = facetTitles[option];
|
|
168
|
+
const facetBuckets: FacetBucket[] = buckets.buckets.map(bucket => ({
|
|
169
|
+
key: `${bucket.key}`,
|
|
170
|
+
count: bucket.doc_count,
|
|
171
|
+
selected: false,
|
|
172
|
+
}));
|
|
173
|
+
const group: FacetGroup = {
|
|
174
|
+
title,
|
|
175
|
+
key: option,
|
|
176
|
+
buckets: facetBuckets,
|
|
177
|
+
};
|
|
178
|
+
facetGroups.push(group);
|
|
179
|
+
});
|
|
180
|
+
return facetGroups;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private getFacetTemplate(facetGroup: FacetGroup) {
|
|
184
|
+
return html`
|
|
185
|
+
<h2>${facetGroup.title}</h2>
|
|
186
|
+
${repeat(
|
|
187
|
+
facetGroup.buckets,
|
|
188
|
+
bucket => `${facetGroup.key}:${bucket.key}`,
|
|
189
|
+
bucket => html`
|
|
190
|
+
<label class="facet-row">
|
|
191
|
+
<div class="facet-checkbox">
|
|
192
|
+
<input
|
|
193
|
+
type="checkbox"
|
|
194
|
+
.name=${facetGroup.key}
|
|
195
|
+
.value=${bucket.key}
|
|
196
|
+
@click=${this.facetToggled}
|
|
197
|
+
?checked=${bucket.selected}
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
<div class="facet-title">${bucket.key}</div>
|
|
201
|
+
<div class="facet-count">${bucket.count}</div>
|
|
202
|
+
</label>
|
|
203
|
+
`
|
|
204
|
+
)}
|
|
205
|
+
`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private facetChecked(name: string, value: string) {
|
|
209
|
+
const { selectedFacets } = this;
|
|
210
|
+
const facetClone = { ...selectedFacets };
|
|
211
|
+
const currentFacetValues = facetClone[name];
|
|
212
|
+
if (currentFacetValues) {
|
|
213
|
+
currentFacetValues.push(value);
|
|
214
|
+
facetClone[name] = currentFacetValues;
|
|
215
|
+
} else {
|
|
216
|
+
facetClone[name] = [value];
|
|
217
|
+
}
|
|
218
|
+
this.selectedFacets = facetClone;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private facetUnchecked(name: string, value: string) {
|
|
222
|
+
const { selectedFacets } = this;
|
|
223
|
+
const facetClone = { ...selectedFacets };
|
|
224
|
+
let currentFacetValues = selectedFacets[name];
|
|
225
|
+
if (currentFacetValues) {
|
|
226
|
+
currentFacetValues = currentFacetValues.filter(el => el !== value);
|
|
227
|
+
facetClone[name] = currentFacetValues;
|
|
228
|
+
if (currentFacetValues.length === 0) {
|
|
229
|
+
delete facetClone[name];
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
this.selectedFacets = facetClone;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private facetToggled(e: Event) {
|
|
236
|
+
const target = e.target as HTMLInputElement;
|
|
237
|
+
const { checked, name, value } = target;
|
|
238
|
+
if (checked) {
|
|
239
|
+
this.facetChecked(name, value);
|
|
240
|
+
} else {
|
|
241
|
+
this.facetUnchecked(name, value);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const event = new CustomEvent<Record<string, string[]>>('facetsChanged', {
|
|
245
|
+
detail: this.selectedFacets,
|
|
246
|
+
});
|
|
247
|
+
this.dispatchEvent(event);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Parse the aggregate key title into the human readable title
|
|
252
|
+
*
|
|
253
|
+
* Example: user_aggs__terms__field:mediatypeSorter__size:6 => Media Type
|
|
254
|
+
*
|
|
255
|
+
* @param key
|
|
256
|
+
* @returns
|
|
257
|
+
*/
|
|
258
|
+
private getFacetOptionFromKey(key: string): FacetOption {
|
|
259
|
+
const parts = key.split('__');
|
|
260
|
+
const fieldNamePart = parts[2];
|
|
261
|
+
const fieldName = fieldNamePart.split(':')[1];
|
|
262
|
+
const facetMatch = Object.entries(aggregationToFacetOption).find(([key2]) =>
|
|
263
|
+
fieldName.includes(key2)
|
|
264
|
+
);
|
|
265
|
+
const option = facetMatch?.[1];
|
|
266
|
+
if (!option) throw new Error(`Could not find facet option for key: ${key}`);
|
|
267
|
+
return option;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
static get styles() {
|
|
271
|
+
return css`
|
|
272
|
+
.facet-row {
|
|
273
|
+
display: flex;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.facet-title {
|
|
277
|
+
flex: 1;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.facet-count {
|
|
281
|
+
margin-left: 0.5rem;
|
|
282
|
+
}
|
|
283
|
+
`;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { css, CSSResultGroup, html, LitElement } from 'lit';
|
|
2
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
3
|
+
|
|
4
|
+
import { audioIcon } from './assets/img/icons/mediatype/audio';
|
|
5
|
+
import { etreeIcon } from './assets/img/icons/mediatype/etree';
|
|
6
|
+
import { imagesIcon } from './assets/img/icons/mediatype/images';
|
|
7
|
+
import { filmIcon } from './assets/img/icons/mediatype/film';
|
|
8
|
+
import { softwareIcon } from './assets/img/icons/mediatype/software';
|
|
9
|
+
import { textsIcon } from './assets/img/icons/mediatype/texts';
|
|
10
|
+
import { tvIcon } from './assets/img/icons/mediatype/tv';
|
|
11
|
+
import { videoIcon } from './assets/img/icons/mediatype/video';
|
|
12
|
+
import { webIcon } from './assets/img/icons/mediatype/web';
|
|
13
|
+
import { collectionIcon } from './assets/img/icons/mediatype/collection';
|
|
14
|
+
|
|
15
|
+
@customElement('mediatype-icon')
|
|
16
|
+
export class MediatypeIcon extends LitElement {
|
|
17
|
+
@property({ type: String }) mediatype: string | undefined;
|
|
18
|
+
|
|
19
|
+
@property({ type: Boolean }) showText = false;
|
|
20
|
+
|
|
21
|
+
private readonly mediatypeIcons: { [key: string]: any } = {
|
|
22
|
+
audio: audioIcon,
|
|
23
|
+
collection: collectionIcon,
|
|
24
|
+
data: etreeIcon,
|
|
25
|
+
etree: etreeIcon,
|
|
26
|
+
film: filmIcon,
|
|
27
|
+
image: imagesIcon,
|
|
28
|
+
movies: filmIcon,
|
|
29
|
+
software: softwareIcon,
|
|
30
|
+
texts: textsIcon,
|
|
31
|
+
tv: tvIcon,
|
|
32
|
+
video: videoIcon,
|
|
33
|
+
web: webIcon,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
private readonly mediatypeText: { [key: string]: any } = {
|
|
37
|
+
audio: 'Audio',
|
|
38
|
+
collection: 'Collection',
|
|
39
|
+
data: 'Data',
|
|
40
|
+
etree: 'E-tree',
|
|
41
|
+
film: 'Film',
|
|
42
|
+
image: 'Image',
|
|
43
|
+
movies: 'Movie',
|
|
44
|
+
software: 'Software',
|
|
45
|
+
texts: 'Text',
|
|
46
|
+
tv: 'TV',
|
|
47
|
+
video: 'Video',
|
|
48
|
+
web: 'Web',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
render() {
|
|
52
|
+
if (!this.mediatype) {
|
|
53
|
+
return html``;
|
|
54
|
+
}
|
|
55
|
+
return html`
|
|
56
|
+
<div id="icon" class="${this.showText ? 'show-text' : 'hide-text'}">
|
|
57
|
+
${this.mediatypeIcons[this.mediatype]}
|
|
58
|
+
<p class="status-text">${this.mediatypeText[this.mediatype]}</p>
|
|
59
|
+
</div>
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static get styles(): CSSResultGroup {
|
|
64
|
+
return css`
|
|
65
|
+
.status-text {
|
|
66
|
+
font-size: 14px;
|
|
67
|
+
color: #2c2c2c;
|
|
68
|
+
margin: auto;
|
|
69
|
+
display: block;
|
|
70
|
+
text-align: center;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#icon.hide-text p {
|
|
74
|
+
display: none;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#icon.show-text svg {
|
|
78
|
+
height: 10px;
|
|
79
|
+
width: 10px;
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/models.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MediaType } from '@internetarchive/field-parsers';
|
|
2
|
+
|
|
3
|
+
export interface TileModel {
|
|
4
|
+
identifier: string;
|
|
5
|
+
title: string;
|
|
6
|
+
averageRating?: number;
|
|
7
|
+
dateAdded?: Date; // Date added to public search (software-defined) [from: addeddate]
|
|
8
|
+
dateArchived?: Date; // Date archived (software-defined) item created on archive.org [from: publicdate]
|
|
9
|
+
dateReviewed?: Date; // Date reviewed (user-created) most recent review [from: reviewdate]
|
|
10
|
+
datePublished?: Date; // Date work published in the world (user-defined) [from: date]
|
|
11
|
+
mediatype: MediaType;
|
|
12
|
+
viewCount: number;
|
|
13
|
+
itemCount: number;
|
|
14
|
+
favCount: number;
|
|
15
|
+
commentCount: number;
|
|
16
|
+
description?: string;
|
|
17
|
+
collectionIdentifier?: string;
|
|
18
|
+
collectionName?: string;
|
|
19
|
+
creator?: string;
|
|
20
|
+
subject?: string;
|
|
21
|
+
source?: string;
|
|
22
|
+
collection: string[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type CollectionDisplayMode = 'grid' | 'list-compact' | 'list-detail';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { LitElement, html, css, nothing } from 'lit';
|
|
2
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
3
|
+
|
|
4
|
+
@customElement('alpha-bar')
|
|
5
|
+
export class AlphaBar extends LitElement {
|
|
6
|
+
@property({ type: String }) headline?: string;
|
|
7
|
+
|
|
8
|
+
@property({ type: String }) selectedLetter?: string;
|
|
9
|
+
|
|
10
|
+
private get selectedUppercaseLetter(): string | undefined {
|
|
11
|
+
return this.selectedLetter?.toUpperCase();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
render() {
|
|
15
|
+
return html`
|
|
16
|
+
<h1>${this.headline}</h1>
|
|
17
|
+
<div id="container">
|
|
18
|
+
<ul>
|
|
19
|
+
${this.alphabet.map(
|
|
20
|
+
letter =>
|
|
21
|
+
html`
|
|
22
|
+
<li
|
|
23
|
+
class=${letter === this.selectedUppercaseLetter
|
|
24
|
+
? 'selected'
|
|
25
|
+
: nothing}
|
|
26
|
+
>
|
|
27
|
+
<a href="#" @click=${() => this.letterClicked(letter)}>
|
|
28
|
+
${letter}
|
|
29
|
+
</a>
|
|
30
|
+
</li>
|
|
31
|
+
`
|
|
32
|
+
)}
|
|
33
|
+
</ul>
|
|
34
|
+
</div>
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private letterClicked(letter: string) {
|
|
39
|
+
if (letter === this.selectedUppercaseLetter) {
|
|
40
|
+
this.selectedLetter = undefined;
|
|
41
|
+
} else {
|
|
42
|
+
this.selectedLetter = letter;
|
|
43
|
+
}
|
|
44
|
+
this.dispatchEvent(
|
|
45
|
+
new CustomEvent('letterChanged', {
|
|
46
|
+
detail: { selectedLetter: this.selectedUppercaseLetter },
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private readonly alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
|
|
52
|
+
|
|
53
|
+
static styles = css`
|
|
54
|
+
h1 {
|
|
55
|
+
font-size: 1.2rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#container {
|
|
59
|
+
background-color: #ddd;
|
|
60
|
+
color: #333;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
ul {
|
|
64
|
+
list-style: none;
|
|
65
|
+
display: flex;
|
|
66
|
+
margin: 0;
|
|
67
|
+
padding: 0.5rem 1rem;
|
|
68
|
+
justify-content: space-between;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
a {
|
|
72
|
+
color: #333;
|
|
73
|
+
text-decoration: none;
|
|
74
|
+
padding: 0.5rem 0.7rem;
|
|
75
|
+
display: block;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.selected {
|
|
79
|
+
background-color: darkgray;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.selected a {
|
|
83
|
+
color: white;
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
86
|
+
}
|