renusify 2.5.2 → 3.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 (212) hide show
  1. package/components/app/index.vue +74 -22
  2. package/components/app/toast/index.vue +76 -71
  3. package/components/app/toast/toast.vue +62 -44
  4. package/components/avatar/index.vue +208 -84
  5. package/components/button/buttonConfirm.vue +53 -26
  6. package/components/button/buttonGroup.js +0 -2
  7. package/components/button/buttonGroup.vue +310 -62
  8. package/components/button/index.vue +584 -100
  9. package/components/calendar/index.js +0 -2
  10. package/components/calendar/index.vue +326 -262
  11. package/components/calendar/month.vue +64 -55
  12. package/components/calendar/year.vue +30 -25
  13. package/components/card/index.vue +139 -59
  14. package/components/codeEditor/highlightCss.vue +38 -39
  15. package/components/codeEditor/highlightHtml.vue +64 -64
  16. package/components/codeEditor/highlightJs.vue +37 -38
  17. package/components/codeEditor/index.vue +129 -79
  18. package/components/codeEditor/run.vue +225 -39
  19. package/components/codeEditor/useCodeFormatter.js +150 -0
  20. package/components/confirm/index.vue +139 -80
  21. package/components/container/col.vue +5 -4
  22. package/components/container/divider.vue +28 -19
  23. package/components/container/index.vue +34 -15
  24. package/components/container/row.vue +26 -9
  25. package/components/container/spacer.vue +2 -4
  26. package/components/container/style.scss +3 -0
  27. package/components/content/index.vue +49 -32
  28. package/components/cropper/index.vue +401 -244
  29. package/components/float/index.vue +542 -415
  30. package/components/form/addressInput/index.vue +184 -109
  31. package/components/form/camInput/index.vue +370 -244
  32. package/components/form/checkInput/index.vue +138 -71
  33. package/components/form/checkboxInput/index.vue +87 -47
  34. package/components/form/colorInput/Alpha.vue +81 -83
  35. package/components/form/colorInput/Hue.vue +91 -68
  36. package/components/form/colorInput/Preview.vue +43 -47
  37. package/components/form/colorInput/Saturation.vue +101 -86
  38. package/components/form/colorInput/index.vue +71 -39
  39. package/components/form/colorInput/picker.vue +111 -106
  40. package/components/form/colorInput/useColor.js +153 -0
  41. package/components/form/dateInput/index.vue +691 -356
  42. package/components/form/dateInput/month.vue +63 -54
  43. package/components/form/dateInput/year.vue +35 -25
  44. package/components/form/fileInput/index.js +0 -1
  45. package/components/form/fileInput/index.vue +263 -106
  46. package/components/form/fileInput/single.vue +323 -164
  47. package/components/form/groupInput/index.vue +199 -101
  48. package/components/form/index.vue +189 -83
  49. package/components/form/input/index.vue +416 -377
  50. package/components/form/jsonInput/JsonView.vue +54 -56
  51. package/components/form/jsonInput/index.vue +247 -165
  52. package/components/form/maskInput/index.vue +252 -132
  53. package/components/form/numberInput/index.js +0 -1
  54. package/components/form/numberInput/index.vue +226 -117
  55. package/components/form/passwordInput/index.js +2 -1
  56. package/components/form/passwordInput/index.vue +269 -102
  57. package/components/form/radioInput/index.vue +143 -72
  58. package/components/form/rangeInput/index.vue +280 -167
  59. package/components/form/ratingInput/index.vue +57 -57
  60. package/components/form/selectInput/index.js +1 -3
  61. package/components/form/selectInput/index.vue +584 -296
  62. package/components/form/switchInput/index.vue +73 -59
  63. package/components/form/telInput/index.js +0 -1
  64. package/components/form/telInput/index.vue +238 -135
  65. package/components/form/textArea/index.vue +72 -35
  66. package/components/form/textEditor/index.vue +739 -0
  67. package/components/form/{text-editor → textEditor}/style.scss +8 -16
  68. package/components/form/textInput/index.vue +54 -32
  69. package/components/form/timeInput/index.vue +82 -55
  70. package/components/form/timeInput/range.vue +115 -94
  71. package/components/form/timeInput/timepicker.vue +382 -449
  72. package/components/form/uniqueInput/index.vue +105 -48
  73. package/components/form/unitInput/index.vue +139 -84
  74. package/components/formCreator/index.js +0 -1
  75. package/components/formCreator/index.vue +314 -148
  76. package/components/highlight/index.vue +41 -25
  77. package/components/highlight/style.scss +2 -2
  78. package/components/highlight/{mixin.js → useHighlight.js} +181 -160
  79. package/components/icon/index.vue +79 -33
  80. package/components/img/index.vue +249 -147
  81. package/components/img/preview.vue +180 -198
  82. package/components/img/svgImg.vue +42 -39
  83. package/components/index.js +5 -20
  84. package/components/infinite/index.js +1 -2
  85. package/components/infinite/index.vue +248 -66
  86. package/components/map/index.vue +428 -261
  87. package/components/map/route.vue +794 -487
  88. package/components/map/select.vue +118 -58
  89. package/components/menu/index.vue +201 -91
  90. package/components/meta/meta.js +26 -3
  91. package/components/modal/index.vue +382 -156
  92. package/components/notify/index.vue +204 -86
  93. package/components/notify/notification.vue +38 -55
  94. package/components/progress/circle.vue +189 -70
  95. package/components/progress/line.vue +266 -46
  96. package/components/searchBox/index.js +1 -3
  97. package/components/searchBox/index.vue +194 -101
  98. package/components/skeleton/index.vue +45 -20
  99. package/components/slider/index.vue +318 -156
  100. package/components/swiper/index.vue +254 -106
  101. package/components/table/crud/footer.vue +77 -53
  102. package/components/table/crud/header.vue +71 -72
  103. package/components/table/crud/index.vue +629 -399
  104. package/components/table/index.vue +721 -278
  105. package/components/timeAgo/index.vue +145 -96
  106. package/components/tour/index.vue +338 -235
  107. package/components/tree/index.vue +235 -89
  108. package/components/tree/tree-element.vue +106 -106
  109. package/directive/animate/index.js +77 -0
  110. package/directive/clickOutSide/index.js +98 -0
  111. package/directive/drag/index.js +153 -0
  112. package/directive/index.js +11 -13
  113. package/directive/intersect/index.js +263 -0
  114. package/directive/mask/index.js +67 -0
  115. package/directive/parallax/index.js +78 -0
  116. package/directive/ripple/index.js +14 -0
  117. package/directive/scroll/index.js +244 -0
  118. package/directive/sortable/index.js +274 -0
  119. package/directive/title/index.js +75 -0
  120. package/directive/touch/index.js +268 -0
  121. package/index.js +10 -8
  122. package/package.json +5 -2
  123. package/plugins/validation/Validate.js +88 -79
  124. package/scripts/generate-docs.mjs +226 -0
  125. package/scripts/menu.mjs +240 -0
  126. package/scripts/parser.mjs +1086 -0
  127. package/style/_index.scss +7 -0
  128. package/style/app.scss +13 -65
  129. package/style/colors.scss +5 -22
  130. package/style/functions/index.scss +8 -0
  131. package/style/mixins/index.scss +17 -5
  132. package/style/variables/base.scss +154 -175
  133. package/style/variables/color.scss +0 -12
  134. package/style/variables/utilities.scss +0 -180
  135. package/tools/helper.js +0 -8
  136. package/tools/icons.js +6 -1
  137. package/tools/root.js +71 -0
  138. package/components/app/style.scss +0 -41
  139. package/components/app/toast/style.scss +0 -20
  140. package/components/avatar/style.scss +0 -32
  141. package/components/bar/bottomNav.js +0 -1
  142. package/components/bar/bottomNav.vue +0 -28
  143. package/components/bar/bottomNavigationCircle.js +0 -2
  144. package/components/bar/bottomNavigationCircle.vue +0 -99
  145. package/components/bar/scss/bottomNav.scss +0 -67
  146. package/components/bar/scss/toolbar.scss +0 -174
  147. package/components/bar/toolbar/index.js +0 -8
  148. package/components/bar/toolbar/index.vue +0 -35
  149. package/components/bar/toolbar/laptop.vue +0 -33
  150. package/components/bar/toolbar/menuChilds.vue +0 -41
  151. package/components/bar/toolbar/menuLaptop.vue +0 -41
  152. package/components/bar/toolbar/menuMob.vue +0 -39
  153. package/components/bar/toolbar/mixin.js +0 -43
  154. package/components/bar/toolbar/mobile.vue +0 -34
  155. package/components/breadcrumb/bredcrumbItem.vue +0 -39
  156. package/components/breadcrumb/index.js +0 -3
  157. package/components/breadcrumb/index.vue +0 -71
  158. package/components/breadcrumb/style.scss +0 -51
  159. package/components/button/style.scss +0 -411
  160. package/components/card/style.scss +0 -86
  161. package/components/chart/chart.js +0 -1
  162. package/components/chart/chart.vue +0 -69
  163. package/components/chart/worldMap.js +0 -2
  164. package/components/chart/worldMap.vue +0 -1112
  165. package/components/chat/MessageList.vue +0 -163
  166. package/components/chat/chatInput.vue +0 -150
  167. package/components/chat/chatMsg.vue +0 -276
  168. package/components/chat/index.js +0 -11
  169. package/components/chat/index.vue +0 -113
  170. package/components/chip/index.js +0 -3
  171. package/components/chip/index.vue +0 -77
  172. package/components/chip/style.scss +0 -199
  173. package/components/codeEditor/mixin.js +0 -145
  174. package/components/countdown/index.js +0 -1
  175. package/components/countdown/index.vue +0 -105
  176. package/components/form/colorInput/mixin.js +0 -132
  177. package/components/form/fileInput/file.js +0 -148
  178. package/components/form/telInput/assets/flags.png +0 -0
  179. package/components/form/telInput/assets/flags@2x.png +0 -0
  180. package/components/form/text-editor/index.vue +0 -705
  181. package/components/icon/style.scss +0 -17
  182. package/components/infinite/div.js +0 -6
  183. package/components/infinite/div.vue +0 -193
  184. package/components/infinite/page.js +0 -3
  185. package/components/infinite/page.vue +0 -105
  186. package/components/list/index.js +0 -3
  187. package/components/list/index.vue +0 -122
  188. package/components/list/style.scss +0 -66
  189. package/components/message/index.js +0 -4
  190. package/components/message/index.vue +0 -40
  191. package/components/modal/style.scss +0 -146
  192. package/components/nestable/NestableItem.vue +0 -307
  193. package/components/nestable/editable.js +0 -44
  194. package/components/nestable/index.js +0 -1
  195. package/components/nestable/index.vue +0 -226
  196. package/components/nestable/methods.js +0 -416
  197. package/components/progress/style.scss +0 -229
  198. package/components/table/style.scss +0 -338
  199. package/components/tabs/index.js +0 -3
  200. package/components/tabs/index.vue +0 -151
  201. package/components/timeline/index.js +0 -6
  202. package/components/timeline/index.vue +0 -76
  203. package/directive/resize/index.js +0 -30
  204. package/directive/skeleton/index.js +0 -27
  205. package/directive/skeleton/style.scss +0 -37
  206. package/plugins/request/Request.js +0 -68
  207. package/style/animation.scss +0 -94
  208. package/style/style.scss +0 -8
  209. package/tools/rootable.js +0 -75
  210. /package/components/form/{text-editor → textEditor}/index.js +0 -0
  211. /package/components/form/{text-editor → textEditor}/preview.js +0 -0
  212. /package/components/form/{text-editor → textEditor}/preview.vue +0 -0
