"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _uniqueId2 = _interopRequireDefault(require("lodash/uniqueId"));

var _partition4 = _interopRequireDefault(require("lodash/partition"));

var _get2 = _interopRequireDefault(require("lodash/get"));

var _react = _interopRequireDefault(require("react"));

var _propTypes = _interopRequireDefault(require("prop-types"));

var _components = require("@sanity/base/components");

var _changeIndicators = require("@sanity/base/change-indicators");

var _icons = require("@sanity/icons");

var _ui = require("@sanity/ui");

var _presence = require("@sanity/base/presence");

var _WithMaterializedReference = _interopRequireDefault(require("../../../utils/WithMaterializedReference"));

var _PatchEvent = _interopRequireWildcard(require("../../../PatchEvent"));

var _UploadPlaceholder = _interopRequireDefault(require("../common/UploadPlaceholder"));

var _FileInputButton = require("../common/FileInputButton/FileInputButton");

var _styles = require("../common/styles");

var _UploadProgress = require("../common/UploadProgress");

var _DropMessage = require("../common/DropMessage");

var _assetSource = require("../common/assetSource");

var _styles2 = require("./styles");

var _FileInputField = require("./FileInputField");

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 _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

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 = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var HIDDEN_FIELDS = ['asset'];

class FileInput extends _react.default.PureComponent {
  constructor() {
    var _this;

    super(...arguments);
    _this = this;

    _defineProperty(this, "_inputId", (0, _uniqueId2.default)('FileInput'));

    _defineProperty(this, "dialogId", (0, _uniqueId2.default)('fileinput-dialog'));

    _defineProperty(this, "_focusRef", null);

    _defineProperty(this, "uploadSubscription", null);

    _defineProperty(this, "state", {
      isUploading: false,
      isAdvancedEditOpen: false,
      selectedAssetSource: null,
      hoveringFiles: []
    });

    _defineProperty(this, "toast", null);

    _defineProperty(this, "handleRemoveButtonClick", () => {
      var getValuePath = this.context.getValuePath;
      var value = this.props.value;
      var parentPathSegment = getValuePath().slice(-1)[0]; // String path segment mean an object path, while a number or a
      // keyed segment means we're a direct child of an array

      var isArrayElement = typeof parentPathSegment !== 'string'; // When removing the file, _type and _key are "meta"-properties and
      // are not significant unless other properties are present. Thus, we
      // want to remove the entire "container" object if these are the only
      // properties present, BUT only if we're not an array element, as
      // removing the array element will close the selection dialog. Instead,
      // when closing the dialog, the array logic will check for an "empty"
      // value and remove it for us

      var allKeys = Object.keys(value || {});
      var remainingKeys = allKeys.filter(key => !['_type', '_key', '_upload', 'asset'].includes(key));
      var isEmpty = remainingKeys.length === 0;
      var removeKeys = ['asset'].concat(allKeys.filter(key => ['_upload'].includes(key))).map(key => (0, _PatchEvent.unset)([key]));
      this.props.onChange(_PatchEvent.default.from(isEmpty && !isArrayElement ? (0, _PatchEvent.unset)() : removeKeys));
    });

    _defineProperty(this, "handleCancelUpload", () => {
      this.cancelUpload();
    });

    _defineProperty(this, "handleClearUploadState", () => {
      this.clearUploadStatus();
    });

    _defineProperty(this, "handleSelectFiles", files => {
      this.uploadFirstAccepted(files);
    });

    _defineProperty(this, "handleSelectFileFromAssetSource", source => {
      this.setState({
        selectedAssetSource: source
      });
    });

    _defineProperty(this, "handleAssetSourceClosed", () => {
      this.setState({
        selectedAssetSource: null
      });
    });

    _defineProperty(this, "uploadWith", function (uploader, file) {
      var assetDocumentProps = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      var _this$props = _this.props,
          type = _this$props.type,
          onChange = _this$props.onChange;
      var source = assetDocumentProps.source;
      var options = {
        metadata: (0, _get2.default)(type, 'options.metadata'),
        storeOriginalFilename: (0, _get2.default)(type, 'options.storeOriginalFilename'),
        source
      };

      _this.cancelUpload();

      _this.setState({
        isUploading: true
      });

      onChange(_PatchEvent.default.from([(0, _PatchEvent.setIfMissing)({
        _type: type.name
      })]));
      _this.uploadSubscription = uploader.upload(file, type, options).subscribe({
        next: uploadEvent => {
          if (uploadEvent.patches) {
            onChange(_PatchEvent.default.from(uploadEvent.patches));
          }
        },
        error: err => {
          var _this$toast;

          // eslint-disable-next-line no-console
          console.error(err);
          (_this$toast = _this.toast) === null || _this$toast === void 0 ? void 0 : _this$toast.push({
            status: 'error',
            description: 'The upload could not be completed at this time.',
            title: 'Upload failed'
          });

          _this.clearUploadStatus();
        },
        complete: () => {
          _this.setState({
            isUploading: false
          });
        }
      });
    });

    _defineProperty(this, "renderMaterializedAsset", assetDocument => {
      var showTooltip = ((assetDocument === null || assetDocument === void 0 ? void 0 : assetDocument.originalFilename) || '').length > 12;
      return /*#__PURE__*/_react.default.createElement(_ui.Stack, {
        space: 3
      }, /*#__PURE__*/_react.default.createElement(_ui.Flex, {
        align: "center",
        justify: "center"
      }, /*#__PURE__*/_react.default.createElement(_ui.Box, null, /*#__PURE__*/_react.default.createElement(_ui.Text, {
        size: 4
      }, /*#__PURE__*/_react.default.createElement(_icons.BinaryDocumentIcon, null))), /*#__PURE__*/_react.default.createElement(_ui.Box, {
        marginLeft: 3
      }, showTooltip && /*#__PURE__*/_react.default.createElement(_ui.Tooltip, {
        content: /*#__PURE__*/_react.default.createElement(_ui.Box, {
          padding: 2
        }, /*#__PURE__*/_react.default.createElement(_ui.Text, {
          muted: true,
          size: 1
        }, assetDocument.originalFilename)),
        fallbackPlacements: ['right', 'left'],
        placement: "top",
        portal: true
      }, /*#__PURE__*/_react.default.createElement(_ui.Text, {
        textOverflow: "ellipsis",
        weight: "medium"
      }, assetDocument.originalFilename)), !showTooltip && /*#__PURE__*/_react.default.createElement(_ui.Text, {
        textOverflow: "ellipsis",
        weight: "medium"
      }, assetDocument.originalFilename))), /*#__PURE__*/_react.default.createElement(_ui.Button, {
        as: "a",
        fontSize: 1,
        href: "".concat(assetDocument.url, "?dl"),
        icon: _icons.DownloadIcon,
        mode: "ghost",
        text: "Download file"
      }));
    });

    _defineProperty(this, "handleFieldChange", event => {
      var _this$props2 = this.props,
          onChange = _this$props2.onChange,
          type = _this$props2.type;
      onChange(event.prepend((0, _PatchEvent.setIfMissing)({
        _type: type.name
      })));
    });

    _defineProperty(this, "handleStartAdvancedEdit", () => {
      this.setState({
        isAdvancedEditOpen: true
      });
    });

    _defineProperty(this, "handleStopAdvancedEdit", () => {
      this.setState({
        isAdvancedEditOpen: false
      });
    });

    _defineProperty(this, "handleSelectAssetFromSource", assetFromSource => {
      var _this$props3 = this.props,
          onChange = _this$props3.onChange,
          type = _this$props3.type,
          resolveUploader = _this$props3.resolveUploader;
      (0, _assetSource.handleSelectAssetFromSource)({
        assetFromSource,
        onChange,
        type,
        resolveUploader,
        uploadWith: this.uploadWith
      });
      this.setState({
        selectedAssetSource: null
      });
    });

    _defineProperty(this, "handleFileTargetFocus", () => {
      this.props.onFocus(['asset']);
    });

    _defineProperty(this, "handleFileTargetBlur", () => {
      this.props.onBlur();
    });

    _defineProperty(this, "handleFilesOver", fileInfo => {
      this.setState({
        hoveringFiles: fileInfo
      });
    });

    _defineProperty(this, "handleFilesOut", () => {
      this.setState({
        hoveringFiles: []
      });
    });

    _defineProperty(this, "setFocusInput", ref => {
      this._focusRef = ref;
    });

    _defineProperty(this, "handleUpload", _ref => {
      var file = _ref.file,
          uploader = _ref.uploader;
      this.uploadWith(uploader, file);
    });

    _defineProperty(this, "setToast", toast => {
      this.toast = toast;
    });
  }

