{"version":3,"file":"ImagePicker.cjs","sources":["../../../../src/components/forms/image-picker/ImagePicker.tsx"],"sourcesContent":["import FormLabel from '@components/forms/_shared/components/form-label/FormLabel.tsx'\r\nimport HelperText from '@components/forms/_shared/components/helper-text/HelperText.tsx'\r\nimport {\r\n    ImagePickerActions,\r\n    ImagePickerCropDialog,\r\n    ImagePickerInput,\r\n    ImagePickerPreview,\r\n    ImagePickerRemoveButton,\r\n} from '@components/forms/image-picker/components'\r\nimport { IMAGE_PICKER_DEFAULT_ACCEPT } from '@components/forms/image-picker/constants/imagePickerDefaults.const.ts'\r\nimport { IMAGE_PICKER_SHAPES } from '@components/forms/image-picker/constants'\r\nimport { IMAGE_PICKER_STYLES } from '@components/forms/image-picker/styles/ImagePicker.css.ts'\r\nimport type { ImagePickerProps } from '@components/forms/image-picker/types/ImagePicker.props.ts'\r\nimport { useImagePicker } from '@components/forms/image-picker/hooks/useImagePicker.hook.ts'\r\nimport { resolveImagePickerAspectRatio } from '@components/forms/image-picker/utils/imagePicker.utils.ts'\r\nimport { cn } from '@utils/cn.ts'\r\nimport { memo, useId } from 'react'\r\n\r\n/**\r\n * A controlled or uncontrolled single-image picker, designed first for\r\n * profile pictures. The preview is the button that opens the native chooser.\r\n * On desktop it accepts drag and drop, reveals a \"change\" hint on hover and\r\n * shows a corner remove button; with `isMobile` it renders explicit\r\n * touch-sized action buttons instead. Change and remove actions can be\r\n * disabled independently. Set `crop` to open a crop editor before a newly\r\n * selected image is committed.\r\n *\r\n * @example\r\n * ```tsx\r\n * import { useState } from 'react'\r\n * import { ImagePicker } from '@choco-vanille/ui'\r\n *\r\n * const ProfilePictureField = () => {\r\n *     const [value, setValue] = useState<File | string | null>(null)\r\n *\r\n *     return (\r\n *         <ImagePicker\r\n *             label=\"Profile picture\"\r\n *             name=\"profilePicture\"\r\n *             value={value}\r\n *             onValueChange={setValue}\r\n *             shape=\"circle\"\r\n *         />\r\n *     )\r\n * }\r\n * ```\r\n */\r\nconst ImagePicker = memo<ImagePickerProps>(\r\n    ({\r\n        value,\r\n        defaultValue,\r\n        onValueChange,\r\n        label,\r\n        alt,\r\n        helperText,\r\n        error,\r\n        accept: acceptProp,\r\n        maxSize,\r\n        onValidationError,\r\n        disabled = false,\r\n        disableChange = false,\r\n        disableRemove = false,\r\n        required = false,\r\n        clearable = true,\r\n        isMobile = false,\r\n        icon,\r\n        inputId,\r\n        name,\r\n        capture,\r\n        labels,\r\n        crop = false,\r\n        cropAspectRatio,\r\n        width,\r\n        height,\r\n        aspectRatio,\r\n        shape = IMAGE_PICKER_SHAPES.rounded,\r\n        objectFit = 'cover',\r\n        className,\r\n        style,\r\n        ref,\r\n        'aria-label': ariaLabel,\r\n        'aria-labelledby': ariaLabelledBy,\r\n        'aria-describedby': ariaDescribedBy,\r\n        'aria-invalid': ariaInvalid,\r\n        ...rootProps\r\n    }) => {\r\n        const generatedId = useId()\r\n        const resolvedInputId = inputId ?? generatedId\r\n        const labelId = label ? `${resolvedInputId}-label` : undefined\r\n        const actionId = `${resolvedInputId}-action`\r\n        const helperId = `${resolvedInputId}-helper`\r\n        const accept = acceptProp ?? IMAGE_PICKER_DEFAULT_ACCEPT\r\n        const {\r\n            inputRef,\r\n            previewUrl,\r\n            resolvedLabels,\r\n            validationMessage,\r\n            hasValue,\r\n            isChangeDisabled,\r\n            isRemoveDisabled,\r\n            isDragging,\r\n            handleOpen,\r\n            handleInputChange,\r\n            handleDragOver,\r\n            handleDragLeave,\r\n            handleDrop,\r\n            handleClear,\r\n            pendingCropFile,\r\n            handleCropCancel,\r\n            handleCropConfirm,\r\n        } = useImagePicker({\r\n            value,\r\n            defaultValue,\r\n            onValueChange,\r\n            accept,\r\n            maxSize,\r\n            onValidationError,\r\n            disabled,\r\n            disableChange,\r\n            disableRemove,\r\n            isMobile,\r\n            labels,\r\n            crop,\r\n        })\r\n        const displayedError = error ?? validationMessage\r\n        const hasError = Boolean(displayedError)\r\n        const buttonAriaInvalid = hasError ? true : ariaInvalid\r\n        const hasDescription = Boolean(displayedError || helperText)\r\n        const actionLabel = hasValue\r\n            ? resolvedLabels.change\r\n            : resolvedLabels.choose\r\n        // The picker button is named \"<label> <action>\", e.g. \"Profile picture Change image\".\r\n        const previewLabelledBy = ariaLabel\r\n            ? undefined\r\n            : [ariaLabelledBy ?? labelId, actionId].filter(Boolean).join(' ')\r\n        const describedBy =\r\n      [ariaDescribedBy, hasDescription ? helperId : undefined]\r\n          .filter(Boolean)\r\n          .join(' ') || undefined\r\n        const showDesktopRemove = !isMobile && hasValue && clearable\r\n        const resolvedCropAspectRatio = cropAspectRatio ?? (\r\n            shape === IMAGE_PICKER_SHAPES.circle\r\n                ? 1\r\n                : resolveImagePickerAspectRatio(aspectRatio)\r\n        )\r\n        const rootStyle = isMobile\r\n            ? style\r\n            : { width, ...style }\r\n        const previewWrapperStyle = isMobile ? { width } : undefined\r\n\r\n        return (\r\n            <div\r\n                ref={ref}\r\n                className={cn(IMAGE_PICKER_STYLES.root, className)}\r\n                style={rootStyle}\r\n                data-mobile={isMobile ? 'true' : 'false'}\r\n                {...rootProps}\r\n            >\r\n                {label && (\r\n                    <FormLabel\r\n                        id={labelId}\r\n                        disabled={disabled}\r\n                        required={required}\r\n                    >\r\n                        {label}\r\n                    </FormLabel>\r\n                )}\r\n\r\n                <div\r\n                    className={IMAGE_PICKER_STYLES.wrapper}\r\n                    style={previewWrapperStyle}\r\n                >\r\n                    <ImagePickerPreview\r\n                        actionId={actionId}\r\n                        actionLabel={actionLabel}\r\n                        alt={alt ?? label ?? resolvedLabels.selected}\r\n                        ariaDescribedBy={describedBy}\r\n                        ariaInvalid={buttonAriaInvalid}\r\n                        ariaLabel={ariaLabel}\r\n                        ariaLabelledBy={previewLabelledBy}\r\n                        aspectRatio={aspectRatio}\r\n                        disabled={isChangeDisabled}\r\n                        emptyLabel={resolvedLabels.empty}\r\n                        hasError={hasError}\r\n                        hasValue={hasValue}\r\n                        height={height}\r\n                        icon={icon}\r\n                        isDragging={isDragging}\r\n                        isMobile={isMobile}\r\n                        objectFit={objectFit}\r\n                        onClick={handleOpen}\r\n                        onDragLeave={handleDragLeave}\r\n                        onDragOver={handleDragOver}\r\n                        onDrop={handleDrop}\r\n                        previewUrl={previewUrl}\r\n                        shape={shape}\r\n                    />\r\n\r\n                    {showDesktopRemove && (\r\n                        <ImagePickerRemoveButton\r\n                            disabled={isRemoveDisabled}\r\n                            label={resolvedLabels.remove}\r\n                            onClick={handleClear}\r\n                            shape={shape}\r\n                        />\r\n                    )}\r\n\r\n                    <ImagePickerInput\r\n                        accept={accept}\r\n                        capture={capture}\r\n                        disabled={isChangeDisabled}\r\n                        id={resolvedInputId}\r\n                        inputRef={inputRef}\r\n                        name={name}\r\n                        required={required && !hasValue}\r\n                        onChange={handleInputChange}\r\n                    />\r\n                </div>\r\n\r\n                {isMobile && (\r\n                    <ImagePickerActions\r\n                        actionLabel={actionLabel}\r\n                        changeDisabled={isChangeDisabled}\r\n                        hasValue={hasValue}\r\n                        onChange={handleOpen}\r\n                        onRemove={handleClear}\r\n                        removeDisabled={isRemoveDisabled}\r\n                        removeLabel={resolvedLabels.remove}\r\n                        showRemove={clearable}\r\n                    />\r\n                )}\r\n\r\n                {hasDescription && (\r\n                    <HelperText\r\n                        id={helperId}\r\n                        hasError={hasError}\r\n                    >\r\n                        {displayedError ?? helperText}\r\n                    </HelperText>\r\n                )}\r\n\r\n                {pendingCropFile && (\r\n                    <ImagePickerCropDialog\r\n                        file={pendingCropFile}\r\n                        aspectRatio={resolvedCropAspectRatio}\r\n                        shape={shape}\r\n                        labels={resolvedLabels}\r\n                        onCancel={handleCropCancel}\r\n                        onConfirm={handleCropConfirm}\r\n                    />\r\n                )}\r\n            </div>\r\n        )\r\n    },\r\n)\r\n\r\nImagePicker.displayName = 'ImagePicker'\r\n\r\nexport default ImagePicker\r\n"],"names":["ImagePicker","memo","value","defaultValue","onValueChange","label","alt","helperText","error","acceptProp","maxSize","onValidationError","disabled","disableChange","disableRemove","required","clearable","isMobile","icon","inputId","name","capture","labels","crop","cropAspectRatio","width","height","aspectRatio","shape","IMAGE_PICKER_SHAPES","objectFit","className","style","ref","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaInvalid","rootProps","generatedId","useId","resolvedInputId","labelId","actionId","helperId","accept","IMAGE_PICKER_DEFAULT_ACCEPT","inputRef","previewUrl","resolvedLabels","validationMessage","hasValue","isChangeDisabled","isRemoveDisabled","isDragging","handleOpen","handleInputChange","handleDragOver","handleDragLeave","handleDrop","handleClear","pendingCropFile","handleCropCancel","handleCropConfirm","useImagePicker","displayedError","hasError","buttonAriaInvalid","hasDescription","actionLabel","previewLabelledBy","describedBy","showDesktopRemove","resolvedCropAspectRatio","resolveImagePickerAspectRatio","rootStyle","previewWrapperStyle","jsxs","cn","IMAGE_PICKER_STYLES","jsx","FormLabel","ImagePickerPreview","ImagePickerRemoveButton","ImagePickerInput","ImagePickerActions","HelperText","ImagePickerCropDialog"],"mappings":"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"}