@@ -1,33 +1,34 @@
1
1
  <template>
2
2
  <r-input :class="`${$r.prefix}group-input`"
3
3
  :model-value="modelValue"
4
- hide>
5
- <div class="w-full">
6
- <span v-if="label" class="group-input-label">{{ label }}</span>
4
+ label-active
5
+ >
6
+ <div>
7
7
  <div :key="key" v-sortable="{grab:'.grab-btn-group-input',end:end}">
8
- <div class="group-holder" v-for="(item,i) in modelValue" :key="i">
9
- <div class="group-slot">
10
- <slot :item="item" :index="i" :disableDel="disDel">
11
- <div v-if="template" class="d-flex flex-wrap pt-5 v-start">
12
- <template v-for="(v,k) in item" :key="i+'-'+k">
13
- <component :is="template[k]['type']" v-if="template[k]&&template[k]['type']" v-model="item[k]"
14
- :label="t(k)"
15
- class="flex-grow-0 me-1" v-bind="template[k]['props']"></component>
16
- </template>
17
- </div>
18
- </slot>
19
- </div>
20
- <div class="group-action">
21
- <r-btn icon text class="grab-btn-group-input">
22
- <r-icon v-html="$r.icons.drag"></r-icon>
23
- </r-btn>
24
- <r-btn v-if="!disableItems.includes(itemKey?item[itemKey]:'')" icon text @click.prevent="del(i)">
25
- <r-icon v-html="$r.icons.delete" class="color-error-text"></r-icon>
26
- </r-btn>
8
+ <div v-for="(item,i) in modelValue" :key="i" class="group-holder">
9
+ <div class="group-slot">
10
+ <!-- Default slot for custom group item content. Provide disableDel, index, item props. -->
11
+ <slot :disableDel="disDel" :index="i" :item="item">
12
+ <div v-if="template" class="d-flex flex-wrap pt-5 v-start">
13
+ <template v-for="(v,k) in item" :key="i+'-'+k">
14
+ <component :is="template[k]['type']" v-if="template[k]&&template[k]['type']" v-model="item[k]"
15
+ :label="t(k)"
16
+ class="flex-grow-0 me-1" v-bind="template[k]['props']"></component>
17
+ </template>
18
+ </div>
19
+ </slot>
20
+ </div>
21
+ <div class="group-action">
22
+ <r-btn class="grab-btn-group-input" icon text>
23
+ <r-icon v-html="$r.icons.drag"></r-icon>
24
+ </r-btn>
25
+ <r-btn v-if="!disableItems.includes(itemKey?item[itemKey]:'')" icon text @click.prevent="del(i)">
26
+ <r-icon class="color-error-text" v-html="$r.icons.delete"></r-icon>
27
+ </r-btn>
28
+ </div>
27
29
  </div>
