@astryxdesign/core 0.7.6-canary.cfbc66f → 0.7.6-canary.d18fdaa

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":"Badge.d.ts","sourceRoot":"","sources":["../../src/Badge/Badge.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAW5C,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,SAAS,CAAC;AAwJ7C;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC;AAEjD,MAAM,WAAW,UAAW,SAAQ,SAAS,CAAC,eAAe,CAAC;IAC5D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACjC;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,KAAK,CAAC,EACpB,OAAmB,EACnB,KAAK,EACL,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,UAAU,+BA6CZ;yBAtDe,KAAK"}
1
+ {"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../src/Badge/Badge.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAY5C,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,SAAS,CAAC;AAwJ7C;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC;AAEjD,MAAM,WAAW,UAAW,SAAQ,SAAS,CAAC,eAAe,CAAC;IAC5D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACjC;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,KAAK,CAAC,EACpB,OAAmB,EACnB,KAAK,EACL,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,UAAU,+BA6CZ;yBAtDe,KAAK"}
@@ -19,6 +19,7 @@ import "../theme/tokens.stylex.js";
19
19
  import { colorVars, spacingVars, radiusVars, fontWeightVars, typeScaleVars } from "../theme/tokens.stylex.js";
20
20
  import { mergeProps } from "../utils/index.js";
21
21
  import { themeProps } from "../utils/themeProps.js";
