@datagouv/components-next 1.0.2-dev.11 → 1.0.2-dev.110
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/{Control-DuZJdKV_.js → Control-ZFh5ta_U.js} +1 -1
- package/dist/{Datafair.client-8haHXl47.js → Datafair.client-rf4T1IkA.js} +1 -1
- package/dist/{Event--kp8kMdJ.js → Event-DSQcW7OF.js} +24 -24
- package/dist/{Image-34hvypZI.js → Image-BijNEG0p.js} +6 -6
- package/dist/JsonPreview.client-dzar6iuh.js +40 -0
- package/dist/{Map-BjUnLyj8.js → Map-BUtPf5GN.js} +756 -756
- package/dist/{MapContainer.client-l6HuXTHR.js → MapContainer.client-D-MoRNhG.js} +37 -38
- package/dist/{OSM-s40W6sQ2.js → OSM-D4MTdBtk.js} +2 -2
- package/dist/{PdfPreview.client-4OueK-2Z.js → PdfPreview.client-DoDYLmJD.js} +822 -850
- package/dist/{Pmtiles.client-4j3VTYkz.js → Pmtiles.client-Dzm01Zfm.js} +1 -1
- package/dist/PreviewWrapper.vue_vue_type_script_setup_true_lang-BRNYswg3.js +61 -0
- package/dist/{ScaleLine-KW-nXqp3.js → ScaleLine-hJQIqcZm.js} +2 -2
- package/dist/{Tile-DbNFNPfU.js → Tile-Dcl7oIVu.js} +35 -35
- package/dist/{TileImage-BsXBxMtq.js → TileImage-BJeHipMX.js} +4 -4
- package/dist/{View-BR92hTWP.js → View-xp_P_OHw.js} +412 -401
- package/dist/XmlPreview.client-cOhwff6P.js +34 -0
- package/dist/{common-PJfpC179.js → common-BjQlan3k.js} +36 -36
- package/dist/components-next.css +6 -6
- package/dist/components-next.js +165 -142
- package/dist/components.css +1 -1
- package/dist/{index-CVTIoZQ0.js → index-NofRBuyf.js} +32886 -27183
- package/dist/main-Iz1ZCL6k.js +73606 -0
- package/dist/{proj-DsetBcW7.js → proj-CsNo9yH1.js} +532 -512
- package/dist/{tilecoord-Db24Px13.js → tilecoord-A0fLnBZr.js} +28 -28
- package/dist/{vue3-xml-viewer.common-CWer_T5-.js → vue3-xml-viewer.common-tVI9uXUz.js} +1 -1
- package/package.json +25 -11
- package/src/chart.ts +5 -0
- package/src/components/ActivityList/ActivityList.vue +3 -2
- package/src/components/Chart/ChartViewer.vue +226 -0
- package/src/components/Chart/ChartViewerWrapper.vue +170 -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/ReadMore.vue +1 -1
- 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/Preview.vue +11 -11
- package/src/components/ResourceAccordion/PreviewWrapper.vue +82 -0
- package/src/components/ResourceAccordion/ResourceAccordion.vue +10 -109
- package/src/components/ResourceAccordion/XmlPreview.client.vue +16 -98
- package/src/components/ResourceExplorer/ResourceExplorer.vue +14 -10
- package/src/components/ResourceExplorer/ResourceExplorerSidebar.vue +2 -2
- package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +46 -147
- package/src/components/ResourceExplorer/ResourceSelector.vue +113 -0
- package/src/components/ReuseCard.vue +12 -4
- package/src/components/Search/GlobalSearch.vue +201 -113
- 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 +973 -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 +13 -0
- package/src/composables/useMetrics.ts +1 -1
- package/src/composables/useSearchFilter.ts +118 -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 +9 -37
- 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/metrics.ts +6 -4
- 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 +90 -9
- 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 +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 +89 -0
- package/assets/swagger-themes/newspaper.css +0 -1670
- package/dist/JsonPreview.client-D53pj9Cw.js +0 -72
- package/dist/Swagger.client-DPBmsH9q.js +0 -4
- package/dist/XmlPreview.client-XElkoA4F.js +0 -64
- package/dist/main-BbT-LUXy.js +0 -105854
- package/src/components/ResourceAccordion/Swagger.client.vue +0 -48
- package/src/functions/pagination.ts +0 -9
|
@@ -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
|
/>
|
|
@@ -134,16 +141,27 @@
|
|
|
134
141
|
:resource="resource"
|
|
135
142
|
:dataset="dataset"
|
|
136
143
|
/>
|
|
137
|
-
<
|
|
144
|
+
<OpenApiViewer
|
|
138
145
|
v-else-if="hasOpenAPIPreview"
|
|
139
146
|
:url="resource.extras['apidocUrl'] as string"
|
|
140
147
|
/>
|
|
141
|
-
<
|
|
148
|
+
<TabularExplorer
|
|
142
149
|
v-else-if="hasTabularData"
|
|
143
|
-
:resource="resource"
|
|
150
|
+
:resource-id="resource.id"
|
|
144
151
|
/>
|
|
145
152
|
<PreviewUnavailable v-else>
|
|
146
|
-
|
|
153
|
+
<!-- "File too large to download" is the only analysis:error value from hydra for now -->
|
|
154
|
+
<template v-if="resource.extras['analysis:error'] === 'File too large to download'">
|
|
155
|
+
{{ t("Ce fichier est trop volumineux pour être analysé et prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.") }}
|
|
156
|
+
</template>
|
|
157
|
+
<template v-else-if="resource.extras['analysis:parsing:error']">
|
|
158
|
+
{{ 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.") }}
|
|
159
|
+
<br>
|
|
160
|
+
<span class="text-gray-medium text-xs">{{ resource.extras['analysis:parsing:error'] }}</span>
|
|
161
|
+
</template>
|
|
162
|
+
<template v-else>
|
|
163
|
+
{{ t("Ce fichier ne peut pas être prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.") }}
|
|
164
|
+
</template>
|
|
147
165
|
</PreviewUnavailable>
|
|
148
166
|
</div>
|
|
149
167
|
<div v-if="tab.key === 'description'">
|
|
@@ -162,128 +180,10 @@
|
|
|
162
180
|
<Metadata :resource />
|
|
163
181
|
</div>
|
|
164
182
|
<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>
|
|
183
|
+
<Downloads
|
|
184
|
+
:resource="resource"
|
|
185
|
+
:dataset="dataset"
|
|
186
|
+
/>
|
|
287
187
|
</div>
|
|
288
188
|
<div v-if="tab.key === 'swagger'">
|
|
289
189
|
<div class="fr-mb-4w">
|
|
@@ -292,7 +192,7 @@
|
|
|
292
192
|
<p>{{ t("- Si le fichier est supprimé, l'API sera également supprimée.") }}</p>
|
|
293
193
|
<p>{{ t("Pour des usages pérennes, prévoyez que cette API dépend directement du fichier source.") }}</p>
|
|
294
194
|
</div>
|
|
295
|
-
<
|
|
195
|
+
<OpenApiViewer
|
|
296
196
|
v-if="hasTabularData"
|
|
297
197
|
:url="`${config.tabularApiUrl}/api/resources/${resource.id}/swagger/`"
|
|
298
198
|
/>
|
|
@@ -306,32 +206,33 @@
|
|
|
306
206
|
|
|
307
207
|
<script setup lang="ts">
|
|
308
208
|
import { computed, defineAsyncComponent } from 'vue'
|
|
309
|
-
import { RiDownloadLine, RiFileCopyLine, RiFileWarningLine,
|
|
209
|
+
import { RiDownloadLine, RiFileCopyLine, RiFileWarningLine, RiSubtractLine } from '@remixicon/vue'
|
|
310
210
|
import PreviewUnavailable from '../ResourceAccordion/PreviewUnavailable.vue'
|
|
311
211
|
import { toast } from 'vue-sonner'
|
|
312
212
|
import BrandedButton from '../BrandedButton.vue'
|
|
313
213
|
import CopyButton from '../CopyButton.vue'
|
|
314
214
|
import MarkdownViewer from '../MarkdownViewer.vue'
|
|
315
215
|
import ResourceIcon from '../ResourceAccordion/ResourceIcon.vue'
|
|
316
|
-
import
|
|
216
|
+
import OpenApiViewer from '../OpenApiViewer/OpenApiViewer.vue'
|
|
317
217
|
import TabGroup from '../Tabs/TabGroup.vue'
|
|
318
218
|
import TabList from '../Tabs/TabList.vue'
|
|
319
219
|
import Tab from '../Tabs/Tab.vue'
|
|
320
220
|
import TabPanels from '../Tabs/TabPanels.vue'
|
|
321
221
|
import TabPanel from '../Tabs/TabPanel.vue'
|
|
322
|
-
import
|
|
323
|
-
import Preview from '../ResourceAccordion/Preview.vue'
|
|
222
|
+
import TabularExplorer from '../TabularExplorer/TabularExplorer.vue'
|
|
324
223
|
import DataStructure from '../ResourceAccordion/DataStructure.vue'
|
|
224
|
+
import Downloads from '../ResourceAccordion/Downloads.vue'
|
|
325
225
|
import Metadata from '../ResourceAccordion/Metadata.vue'
|
|
326
226
|
import SchemaBadge from '../ResourceAccordion/SchemaBadge.vue'
|
|
227
|
+
import ResourceSelector from './ResourceSelector.vue'
|
|
327
228
|
import { filesize, summarize } from '../../functions/helpers'
|
|
328
|
-
import { getResourceFormatIcon } from '../../functions/resources'
|
|
329
|
-
import { getResourceExternalUrl, getResourceFilesize } from '../../functions/datasets'
|
|
229
|
+
import { getResourceFormatIcon, getResourceExternalUrl, getResourceFilesize } from '../../functions/resources'
|
|
330
230
|
import { trackEvent } from '../../functions/matomo'
|
|
331
231
|
import { useComponentsConfig } from '../../config'
|
|
332
232
|
import { useFormatDate } from '../../functions/dates'
|
|
333
233
|
import { useTranslation } from '../../composables/useTranslation'
|
|
334
234
|
import { useResourceCapabilities } from '../../composables/useResourceCapabilities'
|
|
235
|
+
import { provideTabularProfile } from '../../composables/useTabularProfile'
|
|
335
236
|
import type { Resource } from '../../types/resources'
|
|
336
237
|
import type { Dataset, DatasetV2 } from '../../types/datasets'
|
|
337
238
|
|
|
@@ -353,13 +254,15 @@ const MapContainer = defineAsyncComponent(() =>
|
|
|
353
254
|
const Pmtiles = defineAsyncComponent(() =>
|
|
354
255
|
import('../ResourceAccordion/Pmtiles.client.vue'),
|
|
355
256
|
)
|
|
356
|
-
const SwaggerClient = defineAsyncComponent(() =>
|
|
357
|
-
import('../ResourceAccordion/Swagger.client.vue'),
|
|
358
|
-
)
|
|
359
257
|
|
|
360
258
|
const props = defineProps<{
|
|
361
259
|
dataset: Dataset | DatasetV2
|
|
362
260
|
resource: Resource
|
|
261
|
+
resources?: Resource[]
|
|
262
|
+
}>()
|
|
263
|
+
|
|
264
|
+
const emit = defineEmits<{
|
|
265
|
+
select: [resource: Resource]
|
|
363
266
|
}>()
|
|
364
267
|
|
|
365
268
|
const { t } = useTranslation()
|
|
@@ -373,13 +276,13 @@ const {
|
|
|
373
276
|
hasOpenAPIPreview,
|
|
374
277
|
ogcService,
|
|
375
278
|
ogcWms,
|
|
376
|
-
generatedFormats,
|
|
377
|
-
wfsFormats,
|
|
378
|
-
defaultWfsProjection,
|
|
379
279
|
isResourceUrl,
|
|
380
280
|
tabsOptions,
|
|
381
281
|
} = useResourceCapabilities(() => props.resource, () => props.dataset)
|
|
382
282
|
|
|
283
|
+
// Share the tabular profile fetch between TabularExplorer and DataStructure tabs.
|
|
284
|
+
await provideTabularProfile(() => props.resource.id)
|
|
285
|
+
|
|
383
286
|
const resourceFilesize = computed(() => getResourceFilesize(props.resource))
|
|
384
287
|
const resourceExternalUrl = computed(() => getResourceExternalUrl(props.dataset, props.resource))
|
|
385
288
|
|
|
@@ -393,10 +296,6 @@ const downloadButtonTitle = computed(() => {
|
|
|
393
296
|
return t('Télécharger le fichier en {format}', { format: format.value })
|
|
394
297
|
})
|
|
395
298
|
|
|
396
|
-
const conversionsLastUpdate = computed(() =>
|
|
397
|
-
formatRelativeIfRecentDate(props.resource.extras['analysis:parsing:finished_at'] as string | undefined),
|
|
398
|
-
)
|
|
399
|
-
|
|
400
299
|
const copyResourceUrl = async () => {
|
|
401
300
|
try {
|
|
402
301
|
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
|
|
@@ -66,13 +69,14 @@ import { computed } from 'vue'
|
|
|
66
69
|
import type { RouteLocationRaw } from 'vue-router'
|
|
67
70
|
import { useFormatDate } from '../functions/dates'
|
|
68
71
|
import type { Reuse } 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<{
|
|
79
|
+
const props = withDefaults(defineProps<{
|
|
76
80
|
reuse: Reuse
|
|
77
81
|
|
|
78
82
|
/**
|
|
@@ -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()
|