| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- import Command from './command'
- import history from '../history'
- import { Data, GroupShape, ShapeType } from 'types'
- import {
- getCommonBounds,
- getPage,
- getSelectedIds,
- getSelectedShapes,
- getShape,
- setSelectedIds,
- } from 'utils/utils'
- import { current } from 'immer'
- import { createShape, getShapeUtils } from 'state/shape-utils'
- import commands from '.'
-
- export default function groupCommand(data: Data): void {
- const cData = current(data)
- const { currentPageId } = cData
-
- const oldSelectedIds = getSelectedIds(cData)
-
- const initialShapes = getSelectedShapes(cData).sort(
- (a, b) => a.childIndex - b.childIndex
- )
-
- const isAllSameParent = initialShapes.every(
- (shape, i) => i === 0 || shape.parentId === initialShapes[i - 1].parentId
- )
-
- let newGroupParentId: string
-
- const initialShapeIds = initialShapes.map((s) => s.id)
-
- const commonBounds = getCommonBounds(
- ...initialShapes.map((shape) =>
- getShapeUtils(shape).getRotatedBounds(shape)
- )
- )
-
- if (isAllSameParent) {
- const parentId = initialShapes[0].parentId
- if (parentId === currentPageId) {
- newGroupParentId = currentPageId
- } else {
- // Are all of the parent's children selected?
- const parent = getShape(data, parentId) as GroupShape
-
- if (parent.children.length === initialShapes.length) {
- // !!! Hey! We're not going any further. We need to ungroup those shapes.
- commands.ungroup(data)
- return
- } else {
- // Make the group inside of the current group
- newGroupParentId = parentId
- }
- }
- } else {
- // Find the least-deep parent among the shapes and add the group as a child
- let minDepth = Infinity
-
- for (const parentId of initialShapes.map((shape) => shape.parentId)) {
- const depth = getShapeDepth(data, parentId)
- if (depth < minDepth) {
- minDepth = depth
- newGroupParentId = parentId
- }
- }
- }
-
- const newGroupShape = createShape(ShapeType.Group, {
- parentId: newGroupParentId,
- point: [commonBounds.minX, commonBounds.minY],
- size: [commonBounds.width, commonBounds.height],
- children: initialShapeIds,
- childIndex: initialShapes[0].childIndex,
- })
-
- history.execute(
- data,
- new Command({
- name: 'group_shapes',
- category: 'canvas',
- manualSelection: true,
- do(data) {
- const { shapes } = getPage(data, currentPageId)
-
- // Create the new group
- shapes[newGroupShape.id] = newGroupShape
-
- // Assign the group to its new parent
- if (newGroupParentId !== data.currentPageId) {
- const parent = shapes[newGroupParentId]
- getShapeUtils(parent).setProperty(parent, 'children', [
- ...parent.children,
- newGroupShape.id,
- ])
- }
-
- // Assign the shapes to their new parent
- initialShapes.forEach((initialShape, i) => {
- // Remove shape from its old parent
- if (initialShape.parentId !== currentPageId) {
- const oldParent = shapes[initialShape.parentId] as GroupShape
- getShapeUtils(oldParent).setProperty(
- oldParent,
- 'children',
- oldParent.children.filter((id) => !oldSelectedIds.has(id))
- )
- }
-
- // Assign the shape to its new parent, with its new childIndex
- const shape = shapes[initialShape.id]
- getShapeUtils(shape)
- .setProperty(shape, 'childIndex', i)
- .setProperty(shape, 'parentId', newGroupShape.id)
- })
-
- setSelectedIds(data, [newGroupShape.id])
- },
- undo(data) {
- const { shapes } = getPage(data, currentPageId)
-
- const group = shapes[newGroupShape.id]
-
- // remove the group from its parent
- if (group.parentId !== data.currentPageId) {
- const parent = shapes[group.parentId]
- getShapeUtils(parent).setProperty(
- parent,
- 'children',
- parent.children.filter((id) => id !== newGroupShape.id)
- )
- }
-
- // Move the shapes back to their previous parent / childIndex
- initialShapes.forEach(({ id, parentId, childIndex }) => {
- const shape = shapes[id]
- getShapeUtils(shape)
- .setProperty(shape, 'parentId', parentId)
- .setProperty(shape, 'childIndex', childIndex)
-
- if (parentId !== data.currentPageId) {
- const parent = shapes[parentId]
- getShapeUtils(parent).setProperty(parent, 'children', [
- ...parent.children,
- id,
- ])
- }
- })
-
- // Delete the group
- delete shapes[newGroupShape.id]
-
- // Reselect the children of the group
- setSelectedIds(data, initialShapeIds)
- },
- })
- )
- }
-
- function getShapeDepth(data: Data, id: string, depth = 0) {
- if (id === data.currentPageId) {
- return depth
- }
-
- return getShapeDepth(data, getShape(data, id).parentId, depth + 1)
- }
|