@internetarchive/collection-browser 0.0.1-alpha.9 → 0.1.2

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 (227) hide show
  1. package/README.md +8 -11
  2. package/demo/app-root.ts +17 -92
  3. package/dist/demo/app-root.d.ts +3 -5
  4. package/dist/demo/app-root.js +14 -83
  5. package/dist/demo/app-root.js.map +1 -1
  6. package/dist/index.d.ts +6 -0
  7. package/dist/index.js +6 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/src/assets/img/icons/chevron.d.ts +2 -0
  10. package/dist/src/assets/img/icons/chevron.js +4 -0
  11. package/dist/src/assets/img/icons/chevron.js.map +1 -0
  12. package/dist/src/assets/img/icons/mediatype/account.d.ts +1 -2
  13. package/dist/src/assets/img/icons/mediatype/account.js +6 -4
  14. package/dist/src/assets/img/icons/mediatype/account.js.map +1 -1
  15. package/dist/src/assets/img/icons/mediatype/audio.js +7 -4
  16. package/dist/src/assets/img/icons/mediatype/audio.js.map +1 -1
  17. package/dist/src/assets/img/icons/mediatype/collection.js +7 -4
  18. package/dist/src/assets/img/icons/mediatype/collection.js.map +1 -1
  19. package/dist/src/assets/img/icons/mediatype/data.d.ts +1 -0
  20. package/dist/src/assets/img/icons/mediatype/data.js +15 -0
  21. package/dist/src/assets/img/icons/mediatype/data.js.map +1 -0
  22. package/dist/src/assets/img/icons/mediatype/etree.js +10 -5
  23. package/dist/src/assets/img/icons/mediatype/etree.js.map +1 -1
  24. package/dist/src/assets/img/icons/mediatype/film.js +2 -1
  25. package/dist/src/assets/img/icons/mediatype/film.js.map +1 -1
  26. package/dist/src/assets/img/icons/mediatype/images.js +9 -6
  27. package/dist/src/assets/img/icons/mediatype/images.js.map +1 -1
  28. package/dist/src/assets/img/icons/mediatype/livemusic.d.ts +1 -0
  29. package/dist/src/assets/img/icons/mediatype/livemusic.js +7 -0
  30. package/dist/src/assets/img/icons/mediatype/livemusic.js.map +1 -0
  31. package/dist/src/assets/img/icons/mediatype/photos.d.ts +1 -0
  32. package/dist/src/assets/img/icons/mediatype/photos.js +7 -0
  33. package/dist/src/assets/img/icons/mediatype/photos.js.map +1 -0
  34. package/dist/src/assets/img/icons/mediatype/radio.d.ts +1 -0
  35. package/dist/src/assets/img/icons/mediatype/radio.js +15 -0
  36. package/dist/src/assets/img/icons/mediatype/radio.js.map +1 -0
  37. package/dist/src/assets/img/icons/mediatype/software.js +9 -6
  38. package/dist/src/assets/img/icons/mediatype/software.js.map +1 -1
  39. package/dist/src/assets/img/icons/mediatype/texts.js +9 -6
  40. package/dist/src/assets/img/icons/mediatype/texts.js.map +1 -1
  41. package/dist/src/assets/img/icons/mediatype/tv.js +10 -5
  42. package/dist/src/assets/img/icons/mediatype/tv.js.map +1 -1
  43. package/dist/src/assets/img/icons/mediatype/video.js +10 -6
  44. package/dist/src/assets/img/icons/mediatype/video.js.map +1 -1
  45. package/dist/src/assets/img/icons/mediatype/web.js +9 -6
  46. package/dist/src/assets/img/icons/mediatype/web.js.map +1 -1
  47. package/dist/src/collection-browser.d.ts +54 -31
  48. package/dist/src/collection-browser.js +508 -166
  49. package/dist/src/collection-browser.js.map +1 -1
  50. package/dist/src/collection-facets.d.ts +28 -13
  51. package/dist/src/collection-facets.js +276 -160
  52. package/dist/src/collection-facets.js.map +1 -1
  53. package/dist/src/helpers.d.ts +1 -0
  54. package/dist/src/helpers.js +20 -0
  55. package/dist/src/helpers.js.map +1 -0
  56. package/dist/src/language-code-handler/language-code-handler.d.ts +37 -0
  57. package/dist/src/language-code-handler/language-code-handler.js +27 -0
  58. package/dist/src/language-code-handler/language-code-handler.js.map +1 -0
  59. package/dist/src/language-code-handler/language-code-mapping.d.ts +1 -0
  60. package/dist/src/language-code-handler/language-code-mapping.js +563 -0
  61. package/dist/src/language-code-handler/language-code-mapping.js.map +1 -0
  62. package/dist/src/mediatype/mediatype-color.d.ts +3 -0
  63. package/dist/src/mediatype/mediatype-color.js +15 -0
  64. package/dist/src/mediatype/mediatype-color.js.map +1 -0
  65. package/dist/src/mediatype/mediatype-config.d.ts +3 -0
  66. package/dist/src/mediatype/mediatype-config.js +86 -0
  67. package/dist/src/mediatype/mediatype-config.js.map +1 -0
  68. package/dist/src/mediatype/mediatype-display.d.ts +3 -0
  69. package/dist/src/mediatype/mediatype-display.js +86 -0
  70. package/dist/src/mediatype/mediatype-display.js.map +1 -0
  71. package/dist/src/mediatype/mediatype-icon.d.ts +10 -0
  72. package/dist/src/mediatype/mediatype-icon.js +105 -0
  73. package/dist/src/mediatype/mediatype-icon.js.map +1 -0
  74. package/dist/src/mediatype/mediatype-text.d.ts +3 -0
  75. package/dist/src/mediatype/mediatype-text.js +17 -0
  76. package/dist/src/mediatype/mediatype-text.js.map +1 -0
  77. package/dist/src/mediatype/mediatypeConfig.d.ts +3 -0
  78. package/dist/src/mediatype/mediatypeConfig.js +86 -0
  79. package/dist/src/mediatype/mediatypeConfig.js.map +1 -0
  80. package/dist/src/mediatype-icon.d.ts +2 -2
  81. package/dist/src/mediatype-icon.js +35 -46
  82. package/dist/src/mediatype-icon.js.map +1 -1
  83. package/dist/src/models.d.ts +72 -13
  84. package/dist/src/models.js +57 -1
  85. package/dist/src/models.js.map +1 -1
  86. package/dist/src/restoration-state-handler.d.ts +38 -0
  87. package/dist/src/restoration-state-handler.js +204 -0
  88. package/dist/src/restoration-state-handler.js.map +1 -0
  89. package/dist/src/sort-filter-bar/alpha-bar.d.ts +1 -1
  90. package/dist/src/sort-filter-bar/alpha-bar.js +9 -2
  91. package/dist/src/sort-filter-bar/alpha-bar.js.map +1 -1
  92. package/dist/src/sort-filter-bar/img/compact.d.ts +1 -0
  93. package/dist/src/sort-filter-bar/img/compact.js +5 -0
  94. package/dist/src/sort-filter-bar/img/compact.js.map +1 -0
  95. package/dist/src/sort-filter-bar/img/list.js +1 -1
  96. package/dist/src/sort-filter-bar/img/list.js.map +1 -1
  97. package/dist/src/sort-filter-bar/img/tile.d.ts +1 -0
  98. package/dist/src/sort-filter-bar/img/tile.js +5 -0
  99. package/dist/src/sort-filter-bar/img/tile.js.map +1 -0
  100. package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +71 -14
  101. package/dist/src/sort-filter-bar/sort-filter-bar.js +499 -216
  102. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
  103. package/dist/src/tiles/collection-browser-loading-tile.d.ts +5 -0
  104. package/dist/src/tiles/collection-browser-loading-tile.js +32 -0
  105. package/dist/src/tiles/collection-browser-loading-tile.js.map +1 -0
  106. package/dist/src/tiles/grid/account-tile.d.ts +1 -1
  107. package/dist/src/tiles/grid/account-tile.js +5 -5
  108. package/dist/src/tiles/grid/account-tile.js.map +1 -1
  109. package/dist/src/tiles/grid/collection-tile.js +1 -2
  110. package/dist/src/tiles/grid/collection-tile.js.map +1 -1
  111. package/dist/src/tiles/grid/icons/views.d.ts +1 -1
  112. package/dist/src/tiles/grid/icons/views.js +2 -2
  113. package/dist/src/tiles/grid/icons/views.js.map +1 -1
  114. package/dist/src/tiles/grid/item-tile.d.ts +3 -3
  115. package/dist/src/tiles/grid/item-tile.js +55 -152
  116. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  117. package/dist/src/tiles/item-image.d.ts +21 -0
  118. package/dist/src/tiles/item-image.js +215 -0
  119. package/dist/src/tiles/item-image.js.map +1 -0
  120. package/dist/src/tiles/list/account-label.d.ts +1 -0
  121. package/dist/src/tiles/list/account-label.js +7 -0
  122. package/dist/src/tiles/list/account-label.js.map +1 -0
  123. package/dist/src/tiles/list/date-label.d.ts +1 -0
  124. package/dist/src/tiles/list/date-label.js +13 -0
  125. package/dist/src/tiles/list/date-label.js.map +1 -0
  126. package/dist/src/tiles/list/tile-list-compact-header.d.ts +12 -0
  127. package/dist/src/tiles/list/tile-list-compact-header.js +84 -0
  128. package/dist/src/tiles/list/tile-list-compact-header.js.map +1 -0
  129. package/dist/src/tiles/list/tile-list-compact.d.ts +13 -0
  130. package/dist/src/tiles/list/tile-list-compact.js +206 -6
  131. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  132. package/dist/src/tiles/list/tile-list.d.ts +36 -10
  133. package/dist/src/tiles/list/tile-list.js +370 -103
  134. package/dist/src/tiles/list/tile-list.js.map +1 -1
  135. package/dist/src/tiles/loading-tile.js +1 -42
  136. package/dist/src/tiles/loading-tile.js.map +1 -1
  137. package/dist/src/tiles/mediatype-icon.d.ts +9 -0
  138. package/dist/src/tiles/mediatype-icon.js +78 -0
  139. package/dist/src/tiles/mediatype-icon.js.map +1 -0
  140. package/dist/src/tiles/tile-dispatcher.d.ts +12 -4
  141. package/dist/src/tiles/tile-dispatcher.js +62 -20
  142. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  143. package/dist/src/utils/format-date.js +2 -2
  144. package/dist/src/utils/format-date.js.map +1 -1
  145. package/dist/src/utils/format-string.d.ts +2 -0
  146. package/dist/src/utils/format-string.js +7 -0
  147. package/dist/src/utils/format-string.js.map +1 -0
  148. package/dist/src/waveform-thumbnail.d.ts +7 -0
  149. package/dist/src/waveform-thumbnail.js +106 -0
  150. package/dist/src/waveform-thumbnail.js.map +1 -0
  151. package/dist/src/waveform-view.d.ts +0 -0
  152. package/dist/src/waveform-view.js +2 -0
  153. package/dist/src/waveform-view.js.map +1 -0
  154. package/dist/src/your-webcomponent.d.ts +8 -0
  155. package/dist/src/your-webcomponent.js +38 -0
  156. package/dist/src/your-webcomponent.js.map +1 -0
  157. package/dist/test/collection-browser.test.d.ts +1 -0
  158. package/dist/test/collection-browser.test.js +16 -2
  159. package/dist/test/collection-browser.test.js.map +1 -1
  160. package/dist/test/mediatype-config.test.d.ts +1 -0
  161. package/dist/test/mediatype-config.test.js +17 -0
  162. package/dist/test/mediatype-config.test.js.map +1 -0
  163. package/dist/test/mediatype-icon.test.d.ts +0 -0
  164. package/dist/test/mediatype-icon.test.js +3 -0
  165. package/dist/test/mediatype-icon.test.js.map +1 -0
  166. package/dist/test/mediatypeConfig.test.d.ts +1 -0
  167. package/dist/test/mediatypeConfig.test.js +17 -0
  168. package/dist/test/mediatypeConfig.test.js.map +1 -0
  169. package/dist/test/utils/format-date.test.js +1 -1
  170. package/dist/test/utils/format-date.test.js.map +1 -1
  171. package/dist/test/utils/format-string.test.d.ts +1 -0
  172. package/dist/test/utils/format-string.test.js +17 -0
  173. package/dist/test/utils/format-string.test.js.map +1 -0
  174. package/dist/test/your-webcomponent.test.d.ts +1 -0
  175. package/dist/test/your-webcomponent.test.js +23 -0
  176. package/dist/test/your-webcomponent.test.js.map +1 -0
  177. package/index.ts +6 -0
  178. package/local.archive.org.cert +86 -0
  179. package/local.archive.org.key +27 -0
  180. package/package.json +9 -5
  181. package/src/assets/img/icons/chevron.ts +4 -0
  182. package/src/assets/img/icons/mediatype/account.ts +6 -4
  183. package/src/assets/img/icons/mediatype/audio.ts +7 -4
  184. package/src/assets/img/icons/mediatype/collection.ts +7 -4
  185. package/src/assets/img/icons/mediatype/data.ts +15 -0
  186. package/src/assets/img/icons/mediatype/etree.ts +10 -5
  187. package/src/assets/img/icons/mediatype/film.ts +2 -1
  188. package/src/assets/img/icons/mediatype/images.ts +9 -6
  189. package/src/assets/img/icons/mediatype/radio.ts +15 -0
  190. package/src/assets/img/icons/mediatype/software.ts +9 -6
  191. package/src/assets/img/icons/mediatype/texts.ts +9 -6
  192. package/src/assets/img/icons/mediatype/tv.ts +10 -5
  193. package/src/assets/img/icons/mediatype/video.ts +10 -6
  194. package/src/assets/img/icons/mediatype/web.ts +9 -6
  195. package/src/collection-browser.ts +532 -163
  196. package/src/collection-facets.ts +307 -205
  197. package/src/language-code-handler/language-code-handler.ts +64 -0
  198. package/src/language-code-handler/language-code-mapping.ts +564 -0
  199. package/src/mediatype/mediatype-config.ts +86 -0
  200. package/src/models.ts +141 -13
  201. package/src/restoration-state-handler.ts +266 -0
  202. package/src/sort-filter-bar/alpha-bar.ts +9 -3
  203. package/src/sort-filter-bar/img/compact.ts +5 -0
  204. package/src/sort-filter-bar/img/list.ts +1 -1
  205. package/src/sort-filter-bar/img/tile.ts +5 -0
  206. package/src/sort-filter-bar/sort-filter-bar.ts +557 -225
  207. package/src/tiles/collection-browser-loading-tile.ts +29 -0
  208. package/src/tiles/grid/account-tile.ts +1 -1
  209. package/src/tiles/grid/collection-tile.ts +1 -2
  210. package/src/tiles/grid/icons/views.ts +2 -2
  211. package/src/tiles/grid/item-tile.ts +54 -164
  212. package/src/tiles/item-image.ts +218 -0
  213. package/src/tiles/list/account-label.ts +6 -0
  214. package/src/tiles/list/date-label.ts +12 -0
  215. package/src/tiles/list/tile-list-compact-header.ts +77 -0
  216. package/src/tiles/list/tile-list-compact.ts +220 -0
  217. package/src/tiles/list/tile-list.ts +414 -107
  218. package/src/tiles/mediatype-icon.ts +75 -0
  219. package/src/tiles/tile-dispatcher.ts +71 -19
  220. package/src/utils/format-date.ts +2 -2
  221. package/test/collection-browser.test.ts +20 -1
  222. package/test/mediatype-config.test.ts +18 -0
  223. package/test/utils/format-date.test.ts +1 -1
  224. package/web-dev-server.config.mjs +3 -1
  225. package/src/mediatype-icon.ts +0 -83
  226. package/src/sort-filter-bar/img/grid.ts +0 -5
  227. package/src/tiles/loading-tile.ts +0 -70
