186 lines
4.9 KiB
TypeScript
186 lines
4.9 KiB
TypeScript
import {
|
|
Box,
|
|
Text,
|
|
Group,
|
|
ActionIcon,
|
|
ScrollArea,
|
|
Divider,
|
|
} from "@mantine/core";
|
|
import { ArrowLeftIcon, CheckIcon } from "@phosphor-icons/react";
|
|
import { useState, ReactNode } from "react";
|
|
import { SlidePanelContext, type PanelConfig } from "./slide-panel-context";
|
|
import Button from "@/components/button";
|
|
|
|
interface SlidePanelProps {
|
|
children: ReactNode;
|
|
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
|
|
onCancel?: () => void;
|
|
submitText?: string;
|
|
cancelText?: string;
|
|
cancelColor?: string;
|
|
maxHeight?: string;
|
|
formProps?: Record<string, any>;
|
|
loading?: boolean;
|
|
}
|
|
|
|
const SlidePanel = ({
|
|
children,
|
|
onSubmit,
|
|
onCancel,
|
|
submitText = "Submit",
|
|
cancelText = "Cancel",
|
|
cancelColor = "red",
|
|
maxHeight = "70vh",
|
|
formProps = {},
|
|
loading = false,
|
|
}: SlidePanelProps) => {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const [panelConfig, setPanelConfig] = useState<PanelConfig | null>(null);
|
|
const [tempValue, setTempValue] = useState<any>(null);
|
|
|
|
const openPanel = (config: PanelConfig) => {
|
|
setPanelConfig(config);
|
|
setTempValue(config.value);
|
|
setIsOpen(true);
|
|
};
|
|
|
|
const closePanel = () => {
|
|
setIsOpen(false);
|
|
};
|
|
|
|
const handleConfirm = () => {
|
|
if (panelConfig) {
|
|
panelConfig.onChange(tempValue);
|
|
}
|
|
setIsOpen(false);
|
|
};
|
|
|
|
const handleFormSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
event.preventDefault();
|
|
onSubmit(event);
|
|
};
|
|
|
|
return (
|
|
<SlidePanelContext.Provider value={{ openPanel, closePanel }}>
|
|
<Box
|
|
style={{
|
|
position: "relative",
|
|
height: maxHeight,
|
|
overflow: "hidden",
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
}}
|
|
>
|
|
<Box
|
|
style={{
|
|
position: "absolute",
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
transform: isOpen ? "translateX(-100%)" : "translateX(0)",
|
|
transition: "transform 0.3s ease-in-out",
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
}}
|
|
>
|
|
<form
|
|
{...formProps}
|
|
onSubmit={handleFormSubmit}
|
|
style={{
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
height: "100%",
|
|
...formProps.style,
|
|
}}
|
|
>
|
|
<ScrollArea
|
|
style={{ flex: 1 }}
|
|
scrollbarSize={8}
|
|
scrollbars="y"
|
|
type="always"
|
|
>
|
|
<Box p="md">{children}</Box>
|
|
</ScrollArea>
|
|
|
|
<Box p="sm">
|
|
<Group gap="md">
|
|
<Button
|
|
type="submit"
|
|
fullWidth
|
|
loading={loading}
|
|
disabled={loading}
|
|
>
|
|
{submitText}
|
|
</Button>
|
|
{onCancel && (
|
|
<Button
|
|
variant="subtle"
|
|
color={cancelColor}
|
|
fullWidth
|
|
onClick={onCancel}
|
|
type="button"
|
|
disabled={loading}
|
|
>
|
|
{cancelText}
|
|
</Button>
|
|
)}
|
|
</Group>
|
|
</Box>
|
|
</form>
|
|
</Box>
|
|
|
|
<Box
|
|
style={{
|
|
position: "absolute",
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
transform: isOpen ? "translateX(0)" : "translateX(100%)",
|
|
transition: "transform 0.3s ease-in-out",
|
|
backgroundColor: "var(--mantine-color-body)",
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
alignItems: "center",
|
|
}}
|
|
>
|
|
{panelConfig && (
|
|
<>
|
|
<Group justify="space-between" p="md" align="center" w="100%">
|
|
<ActionIcon variant="transparent" onClick={closePanel}>
|
|
<ArrowLeftIcon size={24} />
|
|
</ActionIcon>
|
|
<Text fw={500}>{panelConfig.title}</Text>
|
|
<ActionIcon
|
|
variant="transparent"
|
|
color="green"
|
|
onClick={handleConfirm}
|
|
>
|
|
<CheckIcon size={24} />
|
|
</ActionIcon>
|
|
</Group>
|
|
|
|
<Divider
|
|
h="1px"
|
|
w="100%"
|
|
bg="var(--mantine-color-dimmed)"
|
|
my="xs"
|
|
/>
|
|
<panelConfig.Component
|
|
value={tempValue}
|
|
onChange={setTempValue}
|
|
{...(panelConfig.componentProps || {})}
|
|
/>
|
|
<Button mt="md" onClick={handleConfirm}>Confirm</Button>
|
|
<Button variant="subtle" onClick={closePanel} mt="sm" color="red">Cancel</Button>
|
|
</>
|
|
)}
|
|
</Box>
|
|
</Box>
|
|
</SlidePanelContext.Provider>
|
|
);
|
|
};
|
|
|
|
export { SlidePanel };
|