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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) 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/language-code-handler/language-code-handler.d.ts +37 -0
  48. package/dist/src/language-code-handler/language-code-handler.js +27 -0
  49. package/dist/src/language-code-handler/language-code-handler.js.map +1 -0
  50. package/dist/src/language-code-handler/language-code-mapping.d.ts +1 -0
  51. package/dist/src/language-code-handler/language-code-mapping.js +563 -0
  52. package/dist/src/language-code-handler/language-code-mapping.js.map +1 -0
  53. package/dist/src/mediatype/mediatype-config.d.ts +3 -0
  54. package/dist/src/mediatype/mediatype-config.js +86 -0
  55. package/dist/src/mediatype/mediatype-config.js.map +1 -0
  56. package/dist/src/models.d.ts +72 -13
  57. package/dist/src/models.js +57 -1
  58. package/dist/src/models.js.map +1 -1
  59. package/dist/src/restoration-state-handler.d.ts +38 -0
  60. package/dist/src/restoration-state-handler.js +204 -0
  61. package/dist/src/restoration-state-handler.js.map +1 -0
  62. package/dist/src/sort-filter-bar/alpha-bar.d.ts +1 -1
  63. package/dist/src/sort-filter-bar/alpha-bar.js +9 -2
  64. package/dist/src/sort-filter-bar/alpha-bar.js.map +1 -1
  65. package/dist/src/sort-filter-bar/img/compact.d.ts +1 -0
  66. package/dist/src/sort-filter-bar/img/compact.js +5 -0
  67. package/dist/src/sort-filter-bar/img/compact.js.map +1 -0
  68. package/dist/src/sort-filter-bar/img/list.js +1 -1
  69. package/dist/src/sort-filter-bar/img/list.js.map +1 -1
  70. package/dist/src/sort-filter-bar/img/tile.d.ts +1 -0
  71. package/dist/src/sort-filter-bar/img/tile.js +5 -0
  72. package/dist/src/sort-filter-bar/img/tile.js.map +1 -0
  73. package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +71 -14
  74. package/dist/src/sort-filter-bar/sort-filter-bar.js +499 -216
  75. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
  76. package/dist/src/tiles/{loading-tile.d.ts → collection-browser-loading-tile.d.ts} +1 -1
  77. package/dist/src/tiles/collection-browser-loading-tile.js +32 -0
  78. package/dist/src/tiles/collection-browser-loading-tile.js.map +1 -0
  79. package/dist/src/tiles/grid/account-tile.d.ts +1 -1
  80. package/dist/src/tiles/grid/account-tile.js +5 -5
  81. package/dist/src/tiles/grid/account-tile.js.map +1 -1
  82. package/dist/src/tiles/grid/collection-tile.js +1 -2
  83. package/dist/src/tiles/grid/collection-tile.js.map +1 -1
  84. package/dist/src/tiles/grid/icons/views.d.ts +1 -1
  85. package/dist/src/tiles/grid/icons/views.js +2 -2
  86. package/dist/src/tiles/grid/icons/views.js.map +1 -1
  87. package/dist/src/tiles/grid/item-tile.d.ts +2 -2
  88. package/dist/src/tiles/grid/item-tile.js +57 -151
  89. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  90. package/dist/src/tiles/item-image.d.ts +19 -0
  91. package/dist/src/tiles/item-image.js +204 -0
  92. package/dist/src/tiles/item-image.js.map +1 -0
  93. package/dist/src/tiles/list/account-label.d.ts +1 -0
  94. package/dist/src/tiles/list/account-label.js +7 -0
  95. package/dist/src/tiles/list/account-label.js.map +1 -0
  96. package/dist/src/tiles/list/date-label.d.ts +1 -0
  97. package/dist/src/tiles/list/date-label.js +13 -0
  98. package/dist/src/tiles/list/date-label.js.map +1 -0
  99. package/dist/src/tiles/list/tile-list-compact-header.d.ts +12 -0
  100. package/dist/src/tiles/list/tile-list-compact-header.js +84 -0
  101. package/dist/src/tiles/list/tile-list-compact-header.js.map +1 -0
  102. package/dist/src/tiles/list/tile-list-compact.d.ts +12 -0
  103. package/dist/src/tiles/list/tile-list-compact.js +203 -6
  104. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  105. package/dist/src/tiles/list/tile-list.d.ts +35 -10
  106. package/dist/src/tiles/list/tile-list.js +368 -104
  107. package/dist/src/tiles/list/tile-list.js.map +1 -1
  108. package/dist/src/{mediatype-icon.d.ts → tiles/mediatype-icon.d.ts} +2 -2
  109. package/dist/src/tiles/mediatype-icon.js +78 -0
  110. package/dist/src/tiles/mediatype-icon.js.map +1 -0
  111. package/dist/src/tiles/tile-dispatcher.d.ts +11 -4
  112. package/dist/src/tiles/tile-dispatcher.js +56 -19
  113. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  114. package/dist/src/utils/format-date.js +2 -2
  115. package/dist/src/utils/format-date.js.map +1 -1
  116. package/dist/test/collection-browser.test.d.ts +1 -0
  117. package/dist/test/collection-browser.test.js +16 -2
  118. package/dist/test/collection-browser.test.js.map +1 -1
  119. package/dist/test/mediatype-config.test.d.ts +1 -0
  120. package/dist/test/mediatype-config.test.js +17 -0
  121. package/dist/test/mediatype-config.test.js.map +1 -0
  122. package/dist/test/utils/format-date.test.js +1 -1
  123. package/dist/test/utils/format-date.test.js.map +1 -1
  124. package/index.ts +6 -0
  125. package/local.archive.org.cert +86 -0
  126. package/local.archive.org.key +27 -0
  127. package/package.json +9 -5
  128. package/src/assets/img/icons/chevron.ts +4 -0
  129. package/src/assets/img/icons/mediatype/account.ts +6 -4
  130. package/src/assets/img/icons/mediatype/audio.ts +7 -4
  131. package/src/assets/img/icons/mediatype/collection.ts +7 -4
  132. package/src/assets/img/icons/mediatype/data.ts +15 -0
  133. package/src/assets/img/icons/mediatype/etree.ts +10 -5
  134. package/src/assets/img/icons/mediatype/film.ts +2 -1
  135. package/src/assets/img/icons/mediatype/images.ts +9 -6
  136. package/src/assets/img/icons/mediatype/radio.ts +15 -0
  137. package/src/assets/img/icons/mediatype/software.ts +9 -6
  138. package/src/assets/img/icons/mediatype/texts.ts +9 -6
  139. package/src/assets/img/icons/mediatype/tv.ts +10 -5
  140. package/src/assets/img/icons/mediatype/video.ts +10 -6
  141. package/src/assets/img/icons/mediatype/web.ts +9 -6
  142. package/src/collection-browser.ts +529 -163
  143. package/src/collection-facets.ts +307 -205
  144. package/src/language-code-handler/language-code-handler.ts +64 -0
  145. package/src/language-code-handler/language-code-mapping.ts +564 -0
  146. package/src/mediatype/mediatype-config.ts +86 -0
  147. package/src/models.ts +141 -13
  148. package/src/restoration-state-handler.ts +266 -0
  149. package/src/sort-filter-bar/alpha-bar.ts +9 -3
  150. package/src/sort-filter-bar/img/compact.ts +5 -0
  151. package/src/sort-filter-bar/img/list.ts +1 -1
  152. package/src/sort-filter-bar/img/tile.ts +5 -0
  153. package/src/sort-filter-bar/sort-filter-bar.ts +557 -225
  154. package/src/tiles/collection-browser-loading-tile.ts +29 -0
  155. package/src/tiles/grid/account-tile.ts +1 -1
  156. package/src/tiles/grid/collection-tile.ts +1 -2
  157. package/src/tiles/grid/icons/views.ts +2 -2
  158. package/src/tiles/grid/item-tile.ts +56 -163
  159. package/src/tiles/item-image.ts +206 -0
  160. package/src/tiles/list/account-label.ts +6 -0
  161. package/src/tiles/list/date-label.ts +12 -0
  162. package/src/tiles/list/tile-list-compact-header.ts +77 -0
  163. package/src/tiles/list/tile-list-compact.ts +218 -0
  164. package/src/tiles/list/tile-list.ts +412 -107
  165. package/src/tiles/mediatype-icon.ts +75 -0
  166. package/src/tiles/tile-dispatcher.ts +66 -18
  167. package/src/utils/format-date.ts +2 -2
  168. package/test/collection-browser.test.ts +20 -1
  169. package/test/mediatype-config.test.ts +18 -0
  170. package/test/utils/format-date.test.ts +1 -1
  171. package/web-dev-server.config.mjs +3 -1
  172. package/dist/src/mediatype-icon.js +0 -89
  173. package/dist/src/mediatype-icon.js.map +0 -1
  174. package/dist/src/sort-filter-bar/img/grid.d.ts +0 -1
  175. package/dist/src/sort-filter-bar/img/grid.js +0 -5
  176. package/dist/src/sort-filter-bar/img/grid.js.map +0 -1
  177. package/dist/src/tiles/list/tile-list-detail.d.ts +0 -7
  178. package/dist/src/tiles/list/tile-list-detail.js +0 -28
  179. package/dist/src/tiles/list/tile-list-detail.js.map +0 -1
  180. package/dist/src/tiles/loading-tile.js +0 -73
  181. package/dist/src/tiles/loading-tile.js.map +0 -1
  182. package/src/mediatype-icon.ts +0 -83
  183. package/src/sort-filter-bar/img/grid.ts +0 -5
  184. package/src/tiles/loading-tile.ts +0 -70
