diff --git a/public/index.html b/public/index.html index 36434bf33..562a26833 100644 --- a/public/index.html +++ b/public/index.html @@ -81,7 +81,7 @@ - +

Excalidraw

diff --git a/src/actions/actionCanvas.tsx b/src/actions/actionCanvas.tsx index c1c31a0da..aa912cb9b 100644 --- a/src/actions/actionCanvas.tsx +++ b/src/actions/actionCanvas.tsx @@ -3,7 +3,7 @@ import { Action } from "./types"; import { ColorPicker } from "../components/ColorPicker"; import { getDefaultAppState } from "../appState"; import { trash } from "../components/icons"; -import { ToolIcon } from "../components/ToolIcon"; +import { ToolButton } from "../components/ToolButton"; export const actionChangeViewBackgroundColor: Action = { name: "changeViewBackgroundColor", @@ -14,6 +14,7 @@ export const actionChangeViewBackgroundColor: Action = { return (
updateData(color)} @@ -32,7 +33,7 @@ export const actionClearCanvas: Action = { }; }, PanelComponent: ({ updateData, t }) => ( - ( - ( - ( <> -
{t("labels.stroke")}
+ ( <> -
{t("labels.background")}
+ ( - <> -
{t("labels.fill")}
+
+ {t("labels.fill")} - +
), }; @@ -136,9 +139,10 @@ export const actionChangeStrokeWidth: Action = { }; }, PanelComponent: ({ elements, appState, updateData, t }) => ( - <> -
{t("labels.strokeWidth")}
+
+ {t("labels.strokeWidth")} updateData(value)} /> - +
), }; @@ -167,9 +171,10 @@ export const actionChangeSloppiness: Action = { }; }, PanelComponent: ({ elements, appState, updateData, t }) => ( - <> -
{t("labels.sloppiness")}
+
+ {t("labels.sloppiness")} updateData(value)} /> - +
), }; @@ -198,8 +203,8 @@ export const actionChangeOpacity: Action = { }; }, PanelComponent: ({ elements, appState, updateData, t }) => ( - <> -
{t("labels.opacity")}
+ ), }; @@ -238,9 +243,10 @@ export const actionChangeFontSize: Action = { }; }, PanelComponent: ({ elements, appState, updateData, t }) => ( - <> -
{t("labels.fontSize")}
+
+ {t("labels.fontSize")} updateData(value)} /> - +
), }; @@ -278,9 +284,10 @@ export const actionChangeFontFamily: Action = { }; }, PanelComponent: ({ elements, appState, updateData, t }) => ( - <> -
{t("labels.fontFamily")}
+
+ {t("labels.fontFamily")} updateData(value)} /> - +
), }; diff --git a/src/components/ButtonSelect.tsx b/src/components/ButtonSelect.tsx index a345b8b02..0177c4b38 100644 --- a/src/components/ButtonSelect.tsx +++ b/src/components/ButtonSelect.tsx @@ -4,21 +4,28 @@ export function ButtonSelect({ options, value, onChange, + group, }: { options: { value: T; text: string }[]; value: T | null; onChange: (value: T) => void; + group: string; }) { return (
{options.map(option => ( - + ))}
); diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx index 2885203cd..956a215cf 100644 --- a/src/components/ColorPicker.tsx +++ b/src/components/ColorPicker.tsx @@ -10,10 +10,12 @@ const Picker = function({ colors, color, onChange, + label, }: { colors: string[]; color: string | null; onChange: (color: string) => void; + label: string; }) { return (
@@ -42,6 +44,7 @@ const Picker = function({
{ onChange(color); }} @@ -54,9 +57,11 @@ const Picker = function({ function ColorInput({ color, onChange, + label, }: { color: string | null; onChange: (color: string) => void; + label: string; }) { const colorRegex = /^([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8}|transparent)$/; const [innerValue, setInnerValue] = React.useState(color); @@ -71,7 +76,7 @@ function ColorInput({ { const value = e.target.value; if (value.match(colorRegex)) { @@ -91,10 +96,12 @@ export function ColorPicker({ type, color, onChange, + label, }: { type: "canvasBackground" | "elementBackground" | "elementStroke"; color: string | null; onChange: (color: string) => void; + label: string; }) { const [isActive, setActive] = React.useState(false); @@ -103,12 +110,13 @@ export function ColorPicker({
+ ); + + return ( + + ); +} diff --git a/src/components/ToolIcon.tsx b/src/components/ToolIcon.tsx deleted file mode 100644 index 84a2a5067..000000000 --- a/src/components/ToolIcon.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import "./ToolIcon.scss"; - -import React from "react"; - -type ToolIconSize = "s" | "m"; - -type ToolIconProps = - | { - type: "button"; - icon: React.ReactNode; - "aria-label": string; - title?: string; - name?: string; - id?: string; - onClick?(): void; - size?: ToolIconSize; - } - | { - type: "radio"; - icon: React.ReactNode; - title?: string; - name?: string; - id?: string; - checked: boolean; - onChange?(): void; - size?: ToolIconSize; - }; - -const DEFAULT_SIZE: ToolIconSize = "m"; - -export function ToolIcon(props: ToolIconProps) { - const sizeCn = `ToolIcon_size_${props.size || DEFAULT_SIZE}`; - - if (props.type === "button") - return ( - - ); - - return ( - - ); -} diff --git a/src/index.tsx b/src/index.tsx index 56acd9feb..ac50de211 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -80,7 +80,7 @@ import { getDefaultAppState } from "./appState"; import { Island } from "./components/Island"; import Stack from "./components/Stack"; import { FixedSideContainer } from "./components/FixedSideContainer"; -import { ToolIcon } from "./components/ToolIcon"; +import { ToolButton } from "./components/ToolButton"; import { LockIcon } from "./components/LockIcon"; import { ExportDialog } from "./components/ExportDialog"; import { withTranslation } from "react-i18next"; @@ -501,7 +501,7 @@ export class App extends React.Component { {SHAPES.map(({ value, icon }, index) => { const label = t(`toolBar.${value}`); return ( - { title={`${capitalizeString(label)} — ${ capitalizeString(value)[0] }, ${index + 1}`} + aria-label={capitalizeString(label)} + aria-keyshortcuts={`${label[0]} ${index + 1}`} onChange={() => { this.setState({ elementType: value }); elements = clearSelection(elements); @@ -517,7 +519,7 @@ export class App extends React.Component { value === "text" ? CURSOR_TYPE.TEXT : CURSOR_TYPE.CROSSHAIR; this.forceUpdate(); }} - > + > ); })} {this.renderShapeLock()} @@ -610,6 +612,7 @@ export class App extends React.Component {
+

Canvas actions

{this.renderCanvasActions()}
@@ -618,6 +621,7 @@ export class App extends React.Component { +

Shapes

{this.renderShapesSwitcher()}
diff --git a/src/styles.scss b/src/styles.scss index 148b15345..b85e574fd 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -33,24 +33,52 @@ body { display: flex; flex-direction: column; - h5 { + h3, + legend, + .control-label { margin-top: 0.333rem; - margin-bottom: 0.333em; + margin-bottom: 0.333rem; font-size: 0.75rem; color: var(--text-color-primary); + font-weight: bold; + display: block; } - h5:first-child { + .control-label input { + display: block; + width: 100%; + } + + h3:first-child, + legend:first-child, + .control-label:first-child { margin-top: 0; } + legend { + padding: 0; + } + .buttonList { flex-wrap: wrap; - button { + label { margin-right: 0.25rem; font-size: 0.75rem; + display: inline-block; } + + input[type="radio"] { + opacity: 0; + position: absolute; + } + } + + fieldset { + margin: 0; + margin-top: 0.333rem; + padding: 0; + border: none; } } @@ -65,7 +93,8 @@ input:focus { box-shadow: 0 0 0 2px #a5d8ff; } -button { +button, +.buttonList label { background-color: #e9ecef; border: 0; border-radius: 4px; @@ -92,7 +121,8 @@ button { } } -.active { +.active, +.buttonList label.active { background-color: #ced4da; &:hover { background-color: #ced4da; @@ -216,3 +246,13 @@ button { background-color: #ced4da; } } + +.visually-hidden { + position: absolute !important; + height: 1px; + width: 1px; + overflow: hidden; + clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ + clip: rect(1px, 1px, 1px, 1px); + white-space: nowrap; /* added line */ +}