@paris-ias/list 1.0.4 → 1.0.7

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 (99) hide show
  1. package/dist/module.ts +119 -0
  2. package/dist/runtime/components/events/Badges.vue +73 -0
  3. package/dist/runtime/components/events/DateTimePlace.vue +77 -0
  4. package/dist/runtime/components/events/DenseItem.vue +40 -0
  5. package/dist/runtime/components/events/ExpandedItem.vue +11 -0
  6. package/dist/runtime/components/events/ListContainer.vue +41 -0
  7. package/dist/runtime/components/events/RegisterModal.vue +51 -0
  8. package/dist/runtime/components/events/RelatedItem.vue +44 -0
  9. package/dist/runtime/components/events/RowsItem.vue +114 -0
  10. package/dist/runtime/components/events/View.vue +333 -0
  11. package/dist/runtime/components/fellowships/Badges.vue +48 -0
  12. package/dist/runtime/components/fellowships/DenseItem.vue +39 -0
  13. package/dist/runtime/components/fellowships/ExpandedItem.vue +7 -0
  14. package/dist/runtime/components/fellowships/RegisterModal.vue +41 -0
  15. package/dist/runtime/components/fellowships/RowsItem.vue +61 -0
  16. package/dist/runtime/components/fellowships/View.vue +210 -0
  17. package/dist/runtime/components/list/atoms/FiltersMenu.vue +46 -0
  18. package/dist/runtime/components/list/atoms/SearchInput.vue +129 -0
  19. package/dist/runtime/components/list/atoms/SearchItem.vue +59 -0
  20. package/dist/runtime/components/list/atoms/SearchString.vue +161 -0
  21. package/dist/runtime/components/list/atoms/SortMenu.vue +97 -0
  22. package/dist/runtime/components/list/atoms/ViewMenu.vue +71 -0
  23. package/dist/runtime/components/list/inputs/AutoComplete.vue +22 -0
  24. package/dist/runtime/components/list/inputs/BooleanSwitch.vue +18 -0
  25. package/dist/runtime/components/list/inputs/Checkbox.vue +21 -0
  26. package/dist/runtime/components/list/inputs/Select.vue +25 -0
  27. package/dist/runtime/components/list/molecules/Filters.vue +97 -0
  28. package/dist/runtime/components/list/molecules/Header.vue +47 -0
  29. package/dist/runtime/components/list/molecules/Pagination.vue +243 -0
  30. package/dist/runtime/components/list/organisms/List.vue +92 -0
  31. package/dist/runtime/components/list/views/Dense.vue +25 -0
  32. package/dist/runtime/components/list/views/Expanded.vue +10 -0
  33. package/dist/runtime/components/list/views/Grid.vue +13 -0
  34. package/dist/runtime/components/list/views/Rows.vue +13 -0
  35. package/dist/runtime/components/list/views/Table.vue +13 -0
  36. package/dist/runtime/components/misc/atoms/CountUp.vue +198 -0
  37. package/dist/runtime/components/misc/atoms/DateStamp.vue +104 -0
  38. package/dist/runtime/components/misc/atoms/ImageContainer.vue +105 -0
  39. package/dist/runtime/components/misc/atoms/ShareMenu.vue +60 -0
  40. package/dist/runtime/components/misc/atoms/Socials.vue +127 -0
  41. package/dist/runtime/components/misc/molecules/ChipContainer.vue +35 -0
  42. package/dist/runtime/components/misc/molecules/Related.vue +41 -0
  43. package/dist/runtime/components/misc/molecules/RelatedItems.vue +29 -0
  44. package/dist/runtime/components/misc/molecules/SearchItem.vue +26 -0
  45. package/dist/runtime/components/news/DenseItem.vue +62 -0
  46. package/dist/runtime/components/news/ExpandedItem.vue +153 -0
  47. package/dist/runtime/components/news/Header.vue +9 -0
  48. package/dist/runtime/components/news/RelatedItem.vue +44 -0
  49. package/dist/runtime/components/news/RowsItem.vue +160 -0
  50. package/dist/runtime/components/news/View.vue +190 -0
  51. package/dist/runtime/components/people/DenseItem.vue +37 -0
  52. package/dist/runtime/components/people/ExpandedItem.vue +16 -0
  53. package/dist/runtime/components/people/GroupBadges.vue +56 -0
  54. package/dist/runtime/components/people/RelatedItem.vue +41 -0
  55. package/dist/runtime/components/people/RowsItem.vue +95 -0
  56. package/dist/runtime/components/people/View.vue +162 -0
  57. package/dist/runtime/components/projects/ExpandedItem.vue +14 -0
  58. package/dist/runtime/components/projects/RelatedItem.vue +44 -0
  59. package/dist/runtime/components/projects/RowsItem.vue +106 -0
  60. package/dist/runtime/components/projects/View.vue +131 -0
  61. package/dist/runtime/components/publications/RelatedItem.vue +44 -0
  62. package/dist/runtime/components/publications/RowsItem.vue +105 -0
  63. package/dist/runtime/components/publications/View.vue +139 -0
  64. package/dist/runtime/composables/useFetchItem.ts +64 -0
  65. package/dist/runtime/composables/useIcons.ts +30 -0
  66. package/dist/runtime/composables/useUtils.ts +75 -0
  67. package/dist/runtime/graphql/queries/buildFiltersValues.gql +35 -0
  68. package/dist/runtime/graphql/queries/item/action.gql +0 -0
  69. package/dist/runtime/graphql/queries/item/apps.gql +0 -0
  70. package/dist/runtime/graphql/queries/item/events.gql +120 -0
  71. package/dist/runtime/graphql/queries/item/fellowships.gql +164 -0
  72. package/dist/runtime/graphql/queries/item/news.gql +129 -0
  73. package/dist/runtime/graphql/queries/item/people.gql +174 -0
  74. package/dist/runtime/graphql/queries/item/projects.gql +171 -0
  75. package/dist/runtime/graphql/queries/item/publications.gql +169 -0
  76. package/dist/runtime/graphql/queries/item/users.gql +0 -0
  77. package/dist/runtime/graphql/queries/list/action.gql +0 -0
  78. package/dist/runtime/graphql/queries/list/apps.gql +32 -0
  79. package/dist/runtime/graphql/queries/list/events.gql +44 -0
  80. package/dist/runtime/graphql/queries/list/fellowships.gql +53 -0
  81. package/dist/runtime/graphql/queries/list/news.gql +39 -0
  82. package/dist/runtime/graphql/queries/list/people.gql +49 -0
  83. package/dist/runtime/graphql/queries/list/projects.gql +37 -0
  84. package/dist/runtime/graphql/queries/list/publications.gql +37 -0
  85. package/dist/runtime/graphql/queries/list/search.gql +148 -0
  86. package/dist/runtime/graphql/queries/list/users.gql +32 -0
  87. package/dist/runtime/graphql/queries/login.gql +0 -0
  88. package/dist/runtime/plugins/pinia.ts +88 -0
  89. package/dist/runtime/plugins/vuetify.js +21 -0
  90. package/dist/runtime/stores/factory.ts +18 -0
  91. package/dist/runtime/stores/root.ts +353 -0
  92. package/dist/runtime/translations/en.json +436 -0
  93. package/dist/runtime/translations/fr.json +429 -0
  94. package/dist/runtime/types/imports.d.ts +13 -0
  95. package/dist/runtime/types/stores.d.ts +11 -0
  96. package/example/.env.example +3 -0
  97. package/example/nuxt.config.ts +19 -0
  98. package/example/pages/index.vue +27 -0
  99. package/package.json +8 -17
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <v-row>
3
+ <slot>
4
+ <!-- fallback content -->
5
+ Rows view
6
+ </slot>
7
+ </v-row>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ /* const { signIn } = useSession() */
12
+ onMounted(() => {})
13
+ </script>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ GRID DISPLAY
3
+ <template v-for="(item, index) in items" :key="index">
4
+ <slot name="data" :item="item" />
5
+ </template>
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ const props = defineProps({
10
+ items: [Object],
11
+ type: String,
12
+ })
13
+ </script>
@@ -0,0 +1,198 @@
1
+ <template>
2
+ <span> {{ displayedAmount }}</span>
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ /*
7
+ /!\ This component is a port by Antoine Cordelois
8
+ of the vue3-autocounter package made by ps-christopher
9
+ into a nuxt composition API component.
10
+
11
+ Source code is here: https://github.com/ps-cristopher/vue3-autocounter/blob/master/src/vue3-autocounter.vue
12
+ */
13
+ interface Data {
14
+ timestamp: number
15
+ startTimestamp: number | null
16
+ currentStartAmount: number
17
+ currentAmount: number
18
+ currentDuration: number
19
+ paused: boolean
20
+ remaining: number
21
+ animationFrame: number
22
+ }
23
+
24
+ // Props
25
+ const props = defineProps({
26
+ startAmount: {
27
+ type: Number,
28
+ default: 0,
29
+ },
30
+ endAmount: {
31
+ type: Number,
32
+ default: 0,
33
+ required: true,
34
+ },
35
+ duration: {
36
+ type: Number,
37
+ default: 3,
38
+ validator(duration: number): boolean {
39
+ return duration > 0
40
+ },
41
+ },
42
+ autoinit: {
43
+ type: Boolean,
44
+ default: true,
45
+ },
46
+ prefix: {
47
+ type: String,
48
+ default: "",
49
+ },
50
+ suffix: {
51
+ type: String,
52
+ default: "",
53
+ },
54
+ separator: {
55
+ type: String,
56
+ default: ",",
57
+ },
58
+ decimalSeparator: {
59
+ type: String,
60
+ default: ".",
61
+ },
62
+ decimals: {
63
+ type: Number,
64
+ default: 0,
65
+ validator(decimals: number): boolean {
66
+ return decimals >= 0
67
+ },
68
+ },
69
+ })
70
+
71
+ // Reactive state
72
+ const state = reactive<Data>({
73
+ timestamp: 0,
74
+ startTimestamp: null,
75
+ currentAmount: props.startAmount,
76
+ currentStartAmount: props.startAmount,
77
+ currentDuration: props.duration * 1000,
78
+ paused: false,
79
+ remaining: props.duration * 1000,
80
+ animationFrame: 0,
81
+ })
82
+
83
+ // Computed properties
84
+ const isCountingUp = computed(() => props.endAmount > props.startAmount)
85
+
86
+ const formatedAmount = computed((): string => {
87
+ const regex = /(\d+)(\d{3})/
88
+ const numberString: string = state.currentAmount.toFixed(props.decimals)
89
+ const numberArray: string[] = numberString.split(".")
90
+ let numbers: string = numberArray[0]
91
+ const decimals: string =
92
+ numberArray.length > 1 ? props.decimalSeparator + numberArray[1] : ""
93
+ const isNumber = !isNaN(parseFloat(props.separator))
94
+
95
+ if (props.separator && !isNumber) {
96
+ while (regex.test(numbers)) {
97
+ numbers = numbers.replace(regex, "$1" + props.separator + "$2")
98
+ }
99
+ }
100
+
101
+ return numbers + decimals
102
+ })
103
+
104
+ const displayedAmount = computed(
105
+ () => `${props.prefix}${formatedAmount.value}${props.suffix}`
106
+ )
107
+
108
+ // Methods
109
+
110
+ const start = (): void => {
111
+ cancelAnimation()
112
+ state.currentStartAmount = props.startAmount
113
+ state.startTimestamp = null
114
+ state.currentDuration = props.duration * 1000
115
+ state.paused = false
116
+ state.animationFrame = window.requestAnimationFrame(counting)
117
+ }
118
+
119
+ const pause = (): void => {
120
+ if (state.paused) return
121
+ cancelAnimation()
122
+ state.paused = true
123
+ }
124
+
125
+ const resume = (): void => {
126
+ if (!state.paused) return
127
+ state.startTimestamp = null
128
+ state.currentDuration = +state.remaining
129
+ state.currentStartAmount = +state.currentAmount
130
+ state.animationFrame = window.requestAnimationFrame(counting)
131
+ state.paused = false
132
+ }
133
+
134
+ const reset = (): void => {
135
+ state.paused = false
136
+ state.startTimestamp = null
137
+ cancelAnimation()
138
+ state.currentAmount = props.startAmount
139
+ if (props.autoinit) start()
140
+ else state.paused = true
141
+ }
142
+
143
+ const counting = (timestamp: number): void => {
144
+ state.timestamp = timestamp
145
+ if (!state.startTimestamp) state.startTimestamp = timestamp
146
+ const progress: number = timestamp - state.startTimestamp
147
+ state.remaining = state.currentDuration - progress
148
+
149
+ if (!isCountingUp.value) {
150
+ state.currentAmount =
151
+ state.currentStartAmount -
152
+ (state.currentStartAmount - props.endAmount) *
153
+ (progress / state.currentDuration)
154
+ state.currentAmount =
155
+ state.currentAmount < props.endAmount
156
+ ? props.endAmount
157
+ : state.currentAmount
158
+ } else {
159
+ state.currentAmount =
160
+ state.currentStartAmount +
161
+ (props.endAmount - state.currentStartAmount) *
162
+ (progress / state.currentDuration)
163
+ state.currentAmount =
164
+ state.currentAmount > props.endAmount
165
+ ? props.endAmount
166
+ : state.currentAmount
167
+ }
168
+
169
+ if (progress < state.currentDuration) {
170
+ state.animationFrame = window.requestAnimationFrame(counting)
171
+ } else {
172
+ console.log("finished")
173
+ }
174
+ }
175
+
176
+ const cancelAnimation = (): void => {
177
+ if (state.animationFrame) window.cancelAnimationFrame(state.animationFrame)
178
+ }
179
+
180
+ // Watchers
181
+ watch(() => props.startAmount, reset)
182
+ watch(() => props.endAmount, reset)
183
+ watch(() => props.duration, reset)
184
+
185
+ // Lifecycle hooks
186
+ onMounted(() => {
187
+ state.currentAmount = props.startAmount
188
+ state.currentStartAmount = props.startAmount
189
+ state.currentDuration = props.duration * 1000
190
+ state.remaining = props.duration * 1000
191
+ if (props.autoinit) start()
192
+ else state.paused = true
193
+ })
194
+
195
+ onUnmounted(() => {
196
+ cancelAnimation()
197
+ })
198
+ </script>
@@ -0,0 +1,104 @@
1
+ <template>
2
+ <v-skeleton-loader
3
+ v-if="loading"
4
+ max-width="120px"
5
+ class="d-flex flex-row flex-md-column align-center align-md-end"
6
+ :type="
7
+ ['avatar', 'avatar', 'avatar', 'avatar', 'avatar', 'avatar'][
8
+ ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'].indexOf(name || 'md')
9
+ ]
10
+ "
11
+ />
12
+ <div
13
+ v-else
14
+ class="date-stamp d-flex flex-md-column text-md-right align-center align-md-end"
15
+ >
16
+ <span class="day"> {{ detailedDateStart.day }}</span>
17
+ <span class="month-year">
18
+ {{ detailedDateStart.month }}<br />
19
+ {{ detailedDateStart.year }}
20
+ </span>
21
+ <template v-if="showDateStop && detailedDateStop.day">
22
+ <span>&ndash;</span>
23
+ <template v-if="mdAndUp">
24
+ <span class="month-year"> {{ detailedDateStop.day }}</span>
25
+ <span class="month-year">
26
+ {{ detailedDateStop.month }}<br />
27
+ {{ detailedDateStop.year }}
28
+ </span>
29
+ </template>
30
+
31
+ <template v-if="smAndDown">
32
+ <span class="day"> {{ detailedDateStop.day }}</span>
33
+ <span class="month-year">
34
+ {{ detailedDateStop.month }}<br />
35
+ {{ detailedDateStop.year }}
36
+ </span>
37
+ </template>
38
+ </template>
39
+ </div>
40
+ </template>
41
+
42
+ <script setup>
43
+ import { useDisplay } from "vuetify"
44
+ import {
45
+ getDetailedFormatedDate,
46
+ formatDateValue,
47
+ } from "../../../composables/useUtils"
48
+ const { smAndDown, mdAndUp, name } = useDisplay()
49
+
50
+ const { locale } = useI18n()
51
+ const props = defineProps({
52
+ dateStart: {
53
+ type: String,
54
+ },
55
+ dateStop: {
56
+ type: String,
57
+ },
58
+ loading: {
59
+ type: Boolean,
60
+ default: false,
61
+ required: true,
62
+ },
63
+ })
64
+
65
+ const detailedDateStart = computed(() =>
66
+ getDetailedFormatedDate(props.dateStart, locale.value)
67
+ )
68
+
69
+ const detailedDateStop = computed(() =>
70
+ getDetailedFormatedDate(props.dateStop, locale.value)
71
+ )
72
+
73
+ const showDateStop = computed(() => {
74
+ const dateStartFormatted = formatDateValue(props.dateStart, locale.value)
75
+ const dateStopFormatted = formatDateValue(props.dateStop, locale.value)
76
+ return dateStopFormatted > dateStartFormatted
77
+ })
78
+ </script>
79
+ <style lang="scss" scoped>
80
+ .date-stamp {
81
+ font-family: "Open sans";
82
+ .day {
83
+ font-size: 2.6rem;
84
+ line-height: 2.6rem;
85
+ font-family: "Bodoni Moda", sans-serif;
86
+ }
87
+ .month-year {
88
+ padding-left: 0.5rem;
89
+ font-size: 1rem;
90
+ line-height: 1.2rem;
91
+ }
92
+
93
+ .day-stop {
94
+ font-size: 2rem;
95
+ line-height: 2rem;
96
+ font-family: "Bodoni Moda", sans-serif;
97
+ }
98
+ .month-year-stop {
99
+ padding-left: 0.5rem;
100
+ font-size: 0.8rem;
101
+ line-height: 1.2rem;
102
+ }
103
+ }
104
+ </style>
@@ -0,0 +1,105 @@
1
+ <template>
2
+ <div>
3
+ <!-- TODO
4
+ - add skeleton UI
5
+ - test lazy-src
6
+ - validate requested quality
7
+ - Add conditional overlays slots (top left/right, bottom left/right/center for date, caption, copyright)-->
8
+
9
+ <v-skeleton-loader v-if="loading" height="100%" type="image" />
10
+
11
+ <template v-else>
12
+ <nuxt-link
13
+ :to="
14
+ link
15
+ ? localePath({
16
+ name: link,
17
+ params: { slug: slugify(slug) },
18
+ })
19
+ : null
20
+ "
21
+ >
22
+ <div class="overflow-hidden mw-100">
23
+ <!-- TODO debug why the picture is not displaying/sizing properly -->
24
+ <v-img
25
+ v-if="src"
26
+ :aspect-ratio="ratio"
27
+ :class="{ 'img-animation': animate }"
28
+ :lazy-src="
29
+ img(computedSrc, {
30
+ width,
31
+ quality: 70,
32
+ })
33
+ "
34
+ :src="
35
+ img(computedSrc, {
36
+ width,
37
+ quality: 70,
38
+ })
39
+ "
40
+ :srcset="_srcset.srcset"
41
+ :sizes="_srcset.sizes"
42
+ :title="caption"
43
+ v-bind="$attrs"
44
+ >
45
+ <slot />
46
+ </v-img></div
47
+ ></nuxt-link>
48
+ </template>
49
+ </div>
50
+ </template>
51
+
52
+ <script setup>
53
+ const localePath = useLocalePath()
54
+ const img = useImage()
55
+
56
+ const computedSrc = computed(() => {
57
+ return typeof props.src === "string" ? props.src : props.src.url
58
+ })
59
+ const props = defineProps({
60
+ src: {
61
+ type: [Object, String],
62
+ required: false,
63
+ default: {
64
+ alt: "default",
65
+ copyright: "IEA PARIS",
66
+ url: "/logo_grey.png",
67
+ },
68
+ },
69
+ loading: {
70
+ type: Boolean,
71
+ default: false,
72
+ required: true,
73
+ },
74
+ width: { type: Number, default: 0 },
75
+ ratio: { type: Number, required: true, default: 1 },
76
+ caption: { type: String, default: "" },
77
+ slug: { type: String, default: "" },
78
+ link: { type: String, default: "" },
79
+ animate: { type: Boolean, default: true },
80
+ })
81
+ const _srcset = computed(() => {
82
+ return img.getSizes(
83
+ typeof props.src === "string" ? props.src : props.src.url,
84
+ {
85
+ sizes: "xs:100vw sm:100vw md:100vw lg:100vw xl:100vw",
86
+ modifiers: {
87
+ format: "webp",
88
+ quality: 70,
89
+ ...(props.width && { width: props.width }),
90
+ },
91
+ }
92
+ )
93
+ })
94
+ </script>
95
+
96
+ <style scoped>
97
+ .img-animation {
98
+ transition: all 0.5s ease-in-out;
99
+ width: 100%;
100
+ }
101
+
102
+ .img-animation:hover {
103
+ transform: scale(1.1);
104
+ }
105
+ </style>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <v-menu>
3
+ <template #activator="{ props: menu }">
4
+ <v-tooltip location="top">
5
+ <template #activator="{ props: tooltip }">
6
+ <v-btn variant="outlined" v-bind="mergeProps(menu, tooltip)">
7
+ <v-icon>mdi-share-variant</v-icon>
8
+ </v-btn>
9
+ </template>
10
+ <div v-html="$t('share')" />
11
+ </v-tooltip>
12
+ </template>
13
+ <v-list>
14
+ <v-list-item
15
+ v-for="(network, index) in networks"
16
+ :key="network"
17
+ :value="network"
18
+ >
19
+ <SocialShare
20
+ :key="network"
21
+ class="text-black"
22
+ :network="network"
23
+ :title="$t('share-this-on', { item: item.name })"
24
+ :aria-label="$t('share-on', { network: network })"
25
+ :image="item.image"
26
+ :hashtags="item.tags.map((tag) => tag.name)"
27
+ :url="config.url + route.fullPath"
28
+ ><template #label>{{ network }}</template>
29
+ </SocialShare>
30
+ </v-list-item>
31
+ </v-list>
32
+ </v-menu>
33
+ </template>
34
+ <script setup>
35
+ import config from "~/static.config"
36
+ import { mergeProps } from "vue"
37
+ const route = useRoute()
38
+
39
+ const props = defineProps({
40
+ networks: {
41
+ type: Array,
42
+ default: [
43
+ "facebook",
44
+ "twitter",
45
+ "linkedin",
46
+ "email",
47
+ "pinterest",
48
+ "reddit",
49
+ "whatsapp",
50
+ "pocket",
51
+ ],
52
+ },
53
+ item: {
54
+ type: Object,
55
+ required: true,
56
+ },
57
+ })
58
+
59
+ /* console.log("PRPOS", props.item) */
60
+ </script>
@@ -0,0 +1,127 @@
1
+ <template>
2
+ <v-row no-gutters>
3
+ <v-col>
4
+ <template v-for="(value, key, index) in socials" :key="key + value">
5
+ <v-tooltip :location="location" v-if="value">
6
+ <template v-slot:activator="{ props }">
7
+ <v-btn
8
+ tile
9
+ v-bind="mergeProps(props, attrs)"
10
+ target="_blank"
11
+ :href="getSocialId(key, value)"
12
+ :color="dark ? 'black' : 'white'"
13
+ flat
14
+ >
15
+ <v-icon :color="dark ? 'white' : 'black'">
16
+ {{ getProfileIcon(key) }}</v-icon
17
+ >
18
+ <div v-if="labelled" class="text-uppercase text-muted ml-3">
19
+ {{ key }}
20
+ </div>
21
+ </v-btn>
22
+ </template>
23
+ <span>{{ $t("socials." + key) }}</span>
24
+ </v-tooltip>
25
+ <br v-if="labelled" />
26
+ </template>
27
+ </v-col>
28
+ </v-row>
29
+ </template>
30
+
31
+ <script setup>
32
+ import { useAttrs } from "vue"
33
+ import { mergeProps } from "vue"
34
+
35
+ const attrs = useAttrs()
36
+ import { useDisplay } from "vuetify"
37
+ const { name } = useDisplay()
38
+
39
+ const props = defineProps({
40
+ socials: {
41
+ type: Object,
42
+ required: true,
43
+ },
44
+ location: {
45
+ type: String,
46
+ default: "bottom",
47
+ },
48
+ dark: {
49
+ type: Boolean,
50
+ default: false,
51
+ },
52
+ labelled: {
53
+ type: Boolean,
54
+ default: false,
55
+ },
56
+ })
57
+
58
+ const getSocialId = (name, id) => {
59
+ switch (name) {
60
+ case "idRef":
61
+ return `https://www.idref.fr/${id}`
62
+ case "mendeley":
63
+ return `https://www.mendeley.com/${id}`
64
+ case "linkedin":
65
+ return `https://www.linkedin.com/company/${id}`
66
+ case "twitter":
67
+ return `https://x.com/${id}`
68
+ case "orcid":
69
+ return `https://orcid.org/${id}`
70
+ case "scholar":
71
+ return `https://scholar.google.com/${id}`
72
+ case "wikipedia":
73
+ return `https://fr.wikipedia.org/${id}`
74
+ case "researchgate":
75
+ return `https://www.researchgate.net/${id}`
76
+ case "youtube":
77
+ return `https://www.youtube.com/${id}`
78
+ case "facebook":
79
+ return `https://www.facebook.com/pages/${id}`
80
+ case "instagram":
81
+ return `https://www.instagram.com/${id}`
82
+ case "github":
83
+ return `https://gitbub.com/${id}`
84
+ case "rss":
85
+ return ""
86
+ case "website":
87
+ return ""
88
+ }
89
+ }
90
+
91
+ const getProfileIcon = (name) => {
92
+ switch (name) {
93
+ case "idRef":
94
+ return "mdi-account-plus-outline"
95
+ case "mendeley":
96
+ return "mdi-book"
97
+ case "bluesky":
98
+ return "mdi-cloud"
99
+ case "linkedin":
100
+ return "mdi-linkedin"
101
+ case "twitter":
102
+ return "mdi-twitter"
103
+ case "orcid":
104
+ return "mdi-account"
105
+ case "scholar":
106
+ return "mdi-google"
107
+ case "wikipedia":
108
+ return "mdi-wikipedia"
109
+ case "researchgate":
110
+ return "mdi-account"
111
+ case "youtube":
112
+ return "mdi-youtube"
113
+ case "facebook":
114
+ return "mdi-facebook"
115
+ case "instagram":
116
+ return "mdi-instagram"
117
+ case "github":
118
+ return "mdi-github"
119
+ case "rss":
120
+ return "mdi-rss"
121
+ case "website":
122
+ return "mdi-web"
123
+ }
124
+ }
125
+ </script>
126
+
127
+ <style lang="scss"></style>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <div class="d-flex flex-row flex-wrap" v-bind="attrs">
3
+ <div v-for="(tag, index) in items" :key="tag + index" class="">
4
+ <v-chip
5
+ :size="
6
+ ['small', 'small', 'small', 'default', 'large'][
7
+ ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'].indexOf(name || 'md')
8
+ ]
9
+ "
10
+ class="mr-2 mt-2"
11
+ variant="outlined"
12
+ label
13
+ >{{ tag }}</v-chip
14
+ >
15
+ </div>
16
+ </div>
17
+ </template>
18
+
19
+ <script setup>
20
+ import { useAttrs } from "vue"
21
+
22
+ import { useDisplay } from "vuetify"
23
+ const attrs = useAttrs()
24
+ const { name, md, xl, lg, smAndDown, mdAndUp } = useDisplay()
25
+
26
+ const props = defineProps({
27
+ items: {
28
+ type: Array,
29
+ required: true,
30
+ default: [],
31
+ },
32
+ })
33
+ </script>
34
+
35
+ <style lang="scss" scoped></style>
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <v-row>
3
+ <v-col
4
+ v-for="(items, type) in filteredRelatedItems"
5
+ :key="type"
6
+ cols="12"
7
+ md="6"
8
+ lg="4"
9
+ xl="3"
10
+ class="d-flex flex-column align-center"
11
+ >
12
+ <MiscMoleculesRelatedItems :type="type" :items="items" />
13
+ </v-col>
14
+ </v-row>
15
+ </template>
16
+
17
+ <script setup>
18
+ const props = defineProps({
19
+ related: {
20
+ type: Object,
21
+ required: true,
22
+ },
23
+ })
24
+
25
+ const typeMapping = {
26
+ events: "events",
27
+ people: "people",
28
+ news: "news",
29
+ projects: "projects",
30
+ }
31
+
32
+ const filteredRelatedItems = computed(() => {
33
+ const result = {}
34
+ for (const key in typeMapping) {
35
+ if (props.related[key] && props.related[key].length > 0) {
36
+ result[typeMapping[key]] = props.related[key]
37
+ }
38
+ }
39
+ return result
40
+ })
41
+ </script>