Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IBX-2925: [D&D] Functionality and design improvements #435

Merged
merged 23 commits into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 149 additions & 45 deletions src/bundle/Resources/public/js/scripts/admin.contenttype.edit.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
(function (global, doc, ibexa, Routing, Translator) {
(function (global, doc, ibexa, Routing, Translator, bootstrap) {
const SELECTOR_INPUTS_TO_VALIDATE = '.ibexa-input[required]:not([disabled]):not([hidden])';
let targetContainer = null;
const TIMEOUT_REMOVE_HIGHLIGHT = 3000;
let sourceContainer = null;
let currentDraggedItem = null;
let draggedItemPosition = null;
let isEditFormValid = false;
const editForm = doc.querySelector('.ibexa-content-type-edit-form');
let inputsToValidate = editForm.querySelectorAll(SELECTOR_INPUTS_TO_VALIDATE);
Expand All @@ -14,6 +13,7 @@
const filterFieldInput = doc.querySelector('.ibexa-available-field-types__sidebar-filter');
const popupMenuElement = sectionsNode.querySelector('.ibexa-popup-menu');
const addGroupTriggerBtn = sectionsNode.querySelector('.ibexa-content-type-edit__add-field-definitions-group-btn');
const fieldDefinitionsGroups = doc.querySelectorAll('.ibexa-collapse--field-definitions-group');
const noFieldsAddedError = Translator.trans(
/*@Desc("You have to add at least one field definition")*/ 'content_type.edit.error.no_added_fields_definition',
{},
Expand Down Expand Up @@ -68,13 +68,11 @@
});
};
const removeDragPlaceholders = () => {
const placeholderNodes = doc.querySelectorAll(
'.ibexa-field-definitions-placeholder:not(.ibexa-field-definitions-placeholder--anchored)',
);
const placeholderNodes = doc.querySelectorAll('.ibexa-field-definitions-placeholder');

placeholderNodes.forEach((placeholderNode) => placeholderNode.remove());
};
const createFieldDefinitionNode = (fieldNode) => {
const createFieldDefinitionNode = (fieldNode, { targetContainer, draggedItemPosition }) => {
let targetPlace = '';
const items = targetContainer.querySelectorAll('.ibexa-collapse');

Expand All @@ -86,19 +84,19 @@
}

if (draggedItemPosition === -1) {
targetPlace = targetContainer.querySelector('.ibexa-field-definitions-placeholder--anchored');
targetPlace = targetContainer.lastChild;
} else if (draggedItemPosition === 0) {
targetPlace = targetContainer.firstChild;
} else {
targetPlace = [...items].find((item, index) => index === draggedItemPosition);
}

fieldNode.classList.add('ibexa-collapse--field-definition-highlight');
fieldNode.classList.add('ibexa-collapse--field-definition-highlighted');
targetContainer.insertBefore(fieldNode, targetPlace);

return fieldNode;
};
const attachFieldDefinitionNodeEvents = (fieldNode) => {
const attachFieldDefinitionNodeEvents = (fieldNode, { targetContainer }) => {
const fieldGroupInput = fieldNode.querySelector('.ibexa-input--field-group');
const removeFieldsBtn = fieldNode.querySelectorAll('.ibexa-collapse__extra-action-button--remove-field-definitions');
const fieldInputsToValidate = fieldNode.querySelectorAll(SELECTOR_INPUTS_TO_VALIDATE);
Expand Down Expand Up @@ -133,11 +131,11 @@
}),
);
};
const insertFieldDefinitionNode = (fieldNode) => {
const fieldDefinitionNode = createFieldDefinitionNode(fieldNode);
const insertFieldDefinitionNode = (fieldNode, { targetContainer, draggedItemPosition }) => {
const fieldDefinitionNode = createFieldDefinitionNode(fieldNode, { targetContainer, draggedItemPosition });

removeDragPlaceholders();
attachFieldDefinitionNodeEvents(fieldDefinitionNode);
attachFieldDefinitionNodeEvents(fieldDefinitionNode, { targetContainer });
dispatchInsertFieldDefinitionNode(fieldDefinitionNode);

return fieldDefinitionNode;
Expand Down Expand Up @@ -171,11 +169,9 @@
groups.forEach((group) => {
const groupFieldsDefinitionCount = group.querySelectorAll('.ibexa-collapse--field-definition').length;
const emptyGroupPlaceholder = group.querySelector('.ibexa-field-definitions-empty-group');
const anchoredPlaceholder = group.querySelector('.ibexa-field-definitions-placeholder--anchored');
const removeBtn = group.querySelector('.ibexa-collapse__extra-action-button--remove-field-definitions-group');

emptyGroupPlaceholder.classList.toggle('ibexa-field-definitions-empty-group--hidden', groupFieldsDefinitionCount !== 0);
anchoredPlaceholder.classList.toggle('ibexa-field-definitions-placeholder--hidden', groupFieldsDefinitionCount === 0);
removeBtn.disabled = groupFieldsDefinitionCount > 0;
});

Expand All @@ -187,12 +183,16 @@
});

