@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
@@ -1,7 +1,7 @@
1
1
  import type { FieldProps as SurfaceFieldProps } from '@ankhorage/surface';
2
2
  import type React from 'react';
3
- import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
4
- import type { TextInputProps } from '../../types/text-input';
3
+ import type { ZoraBaseProps } from '../theme/ZoraBaseProps';
4
+ import type { TextInputProps } from './text-input';
5
5
  export type ValidationRule = {
6
6
  kind: 'required';
7
7
  message?: string;
@@ -41,20 +41,19 @@ export interface FormFieldConfig<TName extends string = string> {
41
41
  disabled?: boolean;
42
42
  testID?: string;
43
43
  }
44
- interface FormFieldWrapperProps extends ZoraBaseProps, Pick<Omit<SurfaceFieldProps, 'mode' | 'themeId'>, 'children' | 'disabled' | 'errorText' | 'invalid' | 'readOnly' | 'required'> {
44
+ export interface FormProps extends ZoraBaseProps {
45
+ children?: React.ReactNode;
46
+ onSubmit?: () => void | Promise<void>;
47
+ loading?: boolean;
48
+ disabled?: boolean;
49
+ submitLabel?: React.ReactNode;
50
+ actions?: React.ReactNode;
51
+ }
52
+ export interface FormFieldProps extends ZoraBaseProps, Pick<Omit<SurfaceFieldProps, 'mode' | 'themeId'>, 'children' | 'disabled' | 'errorText' | 'invalid' | 'readOnly' | 'required'> {
45
53
  label: React.ReactNode;
46
54
  description?: React.ReactNode;
47
55
  helperText?: React.ReactNode;
48
56
  }
49
- export interface FormFieldControlProps<TName extends string = string> extends ZoraBaseProps {
50
- field: FormFieldConfig<TName>;
51
- value: FormFieldValue;
52
- onChange: (name: TName, value: FormFieldValue) => void;
53
- error?: React.ReactNode;
54
- disabled?: boolean;
55
- loading?: boolean;
56
- }
57
- export type FormFieldProps<TName extends string = string> = FormFieldControlProps<TName> | FormFieldWrapperProps;
58
57
  export interface FormActionsProps extends ZoraBaseProps {
59
58
  submitLabel?: React.ReactNode;
60
59
  loading?: boolean;
@@ -65,20 +64,6 @@ export interface FormActionsProps extends ZoraBaseProps {
65
64
  export interface FormErrorProps extends ZoraBaseProps {
66
65
  error?: React.ReactNode;
67
66
  }
68
- export interface FormProps<TName extends string = string> extends ZoraBaseProps {
69
- fields: readonly FormFieldConfig<TName>[];
70
- values: FormValues<TName>;
71
- onChange: (values: FormValues<TName>) => void;
72
- onSubmit: (values: FormValues<TName>) => void | Promise<void>;
73
- errors?: FormErrors<TName>;
74
- error?: React.ReactNode;
75
- loading?: boolean;
76
- disabled?: boolean;
77
- submitLabel?: React.ReactNode;
78
- actions?: React.ReactNode;
79
- footer?: React.ReactNode;
80
- validateOnChange?: boolean;
81
- }
82
67
  export interface FormValidationResult<TName extends string = string> {
83
68
  errors: FormValidationErrors<TName>;
84
69
  valid: boolean;
@@ -101,5 +86,4 @@ export interface UseFormControllerResult<TName extends string = string> {
101
86
  handleSubmit: () => Promise<void>;
102
87
  reset: () => void;
103
88
  }
104
- export {};
105
- //# sourceMappingURL=types.d.ts.map
89
+ //# sourceMappingURL=form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../src/types/form.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.js","sourceRoot":"","sources":["../../src/types/form.ts"],"names":[],"mappings":"","sourcesContent":["import type { FieldProps as SurfaceFieldProps } from '@ankhorage/surface';\nimport type React from 'react';\n\nimport type { ZoraBaseProps } from '../theme/ZoraBaseProps';\nimport type { TextInputProps } from './text-input';\n\nexport type ValidationRule =\n | { kind: 'required'; message?: string }\n | { kind: 'email'; message?: string }\n | { kind: 'minLength'; value: number; message?: string }\n | { kind: 'pattern'; value: string; message?: string };\nexport type FormFieldValue = string;\nexport type FormValues<TName extends string = string> = Record<TName, FormFieldValue>;\nexport type FormErrors<TName extends string = string> = Partial<Record<TName, React.ReactNode>>;\nexport type FormValidationErrors<TName extends string = string> = Partial<Record<TName, string>>;\nexport type FormFieldInputType = 'email' | 'number' | 'otp' | 'password' | 'tel' | 'text' | 'url';\n\nexport interface FormFieldConfig<TName extends string = string> {\n name: TName;\n label: React.ReactNode;\n description?: React.ReactNode;\n helperText?: React.ReactNode;\n type?: FormFieldInputType;\n placeholder?: string;\n rules?: readonly ValidationRule[];\n autoCapitalize?: TextInputProps['autoCapitalize'];\n autoComplete?: TextInputProps['autoComplete'];\n keyboardType?: TextInputProps['keyboardType'];\n maxLength?: TextInputProps['maxLength'];\n readOnly?: boolean;\n required?: boolean;\n secureTextEntry?: boolean;\n textContentType?: TextInputProps['textContentType'];\n disabled?: boolean;\n testID?: string;\n}\n\nexport interface FormProps extends ZoraBaseProps {\n children?: React.ReactNode;\n onSubmit?: () => void | Promise<void>;\n loading?: boolean;\n disabled?: boolean;\n submitLabel?: React.ReactNode;\n actions?: React.ReactNode;\n}\n\nexport interface FormFieldProps\n extends\n ZoraBaseProps,\n Pick<\n Omit<SurfaceFieldProps, 'mode' | 'themeId'>,\n 'children' | 'disabled' | 'errorText' | 'invalid' | 'readOnly' | 'required'\n > {\n label: React.ReactNode;\n description?: React.ReactNode;\n helperText?: React.ReactNode;\n}\n\nexport interface FormActionsProps extends ZoraBaseProps {\n submitLabel?: React.ReactNode;\n loading?: boolean;\n disabled?: boolean;\n onSubmit?: () => void;\n children?: React.ReactNode;\n}\nexport interface FormErrorProps extends ZoraBaseProps {\n error?: React.ReactNode;\n}\nexport interface FormValidationResult<TName extends string = string> {\n errors: FormValidationErrors<TName>;\n valid: boolean;\n}\nexport interface UseFormControllerOptions<TName extends string = string> {\n fields: readonly FormFieldConfig<TName>[];\n initialValues?: Partial<FormValues<TName>>;\n values?: FormValues<TName>;\n errors?: FormErrors<TName>;\n onChange?: (values: FormValues<TName>) => void;\n onSubmit?: (values: FormValues<TName>) => void | Promise<void>;\n validateOnChange?: boolean;\n}\nexport interface UseFormControllerResult<TName extends string = string> {\n values: FormValues<TName>;\n errors: FormErrors<TName>;\n setValues: (values: FormValues<TName>) => void;\n setFieldValue: (name: TName, value: FormFieldValue) => void;\n validate: () => FormValidationResult<TName>;\n handleSubmit: () => Promise<void>;\n reset: () => void;\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { KeyboardAvoidingViewProps as SurfaceKeyboardAvoidingViewProps } from '@ankhorage/surface';
2
+ import type { ZoraBaseProps } from '../theme/ZoraBaseProps';
3
+ export type KeyboardAvoidingViewBehavior = NonNullable<SurfaceKeyboardAvoidingViewProps['behavior']>;
4
+ export interface KeyboardAvoidingViewProps extends ZoraBaseProps, Omit<SurfaceKeyboardAvoidingViewProps, keyof ZoraBaseProps> {
5
+ }
6
+ //# sourceMappingURL=keyboard-avoiding-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard-avoiding-view.d.ts","sourceRoot":"","sources":["../../src/types/keyboard-avoiding-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,IAAI,gCAAgC,EAAE,MAAM,oBAAoB,CAAC;AAExG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5D,MAAM,MAAM,4BAA4B,GAAG,WAAW,CACpD,gCAAgC,CAAC,UAAU,CAAC,CAC7C,CAAC;AAEF,MAAM,WAAW,yBACf,SAAQ,aAAa,EAAE,IAAI,CAAC,gCAAgC,EAAE,MAAM,aAAa,CAAC;CAAG"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=keyboard-avoiding-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard-avoiding-view.js","sourceRoot":"","sources":["../../src/types/keyboard-avoiding-view.ts"],"names":[],"mappings":"","sourcesContent":["import type { KeyboardAvoidingViewProps as SurfaceKeyboardAvoidingViewProps } from '@ankhorage/surface';\n\nimport type { ZoraBaseProps } from '../theme/ZoraBaseProps';\n\nexport type KeyboardAvoidingViewBehavior = NonNullable<\n SurfaceKeyboardAvoidingViewProps['behavior']\n>;\n\nexport interface KeyboardAvoidingViewProps\n extends ZoraBaseProps, Omit<SurfaceKeyboardAvoidingViewProps, keyof ZoraBaseProps> {}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/zora",
3
3
  "type": "module",
4
- "version": "9.0.1",
4
+ "version": "11.0.0",
5
5
  "description": "Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.",
6
6
  "homepage": "https://github.com/ankhorage/zora#readme",
7
7
  "bugs": {
@@ -42,7 +42,8 @@
42
42
  "dependencies": {
43
43
  "@ankhorage/color-theory": "^0.3.0",
44
44
  "@ankhorage/contracts": "^12.0.1",
45
- "@ankhorage/surface": "^4.1.0",
45
+ "@ankhorage/surface": "^4.2.0",
46
+ "@ankhorage/utility": "^0.8.0",
46
47
  "expo-document-picker": "~57.0.1",
47
48
  "expo-image-picker": "~57.0.16"
48
49
  },
package/src/audit.test.ts CHANGED
@@ -141,17 +141,15 @@ describe('Plan 5 audit — product-facing src imports ZORA foundation, not Surfa
141
141
  join(SRC_ROOT, 'components', 'modal', 'Modal.tsx'),
142
142
  join(SRC_ROOT, 'features', 'form', 'text-input', 'adapters', 'inbound', 'TextInput.tsx'),
143
143
  join(SRC_ROOT, 'features', 'typography', 'adapters', 'inbound', 'Text.tsx'),
144
- join(SRC_ROOT, 'components', 'form', 'FormField.tsx'),
145
- join(SRC_ROOT, 'patterns', 'form-field', 'FormField.tsx'),
144
+ join(SRC_ROOT, 'features', 'form', 'adapters', 'inbound', 'FormField.tsx'),
146
145
  // Type-only imports that intentionally expose Surface-compatible prop specs.
147
146
  join(SRC_ROOT, 'components', 'checkbox', 'types.ts'),
148
- join(SRC_ROOT, 'components', 'form', 'types.ts'),
147
+ join(SRC_ROOT, 'types', 'form.ts'),
149
148
  join(SRC_ROOT, 'types', 'icon-button.ts'),
150
149
  join(SRC_ROOT, 'components', 'tabs', 'types.ts'),
151
150
  join(SRC_ROOT, 'types', 'text.ts'),
152
151
  join(SRC_ROOT, 'components', 'textarea', 'types.ts'),
153
152
  join(SRC_ROOT, 'components', 'toolbar', 'types.ts'),
154
- join(SRC_ROOT, 'patterns', 'form-field', 'types.ts'),
155
153
  join(SRC_ROOT, 'patterns', 'tile-grid', 'types.ts'),
156
154
  ]);
157
155
 
package/src/constants.ts CHANGED
@@ -11,12 +11,13 @@ export const FEATURE_MANIFEST_ELEMENTS = {
11
11
  card: ['Card', 'MediaCard', 'MetricCard', 'PostCard', 'ProductCard'],
12
12
  chip: ['Chip', 'ChipGroup'],
13
13
  'data-table': ['DataTable'],
14
+ form: ['Form', 'FormError', 'FormField'],
14
15
  button: ['Button', 'IconButton', 'ButtonGroup'],
15
16
  icon: ['Icon'],
16
17
  image: ['Image'],
18
+ 'keyboard-avoiding-view': ['KeyboardAvoidingView'],
17
19
  layout: ['Box', 'Container', 'ContentRail', 'Divider', 'Grid', 'Stack'],
18
20
  list: ['FlatList', 'SectionList'],
19
- 'splash-screen': ['SplashScreen'],
20
21
  typography: ['Heading', 'Text'],
21
22
  uploader: ['Uploader'],
22
23
  'bottom-sheet': ['BottomSheet'],
@@ -1,3 +1,5 @@
1
+ import { existsSync } from 'node:fs';
2
+
1
3
  import { expect, test } from 'bun:test';
2
4
 
3
5
  import { FEATURE_MANIFEST_ELEMENTS } from './constants';
@@ -17,6 +19,11 @@ test('every selected feature element is directly authorable through its canonica
17
19
  }
18
20
  });
19
21
 
22
+ test('form composition has one canonical feature owner', () => {
23
+ expect(existsSync('src/components/form')).toBe(false);
24
+ expect(existsSync('src/patterns/form-field')).toBe(false);
25
+ });
26
+
20
27
  test('interactive authoring retains state and event payload bindings after plugin composition', () => {
21
28
  const meta = ZORA_CORE_PLUGIN_METADATA.componentMeta;
22
29
  expect(meta.DataTable?.bindings?.props?.sort?.value.type).toBe('object');
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+
3
+ import { withZoraThemeScope } from '../../../../theme/withZoraThemeScope';
4
+ import type { FormProps } from '../../../../types/form';
5
+ import { Stack } from '../../../layout/public';
6
+ import { FormActions } from './FormActions';
7
+
8
+ function FormInner({
9
+ themeId: _themeId,
10
+ mode: _mode,
11
+ children,
12
+ onSubmit,
13
+ loading = false,
14
+ disabled = false,
15
+ submitLabel = 'Submit',
16
+ actions,
17
+ testID,
18
+ interactionPolicy,
19
+ }: FormProps) {
20
+ return (
21
+ <Stack gap="m" testID={testID}>
22
+ {children}
23
+ <FormActions
24
+ disabled={disabled}
25
+ interactionPolicy={interactionPolicy}
26
+ loading={loading}
27
+ onSubmit={() => {
28
+ if (interactionPolicy !== 'passive') void onSubmit?.();
29
+ }}
30
+ submitLabel={submitLabel}
31
+ >
32
+ {actions}
33
+ </FormActions>
34
+ </Stack>
35
+ );
36
+ }
37
+
38
+ /*** Composes explicit form fields, form errors, and submit actions. */
39
+ export const Form = withZoraThemeScope(FormInner);
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
2
 
3
- import { Button } from '../../features/button/public';
4
- import { Box, Stack } from '../../features/layout/public';
5
- import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
6
- import type { FormActionsProps } from './types';
3
+ import { withZoraThemeScope } from '../../../../theme/withZoraThemeScope';
4
+ import type { FormActionsProps } from '../../../../types/form';
5
+ import { Button } from '../../../button/public';
6
+ import { Box, Stack } from '../../../layout/public';
7
7
 
8
8
  function FormActionsInner({
9
9
  themeId: _themeId,
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+
3
+ import { useZoraTheme } from '../../../../theme/useZoraTheme';
4
+ import { withZoraThemeScope } from '../../../../theme/withZoraThemeScope';
5
+ import type { FormErrorProps } from '../../../../types/form';
6
+ import { Box } from '../../../layout/public';
7
+ import { Text } from '../../../typography/public';
8
+
9
+ function FormErrorInner({ themeId: _themeId, mode: _mode, error, testID }: FormErrorProps) {
10
+ const { theme } = useZoraTheme();
11
+ if (!error) return null;
12
+ return (
13
+ <Box borderColor={theme.colors.error} borderWidth={1} p="s" radius="m" testID={testID}>
14
+ <Text color="error" variant="bodySmall">
15
+ {error}
16
+ </Text>
17
+ </Box>
18
+ );
19
+ }
20
+
21
+ /*** Displays a form-level validation or submission error. */
22
+ export const FormError = withZoraThemeScope(FormErrorInner);
@@ -1,10 +1,10 @@
1
1
  import { Field } from '@ankhorage/surface';
2
2
  import React from 'react';
3
3
 
4
- import { Stack } from '../../features/layout/public';
5
- import { Text } from '../../features/typography/public';
6
- import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
7
- import type { FormFieldProps } from './types';
4
+ import { withZoraThemeScope } from '../../../../theme/withZoraThemeScope';
5
+ import type { FormFieldProps } from '../../../../types/form';
6
+ import { Stack } from '../../../layout/public';
7
+ import { Text } from '../../../typography/public';
8
8
 
9
9
  function FormFieldInner({
10
10
  themeId: _themeId,
@@ -13,6 +13,7 @@ function FormFieldInner({
13
13
  description,
14
14
  helperText,
15
15
  children,
16
+ interactionPolicy: _interactionPolicy,
16
17
  ...props
17
18
  }: FormFieldProps) {
18
19
  return (
@@ -37,4 +38,5 @@ function FormFieldInner({
37
38
  );
38
39
  }
39
40
 
41
+ /*** Groups one form control with its label, description, helper text, and field error. */
40
42
  export const FormField = withZoraThemeScope(FormFieldInner);
@@ -0,0 +1,9 @@
1
+ import type { ZoraComponentMeta } from '../../metadata';
2
+ export const formActionsMeta = {
3
+ name: 'FormActions',
4
+ category: 'component',
5
+ directManifestNode: false,
6
+ allowedChildren: [],
7
+ note: 'Internal form action helper.',
8
+ props: {},
9
+ } as const satisfies ZoraComponentMeta;
@@ -0,0 +1,19 @@
1
+ import type { ZoraComponentMeta } from '../../metadata';
2
+ export const formErrorMeta = {
3
+ name: 'FormError',
4
+ category: 'component',
5
+ directManifestNode: true,
6
+ allowedChildren: [],
7
+ blueprint: { label: 'Form error', defaultProps: { error: 'Unable to submit the form.' } },
8
+ bindings: {
9
+ props: {
10
+ error: {
11
+ label: 'Error',
12
+ description: 'Global form or submission error.',
13
+ value: { type: 'unknown' },
14
+ acceptsFallback: true,
15
+ },
16
+ },
17
+ },
18
+ props: { error: { type: 'string', category: 'Content', label: 'Error' } },
19
+ } as const satisfies ZoraComponentMeta;
@@ -0,0 +1,35 @@
1
+ import type { ZoraComponentMeta } from '../../metadata';
2
+ export const formFieldMeta = {
3
+ name: 'FormField',
4
+ category: 'component',
5
+ directManifestNode: true,
6
+ allowedChildren: ['Checkbox', 'CheckboxGroup', 'RadioGroup', 'TextInput'],
7
+ blueprint: { label: 'Form field', defaultProps: { label: 'Label' } },
8
+ bindings: {
9
+ props: {
10
+ errorText: {
11
+ label: 'Error text',
12
+ description: 'Field-level validation error.',
13
+ value: { type: 'unknown' },
14
+ acceptsFallback: true,
15
+ },
16
+ disabled: {
17
+ label: 'Disabled',
18
+ description: 'Whether the field is disabled.',
19
+ value: { type: 'boolean' },
20
+ acceptsFallback: true,
21
+ acceptsTransforms: true,
22
+ },
23
+ },
24
+ },
25
+ props: {
26
+ label: { type: 'string', category: 'Content', label: 'Label', default: 'Label' },
27
+ description: { type: 'string', category: 'Content', label: 'Description' },
28
+ helperText: { type: 'string', category: 'Content', label: 'Helper text' },
29
+ errorText: { type: 'string', category: 'Validation', label: 'Error text' },
30
+ required: { type: 'boolean', category: 'Validation', label: 'Required', default: false },
31
+ invalid: { type: 'boolean', category: 'Validation', label: 'Invalid', default: false },
32
+ disabled: { type: 'boolean', category: 'State', label: 'Disabled', default: false },
33
+ readOnly: { type: 'boolean', category: 'State', label: 'Read-only', default: false },
34
+ },
35
+ } as const satisfies ZoraComponentMeta;
@@ -0,0 +1,34 @@
1
+ import type { ZoraComponentMeta } from '../../metadata';
2
+ export const formMeta = {
3
+ name: 'Form',
4
+ category: 'component',
5
+ directManifestNode: true,
6
+ allowedChildren: ['FormError', 'FormField'],
7
+ blueprint: {
8
+ label: 'Form',
9
+ defaultProps: { submitLabel: 'Submit', loading: false, disabled: false },
10
+ },
11
+ bindings: {
12
+ events: {
13
+ submit: {
14
+ label: 'Submit',
15
+ description: 'Runs when the form is submitted.',
16
+ payload: { eventType: 'form.submit', fields: [] },
17
+ },
18
+ },
19
+ },
20
+ events: {
21
+ submit: {
22
+ label: 'Submit',
23
+ eventType: 'form.submit',
24
+ description: 'Emitted when the form is submitted.',
25
+ payloadFields: [],
26
+ },
27
+ },
28
+ slots: { actions: { label: 'Actions', allowedChildren: ['Button', 'ButtonGroup'] } },
29
+ props: {
30
+ submitLabel: { type: 'string', category: 'Content', label: 'Submit label', default: 'Submit' },
31
+ loading: { type: 'boolean', category: 'State', label: 'Loading', default: false },
32
+ disabled: { type: 'boolean', category: 'State', label: 'Disabled', default: false },
33
+ },
34
+ } as const satisfies ZoraComponentMeta;
@@ -1,13 +1,8 @@
1
- export { Form } from './Form';
2
- export { FormActions } from './FormActions';
3
- export { FormError } from './FormError';
4
- export { FormField } from './FormField';
5
1
  export type {
6
2
  FormActionsProps,
7
3
  FormErrorProps,
8
4
  FormErrors,
9
5
  FormFieldConfig,
10
- FormFieldControlProps,
11
6
  FormFieldInputType,
12
7
  FormFieldProps,
13
8
  FormProps,
@@ -17,6 +12,10 @@ export type {
17
12
  UseFormControllerOptions,
18
13
  UseFormControllerResult,
19
14
  ValidationRule,
20
- } from './types';
15
+ } from '../../types/form';
16
+ export { Form } from './adapters/inbound/Form';
17
+ export { FormActions } from './adapters/inbound/FormActions';
18
+ export { FormError } from './adapters/inbound/FormError';
19
+ export { FormField } from './adapters/inbound/FormField';
21
20
  export { useFormController } from './useFormController';
22
- export { hasRequiredRule, validateField, validateFields, validateValue } from './validation';
21
+ export { hasRequiredRule, validateField, validateFields, validateValue } from './utils/validation';
@@ -6,8 +6,8 @@ import type {
6
6
  FormValues,
7
7
  UseFormControllerOptions,
8
8
  UseFormControllerResult,
9
- } from './types';
10
- import { validateFields } from './validation';
9
+ } from '../../types/form';
10
+ import { validateFields } from './utils/validation';
11
11
 
12
12
  function createInitialValues<TName extends string>(
13
13
  fields: readonly { name: TName }[],
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, test } from 'bun:test';
2
2
 
3
- import type { FormFieldConfig, FormValues } from './types';
3
+ import type { FormFieldConfig, FormValues } from '../../../types/form';
4
4
  import { validateFields, validateValue } from './validation';
5
5
 
6
6
  describe('form validation', () => {
@@ -12,6 +12,9 @@ describe('form validation', () => {
12
12
 
13
13
  test('validates email fields', () => {
14
14
  expect(validateValue('not-email', [{ kind: 'email' }])).toBe('Enter a valid email address.');
15
+ expect(validateValue('person@exam_ple.com', [{ kind: 'email' }])).toBe(
16
+ 'Enter a valid email address.',
17
+ );
15
18
  expect(validateValue('person@example.com', [{ kind: 'email' }])).toBeUndefined();
16
19
  });
17
20
 
@@ -1,12 +1,12 @@
1
+ import { isEmail } from '@ankhorage/utility/regex';
2
+
1
3
  import type {
2
4
  FormFieldConfig,
3
5
  FormValidationErrors,
4
6
  FormValidationResult,
5
7
  FormValues,
6
8
  ValidationRule,
7
- } from './types';
8
-
9
- const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
9
+ } from '../../../types/form';
10
10
 
11
11
  export function hasRequiredRule(rules: readonly ValidationRule[] | undefined): boolean {
12
12
  return rules?.some((rule) => rule.kind === 'required') ?? false;
@@ -27,11 +27,7 @@ export function validateValue(
27
27
  return rule.message ?? 'This field is required.';
28
28
  }
29
29
 
30
- if (
31
- rule.kind === 'email' &&
32
- normalizedValue.length > 0 &&
33
- !emailPattern.test(normalizedValue)
34
- ) {
30
+ if (rule.kind === 'email' && normalizedValue.length > 0 && !isEmail(normalizedValue)) {
35
31
  return rule.message ?? 'Enter a valid email address.';
36
32
  }
37
33
 
@@ -0,0 +1,18 @@
1
+ import { KeyboardAvoidingView as SurfaceKeyboardAvoidingView } from '@ankhorage/surface';
2
+ import React from 'react';
3
+
4
+ import { withZoraThemeScope } from '../../../../theme/withZoraThemeScope';
5
+ import type { KeyboardAvoidingViewProps } from '../../../../types/keyboard-avoiding-view';
6
+
7
+ /*** Native keyboard-aware container exposed through the canonical ZORA feature boundary. */
8
+ export const KeyboardAvoidingView = withZoraThemeScope(KeyboardAvoidingViewInner);
9
+
10
+ /*** Forwards React Native keyboard avoidance and view props without adding platform behavior. */
11
+ function KeyboardAvoidingViewInner({
12
+ themeId: _themeId,
13
+ mode: _mode,
14
+ interactionPolicy: _interactionPolicy,
15
+ ...props
16
+ }: KeyboardAvoidingViewProps) {
17
+ return <SurfaceKeyboardAvoidingView {...props} />;
18
+ }
@@ -0,0 +1,38 @@
1
+ import { CONTAINER_ALLOWED_CHILDREN } from '../../metadata/allowedChildren';
2
+ import type { ZoraComponentMeta } from '../../metadata/types';
3
+
4
+ export const keyboardAvoidingViewMeta = {
5
+ name: 'KeyboardAvoidingView',
6
+ category: 'layout',
7
+ directManifestNode: true,
8
+ allowedChildren: [...CONTAINER_ALLOWED_CHILDREN],
9
+ blueprint: {
10
+ label: 'Keyboard avoiding view',
11
+ defaultProps: {
12
+ behavior: 'padding',
13
+ enabled: true,
14
+ keyboardVerticalOffset: 0,
15
+ },
16
+ },
17
+ props: {
18
+ behavior: {
19
+ type: 'enum',
20
+ category: 'Behavior',
21
+ label: 'Behavior',
22
+ enum: ['height', 'position', 'padding'],
23
+ default: 'padding',
24
+ },
25
+ enabled: {
26
+ type: 'boolean',
27
+ category: 'Behavior',
28
+ label: 'Enabled',
29
+ default: true,
30
+ },
31
+ keyboardVerticalOffset: {
32
+ type: 'number',
33
+ category: 'Layout',
34
+ label: 'Keyboard vertical offset',
35
+ default: 0,
36
+ },
37
+ },
38
+ } as const satisfies ZoraComponentMeta;
@@ -0,0 +1,5 @@
1
+ export {
2
+ type KeyboardAvoidingViewBehavior,
3
+ type KeyboardAvoidingViewProps,
4
+ } from '../../types/keyboard-avoiding-view';
5
+ export { KeyboardAvoidingView } from './adapters/inbound/KeyboardAvoidingView';
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { Modal } from '../../../../components/modal';
4
- import { FormField } from '../../../../patterns/form-field';
5
4
  import { withZoraThemeScope } from '../../../../theme/withZoraThemeScope';
6
5
  import type { UploadAsset, UploaderProps, UploadType } from '../../../../types/upload';
7
6
  import { Button } from '../../../button/public';
7
+ import { FormField } from '../../../form/public';
8
8
  import { Icon } from '../../../icon/public';
9
9
  import { Image } from '../../../image/public';
10
10
  import { Box, Stack } from '../../../layout/public';
package/src/index.ts CHANGED
@@ -4,33 +4,6 @@ export type { BreadcrumbItem, BreadcrumbsProps } from './components/breadcrumbs'
4
4
  export { Breadcrumbs } from './components/breadcrumbs';
5
5
  export type { DatePickerProps, DatePickerValue } from './components/date-picker';
6
6
  export { DatePicker } from './components/date-picker';
7
- export type {
8
- FormActionsProps,
9
- FormErrorProps,
10
- FormErrors,
11
- FormFieldConfig,
12
- FormFieldControlProps,
13
- FormFieldInputType,
14
- FormFieldProps,
15
- FormProps,
16
- FormValidationErrors,
17
- FormValidationResult,
18
- FormValues,
19
- UseFormControllerOptions,
20
- UseFormControllerResult,
21
- ValidationRule,
22
- } from './components/form';
23
- export {
24
- Form,
25
- FormActions,
26
- FormError,
27
- FormField,
28
- hasRequiredRule,
29
- useFormController,
30
- validateField,
31
- validateFields,
32
- validateValue,
33
- } from './components/form';
34
7
  export type {
35
8
  GradientColor,
36
9
  GradientColors,
@@ -139,6 +112,32 @@ export {
139
112
  type CheckboxGroupProps,
140
113
  type CheckboxProps,
141
114
  } from './features/form/checkbox/public';
115
+ export type {
116
+ FormActionsProps,
117
+ FormErrorProps,
118
+ FormErrors,
119
+ FormFieldConfig,
120
+ FormFieldInputType,
121
+ FormFieldProps,
122
+ FormProps,
123
+ FormValidationErrors,
124
+ FormValidationResult,
125
+ FormValues,
126
+ UseFormControllerOptions,
127
+ UseFormControllerResult,
128
+ ValidationRule,
129
+ } from './features/form/public';
130
+ export {
131
+ Form,
132
+ FormActions,
133
+ FormError,
134
+ FormField,
135
+ hasRequiredRule,
136
+ useFormController,
137
+ validateField,
138
+ validateFields,
139
+ validateValue,
140
+ } from './features/form/public';
142
141
  export {
143
142
  Radio,
144
143
  RadioGroup,
@@ -155,6 +154,11 @@ export type { IconProps } from './features/icon/public';
155
154
  export { Icon } from './features/icon/public';
156
155
  export type { ImageFit, ImageProps, SurfaceImageSource } from './features/image/public';
157
156
  export { Image } from './features/image/public';
157
+ export {
158
+ KeyboardAvoidingView,
159
+ type KeyboardAvoidingViewBehavior,
160
+ type KeyboardAvoidingViewProps,
161
+ } from './features/keyboard-avoiding-view/public';
158
162
  export type {
159
163
  BoxProps,
160
164
  ContainerProps,
@@ -185,8 +189,6 @@ export {
185
189
  ProgressRing,
186
190
  type ProgressRingProps,
187
191
  } from './features/progress/public';
188
- export type { SplashScreenLogoShape, SplashScreenProps } from './features/splash-screen/public';
189
- export { SplashScreen } from './features/splash-screen/public';
190
192
  export type { SurfaceProps, SurfaceVariant } from './features/surface/public';
191
193
  export { Surface } from './features/surface/public';
192
194
  export type {