diff --git a/lib/cosmoz-omnitable-range-input-mixin.js b/lib/cosmoz-omnitable-range-input-mixin.js index d338051f..fa4f81dc 100644 --- a/lib/cosmoz-omnitable-range-input-mixin.js +++ b/lib/cosmoz-omnitable-range-input-mixin.js @@ -2,11 +2,10 @@ import { Debouncer } from '@polymer/polymer/lib/utils/debounce.js'; import { timeOut } from '@polymer/polymer/lib/utils/async.js'; import { enqueueDebouncer } from '@polymer/polymer/lib/utils/flush.js'; +import { invoke } from '@neovici/cosmoz-utils/function'; -const getCloseableParent = el => - typeof el.close === 'function' - ? el - : getCloseableParent(el.parentElement); +const getCloseableParent = (el) => + typeof el.close === 'function' ? el : getCloseableParent(el.parentElement); /** * @polymer @@ -14,39 +13,46 @@ const getCloseableParent = el => * @param {class} base The base class * @returns {class} The base class with the mixin applied */ -export const rangeInputMixin = base => // eslint-disable-line max-lines-per-function +export const rangeInputMixin = ( + base, // eslint-disable-line max-lines-per-function +) => /** * @polymer * @mixinClass */ class extends base { - static get properties() { // eslint-disable-line max-lines-per-function + static get properties() { + // eslint-disable-line max-lines-per-function return { filter: { type: Object, - notify: true + notify: true, }, values: { type: Array, value() { return []; - } + }, }, headerFocused: { type: Boolean, - notify: true + notify: true, }, min: { type: Number, - value: null + value: null, }, max: { type: Number, - value: null + value: null, + }, + + limits: { + type: Function, }, /** @@ -54,12 +60,12 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func */ autoupdate: { type: String, - value: true + value: true, }, locale: { type: String, - value: null + value: null, }, _filterInput: { @@ -67,14 +73,14 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func value() { return { min: null, - max: null + max: null, }; - } + }, }, _range: { type: Object, - computed: '_computeRange(values.*)' + computed: '_computeRange(values.*)', }, _limit: { @@ -82,30 +88,32 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func computed: '_computeLimit(_range, _filterInput.*, min, max)', value() { return {}; - } + }, }, _tooltip: { type: String, - computed: '_computeTooltip(title, _filterText)' + computed: '_computeTooltip(title, _filterText)', }, _fromClasses: { type: String, - computed: '_computeInputClasses(_filterInput.min)' + computed: '_computeInputClasses(_filterInput.min)', }, _toClasses: { type: String, - computed: '_computeInputClasses(_filterInput.max)' - } + computed: '_computeInputClasses(_filterInput.max)', + }, }; } + static get observers() { return [ '_filterInputChanged(_filterInput.*, autoupdate)', - '_filterChanged(filter.*)' + '_filterChanged(filter.*)', + '_updateLimits(limits, headerFocused)', ]; } @@ -121,13 +129,13 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func } /** - * Converts a value to number optionaly limiting it. - * - * @param {Number|*} value The value to convert to number - * @param {Number|*} limit The value used to limit the number - * @param {Function} limitFunc The function used to limit the number (Math.min|Math.max) - * @returns {Number|void} Value converted to Number or void - */ + * Converts a value to number optionaly limiting it. + * + * @param {Number|*} value The value to convert to number + * @param {Number|*} limit The value used to limit the number + * @param {Function} limitFunc The function used to limit the number (Math.min|Math.max) + * @returns {Number|void} Value converted to Number or void + */ toNumber(value, limit, limitFunc) { if (value == null || value === '') { return; @@ -152,11 +160,11 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func /** * Get the comparable value of an item. - * - * @param {Object} item Item to be processed - * @param {String} valuePath Property path - * @returns {Number|void} Valid value or void - */ + * + * @param {Object} item Item to be processed + * @param {String} valuePath Property path + * @returns {Number|void} Valid value or void + */ getComparableValue(item, valuePath) { if (item == null) { return; @@ -177,26 +185,28 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func } /** - * Computes min/max range from values. - * - * @param {Object} change `values` property changes - * @returns {Object} Computed min/max - */ + * Computes min/max range from values. + * + * @param {Object} change `values` property changes + * @returns {Object} Computed min/max + */ _computeRange(change) { const allValues = change.base, - values = Array.isArray(allValues) && allValues.length - && allValues.map(v => this.toValue(v)).filter(n => n != null); + values = + Array.isArray(allValues) && + allValues.length && + allValues.map((v) => this.toValue(v)).filter((n) => n != null); if (!values || values.length < 1) { return { min: null, - max: null + max: null, }; } return values.reduce((p, n) => { return { min: this.toValue(n, p.min, Math.min), - max: this.toValue(n, p.max, Math.max) + max: this.toValue(n, p.max, Math.max), }; }, {}); } @@ -213,13 +223,20 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func return { fromMin: aMin, - fromMax: this.toValue(aMax, this._fromInputString(input.max, 'max'), Math.min), - toMin: this.toValue(aMin, this._fromInputString(input.min, 'min'), Math.max), - toMax: aMax + fromMax: this.toValue( + aMax, + this._fromInputString(input.max, 'max'), + Math.min, + ), + toMin: this.toValue( + aMin, + this._fromInputString(input.min, 'min'), + Math.max, + ), + toMax: aMax, }; } - _computeFilterText(change) { if (change.base == null) { return undefined; @@ -243,7 +260,7 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func if (text == null) { return title; } - return `${ title }: ${ text }`; + return `${title}: ${text}`; } _fromInputString(value) { @@ -261,17 +278,17 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func _getDefaultFilter() { return { min: null, - max: null + max: null, }; } /** - * Observes changes of _filterInput, saves the path, debounces _limitInput. - * - * @param {Object} change '_filterInput' property changes - * @param {Boolean} autoupdate whether to auto-update on value changes - * @returns {void} - */ + * Observes changes of _filterInput, saves the path, debounces _limitInput. + * + * @param {Object} change '_filterInput' property changes + * @param {Boolean} autoupdate whether to auto-update on value changes + * @returns {void} + */ _filterInputChanged(change, autoupdate) { const path = change.path.split('.')[1]; this.__inputChangePath = path || null; @@ -286,7 +303,7 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func () => { this._limitInput(); this._updateFilter(); - } + }, ); enqueueDebouncer(this._limitInputDebouncer); } @@ -308,32 +325,37 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func _onKeyDown(event) { const input = event.currentTarget, - inputs = Array.from(input.parentElement.querySelectorAll('cosmoz-input')), - nextInput = inputs[inputs.findIndex(i => i === input) + 1], + inputs = Array.from( + input.parentElement.querySelectorAll('cosmoz-input'), + ), + nextInput = inputs[inputs.findIndex((i) => i === input) + 1], isLastInput = !nextInput, isFirstInput = inputs[0] === input; switch (event.keyCode) { - case 13: // Enter - event.preventDefault(); - - if (!isLastInput) { - nextInput.focus(); - } else { - // if this is the last input, update the filter - const limited = this._limitInput(); - this._updateFilter(); - // and close the dropdown if the value was not out of bounds - if (!limited) { - this._closeParent(input); + case 13: // Enter + event.preventDefault(); + + if (!isLastInput) { + nextInput.focus(); + } else { + // if this is the last input, update the filter + const limited = this._limitInput(); + this._updateFilter(); + // and close the dropdown if the value was not out of bounds + if (!limited) { + this._closeParent(input); + } } - } - break; + break; - case 9: // Tab - if (isLastInput && !event.shiftKey || isFirstInput && event.shiftKey) { - this._closeParent(input); - } + case 9: // Tab + if ( + (isLastInput && !event.shiftKey) || + (isFirstInput && event.shiftKey) + ) { + this._closeParent(input); + } } } @@ -341,27 +363,29 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func getCloseableParent(input).close(); } - _onDropdownOpenedChanged({ - currentTarget, detail: { value } - }) { + _onDropdownOpenedChanged({ currentTarget, detail: { value } }) { if (!value) { return; } // focus the first input after the dropdown is visible - setTimeout(() => currentTarget.querySelector('cosmoz-input').focus(), 100); + setTimeout( + () => currentTarget.querySelector('cosmoz-input').focus(), + 100, + ); } - /** - * Debounced function called by `_filterInputChanged` when `_filterInput` changes. - * - * @returns {void} - */ + * Debounced function called by `_filterInputChanged` when `_filterInput` changes. + * + * @returns {void} + */ _limitInput() { const input = this._filterInput, path = this.__inputChangePath, - value = path ? this._fromInputString(this.get(path, input), path) : null; + value = path + ? this._fromInputString(this.get(path, input), path) + : null; this.__inputChangePath = null; @@ -377,9 +401,14 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func minValue = this.toValue(value, lowerLimit, Math.max), limitedValue = this.toValue(minValue, upperLimit, Math.min); - if (this.getComparableValue(value) !== this.getComparableValue(limitedValue)) { + if ( + this.getComparableValue(value) !== this.getComparableValue(limitedValue) + ) { //set value without debouncing _limitInput again. - this.set(['_filterInput', path], this._toInputString(limitedValue, path)); + this.set( + ['_filterInput', path], + this._toInputString(limitedValue, path), + ); if (this._limitInputDebouncer) { this._limitInputDebouncer.cancel(); } @@ -395,8 +424,10 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func min = this._fromInputString(input.min, 'min'), max = this._fromInputString(input.max, 'max'); - if (this.getComparableValue(min) === this.getComparableValue(filter, 'min') - && this.getComparableValue(max) === this.getComparableValue(filter, 'max') + if ( + this.getComparableValue(min) === + this.getComparableValue(filter, 'min') && + this.getComparableValue(max) === this.getComparableValue(filter, 'max') ) { return; } @@ -413,15 +444,17 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func min = this._fromInputString(input.min, 'min'), max = this._fromInputString(input.max, 'max'); - if (this.getComparableValue(min) === this.getComparableValue(filter, 'min') - && this.getComparableValue(max) === this.getComparableValue(filter, 'max') + if ( + this.getComparableValue(min) === + this.getComparableValue(filter, 'min') && + this.getComparableValue(max) === this.getComparableValue(filter, 'max') ) { return; } this.set('_filterInput', { min: this._toInputString(filter.min), - max: this._toInputString(filter.max) + max: this._toInputString(filter.max), }); if (this._limitInputDebouncer) { this._limitInputDebouncer.cancel(); @@ -433,10 +466,25 @@ export const rangeInputMixin = base => // eslint-disable-line max-lines-per-func if (filter == null) { return false; } - return this.toValue(filter.min) != null || this.toValue(filter.max) != null; + return ( + this.toValue(filter.min) != null || this.toValue(filter.max) != null + ); } resetFilter() { this.filter = this._getDefaultFilter(); } + + _updateLimits(limits, headerFocused) { + if (!limits) return; + Promise.resolve(invoke(limits, { active: headerFocused })).then( + (res) => { + const {min, max} = res ?? {}; + Object.assign(this, { + ...(min != null ? { min } : {}), + ...(max != null ? { max } : {}), + }); + }, + ); + } }; diff --git a/package-lock.json b/package-lock.json index 44a023f3..aa5b3715 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1625,14 +1625,14 @@ "dev": true }, "node_modules/@neovici/cfg": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/@neovici/cfg/-/cfg-1.55.0.tgz", - "integrity": "sha512-sme38Lhfm2wZB9L5rRnge5NLF0uVyekJ40l4hVJfeSsZCiv7eM+b5lJxd47u9GS8yQwFbqkx4F6e1NW/iTXHIw==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@neovici/cfg/-/cfg-1.57.0.tgz", + "integrity": "sha512-BNPQ9xwZqv0MvzwSqcH8CPv7lqfonaF5CPyM4bqGwEBjbSpWXVA2jvBc22hS7ad7LBlVF4cQxUNlPSiBlUcSeA==", "dev": true, "dependencies": { "@playwright/test": "^1.40.1", - "@typescript-eslint/eslint-plugin": "^7.0.0", - "@typescript-eslint/parser": "^7.0.0", + "@typescript-eslint/eslint-plugin": "^7.0.0 || ^8.0.0", + "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", "@web/dev-server": "^0.4.0", "@web/dev-server-esbuild": "^1.0.0", "@web/test-runner": "^0.18.0", @@ -1743,9 +1743,9 @@ } }, "node_modules/@neovici/cosmoz-utils": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@neovici/cosmoz-utils/-/cosmoz-utils-6.9.0.tgz", - "integrity": "sha512-qMMmUWGBEqqB5Wwgj9xgNg4jyiMsbLfUw+VRYbPycUz9yWoGeCA691WcqI80SHMsNafzle+50Mu5wt62ymKQNA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@neovici/cosmoz-utils/-/cosmoz-utils-6.10.0.tgz", + "integrity": "sha512-Jqslo5IDrC9ti5m2MKgbhmOe3gi1di8AHet4Xyd5fIdtW6LSvkQkBMzNS32Xzltr8hYZEDx9/oLQxJjcVmC4yg==", "dependencies": { "@pionjs/pion": "^2.0.0" } @@ -2005,9 +2005,9 @@ } }, "node_modules/@pionjs/pion": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@pionjs/pion/-/pion-2.5.2.tgz", - "integrity": "sha512-6RHrGmiJuQ2mLX2Yye3vB2r/IeISCsUOlSLmiijNiG/q0dIsAlzxgZ+tZ9F7k/QMPpdpy7YoJi6QFjzArJfaeg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@pionjs/pion/-/pion-2.7.0.tgz", + "integrity": "sha512-HKvZ9WFrzlYTWTKnUw0cbS9evUStCNrW7I/2eHOZvpAtJecTnyIQ7UC6CDkVlx8rEPapTNN4uQCHbUkaURmizA==", "dependencies": { "lit-html": "^2.0.0 || ^3.0.0" } @@ -7307,12 +7307,12 @@ } }, "node_modules/husky": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", - "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz", + "integrity": "sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==", "dev": true, "bin": { - "husky": "bin.mjs" + "husky": "bin.js" }, "engines": { "node": ">=18" @@ -8292,9 +8292,9 @@ } }, "node_modules/lit-html": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.4.tgz", - "integrity": "sha512-yKKO2uVv7zYFHlWMfZmqc+4hkmSbFp8jgjdZY9vvR9jr4J8fH6FUMXhr+ljfELgmjpvlF7Z1SJ5n5/Jeqtc9YA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.2.0.tgz", + "integrity": "sha512-pwT/HwoxqI9FggTrYVarkBKFN9MlTUpLrDHubTmW4SrkL3kkqW5gxwbxMMUnbbRHBC0WTZnYHcjDSCM559VyfA==", "dependencies": { "@types/trusted-types": "^2.0.2" } @@ -13239,13 +13239,13 @@ } }, "node_modules/sinon": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-18.0.0.tgz", - "integrity": "sha512-+dXDXzD1sBO6HlmZDd7mXZCR/y5ECiEiGCBSGuFD/kZ0bDTofPYc6JaeGmPSF+1j1MejGUWkORbYOLDyvqCWpA==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-18.0.1.tgz", + "integrity": "sha512-a2N2TDY1uGviajJ6r4D1CyRAkzE9NNVlYOV1wX5xQDuAk0ONgzgRl0EjCQuRCPxOwp13ghsMwt9Gdldujs39qw==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/fake-timers": "11.2.2", "@sinonjs/samsam": "^8.0.0", "diff": "^5.2.0", "nise": "^6.0.0",