@astryxdesign/core 0.7.8-canary.bd69bed → 0.7.8-canary.c1e6a5e

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 +1 @@
1
- {"version":3,"file":"PickerWheel.d.ts","sourceRoot":"","sources":["../../src/DateInput/PickerWheel.tsx"],"names":[],"mappings":"AAmDA,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,gBAAgB,CAAC;AA2HxB;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,KAAK,EACL,QAAQ,EACR,QAAe,GAChB,EAAE,UAAU,+BAghBZ;yBAthBe,WAAW"}
1
+ {"version":3,"file":"PickerWheel.d.ts","sourceRoot":"","sources":["../../src/DateInput/PickerWheel.tsx"],"names":[],"mappings":"AAmDA,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,gBAAgB,CAAC;AA2HxB;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,KAAK,EACL,QAAQ,EACR,QAAe,GAChB,EAAE,UAAU,+BA8gBZ;yBAphBe,WAAW"}
@@ -468,9 +468,6 @@ export function PickerWheel({
468
468
  isEnabled: isActive,
469
469
  isPointerKept: true
470
470
  });
471
-
472
- // A key commits the row at `position` at once and turns the wheel there
473
- // from `offset`.
474
471
  const turnToPosition = useCallback((position, offset) => {
475
472
  const current = latestRef.current;
476
473
  const option = current.options[position];
@@ -1 +1 @@
1
- {"version":3,"file":"Wheel.d.ts","sourceRoot":"","sources":["../../src/DateInput/Wheel.tsx"],"names":[],"mappings":"AAoDA,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,mBAAmB,EACnB,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AAoMxB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,+BAKtC;yBALe,KAAK;;;AASrB;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,KAAK,EACL,QAAQ,EACR,QAAe,GAChB,EAAE,UAAU,+BAoUZ;yBA1Ue,WAAW"}
1
+ {"version":3,"file":"Wheel.d.ts","sourceRoot":"","sources":["../../src/DateInput/Wheel.tsx"],"names":[],"mappings":"AAoDA,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,mBAAmB,EACnB,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AAoMxB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,+BAKtC;yBALe,KAAK;;;AASrB;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,KAAK,EACL,QAAQ,EACR,QAAe,GAChB,EAAE,UAAU,+BAmUZ;yBAzUe,WAAW"}
@@ -351,8 +351,6 @@ export function ScrollWheel({
351
351
  useOwnScrollGesture(scrollerRef, 'all', {
352
352
  isEnabled: isActive
353
353
  });
354
-
355
- // A key commits the row it moves to and scrolls the column there.
356
354
  const moveTo = useCallback(row => {
357
355
  const option = row == null ? undefined : options[row];
358
356
  if (row == null || option == null || option.isDisabled) {
@@ -1 +1 @@
1
- {"version":3,"file":"Overlay.d.ts","sourceRoot":"","sources":["../../src/Overlay/Overlay.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAE,GAAG,EAAC,MAAM,OAAO,CAAC;AAO1C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,aAAa,EACd,MAAM,gBAAgB,CAAC;AAGxB,MAAM,WAAW,YAAa,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC7D,8CAA8C;IAC9C,GAAG,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC1B,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,iDAAiD;IACjD,OAAO,EAAE,SAAS,CAAC;IACnB,wBAAwB;IACxB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,yCAAyC;IACzC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sBAAsB;IACtB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,qBAAqB;IACrB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,OAAO,EACP,MAAM,EACN,MAAM,EACN,KAAK,EACL,QAAQ,EACR,KAAK,EACL,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,aAAa,EACxB,GAAG,IAAI,EACR,EAAE,YAAY,+BAqDd;yBApEe,OAAO"}
1
+ {"version":3,"file":"Overlay.d.ts","sourceRoot":"","sources":["../../src/Overlay/Overlay.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAE,GAAG,EAAC,MAAM,OAAO,CAAC;AAO1C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,aAAa,EACd,MAAM,gBAAgB,CAAC;AAGxB,MAAM,WAAW,YAAa,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC7D,8CAA8C;IAC9C,GAAG,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC1B,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,iDAAiD;IACjD,OAAO,EAAE,SAAS,CAAC;IACnB,wBAAwB;IACxB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,yCAAyC;IACzC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sBAAsB;IACtB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,qBAAqB;IACrB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,OAAO,EACP,MAAM,EACN,MAAM,EACN,KAAK,EACL,QAAQ,EACR,KAAK,EACL,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,aAAa,EACxB,GAAG,IAAI,EACR,EAAE,YAAY,+BA0Dd;yBAzEe,OAAO"}
@@ -66,8 +66,13 @@ export function Overlay({
66
66
  align
67
67
  });
68
68
 
69
- // Border radius: mirror first child's radius onto the wrapper.
70
- // Only the component needs this — hook consumers have their own radius.
69
+ // Copies the first child's radius onto the wrapper, so the scrim clips to
70
+ // the media's rounded corners. This is a layout read of the mounted DOM, and
71
+ // no user interaction causes it. It runs again when a hidden Activity shows
72
+ // the wrapper, because the child can change while it is hidden. A ref
73
+ // callback runs again when a caller's ref changes identity, so it cannot
74
+ // keep this once-per-show timing. A useOverlay consumer sets the radius on
75
+ // its own element.
71
76
  useIsomorphicLayoutEffect(() => {
72
77
  const el = overlay.containerRef.current;
73
78
  if (!el) {
@@ -189,10 +189,10 @@ function TokenizerSheetContent({
189
189
  // has an entrance animation and no exit, so a close is its unmount; the
190
190
  // close handler commits that unmount at once and then places focus itself,
191
191
  // now that the modal is gone and the sheet's content can take focus again
192
- // (`architecture:react-component-runtime` INV2): on the search field
193
- // after a confirmed clear — the clear is spent, and the search field is
194
- // where the next selection starts (FR24, `spec:AST-085` FR4) — and back on
195
- // Clear all after a cancel. A confirmed clear is announced there too — a
192
+ // (`architecture:react-component-runtime` INV2). After a clear, Clear all
193
+ // is aria-disabled but stays focusable, so it keeps focus (FR24,
194
+ // `spec:AST-085` FR4). Focus on the search field opens the on-screen
195
+ // keyboard over the cleared rows. A confirmed clear is announced too — a
196
196
  // change the person hears about, as an add or a remove is
197
197
  // (`component:Tokenizer` AR6) — after the confirmation's dialog has gone,
198
198
  // so the sheet is the modal on top and the words are delivered inside it
@@ -206,11 +206,9 @@ function TokenizerSheetContent({
206
206
  const closeClearConfirmation = confirmed => {
207
207
  // eslint-disable-next-line @eslint-react/dom-no-flush-sync -- the confirmation must be gone before focus moves into the sheet and the words are routed into it
208
208
  flushSync(() => setIsClearConfirmationOpen(false));
209
+ clearAllRef.current?.focus();
209
210
  if (confirmed) {
210
- searchRef.current?.focus();
211
211
  announce(t('@astryx.tokenizer.allRemoved'));
212
- } else {
213
- clearAllRef.current?.focus();
214
212
  }
215
213
  };
216
214
  // Keep editing: the confirmation is gone, and focus returns to the search
@@ -745,11 +743,9 @@ function TokenizerSheetContent({
745
743
  variant: "secondary",
746
744
  size: "lg",
747
745
  isDisabled: draftValue.length === 0
748
- // With a tooltip, Button's disabled mode is `aria-disabled`: the
749
- // action keeps its focus order and says why it does nothing
750
- // (`spec:AST-085` FR1), instead of leaving the tab order as a
751
- // natively disabled button does — a confirmed Clear all returns
752
- // focus here before the search field takes it (FR24).
746
+ // With a tooltip, Button uses aria-disabled, so Clear all stays
747
+ // focusable after a clear and says why it does nothing
748
+ // (`spec:AST-085` FR1; FR24).
753
749
  ,
754
750
  tooltip: draftValue.length === 0 ? t('@astryx.tokenizer.nothingToClear') : undefined,
755
751
  ref: clearAllRef,
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @file datePicker.ts
3
+ * @input Nothing; data only
4
+ * @output Exports `datePicker`: the rules UIDatePicker adds to its wheels. The
5
+ * month/year wheels change the date, the AM/PM column follows the hour, a
6
+ * column turns back onto a bound, and a day clamps to its month.
7
+ * @position The date picker domain of the UIKit constants table
8
+ * (motion/uikit), for the date and time touch sheets. Every leaf names its
9
+ * source, which uikit.test.ts checks.
10
+ *
11
+ * SYNC: When modified, update /packages/core/src/motion/MEASUREMENTS.md and
12
+ * /packages/core/src/motion/README.md.
13
+ */
14
+ export declare const datePicker: {
15
+ readonly calendar: {
16
+ readonly monthYearWheels: {
17
+ /**
18
+ * Turning the month or year wheel changes the picker's date, keeping
19
+ * the day, and fires `valueChanged`.
20
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.date` `.inline`,
21
+ * October 15 → May 15 after the month wheel's fling, with a
22
+ * `valueChanged` carrying May 15, Oct 23 2026
23
+ */
24
+ readonly changesDate: true;
25
+ };
26
+ };
27
+ readonly wheels: {
28
+ /**
29
+ * In 12-hour time the AM/PM column follows the hour. Turning the hour from
30
+ * 11 to 12 turns the time from AM to PM, and back. From 12 to 1 it keeps
31
+ * the half of the day.
32
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.time` `.wheels`,
33
+ * en_US: 11:58 AM, hour wheel +1 row → `valueChanged` 12:58 PM; then
34
+ * the minute wheel's +4 rows carried the hour from 12 to 1 → 1:02 PM,
35
+ * Oct 23 2026
36
+ */
37
+ readonly meridiemFollowsHour: true;
38
+ /**
39
+ * A column that comes to rest past `minimumDate` or `maximumDate` turns
40
+ * back onto the bound. Its `valueChanged` already carries the bound. For
41
+ * example, with bounds 9:00–17:00, the hour of 10:00 dragged to 7 reads
42
+ * 9:00 at rest. The hour column then turns 64 pt, two rows, back to 9.
43
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.time` `.wheels`
44
+ * with bounds 9:00–17:00 and `minuteInterval` 15, per-frame hour
45
+ * offsets after the rest (0.4, 2, 7.4, 16, 26.4, 37.7, 48.3, 56.7,
46
+ * 62, 64 pt at 33 ms steps) and the `valueChanged` time, Oct 23 2026
47
+ */
48
+ readonly restPastBound: "turnToBound";
49
+ /**
50
+ * In `.date` wheels a day the month does not have clamps to the month's
51
+ * last day (January 30, month +1 → February 28).
52
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.date` `.wheels`
53
+ * with bounds Jun 1 2026 – Mar 15 2027, Oct 23 2026
54
+ */
55
+ readonly dayClampsToMonth: true;
56
+ };
57
+ };
58
+ //# sourceMappingURL=datePicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datePicker.d.ts","sourceRoot":"","sources":["../../../src/motion/uikit/datePicker.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,UAAU;;;YAGjB;;;;;;eAMG;;;;;QAKL;;;;;;;;WAQG;;QAEH;;;;;;;;;WASG;;QAEH;;;;;WAKG;;;CAGG,CAAC"}
@@ -0,0 +1,60 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file datePicker.ts
5
+ * @input Nothing; data only
6
+ * @output Exports `datePicker`: the rules UIDatePicker adds to its wheels. The
7
+ * month/year wheels change the date, the AM/PM column follows the hour, a
8
+ * column turns back onto a bound, and a day clamps to its month.
9
+ * @position The date picker domain of the UIKit constants table
10
+ * (motion/uikit), for the date and time touch sheets. Every leaf names its
11
+ * source, which uikit.test.ts checks.
12
+ *
13
+ * SYNC: When modified, update /packages/core/src/motion/MEASUREMENTS.md and
14
+ * /packages/core/src/motion/README.md.
15
+ */
16
+
17
+ export const datePicker = {
18
+ calendar: {
19
+ monthYearWheels: {
20
+ /**
21
+ * Turning the month or year wheel changes the picker's date, keeping
22
+ * the day, and fires `valueChanged`.
23
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.date` `.inline`,
24
+ * October 15 → May 15 after the month wheel's fling, with a
25
+ * `valueChanged` carrying May 15, Oct 23 2026
26
+ */
27
+ changesDate: true
28
+ }
29
+ },
30
+ wheels: {
31
+ /**
32
+ * In 12-hour time the AM/PM column follows the hour. Turning the hour from
33
+ * 11 to 12 turns the time from AM to PM, and back. From 12 to 1 it keeps
34
+ * the half of the day.
35
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.time` `.wheels`,
36
+ * en_US: 11:58 AM, hour wheel +1 row → `valueChanged` 12:58 PM; then
37
+ * the minute wheel's +4 rows carried the hour from 12 to 1 → 1:02 PM,
38
+ * Oct 23 2026
39
+ */
40
+ meridiemFollowsHour: true,
41
+ /**
42
+ * A column that comes to rest past `minimumDate` or `maximumDate` turns
43
+ * back onto the bound. Its `valueChanged` already carries the bound. For
44
+ * example, with bounds 9:00–17:00, the hour of 10:00 dragged to 7 reads
45
+ * 9:00 at rest. The hour column then turns 64 pt, two rows, back to 9.
46
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.time` `.wheels`
47
+ * with bounds 9:00–17:00 and `minuteInterval` 15, per-frame hour
48
+ * offsets after the rest (0.4, 2, 7.4, 16, 26.4, 37.7, 48.3, 56.7,
49
+ * 62, 64 pt at 33 ms steps) and the `valueChanged` time, Oct 23 2026
50
+ */
51
+ restPastBound: 'turnToBound',
52
+ /**
53
+ * In `.date` wheels a day the month does not have clamps to the month's
54
+ * last day (January 30, month +1 → February 28).
55
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.date` `.wheels`
56
+ * with bounds Jun 1 2026 – Mar 15 2027, Oct 23 2026
57
+ */
58
+ dayClampsToMonth: true
59
+ }
60
+ };
@@ -3,9 +3,9 @@
3
3
  * @input The domain files of the UIKit constants table
4
4
  * @output Re-exports `springs` and one constant per spring (`SHEET_PRESENT_SPRING`,
5
5
  * …), `scroll`, `timing`, `navigation`, `picker`, `progressBar`,
6
- * `segmentedControl`, the `slider` and switch (`switchControl`) domains, and
7
- * the gesture thresholds (one `UIKIT_*` group per recognizer, their union
8
- * `UIKIT_GESTURES`, and the haptic tables)
6
+ * `segmentedControl`, `datePicker`, `slider` and `switchControl`. It also
7
+ * re-exports the gesture thresholds: one `UIKIT_*` group per recognizer,
8
+ * their union `UIKIT_GESTURES`, and the haptic tables
9
9
  * @position Entry of the UIKit constants table (motion/uikit). Internal to
10
10
  * core: not exported from the package entry point
11
11
  *
@@ -18,6 +18,7 @@
18
18
  * /packages/core/src/motion/README.md.
19
19
  */
20
20
  export { HAPTIC_FEEDBACK_KINDS, HAPTIC_VIBRATION_PATTERNS, UIKIT_CLICK, UIKIT_CONTROL, UIKIT_GESTURES, UIKIT_LONG_PRESS, UIKIT_PAN, UIKIT_PINCH, UIKIT_PRESS, UIKIT_ROTATION, UIKIT_SCREEN_EDGE_PAN, UIKIT_SWIPE, UIKIT_TAP, UIKIT_VELOCITY, type GestureConstant, type GestureConstantUnit, type HapticFeedbackKind, } from './gestures.js';
21
+ export { datePicker } from './datePicker.js';
21
22
  export { navigation, registerNavigationTable } from './navigation.js';
22
23
  export { picker } from './picker.js';
23
24
  export { progressBar, registerProgressBarTable } from './progressBar.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/motion/uikit/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,WAAW,EACX,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,WAAW,EACX,SAAS,EACT,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAC,UAAU,EAAE,uBAAuB,EAAC,MAAM,cAAc,CAAC;AACjE,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAChC,OAAO,EAAC,WAAW,EAAE,wBAAwB,EAAC,MAAM,eAAe,CAAC;AACpE,OAAO,EAAC,mBAAmB,EAAE,MAAM,EAAC,MAAM,UAAU,CAAC;AACrD,OAAO,EAAC,gBAAgB,EAAC,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAChC,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,EAC3B,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACb,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,0BAA0B,EAC1B,8BAA8B,EAC9B,wCAAwC,EACxC,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,0BAA0B,EAC1B,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,OAAO,GACR,MAAM,WAAW,CAAC;AACnB,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAC;AACvC,OAAO,EAAC,mBAAmB,EAAE,MAAM,EAAC,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/motion/uikit/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,WAAW,EACX,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,WAAW,EACX,SAAS,EACT,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AACxC,OAAO,EAAC,UAAU,EAAE,uBAAuB,EAAC,MAAM,cAAc,CAAC;AACjE,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAChC,OAAO,EAAC,WAAW,EAAE,wBAAwB,EAAC,MAAM,eAAe,CAAC;AACpE,OAAO,EAAC,mBAAmB,EAAE,MAAM,EAAC,MAAM,UAAU,CAAC;AACrD,OAAO,EAAC,gBAAgB,EAAC,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAChC,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,EAC3B,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACb,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,0BAA0B,EAC1B,8BAA8B,EAC9B,wCAAwC,EACxC,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,0BAA0B,EAC1B,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,OAAO,GACR,MAAM,WAAW,CAAC;AACnB,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAC;AACvC,OAAO,EAAC,mBAAmB,EAAE,MAAM,EAAC,MAAM,UAAU,CAAC"}
@@ -5,9 +5,9 @@
5
5
  * @input The domain files of the UIKit constants table
6
6
  * @output Re-exports `springs` and one constant per spring (`SHEET_PRESENT_SPRING`,
7
7
  * …), `scroll`, `timing`, `navigation`, `picker`, `progressBar`,
8
- * `segmentedControl`, the `slider` and switch (`switchControl`) domains, and
9
- * the gesture thresholds (one `UIKIT_*` group per recognizer, their union
10
- * `UIKIT_GESTURES`, and the haptic tables)
8
+ * `segmentedControl`, `datePicker`, `slider` and `switchControl`. It also
9
+ * re-exports the gesture thresholds: one `UIKIT_*` group per recognizer,
10
+ * their union `UIKIT_GESTURES`, and the haptic tables
11
11
  * @position Entry of the UIKit constants table (motion/uikit). Internal to
12
12
  * core: not exported from the package entry point
13
13
  *
@@ -21,6 +21,7 @@
21
21
  */
22
22
 
23
23
  export { HAPTIC_FEEDBACK_KINDS, HAPTIC_VIBRATION_PATTERNS, UIKIT_CLICK, UIKIT_CONTROL, UIKIT_GESTURES, UIKIT_LONG_PRESS, UIKIT_PAN, UIKIT_PINCH, UIKIT_PRESS, UIKIT_ROTATION, UIKIT_SCREEN_EDGE_PAN, UIKIT_SWIPE, UIKIT_TAP, UIKIT_VELOCITY } from "./gestures.js";
24
+ export { datePicker } from "./datePicker.js";
24
25
  export { navigation, registerNavigationTable } from "./navigation.js";
25
26
  export { picker } from "./picker.js";
26
27
  export { progressBar, registerProgressBarTable } from "./progressBar.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.7.8-canary.bd69bed",
3
+ "version": "0.7.8-canary.c1e6a5e",
4
4
  "displayName": "Astryx Core",
5
5
  "description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
6
6
  "author": "Meta Open Source",
@@ -870,9 +870,9 @@
870
870
  "react-dom": ">=19.0.0"
871
871
  },
872
872
  "devDependencies": {
873
- "@astryxdesign/a11y-spec": "0.7.8-canary.bd69bed",
874
- "@astryxdesign/cli": "0.7.8-canary.bd69bed",
875
- "@astryxdesign/interaction-harness": "0.7.8-canary.bd69bed",
873
+ "@astryxdesign/a11y-spec": "0.7.8-canary.c1e6a5e",
874
+ "@astryxdesign/cli": "0.7.8-canary.c1e6a5e",
875
+ "@astryxdesign/interaction-harness": "0.7.8-canary.c1e6a5e",
876
876
  "@babel/cli": "^7.29.7",
877
877
  "@babel/core": "^7.29.7",
878
878
  "@babel/preset-react": "^7.29.7",
@@ -584,8 +584,6 @@ export function PickerWheel({
584
584
  isPointerKept: true,
585
585
  });
586
586
 
587
- // A key commits the row at `position` at once and turns the wheel there
588
- // from `offset`.
589
587
  const turnToPosition = useCallback(
590
588
  (position: number, offset: number) => {
591
589
  const current = latestRef.current;
@@ -532,7 +532,6 @@ export function ScrollWheel({
532
532
  // there is no way to share — it takes every touch that lands on it.
533
533
  useOwnScrollGesture(scrollerRef, 'all', {isEnabled: isActive});
534
534
 
535
- // A key commits the row it moves to and scrolls the column there.
536
535
  const moveTo = useCallback(
537
536
  (row: number | null) => {
538
537
  const option = row == null ? undefined : options[row];
@@ -94,8 +94,13 @@ export function Overlay({
94
94
  align,
95
95
  });
96
96
 
97
- // Border radius: mirror first child's radius onto the wrapper.
98
- // Only the component needs this — hook consumers have their own radius.
97
+ // Copies the first child's radius onto the wrapper, so the scrim clips to
98
+ // the media's rounded corners. This is a layout read of the mounted DOM, and
99
+ // no user interaction causes it. It runs again when a hidden Activity shows
100
+ // the wrapper, because the child can change while it is hidden. A ref
101
+ // callback runs again when a caller's ref changes identity, so it cannot
102
+ // keep this once-per-show timing. A useOverlay consumer sets the radius on
103
+ // its own element.
99
104
  useIsomorphicLayoutEffect(() => {
100
105
  const el = overlay.containerRef.current;
101
106
  if (!el) {
@@ -354,10 +354,10 @@ function TokenizerSheetContent<T extends SearchableItem>({
354
354
  // has an entrance animation and no exit, so a close is its unmount; the
355
355
  // close handler commits that unmount at once and then places focus itself,
356
356
  // now that the modal is gone and the sheet's content can take focus again
357
- // (`architecture:react-component-runtime` INV2): on the search field
358
- // after a confirmed clear — the clear is spent, and the search field is
359
- // where the next selection starts (FR24, `spec:AST-085` FR4) — and back on
360
- // Clear all after a cancel. A confirmed clear is announced there too — a
357
+ // (`architecture:react-component-runtime` INV2). After a clear, Clear all
358
+ // is aria-disabled but stays focusable, so it keeps focus (FR24,
359
+ // `spec:AST-085` FR4). Focus on the search field opens the on-screen
360
+ // keyboard over the cleared rows. A confirmed clear is announced too — a
361
361
  // change the person hears about, as an add or a remove is
362
362
  // (`component:Tokenizer` AR6) — after the confirmation's dialog has gone,
363
363
  // so the sheet is the modal on top and the words are delivered inside it
@@ -372,11 +372,9 @@ function TokenizerSheetContent<T extends SearchableItem>({
372
372
  const closeClearConfirmation = (confirmed: boolean) => {
373
373
  // eslint-disable-next-line @eslint-react/dom-no-flush-sync -- the confirmation must be gone before focus moves into the sheet and the words are routed into it
374
374
  flushSync(() => setIsClearConfirmationOpen(false));
375
+ clearAllRef.current?.focus();
375
376
  if (confirmed) {
376
- searchRef.current?.focus();
377
377
  announce(t('@astryx.tokenizer.allRemoved'));
378
- } else {
379
- clearAllRef.current?.focus();
380
378
  }
381
379
  };
382
380
  // Keep editing: the confirmation is gone, and focus returns to the search
@@ -1000,11 +998,9 @@ function TokenizerSheetContent<T extends SearchableItem>({
1000
998
  variant="secondary"
1001
999
  size="lg"
1002
1000
  isDisabled={draftValue.length === 0}
1003
- // With a tooltip, Button's disabled mode is `aria-disabled`: the
1004
- // action keeps its focus order and says why it does nothing
1005
- // (`spec:AST-085` FR1), instead of leaving the tab order as a
1006
- // natively disabled button does — a confirmed Clear all returns
1007
- // focus here before the search field takes it (FR24).
1001
+ // With a tooltip, Button uses aria-disabled, so Clear all stays
1002
+ // focusable after a clear and says why it does nothing
1003
+ // (`spec:AST-085` FR1; FR24).
1008
1004
  tooltip={
1009
1005
  draftValue.length === 0
1010
1006
  ? t('@astryx.tokenizer.nothingToClear')
@@ -268,6 +268,16 @@ match the probe.
268
268
  | M-SH21 | SheetProbe on iPad, iOS 27 `preferredPlacement` each case, RTL | present | placement geometry and motion | B, C |
269
269
  | M-SH22 | SheetProbe iOS 26 `preferredTransition = .zoom` from a bar button | present, dismiss | morph geometry per frame | A, C |
270
270
 
271
+ ## Date picker (UIDatePicker)
272
+
273
+ Rules the DateInput, DateTimeInput and TimeInput touch sheets follow, in their
274
+ own table (`uikit/datePicker.ts`). The wheels' physics are UIPickerView's: the
275
+ table only names them, and their values belong to the wheel's own table.
276
+
277
+ Done (UIKit lab, iOS 26.2 simulator, Oct 23 2026; in the date picker's table):
278
+ the month/year wheels changing the date, the AM/PM column following the hour,
279
+ the turn back onto a bound, and the day clamped to the month.
280
+
271
281
  ## Context menu (UIContextMenuInteraction)
272
282
 
273
283
  Values the context menu reads from its own table, which lands with the
@@ -26,6 +26,7 @@ roles through `resolveMotion`.
26
26
  | `uikit/picker.ts` | UIPickerView: row pitch, height and rubber band, velocity floor, the coast's stop speed, snap at rest, tap spin, tick. |
27
27
  | `uikit/segmented.ts` | UISegmentedControl: the selection indicator's spring; under Reduce Motion the animator's cross-fade replaces it. |
28
28
  | `uikit/switch.ts` | UISwitch on iOS 26.2: geometry, the lift, settle, travel and tracking springs, the flip distance, the track colour. |
29
+ | `uikit/datePicker.ts` | UIDatePicker: the rules it adds to its wheels. |
29
30
  | `roles.ts` | The internal motion roles: the springs, durations and curves `spec:AST-094` FR4 names, registered by the shared tables, and a role's CSS form read back. |
30
31
  | `roleValue.ts` | A role's CSS form, written: a spring's duration and bounce, a duration in ms, a curve's `cubic-bezier()`. |
31
32
  | `resolveMotion.ts` | A role's value: its table value, as the per-role tables stay internal (`spec:AST-094` DEC-15). |
@@ -0,0 +1,60 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file datePicker.ts
5
+ * @input Nothing; data only
6
+ * @output Exports `datePicker`: the rules UIDatePicker adds to its wheels. The
7
+ * month/year wheels change the date, the AM/PM column follows the hour, a
8
+ * column turns back onto a bound, and a day clamps to its month.
9
+ * @position The date picker domain of the UIKit constants table
10
+ * (motion/uikit), for the date and time touch sheets. Every leaf names its
11
+ * source, which uikit.test.ts checks.
12
+ *
13
+ * SYNC: When modified, update /packages/core/src/motion/MEASUREMENTS.md and
14
+ * /packages/core/src/motion/README.md.
15
+ */
16
+
17
+ export const datePicker = {
18
+ calendar: {
19
+ monthYearWheels: {
20
+ /**
21
+ * Turning the month or year wheel changes the picker's date, keeping
22
+ * the day, and fires `valueChanged`.
23
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.date` `.inline`,
24
+ * October 15 → May 15 after the month wheel's fling, with a
25
+ * `valueChanged` carrying May 15, Oct 23 2026
26
+ */
27
+ changesDate: true,
28
+ },
29
+ },
30
+ wheels: {
31
+ /**
32
+ * In 12-hour time the AM/PM column follows the hour. Turning the hour from
33
+ * 11 to 12 turns the time from AM to PM, and back. From 12 to 1 it keeps
34
+ * the half of the day.
35
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.time` `.wheels`,
36
+ * en_US: 11:58 AM, hour wheel +1 row → `valueChanged` 12:58 PM; then
37
+ * the minute wheel's +4 rows carried the hour from 12 to 1 → 1:02 PM,
38
+ * Oct 23 2026
39
+ */
40
+ meridiemFollowsHour: true,
41
+ /**
42
+ * A column that comes to rest past `minimumDate` or `maximumDate` turns
43
+ * back onto the bound. Its `valueChanged` already carries the bound. For
44
+ * example, with bounds 9:00–17:00, the hour of 10:00 dragged to 7 reads
45
+ * 9:00 at rest. The hour column then turns 64 pt, two rows, back to 9.
46
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.time` `.wheels`
47
+ * with bounds 9:00–17:00 and `minuteInterval` 15, per-frame hour
48
+ * offsets after the rest (0.4, 2, 7.4, 16, 26.4, 37.7, 48.3, 56.7,
49
+ * 62, 64 pt at 33 ms steps) and the `valueChanged` time, Oct 23 2026
50
+ */
51
+ restPastBound: 'turnToBound',
52
+ /**
53
+ * In `.date` wheels a day the month does not have clamps to the month's
54
+ * last day (January 30, month +1 → February 28).
55
+ * @source measured: UIKit lab, iOS 26.2 simulator, `.date` `.wheels`
56
+ * with bounds Jun 1 2026 – Mar 15 2027, Oct 23 2026
57
+ */
58
+ dayClampsToMonth: true,
59
+ },
60
+ } as const;
@@ -5,9 +5,9 @@
5
5
  * @input The domain files of the UIKit constants table
6
6
  * @output Re-exports `springs` and one constant per spring (`SHEET_PRESENT_SPRING`,
7
7
  * …), `scroll`, `timing`, `navigation`, `picker`, `progressBar`,
8
- * `segmentedControl`, the `slider` and switch (`switchControl`) domains, and
9
- * the gesture thresholds (one `UIKIT_*` group per recognizer, their union
10
- * `UIKIT_GESTURES`, and the haptic tables)
8
+ * `segmentedControl`, `datePicker`, `slider` and `switchControl`. It also
9
+ * re-exports the gesture thresholds: one `UIKIT_*` group per recognizer,
10
+ * their union `UIKIT_GESTURES`, and the haptic tables
11
11
  * @position Entry of the UIKit constants table (motion/uikit). Internal to
12
12
  * core: not exported from the package entry point
13
13
  *
@@ -39,6 +39,7 @@ export {
39
39
  type GestureConstantUnit,
40
40
  type HapticFeedbackKind,
41
41
  } from './gestures';
42
+ export {datePicker} from './datePicker';
42
43
  export {navigation, registerNavigationTable} from './navigation';
43
44
  export {picker} from './picker';
44
45
  export {progressBar, registerProgressBarTable} from './progressBar';