2020-01-12 02:22:03 +04:00
|
|
|
import React from "react";
|
|
|
|
import { Action } from "./types";
|
|
|
|
import { ExcalidrawElement, ExcalidrawTextElement } from "../element/types";
|
2020-01-21 00:16:22 +01:00
|
|
|
import { getCommonAttributeOfSelectedElements } from "../scene";
|
2020-01-12 02:22:03 +04:00
|
|
|
import { ButtonSelect } from "../components/ButtonSelect";
|
|
|
|
import { isTextElement, redrawTextBoundingBox } from "../element";
|
2020-01-15 20:42:02 +05:00
|
|
|
import { ColorPicker } from "../components/ColorPicker";
|
2020-01-21 00:16:22 +01:00
|
|
|
import { AppState } from "../../src/types";
|
2020-01-12 02:22:03 +04:00
|
|
|
|
|
|
|
const changeProperty = (
|
|
|
|
elements: readonly ExcalidrawElement[],
|
|
|
|
callback: (element: ExcalidrawElement) => ExcalidrawElement
|
|
|
|
) => {
|
|
|
|
return elements.map(element => {
|
|
|
|
if (element.isSelected) {
|
|
|
|
return callback(element);
|
|
|
|
}
|
|
|
|
return element;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2020-01-21 00:16:22 +01:00
|
|
|
const getFormValue = function<T>(
|
|
|
|
editingElement: AppState["editingElement"],
|
|
|
|
elements: readonly ExcalidrawElement[],
|
|
|
|
getAttribute: (element: ExcalidrawElement) => T,
|
|
|
|
defaultValue?: T
|
|
|
|
): T | null {
|
|
|
|
return (
|
2020-01-22 20:51:56 +01:00
|
|
|
(editingElement && getAttribute(editingElement)) ??
|
|
|
|
getCommonAttributeOfSelectedElements(elements, getAttribute) ??
|
|
|
|
defaultValue ??
|
2020-01-21 00:16:22 +01:00
|
|
|
null
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2020-01-12 02:22:03 +04:00
|
|
|
export const actionChangeStrokeColor: Action = {
|
|
|
|
name: "changeStrokeColor",
|
|
|
|
perform: (elements, appState, value) => {
|
|
|
|
return {
|
|
|
|
elements: changeProperty(elements, el => ({
|
|
|
|
...el,
|
2020-01-12 04:00:00 +04:00
|
|
|
shape: null,
|
2020-01-12 02:22:03 +04:00
|
|
|
strokeColor: value
|
|
|
|
})),
|
|
|
|
appState: { ...appState, currentItemStrokeColor: value }
|
|
|
|
};
|
|
|
|
},
|
2020-01-21 00:16:22 +01:00
|
|
|
PanelComponent: ({ elements, appState, updateData, t }) => (
|
|
|
|
<>
|
|
|
|
<h5>{t("labels.stroke")}</h5>
|
|
|
|
<ColorPicker
|
|
|
|
type="elementStroke"
|
|
|
|
color={getFormValue(
|
|
|
|
appState.editingElement,
|
|
|
|
elements,
|
|
|
|
element => element.strokeColor,
|
|
|
|
appState.currentItemStrokeColor
|
|
|
|
)}
|
|
|
|
onChange={updateData}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
)
|
2020-01-12 02:22:03 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
export const actionChangeBackgroundColor: Action = {
|
|
|
|
name: "changeBackgroundColor",
|
|
|
|
perform: (elements, appState, value) => {
|
|
|
|
return {
|
|
|
|
elements: changeProperty(elements, el => ({
|
|
|
|
...el,
|
2020-01-12 04:00:00 +04:00
|
|
|
shape: null,
|
2020-01-12 02:22:03 +04:00
|
|
|
backgroundColor: value
|
|
|
|
})),
|
|
|
|
appState: { ...appState, currentItemBackgroundColor: value }
|
|
|
|
};
|
|
|
|
},
|
2020-01-21 01:14:10 +02:00
|
|
|
PanelComponent: ({ elements, appState, updateData, t }) => (
|
2020-01-15 20:42:02 +05:00
|
|
|
<>
|
2020-01-21 01:14:10 +02:00
|
|
|
<h5>{t("labels.background")}</h5>
|
2020-01-15 20:42:02 +05:00
|
|
|
<ColorPicker
|
|
|
|
type="elementBackground"
|
2020-01-21 00:16:22 +01:00
|
|
|
color={getFormValue(
|
|
|
|
appState.editingElement,
|
|
|
|
elements,
|
|
|
|
element => element.backgroundColor,
|
2020-01-19 00:45:35 +01:00
|
|
|
appState.currentItemBackgroundColor
|
2020-01-21 00:16:22 +01:00
|
|
|
)}
|
2020-01-15 20:42:02 +05:00
|
|
|
onChange={updateData}
|
|
|
|
/>
|
|
|
|
</>
|
2020-01-12 02:22:03 +04:00
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
export const actionChangeFillStyle: Action = {
|
|
|
|
name: "changeFillStyle",
|
|
|
|
perform: (elements, appState, value) => {
|
|
|
|
return {
|
|
|
|
elements: changeProperty(elements, el => ({
|
|
|
|
...el,
|
2020-01-12 04:00:00 +04:00
|
|
|
shape: null,
|
2020-01-12 02:22:03 +04:00
|
|
|
fillStyle: value
|
|
|
|
}))
|
|
|
|
};
|
|
|
|
},
|
2020-01-21 00:16:22 +01:00
|
|
|
PanelComponent: ({ elements, appState, updateData, t }) => (
|
2020-01-12 02:22:03 +04:00
|
|
|
<>
|
2020-01-21 01:14:10 +02:00
|
|
|
<h5>{t("labels.fill")}</h5>
|
2020-01-12 02:22:03 +04:00
|
|
|
<ButtonSelect
|
|
|
|
options={[
|
2020-01-22 16:25:04 +02:00
|
|
|
{ value: "solid", text: t("labels.solid") },
|
|
|
|
{ value: "hachure", text: t("labels.hachure") },
|
|
|
|
{ value: "cross-hatch", text: t("labels.crossHatch") }
|
2020-01-12 02:22:03 +04:00
|
|
|
]}
|
2020-01-21 00:16:22 +01:00
|
|
|
value={getFormValue(
|
|
|
|
appState.editingElement,
|
|
|
|
elements,
|
|
|
|
element => element.fillStyle
|
|
|
|
)}
|
2020-01-12 02:22:03 +04:00
|
|
|
onChange={value => {
|
|
|
|
updateData(value);
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
export const actionChangeStrokeWidth: Action = {
|
|
|
|
name: "changeStrokeWidth",
|
|
|
|
perform: (elements, appState, value) => {
|
|
|
|
return {
|
|
|
|
elements: changeProperty(elements, el => ({
|
|
|
|
...el,
|
2020-01-12 04:00:00 +04:00
|
|
|
shape: null,
|
2020-01-12 02:22:03 +04:00
|
|
|
strokeWidth: value
|
|
|
|
}))
|
|
|
|
};
|
|
|
|
},
|
2020-01-21 01:14:10 +02:00
|
|
|
PanelComponent: ({ elements, appState, updateData, t }) => (
|
2020-01-12 02:22:03 +04:00
|
|
|
<>
|
2020-01-21 01:14:10 +02:00
|
|
|
<h5>{t("labels.strokeWidth")}</h5>
|
2020-01-12 02:22:03 +04:00
|
|
|
<ButtonSelect
|
|
|
|
options={[
|
2020-01-22 16:25:04 +02:00
|
|
|
{ value: 1, text: t("labels.thin") },
|
|
|
|
{ value: 2, text: t("labels.bold") },
|
|
|
|
{ value: 4, text: t("labels.extraBold") }
|
2020-01-12 02:22:03 +04:00
|
|
|
]}
|
2020-01-21 00:16:22 +01:00
|
|
|
value={getFormValue(
|
|
|
|
appState.editingElement,
|
|
|
|
elements,
|
|
|
|
element => element.strokeWidth
|
|
|
|
)}
|
2020-01-12 02:22:03 +04:00
|
|
|
onChange={value => updateData(value)}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
export const actionChangeSloppiness: Action = {
|
|
|
|
name: "changeSloppiness",
|
|
|
|
perform: (elements, appState, value) => {
|
|
|
|
return {
|
|
|
|
elements: changeProperty(elements, el => ({
|
|
|
|
...el,
|
2020-01-12 04:00:00 +04:00
|
|
|
shape: null,
|
2020-01-12 02:22:03 +04:00
|
|
|
roughness: value
|
|
|
|
}))
|
|
|
|
};
|
|
|
|
},
|
2020-01-21 01:14:10 +02:00
|
|
|
PanelComponent: ({ elements, appState, updateData, t }) => (
|
2020-01-12 02:22:03 +04:00
|
|
|
<>
|
2020-01-21 01:14:10 +02:00
|
|
|
<h5>{t("labels.sloppiness")}</h5>
|
2020-01-12 02:22:03 +04:00
|
|
|
<ButtonSelect
|
|
|
|
options={[
|
2020-01-22 16:25:04 +02:00
|
|
|
{ value: 0, text: t("labels.architect") },
|
|
|
|
{ value: 1, text: t("labels.artist") },
|
|
|
|
{ value: 3, text: t("labels.cartoonist") }
|
2020-01-12 02:22:03 +04:00
|
|
|
]}
|
2020-01-21 00:16:22 +01:00
|
|
|
value={getFormValue(
|
|
|
|
appState.editingElement,
|
|
|
|
elements,
|
|
|
|
element => element.roughness
|
|
|
|
)}
|
2020-01-12 02:22:03 +04:00
|
|
|
onChange={value => updateData(value)}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
export const actionChangeOpacity: Action = {
|
|
|
|
name: "changeOpacity",
|
|
|
|
perform: (elements, appState, value) => {
|
|
|
|
return {
|
|
|
|
elements: changeProperty(elements, el => ({
|
|
|
|
...el,
|
2020-01-12 04:00:00 +04:00
|
|
|
shape: null,
|
2020-01-12 02:22:03 +04:00
|
|
|
opacity: value
|
|
|
|
}))
|
|
|
|
};
|
|
|
|
},
|
2020-01-21 00:16:22 +01:00
|
|
|
PanelComponent: ({ elements, appState, updateData, t }) => (
|
2020-01-12 02:22:03 +04:00
|
|
|
<>
|
2020-01-21 17:39:39 +01:00
|
|
|
<h5>{t("labels.opacity")}</h5>
|
2020-01-12 02:22:03 +04:00
|
|
|
<input
|
|
|
|
type="range"
|
|
|
|
min="0"
|
|
|
|
max="100"
|
|
|
|
onChange={e => updateData(+e.target.value)}
|
|
|
|
value={
|
2020-01-21 00:16:22 +01:00
|
|
|
getFormValue(
|
|
|
|
appState.editingElement,
|
|
|
|
elements,
|
|
|
|
element => element.opacity,
|
|
|
|
100 /* default opacity */
|
2020-01-22 20:51:56 +01:00
|
|
|
) ?? undefined
|
2020-01-12 02:22:03 +04:00
|
|
|
}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
export const actionChangeFontSize: Action = {
|
|
|
|
name: "changeFontSize",
|
|
|
|
perform: (elements, appState, value) => {
|
|
|
|
return {
|
|
|
|
elements: changeProperty(elements, el => {
|
|
|
|
if (isTextElement(el)) {
|
|
|
|
const element: ExcalidrawTextElement = {
|
|
|
|
...el,
|
2020-01-12 04:00:00 +04:00
|
|
|
shape: null,
|
2020-01-12 02:22:03 +04:00
|
|
|
font: `${value}px ${el.font.split("px ")[1]}`
|
|
|
|
};
|
|
|
|
redrawTextBoundingBox(element);
|
|
|
|
return element;
|
|
|
|
}
|
|
|
|
|
|
|
|
return el;
|
|
|
|
})
|
|
|
|
};
|
|
|
|
},
|
2020-01-21 00:16:22 +01:00
|
|
|
PanelComponent: ({ elements, appState, updateData, t }) => (
|
2020-01-12 02:22:03 +04:00
|
|
|
<>
|
2020-01-21 01:14:10 +02:00
|
|
|
<h5>{t("labels.fontSize")}</h5>
|
2020-01-12 02:22:03 +04:00
|
|
|
<ButtonSelect
|
|
|
|
options={[
|
2020-01-22 16:25:04 +02:00
|
|
|
{ value: 16, text: t("labels.small") },
|
|
|
|
{ value: 20, text: t("labels.medium") },
|
|
|
|
{ value: 28, text: t("labels.large") },
|
|
|
|
{ value: 36, text: t("labels.veryLarge") }
|
2020-01-12 02:22:03 +04:00
|
|
|
]}
|
2020-01-21 00:16:22 +01:00
|
|
|
value={getFormValue(
|
|
|
|
appState.editingElement,
|
2020-01-12 02:22:03 +04:00
|
|
|
elements,
|
|
|
|
element => isTextElement(element) && +element.font.split("px ")[0]
|
|
|
|
)}
|
|
|
|
onChange={value => updateData(value)}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
export const actionChangeFontFamily: Action = {
|
|
|
|
name: "changeFontFamily",
|
|
|
|
perform: (elements, appState, value) => {
|
|
|
|
return {
|
|
|
|
elements: changeProperty(elements, el => {
|
|
|
|
if (isTextElement(el)) {
|
|
|
|
const element: ExcalidrawTextElement = {
|
|
|
|
...el,
|
2020-01-12 04:00:00 +04:00
|
|
|
shape: null,
|
2020-01-12 02:22:03 +04:00
|
|
|
font: `${el.font.split("px ")[0]}px ${value}`
|
|
|
|
};
|
|
|
|
redrawTextBoundingBox(element);
|
|
|
|
return element;
|
|
|
|
}
|
|
|
|
|
|
|
|
return el;
|
|
|
|
})
|
|
|
|
};
|
|
|
|
},
|
2020-01-21 00:16:22 +01:00
|
|
|
PanelComponent: ({ elements, appState, updateData, t }) => (
|
2020-01-12 02:22:03 +04:00
|
|
|
<>
|
2020-01-21 01:14:10 +02:00
|
|
|
<h5>{t("labels.fontFamily")}</h5>
|
2020-01-12 02:22:03 +04:00
|
|
|
<ButtonSelect
|
|
|
|
options={[
|
2020-01-21 01:14:10 +02:00
|
|
|
{ value: "Virgil", text: t("labels.handDrawn") },
|
|
|
|
{ value: "Helvetica", text: t("labels.normal") },
|
|
|
|
{ value: "Cascadia", text: t("labels.code") }
|
2020-01-12 02:22:03 +04:00
|
|
|
]}
|
2020-01-21 00:16:22 +01:00
|
|
|
value={getFormValue(
|
|
|
|
appState.editingElement,
|
2020-01-12 02:22:03 +04:00
|
|
|
elements,
|
|
|
|
element => isTextElement(element) && element.font.split("px ")[1]
|
|
|
|
)}
|
|
|
|
onChange={value => updateData(value)}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
};
|