@ptsecurity/mosaic 15.9.3 → 15.9.5

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.
@@ -2,17 +2,17 @@ import * as i3$1 from '@angular/cdk/a11y';
2
2
  import { A11yModule } from '@angular/cdk/a11y';
3
3
  import { CommonModule } from '@angular/common';
4
4
  import * as i0 from '@angular/core';
5
- import { InjectionToken, Directive, Optional, Self, Attribute, Input, Inject, forwardRef, Component, ChangeDetectionStrategy, ViewEncapsulation, NgModule } from '@angular/core';
5
+ import { InjectionToken, forwardRef, EventEmitter, Directive, Attribute, Input, Optional, Self, Inject, Component, ChangeDetectionStrategy, ViewEncapsulation, NgModule } from '@angular/core';
6
6
  import * as i1 from '@angular/forms';
7
- import { NG_VALIDATORS, Validators, FormsModule } from '@angular/forms';
7
+ import { NG_VALUE_ACCESSOR, NG_VALIDATORS, Validators, FormsModule } from '@angular/forms';
8
8
  import * as i3 from '@ptsecurity/mosaic/core';
9
9
  import { mixinErrorState, PopUpTriggers, McCommonModule } from '@ptsecurity/mosaic/core';
10
10
  import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
11
11
  import { getSupportedInputTypes } from '@angular/cdk/platform';
12
12
  import * as i4 from '@ptsecurity/mosaic/form-field';
13
13
  import { McFormFieldControl } from '@ptsecurity/mosaic/form-field';
14
- import { Subject } from 'rxjs';
15
- import { A, C, V, X, Z, F1, F12, ZERO, NINE, NUMPAD_ZERO, NUMPAD_NINE, NUMPAD_MINUS, DASH, FF_MINUS, DELETE, BACKSPACE, TAB, ESCAPE, ENTER, LEFT_ARROW, RIGHT_ARROW, HOME, END, UP_ARROW, DOWN_ARROW } from '@ptsecurity/cdk/keycodes';
14
+ import { Subject, Subscription } from 'rxjs';
15
+ import { A, C, V, X, Z, NUMPAD_MINUS, DASH, FF_MINUS, DELETE, BACKSPACE, TAB, ESCAPE, ENTER, LEFT_ARROW, RIGHT_ARROW, HOME, END, isFunctionKey, isNumberKey, isNumpadKey, UP_ARROW, DOWN_ARROW } from '@ptsecurity/cdk/keycodes';
16
16
  import { McTooltipTrigger, MC_TOOLTIP_SCROLL_STRATEGY } from '@ptsecurity/mosaic/tooltip';
17
17
  import * as i1$1 from '@angular/cdk/overlay';
18
18
  import * as i2 from '@angular/cdk/bidi';
@@ -25,6 +25,7 @@ const MC_INPUT_VALUE_ACCESSOR = new InjectionToken('MC_INPUT_VALUE_ACCESSOR');
25
25
 
26
26
  const BIG_STEP = 10;
27
27
  const SMALL_STEP = 1;
28
+ // TODO: подставлять локализованный сплиттер
28
29
  function normalizeSplitter(value) {
29
30
  return value ? value.replace(/,/g, '.') : value;
30
31
  }
@@ -48,16 +49,33 @@ function add(value1, value2) {
48
49
  const precision = Math.max(getPrecision(value1), getPrecision(value2));
49
50
  return (value1 * precision + value2 * precision) / precision;
50
51
  }
