@datagouv/components-next 0.0.31 → 0.1.0

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 (86) hide show
  1. package/README.md +36 -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-NQ9byxF5.js +92 -0
  8. package/dist/{MapContainer.client-DXVDyZYz.js → MapContainer.client-D7Y0OXMU.js} +17587 -6826
  9. package/dist/{PdfPreview.client-XwjUHnmx.js → PdfPreview.client-DU6FbUh0.js} +47 -48
  10. package/dist/{Pmtiles.client-DfnKDlpg.js → Pmtiles.client-DCOxft6M.js} +7609 -7503
  11. package/dist/Swagger.client-DTHhEAFT.js +4 -0
  12. package/dist/XmlPreview.client-BEOCeCP8.js +84 -0
  13. package/dist/components-next.css +6 -6
  14. package/dist/components-next.js +96 -68
  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-Qu3kUOIH.js → main-DFEQrdg5.js} +54440 -56951
  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-NQY1dx9T.js → vue3-xml-viewer.common-RC76oYFu.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 +17 -17
  36. package/src/components/DatasetLabelTag.vue +40 -0
  37. package/src/components/DatasetQuality.vue +13 -11
  38. package/src/components/DatasetQualityInline.vue +7 -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 +4 -5
  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 +9 -3
  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 +2 -2
  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 +6 -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 +6 -6
  63. package/src/components/Toggletip.vue +15 -20
  64. package/src/components/TranslationT.vue +51 -0
  65. package/src/composables/useActiveDescendant.ts +1 -1
  66. package/src/composables/useTranslation.ts +169 -0
  67. package/src/functions/activities.ts +36 -0
  68. package/src/functions/api.ts +4 -4
  69. package/src/functions/datasets.ts +29 -1
  70. package/src/functions/dates.ts +4 -4
  71. package/src/functions/helpers.ts +3 -3
  72. package/src/functions/organizations.ts +3 -3
  73. package/src/functions/pagination.ts +9 -0
  74. package/src/functions/resources.ts +2 -2
  75. package/src/functions/reuses.ts +3 -3
  76. package/src/main.ts +46 -26
  77. package/src/types/activity.ts +24 -0
  78. package/src/types/api.ts +8 -0
  79. package/src/types/badges.ts +4 -0
  80. package/src/types/dataservices.ts +6 -8
  81. package/src/types/datasets.ts +3 -1
  82. package/src/types/site.ts +1 -0
  83. package/src/types/topics.ts +17 -2
  84. package/dist/JsonPreview.client-B5cv59th.js +0 -93
  85. package/dist/Swagger.client-BisHyZkP.js +0 -4
  86. package/dist/XmlPreview.client-BSjMew4d.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
@@ -61,7 +61,7 @@
61
61
  </div>
62
62
  <template v-if="lastValue && lastMonth">
63
63
  <p class="mt-1 mb-0 text-xs">
64
- {{ $t('depuis juillet 2022') }}
64
+ {{ t('depuis juillet 2022') }}
65
65
  </p>
66
66
  <p class="mt-1 mb-0 text-xs text-success-darkest">
67
67
  <strong>
@@ -124,7 +124,7 @@
124
124
  />
125
125
  </ContentLoader>
126
126
  <div
127
- v-else-if="changesThisYear"
127
+ v-else-if="data && changesThisYear"
128
128
  class="ml-2"
129
129
  >
130
130
  <SmallChart
@@ -149,21 +149,21 @@
149
149
 
150
150
  <script setup lang="ts">
151
151
  import { computed } from 'vue'
152
- import { useI18n } from 'vue-i18n'
153
152
  import { ContentLoader } from 'vue-content-loader'
154
153
  import { useFormatDate } from '../functions/dates'
155
154
  import { summarize } from '../functions/helpers'
155
+ import { useTranslation } from '../composables/useTranslation'
156
156
  import SmallChart from './SmallChart.vue'
157
157
 
158
158
  const props = defineProps<{
159
159
  title: string
160
- data: Record<string, number> | null
160
+ data?: Record<string, number> | null
161
161
  type: 'line' | 'bar'
162
162
  size?: 'sm'
163
163
  summary?: number | null
164
164
  }>()
165
165
 
166
- const { t } = useI18n()
166
+ const { t } = useTranslation()
167
167
  const { formatDate } = useFormatDate()
168
168
 
169
169
  const months = computed(() => props.data ? Object.keys(props.data) : [])
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <Popover
3
3
  v-slot="{ open, close }"
4
+ ref="popover"
4
5
  class="relative"
5
6
  >
