Skip to content

Commit 4f10d2d

Browse files
authored
IBX-7184: [Sub-items] For lower resolution list header actions ale not fully visible (#1024)
1 parent 260a0b5 commit 4f10d2d

File tree

3 files changed

+167
-16
lines changed

3 files changed

+167
-16
lines changed

src/bundle/Resources/public/scss/ui/modules/sub-items-list/_main.scss

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,34 @@
1313
padding-bottom: calculateRem(20px);
1414
border-bottom: calculateRem(1px) solid $ibexa-color-dark-200;
1515
margin-top: 0;
16+
17+
&__headline {
18+
overflow: initial;
19+
}
20+
21+
&__actions {
22+
&--adaptive {
23+
overflow: hidden;
24+
flex-grow: 1;
25+
justify-content: flex-end;
26+
27+
.ibexa-adaptive-items__item--hidden {
28+
display: none;
29+
}
30+
}
31+
}
32+
33+
.c-simple-dropdown {
34+
&:hover {
35+
.c-simple-dropdown__selected {
36+
color: $ibexa-color-primary;
37+
38+
.ibexa-icon {
39+
fill: $ibexa-color-primary;
40+
}
41+
}
42+
}
43+
}
1644
}
1745

1846
&__list {

src/bundle/ui-dev/src/modules/multi-file-upload/multi.file.upload.module.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import React, { Component } from 'react';
2+
import ReactDOM from 'react-dom';
23
import PropTypes from 'prop-types';
34

45
import UploadPopupComponent from './components/upload-popup/upload.popup.component';
56
import { createFileStruct, publishFile, deleteFile, checkCanUpload } from './services/multi.file.upload.service';
67
import Icon from '../common/icon/icon';
78

8-
const { Translator, ibexa } = window;
9+
const { Translator, ibexa, document } = window;
910

1011
export default class MultiFileUploadModule extends Component {
1112
constructor(props) {
@@ -270,7 +271,7 @@ export default class MultiFileUploadModule extends Component {
270271
removeItemsToUpload: this.removeItemsToUpload,
271272
};
272273

273-
return <UploadPopupComponent {...attrs} />;
274+
return ReactDOM.createPortal(<UploadPopupComponent {...attrs} />, document.body);
274275
}
275276

276277
render() {

src/bundle/ui-dev/src/modules/sub-items/sub.items.module.js

Lines changed: 136 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ import Icon from '../common/icon/icon.js';
1414
import PaginationInfo from '../common/pagination/pagination.info.js';
1515

1616
import deepClone from '../common/helpers/deep.clone.helper.js';
17+
import { createCssClassNames } from '../common/helpers/css.class.names';
1718
import { updateLocationPriority, loadLocation as loadLocationService } from './services/sub.items.service';
1819
import { bulkAddLocations, bulkDeleteItems, bulkHideLocations, bulkUnhideLocations, bulkMoveLocations } from './services/bulk.service.js';
1920

20-
const { Translator, ibexa } = window;
21+
const { Translator, ibexa, Popper, document } = window;
2122

2223
export const ASCENDING_SORT_ORDER = 'ascending';
2324
const DESCENDING_SORT_ORDER = 'descending';
@@ -93,9 +94,17 @@ export default class SubItemsModule extends Component {
9394
this.resizeSubItems = this.resizeSubItems.bind(this);
9495
this.setColumnsVisibilityInLocalStorage = this.setColumnsVisibilityInLocalStorage.bind(this);
9596
this.toggleColumnVisibility = this.toggleColumnVisibility.bind(this);
97+
this.adaptHeaderActions = this.adaptHeaderActions.bind(this);
98+
this.showMorePanel = this.showMorePanel.bind(this);
99+
this.hideMorePanel = this.hideMorePanel.bind(this);
100+
this.renderExtraActions = this.renderExtraActions.bind(this);
101+
this.renderActionBtnWrapper = this.renderActionBtnWrapper.bind(this);
96102

97103
this._refListViewWrapper = React.createRef();
98104
this._refMainContainerWrapper = React.createRef();
105+
this._refAdaptiveItemsWrapper = React.createRef();
106+
this._refAdaptiveItemMoreBtn = React.createRef();
107+
this._refAdaptiveItemMorePanel = React.createRef();
99108
this.bulkActionModalContainer = null;
100109
this.udwContainer = null;
101110
this.adminUiConfig = getAdminUiConfig();
@@ -121,6 +130,8 @@ export default class SubItemsModule extends Component {
121130
sortOrder: sortClauseData.order,
122131
subItemsWidth: this.calculateSubItemsWidth(),
123132
columnsVisibility: this.getColumnsVisibilityFromLocalStorage(),
133+
morePanelVisible: false,
134+
morePanelVisibleItemsIndexes: [],
124135
};
125136
}
126137

@@ -146,6 +157,15 @@ export default class SubItemsModule extends Component {
146157
if (!this.state.activePageItems) {
147158
this.loadPage(0);
148159
}
160+
161+
this.adaptHeaderActions();
162+
163+
const subitemsTab = this._refMainContainerWrapper.current.closest('.ibexa-tab-content__pane');
164+
const subitemsNavTab = document.querySelector(`.ibexa-tabs__link[href="#${subitemsTab.id}"]`);
165+
166+
subitemsNavTab.addEventListener('shown.bs.tab', () => {
167+
this.popperInstance.forceUpdate();
168+
});
149169
}
150170

151171
componentDidUpdate() {
@@ -180,6 +200,7 @@ export default class SubItemsModule extends Component {
180200
const { subItemsWidth } = this.state;
181201

182202
if (calculatedWidth !== subItemsWidth) {
203+
this.popperInstance.forceUpdate();
183204
this.setState({ subItemsWidth: calculatedWidth });
184205
}
185206
}
@@ -1120,7 +1141,7 @@ export default class SubItemsModule extends Component {
11201141
renderExtraActions(action, index) {
11211142
const Action = action.component;
11221143

1123-
return <Action key={index} className="m-sub-items__action" {...action.attrs} />;
1144+
return this.renderActionBtnWrapper(<Action {...action.attrs} />, 'm-sub-items__action', { key: index });
11241145
}
11251146

11261147
/**
@@ -1169,34 +1190,46 @@ export default class SubItemsModule extends Component {
11691190
);
11701191
}
11711192

1193+
renderActionBtnWrapper(btn, extraClasses = '', extraProps = {}) {
1194+
return (
1195+
<div className={`ibexa-adaptive-items__item ${extraClasses}`} {...extraProps}>
1196+
{btn}
1197+
</div>
1198+
);
1199+
}
1200+
11721201
renderBulkMoveBtn(disabled) {
11731202
const label = Translator.trans(/*@Desc("Move")*/ 'move_btn.label', {}, 'ibexa_sub_items');
11741203

1175-
return <ActionButton disabled={disabled} onClick={this.onMoveBtnClick} label={label} type="move" />;
1204+
return this.renderActionBtnWrapper(<ActionButton disabled={disabled} onClick={this.onMoveBtnClick} label={label} type="move" />);
11761205
}
11771206

11781207
renderBulkAddLocationBtn(disabled) {
11791208
const label = Translator.trans(/*@Desc("Add Locations")*/ 'add_locations_btn.label', {}, 'ibexa_sub_items');
11801209

1181-
return <ActionButton disabled={disabled} onClick={this.onAddLocationsBtnClick} label={label} type="create-location" />;
1210+
return this.renderActionBtnWrapper(
1211+
<ActionButton disabled={disabled} onClick={this.onAddLocationsBtnClick} label={label} type="create-location" />,
1212+
);
11821213
}
11831214

11841215
renderBulkHideBtn(disabled) {
11851216
const label = Translator.trans(/*@Desc("Hide")*/ 'hide_locations_btn.label', {}, 'ibexa_sub_items');
11861217

1187-
return <ActionButton disabled={disabled} onClick={this.onHideBtnClick} label={label} type="hide" />;
1218+
return this.renderActionBtnWrapper(<ActionButton disabled={disabled} onClick={this.onHideBtnClick} label={label} type="hide" />);
11881219
}
11891220

11901221
renderBulkUnhideBtn(disabled) {
11911222
const label = Translator.trans(/*@Desc("Reveal")*/ 'unhide_locations_btn.label', {}, 'ibexa_sub_items');
11921223

1193-
return <ActionButton disabled={disabled} onClick={this.onUnhideBtnClick} label={label} type="reveal" />;
1224+
return this.renderActionBtnWrapper(
1225+
<ActionButton disabled={disabled} onClick={this.onUnhideBtnClick} label={label} type="reveal" />,
1226+
);
11941227
}
11951228

11961229
renderBulkDeleteBtn(disabled) {
11971230
const label = Translator.trans(/*@Desc("Delete")*/ 'trash_btn.label', {}, 'ibexa_sub_items');
11981231

1199-
return <ActionButton disabled={disabled} onClick={this.onDeleteBtnClick} label={label} type="trash" />;
1232+
return this.renderActionBtnWrapper(<ActionButton disabled={disabled} onClick={this.onDeleteBtnClick} label={label} type="trash" />);
12001233
}
12011234

12021235
renderSpinner() {
@@ -1327,6 +1360,86 @@ export default class SubItemsModule extends Component {
13271360
);
13281361
}
13291362

1363+
hideMorePanel() {
1364+
this.setState(
1365+
() => ({ morePanelVisible: false }),
1366+
() => {
1367+
setTimeout(() => {
1368+
document.body.removeEventListener('click', this.hideMorePanel, false);
1369+
}, 1);
1370+
},
1371+
);
1372+
}
1373+
1374+
showMorePanel() {
1375+
this.setState(
1376+
() => ({ morePanelVisible: true }),
1377+
() => {
1378+
setTimeout(() => {
1379+
document.body.addEventListener('click', this.hideMorePanel, false);
1380+
}, 1);
1381+
},
1382+
);
1383+
}
1384+
1385+
renderMoreBtn(actionBtns) {
1386+
const panelClasses = createCssClassNames({
1387+
'm-sub-items__adaptive-items-popup': true,
1388+
'ibexa-popup-menu': true,
1389+
'ibexa-popup-menu--hidden': !this.state.morePanelVisible,
1390+
});
1391+
const filteredActionBtns = actionBtns.filter((el, index) => {
1392+
return this.state.morePanelVisibleItemsIndexes.includes(index);
1393+
});
1394+
1395+
return [
1396+
this.renderActionBtnWrapper(
1397+
<ActionButton disabled={false} onClick={this.showMorePanel} type="options" />,
1398+
'ibexa-adaptive-items__item--selector',
1399+
{ ref: this._refAdaptiveItemMoreBtn },
1400+
),
1401+
ReactDOM.createPortal(
1402+
<div className={panelClasses} ref={this._refAdaptiveItemMorePanel}>
1403+
{filteredActionBtns}
1404+
</div>,
1405+
document.body,
1406+
),
1407+
];
1408+
}
1409+
1410+
adaptHeaderActions() {
1411+
this.popperInstance = new Popper.createPopper(this._refAdaptiveItemMoreBtn.current, this._refAdaptiveItemMorePanel.current, {
1412+
placement: 'bottom-end',
1413+
modifiers: [
1414+
{
1415+
name: 'flip',
1416+
enabled: true,
1417+
options: {
1418+
fallbackPlacements: ['top-end'],
1419+
boundary: document.body,
1420+
},
1421+
},
1422+
],
1423+
});
1424+
1425+
this.adaptiveItems = new ibexa.core.AdaptiveItems({
1426+
itemHiddenClass: 'ibexa-adaptive-items__item--hidden',
1427+
container: this._refAdaptiveItemsWrapper.current,
1428+
getActiveItem: () => null,
1429+
onAdapted: (visibleItems, hiddenItems) => {
1430+
const adaptiveItemsIterableArr = [...this.adaptiveItems.items];
1431+
1432+
const visibleItemsInPanelIndexes = [...hiddenItems].map((hiddenItem) => {
1433+
return adaptiveItemsIterableArr.indexOf(hiddenItem);
1434+
});
1435+
1436+
this.setState(() => ({ morePanelVisibleItemsIndexes: visibleItemsInPanelIndexes }));
1437+
},
1438+
});
1439+
1440+
this.adaptiveItems.init();
1441+
}
1442+
13301443
render() {
13311444
const listTitle = Translator.trans(/*@Desc("Sub-items")*/ 'items_list.title', {}, 'ibexa_sub_items');
13321445
const { selectedItems, activeView, totalCount, isDuringBulkOperation, activePageItems, subItemsWidth, columnsVisibility } =
@@ -1335,7 +1448,6 @@ export default class SubItemsModule extends Component {
13351448
const isTableViewActive = activeView === VIEW_MODE_TABLE;
13361449
const pageLoaded = !!activePageItems;
13371450
const bulkBtnDisabled = nothingSelected || !isTableViewActive || !pageLoaded;
1338-
13391451
let bulkHideBtnDisabled = true;
13401452
let bulkUnhideBtnDisabled = true;
13411453
let listClassName = 'm-sub-items__list';
@@ -1351,20 +1463,30 @@ export default class SubItemsModule extends Component {
13511463
bulkUnhideBtnDisabled = !selectedItemsValues.some((item) => !!item.hidden);
13521464
}
13531465

1466+
const actionBtns = [
1467+
...this.props.extraActions.map(this.renderExtraActions),
1468+
this.renderBulkMoveBtn(bulkBtnDisabled),
1469+
this.renderBulkAddLocationBtn(bulkBtnDisabled),
1470+
this.renderBulkHideBtn(bulkHideBtnDisabled),
1471+
this.renderBulkUnhideBtn(bulkUnhideBtnDisabled),
1472+
this.renderBulkDeleteBtn(bulkBtnDisabled),
1473+
];
1474+
13541475
return (
13551476
<div ref={this._refMainContainerWrapper}>
13561477
<div className="m-sub-items" style={{ width: `${subItemsWidth}px` }}>
13571478
<div className="ibexa-table-header ">
13581479
<div className="ibexa-table-header__headline">
13591480
{listTitle} ({this.state.totalCount})
13601481
</div>
1482+
<div
1483+
className="ibexa-table-header__actions ibexa-table-header__actions--adaptive ibexa-adaptive-items"
1484+
ref={this._refAdaptiveItemsWrapper}
1485+
>
1486+
{actionBtns}
1487+
{this.renderMoreBtn(actionBtns)}
1488+
</div>
13611489
<div className="ibexa-table-header__actions">
1362-
{this.props.extraActions.map(this.renderExtraActions)}
1363-
{this.renderBulkMoveBtn(bulkBtnDisabled)}
1364-
{this.renderBulkAddLocationBtn(bulkBtnDisabled)}
1365-
{this.renderBulkHideBtn(bulkHideBtnDisabled)}
1366-
{this.renderBulkUnhideBtn(bulkUnhideBtnDisabled)}
1367-
{this.renderBulkDeleteBtn(bulkBtnDisabled)}
13681490
<ViewColumnsTogglerComponent
13691491
columnsVisibility={this.filterColumnsVisibility(columnsVisibility)}
13701492
toggleColumnVisibility={this.toggleColumnVisibility}

0 commit comments

Comments
 (0)