text-field.tsx 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. "use client";
  2. import { type ComponentRenderProps } from "@json-render/react";
  3. import { useData, useFieldValidation } from "@json-render/react";
  4. import { getByPath } from "@json-render/core";
  5. export function TextField({ element }: ComponentRenderProps) {
  6. const { label, valuePath, placeholder, type, checks, validateOn } =
  7. element.props as {
  8. label: string;
  9. valuePath: string;
  10. placeholder?: string | null;
  11. type?: string | null;
  12. checks?: Array<{ fn: string; message: string }> | null;
  13. validateOn?: string | null;
  14. };
  15. const { data, set } = useData();
  16. const value = getByPath(data, valuePath) as string | undefined;
  17. const { errors, validate, touch } = useFieldValidation(valuePath, {
  18. checks: checks ?? undefined,
  19. validateOn: (validateOn as "change" | "blur" | "submit") ?? "blur",
  20. });
  21. return (
  22. <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
  23. <label style={{ fontSize: 14, fontWeight: 500 }}>{label}</label>
  24. <input
  25. type={type || "text"}
  26. value={value ?? ""}
  27. onChange={(e) => {
  28. set(valuePath, e.target.value);
  29. if (validateOn === "change") validate();
  30. }}
  31. onBlur={() => {
  32. touch();
  33. if (validateOn === "blur" || !validateOn) validate();
  34. }}
  35. placeholder={placeholder ?? ""}
  36. style={{
  37. padding: "8px 12px",
  38. borderRadius: "var(--radius)",
  39. border:
  40. errors.length > 0 ? "1px solid #ef4444" : "1px solid var(--border)",
  41. background: "var(--card)",
  42. color: "var(--foreground)",
  43. fontSize: 16,
  44. outline: "none",
  45. }}
  46. />
  47. {errors.map((error, i) => (
  48. <span key={i} style={{ fontSize: 12, color: "#ef4444" }}>
  49. {error}
  50. </span>
  51. ))}
  52. </div>
  53. );
  54. }