@paris-ias/list 1.0.193 → 1.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.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@paris-ias/list",
3
3
  "configKey": "list",
4
- "version": "1.0.193",
4
+ "version": "1.1.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
@@ -0,0 +1,252 @@
1
+ <template>
2
+ <ListMoleculesDenseItemContainer
3
+ class="expandable-dense-item"
4
+ :class="expanded ? 'expanded' : 'collapsed'"
5
+ @click="toggle"
6
+ >
7
+ <v-col
8
+ v-if="mdAndUp"
9
+ :cols="expanded ? 3 : 'auto'"
10
+ :offset="!expanded && name.startsWith('search') ? 1 : 0"
11
+ class="animated-col"
12
+ :class="{ 'expanded-col': expanded }"
13
+ >
14
+ <div
15
+ class="ma-1 animated-image logo-frame"
16
+ :class="expanded ? 'scaled-up' : 'scaled-down'"
17
+ :style="{
18
+ backgroundColor: logoBackground,
19
+ width: expanded ? '100%' : '100px',
20
+ height: expanded ? undefined : '100px',
21
+ aspectRatio: expanded ? '1 / 1' : undefined,
22
+ }"
23
+ >
24
+ <MiscAtomsImageContainer
25
+ :loading="loading"
26
+ :src="
27
+ item && item.image && item.image.url
28
+ ? item.image.url
29
+ : '/default.png'
30
+ "
31
+ :ratio="1 / 1"
32
+ :width="expanded ? undefined : 100"
33
+ class="logo-image"
34
+ />
35
+ </div>
36
+ </v-col>
37
+ <v-col
38
+ align-self="start"
39
+ class="dense"
40
+ :class="expanded ? 'ml-md-8' : 'ml-md-2'"
41
+ >
42
+ <v-skeleton-loader v-if="loading" type="heading" />
43
+ <div
44
+ v-else
45
+ class="d-flex justify-space-between text-title align-center pt-md-2"
46
+ :class="expanded ? 'text-h5 text-md-h4' : 'text-h5'"
47
+ >
48
+ <span
49
+ v-html="
50
+ searchQuery.length
51
+ ? highlightAndTruncate(300, item.name, searchQuery.split(' '))
52
+ : item.name
53
+ "
54
+ />
55
+ </div>
56
+
57
+ <v-skeleton-loader v-if="loading" type="paragraph" />
58
+
59
+ <Transition v-else name="text-slide">
60
+ <div
61
+ v-if="expanded"
62
+ key="description"
63
+ class="text-body-1 font-weight-light paragraph"
64
+ :style="'-webkit-line-clamp:' + expandedLineClamp"
65
+ >
66
+ <MDC
67
+ v-if="item.description && item.description.length"
68
+ :value="
69
+ searchQuery.length
70
+ ? highlightAndTruncate(
71
+ 600,
72
+ item.description,
73
+ searchQuery.split(' '),
74
+ )
75
+ : item.description
76
+ "
77
+ />
78
+ <div v-else class="text-body-2 font-italic">
79
+ {{ $t("no-description") }}
80
+ </div>
81
+ </div>
82
+ <div
83
+ v-else-if="mdAndUp && item.summary && item.summary.length"
84
+ key="summary"
85
+ class="text-body-1 font-weight-light paragraph"
86
+ :style="'-webkit-line-clamp:' + collapsedLineClamp"
87
+ v-html="
88
+ searchQuery.length
89
+ ? highlightAndTruncate(100, item.summary, searchQuery.split(' '))
90
+ : item.summary
91
+ "
92
+ />
93
+ </Transition>
94
+
95
+ <v-expand-transition>
96
+ <div v-show="expanded" class="mt-3" @click.stop>
97
+ <v-btn
98
+ variant="outlined"
99
+ size="small"
100
+ tile
101
+ :href="itemUrl"
102
+ target="_blank"
103
+ rel="noopener"
104
+ >
105
+ {{ $t("see-more") }}
106
+ <v-icon end>mdi-open-in-new</v-icon>
107
+ </v-btn>
108
+ </div>
109
+ </v-expand-transition>
110
+ </v-col>
111
+ </ListMoleculesDenseItemContainer>
112
+ </template>
113
+
114
+ <script setup>
115
+ import { useRootStore } from "../../stores/root"
116
+ import { highlightAndTruncate } from "../../composables/useUtils"
117
+ import { computed, ref, useRoute, useNuxtApp, useLocalePath } from "#imports"
118
+ import { useDisplay } from "vuetify"
119
+ const { name } = useRoute()
120
+ const localePath = useLocalePath()
121
+
122
+ const { mdAndUp, name: displayName } = useDisplay()
123
+ const rootStore = useRootStore()
124
+ const { $stores } = useNuxtApp()
125
+ const searchQuery = computed(() =>
126
+ name.startsWith("search")
127
+ ? rootStore.search
128
+ : $stores["affiliations"]?.search || "",
129
+ )
130
+ const props = defineProps({
131
+ item: {
132
+ type: Object,
133
+ required: true,
134
+ },
135
+ index: {
136
+ type: Number,
137
+ required: true,
138
+ },
139
+ loading: {
140
+ type: Boolean,
141
+ required: false,
142
+ default: false,
143
+ },
144
+ })
145
+
146
+ const expanded = ref(false)
147
+ const toggle = () => {
148
+ expanded.value = !expanded.value
149
+ }
150
+
151
+ const breakpointIndex = computed(() =>
152
+ ["xs", "sm", "md", "lg", "xl", "xxl"].indexOf(displayName.value || "md"),
153
+ )
154
+
155
+ const collapsedLineClamp = computed(
156
+ () => [1, 1, 1, 2, 3, 3][breakpointIndex.value],
157
+ )
158
+
159
+ const expandedLineClamp = computed(
160
+ () => [4, 5, 5, 7, 9, 11][breakpointIndex.value],
161
+ )
162
+
163
+ const itemUrl = computed(() => {
164
+ const url = props.item?.url
165
+ if (!url) return undefined
166
+ if (/^https?:\/\//i.test(url)) return url
167
+ return localePath(url.startsWith("/") ? url : "/" + url)
168
+ })
169
+
170
+ const logoBackground = computed(() => {
171
+ const named = { white: "#fff", black: "#000" }
172
+ const raw = props.item?.image?.backgroundColor
173
+ if (!raw) return "transparent"
174
+ const lower = String(raw).toLowerCase()
175
+ return named[lower] || raw
176
+ })
177
+ </script>
178
+
179
+ <style scoped>
180
+ .expandable-dense-item {
181
+ overflow: hidden;
182
+ transition: background-color 0.6s cubic-bezier(0.4, 0, 0.2, 1);
183
+ }
184
+ .expandable-dense-item .animated-col {
185
+ transition: flex 0.7s cubic-bezier(0.4, 0, 0.2, 1), max-width 0.7s cubic-bezier(0.4, 0, 0.2, 1);
186
+ }
187
+ .expandable-dense-item .animated-col.expanded-col {
188
+ max-width: 20.833%;
189
+ flex-basis: 20.833%;
190
+ }
191
+
192
+ .animated-image {
193
+ transform-origin: top left;
194
+ transition: transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1);
195
+ }
196
+ .animated-image.scaled-down {
197
+ transform: scale(0.85);
198
+ }
199
+ .animated-image.scaled-up {
200
+ transform: scale(1.05);
201
+ }
202
+
203
+ .logo-frame {
204
+ display: flex;
205
+ align-items: center;
206
+ justify-content: center;
207
+ padding: 8px;
208
+ border-radius: 2px;
209
+ box-sizing: border-box;
210
+ overflow: hidden;
211
+ }
212
+ .logo-frame .logo-image {
213
+ width: 100%;
214
+ height: 100%;
215
+ }
216
+ .logo-frame .logo-image :deep(> div) {
217
+ width: 100% !important;
218
+ height: 100% !important;
219
+ }
220
+ .logo-frame :deep(.v-img__img) {
221
+ object-fit: contain !important;
222
+ }
223
+
224
+ .paragraph {
225
+ max-width: 83ch !important;
226
+ display: -webkit-box;
227
+ -webkit-box-orient: vertical;
228
+ overflow: hidden;
229
+ }
230
+
231
+ .text-slide-enter-active,
232
+ .text-slide-leave-active {
233
+ transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1), transform 0.6s cubic-bezier(0.4, 0, 0.2, 1), filter 0.6s ease;
234
+ }
235
+
236
+ .text-slide-leave-active {
237
+ position: absolute;
238
+ width: 100%;
239
+ }
240
+
241
+ .text-slide-enter-from {
242
+ opacity: 0;
243
+ transform: translateY(-8px);
244
+ filter: blur(1.5px);
245
+ }
246
+
247
+ .text-slide-leave-to {
248
+ opacity: 0;
249
+ transform: translateY(8px);
250
+ filter: blur(1.5px);
251
+ }
252
+ </style>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover py-2">
2
+ <ListMoleculesDenseItemContainer>
3
3
  <v-col v-if="mdAndUp" cols="1">
