@cdx-ui/formatters 0.2.0 → 0.2.1

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.
Files changed (35) hide show
  1. package/coverage.svg +1 -1
  2. package/lib/commonjs/dates/index.js +13 -0
  3. package/lib/commonjs/dates/index.js.map +1 -1
  4. package/lib/commonjs/dates/labels/dateComponentLabels.js +48 -3
  5. package/lib/commonjs/dates/labels/dateComponentLabels.js.map +1 -1
  6. package/lib/commonjs/dates/validation/getDateValidationReason.js +33 -0
  7. package/lib/commonjs/dates/validation/getDateValidationReason.js.map +1 -0
  8. package/lib/commonjs/index.js +12 -0
  9. package/lib/commonjs/index.js.map +1 -1
  10. package/lib/module/dates/index.js +2 -1
  11. package/lib/module/dates/index.js.map +1 -1
  12. package/lib/module/dates/labels/dateComponentLabels.js +48 -3
  13. package/lib/module/dates/labels/dateComponentLabels.js.map +1 -1
  14. package/lib/module/dates/validation/getDateValidationReason.js +29 -0
  15. package/lib/module/dates/validation/getDateValidationReason.js.map +1 -0
  16. package/lib/module/index.js +3 -1
  17. package/lib/module/index.js.map +1 -1
  18. package/lib/typescript/dates/index.d.ts +3 -2
  19. package/lib/typescript/dates/index.d.ts.map +1 -1
  20. package/lib/typescript/dates/labels/dateComponentLabels.d.ts +31 -2
  21. package/lib/typescript/dates/labels/dateComponentLabels.d.ts.map +1 -1
  22. package/lib/typescript/dates/types.d.ts +11 -0
  23. package/lib/typescript/dates/types.d.ts.map +1 -1
  24. package/lib/typescript/dates/validation/getDateValidationReason.d.ts +16 -0
  25. package/lib/typescript/dates/validation/getDateValidationReason.d.ts.map +1 -0
  26. package/lib/typescript/index.d.ts +2 -2
  27. package/lib/typescript/index.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/src/__tests__/dateComponentLabels.test.ts +55 -0
  30. package/src/__tests__/getDateValidationReason.test.ts +45 -0
  31. package/src/dates/index.ts +5 -0
  32. package/src/dates/labels/dateComponentLabels.ts +68 -2
  33. package/src/dates/types.ts +13 -0
  34. package/src/dates/validation/getDateValidationReason.ts +31 -0
  35. package/src/index.ts +5 -0
package/coverage.svg CHANGED
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="108" height="20" role="img" aria-label="coverage: 95.3%"><title>coverage: 95.3%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="108" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="47" height="20" fill="#4b0"/><rect width="108" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="835" y="150" fill-opacity=".8" filter="url(#blur)" textLength="370">95.3%</text><text x="835" y="150" fill-opacity=".3" textLength="370">95.3%</text></g><text x="835" y="140" textLength="370">95.3%</text></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="108" height="20" role="img" aria-label="coverage: 95.7%"><title>coverage: 95.7%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="108" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="47" height="20" fill="#4b0"/><rect width="108" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="835" y="150" fill-opacity=".8" filter="url(#blur)" textLength="370">95.7%</text><text x="835" y="150" fill-opacity=".3" textLength="370">95.7%</text></g><text x="835" y="140" textLength="370">95.7%</text></g></g></svg>
@@ -147,6 +147,12 @@ Object.defineProperty(exports, "formatDateTimeListPeriod", {
147
147
  return _formatDateTimeListPeriod.formatDateTimeListPeriod;
148
148
  }
149
149
  });
