mirror of
https://github.com/r2r90/canvas-label.git
synced 2025-12-16 21:19:38 +00:00
text edit finition
This commit is contained in:
@@ -2,10 +2,7 @@
|
||||
|
||||
import * as React from "react";
|
||||
import fonts from "../assets/fonts.json";
|
||||
import {
|
||||
CaretSortIcon,
|
||||
CheckIcon,
|
||||
} from "@radix-ui/react-icons";
|
||||
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
@@ -26,15 +23,14 @@ import {
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
type PopoverTriggerProps = React.ComponentPropsWithoutRef<
|
||||
typeof PopoverTrigger
|
||||
>;
|
||||
type Props = {
|
||||
handleTextFontFamilyChange: (fontFamiy: string | undefined) => void;
|
||||
selectedFont: string;
|
||||
};
|
||||
|
||||
type Props = PopoverTriggerProps;
|
||||
|
||||
export function FontFamilyPicker({}: Props) {
|
||||
export function FontFamilyPicker({ handleTextFontFamilyChange, selectedFont }: Props) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [selectedFont, setSelectedFont] = React.useState<string>("Roboto");
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
fonts.items.map((font): void => {
|
||||
@@ -73,8 +69,8 @@ export function FontFamilyPicker({}: Props) {
|
||||
<CommandItem
|
||||
key={font.family}
|
||||
onSelect={() => {
|
||||
setSelectedFont(font.family);
|
||||
setOpen(false);
|
||||
handleTextFontFamilyChange(font.family);
|
||||
}}
|
||||
className="text-sm"
|
||||
style={{ fontFamily: font.family }}
|
||||
|
||||
@@ -22,7 +22,7 @@ export function FontSizeSelector({ value, fontSizeHandler }: FontSizeSelector) {
|
||||
<Slider
|
||||
id="fontSize"
|
||||
min={1}
|
||||
max={48}
|
||||
max={100}
|
||||
defaultValue={[value ?? 16]}
|
||||
step={1}
|
||||
onValueChange={(e) => {
|
||||
@@ -33,13 +33,6 @@ export function FontSizeSelector({ value, fontSizeHandler }: FontSizeSelector) {
|
||||
/>
|
||||
</div>
|
||||
</HoverCardTrigger>
|
||||
{/* <HoverCardContent
|
||||
align="start"
|
||||
className="w-[260px] text-sm"
|
||||
side="left"
|
||||
>
|
||||
Select font
|
||||
</HoverCardContent> */}
|
||||
</HoverCard>
|
||||
</>
|
||||
);
|
||||
|
||||
50
src/components/spacing-settings.tsx
Normal file
50
src/components/spacing-settings.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { GiSettingsKnobs } from "react-icons/gi";
|
||||
import { Button } from "./ui/button";
|
||||
import { Input } from "./ui/input";
|
||||
import { Label } from "./ui/label";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||
import { Slider } from "./ui/slider";
|
||||
|
||||
export const SpacingSettings = () => {
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline">
|
||||
<GiSettingsKnobs />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-80">
|
||||
<div className="grid gap-4">
|
||||
<div className="grid gap-2">
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<Label htmlFor="width">Width</Label>
|
||||
<Slider
|
||||
id="fontSize"
|
||||
min={1}
|
||||
max={100}
|
||||
step={1}
|
||||
|
||||
className="[&_[role=slider]]:h-4 [&_[role=slider]]:w-4"
|
||||
aria-label="fontSize"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<Label htmlFor="maxWidth">Letter Spacing</Label>
|
||||
<Slider
|
||||
id="fontSize"
|
||||
min={1}
|
||||
max={100}
|
||||
step={1}
|
||||
|
||||
className="[&_[role=slider]]:h-4 [&_[role=slider]]:w-4"
|
||||
aria-label="fontSize"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</PopoverContent>
|
||||
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +1,13 @@
|
||||
import { useAppDispatch, useAppSelector } from "@/hooks";
|
||||
import { updateText } from "@/store/app.slice";
|
||||
import { Toggle } from "@/components/ui/toggle";
|
||||
import { Select } from "@/components/ui/select";
|
||||
import { FontFamilyPicker } from "./font-family-picker";
|
||||
import { FontSizeSelector } from "./font-size-selector";
|
||||
import { useState, type ChangeEvent } from "react";
|
||||
import { SpacingSettings } from "./spacing-settings";
|
||||
|
||||
export const Toolbar = () => {
|
||||
const [alignment, setAlignment] = useState("left");
|
||||
const dispatch = useAppDispatch();
|
||||
const selectedItemId = useAppSelector((state) => state.app.selectedItemId);
|
||||
const texts = useAppSelector((state) => state.app.texts);
|
||||
@@ -15,9 +17,10 @@ export const Toolbar = () => {
|
||||
if (!currentText) return null;
|
||||
|
||||
const handleFontStyleToggle = (button: "bold" | "italic") => () => {
|
||||
if (!selectedItemId) return;
|
||||
dispatch(
|
||||
updateText({
|
||||
...currentText,
|
||||
id: selectedItemId,
|
||||
fontStyle: getFontStyle(currentText.fontStyle, button),
|
||||
}),
|
||||
);
|
||||
@@ -25,23 +28,57 @@ export const Toolbar = () => {
|
||||
|
||||
const handleTextDecorationToggle =
|
||||
(button: "underline" | "line-through") => () => {
|
||||
if (!selectedItemId) return;
|
||||
|
||||
dispatch(
|
||||
updateText({
|
||||
...currentText,
|
||||
id: selectedItemId,
|
||||
textDecoration: getFontStyle(currentText.textDecoration, button),
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleTextFontSizeChange = (e: number | undefined) => {
|
||||
if (!selectedItemId) return;
|
||||
dispatch(
|
||||
updateText({
|
||||
...currentText,
|
||||
id: selectedItemId,
|
||||
fontSize: e,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleTextFontFamilyChange = (e: string | undefined) => {
|
||||
if (!selectedItemId) return;
|
||||
dispatch(
|
||||
updateText({
|
||||
id: selectedItemId,
|
||||
fontFamily: e,
|
||||
}),
|
||||
);
|
||||
};
|
||||
const handleTextColorChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!selectedItemId) return;
|
||||
dispatch(
|
||||
updateText({
|
||||
id: selectedItemId,
|
||||
fill: e.target.value,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleTextAlignChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!selectedItemId) return;
|
||||
dispatch(
|
||||
updateText({
|
||||
id: selectedItemId,
|
||||
align: e.target.value,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleAlignmentChange = (newAlignment: string | undefined) => {};
|
||||
|
||||
return (
|
||||
<div className="flex h-[5rem] w-full gap-6 border bg-white p-[1rem] transition">
|
||||
<Toggle
|
||||
@@ -73,8 +110,22 @@ export const Toolbar = () => {
|
||||
value={currentText.fontSize ?? 16}
|
||||
fontSizeHandler={handleTextFontSizeChange}
|
||||
/>
|
||||
<input type="color" />
|
||||
<FontFamilyPicker />
|
||||
<input
|
||||
type="color"
|
||||
onChange={handleTextColorChange}
|
||||
value={currentText.fill}
|
||||
/>
|
||||
<FontFamilyPicker
|
||||
handleTextFontFamilyChange={handleTextFontFamilyChange}
|
||||
selectedFont={currentText.fontFamily}
|
||||
/>
|
||||
|
||||
<Toggle
|
||||
className="data-[state=on]:font-bold"
|
||||
onClick={handleAlignmentChange("left")}
|
||||
></Toggle>
|
||||
|
||||
<SpacingSettings />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ type TransformableTextConfig = Omit<TextConfig, "text"> & {
|
||||
text?: TextConfig["text"];
|
||||
id: string;
|
||||
direction?: string;
|
||||
fontFamily?: string;
|
||||
fontFamily: string;
|
||||
fontSize?: number;
|
||||
fontStyle?: string;
|
||||
fontVariant?: string;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { TransformableImageProps } from "@/components/transformable-image";
|
||||
import type { TransformableTextProps } from "@/components/transformable-text";
|
||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||
import type { KonvaEventObject } from "konva/lib/Node";
|
||||
import type { ChangeEvent } from "react";
|
||||
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { v1 } from "uuid";
|
||||
import { TextConfig } from "konva/lib/shapes/Text";
|
||||
import type { TextConfig } from "konva/lib/shapes/Text";
|
||||
import { RootState } from "./store";
|
||||
|
||||
const initialState = {
|
||||
selectedItemId: null as string | null,
|
||||
@@ -14,9 +14,12 @@ const initialState = {
|
||||
texts: [] as TransformableTextProps["textProps"][],
|
||||
};
|
||||
|
||||
type InitialState = typeof initialState;
|
||||
|
||||
const defaultTextConfig = {
|
||||
fontSize: 16,
|
||||
align: "center",
|
||||
fontFamily: "Roboto",
|
||||
};
|
||||
|
||||
export const appSlice = createSlice({
|
||||
@@ -51,40 +54,22 @@ export const appSlice = createSlice({
|
||||
|
||||
updateText: (
|
||||
state,
|
||||
action: PayloadAction<Omit<TextConfig, "id"> & { id: string }>,
|
||||
action: PayloadAction<{ id: string } & Partial<TextConfig>>,
|
||||
) => {
|
||||
const textToUpdate = state.texts.findIndex(
|
||||
const textToUpdateIndex = state.texts.findIndex(
|
||||
(t) => t.id === action.payload.id,
|
||||
);
|
||||
state.texts[textToUpdate] = action.payload;
|
||||
},
|
||||
updateTextFontSize: (
|
||||
state,
|
||||
action: PayloadAction<Omit<TextConfig, "id"> & { id: string }>,
|
||||
) => {
|
||||
const textToUpdate = state.texts.findIndex(
|
||||
(t) => t.id === action.payload.id,
|
||||
);
|
||||
state.texts[textToUpdate] = action.payload;
|
||||
},
|
||||
const textToUpdate = state.texts[textToUpdateIndex];
|
||||
|
||||
// updateTextFontFamily: (
|
||||
// state,
|
||||
// action: PayloadAction<Omit<TextConfig, "id"> & { id: string }>,
|
||||
// ) => {
|
||||
// const textToUpdate = state.texts.findIndex(
|
||||
// (t) => t.id === action.payload.id,
|
||||
// );
|
||||
// state.texts[textToUpdate] = action.payload;
|
||||
// },
|
||||
if (!textToUpdate) return;
|
||||
|
||||
state.texts[textToUpdateIndex] = {
|
||||
...textToUpdate,
|
||||
...action.payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
addImage,
|
||||
addText,
|
||||
selectItem,
|
||||
deselectItem,
|
||||
updateText,
|
||||
updateTextFontSize,
|
||||
} = appSlice.actions;
|
||||
export const { addImage, addText, selectItem, deselectItem, updateText } =
|
||||
appSlice.actions;
|
||||
|
||||
9
todo.md
9
todo.md
@@ -1,2 +1,7 @@
|
||||
[ ] image toolbar
|
||||
[ ] text toolbar
|
||||
Property 'PopoverTriggerProps' is missing in type '{ handleTextFontFamilyChange: (e: string | undefined) => void; }' but required in type 'Props'.ts(2741)
|
||||
font-family-picker.tsx(31, 3): 'PopoverTriggerProps' is declared here.
|
||||
(alias) function FontFamilyPicker({ handleTextFontFamilyChange }: Props): React.JSX.Element
|
||||
import FontFamilyPicker
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user