@datagouv/components-next 1.0.2-dev.19 → 1.0.2-dev.191
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/README.md +7 -6
- package/assets/main.css +4 -0
- package/dist/{Control-DuZJdKV_.js → Control-B9WQxlpJ.js} +1 -1
- package/dist/{Datafair.client-Ds3VQrRi.js → Datafair.client-fxOwwaND.js} +9 -9
- package/dist/{Event--kp8kMdJ.js → Event-iXDrp-fy.js} +31 -31
- package/dist/{Image-34hvypZI.js → Image-mNMBOofs.js} +9 -9
- package/dist/ImagePreview.client-XSR9THWR.js +36 -0
- package/dist/JsonPreview.client-CU1FWFIy.js +40 -0
- package/dist/{Map-BjUnLyj8.js → Map-BfqKMw0S.js} +1739 -1599
- package/dist/{MapContainer.client-B2BijMx5.js → MapContainer.client-D0-Dn7jq.js} +41 -42
- package/dist/{OSM-s40W6sQ2.js → OSM-CcrUhItr.js} +15 -23
- package/dist/{PdfPreview.client-DJBMzrlC.js → PdfPreview.client-B7eDgq8y.js} +835 -856
- package/dist/Pmtiles.client-CCpvggkZ.js +26333 -0
- package/dist/PreviewWrapper.vue_vue_type_script_setup_true_lang-BzhNjYoY.js +60 -0
- package/dist/{ScaleLine-KW-nXqp3.js → ScaleLine-VGQ7nkMS.js} +2 -2
- package/dist/{Tile-DbNFNPfU.js → Tile-CSpn0Ca8.js} +160 -163
- package/dist/{TileImage-BsXBxMtq.js → TileImage-CpbaEFwd.js} +143 -131
- package/dist/{View-BR92hTWP.js → View-BYBpt-AU.js} +483 -422
- package/dist/XmlPreview.client-CyjpNh5i.js +34 -0
- package/dist/{common-PJfpC179.js → common-BoeTUnDZ.js} +52 -52
- package/dist/components-next.css +1 -52
- package/dist/components-next.js +194 -149
- package/dist/components.css +2 -2
- package/dist/{index-BFQX7Ccz.js → index-BBnxH-Nj.js} +21842 -15810
- package/dist/{leaflet-src-7m1mB8LI.js → leaflet-src-Be0Ohp2k.js} +185 -187
- package/dist/{main-vqhkVhVS.js → main-C2kCZkPD.js} +41515 -73245
- package/dist/{proj-DsetBcW7.js → proj-DxGbobp7.js} +662 -626
- package/dist/{text-clamp.esm-B5kW_XMt.js → text-clamp.esm-DVseBqZZ.js} +4 -4
- package/dist/{tilecoord-Db24Px13.js → tilecoord-DvX7WWiA.js} +273 -264
- package/dist/{vue3-json-viewer-BXwup7nO.js → vue3-json-viewer-CJLQFzXZ.js} +10 -16
- package/dist/{vue3-xml-viewer.common-BcNJo35o.js → vue3-xml-viewer.common-__BZsi8i.js} +16 -16
- package/package.json +47 -38
- package/src/chart.ts +5 -0
- package/src/components/ActivityList/ActivityList.vue +3 -2
- package/src/components/AppLink.vue +3 -0
- package/src/components/BannerAction.vue +6 -2
- package/src/components/Chart/ChartViewer.vue +320 -0
- package/src/components/Chart/ChartViewerWrapper.vue +182 -0
- package/src/components/CopyButton.vue +6 -2
- package/src/components/DataserviceCard.vue +9 -0
- package/src/components/DataserviceQuality.vue +82 -0
- package/src/components/DataserviceQualityInline.vue +31 -0
- package/src/components/DataserviceQualityTooltipContent.vue +79 -0
- package/src/components/DatasetCard.vue +9 -4
- package/src/components/DatasetQuality.vue +11 -11
- package/src/components/DatasetQualityInline.vue +2 -2
- package/src/components/DatasetQualityTooltipContent.vue +1 -1
- package/src/components/Form/Listbox.vue +101 -0
- package/src/components/Form/SchemaSelect.vue +2 -0
- package/src/components/InfiniteLoader.vue +53 -0
- package/src/components/ObjectCardHeader.vue +11 -4
- 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 +23 -58
- package/src/components/RadioInput.vue +7 -2
- package/src/components/ResourceAccordion/DataStructure.vue +11 -33
- package/src/components/ResourceAccordion/Datafair.client.vue +1 -1
- package/src/components/ResourceAccordion/Downloads.vue +160 -0
- package/src/components/ResourceAccordion/ImagePreview.client.vue +48 -0
- package/src/components/ResourceAccordion/JsonPreview.client.vue +23 -104
- package/src/components/ResourceAccordion/MapContainer.client.vue +2 -11
- package/src/components/ResourceAccordion/Metadata.vue +1 -2
- package/src/components/ResourceAccordion/PdfPreview.client.vue +58 -97
- package/src/components/ResourceAccordion/PdfSkeleton.vue +44 -0
- package/src/components/ResourceAccordion/Pmtiles.client.vue +28 -13
- package/src/components/ResourceAccordion/Preview.vue +11 -11
- package/src/components/ResourceAccordion/PreviewWrapper.vue +89 -0
- package/src/components/ResourceAccordion/ResourceAccordion.vue +23 -114
- package/src/components/ResourceAccordion/XmlPreview.client.vue +16 -98
- package/src/components/ResourceExplorer/ResourceExplorer.vue +102 -210
- package/src/components/ResourceExplorer/ResourceExplorerHeader.vue +74 -0
- package/src/components/ResourceExplorer/ResourceExplorerSidebar.vue +103 -71
- package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +143 -291
- package/src/components/ResourceExplorer/ResourceMainAction.vue +240 -0
- package/src/components/ResourceExplorer/ResourceSelector.vue +51 -0
- package/src/components/ResourceExplorer/ResourceViewerHeader.vue +115 -0
- package/src/components/ResourceExplorer/ResourceViewerSkeleton.vue +94 -0
- package/src/components/ResourceListItem.vue +161 -0
- package/src/components/ReuseCard.vue +14 -6
- package/src/components/ReuseDetails.vue +2 -2
- package/src/components/ReuseHorizontalCard.vue +2 -2
- package/src/components/SchemaCard.vue +10 -1
- package/src/components/Search/Filter/AccessTypeFilter.vue +2 -2
- package/src/components/Search/GlobalSearch.vue +220 -117
- package/src/components/Search/SearchInput.vue +5 -4
- package/src/components/Search/Sidemenu.vue +4 -3
- package/src/components/TabularExplorer/TabularActiveFilters.vue +79 -0
- package/src/components/TabularExplorer/TabularCell.vue +50 -0
- package/src/components/TabularExplorer/TabularCellPopover.vue +170 -0
- package/src/components/TabularExplorer/TabularColumnsMenu.vue +112 -0
- package/src/components/TabularExplorer/TabularExplorer.vue +299 -0
- package/src/components/TabularExplorer/TabularFilterContent.vue +362 -0
- package/src/components/TabularExplorer/TabularFilterPopover.vue +106 -0
- package/src/components/TabularExplorer/TabularMobileFilterButton.vue +21 -0
- package/src/components/TabularExplorer/TabularMobileFilters.vue +150 -0
- package/src/components/TabularExplorer/TabularRowsInfo.vue +31 -0
- package/src/components/TabularExplorer/TabularSkeleton.vue +92 -0
- package/src/components/TabularExplorer/TabularTable.vue +481 -0
- package/src/components/TabularExplorer/TabularToolbar.vue +25 -0
- package/src/components/TabularExplorer/types.ts +81 -0
- package/src/components/TabularExplorer/useColumnMetadata.ts +98 -0
- package/src/components/TabularExplorer/useTabularContext.ts +82 -0
- package/src/composables/useActiveDescendant.ts +5 -0
- package/src/composables/useDatasetResources.ts +164 -0
- package/src/composables/useHasTabularData.ts +15 -1
- package/src/composables/useMetrics.ts +1 -1
- package/src/composables/useResizable.ts +46 -0
- package/src/composables/useResourceCapabilities.ts +48 -40
- package/src/composables/useSearchFilter.ts +120 -0
- package/src/composables/useStableQueryParams.ts +38 -6
- package/src/composables/useTabularProfile.ts +70 -0
- package/src/composables/useTranslation.ts +24 -9
- package/src/config.ts +21 -3
- package/src/functions/activities.ts +3 -3
- package/src/functions/api.ts +20 -42
- package/src/functions/api.types.ts +1 -0
- package/src/functions/charts.ts +99 -0
- package/src/functions/datasets.ts +0 -17
- package/src/functions/dates.ts +3 -2
- package/src/functions/helpers.ts +15 -0
- package/src/functions/metrics.ts +63 -60
- package/src/functions/paginate.ts +46 -0
- package/src/functions/resourceCapabilities.ts +34 -0
- package/src/functions/resources.ts +136 -1
- package/src/functions/schemas.ts +27 -0
- package/src/functions/tabular.ts +214 -0
- package/src/functions/tabularApi.ts +96 -11
- package/src/main.ts +117 -14
- package/src/types/dataservices.ts +17 -0
- package/src/types/organizations.ts +10 -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/resources.ts +0 -1
- package/src/types/reuses.ts +12 -1
- package/src/types/search.ts +63 -1
- package/src/types/site.ts +5 -3
- package/src/types/ui.ts +2 -0
- package/src/types/users.ts +2 -1
- package/src/types/visualizations.ts +93 -0
- package/assets/swagger-themes/newspaper.css +0 -1670
- package/dist/JsonPreview.client-CYxmz0yV.js +0 -72
- package/dist/Pmtiles.client-C_hvRDuy.js +0 -25185
- package/dist/Swagger.client-B4y0hXME.js +0 -4
- package/dist/XmlPreview.client-LETEfNBH.js +0 -64
- package/src/components/ResourceAccordion/Swagger.client.vue +0 -48
- package/src/functions/pagination.ts +0 -9
- /package/src/components/{DatasetQualityItem.vue → QualityItem.vue} +0 -0
- /package/src/components/{DatasetQualityItemWarning.vue → QualityItemWarning.vue} +0 -0
- /package/src/components/{DatasetQualityScore.vue → QualityScore.vue} +0 -0
|
@@ -1,95 +1,23 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
</h3>
|
|
13
|
-
<CopyButton
|
|
14
|
-
:label="t('Copier le lien')"
|
|
15
|
-
:copied-label="t('Lien copié !')"
|
|
16
|
-
:text="resourceExternalUrl"
|
|
17
|
-
/>
|
|
18
|
-
</div>
|
|
19
|
-
<div class="text-gray-medium text-xs flex items-center gap-1">
|
|
20
|
-
<SchemaBadge :resource />
|
|
21
|
-
<RiSubtractLine
|
|
22
|
-
v-if="resource.schema"
|
|
23
|
-
aria-hidden="true"
|
|
24
|
-
class="size-3 fill-gray-medium"
|
|
25
|
-
/>
|
|
26
|
-
<span>{{ t('mis à jour {date}', { date: formatRelativeIfRecentDate(resource.last_modified) }) }}</span>
|
|
27
|
-
<RiSubtractLine
|
|
28
|
-
aria-hidden="true"
|
|
29
|
-
class="size-3 fill-gray-medium"
|
|
30
|
-
/>
|
|
31
|
-
<template v-if="resource.format">
|
|
32
|
-
<span>
|
|
33
|
-
{{ resource.format.trim().toLowerCase() }}
|
|
34
|
-
<span v-if="resourceFilesize">({{ filesize(resourceFilesize) }})</span>
|
|
35
|
-
</span>
|
|
36
|
-
<RiSubtractLine
|
|
37
|
-
aria-hidden="true"
|
|
38
|
-
class="size-3 fill-gray-medium"
|
|
39
|
-
/>
|
|
40
|
-
</template>
|
|
41
|
-
<span class="inline-flex items-center">
|
|
42
|
-
<RiDownloadLine class="size-3 mr-0.5" />
|
|
43
|
-
{{ summarize(resource.metrics.views) }}
|
|
44
|
-
</span>
|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
47
|
-
<div class="flex items-center gap-2">
|
|
48
|
-
<BrandedButton
|
|
49
|
-
v-if="isResourceUrl"
|
|
50
|
-
:href="resource.latest"
|
|
51
|
-
:title="t('Lien du fichier - ouvre une nouvelle fenêtre')"
|
|
52
|
-
rel="ugc nofollow noopener"
|
|
53
|
-
new-tab
|
|
54
|
-
size="xs"
|
|
55
|
-
external
|
|
56
|
-
@click="trackEvent('Jeux de données', 'Télécharger un fichier', 'Bouton : télécharger un fichier')"
|
|
57
|
-
>
|
|
58
|
-
{{ t('Visiter') }}
|
|
59
|
-
</BrandedButton>
|
|
60
|
-
<BrandedButton
|
|
61
|
-
v-else-if="ogcService"
|
|
62
|
-
:icon="RiFileCopyLine"
|
|
63
|
-
color="primary"
|
|
64
|
-
size="xs"
|
|
65
|
-
@click="copyResourceUrl"
|
|
66
|
-
>
|
|
67
|
-
{{ t('Copier le lien') }}
|
|
68
|
-
</BrandedButton>
|
|
69
|
-
<BrandedButton
|
|
70
|
-
v-else
|
|
71
|
-
:href="resource.latest"
|
|
72
|
-
rel="ugc nofollow noopener"
|
|
73
|
-
:title="downloadButtonTitle"
|
|
74
|
-
download
|
|
75
|
-
class="matomo_download"
|
|
76
|
-
:icon="unavailable ? RiFileWarningLine : RiDownloadLine"
|
|
77
|
-
size="xs"
|
|
78
|
-
color="primary"
|
|
79
|
-
external
|
|
80
|
-
@click="trackEvent('Jeux de données', 'Télécharger un fichier', 'Bouton : télécharger un fichier')"
|
|
81
|
-
>
|
|
82
|
-
{{ t('Télécharger') }}
|
|
83
|
-
</BrandedButton>
|
|
84
|
-
</div>
|
|
85
|
-
</header>
|
|
2
|
+
<div :class="fullscreen ? 'flex min-h-0 flex-1 flex-col' : ''">
|
|
3
|
+
<ResourceViewerHeader
|
|
4
|
+
:dataset
|
|
5
|
+
:resource
|
|
6
|
+
:resources
|
|
7
|
+
:resource-to
|
|
8
|
+
:explore-to="exploreTo"
|
|
9
|
+
:replace
|
|
10
|
+
:fullscreen
|
|
11
|
+
/>
|
|
86
12
|
|
|
87
|
-
<section class="
|
|
13
|
+
<section :class="fullscreen ? 'flex min-h-0 flex-1 flex-col' : ''">
|
|
88
14
|
<TabGroup
|
|
89
15
|
size="sm"
|
|
16
|
+
:default-index="defaultTabIndex"
|
|
17
|
+
:class="fullscreen ? 'flex min-h-0 flex-1 flex-col' : ''"
|
|
90
18
|
@change="switchTab"
|
|
91
19
|
>
|
|
92
|
-
<div class="
|
|
20
|
+
<div class="flex shrink-0 items-center border-b border-gray-default p-2">
|
|
93
21
|
<TabList class="max-w-full overflow-x-auto">
|
|
94
22
|
<Tab
|
|
95
23
|
v-for="tab in tabsOptions"
|
|
@@ -99,13 +27,16 @@
|
|
|
99
27
|
</Tab>
|
|
100
28
|
</TabList>
|
|
101
29
|
</div>
|
|
102
|
-
<TabPanels>
|
|
30
|
+
<TabPanels :class="fullscreen ? 'flex min-h-0 flex-1 flex-col' : ''">
|
|
103
31
|
<TabPanel
|
|
104
32
|
v-for="tab in tabsOptions"
|
|
105
33
|
:key="tab.key"
|
|
106
|
-
class="
|
|
34
|
+
:class="[tab.key === 'data' || tab.key === 'map' ? '' : 'p-4', fullscreen ? 'flex min-h-0 flex-1 flex-col' : '']"
|
|
107
35
|
>
|
|
108
|
-
<div
|
|
36
|
+
<div
|
|
37
|
+
v-if="tab.key === 'map'"
|
|
38
|
+
:class="fullscreen ? 'flex min-h-0 flex-1 flex-col' : 'h-[600px]'"
|
|
39
|
+
>
|
|
109
40
|
<Pmtiles
|
|
110
41
|
v-if="hasPmtiles"
|
|
111
42
|
:resource="resource"
|
|
@@ -115,36 +46,81 @@
|
|
|
115
46
|
v-if="ogcWms"
|
|
116
47
|
:resource="resource"
|
|
117
48
|
/>
|
|
49
|
+
<PreviewUnavailable v-if="!hasPmtiles && !ogcWms && hasPmtilesError">
|
|
50
|
+
{{ t("La carte n'a pas pu être générée automatiquement pour ce fichier.") }}
|
|
51
|
+
<br>
|
|
52
|
+
<span class="text-gray-medium text-xs">{{ pmtilesError }}</span>
|
|
53
|
+
</PreviewUnavailable>
|
|
118
54
|
</div>
|
|
119
|
-
<div
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
55
|
+
<div
|
|
56
|
+
v-if="tab.key === 'data'"
|
|
57
|
+
:class="fullscreen ? 'flex min-h-0 flex-1 flex-col' : ''"
|
|
58
|
+
>
|
|
59
|
+
<!-- Wrapped in Suspense so switching to this tab (or loading its data) shows
|
|
60
|
+
the table skeleton instead of a blank gap while TabularExplorer resolves. -->
|
|
61
|
+
<Suspense
|
|
62
|
+
v-if="previewKind === 'tabular'"
|
|
63
|
+
:timeout="200"
|
|
64
|
+
>
|
|
65
|
+
<TabularExplorer :resource-id="resource.id">
|
|
66
|
+
<TabularToolbar class="shrink-0 border-b border-gray-default p-2" />
|
|
67
|
+
<TabularTable :fill="fullscreen" />
|
|
68
|
+
<TabularMobileFilters />
|
|
69
|
+
</TabularExplorer>
|
|
70
|
+
<template #fallback>
|
|
71
|
+
<TabularSkeleton :fill="fullscreen" />
|
|
72
|
+
</template>
|
|
73
|
+
</Suspense>
|
|
74
|
+
|
|
75
|
+
<!-- PDF is a full-bleed visual preview like the table and the map: it
|
|
76
|
+
owns its own reader backdrop, so it sits outside the padded wrapper. -->
|
|
124
77
|
<PdfPreview
|
|
125
|
-
v-else-if="
|
|
126
|
-
:resource="resource"
|
|
127
|
-
/>
|
|
128
|
-
<XmlPreview
|
|
129
|
-
v-else-if="resource.format && resource.format.toLowerCase() === 'xml'"
|
|
130
|
-
:resource="resource"
|
|
131
|
-
/>
|
|
132
|
-
<DatafairPreview
|
|
133
|
-
v-else-if="hasDatafairPreview"
|
|
134
|
-
:resource="resource"
|
|
135
|
-
:dataset="dataset"
|
|
136
|
-
/>
|
|
137
|
-
<SwaggerClient
|
|
138
|
-
v-else-if="hasOpenAPIPreview"
|
|
139
|
-
:url="resource.extras['apidocUrl'] as string"
|
|
140
|
-
/>
|
|
141
|
-
<Preview
|
|
142
|
-
v-else-if="hasTabularData"
|
|
78
|
+
v-else-if="previewKind === 'pdf'"
|
|
143
79
|
:resource="resource"
|
|
80
|
+
:fill="fullscreen"
|
|
144
81
|
/>
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
82
|
+
|
|
83
|
+
<!-- Text previews stay padded inside the tab panel -->
|
|
84
|
+
<div
|
|
85
|
+
v-else
|
|
86
|
+
class="p-4"
|
|
87
|
+
>
|
|
88
|
+
<JsonPreview
|
|
89
|
+
v-if="previewKind === 'json'"
|
|
90
|
+
:resource="resource"
|
|
91
|
+
/>
|
|
92
|
+
<XmlPreview
|
|
93
|
+
v-else-if="previewKind === 'xml'"
|
|
94
|
+
:resource="resource"
|
|
95
|
+
/>
|
|
96
|
+
<ImagePreview
|
|
97
|
+
v-else-if="previewKind === 'image'"
|
|
98
|
+
:resource="resource"
|
|
99
|
+
/>
|
|
100
|
+
<DatafairPreview
|
|
101
|
+
v-else-if="previewKind === 'datafair'"
|
|
102
|
+
:resource="resource"
|
|
103
|
+
:dataset="dataset"
|
|
104
|
+
/>
|
|
105
|
+
<OpenApiViewer
|
|
106
|
+
v-else-if="previewKind === 'openapi'"
|
|
107
|
+
:url="resource.extras['apidocUrl'] as string"
|
|
108
|
+
/>
|
|
109
|
+
<PreviewUnavailable v-else>
|
|
110
|
+
<!-- "File too large to download" is the only analysis:error value from hydra for now -->
|
|
111
|
+
<template v-if="resource.extras['analysis:error'] === 'File too large to download'">
|
|
112
|
+
{{ t("Ce fichier est trop volumineux pour être analysé et prévisualisé. Téléchargez-le avec le bouton Télécharger.") }}
|
|
113
|
+
</template>
|
|
114
|
+
<template v-else-if="resource.extras['analysis:parsing:error']">
|
|
115
|
+
{{ t("L'analyse de ce fichier a rencontré une erreur, l'aperçu n'est pas disponible. Téléchargez-le avec le bouton Télécharger.") }}
|
|
116
|
+
<br>
|
|
117
|
+
<span class="text-gray-medium text-xs">{{ resource.extras['analysis:parsing:error'] }}</span>
|
|
118
|
+
</template>
|
|
119
|
+
<template v-else>
|
|
120
|
+
{{ t("Ce fichier ne peut pas être prévisualisé. Téléchargez-le avec le bouton Télécharger.") }}
|
|
121
|
+
</template>
|
|
122
|
+
</PreviewUnavailable>
|
|
123
|
+
</div>
|
|
148
124
|
</div>
|
|
149
125
|
<div v-if="tab.key === 'description'">
|
|
150
126
|
<MarkdownViewer
|
|
@@ -161,138 +137,17 @@
|
|
|
161
137
|
<div v-if="tab.key === 'metadata'">
|
|
162
138
|
<Metadata :resource />
|
|
163
139
|
</div>
|
|
164
|
-
<div v-if="tab.key === '
|
|
165
|
-
<dl class="fr-pl-0">
|
|
166
|
-
<dt
|
|
167
|
-
v-if="resource.format === 'url'"
|
|
168
|
-
class="font-bold fr-text--sm fr-mb-0"
|
|
169
|
-
>
|
|
170
|
-
{{ t("URL d'origine") }}
|
|
171
|
-
</dt>
|
|
172
|
-
<dt
|
|
173
|
-
v-else
|
|
174
|
-
class="font-bold fr-text--sm fr-mb-0"
|
|
175
|
-
>
|
|
176
|
-
{{ t('Format original') }}
|
|
177
|
-
</dt>
|
|
178
|
-
<dd class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center">
|
|
179
|
-
<span
|
|
180
|
-
v-if="resource.format === 'url'"
|
|
181
|
-
class="inline-flex items-center max-w-full"
|
|
182
|
-
>
|
|
183
|
-
<a
|
|
184
|
-
:href="resource.latest"
|
|
185
|
-
class="fr-link no-icon-after truncate"
|
|
186
|
-
rel="ugc nofollow noopener"
|
|
187
|
-
target="_blank"
|
|
188
|
-
@click="trackEvent('Jeux de données', 'Télécharger un fichier', 'Bouton : télécharger un fichier')"
|
|
189
|
-
>
|
|
190
|
-
{{ resource.url }}
|
|
191
|
-
</a>
|
|
192
|
-
<span class="fr-ml-1v fr-icon-external-link-line fr-icon--sm shrink-0" />
|
|
193
|
-
</span>
|
|
194
|
-
<span v-else>
|
|
195
|
-
<span class="text-datagouv fr-icon-download-line fr-icon--sm fr-mr-1v fr-mt-1v" />
|
|
196
|
-
<a
|
|
197
|
-
:href="resource.latest"
|
|
198
|
-
class="fr-link"
|
|
199
|
-
rel="ugc nofollow noopener"
|
|
200
|
-
@click="trackEvent('Jeux de données', 'Télécharger un fichier', `Bouton : format ${resource.format}`)"
|
|
201
|
-
>
|
|
202
|
-
<span>{{ t('Format {format}', { format: resource.format }) }}<span v-if="resourceFilesize"> - {{ filesize(resourceFilesize) }}</span></span>
|
|
203
|
-
</a>
|
|
204
|
-
</span>
|
|
205
|
-
<CopyButton
|
|
206
|
-
:label="t('Copier le lien')"
|
|
207
|
-
:copied-label="t('Lien copié !')"
|
|
208
|
-
:text="resource.latest"
|
|
209
|
-
class="relative"
|
|
210
|
-
/>
|
|
211
|
-
</dd>
|
|
212
|
-
<template v-if="generatedFormats.length">
|
|
213
|
-
<dt class="font-bold fr-text--sm fr-mb-0">
|
|
214
|
-
{{ t('Formats générés automatiquement par {platform} (dernière mise à jour {date})', { platform: config.name, date: conversionsLastUpdate }) }}
|
|
215
|
-
</dt>
|
|
216
|
-
<dd
|
|
217
|
-
v-for="generatedFormat in generatedFormats"
|
|
218
|
-
:key="generatedFormat.format"
|
|
219
|
-
class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center"
|
|
220
|
-
>
|
|
221
|
-
<span>
|
|
222
|
-
<span class="text-datagouv fr-icon-download-line fr-icon--sm fr-mr-1v fr-mt-1v" />
|
|
223
|
-
<a
|
|
224
|
-
:href="generatedFormat.url"
|
|
225
|
-
class="fr-link"
|
|
226
|
-
rel="ugc nofollow noopener"
|
|
227
|
-
@click="trackEvent('Jeux de données', 'Télécharger un fichier', `Bouton : format ${generatedFormat.format}`)"
|
|
228
|
-
>
|
|
229
|
-
<span>{{ t('Format {format}', { format: generatedFormat.format }) }}<span v-if="generatedFormat.size"> - {{ filesize(generatedFormat.size) }}</span></span>
|
|
230
|
-
</a>
|
|
231
|
-
</span>
|
|
232
|
-
<CopyButton
|
|
233
|
-
:label="t('Copier le lien')"
|
|
234
|
-
:copied-label="t('Lien copié !')"
|
|
235
|
-
:text="generatedFormat.url"
|
|
236
|
-
class="relative"
|
|
237
|
-
/>
|
|
238
|
-
</dd>
|
|
239
|
-
</template>
|
|
240
|
-
<template v-if="wfsFormats.length">
|
|
241
|
-
<dt class="font-bold fr-text--sm fr-mb-0">
|
|
242
|
-
<div class="flex gap-1 items-center">
|
|
243
|
-
{{ t('Formats exportés depuis le service WFS') }}
|
|
244
|
-
<span v-if="defaultWfsProjection"> ({{ t('projection {crs}', { crs: defaultWfsProjection }) }})</span>
|
|
245
|
-
<Tooltip>
|
|
246
|
-
<RiInformationLine
|
|
247
|
-
class="flex-none size-4"
|
|
248
|
-
:aria-label="t(`Le lien de téléchargement interroge directement le flux WFS distant. Le nombre de features téléchargées peut être limité.`)"
|
|
249
|
-
aria-hidden="true"
|
|
250
|
-
/>
|
|
251
|
-
<template #tooltip>
|
|
252
|
-
<p class="text-sm font-normal mb-0">
|
|
253
|
-
{{ t(`Le lien de téléchargement interroge directement le flux WFS distant.`) }}
|
|
254
|
-
</p>
|
|
255
|
-
<p class="text-sm font-normal mb-0">
|
|
256
|
-
{{ t(`Le nombre de features téléchargées peut être limité.`) }}
|
|
257
|
-
</p>
|
|
258
|
-
</template>
|
|
259
|
-
</Tooltip>
|
|
260
|
-
</div>
|
|
261
|
-
</dt>
|
|
262
|
-
<dd
|
|
263
|
-
v-for="wfsFormat in wfsFormats"
|
|
264
|
-
:key="wfsFormat.format"
|
|
265
|
-
class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center"
|
|
266
|
-
>
|
|
267
|
-
<span>
|
|
268
|
-
<span class="text-datagouv fr-icon-download-line fr-icon--sm fr-mr-1v fr-mt-1v" />
|
|
269
|
-
<a
|
|
270
|
-
:href="wfsFormat.url"
|
|
271
|
-
class="fr-link"
|
|
272
|
-
rel="ugc nofollow noopener"
|
|
273
|
-
@click="trackEvent('Jeux de données', 'Télécharger un fichier', `Bouton : format ${wfsFormat.format}`)"
|
|
274
|
-
>
|
|
275
|
-
<span>{{ t('Format {format}', { format: wfsFormat.format }) }}</span>
|
|
276
|
-
</a>
|
|
277
|
-
</span>
|
|
278
|
-
<CopyButton
|
|
279
|
-
:label="t('Copier le lien')"
|
|
280
|
-
:copied-label="t('Lien copié !')"
|
|
281
|
-
:text="wfsFormat.url"
|
|
282
|
-
class="relative"
|
|
283
|
-
/>
|
|
284
|
-
</dd>
|
|
285
|
-
</template>
|
|
286
|
-
</dl>
|
|
287
|
-
</div>
|
|
288
|
-
<div v-if="tab.key === 'swagger'">
|
|
140
|
+
<div v-if="tab.key === 'api'">
|
|
289
141
|
<div class="fr-mb-4w">
|
|
290
142
|
<p>{{ t("Cette API est générée automatiquement par {platform} à partir du fichier.", { platform: config.name }) }}</p>
|
|
291
143
|
<p>{{ t("- Si le fichier est modifié, l'API sera mise à jour et sa structure pourra changer.") }}</p>
|
|
292
144
|
<p>{{ t("- Si le fichier est supprimé, l'API sera également supprimée.") }}</p>
|
|
293
145
|
<p>{{ t("Pour des usages pérennes, prévoyez que cette API dépend directement du fichier source.") }}</p>
|
|
146
|
+
<p v-if="config.tabularApiUrl">
|
|
147
|
+
{{ t("L'URL de base de l'API est {url}", { url: config.tabularApiUrl }) }}
|
|
148
|
+
</p>
|
|
294
149
|
</div>
|
|
295
|
-
<
|
|
150
|
+
<OpenApiViewer
|
|
296
151
|
v-if="hasTabularData"
|
|
297
152
|
:url="`${config.tabularApiUrl}/api/resources/${resource.id}/swagger/`"
|
|
298
153
|
/>
|
|
@@ -306,32 +161,29 @@
|
|
|
306
161
|
|
|
307
162
|
<script setup lang="ts">
|
|
308
163
|
import { computed, defineAsyncComponent } from 'vue'
|
|
309
|
-
import {
|
|
164
|
+
import { useRoute, useRouter } from 'vue-router'
|
|
310
165
|
import PreviewUnavailable from '../ResourceAccordion/PreviewUnavailable.vue'
|
|
311
|
-
import { toast } from 'vue-sonner'
|
|
312
|
-
import BrandedButton from '../BrandedButton.vue'
|
|
313
|
-
import CopyButton from '../CopyButton.vue'
|
|
314
166
|
import MarkdownViewer from '../MarkdownViewer.vue'
|
|
315
|
-
import
|
|
316
|
-
import Swagger from '../ResourceAccordion/Swagger.client.vue'
|
|
167
|
+
import OpenApiViewer from '../OpenApiViewer/OpenApiViewer.vue'
|
|
317
168
|
import TabGroup from '../Tabs/TabGroup.vue'
|
|
318
169
|
import TabList from '../Tabs/TabList.vue'
|
|
319
170
|
import Tab from '../Tabs/Tab.vue'
|
|
320
171
|
import TabPanels from '../Tabs/TabPanels.vue'
|
|
321
172
|
import TabPanel from '../Tabs/TabPanel.vue'
|
|
322
|
-
import
|
|
323
|
-
import
|
|
173
|
+
import TabularExplorer from '../TabularExplorer/TabularExplorer.vue'
|
|
174
|
+
import TabularToolbar from '../TabularExplorer/TabularToolbar.vue'
|
|
175
|
+
import TabularTable from '../TabularExplorer/TabularTable.vue'
|
|
176
|
+
import TabularMobileFilters from '../TabularExplorer/TabularMobileFilters.vue'
|
|
177
|
+
import TabularSkeleton from '../TabularExplorer/TabularSkeleton.vue'
|
|
324
178
|
import DataStructure from '../ResourceAccordion/DataStructure.vue'
|
|
325
179
|
import Metadata from '../ResourceAccordion/Metadata.vue'
|
|
326
|
-
import
|
|
327
|
-
import { filesize, summarize } from '../../functions/helpers'
|
|
328
|
-
import { getResourceFormatIcon } from '../../functions/resources'
|
|
329
|
-
import { getResourceExternalUrl, getResourceFilesize } from '../../functions/datasets'
|
|
180
|
+
import ResourceViewerHeader from './ResourceViewerHeader.vue'
|
|
330
181
|
import { trackEvent } from '../../functions/matomo'
|
|
331
182
|
import { useComponentsConfig } from '../../config'
|
|
332
|
-
import { useFormatDate } from '../../functions/dates'
|
|
333
183
|
import { useTranslation } from '../../composables/useTranslation'
|
|
334
184
|
import { useResourceCapabilities } from '../../composables/useResourceCapabilities'
|
|
185
|
+
import { provideTabularProfile } from '../../composables/useTabularProfile'
|
|
186
|
+
import type { RouteLocationRaw } from 'vue-router'
|
|
335
187
|
import type { Resource } from '../../types/resources'
|
|
336
188
|
import type { Dataset, DatasetV2 } from '../../types/datasets'
|
|
337
189
|
|
|
@@ -344,6 +196,9 @@ const PdfPreview = defineAsyncComponent(() =>
|
|
|
344
196
|
const XmlPreview = defineAsyncComponent(() =>
|
|
345
197
|
import('../ResourceAccordion/XmlPreview.client.vue'),
|
|
346
198
|
)
|
|
199
|
+
const ImagePreview = defineAsyncComponent(() =>
|
|
200
|
+
import('../ResourceAccordion/ImagePreview.client.vue'),
|
|
201
|
+
)
|
|
347
202
|
const DatafairPreview = defineAsyncComponent(() =>
|
|
348
203
|
import('../ResourceAccordion/Datafair.client.vue'),
|
|
349
204
|
)
|
|
@@ -353,63 +208,60 @@ const MapContainer = defineAsyncComponent(() =>
|
|
|
353
208
|
const Pmtiles = defineAsyncComponent(() =>
|
|
354
209
|
import('../ResourceAccordion/Pmtiles.client.vue'),
|
|
355
210
|
)
|
|
356
|
-
const SwaggerClient = defineAsyncComponent(() =>
|
|
357
|
-
import('../ResourceAccordion/Swagger.client.vue'),
|
|
358
|
-
)
|
|
359
211
|
|
|
360
|
-
const props = defineProps<{
|
|
212
|
+
const props = withDefaults(defineProps<{
|
|
361
213
|
dataset: Dataset | DatasetV2
|
|
362
214
|
resource: Resource
|
|
363
|
-
|
|
215
|
+
resources?: Resource[]
|
|
216
|
+
resourceTo: (resource: Resource) => RouteLocationRaw
|
|
217
|
+
// When provided (inline mode), shows an "Explorer" button next to the download
|
|
218
|
+
// action that opens the fullscreen explorer on the current resource.
|
|
219
|
+
exploreTo?: (resource: Resource) => string
|
|
220
|
+
replace?: boolean
|
|
221
|
+
// Fullscreen mode: make the viewer a flex column so the table fills down to the
|
|
222
|
+
// bottom, and hide the inline download/visit/copy actions — they're shown in the
|
|
223
|
+
// dataset context bar above. Inline mode (dataset page) shows them in the header.
|
|
224
|
+
fullscreen?: boolean
|
|
225
|
+
}>(), {
|
|
226
|
+
fullscreen: false,
|
|
227
|
+
})
|
|
364
228
|
|
|
365
229
|
const { t } = useTranslation()
|
|
366
230
|
const config = useComponentsConfig()
|
|
367
|
-
const
|
|
231
|
+
const route = useRoute()
|
|
232
|
+
const router = useRouter()
|
|
368
233
|
|
|
369
234
|
const {
|
|
235
|
+
previewKind,
|
|
370
236
|
hasTabularData,
|
|
371
237
|
hasPmtiles,
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
ogcService,
|
|
238
|
+
hasPmtilesError,
|
|
239
|
+
pmtilesError,
|
|
375
240
|
ogcWms,
|
|
376
|
-
generatedFormats,
|
|
377
|
-
wfsFormats,
|
|
378
|
-
defaultWfsProjection,
|
|
379
|
-
isResourceUrl,
|
|
380
241
|
tabsOptions,
|
|
381
242
|
} = useResourceCapabilities(() => props.resource, () => props.dataset)
|
|
382
243
|
|
|
383
|
-
|
|
384
|
-
|
|
244
|
+
// Share the tabular profile fetch between TabularExplorer and DataStructure tabs.
|
|
245
|
+
// Only tabular resources have one: asking for the profile of a PDF or an image is a
|
|
246
|
+
// request to the Tabular API that can only fail.
|
|
247
|
+
if (hasTabularData.value) {
|
|
248
|
+
await provideTabularProfile(() => props.resource.id)
|
|
249
|
+
}
|
|
385
250
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
return t('Télécharger le fichier en {format}', { format: format.value })
|
|
251
|
+
// The active tab lives in the URL so a shared link opens on the same one. Read once
|
|
252
|
+
// at mount (TabGroup only takes an initial index), which is enough: switching resource
|
|
253
|
+
// remounts the viewer, and the tab is preserved when the new resource also has it.
|
|
254
|
+
const defaultTabIndex = computed(() => {
|
|
255
|
+
const index = tabsOptions.value.findIndex(option => option.key === route.query.tab)
|
|
256
|
+
return index === -1 ? 0 : index
|
|
394
257
|
})
|
|
395
258
|
|
|
396
|
-
const conversionsLastUpdate = computed(() =>
|
|
397
|
-
formatRelativeIfRecentDate(props.resource.extras['analysis:parsing:finished_at'] as string | undefined),
|
|
398
|
-
)
|
|
399
|
-
|
|
400
|
-
const copyResourceUrl = async () => {
|
|
401
|
-
try {
|
|
402
|
-
await navigator.clipboard.writeText(props.resource.url)
|
|
403
|
-
toast.success(t('Lien copié !'))
|
|
404
|
-
}
|
|
405
|
-
catch {
|
|
406
|
-
toast.error(t('Impossible de copier dans le presse-papier'))
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
259
|
const switchTab = (index: number) => {
|
|
411
260
|
const option = tabsOptions.value[index]
|
|
412
261
|
if (!option) return
|
|
262
|
+
// The first tab is the default: drop `tab` rather than writing it in the URL.
|
|
263
|
+
const { tab: _, ...query } = route.query
|
|
264
|
+
router.replace({ query: index === 0 ? query : { ...query, tab: option.key } })
|
|
413
265
|
trackEvent('View resource tab', props.resource.id, option.label)
|
|
414
266
|
if (option.key === 'data') {
|
|
415
267
|
trackEvent('Show preview', props.resource.id)
|