UNPKG

@lyra/form-builder

Version:
518 lines (437 loc) 16.8 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 _circle = require('part:@lyra/components/progress/circle'); var _circle2 = _interopRequireDefault(_circle); var _editIcon = require('part:@lyra/base/edit-icon'); var _editIcon2 = _interopRequireDefault(_editIcon); var _uploadIcon = require('part:@lyra/base/upload-icon'); var _uploadIcon2 = _interopRequireDefault(_uploadIcon); var _visibilityIcon = require('part:@lyra/base/visibility-icon'); var _visibilityIcon2 = _interopRequireDefault(_visibilityIcon); var _PatchEvent = require('../../PatchEvent'); var _PatchEvent2 = _interopRequireDefault(_PatchEvent); var _ImageInput = require('./styles/ImageInput.css'); var _ImageInput2 = _interopRequireDefault(_ImageInput); 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 _ImageToolInput = require('../ImageToolInput'); var _ImageToolInput2 = _interopRequireDefault(_ImageToolInput); var _HotspotImage = require('@lyra/imagetool/HotspotImage'); var _HotspotImage2 = _interopRequireDefault(_HotspotImage); var _SelectAsset = require('./SelectAsset'); var _SelectAsset2 = _interopRequireDefault(_SelectAsset); 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, isSelectAssetOpen: boolean, hasFocus: boolean }*/ const HIDDEN_FIELDS = ['asset', 'hotspot', 'crop']; class ImageInput extends _react2.default.PureComponent /*:: <Props, State>*/ { constructor(...args) { var _temp; return _temp = super(...args), this.state = { isUploading: false, uploadError: null, isAdvancedEditOpen: false, isSelectAssetOpen: false, hasFocus: false }, 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*/ => { var _props$value = this.props.value; const value = _props$value === undefined ? {} : _props$value; return _react2.default.createElement(_HotspotImage2.default, { aspectRatio: 'auto', src: assetDocument.url, srcAspectRatio: assetDocument.metadata.dimensions.aspectRatio, hotspot: value.hotspot, crop: value.crop }); }, 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.handleOpenSelectAsset = () => { this.setState({ isSelectAssetOpen: true }); }, this.handleCloseSelectAsset = () => { this.setState({ isSelectAssetOpen: false }); }, this.handleSelectAsset = (asset /*: Object*/) => { var _props2 = this.props; const onChange = _props2.onChange, type = _props2.type; onChange(_PatchEvent2.default.from([(0, _PatchEvent.setIfMissing)({ _type: type.name }), (0, _PatchEvent.unset)(['hotspot']), (0, _PatchEvent.unset)(['crop']), (0, _PatchEvent.set)({ _type: 'reference', _ref: asset._id }, ['asset'])])); this.setState({ isSelectAssetOpen: false }); }, this.handleFocus = event => { this.setState({ hasFocus: true }); this.props.onFocus(event); }, this.handleBlur = event => { this.props.onBlur(event); this.setState({ hasFocus: false }); }, this.setFocusArea = (el /*: ?FocusArea*/) => { this._focusArea = el; }, this.getUploadOptions = (file /*: File*/) /*: Array<UploadOption>*/ => { var _props3 = this.props; const type = _props3.type, resolveUploader = _props3.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 _props4 = this.props; const resolveUploader = _props4.resolveUploader, type = _props4.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 _props5 = this.props; const type = _props5.type, onChange = _props5.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 => { console.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 && !!(this.props.value && this.props.value.asset); return _react2.default.createElement( 'div', { className: _ImageInput2.default.progress }, _react2.default.createElement( 'div', null, _react2.default.createElement( 'div', { className: isComplete ? _ImageInput2.default.progressBarCompleted : _ImageInput2.default.progressBar }, _react2.default.createElement(_circle2.default, { percent: status === 'complete' ? 100 : uploadState.progress, text: isComplete ? 'Please wait…' : 'Uploading…', completed: isComplete, showPercent: true, animation: true }) ), isUploading && _react2.default.createElement( _default2.default, { kind: 'simple', color: 'danger', onClick: this.handleCancelUpload }, 'Cancel' ) ) ); } isImageToolEnabled() { return (0, _get3.default)(this.props.type, 'options.hotspot') === true; } renderAdvancedEdit(fields /*: Array<FieldT>*/) { var _props6 = this.props; const value = _props6.value, level = _props6.level, onChange = _props6.onChange, readOnly = _props6.readOnly, materialize = _props6.materialize; return _react2.default.createElement( _fullscreen2.default, { title: 'Edit details', onClose: this.handleStopAdvancedEdit, isOpen: true }, this.isImageToolEnabled() && value && value.asset && _react2.default.createElement( _WithMaterializedReference2.default, { materialize: materialize, reference: value.asset }, imageAsset => _react2.default.createElement(_ImageToolInput2.default, { level: level, readOnly: readOnly, imageUrl: imageAsset.url, value: value, onChange: onChange }) ), _react2.default.createElement( 'div', { className: _ImageInput2.default.advancedEditFields }, 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 _props7 = this.props; const value = _props7.value, level = _props7.level, focusPath = _props7.focusPath, onFocus = _props7.onFocus, readOnly = _props7.readOnly, onBlur = _props7.onBlur; const fieldValue = value && value[field.name]; return _react2.default.createElement( 'div', { className: _ImageInput2.default.field, key: field.name }, _react2.default.createElement(_FormBuilderInput.FormBuilderInput, { 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 }) ); } focus() { if (this._focusArea) { this._focusArea.focus(); } } render() { var _props8 = this.props; const type = _props8.type, value = _props8.value, level = _props8.level, materialize = _props8.materialize, readOnly = _props8.readOnly; var _state = this.state; const isAdvancedEditOpen = _state.isAdvancedEditOpen, isSelectAssetOpen = _state.isSelectAssetOpen, uploadError = _state.uploadError, hasFocus = _state.hasFocus; 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; const showAdvancedEditButton = value && (otherFields.length > 0 || this.isImageToolEnabled()); return _react2.default.createElement( _UploadTargetFieldset2.default, { legend: type.title, description: type.description, level: level, onFocus: this.handleFocus, onBlur: this.handleBlur, className: _ImageInput2.default.root, 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: _ImageInput2.default.content }, _react2.default.createElement( 'div', { className: _ImageInput2.default.assetWrapper }, value && value._upload && _react2.default.createElement( 'div', { className: _ImageInput2.default.uploadState }, this.renderUploadState(value._upload) ), hasAsset ? _react2.default.createElement( _WithMaterializedReference2.default, { reference: value.asset, materialize: materialize }, this.renderMaterializedAsset ) : readOnly ? _react2.default.createElement( 'span', null, 'Field is read only' ) : _react2.default.createElement(_UploadPlaceholder2.default, { hasFocus: hasFocus }) ), highlightedFields.length > 0 && _react2.default.createElement( 'div', { className: _ImageInput2.default.fieldsWrapper }, this.renderFields(highlightedFields) ) ), _react2.default.createElement( 'div', { className: _ImageInput2.default.functions }, !readOnly && _react2.default.createElement( _button2.default, { icon: _uploadIcon2.default, onSelect: this.handleSelectFile, accept: '' /* todo build from this.props.resolveUploaders */ }, 'Upload' ), !readOnly && _react2.default.createElement( _default2.default, { onClick: this.handleOpenSelectAsset, kind: 'simple' }, 'Select from library' ), showAdvancedEditButton && _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' ), hasAsset && !readOnly && _react2.default.createElement( _default2.default, { color: 'danger', kind: 'simple', onClick: this.handleRemoveButtonClick }, 'Remove' ) ), isAdvancedEditOpen && this.renderAdvancedEdit(otherFields), isSelectAssetOpen && _react2.default.createElement( _fullscreen2.default, { title: 'Select image', onClose: this.handleCloseSelectAsset, isOpen: true }, _react2.default.createElement(_SelectAsset2.default, { onSelect: this.handleSelectAsset }) ) ); } } exports.default = ImageInput;