4
4
  <MiscAtomsDateStamp
5
5
  v-if="item.start"
@@ -21,7 +21,7 @@
21
21
  />
22
22
  </v-col>
23
23
 
24
- <v-col align-self="start" class="pl-4">
24
+ <v-col align-self="start" class="pl-md-4">
25
25
  <v-skeleton-loader v-if="loading" type="chip" class="mr-3" width="120" />
26
26
  <v-chip
27
27
  v-else
@@ -82,14 +82,7 @@
82
82
  )}`"
83
83
  />
84
84
  </v-col>
85
-
86
- <!-- <v-col align-self="center" cols="auto">
87
- <v-skeleton-loader v-if="loading" type="button" />
88
- <div v-else>
89
- <EventsBadges :item />
90
- </div>
91
- </v-col> -->
92
- </v-row>
85
+ </ListMoleculesDenseItemContainer>
93
86
  </template>
94
87
 
95
88
  <script setup>
@@ -124,6 +117,4 @@ const props = defineProps({
124
117
  default: false,
125
118
  },
126
119
  })
127
-
128
- // const loading = computed(() => rootStore.loading || props.loading)
129
120
  </script>
@@ -1,19 +1,17 @@
1
1
  <template>
2
- <v-row
3
- v-ripple
4
- no-gutters
5
- class="cursor-pointer highlight-on-hover pt-2 pl-md-2"
6
- >
2
+ <ListMoleculesDenseItemContainer>
7
3
  <v-col
8
4
  v-if="mdAndUp"
9
- cols="1"
5
+ cols="auto"
10
6
  class="align-center"
11
7
  :offset="name.startsWith('search') ? 1 : 0"
12
8
  >
13
9
  <MiscAtomsImageContainer
14
10
  cover
15
11
  :loading="loading"
16
- :src="item?.image?.url || '/default.png'"
12
+ :src="
13
+ item && item.image && item.image.url ? item.image.url : '/default.png'
14
+ "
17
15
  :ratio="1 / 1"
18
16
  :width="100"
19
17
  />
@@ -21,8 +19,7 @@
21
19
 
22
20
  <v-col
23
21
  :cols="mdAndUp ? 8 : undefined"
24
- class="text-h5 dense d-flex flex-column ml-md-2"
25
- :offset="name.startsWith('search') ? 1 : 0"
22
+ class="text-h5 dense d-flex flex-column"
26
23
  >
27
24
  <v-skeleton-loader
28
25
  v-if="loading"
@@ -37,6 +34,7 @@
37
34
  ? highlightAndTruncate(300, item.name, searchQuery.split(' '))
38
35
  : item.name
39
36
  "
37
+ class="pl-md-4"
40
38
  />
41
39
  <FellowshipsBadges :item="item" :loading="loading" />
42
40
 
@@ -60,7 +58,7 @@
60
58
  </template>
61
59
  </v-col>
62
60
 
63
- <v-col v-if="mdAndUp" class="d-flex flex-column">
61
+ <v-col v-if="mdAndUp" class="d-flex flex-column" cols="1">
64
62
  <v-skeleton-loader v-if="loading" type="chip" width="260" class="mt-2" />
65
63
  <MiscMoleculesChipContainer
66
64
  v-else
@@ -72,7 +70,7 @@
72
70
  class="mt-2"
73
71
  />
74
72
  </v-col>
75
- </v-row>
73
+ </ListMoleculesDenseItemContainer>
76
74
  </template>
77
75
 
78
76
  <script setup>
@@ -81,7 +79,7 @@ import { useNuxtApp, useRoute, computed } from "#imports"
81
79
  import { highlightAndTruncate } from "../../composables/useUtils"
82
80
  const { $rootStore, $stores } = useNuxtApp()
83
81
  const { name } = useRoute()
84
- const { mdAndUp, mdAndDown } = useDisplay()
82
+ const { mdAndUp, mdAndDown, smAndDown } = useDisplay()
85
83
  const searchQuery = computed(() =>
86
84
  name.startsWith("search")
87
85
  ? $rootStore.search
@@ -16,8 +16,7 @@
16
16
  :disabled="$stores[type] && $stores[type].loading"
17
17
  @click="$emit('open', !open)"
18
18
  >
19
- <!-- <v-icon>mdi-tune-variant</v-icon> -->
20
- {{ $t("filters") }}
19
+ {{ $t("filters", 2) }}
21
20
  </v-btn>
22
21
  </template>
23
22
  <div
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover pa-2">
3
+ <slot />
4
+ </v-row>
5
+ </template>
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="d-flex align-center">
3
3
  <v-menu
4
- v-if="filter"
4
+ v-if="filter && mdAndUp"
5
5
  v-model="filterMenuOpen"
6
6
  :close-on-content-click="false"
7
7
  location="bottom end"
@@ -150,8 +150,14 @@ const allFilterOptions = computed(() => ({
150
150
  people: { value: "people", label: capitalize(t("items.people", 2)) },
151
151
  events: { value: "events", label: capitalize(t("items.events", 2)) },
152
152
  news: { value: "news", label: capitalize(t("items.news", 2)) },
153
- publications: { value: "publications", label: capitalize(t("items.publications", 2)) },
154
- fellowships: { value: "fellowships", label: capitalize(t("items.fellowships", 2)) },
153
+ publications: {
154
+ value: "publications",
155
+ label: capitalize(t("items.publications", 2)),
156
+ },
157
+ fellowships: {
158
+ value: "fellowships",
159
+ label: capitalize(t("items.fellowships", 2)),
160
+ },
155
161
  projects: { value: "projects", label: capitalize(t("items.projects", 2)) },
156
162
  }))
157
163
 
@@ -159,7 +165,9 @@ const filterOptions = computed(() => {
159
165
  const map = allFilterOptions.value
160
166
  if (props.filterOrder.length > 0) {
161
167
  const ordered = props.filterOrder.filter((k) => map[k]).map((k) => map[k])
162
- const rest = Object.values(map).filter((o) => !props.filterOrder.includes(o.value))
168
+ const rest = Object.values(map).filter(
169
+ (o) => !props.filterOrder.includes(o.value),
170
+ )
163
171
  return [...ordered, ...rest]
164
172
  }
165
173
  return Object.values(map)
@@ -18,7 +18,7 @@
18
18
  <ListAtomsSortMenu :type />
19
19
  </div>
20
20
  <v-expand-transition>
21
- <div v-show="filtersOpen" class="mb-7">
21
+ <div v-show="filtersOpen" class="mb-6">
22
22
  <ListMoleculesFilters :type />
23
23
  </div>
24
24
  </v-expand-transition>
@@ -2,6 +2,7 @@
2
2
  <div>
3
3
  <div class="d-flex align-center justify-space-between my-2">
4
4
  <v-btn
5
+ v-if="mdAndUp"
5
6
  variant="text"
6
7
  class="mr-2"
7
8
  @click="$emit('toggle', type)"
@@ -16,6 +17,7 @@
16
17
  @click="$emit('toggle', type)"
17
18
  >
18
19
  <h3
20
+ class="mt-4 mb-0"
19
21
  :class="
20
22
  $rootStore.results[type] && $rootStore.results[type].total > 0
21
23
  ? 'black'
@@ -24,7 +26,7 @@
24
26
  >
25
27
  {{ capitalize($t("items." + props.type, 2)) }}
26
28
  </h3>
27
- <div class="text-overline">
29
+ <div class="text-overline mb-3">
28
30
  {{
29
31
  feminine
30
32
  ? $t(
@@ -83,8 +85,11 @@
83
85
  </template>
84
86
  <script setup>
85
87
  import { useNuxtApp, useLocalePath } from "#imports"
88
+ import { useDisplay } from "vuetify"
86
89
  const localePath = useLocalePath()
87
90
 
91
+ const { mdAndUp } = useDisplay()
92
+
88
93
  // Utility function to capitalize first letter
89
94
  const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1)
90
95
 
@@ -61,14 +61,16 @@ import {
61
61
  computed,
62
62
  watch,
63
63
  } from "#imports"
64
+ import { useRootStore } from "../../../stores/root"
64
65
  import SEARCH from "@paris-ias/trees/dist/graphql/client/misc/query.search.all.gql"
65
66
 
66
67
  const { $rootStore } = useNuxtApp()
68
+ const rootStore = useRootStore()
67
69
  const appConfig = useAppConfig()
68
70
  const { locale } = useI18n()
69
71
  const route = useRoute()
70
72
  if (route.query.search) {
71
- $rootStore.search = route.query.search
73
+ rootStore.search = String(route.query.search)
72
74
  }
73
75
 
74
76
  const open = reactive(
@@ -109,16 +111,15 @@ const searchTerm = computed(() => $rootStore.search || "")
109
111
  const currentLocale = computed(() => locale.value)
110
112
 
111
113
  const { data, pending, error } = useAsyncQuery(
112
- SEARCH,
113
- computed(() => ({
114
- search: searchTerm.value,
115
- appId: "iea",
116
- lang: currentLocale.value,
117
- })),
118
114
  {
119
- server: false,
120
- enabled: computed(() => searchTerm.value.length > 0),
115
+ query: SEARCH,
116
+ variables: computed(() => ({
117
+ search: searchTerm.value,
118
+ appId: "iea",
119
+ lang: currentLocale.value,
120
+ })),
121
121
  },
122
+ { server: false },
122
123
  )
123
124
 
124
125
  watch(data, (newData) => {
@@ -13,7 +13,7 @@
13
13
  <div class="text-body-1">{{ item.name }}</div>
14
14
  <div>
15
15
  <span class="text-primary text-body-2"> {{ item.name }}</span> &mdash;
16
- <span class="text-body-2"> {{ item.subtitle }}</span>
16
+ <span class="text-body-2"> {{ item.summary }}</span>
17
17
  </div>
18
18
  </v-col>
19
19
  </v-row>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover pa-2">
2
+ <ListMoleculesDenseItemContainer>
3
3
  <v-col v-if="mdAndUp" cols="1">
4
4
  <MiscAtomsDateStamp
5
5
  v-if="item.date"
@@ -8,7 +8,7 @@
8
8
  class="mr-4"
9
9
  />
10
10
  </v-col>
11
- <v-col v-if="mdAndUp" align-self="start" cols="1">
11
+ <v-col v-if="mdAndUp" align-self="start" cols="auto">
12
12
  <MiscAtomsImageContainer
13
13
  cover
14
14
  :loading="loading"
@@ -19,7 +19,7 @@
19
19
  :width="100"
20
20
  />
21
21
  </v-col>
22
- <v-col align-self="start" class="text-h5 dense pl-4 paragraph">
22
+ <v-col align-self="start" class="text-h5 dense pl-md-4 paragraph">
23
23
  <v-skeleton-loader v-if="loading" type="heading" />
24
24
  <template v-else>
25
25
  <v-skeleton-loader
@@ -67,7 +67,7 @@
67
67
  />
68
68
  </template>
69
69
  </v-col>
70
- </v-row>
70
+ </ListMoleculesDenseItemContainer>
71
71
  </template>
72
72
 
73
73
  <script setup>
@@ -1,6 +1,10 @@
1
1
  <template>
2
- <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover">
3
- <v-col v-if="mdAndUp" cols="1" :offset="name.startsWith('search') ? 1 : 0">
2
+ <ListMoleculesDenseItemContainer>
3
+ <v-col
4
+ v-if="mdAndUp"
5
+ cols="auto"
6
+ :offset="name.startsWith('search') ? 1 : 0"
7
+ >
4
8
  <MiscAtomsImageContainer
5
9
  cover
6
10
  :loading="loading"
@@ -12,11 +16,11 @@
12
16
  class="ma-1"
13
17
  />
14
18
  </v-col>
15
- <v-col align-self="start" class="text-sm-h6 dense ml-4">
19
+ <v-col align-self="start" class="dense ml-md-4">
16
20
  <v-skeleton-loader v-if="loading" type="heading" />
17
21
  <div
18
22
  v-else
19
- class="d-flex justify-space-between text-title text-md-h5 align-center"
23
+ class="d-flex justify-space-between text-title text-h5 align-center pt-md-2"
20
24
  >
21
25
  <span
22
26
  v-html="
@@ -63,7 +67,7 @@
63
67
  "
64
68
  />
65
69
  </v-col>
66
- </v-row>
70
+ </ListMoleculesDenseItemContainer>
67
71
  </template>
68
72
 
69
73
  <script setup>
@@ -0,0 +1,233 @@
1
+ <template>
2
+ <ListMoleculesDenseItemContainer
3
+ class="expandable-dense-item"
4
+ :class="expanded ? 'expanded' : 'collapsed'"
5
+ @click="toggle"
6
+ >
7
+ <v-col
8
+ v-if="mdAndUp"
9
+ :cols="expanded ? 3 : 'auto'"
10
+ :offset="!expanded && name.startsWith('search') ? 1 : 0"
11
+ class="animated-col"
12
+ :class="{ 'expanded-col': expanded }"
13
+ >
14
+ <MiscAtomsImageContainer
15
+ cover
16
+ :loading="loading"
17
+ :src="
18
+ item && item.image && item.image.url ? item.image.url : '/default.png'
19
+ "
20
+ :ratio="1 / 1"
21
+ :width="expanded ? undefined : 100"
22
+ class="ma-1 animated-image"
23
+ :class="expanded ? 'scaled-up' : 'scaled-down'"
24
+ />
25
+ </v-col>
26
+ <v-col
27
+ align-self="start"
28
+ class="dense"
29
+ :class="expanded ? 'ml-md-8' : 'ml-md-2'"
30
+ >
31
+ <v-skeleton-loader v-if="loading" type="heading" />
32
+ <div
33
+ v-else
34
+ class="d-flex justify-space-between text-title align-center pt-md-2"
35
+ :class="expanded ? 'text-h5 text-md-h4' : 'text-h5'"
36
+ >
37
+ <span
38
+ v-html="
39
+ searchQuery.length
40
+ ? highlightAndTruncate(
41
+ 300,
42
+ item.firstname + ' ' + item.lastname,
43
+ searchQuery.split(' '),
44
+ )
45
+ : item.firstname + ' ' + item.lastname
46
+ "
47
+ />
48
+ <v-spacer />
49
+ <PeopleBadges :item="item" />
50
+ </div>
51
+ <div
52
+ v-if="item.group && item.groups.vintage && item.groups.vintage[0].theme"
53
+ class="text-body-1 font-weight-light paragraph"
54
+ v-html="
55
+ searchQuery.length
56
+ ? highlightAndTruncate(
57
+ 300,
58
+ item.groups.vintage[0].theme,
59
+ searchQuery.split(' '),
60
+ )
61
+ : item.groups.vintage[0].theme
62
+ "
63
+ />
64
+
65
+ <v-expand-transition>
66
+ <div v-show="expanded && item.socials" @click.stop>
67
+ <MiscAtomsSocials v-if="item.socials" :socials="item.socials" />
68
+ </div>
69
+ </v-expand-transition>
70
+
71
+ <v-skeleton-loader v-if="loading" type="paragraph" />
72
+
73
+ <Transition v-else name="text-slide">
74
+ <div
75
+ v-if="expanded"
76
+ key="biography"
77
+ class="text-body-1 font-weight-light paragraph"
78
+ :style="'-webkit-line-clamp:' + expandedLineClamp"
79
+ >
80
+ <MDC
81
+ v-if="item.biography && item.biography.length"
82
+ :value="
83
+ searchQuery.length
84
+ ? highlightAndTruncate(
85
+ 600,
86
+ item.biography,
87
+ searchQuery.split(' '),
88
+ )
89
+ : item.biography
90
+ "
91
+ />
92
+ <div v-else class="text-body-2 font-italic">
93
+ {{ $t("no-biography") }}
94
+ </div>
95
+ </div>
96
+ <div
97
+ v-else-if="mdAndUp && item.summary && item.summary.length"
98
+ key="summary"
99
+ class="text-body-1 font-weight-light paragraph"
100
+ :style="'-webkit-line-clamp:' + collapsedLineClamp"
101
+ v-html="
102
+ searchQuery.length
103
+ ? highlightAndTruncate(100, item.summary, searchQuery.split(' '))
104
+ : item.summary
105
+ "
106
+ />
107
+ </Transition>
108
+
109
+ <v-expand-transition>
110
+ <div v-show="expanded && isFellow" class="mt-3" @click.stop>
111
+ <v-btn
112
+ variant="outlined"
113
+ size="small"
114
+ tile
115
+ :to="localePath('/fellows/' + fellowSlug)"
116
+ >
117
+ {{ $t("see-more") }}
118
+ <v-icon end>mdi-arrow-right</v-icon>
119
+ </v-btn>
120
+ </div>
121
+ </v-expand-transition>
122
+ </v-col>
123
+ </ListMoleculesDenseItemContainer>
124
+ </template>
125
+
126
+ <script setup>
127
+ import { useRootStore } from "../../stores/root"
128
+ import { highlightAndTruncate } from "../../composables/useUtils"
129
+ import { computed, ref, useRoute, useNuxtApp, useLocalePath } from "#imports"
130
+ import { useDisplay } from "vuetify"
131
+ const { name } = useRoute()
132
+ const localePath = useLocalePath()
133
+
134
+ const { mdAndUp, name: displayName } = useDisplay()
135
+ const rootStore = useRootStore()
136
+ const { $stores } = useNuxtApp()
137
+ const searchQuery = computed(() =>
138
+ name.startsWith("search") ? rootStore.search : $stores["people"].search || "",
139
+ )
140
+ const props = defineProps({
141
+ item: {
142
+ type: Object,
143
+ required: true,
144
+ },
145
+ index: {
146
+ type: Number,
147
+ required: true,
148
+ },
149
+ loading: {
150
+ type: Boolean,
151
+ required: false,
152
+ default: false,
153
+ },
154
+ })
155
+
156
+ const expanded = ref(false)
157
+ const toggle = () => {
158
+ expanded.value = !expanded.value
159
+ }
160
+
161
+ const breakpointIndex = computed(() =>
162
+ ["xs", "sm", "md", "lg", "xl", "xxl"].indexOf(displayName.value || "md"),
163
+ )
164
+
165
+ const collapsedLineClamp = computed(
166
+ () => [1, 1, 1, 2, 3, 3][breakpointIndex.value],
167
+ )
168
+
169
+ const expandedLineClamp = computed(() => {
170
+ let base = [4, 5, 5, 7, 9, 11][breakpointIndex.value]
171
+ if (props.item?.socials && Object.keys(props.item.socials).length > 0) {
172
+ base -= [0, 1, 1, 1, 1, 2][breakpointIndex.value]
173
+ }
174
+ return base
175
+ })
176
+
177
+ const isFellow = computed(() => props.item?.groups?.fellows === true)
178
+ const fellowSlug = computed(() => props.item?.slug || props.item?.id)
179
+ </script>
180
+
181
+ <style scoped>
182
+ .expandable-dense-item {
183
+ overflow: hidden;
184
+ transition: background-color 0.6s cubic-bezier(0.4, 0, 0.2, 1);
185
+ }
186
+ .expandable-dense-item .animated-col {
187
+ transition: flex 0.7s cubic-bezier(0.4, 0, 0.2, 1), max-width 0.7s cubic-bezier(0.4, 0, 0.2, 1);
188
+ }
189
+ .expandable-dense-item .animated-col.expanded-col {
190
+ max-width: 20.833%;
191
+ flex-basis: 20.833%;
192
+ }
193
+
194
+ .animated-image {
195
+ transform-origin: top left;
196
+ transition: transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1);
197
+ }
198
+ .animated-image.scaled-down {
199
+ transform: scale(0.85);
200
+ }
201
+ .animated-image.scaled-up {
202
+ transform: scale(1.05);
203
+ }
204
+
205
+ .paragraph {
206
+ max-width: 83ch !important;
207
+ display: -webkit-box;
208
+ -webkit-box-orient: vertical;
209
+ overflow: hidden;
210
+ }
211
+
212
+ .text-slide-enter-active,
213
+ .text-slide-leave-active {
214
+ transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1), transform 0.6s cubic-bezier(0.4, 0, 0.2, 1), filter 0.6s ease;
215
+ }
216
+
217
+ .text-slide-leave-active {
218
+ position: absolute;
219
+ width: 100%;
220
+ }
221
+
222
+ .text-slide-enter-from {
223
+ opacity: 0;
224
+ transform: translateY(-8px);
225
+ filter: blur(1.5px);
226
+ }
227
+
228
+ .text-slide-leave-to {
229
+ opacity: 0;
230
+ transform: translateY(8px);
231
+ filter: blur(1.5px);
232
+ }
233
+ </style>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover">
2
+ <ListMoleculesDenseItemContainer>
3
3
  <v-col
4
4
  v-if="mdAndUp"
5
5
  align-self="start"
@@ -16,7 +16,7 @@
16
16
  :width="100"
17
17
  />
18
18
  </v-col>
19
- <v-col align-self="center" class="text-h5 dense pt-2 pl-4">
19
+ <v-col align-self="start" class="text-h5 dense pl-md-4">
20
20
  <v-skeleton-loader v-if="loading" type="heading" width="50%" />
21
21
  <span
22
22
  v-else
@@ -38,13 +38,13 @@
38
38
  v-else-if="item.summary && item.summary.length && mdAndUp"
39
39
  class="text-caption font-weight-light my-n2"
40
40
  :value="`${highlightAndTruncate(
41
- 130,
41
+ 180,
42
42
  item.summary,
43
43
  searchQuery.split(' '),
44
44
  )}`"
45
45
  />
46
46
  </v-col>
47
- </v-row>
47
+ </ListMoleculesDenseItemContainer>
48
48
  </template>
49
49
 
50
50
  <script setup>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <v-row v-ripple no-gutters class="cursor-pointer highlight-on-hover my-2">
2
+ <ListMoleculesDenseItemContainer>
3
3
  <v-col v-if="mdAndUp" cols="1">
4
4
  <MiscAtomsDateStamp
5
5
  v-if="item.date"
@@ -8,7 +8,7 @@
8
8
  class="mr-4"
9
9
  />
10
10
  </v-col>
11
- <v-col v-if="mdAndUp" cols="1" class="align-center">
11
+ <v-col v-if="mdAndUp" cols="auto" class="align-center">
12
12
  <MiscAtomsImageContainer
13
13
  cover
14
14
  :loading="loading"
@@ -19,8 +19,8 @@
19
19
  :width="100"
20
20
  />
21
21
  </v-col>
22
- <v-col class="pl-4">
23
- <div class="inline-flex flex-row flex-wrap">
22
+ <v-col class="" cols="8">
23
+ <div class="inline-flex flex-row flex-wrap pl-md-4">
24
24
  <v-skeleton-loader v-if="loading" :type="heading" />
25
25
 
26
26
  <template v-else>
@@ -69,7 +69,7 @@
69
69
  )}`"
