update: show scroll arrows on narrow displays
This commit is contained in:
committed by
Nicolás Hatcher Andrés
parent
7bcd978998
commit
6c27ae1355
@@ -14,6 +14,8 @@ import {
|
|||||||
ArrowUpToLine,
|
ArrowUpToLine,
|
||||||
Bold,
|
Bold,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
DecimalsArrowLeft,
|
DecimalsArrowLeft,
|
||||||
DecimalsArrowRight,
|
DecimalsArrowRight,
|
||||||
Euro,
|
Euro,
|
||||||
@@ -36,7 +38,7 @@ import {
|
|||||||
Undo2,
|
Undo2,
|
||||||
WrapText,
|
WrapText,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ArrowMiddleFromLine } from "../../icons";
|
import { ArrowMiddleFromLine } from "../../icons";
|
||||||
import { theme } from "../../theme";
|
import { theme } from "../../theme";
|
||||||
@@ -94,443 +96,494 @@ function Toolbar(properties: ToolbarProperties) {
|
|||||||
const [fillColorPickerOpen, setFillColorPickerOpen] = useState(false);
|
const [fillColorPickerOpen, setFillColorPickerOpen] = useState(false);
|
||||||
const [borderPickerOpen, setBorderPickerOpen] = useState(false);
|
const [borderPickerOpen, setBorderPickerOpen] = useState(false);
|
||||||
const [nameManagerDialogOpen, setNameManagerDialogOpen] = useState(false);
|
const [nameManagerDialogOpen, setNameManagerDialogOpen] = useState(false);
|
||||||
|
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
||||||
|
const [showRightArrow, setShowRightArrow] = useState(false);
|
||||||
|
|
||||||
const fontColorButton = useRef(null);
|
const fontColorButton = useRef(null);
|
||||||
const fillColorButton = useRef(null);
|
const fillColorButton = useRef(null);
|
||||||
const borderButton = useRef(null);
|
const borderButton = useRef(null);
|
||||||
|
const toolbarRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { canEdit } = properties;
|
const { canEdit } = properties;
|
||||||
|
|
||||||
|
const scrollLeft = () =>
|
||||||
|
toolbarRef.current?.scrollBy({ left: -200, behavior: "smooth" });
|
||||||
|
const scrollRight = () =>
|
||||||
|
toolbarRef.current?.scrollBy({ left: 200, behavior: "smooth" });
|
||||||
|
|
||||||
|
const updateArrows = useCallback(() => {
|
||||||
|
if (!toolbarRef.current) return;
|
||||||
|
const { scrollLeft, scrollWidth, clientWidth } = toolbarRef.current;
|
||||||
|
setShowLeftArrow(scrollLeft > 0);
|
||||||
|
setShowRightArrow(scrollLeft < scrollWidth - clientWidth);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const toolbar = toolbarRef.current;
|
||||||
|
if (!toolbar) return;
|
||||||
|
|
||||||
|
updateArrows();
|
||||||
|
toolbar.addEventListener("scroll", updateArrows);
|
||||||
|
return () => toolbar.removeEventListener("scroll", updateArrows);
|
||||||
|
}, [updateArrows]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolbarContainer>
|
<ToolbarWrapper>
|
||||||
{/* History/Edit Group */}
|
{showLeftArrow && (
|
||||||
<ButtonGroup>
|
<ScrollArrow
|
||||||
<StyledButton
|
$direction="left"
|
||||||
type="button"
|
onClick={scrollLeft}
|
||||||
$pressed={false}
|
title={t("toolbar.scroll_left")}
|
||||||
onClick={properties.onUndo}
|
|
||||||
disabled={!properties.canUndo}
|
|
||||||
title={t("toolbar.undo")}
|
|
||||||
>
|
>
|
||||||
<Undo2 />
|
<ChevronLeft />
|
||||||
</StyledButton>
|
</ScrollArrow>
|
||||||
<StyledButton
|
)}
|
||||||
type="button"
|
<ToolbarContainer ref={toolbarRef}>
|
||||||
$pressed={false}
|
{/* History/Edit Group */}
|
||||||
onClick={properties.onRedo}
|
<ButtonGroup>
|
||||||
disabled={!properties.canRedo}
|
<StyledButton
|
||||||
title={t("toolbar.redo")}
|
type="button"
|
||||||
>
|
$pressed={false}
|
||||||
<Redo2 />
|
onClick={properties.onUndo}
|
||||||
</StyledButton>
|
disabled={!properties.canUndo}
|
||||||
</ButtonGroup>
|
title={t("toolbar.undo")}
|
||||||
|
>
|
||||||
|
<Undo2 />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={false}
|
||||||
|
onClick={properties.onRedo}
|
||||||
|
disabled={!properties.canRedo}
|
||||||
|
title={t("toolbar.redo")}
|
||||||
|
>
|
||||||
|
<Redo2 />
|
||||||
|
</StyledButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
{/* Format Tools Group */}
|
{/* Format Tools Group */}
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
type="button"
|
type="button"
|
||||||
$pressed={false}
|
$pressed={false}
|
||||||
onClick={properties.onCopyStyles}
|
onClick={properties.onCopyStyles}
|
||||||
title={t("toolbar.copy_styles")}
|
title={t("toolbar.copy_styles")}
|
||||||
>
|
>
|
||||||
<PaintRoller />
|
<PaintRoller />
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
type="button"
|
type="button"
|
||||||
$pressed={false}
|
$pressed={false}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
properties.onClearFormatting();
|
properties.onClearFormatting();
|
||||||
}}
|
}}
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
title={t("toolbar.clear_formatting")}
|
title={t("toolbar.clear_formatting")}
|
||||||
>
|
>
|
||||||
<RemoveFormatting />
|
<RemoveFormatting />
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
{/* Number Format Group */}
|
{/* Number Format Group */}
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
type="button"
|
type="button"
|
||||||
$pressed={false}
|
$pressed={false}
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
properties.onNumberFormatPicked(NumberFormats.CURRENCY_EUR);
|
properties.onNumberFormatPicked(NumberFormats.CURRENCY_EUR);
|
||||||
}}
|
}}
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
title={t("toolbar.euro")}
|
title={t("toolbar.euro")}
|
||||||
>
|
>
|
||||||
<Euro />
|
<Euro />
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
type="button"
|
type="button"
|
||||||
$pressed={false}
|
$pressed={false}
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
properties.onNumberFormatPicked(NumberFormats.PERCENTAGE);
|
properties.onNumberFormatPicked(NumberFormats.PERCENTAGE);
|
||||||
}}
|
}}
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
title={t("toolbar.percentage")}
|
title={t("toolbar.percentage")}
|
||||||
>
|
>
|
||||||
<Percent />
|
<Percent />
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
type="button"
|
type="button"
|
||||||
$pressed={false}
|
$pressed={false}
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
properties.onNumberFormatPicked(
|
properties.onNumberFormatPicked(
|
||||||
decreaseDecimalPlaces(properties.numFmt),
|
decreaseDecimalPlaces(properties.numFmt),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
title={t("toolbar.decimal_places_decrease")}
|
title={t("toolbar.decimal_places_decrease")}
|
||||||
>
|
>
|
||||||
<DecimalsArrowLeft />
|
<DecimalsArrowLeft />
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
type="button"
|
type="button"
|
||||||
$pressed={false}
|
$pressed={false}
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
properties.onNumberFormatPicked(
|
properties.onNumberFormatPicked(
|
||||||
increaseDecimalPlaces(properties.numFmt),
|
increaseDecimalPlaces(properties.numFmt),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
title={t("toolbar.decimal_places_increase")}
|
title={t("toolbar.decimal_places_increase")}
|
||||||
>
|
>
|
||||||
<DecimalsArrowRight />
|
<DecimalsArrowRight />
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
<FormatMenu
|
<FormatMenu
|
||||||
numFmt={properties.numFmt}
|
numFmt={properties.numFmt}
|
||||||
onChange={(numberFmt): void => {
|
onChange={(numberFmt): void => {
|
||||||
properties.onNumberFormatPicked(numberFmt);
|
properties.onNumberFormatPicked(numberFmt);
|
||||||
}}
|
}}
|
||||||
onExited={(): void => {}}
|
onExited={(): void => {}}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
horizontal: 20, // Aligning the menu to the middle of FormatButton
|
horizontal: 20, // Aligning the menu to the middle of FormatButton
|
||||||
vertical: "bottom",
|
vertical: "bottom",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={false}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.format_number")}
|
||||||
|
sx={{
|
||||||
|
width: "40px", // Keep in sync with anchorOrigin in FormatMenu above
|
||||||
|
padding: "0px 4px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{"123"}
|
||||||
|
<ChevronDown />
|
||||||
|
</StyledButton>
|
||||||
|
</FormatMenu>
|
||||||
|
</ButtonGroup>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
{/* Font Size Group */}
|
||||||
|
<ButtonGroup>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
type="button"
|
type="button"
|
||||||
$pressed={false}
|
$pressed={false}
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
title={t("toolbar.format_number")}
|
onClick={() => {
|
||||||
sx={{
|
properties.onIncreaseFontSize(-1);
|
||||||
width: "40px", // Keep in sync with anchorOrigin in FormatMenu above
|
|
||||||
padding: "0px 4px",
|
|
||||||
}}
|
}}
|
||||||
|
title={t("toolbar.decrease_font_size")}
|
||||||
>
|
>
|
||||||
{"123"}
|
<Minus />
|
||||||
<ChevronDown />
|
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
</FormatMenu>
|
<FontSizeBox>{properties.fontSize}</FontSizeBox>
|
||||||
</ButtonGroup>
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={false}
|
||||||
|
disabled={!canEdit}
|
||||||
|
onClick={() => {
|
||||||
|
properties.onIncreaseFontSize(1);
|
||||||
|
}}
|
||||||
|
title={t("toolbar.increase_font_size")}
|
||||||
|
>
|
||||||
|
<Plus />
|
||||||
|
</StyledButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
{/* Font Size Group */}
|
{/* Text Style Group */}
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
type="button"
|
type="button"
|
||||||
$pressed={false}
|
$pressed={properties.bold}
|
||||||
disabled={!canEdit}
|
onClick={() => properties.onToggleBold(!properties.bold)}
|
||||||
onClick={() => {
|
disabled={!canEdit}
|
||||||
properties.onIncreaseFontSize(-1);
|
title={t("toolbar.bold")}
|
||||||
|
>
|
||||||
|
<Bold />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={properties.italic}
|
||||||
|
onClick={() => properties.onToggleItalic(!properties.italic)}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.italic")}
|
||||||
|
>
|
||||||
|
<Italic />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={properties.underline}
|
||||||
|
onClick={() => properties.onToggleUnderline(!properties.underline)}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.underline")}
|
||||||
|
>
|
||||||
|
<Underline />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={properties.strike}
|
||||||
|
onClick={() => properties.onToggleStrike(!properties.strike)}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.strike_through")}
|
||||||
|
>
|
||||||
|
<Strikethrough />
|
||||||
|
</StyledButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
{/* Color & Border Group */}
|
||||||
|
<ButtonGroup>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={false}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.font_color")}
|
||||||
|
ref={fontColorButton}
|
||||||
|
onClick={() => setFontColorPickerOpen(true)}
|
||||||
|
>
|
||||||
|
<Type />
|
||||||
|
<ColorLine color={properties.fontColor} />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={false}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.fill_color")}
|
||||||
|
ref={fillColorButton}
|
||||||
|
onClick={() => setFillColorPickerOpen(true)}
|
||||||
|
>
|
||||||
|
<PaintBucket />
|
||||||
|
<ColorLine color={properties.fillColor} />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={false}
|
||||||
|
onClick={() => setBorderPickerOpen(true)}
|
||||||
|
ref={borderButton}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.borders.title")}
|
||||||
|
>
|
||||||
|
<Grid2X2 />
|
||||||
|
</StyledButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
{/* Alignment Group */}
|
||||||
|
<ButtonGroup>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={properties.horizontalAlign === "left"}
|
||||||
|
onClick={() =>
|
||||||
|
properties.onToggleHorizontalAlign(
|
||||||
|
properties.horizontalAlign === "left" ? "general" : "left",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.align_left")}
|
||||||
|
>
|
||||||
|
<AlignLeft />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={properties.horizontalAlign === "center"}
|
||||||
|
onClick={() =>
|
||||||
|
properties.onToggleHorizontalAlign(
|
||||||
|
properties.horizontalAlign === "center" ? "general" : "center",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.align_center")}
|
||||||
|
>
|
||||||
|
<AlignCenter />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={properties.horizontalAlign === "right"}
|
||||||
|
onClick={() =>
|
||||||
|
properties.onToggleHorizontalAlign(
|
||||||
|
properties.horizontalAlign === "right" ? "general" : "right",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.align_right")}
|
||||||
|
>
|
||||||
|
<AlignRight />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={properties.verticalAlign === "top"}
|
||||||
|
onClick={() => properties.onToggleVerticalAlign("top")}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.vertical_align_top")}
|
||||||
|
>
|
||||||
|
<ArrowUpToLine />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={properties.verticalAlign === "center"}
|
||||||
|
onClick={() => properties.onToggleVerticalAlign("center")}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.vertical_align_middle")}
|
||||||
|
>
|
||||||
|
<ArrowMiddleFromLine />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={properties.verticalAlign === "bottom"}
|
||||||
|
onClick={() => properties.onToggleVerticalAlign("bottom")}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.vertical_align_bottom")}
|
||||||
|
>
|
||||||
|
<ArrowDownToLine />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={properties.wrapText === true}
|
||||||
|
onClick={() => {
|
||||||
|
properties.onToggleWrapText(!properties.wrapText);
|
||||||
|
}}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.wrap_text")}
|
||||||
|
>
|
||||||
|
<WrapText />
|
||||||
|
</StyledButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
{/* View & Tools Group */}
|
||||||
|
<ButtonGroup>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={false}
|
||||||
|
onClick={() =>
|
||||||
|
properties.onToggleShowGridLines(!properties.showGridLines)
|
||||||
|
}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.show_hide_grid_lines")}
|
||||||
|
>
|
||||||
|
{properties.showGridLines ? <Grid2x2Check /> : <Grid2x2X />}
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={false}
|
||||||
|
onClick={() => {
|
||||||
|
setNameManagerDialogOpen(true);
|
||||||
|
}}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.name_manager")}
|
||||||
|
>
|
||||||
|
<Tags />
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
type="button"
|
||||||
|
$pressed={false}
|
||||||
|
onClick={() => {
|
||||||
|
properties.onDownloadPNG();
|
||||||
|
}}
|
||||||
|
disabled={!canEdit}
|
||||||
|
title={t("toolbar.selected_png")}
|
||||||
|
>
|
||||||
|
<ImageDown />
|
||||||
|
</StyledButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
|
||||||
|
<ColorPicker
|
||||||
|
color={properties.fontColor}
|
||||||
|
defaultColor="#000000"
|
||||||
|
title={t("color_picker.default")}
|
||||||
|
onChange={(color): void => {
|
||||||
|
properties.onTextColorPicked(color);
|
||||||
|
setFontColorPickerOpen(false);
|
||||||
}}
|
}}
|
||||||
title={t("toolbar.decrease_font_size")}
|
onClose={() => {
|
||||||
>
|
setFontColorPickerOpen(false);
|
||||||
<Minus />
|
|
||||||
</StyledButton>
|
|
||||||
<FontSizeBox>{properties.fontSize}</FontSizeBox>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={false}
|
|
||||||
disabled={!canEdit}
|
|
||||||
onClick={() => {
|
|
||||||
properties.onIncreaseFontSize(1);
|
|
||||||
}}
|
}}
|
||||||
title={t("toolbar.increase_font_size")}
|
anchorEl={fontColorButton}
|
||||||
>
|
open={fontColorPickerOpen}
|
||||||
<Plus />
|
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
||||||
</StyledButton>
|
transformOrigin={{ vertical: "top", horizontal: "left" }}
|
||||||
</ButtonGroup>
|
/>
|
||||||
|
<ColorPicker
|
||||||
<Divider />
|
color={properties.fillColor}
|
||||||
|
defaultColor=""
|
||||||
{/* Text Style Group */}
|
title={t("color_picker.default")}
|
||||||
<ButtonGroup>
|
onChange={(color): void => {
|
||||||
<StyledButton
|
if (color !== null) {
|
||||||
type="button"
|
properties.onFillColorPicked(color);
|
||||||
$pressed={properties.bold}
|
}
|
||||||
onClick={() => properties.onToggleBold(!properties.bold)}
|
setFillColorPickerOpen(false);
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.bold")}
|
|
||||||
>
|
|
||||||
<Bold />
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={properties.italic}
|
|
||||||
onClick={() => properties.onToggleItalic(!properties.italic)}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.italic")}
|
|
||||||
>
|
|
||||||
<Italic />
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={properties.underline}
|
|
||||||
onClick={() => properties.onToggleUnderline(!properties.underline)}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.underline")}
|
|
||||||
>
|
|
||||||
<Underline />
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={properties.strike}
|
|
||||||
onClick={() => properties.onToggleStrike(!properties.strike)}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.strike_through")}
|
|
||||||
>
|
|
||||||
<Strikethrough />
|
|
||||||
</StyledButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{/* Color & Border Group */}
|
|
||||||
<ButtonGroup>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={false}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.font_color")}
|
|
||||||
ref={fontColorButton}
|
|
||||||
onClick={() => setFontColorPickerOpen(true)}
|
|
||||||
>
|
|
||||||
<Type />
|
|
||||||
<ColorLine color={properties.fontColor} />
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={false}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.fill_color")}
|
|
||||||
ref={fillColorButton}
|
|
||||||
onClick={() => setFillColorPickerOpen(true)}
|
|
||||||
>
|
|
||||||
<PaintBucket />
|
|
||||||
<ColorLine color={properties.fillColor} />
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={false}
|
|
||||||
onClick={() => setBorderPickerOpen(true)}
|
|
||||||
ref={borderButton}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.borders.title")}
|
|
||||||
>
|
|
||||||
<Grid2X2 />
|
|
||||||
</StyledButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{/* Alignment Group */}
|
|
||||||
<ButtonGroup>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={properties.horizontalAlign === "left"}
|
|
||||||
onClick={() =>
|
|
||||||
properties.onToggleHorizontalAlign(
|
|
||||||
properties.horizontalAlign === "left" ? "general" : "left",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.align_left")}
|
|
||||||
>
|
|
||||||
<AlignLeft />
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={properties.horizontalAlign === "center"}
|
|
||||||
onClick={() =>
|
|
||||||
properties.onToggleHorizontalAlign(
|
|
||||||
properties.horizontalAlign === "center" ? "general" : "center",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.align_center")}
|
|
||||||
>
|
|
||||||
<AlignCenter />
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={properties.horizontalAlign === "right"}
|
|
||||||
onClick={() =>
|
|
||||||
properties.onToggleHorizontalAlign(
|
|
||||||
properties.horizontalAlign === "right" ? "general" : "right",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.align_right")}
|
|
||||||
>
|
|
||||||
<AlignRight />
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={properties.verticalAlign === "top"}
|
|
||||||
onClick={() => properties.onToggleVerticalAlign("top")}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.vertical_align_top")}
|
|
||||||
>
|
|
||||||
<ArrowUpToLine />
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={properties.verticalAlign === "center"}
|
|
||||||
onClick={() => properties.onToggleVerticalAlign("center")}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.vertical_align_middle")}
|
|
||||||
>
|
|
||||||
<ArrowMiddleFromLine />
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={properties.verticalAlign === "bottom"}
|
|
||||||
onClick={() => properties.onToggleVerticalAlign("bottom")}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.vertical_align_bottom")}
|
|
||||||
>
|
|
||||||
<ArrowDownToLine />
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={properties.wrapText === true}
|
|
||||||
onClick={() => {
|
|
||||||
properties.onToggleWrapText(!properties.wrapText);
|
|
||||||
}}
|
}}
|
||||||
disabled={!canEdit}
|
onClose={() => {
|
||||||
title={t("toolbar.wrap_text")}
|
setFillColorPickerOpen(false);
|
||||||
>
|
|
||||||
<WrapText />
|
|
||||||
</StyledButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{/* View & Tools Group */}
|
|
||||||
<ButtonGroup>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={false}
|
|
||||||
onClick={() =>
|
|
||||||
properties.onToggleShowGridLines(!properties.showGridLines)
|
|
||||||
}
|
|
||||||
disabled={!canEdit}
|
|
||||||
title={t("toolbar.show_hide_grid_lines")}
|
|
||||||
>
|
|
||||||
{properties.showGridLines ? <Grid2x2Check /> : <Grid2x2X />}
|
|
||||||
</StyledButton>
|
|
||||||
<StyledButton
|
|
||||||
type="button"
|
|
||||||
$pressed={false}
|
|
||||||
onClick={() => {
|
|
||||||
setNameManagerDialogOpen(true);
|
|
||||||
}}
|
}}
|
||||||
disabled={!canEdit}
|
anchorEl={fillColorButton}
|
||||||
title={t("toolbar.name_manager")}
|
open={fillColorPickerOpen}
|
||||||
>
|
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
||||||
<Tags />
|
transformOrigin={{ vertical: "top", horizontal: "left" }}
|
||||||
</StyledButton>
|
/>
|
||||||
<StyledButton
|
<BorderPicker
|
||||||
type="button"
|
onChange={(border): void => {
|
||||||
$pressed={false}
|
properties.onBorderChanged(border);
|
||||||
onClick={() => {
|
|
||||||
properties.onDownloadPNG();
|
|
||||||
}}
|
}}
|
||||||
disabled={!canEdit}
|
onClose={() => {
|
||||||
title={t("toolbar.selected_png")}
|
setBorderPickerOpen(false);
|
||||||
|
}}
|
||||||
|
anchorEl={borderButton}
|
||||||
|
open={borderPickerOpen}
|
||||||
|
/>
|
||||||
|
<NameManagerDialog
|
||||||
|
open={nameManagerDialogOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setNameManagerDialogOpen(false);
|
||||||
|
}}
|
||||||
|
model={properties.nameManagerProperties}
|
||||||
|
/>
|
||||||
|
</ToolbarContainer>
|
||||||
|
{showRightArrow && (
|
||||||
|
<ScrollArrow
|
||||||
|
$direction="right"
|
||||||
|
onClick={scrollRight}
|
||||||
|
title={t("toolbar.scroll_right")}
|
||||||
>
|
>
|
||||||
<ImageDown />
|
<ChevronRight />
|
||||||
</StyledButton>
|
</ScrollArrow>
|
||||||
</ButtonGroup>
|
)}
|
||||||
|
</ToolbarWrapper>
|
||||||
<ColorPicker
|
|
||||||
color={properties.fontColor}
|
|
||||||
defaultColor="#000000"
|
|
||||||
title={t("color_picker.default")}
|
|
||||||
onChange={(color): void => {
|
|
||||||
properties.onTextColorPicked(color);
|
|
||||||
setFontColorPickerOpen(false);
|
|
||||||
}}
|
|
||||||
onClose={() => {
|
|
||||||
setFontColorPickerOpen(false);
|
|
||||||
}}
|
|
||||||
anchorEl={fontColorButton}
|
|
||||||
open={fontColorPickerOpen}
|
|
||||||
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
|
||||||
transformOrigin={{ vertical: "top", horizontal: "left" }}
|
|
||||||
/>
|
|
||||||
<ColorPicker
|
|
||||||
color={properties.fillColor}
|
|
||||||
defaultColor=""
|
|
||||||
title={t("color_picker.default")}
|
|
||||||
onChange={(color): void => {
|
|
||||||
if (color !== null) {
|
|
||||||
properties.onFillColorPicked(color);
|
|
||||||
}
|
|
||||||
setFillColorPickerOpen(false);
|
|
||||||
}}
|
|
||||||
onClose={() => {
|
|
||||||
setFillColorPickerOpen(false);
|
|
||||||
}}
|
|
||||||
anchorEl={fillColorButton}
|
|
||||||
open={fillColorPickerOpen}
|
|
||||||
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
|
||||||
transformOrigin={{ vertical: "top", horizontal: "left" }}
|
|
||||||
/>
|
|
||||||
<BorderPicker
|
|
||||||
onChange={(border): void => {
|
|
||||||
properties.onBorderChanged(border);
|
|
||||||
}}
|
|
||||||
onClose={() => {
|
|
||||||
setBorderPickerOpen(false);
|
|
||||||
}}
|
|
||||||
anchorEl={borderButton}
|
|
||||||
open={borderPickerOpen}
|
|
||||||
/>
|
|
||||||
<NameManagerDialog
|
|
||||||
open={nameManagerDialogOpen}
|
|
||||||
onClose={() => {
|
|
||||||
setNameManagerDialogOpen(false);
|
|
||||||
}}
|
|
||||||
model={properties.nameManagerProperties}
|
|
||||||
/>
|
|
||||||
</ToolbarContainer>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ToolbarContainer = styled("div")`
|
const ToolbarWrapper = styled("div")`
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: ${({ theme }) => theme.palette.background.paper};
|
background: ${({ theme }) => theme.palette.background.paper};
|
||||||
height: ${TOOLBAR_HEIGHT}px;
|
height: ${TOOLBAR_HEIGHT}px;
|
||||||
line-height: ${TOOLBAR_HEIGHT}px;
|
|
||||||
border-bottom: 1px solid ${({ theme }) => theme.palette.grey["300"]};
|
border-bottom: 1px solid ${({ theme }) => theme.palette.grey["300"]};
|
||||||
font-family: Inter;
|
|
||||||
border-radius: 4px 4px 0px 0px;
|
border-radius: 4px 4px 0px 0px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ToolbarContainer = styled("div")`
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
align-items: center;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
padding: 0px 12px;
|
padding: 0px 12px;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type TypeButtonProperties = { $pressed: boolean };
|
type TypeButtonProperties = { $pressed: boolean };
|
||||||
@@ -617,4 +670,33 @@ const ButtonGroup = styled("div")({
|
|||||||
gap: "4px",
|
gap: "4px",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type ScrollArrowProps = { $direction: "left" | "right" };
|
||||||
|
const ScrollArrow = styled("button", {
|
||||||
|
shouldForwardProp: (prop) => prop !== "$direction",
|
||||||
|
})<ScrollArrowProps>(({ $direction }) => ({
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
transform: "translateY(-50%)",
|
||||||
|
[$direction]: "0px",
|
||||||
|
zIndex: 10,
|
||||||
|
width: "24px",
|
||||||
|
height: "100%",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
backgroundColor: "white",
|
||||||
|
border:
|
||||||
|
$direction === "left"
|
||||||
|
? `none; border-right: 1px solid ${theme.palette.grey["300"]};`
|
||||||
|
: `none; border-left: 1px solid ${theme.palette.grey["300"]};`,
|
||||||
|
cursor: "pointer",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: theme.palette.grey["100"],
|
||||||
|
},
|
||||||
|
svg: {
|
||||||
|
width: "16px",
|
||||||
|
height: "16px",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
export default Toolbar;
|
export default Toolbar;
|
||||||
|
|||||||
@@ -27,6 +27,8 @@
|
|||||||
"vertical_align_top": "Align top",
|
"vertical_align_top": "Align top",
|
||||||
"selected_png": "Export Selected area as PNG",
|
"selected_png": "Export Selected area as PNG",
|
||||||
"wrap_text": "Wrap text",
|
"wrap_text": "Wrap text",
|
||||||
|
"scroll_left": "Scroll left",
|
||||||
|
"scroll_right": "Scroll right",
|
||||||
"format_menu": {
|
"format_menu": {
|
||||||
"auto": "Auto",
|
"auto": "Auto",
|
||||||
"number": "Number",
|
"number": "Number",
|
||||||
|
|||||||
Reference in New Issue
Block a user