@ankhorage/surface 5.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -3
  3. package/dist/components/field/Field.d.ts +1 -0
  4. package/dist/components/field/Field.d.ts.map +1 -1
  5. package/dist/components/field/Field.js +5 -4
  6. package/dist/components/field/Field.js.map +1 -1
  7. package/dist/components/list-item/ListItem.d.ts +1 -0
  8. package/dist/components/list-item/ListItem.d.ts.map +1 -1
  9. package/dist/components/list-item/ListItem.js +13 -12
  10. package/dist/components/list-item/ListItem.js.map +1 -1
  11. package/dist/components/switch/Switch.d.ts +1 -0
  12. package/dist/components/switch/Switch.d.ts.map +1 -1
  13. package/dist/components/switch/Switch.js +10 -13
  14. package/dist/components/switch/Switch.js.map +1 -1
  15. package/dist/{layout → core/responsive}/Show.d.ts +2 -1
  16. package/dist/core/responsive/Show.d.ts.map +1 -0
  17. package/dist/{layout → core/responsive}/Show.js +3 -1
  18. package/dist/core/responsive/Show.js.map +1 -0
  19. package/dist/core/responsive/index.d.ts +1 -0
  20. package/dist/core/responsive/index.d.ts.map +1 -1
  21. package/dist/core/responsive/index.js +1 -0
  22. package/dist/core/responsive/index.js.map +1 -1
  23. package/dist/features/app-bar/adapters/inbound/AppBar.d.ts +5 -0
  24. package/dist/features/app-bar/adapters/inbound/AppBar.d.ts.map +1 -0
  25. package/dist/features/app-bar/adapters/inbound/AppBar.js +40 -0
  26. package/dist/features/app-bar/adapters/inbound/AppBar.js.map +1 -0
  27. package/dist/features/app-bar/public.d.ts +3 -0
  28. package/dist/features/app-bar/public.d.ts.map +1 -0
  29. package/dist/features/app-bar/public.js +2 -0
  30. package/dist/features/app-bar/public.js.map +1 -0
  31. package/dist/features/badge/adapters/inbound/Badge.js +3 -3
  32. package/dist/features/badge/adapters/inbound/Badge.js.map +1 -1
  33. package/dist/features/button/adapters/inbound/Button.js +8 -8
  34. package/dist/features/button/adapters/inbound/Button.js.map +1 -1
  35. package/dist/features/button/adapters/inbound/IconButton.js +3 -3
  36. package/dist/features/button/adapters/inbound/IconButton.js.map +1 -1
  37. package/dist/features/form/checkbox/adapters/inbound/Checkbox.js +7 -7
  38. package/dist/features/form/checkbox/adapters/inbound/Checkbox.js.map +1 -1
  39. package/dist/features/form/radio/adapters/inbound/Radio.js +8 -8
  40. package/dist/features/form/radio/adapters/inbound/Radio.js.map +1 -1
  41. package/dist/features/layout/adapters/inbound/Divider.js +2 -2
  42. package/dist/features/layout/adapters/inbound/Divider.js.map +1 -1
  43. package/dist/features/layout/adapters/inbound/Grid.d.ts.map +1 -1
  44. package/dist/features/layout/adapters/inbound/Grid.js +7 -9
  45. package/dist/features/layout/adapters/inbound/Grid.js.map +1 -1
  46. package/dist/features/layout/adapters/inbound/ScrollView.d.ts +5 -0
  47. package/dist/features/layout/adapters/inbound/ScrollView.d.ts.map +1 -0
  48. package/dist/features/layout/adapters/inbound/ScrollView.js +69 -0
  49. package/dist/features/layout/adapters/inbound/ScrollView.js.map +1 -0
  50. package/dist/features/layout/adapters/inbound/View.d.ts +5 -0
  51. package/dist/features/layout/adapters/inbound/View.d.ts.map +1 -0
  52. package/dist/features/layout/adapters/inbound/View.js +17 -0
  53. package/dist/features/layout/adapters/inbound/View.js.map +1 -0
  54. package/dist/features/layout/public.d.ts +3 -4
  55. package/dist/features/layout/public.d.ts.map +1 -1
  56. package/dist/features/layout/public.js +2 -3
  57. package/dist/features/layout/public.js.map +1 -1
  58. package/dist/{components/modal → features/modal/adapters/inbound}/Modal.d.ts +2 -1
  59. package/dist/features/modal/adapters/inbound/Modal.d.ts.map +1 -0
  60. package/dist/features/modal/adapters/inbound/Modal.js +51 -0
  61. package/dist/features/modal/adapters/inbound/Modal.js.map +1 -0
  62. package/dist/features/modal/public.d.ts +3 -0
  63. package/dist/features/modal/public.d.ts.map +1 -0
  64. package/dist/features/modal/public.js +2 -0
  65. package/dist/features/modal/public.js.map +1 -0
  66. package/dist/features/popover-menu/adapters/inbound/PopoverMenu.d.ts.map +1 -1
  67. package/dist/features/popover-menu/adapters/inbound/PopoverMenu.js +11 -12
  68. package/dist/features/popover-menu/adapters/inbound/PopoverMenu.js.map +1 -1
  69. package/dist/features/surface/adapters/inbound/Surface.d.ts +1 -1
  70. package/dist/features/surface/adapters/inbound/Surface.d.ts.map +1 -1
  71. package/dist/features/surface/adapters/inbound/Surface.js +3 -3
  72. package/dist/features/surface/adapters/inbound/Surface.js.map +1 -1
  73. package/dist/features/tabs/adapters/inbound/Tab.d.ts +5 -0
  74. package/dist/features/tabs/adapters/inbound/Tab.d.ts.map +1 -0
  75. package/dist/features/tabs/adapters/inbound/Tab.js +33 -0
  76. package/dist/features/tabs/adapters/inbound/Tab.js.map +1 -0
  77. package/dist/features/tabs/adapters/inbound/TabList.d.ts +5 -0
  78. package/dist/features/tabs/adapters/inbound/TabList.d.ts.map +1 -0
  79. package/dist/features/tabs/adapters/inbound/TabList.js +40 -0
  80. package/dist/features/tabs/adapters/inbound/TabList.js.map +1 -0
  81. package/dist/features/tabs/adapters/inbound/TabPanel.d.ts +5 -0
  82. package/dist/features/tabs/adapters/inbound/TabPanel.d.ts.map +1 -0
  83. package/dist/features/tabs/adapters/inbound/TabPanel.js +14 -0
  84. package/dist/features/tabs/adapters/inbound/TabPanel.js.map +1 -0
  85. package/dist/features/tabs/adapters/inbound/Tabs.d.ts +5 -0
  86. package/dist/features/tabs/adapters/inbound/Tabs.d.ts.map +1 -0
  87. package/dist/features/tabs/adapters/inbound/Tabs.js +12 -0
  88. package/dist/features/tabs/adapters/inbound/Tabs.js.map +1 -0
  89. package/dist/features/tabs/composition/TabsContext.d.ts +4 -0
  90. package/dist/features/tabs/composition/TabsContext.d.ts.map +1 -0
  91. package/dist/features/tabs/composition/TabsContext.js +3 -0
  92. package/dist/features/tabs/composition/TabsContext.js.map +1 -0
  93. package/dist/features/tabs/composition/useTabRegistration.d.ts +7 -0
  94. package/dist/features/tabs/composition/useTabRegistration.d.ts.map +1 -0
  95. package/dist/features/tabs/composition/useTabRegistration.js +20 -0
  96. package/dist/features/tabs/composition/useTabRegistration.js.map +1 -0
  97. package/dist/features/tabs/composition/useTabsContext.d.ts +4 -0
  98. package/dist/features/tabs/composition/useTabsContext.d.ts.map +1 -0
  99. package/dist/{components/tabs/context.js → features/tabs/composition/useTabsContext.js} +4 -4
  100. package/dist/features/tabs/composition/useTabsContext.js.map +1 -0
  101. package/dist/features/tabs/composition/useTabsController.d.ts +4 -0
  102. package/dist/features/tabs/composition/useTabsController.d.ts.map +1 -0
  103. package/dist/features/tabs/composition/useTabsController.js +43 -0
  104. package/dist/features/tabs/composition/useTabsController.js.map +1 -0
  105. package/dist/features/tabs/public.d.ts +6 -0
  106. package/dist/features/tabs/public.d.ts.map +1 -0
  107. package/dist/features/tabs/public.js +5 -0
  108. package/dist/features/tabs/public.js.map +1 -0
  109. package/dist/features/tabs/utils/resolveNextTabValue.d.ts +4 -0
  110. package/dist/features/tabs/utils/resolveNextTabValue.d.ts.map +1 -0
  111. package/dist/{components/tabs/navigation.js → features/tabs/utils/resolveNextTabValue.js} +10 -13
  112. package/dist/features/tabs/utils/resolveNextTabValue.js.map +1 -0
  113. package/dist/features/tabs/utils/resolveTabElementId.d.ts +3 -0
  114. package/dist/features/tabs/utils/resolveTabElementId.d.ts.map +1 -0
  115. package/dist/features/tabs/utils/resolveTabElementId.js +10 -0
  116. package/dist/features/tabs/utils/resolveTabElementId.js.map +1 -0
  117. package/dist/features/toast/adapters/inbound/Toast.js +5 -5
  118. package/dist/features/toast/adapters/inbound/Toast.js.map +1 -1
  119. package/dist/features/toast/adapters/inbound/ToastProvider.js +3 -3
  120. package/dist/features/toast/adapters/inbound/ToastProvider.js.map +1 -1
  121. package/dist/index.d.ts +8 -7
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +4 -4
  124. package/dist/index.js.map +1 -1
  125. package/dist/primitives/button-base/ButtonBase.d.ts +1 -0
  126. package/dist/primitives/button-base/ButtonBase.d.ts.map +1 -1
  127. package/dist/primitives/button-base/ButtonBase.js +6 -5
  128. package/dist/primitives/button-base/ButtonBase.js.map +1 -1
  129. package/dist/primitives/button-base/types.d.ts +2 -2
  130. package/dist/primitives/button-base/types.d.ts.map +1 -1
  131. package/dist/primitives/button-base/types.js.map +1 -1
  132. package/dist/types/app-bar.d.ts +12 -0
  133. package/dist/types/app-bar.d.ts.map +1 -0
  134. package/dist/types/app-bar.js +2 -0
  135. package/dist/types/app-bar.js.map +1 -0
  136. package/dist/types/layout.d.ts +15 -17
  137. package/dist/types/layout.d.ts.map +1 -1
  138. package/dist/types/layout.js.map +1 -1
  139. package/dist/{components/modal/types.d.ts → types/modal.d.ts} +2 -2
  140. package/dist/types/modal.d.ts.map +1 -0
  141. package/dist/types/modal.js +2 -0
  142. package/dist/types/modal.js.map +1 -0
  143. package/dist/types/surface.d.ts +2 -2
  144. package/dist/types/surface.d.ts.map +1 -1
  145. package/dist/types/surface.js.map +1 -1
  146. package/dist/types/tabs.d.ts +43 -0
  147. package/dist/types/tabs.d.ts.map +1 -0
  148. package/dist/types/tabs.js +2 -0
  149. package/dist/types/tabs.js.map +1 -0
  150. package/dist/utils/resolveViewStyles.d.ts +7 -0
  151. package/dist/utils/resolveViewStyles.d.ts.map +1 -0
  152. package/dist/utils/{resolveBoxStyles.js → resolveViewStyles.js} +11 -4
  153. package/dist/utils/resolveViewStyles.js.map +1 -0
  154. package/package.json +1 -1
  155. package/src/components/field/Field.tsx +5 -4
  156. package/src/components/list-item/ListItem.tsx +17 -13
  157. package/src/components/switch/Switch.tsx +10 -15
  158. package/src/{layout → core/responsive}/Show.tsx +4 -1
  159. package/src/core/responsive/index.ts +1 -0
  160. package/src/features/app-bar/adapters/inbound/AppBar.tsx +69 -0
  161. package/src/features/app-bar/public.ts +2 -0
  162. package/src/features/badge/adapters/inbound/Badge.tsx +3 -3
  163. package/src/features/button/adapters/inbound/Button.tsx +8 -8
  164. package/src/features/button/adapters/inbound/IconButton.tsx +3 -3
  165. package/src/features/form/checkbox/adapters/inbound/Checkbox.tsx +7 -7
  166. package/src/features/form/radio/adapters/inbound/Radio.tsx +8 -8
  167. package/src/features/layout/adapters/inbound/Divider.tsx +2 -2
  168. package/src/features/layout/adapters/inbound/Grid.tsx +12 -14
  169. package/src/features/layout/adapters/inbound/ScrollView.tsx +102 -0
  170. package/src/features/layout/adapters/inbound/{Box.tsx → View.tsx} +9 -9
  171. package/src/features/layout/public.ts +3 -10
  172. package/src/features/modal/adapters/inbound/Modal.tsx +77 -0
  173. package/src/features/modal/public.ts +2 -0
  174. package/src/features/popover-menu/adapters/inbound/PopoverMenu.tsx +11 -14
  175. package/src/features/surface/adapters/inbound/Surface.tsx +3 -3
  176. package/src/features/tabs/adapters/inbound/Tab.tsx +67 -0
  177. package/src/features/tabs/adapters/inbound/TabList.tsx +47 -0
  178. package/src/{components/tabs → features/tabs/adapters/inbound}/TabPanel.tsx +6 -10
  179. package/src/features/tabs/adapters/inbound/Tabs.tsx +17 -0
  180. package/src/features/tabs/composition/TabsContext.tsx +5 -0
  181. package/src/features/tabs/composition/useTabRegistration.ts +27 -0
  182. package/src/features/tabs/composition/useTabsContext.ts +13 -0
  183. package/src/features/tabs/composition/useTabsController.ts +55 -0
  184. package/src/features/tabs/public.ts +5 -0
  185. package/src/{components/tabs/navigation.test.ts → features/tabs/utils/resolveNextTabValue.test.ts} +2 -2
  186. package/src/features/tabs/utils/resolveNextTabValue.ts +24 -0
  187. package/src/features/tabs/utils/resolveTabElementId.test.ts +19 -0
  188. package/src/features/tabs/utils/resolveTabElementId.ts +14 -0
  189. package/src/features/toast/adapters/inbound/Toast.tsx +5 -5
  190. package/src/features/toast/adapters/inbound/ToastProvider.tsx +3 -3
  191. package/src/index.test.ts +14 -38
  192. package/src/index.ts +8 -13
  193. package/src/primitives/button-base/ButtonBase.tsx +6 -8
  194. package/src/primitives/button-base/types.ts +2 -2
  195. package/src/types/app-bar.ts +13 -0
  196. package/src/types/layout.ts +25 -20
  197. package/src/{components/modal/types.ts → types/modal.ts} +1 -1
  198. package/src/types/surface.ts +2 -2
  199. package/src/types/tabs.ts +51 -0
  200. package/src/utils/resolveViewStyles.test.ts +71 -0
  201. package/src/utils/{resolveBoxStyles.ts → resolveViewStyles.ts} +16 -9
  202. package/src/webAcceptance.test.tsx +8 -9
  203. package/dist/components/modal/Modal.d.ts.map +0 -1
  204. package/dist/components/modal/Modal.js +0 -59
  205. package/dist/components/modal/Modal.js.map +0 -1
  206. package/dist/components/modal/index.d.ts +0 -3
  207. package/dist/components/modal/index.d.ts.map +0 -1
  208. package/dist/components/modal/index.js +0 -2
  209. package/dist/components/modal/index.js.map +0 -1
  210. package/dist/components/modal/types.d.ts.map +0 -1
  211. package/dist/components/modal/types.js +0 -2
  212. package/dist/components/modal/types.js.map +0 -1
  213. package/dist/components/tabs/Tab.d.ts +0 -4
  214. package/dist/components/tabs/Tab.d.ts.map +0 -1
  215. package/dist/components/tabs/Tab.js +0 -48
  216. package/dist/components/tabs/Tab.js.map +0 -1
  217. package/dist/components/tabs/TabList.d.ts +0 -4
  218. package/dist/components/tabs/TabList.d.ts.map +0 -1
  219. package/dist/components/tabs/TabList.js +0 -37
  220. package/dist/components/tabs/TabList.js.map +0 -1
  221. package/dist/components/tabs/TabPanel.d.ts +0 -4
  222. package/dist/components/tabs/TabPanel.d.ts.map +0 -1
  223. package/dist/components/tabs/TabPanel.js +0 -16
  224. package/dist/components/tabs/TabPanel.js.map +0 -1
  225. package/dist/components/tabs/Tabs.d.ts +0 -4
  226. package/dist/components/tabs/Tabs.d.ts.map +0 -1
  227. package/dist/components/tabs/Tabs.js +0 -54
  228. package/dist/components/tabs/Tabs.js.map +0 -1
  229. package/dist/components/tabs/a11y.d.ts +0 -3
  230. package/dist/components/tabs/a11y.d.ts.map +0 -1
  231. package/dist/components/tabs/a11y.js +0 -13
  232. package/dist/components/tabs/a11y.js.map +0 -1
  233. package/dist/components/tabs/context.d.ts +0 -20
  234. package/dist/components/tabs/context.d.ts.map +0 -1
  235. package/dist/components/tabs/context.js.map +0 -1
  236. package/dist/components/tabs/index.d.ts +0 -6
  237. package/dist/components/tabs/index.d.ts.map +0 -1
  238. package/dist/components/tabs/index.js +0 -5
  239. package/dist/components/tabs/index.js.map +0 -1
  240. package/dist/components/tabs/navigation.d.ts +0 -3
  241. package/dist/components/tabs/navigation.d.ts.map +0 -1
  242. package/dist/components/tabs/navigation.js.map +0 -1
  243. package/dist/components/tabs/types.d.ts +0 -26
  244. package/dist/components/tabs/types.d.ts.map +0 -1
  245. package/dist/components/tabs/types.js +0 -2
  246. package/dist/components/tabs/types.js.map +0 -1
  247. package/dist/features/layout/adapters/inbound/Box.d.ts +0 -5
  248. package/dist/features/layout/adapters/inbound/Box.d.ts.map +0 -1
  249. package/dist/features/layout/adapters/inbound/Box.js +0 -17
  250. package/dist/features/layout/adapters/inbound/Box.js.map +0 -1
  251. package/dist/features/layout/adapters/inbound/Container.d.ts +0 -5
  252. package/dist/features/layout/adapters/inbound/Container.d.ts.map +0 -1
  253. package/dist/features/layout/adapters/inbound/Container.js +0 -28
  254. package/dist/features/layout/adapters/inbound/Container.js.map +0 -1
  255. package/dist/features/layout/adapters/inbound/Stack.d.ts +0 -5
  256. package/dist/features/layout/adapters/inbound/Stack.d.ts.map +0 -1
  257. package/dist/features/layout/adapters/inbound/Stack.js +0 -27
  258. package/dist/features/layout/adapters/inbound/Stack.js.map +0 -1
  259. package/dist/layout/AppBar.d.ts +0 -13
  260. package/dist/layout/AppBar.d.ts.map +0 -1
  261. package/dist/layout/AppBar.js +0 -30
  262. package/dist/layout/AppBar.js.map +0 -1
  263. package/dist/layout/Center.d.ts +0 -7
  264. package/dist/layout/Center.d.ts.map +0 -1
  265. package/dist/layout/Center.js +0 -12
  266. package/dist/layout/Center.js.map +0 -1
  267. package/dist/layout/Inline.d.ts +0 -5
  268. package/dist/layout/Inline.d.ts.map +0 -1
  269. package/dist/layout/Inline.js +0 -6
  270. package/dist/layout/Inline.js.map +0 -1
  271. package/dist/layout/ScrollArea.d.ts +0 -9
  272. package/dist/layout/ScrollArea.d.ts.map +0 -1
  273. package/dist/layout/ScrollArea.js +0 -52
  274. package/dist/layout/ScrollArea.js.map +0 -1
  275. package/dist/layout/Show.d.ts.map +0 -1
  276. package/dist/layout/Show.js.map +0 -1
  277. package/dist/layout/Spacer.d.ts +0 -9
  278. package/dist/layout/Spacer.d.ts.map +0 -1
  279. package/dist/layout/Spacer.js +0 -12
  280. package/dist/layout/Spacer.js.map +0 -1
  281. package/dist/layout/Template.d.ts +0 -18
  282. package/dist/layout/Template.d.ts.map +0 -1
  283. package/dist/layout/Template.js +0 -36
  284. package/dist/layout/Template.js.map +0 -1
  285. package/dist/layout/index.d.ts +0 -15
  286. package/dist/layout/index.d.ts.map +0 -1
  287. package/dist/layout/index.js +0 -8
  288. package/dist/layout/index.js.map +0 -1
  289. package/dist/utils/resolveBoxStyles.d.ts +0 -7
  290. package/dist/utils/resolveBoxStyles.d.ts.map +0 -1
  291. package/dist/utils/resolveBoxStyles.js.map +0 -1
  292. package/src/components/modal/Modal.tsx +0 -96
  293. package/src/components/modal/index.ts +0 -2
  294. package/src/components/tabs/Tab.tsx +0 -89
  295. package/src/components/tabs/TabList.tsx +0 -51
  296. package/src/components/tabs/Tabs.tsx +0 -67
  297. package/src/components/tabs/a11y.test.ts +0 -15
  298. package/src/components/tabs/a11y.ts +0 -15
  299. package/src/components/tabs/context.tsx +0 -31
  300. package/src/components/tabs/index.ts +0 -5
  301. package/src/components/tabs/navigation.ts +0 -32
  302. package/src/components/tabs/types.ts +0 -30
  303. package/src/features/layout/adapters/inbound/Container.tsx +0 -43
  304. package/src/features/layout/adapters/inbound/Stack.test.ts +0 -13
  305. package/src/features/layout/adapters/inbound/Stack.tsx +0 -44
  306. package/src/layout/AppBar.tsx +0 -60
  307. package/src/layout/Center.tsx +0 -22
  308. package/src/layout/Inline.tsx +0 -9
  309. package/src/layout/ScrollArea.tsx +0 -116
  310. package/src/layout/Spacer.tsx +0 -22
  311. package/src/layout/Template.tsx +0 -86
  312. package/src/layout/index.ts +0 -14
  313. package/src/utils/resolveBoxStyles.test.ts +0 -54
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+
3
+ import { useControllableState } from '../../../internal/useControllableState';
4
+ import type { TabRegistration, TabsContextValue, TabsProps } from '../../../types/tabs';
5
+ import { resolveTabElementId } from '../utils/resolveTabElementId';
6
+
7
+ /*** Owns controlled state, registration, focus state, and ids for one Tabs instance. */
8
+ export function useTabsController({
9
+ defaultValue,
10
+ onValueChange,
11
+ testID,
12
+ value,
13
+ }: Pick<TabsProps, 'defaultValue' | 'onValueChange' | 'testID' | 'value'>): TabsContextValue {
14
+ const [activeValue, setActiveValue] = useControllableState<string | undefined>({
15
+ defaultValue,
16
+ onChange: (nextValue) => {
17
+ if (nextValue !== undefined) onValueChange?.(nextValue);
18
+ },
19
+ value,
20
+ });
21
+ const [focusedValue, setFocusedValue] = React.useState<string | undefined>(undefined);
22
+ const [tabs, setTabs] = React.useState<readonly TabRegistration[]>([]);
23
+
24
+ const registerTab = React.useCallback((tab: TabRegistration) => {
25
+ setTabs((current) =>
26
+ current.some((entry) => entry.value === tab.value)
27
+ ? current.map((entry) => (entry.value === tab.value ? tab : entry))
28
+ : [...current, tab],
29
+ );
30
+ }, []);
31
+ const unregisterTab = React.useCallback((valueToRemove: string) => {
32
+ setTabs((current) => current.filter((entry) => entry.value !== valueToRemove));
33
+ }, []);
34
+
35
+ React.useEffect(() => {
36
+ if (activeValue !== undefined) return;
37
+ const firstEnabledTab = tabs.find((entry) => !entry.disabled);
38
+ if (firstEnabledTab) setActiveValue(firstEnabledTab.value);
39
+ }, [activeValue, setActiveValue, tabs]);
40
+
41
+ return React.useMemo(
42
+ () => ({
43
+ activeValue,
44
+ focusedValue,
45
+ getPanelId: (tabValue: string) => resolveTabElementId('panel', testID, tabValue),
46
+ getTabId: (tabValue: string) => resolveTabElementId('tab', testID, tabValue),
47
+ registerTab,
48
+ setActiveValue: (nextValue: string) => setActiveValue(nextValue),
49
+ setFocusedValue,
50
+ tabs,
51
+ unregisterTab,
52
+ }),
53
+ [activeValue, focusedValue, registerTab, setActiveValue, tabs, testID, unregisterTab],
54
+ );
55
+ }
@@ -0,0 +1,5 @@
1
+ export type { TabListProps, TabPanelProps, TabProps, TabsProps } from '../../types/tabs';
2
+ export { Tab } from './adapters/inbound/Tab';
3
+ export { TabList } from './adapters/inbound/TabList';
4
+ export { TabPanel } from './adapters/inbound/TabPanel';
5
+ export { Tabs } from './adapters/inbound/Tabs';
@@ -1,12 +1,12 @@
1
1
  import { describe, expect, it } from 'bun:test';