  clearUploadStatus() {
    var _this$props$value;

    // todo: this is kind of hackish
    if ((_this$props$value = this.props.value) !== null && _this$props$value !== void 0 && _this$props$value._upload) {
      this.props.onChange(_PatchEvent.default.from([(0, _PatchEvent.unset)(['_upload'])]));
    }
  }

  cancelUpload() {
    if (this.uploadSubscription) {
      this.uploadSubscription.unsubscribe();
      this.clearUploadStatus();
    }
  }

  uploadFirstAccepted(files) {
    var _this$props4 = this.props,
        resolveUploader = _this$props4.resolveUploader,
        type = _this$props4.type;
    var match = files.map(file => ({
      file,
      uploader: resolveUploader(type, file)
    })).find(result => result.uploader);

    if (match) {
      this.uploadWith(match.uploader, match.file);
    }
  }

  renderUploadState(uploadState) {
    var isUploading = this.state.isUploading;
    return /*#__PURE__*/_react.default.createElement(_UploadProgress.UploadProgress, {
      uploadState: uploadState,
      onCancel: isUploading ? this.handleCancelUpload : undefined,
      onClearStale: this.handleClearUploadState
    });
  }

  renderSelectFileButton() {
    var assetSources = this.props.assetSources;

    if (!(assetSources !== null && assetSources !== void 0 && assetSources.length)) {
      return null;
    } // If multiple asset sources render a dropdown


    if (assetSources.length > 1) {
      return /*#__PURE__*/_react.default.createElement(_ui.MenuButton, {
        id: "".concat(this._inputId, "_assetFileButton"),
        button: /*#__PURE__*/_react.default.createElement(_ui.Button, {
          mode: "ghost",
          text: "Select\u2026",
          icon: _icons.SearchIcon
        }),
        "data-testid": "file-input-select-button",
        menu: /*#__PURE__*/_react.default.createElement(_ui.Menu, null, assetSources.map(assetSource => {
          return /*#__PURE__*/_react.default.createElement(_ui.MenuItem, {
            key: assetSource.name,
            text: assetSource.title,
            onClick: () => this.handleSelectFileFromAssetSource(assetSource),
            icon: assetSource.icon || _icons.ImageIcon
          });
        }))
      });
    } // Single asset source (just a normal button)


    return /*#__PURE__*/_react.default.createElement(_ui.Button, {
      icon: _icons.SearchIcon,
      onClick: () => this.handleSelectFileFromAssetSource(assetSources[0]),
      mode: "ghost",
      text: "Select",
      "data-testid": "file-input-select-button"
    });
  }

