diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_modules.ts b/packages/devextreme/js/__internal/grids/grid_core/m_modules.ts index 542718f66db3..2373973ca5ee 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_modules.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_modules.ts @@ -5,7 +5,7 @@ import Callbacks from '@js/core/utils/callbacks'; // @ts-expect-error import { grep } from '@js/core/utils/common'; import { each } from '@js/core/utils/iterator'; -import { isDefined, isFunction } from '@js/core/utils/type'; +import { isFunction } from '@js/core/utils/type'; import { hasWindow } from '@js/core/utils/window'; import messageLocalization from '@js/localization/message'; import errors from '@js/ui/widget/ui.errors'; @@ -16,11 +16,11 @@ import type { View as ViewType, ViewController as ViewControllerType, } from './m_types'; +import type { ViewsWithBorder } from './views/utils/update_views_borders'; +import { updateViewsBorders } from './views/utils/update_views_borders'; const WIDGET_WITH_LEGACY_CONTAINER_NAME = 'dxDataGrid'; -const BORDERED_VIEWS = ['columnHeadersView', 'rowsView', 'footerView', 'filterPanelView']; - const ModuleItem = Class.inherit({ _endUpdateCore() { }, @@ -262,83 +262,13 @@ const View: ModuleType = ModuleItem.inherit({ return this.component._views[name]; }, - getFirstVisibleViewElement() { - const columnHeaderView = this.getView('columnHeadersView'); - if (columnHeaderView && columnHeaderView.isVisible()) { - return columnHeaderView.element(); - } - - return this.getView('rowsView').element(); - }, - - getLastVisibleViewElement() { - const filterPanelView = this.getView('filterPanelView'); - if (filterPanelView && filterPanelView.isVisible()) { - return filterPanelView.element(); - } - - const footerView = this.getView('footerView'); - if (footerView && footerView.isVisible()) { - return footerView.element(); - } - - return this.getView('rowsView').element(); - }, - - getViewElementWithClass(className) { - const borderedView = BORDERED_VIEWS.map((viewName) => this.getView(viewName)) - .filter((view) => view && view.element()) - .find((view) => view.element().hasClass(className)); - - return borderedView && borderedView.element(); - }, - - updateBorderedViews() { - const BORDERED_TOP_VIEW_CLASS = 'dx-bordered-top-view'; - const BORDERED_BOTTOM_VIEW_CLASS = 'dx-bordered-bottom-view'; - - const oldFirstBorderedElement = this.getViewElementWithClass(BORDERED_TOP_VIEW_CLASS); - const oldLastBorderedElement = this.getViewElementWithClass(BORDERED_BOTTOM_VIEW_CLASS); - const newFirstBorderedElement = this.getFirstVisibleViewElement(); - const newLastBorderedElement = this.getLastVisibleViewElement(); - - if (oldFirstBorderedElement && !oldFirstBorderedElement.is(newFirstBorderedElement)) { - oldFirstBorderedElement.removeClass(BORDERED_TOP_VIEW_CLASS); - } - - if (oldLastBorderedElement && !oldLastBorderedElement.is(newLastBorderedElement)) { - oldLastBorderedElement.removeClass(BORDERED_BOTTOM_VIEW_CLASS); - } - - if (!newFirstBorderedElement.hasClass(BORDERED_TOP_VIEW_CLASS)) { - newFirstBorderedElement.addClass(BORDERED_TOP_VIEW_CLASS); - } - - if (!newLastBorderedElement.hasClass(BORDERED_BOTTOM_VIEW_CLASS)) { - newLastBorderedElement.addClass(BORDERED_BOTTOM_VIEW_CLASS); - } - }, - - isViewsStateValid() { - if (this.component._views) { - if (!BORDERED_VIEWS.includes(this.name)) { - return false; - } - - const rowsView = this.getView('rowsView'); - if (!(rowsView && isDefined(rowsView.element?.()))) { - return false; - } - - const optionalViews = ['columnHeadersView', 'footerView', 'filterPanelView'] - .map((viewName) => this.getView(viewName)) - .filter((view) => view && view.isVisible?.()); - const isOptionalViewsRendered = optionalViews.every((view) => view && isDefined(view.element())); - - return isOptionalViewsRendered; - } - - return false; + _getBorderedViews(): ViewsWithBorder { + return { + columnHeadersView: this.component._views.columnHeadersView, + rowsView: this.component._views.rowsView, + filterPanelView: this.component._views.filterPanelView, + footerView: this.component._views.footerView, + }; }, render($parent, options) { @@ -356,8 +286,8 @@ const View: ModuleType = ModuleItem.inherit({ $element.toggleClass('dx-hidden', !isVisible); - if (this.isViewsStateValid()) { - this.updateBorderedViews(); + if (this.component._views) { + updateViewsBorders(this.name, this._getBorderedViews()); } if (isVisible) { diff --git a/packages/devextreme/js/__internal/grids/grid_core/views/utils/update_views_borders.ts b/packages/devextreme/js/__internal/grids/grid_core/views/utils/update_views_borders.ts new file mode 100644 index 000000000000..35bf2e058565 --- /dev/null +++ b/packages/devextreme/js/__internal/grids/grid_core/views/utils/update_views_borders.ts @@ -0,0 +1,106 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +import { dxElementWrapper } from '@js/core/renderer'; +import { isDefined } from '@js/core/utils/type'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type View = any; + +// TODO: Move to the grid_core/m_types views interface. +export interface ViewsWithBorder { + columnHeadersView: View; + rowsView: View; + filterPanelView: View; + footerView: View; +} + +const CLASSES = { + borderedTop: 'dx-bordered-top-view', + borderedBottom: 'dx-bordered-bottom-view', +}; + +const getFirstVisibleViewElement = ({ + columnHeadersView, + rowsView, +}: ViewsWithBorder): dxElementWrapper => { + if (columnHeadersView?.isVisible()) { + return columnHeadersView.element(); + } + + return rowsView.element(); +}; + +const getLastVisibleViewElement = ({ + filterPanelView, + footerView, + rowsView, +}: ViewsWithBorder): dxElementWrapper => { + if (filterPanelView?.isVisible()) { + return filterPanelView.element(); + } + + if (footerView?.isVisible()) { + return footerView.element(); + } + + return rowsView.element(); +}; + +const getViewElementWithClass = ( + viewsWithBorder: ViewsWithBorder, + className: string, +): dxElementWrapper | null => { + const borderedView = Object.values(viewsWithBorder) + .find((view) => view?.element()?.hasClass(className)); + + return borderedView?.element() ?? null; +}; + +const shouldUpdateBorders = ( + viewName: string, + viewsWithBorder: ViewsWithBorder, +): boolean => { + if (!Object.keys(viewsWithBorder).includes(viewName)) { + return false; + } + + const { rowsView, ...otherViews } = viewsWithBorder; + if (!isDefined(rowsView?.element?.())) { + return false; + } + + return Object.values(otherViews) + .filter((view) => view?.isVisible?.()) + .every((view) => isDefined(view?.element())); +}; + +export const updateViewsBorders = ( + viewName: string, + viewsWithBorder: ViewsWithBorder, +): void => { + if (!shouldUpdateBorders(viewName, viewsWithBorder)) { + return; + } + + const $oldFirst = getViewElementWithClass(viewsWithBorder, CLASSES.borderedTop); + const $oldLast = getViewElementWithClass(viewsWithBorder, CLASSES.borderedBottom); + const $newFirst = getFirstVisibleViewElement(viewsWithBorder); + const $newLast = getLastVisibleViewElement(viewsWithBorder); + + // @ts-expect-error The dxElementWrapper's "is" method is badly typed. + if ($oldFirst && !$oldFirst.is($newFirst)) { + $oldFirst.removeClass(CLASSES.borderedTop); + } + + // @ts-expect-error The dxElementWrapper's "is" method is badly typed. + if ($oldLast && !$oldLast.is($newLast)) { + $oldLast.removeClass(CLASSES.borderedBottom); + } + + if (!$newFirst.hasClass(CLASSES.borderedTop)) { + $newFirst.addClass(CLASSES.borderedTop); + } + + if (!$newLast.hasClass(CLASSES.borderedBottom)) { + $newLast.addClass(CLASSES.borderedBottom); + } +};