@paris-ias/list 1.0.137 → 1.0.139

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 (49) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/actions/DenseItem.vue +64 -0
  3. package/dist/runtime/components/actions/ExpandedItem.vue +17 -14
  4. package/dist/runtime/components/actions/RowsItem.vue +56 -10
  5. package/dist/runtime/components/actions/View.vue +5 -17
  6. package/dist/runtime/components/affiliation/DenseItem.vue +26 -10
  7. package/dist/runtime/components/affiliation/RowsItem.vue +26 -10
  8. package/dist/runtime/components/apps/DenseItem.vue +26 -10
  9. package/dist/runtime/components/apps/RowsItem.vue +26 -10
  10. package/dist/runtime/components/disciplines/DenseItem.vue +26 -10
  11. package/dist/runtime/components/disciplines/RowsItem.vue +26 -10
  12. package/dist/runtime/components/events/DenseItem.vue +12 -3
  13. package/dist/runtime/components/events/RowsItem.vue +12 -5
  14. package/dist/runtime/components/fellowships/DenseItem.vue +14 -0
  15. package/dist/runtime/components/fellowships/RowsItem.vue +16 -2
  16. package/dist/runtime/components/fellowships/View.vue +2 -2
  17. package/dist/runtime/components/files/DenseItem.vue +25 -10
  18. package/dist/runtime/components/files/RowsItem.vue +26 -10
  19. package/dist/runtime/components/list/atoms/FiltersMenu.vue +9 -0
  20. package/dist/runtime/components/list/atoms/PerPage.vue +3 -2
  21. package/dist/runtime/components/list/atoms/ResetButton.vue +5 -1
  22. package/dist/runtime/components/list/atoms/SearchInput.vue +15 -2
  23. package/dist/runtime/components/list/atoms/SearchString.vue +169 -133
  24. package/dist/runtime/components/list/atoms/SortMenu.vue +22 -18
  25. package/dist/runtime/components/list/atoms/ViewMenu.vue +26 -14
  26. package/dist/runtime/components/list/molecules/Filters.vue +4 -4
  27. package/dist/runtime/components/list/molecules/GlobalSearchInput.vue +12 -14
  28. package/dist/runtime/components/list/molecules/Header.vue +11 -20
  29. package/dist/runtime/components/list/molecules/Pagination.vue +51 -48
  30. package/dist/runtime/components/list/molecules/ResultsContainer.vue +5 -2
  31. package/dist/runtime/components/list/organisms/List.vue +105 -74
  32. package/dist/runtime/components/list/organisms/Results.vue +31 -17
  33. package/dist/runtime/components/mailing/RowsItem.vue +26 -10
  34. package/dist/runtime/components/news/DenseItem.vue +59 -45
  35. package/dist/runtime/components/news/RowsItem.vue +11 -7
  36. package/dist/runtime/components/people/DenseItem.vue +10 -8
  37. package/dist/runtime/components/people/RowsItem.vue +11 -2
  38. package/dist/runtime/components/projects/DenseItem.vue +11 -4
  39. package/dist/runtime/components/projects/RowsItem.vue +10 -3
  40. package/dist/runtime/components/publications/DenseItem.vue +12 -8
  41. package/dist/runtime/components/publications/RowsItem.vue +12 -4
  42. package/dist/runtime/components/tags/RowsItem.vue +23 -10
  43. package/dist/runtime/components/users/DenseItem.vue +24 -10
  44. package/dist/runtime/components/users/RowsItem.vue +24 -10
  45. package/dist/runtime/composables/useUtils.js +1 -1
  46. package/dist/runtime/plugins/pinia.js +5 -2
  47. package/dist/runtime/stores/root.d.ts +10 -9
  48. package/dist/runtime/stores/root.js +82 -117
  49. package/package.json +1 -1
@@ -1,23 +1,26 @@
1
1
  <template>
2
- <v-menu>
2
+ <v-menu :disabled="$stores[type].loading">
3
3
  <template #activator="{ props: menu }">
4
4
  <v-tooltip location="top">
5
5
  <template #activator="{ props: tooltip }">
