Skip to content

Commit

Permalink
feat: collapse desktop header in scrolling (#288)
Browse files Browse the repository at this point in the history
EMP-1003

---------

Co-authored-by: Guillermo Cacheda <cachedacodes@gmail.com>
  • Loading branch information
lauramargar and CachedaCodes authored Jun 13, 2023
1 parent 1e9f821 commit 979ff84
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 39 deletions.
44 changes: 44 additions & 0 deletions src/components/collapse-height-animation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<template>
<div
class="x-collapse-height"
:class="{
'x-collapse-height--is-collapsed': isCollapsed
}"
>
<slot />
</div>
</template>

<script>
import { defineComponent } from 'vue';
export default defineComponent({
props: {
isCollapsed: {
type: Boolean
}
}
});
</script>

<style lang="scss" scoped>
.x-collapse-height {
display: grid;
grid-template-rows: 1fr;
overflow: hidden;
transition: grid-template-rows 0.35s;
& > * {
min-height: 0;
transition: visibility 0.35s;
visibility: visible;
}
&--is-collapsed {
grid-template-rows: 0fr;
& > * {
visibility: hidden;
}
}
}
</style>
10 changes: 1 addition & 9 deletions src/components/desktop/desktop-header-floating-predictive.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<MaxDesktopWidthItem class="x-pb-24">
<MaxDesktopWidthItem class="x-pb-8">
<header class="x-grid x-grid-cols-6 x-items-center x-gap-12 x-pt-24">
<Logo />

Expand All @@ -16,12 +16,6 @@
<CrossIcon class="x-icon-lg" />
</CloseMainModal>
</header>

<div class="x-grid x-grid-cols-6">
<LocationProvider location="predictive_layer" class="x-col-span-4 x-col-start-2">
<RelatedTags v-if="$x.relatedTags.length > 0" class="x-pt-8" />
</LocationProvider>
</div>
</MaxDesktopWidthItem>
</template>

Expand All @@ -32,7 +26,6 @@
import SearchBox from '../search-box.vue';
import Logo from '../logo.vue';
import MaxDesktopWidthItem from '../max-desktop-width-item.vue';
import { RelatedTags } from '../search';
export default defineComponent({
components: {
Expand All @@ -42,7 +35,6 @@
Logo,
SearchBox,
PredictiveLayer,
RelatedTags,
LocationProvider
}
});
Expand Down
16 changes: 1 addition & 15 deletions src/components/desktop/desktop-header-full-predictive.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="x-relative x-pb-24">
<div class="x-relative x-pb-8">
<MaxDesktopWidthItem>
<header class="x-flex x-items-center x-gap-48 x-pt-24">
<Logo />
Expand All @@ -19,16 +19,6 @@
<LocationProvider location="predictive_layer">
<FullWidthPredictive />
</LocationProvider>

<MaxDesktopWidthItem>
<DesktopSearchboxAlign class="x-layout-container">
<div class="x-layout-item">
<LocationProvider location="predictive_layer">
<RelatedTags v-if="$x.relatedTags.length > 0" class="x-pt-8" />
</LocationProvider>
</div>
</DesktopSearchboxAlign>
</MaxDesktopWidthItem>
</div>
</template>

Expand All @@ -39,19 +29,15 @@
import Logo from '../logo.vue';
import FullWidthPredictive from '../predictive-layer/full-width-predictive.vue';
import MaxDesktopWidthItem from '../max-desktop-width-item.vue';
import { RelatedTags } from '../search';
import DesktopSearchboxAlign from './desktop-searchbox-align.vue';
export default defineComponent({
components: {
DesktopSearchboxAlign,
MaxDesktopWidthItem,
FullWidthPredictive,
CloseMainModal,
CrossIcon,
Logo,
SearchBox,
RelatedTags,
LocationProvider
}
});
Expand Down
4 changes: 3 additions & 1 deletion src/components/desktop/desktop-searchbox-align.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<template>
<div class="x-layout-container-ml-[calc(142px+48px)] x-layout-container-mr-[calc(40px+48px)]">
<div
class="x-layout-container x-layout-container-ml-[calc(142px+48px)] x-layout-container-mr-[calc(40px+48px)]"
>
<slot />
</div>
</template>
Expand Down
52 changes: 52 additions & 0 deletions src/components/desktop/desktop-sub-header.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<template>
<CollapseHeightAnimation :isCollapsed="hasScrolledPastThreshold">
<MaxDesktopWidthItem>
<DesktopSearchboxAlign>
<div class="x-layout-item" :class="{ 'x-grid x-grid-cols-6': !isFullPredictive }">
<LocationProvider location="predictive_layer">
<RelatedTags v-if="$x.relatedTags.length > 0" class="x-pb-24" />
</LocationProvider>
</div>
</DesktopSearchboxAlign>

