@datagouv/components-next 0.2.0 → 1.0.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.
- package/README.md +1 -1
- package/assets/main.css +56 -1
- package/dist/Control-BNCDn-8E.js +148 -0
- package/dist/{Datafair.client-x39O4yfF.js → Datafair.client-Dls5AHTE.js} +1 -1
- package/dist/Event-BOgJUhNR.js +738 -0
- package/dist/Image-BN-4XkIn.js +247 -0
- package/dist/{JsonPreview.client-BMsC5JcY.js → JsonPreview.client-DPDTs433.js} +14 -14
- package/dist/Map-BdT3i2C4.js +7609 -0
- package/dist/MapContainer.client-BdAzd7bj.js +105 -0
- package/dist/OSM-CamriM9b.js +71 -0
- package/dist/{PdfPreview.client-COOkEkRA.js → PdfPreview.client-CopqSDyt.js} +3 -3
- package/dist/{Pmtiles.client-BaiIo4VZ.js → Pmtiles.client-mF6xaOO_.js} +2 -2
- package/dist/ScaleLine-BiesrgOv.js +165 -0
- package/dist/Swagger.client-eJ7gpfZA.js +4 -0
- package/dist/Tile-DCuqwNOI.js +1206 -0
- package/dist/TileImage-CmZf8EdU.js +1067 -0
- package/dist/View-DcDc7N2K.js +2858 -0
- package/dist/{XmlPreview.client-CAdN0w_Y.js → XmlPreview.client-C0OgBkSq.js} +7 -7
- package/dist/common-C4rDcQpp.js +243 -0
- package/dist/components-next.css +1 -1
- package/dist/components-next.js +153 -117
- package/dist/components.css +1 -1
- package/dist/{MapContainer.client-DeSo8EvG.js → index-BRGqW8aQ.js} +4975 -21416
- package/dist/leaflet-src-7m1mB8LI.js +6338 -0
- package/dist/{main-Dgri3TQL.js → main-CNHxAJ8J.js} +56758 -51450
- package/dist/proj-CKwYjU38.js +1569 -0
- package/dist/tilecoord-YW3qEH_j.js +884 -0
- package/dist/{vue3-xml-viewer.common-D6skc_Ai.js → vue3-xml-viewer.common-CmAdQfIy.js} +1 -1
- package/package.json +5 -1
- package/src/components/ActivityList/ActivityList.vue +6 -2
- package/src/components/AppLink.vue +4 -1
- package/src/components/Avatar.vue +2 -2
- package/src/components/AvatarWithName.vue +8 -4
- package/src/components/BouncingDots.vue +21 -0
- package/src/components/BrandedButton.vue +2 -0
- package/src/components/CopyButton.vue +19 -7
- package/src/components/DataserviceCard.vue +83 -118
- package/src/components/DatasetCard.vue +110 -171
- package/src/components/DatasetInformation/DatasetEmbedSection.vue +43 -0
- package/src/components/DatasetInformation/DatasetInformationSection.vue +73 -0
- package/src/components/DatasetInformation/DatasetSchemaSection.vue +74 -0
- package/src/components/DatasetInformation/DatasetSpatialSection.vue +59 -0
- package/src/components/DatasetInformation/DatasetTemporalitySection.vue +45 -0
- package/src/components/DatasetInformation/index.ts +5 -0
- package/src/components/DatasetQualityTooltipContent.vue +3 -3
- package/src/components/DescriptionList.vue +1 -4
- package/src/components/DescriptionListDetails.vue +5 -0
- package/src/components/DescriptionListTerm.vue +5 -0
- package/src/components/DiscussionMessageCard.vue +63 -0
- package/src/components/ExtraAccordion.vue +4 -4
- package/src/components/Form/BadgeSelect.vue +35 -0
- package/src/components/Form/FormatSelect.vue +28 -0
- package/src/components/Form/GeozoneSelect.vue +52 -0
- package/src/components/Form/GranularitySelect.vue +29 -0
- package/src/components/Form/LicenseSelect.vue +30 -0
- package/src/components/Form/OrganizationSelect.vue +62 -0
- package/src/components/Form/OrganizationTypeSelect.vue +34 -0
- package/src/components/Form/ReuseTopicSelect.vue +29 -0
- package/src/components/Form/SchemaSelect.vue +30 -0
- package/src/components/Form/SearchableSelect.vue +334 -0
- package/src/components/Form/SelectGroup.vue +132 -0
- package/src/components/Form/TagSelect.vue +38 -0
- package/src/components/LeafletMap.vue +31 -0
- package/src/components/LicenseBadge.vue +24 -0
- package/src/components/LoadingBlock.vue +23 -2
- package/src/components/MarkdownViewer.vue +3 -1
- package/src/components/ObjectCard.vue +42 -0
- package/src/components/ObjectCardBadge.vue +22 -0
- package/src/components/ObjectCardHeader.vue +35 -0
- package/src/components/ObjectCardOwner.vue +43 -0
- package/src/components/ObjectCardShortDescription.vue +28 -0
- package/src/components/OrganizationCard.vue +35 -20
- package/src/components/OrganizationLogo.vue +1 -1
- package/src/components/OrganizationNameWithCertificate.vue +13 -7
- package/src/components/OwnerTypeIcon.vue +1 -0
- package/src/components/Pagination.vue +1 -1
- package/src/components/Placeholder.vue +5 -2
- package/src/components/PostCard.vue +62 -0
- package/src/components/RadioGroup.vue +32 -0
- package/src/components/RadioInput.vue +64 -0
- package/src/components/ResourceAccordion/EditButton.vue +2 -3
- package/src/components/ResourceAccordion/MapContainer.client.vue +20 -16
- package/src/components/ResourceAccordion/Metadata.vue +11 -24
- package/src/components/ResourceAccordion/Pmtiles.client.vue +1 -1
- package/src/components/ResourceAccordion/Preview.vue +1 -1
- package/src/components/ResourceAccordion/ResourceAccordion.vue +30 -20
- package/src/components/ResourceAccordion/ResourceIcon.vue +1 -0
- package/src/components/ResourceAccordion/SchemaBadge.vue +2 -2
- package/src/components/ResourceExplorer/ResourceExplorer.vue +243 -0
- package/src/components/ResourceExplorer/ResourceExplorerSidebar.vue +116 -0
- package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +361 -0
- package/src/components/ReuseCard.vue +8 -28
- package/src/components/ReuseHorizontalCard.vue +80 -0
- package/src/components/Search/BasicAndAdvancedFilters.vue +49 -0
- package/src/components/Search/Filter/AccessTypeFilter.vue +37 -0
- package/src/components/Search/Filter/DatasetBadgeFilter.vue +40 -0
- package/src/components/Search/Filter/FilterButtonGroup.vue +78 -0
- package/src/components/Search/Filter/FormatFamilyFilter.vue +39 -0
- package/src/components/Search/Filter/LastUpdateRangeFilter.vue +37 -0
- package/src/components/Search/Filter/ProducerTypeFilter.vue +39 -0
- package/src/components/Search/Filter/ReuseTypeFilter.vue +42 -0
- package/src/components/Search/GlobalSearch.vue +611 -0
- package/src/components/Search/SearchInput.vue +63 -0
- package/src/components/Search/Sidemenu.vue +38 -0
- package/src/components/StatBox.vue +5 -5
- package/src/components/Tag.vue +30 -0
- package/src/components/Toggletip.vue +6 -2
- package/src/components/Tooltip.vue +2 -3
- package/src/components/TopicCard.vue +134 -0
- package/src/components/radioGroupContext.ts +9 -0
- package/src/composables/useDebouncedRef.ts +31 -0
- package/src/composables/useMetrics.ts +4 -3
- package/src/composables/useResourceCapabilities.ts +118 -0
- package/src/composables/useRouteQueryBoolean.ts +10 -0
- package/src/composables/useSelectModelSync.ts +89 -0
- package/src/composables/useStableQueryParams.ts +84 -0
- package/src/config.ts +4 -0
- package/src/functions/api.ts +17 -6
- package/src/functions/api.types.ts +4 -2
- package/src/functions/datasets.ts +1 -29
- package/src/functions/description.ts +33 -0
- package/src/functions/helpers.ts +11 -0
- package/src/functions/markdown.ts +60 -16
- package/src/functions/metrics.ts +33 -0
- package/src/functions/organizations.ts +5 -5
- package/src/main.ts +89 -7
- package/src/types/dataservices.ts +14 -12
- package/src/types/datasets.ts +20 -7
- package/src/types/discussions.ts +20 -0
- package/src/types/licenses.ts +3 -3
- package/src/types/organizations.ts +13 -1
- package/src/types/owned.ts +4 -2
- package/src/types/pages.ts +70 -0
- package/src/types/posts.ts +27 -0
- package/src/types/resources.ts +6 -0
- package/src/types/reuses.ts +14 -5
- package/src/types/search.ts +379 -0
- package/src/types/users.ts +12 -3
- package/dist/Swagger.client-CpLgaLg6.js +0 -4
- package/src/components/DatasetInformationPanel.vue +0 -211
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<h3 class="w-full text-base mb-0 flex">
|
|
3
|
+
<AppLink
|
|
4
|
+
:to="url"
|
|
5
|
+
class="text-gray-title text-base bg-none flex items-center w-full truncate gap-1"
|
|
6
|
+
:target="target"
|
|
7
|
+
>
|
|
8
|
+
<component
|
|
9
|
+
:is="icon"
|
|
10
|
+
aria-hidden="true"
|
|
11
|
+
class="size-4 flex-none"
|
|
12
|
+
/>
|
|
13
|
+
<span
|
|
14
|
+
class="block truncate"
|
|
15
|
+
:class="$slots.extra ? 'flex-initial' : 'flex-1'"
|
|
16
|
+
>
|
|
17
|
+
<slot />
|
|
18
|
+
</span>
|
|
19
|
+
<slot name="extra" />
|
|
20
|
+
<span class="absolute inset-0" />
|
|
21
|
+
</AppLink>
|
|
22
|
+
</h3>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup lang="ts">
|
|
26
|
+
import type { Component } from 'vue'
|
|
27
|
+
import type { RouteLocationRaw } from 'vue-router'
|
|
28
|
+
import AppLink from './AppLink.vue'
|
|
29
|
+
|
|
30
|
+
defineProps<{
|
|
31
|
+
icon: Component
|
|
32
|
+
url: RouteLocationRaw
|
|
33
|
+
target?: string
|
|
34
|
+
}>()
|
|
35
|
+
</script>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="organization"
|
|
4
|
+
class="-mr-0.5 flex-initial truncate"
|
|
5
|
+
>
|
|
6
|
+
<AppLink
|
|
7
|
+
class="link text-sm overflow-hidden flex items-center relative z-[2] truncate"
|
|
8
|
+
:to="organizationUrl || organization.page"
|
|
9
|
+
>
|
|
10
|
+
<OrganizationNameWithCertificate
|
|
11
|
+
:organization
|
|
12
|
+
size="sm"
|
|
13
|
+
/>
|
|
14
|
+
</AppLink>
|
|
15
|
+
</div>
|
|
16
|
+
<div
|
|
17
|
+
v-else-if="ownerName"
|
|
18
|
+
class="mr-1 truncate"
|
|
19
|
+
>
|
|
20
|
+
{{ ownerName }}
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import { computed } from 'vue'
|
|
26
|
+
import type { RouteLocationRaw } from 'vue-router'
|
|
27
|
+
import { getOwnerName } from '../functions/owned'
|
|
28
|
+
import type { OrganizationReference } from '../types/organizations'
|
|
29
|
+
import type { UserReference } from '../types/users'
|
|
30
|
+
import AppLink from './AppLink.vue'
|
|
31
|
+
import OrganizationNameWithCertificate from './OrganizationNameWithCertificate.vue'
|
|
32
|
+
|
|
33
|
+
const props = defineProps<{
|
|
34
|
+
organization?: OrganizationReference | null
|
|
35
|
+
owner?: UserReference | null
|
|
36
|
+
organizationUrl?: RouteLocationRaw
|
|
37
|
+
}>()
|
|
38
|
+
|
|
39
|
+
const ownerName = computed(() => {
|
|
40
|
+
if (!props.owner) return ''
|
|
41
|
+
return getOwnerName({ organization: null, owner: props.owner })
|
|
42
|
+
})
|
|
43
|
+
</script>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<p
|
|
3
|
+
v-if="shortDescription"
|
|
4
|
+
class="fr-text--sm fr-mt-1w fr-mb-0 overflow-wrap-anywhere hidden sm:line-clamp-2"
|
|
5
|
+
>
|
|
6
|
+
{{ shortDescription }}
|
|
7
|
+
</p>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { computed } from 'vue'
|
|
12
|
+
import { removeMarkdownSync } from '../functions/markdown'
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<{
|
|
15
|
+
text?: string | null
|
|
16
|
+
maxLength?: number
|
|
17
|
+
}>(), {
|
|
18
|
+
maxLength: 300,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Truncate in JS to avoid sending very long markdown-cleaned text to the DOM.
|
|
22
|
+
// The visual clamp is handled by CSS line-clamp-2.
|
|
23
|
+
const shortDescription = computed(() => {
|
|
24
|
+
if (!props.text) return ''
|
|
25
|
+
const cleaned = removeMarkdownSync(props.text)
|
|
26
|
+
return cleaned.length > props.maxLength ? cleaned.substring(0, props.maxLength) + '…' : cleaned
|
|
27
|
+
})
|
|
28
|
+
</script>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
loading="lazy"
|
|
12
12
|
>
|
|
13
13
|
</div>
|
|
14
|
-
<
|
|
14
|
+
<div class="mb-0.5 font-bold">
|
|
15
15
|
<AppLink
|
|
16
16
|
:to="organization.page"
|
|
17
17
|
class="overflow-hidden"
|
|
@@ -21,55 +21,70 @@
|
|
|
21
21
|
:organization
|
|
22
22
|
/>
|
|
23
23
|
</AppLink>
|
|
24
|
-
</
|
|
24
|
+
</div>
|
|
25
25
|
<div class="mb-2 flex flex-wrap gap-1 items-center">
|
|
26
26
|
<template v-if="type !== 'other'">
|
|
27
27
|
<OwnerType
|
|
28
28
|
class="mb-0 text-sm"
|
|
29
29
|
:type
|
|
30
30
|
/>
|
|
31
|
-
<RiSubtractLine
|
|
31
|
+
<RiSubtractLine
|
|
32
|
+
aria-hidden="true"
|
|
33
|
+
class="size-4 fill-gray-medium"
|
|
34
|
+
/>
|
|
32
35
|
</template>
|
|
33
36
|
<div>
|
|
34
37
|
<div
|
|
35
|
-
v-if="organization
|
|
38
|
+
v-if="'metrics' in organization"
|
|
36
39
|
class="text-gray-medium flex items-center text-sm gap-0.5"
|
|
40
|
+
:aria-label="t('{datasets} jeux de données, {dataservices} API et {reuses} réutilisations', {
|
|
41
|
+
datasets: organization.metrics.datasets,
|
|
42
|
+
dataservices: organization.metrics.dataservices,
|
|
43
|
+
reuses: organization.metrics.reuses,
|
|
44
|
+
})"
|
|
37
45
|
>
|
|
38
|
-
<RiDatabase2Line
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
<RiDatabase2Line
|
|
47
|
+
aria-hidden="true"
|
|
48
|
+
class="size-4 -mt-1"
|
|
49
|
+
/> {{ organization.metrics.datasets }}
|
|
50
|
+
<RiTerminalLine
|
|
51
|
+
aria-hidden="true"
|
|
52
|
+
class="size-4 -mt-1 ml-1"
|
|
53
|
+
/> {{ organization.metrics.dataservices }}
|
|
54
|
+
<RiLineChartLine
|
|
55
|
+
aria-hidden="true"
|
|
56
|
+
class="size-4 -mt-1 ml-1"
|
|
57
|
+
/> {{ organization.metrics.reuses }}
|
|
41
58
|
</div>
|
|
42
59
|
</div>
|
|
43
60
|
</div>
|
|
44
|
-
<
|
|
61
|
+
<div class="mt-1 mb-0">
|
|
45
62
|
<TextClamp
|
|
46
|
-
v-if="description"
|
|
47
|
-
:text="description"
|
|
63
|
+
v-if="'description' in organization"
|
|
64
|
+
:text="removeMarkdownSync(organization.description)"
|
|
48
65
|
:max-lines="3"
|
|
49
66
|
/>
|
|
50
|
-
</
|
|
67
|
+
</div>
|
|
51
68
|
</div>
|
|
52
69
|
</div>
|
|
53
70
|
</template>
|
|
54
71
|
|
|
55
72
|
<script setup lang="ts">
|
|
56
73
|
import { RiLineChartLine, RiDatabase2Line, RiTerminalLine, RiSubtractLine } from '@remixicon/vue'
|
|
57
|
-
import { computed
|
|
58
|
-
import {
|
|
74
|
+
import { computed } from 'vue'
|
|
75
|
+
import { removeMarkdownSync } from '../functions/markdown'
|
|
59
76
|
import { getOrganizationType } from '../functions/organizations'
|
|
60
|
-
import type { Organization } from '../types/organizations'
|
|
77
|
+
import type { Organization, OrganizationReference } from '../types/organizations'
|
|
61
78
|
import OwnerType from './OwnerType.vue'
|
|
62
79
|
import OrganizationNameWithCertificate from './OrganizationNameWithCertificate.vue'
|
|
63
80
|
import AppLink from './AppLink.vue'
|
|
81
|
+
import { useTranslation } from '../composables/useTranslation'
|
|
64
82
|
|
|
65
83
|
const props = defineProps<{
|
|
66
|
-
organization: Organization
|
|
84
|
+
organization: Organization | OrganizationReference
|
|
67
85
|
}>()
|
|
68
86
|
|
|
69
|
-
const
|
|
87
|
+
const { t } = useTranslation()
|
|
70
88
|
|
|
71
|
-
const
|
|
72
|
-
watchEffect(async () => {
|
|
73
|
-
description.value = await removeMarkdown(props.organization.description)
|
|
74
|
-
})
|
|
89
|
+
const type = computed(() => getOrganizationType(props.organization))
|
|
75
90
|
</script>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<script setup lang="ts">
|
|
17
17
|
import { computed } from 'vue'
|
|
18
18
|
import type { OrganizationOrSuggest } from '../types/organizations'
|
|
19
|
-
import { throwOnNever } from '../
|
|
19
|
+
import { throwOnNever } from '../functions/never'
|
|
20
20
|
import Placeholder from './Placeholder.vue'
|
|
21
21
|
|
|
22
22
|
const props = defineProps<{
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
v-if="showType"
|
|
5
5
|
:type="getOrganizationType(organization)"
|
|
6
6
|
/>
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
<component
|
|
8
|
+
:is="as"
|
|
9
|
+
class="mb-0 truncate flex-initial font-normal"
|
|
10
|
+
:class="[colorClass, { 'text-xs': size === 'xs', 'text-sm': size === 'sm', 'text-base': size === 'base' }]"
|
|
10
11
|
>
|
|
11
12
|
{{ organization.name }}
|
|
12
13
|
<small
|
|
@@ -15,11 +16,12 @@
|
|
|
15
16
|
>
|
|
16
17
|
{{ organization.acronym }}
|
|
17
18
|
</small>
|
|
18
|
-
</
|
|
19
|
+
</component>
|
|
19
20
|
<Tooltip v-if="isOrganizationCertified(organization)">
|
|
20
21
|
<RiCheckboxCircleLine
|
|
21
22
|
class="flex-none"
|
|
22
23
|
:class="{
|
|
24
|
+
'size-3': size === 'xs',
|
|
23
25
|
'size-4': size === 'sm',
|
|
24
26
|
'size-5': size === 'base',
|
|
25
27
|
}"
|
|
@@ -38,7 +40,7 @@
|
|
|
38
40
|
<script setup lang="ts">
|
|
39
41
|
import { RiCheckboxCircleLine } from '@remixicon/vue'
|
|
40
42
|
import { getOrganizationType, isOrganizationCertified } from '../functions/organizations'
|
|
41
|
-
import type { OrganizationReference } from '../types/organizations'
|
|
43
|
+
import type { Organization, OrganizationReference } from '../types/organizations'
|
|
42
44
|
import { useComponentsConfig } from '../config'
|
|
43
45
|
import { useTranslation } from '../composables/useTranslation'
|
|
44
46
|
import OwnerTypeIcon from './OwnerTypeIcon.vue'
|
|
@@ -48,13 +50,17 @@ const config = useComponentsConfig()
|
|
|
48
50
|
|
|
49
51
|
const { t } = useTranslation()
|
|
50
52
|
withDefaults(defineProps<{
|
|
51
|
-
organization: OrganizationReference
|
|
53
|
+
organization: Organization | OrganizationReference
|
|
52
54
|
showAcronym?: boolean
|
|
53
55
|
showType?: boolean
|
|
54
|
-
size?: 'base' | 'sm'
|
|
56
|
+
size?: 'base' | 'sm' | 'xs'
|
|
57
|
+
colorClass?: string
|
|
58
|
+
as?: string
|
|
55
59
|
}>(), {
|
|
56
60
|
showAcronym: false,
|
|
57
61
|
showType: true,
|
|
58
62
|
size: 'base',
|
|
63
|
+
colorClass: 'text-new-primary',
|
|
64
|
+
as: 'div',
|
|
59
65
|
})
|
|
60
66
|
</script>
|
|
@@ -3,23 +3,26 @@
|
|
|
3
3
|
<component
|
|
4
4
|
:is="icon"
|
|
5
5
|
class="size-1/2 text-gray-plain"
|
|
6
|
+
aria-hidden="true"
|
|
6
7
|
/>
|
|
7
8
|
</div>
|
|
8
9
|
</template>
|
|
9
10
|
|
|
10
11
|
<script setup lang="ts">
|
|
11
12
|
import { computed } from 'vue'
|
|
12
|
-
import { RiBuilding2Line, RiDatabase2Line, RiLineChartLine } from '@remixicon/vue'
|
|
13
|
+
import { RiBookShelfLine, RiBuilding2Line, RiDatabase2Line, RiLineChartLine, RiTerminalLine } from '@remixicon/vue'
|
|
13
14
|
|
|
14
15
|
const props = defineProps<{
|
|
15
|
-
type: 'Dataset' | 'Reuse' | 'Organization'
|
|
16
|
+
type: 'Dataset' | 'Dataservice' | 'Reuse' | 'Organization' | 'Topic'
|
|
16
17
|
}>()
|
|
17
18
|
|
|
18
19
|
const icon = computed(() => {
|
|
19
20
|
return {
|
|
20
21
|
Dataset: RiDatabase2Line,
|
|
22
|
+
Dataservice: RiTerminalLine,
|
|
21
23
|
Reuse: RiLineChartLine,
|
|
22
24
|
Organization: RiBuilding2Line,
|
|
25
|
+
Topic: RiBookShelfLine,
|
|
23
26
|
}[props.type]
|
|
24
27
|
})
|
|
25
28
|
</script>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ObjectCard media-size="lg">
|
|
3
|
+
<template #media>
|
|
4
|
+
<img
|
|
5
|
+
v-if="post.image"
|
|
6
|
+
:src="post.image"
|
|
7
|
+
class="w-full h-full object-cover"
|
|
8
|
+
:alt="post.name"
|
|
9
|
+
>
|
|
10
|
+
<Placeholder
|
|
11
|
+
v-else
|
|
12
|
+
type="Dataset"
|
|
13
|
+
class="w-full h-full"
|
|
14
|
+
/>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<ObjectCardHeader
|
|
18
|
+
:icon="RiArticleLine"
|
|
19
|
+
:url="postUrl || post.page || '#'"
|
|
20
|
+
>
|
|
21
|
+
{{ post.name }}
|
|
22
|
+
</ObjectCardHeader>
|
|
23
|
+
|
|
24
|
+
<ObjectCardShortDescription :text="post.headline || post.content" />
|
|
25
|
+
|
|
26
|
+
<div
|
|
27
|
+
v-if="post.published || post.created_at"
|
|
28
|
+
class="text-sm text-gray-medium mt-1"
|
|
29
|
+
>
|
|
30
|
+
{{ t('Publié {date}', { date: formatDate(post.published || post.created_at) }) }}
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<slot />
|
|
34
|
+
</ObjectCard>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import { RiArticleLine } from '@remixicon/vue'
|
|
39
|
+
import type { RouteLocationRaw } from 'vue-router'
|
|
40
|
+
import { useFormatDate } from '../functions/dates'
|
|
41
|
+
import { useTranslation } from '../composables/useTranslation'
|
|
42
|
+
import type { Post } from '../types/posts'
|
|
43
|
+
import Placeholder from './Placeholder.vue'
|
|
44
|
+
import ObjectCard from './ObjectCard.vue'
|
|
45
|
+
import ObjectCardHeader from './ObjectCardHeader.vue'
|
|
46
|
+
import ObjectCardShortDescription from './ObjectCardShortDescription.vue'
|
|
47
|
+
|
|
48
|
+
defineProps<{
|
|
49
|
+
post: Post
|
|
50
|
+
postUrl?: RouteLocationRaw
|
|
51
|
+
}>()
|
|
52
|
+
|
|
53
|
+
const { t } = useTranslation()
|
|
54
|
+
const { formatRelativeIfRecentDate } = useFormatDate()
|
|
55
|
+
|
|
56
|
+
const formatDate = (dateString: string) => {
|
|
57
|
+
return formatRelativeIfRecentDate(dateString, {
|
|
58
|
+
dateStyle: 'long',
|
|
59
|
+
timeStyle: 'short',
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<fieldset class="flex flex-col gap-2 min-w-0">
|
|
3
|
+
<legend
|
|
4
|
+
v-if="legend"
|
|
5
|
+
class="fr-label mb-2"
|
|
6
|
+
>
|
|
7
|
+
{{ legend }}
|
|
8
|
+
</legend>
|
|
9
|
+
<slot />
|
|
10
|
+
</fieldset>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { provide, toRef } from 'vue'
|
|
15
|
+
import { radioGroupInjectionKey } from './radioGroupContext'
|
|
16
|
+
|
|
17
|
+
const props = defineProps<{
|
|
18
|
+
modelValue: string
|
|
19
|
+
name: string
|
|
20
|
+
legend?: string
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const emit = defineEmits<{
|
|
24
|
+
(event: 'update:modelValue', value: string): void
|
|
25
|
+
}>()
|
|
26
|
+
|
|
27
|
+
provide(radioGroupInjectionKey, {
|
|
28
|
+
name: toRef(() => props.name),
|
|
29
|
+
modelValue: toRef(() => props.modelValue),
|
|
30
|
+
select: (value: string) => emit('update:modelValue', value),
|
|
31
|
+
})
|
|
32
|
+
</script>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<label
|
|
3
|
+
class="flex items-center gap-2 p-1 rounded cursor-pointer transition has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-blue-500"
|
|
4
|
+
:class="selectedClass"
|
|
5
|
+
>
|
|
6
|
+
<input
|
|
7
|
+
type="radio"
|
|
8
|
+
:name="group?.name.value"
|
|
9
|
+
:value="value"
|
|
10
|
+
:checked="isSelected"
|
|
11
|
+
class="sr-only"
|
|
12
|
+
@change="group?.select(value)"
|
|
13
|
+
>
|
|
14
|
+
<component
|
|
15
|
+
:is="icon"
|
|
16
|
+
v-if="icon"
|
|
17
|
+
class="w-4 h-4"
|
|
18
|
+
/>
|
|
19
|
+
<span class="text-sm flex-1 min-w-0">
|
|
20
|
+
<slot />
|
|
21
|
+
</span>
|
|
22
|
+
<span
|
|
23
|
+
v-if="loading || count !== undefined"
|
|
24
|
+
class="text-xs font-bold px-1 py-0.5 rounded"
|
|
25
|
+
:class="isSelected && highlighted ? 'bg-white/20 text-white' : 'bg-gray-200 text-gray-600'"
|
|
26
|
+
>
|
|
27
|
+
<BouncingDots v-if="loading" />
|
|
28
|
+
<template v-else>{{ formattedCount }}</template>
|
|
29
|
+
</span>
|
|
30
|
+
</label>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script setup lang="ts">
|
|
34
|
+
import { computed, inject, type Component } from 'vue'
|
|
35
|
+
import { radioGroupInjectionKey } from './radioGroupContext'
|
|
36
|
+
import { useTranslation } from '../composables/useTranslation'
|
|
37
|
+
import BouncingDots from './BouncingDots.vue'
|
|
38
|
+
|
|
39
|
+
type Props = {
|
|
40
|
+
value: string
|
|
41
|
+
count?: number
|
|
42
|
+
loading?: boolean
|
|
43
|
+
icon?: Component
|
|
44
|
+
highlighted?: boolean
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const props = defineProps<Props>()
|
|
48
|
+
|
|
49
|
+
const group = inject(radioGroupInjectionKey)
|
|
50
|
+
const { locale } = useTranslation()
|
|
51
|
+
|
|
52
|
+
const isSelected = computed(() => group?.modelValue.value === props.value)
|
|
53
|
+
|
|
54
|
+
const selectedClass = computed(() => {
|
|
55
|
+
if (!isSelected.value) return 'hover:bg-gray-100'
|
|
56
|
+
if (props.highlighted) return 'bg-blue-800 text-white'
|
|
57
|
+
return 'bg-gray-200'
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const formattedCount = computed(() => {
|
|
61
|
+
if (props.count === undefined) return ''
|
|
62
|
+
return new Intl.NumberFormat(locale, { notation: 'compact' }).format(props.count)
|
|
63
|
+
})
|
|
64
|
+
</script>
|
|
@@ -18,22 +18,6 @@
|
|
|
18
18
|
import { onMounted, ref, useTemplateRef } from 'vue'
|
|
19
19
|
import { RiErrorWarningLine } from '@remixicon/vue'
|
|
20
20
|
|
|
21
|
-
import View from 'ol/View'
|
|
22
|
-
import Map from 'ol/Map'
|
|
23
|
-
import ScaleLine from 'ol/control/ScaleLine'
|
|
24
|
-
import TileLayer from 'ol/layer/Tile'
|
|
25
|
-
import OSM from 'ol/source/OSM'
|
|
26
|
-
|
|
27
|
-
import {
|
|
28
|
-
CRS,
|
|
29
|
-
GeoportalAttribution,
|
|
30
|
-
GeoportalFullScreen,
|
|
31
|
-
GeoportalZoom,
|
|
32
|
-
LayerImport,
|
|
33
|
-
LayerSwitcher,
|
|
34
|
-
// @ts-expect-error no types provided
|
|
35
|
-
} from 'geopf-extensions-openlayers'
|
|
36
|
-
|
|
37
21
|
import SimpleBanner from '../SimpleBanner.vue'
|
|
38
22
|
import type { Resource } from '../../types/resources'
|
|
39
23
|
import { useTranslation } from '../../composables/useTranslation'
|
|
@@ -47,6 +31,26 @@ const mapRef = useTemplateRef('mapRef')
|
|
|
47
31
|
const hasError = ref(false)
|
|
48
32
|
|
|
49
33
|
async function displayMap() {
|
|
34
|
+
// Dynamic imports for client-only libraries
|
|
35
|
+
const [
|
|
36
|
+
{ default: View },
|
|
37
|
+
{ default: Map },
|
|
38
|
+
{ default: ScaleLine },
|
|
39
|
+
{ default: TileLayer },
|
|
40
|
+
{ default: OSM },
|
|
41
|
+
geopf,
|
|
42
|
+
] = await Promise.all([
|
|
43
|
+
import('ol/View'),
|
|
44
|
+
import('ol/Map'),
|
|
45
|
+
import('ol/control/ScaleLine'),
|
|
46
|
+
import('ol/layer/Tile'),
|
|
47
|
+
import('ol/source/OSM'),
|
|
48
|
+
// @ts-expect-error no types provided
|
|
49
|
+
import('geopf-extensions-openlayers'),
|
|
50
|
+
])
|
|
51
|
+
|
|
52
|
+
const { CRS, GeoportalAttribution, GeoportalFullScreen, GeoportalZoom, LayerImport, LayerSwitcher } = geopf
|
|
53
|
+
|
|
50
54
|
await import('ol/ol.css')
|
|
51
55
|
await import('@gouvfr/dsfr/dist/dsfr.css')
|
|
52
56
|
await import('@gouvfr/dsfr/dist/utility/icons/icons.css')
|
|
@@ -3,7 +3,6 @@ import { computed } from 'vue'
|
|
|
3
3
|
import type { Resource } from '../../types/resources'
|
|
4
4
|
import CopyButton from '../CopyButton.vue'
|
|
5
5
|
import DescriptionDetails from '../DescriptionDetails.vue'
|
|
6
|
-
import DescriptionList from '../DescriptionList.vue'
|
|
7
6
|
import DescriptionTerm from '../DescriptionTerm.vue'
|
|
8
7
|
import { useFormatDate } from '../../functions/dates'
|
|
9
8
|
import { filesize } from '../../functions/helpers'
|
|
@@ -27,7 +26,7 @@ const { formatDate } = useFormatDate()
|
|
|
27
26
|
<template>
|
|
28
27
|
<div>
|
|
29
28
|
<div class="flex flex-wrap gap-12 flex-col md:flex-row overflow-hidden">
|
|
30
|
-
<
|
|
29
|
+
<dl class="flex-1 max-w-full p-0 m-0">
|
|
31
30
|
<DescriptionTerm>
|
|
32
31
|
{{ t('URL') }}
|
|
33
32
|
<CopyButton
|
|
@@ -90,8 +89,10 @@ const { formatDate } = useFormatDate()
|
|
|
90
89
|
</code>
|
|
91
90
|
</DescriptionDetails>
|
|
92
91
|
</template>
|
|
93
|
-
</
|
|
94
|
-
<
|
|
92
|
+
</dl>
|
|
93
|
+
<dl
|
|
94
|
+
class="p-0 m-0 shrink-0"
|
|
95
|
+
>
|
|
95
96
|
<DescriptionTerm>{{ t('Créée le') }}</DescriptionTerm>
|
|
96
97
|
<DescriptionDetails>
|
|
97
98
|
{{ formatDate(resource.created_at) }}
|
|
@@ -100,8 +101,10 @@ const { formatDate } = useFormatDate()
|
|
|
100
101
|
<DescriptionDetails>
|
|
101
102
|
{{ formatDate(resource.last_modified) }}
|
|
102
103
|
</DescriptionDetails>
|
|
103
|
-
</
|
|
104
|
-
<
|
|
104
|
+
</dl>
|
|
105
|
+
<dl
|
|
106
|
+
class="p-0 m-0 shrink-0"
|
|
107
|
+
>
|
|
105
108
|
<template v-if="resourceFilesize">
|
|
106
109
|
<DescriptionTerm>{{ t('Taille') }}</DescriptionTerm>
|
|
107
110
|
<DescriptionDetails>
|
|
@@ -120,12 +123,12 @@ const { formatDate } = useFormatDate()
|
|
|
120
123
|
<code class="code truncate">{{ resource.mime }}</code>
|
|
121
124
|
</DescriptionDetails>
|
|
122
125
|
</template>
|
|
123
|
-
</
|
|
126
|
+
</dl>
|
|
124
127
|
</div>
|
|
125
128
|
<div>
|
|
126
129
|
<ExtraAccordion
|
|
127
130
|
v-if="hasExtras"
|
|
128
|
-
class="pt-6 mt-6 border-
|
|
131
|
+
class="pt-6 mt-6 border-t border-gray-default"
|
|
129
132
|
:button-text="t('Voir les extras')"
|
|
130
133
|
:title-text="t('Extras de la ressource')"
|
|
131
134
|
title-level="h5"
|
|
@@ -134,19 +137,3 @@ const { formatDate } = useFormatDate()
|
|
|
134
137
|
</div>
|
|
135
138
|
</div>
|
|
136
139
|
</template>
|
|
137
|
-
|
|
138
|
-
<style scoped>
|
|
139
|
-
.gap-3rem {
|
|
140
|
-
gap: 3rem;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
.gap-3rem dl {
|
|
144
|
-
padding-inline-start: 0;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
@container (max-width: 600px) {
|
|
148
|
-
.flex-col-on-small {
|
|
149
|
-
flex-direction: column
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
</style>
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<p class="fr-text--bold fr-m-0">
|
|
26
26
|
{{ t("Explorer les données en détail") }}
|
|
27
27
|
</p>
|
|
28
|
-
<p class="fr-text--sm fr-m-0
|
|
28
|
+
<p class="fr-text--sm fr-m-0 italic">
|
|
29
29
|
{{ t("Utiliser un visualisateur PMTiles pour obtenir un aperçu des données.") }}
|
|
30
30
|
</p>
|
|
31
31
|
</div>
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<p class="fr-text--bold fr-m-0">
|
|
24
24
|
{{ t("Explorer les données en détail") }}
|
|
25
25
|
</p>
|
|
26
|
-
<p class="fr-text--sm fr-m-0
|
|
26
|
+
<p class="fr-text--sm fr-m-0 italic">
|
|
27
27
|
{{ t("Utiliser notre outil pour obtenir un aperçu des données, en savoir plus sur les différentes colonnes ou réaliser des filtres et des tris.") }}
|
|
28
28
|
</p>
|
|
29
29
|
</div>
|