mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-04 19:15:33 +08:00
@@ -0,0 +1,33 @@
|
||||
import { BaseSelection, SelectionExtension } from '@blocksuite/store';
|
||||
import z from 'zod';
|
||||
|
||||
const BlockSelectionSchema = z.object({
|
||||
blockId: z.string(),
|
||||
});
|
||||
|
||||
export class BlockSelection extends BaseSelection {
|
||||
static override group = 'note';
|
||||
|
||||
static override type = 'block';
|
||||
|
||||
static override fromJSON(json: Record<string, unknown>): BlockSelection {
|
||||
const result = BlockSelectionSchema.parse(json);
|
||||
return new BlockSelection(result);
|
||||
}
|
||||
|
||||
override equals(other: BaseSelection): boolean {
|
||||
if (other instanceof BlockSelection) {
|
||||
return this.blockId === other.blockId;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
override toJSON(): Record<string, unknown> {
|
||||
return {
|
||||
type: 'block',
|
||||
blockId: this.blockId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const BlockSelectionExtension = SelectionExtension(BlockSelection);
|
||||
@@ -0,0 +1,45 @@
|
||||
import { BaseSelection, SelectionExtension } from '@blocksuite/store';
|
||||
import z from 'zod';
|
||||
|
||||
const CursorSelectionSchema = z.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
});
|
||||
|
||||
export class CursorSelection extends BaseSelection {
|
||||
static override group = 'gfx';
|
||||
|
||||
static override type = 'cursor';
|
||||
|
||||
readonly x: number;
|
||||
|
||||
readonly y: number;
|
||||
|
||||
constructor(x: number, y: number) {
|
||||
super({ blockId: '[gfx-cursor]' });
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
static override fromJSON(json: Record<string, unknown>): CursorSelection {
|
||||
const { x, y } = CursorSelectionSchema.parse(json);
|
||||
return new CursorSelection(x, y);
|
||||
}
|
||||
|
||||
override equals(other: BaseSelection): boolean {
|
||||
if (other instanceof CursorSelection) {
|
||||
return this.x === other.x && this.y === other.y;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
override toJSON(): Record<string, unknown> {
|
||||
return {
|
||||
type: 'cursor',
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const CursorSelectionExtension = SelectionExtension(CursorSelection);
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './block.js';
|
||||
export * from './cursor.js';
|
||||
export * from './surface.js';
|
||||
export * from './text.js';
|
||||
@@ -0,0 +1,70 @@
|
||||
import { BaseSelection, SelectionExtension } from '@blocksuite/store';
|
||||
import z from 'zod';
|
||||
|
||||
const SurfaceSelectionSchema = z.object({
|
||||
blockId: z.string(),
|
||||
elements: z.array(z.string()),
|
||||
editing: z.boolean(),
|
||||
inoperable: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export class SurfaceSelection extends BaseSelection {
|
||||
static override group = 'gfx';
|
||||
|
||||
static override type = 'surface';
|
||||
|
||||
readonly editing: boolean;
|
||||
|
||||
readonly elements: string[];
|
||||
|
||||
readonly inoperable: boolean;
|
||||
|
||||
constructor(
|
||||
blockId: string,
|
||||
elements: string[],
|
||||
editing: boolean,
|
||||
inoperable = false
|
||||
) {
|
||||
super({ blockId });
|
||||
|
||||
this.elements = elements;
|
||||
this.editing = editing;
|
||||
this.inoperable = inoperable;
|
||||
}
|
||||
|
||||
static override fromJSON(json: Record<string, unknown>): SurfaceSelection {
|
||||
const { blockId, elements, editing, inoperable } =
|
||||
SurfaceSelectionSchema.parse(json);
|
||||
return new SurfaceSelection(blockId, elements, editing, inoperable);
|
||||
}
|
||||
|
||||
override equals(other: BaseSelection): boolean {
|
||||
if (other instanceof SurfaceSelection) {
|
||||
return (
|
||||
this.blockId === other.blockId &&
|
||||
this.editing === other.editing &&
|
||||
this.inoperable === other.inoperable &&
|
||||
this.elements.length === other.elements.length &&
|
||||
this.elements.every((id, idx) => id === other.elements[idx])
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return this.elements.length === 0 && !this.editing;
|
||||
}
|
||||
|
||||
override toJSON(): Record<string, unknown> {
|
||||
return {
|
||||
type: 'surface',
|
||||
blockId: this.blockId,
|
||||
elements: this.elements,
|
||||
editing: this.editing,
|
||||
inoperable: this.inoperable,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const SurfaceSelectionExtension = SelectionExtension(SurfaceSelection);
|
||||
@@ -0,0 +1,115 @@
|
||||
import { BaseSelection, SelectionExtension } from '@blocksuite/store';
|
||||
import z from 'zod';
|
||||
|
||||
export type TextRangePoint = {
|
||||
blockId: string;
|
||||
index: number;
|
||||
length: number;
|
||||
};
|
||||
|
||||
export type TextSelectionProps = {
|
||||
from: TextRangePoint;
|
||||
to: TextRangePoint | null;
|
||||
reverse?: boolean;
|
||||
};
|
||||
|
||||
const TextSelectionSchema = z.object({
|
||||
from: z.object({
|
||||
blockId: z.string(),
|
||||
index: z.number(),
|
||||
length: z.number(),
|
||||
}),
|
||||
to: z
|
||||
.object({
|
||||
blockId: z.string(),
|
||||
index: z.number(),
|
||||
length: z.number(),
|
||||
})
|
||||
.nullable(),
|
||||
// The `optional()` is for backward compatibility,
|
||||
// since `reverse` may not exist in remote selection.
|
||||
reverse: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export class TextSelection extends BaseSelection {
|
||||
static override group = 'note';
|
||||
|
||||
static override type = 'text';
|
||||
|
||||
from: TextRangePoint;
|
||||
|
||||
reverse: boolean;
|
||||
|
||||
to: TextRangePoint | null;
|
||||
|
||||
get end(): TextRangePoint {
|
||||
return this.reverse ? this.from : (this.to ?? this.from);
|
||||
}
|
||||
|
||||
get start(): TextRangePoint {
|
||||
return this.reverse ? (this.to ?? this.from) : this.from;
|
||||
}
|
||||
|
||||
constructor({ from, to, reverse }: TextSelectionProps) {
|
||||
super({
|
||||
blockId: from.blockId,
|
||||
});
|
||||
this.from = from;
|
||||
|
||||
this.to = this._equalPoint(from, to) ? null : to;
|
||||
|
||||
this.reverse = !!reverse;
|
||||
}
|
||||
|
||||
static override fromJSON(json: Record<string, unknown>): TextSelection {
|
||||
const result = TextSelectionSchema.parse(json);
|
||||
return new TextSelection(result);
|
||||
}
|
||||
|
||||
private _equalPoint(
|
||||
a: TextRangePoint | null,
|
||||
b: TextRangePoint | null
|
||||
): boolean {
|
||||
if (a && b) {
|
||||
return (
|
||||
a.blockId === b.blockId && a.index === b.index && a.length === b.length
|
||||
);
|
||||
}
|
||||
|
||||
return a === b;
|
||||
}
|
||||
|
||||
empty(): boolean {
|
||||
return !!this.to;
|
||||
}
|
||||
|
||||
override equals(other: BaseSelection): boolean {
|
||||
if (other instanceof TextSelection) {
|
||||
return (
|
||||
this.blockId === other.blockId &&
|
||||
this._equalPoint(other.from, this.from) &&
|
||||
this._equalPoint(other.to, this.to)
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isCollapsed(): boolean {
|
||||
return this.to === null && this.from.length === 0;
|
||||
}
|
||||
|
||||
isInSameBlock(): boolean {
|
||||
return this.to === null || this.from.blockId === this.to.blockId;
|
||||
}
|
||||
|
||||
override toJSON(): Record<string, unknown> {
|
||||
return {
|
||||
type: 'text',
|
||||
from: this.from,
|
||||
to: this.to,
|
||||
reverse: this.reverse,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const TextSelectionExtension = SelectionExtension(TextSelection);
|
||||
Reference in New Issue
Block a user