@datagouv/components-next 1.0.2-dev.161 → 1.0.2-dev.162
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-Bz1l4SLK.js → Datafair.client-DXPU1V24.js} +1 -1
- package/dist/{JsonPreview.client-XwLb74J8.js → JsonPreview.client-D1SnGfWG.js} +2 -2
- package/dist/{MapContainer.client-DJm7fYDx.js → MapContainer.client-DlsaEVGx.js} +2 -2
- package/dist/{PdfPreview.client-CoLz0aZp.js → PdfPreview.client-C9OrLR0z.js} +2 -2
- package/dist/{Pmtiles.client-bKmHL2Qg.js → Pmtiles.client-CyylWfTy.js} +5660 -5842
- package/dist/{PreviewWrapper.vue_vue_type_script_setup_true_lang-CxXJ7rC5.js → PreviewWrapper.vue_vue_type_script_setup_true_lang-CtXZOHXI.js} +10 -7
- package/dist/{XmlPreview.client-Baetgqam.js → XmlPreview.client-DoX7RurH.js} +3 -3
- package/dist/components-next.css +1 -1
- package/dist/components-next.js +171 -178
- package/dist/components.css +1 -1
- package/dist/{index-Crp28Eov.js → index-Br1sa7wQ.js} +4293 -4392
- package/dist/{leaflet-src-CRXb3b2x.js → leaflet-src-DWG7tmpQ.js} +1 -1
- package/dist/{main-Cmq8fQdM.js → main-BU6kP4dE.js} +14044 -14487
- package/dist/{vue3-xml-viewer.common-H2czi46-.js → vue3-xml-viewer.common-Cpui-Yi9.js} +1 -1
- package/package.json +5 -4
- package/src/components/Chart/ChartViewer.vue +10 -103
- package/src/components/Chart/ChartViewerWrapper.vue +0 -8
- package/src/components/DataserviceCard.vue +0 -6
- package/src/components/DatasetQuality.vue +11 -11
- package/src/components/DatasetQualityInline.vue +2 -2
- package/src/components/DatasetQualityTooltipContent.vue +1 -1
- package/src/components/ResourceAccordion/ResourceAccordion.vue +3 -8
- package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +1 -15
- package/src/components/ReuseCard.vue +2 -2
- package/src/components/ReuseDetails.vue +2 -2
- package/src/components/ReuseHorizontalCard.vue +2 -2
- package/src/components/Search/GlobalSearch.vue +2 -2
- package/src/components/TabularExplorer/TabularExplorer.vue +5 -31
- package/src/components/TabularExplorer/types.ts +2 -2
- package/src/composables/useHasTabularData.ts +1 -2
- package/src/composables/useResourceCapabilities.ts +14 -27
- package/src/config.ts +0 -1
- package/src/functions/charts.ts +2 -18
- package/src/functions/resources.ts +0 -56
- package/src/main.ts +5 -12
- package/src/types/dataservices.ts +0 -15
- package/src/types/organizations.ts +0 -10
- package/src/types/reuses.ts +1 -12
- package/src/types/search.ts +2 -2
- package/src/types/visualizations.ts +1 -1
- package/dist/ImagePreview.client-CogDiX0n.js +0 -36
- package/src/components/DataserviceQuality.vue +0 -82
- package/src/components/DataserviceQualityInline.vue +0 -31
- package/src/components/DataserviceQualityTooltipContent.vue +0 -79
- package/src/components/ResourceAccordion/ImagePreview.client.vue +0 -48
- /package/src/components/{QualityItem.vue → DatasetQualityItem.vue} +0 -0
- /package/src/components/{QualityItemWarning.vue → DatasetQualityItemWarning.vue} +0 -0
- /package/src/components/{QualityScore.vue → DatasetQualityScore.vue} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagouv/components-next",
|
|
3
|
-
"version": "1.0.2-dev.
|
|
3
|
+
"version": "1.0.2-dev.162",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"src"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@floating-ui/vue": "^
|
|
26
|
+
"@floating-ui/vue": "^1.1.11",
|
|
27
27
|
"@headlessui/vue": "^1.7.23",
|
|
28
28
|
"@remixicon/vue": "^4.9.0",
|
|
29
29
|
"@types/hast": "^3.0.4",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@vueuse/core": "^14.3.0",
|
|
32
32
|
"@vueuse/router": "^14.3.0",
|
|
33
33
|
"chart.js": "^4.5.1",
|
|
34
|
-
"dompurify": "^3.4.
|
|
34
|
+
"dompurify": "^3.4.7",
|
|
35
35
|
"echarts": "^6.1.0",
|
|
36
36
|
"geopf-extensions-openlayers": "1.0.0-beta.11",
|
|
37
37
|
"leaflet": "^1.9.4",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"yaml": "^2.9.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
+
"@intlify/cli": "^0.13.1",
|
|
66
67
|
"@tailwindcss/cli": "^4.3.0",
|
|
67
68
|
"@tailwindcss/typography": "^0.5.19",
|
|
68
69
|
"@tsconfig/node22": "^22.0.5",
|
|
@@ -82,7 +83,7 @@
|
|
|
82
83
|
"typescript": "^5.9.3",
|
|
83
84
|
"vite": "^7.3.3",
|
|
84
85
|
"vite-plugin-vue-devtools": "^8.1.2",
|
|
85
|
-
"vue": "^3.5.
|
|
86
|
+
"vue": "^3.5.35",
|
|
86
87
|
"vue-router": "^5.0.7",
|
|
87
88
|
"vue-tsc": "^3.3.2"
|
|
88
89
|
},
|
|
@@ -13,8 +13,7 @@ import { TitleComponent, TooltipComponent, LegendComponent, GridComponent, Datas
|
|
|
13
13
|
import { init, type ECharts as EChartsType } from 'echarts'
|
|
14
14
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
15
15
|
import { summarize } from '../../functions/helpers'
|
|
16
|
-
import {
|
|
17
|
-
import type { Chart, ColumnDefinition, ColumnsDefinition, XAxis, YAxis, XAxisForm, ChartForApi } from '../../types/visualizations'
|
|
16
|
+
import type { Chart, XAxis, YAxis, XAxisForm, ChartForApi } from '../../types/visualizations'
|
|
18
17
|
import { useTranslation } from '../../composables/useTranslation'
|
|
19
18
|
|
|
20
19
|
use([CanvasRenderer, LineChart, BarChart, TitleComponent, TooltipComponent, LegendComponent, GridComponent, DatasetComponent])
|
|
@@ -23,26 +22,17 @@ const props = defineProps<{
|
|
|
23
22
|
chart: Chart | ChartForApi
|
|
24
23
|
series: {
|
|
25
24
|
data: Record<string, Array<Record<string, unknown>>>
|
|
26
|
-
columns:
|
|
25
|
+
columns: Record<string, Array<{ name: string, type: string }>>
|
|
27
26
|
}
|
|
28
27
|
}>()
|
|
29
28
|
|
|
30
29
|
const { locale } = useTranslation()
|
|
31
|
-
const { formatDate } = useFormatDate()
|
|
32
30
|
const chartContainer = ref<HTMLElement | null>(null)
|
|
33
31
|
let echartsInstance: EChartsType | null = null
|
|
34
32
|
|
|
35
|
-
function
|
|
36
|
-
return echartsInstance?.getDataURL() ?? null
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
defineExpose({ capture })
|
|
40
|
-
|
|
41
|
-
// ECharts has a dedicated 'time' axis type for continuous date values.
|
|
42
|
-
function mapXAxisType(xAxis: XAxis | XAxisForm): 'category' | 'value' | 'time' {
|
|
33
|
+
function mapXAxisType(xAxis: XAxis | XAxisForm): 'category' | 'value' {
|
|
43
34
|
if (!xAxis) return 'category'
|
|
44
|
-
|
|
45
|
-
return isDateAxis.value ? 'time' : 'value'
|
|
35
|
+
return xAxis.type === 'continuous' ? 'value' : 'category'
|
|
46
36
|
}
|
|
47
37
|
|
|
48
38
|
function buildYAxisFormatter(yAxis: YAxis): ((value: number) => string) | undefined {
|
|
@@ -54,77 +44,6 @@ function buildYAxisFormatter(yAxis: YAxis): ((value: number) => string) | undefi
|
|
|
54
44
|
}
|
|
55
45
|
}
|
|
56
46
|
|
|
57
|
-
function buildXAxisFormatter(column: ColumnDefinition | null, options?: Intl.DateTimeFormatOptions): ((value: string | number | Date) => string) | undefined {
|
|
58
|
-
if (column?.type !== 'date') return undefined
|
|
59
|
-
return (value: string | number | Date) => {
|
|
60
|
-
if (value === null || value === undefined) return ''
|
|
61
|
-
const date = value instanceof Date ? value : new Date(value)
|
|
62
|
-
if (Number.isNaN(date.getTime())) return String(value)
|
|
63
|
-
return formatDate(date, options ?? { dateStyle: 'short' })
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function parseXValueAsDate(row: Record<string, unknown>, xColumn: string): Record<string, unknown> {
|
|
68
|
-
const value = row[xColumn]
|
|
69
|
-
if (value === null || value === undefined || value instanceof Date) return row
|
|
70
|
-
if (typeof value === 'string') {
|
|
71
|
-
const date = new Date(value)
|
|
72
|
-
if (!Number.isNaN(date.getTime())) {
|
|
73
|
-
return { ...row, [xColumn]: date }
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return row
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const xAxisColumn = computed(() => {
|
|
80
|
-
const columnName = props.chart.x_axis.column_x
|
|
81
|
-
if (!columnName) return null
|
|
82
|
-
for (const columns of Object.values(props.series.columns)) {
|
|
83
|
-
const column = columns.find(c => c.name === columnName)
|
|
84
|
-
if (column) return column
|
|
85
|
-
}
|
|
86
|
-
return null
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
const isDateAxis = computed(() => xAxisColumn.value?.type === 'date')
|
|
90
|
-
|
|
91
|
-
const xAxisBounds = computed(() => {
|
|
92
|
-
const columnName = props.chart.x_axis.column_x
|
|
93
|
-
if (!columnName) return { min: undefined, max: undefined }
|
|
94
|
-
|
|
95
|
-
let min: number | undefined
|
|
96
|
-
let max: number | undefined
|
|
97
|
-
|
|
98
|
-
for (const s of props.chart.series) {
|
|
99
|
-
const xColumn = s.column_x_name_override ?? columnName
|
|
100
|
-
const data = props.series.data[s.resource_id]
|
|
101
|
-
if (!data) continue
|
|
102
|
-
|
|
103
|
-
for (const row of data) {
|
|
104
|
-
const value = row[xColumn]
|
|
105
|
-
if (value === null || value === undefined) continue
|
|
106
|
-
|
|
107
|
-
let numericValue: number | undefined
|
|
108
|
-
if (value instanceof Date) {
|
|
109
|
-
numericValue = value.getTime()
|
|
110
|
-
}
|
|
111
|
-
else if (isDateAxis.value && typeof value === 'string') {
|
|
112
|
-
numericValue = new Date(value).getTime()
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
numericValue = Number(value)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (numericValue === undefined || Number.isNaN(numericValue)) continue
|
|
119
|
-
|
|
120
|
-
if (min === undefined || numericValue < min) min = numericValue
|
|
121
|
-
if (max === undefined || numericValue > max) max = numericValue
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return { min, max }
|
|
126
|
-
})
|
|
127
|
-
|
|
128
47
|
const echartsOption = computed(() => {
|
|
129
48
|
const seriesCount = props.chart.series.length
|
|
130
49
|
if (!props.chart.series || seriesCount === 0) return
|
|
@@ -139,15 +58,12 @@ const echartsOption = computed(() => {
|
|
|
139
58
|
return null
|
|
140
59
|
}
|
|
141
60
|
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
? rawData.map(row => parseXValueAsDate(row, xColumn))
|
|
145
|
-
: [...rawData]
|
|
146
|
-
const sortBy = props.chart.x_axis.sort_x_by ?? (isDateAxis.value ? 'axis_x' : null)
|
|
61
|
+
const sortedData = [...props.series.data[resourceId]]
|
|
62
|
+
const sortBy = props.chart.x_axis.sort_x_by
|
|
147
63
|
const sortDirection = props.chart.x_axis.sort_x_direction ?? 'asc'
|
|
148
64
|
|
|
149
65
|
if (sortBy && sortDirection && props.chart.x_axis.column_x) {
|
|
150
|
-
const sortKey = sortBy === '
|
|
66
|
+
const sortKey = sortBy === 'axis_x' ? xColumn : yColumn
|
|
151
67
|
sortedData.sort((a, b) => {
|
|
152
68
|
const valA = a[sortKey]
|
|
153
69
|
const valB = b[sortKey]
|
|
@@ -220,8 +136,6 @@ const echartsOption = computed(() => {
|
|
|
220
136
|
seriesData.data.push(curr.data)
|
|
221
137
|
}
|
|
222
138
|
|
|
223
|
-
const xAxisType = mapXAxisType(props.chart.x_axis)
|
|
224
|
-
|
|
225
139
|
return {
|
|
226
140
|
dataset: [...seriesData.data],
|
|
227
141
|
textStyle: {
|
|
@@ -232,12 +146,10 @@ const echartsOption = computed(() => {
|
|
|
232
146
|
trigger: 'axis' as const,
|
|
233
147
|
formatter: (params: Array<{ value: Record<string, unknown>, axisValueLabel: string, seriesName: string }>) => {
|
|
234
148
|
let tooltip = ''
|
|
235
|
-
const
|
|
236
|
-
const xAxisFormatter = buildXAxisFormatter(xAxisColumn.value, { dateStyle: 'medium' })
|
|
149
|
+
const formatter = new Intl.NumberFormat('fr-FR')
|
|
237
150
|
for (const param of params) {
|
|
238
151
|
const seriesName = param.seriesName
|
|
239
|
-
|
|
240
|
-
tooltip += `${format.encodeHTML(String(axisLabel))}: <strong>${valueFormatter.format(Number(param.value[seriesName]))}</strong><br>`
|
|
152
|
+
tooltip += `${format.encodeHTML(param.axisValueLabel)}: <strong>${formatter.format(Number(param.value[seriesName]))}</strong><br>`
|
|
241
153
|
}
|
|
242
154
|
return tooltip
|
|
243
155
|
},
|
|
@@ -254,13 +166,8 @@ const echartsOption = computed(() => {
|
|
|
254
166
|
containLabel: true,
|
|
255
167
|
},
|
|
256
168
|
xAxis: {
|
|
257
|
-
type:
|
|
169
|
+
type: mapXAxisType(props.chart.x_axis),
|
|
258
170
|
name: (props.chart.x_axis as XAxis).column_x,
|
|
259
|
-
min: xAxisType !== 'category' ? xAxisColumn.value?.min ?? xAxisBounds.value.min ?? undefined : undefined,
|
|
260
|
-
max: xAxisType !== 'category' ? xAxisColumn.value?.max ?? xAxisBounds.value.max ?? undefined : undefined,
|
|
261
|
-
axisLabel: xAxisType === 'time'
|
|
262
|
-
? { formatter: buildXAxisFormatter(xAxisColumn.value) }
|
|
263
|
-
: undefined,
|
|
264
171
|
},
|
|
265
172
|
yAxis: {
|
|
266
173
|
type: 'value' as const,
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
>
|
|
6
6
|
<template #default="{ data }">
|
|
7
7
|
<ChartViewer
|
|
8
|
-
ref="chartViewerRef"
|
|
9
8
|
:chart="chart"
|
|
10
9
|
:series="data"
|
|
11
10
|
/>
|
|
@@ -34,13 +33,6 @@ const props = defineProps<{
|
|
|
34
33
|
}>()
|
|
35
34
|
|
|
36
35
|
const { t } = useTranslation()
|
|
37
|
-
const chartViewerRef = ref<InstanceType<typeof ChartViewer> | null>(null)
|
|
38
|
-
|
|
39
|
-
function capture(): string | null {
|
|
40
|
-
return chartViewerRef.value?.capture() ?? null
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
defineExpose({ capture })
|
|
44
36
|
const config = useComponentsConfig()
|
|
45
37
|
const getProfile = useGetProfile()
|
|
46
38
|
|
|
@@ -99,11 +99,6 @@
|
|
|
99
99
|
</p>
|
|
100
100
|
</div>
|
|
101
101
|
</div>
|
|
102
|
-
<DataserviceQualityInline
|
|
103
|
-
v-if="dataservice.quality"
|
|
104
|
-
:quality="dataservice.quality"
|
|
105
|
-
/>
|
|
106
|
-
|
|
107
102
|
<ObjectCardShortDescription
|
|
108
103
|
v-if="showDescription"
|
|
109
104
|
:text="dataservice.description"
|
|
@@ -131,7 +126,6 @@ import ObjectCardBadge from './ObjectCardBadge.vue'
|
|
|
131
126
|
import ObjectCardHeader from './ObjectCardHeader.vue'
|
|
132
127
|
import ObjectCardOwner from './ObjectCardOwner.vue'
|
|
133
128
|
import ObjectCardShortDescription from './ObjectCardShortDescription.vue'
|
|
134
|
-
import DataserviceQualityInline from './DataserviceQualityInline.vue'
|
|
135
129
|
|
|
136
130
|
type Props = {
|
|
137
131
|
dataservice: Dataservice
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{{ t('Qualité des métadonnées:') }}
|
|
14
14
|
</span>
|
|
15
15
|
</span>
|
|
16
|
-
<
|
|
16
|
+
<DatasetQualityScore
|
|
17
17
|
:score="quality.score"
|
|
18
18
|
class="w-full"
|
|
19
19
|
/>
|
|
@@ -23,35 +23,35 @@
|
|
|
23
23
|
</Toggletip>
|
|
24
24
|
<template v-if="!hideWarnings">
|
|
25
25
|
<ul class="list-none pl-0">
|
|
26
|
-
<
|
|
26
|
+
<DatasetQualityItemWarning
|
|
27
27
|
:quality-item="quality.dataset_description_quality"
|
|
28
28
|
:message="t('Description des données non renseignée')"
|
|
29
29
|
/>
|
|
30
|
-
<
|
|
30
|
+
<DatasetQualityItemWarning
|
|
31
31
|
:quality-item="quality.resources_documentation"
|
|
32
32
|
:message="t('Documentation des fichiers manquante')"
|
|
33
33
|
/>
|
|
34
|
-
<
|
|
34
|
+
<DatasetQualityItemWarning
|
|
35
35
|
:quality-item="quality.license"
|
|
36
36
|
:message="t('Licence non renseignée')"
|
|
37
37
|
/>
|
|
38
|
-
<
|
|
38
|
+
<DatasetQualityItemWarning
|
|
39
39
|
:quality-item="quality.update_frequency && quality.update_fulfilled_in_time"
|
|
40
40
|
:message="quality.update_frequency ? t('Fréquence de mise à jour non respectée') : t('Fréquence de mise à jour non renseignée')"
|
|
41
41
|
/>
|
|
42
|
-
<
|
|
42
|
+
<DatasetQualityItemWarning
|
|
43
43
|
:quality-item="quality.has_open_format"
|
|
44
44
|
:message="t('Formats de fichiers non standards')"
|
|
45
45
|
/>
|
|
46
|
-
<
|
|
46
|
+
<DatasetQualityItemWarning
|
|
47
47
|
:quality-item="quality.temporal_coverage"
|
|
48
48
|
:message="t('Couverture temporelle non renseignée')"
|
|
49
49
|
/>
|
|
50
|
-
<
|
|
50
|
+
<DatasetQualityItemWarning
|
|
51
51
|
:quality-item="quality.spatial"
|
|
52
52
|
:message="t('Couverture spatiale non renseignée')"
|
|
53
53
|
/>
|
|
54
|
-
<
|
|
54
|
+
<DatasetQualityItemWarning
|
|
55
55
|
:quality-item="quality.all_resources_available"
|
|
56
56
|
:message="t('Certains fichiers ne sont pas disponibles')"
|
|
57
57
|
/>
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
<script setup lang="ts">
|
|
63
63
|
import { RiInformationLine } from '@remixicon/vue'
|
|
64
64
|
import type { Quality } from '../types/datasets'
|
|
65
|
-
import
|
|
66
|
-
import
|
|
65
|
+
import DatasetQualityItemWarning from './DatasetQualityItemWarning.vue'
|
|
66
|
+
import DatasetQualityScore from './DatasetQualityScore.vue'
|
|
67
67
|
import Toggletip from './Toggletip.vue'
|
|
68
68
|
import DatasetQualityTooltipContent from './DatasetQualityTooltipContent.vue'
|
|
69
69
|
import { useTranslation } from '../composables/useTranslation'
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
>
|
|
8
8
|
<RiInformationLine class="size-3.5 shrink-0" />
|
|
9
9
|
<span class="text-gray-medium text-sm group-hover/quality:underline">{{ t('Métadonnées :') }}</span>
|
|
10
|
-
<
|
|
10
|
+
<DatasetQualityScore :score="quality.score" />
|
|
11
11
|
<template #toggletip>
|
|
12
12
|
<DatasetQualityTooltipContent :quality />
|
|
13
13
|
</template>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script setup lang="ts">
|
|
19
19
|
import { RiInformationLine } from '@remixicon/vue'
|
|
20
20
|
import type { Quality } from '../types/datasets'
|
|
21
|
-
import
|
|
21
|
+
import DatasetQualityScore from './DatasetQualityScore.vue'
|
|
22
22
|
import DatasetQualityTooltipContent from './DatasetQualityTooltipContent.vue'
|
|
23
23
|
import Toggletip from './Toggletip.vue'
|
|
24
24
|
import { useTranslation } from '../composables/useTranslation'
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
import type { Quality } from '../types/datasets'
|
|
69
69
|
import { useComponentsConfig } from '../config'
|
|
70
70
|
import { useTranslation } from '../composables/useTranslation'
|
|
71
|
-
import DatasetQualityItem from './
|
|
71
|
+
import DatasetQualityItem from './DatasetQualityItem.vue'
|
|
72
72
|
|
|
73
73
|
defineProps<{
|
|
74
74
|
quality: Quality
|
|
@@ -224,10 +224,6 @@
|
|
|
224
224
|
v-else-if="resource.format && resource.format.toLowerCase() === 'xml'"
|
|
225
225
|
:resource="resource"
|
|
226
226
|
/>
|
|
227
|
-
<ImagePreview
|
|
228
|
-
v-else-if="isImagePreviewFormat(resource.format)"
|
|
229
|
-
:resource="resource"
|
|
230
|
-
/>
|
|
231
227
|
<!-- Show Datafair embedded preview (koumoul) -->
|
|
232
228
|
<DatafairPreview
|
|
233
229
|
v-else-if="hasDatafairPreview"
|
|
@@ -313,7 +309,7 @@ import { trackEvent } from '../../functions/matomo'
|
|
|
313
309
|
import CopyButton from '../CopyButton.vue'
|
|
314
310
|
import { useComponentsConfig } from '../../config'
|
|
315
311
|
import { getOwnerName } from '../../functions/owned'
|
|
316
|
-
import { getResourceFormatIcon, getResourceTitleId, detectOgcService, getResourceExternalUrl, getResourceFilesize
|
|
312
|
+
import { getResourceFormatIcon, getResourceTitleId, detectOgcService, getResourceExternalUrl, getResourceFilesize } from '../../functions/resources'
|
|
317
313
|
import BrandedButton from '../BrandedButton.vue'
|
|
318
314
|
import { useTranslation } from '../../composables/useTranslation'
|
|
319
315
|
import { useHasTabularData } from '../../composables/useHasTabularData'
|
|
@@ -348,7 +344,6 @@ const Pmtiles = defineAsyncComponent(() => import('./Pmtiles.client.vue'))
|
|
|
348
344
|
const JsonPreview = defineAsyncComponent(() => import('./JsonPreview.client.vue'))
|
|
349
345
|
const PdfPreview = defineAsyncComponent(() => import('./PdfPreview.client.vue'))
|
|
350
346
|
const XmlPreview = defineAsyncComponent(() => import('./XmlPreview.client.vue'))
|
|
351
|
-
const ImagePreview = defineAsyncComponent(() => import('./ImagePreview.client.vue'))
|
|
352
347
|
const DatafairPreview = defineAsyncComponent(() => import('./Datafair.client.vue'))
|
|
353
348
|
|
|
354
349
|
const { t } = useTranslation()
|
|
@@ -356,11 +351,11 @@ const { formatRelativeIfRecentDate } = useFormatDate()
|
|
|
356
351
|
const checkTabularData = useHasTabularData()
|
|
357
352
|
|
|
358
353
|
const hasPreview = computed(() => {
|
|
359
|
-
// For JSON, PDF,
|
|
354
|
+
// For JSON, PDF, and XML files, show preview.
|
|
360
355
|
// We cannot check for CORS issues here because we cannot use an async component here.
|
|
361
356
|
// 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.
|
|
362
357
|
const format = props.resource.format?.toLowerCase()
|
|
363
|
-
return format === 'json' || format === 'pdf' || format === 'xml'
|
|
358
|
+
return format === 'json' || format === 'pdf' || format === 'xml'
|
|
364
359
|
})
|
|
365
360
|
|
|
366
361
|
const hasTabularData = computed(() => checkTabularData(props.resource))
|
|
@@ -122,11 +122,6 @@
|
|
|
122
122
|
v-if="ogcWms"
|
|
123
123
|
:resource="resource"
|
|
124
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>
|
|
130
125
|
</div>
|
|
131
126
|
<div v-if="tab.key === 'data'">
|
|
132
127
|
<JsonPreview
|
|
@@ -141,10 +136,6 @@
|
|
|
141
136
|
v-else-if="resource.format && resource.format.toLowerCase() === 'xml'"
|
|
142
137
|
:resource="resource"
|
|
143
138
|
/>
|
|
144
|
-
<ImagePreview
|
|
145
|
-
v-else-if="isImagePreviewFormat(resource.format)"
|
|
146
|
-
:resource="resource"
|
|
147
|
-
/>
|
|
148
139
|
<DatafairPreview
|
|
149
140
|
v-else-if="hasDatafairPreview"
|
|
150
141
|
:resource="resource"
|
|
@@ -238,7 +229,7 @@ import Metadata from '../ResourceAccordion/Metadata.vue'
|
|
|
238
229
|
import SchemaBadge from '../ResourceAccordion/SchemaBadge.vue'
|
|
239
230
|
import ResourceSelector from './ResourceSelector.vue'
|
|
240
231
|
import { filesize, summarize } from '../../functions/helpers'
|
|
241
|
-
import { getResourceFormatIcon, getResourceExternalUrl, getResourceFilesize
|
|
232
|
+
import { getResourceFormatIcon, getResourceExternalUrl, getResourceFilesize } from '../../functions/resources'
|
|
242
233
|
import { trackEvent } from '../../functions/matomo'
|
|
243
234
|
import { useComponentsConfig } from '../../config'
|
|
244
235
|
import { useFormatDate } from '../../functions/dates'
|
|
@@ -257,9 +248,6 @@ const PdfPreview = defineAsyncComponent(() =>
|
|
|
257
248
|
const XmlPreview = defineAsyncComponent(() =>
|
|
258
249
|
import('../ResourceAccordion/XmlPreview.client.vue'),
|
|
259
250
|
)
|
|
260
|
-
const ImagePreview = defineAsyncComponent(() =>
|
|
261
|
-
import('../ResourceAccordion/ImagePreview.client.vue'),
|
|
262
|
-
)
|
|
263
251
|
const DatafairPreview = defineAsyncComponent(() =>
|
|
264
252
|
import('../ResourceAccordion/Datafair.client.vue'),
|
|
265
253
|
)
|
|
@@ -287,8 +275,6 @@ const { formatRelativeIfRecentDate } = useFormatDate()
|
|
|
287
275
|
const {
|
|
288
276
|
hasTabularData,
|
|
289
277
|
hasPmtiles,
|
|
290
|
-
hasPmtilesError,
|
|
291
|
-
pmtilesError,
|
|
292
278
|
hasDatafairPreview,
|
|
293
279
|
hasOpenAPIPreview,
|
|
294
280
|
ogcService,
|
|
@@ -68,7 +68,7 @@ import { RiLockLine, RiSubtractLine } from '@remixicon/vue'
|
|
|
68
68
|
import { computed } from 'vue'
|
|
69
69
|
import type { RouteLocationRaw } from 'vue-router'
|
|
70
70
|
import { useFormatDate } from '../functions/dates'
|
|
71
|
-
import type { Reuse
|
|
71
|
+
import type { Reuse } from '../types/reuses'
|
|
72
72
|
import type { TitleTag } from '../types/ui'
|
|
73
73
|
import { useTranslation } from '../composables/useTranslation'
|
|
74
74
|
import AppLink from './AppLink.vue'
|
|
@@ -77,7 +77,7 @@ import ReuseDetails from './ReuseDetails.vue'
|
|
|
77
77
|
import Placeholder from './Placeholder.vue'
|
|
78
78
|
|
|
79
79
|
const props = withDefaults(defineProps<{
|
|
80
|
-
reuse: Reuse
|
|
80
|
+
reuse: Reuse
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
83
|
* The reuseUrl is a route location object to allow Vue Router to navigate to the details of a reuse.
|
|
@@ -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
|
|
35
|
+
import type { Reuse } from '../types/reuses'
|
|
36
36
|
import { useTranslation } from '../composables/useTranslation'
|
|
37
37
|
|
|
38
38
|
const props = defineProps<{
|
|
39
|
-
reuse: Reuse
|
|
39
|
+
reuse: Reuse
|
|
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
|
|
63
|
+
import type { Reuse } 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
|
|
75
75
|
reuseUrl?: RouteLocationRaw
|
|
76
76
|
organizationUrl?: RouteLocationRaw
|
|
77
77
|
}>()
|
|
@@ -266,7 +266,7 @@
|
|
|
266
266
|
name="reuse"
|
|
267
267
|
:reuse="result"
|
|
268
268
|
>
|
|
269
|
-
<ReuseHorizontalCard :reuse="(result as
|
|
269
|
+
<ReuseHorizontalCard :reuse="(result as Reuse)" />
|
|
270
270
|
</slot>
|
|
271
271
|
</template>
|
|
272
272
|
<template v-else-if="currentTypeConfig?.class === 'organizations'">
|
|
@@ -368,7 +368,7 @@ import type { AsyncDataRequestStatus } from '../../functions/api.types'
|
|
|
368
368
|
import type { Dataset } from '../../types/datasets'
|
|
369
369
|
import type { Dataservice } from '../../types/dataservices'
|
|
370
370
|
import type { Organization } from '../../types/organizations'
|
|
371
|
-
import type {
|
|
371
|
+
import type { Reuse } from '../../types/reuses'
|
|
372
372
|
import type { TopicV2 } from '../../types/topics'
|
|
373
373
|
import type { GlobalSearchConfig, SearchResponseByClass, SearchType, SortOption, FacetItem } from '../../types/search'
|
|
374
374
|
import { getDefaultGlobalSearchConfig } from '../../types/search'
|
|
@@ -597,7 +597,6 @@
|
|
|
597
597
|
<script setup lang="ts">
|
|
598
598
|
import { computed, nextTick, onUnmounted, ref, watch, useTemplateRef } from 'vue'
|
|
599
599
|
import { ofetch } from 'ofetch'
|
|
600
|
-
import { useElementSize } from '@vueuse/core'
|
|
601
600
|
import { flip, shift, autoUpdate, useFloating } from '@floating-ui/vue'
|
|
602
601
|
import { Dialog, DialogPanel, DialogTitle, Popover, PopoverButton, PopoverPanel, TransitionChild, TransitionRoot } from '@headlessui/vue'
|
|
603
602
|
import {
|
|
@@ -833,43 +832,18 @@ function redistributeColumnWidths() {
|
|
|
833
832
|
columnWidths.value = widths
|
|
834
833
|
}
|
|
835
834
|
|
|
836
|
-
// Measure
|
|
837
|
-
//
|
|
838
|
-
function ensureMeasured() {
|
|
839
|
-
if (Object.keys(naturalWidths.value).length > 0) return
|
|
840
|
-
if (displayedColumns.value.length === 0) return
|
|
841
|
-
measureNaturalWidths()
|
|
842
|
-
redistributeColumnWidths()
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
// Drive measurement and re-stretch off the container's reactive width
|
|
846
|
-
// (`useElementSize` wraps a ResizeObserver and disconnects it on unmount). The
|
|
847
|
-
// width goes 0 -> N once the table is laid out in the DOM, which is also when
|
|
848
|
-
// the <thead> carries its content-driven natural widths: the first non-zero
|
|
849
|
-
// width measures the stable base, later changes (window resize) only
|
|
850
|
-
// redistribute it.
|
|
851
|
-
const { width: containerWidth } = useElementSize(scrollContainerRef)
|
|
852
|
-
|
|
853
|
-
watch(containerWidth, (width) => {
|
|
854
|
-
if (width === 0) return
|
|
855
|
-
if (Object.keys(naturalWidths.value).length === 0) {
|
|
856
|
-
ensureMeasured()
|
|
857
|
-
}
|
|
858
|
-
else {
|
|
859
|
-
redistributeColumnWidths()
|
|
860
|
-
}
|
|
861
|
-
})
|
|
862
|
-
|
|
863
|
-
// Fallback: re-run once columns appear if they weren't available when the
|
|
864
|
-
// container was first sized (standalone usage where the profile resolves late).
|
|
835
|
+
// Measure once both data and profile are ready (so the <thead> is rendered with
|
|
836
|
+
// content-driven natural widths), then distribute.
|
|
865
837
|
watch(
|
|
866
838
|
() => [tableData.value !== null, displayedColumns.value.length] as const,
|
|
867
839
|
async ([dataReady, colCount]) => {
|
|
868
840
|
if (!dataReady || colCount === 0) return
|
|
869
841
|
if (Object.keys(naturalWidths.value).length > 0) return
|
|
870
842
|
await nextTick()
|
|
871
|
-
|
|
843
|
+
measureNaturalWidths()
|
|
844
|
+
redistributeColumnWidths()
|
|
872
845
|
},
|
|
846
|
+
{ immediate: true },
|
|
873
847
|
)
|
|
874
848
|
|
|
875
849
|
// Re-stretch the remaining columns whenever the displayed set changes (columns
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useComponentsConfig } from '../config'
|
|
2
|
-
import { hasTabularParsingError } from '../functions/resources'
|
|
3
2
|
import type { Resource } from '../types/resources'
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -21,7 +20,7 @@ export const useHasTabularData = () => {
|
|
|
21
20
|
return (
|
|
22
21
|
config.tabularApiUrl
|
|
23
22
|
&& resource.extras['analysis:parsing:parsing_table']
|
|
24
|
-
&& !
|
|
23
|
+
&& !resource.extras['analysis:parsing:error']
|
|
25
24
|
&& !sourceUnreachable
|
|
26
25
|
&& (config.tabularAllowRemote || resource.filetype === 'file')
|
|
27
26
|
)
|