Skip to content

Commit

Permalink
use icons for toggle labels (#2315)
Browse files Browse the repository at this point in the history
  • Loading branch information
nschnierer committed Nov 1, 2020
1 parent 856ab50 commit 7491fcc
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 26 deletions.
124 changes: 105 additions & 19 deletions src/actions/actionProperties.tsx
Expand Up @@ -12,6 +12,7 @@ import {
canChangeSharpness,
} from "../scene";
import { ButtonSelect } from "../components/ButtonSelect";
import { ButtonIconSelect } from "../components/ButtonIconSelect";
import {
isTextElement,
redrawTextBoundingBox,
Expand All @@ -25,6 +26,20 @@ import { register } from "./register";
import { newElementWith } from "../element/mutateElement";
import { DEFAULT_FONT_SIZE, DEFAULT_FONT_FAMILY } from "../constants";
import { randomInteger } from "../random";
import {
FillHachureIcon,
FillCrossHatchIcon,
FillSolidIcon,
StrokeWidthIcon,
StrokeStyleSolidIcon,
StrokeStyleDashedIcon,
StrokeStyleDottedIcon,
EdgeSharpIcon,
EdgeRoundIcon,
SloppinessArchitectIcon,
SloppinessArtistIcon,
SloppinessCartoonistIcon,
} from "../components/icons";

const changeProperty = (
elements: readonly ExcalidrawElement[],
Expand Down Expand Up @@ -141,11 +156,23 @@ export const actionChangeFillStyle = register({
PanelComponent: ({ elements, appState, updateData }) => (
<fieldset>
<legend>{t("labels.fill")}</legend>
<ButtonSelect
<ButtonIconSelect
options={[
{ value: "hachure", text: t("labels.hachure") },
{ value: "cross-hatch", text: t("labels.crossHatch") },
{ value: "solid", text: t("labels.solid") },
{
value: "hachure",
text: t("labels.hachure"),
icon: <FillHachureIcon appearance={appState.appearance} />,
},
{
value: "cross-hatch",
text: t("labels.crossHatch"),
icon: <FillCrossHatchIcon appearance={appState.appearance} />,
},
{
value: "solid",
text: t("labels.solid"),
icon: <FillSolidIcon appearance={appState.appearance} />,
},
]}
group="fill"
value={getFormValue(
Expand Down Expand Up @@ -178,12 +205,39 @@ export const actionChangeStrokeWidth = register({
PanelComponent: ({ elements, appState, updateData }) => (
<fieldset>
<legend>{t("labels.strokeWidth")}</legend>
<ButtonSelect
<ButtonIconSelect
group="stroke-width"
options={[
{ value: 1, text: t("labels.thin") },
{ value: 2, text: t("labels.bold") },
{ value: 4, text: t("labels.extraBold") },
{
value: 1,
text: t("labels.thin"),
icon: (
<StrokeWidthIcon
appearance={appState.appearance}
strokeWidth={2}
/>
),
},
{
value: 2,
text: t("labels.bold"),
icon: (
<StrokeWidthIcon
appearance={appState.appearance}
strokeWidth={6}
/>
),
},
{
value: 4,
text: t("labels.extraBold"),
icon: (
<StrokeWidthIcon
appearance={appState.appearance}
strokeWidth={10}
/>
),
},
]}
value={getFormValue(
elements,
Expand Down Expand Up @@ -214,12 +268,24 @@ export const actionChangeSloppiness = register({
PanelComponent: ({ elements, appState, updateData }) => (
<fieldset>
<legend>{t("labels.sloppiness")}</legend>
<ButtonSelect
<ButtonIconSelect
group="sloppiness"
options={[
{ value: 0, text: t("labels.architect") },
{ value: 1, text: t("labels.artist") },
{ value: 2, text: t("labels.cartoonist") },
{
value: 0,
text: t("labels.architect"),
icon: <SloppinessArchitectIcon appearance={appState.appearance} />,
},
{
value: 1,
text: t("labels.artist"),
icon: <SloppinessArtistIcon appearance={appState.appearance} />,
},
{
value: 2,
text: t("labels.cartoonist"),
icon: <SloppinessCartoonistIcon appearance={appState.appearance} />,
},
]}
value={getFormValue(
elements,
Expand Down Expand Up @@ -249,12 +315,24 @@ export const actionChangeStrokeStyle = register({
PanelComponent: ({ elements, appState, updateData }) => (
<fieldset>
<legend>{t("labels.strokeStyle")}</legend>
<ButtonSelect
<ButtonIconSelect
group="strokeStyle"
options={[
{ value: "solid", text: t("labels.strokeStyle_solid") },
{ value: "dashed", text: t("labels.strokeStyle_dashed") },
{ value: "dotted", text: t("labels.strokeStyle_dotted") },
{
value: "solid",
text: t("labels.strokeStyle_solid"),
icon: <StrokeStyleSolidIcon appearance={appState.appearance} />,
},
{
value: "dashed",
text: t("labels.strokeStyle_dashed"),
icon: <StrokeStyleDashedIcon appearance={appState.appearance} />,
},
{
value: "dotted",
text: t("labels.strokeStyle_dotted"),
icon: <StrokeStyleDottedIcon appearance={appState.appearance} />,
},
]}
value={getFormValue(
elements,
Expand Down Expand Up @@ -488,11 +566,19 @@ export const actionChangeSharpness = register({
PanelComponent: ({ elements, appState, updateData }) => (
<fieldset>
<legend>{t("labels.edges")}</legend>
<ButtonSelect
<ButtonIconSelect
group="edges"
options={[
{ value: "sharp", text: t("labels.sharp") },
{ value: "round", text: t("labels.round") },
{
value: "sharp",
text: t("labels.sharp"),
icon: <EdgeSharpIcon appearance={appState.appearance} />,
},
{
value: "round",
text: t("labels.round"),
icon: <EdgeRoundIcon appearance={appState.appearance} />,
},
]}
value={getFormValue(
elements,
Expand Down
33 changes: 33 additions & 0 deletions src/components/ButtonIconSelect.tsx
@@ -0,0 +1,33 @@
import React from "react";
import clsx from "clsx";

// TODO: It might be "clever" to add option.icon to the existing component <ButtonSelect />
export const ButtonIconSelect = <T extends Object>({
options,
value,
onChange,
group,
}: {
options: { value: T; text: string; icon: JSX.Element }[];
value: T | null;
onChange: (value: T) => void;
group: string;
}) => (
<div className="buttonList buttonListIcon">
{options.map((option) => (
<label
key={option.text}
className={clsx({ active: value === option.value })}
title={option.text}
>
<input
type="radio"
name={group}
onChange={() => onChange(option.value)}
checked={value === option.value ? true : false}
/>
{option.icon}
</label>
))}
</div>
);
177 changes: 177 additions & 0 deletions src/components/icons.tsx
Expand Up @@ -504,3 +504,180 @@ export const UngroupIcon = React.memo(
{ width: 182, height: 182 },
),
);

export const FillHachureIcon = React.memo(
({ appearance }: { appearance: "light" | "dark" }) =>
createIcon(
<g stroke={iconFillColor(appearance)} fill="none">
<path d="M0 0s0 0 0 0m0 0s0 0 0 0m.133 12.04L10.63-.033M.133 12.04L10.63-.034M2.234 21.818L21.26-.07M2.234 21.818L21.26-.07m-8.395 21.852L31.89-.103M12.865 21.783L31.89-.103m-8.395 21.852L41.208 1.37M23.495 21.75L41.208 1.37m-7.083 20.343l7.216-8.302m-7.216 8.302l7.216-8.302" />
<path
d="M0 0h40M0 0h40m0 0v20m0-20v20m0 0H0m40 0H0m0 0V0m0 20V0"
strokeWidth={2}
/>
</g>,
{ width: 40, height: 20 },
),
);

export const FillCrossHatchIcon = React.memo(
({ appearance }: { appearance: "light" | "dark" }) =>
createIcon(
<g stroke={iconFillColor(appearance)} fill="none">
<path d="M0 0s0 0 0 0m0 0s0 0 0 0m.133 12.04L10.63-.033M.133 12.04L10.63-.034M2.234 21.818L21.26-.07M2.234 21.818L21.26-.07m-8.395 21.852L31.89-.103M12.865 21.783C17.87 16.025 22.875 10.266 31.89-.103m-8.395 21.852L41.208 1.37M23.495 21.75L41.208 1.37m-7.083 20.343l7.216-8.302m-7.216 8.302l7.216-8.302M-.09 19.92s0 0 0 0m0 0s0 0 0 0m12.04-.133L-.126 9.29m12.075 10.497L-.126 9.29m24.871 11.02C19.872 16.075 15 11.84.595-.684m24.15 20.994L.595-.684m36.19 20.861L12.636-.817m24.15 20.994L12.636-.817m30.909 16.269L24.676-.95m18.868 16.402L24.676-.95m18.833 5.771L37.472-.427m6.037 5.248L37.472-.427" />
<path
d="M0 0h40M0 0h40m0 0v20m0-20v20m0 0H0m40 0H0m0 0V0m0 20V0"
strokeWidth={2}
/>
</g>,
{ width: 40, height: 20 },
),
);

export const FillSolidIcon = React.memo(
({ appearance }: { appearance: "light" | "dark" }) =>
createIcon(
<>
<path d="M0 0h120v60H0" strokeWidth={0} />
<path
d="M0 0h40M0 0h40m0 0v20m0-20v20m0 0H0m40 0H0m0 0V0m0 20V0"
stroke={iconFillColor(appearance)}
strokeWidth={2}
fill="none"
/>
</>,
{ width: 40, height: 20 },
),
);

export const StrokeWidthIcon = React.memo(
({
appearance,
strokeWidth,
}: {
appearance: "light" | "dark";
strokeWidth: number;
}) =>
createIcon(
<path
d="M0 10h40M0 10h40"
stroke={iconFillColor(appearance)}
strokeWidth={strokeWidth}
fill="none"
/>,
{ width: 40, height: 20 },
),
);

export const StrokeStyleSolidIcon = React.memo(
({ appearance }: { appearance: "light" | "dark" }) =>
createIcon(
<path
d="M0 10h40M0 10h40"
stroke={iconFillColor(appearance)}
strokeWidth={2}
fill="none"
/>,
{ width: 40, height: 20 },
),
);

export const StrokeStyleDashedIcon = React.memo(
({ appearance }: { appearance: "light" | "dark" }) =>
createIcon(
<path
d="M3.286 9.998h32.759"
stroke={iconFillColor(appearance)}
strokeWidth={2.5}
fill="none"
strokeDasharray="12 8"
/>,
{ width: 40, height: 20 },
),
);

export const StrokeStyleDottedIcon = React.memo(
({ appearance }: { appearance: "light" | "dark" }) =>
createIcon(
<path
d="M0 10h40M0 10h40"
stroke={iconFillColor(appearance)}
strokeWidth={2}
fill="none"
strokeDasharray="3 6"
/>,
{ width: 40, height: 20 },
),
);

export const SloppinessArchitectIcon = React.memo(
({ appearance }: { appearance: "light" | "dark" }) =>
createIcon(
<path
d="M.268 17.938C4.05 15.093 19.414.725 22.96.868c3.547.143-4.149 16.266-1.41 17.928 2.738 1.662 14.866-6.632 17.84-7.958m-39.123 7.1C4.05 15.093 19.414.725 22.96.868c3.547.143-4.149 16.266-1.41 17.928 2.738 1.662 14.866-6.632 17.84-7.958"
stroke={iconFillColor(appearance)}
strokeWidth={2}
fill="none"
/>,
{ width: 40, height: 20 },
),
);

export const SloppinessArtistIcon = React.memo(
({ appearance }: { appearance: "light" | "dark" }) =>
createIcon(
<path
d="M2.663 18.134c3.963-2.578 18.855-12.098 22.675-12.68 3.82-.58-1.966 8.367.242 9.196 2.209.828 10.649-3.14 13.01-4.224M7.037 15.474c4.013-2.198 14.19-14.648 17.18-14.32 2.99.329-1.749 14.286.759 16.292 2.507 2.006 12.284-2.68 14.286-4.256"
stroke={iconFillColor(appearance)}
strokeWidth={2}
fill="none"
/>,
{ width: 40, height: 20 },
),
);

export const SloppinessCartoonistIcon = React.memo(
({ appearance }: { appearance: "light" | "dark" }) =>
createIcon(
<>
<path
d="M1.944 17.15C6.056 14.637 22.368 1.86 26.615 2.083c4.248.223-.992 14.695.815 16.406 1.807 1.71 8.355-5.117 10.026-6.14m-35.512 4.8C6.056 14.637 22.368 1.86 26.615 2.083c4.248.223-.992 14.695.815 16.406 1.807 1.71 8.355-5.117 10.026-6.14"
stroke={iconFillColor(appearance)}
strokeWidth={2}
fill="none"
/>
<path
d="M3.114 10.534c2.737-1.395 12.854-8.814 16.42-8.368 3.568.445 2.35 10.282 4.984 11.04 2.635.756 9.019-5.416 10.822-6.5M3.114 10.535c2.737-1.395 12.854-8.814 16.42-8.368 3.568.445 2.35 10.282 4.984 11.04 2.635.756 9.019-5.416 10.822-6.5"
stroke={iconFillColor(appearance)}
strokeWidth={2}
fill="none"
/>
</>,
{ width: 40, height: 20 },
),
);

export const EdgeSharpIcon = React.memo(
({ appearance }: { appearance: "light" | "dark" }) =>
createIcon(
<path
d="M9.18 19.68V6.346m0 13.336V6.345m0 0h29.599m-29.6 0h29.6"
stroke={iconFillColor(appearance)}
strokeWidth={2}
fill="none"
/>,
{ width: 40, height: 20 },
),
);

export const EdgeRoundIcon = React.memo(
({ appearance }: { appearance: "light" | "dark" }) =>
createIcon(
<path
d="M9.444 19.537c.484-2.119-2.1-10.449 2.904-12.71 5.004-2.263 22.601-.72 27.121-.863M9.444 19.537c.484-2.119-2.1-10.449 2.904-12.71 5.004-2.263 22.601-.72 27.121-.863"
stroke={iconFillColor(appearance)}
strokeWidth={2}
fill="none"
/>,
{ width: 40, height: 20 },
),
);

1 comment on commit 7491fcc

@vercel
Copy link

@vercel vercel bot commented on 7491fcc Nov 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.