improve typing for partial updates
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
|||||||
import type { Mutable } from "@excalidraw/common/utility-types";
|
import type { Mutable } from "@excalidraw/common/utility-types";
|
||||||
import type {
|
import type {
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
|
ExcalidrawNonSelectionElement,
|
||||||
OrderedExcalidrawElement,
|
OrderedExcalidrawElement,
|
||||||
SceneElementsMap,
|
SceneElementsMap,
|
||||||
} from "@excalidraw/element/types";
|
} from "@excalidraw/element/types";
|
||||||
@@ -800,9 +801,19 @@ export class TransactionLedger {
|
|||||||
export type TransactionStatus = "active" | "committed" | "canceled";
|
export type TransactionStatus = "active" | "committed" | "canceled";
|
||||||
|
|
||||||
/** Per-element partial patch used by tx.updateElements(). */
|
/** Per-element partial patch used by tx.updateElements(). */
|
||||||
export type TransactionElementUpdate = {
|
type TransactionUpdatableElementType = ExcalidrawNonSelectionElement["type"];
|
||||||
id: ExcalidrawElement["id"];
|
type TransactionElementOfType<TType extends TransactionUpdatableElementType> =
|
||||||
} & ElementUpdate<OrderedExcalidrawElement>;
|
Extract<ExcalidrawNonSelectionElement, { type: TType }>;
|
||||||
|
|
||||||
|
export type TransactionElementUpdate<
|
||||||
|
TType extends TransactionUpdatableElementType = TransactionUpdatableElementType,
|
||||||
|
> = TType extends TransactionUpdatableElementType
|
||||||
|
? {
|
||||||
|
id: ExcalidrawElement["id"];
|
||||||
|
type: TType;
|
||||||
|
updates: ElementUpdate<TransactionElementOfType<TType>>;
|
||||||
|
}
|
||||||
|
: never;
|
||||||
|
|
||||||
/** Final summary returned when a transaction is committed or canceled. */
|
/** Final summary returned when a transaction is committed or canceled. */
|
||||||
export type TransactionSummary = {
|
export type TransactionSummary = {
|
||||||
@@ -972,21 +983,20 @@ export class Transaction {
|
|||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* tx.updateElements({
|
* tx.updateElements({
|
||||||
* elements: [{ id: "a", strokeColor: "#f00" }, { id: "b", x: 10, y: 20 }],
|
* elements: [
|
||||||
|
* { id: "a", type: "rectangle", updates: { strokeColor: "#f00" } },
|
||||||
|
* { id: "b", type: "rectangle", updates: { x: 10, y: 20 } },
|
||||||
|
* ],
|
||||||
* })
|
* })
|
||||||
*/
|
*/
|
||||||
updateElements<K extends keyof AppState>(data: {
|
updateElements<K extends keyof AppState>(data: {
|
||||||
elements: readonly TransactionElementUpdate[];
|
elements: readonly TransactionElementUpdate[];
|
||||||
appState?: Pick<AppState, K> | null;
|
appState?: Pick<AppState, K> | null;
|
||||||
}): void {
|
}): void {
|
||||||
const updatesById = new Map<
|
const updatesById = new Map<string, TransactionElementUpdate>();
|
||||||
string,
|
|
||||||
ElementUpdate<OrderedExcalidrawElement>
|
|
||||||
>();
|
|
||||||
|
|
||||||
for (const update of data.elements) {
|
for (const update of data.elements) {
|
||||||
const { id, ...partialUpdate } = update;
|
updatesById.set(update.id, update);
|
||||||
updatesById.set(id, partialUpdate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updatesById.size === 0) {
|
if (updatesById.size === 0) {
|
||||||
@@ -997,8 +1007,21 @@ export class Transaction {
|
|||||||
const nextElements = this.app.scene
|
const nextElements = this.app.scene
|
||||||
.getElementsIncludingDeleted()
|
.getElementsIncludingDeleted()
|
||||||
.map((element) => {
|
.map((element) => {
|
||||||
const partialUpdate = updatesById.get(element.id);
|
const update = updatesById.get(element.id);
|
||||||
return partialUpdate ? newElementWith(element, partialUpdate) : element;
|
if (!update) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
if (element.type !== update.type) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot apply tx.updateElements update for "${update.id}": expected "${update.type}", got "${element.type}".`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type MatchingElement = TransactionElementOfType<typeof update.type>;
|
||||||
|
return newElementWith(
|
||||||
|
element as MatchingElement,
|
||||||
|
update.updates as ElementUpdate<MatchingElement>,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.updateScene({
|
this.updateScene({
|
||||||
|
|||||||
Reference in New Issue
Block a user