@oxy.so/bloom 4.23.0 → 4.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/docs/adoption-matrix.mdx +10 -10
  2. package/docs/ai-chat.mdx +7 -3
  3. package/docs/bottom-sheet.mdx +2 -0
  4. package/docs/dialog.mdx +28 -0
  5. package/docs/sidebar.mdx +2 -0
  6. package/lib/commonjs/ai-chat/AiChatContainer.js +22 -11
  7. package/lib/commonjs/ai-chat/AiChatContainer.js.map +1 -1
  8. package/lib/commonjs/ai-chat/AiChatShell.js +8 -2
  9. package/lib/commonjs/ai-chat/AiChatShell.js.map +1 -1
  10. package/lib/commonjs/ai-chat/use-panel-interaction.js +20 -33
  11. package/lib/commonjs/ai-chat/use-panel-interaction.js.map +1 -1
  12. package/lib/commonjs/bottom-bar/BottomBarBase.js +5 -2
  13. package/lib/commonjs/bottom-bar/BottomBarBase.js.map +1 -1
  14. package/lib/commonjs/bottom-sheet/BottomSheetBase.js +10 -3
  15. package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -1
  16. package/lib/commonjs/dialog/Dialog.web.js +28 -22
  17. package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
  18. package/lib/commonjs/dialog/DialogBottomSheet.js +9 -4
  19. package/lib/commonjs/dialog/DialogBottomSheet.js.map +1 -1
  20. package/lib/commonjs/overlay/ModalKeyboard.js +88 -0
  21. package/lib/commonjs/overlay/ModalKeyboard.js.map +1 -0
  22. package/lib/commonjs/overlay/modal-keyboard.js +131 -0
  23. package/lib/commonjs/overlay/modal-keyboard.js.map +1 -0
  24. package/lib/commonjs/overlay/stack.js +16 -0
  25. package/lib/commonjs/overlay/stack.js.map +1 -1
  26. package/lib/commonjs/sidebar/Sidebar.js +18 -6
  27. package/lib/commonjs/sidebar/Sidebar.js.map +1 -1
  28. package/lib/module/ai-chat/AiChatContainer.js +22 -11
  29. package/lib/module/ai-chat/AiChatContainer.js.map +1 -1
  30. package/lib/module/ai-chat/AiChatShell.js +8 -2
  31. package/lib/module/ai-chat/AiChatShell.js.map +1 -1
  32. package/lib/module/ai-chat/use-panel-interaction.js +20 -32
  33. package/lib/module/ai-chat/use-panel-interaction.js.map +1 -1
  34. package/lib/module/bottom-bar/BottomBarBase.js +5 -2
  35. package/lib/module/bottom-bar/BottomBarBase.js.map +1 -1
  36. package/lib/module/bottom-sheet/BottomSheetBase.js +10 -3
  37. package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -1
  38. package/lib/module/dialog/Dialog.web.js +28 -22
  39. package/lib/module/dialog/Dialog.web.js.map +1 -1
  40. package/lib/module/dialog/DialogBottomSheet.js +9 -4
  41. package/lib/module/dialog/DialogBottomSheet.js.map +1 -1
  42. package/lib/module/overlay/ModalKeyboard.js +84 -0
  43. package/lib/module/overlay/ModalKeyboard.js.map +1 -0
  44. package/lib/module/overlay/modal-keyboard.js +124 -0
  45. package/lib/module/overlay/modal-keyboard.js.map +1 -0
  46. package/lib/module/overlay/stack.js +15 -0
  47. package/lib/module/overlay/stack.js.map +1 -1
  48. package/lib/module/sidebar/Sidebar.js +18 -6
  49. package/lib/module/sidebar/Sidebar.js.map +1 -1
  50. package/lib/typescript/commonjs/ai-chat/AiChatContainer.d.ts +5 -4
  51. package/lib/typescript/commonjs/ai-chat/AiChatContainer.d.ts.map +1 -1
  52. package/lib/typescript/commonjs/ai-chat/AiChatShell.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/ai-chat/use-panel-interaction.d.ts +10 -1
  54. package/lib/typescript/commonjs/ai-chat/use-panel-interaction.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/bottom-bar/BottomBarBase.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/overlay/ModalKeyboard.d.ts +32 -0
  60. package/lib/typescript/commonjs/overlay/ModalKeyboard.d.ts.map +1 -0
  61. package/lib/typescript/commonjs/overlay/modal-keyboard.d.ts +17 -0
  62. package/lib/typescript/commonjs/overlay/modal-keyboard.d.ts.map +1 -0
  63. package/lib/typescript/commonjs/overlay/stack.d.ts +6 -0
  64. package/lib/typescript/commonjs/overlay/stack.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/sidebar/Sidebar.d.ts.map +1 -1
  66. package/lib/typescript/module/ai-chat/AiChatContainer.d.ts +5 -4
  67. package/lib/typescript/module/ai-chat/AiChatContainer.d.ts.map +1 -1
  68. package/lib/typescript/module/ai-chat/AiChatShell.d.ts.map +1 -1
  69. package/lib/typescript/module/ai-chat/use-panel-interaction.d.ts +10 -1
  70. package/lib/typescript/module/ai-chat/use-panel-interaction.d.ts.map +1 -1
  71. package/lib/typescript/module/bottom-bar/BottomBarBase.d.ts.map +1 -1
  72. package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
  73. package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
  74. package/lib/typescript/module/dialog/DialogBottomSheet.d.ts.map +1 -1
  75. package/lib/typescript/module/overlay/ModalKeyboard.d.ts +32 -0
  76. package/lib/typescript/module/overlay/ModalKeyboard.d.ts.map +1 -0
  77. package/lib/typescript/module/overlay/modal-keyboard.d.ts +17 -0
  78. package/lib/typescript/module/overlay/modal-keyboard.d.ts.map +1 -0
  79. package/lib/typescript/module/overlay/stack.d.ts +6 -0
  80. package/lib/typescript/module/overlay/stack.d.ts.map +1 -1
  81. package/lib/typescript/module/sidebar/Sidebar.d.ts.map +1 -1
  82. package/package.json +1 -1
  83. package/src/ai-chat/AiChatContainer.tsx +18 -8
  84. package/src/ai-chat/AiChatShell.tsx +12 -2
  85. package/src/ai-chat/use-panel-interaction.ts +20 -34
  86. package/src/bottom-bar/BottomBarBase.tsx +5 -2
  87. package/src/bottom-sheet/BottomSheetBase.tsx +8 -0
  88. package/src/dialog/Dialog.web.tsx +22 -18
  89. package/src/dialog/DialogBottomSheet.tsx +9 -4
  90. package/src/overlay/ModalKeyboard.tsx +88 -0
  91. package/src/overlay/modal-keyboard.ts +133 -0
  92. package/src/overlay/stack.ts +13 -0
  93. package/src/sidebar/Sidebar.tsx +13 -4
