diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 28119ea1d8..5f8e5d2beb 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -8430,6 +8430,11 @@ parameters: count: 1 path: src/lib/REST/Input/Parser/Operation.php + - + message: "#^Cannot call method resolve\\(\\) on Ibexa\\\\Contracts\\\\AdminUi\\\\REST\\\\ApplicationConfigRestResolverInterface\\|null\\.$#" + count: 1 + path: src/lib/REST/Output/ValueObjectVisitor/ApplicationConfigVisitor.php + - message: "#^Method Ibexa\\\\AdminUi\\\\REST\\\\Output\\\\ValueObjectVisitor\\\\BulkOperationResponse\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 diff --git a/src/bundle/Resources/config/services/controllers.yaml b/src/bundle/Resources/config/services/controllers.yaml index 0d3ebae38b..9c6adb1153 100644 --- a/src/bundle/Resources/config/services/controllers.yaml +++ b/src/bundle/Resources/config/services/controllers.yaml @@ -237,7 +237,6 @@ services: parent: Ibexa\Contracts\AdminUi\Controller\Controller autowire: true - Ibexa\Bundle\AdminUi\Controller\ApplicationConfigController: parent: Ibexa\Rest\Server\Controller autowire: true diff --git a/src/bundle/Resources/encore/ibexa.config.setup.js b/src/bundle/Resources/encore/ibexa.config.setup.js index eddeab341e..940ab5c287 100644 --- a/src/bundle/Resources/encore/ibexa.config.setup.js +++ b/src/bundle/Resources/encore/ibexa.config.setup.js @@ -3,5 +3,6 @@ const path = require('path'); module.exports = (Encore) => { Encore.addAliases({ '@ibexa-admin-ui': path.resolve('./vendor/ibexa/admin-ui'), + '@ibexa-admin-ui-modules': path.resolve('./vendor/ibexa/admin-ui/src/bundle/ui-dev/src/modules'), }); }; diff --git a/src/bundle/Resources/encore/ibexa.js.config.js b/src/bundle/Resources/encore/ibexa.js.config.js index 0b9e8bae74..72deeb9054 100644 --- a/src/bundle/Resources/encore/ibexa.js.config.js +++ b/src/bundle/Resources/encore/ibexa.js.config.js @@ -3,25 +3,7 @@ const fs = require('fs'); const translationsPath = path.resolve('./public/assets/translations/'); const fieldTypesPath = path.resolve(__dirname, '../public/js/scripts/fieldType/'); const layout = [ - path.resolve(__dirname, '../public/js/scripts/helpers/icon.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/location.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/text.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/request.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/notification.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/timezone.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/content.type.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/user.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/tooltips.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/table.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/cookies.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/tag.view.select.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/pagination.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/object.instances.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/middle.ellipsis.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/form.validation.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/form.error.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/system.helper.js'), - path.resolve(__dirname, '../public/js/scripts/helpers/highlight.helper.js'), + path.resolve(__dirname, '../public/js/scripts/helpers/config.loader.js'), path.resolve(__dirname, '../public/js/scripts/admin.format.date.js'), path.resolve(__dirname, '../public/js/scripts/core/draggable.js'), path.resolve(__dirname, '../public/js/scripts/core/dropdown.js'), @@ -219,6 +201,7 @@ module.exports = (Encore) => { path.resolve(__dirname, '../public/js/scripts/admin.settings.datetimeformat.update.js'), ]) .addEntry('ibexa-admin-ui-udw-js', [ + path.resolve(__dirname, '../../ui-dev/src/modules/universal-discovery/config.loader.js'), path.resolve(__dirname, '../../ui-dev/src/modules/universal-discovery/universal.discovery.module.js'), ]) .addEntry('ibexa-admin-ui-udw-tabs-js', [ @@ -251,6 +234,7 @@ module.exports = (Encore) => { .addEntry('ibexa-admin-ui-subitems-js', [path.resolve(__dirname, '../../ui-dev/src/modules/sub-items/sub.items.module.js')]) .addEntry('ibexa-admin-ui-content-tree-js', [ path.resolve(__dirname, '../../ui-dev/src/modules/content-tree/content.tree.module.js'), + path.resolve(__dirname, '../../ui-dev/src/modules/content-tree/config.loader.js'), ]) .addEntry('ibexa-admin-ui-url-management-js', [ path.resolve(__dirname, '../public/js/scripts/button.state.toggle.js'), diff --git a/src/bundle/Resources/public/js/scripts/core/date.time.picker.js b/src/bundle/Resources/public/js/scripts/core/date.time.picker.js index 3541f8352e..6bb2b08a70 100644 --- a/src/bundle/Resources/public/js/scripts/core/date.time.picker.js +++ b/src/bundle/Resources/public/js/scripts/core/date.time.picker.js @@ -1,118 +1,124 @@ -(function (global, doc, ibexa, flatpickr) { - const { convertDateToTimezone, formatShortDateTime } = ibexa.helpers.timezone; - const userTimezone = ibexa.adminUiConfig.timezone; - const DEFAULT_CONFIG = { - enableTime: true, - time_24hr: true, - formatDate: (date) => formatShortDateTime(date, null), - }; - - class DateTimePicker { - constructor(config) { - this.container = config.container; - this.fieldWrapper = this.container.querySelector('.ibexa-date-time-picker'); - this.inputField = this.fieldWrapper.querySelector('.ibexa-date-time-picker__input'); - this.actionsWrapper = this.fieldWrapper.querySelector('.ibexa-input-text-wrapper__actions'); - this.calendarBtn = this.actionsWrapper.querySelector('.ibexa-input-text-wrapper__action-btn--calendar'); - this.clearBtn = this.fieldWrapper.querySelector('.ibexa-input-text-wrapper__action-btn--clear'); - this.customOnChange = config.onChange; - - this.init = this.init.bind(this); - this.onChange = this.onChange.bind(this); - this.onInput = this.onInput.bind(this); - this.clear = this.clear.bind(this); - - this.flatpickrConfig = { - ...DEFAULT_CONFIG, - inline: this.fieldWrapper.classList.contains('ibexa-date-time-picker--inline-datetime-popup'), - onChange: this.onChange, - ignoredFocusElements: [this.actionsWrapper], - ...(config.flatpickrConfig ?? {}), - }; - - ibexa.helpers.objectInstances.setInstance(this.container, this); - } +import { getAdminUiConfig, getFlatpickr } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; +import { convertDateToTimezone, formatShortDateTime } from '../helpers/timezone.helper'; +import { setInstance } from '../helpers/object.instances'; + +const { ibexa } = window; + +const DEFAULT_CONFIG = { + enableTime: true, + time_24hr: true, + formatDate: (date) => formatShortDateTime(date, null), +}; + +class DateTimePicker { + constructor(config) { + this.container = config.container; + this.fieldWrapper = this.container.querySelector('.ibexa-date-time-picker'); + this.inputField = this.fieldWrapper.querySelector('.ibexa-date-time-picker__input'); + this.actionsWrapper = this.fieldWrapper.querySelector('.ibexa-input-text-wrapper__actions'); + this.calendarBtn = this.actionsWrapper.querySelector('.ibexa-input-text-wrapper__action-btn--calendar'); + this.clearBtn = this.fieldWrapper.querySelector('.ibexa-input-text-wrapper__action-btn--clear'); + this.customOnChange = config.onChange; + + this.init = this.init.bind(this); + this.onChange = this.onChange.bind(this); + this.onInput = this.onInput.bind(this); + this.clear = this.clear.bind(this); + + this.flatpickrConfig = { + ...DEFAULT_CONFIG, + inline: this.fieldWrapper.classList.contains('ibexa-date-time-picker--inline-datetime-popup'), + onChange: this.onChange, + ignoredFocusElements: [this.actionsWrapper], + ...(config.flatpickrConfig ?? {}), + }; + + setInstance(this.container, this); + } - clear() { - this.flatpickrInstance.clear(); - } + clear() { + this.flatpickrInstance.clear(); + } - onChange(dates) { - const isDateSelected = !!dates[0]; - const otherArguments = { inputField: this.inputField, dates }; + onChange(dates) { + const isDateSelected = !!dates[0]; + const otherArguments = { inputField: this.inputField, dates }; - if (!isDateSelected) { - this.inputField.dataset.timestamp = ''; + if (!isDateSelected) { + this.inputField.dataset.timestamp = ''; - this.customOnChange([''], otherArguments); + this.customOnChange([''], otherArguments); - return; - } + return; + } - const timestamps = dates.map((date) => { - const selectedDateWithUserTimezone = convertDateToTimezone(date, userTimezone, true); - const timestamp = Math.floor(selectedDateWithUserTimezone.valueOf() / 1000); + const timestamps = dates.map((date) => { + const { timezone } = getAdminUiConfig(); + const selectedDateWithUserTimezone = convertDateToTimezone(date, timezone, true); + const timestamp = Math.floor(selectedDateWithUserTimezone.valueOf() / 1000); - return timestamp; - }); + return timestamp; + }); - [this.inputField.dataset.timestamp] = timestamps; + [this.inputField.dataset.timestamp] = timestamps; - this.customOnChange(timestamps, otherArguments); - } + this.customOnChange(timestamps, otherArguments); + } - onInput(event) { - event.preventDefault(); + onInput(event) { + event.preventDefault(); - if (event.target.value === '' && this.inputField.dataset.timestamp !== '') { - this.clear(); - } + if (event.target.value === '' && this.inputField.dataset.timestamp !== '') { + this.clear(); } + } - onKeyUp(isMinute, event) { - const inputValue = event.target.value; - - if (inputValue.length === 0) { - return; - } + onKeyUp(isMinute, event) { + const inputValue = event.target.value; - const value = parseInt(inputValue, 10); + if (inputValue.length === 0) { + return; + } - if (typeof value === 'number' && value >= 0) { - const flatpickrDate = this.flatpickrInstance.selectedDates[0]; + const value = parseInt(inputValue, 10); - if (isMinute) { - flatpickrDate.setMinutes(value); - } else { - flatpickrDate.setHours(value); - } + if (typeof value === 'number' && value >= 0) { + const flatpickrDate = this.flatpickrInstance.selectedDates[0]; - if (this.flatpickrConfig.minDate.getTime() > flatpickrDate.getTime()) { - return; - } + if (isMinute) { + flatpickrDate.setMinutes(value); + } else { + flatpickrDate.setHours(value); + } - this.flatpickrInstance.setDate(flatpickrDate, true); + if (this.flatpickrConfig.minDate.getTime() > flatpickrDate.getTime()) { + return; } + + this.flatpickrInstance.setDate(flatpickrDate, true); } + } - init() { - this.flatpickrInstance = flatpickr(this.inputField, this.flatpickrConfig); - - this.inputField.addEventListener('input', this.onInput, false); - this.calendarBtn.addEventListener( - 'click', - () => { - this.flatpickrInstance.open(); - }, - false, - ); - - if (this.flatpickrInstance.config.enableTime) { - this.flatpickrInstance.minuteElement.addEventListener('keyup', this.onKeyUp.bind(this, true), false); - this.flatpickrInstance.hourElement.addEventListener('keyup', this.onKeyUp.bind(this, false), false); - } + init() { + const flatpickr = getFlatpickr(); + this.flatpickrInstance = flatpickr(this.inputField, this.flatpickrConfig); + + this.inputField.addEventListener('input', this.onInput, false); + this.calendarBtn.addEventListener( + 'click', + () => { + this.flatpickrInstance.open(); + }, + false, + ); + + if (this.flatpickrInstance.config.enableTime) { + this.flatpickrInstance.minuteElement.addEventListener('keyup', this.onKeyUp.bind(this, true), false); + this.flatpickrInstance.hourElement.addEventListener('keyup', this.onKeyUp.bind(this, false), false); } } +} + +ibexa?.addConfig('core.DateTimePicker', DateTimePicker); - ibexa.addConfig('core.DateTimePicker', DateTimePicker); -})(window, window.document, window.ibexa, window.flatpickr); +export { DateTimePicker }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/config.loader.js b/src/bundle/Resources/public/js/scripts/helpers/config.loader.js new file mode 100644 index 0000000000..038cc15882 --- /dev/null +++ b/src/bundle/Resources/public/js/scripts/helpers/config.loader.js @@ -0,0 +1,41 @@ +import * as contentType from './content.type.helper'; +import * as cookies from './cookies.helper'; +import * as formError from './form.error.helper'; +import * as formValidation from './form.validation.helper'; +import * as highlight from './highlight.helper'; +import * as icon from './icon.helper'; +import * as location from './location.helper'; +import * as middleEllipsis from './middle.ellipsis'; +import * as notification from './notification.helper'; +import * as objectInstances from './object.instances'; +import * as pagination from './pagination.helper'; +import * as request from './request.helper'; +import * as system from './system.helper'; +import * as table from './table.helper'; +import * as tagViewSelect from './tag.view.select.helper'; +import * as text from './text.helper'; +import * as timezone from './timezone.helper'; +import * as tooltips from './tooltips.helper'; +import * as user from './user.helper'; + +(function (ibexa) { + ibexa.addConfig('helpers.contentType', contentType); + ibexa.addConfig('helpers.cookies', cookies); + ibexa.addConfig('helpers.formError', formError); + ibexa.addConfig('helpers.formValidation', formValidation); + ibexa.addConfig('helpers.highlight', highlight); + ibexa.addConfig('helpers.icon', icon); + ibexa.addConfig('helpers.location', location); + ibexa.addConfig('helpers.ellipsis.middle', middleEllipsis); + ibexa.addConfig('helpers.notification', notification); + ibexa.addConfig('helpers.objectInstances', objectInstances); + ibexa.addConfig('helpers.pagination', pagination); + ibexa.addConfig('helpers.request', request); + ibexa.addConfig('helpers.system', system); + ibexa.addConfig('helpers.table', table); + ibexa.addConfig('helpers.tagViewSelect', tagViewSelect); + ibexa.addConfig('helpers.text', text); + ibexa.addConfig('helpers.timezone', timezone); + ibexa.addConfig('helpers.tooltips', tooltips); + ibexa.addConfig('helpers.user', user); +})(window.ibexa); diff --git a/src/bundle/Resources/public/js/scripts/helpers/content.type.helper.js b/src/bundle/Resources/public/js/scripts/helpers/content.type.helper.js index 5a729f4773..0d1d8ed005 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/content.type.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/content.type.helper.js @@ -1,106 +1,100 @@ -(function (global, doc, ibexa) { - let contentTypesDataMap = null; - let contentTypesDataMapByHref = null; - - /** - * Creates map with content types identifiers as keys for faster lookup - * - * @function createContentTypeDataMap - * @returns {Object} contentTypesDataMap - */ - const createContentTypeDataMap = () => - Object.values(ibexa.adminUiConfig.contentTypes).reduce((contentTypeDataMap, contentTypeGroup) => { - for (const contentTypeData of contentTypeGroup) { - contentTypeDataMap[contentTypeData.identifier] = contentTypeData; - } - - return contentTypeDataMap; - }, {}); - - const createContentTypeDataMapByHref = () => - Object.values(ibexa.adminUiConfig.contentTypes).reduce((contentTypeDataMapByHref, contentTypeGroup) => { - for (const contentTypeData of contentTypeGroup) { - contentTypeDataMapByHref[contentTypeData.href] = contentTypeData; - } - - return contentTypeDataMapByHref; - }, {}); - - /** - * Returns an URL to a content type icon - * - * @function getContentTypeIcon - * @param {String} contentTypeIdentifier - * @returns {String|null} url to icon - */ - const getContentTypeIconUrl = (contentTypeIdentifier) => { - if (!contentTypesDataMap) { - contentTypesDataMap = createContentTypeDataMap(); +import { getAdminUiConfig } from './context.helper'; + +let contentTypesDataMap = null; +let contentTypesDataMapByHref = null; + +/** + * Creates map with content types identifiers as keys for faster lookup + * + * @function createContentTypeDataMap + * @returns {Object} contentTypesDataMap + */ +const createContentTypeDataMap = () => + Object.values(getAdminUiConfig().contentTypes).reduce((contentTypeDataMap, contentTypeGroup) => { + for (const contentTypeData of contentTypeGroup) { + contentTypeDataMap[contentTypeData.identifier] = contentTypeData; } - if (!contentTypeIdentifier || !contentTypesDataMap[contentTypeIdentifier]) { - return null; - } - - const iconUrl = contentTypesDataMap[contentTypeIdentifier].thumbnail; - - return iconUrl; - }; - - /** - * Returns contentType name from contentType identifier - * - * @function getContentTypeName - * @param {String} contentTypeIdentifier - * @returns {String|null} contentType name - */ - const getContentTypeName = (contentTypeIdentifier) => { - if (!contentTypesDataMap) { - contentTypesDataMap = createContentTypeDataMap(); - } - - if (!contentTypeIdentifier || !contentTypesDataMap[contentTypeIdentifier]) { - return null; - } - - return contentTypesDataMap[contentTypeIdentifier].name; - }; - - const getContentTypeIconUrlByHref = (contentTypeHref) => { - if (!contentTypesDataMapByHref) { - contentTypesDataMapByHref = createContentTypeDataMapByHref(); - } - - if (!contentTypeHref || !contentTypesDataMapByHref[contentTypeHref]) { - return null; - } - - const iconUrl = contentTypesDataMapByHref[contentTypeHref].thumbnail; - - return iconUrl; - }; - - const getContentTypeDataByHref = (contentTypeHref) => { - if (!contentTypesDataMapByHref) { - contentTypesDataMapByHref = createContentTypeDataMapByHref(); - } + return contentTypeDataMap; + }, {}); - if (!contentTypeHref || !contentTypesDataMapByHref[contentTypeHref]) { - return null; +const createContentTypeDataMapByHref = () => + Object.values(getAdminUiConfig().contentTypes).reduce((contentTypeDataMapByHref, contentTypeGroup) => { + for (const contentTypeData of contentTypeGroup) { + contentTypeDataMapByHref[contentTypeData.href] = contentTypeData; } - return contentTypesDataMapByHref[contentTypeHref]; - }; - - const getContentTypeNameByHref = (contentTypeHref) => { - return getContentTypeDataByHref(contentTypeHref)?.name ?? null; - }; - - ibexa.addConfig('helpers.contentType', { - getContentTypeIconUrl, - getContentTypeName, - getContentTypeIconUrlByHref, - getContentTypeDataByHref, - getContentTypeNameByHref, - }); -})(window, window.document, window.ibexa); + return contentTypeDataMapByHref; + }, {}); + +/** + * Returns an URL to a content type icon + * + * @function getContentTypeIcon + * @param {String} contentTypeIdentifier + * @returns {String|null} url to icon + */ +const getContentTypeIconUrl = (contentTypeIdentifier) => { + if (!contentTypesDataMap) { + contentTypesDataMap = createContentTypeDataMap(); + } + + if (!contentTypeIdentifier || !contentTypesDataMap[contentTypeIdentifier]) { + return null; + } + + const iconUrl = contentTypesDataMap[contentTypeIdentifier].thumbnail; + + return iconUrl; +}; + +/** + * Returns contentType name from contentType identifier + * + * @function getContentTypeName + * @param {String} contentTypeIdentifier + * @returns {String|null} contentType name + */ +const getContentTypeName = (contentTypeIdentifier) => { + if (!contentTypesDataMap) { + contentTypesDataMap = createContentTypeDataMap(); + } + + if (!contentTypeIdentifier || !contentTypesDataMap[contentTypeIdentifier]) { + return null; + } + + return contentTypesDataMap[contentTypeIdentifier].name; +}; + +const getContentTypeIconUrlByHref = (contentTypeHref) => { + if (!contentTypesDataMapByHref) { + contentTypesDataMapByHref = createContentTypeDataMapByHref(); + } + + if (!contentTypeHref || !contentTypesDataMapByHref[contentTypeHref]) { + return null; + } + + const iconUrl = contentTypesDataMapByHref[contentTypeHref].thumbnail; + + return iconUrl; +}; + +const getContentTypeNameByHref = (contentTypeHref) => { + return getContentTypeDataByHref(contentTypeHref)?.name ?? null; +}; + +const getContentTypeDataByHref = (contentTypeHref) => { + if (!contentTypesDataMapByHref) { + contentTypesDataMapByHref = createContentTypeDataMapByHref(); + } + + if (!contentTypeHref || !contentTypesDataMapByHref[contentTypeHref]) { + return null; + } + + return contentTypesDataMapByHref[contentTypeHref]; +}; + +export { getContentTypeIconUrl, getContentTypeName, getContentTypeIconUrlByHref, getContentTypeDataByHref, getContentTypeNameByHref }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/context.helper.js b/src/bundle/Resources/public/js/scripts/helpers/context.helper.js new file mode 100644 index 0000000000..016b150042 --- /dev/null +++ b/src/bundle/Resources/public/js/scripts/helpers/context.helper.js @@ -0,0 +1,55 @@ +let { bootstrap, flatpickr, moment, Popper, Routing, Translator } = window; +let adminUiConfig = window.ibexa?.adminUiConfig; +const restInfo = { + accessToken: null, + instanceUrl: window.location.origin, + token: document.querySelector('meta[name="CSRF-Token"]')?.content, + siteaccess: document.querySelector('meta[name="SiteAccess"]')?.content, +}; + +export const setRestInfo = ({ instanceUrl, token, csrfToken, siteaccess }) => { + restInfo.instanceUrl = instanceUrl ?? restInfo.instanceUrl; + restInfo.token = token ?? restInfo.token; + restInfo.csrfToken = csrfToken ?? restInfo.csrfToken; + restInfo.siteaccess = siteaccess ?? restInfo.siteaccess; +}; +export const setAdminUiConfig = (loadedAdminUiConfig) => (adminUiConfig = loadedAdminUiConfig); +export const setBootstrap = (bootstrapInstance, forceSet = false) => { + if (!bootstrap || forceSet) { + bootstrap = bootstrapInstance; + } +}; +export const setFlatpickr = (flatpickrInstance, forceSet = false) => { + if (!flatpickr || forceSet) { + flatpickr = flatpickrInstance; + } +}; +export const setMoment = (momentInstance, forceSet = false) => { + if (!moment || forceSet) { + moment = momentInstance; + } +}; +export const setPopper = (PopperInstance, forceSet = false) => { + if (!Popper || forceSet) { + Popper = PopperInstance; + } +}; +export const setRouting = (RoutingInstance, forceSet = false) => { + if (!Routing || forceSet) { + Routing = RoutingInstance; + } +}; +export const setTranslator = (TranslatorInstance, forceSet = false) => { + if (!Translator || forceSet) { + Translator = TranslatorInstance; + } +}; + +export const getAdminUiConfig = () => adminUiConfig; +export const getBootstrap = () => bootstrap; +export const getFlatpickr = () => flatpickr; +export const getMoment = () => moment; +export const getPopper = () => Popper; +export const getRouting = () => Routing; +export const getTranslator = () => Translator; +export const getRestInfo = () => restInfo; diff --git a/src/bundle/Resources/public/js/scripts/helpers/cookies.helper.js b/src/bundle/Resources/public/js/scripts/helpers/cookies.helper.js index 4eebda855e..700a844e90 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/cookies.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/cookies.helper.js @@ -1,30 +1,27 @@ -(function (global, doc, ibexa) { - const { backOfficePath } = ibexa.adminUiConfig; - const setBackOfficeCookie = (name, value, maxAgeDays = 356, path = backOfficePath) => { - setCookie(name, value, maxAgeDays, path); - }; - const setCookie = (name, value, maxAgeDays = 356, path = '/') => { - const maxAge = maxAgeDays * 24 * 60 * 60; +import { getAdminUiConfig } from './context.helper'; - doc.cookie = `${name}=${value};max-age=${maxAge};path=${path}`; - }; - const getCookie = (name) => { - const decodedCookie = decodeURIComponent(doc.cookie); - const cookiesArray = decodedCookie.split(';'); +const { document: doc } = window; - const cookieValue = cookiesArray.find((cookie) => { - const cookieString = cookie.trim(); - const seachingString = `${name}=`; +const setBackOfficeCookie = (name, value, maxAgeDays = 356, path = getAdminUiConfig().backOfficePath) => { + setCookie(name, value, maxAgeDays, path); +}; +const setCookie = (name, value, maxAgeDays = 356, path = '/') => { + const maxAge = maxAgeDays * 24 * 60 * 60; - return cookieString.indexOf(seachingString) === 0; - }); + doc.cookie = `${name}=${value};max-age=${maxAge};path=${path}`; +}; +const getCookie = (name) => { + const decodedCookie = decodeURIComponent(doc.cookie); + const cookiesArray = decodedCookie.split(';'); - return cookieValue ? cookieValue.split('=')[1] : null; - }; + const cookieValue = cookiesArray.find((cookie) => { + const cookieString = cookie.trim(); + const seachingString = `${name}=`; - ibexa.addConfig('helpers.cookies', { - getCookie, - setCookie, - setBackOfficeCookie, + return cookieString.indexOf(seachingString) === 0; }); -})(window, window.document, window.ibexa); + + return cookieValue ? cookieValue.split('=')[1] : null; +}; + +export { getCookie, setCookie, setBackOfficeCookie }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/form.error.helper.js b/src/bundle/Resources/public/js/scripts/helpers/form.error.helper.js index 6bc39c4760..c5f8c0f52e 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/form.error.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/form.error.helper.js @@ -1,12 +1,12 @@ -(function (global, doc, ibexa) { - // @deprecated, will be removed in 5.0 - ibexa.addConfig('helpers.formError', { - formatLine: (...args) => { - console.warn( - 'helpers.formError.formatLine method is deprecated and will be removed in 5.0, please use helpers.formValidation.formatErrorLine instead.', - ); +import { formatErrorLine } from './form.validation.helper'; - return ibexa.helpers.formValidation.formatErrorLine(...args); - }, - }); -})(window, window.document, window.ibexa); +// @deprecated, will be removed in 5.0 +const formatLine = (...args) => { + console.warn( + 'helpers.formError.formatLine method is deprecated and will be removed in 5.0, please use helpers.formValidation.formatErrorLine instead.', + ); + + return formatErrorLine(...args); +}; + +export { formatLine }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/form.validation.helper.js b/src/bundle/Resources/public/js/scripts/helpers/form.validation.helper.js index 7cfedc9457..aae1b04286 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/form.validation.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/form.validation.helper.js @@ -1,52 +1,52 @@ -(function (global, doc, ibexa, Translator) { - const formatErrorLine = (errorMessage) => { - const errorIcon = ` - - `; - const container = document.createElement('em'); - const errorMessageNode = document.createTextNode(errorMessage); - - container.classList.add('ibexa-form-error__row'); - container.insertAdjacentHTML('beforeend', errorIcon); - container.append(errorMessageNode); - - return container; +import { getTranslator } from './context.helper'; +import { getIconPath } from './icon.helper'; + +const formatErrorLine = (errorMessage) => { + const errorIcon = ` + + `; + const container = document.createElement('em'); + const errorMessageNode = document.createTextNode(errorMessage); + + container.classList.add('ibexa-form-error__row'); + container.insertAdjacentHTML('beforeend', errorIcon); + container.append(errorMessageNode); + + return container; +}; +const checkIsEmpty = (field) => { + let errorMessage = ''; + const Translator = getTranslator(); + const input = field.querySelector('.ibexa-input'); + const label = field.querySelector('.ibexa-label'); + + if (label) { + const fieldName = label.innerText; + + errorMessage = Translator.trans(/*@Desc("%fieldName% cannot be empty")*/ 'error.required.field', { fieldName }, 'forms'); + } else { + errorMessage = Translator.trans(/*@Desc("This value should not be blank")*/ 'error.required.field_not_blank', {}, 'forms'); + } + + return { + isValid: input.value, + errorMessage, }; - const checkIsEmpty = (field) => { - let errorMessage = ''; - const input = field.querySelector('.ibexa-input'); - const label = field.querySelector('.ibexa-label'); - - if (label) { - const fieldName = label.innerText; - - errorMessage = Translator.trans(/*@Desc("%fieldName% cannot be empty")*/ 'error.required.field', { fieldName }, 'forms'); - } else { - errorMessage = Translator.trans(/*@Desc("This value should not be blank")*/ 'error.required.field_not_blank', {}, 'forms'); - } - - return { - isValid: input.value, - errorMessage, - }; - }; - const validateIsEmptyField = (field) => { - const input = field.querySelector('.ibexa-input'); - const errorWrapper = field.querySelector('.ibexa-form-error'); - const validatorOutput = checkIsEmpty(field); - const { isValid, errorMessage } = validatorOutput; +}; +const validateIsEmptyField = (field) => { + const input = field.querySelector('.ibexa-input'); + const errorWrapper = field.querySelector('.ibexa-form-error'); + const validatorOutput = checkIsEmpty(field); + const { isValid, errorMessage } = validatorOutput; - input.classList.toggle('is-invalid', !isValid); - errorWrapper.innerText = ''; + input.classList.toggle('is-invalid', !isValid); + errorWrapper.innerText = ''; - if (!isValid) { - errorWrapper.append(formatErrorLine(errorMessage)); - } + if (!isValid) { + errorWrapper.append(formatErrorLine(errorMessage)); + } - return validatorOutput; - }; - ibexa.addConfig('helpers.formValidation', { - formatErrorLine, - validateIsEmptyField, - }); -})(window, window.document, window.ibexa, window.Translator); + return validatorOutput; +}; + +export { formatErrorLine, validateIsEmptyField }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/highlight.helper.js b/src/bundle/Resources/public/js/scripts/helpers/highlight.helper.js index c3a11812cc..961dd0a8ea 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/highlight.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/highlight.helper.js @@ -1,28 +1,25 @@ -(function (global, doc, ibexa) { - const { escapeHTML } = ibexa.helpers.text; - const highlightText = (searchText, string, template) => { - const stringLowerCase = string.toLowerCase(); - const searchTextLowerCase = searchText.toLowerCase(); - const matches = stringLowerCase.matchAll(searchTextLowerCase); - const stringArray = []; - let previousIndex = 0; +import { escapeHTML } from './text.helper'; - for (const match of matches) { - const endOfSearchTextIndex = match.index + searchText.length; - const renderedTemplate = template.replace('{{ highlightText }}', escapeHTML(string.slice(match.index, endOfSearchTextIndex))); +const highlightText = (searchText, string, template) => { + const stringLowerCase = string.toLowerCase(); + const searchTextLowerCase = searchText.toLowerCase(); + const matches = stringLowerCase.matchAll(searchTextLowerCase); + const stringArray = []; + let previousIndex = 0; - stringArray.push(escapeHTML(string.slice(previousIndex, match.index))); - stringArray.push(renderedTemplate); + for (const match of matches) { + const endOfSearchTextIndex = match.index + searchText.length; + const renderedTemplate = template.replace('{{ highlightText }}', escapeHTML(string.slice(match.index, endOfSearchTextIndex))); - previousIndex = match.index + searchText.length; - } + stringArray.push(escapeHTML(string.slice(previousIndex, match.index))); + stringArray.push(renderedTemplate); - stringArray.push(escapeHTML(string.slice(previousIndex))); + previousIndex = match.index + searchText.length; + } - return stringArray.join(''); - }; + stringArray.push(escapeHTML(string.slice(previousIndex))); - ibexa.addConfig('helpers.highlight', { - highlightText, - }); -})(window, window.document, window.ibexa); + return stringArray.join(''); +}; + +export { highlightText }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/icon.helper.js b/src/bundle/Resources/public/js/scripts/helpers/icon.helper.js index 14e98289a9..9d7c000a3e 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/icon.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/icon.helper.js @@ -1,11 +1,15 @@ -(function (global, doc, ibexa) { - const getIconPath = (path, iconSet = ibexa.adminUiConfig.iconPaths.defaultIconSet) => { - const iconSetPath = ibexa.adminUiConfig.iconPaths.iconSets[iconSet]; +import { getAdminUiConfig } from './context.helper'; - return `${iconSetPath}#${path}`; - }; +const getIconPath = (path, iconSet) => { + const adminUiConfig = getAdminUiConfig(); - ibexa.addConfig('helpers.icon', { - getIconPath, - }); -})(window, window.document, window.ibexa); + if (!iconSet) { + iconSet = adminUiConfig.iconPaths.defaultIconSet; + } + + const iconSetPath = adminUiConfig.iconPaths.iconSets[iconSet]; + + return `${iconSetPath}#${path}`; +}; + +export { getIconPath }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/location.helper.js b/src/bundle/Resources/public/js/scripts/helpers/location.helper.js index b27e565dda..6898569789 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/location.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/location.helper.js @@ -1,57 +1,59 @@ -(function (global, doc, ibexa, Translator) { - const token = doc.querySelector('meta[name="CSRF-Token"]').content; - const siteaccess = doc.querySelector('meta[name="SiteAccess"]').content; - const removeRootFromPathString = (pathString) => { - const pathArray = pathString.split('/').filter((id) => id); +import { escapeHTML } from './text.helper'; +import { getJsonFromResponse, getRequestHeaders, getRequestMode } from './request.helper'; +import { showErrorNotification } from './notification.helper'; +import { getRestInfo, getTranslator } from './context.helper'; - return pathArray.splice(1, pathArray.length - 1); - }; - const buildLocationsBreadcrumbs = (locations) => - locations.map((Location) => ibexa.helpers.text.escapeHTML(Location.ContentInfo.Content.TranslatedName)).join(' / '); - const findLocationsByIds = (idList, callback) => { - const body = JSON.stringify({ - ViewInput: { - identifier: `locations-by-path-string-${idList.join('-')}`, - public: false, - LocationQuery: { - FacetBuilders: {}, - SortClauses: { SectionIdentifier: 'ascending' }, - Filter: { LocationIdCriterion: idList.join(',') }, - limit: 50, - offset: 0, - }, +const removeRootFromPathString = (pathString) => { + const pathArray = pathString.split('/').filter((id) => id); + + return pathArray.splice(1, pathArray.length - 1); +}; +const buildLocationsBreadcrumbs = (locations) => + locations.map((Location) => escapeHTML(Location.ContentInfo.Content.TranslatedName)).join(' / '); +const findLocationsByIds = (idList, callback) => { + const { token, siteaccess, accessToken, instanceUrl } = getRestInfo(); + const Translator = getTranslator(); + const body = JSON.stringify({ + ViewInput: { + identifier: `locations-by-path-string-${idList.join('-')}`, + public: false, + LocationQuery: { + FacetBuilders: {}, + SortClauses: { SectionIdentifier: 'ascending' }, + Filter: { LocationIdCriterion: idList.join(',') }, + limit: 50, + offset: 0, }, - }); - const request = new Request('/api/ibexa/v2/views', { - method: 'POST', - headers: { + }, + }); + const request = new Request(`${instanceUrl}/api/ibexa/v2/views`, { + method: 'POST', + headers: getRequestHeaders({ + token, + siteaccess, + accessToken, + extraHeaders: { Accept: 'application/vnd.ibexa.api.View+json; version=1.1', 'Content-Type': 'application/vnd.ibexa.api.ViewInput+json; version=1.1', 'X-Requested-With': 'XMLHttpRequest', - 'X-Siteaccess': siteaccess, - 'X-CSRF-Token': token, }, - body, - mode: 'same-origin', - credentials: 'same-origin', - }); - const errorMessage = Translator.trans( - /*@Desc("Cannot find children Locations with ID %idList%")*/ 'select_location.error', - { idList: idList.join(',') }, - 'ibexa_universal_discovery_widget', - ); + }), + body, + mode: getRequestMode({ instanceUrl }), + credentials: 'same-origin', + }); + const errorMessage = Translator.trans( + /*@Desc("Cannot find children Locations with ID %idList%")*/ 'select_location.error', + { idList: idList.join(',') }, + 'ibexa_universal_discovery_widget', + ); - fetch(request) - .then(ibexa.helpers.request.getJsonFromResponse) - .then((viewData) => viewData.View.Result.searchHits.searchHit) - .then((searchHits) => searchHits.map((searchHit) => searchHit.value.Location)) - .then(callback) - .catch(() => ibexa.helpers.notification.showErrorNotification(errorMessage)); - }; + fetch(request) + .then(getJsonFromResponse) + .then((viewData) => viewData.View.Result.searchHits.searchHit) + .then((searchHits) => searchHits.map((searchHit) => searchHit.value.Location)) + .then(callback) + .catch(() => showErrorNotification(errorMessage)); +}; - ibexa.addConfig('helpers.location', { - removeRootFromPathString, - findLocationsByIds, - buildLocationsBreadcrumbs, - }); -})(window, window.document, window.ibexa, window.Translator); +export { removeRootFromPathString, findLocationsByIds, buildLocationsBreadcrumbs }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/middle.ellipsis.js b/src/bundle/Resources/public/js/scripts/helpers/middle.ellipsis.js index 248238284b..43e53abb4b 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/middle.ellipsis.js +++ b/src/bundle/Resources/public/js/scripts/helpers/middle.ellipsis.js @@ -1,58 +1,56 @@ -(function (global, doc, ibexa) { - const resizeEllipsisObserver = new ResizeObserver((entries) => { - entries.forEach((entry) => { - parse(entry.target); - }); +import { parse as parseTooltips } from './tooltips.helper'; +import { escapeHTML } from './text.helper'; + +const { document: doc } = window; +const resizeEllipsisObserver = new ResizeObserver((entries) => { + entries.forEach((entry) => { + parse(entry.target); }); - const parse = (baseElement = doc) => { - const isHTMLElement = baseElement instanceof Element || baseElement instanceof Document; +}); +const parse = (baseElement = doc) => { + const isHTMLElement = baseElement instanceof Element || baseElement instanceof Document; - if (!isHTMLElement) { - console.warn('Provided element does not belong to Document interface'); + if (!isHTMLElement) { + console.warn('Provided element does not belong to Document interface'); - return; - } + return; + } + + const middleEllipsisContainers = [...baseElement.querySelectorAll('.ibexa-middle-ellipsis')]; + + if (baseElement instanceof Element && baseElement.classList.contains('ibexa-middle-ellipsis')) { + middleEllipsisContainers.push(baseElement); + } - const middleEllipsisContainers = [...baseElement.querySelectorAll('.ibexa-middle-ellipsis')]; + middleEllipsisContainers.forEach((middleEllipsisContainer) => { + const partStart = middleEllipsisContainer.querySelector('.ibexa-middle-ellipsis__name--start'); + const isEllipsized = partStart.scrollWidth > partStart.offsetWidth; - if (baseElement instanceof Element && baseElement.classList.contains('ibexa-middle-ellipsis')) { - middleEllipsisContainers.push(baseElement); + if (!isEllipsized) { + middleEllipsisContainer.dataset.bsOriginalTitle = ''; + } else { + const partStartContentNode = partStart.querySelector('.ibexa-middle-ellipsis__name-ellipsized'); + + middleEllipsisContainer.dataset.bsOriginalTitle = partStartContentNode.innerHTML; } - middleEllipsisContainers.forEach((middleEllipsisContainer) => { - const partStart = middleEllipsisContainer.querySelector('.ibexa-middle-ellipsis__name--start'); - const isEllipsized = partStart.scrollWidth > partStart.offsetWidth; - - if (!isEllipsized) { - middleEllipsisContainer.dataset.bsOriginalTitle = ''; - } else { - const partStartContentNode = partStart.querySelector('.ibexa-middle-ellipsis__name-ellipsized'); - - middleEllipsisContainer.dataset.bsOriginalTitle = partStartContentNode.innerHTML; - } - - middleEllipsisContainer.classList.toggle('ibexa-middle-ellipsis--ellipsized', isEllipsized); - ibexa.helpers.tooltips.parse(middleEllipsisContainer); - - resizeEllipsisObserver.observe(middleEllipsisContainer); - }); - }; - // @deprecated, will be removed in 5.0 - const parseAll = () => parse(doc); - const update = (baseElement, content) => { - const contentElements = [...baseElement.querySelectorAll('.ibexa-middle-ellipsis__name-ellipsized')]; - const contentEscaped = ibexa.helpers.text.escapeHTML(content); - - baseElement.dataset.bsOriginalTitle = contentEscaped; - contentElements.forEach((contentElement) => { - contentElement.innerHTML = contentEscaped; - }); - parse(baseElement); - }; - - ibexa.addConfig('helpers.ellipsis.middle', { - parse, - parseAll, - update, + middleEllipsisContainer.classList.toggle('ibexa-middle-ellipsis--ellipsized', isEllipsized); + parseTooltips(middleEllipsisContainer); + + resizeEllipsisObserver.observe(middleEllipsisContainer); }); -})(window, window.document, window.ibexa); +}; +// @deprecated, will be removed in 5.0 +const parseAll = () => parse(doc); +const update = (baseElement, content) => { + const contentElements = [...baseElement.querySelectorAll('.ibexa-middle-ellipsis__name-ellipsized')]; + const contentEscaped = escapeHTML(content); + + baseElement.dataset.bsOriginalTitle = contentEscaped; + contentElements.forEach((contentElement) => { + contentElement.innerHTML = contentEscaped; + }); + parse(baseElement); +}; + +export { parse, parseAll, update }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/notification.helper.js b/src/bundle/Resources/public/js/scripts/helpers/notification.helper.js index 48d3351edc..fbaee33f5a 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/notification.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/notification.helper.js @@ -1,98 +1,92 @@ -(function (global, doc, ibexa) { - const NOTIFICATION_INFO_LABEL = 'info'; - const NOTIFICATION_SUCCESS_LABEL = 'success'; - const NOTIFICATION_WARNING_LABEL = 'warning'; - const NOTIFICATION_ERROR_LABEL = 'error'; +const { document: doc } = window; - /** - * Dispatches notification event - * - * @function showNotification - * @param {Object} detail - * @param {String} detail.message - * @param {String} detail.label - * @param {Function} [detail.onShow] to be called after notification Node was added - * @param {Object} detail.rawPlaceholdersMap - */ - const showNotification = (detail) => { - const event = new CustomEvent('ibexa-notify', { detail }); +const NOTIFICATION_INFO_LABEL = 'info'; +const NOTIFICATION_SUCCESS_LABEL = 'success'; +const NOTIFICATION_WARNING_LABEL = 'warning'; +const NOTIFICATION_ERROR_LABEL = 'error'; - doc.body.dispatchEvent(event); - }; +/** + * Dispatches notification event + * + * @function showNotification + * @param {Object} detail + * @param {String} detail.message + * @param {String} detail.label + * @param {Function} [detail.onShow] to be called after notification Node was added + * @param {Object} detail.rawPlaceholdersMap + */ +const showNotification = (detail) => { + const event = new CustomEvent('ibexa-notify', { detail }); - /** - * Dispatches info notification event - * - * @function showInfoNotification - * @param {String} message - * @param {Function} [onShow] to be called after notification Node was added - * @param {Object} rawPlaceholdersMap - */ - const showInfoNotification = (message, onShow, rawPlaceholdersMap = {}) => - showNotification({ - message, - label: NOTIFICATION_INFO_LABEL, - onShow, - rawPlaceholdersMap, - }); + doc.body.dispatchEvent(event); +}; - /** - * Dispatches success notification event - * - * @function showSuccessNotification - * @param {String} message - * @param {Function} [onShow] to be called after notification Node was added - * @param {Object} rawPlaceholdersMap - */ - const showSuccessNotification = (message, onShow, rawPlaceholdersMap = {}) => - showNotification({ - message, - label: NOTIFICATION_SUCCESS_LABEL, - onShow, - rawPlaceholdersMap, - }); +/** + * Dispatches info notification event + * + * @function showInfoNotification + * @param {String} message + * @param {Function} [onShow] to be called after notification Node was added + * @param {Object} rawPlaceholdersMap + */ +const showInfoNotification = (message, onShow, rawPlaceholdersMap = {}) => + showNotification({ + message, + label: NOTIFICATION_INFO_LABEL, + onShow, + rawPlaceholdersMap, + }); - /** - * Dispatches warning notification event - * - * @function showWarningNotification - * @param {String} message - * @param {Function} [onShow] to be called after notification Node was added - * @param {Object} rawPlaceholdersMap - */ - const showWarningNotification = (message, onShow, rawPlaceholdersMap = {}) => - showNotification({ - message, - label: NOTIFICATION_WARNING_LABEL, - onShow, - rawPlaceholdersMap, - }); +/** + * Dispatches success notification event + * + * @function showSuccessNotification + * @param {String} message + * @param {Function} [onShow] to be called after notification Node was added + * @param {Object} rawPlaceholdersMap + */ +const showSuccessNotification = (message, onShow, rawPlaceholdersMap = {}) => + showNotification({ + message, + label: NOTIFICATION_SUCCESS_LABEL, + onShow, + rawPlaceholdersMap, + }); - /** - * Dispatches error notification event - * - * @function showErrorNotification - * @param {(string | Error)} error - * @param {Function} [onShow] to be called after notification Node was added - * @param {Object} rawPlaceholdersMap - */ - const showErrorNotification = (error, onShow, rawPlaceholdersMap = {}) => { - const isErrorObj = error instanceof Error; - const message = isErrorObj ? error.message : error; +/** + * Dispatches warning notification event + * + * @function showWarningNotification + * @param {String} message + * @param {Function} [onShow] to be called after notification Node was added + * @param {Object} rawPlaceholdersMap + */ +const showWarningNotification = (message, onShow, rawPlaceholdersMap = {}) => + showNotification({ + message, + label: NOTIFICATION_WARNING_LABEL, + onShow, + rawPlaceholdersMap, + }); - showNotification({ - message, - label: NOTIFICATION_ERROR_LABEL, - onShow, - rawPlaceholdersMap, - }); - }; +/** + * Dispatches error notification event + * + * @function showErrorNotification + * @param {(string | Error)} error + * @param {Function} [onShow] to be called after notification Node was added + * @param {Object} rawPlaceholdersMap + */ +const showErrorNotification = (error, onShow, rawPlaceholdersMap = {}) => { + const isErrorObj = error instanceof Error; + const message = isErrorObj ? error.message : error; - ibexa.addConfig('helpers.notification', { - showNotification, - showInfoNotification, - showSuccessNotification, - showWarningNotification, - showErrorNotification, + showNotification({ + message, + label: NOTIFICATION_ERROR_LABEL, + onShow, + rawPlaceholdersMap, }); -})(window, window.document, window.ibexa); +}; + +export { showNotification, showInfoNotification, showSuccessNotification, showWarningNotification, showErrorNotification }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/object.instances.js b/src/bundle/Resources/public/js/scripts/helpers/object.instances.js index f5dfb49fb7..05f8632c21 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/object.instances.js +++ b/src/bundle/Resources/public/js/scripts/helpers/object.instances.js @@ -1,21 +1,15 @@ -(function (global, doc, ibexa) { - const setInstance = (domElement, instance) => { - if (domElement.ibexaInstance) { - throw new Error('Instance for this DOM element already exists!'); - } +const setInstance = (domElement, instance) => { + if (domElement.ibexaInstance) { + throw new Error('Instance for this DOM element already exists!'); + } - domElement.ibexaInstance = instance; - }; - const getInstance = (domElement) => { - return domElement.ibexaInstance; - }; - const clearInstance = (domElement) => { - delete domElement.ibexaInstance; - }; + domElement.ibexaInstance = instance; +}; +const getInstance = (domElement) => { + return domElement.ibexaInstance; +}; +const clearInstance = (domElement) => { + delete domElement.ibexaInstance; +}; - ibexa.addConfig('helpers.objectInstances', { - setInstance, - getInstance, - clearInstance, - }); -})(window, window.document, window.ibexa); +export { setInstance, getInstance, clearInstance }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/pagination.helper.js b/src/bundle/Resources/public/js/scripts/helpers/pagination.helper.js index de49ca2c4a..cb3e78de2d 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/pagination.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/pagination.helper.js @@ -1,41 +1,37 @@ -(function (global, doc, ibexa) { - /** - * Computes array with pagination pages. - * - * Example 1: [ 1, "...", 5, 6, 7, 8, 9, 10 ] (for: proximity = 2; pagesNumber = 10; activePageIndex = 7) - * Example 2: [ 1, "...", 3, 4, 5, 6, 7, "...", 10 ] (for: proximity = 2; pagesNumber = 10; activePageIndex = 5) - * Example 3: [ 1, "...", 8, 9, 10, 11, 12, "...", 20 ] (for: proximity = 2; pagesNumber = 20; activePageIndex = 10) - * - * @param {Object} params - * @param {Number} params.proximity - * @param {Number} params.activePageIndex - * @param {Number} params.pagesCount - * @param {String} params.separator - * - * @returns {Array} - */ - const computePages = ({ proximity = 2, activePageIndex, pagesCount, separator = '...' }) => { - const pages = []; - let wasSeparator = false; +/** + * Computes array with pagination pages. + * + * Example 1: [ 1, "...", 5, 6, 7, 8, 9, 10 ] (for: proximity = 2; pagesNumber = 10; activePageIndex = 7) + * Example 2: [ 1, "...", 3, 4, 5, 6, 7, "...", 10 ] (for: proximity = 2; pagesNumber = 10; activePageIndex = 5) + * Example 3: [ 1, "...", 8, 9, 10, 11, 12, "...", 20 ] (for: proximity = 2; pagesNumber = 20; activePageIndex = 10) + * + * @param {Object} params + * @param {Number} params.proximity + * @param {Number} params.activePageIndex + * @param {Number} params.pagesCount + * @param {String} params.separator + * + * @returns {Array} + */ +const computePages = ({ proximity = 2, activePageIndex, pagesCount, separator = '...' }) => { + const pages = []; + let wasSeparator = false; - for (let i = 1; i <= pagesCount; i++) { - const isFirstPage = i === 1; - const isLastPage = i === pagesCount; - const isInRange = i >= activePageIndex + 1 - proximity && i <= activePageIndex + 1 + proximity; + for (let i = 1; i <= pagesCount; i++) { + const isFirstPage = i === 1; + const isLastPage = i === pagesCount; + const isInRange = i >= activePageIndex + 1 - proximity && i <= activePageIndex + 1 + proximity; - if (isFirstPage || isLastPage || isInRange) { - pages.push(i); - wasSeparator = false; - } else if (!wasSeparator) { - pages.push(separator); - wasSeparator = true; - } + if (isFirstPage || isLastPage || isInRange) { + pages.push(i); + wasSeparator = false; + } else if (!wasSeparator) { + pages.push(separator); + wasSeparator = true; } + } - return pages; - }; + return pages; +}; - ibexa.addConfig('helpers.pagination', { - computePages, - }); -})(window, window.document, window.ibexa); +export { computePages }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/request.helper.js b/src/bundle/Resources/public/js/scripts/helpers/request.helper.js index a3d4714ec6..ea378f2d86 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/request.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/request.helper.js @@ -1,55 +1,41 @@ -(function (global, doc, ibexa) { - /** - * Handles request error - * - * @function handleRequest - * @param {Response} response - * @returns {Error|Response} - */ - const handleRequest = (response) => { - if (!response.ok) { - throw Error(response.statusText); - } - - return response; - }; +const handleRequest = (response) => { + if (!response.ok) { + throw Error(response.statusText); + } - /** - * Handles request JSON response - * - * @function getJsonFromResponse - * @param {Response} response - * @returns {Error|Promise} - */ - const getJsonFromResponse = (response) => { - return handleRequest(response).json(); - }; + return response; +}; - /** - * Handles request text response - * - * @function getTextFromResponse - * @param {Response} response - * @returns {Error|Promise} - */ - const getTextFromResponse = (response) => { - return handleRequest(response).text(); - }; +const getJsonFromResponse = (response) => { + return handleRequest(response).json(); +}; + +const getTextFromResponse = (response) => { + return handleRequest(response).text(); +}; + +const getStatusFromResponse = (response) => { + return handleRequest(response).status; +}; + +const getRequestMode = ({ instanceUrl }) => { + return window.location.origin === instanceUrl ? 'same-origin' : 'cors'; +}; + +const getRequestHeaders = ({ token, siteaccess, accessToken, extraHeaders }) => { + if (accessToken) { + return { + Authorization: `Bearer ${accessToken}`, + ...(siteaccess && { 'X-Siteaccess': siteaccess }), + ...extraHeaders, + }; + } - /** - * Handles request response; returns status if response is OK - * - * @function getStatusFromResponse - * @param {Response} response - * @returns {Error|Promise} - */ - const getStatusFromResponse = (response) => { - return handleRequest(response).status; + return { + ...(token && { 'X-CSRF-Token': token }), + ...(siteaccess && { 'X-Siteaccess': siteaccess }), + ...extraHeaders, }; +}; - ibexa.addConfig('helpers.request', { - getJsonFromResponse, - getTextFromResponse, - getStatusFromResponse, - }); -})(window, window.document, window.ibexa); +export { getJsonFromResponse, getTextFromResponse, getStatusFromResponse, getRequestMode, getRequestHeaders }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/system.helper.js b/src/bundle/Resources/public/js/scripts/helpers/system.helper.js index 3fc127ba5e..1b362f7559 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/system.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/system.helper.js @@ -1,79 +1,77 @@ -(function (global, doc, ibexa) { - const { userAgent } = window.navigator; - const isWindows = () => { - return userAgent.includes('Windows'); - }; - const isMac = () => { - return userAgent.includes('Mac OS X'); - }; - const isLinux = () => { - return userAgent.includes('Linux'); - }; - const isShortcutWithLetter = (event, letter) => { - if (isMac()) { - return event.metaKey && event.key === letter; - } +const { userAgent } = window.navigator; +const isWindows = () => { + return userAgent.includes('Windows'); +}; +const isMac = () => { + return userAgent.includes('Mac OS X'); +}; +const isLinux = () => { + return userAgent.includes('Linux'); +}; +const isShortcutWithLetter = (event, letter) => { + if (isMac()) { + return event.metaKey && event.key === letter; + } - if (isWindows() || isLinux()) { - return event.ctrlKey && event.key === letter; - } + if (isWindows() || isLinux()) { + return event.ctrlKey && event.key === letter; + } - return false; - }; - const isUndoPressed = (event) => { - if (isMac()) { - return event.metaKey && !event.shiftKey && event.key === 'z'; - } + return false; +}; +const isUndoPressed = (event) => { + if (isMac()) { + return event.metaKey && !event.shiftKey && event.key === 'z'; + } - if (isWindows() || isLinux()) { - return event.ctrlKey && event.key === 'z'; - } + if (isWindows() || isLinux()) { + return event.ctrlKey && event.key === 'z'; + } - return false; - }; + return false; +}; - const isRedoPressed = (event) => { - if (isMac()) { - return event.metaKey && event.shiftKey && event.key === 'z'; - } +const isRedoPressed = (event) => { + if (isMac()) { + return event.metaKey && event.shiftKey && event.key === 'z'; + } - if (isWindows() || isLinux()) { - return event.ctrlKey && event.key === 'y'; - } + if (isWindows() || isLinux()) { + return event.ctrlKey && event.key === 'y'; + } - return false; - }; - const isSavePressed = (event) => { - return isShortcutWithLetter(event, 's'); - }; - const isCopyPressed = (event) => { - return isShortcutWithLetter(event, 'c'); - }; - const isCutPressed = (event) => { - return isShortcutWithLetter(event, 'x'); - }; - const isPastePressed = (event) => { - return isShortcutWithLetter(event, 'v'); - }; - const isPrintPressed = (event) => { - return isShortcutWithLetter(event, 'p'); - }; - const isSelectAllPressed = (event) => { - return isShortcutWithLetter(event, 'a'); - }; + return false; +}; +const isSavePressed = (event) => { + return isShortcutWithLetter(event, 's'); +}; +const isCopyPressed = (event) => { + return isShortcutWithLetter(event, 'c'); +}; +const isCutPressed = (event) => { + return isShortcutWithLetter(event, 'x'); +}; +const isPastePressed = (event) => { + return isShortcutWithLetter(event, 'v'); +}; +const isPrintPressed = (event) => { + return isShortcutWithLetter(event, 'p'); +}; +const isSelectAllPressed = (event) => { + return isShortcutWithLetter(event, 'a'); +}; - ibexa.addConfig('helpers.system', { - isWindows: isWindows(), - isMac: isMac(), - isLinux: isLinux(), - isUndoPressed, - isRedoPressed, - isSavePressed, - isCopyPressed, - isCutPressed, - isPastePressed, - isPrintPressed, - isSelectAllPressed, - isShortcutWithLetter, - }); -})(window, window.document, window.ibexa); +export { + isWindows, + isMac, + isLinux, + isUndoPressed, + isRedoPressed, + isSavePressed, + isCopyPressed, + isCutPressed, + isPastePressed, + isPrintPressed, + isSelectAllPressed, + isShortcutWithLetter, +}; diff --git a/src/bundle/Resources/public/js/scripts/helpers/table.helper.js b/src/bundle/Resources/public/js/scripts/helpers/table.helper.js index 2545b91436..cd3f01041c 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/table.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/table.helper.js @@ -1,18 +1,16 @@ -(function (global, doc, ibexa) { - const onChangeHandler = (activeClass, event) => { - const { checked } = event.target; - const action = checked ? 'add' : 'remove'; - const parentRow = event.target.closest('tr'); +const { document: doc } = window; - parentRow.classList[action](activeClass); - }; - const parseCheckbox = (checkboxSelector, activeClass) => { - doc.querySelectorAll(checkboxSelector).forEach((checkboxNode) => { - checkboxNode.addEventListener('change', onChangeHandler.bind(this, activeClass), false); - }); - }; +const onChangeHandler = (activeClass, event) => { + const { checked } = event.target; + const action = checked ? 'add' : 'remove'; + const parentRow = event.target.closest('tr'); - ibexa.addConfig('helpers.table', { - parseCheckbox, + parentRow.classList[action](activeClass); +}; +const parseCheckbox = (checkboxSelector, activeClass) => { + doc.querySelectorAll(checkboxSelector).forEach((checkboxNode) => { + checkboxNode.addEventListener('change', onChangeHandler.bind(this, activeClass), false); }); -})(window, window.document, window.ibexa); +}; + +export { parseCheckbox }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/tag.view.select.helper.js b/src/bundle/Resources/public/js/scripts/helpers/tag.view.select.helper.js index e294c13060..924bbc7bda 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/tag.view.select.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/tag.view.select.helper.js @@ -1,23 +1,19 @@ -(function (global, doc, ibexa) { - const buildItemsFromUDWResponse = (udwItems, getId, callback) => { - const { removeRootFromPathString, findLocationsByIds, buildLocationsBreadcrumbs } = window.ibexa.helpers.location; +import { removeRootFromPathString, findLocationsByIds, buildLocationsBreadcrumbs } from './location.helper'; - Promise.all( - udwItems.map( - (item) => - new Promise((resolve) => { - findLocationsByIds(removeRootFromPathString(item.pathString), (locations) => { - resolve({ - id: getId(item), - name: buildLocationsBreadcrumbs(locations), - }); +const buildItemsFromUDWResponse = (udwItems, getId, callback) => { + Promise.all( + udwItems.map( + (item) => + new Promise((resolve) => { + findLocationsByIds(removeRootFromPathString(item.pathString), (locations) => { + resolve({ + id: getId(item), + name: buildLocationsBreadcrumbs(locations), }); - }), - ), - ).then(callback); - }; + }); + }), + ), + ).then(callback); +}; - ibexa.addConfig('helpers.tagViewSelect', { - buildItemsFromUDWResponse, - }); -})(window, window.document, window.ibexa); +export { buildItemsFromUDWResponse }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/text.helper.js b/src/bundle/Resources/public/js/scripts/helpers/text.helper.js index 4a42f9b113..1775fa510f 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/text.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/text.helper.js @@ -1,13 +1,11 @@ -(function (global, doc, ibexa) { - const escapeHTML = (string) => { - const stringTempNode = doc.createElement('div'); +const { document: doc } = window; - stringTempNode.appendChild(doc.createTextNode(string)); +const escapeHTML = (string) => { + const stringTempNode = doc.createElement('div'); - return stringTempNode.innerHTML; - }; + stringTempNode.appendChild(doc.createTextNode(string)); - ibexa.addConfig('helpers.text', { - escapeHTML, - }); -})(window, window.document, window.ibexa); + return stringTempNode.innerHTML; +}; + +export { escapeHTML }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/timezone.helper.js b/src/bundle/Resources/public/js/scripts/helpers/timezone.helper.js index 5272f5c053..b2f8f6f039 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/timezone.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/timezone.helper.js @@ -1,32 +1,27 @@ -(function (global, doc, ibexa, moment) { - const userPreferredTimezone = ibexa.adminUiConfig.timezone; - const userPreferredFullDateTimeFormat = ibexa.adminUiConfig.dateFormat.fullDateTime; - const userPreferredShortDateTimeFormat = ibexa.adminUiConfig.dateFormat.shortDateTime; +import { getAdminUiConfig, getMoment } from './context.helper'; - const convertDateToTimezone = (date, timezone = userPreferredTimezone, forceSameTime = false) => { - return moment(date).tz(timezone, forceSameTime); - }; - const formatDate = (date, timezone = null, format) => { - if (timezone) { - date = convertDateToTimezone(date, timezone); - } +const convertDateToTimezone = (date, timezone = getAdminUiConfig().timezone, forceSameTime = false) => { + const moment = getMoment(); - return moment(date).formatICU(format); - }; - const formatFullDateTime = (date, timezone = userPreferredTimezone, format = userPreferredFullDateTimeFormat) => { - return formatDate(date, timezone, format); - }; - const formatShortDateTime = (date, timezone = userPreferredTimezone, format = userPreferredShortDateTimeFormat) => { - return formatDate(date, timezone, format); - }; - const getBrowserTimezone = () => { - return Intl.DateTimeFormat().resolvedOptions().timeZone; - }; + return moment(date).tz(timezone, forceSameTime); +}; +const formatDate = (date, timezone = null, format) => { + if (timezone) { + date = convertDateToTimezone(date, timezone); + } - ibexa.addConfig('helpers.timezone', { - convertDateToTimezone, - formatFullDateTime, - formatShortDateTime, - getBrowserTimezone, - }); -})(window, window.document, window.ibexa, window.moment); + const moment = getMoment(); + + return moment(date).formatICU(format); +}; +const formatFullDateTime = (date, timezone = getAdminUiConfig().timezone, format = getAdminUiConfig().dateFormat.fullDateTime) => { + return formatDate(date, timezone, format); +}; +const formatShortDateTime = (date, timezone = getAdminUiConfig().timezone, format = getAdminUiConfig().dateFormat.shortDateTime) => { + return formatDate(date, timezone, format); +}; +const getBrowserTimezone = () => { + return Intl.DateTimeFormat().resolvedOptions().timeZone; +}; + +export { convertDateToTimezone, formatFullDateTime, formatShortDateTime, getBrowserTimezone }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper.js b/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper.js index 32cd2bf521..b17a1d1168 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper.js @@ -1,222 +1,222 @@ -(function (global, doc, ibexa, bootstrap) { - let lastInsertTooltipTarget = null; - const TOOLTIPS_SELECTOR = '[title], [data-tooltip-title]'; - const observerConfig = { - childList: true, - subtree: true, - }; - const resizeEllipsisObserver = new ResizeObserver((entries) => { - entries.forEach((entry) => { - ibexa.helpers.tooltips.parse(entry.target); - }); - }); - const observer = new MutationObserver((mutationsList) => { - if (lastInsertTooltipTarget) { - mutationsList.forEach((mutation) => { - const { addedNodes, removedNodes } = mutation; - - if (addedNodes.length) { - addedNodes.forEach((addedNode) => { - if (addedNode instanceof Element) { - parse(addedNode); - } - }); - } - - if (removedNodes.length) { - removedNodes.forEach((removedNode) => { - if (removedNode.classList && !removedNode.classList.contains('ibexa-tooltip')) { - lastInsertTooltipTarget = null; - doc.querySelectorAll('.ibexa-tooltip.show').forEach((tooltipNode) => { - tooltipNode.remove(); - }); - } - }); - } - }); - } +import { getBootstrap } from './context.helper'; + +const { document: doc } = window; + +let lastInsertTooltipTarget = null; +const TOOLTIPS_SELECTOR = '[title], [data-tooltip-title]'; +const observerConfig = { + childList: true, + subtree: true, +}; +const resizeEllipsisObserver = new ResizeObserver((entries) => { + entries.forEach((entry) => { + parse(entry.target); }); - const modifyPopperConfig = (iframe, defaultBsPopperConfig) => { - if (!iframe) { - return defaultBsPopperConfig; - } - - const iframeDOMRect = iframe.getBoundingClientRect(); - const offsetX = iframeDOMRect.x; - const offsetY = iframeDOMRect.y; - const offsetModifier = { - name: 'offset', - options: { - offset: ({ placement }) => { - const [basePlacement] = placement.split('-'); - - switch (basePlacement) { - case 'top': - return [offsetX, -offsetY]; - case 'bottom': - return [offsetX, offsetY]; - case 'right': - return [offsetY, offsetX]; - case 'left': - return [offsetY, -offsetX]; - default: - return []; +}); +const observer = new MutationObserver((mutationsList) => { + if (lastInsertTooltipTarget) { + mutationsList.forEach((mutation) => { + const { addedNodes, removedNodes } = mutation; + + if (addedNodes.length) { + addedNodes.forEach((addedNode) => { + if (addedNode instanceof Element) { + parse(addedNode); } - }, - }, - }; - const offsetModifierIndex = defaultBsPopperConfig.modifiers.findIndex((modifier) => modifier.name == 'offset'); - - if (offsetModifierIndex != -1) { - defaultBsPopperConfig.modifiers[offsetModifierIndex] = offsetModifier; - } else { - defaultBsPopperConfig.modifiers.push(offsetModifier); - } + }); + } + if (removedNodes.length) { + removedNodes.forEach((removedNode) => { + if (removedNode.classList && !removedNode.classList.contains('ibexa-tooltip')) { + lastInsertTooltipTarget = null; + doc.querySelectorAll('.ibexa-tooltip.show').forEach((tooltipNode) => { + tooltipNode.remove(); + }); + } + }); + } + }); + } +}); +const modifyPopperConfig = (iframe, defaultBsPopperConfig) => { + if (!iframe) { return defaultBsPopperConfig; + } + + const iframeDOMRect = iframe.getBoundingClientRect(); + const offsetX = iframeDOMRect.x; + const offsetY = iframeDOMRect.y; + const offsetModifier = { + name: 'offset', + options: { + offset: ({ placement }) => { + const [basePlacement] = placement.split('-'); + + switch (basePlacement) { + case 'top': + return [offsetX, -offsetY]; + case 'bottom': + return [offsetX, offsetY]; + case 'right': + return [offsetY, offsetX]; + case 'left': + return [offsetY, -offsetX]; + default: + return []; + } + }, + }, }; - const getTextHeight = (text, styles) => { - const tag = doc.createElement('div'); - - tag.innerHTML = text; - - for (const key in styles) { - tag.style[key] = styles[key]; - } - - doc.body.appendChild(tag); - - const { height: texHeight } = tag.getBoundingClientRect(); - - doc.body.removeChild(tag); - - return texHeight; - }; - const isTitleEllipsized = (node) => { - const title = node.dataset.originalTitle; - const { width: nodeWidth, height: nodeHeight } = node.getBoundingClientRect(); - const computedNodeStyles = getComputedStyle(node); - const styles = { - width: `${nodeWidth}px`, - padding: computedNodeStyles.getPropertyValue('padding'), - 'font-size': computedNodeStyles.getPropertyValue('font-size'), - 'font-family': computedNodeStyles.getPropertyValue('font-family'), - 'font-weight': computedNodeStyles.getPropertyValue('font-weight'), - 'font-style': computedNodeStyles.getPropertyValue('font-style'), - 'font-variant': computedNodeStyles.getPropertyValue('font-variant'), - 'line-height': computedNodeStyles.getPropertyValue('line-height'), - 'word-break': 'break-all', - }; - - const textHeight = getTextHeight(title, styles); - - return textHeight > nodeHeight; + const offsetModifierIndex = defaultBsPopperConfig.modifiers.findIndex((modifier) => modifier.name == 'offset'); + + if (offsetModifierIndex != -1) { + defaultBsPopperConfig.modifiers[offsetModifierIndex] = offsetModifier; + } else { + defaultBsPopperConfig.modifiers.push(offsetModifier); + } + + return defaultBsPopperConfig; +}; +const getTextHeight = (text, styles) => { + const tag = doc.createElement('div'); + + tag.innerHTML = text; + + for (const key in styles) { + tag.style[key] = styles[key]; + } + + doc.body.appendChild(tag); + + const { height: texHeight } = tag.getBoundingClientRect(); + + doc.body.removeChild(tag); + + return texHeight; +}; +const isTitleEllipsized = (node) => { + const title = node.dataset.originalTitle; + const { width: nodeWidth, height: nodeHeight } = node.getBoundingClientRect(); + const computedNodeStyles = getComputedStyle(node); + const styles = { + width: `${nodeWidth}px`, + padding: computedNodeStyles.getPropertyValue('padding'), + 'font-size': computedNodeStyles.getPropertyValue('font-size'), + 'font-family': computedNodeStyles.getPropertyValue('font-family'), + 'font-weight': computedNodeStyles.getPropertyValue('font-weight'), + 'font-style': computedNodeStyles.getPropertyValue('font-style'), + 'font-variant': computedNodeStyles.getPropertyValue('font-variant'), + 'line-height': computedNodeStyles.getPropertyValue('line-height'), + 'word-break': 'break-all', }; - const initializeTooltip = (tooltipNode) => { - const { delayShow, delayHide } = tooltipNode.dataset; - const delay = { - show: delayShow ? parseInt(delayShow, 10) : 150, - hide: delayHide ? parseInt(delayHide, 10) : 75, - }; - const extraClass = tooltipNode.dataset.tooltipExtraClass ?? ''; - const placement = tooltipNode.dataset.tooltipPlacement ?? 'bottom'; - const trigger = tooltipNode.dataset.tooltipTrigger ?? 'hover'; - const useHtml = tooltipNode.dataset.tooltipUseHtml !== undefined; - const container = tooltipNode.dataset.tooltipContainerSelector - ? tooltipNode.closest(tooltipNode.dataset.tooltipContainerSelector) - : 'body'; - const iframe = document.querySelector(tooltipNode.dataset.tooltipIframeSelector); - - new bootstrap.Tooltip(tooltipNode, { - delay, - placement, - trigger, - container, - popperConfig: modifyPopperConfig.bind(null, iframe), - html: useHtml, - template: `
-
-
-
`, - }); - tooltipNode.addEventListener('inserted.bs.tooltip', (event) => { - lastInsertTooltipTarget = event.currentTarget; - }); - }; - const parse = (baseElement = doc) => { - if (!baseElement) { - return; - } - - const tooltipNodes = [...baseElement.querySelectorAll(TOOLTIPS_SELECTOR)]; + const textHeight = getTextHeight(title, styles); - if (baseElement instanceof Element) { - tooltipNodes.push(baseElement); - } + return textHeight > nodeHeight; +}; +const initializeTooltip = (tooltipNode) => { + const bootstrap = getBootstrap(); + const { delayShow, delayHide } = tooltipNode.dataset; + const delay = { + show: delayShow ? parseInt(delayShow, 10) : 150, + hide: delayHide ? parseInt(delayHide, 10) : 75, + }; + const extraClass = tooltipNode.dataset.tooltipExtraClass ?? ''; + const placement = tooltipNode.dataset.tooltipPlacement ?? 'bottom'; + const trigger = tooltipNode.dataset.tooltipTrigger ?? 'hover'; + const useHtml = tooltipNode.dataset.tooltipUseHtml !== undefined; + const container = tooltipNode.dataset.tooltipContainerSelector + ? tooltipNode.closest(tooltipNode.dataset.tooltipContainerSelector) + : 'body'; + const iframe = document.querySelector(tooltipNode.dataset.tooltipIframeSelector); + + new bootstrap.Tooltip(tooltipNode, { + delay, + placement, + trigger, + container, + popperConfig: modifyPopperConfig.bind(null, iframe), + html: useHtml, + template: `
+
+
+
`, + }); - for (const tooltipNode of tooltipNodes) { - const hasEllipsisStyle = getComputedStyle(tooltipNode).textOverflow === 'ellipsis'; - const hasNewTitle = tooltipNode.hasAttribute('title'); - const tooltipInitialized = !!tooltipNode.dataset.originalTitle; - let shouldHaveTooltip = !hasEllipsisStyle; + tooltipNode.addEventListener('inserted.bs.tooltip', (event) => { + lastInsertTooltipTarget = event.currentTarget; + }); +}; +const parse = (baseElement = doc) => { + if (!baseElement) { + return; + } + const bootstrap = getBootstrap(); + const tooltipNodes = [...baseElement.querySelectorAll(TOOLTIPS_SELECTOR)]; + + if (baseElement instanceof Element) { + tooltipNodes.push(baseElement); + } + + for (const tooltipNode of tooltipNodes) { + const hasEllipsisStyle = getComputedStyle(tooltipNode).textOverflow === 'ellipsis'; + const hasNewTitle = tooltipNode.hasAttribute('title'); + const tooltipInitialized = !!tooltipNode.dataset.originalTitle; + let shouldHaveTooltip = !hasEllipsisStyle; + + if (!tooltipInitialized && hasNewTitle) { + resizeEllipsisObserver.observe(tooltipNode); + tooltipNode.dataset.originalTitle = tooltipNode.title; + + if (!shouldHaveTooltip) { + shouldHaveTooltip = isTitleEllipsized(tooltipNode); + } - if (!tooltipInitialized && hasNewTitle) { - resizeEllipsisObserver.observe(tooltipNode); + if (shouldHaveTooltip) { + initializeTooltip(tooltipNode); + } else { + tooltipNode.removeAttribute('title'); + } + } else if (tooltipInitialized && (hasNewTitle || hasEllipsisStyle)) { + if (hasNewTitle) { tooltipNode.dataset.originalTitle = tooltipNode.title; + } + const tooltipInstance = bootstrap.Tooltip.getInstance(tooltipNode); + const hasTooltip = !!tooltipInstance; - if (!shouldHaveTooltip) { - shouldHaveTooltip = isTitleEllipsized(tooltipNode); - } - - if (shouldHaveTooltip) { - initializeTooltip(tooltipNode); - } else { - tooltipNode.removeAttribute('title'); - } - } else if (tooltipInitialized && (hasNewTitle || hasEllipsisStyle)) { - if (hasNewTitle) { - tooltipNode.dataset.originalTitle = tooltipNode.title; - } - const tooltipInstance = bootstrap.Tooltip.getInstance(tooltipNode); - const hasTooltip = !!tooltipInstance; - - if (!shouldHaveTooltip) { - shouldHaveTooltip = isTitleEllipsized(tooltipNode); - } - - if (hasTooltip && ((hasNewTitle && shouldHaveTooltip) || !shouldHaveTooltip)) { - tooltipInstance.dispose(); - } - - if (shouldHaveTooltip && (hasNewTitle || !hasTooltip)) { - tooltipNode.title = tooltipNode.dataset.originalTitle; + if (!shouldHaveTooltip) { + shouldHaveTooltip = isTitleEllipsized(tooltipNode); + } - initializeTooltip(tooltipNode); - } else { - tooltipNode.removeAttribute('title'); - } + if (hasTooltip && ((hasNewTitle && shouldHaveTooltip) || !shouldHaveTooltip)) { + tooltipInstance.dispose(); } - } - }; - const hideAll = (baseElement = doc) => { - if (!baseElement) { - return; - } - const tooltipsNode = baseElement.querySelectorAll(TOOLTIPS_SELECTOR); + if (shouldHaveTooltip && (hasNewTitle || !hasTooltip)) { + tooltipNode.title = tooltipNode.dataset.originalTitle; - for (const tooltipNode of tooltipsNode) { - bootstrap.Tooltip.getOrCreateInstance(tooltipNode).hide(); + initializeTooltip(tooltipNode); + } else { + tooltipNode.removeAttribute('title'); + } } - }; - const observe = (baseElement = doc) => { - observer.observe(baseElement, observerConfig); - }; - - ibexa.addConfig('helpers.tooltips', { - parse, - hideAll, - observe, - }); -})(window, window.document, window.ibexa, window.bootstrap); + } +}; +const hideAll = (baseElement = doc) => { + if (!baseElement) { + return; + } + + const bootstrap = getBootstrap(); + const tooltipsNode = baseElement.querySelectorAll(TOOLTIPS_SELECTOR); + + for (const tooltipNode of tooltipsNode) { + bootstrap.Tooltip.getOrCreateInstance(tooltipNode).hide(); + } +}; +const observe = (baseElement = doc) => { + observer.observe(baseElement, observerConfig); +}; + +export { parse, hideAll, observe }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/user.helper.js b/src/bundle/Resources/public/js/scripts/helpers/user.helper.js index 07443b4c73..a860d27ea4 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/user.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/user.helper.js @@ -1,7 +1,7 @@ -(function (global, doc, ibexa) { - const getId = () => doc.querySelector('meta[name="UserId"]').content; +const { document: doc } = window; - ibexa.addConfig('helpers.user', { - getId, - }); -})(window, window.document, window.ibexa); +const getId = () => { + return doc.querySelector('meta[name="UserId"]')?.content ?? 0; +}; + +export { getId }; diff --git a/src/bundle/Resources/public/scss/ui/modules/_universal.discovery.scss b/src/bundle/Resources/public/scss/ui/modules/_universal.discovery.scss index 1b37921cd3..022c324534 100644 --- a/src/bundle/Resources/public/scss/ui/modules/_universal.discovery.scss +++ b/src/bundle/Resources/public/scss/ui/modules/_universal.discovery.scss @@ -29,3 +29,4 @@ @import 'universal-discovery/tree'; @import 'universal-discovery/toggle.selection'; @import 'universal-discovery/dropdown'; +@import 'universal-discovery/global.loader'; diff --git a/src/bundle/Resources/public/scss/ui/modules/universal-discovery/_global.loader.scss b/src/bundle/Resources/public/scss/ui/modules/universal-discovery/_global.loader.scss new file mode 100644 index 0000000000..9e9f0110a2 --- /dev/null +++ b/src/bundle/Resources/public/scss/ui/modules/universal-discovery/_global.loader.scss @@ -0,0 +1,22 @@ +.m-ud-global-loader { + position: fixed; + z-index: 99999; + background: $ibexa-color-dark; + width: 100%; + height: 100%; + padding: calculateRem(16px); + + &__spinner-container { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + border-radius: $ibexa-border-radius; + background: $ibexa-color-white; + } + + &__spinner { + @include spinner(calculateRem(48px), calculateRem(6px), $ibexa-color-primary); + } +} diff --git a/src/bundle/ui-dev/src/modules/common/dropdown/dropdown.js b/src/bundle/ui-dev/src/modules/common/dropdown/dropdown.js index 33745cf612..3ee63d8a10 100644 --- a/src/bundle/ui-dev/src/modules/common/dropdown/dropdown.js +++ b/src/bundle/ui-dev/src/modules/common/dropdown/dropdown.js @@ -4,8 +4,9 @@ import PropTypes from 'prop-types'; import { createCssClassNames } from '../../common/helpers/css.class.names'; import Icon from '../../common/icon/icon'; +import { getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; -const { Translator, document } = window; +const { document } = window; const MIN_SEARCH_ITEMS_DEFAULT = 5; const MIN_ITEMS_LIST_HEIGHT = 150; const ITEMS_LIST_WIDGET_MARGIN = 8; @@ -25,6 +26,7 @@ const Dropdown = ({ renderSelectedItem, minSearchItems, }) => { + const Translator = getTranslator(); const containerRef = useRef(); const containerItemsRef = useRef(); const selectionInfoRef = useRef(); @@ -185,7 +187,7 @@ const Dropdown = ({ }; document.body.addEventListener('click', onInteractionOutside, false); - scrollContainer.addEventListener('scroll', calculateAndSetItemsListStyles, false); + scrollContainer?.addEventListener('scroll', calculateAndSetItemsListStyles, false); return () => { document.body.removeEventListener('click', onInteractionOutside); diff --git a/src/bundle/ui-dev/src/modules/common/icon/icon.js b/src/bundle/ui-dev/src/modules/common/icon/icon.js index 38f05f09b6..d286ca28ad 100644 --- a/src/bundle/ui-dev/src/modules/common/icon/icon.js +++ b/src/bundle/ui-dev/src/modules/common/icon/icon.js @@ -1,8 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { getIconPath } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/icon.helper'; + const Icon = (props) => { - const linkHref = props.customPath ? props.customPath : window.ibexa.helpers.icon.getIconPath(props.name); + const linkHref = props.customPath ? props.customPath : getIconPath(props.name); let className = 'ibexa-icon'; if (props.extraClasses) { diff --git a/src/bundle/ui-dev/src/modules/common/input/filter.search.js b/src/bundle/ui-dev/src/modules/common/input/filter.search.js index 54cae92876..5ecbb223a9 100644 --- a/src/bundle/ui-dev/src/modules/common/input/filter.search.js +++ b/src/bundle/ui-dev/src/modules/common/input/filter.search.js @@ -1,11 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import { createCssClassNames } from '../../common/helpers/css.class.names'; - -const { Translator } = window; -const defaultPlaceholder = Translator.trans(/*@Desc("Search...")*/ 'search.placeholder', {}, 'ibexa_universal_discovery_widget'); +import { getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const Search = ({ onChange, placeholder, extraClasses, value }) => { + const inputPlaceholder = + placeholder ?? getTranslator().trans(/*@Desc("Search...")*/ 'search.placeholder', {}, 'ibexa_universal_discovery_widget'); const searchClassName = createCssClassNames({ 'form-control': true, 'ibexa-input': true, @@ -13,7 +13,7 @@ const Search = ({ onChange, placeholder, extraClasses, value }) => { [extraClasses]: true, }); - return ; + return ; }; Search.propTypes = { @@ -24,7 +24,7 @@ Search.propTypes = { }; Search.defaultProps = { - placeholder: defaultPlaceholder, + placeholder: '', extraClasses: '', }; diff --git a/src/bundle/ui-dev/src/modules/common/pagination/pagination.info.js b/src/bundle/ui-dev/src/modules/common/pagination/pagination.info.js index c91bafad46..f9bab3982f 100644 --- a/src/bundle/ui-dev/src/modules/common/pagination/pagination.info.js +++ b/src/bundle/ui-dev/src/modules/common/pagination/pagination.info.js @@ -2,13 +2,13 @@ import React from 'react'; import PropTypes from 'prop-types'; import { createCssClassNames } from '../helpers/css.class.names'; -const { Translator } = window; +import { getTranslator } from '../../../../../Resources/public/js/scripts/helpers/context.helper'; const PaginationInfo = ({ totalCount, viewingCount, extraClasses }) => { if (totalCount === 0) { return null; } - + const Translator = getTranslator(); const className = createCssClassNames({ 'ibexa-pagination__info': true, [extraClasses]: true, diff --git a/src/bundle/ui-dev/src/modules/common/pagination/pagination.js b/src/bundle/ui-dev/src/modules/common/pagination/pagination.js index 8d82d99bd5..e041bc1a95 100644 --- a/src/bundle/ui-dev/src/modules/common/pagination/pagination.js +++ b/src/bundle/ui-dev/src/modules/common/pagination/pagination.js @@ -1,10 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { computePages as computePagesPagination } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/pagination.helper'; import PaginationButton from './pagination.button'; const DOTS = '...'; -const { ibexa } = window; const Pagination = ({ totalCount, itemsPerPage, proximity, activePageIndex, onPageChange, disabled: paginationDisabled }) => { const pagesCount = Math.ceil(totalCount / itemsPerPage); @@ -17,7 +17,7 @@ const Pagination = ({ totalCount, itemsPerPage, proximity, activePageIndex, onPa const nextPage = activePageIndex + 1; const isFirstPage = activePageIndex === 0; const isLastPage = activePageIndex + 1 === pagesCount; - const pages = ibexa.helpers.pagination.computePages({ proximity, activePageIndex, pagesCount, separator: DOTS }); + const pages = computePagesPagination({ proximity, activePageIndex, pagesCount, separator: DOTS }); const paginationButtons = pages.map((page, index) => { if (page === DOTS) { const key = `dots-${index}`; diff --git a/src/bundle/ui-dev/src/modules/common/popup/popup.component.js b/src/bundle/ui-dev/src/modules/common/popup/popup.component.js index bb671b2269..af3eba92c9 100644 --- a/src/bundle/ui-dev/src/modules/common/popup/popup.component.js +++ b/src/bundle/ui-dev/src/modules/common/popup/popup.component.js @@ -3,8 +3,7 @@ import PropTypes from 'prop-types'; import Icon from '../icon/icon'; import { createCssClassNames } from '@ibexa-admin-ui/src/bundle/ui-dev/src/modules/common/helpers/css.class.names'; - -const { bootstrap, Translator } = window; +import { getTranslator, getBootstrap } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const CLASS_NON_SCROLLABLE = 'ibexa-non-scrollable'; const CLASS_MODAL_OPEN = 'modal-open'; @@ -33,6 +32,8 @@ const Popup = ({ extraClasses, }) => { const modalRef = useRef(null); + const Translator = getTranslator(); + const bootstrap = getBootstrap(); useEffect(() => { document.body.classList.toggle(CLASS_MODAL_OPEN, isVisible); 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 d7007de970..5c6488d2b0 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 @@ -36,7 +36,7 @@ const SimpleDropdown = ({ options, selectedOption, extraClasses, onOptionClick, return (
  • onOptionClickWrapper(item)}> {item.iconName && } - {item.label} + {item.label ?? item.getLabel()} {isItemSelected && (
    @@ -57,7 +57,7 @@ const SimpleDropdown = ({ options, selectedOption, extraClasses, onOptionClick, return ( - {selectedItemLabel.length ? selectedItemLabel : selectedOption.label} + {selectedItemLabel.length ? selectedItemLabel : selectedOption.label ?? selectedOption.getLabel()} ); }; diff --git a/src/bundle/ui-dev/src/modules/content-tree/components/header/header.js b/src/bundle/ui-dev/src/modules/content-tree/components/header/header.js index 9225a460a1..245b6d4c61 100644 --- a/src/bundle/ui-dev/src/modules/content-tree/components/header/header.js +++ b/src/bundle/ui-dev/src/modules/content-tree/components/header/header.js @@ -4,10 +4,10 @@ import PropTypes from 'prop-types'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; import Icon from '../../../common/icon/icon'; import PopupActions from '../popup-actions/popup.actions'; - -const { Translator } = window; +import { getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const Header = ({ isCollapsed, toggleCollapseTree, actions, popupRef }) => { + const Translator = getTranslator(); const headerTitle = Translator.trans(/*@Desc("Content tree")*/ 'content_tree.header', {}, 'ibexa_content_tree'); const renderCollapseButton = () => { const iconName = isCollapsed ? 'caret-next' : 'caret-back'; diff --git a/src/bundle/ui-dev/src/modules/content-tree/components/list-item/list.item.component.js b/src/bundle/ui-dev/src/modules/content-tree/components/list-item/list.item.component.js index 888f452783..acc72f72cd 100644 --- a/src/bundle/ui-dev/src/modules/content-tree/components/list-item/list.item.component.js +++ b/src/bundle/ui-dev/src/modules/content-tree/components/list-item/list.item.component.js @@ -2,7 +2,9 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Icon from '../../../common/icon/icon'; -const { ibexa, Translator } = window; +import { showWarningNotification } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/notification.helper'; +import { getContentTypeIconUrl } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/content.type.helper'; +import { getTranslator, getAdminUiConfig } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; class ListItem extends Component { constructor(props) { @@ -13,6 +15,8 @@ class ListItem extends Component { this.loadMoreSubitems = this.loadMoreSubitems.bind(this); this.handleAfterExpandedStateChange = this.handleAfterExpandedStateChange.bind(this); + this.Translator = getTranslator(); + this.adminUiConfig = getAdminUiConfig(); this.secondaryItemActions = this.getSecondaryItemActions(); this.sortedActions = this.getSortedActions(); @@ -23,7 +27,7 @@ class ListItem extends Component { } getSecondaryItemActions() { - const { secondaryItemActions } = ibexa.adminUiConfig.contentTreeWidget; + const { secondaryItemActions } = this.adminUiConfig.contentTreeWidget; if (!secondaryItemActions) { return []; @@ -35,7 +39,7 @@ class ListItem extends Component { } getSortedActions() { - const { itemActions } = ibexa.adminUiConfig.contentTreeWidget; + const { itemActions } = this.adminUiConfig.contentTreeWidget; const actions = itemActions ? [...itemActions] : []; return actions.sort((actionA, actionB) => { @@ -52,13 +56,13 @@ class ListItem extends Component { const currentDepth = currentPath.split(',').length - 1; if (currentDepth >= treeMaxDepth) { - const notificationMessage = Translator.trans( + const notificationMessage = this.Translator.trans( /*@Desc("Cannot load sub-items for this Location because you reached max tree depth.")*/ 'expand_item.limit.message', {}, 'ibexa_content_tree', ); - ibexa.helpers.notification.showWarningNotification(notificationMessage); + showWarningNotification(notificationMessage); return; } @@ -121,11 +125,9 @@ class ListItem extends Component { if (!this.state.isLoading || this.props.subitems.length) { if (locationId === 1) { - iconAttrs.customPath = ibexa.helpers.contentType.getContentTypeIconUrl('folder'); + iconAttrs.customPath = getContentTypeIconUrl('folder'); } else { - iconAttrs.customPath = - ibexa.helpers.contentType.getContentTypeIconUrl(contentTypeIdentifier) || - ibexa.helpers.contentType.getContentTypeIconUrl('file'); + iconAttrs.customPath = getContentTypeIconUrl(contentTypeIdentifier) || getContentTypeIconUrl('file'); } } else { iconAttrs.name = 'spinner'; @@ -148,8 +150,8 @@ class ListItem extends Component { } const { isLoading } = this.state; - const seeMoreLabel = Translator.trans(/*@Desc("See more")*/ 'see_more', {}, 'ibexa_content_tree'); - const loadingMoreLabel = Translator.trans(/*@Desc("Loading more...")*/ 'loading_more', {}, 'ibexa_content_tree'); + const seeMoreLabel = this.Translator.trans(/*@Desc("See more")*/ 'see_more', {}, 'ibexa_content_tree'); + const loadingMoreLabel = this.Translator.trans(/*@Desc("Loading more...")*/ 'loading_more', {}, 'ibexa_content_tree'); const btnLabel = isLoading ? loadingMoreLabel : seeMoreLabel; let loadingSpinner = null; @@ -172,7 +174,7 @@ class ListItem extends Component { return null; } - const message = Translator.trans(/*@Desc("Loading limit reached")*/ 'show_more.limit_reached', {}, 'ibexa_content_tree'); + const message = this.Translator.trans(/*@Desc("Loading limit reached")*/ 'show_more.limit_reached', {}, 'ibexa_content_tree'); return
    {message}
    ; } @@ -287,6 +289,7 @@ ListItem.propTypes = { isRootItem: PropTypes.bool, onClick: PropTypes.func, indent: PropTypes.number, + adminUiConfig: PropTypes.object, }; ListItem.defaultProps = { @@ -296,6 +299,7 @@ ListItem.defaultProps = { onClick: () => {}, subitemsLoadLimit: null, indent: 0, + adminUiConfig: window.ibexa?.adminUiConfig, }; export default ListItem; diff --git a/src/bundle/ui-dev/src/modules/content-tree/components/list/list.component.js b/src/bundle/ui-dev/src/modules/content-tree/components/list/list.component.js index 0ac20f2575..1f7cea6d7c 100644 --- a/src/bundle/ui-dev/src/modules/content-tree/components/list/list.component.js +++ b/src/bundle/ui-dev/src/modules/content-tree/components/list/list.component.js @@ -1,8 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import ListItem from '../list-item/list.item.component'; - -const { Translator } = window; +import { getTranslator, getRouting } from '../../../../../../Resources/public/js/scripts/helpers/context.helper'; const List = ({ items, @@ -17,6 +16,8 @@ const List = ({ isRoot, onClickItem, }) => { + const Translator = getTranslator(); + const Routing = getRouting(); const commonAttrs = { loadMoreSubitems, subitemsLoadLimit, subitemsLimit, treeMaxDepth, afterItemToggle, onClickItem }; const listAttrs = { ...commonAttrs, currentLocationId }; const listItemAttrs = commonAttrs; @@ -36,7 +37,7 @@ const List = ({
      {items.map((item) => { const hasPreviousPath = path && path.length; - const locationHref = window.Routing.generate('ibexa.content.view', { + const locationHref = Routing.generate('ibexa.content.view', { contentId: item.contentId, locationId: item.locationId, }); diff --git a/src/bundle/ui-dev/src/modules/content-tree/config.loader.js b/src/bundle/ui-dev/src/modules/content-tree/config.loader.js new file mode 100644 index 0000000000..6b2bac911f --- /dev/null +++ b/src/bundle/ui-dev/src/modules/content-tree/config.loader.js @@ -0,0 +1,5 @@ +import ContentTreeModule from './content.tree.module'; + +(function (ibexa) { + ibexa.addConfig('modules.ContentTree', ContentTreeModule); +})(window.ibexa); diff --git a/src/bundle/ui-dev/src/modules/content-tree/content.tree.module.js b/src/bundle/ui-dev/src/modules/content-tree/content.tree.module.js index 153edb86f0..312d4de187 100644 --- a/src/bundle/ui-dev/src/modules/content-tree/content.tree.module.js +++ b/src/bundle/ui-dev/src/modules/content-tree/content.tree.module.js @@ -347,6 +347,7 @@ export default class ContentTreeModule extends Component { return { token: this.props.restInfo.token, siteaccess: this.props.restInfo.siteaccess, + accessToken: this.props.restInfo.accessToken, subtree: this.subtree, sortClause: this.props.sort.sortClause, sortOrder: this.props.sort.sortOrder, @@ -373,8 +374,6 @@ export default class ContentTreeModule extends Component { } } -ibexa.addConfig('modules.ContentTree', ContentTreeModule); - ContentTreeModule.propTypes = { rootLocationId: PropTypes.number, currentLocationPath: PropTypes.number.isRequired, @@ -386,6 +385,7 @@ ContentTreeModule.propTypes = { restInfo: PropTypes.shape({ token: PropTypes.string.isRequired, siteaccess: PropTypes.string.isRequired, + accessToken: PropTypes.string, }).isRequired, onClickItem: PropTypes.func, readSubtree: PropTypes.func, @@ -399,10 +399,10 @@ ContentTreeModule.propTypes = { ContentTreeModule.defaultProps = { preloadedLocations: [], - rootLocationId: ibexa.adminUiConfig.contentTree.treeRootLocationId, - subitemsLimit: ibexa.adminUiConfig.contentTree.childrenLoadMaxLimit, - subitemsLoadLimit: ibexa.adminUiConfig.contentTree.loadMoreLimit, - treeMaxDepth: ibexa.adminUiConfig.contentTree.treeMaxDepth, + rootLocationId: ibexa?.adminUiConfig.contentTree.treeRootLocationId, + subitemsLimit: ibexa?.adminUiConfig.contentTree.childrenLoadMaxLimit, + subitemsLoadLimit: ibexa?.adminUiConfig.contentTree.loadMoreLimit, + treeMaxDepth: ibexa?.adminUiConfig.contentTree.treeMaxDepth, afterItemToggle: () => {}, sort: {}, resizable: true, diff --git a/src/bundle/ui-dev/src/modules/content-tree/services/content.tree.service.js b/src/bundle/ui-dev/src/modules/content-tree/services/content.tree.service.js index a42d59d5a4..1de0e50cc5 100644 --- a/src/bundle/ui-dev/src/modules/content-tree/services/content.tree.service.js +++ b/src/bundle/ui-dev/src/modules/content-tree/services/content.tree.service.js @@ -1,18 +1,29 @@ +import { getRequestHeaders, getRequestMode } from '../../../../../Resources/public/js/scripts/helpers/request.helper'; import { handleRequestResponse } from '../../common/helpers/request.helper'; import { showErrorNotification } from '../../common/services/notification.service'; const ENDPOINT_LOAD_SUBITEMS = '/api/ibexa/v2/location/tree/load-subitems'; const ENDPOINT_LOAD_SUBTREE = '/api/ibexa/v2/location/tree/load-subtree'; +const DEFAULT_INSTANCE_URL = window.location.origin; -export const loadLocationItems = ({ siteaccess }, parentLocationId, callback, limit = 50, offset = 0) => { +export const loadLocationItems = ( + { siteaccess, accessToken, instanceUrl = DEFAULT_INSTANCE_URL }, + parentLocationId, + callback, + limit = 50, + offset = 0, +) => { const request = new Request(`${ENDPOINT_LOAD_SUBITEMS}/${parentLocationId}/${limit}/${offset}`, { method: 'GET', - mode: 'same-origin', + mode: getRequestMode({ instanceUrl }), credentials: 'same-origin', - headers: { - Accept: 'application/vnd.ibexa.api.ContentTreeNode+json', - 'X-Siteaccess': siteaccess, - }, + headers: getRequestHeaders({ + siteaccess, + accessToken, + extraHeaders: { + Accept: 'application/vnd.ibexa.api.ContentTreeNode+json', + }, + }), }); fetch(request) @@ -28,8 +39,11 @@ export const loadLocationItems = ({ siteaccess }, parentLocationId, callback, li .catch(showErrorNotification); }; -export const loadSubtree = ({ token, siteaccess, subtree, sortClause, sortOrder }, callback) => { - let path = ENDPOINT_LOAD_SUBTREE; +export const loadSubtree = ( + { token, siteaccess, accessToken, subtree, sortClause, sortOrder, instanceUrl = DEFAULT_INSTANCE_URL }, + callback, +) => { + let path = `${instanceUrl}${ENDPOINT_LOAD_SUBTREE}`; if (sortClause && sortOrder) { path += `?sortClause=${sortClause}&sortOrder=${sortOrder}`; @@ -37,7 +51,7 @@ export const loadSubtree = ({ token, siteaccess, subtree, sortClause, sortOrder const request = new Request(path, { method: 'POST', - mode: 'same-origin', + mode: getRequestMode({ instanceUrl }), credentials: 'same-origin', body: JSON.stringify({ LoadSubtreeRequest: { @@ -45,12 +59,15 @@ export const loadSubtree = ({ token, siteaccess, subtree, sortClause, sortOrder nodes: subtree, }, }), - headers: { - Accept: 'application/vnd.ibexa.api.ContentTreeRoot+json', - 'Content-Type': 'application/vnd.ibexa.api.ContentTreeLoadSubtreeRequest+json', - 'X-Siteaccess': siteaccess, - 'X-CSRF-Token': token, - }, + headers: getRequestHeaders({ + token, + siteaccess, + accessToken, + extraHeaders: { + Accept: 'application/vnd.ibexa.api.ContentTreeRoot+json', + 'Content-Type': 'application/vnd.ibexa.api.ContentTreeLoadSubtreeRequest+json', + }, + }), }); fetch(request) diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/bookmarks.tab.module.js b/src/bundle/ui-dev/src/modules/universal-discovery/bookmarks.tab.module.js index 09e74c558e..7c8352d0f3 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/bookmarks.tab.module.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/bookmarks.tab.module.js @@ -18,7 +18,8 @@ import { } from './universal.discovery.module'; import { loadAccordionData } from './services/universal.discovery.service'; -const { Translator, ibexa } = window; +import { getIconPath } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/icon.helper'; +import { getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const BookmarksTabModule = () => { const shouldRestorePreviousStateRef = useRef(true); @@ -95,17 +96,11 @@ const BookmarksTabModule = () => { ); }; -ibexa.addConfig( - 'adminUiConfig.universalDiscoveryWidget.tabs', - [ - { - id: 'bookmarks', - component: BookmarksTabModule, - label: Translator.trans(/*@Desc("Bookmarks")*/ 'bookmarks.label', {}, 'ibexa_universal_discovery_widget'), - icon: ibexa.helpers.icon.getIconPath('bookmark'), - }, - ], - true, -); +export const BookmarksTab = { + id: 'bookmarks', + component: BookmarksTabModule, + getLabel: () => getTranslator().trans(/*@Desc("Bookmarks")*/ 'bookmarks.label', {}, 'ibexa_universal_discovery_widget'), + getIcon: () => getIconPath('bookmark'), +}; export default BookmarksTabModule; diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/browse.tab.module.js b/src/bundle/ui-dev/src/modules/universal-discovery/browse.tab.module.js index 2892faa225..124e0ae6a8 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/browse.tab.module.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/browse.tab.module.js @@ -7,7 +7,8 @@ import TreeView from './components/tree-view/tree.view'; import { CurrentViewContext, TabsConfigContext } from './universal.discovery.module'; -const { Translator, ibexa } = window; +import { getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; +import { getIconPath } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/icon.helper'; const BrowseTabModule = () => { const [currentView] = useContext(CurrentViewContext); @@ -25,17 +26,11 @@ const BrowseTabModule = () => { ); }; -ibexa.addConfig( - 'adminUiConfig.universalDiscoveryWidget.tabs', - [ - { - id: 'browse', - component: BrowseTabModule, - label: Translator.trans(/*@Desc("Browse")*/ 'browse.label', {}, 'ibexa_universal_discovery_widget'), - icon: window.ibexa.helpers.icon.getIconPath('browse'), - }, - ], - true, -); +export const BrowseTab = { + id: 'browse', + component: BrowseTabModule, + getLabel: () => getTranslator().trans(/*@Desc("Browse")*/ 'browse.label', {}, 'ibexa_universal_discovery_widget'), + getIcon: () => getIconPath('browse'), +}; export default BrowseTabModule; diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/actions-menu/actions.menu.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/actions-menu/actions.menu.js index 32703fecca..339875195b 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/actions-menu/actions.menu.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/actions-menu/actions.menu.js @@ -1,10 +1,10 @@ import React, { useContext } from 'react'; import { AllowConfirmationContext, ConfirmContext, CancelContext, SelectedLocationsContext } from '../../universal.discovery.module'; - -const { Translator } = window; +import { getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const ActionsMenu = () => { + const Translator = getTranslator(); const onConfirm = useContext(ConfirmContext); const cancelUDW = useContext(CancelContext); const allowConfirmation = useContext(AllowConfirmationContext); diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/bookmarks-list/bookmarks.list.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/bookmarks-list/bookmarks.list.js index 35a50aae60..47467506b3 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/bookmarks-list/bookmarks.list.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/bookmarks-list/bookmarks.list.js @@ -1,6 +1,8 @@ import React, { useContext, useState, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; +import { parse as parseTooltip } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper'; + import Icon from '../../../common/icon/icon'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; @@ -15,8 +17,6 @@ import { AllowedContentTypesContext, } from '../../universal.discovery.module'; -const { ibexa } = window; - const SCROLL_OFFSET = 200; const BookmarksList = ({ setBookmarkedLocationMarked, itemsPerPage }) => { @@ -62,7 +62,7 @@ const BookmarksList = ({ setBookmarkedLocationMarked, itemsPerPage }) => { }, [data.items, isLoading]); useEffect(() => { - ibexa.helpers.tooltips.parse(refBookmarksList.current); + parseTooltip(refBookmarksList.current); }, [bookmarks]); if (!bookmarks.length) { diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/breadcrumbs/breadcrumbs.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/breadcrumbs/breadcrumbs.js index 9608b37260..e2a2feafd1 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/breadcrumbs/breadcrumbs.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/breadcrumbs/breadcrumbs.js @@ -4,10 +4,10 @@ import Icon from '../../../common/icon/icon'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; import { LoadedLocationsMapContext } from '../../universal.discovery.module'; - -const { Translator } = window; +import { getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const Breadcrumbs = () => { + const Translator = getTranslator(); const [loadedLocationsMap, dispatchLoadedLocationsAction] = useContext(LoadedLocationsMapContext); const [hiddenListVisible, setHiddenListVisible] = useState(false); const { visibleItems, hiddenItems } = useMemo(() => { diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/collapsible/collapsible.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/collapsible/collapsible.js index be4f682c19..232ae1c7c0 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/collapsible/collapsible.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/collapsible/collapsible.js @@ -1,8 +1,7 @@ import React, { useState } from 'react'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; - -const { ibexa } = window; +import { parse as parseTooltip } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper'; const Collapsible = ({ isInitiallyExpanded, title, children }) => { const [isExpanded, setIsExpanded] = useState(isInitiallyExpanded); @@ -12,7 +11,7 @@ const Collapsible = ({ isInitiallyExpanded, title, children }) => { }); const toggleCollapsed = () => setIsExpanded((prevState) => !prevState); const initTooltipsRef = (node) => { - ibexa.helpers.tooltips.parse(node); + parseTooltip(node); }; return ( diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-create-button/content.create.button.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-create-button/content.create.button.js index e2bbe92d49..0d9b89606e 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-create-button/content.create.button.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-create-button/content.create.button.js @@ -1,6 +1,8 @@ import React, { useContext } from 'react'; import PropTypes from 'prop-types'; +import { getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; +import { hideAll as hideAllTooltips } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper'; import Icon from '../../../common/icon/icon'; import { @@ -13,9 +15,8 @@ import { ContentTypesMapContext, } from '../../universal.discovery.module'; -const { Translator, ibexa } = window; - const ContentCreateButton = ({ isDisabled }) => { + const Translator = getTranslator(); const [markedLocationId] = useContext(MarkedLocationIdContext); const [loadedLocationsMap] = useContext(LoadedLocationsMapContext); const [, setCreateContentVisible] = useContext(CreateContentWidgetContext); @@ -25,7 +26,7 @@ const ContentCreateButton = ({ isDisabled }) => { const contentTypesMap = useContext(ContentTypesMapContext); const createLabel = Translator.trans(/*@Desc("Create")*/ 'create_content.create', {}, 'ibexa_universal_discovery_widget'); const toggleContentCreateVisibility = () => { - ibexa.helpers.tooltips.hideAll(); + hideAllTooltips(); setCreateContentVisible((prevState) => !prevState); }; let selectedLocation = loadedLocationsMap.find((loadedLocation) => loadedLocation.parentLocationId === markedLocationId); @@ -72,16 +73,10 @@ ContentCreateButton.defaultProps = { isDisabled: false, }; -ibexa.addConfig( - 'adminUiConfig.universalDiscoveryWidget.topMenuActions', - [ - { - id: 'content-create-button', - priority: 30, - component: ContentCreateButton, - }, - ], - true, -); +export const ContentCreateButtonMenuItem = { + id: 'content-create-button', + priority: 30, + component: ContentCreateButton, +}; export default ContentCreateButton; diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-create-widget/content.create.widget.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-create-widget/content.create.widget.js index 39bbc825db..3dda5eb95b 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-create-widget/content.create.widget.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-create-widget/content.create.widget.js @@ -1,8 +1,9 @@ import React, { useContext, useState, useEffect, useRef } from 'react'; +import { createCssClassNames } from '../../../common/helpers/css.class.names'; import Icon from '../../../common/icon/icon'; +import Dropdown from '../../../common/dropdown/dropdown'; -import { createCssClassNames } from '../../../common/helpers/css.class.names'; import { DropdownPortalRefContext, CreateContentWidgetContext, @@ -13,25 +14,25 @@ import { ContentOnTheFlyConfigContext, AllowedContentTypesContext, } from '../../universal.discovery.module'; -import Dropdown from '../../../common/dropdown/dropdown'; - -const { Translator, ibexa } = window; -const configLanguages = ibexa.adminUiConfig.languages; -const languages = configLanguages.priority.map((languageCode) => { - return configLanguages.mappings[languageCode]; -}); -const contentTypes = Object.entries(ibexa.adminUiConfig.contentTypes); +import { parse as parseTooltip } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper'; +import { getAdminUiConfig, getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const ContentCreateWidget = () => { + const Translator = getTranslator(); + const adminUiConfig = getAdminUiConfig(); const refContentTree = useRef(null); const dropdownListRef = useContext(DropdownPortalRefContext); const [markedLocationId] = useContext(MarkedLocationIdContext); const [loadedLocationsMap] = useContext(LoadedLocationsMapContext); const { allowedLanguages, preselectedLanguage, preselectedContentType } = useContext(ContentOnTheFlyConfigContext); const allowedContentTypes = useContext(AllowedContentTypesContext); + const { languages, contentTypes } = adminUiConfig; const selectedLocation = loadedLocationsMap.find((loadedLocation) => loadedLocation.parentLocationId === markedLocationId); - const filteredLanguages = languages.filter((language) => { + const mappedLanguages = languages.priority.map((languageCode) => { + return languages.mappings[languageCode]; + }); + const filteredLanguages = mappedLanguages.filter((language) => { const userHasPermission = !selectedLocation || !selectedLocation.permissions || @@ -101,7 +102,7 @@ const ContentCreateWidget = () => { 'ibexa-extra-actions--hidden': !createContentVisible, 'c-content-create': true, }); - const languageOptions = languages + const languageOptions = mappedLanguages .filter((language) => language.enabled) .map((language) => ({ value: language.languageCode, @@ -113,7 +114,7 @@ const ContentCreateWidget = () => { }, [preselectedLanguage, firstLanguageCode]); useEffect(() => { - ibexa.helpers.tooltips.parse(refContentTree.current); + parseTooltip(refContentTree.current); }, []); return ( @@ -160,7 +161,7 @@ const ContentCreateWidget = () => {
    {filtersDescLabel}
    - {contentTypes.map(([groupName, groupItems]) => { + {Object.entries(contentTypes).map(([groupName, groupItems]) => { const restrictedContentTypeIds = selectedLocation?.permissions?.create.restrictedContentTypeIds ?? []; const isHiddenGroup = groupItems.every((groupItem) => { const isNotSearchedName = filterQuery && !groupItem.name.toLowerCase().includes(filterQuery); diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-edit-button/content.edit.button.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-edit-button/content.edit.button.js index 0d07884e4b..511f305da1 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-edit-button/content.edit.button.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-edit-button/content.edit.button.js @@ -4,7 +4,8 @@ import PropTypes from 'prop-types'; import Icon from '../../../common/icon/icon'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; import TranslationSelector from '../translation-selector/translation.selector'; -import { createDraft } from '../..//services/universal.discovery.service'; +import { getAdminUiConfig, getRouting } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; +import { createDraft } from '../../services/universal.discovery.service'; import { RestInfoContext, EditOnTheFlyDataContext, @@ -13,9 +14,9 @@ import { ContentTypesMapContext, } from '../..//universal.discovery.module'; -const { Routing, ibexa } = window; - const ContentEditButton = ({ version, location, isDisabled, label }) => { + const Routing = getRouting(); + const adminUiConfig = getAdminUiConfig(); const restInfo = useContext(RestInfoContext); const allowRedirects = useContext(AllowRedirectsContext); const [, setEditOnTheFlyData] = useContext(EditOnTheFlyDataContext); @@ -23,7 +24,7 @@ const ContentEditButton = ({ version, location, isDisabled, label }) => { const contentTypesMap = useContext(ContentTypesMapContext); const [isTranslationSelectorVisible, setIsTranslationSelectorVisible] = useState(false); const contentTypeInfo = contentTypesMap[location.ContentInfo.Content.ContentType._href]; - const isUserContentType = ibexa.adminUiConfig.userContentTypes.includes(contentTypeInfo.identifier); + const isUserContentType = adminUiConfig.userContentTypes.includes(contentTypeInfo.identifier); const btnClassName = createCssClassNames({ 'c-content-edit-button__btn btn ibexa-btn ibexa-btn--ghost': true, 'ibexa-btn--no-text': label !== null, diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-edit-button/selected.item.edit.button.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-edit-button/selected.item.edit.button.js index c7cd0e7825..63670503a0 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-edit-button/selected.item.edit.button.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-edit-button/selected.item.edit.button.js @@ -3,8 +3,6 @@ import PropTypes from 'prop-types'; import ContentEditButton from '../content-edit-button/content.edit.button'; -const { ibexa } = window; - const SelectedItemEditButton = ({ location, permissions }) => { const hasAccess = permissions && permissions.edit.hasAccess; @@ -15,21 +13,15 @@ const SelectedItemEditButton = ({ location, permissions }) => { ); }; -ibexa.addConfig( - 'adminUiConfig.universalDiscoveryWidget.selectedItemActions', - [ - { - id: 'content-edit-button', - priority: 30, - component: SelectedItemEditButton, - }, - ], - true, -); - SelectedItemEditButton.propTypes = { location: PropTypes.object.isRequired, permissions: PropTypes.object.isRequired, }; +export const SelectedItemEditMenuButton = { + id: 'content-edit-button', + priority: 30, + component: SelectedItemEditButton, +}; + export default SelectedItemEditButton; diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.item.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.item.js index 41a9e68836..bf42a03d8b 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.item.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.item.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import ToggleSelection from '../toggle-selection/toggle.selection'; 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 { @@ -32,7 +33,6 @@ const ContentTableItem = ({ location }) => { const [, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext); const [multiple] = useContext(MultipleConfigContext); const rootLocationId = useContext(RootLocationIdContext); - const { formatShortDateTime } = window.ibexa.helpers.timezone; const allowedContentTypes = useContext(AllowedContentTypesContext); const contentTypeInfo = contentTypesMap[location.ContentInfo.Content.ContentType._href]; const containersOnly = useContext(ContainersOnlyContext); diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.js index b19a3ea303..6060316e67 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-table/content.table.js @@ -5,9 +5,11 @@ import ContentTableItem from './content.table.item'; import Pagination from '../../../common/pagination/pagination'; import { MultipleConfigContext } from '../../universal.discovery.module'; -const { Translator, ibexa } = window; +import { getTranslator } from '../../../../../../Resources/public/js/scripts/helpers/context.helper'; +import { parse as parseTooltip } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper'; const ContentTable = ({ count, itemsPerPage, items, activePageIndex, title, onPageChange, renderCustomHeader }) => { + const Translator = getTranslator(); const [multiple] = useContext(MultipleConfigContext); const refContentTable = useRef(null); const nameLabel = Translator.trans(/*@Desc("Name")*/ 'content_table.name', {}, 'ibexa_universal_discovery_widget'); @@ -24,7 +26,7 @@ const ContentTable = ({ count, itemsPerPage, items, activePageIndex, title, onPa ); useEffect(() => { - ibexa.helpers.tooltips.parse(refContentTable.current); + parseTooltip(refContentTable.current); }, []); return ( diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-type-selector/content.type.selector.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-type-selector/content.type.selector.js index 2402cccbb5..fc06e1bcb3 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/content-type-selector/content.type.selector.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/content-type-selector/content.type.selector.js @@ -4,11 +4,11 @@ import { SelectedContentTypesContext } from '../search/search'; import { AllowedContentTypesContext } from '../../universal.discovery.module'; import Collapsible from '../collapsible/collapsible'; - -const { ibexa } = window; +import { getAdminUiConfig } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const ContentTypeSelector = () => { - const { contentTypes: contentTypesMap } = ibexa.adminUiConfig; + const adminUiConfig = getAdminUiConfig(); + const { contentTypes: contentTypesMap } = adminUiConfig; const allowedContentTypes = useContext(AllowedContentTypesContext); const [selectedContentTypes, dispatchSelectedContentTypesAction] = useContext(SelectedContentTypesContext); const handleContentTypeSelect = ({ nativeEvent }) => { diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/filters/filters.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/filters/filters.js index bfe98ee34b..8bc781a9df 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/filters/filters.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/filters/filters.js @@ -9,17 +9,23 @@ import { SelectedLanguageContext, SelectedSubtreeBreadcrumbsContext, } from '../search/search'; + import UniversalDiscoveryModule, { DropdownPortalRefContext } from '../../universal.discovery.module'; import Dropdown from '../../../common/dropdown/dropdown'; import ContentTypeSelector from '../content-type-selector/content.type.selector'; import Icon from '../../../common/icon/icon'; -const { Translator, ibexa } = window; - -const languages = Object.values(ibexa.adminUiConfig.languages.mappings); +import { + removeRootFromPathString, + findLocationsByIds, + buildLocationsBreadcrumbs, +} from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/location.helper'; +import { getAdminUiConfig, getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const Filters = ({ search }) => { + const Translator = getTranslator(); + const adminUiConfig = getAdminUiConfig(); const [selectedContentTypes, dispatchSelectedContentTypesAction] = useContext(SelectedContentTypesContext); const [selectedSection, setSelectedSection] = useContext(SelectedSectionContext); const [selectedSubtree, setSelectedSubtree] = useContext(SelectedSubtreeContext); @@ -31,7 +37,6 @@ const Filters = ({ search }) => { const [isNestedUdwOpened, setIsNestedUdwOpened] = useState(false); const filterSubtreeUdwConfig = JSON.parse(window.document.querySelector('#react-udw').dataset.filterSubtreeUdwConfig); const handleNestedUdwConfirm = (items) => { - const { removeRootFromPathString, findLocationsByIds, buildLocationsBreadcrumbs } = ibexa.helpers.location; const [{ pathString }] = items; findLocationsByIds(removeRootFromPathString(pathString), (locations) => @@ -41,11 +46,10 @@ const Filters = ({ search }) => { setSelectedSubtree(pathString); setIsNestedUdwOpened(false); }; - const nestedUdwConfig = { onConfirm: handleNestedUdwConfirm, onCancel: () => setIsNestedUdwOpened(false), - tabs: ibexa.adminUiConfig.universalDiscoveryWidget.tabs, + tabs: adminUiConfig.universalDiscoveryWidget.tabs, title: 'Browsing content', ...filterSubtreeUdwConfig, }; @@ -113,13 +117,13 @@ const Filters = ({ search }) => { const subtreeLabel = Translator.trans(/*@Desc("Subtree")*/ 'filters.subtree', {}, 'ibexa_universal_discovery_widget'); const clearLabel = Translator.trans(/*@Desc("Clear")*/ 'filters.clear', {}, 'ibexa_universal_discovery_widget'); const applyLabel = Translator.trans(/*@Desc("Apply")*/ 'filters.apply', {}, 'ibexa_universal_discovery_widget'); - const languageOptions = languages + const languageOptions = Object.values(adminUiConfig.languages.mappings) .filter((language) => language.enabled) .map((language) => ({ value: language.languageCode, label: language.name, })); - const sectionOptions = Object.entries(ibexa.adminUiConfig.sections).map(([sectionIdentifier, sectionName]) => ({ + const sectionOptions = Object.entries(adminUiConfig.sections).map(([sectionIdentifier, sectionName]) => ({ value: sectionIdentifier, label: sectionName, })); diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.branch.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.branch.js index 11125f165f..31cb4ac20d 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.branch.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.branch.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import FinderLeaf from './finder.leaf'; import Icon from '../../../common/icon/icon'; +import { getIconPath } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/icon.helper.js'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; import { useFindLocationsByParentLocationIdFetch } from '../../hooks/useFindLocationsByParentLocationIdFetch'; @@ -15,8 +16,6 @@ import { SORTING_OPTIONS, } from '../../universal.discovery.module'; -const { ibexa } = window; - const CLASS_IS_BRANCH_RESIZING = 'ibexa-is-branch-resizing'; const SCROLL_OFFSET = 200; @@ -89,7 +88,7 @@ const FinderBranch = ({ locationData, itemsPerPage }) => { const contentName = selectedLocation ? selectedLocation.location.ContentInfo.Content.TranslatedName : ''; const iconPath = locationData.location ? contentTypesMap[locationData.location.ContentInfo.Content.ContentType._href].thumbnail - : ibexa.helpers.icon.getIconPath('folder'); + : getIconPath('folder'); return (
    diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.leaf.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.leaf.js index 4d279e2331..045fd38639 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.leaf.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/finder/finder.leaf.js @@ -1,6 +1,8 @@ import React, { useContext, useEffect } from 'react'; import PropTypes from 'prop-types'; +import { parse as parseTooltip } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper'; + import ToggleSelection from '../toggle-selection/toggle.selection'; import Icon from '../../../common/icon/icon'; @@ -15,7 +17,7 @@ import { AllowedContentTypesContext, } from '../../universal.discovery.module'; -const { document, ibexa } = window; +const { document } = window; const FinderLeaf = ({ location }) => { const [markedLocationId, setMarkedLocationId] = useContext(MarkedLocationIdContext); @@ -62,7 +64,7 @@ const FinderLeaf = ({ location }) => { }); useEffect(() => { - ibexa.helpers.tooltips.parse(document.querySelector('.c-udw-tab')); + parseTooltip(document.querySelector('.c-udw-tab')); }, []); return ( diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/search/search.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/search/search.js index 6e1d2775c8..48c072c7d1 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/search/search.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/search/search.js @@ -14,8 +14,7 @@ import SearchTags from './search.tags'; import { useSearchByQueryFetch } from '../../hooks/useSearchByQueryFetch'; import { AllowedContentTypesContext, SearchTextContext } from '../../universal.discovery.module'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; - -const { Translator, ibexa } = window; +import { getAdminUiConfig, getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const selectedContentTypesReducer = (state, action) => { switch (action.type) { @@ -30,12 +29,9 @@ const selectedContentTypesReducer = (state, action) => { } }; -const configLanguages = ibexa.adminUiConfig.languages; -const languages = configLanguages.priority.map((value) => { - return configLanguages.mappings[value]; -}); - const Search = ({ itemsPerPage }) => { + const Translator = getTranslator(); + const adminUiConfig = getAdminUiConfig(); const allowedContentTypes = useContext(AllowedContentTypesContext); const [searchText] = useContext(SearchTextContext); const [offset, setOffset] = useState(0); @@ -43,10 +39,15 @@ const Search = ({ itemsPerPage }) => { const [selectedSection, setSelectedSection] = useState(''); const [selectedSubtree, setSelectedSubtree] = useState(''); const [selectedSubtreeBreadcrumbs, setSelectedSubtreeBreadcrumbs] = useState(''); - const firstLanguageCode = languages.length ? languages[0].languageCode : ''; + const { languages } = adminUiConfig; + const mappedLanguages = languages.priority.map((value) => { + return languages.mappings[value]; + }); + const firstLanguageCode = mappedLanguages.length ? mappedLanguages[0].languageCode : ''; const [selectedLanguage, setSelectedLanguage] = useState(firstLanguageCode); const prevSearchText = useRef(null); const [isLoading, data, searchByQuery] = useSearchByQueryFetch(); + const search = () => { const shouldResetOffset = prevSearchText.current !== searchText && offset !== 0; @@ -64,7 +65,7 @@ const Search = ({ itemsPerPage }) => { }; const changePage = (pageIndex) => setOffset(pageIndex * itemsPerPage); const renderCustomTableHeader = () => { - const selectedLanguageName = ibexa.adminUiConfig.languages.mappings[selectedLanguage].name; + const selectedLanguageName = languages.mappings[selectedLanguage].name; const searchResultsTitle = Translator.trans( /*@Desc("Results for “%search_phrase%” (%total%)")*/ 'search.search_results', { diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/search/search.tags.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/search/search.tags.js index 9cf2c3feea..bd58bbfbc7 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/search/search.tags.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/search/search.tags.js @@ -6,10 +6,10 @@ import { SelectedSubtreeBreadcrumbsContext, } from '../search/search'; import Tag from '../../../common/tag/tag'; - -const { ibexa } = window; +import { getAdminUiConfig } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const SearchTags = () => { + const adminUiConfig = getAdminUiConfig(); const [selectedContentTypes, dispatchSelectedContentTypesAction] = useContext(SelectedContentTypesContext); const [selectedSection, setSelectedSection] = useContext(SelectedSectionContext); const [, setSelectedSubtree] = useContext(SelectedSubtreeContext); @@ -18,7 +18,7 @@ const SearchTags = () => { setSelectedSubtree(''); setSelectedSubtreeBreadcrumbs(''); }; - const contentTypesMap = Object.values(ibexa.adminUiConfig.contentTypes).reduce((contentTypeDataMap, contentTypeGroup) => { + const contentTypesMap = Object.values(adminUiConfig.contentTypes).reduce((contentTypeDataMap, contentTypeGroup) => { for (const contentTypeData of contentTypeGroup) { contentTypeDataMap[contentTypeData.identifier] = contentTypeData; } diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/selected-locations/selected.locations.item.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/selected-locations/selected.locations.item.js index 42c674a898..010f4b082d 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/selected-locations/selected.locations.item.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/selected-locations/selected.locations.item.js @@ -1,14 +1,20 @@ import React, { useContext, useEffect, useMemo, useRef } from 'react'; import PropTypes from 'prop-types'; +import { + parse as parseTooltip, + hideAll as hideAllTooltips, +} from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper'; + import Icon from '../../../common/icon/icon'; import Thumbnail from '../../../common/thumbnail/thumbnail'; import { SelectedLocationsContext, ContentTypesMapContext } from '../../universal.discovery.module'; - -const { Translator, ibexa } = window; +import { getAdminUiConfig, getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const SelectedLocationsItem = ({ location, permissions }) => { + const adminUiConfig = getAdminUiConfig(); + const Translator = getTranslator(); const refSelectedLocationsItem = useRef(null); const [, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext); const contentTypesMap = useContext(ContentTypesMapContext); @@ -18,11 +24,11 @@ const SelectedLocationsItem = ({ location, permissions }) => { 'ibexa_universal_discovery_widget', ); const removeFromSelection = () => { - ibexa.helpers.tooltips.hideAll(refSelectedLocationsItem.current); + hideAllTooltips(refSelectedLocationsItem.current); dispatchSelectedLocationsAction({ type: 'REMOVE_SELECTED_LOCATION', id: location.id }); }; const sortedActions = useMemo(() => { - const { selectedItemActions } = ibexa.adminUiConfig.universalDiscoveryWidget; + const { selectedItemActions } = adminUiConfig.universalDiscoveryWidget; const actions = selectedItemActions ? [...selectedItemActions] : []; return actions.sort((actionA, actionB) => { @@ -33,7 +39,7 @@ const SelectedLocationsItem = ({ location, permissions }) => { const thumbnailData = version ? version.Thumbnail : {}; useEffect(() => { - ibexa.helpers.tooltips.parse(refSelectedLocationsItem.current); + parseTooltip(refSelectedLocationsItem.current); }, []); return ( diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/selected-locations/selected.locations.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/selected-locations/selected.locations.js index 26b3dc0451..87fe7a38ca 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/selected-locations/selected.locations.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/selected-locations/selected.locations.js @@ -1,14 +1,19 @@ import React, { useContext, useState, useEffect, useRef } from 'react'; +import { + parse as parseTooltip, + hideAll as hideAllTooltips, +} from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper'; +import { getBootstrap, getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; + import Icon from '../../../common/icon/icon'; import SelectedLocationsItem from './selected.locations.item'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; import { SelectedLocationsContext, AllowConfirmationContext } from '../../universal.discovery.module'; -const { Translator, ibexa, bootstrap } = window; - const SelectedLocations = () => { + const Translator = getTranslator(); const refSelectedLocations = useRef(null); const refTogglerButton = useRef(null); const [selectedLocations, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext); @@ -30,7 +35,7 @@ const SelectedLocations = () => { ); const togglerLabel = isExpanded ? collapseLabel : expandLabel; const clearSelection = () => { - ibexa.helpers.tooltips.hideAll(refSelectedLocations.current); + hideAllTooltips(refSelectedLocations.current); dispatchSelectedLocationsAction({ type: 'CLEAR_SELECTED_LOCATIONS' }); }; const toggleExpanded = () => { @@ -102,9 +107,10 @@ const SelectedLocations = () => { }; useEffect(() => { - ibexa.helpers.tooltips.parse(refSelectedLocations.current); - ibexa.helpers.tooltips.hideAll(); + parseTooltip(refSelectedLocations.current); + hideAllTooltips(); + const bootstrap = getBootstrap(); const toggleButtonTooltip = bootstrap.Tooltip.getOrCreateInstance('.c-selected-locations__toggle-button'); toggleButtonTooltip.setContent({ '.tooltip-inner': togglerLabel }); diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/sort-switcher/sort.switcher.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/sort-switcher/sort.switcher.js index 023119ee90..c398082678 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/sort-switcher/sort.switcher.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/sort-switcher/sort.switcher.js @@ -4,8 +4,6 @@ import PropTypes from 'prop-types'; import SimpleDropdown from '../../../common/simple-dropdown/simple.dropdown'; import { SortingContext, SortOrderContext, SORTING_OPTIONS } from '../../universal.discovery.module'; -const { ibexa } = window; - const SortSwitcher = ({ isDisabled }) => { const [sorting, setSorting] = useContext(SortingContext); const [sortOrder, setSortOrder] = useContext(SortOrderContext); @@ -36,16 +34,10 @@ SortSwitcher.defaultProps = { isDisabled: false, }; -ibexa.addConfig( - 'adminUiConfig.universalDiscoveryWidget.topMenuActions', - [ - { - id: 'sort-switcher', - priority: 20, - component: SortSwitcher, - }, - ], - true, -); +export const SortSwitcherMenuButton = { + id: 'sort-switcher', + priority: 20, + component: SortSwitcher, +}; export default SortSwitcher; diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/tab-selector/tab.selector.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/tab-selector/tab.selector.js index 701a7a398e..2a7b5c3b31 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/tab-selector/tab.selector.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/tab-selector/tab.selector.js @@ -39,10 +39,10 @@ const TabSelector = () => { className={className} key={tab.id} onClick={onClick} - title={tab.label} + title={tab.getLabel()} data-tooltip-container-selector=".c-udw-tab" > - +
    ); })} diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/tab/tab.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/tab/tab.js index d705f0f5f3..82964f0856 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/tab/tab.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/tab/tab.js @@ -6,16 +6,14 @@ import ActionsMenu from '../actions-menu/actions.menu'; import TabSelector from '../tab-selector/tab.selector'; import SelectedLocations from '../selected-locations/selected.locations'; import ContentCreateWidget from '../content-create-widget/content.create.widget'; +import ContentMetaPreview from '../../content.meta.preview.module'; import { SelectedLocationsContext, DropdownPortalRefContext, MultipleConfigContext } from '../../universal.discovery.module'; -const { ibexa } = window; - const Tab = ({ children, actionsDisabledMap }) => { const topBarRef = useRef(); const bottomBarRef = useRef(); const [contentHeight, setContentHeight] = useState('100%'); - const ContentMetaPreview = ibexa.adminUiConfig.universalDiscoveryWidget.contentMetaPreview; const [selectedLocations] = useContext(SelectedLocationsContext); const dropdownPortalRef = useContext(DropdownPortalRefContext); const [multiple] = useContext(MultipleConfigContext); diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/top-menu/top.menu.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/top-menu/top.menu.js index 153ad69752..4e6555c1cb 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/top-menu/top.menu.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/top-menu/top.menu.js @@ -6,15 +6,17 @@ import Icon from '../../../common/icon/icon'; import { TitleContext, CancelContext } from '../../universal.discovery.module'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; - -const { Translator, ibexa } = window; +import { getAdminUiConfig, getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const TopMenu = ({ actionsDisabledMap }) => { + const Translator = getTranslator(); + const adminUiConfig = getAdminUiConfig(); + const { topMenuActions } = adminUiConfig.universalDiscoveryWidget; const title = useContext(TitleContext); const cancelUDW = useContext(CancelContext); const [isSearchOpened, setIsSearchOpened] = useState(false); const sortedActions = useMemo(() => { - const actions = [...ibexa.adminUiConfig.universalDiscoveryWidget.topMenuActions]; + const actions = topMenuActions; return actions.sort((actionA, actionB) => { return actionB.priority - actionA.priority; diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/translation-selector/translation.selector.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/translation-selector/translation.selector.js index 202da88f31..32e4b4d050 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/translation-selector/translation.selector.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/translation-selector/translation.selector.js @@ -4,9 +4,11 @@ import PropTypes from 'prop-types'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; import Icon from '../../../common/icon/icon'; -const { Translator, ibexa } = window; +import { getAdminUiConfig, getTranslator } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const TranslationSelectorButton = ({ hideTranslationSelector, selectTranslation, version, isOpen }) => { + const Translator = getTranslator(); + const adminUiConfig = getAdminUiConfig(); const languageCodes = version ? version.VersionInfo.languageCodes.split(',') : []; const editTranslationLabel = Translator.trans( /*@Desc("Select translation")*/ 'meta_preview.edit_translation', @@ -33,7 +35,7 @@ const TranslationSelectorButton = ({ hideTranslationSelector, selectTranslation, className="c-translation-selector__language" onClick={selectTranslation.bind(this, languageCode)} > - {ibexa.adminUiConfig.languages.mappings[languageCode].name} + {adminUiConfig.languages.mappings[languageCode].name}
    ))} diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-item-toggle-selection/tree.item.toggle.selection.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-item-toggle-selection/tree.item.toggle.selection.js index 29d2640e8d..6fbea50985 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-item-toggle-selection/tree.item.toggle.selection.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-item-toggle-selection/tree.item.toggle.selection.js @@ -1,6 +1,8 @@ import React, { useContext, useEffect } from 'react'; import PropTypes from 'prop-types'; +import { parse as parseTooltip } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper'; + import { UDWContext, SelectedLocationsContext, @@ -12,13 +14,13 @@ import { import { findLocationsById } from '../../services/universal.discovery.service'; import ToggleSelection from '../toggle-selection/toggle.selection'; -const { ibexa, document } = window; +const { document } = window; const TreeItemToggleSelection = ({ locationId, isContainer, contentTypeIdentifier }) => { const isUDW = useContext(UDWContext); useEffect(() => { - ibexa.helpers.tooltips.parse(document.querySelector('.c-list')); + parseTooltip(document.querySelector('.c-list')); }, []); if (!isUDW) { @@ -53,17 +55,11 @@ const TreeItemToggleSelection = ({ locationId, isContainer, contentTypeIdentifie ); }; -ibexa.addConfig( - 'adminUiConfig.contentTreeWidget.secondaryItemActions', - [ - { - id: 'toggle-selection-button', - priority: 30, - component: TreeItemToggleSelection, - }, - ], - true, -); +export const TreeItemToggleSelectionMenuButton = { + id: 'toggle-selection-button', + priority: 30, + component: TreeItemToggleSelection, +}; TreeItemToggleSelection.propTypes = { locationId: PropTypes.number.isRequired, diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-view/tree.view.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-view/tree.view.js index 1a9728b8d1..b57a1310f3 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-view/tree.view.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/tree-view/tree.view.js @@ -1,7 +1,9 @@ import React, { useContext, useMemo } from 'react'; import PropTypes from 'prop-types'; +import { getId as getUserId } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/user.helper'; import { createCssClassNames } from '../../../common/helpers/css.class.names'; + import ContentTreeModule from '../../../content-tree/content.tree.module'; import { loadAccordionData } from '../../services/universal.discovery.service'; import { getLocationData } from '../../content.meta.preview.module'; @@ -18,10 +20,10 @@ import { SortOrderContext, SortingContext, } from '../../universal.discovery.module'; - -const { ibexa } = window; +import { getAdminUiConfig } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const TreeView = ({ itemsPerPage }) => { + const adminUiConfig = getAdminUiConfig(); const [loadedLocationsMap, dispatchLoadedLocationsAction] = useContext(LoadedLocationsMapContext); const [markedLocationId, setMarkedLocationId] = useContext(MarkedLocationIdContext); const [multiple] = useContext(MultipleConfigContext); @@ -34,7 +36,7 @@ const TreeView = ({ itemsPerPage }) => { const restInfo = useContext(RestInfoContext); const rootLocationId = useContext(RootLocationIdContext); const locationData = useMemo(() => getLocationData(loadedLocationsMap, markedLocationId), [markedLocationId, loadedLocationsMap]); - const userId = ibexa.helpers.user.getId(); + const userId = adminUiConfig.userId ?? getUserId(); const expandItem = (item, event) => { event.preventDefault(); event.currentTarget.closest('.c-list-item__row').querySelector('.c-list-item__toggler').click(); @@ -107,6 +109,9 @@ const TreeView = ({ itemsPerPage }) => { userId={userId} currentLocationPath={currentLocationPath} rootLocationId={rootLocationId} + subitemsLimit={adminUiConfig.contentTree.childrenLoadMaxLimi} + subitemsLoadLimit={adminUiConfig.contentTree.loadMoreLimit} + treeMaxDepth={adminUiConfig.contentTree.treeMaxDepth} restInfo={restInfo} onClickItem={expandItem} readSubtree={readSubtree} diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/components/view-switcher/view.switcher.js b/src/bundle/ui-dev/src/modules/universal-discovery/components/view-switcher/view.switcher.js index e2d734b90e..770e5df0fc 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/components/view-switcher/view.switcher.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/components/view-switcher/view.switcher.js @@ -2,11 +2,11 @@ import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import SimpleDropdown from '../../../common/simple-dropdown/simple.dropdown'; +import { getTranslator } from '../../../../../../Resources/public/js/scripts/helpers/context.helper'; import { CurrentViewContext, VIEWS } from '../../universal.discovery.module'; -const { ibexa, Translator } = window; - const ViewSwitcher = ({ isDisabled }) => { + const Translator = getTranslator(); const viewLabel = Translator.trans(/*@Desc("View")*/ 'view_switcher.view', {}, 'ibexa_universal_discovery_widget'); const [currentView, setCurrentView] = useContext(CurrentViewContext); const selectedOption = VIEWS.find((option) => option.value === currentView); @@ -36,16 +36,10 @@ ViewSwitcher.defaultProps = { isDisabled: false, }; -ibexa.addConfig( - 'adminUiConfig.universalDiscoveryWidget.topMenuActions', - [ - { - id: 'view-switcher', - priority: 10, - component: ViewSwitcher, - }, - ], - true, -); +export const ViewSwitcherButton = { + id: 'view-switcher', + priority: 10, + component: ViewSwitcher, +}; export default ViewSwitcher; diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/config.loader.js b/src/bundle/ui-dev/src/modules/universal-discovery/config.loader.js new file mode 100644 index 0000000000..47e693aeb6 --- /dev/null +++ b/src/bundle/ui-dev/src/modules/universal-discovery/config.loader.js @@ -0,0 +1,32 @@ +import UniversalDiscoveryModule from './universal.discovery.module'; + +import { BookmarksTab } from './bookmarks.tab.module'; +import { BrowseTab } from './browse.tab.module'; +import { ContentCreateTab } from './content.create.tab.module'; +import { ContentEditTab } from './content.edit.tab.module'; +import { SearchTab } from './search.tab.module'; + +import { SortSwitcherMenuButton } from './components/sort-switcher/sort.switcher'; +import { ContentCreateButtonMenuItem } from './components/content-create-button/content.create.button'; +import { ViewSwitcherButton } from './components/view-switcher/view.switcher'; + +import { SelectedItemEditMenuButton } from './components/content-edit-button/selected.item.edit.button'; + +import { TreeItemToggleSelectionMenuButton } from './components/tree-item-toggle-selection/tree.item.toggle.selection'; + +(function (ibexa) { + ibexa.addConfig('modules.UniversalDiscovery', UniversalDiscoveryModule); + ibexa.addConfig('adminUiConfig.universalDiscoveryWidget.tabs', [BookmarksTab], true); + ibexa.addConfig('adminUiConfig.universalDiscoveryWidget.tabs', [BrowseTab], true); + ibexa.addConfig('adminUiConfig.universalDiscoveryWidget.tabs', [ContentCreateTab], true); + ibexa.addConfig('adminUiConfig.universalDiscoveryWidget.tabs', [ContentEditTab], true); + ibexa.addConfig('adminUiConfig.universalDiscoveryWidget.tabs', [SearchTab], true); + + ibexa.addConfig('adminUiConfig.universalDiscoveryWidget.topMenuActions', [ContentCreateButtonMenuItem], true); + ibexa.addConfig('adminUiConfig.universalDiscoveryWidget.topMenuActions', [SortSwitcherMenuButton], true); + ibexa.addConfig('adminUiConfig.universalDiscoveryWidget.topMenuActions', [ViewSwitcherButton], true); + + ibexa.addConfig('adminUiConfig.universalDiscoveryWidget.selectedItemActions', [SelectedItemEditMenuButton], true); + + ibexa.addConfig('adminUiConfig.contentTreeWidget.secondaryItemActions', [TreeItemToggleSelectionMenuButton], true); +})(window.ibexa); diff --git a/src/bundle/ui-dev/src/modules/universal-discovery/content.create.tab.module.js b/src/bundle/ui-dev/src/modules/universal-discovery/content.create.tab.module.js index f298094027..760ee79e3b 100644 --- a/src/bundle/ui-dev/src/modules/universal-discovery/content.create.tab.module.js +++ b/src/bundle/ui-dev/src/modules/universal-discovery/content.create.tab.module.js @@ -12,20 +12,15 @@ import { LoadedLocationsMapContext, MultipleConfigContext, } from './universal.discovery.module'; + import { findLocationsById } from './services/universal.discovery.service'; import deepClone from '../common/helpers/deep.clone.helper'; -const { ibexa, Translator, Routing } = window; - -const generateIframeUrl = ({ locationId, languageCode, contentTypeIdentifier }) => { - return Routing.generate('ibexa.content.on_the_fly.create', { - locationId, - languageCode, - contentTypeIdentifier, - }); -}; +import { getIconPath } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/icon.helper'; +import { getTranslator, getRouting } from '@ibexa-admin-ui/src/bundle/Resources/public/js/scripts/helpers/context.helper'; const ContentCreateTabModule = () => { + const Routing = getRouting(); const [contentOnTheFlyData, setContentOnTheFlyData] = useContext(ContentOnTheFlyDataContext); const tabs = useContext(TabsContext); const contentOnTheFlyConfig = useContext(ContentOnTheFlyConfigContext); @@ -36,8 +31,16 @@ const ContentCreateTabModule = () => { const [selectedLocations, dispatchSelectedLocationsAction] = useContext(SelectedLocationsContext); const [loadedLocationsMap, dispatchLoadedLocationsAction] = useContext(LoadedLocationsMapContext); const [multiple] = useContext(MultipleConfigContext); - const iframeUrl = generateIframeUrl(contentOnTheFlyData); const iframeRef = createRef(); + const getIframeUrl = () => { + const { locationId, languageCode, contentTypeIdentifier } = contentOnTheFlyData; + + return Routing.generate('ibexa.content.on_the_fly.create', { + locationId, + languageCode, + contentTypeIdentifier, + }); + }; const cancelContentCreate = () => { setCreateContentVisible(false); setContentOnTheFlyData({}); @@ -93,23 +96,17 @@ const ContentCreateTabModule = () => { return (
    -