diff --git a/dependencies.json b/dependencies.json new file mode 100644 index 0000000000..aaa643e4ee --- /dev/null +++ b/dependencies.json @@ -0,0 +1,11 @@ +{ + "recipesEndpoint": "", + "packages": [ + { + "requirement": "dev-ibx-9807-adjusted-date-range-filter as 4.6.x-dev", + "repositoryUrl": "https://github.com/ibexa/search", + "package": "ibexa/search", + "shouldBeAddedAsVCS": true + } + ] +} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4a49247916..7f92b0f688 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6702,36 +6702,12 @@ parameters: count: 1 path: src/lib/Form/DataTransformer/DateIntervalToArrayTransformer.php - - - message: '#^Call to function array_key_exists\(\) with ''date_interval'' and non\-empty\-array will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/lib/Form/DataTransformer/DateIntervalTransformer.php - - - - message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/lib/Form/DataTransformer/DateIntervalTransformer.php - - message: '#^Class Ibexa\\AdminUi\\Form\\DataTransformer\\DateIntervalTransformer implements generic interface Symfony\\Component\\Form\\DataTransformerInterface but does not specify its types\: T, R$#' identifier: missingType.generics count: 1 path: src/lib/Form/DataTransformer/DateIntervalTransformer.php - - - message: '#^Method Ibexa\\AdminUi\\Form\\DataTransformer\\DateIntervalTransformer\:\:reverseTransform\(\) has parameter \$value with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/lib/Form/DataTransformer/DateIntervalTransformer.php - - - - message: '#^Method Ibexa\\AdminUi\\Form\\DataTransformer\\DateIntervalTransformer\:\:reverseTransform\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/lib/Form/DataTransformer/DateIntervalTransformer.php - - message: '#^Method Ibexa\\AdminUi\\Form\\DataTransformer\\DateIntervalTransformer\:\:transform\(\) has Symfony\\Component\\Form\\Exception\\TransformationFailedException in PHPDoc @throws tag but it''s not thrown\.$#' identifier: throws.unusedType diff --git a/src/bundle/Resources/encore/ibexa.js.config.js b/src/bundle/Resources/encore/ibexa.js.config.js index b0e571de94..bf42ec470b 100644 --- a/src/bundle/Resources/encore/ibexa.js.config.js +++ b/src/bundle/Resources/encore/ibexa.js.config.js @@ -22,6 +22,7 @@ const layout = [ path.resolve(__dirname, '../public/js/scripts/core/toggle.button.js'), path.resolve(__dirname, '../public/js/scripts/core/slug.value.input.autogenerator.js'), path.resolve(__dirname, '../public/js/scripts/core/date.time.picker.js'), + path.resolve(__dirname, '../public/js/scripts/core/date.time.range.single.js'), path.resolve(__dirname, '../public/js/scripts/core/taggify.js'), path.resolve(__dirname, '../public/js/scripts/core/suggestion.taggify.js'), path.resolve(__dirname, '../public/js/scripts/core/storage.js'), @@ -49,6 +50,7 @@ const layout = [ path.resolve(__dirname, '../public/js/scripts/admin.table.js'), path.resolve(__dirname, '../public/js/scripts/core/collapse.js'), path.resolve(__dirname, '../public/js/scripts/admin.dropdown.js'), + path.resolve(__dirname, '../public/js/scripts/admin.date.time.range.single.js'), path.resolve(__dirname, '../public/js/scripts/double.click.mark.js'), path.resolve(__dirname, '../public/js/scripts/autogenerate.identifier.js'), path.resolve(__dirname, '../public/js/scripts/admin.back.to.top.js'), diff --git a/src/bundle/Resources/public/js/scripts/admin.date.time.range.single.js b/src/bundle/Resources/public/js/scripts/admin.date.time.range.single.js new file mode 100644 index 0000000000..64dcc2774d --- /dev/null +++ b/src/bundle/Resources/public/js/scripts/admin.date.time.range.single.js @@ -0,0 +1,12 @@ +(function (global, doc, ibexa) { + const { DateTimeRangeSingle } = ibexa.core; + const containers = doc.querySelectorAll('.ibexa-date-time-range-single'); + + containers.forEach((container) => { + const dateTimeRangeSingle = new DateTimeRangeSingle({ + container, + }); + + dateTimeRangeSingle.init(); + }); +})(window, window.document, window.ibexa); diff --git a/src/bundle/Resources/public/js/scripts/admin.search.filters.js b/src/bundle/Resources/public/js/scripts/admin.search.filters.js index 293225b017..bf15dcea96 100644 --- a/src/bundle/Resources/public/js/scripts/admin.search.filters.js +++ b/src/bundle/Resources/public/js/scripts/admin.search.filters.js @@ -1,22 +1,24 @@ (function (global, doc, ibexa, flatpickr, React, ReactDOM) { const { escapeHTML, escapeHTMLAttribute } = ibexa.helpers.text; const { dangerouslySetInnerHTML } = ibexa.helpers.dom; + const { getInstance } = ibexa.helpers.objectInstances; let getUsersTimeout; - const CLASS_DATE_RANGE = 'ibexa-filters__range-wrapper'; - const CLASS_VISIBLE_DATE_RANGE = 'ibexa-filters__range-wrapper--visible'; const SELECTOR_TAG = '.ibexa-tag'; const token = doc.querySelector('meta[name="CSRF-Token"]').content; const siteaccess = doc.querySelector('meta[name="SiteAccess"]').content; const filters = doc.querySelector('.ibexa-filters'); const clearBtn = filters.querySelector('.ibexa-btn--clear'); const applyBtn = filters.querySelector('.ibexa-btn--apply'); - const dateFields = doc.querySelectorAll('.ibexa-filters__range-wrapper'); const contentTypeSelect = doc.querySelector('.ibexa-filters__item--content-type .ibexa-filters__select'); const sectionSelect = doc.querySelector('.ibexa-filters__item--section .ibexa-filters__select'); - const lastModifiedSelect = doc.querySelector('.ibexa-filters__item--modified .ibexa-filters__select'); - const lastModifiedDateRange = doc.querySelector('.ibexa-filters__item--modified .ibexa-filters__range-select'); - const lastCreatedSelect = doc.querySelector('.ibexa-filters__item--created .ibexa-filters__select'); - const lastCreatedDateRange = doc.querySelector('.ibexa-filters__item--created .ibexa-filters__range-select'); + const lastModifiedSelectNode = doc.querySelector('.ibexa-filters__item--modified .ibexa-filters__select'); + const lastModifiedSelect = getInstance(lastModifiedSelectNode); + const lastModifiedDateRangeNode = doc.querySelector('.ibexa-filters__item--modified .ibexa-date-time-range-single'); + const lastModifiedDateRange = getInstance(lastModifiedDateRangeNode); + const lastCreatedSelectNode = doc.querySelector('.ibexa-filters__item--created .ibexa-filters__select'); + const lastCreatedSelect = getInstance(lastCreatedSelectNode); + const lastCreatedDateRangeNode = doc.querySelector('.ibexa-filters__item--created .ibexa-date-time-range-single'); + const lastCreatedDateRange = getInstance(lastCreatedDateRangeNode); const creatorInput = doc.querySelector('.ibexa-filters__item--creator .ibexa-input'); const searchCreatorInput = doc.querySelector('#search_creator'); const usersList = doc.querySelector('.ibexa-filters__item--creator .ibexa-filters__user-list'); @@ -24,20 +26,13 @@ const selectSubtreeBtn = doc.querySelector('.ibexa-filters__item--subtree .ibexa-tag-view-select__btn-select-path'); const subtreeInput = doc.querySelector('#search_subtree'); const showMoreBtns = doc.querySelectorAll('.ibexa-content-type-selector__show-more'); - const dateConfig = { - mode: 'range', - locale: { - rangeSeparator: ' - ', - }, - formatDate: (date) => ibexa.helpers.timezone.formatShortDateTime(date, null, ibexa.adminUiConfig.dateFormat.shortDate), - }; const clearFilters = (event) => { event.preventDefault(); const option = contentTypeSelect.querySelector('option'); const defaultText = option.dataset.default; - const lastModifiedDataRange = doc.querySelector(lastModifiedSelect.dataset.targetSelector); - const lastCreatedDataRange = doc.querySelector(lastCreatedSelect.dataset.targetSelector); + const lastModifiedDataRange = doc.querySelector(lastModifiedSelectNode.dataset.targetSelector); + const lastCreatedDataRange = doc.querySelector(lastCreatedSelectNode.dataset.targetSelector); const lastModifiedPeriod = doc.querySelector(lastModifiedDataRange.dataset.periodSelector); const lastModifiedEnd = doc.querySelector(lastModifiedDataRange.dataset.endSelector); const lastCreatedPeriod = doc.querySelector(lastCreatedDataRange.dataset.periodSelector); @@ -53,9 +48,9 @@ sectionSelect[0].selected = true; } - lastModifiedSelect[0].selected = true; - lastCreatedSelect[0].selected = true; - lastModifiedSelect.querySelector('option').selected = true; + lastModifiedSelectNode[0].selected = true; + lastCreatedSelectNode[0].selected = true; + lastModifiedSelectNode.querySelector('option').selected = true; lastModifiedPeriod.value = ''; lastModifiedEnd.value = ''; lastCreatedPeriod.value = ''; @@ -72,12 +67,11 @@ const isSectionSelected = sectionSelect ? !!sectionSelect.value : false; const isCreatorSelected = !!searchCreatorInput.value; const isSubtreeSelected = !!subtreeInput.value.trim().length; - let isModifiedSelected = !!lastModifiedSelect.value; - let isCreatedSelected = !!lastCreatedSelect.value; + let isModifiedSelected = !!lastModifiedSelectNode.value; + let isCreatedSelected = !!lastCreatedSelectNode.value; - if (lastModifiedSelect.value === 'custom_range') { - const lastModifiedWrapper = lastModifiedDateRange.closest(`.${CLASS_DATE_RANGE}`); - const { periodSelector, endSelector } = lastModifiedWrapper.dataset; + if (lastModifiedSelectNode.value === 'custom_range') { + const { periodSelector, endSelector } = lastModifiedDateRangeNode.dataset; const lastModifiedPeriodValue = doc.querySelector(periodSelector).value; const lastModifiedEndDate = doc.querySelector(endSelector).value; @@ -86,9 +80,8 @@ } } - if (lastCreatedSelect.value === 'custom_range') { - const lastCreatedWrapper = lastCreatedDateRange.closest(`.${CLASS_DATE_RANGE}`); - const { periodSelector, endSelector } = lastCreatedWrapper.dataset; + if (lastCreatedSelectNode.value === 'custom_range') { + const { periodSelector, endSelector } = lastCreatedDateRangeNode.dataset; const lastCreatedPeriodValue = doc.querySelector(periodSelector).value; const lastCreatedEndDate = doc.querySelector(endSelector).value; @@ -103,20 +96,21 @@ applyBtn[methodName]('disabled', !isEnabled); }; - const toggleDatesSelectVisibility = (event) => { + const toggleDatesSelectVisibility = (event, select, dateRange) => { const datesRangeNode = doc.querySelector(event.target.dataset.targetSelector); - if (event.target.value !== 'custom_range') { + if (select.value !== 'custom_range') { + dateRange.toggleHidden(true); + + dateRange.clearDates(); doc.querySelector(datesRangeNode.dataset.periodSelector).value = event.target.value; - doc.querySelector(datesRangeNode.dataset.endSelector).value = ''; - datesRangeNode.classList.remove(CLASS_VISIBLE_DATE_RANGE); toggleDisabledStateOnApplyBtn(); return; } - datesRangeNode.classList.add(CLASS_VISIBLE_DATE_RANGE); + dateRange.toggleHidden(false); }; const filterByContentType = () => { const selectedCheckboxes = [...contentTypeCheckboxes].filter((checkbox) => checkbox.checked); @@ -128,31 +122,6 @@ toggleDisabledStateOnApplyBtn(); }; - const setSelectedDateRange = (timestamps, { dates, inputField }) => { - const dateRange = inputField.closest('.ibexa-filters__range-wrapper'); - - if (dates.length === 2) { - const startDate = getUnixTimestampUTC(dates[0]); - const endDate = getUnixTimestampUTC(dates[1]); - const secondsInDay = 86400; - const days = (endDate - startDate) / secondsInDay; - - doc.querySelector(dateRange.dataset.periodSelector).value = `P0Y0M${days}D`; - doc.querySelector(dateRange.dataset.endSelector).value = endDate; - } else if (dates.length === 0) { - doc.querySelector(dateRange.dataset.periodSelector).value = ''; - doc.querySelector(dateRange.dataset.endSelector).value = ''; - } - - toggleDisabledStateOnApplyBtn(); - }; - const getUnixTimestampUTC = (dateObject) => { - let date = new Date(Date.UTC(dateObject.getFullYear(), dateObject.getMonth(), dateObject.getDate())); - - date = Math.floor(date.getTime() / 1000); - - return date; - }; const getUsersList = (value) => { const body = JSON.stringify({ ViewInput: { @@ -244,21 +213,6 @@ usersList.classList.add('ibexa-filters__user-list--hidden'); doc.querySelector('body').removeEventListener('click', handleClickOutsideUserList, false); }; - const initFlatPickr = (dateRangeField) => { - const { start, end } = dateRangeField.querySelector('.ibexa-filters__range-select').dataset; - const defaultDate = start && end ? [start, end] : []; - - const dateTimePickerWidget = new ibexa.core.DateTimePicker({ - container: dateRangeField, - onChange: setSelectedDateRange, - flatpickrConfig: { - ...dateConfig, - defaultDate, - }, - }); - - dateTimePickerWidget.init(); - }; const removeSearchTag = (event) => { const tag = event.currentTarget.closest(SELECTOR_TAG); const form = event.currentTarget.closest('form'); @@ -281,16 +235,10 @@ subtreeInput.value = ''; removeSearchTag(event); }; - const clearDataRange = (event, selector) => { - const dataRange = doc.querySelector(selector); - const rangeSelect = dataRange.parentNode.querySelector('.ibexa-filters__select'); - const periodInput = doc.querySelector(dataRange.dataset.periodSelector); - const endDateInput = doc.querySelector(dataRange.dataset.endSelector); - - rangeSelect[0].selected = true; - periodInput.value = ''; - endDateInput.vaue = ''; - dataRange.classList.remove(CLASS_VISIBLE_DATE_RANGE); + const clearDataRange = (event, select, dateRange) => { + select.clearCurrentSelection(); + dateRange.clearDates(); + dateRange.toggleHidden(true); removeSearchTag(event); }; const clearCreator = (event) => { @@ -302,8 +250,8 @@ subtree: (event) => clearSubtree(event), creator: (event) => clearCreator(event), 'content-types': (event) => clearContentType(event), - 'last-modified': (event) => clearDataRange(event, lastModifiedSelect.dataset.targetSelector), - 'last-created': (event) => clearDataRange(event, lastCreatedSelect.dataset.targetSelector), + 'last-modified': (event) => clearDataRange(event, lastModifiedSelect, lastModifiedDateRange), + 'last-created': (event) => clearDataRange(event, lastCreatedSelect, lastCreatedDateRange), }; const showMoreContentTypes = (event) => { const btn = event.currentTarget; @@ -347,7 +295,6 @@ ); }; - dateFields.forEach(initFlatPickr); filterByContentType(); clearBtn.addEventListener('click', clearFilters, false); @@ -363,8 +310,16 @@ } subtreeInput.addEventListener('change', toggleDisabledStateOnApplyBtn, false); - lastModifiedSelect.addEventListener('change', toggleDatesSelectVisibility, false); - lastCreatedSelect.addEventListener('change', toggleDatesSelectVisibility, false); + lastModifiedSelectNode.addEventListener( + 'change', + (event) => toggleDatesSelectVisibility(event, lastModifiedSelectNode, lastModifiedDateRange), + false, + ); + lastCreatedSelectNode.addEventListener( + 'change', + (event) => toggleDatesSelectVisibility(event, lastCreatedSelectNode, lastCreatedDateRange), + false, + ); creatorInput.addEventListener('keyup', handleTyping, false); usersList.addEventListener('click', handleSelectUser, false); contentTypeCheckboxes.forEach((checkbox) => checkbox.addEventListener('change', filterByContentType, false)); diff --git a/src/bundle/Resources/public/js/scripts/admin.trash.list.js b/src/bundle/Resources/public/js/scripts/admin.trash.list.js index dc59f3cc0e..0f37b02431 100644 --- a/src/bundle/Resources/public/js/scripts/admin.trash.list.js +++ b/src/bundle/Resources/public/js/scripts/admin.trash.list.js @@ -1,13 +1,14 @@ (function (global, doc, ibexa, React, ReactDOM, Translator) { const { escapeHTML, escapeHTMLAttribute } = ibexa.helpers.text; const { dangerouslySetInnerHTML } = ibexa.helpers.dom; + const { getInstance } = ibexa.helpers.objectInstances; let getUsersTimeout; const CLASS_SORTED_ASC = 'ibexa-table__sort-column--asc'; const CLASS_SORTED_DESC = 'ibexa-table__sort-column--desc'; - const CLASS_VISIBLE_DATE_RANGE = 'ibexa-trash-search-form__range-wrapper--visible'; const sortedActiveField = doc.querySelector('#trash_search_sort_field').value; const sortedActiveDirection = doc.querySelector('#trash_search_sort_direction').value; - const dateFields = doc.querySelectorAll('.ibexa-trash-search-form__range-wrapper'); + const trashedDateTimeRangeNode = doc.querySelector('.ibexa-trash-search-form__trashed-date-time-range'); + const trashedDateTimeRange = getInstance(trashedDateTimeRangeNode); const trashedTypeInput = doc.querySelector('#trash_search_trashed'); const token = doc.querySelector('meta[name="CSRF-Token"]').content; const siteaccess = doc.querySelector('meta[name="SiteAccess"]').content; @@ -23,13 +24,6 @@ const udwContainer = doc.getElementById('react-udw'); const autoSendNodes = doc.querySelectorAll('.ibexa-trash-search-form__item--auto-send'); const errorMessage = Translator.trans(/*@Desc("Cannot fetch user list")*/ 'trash.user_list.error', {}, 'ibexa_trash_ui'); - const dateConfig = { - mode: 'range', - locale: { - rangeSeparator: ' - ', - }, - formatDate: (date) => ibexa.helpers.timezone.formatShortDateTime(date, null, ibexa.adminUiConfig.dateFormat.shortDate), - }; let udwRoot = null; const closeUDW = () => udwRoot.unmount(); const onConfirm = (form, content) => { @@ -203,56 +197,17 @@ const datesRangeNode = doc.querySelector(event.target.dataset.targetSelector); if (event.target.value !== 'custom_range') { - doc.querySelector(datesRangeNode.dataset.periodSelector).value = event.target.value; - doc.querySelector(datesRangeNode.dataset.endSelector).value = ''; - datesRangeNode.classList.remove(CLASS_VISIBLE_DATE_RANGE); - formSearch.submit(); + trashedDateTimeRange.toggleHidden(true); - return; - } - - datesRangeNode.classList.add(CLASS_VISIBLE_DATE_RANGE); - }; - const setSelectedDateRange = (timestamps, { dates, inputField }) => { - const dateRange = inputField.closest('.ibexa-trash-search-form__range-wrapper'); - - if (dates.length === 2) { - const startDate = getUnixTimestampUTC(dates[0]); - const endDate = getUnixTimestampUTC(dates[1]); - const secondsInDay = 86400; - const days = (endDate - startDate) / secondsInDay; - - doc.querySelector(dateRange.dataset.periodSelector).value = `P0Y0M${days}D`; - doc.querySelector(dateRange.dataset.endSelector).value = endDate; + trashedDateTimeRange.clearDates(); + doc.querySelector(datesRangeNode.dataset.periodSelector).value = event.target.value; formSearch.submit(); - } else if (dates.length === 0) { - doc.querySelector(dateRange.dataset.periodSelector).value = ''; - doc.querySelector(dateRange.dataset.endSelector).value = ''; - formSearch.submit(); + return; } - }; - const getUnixTimestampUTC = (dateObject) => { - let date = new Date(Date.UTC(dateObject.getFullYear(), dateObject.getMonth(), dateObject.getDate())); - date = Math.floor(date.getTime() / 1000); - - return date; - }; - const initFlatPickr = (dateRangeField) => { - const { start, end } = dateRangeField.querySelector('.ibexa-trash-search-form__range-select').dataset; - const defaultDate = start && end ? [start, end] : []; - - const dateTimePickerWidget = new ibexa.core.DateTimePicker({ - container: dateRangeField, - onChange: setSelectedDateRange, - flatpickrConfig: { - ...dateConfig, - defaultDate, - }, - }); - dateTimePickerWidget.init(); + trashedDateTimeRange.toggleHidden(false); }; const handleAutoSubmitNodes = (event) => { event.preventDefault(); @@ -282,7 +237,17 @@ }; setSortedClass(); - dateFields.forEach(initFlatPickr); + trashedDateTimeRangeNode.addEventListener( + 'ibexa:date-time-range-single:change', + (event) => { + const { dates } = event.detail; + + if (dates.length === 2 || dates.length === 0) { + formSearch.submit(); + } + }, + false, + ); autoSendNodes.forEach((node) => node.addEventListener('change', handleAutoSubmitNodes, false)); sortableColumns.forEach((column) => column.addEventListener('click', sortTrashItems, false)); trashedTypeInput.addEventListener('change', toggleDatesSelectVisibility, false); 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 e6030c918f..c05b7c936b 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 @@ -32,7 +32,8 @@ class DateTimePicker { ...(config.flatpickrConfig ?? {}), }; - setInstance(this.container, this); + setInstance(this.container, this); // TODO: remove in 5.0 + setInstance(this.fieldWrapper, this); } clear() { diff --git a/src/bundle/Resources/public/js/scripts/core/date.time.range.single.js b/src/bundle/Resources/public/js/scripts/core/date.time.range.single.js new file mode 100644 index 0000000000..24efcb01fc --- /dev/null +++ b/src/bundle/Resources/public/js/scripts/core/date.time.range.single.js @@ -0,0 +1,102 @@ +import { formatShortDateTime } from '../helpers/timezone.helper'; +import { setInstance } from '../helpers/object.instances'; + +const { ibexa, document } = window; + +class DateTimeRangeSingle { + constructor(config) { + this.container = config.container; + this.dateTimePickerInputWrapper = this.container.querySelector('.ibexa-date-time-range-single__date-time-picker-input-wrapper'); + + const { periodSelector, startSelector, endSelector } = this.container.dataset; + this.periodInput = document.querySelector(periodSelector); + this.startInput = document.querySelector(startSelector); + this.endInput = document.querySelector(endSelector); + + const customDateConfig = config.dateConfig || {}; + this.dateConfig = { + mode: 'range', + locale: { + rangeSeparator: ' - ', + }, + formatDate: (date) => formatShortDateTime(date, null, ibexa.adminUiConfig.dateFormat.shortDateTime), + ...customDateConfig, + }; + + this.setSelectedDateRange = this.setSelectedDateRange.bind(this); + + setInstance(this.container, this); + } + + getUnixTimestampUTC(dateObject) { + return Math.floor(dateObject.getTime() / 1000); + } + + setDates(dates) { + if (dates.length === 2) { + const startDate = this.getUnixTimestampUTC(dates[0]); + const endDate = this.getUnixTimestampUTC(dates[1]); + + this.periodInput.value = ''; + this.periodInput.dispatchEvent(new Event('change')); + this.periodInput.dispatchEvent(new Event('input')); + + this.startInput.value = startDate; + this.startInput.dispatchEvent(new Event('change')); + this.startInput.dispatchEvent(new Event('input')); + + this.endInput.value = endDate; + this.endInput.dispatchEvent(new Event('change')); + this.endInput.dispatchEvent(new Event('input')); + } else if (dates.length === 0) { + this.startInput.value = ''; + this.startInput.dispatchEvent(new Event('change')); + this.startInput.dispatchEvent(new Event('input')); + + this.endInput.value = ''; + this.endInput.dispatchEvent(new Event('change')); + this.endInput.dispatchEvent(new Event('input')); + } + } + + clearDates() { + this.dateTimePickerWidget.clear(); + } + + setSelectedDateRange(timestamps, { dates }) { + this.setDates(dates); + + this.container.dispatchEvent( + new CustomEvent('ibexa:date-time-range-single:change', { + detail: { + timestamps, + dates, + }, + }), + ); + } + + toggleHidden(isHidden) { + this.container.classList.toggle('ibexa-date-time-range-single--hidden', isHidden); + } + + init() { + const { start, end } = this.container.dataset; + const defaultDate = start && end ? [start, end] : []; + + this.dateTimePickerWidget = new ibexa.core.DateTimePicker({ + container: this.dateTimePickerInputWrapper, + onChange: this.setSelectedDateRange, + flatpickrConfig: { + ...this.dateConfig, + defaultDate, + }, + }); + + this.dateTimePickerWidget.init(); + } +} + +ibexa?.addConfig('core.DateTimeRangeSingle', DateTimeRangeSingle); + +export { DateTimeRangeSingle as DateRangeSingle }; diff --git a/src/bundle/Resources/public/js/scripts/filters.action.btns.js b/src/bundle/Resources/public/js/scripts/filters.action.btns.js index e1a82c5747..6b0b7d9ac1 100644 --- a/src/bundle/Resources/public/js/scripts/filters.action.btns.js +++ b/src/bundle/Resources/public/js/scripts/filters.action.btns.js @@ -57,9 +57,19 @@ dateInputNodes.forEach((dateInputNode) => { if (!dateInputNode.disabled) { const datePickerNode = dateInputNode.closest('.ibexa-picker'); - const datePickerInstance = ibexa.helpers.objectInstances.getInstance(datePickerNode); + if (datePickerNode) { + const datePickerInstance = ibexa.helpers.objectInstances.getInstance(datePickerNode); - datePickerInstance.clear(); + datePickerInstance.clear(); + } + + const dateTimeRangeSingleNode = dateInputNode.closest('.ibexa-date-time-range-single'); + + if (dateTimeRangeSingleNode) { + const dateTimeRangeSingleInstance = ibexa.helpers.objectInstances.getInstance(dateTimeRangeSingleNode); + + dateTimeRangeSingleInstance.clearDates(); + } } }); dropdownNodes.forEach((dropdownNode) => { diff --git a/src/bundle/Resources/public/scss/_date-time-range-single.scss b/src/bundle/Resources/public/scss/_date-time-range-single.scss new file mode 100644 index 0000000000..bf521d2ea1 --- /dev/null +++ b/src/bundle/Resources/public/scss/_date-time-range-single.scss @@ -0,0 +1,7 @@ +.ibexa-date-time-range-single { + margin-top: calculateRem(16px); + + &--hidden { + display: none; + } +} diff --git a/src/bundle/Resources/public/scss/_filters.scss b/src/bundle/Resources/public/scss/_filters.scss index f750df9da6..c717edf5f3 100644 --- a/src/bundle/Resources/public/scss/_filters.scss +++ b/src/bundle/Resources/public/scss/_filters.scss @@ -69,26 +69,6 @@ } } - &__range-wrapper { - height: 0; - opacity: 0; - background: $ibexa-color-white; - transition: all $ibexa-admin-transition-duration $ibexa-admin-transition; - pointer-events: none; - - &--visible { - height: calculateRem(40px); - opacity: 1; - pointer-events: auto; - margin-top: calculateRem(16px); - } - } - - &__range-select { - outline: none; - border-radius: $ibexa-border-radius-small; - } - &__header { display: flex; align-items: center; diff --git a/src/bundle/Resources/public/scss/_trash-search-form.scss b/src/bundle/Resources/public/scss/_trash-search-form.scss index 98ec779992..b5cae6933e 100644 --- a/src/bundle/Resources/public/scss/_trash-search-form.scss +++ b/src/bundle/Resources/public/scss/_trash-search-form.scss @@ -94,25 +94,7 @@ } } - &__range-wrapper { - width: 0; - opacity: 0; - background: $ibexa-color-white; - border: calculateRem(1px) solid $ibexa-color-white; - border-radius: calculateRem(4px); - transition: all $ibexa-admin-transition-duration $ibexa-admin-transition; - pointer-events: none; - overflow: hidden; - - &--visible { - width: auto; - margin-left: calculateRem(16px); - opacity: 1; - pointer-events: auto; - } - } - - &__range-select { - outline: none; + .ibexa-date-time-range-single:not(.ibexa-date-time-range-single--hidden) { + margin-left: calculateRem(16px); } } diff --git a/src/bundle/Resources/public/scss/ibexa.scss b/src/bundle/Resources/public/scss/ibexa.scss index bb74981f5f..8e7404b4b1 100644 --- a/src/bundle/Resources/public/scss/ibexa.scss +++ b/src/bundle/Resources/public/scss/ibexa.scss @@ -117,6 +117,7 @@ @import 'object-state-group-view'; @import 'back-to-top'; @import 'date-time-picker'; +@import 'date-time-range-single'; @import 'middle-ellipsis'; @import 'user-thumbnail'; @import 'user-group-invitation'; diff --git a/src/bundle/Resources/views/themes/admin/trash/list.html.twig b/src/bundle/Resources/views/themes/admin/trash/list.html.twig index b74857a087..87a055aeb6 100644 --- a/src/bundle/Resources/views/themes/admin/trash/list.html.twig +++ b/src/bundle/Resources/views/themes/admin/trash/list.html.twig @@ -61,7 +61,7 @@ 'attr': {'class': 'ibexa-trash-search-form__item ibexa-trash-search-form__item--auto-send'} }), form_row(form_search.trashed, { - 'attr': {'data-target-selector': '.ibexa-trash-search-form__range-wrapper--select-trashed-range'} + attr: { 'data-target-selector': '.ibexa-trash-search-form__trashed-date-time-range' }, }), ] %} diff --git a/src/bundle/Resources/views/themes/admin/ui/component/date_time_range_single/date_time_range_single.twig b/src/bundle/Resources/views/themes/admin/ui/component/date_time_range_single/date_time_range_single.twig new file mode 100644 index 0000000000..ceb8e28520 --- /dev/null +++ b/src/bundle/Resources/views/themes/admin/ui/component/date_time_range_single/date_time_range_single.twig @@ -0,0 +1,30 @@ +{% import '@ibexadesign/ui/component/macros.html.twig' as html %} + +{% set attr = attr|default({})|merge({ + class: ('ibexa-date-time-range-single ' + ~ (is_hidden|default(false) ? 'ibexa-date-time-range-single--hidden ') + ~ attr.class|default(''))|trim, + 'data-period-selector': period_input_selector, + 'data-start-selector': start_input_selector, + 'data-end-selector': end_input_selector, + 'data-start': start_date is defined and start_date is not null + ? start_date|date('Y-m-d H:i:s') : '', + 'data-end': end_date is defined and end_date is not null + ? end_date|date('Y-m-d H:i:s') : '', +}) %} + +{% set picker_wrapper_attr = picker_wrapper_attr|default({})|merge({ + class: ('ibexa-date-time-range-single__date-time-picker-input-wrapper ' + ~ picker_wrapper_attr.class|default(''))|trim, +}) %} + +