{"version":3,"file":"index.mjs","names":["useMemo","useState","clsx","Button","Modal","ModalContent","ModalDescription","ModalFooter","ModalHeader","Media","Props","onPick","media","allowedTypes","imageRatio","className","id","mockMedias","label","origin","category","mimeType","width","height","size","src","UploaderMock","t0","$","_c","isOpen","setIsOpen","t1","t2","cssProperties","t3","t4","Symbol","for","t5","t6","t7","map","t8","idx","t9","t10","t11","t12","useControl","useField","FieldName","Field","FieldProps","isMediaImage","MediaPreview","Button","UploaderMock","Media","Props","Omit","name","mediaContainerClassName","allowedTypes","imageRatio","FileField","id","forcedId","rest","field","itemErrors","Object","values","fieldErrors","flat","errors","length","undefined","control","defaultValue","parse","payload","Error","JSON","serialize","value","stringify","media","register","change","console","log","z","mediaSchema","object","id","string","category","mimeType","width","optional","coerce","number","check","int","height","origin","enum","src","size","_default","nullable","Media","infer","MediaImage","Omit","parseMediaValue","payload","undefined","Error","JSON","parse","serializeMediaValue","value","stringify"],"sources":["../../../../src/components/fields/file/UploaderMock.tsx","../../../../src/components/fields/file/FileField.tsx","../../../../src/components/fields/file/types.ts"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport clsx from \"clsx\";\nimport { Button } from \"../../button/Button\";\nimport { Modal, ModalContent, ModalDescription, ModalFooter, ModalHeader } from \"../../modal\";\nimport type { Media } from \"./types\";\n\ninterface Props {\n  onPick: (media: Media | null) => void;\n  allowedTypes?: \"image\" | \"audio\" | \"all-safe\";\n  imageRatio?: number;\n  className?: string;\n  id?: string;\n}\n\nconst mockMedias: { label: string; media: Media | null }[] = [\n  {\n    label: \"Image paysage\",\n    media: {\n      id: \"mock-landscape\",\n      origin: \"external\",\n      category: \"image\",\n      mimeType: \"image/jpg\",\n      width: 400,\n      height: 300,\n      size: null,\n      src: \"/medias/400x300.jpg\",\n    },\n  },\n  {\n    label: \"Image portrait\",\n    media: {\n      id: \"mock-portrait\",\n      origin: \"external\",\n      category: \"image\",\n      mimeType: \"image/jpeg\",\n      width: 600,\n      height: 900,\n      size: null,\n      src: \"https://picsum.photos/600/900\",\n    },\n  },\n  {\n    label: \"Audio\",\n    media: {\n      id: \"mock-audio\",\n      origin: \"external\",\n      category: \"audio\",\n      mimeType: \"audio/mpeg\",\n      size: null,\n      src: \"/audio/mock-track.mp3\",\n    },\n  },\n  {\n    label: \"PDF\",\n    media: {\n      id: \"mock-pdf\",\n      origin: \"external\",\n      category: \"document\",\n      mimeType: \"application/pdf\",\n      size: null,\n      src: \"/documents/mock-file.pdf\",\n    },\n  },\n  {\n    label: \"Null\",\n    media: null,\n  },\n];\n\nexport function UploaderMock({ onPick, imageRatio, className, id }: Props) {\n  const [isOpen, setIsOpen] = useState(false);\n\n  const cssProperties = useMemo(() => ({ \"--media-ratio\": imageRatio ?? 16 / 9 }), [imageRatio]);\n\n  return (\n    <div\n      className={clsx(\n        \"bg-gray-0/50 outline-gray-2 relative flex aspect-(--media-ratio) flex-col items-center justify-center rounded-2xl outline\",\n        className,\n      )}\n      style={cssProperties}\n    >\n      <Button id={id} type=\"button\" onClick={() => setIsOpen(true)}>\n        Choisir un média (mock)\n      </Button>\n\n      <Modal open={isOpen} onOpen={setIsOpen}>\n        <ModalContent>\n          <ModalHeader>Choisir un média (mock)</ModalHeader>\n          <ModalDescription>\n            <div className=\"flex flex-col gap-2 p-4\">\n              {mockMedias.map(({ label, media }, idx) => (\n                <Button\n                  key={media?.id ?? idx}\n                  type=\"button\"\n                  onClick={() => {\n                    onPick(media);\n                    setIsOpen(false);\n                  }}\n                >\n                  {label}\n                </Button>\n              ))}\n            </div>\n          </ModalDescription>\n          <ModalFooter>\n            <div className=\"flex justify-end\">\n              <Button variant=\"text\" color=\"gray\" onClick={() => setIsOpen(false)}>\n                Annuler\n              </Button>\n            </div>\n          </ModalFooter>\n        </ModalContent>\n      </Modal>\n    </div>\n  );\n}\n","import { useControl, useField, type FieldName } from \"@conform-to/react/future\";\nimport { Field, type FieldProps } from \"../field/Field\";\nimport { isMediaImage, MediaPreview } from \"../../media-preview\";\nimport { Button } from \"../../button/Button\";\n\nimport { UploaderMock } from \"./UploaderMock\";\nimport { Media } from \"./types\";\n\ntype Props = Omit<FieldProps, \"children\" | \"group\"> & {\n  name: FieldName<Media | null | undefined>;\n  mediaContainerClassName?: string;\n  allowedTypes?: \"image\" | \"audio\" | \"all-safe\";\n  imageRatio?: number;\n};\n\nexport function FileField({\n  name,\n  mediaContainerClassName,\n  allowedTypes = \"all-safe\",\n  imageRatio,\n  id: forcedId,\n  ...rest\n}: Props) {\n  const field = useField(name);\n  const id = forcedId ?? field.id;\n\n  const itemErrors = Object.values(field.fieldErrors).flat();\n  const errors = field.errors ?? (itemErrors.length > 0 ? itemErrors : undefined);\n\n  const control = useControl<Media, string>({\n    defaultValue: field.defaultValue,\n    parse(payload) {\n      if (typeof payload !== \"string\") {\n        throw new Error(\"media input must return string\");\n      }\n      if (payload == null || payload === \"\") {\n        return undefined;\n      }\n      return JSON.parse(payload);\n    },\n    // appelé uniquement par control.change(value) (ex: onPick, le bouton de suppression) :\n    // convertit le Media/null en string écrite dans l'input caché et déclenche l'event\n    // \"input\" natif. N'est pas utilisé pour le defaultValue initial (déjà une string).\n    serialize(value) {\n      return typeof value !== \"string\" && value != null ? JSON.stringify(value) : value;\n    },\n  });\n\n  const media = control.payload;\n\n  return (\n    <>\n      <input\n        name={field.name}\n        type=\"text\"\n        autoComplete=\"off\"\n        tabIndex={-1}\n        ref={control.register}\n        defaultValue={control.defaultValue ?? \"\"}\n      />\n      <Field id={id} errors={errors} data-testid={field.name} {...rest}>\n        {media ? (\n          <MediaPreview\n            media={media}\n            fit={isMediaImage(media) ? \"original\" : \"cover\"}\n            className={mediaContainerClassName}\n          >\n            <Button\n              onClick={() => {\n                control.change(null);\n              }}\n              type=\"button\"\n              icon\n              color=\"gray\"\n              size=\"large\"\n            >\n              <i className=\"fe-trash text-body-xl\"></i>\n            </Button>\n          </MediaPreview>\n        ) : (\n          <UploaderMock\n            id={id}\n            className={mediaContainerClassName}\n            onPick={(media: Media | null) => {\n              console.log(\"onPick\", media);\n              control.change(media);\n            }}\n            imageRatio={imageRatio}\n            allowedTypes={allowedTypes}\n          />\n        )}\n      </Field>\n    </>\n  );\n}\n","import { z } from \"zod/v4-mini\";\n\nexport const mediaSchema = z.object({\n  id: z.string(),\n  category: z.string(),\n  mimeType: z.string(),\n  width: z.optional(z.coerce.number().check(z.int())),\n  height: z.optional(z.coerce.number().check(z.int())),\n  origin: z.enum([\"s3Upload\", \"localUpload\", \"external\", \"asset\"]),\n  src: z.string(),\n  // in bytes\n  size: z._default(z.nullable(z.coerce.number().check(z.int())), null),\n});\n\nexport type Media = z.infer<typeof mediaSchema>;\n\nexport type MediaImage = Omit<Media, \"width\" | \"height\"> & {\n  width: number;\n  height: number;\n};\n\nexport function parseMediaValue(payload: unknown): Media | undefined {\n  if (payload == null || payload === \"\") {\n    return undefined;\n  }\n  if (typeof payload !== \"string\") {\n    throw new Error(\"media value must be a JSON string\");\n  }\n  try {\n    return JSON.parse(payload);\n  } catch {\n    // JSON corrompu (ex. donnée persistée avant une migration de schéma) : on dégrade\n    // gracieusement vers \"absent\" plutôt que de faire planter le rendu du champ.\n    return undefined;\n  }\n}\n\nexport function serializeMediaValue(value: unknown): string | null {\n  return value == null ? null : typeof value === \"string\" ? value : JSON.stringify(value);\n}\n"],"mappings":"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"}