@datagouv/components-next 1.0.2-dev.8 → 1.0.2-dev.80
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/assets/main.css +4 -0
- package/dist/Datafair.client-BzW-ctDf.js +30 -0
- package/dist/JsonPreview.client-BfMSzR07.js +40 -0
- package/dist/{MapContainer.client-DRkAmdOc.js → MapContainer.client-CLs-im9i.js} +35 -38
- package/dist/{PdfPreview.client-C-w6-w44.js → PdfPreview.client-C13PQCU_.js} +822 -865
- package/dist/{Pmtiles.client-BR7_ldHY.js → Pmtiles.client-CL7PXXDl.js} +574 -579
- package/dist/PreviewWrapper.vue_vue_type_script_setup_true_lang-C6XnsZ-7.js +61 -0
- package/dist/XmlPreview.client-KaENrbbG.js +34 -0
- package/dist/components-next.css +3 -3
- package/dist/components-next.js +166 -148
- package/dist/components.css +1 -1
- package/dist/{index-SrYZwgCT.js → index-C7WVVGgD.js} +1 -1
- package/dist/{main-B2kXxWRG.js → main-K-42Oe8-.js} +91315 -75834
- package/dist/{vue3-xml-viewer.common-BRxsqI9j.js → vue3-xml-viewer.common-sHPSE-jD.js} +1 -1
- package/package.json +17 -10
- package/src/components/ActivityList/ActivityList.vue +0 -2
- package/src/components/Chart/ChartViewer.vue +226 -0
- package/src/components/Chart/ChartViewerWrapper.vue +170 -0
- package/src/components/Form/Listbox.vue +101 -0
- package/src/components/Form/SearchableSelect.vue +2 -1
- package/src/components/InfiniteLoader.vue +53 -0
- package/src/components/OpenApiViewer/ContentTypeSelect.vue +48 -0
- package/src/components/OpenApiViewer/EndpointRequest.vue +164 -0
- package/src/components/OpenApiViewer/EndpointResponses.vue +149 -0
- package/src/components/OpenApiViewer/OpenApiViewer.vue +308 -0
- package/src/components/OpenApiViewer/SchemaPanel.vue +53 -0
- package/src/components/OpenApiViewer/SchemaTree.vue +77 -0
- package/src/components/OpenApiViewer/openapi.ts +150 -0
- package/src/components/OrganizationNameWithCertificate.vue +3 -2
- package/src/components/Pagination.vue +8 -5
- package/src/components/ReadMore.vue +1 -1
- package/src/components/ResourceAccordion/Datafair.client.vue +4 -10
- package/src/components/ResourceAccordion/JsonPreview.client.vue +23 -121
- package/src/components/ResourceAccordion/MapContainer.client.vue +7 -11
- package/src/components/ResourceAccordion/Metadata.vue +1 -2
- package/src/components/ResourceAccordion/PdfPreview.client.vue +24 -103
- package/src/components/ResourceAccordion/Pmtiles.client.vue +5 -10
- package/src/components/ResourceAccordion/Preview.vue +16 -21
- package/src/components/ResourceAccordion/PreviewLoader.vue +1 -2
- package/src/components/ResourceAccordion/PreviewUnavailable.vue +22 -0
- package/src/components/ResourceAccordion/PreviewWrapper.vue +82 -0
- package/src/components/ResourceAccordion/ResourceAccordion.vue +5 -7
- package/src/components/ResourceAccordion/XmlPreview.client.vue +16 -115
- package/src/components/ResourceExplorer/ResourceExplorer.vue +81 -13
- package/src/components/ResourceExplorer/ResourceExplorerSidebar.vue +2 -2
- package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +30 -11
- package/src/components/Search/GlobalSearch.vue +173 -108
- package/src/components/Search/SearchInput.vue +3 -3
- package/src/components/TabularExplorer/TabularCell.vue +51 -0
- package/src/components/TabularExplorer/TabularCellPopover.vue +170 -0
- package/src/components/TabularExplorer/TabularExplorer.vue +870 -0
- package/src/components/TabularExplorer/TabularFilterContent.vue +351 -0
- package/src/components/TabularExplorer/TabularFilterPopover.vue +111 -0
- package/src/components/TabularExplorer/types.ts +83 -0
- package/src/composables/useHasTabularData.ts +6 -0
- package/src/composables/useResourceCapabilities.ts +1 -1
- package/src/composables/useSearchFilter.ts +118 -0
- package/src/composables/useStableQueryParams.ts +31 -3
- package/src/config.ts +3 -0
- package/src/functions/api.ts +34 -33
- package/src/functions/api.types.ts +1 -0
- package/src/functions/charts.ts +68 -0
- package/src/functions/datasets.ts +0 -17
- package/src/functions/resources.ts +56 -1
- package/src/functions/tabular.ts +60 -0
- package/src/functions/tabularApi.ts +138 -11
- package/src/main.ts +55 -7
- package/src/types/dataservices.ts +2 -0
- package/src/types/pages.ts +0 -5
- package/src/types/posts.ts +2 -2
- package/src/types/reports.ts +5 -1
- package/src/types/search.ts +52 -1
- package/src/types/site.ts +5 -3
- package/src/types/users.ts +2 -1
- package/src/types/visualizations.ts +89 -0
- package/assets/swagger-themes/newspaper.css +0 -1670
- package/dist/Datafair.client-E5D6ePRC.js +0 -35
- package/dist/JsonPreview.client-C-6eBbPw.js +0 -87
- package/dist/Swagger.client-D4-F6yEf.js +0 -4
- package/dist/XmlPreview.client-Dl2VCgXF.js +0 -79
- package/src/components/ResourceAccordion/Swagger.client.vue +0 -48
- package/src/functions/pagination.ts +0 -9
- /package/assets/illustrations/{_microscope.svg → microscope.svg} +0 -0
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
<div v-if="allResources.length || hasAnyResources">
|
|
3
3
|
<div class="flex gap-6">
|
|
4
4
|
<div class="flex-1 min-w-0">
|
|
5
|
+
<div
|
|
6
|
+
v-if="dataset.resources.total > 1"
|
|
7
|
+
class="md:hidden flex justify-end mb-3"
|
|
8
|
+
>
|
|
9
|
+
<BrandedButton
|
|
10
|
+
size="xs"
|
|
11
|
+
color="secondary"
|
|
12
|
+
:icon="RiListUnordered"
|
|
13
|
+
@click="mobileSidebarOpen = true"
|
|
14
|
+
>
|
|
15
|
+
{{ t('Ressources ({count})', { count: dataset.resources.total }) }}
|
|
16
|
+
</BrandedButton>
|
|
17
|
+
</div>
|
|
5
18
|
<ResourceExplorerViewer
|
|
6
19
|
v-if="selectedResource && allResources.length"
|
|
7
20
|
:key="selectedResource.id"
|
|
@@ -30,17 +43,38 @@
|
|
|
30
43
|
</BrandedButton>
|
|
31
44
|
</div>
|
|
32
45
|
</div>
|
|
46
|
+
<div class="hidden md:block">
|
|
47
|
+
<ResourceExplorerSidebar
|
|
48
|
+
:resources="allResources"
|
|
49
|
+
:selected-resource-id="selectedResource?.id ?? null"
|
|
50
|
+
:collapsed="sidebarCollapsed"
|
|
51
|
+
:search
|
|
52
|
+
@select="selectResource"
|
|
53
|
+
@load-more="loadMore"
|
|
54
|
+
@update:collapsed="sidebarCollapsed = $event"
|
|
55
|
+
@update:search="updateSearch($event)"
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<!-- Mobile sidebar panel -->
|
|
61
|
+
<dialog
|
|
62
|
+
ref="mobileSidebarDialog"
|
|
63
|
+
class="mobile-sidebar md:hidden fixed inset-0 m-0 ml-auto p-0 h-dvh max-h-dvh w-80 max-w-[85vw] bg-white shadow-lg overflow-y-auto overscroll-contain backdrop:bg-black/30"
|
|
64
|
+
@close="mobileSidebarOpen = false"
|
|
65
|
+
@click.self="closeMobileSidebar"
|
|
66
|
+
>
|
|
33
67
|
<ResourceExplorerSidebar
|
|
34
68
|
:resources="allResources"
|
|
35
69
|
:selected-resource-id="selectedResource?.id ?? null"
|
|
36
|
-
:collapsed="
|
|
70
|
+
:collapsed="false"
|
|
37
71
|
:search
|
|
38
72
|
@select="selectResource"
|
|
39
73
|
@load-more="loadMore"
|
|
40
|
-
@update:collapsed="
|
|
74
|
+
@update:collapsed="closeMobileSidebar"
|
|
41
75
|
@update:search="updateSearch($event)"
|
|
42
76
|
/>
|
|
43
|
-
</
|
|
77
|
+
</dialog>
|
|
44
78
|
</div>
|
|
45
79
|
<div
|
|
46
80
|
v-else
|
|
@@ -73,6 +107,7 @@ import type { DatasetV2 } from '../../types/datasets'
|
|
|
73
107
|
import type { Resource, ResourceGroup, ResourceType } from '../../types/resources'
|
|
74
108
|
import ResourceExplorerSidebar from './ResourceExplorerSidebar.vue'
|
|
75
109
|
import ResourceExplorerViewer from './ResourceExplorerViewer.vue'
|
|
110
|
+
import { RiListUnordered } from '@remixicon/vue'
|
|
76
111
|
import BrandedButton from '../BrandedButton.vue'
|
|
77
112
|
|
|
78
113
|
const props = withDefaults(defineProps<{
|
|
@@ -159,6 +194,22 @@ watch(searchDebounced, () => {
|
|
|
159
194
|
}
|
|
160
195
|
})
|
|
161
196
|
|
|
197
|
+
// Separate useFetch for loadMore, initialized at setup time with immediate: false
|
|
198
|
+
// so that it doesn't fetch until execute() is called from the event handler.
|
|
199
|
+
const loadMoreType = ref<ResourceType>('main')
|
|
200
|
+
const loadMorePage = ref(1)
|
|
201
|
+
const loadMoreParams = computed(() => ({
|
|
202
|
+
type: loadMoreType.value,
|
|
203
|
+
page_size: PAGE_SIZE,
|
|
204
|
+
page: loadMorePage.value,
|
|
205
|
+
q: searchDebounced.value || undefined,
|
|
206
|
+
}))
|
|
207
|
+
const { data: loadMoreData, execute: executeLoadMore } = await useFetch<PaginatedArray<Resource>>(url, {
|
|
208
|
+
params: loadMoreParams,
|
|
209
|
+
immediate: false,
|
|
210
|
+
watch: false,
|
|
211
|
+
})
|
|
212
|
+
|
|
162
213
|
const loadMore = async (type: ResourceType) => {
|
|
163
214
|
const index = RESOURCE_TYPE.indexOf(type)
|
|
164
215
|
if (index === -1) return
|
|
@@ -166,17 +217,12 @@ const loadMore = async (type: ResourceType) => {
|
|
|
166
217
|
const extraRef = extraResourcesByType[index]!
|
|
167
218
|
pageRef.value++
|
|
168
219
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
page_size: PAGE_SIZE,
|
|
173
|
-
page: pageRef.value,
|
|
174
|
-
q: searchDebounced.value || undefined,
|
|
175
|
-
},
|
|
176
|
-
})
|
|
220
|
+
loadMoreType.value = type
|
|
221
|
+
loadMorePage.value = pageRef.value
|
|
222
|
+
await executeLoadMore()
|
|
177
223
|
|
|
178
|
-
if (
|
|
179
|
-
extraRef.value = [...extraRef.value, ...
|
|
224
|
+
if (loadMoreData.value) {
|
|
225
|
+
extraRef.value = [...extraRef.value, ...loadMoreData.value.data]
|
|
180
226
|
}
|
|
181
227
|
}
|
|
182
228
|
|
|
@@ -200,6 +246,21 @@ const flatResources = computed(() =>
|
|
|
200
246
|
|
|
201
247
|
// Fetch resource by ID if specified in URL (for SSR)
|
|
202
248
|
const initialResourceId = resourceIdQuery.value
|
|
249
|
+
const mobileSidebarOpen = ref(false)
|
|
250
|
+
const mobileSidebarDialog = ref<HTMLDialogElement | null>(null)
|
|
251
|
+
|
|
252
|
+
watch(mobileSidebarOpen, (open) => {
|
|
253
|
+
if (open) {
|
|
254
|
+
mobileSidebarDialog.value?.showModal()
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
mobileSidebarDialog.value?.close()
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
function closeMobileSidebar() {
|
|
262
|
+
mobileSidebarOpen.value = false
|
|
263
|
+
}
|
|
203
264
|
const { data: fetchedResource } = initialResourceId
|
|
204
265
|
? await useFetch<Resource>(`/api/1/datasets/${props.dataset.id}/resources/${initialResourceId}/`)
|
|
205
266
|
: { data: ref(null) }
|
|
@@ -227,6 +288,7 @@ function updateSearch(newSearch: string) {
|
|
|
227
288
|
|
|
228
289
|
const selectResource = (resource: Resource) => {
|
|
229
290
|
selectedResource.value = resource
|
|
291
|
+
mobileSidebarOpen.value = false
|
|
230
292
|
router.replace({
|
|
231
293
|
query: { ...router.currentRoute.value.query, resource_id: resource.id },
|
|
232
294
|
})
|
|
@@ -241,3 +303,9 @@ watch(flatResources, () => {
|
|
|
241
303
|
}
|
|
242
304
|
})
|
|
243
305
|
</script>
|
|
306
|
+
|
|
307
|
+
<style>
|
|
308
|
+
html:has(dialog.mobile-sidebar[open]) {
|
|
309
|
+
overflow: hidden;
|
|
310
|
+
}
|
|
311
|
+
</style>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<aside
|
|
3
3
|
v-if="!collapsed"
|
|
4
|
-
class="w-72 shrink-0
|
|
4
|
+
class="w-full md:w-72 shrink-0 p-4 md:pr-0"
|
|
5
5
|
>
|
|
6
6
|
<div class="flex items-center justify-between mb-3">
|
|
7
7
|
<h3 class="text-sm font-bold uppercase mb-0">
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
>
|
|
33
33
|
</div>
|
|
34
34
|
|
|
35
|
-
<div class="space-y-4 overflow-y-auto">
|
|
35
|
+
<div class="space-y-4 overflow-y-auto md:max-h-[calc(100vh-14rem)]">
|
|
36
36
|
<div
|
|
37
37
|
v-for="group in resources"
|
|
38
38
|
:key="group.type"
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<h3 class="m-0 flex items-baseline text-base font-bold leading-tight">
|
|
7
7
|
<ResourceIcon
|
|
8
8
|
:resource
|
|
9
|
-
class="size-3.5 mr-1"
|
|
9
|
+
class="size-3.5 mr-1 shrink-0 translate-y-px"
|
|
10
10
|
/>
|
|
11
11
|
<span class="line-clamp-2">{{ resource.title || t('Fichier sans nom') }}</span>
|
|
12
12
|
</h3>
|
|
@@ -14,9 +14,16 @@
|
|
|
14
14
|
:label="t('Copier le lien')"
|
|
15
15
|
:copied-label="t('Lien copié !')"
|
|
16
16
|
:text="resourceExternalUrl"
|
|
17
|
+
class="hidden md:inline-flex"
|
|
17
18
|
/>
|
|
18
19
|
</div>
|
|
19
|
-
<div class="text-gray-medium text-xs flex items-center gap-1">
|
|
20
|
+
<div class="text-gray-medium text-xs flex items-center gap-1 flex-wrap">
|
|
21
|
+
<SchemaBadge :resource />
|
|
22
|
+
<RiSubtractLine
|
|
23
|
+
v-if="resource.schema?.name || resource.schema?.url"
|
|
24
|
+
aria-hidden="true"
|
|
25
|
+
class="size-3 fill-gray-medium"
|
|
26
|
+
/>
|
|
20
27
|
<span>{{ t('mis à jour {date}', { date: formatRelativeIfRecentDate(resource.last_modified) }) }}</span>
|
|
21
28
|
<RiSubtractLine
|
|
22
29
|
aria-hidden="true"
|
|
@@ -128,14 +135,28 @@
|
|
|
128
135
|
:resource="resource"
|
|
129
136
|
:dataset="dataset"
|
|
130
137
|
/>
|
|
131
|
-
<
|
|
138
|
+
<OpenApiViewer
|
|
132
139
|
v-else-if="hasOpenAPIPreview"
|
|
133
140
|
:url="resource.extras['apidocUrl'] as string"
|
|
134
141
|
/>
|
|
135
142
|
<Preview
|
|
136
|
-
v-else
|
|
143
|
+
v-else-if="hasTabularData"
|
|
137
144
|
:resource="resource"
|
|
138
145
|
/>
|
|
146
|
+
<PreviewUnavailable v-else>
|
|
147
|
+
<!-- "File too large to download" is the only analysis:error value from hydra for now -->
|
|
148
|
+
<template v-if="resource.extras['analysis:error'] === 'File too large to download'">
|
|
149
|
+
{{ t("Ce fichier est trop volumineux pour être analysé et prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.") }}
|
|
150
|
+
</template>
|
|
151
|
+
<template v-else-if="resource.extras['analysis:parsing:error']">
|
|
152
|
+
{{ t("L'analyse de ce fichier a rencontré une erreur, l'aperçu n'est pas disponible. Téléchargez-le depuis l'onglet Téléchargements.") }}
|
|
153
|
+
<br>
|
|
154
|
+
<span class="text-gray-medium text-xs">{{ resource.extras['analysis:parsing:error'] }}</span>
|
|
155
|
+
</template>
|
|
156
|
+
<template v-else>
|
|
157
|
+
{{ t("Ce fichier ne peut pas être prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.") }}
|
|
158
|
+
</template>
|
|
159
|
+
</PreviewUnavailable>
|
|
139
160
|
</div>
|
|
140
161
|
<div v-if="tab.key === 'description'">
|
|
141
162
|
<MarkdownViewer
|
|
@@ -283,7 +304,7 @@
|
|
|
283
304
|
<p>{{ t("- Si le fichier est supprimé, l'API sera également supprimée.") }}</p>
|
|
284
305
|
<p>{{ t("Pour des usages pérennes, prévoyez que cette API dépend directement du fichier source.") }}</p>
|
|
285
306
|
</div>
|
|
286
|
-
<
|
|
307
|
+
<OpenApiViewer
|
|
287
308
|
v-if="hasTabularData"
|
|
288
309
|
:url="`${config.tabularApiUrl}/api/resources/${resource.id}/swagger/`"
|
|
289
310
|
/>
|
|
@@ -298,12 +319,13 @@
|
|
|
298
319
|
<script setup lang="ts">
|
|
299
320
|
import { computed, defineAsyncComponent } from 'vue'
|
|
300
321
|
import { RiDownloadLine, RiFileCopyLine, RiFileWarningLine, RiInformationLine, RiSubtractLine } from '@remixicon/vue'
|
|
322
|
+
import PreviewUnavailable from '../ResourceAccordion/PreviewUnavailable.vue'
|
|
301
323
|
import { toast } from 'vue-sonner'
|
|
302
324
|
import BrandedButton from '../BrandedButton.vue'
|
|
303
325
|
import CopyButton from '../CopyButton.vue'
|
|
304
326
|
import MarkdownViewer from '../MarkdownViewer.vue'
|
|
305
327
|
import ResourceIcon from '../ResourceAccordion/ResourceIcon.vue'
|
|
306
|
-
import
|
|
328
|
+
import OpenApiViewer from '../OpenApiViewer/OpenApiViewer.vue'
|
|
307
329
|
import TabGroup from '../Tabs/TabGroup.vue'
|
|
308
330
|
import TabList from '../Tabs/TabList.vue'
|
|
309
331
|
import Tab from '../Tabs/Tab.vue'
|
|
@@ -313,9 +335,9 @@ import Tooltip from '../Tooltip.vue'
|
|
|
313
335
|
import Preview from '../ResourceAccordion/Preview.vue'
|
|
314
336
|
import DataStructure from '../ResourceAccordion/DataStructure.vue'
|
|
315
337
|
import Metadata from '../ResourceAccordion/Metadata.vue'
|
|
338
|
+
import SchemaBadge from '../ResourceAccordion/SchemaBadge.vue'
|
|
316
339
|
import { filesize, summarize } from '../../functions/helpers'
|
|
317
|
-
import { getResourceFormatIcon } from '../../functions/resources'
|
|
318
|
-
import { getResourceExternalUrl, getResourceFilesize } from '../../functions/datasets'
|
|
340
|
+
import { getResourceFormatIcon, getResourceExternalUrl, getResourceFilesize } from '../../functions/resources'
|
|
319
341
|
import { trackEvent } from '../../functions/matomo'
|
|
320
342
|
import { useComponentsConfig } from '../../config'
|
|
321
343
|
import { useFormatDate } from '../../functions/dates'
|
|
@@ -342,9 +364,6 @@ const MapContainer = defineAsyncComponent(() =>
|
|
|
342
364
|
const Pmtiles = defineAsyncComponent(() =>
|
|
343
365
|
import('../ResourceAccordion/Pmtiles.client.vue'),
|
|
344
366
|
)
|
|
345
|
-
const SwaggerClient = defineAsyncComponent(() =>
|
|
346
|
-
import('../ResourceAccordion/Swagger.client.vue'),
|
|
347
|
-
)
|
|
348
367
|
|
|
349
368
|
const props = defineProps<{
|
|
350
369
|
dataset: Dataset | DatasetV2
|