Skip to content

Commit

Permalink
feat(core): add info modal to doc title bar
Browse files Browse the repository at this point in the history
  • Loading branch information
JimmFly committed Jul 3, 2024
1 parent cc7740d commit ae8d9f1
Show file tree
Hide file tree
Showing 16 changed files with 502 additions and 32 deletions.
1 change: 1 addition & 0 deletions packages/frontend/core/src/atoms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const openQuotaModalAtom = atom(false);
export const openStarAFFiNEModalAtom = atom(false);
export const openIssueFeedbackModalAtom = atom(false);
export const openHistoryTipsModalAtom = atom(false);
export const openInfoModalAtom = atom(false);

export const rightSidebarWidthAtom = atom(320);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './icons-mapping';
export * from './info-modal/info-modal';
export * from './page-properties-manager';
export * from './table';
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { cssVar } from '@toeverything/theme';
import { globalStyle, style } from '@vanilla-extract/css';

export const title = style({
fontSize: cssVar('fontSm'),
fontWeight: '500',
color: cssVar('textSecondaryColor'),
padding: '6px',
});

export const wrapper = style({
width: '100%',
borderRadius: 4,
color: cssVar('textPrimaryColor'),
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: 2,
padding: '6px',
':hover': {
backgroundColor: cssVar('hoverColor'),
},
});

