matcha-components 20.125.0 → 20.126.0

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { EventEmitter, Output, HostBinding, Input, Component, ContentChildren, ElementRef, Renderer2, Inject, ViewEncapsulation, ChangeDetectionStrategy, HostListener, ContentChild, Directive, forwardRef, InjectionToken, inject, Injectable, ViewChild, TemplateRef, Optional, NgModule, createComponent, PLATFORM_ID, Pipe } from '@angular/core';
2
+ import { EventEmitter, Output, HostBinding, Input, Component, ContentChildren, ElementRef, Renderer2, Inject, ViewEncapsulation, ChangeDetectionStrategy, HostListener, ContentChild, Directive, forwardRef, ViewChild, InjectionToken, inject, Injectable, TemplateRef, Optional, NgModule, createComponent, PLATFORM_ID, Pipe } from '@angular/core';
3
3
  import { animation, style, animate, trigger, transition, useAnimation, state, query, stagger, animateChild, sequence, group } from '@angular/animations';
4
4
  import { Subscription, Subject, BehaviorSubject, fromEvent, of } from 'rxjs';
5
5
  import { debounceTime, takeUntil, filter, startWith, map, distinctUntilChanged } from 'rxjs/operators';
@@ -3136,18 +3136,68 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImpor
3136
3136
 
