@internetarchive/collection-browser 0.0.1-alpha.2 → 0.0.1-alpha.22

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 (183) hide show
  1. package/demo/app-root.ts +24 -158
  2. package/dist/demo/app-root.d.ts +2 -16
  3. package/dist/demo/app-root.js +23 -141
  4. package/dist/demo/app-root.js.map +1 -1
  5. package/dist/src/assets/img/icons/chevron.d.ts +2 -0
  6. package/dist/src/assets/img/icons/chevron.js +4 -0
  7. package/dist/src/assets/img/icons/chevron.js.map +1 -0
  8. package/dist/src/assets/img/icons/eye-closed.d.ts +2 -0
  9. package/dist/src/assets/img/icons/eye-closed.js +5 -0
  10. package/dist/src/assets/img/icons/eye-closed.js.map +1 -0
  11. package/dist/src/assets/img/icons/eye.d.ts +2 -0
  12. package/dist/src/assets/img/icons/eye.js +5 -0
  13. package/dist/src/assets/img/icons/eye.js.map +1 -0
  14. package/dist/src/assets/img/icons/mediatype/account.d.ts +1 -2
  15. package/dist/src/assets/img/icons/mediatype/account.js +5 -4
  16. package/dist/src/assets/img/icons/mediatype/account.js.map +1 -1
  17. package/dist/src/assets/img/icons/mediatype/audio.js +7 -4
  18. package/dist/src/assets/img/icons/mediatype/audio.js.map +1 -1
  19. package/dist/src/assets/img/icons/mediatype/collection.js +7 -4
  20. package/dist/src/assets/img/icons/mediatype/collection.js.map +1 -1
  21. package/dist/src/assets/img/icons/mediatype/etree.js +10 -5
  22. package/dist/src/assets/img/icons/mediatype/etree.js.map +1 -1
  23. package/dist/src/assets/img/icons/mediatype/film.js +2 -1
  24. package/dist/src/assets/img/icons/mediatype/film.js.map +1 -1
  25. package/dist/src/assets/img/icons/mediatype/images.js +9 -6
  26. package/dist/src/assets/img/icons/mediatype/images.js.map +1 -1
  27. package/dist/src/assets/img/icons/mediatype/software.js +9 -6
  28. package/dist/src/assets/img/icons/mediatype/software.js.map +1 -1
  29. package/dist/src/assets/img/icons/mediatype/texts.js +9 -6
  30. package/dist/src/assets/img/icons/mediatype/texts.js.map +1 -1
  31. package/dist/src/assets/img/icons/mediatype/tv.js +10 -5
  32. package/dist/src/assets/img/icons/mediatype/tv.js.map +1 -1
  33. package/dist/src/assets/img/icons/mediatype/video.js +10 -6
  34. package/dist/src/assets/img/icons/mediatype/video.js.map +1 -1
  35. package/dist/src/assets/img/icons/mediatype/web.js +9 -6
  36. package/dist/src/assets/img/icons/mediatype/web.js.map +1 -1
  37. package/dist/src/async-collection-name.d.ts +11 -0
  38. package/dist/src/async-collection-name.js +38 -0
  39. package/dist/src/async-collection-name.js.map +1 -0
  40. package/dist/src/collection-browser.d.ts +55 -17
  41. package/dist/src/collection-browser.js +466 -106
  42. package/dist/src/collection-browser.js.map +1 -1
  43. package/dist/src/collection-facets.d.ts +24 -5
  44. package/dist/src/collection-facets.js +300 -78
  45. package/dist/src/collection-facets.js.map +1 -1
  46. package/dist/src/collection-name-cache.d.ts +18 -0
  47. package/dist/src/collection-name-cache.js +89 -0
  48. package/dist/src/collection-name-cache.js.map +1 -0
  49. package/dist/src/mediatype-icon.js +10 -3
  50. package/dist/src/mediatype-icon.js.map +1 -1
  51. package/dist/src/models.d.ts +72 -14
  52. package/dist/src/models.js +57 -1
  53. package/dist/src/models.js.map +1 -1
  54. package/dist/src/restoration-state-handler.d.ts +37 -0
  55. package/dist/src/restoration-state-handler.js +177 -0
  56. package/dist/src/restoration-state-handler.js.map +1 -0
  57. package/dist/src/sort-filter-bar/alpha-bar.d.ts +1 -2
  58. package/dist/src/sort-filter-bar/alpha-bar.js +19 -9
  59. package/dist/src/sort-filter-bar/alpha-bar.js.map +1 -1
  60. package/dist/src/sort-filter-bar/img/compact.d.ts +1 -0
  61. package/dist/src/sort-filter-bar/img/compact.js +5 -0
  62. package/dist/src/sort-filter-bar/img/compact.js.map +1 -0
  63. package/dist/src/sort-filter-bar/img/grid.d.ts +1 -0
  64. package/dist/src/sort-filter-bar/img/grid.js +5 -0
  65. package/dist/src/sort-filter-bar/img/grid.js.map +1 -0
  66. package/dist/src/sort-filter-bar/img/list.d.ts +1 -0
  67. package/dist/src/sort-filter-bar/img/list.js +5 -0
  68. package/dist/src/sort-filter-bar/img/list.js.map +1 -0
  69. package/dist/src/sort-filter-bar/img/sort-triangle.d.ts +1 -0
  70. package/dist/src/sort-filter-bar/img/sort-triangle.js +5 -0
  71. package/dist/src/sort-filter-bar/img/sort-triangle.js.map +1 -0
  72. package/dist/src/sort-filter-bar/img/tile.d.ts +1 -0
  73. package/dist/src/sort-filter-bar/img/tile.js +5 -0
  74. package/dist/src/sort-filter-bar/img/tile.js.map +1 -0
  75. package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +65 -11
  76. package/dist/src/sort-filter-bar/sort-filter-bar.js +453 -142
  77. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
  78. package/dist/src/tiles/grid/collection-tile.js +1 -2
  79. package/dist/src/tiles/grid/collection-tile.js.map +1 -1
  80. package/dist/src/tiles/grid/item-tile.d.ts +4 -0
  81. package/dist/src/tiles/grid/item-tile.js +134 -45
  82. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  83. package/dist/src/tiles/list/tile-list-compact-header.d.ts +11 -0
  84. package/dist/src/tiles/list/tile-list-compact-header.js +79 -0
  85. package/dist/src/tiles/list/tile-list-compact-header.js.map +1 -0
  86. package/dist/src/tiles/list/tile-list-compact.d.ts +1 -0
  87. package/dist/src/tiles/list/tile-list-compact.js +122 -31
  88. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  89. package/dist/src/tiles/list/tile-list-detail.d.ts +0 -10
  90. package/dist/src/tiles/list/tile-list-detail.js +6 -159
  91. package/dist/src/tiles/list/tile-list-detail.js.map +1 -1
  92. package/dist/src/tiles/list/tile-list.d.ts +19 -6
  93. package/dist/src/tiles/list/tile-list.js +240 -108
  94. package/dist/src/tiles/list/tile-list.js.map +1 -1
  95. package/dist/src/tiles/tile-dispatcher.d.ts +8 -1
  96. package/dist/src/tiles/tile-dispatcher.js +46 -11
  97. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  98. package/dist/src/utils/format-date.js +1 -2
  99. package/dist/src/utils/format-date.js.map +1 -1
  100. package/dist/test/{utils/format-string.test.d.ts → collection-name-cache.test.d.ts} +0 -0
  101. package/dist/test/collection-name-cache.test.js +158 -0
  102. package/dist/test/collection-name-cache.test.js.map +1 -0
  103. package/dist/test/mocks/mock-search-response.d.ts +5 -0
  104. package/dist/test/mocks/mock-search-response.js +62 -0
  105. package/dist/test/mocks/mock-search-response.js.map +1 -0
  106. package/dist/test/mocks/mock-search-service.d.ts +13 -0
  107. package/dist/test/mocks/mock-search-service.js +20 -0
  108. package/dist/test/mocks/mock-search-service.js.map +1 -0
  109. package/package.json +9 -4
  110. package/src/assets/img/icons/chevron.ts +4 -0
  111. package/src/assets/img/icons/eye-closed.ts +5 -0
  112. package/src/assets/img/icons/eye.ts +5 -0
  113. package/src/assets/img/icons/mediatype/account.ts +5 -4
  114. package/src/assets/img/icons/mediatype/audio.ts +7 -4
  115. package/src/assets/img/icons/mediatype/collection.ts +7 -4
  116. package/src/assets/img/icons/mediatype/etree.ts +10 -5
  117. package/src/assets/img/icons/mediatype/film.ts +2 -1
  118. package/src/assets/img/icons/mediatype/images.ts +9 -6
  119. package/src/assets/img/icons/mediatype/software.ts +9 -6
  120. package/src/assets/img/icons/mediatype/texts.ts +9 -6
  121. package/src/assets/img/icons/mediatype/tv.ts +10 -5
  122. package/src/assets/img/icons/mediatype/video.ts +10 -6
  123. package/src/assets/img/icons/mediatype/web.ts +9 -6
  124. package/src/collection-browser.ts +490 -105
  125. package/src/collection-facets.ts +325 -109
  126. package/src/mediatype-icon.ts +10 -3
  127. package/src/models.ts +139 -14
  128. package/src/restoration-state-handler.ts +234 -0
  129. package/src/sort-filter-bar/alpha-bar.ts +19 -9
  130. package/src/sort-filter-bar/img/compact.ts +5 -0
  131. package/src/sort-filter-bar/img/list.ts +5 -0
  132. package/src/sort-filter-bar/img/sort-triangle.ts +5 -0
  133. package/src/sort-filter-bar/img/tile.ts +5 -0
  134. package/src/sort-filter-bar/sort-filter-bar.ts +499 -149
  135. package/src/tiles/grid/collection-tile.ts +1 -2
  136. package/src/tiles/grid/item-tile.ts +138 -56
  137. package/src/tiles/list/tile-list-compact-header.ts +75 -0
  138. package/src/tiles/list/tile-list-compact.ts +209 -0
  139. package/src/tiles/list/tile-list.ts +261 -110
  140. package/src/tiles/tile-dispatcher.ts +51 -11
  141. package/src/utils/format-date.ts +1 -2
  142. package/dist/src/assets/img/icons/audio.d.ts +0 -1
  143. package/dist/src/assets/img/icons/audio.js +0 -9
  144. package/dist/src/assets/img/icons/audio.js.map +0 -1
  145. package/dist/src/assets/img/icons/collection.d.ts +0 -1
  146. package/dist/src/assets/img/icons/collection.js +0 -9
  147. package/dist/src/assets/img/icons/collection.js.map +0 -1
  148. package/dist/src/assets/img/icons/etree.d.ts +0 -1
  149. package/dist/src/assets/img/icons/etree.js +0 -9
  150. package/dist/src/assets/img/icons/etree.js.map +0 -1
  151. package/dist/src/assets/img/icons/images.d.ts +0 -1
  152. package/dist/src/assets/img/icons/images.js +0 -10
  153. package/dist/src/assets/img/icons/images.js.map +0 -1
  154. package/dist/src/assets/img/icons/mediatype/etree copy.d.ts +0 -1
  155. package/dist/src/assets/img/icons/mediatype/etree copy.js +0 -9
  156. package/dist/src/assets/img/icons/mediatype/etree copy.js.map +0 -1
  157. package/dist/src/assets/img/icons/mediatype/livemusic.d.ts +0 -1
  158. package/dist/src/assets/img/icons/mediatype/livemusic.js +0 -7
  159. package/dist/src/assets/img/icons/mediatype/livemusic.js.map +0 -1
  160. package/dist/src/assets/img/icons/mediatype/photos.d.ts +0 -1
  161. package/dist/src/assets/img/icons/mediatype/photos.js +0 -7
  162. package/dist/src/assets/img/icons/mediatype/photos.js.map +0 -1
  163. package/dist/src/assets/img/icons/software.d.ts +0 -1
  164. package/dist/src/assets/img/icons/software.js +0 -10
  165. package/dist/src/assets/img/icons/software.js.map +0 -1
  166. package/dist/src/assets/img/icons/texts.d.ts +0 -1
  167. package/dist/src/assets/img/icons/texts.js +0 -10
  168. package/dist/src/assets/img/icons/texts.js.map +0 -1
  169. package/dist/src/assets/img/icons/tv.d.ts +0 -1
  170. package/dist/src/assets/img/icons/tv.js +0 -9
  171. package/dist/src/assets/img/icons/tv.js.map +0 -1
  172. package/dist/src/assets/img/icons/video.d.ts +0 -1
  173. package/dist/src/assets/img/icons/video.js +0 -10
  174. package/dist/src/assets/img/icons/video.js.map +0 -1
  175. package/dist/src/assets/img/icons/web.d.ts +0 -1
  176. package/dist/src/assets/img/icons/web.js +0 -10
  177. package/dist/src/assets/img/icons/web.js.map +0 -1
  178. package/dist/src/utils/format-string.d.ts +0 -2
  179. package/dist/src/utils/format-string.js +0 -7
  180. package/dist/src/utils/format-string.js.map +0 -1
  181. package/dist/test/utils/format-string.test.js +0 -17
  182. package/dist/test/utils/format-string.test.js.map +0 -1
  183. package/src/assets/img/icons/mediatype/foo.svg +0 -5