@@ -0,0 +1,86 @@
1
+ import { accountIcon } from '../assets/img/icons/mediatype/account';
2
+ import { audioIcon } from '../assets/img/icons/mediatype/audio';
3
+ import { collectionIcon } from '../assets/img/icons/mediatype/collection';
4
+ import { dataIcon } from '../assets/img/icons/mediatype/data';
5
+ import { etreeIcon } from '../assets/img/icons/mediatype/etree';
6
+ import { imagesIcon } from '../assets/img/icons/mediatype/images';
7
+ import { filmIcon } from '../assets/img/icons/mediatype/film';
8
+ import { radioIcon } from '../assets/img/icons/mediatype/radio';
9
+ import { softwareIcon } from '../assets/img/icons/mediatype/software';
10
+ import { textsIcon } from '../assets/img/icons/mediatype/texts';
11
+ import { tvIcon } from '../assets/img/icons/mediatype/tv';
12
+ import { videoIcon } from '../assets/img/icons/mediatype/video';
13
+ import { webIcon } from '../assets/img/icons/mediatype/web';
14
+
15
+ export const mediatypeConfig: { [key: string]: any } = {
16
+ account: {
17
+ color: '#000000',
18
+ icon: accountIcon,
19
+ text: 'Account',
20
+ },
21
+ audio: {
22
+ color: '#8fdaef',
23
+ icon: audioIcon,
24
+ text: 'Audio',
25
+ },
26
+ collection: {
27
+ color: '#000000',
28
+ icon: collectionIcon,
29
+ text: 'Collection',
30
+ },
31
+ data: {
32
+ color: '#333333',
33
+ icon: dataIcon,
34
+ text: 'Data',
35
+ },
36
+ etree: {
37
+ color: '#3871c1',
38
+ icon: etreeIcon,
39
+ text: 'E-tree',
40
+ },
41
+ film: {
42
+ color: '#bf1b2c',
43
+ icon: filmIcon,
44
+ text: 'Film',
45
+ },
46
+ image: {
47
+ color: '#62c4a9',
48
+ icon: imagesIcon,
49
+ text: 'Image',
50
+ },
51
+ movies: {
52
+ color: '#bf1b2c',
53
+ icon: filmIcon,
54
+ text: 'Movie',
55
+ },
56
+ radio: {
57
+ color: '#8fdaef',
58
+ icon: radioIcon,
59
+ text: 'Radio',
60
+ },
61
+ software: {
62
+ color: '#80cc28',
63
+ icon: softwareIcon,
64
+ text: 'Software',
65
+ },
66
+ texts: {
67
+ color: '#f9a72b',
68
+ icon: textsIcon,
69
+ text: 'Text',
70
+ },
71
+ tv: {
72
+ color: '#f25d54',
73
+ icon: tvIcon,
74
+ text: 'TV',
75
+ },
76
+ video: {
77
+ color: '#bf1b2c',
78
+ icon: videoIcon,
79
+ text: 'Video',
80
+ },
81
+ web: {
82
+ color: '#fddd10',
83
+ icon: webIcon,
84
+ text: 'Web',
85
+ },
86
+ };
package/src/models.ts CHANGED
@@ -1,25 +1,153 @@
1
1
  import type { MediaType } from '@internetarchive/field-parsers';
