@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.
Files changed (203) hide show
  1. package/.editorconfig +29 -0
  2. package/.github/workflows/ci.yml +26 -0
  3. package/.husky/pre-commit +4 -0
  4. package/LICENSE +661 -0
  5. package/README.md +68 -0
  6. package/demo/app-root.ts +414 -0
  7. package/demo/index.html +26 -0
  8. package/dist/demo/app-root.d.ts +43 -0
  9. package/dist/demo/app-root.js +385 -0
  10. package/dist/demo/app-root.js.map +1 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.js +3 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/src/assets/img/icons/audio.d.ts +1 -0
  15. package/dist/src/assets/img/icons/audio.js +9 -0
  16. package/dist/src/assets/img/icons/audio.js.map +1 -0
  17. package/dist/src/assets/img/icons/collection.d.ts +1 -0
  18. package/dist/src/assets/img/icons/collection.js +9 -0
  19. package/dist/src/assets/img/icons/collection.js.map +1 -0
  20. package/dist/src/assets/img/icons/etree.d.ts +1 -0
  21. package/dist/src/assets/img/icons/etree.js +9 -0
  22. package/dist/src/assets/img/icons/etree.js.map +1 -0
  23. package/dist/src/assets/img/icons/images.d.ts +1 -0
  24. package/dist/src/assets/img/icons/images.js +10 -0
  25. package/dist/src/assets/img/icons/images.js.map +1 -0
  26. package/dist/src/assets/img/icons/mediatype/account.d.ts +2 -0
  27. package/dist/src/assets/img/icons/mediatype/account.js +12 -0
  28. package/dist/src/assets/img/icons/mediatype/account.js.map +1 -0
  29. package/dist/src/assets/img/icons/mediatype/audio.d.ts +1 -0
  30. package/dist/src/assets/img/icons/mediatype/audio.js +11 -0
  31. package/dist/src/assets/img/icons/mediatype/audio.js.map +1 -0
  32. package/dist/src/assets/img/icons/mediatype/collection.d.ts +1 -0
  33. package/dist/src/assets/img/icons/mediatype/collection.js +9 -0
  34. package/dist/src/assets/img/icons/mediatype/collection.js.map +1 -0
  35. package/dist/src/assets/img/icons/mediatype/etree copy.d.ts +1 -0
  36. package/dist/src/assets/img/icons/mediatype/etree copy.js +9 -0
  37. package/dist/src/assets/img/icons/mediatype/etree copy.js.map +1 -0
  38. package/dist/src/assets/img/icons/mediatype/etree.d.ts +1 -0
  39. package/dist/src/assets/img/icons/mediatype/etree.js +9 -0
  40. package/dist/src/assets/img/icons/mediatype/etree.js.map +1 -0
  41. package/dist/src/assets/img/icons/mediatype/film.d.ts +1 -0
  42. package/dist/src/assets/img/icons/mediatype/film.js +13 -0
  43. package/dist/src/assets/img/icons/mediatype/film.js.map +1 -0
  44. package/dist/src/assets/img/icons/mediatype/images.d.ts +1 -0
  45. package/dist/src/assets/img/icons/mediatype/images.js +10 -0
  46. package/dist/src/assets/img/icons/mediatype/images.js.map +1 -0
  47. package/dist/src/assets/img/icons/mediatype/livemusic.d.ts +1 -0
  48. package/dist/src/assets/img/icons/mediatype/livemusic.js +7 -0
  49. package/dist/src/assets/img/icons/mediatype/livemusic.js.map +1 -0
  50. package/dist/src/assets/img/icons/mediatype/photos.d.ts +1 -0
  51. package/dist/src/assets/img/icons/mediatype/photos.js +7 -0
  52. package/dist/src/assets/img/icons/mediatype/photos.js.map +1 -0
  53. package/dist/src/assets/img/icons/mediatype/software.d.ts +1 -0
  54. package/dist/src/assets/img/icons/mediatype/software.js +10 -0
  55. package/dist/src/assets/img/icons/mediatype/software.js.map +1 -0
  56. package/dist/src/assets/img/icons/mediatype/texts.d.ts +1 -0
  57. package/dist/src/assets/img/icons/mediatype/texts.js +10 -0
  58. package/dist/src/assets/img/icons/mediatype/texts.js.map +1 -0
  59. package/dist/src/assets/img/icons/mediatype/tv.d.ts +1 -0
  60. package/dist/src/assets/img/icons/mediatype/tv.js +9 -0
  61. package/dist/src/assets/img/icons/mediatype/tv.js.map +1 -0
  62. package/dist/src/assets/img/icons/mediatype/video.d.ts +1 -0
  63. package/dist/src/assets/img/icons/mediatype/video.js +10 -0
  64. package/dist/src/assets/img/icons/mediatype/video.js.map +1 -0
  65. package/dist/src/assets/img/icons/mediatype/web.d.ts +1 -0
  66. package/dist/src/assets/img/icons/mediatype/web.js +10 -0
  67. package/dist/src/assets/img/icons/mediatype/web.js.map +1 -0
  68. package/dist/src/assets/img/icons/software.d.ts +1 -0
  69. package/dist/src/assets/img/icons/software.js +10 -0
  70. package/dist/src/assets/img/icons/software.js.map +1 -0
  71. package/dist/src/assets/img/icons/texts.d.ts +1 -0
  72. package/dist/src/assets/img/icons/texts.js +10 -0
  73. package/dist/src/assets/img/icons/texts.js.map +1 -0
  74. package/dist/src/assets/img/icons/tv.d.ts +1 -0
  75. package/dist/src/assets/img/icons/tv.js +9 -0
  76. package/dist/src/assets/img/icons/tv.js.map +1 -0
  77. package/dist/src/assets/img/icons/video.d.ts +1 -0
  78. package/dist/src/assets/img/icons/video.js +10 -0
  79. package/dist/src/assets/img/icons/video.js.map +1 -0
  80. package/dist/src/assets/img/icons/web.d.ts +1 -0
  81. package/dist/src/assets/img/icons/web.js +10 -0
  82. package/dist/src/assets/img/icons/web.js.map +1 -0
  83. package/dist/src/circular-activity-indicator.d.ts +5 -0
  84. package/dist/src/circular-activity-indicator.js +66 -0
  85. package/dist/src/circular-activity-indicator.js.map +1 -0
  86. package/dist/src/collection-browser.d.ts +151 -0
  87. package/dist/src/collection-browser.js +697 -0
  88. package/dist/src/collection-browser.js.map +1 -0
  89. package/dist/src/collection-facets.d.ts +34 -0
  90. package/dist/src/collection-facets.js +245 -0
  91. package/dist/src/collection-facets.js.map +1 -0
  92. package/dist/src/mediatype-icon.d.ts +9 -0
  93. package/dist/src/mediatype-icon.js +89 -0
  94. package/dist/src/mediatype-icon.js.map +1 -0
  95. package/dist/src/models.d.ts +23 -0
  96. package/dist/src/models.js +2 -0
  97. package/dist/src/models.js.map +1 -0
  98. package/dist/src/sort-filter-bar/alpha-bar.d.ts +10 -0
  99. package/dist/src/sort-filter-bar/alpha-bar.js +88 -0
  100. package/dist/src/sort-filter-bar/alpha-bar.js.map +1 -0
  101. package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +24 -0
  102. package/dist/src/sort-filter-bar/sort-filter-bar.js +257 -0
  103. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -0
  104. package/dist/src/tiles/grid/account-tile.d.ts +7 -0
  105. package/dist/src/tiles/grid/account-tile.js +144 -0
  106. package/dist/src/tiles/grid/account-tile.js.map +1 -0
  107. package/dist/src/tiles/grid/collection-tile.d.ts +7 -0
  108. package/dist/src/tiles/grid/collection-tile.js +160 -0
  109. package/dist/src/tiles/grid/collection-tile.js.map +1 -0
  110. package/dist/src/tiles/grid/icons/account.d.ts +1 -0
  111. package/dist/src/tiles/grid/icons/account.js +12 -0
  112. package/dist/src/tiles/grid/icons/account.js.map +1 -0
  113. package/dist/src/tiles/grid/icons/favorite-filled.d.ts +1 -0
  114. package/dist/src/tiles/grid/icons/favorite-filled.js +11 -0
  115. package/dist/src/tiles/grid/icons/favorite-filled.js.map +1 -0
  116. package/dist/src/tiles/grid/icons/reviews.d.ts +1 -0
  117. package/dist/src/tiles/grid/icons/reviews.js +11 -0
  118. package/dist/src/tiles/grid/icons/reviews.js.map +1 -0
  119. package/dist/src/tiles/grid/icons/upload.d.ts +1 -0
  120. package/dist/src/tiles/grid/icons/upload.js +12 -0
  121. package/dist/src/tiles/grid/icons/upload.js.map +1 -0
  122. package/dist/src/tiles/grid/icons/views.d.ts +2 -0
  123. package/dist/src/tiles/grid/icons/views.js +11 -0
  124. package/dist/src/tiles/grid/icons/views.js.map +1 -0
  125. package/dist/src/tiles/grid/item-tile.d.ts +9 -0
  126. package/dist/src/tiles/grid/item-tile.js +240 -0
  127. package/dist/src/tiles/grid/item-tile.js.map +1 -0
  128. package/dist/src/tiles/list/tile-list-compact.d.ts +16 -0
  129. package/dist/src/tiles/list/tile-list-compact.js +125 -0
  130. package/dist/src/tiles/list/tile-list-compact.js.map +1 -0
  131. package/dist/src/tiles/list/tile-list-detail.d.ts +17 -0
  132. package/dist/src/tiles/list/tile-list-detail.js +181 -0
  133. package/dist/src/tiles/list/tile-list-detail.js.map +1 -0
  134. package/dist/src/tiles/list/tile-list.d.ts +21 -0
  135. package/dist/src/tiles/list/tile-list.js +229 -0
  136. package/dist/src/tiles/list/tile-list.js.map +1 -0
  137. package/dist/src/tiles/loading-tile.d.ts +5 -0
  138. package/dist/src/tiles/loading-tile.js +73 -0
  139. package/dist/src/tiles/loading-tile.js.map +1 -0
  140. package/dist/src/tiles/tile-dispatcher.d.ts +27 -0
  141. package/dist/src/tiles/tile-dispatcher.js +160 -0
  142. package/dist/src/tiles/tile-dispatcher.js.map +1 -0
  143. package/dist/src/utils/format-count.d.ts +7 -0
  144. package/dist/src/utils/format-count.js +76 -0
  145. package/dist/src/utils/format-count.js.map +1 -0
  146. package/dist/src/utils/format-date.d.ts +2 -0
  147. package/dist/src/utils/format-date.js +24 -0
  148. package/dist/src/utils/format-date.js.map +1 -0
  149. package/dist/src/utils/format-string.d.ts +2 -0
  150. package/dist/src/utils/format-string.js +7 -0
  151. package/dist/src/utils/format-string.js.map +1 -0
  152. package/dist/test/collection-browser.test.d.ts +0 -0
  153. package/dist/test/collection-browser.test.js +3 -0
  154. package/dist/test/collection-browser.test.js.map +1 -0
  155. package/dist/test/utils/format-count.test.d.ts +1 -0
  156. package/dist/test/utils/format-count.test.js +24 -0
  157. package/dist/test/utils/format-count.test.js.map +1 -0
  158. package/dist/test/utils/format-date.test.d.ts +1 -0
  159. package/dist/test/utils/format-date.test.js +18 -0
  160. package/dist/test/utils/format-date.test.js.map +1 -0
  161. package/dist/test/utils/format-string.test.d.ts +1 -0
  162. package/dist/test/utils/format-string.test.js +17 -0
  163. package/dist/test/utils/format-string.test.js.map +1 -0
  164. package/index.ts +3 -0
  165. package/package.json +95 -0
  166. package/src/assets/img/icons/mediatype/account.ts +12 -0
  167. package/src/assets/img/icons/mediatype/audio.ts +11 -0
  168. package/src/assets/img/icons/mediatype/collection.ts +9 -0
  169. package/src/assets/img/icons/mediatype/etree.ts +9 -0
  170. package/src/assets/img/icons/mediatype/film.ts +13 -0
  171. package/src/assets/img/icons/mediatype/foo.svg +5 -0
  172. package/src/assets/img/icons/mediatype/images.ts +10 -0
  173. package/src/assets/img/icons/mediatype/software.ts +10 -0
  174. package/src/assets/img/icons/mediatype/texts.ts +10 -0
  175. package/src/assets/img/icons/mediatype/tv.ts +9 -0
  176. package/src/assets/img/icons/mediatype/video.ts +10 -0
  177. package/src/assets/img/icons/mediatype/web.ts +10 -0
  178. package/src/circular-activity-indicator.ts +64 -0
  179. package/src/collection-browser.ts +756 -0
  180. package/src/collection-facets.ts +285 -0
  181. package/src/mediatype-icon.ts +83 -0
  182. package/src/models.ts +25 -0
  183. package/src/sort-filter-bar/alpha-bar.ts +86 -0
  184. package/src/sort-filter-bar/sort-filter-bar.ts +256 -0
  185. package/src/tiles/grid/account-tile.ts +141 -0
  186. package/src/tiles/grid/collection-tile.ts +157 -0
  187. package/src/tiles/grid/icons/account.ts +12 -0
  188. package/src/tiles/grid/icons/favorite-filled.ts +11 -0
  189. package/src/tiles/grid/icons/reviews.ts +11 -0
  190. package/src/tiles/grid/icons/upload.ts +12 -0
  191. package/src/tiles/grid/icons/views.ts +11 -0
  192. package/src/tiles/grid/item-tile.ts +254 -0
  193. package/src/tiles/list/tile-list.ts +227 -0
  194. package/src/tiles/loading-tile.ts +70 -0
  195. package/src/tiles/tile-dispatcher.ts +160 -0
  196. package/src/utils/format-count.ts +95 -0
  197. package/src/utils/format-date.ts +36 -0
  198. package/test/collection-browser.test.ts +1 -0
  199. package/test/utils/format-count.test.ts +32 -0
  200. package/test/utils/format-date.test.ts +22 -0
  201. package/tsconfig.json +20 -0
  202. package/web-dev-server.config.mjs +28 -0
  203. package/web-test-runner.config.mjs +41 -0
