* Create shapes. * * @example * ```ts * editor.createShapes([myShape]) * editor.createShapes([{ id: 'box1', type: 'text', props: { richText: toRichText("ok") } }]) * ``` * * @param shapes - The shapes (or shape partials) to create. * * @public
(shapes: TLCreateShapePartial<TShape>[])
| 8488 | * @public |
| 8489 | */ |
| 8490 | createShapes<TShape extends TLShape = TLShape>(shapes: TLCreateShapePartial<TShape>[]): this { |
| 8491 | if (!Array.isArray(shapes)) { |
| 8492 | throw Error('Editor.createShapes: must provide an array of shapes or shape partials') |
| 8493 | } |
| 8494 | if (this.getIsReadonly()) return this |
| 8495 | if (shapes.length <= 0) return this |
| 8496 | |
| 8497 | const currentPageShapeIds = this.getCurrentPageShapeIds() |
| 8498 | |
| 8499 | const maxShapesReached = |
| 8500 | shapes.length + currentPageShapeIds.size > this.options.maxShapesPerPage |
| 8501 | |
| 8502 | if (maxShapesReached) { |
| 8503 | // can't create more shapes than fit on the page |
| 8504 | alertMaxShapes(this) |
| 8505 | // todo: throw an error here? Otherwise we'll need to check every time whether the shapes were actually created |
| 8506 | return this |
| 8507 | } |
| 8508 | |
| 8509 | const focusedGroupId = this.getFocusedGroupId() |
| 8510 | |
| 8511 | this.run(() => { |
| 8512 | // 1. Parents |
| 8513 | |
| 8514 | // Make sure that each partial will become the child of either the |
| 8515 | // page or another shape that exists (or that will exist) in this page. |
| 8516 | |
| 8517 | // find last parent id |
| 8518 | const currentPageShapesSorted = this.getCurrentPageShapesSorted() |
| 8519 | |
| 8520 | const partials = shapes.map((partial) => { |
| 8521 | if (!partial.id) { |
| 8522 | partial = { id: createShapeId(), ...partial } |
| 8523 | } |
| 8524 | |
| 8525 | // If the partial does not provide the parentId OR if the provided |
| 8526 | // parentId is NOT in the store AND NOT among the other shapes being |
| 8527 | // created, then we need to find a parent for the shape. This can be |
| 8528 | // another shape that exists under that point and which can receive |
| 8529 | // children of the creating shape's type, or else the page itself. |
| 8530 | if ( |
| 8531 | !partial.parentId || |
| 8532 | !(this.store.has(partial.parentId) || shapes.some((p) => p.id === partial.parentId)) |
| 8533 | ) { |
| 8534 | let parentId: TLParentId = this.getFocusedGroupId() |
| 8535 | |
| 8536 | const isPositioned = partial.x !== undefined && partial.y !== undefined |
| 8537 | |
| 8538 | // If the shape has been explicitly positioned, we'll try to find a parent at |
| 8539 | // that position. If not, we'll assume the user isn't deliberately placing the |
| 8540 | // shape and the positioning will be handled later by another system. |
| 8541 | if (isPositioned) { |
| 8542 | for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) { |
| 8543 | const parent = currentPageShapesSorted[i] |
| 8544 | const util = this.getShapeUtil(parent) |
| 8545 | if ( |
| 8546 | util.canReceiveNewChildrenOfType(parent, partial.type) && |
| 8547 | !this.isShapeHidden(parent) && |