js-draw
Version:
Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.
101 lines (100 loc) • 3.53 kB
JavaScript
import describeComponentList from '../components/util/describeComponentList.mjs';
import { assertIsStringArray } from '../util/assertions.mjs';
import Erase from './Erase.mjs';
import SerializableCommand from './SerializableCommand.mjs';
/**
* A command that duplicates the {@link AbstractComponent}s it's given. This command
* is the reverse of an {@link Erase} command.
*
* @example
* ```ts
* // Given some editor...
*
* // Find all elements intersecting the rectangle with top left (0,0) and
* // (width,height)=(100,100).
* const elems = editor.image.getComponentsIntersecting(
* new Rect2(0, 0, 100, 100)
* );
*
* // Create a command that, when applied, will duplicate the elements.
* const duplicateElems = new Duplicate(elems);
*
* // Apply the command (and make it undoable)
* editor.dispatch(duplicateElems);
* ```
*
* @see {@link Editor.dispatch} {@link EditorImage.getComponentsIntersecting}
*/
class Duplicate extends SerializableCommand {
constructor(toDuplicate,
// @internal -- IDs given to the duplicate elements
idsForDuplicates) {
super('duplicate');
this.toDuplicate = toDuplicate;
this.duplicates = toDuplicate.map((elem, idx) => {
// For collaborative editing, it's important for the clones to have
// the same IDs as the originals
if (idsForDuplicates && idsForDuplicates[idx]) {
return elem.cloneWithId(idsForDuplicates[idx]);
}
else {
return elem.clone();
}
});
this.reverse = new Erase(this.duplicates);
}
apply(editor) {
this.reverse.unapply(editor);
}
unapply(editor) {
this.reverse.apply(editor);
}
onDrop(editor) {
this.reverse.onDrop(editor);
}
description(_editor, localizationTable) {
if (this.duplicates.length === 0) {
return localizationTable.duplicatedNoElements;
}
return localizationTable.duplicateAction(describeComponentList(localizationTable, this.duplicates) ?? localizationTable.elements, this.duplicates.length);
}
serializeToJSON() {
return {
originalIds: this.toDuplicate.map((elem) => elem.getId()),
cloneIds: this.duplicates.map((elem) => elem.getId()),
};
}
}
(() => {
SerializableCommand.register('duplicate', (json, editor) => {
let originalIds;
let cloneIds;
// Compatibility with older editors
if (Array.isArray(json)) {
originalIds = json;
cloneIds = [];
}
else {
originalIds = json.originalIds;
cloneIds = json.cloneIds;
}
assertIsStringArray(originalIds);
assertIsStringArray(cloneIds);
// Resolve to elements -- only keep the elements that can be found in the image.
const resolvedElements = [];
const filteredCloneIds = [];
for (let i = 0; i < originalIds.length; i++) {
const originalId = originalIds[i];
const foundElement = editor.image.lookupElement(originalId);
if (!foundElement) {
console.warn('Duplicate command: Could not find element with ID', originalId);
}
else {
filteredCloneIds.push(cloneIds[i]);
resolvedElements.push(foundElement);
}
}
return new Duplicate(resolvedElements, filteredCloneIds);
});
})();
export default Duplicate;