150
+ Object.defineProperty(exports, "formatDateValidationMessage", {
151
+ enumerable: true,
152
+ get: function () {
153
+ return _dateComponentLabels.formatDateValidationMessage;
154
+ }
155
+ });
150
156
  Object.defineProperty(exports, "formatMonthCalendarMonth", {
151
157
  enumerable: true,
152
158
  get: function () {
@@ -207,6 +213,12 @@ Object.defineProperty(exports, "getDateTimeSegmentVisibility", {
207
213
  return _getDateTimeSegmentVisibility.getDateTimeSegmentVisibility;
208
214
  }
209
215
  });
216
+ Object.defineProperty(exports, "getDateValidationReason", {
217
+ enumerable: true,
218
+ get: function () {
219
+ return _getDateValidationReason.getDateValidationReason;
220
+ }
221
+ });
210
222
  Object.defineProperty(exports, "getEmptyDateSegmentText", {
211
223
  enumerable: true,
212
224
  get: function () {
@@ -358,6 +370,7 @@ var _getDateTimeFieldSegments = require("./segments/getDateTimeFieldSegments");
358
370
  var _getDateTimeSegmentVisibility = require("./segments/getDateTimeSegmentVisibility");
359
371
  var _getEmptyDateSegmentText = require("./segments/getEmptyDateSegmentText");
360
372
  var _parseDateSegment = require("./segments/parseDateSegment");
373
+ var _getDateValidationReason = require("./validation/getDateValidationReason");
361
374
  var _formatCalendarMonthYear = require("./calendar/formatCalendarMonthYear");
362
375
  var _formatDateTimeListDateRow = require("./calendar/formatDateTimeListDateRow");
363
376
  var _formatMonthCalendarMonth = require("./calendar/formatMonthCalendarMonth");
@@ -1 +1 @@
1
- {"version":3,"names":["_clampDateParts","require","_clampMonthParts","_dateId","_monthId","_formatDateId","_formatMonthId","_isValidDateId","_isValidDateParts","_isValidMonthId","_isValidMonthParts","_parseDateId","_parseMonthId","_toMonthId","_todayDateId","_todayMonthId","_clampDateTimeId","_compareDateTimeIds","_dateTimeId","_formatDateTimeId","_isLocaleHour","_formatDateTimeListHour","_formatDateTimeListMinute","_formatDateTimeListPeriod","_isValidDateTimeId","_nowDateTimeId","_parseDateTimeId","_formatDateForDisplay","_formatDateRangeForDisplay","_formatDateTimeForDisplay","_formatMonthForDisplay","_compareDateIds","_compareMonthIds","_dateRangeAroundDay","_dateRangeFromLastNDays","_eachDateIdInRange","_normalizeDateRange","_applyDateSegmentChange","_formatDateSegment","_getDateFieldSegments","_getDateTimeFieldSegments","_getDateTimeSegmentVisibility","_getEmptyDateSegmentText","_parseDateSegment","_formatCalendarMonthYear","_formatDateTimeListDateRow","_formatMonthCalendarMonth","_formatMonthCalendarYear","_getCalendarWeekdayLabels","_dateComponentLabels"],"sourceRoot":"../../../src","sources":["dates/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAAA,eAAA,GAAAC,OAAA;AACA,IAAAC,gBAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;AACA,IAAAG,QAAA,GAAAH,OAAA;AACA,IAAAI,aAAA,GAAAJ,OAAA;AACA,IAAAK,cAAA,GAAAL,OAAA;AACA,IAAAM,cAAA,GAAAN,OAAA;AACA,IAAAO,iBAAA,GAAAP,OAAA;AACA,IAAAQ,eAAA,GAAAR,OAAA;AACA,IAAAS,kBAAA,GAAAT,OAAA;AACA,IAAAU,YAAA,GAAAV,OAAA;AACA,IAAAW,aAAA,GAAAX,OAAA;AACA,IAAAY,UAAA,GAAAZ,OAAA;AACA,IAAAa,YAAA,GAAAb,OAAA;AACA,IAAAc,aAAA,GAAAd,OAAA;AAEA,IAAAe,gBAAA,GAAAf,OAAA;AACA,IAAAgB,mBAAA,GAAAhB,OAAA;AACA,IAAAiB,WAAA,GAAAjB,OAAA;AACA,IAAAkB,iBAAA,GAAAlB,OAAA;AACA,IAAAmB,aAAA,GAAAnB,OAAA;AACA,IAAAoB,uBAAA,GAAApB,OAAA;AACA,IAAAqB,yBAAA,GAAArB,OAAA;AACA,IAAAsB,yBAAA,GAAAtB,OAAA;AACA,IAAAuB,kBAAA,GAAAvB,OAAA;AACA,IAAAwB,cAAA,GAAAxB,OAAA;AACA,IAAAyB,gBAAA,GAAAzB,OAAA;AAEA,IAAA0B,qBAAA,GAAA1B,OAAA;AACA,IAAA2B,0BAAA,GAAA3B,OAAA;AACA,IAAA4B,yBAAA,GAAA5B,OAAA;AACA,IAAA6B,sBAAA,GAAA7B,OAAA;AAEA,IAAA8B,eAAA,GAAA9B,OAAA;AACA,IAAA+B,gBAAA,GAAA/B,OAAA;AACA,IAAAgC,mBAAA,GAAAhC,OAAA;AACA,IAAAiC,uBAAA,GAAAjC,OAAA;AACA,IAAAkC,kBAAA,GAAAlC,OAAA;AACA,IAAAmC,mBAAA,GAAAnC,OAAA;AAEA,IAAAoC,uBAAA,GAAApC,OAAA;AACA,IAAAqC,kBAAA,GAAArC,OAAA;AACA,IAAAsC,qBAAA,GAAAtC,OAAA;AACA,IAAAuC,yBAAA,GAAAvC,OAAA;AACA,IAAAwC,6BAAA,GAAAxC,OAAA;AACA,IAAAyC,wBAAA,GAAAzC,OAAA;AACA,IAAA0C,iBAAA,GAAA1C,OAAA;AAEA,IAAA2C,wBAAA,GAAA3C,OAAA;AACA,IAAA4C,0BAAA,GAAA5C,OAAA;AACA,IAAA6C,yBAAA,GAAA7C,OAAA;AACA,IAAA8C,wBAAA,GAAA9C,OAAA;AACA,IAAA+C,yBAAA,GAAA/C,OAAA;AAGA,IAAAgD,oBAAA,GAAAhD,OAAA","ignoreList":[]}
1
+ {"version":3,"names":["_clampDateParts","require","_clampMonthParts","_dateId","_monthId","_formatDateId","_formatMonthId","_isValidDateId","_isValidDateParts","_isValidMonthId","_isValidMonthParts","_parseDateId","_parseMonthId","_toMonthId","_todayDateId","_todayMonthId","_clampDateTimeId","_compareDateTimeIds","_dateTimeId","_formatDateTimeId","_isLocaleHour","_formatDateTimeListHour","_formatDateTimeListMinute","_formatDateTimeListPeriod","_isValidDateTimeId","_nowDateTimeId","_parseDateTimeId","_formatDateForDisplay","_formatDateRangeForDisplay","_formatDateTimeForDisplay","_formatMonthForDisplay","_compareDateIds","_compareMonthIds","_dateRangeAroundDay","_dateRangeFromLastNDays","_eachDateIdInRange","_normalizeDateRange","_applyDateSegmentChange","_formatDateSegment","_getDateFieldSegments","_getDateTimeFieldSegments","_getDateTimeSegmentVisibility","_getEmptyDateSegmentText","_parseDateSegment","_getDateValidationReason","_formatCalendarMonthYear","_formatDateTimeListDateRow","_formatMonthCalendarMonth","_formatMonthCalendarYear","_getCalendarWeekdayLabels","_dateComponentLabels"],"sourceRoot":"../../../src","sources":["dates/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,IAAAA,eAAA,GAAAC,OAAA;AACA,IAAAC,gBAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;AACA,IAAAG,QAAA,GAAAH,OAAA;AACA,IAAAI,aAAA,GAAAJ,OAAA;AACA,IAAAK,cAAA,GAAAL,OAAA;AACA,IAAAM,cAAA,GAAAN,OAAA;AACA,IAAAO,iBAAA,GAAAP,OAAA;AACA,IAAAQ,eAAA,GAAAR,OAAA;AACA,IAAAS,kBAAA,GAAAT,OAAA;AACA,IAAAU,YAAA,GAAAV,OAAA;AACA,IAAAW,aAAA,GAAAX,OAAA;AACA,IAAAY,UAAA,GAAAZ,OAAA;AACA,IAAAa,YAAA,GAAAb,OAAA;AACA,IAAAc,aAAA,GAAAd,OAAA;AAEA,IAAAe,gBAAA,GAAAf,OAAA;AACA,IAAAgB,mBAAA,GAAAhB,OAAA;AACA,IAAAiB,WAAA,GAAAjB,OAAA;AACA,IAAAkB,iBAAA,GAAAlB,OAAA;AACA,IAAAmB,aAAA,GAAAnB,OAAA;AACA,IAAAoB,uBAAA,GAAApB,OAAA;AACA,IAAAqB,yBAAA,GAAArB,OAAA;AACA,IAAAsB,yBAAA,GAAAtB,OAAA;AACA,IAAAuB,kBAAA,GAAAvB,OAAA;AACA,IAAAwB,cAAA,GAAAxB,OAAA;AACA,IAAAyB,gBAAA,GAAAzB,OAAA;AAEA,IAAA0B,qBAAA,GAAA1B,OAAA;AACA,IAAA2B,0BAAA,GAAA3B,OAAA;AACA,IAAA4B,yBAAA,GAAA5B,OAAA;AACA,IAAA6B,sBAAA,GAAA7B,OAAA;AAEA,IAAA8B,eAAA,GAAA9B,OAAA;AACA,IAAA+B,gBAAA,GAAA/B,OAAA;AACA,IAAAgC,mBAAA,GAAAhC,OAAA;AACA,IAAAiC,uBAAA,GAAAjC,OAAA;AACA,IAAAkC,kBAAA,GAAAlC,OAAA;AACA,IAAAmC,mBAAA,GAAAnC,OAAA;AAEA,IAAAoC,uBAAA,GAAApC,OAAA;AACA,IAAAqC,kBAAA,GAAArC,OAAA;AACA,IAAAsC,qBAAA,GAAAtC,OAAA;AACA,IAAAuC,yBAAA,GAAAvC,OAAA;AACA,IAAAwC,6BAAA,GAAAxC,OAAA;AACA,IAAAyC,wBAAA,GAAAzC,OAAA;AACA,IAAA0C,iBAAA,GAAA1C,OAAA;AAEA,IAAA2C,wBAAA,GAAA3C,OAAA;AAEA,IAAA4C,wBAAA,GAAA5C,OAAA;AACA,IAAA6C,0BAAA,GAAA7C,OAAA;AACA,IAAA8C,yBAAA,GAAA9C,OAAA;AACA,IAAA+C,wBAAA,GAAA/C,OAAA;AACA,IAAAgD,yBAAA,GAAAhD,OAAA;AAGA,IAAAiD,oBAAA,GAAAjD,OAAA","ignoreList":[]}
@@ -4,9 +4,11 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.DEFAULT_DATE_COMPONENT_LABELS = void 0;
7
+ exports.formatDateValidationMessage = formatDateValidationMessage;
7
8
  exports.formatSelectYear = formatSelectYear;
8
9
  exports.getDateSegmentAccessibilityLabel = getDateSegmentAccessibilityLabel;
9
10
  exports.resolveDateComponentLabels = resolveDateComponentLabels;
11
+ var _formatDateForDisplay = require("../display/formatDateForDisplay");
10
12
  /** Overridable UI copy for date pickers, calendars, and segmented date fields. */
11
13
 
12
14
  const DEFAULT_DATE_COMPONENT_LABELS = exports.DEFAULT_DATE_COMPONENT_LABELS = {
@@ -30,7 +32,11 @@ const DEFAULT_DATE_COMPONENT_LABELS = exports.DEFAULT_DATE_COMPONENT_LABELS = {
30
32
  segmentMinute: 'Minute',
31
33
  segmentSecond: 'Second',
32
34
  segmentDayPeriod: 'AM/PM',
33
- rangeSeparator: '–'
35
+ segmentInvalid: '{segment}, invalid',
36
+ rangeSeparator: '–',
37
+ validationBeforeMin: 'Select a date on or after {date}',
38
+ validationAfterMax: 'Select a date on or before {date}',
39
+ validationUnavailable: 'This date is unavailable'
34
40
  };
35
41
  function resolveDateComponentLabels(partial) {
36
42
  return {
@@ -41,6 +47,37 @@ function resolveDateComponentLabels(partial) {
41
47
  function formatSelectYear(labels, year) {
42
48
  return labels.selectYear.replace('{year}', String(year));
43
49
  }
50
+ const VALIDATION_LABEL_KEYS = {
51
+ beforeMin: 'validationBeforeMin',
52
+ afterMax: 'validationAfterMax',
53
+ unavailable: 'validationUnavailable'
54
+ };
55
+
56
+ /**
57
+ * Resolves the user-facing message for a {@link DateValidationReason}.
58
+ *
59
+ * `{date}` in the min / max messages is replaced with the bound, formatted for display
60
+ * with `options.locale` and `options.dateStyle` (defaults: `en-US`, `medium`). `DatePicker`
61
+ * already formats this message with its own `labels` and `locale` (see `onValidationChange`
62
+ * and `Field.Error`); call this directly only outside a picker, and pass the same `locale`.
63
+ *
64
+ * @param labels - Resolved labels (see `resolveDateComponentLabels`).
65
+ * @param reason - Reason returned by `getDateValidationReason` or `onValidationChange`.
66
+ * @param rules - The bounds the reason was computed against.
67
+ * @param options - Display options for the `{date}` placeholder.
68
+ * @returns The message to render in `Field.Error`.
69
+ */
70
+ function formatDateValidationMessage(labels, reason, rules, options) {
71
+ const message = labels[VALIDATION_LABEL_KEYS[reason]];
72
+ const bound = reason === 'beforeMin' ? rules.minValue : reason === 'afterMax' ? rules.maxValue : undefined;
73
+ if (bound === undefined) {
74
+ return message;
75
+ }
76
+ return message.replace('{date}', (0, _formatDateForDisplay.formatDateForDisplay)(bound, {
77
+ dateStyle: 'medium',
78
+ ...options
79
+ }));
80
+ }
44
81
  const SEGMENT_LABEL_KEYS = {
45
82
  year: 'segmentYear',
46
83
  month: 'segmentMonth',
@@ -50,8 +87,16 @@ const SEGMENT_LABEL_KEYS = {
50
87
  second: 'segmentSecond',
51
88
  dayPeriod: 'segmentDayPeriod'
52
89
  };
53
- function getDateSegmentAccessibilityLabel(type, labels) {
90
+
91
+ /**
92
+ * @param invalid - Wraps the label in `labels.segmentInvalid`, for native platforms where
93
+ * `aria-invalid` is not announced.
94
+ */
95
+ function getDateSegmentAccessibilityLabel(type, labels, invalid = false) {
54
96
  const key = SEGMENT_LABEL_KEYS[type];
55
- return key ? labels[key] : undefined;
97
+ if (!key) {
98
+ return undefined;
99
+ }
100
+ return invalid ? labels.segmentInvalid.replace('{segment}', labels[key]) : labels[key];
56
101
  }
57
102
  //# sourceMappingURL=dateComponentLabels.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["DEFAULT_DATE_COMPONENT_LABELS","exports","openDatePicker","openDateTimePicker","openDateRangePicker","openMonthPicker","showCalendar","showYearPicker","showMonths","previousMonth","nextMonth","previousYear","nextYear","selectYear","dateFieldKeyboardInput","segmentYear","segmentMonth","segmentDay","segmentHour","segmentMinute","segmentSecond","segmentDayPeriod","rangeSeparator","resolveDateComponentLabels","partial","formatSelectYear","labels","year","replace","String","SEGMENT_LABEL_KEYS","month","day","hour","minute","second","dayPeriod","getDateSegmentAccessibilityLabel","type","key","undefined"],"sourceRoot":"../../../../src","sources":["dates/labels/dateComponentLabels.ts"],"mappings":";;;;;;;;;AAEA;;AA0BO,MAAMA,6BAAkD,GAAAC,OAAA,CAAAD,6BAAA,GAAG;EAChEE,cAAc,EAAE,kBAAkB;EAClCC,kBAAkB,EAAE,uBAAuB;EAC3CC,mBAAmB,EAAE,wBAAwB;EAC7CC,eAAe,EAAE,mBAAmB;EACpCC,YAAY,EAAE,eAAe;EAC7BC,cAAc,EAAE,kBAAkB;EAClCC,UAAU,EAAE,aAAa;EACzBC,aAAa,EAAE,gBAAgB;EAC/BC,SAAS,EAAE,YAAY;EACvBC,YAAY,EAAE,eAAe;EAC7BC,QAAQ,EAAE,WAAW;EACrBC,UAAU,EAAE,oBAAoB;EAChCC,sBAAsB,EAAE,2BAA2B;EACnDC,WAAW,EAAE,MAAM;EACnBC,YAAY,EAAE,OAAO;EACrBC,UAAU,EAAE,KAAK;EACjBC,WAAW,EAAE,MAAM;EACnBC,aAAa,EAAE,QAAQ;EACvBC,aAAa,EAAE,QAAQ;EACvBC,gBAAgB,EAAE,OAAO;EACzBC,cAAc,EAAE;AAClB,CAAC;AAEM,SAASC,0BAA0BA,CACxCC,OAAsC,EACjB;EACrB,OAAO;IAAE,GAAGxB,6BAA6B;IAAE,GAAGwB;EAAQ,CAAC;AACzD;AAEO,SAASC,gBAAgBA,CAACC,MAA2B,EAAEC,IAAY,EAAU;EAClF,OAAOD,MAAM,CAACb,UAAU,CAACe,OAAO,CAAC,QAAQ,EAAEC,MAAM,CAACF,IAAI,CAAC,CAAC;AAC1D;AAEA,MAAMG,kBAA+E,GAAG;EACtFH,IAAI,EAAE,aAAa;EACnBI,KAAK,EAAE,cAAc;EACrBC,GAAG,EAAE,YAAY;EACjBC,IAAI,EAAE,aAAa;EACnBC,MAAM,EAAE,eAAe;EACvBC,MAAM,EAAE,eAAe;EACvBC,SAAS,EAAE;AACb,CAAC;AAEM,SAASC,gCAAgCA,CAC9CC,IAAqB,EACrBZ,MAA2B,EACP;EACpB,MAAMa,GAAG,GAAGT,kBAAkB,CAACQ,IAAI,CAAC;EACpC,OAAOC,GAAG,GAAGb,MAAM,CAACa,GAAG,CAAC,GAAGC,SAAS;AACtC","ignoreList":[]}
1
+ {"version":3,"names":["_formatDateForDisplay","require","DEFAULT_DATE_COMPONENT_LABELS","exports","openDatePicker","openDateTimePicker","openDateRangePicker","openMonthPicker","showCalendar","showYearPicker","showMonths","previousMonth","nextMonth","previousYear","nextYear","selectYear","dateFieldKeyboardInput","segmentYear","segmentMonth","segmentDay","segmentHour","segmentMinute","segmentSecond","segmentDayPeriod","segmentInvalid","rangeSeparator","validationBeforeMin","validationAfterMax","validationUnavailable","resolveDateComponentLabels","partial","formatSelectYear","labels","year","replace","String","VALIDATION_LABEL_KEYS","beforeMin","afterMax","unavailable","formatDateValidationMessage","reason","rules","options","message","bound","minValue","maxValue","undefined","formatDateForDisplay","dateStyle","SEGMENT_LABEL_KEYS","month","day","hour","minute","second","dayPeriod","getDateSegmentAccessibilityLabel","type","invalid","key"],"sourceRoot":"../../../../src","sources":["dates/labels/dateComponentLabels.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getDateValidationReason = getDateValidationReason;
7
+ var _compareDateIds = require("../ranges/compareDateIds");
8
+ /**
9
+ * Checks a complete wire date against `minValue`, `maxValue`, and `isDateUnavailable`.
10
+ *
11
+ * Bounds are inclusive and are checked before the predicate, so a date outside
12
+ * the range reports `beforeMin` / `afterMax` even when it is also unavailable.
13
+ *
14
+ * @param date - Wire date to check.
15
+ * @param rules - Bounds and unavailable-date predicate.
16
+ * @returns The first rule the date violates, or `undefined` when it is selectable.
17
+ *
18
+ * @example
19
+ * getDateValidationReason(dateId('2026-08-01'), { minValue: dateId('2026-08-10') }) // 'beforeMin'
20
+ */
21
+ function getDateValidationReason(date, rules) {
22
+ if (rules.minValue !== undefined && (0, _compareDateIds.compareDateIds)(date, rules.minValue) < 0) {
23
+ return 'beforeMin';
24
+ }
25
+ if (rules.maxValue !== undefined && (0, _compareDateIds.compareDateIds)(date, rules.maxValue) > 0) {
26
+ return 'afterMax';
27
+ }
28
+ if (rules.isDateUnavailable?.(date)) {
29
+ return 'unavailable';
30
+ }
31
+ return undefined;
32
+ }
33
+ //# sourceMappingURL=getDateValidationReason.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_compareDateIds","require","getDateValidationReason","date","rules","minValue","undefined","compareDateIds","maxValue","isDateUnavailable"],"sourceRoot":"../../../../src","sources":["dates/validation/getDateValidationReason.ts"],"mappings":";;;;;;AAAA,IAAAA,eAAA,GAAAC,OAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,uBAAuBA,CACrCC,IAAY,EACZC,KAA0B,EACQ;EAClC,IAAIA,KAAK,CAACC,QAAQ,KAAKC,SAAS,IAAI,IAAAC,8BAAc,EAACJ,IAAI,EAAEC,KAAK,CAACC,QAAQ,CAAC,GAAG,CAAC,EAAE;IAC5E,OAAO,WAAW;EACpB;EACA,IAAID,KAAK,CAACI,QAAQ,KAAKF,SAAS,IAAI,IAAAC,8BAAc,EAACJ,IAAI,EAAEC,KAAK,CAACI,QAAQ,CAAC,GAAG,CAAC,EAAE;IAC5E,OAAO,UAAU;EACnB;EACA,IAAIJ,KAAK,CAACK,iBAAiB,GAAGN,IAAI,CAAC,EAAE;IACnC,OAAO,aAAa;EACtB;EACA,OAAOG,SAAS;AAClB","ignoreList":[]}
@@ -147,6 +147,12 @@ Object.defineProperty(exports, "formatDateTimeListPeriod", {
147
147
  return _dates.formatDateTimeListPeriod;
148
148
  }
149
149
  });
150
+ Object.defineProperty(exports, "formatDateValidationMessage", {
151
+ enumerable: true,
152
+ get: function () {
153
+ return _dates.formatDateValidationMessage;
154
+ }
155
+ });
150
156
  Object.defineProperty(exports, "formatMonthCalendarMonth", {
151
157
  enumerable: true,
152
158
  get: function () {
@@ -213,6 +219,12 @@ Object.defineProperty(exports, "getDateTimeSegmentVisibility", {
213
219
  return _dates.getDateTimeSegmentVisibility;
214
220
  }
215
221
  });
222
+ Object.defineProperty(exports, "getDateValidationReason", {
223
+ enumerable: true,
224
+ get: function () {
225
+ return _dates.getDateValidationReason;
226
+ }
227
+ });
216
228
  Object.defineProperty(exports, "getEmptyDateSegmentText", {
217
229
  enumerable: true,
218
230
  get: function () {
@@ -1 +1 @@
1
- {"version":3,"names":["_formatNumber","require","_dates"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,aAAA,GAAAC,OAAA;AAoBA,IAAAC,MAAA,GAAAD,OAAA","ignoreList":[]}
1
+ {"version":3,"names":["_formatNumber","require","_dates"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,aAAA,GAAAC,OAAA;AAsBA,IAAAC,MAAA,GAAAD,OAAA","ignoreList":[]}
@@ -50,10 +50,11 @@ export { getDateTimeFieldSegments } from './segments/getDateTimeFieldSegments';
50
50
  export { getDateTimeSegmentVisibility } from './segments/getDateTimeSegmentVisibility';
51
51
  export { getEmptyDateSegmentText } from './segments/getEmptyDateSegmentText';
52
52
  export { parseDateSegment } from './segments/parseDateSegment';
53
+ export { getDateValidationReason } from './validation/getDateValidationReason';
53
54
  export { formatCalendarMonthYear } from './calendar/formatCalendarMonthYear';
54
55
  export { formatDateTimeListDateRow } from './calendar/formatDateTimeListDateRow';
55
56
  export { formatMonthCalendarMonth } from './calendar/formatMonthCalendarMonth';
56
57
  export { formatMonthCalendarYear } from './calendar/formatMonthCalendarYear';
57
58
  export { getCalendarWeekdayLabels } from './calendar/getCalendarWeekdayLabels';
58
- export { DEFAULT_DATE_COMPONENT_LABELS, formatSelectYear, getDateSegmentAccessibilityLabel, resolveDateComponentLabels } from './labels/dateComponentLabels';
59
+ export { DEFAULT_DATE_COMPONENT_LABELS, formatDateValidationMessage, formatSelectYear, getDateSegmentAccessibilityLabel, resolveDateComponentLabels } from './labels/dateComponentLabels';
59
60
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["clampDateParts","clampMonthParts","dateId","monthId","formatDateId","formatMonthId","isValidDateId","isValidDateParts","isValidMonthId","isValidMonthParts","parseDateId","parseMonthId","toMonthId","todayDateId","todayMonthId","clampDateTimeId","compareDateTimeIds","dateTimeId","formatDateTimeId","isLocaleHour12","formatDateTimeListHour","formatDateTimeListMinute","formatDateTimeListPeriod","isValidDateTimeId","nowDateTimeId","parseDateTimeId","formatDateForDisplay","formatDateRangeForDisplay","formatDateTimeForDisplay","formatMonthForDisplay","compareDateIds","compareMonthIds","dateRangeAroundDay","dateRangeFromLastNDays","eachDateIdInRange","normalizeDateRange","applyDateSegmentChange","formatDateSegment","getDateFieldSegments","getDateTimeFieldSegments","getDateTimeSegmentVisibility","getEmptyDateSegmentText","parseDateSegment","formatCalendarMonthYear","formatDateTimeListDateRow","formatMonthCalendarMonth","formatMonthCalendarYear","getCalendarWeekdayLabels","DEFAULT_DATE_COMPONENT_LABELS","formatSelectYear","getDateSegmentAccessibilityLabel","resolveDateComponentLabels"],"sourceRoot":"../../../src","sources":["dates/index.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;;AAoBA,SAASA,cAAc,QAAQ,uBAAuB;AACtD,SAASC,eAAe,QAAQ,wBAAwB;AACxD,SAASC,MAAM,QAAQ,eAAe;AACtC,SAASC,OAAO,QAAQ,gBAAgB;AACxC,SAASC,YAAY,QAAQ,qBAAqB;AAClD,SAASC,aAAa,QAAQ,sBAAsB;AACpD,SAASC,aAAa,QAAQ,sBAAsB;AACpD,SAASC,gBAAgB,QAAQ,yBAAyB;AAC1D,SAASC,cAAc,QAAQ,uBAAuB;AACtD,SAASC,iBAAiB,QAAQ,0BAA0B;AAC5D,SAASC,WAAW,QAAQ,oBAAoB;AAChD,SAASC,YAAY,QAAQ,qBAAqB;AAClD,SAASC,SAAS,QAAQ,kBAAkB;AAC5C,SAASC,WAAW,QAAQ,oBAAoB;AAChD,SAASC,YAAY,QAAQ,qBAAqB;AAElD,SAASC,eAAe,QAAQ,4BAA4B;AAC5D,SAASC,kBAAkB,QAAQ,+BAA+B;AAClE,SAASC,UAAU,QAAQ,uBAAuB;AAClD,SAASC,gBAAgB,QAAQ,6BAA6B;AAC9D,SAASC,cAAc,QAAQ,2BAA2B;AAC1D,SAASC,sBAAsB,QAAQ,mCAAmC;AAC1E,SAASC,wBAAwB,QAAQ,qCAAqC;AAC9E,SAASC,wBAAwB,QAAQ,qCAAqC;AAC9E,SAASC,iBAAiB,QAAQ,8BAA8B;AAChE,SAASC,aAAa,QAAQ,0BAA0B;AACxD,SAASC,eAAe,QAAQ,4BAA4B;AAE5D,SAASC,oBAAoB,QAAQ,gCAAgC;AACrE,SAASC,yBAAyB,QAAQ,qCAAqC;AAC/E,SAASC,wBAAwB,QAAQ,oCAAoC;AAC7E,SAASC,qBAAqB,QAAQ,iCAAiC;AAEvE,SAASC,cAAc,QAAQ,yBAAyB;AACxD,SAASC,eAAe,QAAQ,0BAA0B;AAC1D,SAASC,kBAAkB,QAAQ,6BAA6B;AAChE,SAASC,sBAAsB,QAAQ,iCAAiC;AACxE,SAASC,iBAAiB,QAAQ,4BAA4B;AAC9D,SAASC,kBAAkB,QAAQ,6BAA6B;AAEhE,SAASC,sBAAsB,QAAQ,mCAAmC;AAC1E,SAASC,iBAAiB,QAAQ,8BAA8B;AAChE,SAASC,oBAAoB,QAAQ,iCAAiC;AACtE,SAASC,wBAAwB,QAAQ,qCAAqC;AAC9E,SAASC,4BAA4B,QAAQ,yCAAyC;AACtF,SAASC,uBAAuB,QAAQ,oCAAoC;AAC5E,SAASC,gBAAgB,QAAQ,6BAA6B;AAE9D,SAASC,uBAAuB,QAAQ,oCAAoC;AAC5E,SAASC,yBAAyB,QAAQ,sCAAsC;AAChF,SAASC,wBAAwB,QAAQ,qCAAqC;AAC9E,SAASC,uBAAuB,QAAQ,oCAAoC;AAC5E,SAASC,wBAAwB,QAAQ,qCAAqC;AAG9E,SACEC,6BAA6B,EAC7BC,gBAAgB,EAChBC,gCAAgC,EAChCC,0BAA0B,QACrB,8BAA8B","ignoreList":[]}
1
+ {"version":3,"names":["clampDateParts","clampMonthParts","dateId","monthId","formatDateId","formatMonthId","isValidDateId","isValidDateParts","isValidMonthId","isValidMonthParts","parseDateId","parseMonthId","toMonthId","todayDateId","todayMonthId","clampDateTimeId","compareDateTimeIds","dateTimeId","formatDateTimeId","isLocaleHour12","formatDateTimeListHour","formatDateTimeListMinute","formatDateTimeListPeriod","isValidDateTimeId","nowDateTimeId","parseDateTimeId","formatDateForDisplay","formatDateRangeForDisplay","formatDateTimeForDisplay","formatMonthForDisplay","compareDateIds","compareMonthIds","dateRangeAroundDay","dateRangeFromLastNDays","eachDateIdInRange","normalizeDateRange","applyDateSegmentChange","formatDateSegment","getDateFieldSegments","getDateTimeFieldSegments","getDateTimeSegmentVisibility","getEmptyDateSegmentText","parseDateSegment","getDateValidationReason","formatCalendarMonthYear","formatDateTimeListDateRow","formatMonthCalendarMonth","formatMonthCalendarYear","getCalendarWeekdayLabels","DEFAULT_DATE_COMPONENT_LABELS","formatDateValidationMessage","formatSelectYear","getDateSegmentAccessibilityLabel","resolveDateComponentLabels"],"sourceRoot":"../../../src","sources":["dates/index.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;;AAsBA,SAASA,cAAc,QAAQ,uBAAuB;AACtD,SAASC,eAAe,QAAQ,wBAAwB;AACxD,SAASC,MAAM,QAAQ,eAAe;AACtC,SAASC,OAAO,QAAQ,gBAAgB;AACxC,SAASC,YAAY,QAAQ,qBAAqB;AAClD,SAASC,aAAa,QAAQ,sBAAsB;AACpD,SAASC,aAAa,QAAQ,sBAAsB;AACpD,SAASC,gBAAgB,QAAQ,yBAAyB;AAC1D,SAASC,cAAc,QAAQ,uBAAuB;AACtD,SAASC,iBAAiB,QAAQ,0BAA0B;AAC5D,SAASC,WAAW,QAAQ,oBAAoB;AAChD,SAASC,YAAY,QAAQ,qBAAqB;AAClD,SAASC,SAAS,QAAQ,kBAAkB;AAC5C,SAASC,WAAW,QAAQ,oBAAoB;AAChD,SAASC,YAAY,QAAQ,qBAAqB;AAElD,SAASC,eAAe,QAAQ,4BAA4B;AAC5D,SAASC,kBAAkB,QAAQ,+BAA+B;AAClE,SAASC,UAAU,QAAQ,uBAAuB;AAClD,SAASC,gBAAgB,QAAQ,6BAA6B;AAC9D,SAASC,cAAc,QAAQ,2BAA2B;AAC1D,SAASC,sBAAsB,QAAQ,mCAAmC;AAC1E,SAASC,wBAAwB,QAAQ,qCAAqC;AAC9E,SAASC,wBAAwB,QAAQ,qCAAqC;AAC9E,SAASC,iBAAiB,QAAQ,8BAA8B;AAChE,SAASC,aAAa,QAAQ,0BAA0B;AACxD,SAASC,eAAe,QAAQ,4BAA4B;AAE5D,SAASC,oBAAoB,QAAQ,gCAAgC;AACrE,SAASC,yBAAyB,QAAQ,qCAAqC;AAC/E,SAASC,wBAAwB,QAAQ,oCAAoC;AAC7E,SAASC,qBAAqB,QAAQ,iCAAiC;AAEvE,SAASC,cAAc,QAAQ,yBAAyB;AACxD,SAASC,eAAe,QAAQ,0BAA0B;AAC1D,SAASC,kBAAkB,QAAQ,6BAA6B;AAChE,SAASC,sBAAsB,QAAQ,iCAAiC;AACxE,SAASC,iBAAiB,QAAQ,4BAA4B;AAC9D,SAASC,kBAAkB,QAAQ,6BAA6B;AAEhE,SAASC,sBAAsB,QAAQ,mCAAmC;AAC1E,SAASC,iBAAiB,QAAQ,8BAA8B;AAChE,SAASC,oBAAoB,QAAQ,iCAAiC;AACtE,SAASC,wBAAwB,QAAQ,qCAAqC;AAC9E,SAASC,4BAA4B,QAAQ,yCAAyC;AACtF,SAASC,uBAAuB,QAAQ,oCAAoC;AAC5E,SAASC,gBAAgB,QAAQ,6BAA6B;AAE9D,SAASC,uBAAuB,QAAQ,sCAAsC;AAE9E,SAASC,uBAAuB,QAAQ,oCAAoC;AAC5E,SAASC,yBAAyB,QAAQ,sCAAsC;AAChF,SAASC,wBAAwB,QAAQ,qCAAqC;AAC9E,SAASC,uBAAuB,QAAQ,oCAAoC;AAC5E,SAASC,wBAAwB,QAAQ,qCAAqC;AAG9E,SACEC,6BAA6B,EAC7BC,2BAA2B,EAC3BC,gBAAgB,EAChBC,gCAAgC,EAChCC,0BAA0B,QACrB,8BAA8B","ignoreList":[]}
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
 
3
+ import { formatDateForDisplay } from '../display/formatDateForDisplay';
4
+
3
5
  /** Overridable UI copy for date pickers, calendars, and segmented date fields. */
4
6
 
5
7
  export const DEFAULT_DATE_COMPONENT_LABELS = {
@@ -23,7 +25,11 @@ export const DEFAULT_DATE_COMPONENT_LABELS = {
23
25
  segmentMinute: 'Minute',
24
26
  segmentSecond: 'Second',
25
27
  segmentDayPeriod: 'AM/PM',
26
- rangeSeparator: '–'
28
+ segmentInvalid: '{segment}, invalid',
29
+ rangeSeparator: '–',
30
+ validationBeforeMin: 'Select a date on or after {date}',
31
+ validationAfterMax: 'Select a date on or before {date}',
32
+ validationUnavailable: 'This date is unavailable'
27
33
  };
28
34
  export function resolveDateComponentLabels(partial) {
29
35
  return {
@@ -34,6 +40,37 @@ export function resolveDateComponentLabels(partial) {
34
40
  export function formatSelectYear(labels, year) {
35
41
  return labels.selectYear.replace('{year}', String(year));
36
42
  }
43
+ const VALIDATION_LABEL_KEYS = {
44
+ beforeMin: 'validationBeforeMin',
45
+ afterMax: 'validationAfterMax',
46
+ unavailable: 'validationUnavailable'
47
+ };
48
+
49
+ /**
50
+ * Resolves the user-facing message for a {@link DateValidationReason}.
51
+ *
52
+ * `{date}` in the min / max messages is replaced with the bound, formatted for display
53
+ * with `options.locale` and `options.dateStyle` (defaults: `en-US`, `medium`). `DatePicker`
54
+ * already formats this message with its own `labels` and `locale` (see `onValidationChange`
55
+ * and `Field.Error`); call this directly only outside a picker, and pass the same `locale`.
56
+ *
57
+ * @param labels - Resolved labels (see `resolveDateComponentLabels`).
58
+ * @param reason - Reason returned by `getDateValidationReason` or `onValidationChange`.
59
+ * @param rules - The bounds the reason was computed against.
60
+ * @param options - Display options for the `{date}` placeholder.
61
+ * @returns The message to render in `Field.Error`.
62
+ */
63
+ export function formatDateValidationMessage(labels, reason, rules, options) {
64
+ const message = labels[VALIDATION_LABEL_KEYS[reason]];
65
+ const bound = reason === 'beforeMin' ? rules.minValue : reason === 'afterMax' ? rules.maxValue : undefined;
66
+ if (bound === undefined) {
67
+ return message;
68
+ }
69
+ return message.replace('{date}', formatDateForDisplay(bound, {
70
+ dateStyle: 'medium',
71
+ ...options
72
+ }));
73
+ }
37
74
  const SEGMENT_LABEL_KEYS = {
38
75
  year: 'segmentYear',
39
76
  month: 'segmentMonth',
@@ -43,8 +80,16 @@ const SEGMENT_LABEL_KEYS = {
43
80
  second: 'segmentSecond',
44
81
  dayPeriod: 'segmentDayPeriod'
45
82
  };
46
- export function getDateSegmentAccessibilityLabel(type, labels) {
83
+
84
+ /**
85
+ * @param invalid - Wraps the label in `labels.segmentInvalid`, for native platforms where
86
+ * `aria-invalid` is not announced.
87
+ */
88
+ export function getDateSegmentAccessibilityLabel(type, labels, invalid = false) {
47
89
  const key = SEGMENT_LABEL_KEYS[type];
48
- return key ? labels[key] : undefined;
90
+ if (!key) {
91
+ return undefined;
92
+ }
93
+ return invalid ? labels.segmentInvalid.replace('{segment}', labels[key]) : labels[key];
49
94
  }
50
95
  //# sourceMappingURL=dateComponentLabels.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["DEFAULT_DATE_COMPONENT_LABELS","openDatePicker","openDateTimePicker","openDateRangePicker","openMonthPicker","showCalendar","showYearPicker","showMonths","previousMonth","nextMonth","previousYear","nextYear","selectYear","dateFieldKeyboardInput","segmentYear","segmentMonth","segmentDay","segmentHour","segmentMinute","segmentSecond","segmentDayPeriod","rangeSeparator","resolveDateComponentLabels","partial","formatSelectYear","labels","year","replace","String","SEGMENT_LABEL_KEYS","month","day","hour","minute","second","dayPeriod","getDateSegmentAccessibilityLabel","type","key","undefined"],"sourceRoot":"../../../../src","sources":["dates/labels/dateComponentLabels.ts"],"mappings":";;AAEA;;AA0BA,OAAO,MAAMA,6BAAkD,GAAG;EAChEC,cAAc,EAAE,kBAAkB;EAClCC,kBAAkB,EAAE,uBAAuB;EAC3CC,mBAAmB,EAAE,wBAAwB;EAC7CC,eAAe,EAAE,mBAAmB;EACpCC,YAAY,EAAE,eAAe;EAC7BC,cAAc,EAAE,kBAAkB;EAClCC,UAAU,EAAE,aAAa;EACzBC,aAAa,EAAE,gBAAgB;EAC/BC,SAAS,EAAE,YAAY;EACvBC,YAAY,EAAE,eAAe;EAC7BC,QAAQ,EAAE,WAAW;EACrBC,UAAU,EAAE,oBAAoB;EAChCC,sBAAsB,EAAE,2BAA2B;EACnDC,WAAW,EAAE,MAAM;EACnBC,YAAY,EAAE,OAAO;EACrBC,UAAU,EAAE,KAAK;EACjBC,WAAW,EAAE,MAAM;EACnBC,aAAa,EAAE,QAAQ;EACvBC,aAAa,EAAE,QAAQ;EACvBC,gBAAgB,EAAE,OAAO;EACzBC,cAAc,EAAE;AAClB,CAAC;AAED,OAAO,SAASC,0BAA0BA,CACxCC,OAAsC,EACjB;EACrB,OAAO;IAAE,GAAGvB,6BAA6B;IAAE,GAAGuB;EAAQ,CAAC;AACzD;AAEA,OAAO,SAASC,gBAAgBA,CAACC,MAA2B,EAAEC,IAAY,EAAU;EAClF,OAAOD,MAAM,CAACb,UAAU,CAACe,OAAO,CAAC,QAAQ,EAAEC,MAAM,CAACF,IAAI,CAAC,CAAC;AAC1D;AAEA,MAAMG,kBAA+E,GAAG;EACtFH,IAAI,EAAE,aAAa;EACnBI,KAAK,EAAE,cAAc;EACrBC,GAAG,EAAE,YAAY;EACjBC,IAAI,EAAE,aAAa;EACnBC,MAAM,EAAE,eAAe;EACvBC,MAAM,EAAE,eAAe;EACvBC,SAAS,EAAE;AACb,CAAC;AAED,OAAO,SAASC,gCAAgCA,CAC9CC,IAAqB,EACrBZ,MAA2B,EACP;EACpB,MAAMa,GAAG,GAAGT,kBAAkB,CAACQ,IAAI,CAAC;EACpC,OAAOC,GAAG,GAAGb,MAAM,CAACa,GAAG,CAAC,GAAGC,SAAS;AACtC","ignoreList":[]}
1
+ {"version":3,"names":["formatDateForDisplay","DEFAULT_DATE_COMPONENT_LABELS","openDatePicker","openDateTimePicker","openDateRangePicker","openMonthPicker","showCalendar","showYearPicker","showMonths","previousMonth","nextMonth","previousYear","nextYear","selectYear","dateFieldKeyboardInput","segmentYear","segmentMonth","segmentDay","segmentHour","segmentMinute","segmentSecond","segmentDayPeriod","segmentInvalid","rangeSeparator","validationBeforeMin","validationAfterMax","validationUnavailable","resolveDateComponentLabels","partial","formatSelectYear","labels","year","replace","String","VALIDATION_LABEL_KEYS","beforeMin","afterMax","unavailable","formatDateValidationMessage","reason","rules","options","message","bound","minValue","maxValue","undefined","dateStyle","SEGMENT_LABEL_KEYS","month","day","hour","minute","second","dayPeriod","getDateSegmentAccessibilityLabel","type","invalid","key"],"sourceRoot":"../../../../src","sources":["dates/labels/dateComponentLabels.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ import { compareDateIds } from '../ranges/compareDateIds';
4
+ /**
5
+ * Checks a complete wire date against `minValue`, `maxValue`, and `isDateUnavailable`.
6
+ *
7
+ * Bounds are inclusive and are checked before the predicate, so a date outside
8
+ * the range reports `beforeMin` / `afterMax` even when it is also unavailable.
9
+ *
10
+ * @param date - Wire date to check.
11
+ * @param rules - Bounds and unavailable-date predicate.
12
+ * @returns The first rule the date violates, or `undefined` when it is selectable.
13
+ *
14
+ * @example
15
+ * getDateValidationReason(dateId('2026-08-01'), { minValue: dateId('2026-08-10') }) // 'beforeMin'
16
+ */
17
+ export function getDateValidationReason(date, rules) {
18
+ if (rules.minValue !== undefined && compareDateIds(date, rules.minValue) < 0) {
19
+ return 'beforeMin';
20
+ }
21
+ if (rules.maxValue !== undefined && compareDateIds(date, rules.maxValue) > 0) {
22
+ return 'afterMax';
23
+ }
24
+ if (rules.isDateUnavailable?.(date)) {
25
+ return 'unavailable';
26
+ }
27
+ return undefined;
28
+ }
29
+ //# sourceMappingURL=getDateValidationReason.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["compareDateIds","getDateValidationReason","date","rules","minValue","undefined","maxValue","isDateUnavailable"],"sourceRoot":"../../../../src","sources":["dates/validation/getDateValidationReason.ts"],"mappings":";;AAAA,SAASA,cAAc,QAAQ,0BAA0B;AAGzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,uBAAuBA,CACrCC,IAAY,EACZC,KAA0B,EACQ;EAClC,IAAIA,KAAK,CAACC,QAAQ,KAAKC,SAAS,IAAIL,cAAc,CAACE,IAAI,EAAEC,KAAK,CAACC,QAAQ,CAAC,GAAG,CAAC,EAAE;IAC5E,OAAO,WAAW;EACpB;EACA,IAAID,KAAK,CAACG,QAAQ,KAAKD,SAAS,IAAIL,cAAc,CAACE,IAAI,EAAEC,KAAK,CAACG,QAAQ,CAAC,GAAG,CAAC,EAAE;IAC5E,OAAO,UAAU;EACnB;EACA,IAAIH,KAAK,CAACI,iBAAiB,GAAGL,IAAI,CAAC,EAAE;IACnC,OAAO,aAAa;EACtB;EACA,OAAOG,SAAS;AAClB","ignoreList":[]}
@@ -12,6 +12,8 @@ formatDateForDisplay, formatDateRangeForDisplay, formatDateTimeForDisplay, forma
12
12
  compareDateIds, compareMonthIds, dateRangeAroundDay, dateRangeFromLastNDays, eachDateIdInRange, normalizeDateRange,
13
13
  // Segmented field helpers
14
14
  applyDateSegmentChange, formatDateSegment, getDateFieldSegments, getDateTimeFieldSegments, getDateTimeSegmentVisibility, getEmptyDateSegmentText, parseDateSegment,
15
+ // Validation
16
+ getDateValidationReason,
15
17
  // Calendar / list copy
16
- formatCalendarMonthYear, formatDateTimeListDateRow, formatDateTimeListHour, formatDateTimeListMinute, formatDateTimeListPeriod, formatMonthCalendarMonth, formatMonthCalendarYear, getCalendarWeekdayLabels, DEFAULT_DATE_COMPONENT_LABELS, formatSelectYear, getDateSegmentAccessibilityLabel, resolveDateComponentLabels } from './dates';
18
+ formatCalendarMonthYear, formatDateTimeListDateRow, formatDateTimeListHour, formatDateTimeListMinute, formatDateTimeListPeriod, formatMonthCalendarMonth, formatMonthCalendarYear, getCalendarWeekdayLabels, DEFAULT_DATE_COMPONENT_LABELS, formatDateValidationMessage, formatSelectYear, getDateSegmentAccessibilityLabel, resolveDateComponentLabels } from './dates';
17
19
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["formatNumber","clampDateParts","clampMonthParts","dateId","monthId","formatDateId","formatMonthId","isValidDateId","isValidDateParts","isValidMonthId","isValidMonthParts","parseDateId","parseMonthId","toMonthId","todayDateId","todayMonthId","clampDateTimeId","compareDateTimeIds","dateTimeId","formatDateTimeId","isLocaleHour12","isValidDateTimeId","nowDateTimeId","parseDateTimeId","formatDateForDisplay","formatDateRangeForDisplay","formatDateTimeForDisplay","formatMonthForDisplay","compareDateIds","compareMonthIds","dateRangeAroundDay","dateRangeFromLastNDays","eachDateIdInRange","normalizeDateRange","applyDateSegmentChange","formatDateSegment","getDateFieldSegments","getDateTimeFieldSegments","getDateTimeSegmentVisibility","getEmptyDateSegmentText","parseDateSegment","formatCalendarMonthYear","formatDateTimeListDateRow","formatDateTimeListHour","formatDateTimeListMinute","formatDateTimeListPeriod","formatMonthCalendarMonth","formatMonthCalendarYear","getCalendarWeekdayLabels","DEFAULT_DATE_COMPONENT_LABELS","formatSelectYear","getDateSegmentAccessibilityLabel","resolveDateComponentLabels"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAkC,gBAAgB;AAoBvE;AACE;AACAC,cAAc,EACdC,eAAe,EACfC,MAAM,EACNC,OAAO,EACPC,YAAY,EACZC,aAAa,EACbC,aAAa,EACbC,gBAAgB,EAChBC,cAAc,EACdC,iBAAiB,EACjBC,WAAW,EACXC,YAAY,EACZC,SAAS,EACTC,WAAW,EACXC,YAAY;AACZ;AACAC,eAAe,EACfC,kBAAkB,EAClBC,UAAU,EACVC,gBAAgB,EAChBC,cAAc,EACdC,iBAAiB,EACjBC,aAAa,EACbC,eAAe;AACf;AACAC,oBAAoB,EACpBC,yBAAyB,EACzBC,wBAAwB,EACxBC,qBAAqB;AACrB;AACAC,cAAc,EACdC,eAAe,EACfC,kBAAkB,EAClBC,sBAAsB,EACtBC,iBAAiB,EACjBC,kBAAkB;AAClB;AACAC,sBAAsB,EACtBC,iBAAiB,EACjBC,oBAAoB,EACpBC,wBAAwB,EACxBC,4BAA4B,EAC5BC,uBAAuB,EACvBC,gBAAgB;AAChB;AACAC,uBAAuB,EACvBC,yBAAyB,EACzBC,sBAAsB,EACtBC,wBAAwB,EACxBC,wBAAwB,EACxBC,wBAAwB,EACxBC,uBAAuB,EACvBC,wBAAwB,EACxBC,6BAA6B,EAC7BC,gBAAgB,EAChBC,gCAAgC,EAChCC,0BAA0B,QACrB,SAAS","ignoreList":[]}
1
+ {"version":3,"names":["formatNumber","clampDateParts","clampMonthParts","dateId","monthId","formatDateId","formatMonthId","isValidDateId","isValidDateParts","isValidMonthId","isValidMonthParts","parseDateId","parseMonthId","toMonthId","todayDateId","todayMonthId","clampDateTimeId","compareDateTimeIds","dateTimeId","formatDateTimeId","isLocaleHour12","isValidDateTimeId","nowDateTimeId","parseDateTimeId","formatDateForDisplay","formatDateRangeForDisplay","formatDateTimeForDisplay","formatMonthForDisplay","compareDateIds","compareMonthIds","dateRangeAroundDay","dateRangeFromLastNDays","eachDateIdInRange","normalizeDateRange","applyDateSegmentChange","formatDateSegment","getDateFieldSegments","getDateTimeFieldSegments","getDateTimeSegmentVisibility","getEmptyDateSegmentText","parseDateSegment","getDateValidationReason","formatCalendarMonthYear","formatDateTimeListDateRow","formatDateTimeListHour","formatDateTimeListMinute","formatDateTimeListPeriod","formatMonthCalendarMonth","formatMonthCalendarYear","getCalendarWeekdayLabels","DEFAULT_DATE_COMPONENT_LABELS","formatDateValidationMessage","formatSelectYear","getDateSegmentAccessibilityLabel","resolveDateComponentLabels"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAkC,gBAAgB;AAsBvE;AACE;AACAC,cAAc,EACdC,eAAe,EACfC,MAAM,EACNC,OAAO,EACPC,YAAY,EACZC,aAAa,EACbC,aAAa,EACbC,gBAAgB,EAChBC,cAAc,EACdC,iBAAiB,EACjBC,WAAW,EACXC,YAAY,EACZC,SAAS,EACTC,WAAW,EACXC,YAAY;AACZ;AACAC,eAAe,EACfC,kBAAkB,EAClBC,UAAU,EACVC,gBAAgB,EAChBC,cAAc,EACdC,iBAAiB,EACjBC,aAAa,EACbC,eAAe;AACf;AACAC,oBAAoB,EACpBC,yBAAyB,EACzBC,wBAAwB,EACxBC,qBAAqB;AACrB;AACAC,cAAc,EACdC,eAAe,EACfC,kBAAkB,EAClBC,sBAAsB,EACtBC,iBAAiB,EACjBC,kBAAkB;AAClB;AACAC,sBAAsB,EACtBC,iBAAiB,EACjBC,oBAAoB,EACpBC,wBAAwB,EACxBC,4BAA4B,EAC5BC,uBAAuB,EACvBC,gBAAgB;AAChB;AACAC,uBAAuB;AACvB;AACAC,uBAAuB,EACvBC,yBAAyB,EACzBC,sBAAsB,EACtBC,wBAAwB,EACxBC,wBAAwB,EACxBC,wBAAwB,EACxBC,uBAAuB,EACvBC,wBAAwB,EACxBC,6BAA6B,EAC7BC,2BAA2B,EAC3BC,gBAAgB,EAChBC,gCAAgC,EAChCC,0BAA0B,QACrB,SAAS","ignoreList":[]}
@@ -4,7 +4,7 @@
4
4
  * Wire ids (`DateId`, `MonthId`, `DateTimeId`) are fixed-format strings for state and APIs.
5
5
  * Display helpers return locale-dependent strings for UI labels — never store those in form state.
6
6
  */
7
- export type { DateFieldMode, DateFormatOptions, DateId, DateParts, DateRangeDisplayOptions, DateSegment, DateSegmentType, DateTimeGranularity, DateTimeId, DateTimeParts, FormatDateDisplayOptions, FormatDateTimeDisplayOptions, MonthId, MonthParts, TimeParts, } from './types';
7
+ export type { DateFieldMode, DateFormatOptions, DateId, DateParts, DateRangeDisplayOptions, DateSegment, DateSegmentType, DateTimeGranularity, DateTimeId, DateTimeParts, DateValidationReason, DateValidationRules, FormatDateDisplayOptions, FormatDateTimeDisplayOptions, MonthId, MonthParts, TimeParts, } from './types';
8
8
  export { clampDateParts } from './wire/clampDateParts';
9
9
  export { clampMonthParts } from './wire/clampMonthParts';
10
10
  export { dateId } from './wire/dateId';
@@ -48,11 +48,12 @@ export { getDateTimeFieldSegments } from './segments/getDateTimeFieldSegments';
48
48
  export { getDateTimeSegmentVisibility } from './segments/getDateTimeSegmentVisibility';
49
49
  export { getEmptyDateSegmentText } from './segments/getEmptyDateSegmentText';
50
50
  export { parseDateSegment } from './segments/parseDateSegment';
51
+ export { getDateValidationReason } from './validation/getDateValidationReason';
51
52
  export { formatCalendarMonthYear } from './calendar/formatCalendarMonthYear';
52
53
  export { formatDateTimeListDateRow } from './calendar/formatDateTimeListDateRow';
53
54
  export { formatMonthCalendarMonth } from './calendar/formatMonthCalendarMonth';
54
55
  export { formatMonthCalendarYear } from './calendar/formatMonthCalendarYear';
55
56
  export { getCalendarWeekdayLabels } from './calendar/getCalendarWeekdayLabels';
56
57
  export type { DateComponentLabels } from './labels/dateComponentLabels';
57
- export { DEFAULT_DATE_COMPONENT_LABELS, formatSelectYear, getDateSegmentAccessibilityLabel, resolveDateComponentLabels, } from './labels/dateComponentLabels';
58
+ export { DEFAULT_DATE_COMPONENT_LABELS, formatDateValidationMessage, formatSelectYear, getDateSegmentAccessibilityLabel, resolveDateComponentLabels, } from './labels/dateComponentLabels';
58
59
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dates/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,MAAM,EACN,SAAS,EACT,uBAAuB,EACvB,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,aAAa,EACb,wBAAwB,EACxB,4BAA4B,EAC5B,OAAO,EACP,UAAU,EACV,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAExE,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EAAE,4BAA4B,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAE/E,YAAY,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,6BAA6B,EAC7B,gBAAgB,EAChB,gCAAgC,EAChC,0BAA0B,GAC3B,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dates/index.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { DateSegmentType } from '../types';
1
+ import type { DateSegmentType, DateValidationReason, DateValidationRules, FormatDateDisplayOptions } from '../types';
2
2
  /** Overridable UI copy for date pickers, calendars, and segmented date fields. */
3
3
  export interface DateComponentLabels {
4
4
  openDatePicker: string;
@@ -22,10 +22,39 @@ export interface DateComponentLabels {
22
22
  segmentMinute: string;
23
23
  segmentSecond: string;
24
24
  segmentDayPeriod: string;
25
+ /**
26
+ * Native accessible name of a segment while the field is invalid — iOS and Android have no
27
+ * equivalent of `aria-invalid`. Use `{segment}` as the segment label placeholder.
28
+ */
29
+ segmentInvalid: string;
25
30
  rangeSeparator: string;
31
+ /** Use `{date}` as the `minValue` placeholder. */
32
+ validationBeforeMin: string;
33
+ /** Use `{date}` as the `maxValue` placeholder. */
34
+ validationAfterMax: string;
35
+ validationUnavailable: string;
26
36
  }
27
37
  export declare const DEFAULT_DATE_COMPONENT_LABELS: DateComponentLabels;
28
38
  export declare function resolveDateComponentLabels(partial?: Partial<DateComponentLabels>): DateComponentLabels;
29
39
  export declare function formatSelectYear(labels: DateComponentLabels, year: number): string;
30
- export declare function getDateSegmentAccessibilityLabel(type: DateSegmentType, labels: DateComponentLabels): string | undefined;
40
+ /**
41
+ * Resolves the user-facing message for a {@link DateValidationReason}.
42
+ *
43
+ * `{date}` in the min / max messages is replaced with the bound, formatted for display
44
+ * with `options.locale` and `options.dateStyle` (defaults: `en-US`, `medium`). `DatePicker`
45
+ * already formats this message with its own `labels` and `locale` (see `onValidationChange`
46
+ * and `Field.Error`); call this directly only outside a picker, and pass the same `locale`.
47
+ *
48
+ * @param labels - Resolved labels (see `resolveDateComponentLabels`).
49
+ * @param reason - Reason returned by `getDateValidationReason` or `onValidationChange`.
50
+ * @param rules - The bounds the reason was computed against.
51
+ * @param options - Display options for the `{date}` placeholder.
52
+ * @returns The message to render in `Field.Error`.
53
+ */
54
+ export declare function formatDateValidationMessage(labels: DateComponentLabels, reason: DateValidationReason, rules: Pick<DateValidationRules, 'minValue' | 'maxValue'>, options?: FormatDateDisplayOptions): string;
55
+ /**
56
+ * @param invalid - Wraps the label in `labels.segmentInvalid`, for native platforms where
57
+ * `aria-invalid` is not announced.
58
+ */
59
+ export declare function getDateSegmentAccessibilityLabel(type: DateSegmentType, labels: DateComponentLabels, invalid?: boolean): string | undefined;
31
60
  //# sourceMappingURL=dateComponentLabels.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dateComponentLabels.d.ts","sourceRoot":"","sources":["../../../../src/dates/labels/dateComponentLabels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,6BAA6B,EAAE,mBAsB3C,CAAC;AAEF,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GACrC,mBAAmB,CAErB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAElF;AAYD,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,eAAe,EACrB,MAAM,EAAE,mBAAmB,GAC1B,MAAM,GAAG,SAAS,CAGpB"}
1
+ {"version":3,"file":"dateComponentLabels.d.ts","sourceRoot":"","sources":["../../../../src/dates/labels/dateComponentLabels.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACzB,MAAM,UAAU,CAAC;AAElB,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kDAAkD;IAClD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,6BAA6B,EAAE,mBA0B3C,CAAC;AAEF,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GACrC,mBAAmB,CAErB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAElF;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,mBAAmB,EAC3B,MAAM,EAAE,oBAAoB,EAC5B,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,UAAU,CAAC,EACzD,OAAO,CAAC,EAAE,wBAAwB,GACjC,MAAM,CAWR;AAYD;;;GAGG;AACH,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,eAAe,EACrB,MAAM,EAAE,mBAAmB,EAC3B,OAAO,UAAQ,GACd,MAAM,GAAG,SAAS,CAMpB"}
@@ -39,6 +39,17 @@ export interface TimeParts {
39
39
  export interface DateTimeParts extends DateParts, TimeParts {
40
40
  }
41
41
  export type DateFieldMode = 'date' | 'month';
42
+ /** Why a complete date fails its selection rules. */
43
+ export type DateValidationReason = 'beforeMin' | 'afterMax' | 'unavailable';
44
+ /** Selection rules shared by `Calendar`, `DatePicker`, and `getDateValidationReason`. */
45
+ export interface DateValidationRules {
46
+ /** Earliest valid date (inclusive). */
47
+ minValue?: DateId;
48
+ /** Latest valid date (inclusive). */
49
+ maxValue?: DateId;
50
+ /** Returns `true` for dates that cannot be selected, such as weekends or holidays. */
51
+ isDateUnavailable?: (date: DateId) => boolean;
52
+ }
42
53
  export type DateTimeGranularity = 'day' | 'hour' | 'minute' | 'second';
43
54
  export type DateSegmentType = 'era' | 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'dayPeriod' | 'literal';
44
55
  export interface DateSegment {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/dates/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,qFAAqF;AACrF,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;AAC7D,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAA;CAAE,CAAC;AAC/D,oGAAoG;AACpG,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC;AAErE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAc,SAAQ,SAAS,EAAE,SAAS;CAAG;AAE9D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,CAAC;AAE7C,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvE,MAAM,MAAM,eAAe,GACvB,KAAK,GACL,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,4BAA6B,SAAQ,iBAAiB;IACrE,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACxC,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC/B,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAED,MAAM,WAAW,uBAAwB,SAAQ,wBAAwB;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/dates/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,qFAAqF;AACrF,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;AAC7D,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAA;CAAE,CAAC;AAC/D,oGAAoG;AACpG,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC;AAErE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAc,SAAQ,SAAS,EAAE,SAAS;CAAG;AAE9D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,CAAC;AAE7C,qDAAqD;AACrD,MAAM,MAAM,oBAAoB,GAAG,WAAW,GAAG,UAAU,GAAG,aAAa,CAAC;AAE5E,yFAAyF;AACzF,MAAM,WAAW,mBAAmB;IAClC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC/C;AAED,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvE,MAAM,MAAM,eAAe,GACvB,KAAK,GACL,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,4BAA6B,SAAQ,iBAAiB;IACrE,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACxC,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC/B,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAED,MAAM,WAAW,uBAAwB,SAAQ,wBAAwB;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
@@ -0,0 +1,16 @@
1
+ import type { DateId, DateValidationReason, DateValidationRules } from '../types';
2
+ /**
3
+ * Checks a complete wire date against `minValue`, `maxValue`, and `isDateUnavailable`.
4
+ *
5
+ * Bounds are inclusive and are checked before the predicate, so a date outside
6
+ * the range reports `beforeMin` / `afterMax` even when it is also unavailable.
7
+ *
8
+ * @param date - Wire date to check.
9
+ * @param rules - Bounds and unavailable-date predicate.
10
+ * @returns The first rule the date violates, or `undefined` when it is selectable.
11
+ *
12
+ * @example
13
+ * getDateValidationReason(dateId('2026-08-01'), { minValue: dateId('2026-08-10') }) // 'beforeMin'
14
+ */
15
+ export declare function getDateValidationReason(date: DateId, rules: DateValidationRules): DateValidationReason | undefined;
16
+ //# sourceMappingURL=getDateValidationReason.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getDateValidationReason.d.ts","sourceRoot":"","sources":["../../../../src/dates/validation/getDateValidationReason.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAElF;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,mBAAmB,GACzB,oBAAoB,GAAG,SAAS,CAWlC"}
@@ -1,5 +1,5 @@
1
1
  export { formatNumber, type FormatNumberOptions } from './formatNumber';
2
- export type { DateFieldMode, DateFormatOptions, DateId, DateParts, DateRangeDisplayOptions, DateSegment, DateSegmentType, DateTimeGranularity, DateTimeId, DateTimeParts, FormatDateDisplayOptions, FormatDateTimeDisplayOptions, MonthId, MonthParts, TimeParts, } from './dates/types';
3
- export { clampDateParts, clampMonthParts, dateId, monthId, formatDateId, formatMonthId, isValidDateId, isValidDateParts, isValidMonthId, isValidMonthParts, parseDateId, parseMonthId, toMonthId, todayDateId, todayMonthId, clampDateTimeId, compareDateTimeIds, dateTimeId, formatDateTimeId, isLocaleHour12, isValidDateTimeId, nowDateTimeId, parseDateTimeId, formatDateForDisplay, formatDateRangeForDisplay, formatDateTimeForDisplay, formatMonthForDisplay, compareDateIds, compareMonthIds, dateRangeAroundDay, dateRangeFromLastNDays, eachDateIdInRange, normalizeDateRange, applyDateSegmentChange, formatDateSegment, getDateFieldSegments, getDateTimeFieldSegments, getDateTimeSegmentVisibility, getEmptyDateSegmentText, parseDateSegment, formatCalendarMonthYear, formatDateTimeListDateRow, formatDateTimeListHour, formatDateTimeListMinute, formatDateTimeListPeriod, formatMonthCalendarMonth, formatMonthCalendarYear, getCalendarWeekdayLabels, DEFAULT_DATE_COMPONENT_LABELS, formatSelectYear, getDateSegmentAccessibilityLabel, resolveDateComponentLabels, } from './dates';
2
+ export type { DateFieldMode, DateFormatOptions, DateId, DateParts, DateRangeDisplayOptions, DateSegment, DateSegmentType, DateTimeGranularity, DateTimeId, DateTimeParts, DateValidationReason, DateValidationRules, FormatDateDisplayOptions, FormatDateTimeDisplayOptions, MonthId, MonthParts, TimeParts, } from './dates/types';
3
+ export { clampDateParts, clampMonthParts, dateId, monthId, formatDateId, formatMonthId, isValidDateId, isValidDateParts, isValidMonthId, isValidMonthParts, parseDateId, parseMonthId, toMonthId, todayDateId, todayMonthId, clampDateTimeId, compareDateTimeIds, dateTimeId, formatDateTimeId, isLocaleHour12, isValidDateTimeId, nowDateTimeId, parseDateTimeId, formatDateForDisplay, formatDateRangeForDisplay, formatDateTimeForDisplay, formatMonthForDisplay, compareDateIds, compareMonthIds, dateRangeAroundDay, dateRangeFromLastNDays, eachDateIdInRange, normalizeDateRange, applyDateSegmentChange, formatDateSegment, getDateFieldSegments, getDateTimeFieldSegments, getDateTimeSegmentVisibility, getEmptyDateSegmentText, parseDateSegment, getDateValidationReason, formatCalendarMonthYear, formatDateTimeListDateRow, formatDateTimeListHour, formatDateTimeListMinute, formatDateTimeListPeriod, formatMonthCalendarMonth, formatMonthCalendarYear, getCalendarWeekdayLabels, DEFAULT_DATE_COMPONENT_LABELS, formatDateValidationMessage, formatSelectYear, getDateSegmentAccessibilityLabel, resolveDateComponentLabels, } from './dates';
4
4
  export type { DateComponentLabels } from './dates';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAExE,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,MAAM,EACN,SAAS,EACT,uBAAuB,EACvB,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,aAAa,EACb,wBAAwB,EACxB,4BAA4B,EAC5B,OAAO,EACP,UAAU,EACV,SAAS,GACV,MAAM,eAAe,CAAC;AAEvB,OAAO,EAEL,cAAc,EACd,eAAe,EACf,MAAM,EACN,OAAO,EACP,YAAY,EACZ,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,SAAS,EACT,WAAW,EACX,YAAY,EAEZ,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,eAAe,EAEf,oBAAoB,EACpB,yBAAyB,EACzB,wBAAwB,EACxB,qBAAqB,EAErB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,EAElB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,4BAA4B,EAC5B,uBAAuB,EACvB,gBAAgB,EAEhB,uBAAuB,EACvB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,6BAA6B,EAC7B,gBAAgB,EAChB,gCAAgC,EAChC,0BAA0B,GAC3B,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAExE,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,MAAM,EACN,SAAS,EACT,uBAAuB,EACvB,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,4BAA4B,EAC5B,OAAO,EACP,UAAU,EACV,SAAS,GACV,MAAM,eAAe,CAAC;AAEvB,OAAO,EAEL,cAAc,EACd,eAAe,EACf,MAAM,EACN,OAAO,EACP,YAAY,EACZ,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,SAAS,EACT,WAAW,EACX,YAAY,EAEZ,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,eAAe,EAEf,oBAAoB,EACpB,yBAAyB,EACzB,wBAAwB,EACxB,qBAAqB,EAErB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,EAElB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,4BAA4B,EAC5B,uBAAuB,EACvB,gBAAgB,EAEhB,uBAAuB,EAEvB,uBAAuB,EACvB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,6BAA6B,EAC7B,2BAA2B,EAC3B,gBAAgB,EAChB,gCAAgC,EAChC,0BAA0B,GAC3B,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdx-ui/formatters",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,5 +1,7 @@
1
+ import type { DateId } from '../dates/types';
1
2
  import {
2
3
  DEFAULT_DATE_COMPONENT_LABELS,
4
+ formatDateValidationMessage,
3
5
  formatSelectYear,
4
6
  getDateSegmentAccessibilityLabel,
5
7
  resolveDateComponentLabels,
@@ -22,4 +24,57 @@ describe('dateComponentLabels', () => {
22
24
  expect(getDateSegmentAccessibilityLabel('month', labels)).toBe('Month');
23
25
  expect(getDateSegmentAccessibilityLabel('literal', labels)).toBeUndefined();
24
26
  });
27
+
28
+ it('wraps segment labels in segmentInvalid when invalid', () => {
29
+ expect(getDateSegmentAccessibilityLabel('day', resolveDateComponentLabels(), true)).toBe(
30
+ 'Day, invalid',
31
+ );
32
+ const labels = resolveDateComponentLabels({
33
+ segmentDay: 'Jour',
34
+ segmentInvalid: '{segment} invalide',
35
+ });
36
+ expect(getDateSegmentAccessibilityLabel('day', labels, true)).toBe('Jour invalide');
37
+ expect(getDateSegmentAccessibilityLabel('literal', labels, true)).toBeUndefined();
38
+ });
39
+
40
+ describe('formatDateValidationMessage', () => {
41
+ const rules = { minValue: '2026-08-10' as DateId, maxValue: '2026-08-20' as DateId };
42
+
43
+ it('fills the {date} placeholder with the formatted bound', () => {
44
+ const labels = resolveDateComponentLabels();
45
+ expect(formatDateValidationMessage(labels, 'beforeMin', rules)).toBe(
46
+ 'Select a date on or after Aug 10, 2026',
47
+ );
48
+ expect(formatDateValidationMessage(labels, 'afterMax', rules)).toBe(
49
+ 'Select a date on or before Aug 20, 2026',
50
+ );
51
+ });
52
+
53
+ it('formats the bound in options.locale', () => {
54
+ const labels = resolveDateComponentLabels({ validationBeforeMin: 'À partir du {date}' });
55
+ expect(formatDateValidationMessage(labels, 'beforeMin', rules, { locale: 'fr-FR' })).toBe(
56
+ 'À partir du 10 août 2026',
57
+ );
58
+ });
59
+
60
+ it('returns the unavailable message as-is', () => {
61
+ const labels = resolveDateComponentLabels();
62
+ expect(formatDateValidationMessage(labels, 'unavailable', rules)).toBe(
63
+ 'This date is unavailable',
64
+ );
65
+ });
66
+
67
+ it('uses label overrides and display options', () => {
68
+ const labels = resolveDateComponentLabels({
69
+ validationBeforeMin: 'Pick {date} or later',
70
+ validationUnavailable: 'Payments do not process on this date',
71
+ });
72
+ expect(formatDateValidationMessage(labels, 'beforeMin', rules, { dateStyle: 'short' })).toBe(
73
+ 'Pick 8/10/26 or later',
74
+ );
75
+ expect(formatDateValidationMessage(labels, 'unavailable', rules)).toBe(
76
+ 'Payments do not process on this date',
77
+ );
78
+ });
79
+ });
25
80
  });
@@ -0,0 +1,45 @@
1
+ import { getDateValidationReason } from '../dates/validation/getDateValidationReason';
2
+ import type { DateId } from '../dates/types';
3
+
4
+ const isWeekend = (date: DateId) => {
5
+ const day = new Date(`${date}T00:00:00`).getDay();
6
+ return day === 0 || day === 6;
7
+ };
8
+
9
+ describe('getDateValidationReason', () => {
10
+ const rules = {
11
+ minValue: '2026-08-10' as DateId,
12
+ maxValue: '2026-08-20' as DateId,
13
+ isDateUnavailable: isWeekend,
14
+ };
15
+
16
+ it('returns beforeMin for a date before minValue', () => {
17
+ expect(getDateValidationReason('2026-08-03' as DateId, rules)).toBe('beforeMin');
18
+ });
19
+
20
+ it('returns afterMax for a date after maxValue', () => {
21
+ expect(getDateValidationReason('2026-08-24' as DateId, rules)).toBe('afterMax');
22
+ });
23
+
24
+ it('returns unavailable when the predicate returns true', () => {
25
+ expect(getDateValidationReason('2026-08-15' as DateId, rules)).toBe('unavailable');
26
+ });
27
+
28
+ it('returns undefined for a selectable date', () => {
29
+ expect(getDateValidationReason('2026-08-14' as DateId, rules)).toBeUndefined();
30
+ });
31
+
32
+ it('treats bounds as inclusive', () => {
33
+ expect(getDateValidationReason('2026-08-10' as DateId, rules)).toBeUndefined();
34
+ expect(getDateValidationReason('2026-08-20' as DateId, rules)).toBeUndefined();
35
+ });
36
+
37
+ it('reports bounds before the predicate when both apply', () => {
38
+ expect(getDateValidationReason('2026-08-08' as DateId, rules)).toBe('beforeMin');
39
+ expect(getDateValidationReason('2026-08-22' as DateId, rules)).toBe('afterMax');
40
+ });
41
+
42
+ it('returns undefined when no rules are set', () => {
43
+ expect(getDateValidationReason('2026-08-15' as DateId, {})).toBeUndefined();
44
+ });
45
+ });
@@ -16,6 +16,8 @@ export type {
16
16
  DateTimeGranularity,
17
17
  DateTimeId,
18
18
  DateTimeParts,
19
+ DateValidationReason,
20
+ DateValidationRules,
19
21
  FormatDateDisplayOptions,
20
22
  FormatDateTimeDisplayOptions,
21
23
  MonthId,
@@ -71,6 +73,8 @@ export { getDateTimeSegmentVisibility } from './segments/getDateTimeSegmentVisib
71
73
  export { getEmptyDateSegmentText } from './segments/getEmptyDateSegmentText';
72
74
  export { parseDateSegment } from './segments/parseDateSegment';
73
75
 
76
+ export { getDateValidationReason } from './validation/getDateValidationReason';
77
+
74
78
  export { formatCalendarMonthYear } from './calendar/formatCalendarMonthYear';
75
79
  export { formatDateTimeListDateRow } from './calendar/formatDateTimeListDateRow';
76
80
  export { formatMonthCalendarMonth } from './calendar/formatMonthCalendarMonth';
@@ -80,6 +84,7 @@ export { getCalendarWeekdayLabels } from './calendar/getCalendarWeekdayLabels';
80
84
  export type { DateComponentLabels } from './labels/dateComponentLabels';
81
85
  export {
82
86
  DEFAULT_DATE_COMPONENT_LABELS,
87
+ formatDateValidationMessage,
83
88
  formatSelectYear,
84
89
  getDateSegmentAccessibilityLabel,
85
90
  resolveDateComponentLabels,
@@ -1,4 +1,10 @@
1
- import type { DateSegmentType } from '../types';
1
+ import { formatDateForDisplay } from '../display/formatDateForDisplay';
2
+ import type {
3
+ DateSegmentType,
4
+ DateValidationReason,
5
+ DateValidationRules,
6
+ FormatDateDisplayOptions,
7
+ } from '../types';
2
8
 
3
9
  /** Overridable UI copy for date pickers, calendars, and segmented date fields. */
4
10
  export interface DateComponentLabels {
@@ -23,7 +29,17 @@ export interface DateComponentLabels {
23
29
  segmentMinute: string;
24
30
  segmentSecond: string;
25
31
  segmentDayPeriod: string;
32
+ /**
33
+ * Native accessible name of a segment while the field is invalid — iOS and Android have no
34
+ * equivalent of `aria-invalid`. Use `{segment}` as the segment label placeholder.
35
+ */
36
+ segmentInvalid: string;
26
37
  rangeSeparator: string;
38
+ /** Use `{date}` as the `minValue` placeholder. */
39
+ validationBeforeMin: string;
40
+ /** Use `{date}` as the `maxValue` placeholder. */
41
+ validationAfterMax: string;
42
+ validationUnavailable: string;
27
43
  }
28
44
 
29
45
  export const DEFAULT_DATE_COMPONENT_LABELS: DateComponentLabels = {
@@ -47,7 +63,11 @@ export const DEFAULT_DATE_COMPONENT_LABELS: DateComponentLabels = {
47
63
  segmentMinute: 'Minute',
48
64
  segmentSecond: 'Second',
49
65
  segmentDayPeriod: 'AM/PM',
66
+ segmentInvalid: '{segment}, invalid',
50
67
  rangeSeparator: '–',
68
+ validationBeforeMin: 'Select a date on or after {date}',
69
+ validationAfterMax: 'Select a date on or before {date}',
70
+ validationUnavailable: 'This date is unavailable',
51
71
  };
52
72
 
53
73
  export function resolveDateComponentLabels(
@@ -60,6 +80,44 @@ export function formatSelectYear(labels: DateComponentLabels, year: number): str
60
80
  return labels.selectYear.replace('{year}', String(year));
61
81
  }
62
82
 
83
+ const VALIDATION_LABEL_KEYS: Record<DateValidationReason, keyof DateComponentLabels> = {
84
+ beforeMin: 'validationBeforeMin',
85
+ afterMax: 'validationAfterMax',
86
+ unavailable: 'validationUnavailable',
87
+ };
88
+
89
+ /**
90
+ * Resolves the user-facing message for a {@link DateValidationReason}.
91
+ *
92
+ * `{date}` in the min / max messages is replaced with the bound, formatted for display
93
+ * with `options.locale` and `options.dateStyle` (defaults: `en-US`, `medium`). `DatePicker`
94
+ * already formats this message with its own `labels` and `locale` (see `onValidationChange`
95
+ * and `Field.Error`); call this directly only outside a picker, and pass the same `locale`.
96
+ *
97
+ * @param labels - Resolved labels (see `resolveDateComponentLabels`).
98
+ * @param reason - Reason returned by `getDateValidationReason` or `onValidationChange`.
99
+ * @param rules - The bounds the reason was computed against.
100
+ * @param options - Display options for the `{date}` placeholder.
101
+ * @returns The message to render in `Field.Error`.
102
+ */
103
+ export function formatDateValidationMessage(
104
+ labels: DateComponentLabels,
105
+ reason: DateValidationReason,
106
+ rules: Pick<DateValidationRules, 'minValue' | 'maxValue'>,
107
+ options?: FormatDateDisplayOptions,
108
+ ): string {
109
+ const message = labels[VALIDATION_LABEL_KEYS[reason]];
110
+ const bound =
111
+ reason === 'beforeMin' ? rules.minValue : reason === 'afterMax' ? rules.maxValue : undefined;
112
+ if (bound === undefined) {
113
+ return message;
114
+ }
115
+ return message.replace(
116
+ '{date}',
117
+ formatDateForDisplay(bound, { dateStyle: 'medium', ...options }),
118
+ );
119
+ }
120
+
63
121
  const SEGMENT_LABEL_KEYS: Partial<Record<DateSegmentType, keyof DateComponentLabels>> = {
64
122
  year: 'segmentYear',
65
123
  month: 'segmentMonth',
@@ -70,10 +128,18 @@ const SEGMENT_LABEL_KEYS: Partial<Record<DateSegmentType, keyof DateComponentLab
70
128
  dayPeriod: 'segmentDayPeriod',
71
129
  };
72
130
 
131
+ /**
132
+ * @param invalid - Wraps the label in `labels.segmentInvalid`, for native platforms where
133
+ * `aria-invalid` is not announced.
134
+ */
73
135
  export function getDateSegmentAccessibilityLabel(
74
136
  type: DateSegmentType,
75
137
  labels: DateComponentLabels,
138
+ invalid = false,
76
139
  ): string | undefined {
77
140
  const key = SEGMENT_LABEL_KEYS[type];
78
- return key ? labels[key] : undefined;
141
+ if (!key) {
142
+ return undefined;
143
+ }
144
+ return invalid ? labels.segmentInvalid.replace('{segment}', labels[key]) : labels[key];
79
145
  }
@@ -39,6 +39,19 @@ export interface DateTimeParts extends DateParts, TimeParts {}
39
39
 
40
40
  export type DateFieldMode = 'date' | 'month';
41
41
 
42
+ /** Why a complete date fails its selection rules. */
43
+ export type DateValidationReason = 'beforeMin' | 'afterMax' | 'unavailable';
44
+
45
+ /** Selection rules shared by `Calendar`, `DatePicker`, and `getDateValidationReason`. */
46
+ export interface DateValidationRules {
47
+ /** Earliest valid date (inclusive). */
48
+ minValue?: DateId;
49
+ /** Latest valid date (inclusive). */
50
+ maxValue?: DateId;
51
+ /** Returns `true` for dates that cannot be selected, such as weekends or holidays. */
52
+ isDateUnavailable?: (date: DateId) => boolean;
53
+ }
54
+
42
55
  export type DateTimeGranularity = 'day' | 'hour' | 'minute' | 'second';
43
56
 
44
57
  export type DateSegmentType =
@@ -0,0 +1,31 @@
1
+ import { compareDateIds } from '../ranges/compareDateIds';
2
+ import type { DateId, DateValidationReason, DateValidationRules } from '../types';
3
+
4
+ /**
5
+ * Checks a complete wire date against `minValue`, `maxValue`, and `isDateUnavailable`.
6
+ *
7
+ * Bounds are inclusive and are checked before the predicate, so a date outside
8
+ * the range reports `beforeMin` / `afterMax` even when it is also unavailable.
9
+ *
10
+ * @param date - Wire date to check.
11
+ * @param rules - Bounds and unavailable-date predicate.
12
+ * @returns The first rule the date violates, or `undefined` when it is selectable.
13
+ *
14
+ * @example
15
+ * getDateValidationReason(dateId('2026-08-01'), { minValue: dateId('2026-08-10') }) // 'beforeMin'
16
+ */
17
+ export function getDateValidationReason(
18
+ date: DateId,
19
+ rules: DateValidationRules,
20
+ ): DateValidationReason | undefined {
21
+ if (rules.minValue !== undefined && compareDateIds(date, rules.minValue) < 0) {
22
+ return 'beforeMin';
23
+ }
24
+ if (rules.maxValue !== undefined && compareDateIds(date, rules.maxValue) > 0) {
25
+ return 'afterMax';
26
+ }
27
+ if (rules.isDateUnavailable?.(date)) {
28
+ return 'unavailable';
29
+ }
30
+ return undefined;
31
+ }
package/src/index.ts CHANGED
@@ -11,6 +11,8 @@ export type {
11
11
  DateTimeGranularity,
12
12
  DateTimeId,
13
13
  DateTimeParts,
14
+ DateValidationReason,
15
+ DateValidationRules,
14
16
  FormatDateDisplayOptions,
15
17
  FormatDateTimeDisplayOptions,
16
18
  MonthId,
@@ -64,6 +66,8 @@ export {
64
66
  getDateTimeSegmentVisibility,
65
67
  getEmptyDateSegmentText,
66
68
  parseDateSegment,
69
+ // Validation
70
+ getDateValidationReason,
67
71
  // Calendar / list copy
68
72
  formatCalendarMonthYear,
69
73
  formatDateTimeListDateRow,
@@ -74,6 +78,7 @@ export {
74
78
  formatMonthCalendarYear,
75
79
  getCalendarWeekdayLabels,
76
80
  DEFAULT_DATE_COMPONENT_LABELS,
81
+ formatDateValidationMessage,
77
82
  formatSelectYear,
78
83
  getDateSegmentAccessibilityLabel,
79
84
  resolveDateComponentLabels,