@akinon/akiform-builder 0.7.0 → 1.0.0

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 (57) hide show
  1. package/dist/cjs/__tests__/akiform-builder.test.d.ts +2 -0
  2. package/dist/cjs/__tests__/akiform-builder.test.d.ts.map +1 -0
  3. package/dist/cjs/__tests__/akiform-builder.test.js +1258 -0
  4. package/dist/cjs/__tests__/field-builder.test.d.ts +2 -0
  5. package/dist/cjs/__tests__/field-builder.test.d.ts.map +1 -0
  6. package/dist/cjs/__tests__/field-builder.test.js +251 -0
  7. package/dist/cjs/index.css +7 -0
  8. package/dist/cjs/src/akiform-builder.d.ts +7 -0
  9. package/dist/cjs/src/akiform-builder.d.ts.map +1 -0
  10. package/dist/cjs/src/akiform-builder.js +291 -0
  11. package/dist/cjs/src/field-builder.d.ts +37 -0
  12. package/dist/cjs/src/field-builder.d.ts.map +1 -0
  13. package/dist/cjs/src/field-builder.js +94 -0
  14. package/dist/cjs/src/i18n/index.d.ts +5 -0
  15. package/dist/cjs/src/i18n/index.d.ts.map +1 -0
  16. package/dist/cjs/src/i18n/index.js +14 -0
  17. package/dist/cjs/src/i18n/translations/en.d.ts +7 -0
  18. package/dist/cjs/src/i18n/translations/en.d.ts.map +1 -0
  19. package/dist/cjs/src/i18n/translations/en.js +8 -0
  20. package/dist/cjs/src/i18n/translations/tr.d.ts +7 -0
  21. package/dist/cjs/src/i18n/translations/tr.d.ts.map +1 -0
  22. package/dist/cjs/src/i18n/translations/tr.js +8 -0
  23. package/dist/cjs/src/index.d.ts +4 -0
  24. package/dist/cjs/src/index.d.ts.map +1 -0
  25. package/dist/cjs/src/index.js +21 -0
  26. package/dist/cjs/src/types.d.ts +84 -0
  27. package/dist/cjs/src/types.d.ts.map +1 -0
  28. package/dist/cjs/src/types.js +2 -0
  29. package/dist/esm/__tests__/akiform-builder.test.d.ts +2 -0
  30. package/dist/esm/__tests__/akiform-builder.test.d.ts.map +1 -0
  31. package/dist/esm/__tests__/akiform-builder.test.js +1256 -0
  32. package/dist/esm/__tests__/field-builder.test.d.ts +2 -0
  33. package/dist/esm/__tests__/field-builder.test.d.ts.map +1 -0
  34. package/dist/esm/__tests__/field-builder.test.js +249 -0
  35. package/dist/esm/index.css +7 -0
  36. package/dist/esm/src/akiform-builder.d.ts +7 -0
  37. package/dist/esm/src/akiform-builder.d.ts.map +1 -0
  38. package/dist/esm/src/akiform-builder.js +288 -0
  39. package/dist/esm/src/field-builder.d.ts +37 -0
  40. package/dist/esm/src/field-builder.d.ts.map +1 -0
  41. package/dist/esm/src/field-builder.js +91 -0
  42. package/dist/esm/src/i18n/index.d.ts +5 -0
  43. package/dist/esm/src/i18n/index.d.ts.map +1 -0
  44. package/dist/esm/src/i18n/index.js +11 -0
  45. package/dist/esm/src/i18n/translations/en.d.ts +7 -0
  46. package/dist/esm/src/i18n/translations/en.d.ts.map +1 -0
  47. package/dist/esm/src/i18n/translations/en.js +6 -0
  48. package/dist/esm/src/i18n/translations/tr.d.ts +7 -0
  49. package/dist/esm/src/i18n/translations/tr.d.ts.map +1 -0
  50. package/dist/esm/src/i18n/translations/tr.js +6 -0
  51. package/dist/esm/src/index.d.ts +4 -0
  52. package/dist/esm/src/index.d.ts.map +1 -0
  53. package/dist/esm/src/index.js +3 -0
  54. package/dist/esm/src/types.d.ts +84 -0
  55. package/dist/esm/src/types.d.ts.map +1 -0
  56. package/dist/esm/src/types.js +1 -0
  57. package/package.json +18 -16
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=field-builder.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-builder.test.d.ts","sourceRoot":"","sources":["../../../__tests__/field-builder.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,251 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const akival_1 = require("@akinon/akival");
4
+ const field_builder_1 = require("../src/field-builder");
5
+ describe('FieldBuilder', () => {
6
+ it('should build a text field with all properties', () => {
7
+ const textField = (0, field_builder_1.field)()
8
+ .key('name')
9
+ .label('Name')
10
+ .type('text')
11
+ .placeholder('Enter your name')
12
+ .defaultValue('John Doe')
13
+ .validation(akival_1.akival.string().required())
14
+ .config({ disabled: false, visible: true })
15
+ .build();
16
+ expect(textField).toEqual({
17
+ key: 'name',
18
+ label: 'Name',
19
+ type: 'text',
20
+ placeholder: 'Enter your name',
21
+ defaultValue: 'John Doe',
22
+ validation: expect.any(Object),
23
+ config: { disabled: false, visible: true }
24
+ });
25
+ });
26
+ it('should build a number field', () => {
27
+ const numberField = (0, field_builder_1.field)()
28
+ .key('age')
29
+ .label('Age')
30
+ .type('number')
31
+ .placeholder('Enter your age')
32
+ .defaultValue(18)
33
+ .validation(akival_1.akival.number().min(18))
34
+ .build();
35
+ expect(numberField).toEqual({
36
+ key: 'age',
37
+ label: 'Age',
38
+ type: 'number',
39
+ placeholder: 'Enter your age',
40
+ defaultValue: 18,
41
+ validation: expect.any(Object)
42
+ });
43
+ });
44
+ it('should build a select field', () => {
45
+ const selectField = (0, field_builder_1.field)()
46
+ .key('country')
47
+ .label('Country')
48
+ .type('select')
49
+ .options([
50
+ { value: 'us', label: 'United States' },
51
+ { value: 'ca', label: 'Canada' }
52
+ ])
53
+ .defaultValue('us')
54
+ .build();
55
+ expect(selectField).toEqual({
56
+ key: 'country',
57
+ label: 'Country',
58
+ type: 'select',
59
+ options: [
60
+ { value: 'us', label: 'United States' },
61
+ { value: 'ca', label: 'Canada' }
62
+ ],
63
+ defaultValue: 'us'
64
+ });
65
+ });
66
+ it('should build a checkbox field', () => {
67
+ const checkboxField = (0, field_builder_1.field)()
68
+ .key('subscribe')
69
+ .label('Subscribe to newsletter')
70
+ .type('checkbox')
71
+ .defaultValue(false)
72
+ .build();
73
+ expect(checkboxField).toEqual({
74
+ key: 'subscribe',
75
+ label: 'Subscribe to newsletter',
76
+ type: 'checkbox',
77
+ defaultValue: false
78
+ });
79
+ });
80
+ it('should build a date field', () => {
81
+ const dateField = (0, field_builder_1.field)()
82
+ .key('birthdate')
83
+ .label('Birth Date')
84
+ .type('date')
85
+ .placeholder('Select your birth date')
86
+ .build();
87
+ expect(dateField).toEqual({
88
+ key: 'birthdate',
89
+ label: 'Birth Date',
90
+ type: 'date',
91
+ placeholder: 'Select your birth date'
92
+ });
93
+ });
94
+ it('should build a textarea field', () => {
95
+ const textareaField = (0, field_builder_1.field)()
96
+ .key('description')
97
+ .label('Description')
98
+ .type('textarea')
99
+ .placeholder('Enter a description')
100
+ .build();
101
+ expect(textareaField).toEqual({
102
+ key: 'description',
103
+ label: 'Description',
104
+ type: 'textarea',
105
+ placeholder: 'Enter a description'
106
+ });
107
+ });
108
+ it('should build a custom field', () => {
109
+ const renderFn = vi.fn();
110
+ const customField = (0, field_builder_1.field)()
111
+ .key('custom')
112
+ .label('Custom Field')
113
+ .type('custom')
114
+ .render(renderFn)
115
+ .build();
116
+ expect(customField).toEqual({
117
+ key: 'custom',
118
+ label: 'Custom Field',
119
+ type: 'custom',
120
+ render: renderFn
121
+ });
122
+ });
123
+ it('should throw an error if required properties are missing', () => {
124
+ expect(() => (0, field_builder_1.field)().build()).toThrow('Field must have at least a key, label, and type');
125
+ expect(() => (0, field_builder_1.field)().key('test').build()).toThrow('Field must have at least a key, label, and type');
126
+ expect(() => (0, field_builder_1.field)().key('test').label('Test').build()).toThrow('Field must have at least a key, label, and type');
127
+ });
128
+ it('should only allow options for select fields', () => {
129
+ const selectField = (0, field_builder_1.field)()
130
+ .key('country')
131
+ .label('Country')
132
+ .type('select')
133
+ .options([
134
+ { value: 'us', label: 'United States' },
135
+ { value: 'ca', label: 'Canada' }
136
+ ])
137
+ .build();
138
+ expect(selectField).toEqual({
139
+ key: 'country',
140
+ label: 'Country',
141
+ type: 'select',
142
+ options: [
143
+ { value: 'us', label: 'United States' },
144
+ { value: 'ca', label: 'Canada' }
145
+ ]
146
+ });
147
+ // @ts-expect-error : We are explicitly waiting .options to throw an error.
148
+ (0, field_builder_1.field)().key('name').label('Name').type('text').options([]);
149
+ });
150
+ it('should only allow render for custom fields', () => {
151
+ const renderFn = vi.fn();
152
+ const customField = (0, field_builder_1.field)()
153
+ .key('custom')
154
+ .label('Custom Field')
155
+ .type('custom')
156
+ .render(renderFn)
157
+ .build();
158
+ expect(customField).toEqual({
159
+ key: 'custom',
160
+ label: 'Custom Field',
161
+ type: 'custom',
162
+ render: renderFn
163
+ });
164
+ // @ts-expect-error : We are explicitly waiting .render to throw an error.
165
+ (0, field_builder_1.field)().key('name').label('Name').type('text').render(vi.fn());
166
+ });
167
+ it('should allow chaining methods in any order', () => {
168
+ const textField = (0, field_builder_1.field)()
169
+ .type('text')
170
+ .key('name')
171
+ .label('Name')
172
+ .placeholder('Enter your name')
173
+ .build();
174
+ expect(textField).toEqual({
175
+ key: 'name',
176
+ label: 'Name',
177
+ type: 'text',
178
+ placeholder: 'Enter your name'
179
+ });
180
+ });
181
+ it('should allow building fields with minimal properties', () => {
182
+ const minimalField = (0, field_builder_1.field)()
183
+ .key('minimal')
184
+ .label('Minimal')
185
+ .type('text')
186
+ .build();
187
+ expect(minimalField).toEqual({
188
+ key: 'minimal',
189
+ label: 'Minimal',
190
+ type: 'text'
191
+ });
192
+ });
193
+ it('should build a field array with fields', () => {
194
+ const fieldArray = (0, field_builder_1.field)()
195
+ .key('addresses')
196
+ .label('Addresses')
197
+ .type('fieldArray')
198
+ .fields([
199
+ (0, field_builder_1.field)().key('street').label('Street').type('text').build(),
200
+ (0, field_builder_1.field)().key('city').label('City').type('text').build()
201
+ ])
202
+ .build();
203
+ expect(fieldArray).toEqual({
204
+ key: 'addresses',
205
+ label: 'Addresses',
206
+ type: 'fieldArray',
207
+ fields: [
208
+ { key: 'street', label: 'Street', type: 'text' },
209
+ { key: 'city', label: 'City', type: 'text' }
210
+ ]
211
+ });
212
+ });
213
+ it('should build a section field with fields', () => {
214
+ const sectionField = (0, field_builder_1.field)()
215
+ .key('personalInfo')
216
+ .label('Personal Information')
217
+ .type('section')
218
+ .fields([
219
+ (0, field_builder_1.field)().key('name').label('Name').type('text').build(),
220
+ (0, field_builder_1.field)().key('age').label('Age').type('number').build()
221
+ ])
222
+ .defaultExpanded(true)
223
+ .build();
224
+ expect(sectionField).toEqual({
225
+ key: 'personalInfo',
226
+ label: 'Personal Information',
227
+ type: 'section',
228
+ fields: [
229
+ { key: 'name', label: 'Name', type: 'text' },
230
+ { key: 'age', label: 'Age', type: 'number' }
231
+ ],
232
+ defaultExpanded: true
233
+ });
234
+ });
235
+ it('should not set fields for non-fieldArray and non-section types', () => {
236
+ const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
237
+ const textField = (0, field_builder_1.field)()
238
+ .key('name')
239
+ .label('Name')
240
+ .type('text')
241
+ .fields([(0, field_builder_1.field)().key('invalid').label('Invalid').type('text').build()])
242
+ .build();
243
+ expect(textField).toEqual({
244
+ key: 'name',
245
+ label: 'Name',
246
+ type: 'text'
247
+ });
248
+ expect(consoleSpy).toHaveBeenCalledWith("Fields can only be set for 'fieldArray' or 'section' types. Current type: text");
249
+ consoleSpy.mockRestore();
250
+ });
251
+ });
@@ -0,0 +1,7 @@
1
+ .akiform-builder .sr-only {
2
+ display: none;
3
+ }
4
+
5
+ .akiform-builder-field-array {
6
+ padding-bottom: 1rem;
7
+ }
@@ -0,0 +1,7 @@
1
+ import './index.css';
2
+ import { FieldValues } from '@akinon/akiform';
3
+ import React from 'react';
4
+ import { AkiformBuilderProps, AkiformBuilderRef } from './types';
5
+ export declare const THROTTLE_DELAY = 300;
6
+ export declare const AkiformBuilder: React.ForwardRefExoticComponent<AkiformBuilderProps<FieldValues> & React.RefAttributes<AkiformBuilderRef<FieldValues>>>;
7
+ //# sourceMappingURL=akiform-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"akiform-builder.d.ts","sourceRoot":"","sources":["../../../src/akiform-builder.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AAErB,OAAO,EAKL,WAAW,EAMZ,MAAM,iBAAiB,CAAC;AAWzB,OAAO,KAWN,MAAM,OAAO,CAAC;AAGf,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EAMlB,MAAM,SAAS,CAAC;AAEjB,eAAO,MAAM,cAAc,MAAM,CAAC;AAgJlC,eAAO,MAAM,cAAc,yHAwS1B,CAAC"}
@@ -0,0 +1,291 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.AkiformBuilder = exports.THROTTLE_DELAY = void 0;
15
+ require("./index.css");
16
+ const akiform_1 = require("@akinon/akiform");
17
+ const akival_1 = require("@akinon/akival");
18
+ const ui_button_1 = require("@akinon/ui-button");
19
+ const ui_checkbox_1 = require("@akinon/ui-checkbox");
20
+ const ui_collapse_1 = require("@akinon/ui-collapse");
21
+ const ui_date_picker_1 = require("@akinon/ui-date-picker");
22
+ const ui_input_1 = require("@akinon/ui-input");
23
+ const ui_input_number_1 = require("@akinon/ui-input-number");
24
+ const ui_select_1 = require("@akinon/ui-select");
25
+ const ui_space_1 = require("@akinon/ui-space");
26
+ const ui_typography_1 = require("@akinon/ui-typography");
27
+ const react_1 = require("react");
28
+ const i18n_1 = require("./i18n");
29
+ exports.THROTTLE_DELAY = 300; // ms
30
+ const SectionComponent = ({ field, control, formValues, formState, layout, layoutOptions }) => {
31
+ return (react_1.default.createElement(ui_collapse_1.Collapse, { defaultActiveKey: field.defaultExpanded ? [field.key] : [], items: [
32
+ {
33
+ label: field.label,
34
+ key: field.key,
35
+ children: field.fields.map(nestedField => {
36
+ var _a, _b;
37
+ const isVisible = typeof ((_a = nestedField.config) === null || _a === void 0 ? void 0 : _a.visible) === 'function'
38
+ ? nestedField.config.visible(formValues)
39
+ : ((_b = nestedField.config) === null || _b === void 0 ? void 0 : _b.visible) !== false;
40
+ if (!isVisible) {
41
+ return null;
42
+ }
43
+ return (react_1.default.createElement(akiform_1.FormItem, { key: nestedField.key, control: control, name: nestedField.key, label: nestedField.label }, renderField(nestedField, control, formValues, formState, layout, layoutOptions)));
44
+ })
45
+ }
46
+ ] }));
47
+ };
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ const renderField = (field, control, formValues,
50
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
+ formState, layout, layoutOptions) => {
52
+ const commonProps = {
53
+ 'aria-required': field.validation ? true : false,
54
+ 'aria-invalid': formState.errors[field.key] ? true : false
55
+ };
56
+ switch (field.type) {
57
+ case 'text':
58
+ return (react_1.default.createElement(ui_input_1.Input, Object.assign({ placeholder: field.placeholder, size: "large" }, commonProps)));
59
+ case 'number':
60
+ return (react_1.default.createElement(ui_input_number_1.InputNumber, Object.assign({ placeholder: field.placeholder, size: "large" }, commonProps)));
61
+ case 'select':
62
+ return (react_1.default.createElement(ui_select_1.Select, Object.assign({ placeholder: field.placeholder, options: field.options }, commonProps)));
63
+ case 'checkbox':
64
+ return (react_1.default.createElement(ui_checkbox_1.Checkbox, Object.assign({ checked: formValues[field.key] }, commonProps), field.label));
65
+ case 'date':
66
+ return (react_1.default.createElement(ui_date_picker_1.DatePicker, Object.assign({ placeholder: field.placeholder, size: "large" }, commonProps)));
67
+ case 'textarea':
68
+ return react_1.default.createElement(ui_input_1.InputTextArea, Object.assign({ placeholder: field.placeholder }, commonProps));
69
+ case 'fieldArray':
70
+ return (react_1.default.createElement(FieldArrayComponent, { field: field, control: control, formValues: formValues, formState: formState, layout: layout, layoutOptions: layoutOptions }));
71
+ case 'custom':
72
+ if (typeof field.render === 'function') {
73
+ return field.render({ field, formValues, control, formState });
74
+ }
75
+ console.warn(`Custom field "${field.key}" has no render function`);
76
+ return react_1.default.createElement(react_1.Fragment, null);
77
+ case 'section':
78
+ return (react_1.default.createElement(SectionComponent, { field: field, control: control, formValues: formValues, formState: formState, layout: layout, layoutOptions: layoutOptions }));
79
+ default:
80
+ return react_1.default.createElement(react_1.Fragment, null);
81
+ }
82
+ };
83
+ exports.AkiformBuilder = (0, react_1.forwardRef)((_a, ref) => {
84
+ var _b, _c;
85
+ var { fields, onSubmit, layout = 'vertical', layoutOptions, showResetButton = false, onReset, controlled = false, values, onValueChange } = _a, rest = __rest(_a, ["fields", "onSubmit", "layout", "layoutOptions", "showResetButton", "onReset", "controlled", "values", "onValueChange"]);
86
+ const validationSchema = (0, react_1.useMemo)(() => {
87
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
88
+ const schema = {};
89
+ fields.forEach(field => {
90
+ if (field.validation) {
91
+ schema[field.key] = field.validation;
92
+ }
93
+ if (field.type === 'fieldArray') {
94
+ schema[field.key] = akival_1.akival.array().of(akival_1.akival.object().shape(field.fields.reduce((acc, nestedField) => {
95
+ if (nestedField.validation) {
96
+ acc[nestedField.key] = nestedField.validation;
97
+ }
98
+ return acc;
99
+ },
100
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
101
+ {})));
102
+ }
103
+ });
104
+ return akival_1.akival.object().shape(schema);
105
+ }, [fields]);
106
+ const { control, handleSubmit, reset, setValue, formState } = (0, akiform_1.useForm)(Object.assign({
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ resolver: (0, akiform_1.akivalResolver)(validationSchema),
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ defaultValues: rest.initialValues }, (controlled && { values: values })));
111
+ const formValues = (0, akiform_1.useWatch)({ control });
112
+ const prevFormValuesRef = (0, react_1.useRef)(null);
113
+ const isInitialRenderRef = (0, react_1.useRef)(true);
114
+ const throttleTimeoutRef = (0, react_1.useRef)(null);
115
+ const handleValueChange = (0, react_1.useCallback)((values) => {
116
+ if (!controlled) {
117
+ if (throttleTimeoutRef.current) {
118
+ clearTimeout(throttleTimeoutRef.current);
119
+ }
120
+ throttleTimeoutRef.current = setTimeout(() => {
121
+ onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(values);
122
+ }, exports.THROTTLE_DELAY);
123
+ }
124
+ else {
125
+ onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(values);
126
+ }
127
+ }, [controlled, onValueChange]);
128
+ (0, react_1.useEffect)(() => {
129
+ if (isInitialRenderRef.current) {
130
+ isInitialRenderRef.current = false;
131
+ prevFormValuesRef.current = Object.assign({}, formValues);
132
+ return;
133
+ }
134
+ if (formState.isDirty ||
135
+ Object.keys(formState.touchedFields).length > 0) {
136
+ const hasChanged = JSON.stringify(formValues) !==
137
+ JSON.stringify(prevFormValuesRef.current);
138
+ if (hasChanged) {
139
+ handleValueChange(formValues);
140
+ prevFormValuesRef.current = Object.assign({}, formValues);
141
+ }
142
+ }
143
+ }, [
144
+ formValues,
145
+ formState.isDirty,
146
+ formState.touchedFields,
147
+ handleValueChange
148
+ ]);
149
+ (0, react_1.useEffect)(() => {
150
+ return () => {
151
+ if (throttleTimeoutRef.current) {
152
+ clearTimeout(throttleTimeoutRef.current);
153
+ }
154
+ };
155
+ }, []);
156
+ (0, react_1.useEffect)(() => {
157
+ if (controlled && onValueChange) {
158
+ onValueChange(formValues);
159
+ }
160
+ }, [controlled, onValueChange, formValues]);
161
+ const formItemLayout = (0, react_1.useMemo)(() => {
162
+ const defaultHorizontalLayout = {
163
+ labelCol: { span: 6 },
164
+ wrapperCol: { span: 18 }
165
+ };
166
+ const defaultInlineLayout = {
167
+ wrapperCol: { span: 24 }
168
+ };
169
+ switch (layout) {
170
+ case 'horizontal':
171
+ return {
172
+ labelCol: (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.labelCol) || defaultHorizontalLayout.labelCol,
173
+ wrapperCol: (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.wrapperCol) || defaultHorizontalLayout.wrapperCol
174
+ };
175
+ case 'inline':
176
+ return {
177
+ wrapperCol: (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.wrapperCol) || defaultInlineLayout.wrapperCol
178
+ };
179
+ case 'vertical':
180
+ default:
181
+ return {};
182
+ }
183
+ }, [layout, layoutOptions]);
184
+ const handleReset = (event) => {
185
+ event.preventDefault();
186
+ reset();
187
+ if (onReset) {
188
+ // Create a synthetic FormEvent if the event is a MouseEvent
189
+ const formEvent = event.type === 'click'
190
+ ? {
191
+ preventDefault: () => { },
192
+ target: event.target.closest('form')
193
+ }
194
+ : event;
195
+ onReset(formEvent);
196
+ }
197
+ };
198
+ (0, react_1.useImperativeHandle)(ref, () => ({
199
+ reset: (values) => {
200
+ if (values) {
201
+ // Partial reset: only reset specified fields
202
+ Object.keys(values).forEach(key => {
203
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
204
+ setValue(key, values[key]);
205
+ });
206
+ }
207
+ else {
208
+ // Full reset
209
+ reset();
210
+ }
211
+ if (onReset) {
212
+ const syntheticEvent = {
213
+ preventDefault: () => { },
214
+ target: { reset: () => { } }
215
+ };
216
+ onReset(syntheticEvent);
217
+ }
218
+ }
219
+ }));
220
+ return (react_1.default.createElement("div", { className: "akiform-builder" },
221
+ react_1.default.createElement(akiform_1.Akiform, Object.assign({ onFinish: handleSubmit(onSubmit), onReset: handleReset, layout: layout }, formItemLayout, rest, { "data-testid": "akiform-builder", role: "form", "aria-label": i18n_1.i18n.t('formLabel'), requiredMark: true }),
222
+ fields.map(field => {
223
+ var _a, _b, _c, _d;
224
+ const isDisabled = typeof ((_a = field.config) === null || _a === void 0 ? void 0 : _a.disabled) === 'function'
225
+ ? field.config.disabled(formValues)
226
+ : (_b = field.config) === null || _b === void 0 ? void 0 : _b.disabled;
227
+ const isVisible = typeof ((_c = field.config) === null || _c === void 0 ? void 0 : _c.visible) === 'function'
228
+ ? field.config.visible(formValues)
229
+ : ((_d = field.config) === null || _d === void 0 ? void 0 : _d.visible) !== false;
230
+ if (!isVisible) {
231
+ return null;
232
+ }
233
+ if (field.type === 'section') {
234
+ return (react_1.default.createElement(SectionComponent, { key: field.key, field: field, control: control, formValues: formValues, formState: formState, layout: layout, layoutOptions: layoutOptions }));
235
+ }
236
+ if (field.type === 'fieldArray') {
237
+ return (react_1.default.createElement("div", { className: "akiform-builder-field-array", key: field.key },
238
+ react_1.default.createElement(ui_typography_1.Title, { level: 5 }, field.label),
239
+ renderField(field, control, formValues, formState, layout, layoutOptions)));
240
+ }
241
+ return (react_1.default.createElement(akiform_1.FormItem, { key: field.key, control: control, name: field.key, label: field.label, disabled: isDisabled, required: field.validation ? true : false, tooltip: field.tooltip }, renderField(field, control, formValues, formState, layout, layoutOptions)));
242
+ }),
243
+ react_1.default.createElement(akiform_1.FormItem, { control: control, name: 'form_actions', wrapperCol: layout === 'horizontal'
244
+ ? {
245
+ offset: (_b = formItemLayout.labelCol) === null || _b === void 0 ? void 0 : _b.span,
246
+ span: (_c = formItemLayout.wrapperCol) === null || _c === void 0 ? void 0 : _c.span
247
+ }
248
+ : undefined },
249
+ react_1.default.createElement(ui_space_1.Space, null,
250
+ react_1.default.createElement(ui_button_1.Button, { type: "primary", htmlType: "submit" }, i18n_1.i18n.t('submit')),
251
+ showResetButton && (react_1.default.createElement(ui_button_1.Button
252
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
253
+ , {
254
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
255
+ onClick: handleReset, type: "default", htmlType: "reset" }, i18n_1.i18n.t('reset'))))))));
256
+ });
257
+ exports.AkiformBuilder.displayName = 'AkiformBuilder';
258
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
259
+ const FieldArrayComponent = ({ field, control, formValues, formState, layout, layoutOptions }) => {
260
+ const { fields, append, remove } = (0, akiform_1.useFieldArray)({
261
+ control,
262
+ name: field.key
263
+ });
264
+ const createInitialValue = () => {
265
+ return field.fields.reduce((acc, nestedField) => {
266
+ acc[nestedField.key] = nestedField.defaultValue || '';
267
+ return acc;
268
+ },
269
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
270
+ {});
271
+ };
272
+ return (react_1.default.createElement("div", { role: "group", "aria-labelledby": `${field.key}-label` },
273
+ react_1.default.createElement("div", { id: `${field.key}-label`, className: "sr-only", "aria-hidden": true }, field.label),
274
+ fields.map((item, index) => (react_1.default.createElement("div", { key: item.id },
275
+ field.fields.map(nestedField => {
276
+ var _a, _b;
277
+ const isVisible = typeof ((_a = nestedField.config) === null || _a === void 0 ? void 0 : _a.visible) === 'function'
278
+ ? nestedField.config.visible(((_b = formValues[field.key]) === null || _b === void 0 ? void 0 : _b[index]) || {})
279
+ : true;
280
+ if (!isVisible) {
281
+ return null;
282
+ }
283
+ return (react_1.default.createElement(akiform_1.FormItem, { key: `${field.key}.${index}.${nestedField.key}`, control: control, name: `${field.key}.${index}.${nestedField.key}`, label: nestedField.label, required: nestedField.validation ? true : false }, renderField(Object.assign(Object.assign({}, nestedField), {
284
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
285
+ key: `${field.key}.${index}.${nestedField.key}` }), control, formValues, formState, layout, layoutOptions)));
286
+ }),
287
+ react_1.default.createElement(ui_button_1.Button, { icon: "eksi", size: "small", danger: true, onClick: () => remove(index), "aria-label": `Remove ${field.label} ${index + 1}` }, "Remove")))),
288
+ react_1.default.createElement(ui_button_1.Button, { icon: "arti", size: "small", type: "text",
289
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
290
+ onClick: () => append(createInitialValue()), "aria-label": `Add ${field.label}` }, "Add")));
291
+ };
@@ -0,0 +1,37 @@
1
+ import { FieldPath, FieldValues } from '@akinon/akiform';
2
+ import { AnySchema } from '@akinon/akival';
3
+ import { TooltipProps } from '@akinon/ui-tooltip';
4
+ import { FieldConfig, FieldType, FormField } from './types';
5
+ type FieldTypeToBuilder<T extends FieldType, TFieldValues extends FieldValues = FieldValues> = T extends 'select' ? SelectFieldBuilder<TFieldValues> : T extends 'custom' ? CustomFieldBuilder<TFieldValues> : T extends 'section' ? SectionFieldBuilder<TFieldValues> : BaseFieldBuilder<TFieldValues>;
6
+ declare class BaseFieldBuilder<TFieldValues extends FieldValues = FieldValues> {
7
+ protected field: Partial<FormField<TFieldValues>>;
8
+ key(key: FieldPath<TFieldValues>): this;
9
+ label(label: string): this;
10
+ type<T extends FieldType>(type: T): FieldTypeToBuilder<T, TFieldValues>;
11
+ placeholder(placeholder: string): this;
12
+ defaultValue(value: any): this;
13
+ validation(schema: AnySchema): this;
14
+ config(config: FieldConfig<TFieldValues>): this;
15
+ fields(fields: FormField<TFieldValues>[]): this;
16
+ tooltip(tooltipProps: TooltipProps | string): this;
17
+ build(): FormField<TFieldValues>;
18
+ }
19
+ declare class SelectFieldBuilder<TFieldValues extends FieldValues = FieldValues> extends BaseFieldBuilder<TFieldValues> {
20
+ options(options: Array<{
21
+ value: string | number;
22
+ label: string;
23
+ }>): this;
24
+ }
25
+ declare class CustomFieldBuilder<TFieldValues extends FieldValues = FieldValues> extends BaseFieldBuilder<TFieldValues> {
26
+ render(renderFn: (props: {
27
+ field: FormField<TFieldValues>;
28
+ formValues: TFieldValues;
29
+ control: any;
30
+ }) => React.ReactElement): this;
31
+ }
32
+ declare class SectionFieldBuilder<TFieldValues extends FieldValues = FieldValues> extends BaseFieldBuilder<TFieldValues> {
33
+ defaultExpanded(expanded: boolean): this;
34
+ }
35
+ export declare function field<TFieldValues extends FieldValues = FieldValues>(): BaseFieldBuilder<TFieldValues> & SelectFieldBuilder<TFieldValues> & CustomFieldBuilder<TFieldValues> & SectionFieldBuilder<TFieldValues>;
36
+ export {};
37
+ //# sourceMappingURL=field-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-builder.d.ts","sourceRoot":"","sources":["../../../src/field-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAEL,WAAW,EACX,SAAS,EACT,SAAS,EAEV,MAAM,SAAS,CAAC;AAEjB,KAAK,kBAAkB,CACrB,CAAC,SAAS,SAAS,EACnB,YAAY,SAAS,WAAW,GAAG,WAAW,IAC5C,CAAC,SAAS,QAAQ,GAClB,kBAAkB,CAAC,YAAY,CAAC,GAChC,CAAC,SAAS,QAAQ,GAChB,kBAAkB,CAAC,YAAY,CAAC,GAChC,CAAC,SAAS,SAAS,GACjB,mBAAmB,CAAC,YAAY,CAAC,GACjC,gBAAgB,CAAC,YAAY,CAAC,CAAC;AAEvC,cAAM,gBAAgB,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW;IACnE,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAM;IAEvD,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,YAAY,CAAC,GAAG,IAAI;IAKvC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK1B,IAAI,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE,CAAC,GAAG,kBAAkB,CAAC,CAAC,EAAE,YAAY,CAAC;IAKvE,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAMtC,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI;IAK9B,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAKnC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,YAAY,CAAC,GAAG,IAAI;IAM/C,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,YAAY,CAAC,EAAE,GAAG,IAAI;IAa/C,OAAO,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI;IAKlD,KAAK,IAAI,SAAS,CAAC,YAAY,CAAC;CAMjC;AAED,cAAM,kBAAkB,CACtB,YAAY,SAAS,WAAW,GAAG,WAAW,CAC9C,SAAQ,gBAAgB,CAAC,YAAY,CAAC;IACtC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;CAKzE;AAED,cAAM,kBAAkB,CACtB,YAAY,SAAS,WAAW,GAAG,WAAW,CAC9C,SAAQ,gBAAgB,CAAC,YAAY,CAAC;IACtC,MAAM,CACJ,QAAQ,EAAE,CAAC,KAAK,EAAE;QAChB,KAAK,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;QAC/B,UAAU,EAAE,YAAY,CAAC;QAEzB,OAAO,EAAE,GAAG,CAAC;KACd,KAAK,KAAK,CAAC,YAAY,GACvB,IAAI;CAKR;AAED,cAAM,mBAAmB,CACvB,YAAY,SAAS,WAAW,GAAG,WAAW,CAC9C,SAAQ,gBAAgB,CAAC,YAAY,CAAC;IACtC,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;CAIzC;AAED,wBAAgB,KAAK,CACnB,YAAY,SAAS,WAAW,GAAG,WAAW,KAC3C,gBAAgB,CAAC,YAAY,CAAC,GACjC,kBAAkB,CAAC,YAAY,CAAC,GAChC,kBAAkB,CAAC,YAAY,CAAC,GAChC,mBAAmB,CAAC,YAAY,CAAC,CAoBlC"}