@strictly/react-form 0.0.1 → 0.0.2

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 (173) hide show
  1. package/.out/core/mobx/field_adapter.d.ts +7 -6
  2. package/.out/core/mobx/field_adapter_builder.d.ts +12 -13
  3. package/.out/core/mobx/field_adapter_builder.js +8 -12
  4. package/.out/core/mobx/field_adapters_of_values.d.ts +4 -0
  5. package/.out/core/mobx/flattened_adapters_of_fields.d.ts +2 -2
  6. package/.out/core/mobx/flattened_list_types_of_type.d.ts +8 -0
  7. package/.out/core/mobx/form_fields_of_field_adapters.d.ts +8 -0
  8. package/.out/core/mobx/form_presenter.d.ts +21 -24
  9. package/.out/core/mobx/form_presenter.js +64 -69
  10. package/.out/core/mobx/merge_field_adapters_with_two_way_converter.d.ts +13 -0
  11. package/.out/core/mobx/merge_field_adapters_with_two_way_converter.js +11 -0
  12. package/.out/core/mobx/merge_field_adapters_with_validators.d.ts +11 -0
  13. package/.out/core/mobx/merge_field_adapters_with_validators.js +45 -0
  14. package/.out/core/mobx/specs/fixtures.d.ts +7 -0
  15. package/.out/core/mobx/specs/fixtures.js +20 -0
  16. package/.out/core/mobx/specs/flattened_adapters_of_fields.tests.js +5 -2
  17. package/.out/core/mobx/specs/{flattened_list_type_defs_of.tests.js → flattened_list_types_of_types.tests.js} +7 -7
  18. package/.out/core/mobx/specs/form_presenter.tests.js +162 -60
  19. package/.out/core/mobx/specs/merge_field_adapters_with_two_way_converter.js +89 -0
  20. package/.out/core/mobx/specs/merge_field_adapters_with_validators.tests.js +172 -0
  21. package/.out/core/mobx/types.d.ts +2 -2
  22. package/.out/field_converters/chain_field_converter.d.ts +3 -3
  23. package/.out/field_converters/chain_field_converter.js +17 -12
  24. package/.out/field_converters/identity_converter.d.ts +3 -3
  25. package/.out/field_converters/identity_converter.js +10 -6
  26. package/.out/field_converters/integer_to_string_converter.d.ts +5 -4
  27. package/.out/field_converters/integer_to_string_converter.js +13 -6
  28. package/.out/field_converters/list_converter.d.ts +2 -2
  29. package/.out/field_converters/list_converter.js +6 -1
  30. package/.out/field_converters/maybe_identity_converter.d.ts +3 -3
  31. package/.out/field_converters/maybe_identity_converter.js +3 -1
  32. package/.out/field_converters/nullable_to_boolean_converter.d.ts +9 -8
  33. package/.out/field_converters/nullable_to_boolean_converter.js +13 -7
  34. package/.out/field_converters/select_value_type_converter.d.ts +20 -15
  35. package/.out/field_converters/select_value_type_converter.js +29 -14
  36. package/.out/field_converters/specs/chain_field_converter.tests.d.ts +1 -0
  37. package/.out/field_converters/specs/chain_field_converter.tests.js +251 -0
  38. package/.out/field_converters/trimming_string_converter.d.ts +3 -3
  39. package/.out/field_converters/trimming_string_converter.js +7 -3
  40. package/.out/field_converters/validating_converter.d.ts +3 -3
  41. package/.out/field_converters/validating_converter.js +7 -5
  42. package/.out/index.d.ts +9 -2
  43. package/.out/index.js +9 -2
  44. package/.out/mantine/create_checkbox.d.ts +2 -3
  45. package/.out/mantine/create_checkbox.js +6 -5
  46. package/.out/mantine/create_pill.js +2 -2
  47. package/.out/mantine/create_radio.js +1 -1
  48. package/.out/mantine/create_radio_group.d.ts +2 -3
  49. package/.out/mantine/create_radio_group.js +4 -3
  50. package/.out/mantine/create_text_input.d.ts +2 -3
  51. package/.out/mantine/create_text_input.js +6 -5
  52. package/.out/mantine/create_value_input.d.ts +2 -3
  53. package/.out/mantine/create_value_input.js +6 -5
  54. package/.out/mantine/error_renderer.d.ts +6 -0
  55. package/.out/mantine/error_renderer.js +5 -0
  56. package/.out/mantine/hooks.d.ts +9 -13
  57. package/.out/mantine/hooks.js +10 -15
  58. package/.out/mantine/specs/checkbox_hooks.stories.d.ts +7 -2
  59. package/.out/mantine/specs/checkbox_hooks.stories.js +33 -6
  60. package/.out/mantine/specs/list_hooks.stories.js +2 -2
  61. package/.out/mantine/specs/radio_group_hooks.stories.d.ts +7 -2
  62. package/.out/mantine/specs/radio_group_hooks.stories.js +33 -6
  63. package/.out/mantine/specs/select_hooks.stories.d.ts +8 -2
  64. package/.out/mantine/specs/select_hooks.stories.js +45 -8
  65. package/.out/mantine/specs/text_input_hooks.stories.d.ts +5 -1
  66. package/.out/mantine/specs/text_input_hooks.stories.js +23 -8
  67. package/.out/mantine/specs/value_input_hooks.stories.d.ts +7 -2
  68. package/.out/mantine/specs/value_input_hooks.stories.js +49 -15
  69. package/.out/mantine/types.d.ts +4 -1
  70. package/.out/tsconfig.tsbuildinfo +1 -1
  71. package/.out/types/error_of_field.d.ts +2 -0
  72. package/.out/types/error_of_field.js +1 -0
  73. package/.out/types/field.d.ts +1 -1
  74. package/.out/types/field_converters.d.ts +17 -10
  75. package/.out/types/field_converters.js +5 -5
  76. package/.out/types/flattened_validators_of_fields.d.ts +8 -0
  77. package/.out/types/flattened_validators_of_fields.js +1 -0
  78. package/.out/types/merge_validators.d.ts +7 -0
  79. package/.out/types/merge_validators.js +38 -0
  80. package/.out/types/specs/flattened_validators_of_fields.tests.d.ts +1 -0
  81. package/.out/types/specs/flattened_validators_of_fields.tests.js +16 -0
  82. package/.out/types/specs/merge_validators.tests.d.ts +1 -0
  83. package/.out/types/specs/merge_validators.tests.js +192 -0
  84. package/.out/util/partial.d.ts +11 -5
  85. package/.out/util/partial.js +55 -15
  86. package/.turbo/turbo-build.log +9 -9
  87. package/.turbo/turbo-check-types.log +1 -1
  88. package/.turbo/turbo-release$colon$exports.log +1 -1
  89. package/README.md +5 -1
  90. package/core/mobx/field_adapter.ts +15 -7
  91. package/core/mobx/field_adapter_builder.ts +39 -75
  92. package/core/mobx/field_adapters_of_values.ts +17 -0
  93. package/core/mobx/flattened_adapters_of_fields.ts +3 -3
  94. package/core/mobx/flattened_list_types_of_type.ts +17 -0
  95. package/core/mobx/form_fields_of_field_adapters.ts +16 -0
  96. package/core/mobx/form_presenter.ts +117 -104
  97. package/core/mobx/merge_field_adapters_with_two_way_converter.ts +68 -0
  98. package/core/mobx/merge_field_adapters_with_validators.ts +99 -0
  99. package/core/mobx/specs/fixtures.ts +73 -0
  100. package/core/mobx/specs/flattened_adapters_of_fields.tests.ts +23 -2
  101. package/core/mobx/specs/flattened_list_types_of_types.tests.ts +35 -0
  102. package/core/mobx/specs/form_presenter.tests.ts +248 -124
  103. package/core/mobx/specs/merge_field_adapters_with_two_way_converter.ts +140 -0
  104. package/core/mobx/specs/merge_field_adapters_with_validators.tests.ts +259 -0
  105. package/core/mobx/types.ts +3 -3
  106. package/dist/index.cjs +459 -211
  107. package/dist/index.d.cts +153 -111
  108. package/dist/index.d.ts +153 -111
  109. package/dist/index.js +453 -200
  110. package/field_converters/chain_field_converter.ts +37 -23
  111. package/field_converters/identity_converter.ts +14 -10
  112. package/field_converters/integer_to_string_converter.ts +15 -9
  113. package/field_converters/list_converter.ts +8 -3
  114. package/field_converters/maybe_identity_converter.ts +7 -4
  115. package/field_converters/nullable_to_boolean_converter.ts +23 -16
  116. package/field_converters/select_value_type_converter.ts +86 -26
  117. package/field_converters/specs/chain_field_converter.tests.ts +302 -0
  118. package/field_converters/trimming_string_converter.ts +11 -6
  119. package/field_converters/validating_converter.ts +21 -11
  120. package/index.ts +9 -2
  121. package/mantine/create_checkbox.tsx +15 -8
  122. package/mantine/create_list.tsx +1 -4
  123. package/mantine/create_pill.tsx +2 -2
  124. package/mantine/create_radio.tsx +1 -1
  125. package/mantine/create_radio_group.tsx +8 -6
  126. package/mantine/create_text_input.tsx +20 -8
  127. package/mantine/create_value_input.tsx +17 -8
  128. package/mantine/error_renderer.ts +15 -0
  129. package/mantine/hooks.tsx +25 -51
  130. package/mantine/specs/__snapshots__/checkbox_hooks.tests.tsx.snap +126 -0
  131. package/mantine/specs/__snapshots__/radio_group_hooks.tests.tsx.snap +356 -0
  132. package/mantine/specs/__snapshots__/select_hooks.tests.tsx.snap +208 -12
  133. package/mantine/specs/__snapshots__/text_input_hooks.tests.tsx.snap +45 -0
  134. package/mantine/specs/__snapshots__/value_input_hooks.tests.tsx.snap +194 -8
  135. package/mantine/specs/checkbox_hooks.stories.tsx +47 -7
  136. package/mantine/specs/list_hooks.stories.tsx +2 -2
  137. package/mantine/specs/radio_group_hooks.stories.tsx +47 -7
  138. package/mantine/specs/select_hooks.stories.tsx +55 -8
  139. package/mantine/specs/text_input_hooks.stories.tsx +32 -7
  140. package/mantine/specs/value_input_hooks.stories.tsx +57 -16
  141. package/mantine/types.ts +5 -1
  142. package/package.json +16 -4
  143. package/tsconfig.json +1 -0
  144. package/types/error_of_field.ts +3 -0
  145. package/types/field.ts +1 -1
  146. package/types/field_converters.ts +21 -10
  147. package/types/flattened_validators_of_fields.ts +34 -0
  148. package/types/merge_validators.ts +80 -0
  149. package/types/specs/error_type_of_field.tests.ts +2 -2
  150. package/types/specs/flattened_validators_of_fields.tests.ts +93 -0
  151. package/types/specs/merge_validators.tests.ts +267 -0
  152. package/util/partial.tsx +200 -16
  153. package/.out/core/mobx/flattened_list_type_defs_of.d.ts +0 -8
  154. package/.out/field_validators/minimum_string_length_field_validator.d.ts +0 -2
  155. package/.out/field_validators/minimum_string_length_field_validator.js +0 -8
  156. package/.out/types/error_type_of_field.d.ts +0 -2
  157. package/.out/types/field_validator.d.ts +0 -3
  158. package/.out/types/flattened_form_fields_of.d.ts +0 -9
  159. package/.out/types/specs/flattened_form_fields_of.tests.js +0 -13
  160. package/core/mobx/flattened_list_type_defs_of.ts +0 -17
  161. package/core/mobx/specs/flattened_list_type_defs_of.tests.ts +0 -35
  162. package/field_validators/minimum_string_length_field_validator.ts +0 -13
  163. package/mantine/specs/__snapshots__/check_box_hooks.tests.tsx.snap +0 -227
  164. package/types/error_type_of_field.ts +0 -3
  165. package/types/field_validator.ts +0 -7
  166. package/types/flattened_form_fields_of.ts +0 -16
  167. package/types/specs/flattened_form_fields_of.tests.ts +0 -43
  168. /package/.out/core/mobx/{flattened_list_type_defs_of.js → field_adapters_of_values.js} +0 -0
  169. /package/.out/core/mobx/{specs/flattened_list_type_defs_of.tests.d.ts → flattened_list_types_of_type.js} +0 -0
  170. /package/.out/{types/error_type_of_field.js → core/mobx/form_fields_of_field_adapters.js} +0 -0
  171. /package/.out/{types/field_validator.js → core/mobx/specs/flattened_list_types_of_types.tests.d.ts} +0 -0
  172. /package/.out/{types/flattened_form_fields_of.js → core/mobx/specs/merge_field_adapters_with_two_way_converter.d.ts} +0 -0
  173. /package/.out/{types/specs/flattened_form_fields_of.tests.d.ts → core/mobx/specs/merge_field_adapters_with_validators.tests.d.ts} +0 -0
