@strictly/react-form 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/.eslintrc.cjs +26 -0
  2. package/.out/.storybook/main.d.ts +3 -0
  3. package/.out/.storybook/main.js +32 -0
  4. package/.out/.storybook/preview.d.ts +4 -0
  5. package/.out/.storybook/preview.js +20 -0
  6. package/.out/.vitest/install_deterministic_random.d.ts +2 -0
  7. package/.out/.vitest/install_deterministic_random.js +15 -0
  8. package/.out/.vitest/install_storybook_preview.d.ts +1 -0
  9. package/.out/.vitest/install_storybook_preview.js +7 -0
  10. package/.out/.vitest/match_media.d.ts +1 -0
  11. package/.out/.vitest/match_media.js +5 -0
  12. package/.out/.vitest/resize_observer.d.ts +1 -0
  13. package/.out/.vitest/resize_observer.js +4 -0
  14. package/.out/core/mobx/field_adapter.d.ts +9 -0
  15. package/.out/core/mobx/field_adapter.js +1 -0
  16. package/.out/core/mobx/field_adapter_builder.d.ts +22 -0
  17. package/.out/core/mobx/field_adapter_builder.js +56 -0
  18. package/.out/core/mobx/flattened_adapters_of_fields.d.ts +9 -0
  19. package/.out/core/mobx/flattened_adapters_of_fields.js +1 -0
  20. package/.out/core/mobx/flattened_list_type_defs_of.d.ts +8 -0
  21. package/.out/core/mobx/flattened_list_type_defs_of.js +1 -0
  22. package/.out/core/mobx/form_presenter.d.ts +61 -0
  23. package/.out/core/mobx/form_presenter.js +425 -0
  24. package/.out/core/mobx/specs/flattened_adapters_of_fields.tests.d.ts +1 -0
  25. package/.out/core/mobx/specs/flattened_adapters_of_fields.tests.js +13 -0
  26. package/.out/core/mobx/specs/flattened_list_type_defs_of.tests.d.ts +1 -0
  27. package/.out/core/mobx/specs/flattened_list_type_defs_of.tests.js +16 -0
  28. package/.out/core/mobx/specs/form_presenter.tests.d.ts +1 -0
  29. package/.out/core/mobx/specs/form_presenter.tests.js +697 -0
  30. package/.out/core/mobx/types.d.ts +19 -0
  31. package/.out/core/mobx/types.js +1 -0
  32. package/.out/core/props.d.ts +12 -0
  33. package/.out/core/props.js +1 -0
  34. package/.out/field_converters/chain_field_converter.d.ts +3 -0
  35. package/.out/field_converters/chain_field_converter.js +46 -0
  36. package/.out/field_converters/identity_converter.d.ts +3 -0
  37. package/.out/field_converters/identity_converter.js +14 -0
  38. package/.out/field_converters/integer_to_string_converter.d.ts +7 -0
  39. package/.out/field_converters/integer_to_string_converter.js +26 -0
  40. package/.out/field_converters/list_converter.d.ts +2 -0
  41. package/.out/field_converters/list_converter.js +8 -0
  42. package/.out/field_converters/maybe_identity_converter.d.ts +8 -0
  43. package/.out/field_converters/maybe_identity_converter.js +15 -0
  44. package/.out/field_converters/nullable_to_boolean_converter.d.ts +11 -0
  45. package/.out/field_converters/nullable_to_boolean_converter.js +31 -0
  46. package/.out/field_converters/select_value_type_converter.d.ts +23 -0
  47. package/.out/field_converters/select_value_type_converter.js +60 -0
  48. package/.out/field_converters/trimming_string_converter.d.ts +6 -0
  49. package/.out/field_converters/trimming_string_converter.js +14 -0
  50. package/.out/field_converters/validating_converter.d.ts +3 -0
  51. package/.out/field_converters/validating_converter.js +21 -0
  52. package/.out/field_validators/minimum_string_length_field_validator.d.ts +2 -0
  53. package/.out/field_validators/minimum_string_length_field_validator.js +8 -0
  54. package/.out/field_value_factories/prototyping_field_value_factory.d.ts +2 -0
  55. package/.out/field_value_factories/prototyping_field_value_factory.js +5 -0
  56. package/.out/index.d.ts +16 -0
  57. package/.out/index.js +16 -0
  58. package/.out/mantine/create_checkbox.d.ts +9 -0
  59. package/.out/mantine/create_checkbox.js +37 -0
  60. package/.out/mantine/create_list.d.ts +15 -0
  61. package/.out/mantine/create_list.js +16 -0
  62. package/.out/mantine/create_pill.d.ts +7 -0
  63. package/.out/mantine/create_pill.js +15 -0
  64. package/.out/mantine/create_radio.d.ts +8 -0
  65. package/.out/mantine/create_radio.js +10 -0
  66. package/.out/mantine/create_radio_group.d.ts +9 -0
  67. package/.out/mantine/create_radio_group.js +34 -0
  68. package/.out/mantine/create_text_input.d.ts +19 -0
  69. package/.out/mantine/create_text_input.js +38 -0
  70. package/.out/mantine/create_value_input.d.ts +17 -0
  71. package/.out/mantine/create_value_input.js +38 -0
  72. package/.out/mantine/hooks.d.ts +56 -0
  73. package/.out/mantine/hooks.js +135 -0
  74. package/.out/mantine/specs/checkbox_constants.d.ts +1 -0
  75. package/.out/mantine/specs/checkbox_constants.js +1 -0
  76. package/.out/mantine/specs/checkbox_hooks.stories.d.ts +13 -0
  77. package/.out/mantine/specs/checkbox_hooks.stories.js +63 -0
  78. package/.out/mantine/specs/checkbox_hooks.tests.d.ts +1 -0
  79. package/.out/mantine/specs/checkbox_hooks.tests.js +74 -0
  80. package/.out/mantine/specs/list_hooks.stories.d.ts +11 -0
  81. package/.out/mantine/specs/list_hooks.stories.js +48 -0
  82. package/.out/mantine/specs/list_hooks.tests.d.ts +1 -0
  83. package/.out/mantine/specs/list_hooks.tests.js +12 -0
  84. package/.out/mantine/specs/radio_group_constants.d.ts +4 -0
  85. package/.out/mantine/specs/radio_group_constants.js +11 -0
  86. package/.out/mantine/specs/radio_group_hooks.stories.d.ts +14 -0
  87. package/.out/mantine/specs/radio_group_hooks.stories.js +68 -0
  88. package/.out/mantine/specs/radio_group_hooks.tests.d.ts +1 -0
  89. package/.out/mantine/specs/radio_group_hooks.tests.js +62 -0
  90. package/.out/mantine/specs/select_hooks.stories.d.ts +12 -0
  91. package/.out/mantine/specs/select_hooks.stories.js +57 -0
  92. package/.out/mantine/specs/select_hooks.tests.d.ts +1 -0
  93. package/.out/mantine/specs/select_hooks.tests.js +12 -0
  94. package/.out/mantine/specs/select_hooks_constant.d.ts +1 -0
  95. package/.out/mantine/specs/select_hooks_constant.js +1 -0
  96. package/.out/mantine/specs/text_input_constants.d.ts +1 -0
  97. package/.out/mantine/specs/text_input_constants.js +1 -0
  98. package/.out/mantine/specs/text_input_hooks.stories.d.ts +21 -0
  99. package/.out/mantine/specs/text_input_hooks.stories.js +88 -0
  100. package/.out/mantine/specs/text_input_hooks.tests.d.ts +1 -0
  101. package/.out/mantine/specs/text_input_hooks.tests.js +79 -0
  102. package/.out/mantine/specs/value_input_constants.d.ts +2 -0
  103. package/.out/mantine/specs/value_input_constants.js +2 -0
  104. package/.out/mantine/specs/value_input_hooks.stories.d.ts +23 -0
  105. package/.out/mantine/specs/value_input_hooks.stories.js +124 -0
  106. package/.out/mantine/specs/value_input_hooks.tests.d.ts +1 -0
  107. package/.out/mantine/specs/value_input_hooks.tests.js +12 -0
  108. package/.out/mantine/types.d.ts +11 -0
  109. package/.out/mantine/types.js +1 -0
  110. package/.out/tsconfig.json +27 -0
  111. package/.out/tsconfig.tsbuildinfo +1 -0
  112. package/.out/tsup.config.d.ts +3 -0
  113. package/.out/tsup.config.js +12 -0
  114. package/.out/types/all_fields_of_fields.d.ts +5 -0
  115. package/.out/types/all_fields_of_fields.js +1 -0
  116. package/.out/types/boolean_fields_of_fields.d.ts +5 -0
  117. package/.out/types/boolean_fields_of_fields.js +1 -0
  118. package/.out/types/error_type_of_field.d.ts +2 -0
  119. package/.out/types/error_type_of_field.js +1 -0
  120. package/.out/types/field.d.ts +7 -0
  121. package/.out/types/field.js +1 -0
  122. package/.out/types/field_converters.d.ts +29 -0
  123. package/.out/types/field_converters.js +5 -0
  124. package/.out/types/field_validator.d.ts +3 -0
  125. package/.out/types/field_validator.js +1 -0
  126. package/.out/types/flattened_form_fields_of.d.ts +9 -0
  127. package/.out/types/flattened_form_fields_of.js +1 -0
  128. package/.out/types/list_fields_of_fields.d.ts +5 -0
  129. package/.out/types/list_fields_of_fields.js +1 -0
  130. package/.out/types/specs/boolean_fields_of_fields.tests.d.ts +1 -0
  131. package/.out/types/specs/boolean_fields_of_fields.tests.js +11 -0
  132. package/.out/types/specs/error_type_of_field.tests.d.ts +1 -0
  133. package/.out/types/specs/error_type_of_field.tests.js +7 -0
  134. package/.out/types/specs/flattened_form_fields_of.tests.d.ts +1 -0
  135. package/.out/types/specs/flattened_form_fields_of.tests.js +13 -0
  136. package/.out/types/specs/string_fields_of_fields.tests.d.ts +1 -0
  137. package/.out/types/specs/string_fields_of_fields.tests.js +19 -0
  138. package/.out/types/specs/value_type_of_field.tests.d.ts +1 -0
  139. package/.out/types/specs/value_type_of_field.tests.js +7 -0
  140. package/.out/types/string_fields_of_fields.d.ts +5 -0
  141. package/.out/types/string_fields_of_fields.js +1 -0
  142. package/.out/types/value_type_of_field.d.ts +2 -0
  143. package/.out/types/value_type_of_field.js +1 -0
  144. package/.out/util/partial.d.ts +11 -0
  145. package/.out/util/partial.js +74 -0
  146. package/.out/vitest.workspace.d.ts +2 -0
  147. package/.out/vitest.workspace.js +22 -0
  148. package/.storybook/main.ts +40 -0
  149. package/.storybook/preview.tsx +28 -0
  150. package/.storybook/vite.config.mts +38 -0
  151. package/.turbo/turbo-build.log +18 -0
  152. package/.turbo/turbo-check-types.log +3 -0
  153. package/.turbo/turbo-release$colon$exports.log +3 -0
  154. package/.vitest/install_deterministic_random.ts +17 -0
  155. package/.vitest/install_storybook_preview.ts +9 -0
  156. package/.vitest/match_media.ts +7 -0
  157. package/.vitest/resize_observer.ts +5 -0
  158. package/README.md +2 -0
  159. package/core/mobx/field_adapter.ts +32 -0
  160. package/core/mobx/field_adapter_builder.ts +313 -0
  161. package/core/mobx/flattened_adapters_of_fields.ts +35 -0
  162. package/core/mobx/flattened_list_type_defs_of.ts +17 -0
  163. package/core/mobx/form_presenter.ts +705 -0
  164. package/core/mobx/specs/flattened_adapters_of_fields.tests.ts +72 -0
  165. package/core/mobx/specs/flattened_list_type_defs_of.tests.ts +35 -0
  166. package/core/mobx/specs/form_presenter.tests.ts +989 -0
  167. package/core/mobx/types.ts +54 -0
  168. package/core/props.ts +21 -0
  169. package/dist/index.cjs +11479 -0
  170. package/dist/index.d.cts +345 -0
  171. package/dist/index.d.ts +345 -0
  172. package/dist/index.js +11486 -0
  173. package/field_converters/chain_field_converter.ts +74 -0
  174. package/field_converters/identity_converter.ts +39 -0
  175. package/field_converters/integer_to_string_converter.ts +32 -0
  176. package/field_converters/list_converter.ts +15 -0
  177. package/field_converters/maybe_identity_converter.ts +23 -0
  178. package/field_converters/nullable_to_boolean_converter.ts +56 -0
  179. package/field_converters/select_value_type_converter.ts +141 -0
  180. package/field_converters/trimming_string_converter.ts +23 -0
  181. package/field_converters/validating_converter.ts +35 -0
  182. package/field_validators/minimum_string_length_field_validator.ts +13 -0
  183. package/field_value_factories/prototyping_field_value_factory.ts +11 -0
  184. package/index.ts +16 -0
  185. package/mantine/create_checkbox.tsx +79 -0
  186. package/mantine/create_list.tsx +58 -0
  187. package/mantine/create_pill.tsx +43 -0
  188. package/mantine/create_radio.tsx +36 -0
  189. package/mantine/create_radio_group.tsx +71 -0
  190. package/mantine/create_text_input.tsx +80 -0
  191. package/mantine/create_value_input.tsx +81 -0
  192. package/mantine/hooks.tsx +394 -0
  193. package/mantine/specs/__snapshots__/check_box_hooks.tests.tsx.snap +227 -0
  194. package/mantine/specs/__snapshots__/checkbox_hooks.tests.tsx.snap +227 -0
  195. package/mantine/specs/__snapshots__/list_hooks.tests.tsx.snap +68 -0
  196. package/mantine/specs/__snapshots__/radio_group_hooks.tests.tsx.snap +695 -0
  197. package/mantine/specs/__snapshots__/select_hooks.tests.tsx.snap +225 -0
  198. package/mantine/specs/__snapshots__/text_input_hooks.tests.tsx.snap +202 -0
  199. package/mantine/specs/__snapshots__/value_input_hooks.tests.tsx.snap +613 -0
  200. package/mantine/specs/checkbox_constants.ts +1 -0
  201. package/mantine/specs/checkbox_hooks.stories.tsx +79 -0
  202. package/mantine/specs/checkbox_hooks.tests.tsx +100 -0
  203. package/mantine/specs/list_hooks.stories.tsx +83 -0
  204. package/mantine/specs/list_hooks.tests.tsx +15 -0
  205. package/mantine/specs/radio_group_constants.ts +12 -0
  206. package/mantine/specs/radio_group_hooks.stories.tsx +103 -0
  207. package/mantine/specs/radio_group_hooks.tests.tsx +92 -0
  208. package/mantine/specs/select_hooks.stories.tsx +77 -0
  209. package/mantine/specs/select_hooks.tests.tsx +14 -0
  210. package/mantine/specs/select_hooks_constant.ts +1 -0
  211. package/mantine/specs/text_input_constants.ts +1 -0
  212. package/mantine/specs/text_input_hooks.stories.tsx +124 -0
  213. package/mantine/specs/text_input_hooks.tests.tsx +106 -0
  214. package/mantine/specs/value_input_constants.ts +2 -0
  215. package/mantine/specs/value_input_hooks.stories.tsx +182 -0
  216. package/mantine/specs/value_input_hooks.tests.tsx +14 -0
  217. package/mantine/types.ts +13 -0
  218. package/package.exports.json +18 -0
  219. package/package.json +74 -0
  220. package/tsconfig.build.json +13 -0
  221. package/tsconfig.json +27 -0
  222. package/tsup.config.ts +16 -0
  223. package/types/all_fields_of_fields.ts +9 -0
  224. package/types/boolean_fields_of_fields.ts +8 -0
  225. package/types/error_type_of_field.ts +3 -0
  226. package/types/field.ts +9 -0
  227. package/types/field_converters.ts +64 -0
  228. package/types/field_validator.ts +7 -0
  229. package/types/flattened_form_fields_of.ts +16 -0
  230. package/types/list_fields_of_fields.ts +7 -0
  231. package/types/specs/boolean_fields_of_fields.tests.ts +23 -0
  232. package/types/specs/error_type_of_field.tests.ts +10 -0
  233. package/types/specs/flattened_form_fields_of.tests.ts +43 -0
  234. package/types/specs/string_fields_of_fields.tests.ts +40 -0
  235. package/types/specs/value_type_of_field.tests.ts +10 -0
  236. package/types/string_fields_of_fields.ts +6 -0
  237. package/types/value_type_of_field.ts +3 -0
  238. package/util/partial.tsx +200 -0
  239. package/vitest.workspace.ts +26 -0
