mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 18:26:05 +08:00
feat(editor): improve api for store, and add docs (#10941)
This commit is contained in:
@@ -82,7 +82,7 @@ describe('DatabaseManager', () => {
|
||||
noteBlockId
|
||||
);
|
||||
|
||||
const databaseModel = doc.getBlockById(
|
||||
const databaseModel = doc.getModelById(
|
||||
databaseBlockId
|
||||
) as DatabaseBlockModel;
|
||||
db = databaseModel;
|
||||
@@ -187,7 +187,7 @@ describe('DatabaseManager', () => {
|
||||
addProperty(db, 'end', column);
|
||||
updateCell(db, modelId, cell);
|
||||
|
||||
const model = doc.getBlockById(modelId);
|
||||
const model = doc.getModelById(modelId);
|
||||
|
||||
expect(model).not.toBeNull();
|
||||
|
||||
|
||||
@@ -466,7 +466,7 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
}
|
||||
|
||||
rowMove(rowId: string, position: InsertToPosition): void {
|
||||
const model = this.doc.getBlockById(rowId);
|
||||
const model = this.doc.getModelById(rowId);
|
||||
if (model) {
|
||||
const index = insertPositionToIndex(position, this._model.children);
|
||||
const target = this._model.children[index];
|
||||
|
||||
@@ -68,7 +68,7 @@ export async function uploadBlobForImage(
|
||||
} finally {
|
||||
setImageUploaded(blockId);
|
||||
|
||||
const imageModel = doc.getBlockById(blockId) as ImageBlockModel | null;
|
||||
const imageModel = doc.getModelById(blockId) as ImageBlockModel | null;
|
||||
if (sourceId && imageModel) {
|
||||
const props: Partial<ImageBlockProps> = {
|
||||
sourceId,
|
||||
|
||||
@@ -84,7 +84,7 @@ export const updateBlockType: Command<
|
||||
if (flavour !== 'affine:code') return;
|
||||
const id = mergeToCodeModel(blockModels);
|
||||
if (!id) return;
|
||||
const model = doc.getBlockById(id);
|
||||
const model = doc.getModelById(id);
|
||||
if (!model) return;
|
||||
asyncSetInlineRange(host, model, {
|
||||
index: model.text?.length ?? 0,
|
||||
@@ -115,7 +115,7 @@ export const updateBlockType: Command<
|
||||
nextSiblingId = doc.addBlock('affine:paragraph', {}, parent);
|
||||
}
|
||||
focusTextModel(host.std, nextSiblingId);
|
||||
const newModel = doc.getBlockById(id);
|
||||
const newModel = doc.getModelById(id);
|
||||
if (!newModel) {
|
||||
return next({ updatedBlocks: [] });
|
||||
}
|
||||
@@ -217,7 +217,7 @@ export const updateBlockType: Command<
|
||||
if (!newId) {
|
||||
return;
|
||||
}
|
||||
const newModel = doc.getBlockById(newId);
|
||||
const newModel = doc.getModelById(newId);
|
||||
if (newModel) {
|
||||
newModels.push(newModel);
|
||||
}
|
||||
|
||||
@@ -1091,7 +1091,7 @@ export class EdgelessClipboardController extends PageClipboard {
|
||||
const newSelected = [
|
||||
...canvasElementIds,
|
||||
...blockIds.filter(id => {
|
||||
return isTopLevelBlock(this.doc.getBlockById(id));
|
||||
return isTopLevelBlock(this.doc.getModelById(id));
|
||||
}),
|
||||
];
|
||||
|
||||
|
||||
@@ -380,7 +380,7 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
|
||||
this.edgeless
|
||||
);
|
||||
} else {
|
||||
const model = doc.getBlockById(id);
|
||||
const model = doc.getModelById(id);
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ export class NoteSlicer extends WidgetComponent<
|
||||
doc.root?.id
|
||||
);
|
||||
|
||||
doc.moveBlocks(resetBlocks, doc.getBlockById(newNoteId) as NoteBlockModel);
|
||||
doc.moveBlocks(resetBlocks, doc.getModelById(newNoteId) as NoteBlockModel);
|
||||
|
||||
this._activeSlicerIndex = 0;
|
||||
this._selection.set({
|
||||
|
||||
@@ -188,7 +188,7 @@ export class TemplateJob {
|
||||
if (isMergeBlock) {
|
||||
this._mergeProps(
|
||||
json,
|
||||
this.model.doc.getBlockById(
|
||||
this.model.doc.getModelById(
|
||||
this._getMergeBlockId(json)
|
||||
) as BlockModel
|
||||
);
|
||||
|
||||
@@ -191,7 +191,7 @@ export class PageRootBlockComponent extends BlockComponent<
|
||||
const { doc } = this;
|
||||
|
||||
const noteId = doc.addBlock('affine:note', {}, doc.root?.id);
|
||||
return doc.getBlockById(noteId) as NoteBlockModel;
|
||||
return doc.getModelById(noteId) as NoteBlockModel;
|
||||
}
|
||||
|
||||
private _getDefaultNoteBlock() {
|
||||
@@ -262,7 +262,7 @@ export class PageRootBlockComponent extends BlockComponent<
|
||||
);
|
||||
if (!sel) return;
|
||||
let model: BlockModel | null = null;
|
||||
let current = this.doc.getBlockById(sel.blockId);
|
||||
let current = this.doc.getModelById(sel.blockId);
|
||||
while (current && !model) {
|
||||
if (current.flavour === 'affine:note') {
|
||||
model = current;
|
||||
@@ -280,7 +280,7 @@ export class PageRootBlockComponent extends BlockComponent<
|
||||
}
|
||||
return;
|
||||
}
|
||||
const notes = this.doc.getBlockByFlavour('affine:note');
|
||||
const notes = this.doc.getModelsByFlavour('affine:note');
|
||||
const index = notes.indexOf(prevNote);
|
||||
if (index !== 0) return;
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
|
||||
flavour: 'affine:paragraph',
|
||||
},
|
||||
]);
|
||||
const model = this.doc.getBlockById(paragraphId);
|
||||
const model = this.doc.getModelById(paragraphId);
|
||||
if (!model) return;
|
||||
|
||||
requestConnectedFrame(() => {
|
||||
|
||||
@@ -129,7 +129,7 @@ export class EdgelessCRUDExtension extends Extension {
|
||||
return;
|
||||
}
|
||||
|
||||
const block = this.std.store.getBlockById(id);
|
||||
const block = this.std.store.getModelById(id);
|
||||
if (block) {
|
||||
const key = getLastPropsKey(block.flavour, {
|
||||
...block.yBlock.toJSON(),
|
||||
@@ -145,7 +145,7 @@ export class EdgelessCRUDExtension extends Extension {
|
||||
if (!surface) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -602,7 +602,7 @@ function getNotesInFrameBound(
|
||||
): NoteBlockModel[] {
|
||||
const bound = Bound.deserialize(frame.xywh);
|
||||
|
||||
return (doc.getBlockByFlavour('affine:note') as NoteBlockModel[]).filter(
|
||||
return (doc.getModelsByFlavour('affine:note') as NoteBlockModel[]).filter(
|
||||
ele => {
|
||||
const xywh = Bound.deserialize(ele.xywh);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ export function mindmap(
|
||||
const { connector, outdated } = result;
|
||||
const elementGetter = (id: string) =>
|
||||
model.surface.getElementById(id) ??
|
||||
(model.surface.doc.getBlockById(id) as GfxModel);
|
||||
(model.surface.doc.getModelById(id) as GfxModel);
|
||||
|
||||
if (outdated) {
|
||||
ConnectorPathGenerator.updatePath(connector, null, elementGetter);
|
||||
|
||||
@@ -25,7 +25,7 @@ export class SurfaceBlockService extends BlockService {
|
||||
const disposable = this.doc.slots.blockUpdated.subscribe(payload => {
|
||||
if (payload.flavour === 'affine:surface') {
|
||||
disposable.unsubscribe();
|
||||
const surface = this.doc.getBlockById(
|
||||
const surface = this.doc.getModelById(
|
||||
payload.id
|
||||
) as SurfaceBlockModel | null;
|
||||
if (!surface) return;
|
||||
|
||||
@@ -100,7 +100,7 @@ export function addNote(
|
||||
noteId
|
||||
);
|
||||
if (options.collapse && height > NOTE_MIN_HEIGHT) {
|
||||
const note = doc.getBlockById(noteId) as NoteBlockModel;
|
||||
const note = doc.getModelById(noteId) as NoteBlockModel;
|
||||
doc.updateBlock(note, () => {
|
||||
note.props.edgeless.collapse = true;
|
||||
note.props.edgeless.collapsedHeight = height;
|
||||
|
||||
@@ -8,9 +8,9 @@ export const connectorWatcher: SurfaceMiddleware = (
|
||||
surface: SurfaceBlockModel
|
||||
) => {
|
||||
const hasElementById = (id: string) =>
|
||||
surface.hasElementById(id) || surface.doc.hasBlockById(id);
|
||||
surface.hasElementById(id) || surface.doc.hasBlock(id);
|
||||
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) => {
|
||||
if (
|
||||
((connector.source?.id && hasElementById(connector.source.id)) ||
|
||||
|
||||
@@ -78,7 +78,7 @@ export class FrameBlockModel
|
||||
for (const key of this.childIds) {
|
||||
const element =
|
||||
this.surface.getElementById(key) ||
|
||||
(this.surface.doc.getBlockById(key) as GfxBlockElementModel);
|
||||
(this.surface.doc.getModelById(key) as GfxBlockElementModel);
|
||||
|
||||
element && elements.push(element);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export class RootBlockModel extends BlockModel<RootBlockProps> {
|
||||
const createdSubscription = this.created.subscribe(() => {
|
||||
createdSubscription.unsubscribe();
|
||||
this.doc.slots.rootAdded.subscribe(id => {
|
||||
const model = this.doc.getBlockById(id);
|
||||
const model = this.doc.getModelById(id);
|
||||
if (model instanceof RootBlockModel) {
|
||||
const newDocMeta = this.doc.workspace.meta.getDocMeta(model.doc.id);
|
||||
if (
|
||||
|
||||
@@ -45,7 +45,7 @@ export function calcDropTarget(
|
||||
*/
|
||||
allowSublist: boolean = true
|
||||
): DropTarget | null {
|
||||
const schema = model.doc.getSchemaByFlavour('affine:database');
|
||||
const schema = model.doc.schema.get('affine:database');
|
||||
const children = schema?.model.children ?? [];
|
||||
|
||||
let shouldAppendToDatabase = true;
|
||||
|
||||
@@ -62,7 +62,7 @@ export class EdgelessWatcher {
|
||||
};
|
||||
|
||||
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(
|
||||
surfaceModel[0]!.id
|
||||
) as SurfaceBlockComponent;
|
||||
|
||||
@@ -10,3 +10,7 @@
|
||||
|
||||
- [Extension](classes/Extension.md)
|
||||
- [StoreExtension](classes/StoreExtension.md)
|
||||
|
||||
## Store
|
||||
|
||||
- [Store](classes/Store.md)
|
||||
|
||||
786
blocksuite/docs/api/@blocksuite/store/classes/Store.md
Normal file
786
blocksuite/docs/api/@blocksuite/store/classes/Store.md
Normal file
@@ -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,
|
||||
"excludeExternals": true,
|
||||
"externalPattern": ["node_modules/**/*"],
|
||||
"disableSources": true
|
||||
"disableSources": true,
|
||||
"categoryOrder": ["*", "Other"]
|
||||
},
|
||||
"readme": "none",
|
||||
"plugin": ["typedoc-plugin-markdown"],
|
||||
|
||||
@@ -492,7 +492,7 @@ export class LayerManager extends GfxExtension {
|
||||
private _reset() {
|
||||
const elements = (
|
||||
this._doc
|
||||
.getStore()
|
||||
.getAllModels()
|
||||
.filter(
|
||||
model =>
|
||||
model instanceof GfxBlockElementModel &&
|
||||
@@ -798,7 +798,7 @@ export class LayerManager extends GfxExtension {
|
||||
this._disposable.add(
|
||||
store.slots.blockUpdated.subscribe(payload => {
|
||||
if (payload.type === 'add') {
|
||||
const block = store.getBlockById(payload.id)!;
|
||||
const block = store.getModelById(payload.id)!;
|
||||
|
||||
if (
|
||||
block instanceof GfxBlockElementModel &&
|
||||
@@ -810,7 +810,7 @@ export class LayerManager extends GfxExtension {
|
||||
}
|
||||
}
|
||||
if (payload.type === 'update') {
|
||||
const block = store.getBlockById(payload.id)!;
|
||||
const block = store.getModelById(payload.id)!;
|
||||
|
||||
if (
|
||||
(payload.props.key === 'index' ||
|
||||
@@ -825,7 +825,7 @@ export class LayerManager extends GfxExtension {
|
||||
}
|
||||
}
|
||||
if (payload.type === 'delete') {
|
||||
const block = store.getBlockById(payload.id);
|
||||
const block = store.getModelById(payload.id);
|
||||
|
||||
if (block instanceof GfxBlockElementModel) {
|
||||
this.delete(block as GfxBlockElementModel);
|
||||
|
||||
@@ -396,7 +396,7 @@ export abstract class GfxGroupLikeElementModel<
|
||||
for (const key of this.childIds) {
|
||||
const element =
|
||||
this.surface.getElementById(key) ||
|
||||
(this.surface.doc.getBlockById(key) as GfxBlockElementModel);
|
||||
(this.surface.doc.getModelById(key) as GfxBlockElementModel);
|
||||
|
||||
element && elements.push(element);
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ export class GfxSelectionManager extends GfxExtension {
|
||||
}
|
||||
|
||||
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)[] = [];
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ export class RangeBinding {
|
||||
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 (!model) return;
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ export class BlockComponent<
|
||||
if (this._model) {
|
||||
return this._model;
|
||||
}
|
||||
const model = this.doc.getBlockById<Model>(this.blockId);
|
||||
const model = this.doc.getModelById<Model>(this.blockId);
|
||||
if (!model) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.MissingViewModelError,
|
||||
|
||||
@@ -347,7 +347,7 @@ describe('addBlock', () => {
|
||||
})
|
||||
);
|
||||
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');
|
||||
});
|
||||
|
||||
@@ -512,7 +512,7 @@ describe('deleteBlock', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const deletedModel = doc.getBlockById('1') as BlockModel;
|
||||
const deletedModel = doc.getModelById('1') as BlockModel;
|
||||
doc.deleteBlock(deletedModel);
|
||||
|
||||
assert.deepEqual(serializCollection(doc.rootDoc).spaces[spaceId].blocks, {
|
||||
@@ -581,8 +581,8 @@ describe('deleteBlock', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const deletedModel = doc.getBlockById('2') as BlockModel;
|
||||
const deletedModelParent = doc.getBlockById('1') as BlockModel;
|
||||
const deletedModel = doc.getModelById('2') as BlockModel;
|
||||
const deletedModelParent = doc.getModelById('1') as BlockModel;
|
||||
doc.deleteBlock(deletedModel, {
|
||||
bringChildrenTo: deletedModelParent,
|
||||
});
|
||||
@@ -693,8 +693,8 @@ describe('deleteBlock', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const deletedModel = doc.getBlockById('2') as BlockModel;
|
||||
const moveToModel = doc.getBlockById('3') as BlockModel;
|
||||
const deletedModel = doc.getModelById('2') as BlockModel;
|
||||
const moveToModel = doc.getModelById('3') as BlockModel;
|
||||
doc.deleteBlock(deletedModel, {
|
||||
bringChildrenTo: moveToModel,
|
||||
});
|
||||
@@ -820,11 +820,11 @@ describe('getBlock', () => {
|
||||
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(rootModel.children[0].children.indexOf(text), 1);
|
||||
|
||||
const invalid = doc.getBlockById('😅');
|
||||
const invalid = doc.getModelById('😅');
|
||||
assert.equal(invalid, null);
|
||||
});
|
||||
|
||||
|
||||
@@ -24,7 +24,13 @@ export interface Doc {
|
||||
dispose(): void;
|
||||
|
||||
slots: {
|
||||
/**
|
||||
* This fires when the doc history is updated.
|
||||
*/
|
||||
historyUpdated: Subject<void>;
|
||||
/**
|
||||
* This fires when the doc yBlock is updated.
|
||||
*/
|
||||
yBlockUpdated: Subject<
|
||||
| {
|
||||
type: 'add';
|
||||
|
||||
@@ -61,9 +61,24 @@ export type BlockUpdatedPayload =
|
||||
|
||||
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 {
|
||||
/** @internal */
|
||||
readonly userExtensions: ExtensionType[];
|
||||
|
||||
/**
|
||||
* Group of disposable resources managed by the store
|
||||
*
|
||||
* @category Store Lifecycle
|
||||
*/
|
||||
disposableGroup = new DisposableGroup();
|
||||
|
||||
private readonly _provider: ServiceProvider;
|
||||
@@ -91,6 +106,11 @@ export class Store {
|
||||
|
||||
private readonly _schema: Schema;
|
||||
|
||||
/**
|
||||
* Slots for receiving events from the store.
|
||||
*
|
||||
* @category Store Lifecycle
|
||||
*/
|
||||
readonly slots: Doc['slots'] & {
|
||||
/** This is always triggered after `doc.load` is called. */
|
||||
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.
|
||||
*/
|
||||
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>;
|
||||
/**
|
||||
* This fires when a block is updated via API call or has just been updated from existing ydoc.
|
||||
*/
|
||||
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() {
|
||||
return this._doc.yBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link AwarenessStore} instance for current store
|
||||
*/
|
||||
get awarenessStore() {
|
||||
return this._doc.awarenessStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the di provider for current store.
|
||||
*/
|
||||
get provider() {
|
||||
return this._provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link BlobEngine} instance for current store.
|
||||
*/
|
||||
get blobSync() {
|
||||
return this.workspace.blobSync;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Doc} instance for current store.
|
||||
*/
|
||||
get doc() {
|
||||
return this._doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
get blocks() {
|
||||
return this._blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of blocks in the store
|
||||
*
|
||||
* @category Block CRUD
|
||||
*/
|
||||
get blockSize() {
|
||||
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(
|
||||
flavour: string,
|
||||
blockProps: Partial<BlockProps & Omit<BlockProps, 'flavour'>> = {},
|
||||
@@ -516,6 +501,15 @@ export class Store {
|
||||
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(
|
||||
blocks: Array<{
|
||||
flavour: string;
|
||||
@@ -539,6 +533,15 @@ export class Store {
|
||||
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(
|
||||
targetModel: BlockModel,
|
||||
props: Array<Partial<BlockProps>>,
|
||||
@@ -576,6 +579,95 @@ export class Store {
|
||||
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(
|
||||
model: BlockModel | string,
|
||||
options: {
|
||||
@@ -610,49 +702,49 @@ export class Store {
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a block by its ID
|
||||
* @param id - The block's ID
|
||||
* @returns The block instance if found, undefined otherwise
|
||||
*
|
||||
* @category Block CRUD
|
||||
*/
|
||||
getBlock(id: string): Block | undefined {
|
||||
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 {
|
||||
return this._blocks.value[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use `getBlocksByFlavour` instead.
|
||||
* Get a model by its ID
|
||||
* @param id - The model's ID
|
||||
* @returns The model instance if found, null otherwise
|
||||
*
|
||||
* @category Block CRUD
|
||||
*/
|
||||
getBlockByFlavour(blockFlavour: string | string[]) {
|
||||
return this.getBlocksByFlavour(blockFlavour).map(x => x.model);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use `getBlock` instead.
|
||||
*/
|
||||
getBlockById<Model extends BlockModel = BlockModel>(
|
||||
getModelById<Model extends BlockModel = BlockModel>(
|
||||
id: string
|
||||
): Model | null {
|
||||
return (this.getBlock(id)?.model ?? null) as Model | null;
|
||||
}
|
||||
|
||||
getStore() {
|
||||
return Object.values(this._blocks.peek()).map(block => block.model);
|
||||
}
|
||||
|
||||
getBlocksByFlavour(blockFlavour: string | string[]) {
|
||||
/**
|
||||
* Gets all blocks of specified flavour(s)
|
||||
* @param blockFlavour - Single flavour or array of flavours to filter by
|
||||
* @returns Array of matching blocks
|
||||
*
|
||||
* @category Block CRUD
|
||||
*/
|
||||
getBlocksByFlavour(blockFlavour: string | string[]): Block[] {
|
||||
const flavours =
|
||||
typeof blockFlavour === 'string' ? [blockFlavour] : blockFlavour;
|
||||
|
||||
@@ -661,21 +753,34 @@ export class Store {
|
||||
);
|
||||
}
|
||||
|
||||
getNext(block: BlockModel | string) {
|
||||
return this._getSiblings(
|
||||
block,
|
||||
(parent, index) => parent.children[index + 1] ?? null
|
||||
);
|
||||
/**
|
||||
* Get all models in the store
|
||||
* @returns Array of all models
|
||||
*
|
||||
* @category Block CRUD
|
||||
*/
|
||||
getAllModels() {
|
||||
return Object.values(this._blocks.peek()).map(block => block.model);
|
||||
}
|
||||
|
||||
getNexts(block: BlockModel | string) {
|
||||
return (
|
||||
this._getSiblings(block, (parent, index) =>
|
||||
parent.children.slice(index + 1)
|
||||
) ?? []
|
||||
);
|
||||
/**
|
||||
* Get all models of specified flavour(s)
|
||||
* @param blockFlavour - Single flavour or array of flavours to filter by
|
||||
* @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 {
|
||||
const targetId = typeof target === 'string' ? target : target.id;
|
||||
const parentId = this._crud.getParent(targetId);
|
||||
@@ -687,6 +792,13 @@ export class Store {
|
||||
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) {
|
||||
return this._getSiblings(
|
||||
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) {
|
||||
return (
|
||||
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) {
|
||||
return id in this._blocks.peek();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use `hasBlock` instead.
|
||||
* Move blocks to a new parent block
|
||||
* @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(
|
||||
blocksToMove: BlockModel[],
|
||||
newParent: BlockModel,
|
||||
@@ -755,14 +891,13 @@ export class Store {
|
||||
});
|
||||
}
|
||||
|
||||
get get() {
|
||||
return this.provider.get.bind(this.provider);
|
||||
}
|
||||
|
||||
get getOptional() {
|
||||
return this.provider.getOptional.bind(this.provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new transformer instance for the store
|
||||
* @param middlewares - Optional array of transformer middlewares
|
||||
* @returns A new Transformer instance
|
||||
*
|
||||
* @category Transformer
|
||||
*/
|
||||
getTransformer(middlewares: TransformerMiddleware[] = []) {
|
||||
return new Transformer({
|
||||
schema: this.schema,
|
||||
@@ -775,4 +910,72 @@ export class Store {
|
||||
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
|
||||
.map(tree => doc.getBlockById(tree.draft.id))
|
||||
.map(tree => doc.getModelById(tree.draft.id))
|
||||
.filter((x): x is BlockModel => x !== null)
|
||||
.map(model => toDraftModel(model));
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ let model: SurfaceBlockModel;
|
||||
|
||||
beforeEach(async () => {
|
||||
const cleanup = await setupEditor('edgeless');
|
||||
const models = doc.getBlockByFlavour('affine:surface') as SurfaceBlockModel[];
|
||||
const models = doc.getModelsByFlavour(
|
||||
'affine:surface'
|
||||
) as SurfaceBlockModel[];
|
||||
|
||||
model = models[0];
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ const snapshotTest = async (snapshotUrl: string, elementsCount: number) => {
|
||||
editor.doc = newDoc;
|
||||
await wait();
|
||||
|
||||
const surface = newDoc.getBlockByFlavour(
|
||||
const surface = newDoc.getModelsByFlavour(
|
||||
'affine:surface'
|
||||
)[0] as SurfaceBlockModel;
|
||||
const surfaceElements = [...surface['_elementModels']].map(
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { Store } from '@blocksuite/store';
|
||||
import type { TestAffineEditorContainer } from '../../index.js';
|
||||
|
||||
export function getSurface(doc: Store, editor: TestAffineEditorContainer) {
|
||||
const surfaceModel = doc.getBlockByFlavour('affine:surface');
|
||||
const surfaceModel = doc.getModelsByFlavour('affine:surface');
|
||||
|
||||
return editor.host!.view.getBlock(
|
||||
surfaceModel[0]!.id
|
||||
|
||||
@@ -489,7 +489,7 @@ export class StarterDebugMenu extends ShadowlessElement {
|
||||
);
|
||||
for (const doc of docs) {
|
||||
if (doc) {
|
||||
const noteBlock = window.doc.getBlockByFlavour('affine:note');
|
||||
const noteBlock = window.doc.getModelsByFlavour('affine:note');
|
||||
window.doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ export const database: InitFn = (collection: Workspace, id: string) => {
|
||||
// Add note block inside root block
|
||||
const noteId = doc.addBlock('affine:note', {}, rootId);
|
||||
const pId = doc.addBlock('affine:paragraph', {}, noteId);
|
||||
const model = doc.getBlockById(pId);
|
||||
const model = doc.getModelById(pId);
|
||||
if (!model) {
|
||||
throw new Error('model is not found');
|
||||
}
|
||||
@@ -40,7 +40,7 @@ export const database: InitFn = (collection: Workspace, id: string) => {
|
||||
},
|
||||
noteId
|
||||
);
|
||||
const database = doc.getBlockById(databaseId) as DatabaseBlockModel;
|
||||
const database = doc.getModelById(databaseId) as DatabaseBlockModel;
|
||||
const datasource = new DatabaseBlockDataSource(database);
|
||||
datasource.viewManager.viewAdd('table');
|
||||
database.props.title = new Text(title);
|
||||
|
||||
Reference in New Issue
Block a user