mirror of
https://github.com/excalidraw/excalidraw.git
synced 2024-11-10 11:35:52 +01:00
feat: replaces fontSize and fontFamily text with icons (#2857)
Co-authored-by: Hitesh Goyal <hiteshlyearn@Hiteshs-MacBook-Pro.local> Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
parent
c8743a8c02
commit
78f3a92dd1
@ -1,7 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { AppState } from "../../src/types";
|
import { AppState } from "../../src/types";
|
||||||
import { ButtonIconSelect } from "../components/ButtonIconSelect";
|
import { ButtonIconSelect } from "../components/ButtonIconSelect";
|
||||||
import { ButtonSelect } from "../components/ButtonSelect";
|
|
||||||
import { ColorPicker } from "../components/ColorPicker";
|
import { ColorPicker } from "../components/ColorPicker";
|
||||||
import { IconPicker } from "../components/IconPicker";
|
import { IconPicker } from "../components/IconPicker";
|
||||||
import {
|
import {
|
||||||
@ -21,6 +20,16 @@ import {
|
|||||||
StrokeStyleDottedIcon,
|
StrokeStyleDottedIcon,
|
||||||
StrokeStyleSolidIcon,
|
StrokeStyleSolidIcon,
|
||||||
StrokeWidthIcon,
|
StrokeWidthIcon,
|
||||||
|
FontSizeSmallIcon,
|
||||||
|
FontSizeMediumIcon,
|
||||||
|
FontSizeLargeIcon,
|
||||||
|
FontSizeExtraLargeIcon,
|
||||||
|
FontFamilyHandDrawnIcon,
|
||||||
|
FontFamilyNormalIcon,
|
||||||
|
FontFamilyCodeIcon,
|
||||||
|
TextAlignLeftIcon,
|
||||||
|
TextAlignCenterIcon,
|
||||||
|
TextAlignRightIcon,
|
||||||
} from "../components/icons";
|
} from "../components/icons";
|
||||||
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from "../constants";
|
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from "../constants";
|
||||||
import {
|
import {
|
||||||
@ -413,13 +422,29 @@ export const actionChangeFontSize = register({
|
|||||||
PanelComponent: ({ elements, appState, updateData }) => (
|
PanelComponent: ({ elements, appState, updateData }) => (
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{t("labels.fontSize")}</legend>
|
<legend>{t("labels.fontSize")}</legend>
|
||||||
<ButtonSelect
|
<ButtonIconSelect
|
||||||
group="font-size"
|
group="font-size"
|
||||||
options={[
|
options={[
|
||||||
{ value: 16, text: t("labels.small") },
|
{
|
||||||
{ value: 20, text: t("labels.medium") },
|
value: 16,
|
||||||
{ value: 28, text: t("labels.large") },
|
text: t("labels.small"),
|
||||||
{ value: 36, text: t("labels.veryLarge") },
|
icon: <FontSizeSmallIcon theme={appState.theme} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 20,
|
||||||
|
text: t("labels.medium"),
|
||||||
|
icon: <FontSizeMediumIcon theme={appState.theme} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 28,
|
||||||
|
text: t("labels.large"),
|
||||||
|
icon: <FontSizeLargeIcon theme={appState.theme} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 36,
|
||||||
|
text: t("labels.veryLarge"),
|
||||||
|
icon: <FontSizeExtraLargeIcon theme={appState.theme} />,
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
value={getFormValue(
|
value={getFormValue(
|
||||||
elements,
|
elements,
|
||||||
@ -456,16 +481,28 @@ export const actionChangeFontFamily = register({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
PanelComponent: ({ elements, appState, updateData }) => {
|
PanelComponent: ({ elements, appState, updateData }) => {
|
||||||
const options: { value: FontFamily; text: string }[] = [
|
const options: { value: FontFamily; text: string; icon: JSX.Element }[] = [
|
||||||
{ value: 1, text: t("labels.handDrawn") },
|
{
|
||||||
{ value: 2, text: t("labels.normal") },
|
value: 1,
|
||||||
{ value: 3, text: t("labels.code") },
|
text: t("labels.handDrawn"),
|
||||||
|
icon: <FontFamilyHandDrawnIcon theme={appState.theme} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 2,
|
||||||
|
text: t("labels.normal"),
|
||||||
|
icon: <FontFamilyNormalIcon theme={appState.theme} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 3,
|
||||||
|
text: t("labels.code"),
|
||||||
|
icon: <FontFamilyCodeIcon theme={appState.theme} />,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{t("labels.fontFamily")}</legend>
|
<legend>{t("labels.fontFamily")}</legend>
|
||||||
<ButtonSelect<FontFamily | false>
|
<ButtonIconSelect<FontFamily | false>
|
||||||
group="font-family"
|
group="font-family"
|
||||||
options={options}
|
options={options}
|
||||||
value={getFormValue(
|
value={getFormValue(
|
||||||
@ -506,12 +543,24 @@ export const actionChangeTextAlign = register({
|
|||||||
PanelComponent: ({ elements, appState, updateData }) => (
|
PanelComponent: ({ elements, appState, updateData }) => (
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{t("labels.textAlign")}</legend>
|
<legend>{t("labels.textAlign")}</legend>
|
||||||
<ButtonSelect<TextAlign | false>
|
<ButtonIconSelect<TextAlign | false>
|
||||||
group="text-align"
|
group="text-align"
|
||||||
options={[
|
options={[
|
||||||
{ value: "left", text: t("labels.left") },
|
{
|
||||||
{ value: "center", text: t("labels.center") },
|
value: "left",
|
||||||
{ value: "right", text: t("labels.right") },
|
text: t("labels.left"),
|
||||||
|
icon: <TextAlignLeftIcon theme={appState.theme} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "center",
|
||||||
|
text: t("labels.center"),
|
||||||
|
icon: <TextAlignCenterIcon theme={appState.theme} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "right",
|
||||||
|
text: t("labels.right"),
|
||||||
|
icon: <TextAlignRightIcon theme={appState.theme} />,
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
value={getFormValue(
|
value={getFormValue(
|
||||||
elements,
|
elements,
|
||||||
|
@ -794,3 +794,121 @@ export const ArrowheadBarIcon = React.memo(
|
|||||||
{ width: 40, height: 20 },
|
{ width: 40, height: 20 },
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const FontSizeSmallIcon = React.memo(
|
||||||
|
({ theme }: { theme: "light" | "dark" }) =>
|
||||||
|
createIcon(
|
||||||
|
<path
|
||||||
|
fill={iconFillColor(theme)}
|
||||||
|
d="M 0 69.092 L 0 55.03 A 124.24 124.24 0 0 0 4.706 57.02 Q 6.826 57.863 8.708 58.5 A 53.466 53.466 0 0 0 12.231 59.571 Q 17.236 60.889 21.387 60.889 A 20.909 20.909 0 0 0 24.265 60.704 Q 25.719 60.502 26.903 60.077 A 8.649 8.649 0 0 0 29.028 58.985 Q 31.689 57.08 31.689 53.321 Q 31.689 51.221 30.518 49.585 A 10.126 10.126 0 0 0 29.282 48.177 Q 28.352 47.287 27.075 46.436 A 23.719 23.719 0 0 0 25.752 45.627 Q 23.774 44.492 20.176 42.735 A 254.44 254.44 0 0 0 17.822 41.602 Q 11.503 38.631 8.236 35.888 A 19.742 19.742 0 0 1 8.008 35.694 A 22.18 22.18 0 0 1 2.783 29.102 Q 0.83 25.342 0.83 20.313 A 22.471 22.471 0 0 1 1.733 13.778 A 17.283 17.283 0 0 1 7.251 5.42 A 21.486 21.486 0 0 1 15.177 1.272 Q 18.361 0.338 22.166 0.09 A 43.573 43.573 0 0 1 25 0 A 42.399 42.399 0 0 1 34.349 1.01 A 39.075 39.075 0 0 1 35.62 1.319 A 67.407 67.407 0 0 1 42.108 3.382 A 83.357 83.357 0 0 1 46.191 5.03 L 41.309 16.797 Q 35.596 14.453 31.86 13.526 A 30.762 30.762 0 0 0 25.417 12.612 A 28.337 28.337 0 0 0 24.512 12.598 A 14.846 14.846 0 0 0 22.022 12.793 Q 19.498 13.224 17.92 14.6 Q 15.625 16.602 15.625 19.824 Q 15.625 21.826 16.553 23.316 Q 17.48 24.805 19.507 26.197 A 18.343 18.343 0 0 0 20.659 26.912 Q 22.596 28.035 26.516 29.953 A 299.99 299.99 0 0 0 29.102 31.201 Q 37.91 35.412 41.841 39.642 A 16.553 16.553 0 0 1 42.822 40.796 A 17.675 17.675 0 0 1 46.301 49.233 A 23.517 23.517 0 0 1 46.533 52.588 A 21.581 21.581 0 0 1 45.471 59.515 A 17.733 17.733 0 0 1 39.575 67.823 Q 33.745 72.486 24.094 73.243 A 49.683 49.683 0 0 1 20.215 73.389 A 51.712 51.712 0 0 1 9.448 72.315 A 40.672 40.672 0 0 1 0 69.092 Z"
|
||||||
|
/>,
|
||||||
|
{ width: 47, height: 77 },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FontSizeMediumIcon = React.memo(
|
||||||
|
({ theme }: { theme: "light" | "dark" }) =>
|
||||||
|
createIcon(
|
||||||
|
<path
|
||||||
|
fill={iconFillColor(theme)}
|
||||||
|
d="M 44.092 71.387 L 30.225 71.387 L 13.037 15.381 L 12.598 15.381 A 1505.093 1505.093 0 0 1 12.959 22.313 Q 13.426 31.715 13.508 36.4 A 102.991 102.991 0 0 1 13.525 38.184 L 13.525 71.387 L 0 71.387 L 0 0 L 20.605 0 L 37.5 54.59 L 37.793 54.59 L 55.713 0 L 76.318 0 L 76.318 71.387 L 62.207 71.387 L 62.207 37.598 Q 62.207 35.205 62.28 32.08 A 160.703 160.703 0 0 1 62.326 30.544 Q 62.452 26.754 62.866 17.168 A 5390.536 5390.536 0 0 1 62.939 15.479 L 62.5 15.479 L 44.092 71.387 Z"
|
||||||
|
/>,
|
||||||
|
{ width: 77, height: 75 },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FontSizeLargeIcon = React.memo(
|
||||||
|
({ theme }: { theme: "light" | "dark" }) =>
|
||||||
|
createIcon(
|
||||||
|
<path
|
||||||
|
fill={iconFillColor(theme)}
|
||||||
|
d="M 44.092 71.387 L 0 71.387 L 0 0 L 15.137 0 L 15.137 58.887 L 44.092 58.887 L 44.092 71.387 Z"
|
||||||
|
/>,
|
||||||
|
{ width: 45, height: 75 },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FontSizeExtraLargeIcon = React.memo(
|
||||||
|
({ theme }: { theme: "light" | "dark" }) =>
|
||||||
|
createIcon(
|
||||||
|
<path
|
||||||
|
fill={iconFillColor(theme)}
|
||||||
|
d="M 42.578 35.4 L 66.699 71.387 L 49.414 71.387 L 32.813 44.385 L 16.211 71.387 L 0 71.387 L 23.682 34.57 L 1.514 0 L 18.213 0 L 33.594 25.684 L 48.682 0 L 64.99 0 L 42.578 35.4 Z M 119.775 71.387 L 75.684 71.387 L 75.684 0 L 90.82 0 L 90.82 58.887 L 119.775 58.887 L 119.775 71.387 Z"
|
||||||
|
/>,
|
||||||
|
{ width: 120, height: 75 },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FontFamilyHandDrawnIcon = React.memo(
|
||||||
|
({ theme }: { theme: "light" | "dark" }) =>
|
||||||
|
createIcon(
|
||||||
|
<path
|
||||||
|
fill={iconFillColor(theme)}
|
||||||
|
d="M290.74 93.24l128.02 128.02-277.99 277.99-114.14 12.6C11.35 513.54-1.56 500.62.14 485.34l12.7-114.22 277.9-277.88zm207.2-19.06l-60.11-60.11c-18.75-18.75-49.16-18.75-67.91 0l-56.55 56.55 128.02 128.02 56.55-56.55c18.75-18.76 18.75-49.16 0-67.91z"
|
||||||
|
/>,
|
||||||
|
{ width: 448, height: 512 },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FontFamilyNormalIcon = React.memo(
|
||||||
|
({ theme }: { theme: "light" | "dark" }) =>
|
||||||
|
createIcon(
|
||||||
|
<>
|
||||||
|
<path
|
||||||
|
fill={iconFillColor(theme)}
|
||||||
|
d="M 63.818 71.68 L 54.492 71.68 L 45.898 49.561 L 17.578 49.561 L 9.082 71.68 L 0 71.68 L 27.881 0 L 35.986 0 L 63.818 71.68 Z M 20.605 41.602 L 43.213 41.602 L 35.205 19.971 L 31.787 9.277 Q 30.322 15.137 28.711 19.971 L 20.605 41.602 Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill={iconFillColor(theme)}
|
||||||
|
d="M 68.994 71.68 L 52.686 71.68 L 47.51 54.688 L 21.484 54.688 L 16.309 71.68 L 0 71.68 L 25.195 0 L 43.701 0 L 68.994 71.68 Z M 25.293 41.992 L 43.896 41.992 A 27590.463 27590.463 0 0 1 42.2 36.532 Q 36.965 19.676 35.937 16.273 A 120.932 120.932 0 0 1 35.815 15.869 A 131.65 131.65 0 0 1 35.396 14.435 Q 34.951 12.879 34.675 11.741 A 34.866 34.866 0 0 1 34.521 11.084 A 141.762 141.762 0 0 1 33.706 14.075 Q 31.482 21.957 25.293 41.992 Z"
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
{ width: 70, height: 78 },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FontFamilyCodeIcon = React.memo(
|
||||||
|
({ theme }: { theme: "light" | "dark" }) =>
|
||||||
|
createIcon(
|
||||||
|
<>
|
||||||
|
<path
|
||||||
|
fill={iconFillColor(theme)}
|
||||||
|
d="M278.9 511.5l-61-17.7c-6.4-1.8-10-8.5-8.2-14.9L346.2 8.7c1.8-6.4 8.5-10 14.9-8.2l61 17.7c6.4 1.8 10 8.5 8.2 14.9L293.8 503.3c-1.9 6.4-8.5 10.1-14.9 8.2zm-114-112.2l43.5-46.4c4.6-4.9 4.3-12.7-.8-17.2L117 256l90.6-79.7c5.1-4.5 5.5-12.3.8-17.2l-43.5-46.4c-4.5-4.8-12.1-5.1-17-.5L3.8 247.2c-5.1 4.7-5.1 12.8 0 17.5l144.1 135.1c4.9 4.6 12.5 4.4 17-.5zm327.2.6l144.1-135.1c5.1-4.7 5.1-12.8 0-17.5L492.1 112.1c-4.8-4.5-12.4-4.3-17 .5L431.6 159c-4.6 4.9-4.3 12.7.8 17.2L523 256l-90.6 79.7c-5.1 4.5-5.5 12.3-.8 17.2l43.5 46.4c4.5 4.9 12.1 5.1 17 .6z"
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
{ width: 640, height: 512 },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const TextAlignLeftIcon = React.memo(
|
||||||
|
({ theme }: { theme: "light" | "dark" }) =>
|
||||||
|
createIcon(
|
||||||
|
<path
|
||||||
|
d="M12.83 352h262.34A12.82 12.82 0 00288 339.17v-38.34A12.82 12.82 0 00275.17 288H12.83A12.82 12.82 0 000 300.83v38.34A12.82 12.82 0 0012.83 352zm0-256h262.34A12.82 12.82 0 00288 83.17V44.83A12.82 12.82 0 00275.17 32H12.83A12.82 12.82 0 000 44.83v38.34A12.82 12.82 0 0012.83 96zM432 160H16a16 16 0 00-16 16v32a16 16 0 0016 16h416a16 16 0 0016-16v-32a16 16 0 00-16-16zm0 256H16a16 16 0 00-16 16v32a16 16 0 0016 16h416a16 16 0 0016-16v-32a16 16 0 00-16-16z"
|
||||||
|
fill={iconFillColor(theme)}
|
||||||
|
/>,
|
||||||
|
{ width: 448, height: 512 },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const TextAlignCenterIcon = React.memo(
|
||||||
|
({ theme }: { theme: "light" | "dark" }) =>
|
||||||
|
createIcon(
|
||||||
|
<path
|
||||||
|
d="M432 160H16a16 16 0 00-16 16v32a16 16 0 0016 16h416a16 16 0 0016-16v-32a16 16 0 00-16-16zm0 256H16a16 16 0 00-16 16v32a16 16 0 0016 16h416a16 16 0 0016-16v-32a16 16 0 00-16-16zM108.1 96h231.81A12.09 12.09 0 00352 83.9V44.09A12.09 12.09 0 00339.91 32H108.1A12.09 12.09 0 0096 44.09V83.9A12.1 12.1 0 00108.1 96zm231.81 256A12.09 12.09 0 00352 339.9v-39.81A12.09 12.09 0 00339.91 288H108.1A12.09 12.09 0 0096 300.09v39.81a12.1 12.1 0 0012.1 12.1z"
|
||||||
|
fill={iconFillColor(theme)}
|
||||||
|
/>,
|
||||||
|
{ width: 448, height: 512 },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const TextAlignRightIcon = React.memo(
|
||||||
|
({ theme }: { theme: "light" | "dark" }) =>
|
||||||
|
createIcon(
|
||||||
|
<path
|
||||||
|
d="M16 224h416a16 16 0 0016-16v-32a16 16 0 00-16-16H16a16 16 0 00-16 16v32a16 16 0 0016 16zm416 192H16a16 16 0 00-16 16v32a16 16 0 0016 16h416a16 16 0 0016-16v-32a16 16 0 00-16-16zm3.17-384H172.83A12.82 12.82 0 00160 44.83v38.34A12.82 12.82 0 00172.83 96h262.34A12.82 12.82 0 00448 83.17V44.83A12.82 12.82 0 00435.17 32zm0 256H172.83A12.82 12.82 0 00160 300.83v38.34A12.82 12.82 0 00172.83 352h262.34A12.82 12.82 0 00448 339.17v-38.34A12.82 12.82 0 00435.17 288z"
|
||||||
|
fill={iconFillColor(theme)}
|
||||||
|
/>,
|
||||||
|
{ width: 448, height: 512 },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
@ -222,7 +222,8 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
svg {
|
svg {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 18px;
|
height: 14px;
|
||||||
|
padding: 2px;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
&.active svg {
|
&.active svg {
|
||||||
|
@ -207,7 +207,8 @@ export const textWysiwyg = ({
|
|||||||
// prevent blur when changing properties from the menu
|
// prevent blur when changing properties from the menu
|
||||||
const onPointerDown = (event: MouseEvent) => {
|
const onPointerDown = (event: MouseEvent) => {
|
||||||
if (
|
if (
|
||||||
event.target instanceof HTMLElement &&
|
(event.target instanceof HTMLElement ||
|
||||||
|
event.target instanceof SVGElement) &&
|
||||||
event.target.closest(`.${CLASSES.SHAPE_ACTIONS_MENU}`) &&
|
event.target.closest(`.${CLASSES.SHAPE_ACTIONS_MENU}`) &&
|
||||||
!isWritableElement(event.target)
|
!isWritableElement(event.target)
|
||||||
) {
|
) {
|
||||||
|
@ -607,7 +607,7 @@ describe("regression tests", () => {
|
|||||||
it("updates fontSize & fontFamily appState", () => {
|
it("updates fontSize & fontFamily appState", () => {
|
||||||
UI.clickTool("text");
|
UI.clickTool("text");
|
||||||
expect(h.state.currentItemFontFamily).toEqual(1); // Virgil
|
expect(h.state.currentItemFontFamily).toEqual(1); // Virgil
|
||||||
fireEvent.click(screen.getByText(/code/i));
|
fireEvent.click(screen.getByTitle(/code/i));
|
||||||
expect(h.state.currentItemFontFamily).toEqual(3); // Cascadia
|
expect(h.state.currentItemFontFamily).toEqual(3); // Cascadia
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user