@@ -1,21 +1,24 @@
1
- import { css, html, LitElement } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
1
+ import {
2
+ css,
3
+ html,
4
+ LitElement,
5
+ nothing,
6
+ PropertyValues,
7
+ TemplateResult,
8
+ } from 'lit';
9
+ import { ifDefined } from 'lit/directives/if-defined.js';
10
+ import { join } from 'lit/directives/join.js';
11
+ import { map } from 'lit/directives/map.js';
12
+ import { customElement, property, state } from 'lit/decorators.js';
3
13
  import { SortParam } from '@internetarchive/search-service';
4
14
  import DOMPurify from 'dompurify';
5
- // import * as HtmlSanitizer from '@jitbit/htmlsanitizer';
6
- // import sanitizeHtml from 'sanitize-html';
7
- // - error
8
- // import DOMPurify from 'dompurify';
9
- // - no types
10
- import { CollectionDisplayMode, TileModel } from '../../models';
15
+ import { CollectionNameCacheInterface } from '@internetarchive/collection-name-cache';
16
+ import { dateLabel } from './date-label';
17
+ import { accountLabel } from './account-label';
18
+ import { TileModel } from '../../models';
11
19
  import { formatCount, NumberFormat } from '../../utils/format-count';
12
20
  import { formatDate, DateFormat } from '../../utils/format-date';
