cpk-ui 0.8.0-beta.3 → 0.8.0-beta.5

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 (208) hide show
  1. package/README.md +63 -1
  2. package/components/modals/AlertDialog/AlertDialog.d.ts +7 -0
  3. package/components/modals/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/components/modals/AlertDialog/AlertDialog.js +34 -19
  5. package/components/uis/Accordion/Accordion.d.ts +11 -1
  6. package/components/uis/Accordion/Accordion.d.ts.map +1 -1
  7. package/components/uis/Accordion/Accordion.js +4 -2
  8. package/components/uis/Accordion/AccordionItem.d.ts +6 -3
  9. package/components/uis/Accordion/AccordionItem.d.ts.map +1 -1
  10. package/components/uis/Accordion/AccordionItem.js +96 -184
  11. package/components/uis/Button/Button.d.ts.map +1 -1
  12. package/components/uis/Button/Button.js +20 -4
  13. package/components/uis/CustomPressable/CustomPressable.d.ts.map +1 -1
  14. package/components/uis/CustomPressable/CustomPressable.js +17 -13
  15. package/components/uis/IconButton/IconButton.d.ts.map +1 -1
  16. package/components/uis/IconButton/IconButton.js +10 -2
  17. package/components/uis/ReorderableList/ReorderableList.d.ts +37 -0
  18. package/components/uis/ReorderableList/ReorderableList.d.ts.map +1 -0
  19. package/components/uis/ReorderableList/ReorderableList.js +87 -0
  20. package/index.d.ts +1 -0
  21. package/index.d.ts.map +1 -1
  22. package/index.js +1 -0
  23. package/node_modules/react-native-drax/LICENSE.md +21 -0
  24. package/node_modules/react-native-drax/README.md +454 -0
  25. package/node_modules/react-native-drax/lib/module/DebugOverlay.js +121 -0
  26. package/node_modules/react-native-drax/lib/module/DebugOverlay.js.map +1 -0
  27. package/node_modules/react-native-drax/lib/module/Drax.js +36 -0
  28. package/node_modules/react-native-drax/lib/module/Drax.js.map +1 -0
  29. package/node_modules/react-native-drax/lib/module/DraxContext.js +6 -0
  30. package/node_modules/react-native-drax/lib/module/DraxContext.js.map +1 -0
  31. package/node_modules/react-native-drax/lib/module/DraxHandle.js +47 -0
  32. package/node_modules/react-native-drax/lib/module/DraxHandle.js.map +1 -0
  33. package/node_modules/react-native-drax/lib/module/DraxHandleContext.js +11 -0
  34. package/node_modules/react-native-drax/lib/module/DraxHandleContext.js.map +1 -0
  35. package/node_modules/react-native-drax/lib/module/DraxList.js +108 -0
  36. package/node_modules/react-native-drax/lib/module/DraxList.js.map +1 -0
  37. package/node_modules/react-native-drax/lib/module/DraxProvider.js +203 -0
  38. package/node_modules/react-native-drax/lib/module/DraxProvider.js.map +1 -0
  39. package/node_modules/react-native-drax/lib/module/DraxScrollView.js +167 -0
  40. package/node_modules/react-native-drax/lib/module/DraxScrollView.js.map +1 -0
  41. package/node_modules/react-native-drax/lib/module/DraxSubprovider.js +21 -0
  42. package/node_modules/react-native-drax/lib/module/DraxSubprovider.js.map +1 -0
  43. package/node_modules/react-native-drax/lib/module/DraxView.js +348 -0
  44. package/node_modules/react-native-drax/lib/module/DraxView.js.map +1 -0
  45. package/node_modules/react-native-drax/lib/module/HoverLayer.js +152 -0
  46. package/node_modules/react-native-drax/lib/module/HoverLayer.js.map +1 -0
  47. package/node_modules/react-native-drax/lib/module/SortableBoardContainer.js +386 -0
  48. package/node_modules/react-native-drax/lib/module/SortableBoardContainer.js.map +1 -0
  49. package/node_modules/react-native-drax/lib/module/SortableBoardContext.js +6 -0
  50. package/node_modules/react-native-drax/lib/module/SortableBoardContext.js.map +1 -0
  51. package/node_modules/react-native-drax/lib/module/SortableContainer.js +571 -0
  52. package/node_modules/react-native-drax/lib/module/SortableContainer.js.map +1 -0
  53. package/node_modules/react-native-drax/lib/module/SortableItem.js +225 -0
  54. package/node_modules/react-native-drax/lib/module/SortableItem.js.map +1 -0
  55. package/node_modules/react-native-drax/lib/module/SortableItemContext.js +38 -0
  56. package/node_modules/react-native-drax/lib/module/SortableItemContext.js.map +1 -0
  57. package/node_modules/react-native-drax/lib/module/compat/detectVersion.js +19 -0
  58. package/node_modules/react-native-drax/lib/module/compat/detectVersion.js.map +1 -0
  59. package/node_modules/react-native-drax/lib/module/compat/index.js +5 -0
  60. package/node_modules/react-native-drax/lib/module/compat/index.js.map +1 -0
  61. package/node_modules/react-native-drax/lib/module/compat/types.js +4 -0
  62. package/node_modules/react-native-drax/lib/module/compat/types.js.map +1 -0
  63. package/node_modules/react-native-drax/lib/module/compat/useDraxPanGesture.js +95 -0
  64. package/node_modules/react-native-drax/lib/module/compat/useDraxPanGesture.js.map +1 -0
  65. package/node_modules/react-native-drax/lib/module/hooks/index.js +5 -0
  66. package/node_modules/react-native-drax/lib/module/hooks/index.js.map +1 -0
  67. package/node_modules/react-native-drax/lib/module/hooks/useCallbackDispatch.js +688 -0
  68. package/node_modules/react-native-drax/lib/module/hooks/useCallbackDispatch.js.map +1 -0
  69. package/node_modules/react-native-drax/lib/module/hooks/useDragGesture.js +240 -0
  70. package/node_modules/react-native-drax/lib/module/hooks/useDragGesture.js.map +1 -0
  71. package/node_modules/react-native-drax/lib/module/hooks/useDraxContext.js +12 -0
  72. package/node_modules/react-native-drax/lib/module/hooks/useDraxContext.js.map +1 -0
  73. package/node_modules/react-native-drax/lib/module/hooks/useDraxId.js +13 -0
  74. package/node_modules/react-native-drax/lib/module/hooks/useDraxId.js.map +1 -0
  75. package/node_modules/react-native-drax/lib/module/hooks/useDraxMethods.js +73 -0
  76. package/node_modules/react-native-drax/lib/module/hooks/useDraxMethods.js.map +1 -0
  77. package/node_modules/react-native-drax/lib/module/hooks/useDraxScrollHandler.js +97 -0
  78. package/node_modules/react-native-drax/lib/module/hooks/useDraxScrollHandler.js.map +1 -0
  79. package/node_modules/react-native-drax/lib/module/hooks/useSortableBoard.js +37 -0
  80. package/node_modules/react-native-drax/lib/module/hooks/useSortableBoard.js.map +1 -0
  81. package/node_modules/react-native-drax/lib/module/hooks/useSortableList.js +1025 -0
  82. package/node_modules/react-native-drax/lib/module/hooks/useSortableList.js.map +1 -0
  83. package/node_modules/react-native-drax/lib/module/hooks/useSpatialIndex.js +283 -0
  84. package/node_modules/react-native-drax/lib/module/hooks/useSpatialIndex.js.map +1 -0
  85. package/node_modules/react-native-drax/lib/module/hooks/useViewStyles.js +158 -0
  86. package/node_modules/react-native-drax/lib/module/hooks/useViewStyles.js.map +1 -0
  87. package/node_modules/react-native-drax/lib/module/hooks/useWebScrollFreeze.js +52 -0
  88. package/node_modules/react-native-drax/lib/module/hooks/useWebScrollFreeze.js.map +1 -0
  89. package/node_modules/react-native-drax/lib/module/index.js +37 -0
  90. package/node_modules/react-native-drax/lib/module/index.js.map +1 -0
  91. package/node_modules/react-native-drax/lib/module/math.js +294 -0
  92. package/node_modules/react-native-drax/lib/module/math.js.map +1 -0
  93. package/node_modules/react-native-drax/lib/module/package.json +1 -0
  94. package/node_modules/react-native-drax/lib/module/params.js +88 -0
  95. package/node_modules/react-native-drax/lib/module/params.js.map +1 -0
  96. package/node_modules/react-native-drax/lib/module/types.js +215 -0
  97. package/node_modules/react-native-drax/lib/module/types.js.map +1 -0
  98. package/node_modules/react-native-drax/lib/typescript/package.json +1 -0
  99. package/node_modules/react-native-drax/lib/typescript/src/DebugOverlay.d.ts +17 -0
  100. package/node_modules/react-native-drax/lib/typescript/src/DebugOverlay.d.ts.map +1 -0
  101. package/node_modules/react-native-drax/lib/typescript/src/Drax.d.ts +28 -0
  102. package/node_modules/react-native-drax/lib/typescript/src/Drax.d.ts.map +1 -0
  103. package/node_modules/react-native-drax/lib/typescript/src/DraxContext.d.ts +3 -0
  104. package/node_modules/react-native-drax/lib/typescript/src/DraxContext.d.ts.map +1 -0
  105. package/node_modules/react-native-drax/lib/typescript/src/DraxHandle.d.ts +25 -0
  106. package/node_modules/react-native-drax/lib/typescript/src/DraxHandle.d.ts.map +1 -0
  107. package/node_modules/react-native-drax/lib/typescript/src/DraxHandleContext.d.ts +12 -0
  108. package/node_modules/react-native-drax/lib/typescript/src/DraxHandleContext.d.ts.map +1 -0
  109. package/node_modules/react-native-drax/lib/typescript/src/DraxList.d.ts +66 -0
  110. package/node_modules/react-native-drax/lib/typescript/src/DraxList.d.ts.map +1 -0
  111. package/node_modules/react-native-drax/lib/typescript/src/DraxProvider.d.ts +4 -0
  112. package/node_modules/react-native-drax/lib/typescript/src/DraxProvider.d.ts.map +1 -0
  113. package/node_modules/react-native-drax/lib/typescript/src/DraxScrollView.d.ts +7 -0
  114. package/node_modules/react-native-drax/lib/typescript/src/DraxScrollView.d.ts.map +1 -0
  115. package/node_modules/react-native-drax/lib/typescript/src/DraxSubprovider.d.ts +4 -0
  116. package/node_modules/react-native-drax/lib/typescript/src/DraxSubprovider.d.ts.map +1 -0
  117. package/node_modules/react-native-drax/lib/typescript/src/DraxView.d.ts +4 -0
  118. package/node_modules/react-native-drax/lib/typescript/src/DraxView.d.ts.map +1 -0
  119. package/node_modules/react-native-drax/lib/typescript/src/HoverLayer.d.ts +38 -0
  120. package/node_modules/react-native-drax/lib/typescript/src/HoverLayer.d.ts.map +1 -0
  121. package/node_modules/react-native-drax/lib/typescript/src/SortableBoardContainer.d.ts +11 -0
  122. package/node_modules/react-native-drax/lib/typescript/src/SortableBoardContainer.d.ts.map +1 -0
  123. package/node_modules/react-native-drax/lib/typescript/src/SortableBoardContext.d.ts +4 -0
  124. package/node_modules/react-native-drax/lib/typescript/src/SortableBoardContext.d.ts.map +1 -0
  125. package/node_modules/react-native-drax/lib/typescript/src/SortableContainer.d.ts +13 -0
  126. package/node_modules/react-native-drax/lib/typescript/src/SortableContainer.d.ts.map +1 -0
  127. package/node_modules/react-native-drax/lib/typescript/src/SortableItem.d.ts +14 -0
  128. package/node_modules/react-native-drax/lib/typescript/src/SortableItem.d.ts.map +1 -0
  129. package/node_modules/react-native-drax/lib/typescript/src/SortableItemContext.d.ts +37 -0
  130. package/node_modules/react-native-drax/lib/typescript/src/SortableItemContext.d.ts.map +1 -0
  131. package/node_modules/react-native-drax/lib/typescript/src/compat/detectVersion.d.ts +2 -0
  132. package/node_modules/react-native-drax/lib/typescript/src/compat/detectVersion.d.ts.map +1 -0
  133. package/node_modules/react-native-drax/lib/typescript/src/compat/index.d.ts +4 -0
  134. package/node_modules/react-native-drax/lib/typescript/src/compat/index.d.ts.map +1 -0
  135. package/node_modules/react-native-drax/lib/typescript/src/compat/types.d.ts +33 -0
  136. package/node_modules/react-native-drax/lib/typescript/src/compat/types.d.ts.map +1 -0
  137. package/node_modules/react-native-drax/lib/typescript/src/compat/useDraxPanGesture.d.ts +8 -0
  138. package/node_modules/react-native-drax/lib/typescript/src/compat/useDraxPanGesture.d.ts.map +1 -0
  139. package/node_modules/react-native-drax/lib/typescript/src/hooks/index.d.ts +3 -0
  140. package/node_modules/react-native-drax/lib/typescript/src/hooks/index.d.ts.map +1 -0
  141. package/node_modules/react-native-drax/lib/typescript/src/hooks/useCallbackDispatch.d.ts +40 -0
  142. package/node_modules/react-native-drax/lib/typescript/src/hooks/useCallbackDispatch.d.ts.map +1 -0
  143. package/node_modules/react-native-drax/lib/typescript/src/hooks/useDragGesture.d.ts +17 -0
  144. package/node_modules/react-native-drax/lib/typescript/src/hooks/useDragGesture.d.ts.map +1 -0
  145. package/node_modules/react-native-drax/lib/typescript/src/hooks/useDraxContext.d.ts +2 -0
  146. package/node_modules/react-native-drax/lib/typescript/src/hooks/useDraxContext.d.ts.map +1 -0
  147. package/node_modules/react-native-drax/lib/typescript/src/hooks/useDraxId.d.ts +2 -0
  148. package/node_modules/react-native-drax/lib/typescript/src/hooks/useDraxId.d.ts.map +1 -0
  149. package/node_modules/react-native-drax/lib/typescript/src/hooks/useDraxMethods.d.ts +13 -0
  150. package/node_modules/react-native-drax/lib/typescript/src/hooks/useDraxMethods.d.ts.map +1 -0
  151. package/node_modules/react-native-drax/lib/typescript/src/hooks/useDraxScrollHandler.d.ts +27 -0
  152. package/node_modules/react-native-drax/lib/typescript/src/hooks/useDraxScrollHandler.d.ts.map +1 -0
  153. package/node_modules/react-native-drax/lib/typescript/src/hooks/useSortableBoard.d.ts +10 -0
  154. package/node_modules/react-native-drax/lib/typescript/src/hooks/useSortableBoard.d.ts.map +1 -0
  155. package/node_modules/react-native-drax/lib/typescript/src/hooks/useSortableList.d.ts +11 -0
  156. package/node_modules/react-native-drax/lib/typescript/src/hooks/useSortableList.d.ts.map +1 -0
  157. package/node_modules/react-native-drax/lib/typescript/src/hooks/useSpatialIndex.d.ts +22 -0
  158. package/node_modules/react-native-drax/lib/typescript/src/hooks/useSpatialIndex.d.ts.map +1 -0
  159. package/node_modules/react-native-drax/lib/typescript/src/hooks/useViewStyles.d.ts +183 -0
  160. package/node_modules/react-native-drax/lib/typescript/src/hooks/useViewStyles.d.ts.map +1 -0
  161. package/node_modules/react-native-drax/lib/typescript/src/hooks/useWebScrollFreeze.d.ts +14 -0
  162. package/node_modules/react-native-drax/lib/typescript/src/hooks/useWebScrollFreeze.d.ts.map +1 -0
  163. package/node_modules/react-native-drax/lib/typescript/src/index.d.ts +25 -0
  164. package/node_modules/react-native-drax/lib/typescript/src/index.d.ts.map +1 -0
  165. package/node_modules/react-native-drax/lib/typescript/src/math.d.ts +76 -0
  166. package/node_modules/react-native-drax/lib/typescript/src/math.d.ts.map +1 -0
  167. package/node_modules/react-native-drax/lib/typescript/src/params.d.ts +31 -0
  168. package/node_modules/react-native-drax/lib/typescript/src/params.d.ts.map +1 -0
  169. package/node_modules/react-native-drax/lib/typescript/src/types.d.ts +756 -0
  170. package/node_modules/react-native-drax/lib/typescript/src/types.d.ts.map +1 -0
  171. package/node_modules/react-native-drax/package.json +175 -0
  172. package/node_modules/react-native-drax/src/DebugOverlay.tsx +140 -0
  173. package/node_modules/react-native-drax/src/Drax.ts +33 -0
  174. package/node_modules/react-native-drax/src/DraxContext.ts +8 -0
  175. package/node_modules/react-native-drax/src/DraxHandle.tsx +52 -0
  176. package/node_modules/react-native-drax/src/DraxHandleContext.ts +15 -0
  177. package/node_modules/react-native-drax/src/DraxList.tsx +181 -0
  178. package/node_modules/react-native-drax/src/DraxProvider.tsx +224 -0
  179. package/node_modules/react-native-drax/src/DraxScrollView.tsx +180 -0
  180. package/node_modules/react-native-drax/src/DraxSubprovider.tsx +22 -0
  181. package/node_modules/react-native-drax/src/DraxView.tsx +430 -0
  182. package/node_modules/react-native-drax/src/HoverLayer.tsx +167 -0
  183. package/node_modules/react-native-drax/src/SortableBoardContainer.tsx +439 -0
  184. package/node_modules/react-native-drax/src/SortableBoardContext.ts +6 -0
  185. package/node_modules/react-native-drax/src/SortableContainer.tsx +650 -0
  186. package/node_modules/react-native-drax/src/SortableItem.tsx +263 -0
  187. package/node_modules/react-native-drax/src/SortableItemContext.ts +46 -0
  188. package/node_modules/react-native-drax/src/compat/detectVersion.ts +17 -0
  189. package/node_modules/react-native-drax/src/compat/index.ts +7 -0
  190. package/node_modules/react-native-drax/src/compat/types.ts +35 -0
  191. package/node_modules/react-native-drax/src/compat/useDraxPanGesture.ts +111 -0
  192. package/node_modules/react-native-drax/src/hooks/index.ts +2 -0
  193. package/node_modules/react-native-drax/src/hooks/useCallbackDispatch.tsx +830 -0
  194. package/node_modules/react-native-drax/src/hooks/useDragGesture.ts +273 -0
  195. package/node_modules/react-native-drax/src/hooks/useDraxContext.ts +11 -0
  196. package/node_modules/react-native-drax/src/hooks/useDraxId.ts +11 -0
  197. package/node_modules/react-native-drax/src/hooks/useDraxMethods.ts +71 -0
  198. package/node_modules/react-native-drax/src/hooks/useDraxScrollHandler.ts +121 -0
  199. package/node_modules/react-native-drax/src/hooks/useSortableBoard.ts +44 -0
  200. package/node_modules/react-native-drax/src/hooks/useSortableList.ts +1102 -0
  201. package/node_modules/react-native-drax/src/hooks/useSpatialIndex.ts +336 -0
  202. package/node_modules/react-native-drax/src/hooks/useViewStyles.ts +180 -0
  203. package/node_modules/react-native-drax/src/hooks/useWebScrollFreeze.ts +60 -0
  204. package/node_modules/react-native-drax/src/index.ts +111 -0
  205. package/node_modules/react-native-drax/src/math.ts +333 -0
  206. package/node_modules/react-native-drax/src/params.ts +74 -0
  207. package/node_modules/react-native-drax/src/types.ts +933 -0
  208. package/package.json +11 -5
