mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 10:22:55 +08:00
fix(editor): add reference after duplicate edgeless embed doc as note (#11877)
- fix(editor): add reference in the copied note of embed doc - refactor(editor): add generics parameter `TextAttributes` into `Text`
This commit is contained in:
@@ -7,11 +7,13 @@ import {
|
|||||||
NoteBlockModel,
|
NoteBlockModel,
|
||||||
NoteDisplayMode,
|
NoteDisplayMode,
|
||||||
type NoteProps,
|
type NoteProps,
|
||||||
|
type ParagraphProps,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
draftSelectedModelsCommand,
|
draftSelectedModelsCommand,
|
||||||
duplicateSelectedModelsCommand,
|
duplicateSelectedModelsCommand,
|
||||||
} from '@blocksuite/affine-shared/commands';
|
} from '@blocksuite/affine-shared/commands';
|
||||||
|
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
EditorSettingProvider,
|
EditorSettingProvider,
|
||||||
@@ -24,6 +26,7 @@ import {
|
|||||||
type ToolbarModuleConfig,
|
type ToolbarModuleConfig,
|
||||||
ToolbarModuleExtension,
|
ToolbarModuleExtension,
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
|
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
|
||||||
import { getBlockProps, matchModels } from '@blocksuite/affine-shared/utils';
|
import { getBlockProps, matchModels } from '@blocksuite/affine-shared/utils';
|
||||||
import { Bound } from '@blocksuite/global/gfx';
|
import { Bound } from '@blocksuite/global/gfx';
|
||||||
import {
|
import {
|
||||||
@@ -36,7 +39,12 @@ import {
|
|||||||
OpenInNewIcon,
|
OpenInNewIcon,
|
||||||
} from '@blocksuite/icons/lit';
|
} from '@blocksuite/icons/lit';
|
||||||
import { BlockFlavourIdentifier, isGfxBlockComponent } from '@blocksuite/std';
|
import { BlockFlavourIdentifier, isGfxBlockComponent } from '@blocksuite/std';
|
||||||
import { type BlockModel, type ExtensionType, Slice } from '@blocksuite/store';
|
import {
|
||||||
|
type BlockModel,
|
||||||
|
type ExtensionType,
|
||||||
|
Slice,
|
||||||
|
Text,
|
||||||
|
} from '@blocksuite/store';
|
||||||
import { computed, signal } from '@preact/signals-core';
|
import { computed, signal } from '@preact/signals-core';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||||
@@ -372,6 +380,24 @@ const builtinSurfaceToolbarConfig = {
|
|||||||
ctx.store.root
|
ctx.store.root
|
||||||
);
|
);
|
||||||
|
|
||||||
|
std.store.addBlock(
|
||||||
|
'affine:paragraph',
|
||||||
|
{
|
||||||
|
text: new Text<AffineTextAttributes>([
|
||||||
|
{
|
||||||
|
insert: REFERENCE_NODE,
|
||||||
|
attributes: {
|
||||||
|
reference: {
|
||||||
|
type: 'LinkedPage',
|
||||||
|
pageId: syncedDocModel.props.pageId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
} satisfies Partial<ParagraphProps>,
|
||||||
|
noteId
|
||||||
|
);
|
||||||
|
|
||||||
await std.clipboard.duplicateSlice(
|
await std.clipboard.duplicateSlice(
|
||||||
Slice.fromModels(std.store, children),
|
Slice.fromModels(std.store, children),
|
||||||
std.store,
|
std.store,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
[BlockSuite API Documentation](../../../README.md) / [@blocksuite/store](../README.md) / Text
|
[BlockSuite API Documentation](../../../README.md) / [@blocksuite/store](../README.md) / Text
|
||||||
|
|
||||||
# Class: Text
|
# Class: Text\<TextAttributes\>
|
||||||
|
|
||||||
Text is an abstraction of Y.Text.
|
Text is an abstraction of Y.Text.
|
||||||
It provides useful methods to manipulate the text content.
|
It provides useful methods to manipulate the text content.
|
||||||
@@ -22,11 +22,17 @@ text.split(7, 1);
|
|||||||
|
|
||||||
Text [delta](https://docs.yjs.dev/api/delta-format) is a format from Y.js.
|
Text [delta](https://docs.yjs.dev/api/delta-format) is a format from Y.js.
|
||||||
|
|
||||||
|
## Type Parameters
|
||||||
|
|
||||||
|
### TextAttributes
|
||||||
|
|
||||||
|
`TextAttributes` *extends* `BaseTextAttributes` = `BaseTextAttributes`
|
||||||
|
|
||||||
## Constructors
|
## Constructors
|
||||||
|
|
||||||
### Constructor
|
### Constructor
|
||||||
|
|
||||||
> **new Text**(`input?`): `Text`
|
> **new Text**\<`TextAttributes`\>(`input?`): `Text`\<`TextAttributes`\>
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
@@ -34,11 +40,11 @@ Text [delta](https://docs.yjs.dev/api/delta-format) is a format from Y.js.
|
|||||||
|
|
||||||
The input can be a string, a Y.Text instance, or an array of DeltaInsert.
|
The input can be a string, a Y.Text instance, or an array of DeltaInsert.
|
||||||
|
|
||||||
`string` | `YText` | `DeltaInsert`[]
|
`string` | `YText` | `DeltaInsert`\<`TextAttributes`\>[]
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
||||||
`Text`
|
`Text`\<`TextAttributes`\>
|
||||||
|
|
||||||
## Accessors
|
## Accessors
|
||||||
|
|
||||||
@@ -97,13 +103,13 @@ Clear the text content.
|
|||||||
|
|
||||||
### clone()
|
### clone()
|
||||||
|
|
||||||
> **clone**(): `Text`
|
> **clone**(): `Text`\<\{ `bold`: `null` \| `true`; `code`: `null` \| `true`; `italic`: `null` \| `true`; `link`: `null` \| `string`; `strike`: `null` \| `true`; `underline`: `null` \| `true`; \}\>
|
||||||
|
|
||||||
Clone the text to a new Text instance.
|
Clone the text to a new Text instance.
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
||||||
`Text`
|
`Text`\<\{ `bold`: `null` \| `true`; `code`: `null` \| `true`; `italic`: `null` \| `true`; `link`: `null` \| `string`; `strike`: `null` \| `true`; `underline`: `null` \| `true`; \}\>
|
||||||
|
|
||||||
A new Text instance.
|
A new Text instance.
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
|||||||
import { type Signal, signal } from '@preact/signals-core';
|
import { type Signal, signal } from '@preact/signals-core';
|
||||||
import * as Y from 'yjs';
|
import * as Y from 'yjs';
|
||||||
|
|
||||||
|
import type { BaseTextAttributes } from './attributes';
|
||||||
import type { DeltaInsert, DeltaOperation, OnTextChange } from './types';
|
import type { DeltaInsert, DeltaOperation, OnTextChange } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,7 +23,9 @@ import type { DeltaInsert, DeltaOperation, OnTextChange } from './types';
|
|||||||
*
|
*
|
||||||
* @category Reactive
|
* @category Reactive
|
||||||
*/
|
*/
|
||||||
export class Text {
|
export class Text<
|
||||||
|
TextAttributes extends BaseTextAttributes = BaseTextAttributes,
|
||||||
|
> {
|
||||||
private readonly _deltas$: Signal<DeltaOperation[]>;
|
private readonly _deltas$: Signal<DeltaOperation[]>;
|
||||||
|
|
||||||
private readonly _length$: Signal<number>;
|
private readonly _length$: Signal<number>;
|
||||||
@@ -49,7 +52,7 @@ export class Text {
|
|||||||
/**
|
/**
|
||||||
* @param input - The input can be a string, a Y.Text instance, or an array of DeltaInsert.
|
* @param input - The input can be a string, a Y.Text instance, or an array of DeltaInsert.
|
||||||
*/
|
*/
|
||||||
constructor(input?: Y.Text | string | DeltaInsert[]) {
|
constructor(input?: Y.Text | string | DeltaInsert<TextAttributes>[]) {
|
||||||
let length = 0;
|
let length = 0;
|
||||||
if (typeof input === 'string') {
|
if (typeof input === 'string') {
|
||||||
const text = input.replaceAll('\r\n', '\n');
|
const text = input.replaceAll('\r\n', '\n');
|
||||||
@@ -417,7 +420,7 @@ export class Text {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
let tmpIndex = 0;
|
let tmpIndex = 0;
|
||||||
const rightDeltas: DeltaInsert[] = [];
|
const rightDeltas: DeltaInsert<TextAttributes>[] = [];
|
||||||
for (let i = 0; i < deltas.length; i++) {
|
for (let i = 0; i < deltas.length; i++) {
|
||||||
const insert = deltas[i].insert;
|
const insert = deltas[i].insert;
|
||||||
if (typeof insert === 'string') {
|
if (typeof insert === 'string') {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { AffineReference } from '@blocksuite/affine/inlines/reference';
|
||||||
import { expect, type Page } from '@playwright/test';
|
import { expect, type Page } from '@playwright/test';
|
||||||
|
|
||||||
import { clickView } from '../utils/actions/click.js';
|
import { clickView } from '../utils/actions/click.js';
|
||||||
@@ -95,6 +96,12 @@ test.describe('Embed synced doc in edgeless mode', () => {
|
|||||||
await edgelessEmbedSyncedBlock.click();
|
await edgelessEmbedSyncedBlock.click();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getDocIds = async (page: Page) => {
|
||||||
|
return page.evaluate(() => {
|
||||||
|
return [...window.collection.docs.keys()];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const locateToolbar = (page: Page) => {
|
const locateToolbar = (page: Page) => {
|
||||||
return page.locator(
|
return page.locator(
|
||||||
// TODO(@L-Sun): simplify this selector after that toolbar widget are disabled in preview rendering is ready
|
// TODO(@L-Sun): simplify this selector after that toolbar widget are disabled in preview rendering is ready
|
||||||
@@ -123,7 +130,7 @@ test.describe('Embed synced doc in edgeless mode', () => {
|
|||||||
await expect(embedSyncedBlock).toBeVisible();
|
await expect(embedSyncedBlock).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should using all content of embed-synced-doc to duplicate as a note', async ({
|
test('should render a reference node and all content of embed-synced-doc after click "Duplicate as note" button', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
// switch doc
|
// switch doc
|
||||||
@@ -158,13 +165,21 @@ test.describe('Embed synced doc in edgeless mode', () => {
|
|||||||
await expect(edgelessNotes).toHaveCount(2);
|
await expect(edgelessNotes).toHaveCount(2);
|
||||||
await expect(edgelessNotes.last()).toBeVisible();
|
await expect(edgelessNotes.last()).toBeVisible();
|
||||||
|
|
||||||
const paragraphs = edgelessNotes
|
const blocks = edgelessNotes.last().locator('[data-block-id]');
|
||||||
.last()
|
await expect(blocks).toHaveCount(3);
|
||||||
.locator('affine-paragraph [data-v-root="true"]');
|
const reference = blocks.nth(0).locator('affine-reference');
|
||||||
|
const paragraph1 = blocks.nth(1).locator('[data-v-text="true"]');
|
||||||
|
const paragraph2 = blocks.nth(2).locator('[data-v-text="true"]');
|
||||||
|
const refInfo = await reference.evaluate((reference: AffineReference) => {
|
||||||
|
return reference.delta.attributes?.reference;
|
||||||
|
});
|
||||||
|
|
||||||
await expect(paragraphs).toHaveCount(2);
|
expect(refInfo).toEqual({
|
||||||
await expect(paragraphs.first()).toHaveText('hello page 1');
|
type: 'LinkedPage',
|
||||||
await expect(paragraphs.last()).toHaveText('hello note');
|
pageId: (await getDocIds(page))[1],
|
||||||
|
});
|
||||||
|
await expect(paragraph1).toHaveText('hello page 1');
|
||||||
|
await expect(paragraph2).toHaveText('hello note');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should be selected and not overlay with the embed-synced-doc after duplicating as note', async ({
|
test('should be selected and not overlay with the embed-synced-doc after duplicating as note', async ({
|
||||||
|
|||||||
Reference in New Issue
Block a user