Skip to content

Commit

Permalink
IBX-9227: Restore multipleItemsLimit cehcks in UDW
Browse files Browse the repository at this point in the history
  • Loading branch information
tischsoic committed Nov 20, 2024
1 parent eafdc92 commit 6602c00
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 37 deletions.
4 changes: 4 additions & 0 deletions src/bundle/Resources/public/scss/_tables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
}
}
}

&--not-selectable {
cursor: not-allowed;
}
}

&__cell:first-child {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -17,8 +18,6 @@ import {
ContentTypesMapContext,
SelectedLocationsContext,
MultipleConfigContext,
ContainersOnlyContext,
AllowedContentTypesContext,
RootLocationIdContext,
} from '../../universal.discovery.module';

Expand All @@ -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,
Expand Down Expand Up @@ -85,7 +82,7 @@ const ContentTableItem = ({ location }) => {
}
};
const renderToggleSelection = () => {
return <ToggleSelection location={location} multiple={multiple} isHidden={isNotSelectable} />;
return <ToggleSelection location={location} multiple={multiple} isDisabled={isSelectionBlocked} isHidden={isNotSelectable} />;
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -53,7 +49,7 @@ const FinderLeaf = ({ location }) => {
}
};
const renderToggleSelection = () => {
return <ToggleSelection location={location} multiple={multiple} isHidden={isNotSelectable} />;
return <ToggleSelection location={location} multiple={multiple} isDisabled={isSelectionBlocked} isHidden={isNotSelectable} />;
};
const className = createCssClassNames({
'c-finder-leaf': true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ 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,
ContentTypesMapContext,
SelectedLocationsContext,
MultipleConfigContext,
ContainersOnlyContext,
AllowedContentTypesContext,
GridActiveLocationIdContext,
} from '../../universal.discovery.module';

Expand All @@ -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,
Expand Down Expand Up @@ -68,7 +68,7 @@ const GridViewItem = ({ location, version }) => {
const renderToggleSelection = () => {
return (
<div className="ibexa-grid-view-item__checkbox">
<ToggleSelection location={location} multiple={multiple} isHidden={isNotSelectable} />
<ToggleSelection location={location} multiple={multiple} isDisabled={isSelectionBlocked} isHidden={isNotSelectable} />
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -22,17 +22,21 @@ const ToggleSelection = ({ multiple, location, isHidden }) => {
return null;
}

return <input type="checkbox" className={className} checked={isSelected} disabled={isHidden} onChange={toggleSelection} />;
return (
<input type="checkbox" className={className} checked={isSelected} disabled={isDisabled || isHidden} onChange={toggleSelection} />
);
};

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;
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -49,7 +51,7 @@ const TreeItemToggleSelection = ({ locationId, isContainer, contentTypeIdentifie

return (
<SelectedLocationsContext.Provider value={[selectedLocations, dispatchSelectedLocationsActionWrapper]}>
<ToggleSelection location={location} multiple={multiple} isHidden={isNotSelectable} />
<ToggleSelection location={location} multiple={multiple} isDisabled={isSelectionBlocked} 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
@@ -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 });
Original file line number Diff line number Diff line change
@@ -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,
};
};
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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) => {
Expand Down

0 comments on commit 6602c00

Please sign in to comment.