package/README.md CHANGED
@@ -82,7 +82,7 @@ dark theme tokens. Applications that provide product-specific tokens can pass
82
82
  | Actions | Button, CustomPressable, Fab, IconButton |
83
83
  | Inputs | Calendar, Checkbox, EditText, RadioGroup, Rating, SegmentedControl, SwitchToggle |
84
84
  | Feedback | AlertDialog, ErrorBoundary, LoadingIndicator, Snackbar |
85
- | Display | Accordion, Card, Hr, Icon, ParallaxHeader, Typography |
85
+ | Display | Accordion, Card, Hr, Icon, ParallaxHeader, ReorderableList, Typography |
86
86
  | Media and system | PinchZoom, StatusBarBrightness |
87
87
 
88
88
  Each public component has automated tests and Storybook coverage for its main
@@ -91,6 +91,41 @@ controls, responsive behavior, accessibility results, and source examples.
91
91
 
92
92
  ## Release Matrix
93
93
 
94
+ ### ReorderableList (0.8.0-beta.4)
95
+
96
+ `ReorderableList<T>` composes FlashList and react-native-drax. Provide stable
97
+ domain keys, `getItemLabel`, controlled `data`/`onReorder`, and place the
98
+ supplied `dragHandle` inside `renderItem`. Keep the host within
99
+ `GestureHandlerRootView` and give the list a bounded viewport. Apply the new
100
+ order optimistically and roll back on persistence failure. `disabled` removes
101
+ handles without hiding content. The 48pt handles also expose assistive
102
+ increment/decrement actions; no visible Move up/Move down controls are needed.
103
+ Variable row height, autoscroll and reduced motion are owned by the engine.
104
+ FlashList's default visible-item anchoring is disabled for this reorderable
105
+ surface: keeping the former first row anchored after a move would scroll the
106
+ viewport unexpectedly. Accepted orders become real row layout, not permanent
107
+ transforms, so subsequent inline editor height changes remain coherent.
108
+ The row renderer owns text semantics: compact titles may truncate, body copy
109
+ must not inherit that limit. See Display/ReorderableList stories.
110
+
111
+ Requires FlashList 2 (New Architecture), React 19 and Reanimated 4. The package
112
+ bundles its exact patched, JS-only Drax 1.1.0 dependency and MIT license. The
113
+ reproducible patch is `patches/react-native-drax@1.1.0.patch`; the build refuses
114
+ an unpatched engine. Consumers do not need a local patch, fork or postinstall
115
+ mutation. In RN 0.85+ overlays use `StyleSheet.absoluteFill` instead of removed
116
+ `absoluteFillObject`. Verify the packed engine, not only the source checkout.
117
+ The same patch late-binds SortableItem's snap callback: the container registers
118
+ it in a layout effect, so destructuring it during render captures `undefined`
119
+ and prevents the completed drop from committing. Package-boundary regression
120
+ guards cover source and module output; native drag/persistence remains required.
121
+ The patch also supports partially measured single-column virtualized lists:
122
+ hit testing uses known slots and displacement needs geometry only within the
123
+ changed range. Unknown row heights are not guessed, and grid behavior remains
124
+ unchanged. When the consumer accepts an order, flush the temporary visual
125
+ permutation into actual data and clear shifts in the layout phase. Tests cover
126
+ partial measurement, variable heights, horizontal lists and accepted order;
127
+ native reorder → expand/edit → save → reopen must still be replayed.
128
+
94
129
  The 0.8.0 beta is verified with Expo 54, React Native 0.81, React 19,
