@blocksuite/store
Advanced tools
Comparing version 0.4.1-20230224181613-0101128 to 0.4.1-20230225180029-daf8dec
{ | ||
"name": "@blocksuite/store", | ||
"version": "0.4.1-20230224181613-0101128", | ||
"version": "0.4.1-20230225180029-daf8dec", | ||
"description": "BlockSuite data store built for general purpose state management.", | ||
@@ -5,0 +5,0 @@ "main": "src/index.ts", |
import { Signal } from '@blocksuite/global/utils'; | ||
import { merge } from 'merge'; | ||
import type { Awareness as YAwareness } from 'y-protocols/awareness.js'; | ||
import type { RelativePosition } from 'yjs'; | ||
import * as Y from 'yjs'; | ||
@@ -11,9 +9,11 @@ import type { Space } from './space.js'; | ||
export interface SelectionRange { | ||
id: string; | ||
anchor: RelativePosition; | ||
focus: RelativePosition; | ||
export interface UserRange { | ||
startOffset: number; | ||
endOffset: number; | ||
startBlockId: string; | ||
endBlockId: string; | ||
betweenBlockIds: string[]; | ||
} | ||
interface UserInfo { | ||
export interface UserInfo { | ||
id: number; | ||
@@ -42,3 +42,3 @@ name: string; | ||
> = { | ||
cursor?: Record<Space['prefixedId'], SelectionRange>; | ||
rangeMap?: Record<Space['prefixedId'], UserRange>; | ||
user?: UserInfo; | ||
@@ -189,10 +189,10 @@ flags: Flags; | ||
setLocalCursor(space: Space, range: SelectionRange | null) { | ||
const cursor = this.awareness.getLocalState()?.cursor ?? {}; | ||
setLocalRange(space: Space, range: UserRange | null) { | ||
const rangeMap = this.awareness.getLocalState()?.rangeMap ?? {}; | ||
if (range === null) { | ||
delete cursor[space.prefixedId]; | ||
this.awareness.setLocalStateField('cursor', cursor); | ||
delete rangeMap[space.prefixedId]; | ||
this.awareness.setLocalStateField('rangeMap', rangeMap); | ||
} else { | ||
this.awareness.setLocalStateField('cursor', { | ||
...cursor, | ||
this.awareness.setLocalStateField('rangeMap', { | ||
...rangeMap, | ||
[space.prefixedId]: range, | ||
@@ -203,4 +203,4 @@ }); | ||
getLocalCursor(space: Space): SelectionRange | undefined { | ||
return this.awareness.getLocalState()?.['cursor']?.[space.prefixedId]; | ||
getLocalRange(space: Space): UserRange | undefined { | ||
return this.awareness.getLocalState()?.['rangeMap']?.[space.prefixedId]; | ||
} | ||
@@ -243,7 +243,2 @@ | ||
private _onAwarenessMessage = (awMsg: AwarenessEvent<Flags>) => { | ||
if (awMsg.id === this.awareness.clientID) { | ||
this.store.spaces.forEach(space => this.updateLocalCursor(space)); | ||
} else { | ||
this._resetRemoteCursor(); | ||
} | ||
if (this.getFlag('enable_set_remote_flag') === true) { | ||
@@ -320,61 +315,2 @@ this._handleRemoteFlags(); | ||
private _resetRemoteCursor() { | ||
const states = this.getStates(); | ||
this.store.spaces.forEach(space => { | ||
states.forEach((awState, clientId) => { | ||
if (clientId === this.awareness.clientID) { | ||
return; | ||
} | ||
const cursor = awState.cursor?.[space.prefixedId]; | ||
if (cursor) { | ||
space.richTextAdapters.forEach(textAdapter => | ||
textAdapter.quillCursors.clearCursors() | ||
); | ||
const anchor = Y.createAbsolutePositionFromRelativePosition( | ||
cursor.anchor, | ||
space.doc | ||
); | ||
const focus = Y.createAbsolutePositionFromRelativePosition( | ||
cursor.focus, | ||
space.doc | ||
); | ||
const textAdapter = space.richTextAdapters.get(cursor.id || ''); | ||
if (anchor && focus && textAdapter) { | ||
const user: Partial<UserInfo> = awState.user || {}; | ||
const color = user.color || '#ffa500'; | ||
const name = user.name || 'other'; | ||
textAdapter.quillCursors.createCursor( | ||
clientId.toString(), | ||
name, | ||
color | ||
); | ||
textAdapter.quillCursors.moveCursor(clientId.toString(), { | ||
index: anchor.index, | ||
length: focus.index - anchor.index, | ||
}); | ||
} | ||
} | ||
}); | ||
}); | ||
} | ||
updateLocalCursor(space: Space) { | ||
const localCursor = this.getLocalCursor(space); | ||
if (!localCursor) { | ||
return; | ||
} | ||
const anchor = Y.createAbsolutePositionFromRelativePosition( | ||
localCursor.anchor, | ||
space.doc | ||
); | ||
const focus = Y.createAbsolutePositionFromRelativePosition( | ||
localCursor.focus, | ||
space.doc | ||
); | ||
if (anchor && focus) { | ||
const textAdapter = space.richTextAdapters.get(localCursor.id || ''); | ||
textAdapter?.quill.setSelection(anchor.index, focus.index - anchor.index); | ||
} | ||
} | ||
destroy() { | ||
@@ -381,0 +317,0 @@ if (this.awareness) { |
@@ -119,6 +119,2 @@ import { Signal } from '@blocksuite/global/utils'; | ||
// TODO: separate from model | ||
parentIndex?: number; | ||
depth?: number; | ||
constructor( | ||
@@ -125,0 +121,0 @@ page: Page, |
import type * as Y from 'yjs'; | ||
import type { SelectionRange } from './awareness.js'; | ||
import type { AwarenessStore } from './awareness.js'; | ||
import type { AwarenessStore, UserRange } from './awareness.js'; | ||
import type { RichTextAdapter } from './text-adapter.js'; | ||
@@ -10,3 +9,3 @@ import type { BlockSuiteDoc } from './yjs/index.js'; | ||
export interface StackItem { | ||
meta: Map<'cursor-location', SelectionRange | undefined>; | ||
meta: Map<'cursor-location', UserRange | undefined>; | ||
type: 'undo' | 'redo'; | ||
@@ -13,0 +12,0 @@ } |
@@ -462,3 +462,2 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
focus, | ||
selection, | ||
}; | ||
@@ -465,0 +464,0 @@ } |
@@ -122,3 +122,3 @@ import { isPrimitive, matchFlavours, SYS_KEYS } from '@blocksuite/global/utils'; | ||
page: Page, | ||
block: BaseBlockModel, | ||
block: BaseBlockModel | string, | ||
flavour: keyof BlockSuiteInternal.BlockModels | ||
@@ -125,0 +125,0 @@ ): boolean { |
@@ -88,2 +88,6 @@ import type { BlockTag, TagSchema } from '@blocksuite/global/database'; | ||
get history() { | ||
return this._history; | ||
} | ||
get workspace() { | ||
@@ -248,4 +252,8 @@ return this._workspace; | ||
getParentById(rootId: string, target: BaseBlockModel): BaseBlockModel | null { | ||
if (rootId === target.id) return null; | ||
getParentById( | ||
rootId: string, | ||
target: BaseBlockModel | string | ||
): BaseBlockModel | null { | ||
const targetId = typeof target === 'string' ? target : target.id; | ||
if (rootId === targetId) return null; | ||
@@ -256,3 +264,3 @@ const root = this._blockMap.get(rootId); | ||
for (const [childId] of root.childMap) { | ||
if (childId === target.id) return root; | ||
if (childId === targetId) return root; | ||
@@ -265,3 +273,3 @@ const parent = this.getParentById(childId, target); | ||
getParent(block: BaseBlockModel) { | ||
getParent(block: BaseBlockModel | string) { | ||
if (!this.root) return null; | ||
@@ -465,3 +473,7 @@ | ||
@debug('CRUD') | ||
moveBlock(model: BaseBlockModel, targetModel: BaseBlockModel, top = true) { | ||
moveBlocks( | ||
blocks: BaseBlockModel[], | ||
targetModel: BaseBlockModel, | ||
top = true | ||
) { | ||
if (this.awarenessStore.isReadonly(this)) { | ||
@@ -471,3 +483,11 @@ console.error('cannot modify data in readonly mode'); | ||
} | ||
const currentParentModel = this.getParent(model); | ||
const firstBlock = blocks[0]; | ||
const currentParentModel = this.getParent(firstBlock); | ||
// the blocks must have the same parent (siblings) | ||
if (blocks.some(block => this.getParent(block) !== currentParentModel)) { | ||
console.error('the blocks must have the same parent'); | ||
} | ||
const nextParentModel = this.getParent(targetModel); | ||
@@ -477,7 +497,8 @@ if (currentParentModel === null || nextParentModel === null) { | ||
} | ||
this.transact(() => { | ||
const yParentA = this._yBlocks.get(currentParentModel.id) as YBlock; | ||
const yChildrenA = yParentA.get('sys:children') as Y.Array<string>; | ||
const idx = yChildrenA.toArray().findIndex(id => id === model.id); | ||
yChildrenA.delete(idx); | ||
const idx = yChildrenA.toArray().findIndex(id => id === firstBlock.id); | ||
yChildrenA.delete(idx, blocks.length); | ||
const yParentB = this._yBlocks.get(nextParentModel.id) as YBlock; | ||
@@ -488,6 +509,8 @@ const yChildrenB = yParentB.get('sys:children') as Y.Array<string>; | ||
.findIndex(id => id === targetModel.id); | ||
const ids = blocks.map(block => block.id); | ||
if (top) { | ||
yChildrenB.insert(nextIdx, [model.id]); | ||
yChildrenB.insert(nextIdx, ids); | ||
} else { | ||
yChildrenB.insert(nextIdx + 1, [model.id]); | ||
yChildrenB.insert(nextIdx + 1, ids); | ||
} | ||
@@ -653,9 +676,2 @@ }); | ||
this.richTextAdapters.set(id, adapter); | ||
quill.on('selection-change', () => { | ||
const cursor = adapter.getCursor(); | ||
if (!cursor) return; | ||
this.awarenessStore.setLocalCursor(this, { ...cursor, id }); | ||
}); | ||
}; | ||
@@ -737,3 +753,3 @@ | ||
'cursor-location', | ||
this.awarenessStore.getLocalCursor(this) | ||
this.awarenessStore.getLocalRange(this) | ||
); | ||
@@ -746,8 +762,8 @@ } | ||
private _historyPopObserver = (event: { stackItem: StackItem }) => { | ||
const cursor = event.stackItem.meta.get('cursor-location'); | ||
if (!cursor) { | ||
const range = event.stackItem.meta.get('cursor-location'); | ||
if (!range) { | ||
return; | ||
} | ||
this.awarenessStore.setLocalCursor(this, cursor); | ||
this.awarenessStore.setLocalRange(this, range); | ||
this._historyObserver(); | ||
@@ -754,0 +770,0 @@ }; |
159592
4599