@paris-ias/list 1.0.86 → 1.0.87
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 +1 -1
- package/dist/runtime/components/list/atoms/SearchInput.vue +6 -2
- package/dist/runtime/components/list/atoms/SearchItem.vue +7 -3
- package/dist/runtime/components/misc/atoms/Socials.vue +5 -6
- package/dist/runtime/components/people/RowsItem.vue +6 -2
- package/dist/runtime/components/people/View.vue +7 -1
- package/dist/runtime/graphql/list/people.gql +1 -0
- package/dist/runtime/stores/root.d.ts +29 -4
- package/dist/runtime/stores/root.js +68 -47
- package/dist/runtime/translations/en.json +2 -1
- package/dist/runtime/translations/fr.json +2 -1
- package/package.json +1 -1
- package/dist/runtime/components/events/SlidingItem.vue +0 -82
package/dist/module.json
CHANGED
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
$store.state.scrolled }" -->
|
|
18
18
|
<template v-if="!search" #label>
|
|
19
19
|
<div class="searchLabel">
|
|
20
|
-
{{
|
|
20
|
+
{{
|
|
21
|
+
type === "all"
|
|
22
|
+
? $t("search")
|
|
23
|
+
: $t("list.search-type", [$t("items." + type, 2)])
|
|
24
|
+
}}
|
|
21
25
|
</div>
|
|
22
26
|
</template>
|
|
23
27
|
</v-text-field>
|
|
@@ -37,7 +41,7 @@
|
|
|
37
41
|
v-if="item.type && item.type === 'subheader'"
|
|
38
42
|
:key="'subheader-' + index"
|
|
39
43
|
>
|
|
40
|
-
{{ item.name }}
|
|
44
|
+
{{ $t(item.name) }}
|
|
41
45
|
</v-list-subheader>
|
|
42
46
|
<div
|
|
43
47
|
v-else-if="item.type && item.type === 'no-result'"
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-list-item
|
|
3
|
-
|
|
2
|
+
<v-list-item
|
|
3
|
+
:key="index.toString() + item.id"
|
|
4
|
+
:to="localePath(getPath(item.id))"
|
|
5
|
+
class="search-item py-2"
|
|
6
|
+
>
|
|
7
|
+
<template #item />
|
|
4
8
|
<template v-if="item.prependAvatar" #prepend>
|
|
5
9
|
<v-avatar rounded="0" :image="item.prependAvatar" />
|
|
6
10
|
</template>
|
|
@@ -14,7 +18,7 @@
|
|
|
14
18
|
<v-btn
|
|
15
19
|
icon="mdi-open-in-new"
|
|
16
20
|
variant="text"
|
|
17
|
-
:to="localePath(getPath(item.id))"
|
|
21
|
+
:to="localePath(getPath(item.slug || item.id))"
|
|
18
22
|
target="_blank"
|
|
19
23
|
/>
|
|
20
24
|
</template>
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
<v-row no-gutters>
|
|
3
3
|
<v-col>
|
|
4
4
|
<template v-for="(value, key, index) in socials" :key="key + value">
|
|
5
|
-
<v-tooltip
|
|
6
|
-
<template
|
|
5
|
+
<v-tooltip v-if="value" :location="location">
|
|
6
|
+
<template #activator="{ props }">
|
|
7
7
|
<v-btn
|
|
8
8
|
tile
|
|
9
9
|
v-bind="mergeProps(props, attrs)"
|
|
10
10
|
target="_blank"
|
|
11
11
|
:href="getSocialId(key, value)"
|
|
12
|
-
:color="dark ? '
|
|
12
|
+
:color="dark ? 'transparent' : 'white'"
|
|
13
13
|
flat
|
|
14
14
|
>
|
|
15
15
|
<v-icon :color="dark ? 'white' : 'black'">
|
|
@@ -29,10 +29,9 @@
|
|
|
29
29
|
</template>
|
|
30
30
|
|
|
31
31
|
<script setup>
|
|
32
|
-
import { useAttrs } from "vue";
|
|
33
|
-
import { mergeProps } from "vue";
|
|
34
|
-
const attrs = useAttrs();
|
|
32
|
+
import { useAttrs, mergeProps } from "vue";
|
|
35
33
|
import { useDisplay } from "vuetify";
|
|
34
|
+
const attrs = useAttrs();
|
|
36
35
|
const { name } = useDisplay();
|
|
37
36
|
const props = defineProps({
|
|
38
37
|
socials: {
|
|
@@ -53,11 +53,15 @@
|
|
|
53
53
|
<MiscAtomsSocials v-if="item.socials" :socials="item.socials" />
|
|
54
54
|
<PeopleGroupBadges :item="item" />
|
|
55
55
|
<div
|
|
56
|
-
v-if="item.
|
|
56
|
+
v-if="item.summary && item.summary.length > 0"
|
|
57
57
|
class="text-wrap clamped-text text-black"
|
|
58
58
|
:style="'-webkit-line-clamp:' + lineClamp"
|
|
59
59
|
>
|
|
60
|
-
<MDC :value="item.
|
|
60
|
+
<MDC :value="item.summary" />
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div v-else class="text-body-2">
|
|
64
|
+
{{ $t("no-biography") }}
|
|
61
65
|
</div>
|
|
62
66
|
</div>
|
|
63
67
|
</v-col>
|
|
@@ -84,7 +84,13 @@
|
|
|
84
84
|
"
|
|
85
85
|
/>
|
|
86
86
|
<template v-else class="my-6 flex-wrap">
|
|
87
|
-
<MDC
|
|
87
|
+
<MDC
|
|
88
|
+
v-if="item.biography && item.biography.length > 0"
|
|
89
|
+
:value="item.biography"
|
|
90
|
+
/>
|
|
91
|
+
<div v-else class="text-body-2">
|
|
92
|
+
{{ $t("no-biography") }}
|
|
93
|
+
</div>
|
|
88
94
|
</template>
|
|
89
95
|
|
|
90
96
|
<!-- DIVIDERS -->
|
|
@@ -1,4 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
interface SearchResults {
|
|
2
|
+
events: Record<string, unknown>;
|
|
3
|
+
news: Record<string, unknown>;
|
|
4
|
+
people: Record<string, unknown>;
|
|
5
|
+
projects: Record<string, unknown>;
|
|
6
|
+
fellowships: Record<string, unknown>;
|
|
7
|
+
publications: Record<string, unknown>;
|
|
8
|
+
actions: Record<string, unknown>;
|
|
9
|
+
affiliations: Record<string, unknown>;
|
|
10
|
+
disciplines: Record<string, unknown>;
|
|
11
|
+
files: Record<string, unknown>;
|
|
12
|
+
mailing: Record<string, unknown>;
|
|
13
|
+
tags: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
interface RootStoreState {
|
|
16
|
+
scrolled: boolean;
|
|
17
|
+
loading: boolean;
|
|
18
|
+
total: number;
|
|
19
|
+
skip: number;
|
|
20
|
+
page: number;
|
|
21
|
+
numberOfPages: number;
|
|
22
|
+
search: string;
|
|
23
|
+
results: SearchResults;
|
|
24
|
+
}
|
|
25
|
+
export declare const useRootStore: import("pinia").StoreDefinition<"rootStore", RootStoreState, {}, {
|
|
2
26
|
setLoading(value: boolean, type?: string): void;
|
|
3
27
|
setScrolled(): void;
|
|
4
28
|
saveFiltersToLocalStorage(type: string): void;
|
|
@@ -7,8 +31,8 @@ export declare const useRootStore: import("pinia").StoreDefinition<"rootStore",
|
|
|
7
31
|
setFiltersCount(type: string): void;
|
|
8
32
|
updateRouteQuery(type: string): void;
|
|
9
33
|
resetState(type: string): void;
|
|
10
|
-
updateSort({ value, type }: {
|
|
11
|
-
value: number
|
|
34
|
+
updateSort({ value, type, }: {
|
|
35
|
+
value: (number | string)[];
|
|
12
36
|
type: string;
|
|
13
37
|
}): void;
|
|
14
38
|
updateView({ value, type }: {
|
|
@@ -16,7 +40,7 @@ export declare const useRootStore: import("pinia").StoreDefinition<"rootStore",
|
|
|
16
40
|
type: string;
|
|
17
41
|
}): void;
|
|
18
42
|
updateLocalStorage(key: string, value: string): void;
|
|
19
|
-
updateFilter(key: string, val:
|
|
43
|
+
updateFilter(key: string, val: unknown, type: string): void;
|
|
20
44
|
updateItemsPerPage({ value, type }: {
|
|
21
45
|
value: number;
|
|
22
46
|
type: string;
|
|
@@ -32,3 +56,4 @@ export declare const useRootStore: import("pinia").StoreDefinition<"rootStore",
|
|
|
32
56
|
}): Promise<void>;
|
|
33
57
|
update(type: string, lang?: string): Promise<boolean>;
|
|
34
58
|
}>;
|
|
59
|
+
export {};
|
|
@@ -3,7 +3,7 @@ import SEARCH from "../graphql/list/search.gql";
|
|
|
3
3
|
import { useNuxtApp, useRouter, useAsyncQuery } from "#imports";
|
|
4
4
|
export const useRootStore = defineStore("rootStore", {
|
|
5
5
|
state: () => ({
|
|
6
|
-
scrolled:
|
|
6
|
+
scrolled: typeof window !== "undefined" ? window.scrollY > 0 : false,
|
|
7
7
|
loading: false,
|
|
8
8
|
total: 0,
|
|
9
9
|
skip: 0,
|
|
@@ -29,16 +29,18 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
29
29
|
setLoading(value, type = "") {
|
|
30
30
|
const { $stores } = useNuxtApp();
|
|
31
31
|
this.loading = value;
|
|
32
|
-
if (type.length
|
|
32
|
+
if (type.length && $stores[type]) {
|
|
33
|
+
$stores[type].loading = value;
|
|
34
|
+
}
|
|
33
35
|
},
|
|
34
36
|
setScrolled() {
|
|
35
|
-
if (
|
|
37
|
+
if (typeof window !== "undefined") {
|
|
36
38
|
this.scrolled = window.scrollY > 0;
|
|
37
39
|
}
|
|
38
40
|
},
|
|
39
41
|
saveFiltersToLocalStorage(type) {
|
|
40
42
|
const { $stores } = useNuxtApp();
|
|
41
|
-
const filters = $stores[type]
|
|
43
|
+
const filters = $stores[type]?.filters ?? {};
|
|
42
44
|
const values = Object.fromEntries(
|
|
43
45
|
Object.entries(filters).map(([key, filter]) => [key, filter.value])
|
|
44
46
|
);
|
|
@@ -50,11 +52,14 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
50
52
|
const { $stores } = useNuxtApp();
|
|
51
53
|
const { currentRoute } = useRouter();
|
|
52
54
|
const query = currentRoute.value.query;
|
|
53
|
-
const filters = $stores[type]
|
|
55
|
+
const filters = $stores[type]?.filters ?? {};
|
|
54
56
|
if (Object.keys(query)?.length) {
|
|
55
57
|
Object.keys(query).forEach((filter) => {
|
|
56
58
|
if (filter in filters) {
|
|
57
|
-
|
|
59
|
+
const queryValue = query[filter];
|
|
60
|
+
if (typeof queryValue === "string") {
|
|
61
|
+
filters[filter].value = filters[filter].multiple ? JSON.parse(queryValue) : queryValue;
|
|
62
|
+
}
|
|
58
63
|
}
|
|
59
64
|
});
|
|
60
65
|
}
|
|
@@ -66,10 +71,10 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
66
71
|
const local = JSON.parse(localStorage.getItem("PARIS_IAS") || "{}");
|
|
67
72
|
const saved = local[`${type}_filters`] ?? null;
|
|
68
73
|
if (!saved) {
|
|
69
|
-
console.log(`[${type}]
|
|
74
|
+
console.log(`[${type}] No data to restore.`);
|
|
70
75
|
return;
|
|
71
76
|
}
|
|
72
|
-
const filters = $stores[type]
|
|
77
|
+
const filters = $stores[type]?.filters ?? {};
|
|
73
78
|
for (const [key, value] of Object.entries(saved)) {
|
|
74
79
|
if (filters[key]) {
|
|
75
80
|
filters[key].value = value;
|
|
@@ -77,17 +82,19 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
77
82
|
}
|
|
78
83
|
this.setFiltersCount(type);
|
|
79
84
|
this.updateRouteQuery(type);
|
|
80
|
-
console.log(`[${type}]
|
|
85
|
+
console.log(`[${type}] Filters restored from localStorage`, saved);
|
|
81
86
|
},
|
|
82
87
|
setFiltersCount(type) {
|
|
83
88
|
const { $stores } = useNuxtApp();
|
|
84
|
-
const filters = $stores[type]
|
|
89
|
+
const filters = $stores[type]?.filters ?? {};
|
|
85
90
|
const count = Object.values(filters).reduce((acc, filter) => {
|
|
86
91
|
const value = filter?.value;
|
|
87
92
|
const isEmpty = value === void 0 || value === null || Array.isArray(value) && value.length === 0 || typeof value === "string" && value.trim() === "";
|
|
88
93
|
return isEmpty ? acc : acc + 1;
|
|
89
94
|
}, 0);
|
|
90
|
-
$stores[type]
|
|
95
|
+
if ($stores[type]) {
|
|
96
|
+
$stores[type].filtersCount = count;
|
|
97
|
+
}
|
|
91
98
|
},
|
|
92
99
|
updateRouteQuery(type) {
|
|
93
100
|
const router = useRouter();
|
|
@@ -95,7 +102,7 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
95
102
|
const routeQuery = {
|
|
96
103
|
...this.search ? { search: this.search } : {},
|
|
97
104
|
...this.page > 1 ? { page: this.page.toString() } : {},
|
|
98
|
-
...Object.entries($stores[type]
|
|
105
|
+
...Object.entries($stores[type]?.filters ?? {}).reduce(
|
|
99
106
|
(acc, [key, filter]) => {
|
|
100
107
|
const value = filter?.value;
|
|
101
108
|
console.log("valueStore", value);
|
|
@@ -103,7 +110,7 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
103
110
|
if (isEmpty) return acc;
|
|
104
111
|
return {
|
|
105
112
|
...acc,
|
|
106
|
-
[key]: Array.isArray(value) ? JSON.stringify(value) : value
|
|
113
|
+
[key]: Array.isArray(value) ? JSON.stringify(value) : String(value)
|
|
107
114
|
};
|
|
108
115
|
},
|
|
109
116
|
{}
|
|
@@ -114,8 +121,9 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
114
121
|
resetState(type) {
|
|
115
122
|
const { $stores, $models } = useNuxtApp();
|
|
116
123
|
console.log("$models[type]: ", $models[type]);
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
if ($models[type] && $stores[type]) {
|
|
125
|
+
$stores[type] = $models[type];
|
|
126
|
+
}
|
|
119
127
|
console.log("resetState");
|
|
120
128
|
this.search = "";
|
|
121
129
|
this.page = 1;
|
|
@@ -127,32 +135,41 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
127
135
|
this.setFiltersCount(type);
|
|
128
136
|
this.updateRouteQuery(type);
|
|
129
137
|
},
|
|
130
|
-
updateSort({
|
|
138
|
+
updateSort({
|
|
139
|
+
value,
|
|
140
|
+
type
|
|
141
|
+
}) {
|
|
131
142
|
const { $stores } = useNuxtApp();
|
|
132
|
-
$stores[type]
|
|
133
|
-
|
|
143
|
+
if ($stores[type]) {
|
|
144
|
+
$stores[type].sortBy = [String(value[0])];
|
|
145
|
+
$stores[type].sortDesc = [Number(value[1])];
|
|
146
|
+
}
|
|
134
147
|
this.page = 1;
|
|
135
148
|
this.updateLocalStorage(type + "_sort", value.join("_"));
|
|
136
149
|
this.update(type);
|
|
137
150
|
},
|
|
138
151
|
updateView({ value, type }) {
|
|
139
152
|
const { $stores } = useNuxtApp();
|
|
140
|
-
$stores[type]
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
153
|
+
if ($stores[type]?.views?.[value]) {
|
|
154
|
+
$stores[type].view = {
|
|
155
|
+
...$stores[type].views[value],
|
|
156
|
+
name: value
|
|
157
|
+
};
|
|
158
|
+
}
|
|
144
159
|
this.updateLocalStorage(type + "_view", value);
|
|
145
160
|
this.update(type);
|
|
146
161
|
},
|
|
147
162
|
updateLocalStorage(key, value) {
|
|
148
|
-
const local = JSON.parse(localStorage.getItem("PARIS_IAS")
|
|
163
|
+
const local = JSON.parse(localStorage.getItem("PARIS_IAS") || "{}");
|
|
149
164
|
local[key] = value;
|
|
150
165
|
localStorage.setItem("PARIS_IAS", JSON.stringify(local));
|
|
151
166
|
},
|
|
152
167
|
updateFilter(key, val, type) {
|
|
153
168
|
const { $stores } = useNuxtApp();
|
|
154
169
|
console.log("update filter: ", { key, val, type });
|
|
155
|
-
$stores[type]
|
|
170
|
+
if ($stores[type]?.filters?.[key]) {
|
|
171
|
+
$stores[type].filters[key].value = val;
|
|
172
|
+
}
|
|
156
173
|
this.setFiltersCount(type);
|
|
157
174
|
this.saveFiltersToLocalStorage(type);
|
|
158
175
|
this.updateRouteQuery(type);
|
|
@@ -162,7 +179,9 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
162
179
|
updateItemsPerPage({ value, type }) {
|
|
163
180
|
const { $stores } = useNuxtApp();
|
|
164
181
|
this.page = 1;
|
|
165
|
-
$stores[type]
|
|
182
|
+
if ($stores[type]) {
|
|
183
|
+
$stores[type].itemsPerPage = value;
|
|
184
|
+
}
|
|
166
185
|
this.update(type);
|
|
167
186
|
},
|
|
168
187
|
updatePage({ page, type }) {
|
|
@@ -181,15 +200,16 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
181
200
|
async update(type, lang = "en") {
|
|
182
201
|
const { $stores, $queries } = useNuxtApp();
|
|
183
202
|
this.setLoading(true);
|
|
184
|
-
if (type !== "all") {
|
|
203
|
+
if (type !== "all" && $stores[type]) {
|
|
185
204
|
$stores[type].loading = true;
|
|
186
205
|
}
|
|
187
|
-
const itemsPerPage = type === "all" ? 3 : $stores[type]?.itemsPerPage;
|
|
206
|
+
const itemsPerPage = type === "all" ? 3 : $stores[type]?.itemsPerPage || 10;
|
|
188
207
|
const filters = {};
|
|
189
208
|
if (type !== "all") {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
209
|
+
const storeFilters = $stores[type]?.filters ?? {};
|
|
210
|
+
for (const filter in storeFilters) {
|
|
211
|
+
const filterValue = storeFilters[filter]?.value;
|
|
212
|
+
if (typeof filterValue !== "undefined" && filterValue !== null && (Array.isArray(filterValue) ? filterValue.length > 0 : true)) {
|
|
193
213
|
filters[filter] = filterValue;
|
|
194
214
|
}
|
|
195
215
|
}
|
|
@@ -202,8 +222,8 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
202
222
|
// limit
|
|
203
223
|
limit: itemsPerPage,
|
|
204
224
|
// sort, array of keys and array of directions - to have x tie breakers if necessary
|
|
205
|
-
sortBy: type === "all" ? "searchScore" : $stores[type]
|
|
206
|
-
sortDesc: type === "all" ? -1 : $stores[type]
|
|
225
|
+
sortBy: type === "all" ? "searchScore" : $stores[type]?.sortBy || ["created"],
|
|
226
|
+
sortDesc: type === "all" ? -1 : ($stores[type]?.sortDesc?.[0] || 0) > 0 ? true : false,
|
|
207
227
|
// search (if set)
|
|
208
228
|
...this.search?.length && type !== "all" && { search: this.search },
|
|
209
229
|
// add the store module filters
|
|
@@ -215,32 +235,33 @@ export const useRootStore = defineStore("rootStore", {
|
|
|
215
235
|
})
|
|
216
236
|
);
|
|
217
237
|
args.options.filters = JSON.stringify(args.options.filters);
|
|
218
|
-
let result = {};
|
|
219
238
|
console.log("args: ", args);
|
|
220
239
|
console.log(`Fetching ${type}`);
|
|
221
240
|
const { data, error } = await useAsyncQuery(
|
|
222
|
-
type === "all" ? SEARCH : $queries[type]
|
|
241
|
+
type === "all" ? SEARCH : $queries[type]?.list,
|
|
223
242
|
args
|
|
224
243
|
);
|
|
225
244
|
console.log("data: ", data);
|
|
226
245
|
if (error.value) console.log(error.value);
|
|
227
246
|
const key = type === "all" ? "search" : "list" + type.charAt(0).toUpperCase() + type.slice(1);
|
|
228
247
|
if (type === "all") {
|
|
229
|
-
this.results = data?.value?.[key];
|
|
248
|
+
this.results = data?.value?.[key] || this.results;
|
|
230
249
|
} else {
|
|
231
250
|
const items = data?.value?.[key]?.items ?? [];
|
|
232
|
-
$stores[type]
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
...rest
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
251
|
+
if ($stores[type]) {
|
|
252
|
+
$stores[type].total = data?.value?.[key]?.total || 0;
|
|
253
|
+
const result = {
|
|
254
|
+
...data?.value?.[key],
|
|
255
|
+
items: items.map(({ id, ...rest }) => ({
|
|
256
|
+
...rest,
|
|
257
|
+
_path: `/${id}`
|
|
258
|
+
}))
|
|
259
|
+
};
|
|
260
|
+
$stores[type].items = result.items;
|
|
261
|
+
const lastPage = Math.ceil((result.total || 0) / itemsPerPage);
|
|
262
|
+
this.setFiltersCount(type);
|
|
263
|
+
$stores[type].numberOfPages = lastPage;
|
|
264
|
+
}
|
|
244
265
|
}
|
|
245
266
|
this.setLoading(false, type);
|
|
246
267
|
return true;
|
|
@@ -338,5 +338,6 @@
|
|
|
338
338
|
"visit-this-publications-website": "Visit this publication webpage",
|
|
339
339
|
"close-the-filter-panel": "Close the filter panel",
|
|
340
340
|
"list.by-vintage-from-recent-to-old": "By Year, from recent to old",
|
|
341
|
-
"list.by-vintage-from-old-to-recent": "By Year, from old to recent"
|
|
341
|
+
"list.by-vintage-from-old-to-recent": "By Year, from old to recent",
|
|
342
|
+
"no-biography": "No biography available"
|
|
342
343
|
}
|
|
@@ -336,5 +336,6 @@
|
|
|
336
336
|
"groups": "Catégorie",
|
|
337
337
|
"present": "aujourd'hui",
|
|
338
338
|
"visit-this-project-website": "Visitez le site Web du projet",
|
|
339
|
-
"visit-this-publications-website": "Visitez la page Web de cette publication"
|
|
339
|
+
"visit-this-publications-website": "Visitez la page Web de cette publication",
|
|
340
|
+
"no-biography": "Aucune biographie disponible"
|
|
340
341
|
}
|
package/package.json
CHANGED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-card
|
|
3
|
-
v-ripple
|
|
4
|
-
class="event-sliding-item cursor-pointer"
|
|
5
|
-
elevation="2"
|
|
6
|
-
height="280"
|
|
7
|
-
@click="$router.push(localePath('/activities/events/' + item.slug[locale]))"
|
|
8
|
-
>
|
|
9
|
-
<v-card-text class="pa-4 d-flex flex-column h-100">
|
|
10
|
-
<!-- Date section -->
|
|
11
|
-
<div class="text-overline font-weight-black mb-2 text-primary">
|
|
12
|
-
{{
|
|
13
|
-
new Date(item.start).toLocaleDateString(locale, {
|
|
14
|
-
year: "numeric",
|
|
15
|
-
month: "short",
|
|
16
|
-
day: "numeric",
|
|
17
|
-
})
|
|
18
|
-
}}
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
|
-
<!-- Category badge -->
|
|
22
|
-
<v-chip
|
|
23
|
-
v-if="item.category"
|
|
24
|
-
size="small"
|
|
25
|
-
variant="outlined"
|
|
26
|
-
color="primary"
|
|
27
|
-
class="mb-3 align-self-start"
|
|
28
|
-
>
|
|
29
|
-
{{ $t("list.filters.events.category." + item.category) }}
|
|
30
|
-
</v-chip>
|
|
31
|
-
|
|
32
|
-
<!-- Event title -->
|
|
33
|
-
<h3 class="text-h6 font-weight-bold mb-3 line-clamp-3">
|
|
34
|
-
{{ item.name }}
|
|
35
|
-
</h3>
|
|
36
|
-
|
|
37
|
-
<!-- Event description if available -->
|
|
38
|
-
<p v-if="item.description" class="text-body-2 text-grey-darken-2 line-clamp-4 flex-grow-1">
|
|
39
|
-
{{ item.description }}
|
|
40
|
-
</p>
|
|
41
|
-
|
|
42
|
-
<!-- Location if available -->
|
|
43
|
-
<div v-if="item.location" class="mt-auto pt-2">
|
|
44
|
-
<v-icon size="small" class="mr-1">mdi-map-marker</v-icon>
|
|
45
|
-
<span class="text-caption text-grey-darken-1">{{ item.location }}</span>
|
|
46
|
-
</div>
|
|
47
|
-
</v-card-text>
|
|
48
|
-
|
|
49
|
-
<!-- Hover overlay -->
|
|
50
|
-
<v-overlay
|
|
51
|
-
v-model="isHovered"
|
|
52
|
-
contained
|
|
53
|
-
opacity="0.1"
|
|
54
|
-
class="d-flex align-center justify-center"
|
|
55
|
-
>
|
|
56
|
-
<v-icon size="large" color="primary">mdi-eye</v-icon>
|
|
57
|
-
</v-overlay>
|
|
58
|
-
</v-card>
|
|
59
|
-
</template>
|
|
60
|
-
|
|
61
|
-
<script setup>
|
|
62
|
-
import { ref } from "vue";
|
|
63
|
-
import { useLocalePath, useI18n } from "#imports";
|
|
64
|
-
const { locale } = useI18n();
|
|
65
|
-
const localePath = useLocalePath();
|
|
66
|
-
const props = defineProps({
|
|
67
|
-
item: {
|
|
68
|
-
type: Object,
|
|
69
|
-
required: true
|
|
70
|
-
},
|
|
71
|
-
index: {
|
|
72
|
-
type: Number,
|
|
73
|
-
required: true
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
const { item, index } = props;
|
|
77
|
-
const isHovered = ref(false);
|
|
78
|
-
</script>
|
|
79
|
-
|
|
80
|
-
<style scoped>
|
|
81
|
-
.event-sliding-item{border-radius:12px;overflow:hidden;transition:all .3s cubic-bezier(.25,.8,.25,1)}.event-sliding-item:hover{box-shadow:0 8px 25px rgba(0,0,0,.15)!important;transform:translateY(-4px)}.line-clamp-3{-webkit-line-clamp:3;line-clamp:3}.line-clamp-3,.line-clamp-4{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}.line-clamp-4{-webkit-line-clamp:4;line-clamp:4}
|
|
82
|
-
</style>
|