3137
3137
  class MatchaDateComponent {
3138
3138
  constructor() {
3139
- this.placeholder = 'Selecione uma data';
3139
+ this.placeholder = '__/__/____';
3140
3140
  this.min = '';
3141
3141
  this.max = '';
3142
3142
  this.disabled = false;
3143
+ this.showNativePicker = true; // Mostrar botão para abrir datepicker nativo
3143
3144
  this.value = '';
3145
+ this.displayValue = '';
3144
3146
  this.isDisabled = false;
3145
3147
  // ControlValueAccessor methods
3146
3148
  this.onChange = (value) => { };
3147
3149
  this.onTouched = () => { };
3148
3150
  }
3149
3151
  writeValue(value) {
3150
- this.value = value || '';
3152
+ if (value) {
3153
+ // Se o valor vem como YYYY-MM-DD (do FormControl)
3154
+ if (this.isValidDateFormat(value, 'YYYY-MM-DD')) {
3155
+ this.value = value;
3156
+ this.displayValue = this.formatDateForDisplay(value);
3157
+ // Sincronizar com input nativo
3158
+ if (this.nativeDateInput?.nativeElement) {
3159
+ this.nativeDateInput.nativeElement.value = value;
3160
+ }
3161
+ // Atualizar o input de texto
3162
+ if (this.textInput?.nativeElement) {
3163
+ setTimeout(() => {
3164
+ if (this.textInput?.nativeElement) {
3165
+ this.textInput.nativeElement.value = this.displayValue;
3166
+ }
3167
+ }, 0);
3168
+ }
3169
+ }
3170
+ else if (this.isValidDateFormat(value, 'DD/MM/YYYY')) {
3171
+ // Se o valor já vem mascarado
3172
+ this.displayValue = value;
3173
+ const yyyyMMdd = this.convertToYYYYMMDD(value);
3174
+ this.value = yyyyMMdd;
3175
+ if (this.nativeDateInput?.nativeElement) {
3176
+ this.nativeDateInput.nativeElement.value = yyyyMMdd;
3177
+ }
3178
+ if (this.textInput?.nativeElement) {
3179
+ setTimeout(() => {
3180
+ if (this.textInput?.nativeElement) {
3181
+ this.textInput.nativeElement.value = this.displayValue;
3182
+ }
3183
+ }, 0);
3184
+ }
3185
+ }
3186
+ else {
3187
+ this.value = value;
3188
+ this.displayValue = value || '';
3189
+ }
3190
+ }
3191
+ else {
3192
+ this.value = '';
3193
+ this.displayValue = '';
3194
+ if (this.nativeDateInput?.nativeElement) {
3195
+ this.nativeDateInput.nativeElement.value = '';
3196
+ }
3197
+ if (this.textInput?.nativeElement) {
3198
+ this.textInput.nativeElement.value = '';
3199
+ }
3200
+ }
3151
3201
  }
3152
3202
  registerOnChange(fn) {
3153
3203
  this.onChange = fn;
@@ -3161,20 +3211,193 @@ class MatchaDateComponent {
3161
3211
  }
3162
3212
  onInputChange(event) {
3163
3213
  const input = event.target;
3164
- this.value = input.value;
3165
- this.onChange(this.value);
3214
+ let inputValue = input.value;
3215
+ // Aplicar máscara DD/MM/YYYY manualmente (permite zeros)
3216
+ inputValue = this.applyDateMask(inputValue);
3217
+ // Atualizar o valor do input se a máscara foi aplicada
3218
+ if (input.value !== inputValue) {
3219
+ input.value = inputValue;
3220
+ }
3221
+ this.displayValue = inputValue;
3222
+ // Apenas converter para YYYY-MM-DD se a data estiver completa e válida
3223
+ if (inputValue && inputValue.length === 10 && this.isValidDateFormat(inputValue, 'DD/MM/YYYY')) {
3224
+ const yyyyMMdd = this.convertToYYYYMMDD(inputValue);
3225
+ if (this.isValidDate(yyyyMMdd)) {
3226
+ this.value = yyyyMMdd;
3227
+ this.onChange(yyyyMMdd);
3228
+ // Sincronizar com input nativo
3229
+ if (this.nativeDateInput?.nativeElement) {
3230
+ this.nativeDateInput.nativeElement.value = yyyyMMdd;
3231
+ }
3232
+ }
3233
+ else {
3234
+ // Data inválida (ex: 31/02/2024) - não limpar, apenas não converter
3235
+ this.onChange(null);
3236
+ }
3237
+ }
3238
+ else if (!inputValue || inputValue.length === 0) {
3239
+ // Campo vazio
3240
+ this.value = '';
3241
+ this.onChange(null);
3242
+ if (this.nativeDateInput?.nativeElement) {
3243
+ this.nativeDateInput.nativeElement.value = '';
3244
+ }
3245
+ }
3246
+ else {
3247
+ // Data incompleta - emitir null para indicar que ainda não está completa
3248
+ this.onChange(null);
3249
+ }
3250
+ }
3251
+ onKeyDown(event) {
3252
+ // Permitir Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X, Backspace, Delete, Tab, Arrow keys
3253
+ const allowedKeys = ['Backspace', 'Delete', 'Tab', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'];
3254
+ const isCtrl = event.ctrlKey || event.metaKey;
3255
+ const isAllowedCtrl = isCtrl && ['a', 'c', 'v', 'x'].includes(event.key.toLowerCase());
3256
+ if (allowedKeys.includes(event.key) || isAllowedCtrl) {
3257
+ return; // Permitir
3258
+ }
3259
+ // Permitir apenas números e barras
3260
+ if (!/[0-9\/]/.test(event.key)) {
3261
+ event.preventDefault();
3262
+ }
3263
+ }
3264
+ applyDateMask(value) {
3265
+ // Remove tudo que não é número
3266
+ const numbers = value.replace(/\D/g, '');
3267
+ // Limita a 8 dígitos (DDMMYYYY)
3268
+ const limitedNumbers = numbers.slice(0, 8);
3269
+ // Aplica máscara DD/MM/YYYY
3270
+ if (limitedNumbers.length <= 2) {
3271
+ return limitedNumbers;
3272
+ }
3273
+ else if (limitedNumbers.length <= 4) {
3274
+ return `${limitedNumbers.slice(0, 2)}/${limitedNumbers.slice(2)}`;
3275
+ }
3276
+ else {
3277
+ return `${limitedNumbers.slice(0, 2)}/${limitedNumbers.slice(2, 4)}/${limitedNumbers.slice(4, 8)}`;
3278
+ }
3279
+ }
3280
+ onNativeDateChange(event) {
3281
+ const input = event.target;
3282
+ const value = input.value;
3283
+ if (value) {
3284
+ this.value = value;
3285
+ this.displayValue = this.formatDateForDisplay(value);
3286
+ // Sincronizar com input de texto
3287
+ if (this.textInput?.nativeElement) {
3288
+ this.textInput.nativeElement.value = this.displayValue;
3289
+ }
3290
+ this.onChange(value);
3291
+ }
3292
+ else {
3293
+ this.value = '';
3294
+ this.displayValue = '';
3295
+ if (this.textInput?.nativeElement) {
3296
+ this.textInput.nativeElement.value = '';
3297
+ }
3298
+ this.onChange(null);
3299
+ }
3166
3300
  }
3167
3301
  onBlur() {
3168
3302
  this.onTouched();
3303
+ // Obter o valor atual do input
3304
+ const currentInputValue = this.textInput?.nativeElement?.value || this.displayValue;
3305
+ // Se já temos um valor válido e a data digitada é a mesma, não fazer nada
3306
+ if (this.value && currentInputValue && currentInputValue.length === 10) {
3307
+ const yyyyMMdd = this.convertToYYYYMMDD(currentInputValue);
3308
+ if (this.value === yyyyMMdd) {
3309
+ return;
3310
+ }
3311
+ }
3312
+ // Apenas tentar converter se a data estiver completa
3313
+ // Não limpar nada - deixa o matcha-error mostrar erros de validação
3314
+ if (currentInputValue && currentInputValue.length === 10) {
3315
+ if (this.isValidDateFormat(currentInputValue, 'DD/MM/YYYY')) {
3316
+ const yyyyMMdd = this.convertToYYYYMMDD(currentInputValue);
3317
+ if (this.isValidDate(yyyyMMdd)) {
3318
+ // Data válida, atualizar valores
3319
+ this.value = yyyyMMdd;
3320
+ this.displayValue = currentInputValue;
3321
+ this.onChange(yyyyMMdd);
3322
+ // Sincronizar com input nativo
3323
+ if (this.nativeDateInput?.nativeElement) {
3324
+ this.nativeDateInput.nativeElement.value = yyyyMMdd;
3325
+ }
3326
+ }
3327
+ else {
3328
+ // Data inválida - não limpar, apenas não converter
3329
+ // O matcha-error vai mostrar o erro
3330
+ this.onChange(null);
3331
+ }
3332
+ }
3333
+ else {
3334
+ // Formato inválido - não limpar, apenas não converter
3335
+ this.onChange(null);
3336
+ }
3337
+ }
3338
+ else if (currentInputValue && currentInputValue.length > 0) {
3339
+ // Data incompleta - manter o que foi digitado
3340
+ this.displayValue = currentInputValue;
3341
+ this.onChange(null);
3342
+ }
3343
+ else {
3344
+ // Campo vazio
3345
+ this.displayValue = '';
3346
+ this.value = '';
3347
+ this.onChange(null);
3348
+ }
3349
+ }
3350
+ openNativeDatePicker() {
3351
+ if (this.nativeDateInput?.nativeElement && !this.disabled && !this.isDisabled) {
3352
+ // Verificar se showPicker está disponível (suporte moderno)
3353
+ if (typeof this.nativeDateInput.nativeElement.showPicker === 'function') {
3354
+ this.nativeDateInput.nativeElement.showPicker();
3355
+ }
3356
+ else {
3357
+ // Fallback: focar no input nativo
3358
+ this.nativeDateInput.nativeElement.focus();
3359
+ this.nativeDateInput.nativeElement.click();
3360
+ }
3361
+ }
3362
+ }
3363
+ isValidDateFormat(date, format) {
3364
+ if (format === 'DD/MM/YYYY') {
3365
+ return /^\d{2}\/\d{2}\/\d{4}$/.test(date);
3366
+ }
3367
+ else {
3368
+ return /^\d{4}-\d{2}-\d{2}$/.test(date);
3369
+ }
3370
+ }
3371
+ isValidDate(dateString) {
3372
+ if (!this.isValidDateFormat(dateString, 'YYYY-MM-DD')) {
3373
+ return false;
3374
+ }
3375
+ // Usar Date constructor com componentes separados para evitar problemas de fuso horário
3376
+ const [year, month, day] = dateString.split('-').map(Number);
3377
+ // Criar data usando componentes locais (não UTC)
3378
+ const date = new Date(year, month - 1, day);
3379
+ // Verificar se a data é válida e corresponde ao que foi digitado
3380
+ // Isso evita que 31/02/2024 seja aceito (seria convertido para 03/03/2024)
3381
+ return date.getFullYear() === year &&
3382
+ date.getMonth() + 1 === month &&
3383
+ date.getDate() === day;
3384
+ }
3385
+ convertToYYYYMMDD(ddMMyyyy) {
3386
+ const [day, month, year] = ddMMyyyy.split('/');
3387
+ return `${year}-${month}-${day}`;
3388
+ }
3389
+ formatDateForDisplay(yyyyMMdd) {
3390
+ const [year, month, day] = yyyyMMdd.split('-');
3391
+ return `${day}/${month}/${year}`;
3169
3392
  }
3170
3393
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MatchaDateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3171
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.0", type: MatchaDateComponent, isStandalone: false, selector: "matcha-date", inputs: { placeholder: "placeholder", min: "min", max: "max", disabled: "disabled" }, providers: [
3394
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.0", type: MatchaDateComponent, isStandalone: false, selector: "matcha-date", inputs: { placeholder: "placeholder", min: "min", max: "max", disabled: "disabled", showNativePicker: "showNativePicker" }, providers: [
3172
3395
  {
3173
3396
  provide: NG_VALUE_ACCESSOR,
3174
3397
  useExisting: forwardRef(() => MatchaDateComponent),
3175
3398
  multi: true
3176
3399
  }
3177
- ], ngImport: i0, template: "<input\n type=\"date\"\n class=\"matcha-date-input\"\n [value]=\"value\"\n [placeholder]=\"placeholder\"\n [min]=\"min\"\n [max]=\"max\"\n [disabled]=\"disabled || isDisabled\"\n (input)=\"onInputChange($event)\"\n (blur)=\"onBlur()\"\n/>\n\n", styles: [""], encapsulation: i0.ViewEncapsulation.None }); }
3400
+ ], viewQueries: [{ propertyName: "textInput", first: true, predicate: ["textInput"], descendants: true }, { propertyName: "nativeDateInput", first: true, predicate: ["nativeDateInput"], descendants: true }], ngImport: i0, template: "<div class=\"matcha-date-container\">\n <input\n #textInput\n type=\"text\"\n class=\"matcha-date-input\"\n [value]=\"displayValue\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled || isDisabled\"\n maxlength=\"10\"\n (input)=\"onInputChange($event)\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeyDown($event)\"\n />\n\n <!-- Input nativo de data (hidden) para usar o datepicker nativo -->\n <input\n #nativeDateInput\n type=\"date\"\n class=\"matcha-date-native-input\"\n [value]=\"value\"\n [min]=\"min\"\n [max]=\"max\"\n [disabled]=\"disabled || isDisabled\"\n (change)=\"onNativeDateChange($event)\"\n tabindex=\"-1\"\n />\n\n <!-- Bot\u00E3o para abrir datepicker nativo (opcional) -->\n <button\n *ngIf=\"showNativePicker && !disabled && !isDisabled\"\n type=\"button\"\n class=\"matcha-date-picker-button mr--12\"\n (click)=\"openNativeDatePicker()\"\n [disabled]=\"disabled || isDisabled\"\n aria-label=\"Abrir seletor de data\"\n tabindex=\"0\"\n >\n <span class=\"i-matcha-calendar color-grey\"></span>\n </button>\n</div>\n\n", styles: [".matcha-date-container{position:relative;display:flex;align-items:center;width:100%}.matcha-date-input{width:100%}.matcha-date-native-input{position:absolute;opacity:0;pointer-events:none;width:0;height:0;border:none;padding:0;margin:0}.matcha-date-picker-button{position:absolute;right:8px;background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:var(--matcha-color-primary, #333);z-index:1}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], encapsulation: i0.ViewEncapsulation.None }); }
3178
3401
  }
