diff --git a/src/bundle/Resources/public/scss/ui/modules/_sub.items.list.scss b/src/bundle/Resources/public/scss/ui/modules/_sub.items.list.scss index 04e992e9e2..8d66c72b16 100644 --- a/src/bundle/Resources/public/scss/ui/modules/_sub.items.list.scss +++ b/src/bundle/Resources/public/scss/ui/modules/_sub.items.list.scss @@ -1,6 +1,6 @@ @import 'sub-items-list/no.items'; -@import 'sub-items-list/table.view.columns.toggler.list.element'; -@import 'sub-items-list/table.view.columns.toggler'; +@import 'sub-items-list/view.columns.toggler.list.element'; +@import 'sub-items-list/view.columns.toggler'; @import 'sub-items-list/table.view.item'; @import 'sub-items-list/table.view'; @import 'sub-items-list/language.selector'; diff --git a/src/bundle/Resources/public/scss/ui/modules/common/_simple.dropdown.scss b/src/bundle/Resources/public/scss/ui/modules/common/_simple.dropdown.scss index 05a14cc452..6717e7ea25 100644 --- a/src/bundle/Resources/public/scss/ui/modules/common/_simple.dropdown.scss +++ b/src/bundle/Resources/public/scss/ui/modules/common/_simple.dropdown.scss @@ -9,6 +9,7 @@ &__selected { font-size: $ibexa-text-font-size-medium; + border: 0; padding: calculateRem(2px) calculateRem(30px) calculateRem(2px) calculateRem(8px); min-height: calculateRem(21px); cursor: pointer; diff --git a/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_main.scss b/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_main.scss index 23d9667f95..9af68b3bf9 100644 --- a/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_main.scss +++ b/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_main.scss @@ -1,7 +1,6 @@ .m-sub-items { - margin-top: calculateRem(72px); - margin-bottom: calculateRem(24px); - padding: calculateRem(16px) 0; + margin-top: calculateRem(30px); + max-width: 100%; background: $ibexa-color-white; border-radius: $ibexa-border-radius; diff --git a/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_table.view.columns.toggler.list.element.scss b/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_view.columns.toggler.list.element.scss similarity index 83% rename from src/bundle/Resources/public/scss/ui/modules/sub-items-list/_table.view.columns.toggler.list.element.scss rename to src/bundle/Resources/public/scss/ui/modules/sub-items-list/_view.columns.toggler.list.element.scss index f95d5fb756..163e45f230 100644 --- a/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_table.view.columns.toggler.list.element.scss +++ b/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_view.columns.toggler.list.element.scss @@ -1,8 +1,9 @@ -.c-table-view-columns-toggler-list-element { +.c-view-columns-toggler-list-element { cursor: pointer; .ibexa-popup-menu__item-content { display: flex; + align-items: center; } .ibexa-input--checkbox { diff --git a/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_table.view.columns.toggler.scss b/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_view.columns.toggler.scss similarity index 64% rename from src/bundle/Resources/public/scss/ui/modules/sub-items-list/_table.view.columns.toggler.scss rename to src/bundle/Resources/public/scss/ui/modules/sub-items-list/_view.columns.toggler.scss index 59c29724ca..14c3637398 100644 --- a/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_table.view.columns.toggler.scss +++ b/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_view.columns.toggler.scss @@ -1,10 +1,13 @@ -.c-table-view-columns-toggler { +.c-view-columns-toggler { height: 100%; position: relative; display: flex; justify-content: center; align-items: center; color: $ibexa-color-dark; + background-color: $ibexa-color-light-100; + border-left: calculateRem(2px) solid $ibexa-color-light; + padding: calculateRem(12px) calculateRem(16px); &__panel { top: calculateRem(36px); diff --git a/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_view.switcher.scss b/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_view.switcher.scss index dafe7638a2..72c7796cdf 100644 --- a/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_view.switcher.scss +++ b/src/bundle/Resources/public/scss/ui/modules/sub-items-list/_view.switcher.scss @@ -1,5 +1,6 @@ .c-view-switcher { - margin-left: calculateRem(8px); - padding-left: calculateRem(8px); - border-left: calculateRem(2px) solid $ibexa-color-light; + background-color: $ibexa-color-light-100; + padding: calculateRem(12px) calculateRem(16px) calculateRem(12px) 0; + border-top-right-radius: calculateRem(12px); + border-bottom-right-radius: calculateRem(12px); } diff --git a/src/bundle/Resources/translations/ibexa_locationview.en.xliff b/src/bundle/Resources/translations/ibexa_locationview.en.xliff index 9f9bea1ab8..9b8eb2812e 100644 --- a/src/bundle/Resources/translations/ibexa_locationview.en.xliff +++ b/src/bundle/Resources/translations/ibexa_locationview.en.xliff @@ -296,6 +296,11 @@ Roles key: tab.name.roles + + Sub-items + Sub-items + key: tab.name.sub_items + Translations Translations diff --git a/src/bundle/Resources/translations/ibexa_sub_items.en.xliff b/src/bundle/Resources/translations/ibexa_sub_items.en.xliff index 2853a95cff..50a079d576 100644 --- a/src/bundle/Resources/translations/ibexa_sub_items.en.xliff +++ b/src/bundle/Resources/translations/ibexa_sub_items.en.xliff @@ -201,80 +201,75 @@ Sub-items key: items_list.title - + Content type Content type - key: items_table.header.content_type + key: items_table.columns.content_type - + Contributor Contributor - key: items_table.header.contributor + key: items_table.columns.contributor - + Creator Creator - key: items_table.header.creator + key: items_table.columns.creator - - Filters - Filters - key: items_table.header.filters - - + Location ID Location ID - key: items_table.header.location_id + key: items_table.columns.location_id - + Location remote ID Location remote ID - key: items_table.header.location_remote_id + key: items_table.columns.location_remote_id - + Modified Modified - key: items_table.header.modified + key: items_table.columns.modified - + Name Name - key: items_table.header.name + key: items_table.columns.name - + Object ID Object ID - key: items_table.header.object_id + key: items_table.columns.object_id - + Object remote ID Object remote ID - key: items_table.header.object_remote_id + key: items_table.columns.object_remote_id - + Priority Priority - key: items_table.header.priority + key: items_table.columns.priority - + Published Published - key: items_table.header.pubished + key: items_table.columns.pubished - + Section Section - key: items_table.header.section + key: items_table.columns.section - + Translations Translations - key: items_table.header.translations + key: items_table.columns.translations - + Visibility Visibility - key: items_table.header.visibility + key: items_table.columns.visibility Not Visible @@ -311,6 +306,11 @@ Reveal key: unhide_locations_btn.label + + Columns + Columns + key: view_columns_toggler.label + Grid view Grid view diff --git a/src/bundle/Resources/views/themes/admin/content/location_view.html.twig b/src/bundle/Resources/views/themes/admin/content/location_view.html.twig index 119e63ea1c..3e05dec8d2 100644 --- a/src/bundle/Resources/views/themes/admin/content/location_view.html.twig +++ b/src/bundle/Resources/views/themes/admin/content/location_view.html.twig @@ -123,13 +123,6 @@ {% endblock %} {% block content %} -
@@ -146,21 +139,14 @@ 'roles_pagination_params': roles_pagination_params, 'policies_pagination_params': policies_pagination_params, 'is_location_visible': not location.invisible, + 'subitems_module': subitems_module, }) }} {% if content_type.isContainer %} - {{ form_start(form_subitems_content_edit, { 'action': path('ibexa.content.edit'), 'attr': { 'hidden': 'hidden' }}) }} - {{ form_end(form_subitems_content_edit) }} - {% set udwConfigSingle = ibexa_udw_config('single_container', {}) %} -
- {% include '@ibexadesign/content/modal/location_bulk_action_failed.html.twig' only %} + {{ form_start(form_subitems_content_edit, { 'action': path('ibexa.content.edit'), 'attr': { 'hidden': 'hidden' }}) }} + {{ form_end(form_subitems_content_edit) }} + + {% include '@ibexadesign/content/modal/location_bulk_action_failed.html.twig' only %} {% endif %} {% if form_user_invitation is defined %} @@ -183,8 +169,8 @@ {% endblock %} {% block react_modules %} - {{ encore_entry_script_tags('ibexa-admin-ui-subitems-js', null, 'ibexa') }} {{ encore_entry_script_tags('ibexa-admin-ui-mfu-js', null, 'ibexa') }} + {{ encore_entry_script_tags('ibexa-admin-ui-subitems-js', null, 'ibexa') }} {{ encore_entry_script_tags('ibexa-admin-ui-content-tree-js', null, 'ibexa') }} {% endblock %} diff --git a/src/bundle/Resources/views/themes/admin/content/tab/sub_items.html.twig b/src/bundle/Resources/views/themes/admin/content/tab/sub_items.html.twig new file mode 100644 index 0000000000..329470d49c --- /dev/null +++ b/src/bundle/Resources/views/themes/admin/content/tab/sub_items.html.twig @@ -0,0 +1,4 @@ +{% trans_default_domain 'ibexa_locationview' %} + +{% include '@ibexadesign/ui/component/sub_items/multifile_upload.html.twig' %} +{% include '@ibexadesign/ui/component/sub_items/sub_items.html.twig' %} diff --git a/src/bundle/Resources/views/themes/admin/ui/component/sub_items/multifile_upload.html.twig b/src/bundle/Resources/views/themes/admin/ui/component/sub_items/multifile_upload.html.twig new file mode 100644 index 0000000000..28bb623c67 --- /dev/null +++ b/src/bundle/Resources/views/themes/admin/ui/component/sub_items/multifile_upload.html.twig @@ -0,0 +1,7 @@ +
diff --git a/src/bundle/Resources/views/themes/admin/ui/component/sub_items/sub_items.html.twig b/src/bundle/Resources/views/themes/admin/ui/component/sub_items/sub_items.html.twig new file mode 100644 index 0000000000..9d34b5505e --- /dev/null +++ b/src/bundle/Resources/views/themes/admin/ui/component/sub_items/sub_items.html.twig @@ -0,0 +1,10 @@ +{% set udwConfigSingle = ibexa_udw_config('single_container', {}) %} + +
diff --git a/src/bundle/ui-dev/src/modules/common/simple-dropdown/simple.dropdown.js b/src/bundle/ui-dev/src/modules/common/simple-dropdown/simple.dropdown.js index 4783f66f50..d7007de970 100644 --- a/src/bundle/ui-dev/src/modules/common/simple-dropdown/simple.dropdown.js +++ b/src/bundle/ui-dev/src/modules/common/simple-dropdown/simple.dropdown.js @@ -70,11 +70,11 @@ const SimpleDropdown = ({ options, selectedOption, extraClasses, onOptionClick, }; const renderSelectedItem = () => { return ( -
+
+ ); }; diff --git a/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.columns.toggler.list.element.js b/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.columns.toggler.list.element.js deleted file mode 100644 index ff65331834..0000000000 --- a/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.columns.toggler.list.element.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -const TableViewColumnsTogglerListElement = ({ label, isColumnVisible, toggleColumnVisibility, columnKey }) => { - return ( -
  • toggleColumnVisibility(columnKey)}> -
    - - -
    -
  • - ); -}; - -TableViewColumnsTogglerListElement.propTypes = { - label: PropTypes.string.isRequired, - columnKey: PropTypes.string.isRequired, - isColumnVisible: PropTypes.bool.isRequired, - toggleColumnVisibility: PropTypes.func.isRequired, -}; - -export default TableViewColumnsTogglerListElement; diff --git a/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.component.js b/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.component.js index c475fa02af..63173a2f1c 100644 --- a/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.component.js +++ b/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.component.js @@ -3,51 +3,17 @@ import { createPortal } from 'react-dom'; import PropTypes from 'prop-types'; import TableViewItemComponent from './table.view.item.component'; -import TableViewColumnsTogglerComponent from './table.view.columns.toggler'; import ThreeStateCheckboxComponent from '../three-state-checkbox/three.state.checkbox.component'; import LanguageSelector from '../sub-items-list/language.selector.compoment'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; +import { columnsLabels as headerLabels } from '../../sub.items.module'; -const { Translator } = window; - -const COLUMNS_VISIBILITY_LOCAL_STORAGE_DATA_KEY = 'sub-items_columns-visibility'; -const DEFAULT_COLUMNS_VISIBILITY = { - modified: true, - 'content-type': true, - priority: true, - translations: true, - visibility: true, - creator: true, - contributor: true, - published: true, - section: true, - 'location-id': true, - 'location-remote-id': true, - 'object-id': true, - 'object-remote-id': true, -}; const SORTKEY_MAP = { name: 'ContentName', modified: 'DateModified', priority: 'LocationPriority', }; const TABLE_HEAD_CLASS = 'ibexa-table__header-cell c-table-view__cell c-table-view__cell--head'; -export const headerLabels = { - name: Translator.trans(/*@Desc("Name")*/ 'items_table.header.name', {}, 'ibexa_sub_items'), - modified: Translator.trans(/*@Desc("Modified")*/ 'items_table.header.modified', {}, 'ibexa_sub_items'), - 'content-type': Translator.trans(/*@Desc("Content type")*/ 'items_table.header.content_type', {}, 'ibexa_sub_items'), - priority: Translator.trans(/*@Desc("Priority")*/ 'items_table.header.priority', {}, 'ibexa_sub_items'), - translations: Translator.trans(/*@Desc("Translations")*/ 'items_table.header.translations', {}, 'ibexa_sub_items'), - visibility: Translator.trans(/*@Desc("Visibility")*/ 'items_table.header.visibility', {}, 'ibexa_sub_items'), - creator: Translator.trans(/*@Desc("Creator")*/ 'items_table.header.creator', {}, 'ibexa_sub_items'), - contributor: Translator.trans(/*@Desc("Contributor")*/ 'items_table.header.contributor', {}, 'ibexa_sub_items'), - published: Translator.trans(/*@Desc("Published")*/ 'items_table.header.pubished', {}, 'ibexa_sub_items'), - section: Translator.trans(/*@Desc("Section")*/ 'items_table.header.section', {}, 'ibexa_sub_items'), - 'location-id': Translator.trans(/*@Desc("Location ID")*/ 'items_table.header.location_id', {}, 'ibexa_sub_items'), - 'location-remote-id': Translator.trans(/*@Desc("Location remote ID")*/ 'items_table.header.location_remote_id', {}, 'ibexa_sub_items'), - 'object-id': Translator.trans(/*@Desc("Object ID")*/ 'items_table.header.object_id', {}, 'ibexa_sub_items'), - 'object-remote-id': Translator.trans(/*@Desc("Object remote ID")*/ 'items_table.header.object_remote_id', {}, 'ibexa_sub_items'), -}; export default class TableViewComponent extends Component { constructor(props) { @@ -55,8 +21,6 @@ export default class TableViewComponent extends Component { this.renderItem = this.renderItem.bind(this); this.selectAll = this.selectAll.bind(this); - this.setColumnsVisibilityInLocalStorage = this.setColumnsVisibilityInLocalStorage.bind(this); - this.toggleColumnVisibility = this.toggleColumnVisibility.bind(this); this.setLanguageSelectorData = this.setLanguageSelectorData.bind(this); this.openLanguageSelector = this.openLanguageSelector.bind(this); this.closeLanguageSelector = this.closeLanguageSelector.bind(this); @@ -66,7 +30,6 @@ export default class TableViewComponent extends Component { this._refScroller = createRef(); this.state = { - columnsVisibility: this.getColumnsVisibilityFromLocalStorage(), languageSelectorData: {}, languageSelectorOpen: false, scrollShadowLeft: false, @@ -80,8 +43,8 @@ export default class TableViewComponent extends Component { this.handleScrollerScroll(); } - componentDidUpdate(prevProps, prevState) { - if (this.state.columnsVisibility !== prevState.columnsVisibility) { + componentDidUpdate(prevProps) { + if (this.props.columnsVisibility !== prevProps.columnsVisibility) { this.handleScrollerScroll(); } } @@ -107,27 +70,6 @@ export default class TableViewComponent extends Component { }); } - getColumnsVisibilityFromLocalStorage() { - const columnsVisibilityData = localStorage.getItem(COLUMNS_VISIBILITY_LOCAL_STORAGE_DATA_KEY); - const columnsVisibility = { ...DEFAULT_COLUMNS_VISIBILITY }; - - if (columnsVisibilityData) { - Object.entries(JSON.parse(columnsVisibilityData)).forEach(([id, isVisible]) => { - if (id in columnsVisibility) { - columnsVisibility[id] = isVisible; - } - }); - } - - return columnsVisibility; - } - - setColumnsVisibilityInLocalStorage() { - const columnsVisibilityData = JSON.stringify(this.state.columnsVisibility); - - localStorage.setItem(COLUMNS_VISIBILITY_LOCAL_STORAGE_DATA_KEY, columnsVisibilityData); - } - /** * Selects all visible items */ @@ -139,18 +81,6 @@ export default class TableViewComponent extends Component { toggleAllItemsSelect(isSelectAction); } - toggleColumnVisibility(column) { - this.setState( - (state) => ({ - columnsVisibility: { - ...state.columnsVisibility, - [column]: !state.columnsVisibility[column], - }, - }), - this.setColumnsVisibilityInLocalStorage, - ); - } - /** * Sets language selector data * @@ -185,7 +115,8 @@ export default class TableViewComponent extends Component { * @memberof TableViewComponent */ renderItem(item) { - const { columnsVisibility, scrollShadowLeft, scrollShadowRight } = this.state; + const { scrollShadowLeft, scrollShadowRight } = this.state; + const { columnsVisibility } = this.props; const { handleItemPriorityUpdate, handleEditItem, generateLink, languages, onItemSelect, selectedLocationsIds } = this.props; const isSelected = selectedLocationsIds.has(item.id); @@ -209,8 +140,8 @@ export default class TableViewComponent extends Component { } renderBasicColumnsHeader() { - const { sortClause, sortOrder, onSortChange } = this.props; - const { columnsVisibility, scrollShadowLeft } = this.state; + const { sortClause, sortOrder, onSortChange, columnsVisibility } = this.props; + const { scrollShadowLeft } = this.state; const columnsToRender = { name: true, ...columnsVisibility, @@ -263,16 +194,10 @@ export default class TableViewComponent extends Component { return null; } - const { columnsVisibility, scrollShadowRight } = this.state; const { selectedLocationsIds, items } = this.props; const anyLocationSelected = !!selectedLocationsIds.size; const allLocationsSelected = selectedLocationsIds.size === items.length; const isCheckboxIndeterminate = anyLocationSelected && !allLocationsSelected; - const togglerClassName = createCssClassNames({ - 'c-table-view__cell': true, - 'c-table-view__cell--toggler': true, - 'c-table-view__cell--shadow-left': scrollShadowRight, - }); return ( @@ -287,12 +212,7 @@ export default class TableViewComponent extends Component { /> {this.renderBasicColumnsHeader()} - - - + ); @@ -336,6 +256,7 @@ TableViewComponent.propTypes = { sortClause: PropTypes.string.isRequired, sortOrder: PropTypes.string.isRequired, languageContainerSelector: PropTypes.string.isRequired, + columnsVisibility: PropTypes.object.isRequired, }; TableViewComponent.defaultProps = { diff --git a/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.item.component.js b/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.item.component.js index f4aef24edc..2bebedcc18 100644 --- a/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.item.component.js +++ b/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.item.component.js @@ -263,9 +263,18 @@ export default class TableViewItemComponent extends PureComponent { const { invisible, hidden } = this.props.item; const visibleLabel = Translator.trans(/*@Desc("Visible")*/ 'items_table.row.visible.label', {}, 'ibexa_sub_items'); const notVisibleLabel = Translator.trans(/*@Desc("Not Visible")*/ 'items_table.row.not_visible.label', {}, 'ibexa_sub_items'); - const label = !invisible && !hidden ? visibleLabel : notVisibleLabel; + const isVisible = !invisible && !hidden; + const label = isVisible ? visibleLabel : notVisibleLabel; + const badgeClasses = createCssClassNames({ + 'ibexa-badge': true, + 'ibexa-badge--success': isVisible, + }); - return
    {label}
    ; + return ( +
    + {label} +
    + ); } renderCreatorCell() { diff --git a/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.columns.toggler.js b/src/bundle/ui-dev/src/modules/sub-items/components/view-columns-toggler/view.columns.toggler.js similarity index 61% rename from src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.columns.toggler.js rename to src/bundle/ui-dev/src/modules/sub-items/components/view-columns-toggler/view.columns.toggler.js index c826292aba..9e82c37585 100644 --- a/src/bundle/ui-dev/src/modules/sub-items/components/table-view/table.view.columns.toggler.js +++ b/src/bundle/ui-dev/src/modules/sub-items/components/view-columns-toggler/view.columns.toggler.js @@ -2,15 +2,15 @@ import React, { Component, createRef } from 'react'; import PropTypes from 'prop-types'; import Icon from '../../../common/icon/icon'; -import TableViewColumnsTogglerListElement from './table.view.columns.toggler.list.element'; -import { headerLabels } from './table.view.component'; +import ViewColumnsTogglerListElement from './view.columns.toggler.list.element'; +import { columnsLabels } from '../../sub.items.module'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; const { Translator } = window; const DEFAULT_PANEL_HEIGHT = 450; -export default class TableViewColumnsTogglerComponent extends Component { +export default class ViewColumnsTogglerComponent extends Component { constructor(props) { super(props); @@ -50,9 +50,9 @@ export default class TableViewColumnsTogglerComponent extends Component { } getBtnBottomDocumentOffset() { - const buttonTopOffset = this._refTogglerButton.current.getBoundingClientRect().top + window.scrollY; + const buttonTopOffset = this._refTogglerButton.current.getBoundingClientRect().top; - return document.documentElement.scrollHeight - buttonTopOffset; + return window.innerHeight - buttonTopOffset; } hidePanel({ target }) { @@ -60,7 +60,7 @@ export default class TableViewColumnsTogglerComponent extends Component { return; } - const isClickInsideToggler = target.closest('.c-table-view-columns-toggler'); + const isClickInsideToggler = target.closest('.c-view-columns-toggler'); if (!isClickInsideToggler) { this.setState(() => ({ @@ -71,6 +71,7 @@ export default class TableViewColumnsTogglerComponent extends Component { togglePanel() { this.setState((state) => ({ + buttonBottomDocumentOffset: this.getBtnBottomDocumentOffset(), isOpen: !state.isOpen, })); } @@ -82,21 +83,21 @@ export default class TableViewColumnsTogglerComponent extends Component { const { columnsVisibility, toggleColumnVisibility } = this.props; const { buttonBottomDocumentOffset, panelHeight: measuredPanelHeight } = this.state; - const panelHeight = measuredPanelHeight ? measuredPanelHeight : DEFAULT_PANEL_HEIGHT; + const panelHeight = measuredPanelHeight ?? DEFAULT_PANEL_HEIGHT; const showAboveBtn = buttonBottomDocumentOffset < panelHeight; const className = createCssClassNames({ 'ibexa-popup-menu': true, - 'c-table-view-columns-toggler__panel': true, - 'c-table-view-columns-toggler__panel--above-btn': showAboveBtn, + 'c-view-columns-toggler__panel': true, + 'c-view-columns-toggler__panel--above-btn': showAboveBtn, }); return (
      {Object.entries(columnsVisibility).map(([columnKey, isColumnVisible]) => { - const label = headerLabels[columnKey]; + const label = columnsLabels[columnKey]; return ( - ; + } + + renderToggler() { + const label = Translator.trans(/*@Desc("Columns")*/ 'view_columns_toggler.label', {}, 'ibexa_sub_items'); return ( -
      - - {this.renderPanel()} + + ); + } + + render() { + return ( +
      +
      + {this.renderToggler()} + {this.renderPanel()} +
      ); } } -TableViewColumnsTogglerComponent.propTypes = { +ViewColumnsTogglerComponent.propTypes = { columnsVisibility: PropTypes.object.isRequired, toggleColumnVisibility: PropTypes.func.isRequired, }; diff --git a/src/bundle/ui-dev/src/modules/sub-items/components/view-columns-toggler/view.columns.toggler.list.element.js b/src/bundle/ui-dev/src/modules/sub-items/components/view-columns-toggler/view.columns.toggler.list.element.js new file mode 100644 index 0000000000..9a893e85e9 --- /dev/null +++ b/src/bundle/ui-dev/src/modules/sub-items/components/view-columns-toggler/view.columns.toggler.list.element.js @@ -0,0 +1,27 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const ViewColumnsTogglerListElement = ({ label, isColumnVisible, toggleColumnVisibility, columnKey }) => { + return ( +
    • + +
    • + ); +}; + +ViewColumnsTogglerListElement.propTypes = { + label: PropTypes.string.isRequired, + columnKey: PropTypes.string.isRequired, + isColumnVisible: PropTypes.bool.isRequired, + toggleColumnVisibility: PropTypes.func.isRequired, +}; + +export default ViewColumnsTogglerListElement; diff --git a/src/bundle/ui-dev/src/modules/sub-items/sub.items.module.js b/src/bundle/ui-dev/src/modules/sub-items/sub.items.module.js index 86e7f54758..d40a4bd9bf 100644 --- a/src/bundle/ui-dev/src/modules/sub-items/sub.items.module.js +++ b/src/bundle/ui-dev/src/modules/sub-items/sub.items.module.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; +import ViewColumnsTogglerComponent from './components/view-columns-toggler/view.columns.toggler'; import ViewSwitcherComponent from './components/view-switcher/view.switcher.component.js'; import SubItemsListComponent from './components/sub-items-list/sub.items.list.component.js'; import Popup from '../common/popup/popup.component'; @@ -22,7 +23,40 @@ const DESCENDING_SORT_ORDER = 'descending'; const DEFAULT_SORT_ORDER = ASCENDING_SORT_ORDER; const ACTION_FLOW_ADD_LOCATIONS = 'add'; const ACTION_FLOW_MOVE = 'move'; -const SUBITEMS_PADDING = 15; +const SUBITEMS_PADDING = 24; + +const COLUMNS_VISIBILITY_LOCAL_STORAGE_DATA_KEY = 'sub-items_columns-visibility'; +const DEFAULT_COLUMNS_VISIBILITY = { + 'content-type': true, + priority: false, + translations: true, + visibility: true, + contributor: true, + modified: true, + creator: false, + published: false, + section: false, + 'location-id': false, + 'location-remote-id': false, + 'object-id': false, + 'object-remote-id': false, +}; +export const columnsLabels = { + name: Translator.trans(/*@Desc("Name")*/ 'items_table.columns.name', {}, 'ibexa_sub_items'), + modified: Translator.trans(/*@Desc("Modified")*/ 'items_table.columns.modified', {}, 'ibexa_sub_items'), + 'content-type': Translator.trans(/*@Desc("Content type")*/ 'items_table.columns.content_type', {}, 'ibexa_sub_items'), + priority: Translator.trans(/*@Desc("Priority")*/ 'items_table.columns.priority', {}, 'ibexa_sub_items'), + translations: Translator.trans(/*@Desc("Translations")*/ 'items_table.columns.translations', {}, 'ibexa_sub_items'), + visibility: Translator.trans(/*@Desc("Visibility")*/ 'items_table.columns.visibility', {}, 'ibexa_sub_items'), + creator: Translator.trans(/*@Desc("Creator")*/ 'items_table.columns.creator', {}, 'ibexa_sub_items'), + contributor: Translator.trans(/*@Desc("Contributor")*/ 'items_table.columns.contributor', {}, 'ibexa_sub_items'), + published: Translator.trans(/*@Desc("Published")*/ 'items_table.columns.pubished', {}, 'ibexa_sub_items'), + section: Translator.trans(/*@Desc("Section")*/ 'items_table.columns.section', {}, 'ibexa_sub_items'), + 'location-id': Translator.trans(/*@Desc("Location ID")*/ 'items_table.columns.location_id', {}, 'ibexa_sub_items'), + 'location-remote-id': Translator.trans(/*@Desc("Location remote ID")*/ 'items_table.columns.location_remote_id', {}, 'ibexa_sub_items'), + 'object-id': Translator.trans(/*@Desc("Object ID")*/ 'items_table.columns.object_id', {}, 'ibexa_sub_items'), + 'object-remote-id': Translator.trans(/*@Desc("Object remote ID")*/ 'items_table.columns.object_remote_id', {}, 'ibexa_sub_items'), +}; export const VIEW_MODE_TABLE = 'table'; export const VIEW_MODE_GRID = 'grid'; @@ -56,6 +90,8 @@ export default class SubItemsModule extends Component { this.changeSorting = this.changeSorting.bind(this); this.calculateSubItemsWidth = this.calculateSubItemsWidth.bind(this); this.resizeSubItems = this.resizeSubItems.bind(this); + this.setColumnsVisibilityInLocalStorage = this.setColumnsVisibilityInLocalStorage.bind(this); + this.toggleColumnVisibility = this.toggleColumnVisibility.bind(this); this._refListViewWrapper = React.createRef(); this._refMainContainerWrapper = React.createRef(); @@ -82,6 +118,7 @@ export default class SubItemsModule extends Component { sortClause: sortClauseData.name, sortOrder: sortClauseData.order, subItemsWidth: this.calculateSubItemsWidth(), + columnsVisibility: this.getColumnsVisibilityFromLocalStorage(), }; } @@ -149,7 +186,7 @@ export default class SubItemsModule extends Component { const mainRow = document.querySelector('.ibexa-main-row'); const mainRowRect = mainRow.getBoundingClientRect(); - return mainRowRect.width - SUBITEMS_PADDING; + return mainRowRect.width - 2 * SUBITEMS_PADDING; } getDefaultSortClause(sortClauses) { @@ -1195,7 +1232,7 @@ export default class SubItemsModule extends Component { } renderListView() { - const { activePageItems, sortClause, sortOrder } = this.state; + const { activePageItems, sortClause, sortOrder, columnsVisibility } = this.state; const pageLoaded = !!activePageItems; if (!pageLoaded) { @@ -1218,6 +1255,7 @@ export default class SubItemsModule extends Component { onSortChange={this.changeSorting} sortClause={sortClause} sortOrder={sortOrder} + columnsVisibility={this.filterSmartModeColumns(columnsVisibility)} languageContainerSelector={this.props.languageContainerSelector} /> ); @@ -1233,9 +1271,58 @@ export default class SubItemsModule extends Component { ); } + filterSmartModeColumns(allColumns) { + // TODO: filter when smart mode implemented + const expertModeColumns = ['section', 'location-id', 'location-remote-id', 'object-id', 'object-remote-id']; + + const filteredColumns = {}; + + Object.keys(allColumns).forEach((columnKey) => { + if (!expertModeColumns.includes(columnKey)) { + filteredColumns[columnKey] = allColumns[columnKey]; + } + }); + + return filteredColumns; + } + + getColumnsVisibilityFromLocalStorage() { + const columnsVisibilityData = localStorage.getItem(COLUMNS_VISIBILITY_LOCAL_STORAGE_DATA_KEY); + const columnsVisibility = { ...DEFAULT_COLUMNS_VISIBILITY }; + + if (columnsVisibilityData) { + Object.entries(JSON.parse(columnsVisibilityData)).forEach(([id, isVisible]) => { + if (id in columnsVisibility) { + columnsVisibility[id] = isVisible; + } + }); + } + + return columnsVisibility; + } + + setColumnsVisibilityInLocalStorage() { + const columnsVisibilityData = JSON.stringify(this.state.columnsVisibility); + + localStorage.setItem(COLUMNS_VISIBILITY_LOCAL_STORAGE_DATA_KEY, columnsVisibilityData); + } + + toggleColumnVisibility(column) { + this.setState( + (state) => ({ + columnsVisibility: { + ...state.columnsVisibility, + [column]: !state.columnsVisibility[column], + }, + }), + this.setColumnsVisibilityInLocalStorage, + ); + } + render() { const listTitle = Translator.trans(/*@Desc("Sub-items")*/ 'items_list.title', {}, 'ibexa_sub_items'); - const { selectedItems, activeView, totalCount, isDuringBulkOperation, activePageItems, subItemsWidth } = this.state; + const { selectedItems, activeView, totalCount, isDuringBulkOperation, activePageItems, subItemsWidth, columnsVisibility } = + this.state; const nothingSelected = !selectedItems.size; const isTableViewActive = activeView === VIEW_MODE_TABLE; const pageLoaded = !!activePageItems; @@ -1269,6 +1356,10 @@ export default class SubItemsModule extends Component { {this.renderBulkHideBtn(bulkHideBtnDisabled)} {this.renderBulkUnhideBtn(bulkUnhideBtnDisabled)} {this.renderBulkDeleteBtn(bulkBtnDisabled)} +
    diff --git a/src/lib/Behat/Page/ContentViewPage.php b/src/lib/Behat/Page/ContentViewPage.php index 49cf253ddb..c85af5443c 100644 --- a/src/lib/Behat/Page/ContentViewPage.php +++ b/src/lib/Behat/Page/ContentViewPage.php @@ -76,9 +76,6 @@ class ContentViewPage extends Page /** @var \Ibexa\Contracts\Core\Repository\Repository */ private $repository; - /** @var bool */ - private $expectedIsContainer; - /** @var \Ibexa\Behat\Core\Behat\ArgumentParser; */ private $argumentParser; @@ -193,6 +190,7 @@ public function choosePreview(string $language): void public function goToSubItem(string $contentItemName): void { + $this->switchToTab('Sub-items'); $this->subItemList->verifyIsLoaded(); $this->subItemList->sortBy('Modified', false); @@ -215,9 +213,8 @@ public function navigateToPath(string $path): void public function setExpectedLocationPath(string $locationPath) { - [$this->expectedContentType, $this->expectedContentName, $contentId, $contentMainLocationId, $isContainer] = $this->getContentData($this->argumentParser->parseUrl($locationPath)); + [$this->expectedContentType, $this->expectedContentName, $contentId, $contentMainLocationId] = $this->getContentData($this->argumentParser->parseUrl($locationPath)); $this->route = sprintf('/view/content/%s/full/1/%s', $contentId, $contentMainLocationId); - $this->expectedIsContainer = $isContainer; $this->locationPath = $locationPath; $this->subItemList->shouldHaveGridViewEnabled($this->hasGridViewEnabledByDefault()); } @@ -232,10 +229,6 @@ public function verifyIsLoaded(): void 'Breadcrumb shows invalid path' ); - if ($this->expectedIsContainer) { - $this->subItemList->verifyIsLoaded(); - } - Assert::assertEquals( $this->expectedContentName, $this->getHTMLPage()->find($this->getLocator('pageTitle'))->getText() @@ -266,6 +259,8 @@ public function editContent(?string $language) public function isChildElementPresent(array $parameters): bool { + $this->switchToTab('Sub-items'); + return $this->subItemList->isElementInTable($parameters); } @@ -331,7 +326,6 @@ private function getContentData(string $locationPath): array $content->getName(), $content->id, $content->contentInfo->getMainLocation()->id, - $content->getContentType()->isContainer, ]; }); } diff --git a/src/lib/Tab/LocationView/SubItemsTab.php b/src/lib/Tab/LocationView/SubItemsTab.php new file mode 100644 index 0000000000..c6457cf248 --- /dev/null +++ b/src/lib/Tab/LocationView/SubItemsTab.php @@ -0,0 +1,55 @@ +translator->trans('tab.name.sub_items', [], 'ibexa_locationview'); + } + + public function getOrder(): int + { + return 200; + } + + public function getTemplate(): string + { + return '@ibexadesign/content/tab/sub_items.html.twig'; + } + + public function getTemplateParameters(array $contextParameters = []): array + { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $content */ + $content = $contextParameters['content']; + + $versionInfo = $content->getVersionInfo(); + $contentInfo = $versionInfo->getContentInfo(); + + $viewParameters = [ + 'content_info' => $contentInfo, + 'version_info' => $versionInfo, + ]; + + return array_replace($contextParameters, $viewParameters); + } +}