@datagouv/components-next 0.0.32 → 0.1.1

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.
Files changed (85) hide show
  1. package/README.md +5 -15
  2. package/assets/labels/hvd.svg +15 -0
  3. package/assets/labels/inspire.svg +20 -0
  4. package/assets/labels/sl.svg +5 -0
  5. package/assets/labels/spd.svg +5 -0
  6. package/assets/labels/sr.svg +5 -0
  7. package/dist/JsonPreview.client-nkpNPvZb.js +92 -0
  8. package/dist/{MapContainer.client-Dhfz-YU8.js → MapContainer.client-CrTw-ai8.js} +17587 -6826
  9. package/dist/{PdfPreview.client--W0FK7CN.js → PdfPreview.client-JpAWf0A2.js} +47 -48
  10. package/dist/{Pmtiles.client-B6vRTwrm.js → Pmtiles.client-B83l4Ft5.js} +7609 -7503
  11. package/dist/Swagger.client-D1TfRQjI.js +4 -0
  12. package/dist/XmlPreview.client-DV8N1S9Y.js +84 -0
  13. package/dist/components-next.css +6 -6
  14. package/dist/components-next.js +95 -80
  15. package/dist/components.css +2 -2
  16. package/dist/en-CuSmdvir.js +30 -0
  17. package/dist/hvd-DYeke1vM.js +4 -0
  18. package/dist/inspire-BLXeJvob.js +4 -0
  19. package/dist/{main-yWiuApVL.js → main-CN6IuSUA.js} +53400 -55985
  20. package/dist/{pdf-vue3-Dm2ZCc3P.js → pdf-vue3-IkJO65RH.js} +2 -2
  21. package/dist/{pdf.min-f72cfa08-DAetWL3M.js → pdf.min-f72cfa08-CdgJTooZ.js} +78 -78
  22. package/dist/sl-VR8Tb1_u.js +4 -0
  23. package/dist/spd-BJ-Omhgt.js +4 -0
  24. package/dist/sr-DjSF-8xW.js +4 -0
  25. package/dist/{text-clamp.esm-Mb7Qdtu9.js → text-clamp.esm-B5kW_XMt.js} +54 -55
  26. package/dist/{vue3-json-viewer-B1fiyuLU.js → vue3-json-viewer-BXwup7nO.js} +88 -93
  27. package/dist/{vue3-xml-viewer.common-1QyofKqS.js → vue3-xml-viewer.common-9hga4rGF.js} +54 -54
  28. package/package.json +12 -11
  29. package/src/components/ActivityList/ActivityList.vue +159 -0
  30. package/src/components/ActivityList/UserActivityList.vue +30 -0
  31. package/src/components/AppLink.vue +3 -3
  32. package/src/components/Avatar.vue +1 -0
  33. package/src/components/DataserviceCard.vue +3 -3
  34. package/src/components/DatasetCard.vue +19 -18
  35. package/src/components/DatasetInformationPanel.vue +16 -16
  36. package/src/components/DatasetLabelTag.vue +40 -0
  37. package/src/components/DatasetQuality.vue +13 -10
  38. package/src/components/DatasetQualityInline.vue +5 -2
  39. package/src/components/DatasetQualityScore.vue +3 -3
  40. package/src/components/DatasetQualityTooltipContent.vue +2 -2
  41. package/src/components/DateRangeDetails.vue +7 -3
  42. package/src/components/ExtraAccordion.vue +3 -1
  43. package/src/components/OrganizationNameWithCertificate.vue +2 -2
  44. package/src/components/PaddedContainer.vue +28 -0
  45. package/src/components/Pagination.vue +2 -2
  46. package/src/components/Placeholder.vue +1 -1
  47. package/src/components/ReadMore.vue +17 -17
  48. package/src/components/ResourceAccordion/DataStructure.vue +8 -3
  49. package/src/components/ResourceAccordion/EditButton.vue +2 -2
  50. package/src/components/ResourceAccordion/JsonPreview.client.vue +7 -5
  51. package/src/components/ResourceAccordion/MapContainer.client.vue +2 -2
  52. package/src/components/ResourceAccordion/Metadata.vue +10 -10
  53. package/src/components/ResourceAccordion/PdfPreview.client.vue +7 -5
  54. package/src/components/ResourceAccordion/Pmtiles.client.vue +3 -3
  55. package/src/components/ResourceAccordion/Preview.vue +2 -2
  56. package/src/components/ResourceAccordion/ResourceAccordion.vue +23 -15
  57. package/src/components/ResourceAccordion/SchemaBadge.vue +4 -4
  58. package/src/components/ResourceAccordion/XmlPreview.client.vue +7 -5
  59. package/src/components/ReuseCard.vue +3 -3
  60. package/src/components/ReuseDetails.vue +2 -2
  61. package/src/components/SmallChart.vue +33 -30
  62. package/src/components/StatBox.vue +12 -7
  63. package/src/components/Toggletip.vue +0 -1
  64. package/src/components/TranslationT.vue +51 -0
  65. package/src/composables/useTranslation.ts +169 -0
  66. package/src/functions/activities.ts +36 -0
  67. package/src/functions/api.ts +4 -4
  68. package/src/functions/datasets.ts +28 -0
  69. package/src/functions/dates.ts +4 -4
  70. package/src/functions/helpers.ts +3 -3
  71. package/src/functions/organizations.ts +2 -2
  72. package/src/functions/pagination.ts +9 -0
  73. package/src/functions/resources.ts +2 -2
  74. package/src/functions/reuses.ts +3 -3
  75. package/src/main.ts +24 -20
  76. package/src/types/activity.ts +24 -0
  77. package/src/types/api.ts +8 -0
  78. package/src/types/badges.ts +4 -0
  79. package/src/types/dataservices.ts +6 -8
  80. package/src/types/datasets.ts +6 -1
  81. package/src/types/site.ts +1 -0
  82. package/src/types/topics.ts +17 -2
  83. package/dist/JsonPreview.client-BRhCOHlE.js +0 -93
  84. package/dist/Swagger.client-ch5H8aT2.js +0 -4
  85. package/dist/XmlPreview.client-BcbnRWAp.js +0 -85