globalStyle(`${wrapper} svg`, {
color: cssVar('iconSecondary'),
fontSize: 16,
transform: 'none',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useI18n } from '@affine/i18n';
import { useContext } from 'react';

import { AffinePageReference } from '../../reference-link';
import { managerContext } from '../common';
import * as styles from './back-links-row.css';
export const BackLinksRow = ({
backLinks,
onClick,
}: {
backLinks: { docId: string; blockId: string; title: string }[];
onClick?: () => void;
}) => {
const manager = useContext(managerContext);
const t = useI18n();
return (
<div>
<div className={styles.title}>
{t['com.affine.page-properties.backlinks']()} · {backLinks.length}
</div>
{backLinks.map(link => (
<AffinePageReference
key={link.docId + ':' + link.blockId}
pageId={link.docId}
wrapper={props => (
<div className={styles.wrapper} onClick={onClick} {...props} />
)}
docCollection={manager.workspace.docCollection}
/>
))}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css';

export const container = style({
maxWidth: 480,
minWidth: 360,
padding: '20px 0',
position: 'fixed',
top: '120px',
});

export const titleContainer = style({
display: 'flex',
width: '100%',
flexDirection: 'column',
});

export const titleStyle = style({
fontSize: cssVar('fontH6'),
fontWeight: '600',
});

export const rowNameContainer = style({
display: 'flex',
flexDirection: 'row',
gap: 6,
padding: 6,
width: '160px',
});

export const viewport = style({
maxHeight: 'calc(100vh - 120px)',
padding: '0 24px',
});

export const scrollBar = style({
width: 6,
transform: 'translateX(-4px)',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import {
Divider,
type InlineEditHandle,
Modal,
Scrollable,
} from '@affine/component';
import { DocLinksService } from '@affine/core/modules/doc-link';
import type { Doc } from '@blocksuite/store';
import { useLiveData, useServices, type Workspace } from '@toeverything/infra';
import { Suspense, useCallback, useContext, useRef } from 'react';

import { BlocksuiteHeaderTitle } from '../../../blocksuite/block-suite-header/title';
import { managerContext } from '../common';
import {
PagePropertiesAddProperty,
PagePropertyRow,
PageTagsRow,
SortableProperties,
usePagePropertiesManager,
} from '../table';
import { BackLinksRow } from './back-links-row';
import * as styles from './info-modal.css';
import { TimeRow } from './time-row';

export const InfoModal = ({
open,
onOpenChange,
page,
workspace,
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
page: Doc;
workspace: Workspace;
}) => {
const titleInputHandleRef = useRef<InlineEditHandle>(null);

const manager = usePagePropertiesManager(page);
const handleClose = useCallback(() => {
onOpenChange(false);
}, [onOpenChange]);
return (
<Modal
contentOptions={{ className: styles.container }}
open={open}
onOpenChange={onOpenChange}
withoutCloseButton
>
<Scrollable.Root>
<Scrollable.Viewport className={styles.viewport}>
<div className={styles.titleContainer}>
<BlocksuiteHeaderTitle
className={styles.titleStyle}
inputHandleRef={titleInputHandleRef}
pageId={page.id}
docCollection={workspace.docCollection}
/>
</div>
<managerContext.Provider value={manager}>
<Suspense>
<InfoTable onClose={handleClose} />
</Suspense>
</managerContext.Provider>
</Scrollable.Viewport>
<Scrollable.Scrollbar className={styles.scrollBar} />
</Scrollable.Root>
</Modal>
);
};

const InfoTable = ({ onClose }: { onClose: () => void }) => {
const manager = useContext(managerContext);
const { docLinksServices } = useServices({
DocLinksServices: DocLinksService,
});
const docBacklinks = docLinksServices.backlinks;
const backLinks = useLiveData(docBacklinks.backlinks$);
return (
<div>
<TimeRow />
<Divider />
{backLinks.length > 0 ? (
<>
<BackLinksRow backLinks={backLinks} onClick={onClose} />
<Divider />
</>
) : null}
<PageTagsRow rowNameClassName={styles.rowNameContainer} />
<SortableProperties>
{properties =>
properties.length ? (
<div>
{properties
.filter(
property =>
manager.isPropertyRequired(property.id) ||
(property.visibility !== 'hide' &&
!(
property.visibility === 'hide-if-empty' &&
!property.value
))
)
.map(property => (
<PagePropertyRow
key={property.id}
property={property}
rowNameClassName={styles.rowNameContainer}
/>
))}
</div>
) : null
}
</SortableProperties>
{manager.readonly ? null : <PagePropertiesAddProperty />}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css';

export const icon = style({
fontSize: 16,
color: cssVar('iconSecondary'),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
});

export const rowNameContainer = style({
display: 'flex',
flexDirection: 'row',
alignItems: 'center',

gap: 6,
padding: 6,
width: '160px',
});

export const rowName = style({
flexGrow: 1,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
fontSize: cssVar('fontSm'),
color: cssVar('textSecondaryColor'),
});

export const time = style({
display: 'flex',
alignItems: 'center',
padding: '6px 8px',
flexGrow: 1,
fontSize: cssVar('fontSm'),
});

export const rowCell = style({
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: 4,
});

export const container = style({
display: 'flex',
flexDirection: 'column',
marginTop: 20,
marginBottom: 4,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { i18nTime, useI18n } from '@affine/i18n';
import { DateTimeIcon, HistoryIcon } from '@blocksuite/icons/rc';
import {
DocService,
useLiveData,
useServices,
WorkspaceService,
} from '@toeverything/infra';
import { useDebouncedValue } from 'foxact/use-debounced-value';
import { type ReactNode, useContext, useMemo } from 'react';

import { managerContext } from '../common';
import * as styles from './time-row.css';

const RowComponent = ({
name,
icon,
time,
}: {
name: string;
icon: ReactNode;
time?: string | null;
}) => {
return (
<div className={styles.rowCell}>
<div className={styles.rowNameContainer}>
<div className={styles.icon}>{icon}</div>
<span className={styles.rowName}>{name}</span>
</div>
<div className={styles.time}>{time ? time : 'unknown'}</div>
</div>
);
};

export const TimeRow = () => {
const t = useI18n();
const manager = useContext(managerContext);
const { docService, workspaceService } = useServices({
DocService,
WorkspaceService,
});
const { syncing, retrying, serverClock } = useLiveData(
workspaceService.workspace.engine.doc.docState$(docService.doc.id)
);
const timestampElement = useMemo(() => {
const localizedCreateTime = manager.createDate
? i18nTime(manager.createDate)
: null;

return (
<>
<RowComponent
icon={<DateTimeIcon />}
name={t['Created']()}
time={
manager.createDate
? i18nTime(manager.createDate, {
relative: {
max: [1, 'day'],
accuracy: 'minute',
},
absolute: {
accuracy: 'day',
},
})
: localizedCreateTime
}
/>
{serverClock ? (
<RowComponent
icon={<HistoryIcon />}
name={t[!syncing && !retrying ? 'Updated' : 'com.affine.syncing']()}
time={
!syncing && !retrying
? i18nTime(serverClock, {
relative: {
max: [1, 'day'],
accuracy: 'minute',
},
absolute: {
accuracy: 'day',
},
})
: null
}
/>
) : manager.updatedDate ? (
<RowComponent
icon={<HistoryIcon />}
name={t['Updated']()}
time={i18nTime(manager.updatedDate)}
/>
) : null}
</>
);
}, [
manager.createDate,
manager.updatedDate,
retrying,
serverClock,
syncing,
t,
]);

const dTimestampElement = useDebouncedValue(timestampElement, 500);

return <div className={styles.container}>{dTimestampElement}</div>;
};
Loading

0 comments on commit ae8d9f1

Please sign in to comment.