Files
sass-mohfarawati/components/root/media-field-picker.tsx
T
2026-03-07 18:48:01 +01:00

157 lines
4.5 KiB
TypeScript

"use client";
/* eslint-disable @next/next/no-img-element */
import type { MediaKind } from "@prisma/client";
import type { MediaOption } from "@/lib/media";
import { cn } from "@/lib/utils";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
export type MediaFieldState = {
mode: "upload" | "external" | "library";
assetId: string;
url: string;
label: string;
kind: MediaKind;
};
type MediaFieldPickerProps = {
title: string;
value: MediaFieldState;
onChange: (nextValue: MediaFieldState) => void;
options: MediaOption[];
inputName: string;
fileFieldName: string;
fileLabel?: string;
externalLabel?: string;
libraryLabel?: string;
accept?: string;
};
const modeOptions: Array<MediaFieldState["mode"]> = ["upload", "external", "library"];
export function MediaFieldPicker({
title,
value,
onChange,
options,
inputName,
fileFieldName,
fileLabel,
externalLabel,
libraryLabel,
accept,
}: MediaFieldPickerProps) {
const filteredOptions = options.filter((option) => option.kind === value.kind);
const selectedOption = filteredOptions.find((option) => option.id === value.assetId) ?? null;
const previewUrl =
value.mode === "library"
? selectedOption?.url ?? ""
: value.mode === "external"
? value.url
: "";
return (
<div className="space-y-3 rounded-surface border border-border p-4">
<div className="space-y-2">
<Label>{title}</Label>
<div className="flex flex-wrap gap-2">
{modeOptions.map((mode) => (
<button
key={mode}
type="button"
onClick={() =>
onChange({
...value,
mode,
assetId: mode === "library" ? value.assetId : "",
url: mode === "external" ? value.url : "",
})
}
className={cn(
"rounded-pill border px-4 py-2 text-sm transition-colors",
value.mode === mode
? "border-border-strong bg-foreground text-background"
: "border-border bg-background text-foreground/75 hover:border-border-strong hover:text-foreground",
)}
>
{mode}
</button>
))}
</div>
</div>
<input
type="hidden"
name={inputName}
value={JSON.stringify({
mode: value.mode,
assetId: value.assetId,
url: value.url,
label: value.label,
kind: value.kind,
})}
/>
<div className="space-y-2">
<Label>Label</Label>
<Input
value={value.label}
onChange={(event) => onChange({ ...value, label: event.target.value })}
placeholder="Homepage Hero"
/>
</div>
{value.mode === "upload" ? (
<div className="space-y-2">
<Label>{fileLabel ?? fileFieldName}</Label>
<Input name={fileFieldName} type="file" accept={accept} />
</div>
) : null}
{value.mode === "external" ? (
<div className="space-y-2">
<Label>{externalLabel ?? "External URL"}</Label>
<Input
value={value.url}
onChange={(event) => onChange({ ...value, url: event.target.value })}
placeholder="https://example.com/image.jpg"
/>
</div>
) : null}
{value.mode === "library" ? (
<div className="space-y-2">
<Label>{libraryLabel ?? "Media Library"}</Label>
<select
value={value.assetId}
onChange={(event) => onChange({ ...value, assetId: event.target.value })}
className="flex h-11 w-full rounded-pill border border-border bg-background px-4 text-sm text-foreground shadow-sm"
>
<option value="">Select media</option>
{filteredOptions.map((option) => (
<option key={option.id} value={option.id}>
{option.label}
</option>
))}
</select>
</div>
) : null}
{previewUrl ? (
value.kind === "IMAGE" ? (
<div className="overflow-hidden rounded-nested border border-border bg-surface-1">
<img src={previewUrl} alt={value.label || title} className="h-40 w-full object-cover" />
</div>
) : (
<div className="rounded-nested border border-border bg-surface-1 px-4 py-3 text-sm text-muted-foreground">
{previewUrl}
</div>
)
) : null}
</div>
);
}