mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-18 10:36:22 +08:00
df910d7013
[BS-2369](https://linear.app/affine-design/issue/BS-2369/新增-affinetextattribute-footnote) [BS-2370](https://linear.app/affine-design/issue/BS-2370/支持-footnote-自定义渲染行内内容) [BS-2372](https://linear.app/affine-design/issue/BS-2372/提供-footnoteconfigextension) [BS-2375](https://linear.app/affine-design/issue/BS-2375/footnote-自定义渲染-popup) ### Add new AffineTextAttribute: footnote ``` /** * FootNote is used to reference a doc, attachment or url. */ export interface AffineTextAttributes { ... footnote?: { label: string; // label of the footnote reference: { type: 'doc' | 'attachment' | 'url'; // type of reference docId?: string; // the id of the reference doc url?: string; // the url of the reference network resource blobId?: string; // the id of the reference attachment fileName?: string; // the name of the reference attachment fileType?: string; // the type of the reference attachment } } | null } ``` ### FootNoteNodeConfigProvider Extension #### FootNoteNodeConfig Type Definition ``` type FootNoteNodeRenderer = ( footnote: FootNote, std: BlockStdScope ) => TemplateResult<1>; type FootNotePopupRenderer = ( footnote: FootNote, std: BlockStdScope, abortController: AbortController ) => TemplateResult<1>; export interface FootNoteNodeConfig { customNodeRenderer?: FootNoteNodeRenderer; customPopupRenderer?: FootNotePopupRenderer; interactive?: boolean; hidePopup?: boolean; } ``` #### FootNoteNodeConfigProvider Class ``` export class FootNoteNodeConfigProvider { private _customNodeRenderer?: FootNoteNodeRenderer; private _customPopupRenderer?: FootNotePopupRenderer; private _hidePopup: boolean; private _interactive: boolean; get customNodeRenderer() { return this._customNodeRenderer; } get customPopupRenderer() { return this._customPopupRenderer; } get doc() { return this.std.store; } get hidePopup() { return this._hidePopup; } get interactive() { return this._interactive; } constructor( config: FootNoteNodeConfig, readonly std: BlockStdScope ) { this._customNodeRenderer = config.customNodeRenderer; this._customPopupRenderer = config.customPopupRenderer; this._hidePopup = config.hidePopup ?? false; this._interactive = config.interactive ?? true; } setCustomNodeRenderer(renderer: FootNoteNodeRenderer) { this._customNodeRenderer = renderer; } setCustomPopupRenderer(renderer: FootNotePopupRenderer) { this._customPopupRenderer = renderer; } setHidePopup(hidePopup: boolean) { this._hidePopup = hidePopup; } setInteractive(interactive: boolean) { this._interactive = interactive; } } ``` #### FootNoteNodeConfigProvider Extension ``` export const FootNoteNodeConfigIdentifier = createIdentifier<FootNoteNodeConfigProvider>('AffineFootNoteNodeConfig'); export function FootNoteNodeConfigExtension( config: FootNoteNodeConfig ): ExtensionType { return { setup: di => { di.addImpl( FootNoteNodeConfigIdentifier, provider => new FootNoteNodeConfigProvider(config, provider.get(StdIdentifier)) ); }, }; } ``` The footnote node can be extended by this extension. ### FootnoteInlineSpec ``` export const FootNoteInlineSpecExtension = InlineSpecExtension( 'footnote', provider => { const std = provider.get(StdIdentifier); const config = provider.getOptional(FootNoteNodeConfigIdentifier) ?? undefined; return { name: 'footnote', schema: FootNoteSchema.optional().nullable().catch(undefined), match: delta => { return !!delta.attributes?.footnote; }, renderer: ({ delta }) => { return html`<affine-footnote-node .delta=${delta} .std=${std} .config=${config} ></affine-footnote-node>`; }, embed: true, }; } ); ```
78 lines
2.1 KiB
TypeScript
78 lines
2.1 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
export type DocMode = 'edgeless' | 'page';
|
|
|
|
export const DocModes = ['edgeless', 'page'] as const;
|
|
|
|
export type FootNoteReferenceType = 'doc' | 'attachment' | 'url';
|
|
|
|
export const FootNoteReferenceTypes = ['doc', 'attachment', 'url'] as const;
|
|
|
|
/**
|
|
* Custom title and description information.
|
|
*
|
|
* Supports the following blocks:
|
|
*
|
|
* 1. Inline View: `AffineReference` - title
|
|
* 2. Card View: `EmbedLinkedDocBlock` - title & description
|
|
* 3. Embed View: `EmbedSyncedDocBlock` - title
|
|
*/
|
|
export const AliasInfoSchema = z
|
|
.object({
|
|
title: z.string(),
|
|
description: z.string(),
|
|
})
|
|
.partial();
|
|
|
|
export type AliasInfo = z.infer<typeof AliasInfoSchema>;
|
|
|
|
export const ReferenceParamsSchema = z
|
|
.object({
|
|
mode: z.enum(DocModes),
|
|
blockIds: z.string().array(),
|
|
elementIds: z.string().array(),
|
|
databaseId: z.string().optional(),
|
|
databaseRowId: z.string().optional(),
|
|
})
|
|
.partial();
|
|
|
|
export type ReferenceParams = z.infer<typeof ReferenceParamsSchema>;
|
|
|
|
export const ReferenceInfoSchema = z
|
|
.object({
|
|
pageId: z.string(),
|
|
params: ReferenceParamsSchema.optional(),
|
|
})
|
|
.merge(AliasInfoSchema);
|
|
|
|
export type ReferenceInfo = z.infer<typeof ReferenceInfoSchema>;
|
|
|
|
/**
|
|
* FootNoteReferenceParamsSchema is used to define the parameters for a footnote reference.
|
|
* It supports the following types:
|
|
* 1. docId: string - the id of the doc
|
|
* 2. blobId: string - the id of the attachment
|
|
* 3. url: string - the url of the reference
|
|
* 4. fileName: string - the name of the attachment
|
|
* 5. fileType: string - the type of the attachment
|
|
*/
|
|
export const FootNoteReferenceParamsSchema = z.object({
|
|
type: z.enum(FootNoteReferenceTypes),
|
|
docId: z.string().optional(),
|
|
blobId: z.string().optional(),
|
|
fileName: z.string().optional(),
|
|
fileType: z.string().optional(),
|
|
url: z.string().optional(),
|
|
});
|
|
|
|
export type FootNoteReferenceParams = z.infer<
|
|
typeof FootNoteReferenceParamsSchema
|
|
>;
|
|
|
|
export const FootNoteSchema = z.object({
|
|
label: z.string(),
|
|
reference: FootNoteReferenceParamsSchema,
|
|
});
|
|
|
|
export type FootNote = z.infer<typeof FootNoteSchema>;
|