mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
wip: course layout and func to context
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
// components/generic/CMS/blocks/TextImageBlock.jsx
|
||||
|
||||
import { useState } from "react";
|
||||
import { ImageIcon } from "lucide-react";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { AssetPickerSheet } from "../AssetPickerSheet";
|
||||
|
||||
export function TextImageBlock({ content, onUpdate }) {
|
||||
const [pickerOpen, setPickerOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
|
||||
{/* ── Layout ── */}
|
||||
<div className="space-y-1.5 max-w-[200px]">
|
||||
<Label>Image Position</Label>
|
||||
<Select
|
||||
value={content.image_position ?? "right"}
|
||||
onValueChange={(v) => onUpdate({ ...content, image_position: v })}
|
||||
>
|
||||
<SelectTrigger><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="left">Left</SelectItem>
|
||||
<SelectItem value="right">Right</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className={[
|
||||
"grid gap-4",
|
||||
"grid-cols-1 md:grid-cols-2",
|
||||
].join(" ")}>
|
||||
|
||||
{/* ── Text side ── */}
|
||||
<div className="space-y-1.5">
|
||||
<Label>Content</Label>
|
||||
<Textarea
|
||||
placeholder="Enter text content..."
|
||||
rows={5}
|
||||
value={content.body ?? ""}
|
||||
onChange={(e) => onUpdate({ ...content, body: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* ── Image side ── */}
|
||||
<div className={[
|
||||
"space-y-1.5",
|
||||
content.image_position === "left" ? "md:order-first" : "",
|
||||
].join(" ")}>
|
||||
<Label>Image</Label>
|
||||
|
||||
{content.url ? (
|
||||
<div
|
||||
className="relative rounded-lg overflow-hidden cursor-pointer group"
|
||||
onClick={() => setPickerOpen(true)}
|
||||
>
|
||||
<img
|
||||
src={content.url}
|
||||
alt={content.alt ?? ""}
|
||||
className="w-full aspect-video object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
|
||||
<p className="text-white text-sm font-medium">Change Image</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPickerOpen(true)}
|
||||
className="w-full aspect-video rounded-lg border-2 border-dashed border-muted-foreground/30 hover:border-primary/50 hover:bg-muted/20 transition-colors flex flex-col items-center justify-center gap-2"
|
||||
>
|
||||
<ImageIcon className="h-8 w-8 text-muted-foreground/50" />
|
||||
<p className="text-sm text-muted-foreground">Click to select an image</p>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{content.url && (
|
||||
<div className="space-y-1.5">
|
||||
<Label>Alt Text</Label>
|
||||
<Input
|
||||
placeholder="Describe the image..."
|
||||
value={content.alt ?? ""}
|
||||
onChange={(e) => onUpdate({ ...content, alt: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AssetPickerSheet
|
||||
open={pickerOpen}
|
||||
onOpenChange={setPickerOpen}
|
||||
fileType="image"
|
||||
onSelect={(asset) => onUpdate({
|
||||
...content,
|
||||
asset_id: asset.asset_id,
|
||||
url: asset.file_url,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user