mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 20:38:52 +00:00
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:
@@ -7,7 +7,12 @@ import {
|
||||
Slot,
|
||||
} 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 { GfxExtension, GfxExtensionIdentifier } from './extension.js';
|
||||
import type { GfxModel } from './model/model.js';
|
||||
@@ -215,9 +220,9 @@ export class GfxSelectionManager extends GfxExtension {
|
||||
this.disposable.add(
|
||||
this.stdSelection.slots.changed.on(selections => {
|
||||
const { cursor = [], surface = [] } = groupBy(selections, sel => {
|
||||
if (sel.is('surface')) {
|
||||
if (sel.is(SurfaceSelection)) {
|
||||
return 'surface';
|
||||
} else if (sel.is('cursor')) {
|
||||
} else if (sel.is(CursorSelection)) {
|
||||
return 'cursor';
|
||||
}
|
||||
|
||||
@@ -261,15 +266,15 @@ export class GfxSelectionManager extends GfxExtension {
|
||||
let hasBlockSelection = false;
|
||||
|
||||
selections.forEach(selection => {
|
||||
if (selection.is('text')) {
|
||||
if (selection.is(TextSelection)) {
|
||||
hasTextSelection = true;
|
||||
}
|
||||
|
||||
if (selection.is('block')) {
|
||||
if (selection.is(BlockSelection)) {
|
||||
hasBlockSelection = true;
|
||||
}
|
||||
|
||||
if (selection.is('surface')) {
|
||||
if (selection.is(SurfaceSelection)) {
|
||||
const surfaceSelections = surfaceMap.get(id) ?? [];
|
||||
surfaceSelections.push(selection);
|
||||
surfaceMap.set(id, surfaceSelections);
|
||||
@@ -277,7 +282,7 @@ export class GfxSelectionManager extends GfxExtension {
|
||||
selection.elements.forEach(id => selectedSet.add(id));
|
||||
}
|
||||
|
||||
if (selection.is('cursor')) {
|
||||
if (selection.is(CursorSelection)) {
|
||||
cursorMap.set(id, selection);
|
||||
}
|
||||
});
|
||||
@@ -318,7 +323,7 @@ export class GfxSelectionManager extends GfxExtension {
|
||||
if (elements.length > 0 && this.surfaceModel) {
|
||||
instances.push(
|
||||
this.stdSelection.create(
|
||||
'surface',
|
||||
SurfaceSelection,
|
||||
this.surfaceModel.id,
|
||||
elements,
|
||||
selection.editing ?? false,
|
||||
@@ -331,7 +336,7 @@ export class GfxSelectionManager extends GfxExtension {
|
||||
instances = instances.concat(
|
||||
blocks.map(blockId =>
|
||||
this.stdSelection.create(
|
||||
'surface',
|
||||
SurfaceSelection,
|
||||
blockId,
|
||||
[blockId],
|
||||
selection.editing ?? false,
|
||||
@@ -368,7 +373,11 @@ export class GfxSelectionManager extends GfxExtension {
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { InlineRange, InlineRangeProvider } from '@blocksuite/inline';
|
||||
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';
|
||||
|
||||
export const getInlineRangeProvider: (
|
||||
@@ -40,7 +40,7 @@ export const getInlineRangeProvider: (
|
||||
}
|
||||
|
||||
const elementRange = rangeManager.textSelectionToRange(
|
||||
selectionManager.create('text', {
|
||||
selectionManager.create(TextSelection, {
|
||||
from: {
|
||||
index: 0,
|
||||
blockId: element.blockId,
|
||||
@@ -72,7 +72,7 @@ export const getInlineRangeProvider: (
|
||||
if (!inlineRange) {
|
||||
selectionManager.clear(['text']);
|
||||
} else {
|
||||
const textSelection = selectionManager.create('text', {
|
||||
const textSelection = selectionManager.create(TextSelection, {
|
||||
from: {
|
||||
blockId: element.blockId,
|
||||
index: inlineRange.index,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { throttle } from '@blocksuite/global/utils';
|
||||
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 { BLOCK_ID_ATTR } from '../view/index.js';
|
||||
import { RANGE_SYNC_EXCLUDE_ATTR } from './consts.js';
|
||||
@@ -30,7 +30,7 @@ export class RangeBinding {
|
||||
};
|
||||
|
||||
private readonly _onBeforeInput = (event: InputEvent) => {
|
||||
const selection = this.selectionManager.find('text');
|
||||
const selection = this.selectionManager.find(TextSelection);
|
||||
if (!selection) 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: {
|
||||
blockId: from.blockId,
|
||||
index: from.index + (event.data?.length ?? 0),
|
||||
@@ -95,7 +95,7 @@ export class RangeBinding {
|
||||
};
|
||||
|
||||
private readonly _onCompositionStart = () => {
|
||||
const selection = this.selectionManager.find('text');
|
||||
const selection = this.selectionManager.find(TextSelection);
|
||||
if (!selection) return;
|
||||
|
||||
const { from, to } = selection;
|
||||
@@ -153,7 +153,7 @@ export class RangeBinding {
|
||||
|
||||
await this.host.updateComplete;
|
||||
|
||||
const selection = this.selectionManager.create('text', {
|
||||
const selection = this.selectionManager.create(TextSelection, {
|
||||
from: {
|
||||
blockId: from.blockId,
|
||||
index: from.index + (event.data?.length ?? 0),
|
||||
@@ -249,7 +249,7 @@ export class RangeBinding {
|
||||
private readonly _onStdSelectionChanged = (selections: BaseSelection[]) => {
|
||||
const text =
|
||||
selections.find((selection): selection is TextSelection =>
|
||||
selection.is('text')
|
||||
selection.is(TextSelection)
|
||||
) ?? null;
|
||||
|
||||
if (text === this._prevTextSelection) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INLINE_ROOT_ATTR, type InlineRootElement } from '@blocksuite/inline';
|
||||
|
||||
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 { BLOCK_ID_ATTR } from '../view/index.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 this.std.host.selection.create('text', {
|
||||
return this.std.host.selection.create(TextSelection, {
|
||||
from: {
|
||||
blockId: startBlock.blockId,
|
||||
index: startInlineRange.index,
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||
|
||||
type SelectionConstructor<T = unknown> = {
|
||||
type: string;
|
||||
group: string;
|
||||
new (...args: unknown[]): T;
|
||||
};
|
||||
import type { SelectionConstructor } from './manager';
|
||||
|
||||
export type BaseSelectionOptions = {
|
||||
blockId: string;
|
||||
@@ -21,9 +17,8 @@ export abstract class BaseSelection {
|
||||
return (this.constructor as SelectionConstructor).group;
|
||||
}
|
||||
|
||||
get type(): BlockSuite.SelectionType {
|
||||
return (this.constructor as SelectionConstructor)
|
||||
.type as BlockSuite.SelectionType;
|
||||
get type(): string {
|
||||
return (this.constructor as SelectionConstructor).type as string;
|
||||
}
|
||||
|
||||
constructor({ blockId }: BaseSelectionOptions) {
|
||||
@@ -39,10 +34,10 @@ export abstract class BaseSelection {
|
||||
|
||||
abstract equals(other: BaseSelection): boolean;
|
||||
|
||||
is<T extends BlockSuite.SelectionType>(
|
||||
is<T extends SelectionConstructor>(
|
||||
type: T
|
||||
): this is BlockSuite.SelectionInstance[T] {
|
||||
return this.type === type;
|
||||
): this is T extends SelectionConstructor<infer U> ? U : never {
|
||||
return this.type === type.type;
|
||||
}
|
||||
|
||||
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 './manager.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 { BaseSelection } from './base.js';
|
||||
|
||||
export interface SelectionConstructor {
|
||||
export interface SelectionConstructor<T extends BaseSelection = BaseSelection> {
|
||||
type: string;
|
||||
group: string;
|
||||
|
||||
new (...args: any[]): BaseSelection;
|
||||
fromJSON(json: Record<string, unknown>): BaseSelection;
|
||||
new (...args: any[]): T;
|
||||
fromJSON(json: Record<string, unknown>): T;
|
||||
}
|
||||
|
||||
export class SelectionManager extends LifeCycleWatcher {
|
||||
@@ -143,18 +144,11 @@ export class SelectionManager extends LifeCycleWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
create<T extends BlockSuite.SelectionType>(
|
||||
type: T,
|
||||
...args: ConstructorParameters<BlockSuite.Selection[T]>
|
||||
): BlockSuite.SelectionInstance[T] {
|
||||
const ctor = this._selectionConstructors[type];
|
||||
if (!ctor) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.SelectionError,
|
||||
`Unknown selection type: ${type}`
|
||||
);
|
||||
}
|
||||
return new ctor(...args) as BlockSuite.SelectionInstance[T];
|
||||
create<T extends SelectionConstructor>(
|
||||
Type: T,
|
||||
...args: ConstructorParameters<T>
|
||||
): InstanceType<T> {
|
||||
return new Type(...args) as InstanceType<T>;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -162,27 +156,23 @@ export class SelectionManager extends LifeCycleWatcher {
|
||||
this.disposables.dispose();
|
||||
}
|
||||
|
||||
filter<T extends BlockSuite.SelectionType>(type: T) {
|
||||
filter<T extends SelectionConstructor>(type: T) {
|
||||
return this.filter$(type).value;
|
||||
}
|
||||
|
||||
filter$<T extends BlockSuite.SelectionType>(type: T) {
|
||||
filter$<T extends SelectionConstructor>(type: T) {
|
||||
return computed(() =>
|
||||
this.value.filter((sel): sel is BlockSuite.SelectionInstance[T] =>
|
||||
sel.is(type)
|
||||
)
|
||||
this.value.filter((sel): sel is InstanceType<T> => sel.is(type))
|
||||
);
|
||||
}
|
||||
|
||||
find<T extends BlockSuite.SelectionType>(type: T) {
|
||||
find<T extends SelectionConstructor>(type: T) {
|
||||
return this.find$(type).value;
|
||||
}
|
||||
|
||||
find$<T extends BlockSuite.SelectionType>(type: T) {
|
||||
find$<T extends SelectionConstructor>(type: T) {
|
||||
return computed(() =>
|
||||
this.value.find((sel): sel is BlockSuite.SelectionInstance[T] =>
|
||||
sel.is(type)
|
||||
)
|
||||
this.value.find((sel): sel is InstanceType<T> => sel.is(type))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user