@lyra/form-builder
Version:
Lyra form builder
518 lines (437 loc) • 16.8 kB
JavaScript
'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;