@paris-ias/list 1.0.194 → 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.194",
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>
@@ -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
@@ -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>
@@ -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>
@@ -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>
@@ -38,7 +38,7 @@
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
  )}`"
@@ -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.194",
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": {