@internetarchive/collection-browser 0.0.1-alpha.8 → 0.1.1

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