mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 13:01:59 +03:00
feat(core): journal sidebar dater-picker navigation (#5558)
This commit is contained in:
parent
496dc588be
commit
70ea1e5ef8
@ -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}
|
||||||
|
@ -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)',
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const calendar = style({
|
||||||
|
padding: '16px',
|
||||||
|
});
|
@ -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 = {
|
||||||
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
@ -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');
|
||||||
|
Loading…
Reference in New Issue
Block a user