import { useState } from "react"; import { Avatar, AvatarFallback, AvatarImage, Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@godxjp/ui/data-display"; import { Upload, UploadCropDialog } from "@godxjp/ui/data-entry"; import { Text } from "@godxjp/ui/general"; import { Flex, PageContainer } from "@godxjp/ui/layout"; /** * UploadCropDialog — the square-crop step of an avatar flow, on its own. * * MOST SCREENS SHOULD NOT REACH FOR THIS. `` already embeds this * dialog and owns the picker, the object URL and the commit; mounting both double-mounts the * dialog. The standalone component exists for the case where the FILE ARRIVES FROM SOMEWHERE ELSE * — a drag-drop zone you already own, a paste handler, a file already on the server — and only the * crop step is wanted. That is the case this frame shows, beside the composed one for contrast. * * It is fully controlled: `open`, `file` and `onConfirm` are all required, and the dialog never * opens itself. It creates and revokes its own object URL, so a caller passes the raw `File` and * does NOT pre-create one. The result is always a 256×256 image/jpeg. */ export default function Demo() { const [cropFile, setCropFile] = useState(null); const [avatar, setAvatar] = useState(null); const [lastResult, setLastResult] = useState(null); return ( 単体で使う ファイルの取得は呼び出し側の責任です。ここではブラウザのピッカーから受け取り、 切り抜き結果だけをこのダイアログから受け取ります。 {avatar ? : null} {/* The picker is the PAGE's, not the dialog's — that is the whole point of using this component standalone. It is `Upload variant="button"` rather than a raw : rule §3 forbids a raw control in an example, because a consumer copies what they see. `onUpload` hands back the File, which is exactly what UploadCropDialog wants. */} { /* No onUpload here on purpose: this page wants the FILE, not an upload. The catalog states it outright — "without onUpload the File object sits in item.file until you manually process it", which is exactly the standalone crop case. */ const picked = items[0]?.file ?? null; if (picked) setCropFile(picked); }} > 写真を選ぶ {lastResult ?? "まだ切り抜いていません。"} !open && setCropFile(null)} file={cropFile} onConfirm={(cropped) => { setAvatar(URL.createObjectURL(cropped)); setLastResult( `${cropped.name} · ${cropped.type} · ${Math.round(cropped.size / 1024)} KB`, ); }} /> 通常はこちら `Upload variant="avatar-crop"` は同じダイアログを内部に持ち、ピッカーも オブジェクトURLも引き受けます。両方を同時に置いてはいけません。 ); }