@twintag/twintag-form 1.4.0-beta.1 → 1.4.0-beta.3

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 (34) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +12 -0
  3. package/README.md +8 -4
  4. package/dev/main.tsx +453 -233
  5. package/dist/components/config.d.ts +2 -2
  6. package/dist/components/containers/accordion.d.ts +2 -4
  7. package/dist/components/fields/checkbox.d.ts +1 -1
  8. package/dist/components/fields/date-picker.d.ts +1 -1
  9. package/dist/components/fields/file.d.ts +1 -1
  10. package/dist/components/fields/input.d.ts +1 -1
  11. package/dist/components/fields/radiogroup.d.ts +3 -1
  12. package/dist/components/fields/textarea.d.ts +1 -1
  13. package/dist/components/fields/toggle.d.ts +1 -1
  14. package/dist/components/fields/tri-toggle.d.ts +1 -1
  15. package/dist/components/provider.d.ts +3 -3
  16. package/dist/twintag-form.react.js +1637 -1624
  17. package/dist/types/index.d.ts +37 -22
  18. package/dist/utils/data.d.ts +5 -1
  19. package/index.html +1 -0
  20. package/lib/components/config.tsx +2 -2
  21. package/lib/components/containers/accordion.tsx +33 -14
  22. package/lib/components/fields/checkbox.tsx +1 -1
  23. package/lib/components/fields/date-picker.tsx +5 -1
  24. package/lib/components/fields/file.tsx +83 -83
  25. package/lib/components/fields/input.tsx +1 -1
  26. package/lib/components/fields/radiogroup.tsx +5 -1
  27. package/lib/components/fields/textarea.tsx +1 -1
  28. package/lib/components/fields/toggle.tsx +1 -1
  29. package/lib/components/fields/tri-toggle.tsx +1 -1
  30. package/lib/components/provider.tsx +163 -159
  31. package/lib/index.tsx +41 -41
  32. package/lib/types/index.ts +82 -49
  33. package/lib/utils/data.ts +5 -2
  34. package/package.json +1 -1