13
- import '../../mediatype-icon';
14
-
15
- /*
16
- at 750 creator, title trimmed
17
- at 530
18
- */
21
+ import '../mediatype-icon';
19
22
 
20
23
  @customElement('tile-list')
21
24
  export class TileList extends LitElement {
@@ -23,71 +26,315 @@ export class TileList extends LitElement {
23
26
 
24
27
  @property({ type: String }) baseNavigationUrl?: string;
25
28
 
29
+ @property({ type: Object })
30
+ collectionNameCache?: CollectionNameCacheInterface;
31
+
26
32
  @property({ type: Number }) currentWidth?: number;
27
33
 
28
34
  @property({ type: Number }) currentHeight?: number;
29
35
 
30
- @property({ type: Object }) sortParam?: SortParam;
36
+ @property({ type: Object }) sortParam: SortParam | null = null;
37
+
38
+ @property({ type: Number }) mobileBreakpoint?: number;
31
39
 
32
- @property({ type: String }) displayMode: CollectionDisplayMode =
33
- 'list-compact';
40
+ @state() private collectionLinks: TemplateResult[] = [];
34
41
 
35
- // private HtmlSanitizer = HtmlSanitizer();
42
+ @property({ type: String }) baseImageUrl?: string;
43
+
44
+ protected updated(changed: PropertyValues): void {
45
+ if (changed.has('model')) {
46
+ this.fetchCollectionNames();
47
+ }
48
+ }
49
+
50
+ private async fetchCollectionNames() {
51
+ if (
52
+ !this.model?.collections ||
53
+ this.model.collections.length === 0 ||
54
+ !this.collectionNameCache
55
+ ) {
56
+ return;
57
+ }
58
+ // Note: quirk of Lit: need to replace collectionLinks array,
59
+ // otherwise it will not re-render. Can't simply alter the array.
60
+ this.collectionLinks = [];
61
+ const newCollellectionLinks: TemplateResult[] = [];
62
+ const promises: Promise<void>[] = [];
63
+ for (const collection of this.model.collections) {
64
+ promises.push(
65
+ this.collectionNameCache?.collectionNameFor(collection).then(name => {
66
+ newCollellectionLinks.push(
67
+ this.detailsLink(collection, name ?? collection)
68
+ );
69
+ })
70
+ );
71
+ }
72
+ await Promise.all(promises);
73
+ this.collectionLinks = newCollellectionLinks;
74
+ }
36
75
 
37
76
  render() {
38
77
  return html`
39
78
  <div id="list-line" class="${this.classSize}">
40
- <div id="views">
41
- ${formatCount(this.model?.viewCount ?? 0, this.formatSize)}
79
+ ${this.classSize === 'mobile'
80
+ ? this.mobileTemplate
81
+ : this.desktopTemplate}
82
+ </div>
83
+ `;
84
+ }
85
+
86
+ private get mobileTemplate() {
87
+ return html`
88
+ <div id="list-line-top">
89
+ <div id="list-line-left">
90
+ <div id="thumb">${this.imgTemplate}</div>
42
91
  </div>
43
- <div id="title">${this.model?.title}</div>
44
- <div id="date">${formatDate(this.date, this.formatSize)}</div>
45
- <div id="creator">${this.model?.creator}</div>
46
- <div id="icon">
47
- <mediatype-icon .mediatype=${this.model?.mediatype}></mediatype-icon>
92
+ <div id="list-line-right">
93
+ <div id="title-line">
94
+ <div id="title">${this.titleTemplate}</div>
95
+ ${this.iconRightTemplate}
96
+ </div>
97
+ </div>
98
+ </div>
99
+ <div id="list-line-bottom">${this.detailsTemplate}</div>
100
+ `;
101
+ }
102
+
103
+ private get desktopTemplate() {
104
+ return html`
105
+ <div id="list-line-left">
106
+ <div id="thumb">${this.imgTemplate}</div>
107
+ </div>
108
+ <div id="list-line-right">
109
+ <div id="title-line">
110
+ <div id="title">${this.titleTemplate}</div>
111
+ ${this.iconRightTemplate}
48
112
  </div>
113
+ ${this.detailsTemplate}
114
+ </div>
115
+ `;
116
+ }
117
+
118
+ private get detailsTemplate() {
119
+ return html`
120
+ ${this.itemLineTemplate} ${this.creatorTemplate}
121
+ <div id="dates-line">
122
+ ${this.datePublishedTemplate} ${this.dateSortByTemplate}
123
+ </div>
124
+ <div id="views-line">
125
+ ${this.viewsTemplate} ${this.ratingTemplate} ${this.reviewsTemplate}
126
+ </div>
127
+ ${this.topicsTemplate} ${this.collectionsTemplate}
128
+ ${this.descriptionTemplate}
129
+ `;
130
+ }
131
+
132
+ // Data templates
133
+ private get imgTemplate() {
134
+ if (!this.model?.identifier) {
135
+ return nothing;
136
+ }
137
+ return html` <img
138
+ src="${this.baseImageUrl}/services/img/${this.model.identifier}"
139
+ alt="${this.model.identifier}"
140
+ class=${this.model?.mediatype}
141
+ />`;
142
+ }
143
+
144
+ private get iconRightTemplate() {
145
+ return html`
146
+ <div id="icon-right">
147
+ <mediatype-icon
148
+ .mediatype=${this.model?.mediatype}
149
+ .collections=${this.model?.collections}
150
+ style="--iconCustomFillColor: ${ifDefined(this.collectionColor)}"
151
+ >
152
+ </mediatype-icon>
153
+ </div>
154
+ `;
155
+ }
156
+
157
+ // Only in list, not tile
158
+ private get collectionColor() {
159
+ if (this.model?.mediatype !== 'collection') {
160
+ return undefined;
161
+ }
162
+ return '#4666FF';
163
+ }
164
+
165
+ private get titleTemplate() {
166
+ if (!this.model?.title) {
167
+ return nothing;
168
+ }
169
+ return html` ${this.detailsLink(this.model.identifier, this.model.title)} `;
170
+ }
171
+
172
+ private get itemLineTemplate() {
173
+ const source = this.sourceTemplate;
174
+ const volume = this.volumeTemplate;
175
+ const issue = this.issueTemplate;
176
+ if (!source && !volume && !issue) {
177
+ return nothing;
178
+ }
179
+ return html` <div id="item-line">${source} ${volume} ${issue}</div> `;
180
+ }
181
+
182
+ private get sourceTemplate() {
183
+ if (!this.model?.source) {
184
+ return nothing;
185
+ }
186
+ return html`
187
+ <div id="source" class="metadata">
188
+ ${this.labelTemplate('Source')}
189
+ ${this.searchLink('source', this.model.source)}
49
190
  </div>
50
- ${this.displayMode === 'list-detail' ? this.detail() : html``}
51
191
  `;
52
192
  }
53
193
 
54
- private detail() {
55
- const descriptionHtml = this.description();
56
- const topicHtml = this.topic();
57
- const sourceHtml = this.source();
194
+ private get volumeTemplate() {
195
+ return this.metadataTemplate(this.model?.volume, 'Volume');
196
+ }
197
+
198
+ private get issueTemplate() {
199
+ return this.metadataTemplate(this.model?.issue, 'Issue');
200
+ }
58
201
 
59
- if (descriptionHtml || topicHtml || sourceHtml) {
202
+ private get creatorTemplate() {
203
+ // "Achivist since" if account
204
+ if (this.model?.mediatype === 'account') {
60
205
  return html`
61
- <div id="list-detail" class="${this.classSize}">
62
- <div></div>
63
- <div id="details">${descriptionHtml} ${topicHtml} ${sourceHtml}</div>
64
- <div></div>
206
+ <div id="creator" class="metadata">
207
+ <span class="label"> ${accountLabel(this.model?.dateAdded)} </span>
65
208
  </div>
66
209
  `;
67
210
  }
68
- return html``;
211
+ // "Creator" if not account tile
212
+ if (!this.model?.creators || this.model.creators.length === 0) {
213
+ return nothing;
214
+ }
215
+ return html`
216
+ <div id="creator" class="metadata">
217
+ ${this.labelTemplate('By')}
218
+ ${join(
219
+ map(this.model.creators, id => this.searchLink('creator', id)),
220
+ html`, `
221
+ )}
222
+ </div>
223
+ `;
224
+ }
225
+
226
+ private get datePublishedTemplate() {
227
+ return this.metadataTemplate(
228
+ formatDate(this.model?.datePublished, 'long'),
229
+ 'Published'
230
+ );
231
+ }
232
+
233
+ // Show date label/value when sorted by date type
234
+ // Except datePublished which is always shown
235
+ private get dateSortByTemplate() {
236
+ if (
237
+ this.sortParam &&
238
+ (this.sortParam.field === 'addeddate' ||
239
+ this.sortParam.field === 'reviewdate' ||
240
+ this.sortParam.field === 'publicdate')
241
+ ) {
242
+ return this.metadataTemplate(
243
+ formatDate(this.date, 'long'),
244
+ dateLabel(this.sortParam.field)
245
+ );
246
+ }
247
+ return nothing;
248
+ }
249
+
250
+ private get viewsTemplate() {
251
+ return this.metadataTemplate(
252
+ `${formatCount(this.model?.viewCount ?? 0, this.formatSize)}`,
253
+ 'Views'
254
+ );
255
+ }
256
+
257
+ private get ratingTemplate() {
258
+ return this.metadataTemplate(this.model?.averageRating, 'Avg Rating');
259
+ }
260
+
261
+ private get reviewsTemplate() {
262
+ return this.metadataTemplate(this.model?.commentCount, 'Reviews');
69
263
  }
70
264
 
71
- private description() {
72
- if (this.model?.description) {
73
- const description = DOMPurify.sanitize(`${this.model?.description}`);
74
- return html` <div id="description">${description}</div> `;
265
+ private get topicsTemplate() {
266
+ if (!this.model?.subjects || this.model.subjects.length === 0) {
267
+ return nothing;
75
268
  }
76
- return html``;
269
+ return html`
270
+ <div id="topics" class="metadata">
271
+ ${this.labelTemplate('Topics')}
272
+ ${join(
273
+ map(this.model.subjects, id => this.searchLink('subject', id)),
274
+ html`, `
275
+ )}
276
+ </div>
277
+ `;
77
278
  }
78
279
 
79
- private topic() {
80
- if (this.model?.subject) {
81
- return html` <div id="topic">Topic: ${this.model?.subject}</div> `;
280
+ private get collectionsTemplate() {
281
+ if (!this.collectionLinks || this.collectionLinks.length === 0) {
282
+ return nothing;
82
283
  }
83
- return html``;
284
+ return html`
285
+ <div id="collections" class="metadata">
286
+ ${this.labelTemplate('Collections')}
287
+ ${join(this.collectionLinks, html`, `)}
288
+ </div>
289
+ `;
290
+ }
291
+
292
+ private get descriptionTemplate() {
293
+ return this.metadataTemplate(
294
+ DOMPurify.sanitize(this.model?.description ?? ''),
295
+ '',
296
+ 'description'
297
+ );
84
298
  }
85
299
 
86
- private source() {
87
- if (this.model?.source) {
88
- return html` <div id="source">Source: ${this.model?.source}</div> `;
300
+ // Utility functions
301
+ private metadataTemplate(text: any, label = '', id?: string) {
302
+ if (!text) return nothing;
303
+ return html`
304
+ <div id=${ifDefined(id)} class="metadata">
305
+ ${this.labelTemplate(label)} ${text}
306
+ </div>
307
+ `;
308
+ }
309
+
310
+ private labelTemplate(label: string) {
311
+ return html` ${label
312
+ ? html`<span class="label">${label}: </span>`
313
+ : nothing}`;
314
+ }
315
+
316
+ private searchLink(field: string, searchTerm: string) {
317
+ if (!field || !searchTerm) {
318
+ return nothing;
89
319
  }
90
- return html``;
320
+ const query = encodeURIComponent(`${field}:"${searchTerm}"`);
321
+ // No whitespace after closing tag
322
+ // Note: single ' for href='' to wrap " in query var gets changed back by yarn format
323
+
324
+ // eslint-disable-next-line lit/no-invalid-html
325
+ return html`<a href="${this.baseNavigationUrl}/search.php?query=${query}">
326
+ ${DOMPurify.sanitize(searchTerm)}</a
327
+ >`;
328
+ }
329
+
330
+ private detailsLink(identifier: string, text?: string): TemplateResult {
331
+ const linkText = text ?? identifier;
332
+ // No whitespace after closing tag
333
+ // identifiers (all ASCII in their creation) should be safe to use in href, but sanitize anyway
334
+ return html`<a
335
+ href="${this.baseNavigationUrl}/details/${encodeURI(identifier)}"
336
+ >${DOMPurify.sanitize(linkText)}</a
337
+ >`;
91
338
  }
92
339
 
93
340
  /*
@@ -109,118 +356,178 @@ export class TileList extends LitElement {
109
356
  }
110
357
 
111
358
  private get classSize(): string {
112
- return (this.currentWidth ?? 531) < 530 ? 'mobile' : 'desktop';
359
+ if (
360
+ this.mobileBreakpoint &&
361
+ this.currentWidth &&
362
+ this.currentWidth < this.mobileBreakpoint
363
+ ) {
364
+ return 'mobile';
365
+ }
366
+ return 'desktop';
113
367
  }
114
368
 
115
369
  private get formatSize(): DateFormat | NumberFormat {
116
- return (this.currentWidth ?? 531) < 530 ? 'short' : 'long';
370
+ if (
371
+ this.mobileBreakpoint &&
372
+ this.currentWidth &&
373
+ this.currentWidth < this.mobileBreakpoint
374
+ ) {
375
+ return 'short';
376
+ }
377
+ return 'long';
117
378
  }
118
379
 
119
380
  static get styles() {
120
381
  return css`
121
- .mobile div {
122
- font-size: 11px;
382
+ html {
383
+ font-size: unset;
123
384
  }
124
- .desktop div {
385
+
386
+ div {
125
387
  font-size: 14px;
126
388
  }
127
389
 
390
+ div a {
391
+ text-decoration: none;
392
+ }
393
+
394
+ .label {
395
+ font-weight: bold;
396
+ }
397
+
398
+ #list-line.mobile {
399
+ --infiniteScrollerRowGap: 20px;
400
+ --infiniteScrollerRowHeight: auto;
401
+ }
402
+
403
+ #list-line.desktop {
404
+ --infiniteScrollerRowGap: 30px;
405
+ --infiniteScrollerRowHeight: auto;
406
+ }
407
+
128
408
  /* fields */
129
409
 
130
- #title {
131
- color: #4b64ff;
132
- text-decoration: none;
410
+ #thumb img {
411
+ object-fit: cover;
412
+ display: block;
133
413
  }
134
414
 
135
- #title,
136
- #creator,
137
- #topic,
138
- #source {
139
- text-overflow: ellipsis;
140
- overflow: hidden;
415
+ .mobile #thumb img {
416
+ width: 90px;
417
+ height: 90px;
141
418
  }
142
419
 
143
- #title,
144
- #creator {
145
- white-space: nowrap;
420
+ .desktop #thumb img {
421
+ width: 100px;
422
+ height: 100px;
146
423
  }
147
424
 
148
- #views,
149
- #date {
150
- text-align: right;
425
+ #thumb img.collection {
426
+ border-radius: 8px;
427
+ -webkit-border-radius: 8px;
428
+ -moz-border-radius: 8px;
151
429
  }
152
430
 
153
- #views,
154
- #details {
155
- color: #767676;
431
+ .mobile #thumb img.account {
432
+ border-radius: 45px;
433
+ -webkit-border-radius: 45px;
434
+ -moz-border-radius: 45px;
156
435
  }
157
436
 
158
- #icon {
159
- padding-right: 6px;
437
+ .desktop #thumb img.account {
438
+ border-radius: 50px;
439
+ -webkit-border-radius: 50px;
440
+ -moz-border-radius: 50px;
160
441
  }
161
442
 
162
- .desktop #description,
163
- .desktop #topic,
164
- .desktop #source {
165
- font-size: 12px;
443
+ #icon-right {
444
+ width: 20px;
445
+ padding-top: 5px;
446
+ --iconHeight: 20px;
447
+ --iconWidth: 20px;
448
+ --iconTextAlign: right;
449
+ margin-top: -8px;
450
+ text-align: right;
166
451
  }
167
452
 
168
- .mobile #description,
169
- .mobile #topic,
170
- .mobile #source {
171
- font-size: 11px;
453
+ #title {
454
+ color: #4b64ff;
455
+ text-decoration: none;
456
+ font-size: 22px;
457
+ font-weight: bold;
458
+ /* align top of text with image */
459
+ line-height: 25px;
460
+ margin-top: -4px;
461
+ padding-bottom: 2px;
462
+ flex-grow: 1;
172
463
  }
173
464
 
174
- #description {
465
+ .metadata {
466
+ line-height: 20px;
467
+ }
468
+
469
+ #description,
470
+ #creator,
471
+ #topics,
472
+ #source {
473
+ text-align: left;
175
474
  overflow: hidden;
176
475
  text-overflow: ellipsis;
177
- -webkit-line-clamp: 2;
178
476
  -webkit-box-orient: vertical;
179
477
  display: -webkit-box;
180
478
  word-break: break-word;
181
479
  -webkit-line-clamp: 3; /* number of lines to show */
182
480
  line-clamp: 3;
183
- -webkit-box-orient: vertical;
184
- text-align: left;
185
481
  }
186
482
 
187
- /* list-line */
483
+ #icon {
484
+ padding-top: 5px;
485
+ }
486
+
487
+ #description {
488
+ padding-top: 10px;
489
+ }
188
490
 
