@strictly/react-form 0.0.15 → 0.0.16

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 (38) hide show
  1. package/.out/core/mobx/field_adapter_builder.d.ts +1 -1
  2. package/.out/core/mobx/form_model.d.ts +3 -4
  3. package/.out/core/mobx/form_model.js +27 -23
  4. package/.out/core/mobx/specs/form_model.tests.js +18 -8
  5. package/.out/core/mobx/types.d.ts +4 -4
  6. package/.out/index.d.ts +0 -1
  7. package/.out/index.js +0 -1
  8. package/.out/mantine/create_field_view.d.ts +20 -0
  9. package/.out/mantine/create_field_view.js +54 -0
  10. package/.out/mantine/create_list.js +3 -2
  11. package/.out/mantine/hooks.d.ts +4 -1
  12. package/.out/mantine/hooks.js +13 -1
  13. package/.out/mantine/specs/field_view_hooks.stories.d.ts +12 -0
  14. package/.out/mantine/specs/field_view_hooks.stories.js +61 -0
  15. package/.out/mantine/specs/field_view_hooks.tests.d.ts +1 -0
  16. package/.out/mantine/specs/field_view_hooks.tests.js +12 -0
  17. package/.out/tsconfig.tsbuildinfo +1 -1
  18. package/.turbo/turbo-build.log +8 -8
  19. package/.turbo/turbo-check-types.log +1 -1
  20. package/core/mobx/field_adapter_builder.ts +2 -2
  21. package/core/mobx/form_model.ts +43 -27
  22. package/core/mobx/specs/form_model.tests.ts +28 -11
  23. package/core/mobx/types.ts +4 -4
  24. package/dist/index.cjs +124 -70
  25. package/dist/index.d.cts +26 -26
  26. package/dist/index.d.ts +26 -26
  27. package/dist/index.js +121 -62
  28. package/index.ts +0 -1
  29. package/mantine/create_field_view.tsx +94 -0
  30. package/mantine/create_list.tsx +9 -2
  31. package/mantine/hooks.tsx +18 -1
  32. package/mantine/specs/__snapshots__/field_view_hooks.tests.tsx.snap +153 -0
  33. package/mantine/specs/field_view_hooks.stories.tsx +112 -0
  34. package/mantine/specs/field_view_hooks.tests.tsx +15 -0
  35. package/package.json +1 -1
  36. package/.out/mantine/field_view.d.ts +0 -18
  37. package/.out/mantine/field_view.js +0 -16
  38. package/mantine/field_view.tsx +0 -39
@@ -21,5 +21,5 @@ export declare function adapterFromPrototype<From, To, ValuePath extends string,
21
21
  export declare function adapterFromPrototype<From, To, E, ValuePath extends string, Context>(converter: TwoWayFieldConverter<From, To, E, ValuePath, Context>, prototype: From): FieldAdapterBuilder<From, To, E, ValuePath, Context>;
22
22
  export declare function identityAdapter<V, ValuePath extends string, Context>(prototype: V, required?: boolean): FieldAdapterBuilder<V, V, never, ValuePath, Context>;
23
23
  export declare function trimmingStringAdapter<ValuePath extends string, Context>(): FieldAdapterBuilder<string, string, never, ValuePath, Context>;
24
- export declare function listAdapter<E, ValuePath extends string, Context>(): FieldAdapterBuilder<readonly E[], readonly E[], never, ValuePath, Context>;
24
+ export declare function listAdapter<E, ValuePath extends string = string, Context = unknown>(): FieldAdapterBuilder<readonly E[], readonly E[], never, ValuePath, Context>;
25
25
  export {};
@@ -21,8 +21,8 @@ export type ValuePathsToAdaptersOf<TypePathsToAdapters extends Partial<Readonly<
21
21
  readonly [K in keyof ValuePathsToTypePaths as unknown extends TypePathsToAdapters[ValuePathsToTypePaths[K]] ? never : K]: NonNullable<TypePathsToAdapters[ValuePathsToTypePaths[K]]>;
22
22
  } : never;
