Skip to content

Commit daf8d98

Browse files
committed
feat: introduce location based apis
1 parent 0b03c0c commit daf8d98

File tree

4 files changed

+51
-49
lines changed

4 files changed

+51
-49
lines changed

packages/core/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,23 @@ import type { Node } from "prosemirror-model";
22
import type { Transaction } from "prosemirror-state";
33
import type { Block, PartialBlock } from "../../../../blocks/defaultBlocks.js";
44
import type {
5-
BlockIdentifier,
65
BlockSchema,
76
InlineContentSchema,
87
StyleSchema,
98
} from "../../../../schema/index.js";
109
import { blockToNode } from "../../../nodeConversions/blockToNode.js";
1110
import { nodeToBlock } from "../../../nodeConversions/nodeToBlock.js";
1211
import { getPmSchema } from "../../../pmUtil.js";
12+
import { Location } from "../../../../locations/types.js";
13+
import { getBlockRange } from "../../../../locations/utils.js";
1314

1415
export function removeAndInsertBlocks<
1516
BSchema extends BlockSchema,
1617
I extends InlineContentSchema,
1718
S extends StyleSchema,
1819
>(
1920
tr: Transaction,
20-
blocksToRemove: BlockIdentifier[],
21+
blocksToRemove: Location[],
2122
blocksToInsert: PartialBlock<BSchema, I, S>[],
2223
): {
2324
insertedBlocks: Block<BSchema, I, S>[];
@@ -30,17 +31,12 @@ export function removeAndInsertBlocks<
3031
blockToNode(block, pmSchema),
3132
);
3233

34+
const [insertionBlockId] = getBlockRange(blocksToRemove[0]);
3335
const idsOfBlocksToRemove = new Set<string>(
34-
blocksToRemove.map((block) =>
35-
typeof block === "string" ? block : block.id,
36-
),
36+
blocksToRemove.flatMap((block) => getBlockRange(block)),
3737
);
3838
const removedBlocks: Block<BSchema, I, S>[] = [];
3939

40-
const idOfFirstBlock =
41-
typeof blocksToRemove[0] === "string"
42-
? blocksToRemove[0]
43-
: blocksToRemove[0].id;
4440
let removedSize = 0;
4541

