Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions app/router.options.ts

This file was deleted.

5 changes: 3 additions & 2 deletions components/Datasets/Structured/Step2Sheet.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { RiErrorWarningLine } from '@remixicon/vue'
import { computedAsync } from '@vueuse/core'
import { ofetch } from 'ofetch'
import type { ValidationReport } from '~/types/schema'
import { goToStep } from '~/utils/scroll'

const props = defineProps<{
schema: RegisteredSchema | null
Expand Down Expand Up @@ -126,11 +127,11 @@ const schemaDetails = computedAsync<SchemaDetails | null>(
)

const goBack = () => {
navigateTo({ path: route.path, query: { step: 2 } })
goToStep(route, { step: 2 })
}

const changeSchema = () => {
navigateTo({ path: route.path, query: { step: 1 } })
goToStep(route, { step: 1 })
}

const submit = async () => {
Expand Down
3 changes: 1 addition & 2 deletions components/GristTableViewer/GristTableViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,7 @@ const total = computed(() => {

const { formatNumber } = useFormatTabular()

// One URL-synced model per filter slug. The page hosting the filters must set
// definePageMeta({ keepScroll: true }) so the query updates don't scroll back to top.
// One URL-synced model per filter slug.
// A filter may declare `aliases` (legacy query keys): the model reads the canonical
// key first, then falls back to the first alias that has a value, so old links keep
// filtering. Writing the filter sets the canonical key and clears the aliases.
Expand Down
5 changes: 4 additions & 1 deletion datagouv-components/src/components/LoadingBlock.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<template>
<div class="relative">
<div :class="{ 'opacity-50 min-h-64': loading }">
<!-- Keep the previous content while reloading, dimmed under the loader:
dropping it collapses the page height, which makes the browser clamp
the scroll and throws the reader back to the top. -->
<slot
v-if="!loading && !hasError && data"
v-if="!hasError && data"
:data="data"
/>
<slot
Expand Down
6 changes: 2 additions & 4 deletions datagouv-components/src/components/Pagination.vue
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ import { computed, useTemplateRef } from 'vue'
import { useRoute } from 'vue-router'
import { useTranslation } from '../composables/useTranslation'
import { clampPage, getVisiblePages } from '../functions/paginate'
import { scrollToBlockTop } from '../functions/scroll'

type Props = {
/**
Expand Down Expand Up @@ -141,10 +142,7 @@ const nav = useTemplateRef('navRef')
function change(index: number) {
emit('change', index)

if (!nav.value || !nav.value.parentElement) return

nav.value.parentElement.style.scrollMarginTop = '100px'
nav.value.parentElement.scrollIntoView({ behavior: 'smooth', block: 'start' })
scrollToBlockTop(nav.value?.parentElement)
}

function onClick(index: number) {
Expand Down
15 changes: 9 additions & 6 deletions datagouv-components/src/components/Search/GlobalSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
>
<div
v-if="!hideSearchInput"
ref="search"
class="flex flex-wrap items-center justify-between"
data-cy="search"
>
Expand Down Expand Up @@ -357,6 +356,7 @@
<script setup lang="ts">
import { computed, provide, shallowReactive, useSlots, watch, useTemplateRef, type Component, type Ref } from 'vue'
import { useRouteQuery } from '@vueuse/router'
import { useRoute } from 'vue-router'
import { RiBookShelfLine, RiBuilding2Line, RiCloseCircleLine, RiDatabase2Line, RiLightbulbLine, RiLineChartLine, RiRssLine, RiTerminalLine } from '@remixicon/vue'
import magnifyingGlassSrc from '../../../assets/illustrations/magnifying_glass.svg?url'
import { useTranslation } from '../../composables/useTranslation'
Expand All @@ -365,6 +365,7 @@ import { configKey, forEachActiveCustomFilter, isCustomFilterActive, searchFilte
import { useStableQueryParams } from '../../composables/useStableQueryParams'
import { useComponentsConfig } from '../../config'
import { useFetch } from '../../functions/api'
import { scrollToBlockTop } from '../../functions/scroll'
import type { AsyncDataRequestStatus } from '../../functions/api.types'
import type { Dataset } from '../../types/datasets'
import type { Dataservice } from '../../types/dataservices'
Expand Down Expand Up @@ -423,6 +424,7 @@ if (!currentType.value) currentType.value = configKey(props.config[0] ?? { class

const { t } = useTranslation()
const componentsConfig = useComponentsConfig()
const route = useRoute()

// Custom filter registry for useSearchFilter composable
const customFilterRegistry = shallowReactive(new Map<string, CustomFilterEntry>())
Expand Down Expand Up @@ -797,14 +799,15 @@ function getFacets(key: string): FacetItem[] | undefined {
}

// Scroll handling
const searchRef = useTemplateRef('search')
const resultsRef = useTemplateRef('results')

function scrollToTop() {
searchRef.value?.scrollIntoView({ behavior: 'smooth' })
}
// Every criteria lives in the URL, custom filters included, so watching the
// query covers them all: whenever the result list is replaced, bring its top
// back into view rather than leaving the reader in the middle of a list they
// have not seen yet.
watch(() => route.query, () => scrollToBlockTop(resultsRef.value))

function changePage(newPage: number) {
page.value = newPage
scrollToTop()
}
</script>
20 changes: 20 additions & 0 deletions datagouv-components/src/functions/scroll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Leaves room for the sticky elements sitting at the top of the viewport.
const SCROLL_MARGIN = 100

/**
* Bring the top of a block back into view after its content changed (paging,
* searching, filtering), so the reader restarts at the first result.
*
* Does nothing when the top of the block is already on screen: aligning the
* viewport on a block the reader is already looking at moves the page under
* them for no reason.
*/
export function scrollToBlockTop(block: HTMLElement | null | undefined) {
if (!block) return

const { top } = block.getBoundingClientRect()
if (top >= 0 && top <= window.innerHeight) return

block.style.scrollMarginTop = `${SCROLL_MARGIN}px`
block.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
3 changes: 2 additions & 1 deletion pages/admin/community-resources/new.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import CompleteResourcePublicationStep from '~/components/Datasets/CompleteResou
import DescribeResource from '~/components/Datasets/DescribeResource.vue'
import Stepper from '~/components/Stepper/Stepper.vue'
import type { CommunityResourceForm } from '~/types/types'
import { goToStep } from '~/utils/scroll'

const { t } = useTranslation()
const route = useRoute()
Expand Down Expand Up @@ -90,7 +91,7 @@ const isCurrentStepValid = computed(() => {
})

function moveToStep(step: number) {
return navigateTo({ path: route.path, query: { ...route.query, step } })
return goToStep(route, { ...route.query, step })
}

async function save() {
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/dataservices/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ import AdminBreadcrumb from '~/components/Breadcrumbs/AdminBreadcrumb.vue'
import BreadcrumbItem from '~/components/Breadcrumbs/BreadcrumbItem.vue'
import TabLinks from '~/components/TabLinks.vue'
import type { PaginatedArray } from '~/types/types'
import { keepScrollWithinPage } from '~/utils/scroll'

definePageMeta({
keepScroll: true,
scrollToTop: keepScrollWithinPage,
})

const { t } = useTranslation()
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/dataservices/new.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import type {
DataserviceForm,
DatasetSuggest,
} from '~/types/types'
import { goToStep } from '~/utils/scroll'

const { t } = useTranslation()
const route = useRoute()
Expand Down Expand Up @@ -123,7 +124,7 @@ const isCurrentStepValid = computed(() => {
})

function moveToStep(step: number) {
return navigateTo({ path: route.path, query: { ...route.query, step } })
return goToStep(route, { ...route.query, step })
}

const dataserviceNext = () => {
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/datasets/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,10 @@ import AdminBreadcrumb from '~/components/Breadcrumbs/AdminBreadcrumb.vue'
import BreadcrumbItem from '~/components/Breadcrumbs/BreadcrumbItem.vue'
import TabLinks from '~/components/TabLinks.vue'
import type { PaginatedArray } from '~/types/types'
import { keepScrollWithinPage } from '~/utils/scroll'

definePageMeta({
keepScroll: true,
scrollToTop: keepScrollWithinPage,
})

const { t } = useTranslation()
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/datasets/new.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import Stepper from '~/components/Stepper/Stepper.vue'
import type { DatasetForm, EnrichedLicense, ResourceForm, SpatialGranularity, SpatialZone, Tag } from '~/types/types'
import Breadcrumb from '~/components/Breadcrumb/Breadcrumb.vue'
import BreadcrumbItem from '~/components/Breadcrumbs/BreadcrumbItem.vue'
import { goToStep } from '~/utils/scroll'

const { t } = useTranslation()
const config = useRuntimeConfig()
Expand Down Expand Up @@ -114,7 +115,7 @@ const isCurrentStepValid = computed(() => {
})

const moveToStep = (step: number) => {
return navigateTo({ path: route.path, query: { ...route.query, step } })
return goToStep(route, { ...route.query, step })
}

const datasetNext = () => {
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/datasets/structured.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import type { DatasetForm, EnrichedLicense, ResourceForm, SpatialGranularity, Ta
import Breadcrumb from '~/components/Breadcrumb/Breadcrumb.vue'
import BreadcrumbItem from '~/components/Breadcrumbs/BreadcrumbItem.vue'
import type { AssociateSchemaForm } from '~/types/schema'
import { goToStep } from '~/utils/scroll'

const { t } = useTranslation()
const config = useRuntimeConfig()
Expand Down Expand Up @@ -159,7 +160,7 @@ function moveToStep(step: '2-sheet' | 1 | 2 | 3 | 4) {
if (step !== '2-sheet') {
file.value = null
}
return navigateTo({ path: route.path, query: { ...route.query, step } })
return goToStep(route, { ...route.query, step })
}

function dataNext() {
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/harvesters/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,10 @@ import BreadcrumbItem from '~/components/Breadcrumbs/BreadcrumbItem.vue'
import HarvesterBadge from '~/components/Harvesters/HarvesterBadge.vue'
import TabLinks from '~/components/TabLinks.vue'
import type { HarvesterJob, HarvesterSource } from '~/types/harvesters'
import { keepScrollWithinPage } from '~/utils/scroll'

definePageMeta({
keepScroll: true,
scrollToTop: keepScrollWithinPage,
})

const config = useRuntimeConfig()
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/harvesters/new.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import DescribeHarvester from '~/components/Harvesters/DescribeHarvester.vue'
import PreviewStep from '~/components/Harvesters/PreviewStep.vue'
import Stepper from '~/components/Stepper/Stepper.vue'
import type { HarvesterForm, HarvesterSource } from '~/types/harvesters'
import { goToStep } from '~/utils/scroll'

const { t } = useTranslation()
const route = useRoute()
Expand Down Expand Up @@ -109,7 +110,7 @@ const isCurrentStepValid = computed(() => {
})

function moveToStep(step: number) {
return navigateTo({ path: route.path, query: { ...route.query, step } })
return goToStep(route, { ...route.query, step })
}

function harvesterNext() {
Expand Down
4 changes: 3 additions & 1 deletion pages/admin/me/metrics.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
</template>

<script setup lang="ts">
import { keepScrollWithinPage } from '~/utils/scroll'

definePageMeta({
keepScroll: true,
scrollToTop: keepScrollWithinPage,
})

const me = useMe()
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/me/profile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@
import AdminUserProfileHeader from '~/components/User/AdminUserProfileHeader.vue'
import AdminOrgInvitation from '~/components/AdminOrgInvitation/AdminOrgInvitation.vue'
import type { OrgInvitation } from '~/types/types'
import { keepScrollWithinPage } from '~/utils/scroll'

definePageMeta({
keepScroll: true,
scrollToTop: keepScrollWithinPage,
})

const { t } = useTranslation()
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/organizations/[oid]/metrics.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

<script setup lang="ts">
import type { Organization } from '@datagouv/components-next'
import { keepScrollWithinPage } from '~/utils/scroll'

definePageMeta({
keepScroll: true,
scrollToTop: keepScrollWithinPage,
})

defineProps<{
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/organizations/[oid]/profile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,14 @@ import { BrandedButton, isOrganizationCertified, OrganizationLogo, PaddedContain
import { RiEyeLine } from '@remixicon/vue'
import AdminBreadcrumb from '~/components/Breadcrumbs/AdminBreadcrumb.vue'
import BreadcrumbItem from '~/components/Breadcrumbs/BreadcrumbItem.vue'
import { keepScrollWithinPage } from '~/utils/scroll'

defineEmits<{
refresh: []
}>()

definePageMeta({
keepScroll: true,
scrollToTop: keepScrollWithinPage,
})

const props = defineProps<{
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/organizations/[oid]/reuses/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ import { RiEyeLine } from '@remixicon/vue'
import AdminBreadcrumb from '~/components/Breadcrumbs/AdminBreadcrumb.vue'
import BreadcrumbItem from '~/components/Breadcrumbs/BreadcrumbItem.vue'
import TabLinks from '~/components/TabLinks.vue'
import { keepScrollWithinPage } from '~/utils/scroll'

definePageMeta({
keepScroll: true,
scrollToTop: keepScrollWithinPage,
})

const { t } = useTranslation()
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/organizations/new.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import Stepper from '~/components/Stepper/Stepper.vue'
import { loadMe } from '~/utils/auth'
import type { NewOrganization } from '~/types/types'
import BreadcrumbItem from '~/components/Breadcrumbs/BreadcrumbItem.vue'
import { goToStep } from '~/utils/scroll'

const { t } = useTranslation()
const config = useRuntimeConfig()
Expand Down Expand Up @@ -89,7 +90,7 @@ const isCurrentStepValid = computed(() => {
})

function moveToStep(step: number) {
navigateTo({ path: route.path, query: { ...route.query, step } })
goToStep(route, { ...route.query, step })
}

async function createOrganizationAndMoveToNextStep(logo_file: File | null) {
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/posts/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ import AdminBreadcrumb from '~/components/Breadcrumbs/AdminBreadcrumb.vue'
import BreadcrumbItem from '~/components/Breadcrumbs/BreadcrumbItem.vue'
import TabLinks from '~/components/TabLinks.vue'
import type { Post } from '~/types/posts'
import { keepScrollWithinPage } from '~/utils/scroll'

definePageMeta({
keepScroll: true,
scrollToTop: keepScrollWithinPage,
})

const { t } = useTranslation()
Expand Down
3 changes: 2 additions & 1 deletion pages/admin/posts/new.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import DescribePost from '~/components/Posts/DescribePost.vue'
import PostContentForm from '~/components/Posts/PostContentForm.vue'
import Stepper from '~/components/Stepper/Stepper.vue'
import type { Post, PostForm } from '~/types/posts'
import { goToStep } from '~/utils/scroll'

const { t } = useTranslation()
const route = useRoute()
Expand Down Expand Up @@ -94,7 +95,7 @@ const isCurrentStepValid = computed(() => {
})

function moveToStep(step: number) {
return navigateTo({ path: route.path, query: { ...route.query, step } })
return goToStep(route, { ...route.query, step })
}

async function postNext(form: PostForm) {
Expand Down
Loading
Loading