UNPKG

@lyra/form-builder

Version:
463 lines (388 loc) 14.5 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _partition4 = require('lodash/partition'); var _partition5 = _interopRequireDefault(_partition4); var _get2 = require('lodash/get'); var _get3 = _interopRequireDefault(_get2); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_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"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _default = require('part:@lyra/components/buttons/default'); var _default2 = _interopRequireDefault(_default); var _button = require('part:@lyra/components/fileinput/button'); var _button2 = _interopRequireDefault(_button); var _bar = require('part:@lyra/components/progress/bar'); var _bar2 = _interopRequireDefault(_bar); var _editIcon = require('part:@lyra/base/edit-icon'); var _editIcon2 = _interopRequireDefault(_editIcon); var _visibilityIcon = require('part:@lyra/base/visibility-icon'); var _visibilityIcon2 = _interopRequireDefault(_visibilityIcon); var _fileIcon = require('part:@lyra/base/file-icon'); var _fileIcon2 = _interopRequireDefault(_fileIcon); var _uploadIcon = require('part:@lyra/base/upload-icon'); var _uploadIcon2 = _interopRequireDefault(_uploadIcon); var _PatchEvent = require('../../PatchEvent'); var _PatchEvent2 = _interopRequireDefault(_PatchEvent); var _FileInput = require('./styles/FileInput.css'); var _FileInput2 = _interopRequireDefault(_FileInput); var _fullscreen = require('part:@lyra/components/dialogs/fullscreen'); var _fullscreen2 = _interopRequireDefault(_fullscreen); var _observable = require('../../typedefs/observable'); var _WithMaterializedReference = require('../../utils/WithMaterializedReference'); var _WithMaterializedReference2 = _interopRequireDefault(_WithMaterializedReference); var _FormBuilderInput = require('../../FormBuilderInput'); var _UploadPlaceholder = require('../common/UploadPlaceholder'); var _UploadPlaceholder2 = _interopRequireDefault(_UploadPlaceholder); var _UploadTargetFieldset = require('../../utils/UploadTargetFieldset'); var _UploadTargetFieldset2 = _interopRequireDefault(_UploadTargetFieldset); var _default3 = require('part:@lyra/components/snackbar/default'); var _default4 = _interopRequireDefault(_default3); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /* eslint-disable complexity */ /*:: import type {Node} from 'react'*/ /*:: import type {Reference, Type} from '../../typedefs'*/ /*:: import type {Uploader, UploaderResolver} from '../../lyra/uploads/typedefs'*/ /*:: type FieldT = { name: string, type: Type }*/ /*:: type Value = { _upload?: any, asset?: Reference, hotspot?: Object, crop?: Object }*/ /*:: type Props = { value?: Value, type: Type, level: number, onChange: PatchEvent => void, resolveUploader: UploaderResolver, materialize: string => ObservableI<Object>, onBlur: () => void, onFocus: () => void, readOnly: ?boolean, focusPath: Array<*> }*/ /*:: type State = { isAdvancedEditOpen: boolean, isUploading: boolean }*/ const HIDDEN_FIELDS = ['asset', 'hotspot', 'crop']; class FileInput extends _react2.default.PureComponent /*:: <Props, State>*/ { constructor(...args) { var _temp; return _temp = super(...args), this.state = { isUploading: false, isAdvancedEditOpen: false, uploadError: null }, this.handleRemoveButtonClick = (event /*: SyntheticEvent<*>*/) => { this.props.onChange(_PatchEvent2.default.from((0, _PatchEvent.unset)(['asset']))); }, this.handleCancelUpload = () => { this.cancelUpload(); }, this.handleSelectFile = (files /*: FileList*/) => { this.uploadFirstAccepted(files); }, this.renderMaterializedAsset = (assetDocument /*: Object*/) /*: Node*/ => { return _react2.default.createElement( 'div', { className: _FileInput2.default.previewAsset }, _react2.default.createElement( 'div', { className: _FileInput2.default.fileIcon }, _react2.default.createElement(_fileIcon2.default, null) ), _react2.default.createElement( 'div', null, assetDocument.originalFilename, ' ', _react2.default.createElement( 'a', { href: `${assetDocument.url}?dl`, download: true }, 'Download' ) ) ); }, this.handleFieldChange = (event /*: PatchEvent*/, field /*: FieldT*/) => { var _props = this.props; const onChange = _props.onChange, type = _props.type; onChange(event.prefixAll(field.name).prepend((0, _PatchEvent.setIfMissing)({ _type: type.name }))); }, this.handleStartAdvancedEdit = () => { this.setState({ isAdvancedEditOpen: true }); }, this.handleStopAdvancedEdit = () => { this.setState({ isAdvancedEditOpen: false }); }, this.handleFocus = event => { this.setState({ hasFocus: true }); this.props.onFocus(event); }, this.handleBlur = event => { this.setState({ hasFocus: false }); this.props.onBlur(event); }, this.setFocusArea = (el /*: ?FocusArea*/) => { this._focusArea = el; }, this.getUploadOptions = (file /*: File*/) /*: Array<UploadOption>*/ => { var _props2 = this.props; const type = _props2.type, resolveUploader = _props2.resolveUploader; const uploader = resolveUploader && resolveUploader(type, file); return uploader ? [{ type: type, uploader }] : []; }, this.handleUpload = ({ file, uploader }) => { this.uploadWith(uploader, file); }, _temp; } clearUploadStatus() { this.props.onChange(_PatchEvent2.default.from([(0, _PatchEvent.unset)(['_upload'])])); // todo: this is kind of hackish } cancelUpload() { if (this.uploadSubscription) { this.uploadSubscription.unsubscribe(); this.clearUploadStatus(); } } uploadFirstAccepted(fileList /*: FileList*/) { var _props3 = this.props; const resolveUploader = _props3.resolveUploader, type = _props3.type; let match /*: ?{uploader: Uploader, file: File}*/; Array.from(fileList).some(file => { const uploader = resolveUploader(type, file); if (uploader) { match = { file, uploader }; return true; } return false; }); if (match) { this.uploadWith(match.uploader, match.file); } } uploadWith(uploader /*: Uploader*/, file /*: File*/) { var _props4 = this.props; const type = _props4.type, onChange = _props4.onChange; const options = { metadata: (0, _get3.default)(type, 'options.metadata') }; this.cancelUpload(); this.setState({ isUploading: true }); onChange(_PatchEvent2.default.from([(0, _PatchEvent.setIfMissing)({ _type: type.name })])); this.uploadSubscription = uploader.upload(file, type, options).subscribe({ next: uploadEvent => { if (uploadEvent.patches) { onChange(_PatchEvent2.default.from(uploadEvent.patches)); } }, error: err => { this.setState({ uploadError: err }); this.clearUploadStatus(); }, complete: () => { onChange(_PatchEvent2.default.from([(0, _PatchEvent.unset)(['hotspot']), (0, _PatchEvent.unset)(['crop'])])); this.setState({ isUploading: false }); } }); } renderUploadState(uploadState /*: any*/) { const isUploading = this.state.isUploading; const isComplete = uploadState.progress === 100; const filename = (0, _get3.default)(uploadState, 'file.name'); return _react2.default.createElement( 'div', { className: _FileInput2.default.uploadState }, _react2.default.createElement( 'div', null, _react2.default.createElement( 'div', { className: isComplete ? _FileInput2.default.progressBarCompleted : _FileInput2.default.progressBar }, _react2.default.createElement(_bar2.default, { percent: status === 'complete' ? 100 : uploadState.progress, text: isComplete ? 'Complete' : `Uploading${filename ? ` "${filename}"` : '...'}`, completed: isComplete, showPercent: true, animation: true }) ), _react2.default.createElement( 'div', { className: _FileInput2.default.cancelButton }, isUploading && _react2.default.createElement( _default2.default, { kind: 'simple', color: 'danger', onClick: this.handleCancelUpload }, 'Cancel' ) ) ) ); } renderAdvancedEdit(fields /*: Array<FieldT>*/) { return _react2.default.createElement( _fullscreen2.default, { title: 'Edit details', onClose: this.handleStopAdvancedEdit, isOpen: true }, _react2.default.createElement( 'div', null, this.renderFields(fields) ), _react2.default.createElement( _default2.default, { onClick: this.handleStopAdvancedEdit }, 'Close' ) ); } renderFields(fields /*: Array<FieldT>*/) { return fields.map(field => this.renderField(field)); } renderField(field /*: FieldT*/) { var _props5 = this.props; const value = _props5.value, level = _props5.level, focusPath = _props5.focusPath, onFocus = _props5.onFocus, readOnly = _props5.readOnly, onBlur = _props5.onBlur; const fieldValue = value && value[field.name]; return _react2.default.createElement(_FormBuilderInput.FormBuilderInput, { key: field.name, value: fieldValue, type: field.type, onChange: ev => this.handleFieldChange(ev, field), path: [field.name], onFocus: onFocus, onBlur: onBlur, readOnly: readOnly || field.type.readOnly, focusPath: focusPath, level: level }); } renderAsset() { var _props6 = this.props; const value = _props6.value, materialize = _props6.materialize, readOnly = _props6.readOnly; if (value && value.asset) { return _react2.default.createElement( _WithMaterializedReference2.default, { reference: value.asset, materialize: materialize }, this.renderMaterializedAsset ); } return readOnly ? _react2.default.createElement( 'span', null, 'Field is read only' ) : _react2.default.createElement(_UploadPlaceholder2.default, { hasFocus: this.state.hasFocus }); } focus() { if (this._focusArea) { this._focusArea.focus(); } } render() { var _props7 = this.props; const type = _props7.type, value = _props7.value, level = _props7.level, readOnly = _props7.readOnly; var _state = this.state; const isAdvancedEditOpen = _state.isAdvancedEditOpen, uploadError = _state.uploadError; var _partition2 = (0, _partition5.default)(type.fields.filter(field => !HIDDEN_FIELDS.includes(field.name)), 'type.options.isHighlighted'), _partition3 = _slicedToArray(_partition2, 2); const highlightedFields = _partition3[0], otherFields = _partition3[1]; const hasAsset = value && value.asset; return _react2.default.createElement( _UploadTargetFieldset2.default, { legend: type.title, description: type.description, level: level, onFocus: this.handleFocus, onBlur: this.handleBlur, onUpload: this.handleUpload, getUploadOptions: this.getUploadOptions, ref: this.setFocusArea }, uploadError && _react2.default.createElement( _default4.default, { kind: 'error', action: { title: 'OK' }, onAction: () => this.setState({ uploadError: null }) }, "We're", ' really sorry, but the upload could not be completed.' ), _react2.default.createElement( 'div', { className: _FileInput2.default.content }, _react2.default.createElement( 'div', { className: _FileInput2.default.assetWrapper }, value && value._upload && _react2.default.createElement( 'div', { className: _FileInput2.default.uploadState }, this.renderUploadState(value._upload) ), this.renderAsset() ), highlightedFields.length > 0 && _react2.default.createElement( 'div', { className: _FileInput2.default.fieldsWrapper }, this.renderFields(highlightedFields) ) ), _react2.default.createElement( 'div', { className: _FileInput2.default.functions }, !readOnly && _react2.default.createElement( _button2.default, { icon: _uploadIcon2.default, onSelect: this.handleSelectFile, accept: '' /* todo build from this.props.resolveUploaders */ }, 'Upload' ), value && otherFields.length > 0 && _react2.default.createElement( _default2.default, { icon: readOnly ? _visibilityIcon2.default : _editIcon2.default, kind: 'simple', title: readOnly ? 'View details' : 'Edit details', onClick: this.handleStartAdvancedEdit }, readOnly ? 'View details' : 'Edit' ), !readOnly && hasAsset && _react2.default.createElement( _default2.default, { color: 'danger', kind: 'simple', onClick: this.handleRemoveButtonClick }, 'Remove' ) ), isAdvancedEditOpen && this.renderAdvancedEdit(otherFields) ); } } exports.default = FileInput;