Feature/ add text 2 (#45)

* combobox styling

* text editor can not autofocus text area

* better styles and input method for the color picker
This commit is contained in:
wil authored and GitHub committed 2024-10-08 13:08:23 -04:00
1 parent 769dedceca
commit 486e3a6384
6 files changed
+233 -117

No files matched your search

@@ -1,4 +1,4 @@
import { useState, ChangeEvent } from "react";
import { useState } from "react";
import { twMerge } from "tailwind-merge";
import { Dialog, DialogPanel, DialogTitle } from "@headlessui/react";
import { Button, TextEditor } from "~/components/ui";
@@ -72,7 +72,7 @@ export const DialogEditText = ({
<DialogPanel
className={twMerge(
paperWrapperStyles,
"flex w-full max-w-5xl flex-col justify-between gap-4 px-6 pb-6 pt-4",
"flex w-fit max-w-5xl flex-col justify-between gap-4 px-6 pb-6 pt-4",
)}
// style={{ height: "calc(100vh - 200px)" }}
>
+1 -1
View File
@@ -31,7 +31,7 @@ export const Button = ({
return "bg-tertiary hover:bg-tertiary-400 text-white focus-visible:outline-tertiary-600";
}
case "secondary": {
return "bg-secondary hover:bg-secondary-900 text-white focus-visible:outline-secondary";
return "bg-secondary-600 hover:bg-secondary-400 text-white focus-visible:outline-secondary";
}
case "action": {
return " bg-action hover:bg-action-500 text-white focus-visible:outline-action";
+9 -9
View File
@@ -29,25 +29,25 @@ export const Combobox = ({ options, value, onChange }: ComboBoxInterface) => {
onChange={onChange}
onClose={() => setQuery("")}
>
<div className="flex">
<div className="relative flex h-10 rounded-md border">
<ComboboxInput
aria-label="Assignee"
className="rounded-md"
displayValue={(font: string) => font}
onChange={(event) => setQuery(event.target.value)}
/>
<ComboboxButton className="">
<FontAwesomeIcon
icon={faChevronDown}
className="size-4 fill-black/60 group-data-[hover]:fill-black"
/>
<ComboboxButton className="absolute right-0 top-0 flex h-10 items-center justify-center hover:text-primary">
<FontAwesomeIcon icon={faChevronDown} className="size-4 p-2" />
</ComboboxButton>
</div>
<ComboboxOptions anchor="bottom" className="border empty:invisible">
<ComboboxOptions
anchor="bottom"
className="mt-1 w-[var(--input-width)] rounded-lg border empty:invisible"
>
{filteredOptions.map((option, idx) => (
<ComboboxOption
key={idx}
value={option}
className="data-[focus]:bg-blue-100"
className="cursor-pointer bg-white p-2 data-[focus]:bg-primary-200 data-[selected]:font-medium"
>
{option}
</ComboboxOption>
+36 -36
View File
@@ -1,4 +1,4 @@
import { TextareaHTMLAttributes } from "react";
import { TextareaHTMLAttributes, forwardRef } from "react";
import { twMerge } from "tailwind-merge";
import { kebabCase } from "~/utilities";
@@ -16,39 +16,39 @@ export interface TextareaInterface
label?: string;
resize?: ResizeType;
}
export const Textarea = ({
className,
label,
resize = "vertical",
id,
...rest
}: TextareaInterface) => {
return (
<div className="flex flex-col">
{label && <label htmlFor={id ? id : kebabCase(label)}>{label}</label>}
export const Textarea = forwardRef<HTMLTextAreaElement>(
(
{ className, label, resize = "vertical", id, ...rest }: TextareaInterface,
ref,
) => {
return (
<div className="flex flex-col">
{label && <label htmlFor={id ? id : kebabCase(label)}>{label}</label>}
<textarea
id={id ? id : label ? kebabCase(label) : undefined}
className={twMerge(
"border-ui-panel-border bg-ui-controls rounded-lg border px-3 py-2",
className,
)}
style={{
outline: "2px solid transparent",
transition: "outline-color 0.15s ease-in-out",
resize: resize,
}}
onFocus={(e) => {
// disableHotkeyInput(DomLevels.INPUT);
e.currentTarget.style.outlineColor = "#e66462";
}}
onBlur={(e) => {
// enableHotkeyInput(DomLevels.INPUT);
e.currentTarget.style.outlineColor = "transparent";
}}
onKeyDown={(event) => event.stopPropagation()}
{...rest}
/>
</div>
);
};
<textarea
ref={ref}
id={id ? id : label ? kebabCase(label) : undefined}
className={twMerge(
"border-ui-panel-border bg-ui-controls rounded-lg border px-3 py-2",
className,
)}
style={{
outline: "2px solid transparent",
transition: "outline-color 0.15s ease-in-out",
resize: resize,
}}
onFocus={(e) => {
// disableHotkeyInput(DomLevels.INPUT);
e.currentTarget.style.outlineColor = "#e66462";
}}
onBlur={(e) => {
// enableHotkeyInput(DomLevels.INPUT);
e.currentTarget.style.outlineColor = "transparent";
}}
onKeyDown={(event) => event.stopPropagation()}
{...rest}
/>
</div>
);
},
);
@@ -0,0 +1,98 @@
import { ChangeEvent, useState } from "react";
import { twMerge } from "tailwind-merge";
import { faFont, faHashtag } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
import { HexColorPicker } from "react-colorful";
import { Button } from "../Button";
import { Input } from "../Input";
import { paperWrapperStyles } from "~/components/styles";
export const ColorPicker = ({
color: prevColor,
onChange,
}: {
color: string;
onChange: (newColor: string) => void;
}) => {
const [{ currColor, textInput }, setStates] = useState<{
currColor: string;
textInput: string;
}>({
currColor: prevColor,
textInput: prevColor.substring(1),
});
const handleSelectColor = (newColor: string) => {
setStates({ currColor: newColor, textInput: newColor.substring(1) });
};
const isHexColorCode = (color: string) => {
const hexColorRegex = /^(?:[0-9a-fA-F]{3}){1,2}$/;
return hexColorRegex.test(color);
};
const handleTextInput = (e: ChangeEvent<HTMLInputElement>) => {
if (isHexColorCode(e.target.value)) {
setStates({
currColor: `#${e.target.value}`,
textInput: e.target.value,
});
} else {
setStates((curr) => ({
...curr,
textInput: e.target.value,
}));
}
};
return (
<Popover className="relative">
<PopoverButton className="flex size-10 flex-col items-center gap-1 rounded-md border p-2">
<FontAwesomeIcon icon={faFont} />
<span className="h-1 w-full" style={{ backgroundColor: prevColor }} />
</PopoverButton>
<PopoverPanel
anchor="bottom"
className={twMerge(
paperWrapperStyles,
"flex flex-col items-center gap-2",
)}
>
{({ close }) => {
return (
<>
<HexColorPicker color={currColor} onChange={handleSelectColor} />
<Input
style={{ width: "198px" }}
icon={faHashtag}
value={textInput}
onChange={handleTextInput}
/>
<div className="flex w-full justify-center gap-2">
<Button
variant="secondary"
onClick={() => {
setStates({
currColor: prevColor,
textInput: prevColor.substring(1),
});
close();
}}
>
Cancel
</Button>
<Button
onClick={() => {
onChange(currColor);
close();
}}
>
OK
</Button>
</div>
</>
);
}}
</PopoverPanel>
</Popover>
);
};
+87 -69
View File
@@ -1,20 +1,12 @@
import { ChangeEvent } from "react";
import { faFont } from "@fortawesome/pro-solid-svg-icons";
import {
CloseButton,
Popover,
PopoverButton,
PopoverPanel,
} from "@headlessui/react";
import { HexColorPicker } from "react-colorful";
import { ChangeEvent, useCallback, useEffect, useRef } from "react";
import { Textarea, TextareaInterface, ResizeType } from "../TextArea";
import { InputNumber } from "../InputNumber";
import { Combobox } from "../ComboBox";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { ButtonsAlignments } from "./ButtonsAlignment";
import { ButtonsTextStyles } from "./ButtonTextStyles";
import { ColorPicker } from "./ColorPicker";
import {
TextFormatData,
TextAlign,
@@ -38,57 +30,99 @@ export const TextEditor = ({
onChangeFormatting: (newFormatData: Partial<TextFormatData>) => void;
TextareaProps?: TextareaInterface;
}) => {
const handleOnChangeText = (e: ChangeEvent<HTMLTextAreaElement>) => {
onChangeText(e.target.value);
};
const onChangeTextAlignment = (newAlignment: TextAlign) => {
onChangeFormatting({
textAlign: newAlignment,
});
};
const onChangeFontStyle = (newStyle: FontStyle) => {
onChangeFormatting({
fontStyle: newStyle,
});
};
const onChangeFontWeight = (newWeight: FontWeight) => {
onChangeFormatting({
fontWeight: newWeight,
});
};
const onChangeTextDecoration = (newDecor: TextDecoration) => {
onChangeFormatting({
textDecoration: newDecor,
});
};
const onChangeFontFamily = (newFontFamily: string) => {
onChangeFormatting({
fontFamily: newFontFamily,
});
};
const onChangeTextColor = (newTextColor: string) => {
onChangeFormatting({
color: newTextColor,
});
};
const onChangeFontSize = (newVal: number) => {
onChangeFormatting({
fontSize: newVal,
});
};
const textAreaRef = useRef<HTMLTextAreaElement>(null);
const focusOnTextArea = useCallback(() => {
if (textAreaRef.current === null) {
return;
}
textAreaRef.current.focus();
}, []);
useEffect(() => {
focusOnTextArea();
}, []);
const handleOnChangeText = useCallback(
(e: ChangeEvent<HTMLTextAreaElement>) => {
onChangeText(e.target.value);
focusOnTextArea();
},
[onChangeText],
);
const onChangeTextAlignment = useCallback(
(newAlignment: TextAlign) => {
onChangeFormatting({
textAlign: newAlignment,
});
focusOnTextArea();
},
[onChangeFormatting],
);
const onChangeFontStyle = useCallback(
(newStyle: FontStyle) => {
onChangeFormatting({
fontStyle: newStyle,
});
focusOnTextArea();
},
[onChangeFormatting],
);
const onChangeFontWeight = useCallback(
(newWeight: FontWeight) => {
onChangeFormatting({
fontWeight: newWeight,
});
focusOnTextArea();
},
[onChangeFormatting],
);
const onChangeTextDecoration = useCallback(
(newDecor: TextDecoration) => {
onChangeFormatting({
textDecoration: newDecor,
});
focusOnTextArea();
},
[onChangeFormatting],
);
const onChangeFontFamily = useCallback(
(newFontFamily: string) => {
onChangeFormatting({
fontFamily: newFontFamily,
});
focusOnTextArea();
},
[onChangeFormatting],
);
const onChangeTextColor = useCallback(
(newTextColor: string) => {
onChangeFormatting({
color: newTextColor,
});
},
[onChangeFormatting],
);
const onChangeFontSize = useCallback(
(newVal: number) => {
onChangeFormatting({
fontSize: newVal,
});
// focusOnTextArea();
},
[onChangeFormatting],
);
const TextAreaStates = {
style: {
...formatData,
width: "500px",
height: "500px",
},
rows: 7,
placeholder: "...",
resize: "none" as ResizeType,
onChange: handleOnChangeText,
value: text,
};
const unionTextAreaProps = {
const unionedTextAreaProps = {
...TextareaProps,
...TextAreaStates,
};
@@ -100,23 +134,7 @@ export const TextEditor = ({
value={formatData.fontFamily}
onChange={onChangeFontFamily}
/>
<Popover className="relative">
<PopoverButton className="flex size-10 flex-col items-center gap-1 rounded-md border p-2">
<FontAwesomeIcon icon={faFont} />
<span
className="h-1 w-full"
style={{ backgroundColor: formatData.color }}
/>
</PopoverButton>
<PopoverPanel anchor="bottom" className="flex flex-col">
<HexColorPicker
color={formatData.color}
onChange={onChangeTextColor}
/>
<p>{formatData.color}</p>
<CloseButton>OK</CloseButton>
</PopoverPanel>
</Popover>
<ColorPicker color={formatData.color} onChange={onChangeTextColor} />
<InputNumber value={formatData.fontSize} onChange={onChangeFontSize} />
</div>
<div className="flex items-center gap-4">
@@ -133,7 +151,7 @@ export const TextEditor = ({
onChangeTextDecoration={onChangeTextDecoration}
/>
</div>
<Textarea {...unionTextAreaProps} />
<Textarea {...unionedTextAreaProps} ref={textAreaRef} />
</div>
);
};