@@ -0,0 +1,254 @@
1
+ import { css, CSSResultGroup, html, LitElement } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+ import { TileModel } from '../../models';
4
+ import { formatCount } from '../../utils/format-count';
5
+
6
+ import '../../mediatype-icon';
7
+
8
+ import { favoriteFilledIcon } from './icons/favorite-filled';
9
+ import { reviewsIcon } from './icons/reviews';
10
+ import viewsIcon from './icons/views';
11
+
12
+ @customElement('item-tile')
13
+ export class ItemTile extends LitElement {
14
+ @property({ type: Object }) model?: TileModel;
15
+
16
+ get renderItemImageView() {
17
+ const imgSrcUrl = `https://archive.org/services/img/${this.model?.identifier}`;
18
+ const containsDeemphasize = this.model?.collection.includes('deemphasize');
19
+ return !containsDeemphasize
20
+ ? html`<div
21
+ id="item-image"
22
+ style="background-image:url(${imgSrcUrl})"
23
+ ></div>`
24
+ : html`<div id="item-image-box">
25
+ <div
26
+ id="item-image-deemphasize"
27
+ style="background-image:url(${imgSrcUrl})"
28
+ ></div>
29
+ <div class="tile-action no-preview">Content may be inappropriate</div>
30
+ </div>`;
31
+ }
32
+
33
+ render() {
34
+ const itemTitle = this.model?.title || '';
35
+ const itemCreator = this.model?.creator || '-';
36
+
37
+ return html`
38
+ <div id="container">
39
+ <div id="title-image-container">
40
+ <p id="item-title" title=${itemTitle}>
41
+ ${this.model?.title}
42
+ </p>
43
+ <div id="item-image-container">
44
+ ${this.renderItemImageView}
45
+ </div>
46
+ <div class="item-creator">
47
+ <span id="text-by">By:</span>
48
+ <span>${itemCreator}</span>
49
+ </div>
50
+ </div>
51
+ <div id="item-stats-container">
52
+ <div id="stats-holder">
53
+ <div class="col">
54
+ <mediatype-icon mediatype="${
55
+ this.model?.mediatype
56
+ }" showText="true">
57
+ </div>
58
+ <div class="col">
59
+ ${viewsIcon}
60
+ <p class="status-text">${formatCount(
61
+ this.model?.viewCount,
62
+ 'short',
63
+ 'short'
64
+ )}</p>
65
+ </div>
66
+ <div class="col">
67
+ ${favoriteFilledIcon}
68
+ <p class="status-text">${formatCount(
69
+ this.model?.itemCount,
70
+ 'short',
71
+ 'short'
72
+ )}</p>
73
+ </div>
74
+ <div class="col">
75
+ ${reviewsIcon}
76
+ <p class="status-text">${formatCount(
77
+ this.model?.favCount,
78
+ 'short',
79
+ 'short'
80
+ )}</p>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ `;
86
+ }
87
+
88
+ static get styles(): CSSResultGroup {
89
+ const cornerRadiusCss = css`var(--collectionTileCornerRadius, 4px)`;
90
+
91
+ return css`
92
+ #container {
93
+ background-color: #ffffff;
94
+ border-radius: ${cornerRadiusCss};
95
+ box-shadow: 1px 1px 2px 0px;
96
+ display: flex;
97
+ flex-direction: column;
98
+ height: 100%;
99
+ }
100
+
101
+ #title-image-container {
102
+ display: flex;
103
+ flex: 1;
104
+ flex-direction: column;
105
+ padding: 0.5rem;
106
+ }
107
+
108
+ #item-title {
109
+ font-weight: bold;
110
+ color: #000000;
111
+ font-size: 1.6rem;
112
+ text-align: center;
113
+ margin-top: 0rem;
114
+ margin-bottom: 0.5rem;
115
+ overflow: hidden;
116
+ text-overflow: ellipsis;
117
+ display: -webkit-box;
118
+ -webkit-line-clamp: 2;
119
+ -webkit-box-orient: vertical;
120
+ line-height: 2rem;
121
+ height: 4rem;
122
+ }
123
+
124
+ #item-image-container {
125
+ display: flex;
126
+ justify-content: center;
127
+ flex: 1;
128
+ }
129
+
130
+ #item-image {
131
+ width: 16rem;
132
+ height: 16rem;
133
+ border-radius: 0.8rem;
134
+ overflow: hidden;
135
+ box-shadow: 1px 1px 2px 0px;
136
+ object-fit: cover;
137
+ background-position: center;
138
+ background-size: cover;
139
+ }
140
+
141
+ #item-image-box {
142
+ width: 16rem;
143
+ height: 16rem;
144
+ border-radius: 0.8rem;
145
+ overflow: hidden;
146
+ box-shadow: 1px 1px 2px 0px;
147
+ border: 5px solid #0000000;
148
+ position: relative;
149
+ display: flex;
150
+ }
151
+
152
+ #item-image-deemphasize {
153
+ width: 16rem;
154
+ height: 16rem;
155
+ object-fit: cover;
156
+ background-position: center;
157
+ background-size: cover;
158
+ filter: blur(15px);
159
+ position: absolute;
160
+ z-index: 1;
161
+ }
162
+
163
+ .tile-action {
164
+ border: 2px solid currentColor;
165
+ border-radius: 1px;
166
+ padding: 5px;
167
+ font-weight: 500;
168
+ width: auto;
169
+ position: absolute;
170
+ z-index: 2;
171
+ display: flex;
172
+ top: 5.5rem;
173
+ }
174
+
175
+ .no-preview {
176
+ background-color: #fffecb;
177
+ color: #000000;
178
+ font-size: 1.4rem;
179
+ line-height: 2rem;
180
+ text-align: center;
181
+ }
182
+
183
+ .hidden {
184
+ display: none;
185
+ }
186
+
187
+ #container:hover > #title-image-container > .item-title {
188
+ text-decoration: underline;
189
+ }
190
+
191
+ /** this is a workaround for Safari 15 where the hover effects are not working */
192
+ #title-image-container:hover > #item-title {
193
+ text-decoration: underline;
194
+ }
195
+
196
+ #container:hover > #item-title {
197
+ background-color: #fcfcfc;
198
+ }
199
+
200
+ /** creator **/
201
+ .item-creator {
202
+ color: #000000;
203
+ display: -webkit-box;
204
+ font-size: 1.4rem;
205
+ height: 3rem;
206
+ margin: 0px;
207
+ overflow: hidden;
208
+ padding: 0.5rem;
209
+ text-align: center;
210
+ text-overflow: ellipsis;
211
+ -webkit-line-clamp: 2;
212
+ -webkit-box-orient: vertical;
213
+ }
214
+
215
+ #text-by {
216
+ font-weight: bold;
217
+ }
218
+
219
+ #item-stats-container {
220
+ align-items: center;
221
+ display: flex;
222
+ height: 5.5rem;
223
+ padding-left: 1rem;
224
+ padding-right: 0.5rem;
225
+ }
226
+
227
+ #stats-holder {
228
+ align-items: center;
229
+ display: flex;
230
+ flex: 1;
231
+ justify-content: space-evenly;
232
+ text-align: center;
233
+ width: 100%;
234
+ }
235
+
236
+ svg {
237
+ height: 10px;
238
+ width: 10px;
239
+ }
240
+
241
+ .status-text {
242
+ font-size: 14px;
243
+ color: #2c2c2c;
244
+ margin: auto;
245
+ display: block;
246
+ text-align: center;
247
+ }
248
+
249
+ .col {
250
+ width: 25%;
251
+ }
252
+ `;
253
+ }
254
+ }
@@ -0,0 +1,227 @@
1
+ import { css, html, LitElement } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+ import { SortParam } from '@internetarchive/search-service';
4
+ 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';
11
+ import { formatCount, NumberFormat } from '../../utils/format-count';
12
+ import { formatDate, DateFormat } from '../../utils/format-date';
13
+ import '../../mediatype-icon';
14
+
15
+ /*
16
+ at 750 creator, title trimmed
17
+ at 530
18
+ */
19
+
20
+ @customElement('tile-list')
21
+ export class TileList extends LitElement {
22
+ @property({ type: Object }) model?: TileModel;
23
+
24
+ @property({ type: String }) baseNavigationUrl?: string;
25
+
26
+ @property({ type: Number }) currentWidth?: number;
27
+
28
+ @property({ type: Number }) currentHeight?: number;
29
+
30
+ @property({ type: Object }) sortParam?: SortParam;
31
+
32
+ @property({ type: String }) displayMode: CollectionDisplayMode =
33
+ 'list-compact';
34
+
35
+ // private HtmlSanitizer = HtmlSanitizer();
36
+
37
+ render() {
38
+ return html`
39
+ <div id="list-line" class="${this.classSize}">
40
+ <div id="views">
41
+ ${formatCount(this.model?.viewCount ?? 0, this.formatSize)}
42
+ </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>
48
+ </div>
49
+ </div>
50
+ ${this.displayMode === 'list-detail' ? this.detail() : html``}
51
+ `;
52
+ }
53
+
54
+ private detail() {
55
+ const descriptionHtml = this.description();
56
+ const topicHtml = this.topic();
57
+ const sourceHtml = this.source();
58
+
59
+ if (descriptionHtml || topicHtml || sourceHtml) {
60
+ return html`
61
+ <div id="list-detail" class="${this.classSize}">
62
+ <div></div>
63
+ <div id="details">${descriptionHtml} ${topicHtml} ${sourceHtml}</div>
64
+ <div></div>
65
+ </div>
66
+ `;
67
+ }
68
+ return html``;
69
+ }
70
+
71
+ private description() {
72
+ if (this.model?.description) {
73
+ const description = DOMPurify.sanitize(`${this.model?.description}`);
74
+ return html` <div id="description">${description}</div> `;
75
+ }
76
+ return html``;
77
+ }
78
+
79
+ private topic() {
80
+ if (this.model?.subject) {
81
+ return html` <div id="topic">Topic: ${this.model?.subject}</div> `;
82
+ }
83
+ return html``;
84
+ }
85
+
86
+ private source() {
87
+ if (this.model?.source) {
88
+ return html` <div id="source">Source: ${this.model?.source}</div> `;
89
+ }
90
+ return html``;
91
+ }
92
+
93
+ /*
94
+ * TODO: fix field names to match model in src/collection-browser.ts
95
+ * private get dateSortSelector()
96
+ * @see src/models.ts
97
+ */
98
+ private get date(): Date | undefined {
99
+ switch (this.sortParam?.field) {
100
+ case 'date':
101
+ return this.model?.datePublished;
102
+ case 'reviewdate':
103
+ return this.model?.dateReviewed;
104
+ case 'addeddate':
105
+ return this.model?.dateAdded;
106
+ default:
107
+ return this.model?.dateArchived; // publicdate
108
+ }
109
+ }
110
+
111
+ private get classSize(): string {
112
+ return (this.currentWidth ?? 531) < 530 ? 'mobile' : 'desktop';
113
+ }
114
+
115
+ private get formatSize(): DateFormat | NumberFormat {
116
+ return (this.currentWidth ?? 531) < 530 ? 'short' : 'long';
117
+ }
118
+
119
+ static get styles() {
120
+ return css`
121
+ .mobile div {
122
+ font-size: 11px;
123
+ }
124
+ .desktop div {
125
+ font-size: 14px;
126
+ }
127
+
128
+ /* fields */
129
+
130
+ #title {
131
+ color: #4b64ff;
132
+ text-decoration: none;
133
+ }
134
+
135
+ #title,
136
+ #creator,
137
+ #topic,
138
+ #source {
139
+ text-overflow: ellipsis;
140
+ overflow: hidden;
141
+ }
142
+
143
+ #title,
144
+ #creator {
145
+ white-space: nowrap;
146
+ }
147
+
148
+ #views,
149
+ #date {
150
+ text-align: right;
151
+ }
152
+
153
+ #views,
154
+ #details {
155
+ color: #767676;
156
+ }
157
+
158
+ #icon {
159
+ padding-right: 6px;
160
+ }
161
+
162
+ .desktop #description,
163
+ .desktop #topic,
164
+ .desktop #source {
165
+ font-size: 12px;
166
+ }
167
+
168
+ .mobile #description,
169
+ .mobile #topic,
170
+ .mobile #source {
171
+ font-size: 11px;
172
+ }
173
+
174
+ #description {
175
+ overflow: hidden;
176
+ text-overflow: ellipsis;
177
+ -webkit-line-clamp: 2;
178
+ -webkit-box-orient: vertical;
179
+ display: -webkit-box;
180
+ word-break: break-word;
181
+ -webkit-line-clamp: 3; /* number of lines to show */
182
+ line-clamp: 3;
183
+ -webkit-box-orient: vertical;
184
+ text-align: left;
185
+ }
186
+
187
+ /* list-line */
188
+
189
+ #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;
196
+ }
197
+
198
+ #list-line.mobile {
199
+ grid-template-columns: 33px 3fr 30px 2fr 29.5px;
200
+ }
201
+ #list-line.desktop {
202
+ grid-template-columns: 60px 3fr 90px 2fr 29.5px;
203
+ }
204
+
205
+ #list-line:hover #title {
206
+ text-decoration: underline;
207
+ }
208
+
209
+ /* list-detail */
210
+
211
+ #list-detail {
212
+ display: grid;
213
+ column-gap: 10px;
214
+ line-height: 1.42857143;
215
+ align-items: center;
216
+ }
217
+
218
+ #list-detail.mobile {
219
+ grid-template-columns: 33px auto 29.5px;
220
+ }
221
+
222
+ #list-detail.desktop {
223
+ grid-template-columns: 60px auto 29.5px;
224
+ }
225
+ `;
226
+ }
227
+ }
@@ -0,0 +1,70 @@
1
+ import { css, html, LitElement } from 'lit';
2
+ import { customElement } from 'lit/decorators.js';
3
+
4
+ @customElement('loading-tile')
5
+ export class LoadingTile extends LitElement {
6
+ render() {
7
+ return html` <div id="container"></div> `;
8
+ }
9
+
10
+ static get styles() {
11
+ return css`
12
+ :host {
13
+ display: block;
14
+ height: 100%;
15
+ }
16
+
17
+ #container {
18
+ background: linear-gradient(
19
+ to right,
20
+ rgba(25, 69, 154, 0.1),
21
+ rgb(105, 161, 234, 0.2)
22
+ );
23
+ background-size: 400% 400%;
24
+
25
+ -webkit-animation: AnimationName 4s ease infinite;
26
+ -moz-animation: AnimationName 4s ease infinite;
27
+ animation: AnimationName 4s ease infinite;
28
+
29
+ display: block;
30
+ height: 100%;
31
+ }
32
+
33
+ @-webkit-keyframes AnimationName {
34
+ 0% {
35
+ background-position: 0% 50%;
36
+ }
37
+ 50% {
38
+ background-position: 100% 50%;
39
+ }
40
+ 100% {
41
+ background-position: 0% 50%;
42
+ }
43
+ }
44
+
45
+ @-moz-keyframes AnimationName {
46
+ 0% {
47
+ background-position: 0% 50%;
48
+ }
49
+ 50% {
50
+ background-position: 100% 50%;
51
+ }
52
+ 100% {
53
+ background-position: 0% 50%;
54
+ }
55
+ }
56
+
57
+ @keyframes AnimationName {
58
+ 0% {
59
+ background-position: 0% 50%;
60
+ }
61
+ 50% {
62
+ background-position: 100% 50%;
63
+ }
64
+ 100% {
65
+ background-position: 0% 50%;
66
+ }
67
+ }
68
+ `;
69
+ }
70
+ }
@@ -0,0 +1,160 @@
1
+ import { css, html, LitElement, nothing, PropertyValues } from 'lit';
2
+ import { customElement, property, query } from 'lit/decorators.js';
3
+ import { ifDefined } from 'lit/directives/if-defined.js';
4
+ import {
5
+ SharedResizeObserverInterface,
6
+ SharedResizeObserverResizeHandlerInterface,
7
+ } from '@internetarchive/shared-resize-observer';
8
+ import { SortParam } from '@internetarchive/search-service';
9
+ import type { CollectionDisplayMode, TileModel } from '../models';
10
+ import './grid/collection-tile';
11
+ import './grid/item-tile';
12
+ import './grid/account-tile';
13
+ import './list/tile-list';
14
+
15
+ @customElement('tile-dispatcher')
16
+ export class TileDispatcher
17
+ extends LitElement
18
+ implements SharedResizeObserverResizeHandlerInterface
19
+ {
20
+ @property({ type: String }) displayMode: CollectionDisplayMode = 'grid';
21
+
22
+ @property({ type: Object }) model?: TileModel;
23
+
24
+ @property({ type: String }) baseNavigationUrl?: string;
25
+
26
+ @property({ type: Boolean }) showDeleteButton = false;
27
+
28
+ @property({ type: Number }) currentWidth?: number;
29
+
30
+ @property({ type: Number }) currentHeight?: number;
31
+
32
+ @property({ type: Object }) resizeObserver?: SharedResizeObserverInterface;
33
+
34
+ @property({ type: Object }) sortParam?: SortParam;
35
+
36
+ @query('#container') private container!: HTMLDivElement;
37
+
38
+ render() {
39
+ return html`
40
+ <div id="container">
41
+ ${this.showDeleteButton
42
+ ? html`<button id="delete-button">X</button>`
43
+ : nothing}
44
+ <a
45
+ href="${this.baseNavigationUrl}/details/${this.model?.identifier}"
46
+ title=${ifDefined(this.model?.title)}
47
+ >
48
+ ${this.tile}
49
+ </a>
50
+ </div>
51
+ `;
52
+ }
53
+
54
+ handleResize(entry: ResizeObserverEntry): void {
55
+ this.currentWidth = entry.contentRect.width;
56
+ this.currentHeight = entry.contentRect.height;
57
+ }
58
+
59
+ disconnectedCallback(): void {
60
+ this.stopResizeObservation(this.resizeObserver);
61
+ }
62
+
63
+ private stopResizeObservation(observer?: SharedResizeObserverInterface) {
64
+ observer?.removeObserver({
65
+ handler: this,
66
+ target: this.container,
67
+ });
68
+ }
69
+
70
+ private startResizeObservation() {
71
+ this.stopResizeObservation(this.resizeObserver);
72
+ this.resizeObserver?.addObserver({
73
+ handler: this,
74
+ target: this.container,
75
+ });
76
+ }
77
+
78
+ updated(props: PropertyValues) {
79
+ if (props.has('resizeObserver')) {
80
+ const previousObserver = props.get(
81
+ 'resizeObserver'
82
+ ) as SharedResizeObserverInterface;
83
+ this.stopResizeObservation(previousObserver);
84
+ this.startResizeObservation();
85
+ }
86
+ }
87
+
88
+ private get tile() {
89
+ const { model, baseNavigationUrl, currentWidth, currentHeight, sortParam } =
90
+ this;
91
+
92
+ if (!model) return nothing;
93
+
94
+ switch (this.displayMode) {
95
+ case 'grid':
96
+ switch (model.mediatype) {
97
+ case 'collection':
98
+ return html`<collection-tile
99
+ .model=${model}
100
+ .currentWidth=${currentWidth}
101
+ .currentHeight=${currentHeight}
102
+ >
103
+ </collection-tile>`;
104
+ case 'account':
105
+ return html`<account-tile
106
+ .model=${model}
107
+ .currentWidth=${currentWidth}
108
+ .currentHeight=${currentHeight}
109
+ ></account-tile>`;
110
+ default:
111
+ return html`<item-tile
112
+ .model=${model}
113
+ .currentWidth=${currentWidth}
114
+ .currentHeight=${currentHeight}
115
+ ></item-tile>`;
116
+ }
117
+ case 'list-compact':
118
+ case 'list-detail':
119
+ return html`<tile-list
120
+ .model=${model}
121
+ .currentWidth=${currentWidth}
122
+ .currentHeight=${currentHeight}
123
+ .baseNavigationUrl=${baseNavigationUrl}
124
+ .sortParam=${sortParam}
125
+ .displayMode=${this.displayMode}
126
+ ></tile-list>`;
127
+ default:
128
+ return nothing;
129
+ }
130
+ }
131
+
132
+ static get styles() {
133
+ return css`
134
+ :host {
135
+ display: block;
136
+ height: 100%;
137
+ }
138
+
139
+ #container {
140
+ height: 100%;
141
+ }
142
+
143
+ #delete-button {
144
+ float: right;
145
+ }
146
+
147
+ a {
148
+ display: block;
149
+ height: 100%;
150
+ color: unset;
151
+ text-decoration: none;
152
+ }
153
+
154
+ a :first-child {
155
+ display: block;
156
+ height: 100%;
157
+ }
158
+ `;
159
+ }
160
+ }