@paris-ias/list 1.2.2 → 1.3.2

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.2.2",
4
+ "version": "1.3.2",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
@@ -23,17 +23,20 @@
23
23
 
24
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
- <v-chip
27
- v-else
28
- class="mr-3"
29
- color="black"
30
- size="small"
31
- style="background-color: white; color: black"
32
- tile
33
- variant="outlined"
34
- >
35
- {{ $t("list.filters.events.category." + item.category) }}
36
- </v-chip>
26
+ <span v-else>
27
+ <v-chip
28
+ class="mr-3"
29
+ color="black"
30
+ size="small"
31
+ style="background-color: white; color: black"
32
+ tile
33
+ variant="outlined"
34
+ >
35
+ {{ $t("list.filters.events.category." + item.category) }}
36
+ </v-chip>
37
+ <!-- DISCIPLINES -->
38
+ <MiscMoleculesDisciplinesTags :disciplines="item.disciplines" inline />
39
+ </span>
37
40
  <v-skeleton-loader
38
41
  v-if="loading && smAndDown"
39
42
  type="text"
@@ -49,12 +49,15 @@
49
49
  'list.filters.fellowships.fellowshipType.' +
50
50
  item.fellowshipType,
51
51
  ),
52
- ...(props.item && props.item.disciplines
53
- ? props.item.disciplines.map((discipline) => discipline.name)
54
- : []),
55
52
  ]"
56
53
  class="mt-2"
57
54
  />
55
+ <!-- DISCIPLINES -->
56
+ <MiscMoleculesDisciplinesTags
57
+ :disciplines="item.disciplines"
58
+ justify="center"
59
+ class="mt-4"
60
+ />
58
61
  <div class="mt-5">
59
62
  <FellowshipsBadges :item="item" :view="view" :loading="loading" />
60
63
  </div>
@@ -152,58 +155,43 @@
152
155
 
153
156
  <script setup>
154
157
  import { useDisplay } from "vuetify"
155
- import { ref } from "#imports"
158
+ import { computed, ref } from "#imports"
156
159
  const { name } = useDisplay()
157
160
  const accordeon = ref(-1)
158
161
  const props = defineProps({
162
+ // null while the resource item is loading (see useI18nResourceItem)
159
163
  item: {
160
164
  type: Object,
161
- required: true,
165
+ default: null,
162
166
  },
163
167
  loading: {
164
168
  type: Boolean,
165
169
  default: false,
166
- required: true,
167
170
  },
168
171
  })
169
172
 
170
173
  const view = ref(true)
171
174
 
172
- const renderedDetails = {
173
- ...(props.item?.fellowshipDetails?.type && {
174
- type: props.item?.fellowshipDetails?.type,
175
- }),
176
- ...(props.item?.fellowshipDetails?.fundingPeriod && {
177
- fundingPeriod: props.item?.fellowshipDetails?.fundingPeriod,
178
- }),
179
- ...(props.item?.fellowshipDetails?.profile && {
180
- profile: props.item?.fellowshipDetails?.profile,
181
- }),
182
- ...(props.item?.fellowshipDetails?.tasks && {
183
- tasks: props.item?.fellowshipDetails?.tasks,
184
- }),
185
- ...(props.item?.fellowshipDetails?.location && {
186
- location: props.item?.fellowshipDetails?.location,
187
- }),
188
- ...(props.item?.fellowshipDetails?.funding && {
189
- funding: props.item?.fellowshipDetails?.funding,
190
- }),
191
- ...(props.item?.fellowshipDetails?.housing && {
192
- housing: props.item?.fellowshipDetails?.housing,
193
- }),
194
- ...(props.item?.fellowshipDetails?.meals && {
195
- meals: props.item?.fellowshipDetails?.meals,
196
- }),
197
- ...(props.item?.fellowshipDetails?.applicationMaterials && {
198
- applicationMaterials: props.item?.fellowshipDetails?.applicationMaterials,
199
- }),
200
- ...(props.item?.fellowshipDetails?.selectionProcess && {
201
- selectionProcess: props.item?.fellowshipDetails?.selectionProcess,
202
- }),
203
- ...(props.item?.fellowshipDetails?.researchProcess && {
204
- researchProcess: props.item?.fellowshipDetails?.researchProcess,
205
- }),
206
- }
175
+ // computed (not a one-shot setup value) so it picks up `item` once it loads in
176
+ const renderedDetails = computed(() => {
177
+ const d = props.item?.fellowshipDetails
178
+ if (!d) return {}
179
+ return {
180
+ ...(d.type && { type: d.type }),
181
+ ...(d.fundingPeriod && { fundingPeriod: d.fundingPeriod }),
182
+ ...(d.profile && { profile: d.profile }),
183
+ ...(d.tasks && { tasks: d.tasks }),
184
+ ...(d.location && { location: d.location }),
185
+ ...(d.funding && { funding: d.funding }),
186
+ ...(d.housing && { housing: d.housing }),
187
+ ...(d.meals && { meals: d.meals }),
188
+ ...(d.applicationMaterials && {
189
+ applicationMaterials: d.applicationMaterials,
190
+ }),
191
+ ...(d.selectionProcess && { selectionProcess: d.selectionProcess }),
192
+ ...(d.researchProcess && { researchProcess: d.researchProcess }),
193
+ }
194
+ })
207
195
  </script>
