feat(media): extract workspace package
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import * as React from "react"
|
||||
import { useTranslate } from "@workspace/i18n"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@workspace/ui/components/dialog"
|
||||
|
||||
import { useMedia } from "./context"
|
||||
import { MediaLibrary } from "./media-library"
|
||||
import { mediaMessages } from "./messages"
|
||||
import type { MediaAsset, MediaKind } from "./types"
|
||||
import { defaultMediaReference } from "./utils"
|
||||
|
||||
const EMPTY_SELECTION: readonly MediaAsset[] = []
|
||||
|
||||
export interface MediaPickerDialogProps {
|
||||
allowedKinds?: readonly MediaKind[]
|
||||
initialSelection?: readonly MediaAsset[]
|
||||
multiple?: boolean
|
||||
open: boolean
|
||||
onConfirm: (assets: readonly MediaAsset[]) => void
|
||||
onOpenChange: (open: boolean) => void
|
||||
}
|
||||
|
||||
export function MediaPickerDialog({
|
||||
allowedKinds,
|
||||
initialSelection = EMPTY_SELECTION,
|
||||
multiple = false,
|
||||
open,
|
||||
onConfirm,
|
||||
onOpenChange,
|
||||
}: MediaPickerDialogProps) {
|
||||
const { adapter } = useMedia()
|
||||
const t = useTranslate()
|
||||
const [selected, setSelected] =
|
||||
React.useState<readonly MediaAsset[]>(initialSelection)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (open) setSelected(initialSelection)
|
||||
}, [initialSelection, open])
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent
|
||||
className="flex h-[min(88dvh,54rem)] max-h-[calc(100dvh-2rem)] flex-col gap-3 p-4 sm:max-w-[min(94vw,86rem)]"
|
||||
showCloseButton={false}
|
||||
>
|
||||
<DialogHeader className="sr-only">
|
||||
<DialogTitle>{t(mediaMessages.selectMedia)}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{t(mediaMessages.selectMediaDescription)}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<MediaLibrary
|
||||
allowedKinds={allowedKinds}
|
||||
selectionMode={multiple ? "multiple" : "single"}
|
||||
selectedAssets={selected}
|
||||
onSelectionChange={setSelected}
|
||||
className="min-h-0 flex-1"
|
||||
footer={
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{t(mediaMessages.selectedCount, { count: selected.length })}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
{t(mediaMessages.cancel)}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
disabled={selected.length === 0}
|
||||
onClick={() => {
|
||||
onConfirm(
|
||||
selected.map((asset) => {
|
||||
const url =
|
||||
adapter.resolveReference?.(asset) ??
|
||||
defaultMediaReference(asset)
|
||||
return url === asset.url
|
||||
? asset
|
||||
: Object.assign({}, asset, { url })
|
||||
})
|
||||
)
|
||||
onOpenChange(false)
|
||||
}}
|
||||
>
|
||||
{t(mediaMessages.useSelected)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user