feat(mobile): sticky journal date-picker, auto fold when scroll (#8885)
This commit is contained in:
@@ -6,6 +6,7 @@ export const MONTH_VIEW_HEIGHT =
|
|||||||
TOTAL_WEEKS * CELL_HEIGHT + (TOTAL_WEEKS - 1) * ROWS_GAP;
|
TOTAL_WEEKS * CELL_HEIGHT + (TOTAL_WEEKS - 1) * ROWS_GAP;
|
||||||
export const WEEK_VIEW_HEIGHT = CELL_HEIGHT;
|
export const WEEK_VIEW_HEIGHT = CELL_HEIGHT;
|
||||||
|
|
||||||
export const HORIZONTAL_SWIPE_THRESHOLD = 4 * CELL_HEIGHT;
|
export const HORIZONTAL_SWIPE_THRESHOLD = 2 * CELL_HEIGHT;
|
||||||
|
export const SCROLL_DOWN_TO_FOLD_THRESHOLD = 30;
|
||||||
|
|
||||||
export const DATE_FORMAT = 'YYYY-MM-DD';
|
export const DATE_FORMAT = 'YYYY-MM-DD';
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { type HTMLAttributes, useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { JournalDatePickerContext } from './context';
|
import { JournalDatePickerContext } from './context';
|
||||||
import { ResizeViewport } from './viewport';
|
import { ResizeViewport } from './viewport';
|
||||||
|
|
||||||
export interface JournalDatePickerProps {
|
export interface JournalDatePickerProps
|
||||||
|
extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
||||||
date: string;
|
date: string;
|
||||||
onChange: (date: string) => void;
|
onChange: (date: string) => void;
|
||||||
withDotDates: Set<string | null | undefined>;
|
withDotDates: Set<string | null | undefined>;
|
||||||
@@ -12,6 +13,7 @@ export const JournalDatePicker = ({
|
|||||||
date: selected,
|
date: selected,
|
||||||
onChange,
|
onChange,
|
||||||
withDotDates,
|
withDotDates,
|
||||||
|
...attrs
|
||||||
}: JournalDatePickerProps) => {
|
}: JournalDatePickerProps) => {
|
||||||
const [cursor, setCursor] = useState(selected);
|
const [cursor, setCursor] = useState(selected);
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ export const JournalDatePicker = ({
|
|||||||
withDotDates,
|
withDotDates,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ResizeViewport></ResizeViewport>
|
<ResizeViewport {...attrs} />
|
||||||
</JournalDatePickerContext.Provider>
|
</JournalDatePickerContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
|
import { useGlobalEvent } from '@affine/core/mobile/hooks/use-global-events';
|
||||||
import anime from 'animejs';
|
import anime from 'animejs';
|
||||||
|
import clsx from 'clsx';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
import {
|
||||||
|
type HTMLAttributes,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CELL_HEIGHT,
|
CELL_HEIGHT,
|
||||||
DATE_FORMAT,
|
DATE_FORMAT,
|
||||||
MONTH_VIEW_HEIGHT,
|
MONTH_VIEW_HEIGHT,
|
||||||
|
SCROLL_DOWN_TO_FOLD_THRESHOLD,
|
||||||
WEEK_VIEW_HEIGHT,
|
WEEK_VIEW_HEIGHT,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { JournalDatePickerContext } from './context';
|
import { JournalDatePickerContext } from './context';
|
||||||
@@ -14,7 +24,10 @@ import { SwipeHelper } from './swipe-helper';
|
|||||||
import * as styles from './viewport.css';
|
import * as styles from './viewport.css';
|
||||||
import { WeekHeader, WeekRowSwipe } from './week';
|
import { WeekHeader, WeekRowSwipe } from './week';
|
||||||
|
|
||||||
export const ResizeViewport = () => {
|
export const ResizeViewport = ({
|
||||||
|
className,
|
||||||
|
...attrs
|
||||||
|
}: HTMLAttributes<HTMLDivElement>) => {
|
||||||
const { selected } = useContext(JournalDatePickerContext);
|
const { selected } = useContext(JournalDatePickerContext);
|
||||||
const draggableRef = useRef<HTMLDivElement>(null);
|
const draggableRef = useRef<HTMLDivElement>(null);
|
||||||
const viewportRef = useRef<HTMLDivElement>(null);
|
const viewportRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -97,8 +110,29 @@ export const ResizeViewport = () => {
|
|||||||
});
|
});
|
||||||
}, [handleToggleModeWithAnimation, mode]);
|
}, [handleToggleModeWithAnimation, mode]);
|
||||||
|
|
||||||
|
// auto fold when scroll down
|
||||||
|
const prevScrollYRef = useRef(window.scrollY);
|
||||||
|
useGlobalEvent(
|
||||||
|
'scroll',
|
||||||
|
useCallback(() => {
|
||||||
|
if (mode === 'week') return;
|
||||||
|
if (isAnimating) return;
|
||||||
|
const offset = window.scrollY - prevScrollYRef.current;
|
||||||
|
if (offset >= SCROLL_DOWN_TO_FOLD_THRESHOLD) {
|
||||||
|
prevScrollYRef.current = window.scrollY;
|
||||||
|
handleToggleModeWithAnimation('week', 0);
|
||||||
|
}
|
||||||
|
}, [handleToggleModeWithAnimation, isAnimating, mode])
|
||||||
|
);
|
||||||
|
useGlobalEvent(
|
||||||
|
'scrollend',
|
||||||
|
useCallback(() => {
|
||||||
|
prevScrollYRef.current = window.scrollY;
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.root}>
|
<div className={clsx(styles.root, className)} {...attrs}>
|
||||||
<WeekHeader className={styles.weekRow} />
|
<WeekHeader className={styles.weekRow} />
|
||||||
<div ref={viewportRef} style={{ height: draggedHeight }}>
|
<div ref={viewportRef} style={{ height: draggedHeight }}>
|
||||||
{mode === 'month' || isDragging || isAnimating ? (
|
{mode === 'month' || isDragging || isAnimating ? (
|
||||||
|
|||||||
@@ -93,3 +93,15 @@ export const journalIconButton = style({
|
|||||||
right: 12,
|
right: 12,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const journalDatePickerSticky = style({
|
||||||
|
background: cssVarV2('layer/background/primary'),
|
||||||
|
position: 'sticky',
|
||||||
|
zIndex: 1,
|
||||||
|
top: 56,
|
||||||
|
selectors: {
|
||||||
|
'&[data-standalone]': {
|
||||||
|
top: 'calc(44px + env(safe-area-inset-top, 12px))',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -262,6 +262,12 @@ const MobileDetailPage = ({
|
|||||||
date={date}
|
date={date}
|
||||||
onChange={handleDateChange}
|
onChange={handleDateChange}
|
||||||
withDotDates={allJournalDates}
|
withDotDates={allJournalDates}
|
||||||
|
className={styles.journalDatePickerSticky}
|
||||||
|
data-standalone={
|
||||||
|
environment.isPwa || BUILD_CONFIG.isAndroid || BUILD_CONFIG.isIOS
|
||||||
|
? ''
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<DetailPageImpl />
|
<DetailPageImpl />
|
||||||
|
|||||||
Reference in New Issue
Block a user