3179
3402
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MatchaDateComponent, decorators: [{
3180
3403
  type: Component,
@@ -3184,7 +3407,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImpor
3184
3407
  useExisting: forwardRef(() => MatchaDateComponent),
3185
3408
  multi: true
3186
3409
  }
3187
- ], template: "<input\n type=\"date\"\n class=\"matcha-date-input\"\n [value]=\"value\"\n [placeholder]=\"placeholder\"\n [min]=\"min\"\n [max]=\"max\"\n [disabled]=\"disabled || isDisabled\"\n (input)=\"onInputChange($event)\"\n (blur)=\"onBlur()\"\n/>\n\n" }]
3410
+ ], template: "<div class=\"matcha-date-container\">\n <input\n #textInput\n type=\"text\"\n class=\"matcha-date-input\"\n [value]=\"displayValue\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled || isDisabled\"\n maxlength=\"10\"\n (input)=\"onInputChange($event)\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeyDown($event)\"\n />\n\n <!-- Input nativo de data (hidden) para usar o datepicker nativo -->\n <input\n #nativeDateInput\n type=\"date\"\n class=\"matcha-date-native-input\"\n [value]=\"value\"\n [min]=\"min\"\n [max]=\"max\"\n [disabled]=\"disabled || isDisabled\"\n (change)=\"onNativeDateChange($event)\"\n tabindex=\"-1\"\n />\n\n <!-- Bot\u00E3o para abrir datepicker nativo (opcional) -->\n <button\n *ngIf=\"showNativePicker && !disabled && !isDisabled\"\n type=\"button\"\n class=\"matcha-date-picker-button mr--12\"\n (click)=\"openNativeDatePicker()\"\n [disabled]=\"disabled || isDisabled\"\n aria-label=\"Abrir seletor de data\"\n tabindex=\"0\"\n >\n <span class=\"i-matcha-calendar color-grey\"></span>\n </button>\n</div>\n\n", styles: [".matcha-date-container{position:relative;display:flex;align-items:center;width:100%}.matcha-date-input{width:100%}.matcha-date-native-input{position:absolute;opacity:0;pointer-events:none;width:0;height:0;border:none;padding:0;margin:0}.matcha-date-picker-button{position:absolute;right:8px;background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:var(--matcha-color-primary, #333);z-index:1}\n"] }]
3188
3411
  }], propDecorators: { placeholder: [{
3189
3412
  type: Input
3190
3413
  }], min: [{
@@ -3193,6 +3416,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImpor
3193
3416
  type: Input
3194
3417
  }], disabled: [{
3195
3418
  type: Input
3419
+ }], showNativePicker: [{
3420
+ type: Input
3421
+ }], textInput: [{
3422
+ type: ViewChild,
3423
+ args: ['textInput']
3424
+ }], nativeDateInput: [{
3425
+ type: ViewChild,
3426
+ args: ['nativeDateInput']
3196
3427
  }] } });
