native-document 1.0.138 → 1.0.140

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 (259) hide show
  1. package/components.js +1 -1
  2. package/devtools/ComponentRegistry.js +3 -2
  3. package/dist/native-document.components.min.css +1 -0
  4. package/dist/native-document.components.min.js +11506 -8566
  5. package/dist/native-document.dev.js +2645 -2166
  6. package/dist/native-document.dev.js.map +1 -1
  7. package/dist/native-document.min.js +1 -1
  8. package/index.js +1 -0
  9. package/package.json +5 -2
  10. package/rollup.config.js +5 -2
  11. package/src/components/$traits/has-draggable/HasDraggable.js +69 -0
  12. package/src/components/$traits/has-draggable/has-draggable.css +8 -0
  13. package/src/components/$traits/{HasItems.js → has-items/HasItems.js} +2 -11
  14. package/src/components/$traits/has-position/HasFullPosition.js +51 -0
  15. package/src/components/$traits/has-position/HasPosition.js +23 -0
  16. package/src/components/$traits/has-resizable/HasResizable.js +113 -0
  17. package/src/components/$traits/has-resizable/has-resizable.css +121 -0
  18. package/src/components/$traits/has-validation/HasValidation.js +86 -0
  19. package/src/components/BaseComponent.js +109 -4
  20. package/src/components/accordion/Accordion.js +37 -31
  21. package/src/components/accordion/AccordionItem.js +14 -21
  22. package/src/components/alert/Alert.js +48 -40
  23. package/src/components/avatar/Avatar.js +4 -3
  24. package/src/components/avatar/AvatarGroup.js +8 -2
  25. package/src/components/badge/Badge.js +7 -7
  26. package/src/components/base-component.css +0 -0
  27. package/src/components/breadcrumb/BreadCrumb.js +30 -21
  28. package/src/components/button/Button.js +3 -14
  29. package/src/components/context-menu/ContextMenu.js +49 -26
  30. package/src/components/dropdown/Dropdown.js +172 -34
  31. package/src/components/dropdown/DropdownDivider.js +4 -3
  32. package/src/components/dropdown/DropdownGroup.js +19 -19
  33. package/src/components/dropdown/DropdownItem.js +24 -18
  34. package/src/components/dropdown/helpers.js +52 -0
  35. package/src/components/form/FormControl.js +207 -108
  36. package/src/components/form/field/Field.js +106 -174
  37. package/src/components/form/field/FieldCollection.js +110 -203
  38. package/src/components/form/field/types/AutocompleteField.js +26 -5
  39. package/src/components/form/field/types/CheckboxField.js +4 -4
  40. package/src/components/form/field/types/CheckboxGroupField.js +11 -5
  41. package/src/components/form/field/types/ColorField.js +4 -4
  42. package/src/components/form/field/types/DateField.js +110 -18
  43. package/src/components/form/field/types/EmailField.js +2 -2
  44. package/src/components/form/field/types/FileField.js +87 -21
  45. package/src/components/form/field/types/HiddenField.js +4 -4
  46. package/src/components/form/field/types/ImageField.js +4 -4
  47. package/src/components/form/field/types/NumberField.js +5 -5
  48. package/src/components/form/field/types/PasswordField.js +28 -6
  49. package/src/components/form/field/types/RadioField.js +12 -8
  50. package/src/components/form/field/types/RangeField.js +29 -5
  51. package/src/components/form/field/types/SearchField.js +4 -4
  52. package/src/components/form/field/types/SelectField.js +75 -8
  53. package/src/components/form/field/types/StringField.js +4 -4
  54. package/src/components/form/field/types/TelField.js +4 -4
  55. package/src/components/form/field/types/TextAreaField.js +7 -5
  56. package/src/components/form/field/types/TimeField.js +52 -6
  57. package/src/components/form/field/types/UrlField.js +4 -5
  58. package/src/components/form/field/types/file-field-mode/FileAvatarMode.js +104 -0
  59. package/src/components/form/field/types/file-field-mode/FileDropzoneMode.js +59 -0
  60. package/src/components/form/field/types/file-field-mode/FileItemPreview.js +74 -0
  61. package/src/components/form/field/types/file-field-mode/FileNativeMode.js +21 -0
  62. package/src/components/form/field/types/file-field-mode/FileUploadButtonMode.js +59 -0
  63. package/src/components/form/field/types/file-field-mode/FileWallMode.js +53 -0
  64. package/src/components/form/index.js +14 -2
  65. package/src/components/form/validation/Validation.js +9 -0
  66. package/src/components/list/List.js +1 -1
  67. package/src/components/list/ListGroup.js +1 -1
  68. package/src/components/menu/HasMenuItem.js +133 -0
  69. package/src/components/menu/Menu.js +47 -56
  70. package/src/components/menu/MenuGroup.js +36 -8
  71. package/src/components/menu/MenuItem.js +24 -10
  72. package/src/components/menu/MenuLink.js +2 -0
  73. package/src/components/modal/Modal.js +153 -23
  74. package/src/components/pagination/Pagination.js +55 -21
  75. package/src/components/popover/Popover.js +127 -40
  76. package/src/components/progress/Progress.js +14 -24
  77. package/src/components/skeleton/Skeleton.js +36 -13
  78. package/src/components/slider/Slider.js +96 -70
  79. package/src/components/spinner/Spinner.js +4 -2
  80. package/src/components/splitter/Splitter.js +15 -8
  81. package/src/components/splitter/SplitterGutter.js +28 -7
  82. package/src/components/splitter/SplitterPanel.js +9 -15
  83. package/src/components/splitter/index.js +3 -1
  84. package/src/components/stacks/AbsoluteStack.js +33 -0
  85. package/src/components/stacks/FixedStack.js +33 -0
  86. package/src/components/stacks/PositionStack.js +146 -0
  87. package/src/components/stacks/RelativeStack.js +33 -0
  88. package/src/components/{layouts → stacks}/Stack.js +22 -3
  89. package/src/components/{layouts → stacks}/index.js +6 -4
  90. package/src/components/stepper/Stepper.js +159 -67
  91. package/src/components/stepper/StepperStep.js +49 -12
  92. package/src/components/switch/Switch.js +59 -29
  93. package/src/components/switch/index.js +6 -0
  94. package/src/components/table/Column.js +22 -26
  95. package/src/components/table/ColumnGroup.js +4 -13
  96. package/src/components/table/DataTable.js +388 -103
  97. package/src/components/table/SimpleTable.js +28 -143
  98. package/src/components/tabs/Tabs.js +142 -44
  99. package/src/components/toast/Toast.js +40 -35
  100. package/src/components/tooltip/Tooltip.js +152 -35
  101. package/src/core/data/ObservableArray.js +94 -30
  102. package/src/core/data/ObservableChecker.js +20 -75
  103. package/src/core/data/ObservableItem.js +35 -3
  104. package/src/core/data/observable-helpers/computed.js +2 -1
  105. package/src/core/data/observable-helpers/object.js +13 -8
  106. package/src/core/data/observable-helpers/observable.is-to.js +196 -0
  107. package/src/core/elements/anchor/anchor.js +3 -2
  108. package/src/core/elements/control/for-each-array.js +44 -30
  109. package/src/core/elements/control/for-each.js +6 -3
  110. package/src/core/elements/control/show-if.js +11 -6
  111. package/src/core/elements/control/switch.js +2 -1
  112. package/src/core/elements/index.js +1 -1
  113. package/src/core/elements/svg.js +61 -0
  114. package/src/core/utils/HasEventEmitter.js +6 -0
  115. package/src/core/utils/debug-manager.js +2 -5
  116. package/src/core/utils/filters/standard.js +2 -1
  117. package/src/core/utils/property-accumulator.js +35 -6
  118. package/src/core/utils/shortcut-manager.js +242 -0
  119. package/src/core/utils/validator.js +1 -1
  120. package/src/core/wrappers/AttributesWrapper.js +41 -4
  121. package/src/core/wrappers/DocumentObserver.js +0 -1
  122. package/src/core/wrappers/HtmlElementWrapper.js +1 -1
  123. package/src/core/wrappers/NDElement.js +15 -2
  124. package/src/core/wrappers/SvgElementWrapper.js +15 -0
  125. package/src/core/wrappers/prototypes/attributes-extensions.js +4 -1
  126. package/src/core/wrappers/prototypes/nd-element-extensions.js +8 -1
  127. package/src/router/Router.js +0 -1
  128. package/src/ui/components/accordion/AccordionItemRender.js +63 -0
  129. package/src/ui/components/accordion/AccordionRender.js +34 -0
  130. package/src/ui/components/accordion/accordion.css +121 -0
  131. package/src/ui/components/alert/AlertRender.js +80 -0
  132. package/src/ui/components/alert/alert.css +163 -0
  133. package/src/ui/components/avatar/avata-group/AvatarGroupRender.js +49 -0
  134. package/src/ui/components/avatar/avata-group/avatar-group.css +38 -0
  135. package/src/ui/components/avatar/avatar/AvatarRender.js +87 -0
  136. package/src/ui/components/avatar/avatar/avatar.css +189 -0
  137. package/src/ui/components/badge/BadgeRender.js +24 -0
  138. package/src/ui/components/badge/badge.css +168 -0
  139. package/src/ui/components/breadcrumb/BreadcrumbRender.js +43 -0
  140. package/src/ui/components/breadcrumb/breadcrumb.css +55 -0
  141. package/src/ui/components/button/ButtonRender.js +65 -0
  142. package/src/ui/components/button/button.css +288 -354
  143. package/src/ui/components/contextmenu/ContextmenuRender.js +70 -0
  144. package/src/ui/components/contextmenu/contextmenu.css +36 -0
  145. package/src/ui/components/divider/DividerRender.js +70 -0
  146. package/src/ui/components/divider/divider.css +70 -0
  147. package/src/ui/components/dropdown/DropdownRender.js +92 -0
  148. package/src/ui/components/dropdown/divider/DropdownDividerRender.js +9 -0
  149. package/src/ui/components/dropdown/divider/dropdown-divider.css +0 -0
  150. package/src/ui/components/dropdown/dropdown.css +179 -0
  151. package/src/ui/components/dropdown/group/DropdownGroupRender.js +23 -0
  152. package/src/ui/components/dropdown/group/dropdown-group.css +0 -0
  153. package/src/ui/components/dropdown/item/DropdownItemRender.js +29 -0
  154. package/src/ui/components/dropdown/item/dropdown-item.css +0 -0
  155. package/src/ui/components/form/FieldCollectionRender.js +110 -0
  156. package/src/ui/components/form/FormControlRender.js +84 -0
  157. package/src/ui/components/form/field-collection.css +55 -0
  158. package/src/ui/components/form/fields/AutocompleteFieldRender.js +143 -0
  159. package/src/ui/components/form/fields/CheckboxFieldRender.js +59 -0
  160. package/src/ui/components/form/fields/CheckboxGroupFieldRender.js +92 -0
  161. package/src/ui/components/form/fields/ColorFieldRender.js +30 -0
  162. package/src/ui/components/form/fields/DateFieldRender.js +154 -0
  163. package/src/ui/components/form/fields/EmailFieldRender.js +5 -0
  164. package/src/ui/components/form/fields/FieldRender.js +117 -0
  165. package/src/ui/components/form/fields/FileFieldRender.js +41 -0
  166. package/src/ui/components/form/fields/HiddenFieldRender.js +14 -0
  167. package/src/ui/components/form/fields/ImageFieldRender.js +0 -0
  168. package/src/ui/components/form/fields/NumberFieldRender.js +52 -0
  169. package/src/ui/components/form/fields/PasswordFieldRender.js +65 -0
  170. package/src/ui/components/form/fields/RadioFieldRender.js +77 -0
  171. package/src/ui/components/form/fields/RangeFieldRender.js +121 -0
  172. package/src/ui/components/form/fields/SelectFieldRender.js +248 -0
  173. package/src/ui/components/form/fields/SliderFieldRender.js +359 -0
  174. package/src/ui/components/form/fields/StringFieldRender.js +6 -0
  175. package/src/ui/components/form/fields/TelFieldRender.js +6 -0
  176. package/src/ui/components/form/fields/TextAreaFieldRender.js +96 -0
  177. package/src/ui/components/form/fields/TimeFieldRender.js +141 -0
  178. package/src/ui/components/form/fields/UrlFieldRender.js +6 -0
  179. package/src/ui/components/form/fields/date-field.css +32 -0
  180. package/src/ui/components/form/fields/field.css +402 -0
  181. package/src/ui/components/form/fields/file-field.css +79 -0
  182. package/src/ui/components/form/fields/password-field.css +50 -0
  183. package/src/ui/components/form/fields/range-field.css +120 -0
  184. package/src/ui/components/form/fields/slider.css +195 -0
  185. package/src/ui/components/form/file-upload-mode/FileAvatarModeRender.js +143 -0
  186. package/src/ui/components/form/file-upload-mode/FileDropzoneModeRender.js +108 -0
  187. package/src/ui/components/form/file-upload-mode/FileNativeModeRender.js +22 -0
  188. package/src/ui/components/form/file-upload-mode/FileUploadButtonModeRender.js +89 -0
  189. package/src/ui/components/form/file-upload-mode/FileWallModeRender.js +91 -0
  190. package/src/ui/components/form/file-upload-mode/file-avatar-mode.css +139 -0
  191. package/src/ui/components/form/file-upload-mode/file-dropzone-mode.css +88 -0
  192. package/src/ui/components/form/file-upload-mode/file-upload-button-mode.css +44 -0
  193. package/src/ui/components/form/file-upload-mode/file-wall-mode.css +88 -0
  194. package/src/ui/components/form/form-control.css +40 -0
  195. package/src/ui/components/form/helpers.js +112 -0
  196. package/src/ui/components/form/index.js +61 -0
  197. package/src/ui/components/menu/MenuDividerRender.js +12 -0
  198. package/src/ui/components/menu/MenuGroupRender.js +60 -0
  199. package/src/ui/components/menu/MenuItemRender.js +57 -0
  200. package/src/ui/components/menu/MenuLinkRender.js +55 -0
  201. package/src/ui/components/menu/MenuRender.js +21 -0
  202. package/src/ui/components/menu/helpers.js +121 -0
  203. package/src/ui/components/menu/menu.css +308 -0
  204. package/src/ui/components/modal/ModalRender.js +119 -0
  205. package/src/ui/components/modal/modal.css +156 -0
  206. package/src/ui/components/pagination/PaginationRender.js +112 -0
  207. package/src/ui/components/pagination/pagination.css +63 -0
  208. package/src/ui/components/popover/PopoverRender.js +234 -0
  209. package/src/ui/components/popover/popover.css +139 -0
  210. package/src/ui/components/progress/ProgressRender.js +168 -0
  211. package/src/ui/components/progress/progress.css +197 -0
  212. package/src/ui/components/skeleton/SkeletonRender.js +79 -0
  213. package/src/ui/components/skeleton/skeleton.css +154 -0
  214. package/src/ui/components/spinner/SpinnerRender.js +46 -0
  215. package/src/ui/components/spinner/spinner.css +152 -0
  216. package/src/ui/components/splitter/SplitterGutterRender.js +94 -0
  217. package/src/ui/components/splitter/SplitterPanelRender.js +38 -0
  218. package/src/ui/components/splitter/SplitterRender.js +74 -0
  219. package/src/ui/components/splitter/splitter.css +128 -0
  220. package/src/ui/components/stacks/PositionStackRender.js +38 -0
  221. package/src/ui/components/stacks/StackRender.js +42 -0
  222. package/src/ui/components/stacks/absolute-stack/AbsoluteStackRender.js +5 -0
  223. package/src/ui/components/stacks/fixed-stack/FixedStackRender.js +5 -0
  224. package/src/ui/components/stacks/h-stack/HStackRender.js +7 -0
  225. package/src/ui/components/stacks/h-stack/h-stack.css +4 -0
  226. package/src/ui/components/stacks/index.js +15 -0
  227. package/src/ui/components/stacks/position-stack.css +62 -0
  228. package/src/ui/components/stacks/relative-stack/RelativeStackRender.js +7 -0
  229. package/src/ui/components/stacks/relative-stack/relative-stack.css +3 -0
  230. package/src/ui/components/stacks/stack.css +78 -0
  231. package/src/ui/components/stacks/v-stack/VStackRender.js +5 -0
  232. package/src/ui/components/stacks/v-stack/v-stack.css +4 -0
  233. package/src/ui/components/stepper/StepperRender.js +70 -0
  234. package/src/ui/components/stepper/StepperStepRender.js +67 -0
  235. package/src/ui/components/stepper/stepper.css +359 -0
  236. package/src/ui/components/switch/SwitchRender.js +82 -0
  237. package/src/ui/components/switch/switch.css +143 -0
  238. package/src/ui/components/table/data-table/DataTableRender.js +49 -0
  239. package/src/ui/components/table/data-table/bulk-actions.js +35 -0
  240. package/src/ui/components/table/data-table/data-table.css +246 -0
  241. package/src/ui/components/table/data-table/pagination.js +56 -0
  242. package/src/ui/components/table/data-table/tables.js +363 -0
  243. package/src/ui/components/table/data-table/toolbar.js +67 -0
  244. package/src/ui/components/table/simple-table/SimpleTableRender.js +188 -0
  245. package/src/ui/components/table/simple-table/simple-table.css +50 -0
  246. package/src/ui/components/tabs/TabsRender.js +226 -0
  247. package/src/ui/components/tabs/tabs.css +253 -0
  248. package/src/ui/components/toast/ToastRender.js +98 -0
  249. package/src/ui/components/toast/toast.css +201 -0
  250. package/src/ui/components/tooltip/TooltipRender.js +8 -0
  251. package/src/ui/components/tooltip/tooltip.css +113 -0
  252. package/src/ui/index.js +82 -0
  253. package/src/ui/tokens/colors-dark.scss +2 -1
  254. package/src/ui/tokens/reset.scss +3 -0
  255. package/types/control-flow.d.ts +2 -2
  256. package/src/components/layouts/ZStack.js +0 -41
  257. package/src/ui/components/button/button.render.js +0 -63
  258. /package/src/components/{layouts → stacks}/HStack.js +0 -0
  259. /package/src/components/{layouts → stacks}/VStack.js +0 -0
