@datagouv/components-next 1.0.2-dev.97 → 1.0.2-dev.99
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/dist/{Datafair.client-8bXp6UeQ.js → Datafair.client-C2j760M5.js} +1 -1
- package/dist/{JsonPreview.client-BxuoPK_w.js → JsonPreview.client-PFfBR4J8.js} +2 -2
- package/dist/{MapContainer.client-CTz0wmJG.js → MapContainer.client-CGrS2baS.js} +2 -2
- package/dist/{PdfPreview.client-DpVreUSl.js → PdfPreview.client-DU36UBGQ.js} +2 -2
- package/dist/{Pmtiles.client-BwmHo3T8.js → Pmtiles.client-DuTezcn5.js} +1 -1
- package/dist/{PreviewWrapper.vue_vue_type_script_setup_true_lang-stmU5qEB.js → PreviewWrapper.vue_vue_type_script_setup_true_lang-ProPRqX6.js} +1 -1
- package/dist/{XmlPreview.client-DAOs89cR.js → XmlPreview.client-Bcq2Ye14.js} +3 -3
- package/dist/components-next.css +1 -1
- package/dist/components-next.js +169 -161
- package/dist/components.css +1 -1
- package/dist/{index-JqjPja1u.js → index-BJ-zwAF5.js} +1 -1
- package/dist/{main-D4WQMky0.js → main-TqHFAOCi.js} +73370 -73298
- package/dist/{vue3-xml-viewer.common-BGsoNyZe.js → vue3-xml-viewer.common-BnJTx_B7.js} +1 -1
- package/package.json +1 -1
- package/src/components/DatasetCard.vue +6 -4
- package/src/components/ResourceAccordion/DataStructure.vue +11 -33
- package/src/components/ResourceAccordion/Downloads.vue +160 -0
- package/src/components/ResourceAccordion/ResourceAccordion.vue +5 -102
- package/src/components/ResourceExplorer/ResourceExplorer.vue +2 -55
- package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +26 -135
- package/src/components/ResourceExplorer/ResourceSelector.vue +113 -0
- package/src/components/TabularExplorer/TabularExplorer.vue +257 -154
- package/src/composables/useHasTabularData.ts +7 -0
- package/src/composables/useTabularProfile.ts +70 -0
- package/src/main.ts +12 -0
package/package.json
CHANGED
|
@@ -107,10 +107,12 @@
|
|
|
107
107
|
</p>
|
|
108
108
|
</div>
|
|
109
109
|
</div>
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
<slot>
|
|
111
|
+
<ObjectCardShortDescription
|
|
112
|
+
v-if="showDescriptionShort"
|
|
113
|
+
:text="getDescriptionShort(props.dataset)"
|
|
114
|
+
/>
|
|
115
|
+
</slot>
|
|
114
116
|
</ObjectCard>
|
|
115
117
|
</template>
|
|
116
118
|
|
|
@@ -49,45 +49,23 @@
|
|
|
49
49
|
</template>
|
|
50
50
|
|
|
51
51
|
<script setup lang="ts">
|
|
52
|
-
import {
|
|
52
|
+
import { computed } from 'vue'
|
|
53
53
|
import type { Resource } from '../../types/resources'
|
|
54
|
-
import { useGetProfile } from '../../functions/tabularApi'
|
|
55
54
|
import { useTranslation } from '../../composables/useTranslation'
|
|
55
|
+
import { injectTabularProfile } from '../../composables/useTabularProfile'
|
|
56
56
|
import PreviewLoader from './PreviewLoader.vue'
|
|
57
57
|
|
|
58
58
|
const props = defineProps<{ resource: Resource }>()
|
|
59
|
-
const getProfile = useGetProfile()
|
|
60
59
|
const { t } = useTranslation()
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
61
|
+
// Profile is shared with sibling components (e.g. TabularExplorer) via
|
|
62
|
+
// `provideTabularProfile` in the parent. Falls back to a local fetch
|
|
63
|
+
// when no parent provides it (standalone usage).
|
|
64
|
+
const { data: profileData, status } = await injectTabularProfile(() => props.resource.id)
|
|
67
65
|
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
onMounted(async () => {
|
|
75
|
-
try {
|
|
76
|
-
const response = await getProfile(props.resource.id) // Assurez-vous que cette fonction retourne bien les données attendues
|
|
77
|
-
if ('profile' in response && response.profile) {
|
|
78
|
-
columns.value = Object.keys(response.profile.columns)
|
|
79
|
-
columnsInfo.value = response.profile.columns
|
|
80
|
-
hasColumnInfo.value = true
|
|
81
|
-
loading.value = false
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
hasError.value = true
|
|
85
|
-
loading.value = false
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
hasError.value = true
|
|
90
|
-
loading.value = false
|
|
91
|
-
}
|
|
92
|
-
})
|
|
66
|
+
const loading = computed(() => status.value === 'idle' || status.value === 'pending')
|
|
67
|
+
const hasError = computed(() => status.value === 'error')
|
|
68
|
+
const hasColumnInfo = computed(() => !!profileData.value?.profile?.columns)
|
|
69
|
+
const columns = computed(() => profileData.value?.profile ? Object.keys(profileData.value.profile.columns) : [])
|
|
70
|
+
const columnsInfo = computed(() => profileData.value?.profile?.columns ?? {})
|
|
93
71
|
</script>
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<dl class="fr-pl-0">
|
|
3
|
+
<dt
|
|
4
|
+
v-if="resource.format === 'url'"
|
|
5
|
+
class="font-bold fr-text--sm fr-mb-0"
|
|
6
|
+
>
|
|
7
|
+
{{ t("URL d'origine") }}
|
|
8
|
+
</dt>
|
|
9
|
+
<dt
|
|
10
|
+
v-else
|
|
11
|
+
class="font-bold fr-text--sm fr-mb-0"
|
|
12
|
+
>
|
|
13
|
+
{{ t('Format original') }}
|
|
14
|
+
</dt>
|
|
15
|
+
<dd class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center">
|
|
16
|
+
<span
|
|
17
|
+
v-if="resource.format === 'url'"
|
|
18
|
+
class="inline-flex items-center max-w-full"
|
|
19
|
+
>
|
|
20
|
+
<a
|
|
21
|
+
:href="resource.latest"
|
|
22
|
+
class="fr-link no-icon-after truncate"
|
|
23
|
+
rel="ugc nofollow noopener"
|
|
24
|
+
target="_blank"
|
|
25
|
+
@click="trackEvent('Jeux de données', 'Télécharger un fichier', 'Bouton : télécharger un fichier')"
|
|
26
|
+
>
|
|
27
|
+
{{ resource.url }}
|
|
28
|
+
</a>
|
|
29
|
+
<span class="fr-ml-1v fr-icon-external-link-line fr-icon--sm shrink-0" />
|
|
30
|
+
</span>
|
|
31
|
+
<span v-else>
|
|
32
|
+
<span class="text-datagouv fr-icon-download-line fr-icon--sm fr-mr-1v fr-mt-1v" />
|
|
33
|
+
<a
|
|
34
|
+
:href="resource.latest"
|
|
35
|
+
class="fr-link"
|
|
36
|
+
rel="ugc nofollow noopener"
|
|
37
|
+
@click="trackEvent('Jeux de données', 'Télécharger un fichier', `Bouton : format ${resource.format}`)"
|
|
38
|
+
>
|
|
39
|
+
<span>{{ t('Format {format}', { format: resource.format }) }}<span v-if="resourceFilesize"> - {{ filesize(resourceFilesize) }}</span></span>
|
|
40
|
+
</a>
|
|
41
|
+
</span>
|
|
42
|
+
<CopyButton
|
|
43
|
+
:label="t('Copier le lien')"
|
|
44
|
+
:copied-label="t('Lien copié !')"
|
|
45
|
+
:text="resource.latest"
|
|
46
|
+
class="relative"
|
|
47
|
+
/>
|
|
48
|
+
</dd>
|
|
49
|
+
<template v-if="generatedFormats.length">
|
|
50
|
+
<dt class="font-bold fr-text--sm fr-mb-0">
|
|
51
|
+
{{ t('Formats générés automatiquement par {platform} (dernière mise à jour {date})', { platform: config.name, date: conversionsLastUpdate }) }}
|
|
52
|
+
</dt>
|
|
53
|
+
<dd
|
|
54
|
+
v-for="generatedFormat in generatedFormats"
|
|
55
|
+
:key="generatedFormat.format"
|
|
56
|
+
class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center"
|
|
57
|
+
>
|
|
58
|
+
<span>
|
|
59
|
+
<span class="text-datagouv fr-icon-download-line fr-icon--sm fr-mr-1v fr-mt-1v" />
|
|
60
|
+
<a
|
|
61
|
+
:href="generatedFormat.url"
|
|
62
|
+
class="fr-link"
|
|
63
|
+
rel="ugc nofollow noopener"
|
|
64
|
+
@click="trackEvent('Jeux de données', 'Télécharger un fichier', `Bouton : format ${generatedFormat.format}`)"
|
|
65
|
+
>
|
|
66
|
+
<span>{{ t('Format {format}', { format: generatedFormat.format }) }}<span v-if="generatedFormat.size"> - {{ filesize(generatedFormat.size) }}</span></span>
|
|
67
|
+
</a>
|
|
68
|
+
</span>
|
|
69
|
+
<CopyButton
|
|
70
|
+
:label="t('Copier le lien')"
|
|
71
|
+
:copied-label="t('Lien copié !')"
|
|
72
|
+
:text="generatedFormat.url"
|
|
73
|
+
class="relative"
|
|
74
|
+
/>
|
|
75
|
+
</dd>
|
|
76
|
+
</template>
|
|
77
|
+
<template v-if="wfsFormats.length">
|
|
78
|
+
<dt class="font-bold fr-text--sm fr-mb-0">
|
|
79
|
+
<div class="flex gap-1 items-center">
|
|
80
|
+
{{ t('Formats exportés depuis le service WFS') }}
|
|
81
|
+
<span v-if="defaultWfsProjection"> ({{ t('projection {crs}', { crs: defaultWfsProjection }) }})</span>
|
|
82
|
+
<Tooltip>
|
|
83
|
+
<RiInformationLine
|
|
84
|
+
class="flex-none size-4"
|
|
85
|
+
: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é.`)"
|
|
86
|
+
aria-hidden="true"
|
|
87
|
+
/>
|
|
88
|
+
<template #tooltip>
|
|
89
|
+
<p class="text-sm font-normal mb-0">
|
|
90
|
+
{{ t(`Le lien de téléchargement interroge directement le flux WFS distant.`) }}
|
|
91
|
+
</p>
|
|
92
|
+
<p class="text-sm font-normal mb-0">
|
|
93
|
+
{{ t(`Le nombre de features téléchargées peut être limité.`) }}
|
|
94
|
+
</p>
|
|
95
|
+
</template>
|
|
96
|
+
</Tooltip>
|
|
97
|
+
</div>
|
|
98
|
+
</dt>
|
|
99
|
+
<dd
|
|
100
|
+
v-for="wfsFormat in wfsFormats"
|
|
101
|
+
:key="wfsFormat.format"
|
|
102
|
+
class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center"
|
|
103
|
+
>
|
|
104
|
+
<span>
|
|
105
|
+
<span class="text-datagouv fr-icon-download-line fr-icon--sm fr-mr-1v fr-mt-1v" />
|
|
106
|
+
<a
|
|
107
|
+
:href="wfsFormat.url"
|
|
108
|
+
class="fr-link"
|
|
109
|
+
rel="ugc nofollow noopener"
|
|
110
|
+
@click="trackEvent('Jeux de données', 'Télécharger un fichier', `Bouton : format ${wfsFormat.format}`)"
|
|
111
|
+
>
|
|
112
|
+
<span>{{ t('Format {format}', { format: wfsFormat.format }) }}</span>
|
|
113
|
+
</a>
|
|
114
|
+
</span>
|
|
115
|
+
<CopyButton
|
|
116
|
+
:label="t('Copier le lien')"
|
|
117
|
+
:copied-label="t('Lien copié !')"
|
|
118
|
+
:text="wfsFormat.url"
|
|
119
|
+
class="relative"
|
|
120
|
+
/>
|
|
121
|
+
</dd>
|
|
122
|
+
</template>
|
|
123
|
+
</dl>
|
|
124
|
+
</template>
|
|
125
|
+
|
|
126
|
+
<script setup lang="ts">
|
|
127
|
+
import { computed } from 'vue'
|
|
128
|
+
import { RiInformationLine } from '@remixicon/vue'
|
|
129
|
+
import CopyButton from '../CopyButton.vue'
|
|
130
|
+
import Tooltip from '../Tooltip.vue'
|
|
131
|
+
import { filesize } from '../../functions/helpers'
|
|
132
|
+
import { getResourceFilesize } from '../../functions/resources'
|
|
133
|
+
import { trackEvent } from '../../functions/matomo'
|
|
134
|
+
import { useComponentsConfig } from '../../config'
|
|
135
|
+
import { useFormatDate } from '../../functions/dates'
|
|
136
|
+
import { useTranslation } from '../../composables/useTranslation'
|
|
137
|
+
import { useResourceCapabilities } from '../../composables/useResourceCapabilities'
|
|
138
|
+
import type { Resource } from '../../types/resources'
|
|
139
|
+
import type { Dataset, DatasetV2 } from '../../types/datasets'
|
|
140
|
+
|
|
141
|
+
const props = defineProps<{
|
|
142
|
+
resource: Resource
|
|
143
|
+
dataset: Dataset | DatasetV2
|
|
144
|
+
}>()
|
|
145
|
+
|
|
146
|
+
const { t } = useTranslation()
|
|
147
|
+
const config = useComponentsConfig()
|
|
148
|
+
const { formatRelativeIfRecentDate } = useFormatDate()
|
|
149
|
+
|
|
150
|
+
const { generatedFormats, wfsFormats, defaultWfsProjection } = useResourceCapabilities(
|
|
151
|
+
() => props.resource,
|
|
152
|
+
() => props.dataset,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
const resourceFilesize = computed(() => getResourceFilesize(props.resource))
|
|
156
|
+
|
|
157
|
+
const conversionsLastUpdate = computed(() =>
|
|
158
|
+
formatRelativeIfRecentDate(props.resource.extras['analysis:parsing:finished_at'] as string | undefined),
|
|
159
|
+
)
|
|
160
|
+
</script>
|
|
@@ -265,88 +265,10 @@
|
|
|
265
265
|
<div
|
|
266
266
|
v-if="tab.key === 'downloads'"
|
|
267
267
|
>
|
|
268
|
-
<
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
>
|
|
273
|
-
{{ t("URL d'origine") }}
|
|
274
|
-
</dt>
|
|
275
|
-
<dt
|
|
276
|
-
v-else
|
|
277
|
-
class="font-bold fr-text--sm fr-mb-0"
|
|
278
|
-
>
|
|
279
|
-
{{ t('Format original') }}
|
|
280
|
-
</dt>
|
|
281
|
-
<dd class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center">
|
|
282
|
-
<span v-if="resource.format === 'url'">
|
|
283
|
-
<a
|
|
284
|
-
:href="resource.latest"
|
|
285
|
-
class="fr-link no-icon-after"
|
|
286
|
-
rel="ugc nofollow noopener"
|
|
287
|
-
target="_blank"
|
|
288
|
-
@click="trackEvent('Jeux de données', 'Télécharger un fichier', 'Bouton : télécharger un fichier')"
|
|
289
|
-
>
|
|
290
|
-
<component
|
|
291
|
-
:is="config.textClamp"
|
|
292
|
-
v-if="config && config.textClamp"
|
|
293
|
-
:auto-resize="true"
|
|
294
|
-
:max-lines="1"
|
|
295
|
-
:text="resource.url"
|
|
296
|
-
>
|
|
297
|
-
<template #after>
|
|
298
|
-
<span class="fr-ml-1v fr-icon-external-link-line fr-icon--sm" />
|
|
299
|
-
</template>
|
|
300
|
-
</component>
|
|
301
|
-
</a>
|
|
302
|
-
</span>
|
|
303
|
-
<span v-else>
|
|
304
|
-
<span class="text-datagouv fr-icon-download-line fr-icon--sm fr-mr-1v fr-mt-1v" />
|
|
305
|
-
<a
|
|
306
|
-
:href="resource.latest"
|
|
307
|
-
class="fr-link"
|
|
308
|
-
rel="ugc nofollow noopener"
|
|
309
|
-
@click="trackEvent('Jeux de données', 'Télécharger un fichier', `Bouton : format ${resource.format}`)"
|
|
310
|
-
>
|
|
311
|
-
<span>{{ t('Format {format}', { format: resource.format }) }}<span v-if="resourceFilesize"> - {{ filesize(resourceFilesize) }}</span></span>
|
|
312
|
-
</a>
|
|
313
|
-
</span>
|
|
314
|
-
<CopyButton
|
|
315
|
-
:label="t('Copier le lien')"
|
|
316
|
-
:copied-label="t('Lien copié !')"
|
|
317
|
-
:text="resource.latest"
|
|
318
|
-
class="relative"
|
|
319
|
-
/>
|
|
320
|
-
</dd>
|
|
321
|
-
<template v-if="generatedFormats.length">
|
|
322
|
-
<dt class="font-bold fr-text--sm fr-mb-0">
|
|
323
|
-
{{ t('Formats générés automatiquement par {platform} (dernière mise à jour {date})', { platform: config.name, date: conversionsLastUpdate }) }}
|
|
324
|
-
</dt>
|
|
325
|
-
<dd
|
|
326
|
-
v-for="generatedFormat in generatedFormats"
|
|
327
|
-
:key="generatedFormat.format"
|
|
328
|
-
class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center"
|
|
329
|
-
>
|
|
330
|
-
<span>
|
|
331
|
-
<span class="text-datagouv fr-icon-download-line fr-icon--sm fr-mr-1v fr-mt-1v" />
|
|
332
|
-
<a
|
|
333
|
-
:href="generatedFormat.url"
|
|
334
|
-
class="fr-link"
|
|
335
|
-
rel="ugc nofollow noopener"
|
|
336
|
-
@click="trackEvent('Jeux de données', 'Télécharger un fichier', `Bouton : format ${generatedFormat.format}`)"
|
|
337
|
-
>
|
|
338
|
-
<span>{{ t('Format {format}', { format: generatedFormat.format }) }}<span v-if="generatedFormat.size"> - {{ filesize(generatedFormat.size) }}</span></span>
|
|
339
|
-
</a>
|
|
340
|
-
</span>
|
|
341
|
-
<CopyButton
|
|
342
|
-
:label="t('Copier le lien')"
|
|
343
|
-
:copied-label="t('Lien copié !')"
|
|
344
|
-
:text="generatedFormat.url"
|
|
345
|
-
class="relative"
|
|
346
|
-
/>
|
|
347
|
-
</dd>
|
|
348
|
-
</template>
|
|
349
|
-
</dl>
|
|
268
|
+
<Downloads
|
|
269
|
+
:resource="resource"
|
|
270
|
+
:dataset="dataset"
|
|
271
|
+
/>
|
|
350
272
|
</div>
|
|
351
273
|
<div
|
|
352
274
|
v-if="tab.key === 'swagger'"
|
|
@@ -396,11 +318,11 @@ import SchemaBadge from './SchemaBadge.vue'
|
|
|
396
318
|
import ResourceIcon from './ResourceIcon.vue'
|
|
397
319
|
import EditButton from './EditButton.vue'
|
|
398
320
|
import DataStructure from './DataStructure.vue'
|
|
321
|
+
import Downloads from './Downloads.vue'
|
|
399
322
|
import Preview from './Preview.vue'
|
|
400
323
|
import { isOrganizationCertified } from '../../functions/organizations'
|
|
401
324
|
import OpenApiViewer from '../OpenApiViewer/OpenApiViewer.vue'
|
|
402
325
|
|
|
403
|
-
const GENERATED_FORMATS = ['parquet', 'pmtiles', 'geojson']
|
|
404
326
|
const URL_FORMATS = ['url', 'doi', 'www:link', ' www:link-1.0-http--link', 'www:link-1.0-http--partners', 'www:link-1.0-http--related', 'www:link-1.0-http--samples']
|
|
405
327
|
|
|
406
328
|
const props = withDefaults(defineProps<{
|
|
@@ -460,24 +382,6 @@ const ogcService = computed(() => detectOgcService(props.resource))
|
|
|
460
382
|
|
|
461
383
|
const ogcWms = computed(() => ogcService.value === 'wms')
|
|
462
384
|
|
|
463
|
-
const generatedFormats = computed(() => {
|
|
464
|
-
const formats = GENERATED_FORMATS
|
|
465
|
-
.filter(format => `analysis:parsing:${format}_url` in props.resource.extras)
|
|
466
|
-
.map(format => ({
|
|
467
|
-
url: props.resource.extras[`analysis:parsing:${format}_url`] as string,
|
|
468
|
-
size: props.resource.extras[`analysis:parsing:${format}_size`] as number | undefined,
|
|
469
|
-
format: format,
|
|
470
|
-
}))
|
|
471
|
-
if ('analysis:parsing:parsing_table' in props.resource.extras) {
|
|
472
|
-
formats.push({
|
|
473
|
-
url: `${config.tabularApiUrl}/api/resources/${props.resource.id}/data/json/`,
|
|
474
|
-
size: undefined,
|
|
475
|
-
format: 'json',
|
|
476
|
-
})
|
|
477
|
-
}
|
|
478
|
-
return formats
|
|
479
|
-
})
|
|
480
|
-
|
|
481
385
|
const open = ref(props.expandedOnMount)
|
|
482
386
|
const toggle = () => {
|
|
483
387
|
open.value = !open.value
|
|
@@ -540,7 +444,6 @@ const communityResource = computed<CommunityResource | null>(() => {
|
|
|
540
444
|
const owner = computed(() => communityResource.value ? getOwnerName(communityResource.value) : null)
|
|
541
445
|
|
|
542
446
|
const lastUpdate = props.resource.last_modified
|
|
543
|
-
const conversionsLastUpdate = computed(() => formatRelativeIfRecentDate(props.resource.extras['analysis:parsing:finished_at'] as string | undefined))
|
|
544
447
|
const availabilityChecked = props.resource.extras && 'check:available' in props.resource.extras
|
|
545
448
|
const resourceFilesize = computed(() => getResourceFilesize(props.resource))
|
|
546
449
|
|
|
@@ -2,24 +2,13 @@
|
|
|
2
2
|
<div v-if="allResources.length || hasAnyResources">
|
|
3
3
|
<div class="flex gap-6">
|
|
4
4
|
<div class="flex-1 min-w-0">
|
|
5
|
-
<div
|
|
6
|
-
v-if="dataset.resources.total > 1"
|
|
7
|
-
class="md:hidden flex justify-end mb-3"
|
|
8
|
-
>
|
|
9
|
-
<BrandedButton
|
|
10
|
-
size="xs"
|
|
11
|
-
color="secondary"
|
|
12
|
-
:icon="RiListUnordered"
|
|
13
|
-
@click="mobileSidebarOpen = true"
|
|
14
|
-
>
|
|
15
|
-
{{ t('Ressources ({count})', { count: dataset.resources.total }) }}
|
|
16
|
-
</BrandedButton>
|
|
17
|
-
</div>
|
|
18
5
|
<ResourceExplorerViewer
|
|
19
6
|
v-if="selectedResource && allResources.length"
|
|
20
7
|
:key="selectedResource.id"
|
|
21
8
|
:dataset
|
|
22
9
|
:resource="selectedResource"
|
|
10
|
+
:resources="flatResources"
|
|
11
|
+
@select="selectResource"
|
|
23
12
|
/>
|
|
24
13
|
<div
|
|
25
14
|
v-else-if="search"
|
|
@@ -56,25 +45,6 @@
|
|
|
56
45
|
/>
|
|
57
46
|
</div>
|
|
58
47
|
</div>
|
|
59
|
-
|
|
60
|
-
<!-- Mobile sidebar panel -->
|
|
61
|
-
<dialog
|
|
62
|
-
ref="mobileSidebarDialog"
|
|
63
|
-
class="mobile-sidebar md:hidden fixed inset-0 m-0 ml-auto p-0 h-dvh max-h-dvh w-80 max-w-[85vw] bg-white shadow-lg overflow-y-auto overscroll-contain backdrop:bg-black/30"
|
|
64
|
-
@close="mobileSidebarOpen = false"
|
|
65
|
-
@click.self="closeMobileSidebar"
|
|
66
|
-
>
|
|
67
|
-
<ResourceExplorerSidebar
|
|
68
|
-
:resources="allResources"
|
|
69
|
-
:selected-resource-id="selectedResource?.id ?? null"
|
|
70
|
-
:collapsed="false"
|
|
71
|
-
:search
|
|
72
|
-
@select="selectResource"
|
|
73
|
-
@load-more="loadMore"
|
|
74
|
-
@update:collapsed="closeMobileSidebar"
|
|
75
|
-
@update:search="updateSearch($event)"
|
|
76
|
-
/>
|
|
77
|
-
</dialog>
|
|
78
48
|
</div>
|
|
79
49
|
<div
|
|
80
50
|
v-else
|
|
@@ -107,7 +77,6 @@ import type { DatasetV2 } from '../../types/datasets'
|
|
|
107
77
|
import type { Resource, ResourceGroup, ResourceType } from '../../types/resources'
|
|
108
78
|
import ResourceExplorerSidebar from './ResourceExplorerSidebar.vue'
|
|
109
79
|
import ResourceExplorerViewer from './ResourceExplorerViewer.vue'
|
|
110
|
-
import { RiListUnordered } from '@remixicon/vue'
|
|
111
80
|
import BrandedButton from '../BrandedButton.vue'
|
|
112
81
|
|
|
113
82
|
const props = withDefaults(defineProps<{
|
|
@@ -246,21 +215,6 @@ const flatResources = computed(() =>
|
|
|
246
215
|
|
|
247
216
|
// Fetch resource by ID if specified in URL (for SSR)
|
|
248
217
|
const initialResourceId = resourceIdQuery.value
|
|
249
|
-
const mobileSidebarOpen = ref(false)
|
|
250
|
-
const mobileSidebarDialog = ref<HTMLDialogElement | null>(null)
|
|
251
|
-
|
|
252
|
-
watch(mobileSidebarOpen, (open) => {
|
|
253
|
-
if (open) {
|
|
254
|
-
mobileSidebarDialog.value?.showModal()
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
mobileSidebarDialog.value?.close()
|
|
258
|
-
}
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
function closeMobileSidebar() {
|
|
262
|
-
mobileSidebarOpen.value = false
|
|
263
|
-
}
|
|
264
218
|
const { data: fetchedResource } = initialResourceId
|
|
265
219
|
? await useFetch<Resource>(`/api/1/datasets/${props.dataset.id}/resources/${initialResourceId}/`)
|
|
266
220
|
: { data: ref(null) }
|
|
@@ -288,7 +242,6 @@ function updateSearch(newSearch: string) {
|
|
|
288
242
|
|
|
289
243
|
const selectResource = (resource: Resource) => {
|
|
290
244
|
selectedResource.value = resource
|
|
291
|
-
mobileSidebarOpen.value = false
|
|
292
245
|
router.replace({
|
|
293
246
|
query: { ...router.currentRoute.value.query, resource_id: resource.id },
|
|
294
247
|
})
|
|
@@ -303,9 +256,3 @@ watch(flatResources, () => {
|
|
|
303
256
|
}
|
|
304
257
|
})
|
|
305
258
|
</script>
|
|
306
|
-
|
|
307
|
-
<style>
|
|
308
|
-
html:has(dialog.mobile-sidebar[open]) {
|
|
309
|
-
overflow: hidden;
|
|
310
|
-
}
|
|
311
|
-
</style>
|