diff --git a/packages/grid/src/vaadin-grid-filter-column-mixin.d.ts b/packages/grid/src/vaadin-grid-filter-column-mixin.d.ts new file mode 100644 index 0000000000..faca82c4e5 --- /dev/null +++ b/packages/grid/src/vaadin-grid-filter-column-mixin.d.ts @@ -0,0 +1,22 @@ +/** + * @license + * Copyright (c) 2016 - 2023 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import type { Constructor } from '@open-wc/dedupe-mixin'; + +export declare function GridFilterColumnMixin>( + superclass: T, +): Constructor & T; + +export declare class GridFilterColumnMixinClass { + /** + * Text to display as the label of the column filter text-field. + */ + header: string | null | undefined; + + /** + * JS Path of the property in the item used for filtering the data. + */ + path: string | null | undefined; +} diff --git a/packages/grid/src/vaadin-grid-filter-column-mixin.js b/packages/grid/src/vaadin-grid-filter-column-mixin.js new file mode 100644 index 0000000000..8ea2fb1606 --- /dev/null +++ b/packages/grid/src/vaadin-grid-filter-column-mixin.js @@ -0,0 +1,100 @@ +/** + * @license + * Copyright (c) 2016 - 2023 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ + +/** + * @polymerMixin + */ +export const GridFilterColumnMixin = (superClass) => + class extends superClass { + static get properties() { + return { + /** + * JS Path of the property in the item used for filtering the data. + */ + path: String, + + /** + * Text to display as the label of the column filter text-field. + */ + header: String, + }; + } + + static get observers() { + return ['_onHeaderRendererOrBindingChanged(_headerRenderer, _headerCell, path, header, _filterValue)']; + } + + constructor() { + super(); + + this.__boundOnFilterValueChanged = this.__onFilterValueChanged.bind(this); + } + + /** + * Renders the grid filter with the custom text field to the header cell. + * + * @override + */ + _defaultHeaderRenderer(root, _column) { + let filter = root.firstElementChild; + let textField = filter ? filter.firstElementChild : undefined; + + if (!filter) { + filter = document.createElement('vaadin-grid-filter'); + textField = document.createElement('vaadin-text-field'); + textField.setAttribute('theme', 'small'); + textField.setAttribute('style', 'max-width: 100%;'); + textField.setAttribute('focus-target', ''); + textField.addEventListener('value-changed', this.__boundOnFilterValueChanged); + filter.appendChild(textField); + root.appendChild(filter); + } + + filter.path = this.path; + filter.value = this._filterValue; + + textField.__rendererValue = this._filterValue; + textField.value = this._filterValue; + textField.label = this.__getHeader(this.header, this.path); + } + + /** + * The filter column doesn't allow to use a custom header renderer + * to override the header cell content. + * It always renders the grid filter to the header cell. + * + * @override + */ + _computeHeaderRenderer() { + return this._defaultHeaderRenderer; + } + + /** + * Updates the internal filter value once the filter text field is changed. + * The listener handles only user-fired events. + * + * @private + */ + __onFilterValueChanged(e) { + // Skip if the value is changed by the renderer. + if (e.detail.value === e.target.__rendererValue) { + return; + } + + this._filterValue = e.detail.value; + } + + /** @private */ + __getHeader(header, path) { + if (header) { + return header; + } + + if (path) { + return this._generateHeader(path); + } + } + }; diff --git a/packages/grid/src/vaadin-grid-filter-column.d.ts b/packages/grid/src/vaadin-grid-filter-column.d.ts index b8a3d77f63..022d07e268 100644 --- a/packages/grid/src/vaadin-grid-filter-column.d.ts +++ b/packages/grid/src/vaadin-grid-filter-column.d.ts @@ -4,7 +4,10 @@ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ */ import type { GridDefaultItem } from './vaadin-grid.js'; -import { GridColumn } from './vaadin-grid-column.js'; +import type { GridColumn, GridColumnMixin } from './vaadin-grid-column.js'; +import type { GridFilterColumnMixinClass } from './vaadin-grid-filter-column-mixin.js'; + +export * from './vaadin-grid-filter-column-mixin.js'; /** * `` is a helper element for the `` @@ -19,17 +22,12 @@ import { GridColumn } from './vaadin-grid-column.js'; * ... * ``` */ -declare class GridFilterColumn extends GridColumn { - /** - * Text to display as the label of the column filter text-field. - */ - header: string | null | undefined; +declare class GridFilterColumn extends HTMLElement {} - /** - * JS Path of the property in the item used for filtering the data. - */ - path: string | null | undefined; -} +interface GridFilterColumn + extends GridFilterColumnMixinClass, + GridColumnMixin>, + GridColumn {} declare global { interface HTMLElementTagNameMap { diff --git a/packages/grid/src/vaadin-grid-filter-column.js b/packages/grid/src/vaadin-grid-filter-column.js index 464d29c74f..0c18ef4d8c 100644 --- a/packages/grid/src/vaadin-grid-filter-column.js +++ b/packages/grid/src/vaadin-grid-filter-column.js @@ -6,6 +6,7 @@ import './vaadin-grid-filter.js'; import { defineCustomElement } from '@vaadin/component-base/src/define.js'; import { GridColumn } from './vaadin-grid-column.js'; +import { GridFilterColumnMixin } from './vaadin-grid-filter-column-mixin.js'; /** * `` is a helper element for the `` @@ -22,100 +23,12 @@ import { GridColumn } from './vaadin-grid-column.js'; * * @customElement * @extends GridColumn + * @mixes GridFilterColumnMixin */ -class GridFilterColumn extends GridColumn { +class GridFilterColumn extends GridFilterColumnMixin(GridColumn) { static get is() { return 'vaadin-grid-filter-column'; } - - static get properties() { - return { - /** - * JS Path of the property in the item used for filtering the data. - */ - path: String, - - /** - * Text to display as the label of the column filter text-field. - */ - header: String, - }; - } - - static get observers() { - return ['_onHeaderRendererOrBindingChanged(_headerRenderer, _headerCell, path, header, _filterValue)']; - } - - constructor() { - super(); - - this.__boundOnFilterValueChanged = this.__onFilterValueChanged.bind(this); - } - - /** - * Renders the grid filter with the custom text field to the header cell. - * - * @override - */ - _defaultHeaderRenderer(root, _column) { - let filter = root.firstElementChild; - let textField = filter ? filter.firstElementChild : undefined; - - if (!filter) { - filter = document.createElement('vaadin-grid-filter'); - textField = document.createElement('vaadin-text-field'); - textField.setAttribute('theme', 'small'); - textField.setAttribute('style', 'max-width: 100%;'); - textField.setAttribute('focus-target', ''); - textField.addEventListener('value-changed', this.__boundOnFilterValueChanged); - filter.appendChild(textField); - root.appendChild(filter); - } - - filter.path = this.path; - filter.value = this._filterValue; - - textField.__rendererValue = this._filterValue; - textField.value = this._filterValue; - textField.label = this.__getHeader(this.header, this.path); - } - - /** - * The filter column doesn't allow to use a custom header renderer - * to override the header cell content. - * It always renders the grid filter to the header cell. - * - * @override - */ - _computeHeaderRenderer() { - return this._defaultHeaderRenderer; - } - - /** - * Updates the internal filter value once the filter text field is changed. - * The listener handles only user-fired events. - * - * @private - */ - __onFilterValueChanged(e) { - // Skip if the value is changed by the renderer. - if (e.detail.value === e.target.__rendererValue) { - return; - } - - this._filterValue = e.detail.value; - } - - /** @private */ - __getHeader(header, path) { - if (header) { - return header; - } - - if (path) { - return this._generateHeader(path); - } - } } defineCustomElement(GridFilterColumn); diff --git a/packages/grid/src/vaadin-grid-selection-column-mixin.d.ts b/packages/grid/src/vaadin-grid-selection-column-mixin.d.ts new file mode 100644 index 0000000000..b63e57f41e --- /dev/null +++ b/packages/grid/src/vaadin-grid-selection-column-mixin.d.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright (c) 2016 - 2023 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import type { Constructor } from '@open-wc/dedupe-mixin'; +import { GridSelectionColumnBaseMixinClass } from './vaadin-grid-selection-column-base-mixin.js'; + +/** + * Fired when the `selectAll` property changes. + */ +export type GridSelectionColumnSelectAllChangedEvent = CustomEvent<{ value: boolean }>; + +export interface GridSelectionColumnCustomEventMap { + 'select-all-changed': GridSelectionColumnSelectAllChangedEvent; +} + +export interface GridSelectionColumnEventMap extends HTMLElementEventMap, GridSelectionColumnCustomEventMap {} + +export declare function GridSelectionColumnMixin>( + superclass: T, +): Constructor> & T; + +export declare class GridSelectionColumnMixinClass extends GridSelectionColumnBaseMixinClass {} diff --git a/packages/grid/src/vaadin-grid-selection-column-mixin.js b/packages/grid/src/vaadin-grid-selection-column-mixin.js new file mode 100644 index 0000000000..ca289464ce --- /dev/null +++ b/packages/grid/src/vaadin-grid-selection-column-mixin.js @@ -0,0 +1,194 @@ +/** + * @license + * Copyright (c) 2016 - 2023 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import { GridSelectionColumnBaseMixin } from './vaadin-grid-selection-column-base-mixin.js'; + +/** + * @polymerMixin + */ +export const GridSelectionColumnMixin = (superClass) => + class extends GridSelectionColumnBaseMixin(superClass) { + static get properties() { + return { + /** + * The previous state of activeItem. When activeItem turns to `null`, + * previousActiveItem will have an Object with just unselected activeItem + * @private + */ + __previousActiveItem: Object, + }; + } + + static get observers() { + return ['__onSelectAllChanged(selectAll)']; + } + + constructor() { + super(); + + this.__boundOnActiveItemChanged = this.__onActiveItemChanged.bind(this); + this.__boundOnDataProviderChanged = this.__onDataProviderChanged.bind(this); + this.__boundOnSelectedItemsChanged = this.__onSelectedItemsChanged.bind(this); + } + + /** @protected */ + disconnectedCallback() { + this._grid.removeEventListener('active-item-changed', this.__boundOnActiveItemChanged); + this._grid.removeEventListener('data-provider-changed', this.__boundOnDataProviderChanged); + this._grid.removeEventListener('filter-changed', this.__boundOnSelectedItemsChanged); + this._grid.removeEventListener('selected-items-changed', this.__boundOnSelectedItemsChanged); + + super.disconnectedCallback(); + } + + /** @protected */ + connectedCallback() { + super.connectedCallback(); + if (this._grid) { + this._grid.addEventListener('active-item-changed', this.__boundOnActiveItemChanged); + this._grid.addEventListener('data-provider-changed', this.__boundOnDataProviderChanged); + this._grid.addEventListener('filter-changed', this.__boundOnSelectedItemsChanged); + this._grid.addEventListener('selected-items-changed', this.__boundOnSelectedItemsChanged); + } + } + + /** @private */ + __onSelectAllChanged(selectAll) { + if (selectAll === undefined || !this._grid) { + return; + } + + if (!this.__selectAllInitialized) { + // The initial value for selectAll property was applied, avoid clearing pre-selected items + this.__selectAllInitialized = true; + return; + } + + if (this._selectAllChangeLock) { + return; + } + + if (selectAll && this.__hasArrayDataProvider()) { + this.__withFilteredItemsArray((items) => { + this._grid.selectedItems = items; + }); + } else { + this._grid.selectedItems = []; + } + } + + /** + * Return true if array `a` contains all the items in `b` + * We need this when sorting or to preserve selection after filtering. + * @private + */ + __arrayContains(a, b) { + return Array.isArray(a) && Array.isArray(b) && b.every((i) => a.includes(i)); + } + + /** + * Override a method from `GridSelectionColumnBaseMixin` to handle the user + * selecting all items. + * + * @protected + * @override + */ + _selectAll() { + this.selectAll = true; + } + + /** + * Override a method from `GridSelectionColumnBaseMixin` to handle the user + * deselecting all items. + * + * @protected + * @override + */ + _deselectAll() { + this.selectAll = false; + } + + /** + * Override a method from `GridSelectionColumnBaseMixin` to handle the user + * selecting an item. + * + * @param {Object} item the item to select + * @protected + * @override + */ + _selectItem(item) { + this._grid.selectItem(item); + } + + /** + * Override a method from `GridSelectionColumnBaseMixin` to handle the user + * deselecting an item. + * + * @param {Object} item the item to deselect + * @protected + * @override + */ + _deselectItem(item) { + this._grid.deselectItem(item); + } + + /** @private */ + __onActiveItemChanged(e) { + const activeItem = e.detail.value; + if (this.autoSelect) { + const item = activeItem || this.__previousActiveItem; + if (item) { + this._grid._toggleItem(item); + } + } + this.__previousActiveItem = activeItem; + } + + /** @private */ + __hasArrayDataProvider() { + return Array.isArray(this._grid.items) && !!this._grid.dataProvider; + } + + /** @private */ + __onSelectedItemsChanged() { + this._selectAllChangeLock = true; + if (this.__hasArrayDataProvider()) { + this.__withFilteredItemsArray((items) => { + if (!this._grid.selectedItems.length) { + this.selectAll = false; + this._indeterminate = false; + } else if (this.__arrayContains(this._grid.selectedItems, items)) { + this.selectAll = true; + this._indeterminate = false; + } else { + this.selectAll = false; + this._indeterminate = true; + } + }); + } + this._selectAllChangeLock = false; + } + + /** @private */ + __onDataProviderChanged() { + this._selectAllHidden = !Array.isArray(this._grid.items); + } + + /** + * Assuming the grid uses an items array data provider, fetches all the filtered items + * from the data provider and invokes the callback with the resulting array. + * + * @private + */ + __withFilteredItemsArray(callback) { + const params = { + page: 0, + pageSize: Infinity, + sortOrders: [], + filters: this._grid._mapFilters(), + }; + this._grid.dataProvider(params, (items) => callback(items)); + } + }; diff --git a/packages/grid/src/vaadin-grid-selection-column.d.ts b/packages/grid/src/vaadin-grid-selection-column.d.ts index 0adba69a4d..60d7248678 100644 --- a/packages/grid/src/vaadin-grid-selection-column.d.ts +++ b/packages/grid/src/vaadin-grid-selection-column.d.ts @@ -4,19 +4,14 @@ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ */ import type { GridDefaultItem } from './vaadin-grid.js'; -import { GridColumn } from './vaadin-grid-column.js'; -import type { GridSelectionColumnBaseMixinClass } from './vaadin-grid-selection-column-base-mixin.js'; +import type { GridColumnMixin } from './vaadin-grid-column.js'; +import type { GridColumn } from './vaadin-grid-column.js'; +import type { + GridSelectionColumnEventMap, + GridSelectionColumnMixinClass, +} from './vaadin-grid-selection-column-mixin.js'; -/** - * Fired when the `selectAll` property changes. - */ -export type GridSelectionColumnSelectAllChangedEvent = CustomEvent<{ value: boolean }>; - -export interface GridSelectionColumnCustomEventMap { - 'select-all-changed': GridSelectionColumnSelectAllChangedEvent; -} - -export interface GridSelectionColumnEventMap extends HTMLElementEventMap, GridSelectionColumnCustomEventMap {} +export * from './vaadin-grid-selection-column-mixin.js'; /** * `` is a helper element for the `` @@ -39,10 +34,13 @@ export interface GridSelectionColumnEventMap extends HTMLElementEventMap, GridSe * selection for all the items at once. * * __The default content can also be overridden__ - * - * @fires {CustomEvent} select-all-changed - Fired when the `selectAll` property changes. */ -declare class GridSelectionColumn extends GridColumn { +declare class GridSelectionColumn extends HTMLElement {} + +interface GridSelectionColumn + extends GridSelectionColumnMixinClass, + GridColumnMixin>, + GridColumn { addEventListener( type: K, listener: (this: GridSelectionColumn, ev: GridSelectionColumnEventMap[K]) => void, @@ -56,8 +54,6 @@ declare class GridSelectionColumn extends GridColumn extends GridSelectionColumnBaseMixinClass {} - declare global { interface HTMLElementTagNameMap { 'vaadin-grid-selection-column': GridSelectionColumn; diff --git a/packages/grid/src/vaadin-grid-selection-column.js b/packages/grid/src/vaadin-grid-selection-column.js index f2f6b0ea85..7a4f1cb912 100644 --- a/packages/grid/src/vaadin-grid-selection-column.js +++ b/packages/grid/src/vaadin-grid-selection-column.js @@ -6,7 +6,7 @@ import '@vaadin/checkbox/src/vaadin-checkbox.js'; import { defineCustomElement } from '@vaadin/component-base/src/define.js'; import { GridColumn } from './vaadin-grid-column.js'; -import { GridSelectionColumnBaseMixin } from './vaadin-grid-selection-column-base-mixin.js'; +import { GridSelectionColumnMixin } from './vaadin-grid-selection-column-mixin.js'; /** * `` is a helper element for the `` @@ -31,196 +31,14 @@ import { GridSelectionColumnBaseMixin } from './vaadin-grid-selection-column-bas * __The default content can also be overridden__ * * @customElement - * @extends GridColumn - * @mixes GridSelectionColumnBaseMixin * @fires {CustomEvent} select-all-changed - Fired when the `selectAll` property changes. + * @extends GridColumn + * @mixes GridSelectionColumnMixin */ -class GridSelectionColumn extends GridSelectionColumnBaseMixin(GridColumn) { +class GridSelectionColumn extends GridSelectionColumnMixin(GridColumn) { static get is() { return 'vaadin-grid-selection-column'; } - - static get properties() { - return { - /** - * The previous state of activeItem. When activeItem turns to `null`, - * previousActiveItem will have an Object with just unselected activeItem - * @private - */ - __previousActiveItem: Object, - }; - } - - static get observers() { - return ['__onSelectAllChanged(selectAll)']; - } - - constructor() { - super(); - - this.__boundOnActiveItemChanged = this.__onActiveItemChanged.bind(this); - this.__boundOnDataProviderChanged = this.__onDataProviderChanged.bind(this); - this.__boundOnSelectedItemsChanged = this.__onSelectedItemsChanged.bind(this); - } - - /** @protected */ - disconnectedCallback() { - this._grid.removeEventListener('active-item-changed', this.__boundOnActiveItemChanged); - this._grid.removeEventListener('data-provider-changed', this.__boundOnDataProviderChanged); - this._grid.removeEventListener('filter-changed', this.__boundOnSelectedItemsChanged); - this._grid.removeEventListener('selected-items-changed', this.__boundOnSelectedItemsChanged); - - super.disconnectedCallback(); - } - - /** @protected */ - connectedCallback() { - super.connectedCallback(); - if (this._grid) { - this._grid.addEventListener('active-item-changed', this.__boundOnActiveItemChanged); - this._grid.addEventListener('data-provider-changed', this.__boundOnDataProviderChanged); - this._grid.addEventListener('filter-changed', this.__boundOnSelectedItemsChanged); - this._grid.addEventListener('selected-items-changed', this.__boundOnSelectedItemsChanged); - } - } - - /** @private */ - __onSelectAllChanged(selectAll) { - if (selectAll === undefined || !this._grid) { - return; - } - - if (!this.__selectAllInitialized) { - // The initial value for selectAll property was applied, avoid clearing pre-selected items - this.__selectAllInitialized = true; - return; - } - - if (this._selectAllChangeLock) { - return; - } - - if (selectAll && this.__hasArrayDataProvider()) { - this.__withFilteredItemsArray((items) => { - this._grid.selectedItems = items; - }); - } else { - this._grid.selectedItems = []; - } - } - - /** - * Return true if array `a` contains all the items in `b` - * We need this when sorting or to preserve selection after filtering. - * @private - */ - __arrayContains(a, b) { - return Array.isArray(a) && Array.isArray(b) && b.every((i) => a.includes(i)); - } - - /** - * Override a method from `GridSelectionColumnBaseMixin` to handle the user - * selecting all items. - * - * @protected - * @override - */ - _selectAll() { - this.selectAll = true; - } - - /** - * Override a method from `GridSelectionColumnBaseMixin` to handle the user - * deselecting all items. - * - * @protected - * @override - */ - _deselectAll() { - this.selectAll = false; - } - - /** - * Override a method from `GridSelectionColumnBaseMixin` to handle the user - * selecting an item. - * - * @param {Object} item the item to select - * @protected - * @override - */ - _selectItem(item) { - this._grid.selectItem(item); - } - - /** - * Override a method from `GridSelectionColumnBaseMixin` to handle the user - * deselecting an item. - * - * @param {Object} item the item to deselect - * @protected - * @override - */ - _deselectItem(item) { - this._grid.deselectItem(item); - } - - /** @private */ - __onActiveItemChanged(e) { - const activeItem = e.detail.value; - if (this.autoSelect) { - const item = activeItem || this.__previousActiveItem; - if (item) { - this._grid._toggleItem(item); - } - } - this.__previousActiveItem = activeItem; - } - - /** @private */ - __hasArrayDataProvider() { - return Array.isArray(this._grid.items) && !!this._grid.dataProvider; - } - - /** @private */ - __onSelectedItemsChanged() { - this._selectAllChangeLock = true; - if (this.__hasArrayDataProvider()) { - this.__withFilteredItemsArray((items) => { - if (!this._grid.selectedItems.length) { - this.selectAll = false; - this._indeterminate = false; - } else if (this.__arrayContains(this._grid.selectedItems, items)) { - this.selectAll = true; - this._indeterminate = false; - } else { - this.selectAll = false; - this._indeterminate = true; - } - }); - } - this._selectAllChangeLock = false; - } - - /** @private */ - __onDataProviderChanged() { - this._selectAllHidden = !Array.isArray(this._grid.items); - } - - /** - * Assuming the grid uses an items array data provider, fetches all the filtered items - * from the data provider and invokes the callback with the resulting array. - * - * @private - */ - __withFilteredItemsArray(callback) { - const params = { - page: 0, - pageSize: Infinity, - sortOrders: [], - filters: this._grid._mapFilters(), - }; - this._grid.dataProvider(params, (items) => callback(items)); - } } defineCustomElement(GridSelectionColumn); diff --git a/packages/grid/src/vaadin-grid-sort-column-mixin.d.ts b/packages/grid/src/vaadin-grid-sort-column-mixin.d.ts new file mode 100644 index 0000000000..668d42af3c --- /dev/null +++ b/packages/grid/src/vaadin-grid-sort-column-mixin.d.ts @@ -0,0 +1,36 @@ +/** + * @license + * Copyright (c) 2016 - 2023 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import type { Constructor } from '@open-wc/dedupe-mixin'; +import type { GridSorterDirection } from './vaadin-grid-sorter.js'; + +/** + * Fired when the `direction` property changes. + */ +export type GridSortColumnDirectionChangedEvent = CustomEvent<{ value: GridSorterDirection }>; + +export interface GridSortColumnCustomEventMap { + 'direction-changed': GridSortColumnDirectionChangedEvent; +} + +export interface GridSortColumnEventMap extends HTMLElementEventMap, GridSortColumnCustomEventMap {} + +export declare function GridSortColumnMixin>( + superclass: T, +): Constructor & T; + +export declare class GridSortColumnMixinClass { + /** + * JS Path of the property in the item used for sorting the data. + */ + path: string | null | undefined; + + /** + * How to sort the data. + * Possible values are `asc` to use an ascending algorithm, `desc` to sort the data in + * descending direction, or `null` for not sorting the data. + */ + direction: GridSorterDirection | null | undefined; +} diff --git a/packages/grid/src/vaadin-grid-sort-column-mixin.js b/packages/grid/src/vaadin-grid-sort-column-mixin.js new file mode 100644 index 0000000000..afae53e517 --- /dev/null +++ b/packages/grid/src/vaadin-grid-sort-column-mixin.js @@ -0,0 +1,97 @@ +/** + * @license + * Copyright (c) 2016 - 2023 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ + +/** + * @polymerMixin + */ +export const GridSortColumnMixin = (superClass) => + class extends superClass { + static get properties() { + return { + /** + * JS Path of the property in the item used for sorting the data. + */ + path: String, + + /** + * How to sort the data. + * Possible values are `asc` to use an ascending algorithm, `desc` to sort the data in + * descending direction, or `null` for not sorting the data. + * @type {GridSorterDirection | undefined} + */ + direction: { + type: String, + notify: true, + }, + }; + } + + static get observers() { + return ['_onHeaderRendererOrBindingChanged(_headerRenderer, _headerCell, path, header, direction)']; + } + + constructor() { + super(); + + this.__boundOnDirectionChanged = this.__onDirectionChanged.bind(this); + } + + /** + * Renders the grid sorter to the header cell. + * + * @override + */ + _defaultHeaderRenderer(root, _column) { + let sorter = root.firstElementChild; + if (!sorter) { + sorter = document.createElement('vaadin-grid-sorter'); + sorter.addEventListener('direction-changed', this.__boundOnDirectionChanged); + root.appendChild(sorter); + } + + sorter.path = this.path; + sorter.__rendererDirection = this.direction; + sorter.direction = this.direction; + sorter.textContent = this.__getHeader(this.header, this.path); + } + + /** + * The sort column doesn't allow to use a custom header renderer + * to override the header cell content. + * It always renders the grid sorter to the header cell. + * + * @override + */ + _computeHeaderRenderer() { + return this._defaultHeaderRenderer; + } + + /** + * Updates the sorting direction once the grid sorter's direction is changed. + * The listener handles only user-fired events. + * + * @private + */ + __onDirectionChanged(e) { + // Skip if the direction is changed by the renderer. + if (e.detail.value === e.target.__rendererDirection) { + return; + } + + this.direction = e.detail.value; + } + + /** @private */ + __getHeader(header, path) { + if (header) { + return header; + } + + if (path) { + return this._generateHeader(path); + } + } + }; diff --git a/packages/grid/src/vaadin-grid-sort-column.d.ts b/packages/grid/src/vaadin-grid-sort-column.d.ts index 2525147c54..96d1846bf9 100644 --- a/packages/grid/src/vaadin-grid-sort-column.d.ts +++ b/packages/grid/src/vaadin-grid-sort-column.d.ts @@ -4,19 +4,10 @@ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ */ import type { GridDefaultItem } from './vaadin-grid.js'; -import { GridColumn } from './vaadin-grid-column.js'; -import type { GridSorterDirection } from './vaadin-grid-sorter.js'; +import type { GridColumn, GridColumnMixin } from './vaadin-grid-column.js'; +import type { GridSortColumnEventMap, GridSortColumnMixinClass } from './vaadin-grid-sort-column-mixin.js'; -/** - * Fired when the `direction` property changes. - */ -export type GridSortColumnDirectionChangedEvent = CustomEvent<{ value: GridSorterDirection }>; - -export interface GridSortColumnCustomEventMap { - 'direction-changed': GridSortColumnDirectionChangedEvent; -} - -export interface GridSortColumnEventMap extends HTMLElementEventMap, GridSortColumnCustomEventMap {} +export * from './vaadin-grid-sort-column-mixin.js'; /** * `` is a helper element for the `` @@ -30,22 +21,13 @@ export interface GridSortColumnEventMap extends HTMLElementEventMap, GridSortCol * * ... * ``` - * - * @fires {CustomEvent} direction-changed - Fired when the `direction` property changes. */ -declare class GridSortColumn extends GridColumn { - /** - * JS Path of the property in the item used for sorting the data. - */ - path: string | null | undefined; - - /** - * How to sort the data. - * Possible values are `asc` to use an ascending algorithm, `desc` to sort the data in - * descending direction, or `null` for not sorting the data. - */ - direction: GridSorterDirection | null | undefined; +declare class GridSortColumn extends HTMLElement {} +interface GridSortColumn + extends GridSortColumnMixinClass, + GridColumnMixin>, + GridColumn { addEventListener( type: K, listener: (this: GridSortColumn, ev: GridSortColumnEventMap[K]) => void, diff --git a/packages/grid/src/vaadin-grid-sort-column.js b/packages/grid/src/vaadin-grid-sort-column.js index 1df41ddaad..85590debf2 100644 --- a/packages/grid/src/vaadin-grid-sort-column.js +++ b/packages/grid/src/vaadin-grid-sort-column.js @@ -6,6 +6,7 @@ import './vaadin-grid-sorter.js'; import { defineCustomElement } from '@vaadin/component-base/src/define.js'; import { GridColumn } from './vaadin-grid-column.js'; +import { GridSortColumnMixin } from './vaadin-grid-sort-column-mixin.js'; /** * `` is a helper element for the `` @@ -24,97 +25,12 @@ import { GridColumn } from './vaadin-grid-column.js'; * * @customElement * @extends GridColumn + * @mixes GridSortColumnMixin */ -class GridSortColumn extends GridColumn { +class GridSortColumn extends GridSortColumnMixin(GridColumn) { static get is() { return 'vaadin-grid-sort-column'; } - - static get properties() { - return { - /** - * JS Path of the property in the item used for sorting the data. - */ - path: String, - - /** - * How to sort the data. - * Possible values are `asc` to use an ascending algorithm, `desc` to sort the data in - * descending direction, or `null` for not sorting the data. - * @type {GridSorterDirection | undefined} - */ - direction: { - type: String, - notify: true, - }, - }; - } - - static get observers() { - return ['_onHeaderRendererOrBindingChanged(_headerRenderer, _headerCell, path, header, direction)']; - } - - constructor() { - super(); - - this.__boundOnDirectionChanged = this.__onDirectionChanged.bind(this); - } - - /** - * Renders the grid sorter to the header cell. - * - * @override - */ - _defaultHeaderRenderer(root, _column) { - let sorter = root.firstElementChild; - if (!sorter) { - sorter = document.createElement('vaadin-grid-sorter'); - sorter.addEventListener('direction-changed', this.__boundOnDirectionChanged); - root.appendChild(sorter); - } - - sorter.path = this.path; - sorter.__rendererDirection = this.direction; - sorter.direction = this.direction; - sorter.textContent = this.__getHeader(this.header, this.path); - } - - /** - * The sort column doesn't allow to use a custom header renderer - * to override the header cell content. - * It always renders the grid sorter to the header cell. - * - * @override - */ - _computeHeaderRenderer() { - return this._defaultHeaderRenderer; - } - - /** - * Updates the sorting direction once the grid sorter's direction is changed. - * The listener handles only user-fired events. - * - * @private - */ - __onDirectionChanged(e) { - // Skip if the direction is changed by the renderer. - if (e.detail.value === e.target.__rendererDirection) { - return; - } - - this.direction = e.detail.value; - } - - /** @private */ - __getHeader(header, path) { - if (header) { - return header; - } - - if (path) { - return this._generateHeader(path); - } - } } defineCustomElement(GridSortColumn); diff --git a/packages/grid/src/vaadin-grid-tree-column-mixin.d.ts b/packages/grid/src/vaadin-grid-tree-column-mixin.d.ts index d21e43e275..ca29fca850 100644 --- a/packages/grid/src/vaadin-grid-tree-column-mixin.d.ts +++ b/packages/grid/src/vaadin-grid-tree-column-mixin.d.ts @@ -5,9 +5,8 @@ */ import type { Constructor } from '@open-wc/dedupe-mixin'; -import type { GridColumn } from './vaadin-grid-column.js'; -export declare function GridTreeColumnMixin>( +export declare function GridTreeColumnMixin>( superclass: T, ): Constructor> & T; diff --git a/packages/grid/src/vaadin-grid-tree-column.d.ts b/packages/grid/src/vaadin-grid-tree-column.d.ts index 3d5af552b4..6d5d3d3618 100644 --- a/packages/grid/src/vaadin-grid-tree-column.d.ts +++ b/packages/grid/src/vaadin-grid-tree-column.d.ts @@ -25,7 +25,7 @@ declare class GridTreeColumn extends HTMLElement {} interface GridTreeColumn extends GridTreeColumnMixinClass, - GridColumnMixin>, + GridColumnMixin>, GridColumn {} declare global {