@strictly/react-form 0.0.14 → 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 (42) hide show
  1. package/.out/core/mobx/field_adapter_builder.d.ts +1 -1
  2. package/.out/core/mobx/form_model.d.ts +7 -5
  3. package/.out/core/mobx/form_model.js +27 -23
  4. package/.out/core/mobx/merge_field_adapters_with_validators.d.ts +1 -1
  5. package/.out/core/mobx/specs/form_model.tests.js +18 -8
  6. package/.out/core/mobx/types.d.ts +4 -4
  7. package/.out/index.d.ts +0 -1
  8. package/.out/index.js +0 -1
  9. package/.out/mantine/create_field_view.d.ts +20 -0
  10. package/.out/mantine/create_field_view.js +54 -0
  11. package/.out/mantine/create_list.js +3 -2
  12. package/.out/mantine/hooks.d.ts +4 -1
  13. package/.out/mantine/hooks.js +13 -1
  14. package/.out/mantine/specs/field_view_hooks.stories.d.ts +12 -0
  15. package/.out/mantine/specs/field_view_hooks.stories.js +61 -0
  16. package/.out/mantine/specs/field_view_hooks.tests.d.ts +1 -0
  17. package/.out/mantine/specs/field_view_hooks.tests.js +12 -0
  18. package/.out/tsconfig.tsbuildinfo +1 -1
  19. package/.out/types/merge_validators.d.ts +1 -1
  20. package/.turbo/turbo-build.log +8 -8
  21. package/.turbo/turbo-check-types.log +1 -1
  22. package/core/mobx/field_adapter_builder.ts +2 -2
  23. package/core/mobx/form_model.ts +48 -23
  24. package/core/mobx/merge_field_adapters_with_validators.ts +1 -1
  25. package/core/mobx/specs/form_model.tests.ts +28 -11
  26. package/core/mobx/types.ts +4 -4
  27. package/dist/index.cjs +124 -70
  28. package/dist/index.d.cts +31 -28
  29. package/dist/index.d.ts +31 -28
  30. package/dist/index.js +121 -62
  31. package/index.ts +0 -1
  32. package/mantine/create_field_view.tsx +94 -0
  33. package/mantine/create_list.tsx +9 -2
  34. package/mantine/hooks.tsx +18 -1
  35. package/mantine/specs/__snapshots__/field_view_hooks.tests.tsx.snap +153 -0
  36. package/mantine/specs/field_view_hooks.stories.tsx +112 -0
  37. package/mantine/specs/field_view_hooks.tests.tsx +15 -0
  38. package/package.json +1 -1
  39. package/types/merge_validators.ts +2 -2
  40. package/.out/mantine/field_view.d.ts +0 -18
  41. package/.out/mantine/field_view.js +0 -16
  42. package/mantine/field_view.tsx +0 -39
package/mantine/hooks.tsx CHANGED
@@ -44,6 +44,10 @@ import {
44
44
  createCheckbox,
45
45
  type SuppliedCheckboxProps,
46
46
  } from './create_checkbox'