95
130
  kstyled 0.4, and React Native Web 0.21. This is the release validation matrix,
96
131
  not a claim that every consumer must use those exact React or React Native
@@ -119,6 +154,33 @@ main-branch GitHub Actions publish workflow with provenance.
119
154
  - [Changelog](./CHANGELOG.md)
120
155
  - [Contributing](./CONTRIBUTING.md)
121
156
 
157
+ ### Custom dialog slots
158
+
159
+ `AlertDialog` supports the same slots in controlled props and
160
+ `useCPK().alertDialog.open(options)`: `renderHeader`, `renderBody`, and
161
+ `renderActions`. Each receives `{close}`. Render functions take precedence
162
+ over the existing `title`, `body`, and `actions` content; keep `title` as a
163
+ plain string to name the dialog for assistive technology.
164
+
165
+ ```tsx
166
+ alertDialog.open({
167
+ title: 'Transfer ownership',
168
+ renderHeader: () => <OwnershipHeader />,
169
+ renderBody: () => <OwnershipExplanation />,
170
+ renderActions: ({close}) => [
171
+ <Button key="cancel" text="Cancel" onPress={close} />,
172
+ <Button key="confirm" text="Transfer" onPress={requestTransfer} />,
173
+ ],
174
+ closeOnTouchOutside: false,
175
+ });
176
+ ```
177
+
178
+ The header may contain an icon or any custom content. Keep its root's height
179
+ intrinsic (not `flex: 1` inside the title slot). Custom action `style` overrides
180
+ the equal-width defaults; `flex: 0, flexBasis: 'auto'` works for intrinsic-width
181
+ actions on native and web. Use `styles.actionContainer` to align the group.
182
+ The backdrop consistently darkens either theme and respects `backdropOpacity`.
183
+
122
184
  ## License