  renderAssetSource() {
    var selectedAssetSource = this.state.selectedAssetSource;
    var _this$props5 = this.props,
        value = _this$props5.value,
        materialize = _this$props5.materialize;

    if (!selectedAssetSource) {
      return null;
    }

    var Component = selectedAssetSource.component;

    if (value && value.asset) {
      return /*#__PURE__*/_react.default.createElement(_WithMaterializedReference.default, {
        materialize: materialize,
        reference: value.asset
      }, fileAsset => {
        return /*#__PURE__*/_react.default.createElement(Component, {
          selectedAssets: [fileAsset],
          selectionType: "single",
          assetType: "file",
          dialogHeaderTitle: "Select file",
          onClose: this.handleAssetSourceClosed,
          onSelect: this.handleSelectAssetFromSource
        });
      });
    }

    return /*#__PURE__*/_react.default.createElement(Component, {
      selectedAssets: [],
      selectionType: "single",
      assetType: "file",
      dialogHeaderTitle: "Select file",
      onClose: this.handleAssetSourceClosed,
      onSelect: this.handleSelectAssetFromSource
    });
  }

  renderAdvancedEdit(fields) {
    return /*#__PURE__*/_react.default.createElement(_ui.Dialog, {
      header: "Edit details",
      id: this.dialogId,
      onClose: this.handleStopAdvancedEdit,
      width: 1
    }, /*#__PURE__*/_react.default.createElement(_presence.PresenceOverlay, {
      margins: [0, 0, 1, 0]
    }, /*#__PURE__*/_react.default.createElement(_ui.Box, {
      padding: 4
    }, this.renderFields(fields))));
  }

