Skip to content

Commit e1f8423

Browse files
committed
IBX-9449: Product Picker base object selection with variants
1 parent 4355ebf commit e1f8423

File tree

8 files changed

+117
-45
lines changed

8 files changed

+117
-45
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
11
.c-selected-items-panel-item {
22
@include c-selected-items-panel-item;
3+
4+
flex-direction: column;
5+
padding: 0;
6+
overflow: hidden;
7+
8+
&__main-content,
9+
&__extra-content {
10+
width: 100%;
11+
}
12+
13+
&__main-content {
14+
display: flex;
15+
align-items: center;
16+
padding: calculateRem(8px);
17+
margin-bottom: calculateRem(8px);
18+
}
319
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
.c-selected-items-panel {
22
@include c-selected-items-panel;
3+
4+
width: calculateRem(400px);
35
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import { parse as parseMiddleEllipsis } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/middle.ellipsis';
5+
6+
const MiddleEllipsis = ({ name }) => {
7+
return (
8+
<span className="ibexa-middle-ellipsis" title={name} ref={(node) => parseMiddleEllipsis(node)}>
9+
<span className="ibexa-middle-ellipsis__name ibexa-middle-ellipsis__name--start">
10+
<span className="ibexa-middle-ellipsis__name-ellipsized">{name}</span>
11+
</span>
12+
<span className="ibexa-middle-ellipsis__separator">...</span>
13+
<span className="ibexa-middle-ellipsis__name ibexa-middle-ellipsis__name--end">
14+
<span className="ibexa-middle-ellipsis__name-ellipsized">{name}</span>
15+
</span>
16+
</span>
17+
);
18+
};
19+
20+
MiddleEllipsis.propTypes = {
21+
name: PropTypes.string.isRequired,
22+
};
23+
24+
export default MiddleEllipsis;

Diff for: src/bundle/ui-dev/src/modules/common/popup/popup.component.js

+21-14
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const Popup = ({
3232
actionBtnsConfig,
3333
size,
3434
noHeader,
35+
noFooter,
3536
noCloseBtn,
3637
extraClasses,
3738
showTooltip,
@@ -122,20 +123,24 @@ const Popup = ({
122123
</div>
123124
)}
124125
<div className="modal-body c-popup__body">{children}</div>
125-
<div className="modal-footer c-popup__footer">
126-
{actionBtnsConfig.map(({ className, onClick, disabled = false, preventClose = false, label, ...extraProps }) => (
127-
<button
128-
key={label}
129-
type="button"
130-
className={`btn ibexa-btn ${className}`}
131-
onClick={onClick ? (event) => handleOnClick(event, onClick, preventClose) : hidePopup}
132-
disabled={disabled}
133-
{...extraProps}
134-
>
135-
{label}
136-
</button>
137-
))}
138-
</div>
126+
{!noFooter && (
127+
<div className="modal-footer c-popup__footer">
128+
{actionBtnsConfig.map(
129+
({ className, onClick, disabled = false, preventClose = false, label, ...extraProps }) => (
130+
<button
131+
key={label}
132+
type="button"
133+
className={`btn ibexa-btn ${className}`}
134+
onClick={onClick ? (event) => handleOnClick(event, onClick, preventClose) : hidePopup}
135+
disabled={disabled}
136+
{...extraProps}
137+
>
138+
{label}
139+
</button>
140+
),
141+
)}
142+
</div>
143+
)}
139144
</div>
140145
</div>
141146
</div>
@@ -160,6 +165,7 @@ Popup.propTypes = {
160165
hasFocus: PropTypes.bool,
161166
size: PropTypes.string,
162167
noHeader: PropTypes.bool,
168+
noFooter: PropTypes.bool,
163169
noCloseBtn: PropTypes.bool,
164170
noKeyboard: PropTypes.bool,
165171
extraClasses: PropTypes.string,
@@ -172,6 +178,7 @@ Popup.defaultProps = {
172178
onClose: null,
173179
size: 'large',
174180
noHeader: false,
181+
noFooter: false,
175182
noCloseBtn: false,
176183
extraClasses: '',
177184
title: null,

Diff for: src/bundle/ui-dev/src/modules/universal-discovery/components/selected-items/selected.items.panel.item.js

+32-24
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { SelectedItemsContext } from '../../universal.discovery.module';
1313

1414
import { REMOVE_SELECTED_ITEMS } from '../../hooks/useSelectedItemsReducer';
1515

16-
const SelectedItemsPanelItem = ({ item, thumbnailData, name, description }) => {
16+
const SelectedItemsPanelItem = ({ item, thumbnailData, name, description, extraContent }) => {
1717
const adminUiConfig = getAdminUiConfig();
1818
const Translator = getTranslator();
1919
const refSelectedLocationsItem = useRef(null);
@@ -25,7 +25,7 @@ const SelectedItemsPanelItem = ({ item, thumbnailData, name, description }) => {
2525
);
2626
const removeFromSelection = () => {
2727
hideAllTooltips(refSelectedLocationsItem.current);
28-
dispatchSelectedItemsAction({ type: REMOVE_SELECTED_ITEMS, ids: [{ id: item.id, type: item.type }] });
28+
dispatchSelectedItemsAction({ type: REMOVE_SELECTED_ITEMS, itemsIdsWithTypes: [{ id: item.id, type: item.type }] });
2929
};
3030
const sortedActions = useMemo(() => {
3131
const { universalSelectItemActions } = adminUiConfig.universalDiscoveryWidget;
@@ -44,29 +44,32 @@ const SelectedItemsPanelItem = ({ item, thumbnailData, name, description }) => {
4444
parseTooltip(node);
4545
}}
4646
>
47-
<div className="c-selected-items-panel-item__image-wrapper">
48-
<Thumbnail thumbnailData={thumbnailData} iconExtraClasses="ibexa-icon--small" />
49-
</div>
50-
<div className="c-selected-items-panel-item__info">
51-
<span className="c-selected-items-panel-item__info-name">{name}</span>
52-
<span className="c-selected-items-panel-item__info-description">{description}</span>
53-
</div>
54-
<div className="c-selected-items-panel-item__actions-wrapper">
55-
{sortedActions.map((action) => {
56-
const Component = action.component;
47+
<div className="c-selected-items-panel-item__main-content">
48+
<div className="c-selected-items-panel-item__image-wrapper">
49+
<Thumbnail thumbnailData={thumbnailData} iconExtraClasses="ibexa-icon--small" />
50+
</div>
51+
<div className="c-selected-items-panel-item__info">
52+
<span className="c-selected-items-panel-item__info-name">{name}</span>
53+
<span className="c-selected-items-panel-item__info-description">{description}</span>
54+
</div>
55+
<div className="c-selected-items-panel-item__actions-wrapper">
56+
{sortedActions.map((action) => {
57+
const Component = action.component;
5758

58-
return <Component key={action.id} item={item} />;
59-
})}
60-
<button
61-
type="button"
62-
className="c-selected-items-panel-item__remove-button btn ibexa-btn ibexa-btn--ghost ibexa-btn--no-text"
63-
onClick={removeFromSelection}
64-
title={removeItemLabel}
65-
data-tooltip-container-selector=".c-udw-tab"
66-
>
67-
<Icon name="discard" extraClasses="ibexa-icon--tiny-small" />
68-
</button>
59+
return <Component key={action.id} item={item} />;
60+
})}
61+
<button
62+
type="button"
63+
className="c-selected-items-panel-item__remove-button btn ibexa-btn ibexa-btn--ghost ibexa-btn--no-text"
64+
onClick={removeFromSelection}
65+
title={removeItemLabel}
66+
data-tooltip-container-selector=".c-udw-tab"
67+
>
68+
<Icon name="discard" extraClasses="ibexa-icon--tiny-small" />
69+
</button>
70+
</div>
6971
</div>
72+
<div className="c-selected-items-panel-item__extra-content">{extraContent}</div>
7073
</div>
7174
);
7275
};
@@ -78,7 +81,12 @@ SelectedItemsPanelItem.propTypes = {
7881
resource: PropTypes.string.isRequired,
7982
}).isRequired,
8083
name: PropTypes.string.isRequired,
81-
description: PropTypes.string.isRequired,
84+
description: PropTypes.node.isRequired,
85+
extraContent: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
86+
};
87+
88+
SelectedItemsPanelItem.defaultProps = {
89+
extraContent: null,
8290
};
8391

8492
export default SelectedItemsPanelItem;

Diff for: src/bundle/ui-dev/src/modules/universal-discovery/components/toggle-selection/toggle.item.selection.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import React, { useContext } from 'react';
1+
import React, { useContext, useEffect, useRef } from 'react';
22
import PropTypes from 'prop-types';
33

44
import { createCssClassNames } from '../../../common/helpers/css.class.names';
55
import { MultipleConfigContext, SelectedItemsContext } from '../../universal.discovery.module';
66

7-
const ToggleItemSelection = ({ item, isDisabled, isHidden }) => {
7+
const ToggleItemSelection = ({ item, isDisabled, isHidden, isIndeterminate }) => {
88
const { selectedItems } = useContext(SelectedItemsContext);
99
const [multiple, multipleItemsLimit] = useContext(MultipleConfigContext);
10+
const inputRef = useRef(null);
1011
const isSelected = selectedItems.some((selectedItem) => selectedItem.type === item.type && selectedItem.id === item.id);
1112
const isSelectionBlocked = multipleItemsLimit !== 0 && selectedItems.length >= multipleItemsLimit && !isSelected;
1213
const className = createCssClassNames({
@@ -17,8 +18,17 @@ const ToggleItemSelection = ({ item, isDisabled, isHidden }) => {
1718
});
1819
const inputType = multiple ? 'checkbox' : 'radio';
1920

21+
useEffect(() => {
22+
if (!inputRef.current) {
23+
return;
24+
}
25+
26+
inputRef.current.indeterminate = isIndeterminate;
27+
}, [isIndeterminate]);
28+
2029
return (
2130
<input
31+
ref={inputRef}
2232
type={inputType}
2333
className={className}
2434
checked={isSelected}
@@ -32,11 +42,13 @@ ToggleItemSelection.propTypes = {
3242
item: PropTypes.object.isRequired,
3343
isHidden: PropTypes.bool,
3444
isDisabled: PropTypes.bool,
45+
isIndeterminate: PropTypes.bool,
3546
};
3647

3748
ToggleItemSelection.defaultProps = {
3849
isHidden: false,
3950
isDisabled: false,
51+
isIndeterminate: false,
4052
};
4153

4254
export default ToggleItemSelection;

Diff for: src/bundle/ui-dev/src/modules/universal-discovery/hooks/usePaginableFetch.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,16 @@ const fetchReducer = (state, action) => {
4040
}
4141
};
4242

43-
export const usePaginableFetch = ({ itemsPerPage, extraFetchParams }, fetchFunction) => {
44-
const restInfo = useContext(RestInfoContext);
43+
export const usePaginableFetch = ({ restInfo, itemsPerPage, extraFetchParams }, fetchFunction) => {
44+
const restInfoData = restInfo ?? useContext(RestInfoContext);
4545
const [state, dispatch] = useReducer(fetchReducer, fetchInitialState);
4646
const changePage = (pageIndex) => dispatch({ type: CHANGE_PAGE, pageIndex });
4747

4848
useEffect(() => {
4949
dispatch({ type: FETCH_START });
5050

5151
const offset = state.pageIndex * itemsPerPage;
52-
const { abortController } = fetchFunction({ ...restInfo, limit: itemsPerPage, offset, ...extraFetchParams }, (data) =>
52+
const { abortController } = fetchFunction({ ...restInfoData, limit: itemsPerPage, offset, ...extraFetchParams }, (data) =>
5353
dispatch({ type: FETCH_END, data }),
5454
);
5555

@@ -58,7 +58,7 @@ export const usePaginableFetch = ({ itemsPerPage, extraFetchParams }, fetchFunct
5858
abortController.abort();
5959
}
6060
};
61-
}, [state.pageIndex, restInfo, itemsPerPage, extraFetchParams]);
61+
}, [state.pageIndex, restInfoData, itemsPerPage, extraFetchParams]);
6262

6363
return [state.data, state.isLoading, state.pageIndex, changePage];
6464
};

Diff for: src/bundle/ui-dev/src/modules/universal-discovery/hooks/useSelectedItemsReducer.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ const selectedItemsReducer = (state, action) => {
3232
};
3333
}
3434
case REMOVE_SELECTED_ITEMS:
35-
return filterOutSelectedItems(action.itemsIdsWithTypes, items);
35+
return {
36+
...state,
37+
items: filterOutSelectedItems(action.itemsIdsWithTypes, items),
38+
};
3639
case TOGGLE_SELECTED_ITEMS: {
3740
const oldItemsWithoutDeselectedItems = filterOutSelectedItems(action.items, items);
3841
const newItemsWithoutDeselectedItems = filterOutSelectedItems(items, action.items);

0 commit comments

Comments
 (0)