3197
3428
 
3198
3429
  class MatchaDateRangeComponent {
@@ -3270,14 +3501,14 @@ class MatchaDateRangeComponent {
3270
3501
  }
3271
3502
  // Validação principal: data inicial não pode ser posterior à data final
3272
3503
  if (startDate > endDate) {
3273
- // Adiciona erros quando a data inicial é posterior à final
3504
+ // Adiciona erros específicos quando a data inicial é posterior à final
3274
3505
  this.startDateControl.setErrors({
3275
3506
  ...this.startDateControl.errors,
3276
- ['dateRangeInvalid']: true
3507
+ ['startAfterEnd']: true
3277
3508
  });
3278
3509
  this.endDateControl.setErrors({
3279
3510
  ...this.endDateControl.errors,
3280
- ['dateRangeInvalid']: true
3511
+ ['endBeforeStart']: true
3281
3512
  });
3282
3513
  }
3283
3514
  else {
@@ -3290,13 +3521,11 @@ class MatchaDateRangeComponent {
3290
3521
  */
3291
3522
  clearDateRangeErrors() {
3292
3523
  if (this.startDateControl) {
3293
- this.removeSpecificError(this.startDateControl, 'dateRangeInvalid');
3294
3524
  this.removeSpecificError(this.startDateControl, 'invalidDateFormat');
3295
3525
  this.removeSpecificError(this.startDateControl, 'invalidDate');
3296
3526
  this.removeSpecificError(this.startDateControl, 'startAfterEnd');
3297
3527
  }
3298
3528
  if (this.endDateControl) {
3299
- this.removeSpecificError(this.endDateControl, 'dateRangeInvalid');
3300
3529
  this.removeSpecificError(this.endDateControl, 'invalidDateFormat');
3301
3530
  this.removeSpecificError(this.endDateControl, 'invalidDate');
3302
3531
  this.removeSpecificError(this.endDateControl, 'endBeforeStart');