@datagouv/components-next 1.0.2-dev.40 → 1.0.2-dev.41

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.
@@ -1,5 +1,5 @@
1
1
  import { defineComponent as ly, ref as cy, computed as dc, useTemplateRef as uy, onMounted as hy, createElementBlock as Mp, openBlock as eh, createBlock as py, withCtx as rm, createTextVNode as nm, toDisplayString as fc, unref as Gs, createCommentVNode as dy, createElementVNode as is, createVNode as fy } from "vue";
2
- import { c as my, a as gy, d as _y, u as yy, e as xy, f as vy, _ as by, h as wy, i as Ty, K as Sy, t as Py } from "./main-ifX24DGW.js";
2
+ import { c as my, a as gy, d as _y, u as yy, e as xy, f as vy, _ as by, h as wy, i as Ty, K as Sy, t as Py } from "./main-CF7lWk6R.js";
3
3
  var fn = Uint8Array, Wo = Uint16Array, Iy = Int32Array, mm = new fn([
4
4
  0,
5
5
  0,
@@ -0,0 +1,61 @@
1
+ import { defineComponent as _, ref as g, computed as h, onMounted as S, nextTick as w, createElementBlock as k, openBlock as l, renderSlot as C, createBlock as n, createCommentVNode as b, toDisplayString as r, unref as a, withCtx as o, createTextVNode as u } from "vue";
2
+ import { a as B, g as E, b as L, _ as c } from "./main-CF7lWk6R.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), y = h(() => E(i.resource)), T = h(() => L(i.resource)), v = h(() => {
17
+ const e = y.value, m = i.maxSize;
18
+ return !e || !m ? !1 : e <= m;
19
+ });
20
+ return S(async () => {
21
+ if (!(!v.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", {
33
+ key: 0,
34
+ data: p.value
35
+ }) : d.value ? (l(), k("div", q, r(a(t)("Chargement de l'aperçu {fileType}...", { fileType: s.fileType })), 1)) : v.value ? T.value === "blocked" ? (l(), n(c, { key: 3 }, {
36
+ default: o(() => [
37
+ 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)
38
+ ]),
39
+ _: 1
40
+ })) : f.value === "network" ? (l(), n(c, { key: 4 }, {
41
+ default: o(() => [
42
+ 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)
43
+ ]),
44
+ _: 1
45
+ })) : f.value ? (l(), n(c, { key: 5 }, {
46
+ default: o(() => [
47
+ 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)
48
+ ]),
49
+ _: 1
50
+ })) : b("", !0) : (l(), n(c, { key: 2 }, {
51
+ default: o(() => [
52
+ u(r(y.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)
53
+ ]),
54
+ _: 1
55
+ }))
56
+ ]));
57
+ }
58
+ });
59
+ export {
60
+ V as _
61
+ };
@@ -0,0 +1,4 @@
1
+ import { j as f } from "./main-CF7lWk6R.js";
2
+ export {
3
+ f as default
4
+ };
@@ -0,0 +1,34 @@
1
+ import { defineComponent as i, defineAsyncComponent as m, createBlock as u, openBlock as f, unref as t, withCtx as l, createVNode as p } from "vue";
2
+ import { u as x } from "./main-CF7lWk6R.js";
3
+ import { _ as w } from "./PreviewWrapper.vue_vue_type_script_setup_true_lang-BlcvVwW8.js";
4
+ const X = /* @__PURE__ */ i({
5
+ __name: "XmlPreview.client",
6
+ props: {
7
+ resource: {}
8
+ },
9
+ setup(r) {
10
+ const o = m(
11
+ () => import("./vue3-xml-viewer.common-CAwAbUJl.js").then((e) => e.v).then((e) => e.default || e.XmlViewer)
12
+ ), n = r, s = x(), c = async () => {
13
+ const e = await fetch(n.resource.url);
14
+ if (!e.ok) throw new Error(`HTTP error! status: ${e.status}`);
15
+ return e.text();
16
+ };
17
+ return (e, _) => (f(), u(w, {
18
+ "file-type": "XML",
19
+ resource: r.resource,
20
+ "max-size": t(s).maxXmlPreviewCharSize,
21
+ load: c
22
+ }, {
23
+ default: l(({ data: a }) => [
24
+ p(t(o), {
25
+ xml: a
26
+ }, null, 8, ["xml"])
27
+ ]),
28
+ _: 1
29
+ }, 8, ["resource", "max-size"]));
30
+ }
31
+ });
32
+ export {
33
+ X as default
34
+ };
@@ -1,4 +1,4 @@
1
- import { aU as e, bd as t, y as r, z as o, A as i, B as n, C as c, D as l, i as g, bb as u, be as b, E as d, aT as T, aS as S, F as p, G as C, M as D, H as m, Q as O, R, S as E, T as I, U as f, L as y, J as A, I as h, V as M, O as v, N as z, P as L, W as w, aC as G, bf as P, X as N, Y as _, Z as B, $ as H, a1 as k, bt as U, bg as x, a2 as F, a3 as V, a4 as Q, a5 as W, a6 as Y, a7 as j, bc as q, a8 as X, a9 as J, aa as Z, ac as $, ab as K, br as aa, ad as sa, ae as ea, af as ta, ag as ra, ah as oa, ai as ia, aj as na, ak as ca, al as la, am as ga, an as ua, ao as ba, aD as da, aE as Ta, aF as Sa, ap as pa, aq as Ca, ar as Da, j as ma, as as Oa, at as Ra, au as Ea, av as Ia, aw as fa, a0 as ya, aG as Aa, ay as ha, ax as Ma, az as va, aA as za, bh as La, aB as wa, bG as Ga, bI as Pa, b8 as Na, b6 as _a, x as Ba, bH as Ha, s as ka, r as Ua, w as xa, v as Fa, bu as Va, aY as Qa, aL as Wa, aW as Ya, bl as ja, bC as qa, aZ as Xa, aQ as Ja, b9 as Za, b7 as $a, aR as Ka, m as as, l as ss, q as es, o as ts, n as rs, p as os, aV as is, b3 as ns, b5 as cs, bo as ls, bm as gs, bk as us, f as bs, e as ds, b as Ts, bw as Ss, g as ps, bp as Cs, bs as Ds, bq as ms, ba as Os, bB as Rs, bz as Es, by as Is, bj as fs, bv as ys, bn as As, bi as hs, aN as Ms, b0 as vs, b1 as zs, b2 as Ls, aM as ws, a_ as Gs, a_ as Ps, a$ as Ns, aX as _s, aO as Bs, t as Hs, aH as ks, b4 as Us, aI as xs, u as Fs, bx as Vs, d as Qs, bA as Ws, bD as Ys, bE as js, bF as qs, aP as Xs, aJ as Js, aK as Zs, a as $s } from "./main-ifX24DGW.js";
1
+ import { aU as e, bd as t, y as r, z as o, A as i, B as n, C as c, D as l, i as g, bb as u, be as b, E as d, aT as T, aS as S, F as p, G as C, M as D, H as m, Q as O, R, S as E, T as I, U as f, L as y, J as A, I as h, V as M, O as v, N as z, P as L, W as w, aC as G, bf as P, X as N, Y as _, Z as B, $ as H, a1 as k, bt as U, bg as x, a2 as F, a3 as V, a4 as Q, a5 as W, a6 as Y, a7 as j, bc as q, a8 as X, a9 as J, aa as Z, ac as $, ab as K, br as aa, ad as sa, ae as ea, af as ta, ag as ra, ah as oa, ai as ia, aj as na, ak as ca, al as la, am as ga, an as ua, ao as ba, aD as da, aE as Ta, aF as Sa, ap as pa, aq as Ca, ar as Da, j as ma, as as Oa, at as Ra, au as Ea, av as Ia, aw as fa, a0 as ya, aG as Aa, ay as ha, ax as Ma, az as va, aA as za, bh as La, aB as wa, bG as Ga, bI as Pa, b8 as Na, b6 as _a, x as Ba, bH as Ha, s as ka, r as Ua, w as xa, v as Fa, bu as Va, aY as Qa, aL as Wa, aW as Ya, bl as ja, bC as qa, aZ as Xa, aQ as Ja, b9 as Za, b7 as $a, aR as Ka, m as as, l as ss, q as es, o as ts, n as rs, p as os, aV as is, b3 as ns, b5 as cs, bo as ls, bm as gs, bk as us, f as bs, e as ds, b as Ts, bw as Ss, g as ps, bp as Cs, bs as Ds, bq as ms, ba as Os, bB as Rs, bz as Es, by as Is, bj as fs, bv as ys, bn as As, bi as hs, aN as Ms, b0 as vs, b1 as zs, b2 as Ls, aM as ws, a_ as Gs, a_ as Ps, a$ as Ns, aX as _s, aO as Bs, t as Hs, aH as ks, b4 as Us, aI as xs, u as Fs, bx as Vs, d as Qs, bA as Ws, bD as Ys, bE as js, bF as qs, aP as Xs, aJ as Js, aK as Zs, a as $s } from "./main-CF7lWk6R.js";
2
2
  export {
3
3
  e as AI_SUGGESTION_MIN_DESCRIPTION_LENGTH,
4
4
  t as ASSOCIATION,
@@ -6,7 +6,7 @@ import { a as TileGrid, U as UrlTile, e as extentFromProjection, c as createXYZ,
6
6
  import { D as DEFAULT_MAX_ZOOM, a as assert, f as forEach, i as intersectsLineString, b as intersectsLineStringArray, l as linearRingssContainsXY, c as intersectsLinearRingMultiArray, d as compose, e as create, g as apply, s as setFromArray, m as multiply, h as scale, r as reset, t as translate } from "./common-PJfpC179.js";
7
7
  import { R as RBush$1, C as Collection, a as CollectionEventType, b as asColorLike, d as defaultFillStyle, c as defaultStrokeStyle, e as defaultLineCap, f as defaultLineDash, g as defaultLineDashOffset, h as defaultLineJoin, i as defaultLineWidth, j as defaultMiterLimit, k as defaultPadding, l as defaultTextBaseline, m as defaultTextAlign, n as defaultFont, r as registerFont, o as getTextDimensions, p as drawImageOrLabel, q as measureAndCacheTextWidth, I as Icon, B as BaseVectorLayer, P as PointerInteraction, s as noModifierKeys, t as always, u as shiftKeyOnly, v as InteractionProperty, M as MapBrowserEventType, w as MapBrowserEvent, x as never, y as createEditingStyle, z as primaryAction, A as altKeyOnly, D as singleClick, E as Interaction, F as Fill, S as Stroke, T as Text, G as Style$1, H as CircleStyle, J as checkedFonts, K as Map$1, L as pointerMove, N as ImageStyle, O as Attribution, Z as Zoom, Q as MapProperty } from "./Map-BjUnLyj8.js";
8
8
  import { S as SimpleGeometry, d as deflateCoordinate, r as rotate, G as Geometry, m as maxSquaredDelta, a as assignClosestPoint, i as inflateCoordinates, b as douglasPeucker, c as deflateCoordinates, e as arrayMaxSquaredDelta, f as assignClosestArrayPoint, g as inflateCoordinatesArray, h as douglasPeuckerArray, j as deflateCoordinatesArray, P as Point$1, k as multiArrayMaxSquaredDelta, l as assignClosestMultiArrayPoint, n as linearRingss$1, o as orientLinearRingsArray, p as inflateMultiCoordinatesArray, q as getInteriorPointsOfMultiArray, s as linearRingssAreOriented, t as quantizeMultiArray, u as Polygon, v as deflateMultiCoordinatesArray, w as getInteriorPointOfArray, x as inflateEnds, y as transform2D, z as quantizeArray, A as snap, B as transformGeom2D, V as ViewHint, C as getStrideForLayout, D as linearRingsAreOriented, E as orientLinearRings, F as getLayoutForStride, L as LinearRing, H as View, I as ViewProperty, J as fromExtent } from "./View-BR92hTWP.js";
9
- import { k as commonjsGlobal, c as getDefaultExportFromCjs$1 } from "./main-ifX24DGW.js";
9
+ import { k as commonjsGlobal, c as getDefaultExportFromCjs$1 } from "./main-CF7lWk6R.js";
10
10
  import { E as EventType$1, a as asArray } from "./Event--kp8kMdJ.js";
11
11
  import { Z as ZIndexContext, C as CanvasLayerRenderer, c as canvasPool$1, T as TileLayer, a as CanvasTileLayerRenderer, b as TileProperty } from "./Tile-DbNFNPfU.js";
12
12
  function padNumber(i, e, t) {
@@ -93702,7 +93702,7 @@ const TMe = /* @__PURE__ */ tp(JD), OMe = /* @__PURE__ */ ole({
93702
93702
  canEdit: { type: Boolean, default: !1 }
93703
93703
  },
93704
93704
  setup(e) {
93705
- const t = ["parquet", "pmtiles", "geojson"], n = ["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"], o = e, u = Rn(), f = pa(() => import("./Swagger.client-FpYXdDuX.js")), c = pa(() => import("./MapContainer.client-BZsKgRUh.js")), d = pa(() => import("./Pmtiles.client-CUaeaV-O.js")), a = pa(() => import("./JsonPreview.client-B6aU3vl4.js")), h = pa(() => import("./PdfPreview.client-ClkseuKU.js")), g = pa(() => import("./XmlPreview.client-BNGHvVnU.js")), m = pa(() => import("./Datafair.client-CYO9vwx6.js")), { t: w } = C2(), { formatRelativeIfRecentDate: y } = Ia(), x = jD(), H = s1(() => {
93705
+ const t = ["parquet", "pmtiles", "geojson"], n = ["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"], o = e, u = Rn(), f = pa(() => import("./Swagger.client-U7ZDVUHL.js")), c = pa(() => import("./MapContainer.client-BKGsAP0Y.js")), d = pa(() => import("./Pmtiles.client-C1I7pwT5.js")), a = pa(() => import("./JsonPreview.client-DGiaDxVv.js")), h = pa(() => import("./PdfPreview.client-CGjP5ZYb.js")), g = pa(() => import("./XmlPreview.client-CHUVVEH6.js")), m = pa(() => import("./Datafair.client-BAokThtJ.js")), { t: w } = C2(), { formatRelativeIfRecentDate: y } = Ia(), x = jD(), H = s1(() => {
93706
93706
  const d1 = o.resource.format?.toLowerCase();
93707
93707
  return d1 === "json" || d1 === "pdf" || d1 === "xml";
93708
93708
  }), E = s1(() => x(o.resource)), z = s1(() => o.resource.extras["analysis:parsing:pmtiles_url"] || o.resource.format === "pmtiles"), Z = s1(() => kh(o.dataset.organization) && o.resource.extras.datafairEmbed), R = s1(() => kh(o.dataset.organization) && o.resource.extras.apidocUrl), T = s1(() => Wb(o.resource.format) ? o.resource.format : w("Fichier")), B = s1(() => DD(o.resource)), D = s1(() => B.value === "wms"), U = s1(() => {
@@ -94313,19 +94313,19 @@ const G_e = { class: "border border-gray-default" }, Y_e = { class: "p-4 flex fl
94313
94313
  },
94314
94314
  setup(e) {
94315
94315
  const t = pa(
94316
- () => import("./JsonPreview.client-B6aU3vl4.js")
94316
+ () => import("./JsonPreview.client-DGiaDxVv.js")
94317
94317
  ), n = pa(
94318
- () => import("./PdfPreview.client-ClkseuKU.js")
94318
+ () => import("./PdfPreview.client-CGjP5ZYb.js")
94319
94319
  ), o = pa(
94320
- () => import("./XmlPreview.client-BNGHvVnU.js")
94320
+ () => import("./XmlPreview.client-CHUVVEH6.js")
94321
94321
  ), u = pa(
94322
- () => import("./Datafair.client-CYO9vwx6.js")
94322
+ () => import("./Datafair.client-BAokThtJ.js")
94323
94323
  ), f = pa(
94324
- () => import("./MapContainer.client-BZsKgRUh.js")
94324
+ () => import("./MapContainer.client-BKGsAP0Y.js")
94325
94325
  ), c = pa(
94326
- () => import("./Pmtiles.client-CUaeaV-O.js")
94326
+ () => import("./Pmtiles.client-C1I7pwT5.js")
94327
94327
  ), d = pa(
94328
- () => import("./Swagger.client-FpYXdDuX.js")
94328
+ () => import("./Swagger.client-U7ZDVUHL.js")
94329
94329
  ), a = e, { t: h } = C2(), g = Rn(), { formatRelativeIfRecentDate: m } = Ia(), {
94330
94330
  hasTabularData: w,
94331
94331
  hasPmtiles: y,
@@ -1,4 +1,4 @@
1
- import { c as Ke } from "./main-ifX24DGW.js";
1
+ import { c as Ke } from "./main-CF7lWk6R.js";
2
2
  import We from "vue";
3
3
  function Fe(I, K) {
4
4
  for (var V = 0; V < K.length; V++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datagouv/components-next",
3
- "version": "1.0.2-dev.40",
3
+ "version": "1.0.2-dev.41",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -1,51 +1,31 @@
1
1
  <template>
2
- <div class="fr-text--xs">
3
- <div v-if="jsonData">
4
- <JsonViewer
5
- :value="jsonData"
6
- boxed
7
- sort
8
- theme="light"
9
- :max-depth="3"
10
- :expand-depth="2"
11
- :indent-width="2"
12
- />
13
- </div>
14
- <div
15
- v-else-if="loading"
16
- class="text-gray-medium"
17
- >
18
- {{ t("Chargement de l'aperçu JSON...") }}
19
- </div>
20
- <PreviewUnavailable v-else-if="fileTooLarge">
21
- {{ fileSizeBytes
22
- ? t("Le fichier JSON est trop volumineux pour être prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.")
23
- : t("La taille du fichier est inconnue, l'aperçu n'est pas disponible. Téléchargez-le depuis l'onglet Téléchargements.")
24
- }}
25
- </PreviewUnavailable>
26
- <PreviewUnavailable v-else-if="error === 'cors'">
27
- {{ t("Ce fichier JSON 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.") }}
28
- </PreviewUnavailable>
29
- <PreviewUnavailable v-else-if="error === 'network'">
30
- {{ 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.") }}
31
- </PreviewUnavailable>
32
- <PreviewUnavailable v-else-if="error">
33
- {{ t("L'aperçu de ce fichier n'a pas pu être chargé. Téléchargez-le depuis l'onglet Téléchargements.") }}
34
- </PreviewUnavailable>
35
- </div>
2
+ <PreviewWrapper
3
+ v-slot="{ data }"
4
+ file-type="JSON"
5
+ :resource="resource"
6
+ :max-size="config.maxJsonPreviewCharSize"
7
+ :load="load"
8
+ >
9
+ <JsonViewer
10
+ :value="data"
11
+ boxed
12
+ sort
13
+ theme="light"
14
+ :max-depth="3"
15
+ :expand-depth="2"
16
+ :indent-width="2"
17
+ />
18
+ </PreviewWrapper>
36
19
  </template>
37
20
 
38
21
  <script setup lang="ts">
39
- import { computed, defineAsyncComponent, onMounted, ref } from 'vue'
22
+ import { defineAsyncComponent } from 'vue'
40
23
  import { useComponentsConfig } from '../../config'
41
- import PreviewUnavailable from './PreviewUnavailable.vue'
24
+ import PreviewWrapper from './PreviewWrapper.vue'
42
25
  import type { Resource } from '../../types/resources'
43
- import { getResourceFilesize, getResourceCorsStatus } from '../../functions/resources'
44
- import { useTranslation } from '../../composables/useTranslation'
45
26
 
46
27
  const JsonViewer = defineAsyncComponent(() =>
47
28
  import('vue3-json-viewer').then((module) => {
48
- // Import CSS when component loads
49
29
  import('vue3-json-viewer/dist/vue3-json-viewer.css')
50
30
  return module.JsonViewer
51
31
  }),
@@ -56,75 +36,10 @@ const props = defineProps<{
56
36
  }>()
57
37
 
58
38
  const config = useComponentsConfig()
59
- const { t } = useTranslation()
60
39
 
61
- const jsonData = ref<unknown>(null)
62
- const loading = ref(false)
63
- const error = ref<string | null>(null)
64
- const fileTooLarge = ref(false)
65
-
66
- const fileSizeBytes = computed(() => getResourceFilesize(props.resource))
67
-
68
- const corsStatus = computed(() => getResourceCorsStatus(props.resource))
69
-
70
- const isSizeAllowed = computed(() => {
71
- const size = fileSizeBytes.value
72
- // Convert maxJsonPreviewCharSize from characters to bytes (rough estimate)
73
- // Assuming average 1 byte per character for JSON
74
- const maxByteSize = config.maxJsonPreviewCharSize
75
-
76
- // If we don't know the size or the max size, don't risk loading a potentially huge file
77
- if (!size || !maxByteSize) return false
78
-
79
- return size <= maxByteSize
80
- })
81
-
82
- const fetchJsonData = async () => {
83
- error.value = null
84
- fileTooLarge.value = false
85
-
86
- // Check if file is too large or size is unknown
87
- if (!isSizeAllowed.value) {
88
- fileTooLarge.value = true
89
- return
90
- }
91
-
92
- // Check if CORS is allowed
93
- if (corsStatus.value === 'blocked') {
94
- error.value = 'cors'
95
- return
96
- }
97
-
98
- loading.value = true
99
- try {
100
- const response = await fetch(props.resource.url)
101
- // const response = await fetch('/test-data.json') // For testing locally without CORS issues
102
- if (!response.ok) {
103
- throw new Error(`HTTP error! status: ${response.status}`)
104
- }
105
- const data = await response.json()
106
-
107
- // Use the original data directly - let the JSON viewer handle large files
108
- jsonData.value = data
109
- }
110
- catch (err) {
111
- console.error('Error loading JSON:', err)
112
-
113
- if (err instanceof TypeError) {
114
- error.value = 'network'
115
- }
116
- else {
117
- error.value = 'generic'
118
- }
119
-
120
- jsonData.value = null
121
- }
122
- finally {
123
- loading.value = false
124
- }
40
+ const load = async () => {
41
+ const response = await fetch(props.resource.url)
42
+ if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
43
+ return response.json()
125
44
  }
126
-
127
- onMounted(() => {
128
- fetchJsonData()
129
- })
130
45
  </script>
@@ -1,51 +1,34 @@
1
1
  <template>
2
- <div class="text-xs">
2
+ <PreviewWrapper
3
+ v-slot="{ data }"
4
+ file-type="PDF"
5
+ :resource="resource"
6
+ :max-size="config.maxPdfPreviewByteSize"
7
+ :load="load"
8
+ @loaded="renderAllPages"
9
+ >
3
10
  <div
4
- v-if="pdfReady"
5
11
  ref="containerRef"
6
12
  class="w-full overflow-y-auto max-h-[80vh] space-y-3"
7
13
  >
8
14
  <canvas
9
- v-for="page in totalPages"
15
+ v-for="page in (data as PDFDocumentProxy).numPages"
10
16
  :key="page"
11
17
  :ref="(el) => setCanvasRef(el as HTMLCanvasElement, page)"
12
18
  class="w-full"
13
19
  />
14
20
  </div>
15
- <div
16
- v-else-if="loading"
17
- class="text-gray-medium"
18
- >
19
- {{ t("Chargement de l'aperçu PDF...") }}
20
- </div>
21
- <PreviewUnavailable v-else-if="fileTooLarge">
22
- {{ fileSizeBytes
23
- ? t("Le fichier PDF est trop volumineux pour être prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.")
24
- : t("La taille du fichier est inconnue, l'aperçu n'est pas disponible. Téléchargez-le depuis l'onglet Téléchargements.")
25
- }}
26
- </PreviewUnavailable>
27
- <PreviewUnavailable v-else-if="error === 'cors'">
28
- {{ t("Ce fichier PDF 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.") }}
29
- </PreviewUnavailable>
30
- <PreviewUnavailable v-else-if="error === 'network'">
31
- {{ 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.") }}
32
- </PreviewUnavailable>
33
- <PreviewUnavailable v-else-if="error">
34
- {{ t("L'aperçu de ce fichier n'a pas pu être chargé. Téléchargez-le depuis l'onglet Téléchargements.") }}
35
- </PreviewUnavailable>
36
- </div>
21
+ </PreviewWrapper>
37
22
  </template>
38
23
 
39
24
  <script setup lang="ts">
40
- import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
25
+ import { onBeforeUnmount, ref } from 'vue'
41
26
  import * as pdfjsLib from 'pdfjs-dist'
42
27
  import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?url'
43
28
  import type { PDFDocumentProxy } from 'pdfjs-dist'
44
- import PreviewUnavailable from './PreviewUnavailable.vue'
29
+ import PreviewWrapper from './PreviewWrapper.vue'
45
30
  import { useComponentsConfig } from '../../config'
46
31
  import type { Resource } from '../../types/resources'
47
- import { getResourceFilesize, getResourceCorsStatus } from '../../functions/resources'
48
- import { useTranslation } from '../../composables/useTranslation'
49
32
 
50
33
  pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker
51
34
 
@@ -54,15 +37,8 @@ const props = defineProps<{
54
37
  }>()
55
38
 
56
39
  const config = useComponentsConfig()
57
- const { t } = useTranslation()
58
40
 
59
41
  const containerRef = ref<HTMLElement | null>(null)
60
- const pdfReady = ref(false)
61
- const loading = ref(false)
62
- const error = ref<string | null>(null)
63
- const fileTooLarge = ref(false)
64
- const totalPages = ref(0)
65
-
66
42
  let pdfDoc: PDFDocumentProxy | null = null
67
43
  const canvasRefs = new Map<number, HTMLCanvasElement>()
68
44
 
@@ -98,73 +74,22 @@ async function renderPage(pageNum: number) {
98
74
  }).promise
99
75
  }
100
76
 
101
- const fileSizeBytes = computed(() => getResourceFilesize(props.resource))
102
-
103
- const corsStatus = computed(() => getResourceCorsStatus(props.resource))
104
-
105
- const shouldLoadPdf = computed(() => {
106
- const size = fileSizeBytes.value
107
- if (!size) {
108
- // If we don't know the size, don't risk loading a potentially huge file
109
- return false
110
- }
111
-
112
- // Use maxPdfPreviewByteSize from config, fallback to 10 MB if not set
113
- const maxByteSize = config.maxPdfPreviewByteSize ?? 10_000_000
114
- return size <= maxByteSize
115
- })
116
-
117
- const loadPdf = async () => {
118
- error.value = null
119
- fileTooLarge.value = false
120
-
121
- if (!shouldLoadPdf.value) {
122
- fileTooLarge.value = true
123
- return
124
- }
125
-
126
- // Check if CORS is allowed
127
- if (corsStatus.value === 'blocked') {
128
- error.value = 'cors'
129
- return
130
- }
131
-
132
- loading.value = true
133
- try {
134
- const loadingTask = pdfjsLib.getDocument({
135
- url: props.resource.url,
136
- isEvalSupported: false,
137
- })
138
-
139
- pdfDoc = await loadingTask.promise
140
- totalPages.value = pdfDoc.numPages
141
- pdfReady.value = true
142
-
143
- await nextTick()
77
+ const load = async () => {
78
+ const loadingTask = pdfjsLib.getDocument({
79
+ url: props.resource.url,
80
+ isEvalSupported: false,
81
+ })
82
+ pdfDoc = await loadingTask.promise
83
+ return pdfDoc
84
+ }
144
85
 
145
- for (let i = 1; i <= pdfDoc.numPages; i++) {
146
- await renderPage(i)
147
- }
148
- }
149
- catch (err) {
150
- console.error('Error loading PDF:', err)
151
-
152
- if (err instanceof TypeError) {
153
- error.value = 'network'
154
- }
155
- else {
156
- error.value = 'generic'
157
- }
158
- }
159
- finally {
160
- loading.value = false
86
+ const renderAllPages = async () => {
87
+ if (!pdfDoc) return
88
+ for (let i = 1; i <= pdfDoc.numPages; i++) {
89
+ await renderPage(i)
161
90
  }
162
91
  }
163
92
 
164
- onMounted(() => {
165
- loadPdf()
166
- })
167
-
168
93
  onBeforeUnmount(() => {
169
94
  pdfDoc?.destroy()
170
95
  })
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <div class="text-xs">
3
+ <slot
4
+ v-if="data !== null"
5
+ :data="data"
6
+ />
7
+ <div
8
+ v-else-if="loading"
9
+ class="text-gray-medium"
10
+ >
11
+ {{ t("Chargement de l'aperçu {fileType}...", { fileType }) }}
12
+ </div>
13
+ <PreviewUnavailable v-else-if="!isSizeAllowed">
14
+ {{ fileSizeBytes
15
+ ? t("Le fichier {fileType} est trop volumineux pour être prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.", { fileType })
16
+ : t("La taille du fichier est inconnue, l'aperçu n'est pas disponible. Téléchargez-le depuis l'onglet Téléchargements.")
17
+ }}
18
+ </PreviewUnavailable>
19
+ <PreviewUnavailable v-else-if="corsStatus === 'blocked'">
20
+ {{ 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 }) }}
21
+ </PreviewUnavailable>
22
+ <PreviewUnavailable v-else-if="error === 'network'">
23
+ {{ 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.") }}
24
+ </PreviewUnavailable>
25
+ <PreviewUnavailable v-else-if="error">
26
+ {{ t("L'aperçu de ce fichier n'a pas pu être chargé. Téléchargez-le depuis l'onglet Téléchargements.") }}
27
+ </PreviewUnavailable>
28
+ </div>
29
+ </template>
30
+
31
+ <script setup lang="ts">
32
+ import { computed, nextTick, onMounted, ref } from 'vue'
33
+ import PreviewUnavailable from './PreviewUnavailable.vue'
34
+ import type { Resource } from '../../types/resources'
35
+ import { getResourceFilesize, getResourceCorsStatus } from '../../functions/resources'
36
+ import { useTranslation } from '../../composables/useTranslation'
37
+
38
+ const props = defineProps<{
39
+ fileType: string
40
+ resource: Resource
41
+ maxSize: number | undefined
42
+ load: () => Promise<unknown>
43
+ }>()
44
+
45
+ const emit = defineEmits<{
46
+ loaded: []
47
+ }>()
48
+
49
+ const { t } = useTranslation()
50
+
51
+ const data = ref<unknown>(null)
52
+ const loading = ref(false)
53
+ const error = ref<'network' | 'generic' | null>(null)
54
+
55
+ const fileSizeBytes = computed(() => getResourceFilesize(props.resource))
56
+ const corsStatus = computed(() => getResourceCorsStatus(props.resource))
57
+
58
+ const isSizeAllowed = computed(() => {
59
+ const size = fileSizeBytes.value
60
+ const max = props.maxSize
61
+ if (!size || !max) return false
62
+ return size <= max
63
+ })
64
+
65
+ onMounted(async () => {
66
+ if (!isSizeAllowed.value || corsStatus.value === 'blocked') return
67
+
68
+ loading.value = true
69
+ try {
70
+ data.value = await props.load()
71
+ await nextTick()
72
+ emit('loaded')
73
+ }
74
+ catch (err) {
75
+ console.error('Error loading preview:', err)
76
+ error.value = err instanceof TypeError ? 'network' : 'generic'
77
+ }
78
+ finally {
79
+ loading.value = false
80
+ }
81
+ })
82
+ </script>