Files
flxn-app/src/components/sheet/slide-panel/slide-panel.tsx
2025-09-09 23:20:19 -05:00

187 lines
4.8 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"
/>
<Box>
<panelConfig.Component
value={tempValue}
onChange={setTempValue}
{...(panelConfig.componentProps || {})}
/>
</Box>
</>
)}
</Box>
</Box>
</SlidePanelContext.Provider>
);
};
export { SlidePanel };