@asd20/ui-next 2.1.0 → 2.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.2.1](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.2.0...ui-next-v2.2.1) (2026-04-07)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * fix file header text being dropped ([cb07594](https://github.com/academydistrict20/asd20-ui-next/commit/cb075946768dd58fb39ceed08ae406a1c36a532f))
9
+
10
+ # [2.2.0](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.1.0...ui-next-v2.2.0) (2026-04-07)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * fix plublishing contract regressions ([9ce287b](https://github.com/academydistrict20/asd20-ui-next/commit/9ce287b8f5719db6fdf1467fe4423fc60bbbc19c))
16
+
17
+
18
+ ### Features
19
+
20
+ * add pagination to file list template ([cf9829c](https://github.com/academydistrict20/asd20-ui-next/commit/cf9829cd4776e2fd0c24cee52092397cdc6a16e8))
21
+
3
22
  # [2.1.0](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.0.29...ui-next-v2.1.0) (2026-04-06)
4
23
 
5
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asd20/ui-next",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "private": false,
5
5
  "description": "ASD20 UI component library for Vue 3.",
6
6
  "license": "MIT",
@@ -118,6 +118,27 @@ import {
118
118
  shouldShowExternalIcon,
119
119
  } from '../../../helpers/linkPolicy'
120
120
 
121
+ function resolveRouter(vm) {
122
+ const internal = vm && vm.$
123
+ const context = internal && internal.ctx
124
+ if (context && context.$router) {
125
+ return context.$router
126
+ }
127
+
128
+ const parentContext = internal && internal.parent && internal.parent.ctx
129
+ if (parentContext && parentContext.$router) {
130
+ return parentContext.$router
131
+ }
132
+
133
+ return (
134
+ internal &&
135
+ internal.appContext &&
136
+ internal.appContext.config &&
137
+ internal.appContext.config.globalProperties &&
138
+ internal.appContext.config.globalProperties.$router
139
+ )
140
+ }
141
+
121
142
  export default {
122
143
  name: 'Asd20ListItem',
123
144
 
@@ -183,7 +204,8 @@ export default {
183
204
  return this.link || this.to
184
205
  },
185
206
  usesRouterNavigation() {
186
- if (!this.$router || !this.to) return false
207
+ const router = resolveRouter(this)
208
+ if (!router || !this.to) return false
187
209
  if (typeof this.to === 'string') return this.to.startsWith('/')
188
210
  return typeof this.to === 'object'
189
211
  },
@@ -206,9 +228,11 @@ export default {
206
228
 
207
229
  methods: {
208
230
  onClick(e) {
209
- if (this.usesRouterNavigation) {
231
+ const router = resolveRouter(this)
232
+
233
+ if (this.usesRouterNavigation && router && typeof router.push === 'function') {
210
234
  e.preventDefault()
211
- this.$router.push(this.to)
235
+ router.push(this.to)
212
236
  }
213
237
  this.$emit('click', e)
214
238
  },
@@ -28,10 +28,10 @@
28
28
  @input="input"
29
29
  />
30
30
  <div
31
- v-if="extra || !!$slots.default"
31
+ v-if="extra"
32
32
  class="asd20-search-field__extra"
33
33
  >
34
- {{ extra }}<slot />
34
+ {{ extra }}
35
35
  </div>
36
36
  </div>
37
37
  </template>
@@ -141,35 +141,14 @@ export default {
141
141
  opacity: 1;
142
142
  color: var(--color__primary-t50);
143
143
  }
144
- &:focus + .asd20-search-field__extra {
145
- background: var(--website-page__alternate-background-t40);
146
- }
147
144
  }
148
145
 
149
- .asd20-search-field__extra {
150
- transition: background asd20-speed(1) ease;
151
- align-self: stretch;
152
- display: flex;
153
- align-items: center;
154
- justify-content: center;
155
- font-size: 0.875rem;
146
+ &__extra {
147
+ flex-shrink: 0;
148
+ padding: 0 space(0.5);
149
+ color: var(--color__primary);
150
+ font-size: space(0.5);
156
151
  white-space: nowrap;
157
- color: var(--color__primary-t50);
158
- box-shadow: -1px 0 0 0 var(--color__tertiary);
159
- }
160
- &--large {
161
- & > .asd20-icon {
162
- width: space(1.5);
163
- height: space(1.5);
164
- margin: 0;
165
- }
166
- input {
167
- margin-left: space(0);
168
- font-size: 2rem;
169
- line-height: 1.5;
170
- padding: 0 space(0.25) 0 space(0.5);
171
- height: space(3);
172
- }
173
152
  }
174
153
  }
175
154
  </style>
@@ -3,10 +3,10 @@
3
3
  v-bind="$attrs"
4
4
  ref="listComponent"
5
5
  role="region"
6
- :aria-label="title"
6
+ :aria-label="resolvedTitle"
7
7
  class="asd20-file-list"
8
8
  :max-height="maxHeight"
9
- :headline="title"
9
+ :headline="resolvedTitle"
10
10
  :icon="icon"
11
11
  :column-width="640"
12
12
  >
@@ -19,11 +19,53 @@
19
19
  v-model="searchQuery"
20
20
  class="asd20-file-list__search"
21
21
  :placeholder="searchPlaceholder"
22
- :extra="resultSummary"
23
22
  :id-tag="searchIdTag"
24
23
  />
25
24
  </div>
26
25
  </template>
26
+ <template #after-header>
27
+ <div
28
+ v-if="showTopPagination"
29
+ class="asd20-file-list__pagination"
30
+ >
31
+ <p class="asd20-file-list__pagination-summary">
32
+ {{ paginationSummary }}
33
+ </p>
34
+ <div
35
+ v-if="paginate && totalPages > 1"
36
+ class="asd20-file-list__pagination-actions"
37
+ >
38
+ <asd20-button
39
+ class="asd20-file-list__pagination-button"
40
+ icon="chevron"
41
+ label="Prev"
42
+ :icon-angle="-90"
43
+ size="md"
44
+ horizontal
45
+ transparent
46
+ centered
47
+ opposite
48
+ :disabled="currentPage === 1"
49
+ @click="prevPage"
50
+ />
51
+ <p class="asd20-file-list__pagination-page">
52
+ Page {{ currentPage }} of {{ totalPages }}
53
+ </p>
54
+ <asd20-button
55
+ class="asd20-file-list__pagination-button"
56
+ icon="chevron"
57
+ label="Next"
58
+ :icon-angle="90"
59
+ size="md"
60
+ horizontal
61
+ transparent
62
+ centered
63
+ :disabled="currentPage === totalPages"
64
+ @click="nextPage"
65
+ />
66
+ </div>
67
+ </div>
68
+ </template>
27
69
  <div
28
70
  v-for="category in categorizedFileItems"
29
71
  :key="category.name"
@@ -74,6 +116,7 @@ import Asd20List from '../../../components/organisms/Asd20WidgetList'
74
116
  import mapFilesToListItems from '../../../helpers/mapFilesToListItems'
75
117
  import Asd20ListItem from '../../../components/molecules/Asd20ListItem'
76
118
  import Asd20ListCategory from '../../../components/molecules/Asd20ListCategory'
119
+ import Asd20Button from '../../../components/atoms/Asd20Button'
77
120
  import Asd20SearchField from '../../../components/molecules/Asd20SearchField'
78
121
 
79
122
  function normalizeSearchText(value = '') {
@@ -88,6 +131,7 @@ export default {
88
131
  name: 'Asd20FileList',
89
132
 
90
133
  components: {
134
+ Asd20Button,
91
135
  Asd20List,
92
136
  Asd20ListItem,
93
137
  Asd20ListCategory,
@@ -97,6 +141,7 @@ export default {
97
141
  props: {
98
142
  files: { type: Array, default: () => [] },
99
143
  title: { type: String, default: '' },
144
+ headline: { type: String, default: '' },
100
145
  icon: { type: String, default: '' },
101
146
  url: { type: String, default: '' },
102
147
  groupByOwner: { type: Boolean, default: false },
@@ -107,6 +152,8 @@ export default {
107
152
  maxHeight: { type: String, default: '320px' },
108
153
  searchable: { type: Boolean, default: false },
109
154
  searchPlaceholder: { type: String, default: 'Search files' },
155
+ paginate: { type: Boolean, default: false },
156
+ pageSize: { type: Number, default: 50 },
110
157
  initialVisibleCount: { type: Number, default: 0 },
111
158
  visibleCountStep: { type: Number, default: 50 },
112
159
  },
@@ -114,10 +161,15 @@ export default {
114
161
  data: () => ({
115
162
  loadedFiles: [],
116
163
  searchQuery: '',
164
+ currentPage: 1,
117
165
  visibleCount: 0,
118
166
  }),
119
167
 
120
168
  computed: {
169
+ resolvedTitle() {
170
+ return this.title || this.headline
171
+ },
172
+
121
173
  computedFiles() {
122
174
  return (this.files || []).concat(this.loadedFiles)
123
175
  },
@@ -146,6 +198,25 @@ export default {
146
198
  return this.filteredFiles.length
147
199
  },
148
200
 
201
+ resolvedPageSize() {
202
+ return this.pageSize > 0 ? this.pageSize : 50
203
+ },
204
+
205
+ totalPages() {
206
+ if (!this.paginate || !this.filteredFilesCount) return 1
207
+ return Math.max(1, Math.ceil(this.filteredFilesCount / this.resolvedPageSize))
208
+ },
209
+
210
+ paginationStart() {
211
+ if (!this.filteredFilesCount) return 0
212
+ return (this.currentPage - 1) * this.resolvedPageSize + 1
213
+ },
214
+
215
+ paginationEnd() {
216
+ if (!this.filteredFilesCount) return 0
217
+ return Math.min(this.currentPage * this.resolvedPageSize, this.filteredFilesCount)
218
+ },
219
+
149
220
  hasActiveSearch() {
150
221
  return this.searchTerms.length > 0
151
222
  },
@@ -187,16 +258,6 @@ export default {
187
258
  : this.initialVisibleCount || 50
188
259
  },
189
260
 
190
- resultSummary() {
191
- if (!this.searchable) return ''
192
- if (!this.totalFilesCount) return '0 files'
193
- if (this.hasActiveSearch) {
194
- return `${this.filteredFilesCount} of ${this.totalFilesCount}`
195
- }
196
-
197
- return `${this.totalFilesCount} files`
198
- },
199
-
200
261
  footerSummary() {
201
262
  if (!this.totalFilesCount) return ''
202
263
 
@@ -220,6 +281,7 @@ export default {
220
281
 
221
282
  shouldShowFooter() {
222
283
  return (
284
+ !this.paginate &&
223
285
  this.totalFilesCount > 0 &&
224
286
  (this.canShowMore || this.hasActiveSearch || this.hasLargeResultSet)
225
287
  )
@@ -231,7 +293,7 @@ export default {
231
293
  },
232
294
 
233
295
  searchIdTag() {
234
- return normalizeSearchText(this.title).replace(/\s+/g, '-')
296
+ return normalizeSearchText(this.resolvedTitle).replace(/\s+/g, '-')
235
297
  },
236
298
 
237
299
  emptyStateMessage() {
@@ -241,7 +303,35 @@ export default {
241
303
  return 'No files available.'
242
304
  },
243
305
 
244
- categorizedFileItems() {
306
+ showTopPagination() {
307
+ return this.paginate || this.hasActiveSearch || !!this.initialVisibleCount
308
+ },
309
+
310
+ paginationSummary() {
311
+ if (!this.filteredFilesCount) {
312
+ return this.hasActiveSearch ? 'Showing 0 matching files.' : 'Showing 0 files.'
313
+ }
314
+
315
+ if (this.paginate) {
316
+ if (this.hasActiveSearch) {
317
+ return `Showing ${this.paginationStart}-${this.paginationEnd} of ${this.filteredFilesCount} matching files.`
318
+ }
319
+
320
+ return `Showing ${this.paginationStart}-${this.paginationEnd} of ${this.filteredFilesCount} files.`
321
+ }
322
+
323
+ if (this.canShowMore) {
324
+ return `Showing ${this.visibleFiles.length} of ${this.filteredFilesCount} files.`
325
+ }
326
+
327
+ if (this.hasActiveSearch) {
328
+ return `Showing ${this.filteredFilesCount} matching files.`
329
+ }
330
+
331
+ return `Showing ${this.filteredFilesCount} files.`
332
+ },
333
+
334
+ groupedFileItems() {
245
335
  if (this.groupByDate) {
246
336
  return this.fileItemsGroupedByDate
247
337
  } else if (this.groupByOwner) {
@@ -254,6 +344,39 @@ export default {
254
344
  return this.fileItemsGroupedByCategory
255
345
  },
256
346
 
347
+ paginatedGroupEntries() {
348
+ const entries = this.groupedFileItems.flatMap(group =>
349
+ group.items.map(item => ({
350
+ name: group.name,
351
+ item,
352
+ }))
353
+ )
354
+
355
+ if (!this.paginate) return entries
356
+
357
+ const startIndex = (this.currentPage - 1) * this.resolvedPageSize
358
+ return entries.slice(startIndex, startIndex + this.resolvedPageSize)
359
+ },
360
+
361
+ categorizedFileItems() {
362
+ if (!this.paginate) return this.groupedFileItems
363
+
364
+ return this.paginatedGroupEntries.reduce((groups, entry) => {
365
+ const existingGroup = groups.find(group => group.name === entry.name)
366
+
367
+ if (existingGroup) {
368
+ existingGroup.items.push(entry.item)
369
+ return groups
370
+ }
371
+
372
+ groups.push({
373
+ name: entry.name,
374
+ items: [entry.item],
375
+ })
376
+ return groups
377
+ }, [])
378
+ },
379
+
257
380
  fileItemsGroupedByCategory() {
258
381
  return this.visibleFiles
259
382
  .reduce((a, c) => {
@@ -270,21 +393,19 @@ export default {
270
393
  .map(c => {
271
394
  return {
272
395
  name: c,
273
- items: mapFilesToListItems(
274
- this.visibleFiles
275
- .map(f => ({
276
- ...f,
277
- categories:
278
- f.categories && f.categories.length > 0
279
- ? f.categories
280
- : ['Uncategorized'],
281
- }))
282
- .filter(f => f.categories.indexOf(c) > -1)
283
- )
284
- .sort((a, b) =>
285
- a.label > b.label ? 1 : b.label > a.label ? -1 : 0
396
+ items: this.sortMappedFileItems(
397
+ mapFilesToListItems(
398
+ this.visibleFiles
399
+ .map(f => ({
400
+ ...f,
401
+ categories:
402
+ f.categories && f.categories.length > 0
403
+ ? f.categories
404
+ : ['Uncategorized'],
405
+ }))
406
+ .filter(f => f.categories.indexOf(c) > -1)
286
407
  )
287
- .map(t => ({
408
+ ).map(t => ({
288
409
  ...t,
289
410
  bordered: false,
290
411
  alignTop: false,
@@ -311,21 +432,19 @@ export default {
311
432
  .map(c => {
312
433
  return {
313
434
  name: c,
314
- items: mapFilesToListItems(
315
- this.visibleFiles
316
- .map(f => ({
317
- ...f,
318
- categories:
319
- f.categories && f.categories.length > 0
320
- ? f.categories
321
- : ['Uncategorized'],
322
- }))
323
- .filter(f => f.categories.indexOf(c) > -1)
324
- )
325
- .sort((a, b) =>
326
- a.label > b.label ? 1 : b.label > a.label ? -1 : 0
435
+ items: this.sortMappedFileItems(
436
+ mapFilesToListItems(
437
+ this.visibleFiles
438
+ .map(f => ({
439
+ ...f,
440
+ categories:
441
+ f.categories && f.categories.length > 0
442
+ ? f.categories
443
+ : ['Uncategorized'],
444
+ }))
445
+ .filter(f => f.categories.indexOf(c) > -1)
327
446
  )
328
- .map(t => ({
447
+ ).map(t => ({
329
448
  ...t,
330
449
  bordered: false,
331
450
  alignTop: false,
@@ -349,18 +468,16 @@ export default {
349
468
  .map(t => {
350
469
  return {
351
470
  name: t,
352
- items: mapFilesToListItems(
353
- this.visibleFiles
354
- .map(f => ({
355
- ...f,
356
- tags: f.tags && f.tags.length > 0 ? f.tags : ['Untagged'],
357
- }))
358
- .filter(f => f.tags.indexOf(t) > -1)
359
- )
360
- .sort((a, b) =>
361
- a.label > b.label ? 1 : b.label > a.label ? -1 : 0
471
+ items: this.sortMappedFileItems(
472
+ mapFilesToListItems(
473
+ this.visibleFiles
474
+ .map(f => ({
475
+ ...f,
476
+ tags: f.tags && f.tags.length > 0 ? f.tags : ['Untagged'],
477
+ }))
478
+ .filter(f => f.tags.indexOf(t) > -1)
362
479
  )
363
- .map(t => ({
480
+ ).map(t => ({
364
481
  ...t,
365
482
  bordered: false,
366
483
  alignTop: false,
@@ -382,15 +499,13 @@ export default {
382
499
  return {
383
500
  name: d,
384
501
  unix: new Date(d).valueOf(),
385
- items: mapFilesToListItems(
386
- this.visibleFiles.filter(
387
- f => new Date(f.lastModifiedDateTime).toLocaleDateString() === d
388
- )
389
- )
390
- .sort((a, b) =>
391
- a.label > b.label ? 1 : b.label > a.label ? -1 : 0
502
+ items: this.sortMappedFileItems(
503
+ mapFilesToListItems(
504
+ this.visibleFiles.filter(
505
+ f => new Date(f.lastModifiedDateTime).toLocaleDateString() === d
506
+ )
392
507
  )
393
- .map(t => ({
508
+ ).map(t => ({
394
509
  ...t,
395
510
  bordered: false,
396
511
  alignTop: false,
@@ -405,7 +520,9 @@ export default {
405
520
  fileItemsGroupedByOwner() {
406
521
  return [
407
522
  {
408
- items: mapFilesToListItems(this.visibleFiles).map(t => ({
523
+ items: this.sortMappedFileItems(
524
+ mapFilesToListItems(this.visibleFiles)
525
+ ).map(t => ({
409
526
  ...t,
410
527
  bordered: false,
411
528
  alignTop: false,
@@ -420,6 +537,7 @@ export default {
420
537
  watch: {
421
538
  files: {
422
539
  handler() {
540
+ this.currentPage = 1
423
541
  this.resetVisibleCount()
424
542
  this.$nextTick(() => {
425
543
  // console.log('Change in Files detected:', this.files)
@@ -431,16 +549,24 @@ export default {
431
549
  immediate: true,
432
550
  },
433
551
  searchQuery() {
552
+ this.currentPage = 1
434
553
  this.resetVisibleCount()
435
554
  },
436
555
  },
437
556
 
438
557
  mounted() {
558
+ this.currentPage = 1
439
559
  this.resetVisibleCount()
440
560
  if (this.url) this.loadFiles()
441
561
  },
442
562
 
443
563
  methods: {
564
+ nextPage() {
565
+ this.currentPage = Math.min(this.currentPage + 1, this.totalPages)
566
+ },
567
+ prevPage() {
568
+ this.currentPage = Math.max(this.currentPage - 1, 1)
569
+ },
444
570
  resetVisibleCount() {
445
571
  this.visibleCount = this.initialVisibleCount > 0
446
572
  ? this.initialVisibleCount
@@ -472,6 +598,13 @@ export default {
472
598
  const haystack = this.getFileSearchTokens(file)
473
599
  return this.searchTerms.every(term => haystack.includes(term))
474
600
  },
601
+ sortMappedFileItems(items = []) {
602
+ return [...items].sort((a, b) => {
603
+ const left = String(a.label || '').toLowerCase()
604
+ const right = String(b.label || '').toLowerCase()
605
+ return left.localeCompare(right)
606
+ })
607
+ },
475
608
  // Expose the checkForOverflow method from the asd20viewport component
476
609
  // Expose the handleResize method from the asd20list component
477
610
  checkForOverflow() {
@@ -503,7 +636,7 @@ export default {
503
636
  .asd20-file-list__toolbar {
504
637
  display: flex;
505
638
  align-items: center;
506
- gap: space(0.25);
639
+ justify-content: flex-end;
507
640
  }
508
641
 
509
642
  .asd20-file-list__search {
@@ -521,10 +654,6 @@ export default {
521
654
  font-size: 1rem;
522
655
  min-height: 40px;
523
656
  }
524
-
525
- :deep(.asd20-search-field__extra) {
526
- padding: 0 space(0.5) 0 space(0.75);
527
- }
528
657
  }
529
658
 
530
659
  .asd20-file-list__category-group {
@@ -537,6 +666,29 @@ export default {
537
666
  }
538
667
  }
539
668
 
669
+ .asd20-file-list__pagination {
670
+ display: flex;
671
+ align-items: center;
672
+ justify-content: space-between;
673
+ flex-wrap: wrap;
674
+ margin: 0;
675
+ }
676
+
677
+ .asd20-file-list__pagination-summary {
678
+ margin: 0;
679
+ }
680
+
681
+ .asd20-file-list__pagination-actions {
682
+ display: flex;
683
+ flex-wrap: wrap;
684
+ align-items: center;
685
+ gap: space(0.5);
686
+ }
687
+
688
+ .asd20-file-list__pagination-page {
689
+ margin: 0;
690
+ }
691
+
540
692
  .asd20-file-list__empty {
541
693
  width: 100%;
542
694
  padding: space(0.75) 0;
@@ -547,7 +699,7 @@ export default {
547
699
  display: flex;
548
700
  flex-wrap: wrap;
549
701
  align-items: center;
550
- justify-content: space-between;
702
+ justify-content: center;
551
703
  gap: space(0.5);
552
704
  padding-top: space(0.75);
553
705
  }
@@ -563,12 +715,15 @@ export default {
563
715
  gap: space(0.5);
564
716
  }
565
717
 
718
+
566
719
  @media (max-width: 767px) {
567
720
  .asd20-file-list__toolbar {
568
721
  display: block;
569
- flex-wrap: wrap;
570
722
  width: 100%;
571
723
  }
724
+ .asd20-file-list__pagination {
725
+ justify-content: center;
726
+ }
572
727
 
573
728
  .asd20-file-list__search {
574
729
  min-width: 100%;
@@ -577,12 +732,15 @@ export default {
577
732
  }
578
733
 
579
734
  @media (min-width: 1024px) {
580
- .asd20-file-list__search {
581
- margin: space(0.5) space(0.5);
582
-
583
- :deep(.asd20-icon) {
584
- margin-left: 0;
585
- }
735
+ // .asd20-file-list__search {
736
+ // margin: space(0.5) space(0.5);
737
+
738
+ // :deep(.asd20-icon) {
739
+ // margin-left: 0;
740
+ // }
741
+ // }
742
+ .asd20-file-list__pagination {
743
+ justify-content: space-between;
586
744
  }
587
745
  }
588
746
  </style>
@@ -17,14 +17,60 @@
17
17
  :name="icon"
18
18
  :size="iconSize"
19
19
  />
20
- <component
21
- :is="headlineTag"
20
+ <h1
21
+ v-if="headlineTag === 'h1'"
22
22
  class="asd20-list__headline"
23
23
  v-html="headline"
24
- ></component>
24
+ ></h1>
25
+ <h2
26
+ v-else-if="headlineTag === 'h2'"
27
+ class="asd20-list__headline"
28
+ v-html="headline"
29
+ ></h2>
30
+ <h3
31
+ v-else-if="headlineTag === 'h3'"
32
+ class="asd20-list__headline"
33
+ v-html="headline"
34
+ ></h3>
35
+ <h4
36
+ v-else-if="headlineTag === 'h4'"
37
+ class="asd20-list__headline"
38
+ v-html="headline"
39
+ ></h4>
40
+ <h5
41
+ v-else-if="headlineTag === 'h5'"
42
+ class="asd20-list__headline"
43
+ v-html="headline"
44
+ ></h5>
45
+ <h6
46
+ v-else-if="headlineTag === 'h6'"
47
+ class="asd20-list__headline"
48
+ v-html="headline"
49
+ ></h6>
50
+ <p
51
+ v-else-if="headlineTag === 'p'"
52
+ class="asd20-list__headline"
53
+ v-html="headline"
54
+ ></p>
55
+ <span
56
+ v-else-if="headlineTag === 'span'"
57
+ class="asd20-list__headline"
58
+ v-html="headline"
59
+ ></span>
60
+ <div
61
+ v-else
62
+ class="asd20-list__headline"
63
+ v-html="headline"
64
+ ></div>
25
65
  </div>
26
66
  <slot name="header" />
27
67
  </div>
68
+ <div
69
+ v-if="$slots['after-header']"
70
+ class="asd20-list__after-header"
71
+ >
72
+ <slot name="after-header" />
73
+ </div>
28
74
  <asd20-viewport
29
75
  scrollable
30
76
  :max-height="maxHeight"
@@ -151,6 +197,10 @@ export default {
151
197
  color: var(--color__primary);
152
198
  }
153
199
 
200
+ .asd20-list__after-header {
201
+ margin-bottom: space(0.5);
202
+ }
203
+
154
204
  .asd20-list--multi-column {
155
205
  .asd20-list__items {
156
206
  list-style: none;
@@ -17,14 +17,60 @@
17
17
  :name="icon"
18
18
  :size="iconSize"
19
19
  />
20
- <component
21
- :is="headlineTag"
20
+ <h1
21
+ v-if="headlineTag === 'h1'"
22
22
  class="asd20-list__headline"
23
23
  v-html="headline"
24
- ></component>
24
+ ></h1>
25
+ <h2
26
+ v-else-if="headlineTag === 'h2'"
27
+ class="asd20-list__headline"
28
+ v-html="headline"
29
+ ></h2>
30
+ <h3
31
+ v-else-if="headlineTag === 'h3'"
32
+ class="asd20-list__headline"
33
+ v-html="headline"
34
+ ></h3>
35
+ <h4
36
+ v-else-if="headlineTag === 'h4'"
37
+ class="asd20-list__headline"
38
+ v-html="headline"
39
+ ></h4>
40
+ <h5
41
+ v-else-if="headlineTag === 'h5'"
42
+ class="asd20-list__headline"
43
+ v-html="headline"
44
+ ></h5>
45
+ <h6
46
+ v-else-if="headlineTag === 'h6'"
47
+ class="asd20-list__headline"
48
+ v-html="headline"
49
+ ></h6>
50
+ <p
51
+ v-else-if="headlineTag === 'p'"
52
+ class="asd20-list__headline"
53
+ v-html="headline"
54
+ ></p>
55
+ <span
56
+ v-else-if="headlineTag === 'span'"
57
+ class="asd20-list__headline"
58
+ v-html="headline"
59
+ ></span>
60
+ <div
61
+ v-else
62
+ class="asd20-list__headline"
63
+ v-html="headline"
64
+ ></div>
25
65
  </div>
26
66
  <slot name="header" />
27
67
  </div>
68
+ <div
69
+ v-if="$slots['after-header']"
70
+ class="asd20-list__after-header"
71
+ >
72
+ <slot name="after-header" />
73
+ </div>
28
74
  <asd20-viewport
29
75
  ref="viewport"
30
76
  scrollable
@@ -156,6 +202,10 @@ export default {
156
202
  color: var(--color__primary);
157
203
  }
158
204
 
205
+ .asd20-list__after-header {
206
+ margin-bottom: space(0.5);
207
+ }
208
+
159
209
  .asd20-list--multi-column {
160
210
  .asd20-list__items {
161
211
  list-style: none;
@@ -80,8 +80,8 @@
80
80
  v-bind="filesFeedProps"
81
81
  :multi-column="true"
82
82
  searchable
83
- :initial-visible-count="50"
84
- :visible-count-step="50"
83
+ paginate
84
+ :page-size="50"
85
85
  search-placeholder="Search files"
86
86
  max-height="auto"
87
87
  @files-in-view="$emit('files-in-view')"
@@ -22,6 +22,33 @@ function hasIncomingProp(vm, propName) {
22
22
  )
23
23
  }
24
24
 
25
+ function resolveStore(vm) {
26
+ if (!vm) return null
27
+
28
+ const internal = vm.$
29
+ const context = internal && internal.ctx
30
+ if (context && context.$store) {
31
+ return context.$store
32
+ }
33
+
34
+ if (vm.$options && vm.$options.store) {
35
+ return vm.$options.store
36
+ }
37
+
38
+ const parentContext = internal && internal.parent && internal.parent.ctx
39
+ if (parentContext && parentContext.$store) {
40
+ return parentContext.$store
41
+ }
42
+
43
+ return (
44
+ internal &&
45
+ internal.appContext &&
46
+ internal.appContext.config &&
47
+ internal.appContext.config.globalProperties &&
48
+ internal.appContext.config.globalProperties.$store
49
+ )
50
+ }
51
+
25
52
  export default function(variableName, propOptions, storeModuleName) {
26
53
  const pascalCaseName = variableName[0].toUpperCase() + variableName.slice(1)
27
54
  const resolvedName = `resolved${pascalCaseName}`
@@ -41,14 +68,15 @@ export default function(variableName, propOptions, storeModuleName) {
41
68
  return this[variableName]
42
69
  }
43
70
 
44
- try {
45
- const storeValue = storeModuleName
46
- ? this.$store['state'][storeModuleName][variableName]
47
- : this.$store.state[variableName]
48
- return storeValue !== undefined ? storeValue : this[localStateName]
49
- } catch {
50
- return this[localStateName]
51
- }
71
+ const store = resolveStore(this)
72
+ const state = store && store.state
73
+ const storeState = storeModuleName && state ? state[storeModuleName] : state
74
+ const storeValue =
75
+ storeState && Object.prototype.hasOwnProperty.call(storeState, variableName)
76
+ ? storeState[variableName]
77
+ : undefined
78
+
79
+ return storeValue !== undefined ? storeValue : this[localStateName]
52
80
  },
53
81
  // emit an update event and value and call method to set value in store.
54
82
  set(value) {
@@ -68,9 +96,10 @@ export default function(variableName, propOptions, storeModuleName) {
68
96
  },
69
97
  }
70
98
  mixin.methods[`set${pascalCaseName}`] = function(value) {
71
- if (!this.$store || typeof this.$store.dispatch !== 'function') return false
99
+ const store = resolveStore(this)
100
+ if (!store || typeof store.dispatch !== 'function') return false
72
101
 
73
- this.$store.dispatch(
102
+ store.dispatch(
74
103
  `${storeModuleName ? storeModuleName + '/' : ''}set${pascalCaseName}`,
75
104
  value
76
105
  )