Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: collapse desktop header in scrolling #288

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>
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;
}
}