import React from 'react'; import PropTypes from 'prop-types'; import * as tinyMCEKeys from '../../data/constants/tinyMCE'; import ImageSettingsModal from './ImageSettingsModal'; import SelectImageModal from './SelectImageModal'; // This 'module' self-import hack enables mocking during tests. // See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested // should be re-thought and cleaned up to avoid this pattern. // eslint-disable-next-line import/no-self-import import * as module from '.'; import { updateImageDimensions } from '../TinyMceWidget/hooks'; export const propsString = (props) => ( Object.keys(props).map((key) => `${key}="${props[key]}"`).join(' ') ); export const imgProps = ({ settings, selection, lmsEndpointUrl, editorType, isLibrary, }) => { let url = selection?.externalUrl; if (url?.startsWith(lmsEndpointUrl) && editorType !== 'expandable') { const sourceEndIndex = lmsEndpointUrl.length; url = url.substring(sourceEndIndex); } if (isLibrary) { const index = url.indexOf('static/'); url = url.substring(index); } return { src: url, alt: settings.isDecorative ? '' : settings.altText, width: settings.dimensions.width, height: settings.dimensions.height, }; }; export const saveToEditor = ({ settings, selection, lmsEndpointUrl, editorType, editorRef, isLibrary, }) => { const newImgTag = module.hooks.imgTag({ settings, selection, lmsEndpointUrl, editorType, isLibrary, }); editorRef.current.execCommand( tinyMCEKeys.commands.insertContent, false, newImgTag, ); }; export const updateImagesRef = ({ images, selection, height, width, newImage, }) => { const { result: mappedImages, foundMatch: imageAlreadyExists } = updateImageDimensions({ images: images.current, url: selection.externalUrl, height, width, }); // eslint-disable-next-line no-param-reassign images.current = imageAlreadyExists ? mappedImages : [...images.current, newImage]; }; export const updateReactState = ({ settings, selection, setSelection, images, }) => { const { height, width } = settings.dimensions; const newImage = { externalUrl: selection.externalUrl, altText: settings.altText, width, height, }; updateImagesRef({ images, selection, height, width, newImage, }); setSelection(newImage); }; export const hooks = { createSaveCallback: ({ close, ...args }) => ( settings, ) => { saveToEditor({ settings, ...args }); updateReactState({ settings, ...args }); close(); args.setSelection(null); }, onClose: ({ clearSelection, close }) => () => { clearSelection(); close(); }, imgTag: ({ settings, selection, lmsEndpointUrl, editorType, isLibrary, }) => { const props = module.imgProps({ settings, selection, lmsEndpointUrl, editorType, isLibrary, }); return ``; }, updateReactState, updateImagesRef, saveToEditor, imgProps, propsString, }; const ImageUploadModal = ({ // eslint-disable-next-line editorRef, isOpen, close, clearSelection, selection, setSelection, images, editorType, lmsEndpointUrl, isLibrary, }) => { if (selection && selection.externalUrl) { return ( ); } return ( ); }; ImageUploadModal.defaultProps = { editorRef: null, editorType: null, selection: null, }; ImageUploadModal.propTypes = { clearSelection: PropTypes.func.isRequired, close: PropTypes.func.isRequired, editorRef: PropTypes.oneOfType([ PropTypes.func, // eslint-disable-next-line react/forbid-prop-types PropTypes.shape({ current: PropTypes.any }), ]), isOpen: PropTypes.bool.isRequired, selection: PropTypes.shape({ url: PropTypes.string, externalUrl: PropTypes.string, altText: PropTypes.bool, }), setSelection: PropTypes.func.isRequired, images: PropTypes.shape({}).isRequired, lmsEndpointUrl: PropTypes.string.isRequired, editorType: PropTypes.string, isLibrary: PropTypes.bool, }; export const ImageUploadModalInternal = ImageUploadModal; // For testing only export default ImageUploadModal;