@angular/material 19.0.0-next.5 → 19.0.0-next.7

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/fesm2022/core.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Version, InjectionToken, inject, NgModule, LOCALE_ID, Injectable, Directive, Component, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, ANIMATION_MODULE_TYPE, NgZone, Injector, Input, booleanAttribute, ChangeDetectorRef, EventEmitter, Output, ViewChild } from '@angular/core';
2
+ import { Version, InjectionToken, inject, NgModule, LOCALE_ID, Injectable, Directive, Component, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, ANIMATION_MODULE_TYPE, NgZone, Injector, Input, booleanAttribute, ChangeDetectorRef, EventEmitter, isSignal, Output, ViewChild } from '@angular/core';
3
3
  import { HighContrastModeDetector, isFakeMousedownFromScreenReader, isFakeTouchstartFromScreenReader } from '@angular/cdk/a11y';
4
4
  import { BidiModule } from '@angular/cdk/bidi';
5
5
  import { Subject } from 'rxjs';
@@ -11,7 +11,7 @@ import { ENTER, SPACE, hasModifierKey } from '@angular/cdk/keycodes';
11
11
  import { DOCUMENT } from '@angular/common';
12
12
 
13
13
  /** Current version of Angular Material. */
14
- const VERSION = new Version('19.0.0-next.5');
14
+ const VERSION = new Version('19.0.0-next.7');
15
15
 
16
16
  /** @docs-private */
