@neovici/cosmoz-omnitable 7.2.1 → 8.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/cosmoz-omnitable-column-amount.js +89 -320
- package/cosmoz-omnitable-column-autocomplete.js +36 -47
- package/cosmoz-omnitable-column-boolean.js +107 -209
- package/cosmoz-omnitable-column-date.js +89 -102
- package/cosmoz-omnitable-column-datetime.js +86 -119
- package/cosmoz-omnitable-column-list-data.js +4 -1
- package/cosmoz-omnitable-column-list-horizontal.js +20 -38
- package/cosmoz-omnitable-column-list-mixin.js +133 -140
- package/cosmoz-omnitable-column-list.js +19 -28
- package/cosmoz-omnitable-column-mixin.js +69 -447
- package/cosmoz-omnitable-column-number.js +91 -183
- package/cosmoz-omnitable-column-time.js +77 -162
- package/cosmoz-omnitable-column.js +49 -93
- package/cosmoz-omnitable-group-row.js +1 -5
- package/cosmoz-omnitable-header-row.js +9 -6
- package/cosmoz-omnitable-item-expand.js +0 -3
- package/cosmoz-omnitable-item-row.js +5 -8
- package/cosmoz-omnitable-styles.js +4 -8
- package/cosmoz-omnitable.js +72 -764
- package/lib/cosmoz-omnitable-amount-range-input.js +295 -0
- package/{cosmoz-omnitable-column-date-mixin.js → lib/cosmoz-omnitable-date-input-mixin.js} +4 -26
- package/lib/cosmoz-omnitable-date-range-input.js +81 -0
- package/lib/cosmoz-omnitable-datetime-range-input.js +75 -0
- package/lib/cosmoz-omnitable-number-range-input.js +159 -0
- package/{cosmoz-omnitable-column-range-mixin.js → lib/cosmoz-omnitable-range-input-mixin.js} +45 -123
- package/lib/cosmoz-omnitable-settings.js +8 -5
- package/lib/cosmoz-omnitable-time-range-input.js +130 -0
- package/lib/generic-sorter.js +2 -2
- package/lib/invoke.js +1 -0
- package/lib/memoize.js +54 -0
- package/lib/polymer-haunted-render-mixin.js +19 -0
- package/lib/save-as-csv-action.js +32 -0
- package/lib/save-as-xlsx-action.js +25 -0
- package/lib/use-canvas-width.js +1 -1
- package/lib/use-dom-columns.js +138 -0
- package/lib/use-hash-state.js +59 -0
- package/lib/use-layout.js +1 -1
- package/lib/use-omnitable.js +26 -14
- package/lib/use-processed-items.js +132 -0
- package/lib/use-sort-and-group-options.js +30 -0
- package/lib/utils-amount.js +147 -0
- package/lib/utils-data.js +36 -0
- package/lib/utils-date.js +204 -0
- package/lib/utils-datetime.js +71 -0
- package/lib/utils-number.js +112 -0
- package/lib/utils-time.js +115 -0
- package/package.json +1 -2
- package/lib/use-force-render.js +0 -8
- package/lib/use-render-on-column-updates.js +0 -18
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { _ } from '@neovici/cosmoz-i18next';
|
|
2
|
+
import { PolymerElement } from '@polymer/polymer';
|
|
3
|
+
import { html } from 'lit-html';
|
|
4
|
+
import { ifDefined } from 'lit-html/directives/if-defined';
|
|
5
|
+
import { rangeInputMixin } from './cosmoz-omnitable-range-input-mixin';
|
|
6
|
+
import { polymerHauntedRender } from './polymer-haunted-render-mixin';
|
|
7
|
+
|
|
8
|
+
class AmountRangeInput extends rangeInputMixin(polymerHauntedRender(PolymerElement)) {
|
|
9
|
+
static get properties() {
|
|
10
|
+
return {
|
|
11
|
+
/**
|
|
12
|
+
* Base Currency used in filters
|
|
13
|
+
*/
|
|
14
|
+
currency: { type: String },
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* If this is set true then currency property will be the currency with highest occurrence in values
|
|
18
|
+
*/
|
|
19
|
+
autodetect: { type: Boolean, value: false },
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Exchange rates of currencies. Example: {"EUR": 1, "USD":0.8169982616, "AUD":0.6529827192, "SEK": 0.1019271438}'
|
|
23
|
+
* Default exchange rate is 1 and it is used for every currency that is present on column values but missing from exchange rates object.
|
|
24
|
+
*/
|
|
25
|
+
rates: { type: Object },
|
|
26
|
+
// trigger filter updates manually
|
|
27
|
+
autoupdate: { type: String, value: false },
|
|
28
|
+
_filterText: { type: String, computed: '_computeFilterText(filter.*, _formatters)' }
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static get observers() {
|
|
33
|
+
return [
|
|
34
|
+
'_valuesChanged(autodetect, currency, values)'
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// eslint-disable-next-line max-lines-per-function
|
|
39
|
+
render() {
|
|
40
|
+
const onOpenedChanged = event => {
|
|
41
|
+
this.headerFocused = event.detail.value;
|
|
42
|
+
this._onDropdownOpenedChanged(event);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return html`
|
|
46
|
+
<style>
|
|
47
|
+
paper-dropdown-menu {
|
|
48
|
+
--iron-icon-width: 0;
|
|
49
|
+
display: block;
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
52
|
+
<cosmoz-clear-button @click=${ () => this.resetFilter() } ?visible=${ this.hasFilter() }></cosmoz-clear-button>
|
|
53
|
+
<paper-dropdown-menu
|
|
54
|
+
label=${ this.title }
|
|
55
|
+
placeholder=${ ifDefined(this._filterText) }
|
|
56
|
+
class="external-values-${ this.externalValues }"
|
|
57
|
+
title=${ this._tooltip }
|
|
58
|
+
horizontal-align="right"
|
|
59
|
+
?opened=${ this.headerFocused }
|
|
60
|
+
@opened-changed=${ onOpenedChanged }
|
|
61
|
+
>
|
|
62
|
+
<div class="dropdown-content" slot="dropdown-content" style="padding: 15px; min-width: 150px;">
|
|
63
|
+
<h3 style="margin: 0;">${ this.title }</h3>
|
|
64
|
+
<paper-input
|
|
65
|
+
class=${ this._fromClasses }
|
|
66
|
+
type="number"
|
|
67
|
+
label=${ _('Min amount') }
|
|
68
|
+
title=${ _('Minimum amount') }
|
|
69
|
+
.value=${ this._filterInput?.min }
|
|
70
|
+
@value-changed=${ event => {
|
|
71
|
+
this.set('_filterInput.min', event.detail.value);
|
|
72
|
+
} }
|
|
73
|
+
@blur=${ event => this._onBlur(event) }
|
|
74
|
+
@keydown=${ event => this._onKeyDown(event) }
|
|
75
|
+
min=${ this._toInputStringAmount(this._limit.fromMin) }
|
|
76
|
+
max=${ this._toInputStringAmount(this._limit.fromMax) }
|
|
77
|
+
><div slot="suffix" suffix>${ this.filter?.min?.currency }</div></paper-input>
|
|
78
|
+
<paper-input
|
|
79
|
+
class=${ this._toClasses }
|
|
80
|
+
type="number"
|
|
81
|
+
label=${ _('Max amount') }
|
|
82
|
+
title=${ _('Maximum amount') }
|
|
83
|
+
.value=${ this._filterInput?.max }
|
|
84
|
+
@value-changed=${ event => {
|
|
85
|
+
this.set('_filterInput.max', event.detail.value);
|
|
86
|
+
} }
|
|
87
|
+
@blur=${ event => this._onBlur(event) }
|
|
88
|
+
@keydown=${ event => this._onKeyDown(event) }
|
|
89
|
+
min=${ this._toInputStringAmount(this._limit.toMin) }
|
|
90
|
+
max=${ this._toInputStringAmount(this._limit.toMax) }
|
|
91
|
+
><div slot="suffix" suffix>${ this.filter?.max?.currency }</div></paper-input>
|
|
92
|
+
</div>
|
|
93
|
+
</paper-dropdown-menu>
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Auto-detect currency property: Most frequently occurring currency
|
|
99
|
+
* @param {boolean} autodetect Whether the currency property will be the currency with highest occurrence in values or not.
|
|
100
|
+
* @param {string} currency Base currency used in filters.
|
|
101
|
+
* @param {Array} values Array of amounts
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
_valuesChanged(autodetect, currency, values) {
|
|
105
|
+
if (!Array.isArray(values) || values.length < 1) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!autodetect && currency) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// array with currencies as keys and occurence as value
|
|
112
|
+
const currencies = values.reduce((p, n) => {
|
|
113
|
+
if (n.currency) {
|
|
114
|
+
const c = n.currency;
|
|
115
|
+
p[c] = (p[c] || 0) + 1;
|
|
116
|
+
}
|
|
117
|
+
return p;
|
|
118
|
+
}, {});
|
|
119
|
+
|
|
120
|
+
let key = Object.keys(currencies)[0];
|
|
121
|
+
Object.keys(currencies).reduce((p, n) => {
|
|
122
|
+
const max = Math.max(p, currencies[n]);
|
|
123
|
+
if (max === currencies[n]) {
|
|
124
|
+
key = n;
|
|
125
|
+
}
|
|
126
|
+
return max;
|
|
127
|
+
}, 0);
|
|
128
|
+
this.set('currency', key);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Converts a value to an amount object optionaly limiting it.
|
|
133
|
+
*
|
|
134
|
+
* @param {Object} value The value to convert to number
|
|
135
|
+
* @param {Object} limit The value used to limit the number
|
|
136
|
+
* @param {Function} limitFunc The function used to limit the number (Math.min|Math.max)
|
|
137
|
+
* @returns {Object|void} Value converted to Number or void
|
|
138
|
+
*/
|
|
139
|
+
toAmount(value, limit, limitFunc) { // eslint-disable-line max-statements
|
|
140
|
+
if (value == null || value === '') {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (typeof value !== 'object' || value.currency == null || value.currency === '') {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const number = this.toNumber(value.amount);
|
|
149
|
+
if (number == null || Number.isNaN(number)) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const amount = {
|
|
153
|
+
currency: value.currency,
|
|
154
|
+
amount: number
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
if (limitFunc == null || limit == null) {
|
|
158
|
+
return amount;
|
|
159
|
+
}
|
|
160
|
+
const lAmount = this.toAmount(limit);
|
|
161
|
+
if (lAmount == null) {
|
|
162
|
+
return amount;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// calculate value and limit amounts with rates
|
|
166
|
+
const rates = this.rates || {},
|
|
167
|
+
valAmount = amount.amount * (rates[amount.currency] || 1),
|
|
168
|
+
limAmount = lAmount.amount * (rates[lAmount.currency] || 1),
|
|
169
|
+
lNumber = this.toNumber(valAmount, limAmount, limitFunc);
|
|
170
|
+
return lNumber === valAmount ? amount : lAmount;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
toValue() {
|
|
174
|
+
return this.toAmount.apply(this, arguments);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get the comparable value of an item.
|
|
179
|
+
*
|
|
180
|
+
* @param {Object} item Item to be processed
|
|
181
|
+
* @param {String} valuePath Property path
|
|
182
|
+
* @returns {Number|void} Valid value or void
|
|
183
|
+
*/
|
|
184
|
+
getComparableValue(item, valuePath) {
|
|
185
|
+
const value = super.getComparableValue(item, valuePath);
|
|
186
|
+
|
|
187
|
+
if (value == null) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const amount = this.toNumber(value.amount),
|
|
191
|
+
rates = this.rates;
|
|
192
|
+
|
|
193
|
+
if (rates == null) {
|
|
194
|
+
return amount;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return amount * (rates[value.currency] || 1);
|
|
198
|
+
}
|
|
199
|
+
getString(item, valuePath = this.valuePath) {
|
|
200
|
+
const value = this.toValue(this.get(valuePath, item));
|
|
201
|
+
if (value === undefined) {
|
|
202
|
+
return '';
|
|
203
|
+
}
|
|
204
|
+
if (value === null) {
|
|
205
|
+
return 'Invalid value';
|
|
206
|
+
}
|
|
207
|
+
return this.renderValue(value);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getCurrency(item, valuePath) {
|
|
211
|
+
const value = this.get(valuePath, item);
|
|
212
|
+
return value && value.currency;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
getFormatter(currency, locale) {
|
|
216
|
+
const id = locale ? locale : '',
|
|
217
|
+
key = currency + id || '',
|
|
218
|
+
formatters = this._formatters = this._formatters || {};
|
|
219
|
+
if (formatters[key]) {
|
|
220
|
+
return formatters[key];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
formatters[key] = new Intl.NumberFormat(locale || undefined, {
|
|
224
|
+
style: 'currency',
|
|
225
|
+
currency
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return formatters[key];
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Converts an amount to symbol and value to be rendered.
|
|
232
|
+
*
|
|
233
|
+
* @param {Object} value Amount to be formated
|
|
234
|
+
* @param {Object} _formatters = this._formatters All possible formatters.
|
|
235
|
+
* @returns {String} Formated value or empty string.
|
|
236
|
+
*/
|
|
237
|
+
renderValue(value) {
|
|
238
|
+
const amount = this.toAmount(value);
|
|
239
|
+
if (amount == null) {
|
|
240
|
+
return '';
|
|
241
|
+
}
|
|
242
|
+
return this.getFormatter(amount.currency, this.locale)
|
|
243
|
+
.format(value.amount);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
_amountValueChanged(event) {
|
|
247
|
+
const input = event.target,
|
|
248
|
+
value = input.value,
|
|
249
|
+
item = event.model.item,
|
|
250
|
+
originalValue = this.get(this.valuePath, item),
|
|
251
|
+
amountValue = Number(value),
|
|
252
|
+
newValue = {
|
|
253
|
+
amount: amountValue,
|
|
254
|
+
currency: originalValue.currency
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
this.set(this.valuePath, newValue, item);
|
|
258
|
+
this._fireItemChangeEvent(item, this.valuePath, originalValue, this.renderValue.bind(this));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
_toInputString(value) {
|
|
262
|
+
const amount = this.toValue(value);
|
|
263
|
+
if (amount == null) {
|
|
264
|
+
return null; //Need null to clear input
|
|
265
|
+
}
|
|
266
|
+
return this.toNumber(amount.amount);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
_toInputStringAmount(value) {
|
|
270
|
+
const rates = this.rates;
|
|
271
|
+
if (rates == null) {
|
|
272
|
+
return this._toInputString(value);
|
|
273
|
+
}
|
|
274
|
+
const amount = this.toValue(value);
|
|
275
|
+
if (amount == null) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
const toCurrency = this.toNumber(amount.amount) * (rates[amount.currency] || 1) / (rates[this.currency] || 1);
|
|
279
|
+
return toCurrency.toFixed(2);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
_fromInputString(value, property) {
|
|
283
|
+
const number = this.toNumber(value);
|
|
284
|
+
if (number == null) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
return this.toValue({
|
|
288
|
+
amount: number,
|
|
289
|
+
currency: property && this.get(['filter', property, 'currency']) || this.currency
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
customElements.define('cosmoz-omnitable-amount-range-input', AmountRangeInput);
|
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin';
|
|
2
1
|
import { toLocalISOString } from '@neovici/cosmoz-utils/lib/date';
|
|
3
|
-
|
|
4
|
-
import { rangeColumnMixin } from './cosmoz-omnitable-column-range-mixin';
|
|
2
|
+
import { rangeInputMixin } from './cosmoz-omnitable-range-input-mixin';
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
* @polymer
|
|
8
6
|
* @mixinFunction
|
|
9
|
-
* @appliesMixin rangeColumnMixin
|
|
10
7
|
|
|
11
8
|
* @param {class} base The base class
|
|
12
9
|
* @returns {class} The base class with the mixin applied
|
|
13
10
|
*/
|
|
14
|
-
export const
|
|
11
|
+
export const dateInputMixin = base => // eslint-disable-line max-lines-per-function
|
|
15
12
|
/**
|
|
16
13
|
* @polymer
|
|
17
14
|
* @mixinClass
|
|
18
15
|
*/
|
|
19
|
-
class extends
|
|
16
|
+
class extends rangeInputMixin(base) {
|
|
20
17
|
static get properties() {
|
|
21
18
|
return {
|
|
22
19
|
max: {
|
|
@@ -34,24 +31,6 @@ export const dateColumnMixin = dedupingMixin(base => // eslint-disable-line max-
|
|
|
34
31
|
computed: '_computeFilterText(filter.*, formatter)'
|
|
35
32
|
},
|
|
36
33
|
|
|
37
|
-
width: {
|
|
38
|
-
type: String,
|
|
39
|
-
value: '100px'
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
editWidth: {
|
|
43
|
-
type: String,
|
|
44
|
-
value: '150px'
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* No need to grow, as the values in a date column should have known fixed width
|
|
49
|
-
* @returns {String} Default flex
|
|
50
|
-
*/
|
|
51
|
-
flex: {
|
|
52
|
-
type: String,
|
|
53
|
-
value: '0'
|
|
54
|
-
},
|
|
55
34
|
formatter: {
|
|
56
35
|
type: Object,
|
|
57
36
|
computed: '_computeFormatter(locale)'
|
|
@@ -201,5 +180,4 @@ export const dateColumnMixin = dedupingMixin(base => // eslint-disable-line max-
|
|
|
201
180
|
_toLocalISOString(date) {
|
|
202
181
|
return toLocalISOString(date);
|
|
203
182
|
}
|
|
204
|
-
}
|
|
205
|
-
);
|
|
183
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { _ } from '@neovici/cosmoz-i18next';
|
|
2
|
+
import { PolymerElement } from '@polymer/polymer';
|
|
3
|
+
import { html } from 'lit-html';
|
|
4
|
+
import { ifDefined } from 'lit-html/directives/if-defined';
|
|
5
|
+
import { dateInputMixin } from './cosmoz-omnitable-date-input-mixin';
|
|
6
|
+
import { polymerHauntedRender } from './polymer-haunted-render-mixin';
|
|
7
|
+
|
|
8
|
+
class DateRangeInput extends dateInputMixin(polymerHauntedRender(PolymerElement)) {
|
|
9
|
+
// eslint-disable-next-line max-lines-per-function
|
|
10
|
+
render() {
|
|
11
|
+
return html`
|
|
12
|
+
<style>
|
|
13
|
+
paper-dropdown-menu {
|
|
14
|
+
--iron-icon-width: 0;
|
|
15
|
+
display: block;
|
|
16
|
+
}
|
|
17
|
+
</style>
|
|
18
|
+
<cosmoz-clear-button @click=${ () => this.resetFilter() } ?visible=${ this.hasFilter() }></cosmoz-clear-button>
|
|
19
|
+
<paper-dropdown-menu
|
|
20
|
+
label=${ this.title }
|
|
21
|
+
placeholder=${ ifDefined(this._filterText) }
|
|
22
|
+
class="external-values-${ this.externalValues }"
|
|
23
|
+
title=${ this._tooltip }
|
|
24
|
+
horizontal-align="right"
|
|
25
|
+
?opened=${ this.headerFocused }
|
|
26
|
+
@opened-changed=${ event => {
|
|
27
|
+
// TODO: check ots integration
|
|
28
|
+
this.headerFocused = event.detail.value;
|
|
29
|
+
} }>
|
|
30
|
+
<div class="dropdown-content" slot="dropdown-content" style="padding: 15px; min-width: 100px;">
|
|
31
|
+
<h3 style="margin: 0;">${ this.title }</h3>
|
|
32
|
+
<paper-input
|
|
33
|
+
type="date"
|
|
34
|
+
label=${ _('From date') }
|
|
35
|
+
min=${ this._toInputString(this._limit.fromMin) }
|
|
36
|
+
max=${ this._toInputString(this._limit.fromMax) }
|
|
37
|
+
.value=${ this._filterInput?.min }
|
|
38
|
+
@value-changed=${ event => this.set('_filterInput.min', event.detail.value) }
|
|
39
|
+
></paper-input>
|
|
40
|
+
<paper-input
|
|
41
|
+
type="date"
|
|
42
|
+
label=${ _('Until date') }
|
|
43
|
+
min=${ this._toInputString(this._limit.toMin) }
|
|
44
|
+
max=${ this._toInputString(this._limit.toMax) }
|
|
45
|
+
.value=${ this._filterInput?.max }
|
|
46
|
+
@value-changed=${ event => this.set('_filterInput.max', event.detail.value) }
|
|
47
|
+
></paper-input>
|
|
48
|
+
</div>
|
|
49
|
+
</paper-dropdown-menu>
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_fromInputString(value, property) {
|
|
54
|
+
const date = this.toDate(value);
|
|
55
|
+
if (date == null) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (property === 'min') {
|
|
59
|
+
date.setHours(0, 0, 0, 0);
|
|
60
|
+
}
|
|
61
|
+
if (property === 'max') {
|
|
62
|
+
date.setHours(23, 59, 59);
|
|
63
|
+
}
|
|
64
|
+
return date;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_filterInputChanged(change, autoupdate) {
|
|
68
|
+
const path = change.path.split('.')[1],
|
|
69
|
+
value = path && change.value;
|
|
70
|
+
|
|
71
|
+
// don't trigger change when date input begins with 0; Year (starting from 0000) was limited before the needed value was typed.
|
|
72
|
+
if (value && value.match(/^0+/u)) {
|
|
73
|
+
this._limitInputDebouncer.cancel();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
super._filterInputChanged(change, autoupdate);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
customElements.define('cosmoz-omnitable-date-range-input', DateRangeInput);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { _ } from '@neovici/cosmoz-i18next';
|
|
2
|
+
import { PolymerElement } from '@polymer/polymer';
|
|
3
|
+
import { html } from 'lit-html';
|
|
4
|
+
import { ifDefined } from 'lit-html/directives/if-defined';
|
|
5
|
+
import { dateInputMixin } from './cosmoz-omnitable-date-input-mixin';
|
|
6
|
+
import { polymerHauntedRender } from './polymer-haunted-render-mixin';
|
|
7
|
+
|
|
8
|
+
class DatetimeRangeInput extends dateInputMixin(polymerHauntedRender(PolymerElement)) {
|
|
9
|
+
// eslint-disable-next-line max-lines-per-function
|
|
10
|
+
render() {
|
|
11
|
+
return html`
|
|
12
|
+
<style>
|
|
13
|
+
paper-dropdown-menu {
|
|
14
|
+
--iron-icon-width: 0;
|
|
15
|
+
display: block;
|
|
16
|
+
}
|
|
17
|
+
</style>
|
|
18
|
+
|
|
19
|
+
<cosmoz-clear-button @click=${ () => this.resetFilter() } ?visible=${ this.hasFilter() }></cosmoz-clear-button>
|
|
20
|
+
<paper-dropdown-menu
|
|
21
|
+
label=${ this.title }
|
|
22
|
+
placeholder=${ ifDefined(this._filterText) }
|
|
23
|
+
class="external-values-${ this.externalValues }"
|
|
24
|
+
title=${ this._tooltip }
|
|
25
|
+
horizontal-align="right"
|
|
26
|
+
?opened=${ this.headerFocused }
|
|
27
|
+
@opened-changed=${ event => this.set('headerFocused', event.detail.value) }>
|
|
28
|
+
<div class="dropdown-content" slot="dropdown-content" style="padding: 15px; min-width: 100px;">
|
|
29
|
+
<h3 style="margin: 0;">${ this.title }</h3>
|
|
30
|
+
<cosmoz-datetime-input
|
|
31
|
+
date-label=${ _('From date') }
|
|
32
|
+
time-label=${ _('From time') }
|
|
33
|
+
min=${ this._toInputString(this._limit.fromMin) }
|
|
34
|
+
max=${ this._toInputString(this._limit.fromMax) }
|
|
35
|
+
step=${ this.filterStep }
|
|
36
|
+
.value=${ this._filterInput?.min }
|
|
37
|
+
@value-changed=${ event => this.set('_filterInput.min', event.detail.value) }
|
|
38
|
+
></cosmoz-datetime-input>
|
|
39
|
+
<cosmoz-datetime-input
|
|
40
|
+
date-label=${ _('To date') }
|
|
41
|
+
time-label=${ _('To time') }
|
|
42
|
+
min=${ this._toInputString(this._limit.toMin) }
|
|
43
|
+
max=${ this._toInputString(this._limit.toMax) }
|
|
44
|
+
step=${ this.filterStep }
|
|
45
|
+
.value=${ this._filterInput?.max }
|
|
46
|
+
@value-changed=${ event => this.set('_filterInput.max', event.detail.value) }
|
|
47
|
+
></cosmoz-datetime-input>
|
|
48
|
+
</div>
|
|
49
|
+
</paper-dropdown-menu>
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_toInputString(value) {
|
|
54
|
+
const date = this.toValue(value);
|
|
55
|
+
if (date == null) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
return this._toLocalISOString(date).slice(0, 19);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// OVERRIDES
|
|
62
|
+
_computeFormatter(locale) {
|
|
63
|
+
const timeFormatOption = {
|
|
64
|
+
year: 'numeric',
|
|
65
|
+
month: 'numeric',
|
|
66
|
+
day: 'numeric',
|
|
67
|
+
hour: 'numeric',
|
|
68
|
+
minute: 'numeric'
|
|
69
|
+
};
|
|
70
|
+
return new Intl.DateTimeFormat(locale || undefined, timeFormatOption);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
customElements.define('cosmoz-omnitable-datetime-range-input', DatetimeRangeInput);
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { _ } from '@neovici/cosmoz-i18next';
|
|
2
|
+
import { PolymerElement } from '@polymer/polymer';
|
|
3
|
+
import { html } from 'lit-html';
|
|
4
|
+
import { ifDefined } from 'lit-html/directives/if-defined';
|
|
5
|
+
import { rangeInputMixin } from './cosmoz-omnitable-range-input-mixin';
|
|
6
|
+
import { polymerHauntedRender } from './polymer-haunted-render-mixin';
|
|
7
|
+
|
|
8
|
+
class NumberRangeInput extends rangeInputMixin(polymerHauntedRender(PolymerElement)) {
|
|
9
|
+
static get properties() {
|
|
10
|
+
return {
|
|
11
|
+
maximumFractionDigits: { type: Number, value: null },
|
|
12
|
+
minimumFractionDigits: { type: Number, value: null }, // browser default 0 for numbers, currency-specific or 2 for currency
|
|
13
|
+
formatter: {
|
|
14
|
+
type: Object,
|
|
15
|
+
computed: '_computeFormatter(locale, minimumFractionDigits, maximumFractionDigits)'
|
|
16
|
+
},
|
|
17
|
+
// trigger filter updates manually
|
|
18
|
+
autoupdate: {
|
|
19
|
+
type: String,
|
|
20
|
+
value: false
|
|
21
|
+
},
|
|
22
|
+
_filterText: {
|
|
23
|
+
type: String,
|
|
24
|
+
computed: '_computeFilterText(filter.*, formatter)'
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// eslint-disable-next-line max-lines-per-function
|
|
30
|
+
render() {
|
|
31
|
+
const onOpenedChanged = event => {
|
|
32
|
+
this.headerFocused = event.detail.value;
|
|
33
|
+
this._onDropdownOpenedChanged(event);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return html`
|
|
37
|
+
<style>
|
|
38
|
+
paper-dropdown-menu {
|
|
39
|
+
--iron-icon-width: 0;
|
|
40
|
+
display: block;
|
|
41
|
+
}
|
|
42
|
+
</style>
|
|
43
|
+
|
|
44
|
+
<cosmoz-clear-button @click=${ () => this.resetFilter() } ?visible=${ this.hasFilter() }></cosmoz-clear-button>
|
|
45
|
+
<paper-dropdown-menu
|
|
46
|
+
label=${ this.title }
|
|
47
|
+
placeholder=${ ifDefined(this._filterText) }
|
|
48
|
+
class="external-values-${ this.externalValues }"
|
|
49
|
+
title=${ this._tooltip }
|
|
50
|
+
horizontal-align="right"
|
|
51
|
+
?opened=${ this.headerFocused }
|
|
52
|
+
@opened-changed=${ onOpenedChanged }
|
|
53
|
+
>
|
|
54
|
+
<div class="dropdown-content" slot="dropdown-content" style="padding: 15px; min-width: 100px;">
|
|
55
|
+
<h3 style="margin: 0;">${ this.title }</h3>
|
|
56
|
+
<paper-input
|
|
57
|
+
class=${ this._fromClasses }
|
|
58
|
+
type="number"
|
|
59
|
+
label=${ _('From') }
|
|
60
|
+
.value=${ this._filterInput?.min }
|
|
61
|
+
@value-changed=${ event => {
|
|
62
|
+
this.set('_filterInput.min', event.detail.value);
|
|
63
|
+
} }
|
|
64
|
+
@input=${ event => this.onBadInputFloatLabel(event) }
|
|
65
|
+
@blur=${ event => this._onBlur(event) }
|
|
66
|
+
@keydown=${ event => this._onKeyDown(event) }
|
|
67
|
+
min=${ this._toInputString(this._limit.fromMin) }
|
|
68
|
+
max=${ this._toInputString(this._limit.fromMax) }
|
|
69
|
+
></paper-input>
|
|
70
|
+
<paper-input
|
|
71
|
+
class=${ this._toClasses }
|
|
72
|
+
type="number"
|
|
73
|
+
label=${ _('To') }
|
|
74
|
+
.value=${ this._filterInput?.max }
|
|
75
|
+
@value-changed=${ event => {
|
|
76
|
+
this.set('_filterInput.max', event.detail.value);
|
|
77
|
+
} }
|
|
78
|
+
@input=${ event => this.onBadInputFloatLabel(event) }
|
|
79
|
+
@blur=${ event => this._onBlur(event) }
|
|
80
|
+
@keydown=${ event => this._onKeyDown(event) }
|
|
81
|
+
min=${ this._toInputString(this._limit.toMin) }
|
|
82
|
+
max=${ this._toInputString(this._limit.toMax) }
|
|
83
|
+
></paper-input>
|
|
84
|
+
</div>
|
|
85
|
+
</paper-dropdown-menu>
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
_computeFormatter(locale, minimumFractionDigits, maximumFractionDigits) {
|
|
90
|
+
const options = {
|
|
91
|
+
localeMatcher: 'best fit' // chrome expects this when using custom options
|
|
92
|
+
};
|
|
93
|
+
if (minimumFractionDigits !== null) {
|
|
94
|
+
options.minimumFractionDigits = minimumFractionDigits;
|
|
95
|
+
}
|
|
96
|
+
if (maximumFractionDigits !== null) {
|
|
97
|
+
options.maximumFractionDigits = maximumFractionDigits;
|
|
98
|
+
}
|
|
99
|
+
return new Intl.NumberFormat(locale || undefined, options);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check if label should float based on validity
|
|
104
|
+
*
|
|
105
|
+
* Number inputs can have allowed characters that aren't numbers (-,e) and won't
|
|
106
|
+
* trigger a value change and thus not float the label.
|
|
107
|
+
* However, the validity will report badInput so we can trigger a label float by
|
|
108
|
+
* setting it to something truthy but still not visible.
|
|
109
|
+
* Fixed in paper-input 3.x
|
|
110
|
+
*
|
|
111
|
+
* @param {Event} event KeyboardEvent
|
|
112
|
+
* @returns {void}
|
|
113
|
+
*/
|
|
114
|
+
onBadInputFloatLabel(event) {
|
|
115
|
+
const paperInput = event.currentTarget;
|
|
116
|
+
if (paperInput == null || paperInput.tagName !== 'PAPER-INPUT') {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
paperInput.placeholder = paperInput.$.nativeInput.validity.badInput ? ' ' : '';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get the comparable value of an item.
|
|
124
|
+
*
|
|
125
|
+
* @param {Object} item Item to be processed
|
|
126
|
+
* @param {String} valuePath Property path
|
|
127
|
+
* @returns {Number|void} Valid value or void
|
|
128
|
+
*/
|
|
129
|
+
getComparableValue(item, valuePath) {
|
|
130
|
+
if (item == null) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
let value = item;
|
|
134
|
+
if (valuePath != null) {
|
|
135
|
+
value = this.get(valuePath, item);
|
|
136
|
+
}
|
|
137
|
+
value = this.toValue(value);
|
|
138
|
+
if (value == null) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const decimals = this.maximumFractionDigits;
|
|
143
|
+
if (decimals !== null) {
|
|
144
|
+
return this.toValue(value.toFixed(decimals));
|
|
145
|
+
}
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
renderValue(value, formatter = this.formatter) {
|
|
150
|
+
const number = this.toNumber(value);
|
|
151
|
+
if (number == null) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
return formatter.format(number);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
customElements.define('cosmoz-omnitable-number-range-input', NumberRangeInput);
|