Remove react-sortable-hoc

This commit is contained in:
soupette 2018-08-09 17:02:15 +02:00
parent afa4c28378
commit 3d42a91a0c
14 changed files with 216 additions and 108 deletions

View File

@ -8,6 +8,7 @@ import PropTypes from 'prop-types';
import { DragLayer } from 'react-dnd';
import { flow } from 'lodash';
import DragBox from 'components/DragBox';
import SelectManyDraggedItem from 'components/SelectManyDraggedItem';
import ItemTypes from 'utils/ItemTypes';
import styles from './styles.scss';
@ -33,6 +34,8 @@ class CustomDragLayer extends React.Component {
case ItemTypes.VARIABLE:
case ItemTypes.NORMAL:
return <DragBox name={item.id} />;
case ItemTypes.SORTABLEITEM:
return <SelectManyDraggedItem item={item.data} withLiWrapper />;
default:
return null;
}

View File

@ -20,7 +20,7 @@
color: #333740;
border: 1px solid darken(#AED4FB, 20%)!important;
border-radius: 2px;
cursor: move !important;
// cursor: move !important;
> i {
margin-right: 10px;

View File

@ -39,15 +39,16 @@ function EditRelations(props) {
<SelectMany
currentModelName={props.currentModelName}
key={relationName}
record={props.record}
relation={relation}
schema={props.schema}
isDraggingSibling={props.isDraggingSibling}
location={props.location}
moveAttr={props.moveAttr}
moveAttrEnd={props.moveAttrEnd}
onAddRelationalItem={props.onAddRelationalItem}
onRedirect={props.onRedirect}
onRemoveRelationItem={props.onRemoveRelationItem}
onSort={props.onSort}
record={props.record}
relation={relation}
schema={props.schema}
/>
);
})}
@ -57,7 +58,9 @@ function EditRelations(props) {
EditRelations.defaultProps = {
displayedRelations: [],
isDraggingSibling: false,
moveAttr: () => {},
moveAttrEnd: () => {},
record: {},
schema: {},
};
@ -65,12 +68,13 @@ EditRelations.defaultProps = {
EditRelations.propTypes = {
currentModelName: PropTypes.string.isRequired,
displayedRelations: PropTypes.array,
isDraggingSibling: PropTypes.bool,
location: PropTypes.object.isRequired,
moveAttr: PropTypes.func,
moveAttrEnd: PropTypes.func,
onAddRelationalItem: PropTypes.func.isRequired,
onRedirect: PropTypes.func.isRequired,
onRemoveRelationItem: PropTypes.func.isRequired,
onSort: PropTypes.func.isRequired,
record: PropTypes.object,
schema: PropTypes.object,
};

View File

@ -8,14 +8,12 @@
import React from 'react';
import { findDOMNode } from 'react-dom';
import { DragSource, DropTarget } from 'react-dnd';
import { getEmptyImage } from 'react-dnd-html5-backend';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { flow, get } from 'lodash';
// import { SortableElement } from 'react-sortable-hoc';
// Icons.
import IconRemove from 'assets/images/icon_remove.svg';
import cn from 'classnames';
import SelectManyDraggedItem from 'components/SelectManyDraggedItem';
import ItemTypes from 'utils/ItemTypes';
// CSS.
import styles from './styles.scss';
const sortableItemSource = {
@ -23,9 +21,11 @@ const sortableItemSource = {
return {
id: get(props, ['item', 'value', 'id' ]) || get(props, ['item', 'value', '_id'], ''),
index: props.index,
data: props.item,
};
},
endDrag: () => {
endDrag: props => {
props.moveAttrEnd();
return {};
},
};
@ -80,21 +80,39 @@ const sortableItemTarget = {
};
class SortableItem extends React.Component {
componentDidMount() {
// Use empty image as a drag preview so browsers don't draw it
// and we can draw whatever we want on the custom drag layer instead.
this.props.connectDragPreview(getEmptyImage(), {
// IE fallback: specify that we'd rather screenshot the node
// when it already knows it's being dragged so we can hide it with CSS.
// Removginv the fallabck makes it handle variable height elements
// captureDraggingState: true,
});
}
render() {
const {
connectDragSource,
connectDropTarget,
index,
item,
isDragging,
isDraggingSibling,
onClick,
onRemove,
sortIndex,
} = this.props;
const opacity = isDragging ? 0.2 : 1;
return (
connectDragSource(
connectDropTarget(
<li className={styles.sortableListItem}>
<div>
<li
className={cn(styles.sortableListItem, !isDraggingSibling && styles.sortableListItemHover)}
style={{ opacity }}
>
<SelectManyDraggedItem index={index} item={item} onClick={onClick} onRemove={onRemove} />
{/* <div>
<div className={styles.dragHandle}><span></span></div>
<FormattedMessage id='content-manager.containers.Edit.clickToJump'>
{title => (
@ -110,8 +128,8 @@ class SortableItem extends React.Component {
</div>
<div className={styles.sortableListItemActions}>
<img src={IconRemove} alt="Remove Icon" onClick={() => onRemove(sortIndex)} />
</div>
<img src={IconRemove} alt="Remove Icon" onClick={() => onRemove(index)} />
</div> */}
</li>
),
)
@ -124,18 +142,25 @@ const withDropTarget = DropTarget(ItemTypes.SORTABLEITEM, sortableItemTarget, co
}));
const withDragSource = DragSource(ItemTypes.SORTABLEITEM, sortableItemSource, (connect, monitor) => ({
connectDragPreview: connect.dragPreview(),
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}));
SortableItem.defaultProps = {
isDraggingSibling: false,
};
SortableItem.propTypes = {
connectDragPreview: PropTypes.func.isRequired,
connectDragSource: PropTypes.func.isRequired,
connectDropTarget: PropTypes.func.isRequired,
index: PropTypes.number.isRequired,
isDragging: PropTypes.bool.isRequired,
isDraggingSibling: PropTypes.bool,
item: PropTypes.object.isRequired,
onClick: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
sortIndex: PropTypes.number.isRequired,
};
export default flow([withDropTarget, withDragSource])(SortableItem);

View File

@ -12,20 +12,21 @@ import SortableItem from './SortableItem';
// CSS.
import styles from './styles.scss';
const SortableList = ({ items, keys, moveAttr, onClick, onRemove }) => {
const SortableList = ({ items, isDraggingSibling, keys, moveAttr, moveAttrEnd, onClick, onRemove }) => {
return (
<div className={cn(styles.sortableList)}>
<ul>
{items.map((item, index) => (
<SortableItem
key={`item-${index}`}
isDraggingSibling={isDraggingSibling}
key={item.value.id || item.value._id || `item-${index}`}
keys={keys}
index={index}
item={item}
moveAttr={moveAttr}
moveAttrEnd={moveAttrEnd}
onRemove={onRemove}
onClick={onClick}
sortIndex={index}
/>
))}
</ul>
@ -35,9 +36,11 @@ const SortableList = ({ items, keys, moveAttr, onClick, onRemove }) => {
};
SortableList.propTypes = {
isDraggingSibling: PropTypes.bool.isRequired,
items: PropTypes.array.isRequired,
keys: PropTypes.string.isRequired,
moveAttr: PropTypes.func.isRequired,
moveAttrEnd: PropTypes.func.isRequired,
onClick: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
};

View File

@ -123,23 +123,18 @@ class SelectMany extends React.PureComponent {
});
}
handleSortEnd = ({ oldIndex, newIndex }) => {
this.props.onSort({
key: this.props.relation.alias,
oldIndex,
newIndex,
});
};
handleRemove = (index) => {
const values = get(this.props.record, this.props.relation.alias);
// Add removed value from available option;
this.state.options.push({
const toAdd = {
value: values[index],
label: templateObject({ mainField: this.props.relation.displayedAttribute }, values[index])
.mainField,
});
label: templateObject({ mainField: this.props.relation.displayedAttribute }, values[index]).mainField,
};
this.setState(prevState => ({
options: prevState.options.concat([toAdd]),
}));
this.props.onRemoveRelationItem({
key: this.props.relation.alias,
@ -162,7 +157,6 @@ class SelectMany extends React.PureComponent {
) : (
''
);
const value = get(this.props.record, this.props.relation.alias) || [];
/* eslint-disable jsx-a11y/label-has-for */
@ -197,9 +191,10 @@ class SelectMany extends React.PureComponent {
}
})
}
isDraggingSibling={this.props.isDraggingSibling}
keys={this.props.relation.alias}
moveAttr={this.props.moveAttr}
onSortEnd={this.handleSortEnd}
moveAttrEnd={this.props.moveAttrEnd}
onRemove={this.handleRemove}
distance={1}
onClick={this.handleClick}
@ -211,15 +206,18 @@ class SelectMany extends React.PureComponent {
}
SelectMany.defaultProps = {
isDraggingSibling: false,
moveAttr: () => {},
moveAttrEnd: () => {},
};
SelectMany.propTypes = {
isDraggingSibling: PropTypes.bool,
moveAttr: PropTypes.func,
moveAttrEnd: PropTypes.func,
onAddRelationalItem: PropTypes.func.isRequired,
onRedirect: PropTypes.func.isRequired,
onRemoveRelationItem: PropTypes.func.isRequired,
onSort: PropTypes.func.isRequired,
record: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]).isRequired,
relation: PropTypes.object.isRequired,
};

View File

@ -50,7 +50,7 @@
margin: 4px -20px 0;
padding: 0 20px !important;
list-style: none !important;
overflow: scroll;
overflow: auto;
max-height: 110px;
}
@ -75,22 +75,23 @@
}
}
.sortableListItemHover {
&:hover {
cursor: pointer;
}
}
.sortableListItem {
display: flex;
flex-wrap: nowrap;
align-content: center;
justify-content: space-between;
height: 27px;
&:hover{
cursor: pointer;
}
background-color: transparent !important;
&:active{
.dragHandle{
cursor: pointer;
// cursor: pointer;
> span {
background: #AED4FB;

View File

@ -0,0 +1,48 @@
/**
*
* Content
*/
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import IconRemove from 'assets/images/icon_remove.svg';
import styles from 'components/SelectMany/styles.scss';
function Content({ index, item, onClick, onRemove }) {
return (
<React.Fragment>
<div>
<div className={styles.dragHandle}><span /></div>
<FormattedMessage id="content-manager.containers.Edit.clickToJump">
{title => (
<span
onClick={() => onClick(item)}
title={title}
>
{item.label}
</span>
)}
</FormattedMessage>
</div>
<div className={styles.selectManyDraggedItemActions}>
<img src={IconRemove} alt="Remove Icon" onClick={() => onRemove(index)} />
</div>
</React.Fragment>
);
}
Content.defaultProps = {
index: 0,
onClick: () => {},
onRemove: () => {},
};
Content.propTypes = {
index: PropTypes.number,
item: PropTypes.object.isRequired,
onClick: PropTypes.func,
onRemove: PropTypes.func,
};
export default Content;

View File

@ -0,0 +1,39 @@
/**
*
* SelectManyDraggedItem
*/
import React from 'react';
import PropTypes from 'prop-types';
import styles from 'components/SelectMany/styles.scss';
import Content from './Content';
function SelectManyDraggedItem(props) {
if (props.withLiWrapper) {
return (
<li className={styles.sortableListItem} style={{ padding: '0 2px' }}>
<Content {...props} />
</li>
);
}
return <Content {...props} />;
}
SelectManyDraggedItem.defaultProps = {
index: 0,
onClick: () => {},
onRemove: () => {},
withLiWrapper: false,
};
SelectManyDraggedItem.propTypes = {
index: PropTypes.number,
item: PropTypes.object.isRequired,
onClick: PropTypes.func,
onRemove: PropTypes.func,
withLiWrapper: PropTypes.bool,
};
export default SelectManyDraggedItem;

View File

@ -14,13 +14,13 @@ import {
GET_DATA_SUCCEEDED,
INIT_MODEL_PROPS,
MOVE_ATTR,
MOVE_ATTR_END,
ON_CANCEL,
REMOVE_RELATION_ITEM,
ON_REMOVE_RELATION_ITEM,
RESET_PROPS,
SET_FILE_RELATIONS,
SET_LOADER,
SET_FORM_ERRORS,
SORT_RELATIONS,
SUBMIT,
SUBMIT_SUCCESS,
UNSET_LOADER,
@ -98,15 +98,21 @@ export function moveAttr(dragIndex, hoverIndex, keys) {
};
}
export function moveAttrEnd() {
return {
type: MOVE_ATTR_END,
};
}
export function onCancel() {
return {
type: ON_CANCEL,
};
}
export function removeRelationItem({ key, index }) {
export function onRemoveRelationItem({ key, index }) {
return {
type: REMOVE_RELATION_ITEM,
type: ON_REMOVE_RELATION_ITEM,
key,
index,
};
@ -138,15 +144,6 @@ export function setLoader() {
};
}
export function sortRelations({ key, oldIndex, newIndex }) {
return {
type: SORT_RELATIONS,
key,
oldIndex,
newIndex,
};
}
export function submit() {
return {
type: SUBMIT,

View File

@ -10,13 +10,13 @@ export const GET_DATA = 'ContentManager/EditPage/GET_DATA';
export const GET_DATA_SUCCEEDED = 'ContentManager/EditPage/GET_DATA_SUCCEEDED';
export const INIT_MODEL_PROPS = 'ContentManager/EditPage/INIT_MODEL_PROPS';
export const MOVE_ATTR = 'ContentManager/EditPage/MOVE_ATTR';
export const MOVE_ATTR_END = 'ContentManager/EditPage/MOVE_ATTR_END';
export const ON_CANCEL = 'ContentManager/EditPage/ON_CANCEL';
export const REMOVE_RELATION_ITEM = 'ContentManager/EditPage/REMOVE_RELATION_ITEM';
export const ON_REMOVE_RELATION_ITEM = 'ContentManager/EditPage/ON_REMOVE_RELATION_ITEM';
export const RESET_PROPS = 'ContentManager/EditPage/RESET_PROPS';
export const SET_FILE_RELATIONS = 'ContentManager/EditPage/SET_FILE_RELATIONS';
export const SET_LOADER = 'ContentManager/EditPage/SET_LOADER';
export const SET_FORM_ERRORS = 'ContentManager/EditPage/SET_FORM_ERRORS';
export const SORT_RELATIONS = 'ContentManager/EditPage/SORT_RELATIONS';
export const SUBMIT = 'ContentManager/EditPage/SUBMIT';
export const SUBMIT_SUCCESS = 'ContentManager/EditPage/SUBMIT_SUCCESS';
export const UNSET_LOADER = 'ContentManager/EditPage/UNSET_LOADER';

View File

@ -23,6 +23,7 @@ import LoadingIndicator from 'components/LoadingIndicator';
import PluginHeader from 'components/PluginHeader';
import PopUpWarning from 'components/PopUpWarning';
// Plugin's components
import CustomDragLayer from 'components/CustomDragLayer';
import Edit from 'components/Edit';
import EditRelations from 'components/EditRelations';
// App selectors
@ -40,12 +41,12 @@ import {
getData,
initModelProps,
moveAttr,
moveAttrEnd,
onCancel,
removeRelationItem,
onRemoveRelationItem,
resetProps,
setFileRelations,
setFormErrors,
sortRelations,
submit,
} from './actions';
import reducer from './reducer';
@ -267,14 +268,6 @@ export class EditPage extends React.Component {
/* eslint-enable */
}
handleRemoveRelationItem = ({ key, index }) => {
this.props.removeRelationItem({ key, index });
}
handleSortRelations = ({ key, oldIndex, newIndex }) => {
this.props.sortRelations({ key, oldIndex, newIndex });
}
handleSubmit = (e) => {
e.preventDefault();
const formErrors = checkFormValidity(this.generateFormFromRecord(), this.props.editPage.formValidations);
@ -393,13 +386,14 @@ export class EditPage extends React.Component {
}
render() {
const { editPage, moveAttr } = this.props;
const { editPage, moveAttr, moveAttrEnd } = this.props;
const { showWarning } = this.state;
return (
<div>
<form onSubmit={this.handleSubmit}>
<BackHeader onClick={this.handleGoBack} />
<CustomDragLayer />
<div className={cn('container-fluid', styles.containerFluid)}>
<PluginHeader
actions={this.pluginHeaderActions()}
@ -424,17 +418,18 @@ export class EditPage extends React.Component {
<div className={styles.sub_wrapper}>
{this.hasDisplayedRelations() && (
<EditRelations
changeData={this.props.changeData}
currentModelName={this.getModelName()}
displayedRelations={this.getDisplayedRelations()}
isDraggingSibling={editPage.isDraggingSibling}
location={this.props.location}
changeData={this.props.changeData}
record={editPage.record}
schema={this.getSchema()}
moveAttr={moveAttr}
moveAttrEnd={moveAttrEnd}
onAddRelationalItem={this.handleAddRelationItem}
onRedirect={this.handleRedirect}
onRemoveRelationItem={this.handleRemoveRelationItem}
onSort={this.handleSortRelations}
onRemoveRelationItem={this.props.onRemoveRelationItem}
record={editPage.record}
schema={this.getSchema()}
/>
)}
</div>
@ -466,13 +461,13 @@ EditPage.propTypes = {
location: PropTypes.object.isRequired,
match: PropTypes.object.isRequired,
moveAttr: PropTypes.func.isRequired,
moveAttrEnd: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
removeRelationItem: PropTypes.func.isRequired,
onRemoveRelationItem: PropTypes.func.isRequired,
resetProps: PropTypes.func.isRequired,
schema: PropTypes.object,
setFileRelations: PropTypes.func.isRequired,
setFormErrors: PropTypes.func.isRequired,
sortRelations: PropTypes.func.isRequired,
submit: PropTypes.func.isRequired,
};
@ -484,12 +479,12 @@ function mapDispatchToProps(dispatch) {
getData,
initModelProps,
moveAttr,
moveAttrEnd,
onCancel,
removeRelationItem,
onRemoveRelationItem,
resetProps,
setFileRelations,
setFormErrors,
sortRelations,
submit,
},
dispatch,

View File

@ -11,13 +11,13 @@ import {
GET_DATA_SUCCEEDED,
INIT_MODEL_PROPS,
MOVE_ATTR,
MOVE_ATTR_END,
ON_CANCEL,
REMOVE_RELATION_ITEM,
ON_REMOVE_RELATION_ITEM,
RESET_PROPS,
SET_FILE_RELATIONS,
SET_FORM_ERRORS,
SET_LOADER,
SORT_RELATIONS,
SUBMIT_SUCCESS,
UNSET_LOADER,
} from './constants';
@ -30,6 +30,7 @@ const initialState = fromJS({
isCreating: false,
id: '',
initialRecord: Map({}),
isDraggingSibling: false,
isLoading: true,
modelName: '',
pluginHeaderTitle: 'New Entry',
@ -70,18 +71,22 @@ function editPageReducer(state = initialState, action) {
.update('record', () => fromJS(action.record))
.update('source', () => action.source);
case MOVE_ATTR:
return state.updateIn(['record', action.keys], list => {
return list
.delete(action.dragIndex)
.insert(action.hoverIndex, list.get(action.dragIndex));
});
return state
.updateIn(['record', action.keys], list => {
return list
.delete(action.dragIndex)
.insert(action.hoverIndex, list.get(action.dragIndex));
})
.update('isDraggingSibling', () => true);
case MOVE_ATTR_END:
return state.update('isDraggingSibling', () => false);
case ON_CANCEL:
return state
.update('didCheckErrors', (v) => v = !v)
.update('formErrors', () => List([]))
.update('record', () => state.get('initialRecord'))
.update('resetProps', (v) => v = !v);
case REMOVE_RELATION_ITEM:
case ON_REMOVE_RELATION_ITEM:
return state
.updateIn(['record', action.key], (list) => {
return list
@ -98,16 +103,6 @@ function editPageReducer(state = initialState, action) {
case SET_LOADER:
return state
.update('showLoader', () => true);
case SORT_RELATIONS: {
const item = state.getIn(['record', action.key, action.oldIndex]);
return state
.updateIn(['record', action.key], (list) => {
return list
.delete(action.oldIndex)
.insert(action.newIndex, item);
});
}
case SUBMIT_SUCCESS:
return state.update('submitSuccess', (v) => v = !v);
case UNSET_LOADER:

View File

@ -92,17 +92,17 @@ module.exports = async cb => {
};
});
if (model.orm === 'mongoose') {
fields.createdAt = { label: 'createdAt', description: '', type: 'date', disabled: true };
fields.updatedAt = { label: 'updatedAt', description: '', type: 'date', disabled: true };
schemaModel.attributes.updatedAt = { type: 'date' };
schemaModel.attributes.createdAt = { type: 'date' };
} else {
fields.created_at = { label: 'created_at', description: '', type: 'date', disabled: true };
fields.updated_at = { label: 'updated_at', description: '', type: 'date', disabled: true };
schemaModel.attributes.created_at = { type: 'date' };
schemaModel.attributes.updated_at = { type: 'date' };
}
// if (model.orm === 'mongoose') {
// fields.createdAt = { label: 'createdAt', description: '', type: 'date', disabled: true };
// fields.updatedAt = { label: 'updatedAt', description: '', type: 'date', disabled: true };
// schemaModel.attributes.updatedAt = { type: 'date' };
// schemaModel.attributes.createdAt = { type: 'date' };
// } else {
// fields.created_at = { label: 'created_at', description: '', type: 'date', disabled: true };
// fields.updated_at = { label: 'updated_at', description: '', type: 'date', disabled: true };
// schemaModel.attributes.created_at = { type: 'date' };
// schemaModel.attributes.updated_at = { type: 'date' };
// }
// Don't display fields that are hidden by default like the resetPasswordToken for the model user
fieldsToRemove.forEach(field => {