diff --git a/code_samples/back_office/subitems/render_subitems.js b/code_samples/back_office/subitems/render_subitems.js new file mode 100644 index 0000000000..e943c5f05e --- /dev/null +++ b/code_samples/back_office/subitems/render_subitems.js @@ -0,0 +1,12 @@ +const containerNode = document.querySelector('#sub-items-container'); + +ReactDOM.render( + React.createElement(ibexa.modules.SubItems, { + parentLocationId: { Number }, + restInfo: { + token: { String }, + siteaccess: { String }, + }, + }), + containerNode, +); diff --git a/code_samples/back_office/subitems/render_subitems.jsx b/code_samples/back_office/subitems/render_subitems.jsx new file mode 100644 index 0000000000..39ba660108 --- /dev/null +++ b/code_samples/back_office/subitems/render_subitems.jsx @@ -0,0 +1,9 @@ +const attrs = { + parentLocationId: {Number}, + restInfo: { + token: {String}, + siteaccess: {String} + } +}; + + diff --git a/code_samples/back_office/subitems/timeline_view/registerTimelineView.js b/code_samples/back_office/subitems/timeline_view/registerTimelineView.js new file mode 100644 index 0000000000..a66b5bd7ef --- /dev/null +++ b/code_samples/back_office/subitems/timeline_view/registerTimelineView.js @@ -0,0 +1,11 @@ +import TimelineViewComponent from './timeline.view.component.js'; +import { registerView } from '@ibexa-admin-ui-modules/sub-items/services/view.registry'; + +// Use the existing constants to replace a view +import { VIEW_MODE_GRID, VIEW_MODE_TABLE } from '@ibexa-admin-ui-modules/sub-items/constants'; + +registerView('timeline', { + component: TimelineViewComponent, + iconName: 'timeline', + label: 'Timeline view', +}); diff --git a/code_samples/back_office/subitems/timeline_view/timeline.view.component.js b/code_samples/back_office/subitems/timeline_view/timeline.view.component.js new file mode 100644 index 0000000000..6246dd85d3 --- /dev/null +++ b/code_samples/back_office/subitems/timeline_view/timeline.view.component.js @@ -0,0 +1,46 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import TimelineViewItemComponent from './timeline.view.item.component'; + +const TimelineViewComponent = ({ items, generateLink }) => { + const groupByDate = (items) => { + return items.reduce((groups, item) => { + const date = new Date(item.content._info.modificationDate.timestamp * 1000); + const dateKey = date.toISOString().split('T')[0]; + + if (!groups[dateKey]) { + groups[dateKey] = []; + } + + groups[dateKey].push(item); + return groups; + }, {}); + }; + + const groupedItems = groupByDate(items); + + return ( +
+ {Object.entries(groupedItems).map(([date, dateItems]) => ( +
+
+
+

{new Date(date).toLocaleDateString()}

+
+
+ {dateItems.map((item) => ( + + ))} +
+
+ ))} +
+ ); +}; + +TimelineViewComponent.propTypes = { + items: PropTypes.array.isRequired, + generateLink: PropTypes.func.isRequired, +}; + +export default TimelineViewComponent; diff --git a/code_samples/back_office/subitems/timeline_view/timeline.view.item.component.js b/code_samples/back_office/subitems/timeline_view/timeline.view.item.component.js new file mode 100644 index 0000000000..dd7f81486e --- /dev/null +++ b/code_samples/back_office/subitems/timeline_view/timeline.view.item.component.js @@ -0,0 +1,34 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Icon from '@ibexa-admin-ui-modules/common/icon/icon'; + +const { ibexa } = window; + +const TimelineViewItemComponent = ({ item, generateLink }) => { + const { content } = item; + const contentTypeIdentifier = content._info.contentType.identifier; + const contentTypeIconUrl = ibexa.helpers.contentType.getContentTypeIconUrl(contentTypeIdentifier); + const time = new Date(content._info.modificationDate.timestamp * 1000).toLocaleTimeString(); + + return ( + +
{time}
+
+
+
{content._name}
+
+ + {content._info.contentType.name} +
+
+
+
+ ); +}; + +TimelineViewItemComponent.propTypes = { + item: PropTypes.object.isRequired, + generateLink: PropTypes.func.isRequired, +}; + +export default TimelineViewItemComponent; diff --git a/code_samples/back_office/subitems/timeline_view/timeline.view.scss b/code_samples/back_office/subitems/timeline_view/timeline.view.scss new file mode 100644 index 0000000000..06f9d2f233 --- /dev/null +++ b/code_samples/back_office/subitems/timeline_view/timeline.view.scss @@ -0,0 +1,84 @@ +@use '@ibexa-admin-ui/src/bundle/Resources/public/scss/custom.scss' as *; + +.app-timeline-view { + padding: calculateRem(16px); + + &__group { + position: relative; + margin-bottom: calculateRem(32px); + } + + &__date { + display: flex; + align-items: center; + margin-bottom: calculateRem(16px); + + h3 { + margin: 0; + font-size: $ibexa-text-font-size-large; + color: $ibexa-color-dark; + } + } + + &__date-marker { + width: calculateRem(12px); + height: calculateRem(12px); + border-radius: 50%; + background: $ibexa-color-primary; + margin-right: calculateRem(16px); + } + + &__items { + margin-left: calculateRem(6px); + padding-left: calculateRem(32px); + border-left: calculateRem(2px) solid $ibexa-color-light; + } +} + +.app-timeline-view-item { + display: flex; + align-items: flex-start; + padding: calculateRem(16px); + margin-bottom: calculateRem(8px); + text-decoration: none; + color: inherit; + background: $ibexa-color-light-300; + border-radius: $ibexa-border-radius; + transition: background-color 0.2s $ibexa-admin-transition; + + &:hover { + background: $ibexa-color-light-400; + } + + &__time { + color: $ibexa-color-dark-400; + margin-right: calculateRem(16px); + min-width: calculateRem(80px); + } + + &__content { + display: flex; + align-items: center; + } + + &__icon { + margin-right: calculateRem(16px); + } + + &__name { + font-weight: $ibexa-font-weight-bold; + margin-bottom: calculateRem(4px); + } + + &__type { + font-size: $ibexa-text-font-size-small; + color: $ibexa-color-dark-400; + display: flex; + align-items: center; + gap: calculateRem(8px); + } + + &__type-name { + line-height: calculateRem(16px); + } +} diff --git a/docs/administration/back_office/back_office_elements/importing_assets_from_bundle.md b/docs/administration/back_office/back_office_elements/importing_assets_from_bundle.md index 1b4287e39a..fa5efb5cad 100644 --- a/docs/administration/back_office/back_office_elements/importing_assets_from_bundle.md +++ b/docs/administration/back_office/back_office_elements/importing_assets_from_bundle.md @@ -103,11 +103,15 @@ To add a new configuration under your own namespace and with its own dependencie ## Configuration from main project files -If you prefer to include the asset configuration in the main project files, add it in [`webpack.config.js`](https://github.com/ibexa/recipes/blob/master/ibexa/oss/4.0/encore/webpack.config.js#L31). +If you prefer to include the asset configuration in the main project files, add it in [`webpack.config.js`](https://github.com/ibexa/recipes/blob/master/ibexa/oss/4.6/encore/webpack.config.js#L26). To overwrite the built-in assets, use the following configuration to replace, remove, or add asset files in `webpack.config.js`: ``` js +const ibexaConfigManager = require('./ibexa.webpack.config.manager.js'); + +//... + ibexaConfigManager.replace({ ibexaConfig, entryName: '', diff --git a/docs/administration/back_office/img/subitems/timeline_view.png b/docs/administration/back_office/img/subitems/timeline_view.png new file mode 100644 index 0000000000..ee10a02f49 Binary files /dev/null and b/docs/administration/back_office/img/subitems/timeline_view.png differ diff --git a/docs/administration/back_office/subitems_list.md b/docs/administration/back_office/subitems_list.md index 209f1493d0..fb938cc377 100644 --- a/docs/administration/back_office/subitems_list.md +++ b/docs/administration/back_office/subitems_list.md @@ -1,5 +1,5 @@ --- -description: Inject a sub-items list into your back office customizations. +description: Inject a sub-items list into your back office customizations or customize the view. --- # Sub-items list @@ -7,41 +7,91 @@ description: Inject a sub-items list into your back office customizations. The Sub-items List module is meant to be used as a part of the editorial interface of [[= product_name =]]. It provides an interface for listing the sub-items of any location. -!!! caution +## Create custom sub-items list view + +You can extend the Sub-items List module to replace an existing view or add your own. +The example below adds a new timeline view to highlight the modification date. + +![Sub-items List module using the new Timeline view](img/subitems/timeline_view.png "Sub-items List module using the new Timeline view") + +To recreate it, start by creating the components responsible for rendering the new view. +You can create two files: + +- `assets/js/timeline.view.component.js` responsible for rendering the whole view + +``` js +[[= include_file('code_samples/back_office/subitems/timeline_view/timeline.view.component.js') =]] +``` + +- `assets/js/timeline.view.item.component.js` responsible for rendering a single item + +``` js +[[= include_file('code_samples/back_office/subitems/timeline_view/timeline.view.item.component.js') =]] +``` + +Provide the necessary styling in `assets/scss/timeline.view.scss`. The example below uses [[= product_name =]]'s SCSS variables for consistency with the rest of the back office interface. + +``` scss +[[= include_file('code_samples/back_office/subitems/timeline_view/timeline.view.scss') =]] +``` - If you want to load the Sub-items module, you need to load the JS code for it in your view, as it's not available by default. +The last step is adding the view module to the list of available views in the system, by using the provided `registerView` function. + +You can create a new view by providing an unique identifier, or replace an existing one by reusing its identifier. +The existing view identifiers are defined as JavaScript constants in the `@ibexa-admin-ui-modules/sub-items/constants` module: + +- Grid view: `VIEW_MODE_GRID` constant +- Table view: `VIEW_MODE_TABLE` constant + +Create a file called `assets/js/registerTimelineView.js`: + +``` js +[[= include_file('code_samples/back_office/subitems/timeline_view/registerTimelineView.js') =]] +``` + +And include it into the back office using Webpack Encore, together with your custom styles. +See [configuring assets from main project files](importing_assets_from_bundle.md#configuration-from-main-project-files) to learn more about this mechanism. + +``` js +const ibexaConfigManager = require('./ibexa.webpack.config.manager.js'); + +//... + +ibexaConfigManager.add({ + ibexaConfig, + entryName: 'ibexa-admin-ui-layout-js', + newItems: [ + path.resolve(__dirname, './assets/js/registerTimelineView.js') + ], +}); + +ibexaConfigManager.add({ + ibexaConfig, + entryName: 'ibexa-admin-ui-layout-css', + newItems: [ + path.resolve(__dirname, './assets/scss/timeline.view.scss'), + ], +}); +``` + +Complete the task by running `composer run post-install-cmd`. ## Use sub-items list +!!! caution + + If you want to load the Sub-items module from your custom code, you need to load the JS code for it in your view, as it's not available by default. + With plain JS: ``` js -const containerNode = document.querySelector('#sub-items-container'); - - ReactDOM.render( - React.createElement(ibexa.modules.SubItems, { - parentLocationId: { Number }, - restInfo: { - token: { String }, - siteaccess: { String } - } - }), - containerNode - ); +[[= include_file('code_samples/back_office/subitems/render_subitems.js') =]] ``` With JSX: ``` jsx -const attrs = { - parentLocationId: {Number}, - restInfo: { - token: {String}, - siteaccess: {String} - } -}; - - +[[= include_file('code_samples/back_office/subitems/render_subitems.jsx') =]] ``` ## Properties list