feat(editor): improve api for store, and add docs (#10941)
This commit is contained in:
47 files changed
+1212
-210
No files matched your search
@@ -82,7 +82,7 @@ describe('DatabaseManager', () => {
|
|||||||
noteBlockId
|
noteBlockId
|
||||||
);
|
);
|
||||||
|
|
||||||
const databaseModel = doc.getBlockById(
|
const databaseModel = doc.getModelById(
|
||||||
databaseBlockId
|
databaseBlockId
|
||||||
) as DatabaseBlockModel;
|
) as DatabaseBlockModel;
|
||||||
db = databaseModel;
|
db = databaseModel;
|
||||||
@@ -187,7 +187,7 @@ describe('DatabaseManager', () => {
|
|||||||
addProperty(db, 'end', column);
|
addProperty(db, 'end', column);
|
||||||
updateCell(db, modelId, cell);
|
updateCell(db, modelId, cell);
|
||||||
|
|
||||||
const model = doc.getBlockById(modelId);
|
const model = doc.getModelById(modelId);
|
||||||
|
|
||||||
expect(model).not.toBeNull();
|
expect(model).not.toBeNull();
|
||||||
|
|
||||||
|
|||||||
@@ -466,7 +466,7 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rowMove(rowId: string, position: InsertToPosition): void {
|
rowMove(rowId: string, position: InsertToPosition): void {
|
||||||
const model = this.doc.getBlockById(rowId);
|
const model = this.doc.getModelById(rowId);
|
||||||
if (model) {
|
if (model) {
|
||||||
const index = insertPositionToIndex(position, this._model.children);
|
const index = insertPositionToIndex(position, this._model.children);
|
||||||
const target = this._model.children[index];
|
const target = this._model.children[index];
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export async function uploadBlobForImage(
|
|||||||
} finally {
|
} finally {
|
||||||
setImageUploaded(blockId);
|
setImageUploaded(blockId);
|
||||||
|
|
||||||
const imageModel = doc.getBlockById(blockId) as ImageBlockModel | null;
|
const imageModel = doc.getModelById(blockId) as ImageBlockModel | null;
|
||||||
if (sourceId && imageModel) {
|
if (sourceId && imageModel) {
|
||||||
const props: Partial<ImageBlockProps> = {
|
const props: Partial<ImageBlockProps> = {
|
||||||
sourceId,
|
sourceId,
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export const updateBlockType: Command<
|
|||||||
if (flavour !== 'affine:code') return;
|
if (flavour !== 'affine:code') return;
|
||||||
const id = mergeToCodeModel(blockModels);
|
const id = mergeToCodeModel(blockModels);
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
const model = doc.getBlockById(id);
|
const model = doc.getModelById(id);
|
||||||
if (!model) return;
|
if (!model) return;
|
||||||
asyncSetInlineRange(host, model, {
|
asyncSetInlineRange(host, model, {
|
||||||
index: model.text?.length ?? 0,
|
index: model.text?.length ?? 0,
|
||||||
@@ -115,7 +115,7 @@ export const updateBlockType: Command<
|
|||||||
nextSiblingId = doc.addBlock('affine:paragraph', {}, parent);
|
nextSiblingId = doc.addBlock('affine:paragraph', {}, parent);
|
||||||
}
|
}
|
||||||
focusTextModel(host.std, nextSiblingId);
|
focusTextModel(host.std, nextSiblingId);
|
||||||
const newModel = doc.getBlockById(id);
|
const newModel = doc.getModelById(id);
|
||||||
if (!newModel) {
|
if (!newModel) {
|
||||||
return next({ updatedBlocks: [] });
|
return next({ updatedBlocks: [] });
|
||||||
}
|
}
|
||||||
@@ -217,7 +217,7 @@ export const updateBlockType: Command<
|
|||||||
if (!newId) {
|
if (!newId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newModel = doc.getBlockById(newId);
|
const newModel = doc.getModelById(newId);
|
||||||
if (newModel) {
|
if (newModel) {
|
||||||
newModels.push(newModel);
|
newModels.push(newModel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1091,7 +1091,7 @@ export class EdgelessClipboardController extends PageClipboard {
|
|||||||
const newSelected = [
|
const newSelected = [
|
||||||
...canvasElementIds,
|
...canvasElementIds,
|
||||||
...blockIds.filter(id => {
|
...blockIds.filter(id => {
|
||||||
return isTopLevelBlock(this.doc.getBlockById(id));
|
return isTopLevelBlock(this.doc.getModelById(id));
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -380,7 +380,7 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
|
|||||||
this.edgeless
|
this.edgeless
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const model = doc.getBlockById(id);
|
const model = doc.getModelById(id);
|
||||||
if (!model) {
|
if (!model) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ export class NoteSlicer extends WidgetComponent<
|
|||||||
doc.root?.id
|
doc.root?.id
|
||||||
);
|
);
|
||||||
|
|
||||||
doc.moveBlocks(resetBlocks, doc.getBlockById(newNoteId) as NoteBlockModel);
|
doc.moveBlocks(resetBlocks, doc.getModelById(newNoteId) as NoteBlockModel);
|
||||||
|
|
||||||
this._activeSlicerIndex = 0;
|
this._activeSlicerIndex = 0;
|
||||||
this._selection.set({
|
this._selection.set({
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ export class TemplateJob {
|
|||||||
if (isMergeBlock) {
|
if (isMergeBlock) {
|
||||||
this._mergeProps(
|
this._mergeProps(
|
||||||
json,
|
json,
|
||||||
this.model.doc.getBlockById(
|
this.model.doc.getModelById(
|
||||||
this._getMergeBlockId(json)
|
this._getMergeBlockId(json)
|
||||||
) as BlockModel
|
) as BlockModel
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ export class PageRootBlockComponent extends BlockComponent<
|
|||||||
const { doc } = this;
|
const { doc } = this;
|
||||||
|
|
||||||
const noteId = doc.addBlock('affine:note', {}, doc.root?.id);
|
const noteId = doc.addBlock('affine:note', {}, doc.root?.id);
|
||||||
return doc.getBlockById(noteId) as NoteBlockModel;
|
return doc.getModelById(noteId) as NoteBlockModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getDefaultNoteBlock() {
|
private _getDefaultNoteBlock() {
|
||||||
@@ -262,7 +262,7 @@ export class PageRootBlockComponent extends BlockComponent<
|
|||||||
);
|
);
|
||||||
if (!sel) return;
|
if (!sel) return;
|
||||||
let model: BlockModel | null = null;
|
let model: BlockModel | null = null;
|
||||||
let current = this.doc.getBlockById(sel.blockId);
|
let current = this.doc.getModelById(sel.blockId);
|
||||||
while (current && !model) {
|
while (current && !model) {
|
||||||
if (current.flavour === 'affine:note') {
|
if (current.flavour === 'affine:note') {
|
||||||
model = current;
|
model = current;
|
||||||
@@ -280,7 +280,7 @@ export class PageRootBlockComponent extends BlockComponent<
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const notes = this.doc.getBlockByFlavour('affine:note');
|
const notes = this.doc.getModelsByFlavour('affine:note');
|
||||||
const index = notes.indexOf(prevNote);
|
const index = notes.indexOf(prevNote);
|
||||||
if (index !== 0) return;
|
if (index !== 0) return;
|
||||||
|
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
|
|||||||
flavour: 'affine:paragraph',
|
flavour: 'affine:paragraph',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const model = this.doc.getBlockById(paragraphId);
|
const model = this.doc.getModelById(paragraphId);
|
||||||
if (!model) return;
|
if (!model) return;
|
||||||
|
|
||||||
requestConnectedFrame(() => {
|
requestConnectedFrame(() => {
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export class EdgelessCRUDExtension extends Extension {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const block = this.std.store.getBlockById(id);
|
const block = this.std.store.getModelById(id);
|
||||||
if (block) {
|
if (block) {
|
||||||
const key = getLastPropsKey(block.flavour, {
|
const key = getLastPropsKey(block.flavour, {
|
||||||
...block.yBlock.toJSON(),
|
...block.yBlock.toJSON(),
|
||||||
@@ -145,7 +145,7 @@ export class EdgelessCRUDExtension extends Extension {
|
|||||||
if (!surface) {
|
if (!surface) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const el = surface.getElementById(id) ?? this.std.store.getBlockById(id);
|
const el = surface.getElementById(id) ?? this.std.store.getModelById(id);
|
||||||
return el as GfxModel | null;
|
return el as GfxModel | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -602,7 +602,7 @@ function getNotesInFrameBound(
|
|||||||
): NoteBlockModel[] {
|
): NoteBlockModel[] {
|
||||||
const bound = Bound.deserialize(frame.xywh);
|
const bound = Bound.deserialize(frame.xywh);
|
||||||
|
|
||||||
return (doc.getBlockByFlavour('affine:note') as NoteBlockModel[]).filter(
|
return (doc.getModelsByFlavour('affine:note') as NoteBlockModel[]).filter(
|
||||||
ele => {
|
ele => {
|
||||||
const xywh = Bound.deserialize(ele.xywh);
|
const xywh = Bound.deserialize(ele.xywh);
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export function mindmap(
|
|||||||
const { connector, outdated } = result;
|
const { connector, outdated } = result;
|
||||||
const elementGetter = (id: string) =>
|
const elementGetter = (id: string) =>
|
||||||
model.surface.getElementById(id) ??
|
model.surface.getElementById(id) ??
|
||||||
(model.surface.doc.getBlockById(id) as GfxModel);
|
(model.surface.doc.getModelById(id) as GfxModel);
|
||||||
|
|
||||||
if (outdated) {
|
if (outdated) {
|
||||||
ConnectorPathGenerator.updatePath(connector, null, elementGetter);
|
ConnectorPathGenerator.updatePath(connector, null, elementGetter);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class SurfaceBlockService extends BlockService {
|
|||||||
const disposable = this.doc.slots.blockUpdated.subscribe(payload => {
|
const disposable = this.doc.slots.blockUpdated.subscribe(payload => {
|
||||||
if (payload.flavour === 'affine:surface') {
|
if (payload.flavour === 'affine:surface') {
|
||||||
disposable.unsubscribe();
|
disposable.unsubscribe();
|
||||||
const surface = this.doc.getBlockById(
|
const surface = this.doc.getModelById(
|
||||||
payload.id
|
payload.id
|
||||||
) as SurfaceBlockModel | null;
|
) as SurfaceBlockModel | null;
|
||||||
if (!surface) return;
|
if (!surface) return;
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export function addNote(
|
|||||||
noteId
|
noteId
|
||||||
);
|
);
|
||||||
if (options.collapse && height > NOTE_MIN_HEIGHT) {
|
if (options.collapse && height > NOTE_MIN_HEIGHT) {
|
||||||
const note = doc.getBlockById(noteId) as NoteBlockModel;
|
const note = doc.getModelById(noteId) as NoteBlockModel;
|
||||||
doc.updateBlock(note, () => {
|
doc.updateBlock(note, () => {
|
||||||
note.props.edgeless.collapse = true;
|
note.props.edgeless.collapse = true;
|
||||||
note.props.edgeless.collapsedHeight = height;
|
note.props.edgeless.collapsedHeight = height;
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ export const connectorWatcher: SurfaceMiddleware = (
|
|||||||
surface: SurfaceBlockModel
|
surface: SurfaceBlockModel
|
||||||
) => {
|
) => {
|
||||||
const hasElementById = (id: string) =>
|
const hasElementById = (id: string) =>
|
||||||
surface.hasElementById(id) || surface.doc.hasBlockById(id);
|
surface.hasElementById(id) || surface.doc.hasBlock(id);
|
||||||
const elementGetter = (id: string) =>
|
const elementGetter = (id: string) =>
|
||||||
surface.getElementById(id) ?? (surface.doc.getBlockById(id) as GfxModel);
|
surface.getElementById(id) ?? (surface.doc.getModelById(id) as GfxModel);
|
||||||
const updateConnectorPath = (connector: ConnectorElementModel) => {
|
const updateConnectorPath = (connector: ConnectorElementModel) => {
|
||||||
if (
|
if (
|
||||||
((connector.source?.id && hasElementById(connector.source.id)) ||
|
((connector.source?.id && hasElementById(connector.source.id)) ||
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export class FrameBlockModel
|
|||||||
for (const key of this.childIds) {
|
for (const key of this.childIds) {
|
||||||
const element =
|
const element =
|
||||||
this.surface.getElementById(key) ||
|
this.surface.getElementById(key) ||
|
||||||
(this.surface.doc.getBlockById(key) as GfxBlockElementModel);
|
(this.surface.doc.getModelById(key) as GfxBlockElementModel);
|
||||||
|
|
||||||
element && elements.push(element);
|
element && elements.push(element);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class RootBlockModel extends BlockModel<RootBlockProps> {
|
|||||||
const createdSubscription = this.created.subscribe(() => {
|
const createdSubscription = this.created.subscribe(() => {
|
||||||
createdSubscription.unsubscribe();
|
createdSubscription.unsubscribe();
|
||||||
this.doc.slots.rootAdded.subscribe(id => {
|
this.doc.slots.rootAdded.subscribe(id => {
|
||||||
const model = this.doc.getBlockById(id);
|
const model = this.doc.getModelById(id);
|
||||||
if (model instanceof RootBlockModel) {
|
if (model instanceof RootBlockModel) {
|
||||||
const newDocMeta = this.doc.workspace.meta.getDocMeta(model.doc.id);
|
const newDocMeta = this.doc.workspace.meta.getDocMeta(model.doc.id);
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export function calcDropTarget(
|
|||||||
*/
|
*/
|
||||||
allowSublist: boolean = true
|
allowSublist: boolean = true
|
||||||
): DropTarget | null {
|
): DropTarget | null {
|
||||||
const schema = model.doc.getSchemaByFlavour('affine:database');
|
const schema = model.doc.schema.get('affine:database');
|
||||||
const children = schema?.model.children ?? [];
|
const children = schema?.model.children ?? [];
|
||||||
|
|
||||||
let shouldAppendToDatabase = true;
|
let shouldAppendToDatabase = true;
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export class EdgelessWatcher {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly _showDragHandle = async () => {
|
private readonly _showDragHandle = async () => {
|
||||||
const surfaceModel = this.widget.doc.getBlockByFlavour('affine:surface');
|
const surfaceModel = this.widget.doc.getModelsByFlavour('affine:surface');
|
||||||
const surface = this.widget.std.view.getBlock(
|
const surface = this.widget.std.view.getBlock(
|
||||||
surfaceModel[0]!.id
|
surfaceModel[0]!.id
|
||||||
) as SurfaceBlockComponent;
|
) as SurfaceBlockComponent;
|
||||||
|
|||||||
@@ -10,3 +10,7 @@
|
|||||||
|
|
||||||
- [Extension](classes/Extension.md)
|
- [Extension](classes/Extension.md)
|
||||||
- [StoreExtension](classes/StoreExtension.md)
|
- [StoreExtension](classes/StoreExtension.md)
|
||||||
|
|
||||||
|
## Store
|
||||||
|
|
||||||
|
- [Store](classes/Store.md)
|
||||||
@@ -0,0 +1,786 @@
|
|||||||
|
[**@blocksuite/store**](../../../@blocksuite/store/README.md)
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[BlockSuite API Documentation](../../../README.md) / [@blocksuite/store](../README.md) / Store
|
||||||
|
|
||||||
|
# Class: Store
|
||||||
|
|
||||||
|
Core store class that manages blocks and their lifecycle in BlockSuite
|
||||||
|
|
||||||
|
## Remarks
|
||||||
|
|
||||||
|
The Store class is responsible for managing the lifecycle of blocks, handling transactions,
|
||||||
|
and maintaining the block tree structure.
|
||||||
|
A store is a piece of data created from one or a part of a Y.Doc.
|
||||||
|
|
||||||
|
## Block CRUD
|
||||||
|
|
||||||
|
### updateBlock()
|
||||||
|
|
||||||
|
> **updateBlock**: \<`T`\>(`model`, `props`) => `void`(`model`, `callback`) => `void`
|
||||||
|
|
||||||
|
Updates a block's properties or executes a callback in a transaction
|
||||||
|
|
||||||
|
#### Type Parameters
|
||||||
|
|
||||||
|
##### T
|
||||||
|
|
||||||
|
`T` *extends* `Partial`\<`BlockProps`\>
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### model
|
||||||
|
|
||||||
|
`string` | `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
##### props
|
||||||
|
|
||||||
|
`T`
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`void`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### model
|
||||||
|
|
||||||
|
`string` | `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
##### callback
|
||||||
|
|
||||||
|
() => `void`
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`void`
|
||||||
|
|
||||||
|
#### Param
|
||||||
|
|
||||||
|
The block model or block ID to update
|
||||||
|
|
||||||
|
#### Param
|
||||||
|
|
||||||
|
Either a callback function to execute or properties to update
|
||||||
|
|
||||||
|
#### Throws
|
||||||
|
|
||||||
|
When the block is not found or schema validation fails
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### blockSize
|
||||||
|
|
||||||
|
#### Get Signature
|
||||||
|
|
||||||
|
> **get** **blockSize**(): `number`
|
||||||
|
|
||||||
|
Get the number of blocks in the store
|
||||||
|
|
||||||
|
##### Returns
|
||||||
|
|
||||||
|
`number`
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### addBlock()
|
||||||
|
|
||||||
|
> **addBlock**(`flavour`, `blockProps`, `parent`?, `parentIndex`?): `string`
|
||||||
|
|
||||||
|
Creates and adds a new block to the store
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### flavour
|
||||||
|
|
||||||
|
`string`
|
||||||
|
|
||||||
|
The block's flavour (type)
|
||||||
|
|
||||||
|
##### blockProps
|
||||||
|
|
||||||
|
`Partial`\<`BlockSysProps` & `Record`\<`string`, `unknown`\> & `Omit`\<`BlockProps`, `"flavour"`\>\> = `{}`
|
||||||
|
|
||||||
|
Optional properties for the new block
|
||||||
|
|
||||||
|
##### parent?
|
||||||
|
|
||||||
|
Optional parent block or parent block ID
|
||||||
|
|
||||||
|
`null` | `string` | `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
##### parentIndex?
|
||||||
|
|
||||||
|
`number`
|
||||||
|
|
||||||
|
Optional index position in parent's children
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`string`
|
||||||
|
|
||||||
|
The ID of the newly created block
|
||||||
|
|
||||||
|
#### Throws
|
||||||
|
|
||||||
|
When store is in readonly mode
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### addBlocks()
|
||||||
|
|
||||||
|
> **addBlocks**(`blocks`, `parent`?, `parentIndex`?): `string`[]
|
||||||
|
|
||||||
|
Add multiple blocks to the store
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### blocks
|
||||||
|
|
||||||
|
`object`[]
|
||||||
|
|
||||||
|
Array of blocks to add
|
||||||
|
|
||||||
|
##### parent?
|
||||||
|
|
||||||
|
Optional parent block or parent block ID
|
||||||
|
|
||||||
|
`null` | `string` | `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
##### parentIndex?
|
||||||
|
|
||||||
|
`number`
|
||||||
|
|
||||||
|
Optional index position in parent's children
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`string`[]
|
||||||
|
|
||||||
|
Array of IDs of the newly created blocks
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### addSiblingBlocks()
|
||||||
|
|
||||||
|
> **addSiblingBlocks**(`targetModel`, `props`, `place`): `string`[]
|
||||||
|
|
||||||
|
Add sibling blocks to the store
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### targetModel
|
||||||
|
|
||||||
|
`BlockModel`
|
||||||
|
|
||||||
|
The target block model
|
||||||
|
|
||||||
|
##### props
|
||||||
|
|
||||||
|
`Partial`\<`BlockProps`\>[]
|
||||||
|
|
||||||
|
Array of block properties
|
||||||
|
|
||||||
|
##### place
|
||||||
|
|
||||||
|
Optional position to place the new blocks ('after' or 'before')
|
||||||
|
|
||||||
|
`"after"` | `"before"`
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`string`[]
|
||||||
|
|
||||||
|
Array of IDs of the newly created blocks
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### deleteBlock()
|
||||||
|
|
||||||
|
> **deleteBlock**(`model`, `options`): `void`
|
||||||
|
|
||||||
|
Delete a block from the store
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### model
|
||||||
|
|
||||||
|
The block model or block ID to delete
|
||||||
|
|
||||||
|
`string` | `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
##### options
|
||||||
|
|
||||||
|
Optional options for the deletion
|
||||||
|
|
||||||
|
###### bringChildrenTo?
|
||||||
|
|
||||||
|
`BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
Optional block model to bring children to
|
||||||
|
|
||||||
|
###### deleteChildren?
|
||||||
|
|
||||||
|
`boolean`
|
||||||
|
|
||||||
|
Optional flag to delete children
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`void`
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getAllModels()
|
||||||
|
|
||||||
|
> **getAllModels**(): `BlockModel`\<`object`\>[]
|
||||||
|
|
||||||
|
Get all models in the store
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`BlockModel`\<`object`\>[]
|
||||||
|
|
||||||
|
Array of all models
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getBlock()
|
||||||
|
|
||||||
|
> **getBlock**(`id`): `undefined` \| `Block`
|
||||||
|
|
||||||
|
Gets a block by its ID
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### id
|
||||||
|
|
||||||
|
`string`
|
||||||
|
|
||||||
|
The block's ID
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`undefined` \| `Block`
|
||||||
|
|
||||||
|
The block instance if found, undefined otherwise
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getBlock$()
|
||||||
|
|
||||||
|
> **getBlock$**(`id`): `undefined` \| `Block`
|
||||||
|
|
||||||
|
Gets a block by its ID
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### id
|
||||||
|
|
||||||
|
`string`
|
||||||
|
|
||||||
|
The block's ID
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`undefined` \| `Block`
|
||||||
|
|
||||||
|
The block instance in signal if found, undefined otherwise
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getBlocksByFlavour()
|
||||||
|
|
||||||
|
> **getBlocksByFlavour**(`blockFlavour`): `Block`[]
|
||||||
|
|
||||||
|
Gets all blocks of specified flavour(s)
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### blockFlavour
|
||||||
|
|
||||||
|
Single flavour or array of flavours to filter by
|
||||||
|
|
||||||
|
`string` | `string`[]
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`Block`[]
|
||||||
|
|
||||||
|
Array of matching blocks
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getModelById()
|
||||||
|
|
||||||
|
> **getModelById**\<`Model`\>(`id`): `null` \| `Model`
|
||||||
|
|
||||||
|
Get a model by its ID
|
||||||
|
|
||||||
|
#### Type Parameters
|
||||||
|
|
||||||
|
##### Model
|
||||||
|
|
||||||
|
`Model` *extends* `BlockModel`\<`object`\> = `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### id
|
||||||
|
|
||||||
|
`string`
|
||||||
|
|
||||||
|
The model's ID
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`null` \| `Model`
|
||||||
|
|
||||||
|
The model instance if found, null otherwise
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getModelsByFlavour()
|
||||||
|
|
||||||
|
> **getModelsByFlavour**(`blockFlavour`): `BlockModel`\<`object`\>[]
|
||||||
|
|
||||||
|
Get all models of specified flavour(s)
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### blockFlavour
|
||||||
|
|
||||||
|
Single flavour or array of flavours to filter by
|
||||||
|
|
||||||
|
`string` | `string`[]
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`BlockModel`\<`object`\>[]
|
||||||
|
|
||||||
|
Array of matching models
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getNext()
|
||||||
|
|
||||||
|
> **getNext**(`block`): `null` \| `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
Get the next sibling block of a given block
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### block
|
||||||
|
|
||||||
|
Block model or block ID to find next sibling for
|
||||||
|
|
||||||
|
`string` | `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`null` \| `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
The next sibling block model if found, null otherwise
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getNexts()
|
||||||
|
|
||||||
|
> **getNexts**(`block`): `BlockModel`\<`object`\>[]
|
||||||
|
|
||||||
|
Get all next sibling blocks of a given block
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### block
|
||||||
|
|
||||||
|
Block model or block ID to find next siblings for
|
||||||
|
|
||||||
|
`string` | `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`BlockModel`\<`object`\>[]
|
||||||
|
|
||||||
|
Array of next sibling blocks if found, empty array otherwise
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getParent()
|
||||||
|
|
||||||
|
> **getParent**(`target`): `null` \| `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
Gets the parent block of a given block
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### target
|
||||||
|
|
||||||
|
Block model or block ID to find parent for
|
||||||
|
|
||||||
|
`string` | `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`null` \| `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
The parent block model if found, null otherwise
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getPrev()
|
||||||
|
|
||||||
|
> **getPrev**(`block`): `null` \| `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
Get the previous sibling block of a given block
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### block
|
||||||
|
|
||||||
|
Block model or block ID to find previous sibling for
|
||||||
|
|
||||||
|
`string` | `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`null` \| `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
The previous sibling block model if found, null otherwise
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getPrevs()
|
||||||
|
|
||||||
|
> **getPrevs**(`block`): `BlockModel`\<`object`\>[]
|
||||||
|
|
||||||
|
Get all previous sibling blocks of a given block
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### block
|
||||||
|
|
||||||
|
Block model or block ID to find previous siblings for
|
||||||
|
|
||||||
|
`string` | `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`BlockModel`\<`object`\>[]
|
||||||
|
|
||||||
|
Array of previous sibling blocks if found, empty array otherwise
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### hasBlock()
|
||||||
|
|
||||||
|
> **hasBlock**(`id`): `boolean`
|
||||||
|
|
||||||
|
Check if a block exists by its ID
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### id
|
||||||
|
|
||||||
|
`string`
|
||||||
|
|
||||||
|
The block's ID
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`boolean`
|
||||||
|
|
||||||
|
True if the block exists, false otherwise
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### moveBlocks()
|
||||||
|
|
||||||
|
> **moveBlocks**(`blocksToMove`, `newParent`, `targetSibling`, `shouldInsertBeforeSibling`): `void`
|
||||||
|
|
||||||
|
Move blocks to a new parent block
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### blocksToMove
|
||||||
|
|
||||||
|
`BlockModel`\<`object`\>[]
|
||||||
|
|
||||||
|
Array of block models to move
|
||||||
|
|
||||||
|
##### newParent
|
||||||
|
|
||||||
|
`BlockModel`
|
||||||
|
|
||||||
|
The new parent block model
|
||||||
|
|
||||||
|
##### targetSibling
|
||||||
|
|
||||||
|
Optional target sibling block model
|
||||||
|
|
||||||
|
`null` | `BlockModel`\<`object`\>
|
||||||
|
|
||||||
|
##### shouldInsertBeforeSibling
|
||||||
|
|
||||||
|
`boolean` = `true`
|
||||||
|
|
||||||
|
Optional flag to insert before sibling
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`void`
|
||||||
|
|
||||||
|
## Store Lifecycle
|
||||||
|
|
||||||
|
### disposableGroup
|
||||||
|
|
||||||
|
> **disposableGroup**: `DisposableGroup`
|
||||||
|
|
||||||
|
Group of disposable resources managed by the store
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### slots
|
||||||
|
|
||||||
|
> `readonly` **slots**: `object` & `object`
|
||||||
|
|
||||||
|
Slots for receiving events from the store.
|
||||||
|
|
||||||
|
#### Type declaration
|
||||||
|
|
||||||
|
##### historyUpdated
|
||||||
|
|
||||||
|
> **historyUpdated**: `Subject`\<`void`\>
|
||||||
|
|
||||||
|
This fires when the doc history is updated.
|
||||||
|
|
||||||
|
##### yBlockUpdated
|
||||||
|
|
||||||
|
> **yBlockUpdated**: `Subject`\<\{ `id`: `string`; `isLocal`: `boolean`; `type`: `"add"`; \} \| \{ `id`: `string`; `isLocal`: `boolean`; `type`: `"delete"`; \}\>
|
||||||
|
|
||||||
|
This fires when the doc yBlock is updated.
|
||||||
|
|
||||||
|
#### Type declaration
|
||||||
|
|
||||||
|
##### blockUpdated
|
||||||
|
|
||||||
|
> **blockUpdated**: `Subject`\<`BlockUpdatedPayload`\>
|
||||||
|
|
||||||
|
This fires when a block is updated via API call or has just been updated from existing ydoc.
|
||||||
|
|
||||||
|
##### ready
|
||||||
|
|
||||||
|
> **ready**: `Subject`\<`void`\>
|
||||||
|
|
||||||
|
This is always triggered after `doc.load` is called.
|
||||||
|
|
||||||
|
##### rootAdded
|
||||||
|
|
||||||
|
> **rootAdded**: `Subject`\<`string`\>
|
||||||
|
|
||||||
|
This fires when the root block is added via API call or has just been initialized from existing ydoc.
|
||||||
|
useful for internal block UI components to start subscribing following up events.
|
||||||
|
Note that at this moment, the whole block tree may not be fully initialized yet.
|
||||||
|
|
||||||
|
##### rootDeleted
|
||||||
|
|
||||||
|
> **rootDeleted**: `Subject`\<`string`\>
|
||||||
|
|
||||||
|
This fires when the root block is deleted via API call or has just been removed from existing ydoc.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### dispose()
|
||||||
|
|
||||||
|
> **dispose**(): `void`
|
||||||
|
|
||||||
|
Disposes the store and releases all resources
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`void`
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### load()
|
||||||
|
|
||||||
|
> **load**(`initFn`?): `Store`
|
||||||
|
|
||||||
|
Initializes and loads the store
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### initFn?
|
||||||
|
|
||||||
|
() => `void`
|
||||||
|
|
||||||
|
Optional initialization function
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`Store`
|
||||||
|
|
||||||
|
The store instance
|
||||||
|
|
||||||
|
## Transformer
|
||||||
|
|
||||||
|
### getTransformer()
|
||||||
|
|
||||||
|
> **getTransformer**(`middlewares`): `Transformer`
|
||||||
|
|
||||||
|
Creates a new transformer instance for the store
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### middlewares
|
||||||
|
|
||||||
|
`TransformerMiddleware`[] = `[]`
|
||||||
|
|
||||||
|
Optional array of transformer middlewares
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`Transformer`
|
||||||
|
|
||||||
|
A new Transformer instance
|
||||||
|
|
||||||
|
## Other
|
||||||
|
|
||||||
|
### awarenessStore
|
||||||
|
|
||||||
|
#### Get Signature
|
||||||
|
|
||||||
|
> **get** **awarenessStore**(): `AwarenessStore`
|
||||||
|
|
||||||
|
Get the AwarenessStore instance for current store
|
||||||
|
|
||||||
|
##### Returns
|
||||||
|
|
||||||
|
`AwarenessStore`
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### blobSync
|
||||||
|
|
||||||
|
#### Get Signature
|
||||||
|
|
||||||
|
> **get** **blobSync**(): `BlobEngine`
|
||||||
|
|
||||||
|
Get the BlobEngine instance for current store.
|
||||||
|
|
||||||
|
##### Returns
|
||||||
|
|
||||||
|
`BlobEngine`
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### doc
|
||||||
|
|
||||||
|
#### Get Signature
|
||||||
|
|
||||||
|
> **get** **doc**(): `Doc`
|
||||||
|
|
||||||
|
Get the Doc instance for current store.
|
||||||
|
|
||||||
|
##### Returns
|
||||||
|
|
||||||
|
`Doc`
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### get
|
||||||
|
|
||||||
|
#### Get Signature
|
||||||
|
|
||||||
|
> **get** **get**(): \<`T`\>(`identifier`, `options`?) => `T`
|
||||||
|
|
||||||
|
Get an extension instance from the store
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const extension = store.get(SomeExtension);
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Returns
|
||||||
|
|
||||||
|
`Function`
|
||||||
|
|
||||||
|
The extension instance
|
||||||
|
|
||||||
|
###### Type Parameters
|
||||||
|
|
||||||
|
###### T
|
||||||
|
|
||||||
|
`T`
|
||||||
|
|
||||||
|
###### Parameters
|
||||||
|
|
||||||
|
###### identifier
|
||||||
|
|
||||||
|
`GeneralServiceIdentifier`\<`T`\>
|
||||||
|
|
||||||
|
###### options?
|
||||||
|
|
||||||
|
`ResolveOptions`
|
||||||
|
|
||||||
|
###### Returns
|
||||||
|
|
||||||
|
`T`
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### getOptional
|
||||||
|
|
||||||
|
#### Get Signature
|
||||||
|
|
||||||
|
> **get** **getOptional**(): \<`T`\>(`identifier`, `options`?) => `null` \| `T`
|
||||||
|
|
||||||
|
Optional get an extension instance from the store.
|
||||||
|
The major difference between `get` and `getOptional` is that `getOptional` will not throw an error if the extension is not found.
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const extension = store.getOptional(SomeExtension);
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Returns
|
||||||
|
|
||||||
|
`Function`
|
||||||
|
|
||||||
|
The extension instance
|
||||||
|
|
||||||
|
###### Type Parameters
|
||||||
|
|
||||||
|
###### T
|
||||||
|
|
||||||
|
`T`
|
||||||
|
|
||||||
|
###### Parameters
|
||||||
|
|
||||||
|
###### identifier
|
||||||
|
|
||||||
|
`GeneralServiceIdentifier`\<`T`\>
|
||||||
|
|
||||||
|
###### options?
|
||||||
|
|
||||||
|
`ResolveOptions`
|
||||||
|
|
||||||
|
###### Returns
|
||||||
|
|
||||||
|
`null` \| `T`
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### provider
|
||||||
|
|
||||||
|
#### Get Signature
|
||||||
|
|
||||||
|
> **get** **provider**(): `ServiceProvider`
|
||||||
|
|
||||||
|
Get the di provider for current store.
|
||||||
|
|
||||||
|
##### Returns
|
||||||
|
|
||||||
|
`ServiceProvider`
|
||||||
@@ -10,7 +10,8 @@
|
|||||||
"excludeProtected": true,
|
"excludeProtected": true,
|
||||||
"excludeExternals": true,
|
"excludeExternals": true,
|
||||||
"externalPattern": ["node_modules/**/*"],
|
"externalPattern": ["node_modules/**/*"],
|
||||||
"disableSources": true
|
"disableSources": true,
|
||||||
|
"categoryOrder": ["*", "Other"]
|
||||||
},
|
},
|
||||||
"readme": "none",
|
"readme": "none",
|
||||||
"plugin": ["typedoc-plugin-markdown"],
|
"plugin": ["typedoc-plugin-markdown"],
|
||||||
|
|||||||
@@ -492,7 +492,7 @@ export class LayerManager extends GfxExtension {
|
|||||||
private _reset() {
|
private _reset() {
|
||||||
const elements = (
|
const elements = (
|
||||||
this._doc
|
this._doc
|
||||||
.getStore()
|
.getAllModels()
|
||||||
.filter(
|
.filter(
|
||||||
model =>
|
model =>
|
||||||
model instanceof GfxBlockElementModel &&
|
model instanceof GfxBlockElementModel &&
|
||||||
@@ -798,7 +798,7 @@ export class LayerManager extends GfxExtension {
|
|||||||
this._disposable.add(
|
this._disposable.add(
|
||||||
store.slots.blockUpdated.subscribe(payload => {
|
store.slots.blockUpdated.subscribe(payload => {
|
||||||
if (payload.type === 'add') {
|
if (payload.type === 'add') {
|
||||||
const block = store.getBlockById(payload.id)!;
|
const block = store.getModelById(payload.id)!;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
block instanceof GfxBlockElementModel &&
|
block instanceof GfxBlockElementModel &&
|
||||||
@@ -810,7 +810,7 @@ export class LayerManager extends GfxExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (payload.type === 'update') {
|
if (payload.type === 'update') {
|
||||||
const block = store.getBlockById(payload.id)!;
|
const block = store.getModelById(payload.id)!;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(payload.props.key === 'index' ||
|
(payload.props.key === 'index' ||
|
||||||
@@ -825,7 +825,7 @@ export class LayerManager extends GfxExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (payload.type === 'delete') {
|
if (payload.type === 'delete') {
|
||||||
const block = store.getBlockById(payload.id);
|
const block = store.getModelById(payload.id);
|
||||||
|
|
||||||
if (block instanceof GfxBlockElementModel) {
|
if (block instanceof GfxBlockElementModel) {
|
||||||
this.delete(block as GfxBlockElementModel);
|
this.delete(block as GfxBlockElementModel);
|
||||||
|
|||||||
@@ -396,7 +396,7 @@ export abstract class GfxGroupLikeElementModel<
|
|||||||
for (const key of this.childIds) {
|
for (const key of this.childIds) {
|
||||||
const element =
|
const element =
|
||||||
this.surface.getElementById(key) ||
|
this.surface.getElementById(key) ||
|
||||||
(this.surface.doc.getBlockById(key) as GfxBlockElementModel);
|
(this.surface.doc.getModelById(key) as GfxBlockElementModel);
|
||||||
|
|
||||||
element && elements.push(element);
|
element && elements.push(element);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ export class GfxSelectionManager extends GfxExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { blocks = [], elements = [] } = groupBy(selection.elements, id => {
|
const { blocks = [], elements = [] } = groupBy(selection.elements, id => {
|
||||||
return this.std.store.getBlockById(id) ? 'blocks' : 'elements';
|
return this.std.store.getModelById(id) ? 'blocks' : 'elements';
|
||||||
});
|
});
|
||||||
let instances: (SurfaceSelection | CursorSelection)[] = [];
|
let instances: (SurfaceSelection | CursorSelection)[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ export class RangeBinding {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const model = this.host.doc.getBlockById(textSelection.blockId);
|
const model = this.host.doc.getModelById(textSelection.blockId);
|
||||||
// If the model is not found, the selection maybe in another editor
|
// If the model is not found, the selection maybe in another editor
|
||||||
if (!model) return;
|
if (!model) return;
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export class BlockComponent<
|
|||||||
if (this._model) {
|
if (this._model) {
|
||||||
return this._model;
|
return this._model;
|
||||||
}
|
}
|
||||||
const model = this.doc.getBlockById<Model>(this.blockId);
|
const model = this.doc.getModelById<Model>(this.blockId);
|
||||||
if (!model) {
|
if (!model) {
|
||||||
throw new BlockSuiteError(
|
throw new BlockSuiteError(
|
||||||
ErrorCode.MissingViewModelError,
|
ErrorCode.MissingViewModelError,
|
||||||
|
|||||||
@@ -347,7 +347,7 @@ describe('addBlock', () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
const blockId = await waitOnce(doc.slots.rootAdded);
|
const blockId = await waitOnce(doc.slots.rootAdded);
|
||||||
const block = doc.getBlockById(blockId) as BlockModel;
|
const block = doc.getModelById(blockId) as BlockModel;
|
||||||
assert.equal(block.flavour, 'affine:page');
|
assert.equal(block.flavour, 'affine:page');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -512,7 +512,7 @@ describe('deleteBlock', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const deletedModel = doc.getBlockById('1') as BlockModel;
|
const deletedModel = doc.getModelById('1') as BlockModel;
|
||||||
doc.deleteBlock(deletedModel);
|
doc.deleteBlock(deletedModel);
|
||||||
|
|
||||||
assert.deepEqual(serializCollection(doc.rootDoc).spaces[spaceId].blocks, {
|
assert.deepEqual(serializCollection(doc.rootDoc).spaces[spaceId].blocks, {
|
||||||
@@ -581,8 +581,8 @@ describe('deleteBlock', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const deletedModel = doc.getBlockById('2') as BlockModel;
|
const deletedModel = doc.getModelById('2') as BlockModel;
|
||||||
const deletedModelParent = doc.getBlockById('1') as BlockModel;
|
const deletedModelParent = doc.getModelById('1') as BlockModel;
|
||||||
doc.deleteBlock(deletedModel, {
|
doc.deleteBlock(deletedModel, {
|
||||||
bringChildrenTo: deletedModelParent,
|
bringChildrenTo: deletedModelParent,
|
||||||
});
|
});
|
||||||
@@ -693,8 +693,8 @@ describe('deleteBlock', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const deletedModel = doc.getBlockById('2') as BlockModel;
|
const deletedModel = doc.getModelById('2') as BlockModel;
|
||||||
const moveToModel = doc.getBlockById('3') as BlockModel;
|
const moveToModel = doc.getModelById('3') as BlockModel;
|
||||||
doc.deleteBlock(deletedModel, {
|
doc.deleteBlock(deletedModel, {
|
||||||
bringChildrenTo: moveToModel,
|
bringChildrenTo: moveToModel,
|
||||||
});
|
});
|
||||||
@@ -820,11 +820,11 @@ describe('getBlock', () => {
|
|||||||
doc.addBlock('affine:paragraph', {}, noteId);
|
doc.addBlock('affine:paragraph', {}, noteId);
|
||||||
doc.addBlock('affine:paragraph', {}, noteId);
|
doc.addBlock('affine:paragraph', {}, noteId);
|
||||||
|
|
||||||
const text = doc.getBlockById('3') as BlockModel;
|
const text = doc.getModelById('3') as BlockModel;
|
||||||
assert.equal(text.flavour, 'affine:paragraph');
|
assert.equal(text.flavour, 'affine:paragraph');
|
||||||
assert.equal(rootModel.children[0].children.indexOf(text), 1);
|
assert.equal(rootModel.children[0].children.indexOf(text), 1);
|
||||||
|
|
||||||
const invalid = doc.getBlockById('😅');
|
const invalid = doc.getModelById('😅');
|
||||||
assert.equal(invalid, null);
|
assert.equal(invalid, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,13 @@ export interface Doc {
|
|||||||
dispose(): void;
|
dispose(): void;
|
||||||
|
|
||||||
slots: {
|
slots: {
|
||||||
|
/**
|
||||||
|
* This fires when the doc history is updated.
|
||||||
|
*/
|
||||||
historyUpdated: Subject<void>;
|
historyUpdated: Subject<void>;
|
||||||
|
/**
|
||||||
|
* This fires when the doc yBlock is updated.
|
||||||
|
*/
|
||||||
yBlockUpdated: Subject<
|
yBlockUpdated: Subject<
|
||||||
| {
|
| {
|
||||||
type: 'add';
|
type: 'add';
|
||||||
|
|||||||
@@ -61,9 +61,24 @@ export type BlockUpdatedPayload =
|
|||||||
|
|
||||||
const internalExtensions = [StoreSelectionExtension];
|
const internalExtensions = [StoreSelectionExtension];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core store class that manages blocks and their lifecycle in BlockSuite
|
||||||
|
* @remarks
|
||||||
|
* The Store class is responsible for managing the lifecycle of blocks, handling transactions,
|
||||||
|
* and maintaining the block tree structure.
|
||||||
|
* A store is a piece of data created from one or a part of a Y.Doc.
|
||||||
|
*
|
||||||
|
* @category Store
|
||||||
|
*/
|
||||||
export class Store {
|
export class Store {
|
||||||
|
/** @internal */
|
||||||
readonly userExtensions: ExtensionType[];
|
readonly userExtensions: ExtensionType[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group of disposable resources managed by the store
|
||||||
|
*
|
||||||
|
* @category Store Lifecycle
|
||||||
|
*/
|
||||||
disposableGroup = new DisposableGroup();
|
disposableGroup = new DisposableGroup();
|
||||||
|
|
||||||
private readonly _provider: ServiceProvider;
|
private readonly _provider: ServiceProvider;
|
||||||
@@ -91,6 +106,11 @@ export class Store {
|
|||||||
|
|
||||||
private readonly _schema: Schema;
|
private readonly _schema: Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slots for receiving events from the store.
|
||||||
|
*
|
||||||
|
* @category Store Lifecycle
|
||||||
|
*/
|
||||||
readonly slots: Doc['slots'] & {
|
readonly slots: Doc['slots'] & {
|
||||||
/** This is always triggered after `doc.load` is called. */
|
/** This is always triggered after `doc.load` is called. */
|
||||||
ready: Subject<void>;
|
ready: Subject<void>;
|
||||||
@@ -100,106 +120,60 @@ export class Store {
|
|||||||
* Note that at this moment, the whole block tree may not be fully initialized yet.
|
* Note that at this moment, the whole block tree may not be fully initialized yet.
|
||||||
*/
|
*/
|
||||||
rootAdded: Subject<string>;
|
rootAdded: Subject<string>;
|
||||||
|
/**
|
||||||
|
* This fires when the root block is deleted via API call or has just been removed from existing ydoc.
|
||||||
|
*/
|
||||||
rootDeleted: Subject<string>;
|
rootDeleted: Subject<string>;
|
||||||
|
/**
|
||||||
|
* This fires when a block is updated via API call or has just been updated from existing ydoc.
|
||||||
|
*/
|
||||||
blockUpdated: Subject<BlockUpdatedPayload>;
|
blockUpdated: Subject<BlockUpdatedPayload>;
|
||||||
};
|
};
|
||||||
|
|
||||||
updateBlock: {
|
|
||||||
<T extends Partial<BlockProps>>(model: BlockModel | string, props: T): void;
|
|
||||||
(model: BlockModel | string, callback: () => void): void;
|
|
||||||
} = (
|
|
||||||
modelOrId: BlockModel | string,
|
|
||||||
callBackOrProps: (() => void) | Partial<BlockProps>
|
|
||||||
) => {
|
|
||||||
if (this.readonly) {
|
|
||||||
console.error('cannot modify data in readonly mode');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isCallback = typeof callBackOrProps === 'function';
|
|
||||||
|
|
||||||
const model =
|
|
||||||
typeof modelOrId === 'string'
|
|
||||||
? this.getBlock(modelOrId)?.model
|
|
||||||
: modelOrId;
|
|
||||||
if (!model) {
|
|
||||||
throw new BlockSuiteError(
|
|
||||||
ErrorCode.ModelCRUDError,
|
|
||||||
`updating block: ${modelOrId} not found`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isCallback) {
|
|
||||||
const parent = this.getParent(model);
|
|
||||||
this.schema.validate(
|
|
||||||
model.flavour,
|
|
||||||
parent?.flavour,
|
|
||||||
callBackOrProps.children?.map(child => child.flavour)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const yBlock = this._yBlocks.get(model.id);
|
|
||||||
if (!yBlock) {
|
|
||||||
throw new BlockSuiteError(
|
|
||||||
ErrorCode.ModelCRUDError,
|
|
||||||
`updating block: ${model.id} not found`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const block = this.getBlock(model.id);
|
|
||||||
if (!block) return;
|
|
||||||
|
|
||||||
this.transact(() => {
|
|
||||||
if (isCallback) {
|
|
||||||
callBackOrProps();
|
|
||||||
this._runQuery(block);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callBackOrProps.children) {
|
|
||||||
this._crud.updateBlockChildren(
|
|
||||||
model.id,
|
|
||||||
callBackOrProps.children.map(child => child.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const schema = this.schema.flavourSchemaMap.get(model.flavour);
|
|
||||||
if (!schema) {
|
|
||||||
throw new BlockSuiteError(
|
|
||||||
ErrorCode.ModelCRUDError,
|
|
||||||
`schema for flavour: ${model.flavour} not found`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
syncBlockProps(schema, model, yBlock, callBackOrProps);
|
|
||||||
this._runQuery(block);
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
private get _yBlocks() {
|
private get _yBlocks() {
|
||||||
return this._doc.yBlocks;
|
return this._doc.yBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link AwarenessStore} instance for current store
|
||||||
|
*/
|
||||||
get awarenessStore() {
|
get awarenessStore() {
|
||||||
return this._doc.awarenessStore;
|
return this._doc.awarenessStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the di provider for current store.
|
||||||
|
*/
|
||||||
get provider() {
|
get provider() {
|
||||||
return this._provider;
|
return this._provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link BlobEngine} instance for current store.
|
||||||
|
*/
|
||||||
get blobSync() {
|
get blobSync() {
|
||||||
return this.workspace.blobSync;
|
return this.workspace.blobSync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link Doc} instance for current store.
|
||||||
|
*/
|
||||||
get doc() {
|
get doc() {
|
||||||
return this._doc;
|
return this._doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
get blocks() {
|
get blocks() {
|
||||||
return this._blocks;
|
return this._blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of blocks in the store
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
get blockSize() {
|
get blockSize() {
|
||||||
return Object.values(this._blocks.peek()).length;
|
return Object.values(this._blocks.peek()).length;
|
||||||
}
|
}
|
||||||
@@ -488,6 +462,17 @@ export class Store {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and adds a new block to the store
|
||||||
|
* @param flavour - The block's flavour (type)
|
||||||
|
* @param blockProps - Optional properties for the new block
|
||||||
|
* @param parent - Optional parent block or parent block ID
|
||||||
|
* @param parentIndex - Optional index position in parent's children
|
||||||
|
* @returns The ID of the newly created block
|
||||||
|
* @throws {BlockSuiteError} When store is in readonly mode
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
addBlock(
|
addBlock(
|
||||||
flavour: string,
|
flavour: string,
|
||||||
blockProps: Partial<BlockProps & Omit<BlockProps, 'flavour'>> = {},
|
blockProps: Partial<BlockProps & Omit<BlockProps, 'flavour'>> = {},
|
||||||
@@ -516,6 +501,15 @@ export class Store {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add multiple blocks to the store
|
||||||
|
* @param blocks - Array of blocks to add
|
||||||
|
* @param parent - Optional parent block or parent block ID
|
||||||
|
* @param parentIndex - Optional index position in parent's children
|
||||||
|
* @returns Array of IDs of the newly created blocks
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
addBlocks(
|
addBlocks(
|
||||||
blocks: Array<{
|
blocks: Array<{
|
||||||
flavour: string;
|
flavour: string;
|
||||||
@@ -539,6 +533,15 @@ export class Store {
|
|||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add sibling blocks to the store
|
||||||
|
* @param targetModel - The target block model
|
||||||
|
* @param props - Array of block properties
|
||||||
|
* @param place - Optional position to place the new blocks ('after' or 'before')
|
||||||
|
* @returns Array of IDs of the newly created blocks
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
addSiblingBlocks(
|
addSiblingBlocks(
|
||||||
targetModel: BlockModel,
|
targetModel: BlockModel,
|
||||||
props: Array<Partial<BlockProps>>,
|
props: Array<Partial<BlockProps>>,
|
||||||
@@ -576,6 +579,95 @@ export class Store {
|
|||||||
return this.addBlocks(blocks, parent.id, insertIndex);
|
return this.addBlocks(blocks, parent.id, insertIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a block's properties or executes a callback in a transaction
|
||||||
|
* @param modelOrId - The block model or block ID to update
|
||||||
|
* @param callBackOrProps - Either a callback function to execute or properties to update
|
||||||
|
* @throws {BlockSuiteError} When the block is not found or schema validation fails
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
|
updateBlock: {
|
||||||
|
<T extends Partial<BlockProps>>(model: BlockModel | string, props: T): void;
|
||||||
|
(model: BlockModel | string, callback: () => void): void;
|
||||||
|
} = (
|
||||||
|
modelOrId: BlockModel | string,
|
||||||
|
callBackOrProps: (() => void) | Partial<BlockProps>
|
||||||
|
) => {
|
||||||
|
if (this.readonly) {
|
||||||
|
console.error('cannot modify data in readonly mode');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCallback = typeof callBackOrProps === 'function';
|
||||||
|
|
||||||
|
const model =
|
||||||
|
typeof modelOrId === 'string'
|
||||||
|
? this.getBlock(modelOrId)?.model
|
||||||
|
: modelOrId;
|
||||||
|
if (!model) {
|
||||||
|
throw new BlockSuiteError(
|
||||||
|
ErrorCode.ModelCRUDError,
|
||||||
|
`updating block: ${modelOrId} not found`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isCallback) {
|
||||||
|
const parent = this.getParent(model);
|
||||||
|
this.schema.validate(
|
||||||
|
model.flavour,
|
||||||
|
parent?.flavour,
|
||||||
|
callBackOrProps.children?.map(child => child.flavour)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const yBlock = this._yBlocks.get(model.id);
|
||||||
|
if (!yBlock) {
|
||||||
|
throw new BlockSuiteError(
|
||||||
|
ErrorCode.ModelCRUDError,
|
||||||
|
`updating block: ${model.id} not found`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const block = this.getBlock(model.id);
|
||||||
|
if (!block) return;
|
||||||
|
|
||||||
|
this.transact(() => {
|
||||||
|
if (isCallback) {
|
||||||
|
callBackOrProps();
|
||||||
|
this._runQuery(block);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callBackOrProps.children) {
|
||||||
|
this._crud.updateBlockChildren(
|
||||||
|
model.id,
|
||||||
|
callBackOrProps.children.map(child => child.id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = this.schema.flavourSchemaMap.get(model.flavour);
|
||||||
|
if (!schema) {
|
||||||
|
throw new BlockSuiteError(
|
||||||
|
ErrorCode.ModelCRUDError,
|
||||||
|
`schema for flavour: ${model.flavour} not found`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
syncBlockProps(schema, model, yBlock, callBackOrProps);
|
||||||
|
this._runQuery(block);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a block from the store
|
||||||
|
* @param model - The block model or block ID to delete
|
||||||
|
* @param options - Optional options for the deletion
|
||||||
|
* @param options.bringChildrenTo - Optional block model to bring children to
|
||||||
|
* @param options.deleteChildren - Optional flag to delete children
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
deleteBlock(
|
deleteBlock(
|
||||||
model: BlockModel | string,
|
model: BlockModel | string,
|
||||||
options: {
|
options: {
|
||||||
@@ -610,49 +702,49 @@ export class Store {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
/**
|
||||||
this._provider.getAll(StoreExtensionIdentifier).forEach(ext => {
|
* Gets a block by its ID
|
||||||
ext.disposed();
|
* @param id - The block's ID
|
||||||
});
|
* @returns The block instance if found, undefined otherwise
|
||||||
this.slots.ready.complete();
|
*
|
||||||
this.slots.rootAdded.complete();
|
* @category Block CRUD
|
||||||
this.slots.rootDeleted.complete();
|
*/
|
||||||
this.slots.blockUpdated.complete();
|
|
||||||
this.disposableGroup.dispose();
|
|
||||||
this._isDisposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getBlock(id: string): Block | undefined {
|
getBlock(id: string): Block | undefined {
|
||||||
return this._blocks.peek()[id];
|
return this._blocks.peek()[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a block by its ID
|
||||||
|
* @param id - The block's ID
|
||||||
|
* @returns The block instance in signal if found, undefined otherwise
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
getBlock$(id: string): Block | undefined {
|
getBlock$(id: string): Block | undefined {
|
||||||
return this._blocks.value[id];
|
return this._blocks.value[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* Get a model by its ID
|
||||||
* Use `getBlocksByFlavour` instead.
|
* @param id - The model's ID
|
||||||
|
* @returns The model instance if found, null otherwise
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
*/
|
*/
|
||||||
getBlockByFlavour(blockFlavour: string | string[]) {
|
getModelById<Model extends BlockModel = BlockModel>(
|
||||||
return this.getBlocksByFlavour(blockFlavour).map(x => x.model);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* Use `getBlock` instead.
|
|
||||||
*/
|
|
||||||
getBlockById<Model extends BlockModel = BlockModel>(
|
|
||||||
id: string
|
id: string
|
||||||
): Model | null {
|
): Model | null {
|
||||||
return (this.getBlock(id)?.model ?? null) as Model | null;
|
return (this.getBlock(id)?.model ?? null) as Model | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getStore() {
|
/**
|
||||||
return Object.values(this._blocks.peek()).map(block => block.model);
|
* Gets all blocks of specified flavour(s)
|
||||||
}
|
* @param blockFlavour - Single flavour or array of flavours to filter by
|
||||||
|
* @returns Array of matching blocks
|
||||||
getBlocksByFlavour(blockFlavour: string | string[]) {
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
|
getBlocksByFlavour(blockFlavour: string | string[]): Block[] {
|
||||||
const flavours =
|
const flavours =
|
||||||
typeof blockFlavour === 'string' ? [blockFlavour] : blockFlavour;
|
typeof blockFlavour === 'string' ? [blockFlavour] : blockFlavour;
|
||||||
|
|
||||||
@@ -661,21 +753,34 @@ export class Store {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNext(block: BlockModel | string) {
|
/**
|
||||||
return this._getSiblings(
|
* Get all models in the store
|
||||||
block,
|
* @returns Array of all models
|
||||||
(parent, index) => parent.children[index + 1] ?? null
|
*
|
||||||
);
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
|
getAllModels() {
|
||||||
|
return Object.values(this._blocks.peek()).map(block => block.model);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNexts(block: BlockModel | string) {
|
/**
|
||||||
return (
|
* Get all models of specified flavour(s)
|
||||||
this._getSiblings(block, (parent, index) =>
|
* @param blockFlavour - Single flavour or array of flavours to filter by
|
||||||
parent.children.slice(index + 1)
|
* @returns Array of matching models
|
||||||
) ?? []
|
*
|
||||||
);
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
|
getModelsByFlavour(blockFlavour: string | string[]): BlockModel[] {
|
||||||
|
return this.getBlocksByFlavour(blockFlavour).map(x => x.model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the parent block of a given block
|
||||||
|
* @param target - Block model or block ID to find parent for
|
||||||
|
* @returns The parent block model if found, null otherwise
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
getParent(target: BlockModel | string): BlockModel | null {
|
getParent(target: BlockModel | string): BlockModel | null {
|
||||||
const targetId = typeof target === 'string' ? target : target.id;
|
const targetId = typeof target === 'string' ? target : target.id;
|
||||||
const parentId = this._crud.getParent(targetId);
|
const parentId = this._crud.getParent(targetId);
|
||||||
@@ -687,6 +792,13 @@ export class Store {
|
|||||||
return parent.model;
|
return parent.model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the previous sibling block of a given block
|
||||||
|
* @param block - Block model or block ID to find previous sibling for
|
||||||
|
* @returns The previous sibling block model if found, null otherwise
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
getPrev(block: BlockModel | string) {
|
getPrev(block: BlockModel | string) {
|
||||||
return this._getSiblings(
|
return this._getSiblings(
|
||||||
block,
|
block,
|
||||||
@@ -694,6 +806,13 @@ export class Store {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all previous sibling blocks of a given block
|
||||||
|
* @param block - Block model or block ID to find previous siblings for
|
||||||
|
* @returns Array of previous sibling blocks if found, empty array otherwise
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
getPrevs(block: BlockModel | string) {
|
getPrevs(block: BlockModel | string) {
|
||||||
return (
|
return (
|
||||||
this._getSiblings(block, (parent, index) =>
|
this._getSiblings(block, (parent, index) =>
|
||||||
@@ -702,38 +821,55 @@ export class Store {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSchemaByFlavour(flavour: string) {
|
/**
|
||||||
return this._schema.flavourSchemaMap.get(flavour);
|
* Get the next sibling block of a given block
|
||||||
|
* @param block - Block model or block ID to find next sibling for
|
||||||
|
* @returns The next sibling block model if found, null otherwise
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
|
getNext(block: BlockModel | string) {
|
||||||
|
return this._getSiblings(
|
||||||
|
block,
|
||||||
|
(parent, index) => parent.children[index + 1] ?? null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all next sibling blocks of a given block
|
||||||
|
* @param block - Block model or block ID to find next siblings for
|
||||||
|
* @returns Array of next sibling blocks if found, empty array otherwise
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
|
getNexts(block: BlockModel | string) {
|
||||||
|
return (
|
||||||
|
this._getSiblings(block, (parent, index) =>
|
||||||
|
parent.children.slice(index + 1)
|
||||||
|
) ?? []
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a block exists by its ID
|
||||||
|
* @param id - The block's ID
|
||||||
|
* @returns True if the block exists, false otherwise
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
|
*/
|
||||||
hasBlock(id: string) {
|
hasBlock(id: string) {
|
||||||
return id in this._blocks.peek();
|
return id in this._blocks.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* Move blocks to a new parent block
|
||||||
* Use `hasBlock` instead.
|
* @param blocksToMove - Array of block models to move
|
||||||
|
* @param newParent - The new parent block model
|
||||||
|
* @param targetSibling - Optional target sibling block model
|
||||||
|
* @param shouldInsertBeforeSibling - Optional flag to insert before sibling
|
||||||
|
*
|
||||||
|
* @category Block CRUD
|
||||||
*/
|
*/
|
||||||
hasBlockById(id: string) {
|
|
||||||
return this.hasBlock(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
load(initFn?: () => void) {
|
|
||||||
if (this._isDisposed) {
|
|
||||||
this.disposableGroup = new DisposableGroup();
|
|
||||||
this._subscribeToSlots();
|
|
||||||
this._isDisposed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._doc.load(initFn);
|
|
||||||
this._provider.getAll(StoreExtensionIdentifier).forEach(ext => {
|
|
||||||
ext.loaded();
|
|
||||||
});
|
|
||||||
this.slots.ready.next();
|
|
||||||
this.slots.rootAdded.next(this.root?.id ?? '');
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveBlocks(
|
moveBlocks(
|
||||||
blocksToMove: BlockModel[],
|
blocksToMove: BlockModel[],
|
||||||
newParent: BlockModel,
|
newParent: BlockModel,
|
||||||
@@ -755,14 +891,13 @@ export class Store {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get get() {
|
/**
|
||||||
return this.provider.get.bind(this.provider);
|
* Creates a new transformer instance for the store
|
||||||
}
|
* @param middlewares - Optional array of transformer middlewares
|
||||||
|
* @returns A new Transformer instance
|
||||||
get getOptional() {
|
*
|
||||||
return this.provider.getOptional.bind(this.provider);
|
* @category Transformer
|
||||||
}
|
*/
|
||||||
|
|
||||||
getTransformer(middlewares: TransformerMiddleware[] = []) {
|
getTransformer(middlewares: TransformerMiddleware[] = []) {
|
||||||
return new Transformer({
|
return new Transformer({
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
@@ -775,4 +910,72 @@ export class Store {
|
|||||||
middlewares,
|
middlewares,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an extension instance from the store
|
||||||
|
* @returns The extension instance
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const extension = store.get(SomeExtension);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
get get() {
|
||||||
|
return this.provider.get.bind(this.provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional get an extension instance from the store.
|
||||||
|
* The major difference between `get` and `getOptional` is that `getOptional` will not throw an error if the extension is not found.
|
||||||
|
*
|
||||||
|
* @returns The extension instance
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const extension = store.getOptional(SomeExtension);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
get getOptional() {
|
||||||
|
return this.provider.getOptional.bind(this.provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes and loads the store
|
||||||
|
* @param initFn - Optional initialization function
|
||||||
|
* @returns The store instance
|
||||||
|
*
|
||||||
|
* @category Store Lifecycle
|
||||||
|
*/
|
||||||
|
load(initFn?: () => void) {
|
||||||
|
if (this._isDisposed) {
|
||||||
|
this.disposableGroup = new DisposableGroup();
|
||||||
|
this._subscribeToSlots();
|
||||||
|
this._isDisposed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._doc.load(initFn);
|
||||||
|
this._provider.getAll(StoreExtensionIdentifier).forEach(ext => {
|
||||||
|
ext.loaded();
|
||||||
|
});
|
||||||
|
this.slots.ready.next();
|
||||||
|
this.slots.rootAdded.next(this.root?.id ?? '');
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes the store and releases all resources
|
||||||
|
*
|
||||||
|
* @category Store Lifecycle
|
||||||
|
*/
|
||||||
|
dispose() {
|
||||||
|
this._provider.getAll(StoreExtensionIdentifier).forEach(ext => {
|
||||||
|
ext.disposed();
|
||||||
|
});
|
||||||
|
this.slots.ready.complete();
|
||||||
|
this.slots.rootAdded.complete();
|
||||||
|
this.slots.rootDeleted.complete();
|
||||||
|
this.slots.blockUpdated.complete();
|
||||||
|
this.disposableGroup.dispose();
|
||||||
|
this._isDisposed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -293,7 +293,7 @@ export class Transformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const contentBlocks = blockTree.children
|
const contentBlocks = blockTree.children
|
||||||
.map(tree => doc.getBlockById(tree.draft.id))
|
.map(tree => doc.getModelById(tree.draft.id))
|
||||||
.filter((x): x is BlockModel => x !== null)
|
.filter((x): x is BlockModel => x !== null)
|
||||||
.map(model => toDraftModel(model));
|
.map(model => toDraftModel(model));
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ let model: SurfaceBlockModel;
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const cleanup = await setupEditor('edgeless');
|
const cleanup = await setupEditor('edgeless');
|
||||||
const models = doc.getBlockByFlavour('affine:surface') as SurfaceBlockModel[];
|
const models = doc.getModelsByFlavour(
|
||||||
|
'affine:surface'
|
||||||
|
) as SurfaceBlockModel[];
|
||||||
|
|
||||||
model = models[0];
|
model = models[0];
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const snapshotTest = async (snapshotUrl: string, elementsCount: number) => {
|
|||||||
editor.doc = newDoc;
|
editor.doc = newDoc;
|
||||||
await wait();
|
await wait();
|
||||||
|
|
||||||
const surface = newDoc.getBlockByFlavour(
|
const surface = newDoc.getModelsByFlavour(
|
||||||
'affine:surface'
|
'affine:surface'
|
||||||
)[0] as SurfaceBlockModel;
|
)[0] as SurfaceBlockModel;
|
||||||
const surfaceElements = [...surface['_elementModels']].map(
|
const surfaceElements = [...surface['_elementModels']].map(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { Store } from '@blocksuite/store';
|
|||||||
import type { TestAffineEditorContainer } from '../../index.js';
|
import type { TestAffineEditorContainer } from '../../index.js';
|
||||||
|
|
||||||
export function getSurface(doc: Store, editor: TestAffineEditorContainer) {
|
export function getSurface(doc: Store, editor: TestAffineEditorContainer) {
|
||||||
const surfaceModel = doc.getBlockByFlavour('affine:surface');
|
const surfaceModel = doc.getModelsByFlavour('affine:surface');
|
||||||
|
|
||||||
return editor.host!.view.getBlock(
|
return editor.host!.view.getBlock(
|
||||||
surfaceModel[0]!.id
|
surfaceModel[0]!.id
|
||||||
|
|||||||
@@ -489,7 +489,7 @@ export class StarterDebugMenu extends ShadowlessElement {
|
|||||||
);
|
);
|
||||||
for (const doc of docs) {
|
for (const doc of docs) {
|
||||||
if (doc) {
|
if (doc) {
|
||||||
const noteBlock = window.doc.getBlockByFlavour('affine:note');
|
const noteBlock = window.doc.getModelsByFlavour('affine:note');
|
||||||
window.doc.addBlock(
|
window.doc.addBlock(
|
||||||
'affine:paragraph',
|
'affine:paragraph',
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const database: InitFn = (collection: Workspace, id: string) => {
|
|||||||
// Add note block inside root block
|
// Add note block inside root block
|
||||||
const noteId = doc.addBlock('affine:note', {}, rootId);
|
const noteId = doc.addBlock('affine:note', {}, rootId);
|
||||||
const pId = doc.addBlock('affine:paragraph', {}, noteId);
|
const pId = doc.addBlock('affine:paragraph', {}, noteId);
|
||||||
const model = doc.getBlockById(pId);
|
const model = doc.getModelById(pId);
|
||||||
if (!model) {
|
if (!model) {
|
||||||
throw new Error('model is not found');
|
throw new Error('model is not found');
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ export const database: InitFn = (collection: Workspace, id: string) => {
|
|||||||
},
|
},
|
||||||
noteId
|
noteId
|
||||||
);
|
);
|
||||||
const database = doc.getBlockById(databaseId) as DatabaseBlockModel;
|
const database = doc.getModelById(databaseId) as DatabaseBlockModel;
|
||||||
const datasource = new DatabaseBlockDataSource(database);
|
const datasource = new DatabaseBlockDataSource(database);
|
||||||
datasource.viewManager.viewAdd('table');
|
datasource.viewManager.viewAdd('table');
|
||||||
database.props.title = new Text(title);
|
database.props.title = new Text(title);
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ function addAIChatBlock(
|
|||||||
|
|
||||||
const { doc } = host;
|
const { doc } = host;
|
||||||
const surfaceBlock = doc
|
const surfaceBlock = doc
|
||||||
.getStore()
|
.getAllModels()
|
||||||
.find(block => block.flavour === 'affine:surface');
|
.find(block => block.flavour === 'affine:surface');
|
||||||
if (!surfaceBlock) {
|
if (!surfaceBlock) {
|
||||||
return;
|
return;
|
||||||
@@ -520,7 +520,7 @@ const CREATE_AS_LINKED_DOC = {
|
|||||||
|
|
||||||
const { doc } = host;
|
const { doc } = host;
|
||||||
const surfaceBlock = doc
|
const surfaceBlock = doc
|
||||||
.getStore()
|
.getAllModels()
|
||||||
.find(block => block.flavour === 'affine:surface');
|
.find(block => block.flavour === 'affine:surface');
|
||||||
if (!surfaceBlock) {
|
if (!surfaceBlock) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ function getEdgelessContentBound(host: EditorHost) {
|
|||||||
|
|
||||||
const elements = (
|
const elements = (
|
||||||
host.doc
|
host.doc
|
||||||
.getStore()
|
.getAllModels()
|
||||||
.filter(
|
.filter(
|
||||||
model =>
|
model =>
|
||||||
model instanceof GfxBlockElementModel &&
|
model instanceof GfxBlockElementModel &&
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export class MiniMindmapPreview extends WithDisposable(LitElement) {
|
|||||||
const doc = collection.createDoc({ id: 'doc:home' }).load();
|
const doc = collection.createDoc({ id: 'doc:home' }).load();
|
||||||
const rootId = doc.addBlock('affine:page', {});
|
const rootId = doc.addBlock('affine:page', {});
|
||||||
const surfaceId = doc.addBlock('affine:surface', {}, rootId);
|
const surfaceId = doc.addBlock('affine:surface', {}, rootId);
|
||||||
const surface = doc.getBlockById(surfaceId) as SurfaceBlockModel;
|
const surface = doc.getModelById(surfaceId) as SurfaceBlockModel;
|
||||||
doc.resetHistory();
|
doc.resetHistory();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
const { doc } = this.host;
|
const { doc } = this.host;
|
||||||
// create a new AI chat block
|
// create a new AI chat block
|
||||||
const surfaceBlock = doc
|
const surfaceBlock = doc
|
||||||
.getStore()
|
.getAllModels()
|
||||||
.find(block => block.flavour === 'affine:surface');
|
.find(block => block.flavour === 'affine:surface');
|
||||||
if (!surfaceBlock) {
|
if (!surfaceBlock) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ function processSnapshot(
|
|||||||
text: TextSelection,
|
text: TextSelection,
|
||||||
host: EditorHost
|
host: EditorHost
|
||||||
) {
|
) {
|
||||||
const model = host.doc.getBlockById(snapshot.id);
|
const model = host.doc.getModelById(snapshot.id);
|
||||||
if (!model) {
|
if (!model) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export function useReferenceLinkHelper(docCollection: Workspace) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
] as DeltaInsert<AffineTextAttributes>[]);
|
] as DeltaInsert<AffineTextAttributes>[]);
|
||||||
const [frame] = page.getBlockByFlavour('affine:note');
|
const [frame] = page.getModelsByFlavour('affine:note');
|
||||||
|
|
||||||
frame && page.addBlock('affine:paragraph', { text }, frame.id);
|
frame && page.addBlock('affine:paragraph', { text }, frame.id);
|
||||||
},
|
},
|
||||||
|
|||||||
+3
-3
@@ -44,12 +44,12 @@ describe('useBlockSuitePagePreview', () => {
|
|||||||
{
|
{
|
||||||
text: new Text('Hello, world!'),
|
text: new Text('Hello, world!'),
|
||||||
},
|
},
|
||||||
page.getBlockByFlavour('affine:note')[0].id
|
page.getModelsByFlavour('affine:note')[0].id
|
||||||
);
|
);
|
||||||
const hook = renderHook(() => useAtomValue(useBlockSuitePagePreview(page)));
|
const hook = renderHook(() => useAtomValue(useBlockSuitePagePreview(page)));
|
||||||
expect(hook.result.current).toBe('Hello, world!');
|
expect(hook.result.current).toBe('Hello, world!');
|
||||||
page.transact(() => {
|
page.transact(() => {
|
||||||
page.getBlockById(id)!.text!.insert('Test', 0);
|
page.getModelById(id)!.text!.insert('Test', 0);
|
||||||
});
|
});
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
hook.rerender();
|
hook.rerender();
|
||||||
@@ -61,7 +61,7 @@ describe('useBlockSuitePagePreview', () => {
|
|||||||
{
|
{
|
||||||
text: new Text('First block!'),
|
text: new Text('First block!'),
|
||||||
},
|
},
|
||||||
page.getBlockByFlavour('affine:note')[0].id,
|
page.getModelsByFlavour('affine:note')[0].id,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ test.describe('Embed synced doc', () => {
|
|||||||
},
|
},
|
||||||
noteId
|
noteId
|
||||||
);
|
);
|
||||||
const model = doc2.getBlockById(databaseId) as DatabaseBlockModel;
|
const model = doc2.getModelById(databaseId) as DatabaseBlockModel;
|
||||||
const datasource =
|
const datasource =
|
||||||
new window.$blocksuite.blocks.database.DatabaseBlockDataSource(model);
|
new window.$blocksuite.blocks.database.DatabaseBlockDataSource(model);
|
||||||
datasource.viewManager.viewAdd('table');
|
datasource.viewManager.viewAdd('table');
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export async function getNoteRect(page: Page, noteId: string) {
|
|||||||
const xywh: string | null = await page.evaluate(
|
const xywh: string | null = await page.evaluate(
|
||||||
([noteId]) => {
|
([noteId]) => {
|
||||||
const doc = window.collection.getDoc('doc:home');
|
const doc = window.collection.getDoc('doc:home');
|
||||||
const block = doc?.getBlockById(noteId);
|
const block = doc?.getModelById(noteId);
|
||||||
if (block?.flavour === 'affine:note') {
|
if (block?.flavour === 'affine:note') {
|
||||||
return (block as NoteBlockModel).xywh;
|
return (block as NoteBlockModel).xywh;
|
||||||
} else {
|
} else {
|
||||||
@@ -85,7 +85,7 @@ export async function getNoteProps(page: Page, noteId: string) {
|
|||||||
const props = await page.evaluate(
|
const props = await page.evaluate(
|
||||||
([id]) => {
|
([id]) => {
|
||||||
const doc = window.collection.getDoc('doc:home');
|
const doc = window.collection.getDoc('doc:home');
|
||||||
const block = doc?.getBlockById(id);
|
const block = doc?.getModelById(id);
|
||||||
if (block?.flavour === 'affine:note') {
|
if (block?.flavour === 'affine:note') {
|
||||||
return (block as NoteBlockModel).keys.reduce(
|
return (block as NoteBlockModel).keys.reduce(
|
||||||
(pre, key) => {
|
(pre, key) => {
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ export async function initEmptyDatabaseState(page: Page, rootId?: string) {
|
|||||||
},
|
},
|
||||||
noteId
|
noteId
|
||||||
);
|
);
|
||||||
const model = doc.getBlockById(databaseId) as DatabaseBlockModel;
|
const model = doc.getModelById(databaseId) as DatabaseBlockModel;
|
||||||
const datasource =
|
const datasource =
|
||||||
new window.$blocksuite.blocks.database.DatabaseBlockDataSource(model);
|
new window.$blocksuite.blocks.database.DatabaseBlockDataSource(model);
|
||||||
datasource.viewManager.viewAdd('table');
|
datasource.viewManager.viewAdd('table');
|
||||||
@@ -373,7 +373,7 @@ export async function initKanbanViewState(
|
|||||||
},
|
},
|
||||||
noteId
|
noteId
|
||||||
);
|
);
|
||||||
const model = doc.getBlockById(databaseId) as DatabaseBlockModel;
|
const model = doc.getModelById(databaseId) as DatabaseBlockModel;
|
||||||
const datasource =
|
const datasource =
|
||||||
new window.$blocksuite.blocks.database.DatabaseBlockDataSource(model);
|
new window.$blocksuite.blocks.database.DatabaseBlockDataSource(model);
|
||||||
const rowIds = config.rows.map(rowText => {
|
const rowIds = config.rows.map(rowText => {
|
||||||
@@ -432,7 +432,7 @@ export async function initEmptyDatabaseWithParagraphState(
|
|||||||
},
|
},
|
||||||
noteId
|
noteId
|
||||||
);
|
);
|
||||||
const model = doc.getBlockById(databaseId) as DatabaseBlockModel;
|
const model = doc.getModelById(databaseId) as DatabaseBlockModel;
|
||||||
const datasource =
|
const datasource =
|
||||||
new window.$blocksuite.blocks.database.DatabaseBlockDataSource(model);
|
new window.$blocksuite.blocks.database.DatabaseBlockDataSource(model);
|
||||||
datasource.viewManager.viewAdd('table');
|
datasource.viewManager.viewAdd('table');
|
||||||
|
|||||||
Reference in new issue
Block a user