@@ -0,0 +1,267 @@
1
+ import {
2
+ type AnnotatedValidator,
3
+ annotations,
4
+ type FunctionalValidator,
5
+ validate,
6
+ type Validator,
7
+ } from '@strictly/define'
8
+ import {
9
+ type MergedOfValidators,
10
+ mergeValidators,
11
+ } from 'types/merge_validators'
12
+ import {
13
+ type Mock,
14
+ type Mocked,
15
+ } from 'vitest'
16
+ import {
17
+ mock,
18
+ mockReset,
19
+ } from 'vitest-mock-extended'
20
+
21
+ describe('MergedOfValidators', function () {
22
+ describe('empty validators 1', function () {
23
+ type Validators1 = {
24
+ readonly a: Validator<string, 'error a', 'a', null>,
25
+ readonly b: Validator<number, 'error b', 'b', null>,
26
+ }
27
+ type Validators2 = {}
28
+
29
+ type Validators = MergedOfValidators<Validators1, Validators2>
30
+
31
+ it('equals the expected type', function () {
32
+ expectTypeOf<Validators>().toEqualTypeOf<Validators1>()
33
+ })
34
+ })
35
+
36
+ describe('empty validators 2', function () {
37
+ type Validators1 = {}
38
+ type Validators2 = {
39
+ readonly a: Validator<string, 'error a', 'a', null>,
40
+ readonly b: Validator<number, 'error b', 'b', null>,
41
+ }
42
+
43
+ type Validators = MergedOfValidators<Validators1, Validators2>
44
+
45
+ it('equals the expected type', function () {
46
+ expectTypeOf<Validators>().toEqualTypeOf<Validators2>()
47
+ })
48
+ })
49
+
50
+ describe('merged validators with different keys', function () {
51
+ type Validators1 = {
52
+ a: Validator<string, 'error a', 'a', null>,
53
+ }
54
+ type Validators2 = {
55
+ b: Validator<number, 'error b', 'b', null>,
56
+ }
57
+
58
+ type Validators = MergedOfValidators<Validators1, Validators2>
59
+
60
+ it('equals the expected type', function () {
61
+ expectTypeOf<Validators>().toEqualTypeOf<{
62
+ readonly a: Validator<string, 'error a', 'a', null>,
63
+ readonly b: Validator<number, 'error b', 'b', null>,
64
+ }>()
65
+ })
66
+ })
67
+
68
+ describe('merged validators with same key', function () {
69
+ type Validators1 = {
70
+ a: Validator<string, 'error a', 'a', null>,
71
+ }
72
+ type Validators2 = {
73
+ a: Validator<string, 'error b', 'a', null>,
74
+ }
75
+
76
+ type Validators = MergedOfValidators<Validators1, Validators2>
77
+
78
+ it('equals the expected type', function () {
79
+ expectTypeOf<Validators>().toEqualTypeOf<{
80
+ readonly a: Validator<string, 'error a' | 'error b', 'a', null>,
81
+ }>()
82
+ })
83
+ })
84
+ })
85
+
86
+ describe('mergeValidators', function () {
87
+ describe('functional validators', function () {
88
+ const validatorA1: Mock<FunctionalValidator<string, 'error a1', 'a', null>> = vi.fn()
89
+ const validatorA2: Mock<FunctionalValidator<string, 'error a2', 'a', null>> = vi.fn()
90
+ const validatorB: Mock<FunctionalValidator<boolean, 'error b', 'b', null>> = vi.fn()
91
+ describe('produces expected type', function () {
92
+ describe('empty validators 1', function () {
93
+ const validators1 = {
94
+ a: validatorA1,
95
+ b: validatorB,
96
+ } as const
97
+ const validators2 = {} as const
98
+ const validators = mergeValidators(validators1, validators2)
99
+ it('equals expected value', function () {
100
+ expect(validators).toEqual(validators1)
101
+ })
102
+ })
103
+
104
+ describe('empty validators 2', function () {
105
+ const validators1 = {} as const
106
+ const validators2 = {
107
+ a: validatorA1,
108
+ b: validatorB,
109
+ } as const
110
+ const validators = mergeValidators(validators1, validators2)
111
+ it('equals expected value', function () {
112
+ expect(validators).toEqual(validators2)
113
+ })
114
+ })
115
+
116
+ describe('merged validators with different keys', function () {
117
+ const validators1 = {
118
+ a: validatorA1,
119
+ } as const
120
+ const validators2 = {
121
+ b: validatorB,
122
+ } as const
123
+ const validators = mergeValidators(validators1, validators2)
124
+ it('equals expected value', function () {
125
+ expect(validators).toEqual({
126
+ a: validatorA1,
127
+ b: validatorB,
128
+ })
129
+ })
130
+ })
131
+
132
+ describe('merged validators with same key', function () {
133
+ const validators1 = {
134
+ a: validatorA1,
135
+ } as const
136
+ const validators2 = {
137
+ a: validatorA2,
138
+ } as const
139
+ const validators = mergeValidators(validators1, validators2)
140
+ it('has the expected keys', function () {
141
+ expect(Array.from(Object.keys(validators))).toEqual(['a'])
142
+ })
143
+
144
+ it('reports no error when validators report no error', function () {
145
+ const result = validate(validators.a, 'x', 'a', null)
146
+ expect(result).toBeUndefined()
147
+ })
148
+
149
+ it('reports an error from first validator', function () {
150
+ validatorA1.mockReturnValueOnce('error a1')
151
+
152
+ const result = validate(validators.a, 'x', 'a', null)
153
+ expect(result).toEqual('error a1')
154
+ })
155
+
156
+ it('reports an error from second validator', function () {
157
+ validatorA2.mockReturnValueOnce('error a2')
158
+
159
+ const result = validate(validators.a, 'x', 'a', null)
160
+ expect(result).toEqual('error a2')
161
+ })
162
+ })
163
+ })
164
+ })
165
+
166
+ describe('annotated validators', function () {
167
+ const validatorA1: Mocked<AnnotatedValidator<string, 'error a1', 'a', null>> = mock()
168
+ const validatorA2: Mocked<AnnotatedValidator<string, 'error a2', 'a', null>> = mock()
169
+
170
+ beforeEach(function () {
171
+ mockReset(validatorA1)
172
+ mockReset(validatorA2)
173
+ })
174
+
175
+ const validators1 = {
176
+ a: validatorA1,
177
+ } as const
178
+ const validators2 = {
179
+ a: validatorA2,
180
+ } as const
181
+ const validators = mergeValidators(validators1, validators2)
182
+
183
+ describe.each([
184
+ [
185
+ false,
186
+ false,
187
+ false,
188
+ ],
189
+ [
190
+ false,
191
+ true,
192
+ true,
193
+ ],
194
+ [
195
+ true,
196
+ false,
197
+ true,
198
+ ],
199
+ [
200
+ true,
201
+ true,
202
+ true,
203
+ ],
204
+ ] as const)('required', function (required1, required2, required) {
205
+ beforeEach(function () {
206
+ validatorA1.validate.mockReturnValue('error a1')
207
+ validatorA1.annotations.mockReturnValue({
208
+ required: required1,
209
+ readonly: false,
210
+ })
211
+ validatorA2.annotations.mockReturnValue({
212
+ required: required2,
213
+ readonly: false,
214
+ })
215
+ })
216
+
217
+ it('has the expected required value', function () {
218
+ expect(annotations(validators.a, 'a', null)).toEqual({
219
+ required,
220
+ readonly: false,
221
+ })
222
+ })
223
+ })
224
+
225
+ describe.each([
226
+ [
227
+ false,
228
+ false,
229
+ false,
230
+ ],
231
+ [
232
+ false,
233
+ true,
234
+ true,
235
+ ],
236
+ [
237
+ true,
238
+ false,
239
+ true,
240
+ ],
241
+ [
242
+ true,
243
+ true,
244
+ true,
245
+ ],
246
+ ] as const)('required', function (readonly1, readonly2, readonly) {
247
+ beforeEach(function () {
248
+ validatorA1.validate.mockReturnValue('error a1')
249
+ validatorA1.annotations.mockReturnValue({
250
+ required: false,
251
+ readonly: readonly1,
252
+ })
253
+ validatorA2.annotations.mockReturnValue({
254
+ required: false,
255
+ readonly: readonly2,
256
+ })
257
+ })
258
+
259
+ it('has the expected required value', function () {
260
+ expect(annotations(validators.a, 'a', null)).toEqual({
261
+ required: false,
262
+ readonly,
263
+ })
264
+ })
265
+ })
266
+ })
267
+ })
package/util/partial.tsx CHANGED
@@ -1,3 +1,4 @@
1
+ import { type FriendlyExhaustiveArrayOfUnion } from '@strictly/base'
1
2
  import { observer } from 'mobx-react'