47
+ import {
48
+ createFieldView,
49
+ type FieldViewProps,
50
+ } from './create_field_view'
47
51
  import {
48
52
  createFieldsView,
49
53
  type FieldsView,
@@ -93,7 +97,8 @@ export function useMantineFormFields<
93
97
  onFieldFocus,
94
98
  onFieldSubmit,
95
99
  fields,
96
- }: FieldsViewProps<F>): MantineFormImpl<F> {
100
+ // should use FieldView rather than observing fields directly from here
101
+ }: FieldsViewProps<F>): Omit<MantineFormImpl<F>, 'fields'> {
97
102
  const form = useMemo(
98
103
  function () {
99
104
  return new MantineFormImpl(fields)
@@ -184,6 +189,12 @@ class MantineFormImpl<
184
189
  > = new Cache(
185
190
  createList.bind(this),
186
191
  )
192
+ private readonly fieldViewCache: Cache<
193
+ [keyof AllFieldsOfFields<F>],
194
+ ComponentType<FieldViewProps<F, Exclude<keyof AllFieldsOfFields<F>, number | symbol>>>
195
+ > = new Cache(
196
+ createFieldView.bind(this),
197
+ )
187
198
  private readonly fieldsViewCache: Cache<
188
199
  // the cache cannot reference keys, so we just use any
189
200
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -392,6 +403,12 @@ class MantineFormImpl<
392
403
  >
393
404
  }
394
405
 
406
+ fieldView<K extends keyof AllFieldsOfFields<F>>(valuePath: K): ComponentType<FieldViewProps<F, K>> {
407
+ return this.fieldViewCache.retrieveOrCreate(
408
+ valuePath,
409
+ )
410
+ }
411
+
395
412
  fieldsView<
396
413
  K extends keyof AllFieldsOfFields<F>,
397
414
  P extends FieldsViewProps<Fields> = FieldsViewProps<SubFormFields<F, K>>,
@@ -0,0 +1,153 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`field view hooks > renders Empty 1`] = `
4
+ <div>
5
+ <style
6
+ data-mantine-styles="classes"
7
+ >
8
+ @media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}
9
+ </style>
10
+ <div
11
+ class="m_4081bf90 mantine-Group-root"
12
+ style="--group-gap: var(--mantine-spacing-md); --group-align: start; --group-justify: flex-start; --group-wrap: wrap; flex: 1;"
13
+ >
14
+ <div
15
+ class="m_46b77525 mantine-InputWrapper-root mantine-TextInput-root"
16
+ style="flex: 1;"
17
+ >
18
+ <div
19
+ class="m_6c018570 mantine-Input-wrapper mantine-TextInput-wrapper"
20
+ data-variant="default"
21
+ >
22
+ <input
23
+ aria-invalid="false"
24
+ class="m_8fb7ebe7 mantine-Input-input mantine-TextInput-input"
25
+ data-variant="default"
26
+ id="mantine-0px4bipx4"
27
+ value=""
28
+ />
29
+ </div>
30
+ </div>
31
+ <button
32
+ class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root"
33
+ style="--button-color: var(--mantine-color-white);"
34
+ type="button"
35
+ >
36
+ <span
37
+ class="m_80f1301b mantine-Button-inner"
38
+ >
39
+ <span
40
+ class="m_811560b9 mantine-Button-label"
41
+ >
42
+ Submit Field
43
+ </span>
44
+ </span>
45
+ </button>
46
+ </div>
47
+ </div>
48
+ `;
49
+
50
+ exports[`field view hooks > renders Error 1`] = `
51
+ <div>
52
+ <style
53
+ data-mantine-styles="classes"
54
+ >
55
+ @media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}
56
+ </style>
57
+ <div
58
+ class="m_4081bf90 mantine-Group-root"
59
+ style="--group-gap: var(--mantine-spacing-md); --group-align: start; --group-justify: flex-start; --group-wrap: wrap; flex: 1;"
60
+ >
61
+ <div
62
+ class="m_46b77525 mantine-InputWrapper-root mantine-TextInput-root"
63
+ data-error="true"
64
+ style="flex: 1;"
65
+ >
66
+ <div
67
+ class="m_6c018570 mantine-Input-wrapper mantine-TextInput-wrapper"
68
+ data-error="true"
69
+ data-variant="default"
70
+ style="--input-margin-bottom: calc(var(--mantine-spacing-xs) / 2);"
71
+ >
72
+ <input
73
+ aria-describedby="mantine-0cyk5rcyk-error"
74
+ aria-invalid="true"
75
+ class="m_8fb7ebe7 mantine-Input-input mantine-TextInput-input"
76
+ data-error="true"
77
+ data-variant="default"
78
+ id="mantine-0cyk5rcyk"
79
+ value="hello"
80
+ />
81
+ </div>
82
+ <p
83
+ class="m_8f816625 mantine-InputWrapper-error mantine-TextInput-error"
84
+ id="mantine-0cyk5rcyk-error"
85
+ >
86
+ no hellos allowed
87
+ </p>
88
+ </div>
89
+ <button
90
+ class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root"
91
+ style="--button-color: var(--mantine-color-white);"
92
+ type="button"
93
+ >
94
+ <span
95
+ class="m_80f1301b mantine-Button-inner"
96
+ >
97
+ <span
98
+ class="m_811560b9 mantine-Button-label"
99
+ >
100
+ Submit Field
101
+ </span>
102
+ </span>
103
+ </button>
104
+ </div>
105
+ </div>
106
+ `;
107
+
108
+ exports[`field view hooks > renders Populated 1`] = `
109
+ <div>
110
+ <style
111
+ data-mantine-styles="classes"
112
+ >
113
+ @media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}
114
+ </style>
115
+ <div
116
+ class="m_4081bf90 mantine-Group-root"
117
+ style="--group-gap: var(--mantine-spacing-md); --group-align: start; --group-justify: flex-start; --group-wrap: wrap; flex: 1;"
118
+ >
119
+ <div
120
+ class="m_46b77525 mantine-InputWrapper-root mantine-TextInput-root"
121
+ style="flex: 1;"
122
+ >
123
+ <div
124
+ class="m_6c018570 mantine-Input-wrapper mantine-TextInput-wrapper"
125
+ data-variant="default"
126
+ >
127
+ <input
128
+ aria-invalid="false"
129
+ class="m_8fb7ebe7 mantine-Input-input mantine-TextInput-input"
130
+ data-variant="default"
131
+ id="mantine-0cyk5rcyk"
132
+ value="hello"
133
+ />
134
+ </div>
135
+ </div>
136
+ <button
137
+ class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root"
138
+ style="--button-color: var(--mantine-color-white);"
139
+ type="button"
140
+ >
141
+ <span
142
+ class="m_80f1301b mantine-Button-inner"
143
+ >
144
+ <span
145
+ class="m_811560b9 mantine-Button-label"
146
+ >
147
+ Submit Field
148
+ </span>
149
+ </span>
150
+ </button>
151
+ </div>
152
+ </div>
153
+ `;
@@ -0,0 +1,112 @@
1
+ import {
2
+ Button,
3
+ Group,
4
+ TextInput,
5
+ } from '@mantine/core'
6
+ import { action } from '@storybook/addon-actions'
7
+ import {
8
+ type Meta,
9
+ type StoryObj,
10
+ } from '@storybook/react'
11
+ import { type FieldsViewProps } from 'core/props'
12
+ import { useMantineFormFields } from 'mantine/hooks'
13
+ import {
14
+ type ChangeEvent,
15
+ useCallback,
16
+ } from 'react'
17
+ import { type Field } from 'types/field'
18
+
19
+ function Component(props: FieldsViewProps<{
20
+ $: Field<string, string>,
21
+ }>) {
22
+ const form = useMantineFormFields(props)
23
+ const FieldView = form.fieldView('$')
24
+ return (
25
+ <FieldView>
26
+ {({
27
+ error,
28
+ value,
29
+ onBlur,
30
+ onFocus,
31
+ onSubmit,
32
+ onValueChange,
33
+ }) => {
34
+ // this *is* a component eslint
35
+ // eslint-disable-next-line react-hooks/rules-of-hooks
36
+ const onChange = useCallback(({ target: { value } }: ChangeEvent<HTMLInputElement>) => {
37
+ onValueChange(value)
38
+ }, [onValueChange])
39
+ return (
40
+ <Group
41
+ align='start'
42
+ flex={1}
43
+ >
44
+ <TextInput
45
+ error={error}
46
+ flex={1}
47
+ onBlur={onBlur}
48
+ onChange={onChange}
49
+ onFocus={onFocus}
50
+ value={value}
51
+ />
52
+ {/* normally this would be done on enter, but with a field view you can implement it however you want */}
53
+ <Button onClick={onSubmit}>
54
+ Submit Field
55
+ </Button>
56
+ </Group>
57
+ )
58
+ }}
59
+ </FieldView>
60
+ )
61
+ }
62
+
63
+ const meta: Meta<typeof Component> = {
64
+ component: Component,
65
+ args: {
66
+ onFieldBlur: action('onFieldBlur'),
67
+ onFieldFocus: action('onFieldFocus'),
68
+ onFieldSubmit: action('onFieldSubmit'),
69
+ onFieldValueChange: action('onFieldValueChange'),
70
+ },
71
+ }
72
+
73
+ export default meta
74
+
75
+ type Story = StoryObj<typeof Component>
76
+
77
+ export const Empty: Story = {
78
+ args: {
79
+ fields: {
80
+ $: {
81
+ readonly: false,
82
+ required: false,
83
+ value: '',
84
+ },
85
+ },
86
+ },
87
+ }
88
+
89
+ export const Populated: Story = {
90
+ args: {
91
+ fields: {
92
+ $: {
93
+ readonly: false,
94
+ required: false,
95
+ value: 'hello',
96
+ },
97
+ },
98
+ },
99
+ }
100
+
101
+ export const Error: Story = {
102
+ args: {
103
+ fields: {
104
+ $: {
105
+ readonly: false,
106
+ required: false,
107
+ value: 'hello',
108
+ error: 'no hellos allowed',
109
+ },
110
+ },
111
+ },
112
+ }
@@ -0,0 +1,15 @@
1
+ import { composeStories } from '@storybook/react'
2
+ import { toArray } from '@strictly/base'
3
+ import {
4
+ render,
5
+ } from '@testing-library/react'
6
+ import * as stories from './field_view_hooks.stories'
7
+
8
+ const composedStories = composeStories(stories)
9
+
10
+ describe('field view hooks', function () {
11
+ it.each(toArray(composedStories))('renders %s', function (_name, Story) {
12
+ const wrapper = render(<Story />)
13
+ expect(wrapper.container).toMatchSnapshot()
14
+ })
15
+ })
package/package.json CHANGED
@@ -70,7 +70,7 @@
70
70
  "test:watch": "vitest"
71
71
  },
