mirror of
https://github.com/tale/headplane.git
synced 2026-08-27 15:37:04 +00:00
feat: switch to mist color from tailwind
This commit is contained in:
+151
-163
@@ -1,194 +1,182 @@
|
||||
import React, { cloneElement, useEffect, useRef } from 'react';
|
||||
import React, { cloneElement, useEffect, useRef } from "react";
|
||||
import {
|
||||
type AriaDialogProps,
|
||||
type AriaModalOverlayProps,
|
||||
Overlay,
|
||||
useDialog,
|
||||
useModalOverlay,
|
||||
useOverlayTrigger,
|
||||
} from 'react-aria';
|
||||
import { Form, type HTMLFormMethod } from 'react-router';
|
||||
type AriaDialogProps,
|
||||
type AriaModalOverlayProps,
|
||||
Overlay,
|
||||
useDialog,
|
||||
useModalOverlay,
|
||||
useOverlayTrigger,
|
||||
} from "react-aria";
|
||||
import { Form, type HTMLFormMethod } from "react-router";
|
||||
import {
|
||||
type OverlayTriggerProps,
|
||||
type OverlayTriggerState,
|
||||
useOverlayTriggerState,
|
||||
} from 'react-stately';
|
||||
import Button, { ButtonProps } from '~/components/Button';
|
||||
import Card from '~/components/Card';
|
||||
import IconButton, { IconButtonProps } from '~/components/IconButton';
|
||||
import Text from '~/components/Text';
|
||||
import Title from '~/components/Title';
|
||||
import cn from '~/utils/cn';
|
||||
import { useLiveData } from '~/utils/live-data';
|
||||
type OverlayTriggerProps,
|
||||
type OverlayTriggerState,
|
||||
useOverlayTriggerState,
|
||||
} from "react-stately";
|
||||
|
||||
import Button, { ButtonProps } from "~/components/Button";
|
||||
import Card from "~/components/Card";
|
||||
import IconButton, { IconButtonProps } from "~/components/IconButton";
|
||||
import Text from "~/components/Text";
|
||||
import Title from "~/components/Title";
|
||||
import cn from "~/utils/cn";
|
||||
import { useLiveData } from "~/utils/live-data";
|
||||
|
||||
export interface DialogProps extends OverlayTriggerProps {
|
||||
children:
|
||||
| [
|
||||
React.ReactElement<ButtonProps> | React.ReactElement<IconButtonProps>,
|
||||
React.ReactElement<DialogPanelProps>,
|
||||
]
|
||||
| React.ReactElement<DialogPanelProps>;
|
||||
children:
|
||||
| [
|
||||
React.ReactElement<ButtonProps> | React.ReactElement<IconButtonProps>,
|
||||
React.ReactElement<DialogPanelProps>,
|
||||
]
|
||||
| React.ReactElement<DialogPanelProps>;
|
||||
}
|
||||
|
||||
function Dialog(props: DialogProps) {
|
||||
const { pause, resume } = useLiveData();
|
||||
const state = useOverlayTriggerState(props);
|
||||
const { triggerProps, overlayProps } = useOverlayTrigger(
|
||||
{
|
||||
type: 'dialog',
|
||||
},
|
||||
state,
|
||||
);
|
||||
const { pause, resume } = useLiveData();
|
||||
const state = useOverlayTriggerState(props);
|
||||
const { triggerProps, overlayProps } = useOverlayTrigger(
|
||||
{
|
||||
type: "dialog",
|
||||
},
|
||||
state,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.isOpen) {
|
||||
pause();
|
||||
} else {
|
||||
resume();
|
||||
}
|
||||
}, [state.isOpen]);
|
||||
useEffect(() => {
|
||||
if (state.isOpen) {
|
||||
pause();
|
||||
} else {
|
||||
resume();
|
||||
}
|
||||
}, [state.isOpen]);
|
||||
|
||||
if (Array.isArray(props.children)) {
|
||||
const [button, panel] = props.children;
|
||||
return (
|
||||
<>
|
||||
{cloneElement(button, triggerProps)}
|
||||
{state.isOpen && (
|
||||
<DModal state={state}>
|
||||
{cloneElement(panel, {
|
||||
...overlayProps,
|
||||
close: () => state.close(),
|
||||
})}
|
||||
</DModal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (Array.isArray(props.children)) {
|
||||
const [button, panel] = props.children;
|
||||
return (
|
||||
<>
|
||||
{cloneElement(button, triggerProps)}
|
||||
{state.isOpen && (
|
||||
<DModal state={state}>
|
||||
{cloneElement(panel, {
|
||||
...overlayProps,
|
||||
close: () => state.close(),
|
||||
})}
|
||||
</DModal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DModal state={state}>
|
||||
{cloneElement(props.children, {
|
||||
...overlayProps,
|
||||
close: () => state.close(),
|
||||
})}
|
||||
</DModal>
|
||||
);
|
||||
return (
|
||||
<DModal state={state}>
|
||||
{cloneElement(props.children, {
|
||||
...overlayProps,
|
||||
close: () => state.close(),
|
||||
})}
|
||||
</DModal>
|
||||
);
|
||||
}
|
||||
|
||||
export interface DialogPanelProps extends AriaDialogProps {
|
||||
children: React.ReactNode;
|
||||
variant?: 'normal' | 'destructive' | 'unactionable';
|
||||
onSubmit?: React.FormEventHandler<HTMLFormElement>;
|
||||
method?: HTMLFormMethod;
|
||||
isDisabled?: boolean;
|
||||
children: React.ReactNode;
|
||||
variant?: "normal" | "destructive" | "unactionable";
|
||||
onSubmit?: React.FormEventHandler<HTMLFormElement>;
|
||||
method?: HTMLFormMethod;
|
||||
isDisabled?: boolean;
|
||||
|
||||
// Anonymous (passed by parent)
|
||||
close?: () => void;
|
||||
// Anonymous (passed by parent)
|
||||
close?: () => void;
|
||||
}
|
||||
|
||||
function Panel(props: DialogPanelProps) {
|
||||
const {
|
||||
children,
|
||||
onSubmit,
|
||||
isDisabled,
|
||||
close,
|
||||
variant,
|
||||
method = 'POST',
|
||||
} = props;
|
||||
const ref = useRef<HTMLFormElement | null>(null);
|
||||
const { dialogProps } = useDialog(
|
||||
{
|
||||
...props,
|
||||
role: 'alertdialog',
|
||||
},
|
||||
ref,
|
||||
);
|
||||
const { children, onSubmit, isDisabled, close, variant, method = "POST" } = props;
|
||||
const ref = useRef<HTMLFormElement | null>(null);
|
||||
const { dialogProps } = useDialog(
|
||||
{
|
||||
...props,
|
||||
role: "alertdialog",
|
||||
},
|
||||
ref,
|
||||
);
|
||||
|
||||
return (
|
||||
<Form
|
||||
{...dialogProps}
|
||||
className={cn(
|
||||
'outline-hidden rounded-3xl w-full max-w-lg',
|
||||
'bg-white dark:bg-headplane-900',
|
||||
)}
|
||||
method={method ?? 'POST'}
|
||||
onSubmit={(event) => {
|
||||
if (onSubmit) {
|
||||
onSubmit(event);
|
||||
}
|
||||
return (
|
||||
<Form
|
||||
{...dialogProps}
|
||||
className={cn("outline-hidden rounded-lg w-full max-w-lg", "bg-white dark:bg-mist-900")}
|
||||
method={method ?? "POST"}
|
||||
onSubmit={(event) => {
|
||||
if (onSubmit) {
|
||||
onSubmit(event);
|
||||
}
|
||||
|
||||
close?.();
|
||||
}}
|
||||
ref={ref}
|
||||
>
|
||||
<Card className="w-full max-w-lg" variant="flat">
|
||||
{children}
|
||||
<div className="mt-6 flex justify-end gap-4">
|
||||
{variant === 'unactionable' ? (
|
||||
<Button onPress={close}>Close</Button>
|
||||
) : (
|
||||
<>
|
||||
<Button onPress={close}>Cancel</Button>
|
||||
<Button
|
||||
isDisabled={isDisabled}
|
||||
type="submit"
|
||||
variant={variant === 'destructive' ? 'danger' : 'heavy'}
|
||||
>
|
||||
Confirm
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Form>
|
||||
);
|
||||
close?.();
|
||||
}}
|
||||
ref={ref}
|
||||
>
|
||||
<Card className="w-full max-w-lg" variant="flat">
|
||||
{children}
|
||||
<div className="mt-6 flex justify-end gap-4">
|
||||
{variant === "unactionable" ? (
|
||||
<Button onPress={close}>Close</Button>
|
||||
) : (
|
||||
<>
|
||||
<Button onPress={close}>Cancel</Button>
|
||||
<Button
|
||||
isDisabled={isDisabled}
|
||||
type="submit"
|
||||
variant={variant === "destructive" ? "danger" : "heavy"}
|
||||
>
|
||||
Confirm
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
interface DModalProps extends AriaModalOverlayProps {
|
||||
children: React.ReactNode;
|
||||
state: OverlayTriggerState;
|
||||
children: React.ReactNode;
|
||||
state: OverlayTriggerState;
|
||||
}
|
||||
|
||||
function DModal(props: DModalProps) {
|
||||
const { children, state } = props;
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { modalProps, underlayProps } = useModalOverlay(props, state, ref);
|
||||
const { children, state } = props;
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { modalProps, underlayProps } = useModalOverlay(props, state, ref);
|
||||
|
||||
if (!state.isOpen) {
|
||||
return null;
|
||||
}
|
||||
if (!state.isOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Overlay>
|
||||
<div
|
||||
{...underlayProps}
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
'fixed inset-0 h-screen w-screen z-20',
|
||||
'flex items-center justify-center',
|
||||
'bg-headplane-900/15 dark:bg-headplane-900/30',
|
||||
'entering:animate-in exiting:animate-out',
|
||||
'entering:fade-in entering:duration-100 entering:ease-out',
|
||||
'exiting:fade-out exiting:duration-50 exiting:ease-in',
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
{...modalProps}
|
||||
className={cn(
|
||||
'fixed inset-0 h-screen w-screen z-20',
|
||||
'flex items-center justify-center',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Overlay>
|
||||
);
|
||||
return (
|
||||
<Overlay>
|
||||
<div
|
||||
{...underlayProps}
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
"fixed inset-0 h-screen w-screen z-20",
|
||||
"flex items-center justify-center",
|
||||
"bg-mist-900/15 dark:bg-mist-900/30",
|
||||
"entering:animate-in exiting:animate-out",
|
||||
"entering:fade-in entering:duration-100 entering:ease-out",
|
||||
"exiting:fade-out exiting:duration-50 exiting:ease-in",
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
{...modalProps}
|
||||
className={cn("fixed inset-0 h-screen w-screen z-20", "flex items-center justify-center")}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Overlay>
|
||||
);
|
||||
}
|
||||
|
||||
export default Object.assign(Dialog, {
|
||||
Button,
|
||||
IconButton,
|
||||
Panel,
|
||||
Title,
|
||||
Text,
|
||||
Button,
|
||||
IconButton,
|
||||
Panel,
|
||||
Title,
|
||||
Text,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user