mirror of
https://github.com/stefanpejcic/openpanel
synced 2025-06-26 18:28:26 +00:00
328 lines
8.7 KiB
TypeScript
328 lines
8.7 KiB
TypeScript
import { useCallback } from "react";
|
|
import {
|
|
BaseKey,
|
|
BaseRecord,
|
|
FormWithSyncWithLocationParams,
|
|
HttpError,
|
|
useGo,
|
|
useModal,
|
|
useParsed,
|
|
useResource,
|
|
useUserFriendlyName,
|
|
useTranslate,
|
|
useWarnAboutChange,
|
|
useInvalidate,
|
|
} from "@refinedev/core";
|
|
import { FieldValues } from "react-hook-form";
|
|
|
|
import { useForm, UseFormProps, UseFormReturnType } from "../useForm";
|
|
import React from "react";
|
|
|
|
export type UseModalFormReturnType<
|
|
TQueryFnData extends BaseRecord = BaseRecord,
|
|
TError extends HttpError = HttpError,
|
|
TVariables extends FieldValues = FieldValues,
|
|
TContext extends object = {},
|
|
TData extends BaseRecord = TQueryFnData,
|
|
TResponse extends BaseRecord = TData,
|
|
TResponseError extends HttpError = TError,
|
|
> = UseFormReturnType<
|
|
TQueryFnData,
|
|
TError,
|
|
TVariables,
|
|
TContext,
|
|
TData,
|
|
TResponse,
|
|
TResponseError
|
|
> & {
|
|
modal: {
|
|
submit: (values: TVariables) => void;
|
|
close: () => void;
|
|
show: (id?: BaseKey) => void;
|
|
visible: boolean;
|
|
title: string;
|
|
};
|
|
};
|
|
|
|
export type UseModalFormProps<
|
|
TQueryFnData extends BaseRecord = BaseRecord,
|
|
TError extends HttpError = HttpError,
|
|
TVariables extends FieldValues = FieldValues,
|
|
TContext extends object = {},
|
|
TData extends BaseRecord = TQueryFnData,
|
|
TResponse extends BaseRecord = TData,
|
|
TResponseError extends HttpError = TError,
|
|
> = UseFormProps<
|
|
TQueryFnData,
|
|
TError,
|
|
TVariables,
|
|
TContext,
|
|
TData,
|
|
TResponse,
|
|
TResponseError
|
|
> & {
|
|
/**
|
|
* @description Configuration object for the modal.
|
|
* `defaultVisible`: Initial visibility state of the modal.
|
|
*
|
|
* `autoSubmitClose`: Whether the form should be submitted when the modal is closed.
|
|
*
|
|
* `autoResetForm`: Whether the form should be reset when the form is submitted.
|
|
* @type `{
|
|
defaultVisible?: boolean;
|
|
autoSubmitClose?: boolean;
|
|
autoResetForm?: boolean;
|
|
}`
|
|
* @default `defaultVisible = false` `autoSubmitClose = true` `autoResetForm = true`
|
|
*/
|
|
modalProps?: {
|
|
defaultVisible?: boolean;
|
|
autoSubmitClose?: boolean;
|
|
autoResetForm?: boolean;
|
|
};
|
|
} & FormWithSyncWithLocationParams;
|
|
|
|
export const useModalForm = <
|
|
TQueryFnData extends BaseRecord = BaseRecord,
|
|
TError extends HttpError = HttpError,
|
|
TVariables extends FieldValues = FieldValues,
|
|
TContext extends object = {},
|
|
TData extends BaseRecord = TQueryFnData,
|
|
TResponse extends BaseRecord = TData,
|
|
TResponseError extends HttpError = TError,
|
|
>({
|
|
modalProps,
|
|
refineCoreProps,
|
|
syncWithLocation,
|
|
...rest
|
|
}: UseModalFormProps<
|
|
TQueryFnData,
|
|
TError,
|
|
TVariables,
|
|
TContext,
|
|
TData,
|
|
TResponse,
|
|
TResponseError
|
|
> = {}): UseModalFormReturnType<
|
|
TQueryFnData,
|
|
TError,
|
|
TVariables,
|
|
TContext,
|
|
TData,
|
|
TResponse,
|
|
TResponseError
|
|
> => {
|
|
const invalidate = useInvalidate();
|
|
const [initiallySynced, setInitiallySynced] = React.useState(false);
|
|
|
|
const translate = useTranslate();
|
|
|
|
const { resource: resourceProp, action: actionProp } =
|
|
refineCoreProps ?? {};
|
|
|
|
const {
|
|
resource,
|
|
action: actionFromParams,
|
|
identifier,
|
|
} = useResource(resourceProp);
|
|
|
|
const parsed = useParsed();
|
|
const go = useGo();
|
|
const getUserFriendlyName = useUserFriendlyName();
|
|
|
|
const action = actionProp ?? actionFromParams ?? "";
|
|
|
|
const syncingId = !(
|
|
typeof syncWithLocation === "object" &&
|
|
syncWithLocation?.syncId === false
|
|
);
|
|
|
|
const syncWithLocationKey =
|
|
typeof syncWithLocation === "object" && "key" in syncWithLocation
|
|
? syncWithLocation.key
|
|
: resource && action && syncWithLocation
|
|
? `modal-${identifier}-${action}`
|
|
: undefined;
|
|
|
|
const {
|
|
defaultVisible = false,
|
|
autoSubmitClose = true,
|
|
autoResetForm = true,
|
|
} = modalProps ?? {};
|
|
|
|
const useHookFormResult = useForm<
|
|
TQueryFnData,
|
|
TError,
|
|
TVariables,
|
|
TContext,
|
|
TData,
|
|
TResponse,
|
|
TResponseError
|
|
>({
|
|
refineCoreProps: {
|
|
...refineCoreProps,
|
|
meta: {
|
|
...(syncWithLocationKey
|
|
? { [syncWithLocationKey]: undefined }
|
|
: {}),
|
|
...refineCoreProps?.meta,
|
|
},
|
|
},
|
|
...rest,
|
|
});
|
|
|
|
const {
|
|
reset,
|
|
refineCore: { onFinish, id, setId, autoSaveProps },
|
|
saveButtonProps,
|
|
handleSubmit,
|
|
} = useHookFormResult;
|
|
|
|
const { visible, show, close } = useModal({
|
|
defaultVisible,
|
|
});
|
|
|
|
React.useEffect(() => {
|
|
if (initiallySynced === false && syncWithLocationKey) {
|
|
const openStatus = parsed?.params?.[syncWithLocationKey]?.open;
|
|
if (typeof openStatus === "boolean") {
|
|
if (openStatus) {
|
|
show();
|
|
}
|
|
} else if (typeof openStatus === "string") {
|
|
if (openStatus === "true") {
|
|
show();
|
|
}
|
|
}
|
|
|
|
if (syncingId) {
|
|
const idFromParams = parsed?.params?.[syncWithLocationKey]?.id;
|
|
if (idFromParams) {
|
|
setId?.(idFromParams);
|
|
}
|
|
}
|
|
|
|
setInitiallySynced(true);
|
|
}
|
|
}, [syncWithLocationKey, parsed, syncingId, setId]);
|
|
|
|
React.useEffect(() => {
|
|
if (initiallySynced === true) {
|
|
if (visible && syncWithLocationKey) {
|
|
go({
|
|
query: {
|
|
[syncWithLocationKey]: {
|
|
...parsed?.params?.[syncWithLocationKey],
|
|
open: true,
|
|
...(syncingId && id && { id }),
|
|
},
|
|
},
|
|
options: { keepQuery: true },
|
|
type: "replace",
|
|
});
|
|
} else if (syncWithLocationKey && !visible) {
|
|
go({
|
|
query: {
|
|
[syncWithLocationKey]: undefined,
|
|
},
|
|
options: { keepQuery: true },
|
|
type: "replace",
|
|
});
|
|
}
|
|
}
|
|
}, [id, visible, show, syncWithLocationKey, syncingId]);
|
|
|
|
const submit = async (values: TVariables) => {
|
|
await onFinish(values);
|
|
|
|
if (autoSubmitClose) {
|
|
close();
|
|
}
|
|
|
|
if (autoResetForm) {
|
|
reset();
|
|
}
|
|
};
|
|
|
|
const { warnWhen, setWarnWhen } = useWarnAboutChange();
|
|
const handleClose = useCallback(() => {
|
|
if (
|
|
autoSaveProps.status === "success" &&
|
|
refineCoreProps?.autoSave?.invalidateOnClose
|
|
) {
|
|
invalidate({
|
|
id,
|
|
invalidates: refineCoreProps.invalidates || [
|
|
"list",
|
|
"many",
|
|
"detail",
|
|
],
|
|
dataProviderName: refineCoreProps.dataProviderName,
|
|
resource: identifier,
|
|
});
|
|
}
|
|
|
|
if (warnWhen) {
|
|
const warnWhenConfirm = window.confirm(
|
|
translate(
|
|
"warnWhenUnsavedChanges",
|
|
"Are you sure you want to leave? You have unsaved changes.",
|
|
),
|
|
);
|
|
|
|
if (warnWhenConfirm) {
|
|
setWarnWhen(false);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
setId?.(undefined);
|
|
close();
|
|
}, [warnWhen, autoSaveProps.status]);
|
|
|
|
const handleShow = useCallback(
|
|
(showId?: BaseKey) => {
|
|
if (typeof showId !== "undefined") {
|
|
setId?.(showId);
|
|
}
|
|
const needsIdToOpen = action === "edit" || action === "clone";
|
|
const hasId =
|
|
typeof showId !== "undefined" || typeof id !== "undefined";
|
|
if (needsIdToOpen ? hasId : true) {
|
|
show();
|
|
}
|
|
},
|
|
[id],
|
|
);
|
|
|
|
const title = translate(
|
|
`${identifier}.titles.${actionProp}`,
|
|
undefined,
|
|
`${getUserFriendlyName(
|
|
`${actionProp} ${
|
|
resource?.meta?.label ??
|
|
resource?.options?.label ??
|
|
resource?.label ??
|
|
identifier
|
|
}`,
|
|
"singular",
|
|
)}`,
|
|
);
|
|
|
|
return {
|
|
modal: {
|
|
submit,
|
|
close: handleClose,
|
|
show: handleShow,
|
|
visible,
|
|
title,
|
|
},
|
|
...useHookFormResult,
|
|
saveButtonProps: {
|
|
...saveButtonProps,
|
|
onClick: (e) => handleSubmit(submit)(e),
|
|
},
|
|
};
|
|
};
|