@@ -0,0 +1,36 @@
1
+ import { type RadioProps } from '@mantine/core'
2
+ import { type ComponentType } from 'react'
3
+ import {
4
+ type Fields,
5
+ } from 'types/field'
6
+ import { type StringFieldsOfFields } from 'types/string_fields_of_fields'
7
+ import { type ValueTypeOfField } from 'types/value_type_of_field'
8
+ import { createUnsafePartialObserverComponent } from 'util/partial'
9
+ import {
10
+ type MantineFieldComponent,
11
+ type MantineForm,
12
+ } from './types'
13
+
14
+ export type SuppliedRadioProps = Pick<RadioProps, 'value' | 'disabled'>
15
+
16
+ export function createRadio<
17
+ F extends Fields,
18
+ K extends keyof StringFieldsOfFields<F>,
19
+ Props extends SuppliedRadioProps,
20
+ >(
21
+ this: MantineForm<F>,
22
+ valuePath: K,
23
+ value: ValueTypeOfField<F[K]>,
24
+ Radio: ComponentType<Props>,
25
+ ): MantineFieldComponent<SuppliedRadioProps, Props> {
26
+ const propSource = () => {
27
+ return {
28
+ disabled: this.fields[valuePath].disabled,
29
+ value,
30
+ }
31
+ }
32
+ return createUnsafePartialObserverComponent(
33
+ Radio,
34
+ propSource,
35
+ )
36
+ }
@@ -0,0 +1,71 @@
1
+ import {
2
+ type RadioGroupProps,
3
+ } from '@mantine/core'
4
+ import { type ComponentType } from 'react'
5
+ import { type ErrorTypeOfField } from 'types/error_type_of_field'
6
+ import {
7
+ type Fields,
8
+ } from 'types/field'
9
+ import { type StringFieldsOfFields } from 'types/string_fields_of_fields'
10
+ import {
11
+ createUnsafePartialObserverComponent,
12
+ } from 'util/partial'
13
+ import { type ErrorRenderer } from './hooks'
14
+ import {
15
+ type MantineFieldComponent,
16
+ type MantineForm,
17
+ } from './types'
18
+
19
+ export type SuppliedRadioGroupProps = Pick<
20
+ RadioGroupProps,
21
+ 'name' | 'value' | 'required' | 'error' | 'onChange' | 'onFocus' | 'onBlur' | 'onKeyUp'
22
+ >
23
+
24
+ export function createRadioGroup<
25
+ F extends Fields,
26
+ K extends keyof StringFieldsOfFields<F>,
27
+ Props extends SuppliedRadioGroupProps,
28
+ >(
29
+ this: MantineForm<F>,
30
+ valuePath: K,
31
+ RadioGroup: ComponentType<Props>,
32
+ ErrorRenderer: ErrorRenderer<ErrorTypeOfField<F[K]>>,
33
+ ): MantineFieldComponent<SuppliedRadioGroupProps, Props> {
34
+ const onChange = (value: string) => {
35
+ this.onFieldValueChange?.(valuePath, value)
36
+ }
37
+ const onFocus = () => {
38
+ this.onFieldFocus?.(valuePath)
39
+ }
40
+ const onBlur = () => {
41
+ this.onFieldBlur?.(valuePath)
42
+ }
43
+ const onKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
44
+ if (e.key === 'Enter') {
45
+ if (this.onFieldSubmit?.(valuePath)) {
46
+ e.preventDefault()
47
+ }
48
+ }
49
+ }
50
+
51
+ const propSource = () => {
52
+ const {
53
+ required,
54
+ value,
55
+ error,
56
+ } = this.fields[valuePath]
57
+
58
+ return {
59
+ name: valuePath,
60
+ value,
61
+ required,
62
+ error: error && <ErrorRenderer error={error} />,
63
+ onChange,
64
+ onFocus,
65
+ onBlur,
66
+ onKeyUp,
67
+ }
68
+ }
69
+
70
+ return createUnsafePartialObserverComponent(RadioGroup, propSource)
71
+ }
@@ -0,0 +1,80 @@
1
+ import { type ErrorTypeOfField } from 'types/error_type_of_field'
2
+ import { type Fields } from 'types/field'
3
+ import { type StringFieldsOfFields } from 'types/string_fields_of_fields'
4
+ import { createUnsafePartialObserverComponent } from 'util/partial'
5
+ import { type ErrorRenderer } from './hooks'
6
+ import {
7
+ type MantineFieldComponent,
8
+ type MantineForm,
9
+ } from './types'
10
+
11
+ export type TextInputTarget = Element & {
12
+ value: string,
13
+ }
14
+
15
+ export type SuppliedTextInputProps<
16
+ T extends TextInputTarget = TextInputTarget,
17
+ > = Partial<{
18
+ name: string,
19
+ value: string | number | readonly string[] | undefined,
20
+ disabled: boolean,
21
+ required: boolean,
22
+ onChange: (e: React.ChangeEvent<T>) => void,
23
+ onFocus: (e: React.FocusEvent<T>) => void,
24
+ onBlur: (e: React.FocusEvent<T>) => void,
25
+ onKeyUp: (e: React.KeyboardEvent<T>) => void,
26
+ }>
27
+
28
+ export function createTextInput<
29
+ F extends Fields,
30
+ K extends keyof StringFieldsOfFields<F>,
31
+ Props extends SuppliedTextInputProps,
32
+ >(
33
+ this: MantineForm<F>,
34
+ valuePath: K,
35
+ TextInput: React.ComponentType<Props>,
36
+ ErrorRenderer: ErrorRenderer<ErrorTypeOfField<F[K]>>,
37
+ ): MantineFieldComponent<SuppliedTextInputProps, Props> {
38
+ const onChange = (e: React.ChangeEvent<TextInputTarget>) => {
39
+ this.onFieldValueChange?.(valuePath, e.target.value)
40
+ }
41
+ const onFocus = () => {
42
+ this.onFieldFocus?.(valuePath)
43
+ }
44
+ const onBlur = () => {
45
+ this.onFieldBlur?.(valuePath)
46
+ }
47
+ const onKeyUp = (e: React.KeyboardEvent) => {
48
+ if (e.key === 'Enter') {
49
+ if (this.onFieldSubmit?.(valuePath)) {
50
+ e.preventDefault()
51
+ }
52
+ }
53
+ }
54
+
55
+ const propSource = () => {
56
+ const {
57
+ disabled,
58
+ required,
59
+ value,
60
+ error,
61
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
62
+ } = this.fields[valuePath as string]
63
+ return {
64
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
65
+ name: valuePath as string,
66
+ value,
67
+ disabled,
68
+ required,
69
+ error: error && <ErrorRenderer error={error} />,
70
+ onChange,
71
+ onFocus,
72
+ onBlur,
73
+ onKeyUp,
74
+ }
75
+ }
76
+ return createUnsafePartialObserverComponent<typeof TextInput, SuppliedTextInputProps>(
77
+ TextInput,
78
+ propSource,
79
+ )
80
+ }
@@ -0,0 +1,81 @@
1
+ import { type AllFieldsOfFields } from 'types/all_fields_of_fields'
2
+ import { type ErrorTypeOfField } from 'types/error_type_of_field'
3
+ import { type Fields } from 'types/field'
4
+ import { type ValueTypeOfField } from 'types/value_type_of_field'
5
+ import { createUnsafePartialObserverComponent } from 'util/partial'
6
+ import { type ErrorRenderer } from './hooks'
7
+ import {
8
+ type MantineFieldComponent,
9
+ type MantineForm,
10
+ } from './types'
11
+
12
+ export type SuppliedValueInputProps<
13
+ V,
14
+ T extends Element = Element,
15
+ > = Partial<{
16
+ name: string,
17
+ value: V,
18
+ disabled: boolean,
19
+ required: boolean,
20
+ onChange: (value: V) => void,
21
+ onFocus: (e: React.FocusEvent<T>) => void,
22
+ onBlur: (e: React.FocusEvent<T>) => void,
23
+ onKeyUp: (e: React.KeyboardEvent<T>) => void,
24
+ }>
25
+
26
+ export function createValueInput<
27
+ F extends Fields,
28
+ K extends keyof AllFieldsOfFields<F>,
29
+ Props extends SuppliedValueInputProps<ValueTypeOfField<F[K]>>,
30
+ >(
31
+ this: MantineForm<F>,
32
+ valuePath: K,
33
+ ValueInput: React.ComponentType<Props>,
34
+ ErrorRenderer: ErrorRenderer<ErrorTypeOfField<F[K]>>,
35
+ ): MantineFieldComponent<SuppliedValueInputProps<ValueTypeOfField<F[K]>>, Props> {
36
+ const onChange = (value: ValueTypeOfField<F[K]>) => {
37
+ this.onFieldValueChange?.(valuePath, value)
38
+ }
39
+ const onFocus = () => {
40
+ this.onFieldFocus?.(valuePath)
41
+ }
42
+ const onBlur = () => {
43
+ this.onFieldBlur?.(valuePath)
44
+ }
45
+ const onKeyUp = (e: React.KeyboardEvent) => {
46
+ if (e.key === 'Enter') {
47
+ if (this.onFieldSubmit?.(valuePath)) {
48
+ e.preventDefault()
49
+ }
50
+ }
51
+ }
52
+
53
+ const propSource = () => {
54
+ const {
55
+ disabled,
56
+ required,
57
+ value,
58
+ error,
59
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
60
+ } = this.fields[valuePath as string]
61
+ return {
62
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
63
+ name: valuePath as string,
64
+ value,
65
+ disabled,
66
+ required,
67
+ error: error && <ErrorRenderer error={error} />,
68
+ onChange,
69
+ onFocus,
70
+ onBlur,
71
+ onKeyUp,
72
+ }
73
+ }
74
+ return createUnsafePartialObserverComponent<
75
+ typeof ValueInput,
76
+ SuppliedValueInputProps<ValueTypeOfField<F[K]>>
77
+ >(
78
+ ValueInput,
79
+ propSource,
80
+ )
81
+ }
@@ -0,0 +1,394 @@
1
+ import {
2
+ Checkbox as CheckboxImpl,
3
+ type CheckboxProps,
4
+ Pill as PillImpl,
5
+ type PillProps,
6
+ Radio as RadioImpl,
7
+ type RadioGroupProps,
8
+ type RadioProps,
9
+ Select,
10
+ type SelectProps,
11
+ TextInput as TextInputImpl,
12
+ type TextInputProps,
13
+ } from '@mantine/core'
14
+ import {
15
+ Cache,
16
+ type ElementOfArray,
17
+ } from '@strictly/base'
18
+ import { type FormProps } from 'core/props'
19
+ import {
20
+ observable,
21
+ runInAction,
22
+ } from 'mobx'
23
+ import {
24
+ type ComponentProps,
25
+ type ComponentType,
26
+ type PropsWithChildren,
27
+ useEffect,
28
+ useMemo,
29
+ } from 'react'
30
+ import { type AllFieldsOfFields } from 'types/all_fields_of_fields'
31
+ import { type BooleanFieldsOfFields } from 'types/boolean_fields_of_fields'
32
+ import { type ErrorTypeOfField } from 'types/error_type_of_field'
33
+ import {
34
+ type Fields,
35
+ } from 'types/field'
36
+ import { type ListFieldsOfFields } from 'types/list_fields_of_fields'
37
+ import { type StringFieldsOfFields } from 'types/string_fields_of_fields'
38
+ import { type ValueTypeOfField } from 'types/value_type_of_field'
39
+ import {
40
+ createCheckbox,
41
+ type SuppliedCheckboxProps,
42
+ } from './create_checkbox'
43
+ import {
44
+ createList,
45
+ DefaultList,
46
+ type SuppliedListProps,
47
+ } from './create_list'
48
+ import {
49
+ createPill,
50
+ type SuppliedPillProps,
51
+ } from './create_pill'
52
+ import {
53
+ createRadio,
54
+ type SuppliedRadioProps,
55
+ } from './create_radio'
56
+ import {
57
+ createRadioGroup,
58
+ type SuppliedRadioGroupProps,
59
+ } from './create_radio_group'
60
+ import {
61
+ createTextInput,
62
+ type SuppliedTextInputProps,
63
+ } from './create_text_input'
64
+ import {
65
+ createValueInput,
66
+ type SuppliedValueInputProps,
67
+ } from './create_value_input'
68
+ import {
69
+ type MantineFieldComponent,
70
+ type MantineForm,
71
+ } from './types'
72
+
73
+ function SimpleSelect(props: SelectProps & {
74
+ onChange?: (value: string | null) => void,
75
+ }) {
76
+ return <Select {...props} />
77
+ }
78
+
79
+ export type ErrorRendererProps<E> = {
80
+ error: E,
81
+ }
82
+
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ export type ErrorRenderer<E = any> = React.ComponentType<ErrorRendererProps<E>>
85
+
86
+ function DefaultErrorRenderer({
87
+ error,
88
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
+ }: ErrorRendererProps<any>) {
90
+ return error
91
+ }
92
+
93
+ export function useMantineForm<
94
+ F extends Fields,
95
+ >({
96
+ onFieldValueChange,
97
+ onFieldBlur,
98
+ onFieldFocus,
99
+ onFieldSubmit,
100
+ fields,
101
+ }: FormProps<F>): MantineFormImpl<F> {
102
+ const form = useMemo(
103
+ function () {
104
+ return new MantineFormImpl(fields)
105
+ },
106
+ // handled separately below
107
+ // eslint-disable-next-line react-hooks/exhaustive-deps
108
+ [],
109
+ )
110
+ useEffect(function () {
111
+ runInAction(function () {
112
+ form.fields = fields
113
+ })
114
+ }, [
115
+ form,
116
+ fields,
117
+ ])
118
+ useEffect(function () {
119
+ form.onFieldValueChange = onFieldValueChange
120
+ }, [
121
+ form,
122
+ onFieldValueChange,
123
+ ])
124
+ useEffect(function () {
125
+ form.onFieldBlur = onFieldBlur
126
+ }, [
127
+ form,
128
+ onFieldBlur,
129
+ ])
130
+ useEffect(function () {
131
+ form.onFieldFocus = onFieldFocus
132
+ }, [
133
+ form,
134
+ onFieldFocus,
135
+ ])
136
+ useEffect(function () {
137
+ form.onFieldSubmit = onFieldSubmit
138
+ }, [
139
+ form,
140
+ onFieldSubmit,
141
+ ])
142
+ return form
143
+ }
144
+
145
+ class MantineFormImpl<
146
+ F extends Fields,
147
+ > implements MantineForm<F> {
148
+ private readonly textInputCache: Cache<
149
+ [keyof StringFieldsOfFields<F>, ComponentType<SuppliedTextInputProps>, ErrorRenderer],
150
+ MantineFieldComponent<SuppliedTextInputProps>
151
+ > = new Cache(
152
+ createTextInput.bind(this),
153
+ )
154
+ private readonly valueInputCache: Cache<
155
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
+ [keyof AllFieldsOfFields<F>, ComponentType<SuppliedValueInputProps<any>>, ErrorRenderer],
157
+ MantineFieldComponent<SuppliedTextInputProps>
158
+ > = new Cache(
159
+ createValueInput.bind(this),
160
+ )
161
+
162
+ private readonly checkboxCache: Cache<
163
+ [keyof BooleanFieldsOfFields<F>, ComponentType<SuppliedCheckboxProps>, ErrorRenderer],
164
+ MantineFieldComponent<SuppliedCheckboxProps>
165
+ > = new Cache(
166
+ createCheckbox.bind(this),
167
+ )
168
+ private readonly radioGroupCache: Cache<
169
+ [keyof StringFieldsOfFields<F>, ComponentType<SuppliedRadioGroupProps>, ErrorRenderer],
170
+ MantineFieldComponent<SuppliedRadioGroupProps>
171
+ > = new Cache(
172
+ createRadioGroup.bind(this),
173
+ )
174
+ private readonly radioCache: Cache<
175
+ [keyof StringFieldsOfFields<F>, string, ComponentType<SuppliedRadioProps>],
176
+ MantineFieldComponent<SuppliedRadioProps>
177
+ > = new Cache(
178
+ createRadio.bind(this),
179
+ )
180
+ private readonly pillCache: Cache<
181
+ [keyof AllFieldsOfFields<F>, ComponentType<SuppliedPillProps>],
182
+ MantineFieldComponent<SuppliedPillProps>
183
+ > = new Cache(
184
+ createPill.bind(this),
185
+ )
186
+ private readonly listCache: Cache<
187
+ [keyof ListFieldsOfFields<F>, ComponentType<ComponentProps<typeof DefaultList>>],
188
+ MantineFieldComponent<SuppliedListProps, ComponentProps<typeof DefaultList>>
189
+ > = new Cache(
190
+ createList.bind(this),
191
+ )
192
+
193
+ @observable.ref
194
+ accessor fields: F
195
+ onFieldValueChange: (<K extends keyof F>(this: void, key: K, value: F[K]['value']) => void) | undefined
196
+ onFieldFocus: ((this: void, key: keyof F) => void) | undefined
197
+ onFieldBlur: ((this: void, key: keyof F) => void) | undefined
198
+ onFieldSubmit: ((this: void, key: keyof F) => boolean | void) | undefined
199
+
200
+ constructor(fields: F) {
201
+ this.fields = fields
202
+ }
203
+
204
+ textInput<
205
+ K extends keyof StringFieldsOfFields<F>,
206
+ >(valuePath: K): MantineFieldComponent<SuppliedTextInputProps, TextInputProps>
207
+ textInput<
208
+ K extends keyof StringFieldsOfFields<F>,
209
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
210
+ P extends SuppliedTextInputProps<any>,
211
+ >(
212
+ valuePath: K,
213
+ TextInput?: ComponentType<P>,
214
+ ErrorRenderer?: ErrorRenderer<ErrorTypeOfField<F[K]>>,
215
+ ): MantineFieldComponent<SuppliedTextInputProps, P>
216
+ textInput<
217
+ K extends keyof StringFieldsOfFields<F>,
218
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
219
+ P extends SuppliedTextInputProps<any>,
220
+ >(
221
+ valuePath: K,
222
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
223
+ TextInput: ComponentType<P> = TextInputImpl as ComponentType<P>,
224
+ ErrorRenderer: ErrorRenderer<ErrorTypeOfField<F[K]>> = DefaultErrorRenderer,
225
+ ): MantineFieldComponent<SuppliedTextInputProps, P> {
226
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
227
+ return this.textInputCache.retrieveOrCreate(
228
+ valuePath,
229
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
230
+ TextInput as ComponentType<SuppliedTextInputProps>,
231
+ ErrorRenderer,
232
+ ) as MantineFieldComponent<SuppliedTextInputProps, P>
233
+ }
234
+
235
+ valueInput<
236
+ K extends keyof AllFieldsOfFields<F>,
237
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
238
+ P extends SuppliedValueInputProps<ValueTypeOfField<F[K]>, any>,
239
+ >(
240
+ valuePath: K,
241
+ ValueInput: ComponentType<P>,
242
+ ErrorRenderer: ErrorRenderer<ErrorTypeOfField<F[K]>> = DefaultErrorRenderer,
243
+ ): MantineFieldComponent<SuppliedValueInputProps<ValueTypeOfField<F[K]>>, P> {
244
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
245
+ return this.valueInputCache.retrieveOrCreate(
246
+ valuePath,
247
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
248
+ ValueInput as ComponentType<SuppliedValueInputProps<ValueTypeOfField<F[K]>>>,
249
+ ErrorRenderer,
250
+ ) as MantineFieldComponent<SuppliedTextInputProps, P>
251
+ }
252
+
253
+ select<
254
+ K extends keyof StringFieldsOfFields<F>,
255
+ >(
256
+ valuePath: K,
257
+ ErrorRenderer: ErrorRenderer<ErrorTypeOfField<F[K]>> = DefaultErrorRenderer,
258
+ ) {
259
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
260
+ return this.valueInputCache.retrieveOrCreate(
261
+ valuePath,
262
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
263
+ SimpleSelect as ComponentType<SuppliedValueInputProps<ValueTypeOfField<F[K]>>>,
264
+ ErrorRenderer,
265
+ ) as MantineFieldComponent<SuppliedTextInputProps, ComponentProps<typeof SimpleSelect>>
266
+ }
267
+
268
+ checkbox<
269
+ K extends keyof BooleanFieldsOfFields<F>,
270
+ >(valuePath: K): MantineFieldComponent<SuppliedCheckboxProps, CheckboxProps>
271
+ checkbox<
272
+ K extends keyof BooleanFieldsOfFields<F>,
273
+ P extends SuppliedCheckboxProps,
274
+ >(
275
+ valuePath: K,
276
+ Checkbox: ComponentType<P>,
277
+ ErrorRenderer?: ErrorRenderer<ErrorTypeOfField<F[K]>>,
278
+ ): MantineFieldComponent<SuppliedCheckboxProps, P>
279
+ checkbox<K extends keyof BooleanFieldsOfFields<F>, P extends SuppliedCheckboxProps>(
280
+ valuePath: K,
281
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
282
+ Checkbox: ComponentType<P> = CheckboxImpl as ComponentType<P>,
283
+ ErrorRenderer: ErrorRenderer<ErrorTypeOfField<F[K]>> = DefaultErrorRenderer,
284
+ ): MantineFieldComponent<SuppliedCheckboxProps, P> {
285
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
286
+ return this.checkboxCache.retrieveOrCreate(
287
+ valuePath,
288
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
289
+ Checkbox as ComponentType<SuppliedCheckboxProps>,
290
+ ErrorRenderer,
291
+ ) as MantineFieldComponent<SuppliedCheckboxProps, P>
292
+ }
293
+
294
+ radioGroup<
295
+ K extends keyof StringFieldsOfFields<F>,
296
+ >(valuePath: K): MantineFieldComponent<SuppliedRadioGroupProps, RadioGroupProps>
297
+ radioGroup<
298
+ K extends keyof StringFieldsOfFields<F>,
299
+ P extends SuppliedRadioGroupProps,
300
+ >(
301
+ valuePath: K,
302
+ RadioGroup: ComponentType<P>,
303
+ ErrorRenderer?: ErrorRenderer<ErrorTypeOfField<F[K]>>,
304
+ ): MantineFieldComponent<SuppliedRadioGroupProps, P>
305
+ radioGroup<
306
+ K extends keyof StringFieldsOfFields<F>,
307
+ P extends SuppliedRadioGroupProps,
308
+ >(
309
+ valuePath: K,
310
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
311
+ RadioGroup: ComponentType<P> = RadioImpl.Group as ComponentType<PropsWithChildren<P>>,
312
+ ErrorRenderer: ErrorRenderer<ErrorTypeOfField<F[K]>> = DefaultErrorRenderer,
313
+ ): MantineFieldComponent<SuppliedRadioGroupProps, P> {
314
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
315
+ return this.radioGroupCache.retrieveOrCreate(
316
+ valuePath,
317
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
318
+ RadioGroup as ComponentType<SuppliedRadioGroupProps>,
319
+ ErrorRenderer,
320
+ ) as MantineFieldComponent<SuppliedRadioGroupProps, P>
321
+ }
322
+
323
+ radio<
324
+ K extends keyof StringFieldsOfFields<F>,
325
+ >(
326
+ valuePath: K,
327
+ value: ValueTypeOfField<F[K]>,
328
+ ): MantineFieldComponent<SuppliedRadioProps, RadioProps>
329
+ radio<
330
+ K extends keyof StringFieldsOfFields<F>,
331
+ P extends SuppliedRadioProps,
332
+ >(
333
+ valuePath: K,
334
+ value: ValueTypeOfField<F[K]>,
335
+ Radio: ComponentType<P>,
336
+ ): MantineFieldComponent<SuppliedRadioProps, P>
337
+ radio<
338
+ K extends keyof StringFieldsOfFields<F>,
339
+ P extends SuppliedRadioProps,
340
+ >(
341
+ valuePath: K,
342
+ value: ValueTypeOfField<F[K]>,
343
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
344
+ Radio: ComponentType<P> = RadioImpl as ComponentType<P>,
345
+ ): MantineFieldComponent<SuppliedRadioProps, P> {
346
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
347
+ return this.radioCache.retrieveOrCreate(
348
+ valuePath,
349
+ value,
350
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
351
+ Radio as ComponentType<SuppliedRadioProps>,
352
+ ) as MantineFieldComponent<SuppliedRadioProps, P>
353
+ }
354
+
355
+ pill<K extends keyof AllFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedPillProps, PillProps>
356
+ pill<
357
+ K extends keyof AllFieldsOfFields<F>,
358
+ P extends SuppliedPillProps,
359
+ >(
360
+ valuePath: K,
361
+ Pill: ComponentType<P>,
362
+ ): MantineFieldComponent<SuppliedPillProps, P>
363
+ pill<
364
+ K extends keyof AllFieldsOfFields<F>,
365
+ P extends SuppliedPillProps,
366
+ >(
367
+ valuePath: K,
368
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
369
+ Pill: ComponentType<P> = PillImpl as ComponentType<P>,
370
+ ): MantineFieldComponent<SuppliedPillProps, P> {
371
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
372
+ return this.pillCache.retrieveOrCreate(
373
+ valuePath,
374
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
375
+ Pill as ComponentType<SuppliedPillProps>,
376
+ ) as MantineFieldComponent<SuppliedPillProps, P>
377
+ }
378
+
379
+ list<
380
+ K extends keyof ListFieldsOfFields<F>,
381
+ >(valuePath: K): MantineFieldComponent<
382
+ SuppliedListProps<ElementOfArray<F[K]>>,
383
+ ComponentProps<typeof DefaultList<ElementOfArray<F[K]>>>
384
+ > {
385
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
386
+ return this.listCache.retrieveOrCreate(
387
+ valuePath,
388
+ DefaultList,
389
+ ) as MantineFieldComponent<
390
+ SuppliedListProps<ElementOfArray<F[K]>>,
391
+ ComponentProps<typeof DefaultList<ElementOfArray<F[K]>>>
392
+ >
393
+ }
394
+ }