@@ -1,149 +1,227 @@
1
1
  import {debounce} from "../../core/utils/helpers";
2
2
  import HasEventEmitter from "../../core/utils/HasEventEmitter";
3
- import {Validator, Observable as $ } from "../../../index";
3
+ import {Validator, Observable as $} from "../../../index";
4
4
  import NativeDocumentError from "../../../src/core/errors/NativeDocumentError";
5
5
  import BaseComponent from "../BaseComponent";
6
6
 
7
- /**
8
- * @param { {data: Observable } } configs
9
- * @constructor
10
- */
11
- export default function FormControl(configs) {
7
+ export default function FormControl(props) {
12
8
  if(!(this instanceof FormControl)) {
13
- return new FormControl(configs);
9
+ return new FormControl(props);
14
10
  }
15
- HasEventEmitter.call(this);
16
-
17
- this.$element = null;
18
- this.$configs = configs;
19
- this.$fields = new Map();
20
- this.$submitting = $(false);
21
- this.$errors = $(null);
22
- this.$isDirty = $(false);
23
- this.$isValid = $(false);
11
+ BaseComponent.call(this, props);
12
+
13
+ this.$fields = {};
14
+
15
+ this.$description = {
16
+ data: props?.data || null,
17
+ fieldBuilder: null,
18
+ layout: null,
19
+ errorsMode: 'dispatch',
20
+ errorsPosition: 'bottom',
21
+ errorsMapper: null,
22
+ renderErrors: null,
23
+ submitting: $(false),
24
+ errors: $(null),
25
+ isDirty: $(false),
26
+ isValid: $(false),
27
+ props
28
+ };
24
29
  }
25
30
 
26
- FormControl.defaultLayoutTemplate = null;
31
+ FormControl.defaultTemplate = null;
27
32
 
28
33
  FormControl.use = function(template) {
29
- FormControl.defaultLayoutTemplate = template.formControl;
34
+ FormControl.defaultTemplate = template;
30
35
  };
31
36
 
32
- FormControl.create = function (configs) {
33
- return new FormControl(configs);
37
+ FormControl.create = function(props) {
38
+ return new FormControl(props);
34
39
  };
35
40
 
36
- BaseComponent.extends(FormControl, HasEventEmitter);
41
+ BaseComponent.extends(FormControl);
42
+ BaseComponent.use(FormControl, HasEventEmitter);
37
43
 
38
44
  Object.defineProperty(FormControl.prototype, 'isDirty', {
39
- get() { return this.$isDirty; }
45
+ get() { return this.$description.isDirty; }
40
46
  });
41
47
 
42
48
  Object.defineProperty(FormControl.prototype, 'isValid', {
43
- get() { return this.$isValid; }
49
+ get() { return this.$description.isValid; }
44
50
  });
45
51
 
46
52
  Object.defineProperty(FormControl.prototype, 'submitting', {
47
- get() { return this.$submitting; }
53
+ get() { return this.$description.submitting; }
48
54
  });
49
55
 
50
- FormControl.prototype.layout = function(layoutCallback) {
51
- if (typeof layoutCallback !== 'function') {
52
- throw new Error('Layout must be a function');
56
+ FormControl.prototype.fields = function(fieldBuilder) {
57
+ if(typeof fieldBuilder !== 'function') {
58
+ throw new NativeDocumentError('FormControl.fields() expects a function');
53
59
  }
60
+ this.$description.fieldBuilder = (...args) => {
61
+ const result = fieldBuilder(...args);
62
+ const dirties = [];
63
+ for(const [name, field] of Object.entries(result)) {
64
+ this.$registerField(name, field);
65
+ dirties.push(field.$description.isDirty);
66
+ }
67
+
68
+ $.computed(() => {
69
+ const isDirty = dirties.some((item) => item.val());
70
+ this.$description.isDirty.set(isDirty);
71
+ }, dirties);
54
72
 
55
- this.$layout = layoutCallback;
73
+ return result;
74
+ };
56
75
  return this;
57
76
  };
58
77
 
59
- FormControl.prototype.field = function(field) {
60
- const name = field.$description.name;
61
- this.$fields.set(name, field);
78
+ FormControl.prototype.$registerField = function(name, field) {
79
+ this.$fields[name] = field;
62
80
 
63
- const dataSource = this.$configs?.data?.[name];
81
+ const dataSource = this.$description.data?.[name];
64
82
  if(!dataSource) {
65
- return this;
83
+ return;
66
84
  }
67
85
 
68
86
  if(Validator.isObservable(dataSource)) {
69
87
  field.model(dataSource);
70
- dataSource.subscribe(() => {
71
- this.$isDirty.set(true);
72
- this.emit('change', name, dataSource, field);
73
- });
74
- return this;
88
+ return;
75
89
  }
76
90
 
77
91
  field.value(dataSource);
78
-
79
- return this;
80
92
  };
81
93
 
82
94
  FormControl.prototype.get = function(fieldName) {
83
- const field = this.$fields.get(fieldName);
84
- if (!field) {
85
- throw new Error(`Field "${fieldName}" not found in form`);
95
+ const field = this.$fields[fieldName];
96
+ if(!field) {
97
+ throw new NativeDocumentError(`Field "${fieldName}" not found in form`);
86
98
  }
87
99
  return field;
88
100
  };
89
101
 
102
+ FormControl.prototype.layout = function(layoutCallback) {
103
+ if(typeof layoutCallback !== 'function') {
104
+ throw new NativeDocumentError('FormControl.layout() expects a function');
105
+ }
106
+ this.$description.layout = layoutCallback;
107
+ return this;
108
+ };
90
109
 
91
- FormControl.prototype.reset = function() {
92
- this.$isDirty.set(false);
93
- this.$isValid.set(true);
110
+ FormControl.prototype.errorsMode = function(mode) {
111
+ this.$description.errorsMode = mode;
112
+ return this;
113
+ };
94
114
 
95
- this.$configs?.data?.reset();
115
+ FormControl.prototype.dispatchErrors = function(mapper = null) {
116
+ this.$description.errorsMode = this.$description.errorsMode === 'summary' ? 'both' : 'dispatch';
117
+ this.$description.errorsMapper = mapper;
118
+ return this;
119
+ };
96
120
 
97
- this.emit('reset');
121
+ FormControl.prototype.summarizeErrors = function() {
122
+ this.$description.errorsMode = this.$description.errorsMode === 'dispatch' ? 'both' : 'summary';
98
123
  return this;
99
124
  };
100
125
 
101
- FormControl.prototype.submit = function() {
102
- this.$element?.submit();
126
+ FormControl.prototype.dispatchAndSummarize = function(mapper = null) {
127
+ this.$description.errorsMode = 'both';
128
+ this.$description.errorsMapper = mapper;
103
129
  return this;
104
130
  };
105
131
 
106
- FormControl.prototype.$handleSubmit = async function(event) {
107
- this.emit('beforeSubmit', event, this);
132
+ FormControl.prototype.errorsPosition = function(position) {
133
+ this.$description.errorsPosition = position;
134
+ return this;
135
+ };
108
136
 
109
- const values = this.values();
110
- const isValid = await this.validate(values);
111
- if (!isValid) {
112
- event.preventDefault();
113
- this.emit('validationError', this.$errors.val(), this);
137
+ FormControl.prototype.errorsAtTop = function() {
138
+ this.$description.errorsPosition = 'top';
139
+ return this;
140
+ };
141
+
142
+ FormControl.prototype.errorsAtBottom = function() {
143
+ this.$description.errorsPosition = 'bottom';
144
+ return this;
145
+ };
146
+
147
+ FormControl.prototype.renderErrors = function(renderFn) {
148
+ this.$description.renderErrors = renderFn;
149
+ return this;
150
+ };
151
+
152
+ FormControl.prototype.$dispatchServerErrors = function(error) {
153
+ const serverErrors = error.fields;
154
+ const mapped = this.$description.errorsMapper
155
+ ? this.$description.errorsMapper(serverErrors)
156
+ : serverErrors;
157
+
158
+ if(!mapped) {
114
159
  return;
115
160
  }
116
161
 
117
- this.$submitting.set(true);
162
+ for(const [fieldName, errors] of Object.entries(mapped)) {
163
+ const field = this.$fields[fieldName];
164
+ if(field) {
165
+ const errs = Array.isArray(errors) ? errors : [errors];
166
+ field.setError(errs);
167
+ }
168
+ }
169
+ };
118
170
 
119
- try {
120
- const result = await this.emit('submit', event, values);
171
+ FormControl.prototype.reset = function() {
172
+ this.$description.isDirty.set(false);
173
+ this.$description.isValid.set(false);
174
+ this.$description.errors.set(null);
121
175
 
122
- this.emit('success', result, values, this);
176
+ this.$description.data?.reset?.();
123
177
 
124
- return result;
178
+ for(const [_, field] of Object.entries(this.$fields)) {
179
+ field.reset();
180
+ }
125
181
 
126
- } catch (error) {
127
- this.emit('error', error, this);
128
- if(!this.hasListeners('error')) {
129
- throw error;
130
- }
131
- } finally {
132
- this.$submitting.set(false);
133
- this.emit('afterSubmit', this);
182
+ this.emit('reset');
183
+ return this;
184
+ };
185
+
186
+ FormControl.prototype.resetField = function(name) {
187
+ const field = this.$fields[name];
188
+ if(!field) {
189
+ return this;
134
190
  }
191
+ field.reset()
192
+ return this;
193
+ };
194
+
195
+ FormControl.prototype.submit = function(event) {
196
+ return this.$handleSubmit(event);
197
+ };
198
+
199
+ FormControl.prototype.trigger = function(...fieldNames) {
200
+ const values = this.values();
201
+ fieldNames.forEach(name => {
202
+ this.$fields[name]?.validate(values);
203
+ });
204
+ return this;
135
205
  };
136
206
 
137
- FormControl.prototype.disable = function() {
138
- for (const [_, field] of this.$fields) {
207
+ FormControl.prototype.disable = function(fieldName = null) {
208
+ if(fieldName) {
209
+ this.$fields[fieldName]?.disabled?.(true);
210
+ return this;
211
+ }
212
+ for(const [_, field] of Object.entries(this.$fields)) {
139
213
  field.disabled?.(true);
140
214
  }
141
215
  this.emit('disable');
142
216
  return this;
143
217
  };
144
218
 
145
- FormControl.prototype.enable = function() {
146
- for (const [_, field] of this.$fields) {
219
+ FormControl.prototype.enable = function(fieldName = null) {
220
+ if(fieldName) {
221
+ this.$fields[fieldName]?.disabled?.(false);
222
+ return this;
223
+ }
224
+ for(const [_, field] of Object.entries(this.$fields)) {
147
225
  field.disabled?.(false);
148
226
  }
149
227
  this.emit('enable');
@@ -152,33 +230,73 @@ FormControl.prototype.enable = function() {
152
230
 
153
231
  FormControl.prototype.values = function() {
154
232
  const values = {};
155
- for(const [_, field] of this.$fields) {
156
- values[field.$description.name] = field.value();
233
+ for(const [name, field] of Object.entries(this.$fields)) {
234
+ values[name] = field.value();
157
235
  }
158
236
  return values;
159
237
  };
160
238
 
239
+ FormControl.prototype.watch = function(fieldName, handler) {
240
+ const field = this.$fields[fieldName];
241
+ if(!field) {
242
+ return this;
243
+ }
244
+ field.$description.value?.subscribe(handler);
245
+ return this;
246
+ };
247
+
161
248
  FormControl.prototype.validate = async function(allValues) {
162
249
  const errors = {};
163
250
  const values = allValues || this.values();
164
251
 
165
- for (const [name, field] of this.$fields) {
252
+ for(const [name, field] of Object.entries(this.$fields)) {
166
253
  const fieldErrors = await field.validate(values);
167
254
 
168
- if (fieldErrors && fieldErrors.length > 0) {
169
- errors[name] = fieldErrors;
255
+ if(fieldErrors && fieldErrors.errors?.length > 0) {
256
+ errors[name] = fieldErrors.errors;
170
257
  }
171
258
  }
172
259
 
173
260
  const hasError = Object.keys(errors).length > 0;
174
261
 
175
- this.$errors.set(hasError ? errors : null);
176
-
177
- this.$isValid.set(!hasError);
262
+ this.$description.errors.set(hasError ? errors : null);
263
+ this.$description.isValid.set(!hasError);
178
264
 
179
265
  this.emit('validate', !hasError, errors);
180
266
 
181
- return Object.keys(errors).length === 0;
267
+ return !hasError;
268
+ };
269
+
270
+ FormControl.prototype.$handleSubmit = async function(event) {
271
+ this.emit('beforeSubmit', event, this);
272
+
273
+ const values = this.values();
274
+ const isValid = await this.validate(values);
275
+
276
+ if(!isValid) {
277
+ event.preventDefault();
278
+ this.emit('validationError', this.$description.errors.val(), this);
279
+ return;
280
+ }
281
+
282
+ this.$description.submitting.set(true);
283
+
284
+ try {
285
+ const result = await this.emit('submit', event, values);
286
+ this.emit('success', result, values, this);
287
+
288
+ return result;
289
+
290
+ } catch(error) {
291
+ this.$dispatchServerErrors(error);
292
+ this.emit('error', error, this);
293
+ if(!this.hasListeners('error')) {
294
+ throw error;
295
+ }
296
+ } finally {
297
+ this.$description.submitting.set(false);
298
+ this.emit('afterSubmit', this);
299
+ }
182
300
  };
183
301
 
184
302
  FormControl.prototype.onSubmit = function(callback) {
@@ -198,12 +316,12 @@ FormControl.prototype.onDebouncedSubmit = function(callback, delay = 300) {
198
316
  return this.onSubmit(debounce(callback.bind(this), delay));
199
317
  };
200
318
 
201
- FormControl.prototype.onSuccess = function (callback) {
319
+ FormControl.prototype.onSuccess = function(callback) {
202
320
  this.on('success', callback);
203
321
  return this;
204
322
  };
205
323
 
206
- FormControl.prototype.onError = function (callback) {
324
+ FormControl.prototype.onError = function(callback) {
207
325
  this.on('error', callback);
208
326
  return this;
209
327
  };
@@ -217,39 +335,20 @@ FormControl.prototype.onReset = function(callback) {
217
335
  this.on('reset', callback);
218
336
  return this;
219
337
  };
338
+
220
339
  FormControl.prototype.onBeforeSubmit = function(callback) {
221
340
  this.on('beforeSubmit', callback);
222
341
  return this;
223
342
  };
343
+
224
344
  FormControl.prototype.onAfterSubmit = function(callback) {
225
345
  this.on('afterSubmit', callback);
226
346
  return this;
227
347
  };
348
+
228
349
  FormControl.prototype.onValidationError = function(callback) {
229
350
  this.on('validationError', callback);
230
351
  return this;
231
352
  };
232
353
 
233
-
234
- FormControl.prototype.$build = function() {
235
- const fieldsObject = {};
236
- for (const [name, field] of this.$fields) {
237
- fieldsObject[name] = field;
238
- }
239
-
240
- const layoutFn = this.$layout || FormControl.defaultLayoutTemplate;
241
-
242
- const form = layoutFn({
243
- fields: fieldsObject,
244
- form: this
245
- });
246
- if(!((form instanceof HTMLFormElement) || form?.$element instanceof HTMLFormElement)) {
247
- throw new NativeDocumentError('Layout must return an HtmlFormElement');
248
- }
249
- const self = this;
250
- form.nd.onSubmit(async function(event) {
251
- return await self.$handleSubmit(event);
252
- });
253
-
254
- return form;
255
- };
354
+ FormControl.prototype.onInvalid = FormControl.prototype.onValidationError;