2
2
 
3
- import { resolveNextTabValue } from './navigation';
3
+ import { resolveNextTabValue } from './resolveNextTabValue';
4
4
 
5
5
  const tabs = [
6
6
  { disabled: false, focus: () => undefined, value: 'overview' },
7
7
  { disabled: true, focus: () => undefined, value: 'disabled' },
8
8
  { disabled: false, focus: () => undefined, value: 'details' },
9
- ];
9
+ ] as const;
10
10
 
11
11
  describe('resolveNextTabValue', () => {
12
12
  it('skips disabled tabs when cycling', () => {
@@ -0,0 +1,24 @@
1
+ import type { TabNavigationKey, TabRegistration } from '../../../types/tabs';
2
+
3
+ /*** Resolves the next enabled tab for keyboard focus navigation. */
4
+ export function resolveNextTabValue(
5
+ tabs: readonly TabRegistration[],
6
+ currentValue: string | undefined,
7
+ key: TabNavigationKey,
8
+ ): string | undefined {
9
+ const enabledTabs = tabs.filter((tab) => !tab.disabled);
10
+ if (enabledTabs.length === 0) return undefined;
11
+
12
+ if (key === 'Home') return enabledTabs.at(0)?.value;
13
+ if (key === 'End') return enabledTabs.at(-1)?.value;
14
+
15
+ const currentIndex = enabledTabs.findIndex((tab) => tab.value === currentValue);
16
+ if (currentIndex === -1) return enabledTabs.at(0)?.value;
17
+
18
+ const nextIndex =
19
+ key === 'ArrowLeft' || key === 'ArrowUp'
20
+ ? (currentIndex - 1 + enabledTabs.length) % enabledTabs.length
21
+ : (currentIndex + 1) % enabledTabs.length;
22
+
23
+ return enabledTabs.at(nextIndex)?.value;
24
+ }
@@ -0,0 +1,19 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+
3
+ import { resolveTabElementId } from './resolveTabElementId';
4
+
5
+ describe('resolveTabElementId', () => {
6
+ it('creates stable ids from tab values', () => {
7
+ expect(resolveTabElementId('tab', undefined, 'Account Settings')).toBe(
8
+ 'tabs-tab-Account-Settings',
9
+ );
10
+ expect(resolveTabElementId('panel', undefined, 'Account Settings')).toBe(
11
+ 'tabs-panel-Account-Settings',
12
+ );
13
+ });
14
+
15
+ it('namespaces ids by tabs testID when provided', () => {
16
+ expect(resolveTabElementId('tab', 'settings', 'billing')).toBe('settings-tabs-tab-billing');
17
+ expect(resolveTabElementId('panel', 'settings', 'billing')).toBe('settings-tabs-panel-billing');
18
+ });
19
+ });
@@ -0,0 +1,14 @@
1
+ /*** Creates a stable tab or tab-panel id from a Tabs test id and tab value. */
2
+ export function resolveTabElementId(
3
+ kind: 'tab' | 'panel',
4
+ testID: string | undefined,
5
+ value: string,
6
+ ): string {
7
+ const base = testID ? `${testID}-tabs` : 'tabs';
8
+ return `${base}-${kind}-${sanitizeTabValue(value)}`;
9
+ }
10
+
11
+ /*** Normalizes a tab value for use inside an accessibility id. */
12
+ function sanitizeTabValue(value: string): string {
13
+ return value.trim().replace(/[^a-zA-Z0-9_-]+/g, '-');
14
+ }
@@ -5,7 +5,7 @@ import type { SurfaceColor } from '../../../../surfaceColor';
5
5
  import { useTheme } from '../../../../theme/ThemeContext';
6
6
  import type { SurfaceTheme } from '../../../../theme/types';
7
7
  import type { ToastProps } from '../../../../types/toast';
8
- import { Box, Stack } from '../../../layout/public';
8
+ import { View } from '../../../layout/public';
9
9
  import { Surface } from '../../../surface/public';
10
10
  import { Text } from '../../../typography/public';
11
11
 
@@ -29,15 +29,15 @@ export function Toast({
29
29
 
30
30
  return (
31
31
  <Surface p="m" style={[styles.root, statusStyle]} testID={testID} variant="raised">
32
- <Stack align="center" direction="row" justify="space-between">
33
- <Box flex={1}>
32
+ <View align="center" direction="row" justify="space-between">
33
+ <View flex={1}>
34
34
  {title ? (
35
35
  <Text variant="label" weight="medium">
36
36
  {title}
37
37
  </Text>
38
38
  ) : null}
39
39
  {description ? <Text emphasis="muted">{description}</Text> : null}
40
- </Box>
40
+ </View>
41
41
  {onDismiss ? (
42
42
  <Pressable
43
43
  accessibilityLabel="Dismiss notification"
@@ -48,7 +48,7 @@ export function Toast({
48
48
  <Text color={statusTextColor}>×</Text>
49
49
  </Pressable>
50
50
  ) : null}
51
- </Stack>
51
+ </View>
52
52
  </Surface>
53
53
  );
54
54
  }
@@ -4,7 +4,7 @@ import { StyleSheet, type ViewStyle } from 'react-native';
4
4
  import { Portal } from '../../../../internal/overlay/Portal';
5
5
  import { resolveOverlayAnimation } from '../../../../internal/resolvers';
6
6
  import type { ToastProviderProps } from '../../../../types/toast';
7
- import { Stack } from '../../../layout/public';
7
+ import { View } from '../../../layout/public';
8
8
  import { ToastContext } from '../../composition/ToastContext';
9
9
  import { useToastRuntime } from '../../composition/useToastRuntime';
10
10
  import { Toast } from './Toast';
@@ -22,7 +22,7 @@ export function ToastProvider({ children, defaultDuration = 4000 }: ToastProvide
22
22
  <ToastContext value={controller}>
23
23
  {children}
24
24
  <Portal layer="toast" visible={toasts.length > 0}>
25
- <Stack gap="s" pointerEvents="box-none" style={[styles.container, animationStyle]}>
25
+ <View gap="s" pointerEvents="box-none" style={[styles.container, animationStyle]}>
26
26
  {toasts.map((toast) => (
27
27
  <Toast
28
28
  description={toast.description}
@@ -33,7 +33,7 @@ export function ToastProvider({ children, defaultDuration = 4000 }: ToastProvide
33
33
  title={toast.title}
34
34
  />
35
35
  ))}
36
- </Stack>
36
+ </View>
37
37
  </Portal>
38
38
  </ToastContext>
39
39
  );
package/src/index.test.ts CHANGED
@@ -6,7 +6,6 @@ import type { SelectionSemantics, SurfaceColorDiagnostics } from './index';
6
6
  import { createTheme } from './theme/createTheme';
7
7
 
8
8
  const indexSource = readFileSync(new URL('./index.ts', import.meta.url), 'utf8');
9
- const layoutIndexSource = readFileSync(new URL('./layout/index.ts', import.meta.url), 'utf8');
10
9
  const packageJson = JSON.parse(
11
10
  readFileSync(new URL('../package.json', import.meta.url), 'utf8'),
12
11
  ) as {
@@ -18,6 +17,7 @@ const packageJson = JSON.parse(
18
17
  };
19
18
 
20
19
  const expectedFeatureRootExports = [
20
+ "export { AppBar } from './features/app-bar/public';",
21
21
  "export { Badge } from './features/badge/public';",
22
22
  "export { Button, IconButton } from './features/button/public';",
23
23
  "export { Card } from './features/card/public';",
@@ -27,10 +27,12 @@ const expectedFeatureRootExports = [
27
27
  "export { Icon, SUPPORTED_ICON_PROVIDERS } from './features/icon/public';",
28
28
  "export { Image } from './features/image/public';",
29
29
  "export { KeyboardAvoidingView } from './features/keyboard-avoiding-view/public';",
30
- "export { Box, Container, Divider, Grid, Stack } from './features/layout/public';",
30
+ "export { Divider, Grid, ScrollView, View } from './features/layout/public';",
31
+ "export { Modal } from './features/modal/public';",
31
32
  "export { PopoverMenu } from './features/popover-menu/public';",
32
33
  "export { Popover } from './features/popover/public';",
33
34
  "export { Surface } from './features/surface/public';",
35
+ "export { Tab, TabList, TabPanel, Tabs } from './features/tabs/public';",
34
36
  "export { Tooltip } from './features/tooltip/public';",
35
37
  "export { Toast, ToastProvider, useToast } from './features/toast/public';",
36
38
  "export { Heading, Text } from './features/typography/public';",
@@ -41,13 +43,10 @@ const expectedLegacyRootExports = [
41
43
  "export { HelperText } from './components/helper-text';",
42
44
  "export { Label } from './components/label';",
43
45
  "export { ListItem } from './components/list-item';",
44
- "export { Modal } from './components/modal';",
45
46
  "export { Switch } from './components/switch';",
46
- "export { Tab, TabList, TabPanel, Tabs } from './components/tabs';",
47
47
  "export { Textarea } from './components/textarea';",
48
48
  "export type { InteractionPolicy, InteractionPolicyProps } from './interactionPolicy';",
49
49
  "export * from './core/responsive';",
50
- "export * from './layout';",
51
50
  ] as const;
52
51
 
53
52
  describe('feature-owned root barrel contract', () => {
@@ -55,39 +54,16 @@ describe('feature-owned root barrel contract', () => {
55
54
  expectedFeatureRootExports.forEach((line) => expect(indexSource).toContain(line));
56
55
  });
57
56
 
58
- it('does not retain migrated UI exports from legacy component or primitive paths', () => {
59
- for (const legacyPath of [
60
- './components/badge',
61
- './components/button',
62
- './components/card',
63
- './components/checkbox',
64
- './components/icon-button',
65
- './components/menu',
66
- './components/radio',
67
- './components/text-input',
68
- './components/toast',
69
- './components/tooltip',
70
- './primitives/heading',
71
- './primitives/icon',
72
- './primitives/image',
73
- './primitives/text',
74
- ]) {
75
- expect(indexSource).not.toContain(legacyPath);
76
- }
77
- });
78
-
79
- it('removes migrated elements from the legacy layout facade', () => {
80
- for (const name of [
81
- 'Box',
82
- 'Container',
83
- 'Divider',
84
- 'Grid',
85
- 'KeyboardAvoidingView',
86
- 'Stack',
87
- 'Surface',
88
- ]) {
89
- expect(layoutIndexSource).not.toContain(`./${name}`);
90
- }
57
+ it('does not retain removed layout aliases or a legacy layout facade', () => {
58
+ expect(indexSource).not.toContain("'./layout");
59
+ expect(indexSource).not.toMatch(/\bBoxProps\b/u);
60
+ expect(indexSource).not.toMatch(/\bContainerProps\b/u);
61
+ expect(indexSource).not.toMatch(/\bStackProps\b/u);
62
+ expect(indexSource).not.toMatch(/\bScrollAreaProps\b/u);
63
+ expect(indexSource).not.toMatch(/export\s*\{[^}]*\bBox\b/u);
64
+ expect(indexSource).not.toMatch(/export\s*\{[^}]*\bContainer\b/u);
65
+ expect(indexSource).not.toMatch(/export\s*\{[^}]*\bStack\b/u);
66
+ expect(indexSource).not.toMatch(/export\s*\{[^}]*\bScrollArea\b/u);
91
67
  });
92
68
  });
93
69
 
package/src/index.ts CHANGED
@@ -6,12 +6,8 @@ export type { LabelProps } from './components/label';
6
6
  export { Label } from './components/label';
7
7
  export type { ListItemProps } from './components/list-item';
8
8
  export { ListItem } from './components/list-item';
9
- export type { ModalProps } from './components/modal';
10
- export { Modal } from './components/modal';
11
9
  export type { SwitchProps } from './components/switch';
12
10
  export { Switch } from './components/switch';
13
- export type { TabListProps, TabPanelProps, TabProps, TabsProps } from './components/tabs';
14
- export { Tab, TabList, TabPanel, Tabs } from './components/tabs';
15
11
  export type { TextareaProps } from './components/textarea';
16
12
  export { Textarea } from './components/textarea';
17
13
  export type { FontRuntime } from './context/FontContext';
@@ -34,6 +30,8 @@ export {
34
30
  AccordionItem,
35
31
  AccordionTrigger,
36
32
  } from './features/accordion/public';
33
+ export type { AppBarProps } from './features/app-bar/public';
34
+ export { AppBar } from './features/app-bar/public';
37
35
  export type { BadgeProps } from './features/badge/public';
38
36
  export { Badge } from './features/badge/public';
39
37
  export type { ButtonIconSpec, ButtonProps, IconButtonProps } from './features/button/public';
@@ -62,14 +60,10 @@ export type {
62
60
  KeyboardAvoidingViewProps,
63
61
  } from './features/keyboard-avoiding-view/public';
64
62
  export { KeyboardAvoidingView } from './features/keyboard-avoiding-view/public';
65
- export type {
66
- BoxProps,
67
- ContainerProps,
68
- DividerProps,
69
- GridProps,
70
- StackProps,
71
- } from './features/layout/public';
72
- export { Box, Container, Divider, Grid, Stack } from './features/layout/public';
63
+ export type { DividerProps, GridProps, ScrollViewProps, ViewProps } from './features/layout/public';
64
+ export { Divider, Grid, ScrollView, View } from './features/layout/public';
65
+ export type { ModalProps } from './features/modal/public';
66
+ export { Modal } from './features/modal/public';
73
67
  export type {
74
68
  PopoverAnchorRenderProps,
75
69
  PopoverPlacement,
@@ -84,6 +78,8 @@ export type {
84
78
  export { PopoverMenu } from './features/popover-menu/public';
85
79
  export type { SurfaceProps, SurfaceVariant } from './features/surface/public';
86
80
  export { Surface } from './features/surface/public';
81
+ export type { TabListProps, TabPanelProps, TabProps, TabsProps } from './features/tabs/public';
82
+ export { Tab, TabList, TabPanel, Tabs } from './features/tabs/public';
87
83
  export type {
88
84
  ToastController,
89
85
  ToastOptions,
@@ -97,7 +93,6 @@ export { Tooltip } from './features/tooltip/public';
97
93
  export type { HeadingLevel, HeadingProps, TextProps } from './features/typography/public';
98
94
  export { Heading, Text } from './features/typography/public';
99
95
  export type { InteractionPolicy, InteractionPolicyProps } from './interactionPolicy';
100
- export * from './layout';
101
96
  export type { ButtonBaseProps } from './primitives/button-base';
102
97
  export { ButtonBase } from './primitives/button-base';
103
98
  export type {
@@ -8,9 +8,10 @@ import {
8
8
  resolveInteractiveState,
9
9
  } from '../../internal/resolvers';
10
10
  import { useTheme } from '../../theme/ThemeContext';
11
- import { resolveBoxStyles } from '../../utils/resolveBoxStyles';
11
+ import { resolveViewStyles } from '../../utils/resolveViewStyles';
12
12
  import type { ButtonBaseProps } from './types';
13
13
 
14
+ /*** Resolves one Pressable state into the canonical Surface interaction state. */
14
15
  function getInteractionState(
15
16
  pressableState: PressableStateCallbackType,
16
17
  hovered: boolean,
@@ -25,6 +26,7 @@ function getInteractionState(
25
26
  });
26
27
  }
27
28
 
29
+ /*** Renders the low-level token-aware Pressable boundary shared by Surface controls. */
28
30
  export function ButtonBase({
29
31
  children,
30
32
  disabled = false,
@@ -43,7 +45,7 @@ export function ButtonBase({
43
45
  const [hovered, setHovered] = React.useState(false);
44
46
  const [focused, setFocused] = React.useState(false);
45
47
  const isWeb = Platform.OS === 'web';
46
- const resolvedBoxStyles = resolveBoxStyles(theme, breakpoint, props);
48
+ const resolvedViewStyles = resolveViewStyles(theme, breakpoint, props);
47
49
  const passive = interactionPolicy === 'passive';
48
50
 
49
51
  return (
@@ -54,9 +56,7 @@ export function ButtonBase({
54
56
  disabled={disabled}
55
57
  onBlur={() => setFocused(false)}
56
58
  onFocus={() => {
57
- if (isWeb) {
58
- setFocused(true);
59
- }
59
+ if (isWeb) setFocused(true);
60
60
  }}
61
61
  onHoverIn={isWeb ? () => setHovered(true) : undefined}
62
62
  onHoverOut={isWeb ? () => setHovered(false) : undefined}
@@ -64,9 +64,8 @@ export function ButtonBase({
64
64
  onPress={passive ? undefined : onPress}
65
65
  style={(pressableState) => {
66
66
  const state = getInteractionState(pressableState, hovered, focused, disabled);
67
-
68
67
  return [
69
- resolvedBoxStyles,
68
+ resolvedViewStyles,
70
69
  resolveFocusRingStyles(theme.semantics.border.focus, state.focused, isWeb),
71
70
  style,
72
71
  ];
@@ -75,7 +74,6 @@ export function ButtonBase({
75
74
  >
76
75
  {(pressableState) => {
77
76
  const state = getInteractionState(pressableState, hovered, focused, disabled);
78
-
79
77
  return typeof children === 'function' ? children(state) : children;
80
78
  }}
81
79
  </Pressable>
@@ -3,10 +3,10 @@ import type { AccessibilityRole, AccessibilityState, GestureResponderEvent } fro
3
3
 
4
4
  import type { InteractionPolicyProps } from '../../interactionPolicy';
5
5
  import type { InteractionState } from '../../internal/resolvers/resolveInteractiveState';
6
- import type { BoxProps } from '../../types/layout';
6
+ import type { ViewProps } from '../../types/layout';
7
7
 
8
8
  export interface ButtonBaseProps
9
- extends Omit<BoxProps, 'children' | 'pointerEvents'>, InteractionPolicyProps {
9
+ extends Omit<ViewProps, 'children' | 'pointerEvents'>, InteractionPolicyProps {
10
10
  children?: React.ReactNode | ((state: InteractionState) => React.ReactNode);
11
11
  disabled?: boolean;
12
12
  onPress?: ((event: GestureResponderEvent) => void) | undefined;
@@ -0,0 +1,13 @@
1
+ import type React from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+
4
+ import type { ViewProps } from './layout';
5
+
6
+ export interface AppBarProps extends Omit<ViewProps, 'children'> {
7
+ leading?: React.ReactNode;
8
+ trailing?: React.ReactNode;
9
+ children?: React.ReactNode;
10
+ safeAreaTop?: boolean;
11
+ divider?: boolean;
12
+ contentStyle?: StyleProp<ViewStyle>;
13
+ }
@@ -1,5 +1,10 @@
1
1
  import type React from 'react';
2
- import type { StyleProp, ViewProps as ReactNativeViewProps, ViewStyle } from 'react-native';
2
+ import type {
3
+ ScrollViewProps as ReactNativeScrollViewProps,
4
+ StyleProp,
5
+ ViewProps as ReactNativeViewProps,
6
+ ViewStyle,
7
+ } from 'react-native';
3
8
 
4
9
  import type { Responsive } from '../core/responsive';
5
10
  import type { SurfaceTheme } from '../theme/types';
@@ -12,7 +17,7 @@ type RadiusValue = number | RadiusToken;
12
17
  export type SpaceValue = number | SpaceToken;
13
18
  export type ColorValue = string | ColorToken;
14
19
 
15
- export interface BoxStyleProps {
20
+ export interface ViewStyleProps {
16
21
  p?: Responsive<SpaceValue>;
17
22
  px?: Responsive<SpaceValue>;
18
23
  py?: Responsive<SpaceValue>;
@@ -27,6 +32,9 @@ export interface BoxStyleProps {
27
32
  mb?: Responsive<SpaceValue>;
28
33
  ml?: Responsive<SpaceValue>;
29
34
  mr?: Responsive<SpaceValue>;
35
+ gap?: Responsive<SpaceValue>;
36
+ rowGap?: Responsive<SpaceValue>;
37
+ columnGap?: Responsive<SpaceValue>;
30
38
  bg?: Responsive<ColorValue>;
31
39
  radius?: Responsive<RadiusValue>;
32
40
  borderWidth?: Responsive<number>;
@@ -41,6 +49,12 @@ export interface BoxStyleProps {
41
49
  flexGrow?: Responsive<number>;
42
50
  flexShrink?: Responsive<number>;
43
51
  flexBasis?: Responsive<number | string>;
52
+ direction?: Responsive<'row' | 'column'>;
53
+ align?: Responsive<'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline'>;
54
+ justify?: Responsive<
55
+ 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly'
56
+ >;
57
+ wrap?: Responsive<'nowrap' | 'wrap'>;
44
58
  alignSelf?: Responsive<ViewStyle['alignSelf']>;
45
59
  position?: Responsive<ViewStyle['position']>;
46
60
  top?: Responsive<number>;
@@ -53,7 +67,7 @@ export interface BoxStyleProps {
53
67
  style?: StyleProp<ViewStyle>;
54
68
  }
55
69
 
56
- export interface BoxProps extends BoxStyleProps {
70
+ export interface ViewProps extends ViewStyleProps {
57
71
  accessibilityLabel?: ReactNativeViewProps['accessibilityLabel'];
58
72
  accessibilityRole?: ReactNativeViewProps['accessibilityRole'];
59
73
  accessibilityState?: ReactNativeViewProps['accessibilityState'];
@@ -63,34 +77,25 @@ export interface BoxProps extends BoxStyleProps {
63
77
  testID?: string;
64
78
  }
65
79
 
66
- export interface ContainerProps extends Omit<BoxProps, 'children'> {
67
- children?: React.ReactNode;
68
- maxWidth?: Responsive<number>;
69
- px?: Responsive<number | keyof SurfaceTheme['spacing']>;
70
- }
71
-
72
- export interface DividerProps extends Omit<BoxProps, 'bg' | 'height' | 'width'> {
80
+ export interface DividerProps extends Omit<ViewProps, 'bg' | 'height' | 'width'> {
73
81
  orientation?: 'horizontal' | 'vertical';
74
82
  color?: ColorValue;
75
83
  thickness?: number;
76
84
  }
77
85
 
78
- export interface GridProps extends Omit<BoxProps, 'children'> {
86
+ export interface GridProps extends Omit<ViewProps, 'children'> {
79
87
  children?: React.ReactNode;
80
- cols: Responsive<number>;
88
+ cols?: Responsive<number>;
81
89
  gap?: Responsive<SpaceValue>;
82
90
  rowGap?: Responsive<SpaceValue>;
83
91
  colGap?: Responsive<SpaceValue>;
84
92
  minItemWidth?: Responsive<number>;
85
93
  }
86
94
 
87
- export interface StackProps extends Omit<BoxProps, 'children'> {
95
+ export interface ScrollViewProps
96
+ extends
97
+ ViewStyleProps,
98
+ Omit<ReactNativeScrollViewProps, 'children' | 'contentContainerStyle' | 'style'> {
88
99
  children?: React.ReactNode;
89
- direction?: Responsive<'row' | 'column'>;
90
- gap?: Responsive<SpaceValue>;
91
- align?: Responsive<'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline'>;
92
- justify?: Responsive<
93
- 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly'
94
- >;
95
- wrap?: Responsive<'nowrap' | 'wrap'>;
100
+ contentContainerStyle?: StyleProp<ViewStyle>;
96
101
  }
@@ -1,6 +1,6 @@
1
1
  import type React from 'react';
2
2
 
3
- import type { InteractionPolicy } from '../../interactionPolicy';
3
+ import type { InteractionPolicy } from '../interactionPolicy';
4
4
 
5
5
  export interface ModalProps {
6
6
  visible: boolean;
@@ -1,7 +1,7 @@
1
- import type { BoxProps } from './layout';
1
+ import type { ViewProps } from './layout';
2
2
 
3
3
  export type SurfaceVariant = 'default' | 'subtle' | 'raised' | 'outline';
4
4
 
5
- export interface SurfaceProps extends Omit<BoxProps, 'bg' | 'borderColor' | 'borderWidth'> {
5
+ export interface SurfaceProps extends Omit<ViewProps, 'bg' | 'borderColor' | 'borderWidth'> {
6
6
  variant?: SurfaceVariant;
7
7
  }
@@ -0,0 +1,51 @@
1
+ import type React from 'react';
2
+
3
+ import type { InteractionPolicy } from '../interactionPolicy';
4
+
5
+ export interface TabsProps {
6
+ children?: React.ReactNode;
7
+ value?: string;
8
+ defaultValue?: string;
9
+ onValueChange?: ((value: string) => void) | undefined;
10
+ testID?: string;
11
+ }
12
+
13
+ export interface TabListProps {
14
+ children?: React.ReactNode;
15
+ testID?: string;
16
+ }
17
+
18
+ export interface TabProps {
19
+ value: string;
20
+ children?: React.ReactNode;
21
+ disabled?: boolean;
22
+ interactionPolicy?: InteractionPolicy;
23
+ testID?: string;
24
+ }
25
+
26
+ export interface TabPanelProps {
27
+ value: string;
28
+ children?: React.ReactNode;
29
+ testID?: string;
30
+ }
31
+
32
+ export interface TabRegistration {
33
+ disabled: boolean;
34
+ focus: () => void;
35
+ value: string;
36
+ }
37
+
38
+ export interface TabsContextValue {
39
+ activeValue: string | undefined;
40
+ focusedValue: string | undefined;
41
+ getPanelId: (value: string) => string;
42
+ getTabId: (value: string) => string;
43
+ registerTab: (tab: TabRegistration) => void;
44
+ setActiveValue: (value: string) => void;
45
+ setFocusedValue: (value: string | undefined) => void;
46
+ tabs: readonly TabRegistration[];
47
+ unregisterTab: (value: string) => void;
48
+ }
49
+
50
+ export type TabNavigationKey =
51
+ 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown' | 'Home' | 'End';