@datagouv/components-next 1.0.2-dev.15 → 1.0.2-dev.150
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-BtQqFBTQ.js → Datafair.client-BRgmha-O.js} +4 -4
- package/dist/{Event--kp8kMdJ.js → Event-iXDrp-fy.js} +31 -31
- package/dist/{Image-34hvypZI.js → Image-mNMBOofs.js} +9 -9
- package/dist/JsonPreview.client-Cs87KC-b.js +40 -0
- package/dist/{Map-BjUnLyj8.js → Map-BfqKMw0S.js} +1739 -1599
- package/dist/{MapContainer.client-DB6p6KF8.js → MapContainer.client-DLJuagQI.js} +38 -39
- package/dist/{OSM-s40W6sQ2.js → OSM-CcrUhItr.js} +15 -23
- package/dist/{PdfPreview.client-Dr4VcIur.js → PdfPreview.client-BI90pK6j.js} +823 -855
- package/dist/Pmtiles.client-bJzz1c3r.js +26142 -0
- package/dist/PreviewWrapper.vue_vue_type_script_setup_true_lang-DS_Tvpti.js +61 -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-L8ZyxNdY.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 +174 -142
- package/dist/components.css +2 -2
- package/dist/{index-D3rieddI.js → index-sr5KjUOp.js} +21147 -15136
- package/dist/{leaflet-src-7m1mB8LI.js → leaflet-src-D34jQJmR.js} +185 -187
- package/dist/main-CWzZZ7OE.js +73583 -0
- 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-CJXShyPB.js → vue3-xml-viewer.common-9Hozz8ne.js} +16 -16
- package/package.json +50 -36
- package/src/chart.ts +5 -0
- package/src/components/ActivityList/ActivityList.vue +3 -2
- package/src/components/Chart/ChartViewer.vue +233 -0
- package/src/components/Chart/ChartViewerWrapper.vue +182 -0
- package/src/components/DataserviceCard.vue +3 -0
- package/src/components/DatasetCard.vue +9 -4
- 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/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 +8 -5
- package/src/components/RadioInput.vue +7 -2
- package/src/components/ResourceAccordion/DataStructure.vue +11 -33
- package/src/components/ResourceAccordion/Downloads.vue +160 -0
- package/src/components/ResourceAccordion/JsonPreview.client.vue +23 -104
- package/src/components/ResourceAccordion/MapContainer.client.vue +1 -3
- package/src/components/ResourceAccordion/Metadata.vue +1 -2
- package/src/components/ResourceAccordion/PdfPreview.client.vue +24 -87
- package/src/components/ResourceAccordion/Pmtiles.client.vue +3 -1
- package/src/components/ResourceAccordion/Preview.vue +11 -11
- package/src/components/ResourceAccordion/PreviewWrapper.vue +82 -0
- package/src/components/ResourceAccordion/ResourceAccordion.vue +12 -111
- package/src/components/ResourceAccordion/XmlPreview.client.vue +16 -98
- package/src/components/ResourceExplorer/ResourceExplorer.vue +22 -19
- package/src/components/ResourceExplorer/ResourceExplorerSidebar.vue +2 -2
- package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +57 -148
- package/src/components/ResourceExplorer/ResourceSelector.vue +113 -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/Search/GlobalSearch.vue +219 -117
- package/src/components/Search/SearchInput.vue +5 -4
- package/src/components/TabularExplorer/TabularCell.vue +51 -0
- package/src/components/TabularExplorer/TabularCellPopover.vue +170 -0
- package/src/components/TabularExplorer/TabularExplorer.vue +1132 -0
- package/src/components/TabularExplorer/TabularFilterContent.vue +353 -0
- package/src/components/TabularExplorer/TabularFilterPopover.vue +106 -0
- package/src/components/TabularExplorer/types.ts +86 -0
- package/src/composables/useHasTabularData.ts +15 -1
- package/src/composables/useMetrics.ts +1 -1
- package/src/composables/useResourceCapabilities.ts +22 -9
- package/src/composables/useSearchFilter.ts +120 -0
- package/src/composables/useStableQueryParams.ts +38 -6
- package/src/composables/useTabularProfile.ts +70 -0
- package/src/config.ts +20 -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 +79 -0
- package/src/functions/datasets.ts +0 -17
- package/src/functions/dates.ts +3 -2
- package/src/functions/metrics.ts +6 -4
- package/src/functions/resources.ts +110 -1
- package/src/functions/tabular.ts +171 -0
- package/src/functions/tabularApi.ts +96 -11
- package/src/main.ts +97 -10
- package/src/types/dataservices.ts +2 -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-DojrmGyU.js +0 -72
- package/dist/Pmtiles.client-DbMjfK00.js +0 -25185
- package/dist/Swagger.client-Db_xYKJv.js +0 -4
- package/dist/XmlPreview.client-CHxSFhd7.js +0 -64
- package/dist/main-D70Xt4a7.js +0 -105854
- package/src/components/ResourceAccordion/Swagger.client.vue +0 -48
- package/src/functions/pagination.ts +0 -9
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
:key="selectedResource.id"
|
|
8
8
|
:dataset
|
|
9
9
|
:resource="selectedResource"
|
|
10
|
+
:resources="flatResources"
|
|
11
|
+
@select="selectResource"
|
|
10
12
|
/>
|
|
11
13
|
<div
|
|
12
14
|
v-else-if="search"
|
|
@@ -30,16 +32,18 @@
|
|
|
30
32
|
</BrandedButton>
|
|
31
33
|
</div>
|
|
32
34
|
</div>
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
<div class="hidden md:block">
|
|
36
|
+
<ResourceExplorerSidebar
|
|
37
|
+
:resources="allResources"
|
|
38
|
+
:selected-resource-id="selectedResource?.id ?? null"
|
|
39
|
+
:collapsed="sidebarCollapsed"
|
|
40
|
+
:search
|
|
41
|
+
@select="selectResource"
|
|
42
|
+
@load-more="loadMore"
|
|
43
|
+
@update:collapsed="sidebarCollapsed = $event"
|
|
44
|
+
@update:search="updateSearch($event)"
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
43
47
|
</div>
|
|
44
48
|
</div>
|
|
45
49
|
<div
|
|
@@ -67,7 +71,7 @@ import { useComponentsConfig } from '../../config'
|
|
|
67
71
|
import { useTranslation } from '../../composables/useTranslation'
|
|
68
72
|
import { useDebouncedRef } from '../../composables/useDebouncedRef'
|
|
69
73
|
import { useFetch } from '../../functions/api'
|
|
70
|
-
import { RESOURCE_TYPE } from '../../functions/resources'
|
|
74
|
+
import { RESOURCE_EXPLORER_PAGE_SIZE, RESOURCE_TYPE } from '../../functions/resources'
|
|
71
75
|
import type { PaginatedArray } from '../../types/api'
|
|
72
76
|
import type { DatasetV2 } from '../../types/datasets'
|
|
73
77
|
import type { Resource, ResourceGroup, ResourceType } from '../../types/resources'
|
|
@@ -90,7 +94,6 @@ const sidebarCollapsed = ref(false)
|
|
|
90
94
|
const search = ref('')
|
|
91
95
|
const { debounced: searchDebounced, flush } = useDebouncedRef(search, config.searchDebounce ?? 300)
|
|
92
96
|
|
|
93
|
-
const PAGE_SIZE = 10
|
|
94
97
|
const url = computed(() => `/api/2/datasets/${props.dataset.id}/resources/`)
|
|
95
98
|
|
|
96
99
|
// Resource ID from URL query
|
|
@@ -99,32 +102,32 @@ const resourceIdQuery = useRouteQuery<string | undefined>('resource_id')
|
|
|
99
102
|
// Fetch resources for each type
|
|
100
103
|
const mainParams = computed(() => ({
|
|
101
104
|
type: 'main' as const,
|
|
102
|
-
page_size:
|
|
105
|
+
page_size: RESOURCE_EXPLORER_PAGE_SIZE,
|
|
103
106
|
q: searchDebounced.value || undefined,
|
|
104
107
|
}))
|
|
105
108
|
const documentationParams = computed(() => ({
|
|
106
109
|
type: 'documentation' as const,
|
|
107
|
-
page_size:
|
|
110
|
+
page_size: RESOURCE_EXPLORER_PAGE_SIZE,
|
|
108
111
|
q: searchDebounced.value || undefined,
|
|
109
112
|
}))
|
|
110
113
|
const updateParams = computed(() => ({
|
|
111
114
|
type: 'update' as const,
|
|
112
|
-
page_size:
|
|
115
|
+
page_size: RESOURCE_EXPLORER_PAGE_SIZE,
|
|
113
116
|
q: searchDebounced.value || undefined,
|
|
114
117
|
}))
|
|
115
118
|
const apiParams = computed(() => ({
|
|
116
119
|
type: 'api' as const,
|
|
117
|
-
page_size:
|
|
120
|
+
page_size: RESOURCE_EXPLORER_PAGE_SIZE,
|
|
118
121
|
q: searchDebounced.value || undefined,
|
|
119
122
|
}))
|
|
120
123
|
const codeParams = computed(() => ({
|
|
121
124
|
type: 'code' as const,
|
|
122
|
-
page_size:
|
|
125
|
+
page_size: RESOURCE_EXPLORER_PAGE_SIZE,
|
|
123
126
|
q: searchDebounced.value || undefined,
|
|
124
127
|
}))
|
|
125
128
|
const otherParams = computed(() => ({
|
|
126
129
|
type: 'other' as const,
|
|
127
|
-
page_size:
|
|
130
|
+
page_size: RESOURCE_EXPLORER_PAGE_SIZE,
|
|
128
131
|
q: searchDebounced.value || undefined,
|
|
129
132
|
}))
|
|
130
133
|
|
|
@@ -165,7 +168,7 @@ const loadMoreType = ref<ResourceType>('main')
|
|
|
165
168
|
const loadMorePage = ref(1)
|
|
166
169
|
const loadMoreParams = computed(() => ({
|
|
167
170
|
type: loadMoreType.value,
|
|
168
|
-
page_size:
|
|
171
|
+
page_size: RESOURCE_EXPLORER_PAGE_SIZE,
|
|
169
172
|
page: loadMorePage.value,
|
|
170
173
|
q: searchDebounced.value || undefined,
|
|
171
174
|
}))
|
|
@@ -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"
|
|
@@ -2,24 +2,31 @@
|
|
|
2
2
|
<div class="border border-gray-default">
|
|
3
3
|
<header class="p-4 flex flex-wrap md:flex-nowrap gap-4 items-center justify-between">
|
|
4
4
|
<div>
|
|
5
|
-
<div class="flex items-center mb-1">
|
|
5
|
+
<div class="flex items-center gap-1 mb-1">
|
|
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>
|
|
13
|
+
<ResourceSelector
|
|
14
|
+
v-if="resources && resources.length > 1"
|
|
15
|
+
:resources
|
|
16
|
+
:selected-id="resource.id"
|
|
17
|
+
@select="emit('select', $event)"
|
|
18
|
+
/>
|
|
13
19
|
<CopyButton
|
|
14
20
|
:label="t('Copier le lien')"
|
|
15
21
|
:copied-label="t('Lien copié !')"
|
|
16
22
|
:text="resourceExternalUrl"
|
|
23
|
+
class="hidden md:inline-flex"
|
|
17
24
|
/>
|
|
18
25
|
</div>
|
|
19
|
-
<div class="text-gray-medium text-xs flex items-center gap-1">
|
|
26
|
+
<div class="text-gray-medium text-xs flex items-center gap-1 flex-wrap">
|
|
20
27
|
<SchemaBadge :resource />
|
|
21
28
|
<RiSubtractLine
|
|
22
|
-
v-if="resource.schema"
|
|
29
|
+
v-if="resource.schema?.name || resource.schema?.url"
|
|
23
30
|
aria-hidden="true"
|
|
24
31
|
class="size-3 fill-gray-medium"
|
|
25
32
|
/>
|
|
@@ -115,6 +122,11 @@
|
|
|
115
122
|
v-if="ogcWms"
|
|
116
123
|
:resource="resource"
|
|
117
124
|
/>
|
|
125
|
+
<PreviewUnavailable v-if="!hasPmtiles && !ogcWms && hasPmtilesError">
|
|
126
|
+
{{ t("La carte n'a pas pu être générée automatiquement pour ce fichier.") }}
|
|
127
|
+
<br>
|
|
128
|
+
<span class="text-gray-medium text-xs">{{ pmtilesError }}</span>
|
|
129
|
+
</PreviewUnavailable>
|
|
118
130
|
</div>
|
|
119
131
|
<div v-if="tab.key === 'data'">
|
|
120
132
|
<JsonPreview
|
|
@@ -134,16 +146,27 @@
|
|
|
134
146
|
:resource="resource"
|
|
135
147
|
:dataset="dataset"
|
|
136
148
|
/>
|
|
137
|
-
<
|
|
149
|
+
<OpenApiViewer
|
|
138
150
|
v-else-if="hasOpenAPIPreview"
|
|
139
151
|
:url="resource.extras['apidocUrl'] as string"
|
|
140
152
|
/>
|
|
141
|
-
<
|
|
153
|
+
<TabularExplorer
|
|
142
154
|
v-else-if="hasTabularData"
|
|
143
|
-
:resource="resource"
|
|
155
|
+
:resource-id="resource.id"
|
|
144
156
|
/>
|
|
145
157
|
<PreviewUnavailable v-else>
|
|
146
|
-
|
|
158
|
+
<!-- "File too large to download" is the only analysis:error value from hydra for now -->
|
|
159
|
+
<template v-if="resource.extras['analysis:error'] === 'File too large to download'">
|
|
160
|
+
{{ t("Ce fichier est trop volumineux pour être analysé et prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.") }}
|
|
161
|
+
</template>
|
|
162
|
+
<template v-else-if="resource.extras['analysis:parsing:error']">
|
|
163
|
+
{{ 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.") }}
|
|
164
|
+
<br>
|
|
165
|
+
<span class="text-gray-medium text-xs">{{ resource.extras['analysis:parsing:error'] }}</span>
|
|
166
|
+
</template>
|
|
167
|
+
<template v-else>
|
|
168
|
+
{{ t("Ce fichier ne peut pas être prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.") }}
|
|
169
|
+
</template>
|
|
147
170
|
</PreviewUnavailable>
|
|
148
171
|
</div>
|
|
149
172
|
<div v-if="tab.key === 'description'">
|
|
@@ -162,137 +185,22 @@
|
|
|
162
185
|
<Metadata :resource />
|
|
163
186
|
</div>
|
|
164
187
|
<div v-if="tab.key === 'downloads'">
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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>
|
|
188
|
+
<Downloads
|
|
189
|
+
:resource="resource"
|
|
190
|
+
:dataset="dataset"
|
|
191
|
+
/>
|
|
287
192
|
</div>
|
|
288
|
-
<div v-if="tab.key === '
|
|
193
|
+
<div v-if="tab.key === 'api'">
|
|
289
194
|
<div class="fr-mb-4w">
|
|
290
195
|
<p>{{ t("Cette API est générée automatiquement par {platform} à partir du fichier.", { platform: config.name }) }}</p>
|
|
291
196
|
<p>{{ t("- Si le fichier est modifié, l'API sera mise à jour et sa structure pourra changer.") }}</p>
|
|
292
197
|
<p>{{ t("- Si le fichier est supprimé, l'API sera également supprimée.") }}</p>
|
|
293
198
|
<p>{{ t("Pour des usages pérennes, prévoyez que cette API dépend directement du fichier source.") }}</p>
|
|
199
|
+
<p v-if="config.tabularApiUrl">
|
|
200
|
+
{{ t("L'URL de base de l'API est {url}", { url: config.tabularApiUrl }) }}
|
|
201
|
+
</p>
|
|
294
202
|
</div>
|
|
295
|
-
<
|
|
203
|
+
<OpenApiViewer
|
|
296
204
|
v-if="hasTabularData"
|
|
297
205
|
:url="`${config.tabularApiUrl}/api/resources/${resource.id}/swagger/`"
|
|
298
206
|
/>
|
|
@@ -306,32 +214,33 @@
|
|
|
306
214
|
|
|
307
215
|
<script setup lang="ts">
|
|
308
216
|
import { computed, defineAsyncComponent } from 'vue'
|
|
309
|
-
import { RiDownloadLine, RiFileCopyLine, RiFileWarningLine,
|
|
217
|
+
import { RiDownloadLine, RiFileCopyLine, RiFileWarningLine, RiSubtractLine } from '@remixicon/vue'
|
|
310
218
|
import PreviewUnavailable from '../ResourceAccordion/PreviewUnavailable.vue'
|
|
311
219
|
import { toast } from 'vue-sonner'
|
|
312
220
|
import BrandedButton from '../BrandedButton.vue'
|
|
313
221
|
import CopyButton from '../CopyButton.vue'
|
|
314
222
|
import MarkdownViewer from '../MarkdownViewer.vue'
|
|
315
223
|
import ResourceIcon from '../ResourceAccordion/ResourceIcon.vue'
|
|
316
|
-
import
|
|
224
|
+
import OpenApiViewer from '../OpenApiViewer/OpenApiViewer.vue'
|
|
317
225
|
import TabGroup from '../Tabs/TabGroup.vue'
|
|
318
226
|
import TabList from '../Tabs/TabList.vue'
|
|
319
227
|
import Tab from '../Tabs/Tab.vue'
|
|
320
228
|
import TabPanels from '../Tabs/TabPanels.vue'
|
|
321
229
|
import TabPanel from '../Tabs/TabPanel.vue'
|
|
322
|
-
import
|
|
323
|
-
import Preview from '../ResourceAccordion/Preview.vue'
|
|
230
|
+
import TabularExplorer from '../TabularExplorer/TabularExplorer.vue'
|
|
324
231
|
import DataStructure from '../ResourceAccordion/DataStructure.vue'
|
|
232
|
+
import Downloads from '../ResourceAccordion/Downloads.vue'
|
|
325
233
|
import Metadata from '../ResourceAccordion/Metadata.vue'
|
|
326
234
|
import SchemaBadge from '../ResourceAccordion/SchemaBadge.vue'
|
|
235
|
+
import ResourceSelector from './ResourceSelector.vue'
|
|
327
236
|
import { filesize, summarize } from '../../functions/helpers'
|
|
328
|
-
import { getResourceFormatIcon } from '../../functions/resources'
|
|
329
|
-
import { getResourceExternalUrl, getResourceFilesize } from '../../functions/datasets'
|
|
237
|
+
import { getResourceFormatIcon, getResourceExternalUrl, getResourceFilesize } from '../../functions/resources'
|
|
330
238
|
import { trackEvent } from '../../functions/matomo'
|
|
331
239
|
import { useComponentsConfig } from '../../config'
|
|
332
240
|
import { useFormatDate } from '../../functions/dates'
|
|
333
241
|
import { useTranslation } from '../../composables/useTranslation'
|
|
334
242
|
import { useResourceCapabilities } from '../../composables/useResourceCapabilities'
|
|
243
|
+
import { provideTabularProfile } from '../../composables/useTabularProfile'
|
|
335
244
|
import type { Resource } from '../../types/resources'
|
|
336
245
|
import type { Dataset, DatasetV2 } from '../../types/datasets'
|
|
337
246
|
|
|
@@ -353,13 +262,15 @@ const MapContainer = defineAsyncComponent(() =>
|
|
|
353
262
|
const Pmtiles = defineAsyncComponent(() =>
|
|
354
263
|
import('../ResourceAccordion/Pmtiles.client.vue'),
|
|
355
264
|
)
|
|
356
|
-
const SwaggerClient = defineAsyncComponent(() =>
|
|
357
|
-
import('../ResourceAccordion/Swagger.client.vue'),
|
|
358
|
-
)
|
|
359
265
|
|
|
360
266
|
const props = defineProps<{
|
|
361
267
|
dataset: Dataset | DatasetV2
|
|
362
268
|
resource: Resource
|
|
269
|
+
resources?: Resource[]
|
|
270
|
+
}>()
|
|
271
|
+
|
|
272
|
+
const emit = defineEmits<{
|
|
273
|
+
select: [resource: Resource]
|
|
363
274
|
}>()
|
|
364
275
|
|
|
365
276
|
const { t } = useTranslation()
|
|
@@ -369,17 +280,19 @@ const { formatRelativeIfRecentDate } = useFormatDate()
|
|
|
369
280
|
const {
|
|
370
281
|
hasTabularData,
|
|
371
282
|
hasPmtiles,
|
|
283
|
+
hasPmtilesError,
|
|
284
|
+
pmtilesError,
|
|
372
285
|
hasDatafairPreview,
|
|
373
286
|
hasOpenAPIPreview,
|
|
374
287
|
ogcService,
|
|
375
288
|
ogcWms,
|
|
376
|
-
generatedFormats,
|
|
377
|
-
wfsFormats,
|
|
378
|
-
defaultWfsProjection,
|
|
379
289
|
isResourceUrl,
|
|
380
290
|
tabsOptions,
|
|
381
291
|
} = useResourceCapabilities(() => props.resource, () => props.dataset)
|
|
382
292
|
|
|
293
|
+
// Share the tabular profile fetch between TabularExplorer and DataStructure tabs.
|
|
294
|
+
await provideTabularProfile(() => props.resource.id)
|
|
295
|
+
|
|
383
296
|
const resourceFilesize = computed(() => getResourceFilesize(props.resource))
|
|
384
297
|
const resourceExternalUrl = computed(() => getResourceExternalUrl(props.dataset, props.resource))
|
|
385
298
|
|
|
@@ -393,10 +306,6 @@ const downloadButtonTitle = computed(() => {
|
|
|
393
306
|
return t('Télécharger le fichier en {format}', { format: format.value })
|
|
394
307
|
})
|
|
395
308
|
|
|
396
|
-
const conversionsLastUpdate = computed(() =>
|
|
397
|
-
formatRelativeIfRecentDate(props.resource.extras['analysis:parsing:finished_at'] as string | undefined),
|
|
398
|
-
)
|
|
399
|
-
|
|
400
309
|
const copyResourceUrl = async () => {
|
|
401
310
|
try {
|
|
402
311
|
await navigator.clipboard.writeText(props.resource.url)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Popover
|
|
3
|
+
v-slot="{ open, close }"
|
|
4
|
+
class="relative inline-block"
|
|
5
|
+
>
|
|
6
|
+
<slot
|
|
7
|
+
name="trigger"
|
|
8
|
+
:open="open"
|
|
9
|
+
>
|
|
10
|
+
<PopoverButton
|
|
11
|
+
class="inline-flex items-center justify-center size-6 rounded text-gray-plain hover:bg-gray-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-new-primary"
|
|
12
|
+
:aria-label="t('Choisir une autre ressource')"
|
|
13
|
+
>
|
|
14
|
+
<RiArrowDownSLine
|
|
15
|
+
class="size-4"
|
|
16
|
+
:class="{ 'rotate-180': open }"
|
|
17
|
+
aria-hidden="true"
|
|
18
|
+
/>
|
|
19
|
+
</PopoverButton>
|
|
20
|
+
</slot>
|
|
21
|
+
<PopoverPanel
|
|
22
|
+
:class="searchable
|
|
23
|
+
? 'absolute left-0 top-full z-50 mt-2 w-96 max-w-[calc(100vw-2rem)] bg-white border border-gray-default rounded shadow-lg p-3 space-y-2'
|
|
24
|
+
: 'absolute left-0 top-full z-50 mt-1 w-80 max-h-96 overflow-auto bg-white border border-gray-default rounded shadow-lg p-1'"
|
|
25
|
+
>
|
|
26
|
+
<input
|
|
27
|
+
v-if="searchable"
|
|
28
|
+
v-model="searchQuery"
|
|
29
|
+
type="search"
|
|
30
|
+
class="w-full border border-gray-default rounded px-2.5 py-1.5 text-sm"
|
|
31
|
+
:placeholder="t('Rechercher dans les ressources…')"
|
|
32
|
+
>
|
|
33
|
+
<ul
|
|
34
|
+
v-if="filteredResources.length > 0"
|
|
35
|
+
class="list-none p-0 m-0 space-y-0.5 max-h-80 overflow-y-auto"
|
|
36
|
+
>
|
|
37
|
+
<li
|
|
38
|
+
v-for="r in filteredResources"
|
|
39
|
+
:key="r.id"
|
|
40
|
+
>
|
|
41
|
+
<button
|
|
42
|
+
v-if="!isDisabled?.(r)"
|
|
43
|
+
type="button"
|
|
44
|
+
class="flex items-center gap-1.5 w-full text-left px-2 py-1.5 rounded text-sm hover:bg-gray-100 focus:outline-none focus-visible:bg-gray-100"
|
|
45
|
+
:class="{ 'font-bold bg-blue-50 text-new-primary': r.id === selectedId }"
|
|
46
|
+
@click="emit('select', r); close()"
|
|
47
|
+
>
|
|
48
|
+
<ResourceIcon
|
|
49
|
+
:resource="r"
|
|
50
|
+
class="size-3.5 shrink-0"
|
|
51
|
+
/>
|
|
52
|
+
<span class="truncate">{{ r.title || t('Fichier sans nom') }}</span>
|
|
53
|
+
<span
|
|
54
|
+
v-if="r.format"
|
|
55
|
+
class="ml-auto text-xs text-gray-medium uppercase shrink-0"
|
|
56
|
+
>
|
|
57
|
+
{{ r.format }}
|
|
58
|
+
</span>
|
|
59
|
+
</button>
|
|
60
|
+
<div
|
|
61
|
+
v-else
|
|
62
|
+
class="flex items-center gap-1.5 px-2 py-1.5 rounded text-sm text-gray-medium cursor-not-allowed"
|
|
63
|
+
:title="disabledTitle"
|
|
64
|
+
>
|
|
65
|
+
<ResourceIcon
|
|
66
|
+
:resource="r"
|
|
67
|
+
class="size-3.5 shrink-0 opacity-50"
|
|
68
|
+
/>
|
|
69
|
+
<span class="truncate opacity-70">{{ r.title || t('Fichier sans nom') }}</span>
|
|
70
|
+
</div>
|
|
71
|
+
</li>
|
|
72
|
+
</ul>
|
|
73
|
+
<p
|
|
74
|
+
v-else
|
|
75
|
+
class="text-sm text-gray-medium italic mb-0 px-2 py-2"
|
|
76
|
+
>
|
|
77
|
+
{{ t('Aucune ressource correspondante') }}
|
|
78
|
+
</p>
|
|
79
|
+
</PopoverPanel>
|
|
80
|
+
</Popover>
|
|
81
|
+
</template>
|
|
82
|
+
|
|
83
|
+
<script setup lang="ts">
|
|
84
|
+
import { computed, ref } from 'vue'
|
|
85
|
+
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
|
|
86
|
+
import { RiArrowDownSLine } from '@remixicon/vue'
|
|
87
|
+
import { useTranslation } from '../../composables/useTranslation'
|
|
88
|
+
import ResourceIcon from '../ResourceAccordion/ResourceIcon.vue'
|
|
89
|
+
import type { Resource } from '../../types/resources'
|
|
90
|
+
|
|
91
|
+
const props = defineProps<{
|
|
92
|
+
resources: Resource[]
|
|
93
|
+
selectedId: string
|
|
94
|
+
searchable?: boolean
|
|
95
|
+
isDisabled?: (resource: Resource) => boolean
|
|
96
|
+
disabledTitle?: string
|
|
97
|
+
}>()
|
|
98
|
+
|
|
99
|
+
const emit = defineEmits<{
|
|
100
|
+
select: [resource: Resource]
|
|
101
|
+
}>()
|
|
102
|
+
|
|
103
|
+
const { t } = useTranslation()
|
|
104
|
+
|
|
105
|
+
const searchQuery = ref('')
|
|
106
|
+
|
|
107
|
+
const filteredResources = computed(() => {
|
|
108
|
+
if (!props.searchable) return props.resources
|
|
109
|
+
const q = searchQuery.value.trim().toLowerCase()
|
|
110
|
+
if (!q) return props.resources
|
|
111
|
+
return props.resources.filter(r => (r.title ?? '').toLowerCase().includes(q))
|
|
112
|
+
})
|
|
113
|
+
</script>
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
<article class="fr-enlarge-link group/reuse-card bg-white border border-gray-default hover:bg-gray-some flex flex-col relative">
|
|
3
3
|
<div class="flex flex-col h-full flex-1 order-2 px-8">
|
|
4
4
|
<div class="order-1 flex flex-col px-4 py-1 h-full -mx-8">
|
|
5
|
-
<
|
|
5
|
+
<component
|
|
6
|
+
:is="titleTag"
|
|
7
|
+
class="font-bold text-base mt-1 mb-0 truncate"
|
|
8
|
+
>
|
|
6
9
|
<AppLink
|
|
7
10
|
class="text-gray-title overflow-hidden"
|
|
8
11
|
:to="reuseUrl"
|
|
9
12
|
>
|
|
10
13
|
{{ reuse.title }}
|
|
11
14
|
</AppLink>
|
|
12
|
-
</
|
|
15
|
+
</component>
|
|
13
16
|
<div class="order-3 text-sm m-0 text-gray-medium">
|
|
14
17
|
<div class="text-sm mb-0 flex items-center">
|
|
15
18
|
<ObjectCardOwner
|
|
@@ -65,15 +68,16 @@ import { RiLockLine, RiSubtractLine } from '@remixicon/vue'
|
|
|
65
68
|
import { computed } from 'vue'
|
|
66
69
|
import type { RouteLocationRaw } from 'vue-router'
|
|
67
70
|
import { useFormatDate } from '../functions/dates'
|
|
68
|
-
import type { Reuse } from '../types/reuses'
|
|
71
|
+
import type { Reuse, ReuseV2 } from '../types/reuses'
|
|
72
|
+
import type { TitleTag } from '../types/ui'
|
|
69
73
|
import { useTranslation } from '../composables/useTranslation'
|
|
70
74
|
import AppLink from './AppLink.vue'
|
|
71
75
|
import ObjectCardOwner from './ObjectCardOwner.vue'
|
|
72
76
|
import ReuseDetails from './ReuseDetails.vue'
|
|
73
77
|
import Placeholder from './Placeholder.vue'
|
|
74
78
|
|
|
75
|
-
const props = defineProps<{
|
|
76
|
-
reuse: Reuse
|
|
79
|
+
const props = withDefaults(defineProps<{
|
|
80
|
+
reuse: Reuse | ReuseV2
|
|
77
81
|
|
|
78
82
|
/**
|
|
79
83
|
* The reuseUrl is a route location object to allow Vue Router to navigate to the details of a reuse.
|
|
@@ -86,7 +90,11 @@ const props = defineProps<{
|
|
|
86
90
|
* It is used as a separate prop to allow other sites using the package to define their own organization pages.
|
|
87
91
|
*/
|
|
88
92
|
organizationUrl?: RouteLocationRaw
|
|
89
|
-
|
|
93
|
+
|
|
94
|
+
titleTag?: TitleTag
|
|
95
|
+
}>(), {
|
|
96
|
+
titleTag: 'h3',
|
|
97
|
+
})
|
|
90
98
|
|
|
91
99
|
const { t } = useTranslation()
|
|
92
100
|
const { formatRelativeIfRecentDate } = useFormatDate()
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
import { RiEyeLine, RiStarLine, RiSubtractLine } from '@remixicon/vue'
|
|
33
33
|
import { useReuseType } from '../composables/useReuseType'
|
|
34
34
|
import { summarize } from '../functions/helpers'
|
|
35
|
-
import type { Reuse } from '../types/reuses'
|
|
35
|
+
import type { Reuse, ReuseV2 } from '../types/reuses'
|
|
36
36
|
import { useTranslation } from '../composables/useTranslation'
|
|
37
37
|
|
|
38
38
|
const props = defineProps<{
|
|
39
|
-
reuse: Reuse
|
|
39
|
+
reuse: Reuse | ReuseV2
|
|
40
40
|
}>()
|
|
41
41
|
|
|
42
42
|
const { t } = useTranslation()
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
<script setup lang="ts">
|
|
61
61
|
import { RiArchiveLine, RiLineChartLine, RiLockLine } from '@remixicon/vue'
|
|
62
62
|
import type { RouteLocationRaw } from 'vue-router'
|
|
63
|
-
import type { Reuse } from '../types/reuses'
|
|
63
|
+
import type { Reuse, ReuseV2 } from '../types/reuses'
|
|
64
64
|
import { useTranslation } from '../composables/useTranslation'
|
|
65
65
|
import ReuseDetails from './ReuseDetails.vue'
|
|
66
66
|
import Placeholder from './Placeholder.vue'
|
|
@@ -71,7 +71,7 @@ import ObjectCardShortDescription from './ObjectCardShortDescription.vue'
|
|
|
71
71
|
import ObjectCardBadge from './ObjectCardBadge.vue'
|
|
72
72
|
|
|
73
73
|
defineProps<{
|
|
74
|
-
reuse: Reuse
|
|
74
|
+
reuse: Reuse | ReuseV2
|
|
75
75
|
reuseUrl?: RouteLocationRaw
|
|
76
76
|
organizationUrl?: RouteLocationRaw
|
|
77
77
|
}>()
|