23
23
  export type ContextOf<TypePathsToAdapters extends Partial<Readonly<Record<string, FieldAdapter>>>> = UnionToIntersection<{
24
- readonly [K in keyof TypePathsToAdapters]: TypePathsToAdapters[K] extends undefined ? undefined : ContextOfFieldAdapter<NonNullable<TypePathsToAdapters[K]>>;
25
- }[keyof TypePathsToAdapters]>;
24
+ readonly [K in keyof TypePathsToAdapters]: TypePathsToAdapters[K] extends undefined ? undefined : unknown extends ContextOfFieldAdapter<NonNullable<TypePathsToAdapters[K]>> ? never : ContextOfFieldAdapter<NonNullable<TypePathsToAdapters[K]>>;
25
+ }[keyof TypePathsToAdapters] | {}>;
26
26
  export declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readonly<Record<string, string>>, TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<FlattenedValuesOfType<T, '*'>, ContextType>, ContextType = ContextOf<TypePathsToAdapters>, ValuePathsToAdapters extends ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths> = ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths>> {
27
27
  readonly type: T;
28
28
  protected readonly adapters: TypePathsToAdapters;
@@ -31,8 +31,7 @@ export declare abstract class FormModel<T extends Type, ValueToTypePaths extends
31
31
  accessor errors: FlattenedErrors<ValuePathsToAdapters>;
32
32
  private readonly flattenedTypeDefs;
33
33
  constructor(type: T, value: ValueOfType<ReadonlyTypeOfType<T>>, adapters: TypePathsToAdapters);
34
- get context(): ContextType;
35
- protected abstract toContext(value: ValueOfType<ReadonlyTypeOfType<T>>): ContextType;
34
+ protected abstract toContext(value: ValueOfType<ReadonlyTypeOfType<T>>, valuePath: keyof ValuePathsToAdapters): ContextType;
36
35
  get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>;
37
36
  private get knownFields();
38
37
  private maybeSynthesizeFieldByValuePath;
@@ -49,7 +49,7 @@ import { computed, observable, runInAction, } from 'mobx';
49
49
  import { UnreliableFieldConversionType, } from 'types/field_converters';
50
50
  let FormModel = (() => {
51
51
  var _a, _FormModel_value_accessor_storage, _FormModel_fieldOverrides_accessor_storage, _FormModel_errors_accessor_storage;
52
- var _b, _c, _d, _e;
52
+ var _b, _c, _d;
53
53
  let _instanceExtraInitializers = [];
54
54
  let _value_decorators;
55
55
  let _value_initializers = [];
@@ -60,7 +60,6 @@ let FormModel = (() => {
60
60
  let _errors_decorators;
61
61
  let _errors_initializers = [];
62
62
  let _errors_extraInitializers = [];
63
- let _get_context_decorators;
64
63
  let _get_fields_decorators;
65
64
  let _get_knownFields_decorators;
66
65
  let _get_accessors_decorators;
@@ -95,10 +94,11 @@ let FormModel = (() => {
95
94
  });
96
95
  this.value = mobxCopy(type, value);
97
96
  this.flattenedTypeDefs = flattenTypesOfType(type);
98
- const contextValue = this.toContext(value);
99
97
  // pre-populate field overrides for consistent behavior when default information is overwritten
100
98
  // then returned to
101
- const conversions = flattenValueTo(type, this.value, () => { }, (_t, value, _setter, typePath, valuePath) => {
99
+ const conversions = flattenValueTo(type, this.value, () => { }, (_t, fieldValue, _setter, typePath, valuePath) => {
100
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
101
+ const contextValue = this.toContext(value, valuePath);
102
102
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
103
103
  const adapter = this.adapters[typePath];
104
104
  if (adapter == null) {
@@ -110,16 +110,13 @@ let FormModel = (() => {
110
110
  return;
111
111
  }
112
112
  // cannot call this.context yet as the "this" pointer has not been fully created
113
- return convert(value, valuePath, contextValue);
113
+ return convert(fieldValue, valuePath, contextValue);
114
114
  });
115
115
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
116
116
  this.fieldOverrides = map(conversions, function (_k, v) {
117
117
  return v && [v.value];
118
118
  });
119
119
  }
120
- get context() {
121
- return this.toContext(this.value);
122
- }
123
120
  get fields() {
124
121
  return new Proxy(this.knownFields, {
125
122
  get: (target, prop) => {
@@ -173,17 +170,18 @@ let FormModel = (() => {
173
170
  const accessor = this.getAccessorForValuePath(valuePath);
174
171
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
175
172
  const fieldTypeDef = this.flattenedTypeDefs[typePath];
173
+ const context = this.toContext(this.value, valuePath);
176
174
  const { value, required, readonly, } = convert(accessor != null
177
175
  ? accessor.value
178
176
  : fieldTypeDef != null
179
177
  ? mobxCopy(fieldTypeDef,
180
178
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
181
- create(valuePath, this.context))
179
+ create(valuePath, context))
182
180
  // fake values can't be copied
183
181
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
184
- : create(valuePath, this.context),
182
+ : create(valuePath, context),
185
183
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
186
- valuePath, this.context);
184
+ valuePath, context);
187
185
  const error = this.errors[valuePath];
188
186
  return {
189
187
  value: fieldOverride != null ? fieldOverride[0] : value,
@@ -235,7 +233,10 @@ let FormModel = (() => {
235
233
  ? elementValue[0]
236
234
  : elementAdapter.create(
237
235
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
238
- elementTypePath, this.context);
236
+ elementTypePath,
237
+ // TODO what can we use for the value path here?
238
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
239
+ this.toContext(this.value, valuePath));
239
240
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
240
241
  const originalList = accessor.value;
241
242
  const newList = [
@@ -346,7 +347,7 @@ let FormModel = (() => {
346
347
  const { revert } = this.getAdapterForValuePath(valuePath);
347
348
  assertExists(revert, 'setting value not supported {}', valuePath);
348
349
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
349
- const conversion = revert(value, valuePath, this.context);
350
+ const conversion = revert(value, valuePath, this.toContext(this.value, valuePath));
350
351
  const accessor = this.getAccessorForValuePath(valuePath);
351
352
  return runInAction(() => {
352
353
  this.fieldOverrides[valuePath] = [value];
@@ -384,8 +385,10 @@ let FormModel = (() => {
384
385
  return;
385
386
  }
386
387
  const { convert, create, } = adapter;
387
- const value = create(valuePath, this.context);
388
- const { value: displayValue, } = convert(value, valuePath, this.context);
388
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
389
+ const context = this.toContext(this.value, valuePath);
390
+ const value = create(valuePath, context);
391
+ const { value: displayValue, } = convert(value, valuePath, context);
389
392
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
390
393
  const key = valuePath;
391
394
  runInAction(() => {
@@ -410,25 +413,26 @@ let FormModel = (() => {
410
413
  const { convert, revert, create, } = this.getAdapterForValuePath(valuePath);
411
414
  const fieldOverride = this.fieldOverrides[valuePath];
412
415
  const accessor = this.getAccessorForValuePath(valuePath);
416
+ const context = this.toContext(this.value, valuePath);
413
417
  const { value: storedValue, } = convert(accessor != null
414
418
  ? accessor.value
415
419
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
416
- : create(valuePath, this.context),
420
+ : create(valuePath, context),
417
421
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
418
- valuePath, this.context);
422
+ valuePath, context);
419
423
  const value = fieldOverride != null
420
424
  ? fieldOverride[0]
421
425
  : storedValue;
422
426
  const dirty = storedValue !== value;
423
427
  assertExists(revert, 'changing field directly not supported {}', valuePath);
424
428
  if (ignoreDefaultValue) {
425
- const { value: defaultDisplayValue, } = convert(create(valuePath, this.context), valuePath, this.context);
429
+ const { value: defaultDisplayValue, } = convert(create(valuePath, context), valuePath, context);
426
430
  if (defaultDisplayValue === value) {
427
431
  return true;
428
432
  }
429
433
  }
430
434
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
431
- const conversion = revert(value, valuePath, this.context);
435
+ const conversion = revert(value, valuePath, context);
432
436
  return runInAction(() => {
433
437
  switch (conversion.type) {
434
438
  case UnreliableFieldConversionType.Failure:
@@ -468,13 +472,15 @@ let FormModel = (() => {
468
472
  return success;
469
473
  }
470
474
  const fieldOverride = this.fieldOverrides[adapterPath];
471
- const { value: storedValue, } = convert(accessor.value, valuePath, this.context);
475
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
476
+ const context = this.toContext(this.value, valuePath);
477
+ const { value: storedValue, } = convert(accessor.value, valuePath, context);
472
478
  const value = fieldOverride != null
473
479
  ? fieldOverride[0]
474
480
  : storedValue;
475
481
  // TODO more nuanced comparison
476
482
  const dirty = fieldOverride != null && fieldOverride[0] !== storedValue;
477
- const conversion = revert(value, valuePath, this.context);
483
+ const conversion = revert(value, valuePath, context);
478
484
  switch (conversion.type) {
479
485
  case UnreliableFieldConversionType.Failure:
480
486
  this.errors[adapterPath] = conversion.error;
@@ -503,14 +509,12 @@ let FormModel = (() => {
503
509
  _value_decorators = [(_b = observable).ref.bind(_b)];
504
510
  _fieldOverrides_decorators = [(_c = observable).shallow.bind(_c)];
505
511
  _errors_decorators = [(_d = observable).shallow.bind(_d)];
506
- _get_context_decorators = [(_e = computed).struct.bind(_e)];
507
512
  _get_fields_decorators = [computed];
508
513
  _get_knownFields_decorators = [computed];
509
514
  _get_accessors_decorators = [computed];
510
515
  __esDecorate(_a, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
511
516
  __esDecorate(_a, null, _fieldOverrides_decorators, { kind: "accessor", name: "fieldOverrides", static: false, private: false, access: { has: obj => "fieldOverrides" in obj, get: obj => obj.fieldOverrides, set: (obj, value) => { obj.fieldOverrides = value; } }, metadata: _metadata }, _fieldOverrides_initializers, _fieldOverrides_extraInitializers);
512
517
  __esDecorate(_a, null, _errors_decorators, { kind: "accessor", name: "errors", static: false, private: false, access: { has: obj => "errors" in obj, get: obj => obj.errors, set: (obj, value) => { obj.errors = value; } }, metadata: _metadata }, _errors_initializers, _errors_extraInitializers);
513
- __esDecorate(_a, null, _get_context_decorators, { kind: "getter", name: "context", static: false, private: false, access: { has: obj => "context" in obj, get: obj => obj.context }, metadata: _metadata }, null, _instanceExtraInitializers);
514
518
  __esDecorate(_a, null, _get_fields_decorators, { kind: "getter", name: "fields", static: false, private: false, access: { has: obj => "fields" in obj, get: obj => obj.fields }, metadata: _metadata }, null, _instanceExtraInitializers);
515
519
  __esDecorate(_a, null, _get_knownFields_decorators, { kind: "getter", name: "knownFields", static: false, private: false, access: { has: obj => "knownFields" in obj, get: obj => obj.knownFields }, metadata: _metadata }, null, _instanceExtraInitializers);
516
520
  __esDecorate(_a, null, _get_accessors_decorators, { kind: "getter", name: "accessors", static: false, private: false, access: { has: obj => "accessors" in obj, get: obj => obj.accessors }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -12,9 +12,10 @@ const IS_NAN_ERROR = 1;
12
12
  const originalIntegerToStringAdapter = adapterFromTwoWayConverter(new IntegerToStringConverter(IS_NAN_ERROR), prototypingFieldValueFactory(0));
13
13
  const originalBooleanToBooleanAdapter = identityAdapter(false);
14
14
  class TestFormModel extends FormModel {
15
- toContext() {
15
+ toContext(value, valuePath) {
16
16
  return {
17
- ctx: true,
17
+ value,
18
+ valuePath,
18
19
  };
19
20
  }
20
21
  }
@@ -493,23 +494,32 @@ describe('all', function () {
493
494
  let contextCopy;
494
495
  beforeEach(function () {
495
496
  integerToStringAdapter.revert.mockImplementationOnce(function (_value, _path, context) {
496
- contextCopy = Object.assign({}, context);
497
+ contextCopy = JSON.stringify(context);
497
498
  return {
498
499
  type: UnreliableFieldConversionType.Success,
499
500
  value: 1,
500
501
  };
501
502
  });
502
503
  });
503
- it('supplies the full, previous context when converting', function () {
504
+ it('supplies the context when converting', function () {
504
505
  model.setFieldValueAndValidate('$.2', '4');
505
506
  expect(integerToStringAdapter.revert).toHaveBeenCalledOnce();
506
507
  expect(integerToStringAdapter.revert).toHaveBeenCalledWith('4', '$.2', {
507
- ctx: true,
508
+ // the supplied value isn't a copy, so it will be the model value, even
509
+ // if the value has since changed
510
+ value: model.value,
511
+ valuePath: '$.2',
508
512
  });
509
513
  });
510
- it('supplies the context', function () {
511
- expect(contextCopy).toEqual({
512
- ctx: true,
514
+ it('supplies the correct context value at the time it is being checked', function () {
515
+ // the copy will show the supplied value however
516
+ expect(JSON.parse(contextCopy)).toEqual({
517
+ value: [
518
+ 1,
519
+ 3,
520
+ 7,
521
+ ],
522
+ valuePath: '$.2',
513
523
  });
514
524
  });
515
525
  });
@@ -1,18 +1,18 @@
1
1
  import { type ToOfFieldAdapter } from './field_adapter';
2
2
  import { type FlattenedConvertedFieldsOf, type FormModel } from './form_model';
3
3
  /**
4
- * Used to extract the supported value paths from a presenter
4
+ * Used to extract the supported value paths from a model
5
5
  */
6
6
  export type ValuePathsOfModel<Model extends FormModel<any, any, any, any, any>> = Model extends FormModel<infer _1, infer _2, infer _3, infer _4, infer ValuePathsToAdapters> ? keyof ValuePathsToAdapters : never;
7
7
  /**
8
8
  * Used to extract the render type (so the value that is passed to the view) of a given value path
9
- * from a presenter
9
+ * from a model
10
10
  */
11
11
  export type ToValueOfModelValuePath<Model extends FormModel<any, any, any, any, any>, K extends ValuePathsOfModel<Model>> = Model extends FormModel<infer _1, infer _2, infer _3, infer _4, infer ValuePathsToAdapters> ? ToOfFieldAdapter<ValuePathsToAdapters[K]> : never;
12
12
  /**
13
- * Extracts the form fields from the presenter. The recommended way is to
13
+ * Extracts the form fields from a form model. The recommended way is to
14
14
  * define the form fields explicitly and use that type to enforce the types
15
- * of your converters, but generating the FormFields from your presenter
15
+ * of your converters, but generating the FormFields from your model
16
16
  * is less typing, albeit at the cost of potentially getting type errors
17
17
  * reported a long way away from the source
18
18
  */
package/.out/index.d.ts CHANGED
@@ -17,7 +17,6 @@ export * from './field_converters/trimming_string_converter';
17
17
  export * from './field_converters/validating_converter';
18
18
  export * from './field_value_factories/prototyping_field_value_factory';
19
19
  export * from './mantine/error_renderer';
20
- export * from './mantine/field_view';
21
20
  export * from './mantine/hooks';
22
21
  export * from './types/error_of_field';
23
22
  export * from './types/field';
package/.out/index.js CHANGED
@@ -17,7 +17,6 @@ export * from './field_converters/trimming_string_converter';
17
17
  export * from './field_converters/validating_converter';
18
18
  export * from './field_value_factories/prototyping_field_value_factory';
19
19
  export * from './mantine/error_renderer';
20
- export * from './mantine/field_view';
21
20
  export * from './mantine/hooks';
22
21
  export * from './types/error_of_field';
23
22
  export * from './types/field';
@@ -0,0 +1,20 @@
1
+ import { type ComponentType } from 'react';
2
+ import { type AllFieldsOfFields } from 'types/all_fields_of_fields';
3
+ import { type ErrorOfField } from 'types/error_of_field';
4
+ import { type Fields } from 'types/field';
5
+ import { type ValueTypeOfField } from 'types/value_type_of_field';
6
+ import { type MantineForm } from './types';
7
+ export type FieldViewProps<F extends Fields, K extends keyof F> = {
8
+ children: (props: {
9
+ value: ValueTypeOfField<F[K]>;
10
+ error: ErrorOfField<F[K]> | undefined;
11
+ ErrorSink: ComponentType<{
12
+ error: ErrorOfField<F[K]>;
13
+ }>;
14
+ onFocus: () => void;
15
+ onBlur: () => void;
16
+ onValueChange: (v: ValueTypeOfField<F[K]>) => void;
17
+ onSubmit: () => void;
18
+ }) => JSX.Element;
19
+ };
20
+ export declare function createFieldView<F extends Fields, K extends keyof AllFieldsOfFields<F>>(this: MantineForm<F>, valuePath: K): ComponentType<FieldViewProps<F, K>>;
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Observer } from 'mobx-react';
3
+ import { useCallback, } from 'react';
4
+ import { Empty } from 'util/empty';
5
+ /**
6
+ * Displays current value and error of a field
7
+ */
8
+ function FieldView({ valuePath, form, children, }) {
9
+ const onFocus = useCallback(() => {
10
+ var _a;
11
+ (_a = form.onFieldFocus) === null || _a === void 0 ? void 0 : _a.call(form, valuePath);
12
+ }, [
13
+ form,
14
+ valuePath,
15
+ ]);
16
+ const onBlur = useCallback(() => {
17
+ var _a;
18
+ (_a = form.onFieldBlur) === null || _a === void 0 ? void 0 : _a.call(form, valuePath);
19
+ }, [
20
+ form,
21
+ valuePath,
22
+ ]);
23
+ const onValueChange = useCallback((value) => {
24
+ var _a;
25
+ (_a = form.onFieldValueChange) === null || _a === void 0 ? void 0 : _a.call(form, valuePath, value);
26
+ }, [
27
+ form,
28
+ valuePath,
29
+ ]);
30
+ const onSubmit = useCallback(() => {
31
+ var _a;
32
+ (_a = form.onFieldSubmit) === null || _a === void 0 ? void 0 : _a.call(form, valuePath);
33
+ }, [
34
+ form,
35
+ valuePath,
36
+ ]);
37
+ return (_jsx(Observer, { children: () => {
38
+ const { value, error, } = form.fields[valuePath];
39
+ return children({
40
+ value,
41
+ error,
42
+ ErrorSink: Empty,
43
+ onFocus,
44
+ onBlur,
45
+ onValueChange,
46
+ onSubmit,
47
+ });
48
+ } }));
49
+ }
50
+ export function createFieldView(valuePath) {
51
+ return (props) => {
52
+ return (_jsx(FieldView, Object.assign({ form: this, valuePath: valuePath }, props)));
53
+ };
54
+ }
@@ -1,4 +1,5 @@
1
- import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Fragment, } from 'react';
2
3
  import { createUnsafePartialObserverComponent } from 'util/partial';
3
4
  export function createList(valuePath, List) {
4
5
  const propSource = () => {
@@ -13,6 +14,6 @@ export function createList(valuePath, List) {
13
14
  export function DefaultList({ values, listPath, children, }) {
14
15
  return (_jsx(_Fragment, { children: values.map(function (value, index) {
15
16
  const valuePath = `${listPath}.${index}`;
16
- return children(valuePath, value, index);
17
+ return (_jsx(Fragment, { children: children(valuePath, value, index) }, valuePath));
17
18
  }) }));
18
19
  }
@@ -11,6 +11,7 @@ import { type StringFieldsOfFields } from 'types/string_fields_of_fields';
11
11
  import { type SubFormFields } from 'types/sub_form_fields';
12
12
  import { type ValueTypeOfField } from 'types/value_type_of_field';
13
13
  import { type SuppliedCheckboxProps } from './create_checkbox';
14
+ import { type FieldViewProps } from './create_field_view';
14
15
  import { type FieldsView } from './create_fields_view';
15
16
  import { DefaultList, type SuppliedListProps } from './create_list';
16
17
  import { type SuppliedPillProps } from './create_pill';
@@ -22,7 +23,7 @@ import { type MantineFieldComponent, type MantineForm } from './types';
22
23
  declare function SimpleSelect(props: SelectProps & {
23
24
  onChange?: (value: string | null) => void;
24
25
  }): import("react/jsx-runtime").JSX.Element;
25
- export declare function useMantineFormFields<F extends Fields>({ onFieldValueChange, onFieldBlur, onFieldFocus, onFieldSubmit, fields, }: FieldsViewProps<F>): MantineFormImpl<F>;
26
+ export declare function useMantineFormFields<F extends Fields>({ onFieldValueChange, onFieldBlur, onFieldFocus, onFieldSubmit, fields, }: FieldsViewProps<F>): Omit<MantineFormImpl<F>, 'fields'>;
26
27
  declare class MantineFormImpl<F extends Fields> implements MantineForm<F> {
27
28
  private readonly textInputCache;
28
29
  private readonly valueInputCache;
@@ -31,6 +32,7 @@ declare class MantineFormImpl<F extends Fields> implements MantineForm<F> {
31
32
  private readonly radioCache;
32
33
  private readonly pillCache;
33
34
  private readonly listCache;
35
+ private readonly fieldViewCache;
34
36
  private readonly fieldsViewCache;
35
37
  private readonly formCache;
36
38
  accessor fields: F;
@@ -52,6 +54,7 @@ declare class MantineFormImpl<F extends Fields> implements MantineForm<F> {
52
54
  pill<K extends keyof AllFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedPillProps, PillProps, ErrorOfField<F[K]>>;
53
55
  pill<K extends keyof AllFieldsOfFields<F>, P extends SuppliedPillProps>(valuePath: K, Pill: ComponentType<P>): MantineFieldComponent<SuppliedPillProps, P, ErrorOfField<F[K]>>;
54
56
  list<K extends keyof ListFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedListProps<`${K}.${number}`>, ComponentProps<typeof DefaultList<ElementOfArray<F[K]['value']>, K>>, never>;
57
+ fieldView<K extends keyof AllFieldsOfFields<F>>(valuePath: K): ComponentType<FieldViewProps<F, K>>;
55
58
  fieldsView<K extends keyof AllFieldsOfFields<F>, P extends FieldsViewProps<Fields> = FieldsViewProps<SubFormFields<F, K>>>(valuePath: K, FieldsView: ComponentType<P>): FieldsView<K, MantineFieldComponent<FieldsViewProps<P['fields']>, P, never>>;
56
59
  form<K extends keyof AllFieldsOfFields<F>, P extends FormProps<ValueTypeOfField<F[K]>> = FormProps<ValueTypeOfField<F[K]>>>(valuePath: K, Form: ComponentType<P>): MantineFieldComponent<FormProps<ValueTypeOfField<F[K]>>, P, never>;
57
60
  }
@@ -49,6 +49,7 @@ import { Cache, } from '@strictly/base';
49
49
  import { observable, runInAction, } from 'mobx';
50
50
  import { useEffect, useMemo, } from 'react';
51
51
  import { createCheckbox, } from './create_checkbox';
52
+ import { createFieldView, } from './create_field_view';
52
53
  import { createFieldsView, } from './create_fields_view';
53
54
  import { createForm } from './create_form';
54
55
  import { createList, DefaultList, } from './create_list';
@@ -60,7 +61,9 @@ import { createValueInput, } from './create_value_input';
60
61
  function SimpleSelect(props) {
61
62
  return _jsx(Select, Object.assign({}, props));
62
63
  }
63
- export function useMantineFormFields({ onFieldValueChange, onFieldBlur, onFieldFocus, onFieldSubmit, fields, }) {
64
+ export function useMantineFormFields({ onFieldValueChange, onFieldBlur, onFieldFocus, onFieldSubmit, fields,
65
+ // should use FieldView rather than observing fields directly from here
66
+ }) {
64
67
  const form = useMemo(function () {
65
68
  return new MantineFormImpl(fields);
66
69
  },
@@ -153,6 +156,12 @@ let MantineFormImpl = (() => {
153
156
  writable: true,
154
157
  value: new Cache(createList.bind(this))
155
158
  });
159
+ Object.defineProperty(this, "fieldViewCache", {
160
+ enumerable: true,
161
+ configurable: true,
162
+ writable: true,
163
+ value: new Cache(createFieldView.bind(this))
164
+ });
156
165
  Object.defineProperty(this, "fieldsViewCache", {
157
166
  enumerable: true,
158
167
  configurable: true,
@@ -248,6 +257,9 @@ let MantineFormImpl = (() => {
248
257
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
249
258
  return this.listCache.retrieveOrCreate(valuePath, DefaultList);
250
259
  }
260
+ fieldView(valuePath) {
261
+ return this.fieldViewCache.retrieveOrCreate(valuePath);
262
+ }
251
263
  fieldsView(valuePath, FieldsView) {
252
264
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
253
265
  return this.fieldsViewCache.retrieveOrCreate(valuePath,
@@ -0,0 +1,12 @@
1
+ import { type Meta, type StoryObj } from '@storybook/react';
2
+ import { type FieldsViewProps } from 'core/props';
3
+ import { type Field } from 'types/field';
4
+ declare function Component(props: FieldsViewProps<{
5
+ $: Field<string, string>;
6
+ }>): import("react/jsx-runtime").JSX.Element;
7
+ declare const meta: Meta<typeof Component>;
8
+ export default meta;
9
+ type Story = StoryObj<typeof Component>;
10
+ export declare const Empty: Story;
11
+ export declare const Populated: Story;
12
+ export declare const Error: Story;
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Group, TextInput, } from '@mantine/core';
3
+ import { action } from '@storybook/addon-actions';
4
+ import { useMantineFormFields } from 'mantine/hooks';
5
+ import { useCallback, } from 'react';
6
+ function Component(props) {
7
+ const form = useMantineFormFields(props);
8
+ const FieldView = form.fieldView('$');
9
+ return (_jsx(FieldView, { children: ({ error, value, onBlur, onFocus, onSubmit, onValueChange, }) => {
10
+ // this *is* a component eslint
11
+ // eslint-disable-next-line react-hooks/rules-of-hooks
12
+ const onChange = useCallback(({ target: { value } }) => {
13
+ onValueChange(value);
14
+ }, [onValueChange]);
15
+ return (_jsxs(Group, { align: 'start', flex: 1, children: [_jsx(TextInput, { error: error, flex: 1, onBlur: onBlur, onChange: onChange, onFocus: onFocus, value: value }), _jsx(Button, { onClick: onSubmit, children: "Submit Field" })] }));
16
+ } }));
17
+ }
18
+ const meta = {
19
+ component: Component,
20
+ args: {
21
+ onFieldBlur: action('onFieldBlur'),
22
+ onFieldFocus: action('onFieldFocus'),
23
+ onFieldSubmit: action('onFieldSubmit'),
24
+ onFieldValueChange: action('onFieldValueChange'),
25
+ },
26
+ };
27
+ export default meta;
28
+ export const Empty = {
29
+ args: {
30
+ fields: {
31
+ $: {
32
+ readonly: false,
33
+ required: false,
34
+ value: '',
35
+ },
36
+ },
37
+ },
38
+ };
39
+ export const Populated = {
40
+ args: {
41
+ fields: {
42
+ $: {
43
+ readonly: false,
44
+ required: false,
45
+ value: 'hello',
46
+ },
47
+ },
48
+ },
49
+ };
50
+ export const Error = {
51
+ args: {
52
+ fields: {
53
+ $: {
54
+ readonly: false,
55
+ required: false,
56
+ value: 'hello',
57
+ error: 'no hellos allowed',
58
+ },
59
+ },
60
+ },
61
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { composeStories } from '@storybook/react';
3
+ import { toArray } from '@strictly/base';
4
+ import { render, } from '@testing-library/react';
5
+ import * as stories from './field_view_hooks.stories';
6
+ const composedStories = composeStories(stories);
7
+ describe('field view hooks', function () {
8
+ it.each(toArray(composedStories))('renders %s', function (_name, Story) {
9
+ const wrapper = render(_jsx(Story, {}));
10
+ expect(wrapper.container).toMatchSnapshot();
11
+ });
12
+ });