208
196
 
209
197
  <style lang="scss" scoped></style>
@@ -2,7 +2,7 @@
2
2
  <v-row>
3
3
  <template v-for="(filterItem, index) in Object.keys($stores[type].filters)">
4
4
  <v-col
5
- v-if="computeVisibility(filterItem)"
5
+ v-if="appAllowed(filterItem) && computeVisibility(filterItem)"
6
6
  :key="type + index + filterItem"
7
7
  cols="12"
8
8
  sm="6"
@@ -44,7 +44,7 @@
44
44
  <script setup>
45
45
  import { useDisplay } from "vuetify"
46
46
  import { capitalize } from "../../../composables/useUtils"
47
- import { useNuxtApp, resolveComponent, useI18n } from "#imports"
47
+ import { useNuxtApp, resolveComponent, useI18n, useAppConfig } from "#imports"
48
48
 
49
49
  const { smAndDown } = useDisplay()
50
50
  const i18n = useI18n()
@@ -52,6 +52,18 @@ const { locale, messages } = useI18n()
52
52
  const { $stores, $filters } = useNuxtApp()
53
53
  const props = defineProps(["type", "expanded"])
54
54
 
55
+ // Consuming app id. Filters may declare an `appId` array of the consumers
56
+ // allowed to render them; this is the id we match against. "all" is a wildcard.
57
+ const appId = useAppConfig().list?.appId ?? "iea"
58
+
59
+ // A filter is shown only on the consumers it opts into. No appId -> shown
60
+ // everywhere (backward compatible). "all" in the list matches any consumer.
61
+ const appAllowed = (filterKey) => {
62
+ const allowed = $stores[props.type].filters[filterKey].appId
63
+ if (!Array.isArray(allowed) || allowed.length === 0) return true
64
+ return allowed.includes("all") || allowed.includes(appId)
65
+ }
66
+
55
67
  const ComponentName = (name) => {
56
68
  return resolveComponent(
57
69
  "ListInputs" + capitalize($stores[props.type].filters[name].type),
@@ -62,28 +74,44 @@ const getItems = (name) => {
62
74
  return []
63
75
  }
64
76
 
77
+ // Disciplines and thematics are shared enums used across most list types.
78
+ // They live in their own top-level i18n namespace (list.filters.disciplines
79
+ // / list.filters.thematics) and are resolved from there for every type,
80
+ // independently of filters.json, to avoid duplicating them per type.
81
+ if (["disciplines", "thematics"].includes(name)) {
82
+ return Object.keys(messages.value[locale.value].list.filters?.[name])
83
+ .filter((key) => key !== "label")
84
+ .map((item) => ({
85
+ title: i18n.t(`list.filters.${name}.${item}`),
86
+ value: item,
87
+ }))
88
+ .sort((a, b) => a.title.localeCompare(b.title))
89
+ }
90
+
91
+ // Prefer the enum declared on the model filter (filters[name].items) over the
92
+ // values baked into filters.json. The model is the source of truth for enum
93
+ // options (e.g. publicationType), so filters.json never has to stay in sync.
94
+ const modelItems = $stores[props.type].filters[name].items
95
+ if (modelItems && typeof modelItems === "object") {
96
+ return Object.values(modelItems)
97
+ .filter((item) => item !== "label")
98
+ .map((item) => ({
99
+ title: i18n.t(`list.filters.${props.type}.${name}.${item}`),
100
+ value: item,
101
+ }))
102
+ }
103
+
65
104
  if ($filters?.[props.type]?.[name]) {
66
- // Disciplines and thematics have their own namespace for translations
67
- if (["disciplines", "thematics"].includes(name)) {
68
- return Object.keys(messages.value[locale.value].list.filters?.[name])
69
- .filter((key) => key !== "label")
70
- .map((item) => ({
71
- title: i18n.t(`list.filters.${name}.${item}`),
72
- value: item,
73
- }))
74
- .sort((a, b) => a.title.localeCompare(b.title))
75
- } else {
76
- return $filters[props.type][name]
77
- .filter((key) => key !== "label")
78
- .map((item) => ({
79
- title: i18n.t(
80
- props.type === "people" && name === "vintage"
81
- ? item
82
- : `list.filters.${props.type}.${name}.${item}`,
83
- ),
84
- value: item,
85
- }))
86
- }
105
+ return $filters[props.type][name]
106
+ .filter((key) => key !== "label")
107
+ .map((item) => ({
108
+ title: i18n.t(
109
+ props.type === "people" && name === "vintage"
110
+ ? item
111
+ : `list.filters.${props.type}.${name}.${item}`,
112
+ ),
113
+ value: item,
114
+ }))
87
115
  }
88
116
 
89
117
  if (!messages.value[locale.value].list.filters[props.type]?.[name]) {
@@ -137,6 +165,16 @@ const computeVisibility = (filterKey) => {
137
165
 
138
166
  const checkRule = (rule) =>
139
167
  Object.entries(rule).every(([depKey, expected]) => {
168
+ // Reserved key: match against the active view modifier rather than a
169
+ // sibling filter's value. On routes like /resources/media the `type` is
170
+ // narrowed server-side via the modifier, so the modifier is what changes.
171
+ if (depKey === "modifier") {
172
+ const cur = $stores[props.type].modifier
173
+ return Array.isArray(expected)
174
+ ? expected.includes(cur)
175
+ : cur === expected
176
+ }
177
+
140
178
  const dep = filters?.[depKey]
141
179
  if (!dep) return false
142
180
  const cur = dep.value
@@ -117,6 +117,10 @@ const props = defineProps({
117
117
  /* console.log("start llocal loading from setup") */
118
118
  rootStore.setLoading(true, props.type)
119
119
 
120
+ // Mirror the route's fixed modifier into the store so the filter UI can branch
121
+ // its conditional visibility on it (the query still reads props.modifier directly).
122
+ rootStore.setModifier(props.type, props.modifier)
123
+
120
124
  // Initial route -> store (single source-of-truth on first load)
121
125
  rootStore.loadRouteQuery(props.type)
122
126
 
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <v-skeleton-loader
3
+ v-if="loading"
4
+ type="chip@3"
5
+ class="discipline-skeleton mt-2"
6
+ />
7
+ <div
8
+ v-else-if="disciplines.length"
9
+ class="flex-wrap"
10
+ :class="[
11
+ inline ? 'd-inline-flex align-center' : 'd-flex',
12
+ justify === 'center' ? 'justify-center' : '',
13
+ ]"
14
+ style="gap: 6px"
15
+ >
16
+ <v-chip
17
+ v-for="(d, i) in shownDisciplines"
18
+ :key="d + i"
19
+ size="small"
20
+ tile
21
+ variant="flat"
22
+ color="grey-lighten-3"
23
+ class="discipline-chip"
24
+ >
25
+ {{ disciplineLabel(d) }}
26
+ </v-chip>
27
+ <v-chip
28
+ v-if="extraDisciplines"
29
+ size="small"
30
+ variant="flat"
31
+ tile
32
+ color="grey-lighten-3"
33
+ class="discipline-chip"
34
+ >
35
+ +{{ extraDisciplines }}
36
+ </v-chip>
37
+ </div>
38
+ </template>
39
+
40
+ <script setup>
41
+ import { computed, useI18n } from "#imports"
42
+
43
+ const { t } = useI18n()
44
+
45
+ const props = defineProps({
46
+ // scalar enum array (e.g. item.disciplines)
47
+ disciplines: {
48
+ type: Array,
49
+ default: () => [],
50
+ },
51
+ loading: {
52
+ type: Boolean,
53
+ default: false,
54
+ },
55
+ // cap the number of chips; 0 = show all (used by detail views)
56
+ max: {
57
+ type: Number,
58
+ default: 0,
59
+ },
60
+ // 'start' (dense list) | 'center' (detail views)
61
+ justify: {
62
+ type: String,
63
+ default: "start",
64
+ },
65
+ // render as inline-flex so the chips flow on the same line as preceding
66
+ // content (e.g. the category chip in the events dense item) instead of
67
+ // forcing a block-level line break
68
+ inline: {
69
+ type: Boolean,
70
+ default: false,
71
+ },
72
+ })
73
+
74
+ // normalise to an array regardless of what the caller passes
75
+ const disciplines = computed(() =>
76
+ Array.isArray(props.disciplines) ? props.disciplines : [],
77
+ )
78
+ const shownDisciplines = computed(() =>
79
+ props.max > 0 ? disciplines.value.slice(0, props.max) : disciplines.value,
80
+ )
81
+ const extraDisciplines = computed(() =>
82
+ props.max > 0 ? Math.max(0, disciplines.value.length - props.max) : 0,
83
+ )
84
+ const disciplineLabel = (d) => t("list.filters.disciplines." + d)
85
+ </script>
86
+
87
+ <style scoped>
88
+ .discipline-chip{border-radius:6px;color:rgba(0,0,0,.78);font-weight:500}.discipline-skeleton :deep(.v-skeleton-loader__chip){margin:0 6px 0 0}
89
+ </style>
@@ -41,6 +41,12 @@
41
41
  {{ $t(eventCategory) }}
42
42
  </v-chip>
43
43
  <MiscMoleculesChipContainer :items="item.tags || []" size="small" />
44
+ <MiscMoleculesDisciplinesTags
45
+ v-if="item.disciplines && item.disciplines.length"
46
+ :disciplines="item.disciplines"
47
+ inline
48
+ class="mt-2"
49
+ />
44
50
  </template>
45
51
  <div
46
52
  v-html="
@@ -73,37 +73,35 @@
73
73
  <div class="mt-6 align-self-center">
74
74
  <PeopleBadges v-if="item && item.groups" :item="item" />
75
75
  </div>
76
-
76
+ <!-- DIVIDERS -->
77
+ <v-responsive class="mx-auto my-6" width="120">
78
+ <v-divider class="mb-1" />
79
+ <v-divider />
80
+ </v-responsive>
77
81
  <!-- FELLOWSHIP -->
78
- <div v-if="fellowName || fellowTheme || fellowDates" class="mt-6">
79
- <div v-if="fellowName" class="text-h6 font-weight-regular">
80
- {{ fellowName }}
81
- </div>
82
-
83
- <!-- DISCIPLINES -->
82
+ <div
83
+ v-if="fellowshipProjectName || fellowTheme || fellowDates"
84
+ class=""
85
+ >
84
86
  <div
85
- v-if="disciplines.length"
86
- class="mt-4 d-flex flex-wrap justify-center"
87
- style="gap: 6px"
87
+ v-if="fellowshipProjectName"
88
+ class="text-h6 font-weight-regular"
88
89
  >
89
- <v-chip
90
- v-for="(d, i) in disciplines"
91
- :key="d + i"
92
- size="small"
93
- variant="flat"
94
- tile
95
- color="grey-lighten-3"
96
- class="discipline-chip"
97
- >
98
- {{ disciplineLabel(d) }}
99
- </v-chip>
90
+ {{ fellowshipProjectName }}
100
91
  </div>
101
- <div v-if="fellowTheme" class="text-body-1 mt-1 font-italic">
92
+
93
+ <!-- DISCIPLINES -->
94
+ <MiscMoleculesDisciplinesTags
95
+ :disciplines="item.disciplines"
96
+ justify="center"
97
+ class="mt-4"
98
+ />
99
+ <div v-if="fellowTheme" class="text-body-1 my-3 font-italic">
102
100
  {{ fellowTheme }}
103
101
  </div>
104
102
  <div
105
103
  v-if="fellowDates"
106
- class="text-body-2 mt-1 text-medium-emphasis"
104
+ class="text-body-2 my-3 text-medium-emphasis"
107
105
  >
108
106
  {{ fellowDates }}
109
107
  </div>
@@ -217,22 +215,17 @@ const { t, locale } = useI18n()
217
215
  const { $stores } = useNuxtApp()
218
216
  const { name, mdAndUp } = useDisplay()
219
217
  const props = defineProps({
220
- item: { type: Object, required: true },
218
+ // null while the resource item is loading (see useI18nResourceItem)
219
+ item: { type: Object, default: null },
221
220
  loading: { type: Boolean, default: false },
222
221
  })
223
222
  $stores.people.loading = false
224
223
 
225
- // disciplines: scalar enum array → translated labels
226
- const disciplines = computed(() =>
227
- Array.isArray(props.item?.disciplines) ? props.item.disciplines : [],
228
- )
229
- const disciplineLabel = (d) => t("list.filters.disciplines." + d)
230
-
231
224
  // latest is a union Vintage | Position; the fellowship program lives on the
232
225
  // Vintage branch (name = program name, theme = fellowship theme) and carries the
233
226
  // arrival/departure dates (start/stop). Guard by field presence.
234
227
  const latest = computed(() => props.item?.latest ?? null)
235
- const fellowName = computed(() => latest.value?.name ?? null)
228
+ const fellowshipProjectName = computed(() => latest.value?.name ?? null)
236
229
  const fellowTheme = computed(() => latest.value?.theme ?? null)
237
230
 
238
231
  // "present" = currently in residence/post: started on or before today and not
@@ -259,5 +252,5 @@ const fellowDates = computed(() => {
259
252
  </script>
260
253
 
261
254
  <style scoped>
262
- .discipline-chip{border-radius:6px;color:rgba(0,0,0,.78);font-weight:500}.present-pill{align-items:center;background-color:rgba(0,0,0,.06);border-radius:999px;color:rgba(0,0,0,.7);display:inline-flex;font-size:.6875rem;font-weight:500;gap:5px;letter-spacing:.04em;line-height:1;padding:4px 10px;text-transform:uppercase}.present-dot{background-color:#4caf50;border-radius:50%;height:6px;width:6px}
255
+ .present-pill{align-items:center;background-color:rgba(0,0,0,.06);border-radius:999px;color:rgba(0,0,0,.7);display:inline-flex;font-size:.6875rem;font-weight:500;gap:5px;letter-spacing:.04em;line-height:1;padding:4px 10px;text-transform:uppercase}.present-dot{background-color:#4caf50;border-radius:50%;height:6px;width:6px}
263
256
  </style>
@@ -45,6 +45,13 @@
45
45
  >
46
46
  <MDC v-if="item && item.subtitle" :value="item.subtitle" />
47
47
  </div>
48
+ <!-- DISCIPLINES -->
49
+ <MiscMoleculesDisciplinesTags
50
+ v-if="item"
51
+ :disciplines="item.disciplines"
52
+ justify="center"
53
+ class="mt-4"
54
+ />
48
55
  <MiscMoleculesChipContainer
49
56
  v-if="item && item.tags"
50
57
  :items="item.tags"
@@ -120,13 +127,14 @@ import { useNuxtApp } from "#imports"
120
127
  const { $stores } = useNuxtApp()
121
128
  const { name } = useDisplay()
122
129
  const props = defineProps({
130
+ // null while the resource item is loading (see useI18nResourceItem)
123
131
  item: {
124
132
  type: Object,
125
- required: true,
133
+ default: null,
126
134
  },
127
135
  loading: {
128
136
  type: Boolean,
129
- required: false,
137
+ default: false,
130
138
  },
131
139
  })
132
140
  </script>
@@ -46,11 +46,17 @@
46
46
  :items="item.tags || []"
47
47
  size="small"
48
48
  />
49
+ <MiscMoleculesDisciplinesTags
50
+ v-if="item.disciplines && item.disciplines.length"
51
+ :disciplines="item.disciplines"
52
+ inline
53
+ class="mt-2"
54
+ />
49
55
  </template>
50
56
  <v-skeleton-loader v-if="loading" type="heading" />
51
- <span
57
+ <div
52
58
  v-else
53
- class="text-h5 dense paragraph"
59
+ class="text-h5 dense paragraph mt-2"
54
60
  v-html="
55
61
  searchQuery.length
56
62
  ? highlightAndTruncate(300, item.name, searchQuery.split(' '))
@@ -40,6 +40,12 @@
40
40
  <div class="overline my-2">
41
41
  {{ formatDateValue(item.date, locale) }}
42
42
  </div>
43
+ <!-- DISCIPLINES -->
44
+ <MiscMoleculesDisciplinesTags
45
+ :disciplines="item.disciplines"
46
+ justify="center"
47
+ class="mt-4"
48
+ />
43
49
  <MiscMoleculesChipContainer
44
50
  v-if="item.tags && item.tags.length"
45
51
  :items="item.tags"
@@ -119,13 +125,13 @@ const { $stores } = useNuxtApp()
119
125
  const { name } = useDisplay()
120
126
  const { locale } = useI18n()
121
127
  const props = defineProps({
128
+ // null while the resource item is loading (see useI18nResourceItem)
122
129
  item: {
123
130
  type: Object,
124
- required: true,
131
+ default: null,
125
132
  },
126
133
  loading: {
127
134
  type: Boolean,
128
- required: false,
129
135
  default: false,
130
136
  },
131
137
  })
@@ -37,6 +37,7 @@ export declare const useRootStore: import("pinia").StoreDefinition<"rootStore",
37
37
  loadRouteQuery(type: string): void;
38
38
  setFiltersCount(type: string): void;
39
39
  updateRouteQuery(type: string): void;
40
+ setModifier(type: string, modifier?: string): void;
40
41
  resetState(type: string, lang?: string): void;
41
42
  updateSort({ type, sortKey }: {
42
43
  type: string;
@@ -113,12 +113,19 @@ export const useRootStore = defineStore("rootStore", {
113
113
  };
114
114
  router.replace({ query: routeQuery });
115
115
  },
116
+ setModifier(type, modifier) {
117
+ const { $stores } = useNuxtApp();
118
+ if ($stores[type]) {
119
+ $stores[type].modifier = modifier;
120
+ }
121
+ },
116
122
  resetState(type, lang = "en") {
117
123
  const { $stores, $models } = useNuxtApp();
118
124
  const model = structuredClone($models[type]);
119
125
  $stores[type].filters = model?.filters;
120
126
  $stores[type].search = "";
121
127
  $stores[type].page = 1;
128
+ $stores[type].modifier = void 0;
122
129
  $stores[type].sortKey = null;
123
130
  },
124
131
  updateSort({ type, sortKey }) {
@@ -234,7 +234,7 @@
234
234
  "type": {
235
235
  "label": "Type",
236
236
  "INITIATIVE": "Initiative",
237
- "PROJECTS": "Project",
237
+ "PROJECT": "Project",
238
238
  "TOOL": "Tool"
239
239
  }
240
240
  },
@@ -224,7 +224,7 @@
224
224
  "type": {
225
225
  "label": "Type",
226
226
  "INITIATIVE": "Initiative",
227
- "PROJECTS": "Projet",
227
+ "PROJECT": "Projet",
228
228
  "TOOL": "Outil"
229
229
  }
230
230
  },
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.2.2",
4
+ "version": "1.3.2",
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.2.17"
11
+ "@paris-ias/trees": "^2.2.18"
12
12
  },
13
13
  "description": "Paris IAS List Module",
14
14
  "peerDependencies": {