feat(media): extract workspace package

This commit is contained in:
Maofeng
2026-09-20 16:44:15 +08:00
parent 2a6284d1b0
commit c519d4e74b
29 changed files with 112 additions and 16 deletions
+103
View File
@@ -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>
)
}