52
+ const MC_NUMBER_INPUT_VALUE_ACCESSOR = {
53
+ provide: NG_VALUE_ACCESSOR,
54
+ useExisting: forwardRef(() => McNumberInput),
55
+ multi: true
56
+ };
51
57
  class McNumberInput {
52
- constructor(elementRef, ngControl, step, bigStep, min, max) {
58
+ constructor(elementRef, renderer, step, bigStep, min, max) {
53
59
  this.elementRef = elementRef;
54
- this.ngControl = ngControl;
55
- this.focused = false;
60
+ this.renderer = renderer;
61
+ /** Emits when the value changes (either due to user input or programmatic change). */
62
+ this.valueChange = new EventEmitter();
63
+ /** Emits when the disabled state has changed */
64
+ this.disabledChange = new EventEmitter();
56
65
  this.stateChanges = new Subject();
66
+ this.controlType = 'input-number';
67
+ this._disabled = false;
68
+ this.focused = false;
69
+ this.localeSubscription = Subscription.EMPTY;
70
+ // tslint:disable-next-line:no-empty
71
+ this.onTouched = () => { };
72
+ // tslint:disable-next-line:no-empty
73
+ this.cvaOnChange = () => { };
57
74
  this.step = isDigit(step) ? parseFloat(step) : SMALL_STEP;
58
75
  this.bigStep = isDigit(bigStep) ? parseFloat(bigStep) : BIG_STEP;
59
76
  this.min = isDigit(min) ? parseFloat(min) : -Infinity;
60
77
  this.max = isDigit(max) ? parseFloat(max) : Infinity;
78
+ setTimeout(() => this.nativeElement.type = 'text', 0);
61
79
  if ('valueAsNumber' in this.nativeElement) {
62
80
  Object.defineProperty(Object.getPrototypeOf(this.nativeElement), 'valueAsNumber', {
63
81
  // tslint:disable-next-line:no-reserved-keywords
@@ -68,9 +86,71 @@ class McNumberInput {
68
86
  });
69
87
  }
70
88
  }
89
+ get value() {
90
+ return this._value;
91
+ }
92
+ set value(value) {
93
+ const oldValue = this.value;
94
+ this._value = value;
95
+ if (oldValue !== value) {
96
+ this.setViewValue(this.formatNumber(value));
97
+ this.valueChange.emit(value);
98
+ }
99
+ }
100
+ get disabled() {
101
+ return this._disabled;
102
+ }
103
+ set disabled(value) {
104
+ const newValue = coerceBooleanProperty(value);
105
+ const element = this.nativeElement;
106
+ if (this._disabled !== newValue) {
107
+ this._disabled = newValue;
108
+ this.disabledChange.emit(newValue);
109
+ }
110
+ // We need to null check the `blur` method, because it's undefined during SSR.
111
+ if (newValue && element.blur) {
112
+ // Normally, native input elements automatically blur if they turn disabled. This behavior
113
+ // is problematic, because it would mean that it triggers another change detection cycle,
114
+ // which then causes a changed after checked error if the input element was focused before.
115
+ element.blur();
116
+ }
117
+ }
71
118
  get nativeElement() {
72
119
  return this.elementRef.nativeElement;
73
120
  }
121
+ get viewValue() {
122
+ return this.nativeElement.value;
123
+ }
124
+ get ngControl() {
125
+ return this.control;
126
+ }
127
+ ngOnDestroy() {
128
+ this.localeSubscription.unsubscribe();
129
+ this.valueChange.complete();
130
+ this.disabledChange.complete();
131
+ }
132
+ onContainerClick() {
133
+ this.focus();
134
+ }
135
+ focus() {
136
+ this.nativeElement.focus();
137
+ }
138
+ // Implemented as part of ControlValueAccessor.
139
+ writeValue(value) {
140
+ this.value = value;
141
+ }
142
+ // Implemented as part of ControlValueAccessor.
143
+ registerOnChange(fn) {
144
+ this.cvaOnChange = fn;
145
+ }
146
+ // Implemented as part of ControlValueAccessor.
147
+ registerOnTouched(fn) {
148
+ this.onTouched = fn;
149
+ }
150
+ // Implemented as part of ControlValueAccessor.
151
+ setDisabledState(isDisabled) {
152
+ this.disabled = isDisabled;
153
+ }
74
154
  focusChanged(isFocused) {
75
155
  if (isFocused !== this.focused) {
76
156
  this.focused = isFocused;
@@ -78,7 +158,6 @@ class McNumberInput {
78
158
  }
79
159
  }
80
160
  onKeyDown(event) {
81
- var _a;
82
161
  // tslint:disable-next-line:deprecation
83
162
  const keyCode = event.keyCode;
84
163
  const isCtrlA = (e) => e.keyCode === A && (e.ctrlKey || e.metaKey);
@@ -86,32 +165,31 @@ class McNumberInput {
86
165
  const isCtrlV = (e) => e.keyCode === V && (e.ctrlKey || e.metaKey);
87
166
  const isCtrlX = (e) => e.keyCode === X && (e.ctrlKey || e.metaKey);
88
167
  const isCtrlZ = (e) => e.keyCode === Z && (e.ctrlKey || e.metaKey);
89
- const isFKey = (e) => e.keyCode >= F1 && e.keyCode <= F12;
90
- const isNumber = (e) => (e.keyCode >= ZERO && e.keyCode <= NINE) ||
91
- (e.keyCode >= NUMPAD_ZERO && e.keyCode <= NUMPAD_NINE);
92
168
  const isPeriod = (e) => e.key === '.' || e.key === ',';
93
169
  const minuses = [NUMPAD_MINUS, DASH, FF_MINUS];
94
170
  const serviceKeys = [DELETE, BACKSPACE, TAB, ESCAPE, ENTER];
95
171
  const arrows = [LEFT_ARROW, RIGHT_ARROW];
96
172
  const allowedKeys = [HOME, END].concat(arrows).concat(serviceKeys).concat(minuses);
97
173
  if (minuses.includes(keyCode) &&
98
- (this.nativeElement.valueAsNumber || this.min >= 0 || ((_a = this.nativeElement.validity) === null || _a === void 0 ? void 0 : _a.badInput))) {
174
+ (this.viewValue.includes(event.key) || this.min >= 0)) {
99
175
  event.preventDefault();
100
176
  return;
101
177
  }
102
178
  if (allowedKeys.indexOf(keyCode) !== -1 ||
103
- isCtrlA(event) ||
104
- isCtrlC(event) ||
105
- isCtrlV(event) ||
106
- isCtrlX(event) ||
107
- isCtrlZ(event) ||
108
- isFKey(event) ||
109
- isPeriod(event)) {
179
+ [
180
+ isCtrlA,
181
+ isCtrlC,
182
+ isCtrlV,
183
+ isCtrlX,
184
+ isCtrlZ,
185
+ isFunctionKey,
186
+ isPeriod
187
+ ].some((fn) => fn(event))) {
110
188
  // let it happen, don't do anything
111
189
  return;
112
190
  }
113
191
  // Ensure that it is not a number and stop the keypress
114
- if (event.shiftKey || !isNumber(event)) {
192
+ if (event.shiftKey || !isNumberKey(event) && !isNumpadKey(event)) {
115
193
  event.preventDefault();
116
194
  // process steps
117
195
  const step = event.shiftKey ? this.bigStep : this.step;
@@ -123,49 +201,124 @@ class McNumberInput {
123
201
  }
124
202
  }
125
203
  }
204
+ onInput(event) {
205
+ var _a;
206
+ const currentValueLength = ((_a = this.formatNumber(this.value)) === null || _a === void 0 ? void 0 : _a.length) || 0;
207
+ setTimeout(() => {
208
+ const fromPaste = event.inputType === 'insertFromPaste';
209
+ let formattedValue;
210
+ if (fromPaste) {
211
+ formattedValue = this.formatNumber(this.valueFromPaste);
212
+ }
213
+ else {
214
+ /*this.viewValue is raw and should be reformatted to localized number */
215
+ formattedValue = this.formatViewValue();
216
+ const offsetWhenSeparatorAdded = 2;
217
+ Promise.resolve().then(() => {
218
+ if (Math.abs(this.viewValue.length - currentValueLength) === offsetWhenSeparatorAdded) {
219
+ const cursorPosition = Math.max(0, (this.nativeElement.selectionStart || 0) + Math.sign(this.viewValue.length - currentValueLength));
220
+ this.renderer.setProperty(this.nativeElement, 'selectionStart', cursorPosition);
221
+ this.renderer.setProperty(this.nativeElement, 'selectionEnd', cursorPosition);
222
+ }
223
+ });
224
+ }
225
+ this.setViewValue(formattedValue, !fromPaste);
226
+ this.viewToModelUpdate(formattedValue);
227
+ });
228
+ }
126
229
  onPaste(event) {
127
- if (!isDigit(normalizeSplitter(event.clipboardData.getData('text')))) {
230
+ var _a;
231
+ this.valueFromPaste = this.checkAndNormalizeLocalizedNumber((_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text'));
232
+ if (this.valueFromPaste === null) {
128
233
  event.preventDefault();
129
234
  }
130
235
  }
131
236
  stepUp(step) {
132
- this.elementRef.nativeElement.focus();
133
- const res = Math.max(Math.min(add(this.nativeElement.valueAsNumber || 0, step), this.max), this.min);
134
- this.nativeElement.value = res.toString();
135
- this.viewToModelUpdate(this.nativeElement.valueAsNumber);
237
+ this.nativeElement.focus();
238
+ const res = Math.max(Math.min(add(this.value || 0, step), this.max), this.min);
239
+ this.setViewValue(this.formatNumber(res));
240
+ this._value = res;
241
+ this.cvaOnChange(res);
242
+ this.valueChange.emit(res);
136
243
  }
137
244
  stepDown(step) {
138
- this.elementRef.nativeElement.focus();
139
- const res = Math.min(Math.max(add(this.nativeElement.valueAsNumber || 0, -step), this.min), this.max);
140
- this.nativeElement.value = res.toString();
141
- this.viewToModelUpdate(this.nativeElement.valueAsNumber);
245
+ this.nativeElement.focus();
246
+ const res = Math.min(Math.max(add(this.value || 0, -step), this.min), this.max);
247
+ this.setViewValue(this.formatNumber(res));
248
+ this._value = res;
249
+ this.cvaOnChange(res);
250
+ this.valueChange.emit(res);
251
+ }
252
+ setViewValue(value, savePosition = false) {
253
+ const cursorPosition = this.nativeElement.selectionStart;
254
+ this.renderer.setProperty(this.nativeElement, 'value', value);
255
+ if (savePosition) {
256
+ this.renderer.setProperty(this.nativeElement, 'selectionStart', cursorPosition);
257
+ this.renderer.setProperty(this.nativeElement, 'selectionEnd', cursorPosition);
258
+ }
142
259
  }
143
- viewToModelUpdate(value) {
144
- if (this.ngControl) {
145
- this.ngControl.control.setValue(value);
260
+ viewToModelUpdate(newValue) {
261
+ var _a;
262
+ const normalizedValue = newValue === null ? null : +this.normalizeNumber(newValue);
263
+ if (normalizedValue !== this.value) {
264
+ this._value = normalizedValue;
265
+ this.cvaOnChange(normalizedValue);
266
+ this.valueChange.emit(normalizedValue);
267
+ }
268
+ (_a = this.ngControl) === null || _a === void 0 ? void 0 : _a.updateValueAndValidity({ emitEvent: false });
269
+ }
270
+ formatViewValue() {
271
+ if (this.viewValue === null || this.viewValue === '' || Number.isNaN(+this.normalizeNumber(this.viewValue))) {
272
+ return null;
273
+ }
274
+ return this.viewValue;
275
+ }
276
+ formatNumber(value) {
277
+ if (value === null || value === undefined) {
278
+ return null;
146
279
  }
280
+ return value.toString();
281
+ }
282
+ /**
283
+ * Method that returns a string representation of a number without localized separators
284
+ */
285
+ normalizeNumber(value, _) {
286
+ if (value === null || value === undefined) {
287
+ return '';
288
+ }
289
+ return value.toString();
290
+ }
291
+ checkAndNormalizeLocalizedNumber(num) {
292
+ if (num === null || num === undefined) {
293
+ return null;
294
+ }
295
+ /* if some locale input config satisfies pasted number, try to normalise with selected locale config */
296
+ let numberOutput = null;
297
+ const normalized = +this.normalizeNumber(num);
298
+ if (!Number.isNaN(normalized)) {
299
+ numberOutput = normalized;
300
+ }
301
+ return numberOutput;
147
302
  }
148
303
  }
149
- /** @nocollapse */ McNumberInput.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: McNumberInput, deps: [{ token: i0.ElementRef }, { token: i1.NgControl, optional: true, self: true }, { token: 'step', attribute: true }, { token: 'big-step', attribute: true }, { token: 'min', attribute: true }, { token: 'max', attribute: true }], target: i0.ɵɵFactoryTarget.Directive });
150
- /** @nocollapse */ McNumberInput.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: McNumberInput, selector: "input[mcInput][type=\"number\"]", inputs: { bigStep: "bigStep", step: "step", min: "min", max: "max" }, host: { listeners: { "blur": "focusChanged(false)", "focus": "focusChanged(true)", "paste": "onPaste($event)", "keydown": "onKeyDown($event)" } }, exportAs: ["mcNumericalInput"], ngImport: i0 });
304
+ /** @nocollapse */ McNumberInput.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: McNumberInput, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: 'step', attribute: true }, { token: 'big-step', attribute: true }, { token: 'min', attribute: true }, { token: 'max', attribute: true }], target: i0.ɵɵFactoryTarget.Directive });
305
+ /** @nocollapse */ McNumberInput.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: McNumberInput, selector: "input[mcInput][type=\"number\"]", inputs: { bigStep: "bigStep", step: "step", min: "min", max: "max", value: "value", disabled: "disabled" }, host: { listeners: { "blur": "focusChanged(false)", "focus": "focusChanged(true)", "paste": "onPaste($event)", "keydown": "onKeyDown($event)", "input": "onInput($event)" } }, providers: [MC_NUMBER_INPUT_VALUE_ACCESSOR], exportAs: ["mcNumericalInput"], ngImport: i0 });
151
306
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: McNumberInput, decorators: [{
152
307
  type: Directive,
153
308
  args: [{
154
309
  selector: `input[mcInput][type="number"]`,
155
310
  exportAs: 'mcNumericalInput',
311
+ providers: [MC_NUMBER_INPUT_VALUE_ACCESSOR],
156
312
  host: {
157
313
  '(blur)': 'focusChanged(false)',
158
314
  '(focus)': 'focusChanged(true)',
159
315
  '(paste)': 'onPaste($event)',
160
- '(keydown)': 'onKeyDown($event)'
316
+ '(keydown)': 'onKeyDown($event)',
317
+ '(input)': 'onInput($event)'
161
318
  }
162
319
  }]
163
320
  }], ctorParameters: function () {
164
- return [{ type: i0.ElementRef }, { type: i1.NgControl, decorators: [{
165
- type: Optional
166
- }, {
167
- type: Self
168
- }] }, { type: undefined, decorators: [{
321
+ return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: undefined, decorators: [{
169
322
  type: Attribute,
170
323
  args: ['step']
171
324
  }] }, { type: undefined, decorators: [{
@@ -186,6 +339,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImpor
186
339
  type: Input
187
340
  }], max: [{
188
341
  type: Input
342
+ }], value: [{
343
+ type: Input
344
+ }], disabled: [{
345
+ type: Input
189
346
  }] } });
190
347
 
191
348
  const MC_INPUT_INVALID_TYPES = [
@@ -947,5 +1104,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImpor
947
1104
  * Generated bundle index. Do not edit.
948
1105
  */
949
1106
 
950
- export { BIG_STEP, MAX_VALIDATOR, MC_INPUT_VALUE_ACCESSOR, MIN_VALIDATOR, MaxValidator, McInput, McInputBase, McInputMixinBase, McInputModule, McInputMono, McInputPassword, McNumberInput, McPasswordToggle, MinValidator, SMALL_STEP, add, getPrecision, isDigit, isFloat, isInt, normalizeSplitter };
1107
+ export { BIG_STEP, MAX_VALIDATOR, MC_INPUT_VALUE_ACCESSOR, MC_NUMBER_INPUT_VALUE_ACCESSOR, MIN_VALIDATOR, MaxValidator, McInput, McInputBase, McInputMixinBase, McInputModule, McInputMono, McInputPassword, McNumberInput, McPasswordToggle, MinValidator, SMALL_STEP, add, getPrecision, isDigit, isFloat, isInt, normalizeSplitter };
951
1108
  //# sourceMappingURL=ptsecurity-mosaic-input.mjs.map