22
+ import { isRenderable } from "../utils/isRenderable.js";
22
23
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
23
24
  /**
24
25
  * Base badge styles
@@ -187,7 +188,7 @@ export function Badge({
187
188
  // a client component, so it is tracked in #5585.
188
189
  const labelTitle = typeof label === 'number' ? String(label) : typeof label === 'string' && label.length > 0 ? label : undefined;
189
190
  // Whether the label renders anything, which is when the badge grows with it.
190
- const hasLabel = label !== '' && label != null && typeof label !== 'boolean';
191
+ const hasLabel = isRenderable(label);
191
192
  return /*#__PURE__*/_jsxs("span", {
192
193
  ref: ref,
193
194
  ...mergeProps(themeProps('badge', {
@@ -1 +1 @@
1
- {"version":3,"file":"MonthScroller.d.ts","sourceRoot":"","sources":["../../src/DateInput/MonthScroller.tsx"],"names":[],"mappings":"AAoDA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,SAAS,EAQf,MAAM,UAAU,CAAC;AAqNlB,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,aAAa,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;CACxE;AAED,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IACxD,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,oBAAoB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,+BAA+B;IAC/B,YAAY,EAAE,SAAS,GAAG,IAAI,CAAC;IAC/B,4CAA4C;IAC5C,KAAK,EAAE,SAAS,CAAC;IACjB,2DAA2D;IAC3D,cAAc,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC;IAC7C,gCAAgC;IAChC,YAAY,EAAE,SAAS,CAAC;IACxB,kCAAkC;IAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC1C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAC5B,SAAS,EACT,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,KAAK,EACL,cAAc,EACd,YAAY,EACZ,QAAQ,GACT,EAAE,kBAAkB,+BAufpB;yBAlgBe,aAAa"}
1
+ {"version":3,"file":"MonthScroller.d.ts","sourceRoot":"","sources":["../../src/DateInput/MonthScroller.tsx"],"names":[],"mappings":"AAoDA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,SAAS,EAQf,MAAM,UAAU,CAAC;AAgNlB,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,aAAa,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;CACxE;AAED,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IACxD,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,oBAAoB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,+BAA+B;IAC/B,YAAY,EAAE,SAAS,GAAG,IAAI,CAAC;IAC/B,4CAA4C;IAC5C,KAAK,EAAE,SAAS,CAAC;IACjB,2DAA2D;IAC3D,cAAc,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC;IAC7C,gCAAgC;IAChC,YAAY,EAAE,SAAS,CAAC;IACxB,kCAAkC;IAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC1C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAC5B,SAAS,EACT,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,KAAK,EACL,cAAc,EACd,YAAY,EACZ,QAAQ,GACT,EAAE,kBAAkB,+BA0fpB;yBArgBe,aAAa"}
@@ -54,8 +54,10 @@ const DAY_SIZE = dateInputTouchSizes.daySize;
54
54
  */
55
55
  const OVERSCAN = 3;
56
56
 
57
- /** Input on the scroller that ends a steer: the user's from here. */
58
- const USER_INPUT_EVENTS = ['touchstart', 'wheel', 'pointerdown', 'keydown'];
57
+ /** Every listener on the scroller is passive; one options object serves all. */
58
+ const PASSIVE = {
59
+ passive: true
60
+ };
59
61
  const styles = {
60
62
  spacer: {
61
63
  kVAEAm: "x1n2onr6",
@@ -483,23 +485,20 @@ export function MonthScroller({
483
485
  onVisibleMonthChangeRef.current(minMonthIndex + row);
484
486
  });
485
487
  };
486
- scroller.addEventListener('scroll', onScroll, {
487
- passive: true
488
- });
489
- for (const type of USER_INPUT_EVENTS) {
490
- scroller.addEventListener(type, endSteer, {
491
- passive: true
492
- });
493
- }
494
- scroller.addEventListener('touchstart', onTouchStart, {
495
- passive: true
496
- });
488
+ scroller.addEventListener('scroll', onScroll, PASSIVE);
489
+ // The user's input on the scroller ends a steer: from here, it's theirs.
490
+ scroller.addEventListener('touchstart', endSteer, PASSIVE);
491
+ scroller.addEventListener('wheel', endSteer, PASSIVE);
492
+ scroller.addEventListener('pointerdown', endSteer, PASSIVE);
493
+ scroller.addEventListener('keydown', endSteer, PASSIVE);
494
+ scroller.addEventListener('touchstart', onTouchStart, PASSIVE);
497
495
  return () => {
498
- scroller.removeEventListener('scroll', onScroll);
499
- for (const type of USER_INPUT_EVENTS) {
500
- scroller.removeEventListener(type, endSteer);
501
- }
502
- scroller.removeEventListener('touchstart', onTouchStart);
496
+ scroller.removeEventListener('scroll', onScroll, PASSIVE);
497
+ scroller.removeEventListener('touchstart', endSteer, PASSIVE);
498
+ scroller.removeEventListener('wheel', endSteer, PASSIVE);
499
+ scroller.removeEventListener('pointerdown', endSteer, PASSIVE);
500
+ scroller.removeEventListener('keydown', endSteer, PASSIVE);
501
+ scroller.removeEventListener('touchstart', onTouchStart, PASSIVE);
503
502
  if (frameRef.current != null) {
504
503
  cancelAnimationFrame(frameRef.current);
505
504
  frameRef.current = undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenuGroup.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuGroup.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAQ,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAQ5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAgB5C,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAClD,SAAS,CAAC,cAAc,CAAC,EACzB,MAAM,CACP;IACC;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,mDAAmD;IACnD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,iBAAiB,EAAE,cAAc,EACjC,GAAG,IAAI,EACR,EAAE,sBAAsB,+BAwBxB;yBAjCe,iBAAiB"}
1
+ {"version":3,"file":"DropdownMenuGroup.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuGroup.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAQ,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAQ5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAiB5C,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAClD,SAAS,CAAC,cAAc,CAAC,EACzB,MAAM,CACP;IACC;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,mDAAmD;IACnD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,iBAAiB,EAAE,cAAc,EACjC,GAAG,IAAI,EACR,EAAE,sBAAsB,+BAwBxB;yBAjCe,iBAAiB"}
@@ -29,6 +29,7 @@ import "../theme/tokens.stylex.js";
29
29
  import { colorVars, spacingVars, typographyVars, typeScaleVars } from "../theme/tokens.stylex.js";
30
30
  import { mergeProps } from "../utils/index.js";
31
31
  import { themeProps } from "../utils/themeProps.js";
32
+ import { isRenderable } from "../utils/isRenderable.js";
32
33
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
33
34
  /**
34
35
  * A titled group of menu rows.
@@ -64,7 +65,7 @@ export function DropdownMenuGroup({
64
65
  ...rest
65
66
  }) {
66
67
  const headingId = useId();
67
- const hasTitle = title != null && title !== '' && title !== false;
68
+ const hasTitle = isRenderable(title);
68
69
  return /*#__PURE__*/_jsxs("div", {
69
70
  ...rest,
70
71
  ref: ref,
@@ -1 +1 @@
1
- {"version":3,"file":"FileInput.d.ts","sourceRoot":"","sources":["../../src/FileInput/FileInput.tsx"],"names":[],"mappings":"AAuCA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACxB,MAAM,UAAU,CAAC;AAUlB,YAAY,EACV,WAAW,IAAI,eAAe,EAC9B,eAAe,IAAI,mBAAmB,GACvC,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAuP9C,MAAM,WAAW,cAAe,SAAQ,IAAI,CAC1C,mBAAmB,EACnB,UAAU,GAAG,cAAc,GAAG,OAAO,CACtC;IACC;;OAEG;IACH,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,KAAK,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IAC5B;;OAEG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,aAAqB,EACrB,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,MAAM,EACN,UAAkB,EAClB,OAAO,EACP,QAAQ,EACR,UAAkB,EAClB,eAAe,EACf,UAAkB,EAClB,SAAiB,EACjB,MAAM,EAAE,UAAU,EAClB,aAA0B,EAC1B,WAAW,EACX,WAAW,EACX,IAAc,EACd,UAAkB,EAClB,YAAY,EACZ,KAAK,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,GAAG,EACH,EAAE,EAAE,MAAM,EACV,GAAG,SAAS,EACb,EAAE,cAAc,+BA8ehB;yBA7gBe,SAAS"}
1
+ {"version":3,"file":"FileInput.d.ts","sourceRoot":"","sources":["../../src/FileInput/FileInput.tsx"],"names":[],"mappings":"AAuCA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACxB,MAAM,UAAU,CAAC;AAUlB,YAAY,EACV,WAAW,IAAI,eAAe,EAC9B,eAAe,IAAI,mBAAmB,GACvC,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AA2P9C,MAAM,WAAW,cAAe,SAAQ,IAAI,CAC1C,mBAAmB,EACnB,UAAU,GAAG,cAAc,GAAG,OAAO,CACtC;IACC;;OAEG;IACH,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,KAAK,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IAC5B;;OAEG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,aAAqB,EACrB,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,MAAM,EACN,UAAkB,EAClB,OAAO,EACP,QAAQ,EACR,UAAkB,EAClB,eAAe,EACf,UAAkB,EAClB,SAAiB,EACjB,MAAM,EAAE,UAAU,EAClB,aAA0B,EAC1B,WAAW,EACX,WAAW,EACX,IAAc,EACd,UAAkB,EAClB,YAAY,EACZ,KAAK,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,GAAG,EACH,EAAE,EAAE,MAAM,EACV,GAAG,SAAS,EACb,EAAE,cAAc,+BA0fhB;yBAzhBe,SAAS"}
@@ -164,6 +164,11 @@ const styles = {
164
164
  kSiTet: "xbyyjgo",
165
165
  kVAM5u: "xvy26l8 x6q1khz",
166
166
  $$css: true
167
+ },
168
+ busy: {
169
+ kkrTdU: "xt0e3qv",
170
+ kGVxlE: "x1gnnqk1",
171
+ $$css: true
167
172
  }
168
173
  };
169
174
  const statusBorderStyles = {
@@ -243,6 +248,10 @@ export function FileInput({
243
248
  const [, startTransition] = useTransition();
244
249
  const [optimisticValue, setOptimisticValue] = useOptimistic(value);
245
250
  const isBusy = isLoading || optimisticValue !== value;
251
+ const isSelectionDisabled = isDisabled || isBusy;
252
+ if (isDragOver && isSelectionDisabled) {
253
+ setIsDragOver(false);
254
+ }
246
255
  // A pick is one user action, already announced as "file selected"; a busy
247
256
  // period it starts (changeAction, or a caller setting isLoading from
248
257
  // onChange) adds no second message (spec:AST-086 FR6). The mark lasts until
@@ -301,7 +310,7 @@ export function FileInput({
301
310
  const defaultPlaceholder = isMultiple ? t('@astryx.fileInput.placeholderMultiple') : t('@astryx.fileInput.placeholder');
302
311
  const displayPlaceholder = placeholder ?? defaultPlaceholder;
303
312
  const handleFiles = useCallback(fileList => {
304
- if (isDisabled) {
313
+ if (isSelectionDisabled) {
305
314
  return;
306
315
  }
307
316
  const {
@@ -340,7 +349,7 @@ export function FileInput({
340
349
  await changeAction(result);
341
350
  });
342
351
  }
343
- }, [accept, isDisabled, isMultiple, maxFiles, maxSize, onChange, changeAction, startTransition, setOptimisticValue, announce, t, formatList]);
352
+ }, [accept, isSelectionDisabled, isMultiple, maxFiles, maxSize, onChange, changeAction, startTransition, setOptimisticValue, announce, t, formatList]);
344
353
  const handleInputChange = useCallback(e => {
345
354
  const fileList = Array.from(e.target.files ?? []);
346
355
  handleFiles(fileList);
@@ -377,20 +386,22 @@ export function FileInput({
377
386
  }
378
387
  }, [onChange, changeAction, startTransition, setOptimisticValue]);
379
388
  const handleClick = useCallback(() => {
380
- if (!isDisabled) {
389
+ if (!isSelectionDisabled) {
381
390
  inputRef.current?.click();
382
391
  }
383
- }, [isDisabled]);
392
+ }, [isSelectionDisabled]);
384
393
 
385
394
  // Explicit keyboard activation so Enter/Space open the picker even where a
386
- // synthetic keydown does not raise a native click. The isDisabled guard
387
- // blocks activation while focusable-disabled.
395
+ // synthetic keydown does not raise a native click. Disabled and busy triggers
396
+ // also cancel the default activation while remaining focusable.
388
397
  const handleKeyDown = useCallback(e => {
389
- if ((e.key === 'Enter' || e.key === ' ') && !isDisabled) {
398
+ if (e.key === 'Enter' || e.key === ' ') {
390
399
  e.preventDefault();
391
- inputRef.current?.click();
400
+ if (!isSelectionDisabled) {
401
+ inputRef.current?.click();
402
+ }
392
403
  }
393
- }, [isDisabled]);
404
+ }, [isSelectionDisabled]);
394
405
 
395
406
  // Make the surface clickable without giving it an interactive role. Clicks on
396
407
  // the nested clear/status controls are ignored by the hook, so those buttons
@@ -401,22 +412,22 @@ export function FileInput({
401
412
  } = useClickableContainer({
402
413
  containerRef,
403
414
  onClick: handleClick,
404
- disabled: isDisabled
415
+ disabled: isSelectionDisabled
405
416
  });
406
417
  const handleDragEnter = useCallback(e => {
407
418
  e.preventDefault();
408
419
  e.stopPropagation();
409
- if (!isDisabled && mode === 'dropzone') {
420
+ if (!isSelectionDisabled && mode === 'dropzone') {
410
421
  setIsDragOver(true);
411
422
  }
412
- }, [isDisabled, mode]);
423
+ }, [isSelectionDisabled, mode]);
413
424
  const handleDragOver = useCallback(e => {
414
425
  e.preventDefault();
415
426
  e.stopPropagation();
416
- if (!isDisabled && mode === 'dropzone') {
427
+ if (!isSelectionDisabled && mode === 'dropzone') {
417
428
  setIsDragOver(true);
418
429
  }
419
- }, [isDisabled, mode]);
430
+ }, [isSelectionDisabled, mode]);
420
431
  const handleDragLeave = useCallback(e => {
421
432
  e.preventDefault();
422
433
  e.stopPropagation();
@@ -432,14 +443,14 @@ export function FileInput({
432
443
  e.preventDefault();
433
444
  e.stopPropagation();
434
445
  setIsDragOver(false);
435
- if (isDisabled || mode !== 'dropzone') {
446
+ if (isSelectionDisabled || mode !== 'dropzone') {
436
447
  return;
437
448
  }
438
449
  const fileList = Array.from(e.dataTransfer.files);
439
450
  if (fileList.length > 0) {
440
451
  handleFiles(fileList);
441
452
  }
442
- }, [isDisabled, mode, handleFiles]);
453
+ }, [isSelectionDisabled, mode, handleFiles]);
443
454
  const hasFiles = optimisticValue != null && (Array.isArray(optimisticValue) ? optimisticValue.length > 0 : true);
444
455
  const fileNames = hasFiles ? Array.isArray(optimisticValue) ? optimisticValue.map(f => f.name).join(', ') : optimisticValue?.name ?? '' : null;
445
456
 
@@ -560,13 +571,13 @@ export function FileInput({
560
571
  // keyboard focus. Handlers are gated internally by isEnabled.
561
572
  disabledMessageTooltip.ref(el);
562
573
  },
563
- onClick: !isDisabled ? onContainerClick : undefined,
564
- onMouseUp: !isDisabled ? onContainerMouseUp : undefined,
574
+ onClick: !isSelectionDisabled ? onContainerClick : undefined,
575
+ onMouseUp: !isSelectionDisabled ? onContainerMouseUp : undefined,
565
576
  ...dragDropProps,
566
577
  ...mergeProps(themeProps('file-input', {
567
578
  mode,
568
579
  status: status?.type ?? null
569
- }), stylex.props(isDropzone ? styles.dropzone : styles.compact, isDropzone && !isDisabled && styles.dropzoneHover, isDropzone && isDragOver && styles.dropzoneActive, isDropzone && isDisabled && styles.dropzoneDisabled, !isDropzone && isDisabled && styles.compactDisabled, status && statusBorderStyles[status.type], controlXstyle), controlClassName, controlStyle),
580
+ }), stylex.props(isDropzone ? styles.dropzone : styles.compact, isDropzone && !isSelectionDisabled && styles.dropzoneHover, isDropzone && !isSelectionDisabled && isDragOver && styles.dropzoneActive, isDropzone && isDisabled && styles.dropzoneDisabled, !isDropzone && isDisabled && styles.compactDisabled, isBusy && styles.busy, status && statusBorderStyles[status.type], controlXstyle), controlClassName, controlStyle),
570
581
  children: [/*#__PURE__*/_jsx(VisuallyHidden, {
571
582
  children: /*#__PURE__*/_jsx("button", {
572
583
  ref: triggerRef,
@@ -574,7 +585,7 @@ export function FileInput({
574
585
  // A disabled trigger keeps its Tab stop (spec:AST-085 FR1).
575
586
  ,
576
587
  tabIndex: 0,
577
- "aria-disabled": isDisabled ? 'true' : undefined,
588
+ "aria-disabled": isSelectionDisabled ? 'true' : undefined,
578
589
  onClick: handleClick,
579
590
  onKeyDown: handleKeyDown
580
591
  // aria-label suppresses child content from the accessible name, so
@@ -602,9 +613,9 @@ export function FileInput({
602
613
  ,
603
614
  form: isDisabled ? '' : undefined
604
615
  // A label or description click activates this input directly; cancel
605
- // it so a disabled input never opens the picker (FR2).
616
+ // it so a disabled or busy input never opens the picker (FR2).
606
617
  ,
607
- onClick: isDisabled ? event => event.preventDefault() : rest.onClick,
618
+ onClick: isSelectionDisabled ? event => event.preventDefault() : rest.onClick,
608
619
  onChange: handleInputChange
609
620
  // The visually-hidden native input is never focused (tabIndex={-1});
610
621
  // its describedby/required/invalid live on the trigger button.
@@ -1 +1 @@
1
- {"version":3,"file":"NumberInput.d.ts","sourceRoot":"","sources":["../../src/NumberInput/NumberInput.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"NumberInput.d.ts","sourceRoot":"","sources":["../../src/NumberInput/NumberInput.tsx"],"names":[],"mappings":"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"}
@@ -17,7 +17,8 @@
17
17
  * - /apps/storybook/stories/NumberInput.stories.tsx (storybook stories)
18
18
  * - /packages/cli/assets/templates/blocks/components/NumberInput/ (showcase blocks)
19
19
  */
20
- import { use, useId, useState, useMemo, useCallback, useLayoutEffect, useRef } from 'react';
20
+ import { use, useId, useState, useMemo, useCallback, useRef } from 'react';
21
+ import { flushSync } from 'react-dom';
21
22
  import * as stylex from '@stylexjs/stylex';
22
23
  import "../theme/tokens.stylex.js";
23
24
  import { colorVars, sizeVars, spacingVars, typographyVars, typeScaleVars, borderVars } from "../theme/tokens.stylex.js";
@@ -367,27 +368,22 @@ export function NumberInput({
367
368
  // `formatValue` the text at rest differs from the editable text, and putting
368
369
  // the editable text in the input after focus collapses that selection to the
369
370
  // end, so typing would append. When focus arrives with the text selected,
370
- // select the swapped-in text too.
371
- const reselectOnFocusRef = useRef(false);
371
+ // the focus handler commits the swap and selects the swapped-in text itself:
372
+ // the work one event causes stays in its handler
373
+ // (architecture:react-component-runtime/INV2).
372
374
  const handleFocus = useCallback(e => {
373
375
  const input = e.currentTarget;
374
- // Only when focusing swaps the text: otherwise no render follows to
375
- // consume the flag, and the first keystroke would.
376
376
  const editableText = pendingInput ?? (value == null ? '' : formatEditableNumber(value, locale));
377
- reselectOnFocusRef.current = input.value.length > 0 && input.value !== editableText && input.selectionStart === 0 && input.selectionEnd === input.value.length;
377
+ const shouldReselect = input.value.length > 0 && input.value !== editableText && input.selectionStart === 0 && input.selectionEnd === input.value.length;
378
378
  setIsFocused(true);
379
379
  onFocus?.(e);
380
- }, [onFocus, pendingInput, value, locale]);
381
- useLayoutEffect(() => {
382
- if (!reselectOnFocusRef.current) {
383
- return;
384
- }
385
- reselectOnFocusRef.current = false;
386
- const input = inputRef.current;
387
- if (input != null && input === document.activeElement) {
380
+ if (shouldReselect) {
381
+ // Commit the swap now, so the editable text is in the input to select.
382
+ // eslint-disable-next-line @eslint-react/dom-no-flush-sync -- the editable text must be in the input before the focus event selects it
383
+ flushSync(() => {});
388
384
  input.setSelectionRange(0, input.value.length);
389
385
  }
390
- }, [displayValue]);
386
+ }, [onFocus, pendingInput, value, locale]);
391
387
  const commitPendingInput = useCallback(trigger => {
392
388
  if (pendingInput === null) {
393
389
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"BaseTypeahead.d.ts","sourceRoot":"","sources":["../../src/Typeahead/BaseTypeahead.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,EAMZ,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AA2BnD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,cAAc,EAAE,YAAY,EAAC,MAAM,SAAS,CAAC;AAY1D,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,cAAc,CAAE,SAAQ,IAAI,CACxE,SAAS,CAAC,WAAW,CAAC,EACtB,UAAU,CACX;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC;;OAEG;IACH,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAE9B;;OAEG;IACH,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IAEhB;;OAEG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAEnC;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC;IAEpC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;IAE5B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;;;;;OASG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAExC;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;IAEtD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,WAAW,CAAC,EAAE,YAAY,CAAC;IAE3B;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE1C;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAE/D;;;;OAIG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC3B;AAoKD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,aAAa,EAi7BrB,CAAC,CAAC,SAAS,cAAc,EAC5B,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,KACzB,KAAK,CAAC,YAAY,CAAC"}
1
+ {"version":3,"file":"BaseTypeahead.d.ts","sourceRoot":"","sources":["../../src/Typeahead/BaseTypeahead.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,EAMZ,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AA2BnD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,cAAc,EAAE,YAAY,EAAC,MAAM,SAAS,CAAC;AAY1D,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,cAAc,CAAE,SAAQ,IAAI,CACxE,SAAS,CAAC,WAAW,CAAC,EACtB,UAAU,CACX;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC;;OAEG;IACH,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAE9B;;OAEG;IACH,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IAEhB;;OAEG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAEnC;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC;IAEpC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;IAE5B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;;;;;OASG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAExC;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;IAEtD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,WAAW,CAAC,EAAE,YAAY,CAAC;IAE3B;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE1C;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAE/D;;;;OAIG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC3B;AAoKD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,aAAa,EAi8BrB,CAAC,CAAC,SAAS,cAAc,EAC5B,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,KACzB,KAAK,CAAC,YAAY,CAAC"}
@@ -715,14 +715,21 @@ export const BaseTypeahead = function BaseTypeahead({
715
715
  if (!popover.isOpen) {
716
716
  if (e.key === 'ArrowDown' && (hasEntriesOnFocus || query.length > 0)) {
717
717
  e.preventDefault();
718
- if (results.length > 0) {
718
+ // Results held from a replaced search source are not current
719
+ // (spec:AST-092 FR4): they count as none, so the new source loads.
720
+ const hasResultsFromCurrentSource = results.length > 0 && resultsGenRef.current >= sourceGenRef.current;
721
+ const isQueryBelowMinLength = isBelowMinQueryLength(query, minQueryLength);
722
+ if (hasResultsFromCurrentSource) {
719
723
  popover.show();
720
724
  setHighlightedIndex(0);
725
+ } else if (results.length > 0 && query.length > 0 && !isQueryBelowMinLength) {
726
+ // The typed query runs again against the new source.
727
+ void performSearch(query);
721
728
  } else if (hasEntriesOnFocus &&
722
729
  // A below-threshold query was never searched; falling back to the
723
730
  // bootstrap entries here would open a menu of suggestions that
724
731
  // ignore what the user has already typed.
725
- !isBelowMinQueryLength(query, minQueryLength)) {
732
+ !isQueryBelowMinLength) {
726
733
  void performBootstrap();
727
734
  }
728
735
  }
@@ -776,7 +783,7 @@ export const BaseTypeahead = function BaseTypeahead({
776
783
  }
777
784
  break;
778
785
  }
779
- }, [isDisabled, popover, results, highlightedIndex, handleSelect, hasEntriesOnFocus, query, minQueryLength, performBootstrap, externalOnKeyDown]);
786
+ }, [isDisabled, popover, results, highlightedIndex, handleSelect, hasEntriesOnFocus, query, minQueryLength, performBootstrap, performSearch, externalOnKeyDown]);
780
787
 
781
788
  // Generate item ID for accessibility
782
789
  const getItemId = useCallback(index => `${listboxId}-option-${index}`, [listboxId]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.7.6-canary.cfbc66f",
3
+ "version": "0.7.6-canary.d18fdaa",
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",
@@ -860,9 +860,9 @@
860
860
  "react-dom": ">=19.0.0"
861
861
  },
862
862
  "devDependencies": {
863
- "@astryxdesign/a11y-spec": "0.7.6-canary.cfbc66f",
864
- "@astryxdesign/cli": "0.7.6-canary.cfbc66f",
865
- "@astryxdesign/interaction-harness": "0.7.6-canary.cfbc66f",
863
+ "@astryxdesign/a11y-spec": "0.7.6-canary.d18fdaa",
864
+ "@astryxdesign/cli": "0.7.6-canary.d18fdaa",
865
+ "@astryxdesign/interaction-harness": "0.7.6-canary.d18fdaa",
866
866
  "@babel/cli": "^7.29.7",
867
867
  "@babel/core": "^7.29.7",
868
868
  "@babel/preset-react": "^7.29.7",
@@ -26,6 +26,7 @@ import {
26
26
  } from '../theme/tokens.stylex';
27
27
  import {mergeProps} from '../utils';
28
28
  import {themeProps} from '../utils/themeProps';
29
+ import {isRenderable} from '../utils/isRenderable';
29
30
  import type {BadgeVariantMap} from './index';
30
31
 
31
32
  /**
@@ -249,7 +250,7 @@ export function Badge({
249
250
  ? label
250
251
  : undefined;
251
252
  // Whether the label renders anything, which is when the badge grows with it.
252
- const hasLabel = label !== '' && label != null && typeof label !== 'boolean';
253
+ const hasLabel = isRenderable(label);
253
254
 
254
255
  return (
255
256
  <span
@@ -85,13 +85,8 @@ const DAY_SIZE = dateInputTouchSizes.daySize;
85
85
  */
86
86
  const OVERSCAN = 3;
87
87
 
88
- /** Input on the scroller that ends a steer: the user's from here. */
89
- const USER_INPUT_EVENTS = [
90
- 'touchstart',
91
- 'wheel',
92
- 'pointerdown',
93
- 'keydown',
94
- ] as const;
88
+ /** Every listener on the scroller is passive; one options object serves all. */
89
+ const PASSIVE: AddEventListenerOptions = {passive: true};
95
90
 
96
91
  const styles = stylex.create({
97
92
  scroller: {
@@ -682,17 +677,20 @@ export function MonthScroller({
682
677
  onVisibleMonthChangeRef.current(minMonthIndex + row);
683
678
  });
684
679
  };
685
- scroller.addEventListener('scroll', onScroll, {passive: true});
686
- for (const type of USER_INPUT_EVENTS) {
687
- scroller.addEventListener(type, endSteer, {passive: true});
688
- }
689
- scroller.addEventListener('touchstart', onTouchStart, {passive: true});
680
+ scroller.addEventListener('scroll', onScroll, PASSIVE);
681
+ // The user's input on the scroller ends a steer: from here, it's theirs.
682
+ scroller.addEventListener('touchstart', endSteer, PASSIVE);
683
+ scroller.addEventListener('wheel', endSteer, PASSIVE);
684
+ scroller.addEventListener('pointerdown', endSteer, PASSIVE);
685
+ scroller.addEventListener('keydown', endSteer, PASSIVE);
686
+ scroller.addEventListener('touchstart', onTouchStart, PASSIVE);
690
687
  return () => {
691
- scroller.removeEventListener('scroll', onScroll);
692
- for (const type of USER_INPUT_EVENTS) {
693
- scroller.removeEventListener(type, endSteer);
694
- }
695
- scroller.removeEventListener('touchstart', onTouchStart);
688
+ scroller.removeEventListener('scroll', onScroll, PASSIVE);
689
+ scroller.removeEventListener('touchstart', endSteer, PASSIVE);
690
+ scroller.removeEventListener('wheel', endSteer, PASSIVE);
691
+ scroller.removeEventListener('pointerdown', endSteer, PASSIVE);
692
+ scroller.removeEventListener('keydown', endSteer, PASSIVE);
693
+ scroller.removeEventListener('touchstart', onTouchStart, PASSIVE);
696
694
  if (frameRef.current != null) {
697
695
  cancelAnimationFrame(frameRef.current);
698
696
  frameRef.current = undefined;
@@ -35,6 +35,7 @@ import {
35
35
  import type {BaseProps} from '../BaseProps';
36
36
  import {mergeProps} from '../utils';
37
37
  import {themeProps} from '../utils/themeProps';
38
+ import {isRenderable} from '../utils/isRenderable';
38
39
 
39
40
  const styles = stylex.create({
40
41
  heading: {
@@ -98,7 +99,7 @@ export function DropdownMenuGroup({
98
99
  ...rest
99
100
  }: DropdownMenuGroupProps) {
100
101
  const headingId = useId();
101
- const hasTitle = title != null && title !== '' && title !== false;
102
+ const hasTitle = isRenderable(title);
102
103
 
103
104
  return (
104
105
  <div
@@ -238,6 +238,10 @@ const styles = stylex.create({
238
238
  ':focus-within': colorVars['--color-accent'],
239
239
  },
240
240
  },
241
+ busy: {
242
+ cursor: 'default',
243
+ boxShadow: 'none',
244
+ },
241
245
  hiddenInput: {
242
246
  position: 'absolute',
243
247
  width: 1,
@@ -500,6 +504,10 @@ export function FileInput({
500
504
  const [, startTransition] = useTransition();
501
505
  const [optimisticValue, setOptimisticValue] = useOptimistic(value);
502
506
  const isBusy = isLoading || optimisticValue !== value;
507
+ const isSelectionDisabled = isDisabled || isBusy;
508
+ if (isDragOver && isSelectionDisabled) {
509
+ setIsDragOver(false);
510
+ }
503
511
  // A pick is one user action, already announced as "file selected"; a busy
504
512
  // period it starts (changeAction, or a caller setting isLoading from
505
513
  // onChange) adds no second message (spec:AST-086 FR6). The mark lasts until
@@ -574,7 +582,7 @@ export function FileInput({
574
582
 
575
583
  const handleFiles = useCallback(
576
584
  (fileList: File[]) => {
577
- if (isDisabled) {
585
+ if (isSelectionDisabled) {
578
586
  return;
579
587
  }
580
588
 
@@ -625,7 +633,7 @@ export function FileInput({
625
633
  },
626
634
  [
627
635
  accept,
628
- isDisabled,
636
+ isSelectionDisabled,
629
637
  isMultiple,
630
638
  maxFiles,
631
639
  maxSize,
@@ -681,22 +689,24 @@ export function FileInput({
681
689
  );
682
690
 
683
691
  const handleClick = useCallback(() => {
684
- if (!isDisabled) {
692
+ if (!isSelectionDisabled) {
685
693
  inputRef.current?.click();
686
694
  }
687
- }, [isDisabled]);
695
+ }, [isSelectionDisabled]);
688
696
 
689
697
  // Explicit keyboard activation so Enter/Space open the picker even where a
690
- // synthetic keydown does not raise a native click. The isDisabled guard
691
- // blocks activation while focusable-disabled.
698
+ // synthetic keydown does not raise a native click. Disabled and busy triggers
699
+ // also cancel the default activation while remaining focusable.
692
700
  const handleKeyDown = useCallback(
693
701
  (e: React.KeyboardEvent) => {
694
- if ((e.key === 'Enter' || e.key === ' ') && !isDisabled) {
702
+ if (e.key === 'Enter' || e.key === ' ') {
695
703
  e.preventDefault();
696
- inputRef.current?.click();
704
+ if (!isSelectionDisabled) {
705
+ inputRef.current?.click();
706
+ }
697
707
  }
698
708
  },
699
- [isDisabled],
709
+ [isSelectionDisabled],
700
710
  );
701
711
 
702
712
  // Make the surface clickable without giving it an interactive role. Clicks on
@@ -706,29 +716,29 @@ export function FileInput({
706
716
  useClickableContainer({
707
717
  containerRef,
708
718
  onClick: handleClick,
709
- disabled: isDisabled,
719
+ disabled: isSelectionDisabled,
710
720
  });
711
721
 
712
722
  const handleDragEnter = useCallback(
713
723
  (e: DragEvent) => {
714
724
  e.preventDefault();
715
725
  e.stopPropagation();
716
- if (!isDisabled && mode === 'dropzone') {
726
+ if (!isSelectionDisabled && mode === 'dropzone') {
717
727
  setIsDragOver(true);
718
728
  }
719
729
  },
720
- [isDisabled, mode],
730
+ [isSelectionDisabled, mode],
721
731
  );
722
732
 
723
733
  const handleDragOver = useCallback(
724
734
  (e: DragEvent) => {
725
735
  e.preventDefault();
726
736
  e.stopPropagation();
727
- if (!isDisabled && mode === 'dropzone') {
737
+ if (!isSelectionDisabled && mode === 'dropzone') {
728
738
  setIsDragOver(true);
729
739
  }
730
740
  },
731
- [isDisabled, mode],
741
+ [isSelectionDisabled, mode],
732
742
  );
733
743
 
734
744
  const handleDragLeave = useCallback((e: DragEvent) => {
@@ -751,7 +761,7 @@ export function FileInput({
751
761
  e.preventDefault();
752
762
  e.stopPropagation();
753
763
  setIsDragOver(false);
754
- if (isDisabled || mode !== 'dropzone') {
764
+ if (isSelectionDisabled || mode !== 'dropzone') {
755
765
  return;
756
766
  }
757
767
  const fileList = Array.from(e.dataTransfer.files);
@@ -759,7 +769,7 @@ export function FileInput({
759
769
  handleFiles(fileList);
760
770
  }
761
771
  },
762
- [isDisabled, mode, handleFiles],
772
+ [isSelectionDisabled, mode, handleFiles],
763
773
  );
764
774
 
765
775
  const hasFiles =
@@ -889,17 +899,21 @@ export function FileInput({
889
899
  // keyboard focus. Handlers are gated internally by isEnabled.
890
900
  disabledMessageTooltip.ref(el);
891
901
  }}
892
- onClick={!isDisabled ? onContainerClick : undefined}
893
- onMouseUp={!isDisabled ? onContainerMouseUp : undefined}
902
+ onClick={!isSelectionDisabled ? onContainerClick : undefined}
903
+ onMouseUp={!isSelectionDisabled ? onContainerMouseUp : undefined}
894
904
  {...dragDropProps}
895
905
  {...mergeProps(
896
906
  themeProps('file-input', {mode, status: status?.type ?? null}),
897
907
  stylex.props(
898
908
  isDropzone ? styles.dropzone : styles.compact,
899
- isDropzone && !isDisabled && styles.dropzoneHover,
900
- isDropzone && isDragOver && styles.dropzoneActive,
909
+ isDropzone && !isSelectionDisabled && styles.dropzoneHover,
910
+ isDropzone &&
911
+ !isSelectionDisabled &&
912
+ isDragOver &&
913
+ styles.dropzoneActive,
901
914
  isDropzone && isDisabled && styles.dropzoneDisabled,
902
915
  !isDropzone && isDisabled && styles.compactDisabled,
916
+ isBusy && styles.busy,
903
917
  status && statusBorderStyles[status.type],
904
918
  controlXstyle,
905
919
  ),
@@ -919,7 +933,7 @@ export function FileInput({
919
933
  type="button"
920
934
  // A disabled trigger keeps its Tab stop (spec:AST-085 FR1).
921
935
  tabIndex={0}
922
- aria-disabled={isDisabled ? 'true' : undefined}
936
+ aria-disabled={isSelectionDisabled ? 'true' : undefined}
923
937
  onClick={handleClick}
924
938
  onKeyDown={handleKeyDown}
925
939
  // aria-label suppresses child content from the accessible name, so
@@ -947,8 +961,10 @@ export function FileInput({
947
961
  // submitted and `required` does not block submission (FR3).
948
962
  form={isDisabled ? '' : undefined}
949
963
  // A label or description click activates this input directly; cancel
950
- // it so a disabled input never opens the picker (FR2).
951
- onClick={isDisabled ? event => event.preventDefault() : rest.onClick}
964
+ // it so a disabled or busy input never opens the picker (FR2).
965
+ onClick={
966
+ isSelectionDisabled ? event => event.preventDefault() : rest.onClick
967
+ }
952
968
  onChange={handleInputChange}
953
969
  // The visually-hidden native input is never focused (tabIndex={-1});
954
970
  // its describedby/required/invalid live on the trigger button.
@@ -24,12 +24,12 @@ import {
24
24
  useState,
25
25
  useMemo,
26
26
  useCallback,
27
- useLayoutEffect,
28
27
  useRef,
29
28
  type FocusEvent,
30
29
  type KeyboardEvent,
31
30
  type ReactNode,
32
31
  } from 'react';
32
+ import {flushSync} from 'react-dom';
33
33
  import * as stylex from '@stylexjs/stylex';
34
34
  import {
35
35
  colorVars,
@@ -723,39 +723,32 @@ export function NumberInput({
723
723
  // `formatValue` the text at rest differs from the editable text, and putting
724
724
  // the editable text in the input after focus collapses that selection to the
725
725
  // end, so typing would append. When focus arrives with the text selected,
726
- // select the swapped-in text too.
727
- const reselectOnFocusRef = useRef(false);
728
-
726
+ // the focus handler commits the swap and selects the swapped-in text itself:
727
+ // the work one event causes stays in its handler
728
+ // (architecture:react-component-runtime/INV2).
729
729
  const handleFocus = useCallback(
730
730
  (e: FocusEvent<HTMLInputElement>) => {
731
731
  const input = e.currentTarget;
732
- // Only when focusing swaps the text: otherwise no render follows to
733
- // consume the flag, and the first keystroke would.
734
732
  const editableText =
735
733
  pendingInput ??
736
734
  (value == null ? '' : formatEditableNumber(value, locale));
737
- reselectOnFocusRef.current =
735
+ const shouldReselect =
738
736
  input.value.length > 0 &&
739
737
  input.value !== editableText &&
740
738
  input.selectionStart === 0 &&
741
739
  input.selectionEnd === input.value.length;
742
740
  setIsFocused(true);
743
741
  onFocus?.(e);
742
+ if (shouldReselect) {
743
+ // Commit the swap now, so the editable text is in the input to select.
744
+ // eslint-disable-next-line @eslint-react/dom-no-flush-sync -- the editable text must be in the input before the focus event selects it
745
+ flushSync(() => {});
746
+ input.setSelectionRange(0, input.value.length);
747
+ }
744
748
  },
745
749
  [onFocus, pendingInput, value, locale],
746
750
  );
747
751
 
748
- useLayoutEffect(() => {
749
- if (!reselectOnFocusRef.current) {
750
- return;
751
- }
752
- reselectOnFocusRef.current = false;
753
- const input = inputRef.current;
754
- if (input != null && input === document.activeElement) {
755
- input.setSelectionRange(0, input.value.length);
756
- }
757
- }, [displayValue]);
758
-
759
752
  const commitPendingInput = useCallback(
760
753
  (trigger: 'blur' | 'Enter') => {
761
754
  if (pendingInput === null) {
@@ -1091,15 +1091,30 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
1091
1091
  if (!popover.isOpen) {
1092
1092
  if (e.key === 'ArrowDown' && (hasEntriesOnFocus || query.length > 0)) {
1093
1093
  e.preventDefault();
1094
- if (results.length > 0) {
1094
+ // Results held from a replaced search source are not current
1095
+ // (spec:AST-092 FR4): they count as none, so the new source loads.
1096
+ const hasResultsFromCurrentSource =
1097
+ results.length > 0 && resultsGenRef.current >= sourceGenRef.current;
1098
+ const isQueryBelowMinLength = isBelowMinQueryLength(
1099
+ query,
1100
+ minQueryLength,
1101
+ );
1102
+ if (hasResultsFromCurrentSource) {
1095
1103
  popover.show();
1096
1104
  setHighlightedIndex(0);
1105
+ } else if (
1106
+ results.length > 0 &&
1107
+ query.length > 0 &&
1108
+ !isQueryBelowMinLength
1109
+ ) {
1110
+ // The typed query runs again against the new source.
1111
+ void performSearch(query);
1097
1112
  } else if (
1098
1113
  hasEntriesOnFocus &&
1099
1114
  // A below-threshold query was never searched; falling back to the
1100
1115
  // bootstrap entries here would open a menu of suggestions that
1101
1116
  // ignore what the user has already typed.
1102
- !isBelowMinQueryLength(query, minQueryLength)
1117
+ !isQueryBelowMinLength
1103
1118
  ) {
1104
1119
  void performBootstrap();
1105
1120
  }
@@ -1170,6 +1185,7 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
1170
1185
  query,
1171
1186
  minQueryLength,
1172
1187
  performBootstrap,
1188
+ performSearch,
1173
1189
  externalOnKeyDown,
1174
1190
  ],
1175
1191
  );