Issues/1827 group-ungroup icons (#1956)
* show group and ungroup action-icon * change group-icon visiblilty don't show group if selected is only a single element or a single group of elements Co-authored-by: rene_mbp <harryloveslearning@googlemail.com>
This commit is contained in:
parent
880cac2359
commit
ebf2923c5e
@ -1,7 +1,11 @@
|
||||
import React from "react";
|
||||
import { KEYS } from "../keys";
|
||||
import { t } from "../i18n";
|
||||
import { getShortcutKey } from "../utils";
|
||||
import { register } from "./register";
|
||||
import { group, ungroup } from "../components/icons";
|
||||
import { newElementWith } from "../element/mutateElement";
|
||||
import { getSelectedElements } from "../scene";
|
||||
import { getSelectedElements, isSomeElementSelected } from "../scene";
|
||||
import {
|
||||
getSelectedGroupIds,
|
||||
selectGroup,
|
||||
@ -13,6 +17,39 @@ import {
|
||||
} from "../groups";
|
||||
import { getNonDeletedElements } from "../element";
|
||||
import { randomId } from "../random";
|
||||
import { ToolButton } from "../components/ToolButton";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { AppState } from "../types";
|
||||
|
||||
const allElementsInSameGroup = (elements: readonly ExcalidrawElement[]) => {
|
||||
if (elements.length >= 2) {
|
||||
const groupIds = elements[0].groupIds;
|
||||
for (const groupId of groupIds) {
|
||||
if (
|
||||
elements.reduce(
|
||||
(acc, element) => acc && isElementInGroup(element, groupId),
|
||||
true,
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const enableActionGroup = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
) => {
|
||||
const selectedElements = getSelectedElements(
|
||||
getNonDeletedElements(elements),
|
||||
appState,
|
||||
);
|
||||
return (
|
||||
selectedElements.length >= 2 && !allElementsInSameGroup(selectedElements)
|
||||
);
|
||||
};
|
||||
|
||||
export const actionGroup = register({
|
||||
name: "group",
|
||||
@ -91,7 +128,7 @@ export const actionGroup = register({
|
||||
contextMenuOrder: 4,
|
||||
contextItemLabel: "labels.group",
|
||||
contextItemPredicate: (elements, appState) =>
|
||||
getSelectedElements(getNonDeletedElements(elements), appState).length > 1,
|
||||
enableActionGroup(elements, appState),
|
||||
keyTest: (event) => {
|
||||
return (
|
||||
!event.shiftKey &&
|
||||
@ -99,6 +136,17 @@ export const actionGroup = register({
|
||||
event.keyCode === KEYS.G_KEY_CODE
|
||||
);
|
||||
},
|
||||
PanelComponent: ({ elements, appState, updateData }) => (
|
||||
<ToolButton
|
||||
hidden={!enableActionGroup(elements, appState)}
|
||||
type="button"
|
||||
icon={group}
|
||||
onClick={() => updateData(null)}
|
||||
title={`${t("labels.group")} — ${getShortcutKey("CtrlOrCmd+G")}`}
|
||||
aria-label={t("labels.group")}
|
||||
visible={isSomeElementSelected(getNonDeletedElements(elements), appState)}
|
||||
></ToolButton>
|
||||
),
|
||||
});
|
||||
|
||||
export const actionUngroup = register({
|
||||
@ -140,4 +188,16 @@ export const actionUngroup = register({
|
||||
contextItemLabel: "labels.ungroup",
|
||||
contextItemPredicate: (elements, appState) =>
|
||||
getSelectedGroupIds(appState).length > 0,
|
||||
|
||||
PanelComponent: ({ elements, appState, updateData }) => (
|
||||
<ToolButton
|
||||
type="button"
|
||||
hidden={getSelectedGroupIds(appState).length === 0}
|
||||
icon={ungroup}
|
||||
onClick={() => updateData(null)}
|
||||
title={`${t("labels.ungroup")} — ${getShortcutKey("CtrlOrCmd+Shift+G")}`}
|
||||
aria-label={t("labels.ungroup")}
|
||||
visible={isSomeElementSelected(getNonDeletedElements(elements), appState)}
|
||||
></ToolButton>
|
||||
),
|
||||
});
|
@ -78,6 +78,8 @@ export const SelectedShapeActions = ({
|
||||
<div className="buttonList">
|
||||
{renderAction("duplicateSelection")}
|
||||
{renderAction("deleteSelectedElements")}
|
||||
{renderAction("group")}
|
||||
{renderAction("ungroup")}
|
||||
</div>
|
||||
</fieldset>
|
||||
)}
|
||||
|
@ -212,3 +212,128 @@ export const shield = createIcon(
|
||||
"M11.553 22.894a.998.998 0 00.894 0s3.037-1.516 5.465-4.097C19.616 16.987 21 14.663 21 12V5a1 1 0 00-.649-.936l-8-3a.998.998 0 00-.702 0l-8 3A1 1 0 003 5v7c0 2.663 1.384 4.987 3.088 6.797 2.428 2.581 5.465 4.097 5.465 4.097zm-1.303-8.481l6.644-6.644a.856.856 0 111.212 1.212l-7.25 7.25a.856.856 0 01-1.212 0l-3.75-3.75a.856.856 0 111.212-1.212l3.144 3.144z",
|
||||
{ width: 24 },
|
||||
);
|
||||
|
||||
export const group = createIcon(
|
||||
<>
|
||||
<path d="M25 26H111V111H25" fill={oc.black} />
|
||||
<path
|
||||
d="M25 111C25 80.2068 25 49.4135 25 26M25 26C48.6174 26 72.2348 26 111 26H25ZM25 26C53.3671 26 81.7343 26 111 26H25ZM111 26C111 52.303 111 78.606 111 111V26ZM111 26C111 51.2947 111 76.5893 111 111V26ZM111 111C87.0792 111 63.1585 111 25 111H111ZM111 111C87.4646 111 63.9293 111 25 111H111ZM25 111C25 81.1514 25 51.3028 25 26V111Z"
|
||||
stroke={oc.black}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<path d="M100 100H160V160H100" fill={oc.black} />
|
||||
<path
|
||||
d="M100 160C100 144.106 100 128.211 100 100M100 100C117.706 100 135.412 100 160 100H100ZM100 100C114.214 100 128.428 100 160 100H100ZM160 100C160 120.184 160 140.369 160 160V100ZM160 100C160 113.219 160 126.437 160 160V100ZM160 160C145.534 160 131.068 160 100 160H160ZM160 160C143.467 160 126.934 160 100 160H160ZM100 160C100 143.661 100 127.321 100 100V160Z"
|
||||
stroke={oc.black}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<rect
|
||||
x="2.5"
|
||||
y="2.5"
|
||||
width="30"
|
||||
height="30"
|
||||
fill={oc.white}
|
||||
stroke={oc.black}
|
||||
strokeWidth="6"
|
||||
/>
|
||||
<rect
|
||||
x="2.5"
|
||||
y="149.5"
|
||||
width="30"
|
||||
height="30"
|
||||
fill={oc.white}
|
||||
stroke={oc.black}
|
||||
strokeWidth="6"
|
||||
/>
|
||||
<rect
|
||||
x="147.5"
|
||||
y="149.5"
|
||||
width="30"
|
||||
height="30"
|
||||
fill={oc.white}
|
||||
stroke={oc.black}
|
||||
strokeWidth="6"
|
||||
/>
|
||||
<rect
|
||||
x="147.5"
|
||||
y="2.5"
|
||||
width="30"
|
||||
height="30"
|
||||
fill={oc.white}
|
||||
stroke={oc.black}
|
||||
strokeWidth="6"
|
||||
/>
|
||||
</>,
|
||||
{ width: 182, height: 182 },
|
||||
);
|
||||
export const ungroup = createIcon(
|
||||
<>
|
||||
<path d="M25 26H111V111H25" fill={oc.black} />
|
||||
<path
|
||||
d="M25 111C25 80.2068 25 49.4135 25 26M25 26C48.6174 26 72.2348 26 111 26H25ZM25 26C53.3671 26 81.7343 26 111 26H25ZM111 26C111 52.303 111 78.606 111 111V26ZM111 26C111 51.2947 111 76.5893 111 111V26ZM111 111C87.0792 111 63.1585 111 25 111H111ZM111 111C87.4646 111 63.9293 111 25 111H111ZM25 111C25 81.1514 25 51.3028 25 26V111Z"
|
||||
stroke={oc.black}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<path d="M100 100H160V160H100" fill={oc.black} />
|
||||
<path
|
||||
d="M100 160C100 144.106 100 128.211 100 100M100 100C117.706 100 135.412 100 160 100H100ZM100 100C114.214 100 128.428 100 160 100H100ZM160 100C160 120.184 160 140.369 160 160V100ZM160 100C160 113.219 160 126.437 160 160V100ZM160 160C145.534 160 131.068 160 100 160H160ZM160 160C143.467 160 126.934 160 100 160H160ZM100 160C100 143.661 100 127.321 100 100V160Z"
|
||||
stroke={oc.black}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<rect
|
||||
x="2.5"
|
||||
y="2.5"
|
||||
width="30"
|
||||
height="30"
|
||||
fill={oc.white}
|
||||
stroke={oc.black}
|
||||
strokeWidth="6"
|
||||
/>
|
||||
<rect
|
||||
x="78.5"
|
||||
y="149.5"
|
||||
width="30"
|
||||
height="30"
|
||||
fill={oc.white}
|
||||
stroke={oc.black}
|
||||
strokeWidth="6"
|
||||
/>
|
||||
<rect
|
||||
x="147.5"
|
||||
y="149.5"
|
||||
width="30"
|
||||
height="30"
|
||||
fill={oc.white}
|
||||
stroke={oc.black}
|
||||
strokeWidth="6"
|
||||
/>
|
||||
<rect
|
||||
x="147.5"
|
||||
y="78.5"
|
||||
width="30"
|
||||
height="30"
|
||||
fill={oc.white}
|
||||
stroke={oc.black}
|
||||
strokeWidth="6"
|
||||
/>
|
||||
<rect
|
||||
x="105.5"
|
||||
y="2.5"
|
||||
width="30"
|
||||
height="30"
|
||||
fill={oc.white}
|
||||
stroke={oc.black}
|
||||
strokeWidth="6"
|
||||
/>
|
||||
<rect
|
||||
x="2.5"
|
||||
y="102.5"
|
||||
width="30"
|
||||
height="30"
|
||||
fill={oc.white}
|
||||
stroke={oc.black}
|
||||
strokeWidth="6"
|
||||
/>
|
||||
</>,
|
||||
{ width: 182, height: 182 },
|
||||
);
|
||||
|
@ -973,7 +973,6 @@ describe("regression tests", () => {
|
||||
"Copy styles",
|
||||
"Paste styles",
|
||||
"Delete",
|
||||
"Group selection",
|
||||
"Ungroup selection",
|
||||
"Add to library",
|
||||
"Send backward",
|
||||
@ -984,7 +983,7 @@ describe("regression tests", () => {
|
||||
];
|
||||
|
||||
expect(contextMenu).not.toBeNull();
|
||||
expect(contextMenu?.children.length).toBe(11);
|
||||
expect(contextMenu?.children.length).toBe(10);
|
||||
options?.forEach((opt, i) => {
|
||||
expect(opt.textContent).toBe(expectedOptions[i]);
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user