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-9322: ezobjectrelationlist field allows selecting the same content multiple times #1409

Open
wants to merge 3 commits into
base: 4.6
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
const itemNodeNameCell = itemNode.querySelector('.ibexa-relations__item-name');

itemNode.dataset.contentId = contentId;
itemNode.dataset.locationId = locationId;
itemNode.querySelector('.ibexa-relations__table-action--remove-item').addEventListener('click', removeItem, false);

itemNodeNameCell.dataset.ibexaUpdateContentId = contentId;
Expand All @@ -113,12 +114,12 @@
sourceInput.dispatchEvent(new CustomEvent(EVENT_CUSTOM));
};
const onConfirm = (items) => {
items = excludeDuplicatedItems(items);
const itemsWithoutDuplicate = excludeDuplicatedItems(items);

renderRows(items);
renderRows(itemsWithoutDuplicate);
attachRowsEventHandlers();

selectedItems = [...selectedItems, ...items.map((item) => item.ContentInfo.Content._id)];
selectedItems = [...selectedItems, ...itemsWithoutDuplicate.map((item) => item.ContentInfo.Content._id)];

updateInputValue(selectedItems);
closeUDW();
Expand All @@ -128,9 +129,15 @@
};
const openUDW = (event) => {
event.preventDefault();

const selectedItemsRow = fieldContainer.querySelectorAll(SELECTOR_ROW);
const config = JSON.parse(event.currentTarget.dataset.udwConfig);
const limit = parseInt(event.currentTarget.dataset.limit, 10);
const selectedLocations = [...selectedItemsRow].reduce((locationsIds, selectedItemRow) => {
const { locationId } = selectedItemRow.dataset;
const parsedLocationId = parseInt(locationId, 10);

return isNaN(parsedLocationId) ? locationsIds : [...locationsIds, parsedLocationId];
}, []);
const title =
limit === 1
? Translator.trans(
Expand All @@ -151,17 +158,15 @@
onCancel: closeUDW,
title,
startingLocationId,
selectedLocations,
isInitLocationsDeselectionBlocked: true,
...config,
multiple: isSingle ? false : selectedItemsLimit !== 1,
multipleItemsLimit: selectedItemsLimit > 1 ? selectedItemsLimit - selectedItems.length : selectedItemsLimit,
}),
);
};
const excludeDuplicatedItems = (items) => {
selectedItemsMap = items.reduce((total, item) => ({ ...total, [item.ContentInfo.Content._id]: item }), selectedItemsMap);

return items.filter((item) => selectedItemsMap[item.ContentInfo.Content._id]);
};
const excludeDuplicatedItems = (items) => items.filter((item) => !selectedItems.includes(item.ContentInfo.Content._id));
const renderRow = (item, index) => {
const { escapeHTML } = ibexa.helpers.text;
const { formatShortDateTime } = ibexa.helpers.timezone;
Expand Down Expand Up @@ -304,7 +309,6 @@
updateInputValue(selectedItems);
};
let selectedItems = [...fieldContainer.querySelectorAll(SELECTOR_ROW)].map((row) => parseInt(row.dataset.contentId, 10));
let selectedItemsMap = selectedItems.reduce((total, item) => ({ ...total, [item]: item }), {});

updateAddBtnState();
attachRowsEventHandlers();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,11 @@
.ibexa-icon {
fill: $ibexa-color-dark;
}

