diff --git a/packages/strapi-plugin-content-manager/admin/src/components/CustomDragLayer/index.js b/packages/strapi-plugin-content-manager/admin/src/components/CustomDragLayer/index.js index 5660f27b8e..b3955a9168 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/CustomDragLayer/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/CustomDragLayer/index.js @@ -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 ; + case ItemTypes.SORTABLEITEM: + return ; default: return null; } diff --git a/packages/strapi-plugin-content-manager/admin/src/components/CustomDragLayer/styles.scss b/packages/strapi-plugin-content-manager/admin/src/components/CustomDragLayer/styles.scss index fcae1f9c29..92b166c527 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/CustomDragLayer/styles.scss +++ b/packages/strapi-plugin-content-manager/admin/src/components/CustomDragLayer/styles.scss @@ -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; diff --git a/packages/strapi-plugin-content-manager/admin/src/components/EditRelations/index.js b/packages/strapi-plugin-content-manager/admin/src/components/EditRelations/index.js index 3ee59aac2e..5719eb04b3 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/EditRelations/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/EditRelations/index.js @@ -39,15 +39,16 @@ function EditRelations(props) { ); })} @@ -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, }; diff --git a/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/SortableItem.js b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/SortableItem.js index 0a9d62193d..8eef7f75ac 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/SortableItem.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/SortableItem.js @@ -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( -
  • -
    +
  • + + {/*
    {title => ( @@ -110,8 +128,8 @@ class SortableItem extends React.Component {
    - Remove Icon onRemove(sortIndex)} /> -
    + Remove Icon onRemove(index)} /> + */}
  • ), ) @@ -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); \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/SortableList.js b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/SortableList.js index 5cc641159c..5723cf59d6 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/SortableList.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/SortableList.js @@ -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 (
      {items.map((item, index) => ( ))}
    @@ -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, }; diff --git a/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/index.js b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/index.js index 1e6814ac38..0093f87a92 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/index.js @@ -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, }; diff --git a/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/styles.scss b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/styles.scss index c73d49d9d5..4cdabeabe3 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/styles.scss +++ b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/styles.scss @@ -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; diff --git a/packages/strapi-plugin-content-manager/admin/src/components/SelectManyDraggedItem/Content.js b/packages/strapi-plugin-content-manager/admin/src/components/SelectManyDraggedItem/Content.js new file mode 100644 index 0000000000..33cb2ee859 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/components/SelectManyDraggedItem/Content.js @@ -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 ( + +
    +
    + + {title => ( + onClick(item)} + title={title} + > + {item.label} + + )} + +
    +
    + Remove Icon onRemove(index)} /> +
    +
    + ); +} + +Content.defaultProps = { + index: 0, + onClick: () => {}, + onRemove: () => {}, +}; + +Content.propTypes = { + index: PropTypes.number, + item: PropTypes.object.isRequired, + onClick: PropTypes.func, + onRemove: PropTypes.func, +}; + +export default Content; \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/components/SelectManyDraggedItem/index.js b/packages/strapi-plugin-content-manager/admin/src/components/SelectManyDraggedItem/index.js new file mode 100644 index 0000000000..27fffb1aa1 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/components/SelectManyDraggedItem/index.js @@ -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 ( +
  • + +
  • + ); + } + + return ; +} + +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; \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/actions.js b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/actions.js index 1a0097c558..b4f5ec76ea 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/actions.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/actions.js @@ -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, diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/constants.js b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/constants.js index ed569261ee..6bf60c3159 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/constants.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/constants.js @@ -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'; diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/index.js b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/index.js index c40fc0844a..86eec4057a 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/index.js @@ -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 (
    +
    {this.hasDisplayedRelations() && ( )}
    @@ -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, diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/reducer.js b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/reducer.js index e114654faa..7842f18de0 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/reducer.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/reducer.js @@ -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: diff --git a/packages/strapi-plugin-content-manager/config/functions/bootstrap.js b/packages/strapi-plugin-content-manager/config/functions/bootstrap.js index f266aa2ad4..ca680cd8e6 100644 --- a/packages/strapi-plugin-content-manager/config/functions/bootstrap.js +++ b/packages/strapi-plugin-content-manager/config/functions/bootstrap.js @@ -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 => {