@viasat/beam-react 2.47.1 → 2.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,204 +1,227 @@
1
- import { jsxs as S, jsx as l } from "react/jsx-runtime";
2
- import ve from "clsx";
3
- import { forwardRef as De } from "react";
4
- import { s as a, b as Fe, c as s, a as he } from "../../../chunks/DateField.Segment.C2qoaY0Z.js";
5
- import { t as xe } from "../../../chunks/constants.Cruz2A1w.js";
6
- import { H as ye } from "../../../chunks/form.context.cEuTL68U.js";
7
- import { LabelProvider as Se } from "../../Form/contexts/label.context.es.js";
8
- import { HelperTextProvider as Ie } from "../../Form/contexts/helperText.context.es.js";
9
- import { useUniversalRef as we } from "../../Form/hooks/useUniversalRef.hook.es.js";
10
- import { DateFieldSegments as Re, PARTS_ORDER as I } from "./DateField.Segments.es.js";
11
- import { toISODateString as Ne } from "./DateField.utils.es.js";
12
- import { useDateFieldFocus as Ve } from "./useDateFieldFocus.es.js";
13
- import { useDateFieldForm as Be } from "./useDateFieldForm.es.js";
14
- import { useDateFieldKeyboard as Ce } from "./useDateFieldKeyboard.es.js";
15
- import { useDateFieldSegments as Oe } from "./useDateFieldSegments.es.js";
16
- import { useDateFieldValidity as Pe } from "./useDateFieldValidity.es.js";
17
- import { useDateFieldValue as Te } from "./useDateFieldValue.es.js";
18
- import { useInvalidState as Me } from "./useInvalidState.es.js";
19
- const ea = De(
1
+ import { jsxs as N, jsx as r } from "react/jsx-runtime";
2
+ import xe from "clsx";
3
+ import { forwardRef as Ie, useEffect as Ne } from "react";
4
+ import { s as a, b as we, c as s, a as w } from "../../../chunks/DateField.Segment.BVe6TzWY.js";
5
+ import { t as Re } from "../../../chunks/constants.Cruz2A1w.js";
6
+ import { H as Ve } from "../../../chunks/form.context.cEuTL68U.js";
7
+ import { LabelProvider as Me } from "../../Form/contexts/label.context.es.js";
8
+ import { HelperTextProvider as Oe } from "../../Form/contexts/helperText.context.es.js";
9
+ import { useUniversalRef as Be } from "../../Form/hooks/useUniversalRef.hook.es.js";
10
+ import { DateFieldSegments as Ce, PARTS_ORDER as R } from "./DateField.Segments.es.js";
11
+ import { isMinMaxMisconfigured as Ee, dateKeyOrNull as V, toISODateString as Pe } from "./DateField.utils.es.js";
12
+ import { useDateFieldFocus as Te } from "./useDateFieldFocus.es.js";
13
+ import { useDateFieldForm as qe } from "./useDateFieldForm.es.js";
14
+ import { useDateFieldKeyboard as He } from "./useDateFieldKeyboard.es.js";
15
+ import { useDateFieldSegments as Ke } from "./useDateFieldSegments.es.js";
16
+ import { useDateFieldValidity as Le } from "./useDateFieldValidity.es.js";
17
+ import { useDateFieldValue as $e } from "./useDateFieldValue.es.js";
18
+ import { useInvalidState as je } from "./useInvalidState.es.js";
19
+ const da = Ie(
20
20
  function({
21
- value: w,
22
- defaultValue: R,
23
- onChange: N,
24
- label: t = null,
25
- helperText: o = null,
26
- error: n,
27
- hideRequiredMarker: V = !1,
28
- validationRules: B,
29
- width: C,
30
- theme: m,
31
- fluid: O = !1,
32
- disabled: P = !1,
33
- readOnly: T = !1,
34
- required: M = !1,
35
- name: u,
36
- id: q,
37
- className: E,
38
- style: H,
39
- "aria-label": L,
40
- "aria-describedby": $,
41
- "aria-labelledby": j,
42
- ...K
43
- }, W) {
21
+ value: M,
22
+ defaultValue: O,
23
+ onChange: B,
24
+ minDate: t,
25
+ maxDate: d,
26
+ getClampAnnouncement: C,
27
+ label: n = null,
28
+ helperText: u = null,
29
+ error: m,
30
+ hideRequiredMarker: E = !1,
31
+ validationRules: P,
32
+ width: T,
33
+ theme: c,
34
+ fluid: q = !1,
35
+ disabled: H = !1,
36
+ readOnly: K = !1,
37
+ required: L = !1,
38
+ name: f,
39
+ id: $,
40
+ className: j,
41
+ style: A,
42
+ "aria-label": W,
43
+ "aria-describedby": _,
44
+ "aria-labelledby": U,
45
+ ...k
46
+ }, z) {
44
47
  const {
45
48
  disabled: e,
46
- readOnly: d,
47
- required: f,
48
- invalid: A,
49
- errorMessageProvided: c,
50
- defaultValue: U,
51
- errorId: p,
52
- helperTextId: _,
53
- validationErrorsId: k,
54
- labelId: b,
55
- ariaDescribedBy: z,
56
- ariaLabelledBy: G,
57
- showErrors: J
58
- } = Be({
59
- name: u,
60
- disabled: P,
61
- readOnly: T,
62
- required: M,
63
- defaultValue: R,
64
- error: n,
65
- hasHelperText: !!o,
66
- hasLabel: !!t,
67
- validationRules: B,
68
- ariaDescribedBy: $,
69
- ariaLabelledBy: j
70
- }), { refFn: Q, ref: g } = we(W), { segmentRefs: X, registerSegmentRef: Y } = Oe(), {
71
- segmentText: Z,
72
- segmentValues: v,
73
- currentValue: D,
74
- wasTouched: ee,
75
- appendDigit: ae,
76
- adjustSegment: re,
77
- clearSegment: le,
78
- beginSegmentEdit: ie,
79
- commitOnBlur: se
80
- } = Te({
81
- value: w,
82
- defaultValue: U,
83
- onChange: N
49
+ readOnly: o,
50
+ required: p,
51
+ invalid: G,
52
+ errorMessageProvided: b,
53
+ defaultValue: J,
54
+ errorId: g,
55
+ helperTextId: Q,
56
+ validationErrorsId: X,
57
+ labelId: v,
58
+ ariaDescribedBy: Y,
59
+ ariaLabelledBy: Z,
60
+ showErrors: ee
61
+ } = qe({
62
+ name: f,
63
+ disabled: H,
64
+ readOnly: K,
65
+ required: L,
66
+ defaultValue: O,
67
+ error: m,
68
+ hasHelperText: !!u,
69
+ hasLabel: !!n,
70
+ validationRules: P,
71
+ ariaDescribedBy: _,
72
+ ariaLabelledBy: U
73
+ });
74
+ Ne(() => {
75
+ process.env.NODE_ENV !== "production" && Ee(t, d) && console.warn(
76
+ "DateField: `minDate` is after `maxDate` — no date satisfies both, so the blur clamp is disabled and values are left as typed."
77
+ );
78
+ }, [V(t), V(d)]);
79
+ const { refFn: ae, ref: F } = Be(z), { segmentRefs: re, registerSegmentRef: ie } = Ke(), {
80
+ segmentText: le,
81
+ segmentValues: D,
82
+ currentValue: h,
83
+ wasTouched: se,
84
+ appendDigit: te,
85
+ adjustSegment: de,
86
+ clearSegment: oe,
87
+ beginSegmentEdit: ne,
88
+ commitOnBlur: ue,
89
+ clampAnnouncement: me
90
+ } = $e({
91
+ value: M,
92
+ defaultValue: J,
93
+ onChange: B,
94
+ minDate: t,
95
+ maxDate: d,
96
+ getClampAnnouncement: C
84
97
  }), {
85
- focusedSegment: F,
86
- fieldTabIndex: de,
87
- handleSegmentFocus: h,
88
- handleFieldFocus: te,
89
- handleFieldBlur: oe,
90
- handleFieldMouseDown: ne,
98
+ focusedSegment: y,
99
+ fieldTabIndex: ce,
100
+ handleSegmentFocus: S,
101
+ handleFieldFocus: fe,
102
+ handleFieldBlur: pe,
103
+ handleFieldMouseDown: be,
91
104
  focusNext: x,
92
- focusPrevious: me
93
- } = Ve({
105
+ focusPrevious: ge
106
+ } = Te({
94
107
  disabled: e,
95
- fieldRef: g,
96
- segmentRefs: X,
97
- partsOrder: I,
98
- onSegmentFocusChange: ie,
99
- onFieldBlur: se
100
- }), ue = Ce({
108
+ fieldRef: F,
109
+ segmentRefs: re,
110
+ partsOrder: R,
111
+ onSegmentFocusChange: ne,
112
+ onFieldBlur: ue
113
+ }), ve = He({
101
114
  disabled: e,
102
- readOnly: d,
103
- fieldRef: g,
115
+ readOnly: o,
116
+ fieldRef: F,
104
117
  focusNext: x,
105
- focusPrevious: me,
106
- adjustSegment: re,
107
- clearSegment: le
108
- }), { isInvalid: fe } = Pe({
109
- segments: v,
110
- currentValue: D,
111
- wasTouched: ee,
112
- isFocused: F !== null
113
- }), ce = (r, i) => {
114
- const { advanced: ge } = ae(r, i);
115
- ge && x(r);
116
- }, y = Me({
117
- formInvalid: A,
118
- valueInvalid: fe
119
- }), pe = (r) => {
118
+ focusPrevious: ge,
119
+ adjustSegment: de,
120
+ clearSegment: oe
121
+ }), { isInvalid: Fe } = Le({
122
+ segments: D,
123
+ currentValue: h,
124
+ wasTouched: se,
125
+ isFocused: y !== null
126
+ }), De = (i, l) => {
127
+ const { advanced: Se } = te(i, l);
128
+ Se && x(i);
129
+ }, I = je({
130
+ formInvalid: G,
131
+ valueInvalid: Fe
132
+ }), he = (i) => {
120
133
  if (e) return;
121
- const i = r.target.closest("label");
122
- !i || i.id !== b || (r.preventDefault(), h(I[0]));
123
- }, be = ve(
124
- a[Fe],
125
- m && xe(m),
126
- O && a[`${s}fluid`],
127
- y && a[`${s}error`],
134
+ const l = i.target.closest("label");
135
+ !l || l.id !== v || (i.preventDefault(), S(R[0]));
136
+ }, ye = xe(
137
+ a[we],
138
+ c && Re(c),
139
+ q && a[`${s}fluid`],
140
+ I && a[`${s}error`],
128
141
  e && a[`${s}disabled`],
129
- d && a[`${s}read-only`],
130
- E
142
+ o && a[`${s}read-only`],
143
+ j
131
144
  );
132
- return /* @__PURE__ */ S(
145
+ return /* @__PURE__ */ N(
133
146
  "div",
134
147
  {
135
- className: be,
136
- style: { ...H, maxWidth: C },
137
- onMouseDownCapture: pe,
148
+ className: ye,
149
+ style: { ...A, maxWidth: T },
150
+ onMouseDownCapture: he,
138
151
  children: [
139
- /* @__PURE__ */ l(
140
- Se,
152
+ /* @__PURE__ */ r(
153
+ Me,
141
154
  {
142
- ctxId: b,
155
+ ctxId: v,
143
156
  ctxDisabled: e,
144
- ctxRequired: f && !V,
145
- children: t
157
+ ctxRequired: p && !E,
158
+ children: n
146
159
  }
147
160
  ),
148
- /* @__PURE__ */ l(
161
+ /* @__PURE__ */ r(
149
162
  "div",
150
163
  {
151
- ...K,
152
- ref: Q,
153
- id: q,
164
+ ...k,
165
+ ref: ae,
166
+ id: $,
154
167
  role: "group",
155
- "aria-label": L,
156
- "aria-labelledby": G,
168
+ "aria-label": W,
169
+ "aria-labelledby": Z,
157
170
  "aria-disabled": e || void 0,
158
- "aria-required": f || void 0,
159
- "aria-invalid": y || void 0,
160
- "aria-describedby": z,
161
- "aria-errormessage": c ? p : void 0,
162
- tabIndex: de,
163
- className: a[he.field],
164
- onMouseDown: ne,
165
- onFocus: te,
166
- children: /* @__PURE__ */ l(
167
- Re,
171
+ "aria-required": p || void 0,
172
+ "aria-invalid": I || void 0,
173
+ "aria-describedby": Y,
174
+ "aria-errormessage": b ? g : void 0,
175
+ tabIndex: ce,
176
+ className: a[w.field],
177
+ onMouseDown: be,
178
+ onFocus: fe,
179
+ children: /* @__PURE__ */ r(
180
+ Ce,
168
181
  {
169
- segmentText: Z,
170
- segmentValues: v,
171
- focusedSegment: F,
182
+ segmentText: le,
183
+ segmentValues: D,
184
+ focusedSegment: y,
172
185
  disabled: e,
173
- readOnly: d,
174
- onSegmentFocus: h,
175
- onSegmentBlur: oe,
176
- onSegmentInput: ce,
177
- onSegmentKeyDown: ue,
178
- registerSegmentRef: Y
186
+ readOnly: o,
187
+ onSegmentFocus: S,
188
+ onSegmentBlur: pe,
189
+ onSegmentInput: De,
190
+ onSegmentKeyDown: ve,
191
+ registerSegmentRef: ie
179
192
  }
180
193
  )
181
194
  }
182
195
  ),
183
- /* @__PURE__ */ l(
196
+ /* @__PURE__ */ r(
197
+ "span",
198
+ {
199
+ role: "status",
200
+ "aria-live": "polite",
201
+ "aria-atomic": "true",
202
+ className: a[w.srStatus],
203
+ children: me
204
+ }
205
+ ),
206
+ /* @__PURE__ */ r(
184
207
  "input",
185
208
  {
186
209
  type: "hidden",
187
- name: u,
188
- value: Ne(D),
210
+ name: f,
211
+ value: Pe(h),
189
212
  disabled: e
190
213
  }
191
214
  ),
192
- /* @__PURE__ */ S(Ie, { ctxId: _, ctxDisabled: e, children: [
193
- o,
194
- c && /* @__PURE__ */ l(ye, { id: p, appearance: "negative", children: n })
215
+ /* @__PURE__ */ N(Oe, { ctxId: Q, ctxDisabled: e, children: [
216
+ u,
217
+ b && /* @__PURE__ */ r(Ve, { id: g, appearance: "negative", children: m })
195
218
  ] }),
196
- J(k)
219
+ ee(X)
197
220
  ]
198
221
  }
199
222
  );
200
223
  }
201
224
  );
202
225
  export {
203
- ea as DateField
226
+ da as DateField
204
227
  };
@@ -89,9 +89,21 @@ export interface DateFieldProps extends DateFieldInputAttributes {
89
89
  defaultValue?: Nullable<Date>;
90
90
  /**
91
91
  * Fires when the derived `Date | null` value transitions.
92
- * In DF-1, user-input handlers don't exist yet — wired up in DF-2.
93
92
  */
94
93
  onChange?: (value: Nullable<Date>) => void;
94
+ /**
95
+ * Lower bound; on blur, a touched value springs up to it. Use `validationRules` for validation.
96
+ */
97
+ minDate?: Date;
98
+ /**
99
+ * Upper bound; on blur, a touched value springs down to it. Use `validationRules` for validation.
100
+ */
101
+ maxDate?: Date;
102
+ /**
103
+ * Called when a touched value springs to a bound on blur; return a localized
104
+ * message announced politely to screen readers. Omit → no announcement.
105
+ */
106
+ getClampAnnouncement?: (clampedDate: Date) => string;
95
107
  }
96
108
  /**
97
109
  * Internal props for the segment subcomponent. Not part of the public API.
@@ -103,8 +115,15 @@ export interface DateFieldSegmentProps {
103
115
  /** Numeric segment value — used for `aria-valuenow`. `null` when the segment has no committed value. */
104
116
  value: Nullable<number>;
105
117
  /**
106
- * The upper bound announced as `aria-valuemax`.
107
- * For day: month-aware (e.g. 28 for Feb non-leap); for month: 12; for year: 9999.
118
+ * The lower bound announced as `aria-valuemin` — an intrinsic calendar
119
+ * range, NOT the `minDate` prop (day/month: always 1; year: always 1).
120
+ * Computed by `DateField.Segments` so the segment itself stays a pure renderer.
121
+ */
122
+ valueMin: number;
123
+ /**
124
+ * The upper bound announced as `aria-valuemax` — an intrinsic calendar
125
+ * range, NOT the `maxDate` prop (day: month-aware, e.g. 28 for Feb
126
+ * non-leap; month: 12; year: always 9999).
108
127
  * Computed by `DateField.Segments` so the segment itself stays a pure renderer.
109
128
  */
110
129
  valueMax: number;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("./DateField.validation.cjs.js"),i=31,l=12,S={day:i,month:l,year:9999},u=(e,t,n)=>Math.min(n,Math.max(t,e)),g=(e,t)=>((e-1)%t+t)%t+1,y=2024,M=(e,t)=>t==null?31:new Date(e??y,t,0).getDate(),N=()=>{const e=new Date;return{day:e.getDate(),month:e.getMonth()+1,year:e.getFullYear()}},f=(e,t,n,r)=>{if(n==="min")return e==="year"?null:1;if(n==="max")return e==="year"?null:e==="day"?r.daysInMonth:l;if(t==null){if(e==="year")return r.today.year;const m=e==="day"?i:l;return n>0?1:m}if(e==="year")return u(t+n,1,9999);const a=e==="day"?i:l;return g(t+n,a)},T={day:"",month:"",year:""},c=e=>e instanceof Date&&!Number.isNaN(e.getTime()),d=e=>{const{day:t,month:n,year:r}=e;if(t==null||n==null||r==null)return null;const a=new Date(r,n-1,t);return a.setFullYear(r),Number.isNaN(a.getTime())||a.getFullYear()!==r||a.getMonth()!==n-1||a.getDate()!==t?null:a},_=(e,t)=>e==null&&t==null?!0:e==null||t==null?!1:e.getTime()===t.getTime(),h=e=>{if(!c(e))return"";const t=String(e.getFullYear()).padStart(4,"0"),n=String(e.getMonth()+1).padStart(2,"0"),r=String(e.getDate()).padStart(2,"0");return`${t}-${n}-${r}`},D=e=>e.filter(t=>t.type!=="literal").map(t=>t.type),A=e=>c(e)?{day:String(e.getDate()).padStart(2,"0"),month:String(e.getMonth()+1).padStart(2,"0"),year:String(e.getFullYear()).padStart(4,"0")}:T,o=(e,t)=>{if(t===""||e==="year"&&t.length<s.SEGMENT_MAX_LENGTH.year)return null;const n=parseInt(t,10);return Number.isNaN(n)||n===0?null:n},E=e=>d({day:o("day",e.day),month:o("month",e.month),year:o("year",e.year)}),p=(e,t)=>t===""?"":t.padStart(s.SEGMENT_MAX_LENGTH[e],"0"),V=(e,t,n)=>t===""?{displayValue:n,isPlaceholder:!0}:{displayValue:t.padStart(s.SEGMENT_MAX_LENGTH[e],"0"),isPlaceholder:!1},F=e=>{const t=window.getSelection();if(!t)return;const n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)},I=e=>{var n;if(!e)return;const t=(n=window.getSelection)==null?void 0:n.call(window);t&&t.rangeCount>0&&e.contains(t.anchorNode)&&t.removeAllRanges()};exports.DAY_SPIN_MAX=i;exports.MONTH_MAX=l;exports.SEGMENT_ARIA_MAX=S;exports.adjustSegmentValue=f;exports.clamp=u;exports.clearSelectionWithin=I;exports.deriveValue=d;exports.deriveValueFromText=E;exports.getDaysInMonth=M;exports.getSegmentDisplay=V;exports.getSegmentOrder=D;exports.isSameDateValue=_;exports.normalizeSegmentText=p;exports.segmentNumber=o;exports.segmentTextFromValue=A;exports.selectElementContents=F;exports.toISODateString=h;exports.todayParts=N;exports.wrapInRange=g;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("./DateField.validation.cjs.js"),u=31,s=12,c=1,d=9999,y=(e,t,n)=>Math.min(n,Math.max(t,e)),S=(e,t)=>((e-1)%t+t)%t+1,N=2024,h=(e,t)=>t==null?31:new Date(e??N,t,0).getDate(),T=()=>{const e=new Date;return{day:e.getDate(),month:e.getMonth()+1,year:e.getFullYear()}},l=e=>e instanceof Date&&!Number.isNaN(e.getTime()),_=(e,t,n,r)=>{if(n==="min")return e==="year"?null:1;if(n==="max")return e==="year"?null:e==="day"?r.daysInMonth:s;if(t==null){if(e==="year")return r.today.year;const f=e==="day"?u:s;return n>0?1:f}if(e==="year")return y(t+n,c,d);const o=e==="day"?u:s;return S(t+n,o)},A={day:"",month:"",year:""},M=e=>{const{day:t,month:n,year:r}=e;if(t==null||n==null||r==null)return null;const o=new Date(r,n-1,t);return o.setFullYear(r),Number.isNaN(o.getTime())||o.getFullYear()!==r||o.getMonth()!==n-1||o.getDate()!==t?null:o},E=(e,t)=>e==null&&t==null?!0:e==null||t==null?!1:e.getTime()===t.getTime(),a=e=>e.getFullYear()*1e4+(e.getMonth()+1)*100+e.getDate(),p=e=>l(e)?a(e):null,D=(e,t,n)=>{const r=a(e);return!(l(t)&&r<a(t)||l(n)&&r>a(n))},m=(e,t)=>l(e)&&l(t)&&a(e)>a(t),Y=(e,t,n)=>{if(m(t,n))return e;const r=a(e);return l(t)&&r<a(t)?new Date(t.getFullYear(),t.getMonth(),t.getDate()):l(n)&&r>a(n)?new Date(n.getFullYear(),n.getMonth(),n.getDate()):e},F=e=>{if(!l(e))return"";const t=String(e.getFullYear()).padStart(4,"0"),n=String(e.getMonth()+1).padStart(2,"0"),r=String(e.getDate()).padStart(2,"0");return`${t}-${n}-${r}`},R=e=>e.filter(t=>t.type!=="literal").map(t=>t.type),I=e=>l(e)?{day:String(e.getDate()).padStart(2,"0"),month:String(e.getMonth()+1).padStart(2,"0"),year:String(e.getFullYear()).padStart(4,"0")}:A,i=(e,t)=>{if(t===""||e==="year"&&t.length<g.SEGMENT_MAX_LENGTH.year)return null;const n=parseInt(t,10);return Number.isNaN(n)||n===0?null:n},V=e=>M({day:i("day",e.day),month:i("month",e.month),year:i("year",e.year)}),w=(e,t)=>t===""?"":t.padStart(g.SEGMENT_MAX_LENGTH[e],"0"),X=(e,t,n)=>t===""?{displayValue:n,isPlaceholder:!0}:{displayValue:t.padStart(g.SEGMENT_MAX_LENGTH[e],"0"),isPlaceholder:!1},O=e=>{const t=window.getSelection();if(!t)return;const n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)},P=e=>{var n;if(!e)return;const t=(n=window.getSelection)==null?void 0:n.call(window);t&&t.rangeCount>0&&e.contains(t.anchorNode)&&t.removeAllRanges()};exports.DAY_SPIN_MAX=u;exports.MONTH_MAX=s;exports.YEAR_MAX=d;exports.YEAR_MIN=c;exports.adjustSegmentValue=_;exports.clamp=y;exports.clampDateToRange=Y;exports.clearSelectionWithin=P;exports.dateKeyOrNull=p;exports.deriveValue=M;exports.deriveValueFromText=V;exports.getDaysInMonth=h;exports.getSegmentDisplay=X;exports.getSegmentOrder=R;exports.isDateInRange=D;exports.isMinMaxMisconfigured=m;exports.isSameDateValue=E;exports.normalizeSegmentText=w;exports.segmentNumber=i;exports.segmentTextFromValue=I;exports.selectElementContents=O;exports.toISODateString=F;exports.todayParts=T;exports.wrapInRange=S;
@@ -15,11 +15,12 @@ export declare const DAY_SPIN_MAX = 31;
15
15
  /** Absolute maximum for month (1-based). */
16
16
  export declare const MONTH_MAX = 12;
17
17
  /**
18
- * Announced aria-valuemax per segment type.
19
- * Day uses the absolute calendar max (31); month-aware override for day is
20
- * computed in `DateField.Segments` and passed as `valueMax` to `DateFieldSegment`.
18
+ * Absolute floor/ceiling for the free-spinning year segment. `minDate`/
19
+ * `maxDate` no longer bound the year directly (DF-10: the whole date clamps
20
+ * to those bounds on blur instead — see `commitOnBlur`).
21
21
  */
22
- export declare const SEGMENT_ARIA_MAX: Record<SegmentType, number>;
22
+ export declare const YEAR_MIN = 1;
23
+ export declare const YEAR_MAX = 9999;
23
24
  /** Clamps `n` to `[lo, hi]` (inclusive). */
24
25
  export declare const clamp: (n: number, lo: number, hi: number) => number;
25
26
  /**
@@ -64,24 +65,15 @@ export interface AdjustSegmentContext {
64
65
  * Computes the next numeric value for a date segment after an increment,
65
66
  * decrement, or boundary jump.
66
67
  *
67
- * Returns `null` to signal "no change" (Home/End on the year segment, which
68
- * has no meaningful bound until DF-10 adds minDate/maxDate).
69
- *
70
- * Rules:
71
- * - **day spin (numeric delta)**: modular wrap within `[1, DAY_SPIN_MAX]` — the
72
- * absolute calendar maximum. Out-of-range dates (e.g. Feb 31) are flagged
73
- * invalid by the existing validity layer (`useDateFieldValidity`), not clamped here.
74
- * - **day End (`'max'`)**: uses `ctx.daysInMonth` — the last valid day of the
75
- * currently selected month (month-aware).
76
- * - **month**: modular wrap within `[1, MONTH_MAX]`.
77
- * - **year**: clamp to `[1, 9999]`; no wrap.
78
- * - **Empty segment + numeric delta**: direction-based first/last valid value
79
- * for day/month — positive amount → `1`, negative amount → `DAY_SPIN_MAX`/
80
- * `MONTH_MAX`. Year has no natural bound, so it seeds to `ctx.today.year`
81
- * regardless of direction.
82
- * - **`'min'`**: returns 1 for day/month, null for year.
83
- * - **`'max'`**: returns `ctx.daysInMonth` for day (End → month-aware last
84
- * valid day), MONTH_MAX for month, null for year.
68
+ * Day and month wrap modularly within their absolute ranges (day:
69
+ * `[1, DAY_SPIN_MAX]`; month: `[1, MONTH_MAX]`); out-of-range dates (e.g. Feb
70
+ * 31) are flagged by the validity layer, not clamped here. Year clamps (no
71
+ * wrap) within `[YEAR_MIN, YEAR_MAX]` and spins free of `minDate`/`maxDate`
72
+ * (DF-10: those clamp the whole date on blur instead — see `commitOnBlur`);
73
+ * year `'min'`/`'max'` (Home/End) are a no-op (`null`). An empty segment seeds
74
+ * on the first numeric press: day/month to a direction-based first/last valid
75
+ * value (positive amount → `1`, negative → `DAY_SPIN_MAX`/`MONTH_MAX`;
76
+ * EPTOOLS-2766), year to `ctx.today.year`.
85
77
  *
86
78
  * @param type - Segment kind (`'day'`, `'month'`, or `'year'`).
87
79
  * @param current - Current numeric value of the segment, or `null` when empty.
@@ -91,6 +83,36 @@ export interface AdjustSegmentContext {
91
83
  export declare const adjustSegmentValue: (type: SegmentType, current: Nullable<number>, amount: SegmentAdjustment, ctx: AdjustSegmentContext) => Nullable<number>;
92
84
  export declare const deriveValue: (segments: SegmentValues) => Nullable<Date>;
93
85
  export declare const isSameDateValue: (a: Nullable<Date>, b: Nullable<Date>) => boolean;
86
+ /**
87
+ * `dateKey` for a possibly-absent/invalid `Date`, collapsing both to `null`
88
+ * (never `NaN` — a `NaN` dependency would itself churn every render). Used to
89
+ * key memo/callback deps on a bound's primitive calendar day instead of the
90
+ * `Date` object's identity.
91
+ */
92
+ export declare const dateKeyOrNull: (d?: Nullable<Date>) => Nullable<number>;
93
+ /**
94
+ * True when `value`'s calendar day falls within `[minDate, maxDate]`
95
+ * (inclusive). Compared by calendar day only, so a `maxDate` with a non-zero
96
+ * time-of-day still accepts the same day. A missing bound is unbounded on
97
+ * that side.
98
+ */
99
+ export declare const isDateInRange: (value: Date, minDate?: Nullable<Date>, maxDate?: Nullable<Date>) => boolean;
100
+ /**
101
+ * True when both bounds are valid Dates but `minDate`'s calendar day is after
102
+ * `maxDate`'s — a misconfiguration where no date can satisfy both. `DateField`
103
+ * dev-warns on this, and `clampDateToRange` skips clamping so a touched value
104
+ * isn't flip-flopped between the two bounds on repeated blurs.
105
+ */
106
+ export declare const isMinMaxMisconfigured: (minDate?: Nullable<Date>, maxDate?: Nullable<Date>) => boolean;
107
+ /**
108
+ * Clamps `value` to the nearest in-range bound by calendar day: `minDate` when
109
+ * the value is before it, `maxDate` when after, else the value unchanged. An
110
+ * invalid/absent bound is ignored (unbounded on that side). Returns a
111
+ * local-midnight `Date` on the bound's calendar day. Used by `commitOnBlur` to
112
+ * normalize a well-formed out-of-range date; structural invalids (Feb 30) never
113
+ * reach here since they derive to `null`.
114
+ */
115
+ export declare const clampDateToRange: (value: Date, minDate?: Nullable<Date>, maxDate?: Nullable<Date>) => Date;
94
116
  export declare const toISODateString: (value: Nullable<Date>) => string;
95
117
  /** Ordered segment types from a parts array (literals stripped). */
96
118
  export declare const getSegmentOrder: (parts: ReadonlyArray<DateFieldPart>) => ReadonlyArray<SegmentType>;