123
185
 
124
186
  MIT
@@ -18,6 +18,13 @@ export type AlertDialogOptions = {
18
18
  actions?: ReactElement[];
19
19
  showCloseButton?: boolean;
20
20
  size?: AlertDialogSizeType;
21
+ /** Custom slots take precedence over title/body/actions. Keep title for a11y. */
22
+ renderHeader?: (context: AlertDialogRenderContext) => ReactElement | null;
23
+ renderBody?: (context: AlertDialogRenderContext) => ReactElement | null;
24
+ renderActions?: (context: AlertDialogRenderContext) => ReactElement[] | null;
25
+ };
26
+ export type AlertDialogRenderContext = {
27
+ close: () => void;
21
28
  };
22
29
  export type AlertDialogProps = {
23
30
  style?: StyleProp<ViewStyle>;
@@ -1 +1 @@
1
- {"version":3,"file":"AlertDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/modals/AlertDialog/AlertDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAQZ,KAAK,YAAY,EAClB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,cAAc,CAAC;AA8DlE,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAExE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,eAAe,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,mBAAmB,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,GAAG,kBAAkB,CAAC;AAEvB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,CAAC,kBAAkB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpD,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAmUF,eAAO,MAAM,WAAW;YA3Ud,SAAS,CAAC,SAAS,CAAC;cAClB,OAAO;cACP,MAAM,IAAI;iEA2UrB,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"AlertDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/modals/AlertDialog/AlertDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EASZ,KAAK,YAAY,EAClB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,cAAc,CAAC;AA8DlE,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAExE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,eAAe,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,iFAAiF;IACjF,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,YAAY,GAAG,IAAI,CAAC;IAC1E,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,YAAY,GAAG,IAAI,CAAC;IACxE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,YAAY,EAAE,GAAG,IAAI,CAAC;CAC9E,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,GAAG,kBAAkB,CAAC;AAEvB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,CAAC,kBAAkB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpD,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAyVF,eAAO,MAAM,WAAW;YAjWd,SAAS,CAAC,SAAS,CAAC;cAClB,OAAO;cACP,MAAM,IAAI;iEAiWrB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { cloneElement, forwardRef, useEffect, useImperativeHandle, useState, useCallback, useMemo, } from 'react';
2
+ import React, { cloneElement, forwardRef, useEffect, useImperativeHandle, useState, useCallback, useMemo, isValidElement, } from 'react';
3
3
  import { KeyboardAvoidingView, Modal, Platform, StyleSheet, TouchableWithoutFeedback, View, } from 'react-native';
4
4
  import { styled, css } from 'kstyled';
5
5
  import { useTheme } from '../../../providers/ThemeProvider';
@@ -52,10 +52,10 @@ const ActionRow = styled.View `
52
52
  padding-right: 4px;
53
53
  flex-direction: row;
54
54
  `;
55
- function AlertDialogImpl({ actions: actionsProp, backdropOpacity: backdropOpacityProp, body: bodyProp, closeOnTouchOutside: closeOnTouchOutsideProp, onClose, showCloseButton: showCloseButtonProp, size: sizeProp, style, styles: stylesProp, title: titleProp, visible: visibleProp, }, ref) {
55
+ function AlertDialogImpl({ actions: actionsProp, backdropOpacity: backdropOpacityProp, body: bodyProp, closeOnTouchOutside: closeOnTouchOutsideProp, onClose, renderHeader: renderHeaderProp, renderBody: renderBodyProp, renderActions: renderActionsProp, showCloseButton: showCloseButtonProp, size: sizeProp, style, styles: stylesProp, title: titleProp, visible: visibleProp, }, ref) {
56
56
  const [openedOptions, setOpenedOptions] = useState(null);
57
57
  const [uncontrolledVisible, setUncontrolledVisible] = useState(false);
58
- const { theme, themeType } = useTheme();
58
+ const { theme } = useTheme();
59
59
  const isControlled = visibleProp !== undefined;
60
60
  const visible = isControlled ? visibleProp : uncontrolledVisible;
61
61
  const options = isControlled
@@ -63,6 +63,9 @@ function AlertDialogImpl({ actions: actionsProp, backdropOpacity: backdropOpacit
63
63
  actions: actionsProp,
64
64
  backdropOpacity: backdropOpacityProp,
65
65
  body: bodyProp,
66
+ renderHeader: renderHeaderProp,
67
+ renderBody: renderBodyProp,
68
+ renderActions: renderActionsProp,
66
69
  closeOnTouchOutside: closeOnTouchOutsideProp,
67
70
  showCloseButton: showCloseButtonProp,
68
71
  size: sizeProp,
@@ -95,7 +98,7 @@ function AlertDialogImpl({ actions: actionsProp, backdropOpacity: backdropOpacit
95
98
  open: handleOpen,
96
99
  close: handleClose,
97
100
  }), [handleOpen, handleClose]);
98
- const { backdropOpacity = 0.2, title, body, styles, actions, closeOnTouchOutside = true, showCloseButton = true, size = 'medium', } = options ?? {};
101
+ const { backdropOpacity = 0.2, title, body, renderHeader, renderBody, renderActions, styles, actions, closeOnTouchOutside = true, showCloseButton = true, size = 'medium', } = options ?? {};
99
102
  // Calculate sizes based on size prop
100
103
  const sizeConfig = useMemo(() => {
101
104
  if (typeof size === 'number') {
@@ -138,10 +141,9 @@ function AlertDialogImpl({ actions: actionsProp, backdropOpacity: backdropOpacit
138
141
  };
139
142
  }
140
143
  }, [size]);
141
- // Memoize backdrop color calculation
142
- const backdropColor = useMemo(() => themeType === 'light'
143
- ? `rgba(0,0,0,${backdropOpacity})`
144
- : `rgba(255,255,255,${backdropOpacity})`, [themeType, backdropOpacity]);
144
+ // A scrim subdues the background in either theme. A white dark-mode overlay
145
+ // washes out the page and reverses the dialog's foreground hierarchy.
146
+ const backdropColor = useMemo(() => `rgba(0,0,0,${backdropOpacity})`, [backdropOpacity]);
145
147
  // Memoize shadow styles
146
148
  const shadowStyles = useMemo(() => Platform.OS !== 'web'
147
149
  ? {
@@ -157,18 +159,31 @@ function AlertDialogImpl({ actions: actionsProp, backdropOpacity: backdropOpacit
157
159
  handleClose();
158
160
  }, [closeOnTouchOutside, handleClose]);
159
161
  const handleCloseButtonPress = handleClose;
162
+ const renderContext = useMemo(() => ({ close: handleClose }), [handleClose]);
160
163
  // Memoize title content
161
- const titleContent = useMemo(() => typeof title === 'string' ? (_jsx(Typography.Heading3, { style: [{ fontSize: sizeConfig.titleFontSize }, styles?.title], children: title })) : (title), [title, sizeConfig.titleFontSize, styles?.title]);
164
+ const titleContent = useMemo(() => renderHeader ? (renderHeader(renderContext)) : typeof title === 'string' ? (_jsx(Typography.Heading3, { style: [{ fontSize: sizeConfig.titleFontSize }, styles?.title], children: title })) : (title), [
165
+ renderHeader,
166
+ renderContext,
167
+ title,
168
+ sizeConfig.titleFontSize,
169
+ styles?.title,
170
+ ]);
162
171
  // Memoize body content
163
- const bodyContent = useMemo(() => typeof body === 'string' ? (_jsx(Typography.Body3, { style: [{ fontSize: sizeConfig.bodyFontSize }, styles?.body], children: body })) : (body), [body, sizeConfig.bodyFontSize, styles?.body]);
172
+ const bodyContent = useMemo(() => renderBody ? (renderBody(renderContext)) : typeof body === 'string' ? (_jsx(Typography.Body3, { style: [{ fontSize: sizeConfig.bodyFontSize }, styles?.body], children: body })) : (body), [renderBody, renderContext, body, sizeConfig.bodyFontSize, styles?.body]);
164
173
  // Memoize actions content
165
- const actionsContent = useMemo(() => actions ? (_jsx(ActionRow, { "$marginTop": sizeConfig.actionMarginTop, style: styles?.actionContainer, children: actions.map((action, index) => cloneElement(action, {
166
- key: `action-${index}`,
167
- style: {
168
- flex: 1,
169
- marginLeft: index !== 0 ? 12 : 0,
170
- },
171
- })) })) : null, [actions, sizeConfig.actionMarginTop, styles?.actionContainer]);
174
+ const renderedActions = useMemo(() => (renderActions ? renderActions(renderContext) : actions), [renderActions, renderContext, actions]);
175
+ const actionsContent = useMemo(() => renderedActions ? (_jsx(ActionRow, { "$marginTop": sizeConfig.actionMarginTop, style: styles?.actionContainer, children: renderedActions.map((action, index) => isValidElement(action)
176
+ ? cloneElement(action, {
177
+ key: `action-${index}`,
178
+ style: [
179
+ {
180
+ flex: 1,
181
+ marginLeft: index !== 0 ? 12 : 0,
182
+ },
183
+ action.props.style,
184
+ ],
185
+ })
186
+ : action) })) : null, [renderedActions, sizeConfig.actionMarginTop, styles?.actionContainer]);
172
187
  // Memoize close button content
173
188
  const closeButtonContent = useMemo(() => showCloseButton ? (_jsx(Button, { accessibilityLabel: "Close dialog", onPress: handleCloseButtonPress, borderRadius: 24, text: _jsx(Icon, { color: theme.text.basic, name: "X", size: sizeConfig.iconSize }), type: "text" })) : null, [
174
189
  showCloseButton,
@@ -177,7 +192,7 @@ function AlertDialogImpl({ actions: actionsProp, backdropOpacity: backdropOpacit
177
192
  theme.text.basic,
178
193
  ]);
179
194
  const titleSlotStyle = useMemo(() => ({ flex: 1, minWidth: 0 }), []);
180
- const AlertDialogContent = useMemo(() => (_jsxs(Container, { style: css `
195
+ const AlertDialogContent = useMemo(() => (_jsxs(Container, { testID: "alert-dialog-overlay", style: css `
181
196
  background-color: ${backdropColor};
182
197
  `, children: [_jsx(TouchableWithoutFeedback, { onPress: handleBackdropPress, children: _jsx(View, { style: StyleSheet.absoluteFill, testID: "alert-dialog-backdrop" }) }), _jsxs(AlertDialogContainer, { "$size": size, accessibilityRole: "alert", accessibilityLabel: typeof title === 'string' ? title : 'Alert dialog', style: containerStyles, children: [_jsxs(TitleRow, { style: styles?.titleContainer, children: [_jsx(View, { style: titleSlotStyle, children: titleContent }), closeButtonContent] }), _jsx(BodyRow, { "$marginTop": sizeConfig.bodyMarginTop, "$marginBottom": sizeConfig.bodyMarginBottom, style: styles?.bodyContainer, children: bodyContent }), actionsContent] })] })), [
183
198
  backdropColor,
@@ -197,7 +212,7 @@ function AlertDialogImpl({ actions: actionsProp, backdropOpacity: backdropOpacit
197
212
  ]);
198
213
  return (
199
214
  // https://github.com/facebook/react-native/issues/48526#issuecomment-2579478884
200
- _jsx(View, { style: style, children: _jsx(Modal, { animationType: "fade", onRequestClose: handleClose, transparent: true, visible: visible, children: _jsx(KeyboardAvoidingView, { behavior: Platform.OS === 'ios' ? 'padding' : undefined, style: keyboardAvoidingStyle, children: closeOnTouchOutside ? (_jsx(TouchableWithoutFeedback, { onPress: handleBackdropPress, children: AlertDialogContent })) : (AlertDialogContent) }) }) }));
215
+ _jsx(View, { style: style, children: _jsx(Modal, { animationType: "fade", onRequestClose: handleClose, transparent: true, visible: visible, children: _jsx(KeyboardAvoidingView, { behavior: Platform.OS === 'ios' ? 'padding' : undefined, style: keyboardAvoidingStyle, children: AlertDialogContent }) }) }));
201
216
  }
202
217
  const keyboardAvoidingStyle = { flex: 1 };
203
218
  export const AlertDialog = React.memo(forwardRef(AlertDialogImpl));
@@ -10,6 +10,11 @@ type Styles = {
10
10
  toggleElement?: StyleProp<ViewStyle>;
11
11
  };
12
12
  export type AccordionSizeType = 'small' | 'medium' | 'large' | number;
13
+ export type AccordionHeaderContext<T> = {
14
+ title: T;
15
+ expanded: boolean;
16
+ toggle: () => void;
17
+ };
13
18
  export type AccordionBaseProps<T = string, K = string> = {
14
19
  data: AccordionItemDataType<T, K>[];
15
20
  style?: StyleProp<ViewStyle>;
@@ -22,16 +27,21 @@ export type AccordionBaseProps<T = string, K = string> = {
22
27
  expandAllOnStart?: boolean;
23
28
  /** Array of indexes that should be expanded on start. Overrides expandAllOnStart */
24
29
  defaultExpandedIndexes?: number[];
30
+ /** Controlled disclosure, including an empty array to close every item. */
31
+ expandedIndexes?: readonly number[];
32
+ onExpandedChange?: (index: number, expanded: boolean) => void;
25
33
  animDuration?: number;
26
34
  activeOpacity?: number;
27
35
  toggleElementPosition?: 'left' | 'right';
28
36
  toggleElement?: ReactElement | null;
29
37
  renderTitle?: (title: T) => ReactElement;
38
+ /** Own the entire header, including independent menu/drag controls. */
39
+ renderHeader?: (context: AccordionHeaderContext<T>) => ReactElement;
30
40
  renderItem?: (body: K) => ReactElement;
31
41
  onPressItem?: (title: T | string, body: K | string) => void;
32
42
  };
33
43
  export type AccordionProps<T = string, K = string> = AccordionBaseProps<T, K>;
34
- declare function Accordion<T, K>({ style, toggleElementPosition, size, data, collapseOnStart, expandAllOnStart, defaultExpandedIndexes, ...rest }: AccordionProps<T, K>): ReactElement;
44
+ declare function Accordion<T, K>({ style, toggleElementPosition, size, data, collapseOnStart, expandAllOnStart, defaultExpandedIndexes, expandedIndexes, onExpandedChange, ...rest }: AccordionProps<T, K>): ReactElement;
35
45
  declare const _default: typeof Accordion;
36
46
  export default _default;
37
47
  export { Accordion };
@@ -1 +1 @@
1
- {"version":3,"file":"Accordion.d.ts","sourceRoot":"","sources":["../../../../src/components/uis/Accordion/Accordion.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAU,KAAK,YAAY,EAAC,MAAM,OAAO,CAAC;AACxD,OAAO,KAAK,EAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,cAAc,CAAC;AAIlE,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,iBAAiB,CAAC;AAO3D,KAAK,MAAM,GAAG;IACZ,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAChC,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAEtE,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,MAAM,IAAI;IACvD,IAAI,EAAE,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IACpC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,+CAA+C;IAC/C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,oFAAoF;IACpF,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qBAAqB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzC,aAAa,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,YAAY,CAAC;IACzC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,YAAY,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC;CAC7D,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,MAAM,IAAI,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE9E,iBAAS,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,EACvB,KAAK,EACL,qBAA+B,EAC/B,IAAe,EACf,IAAI,EACJ,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,GAAG,IAAI,EACR,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CAiDrC;wBAGuC,OAAO,SAAS;AAAxD,wBAAyD;AACzD,OAAO,EAAC,SAAS,EAAC,CAAC"}
1
+ {"version":3,"file":"Accordion.d.ts","sourceRoot":"","sources":["../../../../src/components/uis/Accordion/Accordion.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAU,KAAK,YAAY,EAAC,MAAM,OAAO,CAAC;AACxD,OAAO,KAAK,EAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,cAAc,CAAC;AAIlE,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,iBAAiB,CAAC;AAO3D,KAAK,MAAM,GAAG;IACZ,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAChC,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAEtE,MAAM,MAAM,sBAAsB,CAAC,CAAC,IAAI;IACtC,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,MAAM,IAAI;IACvD,IAAI,EAAE,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IACpC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,+CAA+C;IAC/C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,oFAAoF;IACpF,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC,2EAA2E;IAC3E,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qBAAqB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzC,aAAa,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,YAAY,CAAC;IACzC,uEAAuE;IACvE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,YAAY,CAAC;IACpE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,YAAY,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC;CAC7D,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,MAAM,IAAI,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE9E,iBAAS,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,EACvB,KAAK,EACL,qBAA+B,EAC/B,IAAe,EACf,IAAI,EACJ,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,GAAG,IAAI,EACR,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CAuDrC;wBAGuC,OAAO,SAAS;AAAxD,wBAAyD;AACzD,OAAO,EAAC,SAAS,EAAC,CAAC"}
@@ -6,7 +6,7 @@ import { AccordionItem } from './AccordionItem';
6
6
  const Container = styled(View) `
7
7
  flex-direction: column;
8
8
  `;
9
- function Accordion({ style, toggleElementPosition = 'right', size = 'medium', data, collapseOnStart, expandAllOnStart, defaultExpandedIndexes, ...rest }) {
9
+ function Accordion({ style, toggleElementPosition = 'right', size = 'medium', data, collapseOnStart, expandAllOnStart, defaultExpandedIndexes, expandedIndexes, onExpandedChange, ...rest }) {
10
10
  // Memoize accordion items rendering
11
11
  const accordionItems = useMemo(() => data.map((datum, titleKey) => {
12
12
  // Determine if this item should be collapsed on start
@@ -28,7 +28,7 @@ function Accordion({ style, toggleElementPosition = 'right', size = 'medium', da
28
28
  // Default: all items collapsed
29
29
  shouldCollapse = true;
30
30
  }
31
- return (_jsx(AccordionItem, { data: datum, testID: `${titleKey}`, toggleElementPosition: toggleElementPosition, size: size, collapseOnStart: shouldCollapse, ...rest }, titleKey));
31
+ return (_jsx(AccordionItem, { data: datum, testID: `${titleKey}`, toggleElementPosition: toggleElementPosition, size: size, collapseOnStart: shouldCollapse, expanded: expandedIndexes?.includes(titleKey), onExpandedChange: (expanded) => onExpandedChange?.(titleKey, expanded), ...rest }, titleKey));
32
32
  }), [
33
33
  data,
34
34
  toggleElementPosition,
@@ -36,6 +36,8 @@ function Accordion({ style, toggleElementPosition = 'right', size = 'medium', da
36
36
  collapseOnStart,
37
37
  expandAllOnStart,
38
38
  defaultExpandedIndexes,
39
+ expandedIndexes,
40
+ onExpandedChange,
39
41
  rest,
40
42
  ]);
41
43
  return _jsx(Container, { style: style, children: accordionItems });
@@ -4,11 +4,14 @@ export type AccordionItemDataType<T, K> = {
4
4
  title: T;
5
5
  items: K[];
6
6
  };
7
- type Props<T, K> = Omit<AccordionBaseProps<T, K>, 'data' | 'style'> & {
7
+ type Props<T, K> = Omit<AccordionBaseProps<T, K>, 'data' | 'style' | 'onExpandedChange'> & {
8
8
  testID: string;
9
- data: AccordionItemDataType<string, string> | AccordionItemDataType<T, K>;
9
+ data: AccordionItemDataType<T, K>;
10
+ expanded?: boolean;
11
+ onExpandedChange?: (expanded: boolean) => void;
10
12
  };
11
- export declare function AccordionItem<T, K>({ testID, data: data, size, shouldAnimate, collapseOnStart, animDuration, activeOpacity, toggleElement, toggleElementPosition, onPressItem, renderTitle, renderItem, styles, }: Props<T, K>): ReactElement;
13
+ /** One mounted body: no invisible duplicate controls or JS-frame height loop. */
14
+ export declare function AccordionItem<T, K>({ testID, data, size, shouldAnimate, collapseOnStart, expanded: controlledExpanded, onExpandedChange, animDuration, activeOpacity, toggleElement, toggleElementPosition, onPressItem, renderTitle, renderHeader, renderItem, styles, }: Props<T, K>): ReactElement;
12
15
  declare const _default: typeof AccordionItem;
13
16
  export default _default;
14
17
  //# sourceMappingURL=AccordionItem.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AccordionItem.d.ts","sourceRoot":"","sources":["../../../../src/components/uis/Accordion/AccordionItem.tsx"],"names":[],"mappings":"AACA,OAAc,EAMZ,KAAK,YAAY,EAClB,MAAM,OAAO,CAAC;AAQf,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,aAAa,CAAC;AA+DpD,MAAM,MAAM,qBAAqB,CAAC,CAAC,EAAE,CAAC,IAAI;IACxC,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,CAAC,EAAE,CAAC;CACZ,CAAC;AAEF,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAC3E,CAAC;AAEF,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,EAClC,MAAM,EACN,IAAI,EAAE,IAAI,EACV,IAAe,EACf,aAAoB,EACpB,eAAsB,EACtB,YAAkB,EAClB,aAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,WAAW,EACX,WAAW,EACX,UAAU,EACV,MAAM,GACP,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CAmR5B;wBAG2C,OAAO,aAAa;AAAhE,wBAAiE"}
1
+ {"version":3,"file":"AccordionItem.d.ts","sourceRoot":"","sources":["../../../../src/components/uis/Accordion/AccordionItem.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAoB,KAAK,YAAY,EAAC,MAAM,OAAO,CAAC;AAalE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,aAAa,CAAC;AAEpD,MAAM,MAAM,qBAAqB,CAAC,CAAC,EAAE,CAAC,IAAI;IAAC,KAAK,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,CAAC,EAAE,CAAA;CAAC,CAAC;AAEjE,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CACrB,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,EACxB,MAAM,GAAG,OAAO,GAAG,kBAAkB,CACtC,GAAG;IACF,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,EAClC,MAAM,EACN,IAAI,EACJ,IAAe,EACf,aAAoB,EACpB,eAAsB,EACtB,QAAQ,EAAE,kBAAkB,EAC5B,gBAAgB,EAChB,YAAkB,EAClB,aAAiB,EACjB,aAAa,EACb,qBAA+B,EAC/B,WAAW,EACX,WAAW,EACX,YAAY,EACZ,UAAU,EACV,MAAM,GACP,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CA+M5B;wBAQ2C,OAAO,aAAa;AAAhE,wBAAiE"}
@@ -1,209 +1,121 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- // Caveat: Expo web needs React to be imported
3
- import React, { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
4
- import { Animated, Easing, TouchableOpacity, View } from 'react-native';
5
- import { styled, css } from 'kstyled';
2
+ import React, { useMemo, useState } from 'react';
3
+ import { StyleSheet, TouchableOpacity, View } from 'react-native';
4
+ import Animated, { Easing, FadeIn, FadeOut, LinearTransition, ReduceMotion, useReducedMotion, } from 'react-native-reanimated';
5
+ import { useTheme } from '../../../providers/ThemeProvider';
6
6
  import { Icon } from '../Icon/Icon';
7
7
  import { Typography } from '../Typography/Typography';
8
- const TitleTouch = styled(TouchableOpacity) `
9
- height: ${({ $size = 'medium' }) => {
10
- if (typeof $size === 'number')
11
- return $size * 2.4;
12
- return $size === 'small' ? 40 : $size === 'large' ? 56 : 48;
13
- }};
14
- background-color: ${({ theme }) => theme.bg.basic};
15
-
16
- flex-direction: row;
17
- align-items: center;
18
- padding-top: ${({ $size = 'medium' }) => {
19
- if (typeof $size === 'number')
20
- return $size * 0.4;
21
- return $size === 'small' ? 6 : $size === 'large' ? 10 : 8;
22
- }};
23
- padding-bottom: ${({ $size = 'medium' }) => {
24
- if (typeof $size === 'number')
25
- return $size * 0.4;
26
- return $size === 'small' ? 6 : $size === 'large' ? 10 : 8;
27
- }};
28
- padding-left: ${({ $size = 'medium' }) => {
29
- if (typeof $size === 'number')
30
- return $size * 0.6;
31
- return $size === 'small' ? 10 : $size === 'large' ? 14 : 12;
32
- }};
33
- padding-right: ${({ $size = 'medium' }) => {
34
- if (typeof $size === 'number')
35
- return $size * 0.6;
36
- return $size === 'small' ? 10 : $size === 'large' ? 14 : 12;
37
- }};
38
- `;
39
- const StyledIcon = styled(Icon) `
40
- color: ${({ theme }) => theme.text.basic};
41
- font-weight: bold;
42
- font-size: ${({ $size = 'medium' }) => {
43
- if (typeof $size === 'number')
44
- return $size * 0.7;
45
- return $size === 'small' ? 12 : $size === 'large' ? 16 : 14;
46
- }};
47
- `;
48
- const ItemTouch = styled(TouchableOpacity) `
49
- background-color: ${({ theme }) => theme.bg.paper};
50
- padding-top: ${({ $size = 'medium' }) => {
51
- if (typeof $size === 'number')
52
- return $size * 0.4;
53
- return $size === 'small' ? 6 : $size === 'large' ? 10 : 8;
54
- }};
55
- padding-bottom: ${({ $size = 'medium' }) => {
56
- if (typeof $size === 'number')
57
- return $size * 0.4;
58
- return $size === 'small' ? 6 : $size === 'large' ? 10 : 8;
59
- }};
60
- padding-left: ${({ $size = 'medium' }) => {
61
- if (typeof $size === 'number')
62
- return $size * 0.6;
63
- return $size === 'small' ? 10 : $size === 'large' ? 14 : 12;
64
- }};
65
- padding-right: ${({ $size = 'medium' }) => {
66
- if (typeof $size === 'number')
67
- return $size * 0.6;
68
- return $size === 'small' ? 10 : $size === 'large' ? 14 : 12;
69
- }};
70
-
71
- flex-direction: row;
72
- align-items: center;
73
- `;
74
- export function AccordionItem({ testID, data: data, size = 'medium', shouldAnimate = true, collapseOnStart = true, animDuration = 200, activeOpacity = 1, toggleElement, toggleElementPosition, onPressItem, renderTitle, renderItem, styles, }) {
75
- // Calculate icon size based on accordion size
76
- const iconSize = typeof size === 'number'
77
- ? size * 0.7
8
+ /** One mounted body: no invisible duplicate controls or JS-frame height loop. */
9
+ export function AccordionItem({ testID, data, size = 'medium', shouldAnimate = true, collapseOnStart = true, expanded: controlledExpanded, onExpandedChange, animDuration = 200, activeOpacity = 1, toggleElement, toggleElementPosition = 'right', onPressItem, renderTitle, renderHeader, renderItem, styles, }) {
10
+ const { theme } = useTheme();
11
+ const reducedMotion = useReducedMotion();
12
+ const [localExpanded, setLocalExpanded] = useState(!collapseOnStart);
13
+ const expanded = controlledExpanded ?? localExpanded;
14
+ const duration = shouldAnimate && !reducedMotion ? animDuration : 0;
15
+ const transition = useMemo(() => LinearTransition.duration(duration)
16
+ .easing(Easing.bezier(0.23, 1, 0.32, 1))
17
+ .reduceMotion(ReduceMotion.System), [duration]);
18
+ const entering = useMemo(() => FadeIn.duration(duration).reduceMotion(ReduceMotion.System), [duration]);
19
+ const exiting = useMemo(() => FadeOut.duration(duration).reduceMotion(ReduceMotion.System), [duration]);
20
+ const toggle = () => {
21
+ if (controlledExpanded === undefined)
22
+ setLocalExpanded(!expanded);
23
+ onExpandedChange?.(!expanded);
24
+ };
25
+ const numeric = typeof size === 'number';
26
+ const titleHeight = numeric
27
+ ? size * 2.4
78
28
  : size === 'small'
79
- ? 12
29
+ ? 40
80
30
  : size === 'large'
81
- ? 16
82
- : 14;
83
- const defaultToggleElement = (_jsx(StyledIcon, { name: "CaretDown", "$size": size, size: iconSize }));
84
- const dropDownAnimValueRef = useRef(new Animated.Value(0));
85
- const rotateAnimValueRef = useRef(new Animated.Value(0));
86
- const fadeItemAnim = useRef(new Animated.Value(0)).current;
87
- const [itemHeight, setItemHeight] = useState(0);
88
- const [collapsed, setCollapsed] = useState(collapseOnStart);
89
- // Memoize animation configuration
90
- const animationConfig = useMemo(() => ({
91
- duration: animDuration,
92
- easing: Easing.linear,
93
- useNativeDriver: false,
94
- }), [animDuration]);
95
- useEffect(() => {
96
- Animated.timing(fadeItemAnim, {
97
- toValue: collapsed ? 0 : 1,
98
- duration: !collapsed ? 300 : 100,
99
- useNativeDriver: false,
100
- }).start();
101
- }, [fadeItemAnim, collapsed]);
102
- useEffect(() => {
103
- const targetValue = collapsed ? 0 : 1;
104
- if (!shouldAnimate) {
105
- rotateAnimValueRef.current.setValue(targetValue);
106
- dropDownAnimValueRef.current.setValue(targetValue);
107
- return;
108
- }
109
- const config = {
110
- ...animationConfig,
111
- toValue: targetValue,
112
- };
113
- Animated.parallel([
114
- Animated.timing(rotateAnimValueRef.current, config),
115
- Animated.timing(dropDownAnimValueRef.current, config),
116
- ]).start();
117
- }, [collapsed, shouldAnimate, animationConfig]);
118
- // Memoize callbacks
119
- const handleToggle = useCallback(() => {
120
- setCollapsed(!collapsed);
121
- }, [collapsed]);
122
- const handleItemPress = useCallback((body) => {
123
- onPressItem?.(data.title, body);
124
- }, [onPressItem, data.title]);
125
- const handleLayout = useCallback((e) => {
126
- setItemHeight(e.nativeEvent.layout.height);
127
- }, []);
128
- // Calculate margin based on size
129
- const toggleMargin = typeof size === 'number'
31
+ ? 56
32
+ : 48;
33
+ const vertical = numeric
34
+ ? size * 0.4
35
+ : size === 'small'
36
+ ? 6
37
+ : size === 'large'
38
+ ? 10
39
+ : 8;
40
+ const horizontal = numeric
130
41
  ? size * 0.6
131
42
  : size === 'small'
132
43
  ? 10
133
44
  : size === 'large'
134
45
  ? 14
135
46
  : 12;
136
- // Memoize toggle element container
137
- const toggleElContainer = useMemo(() => (_jsx(Animated.View, { style: [
138
- css `
139
- margin-right: ${toggleElementPosition === 'left'
140
- ? toggleMargin
141
- : 0}px;
142
- `,
143
- {
144
- transform: [
145
- {
146
- rotate: rotateAnimValueRef.current.interpolate({
147
- inputRange: [0, 1],
148
- outputRange: ['0deg', '180deg'],
149
- }),
150
- },
151
- ],
152
- },
153
- styles?.toggleElement,
154
- ], children: toggleElement || defaultToggleElement })), [
155
- toggleElement,
156
- defaultToggleElement,
157
- toggleElementPosition,
158
- toggleMargin,
159
- styles?.toggleElement,
160
- ]);
161
- // Memoize container styles
162
- const containerStyles = useMemo(() => [
163
- css `
164
- background-color: transparent;
165
- overflow: hidden;
166
- flex-direction: column-reverse;
167
- `,
168
- styles?.container,
169
- ], [styles?.container]);
170
- const titleContainerStyles = useMemo(() => [
171
- css `
172
- justify-content: ${toggleElementPosition === 'right'
173
- ? 'space-between'
174
- : 'flex-start'};
175
- `,
176
- styles?.titleContainer,
177
- ], [toggleElementPosition, styles?.titleContainer]);
178
- // Memoize animated styles
179
- const animatedItemStyles = useMemo(() => [
180
- {
181
- opacity: fadeItemAnim,
182
- height: dropDownAnimValueRef.current.interpolate({
183
- inputRange: [0, 1],
184
- outputRange: [0, itemHeight],
185
- }),
186
- },
187
- ], [fadeItemAnim, itemHeight]);
188
- // Calculate font sizes based on size prop
189
- const titleFontSize = typeof size === 'number'
47
+ const titleFontSize = numeric
190
48
  ? size
191
49
  : size === 'small'
192
50
  ? 14
193
51
  : size === 'large'
194
52
  ? 18
195
53
  : 16;
196
- const itemFontSize = typeof size === 'number'
54
+ const itemFontSize = numeric
197
55
  ? size * 0.875
198
56
  : size === 'small'
199
57
  ? 12
200
58
  : size === 'large'
201
59
  ? 16
202
60
  : 14;
203
- return (_jsxs(Animated.View, { style: containerStyles, children: [_jsx(View, { accessibilityElementsHidden: true, "aria-hidden": true, importantForAccessibility: "no-hide-descendants", onLayout: handleLayout, style: css `
204
- position: absolute;
205
- opacity: 0;
206
- `, testID: `measure-body-${testID}`, children: data.items.map((body, index) => (_jsx(ItemTouch, { accessible: false, activeOpacity: activeOpacity, disabled: true, onPress: () => handleItemPress(body), "$size": size, children: typeof body === 'string' && !renderItem ? (_jsx(Typography.Body3, { style: [{ fontSize: itemFontSize }, styles?.itemText], children: body })) : (renderItem?.(body)) }, `body-${index}`))) }), _jsx(Animated.View, { accessibilityElementsHidden: collapsed, accessibilityState: { expanded: !collapsed }, "aria-hidden": collapsed, importantForAccessibility: collapsed ? 'no-hide-descendants' : 'auto', style: animatedItemStyles, testID: `body-${testID}`, children: data.items.map((body, index) => (_jsx(ItemTouch, { activeOpacity: activeOpacity, disabled: collapsed, onPress: () => handleItemPress(body), style: styles?.itemContainer, "$size": size, children: typeof body === 'string' && !renderItem ? (_jsx(Typography.Body3, { style: [{ fontSize: itemFontSize }, styles?.itemText], children: body })) : (renderItem?.(body)) }, `body-${index}`))) }), _jsxs(TitleTouch, { activeOpacity: activeOpacity, "aria-expanded": !collapsed, onPress: handleToggle, accessibilityRole: "button", accessibilityLabel: typeof data.title === 'string' ? data.title : 'Accordion section', accessibilityState: { expanded: !collapsed }, accessibilityHint: collapsed ? 'Double tap to expand' : 'Double tap to collapse', style: titleContainerStyles, testID: `title-${testID}`, "$size": size, children: [toggleElementPosition === 'left' ? toggleElContainer : null, typeof data.title === 'string' && !renderTitle ? (_jsx(Typography.Heading4, { style: [{ fontSize: titleFontSize }, styles?.titleText], children: data.title })) : (renderTitle?.(data.title)), toggleElementPosition === 'right' ? toggleElContainer : null] })] }));
61
+ const iconSize = numeric
62
+ ? size * 0.7
63
+ : size === 'small'
64
+ ? 12
65
+ : size === 'large'
66
+ ? 16
67
+ : 14;
68
+ const toggleView = (_jsx(Animated.View, { style: [
69
+ {
70
+ marginRight: toggleElementPosition === 'left' ? horizontal : 0,
71
+ transform: [{ rotate: expanded ? '180deg' : '0deg' }],
72
+ transitionProperty: 'transform',
73
+ transitionDuration: duration,
74
+ transitionTimingFunction: 'ease-out',
75
+ },
76
+ styles?.toggleElement,
77
+ ], children: toggleElement === undefined ? (_jsx(Icon, { name: "CaretDown", color: theme.text.basic, size: iconSize })) : (toggleElement) }));
78
+ return (_jsxs(Animated.View, { collapsable: false, layout: transition, style: [layout.container, styles?.container], children: [renderHeader ? (renderHeader({ title: data.title, expanded, toggle })) : (_jsxs(TouchableOpacity, { activeOpacity: activeOpacity, "aria-expanded": expanded, accessibilityRole: "button", accessibilityLabel: typeof data.title === 'string' ? data.title : 'Accordion section', accessibilityState: { expanded }, accessibilityHint: expanded ? 'Double tap to collapse' : 'Double tap to expand', onPress: toggle, style: [
79
+ layout.title,
80
+ {
81
+ minHeight: titleHeight,
82
+ paddingVertical: vertical,
83
+ paddingHorizontal: horizontal,
84
+ backgroundColor: theme.bg.basic,
85
+ justifyContent: toggleElementPosition === 'right'
86
+ ? 'space-between'
87
+ : 'flex-start',
88
+ },
89
+ styles?.titleContainer,
90
+ ], testID: `title-${testID}`, children: [toggleElementPosition === 'left' ? toggleView : null, renderTitle ? (renderTitle(data.title)) : typeof data.title === 'string' ? (_jsx(Typography.Heading4, { style: [
91
+ { fontSize: titleFontSize, flexShrink: 1 },
92
+ styles?.titleText,
93
+ ], children: data.title })) : null, toggleElementPosition === 'right' ? toggleView : null] })), _jsx(Animated.View, { collapsable: false, layout: transition, accessibilityElementsHidden: !expanded, accessibilityState: { expanded }, "aria-hidden": !expanded, importantForAccessibility: expanded ? 'auto' : 'no-hide-descendants', pointerEvents: expanded ? 'auto' : 'none', style: [
94
+ layout.body,
95
+ {
96
+ height: expanded ? undefined : 0,
97
+ opacity: expanded ? 1 : 0,
98
+ transitionProperty: 'opacity',
99
+ transitionDuration: duration,
100
+ transitionTimingFunction: 'ease-out',
101
+ },
102
+ ], testID: `body-${testID}`, children: expanded ? (_jsx(Animated.View, { collapsable: false, entering: entering, exiting: exiting, children: data.items.map((body, index) => {
103
+ const content = renderItem ? (renderItem(body)) : typeof body === 'string' ? (_jsx(Typography.Body3, { style: [{ fontSize: itemFontSize }, styles?.itemText], children: body })) : null;
104
+ const itemStyle = [
105
+ {
106
+ backgroundColor: theme.bg.paper,
107
+ paddingVertical: vertical,
108
+ paddingHorizontal: horizontal,
109
+ },
110
+ styles?.itemContainer,
111
+ ];
112
+ // Do not wrap custom controls in a second pressable.
113
+ return onPressItem ? (_jsx(TouchableOpacity, { activeOpacity: activeOpacity, disabled: !expanded, onPress: () => onPressItem(data.title, body), style: itemStyle, children: content }, index)) : (_jsx(View, { style: itemStyle, children: content }, index));
114
+ }) })) : null })] }));
207
115
  }
208
- // Export memoized component for better performance
116
+ const layout = StyleSheet.create({
117
+ container: { overflow: 'hidden', flexDirection: 'column' },
118
+ title: { flexDirection: 'row', alignItems: 'center' },
119
+ body: { overflow: 'hidden' },
120
+ });
209
121
  export default React.memo(AccordionItem);