feat(core): info modal should render backlinks with preview (#9387)
fix AF-2033
This commit is contained in:
@@ -89,6 +89,14 @@ globalStyle(`${link} .affine-reference-title`, {
|
|||||||
borderBottom: 'none',
|
borderBottom: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
globalStyle(`${link} svg`, {
|
||||||
|
color: cssVarV2('icon/secondary'),
|
||||||
|
});
|
||||||
|
|
||||||
|
globalStyle(`${link}:hover svg`, {
|
||||||
|
color: cssVarV2('icon/primary'),
|
||||||
|
});
|
||||||
|
|
||||||
export const linkPreviewContainer = style({
|
export const linkPreviewContainer = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
|||||||
@@ -160,29 +160,13 @@ const usePreviewExtensions = () => {
|
|||||||
return [extensions, portals] as const;
|
return [extensions, portals] as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BiDirectionalLinkPanel = () => {
|
const useBacklinkGroups = () => {
|
||||||
const { docLinksService, workspaceService, docService } = useServices({
|
const { docLinksService } = useServices({
|
||||||
DocLinksService,
|
DocLinksService,
|
||||||
WorkspaceService,
|
|
||||||
DocService,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [extensions, portals] = usePreviewExtensions();
|
|
||||||
const t = useI18n();
|
|
||||||
|
|
||||||
const [show, setShow] = useBiDirectionalLinkPanelCollapseState(
|
|
||||||
docService.doc.id
|
|
||||||
);
|
|
||||||
|
|
||||||
const links = useLiveData(
|
|
||||||
show ? docLinksService.links.links$ : new LiveData([] as Link[])
|
|
||||||
);
|
|
||||||
const backlinkGroups = useLiveData(
|
const backlinkGroups = useLiveData(
|
||||||
LiveData.computed(get => {
|
LiveData.computed(get => {
|
||||||
if (!show) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const links = get(docLinksService.backlinks.backlinks$);
|
const links = get(docLinksService.backlinks.backlinks$);
|
||||||
|
|
||||||
// group by docId
|
// group by docId
|
||||||
@@ -202,17 +186,17 @@ export const BiDirectionalLinkPanel = () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const backlinkCount = useMemo(() => {
|
return backlinkGroups;
|
||||||
return backlinkGroups.reduce((acc, link) => acc + link.links.length, 0);
|
};
|
||||||
}, [backlinkGroups]);
|
|
||||||
|
|
||||||
const handleClickShow = useCallback(() => {
|
export const BacklinkGroups = () => {
|
||||||
setShow(!show);
|
const [extensions, portals] = usePreviewExtensions();
|
||||||
track.doc.biDirectionalLinksPanel.$.toggle({
|
const { workspaceService, docService } = useServices({
|
||||||
type: show ? 'collapse' : 'expand',
|
WorkspaceService,
|
||||||
|
DocService,
|
||||||
});
|
});
|
||||||
}, [show, setShow]);
|
|
||||||
|
|
||||||
|
const backlinkGroups = useBacklinkGroups();
|
||||||
const textRendererOptions = useMemo(() => {
|
const textRendererOptions = useMemo(() => {
|
||||||
const docLinkBaseURLMiddleware: JobMiddleware = ({ adapterConfigs }) => {
|
const docLinkBaseURLMiddleware: JobMiddleware = ({ adapterConfigs }) => {
|
||||||
adapterConfigs.set(
|
adapterConfigs.set(
|
||||||
@@ -229,31 +213,7 @@ export const BiDirectionalLinkPanel = () => {
|
|||||||
}, [extensions, workspaceService.workspace.id]);
|
}, [extensions, workspaceService.workspace.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
|
||||||
{!show && (
|
|
||||||
<div className={styles.dividerContainer}>
|
|
||||||
<div className={styles.divider}></div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className={styles.titleLine}>
|
|
||||||
<div className={styles.title}>Bi-Directional Links</div>
|
|
||||||
<Button className={styles.showButton} onClick={handleClickShow}>
|
|
||||||
{show
|
|
||||||
? t['com.affine.editor.bi-directional-link-panel.hide']()
|
|
||||||
: t['com.affine.editor.bi-directional-link-panel.show']()}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{show && (
|
|
||||||
<>
|
<>
|
||||||
<div className={styles.dividerContainer}>
|
|
||||||
<div className={styles.divider}></div>
|
|
||||||
</div>
|
|
||||||
<div className={styles.linksContainer}>
|
|
||||||
<div className={styles.linksTitles}>
|
|
||||||
{t['com.affine.page-properties.backlinks']()} · {backlinkCount}
|
|
||||||
</div>
|
|
||||||
{backlinkGroups.map(linkGroup => (
|
{backlinkGroups.map(linkGroup => (
|
||||||
<CollapsibleSection
|
<CollapsibleSection
|
||||||
key={linkGroup.docId}
|
key={linkGroup.docId}
|
||||||
@@ -337,6 +297,70 @@ export const BiDirectionalLinkPanel = () => {
|
|||||||
</div>
|
</div>
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
))}
|
))}
|
||||||
|
<>
|
||||||
|
{portals.map(p => (
|
||||||
|
<Fragment key={p.id}>{p.portal}</Fragment>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BiDirectionalLinkPanel = () => {
|
||||||
|
const { docLinksService, docService } = useServices({
|
||||||
|
DocLinksService,
|
||||||
|
DocService,
|
||||||
|
});
|
||||||
|
|
||||||
|
const t = useI18n();
|
||||||
|
|
||||||
|
const [show, setShow] = useBiDirectionalLinkPanelCollapseState(
|
||||||
|
docService.doc.id
|
||||||
|
);
|
||||||
|
|
||||||
|
const links = useLiveData(
|
||||||
|
show ? docLinksService.links.links$ : new LiveData([] as Link[])
|
||||||
|
);
|
||||||
|
|
||||||
|
const backlinkGroups = useBacklinkGroups();
|
||||||
|
|
||||||
|
const backlinkCount = useMemo(() => {
|
||||||
|
return backlinkGroups.reduce((acc, link) => acc + link.links.length, 0);
|
||||||
|
}, [backlinkGroups]);
|
||||||
|
|
||||||
|
const handleClickShow = useCallback(() => {
|
||||||
|
setShow(!show);
|
||||||
|
track.doc.biDirectionalLinksPanel.$.toggle({
|
||||||
|
type: show ? 'collapse' : 'expand',
|
||||||
|
});
|
||||||
|
}, [show, setShow]);
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
{!show && (
|
||||||
|
<div className={styles.dividerContainer}>
|
||||||
|
<div className={styles.divider}></div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={styles.titleLine}>
|
||||||
|
<div className={styles.title}>Bi-Directional Links</div>
|
||||||
|
<Button className={styles.showButton} onClick={handleClickShow}>
|
||||||
|
{show
|
||||||
|
? t['com.affine.editor.bi-directional-link-panel.hide']()
|
||||||
|
: t['com.affine.editor.bi-directional-link-panel.show']()}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{show && (
|
||||||
|
<>
|
||||||
|
<div className={styles.dividerContainer}>
|
||||||
|
<div className={styles.divider}></div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.linksContainer}>
|
||||||
|
<div className={styles.linksTitles}>
|
||||||
|
{t['com.affine.page-properties.backlinks']()} · {backlinkCount}
|
||||||
|
</div>
|
||||||
|
<BacklinkGroups />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.linksContainer}>
|
<div className={styles.linksContainer}>
|
||||||
<div className={styles.linksTitles}>
|
<div className={styles.linksTitles}>
|
||||||
@@ -354,13 +378,6 @@ export const BiDirectionalLinkPanel = () => {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{
|
|
||||||
<>
|
|
||||||
{portals.map(p => (
|
|
||||||
<Fragment key={p.id}>{p.portal}</Fragment>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
PropertyCollapsibleContent,
|
PropertyCollapsibleContent,
|
||||||
PropertyCollapsibleSection,
|
PropertyCollapsibleSection,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
|
import { BacklinkGroups } from '@affine/core/components/blocksuite/block-suite-editor/bi-directional-link-panel';
|
||||||
import { CreatePropertyMenuItems } from '@affine/core/components/doc-properties/menu/create-doc-property';
|
import { CreatePropertyMenuItems } from '@affine/core/components/doc-properties/menu/create-doc-property';
|
||||||
import { DocPropertyRow } from '@affine/core/components/doc-properties/table';
|
import { DocPropertyRow } from '@affine/core/components/doc-properties/table';
|
||||||
import type { DocCustomPropertyInfo } from '@affine/core/modules/db';
|
import type { DocCustomPropertyInfo } from '@affine/core/modules/db';
|
||||||
@@ -162,7 +163,8 @@ export const InfoTable = ({
|
|||||||
{backlinks && backlinks.length > 0 ? (
|
{backlinks && backlinks.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<LinksRow
|
<LinksRow
|
||||||
references={backlinks}
|
count={backlinks.length}
|
||||||
|
references={<BacklinkGroups />}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
label={t['com.affine.page-properties.backlinks']()}
|
label={t['com.affine.page-properties.backlinks']()}
|
||||||
/>
|
/>
|
||||||
@@ -172,6 +174,7 @@ export const InfoTable = ({
|
|||||||
{links && links.length > 0 ? (
|
{links && links.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<LinksRow
|
<LinksRow
|
||||||
|
count={links.length}
|
||||||
references={links}
|
references={links}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
label={t['com.affine.page-properties.outgoing-links']()}
|
label={t['com.affine.page-properties.outgoing-links']()}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { globalStyle, style } from '@vanilla-extract/css';
|
|||||||
export const wrapper = style({
|
export const wrapper = style({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
color: cssVar('textPrimaryColor'),
|
color: cssVarV2('text/primary'),
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -16,10 +16,15 @@ export const wrapper = style({
|
|||||||
});
|
});
|
||||||
|
|
||||||
globalStyle(`${wrapper} svg`, {
|
globalStyle(`${wrapper} svg`, {
|
||||||
color: cssVar('iconSecondary'),
|
color: cssVarV2('icon/secondary'),
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
transform: 'none',
|
transform: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
globalStyle(`${wrapper}:hover svg`, {
|
||||||
|
color: cssVarV2('icon/primary'),
|
||||||
|
});
|
||||||
|
|
||||||
globalStyle(`${wrapper} span`, {
|
globalStyle(`${wrapper} span`, {
|
||||||
fontSize: cssVar('fontSm'),
|
fontSize: cssVar('fontSm'),
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
|
|||||||
@@ -1,27 +1,30 @@
|
|||||||
import { PropertyCollapsibleSection } from '@affine/component';
|
import { PropertyCollapsibleSection } from '@affine/component';
|
||||||
import { AffinePageReference } from '@affine/core/components/affine/reference-link';
|
import { AffinePageReference } from '@affine/core/components/affine/reference-link';
|
||||||
import type { Backlink, Link } from '@affine/core/modules/doc-link';
|
import type { Backlink, Link } from '@affine/core/modules/doc-link';
|
||||||
import type { MouseEvent } from 'react';
|
import type { MouseEvent, ReactNode } from 'react';
|
||||||
|
|
||||||
import * as styles from './links-row.css';
|
import * as styles from './links-row.css';
|
||||||
|
|
||||||
export const LinksRow = ({
|
export const LinksRow = ({
|
||||||
references,
|
references,
|
||||||
|
count,
|
||||||
label,
|
label,
|
||||||
className,
|
className,
|
||||||
onClick,
|
onClick,
|
||||||
}: {
|
}: {
|
||||||
references: Backlink[] | Link[];
|
references: Backlink[] | Link[] | ReactNode;
|
||||||
|
count: number;
|
||||||
label: string;
|
label: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: (e: MouseEvent) => void;
|
onClick?: (e: MouseEvent) => void;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<PropertyCollapsibleSection
|
<PropertyCollapsibleSection
|
||||||
title={`${label} · ${references.length}`}
|
title={`${label} · ${count}`}
|
||||||
className={className}
|
className={className}
|
||||||
>
|
>
|
||||||
{references.map(link => (
|
{Array.isArray(references)
|
||||||
|
? references.map(link => (
|
||||||
<AffinePageReference
|
<AffinePageReference
|
||||||
key={link.docId}
|
key={link.docId}
|
||||||
pageId={link.docId}
|
pageId={link.docId}
|
||||||
@@ -29,7 +32,8 @@ export const LinksRow = ({
|
|||||||
className={styles.wrapper}
|
className={styles.wrapper}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
/>
|
/>
|
||||||
))}
|
))
|
||||||
|
: references}
|
||||||
</PropertyCollapsibleSection>
|
</PropertyCollapsibleSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ export const DocInfoSheet = ({
|
|||||||
<LinksRow
|
<LinksRow
|
||||||
className={styles.linksRow}
|
className={styles.linksRow}
|
||||||
references={backlinks}
|
references={backlinks}
|
||||||
|
count={backlinks.length}
|
||||||
label={t['com.affine.page-properties.backlinks']()}
|
label={t['com.affine.page-properties.backlinks']()}
|
||||||
/>
|
/>
|
||||||
<Divider size="thinner" />
|
<Divider size="thinner" />
|
||||||
@@ -141,6 +142,7 @@ export const DocInfoSheet = ({
|
|||||||
<LinksRow
|
<LinksRow
|
||||||
className={styles.linksRow}
|
className={styles.linksRow}
|
||||||
references={links}
|
references={links}
|
||||||
|
count={links.length}
|
||||||
label={t['com.affine.page-properties.outgoing-links']()}
|
label={t['com.affine.page-properties.outgoing-links']()}
|
||||||
/>
|
/>
|
||||||
<Divider size="thinner" />
|
<Divider size="thinner" />
|
||||||
|
|||||||
Reference in New Issue
Block a user