Start time grid #62
82
frontend/src/components/InlineModal/InlineModal.tsx
Normal file
82
frontend/src/components/InlineModal/InlineModal.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import type { HTMLAttributes, ReactNode } from "react";
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
export type ArrowPosition = "left" | "right";
|
||||||
|
|
||||||
|
export interface InlineModalProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
|
arrowPosition?: ArrowPosition;
|
||||||
|
arrowOffset?: number;
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseClasses = clsx(
|
||||||
|
"relative",
|
||||||
|
"w-full max-w-[450px] md:max-w-none",
|
||||||
|
"bg-sky-35",
|
||||||
|
"border-[length:var(--border-width-sm)] border-sky-100",
|
||||||
|
"rounded-(--border-radius-lg)",
|
||||||
|
"p-(--padding-lg)",
|
||||||
|
);
|
||||||
|
|
||||||
|
const contentClasses = clsx("flex flex-col", "gap-(--spacing-lg)");
|
||||||
|
|
||||||
|
// Position arrow so its center is 48px from the edge
|
||||||
|
// Values are offset by -10px to account for arrow border rendering
|
||||||
|
const arrowPositionClasses: Record<ArrowPosition, string> = {
|
||||||
|
left: "left-[38px]",
|
||||||
|
right: "right-[58px]",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function InlineModal({
|
||||||
|
stne3960 marked this conversation as resolved
Outdated
|
|||||||
|
arrowPosition = "left",
|
||||||
|
arrowOffset,
|
||||||
|
children,
|
||||||
|
className = "",
|
||||||
|
...props
|
||||||
|
}: InlineModalProps) {
|
||||||
|
const useCustomOffset = arrowOffset !== undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx(baseClasses, className)} {...props}>
|
||||||
|
{/* Arrow pointing up - uses two layered CSS triangles to create a bordered arrow effect.
|
||||||
|
stne3960 marked this conversation as resolved
ansv7779
commented
Everything else is a SVG icon, why not this too? Using a CSS border trick feels like a relic from the past. Everything else is a SVG icon, why not this too? Using a CSS border trick feels like a relic from the past.
|
|||||||
|
CSS triangles are made with borders on a zero-size element, but can't have their own border/stroke.
|
||||||
|
Solution: layer two triangles - a larger one in border color behind, a smaller one in background color on top. */}
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"absolute -top-[9px]",
|
||||||
|
!useCustomOffset && arrowPositionClasses[arrowPosition],
|
||||||
|
)}
|
||||||
|
style={useCustomOffset ? { left: arrowOffset } : undefined}
|
||||||
|
>
|
||||||
|
{/* Border arrow: triangle in border color (sky-100), sits behind */}
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"absolute",
|
||||||
|
"w-0 h-0",
|
||||||
|
"border-l-[10px] border-l-transparent",
|
||||||
|
"border-r-[10px] border-r-transparent",
|
||||||
|
"border-b-[10px] border-b-sky-100",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{/* Fill arrow: triangle in background color (sky-35), offset 1px down to cover inner part,
|
||||||
|
leaving only the border visible around the edge */}
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"absolute top-[1px]",
|
||||||
|
"w-0 h-0",
|
||||||
|
"border-l-[10px] border-l-transparent",
|
||||||
|
"border-r-[10px] border-r-transparent",
|
||||||
|
"border-b-[10px] border-b-sky-35",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className={contentClasses}>{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InlineModalDivider() {
|
||||||
|
return <hr className="border-t border-sky-100 m-0" />;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user
This needs some documentation what it is. Inline and modal are to me opposing concepts. What actually is it? When would I use it compared to a modal dialog and how is it different from just a
<div>?