72
72
  "type": "module",
73
- "version": "0.0.14",
73
+ "version": "0.0.16",
74
74
  "exports": {
75
75
  ".": {
76
76
  "import": {
@@ -25,8 +25,8 @@ export type MergedOfValidators<
25
25
  export type MergedOfValidator<
26
26
  Validator1 extends Validator,
27
27
  Validator2 extends Validator,
28
- > = Validator1 extends Validator<infer V, infer E1, infer P, infer C>
29
- ? Validator2 extends Validator<V, infer E2, P, C> ? Validator<V, E1 | E2, P, C>
28
+ > = Validator1 extends Validator<infer V, infer E1, infer P, infer C1>
29
+ ? Validator2 extends Validator<V, infer E2, P, infer C2> ? Validator<V, E1 | E2, P, C1 & C2>
30
30
  : never
31
31
  : never
32
32
 
@@ -1,18 +0,0 @@
1
- import { type ComponentType } from 'react';
2
- import { type ErrorOfField } from 'types/error_of_field';
3
- import { type Fields } from 'types/field';
4
- import { type ValueTypeOfField } from 'types/value_type_of_field';
5
- /**
6
- * Displays current value and error of a field
7
- */
8
- export declare function FieldView<F extends Fields, K extends keyof F>({ fields, valuePath, children, }: {
9
- fields: F;
10
- valuePath: K;
11
- children: (props: {
12
- value: ValueTypeOfField<F[K]>;
13
- error: ErrorOfField<F[K]> | undefined;
14
- ErrorSink: ComponentType<{
15
- error: ErrorOfField<F[K]>;
16
- }>;
17
- }) => JSX.Element;
18
- }): import("react/jsx-runtime").JSX.Element;
@@ -1,16 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Observer } from 'mobx-react';
3
- import { Empty } from 'util/empty';
4
- /**
5
- * Displays current value and error of a field
6
- */
7
- export function FieldView({ fields, valuePath, children, }) {
8
- return (_jsx(Observer, { children: () => {
9
- const { value, error, } = fields[valuePath];
10
- return children({
11
- value,
12
- error,
13
- ErrorSink: Empty,
14
- });
15
- } }));
16
- }
@@ -1,39 +0,0 @@
1
- import { Observer } from 'mobx-react'
2
- import { type ComponentType } from 'react'
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 { Empty } from 'util/empty'
7
-
8
- /**
9
- * Displays current value and error of a field
10
- */
11
- export function FieldView<F extends Fields, K extends keyof F>({
12
- fields,
13
- valuePath,
14
- children,
15
- }: {
16
- fields: F,
17
- valuePath: K,
18
- children: (props: {
19
- value: ValueTypeOfField<F[K]>,
20
- error: ErrorOfField<F[K]> | undefined,
21
- ErrorSink: ComponentType<{ error: ErrorOfField<F[K]> }>,
22
- }) => JSX.Element,
23
- }) {
24
- return (
25
- <Observer>
26
- {() => {
27
- const {
28
- value,
29
- error,
30
- } = fields[valuePath]
31
- return children({
32
- value,
33
- error,
34
- ErrorSink: Empty,
35
- })
36
- }}
37
- </Observer>
38
- )
39
- }