6
7
  <!--
@@ -11,17 +12,13 @@
11
12
  :value="open"
12
13
  @changed="calculatePanelPosition"
13
14
  />
14
- <PopoverButton ref="button">
15
- <BrandedButton
16
- color="secondary-softer"
17
- icon-only
18
- :icon="RiInformationLine"
19
- size="xs"
20
- keep-margins-even-without-borders
21
- v-bind="buttonProps"
22
- >
23
- <slot />
24
- </BrandedButton>
15
+ <PopoverButton
16
+ v-bind="buttonProps"
17
+ class="w-8 h-8 rounded-full -outline-offset-2 inline-flex items-center justify-center bg-transparent border-transparent hover:!bg-gray-some"
18
+ >
19
+ <slot>
20
+ <RiInformationLine class="size-5" />
21
+ </slot>
25
22
  </PopoverButton>
26
23
 
27
24
  <ClientOnly>
@@ -49,7 +46,6 @@
49
46
  import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
50
47
  import { nextTick, onBeforeUnmount, onMounted, onUpdated, ref, useTemplateRef } from 'vue'
51
48
  import { RiInformationLine } from '@remixicon/vue'
52
- import BrandedButton from './BrandedButton.vue'
53
49
  import ClientOnly from './ClientOnly.vue'
54
50
  import ValueWatcher from './ValueWatcher.vue'
55
51
 
@@ -58,7 +54,7 @@ defineProps<{
58
54
  noMargin?: boolean
59
55
  }>()
60
56
 
61
- const buttonRef = useTemplateRef('button')
57
+ const popoverRef = useTemplateRef('popover')
62
58
  const panelStyle = ref({})
63
59
 
64
60
  // Since the parent of the component can have an overflow-hidden
@@ -66,17 +62,16 @@ const panelStyle = ref({})
66
62
  // We need to compute the correct position of the tooltip.
67
63
  const calculatePanelPosition = () => {
68
64
  nextTick(() => {
69
- const button = buttonRef.value?.$el || buttonRef.value
65
+ const popover = popoverRef.value?.$el
70
66
 
71
- if (!button) {
72
- console.error('Cannot find the button of the Toggletip.)')
67
+ if (!popover) {
68
+ console.error('Cannot find the popover of the Toggletip.)')
73
69
  return
74
70
  }
75
-
76
- const buttonRect = button.getBoundingClientRect()
71
+ const popoverRect = popover.getBoundingClientRect()
77
72
  panelStyle.value = {
78
- left: `${buttonRect.left + window.scrollX}px`,
79
- top: `${buttonRect.bottom + window.scrollY}px`,
73
+ left: `${popoverRect.left + window.scrollX}px`,
74
+ top: `${popoverRect.bottom + window.scrollY}px`,
80
75
  }
81
76
  })
82
77
  }
@@ -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>
@@ -5,7 +5,7 @@ export type Option = {
5
5
  id: string
6
6
  }
7
7
 
8
- export default function useActiveDescendant<T extends Option>(options: MaybeRefOrGetter<Array<T>>, direction: 'horizontal' | 'vertical') {
8
+ export function useActiveDescendant<T extends Option>(options: MaybeRefOrGetter<Array<T>>, direction: 'horizontal' | 'vertical') {
9
9
  const active = ref<string | undefined>()
10
10
 
11
11
  const activeOption = computed<T | undefined>(() => toValue(options).find(option => option.id === active.value))
@@ -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]![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)
@@ -8,7 +13,7 @@ function constructUrl(baseUrl: string, path: string): string {
8
13
  return url.toString()
9
14
  }
10
15
 
11
- export default function getDatasetOEmbedHtml(type: string, id: string): string {
16
+ export function getDatasetOEmbedHtml(type: string, id: string): string {
12
17
  const config = useComponentsConfig()
13
18
 
14
19
  const staticUrl = constructUrl(config.baseUrl, 'oembed.js')
@@ -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'),
@@ -91,7 +91,7 @@ export function isOrganizationCertified(organization: Organization | null): bool
91
91
  return hasBadge(organization, CERTIFIED) && (isType(organization, PUBLIC_SERVICE) || isType(organization, LOCAL_AUTHORITY))
92
92
  }
93
93
 
94
- export default function getOrganizationOEmbedHtml(type: string, id: string): string {
94
+ export function getOrganizationOEmbedHtml(type: string, id: string): string {
95
95
  const config = useComponentsConfig()
96
96
 
97
97
  const staticUrl = constructUrl(config.baseUrl, 'oembed.js')
@@ -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
  }