@@ -1,159 +1,163 @@
1
- import type {
2
- ParsedJsonComponent,
3
- RawJsonComponent,
4
- TwintagFormContext,
5
- } from '@twintag/twintag-form/types';
6
- import type { WithChildren } from 'twintag-types/dom';
7
-
8
- import { createContext, useContext, useMemo } from 'react';
9
- import { LabelToString } from 'twintag-types/dom';
10
- import { v4 as uuid } from 'uuid';
11
-
12
- export const twintagFormContext = createContext<TwintagFormContext>({
13
- variant: 'default',
14
- size: 'base',
15
- inputs: new Map(),
16
- components: new Map(),
17
- config: [],
18
- resetValues: {},
19
- });
20
-
21
- export const useTwintagForm = () => {
22
- const context = useContext(twintagFormContext);
23
-
24
- if (!context) {
25
- throw new Error('useTwintagForm must be used within a TwintagFormProvider');
26
- }
27
-
28
- return context;
29
- };
30
-
31
- type Components = TwintagFormContext['components'];
32
- type Inputs = TwintagFormContext['inputs'];
33
-
34
- const format = (component: RawJsonComponent, components: Components) => {
35
- const copy = { ...component };
36
- let formattedConfig: ParsedJsonComponent;
37
-
38
- if (copy.props) {
39
- formattedConfig = { ...copy.props } as ParsedJsonComponent;
40
- } else {
41
- formattedConfig = { ...copy } as unknown as ParsedJsonComponent;
42
- }
43
-
44
- if (!copy.type) return Object.create(null) as ParsedJsonComponent;
45
-
46
- const [type, subtype = undefined] = copy.type.split(':');
47
- formattedConfig.id = `${type}-${uuid()}`;
48
-
49
- const comp = components.get(type);
50
-
51
- if (comp) {
52
- formattedConfig.comp = comp;
53
- }
54
-
55
- formattedConfig.type = type;
56
-
57
- if (subtype) {
58
- formattedConfig.subtype = subtype;
59
- }
60
-
61
- if (copy.props) {
62
- if (copy.props.subtype) {
63
- formattedConfig.subtype = copy.props.subtype;
64
- }
65
-
66
- if (copy.props.label) {
67
- const labelParsed = LabelToString.safeParse(copy.props.label);
68
- formattedConfig.label = labelParsed.success
69
- ? labelParsed.data
70
- : undefined;
71
- }
72
- }
73
-
74
- if (copy.children && Array.isArray(copy.children)) {
75
- formattedConfig.items = copy.children.map((item) =>
76
- format(item, components),
77
- );
78
- }
79
-
80
- return formattedConfig;
81
- };
82
-
83
- export const getResetValues = (
84
- config: RawJsonComponent | RawJsonComponent[],
85
- inputs: Inputs,
86
- ) => {
87
- const stack = Array.isArray(config) ? [...config] : [config];
88
-
89
- const resetValues = new Map<string, unknown>();
90
-
91
- while (stack.length > 0) {
92
- const currentObj = stack.pop();
93
-
94
- if (!currentObj) continue;
95
-
96
- for (const [key, value] of Object.entries(currentObj)) {
97
- if (Array.isArray(value)) {
98
- for (const item of value) {
99
- stack.push(item as unknown as RawJsonComponent);
100
- }
101
- }
102
-
103
- if (typeof value === 'object' && value !== null) {
104
- stack.push(value as unknown as RawJsonComponent);
105
- }
106
-
107
- if (key === 'subtype' && currentObj.bind) {
108
- resetValues.set(
109
- currentObj.bind,
110
- inputs.get(value as string)?.defaultValue,
111
- );
112
- }
113
- }
114
- }
115
-
116
- return Object.fromEntries(resetValues);
117
- };
118
-
119
- export const TwintagFormProvider = ({
120
- variant,
121
- size,
122
- inputs,
123
- components,
124
- config,
125
- children,
126
- }: {
127
- config: RawJsonComponent | RawJsonComponent[];
128
- variant: TwintagFormContext['variant'];
129
- size: TwintagFormContext['size'];
130
- inputs: Inputs;
131
- components: Components;
132
- } & WithChildren) => {
133
- const __config = useMemo(() => {
134
- if (Array.isArray(config)) {
135
- return config.map((item) => format(item, components));
136
- }
137
-
138
- return format(config, components);
139
- }, [config, components]);
140
-
141
- const resetValues = useMemo(() => {
142
- return getResetValues(__config, inputs);
143
- }, [__config, inputs]);
144
-
145
- return (
146
- <twintagFormContext.Provider
147
- value={{
148
- config: __config,
149
- variant,
150
- size,
151
- inputs,
152
- components,
153
- resetValues,
154
- }}
155
- >
156
- {children}
157
- </twintagFormContext.Provider>
158
- );
159
- };
1
+ import type {
2
+ ParsedJsonComponent,
3
+ RawJsonComponent,
4
+ TwintagFormContext,
5
+ } from '@twintag/twintag-form/types';
6
+ import type { WithChildren } from 'twintag-types/dom';
7
+
8
+ import { createContext, useContext, useMemo } from 'react';
9
+ import { LabelToString } from 'twintag-types/dom';
10
+ import { v4 as uuid } from 'uuid';
11
+
12
+ export const twintagFormContext = createContext<TwintagFormContext>({
13
+ variant: 'default',
14
+ size: 'base',
15
+ inputs: new Map(),
16
+ components: new Map(),
17
+ config: [],
18
+ resetValues: {},
19
+ });
20
+
21
+ export const useTwintagForm = () => {
22
+ const context = useContext(twintagFormContext);
23
+
24
+ if (!context) {
25
+ throw new Error('useTwintagForm must be used within a TwintagFormProvider');
26
+ }
27
+
28
+ return context;
29
+ };
30
+
31
+ type Components = TwintagFormContext['components'];
32
+ type Inputs = TwintagFormContext['inputs'];
33
+
34
+ const format = (component: RawJsonComponent, components: Components) => {
35
+ const copy = { ...component };
36
+ let formattedConfig: ParsedJsonComponent;
37
+
38
+ if (copy.props) {
39
+ formattedConfig = { ...copy.props } as ParsedJsonComponent;
40
+ } else {
41
+ formattedConfig = { ...copy } as unknown as ParsedJsonComponent;
42
+ }
43
+
44
+ if (!copy.type) return Object.create(null) as ParsedJsonComponent;
45
+
46
+ const [type, subtype = undefined] = copy.type.split(':');
47
+ formattedConfig.id = `${type}-${uuid()}`;
48
+
49
+ const comp = components.get(type);
50
+
51
+ if (comp) {
52
+ formattedConfig.comp = comp;
53
+ }
54
+
55
+ formattedConfig.type = type;
56
+
57
+ if (subtype) {
58
+ formattedConfig.subtype = subtype;
59
+ }
60
+
61
+ if (copy.props) {
62
+ if (copy.props.subtype) {
63
+ formattedConfig.subtype = copy.props.subtype;
64
+ }
65
+
66
+ if (copy.props.label) {
67
+ const labelParsed = LabelToString.safeParse(copy.props.label);
68
+ formattedConfig.label = labelParsed.success
69
+ ? labelParsed.data
70
+ : undefined;
71
+ }
72
+ }
73
+
74
+ if (copy.children && Array.isArray(copy.children)) {
75
+ formattedConfig.items = copy.children.map((item) =>
76
+ format(item, components),
77
+ );
78
+ }
79
+
80
+ return formattedConfig;
81
+ };
82
+
83
+ export const getResetValues = (
84
+ config: ParsedJsonComponent | ParsedJsonComponent[],
85
+ inputs: Inputs,
86
+ ) => {
87
+ const stack = Array.isArray(config) ? [...config] : [config];
88
+
89
+ const resetValues = new Map<string, unknown>();
90
+
91
+ while (stack.length > 0) {
92
+ const currentObj = stack.pop();
93
+
94
+ if (!currentObj) continue;
95
+
96
+ for (const [key, value] of Object.entries(currentObj)) {
97
+ if (Array.isArray(value)) {
98
+ for (const item of value) {
99
+ stack.push(item);
100
+ }
101
+ }
102
+
103
+ if (typeof value === 'object' && value !== null) {
104
+ stack.push(value as unknown as ParsedJsonComponent);
105
+ }
106
+
107
+ if (
108
+ key === 'subtype' &&
109
+ 'bind' in currentObj &&
110
+ typeof currentObj.bind === 'string'
111
+ ) {
112
+ resetValues.set(
113
+ currentObj.bind,
114
+ inputs.get(value as string)?.defaultValue,
115
+ );
116
+ }
117
+ }
118
+ }
119
+
120
+ return Object.fromEntries(resetValues);
121
+ };
122
+
123
+ export const TwintagFormProvider = ({
124
+ variant,
125
+ size,
126
+ inputs,
127
+ components,
128
+ config,
129
+ children,
130
+ }: {
131
+ config: RawJsonComponent | RawJsonComponent[];
132
+ variant: TwintagFormContext['variant'];
133
+ size: TwintagFormContext['size'];
134
+ inputs: Inputs;
135
+ components: Components;
136
+ } & WithChildren) => {
137
+ const __config = useMemo(() => {
138
+ if (Array.isArray(config)) {
139
+ return config.map((item) => format(item, components));
140
+ }
141
+
142
+ return format(config, components);
143
+ }, [config, components]);
144
+
145
+ const resetValues = useMemo(() => {
146
+ return getResetValues(__config, inputs);
147
+ }, [__config, inputs]);
148
+
149
+ return (
150
+ <twintagFormContext.Provider
151
+ value={{
152
+ config: __config,
153
+ variant,
154
+ size,
155
+ inputs,
156
+ components,
157
+ resetValues,
158
+ }}
159
+ >
160
+ {children}
161
+ </twintagFormContext.Provider>
162
+ );
163
+ };
package/lib/index.tsx CHANGED
@@ -1,41 +1,41 @@
1
- import type { ComponentProps } from 'react';
2
- import type { RawJsonComponent } from '@twintag/twintag-form/types';
3
-
4
- import { forwardRef } from 'react';
5
-
6
- import { components, Inputs } from '@twintag/twintag-form/components/config';
7
- import { TwintagForm } from '@twintag/twintag-form/components/form';
8
- import { TwintagFormProvider } from '@twintag/twintag-form/components/provider';
9
- import { JsonRenderer } from '@twintag/twintag-form/components/renderer';
10
-
11
- type Props = {
12
- config: RawJsonComponent[];
13
- options?: Partial<{
14
- defaultValues?: ComponentProps<typeof TwintagForm>['defaultValues'];
15
- variant?: ComponentProps<typeof TwintagFormProvider>['variant'];
16
- size?: ComponentProps<typeof TwintagFormProvider>['size'];
17
- resetOnSubmit?: boolean;
18
- }>;
19
- };
20
-
21
- export const Form = forwardRef<HTMLFormElement, Props>(
22
- ({ config, options }, ref) => {
23
- return (
24
- <TwintagFormProvider
25
- inputs={Inputs}
26
- components={components}
27
- variant={options?.variant}
28
- size={options?.size}
29
- config={config}
30
- >
31
- <TwintagForm
32
- ref={ref}
33
- defaultValues={options?.defaultValues}
34
- resetOnSubmit={options?.resetOnSubmit}
35
- >
36
- <JsonRenderer />
37
- </TwintagForm>
38
- </TwintagFormProvider>
39
- );
40
- },
41
- );
1
+ import type { ComponentProps } from 'react';
2
+ import type { RawJsonComponent } from '@twintag/twintag-form/types';
3
+
4
+ import { forwardRef } from 'react';
5
+
6
+ import { components, Inputs } from '@twintag/twintag-form/components/config';
7
+ import { TwintagForm } from '@twintag/twintag-form/components/form';
8
+ import { TwintagFormProvider } from '@twintag/twintag-form/components/provider';
9
+ import { JsonRenderer } from '@twintag/twintag-form/components/renderer';
10
+
11
+ type Props = {
12
+ config: RawJsonComponent[];
13
+ options?: Partial<{
14
+ defaultValues?: ComponentProps<typeof TwintagForm>['defaultValues'];
15
+ variant?: ComponentProps<typeof TwintagFormProvider>['variant'];
16
+ size?: ComponentProps<typeof TwintagFormProvider>['size'];
17
+ resetOnSubmit?: boolean;
18
+ }>;
19
+ };
20
+
21
+ export const Form = forwardRef<HTMLFormElement, Props>(
22
+ ({ config, options }, ref) => {
23
+ return (
24
+ <TwintagFormProvider
25
+ inputs={Inputs}
26
+ components={components}
27
+ variant={options?.variant}
28
+ size={options?.size}
29
+ config={config}
30
+ >
31
+ <TwintagForm
32
+ ref={ref}
33
+ defaultValues={options?.defaultValues}
34
+ resetOnSubmit={options?.resetOnSubmit}
35
+ >
36
+ <JsonRenderer />
37
+ </TwintagForm>
38
+ </TwintagFormProvider>
39
+ );
40
+ },
41
+ );
@@ -1,49 +1,82 @@
1
- import type { ElementType } from 'react';
2
-
3
- export type ComponentConfig = {
4
- defaultValue?: unknown;
5
- component: ElementType;
6
- };
7
-
8
- export type ParsedJsonComponent = Props & {
9
- id: string;
10
- type: string;
11
- subtype?: string;
12
- comp?: ElementType;
13
- };
14
-
15
- export type RawJsonComponent = RawProps &
16
- Record<string, unknown> & {
17
- type: string;
18
- children?: RawJsonComponent[];
19
- } & {
20
- props?: RawProps;
21
- };
22
-
23
- type RawProps = Partial<{
24
- bind: string;
25
- subtype: string;
26
- label: string;
27
- icon: string;
28
- options: Partial<{
29
- description: string;
30
- placeholder: string;
31
- error: string;
32
- optionalField: boolean;
33
- }>;
34
- }>;
35
-
36
- export type Props = Omit<RawProps, 'subtype'> & {
37
- bind: string;
38
- items?: ParsedJsonComponent[];
39
- };
40
-
41
- export type TwintagFormContext = {
42
- variant?: 'default' | 'beta';
43
- size?: 'base' | 'sm';
44
- inputs: Map<string, ComponentConfig>;
45
- components: Map<string, ElementType>;
46
- config: ParsedJsonComponent[] | ParsedJsonComponent;
47
- resetValues: Record<string, unknown>;
48
- };
49
-
1
+ import type { ElementType } from 'react';
2
+
3
+ export type InputCompConfig = {
4
+ defaultValue?: unknown;
5
+ component: ElementType;
6
+ };
7
+
8
+ type CompType = 'input' | 'item' | 'accordion' | 'category';
9
+ type StringLiteral<T> = T extends string
10
+ ? string extends T
11
+ ? never
12
+ : T
13
+ : never;
14
+
15
+ type RawProps<
16
+ TType extends string = string,
17
+ TSubtype extends string = string,
18
+ > = TType extends 'input' | 'item'
19
+ ? {
20
+ type: TType;
21
+ bind: string;
22
+ label?: string;
23
+ subtype?: string;
24
+ options?: Partial<{
25
+ description: string;
26
+ placeholder: string;
27
+ error: string;
28
+ optionalField: boolean;
29
+ }>;
30
+ }
31
+ : TType extends 'accordion'
32
+ ? TSubtype extends 'item'
33
+ ? {
34
+ type: TType;
35
+ subtype: TSubtype;
36
+ value?: string;
37
+ label: string;
38
+ icon: string;
39
+ }
40
+ : TSubtype extends 'multiple' | 'single'
41
+ ? { type: TType; subtype: TSubtype; defaultValue?: string }
42
+ : never
43
+ : {
44
+ type: TType;
45
+ label?: string;
46
+ subtype?: string;
47
+ };
48
+
49
+ export type RawJsonComponent = CompType extends infer T
50
+ ? T extends CompType
51
+ ? {
52
+ type: StringLiteral<T>;
53
+ props?: Omit<RawProps<StringLiteral<T>>, 'type'>;
54
+ children?: RawJsonComponent[];
55
+ }
56
+ : never
57
+ : never;
58
+
59
+ export type ParsedJsonComponent<TType extends string = string> =
60
+ RawProps<TType> & {
61
+ id: string;
62
+ type: string;
63
+ subtype?: string;
64
+ comp?: ElementType;
65
+ items?: ParsedJsonComponent[];
66
+ };
67
+
68
+ export type Props<
69
+ TType extends string = string,
70
+ TSubtype extends string = string,
71
+ > = Omit<RawProps<TType, TSubtype>, 'type'> & {
72
+ items?: (Props<'input'> & ParsedJsonComponent)[];
73
+ };
74
+
75
+ export type TwintagFormContext = {
76
+ variant?: 'default' | 'beta';
77
+ size?: 'base' | 'sm';
78
+ inputs: Map<string, InputCompConfig>;
79
+ components: Map<string, ElementType>;
80
+ config: ParsedJsonComponent[] | ParsedJsonComponent;
81
+ resetValues: Record<string, unknown>;
82
+ };
package/lib/utils/data.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import type { Props } from '@twintag/twintag-form/types';
2
2
 
3
- export const getBindSetFromItems = (items: Props[]): Set<string> => {
3
+ type Items = (Props<'input'> & {
4
+ items?: Items[];
5
+ })[];
6
+ export const getBindSetFromItems = (items: Items): Set<string> => {
4
7
  const binds = new Set<string>();
5
8
 
6
9
  items.forEach((item) => {
@@ -9,7 +12,7 @@ export const getBindSetFromItems = (items: Props[]): Set<string> => {
9
12
  }
10
13
 
11
14
  if (item.items) {
12
- const subBinds = getBindSetFromItems(item.items);
15
+ const subBinds = getBindSetFromItems(item.items as unknown as Items);
13
16
  subBinds.forEach((bind) => binds.add(bind));
14
17
  }
15
18
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twintag/twintag-form",
3
- "version": "1.4.0-beta.1",
3
+ "version": "1.4.0-beta.3",
4
4
  "author": "Twintag",
5
5
  "type": "module",
6
6
  "exports": {