@datagouv/components-next 1.0.2-dev.194 → 1.0.2-dev.196

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.
@@ -1,4 +1,4 @@
1
- import { g as Ke } from "./main-dOC1WJIR.js";
1
+ import { g as Ke } from "./main-B03fYwPN.js";
2
2
  import We from "vue";
3
3
  function Fe($, K) {
4
4
  for (var V = 0; V < K.length; V++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datagouv/components-next",
3
- "version": "1.0.2-dev.194",
3
+ "version": "1.0.2-dev.196",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -1,8 +1,11 @@
1
1
  <template>
2
2
  <div class="relative">
3
3
  <div :class="{ 'opacity-50 min-h-64': loading }">
4
+ <!-- Keep the previous content while reloading, dimmed under the loader:
5
+ dropping it collapses the page height, which makes the browser clamp
6
+ the scroll and throws the reader back to the top. -->
4
7
  <slot
5
- v-if="!loading && !hasError && data"
8
+ v-if="!hasError && data"
6
9
  :data="data"
7
10
  />
8
11
  <slot
@@ -104,6 +104,7 @@ import { computed, useTemplateRef } from 'vue'
104
104
  import { useRoute } from 'vue-router'
105
105
  import { useTranslation } from '../composables/useTranslation'
106
106
  import { clampPage, getVisiblePages } from '../functions/paginate'
107
+ import { scrollToBlockTop } from '../functions/scroll'
107
108
 
108
109
  type Props = {
109
110
  /**
@@ -141,10 +142,7 @@ const nav = useTemplateRef('navRef')
141
142
  function change(index: number) {
142
143
  emit('change', index)
143
144
 
144
- if (!nav.value || !nav.value.parentElement) return
145
-
146
- nav.value.parentElement.style.scrollMarginTop = '100px'
147
- nav.value.parentElement.scrollIntoView({ behavior: 'smooth', block: 'start' })
145
+ scrollToBlockTop(nav.value?.parentElement)
148
146
  }
149
147
 
150
148
  function onClick(index: number) {
@@ -6,7 +6,6 @@
6
6
  >
7
7
  <div
8
8
  v-if="!hideSearchInput"
9
- ref="search"
10
9
  class="flex flex-wrap items-center justify-between"
11
10
  data-cy="search"
12
11
  >
@@ -357,6 +356,7 @@
357
356
  <script setup lang="ts">
358
357
  import { computed, provide, shallowReactive, useSlots, watch, useTemplateRef, type Component, type Ref } from 'vue'
359
358
  import { useRouteQuery } from '@vueuse/router'
359
+ import { useRoute } from 'vue-router'
360
360
  import { RiBookShelfLine, RiBuilding2Line, RiCloseCircleLine, RiDatabase2Line, RiLightbulbLine, RiLineChartLine, RiRssLine, RiTerminalLine } from '@remixicon/vue'
361
361
  import magnifyingGlassSrc from '../../../assets/illustrations/magnifying_glass.svg?url'
362
362
  import { useTranslation } from '../../composables/useTranslation'
@@ -365,6 +365,7 @@ import { configKey, forEachActiveCustomFilter, isCustomFilterActive, searchFilte
365
365
  import { useStableQueryParams } from '../../composables/useStableQueryParams'
366
366
  import { useComponentsConfig } from '../../config'
367
367
  import { useFetch } from '../../functions/api'
368
+ import { scrollToBlockTop } from '../../functions/scroll'
368
369
  import type { AsyncDataRequestStatus } from '../../functions/api.types'
369
370
  import type { Dataset } from '../../types/datasets'
370
371
  import type { Dataservice } from '../../types/dataservices'
@@ -423,6 +424,7 @@ if (!currentType.value) currentType.value = configKey(props.config[0] ?? { class
423
424
 
424
425
  const { t } = useTranslation()
425
426
  const componentsConfig = useComponentsConfig()
427
+ const route = useRoute()
426
428
 
427
429
  // Custom filter registry for useSearchFilter composable
428
430
  const customFilterRegistry = shallowReactive(new Map<string, CustomFilterEntry>())
@@ -797,14 +799,15 @@ function getFacets(key: string): FacetItem[] | undefined {
797
799
  }
798
800
 
799
801
  // Scroll handling
800
- const searchRef = useTemplateRef('search')
802
+ const resultsRef = useTemplateRef('results')
801
803
 
802
- function scrollToTop() {
803
- searchRef.value?.scrollIntoView({ behavior: 'smooth' })
804
- }
804
+ // Every criteria lives in the URL, custom filters included, so watching the
805
+ // query covers them all: whenever the result list is replaced, bring its top
806
+ // back into view rather than leaving the reader in the middle of a list they
807
+ // have not seen yet.
808
+ watch(() => route.query, () => scrollToBlockTop(resultsRef.value))
805
809
 
806
810
  function changePage(newPage: number) {
807
811
  page.value = newPage
808
- scrollToTop()
809
812
  }
810
813
  </script>
@@ -0,0 +1,20 @@
1
+ // Leaves room for the sticky elements sitting at the top of the viewport.
2
+ const SCROLL_MARGIN = 100
3
+
4
+ /**
5
+ * Bring the top of a block back into view after its content changed (paging,
6
+ * searching, filtering), so the reader restarts at the first result.
7
+ *
8
+ * Does nothing when the top of the block is already on screen: aligning the
9
+ * viewport on a block the reader is already looking at moves the page under
10
+ * them for no reason.
11
+ */
12
+ export function scrollToBlockTop(block: HTMLElement | null | undefined) {
13
+ if (!block) return
14
+
15
+ const { top } = block.getBoundingClientRect()
16
+ if (top >= 0 && top <= window.innerHeight) return
17
+
18
+ block.style.scrollMarginTop = `${SCROLL_MARGIN}px`
19
+ block.scrollIntoView({ behavior: 'smooth', block: 'start' })
20
+ }