From 6602c00b5ca76258266eacf27b9f8da07cd7e0c0 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 19 Nov 2024 11:36:05 +0100 Subject: [PATCH] IBX-9227: Restore multipleItemsLimit cehcks in UDW --- src/bundle/Resources/public/scss/_tables.scss | 4 +++ .../content-table/content.table.item.js | 13 +++----- .../components/finder/finder.leaf.js | 18 ++++------ .../components/grid-view/grid.view.item.js | 14 ++++---- .../toggle-selection/toggle.selection.js | 8 +++-- .../tree.item.toggle.selection.js | 6 ++-- .../helpers/selected.locations.helper.js | 13 ++++++++ .../hooks/useSelectedLocationsHelpers.js | 33 +++++++++++++++++++ .../universal.discovery.module.js | 18 ++++++---- 9 files changed, 90 insertions(+), 37 deletions(-) create mode 100644 src/bundle/ui-dev/src/modules/universal-discovery/helpers/selected.locations.helper.js create mode 100644 src/bundle/ui-dev/src/modules/universal-discovery/hooks/useSelectedLocationsHelpers.js diff --git a/src/bundle/Resources/public/scss/_tables.scss b/src/bundle/Resources/public/scss/_tables.scss index 43b518ef6b..91d18d16c3 100644 --- a/src/bundle/Resources/public/scss/_tables.scss +++ b/src/bundle/Resources/public/scss/_tables.scss @@ -43,6 +43,10 @@ } } } + + &--not-selectable { + cursor: not-allowed; + } } &__cell:first-child { diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.item.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.item.js index bf42a03d8b..787bc67100 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.item.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.item.js @@ -7,6 +7,7 @@ import Icon from '../../../common/icon/icon'; import { formatShortDateTime } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/timezone.helper'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; import { loadAccordionData } from '../../services/universal.discovery.service'; +import { useSelectedLocationsHelpers } from '../../hooks/useSelectedLocationsHelpers'; import { RestInfoContext, CurrentViewContext, @@ -17,8 +18,6 @@ import { ContentTypesMapContext, SelectedLocationsContext, MultipleConfigContext, - ContainersOnlyContext, - AllowedContentTypesContext, RootLocationIdContext, } from '../../universal.discovery.module'; @@ -33,12 +32,10 @@ const ContentTableItem = ({ location }) => { const [, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext); const [multiple] = useContext(MultipleConfigContext); const rootLocationId = useContext(RootLocationIdContext); - const allowedContentTypes = useContext(AllowedContentTypesContext); const contentTypeInfo = contentTypesMap[location.ContentInfo.Content.ContentType._href]; - const containersOnly = useContext(ContainersOnlyContext); - const { isContainer } = contentTypeInfo; - const isNotSelectable = - (containersOnly && !isContainer) || (allowedContentTypes && !allowedContentTypes.includes(contentTypeInfo.identifier)); + const { checkIsSelectable, checkIsSelectionBlocked } = useSelectedLocationsHelpers(); + const isNotSelectable = !checkIsSelectable(location); + const isSelectionBlocked = checkIsSelectionBlocked(location); const className = createCssClassNames({ 'ibexa-table__row c-content-table-item': true, 'c-content-table-item--marked': markedLocationId === location.id, @@ -85,7 +82,7 @@ const ContentTableItem = ({ location }) => { } }; const renderToggleSelection = () => { - return ; + return ; }; return ( diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.leaf.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.leaf.js index 045fd38639..08f1530919 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.leaf.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.leaf.js @@ -7,14 +7,13 @@ import ToggleSelection from '../toggle-selection/toggle.selection'; import Icon from '../../../common/icon/icon'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; +import { useSelectedLocationsHelpers } from '../../hooks/useSelectedLocationsHelpers'; import { MarkedLocationIdContext, LoadedLocationsMapContext, ContentTypesMapContext, SelectedLocationsContext, MultipleConfigContext, - ContainersOnlyContext, - AllowedContentTypesContext, } from '../../universal.discovery.module'; const { document } = window; @@ -23,15 +22,12 @@ const FinderLeaf = ({ location }) => { const [markedLocationId, setMarkedLocationId] = useContext(MarkedLocationIdContext); const [loadedLocationsMap, dispatchLoadedLocationsAction] = useContext(LoadedLocationsMapContext); const contentTypesMap = useContext(ContentTypesMapContext); - const [selectedLocations, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext); + const [, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext); const [multiple] = useContext(MultipleConfigContext); - const containersOnly = useContext(ContainersOnlyContext); - const allowedContentTypes = useContext(AllowedContentTypesContext); - const contentTypeInfo = contentTypesMap[location.ContentInfo.Content.ContentType._href]; - const { isContainer } = contentTypeInfo; - const isSelected = selectedLocations.some((selectedLocation) => selectedLocation.location.id === location.id); - const isNotSelectable = - (containersOnly && !isContainer) || (allowedContentTypes && !allowedContentTypes.includes(contentTypeInfo.identifier)); + const { checkIsSelectable, checkIsSelected, checkIsSelectionBlocked } = useSelectedLocationsHelpers(); + const isSelected = checkIsSelected(location); + const isNotSelectable = !checkIsSelectable(location); + const isSelectionBlocked = checkIsSelectionBlocked(location); const markLocation = ({ nativeEvent }) => { const isSelectionButtonClicked = nativeEvent.target.closest('.c-udw-toggle-selection'); const isMarkedLocationClicked = location.id === markedLocationId; @@ -53,7 +49,7 @@ const FinderLeaf = ({ location }) => { } }; const renderToggleSelection = () => { - return ; + return ; }; const className = createCssClassNames({ 'c-finder-leaf': true, diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/grid-view/grid.view.item.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/grid-view/grid.view.item.js index 8752c8c1ad..45abf7930e 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/grid-view/grid.view.item.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/grid-view/grid.view.item.js @@ -5,6 +5,7 @@ import ToggleSelection from '../toggle-selection/toggle.selection'; import Thumbnail from '../../../common/thumbnail/thumbnail'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; +import { useSelectedLocationsHelpers } from '../../hooks/useSelectedLocationsHelpers'; import { LoadedLocationsMapContext, MarkedLocationIdContext, @@ -12,7 +13,6 @@ import { SelectedLocationsContext, MultipleConfigContext, ContainersOnlyContext, - AllowedContentTypesContext, GridActiveLocationIdContext, } from '../../universal.discovery.module'; @@ -25,15 +25,15 @@ const GridViewItem = ({ location, version }) => { const [markedLocationId, setMarkedLocationId] = useContext(MarkedLocationIdContext); const [, dispatchLoadedLocationsAction] = useContext(LoadedLocationsMapContext); const contentTypesMap = useContext(ContentTypesMapContext); - const [selectedLocations, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext); + const [, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext); const [multiple] = useContext(MultipleConfigContext); const containersOnly = useContext(ContainersOnlyContext); - const allowedContentTypes = useContext(AllowedContentTypesContext); const contentTypeInfo = contentTypesMap[location.ContentInfo.Content.ContentType._href]; const { isContainer } = contentTypeInfo; - const isSelected = selectedLocations.some((selectedLocation) => selectedLocation.location.id === location.id); - const isNotSelectable = - (containersOnly && !isContainer) || (allowedContentTypes && !allowedContentTypes.includes(contentTypeInfo.identifier)); + const { checkIsSelectable, checkIsSelected, checkIsSelectionBlocked } = useSelectedLocationsHelpers(); + const isSelected = checkIsSelected(location); + const isNotSelectable = !checkIsSelectable(location); + const isSelectionBlocked = checkIsSelectionBlocked(location); const className = createCssClassNames({ 'ibexa-grid-view-item': true, 'ibexa-grid-view-item--marked': markedLocationId === location.id, @@ -68,7 +68,7 @@ const GridViewItem = ({ location, version }) => { const renderToggleSelection = () => { return (
- +
); }; diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/toggle-selection/toggle.selection.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/toggle-selection/toggle.selection.js index a61c219559..1bb40a3fce 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/toggle-selection/toggle.selection.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/toggle-selection/toggle.selection.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; import { SelectedLocationsContext } from '../../universal.discovery.module'; -const ToggleSelection = ({ multiple, location, isHidden }) => { +const ToggleSelection = ({ multiple, location, isDisabled, isHidden }) => { const [selectedLocations, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext); const isSelected = selectedLocations.some((selectedItem) => selectedItem.location.id === location.id); const className = createCssClassNames({ @@ -22,17 +22,21 @@ const ToggleSelection = ({ multiple, location, isHidden }) => { return null; } - return ; + return ( + + ); }; ToggleSelection.propTypes = { location: PropTypes.object.isRequired, multiple: PropTypes.bool.isRequired, isHidden: PropTypes.bool, + isDisabled: PropTypes.bool, }; ToggleSelection.defaultProps = { isHidden: false, + isDisabled: false, }; export default ToggleSelection; diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-item-toggle-selection/tree.item.toggle.selection.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-item-toggle-selection/tree.item.toggle.selection.js index 6fbea50985..04b21ffe80 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-item-toggle-selection/tree.item.toggle.selection.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-item-toggle-selection/tree.item.toggle.selection.js @@ -28,12 +28,14 @@ const TreeItemToggleSelection = ({ locationId, isContainer, contentTypeIdentifie } const [selectedLocations, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext); - const [multiple] = useContext(MultipleConfigContext); + const [multiple, multipleItemsLimit] = useContext(MultipleConfigContext); const containersOnly = useContext(ContainersOnlyContext); const allowedContentTypes = useContext(AllowedContentTypesContext); const restInfo = useContext(RestInfoContext); + 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 location = { id: locationId, }; @@ -49,7 +51,7 @@ const TreeItemToggleSelection = ({ locationId, isContainer, contentTypeIdentifie return ( - + {isNotSelectable &&
} ); diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/helpers/selected.locations.helper.js b/src/bundle/ui-dev/src/modules/universal-discovery/helpers/selected.locations.helper.js new file mode 100644 index 0000000000..272cfc2a87 --- /dev/null +++ b/src/bundle/ui-dev/src/modules/universal-discovery/helpers/selected.locations.helper.js @@ -0,0 +1,13 @@ +export const checkIsSelectable = ({ location, contentTypesMap, allowedContentTypes, containersOnly }) => { + const contentType = contentTypesMap[location.ContentInfo.Content.ContentType._href]; + const { isContainer, identifier } = contentType; + const isAllowedContentType = allowedContentTypes?.includes(identifier) ?? true; + + return (!containersOnly || isContainer) && isAllowedContentType; +}; + +export const checkIsSelected = ({ location, selectedLocations }) => + selectedLocations.some((selectedLocation) => selectedLocation.location.id === location.id); + +export const checkIsSelectionBlocked = ({ location, selectedLocations, multipleItemsLimit }) => + multipleItemsLimit !== 0 && selectedLocations.length >= multipleItemsLimit && !checkIsSelected({ location, selectedLocations }); diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/hooks/useSelectedLocationsHelpers.js b/src/bundle/ui-dev/src/modules/universal-discovery/hooks/useSelectedLocationsHelpers.js new file mode 100644 index 0000000000..b8400a849c --- /dev/null +++ b/src/bundle/ui-dev/src/modules/universal-discovery/hooks/useSelectedLocationsHelpers.js @@ -0,0 +1,33 @@ +import { useCallback, useContext } from 'react'; + +import { checkIsSelectable, checkIsSelected, checkIsSelectionBlocked } from '../helpers/selected.locations.helper'; +import { + AllowedContentTypesContext, + ContainersOnlyContext, + ContentTypesMapContext, + MultipleConfigContext, + SelectedLocationsContext, +} from '../universal.discovery.module'; + +export const useSelectedLocationsHelpers = () => { + const [, multipleItemsLimit] = useContext(MultipleConfigContext); + const contentTypesMap = useContext(ContentTypesMapContext); + const [selectedLocations] = useContext(SelectedLocationsContext); + const containersOnly = useContext(ContainersOnlyContext); + const allowedContentTypes = useContext(AllowedContentTypesContext); + const checkIsSelectableWrapped = useCallback( + (location) => checkIsSelectable({ location, contentTypesMap, allowedContentTypes, containersOnly }), + [contentTypesMap, allowedContentTypes, containersOnly], + ); + const checkIsSelectedWrapped = useCallback((location) => checkIsSelected({ location, selectedLocations }), [selectedLocations]); + const checkIsSelectionBlockedWrapped = useCallback( + (location) => checkIsSelectionBlocked({ location, selectedLocations, multipleItemsLimit }), + [selectedLocations, multipleItemsLimit], + ); + + return { + checkIsSelectable: checkIsSelectableWrapped, + checkIsSelected: checkIsSelectedWrapped, + checkIsSelectionBlocked: checkIsSelectionBlockedWrapped, + }; +}; diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/universal.discovery.module.js b/src/bundle/ui-dev/src/modules/universal-discovery/universal.discovery.module.js index 7c63e58203..cec9ce9c2b 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/universal.discovery.module.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/universal.discovery.module.js @@ -1,4 +1,4 @@ -import React, { useEffect, useCallback, useState, createContext, useRef } from 'react'; +import React, { useEffect, useCallback, useState, createContext, useRef, useMemo } from 'react'; import PropTypes from 'prop-types'; import Icon from '../common/icon/icon'; @@ -268,13 +268,17 @@ const UniversalDiscoveryModule = (props) => { loadContentInfo({ ...restInfo, contentId, signal }, (response) => resolve(response)); }); }; - const contentTypesMapGlobal = Object.values(adminUiConfig.contentTypes).reduce((contentTypesMap, contentTypesGroup) => { - contentTypesGroup.forEach((contentType) => { - contentTypesMap[contentType.href] = contentType; - }); + const contentTypesMapGlobal = useMemo( + () => + Object.values(adminUiConfig.contentTypes).reduce((contentTypesMap, contentTypesGroup) => { + contentTypesGroup.forEach((contentType) => { + contentTypesMap[contentType.href] = contentType; + }); - return contentTypesMap; - }, {}); + return contentTypesMap; + }, {}), + [adminUiConfig.contentTypes], + ); const onConfirm = useCallback( (selectedItems = selectedLocations) => { loadVersions().then((locationsWithVersions) => {