17
17
  class AnimationCurves {
@@ -99,6 +99,7 @@ const MAT_DATE_LOCALE = new InjectionToken('MAT_DATE_LOCALE', {
99
99
  function MAT_DATE_LOCALE_FACTORY() {
100
100
  return inject(LOCALE_ID);
101
101
  }
102
+ const NOT_IMPLEMENTED = 'Method not implemented';
102
103
  /** Adapts type `D` to be usable as a date by cdk-based components that work with dates. */
103
104
  class DateAdapter {
104
105
  constructor() {
@@ -106,6 +107,54 @@ class DateAdapter {
106
107
  /** A stream that emits when the locale changes. */
107
108
  this.localeChanges = this._localeChanges;
108
109
  }
110
+ /**
111
+ * Sets the time of one date to the time of another.
112
+ * @param target Date whose time will be set.
113
+ * @param hours New hours to set on the date object.
114
+ * @param minutes New minutes to set on the date object.
115
+ * @param seconds New seconds to set on the date object.
116
+ */
117
+ setTime(target, hours, minutes, seconds) {
118
+ throw new Error(NOT_IMPLEMENTED);
119
+ }
120
+ /**
121
+ * Gets the hours component of the given date.
122
+ * @param date The date to extract the hours from.
123
+ */
124
+ getHours(date) {
125
+ throw new Error(NOT_IMPLEMENTED);
126
+ }
127
+ /**
128
+ * Gets the minutes component of the given date.
129
+ * @param date The date to extract the minutes from.
130
+ */
131
+ getMinutes(date) {
132
+ throw new Error(NOT_IMPLEMENTED);
133
+ }
134
+ /**
135
+ * Gets the seconds component of the given date.
136
+ * @param date The date to extract the seconds from.
137
+ */
138
+ getSeconds(date) {
139
+ throw new Error(NOT_IMPLEMENTED);
140
+ }
141
+ /**
142
+ * Parses a date with a specific time from a user-provided value.
143
+ * @param value The value to parse.
144
+ * @param parseFormat The expected format of the value being parsed
145
+ * (type is implementation-dependent).
146
+ */
147
+ parseTime(value, parseFormat) {
148
+ throw new Error(NOT_IMPLEMENTED);
149
+ }
150
+ /**
151
+ * Adds an amount of seconds to the specified date.
152
+ * @param date Date to which to add the seconds.
153
+ * @param amount Amount of seconds to add to the date.
154
+ */
155
+ addSeconds(date, amount) {
156
+ throw new Error(NOT_IMPLEMENTED);
157
+ }
109
158
  /**
110
159
  * Given a potential date object, returns that same date object if it is
111
160
  * a valid date, or `null` if it's not a valid date.
@@ -153,6 +202,18 @@ class DateAdapter {
153
202
  this.getMonth(first) - this.getMonth(second) ||
154
203
  this.getDate(first) - this.getDate(second));
155
204
  }
205
+ /**
206
+ * Compares the time values of two dates.
207
+ * @param first First date to compare.
208
+ * @param second Second date to compare.
209
+ * @returns 0 if the times are equal, a number less than 0 if the first time is earlier,
210
+ * a number greater than 0 if the first time is later.
211
+ */
212
+ compareTime(first, second) {
213
+ return (this.getHours(first) - this.getHours(second) ||
214
+ this.getMinutes(first) - this.getMinutes(second) ||
215
+ this.getSeconds(first) - this.getSeconds(second));
216
+ }
156
217
  /**
157
218
  * Checks if two dates are equal.
158
219
  * @param first The first date to check.
@@ -171,6 +232,24 @@ class DateAdapter {
171
232
  }
172
233
  return first == second;
173
234
  }
235
+ /**
236
+ * Checks if the times of two dates are equal.
237
+ * @param first The first date to check.
238
+ * @param second The second date to check.
239
+ * @returns Whether the times of the two dates are equal.
240
+ * Null dates are considered equal to other null dates.
241
+ */
242
+ sameTime(first, second) {
243
+ if (first && second) {
244
+ const firstValid = this.isValid(first);
245
+ const secondValid = this.isValid(second);
246
+ if (firstValid && secondValid) {
247
+ return !this.compareTime(first, second);
248
+ }
249
+ return firstValid == secondValid;
250
+ }
251
+ return first == second;
252
+ }
174
253
  /**
175
254
  * Clamp the given date between min and max dates.
176
255
  * @param date The date to clamp.
@@ -198,6 +277,18 @@ const MAT_DATE_FORMATS = new InjectionToken('mat-date-formats');
198
277
  * because the regex will match strings with an out of bounds month, date, etc.
199
278
  */
200
279
  const ISO_8601_REGEX = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|(?:(?:\+|-)\d{2}:\d{2}))?)?$/;
280
+ /**
281
+ * Matches a time string. Supported formats:
282
+ * - {{hours}}:{{minutes}}
283
+ * - {{hours}}:{{minutes}}:{{seconds}}
284
+ * - {{hours}}:{{minutes}} AM/PM
285
+ * - {{hours}}:{{minutes}}:{{seconds}} AM/PM
286
+ * - {{hours}}.{{minutes}}
287
+ * - {{hours}}.{{minutes}}.{{seconds}}
288
+ * - {{hours}}.{{minutes}} AM/PM
289
+ * - {{hours}}.{{minutes}}.{{seconds}} AM/PM
290
+ */
291
+ const TIME_REGEX = /(\d?\d)[:.](\d?\d)(?:[:.](\d?\d))?\s*(AM|PM)?/i;
201
292
  /** Creates an array and fills it with values. */
202
293
  function range(length, valueFunction) {
203
294
  const valuesArray = Array(length);
@@ -252,7 +343,18 @@ class NativeDateAdapter extends DateAdapter {
252
343
  return this._format(dtf, date);
253
344
  }
254
345
  getFirstDayOfWeek() {
255
- // We can't tell using native JS Date what the first day of the week is, we default to Sunday.
346
+ // At the time of writing `Intl.Locale` isn't available
347
+ // in the internal types so we need to cast to `any`.
348
+ if (typeof Intl !== 'undefined' && Intl.Locale) {
349
+ const locale = new Intl.Locale(this.locale);
350
+ // Some browsers implement a `getWeekInfo` method while others have a `weekInfo` getter.
351
+ // Note that this isn't supported in all browsers so we need to null check it.
352
+ const firstDay = (locale.getWeekInfo?.() || locale.weekInfo)?.firstDay ?? 0;
353
+ // `weekInfo.firstDay` is a number between 1 and 7 where, starting from Monday,
354
+ // whereas our representation is 0 to 6 where 0 is Sunday so we need to normalize it.
355
+ return firstDay === 7 ? 0 : firstDay;
356
+ }
357
+ // Default to Sunday if the browser doesn't provide the week information.
256
358
  return 0;
257
359
  }
258
360
  getNumDaysInMonth(date) {
@@ -351,6 +453,92 @@ class NativeDateAdapter extends DateAdapter {
351
453
  invalid() {
352
454
  return new Date(NaN);
353
455
  }
456
+ setTime(target, hours, minutes, seconds) {
457
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
458
+ if (!inRange(hours, 0, 23)) {
459
+ throw Error(`Invalid hours "${hours}". Hours value must be between 0 and 23.`);
460
+ }
461
+ if (!inRange(minutes, 0, 59)) {
462
+ throw Error(`Invalid minutes "${minutes}". Minutes value must be between 0 and 59.`);
463
+ }
464
+ if (!inRange(seconds, 0, 59)) {
465
+ throw Error(`Invalid seconds "${seconds}". Seconds value must be between 0 and 59.`);
466
+ }
467
+ }
468
+ const clone = this.clone(target);
469
+ clone.setHours(hours, minutes, seconds, 0);
470
+ return clone;
471
+ }
472
+ getHours(date) {
473
+ return date.getHours();
474
+ }
475
+ getMinutes(date) {
476
+ return date.getMinutes();
477
+ }
478
+ getSeconds(date) {
479
+ return date.getSeconds();
480
+ }
481
+ parseTime(userValue, parseFormat) {
482
+ if (typeof userValue !== 'string') {
483
+ return userValue instanceof Date ? new Date(userValue.getTime()) : null;
484
+ }
485
+ const value = userValue.trim();
486
+ if (value.length === 0) {
487
+ return null;
488
+ }
489
+ const today = this.today();
490
+ const base = this.toIso8601(today);
491
+ // JS is able to parse colon-separated times (including AM/PM) by
492
+ // appending it to a valid date string. Generate one from today's date.
493
+ let result = Date.parse(`${base} ${value}`);
494
+ // Some locales use a dot instead of a colon as a separator, try replacing it before parsing.
495
+ if (!result && value.includes('.')) {
496
+ result = Date.parse(`${base} ${value.replace(/\./g, ':')}`);
497
+ }
498
+ // Other locales add extra characters around the time, but are otherwise parseable
499
+ // (e.g. `00:05 ч.` in bg-BG). Try replacing all non-number and non-colon characters.
500
+ if (!result) {
501
+ const withoutExtras = value.replace(/[^0-9:(AM|PM)]/gi, '').trim();
502
+ if (withoutExtras.length > 0) {
503
+ result = Date.parse(`${base} ${withoutExtras}`);
504
+ }
505
+ }
506
+ // Some browser implementations of Date aren't very flexible with the time formats.
507
+ // E.g. Safari doesn't support AM/PM or padded numbers. As a final resort, we try
508
+ // parsing some of the more common time formats ourselves.
509
+ if (!result) {
510
+ const parsed = value.toUpperCase().match(TIME_REGEX);
511
+ if (parsed) {
512
+ let hours = parseInt(parsed[1]);
513
+ const minutes = parseInt(parsed[2]);
514
+ let seconds = parsed[3] == null ? undefined : parseInt(parsed[3]);
515
+ const amPm = parsed[4];
516
+ if (hours === 12) {
517
+ hours = amPm === 'AM' ? 0 : hours;
518
+ }
519
+ else if (amPm === 'PM') {
520
+ hours += 12;
521
+ }
522
+ if (inRange(hours, 0, 23) &&
523
+ inRange(minutes, 0, 59) &&
524
+ (seconds == null || inRange(seconds, 0, 59))) {
525
+ return this.setTime(today, hours, minutes, seconds || 0);
526
+ }
527
+ }
528
+ }
529
+ if (result) {
530
+ const date = new Date(result);
531
+ // Firefox allows overflows in the time string, e.g. 25:00 gets parsed as the next day.
532
+ // Other browsers return invalid date objects in such cases so try to normalize it.
533
+ if (this.sameDate(today, date)) {
534
+ return date;
535
+ }
536
+ }
537
+ return this.invalid();
538
+ }
539
+ addSeconds(date, amount) {
540
+ return new Date(date.getTime() + amount * 1000);
541
+ }
354
542
  /** Creates a date but allows the month and date to overflow. */
355
543
  _createDateWithOverflow(year, month, date) {
356
544
  // Passing the year to the constructor causes year numbers <100 to be converted to 19xx.
@@ -393,16 +581,23 @@ class NativeDateAdapter extends DateAdapter {
393
581
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0-next.3", ngImport: i0, type: NativeDateAdapter, decorators: [{
394
582
  type: Injectable
395
583
  }], ctorParameters: () => [] });
584
+ /** Checks whether a number is within a certain range. */
585
+ function inRange(value, min, max) {
586
+ return !isNaN(value) && value >= min && value <= max;
587
+ }
396
588
 
397
589
  const MAT_NATIVE_DATE_FORMATS = {
398
590
  parse: {
399
591
  dateInput: null,
592
+ timeInput: null,
400
593
  },
401
594
  display: {
402
595
  dateInput: { year: 'numeric', month: 'numeric', day: 'numeric' },
596
+ timeInput: { hour: 'numeric', minute: 'numeric' },
403
597
  monthYearLabel: { year: 'numeric', month: 'short' },
404
598
  dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },
405
599
  monthYearA11yLabel: { year: 'numeric', month: 'long' },
600
+ timeOptionLabel: { hour: 'numeric', minute: 'numeric' },
406
601
  },
407
602
  };
408
603
 
@@ -1278,7 +1473,9 @@ class MatOption {
1278
1473
  }
1279
1474
  /** Whether ripples for the option are disabled. */
1280
1475
  get disableRipple() {
1281
- return !!(this._parent && this._parent.disableRipple);
1476
+ return this._signalDisableRipple
1477
+ ? this._parent.disableRipple()
1478
+ : !!this._parent?.disableRipple;
1282
1479
  }
1283
1480
  /** Whether to display checkmark for single-selection. */
1284
1481
  get hideSingleSelectionIndicator() {
@@ -1289,6 +1486,7 @@ class MatOption {
1289
1486
  this._changeDetectorRef = inject(ChangeDetectorRef);
1290
1487
  this._parent = inject(MAT_OPTION_PARENT_COMPONENT, { optional: true });
1291
1488
  this.group = inject(MAT_OPTGROUP, { optional: true });
1489
+ this._signalDisableRipple = false;
1292
1490
  this._selected = false;
1293
1491
  this._active = false;
1294
1492
  this._disabled = false;
@@ -1300,6 +1498,7 @@ class MatOption {
1300
1498
  this.onSelectionChange = new EventEmitter();
1301
1499
  /** Emits when the state of the option changes and any parents have to be notified. */
1302
1500
  this._stateChanges = new Subject();
1501
+ this._signalDisableRipple = !!this._parent && isSignal(this._parent.disableRipple);
1303
1502
  }
1304
1503
  /**
1305
1504
  * Whether or not the option is currently active and ready to be selected.