@@ -1 +1 @@
1
- {"version":3,"file":"AiChatShell.d.ts","sourceRoot":"","sources":["../../../../src/ai-chat/AiChatShell.tsx"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,qBAAqB,EAI3B,MAAM,cAAc,CAAC;AA4BtB,OAAO,KAAK,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA4DlG;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,aAAa,EACb,QAAQ,EACR,WAAW,EACX,KAA6B,EAC7B,OAAO,EACP,KAAK,EACL,MAAM,GACP,EAAE,uBAAuB,2CAoHzB;AAMD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,uBAAuB,kDAoCnF;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,YAAY,EACZ,gBAAwB,EACxB,qBAAqB,EACrB,aAAa,EACb,QAAQ,EACR,KAAK,EACL,UAAmB,EACnB,SAAuB,EACvB,iBAAuB,EACvB,aAAmB,EACnB,aAAmB,EACnB,OAAO,EAAE,WAAW,EACpB,eAAe,EACf,eAAsB,EACtB,SAAS,EAAE,aAAa,EACxB,iBAAiB,EACjB,UAAU,EACV,OAAc,EACd,MAAoB,EACpB,MAAM,EACN,KAAK,EACL,MAAM,GACP,EAAE,gBAAgB,2CA0ZlB;AAED,YAAY,EAAE,qBAAqB,EAAE,CAAC"}
1
+ {"version":3,"file":"AiChatShell.d.ts","sourceRoot":"","sources":["../../../../src/ai-chat/AiChatShell.tsx"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,qBAAqB,EAI3B,MAAM,cAAc,CAAC;AA4BtB,OAAO,KAAK,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA4DlG;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,aAAa,EACb,QAAQ,EACR,WAAW,EACX,KAA6B,EAC7B,OAAO,EACP,KAAK,EACL,MAAM,GACP,EAAE,uBAAuB,2CAoHzB;AAMD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,uBAAuB,kDAoCnF;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,YAAY,EACZ,gBAAwB,EACxB,qBAAqB,EACrB,aAAa,EACb,QAAQ,EACR,KAAK,EACL,UAAmB,EACnB,SAAuB,EACvB,iBAAuB,EACvB,aAAmB,EACnB,aAAmB,EACnB,OAAO,EAAE,WAAW,EACpB,eAAe,EACf,eAAsB,EACtB,SAAS,EAAE,aAAa,EACxB,iBAAiB,EACjB,UAAU,EACV,OAAc,EACd,MAAoB,EACpB,MAAM,EACN,KAAK,EACL,MAAM,GACP,EAAE,gBAAgB,2CAoalB;AAED,YAAY,EAAE,qBAAqB,EAAE,CAAC"}
@@ -1,4 +1,13 @@
1
1
  import { type View } from 'react-native';
2
- /** Interaction only: the shell continues to own the drawer's layout and motion. */
2
+ /**
3
+ * Interaction only: the shell continues to own the drawer's layout and motion.
4
+ * Used by both shell drawers — the panel and the navigation.
5
+ *
6
+ * open Escape (web) and back (Android) close it
7
+ * modal it covers the workspace: focus moves in on open, Tab is kept
8
+ * inside, and focus goes back to the opener on close
9
+ *
10
+ * Any portaled overlay above (a dialog, a menu) takes all of it first.
11
+ */
3
12
  export declare function usePanelInteraction(open: boolean, modal: boolean, close: () => void): import("react").RefObject<View | null>;
4
13
  //# sourceMappingURL=use-panel-interaction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-panel-interaction.d.ts","sourceRoot":"","sources":["../../../../src/ai-chat/use-panel-interaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,IAAI,EAAE,MAAM,cAAc,CAAC;AAchE,mFAAmF;AACnF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,0CAsDnF"}
1
+ {"version":3,"file":"use-panel-interaction.d.ts","sourceRoot":"","sources":["../../../../src/ai-chat/use-panel-interaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,IAAI,EAAE,MAAM,cAAc,CAAC;AAKhE;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,0CAwCnF"}
@@ -1 +1 @@
1
- {"version":3,"file":"BottomBarBase.d.ts","sourceRoot":"","sources":["../../../../src/bottom-bar/BottomBarBase.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAYzE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,UAAU,KAAM,SAAQ,cAAc;IACpC,UAAU,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACvC,IAAI,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;CAC3C;AACD,wBAAgB,aAAa,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,EAC3H,eAAwB,EAAE,cAAuB,EAAE,QAAwB,EAAE,gBAAgB,EAAE,IAAW,EAAE,QAAc,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,kDAsEnJ"}
1
+ {"version":3,"file":"BottomBarBase.d.ts","sourceRoot":"","sources":["../../../../src/bottom-bar/BottomBarBase.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAYzE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,UAAU,KAAM,SAAQ,cAAc;IACpC,UAAU,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACvC,IAAI,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;CAC3C;AACD,wBAAgB,aAAa,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,EAC3H,eAAwB,EAAE,cAAuB,EAAE,QAAwB,EAAE,gBAAgB,EAAE,IAAW,EAAE,QAAc,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,kDAyEnJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"BottomSheetBase.d.ts","sourceRoot":"","sources":["../../../../src/bottom-sheet/BottomSheetBase.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAmC/B,OAAO,KAAK,EAAE,cAAc,EAA2C,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAsB7G,eAAO,MAAM,eAAe,6FAurB1B,CAAC"}
1
+ {"version":3,"file":"BottomSheetBase.d.ts","sourceRoot":"","sources":["../../../../src/bottom-sheet/BottomSheetBase.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAoC/B,OAAO,KAAK,EAAE,cAAc,EAA2C,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAsB7G,eAAO,MAAM,eAAe,6FA8rB1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.web.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.web.tsx"],"names":[],"mappings":"AA2EA,OAAO,KAAK,EAIV,WAAW,EACZ,MAAM,SAAS,CAAC;AAmDjB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAazD;AA8tBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,8PAG5B,CAAC"}
1
+ {"version":3,"file":"Dialog.web.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.web.tsx"],"names":[],"mappings":"AA4EA,OAAO,KAAK,EAIV,WAAW,EACZ,MAAM,SAAS,CAAC;AAmDjB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAazD;AAiuBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,8PAG5B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"DialogBottomSheet.d.ts","sourceRoot":"","sources":["../../../../src/dialog/DialogBottomSheet.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,IAAI,EAAE,cAAc,EACpB,SAAS,EACT,OAAO,EACP,MAAM,EACN,KAAK,EACL,WAAW,EACX,OAAO,EACP,MAAM,EACN,cAAyC,EACzC,UAAiB,EACjB,iBAAwB,EACxB,cAA+C,EAC/C,UAAU,EACV,KAAK,EACL,KAAK,EACL,UAAU,EACV,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,KAAK,EACL,QAAQ,GACT,EAAE,sBAAsB,2CA6QxB;AAQD;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,WAAW,EACT,SAAS,GACT,MAAM,GACN,WAAW,GACX,SAAS,GACT,QAAQ,GACR,OAAO,GACP,aAAa,GACb,SAAS,GACT,QAAQ,GACR,gBAAgB,GAChB,YAAY,GACZ,mBAAmB,GACnB,gBAAgB,GAChB,YAAY,GACZ,OAAO,GACP,OAAO,GACP,YAAY,GACZ,gBAAgB,GAChB,gBAAgB,GAChB,oBAAoB,GACpB,OAAO,GACP,UAAU,CACb,CAAC"}
1
+ {"version":3,"file":"DialogBottomSheet.d.ts","sourceRoot":"","sources":["../../../../src/dialog/DialogBottomSheet.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,IAAI,EAAE,cAAc,EACpB,SAAS,EACT,OAAO,EACP,MAAM,EACN,KAAK,EACL,WAAW,EACX,OAAO,EACP,MAAM,EACN,cAAyC,EACzC,UAAiB,EACjB,iBAAwB,EACxB,cAA+C,EAC/C,UAAU,EACV,KAAK,EACL,KAAK,EACL,UAAU,EACV,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,KAAK,EACL,QAAQ,GACT,EAAE,sBAAsB,2CAkRxB;AAQD;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,WAAW,EACT,SAAS,GACT,MAAM,GACN,WAAW,GACX,SAAS,GACT,QAAQ,GACR,OAAO,GACP,aAAa,GACb,SAAS,GACT,QAAQ,GACR,gBAAgB,GAChB,YAAY,GACZ,mBAAmB,GACnB,gBAAgB,GAChB,YAAY,GACZ,OAAO,GACP,OAAO,GACP,YAAY,GACZ,gBAAgB,GAChB,gBAAgB,GAChB,oBAAoB,GACpB,OAAO,GACP,UAAU,CACb,CAAC"}
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import type { View } from 'react-native';
3
+ export interface ModalKeyboardProps {
4
+ /** The surface's panel: where focus goes, and what Tab stays inside. */
5
+ panelRef: React.RefObject<View | null>;
6
+ /** The surface has started its exit: focus goes back now, Escape is spent. */
7
+ closing?: boolean;
8
+ /** Whether Escape may dismiss it (a dialog's `dismissOnBackdrop`). */
9
+ dismissible: boolean;
10
+ dismiss: () => void;
11
+ }
12
+ /**
13
+ * An open modal surface's keyboard and focus, WEB. Renders nothing; it must
14
+ * sit INSIDE the surface's `OverlayRoot`, which is how it knows the surface's
15
+ * place in the overlay stack. Used by the web `Dialog` (card and side drawers)
16
+ * and the web `BottomSheet`.
17
+ *
18
+ * - Focus moves INTO the panel when it opens — to whatever the content
19
+ * focused itself (an `autoFocus` field), else the first tabbable, else the
20
+ * panel — and goes BACK to the element that had it once the surface starts
21
+ * closing (or unmounts), unless the host has already put it somewhere else.
22
+ * - Tab and Shift+Tab wrap at the panel's edges.
23
+ * - Escape dismisses (when `dismissible`), from anywhere — a text field of the
24
+ * surface included, which react-native-web keeps every keydown from
25
+ * bubbling out of (`./modal-keyboard.ts`).
26
+ *
27
+ * All three belong to the TOP-MOST surface only: a menu or a second dialog
28
+ * opened from this one takes them until it closes, so Escape closes one layer
29
+ * at a time. Inert without a DOM, so native never runs any of it.
30
+ */
31
+ export declare function ModalKeyboard({ panelRef, closing, dismissible, dismiss }: ModalKeyboardProps): null;
32
+ //# sourceMappingURL=ModalKeyboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModalKeyboard.d.ts","sourceRoot":"","sources":["../../../../src/overlay/ModalKeyboard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAC9D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAMzC,MAAM,WAAW,kBAAkB;IACjC,wEAAwE;IACxE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACvC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,OAAe,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,kBAAkB,QAmDpG"}
@@ -0,0 +1,17 @@
1
+ /** The elements inside `node` that Tab can land on, in document order. */
2
+ export declare function tabbablesWithin(node: HTMLElement | null): HTMLElement[];
3
+ /**
4
+ * Keep Tab inside `node`: from the last tabbable (or from outside) forward to
5
+ * the first, from the first (or from outside) backward to the last. Returns
6
+ * whether it moved focus.
7
+ */
8
+ export declare function wrapTab(event: KeyboardEvent, node: HTMLElement | null): boolean;
9
+ /**
10
+ * Dismiss on Escape — see the file comment for the three steps. `claim` runs
11
+ * in the capture phase and says whether this surface is the one the key is
12
+ * for (the top-most, nothing anchored open above it). An open anchored
13
+ * surface (a menu, a select's list) always keeps the key: it is the top layer
14
+ * whatever the caller's own bookkeeping says.
15
+ */
16
+ export declare function listenForEscape(claim: () => boolean, dismiss: () => void): () => void;
17
+ //# sourceMappingURL=modal-keyboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modal-keyboard.d.ts","sourceRoot":"","sources":["../../../../src/overlay/modal-keyboard.ts"],"names":[],"mappings":"AAiDA,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,WAAW,EAAE,CASvE;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAa/E;AAYD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAkCrF"}
@@ -120,5 +120,11 @@ export declare function releaseOverlayRank(rank: number): void;
120
120
  export declare function resetOverlayStack(): void;
121
121
  /** In-flow shell drawers yield interaction to any portaled surface above them. */
122
122
  export declare function hasActiveOverlays(): boolean;
123
+ /**
124
+ * Whether the surface painting at `layer` is the top-most one open — the one
125
+ * Escape and Tab belong to. A menu opened from inside a dialog takes a higher
126
+ * rank than the dialog, so the dialog stands aside until it closes.
127
+ */
128
+ export declare function isTopmostOverlayLayer(layer: OverlayLayer): boolean;
123
129
  export {};
124
130
  //# sourceMappingURL=stack.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../../../src/overlay/stack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,aAAa,QAC8C,CAAC;AAEzE,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,UAAU,QAAQ;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,8BAA8B,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC1D;AAgBD,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAIvD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAItD;AAED,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIrD;AAED,4CAA4C;AAC5C,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C"}
1
+ {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../../../src/overlay/stack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,aAAa,QAC8C,CAAC;AAEzE,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,UAAU,QAAQ;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,8BAA8B,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC1D;AAgBD,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAIvD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAItD;AAED,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIrD;AAED,4CAA4C;AAC5C,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAMlE"}
@@ -1 +1 @@
1
- {"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../../src/sidebar/Sidebar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAkE,MAAM,OAAO,CAAC;AA4CvF,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,SAAS,CAAC;AA4tB5D,eAAO,MAAM,OAAO,0CAAyB,CAAC"}
1
+ {"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../../src/sidebar/Sidebar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAkE,MAAM,OAAO,CAAC;AA4CvF,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,SAAS,CAAC;AAquB5D,eAAO,MAAM,OAAO,0CAAyB,CAAC"}
@@ -21,10 +21,11 @@ import type { AiChatContainerProps, AiChatThreadHandle, AiChatThreadProps } from
21
21
  * `floatingChrome` changes the geometry, not the parts:
22
22
  *
23
23
  * thread the `children` fill the whole card, under everything below
24
- * header the `header` slot + breadcrumb row, absolute at the top, no band;
25
- * an edge scrim of the surface (opaque at the top, gone
26
- * `SCRIM_TAIL_RATIO` below the block) fades in over the first 16px
27
- * of transcript scrolled under it
24
+ * header the `header` slot + breadcrumb row, absolute at the top, no band
25
+ * at rest; once the transcript scrolls under it, the surface fades
26
+ * in behind it over the first 16px — solid across the whole block,
27
+ * then a ramp to nothing `SCRIM_TAIL_RATIO` below it — so no turn
28
+ * ever shows through the title or the crumbs
28
29
  * footer absolute at the bottom, with no band of its own: the transcript
29
30
  * passes behind the composer and stays in sight around it
30
31
  *
@@ -1 +1 @@
1
- {"version":3,"file":"AiChatContainer.d.ts","sourceRoot":"","sources":["../../../../src/ai-chat/AiChatContainer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAUN,MAAM,OAAO,CAAC;AA0Cf,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAqB3F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,KAAK,EACL,WAA0B,EAC1B,cAAc,EACd,OAAO,EACP,MAAM,EACN,OAAO,EACP,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,OAAe,EACf,YAAY,EACZ,UAAU,EACV,OAAc,EACd,cAAsB,EACtB,MAAM,EACN,KAAK,EACL,MAAM,GACP,EAAE,oBAAoB,2CAsHtB;AAwND;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,YAAY,8FAGvB,CAAC"}
1
+ {"version":3,"file":"AiChatContainer.d.ts","sourceRoot":"","sources":["../../../../src/ai-chat/AiChatContainer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAUN,MAAM,OAAO,CAAC;AA0Cf,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAqB3F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,KAAK,EACL,WAA0B,EAC1B,cAAc,EACd,OAAO,EACP,MAAM,EACN,OAAO,EACP,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,OAAe,EACf,YAAY,EACZ,UAAU,EACV,OAAc,EACd,cAAsB,EACtB,MAAM,EACN,KAAK,EACL,MAAM,GACP,EAAE,oBAAoB,2CAsHtB;AAiOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,YAAY,8FAGvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"AiChatShell.d.ts","sourceRoot":"","sources":["../../../../src/ai-chat/AiChatShell.tsx"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,qBAAqB,EAI3B,MAAM,cAAc,CAAC;AA4BtB,OAAO,KAAK,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA4DlG;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,aAAa,EACb,QAAQ,EACR,WAAW,EACX,KAA6B,EAC7B,OAAO,EACP,KAAK,EACL,MAAM,GACP,EAAE,uBAAuB,2CAoHzB;AAMD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,uBAAuB,kDAoCnF;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,YAAY,EACZ,gBAAwB,EACxB,qBAAqB,EACrB,aAAa,EACb,QAAQ,EACR,KAAK,EACL,UAAmB,EACnB,SAAuB,EACvB,iBAAuB,EACvB,aAAmB,EACnB,aAAmB,EACnB,OAAO,EAAE,WAAW,EACpB,eAAe,EACf,eAAsB,EACtB,SAAS,EAAE,aAAa,EACxB,iBAAiB,EACjB,UAAU,EACV,OAAc,EACd,MAAoB,EACpB,MAAM,EACN,KAAK,EACL,MAAM,GACP,EAAE,gBAAgB,2CA0ZlB;AAED,YAAY,EAAE,qBAAqB,EAAE,CAAC"}
1
+ {"version":3,"file":"AiChatShell.d.ts","sourceRoot":"","sources":["../../../../src/ai-chat/AiChatShell.tsx"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,qBAAqB,EAI3B,MAAM,cAAc,CAAC;AA4BtB,OAAO,KAAK,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA4DlG;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,aAAa,EACb,QAAQ,EACR,WAAW,EACX,KAA6B,EAC7B,OAAO,EACP,KAAK,EACL,MAAM,GACP,EAAE,uBAAuB,2CAoHzB;AAMD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,uBAAuB,kDAoCnF;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,YAAY,EACZ,gBAAwB,EACxB,qBAAqB,EACrB,aAAa,EACb,QAAQ,EACR,KAAK,EACL,UAAmB,EACnB,SAAuB,EACvB,iBAAuB,EACvB,aAAmB,EACnB,aAAmB,EACnB,OAAO,EAAE,WAAW,EACpB,eAAe,EACf,eAAsB,EACtB,SAAS,EAAE,aAAa,EACxB,iBAAiB,EACjB,UAAU,EACV,OAAc,EACd,MAAoB,EACpB,MAAM,EACN,KAAK,EACL,MAAM,GACP,EAAE,gBAAgB,2CAoalB;AAED,YAAY,EAAE,qBAAqB,EAAE,CAAC"}
@@ -1,4 +1,13 @@
1
1
  import { type View } from 'react-native';
2
- /** Interaction only: the shell continues to own the drawer's layout and motion. */
2
+ /**
3
+ * Interaction only: the shell continues to own the drawer's layout and motion.
4
+ * Used by both shell drawers — the panel and the navigation.
5
+ *
6
+ * open Escape (web) and back (Android) close it
7
+ * modal it covers the workspace: focus moves in on open, Tab is kept
8
+ * inside, and focus goes back to the opener on close
9
+ *
10
+ * Any portaled overlay above (a dialog, a menu) takes all of it first.
11
+ */
3
12
  export declare function usePanelInteraction(open: boolean, modal: boolean, close: () => void): import("react").RefObject<View | null>;
4
13
  //# sourceMappingURL=use-panel-interaction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-panel-interaction.d.ts","sourceRoot":"","sources":["../../../../src/ai-chat/use-panel-interaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,IAAI,EAAE,MAAM,cAAc,CAAC;AAchE,mFAAmF;AACnF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,0CAsDnF"}
1
+ {"version":3,"file":"use-panel-interaction.d.ts","sourceRoot":"","sources":["../../../../src/ai-chat/use-panel-interaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,IAAI,EAAE,MAAM,cAAc,CAAC;AAKhE;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,0CAwCnF"}
@@ -1 +1 @@
1
- {"version":3,"file":"BottomBarBase.d.ts","sourceRoot":"","sources":["../../../../src/bottom-bar/BottomBarBase.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAYzE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,UAAU,KAAM,SAAQ,cAAc;IACpC,UAAU,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACvC,IAAI,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;CAC3C;AACD,wBAAgB,aAAa,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,EAC3H,eAAwB,EAAE,cAAuB,EAAE,QAAwB,EAAE,gBAAgB,EAAE,IAAW,EAAE,QAAc,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,kDAsEnJ"}
1
+ {"version":3,"file":"BottomBarBase.d.ts","sourceRoot":"","sources":["../../../../src/bottom-bar/BottomBarBase.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAYzE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,UAAU,KAAM,SAAQ,cAAc;IACpC,UAAU,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACvC,IAAI,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;CAC3C;AACD,wBAAgB,aAAa,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,EAC3H,eAAwB,EAAE,cAAuB,EAAE,QAAwB,EAAE,gBAAgB,EAAE,IAAW,EAAE,QAAc,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,kDAyEnJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"BottomSheetBase.d.ts","sourceRoot":"","sources":["../../../../src/bottom-sheet/BottomSheetBase.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAmC/B,OAAO,KAAK,EAAE,cAAc,EAA2C,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAsB7G,eAAO,MAAM,eAAe,6FAurB1B,CAAC"}
1
+ {"version":3,"file":"BottomSheetBase.d.ts","sourceRoot":"","sources":["../../../../src/bottom-sheet/BottomSheetBase.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAoC/B,OAAO,KAAK,EAAE,cAAc,EAA2C,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAsB7G,eAAO,MAAM,eAAe,6FA8rB1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.web.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.web.tsx"],"names":[],"mappings":"AA2EA,OAAO,KAAK,EAIV,WAAW,EACZ,MAAM,SAAS,CAAC;AAmDjB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAazD;AA8tBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,8PAG5B,CAAC"}
1
+ {"version":3,"file":"Dialog.web.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.web.tsx"],"names":[],"mappings":"AA4EA,OAAO,KAAK,EAIV,WAAW,EACZ,MAAM,SAAS,CAAC;AAmDjB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAazD;AAiuBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,8PAG5B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"DialogBottomSheet.d.ts","sourceRoot":"","sources":["../../../../src/dialog/DialogBottomSheet.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,IAAI,EAAE,cAAc,EACpB,SAAS,EACT,OAAO,EACP,MAAM,EACN,KAAK,EACL,WAAW,EACX,OAAO,EACP,MAAM,EACN,cAAyC,EACzC,UAAiB,EACjB,iBAAwB,EACxB,cAA+C,EAC/C,UAAU,EACV,KAAK,EACL,KAAK,EACL,UAAU,EACV,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,KAAK,EACL,QAAQ,GACT,EAAE,sBAAsB,2CA6QxB;AAQD;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,WAAW,EACT,SAAS,GACT,MAAM,GACN,WAAW,GACX,SAAS,GACT,QAAQ,GACR,OAAO,GACP,aAAa,GACb,SAAS,GACT,QAAQ,GACR,gBAAgB,GAChB,YAAY,GACZ,mBAAmB,GACnB,gBAAgB,GAChB,YAAY,GACZ,OAAO,GACP,OAAO,GACP,YAAY,GACZ,gBAAgB,GAChB,gBAAgB,GAChB,oBAAoB,GACpB,OAAO,GACP,UAAU,CACb,CAAC"}
1
+ {"version":3,"file":"DialogBottomSheet.d.ts","sourceRoot":"","sources":["../../../../src/dialog/DialogBottomSheet.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,IAAI,EAAE,cAAc,EACpB,SAAS,EACT,OAAO,EACP,MAAM,EACN,KAAK,EACL,WAAW,EACX,OAAO,EACP,MAAM,EACN,cAAyC,EACzC,UAAiB,EACjB,iBAAwB,EACxB,cAA+C,EAC/C,UAAU,EACV,KAAK,EACL,KAAK,EACL,UAAU,EACV,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,KAAK,EACL,QAAQ,GACT,EAAE,sBAAsB,2CAkRxB;AAQD;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,WAAW,EACT,SAAS,GACT,MAAM,GACN,WAAW,GACX,SAAS,GACT,QAAQ,GACR,OAAO,GACP,aAAa,GACb,SAAS,GACT,QAAQ,GACR,gBAAgB,GAChB,YAAY,GACZ,mBAAmB,GACnB,gBAAgB,GAChB,YAAY,GACZ,OAAO,GACP,OAAO,GACP,YAAY,GACZ,gBAAgB,GAChB,gBAAgB,GAChB,oBAAoB,GACpB,OAAO,GACP,UAAU,CACb,CAAC"}
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import type { View } from 'react-native';
3
+ export interface ModalKeyboardProps {
4
+ /** The surface's panel: where focus goes, and what Tab stays inside. */
5
+ panelRef: React.RefObject<View | null>;
6
+ /** The surface has started its exit: focus goes back now, Escape is spent. */
7
+ closing?: boolean;
8
+ /** Whether Escape may dismiss it (a dialog's `dismissOnBackdrop`). */
9
+ dismissible: boolean;
10
+ dismiss: () => void;
11
+ }
12
+ /**
13
+ * An open modal surface's keyboard and focus, WEB. Renders nothing; it must
14
+ * sit INSIDE the surface's `OverlayRoot`, which is how it knows the surface's
15
+ * place in the overlay stack. Used by the web `Dialog` (card and side drawers)
16
+ * and the web `BottomSheet`.
17
+ *
18
+ * - Focus moves INTO the panel when it opens — to whatever the content
19
+ * focused itself (an `autoFocus` field), else the first tabbable, else the
20
+ * panel — and goes BACK to the element that had it once the surface starts
21
+ * closing (or unmounts), unless the host has already put it somewhere else.
22
+ * - Tab and Shift+Tab wrap at the panel's edges.
23
+ * - Escape dismisses (when `dismissible`), from anywhere — a text field of the
24
+ * surface included, which react-native-web keeps every keydown from
25
+ * bubbling out of (`./modal-keyboard.ts`).
26
+ *
27
+ * All three belong to the TOP-MOST surface only: a menu or a second dialog
28
+ * opened from this one takes them until it closes, so Escape closes one layer
29
+ * at a time. Inert without a DOM, so native never runs any of it.
30
+ */
31
+ export declare function ModalKeyboard({ panelRef, closing, dismissible, dismiss }: ModalKeyboardProps): null;
32
+ //# sourceMappingURL=ModalKeyboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModalKeyboard.d.ts","sourceRoot":"","sources":["../../../../src/overlay/ModalKeyboard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAC9D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAMzC,MAAM,WAAW,kBAAkB;IACjC,wEAAwE;IACxE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACvC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,OAAe,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,kBAAkB,QAmDpG"}
@@ -0,0 +1,17 @@
1
+ /** The elements inside `node` that Tab can land on, in document order. */
2
+ export declare function tabbablesWithin(node: HTMLElement | null): HTMLElement[];
3
+ /**
4
+ * Keep Tab inside `node`: from the last tabbable (or from outside) forward to
5
+ * the first, from the first (or from outside) backward to the last. Returns
6
+ * whether it moved focus.
7
+ */
8
+ export declare function wrapTab(event: KeyboardEvent, node: HTMLElement | null): boolean;
9
+ /**
10
+ * Dismiss on Escape — see the file comment for the three steps. `claim` runs
11
+ * in the capture phase and says whether this surface is the one the key is
12
+ * for (the top-most, nothing anchored open above it). An open anchored
13
+ * surface (a menu, a select's list) always keeps the key: it is the top layer
14
+ * whatever the caller's own bookkeeping says.
15
+ */
16
+ export declare function listenForEscape(claim: () => boolean, dismiss: () => void): () => void;
17
+ //# sourceMappingURL=modal-keyboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modal-keyboard.d.ts","sourceRoot":"","sources":["../../../../src/overlay/modal-keyboard.ts"],"names":[],"mappings":"AAiDA,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,WAAW,EAAE,CASvE;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAa/E;AAYD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAkCrF"}
@@ -120,5 +120,11 @@ export declare function releaseOverlayRank(rank: number): void;
120
120
  export declare function resetOverlayStack(): void;
121
121
  /** In-flow shell drawers yield interaction to any portaled surface above them. */
122
122
  export declare function hasActiveOverlays(): boolean;
123
+ /**
124
+ * Whether the surface painting at `layer` is the top-most one open — the one
125
+ * Escape and Tab belong to. A menu opened from inside a dialog takes a higher
126
+ * rank than the dialog, so the dialog stands aside until it closes.
127
+ */
128
+ export declare function isTopmostOverlayLayer(layer: OverlayLayer): boolean;
123
129
  export {};
124
130
  //# sourceMappingURL=stack.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../../../src/overlay/stack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,aAAa,QAC8C,CAAC;AAEzE,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,UAAU,QAAQ;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,8BAA8B,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC1D;AAgBD,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAIvD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAItD;AAED,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIrD;AAED,4CAA4C;AAC5C,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C"}
1
+ {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../../../src/overlay/stack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,aAAa,QAC8C,CAAC;AAEzE,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,UAAU,QAAQ;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,8BAA8B,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC1D;AAgBD,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAIvD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAItD;AAED,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIrD;AAED,4CAA4C;AAC5C,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAMlE"}
@@ -1 +1 @@
1
- {"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../../src/sidebar/Sidebar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAkE,MAAM,OAAO,CAAC;AA4CvF,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,SAAS,CAAC;AA4tB5D,eAAO,MAAM,OAAO,0CAAyB,CAAC"}
1
+ {"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../../src/sidebar/Sidebar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAkE,MAAM,OAAO,CAAC;AA4CvF,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,SAAS,CAAC;AAquB5D,eAAO,MAAM,OAAO,0CAAyB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxy.so/bloom",
3
- "version": "4.23.0",
3
+ "version": "4.24.1",
4
4
  "packageManager": "bun@1.3.14",
5
5
  "description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
6
6
  "main": "lib/commonjs/index.js",
@@ -92,10 +92,11 @@ const HIDDEN_FROM_A11Y = {
92
92
  * `floatingChrome` changes the geometry, not the parts:
93
93
  *
94
94
  * thread the `children` fill the whole card, under everything below
95
- * header the `header` slot + breadcrumb row, absolute at the top, no band;
96
- * an edge scrim of the surface (opaque at the top, gone
97
- * `SCRIM_TAIL_RATIO` below the block) fades in over the first 16px
98
- * of transcript scrolled under it
95
+ * header the `header` slot + breadcrumb row, absolute at the top, no band
96
+ * at rest; once the transcript scrolls under it, the surface fades
97
+ * in behind it over the first 16px — solid across the whole block,
98
+ * then a ramp to nothing `SCRIM_TAIL_RATIO` below it — so no turn
99
+ * ever shows through the title or the crumbs
99
100
  * footer absolute at the bottom, with no band of its own: the transcript
100
101
  * passes behind the composer and stays in sight around it
101
102
  *
@@ -340,6 +341,7 @@ function FloatingChrome({
340
341
  const alpha = parsed?.a ?? 1;
341
342
  const topStyle = useAnimatedStyle(() => ({ opacity: topFade.value * alpha }), [topFade, alpha]);
342
343
  const prefix = testID ?? 'ai-chat';
344
+ const scrimTail = Math.round(headerHeight * SCRIM_TAIL_RATIO);
343
345
 
344
346
  // Absolute over a card of fixed height; sticky in the flow of a card that
345
347
  // grows with the document, pinned to the screen's gutter.
@@ -363,9 +365,7 @@ function FloatingChrome({
363
365
  top: 0,
364
366
  left: 0,
365
367
  right: 0,
366
- // Past the block's own bottom: a ramp that ends where the layout
367
- // does is a line across the transcript again.
368
- height: Math.round(headerHeight * (1 + SCRIM_TAIL_RATIO)),
368
+ height: headerHeight + scrimTail,
369
369
  // The card's own corners, since nothing clips a document card.
370
370
  ...(documentScroll
371
371
  ? { borderTopLeftRadius: DOCUMENT_CARD_RADIUS, borderTopRightRadius: DOCUMENT_CARD_RADIUS, overflow: 'hidden' as const }
@@ -373,7 +373,17 @@ function FloatingChrome({
373
373
  },
374
374
  topStyle,
375
375
  ]}>
376
- <EdgeScrim color={solid} />
376
+ {/* SOLID behind the header's own block. A ramp that starts fading
377
+ inside the block leaves the breadcrumb row over half-covered
378
+ turns — measured in a browser, a code block's lines read straight
379
+ through the chat's title and crumbs at every width. The ramp
380
+ only starts where the block ends. */}
381
+ <View testID={`${prefix}-fade-top-surface`} style={{ height: headerHeight, backgroundColor: solid }} />
382
+ {/* Past the block's own bottom: a ramp that ends where the layout
383
+ does is a line across the transcript again. */}
384
+ <View style={{ height: scrimTail }}>
385
+ <EdgeScrim color={solid} />
386
+ </View>
377
387
  </Animated.View>
378
388
  ) : null}
379
389
  {header}
@@ -368,6 +368,10 @@ export function AiChatShell({
368
368
  const panelOpen = panelOpenState && !wide && !!panel;
369
369
  const dismissPanel = useCallback(() => setPanelOpen(false), [setPanelOpen]);
370
370
  const panelRef = usePanelInteraction(panelOpenState && !!panel, panelOpen, dismissPanel);
371
+ const dismissNav = useCallback(() => setNavOpen(false), [setNavOpen]);
372
+ // The nav drawer covers the workspace whenever it is open: Escape closes it,
373
+ // focus moves in and is kept there, and returns to the opener on close.
374
+ const navRef = usePanelInteraction(navOpen, navOpen, dismissNav);
371
375
  // The swipe's handlers are built once and read the drawer's state from here.
372
376
  const navOpenRef = useRef(navOpen);
373
377
  navOpenRef.current = navOpen;
@@ -608,6 +612,7 @@ export function AiChatShell({
608
612
  ) : null}
609
613
  {!navInFlow && mobileSidebar ? (
610
614
  <View
615
+ ref={navRef}
611
616
  {...(navOpen ? null : CLOSED_LAYER)}
612
617
  pointerEvents={navOpen ? 'box-none' : 'none'}
613
618
  style={{ position: overlayPosition, top: 0, bottom: 0, left: 0, zIndex: 10, width: 272, paddingTop: 12, paddingBottom: 12, paddingLeft: 6 }}>
@@ -652,12 +657,17 @@ export function AiChatShell({
652
657
  ]}>
653
658
  {!navInFlow ? (
654
659
  <Animated.View
660
+ {...(navOpen ? null : CLOSED_LAYER)}
655
661
  pointerEvents={navOpen ? 'auto' : 'none'}
656
662
  style={[{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 50 }, veilStyle]}>
663
+ {/* Never a Tab stop: open, focus is kept in the drawer and Escape
664
+ closes it; closed, the veil is not there at all. `tabIndex`,
665
+ not `focusable` — react-native-web's Pressable writes its own
666
+ tabIndex 0 over `focusable`. */}
657
667
  <Pressable
658
668
  accessibilityRole="button"
659
669
  accessibilityLabel={l.closeNavigation}
660
- focusable={navOpen}
670
+ tabIndex={-1}
661
671
  onPress={() => setNavOpen(false)}
662
672
  style={{ flex: 1, backgroundColor: veil }}
663
673
  />
@@ -701,7 +711,7 @@ export function AiChatShell({
701
711
  <Pressable
702
712
  accessibilityRole="button"
703
713
  accessibilityLabel={l.closePanel(panelLabel)}
704
- focusable={panelOpen}
714
+ tabIndex={panelOpen ? 0 : -1}
705
715
  onPress={() => setPanelOpen(false)}
706
716
  style={{ flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.4)' }}
707
717
  />
@@ -1,19 +1,19 @@
1
1
  import { useEffect, useRef } from 'react';
2
2
  import { BackHandler, Platform, type View } from 'react-native';
3
3
  import { useAccessibilityFocus } from '../hooks/use-accessibility-focus';
4
+ import { listenForEscape, tabbablesWithin, wrapTab } from '../overlay/modal-keyboard';
4
5
  import { hasActiveOverlays } from '../overlay/stack';
5
6
 
6
- function visible(element: HTMLElement): boolean {
7
- for (let current: HTMLElement | null = element; current; current = current.parentElement) {
8
- const style = getComputedStyle(current);
9
- if (style.display === 'none' || style.visibility === 'hidden') return false;
10
- }
11
- return true;
12
- }
13
-
14
- const FOCUSABLE = 'button, a[href], input, select, textarea, [tabindex]';
15
-
16
- /** Interaction only: the shell continues to own the drawer's layout and motion. */
7
+ /**
8
+ * Interaction only: the shell continues to own the drawer's layout and motion.
9
+ * Used by both shell drawers — the panel and the navigation.
10
+ *
11
+ * open Escape (web) and back (Android) close it
12
+ * modal it covers the workspace: focus moves in on open, Tab is kept
13
+ * inside, and focus goes back to the opener on close
14
+ *
15
+ * Any portaled overlay above (a dialog, a menu) takes all of it first.
16
+ */
17
17
  export function usePanelInteraction(open: boolean, modal: boolean, close: () => void) {
18
18
  const ref = useAccessibilityFocus<View>(modal);
19
19
  const closeRef = useRef(close);
@@ -33,36 +33,22 @@ export function usePanelInteraction(open: boolean, modal: boolean, close: () =>
33
33
  // This ref is a native View on native, a DOM host on web.
34
34
  const node = ref.current as unknown as HTMLElement | null;
35
35
  const opener = document.activeElement as HTMLElement | null;
36
- const candidates = () => Array.from(node?.querySelectorAll<HTMLElement>(FOCUSABLE) ?? [])
37
- .filter(element => element.tabIndex >= 0 && !element.matches(':disabled, [aria-disabled="true"]') &&
38
- !element.closest('[hidden], [aria-hidden="true"]') && visible(element));
39
- const focusFirst = () => candidates()[0]?.focus();
36
+ const focusFirst = () => tabbablesWithin(node)[0]?.focus();
40
37
  if (modal) focusFirst();
41
38
  const onFocus = (event: FocusEvent) => {
42
39
  if (modal && !hasActiveOverlays() && node && !node.contains(event.target as Node)) focusFirst();
43
40
  };
44
- const onKey = (event: KeyboardEvent) => {
45
- if (event.defaultPrevented || hasActiveOverlays()) return;
46
- if (event.key === 'Escape') {
47
- event.preventDefault();
48
- event.stopPropagation();
49
- closeRef.current();
50
- } else if (modal && event.key === 'Tab') {
51
- const items = candidates();
52
- const first = items[0];
53
- const last = items[items.length - 1];
54
- if (event.shiftKey && (document.activeElement === first || !node?.contains(document.activeElement))) {
55
- event.preventDefault(); last?.focus();
56
- } else if (!event.shiftKey && (document.activeElement === last || !node?.contains(document.activeElement))) {
57
- event.preventDefault(); first?.focus();
58
- }
59
- }
41
+ const onTab = (event: KeyboardEvent) => {
42
+ if (modal && !event.defaultPrevented && !hasActiveOverlays()) wrapTab(event, node);
60
43
  };
61
- // Menus/dialogs consume Escape on document; shell listens after them.
62
- window.addEventListener('keydown', onKey);
44
+ const stopEscape = listenForEscape(() => !hasActiveOverlays(), () => closeRef.current());
45
+ // Capture: a text field inside the drawer stops its own keydowns (see
46
+ // `overlay/modal-keyboard.ts`), and Tab must wrap from there too.
47
+ window.addEventListener('keydown', onTab, true);
63
48
  document.addEventListener('focusin', onFocus);
64
49
  return () => {
65
- window.removeEventListener('keydown', onKey);
50
+ stopEscape();
51
+ window.removeEventListener('keydown', onTab, true);
66
52
  document.removeEventListener('focusin', onFocus);
67
53
  if (opener?.isConnected) opener.focus();
68
54
  };
@@ -61,6 +61,9 @@ export function BottomBarBase({ Navigation, Item, Blur, items, value, onValueCha
61
61
  const rowStyle = useAnimatedStyle(() => ({
62
62
  height: interpolate(reducedMotion ? Number(progress.value > 0.01) : progress.value, [0, 1], [EXPANDED_HEIGHT, MINIMIZED_HEIGHT], 'clamp'),
63
63
  }), [progress, reducedMotion]);
64
+ // Not `items.length` in the mapper: a worklet copies `items` whole to the UI
65
+ // runtime, icon elements and all (`worklet-captures.test.ts`).
66
+ const hasItems = items.length > 0;
64
67
  const actionStyle = useAnimatedStyle(() => {
65
68
  const amount = hideAction ? Math.min(1, Math.max(0, reducedMotion ? Number(progress.value > 0.01) : progress.value)) : 0;
66
69
  return {
@@ -68,11 +71,11 @@ export function BottomBarBase({ Navigation, Item, Blur, items, value, onValueCha
68
71
  // Logical, and the CSS name rather than RN's `marginStart`: reanimated's
69
72
  // web writer hands a mapper's result to RNW's `createReactDOMStyle`,
70
73
  // which never rewrites `marginStart`, so the browser would drop it.
71
- marginInlineStart: items.length && !above ? 10 * (1 - amount) : 0,
74
+ marginInlineStart: hasItems && !above ? 10 * (1 - amount) : 0,
72
75
  opacity: 1 - amount,
73
76
  transform: [{ scale: 1 - amount * 0.15 }],
74
77
  };
75
- }, [progress, hideAction, reducedMotion, actionWidth, items.length, above]);
78
+ }, [progress, hideAction, reducedMotion, actionWidth, hasItems, above]);
76
79
  if (keyboardVisible || (!items.length && !action)) return null;
77
80
  return <View {...dirProps} testID={testID} pointerEvents="box-none" style={[{ height: bottom + footprint, justifyContent: 'flex-end' }, style]}>
78
81
  {blur && material === 'translucent' && <Blur direction="bottom" style={{ position: 'absolute', left: 0, right: 0, bottom: 0, height: bottom + footprint + BLUR_BLEED }} />}
@@ -32,6 +32,7 @@ import Animated, {
32
32
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
33
33
  import { EDGE_GAP, windowEdgeGap } from '../layout/edge';
34
34
  import { Backdrop, BACKDROP_DIM_OPACITY } from '../overlay';
35
+ import { ModalKeyboard } from '../overlay/ModalKeyboard';
35
36
  import { useTheme } from '../theme/use-theme';
36
37
  import type { BottomSheetRef, BottomSheetProps, BottomSheetShellProps, BottomSheetBaseProps } from './types';
37
38
 
@@ -92,6 +93,7 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
92
93
  const closeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
93
94
  const hasClosedRef = useRef(false);
94
95
  const scrollViewRef = useRef<Animated.ScrollView>(null);
96
+ const sheetRef = useRef<View>(null);
95
97
  /**
96
98
  * Monotonically increasing counter that identifies "the current close
97
99
  * attempt". Bumped every time the sheet re-opens (`present()`), so any
@@ -696,6 +698,11 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
696
698
 
697
699
  return (
698
700
  <Shell visible={rendered} onRequestClose={dismiss} keyboardHeight={keyboardHeight}>
701
+ {/* Web keyboard and focus: in on open, back on close, Tab kept in
702
+ the sheet, Escape a USER dismissal — through `onDismissAttempt`,
703
+ exactly like the backdrop. The sheet had no keyboard path at
704
+ all. Inside the shell's `OverlayRoot`; inert on native. */}
705
+ <ModalKeyboard panelRef={sheetRef} dismissible dismiss={handleBackdropPress} />
699
706
  <View style={StyleSheet.absoluteFill}>
700
707
  {backdropComponent ? (
701
708
  <Animated.View style={[StyleSheet.absoluteFill, customBackdropStyle]}>
@@ -723,6 +730,7 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
723
730
 
724
731
  <GestureDetector gesture={panGesture}>
725
732
  <Animated.View
733
+ ref={sheetRef}
726
734
  onLayout={onLayout}
727
735
  style={[
728
736
  dynamicStyles.sheet,