&:disabled {
.ibexa-icon {
fill: $ibexa-color-dark-300;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,10 @@
{% set body_rows = body_rows|merge([{
cols: body_row_cols,
class: 'ibexa-relations__item',
attr: { 'data-content-id': relation.contentInfo.id },
attr: {
'data-content-id': relation.contentInfo.id,
'data-location-id': relation.contentInfo.mainLocationId,
},
}]) %}
{% elseif relation.unauthorized %}
{% set col_raw_unauthorised %}
Expand All @@ -194,7 +197,10 @@
{ content: col_raw_order_input, raw: true, attr: { class: 'ibexa-relations__order-wrapper' } }
],
class: 'ibexa-relations__item',
attr: { 'data-content-id': relation.contentId },
attr: {
'data-content-id': relation.contentId,
'data-location-id': relation.contentInfo.mainLocationId,
},
}]) %}
{% endif %}
{% endfor %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ const FinderLeaf = ({ location }) => {
const contentTypesMap = useContext(ContentTypesMapContext);
const [, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext);
const [multiple] = useContext(MultipleConfigContext);
const { checkIsSelectable, checkIsSelected, checkIsSelectionBlocked } = useSelectedLocationsHelpers();
const { checkIsSelectable, checkIsSelected, checkIsSelectionBlocked, checkIsDeselectionBlocked } = useSelectedLocationsHelpers();
const isSelected = checkIsSelected(location);
const isNotSelectable = !checkIsSelectable(location);
const isSelectionBlocked = checkIsSelectionBlocked(location);
const isDeselectionBlocked = checkIsDeselectionBlocked(location);
const markLocation = ({ nativeEvent }) => {
const isSelectionButtonClicked = nativeEvent.target.closest('.c-udw-toggle-selection');
const isMarkedLocationClicked = location.id === markedLocationId;
Expand All @@ -49,7 +50,14 @@ const FinderLeaf = ({ location }) => {
}
};
const renderToggleSelection = () => {
return <ToggleSelection location={location} multiple={multiple} isDisabled={isSelectionBlocked} isHidden={isNotSelectable} />;
return (
<ToggleSelection
location={location}
multiple={multiple}
isDisabled={isSelectionBlocked || isDeselectionBlocked}
isHidden={isNotSelectable}
/>
);
};
const className = createCssClassNames({
'c-finder-leaf': true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ const GridViewItem = ({ location, version }) => {
const containersOnly = useContext(ContainersOnlyContext);
const contentTypeInfo = contentTypesMap[location.ContentInfo.Content.ContentType._href];
const { isContainer } = contentTypeInfo;
const { checkIsSelectable, checkIsSelected, checkIsSelectionBlocked } = useSelectedLocationsHelpers();
const { checkIsSelectable, checkIsSelected, checkIsSelectionBlocked, checkIsDeselectionBlocked } = useSelectedLocationsHelpers();
const isSelected = checkIsSelected(location);
const isNotSelectable = !checkIsSelectable(location);
const isSelectionBlocked = checkIsSelectionBlocked(location);
const isDeselectionBlocked = checkIsDeselectionBlocked(location);
const className = createCssClassNames({
'ibexa-grid-view-item': true,
'ibexa-grid-view-item--marked': markedLocationId === location.id,
Expand Down Expand Up @@ -68,7 +69,12 @@ const GridViewItem = ({ location, version }) => {
const renderToggleSelection = () => {
return (
<div className="ibexa-grid-view-item__checkbox">
<ToggleSelection location={location} multiple={multiple} isDisabled={isSelectionBlocked} isHidden={isNotSelectable} />
<ToggleSelection
location={location}
multiple={multiple}
isDisabled={isSelectionBlocked || isDeselectionBlocked}
isHidden={isNotSelectable}
/>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ import Thumbnail from '../../../common/thumbnail/thumbnail';

import { SelectedLocationsContext, ContentTypesMapContext } from '../../universal.discovery.module';
import { getAdminUiConfig, getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper';
import { useSelectedLocationsHelpers } from '../../hooks/useSelectedLocationsHelpers';

const SelectedLocationsItem = ({ location, permissions }) => {
const adminUiConfig = getAdminUiConfig();
const Translator = getTranslator();
const refSelectedLocationsItem = useRef(null);
const [, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext);
const { checkIsDeselectionBlocked } = useSelectedLocationsHelpers();
const isDeselectionBlocked = checkIsDeselectionBlocked(location);
const contentTypesMap = useContext(ContentTypesMapContext);
const clearLabel = Translator.trans(
/*@Desc("Clear selection")*/ 'selected_locations.clear_selection',
Expand Down Expand Up @@ -65,6 +68,7 @@ const SelectedLocationsItem = ({ location, permissions }) => {
onClick={removeFromSelection}
title={clearLabel}
data-tooltip-container-selector=".c-udw-tab"
disabled={isDeselectionBlocked}
>
<Icon name="discard" extraClasses="ibexa-icon--tiny-small" />
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { parse as parseTooltip } from '@ibexa-admin-ui/src/bundle/Resources/publ

import {
UDWContext,
SelectionConfigContext,
SelectedLocationsContext,
RestInfoContext,
MultipleConfigContext,
Expand All @@ -23,19 +24,23 @@ const TreeItemToggleSelection = ({ locationId, isContainer, contentTypeIdentifie
parseTooltip(document.querySelector('.c-list'));
}, []);

if (!isUDW) {
return null;
}

const { isInitLocationsDeselectionBlocked, initSelectedLocations } = useContext(SelectionConfigContext);
const [selectedLocations, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext);
const [multiple, multipleItemsLimit] = useContext(MultipleConfigContext);
const containersOnly = useContext(ContainersOnlyContext);
const allowedContentTypes = useContext(AllowedContentTypesContext);
const restInfo = useContext(RestInfoContext);

if (!isUDW) {
return null;
}

const isSelected = selectedLocations.some((selectedLocation) => selectedLocation.location.id === locationId);
const isNotSelectable =
(containersOnly && !isContainer) || (allowedContentTypes && !allowedContentTypes.includes(contentTypeIdentifier));
const isSelectionBlocked = multipleItemsLimit !== 0 && !isSelected && selectedLocations.length >= multipleItemsLimit;
const isInitSelectedLocation = initSelectedLocations.includes(locationId);
const isDeselectionBlocked = isSelected && isInitSelectedLocation && isInitLocationsDeselectionBlocked;
const location = {
id: locationId,
};
Expand All @@ -51,7 +56,12 @@ const TreeItemToggleSelection = ({ locationId, isContainer, contentTypeIdentifie

return (
<SelectedLocationsContext.Provider value={[selectedLocations, dispatchSelectedLocationsActionWrapper]}>
<ToggleSelection location={location} multiple={multiple} isDisabled={isSelectionBlocked} isHidden={isNotSelectable} />
<ToggleSelection
location={location}
multiple={multiple}
isDisabled={isSelectionBlocked || isDeselectionBlocked}
isHidden={isNotSelectable}
/>
{isNotSelectable && <div className="c-list-item__prefix-actions-item-empty" />}
</SelectedLocationsContext.Provider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import {
ContentTypesMapContext,
MultipleConfigContext,
SelectedLocationsContext,
SelectionConfigContext,
} from '../universal.discovery.module';

export const useSelectedLocationsHelpers = () => {
const [, multipleItemsLimit] = useContext(MultipleConfigContext);
const contentTypesMap = useContext(ContentTypesMapContext);
const [selectedLocations] = useContext(SelectedLocationsContext);
const { isInitLocationsDeselectionBlocked, initSelectedLocations } = useContext(SelectionConfigContext);
const containersOnly = useContext(ContainersOnlyContext);
const allowedContentTypes = useContext(AllowedContentTypesContext);
const checkIsSelectableWrapped = useCallback(
Expand All @@ -24,10 +26,17 @@ export const useSelectedLocationsHelpers = () => {
(location) => checkIsSelectionBlocked({ location, selectedLocations, multipleItemsLimit }),
[selectedLocations, multipleItemsLimit],
);
const checkIsDeselectionBlockedWrapped = (location) => {
const isLocationSelected = checkIsSelected({ location, selectedLocations });
const isInitSelectedLocation = initSelectedLocations.includes(location.id);

return isLocationSelected && isInitSelectedLocation && isInitLocationsDeselectionBlocked;
};

return {
checkIsSelectable: checkIsSelectableWrapped,
checkIsSelected: checkIsSelectedWrapped,
checkIsSelectionBlocked: checkIsSelectionBlockedWrapped,
checkIsDeselectionBlocked: checkIsDeselectionBlockedWrapped,
};
};
Loading
Loading