  renderFields(fields) {
    return fields.map(field => this.renderField(field));
  }

  hasFileTargetFocus() {
    var _this$props$focusPath;

    return ((_this$props$focusPath = this.props.focusPath) === null || _this$props$focusPath === void 0 ? void 0 : _this$props$focusPath[0]) === 'asset';
  }

  renderField(field) {
    var _this$props6 = this.props,
        value = _this$props6.value,
        level = _this$props6.level,
        focusPath = _this$props6.focusPath,
        onFocus = _this$props6.onFocus,
        readOnly = _this$props6.readOnly,
        onBlur = _this$props6.onBlur,
        compareValue = _this$props6.compareValue,
        presence = _this$props6.presence,
        markers = _this$props6.markers;
    var fieldValue = value === null || value === void 0 ? void 0 : value[field.name];
    var fieldMarkers = markers.filter(marker => marker.path[0] === field.name);
    return /*#__PURE__*/_react.default.createElement(_FileInputField.FileInputField, {
      key: field.name,
      field: field,
      parentValue: value,
      value: fieldValue,
      onChange: this.handleFieldChange,
      onFocus: onFocus,
      compareValue: compareValue,
      onBlur: onBlur,
      readOnly: readOnly || field.type.readOnly,
      focusPath: focusPath,
      level: level,
      presence: presence,
      markers: fieldMarkers
    });
  }

  renderAsset() {
    var _this$props7 = this.props,
        value = _this$props7.value,
        materialize = _this$props7.materialize;
    return /*#__PURE__*/_react.default.createElement(_WithMaterializedReference.default, {
      reference: value.asset,
      materialize: materialize
    }, this.renderMaterializedAsset);
  }

  renderUploadPlaceholder() {
    var readOnly = this.props.readOnly;
    return readOnly ? /*#__PURE__*/_react.default.createElement(_ui.Text, {
      align: "center",
      muted: true
    }, "This field is read-only") : /*#__PURE__*/_react.default.createElement(_UploadPlaceholder.default, null);
  }

  focus() {
    if (this._focusRef) {
      this._focusRef.focus();
    }
  }