70
70
  />
71
71
  </div> </v-col
72
- ></v-row>
72
+ ></ListMoleculesDenseItemContainer>
73
73
  </template>
74
74
 
75
75
  <script setup>
@@ -30,11 +30,14 @@
30
30
  "file": "document | document | documents",
31
31
  "mailing": "mailing | mailing | mailings",
32
32
  "news": "news | news | news",
33
- "people": "one | person | people",
33
+ "people": "fellow | fellow | fellows",
34
34
  "projects": "project | project | projects",
35
+ "videos": "video | video | videos",
35
36
  "publications": "publication | publication | publications",
36
37
  "tags": "tag | tag | tags",
37
- "user": "user | user | users"
38
+ "user": "user | user | users",
39
+ "initiatives": "initiative | initiative | initiatives",
40
+ "platforms": "platform | platform | platforms"
38
41
  },
39
42
  "learn-more": "read more",
40
43
  "list": {
@@ -386,5 +389,8 @@
386
389
  "search": "Search",
387
390
  "items-per-page": "Items per page",
388
391
  "click-here-to-search": "Click here to search",
389
- "filter-by-type": "Filter by type"
392
+ "filter-by-type": "Filter by type",
393
+ "see-details": "See details",
394
+ "show-less": "Show less",
395
+ "show-more": "Show more"
390
396
  }
