@lyra/form-builder
Version:
Lyra form builder
463 lines (388 loc) • 14.5 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 _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;