  render() {
    var _value$asset, _compareValue$asset, _value$asset2;

    var _this$props8 = this.props,
        directUploads = _this$props8.directUploads,
        type = _this$props8.type,
        value = _this$props8.value,
        compareValue = _this$props8.compareValue,
        level = _this$props8.level,
        markers = _this$props8.markers,
        resolveUploader = _this$props8.resolveUploader,
        readOnly = _this$props8.readOnly,
        presence = _this$props8.presence;
    var _this$state = this.state,
        isAdvancedEditOpen = _this$state.isAdvancedEditOpen,
        hoveringFiles = _this$state.hoveringFiles,
        selectedAssetSource = _this$state.selectedAssetSource;

    var _partition2 = (0, _partition4.default)(type.fields.filter(field => !HIDDEN_FIELDS.includes(field.name)), 'type.options.isHighlighted'),
        _partition3 = _slicedToArray(_partition2, 2),
        highlightedFields = _partition3[0],
        otherFields = _partition3[1];

    var accept = (0, _get2.default)(type, 'options.accept', ''); // Whoever is present at the asset field is who we show on the field itself

    var assetFieldPresence = presence.filter(item => item.path[0] === 'asset');
    return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_components.ImperativeToast, {
      ref: this.setToast
    }), /*#__PURE__*/_react.default.createElement(_components.FormFieldSet, {
      __unstable_markers: markers,
      title: type.title,
      description: type.description,
      level: highlightedFields.length > 0 ? level : 0,
      __unstable_presence: assetFieldPresence,
      __unstable_changeIndicator: false
    }, /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_changeIndicators.ChangeIndicatorCompareValueProvider, {
      value: value === null || value === void 0 ? void 0 : (_value$asset = value.asset) === null || _value$asset === void 0 ? void 0 : _value$asset._ref,
      compareValue: compareValue === null || compareValue === void 0 ? void 0 : (_compareValue$asset = compareValue.asset) === null || _compareValue$asset === void 0 ? void 0 : _compareValue$asset._ref
    }, /*#__PURE__*/_react.default.createElement(_changeIndicators.ChangeIndicatorWithProvidedFullPath, {
      path: [],
      hasFocus: this.hasFileTargetFocus(),
      value: value === null || value === void 0 ? void 0 : (_value$asset2 = value.asset) === null || _value$asset2 === void 0 ? void 0 : _value$asset2._ref
    }, /*#__PURE__*/_react.default.createElement(_styles.FileTarget, {
      tabIndex: readOnly ? undefined : 0,
      disabled: readOnly === true,
      ref: this.setFocusInput,
      onFiles: this.handleSelectFiles,
      onFilesOver: this.handleFilesOver,
      onFilesOut: this.handleFilesOut,
      onFocus: this.handleFileTargetFocus,
      onBlur: this.handleFileTargetBlur,
      tone: "transparent"
    }, /*#__PURE__*/_react.default.createElement(_styles2.AssetBackground, null, /*#__PURE__*/_react.default.createElement(_ui.Container, {
      padding: 3,
      sizing: "border",
      width: 0
    }, (value === null || value === void 0 ? void 0 : value._upload) && this.renderUploadState(value._upload), !(value !== null && value !== void 0 && value._upload) && (value === null || value === void 0 ? void 0 : value.asset) && this.renderAsset(), !(value !== null && value !== void 0 && value._upload) && !(value !== null && value !== void 0 && value.asset) && this.renderUploadPlaceholder(), !(value !== null && value !== void 0 && value._upload) && !readOnly && hoveringFiles.length > 0 && /*#__PURE__*/_react.default.createElement(_styles.Overlay, null, /*#__PURE__*/_react.default.createElement(_DropMessage.DropMessage, {
      hoveringFiles: hoveringFiles,
      resolveUploader: resolveUploader,
      types: [type]
    }))))))), /*#__PURE__*/_react.default.createElement(_ui.Grid, {
      gap: 1,
      marginTop: 3,
      style: {
        gridTemplateColumns: 'repeat(auto-fit, minmax(100px, 1fr))'
      }
    }, !readOnly && directUploads && /*#__PURE__*/_react.default.createElement(_FileInputButton.FileInputButton, {
      onSelect: this.handleSelectFiles,
      mode: "ghost",
      icon: _icons.UploadIcon,
      accept: accept,
      text: "Upload file",
      "data-testid": "file-input-upload-button"
    }), !readOnly && this.renderSelectFileButton(), value && otherFields.length > 0 && /*#__PURE__*/_react.default.createElement(_ui.Button, {
      icon: readOnly ? _icons.EyeOpenIcon : _icons.EditIcon,
      mode: "ghost",
      onClick: this.handleStartAdvancedEdit,
      text: readOnly ? 'View details' : 'Edit details'
    }), !readOnly && (value === null || value === void 0 ? void 0 : value.asset) && /*#__PURE__*/_react.default.createElement(_ui.Button, {
      icon: _icons.TrashIcon,
      mode: "ghost",
      onClick: this.handleRemoveButtonClick,
      text: "Remove file",
      tone: "critical",
      "data-testid": "file-input-remove-button"
    }))), highlightedFields.length > 0 && this.renderFields(highlightedFields), isAdvancedEditOpen && this.renderAdvancedEdit(otherFields), selectedAssetSource && this.renderAssetSource()));
  }

}

exports.default = FileInput;

_defineProperty(FileInput, "contextTypes", {
  getValuePath: _propTypes.default.func
});