UNPKG

@sanity/form-builder

Version:
165 lines (160 loc) • 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.fileTarget = fileTarget; var _react = _interopRequireWildcard(require("react")); var _ui = require("@sanity/ui"); var _extractFiles = require("./utils/extractFiles"); var _imageUrlToBlob = require("./utils/imageUrlToBlob"); var _excluded = ["onFiles", "onFilesOver", "onFilesOut", "disabled"]; function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } // These are managed and can't be passed to the composed component var PASTE_INPUT_STYLE = { opacity: 0, position: 'absolute' }; /** * Higher order component that creates a file target from a given component. * Returns a component that acts both as a drop target and a paste target, emitting a list of Files upon drop or paste */ function fileTarget(Component) { return /*#__PURE__*/_react.default.forwardRef(function FileTarget(props, ref) { var onFiles = props.onFiles, onFilesOver = props.onFilesOver, onFilesOut = props.onFilesOut, disabled = props.disabled, rest = _objectWithoutProperties(props, _excluded); var _React$useState = _react.default.useState(false), _React$useState2 = _slicedToArray(_React$useState, 2), showPasteInput = _React$useState2[0], setShowPasteInput = _React$useState2[1]; var pasteInput = _react.default.useRef(null); var forwardedRef = (0, _ui.useForwardedRef)(ref); var enteredElements = _react.default.useRef([]); var emitFiles = (0, _react.useCallback)(files => { onFiles === null || onFiles === void 0 ? void 0 : onFiles(files); }, [onFiles]); var handleKeyDown = (0, _react.useCallback)(event => { if (event.target === forwardedRef.current && (event.ctrlKey || event.metaKey) && event.key === 'v') { setShowPasteInput(true); } }, [forwardedRef]); var handlePaste = (0, _react.useCallback)(event => { (0, _extractFiles.extractPastedFiles)(event.clipboardData).then(files => { if (!pasteInput.current) { return []; } return files.length > 0 ? files : // Invoke Safari hack if we didn't get any files convertImagesToFilesAndClearContentEditable(pasteInput.current, 'image/jpeg'); }).then(files => { var _forwardedRef$current; emitFiles(files); setShowPasteInput(false); (_forwardedRef$current = forwardedRef.current) === null || _forwardedRef$current === void 0 ? void 0 : _forwardedRef$current.focus(); }); }, [emitFiles, forwardedRef]); var handleDrop = (0, _react.useCallback)(event => { enteredElements.current = []; event.preventDefault(); event.stopPropagation(); var dataTransfer = event.nativeEvent.dataTransfer; if (onFiles && dataTransfer) { (0, _extractFiles.extractDroppedFiles)(dataTransfer).then(files => { if (files) { emitFiles(files); } }); } onFilesOut === null || onFilesOut === void 0 ? void 0 : onFilesOut(); }, [emitFiles, onFiles, onFilesOut]); var handleDragOver = (0, _react.useCallback)(event => { if (onFiles) { event.preventDefault(); event.stopPropagation(); } }, [onFiles]); var handleDragEnter = (0, _react.useCallback)(event => { event.stopPropagation(); if (onFilesOver && forwardedRef.current === event.currentTarget) { /* this is a (hackish) work around to have the drag and drop work when the file is hovered back and forth over it as part of the refactor and adding more components to the "hover" state, it didn't recognise that it just kept adding the same element over and over, so when it tried to remove them on the handleDragLeave, it only removed the last instance. */ enteredElements.current = [...new Set(enteredElements.current), event.currentTarget]; var fileTypes = Array.from(event.dataTransfer.items).map(item => ({ type: item.type, kind: item.kind })); onFilesOver(fileTypes); } }, [onFilesOver, forwardedRef]); var handleDragLeave = (0, _react.useCallback)(event => { event.stopPropagation(); var idx = enteredElements.current.indexOf(event.currentTarget); if (idx > -1) { enteredElements.current.splice(idx, 1); } if (enteredElements.current.length === 0) { onFilesOut === null || onFilesOut === void 0 ? void 0 : onFilesOut(); } }, [onFilesOut]); var prevShowPasteInput = _react.default.useRef(false); _react.default.useEffect(() => { if (!prevShowPasteInput.current && showPasteInput && pasteInput.current) { pasteInput.current.focus(); select(pasteInput.current); // Needed by Edge } else if (prevShowPasteInput.current && !showPasteInput) { var _pasteInput$current; (_pasteInput$current = pasteInput.current) === null || _pasteInput$current === void 0 ? void 0 : _pasteInput$current.focus(); } prevShowPasteInput.current = showPasteInput; }, [showPasteInput]); return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(Component, _extends({}, rest, { ref: forwardedRef, onKeyDown: disabled ? undefined : handleKeyDown, onDragOver: disabled ? undefined : handleDragOver, onDragEnter: disabled ? undefined : handleDragEnter, onDragLeave: disabled ? undefined : handleDragLeave, onDrop: disabled ? undefined : handleDrop })), !disabled && showPasteInput && /*#__PURE__*/_react.default.createElement("div", { contentEditable: true, onPaste: handlePaste, ref: pasteInput, style: PASTE_INPUT_STYLE })); }); } // this is a hack for Safari that reads pasted image(s) from an ContentEditable div instead of the onpaste event function convertImagesToFilesAndClearContentEditable(element) { var targetFormat = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'image/jpeg'; if (!element.isContentEditable) { return Promise.reject(new Error("Expected element to be contentEditable=\"true\". Instead found a non contenteditable ".concat(element.tagName))); } return new Promise(resolve => setTimeout(resolve, 10)) // add a delay so the paste event can finish .then(() => Array.from(element.querySelectorAll('img'))).then(imageElements => { element.innerHTML = ''; // clear return imageElements; }).then(images => Promise.all(images.map(img => (0, _imageUrlToBlob.imageUrlToBlob)(img.src)))).then(imageBlobs => imageBlobs.map(blob => new File([blob], 'pasted-image.jpg', { type: targetFormat }))); } // needed by Edge function select(el) { var range = document.createRange(); range.selectNodeContents(el); var sel = window.getSelection(); sel === null || sel === void 0 ? void 0 : sel.removeAllRanges(); sel === null || sel === void 0 ? void 0 : sel.addRange(range); }