From f4b26a16f8ef09470e0e3d6401dbdd52d3c07128 Mon Sep 17 00:00:00 2001 From: Cats Juice Date: Thu, 18 Jan 2024 14:05:15 +0000 Subject: [PATCH] feat(core): journal sidebar conflict block (#5574) --- .../editor-sidebar/extensions/journal.css.ts | 50 +++++- .../editor-sidebar/extensions/journal.tsx | 151 +++++++++++++++--- packages/frontend/i18n/src/resources/en.json | 3 +- 3 files changed, 177 insertions(+), 27 deletions(-) diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/journal.css.ts b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/journal.css.ts index 728b17475..35f19a962 100644 --- a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/journal.css.ts +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/journal.css.ts @@ -1,7 +1,8 @@ -import { style } from '@vanilla-extract/css'; +import { globalStyle, style } from '@vanilla-extract/css'; const interactive = style({ position: 'relative', + cursor: 'pointer', selectors: { '&:hover': { @@ -134,6 +135,12 @@ export const pageItem = style([ padding: '0 4px', gap: 8, height: 30, + + selectors: { + '&[aria-selected="true"]': { + backgroundColor: 'var(--affine-hover-color)', + }, + }, }, ]); export const pageItemIcon = style({ @@ -151,4 +158,45 @@ export const pageItemLabel = style({ fontSize: 'var(--affine-font-size-sm)', color: 'var(--affine-text-primary-color)', textAlign: 'left', + selectors: { + '[aria-selected="true"] &': { + // TODO: wait for design + color: 'var(--affine-primary-color)', + }, + }, }); + +// conflict +export const journalConflictBlock = style({ + padding: '0 16px 16px 16px', +}); +export const journalConflictWrapper = style({ + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(170px, 1fr))', + rowGap: 4, + columnGap: 8, +}); +export const journalConflictMoreTrigger = style([ + interactive, + { + color: 'var(--affine-text-secondary-color)', + height: 30, + borderRadius: 4, + padding: '0px 8px', + fontSize: 'var(--affine-font-size-sm)', + display: 'flex', + alignItems: 'center', + }, +]); + +// TODO: when date-picker's cell is customizable, we should implement by custom cell +// override date-picker's active day when is not journal +globalStyle( + `.${journalPanel}[data-is-journal="false"] .react-datepicker__day[aria-selected="true"]`, + { + backgroundColor: 'transparent', + color: 'var(--affine-text-primary-color)', + fontWeight: 500, + border: '1px solid var(--affine-primary-color)', + } +); diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/journal.tsx b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/journal.tsx index b939f7e3d..05d34ff8e 100644 --- a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/journal.tsx +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/journal.tsx @@ -1,16 +1,28 @@ -import { AFFiNEDatePicker, Scrollable } from '@affine/component'; +import { + AFFiNEDatePicker, + IconButton, + Menu, + Scrollable, +} from '@affine/component'; +import { MoveToTrash } from '@affine/core/components/page-list'; +import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper'; import { useJournalHelper, useJournalInfoHelper, } from '@affine/core/hooks/use-journal'; import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { EdgelessIcon, PageIcon, TodayIcon } from '@blocksuite/icons'; +import { + EdgelessIcon, + MoreHorizontalIcon, + PageIcon, + TodayIcon, +} from '@blocksuite/icons'; import type { Page } from '@blocksuite/store'; import { assignInlineVars } from '@vanilla-extract/dynamic'; import clsx from 'clsx'; import dayjs from 'dayjs'; -import type { HTMLAttributes, ReactNode } from 'react'; +import type { HTMLAttributes, PropsWithChildren, ReactNode } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import type { EditorExtension, EditorExtensionProps } from '..'; @@ -26,30 +38,30 @@ const CountDisplay = ({ }: { count: number; max?: number } & HTMLAttributes) => { return {count > max ? `${max}+` : count}; }; -interface PageItemProps extends HTMLAttributes { +interface PageItemProps extends HTMLAttributes { page: Page; right?: ReactNode; } const PageItem = ({ page, right, className, ...attrs }: PageItemProps) => { const { isJournal } = useJournalInfoHelper(page.meta); - const icon = isJournal ? ( - - ) : page.meta.mode === 'edgeless' ? ( - - ) : ( - - ); + const Icon = isJournal + ? TodayIcon + : page.meta.mode === 'edgeless' + ? EdgelessIcon + : PageIcon; return ( - + ); }; @@ -59,10 +71,13 @@ interface NavItem { label: string; count: number; } +interface JournalBlockProps extends EditorExtensionProps { + date: dayjs.Dayjs; +} const EditorJournalPanel = (props: EditorExtensionProps) => { const { workspace, page } = props; - const { journalDate } = useJournalInfoHelper(page?.meta); + const { journalDate, isJournal } = useJournalInfoHelper(page?.meta); const { openJournal } = useJournalHelper(workspace); const [date, setDate] = useState(dayjs().format('YYYY-MM-DD')); @@ -79,15 +94,15 @@ const EditorJournalPanel = (props: EditorExtensionProps) => { ); return ( -
+
- - + +
); }; @@ -116,10 +131,9 @@ const DailyCountEmptyFallback = ({ name }: { name: NavItemName }) => {
); }; -const JournalDailyCountBlock = ({ workspace, page }: EditorExtensionProps) => { +const JournalDailyCountBlock = ({ workspace, date }: JournalBlockProps) => { const nodeRef = useRef(null); const t = useAFFiNEI18N(); - const { journalDate } = useJournalInfoHelper(page?.meta); const [activeItem, setActiveItem] = useState('createdToday'); const navigateHelper = useNavigateHelper(); @@ -129,16 +143,13 @@ const JournalDailyCountBlock = ({ workspace, page }: EditorExtensionProps) => { const pages: Page[] = []; Array.from(workspace.pages.values()).forEach(page => { if (page.meta.trash) return; - if ( - page.meta[field] && - dayjs(page.meta[field]).isSame(journalDate, 'day') - ) { + if (page.meta[field] && dayjs(page.meta[field]).isSame(date, 'day')) { pages.push(page); } }); return sortPagesByDate(pages, field); }, - [journalDate, workspace.pages] + [date, workspace.pages] ); const createdToday = useMemo( @@ -227,6 +238,96 @@ const JournalDailyCountBlock = ({ workspace, page }: EditorExtensionProps) => { ); }; +const MAX_CONFLICT_COUNT = 5; +interface ConflictListProps + extends JournalBlockProps, + PropsWithChildren, + HTMLAttributes { + pages: Page[]; +} +const ConflictList = ({ + page: currentPage, + pages, + workspace, + children, + className, + ...attrs +}: ConflictListProps) => { + const navigateHelper = useNavigateHelper(); + const { setTrashModal } = useTrashModalHelper(workspace); + + const handleOpenTrashModal = useCallback( + (page: Page) => { + if (!page.meta) return; + setTrashModal({ + open: true, + pageIds: [page.id], + pageTitles: [page.meta.title], + }); + }, + [setTrashModal] + ); + + return ( +
+ {pages.map(page => { + const isCurrent = page.id === currentPage.id; + return ( + handleOpenTrashModal(page)} /> + } + > + + + + + } + onClick={() => navigateHelper.openPage(workspace.id, page.id)} + /> + ); + })} + {children} +
+ ); +}; +const JournalConflictBlock = (props: JournalBlockProps) => { + const { workspace, date } = props; + const t = useAFFiNEI18N(); + const journalHelper = useJournalHelper(workspace); + const pages = journalHelper.getJournalsByDate(date.format('YYYY-MM-DD')); + + if (pages.length <= 1) return null; + + return ( + + {pages.length > MAX_CONFLICT_COUNT ? ( + + } + > +
+ {t['com.affine.journal.conflict-show-more']({ + count: (pages.length - MAX_CONFLICT_COUNT).toFixed(0), + })} +
+
+ ) : null} +
+ ); +}; + export const journalExtension: EditorExtension = { name: 'journal', icon: , diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index a85767935..d6ade28c9 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -1047,5 +1047,6 @@ "com.affine.journal.created-today": "Created today", "com.affine.journal.updated-today": "Updated today", "com.affine.journal.daily-count-created-empty-tips": "You haven't created anything yet", - "com.affine.journal.daily-count-updated-empty-tips": "You haven't updated anything yet" + "com.affine.journal.daily-count-updated-empty-tips": "You haven't updated anything yet", + "com.affine.journal.conflict-show-more": "{{count}} more articles" }