2
3
  import {
3
4
  type ComponentProps,
@@ -60,28 +61,79 @@ export function createSimplePartialComponent<
60
61
  ) as PartialComponent<Component, CurriedProps>
61
62
  }
62
63
 
64
+ export function createPartialComponent<
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
+ Component extends ComponentType<any>,
67
+ CurriedProps,
68
+ >(
69
+ Component: Component,
70
+ curriedPropsSource: () => CurriedProps,
71
+ ): PartialComponent<Component, CurriedProps, {}>
72
+ export function createPartialComponent<
73
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
+ Component extends ComponentType<any>,
75
+ CurriedProps,
76
+ AdditionalProps,
77
+ AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[],
78
+ >(
79
+ Component: Component,
80
+ curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps,
81
+ additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>,
82
+ ): PartialComponent<Component, CurriedProps, AdditionalProps>
63
83
  export function createPartialComponent<
64
84
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
85
  Component extends ComponentType<any>,
66
86
  CurriedProps extends Partial<ComponentProps<Component>>,
67
- AdditionalProps = {},
87
+ AdditionalProps,
68
88
  >(
69
89
  Component: Component,
70
90
  curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps,
91
+ additionalPropKeys: readonly (keyof AdditionalProps)[] = [],
71
92
  ): PartialComponent<Component, CurriedProps, AdditionalProps> {
72
93
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
73
94
  return forwardRef(
74
95
  function (
75
- exposedProps: PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps>,
96
+ props: PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps>,
76
97
  ref: ForwardedRef<typeof Component>,
77
98
  ) {
78
99
  // forward ref types are really difficult to work with
79
100
  // still needs a cast as `extends ComponentType<any>` != `ComponentType<any>`
80
101
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unnecessary-type-assertion
81
102
  const C = Component as ComponentType<any>
103
+ const [
104
+ additionalProps,
105
+ exposedProps,
106
+ ] = additionalPropKeys.reduce<[AdditionalProps, RemainingComponentProps<Component, CurriedProps>]>(
107
+ function (
108
+ [
109
+ additionalProps,
110
+ exposedProps,
111
+ ],
112
+ key,
113
+ ) {
114
+ const value = props[
115
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
116
+ key as keyof PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps>
117
+ ]
118
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
119
+ delete exposedProps[key as keyof RemainingComponentProps<Component, CurriedProps>]
120
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
121
+ additionalProps[key] = value as any
122
+ return [
123
+ additionalProps,
124
+ exposedProps,
125
+ ]
126
+ },
127
+ [
128
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
129
+ {} as AdditionalProps,
130
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
131
+ { ...props } as RemainingComponentProps<Component, CurriedProps>,
132
+ ],
133
+ )
134
+
82
135
  // TODO is there any way we can memoize this transformation?
83
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
84
- const curriedProps = curriedPropsSource(exposedProps as AdditionalProps)
136
+ const curriedProps = curriedPropsSource(additionalProps)
85
137
 
86
138
  return (
87
139
  <C
@@ -94,69 +146,173 @@ export function createPartialComponent<
94
146
  ) as PartialComponent<Component, CurriedProps, AdditionalProps>
95
147
  }
96
148
 
149
+ export function usePartialComponent<
150
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
151
+ Component extends ComponentType<any>,
152
+ CurriedProps,
153
+ >(
154
+ curriedPropsSource: () => CurriedProps,
155
+ deps: DependencyList,
156
+ Component: Component,
157
+ ): PartialComponent<Component, CurriedProps, {}>
158
+ export function usePartialComponent<
159
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
160
+ Component extends ComponentType<any>,
161
+ CurriedProps,
162
+ AdditionalProps,
163
+ AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[],
164
+ >(
165
+ curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps,
166
+ deps: DependencyList,
167
+ Component: Component,
168
+ additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>,
169
+ ): PartialComponent<Component, CurriedProps, AdditionalProps>
97
170
  export function usePartialComponent<
98
171
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
172
  Component extends ComponentType<any>,
100
173
  CurriedProps extends Partial<ComponentProps<Component>>,
101
- AdditionalProps = {},
174
+ AdditionalProps,
102
175
  >(
103
176
  // has to be first so eslint react-hooks/exhaustive-deps can find the callback
104
177
  // has to be a function so eslint react-hooks/exhaustive-deps can reason about it :(
105
- createCurriedProps: (additionalProps: AdditionalProps) => CurriedProps,
178
+ curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps,
106
179
  // has to be next so eslint react-hooks/exhaustive-deps can find the deps
107
180
  deps: DependencyList,
108
181
  Component: Component,
182
+ additionalPropKeys: readonly (keyof AdditionalProps)[] = [],
109
183
  ): PartialComponent<Component, CurriedProps, AdditionalProps> {
110
184
  return useMemo(
111
185
  function () {
112
- return createPartialComponent<Component, CurriedProps, AdditionalProps>(Component, createCurriedProps)
186
+ return createPartialComponent(
187
+ Component,
188
+ curriedPropsSource,
189
+ additionalPropKeys,
190
+ )
113
191
  },
114
192
  // eslint-disable-next-line react-hooks/exhaustive-deps
115
193
  [
116
194
  // eslint-disable-next-line react-hooks/exhaustive-deps
117
195
  ...deps,
118
196
  Component,
197
+ // eslint-disable-next-line react-hooks/exhaustive-deps
198
+ ...additionalPropKeys,
119
199
  ],
120
200
  )
121
201
  }
122
202
 
203
+ export function createPartialObserverComponent<
204
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
205
+ Component extends ComponentType<any>,
206
+ CurriedProps,
207
+ >(
208
+ Component: Component,
209
+ curriedPropsSource: () => CurriedProps,
210
+ ): PartialComponent<Component, CurriedProps, {}>
211
+ export function createPartialObserverComponent<
212
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
213
+ Component extends ComponentType<any>,
214
+ CurriedProps,
215
+ AdditionalProps,
216
+ AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[],
217
+ >(
218
+ Component: Component,
219
+ curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps,
220
+ additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>,
221
+ ): PartialComponent<Component, CurriedProps, AdditionalProps>
123
222
  export function createPartialObserverComponent<
124
223
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
224
  Component extends ComponentType<any>,
126
225
  CurriedProps extends Partial<ComponentProps<Component>>,
127
- AdditionalProps = {},
226
+ AdditionalProps,
128
227
  >(
129
228
  Component: Component,
130
229
  curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps,
230
+ additionalPropKeys: readonly (keyof AdditionalProps)[] = [],
131
231
  ): PartialComponent<Component, CurriedProps, AdditionalProps> {
132
232
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
133
- return createUnsafePartialObserverComponent(Component, curriedPropsSource) as PartialComponent<Component,
134
- CurriedProps, AdditionalProps>
233
+ return createUnsafePartialObserverComponent(
234
+ Component,
235
+ curriedPropsSource,
236
+ additionalPropKeys,
237
+ ) as PartialComponent<Component, CurriedProps, AdditionalProps>
135
238
  }
136
239
 
137
240
  export function createUnsafePartialObserverComponent<
138
241
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
139
242
  Component extends ComponentType<any>,
140
243
  CurriedProps,
141
- AdditionalProps = {},
244
+ >(
245
+ Component: Component,
246
+ curriedPropsSource: () => CurriedProps,
247
+ ): UnsafePartialComponent<Component, CurriedProps, {}>
248
+ export function createUnsafePartialObserverComponent<
249
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
250
+ Component extends ComponentType<any>,
251
+ CurriedProps,
252
+ AdditionalProps,
253
+ AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[],
142
254
  >(
143
255
  Component: Component,
144
256
  curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps,
257
+ additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>,
258
+ ): UnsafePartialComponent<Component, CurriedProps, AdditionalProps>
259
+ export function createUnsafePartialObserverComponent<
260
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
261
+ Component extends ComponentType<any>,
262
+ CurriedProps,
263
+ AdditionalProps = {},
264
+ >(
265
+ Component: Component,
266
+ curriedPropsSource: (additionalProps?: AdditionalProps) => CurriedProps,
267
+ additionalPropKeys: readonly (keyof AdditionalProps)[] = [],
145
268
  ): UnsafePartialComponent<Component, CurriedProps, AdditionalProps> {
146
269
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
147
270
  return observer(
148
271
  forwardRef(
149
272
  function (
150
- exposedProps: PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps>,
273
+ props: PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps>,
151
274
  ref: ForwardedRef<typeof Component>,
152
275
  ) {
153
276
  // forward ref types are really difficult to work with
154
277
  // still needs a cast as `extends ComponentType<any>` != `ComponentType<any>`
155
278
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unnecessary-type-assertion
156
279
  const C = Component as ComponentType<any>
280
+ // remove the additional props from the exposed props that get passed in to the component
281
+ // as this generates react warnings
282
+ const [
283
+ additionalProps,
284
+ exposedProps,
285
+ ] = additionalPropKeys.reduce<[AdditionalProps, RemainingComponentProps<Component, CurriedProps>]>(
286
+ function (
287
+ [
288
+ additionalProps,
289
+ exposedProps,
290
+ ],
291
+ key,
292
+ ) {
293
+ const value = props[
294
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
295
+ key as keyof PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps>
296
+ ]
297
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
298
+ delete exposedProps[key as keyof RemainingComponentProps<Component, CurriedProps>]
299
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
300
+ additionalProps[key] = value as any
301
+ return [
302
+ additionalProps,
303
+ exposedProps,
304
+ ]
305
+ },
306
+ [
307
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
308
+ {} as AdditionalProps,
309
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
310
+ { ...props } as RemainingComponentProps<Component, CurriedProps>,
311
+ ],
312
+ )
313
+
157
314
  // TODO is there any way we can memoize this transformation?
158
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
159
- const curriedProps = curriedPropsSource(exposedProps as AdditionalProps)
315
+ const curriedProps = curriedPropsSource(additionalProps)
160
316
 
161
317
  return (
162
318
  <C
@@ -170,6 +326,27 @@ export function createUnsafePartialObserverComponent<
170
326
  ) as UnsafePartialComponent<Component, CurriedProps, AdditionalProps>
171
327
  }
172
328
 
329
+ export function usePartialObserverComponent<
330
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
331
+ Component extends ComponentType<any>,
332
+ CurriedProps,
333
+ >(
334
+ curriedPropsSource: () => CurriedProps,
335
+ deps: DependencyList,
336
+ Component: Component,
337
+ ): PartialComponent<Component, CurriedProps, {}>
338
+ export function usePartialObserverComponent<
339
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
340
+ Component extends ComponentType<any>,
341
+ CurriedProps,
342
+ AdditionalProps,
343
+ AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[],
344
+ >(
345
+ curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps,
346
+ deps: DependencyList,
347
+ Component: Component,
348
+ additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>,
349
+ ): PartialComponent<Component, CurriedProps, AdditionalProps>
173
350
  export function usePartialObserverComponent<
174
351
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
175
352
  Component extends ComponentType<any>,
@@ -181,16 +358,23 @@ export function usePartialObserverComponent<
181
358
  // has to be next so eslint react-hooks/exhaustive-deps can find the deps
182
359
  deps: DependencyList,
183
360
  Component: Component,
184
- ) {
361
+ additionalPropKeys: readonly (keyof AdditionalProps)[] = [],
362
+ ): PartialComponent<Component, CurriedProps, AdditionalProps> {
185
363
  return useMemo(
186
364
  function () {
187
- return createPartialObserverComponent(Component, curriedPropsSource)
365
+ return createPartialObserverComponent(
366
+ Component,
367
+ curriedPropsSource,
368
+ additionalPropKeys,
369
+ )
188
370
  },
189
371
  // eslint-disable-next-line react-hooks/exhaustive-deps
190
372
  [
191
373
  // eslint-disable-next-line react-hooks/exhaustive-deps
192
374
  ...deps,
193
375
  Component,
376
+ // eslint-disable-next-line react-hooks/exhaustive-deps
377
+ ...additionalPropKeys,
194
378
  ],
195
379
  )
196
380
  }
@@ -1,8 +0,0 @@
1
- import { type FlattenedTypeDefsOf, type ListTypeDef, type Type } from '@strictly/define';
2
- import { type SimplifyDeep } from 'type-fest';
3
- export type ListJsonPathsOf<T extends Type> = keyof FlattenedListTypeDefsOf<T>;
4
- export type FlattenedListTypeDefsOf<T extends Type> = FlattenedListTypeDefsOfFlattened<SimplifyDeep<FlattenedTypeDefsOf<T, null>>>;
5
- type FlattenedListTypeDefsOfFlattened<T extends Readonly<Record<string, Type>>> = {
6
- [K in keyof T as T[K]['definition'] extends ListTypeDef ? K : never]: T[K];
7
- };
8
- export {};
@@ -1,2 +0,0 @@
1
- import { type FieldValidator } from 'types/field_validator';
2
- export declare function minimumStringLengthFieldValidatorFactory<E, ValuePath extends string, Context>(minimumLength: number, error: E): FieldValidator<string, E, ValuePath, Context>;
@@ -1,8 +0,0 @@
1
- export function minimumStringLengthFieldValidatorFactory(minimumLength, error) {
2
- return function (value) {
3
- if (value.length < minimumLength) {
4
- return error;
5
- }
6
- return null;
7
- };
8
- }
@@ -1,2 +0,0 @@
1
- import { type Field } from './field';
2
- export type ErrorTypeOfField<F extends Field> = F extends Field<infer _V, infer E> ? E : never;
@@ -1,3 +0,0 @@
1
- export type FieldValidator<V, E, ValuePath extends string, Context> = {
2
- (value: V, valuePath: ValuePath, context: Context): E | null;
3
- };
@@ -1,9 +0,0 @@
1
- import { type PrintableOf } from '@strictly/base';
2
- import { type ValueOf } from 'type-fest';
3
- import { type Field } from './field';
4
- /**
5
- * Maps type paths to value paths for
6
- */
7
- export type FlattenedFormFieldsOf<JsonPaths extends Record<string, string>, TypePathsToFormFields extends Partial<Readonly<Record<ValueOf<JsonPaths>, Field>>>> = keyof TypePathsToFormFields extends ValueOf<JsonPaths> ? {
8
- readonly [K in keyof JsonPaths as unknown extends TypePathsToFormFields[JsonPaths[K]] ? never : K]: TypePathsToFormFields[JsonPaths[K]];
9
- } : `fields missing paths: ${PrintableOf<Exclude<keyof TypePathsToFormFields, ValueOf<JsonPaths>>>}`;
@@ -1,13 +0,0 @@
1
- describe('FlattenedFormFieldsOf', function () {
2
- describe('subset', function () {
3
- it('equals expected type', function () {
4
- expectTypeOf().toEqualTypeOf();
5
- });
6
- });
7
- describe('overlap', function () {
8
- it('errors to callee', function () {
9
- expectTypeOf().toEqualTypeOf();
10
- });
11
- });
12
- });
13
- export {};
@@ -1,17 +0,0 @@
1
- import {
2
- type FlattenedTypeDefsOf,
3
- type ListTypeDef,
4
- type Type,
5
- } from '@strictly/define'
6
- import { type SimplifyDeep } from 'type-fest'
7
-
8
- export type ListJsonPathsOf<T extends Type> = keyof FlattenedListTypeDefsOf<T>
9
-
10
- export type FlattenedListTypeDefsOf<T extends Type> = FlattenedListTypeDefsOfFlattened<
11
- // SimplifyDeep is necessary here otherwise FlattenedListTypeDefsOfFlattened will complain about infinite depth
12
- SimplifyDeep<FlattenedTypeDefsOf<T, null>>
13
- >
14
-
15
- type FlattenedListTypeDefsOfFlattened<T extends Readonly<Record<string, Type>>> = {
16
- [K in keyof T as T[K]['definition'] extends ListTypeDef ? K : never]: T[K]
17
- }