28
30
  </div>
29
- </div>
30
- <div class="mt-5" :class="addBtnClass">
31
+ <div :class="addBtnClass" class="my-3">
31
32
  <transition name="scale">
32
33
  <r-btn v-if="show_add" class="color-success" icon @click.prevent="add">
33
34
  <r-icon v-html="$r.icons.plus" class="color-white-text"></r-icon>
@@ -37,101 +38,198 @@
37
38
  </div>
38
39
  </r-input>
39
40
  </template>
40
- <script>
41
- export default {
42
- name: 'rGroupInput',
43
- props: {
44
- label: String,
45
- itemKey: String,
46
- modelValue: Array,
47
- size: Number,
48
- disableAdd: Boolean,
49
- translate: Boolean,
50
- template: Object,
51
- addBtnClass: {'type': String, default: 'text-center'}
41
+ <script setup>
42
+ import {ref, computed, inject} from 'vue'
43
+
44
+ const props = defineProps({
45
+ /**
46
+ * Key to identify items that should not be deletable
47
+ * @type {String}
48
+ */
49
+ itemKey: String,
50
+
51
+ /**
52
+ * The model value for the group items (v-model)
53
+ * @type {Array}
54
+ * @default () => []
55
+ */
56
+ modelValue: {
57
+ type: Array,
58
+ default: () => []
52
59
  },
53
- emits:['update:modelValue','add','delete'],
54
- data() {
55
- return {
56
- key: 0,
57
- disabledDel: {}
60
+
61
+ /**
62
+ * Maximum number of items allowed
63
+ * @type {Number}
64
+ */
65
+ size: Number,
66
+
67
+ /**
68
+ * Disables the add button
69
+ * @type {Boolean}
70
+ */
71
+ disableAdd: Boolean,
72
+
73
+ /**
74
+ * Enables translation for item keys
75
+ * @type {Boolean}
76
+ */
77
+ translate: Boolean,
78
+
79
+ /**
80
+ * Template configuration for dynamic form generation
81
+ * @type {Object}
82
+ */
83
+ template: Object,
84
+
85
+ /**
86
+ * CSS class for the add button container
87
+ * @type {String}
88
+ * @default 'text-center'
89
+ */
90
+ addBtnClass: {
91
+ type: String,
92
+ default: 'text-center'
93
+ }
94
+ })
95
+
96
+ const emit = defineEmits([
97
+ /**
98
+ * Emitted when the model value changes
99
+ * @param {Array} items - Updated array of items
100
+ */
101
+ 'update:modelValue',
102
+
103
+ /**
104
+ * Emitted when a new item is added
105
+ * @param {Boolean} true - Indicates an item was added
106
+ */
107
+ 'add',
108
+
109
+ /**
110
+ * Emitted when an item is deleted
111
+ * @param {Number} index - Index of the deleted item
112
+ */
113
+ 'delete'
114
+ ])
115
+
116
+ const {$t, $helper} = inject('renusify')
117
+
118
+ const key = ref(0)
119
+ const disabledDel = ref({})
120
+
121
+ const disableItems = computed(() => Object.keys(disabledDel.value))
122
+
123
+ const show_add = computed(() => {
124
+ if (props.disableAdd) {
125
+ return false
126
+ }
127
+
128
+ const length = $helper.ifHas(props.modelValue, 0, 'length')
129
+ return !(props.size && length >= props.size)
130
+ })
131
+
132
+ /**
133
+ * Handles drag-and-drop reordering completion
134
+ * @param {Array} e - Array of new item indices after reorder
135
+ */
136
+ const end = (e) => {
137
+ const newArray = []
138
+ for (let i = 0; i < e.length; i++) {
139
+ const index = parseInt(e[i])
140
+ if (props.modelValue[index] !== undefined) {
141
+ newArray.push(props.modelValue[index])
58
142
  }
59
- },
60
- computed: {
61
- disableItems() {
62
- return Object.keys(this.disabledDel)
63
- },
64
- show_add() {
65
- if (this.disableAdd) {
66
- return false
143
+ }
144
+
145
+ emit('update:modelValue', newArray)
146
+ key.value++
147
+ }
148
+
149
+ /**
150
+ * Translates a key if translation is enabled
151
+ * @param {String} k - Key to translate
152
+ * @returns {String} Translated text or original key
153
+ */
154
+ const t = (k) => {
155
+ if (props.translate && $t) {
156
+ return $t(k)
157
+ }
158
+ return k
159
+ }
160
+
161
+ /**
162
+ * Marks an item as non-deletable
163
+ * @param {String} value - Item value to disable deletion for
164
+ */
165
+ const disDel = (value) => {
166
+ disabledDel.value[value] = true
167
+ }
168
+
169
+ /**
170
+ * Adds a new item to the group
171
+ */
172
+ const add = () => {
173
+ if (!show_add.value) return
174
+
175
+ const newArray = [...(props.modelValue || [])]
176
+ let newItem = {}
177
+
178
+ if (props.template) {
179
+ for (const key in props.template) {
180
+ let defaultValue = props.template[key]?.default !== undefined
181
+ ? props.template[key].default
182
+ : null
183
+
184
+ if (typeof defaultValue === 'object' && defaultValue !== null) {
185
+ defaultValue = $helper.clearProxy(defaultValue)
67
186
  }
68
- const l = this.$helper.ifHas(this.modelValue, 0, 'length')
69
- return !(this.size && l >= this.size);
187
+
188
+ newItem[key] = defaultValue
70
189
  }
71
- },
72
- methods: {
73
- end(e) {
74
- let a = []
75
- for (let i = 0; i < e.length; i++) {
76
- a.push(this.modelValue[parseInt(e[i])])
77
- }
78
- this.$emit('update:modelValue', a)
79
- this.key++
80
- },
81
- t(k) {
82
- if (this.translate) {
83
- return this.$t(k)
84
- }
85
- return k
86
- },
87
- disDel(value) {
88
- this.disabledDel[value] = true
89
- },
90
- add() {
91
- if (this.show_add) {
92
- let a = this.modelValue || []
93
- let b = {}
94
- if (this.template) {
95
- for (let k in this.template) {
96
- let d = this.template[k]['default'] !== undefined ? this.template[k]['default'] : null
97
- if (typeof d === 'object') {
98
- d = this.$helper.clearProxy(d)
99
- }
100
- b[k] = d
101
- }
102
- }
103
- a.push(b)
104
- this.$emit('add', true)
105
- this.$emit('update:modelValue', a)
106
- }
107
- },
108
- del(i) {
109
- let a = this.modelValue || []
110
- a.splice(i, 1)
111
- this.$emit('delete', i)
112
- this.$emit('update:modelValue', a)
113
- },
114
190
  }
191
+
192
+ newArray.push(newItem)
193
+ emit('add', true)
194
+ emit('update:modelValue', newArray)
195
+ }
196
+
197
+ /**
198
+ * Deletes an item from the group
199
+ * @param {Number} index - Index of the item to delete
200
+ */
201
+ const del = (index) => {
202
+ const newArray = [...(props.modelValue || [])]
203
+ newArray.splice(index, 1)
204
+
205
+ emit('delete', index)
206
+ emit('update:modelValue', newArray)
115
207
  }
116
208
  </script>
117
209
  <style lang="scss">
118
- @use "../../../style/variables/base";
119
- @use "../../../style/mixins";
210
+ @use "../../../style" as *;
211
+
212
+ .#{$prefix}group-input {
213
+ > .input-control {
214
+ height: auto;
215
+ }
120
216
 
121
- .#{base.$prefix}group-input {
122
- .group-input-label {
123
- color: var(--color-on-sheet);
217
+ .group-holder {
218
+ .input-control {
219
+ background-color: var(--color-sheet-container-low);
220
+ }
124
221
  }
222
+
125
223
  .group-holder {
126
224
  position: relative;
127
225
 
128
226
  .group-action {
129
227
  position: absolute;
130
228
  top: 0;
131
- @include mixins.ltr() {
229
+ @include ltr() {
132
230
  right: 0;
133
231
  }
134
- @include mixins.rtl() {
232
+ @include rtl() {
135
233
  left: 0;
136
234
  }
137
235
  }
@@ -1,102 +1,208 @@
1
1
  <template>
2
2
  <form :class="`${$r.prefix}form`" @submit.prevent="submit">
3
+ <!-- Default slot for form content. Provides form context to child input components through dependency injection.
4
+ @example
5
+ <r-text-input label="name" :rules="[`required`]" ></r-text-input>
6
+ <r-text-input label="last name" :rules="[`required`]" ></r-text-input>
7
+ <r-number-input label="age" :rules="[`required`]" ></r-number-input>
8
+ -->
3
9
  <slot></slot>
4
10
  </form>
5
11
  </template>
6
- <script>
7
-
8
- export default {
9
- name: 'r-form',
10
- provide () {
11
- return {
12
- form: {
13
- register: this.register,
14
- unregister: this.unregister
15
- }
16
- }
17
- },
18
- props: {
19
- modelValue: Boolean
20
- },
21
- emits: ['update:modelValue', 'submit'],
22
- data: () => ({
23
- inputs: [],
24
- watchers: [],
25
- errorBag: {}
26
- }),
27
- watch: {
28
- errorBag: {
29
- handler(val) {
30
- const errors = Object.values(val).includes(true)
31
- this.$emit('update:modelValue', !errors)
32
- },
33
-
34
- deep: true,
35
- immediate: true
36
- }
37
- },
38
- methods: {
39
- submit() {
40
- this.$emit('submit', true)
41
- },
42
- watchInput(input) {
43
- const watcher = input => {
44
- return input.$watch('hasError', val => {
45
- this.errorBag[input.uid] = val
46
- }, {
47
- immediate: true
48
- })
49
- }
50
-
51
- const watchers = {
52
- uid: input.uid,
53
- valid: () => {
54
- },
55
- shouldValidate: () => {
56
- }
57
- }
58
-
59
- watchers.valid = watcher(input)
60
-
61
- return watchers
62
- },
63
12
 
64
- /** @public */
65
- validate () {
66
- return this.inputs.filter(input => !input.validate(true)).length === 0
67
- },
13
+ <script setup>
14
+ import {ref, watch, provide, reactive} from 'vue'
68
15
 
69
- /** @public */
70
- reset () {
71
- this.inputs.forEach(input => input.reset())
72
- },
16
+ const props = defineProps({
17
+ /**
18
+ * The form's model value indicating whether it's valid
19
+ * @model
20
+ * @type {Boolean}
21
+ * @default false
22
+ */
23
+ modelValue: Boolean
24
+ })
73
25
 
74
- /** @public */
75
- resetValidation () {
76
- this.inputs.forEach(input => input.resetValidation())
77
- },
26
+ const emit = defineEmits([
27
+ /**
28
+ * Emitted when form validity changes
29
+ * @param {Boolean} isValid - Whether the form is valid
30
+ */
31
+ 'update:modelValue',
32
+
33
+ /**
34
+ * Emitted when form is submitted
35
+ * @param {Boolean} true - Form submission
36
+ */
37
+ 'submit'])
78
38
 
79
- register (input) {
80
- this.inputs.push(input)
81
- this.watchers.push(this.watchInput(input))
39
+ /**
40
+ * Array of registered input components
41
+ * @type {Ref<Array<Object>>}
42
+ */
43
+ const inputs = ref([])
44
+
45
+ /**
46
+ * Array of watchers for input validation states
47
+ * @type {Ref<Array<Object>>}
48
+ */
49
+ const watchers = ref([])
50
+
51
+ /**
52
+ * Reactive error bag containing validation errors
53
+ * @type {Object}
54
+ * @property {Boolean} [inputUid] - Error state for each input by UID
55
+ */
56
+ const errorBag = reactive({})
57
+
58
+ // Watch for changes in errorBag to emit validity updates
59
+ watch(
60
+ () => errorBag,
61
+ (val) => {
62
+ const errors = Object.values(val).includes(true)
63
+ emit('update:modelValue', !errors)
82
64
  },
65
+ {deep: true, immediate: true}
66
+ )
83
67
 
84
- unregister (input) {
85
- const found = this.inputs.find(i => i.uid === input.uid)
86
- if (!found) return
87
- const unwatch = this.watchers.find(i => i.uid === found.uid)
68
+ /**
69
+ * Form submission handler
70
+ * @method submit
71
+ * @fires RForm#submit
72
+ */
73
+ const submit = () => {
74
+ emit('submit', true)
75
+ }
88
76
 
89
- if (unwatch) {
90
- unwatch.valid()
91
- unwatch.shouldValidate()
92
- }
77
+ /**
78
+ * Creates a watcher for an input's error state
79
+ * @method watchInput
80
+ * @param {Object} input - Input component instance
81
+ * @param {String} input.uid - Unique identifier for the input
82
+ * @param {Ref<Boolean>} input.hasError - Reactive error state
83
+ * @returns {Object} Watcher object with UID and cleanup functions
84
+ * @private
85
+ */
86
+ const watchInput = (input) => {
87
+ const watcher = (input) => {
88
+ return watch(input.hasError, val => {
89
+ errorBag[input.uid] = val
90
+ }, {
91
+ immediate: true
92
+ })
93
+ }
93
94
 
94
- this.watchers = this.watchers.filter(i => i.uid !== found.uid)
95
- this.inputs = this.inputs.filter(i => i.uid !== found.uid)
96
- delete this.errorBag[found.uid]
95
+ const watchersObj = {
96
+ uid: input.uid,
97
+ valid: () => {
98
+ },
99
+ shouldValidate: () => {
97
100
  }
101
+ }
102
+
103
+ watchersObj.valid = watcher(input)
98
104
 
105
+ return watchersObj
106
+ }
107
+
108
+ /**
109
+ * Validates all registered form inputs
110
+ * @method validate
111
+ * @returns {Boolean} True if all inputs are valid
112
+ */
113
+ const validate = () => {
114
+ return inputs.value.filter(input => !input.validate(true)).length === 0
115
+ }
116
+
117
+ /**
118
+ * Resets all registered form inputs to their initial state
119
+ * @method reset
120
+ */
121
+ const reset = () => {
122
+ inputs.value.forEach(input => input.reset())
123
+ }
124
+
125
+ /**
126
+ * Resets validation state for all registered inputs
127
+ * @method resetValidation
128
+ */
129
+ const resetValidation = () => {
130
+ inputs.value.forEach(input => input.resetValidation())
131
+ }
132
+
133
+ /**
134
+ * Registers an input component with the form
135
+ * @method register
136
+ * @param {Object} input - Input component instance
137
+ * @private
138
+ */
139
+ const register = (input) => {
140
+ inputs.value.push(input)
141
+ watchers.value.push(watchInput(input))
142
+ }
143
+
144
+ /**
145
+ * Unregisters an input component from the form
146
+ * @method unregister
147
+ * @param {Object} input - Input component instance
148
+ * @private
149
+ */
150
+ const unregister = (input) => {
151
+ const found = inputs.value.find(i => i.uid === input.uid)
152
+ if (!found) return
153
+ const unwatch = watchers.value.find(i => i.uid === found.uid)
154
+
155
+ if (unwatch) {
156
+ unwatch.valid()
157
+ unwatch.shouldValidate()
99
158
  }
100
159
 
160
+ watchers.value = watchers.value.filter(i => i.uid !== found.uid)
161
+ inputs.value = inputs.value.filter(i => i.uid !== found.uid)
162
+ delete errorBag[found.uid]
101
163
  }
164
+
165
+ /**
166
+ * Provides form context to child components
167
+ * @typedef {Object} FormContext
168
+ * @property {Function} register - Function to register input components
169
+ * @property {Function} unregister - Function to unregister input components
170
+ */
171
+ provide('form', {
172
+ register,
173
+ unregister
174
+ })
175
+
176
+ defineExpose({
177
+ /**
178
+ * Form error bag containing validation errors
179
+ * @type {Object}
180
+ */
181
+ errorBag,
182
+
183
+ /**
184
+ * Validates all form inputs
185
+ * @method validate
186
+ * @returns {Boolean} True if all inputs are valid
187
+ */
188
+ validate,
189
+
190
+ /**
191
+ * Resets all form inputs to their initial state
192
+ * @method reset
193
+ */
194
+ reset,
195
+
196
+ /**
197
+ * Resets validation state for all inputs
198
+ * @method resetValidation
199
+ */
200
+ resetValidation,
201
+
202
+ /**
203
+ * Submits the form programmatically
204
+ * @method submit
205
+ */
206
+ submit
207
+ })
102
208
  </script>