feat(core): journal sidebar dater-picker navigation (#5558)

This commit is contained in:
Cats Juice 2024-01-18 12:34:23 +00:00
parent 496dc588be
commit 70ea1e5ef8
No known key found for this signature in database
GPG Key ID: 1C1E76924FAFDDE4
8 changed files with 88 additions and 28 deletions

View File

@ -5,7 +5,7 @@ import {
} from '@blocksuite/icons'; } from '@blocksuite/icons';
import clsx from 'clsx'; import clsx from 'clsx';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { type HTMLAttributes, useCallback, useState } from 'react'; import { type HTMLAttributes, useCallback, useEffect, useState } from 'react';
import DatePicker, { type ReactDatePickerProps } from 'react-datepicker'; import DatePicker, { type ReactDatePickerProps } from 'react-datepicker';
import * as styles from './index.css'; import * as styles from './index.css';
@ -24,9 +24,10 @@ const months = [
'December', 'December',
]; ];
export interface AFFiNEDatePickerProps export interface AFFiNEDatePickerProps
extends Omit<ReactDatePickerProps, 'onChange'> { extends Omit<ReactDatePickerProps, 'onChange' | 'onSelect'> {
value?: string; value?: string;
onChange: (value: string) => void; onChange?: (value: string) => void;
onSelect?: (value: string) => void;
} }
interface HeaderLayoutProps extends HTMLAttributes<HTMLDivElement> { interface HeaderLayoutProps extends HTMLAttributes<HTMLDivElement> {
@ -74,6 +75,10 @@ const HeaderLayout = ({
export const AFFiNEDatePicker = ({ export const AFFiNEDatePicker = ({
value, value,
onChange, onChange,
onSelect,
calendarClassName,
...props ...props
}: AFFiNEDatePickerProps) => { }: AFFiNEDatePickerProps) => {
const [openMonthPicker, setOpenMonthPicker] = useState(false); const [openMonthPicker, setOpenMonthPicker] = useState(false);
@ -86,10 +91,16 @@ export const AFFiNEDatePicker = ({
const handleCloseMonthPicker = useCallback(() => { const handleCloseMonthPicker = useCallback(() => {
setOpenMonthPicker(false); setOpenMonthPicker(false);
}, []); }, []);
const handleSelectDate = (date: Date | null) => { const handleDateChange = (date: Date | null) => {
if (date) { if (date) {
setSelectedDate(date); setSelectedDate(date);
onChange(dayjs(date).format('YYYY-MM-DD')); onChange?.(dayjs(date).format('YYYY-MM-DD'));
setOpenMonthPicker(false);
}
};
const handleDateSelect = (date: Date | null) => {
if (date) {
onSelect?.(dayjs(date).format('YYYY-MM-DD'));
setOpenMonthPicker(false); setOpenMonthPicker(false);
} }
}; };
@ -207,17 +218,23 @@ export const AFFiNEDatePicker = ({
/> />
); );
}; };
useEffect(() => {
setSelectedDate(value ? dayjs(value).toDate() : null);
}, [value]);
return ( return (
<DatePicker <DatePicker
onClickOutside={handleCloseMonthPicker} onClickOutside={handleCloseMonthPicker}
className={styles.inputStyle} className={styles.inputStyle}
calendarClassName={styles.calendarStyle} calendarClassName={clsx(styles.calendarStyle, calendarClassName)}
weekDayClassName={() => styles.weekStyle} weekDayClassName={() => styles.weekStyle}
dayClassName={() => styles.dayStyle} dayClassName={() => styles.dayStyle}
popperClassName={styles.popperStyle} popperClassName={styles.popperStyle}
monthClassName={() => styles.mouthsStyle} monthClassName={() => styles.mouthsStyle}
selected={selectedDate} selected={selectedDate}
onChange={handleSelectDate} onChange={handleDateChange}
onSelect={handleDateSelect}
showPopperArrow={false} showPopperArrow={false}
dateFormat="MMM dd" dateFormat="MMM dd"
showMonthYearPicker={openMonthPicker} showMonthYearPicker={openMonthPicker}

View File

@ -151,28 +151,22 @@ export const dayStyle = style([
fontWeight: '400', fontWeight: '400',
borderRadius: '8px', borderRadius: '8px',
selectors: { selectors: {
'&:hover': { '&[aria-selected="false"]:hover': {
background: 'var(--affine-hover-color)', background: 'var(--affine-hover-color)',
borderRadius: '8px', borderRadius: '8px',
transition: 'background-color 0.3s ease-in-out', transition: 'background-color 0.3s ease-in-out',
}, },
'&[aria-selected="true"]': { '&[aria-selected="true"]': {
color: 'var(--affine-black)', color: 'var(--affine-pure-white)',
background: 'var(--affine-hover-color)', background: 'var(--affine-primary-color)',
}, fontWeight: '500',
'&[aria-selected="true"]:hover': {
background: 'var(--affine-hover-color)',
}, },
'&[tabindex="0"][aria-selected="false"]': { '&[tabindex="0"][aria-selected="false"]': {
background: 'var(--affine-background-overlay-panel-color)', background: 'var(--affine-background-overlay-panel-color)',
}, },
'&.react-datepicker__day--today[aria-selected="false"]': { '&.react-datepicker__day--today[aria-selected="false"]': {
background: 'var(--affine-primary-color)', fontWeight: '600',
color: 'var(--affine-palette-line-white)', color: 'var(--affine-primary-color)',
},
'&.react-datepicker__day--today[aria-selected="false"]:hover': {
color: 'var(--affine-black)',
background: 'var(--affine-hover-color)',
}, },
'&.react-datepicker__day--outside-month[aria-selected="false"]': { '&.react-datepicker__day--outside-month[aria-selected="false"]': {
color: 'var(--affine-text-disable-color)', color: 'var(--affine-text-disable-color)',

View File

@ -194,7 +194,7 @@ const DetailPageImpl = memo(function DetailPageImpl({ page }: { page: Page }) {
!isInTrash ? ( !isInTrash ? (
<div className={styles.sidebarContainerInner}> <div className={styles.sidebarContainerInner}>
<RightSidebarHeader workspace={blockSuiteWorkspace} page={page} /> <RightSidebarHeader workspace={blockSuiteWorkspace} page={page} />
<EditorSidebar /> <EditorSidebar workspace={blockSuiteWorkspace} page={page} />
</div> </div>
) : null ) : null
} }

View File

@ -1,11 +1,16 @@
import { useAtomValue } from 'jotai'; import { useAtomValue } from 'jotai';
import type { EditorExtensionProps } from '.';
import { editorSidebarActiveExtensionAtom } from './atoms'; import { editorSidebarActiveExtensionAtom } from './atoms';
import * as styles from './editor-sidebar.css'; import * as styles from './editor-sidebar.css';
export const EditorSidebar = () => { export const EditorSidebar = (props: EditorExtensionProps) => {
const activeExtension = useAtomValue(editorSidebarActiveExtensionAtom); const activeExtension = useAtomValue(editorSidebarActiveExtensionAtom);
const Component = activeExtension?.Component; const Component = activeExtension?.Component;
return <div className={styles.root}>{Component ? <Component /> : null}</div>; return (
<div className={styles.root}>
{Component ? <Component {...props} /> : null}
</div>
);
}; };

View File

@ -0,0 +1,5 @@
import { style } from '@vanilla-extract/css';
export const calendar = style({
padding: '16px',
});

View File

@ -1,9 +1,40 @@
import { AFFiNEDatePicker } from '@affine/component';
import {
useJournalHelper,
useJournalInfoHelper,
} from '@affine/core/hooks/use-journal';
import { TodayIcon } from '@blocksuite/icons'; import { TodayIcon } from '@blocksuite/icons';
import dayjs from 'dayjs';
import { useCallback, useEffect, useState } from 'react';
import type { EditorExtension } from '..'; import type { EditorExtension, EditorExtensionProps } from '..';
import * as styles from './journal.css';
const EditorJournalPanel = () => { const EditorJournalPanel = ({ workspace, page }: EditorExtensionProps) => {
return <div>journal extension</div>; const { journalDate } = useJournalInfoHelper(page?.meta);
const { openJournal } = useJournalHelper(workspace);
const [date, setDate] = useState(dayjs().format('YYYY-MM-DD'));
useEffect(() => {
journalDate && setDate(journalDate.format('YYYY-MM-DD'));
}, [journalDate]);
const onDateSelect = useCallback(
(date: string) => {
if (journalDate && dayjs(date).isSame(dayjs(journalDate))) return;
openJournal(date);
},
[journalDate, openJournal]
);
return (
<AFFiNEDatePicker
inline
value={date}
onSelect={onDateSelect}
calendarClassName={styles.calendar}
/>
);
}; };
export const journalExtension: EditorExtension = { export const journalExtension: EditorExtension = {

View File

@ -1,7 +1,15 @@
import type { BlockSuiteWorkspace } from '@affine/core/shared';
import type { Page } from '@blocksuite/store';
export type EditorExtensionName = 'outline' | 'frame' | 'copilot' | 'journal'; export type EditorExtensionName = 'outline' | 'frame' | 'copilot' | 'journal';
export interface EditorExtensionProps {
workspace: BlockSuiteWorkspace;
page: Page;
}
export interface EditorExtension { export interface EditorExtension {
name: EditorExtensionName; name: EditorExtensionName;
icon: React.ReactNode; icon: React.ReactNode;
Component: React.ComponentType; Component: React.ComponentType<EditorExtensionProps>;
} }

View File

@ -460,8 +460,8 @@ test('disable quick search when the link-popup is visitable', async ({
await getBlockSuiteEditorTitle(page).click(); await getBlockSuiteEditorTitle(page).click();
await getBlockSuiteEditorTitle(page).fill(specialTitle); await getBlockSuiteEditorTitle(page).fill(specialTitle);
await page.keyboard.press('Enter', { delay: 10 }); await page.keyboard.press('Enter', { delay: 10 });
await page.keyboard.insertText('123456'); await page.keyboard.insertText('1234567890');
await page.getByText('123456').dblclick(); await page.getByText('1234567890').dblclick();
await withCtrlOrMeta(page, () => page.keyboard.press('k', { delay: 50 })); await withCtrlOrMeta(page, () => page.keyboard.press('k', { delay: 50 }));
const linkPopup = page.locator('.affine-link-popover'); const linkPopup = page.locator('.affine-link-popover');