4642
tr.doc.descendants((node, pos) => {
@@ -61,7 +57,7 @@ export function removeAndInsertBlocks<
6157
removedBlocks.push(nodeToBlock(node, pmSchema));
6258
idsOfBlocksToRemove.delete(node.attrs.id);
6359

64-
if (blocksToInsert.length > 0 && node.attrs.id === idOfFirstBlock) {
60+
if (blocksToInsert.length > 0 && node.attrs.id === insertionBlockId) {
6561
const oldDocSize = tr.doc.nodeSize;
6662
tr.insert(pos, nodesToInsert);
6763
const newDocSize = tr.doc.nodeSize;

packages/core/src/api/blockManipulation/commands/updateBlock/updateBlock.ts

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,23 @@ import { TableMap } from "prosemirror-tables";
99
import { ReplaceStep, Transform } from "prosemirror-transform";
1010

1111
import type { Block, PartialBlock } from "../../../../blocks/defaultBlocks.js";
12-
import type {
13-
BlockIdentifier,
14-
BlockSchema,
15-
} from "../../../../schema/blocks/types.js";
12+
import { getBlockStartPos } from "../../../../locations/location.js";
13+
import { Location } from "../../../../locations/types.js";
14+
import type { BlockSchema } from "../../../../schema/blocks/types.js";
1615
import type { InlineContentSchema } from "../../../../schema/inlineContent/types.js";
1716
import type { StyleSchema } from "../../../../schema/styles/types.js";
1817
import { UnreachableCaseError } from "../../../../util/typescript.js";
1918
import {
2019
type BlockInfo,
2120
getBlockInfoFromResolvedPos,
21+
getNearestBlockPos,
2222
} from "../../../getBlockInfoFromPos.js";
2323
import {
2424
blockToNode,
2525
inlineContentToNodes,
2626
tableContentToNodes,
2727
} from "../../../nodeConversions/blockToNode.js";
2828
import { nodeToBlock } from "../../../nodeConversions/nodeToBlock.js";
29-
import { getNodeById } from "../../../nodeUtil.js";
3029
import { getPmSchema } from "../../../pmUtil.js";
3130

3231
// for compatibility with tiptap. TODO: remove as we want to remove dependency on tiptap command interface
@@ -127,7 +126,7 @@ export function updateBlockTr<
127126
// currently, we calculate the new node and replace the entire node with the desired new node.
128127
// for this, we do a nodeToBlock on the existing block to get the children.
129128
// it would be cleaner to use a ReplaceAroundStep, but this is a bit simpler and it's quite an edge case
130-
const existingBlock = nodeToBlock(blockInfo.bnBlock.node, pmSchema);
129+
const existingBlock = nodeToBlock(blockInfo.bnBlock.node);
131130
tr.replaceWith(
132131
blockInfo.bnBlock.beforePos,
133132
blockInfo.bnBlock.afterPos,
@@ -312,32 +311,18 @@ export function updateBlock<
312311
S extends StyleSchema = any,
313312
>(
314313
tr: Transform,
315-
blockToUpdate: BlockIdentifier,
314+
blockToUpdate: Location,
316315
update: PartialBlock<BSchema, I, S>,
317316
replaceFromPos?: number,
318317
replaceToPos?: number,
319318
): Block<BSchema, I, S> {
320-
const id =
321-
typeof blockToUpdate === "string" ? blockToUpdate : blockToUpdate.id;
322-
const posInfo = getNodeById(id, tr.doc);
323-
if (!posInfo) {
324-
throw new Error(`Block with ID ${id} not found`);
325-
}
319+
const blockStartPos = getBlockStartPos(tr.doc, blockToUpdate);
326320

327-
updateBlockTr(
328-
tr,
329-
posInfo.posBeforeNode,
330-
update,
331-
replaceFromPos,
332-
replaceToPos,
333-
);
321+
updateBlockTr(tr, blockStartPos, update, replaceFromPos, replaceToPos);
334322

335-
const blockContainerNode = tr.doc
336-
.resolve(posInfo.posBeforeNode + 1) // TODO: clean?
337-
.node();
323+
const { node } = getNearestBlockPos(tr.doc, blockStartPos);
338324

339-
const pmSchema = getPmSchema(tr);
340-
return nodeToBlock(blockContainerNode, pmSchema);
325+
return nodeToBlock(node);
341326
}
342327

343328
type CellAnchor = { row: number; col: number; offset: number };

packages/core/src/editor/managers/BlockManager.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ import {
2525
DefaultStyleSchema,
2626
PartialBlock,
2727
} from "../../blocks/defaultBlocks.js";
28-
import {
29-
BlockIdentifier,
28+
import { Location } from "../../locations/types.js";
29+
import { getBlockId } from "../../locations/utils.js";
30+
import type {
3031
BlockSchema,
3132
InlineContentSchema,
3233
StyleSchema,
@@ -58,9 +59,11 @@ export class BlockManager<
5859
* matching block was found.
5960
*/
6061
public getBlock(
61-
blockIdentifier: BlockIdentifier,
62+
blockIdentifier: Location,
6263
): Block<BSchema, ISchema, SSchema> | undefined {
63-
return this.editor.transact((tr) => getBlock(tr.doc, blockIdentifier));
64+
return this.editor.transact((tr) =>
65+
getBlock(tr.doc, getBlockId(blockIdentifier)),
66+
);
6467
}
6568

6669
/**
@@ -73,9 +76,11 @@ export class BlockManager<
7376
* in the document.
7477
*/
7578
public getPrevBlock(
76-
blockIdentifier: BlockIdentifier,
79+
blockIdentifier: Location,
7780
): Block<BSchema, ISchema, SSchema> | undefined {
78-
return this.editor.transact((tr) => getPrevBlock(tr.doc, blockIdentifier));
81+
return this.editor.transact((tr) =>
82+
getPrevBlock(tr.doc, getBlockId(blockIdentifier)),
83+
);
7984
}
8085

8186
/**
@@ -87,9 +92,11 @@ export class BlockManager<
8792
* the document.
8893
*/
8994
public getNextBlock(
90-
blockIdentifier: BlockIdentifier,
95+
blockIdentifier: Location,
9196
): Block<BSchema, ISchema, SSchema> | undefined {
92-
return this.editor.transact((tr) => getNextBlock(tr.doc, blockIdentifier));
97+
return this.editor.transact((tr) =>
98+
getNextBlock(tr.doc, getBlockId(blockIdentifier)),
99+
);
93100
}
94101

95102
/**
@@ -100,10 +107,10 @@ export class BlockManager<
100107
* if no matching block was found, or the block isn't nested.
101108
*/
102109
public getParentBlock(
103-
blockIdentifier: BlockIdentifier,
110+
blockIdentifier: Location,
104111
): Block<BSchema, ISchema, SSchema> | undefined {
105112
return this.editor.transact((tr) =>
106-
getParentBlock(tr.doc, blockIdentifier),
113+
getParentBlock(tr.doc, getBlockId(blockIdentifier)),
107114
);
108115
}
109116

@@ -155,7 +162,7 @@ export class BlockManager<
155162
*/
156163
public insertBlocks(
157164
blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],
158-
referenceBlock: BlockIdentifier,
165+
referenceBlock: Location,
159166
placement: "before" | "after" = "before",
160167
) {
161168
return this.editor.transact((tr) =>
@@ -171,7 +178,7 @@ export class BlockManager<
171178
* @param update A partial block which defines how the existing block should be changed.
172179
*/
173180
public updateBlock(
174-
blockToUpdate: BlockIdentifier,
181+
blockToUpdate: Location,
175182
update: PartialBlock<BSchema, ISchema, SSchema>,
176183
) {
177184
return this.editor.transact((tr) => updateBlock(tr, blockToUpdate, update));
@@ -181,7 +188,7 @@ export class BlockManager<
181188
* Removes existing blocks from the editor. Throws an error if any of the blocks could not be found.
182189
* @param blocksToRemove An array of identifiers for existing blocks that should be removed.
183190
*/
184-
public removeBlocks(blocksToRemove: BlockIdentifier[]) {
191+
public removeBlocks(blocksToRemove: Location[]) {
185192
return this.editor.transact(
186193
(tr) => removeAndInsertBlocks(tr, blocksToRemove, []).removedBlocks,
187194
);
@@ -195,7 +202,7 @@ export class BlockManager<
195202
* @param blocksToInsert An array of partial blocks to replace the old ones with.
196203
*/
197204
public replaceBlocks(
198-
blocksToRemove: BlockIdentifier[],
205+
blocksToRemove: Location[],
199206
blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],
200207
) {
201208
return this.editor.transact((tr) =>

packages/core/src/locations/utils.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,17 @@ export function isLocationEqual(
161161
range1.head.offset === range2.head.offset
162162
);
163163
}
164+
165+
/**
166+
* Gets the block id from a location.
167+
* @throws if the location is not a single block.
168+
*/
169+
export function getBlockId(location: Location): BlockId {
170+
const range = normalizeToRange(location);
171+
if (range.anchor.id !== range.head.id) {
172+
throw new Error("Invalid location, must be a single block", {
173+
cause: { location },
174+
});
175+
}
176+
return range.anchor.id;
177+
}

0 commit comments

Comments
 (0)