@datagouv/components-next 1.2.0 → 1.4.0
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/dist/{Control-ZFh5ta_U.js → Control-B9WQxlpJ.js} +1 -1
- package/dist/{Datafair.client-rf4T1IkA.js → Datafair.client-D_6yzJUG.js} +9 -9
- package/dist/{Event-DSQcW7OF.js → Event-iXDrp-fy.js} +11 -11
- package/dist/{Image-BijNEG0p.js → Image-mNMBOofs.js} +5 -5
- package/dist/ImagePreview.client-B0iPhFm3.js +36 -0
- package/dist/{JsonPreview.client-dzar6iuh.js → JsonPreview.client-DSl1kiwC.js} +5 -5
- package/dist/{Map-BUtPf5GN.js → Map-BfqKMw0S.js} +1306 -1166
- package/dist/{MapContainer.client-D-MoRNhG.js → MapContainer.client-BSfpk_rT.js} +15 -15
- package/dist/{OSM-D4MTdBtk.js → OSM-CcrUhItr.js} +15 -23
- package/dist/{PdfPreview.client-DoDYLmJD.js → PdfPreview.client-BANFeM6K.js} +441 -434
- package/dist/Pmtiles.client-DH9ZrsZR.js +26333 -0
- package/dist/PreviewWrapper.vue_vue_type_script_setup_true_lang-I8EZCgzp.js +60 -0
- package/dist/{ScaleLine-hJQIqcZm.js → ScaleLine-VGQ7nkMS.js} +2 -2
- package/dist/{Tile-Dcl7oIVu.js → Tile-CSpn0Ca8.js} +153 -156
- package/dist/{TileImage-BJeHipMX.js → TileImage-CpbaEFwd.js} +143 -131
- package/dist/{View-xp_P_OHw.js → View-BYBpt-AU.js} +167 -117
- package/dist/{XmlPreview.client-cOhwff6P.js → XmlPreview.client-KCB5mwVf.js} +5 -5
- package/dist/{common-BjQlan3k.js → common-BoeTUnDZ.js} +26 -26
- package/dist/components-next.css +1 -52
- package/dist/components-next.js +201 -170
- package/dist/components.css +2 -2
- package/dist/{index-NofRBuyf.js → index-DHWzX5vK.js} +5999 -5472
- package/dist/{leaflet-src-7m1mB8LI.js → leaflet-src-pVQfonBI.js} +162 -164
- package/dist/{main-Iz1ZCL6k.js → main-BDcYkhOc.js} +36671 -29402
- package/dist/{proj-CsNo9yH1.js → proj-DxGbobp7.js} +312 -296
- package/dist/{text-clamp.esm-B5kW_XMt.js → text-clamp.esm-DVseBqZZ.js} +4 -4
- package/dist/{tilecoord-A0fLnBZr.js → tilecoord-DvX7WWiA.js} +264 -255
- package/dist/{vue3-json-viewer-BXwup7nO.js → vue3-json-viewer-CJLQFzXZ.js} +10 -16
- package/dist/{vue3-xml-viewer.common-tVI9uXUz.js → vue3-xml-viewer.common-Bdcn54ST.js} +1 -1
- package/package.json +40 -39
- package/src/components/ActivityList/ActivityList.vue +21 -12
- package/src/components/AppLink.vue +3 -0
- package/src/components/BannerAction.vue +6 -2
- package/src/components/Chart/ChartViewer.vue +111 -17
- package/src/components/Chart/ChartViewerWrapper.vue +28 -16
- package/src/components/ChartCard.vue +112 -0
- package/src/components/CopyButton.vue +6 -2
- package/src/components/DataserviceCard.vue +23 -6
- 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 +17 -6
- package/src/components/DatasetInformation/DatasetTemporalitySection.vue +7 -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/DiscussionMessageCard.vue +14 -15
- package/src/components/Form/SchemaSelect.vue +2 -0
- package/src/components/FormattedDate.vue +69 -0
- package/src/components/LoadingBlock.vue +4 -1
- package/src/components/ObjectCard.vue +4 -1
- package/src/components/ObjectCardOwner.vue +13 -1
- package/src/components/Pagination.vue +18 -58
- package/src/components/Placeholder.vue +3 -2
- package/src/components/PostCard.vue +13 -15
- package/src/components/ResourceAccordion/Datafair.client.vue +1 -1
- package/src/components/ResourceAccordion/Downloads.vue +19 -7
- package/src/components/ResourceAccordion/ImagePreview.client.vue +48 -0
- package/src/components/ResourceAccordion/MapContainer.client.vue +1 -8
- package/src/components/ResourceAccordion/Metadata.vue +21 -5
- package/src/components/ResourceAccordion/PdfPreview.client.vue +36 -12
- package/src/components/ResourceAccordion/PdfSkeleton.vue +44 -0
- package/src/components/ResourceAccordion/Pmtiles.client.vue +30 -19
- package/src/components/ResourceAccordion/Preview.vue +8 -4
- package/src/components/ResourceAccordion/PreviewWrapper.vue +17 -10
- package/src/components/ResourceAccordion/ResourceAccordion.vue +31 -11
- package/src/components/ResourceExplorer/ResourceExplorer.vue +105 -210
- package/src/components/ResourceExplorer/ResourceExplorerHeader.vue +79 -0
- package/src/components/ResourceExplorer/ResourceExplorerSidebar.vue +104 -71
- package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +140 -184
- package/src/components/ResourceExplorer/ResourceMainAction.vue +240 -0
- package/src/components/ResourceExplorer/ResourceSelector.vue +24 -86
- package/src/components/ResourceExplorer/ResourceViewerHeader.vue +124 -0
- package/src/components/ResourceExplorer/ResourceViewerSkeleton.vue +96 -0
- package/src/components/ResourceListItem.vue +168 -0
- package/src/components/ReuseCard.vue +18 -6
- package/src/components/ReuseDetails.vue +2 -2
- package/src/components/ReuseHorizontalCard.vue +4 -3
- package/src/components/SchemaCard.vue +10 -1
- package/src/components/Search/Filter/AccessTypeFilter.vue +2 -2
- package/src/components/Search/GlobalSearch.vue +28 -10
- package/src/components/Search/SearchInput.vue +1 -1
- package/src/components/Search/Sidemenu.vue +4 -3
- package/src/components/SmallChart.vue +11 -10
- package/src/components/StatBox.vue +13 -5
- package/src/components/TabularExplorer/TabularActiveFilters.vue +79 -0
- package/src/components/TabularExplorer/TabularCell.vue +7 -8
- package/src/components/TabularExplorer/TabularCellPopover.vue +21 -5
- package/src/components/TabularExplorer/TabularColumnsMenu.vue +112 -0
- package/src/components/TabularExplorer/TabularDateField.vue +41 -0
- package/src/components/TabularExplorer/TabularDateFilter.vue +403 -0
- package/src/components/TabularExplorer/TabularExplorer.vue +127 -780
- package/src/components/TabularExplorer/TabularFilterContent.vue +48 -38
- package/src/components/TabularExplorer/TabularFilterPopover.vue +27 -32
- 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 +484 -0
- package/src/components/TabularExplorer/TabularToolbar.vue +25 -0
- package/src/components/TabularExplorer/types.ts +40 -28
- package/src/components/TabularExplorer/useColumnMetadata.ts +98 -0
- package/src/components/TabularExplorer/useTabularContext.ts +82 -0
- package/src/components/TopicCard.vue +15 -14
- package/src/composables/useActiveDescendant.ts +5 -0
- package/src/composables/useDatasetResources.ts +164 -0
- package/src/composables/useHasTabularData.ts +2 -1
- package/src/composables/useResizable.ts +46 -0
- package/src/composables/useResourceCapabilities.ts +48 -40
- package/src/composables/useSearchFilter.ts +7 -5
- package/src/composables/useTranslation.ts +24 -9
- package/src/config.ts +1 -0
- package/src/functions/api.ts +13 -7
- package/src/functions/charts.ts +32 -1
- package/src/functions/dates.ts +26 -7
- package/src/functions/helpers.ts +15 -0
- package/src/functions/metrics.ts +57 -56
- package/src/functions/owned.ts +21 -0
- package/src/functions/paginate.ts +46 -0
- package/src/functions/resourceCapabilities.ts +34 -0
- package/src/functions/resources.ts +88 -0
- package/src/functions/schemas.ts +27 -0
- package/src/functions/scroll.ts +20 -0
- package/src/functions/tabular.ts +222 -5
- package/src/functions/tabularApi.ts +1 -43
- package/src/main.ts +32 -6
- package/src/types/dataservices.ts +15 -0
- package/src/types/organizations.ts +10 -0
- package/src/types/resources.ts +0 -1
- package/src/types/reuses.ts +12 -1
- package/src/types/search.ts +2 -2
- package/src/types/visualizations.ts +5 -0
- package/dist/Pmtiles.client-Dzm01Zfm.js +0 -25185
- package/dist/PreviewWrapper.vue_vue_type_script_setup_true_lang-BRNYswg3.js +0 -61
- /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
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- An A4 sheet on the reader backdrop with a few pulsing text lines, matching the
|
|
3
|
+
real PdfPreview framing (centered, capped ~800px, gray backdrop). -->
|
|
4
|
+
<div
|
|
5
|
+
class="overflow-hidden bg-gray-100"
|
|
6
|
+
:class="fill ? 'min-h-0 flex-1' : 'max-h-[80vh]'"
|
|
7
|
+
role="status"
|
|
8
|
+
:aria-label="t('Chargement de l\'aperçu…')"
|
|
9
|
+
>
|
|
10
|
+
<div class="flex justify-center p-4">
|
|
11
|
+
<div class="aspect-[210/297] w-[800px] max-w-full bg-white p-10 shadow-sm">
|
|
12
|
+
<div class="animate-pulse-placeholder space-y-6">
|
|
13
|
+
<div class="space-y-3">
|
|
14
|
+
<div class="h-5 w-1/2 rounded bg-gray-200" />
|
|
15
|
+
<div class="h-3 w-full rounded bg-gray-200" />
|
|
16
|
+
<div class="h-3 w-full rounded bg-gray-200" />
|
|
17
|
+
<div class="h-3 w-5/6 rounded bg-gray-200" />
|
|
18
|
+
</div>
|
|
19
|
+
<div class="space-y-3">
|
|
20
|
+
<div class="h-3 w-full rounded bg-gray-200" />
|
|
21
|
+
<div class="h-3 w-11/12 rounded bg-gray-200" />
|
|
22
|
+
<div class="h-3 w-full rounded bg-gray-200" />
|
|
23
|
+
<div class="h-3 w-3/4 rounded bg-gray-200" />
|
|
24
|
+
</div>
|
|
25
|
+
<div class="space-y-3">
|
|
26
|
+
<div class="h-3 w-full rounded bg-gray-200" />
|
|
27
|
+
<div class="h-3 w-5/6 rounded bg-gray-200" />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup lang="ts">
|
|
36
|
+
import { useTranslation } from '../../composables/useTranslation'
|
|
37
|
+
|
|
38
|
+
defineProps<{
|
|
39
|
+
// Fill the available height (fullscreen) instead of the default capped height.
|
|
40
|
+
fill?: boolean
|
|
41
|
+
}>()
|
|
42
|
+
|
|
43
|
+
const { t } = useTranslation()
|
|
44
|
+
</script>
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div>
|
|
2
|
+
<div class="flex h-full flex-col">
|
|
3
3
|
<PreviewUnavailable v-if="hasError">
|
|
4
4
|
{{ t("L'aperçu cartographique de ce fichier n'a pas pu être chargé.") }}
|
|
5
5
|
</PreviewUnavailable>
|
|
6
6
|
<div
|
|
7
7
|
v-else
|
|
8
|
-
class="-
|
|
8
|
+
class="flex min-h-0 flex-1 flex-col"
|
|
9
9
|
>
|
|
10
10
|
<div
|
|
11
11
|
v-if="pmtilesViewerUrl"
|
|
12
|
-
class="bg-blue-100 text-datagouv fr-hidden fr-unhidden-md p-4"
|
|
12
|
+
class="bg-blue-100 text-datagouv fr-hidden fr-unhidden-md p-4 shrink-0"
|
|
13
13
|
>
|
|
14
14
|
<div class="fr-grid-row fr-grid-row--middle fr-grid-row--gutters">
|
|
15
15
|
<div
|
|
@@ -35,12 +35,17 @@
|
|
|
35
35
|
</p>
|
|
36
36
|
</div>
|
|
37
37
|
</div>
|
|
38
|
-
<div
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
<div class="relative min-h-0 flex-1">
|
|
39
|
+
<div
|
|
40
|
+
ref="containerRef"
|
|
41
|
+
class="size-full"
|
|
42
|
+
/>
|
|
43
|
+
<!-- Freshness caption overlaid inside the map (bottom-left, the corner left
|
|
44
|
+
free by the navigation/geolocate controls top-right and the attribution
|
|
45
|
+
bottom-right) so the map itself can use the whole height. -->
|
|
46
|
+
<div class="pointer-events-none absolute bottom-2 left-2 z-10 rounded bg-white/90 px-2 py-1 text-[12px] leading-4 text-gray-medium shadow-sm">
|
|
47
|
+
{{ t("Aperçu de la carte mis à jour le {date}", { date: lastUpdate }) }}
|
|
48
|
+
</div>
|
|
44
49
|
</div>
|
|
45
50
|
</div>
|
|
46
51
|
</div>
|
|
@@ -62,7 +67,7 @@ import styleVector from '../../../assets/json/vector.json'
|
|
|
62
67
|
import PreviewUnavailable from './PreviewUnavailable.vue'
|
|
63
68
|
import { useTranslation } from '../../composables/useTranslation'
|
|
64
69
|
import franceSvg from './france.svg?raw'
|
|
65
|
-
import {
|
|
70
|
+
import { getMapAttribution } from '../../functions/owned'
|
|
66
71
|
|
|
67
72
|
const props = defineProps<{ resource: Resource, dataset: Dataset | DatasetV2 }>()
|
|
68
73
|
|
|
@@ -89,11 +94,7 @@ const lastUpdate = computed(() => {
|
|
|
89
94
|
|
|
90
95
|
const container = useTemplateRef('containerRef')
|
|
91
96
|
|
|
92
|
-
const attributions = computed(() =>
|
|
93
|
-
if (!props.dataset.organization && !props.dataset.owner)
|
|
94
|
-
return ''
|
|
95
|
-
return `© <a href="${getOwnerPage(props.dataset)}" target="_blank">${getOwnerName(props.dataset)}</a>`
|
|
96
|
-
})
|
|
97
|
+
const attributions = computed(() => getMapAttribution(props.dataset))
|
|
97
98
|
|
|
98
99
|
async function displayMap() {
|
|
99
100
|
await import('maplibre-gl/dist/maplibre-gl.css')
|
|
@@ -104,12 +105,22 @@ async function displayMap() {
|
|
|
104
105
|
const p = new PMTiles(pmtilesUrl.value)
|
|
105
106
|
protocol.add(p)
|
|
106
107
|
|
|
107
|
-
p.getHeader().then((h: {
|
|
108
|
+
p.getHeader().then((h: { minLon: number, minLat: number, maxLon: number, maxLat: number, centerZoom: number, centerLon: number, centerLat: number }) => {
|
|
109
|
+
// Frame the data's bounding box (carried by the archive header) so we land on
|
|
110
|
+
// the data. Fall back to the header center when the bbox is degenerate — an
|
|
111
|
+
// archive with a single feature, or bounds left unset (all zeroes).
|
|
112
|
+
const hasBounds = h.minLon < h.maxLon && h.minLat < h.maxLat
|
|
108
113
|
const map = new maplibregl.Map({
|
|
109
114
|
container: container.value!,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
// JSON import is inferred too loosely (e.g. center as number[]) to match
|
|
116
|
+
// maplibre's strict StyleSpecification, so cast through unknown.
|
|
117
|
+
style: styleVector as unknown as maplibregl.StyleSpecification,
|
|
118
|
+
...(hasBounds
|
|
119
|
+
? {
|
|
120
|
+
bounds: [[h.minLon, h.minLat], [h.maxLon, h.maxLat]] as [[number, number], [number, number]],
|
|
121
|
+
fitBoundsOptions: { padding: 24 },
|
|
122
|
+
}
|
|
123
|
+
: { center: [h.centerLon, h.centerLat] as [number, number], zoom: h.centerZoom }),
|
|
113
124
|
})
|
|
114
125
|
map.addControl(new maplibregl.NavigationControl())
|
|
115
126
|
map.addControl(
|
|
@@ -90,7 +90,11 @@
|
|
|
90
90
|
@change="changePage"
|
|
91
91
|
/>
|
|
92
92
|
<div class="fr-px-5v">
|
|
93
|
-
|
|
93
|
+
<TranslationT keypath="Dernière mise à jour de la prévisualisation : {date}">
|
|
94
|
+
<template #date>
|
|
95
|
+
<FormattedDate :date="parsingFinishedAt" />
|
|
96
|
+
</template>
|
|
97
|
+
</TranslationT> —
|
|
94
98
|
{{ t('{count} colonnes', columns.length) }} —
|
|
95
99
|
{{ t('Lignes {count}', rowCount) }}
|
|
96
100
|
</div>
|
|
@@ -103,7 +107,8 @@ import { computed, onMounted, ref } from 'vue'
|
|
|
103
107
|
import { RiArrowDownLine, RiArrowUpLine, RiExternalLinkFill } from '@remixicon/vue'
|
|
104
108
|
import Pagination from '../Pagination.vue'
|
|
105
109
|
import { getData, type SortConfig } from '../../functions/tabularApi'
|
|
106
|
-
import
|
|
110
|
+
import FormattedDate from '../FormattedDate.vue'
|
|
111
|
+
import TranslationT from '../TranslationT.vue'
|
|
107
112
|
import { trackEvent } from '../../functions/matomo'
|
|
108
113
|
import type { Resource } from '../../types/resources'
|
|
109
114
|
import { useComponentsConfig } from '../../config'
|
|
@@ -116,7 +121,6 @@ import PreviewLoader from './PreviewLoader.vue'
|
|
|
116
121
|
const props = defineProps<{ resource: Resource }>()
|
|
117
122
|
|
|
118
123
|
const { t } = useTranslation()
|
|
119
|
-
const { formatDate } = useFormatDate()
|
|
120
124
|
|
|
121
125
|
const rows = ref<Array<Record<string, unknown>>>([])
|
|
122
126
|
const columns = ref<Array<string>>([])
|
|
@@ -196,7 +200,7 @@ function sortByField(col: string) {
|
|
|
196
200
|
getTableInfos(currentPage.value, sortConfig.value)
|
|
197
201
|
};
|
|
198
202
|
|
|
199
|
-
const
|
|
203
|
+
const parsingFinishedAt = computed(() => props.resource.extras['analysis:parsing:finished_at'] as string | undefined)
|
|
200
204
|
|
|
201
205
|
onMounted(() => {
|
|
202
206
|
getTableInfos(currentPage.value)
|
|
@@ -1,29 +1,34 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
class="text-xs"
|
|
4
|
+
:class="fill ? 'flex min-h-0 flex-1 flex-col' : ''"
|
|
5
|
+
>
|
|
3
6
|
<slot
|
|
4
7
|
v-if="data !== null"
|
|
5
8
|
:data="data"
|
|
6
9
|
/>
|
|
7
|
-
<
|
|
10
|
+
<slot
|
|
8
11
|
v-else-if="loading"
|
|
9
|
-
|
|
12
|
+
name="loading"
|
|
10
13
|
>
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
<div class="text-gray-medium">
|
|
15
|
+
{{ t("Chargement de l'aperçu {fileType}...", { fileType }) }}
|
|
16
|
+
</div>
|
|
17
|
+
</slot>
|
|
13
18
|
<PreviewUnavailable v-else-if="!isSizeAllowed">
|
|
14
19
|
{{ fileSizeBytes
|
|
15
|
-
? t("Le fichier {fileType} est trop volumineux pour être prévisualisé. Téléchargez-le
|
|
16
|
-
: t("La taille du fichier est inconnue, l'aperçu n'est pas disponible. Téléchargez-le
|
|
20
|
+
? t("Le fichier {fileType} est trop volumineux pour être prévisualisé. Téléchargez-le pour le consulter.", { fileType })
|
|
21
|
+
: t("La taille du fichier est inconnue, l'aperçu n'est pas disponible. Téléchargez-le pour le consulter.")
|
|
17
22
|
}}
|
|
18
23
|
</PreviewUnavailable>
|
|
19
24
|
<PreviewUnavailable v-else-if="corsStatus === 'blocked'">
|
|
20
|
-
{{ t("Ce fichier {fileType} ne peut pas être prévisualisé car il est hébergé sur un site distant qui restreint l'accès (CORS). Téléchargez-le
|
|
25
|
+
{{ t("Ce fichier {fileType} ne peut pas être prévisualisé car il est hébergé sur un site distant qui restreint l'accès (CORS). Téléchargez-le pour le consulter.", { fileType }) }}
|
|
21
26
|
</PreviewUnavailable>
|
|
22
27
|
<PreviewUnavailable v-else-if="error === 'network'">
|
|
23
|
-
{{ t("Ce fichier est hébergé sur un site externe qui ne permet pas la prévisualisation. Téléchargez-le
|
|
28
|
+
{{ t("Ce fichier est hébergé sur un site externe qui ne permet pas la prévisualisation. Téléchargez-le pour le consulter.") }}
|
|
24
29
|
</PreviewUnavailable>
|
|
25
30
|
<PreviewUnavailable v-else-if="error">
|
|
26
|
-
{{ t("L'aperçu de ce fichier n'a pas pu être chargé. Téléchargez-le
|
|
31
|
+
{{ t("L'aperçu de ce fichier n'a pas pu être chargé. Téléchargez-le pour le consulter.") }}
|
|
27
32
|
</PreviewUnavailable>
|
|
28
33
|
</div>
|
|
29
34
|
</template>
|
|
@@ -40,6 +45,8 @@ const props = defineProps<{
|
|
|
40
45
|
resource: Resource
|
|
41
46
|
maxSize: number | undefined
|
|
42
47
|
load: () => Promise<unknown>
|
|
48
|
+
// Fill the available height (fullscreen) instead of sizing to content.
|
|
49
|
+
fill?: boolean
|
|
43
50
|
}>()
|
|
44
51
|
|
|
45
52
|
const emit = defineEmits<{
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
<CopyButton
|
|
48
48
|
:label="t('Copier le lien')"
|
|
49
49
|
:copied-label="t('Lien copié !')"
|
|
50
|
-
:text="
|
|
50
|
+
:text="externalUrl"
|
|
51
51
|
class="z-2"
|
|
52
52
|
/>
|
|
53
53
|
</div>
|
|
@@ -60,7 +60,17 @@
|
|
|
60
60
|
aria-hidden="true"
|
|
61
61
|
class="size-3 fill-gray-medium"
|
|
62
62
|
/>
|
|
63
|
-
<
|
|
63
|
+
<TranslationT
|
|
64
|
+
class="text-xs mb-0"
|
|
65
|
+
keypath="Mis à jour {date}"
|
|
66
|
+
>
|
|
67
|
+
<template #date>
|
|
68
|
+
<FormattedDate
|
|
69
|
+
:date="lastUpdate"
|
|
70
|
+
format="relative"
|
|
71
|
+
/>
|
|
72
|
+
</template>
|
|
73
|
+
</TranslationT>
|
|
64
74
|
<RiSubtractLine
|
|
65
75
|
aria-hidden="true"
|
|
66
76
|
class="size-3 fill-gray-medium"
|
|
@@ -197,7 +207,10 @@
|
|
|
197
207
|
:key="tab.key"
|
|
198
208
|
class="px-4"
|
|
199
209
|
>
|
|
200
|
-
<div
|
|
210
|
+
<div
|
|
211
|
+
v-if="tab.key === 'map'"
|
|
212
|
+
class="-mx-4 h-[600px]"
|
|
213
|
+
>
|
|
201
214
|
<Pmtiles
|
|
202
215
|
v-if="hasPmtiles"
|
|
203
216
|
:resource="resource"
|
|
@@ -224,6 +237,10 @@
|
|
|
224
237
|
v-else-if="resource.format && resource.format.toLowerCase() === 'xml'"
|
|
225
238
|
:resource="resource"
|
|
226
239
|
/>
|
|
240
|
+
<ImagePreview
|
|
241
|
+
v-else-if="isImagePreviewFormat(resource.format)"
|
|
242
|
+
:resource="resource"
|
|
243
|
+
/>
|
|
227
244
|
<!-- Show Datafair embedded preview (koumoul) -->
|
|
228
245
|
<DatafairPreview
|
|
229
246
|
v-else-if="hasDatafairPreview"
|
|
@@ -271,7 +288,7 @@
|
|
|
271
288
|
/>
|
|
272
289
|
</div>
|
|
273
290
|
<div
|
|
274
|
-
v-if="tab.key === '
|
|
291
|
+
v-if="tab.key === 'api'"
|
|
275
292
|
>
|
|
276
293
|
<div class="fr-mb-4w">
|
|
277
294
|
<p>{{ t("Cette API est générée automatiquement par {platform} à partir du fichier.", { platform: config.name }) }}</p>
|
|
@@ -296,7 +313,8 @@ import { ref, computed, defineAsyncComponent } from 'vue'
|
|
|
296
313
|
import { RiDownloadLine, RiFileCopyLine, RiFileWarningLine, RiSubtractLine } from '@remixicon/vue'
|
|
297
314
|
import OrganizationNameWithCertificate from '../OrganizationNameWithCertificate.vue'
|
|
298
315
|
import { filesize, summarize } from '../../functions/helpers'
|
|
299
|
-
import
|
|
316
|
+
import FormattedDate from '../FormattedDate.vue'
|
|
317
|
+
import TranslationT from '../TranslationT.vue'
|
|
300
318
|
import MarkdownViewer from '../MarkdownViewer.vue'
|
|
301
319
|
import type { CommunityResource, Resource } from '../../types/resources'
|
|
302
320
|
import type { Dataset, DatasetV2 } from '../../types/datasets'
|
|
@@ -309,7 +327,7 @@ import { trackEvent } from '../../functions/matomo'
|
|
|
309
327
|
import CopyButton from '../CopyButton.vue'
|
|
310
328
|
import { useComponentsConfig } from '../../config'
|
|
311
329
|
import { getOwnerName } from '../../functions/owned'
|
|
312
|
-
import { getResourceFormatIcon, getResourceTitleId, detectOgcService,
|
|
330
|
+
import { getResourceFormatIcon, getResourceTitleId, detectOgcService, getResourceFilesize, isImagePreviewFormat, resolveResourceExternalUrl } from '../../functions/resources'
|
|
313
331
|
import BrandedButton from '../BrandedButton.vue'
|
|
314
332
|
import { useTranslation } from '../../composables/useTranslation'
|
|
315
333
|
import { useHasTabularData } from '../../composables/useHasTabularData'
|
|
@@ -331,6 +349,8 @@ const props = withDefaults(defineProps<{
|
|
|
331
349
|
isCommunityResource?: boolean
|
|
332
350
|
resource: Resource | CommunityResource
|
|
333
351
|
canEdit?: boolean
|
|
352
|
+
// Overrides the "Copier le lien" target.
|
|
353
|
+
resourceExternalUrl?: (resource: Resource | CommunityResource) => string
|
|
334
354
|
}>(), {
|
|
335
355
|
expandedOnMount: false,
|
|
336
356
|
isCommunityResource: false,
|
|
@@ -344,18 +364,18 @@ const Pmtiles = defineAsyncComponent(() => import('./Pmtiles.client.vue'))
|
|
|
344
364
|
const JsonPreview = defineAsyncComponent(() => import('./JsonPreview.client.vue'))
|
|
345
365
|
const PdfPreview = defineAsyncComponent(() => import('./PdfPreview.client.vue'))
|
|
346
366
|
const XmlPreview = defineAsyncComponent(() => import('./XmlPreview.client.vue'))
|
|
367
|
+
const ImagePreview = defineAsyncComponent(() => import('./ImagePreview.client.vue'))
|
|
347
368
|
const DatafairPreview = defineAsyncComponent(() => import('./Datafair.client.vue'))
|
|
348
369
|
|
|
349
370
|
const { t } = useTranslation()
|
|
350
|
-
const { formatRelativeIfRecentDate } = useFormatDate()
|
|
351
371
|
const checkTabularData = useHasTabularData()
|
|
352
372
|
|
|
353
373
|
const hasPreview = computed(() => {
|
|
354
|
-
// For JSON, PDF, and
|
|
374
|
+
// For JSON, PDF, XML, and image files, show preview.
|
|
355
375
|
// We cannot check for CORS issues here because we cannot use an async component here.
|
|
356
376
|
// If there is a CORS issue when fetching the file for preview, it will be managed and displayed as an error banner by the preview component.
|
|
357
377
|
const format = props.resource.format?.toLowerCase()
|
|
358
|
-
return format === 'json' || format === 'pdf' || format === 'xml'
|
|
378
|
+
return format === 'json' || format === 'pdf' || format === 'xml' || isImagePreviewFormat(format)
|
|
359
379
|
})
|
|
360
380
|
|
|
361
381
|
const hasTabularData = computed(() => checkTabularData(props.resource))
|
|
@@ -417,7 +437,7 @@ const tabsOptions = computed(() => {
|
|
|
417
437
|
options.push({ key: 'downloads', label: t('Téléchargements') })
|
|
418
438
|
|
|
419
439
|
if (hasTabularData.value) {
|
|
420
|
-
options.push({ key: '
|
|
440
|
+
options.push({ key: 'api', label: t('API') })
|
|
421
441
|
}
|
|
422
442
|
|
|
423
443
|
return options
|
|
@@ -450,7 +470,7 @@ const resourceFilesize = computed(() => getResourceFilesize(props.resource))
|
|
|
450
470
|
const unavailable = availabilityChecked && props.resource.extras['check:available'] === false
|
|
451
471
|
const downloadButtonTitle = unavailable ? t(`Le robot de {certifier} n'a pas pu accéder à ce fichier - Télécharger le fichier en {format}`, { certifier: config.name, format: format.value }) : t(`Télécharger le fichier en {format}`, { format: format.value })
|
|
452
472
|
|
|
453
|
-
const
|
|
473
|
+
const externalUrl = computed(() => resolveResourceExternalUrl(props.dataset, props.resource, props.resourceExternalUrl))
|
|
454
474
|
|
|
455
475
|
const resourceContentId = 'resource-' + props.resource.id
|
|
456
476
|
const resourceHeaderId = 'resource-' + props.resource.id + '-header'
|