doc.querySelectorAll('.ibexa-collapse--field-definition').forEach((fieldDefinition, index) => {
fieldDefinition.querySelector('.ibexa-input--position').value = index;
const positionInput = fieldDefinition.querySelector('.ibexa-input--position');

if (positionInput) {
fieldDefinition.querySelector('.ibexa-input--position').value = index;
}
});
};
const addField = () => {
const addField = ({ targetContainer, draggedItemPosition }) => {
if (!sourceContainer.classList.contains('ibexa-available-field-types__list')) {
insertFieldDefinitionNode(currentDraggedItem);
insertFieldDefinitionNode(currentDraggedItem, { targetContainer, draggedItemPosition });
afterChangeGroup();

return;
Expand All @@ -216,15 +216,19 @@
fetch(generateRequest('add', bodyData, languageCode))
.then(ibexa.helpers.request.getTextFromResponse)
.then((response) => {
insertFieldDefinitionNode(response);
removeLoadingField();
insertFieldDefinitionNode(response, { targetContainer, draggedItemPosition });
afterChangeGroup();
})
.catch(ibexa.helpers.notification.showErrorNotification);
};
const reorderFields = () => {
createFieldDefinitionNode(currentDraggedItem);
const reorderFields = ({ targetContainer, draggedItemPosition }) => {
createFieldDefinitionNode(currentDraggedItem, { targetContainer, draggedItemPosition });
removeDragPlaceholders();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

currentDraggedItem.classList.add('ibexa-collapse--field-definition-highlighted');
currentDraggedItem.classList.add('ibexa-collapse--field-definition-loading');

const fieldsOrder = [...doc.querySelectorAll('.ibexa-collapse--field-definition')].map(
(fieldDefinition) => fieldDefinition.dataset.fieldDefinitionIdentifier,
);
Expand All @@ -237,7 +241,10 @@

fetch(request)
.then(ibexa.helpers.request.getTextFromResponse)
.then(() => afterChangeGroup())
.then(() => {
currentDraggedItem.classList.remove('ibexa-collapse--field-definition-loading');
afterChangeGroup();
})
.catch(ibexa.helpers.notification.showErrorNotification);
};
const removeFieldsGroup = (event) => {
Expand Down Expand Up @@ -276,11 +283,21 @@
},
};

collapseNode.classList.add('ibexa-collapse--field-definition-removing');
bootstrap.Collapse.getOrCreateInstance(collapseNode.querySelector('.ibexa-collapse__body'), {
toggle: false,
}).hide();
event.currentTarget.blur();

fetch(generateRequest('remove', bodyData))
.then(ibexa.helpers.request.getTextFromResponse)
.then(() => {
collapseNode.remove();
afterChangeGroup();
collapseNode.classList.add('ibexa-collapse--field-definition-remove-animation');

collapseNode.addEventListener('animationend', () => {
collapseNode.remove();
afterChangeGroup();
});
})
.catch(ibexa.helpers.notification.showErrorNotification);
};
Expand Down Expand Up @@ -343,32 +360,76 @@

scrollToNode.scrollIntoView({ behavior: 'smooth' });
};
const setActiveGroup = (group) => {
const currentActiveGroup = doc.querySelector(
'.ibexa-collapse--field-definitions-group.ibexa-collapse--active-field-definitions-group',
);

currentActiveGroup?.classList.remove('ibexa-collapse--active-field-definitions-group');
group.classList.add('ibexa-collapse--active-field-definitions-group');
};
const removeLoadingField = () => {
const field = doc.querySelector('.ibexa-collapse--field-definition-loading');

field.remove();
};
const removeHighlight = () => {
const field = doc.querySelector('.ibexa-collapse--field-definition-highlighted');

field?.classList.remove('ibexa-collapse--field-definition-highlighted');
};
class FieldDefinitionDraggable extends ibexa.core.Draggable {
constructor(config) {
super(config);

this.emptyContainer = this.itemsContainer.querySelector('.ibexa-field-definitions-empty-group');

this.getPlaceholderNode = this.getPlaceholderNode.bind(this);
this.getPlaceholderPositionTop = this.getPlaceholderPositionTop.bind(this);
}
GrabowskiM marked this conversation as resolved.
Show resolved Hide resolved

onDrop(event) {
targetContainer = event.currentTarget;

const dragContainerItems = targetContainer.querySelectorAll(
'.ibexa-collapse--field-definition, .ibexa-field-definitions-placeholder:not(.ibexa-field-definitions-placeholder--anchored)',
);
const currentActiveGroup = doc.querySelector(
'.ibexa-collapse--field-definitions-group.ibexa-collapse--active-field-definitions-group',
);
const targetContainer = event.currentTarget;
const dragContainerItems = targetContainer.querySelectorAll('.ibexa-collapse--field-definition');
const targetContainerGroup = targetContainer.closest('.ibexa-collapse--field-definitions-group');

draggedItemPosition = [...dragContainerItems].findIndex((item, index, array) => {
const targetContainerList = targetContainerGroup.closest('.ibexa-content-type-edit__field-definitions-group-list');
const fieldTemplate = targetContainerList.dataset.template;
const fieldRendered = fieldTemplate.replace('{{ type }}', currentDraggedItem.dataset.itemIdentifier);
let draggedItemPosition = [...dragContainerItems].findIndex((item, index, array) => {
return item.classList.contains('ibexa-field-definitions-placeholder') && index < array.length - 1;
});

if (draggedItemPosition === -1) {
draggedItemPosition = targetContainer.querySelectorAll('.ibexa-collapse--field-definition').length;
}

if (sourceContainer.isEqualNode(targetContainer)) {
reorderFields();
reorderFields({ targetContainer, draggedItemPosition });
} else {
addField();
createFieldDefinitionNode(fieldRendered, { targetContainer, draggedItemPosition });
addField({ targetContainer, draggedItemPosition });
}

currentActiveGroup?.classList.remove('ibexa-collapse--active-field-definitions-group');
targetContainerGroup.classList.add('ibexa-collapse--active-field-definitions-group');

setActiveGroup(targetContainerGroup);
removeDragPlaceholders();

global.setTimeout(() => {
removeHighlight();
}, TIMEOUT_REMOVE_HIGHLIGHT);
}

getPlaceholderNode(event) {
const draggableItem = super.getPlaceholderNode(event);

if (draggableItem) {
return draggableItem;
}

if (this.emptyContainer.contains(event.target)) {
return this.emptyContainer;
}

return null;
}

onDragStart(event) {
Expand All @@ -378,9 +439,39 @@
sourceContainer = currentDraggedItem.parentNode;
}

onDragOver(event) {
const isDragSuccessful = super.onDragOver(event);

if (!isDragSuccessful) {
return false;
}

const item = this.getPlaceholderNode(event);

if (item.isSameNode(this.emptyContainer)) {
this.emptyContainer.classList.toggle('ibexa-field-definitions-empty-group--hidden');
}

return true;
}

onDragEnd() {
currentDraggedItem.style.removeProperty('display');
}

init() {
super.init();

doc.body.addEventListener('dragover', (event) => {
if (!this.itemsContainer.contains(event.target)) {
const groupFieldsDefinitionCount = this.itemsContainer.querySelectorAll('.ibexa-collapse--field-definition').length;

this.emptyContainer.classList.toggle('ibexa-field-definitions-empty-group--hidden', groupFieldsDefinitionCount !== 0);
} else {
event.preventDefault();
}
});
}
}

filterFieldInput.addEventListener('keyup', searchField, false);
Expand Down Expand Up @@ -426,20 +517,32 @@
availableField.addEventListener(
'click',
(event) => {
const activeTargetContainer = doc.querySelector(
const targetContainer = doc.querySelector(
'.ibexa-collapse--field-definitions-group.ibexa-collapse--active-field-definitions-group .ibexa-content-type-edit__field-definition-drop-zone',
);

if (!activeTargetContainer) {
if (!targetContainer) {
return;
}

currentDraggedItem = event.currentTarget;
sourceContainer = currentDraggedItem.parentNode;
draggedItemPosition = -1;
targetContainer = activeTargetContainer;

addField();
const draggedItemPosition = targetContainer.querySelectorAll('.ibexa-collapse--field-definition').length;
const targetContainerGroup = targetContainer.closest('.ibexa-collapse--field-definitions-group');
const targetContainerList = targetContainerGroup.closest('.ibexa-content-type-edit__field-definitions-group-list');
const fieldTemplate = targetContainerList.dataset.template;
const fieldRendered = fieldTemplate.replace('{{ type }}', currentDraggedItem.dataset.itemIdentifier);

targetContainer
.querySelector('.ibexa-field-definitions-empty-group')
.classList.add('ibexa-field-definitions-empty-group--hidden');
createFieldDefinitionNode(fieldRendered, { targetContainer, draggedItemPosition });
addField({ targetContainer, draggedItemPosition });

global.setTimeout(() => {
removeHighlight();
}, TIMEOUT_REMOVE_HIGHLIGHT);
},
false,
);
Expand All @@ -456,6 +559,7 @@
draggableGroups.push(draggable);
});

fieldDefinitionsGroups.forEach((group) => group.addEventListener('click', () => setActiveGroup(group), false));
inputsToValidate.forEach(attachValidateEvents);

editForm.addEventListener(
Expand Down Expand Up @@ -483,4 +587,4 @@
);

toggleAddGroupTriggerBtnState();
})(window, window.document, window.ibexa, window.Routing, window.Translator);
})(window, window.document, window.ibexa, window.Routing, window.Translator, window.bootstrap);
Loading