From 6f938e33533bfc4f911f3d962864e294616ed02c Mon Sep 17 00:00:00 2001 From: Martin Boulais Date: Tue, 13 Dec 2022 15:25:34 +0100 Subject: [PATCH] Use ToggleableModel for filter panel visibility --- .../components/Filters/common/filtersPanel.js | 44 ++++++++---------- .../Filters/common/filtersToggleButton.js | 13 +++--- .../components/Filters/filtersConstants.js | 14 ------ .../dropdown/SelectionDropdownModel.js | 2 +- .../common/toggle/TogglableModel.js | 28 +++++++++++- .../common/toggle/toggleContainer.js | 9 +++- lib/public/views/Logs/Logs.js | 45 ++++++------------- lib/public/views/Logs/Overview/index.js | 11 +++-- lib/public/views/Runs/Overview/index.js | 11 +++-- lib/public/views/Runs/Runs.js | 45 ++++++------------- 10 files changed, 100 insertions(+), 122 deletions(-) delete mode 100644 lib/public/components/Filters/filtersConstants.js diff --git a/lib/public/components/Filters/common/filtersPanel.js b/lib/public/components/Filters/common/filtersPanel.js index b6d710551b..087f708527 100644 --- a/lib/public/components/Filters/common/filtersPanel.js +++ b/lib/public/components/Filters/common/filtersPanel.js @@ -11,8 +11,6 @@ * or submit itself to any jurisdiction. */ -import { taggedEventRegistry } from '../../../utilities/taggedEventRegistry.js'; -import { FILTER_PANEL_CLICK_TAG } from '../filtersConstants.js'; import { h } from '/js/src/index.js'; /** @@ -26,27 +24,21 @@ import { h } from '/js/src/index.js'; * * @return {vnode} the filters panel */ -export const filtersPanel = (globalModel, filteringModel, columns) => h( - `.w-25.filters${filteringModel.areFiltersVisible ? '.display-block' : '.display-none'}`, - { - onclick: (e) => taggedEventRegistry.tagEvent(e, FILTER_PANEL_CLICK_TAG), - }, - h('.w-100.shadow-level1.br2', [ - h('.f4.ph2', 'Filters'), - Object.values(columns).reduce((accumulator, column) => { - if (column.filter) { - accumulator.push([ - h('.flex-row.items-baseline.ph3.pv1', [ - h('.w-30.f5', column.name), - h('.w-70', typeof column.filter === 'function' ? column.filter(filteringModel, globalModel) : column.filter), - ]), - ]); - } - return accumulator; - }, []), - h('.p2', h('button.btn.btn-danger.mt2', { - disabled: !filteringModel.isAnyFilterActive(), - onclick: () => filteringModel.reset(), - }, 'Reset all filters')), - ]), -); +export const filtersPanel = (globalModel, filteringModel, columns) => h('.w-100.shadow-level1.br2', [ + h('.f4.ph2', 'Filters'), + Object.values(columns).reduce((accumulator, column) => { + if (column.filter) { + accumulator.push([ + h('.flex-row.items-baseline.ph3.pv1', [ + h('.w-30.f5', column.name), + h('.w-70', typeof column.filter === 'function' ? column.filter(filteringModel, globalModel) : column.filter), + ]), + ]); + } + return accumulator; + }, []), + h('.p2', h('button.btn.btn-danger.mt2', { + disabled: !filteringModel.isAnyFilterActive(), + onclick: () => filteringModel.reset(), + }, 'Reset all filters')), +]); diff --git a/lib/public/components/Filters/common/filtersToggleButton.js b/lib/public/components/Filters/common/filtersToggleButton.js index e3862c855c..f8f92a36f5 100644 --- a/lib/public/components/Filters/common/filtersToggleButton.js +++ b/lib/public/components/Filters/common/filtersToggleButton.js @@ -12,19 +12,18 @@ */ import { taggedEventRegistry } from '../../../utilities/taggedEventRegistry.js'; -import { FILTER_PANEL_CLICK_TAG } from '../filtersConstants.js'; import { h } from '/js/src/index.js'; /** * Return a button to show/hide a filters panel * - * @param {Object} filteringModel the model handling the filters state + * @param {ToggleableModel} toggleableModel the model handling the filters visibility * - * @return {vnode} the component to display + * @return {Component} the component to display */ -export const filtersToggleButton = (filteringModel) => h('button#openFilterToggle.btn.btn.btn-primary', { +export const filtersToggleButton = (toggleableModel) => h('button#openFilterToggle.btn.btn.btn-primary', { onclick: (e) => { - filteringModel.toggleFiltersVisibility(); - taggedEventRegistry.tagEvent(e, FILTER_PANEL_CLICK_TAG); + toggleableModel.toggle(); + taggedEventRegistry.tagEvent(e, toggleableModel.closeEventTag); }, -}, `${filteringModel.areFiltersVisible ? 'Close' : 'Open'} filters`); +}, `${toggleableModel.isVisible ? 'Close' : 'Open'} filters`); diff --git a/lib/public/components/Filters/filtersConstants.js b/lib/public/components/Filters/filtersConstants.js deleted file mode 100644 index 2a7ac4c0c6..0000000000 --- a/lib/public/components/Filters/filtersConstants.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright CERN and copyright holders of ALICE O2. This software is - * distributed under the terms of the GNU General Public License v3 (GPL - * Version 3), copied verbatim in the file "COPYING". - * - * See http://alice-o2.web.cern.ch/license for full licensing information. - * - * In applying this license CERN does not waive the privileges and immunities - * granted to it by virtue of its status as an Intergovernmental Organization - * or submit itself to any jurisdiction. - */ - -export const FILTER_PANEL_CLICK_TAG = 'filter-panel-click'; diff --git a/lib/public/components/common/selection/dropdown/SelectionDropdownModel.js b/lib/public/components/common/selection/dropdown/SelectionDropdownModel.js index ddcc1e5d31..cf96b5b6db 100644 --- a/lib/public/components/common/selection/dropdown/SelectionDropdownModel.js +++ b/lib/public/components/common/selection/dropdown/SelectionDropdownModel.js @@ -29,7 +29,7 @@ export class SelectionDropdownModel extends SelectionModel { */ constructor(availableOptions, defaultSelection = []) { super({ availableOptions, defaultSelection }); - this._toggleModel = new ToggleableModel(OPEN_BY_DEFAULT); + this._toggleModel = new ToggleableModel({ isVisible: OPEN_BY_DEFAULT }); this._defaultSelection = defaultSelection; this._searchInputContent = ''; diff --git a/lib/public/components/common/toggle/TogglableModel.js b/lib/public/components/common/toggle/TogglableModel.js index 1b823ae99b..f9a28039b1 100644 --- a/lib/public/components/common/toggle/TogglableModel.js +++ b/lib/public/components/common/toggle/TogglableModel.js @@ -22,12 +22,18 @@ export class ToggleableModel extends Observable { /** * Constructor * - * @param {boolean} [isVisible=false] default visibility of the element + * @param {configuration} [configuration] optionally model configuration + * @param {boolean} [configuration.isVisible=false] default visibility of the element + * @param {boolean} [configuration.hideOnEscape] if true, the visibility must be switched to off when escape key is pressed */ - constructor(isVisible = false) { + constructor(configuration) { super(); + const { isVisible = false, hideOnEscape = true } = configuration || {}; + this._isVisible = isVisible; this._closeEventTag = OPEN_DROPDOWN_EVENT_TAG + getUniqueId(); + + this._hideOnEscape = hideOnEscape; } /** @@ -48,6 +54,24 @@ export class ToggleableModel extends Observable { this.notify(); } + /** + * States if the toggleable element must be hidden when escape key is pressed + * + * @return {boolean} true to hide the element on escape press + */ + get hideOnEscape() { + return this._hideOnEscape; + } + + /** + * Toggle the visibility + * @returns {void } + */ + toggle() { + this._isVisible = !this._isVisible; + this.notify(); + } + /** * Returns the current visibility of the element * diff --git a/lib/public/components/common/toggle/toggleContainer.js b/lib/public/components/common/toggle/toggleContainer.js index d8d6f462be..0b0dfda59e 100644 --- a/lib/public/components/common/toggle/toggleContainer.js +++ b/lib/public/components/common/toggle/toggleContainer.js @@ -24,8 +24,13 @@ import { h } from '/js/src/index.js'; */ export const toggleContainer = (toggleModel, content, attributes) => { attributes = attributes || {}; + if (!attributes.class) { + attributes.class = ''; + } + attributes.class += ' toggle-container'; + return h( - '.toggle-container', + '', { ...attributes, // eslint-disable-next-line require-jsdoc @@ -39,7 +44,7 @@ export const toggleContainer = (toggleModel, content, attributes) => { this.hideToggleable(); }, toggleModel.closeEventTag); - this.hideDropdownOnEscape = (e) => e.key === 'Escape' && toggleModel.hide(); + this.hideDropdownOnEscape = (e) => toggleModel.hideOnEscape && e.key === 'Escape' && toggleModel.hide(); window.addEventListener('keyup', this.hideDropdownOnEscape); }, // eslint-disable-next-line require-jsdoc diff --git a/lib/public/views/Logs/Logs.js b/lib/public/views/Logs/Logs.js index da86a66f72..90b8295748 100644 --- a/lib/public/views/Logs/Logs.js +++ b/lib/public/views/Logs/Logs.js @@ -15,11 +15,10 @@ import { fetchClient, Observable, RemoteData } from '/js/src/index.js'; import { TagFilterModel } from '../../components/Filters/common/TagFilterModel.js'; import { SortModel } from '../../components/common/table/SortModel.js'; import { debounce, INPUT_DEBOUNCE_TIME } from '../../utilities/debounce.js'; -import { taggedEventRegistry } from '../../utilities/taggedEventRegistry.js'; -import { FILTER_PANEL_CLICK_TAG } from '../../components/Filters/filtersConstants.js'; import { FilterInputModel } from '../../components/Filters/common/filters/FilterInputModel.js'; import { LogCreationModel } from './Create/LogCreationModel.js'; import { PaginationModel } from '../../components/Pagination/PaginationModel.js'; +import { ToggleableModel } from '../../components/common/toggle/TogglableModel.js'; /** * Model representing handlers for log entries page @@ -51,15 +50,15 @@ export default class LogModel extends Observable { this._pagination.itemsPerPageSelector$.observe(() => this.notify()); // Filtering models + this._filtersToggle = new ToggleableModel(); + this._filtersToggle.bubbleTo(this); + this._authorFilter = new FilterInputModel(); this._registerFilter(this._authorFilter); this._titleFilter = new FilterInputModel(); this._registerFilter(this._titleFilter); - // Register tagged event listener to close filter if click outside - taggedEventRegistry.addListenerForAnyExceptTagged(() => this.setShowFilters(false), FILTER_PANEL_CLICK_TAG); - this.clearLogs(); this.reset(false); @@ -356,33 +355,6 @@ export default class LogModel extends Observable { this.logs = RemoteData.NotAsked(); } - /** - * Returns whether the filter should be shown or not - * @returns {Boolean} returns whether the filter should be shown - */ - get areFiltersVisible() { - return this.showFilters || false; - } - - /** - * Sets whether the filters are shown or not - * @param {Boolean} showFilters Whether the filter should be shown - * @returns {Boolean} returns boolean - */ - setShowFilters(showFilters) { - this.showFilters = showFilters; - this.notify(); - } - - /** - * Toggles whether the filters are shown - * @returns {void} - */ - toggleFiltersVisibility() { - this.setShowFilters(!this.areFiltersVisible); - this.notify(); - } - /** * Return the model handling the filtering on tags * @@ -437,6 +409,15 @@ export default class LogModel extends Observable { return this._pagination; } + /** + * Returns the toggle model for filters pane + * + * @return {ToggleableModel} the toggle model + */ + get filtersToggle() { + return this._filtersToggle; + } + /** * Apply the current filtering and update the remote data list * diff --git a/lib/public/views/Logs/Overview/index.js b/lib/public/views/Logs/Overview/index.js index 74f945706f..1612c3c3d6 100644 --- a/lib/public/views/Logs/Overview/index.js +++ b/lib/public/views/Logs/Overview/index.js @@ -18,6 +18,7 @@ import { filtersPanel } from '../../../components/Filters/common/filtersPanel.js import { filtersToggleButton } from '../../../components/Filters/common/filtersToggleButton.js'; import { estimateDisplayableRowsCount } from '../../../utilities/estimateDisplayableRowsCount.js'; import { paginationComponent } from '../../../components/Pagination/paginationComponent.js'; +import { toggleContainer } from '../../../components/common/toggle/toggleContainer.js'; const TABLEROW_HEIGHT = 69; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -47,10 +48,14 @@ const showLogsTable = (model, logs) => { return [ h('.flex-row.header-container.pv2', [ - filtersToggleButton(model.logs), - filtersPanel(model, model.logs, logsActiveColumns), + filtersToggleButton(model.logs.filtersToggle), + toggleContainer( + model.logs.filtersToggle, + model.logs.filtersToggle.isVisible && filtersPanel(model, model.logs, logsActiveColumns), + model.logs.filtersToggle.isVisible ? { class: 'w-25 filters' } : null, + ), h( - `.w-50.filters${model.logs.isAnyFilterActive() && !model.logs.areFiltersVisible + `.w-50.filters${model.logs.isAnyFilterActive() && !model.logs.filtersToggle.isVisible ? '.display-block' : '.display-none'}`, h('.f5'), diff --git a/lib/public/views/Runs/Overview/index.js b/lib/public/views/Runs/Overview/index.js index ad230db84f..a67173aac4 100644 --- a/lib/public/views/Runs/Overview/index.js +++ b/lib/public/views/Runs/Overview/index.js @@ -19,6 +19,7 @@ import { filtersPanel } from '../../../components/Filters/common/filtersPanel.js import { filtersToggleButton } from '../../../components/Filters/common/filtersToggleButton.js'; import { estimateDisplayableRowsCount } from '../../../utilities/estimateDisplayableRowsCount.js'; import { paginationComponent } from '../../../components/Pagination/paginationComponent.js'; +import { toggleContainer } from '../../../components/common/toggle/toggleContainer.js'; const TABLEROW_HEIGHT = 59; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -45,10 +46,14 @@ const showRunsTable = (model, runs) => { return [ h('.flex-row.header-container.pv2', [ - filtersToggleButton(model.runs), - filtersPanel(model, model.runs, runsActiveColumns), + filtersToggleButton(model.runs.filtersToggle), + toggleContainer( + model.runs.filtersToggle, + model.runs.filtersToggle.isVisible && filtersPanel(model, model.runs, runsActiveColumns), + model.runs.filtersToggle.isVisible ? { class: 'w-25 filters' } : null, + ), h( - `.w-60.filters${model.runs.isAnyFilterActive() && !model.runs.areFiltersVisible ? '.display-block' : '.display-none'}`, + `.w-60.filters${model.runs.isAnyFilterActive() && !model.runs.filtersToggle.isVisible ? '.display-block' : '.display-none'}`, h('.f5'), `Active filters: ${model.runs.getActiveFilters().join(', ')}`, ), diff --git a/lib/public/views/Runs/Runs.js b/lib/public/views/Runs/Runs.js index b1e2f816a7..5b86df715a 100644 --- a/lib/public/views/Runs/Runs.js +++ b/lib/public/views/Runs/Runs.js @@ -16,12 +16,11 @@ import { createCSVExport, createJSONExport } from '../../utilities/export.js'; import { TagFilterModel } from '../../components/Filters/common/TagFilterModel.js'; import { PickerModel } from '../../components/common/selection/picker/PickerModel.js'; import { debounce, INPUT_DEBOUNCE_TIME } from '../../utilities/debounce.js'; -import { taggedEventRegistry } from '../../utilities/taggedEventRegistry.js'; -import { FILTER_PANEL_CLICK_TAG } from '../../components/Filters/filtersConstants.js'; import { PaginationModel } from '../../components/Pagination/PaginationModel.js'; import { getRemoteDataSlice } from '../../utilities/fetch/getRemoteDataSlice.js'; import { DetectorsFilterModel } from '../../components/Filters/RunsFilter/DetectorsFilterModel.js'; import { RunDetailsModel } from './Details/RunDetailsModel.js'; +import { ToggleableModel } from '../../components/common/toggle/TogglableModel.js'; /** * Model representing handlers for runs page @@ -42,6 +41,9 @@ export default class RunModel extends Observable { this._detailsModel = new RunDetailsModel(model); this._detailsModel.bubbleTo(this); + this._filtersToggle = new ToggleableModel({ hideOnEscape: false }); + this._filtersToggle.bubbleTo(this); + this._listingTagsFilterModel = new TagFilterModel(); this._listingTagsFilterModel.observe(() => this._applyFilters(true)); @@ -62,9 +64,6 @@ export default class RunModel extends Observable { this._currentPageRuns = RemoteData.notAsked(); this._allRuns = RemoteData.notAsked(); - // Register tagged event listener to close filter if click outside - taggedEventRegistry.addListenerForAnyExceptTagged(() => this.setShowFilters(false), FILTER_PANEL_CLICK_TAG); - this.reset(false); this._debouncedFetchAllRuns = debounce(this.fetchAllRuns.bind(this), INPUT_DEBOUNCE_TIME); @@ -796,33 +795,6 @@ export default class RunModel extends Observable { this._applyFilters(); } - /** - * Returns whether the filter should be shown or not - * @returns {Boolean} returns whether the filter should be shown - */ - get areFiltersVisible() { - return this.showFilters || false; - } - - /** - * Sets whether the filters are shown or not - * @param {Boolean} showFilters Whether the filter should be shown - * @returns {void} - */ - setShowFilters(showFilters) { - this.showFilters = showFilters; - this.notify(); - } - - /** - * Toggles whether the filters are shown - * @returns {void} - */ - toggleFiltersVisibility() { - this.setShowFilters(!this.areFiltersVisible); - this.notify(); - } - /** * Return the model handling the filtering on tags * @@ -903,6 +875,15 @@ export default class RunModel extends Observable { return this._detailsModel; } + /** + * Returns the toggle model for filters pane + * + * @return {ToggleableModel} the toggle model + */ + get filtersToggle() { + return this._filtersToggle; + } + /** * Returns the list of URL params corresponding to the currently applied filter *