6
- <v-btn
7
- x-large
8
- tile
9
- flat
10
- :icon="'mdi-' + current?.icon || defaultView?.icon"
11
- :class="{
12
- 'mt-3': isXsDisplay,
13
- }"
14
- v-bind="mergeProps(menu, tooltip)"
15
- />
6
+ <template v-if="$stores[type].loading">
7
+ <v-skeleton-loader type="button" :class="{ 'mt-3': isXsDisplay }" />
8
+ </template>
9
+ <template v-else>
10
+ <v-btn
11
+ x-large
12
+ tile
13
+ flat
14
+ :icon="'mdi-' + (current?.icon || defaultView?.icon)"
15
+ :class="{ 'mt-3': isXsDisplay }"
16
+ v-bind="mergeProps(menu, tooltip)"
17
+ />
18
+ </template>
16
19
  </template>
17
20
  <div
18
21
  v-html="
19
22
  $t('list.view-mode') +
20
- $t('list.' + current.name || defaultView.name)
23
+ $t('list.' + (current?.name || defaultView?.name))
21
24
  "
22
25
  />
23
26
  </v-tooltip>
@@ -26,6 +29,7 @@
26
29
  <v-list-item
27
30
  v-for="(value, key, index) in items"
28
31
  :key="index"
32
+ :disabled="$stores[type].loading"
29
33
  @click="updateView(value.name || key)"
30
34
  >
31
35
  <template #prepend>
@@ -43,7 +47,7 @@
43
47
  import { mergeProps } from "vue"
44
48
  import { useDisplay } from "vuetify"
45
49
  import { useRootStore } from "../../../stores/root"
46
- import { useNuxtApp, ref, useI18n } from "#imports"
50
+ import { useNuxtApp, ref, useI18n, computed } from "#imports"
47
51
  const { locale } = useI18n()
48
52
  const { $stores } = useNuxtApp()
49
53
 
@@ -61,8 +65,16 @@ const items = ref($stores[props.type].views)
61
65
 
62
66
  const current = ref($stores[props.type].view)
63
67
 
68
+ const defaultView = ref(
69
+ $stores[props.type].views[
70
+ Object.keys($stores[props.type].views).find(
71
+ (k) => $stores[props.type].views[k]?.default === true,
72
+ )
73
+ ] || { name: "list", icon: "view-list" },
74
+ )
75
+
64
76
  const updateView = async (value) => {
65
- await rootStore.updateView({ value, type: props.type, lang: locale.value })
77
+ rootStore.updateView({ value, type: props.type, lang: locale.value })
66
78
  }
67
79
  </script>
68
80
 
@@ -1,15 +1,16 @@
1
1
  <template>
2
2
  <v-row>
3
- <template v-for="filterItem in Object.keys($stores[type].filters)">
3
+ <template v-for="(filterItem, index) in Object.keys($stores[type].filters)">
4
4
  <v-col
5
5
  v-if="computeVisibility(filterItem)"
6
- :key="type + filterItem"
6
+ :key="type + index + filterItem"
7
7
  cols="12"
8
8
  sm="6"
9
9
  md="4"
10
10
  >
11
11
  <component
12
12
  :is="ComponentName(filterItem)"
13
+ :id="type + index + filterItem"
13
14
  tile
14
15
  :name="filterItem"
15
16
  hide-details
@@ -53,7 +54,6 @@ const getItems = (name) => {
53
54
  }
54
55
 
