@ankhorage/zora 9.0.1 → 11.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 (237) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +2 -2
  3. package/dist/constants.d.ts +2 -1
  4. package/dist/constants.d.ts.map +1 -1
  5. package/dist/constants.js +2 -1
  6. package/dist/constants.js.map +1 -1
  7. package/dist/features/bottom-sheet/bottomSheetMeta.d.ts +1 -1
  8. package/dist/features/card/cardMeta.d.ts +1 -1
  9. package/dist/features/card/mediaCardMeta.d.ts +1 -1
  10. package/dist/features/card/postCardMeta.d.ts +1 -1
  11. package/dist/features/form/adapters/inbound/Form.d.ts +5 -0
  12. package/dist/features/form/adapters/inbound/Form.d.ts.map +1 -0
  13. package/dist/features/form/adapters/inbound/Form.js +18 -0
  14. package/dist/features/form/adapters/inbound/Form.js.map +1 -0
  15. package/dist/{components/form → features/form/adapters/inbound}/FormActions.d.ts +1 -1
  16. package/dist/features/form/adapters/inbound/FormActions.d.ts.map +1 -0
  17. package/dist/{components/form → features/form/adapters/inbound}/FormActions.js +3 -3
  18. package/dist/features/form/adapters/inbound/FormActions.js.map +1 -0
  19. package/dist/features/form/adapters/inbound/FormError.d.ts +5 -0
  20. package/dist/features/form/adapters/inbound/FormError.d.ts.map +1 -0
  21. package/dist/features/form/adapters/inbound/FormError.js +18 -0
  22. package/dist/features/form/adapters/inbound/FormError.js.map +1 -0
  23. package/dist/features/form/adapters/inbound/FormField.d.ts +5 -0
  24. package/dist/features/form/adapters/inbound/FormField.d.ts.map +1 -0
  25. package/dist/{patterns/form-field → features/form/adapters/inbound}/FormField.js +5 -4
  26. package/dist/features/form/adapters/inbound/FormField.js.map +1 -0
  27. package/dist/features/form/formActionsMeta.d.ts +9 -0
  28. package/dist/features/form/formActionsMeta.d.ts.map +1 -0
  29. package/dist/features/form/formActionsMeta.js +9 -0
  30. package/dist/features/form/formActionsMeta.js.map +1 -0
  31. package/dist/features/form/formErrorMeta.d.ts +32 -0
  32. package/dist/features/form/formErrorMeta.d.ts.map +1 -0
  33. package/dist/features/form/formErrorMeta.js +19 -0
  34. package/dist/features/form/formErrorMeta.js.map +1 -0
  35. package/dist/{components/form/meta.d.ts → features/form/formFieldMeta.d.ts} +28 -39
  36. package/dist/features/form/formFieldMeta.d.ts.map +1 -0
  37. package/dist/features/form/formFieldMeta.js +35 -0
  38. package/dist/features/form/formFieldMeta.js.map +1 -0
  39. package/dist/features/form/formMeta.d.ts +61 -0
  40. package/dist/features/form/formMeta.d.ts.map +1 -0
  41. package/dist/features/form/formMeta.js +34 -0
  42. package/dist/features/form/formMeta.js.map +1 -0
  43. package/dist/features/form/public.d.ts +8 -0
  44. package/dist/features/form/public.d.ts.map +1 -0
  45. package/dist/features/form/public.js +7 -0
  46. package/dist/features/form/public.js.map +1 -0
  47. package/dist/{components → features}/form/useFormController.d.ts +1 -1
  48. package/dist/features/form/useFormController.d.ts.map +1 -0
  49. package/dist/{components → features}/form/useFormController.js +1 -1
  50. package/dist/features/form/useFormController.js.map +1 -0
  51. package/dist/{components/form → features/form/utils}/validation.d.ts +1 -1
  52. package/dist/features/form/utils/validation.d.ts.map +1 -0
  53. package/dist/{components/form → features/form/utils}/validation.js +2 -4
  54. package/dist/features/form/utils/validation.js.map +1 -0
  55. package/dist/features/keyboard-avoiding-view/adapters/inbound/KeyboardAvoidingView.d.ts +5 -0
  56. package/dist/features/keyboard-avoiding-view/adapters/inbound/KeyboardAvoidingView.d.ts.map +1 -0
  57. package/dist/features/keyboard-avoiding-view/adapters/inbound/KeyboardAvoidingView.js +10 -0
  58. package/dist/features/keyboard-avoiding-view/adapters/inbound/KeyboardAvoidingView.js.map +1 -0
  59. package/dist/features/keyboard-avoiding-view/keyboardAvoidingViewMeta.d.ts +36 -0
  60. package/dist/features/keyboard-avoiding-view/keyboardAvoidingViewMeta.d.ts.map +1 -0
  61. package/dist/features/keyboard-avoiding-view/keyboardAvoidingViewMeta.js +37 -0
  62. package/dist/features/keyboard-avoiding-view/keyboardAvoidingViewMeta.js.map +1 -0
  63. package/dist/features/keyboard-avoiding-view/public.d.ts +3 -0
  64. package/dist/features/keyboard-avoiding-view/public.d.ts.map +1 -0
  65. package/dist/features/keyboard-avoiding-view/public.js +3 -0
  66. package/dist/features/keyboard-avoiding-view/public.js.map +1 -0
  67. package/dist/features/layout/boxMeta.d.ts +1 -1
  68. package/dist/features/layout/containerMeta.d.ts +1 -1
  69. package/dist/features/layout/gridMeta.d.ts +1 -1
  70. package/dist/features/layout/stackMeta.d.ts +1 -1
  71. package/dist/features/list/flatListMeta.d.ts +1 -1
  72. package/dist/features/list/sectionListMeta.d.ts +1 -1
  73. package/dist/features/surface/surfaceMeta.d.ts +1 -1
  74. package/dist/features/uploader/adapters/inbound/Uploader.d.ts.map +1 -1
  75. package/dist/features/uploader/adapters/inbound/Uploader.js +1 -1
  76. package/dist/features/uploader/adapters/inbound/Uploader.js.map +1 -1
  77. package/dist/index.d.ts +3 -4
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +2 -2
  80. package/dist/index.js.map +1 -1
  81. package/dist/layout/screen/meta.d.ts +1 -1
  82. package/dist/layout/screen-section/meta.d.ts +1 -1
  83. package/dist/metadata/allowedChildren.d.ts +3 -3
  84. package/dist/metadata/allowedChildren.d.ts.map +1 -1
  85. package/dist/metadata/allowedChildren.js +2 -2
  86. package/dist/metadata/allowedChildren.js.map +1 -1
  87. package/dist/metadata/bindableComponentMeta.d.ts +2 -2
  88. package/dist/metadata/componentMeta.d.ts.map +1 -1
  89. package/dist/metadata/componentMeta.js +6 -3
  90. package/dist/metadata/componentMeta.js.map +1 -1
  91. package/dist/patterns/auth/AuthForm.d.ts +21 -0
  92. package/dist/patterns/auth/AuthForm.d.ts.map +1 -0
  93. package/dist/patterns/auth/AuthForm.js +19 -0
  94. package/dist/patterns/auth/AuthForm.js.map +1 -0
  95. package/dist/patterns/auth/AuthFormField.d.ts +16 -0
  96. package/dist/patterns/auth/AuthFormField.d.ts.map +1 -0
  97. package/dist/patterns/auth/AuthFormField.js +38 -0
  98. package/dist/patterns/auth/AuthFormField.js.map +1 -0
  99. package/dist/patterns/auth/ForgotPasswordForm.d.ts.map +1 -1
  100. package/dist/patterns/auth/ForgotPasswordForm.js +2 -2
  101. package/dist/patterns/auth/ForgotPasswordForm.js.map +1 -1
  102. package/dist/patterns/auth/OtpForm.d.ts.map +1 -1
  103. package/dist/patterns/auth/OtpForm.js +2 -2
  104. package/dist/patterns/auth/OtpForm.js.map +1 -1
  105. package/dist/patterns/auth/SignInForm.d.ts.map +1 -1
  106. package/dist/patterns/auth/SignInForm.js +2 -2
  107. package/dist/patterns/auth/SignInForm.js.map +1 -1
  108. package/dist/patterns/auth/SignUpForm.d.ts.map +1 -1
  109. package/dist/patterns/auth/SignUpForm.js +2 -2
  110. package/dist/patterns/auth/SignUpForm.js.map +1 -1
  111. package/dist/patterns/auth/types.d.ts +1 -1
  112. package/dist/patterns/auth/types.d.ts.map +1 -1
  113. package/dist/patterns/auth/types.js.map +1 -1
  114. package/dist/patterns/auth/utils.d.ts +1 -1
  115. package/dist/patterns/auth/utils.d.ts.map +1 -1
  116. package/dist/patterns/auth/utils.js.map +1 -1
  117. package/dist/patterns/inspector-field/InspectorField.js +1 -1
  118. package/dist/patterns/inspector-field/InspectorField.js.map +1 -1
  119. package/dist/patterns/message-bubble/meta.d.ts +1 -1
  120. package/dist/patterns/notice/meta.d.ts +1 -1
  121. package/dist/patterns/panel/meta.d.ts +1 -1
  122. package/dist/patterns/reader/meta.d.ts +2 -2
  123. package/dist/patterns/scanner/meta.d.ts +2 -2
  124. package/dist/registry.js +3 -3
  125. package/dist/registry.js.map +1 -1
  126. package/dist/{components/form/types.d.ts → types/form.d.ts} +12 -28
  127. package/dist/types/form.d.ts.map +1 -0
  128. package/dist/types/form.js +2 -0
  129. package/dist/types/form.js.map +1 -0
  130. package/dist/types/keyboard-avoiding-view.d.ts +6 -0
  131. package/dist/types/keyboard-avoiding-view.d.ts.map +1 -0
  132. package/dist/types/keyboard-avoiding-view.js +2 -0
  133. package/dist/types/keyboard-avoiding-view.js.map +1 -0
  134. package/package.json +3 -2
  135. package/src/audit.test.ts +2 -4
  136. package/src/constants.ts +2 -1
  137. package/src/featureManifest.test.ts +7 -0
  138. package/src/features/form/adapters/inbound/Form.tsx +39 -0
  139. package/src/{components/form → features/form/adapters/inbound}/FormActions.tsx +4 -4
  140. package/src/features/form/adapters/inbound/FormError.tsx +22 -0
  141. package/src/{patterns/form-field → features/form/adapters/inbound}/FormField.tsx +6 -4
  142. package/src/features/form/formActionsMeta.ts +9 -0
  143. package/src/features/form/formErrorMeta.ts +19 -0
  144. package/src/features/form/formFieldMeta.ts +35 -0
  145. package/src/features/form/formMeta.ts +34 -0
  146. package/src/{components/form/index.ts → features/form/public.ts} +6 -7
  147. package/src/{components → features}/form/useFormController.ts +2 -2
  148. package/src/{components/form → features/form/utils}/validation.test.ts +4 -1
  149. package/src/{components/form → features/form/utils}/validation.ts +4 -8
  150. package/src/features/keyboard-avoiding-view/adapters/inbound/KeyboardAvoidingView.tsx +18 -0
  151. package/src/features/keyboard-avoiding-view/keyboardAvoidingViewMeta.ts +38 -0
  152. package/src/features/keyboard-avoiding-view/public.ts +5 -0
  153. package/src/features/uploader/adapters/inbound/Uploader.tsx +1 -1
  154. package/src/index.ts +31 -29
  155. package/src/metadata/allowedChildren.ts +2 -2
  156. package/src/metadata/componentMeta.test.ts +32 -2
  157. package/src/metadata/componentMeta.ts +6 -3
  158. package/src/patterns/auth/AuthForm.tsx +68 -0
  159. package/src/patterns/auth/AuthFormField.tsx +81 -0
  160. package/src/patterns/auth/ForgotPasswordForm.tsx +3 -2
  161. package/src/patterns/auth/OtpForm.tsx +3 -2
  162. package/src/patterns/auth/SignInForm.tsx +3 -2
  163. package/src/patterns/auth/SignUpForm.tsx +3 -2
  164. package/src/patterns/auth/types.ts +1 -1
  165. package/src/patterns/auth/utils.ts +1 -1
  166. package/src/patterns/inspector-field/InspectorField.tsx +1 -1
  167. package/src/platformAcceptance.test.ts +1 -1
  168. package/src/registry.ts +3 -3
  169. package/src/showcaseCoverage.test.ts +1 -0
  170. package/src/theme/themeScopeStructure.test.ts +5 -7
  171. package/src/{components/form/types.ts → types/form.ts} +12 -36
  172. package/src/types/keyboard-avoiding-view.ts +10 -0
  173. package/dist/components/form/Form.d.ts +0 -7
  174. package/dist/components/form/Form.d.ts.map +0 -1
  175. package/dist/components/form/Form.js +0 -34
  176. package/dist/components/form/Form.js.map +0 -1
  177. package/dist/components/form/FormActions.d.ts.map +0 -1
  178. package/dist/components/form/FormActions.js.map +0 -1
  179. package/dist/components/form/FormError.d.ts +0 -7
  180. package/dist/components/form/FormError.d.ts.map +0 -1
  181. package/dist/components/form/FormError.js +0 -21
  182. package/dist/components/form/FormError.js.map +0 -1
  183. package/dist/components/form/FormField.d.ts +0 -7
  184. package/dist/components/form/FormField.d.ts.map +0 -1
  185. package/dist/components/form/FormField.js +0 -81
  186. package/dist/components/form/FormField.js.map +0 -1
  187. package/dist/components/form/index.d.ts +0 -8
  188. package/dist/components/form/index.d.ts.map +0 -1
  189. package/dist/components/form/index.js +0 -7
  190. package/dist/components/form/index.js.map +0 -1
  191. package/dist/components/form/meta.d.ts.map +0 -1
  192. package/dist/components/form/meta.js +0 -94
  193. package/dist/components/form/meta.js.map +0 -1
  194. package/dist/components/form/types.d.ts.map +0 -1
  195. package/dist/components/form/types.js +0 -2
  196. package/dist/components/form/types.js.map +0 -1
  197. package/dist/components/form/useFormController.d.ts.map +0 -1
  198. package/dist/components/form/useFormController.js.map +0 -1
  199. package/dist/components/form/validation.d.ts.map +0 -1
  200. package/dist/components/form/validation.js.map +0 -1
  201. package/dist/features/splash-screen/adapters/inbound/SplashScreen.d.ts +0 -11
  202. package/dist/features/splash-screen/adapters/inbound/SplashScreen.d.ts.map +0 -1
  203. package/dist/features/splash-screen/adapters/inbound/SplashScreen.js +0 -55
  204. package/dist/features/splash-screen/adapters/inbound/SplashScreen.js.map +0 -1
  205. package/dist/features/splash-screen/public.d.ts +0 -3
  206. package/dist/features/splash-screen/public.d.ts.map +0 -1
  207. package/dist/features/splash-screen/public.js +0 -2
  208. package/dist/features/splash-screen/public.js.map +0 -1
  209. package/dist/features/splash-screen/splashScreenMeta.d.ts +0 -39
  210. package/dist/features/splash-screen/splashScreenMeta.d.ts.map +0 -1
  211. package/dist/features/splash-screen/splashScreenMeta.js +0 -40
  212. package/dist/features/splash-screen/splashScreenMeta.js.map +0 -1
  213. package/dist/patterns/form-field/FormField.d.ts +0 -4
  214. package/dist/patterns/form-field/FormField.d.ts.map +0 -1
  215. package/dist/patterns/form-field/FormField.js.map +0 -1
  216. package/dist/patterns/form-field/index.d.ts +0 -2
  217. package/dist/patterns/form-field/index.d.ts.map +0 -1
  218. package/dist/patterns/form-field/index.js +0 -2
  219. package/dist/patterns/form-field/index.js.map +0 -1
  220. package/dist/patterns/form-field/types.d.ts +0 -9
  221. package/dist/patterns/form-field/types.d.ts.map +0 -1
  222. package/dist/patterns/form-field/types.js +0 -2
  223. package/dist/patterns/form-field/types.js.map +0 -1
  224. package/dist/types/splash-screen.d.ts +0 -16
  225. package/dist/types/splash-screen.d.ts.map +0 -1
  226. package/dist/types/splash-screen.js +0 -2
  227. package/dist/types/splash-screen.js.map +0 -1
  228. package/src/components/form/Form.tsx +0 -74
  229. package/src/components/form/FormError.tsx +0 -34
  230. package/src/components/form/FormField.tsx +0 -157
  231. package/src/components/form/meta.ts +0 -98
  232. package/src/features/splash-screen/adapters/inbound/SplashScreen.tsx +0 -98
  233. package/src/features/splash-screen/public.ts +0 -2
  234. package/src/features/splash-screen/splashScreenMeta.ts +0 -41
  235. package/src/patterns/form-field/index.ts +0 -1
  236. package/src/patterns/form-field/types.ts +0 -16
  237. package/src/types/splash-screen.ts +0 -18
