@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.
- package/.out/core/mobx/field_adapter.d.ts +7 -6
- package/.out/core/mobx/field_adapter_builder.d.ts +12 -13
- package/.out/core/mobx/field_adapter_builder.js +8 -12
- package/.out/core/mobx/field_adapters_of_values.d.ts +4 -0
- package/.out/core/mobx/flattened_adapters_of_fields.d.ts +2 -2
- package/.out/core/mobx/flattened_list_types_of_type.d.ts +8 -0
- package/.out/core/mobx/form_fields_of_field_adapters.d.ts +8 -0
- package/.out/core/mobx/form_presenter.d.ts +21 -24
- package/.out/core/mobx/form_presenter.js +64 -69
- package/.out/core/mobx/merge_field_adapters_with_two_way_converter.d.ts +13 -0
- package/.out/core/mobx/merge_field_adapters_with_two_way_converter.js +11 -0
- package/.out/core/mobx/merge_field_adapters_with_validators.d.ts +11 -0
- package/.out/core/mobx/merge_field_adapters_with_validators.js +45 -0
- package/.out/core/mobx/specs/fixtures.d.ts +7 -0
- package/.out/core/mobx/specs/fixtures.js +20 -0
- package/.out/core/mobx/specs/flattened_adapters_of_fields.tests.js +5 -2
- package/.out/core/mobx/specs/{flattened_list_type_defs_of.tests.js → flattened_list_types_of_types.tests.js} +7 -7
- package/.out/core/mobx/specs/form_presenter.tests.js +162 -60
- package/.out/core/mobx/specs/merge_field_adapters_with_two_way_converter.js +89 -0
- package/.out/core/mobx/specs/merge_field_adapters_with_validators.tests.js +172 -0
- package/.out/core/mobx/types.d.ts +2 -2
- package/.out/field_converters/chain_field_converter.d.ts +3 -3
- package/.out/field_converters/chain_field_converter.js +17 -12
- package/.out/field_converters/identity_converter.d.ts +3 -3
- package/.out/field_converters/identity_converter.js +10 -6
- package/.out/field_converters/integer_to_string_converter.d.ts +5 -4
- package/.out/field_converters/integer_to_string_converter.js +13 -6
- package/.out/field_converters/list_converter.d.ts +2 -2
- package/.out/field_converters/list_converter.js +6 -1
- package/.out/field_converters/maybe_identity_converter.d.ts +3 -3
- package/.out/field_converters/maybe_identity_converter.js +3 -1
- package/.out/field_converters/nullable_to_boolean_converter.d.ts +9 -8
- package/.out/field_converters/nullable_to_boolean_converter.js +13 -7
- package/.out/field_converters/select_value_type_converter.d.ts +20 -15
- package/.out/field_converters/select_value_type_converter.js +29 -14
- package/.out/field_converters/specs/chain_field_converter.tests.d.ts +1 -0
- package/.out/field_converters/specs/chain_field_converter.tests.js +251 -0
- package/.out/field_converters/trimming_string_converter.d.ts +3 -3
- package/.out/field_converters/trimming_string_converter.js +7 -3
- package/.out/field_converters/validating_converter.d.ts +3 -3
- package/.out/field_converters/validating_converter.js +7 -5
- package/.out/index.d.ts +9 -2
- package/.out/index.js +9 -2
- package/.out/mantine/create_checkbox.d.ts +2 -3
- package/.out/mantine/create_checkbox.js +6 -5
- package/.out/mantine/create_pill.js +2 -2
- package/.out/mantine/create_radio.js +1 -1
- package/.out/mantine/create_radio_group.d.ts +2 -3
- package/.out/mantine/create_radio_group.js +4 -3
- package/.out/mantine/create_text_input.d.ts +2 -3
- package/.out/mantine/create_text_input.js +6 -5
- package/.out/mantine/create_value_input.d.ts +2 -3
- package/.out/mantine/create_value_input.js +6 -5
- package/.out/mantine/error_renderer.d.ts +6 -0
- package/.out/mantine/error_renderer.js +5 -0
- package/.out/mantine/hooks.d.ts +9 -13
- package/.out/mantine/hooks.js +10 -15
- package/.out/mantine/specs/checkbox_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/checkbox_hooks.stories.js +33 -6
- package/.out/mantine/specs/list_hooks.stories.js +2 -2
- package/.out/mantine/specs/radio_group_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/radio_group_hooks.stories.js +33 -6
- package/.out/mantine/specs/select_hooks.stories.d.ts +8 -2
- package/.out/mantine/specs/select_hooks.stories.js +45 -8
- package/.out/mantine/specs/text_input_hooks.stories.d.ts +5 -1
- package/.out/mantine/specs/text_input_hooks.stories.js +23 -8
- package/.out/mantine/specs/value_input_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/value_input_hooks.stories.js +49 -15
- package/.out/mantine/types.d.ts +4 -1
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.out/types/error_of_field.d.ts +2 -0
- package/.out/types/error_of_field.js +1 -0
- package/.out/types/field.d.ts +1 -1
- package/.out/types/field_converters.d.ts +17 -10
- package/.out/types/field_converters.js +5 -5
- package/.out/types/flattened_validators_of_fields.d.ts +8 -0
- package/.out/types/flattened_validators_of_fields.js +1 -0
- package/.out/types/merge_validators.d.ts +7 -0
- package/.out/types/merge_validators.js +38 -0
- package/.out/types/specs/flattened_validators_of_fields.tests.d.ts +1 -0
- package/.out/types/specs/flattened_validators_of_fields.tests.js +16 -0
- package/.out/types/specs/merge_validators.tests.d.ts +1 -0
- package/.out/types/specs/merge_validators.tests.js +192 -0
- package/.out/util/partial.d.ts +11 -5
- package/.out/util/partial.js +55 -15
- package/.turbo/turbo-build.log +9 -9
- package/.turbo/turbo-check-types.log +1 -1
- package/.turbo/turbo-release$colon$exports.log +1 -1
- package/README.md +5 -1
- package/core/mobx/field_adapter.ts +15 -7
- package/core/mobx/field_adapter_builder.ts +39 -75
- package/core/mobx/field_adapters_of_values.ts +17 -0
- package/core/mobx/flattened_adapters_of_fields.ts +3 -3
- package/core/mobx/flattened_list_types_of_type.ts +17 -0
- package/core/mobx/form_fields_of_field_adapters.ts +16 -0
- package/core/mobx/form_presenter.ts +117 -104
- package/core/mobx/merge_field_adapters_with_two_way_converter.ts +68 -0
- package/core/mobx/merge_field_adapters_with_validators.ts +99 -0
- package/core/mobx/specs/fixtures.ts +73 -0
- package/core/mobx/specs/flattened_adapters_of_fields.tests.ts +23 -2
- package/core/mobx/specs/flattened_list_types_of_types.tests.ts +35 -0
- package/core/mobx/specs/form_presenter.tests.ts +248 -124
- package/core/mobx/specs/merge_field_adapters_with_two_way_converter.ts +140 -0
- package/core/mobx/specs/merge_field_adapters_with_validators.tests.ts +259 -0
- package/core/mobx/types.ts +3 -3
- package/dist/index.cjs +459 -211
- package/dist/index.d.cts +153 -111
- package/dist/index.d.ts +153 -111
- package/dist/index.js +453 -200
- package/field_converters/chain_field_converter.ts +37 -23
- package/field_converters/identity_converter.ts +14 -10
- package/field_converters/integer_to_string_converter.ts +15 -9
- package/field_converters/list_converter.ts +8 -3
- package/field_converters/maybe_identity_converter.ts +7 -4
- package/field_converters/nullable_to_boolean_converter.ts +23 -16
- package/field_converters/select_value_type_converter.ts +86 -26
- package/field_converters/specs/chain_field_converter.tests.ts +302 -0
- package/field_converters/trimming_string_converter.ts +11 -6
- package/field_converters/validating_converter.ts +21 -11
- package/index.ts +9 -2
- package/mantine/create_checkbox.tsx +15 -8
- package/mantine/create_list.tsx +1 -4
- package/mantine/create_pill.tsx +2 -2
- package/mantine/create_radio.tsx +1 -1
- package/mantine/create_radio_group.tsx +8 -6
- package/mantine/create_text_input.tsx +20 -8
- package/mantine/create_value_input.tsx +17 -8
- package/mantine/error_renderer.ts +15 -0
- package/mantine/hooks.tsx +25 -51
- package/mantine/specs/__snapshots__/checkbox_hooks.tests.tsx.snap +126 -0
- package/mantine/specs/__snapshots__/radio_group_hooks.tests.tsx.snap +356 -0
- package/mantine/specs/__snapshots__/select_hooks.tests.tsx.snap +208 -12
- package/mantine/specs/__snapshots__/text_input_hooks.tests.tsx.snap +45 -0
- package/mantine/specs/__snapshots__/value_input_hooks.tests.tsx.snap +194 -8
- package/mantine/specs/checkbox_hooks.stories.tsx +47 -7
- package/mantine/specs/list_hooks.stories.tsx +2 -2
- package/mantine/specs/radio_group_hooks.stories.tsx +47 -7
- package/mantine/specs/select_hooks.stories.tsx +55 -8
- package/mantine/specs/text_input_hooks.stories.tsx +32 -7
- package/mantine/specs/value_input_hooks.stories.tsx +57 -16
- package/mantine/types.ts +5 -1
- package/package.json +16 -4
- package/tsconfig.json +1 -0
- package/types/error_of_field.ts +3 -0
- package/types/field.ts +1 -1
- package/types/field_converters.ts +21 -10
- package/types/flattened_validators_of_fields.ts +34 -0
- package/types/merge_validators.ts +80 -0
- package/types/specs/error_type_of_field.tests.ts +2 -2
- package/types/specs/flattened_validators_of_fields.tests.ts +93 -0
- package/types/specs/merge_validators.tests.ts +267 -0
- package/util/partial.tsx +200 -16
- package/.out/core/mobx/flattened_list_type_defs_of.d.ts +0 -8
- package/.out/field_validators/minimum_string_length_field_validator.d.ts +0 -2
- package/.out/field_validators/minimum_string_length_field_validator.js +0 -8
- package/.out/types/error_type_of_field.d.ts +0 -2
- package/.out/types/field_validator.d.ts +0 -3
- package/.out/types/flattened_form_fields_of.d.ts +0 -9
- package/.out/types/specs/flattened_form_fields_of.tests.js +0 -13
- package/core/mobx/flattened_list_type_defs_of.ts +0 -17
- package/core/mobx/specs/flattened_list_type_defs_of.tests.ts +0 -35
- package/field_validators/minimum_string_length_field_validator.ts +0 -13
- package/mantine/specs/__snapshots__/check_box_hooks.tests.tsx.snap +0 -227
- package/types/error_type_of_field.ts +0 -3
- package/types/field_validator.ts +0 -7
- package/types/flattened_form_fields_of.ts +0 -16
- package/types/specs/flattened_form_fields_of.tests.ts +0 -43
- /package/.out/core/mobx/{flattened_list_type_defs_of.js → field_adapters_of_values.js} +0 -0
- /package/.out/core/mobx/{specs/flattened_list_type_defs_of.tests.d.ts → flattened_list_types_of_type.js} +0 -0
- /package/.out/{types/error_type_of_field.js → core/mobx/form_fields_of_field_adapters.js} +0 -0
- /package/.out/{types/field_validator.js → core/mobx/specs/flattened_list_types_of_types.tests.d.ts} +0 -0
- /package/.out/{types/flattened_form_fields_of.js → core/mobx/specs/merge_field_adapters_with_two_way_converter.d.ts} +0 -0
- /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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
134
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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,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
|
-
}
|