1 | import { forwardRef, EventEmitter, Component, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, ChangeDetectorRef, Output, Input, ViewChild, NgModule } from '@angular/core';
|
2 | import { CommonModule } from '@angular/common';
|
3 | import { InputTextModule } from 'primeng/inputtext';
|
4 | import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
5 |
|
6 | const SPINNER_VALUE_ACCESSOR = {
|
7 | provide: NG_VALUE_ACCESSOR,
|
8 | useExisting: forwardRef(() => Spinner),
|
9 | multi: true
|
10 | };
|
11 | class Spinner {
|
12 | constructor(el, cd) {
|
13 | this.el = el;
|
14 | this.cd = cd;
|
15 | this.onChange = new EventEmitter();
|
16 | this.onFocus = new EventEmitter();
|
17 | this.onBlur = new EventEmitter();
|
18 | this._step = 1;
|
19 | this.onModelChange = () => { };
|
20 | this.onModelTouched = () => { };
|
21 | this.keyPattern = /[0-9\+\-]/;
|
22 | this.negativeSeparator = '-';
|
23 | }
|
24 | get step() {
|
25 | return this._step;
|
26 | }
|
27 | set step(val) {
|
28 | this._step = val;
|
29 | if (this._step != null) {
|
30 | let tokens = this.step.toString().split(/[,]|[.]/);
|
31 | this.calculatedPrecision = tokens[1] ? tokens[1].length : undefined;
|
32 | }
|
33 | }
|
34 | ngOnInit() {
|
35 | if (this.formatInput) {
|
36 | this.localeDecimalSeparator = (1.1).toLocaleString().substring(1, 2);
|
37 | this.localeThousandSeparator = (1000).toLocaleString().substring(1, 2);
|
38 | this.thousandRegExp = new RegExp(`[${this.thousandSeparator || this.localeThousandSeparator}]`, 'gim');
|
39 | if (this.decimalSeparator && this.thousandSeparator && this.decimalSeparator === this.thousandSeparator) {
|
40 | console.warn("thousandSeparator and decimalSeparator cannot have the same value.");
|
41 | }
|
42 | }
|
43 | }
|
44 | repeat(event, interval, dir) {
|
45 | let i = interval || 500;
|
46 | this.clearTimer();
|
47 | this.timer = setTimeout(() => {
|
48 | this.repeat(event, 40, dir);
|
49 | }, i);
|
50 | this.spin(event, dir);
|
51 | }
|
52 | spin(event, dir) {
|
53 | let step = this.step * dir;
|
54 | let currentValue;
|
55 | let precision = this.getPrecision();
|
56 | if (this.value)
|
57 | currentValue = (typeof this.value === 'string') ? this.parseValue(this.value) : this.value;
|
58 | else
|
59 | currentValue = 0;
|
60 | if (precision)
|
61 | this.value = parseFloat(this.toFixed(currentValue + step, precision));
|
62 | else
|
63 | this.value = currentValue + step;
|
64 | if (this.maxlength !== undefined && this.value.toString().length > this.maxlength) {
|
65 | this.value = currentValue;
|
66 | }
|
67 | if (this.min !== undefined && this.value < this.min) {
|
68 | this.value = this.min;
|
69 | }
|
70 | if (this.max !== undefined && this.value > this.max) {
|
71 | this.value = this.max;
|
72 | }
|
73 | this.formatValue();
|
74 | this.onModelChange(this.value);
|
75 | this.onChange.emit(event);
|
76 | }
|
77 | getPrecision() {
|
78 | return this.precision === undefined ? this.calculatedPrecision : this.precision;
|
79 | }
|
80 | toFixed(value, precision) {
|
81 | let power = Math.pow(10, precision || 0);
|
82 | return String(Math.round(value * power) / power);
|
83 | }
|
84 | onUpButtonMousedown(event) {
|
85 | if (!this.disabled) {
|
86 | this.inputfieldViewChild.nativeElement.focus();
|
87 | this.repeat(event, null, 1);
|
88 | this.updateFilledState();
|
89 | event.preventDefault();
|
90 | }
|
91 | }
|
92 | onUpButtonMouseup(event) {
|
93 | if (!this.disabled) {
|
94 | this.clearTimer();
|
95 | }
|
96 | }
|
97 | onUpButtonMouseleave(event) {
|
98 | if (!this.disabled) {
|
99 | this.clearTimer();
|
100 | }
|
101 | }
|
102 | onDownButtonMousedown(event) {
|
103 | if (!this.disabled) {
|
104 | this.inputfieldViewChild.nativeElement.focus();
|
105 | this.repeat(event, null, -1);
|
106 | this.updateFilledState();
|
107 | event.preventDefault();
|
108 | }
|
109 | }
|
110 | onDownButtonMouseup(event) {
|
111 | if (!this.disabled) {
|
112 | this.clearTimer();
|
113 | }
|
114 | }
|
115 | onDownButtonMouseleave(event) {
|
116 | if (!this.disabled) {
|
117 | this.clearTimer();
|
118 | }
|
119 | }
|
120 | onInputKeydown(event) {
|
121 | if (event.which == 38) {
|
122 | this.spin(event, 1);
|
123 | event.preventDefault();
|
124 | }
|
125 | else if (event.which == 40) {
|
126 | this.spin(event, -1);
|
127 | event.preventDefault();
|
128 | }
|
129 | }
|
130 | onInputChange(event) {
|
131 | this.onChange.emit(event);
|
132 | }
|
133 | onInput(event) {
|
134 | this.value = this.parseValue(event.target.value);
|
135 | this.onModelChange(this.value);
|
136 | this.updateFilledState();
|
137 | }
|
138 | onInputBlur(event) {
|
139 | this.focus = false;
|
140 | this.formatValue();
|
141 | this.onModelTouched();
|
142 | this.onBlur.emit(event);
|
143 | }
|
144 | onInputFocus(event) {
|
145 | this.focus = true;
|
146 | this.onFocus.emit(event);
|
147 | }
|
148 | parseValue(val) {
|
149 | let value;
|
150 | let precision = this.getPrecision();
|
151 | if (val.trim() === '') {
|
152 | value = null;
|
153 | }
|
154 | else {
|
155 | if (this.formatInput) {
|
156 | val = val.replace(this.thousandRegExp, '');
|
157 | }
|
158 | if (precision) {
|
159 | val = this.formatInput ? val.replace(this.decimalSeparator || this.localeDecimalSeparator, '.') : val.replace(',', '.');
|
160 | value = parseFloat(val);
|
161 | }
|
162 | else {
|
163 | value = parseInt(val, 10);
|
164 | }
|
165 | if (!isNaN(value)) {
|
166 | if (this.max !== null && value > this.max) {
|
167 | value = this.max;
|
168 | }
|
169 | if (this.min !== null && value < this.min) {
|
170 | value = this.min;
|
171 | }
|
172 | }
|
173 | else {
|
174 | value = null;
|
175 | }
|
176 | }
|
177 | return value;
|
178 | }
|
179 | formatValue() {
|
180 | let value = this.value;
|
181 | let precision = this.getPrecision();
|
182 | if (value != null) {
|
183 | if (this.formatInput) {
|
184 | value = value.toLocaleString(undefined, { maximumFractionDigits: 20 });
|
185 | if (this.decimalSeparator && this.thousandSeparator) {
|
186 | value = value.split(this.localeDecimalSeparator);
|
187 | if (precision && value[1]) {
|
188 | value[1] = (this.decimalSeparator || this.localeDecimalSeparator) + value[1];
|
189 | }
|
190 | if (this.thousandSeparator && value[0].length > 3) {
|
191 | value[0] = value[0].replace(new RegExp(`[${this.localeThousandSeparator}]`, 'gim'), this.thousandSeparator);
|
192 | }
|
193 | value = value.join('');
|
194 | }
|
195 | }
|
196 | this.formattedValue = value.toString();
|
197 | }
|
198 | else {
|
199 | this.formattedValue = null;
|
200 | }
|
201 | if (this.inputfieldViewChild && this.inputfieldViewChild.nativeElement) {
|
202 | this.inputfieldViewChild.nativeElement.value = this.formattedValue;
|
203 | }
|
204 | }
|
205 | clearTimer() {
|
206 | if (this.timer) {
|
207 | clearInterval(this.timer);
|
208 | }
|
209 | }
|
210 | writeValue(value) {
|
211 | this.value = value;
|
212 | this.formatValue();
|
213 | this.updateFilledState();
|
214 | this.cd.markForCheck();
|
215 | }
|
216 | registerOnChange(fn) {
|
217 | this.onModelChange = fn;
|
218 | }
|
219 | registerOnTouched(fn) {
|
220 | this.onModelTouched = fn;
|
221 | }
|
222 | setDisabledState(val) {
|
223 | this.disabled = val;
|
224 | this.cd.markForCheck();
|
225 | }
|
226 | updateFilledState() {
|
227 | this.filled = (this.value !== undefined && this.value != null);
|
228 | }
|
229 | }
|
230 | Spinner.decorators = [
|
231 | { type: Component, args: [{
|
232 | selector: 'p-spinner',
|
233 | template: `
|
234 | <span class="ui-spinner ui-widget ui-corner-all">
|
235 | <input #inputfield type="text" [attr.id]="inputId" [value]="formattedValue||null" [attr.name]="name" [attr.aria-valumin]="min" [attr.aria-valuemax]="max" [attr.aria-valuenow]="value" [attr.aria-labelledby]="ariaLabelledBy"
|
236 | [attr.size]="size" [attr.maxlength]="maxlength" [attr.tabindex]="tabindex" [attr.placeholder]="placeholder" [disabled]="disabled" [readonly]="readonly" [attr.required]="required"
|
237 | (keydown)="onInputKeydown($event)" (blur)="onInputBlur($event)" (input)="onInput($event)" (change)="onInputChange($event)" (focus)="onInputFocus($event)"
|
238 | [ngStyle]="inputStyle" [class]="inputStyleClass" [ngClass]="'ui-spinner-input ui-inputtext ui-widget ui-state-default ui-corner-all'">
|
239 | <button type="button" [ngClass]="{'ui-spinner-button ui-spinner-up ui-corner-tr ui-button ui-widget ui-state-default':true,'ui-state-disabled':disabled}" [disabled]="disabled||readonly" tabindex="-1" [attr.readonly]="readonly"
|
240 | (mouseleave)="onUpButtonMouseleave($event)" (mousedown)="onUpButtonMousedown($event)" (mouseup)="onUpButtonMouseup($event)">
|
241 | <span class="ui-spinner-button-icon pi pi-caret-up ui-clickable"></span>
|
242 | </button>
|
243 | <button type="button" [ngClass]="{'ui-spinner-button ui-spinner-down ui-corner-br ui-button ui-widget ui-state-default':true,'ui-state-disabled':disabled}" [disabled]="disabled||readonly" tabindex="-1" [attr.readonly]="readonly"
|
244 | (mouseleave)="onDownButtonMouseleave($event)" (mousedown)="onDownButtonMousedown($event)" (mouseup)="onDownButtonMouseup($event)">
|
245 | <span class="ui-spinner-button-icon pi pi-caret-down ui-clickable"></span>
|
246 | </button>
|
247 | </span>
|
248 | `,
|
249 | host: {
|
250 | '[class.ui-inputwrapper-filled]': 'filled',
|
251 | '[class.ui-inputwrapper-focus]': 'focus'
|
252 | },
|
253 | providers: [SPINNER_VALUE_ACCESSOR],
|
254 | changeDetection: ChangeDetectionStrategy.OnPush,
|
255 | encapsulation: ViewEncapsulation.None,
|
256 | styles: [".ui-spinner{display:inline-block;overflow:visible;padding:0;position:relative;vertical-align:middle}.ui-spinner-input{padding-right:1.5em;vertical-align:middle}.ui-spinner-button{cursor:default;display:block;height:50%;margin:0;overflow:hidden;padding:0;position:absolute;right:0;text-align:center;vertical-align:middle;width:1.5em}.ui-spinner .ui-spinner-button-icon{left:50%;margin-left:-.5em;margin-top:-.5em;position:absolute;top:50%;width:1em}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-fluid .ui-spinner{width:100%}.ui-fluid .ui-spinner .ui-spinner-input{padding-right:2em;width:100%}.ui-fluid .ui-spinner .ui-spinner-button{width:1.5em}.ui-fluid .ui-spinner .ui-spinner-button .ui-spinner-button-icon{left:.7em}"]
|
257 | },] }
|
258 | ];
|
259 | Spinner.ctorParameters = () => [
|
260 | { type: ElementRef },
|
261 | { type: ChangeDetectorRef }
|
262 | ];
|
263 | Spinner.propDecorators = {
|
264 | onChange: [{ type: Output }],
|
265 | onFocus: [{ type: Output }],
|
266 | onBlur: [{ type: Output }],
|
267 | min: [{ type: Input }],
|
268 | max: [{ type: Input }],
|
269 | maxlength: [{ type: Input }],
|
270 | size: [{ type: Input }],
|
271 | placeholder: [{ type: Input }],
|
272 | inputId: [{ type: Input }],
|
273 | disabled: [{ type: Input }],
|
274 | readonly: [{ type: Input }],
|
275 | tabindex: [{ type: Input }],
|
276 | required: [{ type: Input }],
|
277 | name: [{ type: Input }],
|
278 | ariaLabelledBy: [{ type: Input }],
|
279 | inputStyle: [{ type: Input }],
|
280 | inputStyleClass: [{ type: Input }],
|
281 | formatInput: [{ type: Input }],
|
282 | decimalSeparator: [{ type: Input }],
|
283 | thousandSeparator: [{ type: Input }],
|
284 | precision: [{ type: Input }],
|
285 | inputfieldViewChild: [{ type: ViewChild, args: ['inputfield',] }],
|
286 | step: [{ type: Input }]
|
287 | };
|
288 | class SpinnerModule {
|
289 | }
|
290 | SpinnerModule.decorators = [
|
291 | { type: NgModule, args: [{
|
292 | imports: [CommonModule, InputTextModule],
|
293 | exports: [Spinner],
|
294 | declarations: [Spinner]
|
295 | },] }
|
296 | ];
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 | export { SPINNER_VALUE_ACCESSOR, Spinner, SpinnerModule };
|
303 |
|