forked from github/plane
chore: added outside click detector for custom attribute forms
This commit is contained in:
parent
4d158a6d8f
commit
2d3c1f93c1
@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
// react-hook-form
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
// hooks
|
||||||
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
// types
|
// types
|
||||||
import { ICustomAttribute } from "types";
|
import { ICustomAttribute } from "types";
|
||||||
|
|
||||||
@ -14,6 +15,8 @@ type Props = {
|
|||||||
export const CustomEmailAttribute: React.FC<Props> = ({ attributeDetails, onChange, value }) => {
|
export const CustomEmailAttribute: React.FC<Props> = ({ attributeDetails, onChange, value }) => {
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
|
||||||
|
const formRef = useRef(null);
|
||||||
|
|
||||||
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { email: "" } });
|
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { email: "" } });
|
||||||
|
|
||||||
const handleFormSubmit = (data: { email: string }) => {
|
const handleFormSubmit = (data: { email: string }) => {
|
||||||
@ -42,6 +45,10 @@ export const CustomEmailAttribute: React.FC<Props> = ({ attributeDetails, onChan
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useOutsideClickDetector(formRef, () => {
|
||||||
|
setIsEditing(false);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
{!isEditing && (
|
{!isEditing && (
|
||||||
@ -59,7 +66,7 @@ export const CustomEmailAttribute: React.FC<Props> = ({ attributeDetails, onChan
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
<form onSubmit={handleSubmit(handleFormSubmit)} className="flex items-center">
|
<form onSubmit={handleSubmit(handleFormSubmit)} className="flex items-center" ref={formRef}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="email"
|
name="email"
|
||||||
@ -68,6 +75,7 @@ export const CustomEmailAttribute: React.FC<Props> = ({ attributeDetails, onChan
|
|||||||
type="email"
|
type="email"
|
||||||
className="text-xs px-2 py-0.5 bg-custom-background-80 rounded w-full outline-none"
|
className="text-xs px-2 py-0.5 bg-custom-background-80 rounded w-full outline-none"
|
||||||
required={attributeDetails.is_required}
|
required={attributeDetails.is_required}
|
||||||
|
placeholder={attributeDetails.display_name}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
// react-hook-form
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
// hooks
|
||||||
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
// ui
|
// ui
|
||||||
import { ProgressBar } from "components/ui";
|
import { ProgressBar } from "components/ui";
|
||||||
// types
|
// types
|
||||||
@ -16,6 +17,8 @@ type Props = {
|
|||||||
export const CustomNumberAttribute: React.FC<Props> = ({ attributeDetails, onChange, value }) => {
|
export const CustomNumberAttribute: React.FC<Props> = ({ attributeDetails, onChange, value }) => {
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
|
||||||
|
const formRef = useRef(null);
|
||||||
|
|
||||||
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { number: "" } });
|
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { number: "" } });
|
||||||
|
|
||||||
const handleFormSubmit = (data: { number: string }) => {
|
const handleFormSubmit = (data: { number: string }) => {
|
||||||
@ -49,6 +52,10 @@ export const CustomNumberAttribute: React.FC<Props> = ({ attributeDetails, onCha
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useOutsideClickDetector(formRef, () => {
|
||||||
|
setIsEditing(false);
|
||||||
|
});
|
||||||
|
|
||||||
const extraSettings = attributeDetails.extra_settings;
|
const extraSettings = attributeDetails.extra_settings;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -100,7 +107,7 @@ export const CustomNumberAttribute: React.FC<Props> = ({ attributeDetails, onCha
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
<form onSubmit={handleSubmit(handleFormSubmit)} className="flex items-center">
|
<form onSubmit={handleSubmit(handleFormSubmit)} className="flex items-center" ref={formRef}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="number"
|
name="number"
|
||||||
@ -112,6 +119,7 @@ export const CustomNumberAttribute: React.FC<Props> = ({ attributeDetails, onCha
|
|||||||
min={extraSettings.divided_by ? 0 : undefined}
|
min={extraSettings.divided_by ? 0 : undefined}
|
||||||
max={extraSettings.divided_by ?? undefined}
|
max={extraSettings.divided_by ?? undefined}
|
||||||
required={attributeDetails.is_required}
|
required={attributeDetails.is_required}
|
||||||
|
placeholder={attributeDetails.display_name}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
// react-hook-form
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
// hooks
|
||||||
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
// types
|
// types
|
||||||
import { ICustomAttribute } from "types";
|
import { ICustomAttribute } from "types";
|
||||||
|
|
||||||
@ -18,6 +19,8 @@ export const CustomTextAttribute: React.FC<Props & { value: string | undefined }
|
|||||||
}) => {
|
}) => {
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
|
||||||
|
const formRef = useRef(null);
|
||||||
|
|
||||||
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { text: "" } });
|
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { text: "" } });
|
||||||
|
|
||||||
const handleFormSubmit = (data: { text: string }) => {
|
const handleFormSubmit = (data: { text: string }) => {
|
||||||
@ -48,6 +51,10 @@ export const CustomTextAttribute: React.FC<Props & { value: string | undefined }
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useOutsideClickDetector(formRef, () => {
|
||||||
|
setIsEditing(false);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
{!isEditing && (
|
{!isEditing && (
|
||||||
@ -55,11 +62,11 @@ export const CustomTextAttribute: React.FC<Props & { value: string | undefined }
|
|||||||
className="cursor-pointer text-xs truncate bg-custom-background-80 px-2.5 py-0.5 w-min max-w-full whitespace-nowrap rounded"
|
className="cursor-pointer text-xs truncate bg-custom-background-80 px-2.5 py-0.5 w-min max-w-full whitespace-nowrap rounded"
|
||||||
onClick={() => setIsEditing(true)}
|
onClick={() => setIsEditing(true)}
|
||||||
>
|
>
|
||||||
{value && value !== "" ? value : `Enter ${attributeDetails.display_name}`}
|
{value && value !== "" ? value : "Empty"}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
<form onSubmit={handleSubmit(handleFormSubmit)} className="flex items-center">
|
<form onSubmit={handleSubmit(handleFormSubmit)} className="flex items-center" ref={formRef}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="text"
|
name="text"
|
||||||
@ -68,6 +75,7 @@ export const CustomTextAttribute: React.FC<Props & { value: string | undefined }
|
|||||||
type="text"
|
type="text"
|
||||||
className="text-xs px-2 py-0.5 bg-custom-background-80 rounded w-full outline-none"
|
className="text-xs px-2 py-0.5 bg-custom-background-80 rounded w-full outline-none"
|
||||||
required={attributeDetails.is_required}
|
required={attributeDetails.is_required}
|
||||||
|
placeholder={attributeDetails.display_name}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
// react-hook-form
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
// hooks
|
||||||
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
// types
|
// types
|
||||||
import { ICustomAttribute } from "types";
|
import { ICustomAttribute } from "types";
|
||||||
|
|
||||||
@ -18,6 +19,8 @@ export const CustomUrlAttribute: React.FC<Props & { value: string | undefined }>
|
|||||||
}) => {
|
}) => {
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
|
||||||
|
const formRef = useRef(null);
|
||||||
|
|
||||||
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { url: "" } });
|
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { url: "" } });
|
||||||
|
|
||||||
const handleFormSubmit = (data: { url: string }) => {
|
const handleFormSubmit = (data: { url: string }) => {
|
||||||
@ -46,6 +49,10 @@ export const CustomUrlAttribute: React.FC<Props & { value: string | undefined }>
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useOutsideClickDetector(formRef, () => {
|
||||||
|
setIsEditing(false);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
{!isEditing && (
|
{!isEditing && (
|
||||||
@ -63,7 +70,7 @@ export const CustomUrlAttribute: React.FC<Props & { value: string | undefined }>
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
<form onSubmit={handleSubmit(handleFormSubmit)} className="flex items-center">
|
<form onSubmit={handleSubmit(handleFormSubmit)} className="flex items-center" ref={formRef}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="url"
|
name="url"
|
||||||
@ -72,6 +79,7 @@ export const CustomUrlAttribute: React.FC<Props & { value: string | undefined }>
|
|||||||
type="url"
|
type="url"
|
||||||
className="text-xs px-2 py-0.5 bg-custom-background-80 rounded w-full outline-none"
|
className="text-xs px-2 py-0.5 bg-custom-background-80 rounded w-full outline-none"
|
||||||
required={attributeDetails.is_required}
|
required={attributeDetails.is_required}
|
||||||
|
placeholder={attributeDetails.display_name}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -221,7 +221,7 @@ export const ObjectModal: React.FC<Props> = observer((props) => {
|
|||||||
<SecondaryButton onClick={handleClose}>Close</SecondaryButton>
|
<SecondaryButton onClick={handleClose}>Close</SecondaryButton>
|
||||||
)}
|
)}
|
||||||
<PrimaryButton type="submit" loading={isSubmitting}>
|
<PrimaryButton type="submit" loading={isSubmitting}>
|
||||||
{data
|
{objectId
|
||||||
? isSubmitting
|
? isSubmitting
|
||||||
? "Saving..."
|
? "Saving..."
|
||||||
: "Save changes"
|
: "Save changes"
|
||||||
@ -234,7 +234,7 @@ export const ObjectModal: React.FC<Props> = observer((props) => {
|
|||||||
</form>
|
</form>
|
||||||
{objectId && (
|
{objectId && (
|
||||||
<>
|
<>
|
||||||
<div className="px-6 pb-5">
|
<div className="px-6">
|
||||||
<h4 className="font-medium">Attributes</h4>
|
<h4 className="font-medium">Attributes</h4>
|
||||||
<div className="mt-2 space-y-2">
|
<div className="mt-2 space-y-2">
|
||||||
{customAttributes.fetchObjectDetailsLoader ? (
|
{customAttributes.fetchObjectDetailsLoader ? (
|
||||||
|
Loading…
Reference in New Issue
Block a user