text component
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { Trash } from "iconsax-react";
|
||||
import type { EditorObject } from "../../store/editorStore";
|
||||
import {
|
||||
TextSettings,
|
||||
ShapeSettings,
|
||||
LineSettings,
|
||||
SizeSettings,
|
||||
@@ -10,6 +9,7 @@ import {
|
||||
TransformSettings,
|
||||
GridSettings,
|
||||
} from "./settings";
|
||||
import TextInstruction from "./instructions/TextInstruction";
|
||||
|
||||
type ObjectSettingsProps = {
|
||||
selectedObject: EditorObject;
|
||||
@@ -30,7 +30,7 @@ const ObjectSettings = ({ selectedObject, onUpdate, onDelete }: ObjectSettingsPr
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{selectedObject.type === "text" && <TextSettings selectedObject={selectedObject} onUpdate={onUpdate} />}
|
||||
{selectedObject.type === "text" && <TextInstruction />}
|
||||
|
||||
{(selectedObject.type === "rectangle" || selectedObject.type === "circle") && (
|
||||
<ShapeSettings selectedObject={selectedObject} onUpdate={onUpdate} />
|
||||
|
||||
@@ -1,8 +1,170 @@
|
||||
const TextInstruction = () => (
|
||||
<div className="text-sm text-gray-600">
|
||||
<p>برای افزودن متن، روی کانوس کلیک کنید</p>
|
||||
</div>
|
||||
);
|
||||
import { type FC, useRef, useState, useEffect } from "react";
|
||||
import Input from "@/components/Input";
|
||||
import Select from "@/components/Select";
|
||||
import { useEditorStore } from "@/pages/editor/store/editorStore";
|
||||
import { useTextDefaultsStore } from "@/pages/editor/store/textDefaultsStore";
|
||||
|
||||
const fontOptions = [
|
||||
{ label: "ایرانسل", value: "1" },
|
||||
{ label: "فونت 2", value: "2" },
|
||||
];
|
||||
|
||||
const fontWeightOptions = [
|
||||
{ label: "Bold", value: "bold" },
|
||||
{ label: "Normal", value: "normal" },
|
||||
{ label: "Light", value: "300" },
|
||||
];
|
||||
|
||||
const TextInstruction: FC = () => {
|
||||
const { selectedObjectId, objects, updateObject } = useEditorStore();
|
||||
const { defaults, updateDefaults } = useTextDefaultsStore();
|
||||
|
||||
const selectedObject = selectedObjectId
|
||||
? objects.find((obj) => obj.id === selectedObjectId && obj.type === "text")
|
||||
: null;
|
||||
|
||||
const isEditing = !!selectedObject;
|
||||
const currentValues = isEditing
|
||||
? {
|
||||
fontFamily: selectedObject.fontFamily || defaults.fontFamily,
|
||||
fontWeight: selectedObject.fontWeight || defaults.fontWeight,
|
||||
fontSize: selectedObject.fontSize || defaults.fontSize,
|
||||
fill: selectedObject.fill || defaults.fill,
|
||||
opacity: selectedObject.opacity ?? defaults.opacity,
|
||||
letterSpacing: selectedObject.letterSpacing ?? defaults.letterSpacing,
|
||||
wordSpacing: selectedObject.wordSpacing ?? defaults.wordSpacing,
|
||||
}
|
||||
: defaults;
|
||||
|
||||
const handleChange = (field: string, value: string | number) => {
|
||||
if (isEditing && selectedObject) {
|
||||
updateObject(selectedObject.id, { [field]: value });
|
||||
} else {
|
||||
updateDefaults({ [field]: value });
|
||||
}
|
||||
};
|
||||
|
||||
const hexColor = (currentValues.fill || "#000000").replace("#", "").toUpperCase();
|
||||
const opacityValue = currentValues.opacity;
|
||||
const colorInputRef = useRef<HTMLInputElement>(null);
|
||||
const [opacityInput, setOpacityInput] = useState<string>(`${opacityValue}`);
|
||||
|
||||
useEffect(() => {
|
||||
setOpacityInput(`${opacityValue}`);
|
||||
}, [opacityValue]);
|
||||
|
||||
const handleColorClick = () => {
|
||||
colorInputRef.current?.click();
|
||||
};
|
||||
|
||||
const handleOpacityChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value.replace("%", "").trim();
|
||||
setOpacityInput(value);
|
||||
};
|
||||
|
||||
const handleOpacityBlur = () => {
|
||||
const numValue = parseInt(opacityInput) || 0;
|
||||
const clampedValue = Math.min(100, Math.max(0, numValue));
|
||||
setOpacityInput(`${clampedValue}`);
|
||||
if (clampedValue !== opacityValue) {
|
||||
handleChange("opacity", clampedValue);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="font-bold">متن</h2>
|
||||
<div className="mt-8">
|
||||
<Select
|
||||
items={fontOptions}
|
||||
placeholder="انتخاب فونت"
|
||||
label="فونت"
|
||||
value={currentValues.fontFamily}
|
||||
onChange={(e) => handleChange("fontFamily", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 mt-4">
|
||||
<Select
|
||||
label="وزن"
|
||||
items={fontWeightOptions}
|
||||
value={currentValues.fontWeight}
|
||||
onChange={(e) => handleChange("fontWeight", e.target.value)}
|
||||
/>
|
||||
|
||||
<Input
|
||||
value={`${currentValues.fontSize}px`}
|
||||
label="سایز"
|
||||
onChange={(e) => {
|
||||
const value = e.target.value.replace("px", "").trim();
|
||||
const numValue = parseInt(value) || 0;
|
||||
handleChange("fontSize", numValue);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<div className="text-sm">رنگ</div>
|
||||
<div className="mt-2 border border-border rounded-xl p-2 flex items-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleColorClick}
|
||||
className="flex-1 flex items-center gap-1.5 cursor-pointer hover:opacity-80 transition-opacity"
|
||||
>
|
||||
<div
|
||||
className="size-6 rounded"
|
||||
style={{ backgroundColor: currentValues.fill }}
|
||||
/>
|
||||
<div className="text-sm">{hexColor}</div>
|
||||
</button>
|
||||
<input
|
||||
ref={colorInputRef}
|
||||
type="color"
|
||||
value={currentValues.fill}
|
||||
onChange={(e) => handleChange("fill", e.target.value)}
|
||||
className="hidden"
|
||||
/>
|
||||
<div className="text-sm flex px-2 mr-3 border-r border-border">
|
||||
<input
|
||||
type="text"
|
||||
className="text-center w-14 border-none outline-none bg-transparent text-sm"
|
||||
value={`${opacityInput}%`}
|
||||
onChange={handleOpacityChange}
|
||||
onBlur={handleOpacityBlur}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.currentTarget.blur();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex gap-2">
|
||||
<Input
|
||||
label="فاصله کلمات"
|
||||
value={`${currentValues.wordSpacing}px`}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value.replace("px", "").trim();
|
||||
const numValue = parseInt(value) || 0;
|
||||
handleChange("wordSpacing", numValue);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="فاصله حروف"
|
||||
value={`${currentValues.letterSpacing}px`}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value.replace("px", "").trim();
|
||||
const numValue = parseInt(value) || 0;
|
||||
handleChange("letterSpacing", numValue);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextInstruction;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user