@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,74 @@
1
+ import {
2
+ UnreachableError,
3
+ } from '@strictly/base'
4
+ import {
5
+ type FieldConversion,
6
+ FieldConversionResult,
7
+ type FieldConverter,
8
+ type SafeFieldConverter,
9
+ } from 'types/field_converters'
10
+
11
+ export function chainFieldConverter<
12
+ From,
13
+ Intermediate,
14
+ To,
15
+ E1,
16
+ E2,
17
+ ValuePath extends string,
18
+ Context,
19
+ >(
20
+ from: FieldConverter<From, Intermediate, E1, ValuePath, Context>,
21
+ to: FieldConverter<Intermediate, To, E2, ValuePath, Context>,
22
+ ): FieldConverter<From, To, E1 | E2, ValuePath, Context> {
23
+ return function (value: From, valuePath: ValuePath, context: Context): FieldConversion<To, E1 | E2> {
24
+ const fromConversion = from(value, valuePath, context)
25
+ switch (fromConversion.type) {
26
+ case FieldConversionResult.Success:
27
+ return to(fromConversion.value, valuePath, context)
28
+ case FieldConversionResult.Failure:
29
+ if (fromConversion.value != null) {
30
+ const toConversion = to(fromConversion.value[0], valuePath, context)
31
+ switch (toConversion.type) {
32
+ case FieldConversionResult.Success:
33
+ return {
34
+ type: FieldConversionResult.Failure,
35
+ error: fromConversion.error,
36
+ value: [toConversion.value],
37
+ }
38
+ case FieldConversionResult.Failure:
39
+ return {
40
+ type: FieldConversionResult.Failure,
41
+ error: fromConversion.error,
42
+ value: toConversion.value,
43
+ }
44
+ default:
45
+ throw new UnreachableError(toConversion)
46
+ }
47
+ } else {
48
+ return {
49
+ type: FieldConversionResult.Failure,
50
+ error: fromConversion.error,
51
+ value: null,
52
+ }
53
+ }
54
+ default:
55
+ throw new UnreachableError(fromConversion)
56
+ }
57
+ }
58
+ }
59
+
60
+ export function chainSafeFieldConverter<
61
+ From,
62
+ Intermediate,
63
+ To,
64
+ ValuePath extends string,
65
+ Context,
66
+ >(
67
+ from: SafeFieldConverter<From, Intermediate, ValuePath, Context>,
68
+ to: SafeFieldConverter<Intermediate, To, ValuePath, Context>,
69
+ ): SafeFieldConverter<From, To, ValuePath, Context> {
70
+ return function (value: From, valuePath: ValuePath, context: Context): To {
71
+ const intermediate = from(value, valuePath, context)
72
+ return to(intermediate, valuePath, context)
73
+ }
74
+ }
@@ -0,0 +1,39 @@
1
+ import {
2
+ FieldConversionResult,
3
+ type FieldConverter,
4
+ type SafeFieldConverter,
5
+ } from 'types/field_converters'
6
+
7
+ export function safeIdentityConverter<
8
+ V,
9
+ ValuePath extends string,
10
+ Context,
11
+ >(): SafeFieldConverter<
12
+ V,
13
+ V,
14
+ ValuePath,
15
+ Context
16
+ > {
17
+ return function (v: V) {
18
+ return v
19
+ }
20
+ }
21
+
22
+ export function identityConverter<
23
+ V,
24
+ ValuePath extends string,
25
+ Context,
26
+ >(): FieldConverter<
27
+ V,
28
+ V,
29
+ never,
30
+ ValuePath,
31
+ Context
32
+ > {
33
+ return function (value: V) {
34
+ return {
35
+ type: FieldConversionResult.Success,
36
+ value,
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,32 @@
1
+ import {
2
+ type FieldConversion,
3
+ FieldConversionResult,
4
+ type TwoWayFieldConverter,
5
+ } from 'types/field_converters'
6
+
7
+ export class IntegerToStringConverter<E, ValuePath extends string, Context>
8
+ implements TwoWayFieldConverter<number, string, E, ValuePath, Context>
9
+ {
10
+ constructor(private readonly isNanError: E) {
11
+ }
12
+
13
+ convert(from: number): string {
14
+ return Math.floor(from).toString()
15
+ }
16
+
17
+ revert(from: string): FieldConversion<number, E> {
18
+ const value = parseInt(from, 10)
19
+ if (Number.isNaN(value)) {
20
+ return {
21
+ type: FieldConversionResult.Failure,
22
+ error: this.isNanError,
23
+ value: null,
24
+ }
25
+ } else {
26
+ return {
27
+ type: FieldConversionResult.Success,
28
+ value,
29
+ }
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,15 @@
1
+ import { type SafeFieldConverter } from 'types/field_converters'
2
+
3
+ export function listConverter<
4
+ E,
5
+ K extends string,
6
+ ValuePath extends string,
7
+ Context,
8
+ >(): SafeFieldConverter<readonly E[], K[], ValuePath, Context> {
9
+ return function (from: readonly E[], valuePath: ValuePath) {
10
+ return from.map(function (_v, i) {
11
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12
+ return `${valuePath as string}.${i}` as K
13
+ })
14
+ }
15
+ }
@@ -0,0 +1,23 @@
1
+ import {
2
+ type FieldConversion,
3
+ type TwoWayFieldConverter,
4
+ } from 'types/field_converters'
5
+
6
+ export class MaybeIdentityConverter<From, To, E, ValuePath extends string, Context>
7
+ implements TwoWayFieldConverter<From, From | To, E, ValuePath, Context>
8
+ {
9
+ constructor(
10
+ private readonly converter: TwoWayFieldConverter<From, To, E, ValuePath, Context>,
11
+ private readonly isFrom: (to: From | To) => to is From,
12
+ ) {
13
+ }
14
+
15
+ convert(from: From, valuePath: ValuePath, context: Context): To | From {
16
+ return this.converter.convert(from, valuePath, context)
17
+ }
18
+
19
+ revert(from: To | From, valuePath: ValuePath, context: Context): FieldConversion<From, E> {
20
+ const value = this.isFrom(from) ? this.converter.convert(from, valuePath, context) : from
21
+ return this.converter.revert(value, valuePath, context)
22
+ }
23
+ }
@@ -0,0 +1,56 @@
1
+ import {
2
+ copy,
3
+ type ReadonlyTypeDefOf,
4
+ type Type,
5
+ type ValueTypeOf,
6
+ } from '@strictly/define'
7
+ import {
8
+ type FieldConversion,
9
+ FieldConversionResult,
10
+ type TwoWayFieldConverterWithValueFactory,
11
+ } from 'types/field_converters'
12
+
13
+ export class NullableToBooleanConverter<
14
+ T extends Type,
15
+ E,
16
+ ValuePath extends string,
17
+ Context,
18
+ > implements TwoWayFieldConverterWithValueFactory<
19
+ ValueTypeOf<ReadonlyTypeDefOf<T>> | null,
20
+ boolean,
21
+ E,
22
+ ValuePath,
23
+ Context
24
+ > {
25
+ readonly defaultValue: ValueTypeOf<ReadonlyTypeDefOf<T>> | null
26
+
27
+ constructor(
28
+ private readonly typeDef: T,
29
+ private readonly prototype: ValueTypeOf<ReadonlyTypeDefOf<T>>,
30
+ defaultToNull = true,
31
+ ) {
32
+ this.defaultValue = defaultToNull ? null : prototype
33
+ }
34
+
35
+ convert(from: ValueTypeOf<ReadonlyTypeDefOf<T>> | null): boolean {
36
+ return from != null
37
+ }
38
+
39
+ revert(from: boolean): FieldConversion<ValueTypeOf<ReadonlyTypeDefOf<T>> | null, E> {
40
+ if (from) {
41
+ const value: ValueTypeOf<T> = copy(this.typeDef, this.prototype)
42
+ return {
43
+ type: FieldConversionResult.Success,
44
+ value,
45
+ }
46
+ }
47
+ return {
48
+ type: FieldConversionResult.Success,
49
+ value: null,
50
+ }
51
+ }
52
+
53
+ create(): ValueTypeOf<ReadonlyTypeDefOf<T>> | null {
54
+ return this.defaultValue
55
+ }
56
+ }
@@ -0,0 +1,141 @@
1
+ import {
2
+ reverse,
3
+ type StringKeyOf,
4
+ } from '@strictly/base'
5
+ import {
6
+ copy,
7
+ type LiteralTypeDef,
8
+ type Type,
9
+ type UnionTypeDef,
10
+ type ValueTypeOf,
11
+ type ValueTypesOfDiscriminatedUnion,
12
+ } from '@strictly/define'
13
+ import {
14
+ type FieldConversion,
15
+ FieldConversionResult,
16
+ type TwoWayFieldConverterWithValueFactory,
17
+ } from 'types/field_converters'
18
+
19
+ export abstract class AbstractSelectValueTypeConverter<
20
+ T extends Type,
21
+ To extends string | null,
22
+ Values extends Readonly<Record<NonNullable<To>, ValueTypeOf<T>>>,
23
+ NoSuchValueError,
24
+ ValuePath extends string,
25
+ Context,
26
+ > implements TwoWayFieldConverterWithValueFactory<
27
+ ValueTypeOf<T>,
28
+ keyof Values | null,
29
+ NoSuchValueError,
30
+ ValuePath,
31
+ Context
32
+ > {
33
+ constructor(
34
+ protected readonly typeDef: T,
35
+ protected readonly values: Values,
36
+ private readonly defaultValueKey: keyof Values | null,
37
+ private readonly noSuchValueError: NoSuchValueError | null,
38
+ ) {
39
+ }
40
+
41
+ revert(from: keyof Values | null): FieldConversion<ValueTypeOf<T>, NoSuchValueError> {
42
+ const prototype: ValueTypeOf<T> | null = from == null ? null : this.values[from]
43
+ if (prototype == null && this.noSuchValueError != null) {
44
+ return {
45
+ type: FieldConversionResult.Failure,
46
+ error: this.noSuchValueError,
47
+ value: null,
48
+ }
49
+ }
50
+ const value = prototype == null ? null : copy(this.typeDef, prototype)
51
+ // TODO given we are dealing with strings, maybe we should have a check to make sure value is in the record
52
+ // of values?
53
+ return {
54
+ type: FieldConversionResult.Success,
55
+ value: value!,
56
+ }
57
+ }
58
+
59
+ convert(from: ValueTypeOf<T>): To {
60
+ if (from == null) {
61
+ return null!
62
+ }
63
+ return this.doConvert(from)
64
+ }
65
+
66
+ protected abstract doConvert(from: ValueTypeOf<T>): To
67
+
68
+ create(): ValueTypeOf<T> {
69
+ return this.defaultValueKey != null ? this.values[this.defaultValueKey] : null!
70
+ }
71
+ }
72
+
73
+ export class SelectDiscriminatedUnionConverter<
74
+ U extends UnionTypeDef,
75
+ To extends StringKeyOf<U['unions']> | null,
76
+ ValuePath extends string,
77
+ Context,
78
+ > extends AbstractSelectValueTypeConverter<
79
+ Type<U>,
80
+ To,
81
+ ValueTypesOfDiscriminatedUnion<U>,
82
+ never,
83
+ ValuePath,
84
+ Context
85
+ > {
86
+ constructor(
87
+ typeDef: Type<U>,
88
+ values: ValueTypesOfDiscriminatedUnion<U>,
89
+ defaultValueKey: keyof U['unions'],
90
+ ) {
91
+ super(
92
+ typeDef,
93
+ values,
94
+ defaultValueKey,
95
+ null,
96
+ )
97
+ }
98
+
99
+ protected override doConvert(from: ValueTypeOf<Type<U>>) {
100
+ const {
101
+ definition: {
102
+ discriminator,
103
+ },
104
+ } = this.typeDef
105
+ return from[discriminator!]
106
+ }
107
+ }
108
+
109
+ export class SelectLiteralConverter<
110
+ L extends string | number | null,
111
+ To extends string | null,
112
+ Values extends Record<NonNullable<L>, NonNullable<To>>,
113
+ NoSuchValueError,
114
+ ValuePath extends string,
115
+ Context,
116
+ > extends AbstractSelectValueTypeConverter<
117
+ Type<LiteralTypeDef<L>>,
118
+ To,
119
+ Record<NonNullable<To>, L>,
120
+ NoSuchValueError,
121
+ ValuePath,
122
+ Context
123
+ > {
124
+ constructor(
125
+ typeDef: Type<LiteralTypeDef<L>>,
126
+ private readonly valuesToStrings: Values,
127
+ defaultValue: L | null,
128
+ noSuchValueError: NoSuchValueError | null,
129
+ ) {
130
+ super(
131
+ typeDef,
132
+ reverse(valuesToStrings),
133
+ defaultValue && valuesToStrings[defaultValue],
134
+ noSuchValueError,
135
+ )
136
+ }
137
+
138
+ protected override doConvert(from: L) {
139
+ return from && this.valuesToStrings[from]
140
+ }
141
+ }
@@ -0,0 +1,23 @@
1
+ import {
2
+ type FieldConversion,
3
+ FieldConversionResult,
4
+ type TwoWayFieldConverter,
5
+ } from 'types/field_converters'
6
+
7
+ export class TrimmingStringConverter<ValuePath extends string, Context>
8
+ implements TwoWayFieldConverter<string, string, never, ValuePath, Context>
9
+ {
10
+ constructor() {
11
+ }
12
+
13
+ convert(to: string): string {
14
+ return to.trim()
15
+ }
16
+
17
+ revert(from: string): FieldConversion<string, never> {
18
+ return {
19
+ type: FieldConversionResult.Success,
20
+ value: from.trim(),
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,35 @@
1
+ import {
2
+ type FieldConversion,
3
+ FieldConversionResult,
4
+ type FieldConverter,
5
+ } from 'types/field_converters'
6
+ import { type FieldValidator } from 'types/field_validator'
7
+
8
+ export function validatingConverter<
9
+ V,
10
+ E,
11
+ ValuePath extends string,
12
+ Context,
13
+ >(validators: readonly FieldValidator<V, E, ValuePath, Context>[] = []): FieldConverter<V, V, E, ValuePath, Context> {
14
+ return function (value: V, valuePath: ValuePath, context: Context): FieldConversion<V, E> {
15
+ return validators.reduce<FieldConversion<V, E>>(
16
+ function (acc, validator) {
17
+ if (acc.type === FieldConversionResult.Success) {
18
+ const error = validator(value, valuePath, context)
19
+ if (error != null) {
20
+ return {
21
+ type: FieldConversionResult.Failure,
22
+ error,
23
+ value: [value],
24
+ }
25
+ }
26
+ }
27
+ return acc
28
+ },
29
+ {
30
+ type: FieldConversionResult.Success,
31
+ value,
32
+ },
33
+ )
34
+ }
35
+ }
@@ -0,0 +1,13 @@
1
+ import { type FieldValidator } from 'types/field_validator'
2
+
3
+ export function minimumStringLengthFieldValidatorFactory<E, ValuePath extends string, Context>(
4
+ minimumLength: number,
5
+ error: E,
6
+ ): FieldValidator<string, E, ValuePath, Context> {
7
+ return function (value: string) {
8
+ if (value.length < minimumLength) {
9
+ return error
10
+ }
11
+ return null
12
+ }
13
+ }
@@ -0,0 +1,11 @@
1
+ import { type FieldValueFactory } from 'types/field_converters'
2
+
3
+ export function prototypingFieldValueFactory<
4
+ V,
5
+ ValuePath extends string,
6
+ Context,
7
+ >(prototype: V): FieldValueFactory<V, ValuePath, Context> {
8
+ return function (): V {
9
+ return prototype
10
+ }
11
+ }
package/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ export * from './core/mobx/field_adapter_builder'
2
+ export * from './core/mobx/flattened_adapters_of_fields'
3
+ export * from './core/mobx/form_presenter'
4
+ export * from './core/mobx/types'
5
+ export * from './core/props'
6
+ export * from './field_converters/integer_to_string_converter'
7
+ export * from './field_converters/nullable_to_boolean_converter'
8
+ export * from './field_converters/select_value_type_converter'
9
+ export * from './field_converters/trimming_string_converter'
10
+ export * from './field_converters/validating_converter'
11
+ export * from './field_validators/minimum_string_length_field_validator'
12
+ export * from './field_value_factories/prototyping_field_value_factory'
13
+ export * from './mantine/hooks'
14
+ export * from './types/field'
15
+ export * from './types/flattened_form_fields_of'
16
+ export * from './util/partial'
@@ -0,0 +1,79 @@
1
+ import {
2
+ type CheckboxProps,
3
+ } from '@mantine/core'
4
+ import { type ComponentType } from 'react'
5
+ import { type BooleanFieldsOfFields } from 'types/boolean_fields_of_fields'
6
+ import { type ErrorTypeOfField } from 'types/error_type_of_field'
7
+ import { type Fields } from 'types/field'
8
+ import { createUnsafePartialObserverComponent } from 'util/partial'
9
+ import { type ErrorRenderer } from './hooks'
10
+ import {
11
+ type MantineFieldComponent,
12
+ type MantineForm,
13
+ } from './types'
14
+
15
+ export type SuppliedCheckboxProps = Pick<
16
+ CheckboxProps,
17
+ | 'name'
18
+ | 'checked'
19
+ | 'disabled'
20
+ | 'required'
21
+ | 'error'
22
+ | 'onChange'
23
+ | 'onFocus'
24
+ | 'onBlur'
25
+ | 'onKeyUp'
26
+ >
27
+
28
+ export function createCheckbox<
29
+ F extends Fields,
30
+ K extends keyof BooleanFieldsOfFields<F>,
31
+ Props extends SuppliedCheckboxProps,
32
+ >(
33
+ this: MantineForm<F>,
34
+ valuePath: K,
35
+ Checkbox: ComponentType<Props>,
36
+ ErrorRenderer: ErrorRenderer<ErrorTypeOfField<F[K]>>,
37
+ ): MantineFieldComponent<SuppliedCheckboxProps, Props> {
38
+ const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
39
+ this.onFieldValueChange?.(valuePath, e.target.checked)
40
+ }
41
+ const onFocus = () => {
42
+ this.onFieldFocus?.(valuePath)
43
+ }
44
+ const onBlur = () => {
45
+ this.onFieldBlur?.(valuePath)
46
+ }
47
+ const onKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
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
+ name: valuePath,
65
+ checked: value,
66
+ disabled,
67
+ required,
68
+ error: error && <ErrorRenderer error={error} />,
69
+ onChange,
70
+ onFocus,
71
+ onBlur,
72
+ onKeyUp,
73
+ }
74
+ }
75
+ return createUnsafePartialObserverComponent<typeof Checkbox, SuppliedCheckboxProps>(
76
+ Checkbox,
77
+ propSource,
78
+ )
79
+ }
@@ -0,0 +1,58 @@
1
+ import { type ElementOfArray } from '@strictly/base'
2
+ import { type ComponentType } from 'react'
3
+ import { type Fields } from 'types/field'
4
+ import { type ListFieldsOfFields } from 'types/list_fields_of_fields'
5
+ import { type ValueTypeOfField } from 'types/value_type_of_field'
6
+ import { createUnsafePartialObserverComponent } from 'util/partial'
7
+ import {
8
+ type MantineFieldComponent,
9
+ type MantineForm,
10
+ } from './types'
11
+
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ export type SuppliedListProps<Value = any> = {
14
+ values: readonly Value[],
15
+ }
16
+
17
+ export function createList<
18
+ F extends Fields,
19
+ K extends keyof ListFieldsOfFields<F>,
20
+ Props extends SuppliedListProps<ElementOfArray<ValueTypeOfField<F[K]>>> & {
21
+ children: (
22
+ value: ElementOfArray<ValueTypeOfField<F[K]>>,
23
+ index: number,
24
+ ) => React.ReactNode,
25
+ },
26
+ >(
27
+ this: MantineForm<F>,
28
+ valuePath: K,
29
+ List: ComponentType<Props>,
30
+ ): MantineFieldComponent<SuppliedListProps<ElementOfArray<ValueTypeOfField<F[K]>>>, Props> {
31
+ const propSource = () => {
32
+ const values = [...this.fields[valuePath].value]
33
+ return {
34
+ values,
35
+ }
36
+ }
37
+ return createUnsafePartialObserverComponent<
38
+ typeof List,
39
+ SuppliedListProps<ElementOfArray<ValueTypeOfField<F[K]>>>
40
+ >(List, propSource)
41
+ }
42
+
43
+ export function DefaultList<
44
+ Value,
45
+ >({
46
+ values,
47
+ children,
48
+ }: SuppliedListProps<Value> & {
49
+ children: (value: Value, index: number) => React.ReactNode,
50
+ }) {
51
+ return (
52
+ <>
53
+ {values.map(function (value, index) {
54
+ return children(value, index)
55
+ })}
56
+ </>
57
+ )
58
+ }
@@ -0,0 +1,43 @@
1
+ import {
2
+ type PillProps,
3
+ } from '@mantine/core'
4
+ import { type ComponentType } from 'react'
5
+ import { type AllFieldsOfFields } from 'types/all_fields_of_fields'
6
+ import { type Fields } from 'types/field'
7
+ import { createUnsafePartialObserverComponent } from 'util/partial'
8
+ import {
9
+ type MantineFieldComponent,
10
+ type MantineForm,
11
+ } from './types'
12
+
13
+ // TODO should probably supply everything
14
+ export type SuppliedPillProps = Pick<
15
+ PillProps,
16
+ | 'children'
17
+ | 'disabled'
18
+ >
19
+
20
+ export function createPill<
21
+ F extends Fields,
22
+ K extends keyof AllFieldsOfFields<F>,
23
+ Props extends SuppliedPillProps,
24
+ >(
25
+ this: MantineForm<F>,
26
+ valuePath: K,
27
+ Pill: ComponentType<Props>,
28
+ ): MantineFieldComponent<SuppliedPillProps, Props> {
29
+ const propSource = () => {
30
+ const {
31
+ disabled,
32
+ value,
33
+ // note: individual pills cannot display an error!
34
+ // error,
35
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
36
+ } = this.fields[valuePath as string]
37
+ return {
38
+ children: value,
39
+ disabled,
40
+ }
41
+ }
42
+ return createUnsafePartialObserverComponent<typeof Pill, SuppliedPillProps>(Pill, propSource)
43
+ }