@datagouv/components-next 1.0.2-dev.172 → 1.0.2-dev.174
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-CES67S3v.js → Datafair.client-BU2gspr8.js} +9 -9
- package/dist/ImagePreview.client-BU5QNqG_.js +36 -0
- package/dist/{JsonPreview.client-DtCw5aCA.js → JsonPreview.client-CvOpMCxW.js} +2 -2
- package/dist/{MapContainer.client-Q3TmXpPR.js → MapContainer.client-DNOQ5XTT.js} +9 -9
- package/dist/{PdfPreview.client-BXJzS-Hm.js → PdfPreview.client-DwAvLLq_.js} +438 -427
- package/dist/{Pmtiles.client-DNiYcHH3.js → Pmtiles.client-CgaDrCHk.js} +1341 -1337
- package/dist/PreviewWrapper.vue_vue_type_script_setup_true_lang-CrWEJnPl.js +60 -0
- package/dist/{XmlPreview.client-CPfIZl9q.js → XmlPreview.client-B24FIhU7.js} +3 -3
- package/dist/components-next.css +1 -1
- package/dist/components-next.js +144 -138
- package/dist/components.css +1 -1
- package/dist/{index-CXeG6J6C.js → index-Dw1lR0oe.js} +1 -1
- package/dist/{leaflet-src-BwEce6uO.js → leaflet-src-BoMqG6Yx.js} +1 -1
- package/dist/{main-D7EaQcIR.js → main-I9G-L-lg.js} +16280 -15511
- package/dist/{vue3-xml-viewer.common-DznVepCg.js → vue3-xml-viewer.common-PTUJZpH4.js} +1 -1
- package/package.json +1 -1
- package/src/components/AppLink.vue +3 -0
- package/src/components/CopyButton.vue +6 -2
- package/src/components/ResourceAccordion/Datafair.client.vue +1 -1
- package/src/components/ResourceAccordion/ImagePreview.client.vue +3 -3
- package/src/components/ResourceAccordion/MapContainer.client.vue +1 -8
- package/src/components/ResourceAccordion/PdfPreview.client.vue +36 -12
- package/src/components/ResourceAccordion/PdfSkeleton.vue +44 -0
- package/src/components/ResourceAccordion/Pmtiles.client.vue +25 -12
- package/src/components/ResourceAccordion/PreviewWrapper.vue +17 -10
- package/src/components/ResourceAccordion/ResourceAccordion.vue +4 -1
- package/src/components/ResourceExplorer/ResourceExplorer.vue +98 -209
- package/src/components/ResourceExplorer/ResourceExplorerHeader.vue +74 -0
- package/src/components/ResourceExplorer/ResourceExplorerSidebar.vue +103 -71
- package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +123 -187
- package/src/components/ResourceExplorer/ResourceMainAction.vue +240 -0
- package/src/components/ResourceExplorer/ResourceSelector.vue +24 -86
- package/src/components/ResourceExplorer/ResourceViewerHeader.vue +115 -0
- package/src/components/ResourceExplorer/ResourceViewerSkeleton.vue +94 -0
- package/src/components/ResourceListItem.vue +154 -0
- package/src/components/TabularExplorer/TabularActiveFilters.vue +79 -0
- package/src/components/TabularExplorer/TabularCell.vue +2 -4
- package/src/components/TabularExplorer/TabularColumnsMenu.vue +112 -0
- package/src/components/TabularExplorer/TabularExplorer.vue +68 -995
- package/src/components/TabularExplorer/TabularFilterContent.vue +2 -10
- package/src/components/TabularExplorer/TabularFilterPopover.vue +5 -6
- package/src/components/TabularExplorer/TabularMobileFilterButton.vue +21 -0
- package/src/components/TabularExplorer/TabularMobileFilters.vue +150 -0
- package/src/components/TabularExplorer/TabularRowsInfo.vue +31 -0
- package/src/components/TabularExplorer/TabularSkeleton.vue +92 -0
- package/src/components/TabularExplorer/TabularTable.vue +481 -0
- package/src/components/TabularExplorer/TabularToolbar.vue +25 -0
- package/src/components/TabularExplorer/types.ts +0 -5
- package/src/components/TabularExplorer/useColumnMetadata.ts +98 -0
- package/src/components/TabularExplorer/useTabularContext.ts +82 -0
- package/src/composables/useDatasetResources.ts +164 -0
- package/src/composables/useResizable.ts +46 -0
- package/src/composables/useResourceCapabilities.ts +20 -7
- package/src/functions/resources.ts +19 -0
- package/src/main.ts +8 -0
- package/dist/ImagePreview.client-c_SJXLpr.js +0 -36
- package/dist/PreviewWrapper.vue_vue_type_script_setup_true_lang-B3ByRwHK.js +0 -58
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { computed, ref, toValue, watch, type MaybeRefOrGetter, type Ref } from 'vue'
|
|
2
|
+
import { useRouteQuery } from '@vueuse/router'
|
|
3
|
+
import { useComponentsConfig } from '../config'
|
|
4
|
+
import { useDebouncedRef } from './useDebouncedRef'
|
|
5
|
+
import { useFetch } from '../functions/api'
|
|
6
|
+
import { RESOURCE_EXPLORER_PAGE_SIZE, RESOURCE_TYPE } from '../functions/resources'
|
|
7
|
+
import type { PaginatedArray } from '../types/api'
|
|
8
|
+
import type { DatasetV2 } from '../types/datasets'
|
|
9
|
+
import type { Resource, ResourceGroup, ResourceType } from '../types/resources'
|
|
10
|
+
|
|
11
|
+
// Fetches a dataset's resources grouped by type (paginated per type, searchable)
|
|
12
|
+
// and derives the currently selected resource from the `resource_id` query param.
|
|
13
|
+
// Shared by the dataset page and the explore page so the left navigation behaves
|
|
14
|
+
// identically in both.
|
|
15
|
+
export async function useDatasetResources(datasetGetter: MaybeRefOrGetter<DatasetV2>) {
|
|
16
|
+
const config = useComponentsConfig()
|
|
17
|
+
|
|
18
|
+
const search = ref('')
|
|
19
|
+
const { debounced: searchDebounced, flush } = useDebouncedRef(search, config.searchDebounce ?? 300)
|
|
20
|
+
|
|
21
|
+
const datasetId = computed(() => toValue(datasetGetter).id)
|
|
22
|
+
const url = computed(() => `/api/2/datasets/${datasetId.value}/resources/`)
|
|
23
|
+
|
|
24
|
+
const makeParams = (type: ResourceType) => computed(() => ({
|
|
25
|
+
type,
|
|
26
|
+
page_size: RESOURCE_EXPLORER_PAGE_SIZE,
|
|
27
|
+
q: searchDebounced.value || undefined,
|
|
28
|
+
}))
|
|
29
|
+
|
|
30
|
+
// Evaluated once at setup (before any search) — never changes afterwards
|
|
31
|
+
const hasAnyResources = computed(() => toValue(datasetGetter).resources.total > 0)
|
|
32
|
+
|
|
33
|
+
const extraResourcesByType: Ref<Resource[]>[] = RESOURCE_TYPE.map(() => ref<Resource[]>([]))
|
|
34
|
+
const pageByType: Ref<number>[] = RESOURCE_TYPE.map(() => ref(1))
|
|
35
|
+
|
|
36
|
+
watch(searchDebounced, () => {
|
|
37
|
+
for (let i = 0; i < RESOURCE_TYPE.length; i++) {
|
|
38
|
+
extraResourcesByType[i]!.value = []
|
|
39
|
+
pageByType[i]!.value = 1
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// Separate useFetch for loadMore, initialized with immediate: false so it doesn't
|
|
44
|
+
// fetch until execute() is called from the event handler.
|
|
45
|
+
const loadMoreType = ref<ResourceType>('main')
|
|
46
|
+
const loadMorePage = ref(1)
|
|
47
|
+
const loadMoreParams = computed(() => ({
|
|
48
|
+
type: loadMoreType.value,
|
|
49
|
+
page_size: RESOURCE_EXPLORER_PAGE_SIZE,
|
|
50
|
+
page: loadMorePage.value,
|
|
51
|
+
q: searchDebounced.value || undefined,
|
|
52
|
+
}))
|
|
53
|
+
|
|
54
|
+
const resourceIdQuery = useRouteQuery<string | undefined>('resource_id')
|
|
55
|
+
const initialResourceId = resourceIdQuery.value
|
|
56
|
+
|
|
57
|
+
// Vue rule: composables must be called synchronously, before any `await` — `inject()`
|
|
58
|
+
// needs the active component instance, which a plain .ts composable loses after its
|
|
59
|
+
// first await (documented in useTabularProfile.ts). The package's `useFetch` calls
|
|
60
|
+
// `useComponentsConfig()` (an inject) on every call, so we kick off every request
|
|
61
|
+
// up-front, then await them together. This is also the idiomatic Nuxt parallel-fetch.
|
|
62
|
+
const typeFetches = RESOURCE_TYPE.map(type =>
|
|
63
|
+
useFetch<PaginatedArray<Resource>>(url, {
|
|
64
|
+
params: makeParams(type),
|
|
65
|
+
// Only main is server-rendered; the rest hydrate client-side to keep SSR light.
|
|
66
|
+
server: type === 'main',
|
|
67
|
+
}),
|
|
68
|
+
)
|
|
69
|
+
const loadMoreFetch = useFetch<PaginatedArray<Resource>>(url, { params: loadMoreParams, immediate: false, watch: false })
|
|
70
|
+
// The resource pointed at by `resource_id` (SSR / deep link) may live beyond the first
|
|
71
|
+
// page of its type group, so it wouldn't be in flatResources — fetch it directly.
|
|
72
|
+
const fetchedResourceFetch = initialResourceId
|
|
73
|
+
? useFetch<Resource>(`/api/1/datasets/${datasetId.value}/resources/${initialResourceId}/`)
|
|
74
|
+
: null
|
|
75
|
+
|
|
76
|
+
const typeResults = await Promise.all(typeFetches)
|
|
77
|
+
const { data: loadMoreData, execute: executeLoadMore } = await loadMoreFetch
|
|
78
|
+
const fetchedResource = fetchedResourceFetch ? (await fetchedResourceFetch).data : ref<Resource | null>(null)
|
|
79
|
+
|
|
80
|
+
const rawDataByType = typeResults.map(result => result.data)
|
|
81
|
+
|
|
82
|
+
// Which type group is currently loading its next page. A single loadMoreFetch is
|
|
83
|
+
// shared across types, so we track the type here to show the spinner on the right
|
|
84
|
+
// group's "Charger plus…" button.
|
|
85
|
+
const loadingType = ref<ResourceType | null>(null)
|
|
86
|
+
|
|
87
|
+
const loadMore = async (type: ResourceType) => {
|
|
88
|
+
const index = RESOURCE_TYPE.indexOf(type)
|
|
89
|
+
if (index === -1) return
|
|
90
|
+
const pageRef = pageByType[index]!
|
|
91
|
+
const extraRef = extraResourcesByType[index]!
|
|
92
|
+
pageRef.value++
|
|
93
|
+
|
|
94
|
+
loadMoreType.value = type
|
|
95
|
+
loadMorePage.value = pageRef.value
|
|
96
|
+
loadingType.value = type
|
|
97
|
+
try {
|
|
98
|
+
await executeLoadMore()
|
|
99
|
+
|
|
100
|
+
if (loadMoreData.value) {
|
|
101
|
+
extraRef.value = [...extraRef.value, ...loadMoreData.value.data]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
loadingType.value = null
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const groups = computed<ResourceGroup[]>(() => {
|
|
110
|
+
return RESOURCE_TYPE
|
|
111
|
+
.map((type, index) => {
|
|
112
|
+
const rawData = rawDataByType[index]!
|
|
113
|
+
const extraData = extraResourcesByType[index]!
|
|
114
|
+
return {
|
|
115
|
+
type: type as ResourceType,
|
|
116
|
+
total: rawData.value?.total ?? 0,
|
|
117
|
+
items: [...(rawData.value?.data ?? []), ...extraData.value],
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
.filter(group => group.items.length > 0)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const flatResources = computed(() => groups.value.flatMap(g => g.items))
|
|
124
|
+
|
|
125
|
+
// The URL is the single source of truth for the selection: no ref to keep in
|
|
126
|
+
// sync, navigation is done with plain links that change `resource_id`.
|
|
127
|
+
let lastShown: Resource | null = null
|
|
128
|
+
const remember = (resource: Resource | null) => {
|
|
129
|
+
if (resource) lastShown = resource
|
|
130
|
+
return resource
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const selectedResource = computed<Resource | null>(() => {
|
|
134
|
+
const wantedId = resourceIdQuery.value
|
|
135
|
+
const fromList = flatResources.value.find(r => r.id === wantedId)
|
|
136
|
+
if (fromList) return remember(fromList)
|
|
137
|
+
// `fetchedResource` answers for `initialResourceId` only: it holds that one
|
|
138
|
+
// resource, so using it for any other id would show something the URL doesn't
|
|
139
|
+
// point at.
|
|
140
|
+
if (wantedId && wantedId === initialResourceId) return remember(fetchedResource.value)
|
|
141
|
+
// A search that matches nothing narrows the navigation, it doesn't unload the
|
|
142
|
+
// viewer — keep showing what we had, which is the dataset's first resource when
|
|
143
|
+
// nothing has been clicked yet.
|
|
144
|
+
return remember(flatResources.value[0] ?? null) ?? lastShown
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
function updateSearch(newSearch: string) {
|
|
148
|
+
search.value = newSearch
|
|
149
|
+
// Only a reset skips the debounce: typing must stay debounced, since each
|
|
150
|
+
// change fires one request per resource type.
|
|
151
|
+
if (!newSearch) flush()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
groups,
|
|
156
|
+
flatResources,
|
|
157
|
+
hasAnyResources,
|
|
158
|
+
selectedResource,
|
|
159
|
+
loadMore,
|
|
160
|
+
loadingType,
|
|
161
|
+
search,
|
|
162
|
+
updateSearch,
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
import { useEventListener } from '@vueuse/core'
|
|
3
|
+
|
|
4
|
+
type UseResizableOptions = {
|
|
5
|
+
initialWidth: number
|
|
6
|
+
minWidth: number
|
|
7
|
+
maxWidth: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Drag-to-resize a horizontal panel. Returns the reactive width, a `resizing`
|
|
11
|
+
// flag (to disable width transitions while dragging) and a `startResize` handler
|
|
12
|
+
// to bind on a drag handle's `mousedown`. Encapsulates the drag math, clamping and
|
|
13
|
+
// the temporary body cursor / text-selection lock. VueUse has no ready-made panel
|
|
14
|
+
// resize (useResizeObserver only observes size), so we build it on `useEventListener`
|
|
15
|
+
// which handles listener cleanup and SSR for us.
|
|
16
|
+
export function useResizable({ initialWidth, minWidth, maxWidth }: UseResizableOptions) {
|
|
17
|
+
const width = ref(initialWidth)
|
|
18
|
+
const resizing = ref(false)
|
|
19
|
+
let startX = 0
|
|
20
|
+
let startWidth = 0
|
|
21
|
+
|
|
22
|
+
const clamp = (value: number) => Math.min(maxWidth, Math.max(minWidth, Math.round(value)))
|
|
23
|
+
|
|
24
|
+
function startResize(event: MouseEvent) {
|
|
25
|
+
event.preventDefault()
|
|
26
|
+
startX = event.clientX
|
|
27
|
+
startWidth = width.value
|
|
28
|
+
resizing.value = true
|
|
29
|
+
document.body.style.userSelect = 'none'
|
|
30
|
+
document.body.style.cursor = 'col-resize'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
useEventListener('mousemove', (event: MouseEvent) => {
|
|
34
|
+
if (!resizing.value) return
|
|
35
|
+
width.value = clamp(startWidth + event.clientX - startX)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
useEventListener('mouseup', () => {
|
|
39
|
+
if (!resizing.value) return
|
|
40
|
+
resizing.value = false
|
|
41
|
+
document.body.style.userSelect = ''
|
|
42
|
+
document.body.style.cursor = ''
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
return { width, resizing, startResize }
|
|
46
|
+
}
|
|
@@ -11,6 +11,11 @@ import { useTranslation } from './useTranslation'
|
|
|
11
11
|
const GENERATED_FORMATS = ['parquet', 'pmtiles', 'geojson']
|
|
12
12
|
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']
|
|
13
13
|
|
|
14
|
+
// Which preview the data tab shows. Single source of truth: the viewer renders it and
|
|
15
|
+
// the skeleton picks the matching placeholder shape, so each holding its own copy of
|
|
16
|
+
// this precedence would let the two drift apart.
|
|
17
|
+
export type PreviewKind = 'json' | 'pdf' | 'xml' | 'image' | 'datafair' | 'openapi' | 'tabular' | 'unavailable'
|
|
18
|
+
|
|
14
19
|
export function useResourceCapabilities(
|
|
15
20
|
resource: MaybeRefOrGetter<Resource>,
|
|
16
21
|
dataset: MaybeRefOrGetter<Dataset | DatasetV2>,
|
|
@@ -19,11 +24,6 @@ export function useResourceCapabilities(
|
|
|
19
24
|
const { t } = useTranslation()
|
|
20
25
|
const checkTabularData = useHasTabularData()
|
|
21
26
|
|
|
22
|
-
const hasPreview = computed(() => {
|
|
23
|
-
const format = toValue(resource).format?.toLowerCase()
|
|
24
|
-
return format === 'json' || format === 'pdf' || format === 'xml' || isImagePreviewFormat(format)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
27
|
const hasTabularData = computed(() => {
|
|
28
28
|
const r = toValue(resource)
|
|
29
29
|
return checkTabularData(r)
|
|
@@ -86,6 +86,20 @@ export function useResourceCapabilities(
|
|
|
86
86
|
|
|
87
87
|
const isResourceUrl = computed(() => URL_FORMATS.includes(toValue(resource).format))
|
|
88
88
|
|
|
89
|
+
// A dedicated preview always wins over the interactive table: a PDF that also
|
|
90
|
+
// carries a parsing table is a PDF first.
|
|
91
|
+
const previewKind = computed<PreviewKind>(() => {
|
|
92
|
+
const format = toValue(resource).format?.toLowerCase()
|
|
93
|
+
if (format === 'json') return 'json'
|
|
94
|
+
if (format === 'pdf') return 'pdf'
|
|
95
|
+
if (format === 'xml') return 'xml'
|
|
96
|
+
if (isImagePreviewFormat(format)) return 'image'
|
|
97
|
+
if (hasDatafairPreview.value) return 'datafair'
|
|
98
|
+
if (hasOpenAPIPreview.value) return 'openapi'
|
|
99
|
+
if (hasTabularData.value) return 'tabular'
|
|
100
|
+
return 'unavailable'
|
|
101
|
+
})
|
|
102
|
+
|
|
89
103
|
const tabsOptions = computed(() => {
|
|
90
104
|
const r = toValue(resource)
|
|
91
105
|
const options = []
|
|
@@ -116,7 +130,6 @@ export function useResourceCapabilities(
|
|
|
116
130
|
}
|
|
117
131
|
|
|
118
132
|
options.push({ key: 'metadata', label: t('Métadonnées') })
|
|
119
|
-
options.push({ key: 'downloads', label: t('Téléchargements') })
|
|
120
133
|
|
|
121
134
|
if (hasTabularData.value) {
|
|
122
135
|
options.push({ key: 'api', label: t('API') })
|
|
@@ -126,7 +139,7 @@ export function useResourceCapabilities(
|
|
|
126
139
|
})
|
|
127
140
|
|
|
128
141
|
return {
|
|
129
|
-
|
|
142
|
+
previewKind,
|
|
130
143
|
hasTabularData,
|
|
131
144
|
hasPmtiles,
|
|
132
145
|
hasPmtilesError,
|
|
@@ -84,6 +84,25 @@ export function getResourceFormatIcon(format: string): Component | null {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// Badge colors for the resource icon, keyed on the same format categorization as
|
|
88
|
+
// getResourceFormatIcon so tabular/geo/code/documentation stay consistent with the icon.
|
|
89
|
+
export function getResourceIconColor(format?: string | null): string {
|
|
90
|
+
const icon = format ? getResourceFormatIcon(format) : null
|
|
91
|
+
switch (icon) {
|
|
92
|
+
case Table:
|
|
93
|
+
return 'bg-[#c3fad5] text-[#18753c]'
|
|
94
|
+
case RiMap2Line:
|
|
95
|
+
case RiEarthLine:
|
|
96
|
+
return 'bg-[#e6eefe] text-[#0063cb]'
|
|
97
|
+
case Code:
|
|
98
|
+
return 'bg-[#fce164] text-[#716043]'
|
|
99
|
+
case Documentation:
|
|
100
|
+
return 'bg-[#fee7fc] text-[#6e445a]'
|
|
101
|
+
default:
|
|
102
|
+
return 'bg-gray-100 text-gray-plain'
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
87
106
|
export function getResourceTitleId(resource: Resource) {
|
|
88
107
|
return 'resource-' + resource.id + '-title'
|
|
89
108
|
}
|
package/src/main.ts
CHANGED
|
@@ -108,6 +108,9 @@ import SelectGroup from './components/Form/SelectGroup.vue'
|
|
|
108
108
|
import Listbox from './components/Form/Listbox.vue'
|
|
109
109
|
import InfiniteLoader from './components/InfiniteLoader.vue'
|
|
110
110
|
import TabularExplorer from './components/TabularExplorer/TabularExplorer.vue'
|
|
111
|
+
import TabularMobileFilters from './components/TabularExplorer/TabularMobileFilters.vue'
|
|
112
|
+
import TabularTable from './components/TabularExplorer/TabularTable.vue'
|
|
113
|
+
import TabularToolbar from './components/TabularExplorer/TabularToolbar.vue'
|
|
111
114
|
import type { UseFetchFunction } from './functions/api.types'
|
|
112
115
|
import { configKey, useComponentsConfig, type PluginConfig } from './config.js'
|
|
113
116
|
import { ofetch } from 'ofetch'
|
|
@@ -123,6 +126,8 @@ export * from './composables/useTranslation'
|
|
|
123
126
|
export * from './composables/useHasTabularData'
|
|
124
127
|
export * from './composables/useResourceCapabilities'
|
|
125
128
|
export * from './composables/useTabularProfile'
|
|
129
|
+
export * from './composables/useDatasetResources'
|
|
130
|
+
export * from './composables/useResizable'
|
|
126
131
|
|
|
127
132
|
export * from './functions/activities'
|
|
128
133
|
export * from './functions/datasets'
|
|
@@ -413,4 +418,7 @@ export {
|
|
|
413
418
|
Listbox,
|
|
414
419
|
InfiniteLoader,
|
|
415
420
|
TabularExplorer,
|
|
421
|
+
TabularMobileFilters,
|
|
422
|
+
TabularTable,
|
|
423
|
+
TabularToolbar,
|
|
416
424
|
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { defineComponent as T, ref as _, computed as l, openBlock as r, createElementBlock as u, createBlock as p, withCtx as f, createTextVNode as m, toDisplayString as g, unref as o } from "vue";
|
|
2
|
-
import { a as y, u as z, b as k, _ as d } from "./main-D7EaQcIR.js";
|
|
3
|
-
const w = { class: "text-xs" }, B = ["src"], E = /* @__PURE__ */ T({
|
|
4
|
-
__name: "ImagePreview.client",
|
|
5
|
-
props: {
|
|
6
|
-
resource: {}
|
|
7
|
-
},
|
|
8
|
-
setup(a) {
|
|
9
|
-
const n = a, { t } = y(), h = z(), i = _(!1), v = l(() => n.resource.format?.toUpperCase() ?? t("Fichier")), c = l(() => k(n.resource)), x = l(() => {
|
|
10
|
-
const s = c.value, e = h.maxImagePreviewByteSize;
|
|
11
|
-
return !s || !e ? !1 : s <= e;
|
|
12
|
-
});
|
|
13
|
-
return (s, e) => (r(), u("div", w, [
|
|
14
|
-
x.value ? i.value ? (r(), p(d, { key: 1 }, {
|
|
15
|
-
default: f(() => [
|
|
16
|
-
m(g(o(t)("L'aperçu de ce fichier n'a pas pu être chargé. Téléchargez-le depuis l'onglet Téléchargements.")), 1)
|
|
17
|
-
]),
|
|
18
|
-
_: 1
|
|
19
|
-
})) : (r(), u("img", {
|
|
20
|
-
key: 2,
|
|
21
|
-
src: a.resource.url,
|
|
22
|
-
alt: "",
|
|
23
|
-
class: "block w-full h-auto max-w-[800px]",
|
|
24
|
-
onError: e[0] || (e[0] = (C) => i.value = !0)
|
|
25
|
-
}, null, 40, B)) : (r(), p(d, { key: 0 }, {
|
|
26
|
-
default: f(() => [
|
|
27
|
-
m(g(c.value ? o(t)("Le fichier {fileType} est trop volumineux pour être prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.", { fileType: v.value }) : o(t)("La taille du fichier est inconnue, l'aperçu n'est pas disponible. Téléchargez-le depuis l'onglet Téléchargements.")), 1)
|
|
28
|
-
]),
|
|
29
|
-
_: 1
|
|
30
|
-
}))
|
|
31
|
-
]));
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
export {
|
|
35
|
-
E as default
|
|
36
|
-
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { defineComponent as _, ref as g, computed as v, onMounted as S, nextTick as w, openBlock as l, createElementBlock as k, renderSlot as C, toDisplayString as r, unref as a, createBlock as n, withCtx as o, createTextVNode as u, createCommentVNode as b } from "vue";
|
|
2
|
-
import { a as B, b as E, c as L, _ as c } from "./main-D7EaQcIR.js";
|
|
3
|
-
const R = { class: "text-xs" }, q = {
|
|
4
|
-
key: 1,
|
|
5
|
-
class: "text-gray-medium"
|
|
6
|
-
}, V = /* @__PURE__ */ _({
|
|
7
|
-
__name: "PreviewWrapper",
|
|
8
|
-
props: {
|
|
9
|
-
fileType: {},
|
|
10
|
-
resource: {},
|
|
11
|
-
maxSize: {},
|
|
12
|
-
load: { type: Function }
|
|
13
|
-
},
|
|
14
|
-
emits: ["loaded"],
|
|
15
|
-
setup(s, { emit: x }) {
|
|
16
|
-
const i = s, z = x, { t } = B(), p = g(null), d = g(!1), f = g(null), h = v(() => E(i.resource)), T = v(() => L(i.resource)), y = v(() => {
|
|
17
|
-
const e = h.value, m = i.maxSize;
|
|
18
|
-
return !e || !m ? !1 : e <= m;
|
|
19
|
-
});
|
|
20
|
-
return S(async () => {
|
|
21
|
-
if (!(!y.value || T.value === "blocked")) {
|
|
22
|
-
d.value = !0;
|
|
23
|
-
try {
|
|
24
|
-
p.value = await i.load(), await w(), z("loaded");
|
|
25
|
-
} catch (e) {
|
|
26
|
-
console.error("Error loading preview:", e), f.value = e instanceof TypeError ? "network" : "generic";
|
|
27
|
-
} finally {
|
|
28
|
-
d.value = !1;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}), (e, m) => (l(), k("div", R, [
|
|
32
|
-
p.value !== null ? C(e.$slots, "default", { data: p.value }, void 0, void 0, 0) : d.value ? (l(), k("div", q, r(a(t)("Chargement de l'aperçu {fileType}...", { fileType: s.fileType })), 1)) : y.value ? T.value === "blocked" ? (l(), n(c, { key: 3 }, {
|
|
33
|
-
default: o(() => [
|
|
34
|
-
u(r(a(t)("Ce fichier {fileType} ne peut pas être prévisualisé car il est hébergé sur un site distant qui restreint l'accès (CORS). Téléchargez-le depuis l'onglet Téléchargements.", { fileType: s.fileType })), 1)
|
|
35
|
-
]),
|
|
36
|
-
_: 1
|
|
37
|
-
})) : f.value === "network" ? (l(), n(c, { key: 4 }, {
|
|
38
|
-
default: o(() => [
|
|
39
|
-
u(r(a(t)("Ce fichier est hébergé sur un site externe qui ne permet pas la prévisualisation. Téléchargez-le depuis l'onglet Téléchargements.")), 1)
|
|
40
|
-
]),
|
|
41
|
-
_: 1
|
|
42
|
-
})) : f.value ? (l(), n(c, { key: 5 }, {
|
|
43
|
-
default: o(() => [
|
|
44
|
-
u(r(a(t)("L'aperçu de ce fichier n'a pas pu être chargé. Téléchargez-le depuis l'onglet Téléchargements.")), 1)
|
|
45
|
-
]),
|
|
46
|
-
_: 1
|
|
47
|
-
})) : b("", !0) : (l(), n(c, { key: 2 }, {
|
|
48
|
-
default: o(() => [
|
|
49
|
-
u(r(h.value ? a(t)("Le fichier {fileType} est trop volumineux pour être prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.", { fileType: s.fileType }) : a(t)("La taille du fichier est inconnue, l'aperçu n'est pas disponible. Téléchargez-le depuis l'onglet Téléchargements.")), 1)
|
|
50
|
-
]),
|
|
51
|
-
_: 1
|
|
52
|
-
}))
|
|
53
|
-
]));
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
export {
|
|
57
|
-
V as _
|
|
58
|
-
};
|