feat(overlays): add managed overlay package
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
import { useTranslate } from "@workspace/i18n"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import {
|
||||
createDrawerHandle,
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
} from "@workspace/ui/components/drawer"
|
||||
import { XIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
import { OverlayController, type OverlayRequest } from "./internal/controller"
|
||||
import { renderOverlayContent } from "./internal/render-content"
|
||||
import { useOverlayController } from "./internal/use-controller"
|
||||
import { overlayMessages } from "./messages"
|
||||
import { globalOverlayScope, type OverlayScope } from "./scope"
|
||||
import type { DrawerOptions } from "./types"
|
||||
|
||||
type DrawerRequestOptions = Omit<DrawerOptions<unknown>, "scope">
|
||||
const drawerChannel = Symbol("overlay.drawer")
|
||||
|
||||
function getDrawerController(scope: OverlayScope) {
|
||||
return scope.getChannel(drawerChannel, () => {
|
||||
const handle = createDrawerHandle<OverlayRequest<DrawerRequestOptions>>()
|
||||
return new OverlayController<DrawerRequestOptions, typeof handle>({
|
||||
handle,
|
||||
isDismissible: (options) => options.dismissible !== false,
|
||||
scope,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function openDrawer<Result = void>(options: DrawerOptions<Result>) {
|
||||
const { scope = globalOverlayScope, ...drawerOptions } = options
|
||||
return getDrawerController(scope).open<Result | undefined>(
|
||||
drawerOptions as DrawerRequestOptions,
|
||||
undefined
|
||||
)
|
||||
}
|
||||
|
||||
export interface DrawerHostProps {
|
||||
scope?: OverlayScope
|
||||
}
|
||||
|
||||
export function DrawerHost({ scope }: DrawerHostProps) {
|
||||
const controller = useOverlayController(scope, getDrawerController)
|
||||
const activeOptions = controller.getActive()?.options
|
||||
|
||||
return (
|
||||
<Drawer<OverlayRequest<DrawerRequestOptions>>
|
||||
disablePointerDismissal={activeOptions?.dismissible === false}
|
||||
handle={controller.handle}
|
||||
modal={activeOptions?.modal}
|
||||
showSwipeHandle={activeOptions?.showSwipeHandle}
|
||||
snapPoints={activeOptions?.snapPoints}
|
||||
swipeDirection={activeOptions?.swipeDirection}
|
||||
onOpenChange={(open, details) => {
|
||||
if (!open) controller.handleCloseRequest(details)
|
||||
}}
|
||||
onOpenChangeComplete={(open) => {
|
||||
if (!open) controller.handleCloseComplete()
|
||||
}}
|
||||
>
|
||||
{({ payload }) =>
|
||||
payload ? (
|
||||
<DrawerContentView
|
||||
key={payload.id}
|
||||
options={payload.options}
|
||||
onDismiss={controller.dismiss}
|
||||
onResolve={(value) => controller.resolve(value)}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
function DrawerContentView({
|
||||
onDismiss,
|
||||
onResolve,
|
||||
options,
|
||||
}: {
|
||||
onDismiss: () => void
|
||||
onResolve: (value: unknown) => void
|
||||
options: DrawerRequestOptions
|
||||
}) {
|
||||
const t = useTranslate()
|
||||
const dismissible = options.dismissible !== false
|
||||
|
||||
return (
|
||||
<DrawerContent className={options.className}>
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>{options.title}</DrawerTitle>
|
||||
{options.description != null && (
|
||||
<DrawerDescription>{options.description}</DrawerDescription>
|
||||
)}
|
||||
</DrawerHeader>
|
||||
<div className="min-h-0 flex-1 overflow-auto p-4">
|
||||
{renderOverlayContent(options.content, {
|
||||
close: onResolve,
|
||||
dismiss: onDismiss,
|
||||
})}
|
||||
</div>
|
||||
{dismissible && (
|
||||
<DrawerClose
|
||||
aria-label={t(overlayMessages.close)}
|
||||
onClick={onDismiss}
|
||||
render={
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
className="absolute end-4 top-4"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<XIcon aria-hidden="true" />
|
||||
</DrawerClose>
|
||||
)}
|
||||
</DrawerContent>
|
||||
)
|
||||
}
|
||||
|
||||
export type { DrawerOptions } from "./types"
|
||||
Reference in New Issue
Block a user