Skip to content
This repository was archived by the owner on Nov 5, 2023. It is now read-only.
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
8 changes: 4 additions & 4 deletions src/Grid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {
fromWindowScroll,
useObservable,
} from "./utilites";
import { PageProvider, pipeline } from "./pipeline";
import { PageProvider, pipeline, ScrollAction } from "./pipeline";
import { once } from "ramda";
import { VueInstance } from "@vueuse/core";

Expand Down Expand Up @@ -95,7 +95,7 @@ export default defineComponent({
const {
buffer$, // the items in the current scanning window
contentHeight$, // the height of the whole list
windowScrollTo$, // the value sent to window.scrollTo()
scrollAction$, // the value sent to window.scrollTo()
} = pipeline({
// streams of prop
length$: fromProp(props, "length"),
Expand All @@ -113,8 +113,8 @@ export default defineComponent({

onUpdated(
once(() => {
windowScrollTo$.subscribe((next) => {
window.scrollTo({ top: next, behavior: "smooth" });
scrollAction$.subscribe(([el, top]: ScrollAction) => {
el.scrollTo({ top, behavior: "smooth" });
});
})
);
Expand Down
38 changes: 23 additions & 15 deletions src/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
shareReplay,
switchMap,
take,
withLatestFrom,
} from "rxjs";
import {
__,
Expand All @@ -35,6 +34,7 @@ import {
without,
zip,
} from "ramda";
import { getVerticalScrollParent } from "./utilites";

export function computeHeightAboveWindowOf(el: Element): number {
const top = el.getBoundingClientRect().top;
Expand Down Expand Up @@ -228,10 +228,12 @@ interface PipelineInput {
scrollTo$: Observable<number | undefined>;
}

export type ScrollAction = [Element, number];

interface PipelineOutput {
buffer$: Observable<InternalItem[]>;
contentHeight$: Observable<number>;
windowScrollTo$: Observable<number>;
scrollAction$: Observable<ScrollAction>;
}

export function pipeline({
Expand Down Expand Up @@ -262,20 +264,26 @@ export function pipeline({
// endregion

// region: scroll to a given item by index
const windowScrollTo$ = scrollTo$.pipe(
const scrollAction$: Observable<ScrollAction> = scrollTo$.pipe(
filter(complement(isNil)),
switchMap((scrollTo) =>
combineLatest([of(scrollTo), resizeMeasurement$, rootResize$]).pipe(
take(1)
)
switchMap<number, Observable<[number, ResizeMeasurement, Element]>>(
(scrollTo) =>
combineLatest([of(scrollTo), resizeMeasurement$, rootResize$]).pipe(
take(1)
)
),
map(
([scrollTo, { columns, itemHeightWithGap }, rootEl]) =>
// The offset within the grid
Math.floor(scrollTo / columns) * itemHeightWithGap +
// The offset of grid root to the document
(rootEl.getBoundingClientRect().top +
document.documentElement.scrollTop)
map<[number, ResizeMeasurement, Element], ScrollAction>(
([scrollTo, { columns, itemHeightWithGap }, rootEl]) => {
const verticalScrollEl = getVerticalScrollParent(rootEl);

const scrollTop =
// The offset within the grid container
Math.floor((scrollTo - 1) / columns) * itemHeightWithGap +
// Offset to the offsetParent
(rootEl instanceof HTMLElement ? rootEl.offsetTop : 0);

return [verticalScrollEl, scrollTop];
}
)
);
// endregion
Expand Down Expand Up @@ -319,5 +327,5 @@ export function pipeline({
).pipe(scan(accumulateBuffer, []));
// endregion

return { buffer$, contentHeight$, windowScrollTo$ };
return { buffer$, contentHeight$, scrollAction$: scrollAction$ };
}
32 changes: 32 additions & 0 deletions src/utilites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,35 @@ export function useObservable<H>(observable: Observable<H>): Readonly<Ref<H>> {

return valueRef as Readonly<Ref<H>>;
}

export function getVerticalScrollParent(
element: Element,
includeHidden: boolean = false
): Element {
const style = getComputedStyle(element);
const excludeStaticParent = style.position === "absolute";
const overflowRegex = includeHidden
? /(auto|scroll|hidden)/
: /(auto|scroll)/;

if (style.position === "fixed") {
return document.body;
}

for (
let parent: Element | null = element;
(parent = parent.parentElement);

) {
const parentStyle = getComputedStyle(parent);

if (excludeStaticParent && parentStyle.position === "static") {
continue;
}

if (overflowRegex.test(parentStyle.overflow + parentStyle.overflowY))
return parent;
}

return document.scrollingElement || document.documentElement;
}