Skip to content

Commit

Permalink
ENH MutliLinkField sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
emteknetnz committed Jan 12, 2024
1 parent 5db919e commit 4c9c78d
Show file tree
Hide file tree
Showing 14 changed files with 7,053 additions and 206 deletions.
6,516 changes: 6,515 additions & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

175 changes: 174 additions & 1 deletion client/dist/styles/bundle.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion client/lang/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') {
"LinkField.CANNOT_CREATE_LINK": "Cannot create link",
"LinkField.FAILED_TO_LOAD_LINKS": "Failed to load links",
"LinkField.FAILED_TO_SAVE_LINK": "Failed to save link",
"LinkField.SAVE_RECORD_FIRST": "Cannot add links until the record has been saved"
"LinkField.SAVE_RECORD_FIRST": "Cannot add links until the record has been saved",
"LinkField.SORT_SUCCESS": "Updated link sort order",
"LinkField.SORT_ERROR": "Unable to sort links"
});
}
4 changes: 3 additions & 1 deletion client/lang/src/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@
"LinkField.CANNOT_CREATE_LINK": "Cannot create link",
"LinkField.FAILED_TO_LOAD_LINKS": "Failed to load links",
"LinkField.FAILED_TO_SAVE_LINK": "Failed to save link",
"LinkField.SAVE_RECORD_FIRST": "Cannot add links until the record has been saved"
"LinkField.SAVE_RECORD_FIRST": "Cannot add links until the record has been saved",
"LinkField.SORT_SUCCESS": "Updated link sort order",
"LinkField.SORT_ERROR": "Unable to sort links"
}
57 changes: 55 additions & 2 deletions client/src/components/LinkField/LinkField.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import React, { useState, useEffect, createContext } from 'react';
import { bindActionCreators, compose } from 'redux';
import { connect } from 'react-redux';
import { DndContext, closestCenter, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { restrictToVerticalAxis, restrictToParentElement } from '@dnd-kit/modifiers';
import { injectGraphql } from 'lib/Injector';
import fieldHolder from 'components/FieldHolder/FieldHolder';
import LinkPicker from 'components/LinkPicker/LinkPicker';
Expand Down Expand Up @@ -47,6 +50,15 @@ const LinkField = ({
const [data, setData] = useState({});
const [editingID, setEditingID] = useState(0);
const [loading, setLoading] = useState(false);
const [forceFetch, setForceFetch] = useState(0);

const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 10
}
})
);

// Ensure we have a valid array
let linkIDs = value;
Expand All @@ -61,6 +73,7 @@ const LinkField = ({

// Read data from endpoint and update component state
// This happens any time a link is added or removed and triggers a re-render

useEffect(() => {
if (!editingID && linkIDs.length > 0) {
setLoading(true);
Expand All @@ -80,7 +93,7 @@ const LinkField = ({
setLoading(false);
});
}
}, [editingID, value && value.length]);
}, [editingID, value && value.length, forceFetch]);

/**
* Unset the editing ID when the editing modal is closed
Expand Down Expand Up @@ -172,10 +185,36 @@ const LinkField = ({
return links;
};

/**
* Drag and drop handler for MultiLinkField's
*/
const handleDragEnd = (event) => {
const {active, over} = event;
if (active.id === over.id) {
return;
}
const fromIndex = linkIDs.indexOf(active.id);
const toIndex = linkIDs.indexOf(over.id);
const newLinkIDs = arrayMove(linkIDs, fromIndex, toIndex);
let endpoint = `${Config.getSection(section).form.linkForm.sortUrl}`;
// CSRF token 'X-SecurityID' headers needs to be present
backend.post(endpoint, { newLinkIDs }, { 'X-SecurityID': Config.get('SecurityID') })
.then(() => {
onChange(newLinkIDs);
actions.toasts.success(i18n._t('LinkField.SORT_SUCCESS', 'Updated link sort order'));
// Force a rerender so that links are retched so that versionState badges are up to date
setForceFetch(forceFetch + 1);
})
.catch(() => {
actions.toasts.error(i18n._t('LinkField.SORT_ERROR', 'Failed to sort links'));
});
}

const saveRecordFirst = ownerID === 0;
const renderPicker = !saveRecordFirst && (isMulti || Object.keys(data).length === 0);
const renderModal = !saveRecordFirst && Boolean(editingID);
const saveRecordFirstText = i18n._t('LinkField.SAVE_RECORD_FIRST', 'Cannot add links until the record has been saved');
const links = renderLinks();

if (loading && !saveRecordFirst) {
return <div className="link-field__loading"><Loading/></div>;
Expand All @@ -189,7 +228,21 @@ const LinkField = ({
types={types}
canCreate={canCreate}
/> }
<div> { renderLinks() } </div>
{ isMulti && <div>
<DndContext modifiers={[restrictToVerticalAxis, restrictToParentElement]}
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<SortableContext
items={linkIDs}
strategy={verticalListSortingStrategy}
>
{links}
</SortableContext>
</DndContext>
</div> }
{ !isMulti && <div>{ renderLinks() }</div>}
{ renderModal && <LinkModalContainer
types={types}
typeKey={data[editingID]?.typeKey}
Expand Down
19 changes: 18 additions & 1 deletion client/src/components/LinkPicker/LinkPicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,14 @@
}

&:hover, &:focus {
background: $gray-100;
text-decoration: none;
color: inherit;
}

&:active {
cursor: grabbing;
}

// version-state icon
&::before {
top: 29px;
Expand Down Expand Up @@ -121,6 +124,20 @@
}
}

.link-picker__drag-handle {
cursor: grab;
display: none;
left: 5px;
position: absolute;
z-index: 100;
}

.link-picker__link:hover {
.link-picker__drag-handle {
display: block;
}
}

.link-picker__link-detail {
flex-grow: 1;
width: 100%;
Expand Down
Loading

0 comments on commit 4c9c78d

Please sign in to comment.