@@ -1,16 +1,46 @@
1
- import { SortParam } from '@internetarchive/search-service';
2
- import { LitElement, html, css, nothing, PropertyValues } from 'lit';
3
- import { customElement, property, state } from 'lit/decorators.js';
4
- import { CollectionDisplayMode } from '../models';
1
+ import {
2
+ LitElement,
3
+ html,
4
+ css,
5
+ nothing,
6
+ PropertyValues,
7
+ TemplateResult,
8
+ } from 'lit';
9
+ import { customElement, property, query, state } from 'lit/decorators.js';
10
+ import {
11
+ SharedResizeObserverInterface,
12
+ SharedResizeObserverResizeHandlerInterface,
13
+ } from '@internetarchive/shared-resize-observer';
14
+ import {
15
+ CollectionDisplayMode,
16
+ SortField,
17
+ SortFieldDisplayName,
18
+ } from '../models';
5
19
  import './alpha-bar';
6
20
 
21
+ import { sortIcon } from './img/sort-triangle';
22
+ import { tileIcon } from './img/tile';
23
+ import { listIcon } from './img/list';
24
+ import { compactIcon } from './img/compact';
25
+
7
26
  @customElement('sort-filter-bar')
8
- export class SortFilterBar extends LitElement {
9
- @property({ type: String }) displayMode: CollectionDisplayMode = 'grid';
27
+ export class SortFilterBar
28
+ extends LitElement
29
+ implements SharedResizeObserverResizeHandlerInterface
30
+ {
31
+ @property({ type: String }) displayMode?: CollectionDisplayMode;
32
+
33
+ @property({ type: String }) sortDirection: 'asc' | 'desc' | null = null;
34
+
35
+ @property({ type: String }) selectedSort: SortField = SortField.relevance;
36
+
37
+ @property({ type: String }) selectedTitleFilter: string | null = null;
38
+
39
+ @property({ type: String }) selectedCreatorFilter: string | null = null;
10
40
 
11
- @property({ type: String }) sortDirection: 'asc' | 'desc' = 'desc';
41
+ @property({ type: Boolean }) showRelevance: boolean = true;
12
42
 
13
- @property({ type: String }) sortField = 'week';
43
+ @property({ type: Object }) resizeObserver?: SharedResizeObserverInterface;
14
44
 
15
45
  @state() titleSelectorVisible: boolean = false;
16
46
 
@@ -18,90 +48,43 @@ export class SortFilterBar extends LitElement {
18
48
 
19
49
  @state() dateSortSelectorVisible = false;
20
50
 
51
+ @state() desktopSelectorBarWidth = 0;
52
+
53
+ @state() selectorBarContainerWidth = 0;
54
+
55
+ @query('#desktop-sort-selector')
56
+ private desktopSortSelector!: HTMLUListElement;
57
+
58
+ @query('#sort-selector-container')
59
+ private sortSelectorContainer!: HTMLDivElement;
60
+
21
61
  render() {
22
62
  return html`
23
- <div id="sort-bar">
24
- <div id="sort-selector">
25
- <ul>
26
- <li>
27
- <button
28
- @click=${() => {
29
- this.sortDirection = 'desc';
30
- }}
31
- >
32
- Desc
33
- </button>
34
- <button
35
- @click=${() => {
36
- this.sortDirection = 'asc';
37
- }}
38
- >
39
- Asc</button
40
- >Sort By
41
- </li>
42
- <li>
43
- <button
44
- @click=${() => {
45
- this.sortField = 'week';
46
- }}
47
- >
48
- Views
49
- </button>
50
- </li>
51
- <li>
52
- <button
53
- @click=${() => {
54
- this.titleSelectorVisible = !this.titleSelectorVisible;
55
- this.sortField = 'titleSorter';
56
- }}
57
- >
58
- Title
59
- </button>
60
- </li>
61
- <li>
62
- <button
63
- @click=${() => {
64
- this.dateSortSelectorVisible = !this.dateSortSelectorVisible;
65
- }}
66
- >
67
- Date Archived
68
- </button>
69
- </li>
70
- <li>
71
- <button
72
- @click=${() => {
73
- this.creatorSelectorVisible = !this.creatorSelectorVisible;
74
- this.sortField = 'creatorSorter';
75
- }}
76
- >
77
- Creator
78
- </button>
79
- </li>
80
- </ul>
81
- </div>
63
+ <div id="container">
64
+ ${this.titleSelectorVisible || this.selectedSort === 'title'
65
+ ? this.titleSelectorBar
66
+ : nothing}
67
+ ${this.creatorSelectorVisible || this.selectedSort === 'creator'
68
+ ? this.creatorSelectorBar
69
+ : nothing}
70
+
71
+ <div id="sort-bar">
72
+ <div id="sort-direction-container">
73
+ ${this.sortDirectionSelectorTemplate}
74
+ </div>
82
75
 
83
- <div id="display-style">
84
- <ul>
85
- ${this.displayMode !== 'grid'
86
- ? html`<li>
87
- <input type="checkbox" @click=${this.detailSelected} />
88
- Details
89
- </li>`
90
- : nothing}
91
-
92
- <li>
93
- <button id="grid-button" @click=${this.gridSelected}>Grid</button>
94
- </li>
95
- <li>
96
- <button id="list-button" @click=${this.listSelected}>List</button>
97
- </li>
98
- </ul>
76
+ <div id="sort-selector-container">
77
+ ${this.mobileSortSelectorTemplate}
78
+ ${this.desktopSortSelectorTemplate}
79
+ </div>
80
+
81
+ <div id="display-style-selector">${this.displayOptionTemplate}</div>
99
82
  </div>
100
- </div>
101
83
 
102
- ${this.dateSortSelectorVisible ? this.dateSortSelector : nothing}
103
- ${this.titleSelectorVisible ? this.titleSelectorBar : nothing}
104
- ${this.creatorSelectorVisible ? this.creatorSelectorBar : nothing}
84
+ ${this.dateSortSelectorVisible ? this.dateSortSelector : nothing}
85
+
86
+ <div id="bottom-shadow"></div>
87
+ </div>
105
88
  `;
106
89
  }
107
90
 
@@ -110,66 +93,318 @@ export class SortFilterBar extends LitElement {
110
93
  this.displayModeChanged();
111
94
  }
112
95
 
113
- if (changed.has('sortDirection') || changed.has('sortField')) {
114
- this.sortChanged();
96
+ if (changed.has('selectedSort') && this.sortDirection === null) {
97
+ this.sortDirection = 'desc';
98
+ }
99
+
100
+ if (changed.has('selectedTitleFilter') && this.selectedTitleFilter) {
101
+ this.titleSelectorVisible = true;
102
+ }
103
+
104
+ if (changed.has('selectedCreatorFilter') && this.selectedCreatorFilter) {
105
+ this.creatorSelectorVisible = true;
106
+ }
107
+
108
+ if (changed.has('resizeObserver')) {
109
+ const oldObserver = changed.get(
110
+ 'resizeObserver'
111
+ ) as SharedResizeObserverInterface;
112
+ if (oldObserver) this.disconnectResizeObserver(oldObserver);
113
+ this.setupResizeObserver();
114
+ }
115
+ }
116
+
117
+ disconnectedCallback(): void {
118
+ if (this.resizeObserver) {
119
+ this.disconnectResizeObserver(this.resizeObserver);
120
+ }
121
+ }
122
+
123
+ private disconnectResizeObserver(
124
+ resizeObserver: SharedResizeObserverInterface
125
+ ) {
126
+ resizeObserver.removeObserver({
127
+ target: this.sortSelectorContainer,
128
+ handler: this,
129
+ });
130
+
131
+ resizeObserver.removeObserver({
132
+ target: this.desktopSortSelector,
133
+ handler: this,
134
+ });
135
+ }
136
+
137
+ private setupResizeObserver() {
138
+ if (!this.resizeObserver) return;
139
+ this.resizeObserver.addObserver({
140
+ target: this.sortSelectorContainer,
141
+ handler: this,
142
+ });
143
+
144
+ this.resizeObserver.addObserver({
145
+ target: this.desktopSortSelector,
146
+ handler: this,
147
+ });
148
+ }
149
+
150
+ private get mobileSelectorVisible() {
151
+ return this.selectorBarContainerWidth - 10 < this.desktopSelectorBarWidth;
152
+ }
153
+
154
+ handleResize(entry: ResizeObserverEntry): void {
155
+ if (entry.target === this.desktopSortSelector)
156
+ this.desktopSelectorBarWidth = entry.contentRect.width;
157
+ else if (entry.target === this.sortSelectorContainer)
158
+ this.selectorBarContainerWidth = entry.contentRect.width;
159
+ }
160
+
161
+ private get sortDirectionSelectorTemplate() {
162
+ return html`
163
+ <div id="sort-direction-selector">
164
+ <button
165
+ id="sort-ascending-btn"
166
+ class="sort-button ${this.sortDirection === 'asc' ? 'selected' : ''}"
167
+ ?disabled=${this.selectedSort === 'relevance'}
168
+ @click=${() => {
169
+ this.setSortDirections('asc');
170
+ }}
171
+ >
172
+ ${sortIcon}
173
+ </button>
174
+ <button
175
+ id="sort-descending-btn"
176
+ class="sort-button ${this.sortDirection === 'desc' ? 'selected' : ''}"
177
+ ?disabled=${this.selectedSort === 'relevance'}
178
+ @click=${() => {
179
+ this.setSortDirections('desc');
180
+ }}
181
+ >
182
+ ${sortIcon}
183
+ </button>
184
+ </div>
185
+ `;
186
+ }
187
+
188
+ private get desktopSortSelectorTemplate() {
189
+ return html`
190
+ <ul
191
+ id="desktop-sort-selector"
192
+ class=${this.mobileSelectorVisible ? 'hidden' : 'visible'}
193
+ >
194
+ <li id="sort-by-text">Sort By</li>
195
+ <li>
196
+ ${this.showRelevance
197
+ ? this.getSortDisplayOption(SortField.relevance)
198
+ : nothing}
199
+ </li>
200
+ <li>${this.getSortDisplayOption(SortField.views)}</li>
201
+ <li>${this.getSortDisplayOption(SortField.title)}</li>
202
+ <li>
203
+ ${this.getSortDisplayOption(SortField.datearchived, {
204
+ additionalClickEvent: () => {
205
+ this.dateSortSelectorVisible = !this.dateSortSelectorVisible;
206
+ },
207
+ displayName: html`${this.dateSortField}`,
208
+ isSelected: () => this.dateOptionSelected,
209
+ })}
210
+ </li>
211
+ <li>
212
+ ${this.getSortDisplayOption(SortField.creator, {
213
+ additionalClickEvent: () => {
214
+ this.creatorSelectorVisible = !this.creatorSelectorVisible;
215
+ },
216
+ })}
217
+ </li>
218
+ </ul>
219
+ `;
220
+ }
221
+
222
+ /**
223
+ * This generates each of the sort option links.
224
+ *
225
+ * It manages the display value and the selected state of the option.
226
+ *
227
+ * @param sortField
228
+ * @param options {
229
+ * additionalClickEvent?: () => void; If this is provided, it will also be called when the option is clicked.
230
+ * displayName?: TemplateResult; The name to display for the option. Defaults to the sortField display name.
231
+ * isSelected?: () => boolean; A function that returns true if the option is selected. Defaults to the selectedSort === sortField.
232
+ * }
233
+ * @returns
234
+ */
235
+ private getSortDisplayOption(
236
+ sortField: SortField,
237
+ options?: {
238
+ additionalClickEvent?: (e: Event) => void;
239
+ isSelected?: () => boolean;
240
+ displayName?: TemplateResult;
115
241
  }
242
+ ): TemplateResult {
243
+ const isSelected =
244
+ options?.isSelected ?? (() => this.selectedSort === sortField);
245
+ const displayName = options?.displayName ?? SortFieldDisplayName[sortField];
246
+ return html`
247
+ <a
248
+ href="#"
249
+ @click=${(e: Event) => {
250
+ e.preventDefault();
251
+ this.setSelectedSort(sortField);
252
+ options?.additionalClickEvent?.(e);
253
+ }}
254
+ class=${isSelected() ? 'selected' : ''}
255
+ >
256
+ ${displayName}
257
+ </a>
258
+ `;
259
+ }
260
+
261
+ private get mobileSortSelectorTemplate() {
262
+ return html`
263
+ <select
264
+ id="mobile-sort-selector"
265
+ @change=${this.mobileSortChanged}
266
+ class=${this.mobileSelectorVisible ? 'visible' : 'hidden'}
267
+ >
268
+ ${Object.keys(SortField).map(
269
+ field => html`
270
+ <option value="${field}" ?selected=${this.selectedSort === field}>
271
+ ${SortFieldDisplayName[field as SortField]}
272
+ </option>
273
+ `
274
+ )}
275
+ </select>
276
+ `;
277
+ }
278
+
279
+ private mobileSortChanged(e: Event) {
280
+ const target = e.target as HTMLSelectElement;
281
+ this.setSelectedSort(target.value as SortField);
282
+ }
283
+
284
+ private get displayOptionTemplate() {
285
+ return html`
286
+ <ul>
287
+ <li>
288
+ <button
289
+ id="grid-button"
290
+ @click=${() => {
291
+ this.displayMode = 'grid';
292
+ }}
293
+ class=${this.displayMode === 'grid' ? 'active' : ''}
294
+ >
295
+ ${tileIcon}
296
+ </button>
297
+ </li>
298
+ <li>
299
+ <button
300
+ id="grid-button"
301
+ @click=${() => {
302
+ this.displayMode = 'list-detail';
303
+ }}
304
+ class=${this.displayMode === 'list-detail' ? 'active' : ''}
305
+ >
306
+ ${listIcon}
307
+ </button>
308
+ </li>
309
+ <li>
310
+ <button
311
+ id="list-button"
312
+ @click=${() => {
313
+ this.displayMode = 'list-compact';
314
+ }}
315
+ class=${this.displayMode === 'list-compact' ? 'active' : ''}
316
+ >
317
+ ${compactIcon}
318
+ </button>
319
+ </li>
320
+ </ul>
321
+ `;
116
322
  }
117
323
 
118
324
  private get dateSortSelector() {
119
325
  return html`
120
326
  <div id="date-sort-selector">
121
327
  <ul>
122
- <li>
123
- <button
124
- @click=${() => {
125
- this.sortField = 'publicdate';
126
- }}
127
- >
128
- Date Archived
129
- </button>
130
- </li>
131
- <li>
132
- <button
133
- @click=${() => {
134
- this.sortField = 'date';
135
- }}
136
- >
137
- Date Published
138
- </button>
139
- </li>
140
- <li>
141
- <button
142
- @click=${() => {
143
- this.sortField = 'reviewdate';
144
- }}
145
- >
146
- Date Reviewed
147
- </button>
148
- </li>
149
- <li>
150
- <button
151
- @click=${() => {
152
- this.sortField = 'addeddate';
153
- }}
154
- >
155
- Date Added
156
- </button>
157
- </li>
328
+ <li>${this.getDateSortButton(SortField.datearchived)}</li>
329
+ <li>${this.getDateSortButton(SortField.datepublished)}</li>
330
+ <li>${this.getDateSortButton(SortField.datereviewed)}</li>
331
+ <li>${this.getDateSortButton(SortField.dateadded)}</li>
158
332
  </ul>
159
333
  </div>
160
334
  `;
161
335
  }
162
336
 
337
+ private getDateSortButton(sortField: SortField) {
338
+ return html`
339
+ <button
340
+ @click=${() => {
341
+ this.selectDateSort(sortField);
342
+ }}
343
+ >
344
+ ${SortFieldDisplayName[sortField]}
345
+ </button>
346
+ `;
347
+ }
348
+
349
+ private selectDateSort(sortField: SortField) {
350
+ this.dateSortSelectorVisible = false;
351
+ this.setSelectedSort(sortField);
352
+ }
353
+
354
+ private setSortDirections(sortDirection: 'asc' | 'desc') {
355
+ this.sortDirection = sortDirection;
356
+ this.emitSortChangedEvent();
357
+ }
358
+
359
+ private setSelectedSort(sort: SortField) {
360
+ this.selectedSort = sort;
361
+ this.emitSortChangedEvent();
362
+ }
363
+
364
+ /**
365
+ * There are four date sort options.
366
+ *
367
+ * This checks to see if the current sort is one of them.
368
+ *
369
+ * @readonly
370
+ * @private
371
+ * @type {boolean}
372
+ * @memberof SortFilterBar
373
+ */
374
+ private get dateOptionSelected(): boolean {
375
+ return [
376
+ 'datearchived',
377
+ 'datepublished',
378
+ 'datereviewed',
379
+ 'dateadded',
380
+ ].includes(this.selectedSort);
381
+ }
382
+
383
+ /**
384
+ * The display name of the current date field
385
+ *
386
+ * @readonly
387
+ * @private
388
+ * @type {string}
389
+ * @memberof SortFilterBar
390
+ */
391
+ private get dateSortField(): string {
392
+ const defaultSort = SortFieldDisplayName[SortField.datearchived];
393
+ return this.dateOptionSelected
394
+ ? SortFieldDisplayName[this.selectedSort] ?? defaultSort
395
+ : defaultSort;
396
+ }
397
+
163
398
  private get titleSelectorBar() {
164
399
  return html` <alpha-bar
165
- headline="Title Starts With"
400
+ .selectedLetter=${this.selectedTitleFilter}
166
401
  @letterChanged=${this.titleLetterChanged}
167
402
  ></alpha-bar>`;
168
403
  }
169
404
 
170
405
  private get creatorSelectorBar() {
171
406
  return html` <alpha-bar
172
- headline="Creator Starts With"
407
+ .selectedLetter=${this.selectedCreatorFilter}
173
408
  @letterChanged=${this.creatorLetterChanged}
174
409
  ></alpha-bar>`;
175
410
  }
@@ -192,20 +427,6 @@ export class SortFilterBar extends LitElement {
192
427
  this.dispatchEvent(event);
193
428
  }
194
429
 
195
- private gridSelected() {
196
- this.displayMode = 'grid';
197
- }
198
-
199
- private listSelected() {
200
- this.displayMode = 'list-compact';
201
- }
202
-
203
- private detailSelected(e: Event) {
204
- this.displayMode = (e.target as HTMLInputElement).checked
205
- ? 'list-detail'
206
- : 'list-compact';
207
- }
208
-
209
430
  private displayModeChanged() {
210
431
  const event = new CustomEvent('displayModeChanged', {
211
432
  detail: { displayMode: this.displayMode },
@@ -213,10 +434,15 @@ export class SortFilterBar extends LitElement {
213
434
  this.dispatchEvent(event);
214
435
  }
215
436
 
216
- private sortChanged() {
217
- const sort = new SortParam(this.sortField, this.sortDirection);
218
- const event = new CustomEvent('sortChanged', {
219
- detail: { sort },
437
+ private emitSortChangedEvent() {
438
+ const event = new CustomEvent<{
439
+ selectedSort: SortField;
440
+ sortDirection: 'asc' | 'desc' | null;
441
+ }>('sortChanged', {
442
+ detail: {
443
+ selectedSort: this.selectedSort,
444
+ sortDirection: this.sortDirection,
445
+ },
220
446
  });
221
447
  this.dispatchEvent(event);
222
448
  }
@@ -225,6 +451,24 @@ export class SortFilterBar extends LitElement {
225
451
  #sort-bar {
226
452
  display: flex;
227
453
  justify-content: space-between;
454
+ border: 1px solid rgb(232, 232, 232);
455
+ align-items: center;
456
+ padding: 0.5rem 1.5rem;
457
+ }
458
+
459
+ #sort-direction-container {
460
+ flex: 0;
461
+ }
462
+
463
+ #sort-by-text {
464
+ text-transform: uppercase;
465
+ }
466
+
467
+ #bottom-shadow {
468
+ height: 1px;
469
+ width: 100%;
470
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
471
+ background-color: #bbb;
228
472
  }
229
473
 
230
474
  ul {
@@ -236,21 +480,127 @@ export class SortFilterBar extends LitElement {
236
480
  }
237
481
 
238
482
  li {
239
- padding: 0.5rem 0 0.5rem 0;
483
+ padding: 0;
484
+ }
485
+
486
+ .sort-button {
487
+ background: none;
488
+ color: inherit;
489
+ border: none;
490
+ padding: 0;
491
+ cursor: pointer;
492
+ outline: inherit;
493
+ width: 12px;
494
+ height: 12px;
495
+ opacity: 0.5;
496
+ }
497
+
498
+ .sort-button.selected {
499
+ opacity: 1;
500
+ }
501
+
502
+ .sort-button:disabled {
503
+ opacity: 0.25;
504
+ cursor: default;
505
+ }
506
+
507
+ #show-details {
508
+ text-transform: uppercase;
509
+ cursor: pointer;
510
+ display: flex;
511
+ }
512
+
513
+ #show-details input {
514
+ margin-right: 0.5rem;
515
+ flex: 0 0 12px;
516
+ }
517
+
518
+ #sort-descending-btn {
519
+ transform: rotate(180deg);
520
+ }
521
+
522
+ #sort-direction-selector {
523
+ display: flex;
524
+ flex-direction: column;
525
+ gap: 3px;
526
+ margin-right: 1rem;
527
+ }
528
+
529
+ #sort-selector-container {
530
+ flex: 1;
531
+ }
532
+
533
+ /*
534
+ we move the desktop sort selector offscreen instead of display: none
535
+ because we need to observe the width of it vs its container to determine
536
+ if it's wide enough to display the desktop version and if you displY: none,
537
+ the width becomes 0
538
+ */
539
+ #desktop-sort-selector.hidden {
540
+ position: absolute;
541
+ top: -9999px;
542
+ left: -9999px;
543
+ }
544
+
545
+ #mobile-sort-selector.hidden {
546
+ display: none;
547
+ }
548
+
549
+ #desktop-sort-selector {
550
+ display: inline-flex;
240
551
  }
