feat: wireframe-to-code (#7334)

This commit is contained in:
David Luzar
2023-11-23 23:07:53 +01:00
committed by GitHub
parent d1e4421823
commit c7ee46e7f8
63 changed files with 2106 additions and 444 deletions
+2 -1
View File
@@ -9,6 +9,7 @@ import {
} from "../components/icons";
import { ToolButton } from "../components/ToolButton";
import { getNonDeletedElements } from "../element";
import { isFrameLikeElement } from "../element/typeChecks";
import { ExcalidrawElement } from "../element/types";
import { updateFrameMembershipOfSelectedElements } from "../frame";
import { t } from "../i18n";
@@ -28,7 +29,7 @@ const alignActionsPredicate = (
return (
selectedElements.length > 1 &&
// TODO enable aligning frames when implemented properly
!selectedElements.some((el) => el.type === "frame")
!selectedElements.some((el) => isFrameLikeElement(el))
);
};
+2 -2
View File
@@ -10,7 +10,7 @@ import { newElementWith } from "../element/mutateElement";
import { getElementsInGroup } from "../groups";
import { LinearElementEditor } from "../element/linearElementEditor";
import { fixBindingsAfterDeletion } from "../element/binding";
import { isBoundToContainer } from "../element/typeChecks";
import { isBoundToContainer, isFrameLikeElement } from "../element/typeChecks";
import { updateActiveTool } from "../utils";
import { TrashIcon } from "../components/icons";
@@ -20,7 +20,7 @@ const deleteSelectedElements = (
) => {
const framesToBeDeleted = new Set(
getSelectedElements(
elements.filter((el) => el.type === "frame"),
elements.filter((el) => isFrameLikeElement(el)),
appState,
).map((el) => el.id),
);
+2 -1
View File
@@ -5,6 +5,7 @@ import {
import { ToolButton } from "../components/ToolButton";
import { distributeElements, Distribution } from "../distribute";
import { getNonDeletedElements } from "../element";
import { isFrameLikeElement } from "../element/typeChecks";
import { ExcalidrawElement } from "../element/types";
import { updateFrameMembershipOfSelectedElements } from "../frame";
import { t } from "../i18n";
@@ -19,7 +20,7 @@ const enableActionGroup = (appState: AppState, app: AppClassProperties) => {
return (
selectedElements.length > 1 &&
// TODO enable distributing frames when implemented properly
!selectedElements.some((el) => el.type === "frame")
!selectedElements.some((el) => isFrameLikeElement(el))
);
};
+5 -5
View File
@@ -20,7 +20,7 @@ import {
bindTextToShapeAfterDuplication,
getBoundTextElement,
} from "../element/textElement";
import { isBoundToContainer, isFrameElement } from "../element/typeChecks";
import { isBoundToContainer, isFrameLikeElement } from "../element/typeChecks";
import { normalizeElementOrder } from "../element/sortElements";
import { DuplicateIcon } from "../components/icons";
import {
@@ -140,11 +140,11 @@ const duplicateElements = (
}
const boundTextElement = getBoundTextElement(element);
const isElementAFrame = isFrameElement(element);
const isElementAFrameLike = isFrameLikeElement(element);
if (idsOfElementsToDuplicate.get(element.id)) {
// if a group or a container/bound-text or frame, duplicate atomically
if (element.groupIds.length || boundTextElement || isElementAFrame) {
if (element.groupIds.length || boundTextElement || isElementAFrameLike) {
const groupId = getSelectedGroupForElement(appState, element);
if (groupId) {
// TODO:
@@ -154,7 +154,7 @@ const duplicateElements = (
sortedElements,
groupId,
).flatMap((element) =>
isFrameElement(element)
isFrameLikeElement(element)
? [...getFrameChildren(elements, element.id), element]
: [element],
);
@@ -180,7 +180,7 @@ const duplicateElements = (
);
continue;
}
if (isElementAFrame) {
if (isElementAFrameLike) {
const elementsInFrame = getFrameChildren(sortedElements, element.id);
elementsWithClones.push(
+2 -1
View File
@@ -1,4 +1,5 @@
import { newElementWith } from "../element/mutateElement";
import { isFrameLikeElement } from "../element/typeChecks";
import { ExcalidrawElement } from "../element/types";
import { KEYS } from "../keys";
import { arrayToMap } from "../utils";
@@ -51,7 +52,7 @@ export const actionToggleElementLock = register({
selectedElementIds: appState.selectedElementIds,
includeBoundTextElement: false,
});
if (selected.length === 1 && selected[0].type !== "frame") {
if (selected.length === 1 && !isFrameLikeElement(selected[0])) {
return selected[0].locked
? "labels.elementLock.unlock"
: "labels.elementLock.lock";
+17 -8
View File
@@ -7,23 +7,27 @@ import { AppClassProperties, AppState } from "../types";
import { updateActiveTool } from "../utils";
import { setCursorForShape } from "../cursor";
import { register } from "./register";
import { isFrameLikeElement } from "../element/typeChecks";
const isSingleFrameSelected = (appState: AppState, app: AppClassProperties) => {
const selectedElements = app.scene.getSelectedElements(appState);
return selectedElements.length === 1 && selectedElements[0].type === "frame";
return (
selectedElements.length === 1 && isFrameLikeElement(selectedElements[0])
);
};
export const actionSelectAllElementsInFrame = register({
name: "selectAllElementsInFrame",
trackEvent: { category: "canvas" },
perform: (elements, appState, _, app) => {
const selectedFrame = app.scene.getSelectedElements(appState)[0];
const selectedElement =
app.scene.getSelectedElements(appState).at(0) || null;
if (selectedFrame && selectedFrame.type === "frame") {
if (isFrameLikeElement(selectedElement)) {
const elementsInFrame = getFrameChildren(
getNonDeletedElements(elements),
selectedFrame.id,
selectedElement.id,
).filter((element) => !(element.type === "text" && element.containerId));
return {
@@ -54,15 +58,20 @@ export const actionRemoveAllElementsFromFrame = register({
name: "removeAllElementsFromFrame",
trackEvent: { category: "history" },
perform: (elements, appState, _, app) => {
const selectedFrame = app.scene.getSelectedElements(appState)[0];
const selectedElement =
app.scene.getSelectedElements(appState).at(0) || null;
if (selectedFrame && selectedFrame.type === "frame") {
if (isFrameLikeElement(selectedElement)) {
return {
elements: removeAllElementsFromFrame(elements, selectedFrame, appState),
elements: removeAllElementsFromFrame(
elements,
selectedElement,
appState,
),
appState: {
...appState,
selectedElementIds: {
[selectedFrame.id]: true,
[selectedElement.id]: true,
},
},
commitToHistory: true,
+4 -4
View File
@@ -22,8 +22,8 @@ import { AppClassProperties, AppState } from "../types";
import { isBoundToContainer } from "../element/typeChecks";
import {
getElementsInResizingFrame,
getFrameElements,
groupByFrames,
getFrameLikeElements,
groupByFrameLikes,
removeElementsFromFrame,
replaceAllElementsInFrame,
} from "../frame";
@@ -102,7 +102,7 @@ export const actionGroup = register({
// when it happens, we want to remove elements that are in the frame
// and are going to be grouped from the frame (mouthful, I know)
if (groupingElementsFromDifferentFrames) {
const frameElementsMap = groupByFrames(selectedElements);
const frameElementsMap = groupByFrameLikes(selectedElements);
frameElementsMap.forEach((elementsInFrame, frameId) => {
nextElements = removeElementsFromFrame(
@@ -219,7 +219,7 @@ export const actionUngroup = register({
.map((element) => element.frameId!),
);
const targetFrames = getFrameElements(elements).filter((frame) =>
const targetFrames = getFrameLikeElements(elements).filter((frame) =>
selectedElementFrameIds.has(frame.id),
);
+2 -2
View File
@@ -20,7 +20,7 @@ import {
hasBoundTextElement,
canApplyRoundnessTypeToElement,
getDefaultRoundnessTypeForElement,
isFrameElement,
isFrameLikeElement,
isArrowElement,
} from "../element/typeChecks";
import { getSelectedElements } from "../scene";
@@ -138,7 +138,7 @@ export const actionPasteStyles = register({
});
}
if (isFrameElement(element)) {
if (isFrameLikeElement(element)) {
newElement = newElementWith(newElement, {
roundness: null,
backgroundColor: "transparent",