491
+ /* Top level container */
189
492
  #list-line {
190
- display: grid;
191
- column-gap: 10px;
192
- line-height: 1.42857143;
193
- border-top: 1px solid #ddd !important;
194
- padding-top: 5px;
195
- align-items: center;
493
+ display: flex;
196
494
  }
197
495
 
198
496
  #list-line.mobile {
199
- grid-template-columns: 33px 3fr 30px 2fr 29.5px;
497
+ flex-direction: column;
200
498
  }
499
+
201
500
  #list-line.desktop {
202
- grid-template-columns: 60px 3fr 90px 2fr 29.5px;
501
+ column-gap: 10px;
203
502
  }
204
503
 
205
- #list-line:hover #title {
206
- text-decoration: underline;
504
+ #list-line-top {
505
+ display: flex;
506
+ column-gap: 7px;
207
507
  }
208
508
 
209
- /* list-detail */
509
+ #list-line-bottom {
510
+ padding-top: 4px;
511
+ }
210
512
 
211
- #list-detail {
212
- display: grid;
213
- column-gap: 10px;
214
- line-height: 1.42857143;
215
- align-items: center;
513
+ #list-line-right,
514
+ #list-line-top,
515
+ #list-line-bottom {
516
+ width: 100%;
216
517
  }
217
518
 
