fix: add styleField prop to Field and TypeTag components for flexible styling options

This commit is contained in:
faisolavolut 2025-03-17 11:38:57 +07:00
parent e6bd6ec210
commit b718aa3227
2 changed files with 72 additions and 14 deletions

View File

@ -70,6 +70,7 @@ export interface FieldProps {
autoRefresh?: boolean; autoRefresh?: boolean;
forceDisabled?: boolean; forceDisabled?: boolean;
description?: string | (() => any); description?: string | (() => any);
styleField?: string | null;
} }
export const Field: React.FC<FieldProps> = ({ export const Field: React.FC<FieldProps> = ({
fm, fm,
@ -103,6 +104,7 @@ export const Field: React.FC<FieldProps> = ({
autoRefresh = false, autoRefresh = false,
forceDisabled, forceDisabled,
description, description,
styleField,
}) => { }) => {
let result = null; let result = null;
const field = useLocal({ const field = useLocal({
@ -458,6 +460,7 @@ export const Field: React.FC<FieldProps> = ({
) : ["tag"].includes(type) ? ( ) : ["tag"].includes(type) ? (
<> <>
<TypeTag <TypeTag
styleField={styleField}
fm={fm} fm={fm}
fields={initField} fields={initField}
name={name} name={name}

View File

@ -1,4 +1,8 @@
import { useRef, useState } from "react"; import { cva } from "class-variance-authority";
import { useRef, useState, useEffect } from "react";
import { Checkbox } from "../../ui/checkbox";
import { X } from "lucide-react";
import { IoIosRadioButtonOff } from "react-icons/io";
export const TypeTag: React.FC<any> = ({ export const TypeTag: React.FC<any> = ({
name, name,
@ -9,6 +13,7 @@ export const TypeTag: React.FC<any> = ({
type, type,
field, field,
onChange, onChange,
styleField,
}) => { }) => {
const [tags, setTags] = useState<string[]>(fm.data?.[name] || []); const [tags, setTags] = useState<string[]>(fm.data?.[name] || []);
const [inputValue, setInputValue] = useState(""); const [inputValue, setInputValue] = useState("");
@ -16,8 +21,15 @@ export const TypeTag: React.FC<any> = ({
const [tempValue, setTempValue] = useState<string>(""); // Nilai sementara untuk pengeditan const [tempValue, setTempValue] = useState<string>(""); // Nilai sementara untuk pengeditan
const tagRefs = useRef<(HTMLDivElement | null)[]>([]); const tagRefs = useRef<(HTMLDivElement | null)[]>([]);
const val = fm?.data?.[name]; const val = fm?.data?.[name];
useEffect(() => {
if (editingIndex !== null && tagRefs.current[editingIndex]) {
tagRefs.current[editingIndex]?.focus();
}
}, [editingIndex]);
const handleSaveEdit = (index: number) => { const handleSaveEdit = (index: number) => {
if (!disabled) return; if (disabled) return;
const updatedTags = [...tags]; const updatedTags = [...tags];
updatedTags[index] = tempValue.trim(); // Update nilai tag updatedTags[index] = tempValue.trim(); // Update nilai tag
setTags(updatedTags); setTags(updatedTags);
@ -30,6 +42,7 @@ export const TypeTag: React.FC<any> = ({
onChange(tags); onChange(tags);
} }
}; };
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") { if (e.key === "Enter") {
e.preventDefault(); e.preventDefault();
@ -50,44 +63,88 @@ export const TypeTag: React.FC<any> = ({
setTags(tags.slice(0, -1)); setTags(tags.slice(0, -1));
} }
}; };
const handleFocusTag = (index: number) => { const handleFocusTag = (index: number) => {
if (disabled) return; if (disabled) return;
setEditingIndex(index); // Masuk ke mode edit setEditingIndex(index); // Masuk ke mode edit
setTempValue(tags[index]); // Isi nilai sementara dengan nilai tag setTempValue(tags[index]); // Isi nilai sementara dengan nilai tag
setTimeout(() => {
tagRefs.current[index]?.focus(); // Fokus pada elemen yang diedit
}, 0);
}; };
const removeTag = (index: number) => { const removeTag = (index: number) => {
if (disabled) return; if (disabled) return;
setTags(tags.filter((_, i) => i !== index)); setTags(tags.filter((_, i) => i !== index));
}; };
const buttonVariants = cva(
"flex flex-row items-center rounded-full text-sm p-1",
{
variants: {
variant: {
default: "bg-blue-100 text-blue-800 m-1",
moe: "",
},
},
}
);
const stylingGroup = ["checkbox", "radio", "order"];
return ( return (
<div <div
className={cx( className={cx(
"flex flex-wrap items-center rounded-md flex-grow ", "flex rounded-md flex-grow ",
stylingGroup.includes(styleField)
? "flex-wrap flex-col"
: "items-center flex-wrap",
disabled && !tags?.length ? "h-9" : "" disabled && !tags?.length ? "h-9" : ""
)} )}
> >
{tags.map((tag, index) => ( {tags.map((tag, index) => (
<div <div
key={index} key={index}
className="flex flex-row items-center bg-blue-100 text-blue-800 rounded-full m-1 text-sm" className={cx(
buttonVariants({ variant: styleField ? styleField : "default" }),
editingIndex === index
? styleField
? "border-b border-gray-500 rounded-none"
: "bg-transparent border border-gray-500 rounded-full text-gray-900"
: ""
)}
> >
{styleField === "checkbox" ? (
<>
<Checkbox
className="border border-primary"
checked={false}
onClick={(e) => {}}
/>{" "}
</>
) : styleField === "radio" ? (
<>
<IoIosRadioButtonOff />{" "}
</>
) : styleField === "order" ? (
<>
{index + 1}
{". "}
</>
) : (
<></>
)}
{disabled ? ( {disabled ? (
<div className="px-2">{tag}</div> <div className="px-2">{tag}</div>
) : ( ) : (
<div <div
ref={(el) => {
if (el) tagRefs.current[index] = el;
}}
className={cx( className={cx(
"px-3 py-1 pr-0 flex-grow focus:shadow-none focus:ring-0 focus:border-none focus:outline-none", "px-3 py-1 pr-0 flex-grow focus:shadow-none focus:ring-0 focus:border-none focus:outline-none",
editingIndex! !== index && "cursor-pointer" editingIndex !== index && "cursor-pointer"
)} )}
contentEditable={editingIndex === index} contentEditable={editingIndex === index}
suppressContentEditableWarning suppressContentEditableWarning
onBlur={() => handleSaveEdit(index)} onBlur={() => handleSaveEdit(index)}
onKeyDown={(e) => { onKeyDown={(e) => {
if (!disabled) return; if (disabled) return;
if (e.key === "Enter") { if (e.key === "Enter") {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -97,9 +154,7 @@ export const TypeTag: React.FC<any> = ({
setEditingIndex(null); setEditingIndex(null);
} }
}} }}
onClick={() => { onClick={() => handleFocusTag(index)}
handleFocusTag(index);
}}
onInput={(e) => onInput={(e) =>
setTempValue((e.target as HTMLDivElement).innerText) setTempValue((e.target as HTMLDivElement).innerText)
} }
@ -112,9 +167,9 @@ export const TypeTag: React.FC<any> = ({
<button <button
type="button" type="button"
onClick={() => removeTag(index)} onClick={() => removeTag(index)}
className="ml-2 text-blue-500 hover:text-blue-700 pr-2" className="ml-2 text-red-500 pr-2"
> >
&times; <X size={16} />
</button> </button>
)} )}
</div> </div>