@@ -46,7 +46,7 @@
46
46
  />
47
47
  </ContentLoader>
48
48
  <div
49
- v-else-if="changesThisYear"
49
+ v-else-if="data && changesThisYear"
50
50
  class="ml-2"
51
51
  >
52
52
  <SmallChart
@@ -60,8 +60,12 @@
60
60
  </div>
61
61
  </div>
62
62
  <template v-if="lastValue && lastMonth">
63
- <p class="mt-1 mb-0 text-xs">
64
- {{ $t('depuis juillet 2022') }}
63
+ <p
64
+ v-if="since"
65
+ class="mt-1 mb-0 text-xs"
66
+ >
67
+ {{ t("depuis ") }}
68
+ {{ formatDate(since, { dateStyle: undefined, year: 'numeric', month: 'short', day: undefined }) }}
65
69
  </p>
66
70
  <p class="mt-1 mb-0 text-xs text-success-darkest">
67
71
  <strong>
@@ -124,7 +128,7 @@
124
128
  />
125
129
  </ContentLoader>
126
130
  <div
127
- v-else-if="changesThisYear"
131
+ v-else-if="data && changesThisYear"
128
132
  class="ml-2"
129
133
  >
130
134
  <SmallChart
@@ -149,21 +153,22 @@
149
153
 
150
154
  <script setup lang="ts">
151
155
  import { computed } from 'vue'
152
- import { useI18n } from 'vue-i18n'
153
156
  import { ContentLoader } from 'vue-content-loader'
154
157
  import { useFormatDate } from '../functions/dates'
155
158
  import { summarize } from '../functions/helpers'
159
+ import { useTranslation } from '../composables/useTranslation'
156
160
  import SmallChart from './SmallChart.vue'
157
161
 
158
162
  const props = defineProps<{
159
163
  title: string
160
- data: Record<string, number> | null
164
+ data?: Record<string, number> | null
161
165
  type: 'line' | 'bar'
162
166
  size?: 'sm'
163
167
  summary?: number | null
168
+ since?: string | null
164
169
  }>()
165
170
 
166
- const { t } = useI18n()
171
+ const { t } = useTranslation()
167
172
  const { formatDate } = useFormatDate()
168
173
 
169
174
  const months = computed(() => props.data ? Object.keys(props.data) : [])
@@ -68,7 +68,6 @@ const calculatePanelPosition = () => {
68
68
  console.error('Cannot find the popover of the Toggletip.)')
69
69
  return
70
70
  }