218
- #list-detail.mobile {
219
- grid-template-columns: 33px auto 29.5px;
519
+ div a:hover {
520
+ text-decoration: underline;
220
521
  }
221
522
 
222
- #list-detail.desktop {
223
- grid-template-columns: 60px auto 29.5px;
523
+ /* Lines containing multiple div as row */
524
+ #item-line,
525
+ #dates-line,
526
+ #views-line,
527
+ #title-line {
528
+ display: flex;
529
+ flex-direction: row;
530
+ gap: 10px;
224
531
  }
225
532
  `;
226
533
  }
@@ -0,0 +1,75 @@
1
+ import { css, CSSResultGroup, html, LitElement } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+
4
+ import { mediatypeConfig } from '../mediatype/mediatype-config';
5
+
6
+ @customElement('mediatype-icon')
7
+ export class MediatypeIcon extends LitElement {
8
+ @property({ type: String }) mediatype: string | undefined;
9
+
10
+ @property({ type: Array }) collections: string[] | undefined;
11
+
12
+ @property({ type: Boolean }) showText = false;
13
+
14
+ private get displayMediatype(): string {
15
+ const tvIdentifier = ['tvnews', 'tvarchive', 'television'];
16
+ const radioIdentifier = ['radio', 'radioprogram'];
17
+
18
+ if (
19
+ this.mediatype === 'movies' &&
20
+ this.collections?.some(id => tvIdentifier.indexOf(id) >= 0)
21
+ ) {
22
+ return 'tv';
23
+ }
24
+ if (
25
+ this.mediatype === 'audio' &&
26
+ this.collections?.some(id => radioIdentifier.indexOf(id) >= 0)
27
+ ) {
28
+ return 'radio';
29
+ }
30
+ return this.mediatype || '';
31
+ }
32
+
33
+ render() {
34
+ const config = mediatypeConfig[this.displayMediatype];
35
+ if (!config) {
36
+ return html``;
37
+ }
38
+
39
+ return html`
40
+ <div
41
+ id="icon"
42
+ class="${this.showText ? 'show-text' : 'hide-text'}"
43
+ style="--iconFillColor: ${config.color}"
44
+ >
45
+ ${config.icon}
46
+ <p class="status-text">${config.text}</p>
47
+ </div>
48
+ `;
49
+ }
50
+
51
+ static get styles(): CSSResultGroup {
52
+ return css`
53
+ .status-text {
54
+ font-size: 14px;
55
+ color: #2c2c2c;
56
+ margin: auto;
57
+ display: block;
58
+ text-align: var(--iconTextAlign, center);
59
+ }
60
+
61
+ #icon.hide-text p {
62
+ display: none;
63
+ }
64
+
65
+ svg {
66
+ height: var(--iconHeight, 10px);
67
+ width: var(--iconWidth, 10px);
68
+ }
69
+
70
+ .fill-color {
71
+ fill: var(--iconCustomFillColor, var(--iconFillColor, '#000000'));
72
+ }
73
+ `;
74
+ }
75
+ }