<div v-if="!$x.redirections.length && hasSearched">
<DesktopToolbar />
</div>
<div v-if="$x.totalResults > 0 && hasSearched && $x.selectedFilters.length">
<SelectedFilters class="x-py-16" />
</div>
</MaxDesktopWidthItem>
</CollapseHeightAnimation>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { LocationProvider } from '@empathyco/x-components';
import RelatedTags from '../search/related-tags.vue';
import CollapseHeightAnimation from '../collapse-height-animation.vue';
import IsScrollingUp from '../has-scroll-past-threshold.mixin';
import MaxDesktopWidthItem from '../max-desktop-width-item.vue';
import DesktopSearchboxAlign from './desktop-searchbox-align.vue';
import DesktopToolbar from './desktop-toolbar.vue';
export default defineComponent({
components: {
MaxDesktopWidthItem,
LocationProvider,
RelatedTags,
CollapseHeightAnimation,
DesktopToolbar,
DesktopSearchboxAlign,
SelectedFilters: () => import('../search').then(m => m.SelectedFilters)
},
mixins: [IsScrollingUp],
props: {
hasSearched: {
type: Boolean
},
isFullPredictive: {
type: Boolean
}
}
});
</script>
19 changes: 19 additions & 0 deletions src/components/desktop/desktop-top-section.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<div>
<DesktopHeaderFullPredictive />

<DesktopSubHeader :hasSearched="hasSearched" :isFullPredictive="true" class="x-layout-item" />
</div>
</template>

<script>
import { defineComponent } from 'vue';
import HasSearchedMixin from '../has-searched.mixin.ts';
import DesktopHeaderFullPredictive from './desktop-header-full-predictive.vue';
import DesktopSubHeader from './desktop-sub-header.vue';
export default defineComponent({
components: { DesktopSubHeader, DesktopHeaderFullPredictive },
mixins: [HasSearchedMixin]
});
</script>
17 changes: 3 additions & 14 deletions src/components/desktop/desktop.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="x-layout-container">
<DesktopHeaderFullPredictive />
<DesktopTopSection />

<MainScroll class="x-flex x-flex-col">
<Scroll id="main-scroll">
Expand All @@ -10,14 +10,8 @@
<SpellcheckMessage class="x-mb-16" data-test="spellcheck-message" />
</LocationProvider>
<NoResultsMessage class="x-mb-16" data-test="no-results-message" />
<DesktopToolbar />
</div>

<SelectedFilters
v-if="$x.totalResults > 0 && hasSearched && $x.selectedFilters.length"
class="x-py-16"
/>

<LocationProvider location="no_query">
<CustomQueryPreview class="x-mt-56" />
</LocationProvider>
Expand Down Expand Up @@ -62,33 +56,28 @@
import { MainScroll, Scroll } from '@empathyco/x-components/scroll';
import Main from '../main.vue';
import CustomQueryPreview from '../pre-search/custom-query-preview.vue';
import PredictiveLayer from '../predictive-layer/predictive-layer.vue';
import ScrollToTop from '../scroll-to-top.vue';
import HasSearchedMixin from '../has-searched.mixin';
import MyHistoryAside from '../my-history/my-history-aside.vue';
import MyHistoryConfirmDisableModal from '../my-history/my-history-confirm-disable-modal.vue';
import MaxDesktopWidthItem from '../max-desktop-width-item.vue';
import DesktopToolbar from './desktop-toolbar.vue';
import DesktopHeaderFullPredictive from './desktop-header-full-predictive.vue';
import DesktopTopSection from './desktop-top-section.vue';
@Component({
components: {
DesktopTopSection,
MaxDesktopWidthItem,
DesktopHeaderFullPredictive,
CustomQueryPreview,
BaseIdModal,
MyHistoryAside,
DesktopToolbar,
LocationProvider,
Main,
MainScroll,
MyHistoryConfirmDisableModal,
PredictiveLayer,
Scroll,
ScrollToTop,
DesktopAside: () => import('../search').then(m => m.DesktopAside),
NoResultsMessage: () => import('../search').then(m => m.NoResultsMessage),
SelectedFilters: () => import('../search').then(m => m.SelectedFilters),
SpellcheckMessage: () => import('../search').then(m => m.SpellcheckMessage)
}
})
Expand Down
50 changes: 50 additions & 0 deletions src/components/has-scroll-past-threshold.mixin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { State } from '@empathyco/x-components';
import Vue from 'vue';
import Component from 'vue-class-component';
import { Dictionary } from '@empathyco/x-utils';
import { ScrollComponentState } from '@empathyco/x-components/scroll';
import { Watch } from 'vue-property-decorator';
@Component
export default class IsScrollingUp extends Vue {
protected hasScrolledPastThresholdFlag = false;
protected scrollOffset = 200;

@State('scroll', 'data')
public scrollPositionsMap!: Dictionary<ScrollComponentState>;
protected get mainScrollPosition(): number {
return this.scrollPositionsMap['main-scroll']?.position;
}

@Watch('mainScrollPosition', { deep: true })
updateHasScrolledPastThreshold(): void {
// TODO change this implementation when the scroll module is fixed. Task EMP-1049
const mainScrollData = this.scrollPositionsMap['main-scroll'];

if (mainScrollData?.hasReachedStart) {
this.hasScrolledPastThresholdFlag = false;
return;
}

if (mainScrollData?.hasAlmostReachedEnd) {
this.hasScrolledPastThresholdFlag = true;
return;
}

const isScrollingUp = mainScrollData?.direction === 'UP';
if (isScrollingUp || this.mainScrollPosition < this.scrollOffset) {
this.hasScrolledPastThresholdFlag = false;
} else if (!isScrollingUp && this.mainScrollPosition > this.scrollOffset) {
this.hasScrolledPastThresholdFlag = true;
}
}

/**
* Checks the direction and the position of the scroll.
*
* @returns True if the user is scrolling up and has scrolled more than
* the defined scrollOffset.
*/
protected get hasScrolledPastThreshold(): boolean {
return this.hasScrolledPastThresholdFlag;
}
}

0 comments on commit 979ff84

Please sign in to comment.