@jsonforms/material-tree-renderer
Version:
Material-based tree renderer for JSON Forms
357 lines (353 loc) • 15.9 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/*
The MIT License
Copyright (c) 2017-2019 EclipseSource Munich
https://github.com/eclipsesource/jsonforms
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// tslint:disable:jsx-no-multiline-js
var isEmpty_1 = __importDefault(require("lodash/isEmpty"));
var clone_1 = __importDefault(require("lodash/clone"));
var initial_1 = __importDefault(require("lodash/initial"));
var groupBy_1 = __importDefault(require("lodash/groupBy"));
var util_1 = require("../helpers/util");
var react_1 = __importDefault(require("react"));
var react_redux_1 = require("react-redux");
var core_1 = require("@jsonforms/core");
var ExpandArray_1 = __importDefault(require("./ExpandArray"));
var property_util_1 = require("../services/property.util");
var _a = require('react-dnd'), DragSource = _a.DragSource, DropTarget = _a.DropTarget;
var dnd_util_1 = require("./dnd.util");
var IconButton_1 = __importDefault(require("@material-ui/core/IconButton"));
var Delete_1 = __importDefault(require("@material-ui/icons/Delete"));
var Add_1 = __importDefault(require("@material-ui/icons/Add"));
var Typography_1 = __importDefault(require("@material-ui/core/Typography"));
var styles_1 = require("@material-ui/core/styles");
var recompose_1 = require("recompose");
var image_provider_util_1 = require("../helpers/image-provider.util");
/**
* The delay (in milliseconds) between removing this object list item's data from the store
* and resetting the selection.
* This is necessary because without a delay the resetted selection is overwritten
* by the on click handler of this object list item which sets the selection to this item.
*/
var RESET_SELECTION_DELAY = 40;
var styles = function (theme) { return styles_1.createStyles({
listItem: {
minHeight: '1em',
position: 'relative',
paddingLeft: '1.5em',
'&:before': {
content: '""',
position: 'absolute',
left: '0.2em',
top: '0.5em',
width: '1em'
},
'&:after': {
content: '""',
position: 'absolute',
left: '0.2em',
top: '-0.5em',
height: '100%'
}
},
withoutBorders: {
'&:last-child:after': {
display: 'none'
},
'&:only-child': {
paddingLeft: '0.25em'
},
'&:only-child:before': {
display: 'none'
}
},
itemContainer: {
display: 'flex',
flexDirection: 'row',
'& .icon': {
flexBasis: '1em',
minHeight: '1em',
marginRight: '0.25em',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center'
},
'&:hover': {
fontWeight: 'bold',
cursor: 'pointer',
opacity: 0.9,
backgroundColor: theme.palette.secondary.main,
},
alignItems: 'center'
},
label: {
display: 'flex',
flex: 1,
marginRight: '1em',
minHeight: '1.5em',
alignItems: 'center',
'& span:first-child:empty': {
background: '#ffff00',
maxHeight: '1.5em'
},
'&:hover $actionButton': {
display: 'flex',
justifyContent: 'center',
'&:hover': {
color: 'white',
backgroundColor: theme.palette.secondary.dark,
borderRadius: '50%'
}
}
},
actionButton: {
display: 'none',
cursor: 'pointer',
marginLeft: '0.25em',
fontWeight: 'normal',
width: 'inherit',
height: 'inherit'
},
selected: {
fontWeight: 'bold'
}
}); };
var ObjectListItem = /** @class */ (function (_super) {
__extends(ObjectListItem, _super);
function ObjectListItem() {
return _super !== null && _super.apply(this, arguments) || this;
}
ObjectListItem.prototype.render = function () {
var _a = this.props, path = _a.path, schema = _a.schema, rootData = _a.rootData, data = _a.data, handlers = _a.handlers, selection = _a.selection, filterPredicate = _a.filterPredicate, containerProperties = _a.containerProperties, labelProvider = _a.labelProvider, imageProvider = _a.imageProvider, classes = _a.classes;
var pathSegments = path.split('.');
var parentPath = initial_1.default(pathSegments).join('.');
var labelClass = selection === data ? classes.selected : '';
var hasParent = !isEmpty_1.default(parentPath);
var liClass = !hasParent ? [classes.listItem, classes.withoutBorders].join(' ') : classes.listItem;
var scopedData = core_1.resolveData(rootData, parentPath);
var groupedProps = groupBy_1.default(containerProperties, function (property) { return property.property; });
return (react_1.default.createElement("li", { className: liClass, key: path },
react_1.default.createElement("div", { className: classes.itemContainer },
image_provider_util_1.wrapImageIfNecessary(imageProvider(schema)),
react_1.default.createElement("span", { className: classes.label, onClick: handlers.onSelect(schema, data, path) },
react_1.default.createElement(Typography_1.default, { className: labelClass }, labelProvider(schema, data, path)),
!isEmpty_1.default(containerProperties) ?
(react_1.default.createElement(IconButton_1.default, { className: classes.actionButton, "aria-label": 'Add', onClick: handlers.onAdd(schema, path) },
react_1.default.createElement(Add_1.default, null))) : '',
(hasParent || Array.isArray(scopedData)) &&
react_1.default.createElement(IconButton_1.default, { className: classes.actionButton, "aria-label": 'Remove', onClick: handlers.onRemove },
react_1.default.createElement(Delete_1.default, null)))),
Object.keys(groupedProps).map(function (groupKey) {
return react_1.default.createElement(ExpandArray_1.default, { key: groupKey, containmentProps: groupedProps[groupKey], path: core_1.Paths.compose(path, groupKey), selection: selection, handlers: handlers, filterPredicate: filterPredicate, labelProvider: labelProvider, imageProvider: imageProvider });
})));
};
return ObjectListItem;
}(react_1.default.Component));
// TODO
var mapStateToProps = function (state, ownProps) {
var index = util_1.indexFromPath(ownProps.path);
var containerProperties = property_util_1.findContainerProperties(ownProps.schema, core_1.getSchema(state), false);
return {
index: index,
rootData: core_1.getData(state),
rootSchema: core_1.getSchema(state),
containerProperties: containerProperties
};
};
// TODO
var mapDispatchToProps = function (dispatch, ownProps) {
var parentPath = initial_1.default(ownProps.path.split('.')).join('.');
var index = util_1.indexFromPath(ownProps.path);
return {
remove: function () {
dispatch(core_1.update(parentPath, function (array) {
var clonedArray = clone_1.default(array);
clonedArray.splice(index, 1);
return clonedArray;
}));
},
moveListItem: dnd_util_1.moveListItem(dispatch)
};
};
var mergeProps = function (stateProps, dispatchProps, ownProps) {
var data = core_1.resolveData(stateProps.rootData, ownProps.path);
var resetSelection = ownProps.handlers.resetSelection;
resetSelection = resetSelection ? resetSelection : function () { return undefined; };
return __assign({}, stateProps, dispatchProps, ownProps, { data: data, handlers: __assign({}, ownProps.handlers, { onRemove: function () {
var result = dispatchProps.remove(data);
// Need to reset the selection with a delay because otherwise it gets overwritten
// by the on click handler of the item also setting the selection.
setTimeout(resetSelection, RESET_SELECTION_DELAY);
return result;
} }) });
};
var ListItem = recompose_1.compose(styles_1.withStyles(styles, { name: 'ObjectListItem' }))(ObjectListItem);
var ObjectListItemDnd = /** @class */ (function (_super) {
__extends(ObjectListItemDnd, _super);
function ObjectListItemDnd() {
return _super !== null && _super.apply(this, arguments) || this;
}
ObjectListItemDnd.prototype.render = function () {
var _a = this.props, path = _a.path, schema = _a.schema, rootData = _a.rootData, data = _a.data, handlers = _a.handlers, selection = _a.selection, isRoot = _a.isRoot,
// isDragging,
connectDragSource = _a.connectDragSource, connectDropTarget = _a.connectDropTarget, filterPredicate = _a.filterPredicate, containerProperties = _a.containerProperties, labelProvider = _a.labelProvider, imageProvider = _a.imageProvider;
var listItem = (react_1.default.createElement(ListItem, { path: path, schema: schema, rootData: rootData, data: data, handlers: handlers, selection: selection, filterPredicate: filterPredicate, containerProperties: containerProperties, labelProvider: labelProvider, imageProvider: imageProvider }));
if (isRoot === true) {
// No Drag and Drop
return listItem;
}
// const opacity = isDragging ? 0.2 : 1;
// wrap in div because react-dnd does not allow directly connecting to components
return connectDragSource(connectDropTarget(react_1.default.createElement("div", null, listItem)));
};
return ObjectListItemDnd;
}(react_1.default.Component));
exports.ObjectListItemDnd = ObjectListItemDnd;
/**
* Define the drag and drop source (item is dragged) behavior of the list items
*/
var objectDragSource = {
beginDrag: function (props) {
var dragInfo = {
originalPath: props.path,
currentPath: props.path,
data: props.data,
schema: props.schema
};
console.log('drag started', props.path);
return dragInfo;
},
endDrag: function (props, monitor) {
var dragInfo = monitor.getItem();
var dropInfo = monitor.getDropResult();
console.log("End Drag; monitor.didDrop(): " + monitor.didDrop());
if (!monitor.didDrop()) {
// List item was not successfully dropped => revert to original position if it was moved
if (dragInfo.originalPath !== dragInfo.currentPath) {
console.log("No valid drop. Revert position. Original: " + dragInfo.originalPath + "." +
("Current: " + dragInfo.currentPath));
props.moveListItem(dragInfo.data, dragInfo.currentPath, dragInfo.originalPath);
}
}
else if (dropInfo.move) {
console.log("Drop in endDrag. Move Target: " + dropInfo.moveTarget);
props.moveListItem(dragInfo.data, dragInfo.currentPath, dropInfo.moveTarget);
}
}
};
/**
* Injects drag and drop (drag source) related properties into a list item
*/
var collectDragSource = function (dndConnect, monitor) {
return {
connectDragSource: dndConnect.dragSource(),
isDragging: monitor.isDragging()
};
};
/**
* Injects drag and drop (drop target) related properties into a list item
*/
var collectDropTarget = function (dndConnect, monitor) {
return {
isOver: monitor.isOver({ shallow: true }),
connectDropTarget: dndConnect.dropTarget()
};
};
/**
* Define the drag and drop target (another item is dragged over this item) behavior of list items.
*/
var objectDropTarget = {
canDrop: function (props, monitor) {
return dnd_util_1.canDropDraggedItem(props.parentProperties, monitor.getItem());
},
/**
* On drop of a dragged item on another dragged item, mark the drop as handled
* and that the item does not need to be moved.
* This is the case because the item is already moved on hover.
*/
drop: function () {
var dropInfo = {
isHandled: true,
move: false
};
return dropInfo;
},
/**
* If the dragged item can be dropped in the list of the hovered item, move it there on the fly.
*
* @param props The properties of the hovered list element
* @param monitor
*/
hover: function (props, monitor) {
if (!monitor.isOver({ shallow: true })) {
return;
}
if (!monitor.canDrop()) {
return;
}
var dragInfo = monitor.getItem();
if (props.path === dragInfo.currentPath) {
return;
}
// move list item and update current path of the currently dragged item
var targetPath = props.path;
var moved = props.moveListItem(dragInfo.data, dragInfo.currentPath, targetPath);
if (moved) {
dragInfo.currentPath = targetPath;
console.log('new current path: ' + dragInfo.currentPath);
}
}
};
/**
* Configure a list item as a drop target and a drag source.
* Drop Target: Allows to drop other list items on this list item and is necessary
* to sort elements inside a list
*
* Drag Source: Allows to drag a list item
*/
var listItemDnd = DropTarget(dnd_util_1.Types.TREE_DND, objectDropTarget, collectDropTarget)(DragSource(dnd_util_1.Types.TREE_DND, objectDragSource, collectDragSource)(ObjectListItemDnd));
exports.default = react_redux_1.connect(mapStateToProps, mapDispatchToProps, mergeProps)(listItemDnd);
//# sourceMappingURL=ObjectListItem.js.map