@blocksuite/blocks
Advanced tools
Comparing version 0.2.24 to 0.3.0-alpha.0
import { LitElement } from 'lit'; | ||
import { Store } from '@blocksuite/store'; | ||
import type { Store } from '@blocksuite/store'; | ||
export declare class DebugMenu extends LitElement { | ||
@@ -10,2 +10,3 @@ store: Store; | ||
_mode: 'page' | 'edgeless'; | ||
get space(): import("@blocksuite/store").Space; | ||
private _onToggleConnection; | ||
@@ -12,0 +13,0 @@ private _convertToList; |
@@ -128,2 +128,5 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
} | ||
get space() { | ||
return this.store.space; | ||
} | ||
_onToggleConnection() { | ||
@@ -145,6 +148,5 @@ if (this.connected === true) { | ||
return; | ||
const store = block.host.store; | ||
// @ts-ignore | ||
const model = store.getBlockById(block.model.id); | ||
convertToList(this.store, model, listType, ''); | ||
const { space } = block.host; | ||
const model = space.getBlockById(block.model.id); | ||
convertToList(this.space, model, listType, ''); | ||
} | ||
@@ -160,4 +162,4 @@ _onDelete() { | ||
return; | ||
this.store.captureSync(); | ||
this.store.updateBlock(block, { type }); | ||
this.space.captureSync(); | ||
this.space.updateBlock(block, { type }); | ||
} | ||
@@ -170,11 +172,11 @@ _onSwitchMode() { | ||
_onAddGroup() { | ||
const root = this.store.root; | ||
const root = this.space.root; | ||
if (!root) | ||
return; | ||
const pageId = root.id; | ||
this.store.captureSync(); | ||
this.space.captureSync(); | ||
const count = root.children.length; | ||
const xywh = `[0,${count * 60},720,480]`; | ||
const groupId = this.store.addBlock({ flavour: 'group', xywh }, pageId); | ||
this.store.addBlock({ flavour: 'paragraph' }, groupId); | ||
const groupId = this.space.addBlock({ flavour: 'affine:group', xywh }, pageId); | ||
this.space.addBlock({ flavour: 'affine:paragraph' }, groupId); | ||
} | ||
@@ -188,5 +190,5 @@ _onExportHtml() { | ||
firstUpdated() { | ||
this.store.signals.historyUpdated.on(() => { | ||
this.canUndo = this.store.canUndo; | ||
this.canRedo = this.store.canRedo; | ||
this.space.signals.historyUpdated.on(() => { | ||
this.canUndo = this.space.canUndo; | ||
this.canRedo = this.space.canRedo; | ||
}); | ||
@@ -202,3 +204,3 @@ } | ||
tabindex="-1" | ||
@click=${() => this.store.undo()} | ||
@click=${() => this.space.undo()} | ||
> | ||
@@ -212,3 +214,3 @@ ${icons.undo} | ||
tabindex="-1" | ||
@click=${() => this.store.redo()} | ||
@click=${() => this.space.redo()} | ||
> | ||
@@ -215,0 +217,0 @@ ${icons.redo} |
@@ -1,3 +0,3 @@ | ||
import type { BaseBlockModel, Store } from '@blocksuite/store'; | ||
import { Quill, type RangeStatic } from 'quill'; | ||
import type { BaseBlockModel, Space } from '@blocksuite/store'; | ||
import type { Quill, RangeStatic } from 'quill'; | ||
interface BindingContext { | ||
@@ -29,4 +29,4 @@ collapsed: boolean; | ||
declare type KeyboardBindingHandler = (this: KeyboardEventThis, range: RangeStatic, context: BindingContext) => boolean; | ||
export declare function createKeyboardBindings(store: Store, model: BaseBlockModel): KeyboardBindings; | ||
export declare function createKeyboardBindings(space: Space, model: BaseBlockModel): KeyboardBindings; | ||
export {}; | ||
//# sourceMappingURL=keyboard.d.ts.map |
@@ -7,3 +7,3 @@ import { ALLOW_DEFAULT, focusNextBlock, focusPreviousBlock, getCurrentRange, isCollapsedAtBlockStart, isMultiBlockRange, noop, PREVENT_DEFAULT, } from '../utils'; | ||
} | ||
export function createKeyboardBindings(store, model) { | ||
export function createKeyboardBindings(space, model) { | ||
function enterMarkdownMatch(range, context) { | ||
@@ -22,5 +22,5 @@ const { prefix } = context; | ||
const isEnd = isAtBlockEnd(this.quill); | ||
const parent = store.getParent(model); | ||
const parent = space.getParent(model); | ||
const isLastChild = parent?.lastChild() === model; | ||
const isEmptyList = model.flavour === 'list' && model.text?.length === 0; | ||
const isEmptyList = model.flavour === 'affine:list' && model.text?.length === 0; | ||
const index = this.quill.getSelection()?.index || 0; | ||
@@ -34,3 +34,3 @@ // Some block should treat Enter as soft enter | ||
{ | ||
flavour: 'paragraph', | ||
flavour: 'affine:paragraph', | ||
type: 'quote', | ||
@@ -40,8 +40,8 @@ }, | ||
if (isEmptyList && | ||
parent?.flavour === 'group' && | ||
parent?.flavour === 'affine:group' && | ||
model.children.length === 0) { | ||
handleLineStartBackspace(store, model); | ||
handleLineStartBackspace(space, model); | ||
} | ||
else if (isEmptyList && isLastChild) { | ||
handleUnindent(store, model, index); | ||
handleUnindent(space, model, index); | ||
} | ||
@@ -55,3 +55,3 @@ else if (isEnd) { | ||
if (shouldSoftEnter) { | ||
softEnter.bind(this)(); | ||
onSoftEnter.bind(this)(); | ||
} | ||
@@ -63,3 +63,3 @@ else { | ||
} | ||
handleBlockEndEnter(store, model); | ||
handleBlockEndEnter(space, model); | ||
} | ||
@@ -72,6 +72,6 @@ } | ||
if (isSoftEnterBlock) { | ||
softEnter.bind(this)(); | ||
onSoftEnter.bind(this)(); | ||
} | ||
else { | ||
handleBlockSplit(store, model, index); | ||
handleBlockSplit(space, model, index); | ||
} | ||
@@ -81,19 +81,19 @@ } | ||
} | ||
function softEnter() { | ||
function onSoftEnter() { | ||
const index = this.quill.getSelection()?.index || 0; | ||
handleSoftEnter(store, model, index); | ||
handleSoftEnter(space, model, index); | ||
this.quill.setSelection(index + 1, 0); | ||
return PREVENT_DEFAULT; | ||
} | ||
function indent() { | ||
function onIndent() { | ||
const index = this.quill.getSelection()?.index || 0; | ||
handleIndent(store, model, index); | ||
handleIndent(space, model, index); | ||
return PREVENT_DEFAULT; | ||
} | ||
function unindent() { | ||
function onUnindent() { | ||
const index = this.quill.getSelection()?.index || 0; | ||
handleUnindent(store, model, index); | ||
handleUnindent(space, model, index); | ||
return PREVENT_DEFAULT; | ||
} | ||
function keyUp(range) { | ||
function onKeyUp(range) { | ||
if (range.index >= 0) { | ||
@@ -104,3 +104,3 @@ return handleKeyUp(model, this.quill.root); | ||
} | ||
function keyDown(range) { | ||
function onKeyDown(range) { | ||
if (range.index >= 0) { | ||
@@ -111,3 +111,3 @@ return handleKeyDown(model, this.quill.root); | ||
} | ||
function keyLeft(range) { | ||
function onKeyLeft(range) { | ||
// range.length === 0 means collapsed selection, if have range length, the cursor is in the start of text | ||
@@ -120,3 +120,3 @@ if (range.index === 0 && range.length === 0) { | ||
} | ||
function keyRight(range) { | ||
function onKeyRight(range) { | ||
const textLength = this.quill.getText().length; | ||
@@ -129,12 +129,12 @@ if (range.index + 1 === textLength) { | ||
} | ||
function space(range, context) { | ||
function onSpace(range, context) { | ||
const { quill } = this; | ||
const { prefix } = context; | ||
return tryMatchSpaceHotkey(store, model, quill, prefix, range); | ||
return tryMatchSpaceHotkey(space, model, quill, prefix, range); | ||
} | ||
function backspace() { | ||
function onBackspace() { | ||
// To workaround uncontrolled behavior when deleting character at block start, | ||
// in this case backspace should be handled in quill. | ||
if (isCollapsedAtBlockStart(this.quill)) { | ||
handleLineStartBackspace(store, model); | ||
handleLineStartBackspace(space, model); | ||
return PREVENT_DEFAULT; | ||
@@ -164,7 +164,7 @@ } | ||
shiftKey: true, | ||
handler: softEnter, | ||
handler: onSoftEnter, | ||
}, | ||
tab: { | ||
key: 'tab', | ||
handler: indent, | ||
handler: onIndent, | ||
}, | ||
@@ -174,3 +174,3 @@ shiftTab: { | ||
shiftKey: true, | ||
handler: unindent, | ||
handler: onUnindent, | ||
}, | ||
@@ -182,3 +182,3 @@ // https://github.com/quilljs/quill/blob/v1.3.7/modules/keyboard.js#L249-L282 | ||
prefix: /^(\d+\.|-|\*|\[ ?\]|\[x\]|(#){1,6}|>)$/, | ||
handler: space, | ||
handler: onSpace, | ||
}, | ||
@@ -189,7 +189,7 @@ 'list autofill shift': { | ||
prefix: /^(\d+\.|-|\*|\[ ?\]|\[x\]|(#){1,6}|>)$/, | ||
handler: space, | ||
handler: onSpace, | ||
}, | ||
backspace: { | ||
key: 'backspace', | ||
handler: backspace, | ||
handler: onBackspace, | ||
}, | ||
@@ -199,3 +199,3 @@ up: { | ||
shiftKey: false, | ||
handler: keyUp, | ||
handler: onKeyUp, | ||
}, | ||
@@ -205,3 +205,3 @@ down: { | ||
shiftKey: false, | ||
handler: keyDown, | ||
handler: onKeyDown, | ||
}, | ||
@@ -211,3 +211,3 @@ 'embed left': { | ||
shiftKey: false, | ||
handler: keyLeft, | ||
handler: onKeyLeft, | ||
}, | ||
@@ -217,3 +217,3 @@ right: { | ||
shiftKey: false, | ||
handler: keyRight, | ||
handler: onKeyRight, | ||
}, | ||
@@ -220,0 +220,0 @@ }; |
@@ -1,4 +0,4 @@ | ||
import { Store } from '@blocksuite/store'; | ||
import type { Space } from '@blocksuite/store'; | ||
import './link-node'; | ||
export declare const createLink: (store: Store, e: KeyboardEvent) => Promise<void>; | ||
export declare const createLink: (space: Space, e: KeyboardEvent) => Promise<void>; | ||
//# sourceMappingURL=create-link.d.ts.map |
@@ -5,3 +5,3 @@ import { assertExists, getRichTextByModel, getStartModelBySelection, isRangeSelection, } from '../../utils'; | ||
import { MockSelectNode } from './mock-select-node'; | ||
export const createLink = async (store, e) => { | ||
export const createLink = async (space, e) => { | ||
if (!isRangeSelection()) { | ||
@@ -24,3 +24,3 @@ // TODO maybe allow user creating a link with text | ||
if (format?.link) { | ||
store.captureSync(); | ||
space.captureSync(); | ||
const { index, length } = range; | ||
@@ -52,5 +52,5 @@ startModel.text?.format(index, length, { link: false }); | ||
const link = linkState.link; | ||
store.captureSync(); | ||
space.captureSync(); | ||
startModel.text?.format(range.index, range.length, { link }); | ||
}; | ||
//# sourceMappingURL=create-link.js.map |
@@ -64,3 +64,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
const model = getModelByElement(this); | ||
const store = model.store; | ||
const { space } = model; | ||
if (text) { | ||
@@ -70,3 +70,4 @@ // Replace the text | ||
const offset = blot.offset(); | ||
store.captureSync(); | ||
space.captureSync(); | ||
// TODO save the format of the original text | ||
model.text?.delete(offset, blot.length()); | ||
@@ -76,3 +77,3 @@ model.text?.insert(text, offset, { link }); | ||
else { | ||
store.captureSync(); | ||
space.captureSync(); | ||
model.text?.format(blot.offset(), blot.length(), { link }); | ||
@@ -79,0 +80,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { LinkDetail } from './link-popover'; | ||
import type { LinkDetail } from './link-popover'; | ||
export declare const showLinkPopover: ({ anchorEl, container, text, link, showMask, interactionKind, signal, }: { | ||
@@ -3,0 +3,0 @@ anchorEl: HTMLElement; |
@@ -1,13 +0,13 @@ | ||
import { Store } from '@blocksuite/store'; | ||
import Quill from 'quill'; | ||
import { Space } from '@blocksuite/store'; | ||
import type Quill from 'quill'; | ||
import { ExtendedModel } from '../utils'; | ||
export declare function handleBlockEndEnter(store: Store, model: ExtendedModel): void; | ||
export declare function handleSoftEnter(store: Store, model: ExtendedModel, index: number): void; | ||
export declare function handleBlockSplit(store: Store, model: ExtendedModel, splitIndex: number): void; | ||
export declare function handleIndent(store: Store, model: ExtendedModel, offset: number): void; | ||
export declare function handleUnindent(store: Store, model: ExtendedModel, offset: number): Promise<void>; | ||
export declare function handleLineStartBackspace(store: Store, model: ExtendedModel): void; | ||
export declare function handleBlockEndEnter(space: Space, model: ExtendedModel): void; | ||
export declare function handleSoftEnter(space: Space, model: ExtendedModel, index: number): void; | ||
export declare function handleBlockSplit(space: Space, model: ExtendedModel, splitIndex: number): void; | ||
export declare function handleIndent(space: Space, model: ExtendedModel, offset: number): void; | ||
export declare function handleUnindent(space: Space, model: ExtendedModel, offset: number): Promise<void>; | ||
export declare function handleLineStartBackspace(space: Space, model: ExtendedModel): void; | ||
export declare function handleKeyUp(model: ExtendedModel, editableContainer: Element): boolean; | ||
export declare function handleKeyDown(model: ExtendedModel, textContainer: HTMLElement): boolean; | ||
export declare function tryMatchSpaceHotkey(store: Store, model: ExtendedModel, quill: Quill, prefix: string, range: { | ||
export declare function tryMatchSpaceHotkey(space: Space, model: ExtendedModel, quill: Quill, prefix: string, range: { | ||
index: number; | ||
@@ -14,0 +14,0 @@ length: number; |
// operations used in rich-text level | ||
import { Text } from '@blocksuite/store'; | ||
import { assertExists, getRichTextByModel, getContainerByModel, getPreviousBlock, ALLOW_DEFAULT, caretRangeFromPoint, focusNextBlock, focusPreviousBlock, getNextBlock, Point, PREVENT_DEFAULT, asyncFocusRichText, convertToList, convertToParagraph, } from '../utils'; | ||
export function handleBlockEndEnter(store, model) { | ||
const parent = store.getParent(model); | ||
export function handleBlockEndEnter(space, model) { | ||
const parent = space.getParent(model); | ||
const index = parent?.children.indexOf(model); | ||
if (parent && index !== undefined && index > -1) { | ||
// make adding text block by enter a standalone operation | ||
store.captureSync(); | ||
space.captureSync(); | ||
let id = ''; | ||
if (model.flavour === 'list') { | ||
if (model.flavour === 'affine:list') { | ||
const blockProps = { | ||
@@ -17,6 +17,6 @@ flavour: model.flavour, | ||
if (model.children.length === 0) { | ||
id = store.addBlock(blockProps, parent, index + 1); | ||
id = space.addBlock(blockProps, parent, index + 1); | ||
} | ||
else { | ||
id = store.addBlock(blockProps, model, 0); | ||
id = space.addBlock(blockProps, model, 0); | ||
} | ||
@@ -29,34 +29,34 @@ } | ||
}; | ||
id = store.addBlock(blockProps, parent, index + 1); | ||
id = space.addBlock(blockProps, parent, index + 1); | ||
} | ||
id && asyncFocusRichText(store, id); | ||
id && asyncFocusRichText(space, id); | ||
} | ||
} | ||
export function handleSoftEnter(store, model, index) { | ||
store.captureSync(); | ||
export function handleSoftEnter(space, model, index) { | ||
space.captureSync(); | ||
model.text?.insert('\n', index); | ||
} | ||
export function handleBlockSplit(store, model, splitIndex) { | ||
export function handleBlockSplit(space, model, splitIndex) { | ||
if (!(model.text instanceof Text)) | ||
return; | ||
const parent = store.getParent(model); | ||
const parent = space.getParent(model); | ||
if (!parent) | ||
return; | ||
const [left, right] = model.text.split(splitIndex); | ||
store.captureSync(); | ||
store.markTextSplit(model.text, left, right); | ||
store.updateBlock(model, { text: left }); | ||
space.captureSync(); | ||
space.markTextSplit(model.text, left, right); | ||
space.updateBlock(model, { text: left }); | ||
let newParent = parent; | ||
let newBlockIndex = newParent.children.indexOf(model) + 1; | ||
if (model.flavour === 'list' && model.children.length > 0) { | ||
if (model.flavour === 'affine:list' && model.children.length > 0) { | ||
newParent = model; | ||
newBlockIndex = 0; | ||
} | ||
const id = store.addBlock({ flavour: model.flavour, text: right, type: model.type }, newParent, newBlockIndex); | ||
asyncFocusRichText(store, id); | ||
const id = space.addBlock({ flavour: model.flavour, text: right, type: model.type }, newParent, newBlockIndex); | ||
asyncFocusRichText(space, id); | ||
} | ||
export function handleIndent(store, model, offset) { | ||
const previousSibling = store.getPreviousSibling(model); | ||
export function handleIndent(space, model, offset) { | ||
const previousSibling = space.getPreviousSibling(model); | ||
if (previousSibling) { | ||
store.captureSync(); | ||
space.captureSync(); | ||
const blockProps = { | ||
@@ -68,7 +68,7 @@ flavour: model.flavour, | ||
}; | ||
store.deleteBlock(model); | ||
const id = store.addBlock(blockProps, previousSibling); | ||
space.deleteBlock(model); | ||
const id = space.addBlock(blockProps, previousSibling); | ||
// FIXME: after quill onload | ||
requestAnimationFrame(() => { | ||
const block = store.getBlockById(id); | ||
const block = space.getBlockById(id); | ||
assertExists(block); | ||
@@ -80,7 +80,7 @@ const richText = getRichTextByModel(block); | ||
} | ||
export async function handleUnindent(store, model, offset) { | ||
const parent = store.getParent(model); | ||
if (!parent || parent?.flavour === 'group') | ||
export async function handleUnindent(space, model, offset) { | ||
const parent = space.getParent(model); | ||
if (!parent || parent?.flavour === 'affine:group') | ||
return; | ||
const grandParent = store.getParent(parent); | ||
const grandParent = space.getParent(parent); | ||
if (!grandParent) | ||
@@ -95,8 +95,8 @@ return; | ||
}; | ||
store.captureSync(); | ||
store.deleteBlock(model); | ||
const id = store.addBlock(blockProps, grandParent, index + 1); | ||
space.captureSync(); | ||
space.deleteBlock(model); | ||
const id = space.addBlock(blockProps, grandParent, index + 1); | ||
// FIXME: after quill onload | ||
requestAnimationFrame(() => { | ||
const block = store.getBlockById(id); | ||
const block = space.getBlockById(id); | ||
assertExists(block); | ||
@@ -107,24 +107,24 @@ const richText = getRichTextByModel(block); | ||
} | ||
export function handleLineStartBackspace(store, model) { | ||
export function handleLineStartBackspace(space, model) { | ||
// When deleting at line start of a paragraph block, | ||
// firstly switch it to normal text, then delete this empty block. | ||
if (model.flavour === 'paragraph') { | ||
if (model.flavour === 'affine:paragraph') { | ||
if (model.type !== 'text') { | ||
store.captureSync(); | ||
store.updateBlock(model, { type: 'text' }); | ||
space.captureSync(); | ||
space.updateBlock(model, { type: 'text' }); | ||
} | ||
else { | ||
const parent = store.getParent(model); | ||
if (!parent || parent?.flavour === 'group') { | ||
const parent = space.getParent(model); | ||
if (!parent || parent?.flavour === 'affine:group') { | ||
const container = getContainerByModel(model); | ||
const previousSibling = getPreviousBlock(container, model.id); | ||
if (previousSibling) { | ||
store.captureSync(); | ||
space.captureSync(); | ||
previousSibling.text?.join(model.text); | ||
store.deleteBlock(model); | ||
asyncFocusRichText(store, previousSibling.id); | ||
space.deleteBlock(model); | ||
asyncFocusRichText(space, previousSibling.id); | ||
} | ||
} | ||
else { | ||
const grandParent = store.getParent(parent); | ||
const grandParent = space.getParent(parent); | ||
if (!grandParent) | ||
@@ -139,5 +139,5 @@ return; | ||
}; | ||
store.captureSync(); | ||
store.deleteBlock(model); | ||
store.addBlock(blockProps, grandParent, index + 1); | ||
space.captureSync(); | ||
space.deleteBlock(model); | ||
space.addBlock(blockProps, grandParent, index + 1); | ||
} | ||
@@ -148,10 +148,10 @@ } | ||
// switch it to normal paragraph block. | ||
else if (model.flavour === 'list') { | ||
const parent = store.getParent(model); | ||
else if (model.flavour === 'affine:list') { | ||
const parent = space.getParent(model); | ||
if (!parent) | ||
return; | ||
const index = parent.children.indexOf(model); | ||
store.captureSync(); | ||
space.captureSync(); | ||
const blockProps = { | ||
flavour: 'paragraph', | ||
flavour: 'affine:paragraph', | ||
type: 'text', | ||
@@ -161,5 +161,5 @@ text: model?.text?.clone(), | ||
}; | ||
store.deleteBlock(model); | ||
const id = store.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(store, id); | ||
space.deleteBlock(model); | ||
const id = space.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(space, id); | ||
} | ||
@@ -185,3 +185,3 @@ } | ||
// FIXME: Then it will turn the input into the div | ||
if (preNodeModel?.flavour === 'group') { | ||
if (preNodeModel?.flavour === 'affine:group') { | ||
document.querySelector('.affine-default-page-block-title').focus(); | ||
@@ -244,3 +244,3 @@ } | ||
if (!nextRange || !textContainer.contains(nextRange.startContainer)) { | ||
focusNextBlock(model, new Point(newRange.startContainer.parentElement?.offsetLeft || left, bottom)); | ||
focusNextBlock(model, new Point(textContainer.getBoundingClientRect().left || left, bottom)); | ||
return PREVENT_DEFAULT; | ||
@@ -252,3 +252,3 @@ } | ||
} | ||
export function tryMatchSpaceHotkey(store, model, quill, prefix, range) { | ||
export function tryMatchSpaceHotkey(space, model, quill, prefix, range) { | ||
const [, offset] = quill.getLine(range.index); | ||
@@ -262,3 +262,3 @@ if (offset > prefix.length) { | ||
case '[ ]': | ||
isConverted = convertToList(store, model, 'todo', prefix, { | ||
isConverted = convertToList(space, model, 'todo', prefix, { | ||
checked: false, | ||
@@ -268,3 +268,3 @@ }); | ||
case '[x]': | ||
isConverted = convertToList(store, model, 'todo', prefix, { | ||
isConverted = convertToList(space, model, 'todo', prefix, { | ||
checked: true, | ||
@@ -275,27 +275,27 @@ }); | ||
case '*': | ||
isConverted = convertToList(store, model, 'bulleted', prefix); | ||
isConverted = convertToList(space, model, 'bulleted', prefix); | ||
break; | ||
case '#': | ||
isConverted = convertToParagraph(store, model, 'h1', prefix); | ||
isConverted = convertToParagraph(space, model, 'h1', prefix); | ||
break; | ||
case '##': | ||
isConverted = convertToParagraph(store, model, 'h2', prefix); | ||
isConverted = convertToParagraph(space, model, 'h2', prefix); | ||
break; | ||
case '###': | ||
isConverted = convertToParagraph(store, model, 'h3', prefix); | ||
isConverted = convertToParagraph(space, model, 'h3', prefix); | ||
break; | ||
case '####': | ||
isConverted = convertToParagraph(store, model, 'h4', prefix); | ||
isConverted = convertToParagraph(space, model, 'h4', prefix); | ||
break; | ||
case '#####': | ||
isConverted = convertToParagraph(store, model, 'h5', prefix); | ||
isConverted = convertToParagraph(space, model, 'h5', prefix); | ||
break; | ||
case '######': | ||
isConverted = convertToParagraph(store, model, 'h6', prefix); | ||
isConverted = convertToParagraph(space, model, 'h6', prefix); | ||
break; | ||
case '>': | ||
isConverted = convertToParagraph(store, model, 'quote', prefix); | ||
isConverted = convertToParagraph(space, model, 'quote', prefix); | ||
break; | ||
default: | ||
isConverted = convertToList(store, model, 'numbered', prefix); | ||
isConverted = convertToList(space, model, 'numbered', prefix); | ||
} | ||
@@ -302,0 +302,0 @@ return isConverted ? PREVENT_DEFAULT : ALLOW_DEFAULT; |
import { LitElement } from 'lit'; | ||
import Quill from 'quill'; | ||
import type { BaseBlockModel } from '@blocksuite/store'; | ||
import { BlockHost } from '../utils'; | ||
import type { BlockHost } from '../utils'; | ||
export declare class RichText extends LitElement { | ||
@@ -6,0 +6,0 @@ static styles: import("lit").CSSResult; |
@@ -32,4 +32,4 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
const { host, model, _textContainer } = this; | ||
const { store } = host; | ||
const keyboardBindings = createKeyboardBindings(store, model); | ||
const { space } = host; | ||
const keyboardBindings = createKeyboardBindings(space, model); | ||
this.quill = new Quill(_textContainer, { | ||
@@ -61,6 +61,10 @@ modules: { | ||
if (retain !== undefined) { | ||
const nextTextLeaf = this.quill.getLeaf(retain + Number(delta.ops[1]?.insert.toString().length) + 1); | ||
const parentElement = nextTextLeaf[0]?.domNode?.parentElement; | ||
const currentLeaf = this.quill.getLeaf(retain + Number(delta.ops[1]?.insert.toString().length)); | ||
const nextLeaf = this.quill.getLeaf(retain + Number(delta.ops[1]?.insert.toString().length) + 1); | ||
const currentParentElement = currentLeaf[0]?.domNode?.parentElement; | ||
const currentEmbedElement = currentParentElement?.closest(selector); | ||
const nextParentElement = nextLeaf[0]?.domNode?.parentElement; | ||
const nextEmbedElement = nextParentElement?.closest(selector); | ||
const insertedString = delta.ops[1]?.insert.toString(); | ||
if (parentElement && !parentElement.closest(selector)) { | ||
if (nextEmbedElement && nextEmbedElement !== currentEmbedElement) { | ||
this.quill.deleteText(retain, delta.ops[1]?.insert.toString().length); | ||
@@ -77,3 +81,3 @@ // @ts-ignore | ||
} | ||
if (!nextTextLeaf[0] && insertedString) { | ||
if (!nextEmbedElement && insertedString) { | ||
this.quill.deleteText(retain, delta.ops[1]?.insert.toString().length); | ||
@@ -94,4 +98,4 @@ this.quill.insertEmbed(retain, 'text', | ||
}); | ||
store.attachRichText(model.id, this.quill); | ||
store.awareness.updateLocalCursor(); | ||
space.attachRichText(model.id, this.quill); | ||
space.awareness.updateLocalCursor(); | ||
this.model.propsUpdated.on(() => this.requestUpdate()); | ||
@@ -101,3 +105,3 @@ } | ||
super.disconnectedCallback(); | ||
this.host.store.detachRichText(this.model.id); | ||
this.host.space.detachRichText(this.model.id); | ||
} | ||
@@ -104,0 +108,0 @@ render() { |
@@ -1,3 +0,3 @@ | ||
import { BaseBlockModel } from '@blocksuite/store'; | ||
import Quill from 'quill'; | ||
import type { BaseBlockModel } from '@blocksuite/store'; | ||
import type Quill from 'quill'; | ||
export declare class Shortcuts { | ||
@@ -4,0 +4,0 @@ static match(quill: Quill, model: BaseBlockModel, prefix: string): boolean; |
@@ -41,3 +41,3 @@ import { ALLOW_DEFAULT, PREVENT_DEFAULT } from '..'; | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -70,3 +70,3 @@ bold: true, | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -97,3 +97,3 @@ bold: true, | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -124,3 +124,3 @@ italic: true, | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -151,3 +151,3 @@ strike: true, | ||
model.text?.insert(' ', selection.index); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -179,3 +179,3 @@ underline: true, | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -203,3 +203,3 @@ code: true, | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -227,3 +227,3 @@ link: annotatedText, | ||
model.text?.insert(' ', selection.index); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(start, hrefText.length, { | ||
@@ -230,0 +230,0 @@ link: hrefLink.slice(1, hrefLink.length - 1), |
@@ -1,8 +0,8 @@ | ||
import { Store } from '@blocksuite/store'; | ||
import type { Space } from '@blocksuite/store'; | ||
import type { Quill } from 'quill'; | ||
import { ExtendedModel } from './types'; | ||
export declare function asyncFocusRichText(store: Store, id: string): void; | ||
import type { ExtendedModel } from './types'; | ||
export declare function asyncFocusRichText(space: Space, id: string): void; | ||
export declare function isCollapsedAtBlockStart(quill: Quill): boolean; | ||
export declare function convertToList(store: Store, model: ExtendedModel, listType: 'bulleted' | 'numbered' | 'todo', prefix: string, otherProperties?: Record<string, unknown>): boolean; | ||
export declare function convertToParagraph(store: Store, model: ExtendedModel, type: 'paragraph' | 'quote' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6', prefix: string): boolean; | ||
export declare function convertToList(space: Space, model: ExtendedModel, listType: 'bulleted' | 'numbered' | 'todo', prefix: string, otherProperties?: Record<string, unknown>): boolean; | ||
export declare function convertToParagraph(space: Space, model: ExtendedModel, type: 'affine:paragraph' | 'quote' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6', prefix: string): boolean; | ||
//# sourceMappingURL=common-operations.d.ts.map |
// XXX: workaround quill lifecycle issue | ||
export function asyncFocusRichText(store, id) { | ||
export function asyncFocusRichText(space, id) { | ||
setTimeout(() => { | ||
const adapter = store.richTextAdapters.get(id); | ||
const adapter = space.richTextAdapters.get(id); | ||
adapter?.quill.focus(); | ||
@@ -11,8 +11,8 @@ }); | ||
} | ||
export function convertToList(store, model, listType, prefix, otherProperties) { | ||
if (model.flavour === 'list' && model['type'] === listType) { | ||
export function convertToList(space, model, listType, prefix, otherProperties) { | ||
if (model.flavour === 'affine:list' && model['type'] === listType) { | ||
return false; | ||
} | ||
if (model.flavour === 'paragraph') { | ||
const parent = store.getParent(model); | ||
if (model.flavour === 'affine:paragraph') { | ||
const parent = space.getParent(model); | ||
if (!parent) | ||
@@ -22,6 +22,6 @@ return false; | ||
model.text?.insert(' ', prefix.length); | ||
store.captureSync(); | ||
space.captureSync(); | ||
model.text?.delete(0, prefix.length + 1); | ||
const blockProps = { | ||
flavour: 'list', | ||
flavour: 'affine:list', | ||
type: listType, | ||
@@ -32,20 +32,20 @@ text: model?.text?.clone(), | ||
}; | ||
store.deleteBlock(model); | ||
const id = store.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(store, id); | ||
space.deleteBlock(model); | ||
const id = space.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(space, id); | ||
} | ||
else if (model.flavour === 'list' && model['type'] !== listType) { | ||
else if (model.flavour === 'affine:list' && model['type'] !== listType) { | ||
model.text?.insert(' ', prefix.length); | ||
store.captureSync(); | ||
space.captureSync(); | ||
model.text?.delete(0, prefix.length + 1); | ||
store.updateBlock(model, { type: listType }); | ||
space.updateBlock(model, { type: listType }); | ||
} | ||
return true; | ||
} | ||
export function convertToParagraph(store, model, type, prefix) { | ||
if (model.flavour === 'paragraph' && model['type'] === type) { | ||
export function convertToParagraph(space, model, type, prefix) { | ||
if (model.flavour === 'affine:paragraph' && model['type'] === type) { | ||
return false; | ||
} | ||
if (model.flavour !== 'paragraph') { | ||
const parent = store.getParent(model); | ||
if (model.flavour !== 'affine:paragraph') { | ||
const parent = space.getParent(model); | ||
if (!parent) | ||
@@ -55,6 +55,6 @@ return false; | ||
model.text?.insert(' ', prefix.length); | ||
store.captureSync(); | ||
space.captureSync(); | ||
model.text?.delete(0, prefix.length + 1); | ||
const blockProps = { | ||
flavour: 'paragraph', | ||
flavour: 'affine:paragraph', | ||
type: type, | ||
@@ -64,11 +64,11 @@ text: model?.text?.clone(), | ||
}; | ||
store.deleteBlock(model); | ||
const id = store.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(store, id); | ||
space.deleteBlock(model); | ||
const id = space.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(space, id); | ||
} | ||
else if (model.flavour === 'paragraph' && model['type'] !== type) { | ||
else if (model.flavour === 'affine:paragraph' && model['type'] !== type) { | ||
model.text?.insert(' ', prefix.length); | ||
store.captureSync(); | ||
space.captureSync(); | ||
model.text?.delete(0, prefix.length + 1); | ||
store.updateBlock(model, { type: type }); | ||
space.updateBlock(model, { type: type }); | ||
} | ||
@@ -75,0 +75,0 @@ return true; |
@@ -6,3 +6,3 @@ import { html } from 'lit'; | ||
switch (model.flavour) { | ||
case 'paragraph': | ||
case 'affine:paragraph': | ||
return html ` | ||
@@ -14,3 +14,3 @@ <paragraph-block | ||
`; | ||
case 'list': | ||
case 'affine:list': | ||
return html ` | ||
@@ -22,3 +22,3 @@ <list-block | ||
`; | ||
case 'group': | ||
case 'affine:group': | ||
return html ` | ||
@@ -25,0 +25,0 @@ <group-block |
@@ -12,6 +12,6 @@ import hotkeys from 'hotkeys-js'; | ||
addListener(hotkey, listener, scope) { | ||
this._hotkeys(hotkey, { scope: scope ?? 'page' }, listener); | ||
this._hotkeys(hotkey, { scope: scope ?? 'affine:page' }, listener); | ||
} | ||
removeListener(hotkey, scope) { | ||
this._hotkeys.unbind((Array.isArray(hotkey) ? hotkey : [hotkey]).join(','), scope ?? 'page'); | ||
this._hotkeys.unbind((Array.isArray(hotkey) ? hotkey : [hotkey]).join(','), scope ?? 'affine:page'); | ||
} | ||
@@ -22,3 +22,3 @@ disableHotkey() { | ||
enableHotkey() { | ||
this._setScope('page'); | ||
this._setScope('affine:page'); | ||
} | ||
@@ -25,0 +25,0 @@ /** |
@@ -1,4 +0,4 @@ | ||
import { BaseBlockModel } from '@blocksuite/store'; | ||
import { DefaultPageBlockComponent } from '../..'; | ||
import { RichText } from '../rich-text/rich-text'; | ||
import type { BaseBlockModel } from '@blocksuite/store'; | ||
import type { DefaultPageBlockComponent } from '../..'; | ||
import type { RichText } from '../rich-text/rich-text'; | ||
declare type ElementTagName = keyof HTMLElementTagNameMap; | ||
@@ -5,0 +5,0 @@ export declare function getBlockById<T extends ElementTagName>(id: string, ele?: Element): HTMLElementTagNameMap[T] | null; |
@@ -61,8 +61,7 @@ import { BLOCK_ID_ATTR as ATTR } from './consts'; | ||
if (previousBlock.model.children.length) { | ||
let firstChildren = previousBlock.model.children[previousBlock.children.length - 1]; | ||
while (firstChildren.children.length) { | ||
firstChildren = | ||
firstChildren.children[firstChildren.children.length - 1]; | ||
let firstChild = previousBlock.model.children[previousBlock.model.children.length - 1]; | ||
while (firstChild.children.length) { | ||
firstChild = firstChild.children[firstChild.children.length - 1]; | ||
} | ||
return firstChildren; | ||
return firstChild; | ||
} | ||
@@ -76,9 +75,9 @@ return previousBlock.model; | ||
export function getDefaultPageBlock(model) { | ||
assertExists(model.store.root); | ||
const page = document.querySelector(`[${ATTR}="${model.store.root.id}"]`); | ||
assertExists(model.space.root); | ||
const page = document.querySelector(`[${ATTR}="${model.space.root.id}"]`); | ||
return page; | ||
} | ||
export function getContainerByModel(model) { | ||
assertExists(model.store.root); | ||
const page = document.querySelector(`[${ATTR}="${model.store.root.id}"]`); | ||
assertExists(model.space.root); | ||
const page = document.querySelector(`[${ATTR}="${model.space.root.id}"]`); | ||
const container = page.closest('editor-container'); | ||
@@ -89,7 +88,7 @@ assertExists(container); | ||
export function getBlockElementByModel(model) { | ||
assertExists(model.store.root); | ||
const page = document.querySelector(`[${ATTR}="${model.store.root.id}"]`); | ||
assertExists(model.space.root); | ||
const page = document.querySelector(`[${ATTR}="${model.space.root.id}"]`); | ||
if (!page) | ||
return null; | ||
if (model.id === model.store.root.id) { | ||
if (model.id === model.space.root.id) { | ||
return page; | ||
@@ -135,3 +134,3 @@ } | ||
const blockElement = getBlockElementByModel(block.model); | ||
const mainElelment = block.model.flavour === 'page' | ||
const mainElelment = block.model.flavour === 'affine:page' | ||
? blockElement?.querySelector('.affine-default-page-block-title-container') | ||
@@ -138,0 +137,0 @@ : blockElement?.querySelector('rich-text'); |
@@ -1,6 +0,6 @@ | ||
import type { BaseBlockModel, Store } from '@blocksuite/store'; | ||
import { RichText } from '../rich-text/rich-text'; | ||
import { SelectionInfo, SelectionPosition } from './types'; | ||
import type { BaseBlockModel, Space } from '@blocksuite/store'; | ||
import type { RichText } from '../rich-text/rich-text'; | ||
import type { SelectionInfo, SelectionPosition } from './types'; | ||
import type { SelectionEvent } from './gesture'; | ||
export declare function focusRichText(position: SelectionPosition, editableContainer: Element): void; | ||
export declare function focusRichText(position: SelectionPosition, editableContainer: Element): Promise<void>; | ||
export declare function focusPreviousBlock(model: BaseBlockModel, position?: SelectionPosition): void; | ||
@@ -15,7 +15,7 @@ export declare function focusNextBlock(model: BaseBlockModel, position?: SelectionPosition): void; | ||
export declare function isMultiBlockRange(range: Range): boolean; | ||
export declare function getSelectInfo(store: Store): SelectionInfo; | ||
export declare function getSelectInfo(space: Space): SelectionInfo; | ||
export declare function handleNativeRangeDragMove(startRange: Range | null, e: SelectionEvent): void; | ||
export declare function isBlankArea(e: SelectionEvent): boolean; | ||
export declare function handleNativeRangeClick(store: Store, e: SelectionEvent): void; | ||
export declare function handleNativeRangeDblClick(store: Store, e: SelectionEvent): void; | ||
export declare function handleNativeRangeClick(space: Space, e: SelectionEvent): void; | ||
export declare function handleNativeRangeDblClick(space: Space, e: SelectionEvent): void; | ||
/** | ||
@@ -27,4 +27,6 @@ * left first search all leaf text nodes | ||
**/ | ||
export declare function leftFirstSearchLeafNodes(node: Node, leafNodes?: Node[]): Node[]; | ||
export declare function leftFirstSearchLeafNodes(node: Node, leafNodes?: Node[]): Text[]; | ||
export declare function getLastTextNode(node: Node): Text | undefined; | ||
export declare function getFirstTextNode(node: Node): Text; | ||
export declare function getSplicedTitle(title: HTMLInputElement): string; | ||
//# sourceMappingURL=selection.d.ts.map |
import { assertExists, caretRangeFromPoint, matchFlavours } from './std'; | ||
import { getBlockElementByModel, getDefaultPageBlock, getContainerByModel, getPreviousBlock, getNextBlock, getModelsByRange, getCurrentRange, getQuillIndexByNativeSelection, } from './query'; | ||
import { Point, Rect } from './rect'; | ||
const SCROLL_THRESHOLD = 100; | ||
// /[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]/u | ||
@@ -24,2 +25,10 @@ const notStrictCharacterReg = /[^\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]/u; | ||
if (newRange) { | ||
if (!(newRange.endContainer.nodeType === Node.TEXT_NODE)) { | ||
const lastTextNode = getLastTextNode(newRange.endContainer); | ||
if (lastTextNode) { | ||
newRange = document.createRange(); | ||
newRange.setStart(lastTextNode, lastTextNode.textContent?.length || 0); | ||
newRange.setEnd(lastTextNode, lastTextNode.textContent?.length || 0); | ||
} | ||
} | ||
range.setEnd(newRange.endContainer, newRange.endOffset); | ||
@@ -39,2 +48,10 @@ } | ||
if (newRange) { | ||
if (!(newRange.startContainer.nodeType === Node.TEXT_NODE)) { | ||
const firstTextNode = getFirstTextNode(newRange.startContainer); | ||
if (firstTextNode) { | ||
newRange = document.createRange(); | ||
newRange.setStart(firstTextNode, 0); | ||
newRange.setEnd(firstTextNode, 0); | ||
} | ||
} | ||
range.setStart(newRange.endContainer, newRange.endOffset); | ||
@@ -48,15 +65,14 @@ } | ||
} | ||
export function focusRichText(position, editableContainer) { | ||
let { top, left, bottom, right } = Rect.fromDom(editableContainer); | ||
const [oldTop, oldBottom] = [top, bottom]; | ||
async function sleep(delay = 0) { | ||
return new Promise(resolve => { | ||
setTimeout(() => { | ||
resolve(null); | ||
}, delay); | ||
}); | ||
} | ||
export async function focusRichText(position, editableContainer) { | ||
// TODO optimize how get scroll container | ||
const scrollContainer = editableContainer.closest('.affine-editor-container'); | ||
const { top, left, bottom, right } = Rect.fromDom(editableContainer); | ||
const { clientHeight } = document.documentElement; | ||
// TODO: improve the logic | ||
if (top + 20 > clientHeight || bottom < 20) { | ||
editableContainer.scrollIntoView(); | ||
const newRect = Rect.fromDom(editableContainer); | ||
top = newRect.top; | ||
left = newRect.left; | ||
bottom = newRect.bottom; | ||
right = newRect.right; | ||
} | ||
const lineHeight = Number(window.getComputedStyle(editableContainer).lineHeight.replace(/\D+$/, '')) || 16; | ||
@@ -68,12 +84,28 @@ let range = null; | ||
let newLeft = x; | ||
if (oldBottom <= y) { | ||
newTop = bottom - lineHeight / 2; | ||
if (bottom <= y) { | ||
let finalBottom = bottom; | ||
if (bottom < SCROLL_THRESHOLD && scrollContainer) { | ||
scrollContainer.scrollTop = | ||
scrollContainer.scrollTop - SCROLL_THRESHOLD + bottom; | ||
// set scroll may has a animation, wait for over | ||
await sleep(); | ||
finalBottom = editableContainer.getBoundingClientRect().bottom; | ||
} | ||
newTop = finalBottom - lineHeight / 2; | ||
} | ||
if (oldTop >= y) { | ||
newTop = top + lineHeight / 2; | ||
if (bottom >= y) { | ||
let finalTop = top; | ||
if (scrollContainer && top > clientHeight - SCROLL_THRESHOLD) { | ||
scrollContainer.scrollTop = | ||
scrollContainer.scrollTop + (top + SCROLL_THRESHOLD - clientHeight); | ||
// set scroll may has a animation, wait for over | ||
await sleep(); | ||
finalTop = editableContainer.getBoundingClientRect().top; | ||
} | ||
newTop = finalTop + lineHeight / 2; | ||
} | ||
if (x < left) { | ||
if (x <= left) { | ||
newLeft = left + 1; | ||
} | ||
if (x > right) { | ||
if (x >= right) { | ||
newLeft = right - 1; | ||
@@ -194,3 +226,3 @@ } | ||
const model = models[i]; | ||
const parent = model.store.getParent(model); | ||
const parent = model.space.getParent(model); | ||
const block = { id: model.id, children: [] }; | ||
@@ -217,4 +249,4 @@ if (!parent || !parentMap.has(parent.id)) { | ||
} | ||
export function getSelectInfo(store) { | ||
if (!store.root) { | ||
export function getSelectInfo(space) { | ||
if (!space.root) { | ||
return { | ||
@@ -228,3 +260,3 @@ type: 'None', | ||
let selectedModels = []; | ||
const page = getDefaultPageBlock(store.root); | ||
const page = getDefaultPageBlock(space.root); | ||
const { state } = page.selection; | ||
@@ -293,3 +325,3 @@ const nativeSelection = window.getSelection(); | ||
} | ||
export function handleNativeRangeClick(store, e) { | ||
export function handleNativeRangeClick(space, e) { | ||
const range = caretRangeFromPoint(e.raw.clientX, e.raw.clientY); | ||
@@ -312,6 +344,6 @@ const startContainer = range?.startContainer; | ||
else if (isBlankAreaAfterLastBlock(startContainer)) { | ||
const { root } = store; | ||
const { root } = space; | ||
const lastChild = root?.lastChild(); | ||
assertExists(lastChild); | ||
if (matchFlavours(lastChild, ['paragraph', 'list'])) { | ||
if (matchFlavours(lastChild, ['affine:paragraph', 'affine:list'])) { | ||
const block = getBlockElementByModel(lastChild); | ||
@@ -324,3 +356,3 @@ if (!block) | ||
} | ||
export function handleNativeRangeDblClick(store, e) { | ||
export function handleNativeRangeDblClick(space, e) { | ||
const selection = window.getSelection(); | ||
@@ -499,2 +531,8 @@ if (selection && selection.isCollapsed && selection.anchorNode) { | ||
} | ||
export function getLastTextNode(node) { | ||
return leftFirstSearchLeafNodes(node).pop(); | ||
} | ||
export function getFirstTextNode(node) { | ||
return leftFirstSearchLeafNodes(node)[0]; | ||
} | ||
export function getSplicedTitle(title) { | ||
@@ -501,0 +539,0 @@ const text = [...title.value]; |
@@ -1,6 +0,6 @@ | ||
import type { BaseBlockModel } from '@blocksuite/store'; | ||
import { Utils } from '@blocksuite/store'; | ||
import type { Detail } from './types'; | ||
export declare function assertExists<T>(val: T | null | undefined): asserts val is T; | ||
export declare function assertFlavours(model: BaseBlockModel, allowed: string[]): void; | ||
export declare function matchFlavours(model: BaseBlockModel, expected: string[]): boolean; | ||
export declare const assertFlavours: typeof Utils.assertFlavours; | ||
export declare const matchFlavours: typeof Utils.matchFlavours; | ||
export declare function caretRangeFromPoint(x: number, y: number): Range | null; | ||
@@ -7,0 +7,0 @@ export declare function almostEqual(a: number, b: number): boolean; |
@@ -0,14 +1,8 @@ | ||
import { Utils } from '@blocksuite/store'; | ||
// workaround ts(2775) | ||
export function assertExists(val) { | ||
if (val === null || val === undefined) { | ||
throw new Error('val does not exist'); | ||
} | ||
Utils.assertExists(val); | ||
} | ||
export function assertFlavours(model, allowed) { | ||
if (!allowed.includes(model.flavour)) { | ||
throw new Error(`model flavour ${model.flavour} is not allowed`); | ||
} | ||
} | ||
export function matchFlavours(model, expected) { | ||
return expected.includes(model.flavour); | ||
} | ||
export const assertFlavours = Utils.assertFlavours; | ||
export const matchFlavours = Utils.matchFlavours; | ||
const IS_FIREFOX = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; | ||
@@ -15,0 +9,0 @@ export function caretRangeFromPoint(x, y) { |
@@ -1,3 +0,3 @@ | ||
import type { BaseBlockModel, Store } from '@blocksuite/store'; | ||
import { Point } from './rect'; | ||
import type { BaseBlockModel, Store, Space } from '@blocksuite/store'; | ||
import type { Point } from './rect'; | ||
export declare type SelectionPosition = 'start' | 'end' | Point; | ||
@@ -11,2 +11,3 @@ export declare type SelectionOptions = { | ||
store: Store; | ||
space: Space; | ||
flavour: string; | ||
@@ -13,0 +14,0 @@ } |
@@ -7,2 +7,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
}; | ||
/// <reference types="vite/client" /> | ||
import { LitElement, html, css, unsafeCSS } from 'lit'; | ||
@@ -9,0 +10,0 @@ import { customElement, property } from 'lit/decorators.js'; |
@@ -1,4 +0,4 @@ | ||
import { Store, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
import { Space, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
export interface GroupBlockProps extends IBaseBlockProps { | ||
flavour: 'group'; | ||
flavour: 'affine:group'; | ||
/** packed field */ | ||
@@ -8,6 +8,6 @@ xywh: string; | ||
export declare class GroupBlockModel extends BaseBlockModel implements GroupBlockProps { | ||
flavour: "group"; | ||
flavour: "affine:group"; | ||
xywh: string; | ||
constructor(store: Store, props: Partial<GroupBlockModel>); | ||
constructor(space: Space, props: Partial<GroupBlockModel>); | ||
} | ||
//# sourceMappingURL=group-model.d.ts.map |
import { BaseBlockModel } from '@blocksuite/store'; | ||
export class GroupBlockModel extends BaseBlockModel { | ||
constructor(store, props) { | ||
super(store, props); | ||
this.flavour = 'group'; | ||
constructor(space, props) { | ||
super(space, props); | ||
this.flavour = 'affine:group'; | ||
this.xywh = props.xywh ?? '[0,0,720,480]'; | ||
@@ -7,0 +7,0 @@ } |
@@ -7,2 +7,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
}; | ||
/// <reference types="vite/client" /> | ||
import { LitElement, html, css, unsafeCSS } from 'lit'; | ||
@@ -34,4 +35,4 @@ import { customElement, property } from 'lit/decorators.js'; | ||
return; | ||
this.host.store.captureSync(); | ||
this.host.store.updateBlock(this.model, { | ||
this.host.space.captureSync(); | ||
this.host.space.updateBlock(this.model, { | ||
checked: !this.model.checked, | ||
@@ -38,0 +39,0 @@ }); |
@@ -1,5 +0,5 @@ | ||
import { Store, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
import { Space, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
declare type ListType = 'bulleted' | 'numbered' | 'todo'; | ||
export interface ListBlockProps extends IBaseBlockProps { | ||
flavour: 'list'; | ||
flavour: 'affine:list'; | ||
type: ListType; | ||
@@ -9,6 +9,6 @@ checked: boolean; | ||
export declare class ListBlockModel extends BaseBlockModel implements ListBlockProps { | ||
flavour: "list"; | ||
flavour: "affine:list"; | ||
type: ListType; | ||
checked: boolean; | ||
constructor(store: Store, props: Partial<ListBlockProps>); | ||
constructor(space: Space, props: Partial<ListBlockProps>); | ||
block2html(childText: string, _previousSiblingId: string, _nextSiblingId: string, begin?: number, end?: number): string; | ||
@@ -15,0 +15,0 @@ } |
import { BaseBlockModel } from '@blocksuite/store'; | ||
export class ListBlockModel extends BaseBlockModel { | ||
constructor(store, props) { | ||
super(store, props); | ||
this.flavour = 'list'; | ||
constructor(space, props) { | ||
super(space, props); | ||
this.flavour = 'affine:list'; | ||
this.type = props.type ?? 'bulleted'; | ||
@@ -11,4 +11,4 @@ this.checked = props.checked ?? false; | ||
let text = super.block2html(childText, _previousSiblingId, _nextSiblingId, begin, end); | ||
const previousSiblingBlock = this.store.getBlockById(_previousSiblingId); | ||
const nextSiblingBlock = this.store.getBlockById(_nextSiblingId); | ||
const previousSiblingBlock = this.space.getBlockById(_previousSiblingId); | ||
const nextSiblingBlock = this.space.getBlockById(_nextSiblingId); | ||
switch (this.type) { | ||
@@ -15,0 +15,0 @@ case 'bulleted': |
@@ -1,2 +0,2 @@ | ||
import { ListBlockModel } from '../list-model'; | ||
import type { ListBlockModel } from '../list-model'; | ||
export declare function getListIcon({ model, index, deep, onClick, }: { | ||
@@ -3,0 +3,0 @@ model: ListBlockModel; |
@@ -1,3 +0,3 @@ | ||
import { BlockHost } from '../../__internal__'; | ||
import { ListBlockModel } from '../list-model'; | ||
import type { BlockHost } from '../../__internal__'; | ||
import type { ListBlockModel } from '../list-model'; | ||
export declare const getListInfo: (host: BlockHost, model: ListBlockModel) => { | ||
@@ -4,0 +4,0 @@ deep: number; |
const getIndex = (host, model) => { | ||
const siblings = host.store.getParent(model)?.children || []; | ||
const siblings = host.space.getParent(model)?.children || []; | ||
const fakeIndex = siblings.findIndex(v => v === model); | ||
@@ -20,6 +20,6 @@ // fakeIndex is the index of the model in the parent's children array | ||
let deep = 0; | ||
let parent = host.store.getParent(model); | ||
let parent = host.space.getParent(model); | ||
while (parent?.flavour === model.flavour) { | ||
deep++; | ||
parent = host.store.getParent(parent); | ||
parent = host.space.getParent(parent); | ||
} | ||
@@ -26,0 +26,0 @@ return deep; |
import { LitElement } from 'lit'; | ||
import { Signal, Store } from '@blocksuite/store'; | ||
import { Signal, Space, Store } from '@blocksuite/store'; | ||
import type { PageBlockModel } from '..'; | ||
@@ -13,3 +13,4 @@ import { type BlockHost, SelectionPosition } from '../../__internal__'; | ||
store: Store; | ||
flavour: "page"; | ||
get space(): Space; | ||
flavour: "affine:page"; | ||
selection: DefaultSelectionManager; | ||
@@ -16,0 +17,0 @@ lastSelectionPosition: SelectionPosition; |
@@ -7,2 +7,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
}; | ||
/// <reference types="vite/client" /> | ||
import { LitElement, html, css, unsafeCSS } from 'lit'; | ||
@@ -14,4 +15,4 @@ import { customElement, property, query, state } from 'lit/decorators.js'; | ||
import { DefaultSelectionManager } from './selection-manager'; | ||
import { batchUpdateTextType, bindCommonHotkey, handleBackspace, handleBlockSelectionBatchDelete, handleSelectAll, removeCommonHotKey, tryUpdateGroupSize, updateTextType, } from '../utils'; | ||
import style from './style.css'; | ||
import { batchUpdateTextType, bindCommonHotkey, handleBackspace, handleBlockSelectionBatchDelete, handleSelectAll, removeCommonHotKey, tryUpdateGroupSize, updateTextType, } from '../utils'; | ||
// https://stackoverflow.com/a/2345915 | ||
@@ -76,3 +77,3 @@ function focusTextEnd(input) { | ||
super(...arguments); | ||
this.flavour = 'page'; | ||
this.flavour = 'affine:page'; | ||
this.lastSelectionPosition = 'start'; | ||
@@ -93,6 +94,9 @@ this.frameSelectionRect = null; | ||
} | ||
get space() { | ||
return this.store.space; | ||
} | ||
_bindHotkeys() { | ||
const { store } = this; | ||
const { space } = this; | ||
const { BACKSPACE, SELECT_ALL, H1, H2, H3, H4, H5, H6, SHIFT_UP, SHIFT_DOWN, NUMBERED_LIST, BULLETED, TEXT, } = HOTKEYS; | ||
bindCommonHotkey(store); | ||
bindCommonHotkey(space); | ||
hotkey.addListener(BACKSPACE, e => { | ||
@@ -106,3 +110,3 @@ const { state } = this.selection; | ||
const title = getSplicedTitle(target); | ||
store.updateBlock(this.model, { title }); | ||
space.updateBlock(this.model, { title }); | ||
} | ||
@@ -116,7 +120,7 @@ // collapsed delete | ||
if (state.type === 'native') { | ||
handleBackspace(store, e); | ||
handleBackspace(space, e); | ||
} | ||
else if (state.type === 'block') { | ||
const { selectedRichTexts } = state; | ||
handleBlockSelectionBatchDelete(store, selectedRichTexts.map(richText => richText.model)); | ||
handleBlockSelectionBatchDelete(space, selectedRichTexts.map(richText => richText.model)); | ||
state.clear(); | ||
@@ -132,11 +136,11 @@ this.signals.updateSelectedRects.emit([]); | ||
}); | ||
hotkey.addListener(H1, () => this._updateType('paragraph', 'h1', store)); | ||
hotkey.addListener(H2, () => this._updateType('paragraph', 'h2', store)); | ||
hotkey.addListener(H3, () => this._updateType('paragraph', 'h3', store)); | ||
hotkey.addListener(H4, () => this._updateType('paragraph', 'h4', store)); | ||
hotkey.addListener(H5, () => this._updateType('paragraph', 'h5', store)); | ||
hotkey.addListener(H6, () => this._updateType('paragraph', 'h6', store)); | ||
hotkey.addListener(NUMBERED_LIST, () => this._updateType('list', 'numbered', store)); | ||
hotkey.addListener(BULLETED, () => this._updateType('list', 'bulleted', store)); | ||
hotkey.addListener(TEXT, () => this._updateType('paragraph', 'text', store)); | ||
hotkey.addListener(H1, () => this._updateType('affine:paragraph', 'h1', space)); | ||
hotkey.addListener(H2, () => this._updateType('affine:paragraph', 'h2', space)); | ||
hotkey.addListener(H3, () => this._updateType('affine:paragraph', 'h3', space)); | ||
hotkey.addListener(H4, () => this._updateType('affine:paragraph', 'h4', space)); | ||
hotkey.addListener(H5, () => this._updateType('affine:paragraph', 'h5', space)); | ||
hotkey.addListener(H6, () => this._updateType('affine:paragraph', 'h6', space)); | ||
hotkey.addListener(NUMBERED_LIST, () => this._updateType('affine:list', 'numbered', space)); | ||
hotkey.addListener(BULLETED, () => this._updateType('affine:list', 'bulleted', space)); | ||
hotkey.addListener(TEXT, () => this._updateType('affine:paragraph', 'text', space)); | ||
hotkey.addListener(SHIFT_UP, e => { | ||
@@ -175,3 +179,3 @@ // TODO expand selection up | ||
_onTitleKeyDown(e) { | ||
const hasContent = !this.store.isEmpty; | ||
const hasContent = !this.space.isEmpty; | ||
if (e.key === 'Enter' && hasContent) { | ||
@@ -184,33 +188,33 @@ assertExists(this._title.selectionStart); | ||
const props = { | ||
flavour: 'paragraph', | ||
text: new Text(this.store, contentRight), | ||
flavour: 'affine:paragraph', | ||
text: new Text(this.space, contentRight), | ||
}; | ||
const newFirstParagraphId = this.store.addBlock(props, defaultGroup, 0); | ||
this.store.updateBlock(this.model, { title: contentLeft }); | ||
asyncFocusRichText(this.store, newFirstParagraphId); | ||
const newFirstParagraphId = this.space.addBlock(props, defaultGroup, 0); | ||
this.space.updateBlock(this.model, { title: contentLeft }); | ||
asyncFocusRichText(this.space, newFirstParagraphId); | ||
} | ||
else if (e.key === 'ArrowDown' && hasContent) { | ||
e.preventDefault(); | ||
asyncFocusRichText(this.store, this.model.children[0].children[0].id); | ||
asyncFocusRichText(this.space, this.model.children[0].children[0].id); | ||
} | ||
} | ||
_onTitleInput(e) { | ||
const { store } = this; | ||
const { space } = this; | ||
if (!this.model.id) { | ||
const title = e.target.value; | ||
const pageId = store.addBlock({ flavour: 'page', title }); | ||
const groupId = store.addBlock({ flavour: 'group' }, pageId); | ||
store.addBlock({ flavour: 'paragraph' }, groupId); | ||
const pageId = space.addBlock({ flavour: 'affine:page', title }); | ||
const groupId = space.addBlock({ flavour: 'affine:group' }, pageId); | ||
space.addBlock({ flavour: 'affine:paragraph' }, groupId); | ||
return; | ||
} | ||
const title = e.target.value; | ||
store.updateBlock(this.model, { title }); | ||
space.updateBlock(this.model, { title }); | ||
} | ||
_updateType(flavour, type, store) { | ||
_updateType(flavour, type, space) { | ||
const { state } = this.selection; | ||
if (state.selectedRichTexts.length > 0) { | ||
batchUpdateTextType(flavour, store, state.selectedRichTexts.map(richText => richText.model), type); | ||
batchUpdateTextType(flavour, space, state.selectedRichTexts.map(richText => richText.model), type); | ||
} | ||
else { | ||
updateTextType(flavour, type, store); | ||
updateTextType(flavour, type, space); | ||
} | ||
@@ -228,3 +232,3 @@ } | ||
if (changedProperties.has('mouseRoot') && changedProperties.has('store')) { | ||
this.selection = new DefaultSelectionManager(this.store, this.mouseRoot, this.signals); | ||
this.selection = new DefaultSelectionManager(this.space, this.mouseRoot, this.signals); | ||
} | ||
@@ -250,7 +254,7 @@ super.update(changedProperties); | ||
}); | ||
tryUpdateGroupSize(this.store, 1); | ||
tryUpdateGroupSize(this.space, 1); | ||
this.addEventListener('keydown', e => { | ||
if (e.ctrlKey || e.metaKey || e.shiftKey) | ||
return; | ||
tryUpdateGroupSize(this.store, 1); | ||
tryUpdateGroupSize(this.space, 1); | ||
}); | ||
@@ -257,0 +261,0 @@ // TMP: clear selected rects on scroll |
@@ -1,4 +0,4 @@ | ||
import { Store } from '@blocksuite/store'; | ||
import type { Space } from '@blocksuite/store'; | ||
import { SelectionEvent } from '../../__internal__'; | ||
import { RichText } from '../../__internal__/rich-text/rich-text'; | ||
import type { RichText } from '../../__internal__/rich-text/rich-text'; | ||
import type { DefaultPageSignals } from './default-page-block'; | ||
@@ -24,3 +24,3 @@ declare type PageSelectionType = 'native' | 'block' | 'none'; | ||
export declare class DefaultSelectionManager { | ||
store: Store; | ||
space: Space; | ||
state: PageSelectionState; | ||
@@ -30,3 +30,3 @@ private _container; | ||
private _signals; | ||
constructor(store: Store, container: HTMLElement, signals: DefaultPageSignals); | ||
constructor(space: Space, container: HTMLElement, signals: DefaultPageSignals); | ||
private _onBlockSelectionDragStart; | ||
@@ -33,0 +33,0 @@ private _onBlockSelectionDragMove; |
import { initMouseEventHandlers, caretRangeFromPoint, resetNativeSelection, assertExists, noop, handleNativeRangeDragMove, isBlankArea, handleNativeRangeClick, isPageTitle, handleNativeRangeDblClick, } from '../../__internal__'; | ||
import { repairerContextMenuRange } from '../utils/cursor'; | ||
import { repairContextMenuRange } from '../utils/cursor'; | ||
function intersects(rect, selectionRect) { | ||
@@ -63,3 +63,3 @@ return (rect.left < selectionRect.right && | ||
export class DefaultSelectionManager { | ||
constructor(store, container, signals) { | ||
constructor(space, container, signals) { | ||
this.state = new PageSelectionState('none'); | ||
@@ -103,3 +103,3 @@ this._onContainerDragStart = (e) => { | ||
return; | ||
handleNativeRangeClick(this.store, e); | ||
handleNativeRangeClick(this.space, e); | ||
}; | ||
@@ -113,6 +113,6 @@ this._onContainerDblClick = (e) => { | ||
return; | ||
handleNativeRangeDblClick(this.store, e); | ||
handleNativeRangeDblClick(this.space, e); | ||
}; | ||
this._onContainerContextMenu = (e) => { | ||
repairerContextMenuRange(e); | ||
repairContextMenuRange(e); | ||
}; | ||
@@ -125,3 +125,3 @@ this._onContainerMouseMove = (e) => { | ||
}; | ||
this.store = store; | ||
this.space = space; | ||
this._signals = signals; | ||
@@ -128,0 +128,0 @@ this._container = container; |
import { type TemplateResult } from 'lit'; | ||
import { BaseBlockModel } from '@blocksuite/store'; | ||
import type { BaseBlockModel } from '@blocksuite/store'; | ||
import type { EdgelessSelectionManager, ViewportState } from './selection-manager'; | ||
@@ -4,0 +4,0 @@ import { BlockHost } from '../../__internal__'; |
import { LitElement } from 'lit'; | ||
import { Signal, Store } from '@blocksuite/store'; | ||
import { Signal, Space, Store } from '@blocksuite/store'; | ||
import type { PageBlockModel } from '../..'; | ||
@@ -7,3 +7,3 @@ import { BlockHost } from '../../__internal__'; | ||
export interface EdgelessContainer extends HTMLElement { | ||
readonly store: Store; | ||
readonly space: Space; | ||
readonly viewport: ViewportState; | ||
@@ -20,2 +20,3 @@ readonly mouseRoot: HTMLElement; | ||
store: Store; | ||
get space(): Space; | ||
flavour: "edgeless"; | ||
@@ -22,0 +23,0 @@ mouseRoot: HTMLElement; |
@@ -7,2 +7,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
}; | ||
/// <reference types="vite/client" /> | ||
import { LitElement, html, unsafeCSS, css } from 'lit'; | ||
@@ -14,4 +15,4 @@ import { customElement, property, state } from 'lit/decorators.js'; | ||
import { EdgelessSelectionManager, ViewportState, } from './selection-manager'; | ||
import { bindCommonHotkey, handleBackspace, removeCommonHotKey, tryUpdateGroupSize, updateTextType, } from '../utils'; | ||
import style from './style.css'; | ||
import { bindCommonHotkey, handleBackspace, removeCommonHotKey, tryUpdateGroupSize, updateTextType, } from '../utils'; | ||
let EdgelessPageBlockComponent = class EdgelessPageBlockComponent extends LitElement { | ||
@@ -28,18 +29,21 @@ constructor() { | ||
} | ||
get space() { | ||
return this.store.space; | ||
} | ||
_bindHotkeys() { | ||
const { store } = this; | ||
hotkey.addListener(HOTKEYS.BACKSPACE, this._handleBackspace.bind(this), this.flavour); | ||
hotkey.addListener(HOTKEYS.H1, () => this._updateType('paragraph', 'h1', store)); | ||
hotkey.addListener(HOTKEYS.H2, () => this._updateType('paragraph', 'h2', store)); | ||
hotkey.addListener(HOTKEYS.H3, () => this._updateType('paragraph', 'h3', store)); | ||
hotkey.addListener(HOTKEYS.H4, () => this._updateType('paragraph', 'h4', store)); | ||
hotkey.addListener(HOTKEYS.H5, () => this._updateType('paragraph', 'h5', store)); | ||
hotkey.addListener(HOTKEYS.H6, () => this._updateType('paragraph', 'h6', store)); | ||
hotkey.addListener(HOTKEYS.NUMBERED_LIST, () => this._updateType('list', 'numbered', store)); | ||
hotkey.addListener(HOTKEYS.BULLETED, () => this._updateType('list', 'bulleted', store)); | ||
hotkey.addListener(HOTKEYS.TEXT, () => this._updateType('paragraph', 'text', store)); | ||
bindCommonHotkey(store); | ||
const { space } = this; | ||
hotkey.addListener(HOTKEYS.BACKSPACE, this._handleBackspace.bind(this)); | ||
hotkey.addListener(HOTKEYS.H1, () => this._updateType('affine:paragraph', 'h1', space)); | ||
hotkey.addListener(HOTKEYS.H2, () => this._updateType('affine:paragraph', 'h2', space)); | ||
hotkey.addListener(HOTKEYS.H3, () => this._updateType('affine:paragraph', 'h3', space)); | ||
hotkey.addListener(HOTKEYS.H4, () => this._updateType('affine:paragraph', 'h4', space)); | ||
hotkey.addListener(HOTKEYS.H5, () => this._updateType('affine:paragraph', 'h5', space)); | ||
hotkey.addListener(HOTKEYS.H6, () => this._updateType('affine:paragraph', 'h6', space)); | ||
hotkey.addListener(HOTKEYS.NUMBERED_LIST, () => this._updateType('affine:list', 'numbered', space)); | ||
hotkey.addListener(HOTKEYS.BULLETED, () => this._updateType('affine:list', 'bulleted', space)); | ||
hotkey.addListener(HOTKEYS.TEXT, () => this._updateType('affine:paragraph', 'text', space)); | ||
bindCommonHotkey(space); | ||
} | ||
_updateType(flavour, type, store) { | ||
updateTextType(flavour, type, store); | ||
_updateType(flavour, type, space) { | ||
updateTextType(flavour, type, space); | ||
} | ||
@@ -52,3 +56,3 @@ _removeHotkeys() { | ||
if (this._selection.blockSelectionState.type === 'single') { | ||
handleBackspace(this.store, e); | ||
handleBackspace(this.space, e); | ||
} | ||
@@ -88,3 +92,3 @@ } | ||
this.signals.updateSelection.on(() => this.requestUpdate()); | ||
this._historyDisposable = this.store.signals.historyUpdated.on(() => { | ||
this._historyDisposable = this.space.signals.historyUpdated.on(() => { | ||
this._clearSelection(); | ||
@@ -96,3 +100,3 @@ }); | ||
return; | ||
tryUpdateGroupSize(this.store, this.viewport.zoom); | ||
tryUpdateGroupSize(this.space, this.viewport.zoom); | ||
}); | ||
@@ -99,0 +103,0 @@ requestAnimationFrame(() => { |
@@ -1,3 +0,3 @@ | ||
import { GroupBlockModel } from '../../group-block'; | ||
import { EdgelessContainer } from './edgeless-page-block'; | ||
import type { GroupBlockModel } from '../../group-block'; | ||
import type { EdgelessContainer } from './edgeless-page-block'; | ||
interface NoneBlockSelectionState { | ||
@@ -47,3 +47,3 @@ type: 'none'; | ||
constructor(container: EdgelessContainer); | ||
private get _store(); | ||
private get _space(); | ||
private get _blocks(); | ||
@@ -50,0 +50,0 @@ get isActive(): boolean; |
import { initMouseEventHandlers, resetNativeSelection, noop, caretRangeFromPoint, handleNativeRangeDragMove, handleNativeRangeClick, } from '../../__internal__'; | ||
import { getSelectionBoxBound, initWheelEventHandlers, pick } from './utils'; | ||
import { repairerContextMenuRange } from '../utils/cursor'; | ||
import { repairContextMenuRange } from '../utils/cursor'; | ||
const MIN_ZOOM = 0.3; | ||
@@ -107,3 +107,3 @@ export class ViewportState { | ||
const { zoom } = this._container.viewport; | ||
this._store.updateBlock(block, { | ||
this._space.updateBlock(block, { | ||
xywh: JSON.stringify([ | ||
@@ -154,3 +154,3 @@ modelX + e.delta.x / zoom, | ||
this._onContainerContextMenu = (e) => { | ||
repairerContextMenuRange(e); | ||
repairContextMenuRange(e); | ||
}; | ||
@@ -179,7 +179,7 @@ this._container = container; | ||
} | ||
get _store() { | ||
return this._container.store; | ||
get _space() { | ||
return this._container.space; | ||
} | ||
get _blocks() { | ||
return this._store.root?.children ?? []; | ||
return this._space.root?.children ?? []; | ||
} | ||
@@ -240,3 +240,3 @@ get isActive() { | ||
} | ||
handleNativeRangeClick(this._store, e); | ||
handleNativeRangeClick(this._space, e); | ||
break; | ||
@@ -243,0 +243,0 @@ } |
@@ -1,3 +0,3 @@ | ||
import { GroupBlockModel } from '../../group-block'; | ||
import { EdgelessContainer } from './edgeless-page-block'; | ||
import type { GroupBlockModel } from '../../group-block'; | ||
import type { EdgelessContainer } from './edgeless-page-block'; | ||
import type { ViewportState } from './selection-manager'; | ||
@@ -4,0 +4,0 @@ export declare const PADDING_X = 48; |
@@ -1,6 +0,6 @@ | ||
import { BaseBlockModel, Store } from '@blocksuite/store'; | ||
import { BaseBlockModel, Space } from '@blocksuite/store'; | ||
export declare class PageBlockModel extends BaseBlockModel { | ||
flavour: "page"; | ||
flavour: "affine:page"; | ||
title: string; | ||
constructor(store: Store, props: Partial<PageBlockModel>); | ||
constructor(space: Space, props: Partial<PageBlockModel>); | ||
block2html(childText: string, _previousSiblingId: string, _nextSiblingId: string, begin?: number, end?: number): string; | ||
@@ -7,0 +7,0 @@ block2Text(childText: string, begin?: number, end?: number): string; |
import { BaseBlockModel } from '@blocksuite/store'; | ||
export class PageBlockModel extends BaseBlockModel { | ||
constructor(store, props) { | ||
super(store, props); | ||
this.flavour = 'page'; | ||
constructor(space, props) { | ||
super(space, props); | ||
this.flavour = 'affine:page'; | ||
this.title = ''; | ||
@@ -7,0 +7,0 @@ this.title = props.title ?? ''; |
@@ -1,4 +0,4 @@ | ||
import { Store } from '@blocksuite/store'; | ||
export declare function bindCommonHotkey(store: Store): void; | ||
import type { Space } from '@blocksuite/store'; | ||
export declare function bindCommonHotkey(space: Space): void; | ||
export declare function removeCommonHotKey(): void; | ||
//# sourceMappingURL=bind-hotkey.d.ts.map |
@@ -5,13 +5,13 @@ import { hotkey, HOTKEYS } from '../../__internal__'; | ||
const { UNDO, REDO, INLINE_CODE, STRIKE, LINK } = HOTKEYS; | ||
export function bindCommonHotkey(store) { | ||
hotkey.addListener(INLINE_CODE, e => handleFormat(store, e, 'code')); | ||
hotkey.addListener(STRIKE, e => handleFormat(store, e, 'strike')); | ||
export function bindCommonHotkey(space) { | ||
hotkey.addListener(INLINE_CODE, e => handleFormat(space, e, 'code')); | ||
hotkey.addListener(STRIKE, e => handleFormat(space, e, 'strike')); | ||
hotkey.addListener(LINK, e => { | ||
e.preventDefault(); | ||
hotkey.withDisableHotkey(async () => { | ||
await createLink(store, e); | ||
await createLink(space, e); | ||
}); | ||
}); | ||
hotkey.addListener(UNDO, () => store.undo()); | ||
hotkey.addListener(REDO, () => store.redo()); | ||
hotkey.addListener(UNDO, () => space.undo()); | ||
hotkey.addListener(REDO, () => space.redo()); | ||
// !!! | ||
@@ -18,0 +18,0 @@ // Don't forget to remove hotkeys at `_removeHotkeys` |
@@ -1,11 +0,11 @@ | ||
import { Store, BaseBlockModel } from '@blocksuite/store'; | ||
import type { Space, BaseBlockModel } from '@blocksuite/store'; | ||
import { ExtendedModel } from '../../__internal__'; | ||
export declare function updateTextType(flavour: string, type: string, store: Store): void; | ||
export declare function transformBlock(store: Store, model: BaseBlockModel, flavour: string, type: string): void; | ||
export declare function batchUpdateTextType(flavour: string, store: Store, models: ExtendedModel[], type: string): void; | ||
export declare function handleBackspace(store: Store, e: KeyboardEvent): void; | ||
export declare function handleFormat(store: Store, e: KeyboardEvent, key: string): void; | ||
export declare function updateTextType(flavour: string, type: string, space: Space): void; | ||
export declare function transformBlock(space: Space, model: BaseBlockModel, flavour: string, type: string): void; | ||
export declare function batchUpdateTextType(flavour: string, space: Space, models: ExtendedModel[], type: string): void; | ||
export declare function handleBackspace(space: Space, e: KeyboardEvent): void; | ||
export declare function handleFormat(space: Space, e: KeyboardEvent, key: string): void; | ||
export declare function handleSelectAll(): void; | ||
export declare function handleBlockSelectionBatchDelete(store: Store, models: ExtendedModel[]): void; | ||
export declare function tryUpdateGroupSize(store: Store, zoom: number): void; | ||
export declare function handleBlockSelectionBatchDelete(space: Space, models: ExtendedModel[]): void; | ||
export declare function tryUpdateGroupSize(space: Space, zoom: number): void; | ||
//# sourceMappingURL=container-operations.d.ts.map |
@@ -5,3 +5,3 @@ import { assertExists, assertFlavours, noop, almostEqual, isCollapsedAtBlockStart, } from '../../__internal__'; | ||
import { isCollapsedSelection, isMultiBlockRange, isNoneSelection, isRangeSelection, resetNativeSelection, } from '../../__internal__/utils/selection'; | ||
function deleteModels(store, models) { | ||
function deleteModels(space, models) { | ||
const selection = window.getSelection(); | ||
@@ -18,27 +18,30 @@ const first = models[0]; | ||
firstRichText.model.text?.delete(firstTextIndex, firstRichText.model.text.length - firstTextIndex); | ||
lastRichText.model.text?.delete(0, endTextIndex); | ||
firstRichText.model.text?.join(lastRichText.model.text); | ||
const isLastRichTextFullSelected = lastRichText.model.text?.length === endTextIndex; | ||
if (!isLastRichTextFullSelected) { | ||
lastRichText.model.text?.delete(0, endTextIndex); | ||
firstRichText.model.text?.join(lastRichText.model.text); | ||
} | ||
// delete models in between | ||
for (let i = 1; i <= models.length - 1; i++) { | ||
store.deleteBlock(models[i]); | ||
space.deleteBlock(models[i]); | ||
} | ||
firstRichText.quill.setSelection(firstTextIndex, 0); | ||
} | ||
export function updateTextType(flavour, type, store) { | ||
export function updateTextType(flavour, type, space) { | ||
const range = window.getSelection()?.getRangeAt(0); | ||
assertExists(range); | ||
const modelsInRange = getModelsByRange(range); | ||
store.captureSync(); | ||
space.captureSync(); | ||
modelsInRange.forEach(model => { | ||
assertFlavours(model, ['paragraph', 'list']); | ||
assertFlavours(model, ['affine:paragraph', 'affine:list']); | ||
if (model.flavour === flavour) { | ||
store.updateBlock(model, { type }); | ||
space.updateBlock(model, { type }); | ||
} | ||
else { | ||
transformBlock(store, model, flavour, type); | ||
transformBlock(space, model, flavour, type); | ||
} | ||
}); | ||
} | ||
export function transformBlock(store, model, flavour, type) { | ||
const parent = store.getParent(model); | ||
export function transformBlock(space, model, flavour, type) { | ||
const parent = space.getParent(model); | ||
assertExists(parent); | ||
@@ -52,19 +55,19 @@ const blockProps = { | ||
const index = parent.children.indexOf(model); | ||
store.deleteBlock(model); | ||
const id = store.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(store, id); | ||
space.deleteBlock(model); | ||
const id = space.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(space, id); | ||
} | ||
export function batchUpdateTextType(flavour, store, models, type) { | ||
store.captureSync(); | ||
export function batchUpdateTextType(flavour, space, models, type) { | ||
space.captureSync(); | ||
for (const model of models) { | ||
assertFlavours(model, ['paragraph', 'list']); | ||
assertFlavours(model, ['affine:paragraph', 'affine:list']); | ||
if (model.flavour === flavour) { | ||
store.updateBlock(model, { type }); | ||
space.updateBlock(model, { type }); | ||
} | ||
else { | ||
transformBlock(store, model, 'paragraph', type); | ||
transformBlock(space, model, 'affine:paragraph', type); | ||
} | ||
} | ||
} | ||
export function handleBackspace(store, e) { | ||
export function handleBackspace(space, e) { | ||
// workaround page title | ||
@@ -91,7 +94,7 @@ if (e.target instanceof HTMLInputElement) | ||
const intersectedModels = getModelsByRange(range); | ||
deleteModels(store, intersectedModels); | ||
deleteModels(space, intersectedModels); | ||
} | ||
} | ||
} | ||
function formatModelsByRange(models, store, key) { | ||
function formatModelsByRange(models, space, key) { | ||
const selection = window.getSelection(); | ||
@@ -118,3 +121,3 @@ const first = models[0]; | ||
const allFormat = formatArr.every(item => item[key]); | ||
store.captureSync(); | ||
space.captureSync(); | ||
firstRichText.model.text?.format(firstIndex, firstRichText.quill.getLength() - firstIndex - 1, { [key]: !allFormat }); | ||
@@ -134,3 +137,3 @@ lastRichText.model.text?.format(0, endIndex, { [key]: !allFormat }); | ||
} | ||
export function handleFormat(store, e, key) { | ||
export function handleFormat(space, e, key) { | ||
// workaround page title | ||
@@ -150,3 +153,3 @@ e.preventDefault(); | ||
assertExists(range); | ||
store.captureSync(); | ||
space.captureSync(); | ||
const { index, length } = range; | ||
@@ -161,3 +164,3 @@ const format = quill.getFormat(range); | ||
else { | ||
formatModelsByRange(models, store, key); | ||
formatModelsByRange(models, space, key); | ||
} | ||
@@ -193,15 +196,15 @@ } | ||
} | ||
export function handleBlockSelectionBatchDelete(store, models) { | ||
store.captureSync(); | ||
export function handleBlockSelectionBatchDelete(space, models) { | ||
space.captureSync(); | ||
assertExists(models[0].text); | ||
models[0].text.delete(0, models[0].text.length); | ||
for (let i = 1; i < models.length; i++) { | ||
store.deleteBlock(models[i]); | ||
space.deleteBlock(models[i]); | ||
} | ||
} | ||
export function tryUpdateGroupSize(store, zoom) { | ||
export function tryUpdateGroupSize(space, zoom) { | ||
requestAnimationFrame(() => { | ||
if (!store.root) | ||
if (!space.root) | ||
return; | ||
const groups = store.root.children; | ||
const groups = space.root.children; | ||
groups.forEach(model => { | ||
@@ -218,3 +221,3 @@ const blockElement = getBlockElementByModel(model); | ||
if (!almostEqual(newModelWidth, w) || !almostEqual(newModelHeight, h)) { | ||
store.updateBlock(model, { | ||
space.updateBlock(model, { | ||
xywh: JSON.stringify([x, y, newModelWidth, newModelHeight]), | ||
@@ -221,0 +224,0 @@ }); |
import { SelectionEvent } from '../../__internal__'; | ||
export declare function repairerContextMenuRange(e: SelectionEvent): void; | ||
export declare function repairContextMenuRange(e: SelectionEvent): void; | ||
//# sourceMappingURL=cursor.d.ts.map |
import { caretRangeFromPoint, resetNativeSelection, } from '../../__internal__'; | ||
export function repairerContextMenuRange(e) { | ||
export function repairContextMenuRange(e) { | ||
const currentRange = window.getSelection()?.getRangeAt(0); | ||
@@ -4,0 +4,0 @@ const pointRange = caretRangeFromPoint(e.x, e.y); |
@@ -7,2 +7,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
}; | ||
/// <reference types="vite/client" /> | ||
import { LitElement, html, css, unsafeCSS } from 'lit'; | ||
@@ -9,0 +10,0 @@ import { customElement, property } from 'lit/decorators.js'; |
@@ -1,13 +0,13 @@ | ||
import { Store, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
import { Space, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
export declare type ParagraphType = 'text' | 'quote' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; | ||
export interface ParagraphBlockProps extends IBaseBlockProps { | ||
flavour: 'paragraph'; | ||
flavour: 'affine:paragraph'; | ||
type: ParagraphType; | ||
} | ||
export declare class ParagraphBlockModel extends BaseBlockModel implements ParagraphBlockProps { | ||
flavour: "paragraph"; | ||
flavour: "affine:paragraph"; | ||
type: ParagraphType; | ||
constructor(store: Store, props: Partial<ParagraphBlockProps>); | ||
constructor(space: Space, props: Partial<ParagraphBlockProps>); | ||
block2html(childText: string, _previousSiblingId: string, _nextSiblingId: string, begin?: number, end?: number): string; | ||
} | ||
//# sourceMappingURL=paragraph-model.d.ts.map |
import { BaseBlockModel } from '@blocksuite/store'; | ||
export class ParagraphBlockModel extends BaseBlockModel { | ||
constructor(store, props) { | ||
super(store, props); | ||
this.flavour = 'paragraph'; | ||
constructor(space, props) { | ||
super(space, props); | ||
this.flavour = 'affine:paragraph'; | ||
this.type = 'text'; | ||
@@ -7,0 +7,0 @@ this.type = props.type ?? 'text'; |
{ | ||
"name": "@blocksuite/blocks", | ||
"version": "0.2.24", | ||
"version": "0.3.0-alpha.0", | ||
"description": "Default BlockSuite editable blocks.", | ||
@@ -11,3 +11,3 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@blocksuite/store": "0.2.24", | ||
"@blocksuite/store": "0.3.0-alpha.0", | ||
"hotkeys-js": "^3.10.0", | ||
@@ -14,0 +14,0 @@ "lit": "^2.3.1", |
@@ -5,8 +5,8 @@ import { LitElement, html, css } from 'lit'; | ||
import { | ||
CommonBlockElement, | ||
type CommonBlockElement, | ||
convertToList, | ||
createEvent, | ||
} from '../__internal__'; | ||
import { BaseBlockModel, Store } from '@blocksuite/store'; | ||
import { GroupBlockModel } from '../group-block'; | ||
import type { BaseBlockModel, Store } from '@blocksuite/store'; | ||
import type { GroupBlockModel } from '../group-block'; | ||
@@ -145,2 +145,6 @@ // Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. | ||
get space() { | ||
return this.store.space; | ||
} | ||
private _onToggleConnection() { | ||
@@ -162,6 +166,5 @@ if (this.connected === true) { | ||
const store = block.host.store as Store; | ||
// @ts-ignore | ||
const model = store.getBlockById(block.model.id) as BaseBlockModel; | ||
convertToList(this.store, model, listType, ''); | ||
const { space } = block.host; | ||
const model = space.getBlockById(block.model.id) as BaseBlockModel; | ||
convertToList(this.space, model, listType, ''); | ||
} | ||
@@ -179,4 +182,4 @@ | ||
this.store.captureSync(); | ||
this.store.updateBlock(block, { type }); | ||
this.space.captureSync(); | ||
this.space.updateBlock(block, { type }); | ||
} | ||
@@ -192,7 +195,7 @@ | ||
private _onAddGroup() { | ||
const root = this.store.root; | ||
const root = this.space.root; | ||
if (!root) return; | ||
const pageId = root.id; | ||
this.store.captureSync(); | ||
this.space.captureSync(); | ||
@@ -202,7 +205,7 @@ const count = root.children.length; | ||
const groupId = this.store.addBlock<GroupBlockModel>( | ||
{ flavour: 'group', xywh }, | ||
const groupId = this.space.addBlock<GroupBlockModel>( | ||
{ flavour: 'affine:group', xywh }, | ||
pageId | ||
); | ||
this.store.addBlock({ flavour: 'paragraph' }, groupId); | ||
this.space.addBlock({ flavour: 'affine:paragraph' }, groupId); | ||
} | ||
@@ -219,5 +222,5 @@ | ||
firstUpdated() { | ||
this.store.signals.historyUpdated.on(() => { | ||
this.canUndo = this.store.canUndo; | ||
this.canRedo = this.store.canRedo; | ||
this.space.signals.historyUpdated.on(() => { | ||
this.canUndo = this.space.canUndo; | ||
this.canRedo = this.space.canRedo; | ||
}); | ||
@@ -273,3 +276,3 @@ } | ||
tabindex="-1" | ||
@click=${() => this.store.undo()} | ||
@click=${() => this.space.undo()} | ||
> | ||
@@ -283,3 +286,3 @@ ${icons.undo} | ||
tabindex="-1" | ||
@click=${() => this.store.redo()} | ||
@click=${() => this.space.redo()} | ||
> | ||
@@ -286,0 +289,0 @@ ${icons.redo} |
@@ -1,3 +0,3 @@ | ||
import type { BaseBlockModel, Store } from '@blocksuite/store'; | ||
import { Quill, type RangeStatic } from 'quill'; | ||
import type { BaseBlockModel, Space } from '@blocksuite/store'; | ||
import type { Quill, RangeStatic } from 'quill'; | ||
import { | ||
@@ -72,3 +72,3 @@ ALLOW_DEFAULT, | ||
export function createKeyboardBindings(store: Store, model: BaseBlockModel) { | ||
export function createKeyboardBindings(space: Space, model: BaseBlockModel) { | ||
function enterMarkdownMatch( | ||
@@ -97,5 +97,6 @@ this: KeyboardEventThis, | ||
const isEnd = isAtBlockEnd(this.quill); | ||
const parent = store.getParent(model); | ||
const parent = space.getParent(model); | ||
const isLastChild = parent?.lastChild() === model; | ||
const isEmptyList = model.flavour === 'list' && model.text?.length === 0; | ||
const isEmptyList = | ||
model.flavour === 'affine:list' && model.text?.length === 0; | ||
const index = this.quill.getSelection()?.index || 0; | ||
@@ -110,3 +111,3 @@ | ||
{ | ||
flavour: 'paragraph', | ||
flavour: 'affine:paragraph', | ||
type: 'quote', | ||
@@ -118,8 +119,8 @@ }, | ||
isEmptyList && | ||
parent?.flavour === 'group' && | ||
parent?.flavour === 'affine:group' && | ||
model.children.length === 0 | ||
) { | ||
handleLineStartBackspace(store, model); | ||
handleLineStartBackspace(space, model); | ||
} else if (isEmptyList && isLastChild) { | ||
handleUnindent(store, model, index); | ||
handleUnindent(space, model, index); | ||
} else if (isEnd) { | ||
@@ -135,3 +136,3 @@ const isSoftEnterBlock = | ||
if (shouldSoftEnter) { | ||
softEnter.bind(this)(); | ||
onSoftEnter.bind(this)(); | ||
} else { | ||
@@ -142,3 +143,3 @@ // delete the \n at the end of block | ||
} | ||
handleBlockEndEnter(store, model); | ||
handleBlockEndEnter(space, model); | ||
} | ||
@@ -152,5 +153,5 @@ } else { | ||
if (isSoftEnterBlock) { | ||
softEnter.bind(this)(); | ||
onSoftEnter.bind(this)(); | ||
} else { | ||
handleBlockSplit(store, model, index); | ||
handleBlockSplit(space, model, index); | ||
} | ||
@@ -162,5 +163,5 @@ } | ||
function softEnter(this: KeyboardEventThis) { | ||
function onSoftEnter(this: KeyboardEventThis) { | ||
const index = this.quill.getSelection()?.index || 0; | ||
handleSoftEnter(store, model, index); | ||
handleSoftEnter(space, model, index); | ||
this.quill.setSelection(index + 1, 0); | ||
@@ -171,15 +172,15 @@ | ||
function indent(this: KeyboardEventThis) { | ||
function onIndent(this: KeyboardEventThis) { | ||
const index = this.quill.getSelection()?.index || 0; | ||
handleIndent(store, model, index); | ||
handleIndent(space, model, index); | ||
return PREVENT_DEFAULT; | ||
} | ||
function unindent(this: KeyboardEventThis) { | ||
function onUnindent(this: KeyboardEventThis) { | ||
const index = this.quill.getSelection()?.index || 0; | ||
handleUnindent(store, model, index); | ||
handleUnindent(space, model, index); | ||
return PREVENT_DEFAULT; | ||
} | ||
function keyUp(this: KeyboardEventThis, range: QuillRange) { | ||
function onKeyUp(this: KeyboardEventThis, range: QuillRange) { | ||
if (range.index >= 0) { | ||
@@ -191,3 +192,3 @@ return handleKeyUp(model, this.quill.root); | ||
function keyDown(this: KeyboardEventThis, range: QuillRange) { | ||
function onKeyDown(this: KeyboardEventThis, range: QuillRange) { | ||
if (range.index >= 0) { | ||
@@ -199,3 +200,3 @@ return handleKeyDown(model, this.quill.root); | ||
function keyLeft(this: KeyboardEventThis, range: QuillRange) { | ||
function onKeyLeft(this: KeyboardEventThis, range: QuillRange) { | ||
// range.length === 0 means collapsed selection, if have range length, the cursor is in the start of text | ||
@@ -209,3 +210,3 @@ if (range.index === 0 && range.length === 0) { | ||
function keyRight(this: KeyboardEventThis, range: QuillRange) { | ||
function onKeyRight(this: KeyboardEventThis, range: QuillRange) { | ||
const textLength = this.quill.getText().length; | ||
@@ -219,3 +220,3 @@ if (range.index + 1 === textLength) { | ||
function space( | ||
function onSpace( | ||
this: KeyboardEventThis, | ||
@@ -227,10 +228,10 @@ range: QuillRange, | ||
const { prefix } = context; | ||
return tryMatchSpaceHotkey(store, model, quill, prefix, range); | ||
return tryMatchSpaceHotkey(space, model, quill, prefix, range); | ||
} | ||
function backspace(this: KeyboardEventThis) { | ||
function onBackspace(this: KeyboardEventThis) { | ||
// To workaround uncontrolled behavior when deleting character at block start, | ||
// in this case backspace should be handled in quill. | ||
if (isCollapsedAtBlockStart(this.quill)) { | ||
handleLineStartBackspace(store, model); | ||
handleLineStartBackspace(space, model); | ||
return PREVENT_DEFAULT; | ||
@@ -260,7 +261,7 @@ } else if (isMultiBlockRange(getCurrentRange())) { | ||
shiftKey: true, | ||
handler: softEnter, | ||
handler: onSoftEnter, | ||
}, | ||
tab: { | ||
key: 'tab', | ||
handler: indent, | ||
handler: onIndent, | ||
}, | ||
@@ -270,3 +271,3 @@ shiftTab: { | ||
shiftKey: true, | ||
handler: unindent, | ||
handler: onUnindent, | ||
}, | ||
@@ -278,3 +279,3 @@ // https://github.com/quilljs/quill/blob/v1.3.7/modules/keyboard.js#L249-L282 | ||
prefix: /^(\d+\.|-|\*|\[ ?\]|\[x\]|(#){1,6}|>)$/, | ||
handler: space, | ||
handler: onSpace, | ||
}, | ||
@@ -285,7 +286,7 @@ 'list autofill shift': { | ||
prefix: /^(\d+\.|-|\*|\[ ?\]|\[x\]|(#){1,6}|>)$/, | ||
handler: space, | ||
handler: onSpace, | ||
}, | ||
backspace: { | ||
key: 'backspace', | ||
handler: backspace, | ||
handler: onBackspace, | ||
}, | ||
@@ -295,3 +296,3 @@ up: { | ||
shiftKey: false, | ||
handler: keyUp, | ||
handler: onKeyUp, | ||
}, | ||
@@ -301,3 +302,3 @@ down: { | ||
shiftKey: false, | ||
handler: keyDown, | ||
handler: onKeyDown, | ||
}, | ||
@@ -307,3 +308,3 @@ 'embed left': { | ||
shiftKey: false, | ||
handler: keyLeft, | ||
handler: onKeyLeft, | ||
}, | ||
@@ -313,3 +314,3 @@ right: { | ||
shiftKey: false, | ||
handler: keyRight, | ||
handler: onKeyRight, | ||
}, | ||
@@ -316,0 +317,0 @@ }; |
@@ -1,2 +0,2 @@ | ||
import { Store } from '@blocksuite/store'; | ||
import type { Space } from '@blocksuite/store'; | ||
import { | ||
@@ -12,3 +12,3 @@ assertExists, | ||
export const createLink = async (store: Store, e: KeyboardEvent) => { | ||
export const createLink = async (space: Space, e: KeyboardEvent) => { | ||
if (!isRangeSelection()) { | ||
@@ -32,3 +32,3 @@ // TODO maybe allow user creating a link with text | ||
if (format?.link) { | ||
store.captureSync(); | ||
space.captureSync(); | ||
const { index, length } = range; | ||
@@ -66,4 +66,4 @@ startModel.text?.format(index, length, { link: false }); | ||
store.captureSync(); | ||
space.captureSync(); | ||
startModel.text?.format(range.index, range.length, { link }); | ||
}; |
@@ -96,3 +96,3 @@ import { css, html, LitElement } from 'lit'; | ||
const model = getModelByElement(this); | ||
const store = model.store; | ||
const { space } = model; | ||
@@ -103,7 +103,8 @@ if (text) { | ||
const offset = blot.offset(); | ||
store.captureSync(); | ||
space.captureSync(); | ||
// TODO save the format of the original text | ||
model.text?.delete(offset, blot.length()); | ||
model.text?.insert(text, offset, { link }); | ||
} else { | ||
store.captureSync(); | ||
space.captureSync(); | ||
model.text?.format(blot.offset(), blot.length(), { link }); | ||
@@ -110,0 +111,0 @@ } |
import { noop } from '../../../utils'; | ||
import { LinkDetail } from './link-popover'; | ||
import type { LinkDetail } from './link-popover'; | ||
@@ -4,0 +4,0 @@ const createEditLinkElement = ( |
// operations used in rich-text level | ||
import { Store, Text } from '@blocksuite/store'; | ||
import Quill from 'quill'; | ||
import { Space, Text } from '@blocksuite/store'; | ||
import type Quill from 'quill'; | ||
import { | ||
@@ -23,11 +23,11 @@ ExtendedModel, | ||
export function handleBlockEndEnter(store: Store, model: ExtendedModel) { | ||
const parent = store.getParent(model); | ||
export function handleBlockEndEnter(space: Space, model: ExtendedModel) { | ||
const parent = space.getParent(model); | ||
const index = parent?.children.indexOf(model); | ||
if (parent && index !== undefined && index > -1) { | ||
// make adding text block by enter a standalone operation | ||
store.captureSync(); | ||
space.captureSync(); | ||
let id = ''; | ||
if (model.flavour === 'list') { | ||
if (model.flavour === 'affine:list') { | ||
const blockProps = { | ||
@@ -38,5 +38,5 @@ flavour: model.flavour, | ||
if (model.children.length === 0) { | ||
id = store.addBlock(blockProps, parent, index + 1); | ||
id = space.addBlock(blockProps, parent, index + 1); | ||
} else { | ||
id = store.addBlock(blockProps, model, 0); | ||
id = space.addBlock(blockProps, model, 0); | ||
} | ||
@@ -48,5 +48,5 @@ } else { | ||
}; | ||
id = store.addBlock(blockProps, parent, index + 1); | ||
id = space.addBlock(blockProps, parent, index + 1); | ||
} | ||
id && asyncFocusRichText(store, id); | ||
id && asyncFocusRichText(space, id); | ||
} | ||
@@ -56,7 +56,7 @@ } | ||
export function handleSoftEnter( | ||
store: Store, | ||
space: Space, | ||
model: ExtendedModel, | ||
index: number | ||
) { | ||
store.captureSync(); | ||
space.captureSync(); | ||
model.text?.insert('\n', index); | ||
@@ -66,3 +66,3 @@ } | ||
export function handleBlockSplit( | ||
store: Store, | ||
space: Space, | ||
model: ExtendedModel, | ||
@@ -73,17 +73,17 @@ splitIndex: number | ||
const parent = store.getParent(model); | ||
const parent = space.getParent(model); | ||
if (!parent) return; | ||
const [left, right] = model.text.split(splitIndex); | ||
store.captureSync(); | ||
store.markTextSplit(model.text, left, right); | ||
store.updateBlock(model, { text: left }); | ||
space.captureSync(); | ||
space.markTextSplit(model.text, left, right); | ||
space.updateBlock(model, { text: left }); | ||
let newParent = parent; | ||
let newBlockIndex = newParent.children.indexOf(model) + 1; | ||
if (model.flavour === 'list' && model.children.length > 0) { | ||
if (model.flavour === 'affine:list' && model.children.length > 0) { | ||
newParent = model; | ||
newBlockIndex = 0; | ||
} | ||
const id = store.addBlock( | ||
const id = space.addBlock( | ||
{ flavour: model.flavour, text: right, type: model.type }, | ||
@@ -93,13 +93,13 @@ newParent, | ||
); | ||
asyncFocusRichText(store, id); | ||
asyncFocusRichText(space, id); | ||
} | ||
export function handleIndent( | ||
store: Store, | ||
space: Space, | ||
model: ExtendedModel, | ||
offset: number | ||
) { | ||
const previousSibling = store.getPreviousSibling(model); | ||
const previousSibling = space.getPreviousSibling(model); | ||
if (previousSibling) { | ||
store.captureSync(); | ||
space.captureSync(); | ||
@@ -112,7 +112,7 @@ const blockProps = { | ||
}; | ||
store.deleteBlock(model); | ||
const id = store.addBlock(blockProps, previousSibling); | ||
space.deleteBlock(model); | ||
const id = space.addBlock(blockProps, previousSibling); | ||
// FIXME: after quill onload | ||
requestAnimationFrame(() => { | ||
const block = store.getBlockById(id); | ||
const block = space.getBlockById(id); | ||
assertExists(block); | ||
@@ -126,10 +126,10 @@ const richText = getRichTextByModel(block); | ||
export async function handleUnindent( | ||
store: Store, | ||
space: Space, | ||
model: ExtendedModel, | ||
offset: number | ||
) { | ||
const parent = store.getParent(model); | ||
if (!parent || parent?.flavour === 'group') return; | ||
const parent = space.getParent(model); | ||
if (!parent || parent?.flavour === 'affine:group') return; | ||
const grandParent = store.getParent(parent); | ||
const grandParent = space.getParent(parent); | ||
if (!grandParent) return; | ||
@@ -145,9 +145,9 @@ | ||
store.captureSync(); | ||
store.deleteBlock(model); | ||
const id = store.addBlock(blockProps, grandParent, index + 1); | ||
space.captureSync(); | ||
space.deleteBlock(model); | ||
const id = space.addBlock(blockProps, grandParent, index + 1); | ||
// FIXME: after quill onload | ||
requestAnimationFrame(() => { | ||
const block = store.getBlockById(id); | ||
const block = space.getBlockById(id); | ||
assertExists(block); | ||
@@ -160,22 +160,22 @@ | ||
export function handleLineStartBackspace(store: Store, model: ExtendedModel) { | ||
export function handleLineStartBackspace(space: Space, model: ExtendedModel) { | ||
// When deleting at line start of a paragraph block, | ||
// firstly switch it to normal text, then delete this empty block. | ||
if (model.flavour === 'paragraph') { | ||
if (model.flavour === 'affine:paragraph') { | ||
if (model.type !== 'text') { | ||
store.captureSync(); | ||
store.updateBlock(model, { type: 'text' }); | ||
space.captureSync(); | ||
space.updateBlock(model, { type: 'text' }); | ||
} else { | ||
const parent = store.getParent(model); | ||
if (!parent || parent?.flavour === 'group') { | ||
const parent = space.getParent(model); | ||
if (!parent || parent?.flavour === 'affine:group') { | ||
const container = getContainerByModel(model); | ||
const previousSibling = getPreviousBlock(container, model.id); | ||
if (previousSibling) { | ||
store.captureSync(); | ||
space.captureSync(); | ||
previousSibling.text?.join(model.text as Text); | ||
store.deleteBlock(model); | ||
asyncFocusRichText(store, previousSibling.id); | ||
space.deleteBlock(model); | ||
asyncFocusRichText(space, previousSibling.id); | ||
} | ||
} else { | ||
const grandParent = store.getParent(parent); | ||
const grandParent = space.getParent(parent); | ||
if (!grandParent) return; | ||
@@ -190,5 +190,5 @@ const index = grandParent.children.indexOf(parent); | ||
store.captureSync(); | ||
store.deleteBlock(model); | ||
store.addBlock(blockProps, grandParent, index + 1); | ||
space.captureSync(); | ||
space.deleteBlock(model); | ||
space.addBlock(blockProps, grandParent, index + 1); | ||
} | ||
@@ -199,11 +199,11 @@ } | ||
// switch it to normal paragraph block. | ||
else if (model.flavour === 'list') { | ||
const parent = store.getParent(model); | ||
else if (model.flavour === 'affine:list') { | ||
const parent = space.getParent(model); | ||
if (!parent) return; | ||
const index = parent.children.indexOf(model); | ||
store.captureSync(); | ||
space.captureSync(); | ||
const blockProps = { | ||
flavour: 'paragraph', | ||
flavour: 'affine:paragraph', | ||
type: 'text', | ||
@@ -213,5 +213,5 @@ text: model?.text?.clone(), | ||
}; | ||
store.deleteBlock(model); | ||
const id = store.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(store, id); | ||
space.deleteBlock(model); | ||
const id = space.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(space, id); | ||
} | ||
@@ -240,3 +240,3 @@ } | ||
// FIXME: Then it will turn the input into the div | ||
if (preNodeModel?.flavour === 'group') { | ||
if (preNodeModel?.flavour === 'affine:group') { | ||
( | ||
@@ -317,6 +317,3 @@ document.querySelector( | ||
model, | ||
new Point( | ||
newRange.startContainer.parentElement?.offsetLeft || left, | ||
bottom | ||
) | ||
new Point(textContainer.getBoundingClientRect().left || left, bottom) | ||
); | ||
@@ -331,3 +328,3 @@ return PREVENT_DEFAULT; | ||
export function tryMatchSpaceHotkey( | ||
store: Store, | ||
space: Space, | ||
model: ExtendedModel, | ||
@@ -346,3 +343,3 @@ quill: Quill, | ||
case '[ ]': | ||
isConverted = convertToList(store, model, 'todo', prefix, { | ||
isConverted = convertToList(space, model, 'todo', prefix, { | ||
checked: false, | ||
@@ -352,3 +349,3 @@ }); | ||
case '[x]': | ||
isConverted = convertToList(store, model, 'todo', prefix, { | ||
isConverted = convertToList(space, model, 'todo', prefix, { | ||
checked: true, | ||
@@ -359,27 +356,27 @@ }); | ||
case '*': | ||
isConverted = convertToList(store, model, 'bulleted', prefix); | ||
isConverted = convertToList(space, model, 'bulleted', prefix); | ||
break; | ||
case '#': | ||
isConverted = convertToParagraph(store, model, 'h1', prefix); | ||
isConverted = convertToParagraph(space, model, 'h1', prefix); | ||
break; | ||
case '##': | ||
isConverted = convertToParagraph(store, model, 'h2', prefix); | ||
isConverted = convertToParagraph(space, model, 'h2', prefix); | ||
break; | ||
case '###': | ||
isConverted = convertToParagraph(store, model, 'h3', prefix); | ||
isConverted = convertToParagraph(space, model, 'h3', prefix); | ||
break; | ||
case '####': | ||
isConverted = convertToParagraph(store, model, 'h4', prefix); | ||
isConverted = convertToParagraph(space, model, 'h4', prefix); | ||
break; | ||
case '#####': | ||
isConverted = convertToParagraph(store, model, 'h5', prefix); | ||
isConverted = convertToParagraph(space, model, 'h5', prefix); | ||
break; | ||
case '######': | ||
isConverted = convertToParagraph(store, model, 'h6', prefix); | ||
isConverted = convertToParagraph(space, model, 'h6', prefix); | ||
break; | ||
case '>': | ||
isConverted = convertToParagraph(store, model, 'quote', prefix); | ||
isConverted = convertToParagraph(space, model, 'quote', prefix); | ||
break; | ||
default: | ||
isConverted = convertToList(store, model, 'numbered', prefix); | ||
isConverted = convertToList(space, model, 'numbered', prefix); | ||
} | ||
@@ -386,0 +383,0 @@ |
@@ -6,3 +6,3 @@ import { LitElement, html, css, unsafeCSS } from 'lit'; | ||
import type { BaseBlockModel } from '@blocksuite/store'; | ||
import { BlockHost } from '../utils'; | ||
import type { BlockHost } from '../utils'; | ||
import { createKeyboardBindings } from './keyboard'; | ||
@@ -50,4 +50,4 @@ | ||
const { host, model, _textContainer } = this; | ||
const { store } = host; | ||
const keyboardBindings = createKeyboardBindings(store, model); | ||
const { space } = host; | ||
const keyboardBindings = createKeyboardBindings(space, model); | ||
@@ -80,8 +80,14 @@ this.quill = new Quill(_textContainer, { | ||
if (retain !== undefined) { | ||
const nextTextLeaf = this.quill.getLeaf( | ||
const currentLeaf = this.quill.getLeaf( | ||
retain + Number(delta.ops[1]?.insert.toString().length) | ||
); | ||
const nextLeaf = this.quill.getLeaf( | ||
retain + Number(delta.ops[1]?.insert.toString().length) + 1 | ||
); | ||
const parentElement = nextTextLeaf[0]?.domNode?.parentElement; | ||
const currentParentElement = currentLeaf[0]?.domNode?.parentElement; | ||
const currentEmbedElement = currentParentElement?.closest(selector); | ||
const nextParentElement = nextLeaf[0]?.domNode?.parentElement; | ||
const nextEmbedElement = nextParentElement?.closest(selector); | ||
const insertedString = delta.ops[1]?.insert.toString(); | ||
if (parentElement && !parentElement.closest(selector)) { | ||
if (nextEmbedElement && nextEmbedElement !== currentEmbedElement) { | ||
this.quill.deleteText( | ||
@@ -103,3 +109,3 @@ retain, | ||
} | ||
if (!nextTextLeaf[0] && insertedString) { | ||
if (!nextEmbedElement && insertedString) { | ||
this.quill.deleteText( | ||
@@ -130,4 +136,4 @@ retain, | ||
}); | ||
store.attachRichText(model.id, this.quill); | ||
store.awareness.updateLocalCursor(); | ||
space.attachRichText(model.id, this.quill); | ||
space.awareness.updateLocalCursor(); | ||
@@ -139,3 +145,3 @@ this.model.propsUpdated.on(() => this.requestUpdate()); | ||
super.disconnectedCallback(); | ||
this.host.store.detachRichText(this.model.id); | ||
this.host.space.detachRichText(this.model.id); | ||
} | ||
@@ -142,0 +148,0 @@ |
@@ -1,3 +0,4 @@ | ||
import { BaseBlockModel } from '@blocksuite/store'; | ||
import Quill, { RangeStatic } from 'quill'; | ||
import type { BaseBlockModel } from '@blocksuite/store'; | ||
import type Quill from 'quill'; | ||
import type { RangeStatic } from 'quill'; | ||
import { ALLOW_DEFAULT, PREVENT_DEFAULT } from '..'; | ||
@@ -65,3 +66,3 @@ | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -103,3 +104,3 @@ bold: true, | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -140,3 +141,3 @@ bold: true, | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -177,3 +178,3 @@ italic: true, | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -214,3 +215,3 @@ strike: true, | ||
model.text?.insert(' ', selection.index); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -252,3 +253,3 @@ underline: true, | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -287,3 +288,3 @@ code: true, | ||
model.text?.insert(' ', startIndex + annotatedText.length); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(startIndex, annotatedText.length, { | ||
@@ -320,3 +321,3 @@ link: annotatedText, | ||
model.text?.insert(' ', selection.index); | ||
model.store.captureSync(); | ||
model.space.captureSync(); | ||
model.text?.format(start, hrefText.length, { | ||
@@ -323,0 +324,0 @@ link: hrefLink.slice(1, hrefLink.length - 1), |
@@ -1,9 +0,9 @@ | ||
import { Store } from '@blocksuite/store'; | ||
import type { Space } from '@blocksuite/store'; | ||
import type { Quill } from 'quill'; | ||
import { ExtendedModel } from './types'; | ||
import type { ExtendedModel } from './types'; | ||
// XXX: workaround quill lifecycle issue | ||
export function asyncFocusRichText(store: Store, id: string) { | ||
export function asyncFocusRichText(space: Space, id: string) { | ||
setTimeout(() => { | ||
const adapter = store.richTextAdapters.get(id); | ||
const adapter = space.richTextAdapters.get(id); | ||
adapter?.quill.focus(); | ||
@@ -20,3 +20,3 @@ }); | ||
export function convertToList( | ||
store: Store, | ||
space: Space, | ||
model: ExtendedModel, | ||
@@ -27,7 +27,7 @@ listType: 'bulleted' | 'numbered' | 'todo', | ||
): boolean { | ||
if (model.flavour === 'list' && model['type'] === listType) { | ||
if (model.flavour === 'affine:list' && model['type'] === listType) { | ||
return false; | ||
} | ||
if (model.flavour === 'paragraph') { | ||
const parent = store.getParent(model); | ||
if (model.flavour === 'affine:paragraph') { | ||
const parent = space.getParent(model); | ||
if (!parent) return false; | ||
@@ -37,7 +37,7 @@ | ||
model.text?.insert(' ', prefix.length); | ||
store.captureSync(); | ||
space.captureSync(); | ||
model.text?.delete(0, prefix.length + 1); | ||
const blockProps = { | ||
flavour: 'list', | ||
flavour: 'affine:list', | ||
type: listType, | ||
@@ -48,12 +48,12 @@ text: model?.text?.clone(), | ||
}; | ||
store.deleteBlock(model); | ||
space.deleteBlock(model); | ||
const id = store.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(store, id); | ||
} else if (model.flavour === 'list' && model['type'] !== listType) { | ||
const id = space.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(space, id); | ||
} else if (model.flavour === 'affine:list' && model['type'] !== listType) { | ||
model.text?.insert(' ', prefix.length); | ||
store.captureSync(); | ||
space.captureSync(); | ||
model.text?.delete(0, prefix.length + 1); | ||
store.updateBlock(model, { type: listType }); | ||
space.updateBlock(model, { type: listType }); | ||
} | ||
@@ -64,12 +64,12 @@ return true; | ||
export function convertToParagraph( | ||
store: Store, | ||
space: Space, | ||
model: ExtendedModel, | ||
type: 'paragraph' | 'quote' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6', | ||
type: 'affine:paragraph' | 'quote' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6', | ||
prefix: string | ||
): boolean { | ||
if (model.flavour === 'paragraph' && model['type'] === type) { | ||
if (model.flavour === 'affine:paragraph' && model['type'] === type) { | ||
return false; | ||
} | ||
if (model.flavour !== 'paragraph') { | ||
const parent = store.getParent(model); | ||
if (model.flavour !== 'affine:paragraph') { | ||
const parent = space.getParent(model); | ||
if (!parent) return false; | ||
@@ -79,7 +79,7 @@ | ||
model.text?.insert(' ', prefix.length); | ||
store.captureSync(); | ||
space.captureSync(); | ||
model.text?.delete(0, prefix.length + 1); | ||
const blockProps = { | ||
flavour: 'paragraph', | ||
flavour: 'affine:paragraph', | ||
type: type, | ||
@@ -89,14 +89,14 @@ text: model?.text?.clone(), | ||
}; | ||
store.deleteBlock(model); | ||
space.deleteBlock(model); | ||
const id = store.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(store, id); | ||
} else if (model.flavour === 'paragraph' && model['type'] !== type) { | ||
const id = space.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(space, id); | ||
} else if (model.flavour === 'affine:paragraph' && model['type'] !== type) { | ||
model.text?.insert(' ', prefix.length); | ||
store.captureSync(); | ||
space.captureSync(); | ||
model.text?.delete(0, prefix.length + 1); | ||
store.updateBlock(model, { type: type }); | ||
space.updateBlock(model, { type: type }); | ||
} | ||
return true; | ||
} |
@@ -13,3 +13,3 @@ import { html } from 'lit'; | ||
switch (model.flavour) { | ||
case 'paragraph': | ||
case 'affine:paragraph': | ||
return html` | ||
@@ -21,3 +21,3 @@ <paragraph-block | ||
`; | ||
case 'list': | ||
case 'affine:list': | ||
return html` | ||
@@ -29,3 +29,3 @@ <list-block | ||
`; | ||
case 'group': | ||
case 'affine:group': | ||
return html` | ||
@@ -32,0 +32,0 @@ <group-block |
@@ -15,3 +15,3 @@ import hotkeys, { KeyHandler } from 'hotkeys-js'; | ||
addListener(hotkey: string, listener: KeyHandler, scope?: string) { | ||
this._hotkeys(hotkey, { scope: scope ?? 'page' }, listener); | ||
this._hotkeys(hotkey, { scope: scope ?? 'affine:page' }, listener); | ||
} | ||
@@ -22,3 +22,3 @@ | ||
(Array.isArray(hotkey) ? hotkey : [hotkey]).join(','), | ||
scope ?? 'page' | ||
scope ?? 'affine:page' | ||
); | ||
@@ -30,3 +30,3 @@ } | ||
enableHotkey() { | ||
this._setScope('page'); | ||
this._setScope('affine:page'); | ||
} | ||
@@ -33,0 +33,0 @@ |
@@ -1,4 +0,4 @@ | ||
import { BaseBlockModel } from '@blocksuite/store'; | ||
import { DefaultPageBlockComponent } from '../..'; | ||
import { RichText } from '../rich-text/rich-text'; | ||
import type { BaseBlockModel } from '@blocksuite/store'; | ||
import type { DefaultPageBlockComponent } from '../..'; | ||
import type { RichText } from '../rich-text/rich-text'; | ||
import { BLOCK_ID_ATTR as ATTR } from './consts'; | ||
@@ -96,9 +96,8 @@ import { assertExists } from './std'; | ||
if (previousBlock.model.children.length) { | ||
let firstChildren = | ||
previousBlock.model.children[previousBlock.children.length - 1]; | ||
while (firstChildren.children.length) { | ||
firstChildren = | ||
firstChildren.children[firstChildren.children.length - 1]; | ||
let firstChild = | ||
previousBlock.model.children[previousBlock.model.children.length - 1]; | ||
while (firstChild.children.length) { | ||
firstChild = firstChild.children[firstChild.children.length - 1]; | ||
} | ||
return firstChildren; | ||
return firstChild; | ||
} | ||
@@ -113,5 +112,5 @@ return previousBlock.model; | ||
export function getDefaultPageBlock(model: BaseBlockModel) { | ||
assertExists(model.store.root); | ||
assertExists(model.space.root); | ||
const page = document.querySelector( | ||
`[${ATTR}="${model.store.root.id}"]` | ||
`[${ATTR}="${model.space.root.id}"]` | ||
) as DefaultPageBlockComponent; | ||
@@ -122,5 +121,5 @@ return page; | ||
export function getContainerByModel(model: BaseBlockModel) { | ||
assertExists(model.store.root); | ||
assertExists(model.space.root); | ||
const page = document.querySelector( | ||
`[${ATTR}="${model.store.root.id}"]` | ||
`[${ATTR}="${model.space.root.id}"]` | ||
) as DefaultPageBlockComponent; | ||
@@ -133,9 +132,9 @@ const container = page.closest('editor-container'); | ||
export function getBlockElementByModel(model: BaseBlockModel) { | ||
assertExists(model.store.root); | ||
assertExists(model.space.root); | ||
const page = document.querySelector( | ||
`[${ATTR}="${model.store.root.id}"]` | ||
`[${ATTR}="${model.space.root.id}"]` | ||
) as DefaultPageBlockComponent; | ||
if (!page) return null; | ||
if (model.id === model.store.root.id) { | ||
if (model.id === model.space.root.id) { | ||
return page as HTMLElement; | ||
@@ -190,3 +189,3 @@ } | ||
const mainElelment = | ||
block.model.flavour === 'page' | ||
block.model.flavour === 'affine:page' | ||
? blockElement?.querySelector( | ||
@@ -193,0 +192,0 @@ '.affine-default-page-block-title-container' |
@@ -1,5 +0,5 @@ | ||
import type { BaseBlockModel, Store } from '@blocksuite/store'; | ||
import { RichText } from '../rich-text/rich-text'; | ||
import type { BaseBlockModel, Space } from '@blocksuite/store'; | ||
import type { RichText } from '../rich-text/rich-text'; | ||
import { assertExists, caretRangeFromPoint, matchFlavours } from './std'; | ||
import { SelectedBlock, SelectionInfo, SelectionPosition } from './types'; | ||
import type { SelectedBlock, SelectionInfo, SelectionPosition } from './types'; | ||
import { | ||
@@ -17,2 +17,3 @@ getBlockElementByModel, | ||
import type { SelectionEvent } from './gesture'; | ||
const SCROLL_THRESHOLD = 100; | ||
@@ -50,2 +51,16 @@ // /[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]/u | ||
if (newRange) { | ||
if (!(newRange.endContainer.nodeType === Node.TEXT_NODE)) { | ||
const lastTextNode = getLastTextNode(newRange.endContainer); | ||
if (lastTextNode) { | ||
newRange = document.createRange(); | ||
newRange.setStart( | ||
lastTextNode, | ||
lastTextNode.textContent?.length || 0 | ||
); | ||
newRange.setEnd( | ||
lastTextNode, | ||
lastTextNode.textContent?.length || 0 | ||
); | ||
} | ||
} | ||
range.setEnd(newRange.endContainer, newRange.endOffset); | ||
@@ -64,2 +79,10 @@ } | ||
if (newRange) { | ||
if (!(newRange.startContainer.nodeType === Node.TEXT_NODE)) { | ||
const firstTextNode = getFirstTextNode(newRange.startContainer); | ||
if (firstTextNode) { | ||
newRange = document.createRange(); | ||
newRange.setStart(firstTextNode, 0); | ||
newRange.setEnd(firstTextNode, 0); | ||
} | ||
} | ||
range.setStart(newRange.endContainer, newRange.endOffset); | ||
@@ -74,18 +97,18 @@ } | ||
export function focusRichText( | ||
async function sleep(delay = 0) { | ||
return new Promise(resolve => { | ||
setTimeout(() => { | ||
resolve(null); | ||
}, delay); | ||
}); | ||
} | ||
export async function focusRichText( | ||
position: SelectionPosition, | ||
editableContainer: Element | ||
) { | ||
let { top, left, bottom, right } = Rect.fromDom(editableContainer); | ||
const [oldTop, oldBottom] = [top, bottom]; | ||
// TODO optimize how get scroll container | ||
const scrollContainer = editableContainer.closest('.affine-editor-container'); | ||
const { top, left, bottom, right } = Rect.fromDom(editableContainer); | ||
const { clientHeight } = document.documentElement; | ||
// TODO: improve the logic | ||
if (top + 20 > clientHeight || bottom < 20) { | ||
editableContainer.scrollIntoView(); | ||
const newRect = Rect.fromDom(editableContainer); | ||
top = newRect.top; | ||
left = newRect.left; | ||
bottom = newRect.bottom; | ||
right = newRect.right; | ||
} | ||
const lineHeight = | ||
@@ -100,12 +123,28 @@ Number( | ||
let newLeft = x; | ||
if (oldBottom <= y) { | ||
newTop = bottom - lineHeight / 2; | ||
if (bottom <= y) { | ||
let finalBottom = bottom; | ||
if (bottom < SCROLL_THRESHOLD && scrollContainer) { | ||
scrollContainer.scrollTop = | ||
scrollContainer.scrollTop - SCROLL_THRESHOLD + bottom; | ||
// set scroll may has a animation, wait for over | ||
await sleep(); | ||
finalBottom = editableContainer.getBoundingClientRect().bottom; | ||
} | ||
newTop = finalBottom - lineHeight / 2; | ||
} | ||
if (oldTop >= y) { | ||
newTop = top + lineHeight / 2; | ||
if (bottom >= y) { | ||
let finalTop = top; | ||
if (scrollContainer && top > clientHeight - SCROLL_THRESHOLD) { | ||
scrollContainer.scrollTop = | ||
scrollContainer.scrollTop + (top + SCROLL_THRESHOLD - clientHeight); | ||
// set scroll may has a animation, wait for over | ||
await sleep(); | ||
finalTop = editableContainer.getBoundingClientRect().top; | ||
} | ||
newTop = finalTop + lineHeight / 2; | ||
} | ||
if (x < left) { | ||
if (x <= left) { | ||
newLeft = left + 1; | ||
} | ||
if (x > right) { | ||
if (x >= right) { | ||
newLeft = right - 1; | ||
@@ -244,3 +283,3 @@ } | ||
const model = models[i]; | ||
const parent = model.store.getParent(model); | ||
const parent = model.space.getParent(model); | ||
const block = { id: model.id, children: [] }; | ||
@@ -268,4 +307,4 @@ if (!parent || !parentMap.has(parent.id)) { | ||
export function getSelectInfo(store: Store): SelectionInfo { | ||
if (!store.root) { | ||
export function getSelectInfo(space: Space): SelectionInfo { | ||
if (!space.root) { | ||
return { | ||
@@ -280,3 +319,3 @@ type: 'None', | ||
let selectedModels: BaseBlockModel[] = []; | ||
const page = getDefaultPageBlock(store.root); | ||
const page = getDefaultPageBlock(space.root); | ||
const { state } = page.selection; | ||
@@ -364,3 +403,3 @@ const nativeSelection = window.getSelection(); | ||
export function handleNativeRangeClick(store: Store, e: SelectionEvent) { | ||
export function handleNativeRangeClick(space: Space, e: SelectionEvent) { | ||
const range = caretRangeFromPoint(e.raw.clientX, e.raw.clientY); | ||
@@ -385,6 +424,6 @@ const startContainer = range?.startContainer; | ||
} else if (isBlankAreaAfterLastBlock(startContainer)) { | ||
const { root } = store; | ||
const { root } = space; | ||
const lastChild = root?.lastChild(); | ||
assertExists(lastChild); | ||
if (matchFlavours(lastChild, ['paragraph', 'list'])) { | ||
if (matchFlavours(lastChild, ['affine:paragraph', 'affine:list'])) { | ||
const block = getBlockElementByModel(lastChild); | ||
@@ -397,3 +436,3 @@ if (!block) return; | ||
export function handleNativeRangeDblClick(store: Store, e: SelectionEvent) { | ||
export function handleNativeRangeDblClick(space: Space, e: SelectionEvent) { | ||
const selection = window.getSelection(); | ||
@@ -434,4 +473,4 @@ if (selection && selection.isCollapsed && selection.anchorNode) { | ||
) { | ||
startNode = currentTextNode; | ||
endNode = currentTextNode; | ||
startNode = currentTextNode as Text; | ||
endNode = currentTextNode as Text; | ||
startOffset = selection.anchorOffset; | ||
@@ -600,5 +639,13 @@ endOffset = selection.anchorOffset + 1; | ||
} | ||
return leafNodes; | ||
return leafNodes as Text[]; | ||
} | ||
export function getLastTextNode(node: Node) { | ||
return leftFirstSearchLeafNodes(node).pop(); | ||
} | ||
export function getFirstTextNode(node: Node) { | ||
return leftFirstSearchLeafNodes(node)[0]; | ||
} | ||
export function getSplicedTitle(title: HTMLInputElement) { | ||
@@ -605,0 +652,0 @@ const text = [...title.value]; |
@@ -1,19 +0,12 @@ | ||
import type { BaseBlockModel } from '@blocksuite/store'; | ||
import { Utils } from '@blocksuite/store'; | ||
import type { Detail } from './types'; | ||
// workaround ts(2775) | ||
export function assertExists<T>(val: T | null | undefined): asserts val is T { | ||
if (val === null || val === undefined) { | ||
throw new Error('val does not exist'); | ||
} | ||
Utils.assertExists(val); | ||
} | ||
export function assertFlavours(model: BaseBlockModel, allowed: string[]) { | ||
if (!allowed.includes(model.flavour)) { | ||
throw new Error(`model flavour ${model.flavour} is not allowed`); | ||
} | ||
} | ||
export const assertFlavours = Utils.assertFlavours; | ||
export function matchFlavours(model: BaseBlockModel, expected: string[]) { | ||
return expected.includes(model.flavour); | ||
} | ||
export const matchFlavours = Utils.matchFlavours; | ||
@@ -20,0 +13,0 @@ const IS_FIREFOX = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; |
@@ -1,3 +0,3 @@ | ||
import type { BaseBlockModel, Store } from '@blocksuite/store'; | ||
import { Point } from './rect'; | ||
import type { BaseBlockModel, Store, Space } from '@blocksuite/store'; | ||
import type { Point } from './rect'; | ||
@@ -14,2 +14,3 @@ export type SelectionPosition = 'start' | 'end' | Point; | ||
store: Store; | ||
space: Space; | ||
flavour: string; | ||
@@ -16,0 +17,0 @@ } |
@@ -0,1 +1,2 @@ | ||
/// <reference types="vite/client" /> | ||
import { LitElement, html, css, unsafeCSS } from 'lit'; | ||
@@ -2,0 +3,0 @@ import { customElement, property } from 'lit/decorators.js'; |
@@ -1,5 +0,5 @@ | ||
import { Store, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
import { Space, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
export interface GroupBlockProps extends IBaseBlockProps { | ||
flavour: 'group'; | ||
flavour: 'affine:group'; | ||
/** packed field */ | ||
@@ -10,9 +10,9 @@ xywh: string; | ||
export class GroupBlockModel extends BaseBlockModel implements GroupBlockProps { | ||
flavour = 'group' as const; | ||
flavour = 'affine:group' as const; | ||
xywh: string; | ||
constructor(store: Store, props: Partial<GroupBlockModel>) { | ||
super(store, props); | ||
constructor(space: Space, props: Partial<GroupBlockModel>) { | ||
super(space, props); | ||
this.xywh = props.xywh ?? '[0,0,720,480]'; | ||
} | ||
} |
@@ -0,1 +1,2 @@ | ||
/// <reference types="vite/client" /> | ||
import { LitElement, html, css, unsafeCSS } from 'lit'; | ||
@@ -46,4 +47,4 @@ import { customElement, property } from 'lit/decorators.js'; | ||
if (this.model.type !== 'todo') return; | ||
this.host.store.captureSync(); | ||
this.host.store.updateBlock(this.model, { | ||
this.host.space.captureSync(); | ||
this.host.space.updateBlock(this.model, { | ||
checked: !this.model.checked, | ||
@@ -50,0 +51,0 @@ }); |
@@ -1,2 +0,2 @@ | ||
import { Store, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
import { Space, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
@@ -6,3 +6,3 @@ type ListType = 'bulleted' | 'numbered' | 'todo'; | ||
export interface ListBlockProps extends IBaseBlockProps { | ||
flavour: 'list'; | ||
flavour: 'affine:list'; | ||
type: ListType; | ||
@@ -13,8 +13,8 @@ checked: boolean; | ||
export class ListBlockModel extends BaseBlockModel implements ListBlockProps { | ||
flavour = 'list' as const; | ||
flavour = 'affine:list' as const; | ||
type: ListType; | ||
checked: boolean; | ||
constructor(store: Store, props: Partial<ListBlockProps>) { | ||
super(store, props); | ||
constructor(space: Space, props: Partial<ListBlockProps>) { | ||
super(space, props); | ||
this.type = props.type ?? 'bulleted'; | ||
@@ -38,4 +38,4 @@ this.checked = props.checked ?? false; | ||
); | ||
const previousSiblingBlock = this.store.getBlockById(_previousSiblingId); | ||
const nextSiblingBlock = this.store.getBlockById(_nextSiblingId); | ||
const previousSiblingBlock = this.space.getBlockById(_previousSiblingId); | ||
const nextSiblingBlock = this.space.getBlockById(_nextSiblingId); | ||
switch (this.type) { | ||
@@ -42,0 +42,0 @@ case 'bulleted': |
@@ -1,2 +0,2 @@ | ||
import { ListBlockModel } from '../list-model'; | ||
import type { ListBlockModel } from '../list-model'; | ||
import { points, checkboxChecked, checkboxUnchecked } from './icons'; | ||
@@ -3,0 +3,0 @@ import { getNumberPrefix } from './get-number-prefix'; |
@@ -1,6 +0,6 @@ | ||
import { BlockHost } from '../../__internal__'; | ||
import { ListBlockModel } from '../list-model'; | ||
import type { BlockHost } from '../../__internal__'; | ||
import type { ListBlockModel } from '../list-model'; | ||
const getIndex = (host: BlockHost, model: ListBlockModel) => { | ||
const siblings = host.store.getParent(model)?.children || []; | ||
const siblings = host.space.getParent(model)?.children || []; | ||
const fakeIndex = siblings.findIndex(v => v === model); | ||
@@ -27,6 +27,6 @@ | ||
let deep = 0; | ||
let parent = host.store.getParent(model); | ||
let parent = host.space.getParent(model); | ||
while (parent?.flavour === model.flavour) { | ||
deep++; | ||
parent = host.store.getParent(parent); | ||
parent = host.space.getParent(parent); | ||
} | ||
@@ -33,0 +33,0 @@ return deep; |
@@ -0,5 +1,6 @@ | ||
/// <reference types="vite/client" /> | ||
import { LitElement, html, css, unsafeCSS } from 'lit'; | ||
import { customElement, property, query, state } from 'lit/decorators.js'; | ||
import { styleMap } from 'lit/directives/style-map.js'; | ||
import { Disposable, Signal, Store, Text } from '@blocksuite/store'; | ||
import { Disposable, Signal, Space, Store, Text } from '@blocksuite/store'; | ||
import type { PageBlockModel } from '..'; | ||
@@ -20,3 +21,2 @@ import { | ||
import { DefaultSelectionManager } from './selection-manager'; | ||
import style from './style.css'; | ||
import { | ||
@@ -32,2 +32,3 @@ batchUpdateTextType, | ||
} from '../utils'; | ||
import style from './style.css'; | ||
@@ -107,4 +108,8 @@ export interface DefaultPageSignals { | ||
flavour = 'page' as const; | ||
get space() { | ||
return this.store.space; | ||
} | ||
flavour = 'affine:page' as const; | ||
selection!: DefaultSelectionManager; | ||
@@ -143,3 +148,3 @@ | ||
private _bindHotkeys() { | ||
const { store } = this; | ||
const { space } = this; | ||
const { | ||
@@ -161,3 +166,3 @@ BACKSPACE, | ||
bindCommonHotkey(store); | ||
bindCommonHotkey(space); | ||
hotkey.addListener(BACKSPACE, e => { | ||
@@ -171,3 +176,3 @@ const { state } = this.selection; | ||
const title = getSplicedTitle(target); | ||
store.updateBlock(this.model, { title }); | ||
space.updateBlock(this.model, { title }); | ||
} | ||
@@ -182,7 +187,7 @@ // collapsed delete | ||
if (state.type === 'native') { | ||
handleBackspace(store, e); | ||
handleBackspace(space, e); | ||
} else if (state.type === 'block') { | ||
const { selectedRichTexts } = state; | ||
handleBlockSelectionBatchDelete( | ||
store, | ||
space, | ||
selectedRichTexts.map(richText => richText.model) | ||
@@ -202,16 +207,28 @@ ); | ||
hotkey.addListener(H1, () => this._updateType('paragraph', 'h1', store)); | ||
hotkey.addListener(H2, () => this._updateType('paragraph', 'h2', store)); | ||
hotkey.addListener(H3, () => this._updateType('paragraph', 'h3', store)); | ||
hotkey.addListener(H4, () => this._updateType('paragraph', 'h4', store)); | ||
hotkey.addListener(H5, () => this._updateType('paragraph', 'h5', store)); | ||
hotkey.addListener(H6, () => this._updateType('paragraph', 'h6', store)); | ||
hotkey.addListener(H1, () => | ||
this._updateType('affine:paragraph', 'h1', space) | ||
); | ||
hotkey.addListener(H2, () => | ||
this._updateType('affine:paragraph', 'h2', space) | ||
); | ||
hotkey.addListener(H3, () => | ||
this._updateType('affine:paragraph', 'h3', space) | ||
); | ||
hotkey.addListener(H4, () => | ||
this._updateType('affine:paragraph', 'h4', space) | ||
); | ||
hotkey.addListener(H5, () => | ||
this._updateType('affine:paragraph', 'h5', space) | ||
); | ||
hotkey.addListener(H6, () => | ||
this._updateType('affine:paragraph', 'h6', space) | ||
); | ||
hotkey.addListener(NUMBERED_LIST, () => | ||
this._updateType('list', 'numbered', store) | ||
this._updateType('affine:list', 'numbered', space) | ||
); | ||
hotkey.addListener(BULLETED, () => | ||
this._updateType('list', 'bulleted', store) | ||
this._updateType('affine:list', 'bulleted', space) | ||
); | ||
hotkey.addListener(TEXT, () => | ||
this._updateType('paragraph', 'text', store) | ||
this._updateType('affine:paragraph', 'text', space) | ||
); | ||
@@ -254,3 +271,3 @@ hotkey.addListener(SHIFT_UP, e => { | ||
private _onTitleKeyDown(e: KeyboardEvent) { | ||
const hasContent = !this.store.isEmpty; | ||
const hasContent = !this.space.isEmpty; | ||
@@ -265,11 +282,11 @@ if (e.key === 'Enter' && hasContent) { | ||
const props = { | ||
flavour: 'paragraph', | ||
text: new Text(this.store, contentRight), | ||
flavour: 'affine:paragraph', | ||
text: new Text(this.space, contentRight), | ||
}; | ||
const newFirstParagraphId = this.store.addBlock(props, defaultGroup, 0); | ||
this.store.updateBlock(this.model, { title: contentLeft }); | ||
asyncFocusRichText(this.store, newFirstParagraphId); | ||
const newFirstParagraphId = this.space.addBlock(props, defaultGroup, 0); | ||
this.space.updateBlock(this.model, { title: contentLeft }); | ||
asyncFocusRichText(this.space, newFirstParagraphId); | ||
} else if (e.key === 'ArrowDown' && hasContent) { | ||
e.preventDefault(); | ||
asyncFocusRichText(this.store, this.model.children[0].children[0].id); | ||
asyncFocusRichText(this.space, this.model.children[0].children[0].id); | ||
} | ||
@@ -279,9 +296,9 @@ } | ||
private _onTitleInput(e: InputEvent) { | ||
const { store } = this; | ||
const { space } = this; | ||
if (!this.model.id) { | ||
const title = (e.target as HTMLInputElement).value; | ||
const pageId = store.addBlock({ flavour: 'page', title }); | ||
const groupId = store.addBlock({ flavour: 'group' }, pageId); | ||
store.addBlock({ flavour: 'paragraph' }, groupId); | ||
const pageId = space.addBlock({ flavour: 'affine:page', title }); | ||
const groupId = space.addBlock({ flavour: 'affine:group' }, pageId); | ||
space.addBlock({ flavour: 'affine:paragraph' }, groupId); | ||
return; | ||
@@ -291,6 +308,6 @@ } | ||
const title = (e.target as HTMLInputElement).value; | ||
store.updateBlock(this.model, { title }); | ||
space.updateBlock(this.model, { title }); | ||
} | ||
private _updateType(flavour: string, type: string, store: Store) { | ||
private _updateType(flavour: string, type: string, space: Space) { | ||
const { state } = this.selection; | ||
@@ -300,3 +317,3 @@ if (state.selectedRichTexts.length > 0) { | ||
flavour, | ||
store, | ||
space, | ||
state.selectedRichTexts.map(richText => richText.model), | ||
@@ -306,3 +323,3 @@ type | ||
} else { | ||
updateTextType(flavour, type, store); | ||
updateTextType(flavour, type, space); | ||
} | ||
@@ -324,3 +341,3 @@ } | ||
this.selection = new DefaultSelectionManager( | ||
this.store, | ||
this.space, | ||
this.mouseRoot, | ||
@@ -360,6 +377,6 @@ this.signals | ||
tryUpdateGroupSize(this.store, 1); | ||
tryUpdateGroupSize(this.space, 1); | ||
this.addEventListener('keydown', e => { | ||
if (e.ctrlKey || e.metaKey || e.shiftKey) return; | ||
tryUpdateGroupSize(this.store, 1); | ||
tryUpdateGroupSize(this.space, 1); | ||
}); | ||
@@ -366,0 +383,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { Store } from '@blocksuite/store'; | ||
import type { Space } from '@blocksuite/store'; | ||
import { | ||
@@ -15,4 +15,4 @@ initMouseEventHandlers, | ||
} from '../../__internal__'; | ||
import { RichText } from '../../__internal__/rich-text/rich-text'; | ||
import { repairerContextMenuRange } from '../utils/cursor'; | ||
import type { RichText } from '../../__internal__/rich-text/rich-text'; | ||
import { repairContextMenuRange } from '../utils/cursor'; | ||
import type { DefaultPageSignals } from './default-page-block'; | ||
@@ -103,3 +103,3 @@ | ||
export class DefaultSelectionManager { | ||
store: Store; | ||
space: Space; | ||
state = new PageSelectionState('none'); | ||
@@ -111,7 +111,7 @@ private _container: HTMLElement; | ||
constructor( | ||
store: Store, | ||
space: Space, | ||
container: HTMLElement, | ||
signals: DefaultPageSignals | ||
) { | ||
this.store = store; | ||
this.space = space; | ||
this._signals = signals; | ||
@@ -213,3 +213,3 @@ this._container = container; | ||
handleNativeRangeClick(this.store, e); | ||
handleNativeRangeClick(this.space, e); | ||
}; | ||
@@ -222,7 +222,7 @@ | ||
if (e.raw.target instanceof HTMLInputElement) return; | ||
handleNativeRangeDblClick(this.store, e); | ||
handleNativeRangeDblClick(this.space, e); | ||
}; | ||
private _onContainerContextMenu = (e: SelectionEvent) => { | ||
repairerContextMenuRange(e); | ||
repairContextMenuRange(e); | ||
}; | ||
@@ -229,0 +229,0 @@ |
import { html, type TemplateResult } from 'lit'; | ||
import { styleMap } from 'lit/directives/style-map.js'; | ||
import { repeat } from 'lit/directives/repeat.js'; | ||
import { BaseBlockModel } from '@blocksuite/store'; | ||
import type { BaseBlockModel } from '@blocksuite/store'; | ||
import { GroupBlockModel } from '../..'; | ||
import type { GroupBlockModel } from '../..'; | ||
import type { | ||
@@ -8,0 +8,0 @@ EdgelessSelectionManager, |
@@ -0,4 +1,5 @@ | ||
/// <reference types="vite/client" /> | ||
import { LitElement, html, unsafeCSS, css } from 'lit'; | ||
import { customElement, property, state } from 'lit/decorators.js'; | ||
import { Disposable, Signal, Store } from '@blocksuite/store'; | ||
import { Disposable, Signal, Space, Store } from '@blocksuite/store'; | ||
import type { GroupBlockModel, PageBlockModel } from '../..'; | ||
@@ -24,3 +25,2 @@ import { | ||
} from './selection-manager'; | ||
import style from './style.css'; | ||
import { | ||
@@ -33,5 +33,6 @@ bindCommonHotkey, | ||
} from '../utils'; | ||
import style from './style.css'; | ||
export interface EdgelessContainer extends HTMLElement { | ||
readonly store: Store; | ||
readonly space: Space; | ||
readonly viewport: ViewportState; | ||
@@ -58,2 +59,6 @@ readonly mouseRoot: HTMLElement; | ||
get space() { | ||
return this.store.space; | ||
} | ||
flavour = 'edgeless' as const; | ||
@@ -84,42 +89,38 @@ | ||
private _bindHotkeys() { | ||
const { store } = this; | ||
const { space } = this; | ||
hotkey.addListener( | ||
HOTKEYS.BACKSPACE, | ||
this._handleBackspace.bind(this), | ||
this.flavour | ||
); | ||
hotkey.addListener(HOTKEYS.BACKSPACE, this._handleBackspace.bind(this)); | ||
hotkey.addListener(HOTKEYS.H1, () => | ||
this._updateType('paragraph', 'h1', store) | ||
this._updateType('affine:paragraph', 'h1', space) | ||
); | ||
hotkey.addListener(HOTKEYS.H2, () => | ||
this._updateType('paragraph', 'h2', store) | ||
this._updateType('affine:paragraph', 'h2', space) | ||
); | ||
hotkey.addListener(HOTKEYS.H3, () => | ||
this._updateType('paragraph', 'h3', store) | ||
this._updateType('affine:paragraph', 'h3', space) | ||
); | ||
hotkey.addListener(HOTKEYS.H4, () => | ||
this._updateType('paragraph', 'h4', store) | ||
this._updateType('affine:paragraph', 'h4', space) | ||
); | ||
hotkey.addListener(HOTKEYS.H5, () => | ||
this._updateType('paragraph', 'h5', store) | ||
this._updateType('affine:paragraph', 'h5', space) | ||
); | ||
hotkey.addListener(HOTKEYS.H6, () => | ||
this._updateType('paragraph', 'h6', store) | ||
this._updateType('affine:paragraph', 'h6', space) | ||
); | ||
hotkey.addListener(HOTKEYS.NUMBERED_LIST, () => | ||
this._updateType('list', 'numbered', store) | ||
this._updateType('affine:list', 'numbered', space) | ||
); | ||
hotkey.addListener(HOTKEYS.BULLETED, () => | ||
this._updateType('list', 'bulleted', store) | ||
this._updateType('affine:list', 'bulleted', space) | ||
); | ||
hotkey.addListener(HOTKEYS.TEXT, () => | ||
this._updateType('paragraph', 'text', store) | ||
this._updateType('affine:paragraph', 'text', space) | ||
); | ||
bindCommonHotkey(store); | ||
bindCommonHotkey(space); | ||
} | ||
private _updateType(flavour: string, type: string, store: Store): void { | ||
updateTextType(flavour, type, store); | ||
private _updateType(flavour: string, type: string, space: Space): void { | ||
updateTextType(flavour, type, space); | ||
} | ||
@@ -134,3 +135,3 @@ | ||
if (this._selection.blockSelectionState.type === 'single') { | ||
handleBackspace(this.store, e); | ||
handleBackspace(this.space, e); | ||
} | ||
@@ -179,3 +180,3 @@ } | ||
this.signals.updateSelection.on(() => this.requestUpdate()); | ||
this._historyDisposable = this.store.signals.historyUpdated.on(() => { | ||
this._historyDisposable = this.space.signals.historyUpdated.on(() => { | ||
this._clearSelection(); | ||
@@ -188,3 +189,3 @@ }); | ||
if (e.ctrlKey || e.metaKey || e.shiftKey) return; | ||
tryUpdateGroupSize(this.store, this.viewport.zoom); | ||
tryUpdateGroupSize(this.space, this.viewport.zoom); | ||
}); | ||
@@ -191,0 +192,0 @@ |
@@ -1,3 +0,3 @@ | ||
import { GroupBlockModel } from '../../group-block'; | ||
import { EdgelessContainer } from './edgeless-page-block'; | ||
import type { GroupBlockModel } from '../../group-block'; | ||
import type { EdgelessContainer } from './edgeless-page-block'; | ||
import { | ||
@@ -13,3 +13,3 @@ SelectionEvent, | ||
import { getSelectionBoxBound, initWheelEventHandlers, pick } from './utils'; | ||
import { repairerContextMenuRange } from '../utils/cursor'; | ||
import { repairContextMenuRange } from '../utils/cursor'; | ||
@@ -168,8 +168,8 @@ interface NoneBlockSelectionState { | ||
private get _store() { | ||
return this._container.store; | ||
private get _space() { | ||
return this._container.space; | ||
} | ||
private get _blocks(): GroupBlockModel[] { | ||
return (this._store.root?.children as GroupBlockModel[]) ?? []; | ||
return (this._space.root?.children as GroupBlockModel[]) ?? []; | ||
} | ||
@@ -239,3 +239,3 @@ | ||
} | ||
handleNativeRangeClick(this._store, e); | ||
handleNativeRangeClick(this._space, e); | ||
break; | ||
@@ -283,3 +283,3 @@ } | ||
this._store.updateBlock(block, { | ||
this._space.updateBlock(block, { | ||
xywh: JSON.stringify([ | ||
@@ -353,3 +353,3 @@ modelX + e.delta.x / zoom, | ||
private _onContainerContextMenu = (e: SelectionEvent) => { | ||
repairerContextMenuRange(e); | ||
repairContextMenuRange(e); | ||
}; | ||
@@ -356,0 +356,0 @@ |
@@ -1,3 +0,3 @@ | ||
import { GroupBlockModel } from '../../group-block'; | ||
import { EdgelessContainer } from './edgeless-page-block'; | ||
import type { GroupBlockModel } from '../../group-block'; | ||
import type { EdgelessContainer } from './edgeless-page-block'; | ||
import type { ViewportState, XYWH } from './selection-manager'; | ||
@@ -4,0 +4,0 @@ |
@@ -1,8 +0,8 @@ | ||
import { BaseBlockModel, Store } from '@blocksuite/store'; | ||
import { BaseBlockModel, Space } from '@blocksuite/store'; | ||
export class PageBlockModel extends BaseBlockModel { | ||
flavour = 'page' as const; | ||
flavour = 'affine:page' as const; | ||
title = ''; | ||
constructor(store: Store, props: Partial<PageBlockModel>) { | ||
super(store, props); | ||
constructor(space: Space, props: Partial<PageBlockModel>) { | ||
super(space, props); | ||
this.title = props.title ?? ''; | ||
@@ -9,0 +9,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { Store } from '@blocksuite/store'; | ||
import type { Space } from '@blocksuite/store'; | ||
import { hotkey, HOTKEYS } from '../../__internal__'; | ||
@@ -7,13 +7,13 @@ import { createLink } from '../../__internal__/rich-text/link-node'; | ||
export function bindCommonHotkey(store: Store) { | ||
hotkey.addListener(INLINE_CODE, e => handleFormat(store, e, 'code')); | ||
hotkey.addListener(STRIKE, e => handleFormat(store, e, 'strike')); | ||
export function bindCommonHotkey(space: Space) { | ||
hotkey.addListener(INLINE_CODE, e => handleFormat(space, e, 'code')); | ||
hotkey.addListener(STRIKE, e => handleFormat(space, e, 'strike')); | ||
hotkey.addListener(LINK, e => { | ||
e.preventDefault(); | ||
hotkey.withDisableHotkey(async () => { | ||
await createLink(store, e); | ||
await createLink(space, e); | ||
}); | ||
}); | ||
hotkey.addListener(UNDO, () => store.undo()); | ||
hotkey.addListener(REDO, () => store.redo()); | ||
hotkey.addListener(UNDO, () => space.undo()); | ||
hotkey.addListener(REDO, () => space.redo()); | ||
// !!! | ||
@@ -20,0 +20,0 @@ // Don't forget to remove hotkeys at `_removeHotkeys` |
@@ -1,3 +0,3 @@ | ||
import { Store, Text, BaseBlockModel } from '@blocksuite/store'; | ||
import { GroupBlockModel } from '../../group-block'; | ||
import type { Space, Text, BaseBlockModel } from '@blocksuite/store'; | ||
import type { GroupBlockModel } from '../../group-block'; | ||
import { | ||
@@ -28,3 +28,3 @@ assertExists, | ||
function deleteModels(store: Store, models: BaseBlockModel[]) { | ||
function deleteModels(space: Space, models: BaseBlockModel[]) { | ||
const selection = window.getSelection(); | ||
@@ -54,8 +54,12 @@ const first = models[0]; | ||
); | ||
lastRichText.model.text?.delete(0, endTextIndex); | ||
firstRichText.model.text?.join(lastRichText.model.text as Text); | ||
const isLastRichTextFullSelected: boolean = | ||
lastRichText.model.text?.length === endTextIndex; | ||
if (!isLastRichTextFullSelected) { | ||
lastRichText.model.text?.delete(0, endTextIndex); | ||
firstRichText.model.text?.join(lastRichText.model.text as Text); | ||
} | ||
// delete models in between | ||
for (let i = 1; i <= models.length - 1; i++) { | ||
store.deleteBlock(models[i]); | ||
space.deleteBlock(models[i]); | ||
} | ||
@@ -66,13 +70,13 @@ | ||
export function updateTextType(flavour: string, type: string, store: Store) { | ||
export function updateTextType(flavour: string, type: string, space: Space) { | ||
const range = window.getSelection()?.getRangeAt(0); | ||
assertExists(range); | ||
const modelsInRange = getModelsByRange(range); | ||
store.captureSync(); | ||
space.captureSync(); | ||
modelsInRange.forEach(model => { | ||
assertFlavours(model, ['paragraph', 'list']); | ||
assertFlavours(model, ['affine:paragraph', 'affine:list']); | ||
if (model.flavour === flavour) { | ||
store.updateBlock(model, { type }); | ||
space.updateBlock(model, { type }); | ||
} else { | ||
transformBlock(store, model, flavour, type); | ||
transformBlock(space, model, flavour, type); | ||
} | ||
@@ -82,3 +86,3 @@ }); | ||
export function transformBlock( | ||
store: Store, | ||
space: Space, | ||
model: BaseBlockModel, | ||
@@ -88,3 +92,3 @@ flavour: string, | ||
) { | ||
const parent = store.getParent(model); | ||
const parent = space.getParent(model); | ||
assertExists(parent); | ||
@@ -98,5 +102,5 @@ const blockProps = { | ||
const index = parent.children.indexOf(model); | ||
store.deleteBlock(model); | ||
const id = store.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(store, id); | ||
space.deleteBlock(model); | ||
const id = space.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(space, id); | ||
} | ||
@@ -106,13 +110,13 @@ | ||
flavour: string, | ||
store: Store, | ||
space: Space, | ||
models: ExtendedModel[], | ||
type: string | ||
) { | ||
store.captureSync(); | ||
space.captureSync(); | ||
for (const model of models) { | ||
assertFlavours(model, ['paragraph', 'list']); | ||
assertFlavours(model, ['affine:paragraph', 'affine:list']); | ||
if (model.flavour === flavour) { | ||
store.updateBlock(model, { type }); | ||
space.updateBlock(model, { type }); | ||
} else { | ||
transformBlock(store, model, 'paragraph', type); | ||
transformBlock(space, model, 'affine:paragraph', type); | ||
} | ||
@@ -122,3 +126,3 @@ } | ||
export function handleBackspace(store: Store, e: KeyboardEvent) { | ||
export function handleBackspace(space: Space, e: KeyboardEvent) { | ||
// workaround page title | ||
@@ -144,3 +148,3 @@ if (e.target instanceof HTMLInputElement) return; | ||
const intersectedModels = getModelsByRange(range); | ||
deleteModels(store, intersectedModels); | ||
deleteModels(space, intersectedModels); | ||
} | ||
@@ -152,3 +156,3 @@ } | ||
models: BaseBlockModel[], | ||
store: Store, | ||
space: Space, | ||
key: string | ||
@@ -191,3 +195,3 @@ ) { | ||
store.captureSync(); | ||
space.captureSync(); | ||
firstRichText.model.text?.format( | ||
@@ -213,3 +217,3 @@ firstIndex, | ||
export function handleFormat(store: Store, e: KeyboardEvent, key: string) { | ||
export function handleFormat(space: Space, e: KeyboardEvent, key: string) { | ||
// workaround page title | ||
@@ -228,3 +232,3 @@ e.preventDefault(); | ||
assertExists(range); | ||
store.captureSync(); | ||
space.captureSync(); | ||
@@ -239,3 +243,3 @@ const { index, length } = range; | ||
} else { | ||
formatModelsByRange(models, store, key); | ||
formatModelsByRange(models, space, key); | ||
} | ||
@@ -278,6 +282,6 @@ } | ||
export function handleBlockSelectionBatchDelete( | ||
store: Store, | ||
space: Space, | ||
models: ExtendedModel[] | ||
) { | ||
store.captureSync(); | ||
space.captureSync(); | ||
assertExists(models[0].text); | ||
@@ -287,10 +291,10 @@ | ||
for (let i = 1; i < models.length; i++) { | ||
store.deleteBlock(models[i]); | ||
space.deleteBlock(models[i]); | ||
} | ||
} | ||
export function tryUpdateGroupSize(store: Store, zoom: number) { | ||
export function tryUpdateGroupSize(space: Space, zoom: number) { | ||
requestAnimationFrame(() => { | ||
if (!store.root) return; | ||
const groups = store.root.children as GroupBlockModel[]; | ||
if (!space.root) return; | ||
const groups = space.root.children as GroupBlockModel[]; | ||
groups.forEach(model => { | ||
@@ -312,3 +316,3 @@ const blockElement = getBlockElementByModel(model); | ||
if (!almostEqual(newModelWidth, w) || !almostEqual(newModelHeight, h)) { | ||
store.updateBlock(model, { | ||
space.updateBlock(model, { | ||
xywh: JSON.stringify([x, y, newModelWidth, newModelHeight]), | ||
@@ -315,0 +319,0 @@ }); |
@@ -7,3 +7,3 @@ import { | ||
export function repairerContextMenuRange(e: SelectionEvent) { | ||
export function repairContextMenuRange(e: SelectionEvent) { | ||
const currentRange = window.getSelection()?.getRangeAt(0); | ||
@@ -10,0 +10,0 @@ const pointRange = caretRangeFromPoint(e.x, e.y); |
@@ -0,1 +1,2 @@ | ||
/// <reference types="vite/client" /> | ||
import { LitElement, html, css, unsafeCSS } from 'lit'; | ||
@@ -2,0 +3,0 @@ import { customElement, property } from 'lit/decorators.js'; |
@@ -1,2 +0,2 @@ | ||
import { Store, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
import { Space, BaseBlockModel, IBaseBlockProps } from '@blocksuite/store'; | ||
@@ -14,3 +14,3 @@ export type ParagraphType = | ||
export interface ParagraphBlockProps extends IBaseBlockProps { | ||
flavour: 'paragraph'; | ||
flavour: 'affine:paragraph'; | ||
type: ParagraphType; | ||
@@ -23,7 +23,7 @@ } | ||
{ | ||
flavour = 'paragraph' as const; | ||
flavour = 'affine:paragraph' as const; | ||
type: ParagraphType = 'text'; | ||
constructor(store: Store, props: Partial<ParagraphBlockProps>) { | ||
super(store, props); | ||
constructor(space: Space, props: Partial<ParagraphBlockProps>) { | ||
super(space, props); | ||
this.type = props.type ?? 'text'; | ||
@@ -30,0 +30,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1281283
27231
+ Added@blocksuite/store@0.3.0-alpha.0(transitive)
+ Addedcall-bound@1.0.3(transitive)
+ Addedy-indexeddb@9.0.12(transitive)
- Removed@blocksuite/store@0.2.24(transitive)
- Removedcall-bound@1.0.2(transitive)