refactor(editor): remove selection global types (#9532)

Closes: [BS-2217](https://linear.app/affine-design/issue/BS-2217/remove-global-types-in-selection)
This commit is contained in:
Saul-Mirone
2025-01-06 03:45:10 +00:00
parent 8669936f2f
commit fc863e484c
105 changed files with 501 additions and 358 deletions
@@ -13,6 +13,11 @@ import {
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { humanFileSize } from '@blocksuite/affine-shared/utils'; import { humanFileSize } from '@blocksuite/affine-shared/utils';
import {
BlockSelection,
SurfaceSelection,
TextSelection,
} from '@blocksuite/block-std';
import { Slice } from '@blocksuite/store'; import { Slice } from '@blocksuite/store';
import { flip, offset } from '@floating-ui/dom'; import { flip, offset } from '@floating-ui/dom';
import { html, nothing } from 'lit'; import { html, nothing } from 'lit';
@@ -44,7 +49,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<
this, this,
({ abortController }) => { ({ abortController }) => {
const selection = this.host.selection; const selection = this.host.selection;
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if ( if (
!!textSelection && !!textSelection &&
(!!textSelection.to || !!textSelection.from.length) (!!textSelection.to || !!textSelection.from.length)
@@ -52,7 +57,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<
return null; return null;
} }
const blockSelections = selection.filter('block'); const blockSelections = selection.filter(BlockSelection);
if ( if (
blockSelections.length > 1 || blockSelections.length > 1 ||
(blockSelections.length === 1 && (blockSelections.length === 1 &&
@@ -126,7 +131,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<
private _selectBlock() { private _selectBlock() {
const selectionManager = this.host.selection; const selectionManager = this.host.selection;
const blockSelection = selectionManager.create('block', { const blockSelection = selectionManager.create(BlockSelection, {
blockId: this.blockId, blockId: this.blockId,
}); });
selectionManager.setGroup('note', [blockSelection]); selectionManager.setGroup('note', [blockSelection]);
@@ -167,7 +172,8 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<
this.disposables.add( this.disposables.add(
this.std.selection.slots.changed.on(() => { this.std.selection.slots.changed.on(() => {
this._isSelected = this._isSelected =
!!this.selected?.is('block') || !!this.selected?.is('surface'); !!this.selected?.is(BlockSelection) ||
!!this.selected?.is(SurfaceSelection);
this._showOverlay = this._showOverlay =
this._isResizing || this._isDragging || !this._isSelected; this._isResizing || this._isDragging || !this._isSelected;
@@ -4,6 +4,7 @@ import {
} from '@blocksuite/affine-components/caption'; } from '@blocksuite/affine-components/caption';
import type { BookmarkBlockModel } from '@blocksuite/affine-model'; import type { BookmarkBlockModel } from '@blocksuite/affine-model';
import { DocModeProvider } from '@blocksuite/affine-shared/services'; import { DocModeProvider } from '@blocksuite/affine-shared/services';
import { BlockSelection } from '@blocksuite/block-std';
import { html } from 'lit'; import { html } from 'lit';
import { property, query } from 'lit/decorators.js'; import { property, query } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
@@ -73,7 +74,7 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<
} }
override renderBlock() { override renderBlock() {
const selected = !!this.selected?.is('block'); const selected = !!this.selected?.is(BlockSelection);
return html` return html`
<div <div
draggable="${this.blockDraggable ? 'true' : 'false'}" draggable="${this.blockDraggable ? 'true' : 'false'}"
@@ -2,7 +2,11 @@ import { getEmbedCardIcons } from '@blocksuite/affine-block-embed';
import { WebIcon16 } from '@blocksuite/affine-components/icons'; import { WebIcon16 } from '@blocksuite/affine-components/icons';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { getHostName } from '@blocksuite/affine-shared/utils'; import { getHostName } from '@blocksuite/affine-shared/utils';
import { ShadowlessElement } from '@blocksuite/block-std'; import {
BlockSelection,
ShadowlessElement,
SurfaceSelection,
} from '@blocksuite/block-std';
import { WithDisposable } from '@blocksuite/global/utils'; import { WithDisposable } from '@blocksuite/global/utils';
import { OpenInNewIcon } from '@blocksuite/icons/lit'; import { OpenInNewIcon } from '@blocksuite/icons/lit';
import { html } from 'lit'; import { html } from 'lit';
@@ -31,7 +35,7 @@ export class BookmarkCard extends WithDisposable(ShadowlessElement) {
private _selectBlock() { private _selectBlock() {
const selectionManager = this.bookmark.host.selection; const selectionManager = this.bookmark.host.selection;
const blockSelection = selectionManager.create('block', { const blockSelection = selectionManager.create(BlockSelection, {
blockId: this.bookmark.blockId, blockId: this.bookmark.blockId,
}); });
selectionManager.setGroup('note', [blockSelection]); selectionManager.setGroup('note', [blockSelection]);
@@ -55,8 +59,8 @@ export class BookmarkCard extends WithDisposable(ShadowlessElement) {
this.disposables.add( this.disposables.add(
this.bookmark.selection.slots.changed.on(() => { this.bookmark.selection.slots.changed.on(() => {
this._isSelected = this._isSelected =
!!this.bookmark.selected?.is('block') || !!this.bookmark.selected?.is(BlockSelection) ||
!!this.bookmark.selected?.is('surface'); !!this.bookmark.selected?.is(SurfaceSelection);
}) })
); );
} }
@@ -11,7 +11,11 @@ import {
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { getViewportElement } from '@blocksuite/affine-shared/utils'; import { getViewportElement } from '@blocksuite/affine-shared/utils';
import type { BlockComponent } from '@blocksuite/block-std'; import type { BlockComponent } from '@blocksuite/block-std';
import { getInlineRangeProvider } from '@blocksuite/block-std'; import {
BlockSelection,
getInlineRangeProvider,
TextSelection,
} from '@blocksuite/block-std';
import { IS_MAC } from '@blocksuite/global/env'; import { IS_MAC } from '@blocksuite/global/env';
import { noop } from '@blocksuite/global/utils'; import { noop } from '@blocksuite/global/utils';
import { import {
@@ -178,7 +182,7 @@ export class CodeBlockComponent extends CaptionedBlockComponent<
this.bindHotKey({ this.bindHotKey({
Backspace: ctx => { Backspace: ctx => {
const state = ctx.get('keyboardState'); const state = ctx.get('keyboardState');
const textSelection = selectionManager.find('text'); const textSelection = selectionManager.find(TextSelection);
if (!textSelection) { if (!textSelection) {
state.raw.preventDefault(); state.raw.preventDefault();
return; return;
@@ -189,7 +193,7 @@ export class CodeBlockComponent extends CaptionedBlockComponent<
if (from.index === 0 && from.length === 0) { if (from.index === 0 && from.length === 0) {
state.raw.preventDefault(); state.raw.preventDefault();
selectionManager.setGroup('note', [ selectionManager.setGroup('note', [
selectionManager.create('block', { blockId: this.blockId }), selectionManager.create(BlockSelection, { blockId: this.blockId }),
]); ]);
return true; return true;
} }
@@ -8,6 +8,7 @@ import {
} from '@blocksuite/affine-components/icons'; } from '@blocksuite/affine-components/icons';
import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar'; import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar';
import { isInsidePageEditor } from '@blocksuite/affine-shared/utils'; import { isInsidePageEditor } from '@blocksuite/affine-shared/utils';
import { BlockSelection } from '@blocksuite/block-std';
import { noop, sleep } from '@blocksuite/global/utils'; import { noop, sleep } from '@blocksuite/global/utils';
import { html } from 'lit'; import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js'; import { ifDefined } from 'lit/directives/if-defined.js';
@@ -134,7 +135,7 @@ export const clipboardGroup: MenuItemGroup<CodeBlockToolbarContext> = {
host.updateComplete host.updateComplete
.then(() => { .then(() => {
host.selection.setGroup('note', [ host.selection.setGroup('note', [
host.selection.create('block', { host.selection.create(BlockSelection, {
blockId: codeId, blockId: codeId,
}), }),
]); ]);
@@ -9,7 +9,11 @@ import {
} from '@blocksuite/affine-components/toolbar'; } from '@blocksuite/affine-components/toolbar';
import type { CodeBlockModel } from '@blocksuite/affine-model'; import type { CodeBlockModel } from '@blocksuite/affine-model';
import { PAGE_HEADER_HEIGHT } from '@blocksuite/affine-shared/consts'; import { PAGE_HEADER_HEIGHT } from '@blocksuite/affine-shared/consts';
import { WidgetComponent } from '@blocksuite/block-std'; import {
BlockSelection,
TextSelection,
WidgetComponent,
} from '@blocksuite/block-std';
import { limitShift, shift } from '@floating-ui/dom'; import { limitShift, shift } from '@floating-ui/dom';
import { html } from 'lit'; import { html } from 'lit';
@@ -34,7 +38,7 @@ export class AffineCodeToolbarWidget extends WidgetComponent<
const codeBlock = this.block; const codeBlock = this.block;
const selection = this.host.selection; const selection = this.host.selection;
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if ( if (
!!textSelection && !!textSelection &&
(!!textSelection.to || !!textSelection.from.length) (!!textSelection.to || !!textSelection.from.length)
@@ -42,7 +46,7 @@ export class AffineCodeToolbarWidget extends WidgetComponent<
return null; return null;
} }
const blockSelections = selection.filter('block'); const blockSelections = selection.filter(BlockSelection);
if ( if (
blockSelections.length > 1 || blockSelections.length > 1 ||
(blockSelections.length === 1 && (blockSelections.length === 1 &&
@@ -1,6 +1,7 @@
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption'; import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
import type { DividerBlockModel } from '@blocksuite/affine-model'; import type { DividerBlockModel } from '@blocksuite/affine-model';
import { BLOCK_CHILDREN_CONTAINER_PADDING_LEFT } from '@blocksuite/affine-shared/consts'; import { BLOCK_CHILDREN_CONTAINER_PADDING_LEFT } from '@blocksuite/affine-shared/consts';
import { BlockSelection } from '@blocksuite/block-std';
import { html } from 'lit'; import { html } from 'lit';
import { dividerBlockStyles } from './styles.js'; import { dividerBlockStyles } from './styles.js';
@@ -15,7 +16,7 @@ export class DividerBlockComponent extends CaptionedBlockComponent<DividerBlockM
this.handleEvent('click', () => { this.handleEvent('click', () => {
this.host.selection.setGroup('note', [ this.host.selection.setGroup('note', [
this.host.selection.create('block', { this.host.selection.create(BlockSelection, {
blockId: this.blockId, blockId: this.blockId,
}), }),
]); ]);
@@ -3,7 +3,11 @@ import type { EdgelessTextBlockModel } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { BlockComponent } from '@blocksuite/block-std'; import type { BlockComponent } from '@blocksuite/block-std';
import { GfxBlockComponent } from '@blocksuite/block-std'; import {
BlockSelection,
GfxBlockComponent,
TextSelection,
} from '@blocksuite/block-std';
import { Bound } from '@blocksuite/global/utils'; import { Bound } from '@blocksuite/global/utils';
import { css, html } from 'lit'; import { css, html } from 'lit';
import { query, state } from 'lit/decorators.js'; import { query, state } from 'lit/decorators.js';
@@ -88,7 +92,7 @@ export class EdgelessTextBlockComponent extends GfxBlockComponent<EdgelessTextBl
.then(() => { .then(() => {
const command = this.std.command; const command = this.std.command;
const blockSelections = this.model.children.map(child => const blockSelections = this.model.children.map(child =>
this.std.selection.create('block', { this.std.selection.create(BlockSelection, {
blockId: child.id, blockId: child.id,
}) })
); );
@@ -178,7 +182,7 @@ export class EdgelessTextBlockComponent extends GfxBlockComponent<EdgelessTextBl
if (newParagraphId) { if (newParagraphId) {
std.selection.setGroup('note', [ std.selection.setGroup('note', [
std.selection.create('text', { std.selection.create(TextSelection, {
from: { from: {
blockId: newParagraphId, blockId: newParagraphId,
index: 0, index: 0,
@@ -310,7 +314,7 @@ export class EdgelessTextBlockComponent extends GfxBlockComponent<EdgelessTextBl
const last = paragraphOrLists.at(-1); const last = paragraphOrLists.at(-1);
if (last) { if (last) {
this.host.selection.setGroup('note', [ this.host.selection.setGroup('note', [
this.host.selection.create('text', { this.host.selection.create(TextSelection, {
from: { from: {
blockId: last.blockId, blockId: last.blockId,
index: last.model.text?.length ?? 0, index: last.model.text?.length ?? 0,
@@ -9,7 +9,7 @@ import {
EMBED_CARD_WIDTH, EMBED_CARD_WIDTH,
} from '@blocksuite/affine-shared/consts'; } from '@blocksuite/affine-shared/consts';
import { DocModeProvider } from '@blocksuite/affine-shared/services'; import { DocModeProvider } from '@blocksuite/affine-shared/services';
import type { BlockService } from '@blocksuite/block-std'; import { BlockSelection, type BlockService } from '@blocksuite/block-std';
import type { GfxCompatibleProps } from '@blocksuite/block-std/gfx'; import type { GfxCompatibleProps } from '@blocksuite/block-std/gfx';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
import type { TemplateResult } from 'lit'; import type { TemplateResult } from 'lit';
@@ -56,7 +56,7 @@ export class EmbedBlockComponent<
} }
} }
const selected = !!this.selected?.is('block'); const selected = !!this.selected?.is(BlockSelection);
return html` return html`
<div <div
draggable="${this.blockDraggable ? 'true' : 'false'}" draggable="${this.blockDraggable ? 'true' : 'false'}"
@@ -7,7 +7,12 @@ import {
EMBED_CARD_HEIGHT, EMBED_CARD_HEIGHT,
EMBED_CARD_WIDTH, EMBED_CARD_WIDTH,
} from '@blocksuite/affine-shared/consts'; } from '@blocksuite/affine-shared/consts';
import type { BlockStdScope } from '@blocksuite/block-std'; import {
BlockSelection,
type BlockStdScope,
SurfaceSelection,
TextSelection,
} from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx'; import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { Bound, Vec } from '@blocksuite/global/utils'; import { Bound, Vec } from '@blocksuite/global/utils';
@@ -26,9 +31,9 @@ export function insertEmbedCard(
const selectionManager = host.selection; const selectionManager = host.selection;
let blockId: string | undefined; let blockId: string | undefined;
const textSelection = selectionManager.find('text'); const textSelection = selectionManager.find(TextSelection);
const blockSelection = selectionManager.find('block'); const blockSelection = selectionManager.find(BlockSelection);
const surfaceSelection = selectionManager.find('surface'); const surfaceSelection = selectionManager.find(SurfaceSelection);
if (textSelection) { if (textSelection) {
blockId = textSelection.blockId; blockId = textSelection.blockId;
} else if (blockSelection) { } else if (blockSelection) {
@@ -3,6 +3,7 @@ import type {
EmbedFigmaModel, EmbedFigmaModel,
EmbedFigmaStyles, EmbedFigmaStyles,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { BlockSelection, SurfaceSelection } from '@blocksuite/block-std';
import { html } from 'lit'; import { html } from 'lit';
import { state } from 'lit/decorators.js'; import { state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
@@ -41,7 +42,7 @@ export class EmbedFigmaBlockComponent extends EmbedBlockComponent<
private _selectBlock() { private _selectBlock() {
const selectionManager = this.host.selection; const selectionManager = this.host.selection;
const blockSelection = selectionManager.create('block', { const blockSelection = selectionManager.create(BlockSelection, {
blockId: this.blockId, blockId: this.blockId,
}); });
selectionManager.setGroup('note', [blockSelection]); selectionManager.setGroup('note', [blockSelection]);
@@ -77,7 +78,8 @@ export class EmbedFigmaBlockComponent extends EmbedBlockComponent<
this.disposables.add( this.disposables.add(
this.std.selection.slots.changed.on(() => { this.std.selection.slots.changed.on(() => {
this._isSelected = this._isSelected =
!!this.selected?.is('block') || !!this.selected?.is('surface'); !!this.selected?.is(BlockSelection) ||
!!this.selected?.is(SurfaceSelection);
this._showOverlay = this._showOverlay =
this._isResizing || this._isDragging || !this._isSelected; this._isResizing || this._isDragging || !this._isSelected;
@@ -4,6 +4,7 @@ import type {
EmbedGithubStyles, EmbedGithubStyles,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { BlockSelection, SurfaceSelection } from '@blocksuite/block-std';
import { html, nothing } from 'lit'; import { html, nothing } from 'lit';
import { property, state } from 'lit/decorators.js'; import { property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
@@ -61,7 +62,7 @@ export class EmbedGithubBlockComponent extends EmbedBlockComponent<
private _selectBlock() { private _selectBlock() {
const selectionManager = this.host.selection; const selectionManager = this.host.selection;
const blockSelection = selectionManager.create('block', { const blockSelection = selectionManager.create(BlockSelection, {
blockId: this.blockId, blockId: this.blockId,
}); });
selectionManager.setGroup('note', [blockSelection]); selectionManager.setGroup('note', [blockSelection]);
@@ -111,7 +112,8 @@ export class EmbedGithubBlockComponent extends EmbedBlockComponent<
this.disposables.add( this.disposables.add(
this.selection.slots.changed.on(() => { this.selection.slots.changed.on(() => {
this._isSelected = this._isSelected =
!!this.selected?.is('block') || !!this.selected?.is('surface'); !!this.selected?.is(BlockSelection) ||
!!this.selected?.is(SurfaceSelection);
}) })
); );
} }
@@ -1,4 +1,5 @@
import type { EmbedHtmlModel, EmbedHtmlStyles } from '@blocksuite/affine-model'; import type { EmbedHtmlModel, EmbedHtmlStyles } from '@blocksuite/affine-model';
import { BlockSelection, SurfaceSelection } from '@blocksuite/block-std';
import { html } from 'lit'; import { html } from 'lit';
import { query, state } from 'lit/decorators.js'; import { query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
@@ -35,7 +36,7 @@ export class EmbedHtmlBlockComponent extends EmbedBlockComponent<EmbedHtmlModel>
private _selectBlock() { private _selectBlock() {
const selectionManager = this.host.selection; const selectionManager = this.host.selection;
const blockSelection = selectionManager.create('block', { const blockSelection = selectionManager.create(BlockSelection, {
blockId: this.blockId, blockId: this.blockId,
}); });
selectionManager.setGroup('note', [blockSelection]); selectionManager.setGroup('note', [blockSelection]);
@@ -54,7 +55,8 @@ export class EmbedHtmlBlockComponent extends EmbedBlockComponent<EmbedHtmlModel>
this.disposables.add( this.disposables.add(
this.std.selection.slots.changed.on(() => { this.std.selection.slots.changed.on(() => {
this._isSelected = this._isSelected =
!!this.selected?.is('block') || !!this.selected?.is('surface'); !!this.selected?.is(BlockSelection) ||
!!this.selected?.is(SurfaceSelection);
this._showOverlay = this._showOverlay =
this._isResizing || this._isDragging || !this._isSelected; this._isResizing || this._isDragging || !this._isSelected;
@@ -21,6 +21,7 @@ import {
matchFlavours, matchFlavours,
referenceToNode, referenceToNode,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import { BlockSelection } from '@blocksuite/block-std';
import { Bound, throttle } from '@blocksuite/global/utils'; import { Bound, throttle } from '@blocksuite/global/utils';
import { Text } from '@blocksuite/store'; import { Text } from '@blocksuite/store';
import { computed } from '@preact/signals-core'; import { computed } from '@preact/signals-core';
@@ -107,7 +108,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
private readonly _selectBlock = () => { private readonly _selectBlock = () => {
const selectionManager = this.host.selection; const selectionManager = this.host.selection;
const blockSelection = selectionManager.create('block', { const blockSelection = selectionManager.create(BlockSelection, {
blockId: this.blockId, blockId: this.blockId,
}); });
selectionManager.setGroup('note', [blockSelection]); selectionManager.setGroup('note', [blockSelection]);
@@ -1,6 +1,7 @@
import { OpenIcon } from '@blocksuite/affine-components/icons'; import { OpenIcon } from '@blocksuite/affine-components/icons';
import type { EmbedLoomModel, EmbedLoomStyles } from '@blocksuite/affine-model'; import type { EmbedLoomModel, EmbedLoomStyles } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { BlockSelection, SurfaceSelection } from '@blocksuite/block-std';
import { html } from 'lit'; import { html } from 'lit';
import { property, state } from 'lit/decorators.js'; import { property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
@@ -46,7 +47,7 @@ export class EmbedLoomBlockComponent extends EmbedBlockComponent<
private _selectBlock() { private _selectBlock() {
const selectionManager = this.host.selection; const selectionManager = this.host.selection;
const blockSelection = selectionManager.create('block', { const blockSelection = selectionManager.create(BlockSelection, {
blockId: this.blockId, blockId: this.blockId,
}); });
selectionManager.setGroup('note', [blockSelection]); selectionManager.setGroup('note', [blockSelection]);
@@ -93,7 +94,8 @@ export class EmbedLoomBlockComponent extends EmbedBlockComponent<
this.disposables.add( this.disposables.add(
this.std.selection.slots.changed.on(() => { this.std.selection.slots.changed.on(() => {
this._isSelected = this._isSelected =
!!this.selected?.is('block') || !!this.selected?.is('surface'); !!this.selected?.is(BlockSelection) ||
!!this.selected?.is(SurfaceSelection);
this._showOverlay = this._showOverlay =
this._isResizing || this._isDragging || !this._isSelected; this._isResizing || this._isDragging || !this._isSelected;
@@ -1,5 +1,9 @@
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { isGfxBlockComponent, ShadowlessElement } from '@blocksuite/block-std'; import {
BlockSelection,
isGfxBlockComponent,
ShadowlessElement,
} from '@blocksuite/block-std';
import { throttle, WithDisposable } from '@blocksuite/global/utils'; import { throttle, WithDisposable } from '@blocksuite/global/utils';
import { html, nothing } from 'lit'; import { html, nothing } from 'lit';
import { property, queryAsync } from 'lit/decorators.js'; import { property, queryAsync } from 'lit/decorators.js';
@@ -64,7 +68,7 @@ export class EmbedSyncedDocCard extends WithDisposable(ShadowlessElement) {
private _selectBlock() { private _selectBlock() {
const selectionManager = this.host.selection; const selectionManager = this.host.selection;
const blockSelection = selectionManager.create('block', { const blockSelection = selectionManager.create(BlockSelection, {
blockId: this.block.blockId, blockId: this.block.blockId,
}); });
selectionManager.setGroup('note', [blockSelection]); selectionManager.setGroup('note', [blockSelection]);
@@ -8,7 +8,7 @@ import {
ThemeExtensionIdentifier, ThemeExtensionIdentifier,
ThemeProvider, ThemeProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { BlockStdScope } from '@blocksuite/block-std'; import { BlockSelection, BlockStdScope } from '@blocksuite/block-std';
import { assertExists, Bound } from '@blocksuite/global/utils'; import { assertExists, Bound } from '@blocksuite/global/utils';
import { html } from 'lit'; import { html } from 'lit';
import { choose } from 'lit/directives/choose.js'; import { choose } from 'lit/directives/choose.js';
@@ -52,7 +52,7 @@ export class EmbedEdgelessSyncedDocBlockComponent extends toEdgelessEmbedBlock(
} }
const theme = this.isPageMode ? appTheme : edgelessTheme; const theme = this.isPageMode ? appTheme : edgelessTheme;
const isSelected = !!this.selected?.is('block'); const isSelected = !!this.selected?.is(BlockSelection);
const scale = this.model.scale ?? 1; const scale = this.model.scale ?? 1;
this.dataset.nestedEditor = ''; this.dataset.nestedEditor = '';
@@ -19,6 +19,7 @@ import {
SpecProvider, SpecProvider,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import { import {
BlockSelection,
BlockServiceWatcher, BlockServiceWatcher,
BlockStdScope, BlockStdScope,
type EditorHost, type EditorHost,
@@ -164,7 +165,7 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
edgelessTheme = themeExtension.getEdgelessTheme(this.syncedDoc.id).value; edgelessTheme = themeExtension.getEdgelessTheme(this.syncedDoc.id).value;
} }
const theme = isPageMode ? appTheme : edgelessTheme; const theme = isPageMode ? appTheme : edgelessTheme;
const isSelected = !!this.selected?.is('block'); const isSelected = !!this.selected?.is(BlockSelection);
this.dataset.nestedEditor = ''; this.dataset.nestedEditor = '';
@@ -428,7 +429,7 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
private _selectBlock() { private _selectBlock() {
const selectionManager = this.host.selection; const selectionManager = this.host.selection;
const blockSelection = selectionManager.create('block', { const blockSelection = selectionManager.create(BlockSelection, {
blockId: this.blockId, blockId: this.blockId,
}); });
selectionManager.setGroup('note', [blockSelection]); selectionManager.setGroup('note', [blockSelection]);
@@ -4,6 +4,7 @@ import type {
EmbedYoutubeStyles, EmbedYoutubeStyles,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { BlockSelection, SurfaceSelection } from '@blocksuite/block-std';
import { html, nothing } from 'lit'; import { html, nothing } from 'lit';
import { property, state } from 'lit/decorators.js'; import { property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
@@ -49,7 +50,7 @@ export class EmbedYoutubeBlockComponent extends EmbedBlockComponent<
private _selectBlock() { private _selectBlock() {
const selectionManager = this.host.selection; const selectionManager = this.host.selection;
const blockSelection = selectionManager.create('block', { const blockSelection = selectionManager.create(BlockSelection, {
blockId: this.blockId, blockId: this.blockId,
}); });
selectionManager.setGroup('note', [blockSelection]); selectionManager.setGroup('note', [blockSelection]);
@@ -96,7 +97,8 @@ export class EmbedYoutubeBlockComponent extends EmbedBlockComponent<
this.disposables.add( this.disposables.add(
this.std.selection.slots.changed.on(() => { this.std.selection.slots.changed.on(() => {
this._isSelected = this._isSelected =
!!this.selected?.is('block') || !!this.selected?.is('surface'); !!this.selected?.is(BlockSelection) ||
!!this.selected?.is(SurfaceSelection);
this._showOverlay = this._showOverlay =
this._isResizing || this._isDragging || !this._isSelected; this._isResizing || this._isDragging || !this._isSelected;
@@ -1,5 +1,10 @@
import { ImageSelection } from '@blocksuite/affine-shared/selection';
import type { BaseSelection, UIEventStateContext } from '@blocksuite/block-std'; import type { BaseSelection, UIEventStateContext } from '@blocksuite/block-std';
import { ShadowlessElement } from '@blocksuite/block-std'; import {
BlockSelection,
ShadowlessElement,
TextSelection,
} from '@blocksuite/block-std';
import { WithDisposable } from '@blocksuite/global/utils'; import { WithDisposable } from '@blocksuite/global/utils';
import { css, html, type PropertyValues } from 'lit'; import { css, html, type PropertyValues } from 'lit';
import { property, query, state } from 'lit/decorators.js'; import { property, query, state } from 'lit/decorators.js';
@@ -66,9 +71,9 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) {
selection.update(selList => selection.update(selList =>
selList selList
.filter<BaseSelection>(sel => !sel.is('image')) .filter<BaseSelection>(sel => !sel.is(ImageSelection))
.concat( .concat(
selection.create('text', { selection.create(TextSelection, {
from: { from: {
blockId, blockId,
index: 0, index: 0,
@@ -86,9 +91,11 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) {
selection.update(selList => { selection.update(selList => {
return selList.map(sel => { return selList.map(sel => {
const current = const current =
sel.is('image') && sel.blockId === this.block.blockId; sel.is(ImageSelection) && sel.blockId === this.block.blockId;
if (current) { if (current) {
return selection.create('block', { blockId: this.block.blockId }); return selection.create(BlockSelection, {
blockId: this.block.blockId,
});
} }
return sel; return sel;
}); });
@@ -119,7 +126,7 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) {
const std = this._host.std; const std = this._host.std;
// If the selection is not image selection, we should not handle it. // If the selection is not image selection, we should not handle it.
if (!std.selection.find('image')) { if (!std.selection.find(ImageSelection)) {
return false; return false;
} }
@@ -145,7 +152,7 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) {
// If the selection is not image selection, we should not handle it. // If the selection is not image selection, we should not handle it.
if (!std.selection.find('image')) { if (!std.selection.find(ImageSelection)) {
return false; return false;
} }
@@ -178,7 +185,7 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) {
this._disposables.add( this._disposables.add(
selection.slots.changed.on(selList => { selection.slots.changed.on(selList => {
this._isSelected = selList.some( this._isSelected = selList.some(
sel => sel.blockId === this.block.blockId && sel.is('image') sel => sel.blockId === this.block.blockId && sel.is(ImageSelection)
); );
}) })
); );
@@ -200,7 +207,9 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) {
selection.update(selList => { selection.update(selList => {
return selList return selList
.filter(sel => !['block', 'image', 'text'].includes(sel.type)) .filter(sel => !['block', 'image', 'text'].includes(sel.type))
.concat(selection.create('image', { blockId: this.block.blockId })); .concat(
selection.create(ImageSelection, { blockId: this.block.blockId })
);
}); });
return true; return true;
} }
@@ -213,7 +222,8 @@ export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) {
selection.update(selList => selection.update(selList =>
selList.filter( selList.filter(
sel => !(sel.is('image') && sel.blockId === this.block.blockId) sel =>
!(sel.is(ImageSelection) && sel.blockId === this.block.blockId)
) )
); );
}, },
@@ -1,6 +1,7 @@
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption'; import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
import { Peekable } from '@blocksuite/affine-components/peek'; import { Peekable } from '@blocksuite/affine-components/peek';
import type { ImageBlockModel } from '@blocksuite/affine-model'; import type { ImageBlockModel } from '@blocksuite/affine-model';
import { BlockSelection } from '@blocksuite/block-std';
import { IS_MOBILE } from '@blocksuite/global/env'; import { IS_MOBILE } from '@blocksuite/global/env';
import { html } from 'lit'; import { html } from 'lit';
import { property, query, state } from 'lit/decorators.js'; import { property, query, state } from 'lit/decorators.js';
@@ -51,7 +52,7 @@ export class ImageBlockComponent extends CaptionedBlockComponent<
event.stopPropagation(); event.stopPropagation();
const selectionManager = this.host.selection; const selectionManager = this.host.selection;
const blockSelection = selectionManager.create('block', { const blockSelection = selectionManager.create(BlockSelection, {
blockId: this.blockId, blockId: this.blockId,
}); });
selectionManager.setGroup('note', [blockSelection]); selectionManager.setGroup('note', [blockSelection]);
@@ -1,6 +1,7 @@
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption'; import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
import { createLitPortal } from '@blocksuite/affine-components/portal'; import { createLitPortal } from '@blocksuite/affine-components/portal';
import type { LatexBlockModel } from '@blocksuite/affine-model'; import type { LatexBlockModel } from '@blocksuite/affine-model';
import { BlockSelection } from '@blocksuite/block-std';
import type { Placement } from '@floating-ui/dom'; import type { Placement } from '@floating-ui/dom';
import { effect } from '@preact/signals-core'; import { effect } from '@preact/signals-core';
import katex from 'katex'; import katex from 'katex';
@@ -19,7 +20,7 @@ export class LatexBlockComponent extends CaptionedBlockComponent<LatexBlockModel
} }
get isBlockSelected() { get isBlockSelected() {
const blockSelection = this.selection.filter('block'); const blockSelection = this.selection.filter(BlockSelection);
return blockSelection.some( return blockSelection.some(
selection => selection.blockId === this.model.id selection => selection.blockId === this.model.id
); );
@@ -1,6 +1,6 @@
import type { IndentContext } from '@blocksuite/affine-shared/types'; import type { IndentContext } from '@blocksuite/affine-shared/types';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
import { correctNumberedListsOrderToPrev } from './utils.js'; import { correctNumberedListsOrderToPrev } from './utils.js';
@@ -13,7 +13,7 @@ export const canDedentListCommand: Command<
const { std } = ctx; const { std } = ctx;
const { selection, doc } = std; const { selection, doc } = std;
if (!blockId) { if (!blockId) {
const text = selection.find('text'); const text = selection.find(TextSelection);
/** /**
* Do nothing if the selection: * Do nothing if the selection:
* - is not a text selection * - is not a text selection
@@ -148,7 +148,7 @@ export const dedentListCommand: Command<'indentContext'> = (ctx, next) => {
doc.moveBlocks([model], grandParent, parent, false); doc.moveBlocks([model], grandParent, parent, false);
correctNumberedListsOrderToPrev(doc, model); correctNumberedListsOrderToPrev(doc, model);
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if (textSelection) { if (textSelection) {
host.updateComplete host.updateComplete
.then(() => { .then(() => {
@@ -3,7 +3,7 @@ import {
getNearestHeadingBefore, getNearestHeadingBefore,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
import { correctNumberedListsOrderToPrev } from './utils.js'; import { correctNumberedListsOrderToPrev } from './utils.js';
@@ -16,7 +16,7 @@ export const canIndentListCommand: Command<
const { std } = ctx; const { std } = ctx;
const { selection, doc } = std; const { selection, doc } = std;
if (!blockId) { if (!blockId) {
const text = selection.find('text'); const text = selection.find(TextSelection);
/** /**
* Do nothing if the selection: * Do nothing if the selection:
* - is not a text selection * - is not a text selection
@@ -133,7 +133,7 @@ export const indentListCommand: Command<'indentContext', never> = (
}); });
} }
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if (textSelection) { if (textSelection) {
host.updateComplete host.updateComplete
.then(() => { .then(() => {
@@ -15,7 +15,11 @@ import {
import { DocModeProvider } from '@blocksuite/affine-shared/services'; import { DocModeProvider } from '@blocksuite/affine-shared/services';
import { getViewportElement } from '@blocksuite/affine-shared/utils'; import { getViewportElement } from '@blocksuite/affine-shared/utils';
import type { BaseSelection, BlockComponent } from '@blocksuite/block-std'; import type { BaseSelection, BlockComponent } from '@blocksuite/block-std';
import { getInlineRangeProvider } from '@blocksuite/block-std'; import {
BlockSelection,
getInlineRangeProvider,
TextSelection,
} from '@blocksuite/block-std';
import type { InlineRangeProvider } from '@blocksuite/inline'; import type { InlineRangeProvider } from '@blocksuite/inline';
import { effect } from '@preact/signals-core'; import { effect } from '@preact/signals-core';
import { html, nothing, type TemplateResult } from 'lit'; import { html, nothing, type TemplateResult } from 'lit';
@@ -95,8 +99,10 @@ export class ListBlockComponent extends CaptionedBlockComponent<ListBlockModel>
const selection = this.host.selection; const selection = this.host.selection;
selection.update(selList => { selection.update(selList => {
return selList return selList
.filter<BaseSelection>(sel => !sel.is('text') && !sel.is('block')) .filter<BaseSelection>(
.concat(selection.create('block', { blockId: this.blockId })); sel => !sel.is(TextSelection) && !sel.is(BlockSelection)
)
.concat(selection.create(BlockSelection, { blockId: this.blockId }));
}); });
} }
@@ -3,7 +3,7 @@ import {
textKeymap, textKeymap,
} from '@blocksuite/affine-components/rich-text'; } from '@blocksuite/affine-components/rich-text';
import { ListBlockSchema } from '@blocksuite/affine-model'; import { ListBlockSchema } from '@blocksuite/affine-model';
import { KeymapExtension } from '@blocksuite/block-std'; import { KeymapExtension, TextSelection } from '@blocksuite/block-std';
import { IS_MAC } from '@blocksuite/global/env'; import { IS_MAC } from '@blocksuite/global/env';
import { forwardDelete } from './utils/forward-delete.js'; import { forwardDelete } from './utils/forward-delete.js';
@@ -12,7 +12,7 @@ export const ListKeymapExtension = KeymapExtension(
std => { std => {
return { return {
Enter: ctx => { Enter: ctx => {
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return false; if (!text) return false;
ctx.get('keyboardState').raw.preventDefault(); ctx.get('keyboardState').raw.preventDefault();
@@ -23,7 +23,7 @@ export const ListKeymapExtension = KeymapExtension(
return true; return true;
}, },
'Mod-Enter': ctx => { 'Mod-Enter': ctx => {
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return false; if (!text) return false;
ctx.get('keyboardState').raw.preventDefault(); ctx.get('keyboardState').raw.preventDefault();
@@ -40,7 +40,7 @@ export const ListKeymapExtension = KeymapExtension(
if (selectedModels?.length !== 1) { if (selectedModels?.length !== 1) {
return false; return false;
} }
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return false; if (!text) return false;
ctx.get('keyboardState').raw.preventDefault(); ctx.get('keyboardState').raw.preventDefault();
@@ -61,7 +61,7 @@ export const ListKeymapExtension = KeymapExtension(
if (selectedModels?.length !== 1) { if (selectedModels?.length !== 1) {
return; return;
} }
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return false; if (!text) return false;
ctx.get('keyboardState').raw.preventDefault(); ctx.get('keyboardState').raw.preventDefault();
@@ -76,7 +76,7 @@ export const ListKeymapExtension = KeymapExtension(
return true; return true;
}, },
Backspace: ctx => { Backspace: ctx => {
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return false; if (!text) return false;
const isCollapsed = text.isCollapsed(); const isCollapsed = text.isCollapsed();
const isStart = isCollapsed && text.from.index === 0; const isStart = isCollapsed && text.from.index === 0;
@@ -2,7 +2,7 @@ import {
getNextContentBlock, getNextContentBlock,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope } from '@blocksuite/block-std'; import { type BlockStdScope, TextSelection } from '@blocksuite/block-std';
import type { Text } from '@blocksuite/store'; import type { Text } from '@blocksuite/store';
// When deleting at line end of a list block, // When deleting at line end of a list block,
@@ -20,7 +20,7 @@ import type { Text } from '@blocksuite/store';
- Line9 - Line9
*/ */
export function forwardDelete(std: BlockStdScope): true | undefined { export function forwardDelete(std: BlockStdScope): true | undefined {
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const isCollapsed = text.isCollapsed(); const isCollapsed = text.isCollapsed();
const doc = std.doc; const doc = std.doc;
@@ -8,7 +8,11 @@ import {
mergeToCodeModel, mergeToCodeModel,
transformModel, transformModel,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import {
BlockSelection,
type Command,
TextSelection,
} from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
type UpdateBlockConfig = { type UpdateBlockConfig = {
@@ -108,11 +112,11 @@ export const updateBlockType: Command<
onModelTextUpdated(host, model) onModelTextUpdated(host, model)
); );
const selectionManager = host.selection; const selectionManager = host.selection;
const textSelection = selectionManager.find('text'); const textSelection = selectionManager.find(TextSelection);
if (!textSelection) { if (!textSelection) {
return false; return false;
} }
const newTextSelection = selectionManager.create('text', { const newTextSelection = selectionManager.create(TextSelection, {
from: { from: {
blockId: firstNewModel.id, blockId: firstNewModel.id,
index: textSelection.from.index, index: textSelection.from.index,
@@ -143,13 +147,13 @@ export const updateBlockType: Command<
const selectionManager = host.selection; const selectionManager = host.selection;
const blockSelections = selectionManager.filter('block'); const blockSelections = selectionManager.filter(BlockSelection);
if (blockSelections.length === 0) { if (blockSelections.length === 0) {
return false; return false;
} }
requestAnimationFrame(() => { requestAnimationFrame(() => {
const selections = updatedBlocks.map(model => { const selections = updatedBlocks.map(model => {
return selectionManager.create('block', { return selectionManager.create(BlockSelection, {
blockId: model.id, blockId: model.id,
}); });
}); });
@@ -1,5 +1,5 @@
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
export const dedentBlocksToRoot: Command< export const dedentBlocksToRoot: Command<
never, never,
@@ -13,7 +13,7 @@ export const dedentBlocksToRoot: Command<
const { std, stopCapture = true } = ctx; const { std, stopCapture = true } = ctx;
const { doc } = std; const { doc } = std;
if (!blockIds || !blockIds.length) { if (!blockIds || !blockIds.length) {
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (text) { if (text) {
// If the text selection is not at the beginning of the block, use default behavior // If the text selection is not at the beginning of the block, use default behavior
if (text.from.index !== 0) return; if (text.from.index !== 0) return;
@@ -2,7 +2,7 @@ import {
calculateCollapsedSiblings, calculateCollapsedSiblings,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
export const dedentBlocks: Command< export const dedentBlocks: Command<
never, never,
@@ -75,7 +75,7 @@ export const dedentBlocks: Command<
std.command.exec('dedentBlock', { blockId: id, stopCapture: false }); std.command.exec('dedentBlock', { blockId: id, stopCapture: false });
}); });
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if (textSelection) { if (textSelection) {
host.updateComplete host.updateComplete
.then(() => { .then(() => {
@@ -1,4 +1,4 @@
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
export const focusBlockEnd: Command<'focusBlock'> = (ctx, next) => { export const focusBlockEnd: Command<'focusBlock'> = (ctx, next) => {
const { focusBlock, std } = ctx; const { focusBlock, std } = ctx;
@@ -7,7 +7,7 @@ export const focusBlockEnd: Command<'focusBlock'> = (ctx, next) => {
const { selection } = std; const { selection } = std;
selection.setGroup('note', [ selection.setGroup('note', [
selection.create('text', { selection.create(TextSelection, {
from: { from: {
blockId: focusBlock.blockId, blockId: focusBlock.blockId,
index: focusBlock.model.text.length, index: focusBlock.model.text.length,
@@ -1,4 +1,4 @@
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
export const focusBlockStart: Command<'focusBlock'> = (ctx, next) => { export const focusBlockStart: Command<'focusBlock'> = (ctx, next) => {
const { focusBlock, std } = ctx; const { focusBlock, std } = ctx;
@@ -7,7 +7,7 @@ export const focusBlockStart: Command<'focusBlock'> = (ctx, next) => {
const { selection } = std; const { selection } = std;
selection.setGroup('note', [ selection.setGroup('note', [
selection.create('text', { selection.create(TextSelection, {
from: { blockId: focusBlock.blockId, index: 0, length: 0 }, from: { blockId: focusBlock.blockId, index: 0, length: 0 },
to: null, to: null,
}), }),
@@ -3,7 +3,7 @@ import {
getNearestHeadingBefore, getNearestHeadingBefore,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
export const indentBlocks: Command< export const indentBlocks: Command<
never, never,
@@ -117,7 +117,7 @@ export const indentBlocks: Command<
} }
} }
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if (textSelection) { if (textSelection) {
host.updateComplete host.updateComplete
.then(() => { .then(() => {
@@ -1,4 +1,4 @@
import type { Command } from '@blocksuite/block-std'; import { BlockSelection, type Command } from '@blocksuite/block-std';
export const selectBlock: Command<'focusBlock'> = (ctx, next) => { export const selectBlock: Command<'focusBlock'> = (ctx, next) => {
const { focusBlock, std } = ctx; const { focusBlock, std } = ctx;
@@ -9,7 +9,7 @@ export const selectBlock: Command<'focusBlock'> = (ctx, next) => {
const { selection } = std; const { selection } = std;
selection.setGroup('note', [ selection.setGroup('note', [
selection.create('block', { blockId: focusBlock.blockId }), selection.create(BlockSelection, { blockId: focusBlock.blockId }),
]); ]);
return next(); return next();
@@ -1,4 +1,4 @@
import type { Command } from '@blocksuite/block-std'; import { BlockSelection, type Command } from '@blocksuite/block-std';
export const selectBlocksBetween: Command< export const selectBlocksBetween: Command<
'focusBlock' | 'anchorBlock', 'focusBlock' | 'anchorBlock',
@@ -14,7 +14,7 @@ export const selectBlocksBetween: Command<
// In same block // In same block
if (anchorBlock.blockId === focusBlock.blockId) { if (anchorBlock.blockId === focusBlock.blockId) {
const blockId = focusBlock.blockId; const blockId = focusBlock.blockId;
selection.setGroup('note', [selection.create('block', { blockId })]); selection.setGroup('note', [selection.create(BlockSelection, { blockId })]);
return next(); return next();
} }
@@ -23,11 +23,11 @@ export const selectBlocksBetween: Command<
if (selections.every(sel => sel.blockId !== focusBlock.blockId)) { if (selections.every(sel => sel.blockId !== focusBlock.blockId)) {
if (tail) { if (tail) {
selections.push( selections.push(
selection.create('block', { blockId: focusBlock.blockId }) selection.create(BlockSelection, { blockId: focusBlock.blockId })
); );
} else { } else {
selections.unshift( selections.unshift(
selection.create('block', { blockId: focusBlock.blockId }) selection.create(BlockSelection, { blockId: focusBlock.blockId })
); );
} }
} }
@@ -1,17 +1,21 @@
import type { BlockSelection, BlockStdScope } from '@blocksuite/block-std'; import {
BlockSelection,
type BlockStdScope,
TextSelection,
} from '@blocksuite/block-std';
const getSelection = (std: BlockStdScope) => std.selection; const getSelection = (std: BlockStdScope) => std.selection;
function getBlockSelectionBySide(std: BlockStdScope, tail: boolean) { function getBlockSelectionBySide(std: BlockStdScope, tail: boolean) {
const selection = getSelection(std); const selection = getSelection(std);
const selections = selection.filter('block'); const selections = selection.filter(BlockSelection);
const sel = selections.at(tail ? -1 : 0) as BlockSelection | undefined; const sel = selections.at(tail ? -1 : 0) as BlockSelection | undefined;
return sel ?? null; return sel ?? null;
} }
function getTextSelection(std: BlockStdScope) { function getTextSelection(std: BlockStdScope) {
const selection = getSelection(std); const selection = getSelection(std);
return selection.find('text'); return selection.find(TextSelection);
} }
const pathToBlock = (std: BlockStdScope, blockId: string) => const pathToBlock = (std: BlockStdScope, blockId: string) =>
@@ -15,7 +15,11 @@ import {
stopPropagation, stopPropagation,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { BlockComponent, EditorHost } from '@blocksuite/block-std'; import type { BlockComponent, EditorHost } from '@blocksuite/block-std';
import { ShadowlessElement, toGfxBlockComponent } from '@blocksuite/block-std'; import {
ShadowlessElement,
TextSelection,
toGfxBlockComponent,
} from '@blocksuite/block-std';
import { import {
almostEqual, almostEqual,
Bound, Bound,
@@ -305,7 +309,7 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
this.updateComplete this.updateComplete
.then(() => { .then(() => {
this.std.selection.setGroup('note', [ this.std.selection.setGroup('note', [
this.std.selection.create('text', { this.std.selection.create(TextSelection, {
from: { from: {
blockId: pId, blockId: pId,
index: 0, index: 0,
@@ -4,9 +4,10 @@ import { matchFlavours } from '@blocksuite/affine-shared/utils';
import { import {
type BaseSelection, type BaseSelection,
type BlockComponent, type BlockComponent,
type BlockSelection, BlockSelection,
BlockService, BlockService,
type BlockStdScope, type BlockStdScope,
TextSelection,
type UIEventHandler, type UIEventHandler,
type UIEventStateContext, type UIEventStateContext,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
@@ -95,7 +96,7 @@ export class NoteBlockService extends BlockService {
const [codeModel] = newModels; const [codeModel] = newModels;
onModelElementUpdated(ctx.std, codeModel, codeElement => { onModelElementUpdated(ctx.std, codeModel, codeElement => {
this._std.selection.setGroup('note', [ this._std.selection.setGroup('note', [
this._std.selection.create('text', { this._std.selection.create(TextSelection, {
from: { from: {
blockId: codeElement.blockId, blockId: codeElement.blockId,
index: 0, index: 0,
@@ -439,7 +440,7 @@ export class NoteBlockService extends BlockService {
const blockId = doc.addBlock('affine:paragraph', {}, parent, index + 1); const blockId = doc.addBlock('affine:paragraph', {}, parent, index + 1);
const sel = selection.create('text', { const sel = selection.create(TextSelection, {
from: { from: {
blockId, blockId,
index: 0, index: 0,
@@ -469,7 +470,7 @@ export class NoteBlockService extends BlockService {
} }
ctx.std.selection.update(selList => { ctx.std.selection.update(selList => {
return selList.filter(sel => !sel.is('block')); return selList.filter(sel => !sel.is(BlockSelection));
}); });
return next(); return next();
@@ -481,7 +482,7 @@ export class NoteBlockService extends BlockService {
private readonly _onSelectAll: UIEventHandler = ctx => { private readonly _onSelectAll: UIEventHandler = ctx => {
const selection = this._std.selection; const selection = this._std.selection;
const block = selection.find('block'); const block = selection.find(BlockSelection);
if (!block) { if (!block) {
return; return;
} }
@@ -492,13 +493,13 @@ export class NoteBlockService extends BlockService {
ctx.get('defaultState').event.preventDefault(); ctx.get('defaultState').event.preventDefault();
const children = note.children; const children = note.children;
const blocks: BlockSelection[] = children.map(child => { const blocks: BlockSelection[] = children.map(child => {
return selection.create('block', { return selection.create(BlockSelection, {
blockId: child.id, blockId: child.id,
}); });
}); });
selection.update(selList => { selection.update(selList => {
return selList return selList
.filter<BaseSelection>(sel => !sel.is('block')) .filter<BaseSelection>(sel => !sel.is(BlockSelection))
.concat(blocks); .concat(blocks);
}); });
}; };
@@ -1,5 +1,5 @@
import { focusTextModel } from '@blocksuite/affine-components/rich-text'; import { focusTextModel } from '@blocksuite/affine-components/rich-text';
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
/** /**
* Add a paragraph next to the current block. * Add a paragraph next to the current block.
@@ -17,7 +17,7 @@ export const addParagraphCommand: Command<
let blockId = ctx.blockId; let blockId = ctx.blockId;
if (!blockId) { if (!blockId) {
const text = selection.find('text'); const text = selection.find(TextSelection);
blockId = text?.blockId; blockId = text?.blockId;
} }
if (!blockId) return; if (!blockId) return;
@@ -3,7 +3,7 @@ import {
calculateCollapsedSiblings, calculateCollapsedSiblings,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
export const canDedentParagraphCommand: Command< export const canDedentParagraphCommand: Command<
never, never,
@@ -13,7 +13,7 @@ export const canDedentParagraphCommand: Command<
let { blockId, inlineIndex } = ctx; let { blockId, inlineIndex } = ctx;
const { std } = ctx; const { std } = ctx;
const { selection, doc } = std; const { selection, doc } = std;
const text = selection.find('text'); const text = selection.find(TextSelection);
if (!blockId) { if (!blockId) {
/** /**
@@ -97,7 +97,7 @@ export const dedentParagraphCommand: Command<'indentContext'> = (ctx, next) => {
doc.moveBlocks([model], grandParent, parent, false); doc.moveBlocks([model], grandParent, parent, false);
} }
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if (textSelection) { if (textSelection) {
host.updateComplete host.updateComplete
.then(() => { .then(() => {
@@ -5,7 +5,7 @@ import {
getNearestHeadingBefore, getNearestHeadingBefore,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
export const canIndentParagraphCommand: Command< export const canIndentParagraphCommand: Command<
never, never,
@@ -18,7 +18,7 @@ export const canIndentParagraphCommand: Command<
const { schema } = doc; const { schema } = doc;
if (!blockId) { if (!blockId) {
const text = selection.find('text'); const text = selection.find(TextSelection);
/** /**
* Do nothing if the selection: * Do nothing if the selection:
* - is not a text selection * - is not a text selection
@@ -140,7 +140,7 @@ export const indentParagraphCommand: Command<'indentContext'> = (ctx, next) => {
} as Partial<ListBlockModel>); } as Partial<ListBlockModel>);
} }
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if (textSelection) { if (textSelection) {
host.updateComplete host.updateComplete
.then(() => { .then(() => {
@@ -3,7 +3,7 @@ import {
getInlineEditorByModel, getInlineEditorByModel,
} from '@blocksuite/affine-components/rich-text'; } from '@blocksuite/affine-components/rich-text';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
export const splitParagraphCommand: Command< export const splitParagraphCommand: Command<
never, never,
@@ -16,7 +16,7 @@ export const splitParagraphCommand: Command<
const { doc, host, selection } = std; const { doc, host, selection } = std;
let blockId = ctx.blockId; let blockId = ctx.blockId;
if (!blockId) { if (!blockId) {
const text = selection.find('text'); const text = selection.find(TextSelection);
blockId = text?.blockId; blockId = text?.blockId;
} }
if (!blockId) return; if (!blockId) return;
@@ -16,7 +16,7 @@ import {
getViewportElement, getViewportElement,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { BlockComponent } from '@blocksuite/block-std'; import type { BlockComponent } from '@blocksuite/block-std';
import { getInlineRangeProvider } from '@blocksuite/block-std'; import { getInlineRangeProvider, TextSelection } from '@blocksuite/block-std';
import type { InlineRangeProvider } from '@blocksuite/inline'; import type { InlineRangeProvider } from '@blocksuite/inline';
import { effect, signal } from '@preact/signals-core'; import { effect, signal } from '@preact/signals-core';
import { html, nothing, type TemplateResult } from 'lit'; import { html, nothing, type TemplateResult } from 'lit';
@@ -119,7 +119,7 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<
this._displayPlaceholder.value = false; this._displayPlaceholder.value = false;
return; return;
} }
const textSelection = this.host.selection.find('text'); const textSelection = this.host.selection.find(TextSelection);
const isCollapsed = textSelection?.isCollapsed() ?? false; const isCollapsed = textSelection?.isCollapsed() ?? false;
if (!this.selected || !isCollapsed) { if (!this.selected || !isCollapsed) {
this._displayPlaceholder.value = false; this._displayPlaceholder.value = false;
@@ -159,7 +159,7 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<
// reset text selection when selected block is collapsed // reset text selection when selected block is collapsed
if (this.model.type.startsWith('h') && collapsed) { if (this.model.type.startsWith('h') && collapsed) {
const collapsedSiblings = this.collapsedSiblings; const collapsedSiblings = this.collapsedSiblings;
const textSelection = this.host.selection.find('text'); const textSelection = this.host.selection.find(TextSelection);
if ( if (
textSelection && textSelection &&
@@ -9,7 +9,7 @@ import {
calculateCollapsedSiblings, calculateCollapsedSiblings,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import { KeymapExtension } from '@blocksuite/block-std'; import { KeymapExtension, TextSelection } from '@blocksuite/block-std';
import { IS_MAC } from '@blocksuite/global/env'; import { IS_MAC } from '@blocksuite/global/env';
import { forwardDelete } from './utils/forward-delete.js'; import { forwardDelete } from './utils/forward-delete.js';
@@ -19,7 +19,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
std => { std => {
return { return {
Backspace: ctx => { Backspace: ctx => {
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const isCollapsed = text.isCollapsed(); const isCollapsed = text.isCollapsed();
const isStart = isCollapsed && text.from.index === 0; const isStart = isCollapsed && text.from.index === 0;
@@ -52,7 +52,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
}, },
'Mod-Enter': ctx => { 'Mod-Enter': ctx => {
const { doc } = std; const { doc } = std;
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const model = doc.getBlock(text.from.blockId)?.model; const model = doc.getBlock(text.from.blockId)?.model;
if (!model || !matchFlavours(model, ['affine:paragraph'])) return; if (!model || !matchFlavours(model, ['affine:paragraph'])) return;
@@ -79,7 +79,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
}, },
Enter: ctx => { Enter: ctx => {
const { doc } = std; const { doc } = std;
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const model = doc.getBlock(text.from.blockId)?.model; const model = doc.getBlock(text.from.blockId)?.model;
if (!model || !matchFlavours(model, ['affine:paragraph'])) return; if (!model || !matchFlavours(model, ['affine:paragraph'])) return;
@@ -3,11 +3,15 @@ import {
getNextContentBlock, getNextContentBlock,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope } from '@blocksuite/block-std'; import {
BlockSelection,
type BlockStdScope,
TextSelection,
} from '@blocksuite/block-std';
export function forwardDelete(std: BlockStdScope) { export function forwardDelete(std: BlockStdScope) {
const { doc, host } = std; const { doc, host } = std;
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const isCollapsed = text.isCollapsed(); const isCollapsed = text.isCollapsed();
const model = doc.getBlock(text.from.blockId)?.model; const model = doc.getBlock(text.from.blockId)?.model;
@@ -30,7 +34,7 @@ export function forwardDelete(std: BlockStdScope) {
if (matchFlavours(nextSibling, ignoreForwardDeleteFlavourList)) { if (matchFlavours(nextSibling, ignoreForwardDeleteFlavourList)) {
std.selection.setGroup('note', [ std.selection.setGroup('note', [
std.selection.create('block', { blockId: nextSibling.id }), std.selection.create(BlockSelection, { blockId: nextSibling.id }),
]); ]);
return true; return true;
} }
@@ -61,7 +65,7 @@ export function forwardDelete(std: BlockStdScope) {
if (nextBlock) { if (nextBlock) {
std.selection.setGroup('note', [ std.selection.setGroup('note', [
std.selection.create('block', { blockId: nextBlock.id }), std.selection.create(BlockSelection, { blockId: nextBlock.id }),
]); ]);
} }
return true; return true;
@@ -11,7 +11,7 @@ import {
getPrevContentBlock, getPrevContentBlock,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { EditorHost } from '@blocksuite/block-std'; import { BlockSelection, type EditorHost } from '@blocksuite/block-std';
import type { BlockModel, Text } from '@blocksuite/store'; import type { BlockModel, Text } from '@blocksuite/store';
/** /**
@@ -72,7 +72,7 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) {
...EMBED_BLOCK_FLAVOUR_LIST, ...EMBED_BLOCK_FLAVOUR_LIST,
]) ])
) { ) {
const selection = editorHost.selection.create('block', { const selection = editorHost.selection.create(BlockSelection, {
blockId: prevBlock.id, blockId: prevBlock.id,
}); });
editorHost.selection.setGroup('note', [selection]); editorHost.selection.setGroup('note', [selection]);
@@ -28,10 +28,12 @@ import {
import { import {
type BaseSelection, type BaseSelection,
BlockComponent, BlockComponent,
BlockSelection,
BlockServiceWatcher, BlockServiceWatcher,
BlockStdScope, BlockStdScope,
type EditorHost, type EditorHost,
LifeCycleWatcher, LifeCycleWatcher,
TextSelection,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { import {
GfxBlockElementModel, GfxBlockElementModel,
@@ -267,7 +269,7 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
private _focusBlock() { private _focusBlock() {
this.selection.update(() => { this.selection.update(() => {
return [this.selection.create('block', { blockId: this.blockId })]; return [this.selection.create(BlockSelection, { blockId: this.blockId })];
}); });
} }
@@ -287,9 +289,9 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
requestConnectedFrame(() => { requestConnectedFrame(() => {
selection.update(selList => { selection.update(selList => {
return selList return selList
.filter<BaseSelection>(sel => !sel.is('block')) .filter<BaseSelection>(sel => !sel.is(BlockSelection))
.concat( .concat(
selection.create('text', { selection.create(TextSelection, {
from: { from: {
blockId: model.id, blockId: model.id,
index: 0, index: 0,
@@ -415,7 +417,7 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
this._disposables.add( this._disposables.add(
selection.slots.changed.on(selList => { selection.slots.changed.on(selList => {
this._focused = selList.some( this._focused = selList.some(
sel => sel.blockId === this.blockId && sel.is('block') sel => sel.blockId === this.blockId && sel.is(BlockSelection)
); );
}) })
); );
@@ -1,4 +1,7 @@
import type { BlockComponent } from '@blocksuite/block-std'; import {
type BlockComponent,
BlockSelection as StdBlockSelection,
} from '@blocksuite/block-std';
import { SignalWatcher } from '@blocksuite/global/utils'; import { SignalWatcher } from '@blocksuite/global/utils';
import { css, LitElement, type PropertyValues } from 'lit'; import { css, LitElement, type PropertyValues } from 'lit';
import { property } from 'lit/decorators.js'; import { property } from 'lit/decorators.js';
@@ -55,7 +58,9 @@ export class BlockSelection extends SignalWatcher(LitElement) {
protected override updated(_changedProperties: PropertyValues): void { protected override updated(_changedProperties: PropertyValues): void {
super.updated(_changedProperties); super.updated(_changedProperties);
if (this.block) { if (this.block) {
this.style.display = this.block.selected?.is('block') ? 'block' : 'none'; this.style.display = this.block.selected?.is(StdBlockSelection)
? 'block'
: 'none';
} }
} }
@@ -3,7 +3,11 @@ import {
getCurrentNativeRange, getCurrentNativeRange,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope, EditorHost } from '@blocksuite/block-std'; import {
type BlockStdScope,
type EditorHost,
TextSelection,
} from '@blocksuite/block-std';
import type { InlineEditor, InlineRange } from '@blocksuite/inline'; import type { InlineEditor, InlineRange } from '@blocksuite/inline';
import { BlockModel } from '@blocksuite/store'; import { BlockModel } from '@blocksuite/store';
@@ -81,7 +85,7 @@ export function selectTextModel(
) { ) {
const { selection } = std; const { selection } = std;
selection.setGroup('note', [ selection.setGroup('note', [
selection.create('text', { selection.create(TextSelection, {
from: { blockId: id, index, length }, from: { blockId: id, index, length },
to: null, to: null,
}), }),
@@ -1,5 +1,5 @@
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { Command, TextSelection } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
import type { Text } from '@blocksuite/store'; import type { Text } from '@blocksuite/store';
export const deleteTextCommand: Command< export const deleteTextCommand: Command<
@@ -36,7 +36,7 @@ export const deleteTextCommand: Command<
if (!to) { if (!to) {
fromText.delete(from.index, from.length); fromText.delete(from.index, from.length);
ctx.std.selection.setGroup('note', [ ctx.std.selection.setGroup('note', [
ctx.std.selection.create('text', { ctx.std.selection.create(TextSelection, {
from: { from: {
blockId: from.blockId, blockId: from.blockId,
index: from.index, index: from.index,
@@ -69,7 +69,7 @@ export const deleteTextCommand: Command<
}); });
ctx.std.selection.setGroup('note', [ ctx.std.selection.setGroup('note', [
ctx.std.selection.create('text', { ctx.std.selection.create(TextSelection, {
from: { from: {
blockId: from.blockId, blockId: from.blockId,
index: from.index, index: from.index,
@@ -2,7 +2,12 @@ import type { ReferenceInfo } from '@blocksuite/affine-model';
import { ParseDocUrlProvider } from '@blocksuite/affine-shared/services'; import { ParseDocUrlProvider } from '@blocksuite/affine-shared/services';
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types'; import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
import type { BlockComponent } from '@blocksuite/block-std'; import type { BlockComponent } from '@blocksuite/block-std';
import { BLOCK_ID_ATTR, ShadowlessElement } from '@blocksuite/block-std'; import {
BLOCK_ID_ATTR,
BlockSelection,
ShadowlessElement,
TextSelection,
} from '@blocksuite/block-std';
import { import {
type DeltaInsert, type DeltaInsert,
INLINE_ROOT_ATTR, INLINE_ROOT_ATTR,
@@ -69,12 +74,12 @@ export class AffineLink extends ShadowlessElement {
} }
const selection = this.std?.selection; const selection = this.std?.selection;
const textSelection = selection?.find('text'); const textSelection = selection?.find(TextSelection);
if (!!textSelection && !textSelection.isCollapsed()) { if (!!textSelection && !textSelection.isCollapsed()) {
return null; return null;
} }
const blockSelections = selection?.filter('block'); const blockSelections = selection?.filter(BlockSelection);
if (blockSelections?.length) { if (blockSelections?.length) {
return null; return null;
} }
@@ -15,6 +15,7 @@ import {
BLOCK_ID_ATTR, BLOCK_ID_ATTR,
type BlockComponent, type BlockComponent,
type BlockStdScope, type BlockStdScope,
TextSelection,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { WithDisposable } from '@blocksuite/global/utils'; import { WithDisposable } from '@blocksuite/global/utils';
import type { InlineRange } from '@blocksuite/inline/types'; import type { InlineRange } from '@blocksuite/inline/types';
@@ -438,7 +439,7 @@ export class LinkPopup extends WithDisposable(LitElement) {
reference: null, reference: null,
}); });
this.inlineEditor.setInlineRange(this.targetInlineRange); this.inlineEditor.setInlineRange(this.targetInlineRange);
const textSelection = this.host?.selection.find('text'); const textSelection = this.host?.selection.find(TextSelection);
if (!textSelection) return; if (!textSelection) return;
this.std?.range.syncTextSelectionToRange(textSelection); this.std?.range.syncTextSelectionToRange(textSelection);
@@ -452,7 +453,7 @@ export class LinkPopup extends WithDisposable(LitElement) {
index: this.targetInlineRange.index, index: this.targetInlineRange.index,
length: text.length, length: text.length,
}); });
const textSelection = this.host?.selection.find('text'); const textSelection = this.host?.selection.find(TextSelection);
if (!textSelection) return; if (!textSelection) return;
this.std?.range.syncTextSelectionToRange(textSelection); this.std?.range.syncTextSelectionToRange(textSelection);
@@ -12,7 +12,9 @@ import {
import { import {
BLOCK_ID_ATTR, BLOCK_ID_ATTR,
type BlockComponent, type BlockComponent,
BlockSelection,
ShadowlessElement, ShadowlessElement,
TextSelection,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { WithDisposable } from '@blocksuite/global/utils'; import { WithDisposable } from '@blocksuite/global/utils';
@@ -108,12 +110,12 @@ export class AffineReference extends WithDisposable(ShadowlessElement) {
if (!selection) { if (!selection) {
return null; return null;
} }
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if (!!textSelection && !textSelection.isCollapsed()) { if (!!textSelection && !textSelection.isCollapsed()) {
return null; return null;
} }
const blockSelections = selection.filter('block'); const blockSelections = selection.filter(BlockSelection);
if (blockSelections.length) { if (blockSelections.length) {
return null; return null;
} }
@@ -1,4 +1,9 @@
import type { BlockStdScope, UIEventHandler } from '@blocksuite/block-std'; import {
BlockSelection,
type BlockStdScope,
TextSelection,
type UIEventHandler,
} from '@blocksuite/block-std';
import { import {
focusTextModel, focusTextModel,
@@ -11,21 +16,21 @@ export const textCommonKeymap = (
): Record<string, UIEventHandler> => { ): Record<string, UIEventHandler> => {
return { return {
ArrowUp: () => { ArrowUp: () => {
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const inline = getInlineEditorByModel(std.host, text.from.blockId); const inline = getInlineEditorByModel(std.host, text.from.blockId);
if (!inline) return; if (!inline) return;
return !inline.isFirstLine(inline.getInlineRange()); return !inline.isFirstLine(inline.getInlineRange());
}, },
ArrowDown: () => { ArrowDown: () => {
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const inline = getInlineEditorByModel(std.host, text.from.blockId); const inline = getInlineEditorByModel(std.host, text.from.blockId);
if (!inline) return; if (!inline) return;
return !inline.isLastLine(inline.getInlineRange()); return !inline.isLastLine(inline.getInlineRange());
}, },
Escape: ctx => { Escape: ctx => {
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
selectBlock(std, text.from.blockId); selectBlock(std, text.from.blockId);
@@ -33,7 +38,7 @@ export const textCommonKeymap = (
return true; return true;
}, },
'Mod-a': ctx => { 'Mod-a': ctx => {
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) return; if (!text) return;
const model = std.doc.getBlock(text.from.blockId)?.model; const model = std.doc.getBlock(text.from.blockId)?.model;
@@ -53,7 +58,7 @@ export const textCommonKeymap = (
return true; return true;
}, },
Enter: ctx => { Enter: ctx => {
const blocks = std.selection.filter('block'); const blocks = std.selection.filter(BlockSelection);
const blockId = blocks.at(-1)?.blockId; const blockId = blocks.at(-1)?.blockId;
if (!blockId) return; if (!blockId) return;
@@ -68,5 +73,7 @@ export const textCommonKeymap = (
}; };
function selectBlock(std: BlockStdScope, blockId: string) { function selectBlock(std: BlockStdScope, blockId: string) {
std.selection.setGroup('note', [std.selection.create('block', { blockId })]); std.selection.setGroup('note', [
std.selection.create(BlockSelection, { blockId }),
]);
} }
@@ -3,7 +3,11 @@ import {
createDefaultDoc, createDefaultDoc,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope, UIEventHandler } from '@blocksuite/block-std'; import {
type BlockStdScope,
TextSelection,
type UIEventHandler,
} from '@blocksuite/block-std';
import type { InlineEditor } from '@blocksuite/inline'; import type { InlineEditor } from '@blocksuite/inline';
import { getInlineEditorByModel } from '../dom.js'; import { getInlineEditorByModel } from '../dom.js';
@@ -20,7 +24,7 @@ export const bracketKeymap = (
const { doc, selection } = std; const { doc, selection } = std;
if (doc.readonly) return; if (doc.readonly) return;
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if (!textSelection) return; if (!textSelection) return;
const model = doc.getBlock(textSelection.from.blockId)?.model; const model = doc.getBlock(textSelection.from.blockId)?.model;
if (!model) return; if (!model) return;
@@ -46,7 +50,7 @@ export const bracketKeymap = (
const { doc, selection } = std; const { doc, selection } = std;
if (doc.readonly) return; if (doc.readonly) return;
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if (!textSelection) return; if (!textSelection) return;
const model = doc.getBlock(textSelection.from.blockId)?.model; const model = doc.getBlock(textSelection.from.blockId)?.model;
if (!model) return; if (!model) return;
@@ -97,7 +101,7 @@ export const bracketKeymap = (
const { doc, selection } = std; const { doc, selection } = std;
if (doc.readonly) return; if (doc.readonly) return;
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if (!textSelection || textSelection.isCollapsed()) return; if (!textSelection || textSelection.isCollapsed()) return;
if (!textSelection.isInSameBlock()) return; if (!textSelection.isInSameBlock()) return;
const model = doc.getBlock(textSelection.from.blockId)?.model; const model = doc.getBlock(textSelection.from.blockId)?.model;
@@ -1,4 +1,8 @@
import type { BlockStdScope, UIEventHandler } from '@blocksuite/block-std'; import {
type BlockStdScope,
TextSelection,
type UIEventHandler,
} from '@blocksuite/block-std';
import { textFormatConfigs } from '../format/index.js'; import { textFormatConfigs } from '../format/index.js';
@@ -13,7 +17,7 @@ export const textFormatKeymap = (std: BlockStdScope) =>
const { doc, selection } = std; const { doc, selection } = std;
if (doc.readonly) return; if (doc.readonly) return;
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if (!textSelection) return; if (!textSelection) return;
config.action(std.host); config.action(std.host);
@@ -2,7 +2,7 @@ import {
isMarkdownPrefix, isMarkdownPrefix,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope } from '@blocksuite/block-std'; import { type BlockStdScope, TextSelection } from '@blocksuite/block-std';
import { getInlineEditorByModel } from '../dom.js'; import { getInlineEditorByModel } from '../dom.js';
import { toDivider } from './divider.js'; import { toDivider } from './divider.js';
@@ -17,7 +17,7 @@ export function markdownInput(
): string | undefined { ): string | undefined {
if (!id) { if (!id) {
const selection = std.selection; const selection = std.selection;
const text = selection.find('text'); const text = selection.find(TextSelection);
id = text?.from.blockId; id = text?.from.blockId;
} }
if (!id) return; if (!id) return;
@@ -1,7 +1,8 @@
import type { import {
BlockStdScope, type BlockStdScope,
EditorHost, type EditorHost,
TextRangePoint, type TextRangePoint,
TextSelection,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import type { import type {
BlockSnapshot, BlockSnapshot,
@@ -38,7 +39,7 @@ const sliceText = (slots: JobSlots, std: EditorHost['std']) => {
const snapshot = payload.snapshot; const snapshot = payload.snapshot;
const model = payload.model; const model = payload.model;
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (text && text.from.blockId === model.id) { if (text && text.from.blockId === model.id) {
handlePoint(text.from, snapshot, model); handlePoint(text.from, snapshot, model);
return; return;
@@ -7,9 +7,10 @@ import {
import { import {
BLOCK_ID_ATTR, BLOCK_ID_ATTR,
type BlockComponent, type BlockComponent,
BlockSelection,
type EditorHost, type EditorHost,
type TextRangePoint, type TextRangePoint,
type TextSelection, TextSelection,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
@@ -25,6 +26,7 @@ import {
import * as Y from 'yjs'; import * as Y from 'yjs';
import { REFERENCE_NODE } from '../../consts'; import { REFERENCE_NODE } from '../../consts';
import { ImageSelection } from '../../selection';
import { import {
ParseDocUrlProvider, ParseDocUrlProvider,
type ParseDocUrlService, type ParseDocUrlService,
@@ -290,19 +292,19 @@ class PasteTr {
} }
if (!cursorModel.text) { if (!cursorModel.text) {
if (matchFlavours(cursorModel, ['affine:image'])) { if (matchFlavours(cursorModel, ['affine:image'])) {
const selection = this.std.selection.create('image', { const selection = this.std.selection.create(ImageSelection, {
blockId: target.blockId, blockId: target.blockId,
}); });
this.std.selection.setGroup('note', [selection]); this.std.selection.setGroup('note', [selection]);
return; return;
} }
const selection = this.std.selection.create('block', { const selection = this.std.selection.create(BlockSelection, {
blockId: target.blockId, blockId: target.blockId,
}); });
this.std.selection.setGroup('note', [selection]); this.std.selection.setGroup('note', [selection]);
return; return;
} }
const selection = this.std.selection.create('text', { const selection = this.std.selection.create(TextSelection, {
from: { from: {
blockId: target.blockId, blockId: target.blockId,
index: cursorModel.text ? this.lastIndex : 0, index: cursorModel.text ? this.lastIndex : 0,
@@ -511,7 +513,7 @@ export const pasteMiddleware = (std: EditorHost['std']): JobMiddleware => {
const { snapshot } = payload; const { snapshot } = payload;
flatNote(snapshot); flatNote(snapshot);
const text = std.selection.find('text'); const text = std.selection.find(TextSelection);
if (!text) { if (!text) {
return; return;
} }
@@ -16,7 +16,7 @@ export const getSelectedBlocksCommand: Command<
blockSelections?: BlockSelection[]; blockSelections?: BlockSelection[];
imageSelections?: ImageSelection[]; imageSelections?: ImageSelection[];
filter?: (el: BlockComponent) => boolean; filter?: (el: BlockComponent) => boolean;
types?: Extract<BlockSuite.SelectionType, 'block' | 'text' | 'image'>[]; types?: Array<'image' | 'text' | 'block'>;
roles?: RoleType[]; roles?: RoleType[];
mode?: 'all' | 'flat' | 'highest'; mode?: 'all' | 'flat' | 'highest';
} }
@@ -1,4 +1,4 @@
import type { Command } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
export const clearAndSelectFirstModelCommand: Command<'selectedModels'> = ( export const clearAndSelectFirstModelCommand: Command<'selectedModels'> = (
ctx, ctx,
@@ -17,7 +17,7 @@ export const clearAndSelectFirstModelCommand: Command<'selectedModels'> = (
const firstModel = models[0]; const firstModel = models[0];
if (firstModel.text) { if (firstModel.text) {
firstModel.text.clear(); firstModel.text.clear();
const selection = ctx.std.selection.create('text', { const selection = ctx.std.selection.create(TextSelection, {
from: { from: {
blockId: firstModel.id, blockId: firstModel.id,
index: 0, index: 0,
@@ -32,7 +32,7 @@ export const getSelectedModelsCommand: Command<
never, never,
'selectedModels', 'selectedModels',
{ {
types?: Extract<BlockSuite.SelectionType, 'block' | 'text' | 'image'>[]; types?: Array<'image' | 'text' | 'block'>;
mode?: 'all' | 'flat' | 'highest'; mode?: 'all' | 'flat' | 'highest';
} }
> = (ctx, next) => { > = (ctx, next) => {
@@ -1,10 +1,10 @@
import type { BlockSelection, Command } from '@blocksuite/block-std'; import { BlockSelection, type Command } from '@blocksuite/block-std';
export const getBlockSelectionsCommand: Command< export const getBlockSelectionsCommand: Command<
never, never,
'currentBlockSelections' 'currentBlockSelections'
> = (ctx, next) => { > = (ctx, next) => {
const currentBlockSelections = ctx.std.selection.filter('block'); const currentBlockSelections = ctx.std.selection.filter(BlockSelection);
if (currentBlockSelections.length === 0) return; if (currentBlockSelections.length === 0) return;
next({ currentBlockSelections }); next({ currentBlockSelections });
@@ -1,12 +1,12 @@
import type { Command } from '@blocksuite/block-std'; import type { Command } from '@blocksuite/block-std';
import type { ImageSelection } from '../../selection/index.js'; import { ImageSelection } from '../../selection/index.js';
export const getImageSelectionsCommand: Command< export const getImageSelectionsCommand: Command<
never, never,
'currentImageSelections' 'currentImageSelections'
> = (ctx, next) => { > = (ctx, next) => {
const currentImageSelections = ctx.std.selection.filter('image'); const currentImageSelections = ctx.std.selection.filter(ImageSelection);
if (currentImageSelections.length === 0) return; if (currentImageSelections.length === 0) return;
next({ currentImageSelections }); next({ currentImageSelections });
@@ -1,10 +1,10 @@
import type { Command, TextSelection } from '@blocksuite/block-std'; import { type Command, TextSelection } from '@blocksuite/block-std';
export const getTextSelectionCommand: Command<never, 'currentTextSelection'> = ( export const getTextSelectionCommand: Command<never, 'currentTextSelection'> = (
ctx, ctx,
next next
) => { ) => {
const currentTextSelection = ctx.std.selection.find('text'); const currentTextSelection = ctx.std.selection.find(TextSelection);
if (!currentTextSelection) return; if (!currentTextSelection) return;
next({ currentTextSelection }); next({ currentTextSelection });
@@ -1,5 +1,10 @@
import { findNoteBlockModel } from '@blocksuite/affine-shared/utils'; import { findNoteBlockModel } from '@blocksuite/affine-shared/utils';
import type { BlockComponent } from '@blocksuite/block-std'; import {
type BlockComponent,
BlockSelection,
SurfaceSelection,
TextSelection,
} from '@blocksuite/block-std';
import type { AffineDragHandleWidget } from '../drag-handle.js'; import type { AffineDragHandleWidget } from '../drag-handle.js';
@@ -15,7 +20,7 @@ export class SelectionHelper {
setSelectedBlocks = (blocks: BlockComponent[], noteId?: string) => { setSelectedBlocks = (blocks: BlockComponent[], noteId?: string) => {
const { selection } = this; const { selection } = this;
const selections = blocks.map(block => const selections = blocks.map(block =>
selection.create('block', { selection.create(BlockSelection, {
blockId: block.blockId, blockId: block.blockId,
}) })
); );
@@ -28,7 +33,7 @@ export class SelectionHelper {
: findNoteBlockModel(blocks[0].model)?.id; : findNoteBlockModel(blocks[0].model)?.id;
if (!surfaceElementId) return; if (!surfaceElementId) return;
const surfaceSelection = selection.create( const surfaceSelection = selection.create(
'surface', SurfaceSelection,
blocks[0]!.blockId, blocks[0]!.blockId,
[surfaceElementId], [surfaceElementId],
true true
@@ -49,9 +54,9 @@ export class SelectionHelper {
get selectedBlocks() { get selectedBlocks() {
const selection = this.selection; const selection = this.selection;
return selection.find('text') return selection.find(TextSelection)
? selection.filter('text') ? selection.filter(TextSelection)
: selection.filter('block'); : selection.filter(BlockSelection);
} }
get selection() { get selection() {
@@ -23,6 +23,7 @@ import {
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import { import {
type BlockComponent, type BlockComponent,
BlockSelection,
type DndEventState, type DndEventState,
isGfxBlockComponent, isGfxBlockComponent,
type UIEventHandler, type UIEventHandler,
@@ -158,7 +159,7 @@ export class DragEventWatcher {
const selectBlockAndStartDragging = () => { const selectBlockAndStartDragging = () => {
this._std.selection.setGroup('note', [ this._std.selection.setGroup('note', [
this._std.selection.create('block', { this._std.selection.create(BlockSelection, {
blockId: hoverBlock.blockId, blockId: hoverBlock.blockId,
}), }),
]); ]);
@@ -101,7 +101,7 @@ export class AffineDocRemoteSelectionWidget extends WidgetComponent {
if (textSelection) { if (textSelection) {
const range = this.std.range.textSelectionToRange( const range = this.std.range.textSelectionToRange(
this._selectionManager.create('text', { this._selectionManager.create(TextSelection, {
from: { from: {
blockId: textSelection.to blockId: textSelection.to
? textSelection.to.blockId ? textSelection.to.blockId
@@ -1,6 +1,5 @@
import '@blocksuite/affine-shared/selection';
import type { DocMode } from '@blocksuite/affine-model'; import type { DocMode } from '@blocksuite/affine-model';
import { HighlightSelection } from '@blocksuite/affine-shared/selection';
import { WidgetComponent } from '@blocksuite/block-std'; import { WidgetComponent } from '@blocksuite/block-std';
import { import {
GfxControllerIdentifier, GfxControllerIdentifier,
@@ -65,7 +64,7 @@ export class AffineScrollAnchoringWidget extends WidgetComponent {
anchorBounds$ = signal<Bound | null>(null); anchorBounds$ = signal<Bound | null>(null);
highlighted$ = computed(() => highlighted$ = computed(() =>
this.service.selectionManager.find('highlight') this.service.selectionManager.find(HighlightSelection)
); );
#getBoundsInEdgeless() { #getBoundsInEdgeless() {
+1
View File
@@ -114,6 +114,7 @@ export {
PlainTextAdapterFactoryExtension, PlainTextAdapterFactoryExtension,
PlainTextAdapterFactoryIdentifier, PlainTextAdapterFactoryIdentifier,
} from '@blocksuite/affine-shared/adapters'; } from '@blocksuite/affine-shared/adapters';
export { HighlightSelection } from '@blocksuite/affine-shared/selection';
export * from '@blocksuite/affine-shared/services'; export * from '@blocksuite/affine-shared/services';
export { scrollbarStyle } from '@blocksuite/affine-shared/styles'; export { scrollbarStyle } from '@blocksuite/affine-shared/styles';
export { export {
@@ -16,6 +16,7 @@ import {
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { LassoMode } from '@blocksuite/affine-shared/types'; import { LassoMode } from '@blocksuite/affine-shared/types';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import { SurfaceSelection, TextSelection } from '@blocksuite/block-std';
import { import {
GfxBlockElementModel, GfxBlockElementModel,
type GfxToolsMap, type GfxToolsMap,
@@ -167,8 +168,8 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
const std = this.rootComponent.std; const std = this.rootComponent.std;
if ( if (
std.selection.getGroup('note').length > 0 || std.selection.getGroup('note').length > 0 ||
std.selection.find('text') || std.selection.find(TextSelection) ||
Boolean(std.selection.find('surface')?.editing) Boolean(std.selection.find(SurfaceSelection)?.editing)
) { ) {
return; return;
} }
@@ -22,12 +22,12 @@ import {
requestConnectedFrame, requestConnectedFrame,
requestThrottledConnectedFrame, requestThrottledConnectedFrame,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { import {
GfxBlockComponent, BlockComponent,
type GfxBlockComponent,
SurfaceSelection, SurfaceSelection,
UIEventHandler, type UIEventHandler,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { BlockComponent } from '@blocksuite/block-std';
import { import {
GfxControllerIdentifier, GfxControllerIdentifier,
type GfxViewportElement, type GfxViewportElement,
@@ -454,7 +454,7 @@ export class EdgelessRootBlockComponent extends BlockComponent<
this.handleEvent('selectionChange', () => { this.handleEvent('selectionChange', () => {
const surface = this.host.selection.value.find( const surface = this.host.selection.value.find(
(sel): sel is SurfaceSelection => sel.is('surface') (sel): sel is SurfaceSelection => sel.is(SurfaceSelection)
); );
if (!surface) return; if (!surface) return;
@@ -9,11 +9,11 @@ import {
ThemeProvider, ThemeProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { requestThrottledConnectedFrame } from '@blocksuite/affine-shared/utils'; import { requestThrottledConnectedFrame } from '@blocksuite/affine-shared/utils';
import type { import {
GfxBlockComponent, BlockComponent,
type GfxBlockComponent,
SurfaceSelection, SurfaceSelection,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { BlockComponent } from '@blocksuite/block-std';
import type { GfxViewportElement } from '@blocksuite/block-std/gfx'; import type { GfxViewportElement } from '@blocksuite/block-std/gfx';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
import { css, html } from 'lit'; import { css, html } from 'lit';
@@ -181,7 +181,7 @@ export class EdgelessRootPreviewBlockComponent
this.handleEvent('selectionChange', () => { this.handleEvent('selectionChange', () => {
const surface = this.host.selection.value.find( const surface = this.host.selection.value.find(
(sel): sel is SurfaceSelection => sel.is('surface') (sel): sel is SurfaceSelection => sel.is(SurfaceSelection)
); );
if (!surface) return; if (!surface) return;
@@ -6,14 +6,18 @@ import {
} from '@blocksuite/affine-block-embed'; } from '@blocksuite/affine-block-embed';
import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph'; import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { BlockComponent, UIEventHandler } from '@blocksuite/block-std'; import {
type BlockComponent,
BlockSelection,
type UIEventHandler,
} from '@blocksuite/block-std';
import { IS_MAC, IS_WINDOWS } from '@blocksuite/global/env'; import { IS_MAC, IS_WINDOWS } from '@blocksuite/global/env';
export class PageKeyboardManager { export class PageKeyboardManager {
private readonly _handleDelete: UIEventHandler = ctx => { private readonly _handleDelete: UIEventHandler = ctx => {
const event = ctx.get('keyboardState').raw; const event = ctx.get('keyboardState').raw;
const blockSelections = this._currentSelection.filter(sel => const blockSelections = this._currentSelection.filter(sel =>
sel.is('block') sel.is(BlockSelection)
); );
if (blockSelections.length === 0) { if (blockSelections.length === 0) {
return; return;
@@ -10,7 +10,11 @@ import {
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { PointerEventState } from '@blocksuite/block-std'; import type { PointerEventState } from '@blocksuite/block-std';
import { BlockComponent } from '@blocksuite/block-std'; import {
BlockComponent,
BlockSelection,
TextSelection,
} from '@blocksuite/block-std';
import type { BlockModel, Text } from '@blocksuite/store'; import type { BlockModel, Text } from '@blocksuite/store';
import { css, html } from 'lit'; import { css, html } from 'lit';
import { query } from 'lit/decorators.js'; import { query } from 'lit/decorators.js';
@@ -236,7 +240,7 @@ export class PageRootBlockComponent extends BlockComponent<
}) })
.flatMap(model => { .flatMap(model => {
return model.children.map(child => { return model.children.map(child => {
return this.std.selection.create('block', { return this.std.selection.create(BlockSelection, {
blockId: child.id, blockId: child.id,
}); });
}); });
@@ -247,7 +251,7 @@ export class PageRootBlockComponent extends BlockComponent<
ArrowUp: () => { ArrowUp: () => {
const selection = this.host.selection; const selection = this.host.selection;
const sel = selection.value.find( const sel = selection.value.find(
sel => sel.is('text') || sel.is('block') sel => sel.is(TextSelection) || sel.is(BlockSelection)
); );
if (!sel) return; if (!sel) return;
let model: BlockModel | null = null; let model: BlockModel | null = null;
@@ -262,8 +266,8 @@ export class PageRootBlockComponent extends BlockComponent<
if (!model) return; if (!model) return;
const prevNote = this.doc.getPrev(model); const prevNote = this.doc.getPrev(model);
if (!prevNote || prevNote.flavour !== 'affine:note') { if (!prevNote || prevNote.flavour !== 'affine:note') {
const isFirstText = sel.is('text') && sel.start.index === 0; const isFirstText = sel.is(TextSelection) && sel.start.index === 0;
const isBlock = sel.is('block'); const isBlock = sel.is(BlockSelection);
if (isBlock || isFirstText) { if (isBlock || isFirstText) {
focusTitle(this.host); focusTitle(this.host);
} }
@@ -361,7 +365,7 @@ export class PageRootBlockComponent extends BlockComponent<
.then(() => { .then(() => {
if (!newTextSelectionId) return; if (!newTextSelectionId) return;
this.host.selection.setGroup('note', [ this.host.selection.setGroup('note', [
this.host.selection.create('text', { this.host.selection.create(TextSelection, {
from: { from: {
blockId: newTextSelectionId, blockId: newTextSelectionId,
index: 0, index: 0,
@@ -52,7 +52,12 @@ import {
ThemeProvider, ThemeProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { getHostName, referenceToNode } from '@blocksuite/affine-shared/utils'; import { getHostName, referenceToNode } from '@blocksuite/affine-shared/utils';
import { type BlockStdScope, WidgetComponent } from '@blocksuite/block-std'; import {
BlockSelection,
type BlockStdScope,
TextSelection,
WidgetComponent,
} from '@blocksuite/block-std';
import { type BlockModel, Text } from '@blocksuite/store'; import { type BlockModel, Text } from '@blocksuite/store';
import { autoUpdate, computePosition, flip, offset } from '@floating-ui/dom'; import { autoUpdate, computePosition, flip, offset } from '@floating-ui/dom';
import { html, nothing, type TemplateResult } from 'lit'; import { html, nothing, type TemplateResult } from 'lit';
@@ -709,13 +714,13 @@ export class EmbedCardToolbar extends WidgetComponent<
this.disposables.add( this.disposables.add(
this._selection.slots.changed.on(() => { this._selection.slots.changed.on(() => {
const hasTextSelection = this._selection.find('text'); const hasTextSelection = this._selection.find(TextSelection);
if (hasTextSelection) { if (hasTextSelection) {
this._hide(); this._hide();
return; return;
} }
const blockSelections = this._selection.filter('block'); const blockSelections = this._selection.filter(BlockSelection);
if (!blockSelections || blockSelections.length !== 1) { if (!blockSelections || blockSelections.length !== 1) {
this._hide(); this._hide();
return; return;
@@ -8,12 +8,15 @@ import {
} from '@blocksuite/affine-components/toolbar'; } from '@blocksuite/affine-components/toolbar';
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types'; import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
import { matchFlavours } from '@blocksuite/affine-shared/utils'; import { matchFlavours } from '@blocksuite/affine-shared/utils';
import type { import {
BaseSelection, type BaseSelection,
BlockComponent, type BlockComponent,
BlockSelection,
CursorSelection, CursorSelection,
TextSelection,
WidgetComponent,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { WidgetComponent } from '@blocksuite/block-std'; import { DatabaseSelection } from '@blocksuite/data-view';
import { import {
assertExists, assertExists,
DisposableGroup, DisposableGroup,
@@ -129,11 +132,12 @@ export class AffineFormatBarWidget extends WidgetComponent {
this.disposables.add( this.disposables.add(
this._selectionManager.slots.changed.on(() => { this._selectionManager.slots.changed.on(() => {
const update = async () => { const update = async () => {
const textSelection = rootComponent.selection.find('text'); const textSelection = rootComponent.selection.find(TextSelection);
const blockSelections = rootComponent.selection.filter('block'); const blockSelections =
rootComponent.selection.filter(BlockSelection);
// Should not re-render format bar when only cursor selection changed in edgeless // Should not re-render format bar when only cursor selection changed in edgeless
const cursorSelection = rootComponent.selection.find('cursor'); const cursorSelection = rootComponent.selection.find(CursorSelection);
if (cursorSelection) { if (cursorSelection) {
if (!this._lastCursor) { if (!this._lastCursor) {
this._lastCursor = cursorSelection; this._lastCursor = cursorSelection;
@@ -202,7 +206,7 @@ export class AffineFormatBarWidget extends WidgetComponent {
this.disposables.addFromEvent(document, 'selectionchange', () => { this.disposables.addFromEvent(document, 'selectionchange', () => {
if (!this.host.event.active) return; if (!this.host.event.active) return;
const databaseSelection = this.host.selection.find('database'); const databaseSelection = this.host.selection.find(DatabaseSelection);
if (!databaseSelection) { if (!databaseSelection) {
return; return;
} }
@@ -10,7 +10,11 @@ import {
} from '@blocksuite/affine-components/toolbar'; } from '@blocksuite/affine-components/toolbar';
import type { ImageBlockModel } from '@blocksuite/affine-model'; import type { ImageBlockModel } from '@blocksuite/affine-model';
import { PAGE_HEADER_HEIGHT } from '@blocksuite/affine-shared/consts'; import { PAGE_HEADER_HEIGHT } from '@blocksuite/affine-shared/consts';
import { WidgetComponent } from '@blocksuite/block-std'; import {
BlockSelection,
TextSelection,
WidgetComponent,
} from '@blocksuite/block-std';
import { limitShift, shift } from '@floating-ui/dom'; import { limitShift, shift } from '@floating-ui/dom';
import { html } from 'lit'; import { html } from 'lit';
@@ -35,7 +39,7 @@ export class AffineImageToolbarWidget extends WidgetComponent<
const imageBlock = this.block; const imageBlock = this.block;
const selection = this.host.selection; const selection = this.host.selection;
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if ( if (
!!textSelection && !!textSelection &&
(!!textSelection.to || !!textSelection.from.length) (!!textSelection.to || !!textSelection.from.length)
@@ -43,7 +47,7 @@ export class AffineImageToolbarWidget extends WidgetComponent<
return null; return null;
} }
const blockSelections = selection.filter('block'); const blockSelections = selection.filter(BlockSelection);
if ( if (
blockSelections.length > 1 || blockSelections.length > 1 ||
(blockSelections.length === 1 && (blockSelections.length === 1 &&
@@ -3,6 +3,7 @@ import {
getBlockProps, getBlockProps,
isInsidePageEditor, isInsidePageEditor,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import { BlockSelection } from '@blocksuite/block-std';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
export function duplicate( export function duplicate(
@@ -38,7 +39,7 @@ export function duplicate(
.then(() => { .then(() => {
const { selection } = editorHost; const { selection } = editorHost;
selection.setGroup('note', [ selection.setGroup('note', [
selection.create('block', { selection.create(BlockSelection, {
blockId: duplicateId, blockId: duplicateId,
}), }),
]); ]);
@@ -7,6 +7,7 @@ import {
import { import {
BLOCK_ID_ATTR, BLOCK_ID_ATTR,
BlockComponent, BlockComponent,
BlockSelection,
type PointerEventState, type PointerEventState,
WidgetComponent, WidgetComponent,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
@@ -188,7 +189,7 @@ export class AffinePageDraggingAreaWidget extends WidgetComponent<
this._allBlocksWithRect, this._allBlocksWithRect,
userRect userRect
).map(blockPath => { ).map(blockPath => {
return this.host.selection.create('block', { return this.host.selection.create(BlockSelection, {
blockId: blockPath, blockId: blockPath,
}); });
}); });
@@ -7,7 +7,7 @@ import {
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { UIEventStateContext } from '@blocksuite/block-std'; import type { UIEventStateContext } from '@blocksuite/block-std';
import { WidgetComponent } from '@blocksuite/block-std'; import { TextSelection, WidgetComponent } from '@blocksuite/block-std';
import { import {
assertExists, assertExists,
assertType, assertType,
@@ -122,7 +122,7 @@ export class AffineSlashMenuWidget extends WidgetComponent {
} }
} }
const textSelection = this.host.selection.find('text'); const textSelection = this.host.selection.find(TextSelection);
if (!textSelection) return; if (!textSelection) return;
const model = this.host.doc.getBlock(textSelection.blockId)?.model; const model = this.host.doc.getBlock(textSelection.blockId)?.model;
@@ -149,7 +149,7 @@ export class AffineSlashMenuWidget extends WidgetComponent {
assertType<RootBlockComponent>(rootComponent); assertType<RootBlockComponent>(rootComponent);
inlineRangeApplyCallback(() => { inlineRangeApplyCallback(() => {
const textSelection = this.host.selection.find('text'); const textSelection = this.host.selection.find(TextSelection);
if (!textSelection) return; if (!textSelection) return;
const model = this.host.doc.getBlock(textSelection.blockId)?.model; const model = this.host.doc.getBlock(textSelection.blockId)?.model;
@@ -3,6 +3,7 @@ import type {
TextFormatConfig, TextFormatConfig,
} from '@blocksuite/affine-components/rich-text'; } from '@blocksuite/affine-components/rich-text';
import { isInsideBlockByFlavour } from '@blocksuite/affine-shared/utils'; import { isInsideBlockByFlavour } from '@blocksuite/affine-shared/utils';
import { BlockSelection } from '@blocksuite/block-std';
import { assertType } from '@blocksuite/global/utils'; import { assertType } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
@@ -128,7 +129,7 @@ export function createTextFormatItem(
.chain() .chain()
.formatBlock({ .formatBlock({
blockSelections: [ blockSelections: [
std.selection.create('block', { std.selection.create(BlockSelection, {
blockId: model.id, blockId: model.id,
}), }),
], ],
@@ -19,7 +19,11 @@ import {
} from '@blocksuite/affine-components/toolbar'; } from '@blocksuite/affine-components/toolbar';
import type { SurfaceRefBlockModel } from '@blocksuite/affine-model'; import type { SurfaceRefBlockModel } from '@blocksuite/affine-model';
import { PAGE_HEADER_HEIGHT } from '@blocksuite/affine-shared/consts'; import { PAGE_HEADER_HEIGHT } from '@blocksuite/affine-shared/consts';
import { WidgetComponent } from '@blocksuite/block-std'; import {
BlockSelection,
TextSelection,
WidgetComponent,
} from '@blocksuite/block-std';
import { offset, shift } from '@floating-ui/dom'; import { offset, shift } from '@floating-ui/dom';
import { html, nothing } from 'lit'; import { html, nothing } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js'; import { ifDefined } from 'lit/directives/if-defined.js';
@@ -48,7 +52,7 @@ export class AffineSurfaceRefToolbar extends WidgetComponent<
const surfaceRefBlock = this.block; const surfaceRefBlock = this.block;
const selection = this.host.selection; const selection = this.host.selection;
const textSelection = selection.find('text'); const textSelection = selection.find(TextSelection);
if ( if (
!!textSelection && !!textSelection &&
(!!textSelection.to || !!textSelection.from.length) (!!textSelection.to || !!textSelection.from.length)
@@ -56,7 +60,7 @@ export class AffineSurfaceRefToolbar extends WidgetComponent<
return null; return null;
} }
const blockSelections = selection.filter('block'); const blockSelections = selection.filter(BlockSelection);
if ( if (
blockSelections.length > 1 || blockSelections.length > 1 ||
(blockSelections.length === 1 && (blockSelections.length === 1 &&
@@ -7,7 +7,12 @@ import {
Slot, Slot,
} from '@blocksuite/global/utils'; } from '@blocksuite/global/utils';
import type { CursorSelection, SurfaceSelection } from '../selection/index.js'; import {
BlockSelection,
CursorSelection,
SurfaceSelection,
TextSelection,
} from '../selection/index.js';
import type { GfxController } from './controller.js'; import type { GfxController } from './controller.js';
import { GfxExtension, GfxExtensionIdentifier } from './extension.js'; import { GfxExtension, GfxExtensionIdentifier } from './extension.js';
import type { GfxModel } from './model/model.js'; import type { GfxModel } from './model/model.js';
@@ -215,9 +220,9 @@ export class GfxSelectionManager extends GfxExtension {
this.disposable.add( this.disposable.add(
this.stdSelection.slots.changed.on(selections => { this.stdSelection.slots.changed.on(selections => {
const { cursor = [], surface = [] } = groupBy(selections, sel => { const { cursor = [], surface = [] } = groupBy(selections, sel => {
if (sel.is('surface')) { if (sel.is(SurfaceSelection)) {
return 'surface'; return 'surface';
} else if (sel.is('cursor')) { } else if (sel.is(CursorSelection)) {
return 'cursor'; return 'cursor';
} }
@@ -261,15 +266,15 @@ export class GfxSelectionManager extends GfxExtension {
let hasBlockSelection = false; let hasBlockSelection = false;
selections.forEach(selection => { selections.forEach(selection => {
if (selection.is('text')) { if (selection.is(TextSelection)) {
hasTextSelection = true; hasTextSelection = true;
} }
if (selection.is('block')) { if (selection.is(BlockSelection)) {
hasBlockSelection = true; hasBlockSelection = true;
} }
if (selection.is('surface')) { if (selection.is(SurfaceSelection)) {
const surfaceSelections = surfaceMap.get(id) ?? []; const surfaceSelections = surfaceMap.get(id) ?? [];
surfaceSelections.push(selection); surfaceSelections.push(selection);
surfaceMap.set(id, surfaceSelections); surfaceMap.set(id, surfaceSelections);
@@ -277,7 +282,7 @@ export class GfxSelectionManager extends GfxExtension {
selection.elements.forEach(id => selectedSet.add(id)); selection.elements.forEach(id => selectedSet.add(id));
} }
if (selection.is('cursor')) { if (selection.is(CursorSelection)) {
cursorMap.set(id, selection); cursorMap.set(id, selection);
} }
}); });
@@ -318,7 +323,7 @@ export class GfxSelectionManager extends GfxExtension {
if (elements.length > 0 && this.surfaceModel) { if (elements.length > 0 && this.surfaceModel) {
instances.push( instances.push(
this.stdSelection.create( this.stdSelection.create(
'surface', SurfaceSelection,
this.surfaceModel.id, this.surfaceModel.id,
elements, elements,
selection.editing ?? false, selection.editing ?? false,
@@ -331,7 +336,7 @@ export class GfxSelectionManager extends GfxExtension {
instances = instances.concat( instances = instances.concat(
blocks.map(blockId => blocks.map(blockId =>
this.stdSelection.create( this.stdSelection.create(
'surface', SurfaceSelection,
blockId, blockId,
[blockId], [blockId],
selection.editing ?? false, selection.editing ?? false,
@@ -368,7 +373,11 @@ export class GfxSelectionManager extends GfxExtension {
} }
setCursor(cursor: CursorSelection | IPoint) { setCursor(cursor: CursorSelection | IPoint) {
const instance = this.stdSelection.create('cursor', cursor.x, cursor.y); const instance = this.stdSelection.create(
CursorSelection,
cursor.x,
cursor.y
);
this.stdSelection.setGroup('gfx', [...this.surfaceSelections, instance]); this.stdSelection.setGroup('gfx', [...this.surfaceSelections, instance]);
} }
@@ -1,7 +1,7 @@
import type { InlineRange, InlineRangeProvider } from '@blocksuite/inline'; import type { InlineRange, InlineRangeProvider } from '@blocksuite/inline';
import { signal } from '@preact/signals-core'; import { signal } from '@preact/signals-core';
import type { TextSelection } from '../selection/index.js'; import { TextSelection } from '../selection/index.js';
import type { BlockComponent } from '../view/element/block-component.js'; import type { BlockComponent } from '../view/element/block-component.js';
export const getInlineRangeProvider: ( export const getInlineRangeProvider: (
@@ -40,7 +40,7 @@ export const getInlineRangeProvider: (
} }
const elementRange = rangeManager.textSelectionToRange( const elementRange = rangeManager.textSelectionToRange(
selectionManager.create('text', { selectionManager.create(TextSelection, {
from: { from: {
index: 0, index: 0,
blockId: element.blockId, blockId: element.blockId,
@@ -72,7 +72,7 @@ export const getInlineRangeProvider: (
if (!inlineRange) { if (!inlineRange) {
selectionManager.clear(['text']); selectionManager.clear(['text']);
} else { } else {
const textSelection = selectionManager.create('text', { const textSelection = selectionManager.create(TextSelection, {
from: { from: {
blockId: element.blockId, blockId: element.blockId,
index: inlineRange.index, index: inlineRange.index,
@@ -1,7 +1,7 @@
import { throttle } from '@blocksuite/global/utils'; import { throttle } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
import type { BaseSelection, TextSelection } from '../selection/index.js'; import { type BaseSelection, TextSelection } from '../selection/index.js';
import type { BlockComponent } from '../view/element/block-component.js'; import type { BlockComponent } from '../view/element/block-component.js';
import { BLOCK_ID_ATTR } from '../view/index.js'; import { BLOCK_ID_ATTR } from '../view/index.js';
import { RANGE_SYNC_EXCLUDE_ATTR } from './consts.js'; import { RANGE_SYNC_EXCLUDE_ATTR } from './consts.js';
@@ -30,7 +30,7 @@ export class RangeBinding {
}; };
private readonly _onBeforeInput = (event: InputEvent) => { private readonly _onBeforeInput = (event: InputEvent) => {
const selection = this.selectionManager.find('text'); const selection = this.selectionManager.find(TextSelection);
if (!selection) return; if (!selection) return;
if (event.isComposing) return; if (event.isComposing) return;
@@ -74,7 +74,7 @@ export class RangeBinding {
}); });
}); });
const newSelection = this.selectionManager.create('text', { const newSelection = this.selectionManager.create(TextSelection, {
from: { from: {
blockId: from.blockId, blockId: from.blockId,
index: from.index + (event.data?.length ?? 0), index: from.index + (event.data?.length ?? 0),
@@ -95,7 +95,7 @@ export class RangeBinding {
}; };
private readonly _onCompositionStart = () => { private readonly _onCompositionStart = () => {
const selection = this.selectionManager.find('text'); const selection = this.selectionManager.find(TextSelection);
if (!selection) return; if (!selection) return;
const { from, to } = selection; const { from, to } = selection;
@@ -153,7 +153,7 @@ export class RangeBinding {
await this.host.updateComplete; await this.host.updateComplete;
const selection = this.selectionManager.create('text', { const selection = this.selectionManager.create(TextSelection, {
from: { from: {
blockId: from.blockId, blockId: from.blockId,
index: from.index + (event.data?.length ?? 0), index: from.index + (event.data?.length ?? 0),
@@ -249,7 +249,7 @@ export class RangeBinding {
private readonly _onStdSelectionChanged = (selections: BaseSelection[]) => { private readonly _onStdSelectionChanged = (selections: BaseSelection[]) => {
const text = const text =
selections.find((selection): selection is TextSelection => selections.find((selection): selection is TextSelection =>
selection.is('text') selection.is(TextSelection)
) ?? null; ) ?? null;
if (text === this._prevTextSelection) { if (text === this._prevTextSelection) {
@@ -1,7 +1,7 @@
import { INLINE_ROOT_ATTR, type InlineRootElement } from '@blocksuite/inline'; import { INLINE_ROOT_ATTR, type InlineRootElement } from '@blocksuite/inline';
import { LifeCycleWatcher } from '../extension/index.js'; import { LifeCycleWatcher } from '../extension/index.js';
import type { TextSelection } from '../selection/index.js'; import { TextSelection } from '../selection/index.js';
import type { BlockComponent } from '../view/element/block-component.js'; import type { BlockComponent } from '../view/element/block-component.js';
import { BLOCK_ID_ATTR } from '../view/index.js'; import { BLOCK_ID_ATTR } from '../view/index.js';
import { RANGE_QUERY_EXCLUDE_ATTR, RANGE_SYNC_EXCLUDE_ATTR } from './consts.js'; import { RANGE_QUERY_EXCLUDE_ATTR, RANGE_SYNC_EXCLUDE_ATTR } from './consts.js';
@@ -163,7 +163,7 @@ export class RangeManager extends LifeCycleWatcher {
return null; return null;
} }
return this.std.host.selection.create('text', { return this.std.host.selection.create(TextSelection, {
from: { from: {
blockId: startBlock.blockId, blockId: startBlock.blockId,
index: startInlineRange.index, index: startInlineRange.index,
@@ -1,10 +1,6 @@
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
type SelectionConstructor<T = unknown> = { import type { SelectionConstructor } from './manager';
type: string;
group: string;
new (...args: unknown[]): T;
};
export type BaseSelectionOptions = { export type BaseSelectionOptions = {
blockId: string; blockId: string;
@@ -21,9 +17,8 @@ export abstract class BaseSelection {
return (this.constructor as SelectionConstructor).group; return (this.constructor as SelectionConstructor).group;
} }
get type(): BlockSuite.SelectionType { get type(): string {
return (this.constructor as SelectionConstructor) return (this.constructor as SelectionConstructor).type as string;
.type as BlockSuite.SelectionType;
} }
constructor({ blockId }: BaseSelectionOptions) { constructor({ blockId }: BaseSelectionOptions) {
@@ -39,10 +34,10 @@ export abstract class BaseSelection {
abstract equals(other: BaseSelection): boolean; abstract equals(other: BaseSelection): boolean;
is<T extends BlockSuite.SelectionType>( is<T extends SelectionConstructor>(
type: T type: T
): this is BlockSuite.SelectionInstance[T] { ): this is T extends SelectionConstructor<infer U> ? U : never {
return this.type === type; return this.type === type.type;
} }
abstract toJSON(): Record<string, unknown>; abstract toJSON(): Record<string, unknown>;
@@ -1,27 +1,3 @@
import type {
BlockSelection,
CursorSelection,
SurfaceSelection,
TextSelection,
} from './variants/index.js';
export * from './base.js'; export * from './base.js';
export * from './manager.js'; export * from './manager.js';
export * from './variants/index.js'; export * from './variants/index.js';
declare global {
namespace BlockSuite {
interface Selection {
block: typeof BlockSelection;
cursor: typeof CursorSelection;
surface: typeof SurfaceSelection;
text: typeof TextSelection;
}
type SelectionType = keyof Selection;
type SelectionInstance = {
[P in SelectionType]: InstanceType<Selection[P]>;
};
}
}
@@ -8,11 +8,12 @@ import { SelectionIdentifier } from '../identifier.js';
import type { BlockStdScope } from '../scope/index.js'; import type { BlockStdScope } from '../scope/index.js';
import type { BaseSelection } from './base.js'; import type { BaseSelection } from './base.js';
export interface SelectionConstructor { export interface SelectionConstructor<T extends BaseSelection = BaseSelection> {
type: string; type: string;
group: string;
new (...args: any[]): BaseSelection; new (...args: any[]): T;
fromJSON(json: Record<string, unknown>): BaseSelection; fromJSON(json: Record<string, unknown>): T;
} }
export class SelectionManager extends LifeCycleWatcher { export class SelectionManager extends LifeCycleWatcher {
@@ -143,18 +144,11 @@ export class SelectionManager extends LifeCycleWatcher {
} }
} }
create<T extends BlockSuite.SelectionType>( create<T extends SelectionConstructor>(
type: T, Type: T,
...args: ConstructorParameters<BlockSuite.Selection[T]> ...args: ConstructorParameters<T>
): BlockSuite.SelectionInstance[T] { ): InstanceType<T> {
const ctor = this._selectionConstructors[type]; return new Type(...args) as InstanceType<T>;
if (!ctor) {
throw new BlockSuiteError(
ErrorCode.SelectionError,
`Unknown selection type: ${type}`
);
}
return new ctor(...args) as BlockSuite.SelectionInstance[T];
} }
dispose() { dispose() {
@@ -162,27 +156,23 @@ export class SelectionManager extends LifeCycleWatcher {
this.disposables.dispose(); this.disposables.dispose();
} }
filter<T extends BlockSuite.SelectionType>(type: T) { filter<T extends SelectionConstructor>(type: T) {
return this.filter$(type).value; return this.filter$(type).value;
} }
filter$<T extends BlockSuite.SelectionType>(type: T) { filter$<T extends SelectionConstructor>(type: T) {
return computed(() => return computed(() =>
this.value.filter((sel): sel is BlockSuite.SelectionInstance[T] => this.value.filter((sel): sel is InstanceType<T> => sel.is(type))
sel.is(type)
)
); );
} }
find<T extends BlockSuite.SelectionType>(type: T) { find<T extends SelectionConstructor>(type: T) {
return this.find$(type).value; return this.find$(type).value;
} }
find$<T extends BlockSuite.SelectionType>(type: T) { find$<T extends SelectionConstructor>(type: T) {
return computed(() => return computed(() =>
this.value.find((sel): sel is BlockSuite.SelectionInstance[T] => this.value.find((sel): sel is InstanceType<T> => sel.is(type))
sel.is(type)
)
); );
} }
@@ -1,5 +1,4 @@
import type { TextSelection } from '@blocksuite/block-std'; import { ShadowlessElement, TextSelection } from '@blocksuite/block-std';
import { ShadowlessElement } from '@blocksuite/block-std';
import type { RichText } from '@blocksuite/blocks'; import type { RichText } from '@blocksuite/blocks';
import { WithDisposable } from '@blocksuite/global/utils'; import { WithDisposable } from '@blocksuite/global/utils';
import { css, html, nothing } from 'lit'; import { css, html, nothing } from 'lit';
@@ -69,7 +68,7 @@ export class CommentInput extends WithDisposable(ShadowlessElement) {
} }
override render() { override render() {
const textSelection = this.host.selection.find('text'); const textSelection = this.host.selection.find(TextSelection);
if (!textSelection) { if (!textSelection) {
this.remove(); this.remove();
return nothing; return nothing;
@@ -1,4 +1,4 @@
import { ShadowlessElement } from '@blocksuite/block-std'; import { ShadowlessElement, TextSelection } from '@blocksuite/block-std';
import { WithDisposable } from '@blocksuite/global/utils'; import { WithDisposable } from '@blocksuite/global/utils';
import { css, html } from 'lit'; import { css, html } from 'lit';
import { property, query } from 'lit/decorators.js'; import { property, query } from 'lit/decorators.js';
@@ -60,7 +60,7 @@ export class CommentPanel extends WithDisposable(ShadowlessElement) {
commentManager: CommentManager | null = null; commentManager: CommentManager | null = null;
private _addComment() { private _addComment() {
const textSelection = this.editor.host?.selection.find('text'); const textSelection = this.editor.host?.selection.find(TextSelection);
if (!textSelection) return; if (!textSelection) return;
const commentInput = new CommentInput(); const commentInput = new CommentInput();
+9 -5
View File
@@ -11,7 +11,11 @@ import {
DEFAULT_NOTE_WIDTH, DEFAULT_NOTE_WIDTH,
DefaultTheme, DefaultTheme,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import type { BlockComponent, EditorHost } from '@blocksuite/block-std'; import type {
BlockComponent,
EditorHost,
TextSelection,
} from '@blocksuite/block-std';
import { BLOCK_ID_ATTR } from '@blocksuite/block-std'; import { BLOCK_ID_ATTR } from '@blocksuite/block-std';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
import type { InlineRootElement } from '@inline/inline-editor.js'; import type { InlineRootElement } from '@inline/inline-editor.js';
@@ -1180,7 +1184,7 @@ export async function assertBlockSelections(page: Page, paths: string[]) {
if (!host) { if (!host) {
throw new Error('editor-host host not found'); throw new Error('editor-host host not found');
} }
return host.selection.filter('block'); return host.selection.value.filter(b => b.type === 'block');
}); });
const actualPaths = selections.map(selection => selection.blockId); const actualPaths = selections.map(selection => selection.blockId);
expect(actualPaths).toEqual(paths); expect(actualPaths).toEqual(paths);
@@ -1199,13 +1203,13 @@ export async function assertTextSelection(
length: number; length: number;
} }
) { ) {
const selection = await page.evaluate(() => { const selection = (await page.evaluate(() => {
const host = document.querySelector<EditorHost>('editor-host'); const host = document.querySelector<EditorHost>('editor-host');
if (!host) { if (!host) {
throw new Error('editor-host host not found'); throw new Error('editor-host host not found');
} }
return host.selection.find('text'); return host.selection.value.find(b => b.type === 'text');
}); })) as TextSelection | undefined;
if (!from && !to) { if (!from && !to) {
expect(selection).toBeUndefined(); expect(selection).toBeUndefined();
@@ -1,7 +1,7 @@
import { WorkspaceImpl } from '@affine/core/modules/workspace/impls/workspace'; import { WorkspaceImpl } from '@affine/core/modules/workspace/impls/workspace';
import type { import {
EditorHost, type EditorHost,
TextRangePoint, type TextRangePoint,
TextSelection, TextSelection,
} from '@blocksuite/affine/block-std'; } from '@blocksuite/affine/block-std';
import { import {
@@ -68,7 +68,7 @@ function processSnapshot(
*/ */
function processTextInSnapshot(snapshot: SliceSnapshot, host: EditorHost) { function processTextInSnapshot(snapshot: SliceSnapshot, host: EditorHost) {
const { content } = snapshot; const { content } = snapshot;
const text = host.selection.find('text'); const text = host.selection.find(TextSelection);
if (!content.length || !text) return; if (!content.length || !text) return;
content.forEach(snapshot => processSnapshot(snapshot, text, host)); content.forEach(snapshot => processSnapshot(snapshot, text, host));
@@ -1,7 +1,7 @@
import { ChatHistoryOrder } from '@affine/graphql'; import { ChatHistoryOrder } from '@affine/graphql';
import type { import {
BlockSelection, BlockSelection,
EditorHost, type EditorHost,
TextSelection, TextSelection,
} from '@blocksuite/affine/block-std'; } from '@blocksuite/affine/block-std';
import type { import type {
@@ -198,8 +198,8 @@ const REPLACE_SELECTION = {
icon: ReplaceIcon, icon: ReplaceIcon,
title: 'Replace selection', title: 'Replace selection',
showWhen: (host: EditorHost) => { showWhen: (host: EditorHost) => {
const textSelection = host.selection.find('text'); const textSelection = host.selection.find(TextSelection);
const blockSelections = host.selection.filter('block'); const blockSelections = host.selection.filter(BlockSelection);
if ( if (
(!textSelection || textSelection.from.length === 0) && (!textSelection || textSelection.from.length === 0) &&
blockSelections?.length === 0 blockSelections?.length === 0
@@ -1,4 +1,8 @@
import type { EditorHost } from '@blocksuite/affine/block-std'; import {
BlockSelection,
type EditorHost,
TextSelection,
} from '@blocksuite/affine/block-std';
import { import {
type AffineAIPanelWidgetConfig, type AffineAIPanelWidgetConfig,
type AIItemGroupConfig, type AIItemGroupConfig,
@@ -92,8 +96,8 @@ export class AskAIToolbarButton extends WithDisposable(LitElement) {
this._panelRoot.style.visibility = text ? 'hidden' : 'visible'; this._panelRoot.style.visibility = text ? 'hidden' : 'visible';
}; };
const textSelection = this.host.selection.find('text'); const textSelection = this.host.selection.find(TextSelection);
const blockSelections = this.host.selection.filter('block'); const blockSelections = this.host.selection.filter(BlockSelection);
let lastBlockId: string | undefined; let lastBlockId: string | undefined;
if (textSelection) { if (textSelection) {
lastBlockId = textSelection.to?.blockId ?? textSelection.blockId; lastBlockId = textSelection.to?.blockId ?? textSelection.blockId;
@@ -1,4 +1,4 @@
import type { EditorHost } from '@blocksuite/affine/block-std'; import { type EditorHost, TextSelection } from '@blocksuite/affine/block-std';
import type { import type {
AffineAIPanelWidget, AffineAIPanelWidget,
AffineAIPanelWidgetConfig, AffineAIPanelWidgetConfig,
@@ -209,7 +209,7 @@ export function actionToHandler<T extends keyof BlockSuitePresets.AIActions>(
export function handleInlineAskAIAction(host: EditorHost) { export function handleInlineAskAIAction(host: EditorHost) {
const panel = getAIPanelWidget(host); const panel = getAIPanelWidget(host);
const selection = host.selection.find('text'); const selection = host.selection.find(TextSelection);
const lastBlockPath = selection const lastBlockPath = selection
? (selection.to?.blockId ?? selection.blockId) ? (selection.to?.blockId ?? selection.blockId)
: null; : null;
@@ -1,4 +1,4 @@
import type { EditorHost } from '@blocksuite/affine/block-std'; import { type EditorHost, TextSelection } from '@blocksuite/affine/block-std';
import { import {
GfxBlockElementModel, GfxBlockElementModel,
type GfxModel, type GfxModel,
@@ -214,7 +214,7 @@ async function insertMarkdownAbove(host: EditorHost) {
} }
function getSelection(host: EditorHost) { function getSelection(host: EditorHost) {
const textSelection = host.selection.find('text'); const textSelection = host.selection.find(TextSelection);
const mode = textSelection ? 'flat' : 'highest'; const mode = textSelection ? 'flat' : 'highest';
const { selectedBlocks } = getSelections(host, mode); const { selectedBlocks } = getSelections(host, mode);
if (!selectedBlocks) return; if (!selectedBlocks) return;
@@ -9,6 +9,8 @@ const buttonOptions: AskAIButtonOptions = {
panelWidth: 240, panelWidth: 240,
}; };
import { BlockSelection } from '@blocksuite/affine/block-std';
import type { AskAIButtonOptions } from '../../_common/components/ask-ai-button'; import type { AskAIButtonOptions } from '../../_common/components/ask-ai-button';
import { buildAICodeItemGroups } from '../../_common/config'; import { buildAICodeItemGroups } from '../../_common/config';
@@ -22,7 +24,9 @@ export function setupCodeToolbarAIEntry(codeToolbar: AffineCodeToolbarWidget) {
action: () => { action: () => {
const { selection } = host; const { selection } = host;
selection.setGroup('note', [ selection.setGroup('note', [
selection.create('block', { blockId: blockComponent.blockId }), selection.create(BlockSelection, {
blockId: blockComponent.blockId,
}),
]); ]);
}, },
render: item => render: item =>
@@ -1,6 +1,9 @@
import '../../_common/components/ask-ai-button'; import '../../_common/components/ask-ai-button';
import type { AffineImageToolbarWidget } from '@blocksuite/affine/blocks'; import {
type AffineImageToolbarWidget,
ImageSelection,
} from '@blocksuite/affine/blocks';
import { html } from 'lit'; import { html } from 'lit';
import type { AskAIButtonOptions } from '../../_common/components/ask-ai-button'; import type { AskAIButtonOptions } from '../../_common/components/ask-ai-button';
@@ -26,7 +29,9 @@ export function setupImageToolbarAIEntry(
action: () => { action: () => {
const { selection } = host; const { selection } = host;
selection.setGroup('note', [ selection.setGroup('note', [
selection.create('image', { blockId: blockComponent.blockId }), selection.create(ImageSelection, {
blockId: blockComponent.blockId,
}),
]); ]);
}, },
render: item => render: item =>

Some files were not shown because too many files have changed in this diff Show More