2
2
 
3
3
  export interface TileModel {
4
- identifier: string;
5
- title: string;
6
4
  averageRating?: number;
5
+ collectionIdentifier?: string;
6
+ collectionName?: string;
7
+ collections: string[];
8
+ commentCount: number;
9
+ creator?: string;
10
+ creators: string[];
7
11
  dateAdded?: Date; // Date added to public search (software-defined) [from: addeddate]
8
12
  dateArchived?: Date; // Date archived (software-defined) item created on archive.org [from: publicdate]
9
- dateReviewed?: Date; // Date reviewed (user-created) most recent review [from: reviewdate]
10
13
  datePublished?: Date; // Date work published in the world (user-defined) [from: date]
11
- mediatype: MediaType;
12
- viewCount: number;
13
- itemCount: number;
14
- favCount: number;
15
- commentCount: number;
14
+ dateReviewed?: Date; // Date reviewed (user-created) most recent review [from: reviewdate]
16
15
  description?: string;
17
- collectionIdentifier?: string;
18
- collectionName?: string;
19
- creator?: string;
20
- subject?: string;
16
+ favCount: number;
17
+ identifier: string;
18
+ issue?: string;
19
+ itemCount: number;
20
+ mediatype: MediaType;
21
21
  source?: string;
22
- collections: string[];
22
+ subjects: string[];
23
+ title: string;
24
+ viewCount: number;
25
+ volume?: string;
23
26
  }
24
27
 
25
28
  export type CollectionDisplayMode = 'grid' | 'list-compact' | 'list-detail';