@@ -10,7 +10,6 @@ export const CONTAINER_ALLOWED_CHILDREN = [
10
10
  'ChipGroup',
11
11
  'DataTable',
12
12
  'IconButton',
13
- 'SplashScreen',
14
13
  'Uploader',
15
14
  'FlatList',
16
15
  'SectionList',
@@ -25,12 +24,13 @@ export const CONTAINER_ALLOWED_CHILDREN = [
25
24
  'Heading',
26
25
  'Icon',
27
26
  'Image',
27
+ 'KeyboardAvoidingView',
28
28
  'Button',
29
29
  'ButtonGroup',
30
30
  'RadioGroup',
31
31
  'ThemeModeToggle',
32
32
  'TextInput',
33
- 'FormField',
33
+ 'Form',
34
34
  'Card',
35
35
  'Panel',
36
36
  'Notice',
@@ -51,7 +51,7 @@ describe('ZORA_COMPONENT_META event metadata', () => {
51
51
  const eventType: ZoraComponentEventPayloadKind = event?.eventType ?? 'form.submit';
52
52
 
53
53
  expect(eventType).toBe('form.submit');
54
- expect(event?.payloadFields?.map((field) => field.path)).toEqual(['payload.values']);
54
+ expect(event?.payloadFields).toEqual([]);
55
55
  });
56
56
 
57
57
  test('declares button event metadata', () => {
@@ -100,6 +100,21 @@ describe('ZORA_COMPONENT_META requirement metadata', () => {
100
100
  });
101
101
 
102
102
  describe('ZORA_COMPONENT_META invariants', () => {
103
+ test('keeps form nodes in their canonical hierarchy', () => {
104
+ expect(ZORA_COMPONENT_META.Form.allowedChildren).toEqual(['FormError', 'FormField']);
105
+ expect(ZORA_COMPONENT_META.FormField.allowedChildren).toEqual([
106
+ 'Checkbox',
107
+ 'CheckboxGroup',
108
+ 'RadioGroup',
109
+ 'TextInput',
110
+ ]);
111
+ for (const [name, meta] of Object.entries(ZORA_COMPONENT_META)) {
112
+ if (name === 'Form') continue;
113
+ expect(meta.allowedChildren).not.toContain('FormError');
114
+ expect(meta.allowedChildren).not.toContain('FormField');
115
+ }
116
+ });
117
+
103
118
  test('every key matches meta.name', () => {
104
119
  for (const [key, meta] of Object.entries(ZORA_COMPONENT_META)) {
105
120
  expect(meta.name).toBe(key);
@@ -136,6 +151,7 @@ describe('ZORA_COMPONENT_META invariants', () => {
136
151
  'EmptyState',
137
152
  'Hero',
138
153
  'Button',
154
+ 'FormError',
139
155
  'Checkbox',
140
156
  'CheckboxGroup',
141
157
  'Radio',
@@ -158,12 +174,13 @@ describe('ZORA_COMPONENT_META invariants', () => {
158
174
  ]);
159
175
 
160
176
  const expectedContainerNodes = new Set([
177
+ 'Form',
161
178
  'Surface',
162
179
  'MediaCard',
163
- 'SplashScreen',
164
180
  'FlatList',
165
181
  'SectionList',
166
182
  'BottomSheet',
183
+ 'KeyboardAvoidingView',
167
184
  'FormField',
168
185
  'ButtonGroup',
169
186
  'Screen',
@@ -244,6 +261,19 @@ describe('ZORA_COMPONENT_META invariants', () => {
244
261
  expect(ZORA_COMPONENT_META.ThemeModeToggle.blueprint?.defaultProps).toEqual({ size: 'm' });
245
262
  });
246
263
 
264
+ test('KeyboardAvoidingView is a direct manifest container with native behavior props', () => {
265
+ const keyboardAvoidingView = ZORA_COMPONENT_META.KeyboardAvoidingView;
266
+
267
+ expect(keyboardAvoidingView.directManifestNode).toBe(true);
268
+ expect(keyboardAvoidingView.allowedChildren.length).toBeGreaterThan(0);
269
+ expect(keyboardAvoidingView.blueprint?.defaultProps).toEqual({
270
+ behavior: 'padding',
271
+ enabled: true,
272
+ keyboardVerticalOffset: 0,
273
+ });
274
+ expect(keyboardAvoidingView.props.behavior?.enum).toEqual(['height', 'position', 'padding']);
275
+ });
276
+
247
277
  test('Progress is a direct manifest leaf with serializable defaults', () => {
248
278
  expect(ZORA_COMPONENT_META.Progress.directManifestNode).toBe(true);
249
279
  expect(ZORA_COMPONENT_META.Progress.allowedChildren).toEqual([]);
@@ -1,7 +1,6 @@
1
1
  import { appBarMeta } from '../components/app-bar/meta';
2
2
  import { breadcrumbsMeta } from '../components/breadcrumbs/meta';
3
3
  import { datePickerMeta } from '../components/date-picker/meta';
4
- import { formActionsMeta, formErrorMeta, formFieldMeta, formMeta } from '../components/form/meta';
5
4
  import { gradientMeta } from '../components/gradient/meta';
6
5
  import { dropdownMenuMeta, menuMeta } from '../components/menu/meta';
7
6
  import { modalMeta } from '../components/modal/meta';
@@ -37,10 +36,15 @@ import { chipMeta } from '../features/chip/chipMeta';
37
36
  import { dataTableMeta } from '../features/data-table/dataTableMeta';
38
37
  import { emptyStateMeta } from '../features/empty-state/emptyStateMeta';
39
38
  import { checkboxGroupMeta, checkboxMeta } from '../features/form/checkbox/checkboxMeta';
39
+ import { formActionsMeta } from '../features/form/formActionsMeta';
40
+ import { formErrorMeta } from '../features/form/formErrorMeta';
41
+ import { formFieldMeta } from '../features/form/formFieldMeta';
42
+ import { formMeta } from '../features/form/formMeta';
40
43
  import { radioGroupMeta, radioMeta } from '../features/form/radio/radioMeta';
41
44
  import { textInputMeta } from '../features/form/text-input/textInputMeta';
42
45
  import { iconMeta } from '../features/icon/iconMeta';
43
46
  import { imageMeta } from '../features/image/imageMeta';
47
+ import { keyboardAvoidingViewMeta } from '../features/keyboard-avoiding-view/keyboardAvoidingViewMeta';
44
48
  import { boxMeta } from '../features/layout/boxMeta';
45
49
  import { containerMeta } from '../features/layout/containerMeta';
46
50
  import { contentRailMeta } from '../features/layout/contentRailMeta';
@@ -50,7 +54,6 @@ import { stackMeta } from '../features/layout/stackMeta';
50
54
  import { flatListMeta } from '../features/list/flatListMeta';
51
55
  import { sectionListMeta } from '../features/list/sectionListMeta';
52
56
  import { progressMeta, progressRingMeta } from '../features/progress/progressMeta';
53
- import { splashScreenMeta } from '../features/splash-screen/splashScreenMeta';
54
57
  import { surfaceMeta } from '../features/surface/surfaceMeta';
55
58
  import { headingMeta } from '../features/typography/headingMeta';
56
59
  import { textMeta } from '../features/typography/textMeta';
@@ -133,6 +136,7 @@ export const ZORA_COMPONENT_META: ZoraComponentMetaRegistry = finalizeFeatureMet
133
136
  Icon: iconMeta,
134
137
  IconButton: iconButtonMeta,
135
138
  Image: imageMeta,
139
+ KeyboardAvoidingView: keyboardAvoidingViewMeta,
136
140
  TextInput: textInputMeta,
137
141
  MediaCard: mediaCardMeta,
138
142
  Menu: menuMeta,
@@ -150,7 +154,6 @@ export const ZORA_COMPONENT_META: ZoraComponentMetaRegistry = finalizeFeatureMet
150
154
  SkeletonCard: skeletonCardMeta,
151
155
  SkeletonList: skeletonListMeta,
152
156
  SkeletonText: skeletonTextMeta,
153
- SplashScreen: splashScreenMeta,
154
157
  Tabs: tabsMeta,
155
158
  Text: textMeta,
156
159
  TimePicker: timePickerMeta,
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+
3
+ import { FormActions, FormError, useFormController } from '../../features/form/public';
4
+ import { Stack } from '../../features/layout/public';
5
+ import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
6
+ import type { FormErrors, FormFieldConfig, FormValues } from '../../types/form';
7
+ import { AuthFormField } from './AuthFormField';
8
+
9
+ interface AuthFormProps<TName extends string> {
10
+ fields: readonly FormFieldConfig<TName>[];
11
+ values: FormValues<TName>;
12
+ onChange: (values: FormValues<TName>) => void;
13
+ onSubmit: (values: FormValues<TName>) => void | Promise<void>;
14
+ errors?: FormErrors<TName>;
15
+ error?: React.ReactNode;
16
+ loading?: boolean;
17
+ disabled?: boolean;
18
+ submitLabel?: React.ReactNode;
19
+ actions?: React.ReactNode;
20
+ testID?: string;
21
+ interactionPolicy?: ZoraBaseProps['interactionPolicy'];
22
+ }
23
+
24
+ /*** Keeps config-driven field orchestration private to the auth patterns that own it. */
25
+ export function AuthForm<TName extends string>({
26
+ fields,
27
+ values,
28
+ onChange,
29
+ onSubmit,
30
+ errors,
31
+ error,
32
+ loading = false,
33
+ disabled = false,
34
+ submitLabel = 'Submit',
35
+ actions,
36
+ testID,
37
+ interactionPolicy,
38
+ }: AuthFormProps<TName>) {
39
+ const controller = useFormController({ fields, values, errors, onChange, onSubmit });
40
+ return (
41
+ <Stack gap="m" testID={testID}>
42
+ <FormError error={error} />
43
+ {fields.map((field) => (
44
+ <AuthFormField
45
+ key={field.name}
46
+ disabled={disabled}
47
+ error={controller.errors[field.name]}
48
+ field={field}
49
+ interactionPolicy={interactionPolicy}
50
+ loading={loading}
51
+ onChange={controller.setFieldValue}
52
+ value={values[field.name]}
53
+ />
54
+ ))}
55
+ <FormActions
56
+ disabled={disabled}
57
+ interactionPolicy={interactionPolicy}
58
+ loading={loading}
59
+ onSubmit={() => {
60
+ if (interactionPolicy !== 'passive') void controller.handleSubmit();
61
+ }}
62
+ submitLabel={submitLabel}
63
+ >
64
+ {actions}
65
+ </FormActions>
66
+ </Stack>
67
+ );
68
+ }
@@ -0,0 +1,81 @@
1
+ import React from 'react';
2
+
3
+ import { FormField, hasRequiredRule } from '../../features/form/public';
4
+ import { TextInput } from '../../features/form/text-input/public';
5
+ import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
6
+ import type { FormFieldConfig, FormFieldValue } from '../../types/form';
7
+
8
+ interface AuthFormFieldProps<TName extends string> {
9
+ field: FormFieldConfig<TName>;
10
+ value: FormFieldValue;
11
+ onChange: (name: TName, value: FormFieldValue) => void;
12
+ error?: React.ReactNode;
13
+ disabled?: boolean;
14
+ loading?: boolean;
15
+ interactionPolicy?: ZoraBaseProps['interactionPolicy'];
16
+ }
17
+
18
+ /*** Adapts an auth field configuration to the public compositional form primitives. */
19
+ export function AuthFormField<TName extends string>({
20
+ field,
21
+ value,
22
+ onChange,
23
+ error,
24
+ disabled = false,
25
+ loading = false,
26
+ interactionPolicy,
27
+ }: AuthFormFieldProps<TName>) {
28
+ const fieldDisabled = disabled || loading || field.disabled;
29
+ return (
30
+ <FormField
31
+ description={field.description}
32
+ disabled={fieldDisabled}
33
+ errorText={error}
34
+ helperText={field.helperText}
35
+ invalid={Boolean(error)}
36
+ label={field.label}
37
+ readOnly={field.readOnly}
38
+ required={field.required ?? hasRequiredRule(field.rules)}
39
+ testID={field.testID}
40
+ >
41
+ <TextInput
42
+ accessibilityLabel={typeof field.label === 'string' ? field.label : undefined}
43
+ autoCapitalize={
44
+ field.autoCapitalize ??
45
+ (['email', 'password', 'url'].includes(field.type ?? 'text') ? 'none' : undefined)
46
+ }
47
+ autoComplete={field.autoComplete}
48
+ disabled={fieldDisabled}
49
+ interactionPolicy={interactionPolicy}
50
+ invalid={Boolean(error)}
51
+ keyboardType={resolveKeyboardType(field)}
52
+ maxLength={field.maxLength}
53
+ onChangeText={(nextValue) => onChange(field.name, nextValue)}
54
+ placeholder={field.placeholder}
55
+ readOnly={field.readOnly}
56
+ secureTextEntry={field.secureTextEntry ?? field.type === 'password'}
57
+ textContentType={resolveTextContentType(field)}
58
+ value={value}
59
+ />
60
+ </FormField>
61
+ );
62
+ }
63
+
64
+ /*** Resolves the native keyboard for an auth field configuration. */
65
+ function resolveKeyboardType(field: FormFieldConfig) {
66
+ if (field.keyboardType) return field.keyboardType;
67
+ if (field.type === 'email') return 'email-address';
68
+ if (field.type === 'number' || field.type === 'otp') return 'number-pad';
69
+ if (field.type === 'tel') return 'phone-pad';
70
+ if (field.type === 'url') return 'url';
71
+ return undefined;
72
+ }
73
+
74
+ /*** Resolves platform text-content hints for an auth field configuration. */
75
+ function resolveTextContentType(field: FormFieldConfig) {
76
+ if (field.textContentType) return field.textContentType;
77
+ if (field.type === 'email') return 'emailAddress';
78
+ if (field.type === 'password') return 'password';
79
+ if (field.type === 'otp') return 'oneTimeCode';
80
+ return undefined;
81
+ }
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
2
 
3
- import { Form, type FormFieldConfig, type FormValues } from '../../components/form';
4
3
  import { Button } from '../../features/button/public';
5
4
  import { Stack } from '../../features/layout/public';
6
5
  import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
6
+ import type { FormFieldConfig, FormValues } from '../../types/form';
7
+ import { AuthForm } from './AuthForm';
7
8
  import type { ForgotPasswordFormProps } from './types';
8
9
  import {
9
10
  defaultIdentifiers,
@@ -56,7 +57,7 @@ function ForgotPasswordFormInner({
56
57
  );
57
58
 
58
59
  return (
59
- <Form
60
+ <AuthForm
60
61
  actions={
61
62
  onSignIn ? (
62
63
  <Stack direction="row" gap="s" wrap="wrap">
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
2
 
3
- import { Form, type FormFieldConfig, type FormValues } from '../../components/form';
4
3
  import { Button } from '../../features/button/public';
5
4
  import { Stack } from '../../features/layout/public';
6
5
  import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
6
+ import type { FormFieldConfig, FormValues } from '../../types/form';
7
+ import { AuthForm } from './AuthForm';
7
8
  import type { OtpFormProps } from './types';
8
9
 
9
10
  type OtpFieldName = 'otp';
@@ -51,7 +52,7 @@ function OtpFormInner({
51
52
  );
52
53
 
53
54
  return (
54
- <Form
55
+ <AuthForm
55
56
  actions={
56
57
  onResend ? (
57
58
  <Stack direction="row" gap="s" wrap="wrap">
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
2
 
3
- import { Form, type FormFieldConfig, type FormValues } from '../../components/form';
4
3
  import { Button } from '../../features/button/public';
5
4
  import { Stack } from '../../features/layout/public';
6
5
  import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
6
+ import type { FormFieldConfig, FormValues } from '../../types/form';
7
+ import { AuthForm } from './AuthForm';
7
8
  import type { SignInFormProps } from './types';
8
9
  import {
9
10
  defaultIdentifiers,
@@ -68,7 +69,7 @@ function SignInFormInner({
68
69
  );
69
70
 
70
71
  return (
71
- <Form
72
+ <AuthForm
72
73
  actions={
73
74
  hasActions ? (
74
75
  <Stack direction="row" gap="s" wrap="wrap">
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
2
 
3
- import { Form, type FormFieldConfig, type FormValues } from '../../components/form';
4
3
  import { Button } from '../../features/button/public';
5
4
  import { Stack } from '../../features/layout/public';
6
5
  import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
6
+ import type { FormFieldConfig, FormValues } from '../../types/form';
7
+ import { AuthForm } from './AuthForm';
7
8
  import type { SignUpFormProps } from './types';
8
9
 
9
10
  const defaultSignUpFields = [
@@ -48,7 +49,7 @@ function SignUpFormInner({
48
49
  const [values, setValues] = React.useState<FormValues>(() => createValues(fields));
49
50
 
50
51
  return (
51
- <Form
52
+ <AuthForm
52
53
  actions={
53
54
  onSignIn ? (
54
55
  <Stack direction="row" gap="s" wrap="wrap">
@@ -1,9 +1,9 @@
1
1
  import type { ButtonIconSpec } from '@ankhorage/surface';
2
2
  import type React from 'react';
3
3
 
4
- import type { FormFieldConfig, FormValues } from '../../components/form';
5
4
  import type { ButtonProps } from '../../features/button/public';
6
5
  import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
6
+ import type { FormFieldConfig, FormValues } from '../../types/form';
7
7
 
8
8
  export type AuthIdentifierKind = 'email' | 'phone' | 'username';
9
9
 
@@ -1,4 +1,4 @@
1
- import type { FormFieldConfig, ValidationRule } from '../../components/form';
1
+ import type { FormFieldConfig, ValidationRule } from '../../types/form';
2
2
  import type { AuthIdentifierKind } from './types';
3
3
 
4
4
  export const defaultIdentifiers = ['email'] as const satisfies readonly AuthIdentifierKind[];
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
2
 
3
+ import { FormField } from '../../features/form/public';
3
4
  import { Box, Stack } from '../../features/layout/public';
4
5
  import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
5
- import { FormField } from '../form-field';
6
6
  import type { InspectorFieldProps } from './types';
7
7
 
8
8
  function InspectorFieldInner({
@@ -9,6 +9,6 @@ test('passes the isolated Surface 4 and RN Web 0.21 acceptance suite', () => {
9
9
  const output = `${subprocess.stdout.toString()}\n${subprocess.stderr.toString()}`;
10
10
 
11
11
  expect(subprocess.exitCode, output).toBe(0);
12
- expect(output).toContain('9 pass');
12
+ expect(output).toContain('10 pass');
13
13
  expect(output).toContain('0 fail');
14
14
  });
package/src/registry.ts CHANGED
@@ -4,7 +4,6 @@ import type React from 'react';
4
4
  import { AppBar } from './components/app-bar';
5
5
  import { Breadcrumbs } from './components/breadcrumbs';
6
6
  import { DatePicker } from './components/date-picker';
7
- import { Form, FormActions, FormError, FormField } from './components/form';
8
7
  import { Gradient } from './components/gradient';
9
8
  import { DropdownMenu, Menu } from './components/menu';
10
9
  import { Modal } from './components/modal';
@@ -35,15 +34,16 @@ import { ChipGroup } from './features/chip/public';
35
34
  import { DataTable } from './features/data-table/public';
36
35
  import { EmptyState } from './features/empty-state/public';
37
36
  import { Checkbox, CheckboxGroup } from './features/form/checkbox/public';
37
+ import { Form, FormActions, FormError, FormField } from './features/form/public';
38
38
  import { Radio, RadioGroup } from './features/form/radio/public';
39
39
  import { TextInput } from './features/form/text-input/public';
40
40
  import { Icon } from './features/icon/public';
41
41
  import { Image } from './features/image/public';
42
+ import { KeyboardAvoidingView } from './features/keyboard-avoiding-view/public';
42
43
  import { Box, Container, Divider, Grid, Stack } from './features/layout/public';
43
44
  import { ContentRail } from './features/layout/public';
44
45
  import { FlatList, SectionList } from './features/list/public';
45
46
  import { Progress, ProgressRing } from './features/progress/public';
46
- import { SplashScreen } from './features/splash-screen/public';
47
47
  import { Surface } from './features/surface/public';
48
48
  import { Heading } from './features/typography/public';
49
49
  import { Text } from './features/typography/public';
@@ -138,6 +138,7 @@ const _ZORA_COMPONENT_REGISTRY = {
138
138
  Icon,
139
139
  IconButton,
140
140
  Image,
141
+ KeyboardAvoidingView,
141
142
  TextInput,
142
143
  MediaCard,
143
144
  Menu,
@@ -155,7 +156,6 @@ const _ZORA_COMPONENT_REGISTRY = {
155
156
  SkeletonCard,
156
157
  SkeletonList,
157
158
  SkeletonText,
158
- SplashScreen,
159
159
  Tabs,
160
160
  Text,
161
161
  TimePicker,
@@ -36,6 +36,7 @@ const REQUIRED_SHOWCASE_COVERAGE = {
36
36
  'Icon',
37
37
  'IconButton',
38
38
  'Image',
39
+ 'KeyboardAvoidingView',
39
40
  'TextInput',
40
41
  'MediaCard',
41
42
  'MetricCard',
@@ -65,10 +65,10 @@ const scopedComponentFiles = [
65
65
  join(srcDir, 'features', 'card', 'adapters', 'inbound', 'MetricCard.tsx'),
66
66
  join(srcDir, 'features', 'form', 'checkbox', 'adapters', 'inbound', 'Checkbox.tsx'),
67
67
  join(srcDir, 'features', 'form', 'checkbox', 'adapters', 'inbound', 'CheckboxGroup.tsx'),
68
- join(srcDir, 'components', 'form', 'Form.tsx'),
69
- join(srcDir, 'components', 'form', 'FormActions.tsx'),
70
- join(srcDir, 'components', 'form', 'FormError.tsx'),
71
- join(srcDir, 'components', 'form', 'FormField.tsx'),
68
+ join(srcDir, 'features', 'form', 'adapters', 'inbound', 'Form.tsx'),
69
+ join(srcDir, 'features', 'form', 'adapters', 'inbound', 'FormActions.tsx'),
70
+ join(srcDir, 'features', 'form', 'adapters', 'inbound', 'FormError.tsx'),
71
+ join(srcDir, 'features', 'form', 'adapters', 'inbound', 'FormField.tsx'),
72
72
  join(srcDir, 'features', 'typography', 'adapters', 'inbound', 'Heading.tsx'),
73
73
  join(srcDir, 'features', 'icon', 'adapters', 'inbound', 'Icon.tsx'),
74
74
  join(srcDir, 'features', 'button', 'adapters', 'inbound', 'IconButton.tsx'),
@@ -99,7 +99,6 @@ const scopedComponentFiles = [
99
99
  join(srcDir, 'patterns', 'confirm-dialog', 'ConfirmDialog.tsx'),
100
100
  join(srcDir, 'patterns', 'disclosure-section', 'DisclosureSection.tsx'),
101
101
  join(srcDir, 'features', 'empty-state', 'adapters', 'inbound', 'EmptyState.tsx'),
102
- join(srcDir, 'patterns', 'form-field', 'FormField.tsx'),
103
102
  join(srcDir, 'patterns', 'inspector-field', 'InspectorField.tsx'),
104
103
  join(srcDir, 'patterns', 'list', 'List.tsx'),
105
104
  join(srcDir, 'patterns', 'list', 'ListRow.tsx'),
@@ -136,7 +135,7 @@ const scopedPropTypeFiles = [
136
135
  join(srcDir, 'types', 'media-card.ts'),
137
136
  join(srcDir, 'types', 'metric-card.ts'),
138
137
  join(srcDir, 'types', 'checkbox.ts'),
139
- join(srcDir, 'components', 'form', 'types.ts'),
138
+ join(srcDir, 'types', 'form.ts'),
140
139
  join(srcDir, 'types', 'heading.ts'),
141
140
  join(srcDir, 'types', 'icon.ts'),
142
141
  join(srcDir, 'types', 'icon-button.ts'),
@@ -162,7 +161,6 @@ const scopedPropTypeFiles = [
162
161
  join(srcDir, 'patterns', 'confirm-dialog', 'types.ts'),
163
162
  join(srcDir, 'patterns', 'disclosure-section', 'types.ts'),
164
163
  join(srcDir, 'types', 'empty-state.ts'),
165
- join(srcDir, 'patterns', 'form-field', 'types.ts'),
166
164
  join(srcDir, 'patterns', 'inspector-field', 'types.ts'),
167
165
  join(srcDir, 'patterns', 'list', 'types.ts'),
168
166
  join(srcDir, 'patterns', 'notice', 'types.ts'),
@@ -1,20 +1,18 @@
1
1
  import type { FieldProps as SurfaceFieldProps } from '@ankhorage/surface';
2
2
  import type React from 'react';
3
3
 
4
- import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
5
- import type { TextInputProps } from '../../types/text-input';
4
+ import type { ZoraBaseProps } from '../theme/ZoraBaseProps';
5
+ import type { TextInputProps } from './text-input';
6
6
 
7
7
  export type ValidationRule =
8
8
  | { kind: 'required'; message?: string }
9
9
  | { kind: 'email'; message?: string }
10
10
  | { kind: 'minLength'; value: number; message?: string }
11
11
  | { kind: 'pattern'; value: string; message?: string };
12
-
13
12
  export type FormFieldValue = string;
14
13
  export type FormValues<TName extends string = string> = Record<TName, FormFieldValue>;
15
14
  export type FormErrors<TName extends string = string> = Partial<Record<TName, React.ReactNode>>;
16
15
  export type FormValidationErrors<TName extends string = string> = Partial<Record<TName, string>>;
17
-
18
16
  export type FormFieldInputType = 'email' | 'number' | 'otp' | 'password' | 'tel' | 'text' | 'url';
19
17
 
20
18
  export interface FormFieldConfig<TName extends string = string> {
@@ -37,7 +35,16 @@ export interface FormFieldConfig<TName extends string = string> {
37
35
  testID?: string;
38
36
  }
39
37
 
40
- interface FormFieldWrapperProps
38
+ export interface FormProps extends ZoraBaseProps {
39
+ children?: React.ReactNode;
40
+ onSubmit?: () => void | Promise<void>;
41
+ loading?: boolean;
42
+ disabled?: boolean;
43
+ submitLabel?: React.ReactNode;
44
+ actions?: React.ReactNode;
45
+ }
46
+
47
+ export interface FormFieldProps
41
48
  extends
42
49
  ZoraBaseProps,
43
50
  Pick<
@@ -49,18 +56,6 @@ interface FormFieldWrapperProps
49
56
  helperText?: React.ReactNode;
50
57
  }
51
58
 
52
- export interface FormFieldControlProps<TName extends string = string> extends ZoraBaseProps {
53
- field: FormFieldConfig<TName>;
54
- value: FormFieldValue;
55
- onChange: (name: TName, value: FormFieldValue) => void;
56
- error?: React.ReactNode;
57
- disabled?: boolean;
58
- loading?: boolean;
59
- }
60
-
61
- export type FormFieldProps<TName extends string = string> =
62
- FormFieldControlProps<TName> | FormFieldWrapperProps;
63
-
64
59
  export interface FormActionsProps extends ZoraBaseProps {
65
60
  submitLabel?: React.ReactNode;
66
61
  loading?: boolean;
@@ -68,31 +63,13 @@ export interface FormActionsProps extends ZoraBaseProps {
68
63
  onSubmit?: () => void;
69
64
  children?: React.ReactNode;
70
65
  }
71
-
72
66
  export interface FormErrorProps extends ZoraBaseProps {
73
67
  error?: React.ReactNode;
74
68
  }
75
-
76
- export interface FormProps<TName extends string = string> extends ZoraBaseProps {
77
- fields: readonly FormFieldConfig<TName>[];
78
- values: FormValues<TName>;
79
- onChange: (values: FormValues<TName>) => void;
80
- onSubmit: (values: FormValues<TName>) => void | Promise<void>;
81
- errors?: FormErrors<TName>;
82
- error?: React.ReactNode;
83
- loading?: boolean;
84
- disabled?: boolean;
85
- submitLabel?: React.ReactNode;
86
- actions?: React.ReactNode;
87
- footer?: React.ReactNode;
88
- validateOnChange?: boolean;
89
- }
90
-
91
69
  export interface FormValidationResult<TName extends string = string> {
92
70
  errors: FormValidationErrors<TName>;
93
71
  valid: boolean;
94
72
  }
95
-
96
73
  export interface UseFormControllerOptions<TName extends string = string> {
97
74
  fields: readonly FormFieldConfig<TName>[];
98
75
  initialValues?: Partial<FormValues<TName>>;
@@ -102,7 +79,6 @@ export interface UseFormControllerOptions<TName extends string = string> {
102
79
  onSubmit?: (values: FormValues<TName>) => void | Promise<void>;
103
80
  validateOnChange?: boolean;
104
81
  }
105
-
106
82
  export interface UseFormControllerResult<TName extends string = string> {
107
83
  values: FormValues<TName>;
108
84
  errors: FormErrors<TName>;
@@ -0,0 +1,10 @@
1
+ import type { KeyboardAvoidingViewProps as SurfaceKeyboardAvoidingViewProps } from '@ankhorage/surface';
2
+
3
+ import type { ZoraBaseProps } from '../theme/ZoraBaseProps';
4
+
5
+ export type KeyboardAvoidingViewBehavior = NonNullable<
6
+ SurfaceKeyboardAvoidingViewProps['behavior']
7
+ >;
8
+
9
+ export interface KeyboardAvoidingViewProps
10
+ extends ZoraBaseProps, Omit<SurfaceKeyboardAvoidingViewProps, keyof ZoraBaseProps> {}
@@ -1,7 +0,0 @@
1
- import React from 'react';
2
- import type { FormProps } from './types';
3
- /***
4
- * Composes fields and actions into a validated form layout.
5
- */
6
- export declare const Form: <TName extends string = string>(props: FormProps<TName>) => React.ReactElement | null;
7
- //# sourceMappingURL=Form.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Form.d.ts","sourceRoot":"","sources":["../../../src/components/form/Form.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AA+DzC;;GAEG;AACH,eAAO,MAAM,IAAI,GA/DE,KAAK,SAAS,MAAM,gEA+DU,CAAC"}