55
56
  if ($filters?.[props.type]?.[name]) {
56
- /* console.log("filters found for ", name, $filters[props.type][name]) */
57
57
  return $filters[props.type][name]
58
58
  .filter((key) => key !== "label")
59
59
  .map((item) => ({
@@ -68,7 +68,7 @@ const getItems = (name) => {
68
68
  if (
69
69
  messages.value[locale.value].list.filters[props.type][name] === undefined
70
70
  ) {
71
- console.log("name not found, no item for this filmter: ", name)
71
+ console.log("name not found, no item for this filter: ", name)
72
72
  return []
73
73
  }
74
74
  // TODO replace with package based values
@@ -2,6 +2,7 @@
2
2
  <div class="d-flex flex-grow-1 flex-column">
3
3
  <div class="d-flex align-center">
4
4
  <v-text-field
5
+ :id="`global-search-input-${type}`"
5
6
  v-model.trim="search"
6
7
  :placeholder="
7
8
  type === 'all'
@@ -24,11 +25,7 @@
24
25
  $store.state.scrolled }" -->
25
26
  <template v-if="!search" #label>
26
27
  <div class="searchLabel">
27
- {{
28
- type === "all"
29
- ? $t("search")
30
- : $t("list.search-type", [$t("items." + type, 2)])
31
- }}
28
+ {{ $t("search") }}
32
29
  </div>
33
30
  </template>
34
31
  </v-text-field>
@@ -78,16 +75,14 @@
78
75
  </v-card>
79
76
  </v-menu>
80
77
  <v-btn
81
- v-bind="attrs"
82
78
  :rounded="0"
83
- variant="test"
79
+ variant="outlined"
84
80
  size="large"
85
81
  height="56"
86
- v-on="on"
87
82
  @keyup.enter="$router.push(localePath('/search'))"
88
83
  @click="$router.push(localePath('/search'))"
89
84
  >
90
- <v-icon>mdi-magnify</v-icon>
85
+ <v-icon size="large">mdi-magnify</v-icon>
91
86
  <v-tooltip activator="parent" location="start">{{
92
87
  $t("click-here-to-search")
93
88
  }}</v-tooltip>
@@ -99,15 +94,14 @@
99
94
  <script setup>
100
95
  import { useDebounceFn } from "@vueuse/core"
101
96
  import { useRootStore } from "../../../stores/root"
102
- import { computed, useI18n, ref } from "#imports"
97
+ import { computed, useI18n, ref, useLocalePath } from "#imports"
98
+ const localePath = useLocalePath()
103
99
  const { locale, t } = useI18n()
104
100
  const rootStore = useRootStore()
105
101
 
106
102
  // Utility function to capitalize first letter
107
103
  const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1)
108
-
109
104
  const emit = defineEmits(["filter-change"])
110
-
111
105
  const props = defineProps({
112
106
  type: {
113
107
  type: String,
@@ -162,8 +156,12 @@ const search = computed({
162
156
  get() {
163
157
  return rootStore.search
164
158
  },
165
- set: await useDebounceFn(async function (v) {
166
- await rootStore.updateSearch({
159
+ set: useDebounceFn(function (v) {
160
+ emit("change", {
161
+ name: "search",
162
+ value: v,
163
+ })
164
+ rootStore.updateSearch({
167
165
  type: props.type,
168
166
  search: v || "",
169
167
  lang: locale.value,
@@ -30,29 +30,10 @@
30
30
  </template>
31
31
 
32
32
  <script setup>
33
- import { ref, computed } from "vue"
33
+ import { ref, unref, computed } from "vue"
34
34
  import { useNuxtApp } from "#imports"
35
35
  const { $stores } = useNuxtApp()
36
36
 
37
- const filtersOpen = ref(false)
38
- const visible = computed(() => {
39
- console.log(
40
- "$stores[props.type]?.filtersCount > 0: ",
41
- $stores[props.type]?.filtersCount > 0
42
- )
43
- console.log(
44
- "$stores[props.type]?.filtersCount: ",
45
- $stores[props.type]?.filtersCount
46
- )
47
- console.log(
48
- !!(
49
- $stores[props.type]?.filtersCount && $stores[props.type]?.filtersCount > 0
50
- )
51
- )
52
- return !!(
53
- $stores[props.type]?.filtersCount && $stores[props.type]?.filtersCount > 0
54
- )
55
- })
56
37
  const props = defineProps({
57
38
  type: {
58
39
  type: String,
@@ -60,4 +41,14 @@ const props = defineProps({
60
41
  default: "",
61
42
  },
62
43
  })
44
+ const visible = computed(() => {
45
+ console.log(
46
+ "SHOULD DISPLAY FILTERS:",
47
+ $stores[props.type]?.filtersCount && $stores[props.type]?.filtersCount > 0,
48
+ )
49
+ return !!(
50
+ $stores[props.type]?.filtersCount && $stores[props.type]?.filtersCount > 0
51
+ )
52
+ })
53
+ const filtersOpen = ref(unref(visible))
63
54
  </script>
@@ -9,9 +9,10 @@
9
9
  <v-btn
10
10
  v-if="!(hidePrevNext && isFirstPage)"
11
11
  :disabled="isFirstPage"
12
- min-width="35"
13
- height="35"
14
- width="35"
12
+ min-width="40"
13
+ height="40"
14
+ width="40"
15
+ class="prev-btn"
15
16
  :tabindex="isFirstPage && hidePrevNext ? -1 : 0"
16
17
  aria-label="Previous Page"
17
18
  @click="onChange(currentPage - 1)"
@@ -19,16 +20,16 @@
19
20
  >
20
21
  <v-icon>mdi-chevron-left</v-icon>
21
22
  </v-btn>
22
-
23
23
  <!-- Page buttons and gaps -->
24
24
  <template v-for="(page, index) in renderPages" :key="page.key">
25
25
  <!-- Ellipsis gap -->
26
26
  <v-btn
27
+ class="ellipsis-btn"
27
28
  v-if="page.isGap"
28
- icon
29
- min-width="35"
30
- height="35"
31
- width="35"
29
+ min-width="40"
30
+ tile
31
+ height="40"
32
+ width="40"
32
33
  @click="onChange(getGapPage(index))"
33
34
  @keyup.enter="onChange(getGapPage(index))"
34
35
  >
@@ -40,10 +41,10 @@
40
41
  v-else
41
42
  :class="['page-button', { 'active-page': page.current }]"
42
43
  tabindex="0"
43
- min-width="35"
44
- height="35"
44
+ min-width="40"
45
+ height="40"
45
46
  tile
46
- width="35"
47
+ width="40"
47
48
  :aria-current="page.current ? 'page' : undefined"
48
49
  :aria-label="
49
50
  page.current
@@ -63,9 +64,11 @@
63
64
  :disabled="isLastPage"
64
65
  :tabindex="isLastPage && hidePrevNext ? -1 : 0"
65
66
  aria-label="Next Page"
66
- min-width="35"
67
- height="35"
68
- width="35"
67
+ min-width="40"
68
+ tile
69
+ class="next-btn"
70
+ height="40"
71
+ width="40"
69
72
  @click="onChange(currentPage + 1)"
70
73
  @keyup.enter="onChange(currentPage + 1)"
71
74
  >
@@ -75,7 +78,7 @@
75
78
  </template>
76
79
 
77
80
  <script setup>
78
- import { computed } from "vue";
81
+ import { computed } from "vue"
79
82
 
80
83
  const props = defineProps({
81
84
  currentPage: { type: Number, required: true },
@@ -83,15 +86,15 @@ const props = defineProps({
83
86
  pagePadding: { type: Number, default: 1, validator: (v) => v > 0 },
84
87
  pageGap: { type: Number, default: 2, validator: (v) => v > 0 },
85
88
  hidePrevNext: { type: Boolean, default: false },
86
- });
89
+ })
87
90
 
88
- const emit = defineEmits(["update"]);
91
+ const emit = defineEmits(["update"])
89
92
 
90
93
  // Computed state for prev/next disabled
91
- const isFirstPage = computed(() => props.currentPage === 1);
94
+ const isFirstPage = computed(() => props.currentPage === 1)
92
95
  const isLastPage = computed(
93
- () => props.currentPage === props.totalPages || props.totalPages === 0
94
- );
96
+ () => props.currentPage === props.totalPages || props.totalPages === 0,
97
+ )
95
98
 
96
99
  // Generate pages and gap positions
97
100
  const renderPages = computed(() => {
@@ -99,41 +102,41 @@ const renderPages = computed(() => {
99
102
  key: `page-${pageIndex}`,
100
103
  value: pageIndex,
101
104
  current: pageIndex === props.currentPage,
102
- });
103
- const createGap = (pageIndex) => ({ key: `gap-${pageIndex}`, isGap: true });
105
+ })
106
+ const createGap = (pageIndex) => ({ key: `gap-${pageIndex}`, isGap: true })
104
107
 
105
- const pages = [];
108
+ const pages = []
106
109
  for (let i = 1; i <= props.totalPages; i++) {
107
110
  if (
108
111
  i === props.currentPage ||
109
112
  i < props.pageGap ||
110
113
  i > props.totalPages - props.pageGap + 1
111
114
  ) {
112
- pages.push(createPage(i));
113
- continue;
115
+ pages.push(createPage(i))
116
+ continue
114
117
  }
115
118
 
116
- let min, max;
119
+ let min, max
117
120
  if (props.currentPage <= props.pageGap + props.pagePadding) {
118
- min = props.pageGap + 1;
119
- max = min + props.pagePadding * 2;
121
+ min = props.pageGap + 1
122
+ max = min + props.pagePadding * 2
120
123
  } else if (
121
124
  props.currentPage >=
122
125
  props.totalPages - props.pageGap - props.pagePadding
123
126
  ) {
124
- max = props.totalPages - props.pageGap;
125
- min = max - props.pagePadding * 2;
127
+ max = props.totalPages - props.pageGap
128
+ min = max - props.pagePadding * 2
126
129
  } else {
127
- min = props.currentPage - props.pagePadding;
128
- max = props.currentPage + props.pagePadding;
130
+ min = props.currentPage - props.pagePadding
131
+ max = props.currentPage + props.pagePadding
129
132
  }
130
133
 
131
134
  if (
132
135
  (i >= min && i <= props.currentPage) ||
133
136
  (i <= max && i >= props.currentPage)
134
137
  ) {
135
- pages.push(createPage(i));
136
- continue;
138
+ pages.push(createPage(i))
139
+ continue
137
140
  }
138
141
 
139
142
  if (i === props.pageGap) {
@@ -141,11 +144,11 @@ const renderPages = computed(() => {
141
144
  min > props.pageGap + 1 &&
142
145
  props.currentPage > props.pageGap + props.pagePadding + 1
143
146
  ) {
144
- pages.push(createGap(i));
147
+ pages.push(createGap(i))
145
148
  } else {
146
- pages.push(createPage(i));
149
+ pages.push(createPage(i))
147
150
  }
148
- continue;
151
+ continue
149
152
  }
150
153
 
151
154
  if (i === props.totalPages - props.pageGap + 1) {
@@ -153,28 +156,28 @@ const renderPages = computed(() => {
153
156
  max < props.totalPages - props.pageGap &&
154
157
  props.currentPage < props.totalPages - props.pageGap - props.pagePadding
155
158
  ) {
156
- pages.push(createGap(i));
159
+ pages.push(createGap(i))
157
160
  } else {
158
- pages.push(createPage(i));
161
+ pages.push(createPage(i))
159
162
  }
160
- continue;
163
+ continue
161
164
  }
162
165
  }
163
- return pages;
164
- });
166
+ return pages
167
+ })
165
168
 
166
169
  // Calculate page to jump when clicking gap
167
170
  const getGapPage = (index) => {
168
- const before = renderPages.value[index - 1];
169
- const after = renderPages.value[index + 1] || { value: props.totalPages };
170
- return Math.floor((before.value + after.value) / 2);
171
- };
171
+ const before = renderPages.value[index - 1]
172
+ const after = renderPages.value[index + 1] || { value: props.totalPages }
173
+ return Math.floor((before.value + after.value) / 2)
174
+ }
172
175
 
173
176
  function onChange(page) {
174
- emit("update", page);
177
+ emit("update", page)
175
178
  }
176
179
  </script>
177
180
 
178
181
  <style scoped>
179
- .page-button{background-color:transparent;border:1px solid rgba(0,0,0,.2);color:#000;transition:background-color .3s ease,color .3s ease,transform .2s ease}.page-button:hover{background-color:#f0f0f0}.page-button.active-page{background-color:#000!important;color:#fff!important;transform:scale(1.05)}.page-button:focus{box-shadow:0 0 0 2px rgba(0,0,0,.3);outline:none}
182
+ .ellipsis-btn,.next-btn,.page-button,.prev-btn{background-color:transparent;border:1px solid rgba(0,0,0,.2);font-size:16px;transition:background-color .3s ease,color .3s ease,transform .2s ease}.page-button:hover{background-color:#f0f0f0}.page-button.active-page{background-color:#000!important;color:#fff!important;transform:scale(1.05)}.page-button:focus{box-shadow:0 0 0 2px rgba(0,0,0,.3);outline:none}
180
183
  </style>
@@ -6,6 +6,7 @@
6
6
  size="large"
7
7
  class=""
8
8
  @click="$emit('toggle', type)"
9
+ :disabled="$rootStore.results[type]?.total === 0"
9
10
  >
10
11
  <v-icon size="large">{{
11
12
  open ? "mdi-chevron-down" : "mdi-chevron-right"
@@ -16,9 +17,9 @@
16
17
  @click="$emit('toggle', type)"
17
18
  >
18
19
  <div
19
- class="text-h4"
20
+ class="text-h5 font-weight-medium"
20
21
  :class="
21
- $rootStore.results[type].total > 0 ? '' : 'text-grey darken-2'
22
+ $rootStore.results[type].total > 0 ? 'black' : 'text-grey darken-2'
22
23
  "
23
24
  >
24
25
  {{ capitalize($t("items." + props.type, 2)) }}
@@ -53,11 +54,13 @@
53
54
  color="default"
54
55
  variant="text"
55
56
  rounded="0"
57
+ v-if="$rootStore.results[type]?.total > 0"
56
58
  :to="localePath(type === 'people' ? '/people' : '/activities/' + type)"
57
59
  >
58
60
  {{ $t("list.pls-x-more", [$rootStore.results[type].total]) }}
59
61
  </v-btn>
60
62
  </div>
63
+ <v-divider></v-divider>
61
64
  </template>
62
65
  <script setup>
63
66
  import { useNuxtApp, useLocalePath } from "#imports"