71
- console.log(popover)
72
71
  const popoverRect = popover.getBoundingClientRect()
73
72
  panelStyle.value = {
74
73
  left: `${popoverRect.left + window.scrollX}px`,
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <component :is="tag">
3
+ <template
4
+ v-for="(part, index) in parts"
5
+ :key="index"
6
+ >
7
+ <component
8
+ :is="slots[part.placeholder]"
9
+ v-if="part.placeholder && slots[part.placeholder]"
10
+ />
11
+ <template v-else-if="part.text">
12
+ {{ part.text }}
13
+ </template>
14
+ <template v-else-if="part.placeholder">
15
+ {{ '{' + part.placeholder + '}' }}
16
+ </template>
17
+ </template>
18
+ </component>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import { computed, useSlots } from 'vue'
23
+ import { parseTextWithPlaceholders, useTranslation } from '../composables/useTranslation'
24
+
25
+ const props = withDefaults(defineProps<{
26
+ keypath: string
27
+ tag?: string
28
+ n?: number
29
+ count?: number
30
+ }>(), {
31
+ tag: 'span',
32
+ })
33
+
34
+ const slots = useSlots()
35
+ const { t } = useTranslation()
36
+
37
+ const parts = computed(() => {
38
+ const { keypath, n, count, ...interpolations } = props
39
+
40
+ const options: Record<string, string | number> = {
41
+ ...interpolations,
42
+ }
43
+
44
+ if (n !== undefined) options.n = n
45
+ if (count !== undefined) options.count = count
46
+
47
+ const translated = t(keypath, options)
48
+
49
+ return parseTextWithPlaceholders(translated)
50
+ })
51
+ </script>
@@ -0,0 +1,169 @@
1
+ import { ref } from 'vue'
2
+
3
+ // Declare useRequestHeaders for TypeScript (Nuxt composable)
4
+ declare const useRequestHeader: ((header: string) => string | undefined) | undefined
5
+
6
+ export type TranslationOptions = Record<string, string | number>
7
+
8
+ const PLACEHOLDER_REGEX = /\{(\w+)\}/g
9
+
10
+ // Pre-register all available translation files at build time
11
+ const translationModules = import.meta.glob<Record<string, string>>('../../../locales/*.json', {
12
+ import: 'default',
13
+ })
14
+
15
+ function detectLanguage(): string {
16
+ // Server-side (Nuxt)
17
+ try {
18
+ const header = useRequestHeader?.('accept-language')
19
+ const acceptLanguage = header
20
+ if (acceptLanguage) {
21
+ const primaryLang = acceptLanguage.split(',')[0]!.split('-')[0]!.toLowerCase()
22
+ return primaryLang
23
+ }
24
+ }
25
+ catch {
26
+ // useRequestHeaders not available, continue to client-side detection
27
+ }
28
+
29
+ // Client-side
30
+ if (typeof window !== 'undefined' && navigator.language) {
31
+ return navigator.language.split('-')[0]!.toLowerCase()
32
+ }
33
+
34
+ return 'fr'
35
+ }
36
+
37
+ async function loadTranslationFile(lang: string): Promise<Record<string, string>> {
38
+ const modulePath = `../../../locales/${lang}.json`
39
+ const moduleLoader = translationModules[modulePath]
40
+
41
+ if (!moduleLoader) {
42
+ return {} // Translation file doesn't exist
43
+ }
44
+
45
+ try {
46
+ return await moduleLoader()
47
+ }
48
+ catch {
49
+ return {}
50
+ }
51
+ }
52
+
53
+ function interpolate(text: string, values: Record<string, string | number>): string {
54
+ return text.replace(PLACEHOLDER_REGEX, (match, key) => {
55
+ return key in values ? String(values[key]) : match
56
+ })
57
+ }
58
+
59
+ export function extractPlaceholders(text: string): string[] {
60
+ const placeholders: string[] = []
61
+ const regex = new RegExp(PLACEHOLDER_REGEX.source, PLACEHOLDER_REGEX.flags)
62
+ let match
63
+
64
+ while ((match = regex.exec(text)) !== null) {
65
+ placeholders.push(match[1]!)
66
+ }
67
+
68
+ return placeholders
69
+ }
70
+
71
+ export function parseTextWithPlaceholders(text: string): Array<{ text?: string, placeholder?: string }> {
72
+ const result: Array<{ text?: string, placeholder?: string }> = []
73
+ const regex = new RegExp(PLACEHOLDER_REGEX.source, PLACEHOLDER_REGEX.flags)
74
+ let lastIndex = 0
75
+ let match
76
+
77
+ while ((match = regex.exec(text)) !== null) {
78
+ if (match.index > lastIndex) {
79
+ result.push({ text: text.slice(lastIndex, match.index) })
80
+ }
81
+
82
+ result.push({ placeholder: match[1] })
83
+
84
+ lastIndex = regex.lastIndex
85
+ }
86
+
87
+ if (lastIndex < text.length) {
88
+ result.push({ text: text.slice(lastIndex) })
89
+ }
90
+
91
+ return result
92
+ }
93
+
94
+ function handlePluralization(key: string, count: number): string {
95
+ const parts = key.split('|').map(part => part.trim())
96
+
97
+ if (parts.length === 1) {
98
+ return parts[0]!
99
+ }
100
+
101
+ if (parts.length === 2) {
102
+ // French pluralization rule: 0 or 1 = singular, > 1 = plural
103
+ return count <= 1 ? parts[0]! : parts[1]!
104
+ }
105
+
106
+ if (parts.length >= 3) {
107
+ if (count === 0) {
108
+ return parts[0]!
109
+ }
110
+ else if (count === 1) {
111
+ return parts[1]!
112
+ }
113
+ else {
114
+ return parts[2]!
115
+ }
116
+ }
117
+
118
+ return parts[0]!
119
+ }
120
+
121
+ const translations = ref<Record<string, Record<string, string>>>({})
122
+
123
+ export const loadCurrentTranslations = async () => {
124
+ const currentLang = detectLanguage()
125
+ if (currentLang in translations.value) {
126
+ // Already loaded
127
+ return
128
+ }
129
+
130
+ translations.value[currentLang] = await loadTranslationFile(currentLang)
131
+ }
132
+
133
+ export const useTranslation = () => {
134
+ const locale = detectLanguage()
135
+
136
+ // Initialize translations if not already done.
137
+ loadCurrentTranslations()
138
+
139
+ const t = (key: string, options?: TranslationOptions | number): string => {
140
+ let result = key
141
+
142
+ if (typeof options == 'number') {
143
+ options = { count: options, n: options }
144
+ }
145
+
146
+ // Try to get translation from loaded translations first
147
+ if (translations.value && translations.value[locale] && translations.value[locale][key]) {
148
+ result = translations.value[locale][key]
149
+ }
150
+
151
+ const count = options?.n ?? options?.count
152
+ if (count !== undefined) {
153
+ result = handlePluralization(result, Number(count))
154
+ }
155
+
156
+ if (options && Object.keys(options).length > 0) {
157
+ result = interpolate(result, options)
158
+ }
159
+
160
+ return result
161
+ }
162
+
163
+ return { t, locale }
164
+ }
165
+
166
+ export const t = async (key: string, options?: TranslationOptions): Promise<string> => {
167
+ const { t: translate } = useTranslation()
168
+ return translate(key, options)
169
+ }
@@ -0,0 +1,36 @@
1
+ import { useTranslation } from '../composables/useTranslation'
2
+ import type { Activity } from '../types/activity'
3
+
4
+ export function getActivityTranslation(activity: Activity): string {
5
+ const { t } = useTranslation()
6
+
7
+ // Simple mapping of activity keys to human-readable text
8
+ const translations: Record<string, string> = {
9
+ 'dataset:created': t('a créé le jeu de données'),
10
+ 'dataset:updated': t('a mis à jour le jeu de données'),
11
+ 'dataset:deleted': t('a supprimé le jeu de données'),
12
+ 'dataset:discussed': t('a discuté du jeu de données'),
13
+ 'dataset:followed': t('suit le jeu de données'),
14
+ 'dataset:resource:added': t('a ajouté une ressource'),
15
+ 'dataset:resource:updated': t('a mis à jour une ressource'),
16
+ 'dataset:resource:deleted': t('a supprimé une ressource'),
17
+ 'dataservice:created': t('a créé le service de données'),
18
+ 'dataservice:updated': t('a mis à jour le service de données'),
19
+ 'dataservice:deleted': t('a supprimé le service de données'),
20
+ 'dataservice:discussed': t('a discuté du service de données'),
21
+ 'dataservice:followed': t('suit le service de données'),
22
+ 'organization:created': t('a créé l\'organisation'),
23
+ 'organization:updated': t('a mis à jour l\'organisation'),
24
+ 'organization:followed': t('suit l\'organisation'),
25
+ 'reuse:created': t('a créé la réutilisation'),
26
+ 'reuse:updated': t('a mis à jour la réutilisation'),
27
+ 'reuse:deleted': t('a supprimé la réutilisation'),
28
+ 'reuse:discussed': t('a discuté de la réutilisation'),
29
+ 'reuse:followed': t('suit la réutilisation'),
30
+ 'user:followed': t('suit l\'utilisateur'),
31
+ 'topic:created': t('a créé le sujet'),
32
+ 'topic:updated': t('a mis à jour le sujet'),
33
+ }
34
+
35
+ return translations[activity.key] || activity.label || activity.key
36
+ }
@@ -1,7 +1,7 @@
1
1
  import { ref, toValue, watchEffect, type ComputedRef, type Ref } from 'vue'
2
2
  import { ofetch } from 'ofetch'
3
- import { useI18n } from 'vue-i18n'
4
3
  import { useComponentsConfig } from '../config'
4
+ import { useTranslation } from '../composables/useTranslation'
5
5
  import type { AsyncData, AsyncDataExecuteOptions, AsyncDataRequestStatus, UseFetchOptions } from './api.types'
6
6
 
7
7
  export async function useFetch<DataT, ErrorT = never>(
@@ -10,7 +10,7 @@ export async function useFetch<DataT, ErrorT = never>(
10
10
  ): Promise<AsyncData<DataT, ErrorT>> {
11
11
  const config = useComponentsConfig()
12
12
 
13
- const { locale } = useI18n()
13
+ const { locale } = useTranslation()
14
14
 
15
15
  if (config.customUseFetch) {
16
16
  return await config.customUseFetch(url, options)
@@ -35,11 +35,11 @@ export async function useFetch<DataT, ErrorT = never>(
35
35
  options.headers.set('X-API-KEY', config.devApiKey)
36
36
  }
37
37
 
38
- if (locale.value) {
38
+ if (locale) {
39
39
  if (!options.params) {
40
40
  options.params = {}
41
41
  }
42
- options.params['lang'] = locale.value
42
+ options.params['lang'] = locale
43
43
  }
44
44
  },
45
45
  async onResponseError() {
@@ -1,6 +1,11 @@
1
1
  import { useComponentsConfig } from '../config'
2
2
  import type { Dataset, DatasetV2 } from '../types/datasets'
3
3
  import type { CommunityResource, Resource } from '../types/resources'
4
+ import { removeMarkdown } from './markdown'
5
+
6
+ // Dataset description constants
7
+ export const DESCRIPTION_SHORT_MAX_LENGTH = 200
8
+ export const DESCRIPTION_MIN_LENGTH = 200
4
9
 
5
10
  function constructUrl(baseUrl: string, path: string): string {
6
11
  const url = new URL(baseUrl)
@@ -22,3 +27,26 @@ export function isCommunityResource(resource: Resource | CommunityResource): boo
22
27
  export function getResourceExternalUrl(dataset: Dataset | DatasetV2 | Omit<Dataset, 'resources' | 'community_resources'>, resource: Resource | CommunityResource): string {
23
28
  return `${dataset.page}#/${isCommunityResource(resource) ? 'community-resources' : 'resources'}/${resource.id}`
24
29
  }
30
+
31
+ /**
32
+ * Returns the short description to display.
33
+ * If description_short is provided, it is used.
34
+ * Otherwise, the first DESCRIPTION_SHORT_MAX_LENGTH characters of description are used.
35
+ */
36
+ export async function getShortDescription(
37
+ description: string | null | undefined,
38
+ descriptionShort: string | null | undefined
39
+ ): Promise<string> {
40
+ if (descriptionShort?.trim()) {
41
+ return descriptionShort
42
+ }
43
+ if (description?.trim()) {
44
+ // description field is a markdown field that may contain HTML tags, so we should trim it
45
+ const plainText = (await removeMarkdown(description)).trim()
46
+ if (plainText.length > DESCRIPTION_SHORT_MAX_LENGTH) {
47
+ return `${plainText.substring(0, DESCRIPTION_SHORT_MAX_LENGTH - 1)}…`
48
+ }
49
+ return plainText
50
+ }
51
+ return ''
52
+ }
@@ -1,9 +1,9 @@
1
- import { useI18n } from 'vue-i18n'
1
+ import { useTranslation } from '../composables/useTranslation'
2
2
 
3
3
  const SECONDS_IN_A_DAY = 3600 * 24
4
4
 
5
5
  export function useFormatDate() {
6
- const { t, locale } = useI18n()
6
+ const { t, locale } = useTranslation()
7
7
 
8
8
  const formatDate = (date: Date | string | null | undefined, options: Intl.DateTimeFormatOptions = {}) => {
9
9
  if (!date) {
@@ -13,7 +13,7 @@ export function useFormatDate() {
13
13
  if (!('dateStyle' in options)) {
14
14
  options.dateStyle = 'long'
15
15
  }
16
- return new Intl.DateTimeFormat(locale.value, options).format(date)
16
+ return new Intl.DateTimeFormat(locale, options).format(date)
17
17
  }
18
18
 
19
19
  /**
@@ -58,7 +58,7 @@ export function useFormatDate() {
58
58
  const diffInUnit = Math.abs(diff / unit.seconds)
59
59
  return diffInUnit < unit.changeAfter
60
60
  })!
61
- return new Intl.RelativeTimeFormat(locale.value, { numeric: 'auto' }).format(Math.round(diff / correctUnit?.seconds), correctUnit?.unit)
61
+ return new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }).format(Math.round(diff / correctUnit?.seconds), correctUnit?.unit)
62
62
  }
63
63
 
64
64
  /**
@@ -1,9 +1,9 @@
1
- import { useI18n } from 'vue-i18n'
1
+ import { useTranslation } from '../composables/useTranslation'
2
2
 
3
3
  export const filesize = (val: number) => {
4
- const { t, locale } = useI18n()
4
+ const { t, locale } = useTranslation()
5
5
  const suffix = t('o')
6
- const formatter = new Intl.NumberFormat(locale.value, { minimumFractionDigits: 1, maximumFractionDigits: 1 })
6
+ const formatter = new Intl.NumberFormat(locale, { minimumFractionDigits: 1, maximumFractionDigits: 1 })
7
7
  const units = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
8
8
  for (const unit of units) {
9
9
  if (Math.abs(val) < 1024.0) {
@@ -1,8 +1,8 @@
1
- import { useI18n } from 'vue-i18n'
2
1
  import type { Component } from 'vue'
3
2
  import { RiBankLine, RiBuilding2Line, RiCommunityLine, RiGovernmentLine, RiUserLine } from '@remixicon/vue'
4
3
  import { useComponentsConfig } from '../config'
5
4
  import type { Organization } from '../types/organizations'
5
+ import { useTranslation } from '../composables/useTranslation'
6
6
 
7
7
  export const CERTIFIED = 'certified'
8
8
  export const PUBLIC_SERVICE = 'public-service'
@@ -31,7 +31,7 @@ export function hasBadge(organization: Organization, kind: string) {
31
31
  }
32
32
 
33
33
  export function getOrganizationTypes(): Array<{ type: OrganizationTypes | UserType, label: string, icon: Component | null }> {
34
- const { t } = useI18n()
34
+ const { t } = useTranslation()
35
35
  return [{
36
36
  type: PUBLIC_SERVICE,
37
37
  label: t('Service public'),
@@ -0,0 +1,9 @@
1
+ import { useRoute } from 'vue-router'
2
+
3
+ export function getLink(page: number): string {
4
+ const route = useRoute()
5
+ const routePath = route.path
6
+ const search = new URLSearchParams(route.query as Record<string, string>)
7
+ search.set('page', page.toFixed(0))
8
+ return `${routePath}?${search.toString()}`
9
+ }
@@ -1,6 +1,5 @@
1
1
  import { readonly, type Component } from 'vue'
2
2
 
3
- import { useI18n } from 'vue-i18n'
4
3
  import { RiEarthLine, RiMap2Line } from '@remixicon/vue'
5
4
  import Archive from '../components/Icons/Archive.vue'
6
5
  import Code from '../components/Icons/Code.vue'
@@ -9,6 +8,7 @@ import Image from '../components/Icons/Image.vue'
9
8
  import Link from '../components/Icons/Link.vue'
10
9
  import Table from '../components/Icons/Table.vue'
11
10
  import type { Resource } from '../types/resources'
11
+ import { useTranslation } from '../composables/useTranslation'
12
12
 
13
13
  export function getResourceFormatIcon(format: string): Component | null {
14
14
  switch (format?.trim()?.toLowerCase()) {
@@ -90,7 +90,7 @@ export const RESOURCE_TYPE = readonly(['main', 'documentation', 'update', 'api',
90
90
  export type ResourceType = typeof RESOURCE_TYPE[number]
91
91
 
92
92
  export const getResourceLabel = (type: ResourceType) => {
93
- const { t } = useI18n()
93
+ const { t } = useTranslation()
94
94
  switch (type) {
95
95
  case 'main':
96
96
  return t('Fichier principal') // TODO: manage the plural case
@@ -1,12 +1,12 @@
1
1
  import { ofetch } from 'ofetch'
2
- import { useI18n } from 'vue-i18n'
3
2
  import { useComponentsConfig } from '../config'
4
3
  import type { ReuseTopic, ReuseType } from '../types/reuses'
4
+ import { useTranslation } from '../composables/useTranslation'
5
5
 
6
6
  let reuseTypesRequest: Promise<Array<ReuseType>> | null = null
7
7
  export function useFetchReuseTypes() {
8
8
  const config = useComponentsConfig()
9
- const { locale } = useI18n()
9
+ const { locale } = useTranslation()
10
10
 
11
11
  return async (): Promise<Array<ReuseType>> => {
12
12
  if (reuseTypesRequest) {
@@ -15,7 +15,7 @@ export function useFetchReuseTypes() {
15
15
 
16
16
  return await (reuseTypesRequest = ofetch<Array<ReuseType>>('api/1/reuses/types/', {
17
17
  baseURL: config.apiBase,
18
- query: { lang: locale.value },
18
+ query: { lang: locale },
19
19
  }))
20
20
  }
21
21
  }
package/src/main.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import type { App, Plugin } from 'vue'
2
- import { createI18n, useI18n } from 'vue-i18n'
2
+ import type { Activity, ActivityKey } from './types/activity.js'
3
+ import type { PaginatedArray } from './types/api.js'
3
4
  import type { ContactPoint, ContactPointRole } from './types/contact_point.js'
4
- import type { Badge, Badges } from './types/badges'
5
+ import type { Badge, Badges, TranslatedBadge } from './types/badges'
5
6
  import type { Dataset, DatasetV2, DatasetV2WithFullObject, NewDataset, Quality, Rel } from './types/datasets'
6
7
  import type { NewDataservice, Dataservice, DataserviceAccessAudience, DataserviceAccessAudienceCondition, DataserviceAccessAudienceType } from './types/dataservices'
7
8
  import type { Frequency, Frequencies } from './types/frequency'
@@ -11,12 +12,14 @@ import type { License } from './types/licenses'
11
12
  import type { Member, MemberRole, NewOrganization, Organization } from './types/organizations'
12
13
  import type { Owned, OwnedWithId } from './types/owned'
13
14
  import type { NewReuse, Reuse, ReuseTopic, ReuseType } from './types/reuses'
14
- import type { TopicV2 } from './types/topics'
15
+ import type { TopicV2, TopicElement, TopicElementClass, TopicElementRel } from './types/topics'
15
16
  import type { CommunityResource, FileResourceFileType, RemoteResourceFileType, ResourceFileType, ResourceType, Resource } from './types/resources'
16
17
  import type { Site } from './types/site'
17
18
  import type { Weight, WellType } from './types/ui'
18
19
  import type { User } from './types/users'
19
20
 
21
+ import ActivityList from './components/ActivityList/ActivityList.vue'
22
+ import UserActivityList from './components/ActivityList/UserActivityList.vue'
20
23
  import AnimatedLoader from './components/AnimatedLoader.vue'
21
24
  import AppLink from './components/AppLink.vue'
22
25
  import Avatar from './components/Avatar.vue'
@@ -34,10 +37,12 @@ import DatasetQualityItem from './components/DatasetQualityItem.vue'
34
37
  import DatasetQualityScore from './components/DatasetQualityScore.vue'
35
38
  import DatasetQualityTooltipContent from './components/DatasetQualityTooltipContent.vue'
36
39
  import ExtraAccordion from './components/ExtraAccordion.vue'
40
+ import LabelTag from './components/DatasetLabelTag.vue'
37
41
  import OrganizationCard from './components/OrganizationCard.vue'
38
42
  import OrganizationNameWithCertificate from './components/OrganizationNameWithCertificate.vue'
39
43
  import OwnerType from './components/OwnerType.vue'
40
44
  import OwnerTypeIcon from './components/OwnerTypeIcon.vue'
45
+ import PaddedContainer from './components/PaddedContainer.vue'
41
46
  import Pagination from './components/Pagination.vue'
42
47
  import Placeholder from './components/Placeholder.vue'
43
48
  import ReadMore from './components/ReadMore.vue'
@@ -55,12 +60,15 @@ import TabPanel from './components/Tabs/TabPanel.vue'
55
60
  import TabPanels from './components/Tabs/TabPanels.vue'
56
61
  import Tooltip from './components/Tooltip.vue'
57
62
  import Toggletip from './components/Toggletip.vue'
63
+ import TranslationT from './components/TranslationT.vue'
58
64
  import type { UseFetchFunction } from './functions/api.types'
59
65
  import { configKey, useComponentsConfig, type PluginConfig } from './config.js'
60
66
 
61
67
  export * from './composables/useActiveDescendant'
62
68
  export * from './composables/useReuseType'
69
+ export * from './composables/useTranslation'
63
70
 
71
+ export * from './functions/activities'
64
72
  export * from './functions/datasets'
65
73
  export * from './functions/dates'
66
74
  export * from './functions/helpers'
@@ -69,6 +77,7 @@ export * from './functions/matomo'
69
77
  export * from './functions/never'
70
78
  export * from './functions/organizations'
71
79
  export * from './functions/owned'
80
+ export * from './functions/pagination'
72
81
  export * from './functions/resources'
73
82
  export * from './functions/reuses'
74
83
  export * from './functions/schemas'
@@ -76,6 +85,8 @@ export * from './functions/users'
76
85
 
77
86
  export type {
78
87
  UseFetchFunction,
88
+ Activity,
89
+ ActivityKey,
79
90
  Badge,
80
91
  Badges,
81
92
  CommunityResource,
@@ -104,6 +115,7 @@ export type {
104
115
  Organization,
105
116
  Owned,
106
117
  OwnedWithId,
118
+ PaginatedArray,
107
119
  Quality,
108
120
  Rel,
109
121
  RemoteResourceFileType,
@@ -115,7 +127,11 @@ export type {
115
127
  ReuseType,
116
128
  Site,
117
129
  SpatialZone,
130
+ TranslatedBadge,
118
131
  TopicV2,
132
+ TopicElement,
133
+ TopicElementClass,
134
+ TopicElementRel,
119
135
  User,
120
136
  Weight,
121
137
  WellType,
@@ -129,29 +145,13 @@ const datagouv: Plugin<PluginConfig> = {
129
145
  const textClamp = await import('vue3-text-clamp')
130
146
  options.textClamp = textClamp.default
131
147
  }
132
- try {
133
- // There is no condition to check if vue-i18n is instancied, only an error...
134
- useI18n()
135
- }
136
- catch {
137
- const i18n = createI18n({
138
- legacy: false,
139
- globalInjection: true,
140
- locale: 'fr',
141
- messages: {},
142
- formatFallbackMessages: true,
143
- missingWarn: false,
144
- fallbackFormat: true,
145
- fallbackWarn: false,
146
- })
147
- app.use(i18n)
148
- }
149
148
  },
150
149
  }
151
150
 
152
151
  export {
153
152
  datagouv,
154
153
  useComponentsConfig,
154
+ ActivityList,
155
155
  AnimatedLoader,
156
156
  AppLink,
157
157
  Avatar,
@@ -169,10 +169,12 @@ export {
169
169
  DatasetQualityTooltipContent,
170
170
  DateRangeDetails,
171
171
  ExtraAccordion,
172
+ LabelTag,
172
173
  OrganizationCard,
173
174
  OrganizationNameWithCertificate,
174
175
  OwnerType,
175
176
  OwnerTypeIcon,
177
+ PaddedContainer,
176
178
  Pagination,
177
179
  Placeholder,
178
180
  ReadMore,
@@ -190,4 +192,6 @@ export {
190
192
  TabPanels,
191
193
  Tooltip,
192
194
  Toggletip,
195
+ TranslationT,
196
+ UserActivityList,
193
197
  }