@datagouv/components-next 0.0.29 → 0.0.31
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/README.md +10 -2
- package/assets/main.css +4 -0
- package/dist/{JsonPreview.client-Dv-dKju6.js → JsonPreview.client-B5cv59th.js} +12 -12
- package/dist/{MapContainer.client-Di5UOWc5.js → MapContainer.client-DXVDyZYz.js} +1 -1
- package/dist/{PdfPreview.client-BPzufzxb.js → PdfPreview.client-XwjUHnmx.js} +5 -5
- package/dist/{Pmtiles.client-0o7LzYmY.js → Pmtiles.client-DfnKDlpg.js} +1 -1
- package/dist/Swagger.client-BisHyZkP.js +4 -0
- package/dist/{XmlPreview.client-BwkMlaG7.js → XmlPreview.client-BSjMew4d.js} +14 -14
- package/dist/components-next.css +1 -1
- package/dist/components-next.js +32 -31
- package/dist/components.css +1 -1
- package/dist/{main-DIQDGjO4.js → main-Qu3kUOIH.js} +14488 -14473
- package/dist/{vue3-xml-viewer.common-CpVTMgj2.js → vue3-xml-viewer.common-NQY1dx9T.js} +1 -1
- package/package.json +1 -1
- package/src/components/Avatar.vue +2 -1
- package/src/components/BrandedButton.vue +4 -1
- package/src/components/ClientOnly.vue +17 -0
- package/src/components/DataserviceCard.vue +50 -78
- package/src/components/DatasetCard.vue +14 -14
- package/src/components/DatasetQuality.vue +1 -2
- package/src/components/DatasetQualityInline.vue +1 -3
- package/src/components/OrganizationCard.vue +9 -7
- package/src/components/OwnerType.vue +1 -1
- package/src/components/ResourceAccordion/DataStructure.vue +6 -5
- package/src/components/ResourceAccordion/JsonPreview.client.vue +5 -5
- package/src/components/ResourceAccordion/PdfPreview.client.vue +3 -3
- package/src/components/ResourceAccordion/Preview.vue +5 -5
- package/src/components/ResourceAccordion/ResourceAccordion.vue +2 -2
- package/src/components/ResourceAccordion/SchemaBadge.vue +5 -3
- package/src/components/ResourceAccordion/XmlPreview.client.vue +5 -5
- package/src/components/Tabs/TabGroup.vue +13 -1
- package/src/components/Toggletip.vue +81 -49
- package/src/components/ValueWatcher.vue +18 -0
- package/src/composables/useReuseType.ts +2 -1
- package/src/config.ts +5 -4
- package/src/functions/organizations.ts +14 -0
- package/src/functions/reuses.ts +10 -7
- package/src/functions/schemas.ts +32 -31
- package/src/functions/tabularApi.ts +2 -3
- package/src/functions/users.ts +2 -2
- package/src/main.ts +2 -0
- package/src/types/dataservices.ts +1 -0
- package/src/types/site.ts +3 -0
- package/dist/Swagger.client-H6fmX_7L.js +0 -4
- package/src/components/ToggletipButton.vue +0 -14
|
@@ -1,62 +1,94 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Popover
|
|
3
|
-
v-slot="{ open }"
|
|
4
|
-
class="relative
|
|
5
|
-
:focus="true"
|
|
3
|
+
v-slot="{ open, close }"
|
|
4
|
+
class="relative"
|
|
6
5
|
>
|
|
7
|
-
|
|
8
|
-
v-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
v-
|
|
23
|
-
class="toggletip"
|
|
24
|
-
:class="{
|
|
25
|
-
'p-0': noMargin,
|
|
26
|
-
'left-0': position === 'right',
|
|
27
|
-
'ml-6 top-24': teleportId,
|
|
28
|
-
}"
|
|
6
|
+
<!--
|
|
7
|
+
Little trick to watch for v-slot changes because HeadlessUI doesn't raise an event on open… :-(
|
|
8
|
+
Need to recompute on show because sometimes, the positions where incorrect because after first render, some div load and change the relative position of the button…
|
|
9
|
+
-->
|
|
10
|
+
<ValueWatcher
|
|
11
|
+
:value="open"
|
|
12
|
+
@changed="calculatePanelPosition"
|
|
13
|
+
/>
|
|
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"
|
|
29
22
|
>
|
|
30
|
-
<slot
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
<slot />
|
|
24
|
+
</BrandedButton>
|
|
25
|
+
</PopoverButton>
|
|
26
|
+
|
|
27
|
+
<ClientOnly>
|
|
28
|
+
<Teleport to="#tooltips">
|
|
29
|
+
<PopoverPanel
|
|
30
|
+
v-show="open"
|
|
31
|
+
class="toggletip absolute z-10"
|
|
32
|
+
:class="{
|
|
33
|
+
'p-0': noMargin,
|
|
34
|
+
}"
|
|
35
|
+
:style="panelStyle"
|
|
36
|
+
static
|
|
37
|
+
>
|
|
38
|
+
<slot
|
|
39
|
+
name="toggletip"
|
|
40
|
+
:close
|
|
41
|
+
/>
|
|
42
|
+
</PopoverPanel>
|
|
43
|
+
</Teleport>
|
|
44
|
+
</ClientOnly>
|
|
36
45
|
</Popover>
|
|
37
46
|
</template>
|
|
38
47
|
|
|
39
48
|
<script setup lang="ts">
|
|
40
49
|
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
|
|
41
|
-
import {
|
|
42
|
-
import
|
|
50
|
+
import { nextTick, onBeforeUnmount, onMounted, onUpdated, ref, useTemplateRef } from 'vue'
|
|
51
|
+
import { RiInformationLine } from '@remixicon/vue'
|
|
52
|
+
import BrandedButton from './BrandedButton.vue'
|
|
53
|
+
import ClientOnly from './ClientOnly.vue'
|
|
54
|
+
import ValueWatcher from './ValueWatcher.vue'
|
|
43
55
|
|
|
44
|
-
|
|
56
|
+
defineProps<{
|
|
57
|
+
buttonProps?: object
|
|
45
58
|
noMargin?: boolean
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
}>()
|
|
60
|
+
|
|
61
|
+
const buttonRef = useTemplateRef('button')
|
|
62
|
+
const panelStyle = ref({})
|
|
63
|
+
|
|
64
|
+
// Since the parent of the component can have an overflow-hidden
|
|
65
|
+
// we teleport the popover to a #tooltips div in the layout.
|
|
66
|
+
// We need to compute the correct position of the tooltip.
|
|
67
|
+
const calculatePanelPosition = () => {
|
|
68
|
+
nextTick(() => {
|
|
69
|
+
const button = buttonRef.value?.$el || buttonRef.value
|
|
70
|
+
|
|
71
|
+
if (!button) {
|
|
72
|
+
console.error('Cannot find the button of the Toggletip.)')
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const buttonRect = button.getBoundingClientRect()
|
|
77
|
+
panelStyle.value = {
|
|
78
|
+
left: `${buttonRect.left + window.scrollX}px`,
|
|
79
|
+
top: `${buttonRect.bottom + window.scrollY}px`,
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
onMounted(() => {
|
|
85
|
+
calculatePanelPosition()
|
|
86
|
+
window.addEventListener('resize', calculatePanelPosition)
|
|
51
87
|
})
|
|
52
|
-
defineOptions({ inheritAttrs: false })
|
|
53
|
-
</script>
|
|
54
88
|
|
|
55
|
-
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
</style>
|
|
89
|
+
onBeforeUnmount(() => {
|
|
90
|
+
window.removeEventListener('resize', calculatePanelPosition)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
onUpdated(() => calculatePanelPosition())
|
|
94
|
+
</script>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<slot />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts" generic="T">
|
|
6
|
+
import { watch } from 'vue'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
value: T
|
|
10
|
+
}>()
|
|
11
|
+
const emits = defineEmits<{
|
|
12
|
+
changed: [T]
|
|
13
|
+
}>()
|
|
14
|
+
|
|
15
|
+
watch(() => props.value, () => {
|
|
16
|
+
emits('changed', props.value)
|
|
17
|
+
})
|
|
18
|
+
</script>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { computedAsync } from '@vueuse/core'
|
|
2
2
|
import { toValue, type MaybeRefOrGetter } from 'vue'
|
|
3
|
-
import {
|
|
3
|
+
import { useFetchReuseTypes, getType } from '../functions/reuses'
|
|
4
4
|
|
|
5
5
|
export function useReuseType(id: MaybeRefOrGetter<string>) {
|
|
6
|
+
const fetchReuseTypes = useFetchReuseTypes()
|
|
6
7
|
const label = computedAsync(async () => {
|
|
7
8
|
const idValue = toValue(id)
|
|
8
9
|
const types = await fetchReuseTypes()
|
package/src/config.ts
CHANGED
|
@@ -7,19 +7,20 @@ export type PluginConfig = {
|
|
|
7
7
|
apiBase: string
|
|
8
8
|
devApiKey?: string | null
|
|
9
9
|
datasetQualityGuideUrl?: string
|
|
10
|
+
maxJsonPreviewCharSize?: number // Maximum size of JSON to preview in characters. JSON preview module is partly collapsed by default so we can have a preview for large files.
|
|
11
|
+
maxPdfPreviewByteSize?: number // Maximum size of PDF to preview in bytes
|
|
12
|
+
maxXmlPreviewCharSize?: number // Maximum size of XML to preview in characters. XML preview module can NOT be collapsed by default so we should not have a preview for large files.
|
|
13
|
+
pmtilesViewerBaseUrl?: string | null // Base URL of a pmtiles viewer (ex: https://pmtiles.io/#url=)
|
|
10
14
|
schemaValidataUrl?: string
|
|
11
15
|
schemaDocumentationUrl?: string
|
|
12
16
|
tabularApiUrl?: string
|
|
13
17
|
tabularApiPageSize?: number
|
|
14
18
|
tabularAllowRemote?: boolean
|
|
15
19
|
tabularApiDataserviceId?: string
|
|
16
|
-
pmtilesViewerBaseUrl?: string | null // Base URL of a pmtiles viewer (ex: https://pmtiles.io/#url=)
|
|
17
20
|
customUseFetch?: UseFetchFunction | null
|
|
18
21
|
textClamp?: string | Component | null
|
|
19
22
|
appLink?: Component | null
|
|
20
|
-
|
|
21
|
-
maxPdfPreviewSize?: number // Maximum size of PDF to preview in bytes
|
|
22
|
-
maxXmlPreviewSize?: number // Maximum size of XML to preview in characters
|
|
23
|
+
clientOnly?: Component | null
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export const configKey = Symbol() as InjectionKey<PluginConfig>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useI18n } from 'vue-i18n'
|
|
2
2
|
import type { Component } from 'vue'
|
|
3
3
|
import { RiBankLine, RiBuilding2Line, RiCommunityLine, RiGovernmentLine, RiUserLine } from '@remixicon/vue'
|
|
4
|
+
import { useComponentsConfig } from '../config'
|
|
4
5
|
import type { Organization } from '../types/organizations'
|
|
5
6
|
|
|
6
7
|
export const CERTIFIED = 'certified'
|
|
@@ -15,6 +16,12 @@ export type OrganizationTypes = typeof PUBLIC_SERVICE | typeof ASSOCIATION | typ
|
|
|
15
16
|
|
|
16
17
|
export type UserType = typeof USER
|
|
17
18
|
|
|
19
|
+
function constructUrl(baseUrl: string, path: string): string {
|
|
20
|
+
const url = new URL(baseUrl)
|
|
21
|
+
url.pathname = `${url.pathname}${path}`
|
|
22
|
+
return url.toString()
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
export function isType(organization: Organization, type: OrganizationTypes) {
|
|
19
26
|
return hasBadge(organization, type)
|
|
20
27
|
}
|
|
@@ -83,3 +90,10 @@ export function isOrganizationCertified(organization: Organization | null): bool
|
|
|
83
90
|
if (!organization) return false
|
|
84
91
|
return hasBadge(organization, CERTIFIED) && (isType(organization, PUBLIC_SERVICE) || isType(organization, LOCAL_AUTHORITY))
|
|
85
92
|
}
|
|
93
|
+
|
|
94
|
+
export default function getOrganizationOEmbedHtml(type: string, id: string): string {
|
|
95
|
+
const config = useComponentsConfig()
|
|
96
|
+
|
|
97
|
+
const staticUrl = constructUrl(config.baseUrl, 'oembed.js')
|
|
98
|
+
return `<div data-udata-${type}="${id}" data-height="1500" data-width="1200"></div><script data-udata="${config.baseUrl}" src="${staticUrl}" async defer></script>`
|
|
99
|
+
}
|
package/src/functions/reuses.ts
CHANGED
|
@@ -4,17 +4,20 @@ import { useComponentsConfig } from '../config'
|
|
|
4
4
|
import type { ReuseTopic, ReuseType } from '../types/reuses'
|
|
5
5
|
|
|
6
6
|
let reuseTypesRequest: Promise<Array<ReuseType>> | null = null
|
|
7
|
+
export function useFetchReuseTypes() {
|
|
8
|
+
const config = useComponentsConfig()
|
|
9
|
+
const { locale } = useI18n()
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
return async (): Promise<Array<ReuseType>> => {
|
|
12
|
+
if (reuseTypesRequest) {
|
|
13
|
+
return reuseTypesRequest
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return await (reuseTypesRequest = ofetch<Array<ReuseType>>('api/1/reuses/types/', {
|
|
13
17
|
baseURL: config.apiBase,
|
|
14
18
|
query: { lang: locale.value },
|
|
15
|
-
})
|
|
19
|
+
}))
|
|
16
20
|
}
|
|
17
|
-
return await reuseTypesRequest
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export function getType(types: Array<ReuseType>, id: string): string {
|
package/src/functions/schemas.ts
CHANGED
|
@@ -45,20 +45,19 @@ export type SchemaResponseData = Array<RegisteredSchema>
|
|
|
45
45
|
|
|
46
46
|
type SchemaPath = { schema_name: string } | { schema_url: string }
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Get Schema Catalog
|
|
52
|
-
*/
|
|
53
|
-
export async function getCatalog(): Promise<SchemaResponseData> {
|
|
48
|
+
let catalogRequest: Promise<Array<RegisteredSchema>> | null = null
|
|
49
|
+
export function useGetCatalog() {
|
|
54
50
|
const config = useComponentsConfig()
|
|
55
|
-
if (catalogRequest) {
|
|
56
|
-
return catalogRequest
|
|
57
|
-
}
|
|
58
51
|
|
|
59
|
-
return
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
return async (): Promise<SchemaResponseData> => {
|
|
53
|
+
if (catalogRequest) {
|
|
54
|
+
return catalogRequest
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return await (catalogRequest = ofetch('api/1/datasets/schemas/', {
|
|
58
|
+
baseURL: config.apiBase,
|
|
59
|
+
}))
|
|
60
|
+
}
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
export function findSchemaInCatalog(catalog: Array<RegisteredSchema>, schema: Schema | null): RegisteredSchema | null {
|
|
@@ -66,30 +65,32 @@ export function findSchemaInCatalog(catalog: Array<RegisteredSchema>, schema: Sc
|
|
|
66
65
|
return catalog.find(registeredSchema => schema.name === registeredSchema.name) || null
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
export function
|
|
68
|
+
export function useGetSchemaDocumentation() {
|
|
70
69
|
const config = useComponentsConfig()
|
|
71
|
-
return `${config.schemaDocumentationUrl}${name}/`
|
|
70
|
+
return (name: string) => `${config.schemaDocumentationUrl}${name}/`
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
export function
|
|
73
|
+
export function useGetSchemaValidationUrl() {
|
|
75
74
|
const config = useComponentsConfig()
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
return (resource: Resource, registeredSchema: RegisteredSchema) => {
|
|
76
|
+
if (!resource.schema || !resource.schema.name) {
|
|
77
|
+
return null
|
|
78
|
+
}
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
let schemaPath: SchemaPath = { schema_name: `schema-datagouvfr.${resource.schema.name}` }
|
|
81
|
+
if (resource.schema && resource.schema.version) {
|
|
82
|
+
const schemaVersion = resource.schema.version
|
|
83
|
+
const versionUrl = registeredSchema.versions.find(version => version.version_name === schemaVersion)?.schema_url
|
|
84
|
+
if (versionUrl) {
|
|
85
|
+
schemaPath = { schema_url: versionUrl }
|
|
86
|
+
}
|
|
86
87
|
}
|
|
88
|
+
const query = new URLSearchParams({
|
|
89
|
+
'input': 'url',
|
|
90
|
+
'url': resource.url,
|
|
91
|
+
'header-case': 'on',
|
|
92
|
+
...schemaPath,
|
|
93
|
+
}).toString()
|
|
94
|
+
return `${config.schemaValidataUrl}/table-schema?${query}`
|
|
87
95
|
}
|
|
88
|
-
const query = new URLSearchParams({
|
|
89
|
-
'input': 'url',
|
|
90
|
-
'url': resource.url,
|
|
91
|
-
'header-case': 'on',
|
|
92
|
-
...schemaPath,
|
|
93
|
-
}).toString()
|
|
94
|
-
return `${config.schemaValidataUrl}/table-schema?${query}`
|
|
95
96
|
}
|
|
@@ -20,8 +20,7 @@ export async function getData(config: PluginConfig, id: string, page: number, so
|
|
|
20
20
|
/**
|
|
21
21
|
* Call Tabular-api to get table profile
|
|
22
22
|
*/
|
|
23
|
-
export
|
|
23
|
+
export function useGetProfile() {
|
|
24
24
|
const config = useComponentsConfig()
|
|
25
|
-
|
|
26
|
-
return await ofetch(`${config.tabularApiUrl}/api/resources/${id}/profile/`)
|
|
25
|
+
return (id: string) => ofetch(`${config.tabularApiUrl}/api/resources/${id}/profile/`)
|
|
27
26
|
}
|
package/src/functions/users.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { User } from '../types/users'
|
|
2
2
|
import { useComponentsConfig } from '../config'
|
|
3
3
|
|
|
4
|
-
export function
|
|
4
|
+
export function useGetUserAvatar() {
|
|
5
5
|
const config = useComponentsConfig()
|
|
6
|
-
return user.avatar_thumbnail || `${config.apiBase}/api/1/avatars/${user.id}/${size}`
|
|
6
|
+
return (user: User, size: number) => user.avatar_thumbnail || `${config.apiBase}/api/1/avatars/${user.id}/${size}`
|
|
7
7
|
}
|
package/src/main.ts
CHANGED
|
@@ -47,6 +47,7 @@ import ReuseCard from './components/ReuseCard.vue'
|
|
|
47
47
|
import SimpleBanner from './components/SimpleBanner.vue'
|
|
48
48
|
import StatBox from './components/StatBox.vue'
|
|
49
49
|
import Tooltip from './components/Tooltip.vue'
|
|
50
|
+
import Toggletip from './components/Toggletip.vue'
|
|
50
51
|
import type { UseFetchFunction } from './functions/api.types'
|
|
51
52
|
import { configKey, useComponentsConfig, type PluginConfig } from './config.js'
|
|
52
53
|
|
|
@@ -172,4 +173,5 @@ export {
|
|
|
172
173
|
StatBox,
|
|
173
174
|
Swagger,
|
|
174
175
|
Tooltip,
|
|
176
|
+
Toggletip,
|
|
175
177
|
}
|
package/src/types/site.ts
CHANGED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<BrandedButton
|
|
3
|
-
color="secondary-softer"
|
|
4
|
-
icon-only
|
|
5
|
-
:icon="RiInformationLine"
|
|
6
|
-
size="xs"
|
|
7
|
-
keep-margins-even-without-borders
|
|
8
|
-
/>
|
|
9
|
-
</template>
|
|
10
|
-
|
|
11
|
-
<script setup lang="ts">
|
|
12
|
-
import { RiInformationLine } from '@remixicon/vue'
|
|
13
|
-
import BrandedButton from './BrandedButton.vue'
|
|
14
|
-
</script>
|