@internetarchive/collection-browser 3.0.1 → 3.0.2-alpha-webdev7960.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.
@@ -1,282 +1,282 @@
1
- import {
2
- css,
3
- html,
4
- LitElement,
5
- TemplateResult,
6
- CSSResultGroup,
7
- nothing,
8
- } from 'lit';
9
- import { customElement, property } from 'lit/decorators.js';
10
- import eyeIcon from '../assets/img/icons/eye';
11
- import eyeClosedIcon from '../assets/img/icons/eye-closed';
12
- import type {
13
- FacetOption,
14
- FacetBucket,
15
- FacetEventDetails,
16
- FacetState,
17
- } from '../models';
18
- import type { CollectionTitles } from '../data-source/models';
19
-
20
- @customElement('facet-row')
21
- export class FacetRow extends LitElement {
22
- //
23
- // UI STATE
24
- //
25
-
26
- /** The name of the facet group to which this facet belongs (e.g., "mediatype") */
27
- @property({ type: String }) facetType?: FacetOption;
28
-
29
- /** The facet bucket containing details about the state, count, and key for this row */
30
- @property({ type: Object }) bucket?: FacetBucket;
31
-
32
- /** The collection name cache for converting collection identifiers to titles */
33
- @property({ type: Object })
34
- collectionTitles?: CollectionTitles;
35
-
36
- //
37
- // COMPONENT LIFECYCLE METHODS
38
- //
39
-
40
- render() {
41
- return html`${this.facetRowTemplate}`;
42
- }
43
-
44
- //
45
- // TEMPLATE GETTERS
46
- //
47
-
48
- /**
49
- * Template for the full facet row, including the positive/negative checks,
50
- * the display name, and the count.
51
- */
52
- private get facetRowTemplate(): TemplateResult | typeof nothing {
53
- const { bucket, facetType } = this;
54
- if (!bucket || !facetType) return nothing;
55
-
56
- const showOnlyCheckboxId = `${facetType}:${bucket.key}-show-only`;
57
- const negativeCheckboxId = `${facetType}:${bucket.key}-negative`;
58
-
59
- const extraNoteSpan = bucket.extraNote
60
- ? html`<span class="facet-note">${bucket.extraNote}</span>`
61
- : nothing;
62
-
63
- // For collections, we render the collection title as a link.
64
- // For other facet types, we just have a static value to use.
65
- const bucketTextDisplay =
66
- facetType !== 'collection'
67
- ? html`${bucket.displayText ?? bucket.key} ${extraNoteSpan}`
68
- : html`<a href="/details/${bucket.key}">
69
- ${this.collectionTitles?.get(bucket.key) ?? bucket.key}
70
- </a> `;
71
-
72
- const facetHidden = bucket.state === 'hidden';
73
- const facetSelected = bucket.state === 'selected';
74
-
75
- const titleText = `${facetType}: ${bucket.displayText ?? bucket.key}`;
76
- const onlyShowText = facetSelected
77
- ? `Show all ${facetType}s`
78
- : `Only show ${titleText}`;
79
- const hideText = `Hide ${titleText}`;
80
- const unhideText = `Unhide ${titleText}`;
81
- const showHideText = facetHidden ? unhideText : hideText;
82
- const ariaLabel = `${titleText}, ${bucket.count} results`;
83
-
84
- // Added data-testid for Playwright testing
85
- return html`
86
- <div class="facet-row-container">
87
- <div class="facet-checkboxes">
88
- <input
89
- type="checkbox"
90
- .name=${facetType}
91
- .value=${bucket.key}
92
- @click=${(e: Event) => {
93
- this.facetClicked(e, false);
94
- }}
95
- .checked=${facetSelected}
96
- class="select-facet-checkbox"
97
- title=${onlyShowText}
98
- id=${showOnlyCheckboxId}
99
- data-testid=${showOnlyCheckboxId}
100
- />
101
- <input
102
- type="checkbox"
103
- id=${negativeCheckboxId}
104
- .name=${facetType}
105
- .value=${bucket.key}
106
- @click=${(e: Event) => {
107
- this.facetClicked(e, true);
108
- }}
109
- .checked=${facetHidden}
110
- class="hide-facet-checkbox"
111
- />
112
- <label
113
- for=${negativeCheckboxId}
114
- class="hide-facet-icon${facetHidden ? ' active' : ''}"
115
- title=${showHideText}
116
- data-testid=${negativeCheckboxId}
117
- >
118
- <span class="eye">${eyeIcon}</span>
119
- <span class="eye-closed">${eyeClosedIcon}</span>
120
- </label>
121
- </div>
122
- <label
123
- for=${showOnlyCheckboxId}
124
- class="facet-info-display"
125
- title=${onlyShowText}
126
- aria-label=${ariaLabel}
127
- >
128
- <div class="facet-title">${bucketTextDisplay}</div>
129
- <div class="facet-count">${bucket.count.toLocaleString()}</div>
130
- </label>
131
- </div>
132
- `;
133
- }
134
-
135
- //
136
- // EVENT HANDLERS & DISPATCHERS
137
- //
138
-
139
- /**
140
- * Handler for whenever this facet is clicked & its state changes
141
- */
142
- private facetClicked(e: Event, negative: boolean) {
143
- const { bucket, facetType } = this;
144
- if (!bucket || !facetType) return;
145
-
146
- const target = e.target as HTMLInputElement;
147
- const { checked } = target;
148
- this.bucket = {
149
- ...bucket,
150
- state: FacetRow.getFacetState(checked, negative),
151
- };
152
-
153
- this.dispatchFacetClickEvent({
154
- facetType,
155
- bucket: this.bucket,
156
- negative,
157
- });
158
- }
159
-
160
- /**
161
- * Emits a `facetClick` event with details about this facet & its current state
162
- */
163
- private dispatchFacetClickEvent(detail: FacetEventDetails) {
164
- const event = new CustomEvent<FacetEventDetails>('facetClick', {
165
- detail,
166
- });
167
- this.dispatchEvent(event);
168
- }
169
-
170
- //
171
- // OTHER METHODS
172
- //
173
-
174
- /**
175
- * Returns the composed facet state corresponding to a positive or negative facet's checked state
176
- */
177
- static getFacetState(checked: boolean, negative: boolean): FacetState {
178
- let state: FacetState;
179
- if (checked) {
180
- state = negative ? 'hidden' : 'selected';
181
- } else {
182
- state = 'none';
183
- }
184
- return state;
185
- }
186
-
187
- //
188
- // STYLES
189
- //
190
-
191
- static get styles(): CSSResultGroup {
192
- const facetRowBorderTop = css`var(--facet-row-border-top, 1px solid transparent)`;
193
- const facetRowBorderBottom = css`var(--facet-row-border-bottom, 1px solid transparent)`;
194
-
195
- return css`
196
- async-collection-name {
197
- display: contents;
198
- }
199
- .facet-checkboxes {
200
- margin: 0 5px 0 0;
201
- display: flex;
202
- height: 15px;
203
- }
204
- .facet-checkboxes input:first-child {
205
- margin-right: 5px;
206
- }
207
- .facet-checkboxes input {
208
- height: 15px;
209
- width: 15px;
210
- margin: 0;
211
- }
212
- .facet-row-container {
213
- display: flex;
214
- font-weight: 500;
215
- font-size: 1.2rem;
216
- margin: 2.5px auto;
217
- height: auto;
218
- border-top: ${facetRowBorderTop};
219
- border-bottom: ${facetRowBorderBottom};
220
- overflow: hidden;
221
- }
222
- .facet-info-display {
223
- display: flex;
224
- flex: 1 1 0%;
225
- cursor: pointer;
226
- flex-wrap: wrap;
227
- }
228
- .facet-title {
229
- word-break: break-word;
230
- display: inline-block;
231
- flex: 1 1 0%;
232
- }
233
- .facet-note {
234
- color: #bbb;
235
- }
236
- .facet-count {
237
- text-align: right;
238
- }
239
- .select-facet-checkbox {
240
- cursor: pointer;
241
- display: inline-block;
242
- }
243
- .hide-facet-checkbox {
244
- display: none;
245
- }
246
- .hide-facet-icon {
247
- width: 15px;
248
- height: 15px;
249
- cursor: pointer;
250
- opacity: 0.3;
251
- display: inline-block;
252
- }
253
- .hide-facet-icon:hover,
254
- .active {
255
- opacity: 1;
256
- }
257
- .hide-facet-icon:hover .eye,
258
- .hide-facet-icon .eye-closed {
259
- display: none;
260
- }
261
- .hide-facet-icon:hover .eye-closed,
262
- .hide-facet-icon.active .eye-closed {
263
- display: inline;
264
- }
265
- .hide-facet-icon.active .eye {
266
- display: none;
267
- }
268
- .sorting-icon {
269
- cursor: pointer;
270
- }
271
-
272
- a:link,
273
- a:visited {
274
- text-decoration: none;
275
- color: var(--ia-theme-link-color, #4b64ff);
276
- }
277
- a:hover {
278
- text-decoration: underline;
279
- }
280
- `;
281
- }
282
- }
1
+ import {
2
+ css,
3
+ html,
4
+ LitElement,
5
+ TemplateResult,
6
+ CSSResultGroup,
7
+ nothing,
8
+ } from 'lit';
9
+ import { customElement, property } from 'lit/decorators.js';
10
+ import eyeIcon from '../assets/img/icons/eye';
11
+ import eyeClosedIcon from '../assets/img/icons/eye-closed';
12
+ import type {
13
+ FacetOption,
14
+ FacetBucket,
15
+ FacetEventDetails,
16
+ FacetState,
17
+ } from '../models';
18
+ import type { CollectionTitles } from '../data-source/models';
19
+
20
+ @customElement('facet-row')
21
+ export class FacetRow extends LitElement {
22
+ //
23
+ // UI STATE
24
+ //
25
+
26
+ /** The name of the facet group to which this facet belongs (e.g., "mediatype") */
27
+ @property({ type: String }) facetType?: FacetOption;
28
+
29
+ /** The facet bucket containing details about the state, count, and key for this row */
30
+ @property({ type: Object }) bucket?: FacetBucket;
31
+
32
+ /** The collection name cache for converting collection identifiers to titles */
33
+ @property({ type: Object })
34
+ collectionTitles?: CollectionTitles;
35
+
36
+ //
37
+ // COMPONENT LIFECYCLE METHODS
38
+ //
39
+
40
+ render() {
41
+ return html`${this.facetRowTemplate}`;
42
+ }
43
+
44
+ //
45
+ // TEMPLATE GETTERS
46
+ //
47
+
48
+ /**
49
+ * Template for the full facet row, including the positive/negative checks,
50
+ * the display name, and the count.
51
+ */
52
+ private get facetRowTemplate(): TemplateResult | typeof nothing {
53
+ const { bucket, facetType } = this;
54
+ if (!bucket || !facetType) return nothing;
55
+
56
+ const showOnlyCheckboxId = `${facetType}:${bucket.key}-show-only`;
57
+ const negativeCheckboxId = `${facetType}:${bucket.key}-negative`;
58
+
59
+ const extraNoteSpan = bucket.extraNote
60
+ ? html`<span class="facet-note">${bucket.extraNote}</span>`
61
+ : nothing;
62
+
63
+ // For collections, we render the collection title as a link.
64
+ // For other facet types, we just have a static value to use.
65
+ const bucketTextDisplay =
66
+ facetType !== 'collection'
67
+ ? html`${bucket.displayText ?? bucket.key} ${extraNoteSpan}`
68
+ : html`<a href="/details/${bucket.key}">
69
+ ${this.collectionTitles?.get(bucket.key) ?? bucket.key}
70
+ </a> `;
71
+
72
+ const facetHidden = bucket.state === 'hidden';
73
+ const facetSelected = bucket.state === 'selected';
74
+
75
+ const titleText = `${facetType}: ${bucket.displayText ?? bucket.key}`;
76
+ const onlyShowText = facetSelected
77
+ ? `Show all ${facetType}s`
78
+ : `Only show ${titleText}`;
79
+ const hideText = `Hide ${titleText}`;
80
+ const unhideText = `Unhide ${titleText}`;
81
+ const showHideText = facetHidden ? unhideText : hideText;
82
+ const ariaLabel = `${titleText}, ${bucket.count} results`;
83
+
84
+ // Added data-testid for Playwright testing
85
+ return html`
86
+ <div class="facet-row-container">
87
+ <div class="facet-checkboxes">
88
+ <input
89
+ type="checkbox"
90
+ .name=${facetType}
91
+ .value=${bucket.key}
92
+ @click=${(e: Event) => {
93
+ this.facetClicked(e, false);
94
+ }}
95
+ .checked=${facetSelected}
96
+ class="select-facet-checkbox"
97
+ title=${onlyShowText}
98
+ id=${showOnlyCheckboxId}
99
+ data-testid=${showOnlyCheckboxId}
100
+ />
101
+ <input
102
+ type="checkbox"
103
+ id=${negativeCheckboxId}
104
+ .name=${facetType}
105
+ .value=${bucket.key}
106
+ @click=${(e: Event) => {
107
+ this.facetClicked(e, true);
108
+ }}
109
+ .checked=${facetHidden}
110
+ class="hide-facet-checkbox"
111
+ />
112
+ <label
113
+ for=${negativeCheckboxId}
114
+ class="hide-facet-icon${facetHidden ? ' active' : ''}"
115
+ title=${showHideText}
116
+ data-testid=${negativeCheckboxId}
117
+ >
118
+ <span class="eye">${eyeIcon}</span>
119
+ <span class="eye-closed">${eyeClosedIcon}</span>
120
+ </label>
121
+ </div>
122
+ <label
123
+ for=${showOnlyCheckboxId}
124
+ class="facet-info-display"
125
+ title=${onlyShowText}
126
+ aria-label=${ariaLabel}
127
+ >
128
+ <div class="facet-title">${bucketTextDisplay}</div>
129
+ <div class="facet-count">${bucket.count.toLocaleString()}</div>
130
+ </label>
131
+ </div>
132
+ `;
133
+ }
134
+
135
+ //
136
+ // EVENT HANDLERS & DISPATCHERS
137
+ //
138
+
139
+ /**
140
+ * Handler for whenever this facet is clicked & its state changes
141
+ */
142
+ private facetClicked(e: Event, negative: boolean) {
143
+ const { bucket, facetType } = this;
144
+ if (!bucket || !facetType) return;
145
+
146
+ const target = e.target as HTMLInputElement;
147
+ const { checked } = target;
148
+ this.bucket = {
149
+ ...bucket,
150
+ state: FacetRow.getFacetState(checked, negative),
151
+ };
152
+
153
+ this.dispatchFacetClickEvent({
154
+ facetType,
155
+ bucket: this.bucket,
156
+ negative,
157
+ });
158
+ }
159
+
160
+ /**
161
+ * Emits a `facetClick` event with details about this facet & its current state
162
+ */
163
+ private dispatchFacetClickEvent(detail: FacetEventDetails) {
164
+ const event = new CustomEvent<FacetEventDetails>('facetClick', {
165
+ detail,
166
+ });
167
+ this.dispatchEvent(event);
168
+ }
169
+
170
+ //
171
+ // OTHER METHODS
172
+ //
173
+
174
+ /**
175
+ * Returns the composed facet state corresponding to a positive or negative facet's checked state
176
+ */
177
+ static getFacetState(checked: boolean, negative: boolean): FacetState {
178
+ let state: FacetState;
179
+ if (checked) {
180
+ state = negative ? 'hidden' : 'selected';
181
+ } else {
182
+ state = 'none';
183
+ }
184
+ return state;
185
+ }
186
+
187
+ //
188
+ // STYLES
189
+ //
190
+
191
+ static get styles(): CSSResultGroup {
192
+ const facetRowBorderTop = css`var(--facet-row-border-top, 1px solid transparent)`;
193
+ const facetRowBorderBottom = css`var(--facet-row-border-bottom, 1px solid transparent)`;
194
+
195
+ return css`
196
+ async-collection-name {
197
+ display: contents;
198
+ }
199
+ .facet-checkboxes {
200
+ margin: 0 5px 0 0;
201
+ display: flex;
202
+ height: 15px;
203
+ }
204
+ .facet-checkboxes input:first-child {
205
+ margin-right: 5px;
206
+ }
207
+ .facet-checkboxes input {
208
+ height: 15px;
209
+ width: 15px;
210
+ margin: 0;
211
+ }
212
+ .facet-row-container {
213
+ display: flex;
214
+ font-weight: 500;
215
+ font-size: 1.2rem;
216
+ margin: 2.5px auto;
217
+ height: auto;
218
+ border-top: ${facetRowBorderTop};
219
+ border-bottom: ${facetRowBorderBottom};
220
+ overflow: hidden;
221
+ }
222
+ .facet-info-display {
223
+ display: flex;
224
+ flex: 1 1 0%;
225
+ cursor: pointer;
226
+ flex-wrap: wrap;
227
+ }
228
+ .facet-title {
229
+ word-break: break-word;
230
+ display: inline-block;
231
+ flex: 1 1 0%;
232
+ }
233
+ .facet-note {
234
+ color: #bbb;
235
+ }
236
+ .facet-count {
237
+ text-align: right;
238
+ }
239
+ .select-facet-checkbox {
240
+ cursor: pointer;
241
+ display: inline-block;
242
+ }
243
+ .hide-facet-checkbox {
244
+ display: none;
245
+ }
246
+ .hide-facet-icon {
247
+ width: 15px;
248
+ height: 15px;
249
+ cursor: pointer;
250
+ opacity: 0.3;
251
+ display: inline-block;
252
+ }
253
+ .hide-facet-icon:hover,
254
+ .active {
255
+ opacity: 1;
256
+ }
257
+ .hide-facet-icon:hover .eye,
258
+ .hide-facet-icon .eye-closed {
259
+ display: none;
260
+ }
261
+ .hide-facet-icon:hover .eye-closed,
262
+ .hide-facet-icon.active .eye-closed {
263
+ display: inline;
264
+ }
265
+ .hide-facet-icon.active .eye {
266
+ display: none;
267
+ }
268
+ .sorting-icon {
269
+ cursor: pointer;
270
+ }
271
+
272
+ a:link,
273
+ a:visited {
274
+ text-decoration: none;
275
+ color: var(--ia-theme-link-color, #4b64ff);
276
+ }
277
+ a:hover {
278
+ text-decoration: underline;
279
+ }
280
+ `;
281
+ }
282
+ }