@@ -24,7 +24,7 @@
24
24
  "fellow": "résident | résident | résidents",
25
25
  "fellowships": "programme d'accueil | programme d'accueil | programmes d'accueil",
26
26
  "news": "actualité | actualités | actualités",
27
- "people": "personne | personne | personnes",
27
+ "people": "résident | résident | résidents",
28
28
  "projects": "projet | projet | projets",
29
29
  "publications": "publication | publications | publications",
30
30
  "activities": "activité | activité | activités",
@@ -33,8 +33,11 @@
33
33
  "disciplines": "discipline | discipline | disciplines",
34
34
  "file": "document | document | documents",
35
35
  "mailing": "mailing | mailing | mailings",
36
+ "videos": "vidéo | vidéo | vidéos",
36
37
  "tags": "tag | tag | tags",
37
- "user": "utilisateur | utilisateur | utilisateurs"
38
+ "user": "utilisateur | utilisateur | utilisateurs",
39
+ "initiatives": "initiative | initiative | initiatives",
40
+ "platforms": "plateforme | plateforme | plateformes"
38
41
  },
39
42
  "learn-more": "En savoir plus",
40
43
  "list": {
@@ -387,5 +390,8 @@
387
390
  "search": "Rechercher",
388
391
  "items-per-page": "Éléments par page",
389
392
  "click-here-to-search": "Cliquez ici pour rechercher",
390
- "filter-by-type": "Filtrer par type"
393
+ "filter-by-type": "Filtrer par type",
394
+ "see-details": "Voir les détails",
395
+ "show-less": "Voir moins",
396
+ "show-more": "Voir plus"
391
397
  }
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "license": "AGPL-3.0-only",
3
3
  "main": "./dist/module.mjs",
4
- "version": "1.0.193",
4
+ "version": "1.1.0",
5
5
  "name": "@paris-ias/list",
6
6
  "repository": {
7
7
  "url": "git+https://github.com/IEA-Paris/list.git",
8
8
  "type": "git"
9
9
  },
10
10
  "dependencies": {
11
- "@paris-ias/trees": "^2.0.49"
11
+ "@paris-ias/trees": "^2.1.0"
12
12
  },
13
13
  "description": "Paris IAS List Module",
14
14
  "peerDependencies": {