29
+
30
+ export type TileDisplayMode =
31
+ | 'grid'
32
+ | 'list-compact'
33
+ | 'list-detail'
34
+ | 'list-header';
35
+
36
+ /**
37
+ * This is mainly used to set the cookies for the collection display mode.
38
+ *
39
+ * It allows the user to set different modes for different contexts (collection page, search page, etc).
40
+ */
41
+ export type CollectionBrowserContext = 'collection' | 'search';
42
+
43
+ /**
44
+ * The sort fields shown in the sort filter bar
45
+ */
46
+ export enum SortField {
47
+ 'relevance' = 'relevance',
48
+ 'views' = 'views',
49
+ 'title' = 'title',
50
+ 'datearchived' = 'datearchived',
51
+ 'date' = 'date',
52
+ 'datereviewed' = 'datereviewed',
53
+ 'dateadded' = 'dateadded',
54
+ 'creator' = 'creator',
55
+ }
56
+
57
+ /**
58
+ * The metadata fields we sort by that map to the SortFields above
59
+ */
60
+ export type MetadataSortField =
61
+ | 'week'
62
+ | 'titleSorter'
63
+ | 'date'
64
+ | 'creatorSorter'
65
+ | 'publicdate'
66
+ | 'reviewdate'
67
+ | 'addeddate';
68
+
69
+ export const SortFieldDisplayName: {
70
+ [key in SortField]: string;
71
+ } = {
72
+ relevance: 'Relevance',
73
+ views: 'Views',
74
+ title: 'Title',
75
+ datearchived: 'Date Archived',
76
+ date: 'Date Published',
77
+ datereviewed: 'Date Reviewed',
78
+ dateadded: 'Date Added',
79
+ creator: 'Creator',
80
+ };
81
+
82
+ /**
83
+ * Maps the SortField above to the corresponding Metadata field in the API.
84
+ */
85
+ export const SortFieldToMetadataField: {
86
+ [key in SortField]: MetadataSortField | null;
87
+ } = {
88
+ relevance: null,
89
+ views: 'week',
90
+ title: 'titleSorter',
91
+ datearchived: 'publicdate',
92
+ date: 'date',
93
+ datereviewed: 'reviewdate',
94
+ dateadded: 'addeddate',
95
+ creator: 'creatorSorter',
96
+ };
97
+
98
+ /**
99
+ * Maps the Metadata field to the corresponding SortField field in the API.
100
+ */
101
+ export const MetadataFieldToSortField: {
102
+ [key in MetadataSortField]: SortField;
103
+ } = {
104
+ titleSorter: SortField.title,
105
+ date: SortField.date,
106
+ publicdate: SortField.datearchived,
107
+ reviewdate: SortField.datereviewed,
108
+ addeddate: SortField.dateadded,
109
+ creatorSorter: SortField.creator,
110
+ week: SortField.views,
111
+ };
112
+
113
+ export type FacetOption =
114
+ | 'subject'
115
+ | 'mediatype'
116
+ | 'language'
117
+ | 'creator'
118
+ | 'collection'
119
+ | 'year';
120
+
121
+ export type SelectedFacetState = 'selected' | 'hidden';
122
+
123
+ export type FacetState = SelectedFacetState | 'none';
124
+
125
+ export interface FacetBucket {
126
+ // for some facets, we augment the key with a display value
127
+ displayText?: string;
128
+ key: string;
129
+ count: number;
130
+ state: FacetState;
131
+ }
132
+
133
+ export interface FacetGroup {
134
+ title: string;
135
+ key: FacetOption;
136
+ buckets: FacetBucket[];
137
+ }
138
+
139
+ export type FacetValue = string;
140
+
141
+ export type SelectedFacets = Record<
142
+ FacetOption,
143
+ Record<FacetValue, SelectedFacetState>
144
+ >;
145
+
146
+ export const defaultSelectedFacets: SelectedFacets = {
147
+ subject: {},
148
+ mediatype: {},
149
+ language: {},
150
+ creator: {},
151
+ collection: {},
152
+ year: {},
153
+ };
@@ -0,0 +1,266 @@
1
+ import { SortDirection, SortParam } from '@internetarchive/search-service';
2
+ import { getCookie, setCookie } from 'typescript-cookie';
3
+ import {
4
+ MetadataFieldToSortField,
5
+ MetadataSortField,
6
+ FacetOption,
7
+ CollectionBrowserContext,
8
+ CollectionDisplayMode,
9
+ SelectedFacets,
10
+ SortField,
11
+ } from './models';
12
+
13
+ export interface RestorationState {
14
+ displayMode?: CollectionDisplayMode;
15
+ sortParam?: SortParam;
16
+ selectedSort?: SortField;
17
+ sortDirection?: SortDirection;
18
+ selectedFacets: SelectedFacets;
19
+ baseQuery?: string;
20
+ currentPage?: number;
21
+ dateRangeQueryClause?: string;
22
+ titleQuery?: string;
23
+ creatorQuery?: string;
24
+ minSelectedDate?: string;
25
+ maxSelectedDate?: string;
26
+ selectedTitleFilter?: string;
27
+ selectedCreatorFilter?: string;
28
+ }
29
+
30
+ export interface RestorationStateHandlerInterface {
31
+ persistState(state: RestorationState): void;
32
+ getRestorationState(): RestorationState;
33
+ }
34
+
35
+ export class RestorationStateHandler
36
+ implements RestorationStateHandlerInterface
37
+ {
38
+ private context: CollectionBrowserContext;
39
+
40
+ private cookieDomain = '.archive.org';
41
+
42
+ private cookieExpiration = 30;
43
+
44
+ private cookiePath = '/';
45
+
46
+ constructor(options: { context: CollectionBrowserContext }) {
47
+ this.context = options.context;
48
+ }
49
+
50
+ persistState(state: RestorationState): void {
51
+ if (state.displayMode) this.persistViewStateToCookies(state.displayMode);
52
+ this.persistQueryStateToUrl(state);
53
+ }
54
+
55
+ getRestorationState(): RestorationState {
56
+ const restorationState = this.loadQueryStateFromUrl();
57
+ const displayMode = this.loadTileViewStateFromCookies();
58
+ restorationState.displayMode = displayMode;
59
+ return restorationState;
60
+ }
61
+
62
+ private persistViewStateToCookies(displayMode: CollectionDisplayMode) {
63
+ const gridState = displayMode === 'grid' ? 'tiles' : 'lists';
64
+ setCookie(`view-${this.context}`, gridState, {
65
+ domain: this.cookieDomain,
66
+ expires: this.cookieExpiration,
67
+ path: this.cookiePath,
68
+ });
69
+ const detailsState = displayMode === 'list-detail' ? 'showdetails' : '';
70
+ setCookie(`showdetails-${this.context}`, detailsState, {
71
+ domain: this.cookieDomain,
72
+ expires: this.cookieExpiration,
73
+ path: this.cookiePath,
74
+ });
75
+ }
76
+
77
+ private loadTileViewStateFromCookies(): CollectionDisplayMode {
78
+ const viewState = getCookie(`view-${this.context}`);
79
+ const detailsState = getCookie(`showdetails-${this.context}`);
80
+ if (viewState === 'tiles' || viewState === undefined) return 'grid';
81
+ if (detailsState === 'showdetails') return 'list-detail';
82
+ return 'list-compact';
83
+ }
84
+
85
+ private persistQueryStateToUrl(state: RestorationState) {
86
+ const url = new URL(window.location.href);
87
+ const { searchParams } = url;
88
+ searchParams.delete('sort');
89
+ searchParams.delete('query');
90
+ searchParams.delete('page');
91
+ searchParams.delete('and[]');
92
+ searchParams.delete('not[]');
93
+
94
+ if (state.sortParam) {
95
+ const prefix = state.sortParam.direction === 'desc' ? '-' : '';
96
+ searchParams.set('sort', `${prefix}${state.sortParam.field}`);
97
+ }
98
+
99
+ if (state.baseQuery) {
100
+ searchParams.set('query', state.baseQuery);
101
+ }
102
+
103
+ if (state.currentPage) {
104
+ if (state.currentPage > 1) {
105
+ searchParams.set('page', state.currentPage.toString());
106
+ } else {
107
+ searchParams.delete('page');
108
+ }
109
+ }
110
+
111
+ if (state.selectedFacets) {
112
+ for (const [facetName, facetValues] of Object.entries(
113
+ state.selectedFacets
114
+ )) {
115
+ const facetEntries = Object.entries(facetValues);
116
+ // eslint-disable-next-line no-continue
117
+ if (facetEntries.length === 0) continue;
118
+ for (const [key, facetState] of facetEntries) {
119
+ const notValue = facetState === 'hidden';
120
+ const paramValue = `${facetName}:"${key}"`;
121
+ if (notValue) {
122
+ searchParams.append('not[]', paramValue);
123
+ } else {
124
+ searchParams.append('and[]', paramValue);
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
+ if (state.dateRangeQueryClause) {
131
+ searchParams.append('and[]', state.dateRangeQueryClause);
132
+ }
133
+ if (state.titleQuery) {
134
+ searchParams.append('and[]', state.titleQuery);
135
+ }
136
+ if (state.creatorQuery) {
137
+ searchParams.append('and[]', state.creatorQuery);
138
+ }
139
+
140
+ window.history.pushState(
141
+ {
142
+ sort: state.sortParam,
143
+ query: state.baseQuery,
144
+ page: state.currentPage,
145
+ and: state.selectedFacets,
146
+ not: state.selectedFacets,
147
+ dateRange: state.dateRangeQueryClause,
148
+ },
149
+ '',
150
+ url
151
+ );
152
+ }
153
+
154
+ private loadQueryStateFromUrl(): RestorationState {
155
+ const url = new URL(window.location.href);
156
+ const pageNumber = url.searchParams.get('page');
157
+ const searchQuery = url.searchParams.get('query');
158
+ const sortQuery = url.searchParams.get('sort');
159
+ const facetAnds = url.searchParams.getAll('and[]');
160
+ const facetNots = url.searchParams.getAll('not[]');
161
+
162
+ const restorationState: RestorationState = {
163
+ selectedFacets: {
164
+ subject: {},
165
+ creator: {},
166
+ mediatype: {},
167
+ language: {},
168
+ collection: {},
169
+ year: {},
170
+ },
171
+ };
172
+
173
+ if (pageNumber) {
174
+ const parsed = parseInt(pageNumber, 10);
175
+ restorationState.currentPage = parsed;
176
+ } else {
177
+ restorationState.currentPage = 1;
178
+ }
179
+ if (searchQuery) {
180
+ restorationState.baseQuery = searchQuery;
181
+ }
182
+ if (sortQuery) {
183
+ // check for two different sort formats: `date desc` and `-date`
184
+ const hasSpace = sortQuery.indexOf(' ') > -1;
185
+ if (hasSpace) {
186
+ const [field, direction] = sortQuery.split(' ');
187
+ const metadataField =
188
+ MetadataFieldToSortField[field as MetadataSortField];
189
+ if (metadataField) {
190
+ restorationState.selectedSort = metadataField;
191
+ }
192
+ if (direction === 'desc' || direction === 'asc') {
193
+ restorationState.sortDirection = direction as SortDirection;
194
+ }
195
+ } else {
196
+ const sortDirection = sortQuery.startsWith('-') ? 'desc' : 'asc';
197
+ const sortField = sortQuery.startsWith('-')
198
+ ? sortQuery.slice(1)
199
+ : sortQuery;
200
+ const metadataField =
201
+ MetadataFieldToSortField[sortField as MetadataSortField];
202
+ if (metadataField) restorationState.selectedSort = metadataField;
203
+ restorationState.sortDirection = sortDirection as SortDirection;
204
+ }
205
+ }
206
+ if (facetAnds) {
207
+ facetAnds.forEach(and => {
208
+ const [field, value] = and.split(':');
209
+ const unQuotedValue = this.stripQuotes(value);
210
+
211
+ switch (field) {
212
+ case 'year': {
213
+ const [minDate, maxDate] = value.split(' TO ');
214
+ // we have two potential ways of filtering by date:
215
+ // the range with "date TO date" or the single date with "date"
216
+ // this is checking for the range case and if we don't have those, fall
217
+ // back to the single date case
218
+ if (minDate && maxDate) {
219
+ restorationState.minSelectedDate = minDate.substring(
220
+ 1,
221
+ minDate.length
222
+ );
223
+ restorationState.maxSelectedDate = maxDate.substring(
224
+ 0,
225
+ maxDate.length - 1
226
+ );
227
+ restorationState.dateRangeQueryClause = `year:${value}`;
228
+ } else {
229
+ restorationState.selectedFacets[field as FacetOption][
230
+ unQuotedValue
231
+ ] = 'selected';
232
+ }
233
+ break;
234
+ }
235
+ case 'firstTitle':
236
+ restorationState.selectedTitleFilter = value;
237
+ break;
238
+ case 'firstCreator':
239
+ restorationState.selectedCreatorFilter = value;
240
+ break;
241
+ default:
242
+ restorationState.selectedFacets[field as FacetOption][
243
+ unQuotedValue
244
+ ] = 'selected';
245
+ }
246
+ });
247
+ }
248
+ if (facetNots) {
249
+ facetNots.forEach(not => {
250
+ const [field, value] = not.split(':');
251
+ const unQuotedValue = this.stripQuotes(value);
252
+ restorationState.selectedFacets[field as FacetOption][unQuotedValue] =
253
+ 'hidden';
254
+ });
255
+ }
256
+ return restorationState;
257
+ }
258
+
259
+ // remove optional opening and closing quotes from a string
260
+ private stripQuotes(value: string): string {
261
+ if (value.startsWith('"') && value.endsWith('"')) {
262
+ return value.substring(1, value.length - 1);
263
+ }
264
+ return value;
265
+ }
266
+ }
@@ -3,7 +3,7 @@ import { customElement, property } from 'lit/decorators.js';
3
3
 
4
4
  @customElement('alpha-bar')
5
5
  export class AlphaBar extends LitElement {
6
- @property({ type: String }) selectedLetter?: string;
6
+ @property({ type: String }) selectedLetter: string | null = null;
7
7
 
8
8
  private get selectedUppercaseLetter(): string | undefined {
9
9
  return this.selectedLetter?.toUpperCase();
@@ -40,7 +40,7 @@ export class AlphaBar extends LitElement {
40
40
 
41
41
  private letterClicked(letter: string) {
42
42
  if (letter === this.selectedUppercaseLetter) {
43
- this.selectedLetter = undefined;
43
+ this.selectedLetter = null;
44
44
  } else {
45
45
  this.selectedLetter = letter;
46
46
  }
@@ -72,10 +72,16 @@ export class AlphaBar extends LitElement {
72
72
  justify-content: space-between;
73
73
  }
74
74
 
75
+ ul li {
76
+ flex: 1;
77
+ text-align: center;
78
+ max-width: 2.5rem;
79
+ }
80
+
75
81
  a {
76
82
  color: #333;
77
83
  text-decoration: none;
78
- padding: 0.5rem 0.7rem;
84
+ padding: 0.5rem 0;
79
85
  display: block;
80
86
  }
81
87
 
@@ -0,0 +1,5 @@
1
+ import { svg } from 'lit';
2
+
3
+ export const compactIcon = svg`
4
+ <svg viewBox="0 0 100 100" xmlns = "http://www.w3.org/2000/svg" > <path d="m96.9964435 6h-93.90621462c-.91561615 0-1.65899868-.29021372-2.23014758-.87064117-.57114891-.58042745-.85783455-1.3048369-.86005692-2.17322835 0-.81214848.28668564-1.50731158.86005692-2.08548931.57337127-.57817773 1.3167538-.86839145 2.23014758-.87064117h93.90621462c.800053 0 1.5012105.29021372 2.1034726.87064117.602262.58042745.9022819 1.27559055.9000718 2.08548931 0 .86839145-.3000321 1.5928009-.9000718 2.17322835-.6000398.58042745-1.3011973.87064117-2.1034726.87064117zm-93.90621462 9.6666667h93.90621462c.800053 0 1.5012105.2861891 2.1034726.8585673.602262.5723782.9022819 1.2579009.9000718 2.0565682 0 .8563487-.3000321 1.5851326-.9000718 2.1863516-.6000398.6012189-1.3011973.9007192-2.1034726.8985129h-93.90621462c-.91561615 0-1.65899868-.2995125-2.23014758-.8985129-.57114891-.5990005-.85783455-1.3277843-.86005692-2.1863516 0-.8008858.28668564-1.4864086.86005692-2.0565682.57337127-.5701597 1.3167538-.8563488 2.23014758-.8585673zm0 15.6700431h93.90621462c.800053 0 1.5012105.303883 2.1034726.9116489.602262.6077659.9022819 1.2886888.9000718 2.0427687-.0022346.7540799-.3022545 1.4496342-.9000718 2.0866629-.5978174.6370287-1.2989749.955543-2.1034726.955543h-93.90621462c-.85783454 0-1.58788286-.3038829-2.19014494-.9116488s-.90228193-1.3179516-.90007182-2.1305571c.00223463-.8126055.30225448-1.5081599.90007182-2.0866629.59781734-.5785031 1.32786566-.8688802 2.19014494-.8711312zm0 15.6632902h93.90621462c.800053 0 1.5012105.2861892 2.1034726.8585675.602262.5723783.9022819 1.2290603.9000718 1.9700462 0 .7986674-.3144775 1.5274514-.943408 2.186352-.6289306.6589006-1.3156427.9872417-2.0601364.9850343h-93.90621462c-.85783454 0-1.58788286-.3139318-2.19014494-.9417731-.60226208-.6278414-.90228193-1.3699365-.90007182-2.2262854 0-.7986674.2866979-1.4697699.86006918-2.0133074.57337127-.5435376 1.3167538-.8153063 2.23014758-.8153063zm0 15.6666667h93.90621462c.800053 0 1.5012105.3038947 2.1034726.9116593.602262.6077647.9022819 1.3472117.9000718 2.218341 0 .7540783-.3000321 1.4203685-.9000718 1.9988703-.6000398.5785019-1.3011973.8688784-2.1034726.8711294h-93.90621462c-.91561615 0-1.65899868-.2757451-2.23014758-.8272352-.57114891-.5514902-.85783455-1.2324117-.86005692-2.0427645 0-.8688784.28668564-1.6083253.86005692-2.218341.57337127-.6100156 1.3167538-.9138979 2.23014758-.9116593zm0 15.6666666h93.90621462c.800053 0 1.5012105.3038948 2.1034726.9116594.602262.6077646.9022819 1.3472116.9000718 2.2183409 0 .7540784-.3000321 1.4203685-.9000718 1.9988704-.6000398.5785019-1.3011973.8688784-2.1034726.8711293h-93.90621462c-.91561615 0-1.65899868-.275745-2.23014758-.8272352-.57114891-.5514901-.85783455-1.2324116-.86005692-2.0427645 0-.8688783.28668564-1.6083253.86005692-2.2183409.57337127-.6100156 1.3167538-.9138979 2.23014758-.9116594zm0 15.6666667h93.90621462c.800053 0 1.5012105.3038947 2.1034726.9116594.602262.6077646.9022819 1.3472116.9000718 2.2183409 0 .7540784-.3000321 1.4203685-.9000718 1.9988704-.6000398.5785019-1.3011973.8688783-2.1034726.8711293h-93.90621462c-.91561615 0-1.65899868-.2757451-2.23014758-.8272352-.57114891-.5514901-.85783455-1.2324116-.86005692-2.0427645 0-.8688783.28668564-1.6083253.86005692-2.2183409.57337127-.6100156 1.3167538-.913898 2.23014758-.9116594z" /> </svg>
5
+ `;
@@ -1,5 +1,5 @@
1
1
  import { svg } from 'lit';
2
2
 
3
3
  export const listIcon = svg`
4
- <svg viewBox="0 0 100 100" xmlns = "http://www.w3.org/2000/svg" > <path d="m96.9964435 21h-93.90621462c-.91561615 0-1.65899868-.2902137-2.23014758-.8706412-.57114891-.5804274-.85783455-1.3048369-.86005692-2.1732283 0-.8121485.28668564-1.5073116.86005692-2.0854893.57337127-.5781778 1.3167538-.8683915 2.23014758-.8706412h93.90621462c.800053 0 1.5012105.2902137 2.1034726.8706412.602262.5804274.9022819 1.2755905.9000718 2.0854893 0 .8683914-.3000321 1.5928009-.9000718 2.1732283-.6000398.5804275-1.3011973.8706412-2.1034726.8706412zm-93.90621462 10h93.90621462c.800053 0 1.5012105.2861891 2.1034726.8585673.602262.5723782.9022819 1.2579009.9000718 2.0565682 0 .8563488-.3000321 1.5851326-.9000718 2.1863516-.6000398.601219-1.3011973.9007192-2.1034726.8985129h-93.90621462c-.91561615 0-1.65899868-.2995125-2.23014758-.8985129-.57114891-.5990004-.85783455-1.3277843-.86005692-2.1863516 0-.8008858.28668564-1.4864085.86005692-2.0565682.57337127-.5701597 1.3167538-.8563488 2.23014758-.8585673zm0 16.0033765h93.90621462c.800053 0 1.5012105.3038829 2.1034726.9116488.602262.6077659.9022819 1.2886888.9000718 2.0427687-.0022346.7540799-.3022545 1.4496342-.9000718 2.0866629-.5978174.6370287-1.2989749.9555431-2.1034726.9555431h-93.90621462c-.85783454 0-1.58788286-.3038829-2.19014494-.9116488s-.90228193-1.3179517-.90007182-2.1305572c.00223463-.8126055.30225448-1.5081598.90007182-2.0866629s1.32786566-.8688801 2.19014494-.8711311zm0 15.9966235h93.90621462c.800053 0 1.5012105.2861892 2.1034726.8585675.602262.5723783.9022819 1.2290603.9000718 1.9700462 0 .7986674-.3144775 1.5274514-.943408 2.186352-.6289306.6589006-1.3156427.9872417-2.0601364.9850343h-93.90621462c-.85783454 0-1.58788286-.3139318-2.19014494-.9417731-.60226208-.6278414-.90228193-1.3699365-.90007182-2.2262854 0-.7986674.2866979-1.4697699.86006918-2.0133074.57337127-.5435376 1.3167538-.8153063 2.23014758-.8153063zm0 16h93.90621462c.800053 0 1.5012105.3038947 2.1034726.9116594.602262.6077646.9022819 1.3472116.9000718 2.2183409 0 .7540784-.3000321 1.4203685-.9000718 1.9988704-.6000398.5785019-1.3011973.8688783-2.1034726.8711293h-93.90621462c-.91561615 0-1.65899868-.2757451-2.23014758-.8272352-.57114891-.5514901-.85783455-1.2324116-.86005692-2.0427645 0-.8688783.28668564-1.6083253.86005692-2.2183409.57337127-.6100156 1.3167538-.913898 2.23014758-.9116594z" /> </svg>
4
+ <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><g fill="#000" fill-rule="nonzero"><path d="m97.8975061 6h-65.7343743c-.6409315 0-1.1612995-.29021372-1.5611039-.87064117-.3998043-.58042745-.6004844-1.3048369-.60204-2.17322835 0-.81214848.20068-1.50731158.60204-2.08548931.4013601-.57817773.921728-.86839145 1.5611039-.87064117h65.7343743c.5600372 0 1.0508477.29021372 1.4724313.87064117s.6315976 1.27559055.6300505 2.08548931c0 .86839145-.2100226 1.5928009-.6300505 2.17322835-.420028.58042745-.9108384.87064117-1.4724313.87064117z"/><path d="m97.8975061 61h-65.7343743c-.6409315 0-1.1612995-.2902137-1.5611039-.8706412-.3998043-.5804274-.6004844-1.3048369-.60204-2.1732283 0-.8121485.20068-1.5073116.60204-2.0854893.4013601-.5781778.921728-.8683915 1.5611039-.8706412h65.7343743c.5600372 0 1.0508477.2902137 1.4724313.8706412.4215836.5804274.6315976 1.2755905.6300505 2.0854893 0 .8683914-.2100226 1.5928009-.6300505 2.1732283-.420028.5804275-.9108384.8706412-1.4724313.8706412z"/><path d="m97.8975061 19h-65.7343743c-.6409315 0-1.1612995-.2902137-1.5611039-.8706412-.3998043-.5804274-.6004844-1.3048369-.60204-2.1732283 0-.8121485.20068-1.5073116.60204-2.0854893.4013601-.5781778.921728-.8683915 1.5611039-.8706412h65.7343743c.5600372 0 1.0508477.2902137 1.4724313.8706412.4215836.5804274.6315976 1.2755905.6300505 2.0854893 0 .8683914-.2100226 1.5928009-.6300505 2.1732283-.420028.5804275-.9108384.8706412-1.4724313.8706412z"/><path d="m97.8975061 74h-65.7343743c-.6409315 0-1.1612995-.2902137-1.5611039-.8706412-.3998043-.5804274-.6004844-1.3048369-.60204-2.1732283 0-.8121485.20068-1.5073116.60204-2.0854893.4013601-.5781778.921728-.8683915 1.5611039-.8706412h65.7343743c.5600372 0 1.0508477.2902137 1.4724313.8706412.4215836.5804274.6315976 1.2755905.6300505 2.0854893 0 .8683914-.2100226 1.5928009-.6300505 2.1732283-.420028.5804275-.9108384.8706412-1.4724313.8706412z"/><path d="m97.8975061 32h-65.7343743c-.6409315 0-1.1612995-.2902137-1.5611039-.8706412-.3998043-.5804274-.6004844-1.3048369-.60204-2.1732283 0-.8121485.20068-1.5073116.60204-2.0854893.4013601-.5781778.921728-.8683915 1.5611039-.8706412h65.7343743c.5600372 0 1.0508477.2902137 1.4724313.8706412.4215836.5804274.6315976 1.2755905.6300505 2.0854893 0 .8683914-.2100226 1.5928009-.6300505 2.1732283-.420028.5804275-.9108384.8706412-1.4724313.8706412z"/><path d="m97.8975061 87h-65.7343743c-.6409315 0-1.1612995-.2902137-1.5611039-.8706412-.3998043-.5804274-.6004844-1.3048369-.60204-2.1732283 0-.8121485.20068-1.5073116.60204-2.0854893.4013601-.5781778.921728-.8683915 1.5611039-.8706412h65.7343743c.5600372 0 1.0508477.2902137 1.4724313.8706412.4215836.5804274.6315976 1.2755905.6300505 2.0854893 0 .8683914-.2100226 1.5928009-.6300505 2.1732283-.420028.5804275-.9108384.8706412-1.4724313.8706412z"/><path d="m97.8975061 45h-65.7343743c-.6409315 0-1.1612995-.2902137-1.5611039-.8706412-.3998043-.5804274-.6004844-1.3048369-.60204-2.1732283 0-.8121485.20068-1.5073116.60204-2.0854893.4013601-.5781778.921728-.8683915 1.5611039-.8706412h65.7343743c.5600372 0 1.0508477.2902137 1.4724313.8706412.4215836.5804274.6315976 1.2755905.6300505 2.0854893 0 .8683914-.2100226 1.5928009-.6300505 2.1732283-.420028.5804275-.9108384.8706412-1.4724313.8706412z"/><path d="m97.8975061 100h-65.7343743c-.6409315 0-1.1612995-.2902137-1.5611039-.8706412-.3998043-.5804274-.6004844-1.3048369-.60204-2.1732283 0-.8121485.20068-1.5073116.60204-2.0854893.4013601-.5781778.921728-.8683915 1.5611039-.8706412h65.7343743c.5600372 0 1.0508477.2902137 1.4724313.8706412.4215836.5804274.6315976 1.2755905.6300505 2.0854893 0 .8683914-.2100226 1.5928009-.6300505 2.1732283-.420028.5804275-.9108384.8706412-1.4724313.8706412z"/><path d="m0 0h25v25h-25z"/><path d="m0 55h25v25h-25z"/></g></svg>
5
5
  `;
@@ -0,0 +1,5 @@
1
+ import { svg } from 'lit';
2
+
3
+ export const tileIcon = svg`
4
+ <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path d="m64 54v46h-28v-46zm36 0v46h-28v-46zm-72 0v46h-28v-46zm36-54v46h-28v-46zm36 0v46h-28v-46zm-72 0v46h-28v-46z"/></svg>
5
+ `;