241
552
 
242
- #sort-selector li::after {
553
+ #desktop-sort-selector li {
554
+ display: flex;
555
+ align-items: center;
556
+ }
557
+
558
+ #desktop-sort-selector li a {
559
+ text-decoration: none;
560
+ text-transform: uppercase;
561
+ font-size: 1.4rem;
562
+ color: #333;
563
+ }
564
+
565
+ #desktop-sort-selector li a.selected {
566
+ font-weight: bold;
567
+ }
568
+
569
+ #desktop-sort-selector li::after {
243
570
  content: '•';
244
571
  padding-left: 1rem;
245
572
  padding-right: 1rem;
246
573
  }
247
574
 
248
- #sort-selector li:first-child::after {
575
+ #desktop-sort-selector li:first-child::after {
249
576
  content: '';
250
577
  }
251
578
 
252
- #sort-selector li:last-child::after {
579
+ #desktop-sort-selector li:last-child::after {
253
580
  content: '';
254
581
  }
582
+
583
+ #display-style-selector {
584
+ flex: 0;
585
+ }
586
+
587
+ #display-style-selector button {
588
+ background: none;
589
+ color: inherit;
590
+ border: none;
591
+ appearance: none;
592
+ cursor: pointer;
593
+ -webkit-appearance: none;
594
+ opacity: 0.5;
595
+ }
596
+
597
+ #display-style-selector button.active {
598
+ opacity: 1;
599
+ }
600
+
601
+ #display-style-selector button svg {
602
+ width: 24px;
603
+ height: 24px;
604
+ }
255
605
  `;
256
606
  }