"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.dropTargetForExternal = dropTargetForExternal; exports.getAvailableItems = getAvailableItems; exports.getAvailableTypes = getAvailableTypes; exports.isAnAvailableType = isAnAvailableType; exports.monitorForExternal = monitorForExternal; var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _bindEventListener = require("bind-event-listener"); var _makeAdapter = require("../make-adapter/make-adapter"); var _android = require("../util/android"); var _isEnteringWindow = require("../util/changing-window/is-entering-window"); var _detectBrokenDrag = require("../util/detect-broken-drag"); var _textMediaType = require("../util/media-types/text-media-type"); var _elementAdapterNativeDataKey = require("./element-adapter-native-data-key"); function isAnAvailableType(_ref) { var type = _ref.type, value = _ref.value; // We don't want to expose our private elementAdapter key / value if (type === _elementAdapterNativeDataKey.elementAdapterNativeDataKey) { return false; } // Not exposing "text/plain" if it contains the android fallback text // We _could_ add an `isAndroid()` check, but it's probably safest // to trim this data out, regardless of what OS we see it on. if (type === _textMediaType.textMediaType && value === _android.androidFallbackText) { return false; } return true; } function getAvailableTypes(transfer) { return Array.from(transfer.types).filter(function (type) { return isAnAvailableType({ type: type, value: transfer.getData(type) }); }); } function getAvailableItems(dataTransfer) { // item.kind is 'string' | 'file' // For 'string' item.type is the mimeType (eg 'text/plain') // For 'file' item.type is the file type (eg 'image/jpg') return Array.from(dataTransfer.items).filter(function (item) { return item.kind === 'file' || isAnAvailableType({ type: item.type, value: dataTransfer.getData(item.type) }); }); } var didDragStartLocally = false; var adapter = (0, _makeAdapter.makeAdapter)({ typeKey: 'external', // for external drags, we are generally making a copy of something that is being dragged defaultDropEffect: 'copy', mount: function mount(api) { // Binding to the `window` so that the element adapter // has a chance to get in first on the`document`. // We are giving preference to the element adapter. return (0, _bindEventListener.bind)(window, { type: 'dragenter', listener: function listener(event) { // drag operation was started within the document, it won't be an "external" drag if (didDragStartLocally) { return; } // Note: not checking if event was cancelled (`event.defaultPrevented`) as // cancelling a "dragenter" accepts the drag operation (not prevent it) // Something has gone wrong with our drag event if (!event.dataTransfer) { // Including this code on "test" and "development" environments: // - Browser tests commonly run against "development" builds // - Unit tests commonly run in "test" if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line no-console console.warn("\n It appears as though you have are not testing DragEvents correctly.\n\n - If you are unit testing, ensure you have pollyfilled DragEvent.\n - If you are browser testing, ensure you are dispatching drag events correctly.\n\n Please see our testing guides for more information:\n https://atlassian.design/components/pragmatic-drag-and-drop/core-package/testing\n ".replace(/ {2}/g, '')); } return; } if (!api.canStart(event)) { return; } if (!(0, _isEnteringWindow.isEnteringWindow)({ dragEnter: event })) { return; } // Note: not checking types for `elementAdapterNativeDataKey` as we expect to see that // key when pdnd started the drag in another document var types = getAvailableTypes(event.dataTransfer); if (!types.length) { return; } var locked = { types: types, items: [], getStringData: function getStringData() { return null; } }; api.start({ event: event, dragType: { type: 'external', startedFrom: 'external', payload: locked, getDropPayload: function getDropPayload(event) { // this would be a platform error // trying to handle it gracefully rather than throwing (for now) if (!event.dataTransfer) { return locked; } var items = getAvailableItems(event.dataTransfer); // need to use `.bind` as `getData` is required // to be run with `event.dataTransfer` as the "this" context var nativeGetData = event.dataTransfer.getData.bind(event.dataTransfer); return { types: types, items: items, // return `null` if there is no result, otherwise string getStringData: function getStringData(mediaType) { // not dragging the requested type // return `null` (no result) if (!types.includes(mediaType)) { return null; } // nativeGetData will return `""` when there is no value, // but at this point we know we will only get explicitly set // values back as we have checked the `types`. // `""` can be an explicitly set value. var value = nativeGetData(mediaType); // not exposing data for unavailable types if (!isAnAvailableType({ type: mediaType, value: value })) { return null; } return value; } }; } } }); } }); } }); /** * Some events don't make sense for the external adapter * * `onGenerateDragPreview` * The browser creates the drag preview for external drags, so we don't * need an event to generate the preview for _monitors_ or the _dropTarget_ * * `onDragStart` * An external drag can never start from in the `window`, so _dropTarget_'s * don't need `onDragStart` */ function dropTargetForExternal(args) { // not removing unused events, just leaning on the type system return adapter.dropTarget(args); } function monitorForExternal(args) { // not removing unused events, just leaning on the type system return adapter.monitor(args); } (function startup() { // server side rendering check if (typeof window === 'undefined') { return; } // A shared single usage registration as we want to capture // all external drag operations, even if there are no drop targets // on the page yet adapter.registerUsage(); // independent of pdnd, we need to keep track of // all drag operations so that we can know if a drag operation // has started locally var idle = { type: 'idle' }; var state = idle; function clear() { if (state.type !== 'dragging') { return; } didDragStartLocally = false; state.cleanup(); state = idle; } function bindEndEvents() { return (0, _bindEventListener.bindAll)(window, [{ type: 'dragend', listener: clear }].concat((0, _toConsumableArray2.default)((0, _detectBrokenDrag.getBindingsForBrokenDrags)({ onDragEnd: clear }))), // we want to make sure we get all the events, // and this helps avoid not seeing events when folks stop // them later on the event path { capture: true }); } // we always keep this event listener active (0, _bindEventListener.bind)(window, { type: 'dragstart', listener: function listener() { // something bad has happened if this is true! if (state.type !== 'idle') { return; } // set our global flag didDragStartLocally = true; state = { type: 'dragging', cleanup: bindEndEvents() }; }, // binding in the capture phase so these listeners are called // before our listeners in the adapters `mount` function options: { capture: true } }); })(); /** Common event payload for all events */ /** A map containing payloads for all events */ /** Common event payload for all drop target events */ /** A map containing payloads for all events on drop targets */ /** Arguments given to all feedback functions (eg `canDrop()`) on a `dropTargetForExternal` */ /** Arguments given to all monitor feedback functions (eg `canMonitor()`) for a `monitorForExternal` */