renusify 2.5.1 → 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.
- package/components/app/index.vue +74 -22
- package/components/app/toast/index.vue +76 -71
- package/components/app/toast/toast.vue +62 -44
- package/components/avatar/index.vue +208 -84
- package/components/button/buttonConfirm.vue +53 -26
- package/components/button/buttonGroup.js +0 -2
- package/components/button/buttonGroup.vue +310 -62
- package/components/button/index.vue +584 -100
- package/components/calendar/index.js +0 -2
- package/components/calendar/index.vue +326 -262
- package/components/calendar/month.vue +64 -55
- package/components/calendar/year.vue +30 -25
- package/components/card/index.vue +139 -59
- package/components/codeEditor/highlightCss.vue +38 -39
- package/components/codeEditor/highlightHtml.vue +64 -64
- package/components/codeEditor/highlightJs.vue +37 -38
- package/components/codeEditor/index.vue +129 -79
- package/components/codeEditor/run.vue +225 -39
- package/components/codeEditor/useCodeFormatter.js +150 -0
- package/components/confirm/index.vue +140 -81
- package/components/container/col.vue +5 -4
- package/components/container/divider.vue +28 -19
- package/components/container/index.vue +34 -15
- package/components/container/row.vue +26 -9
- package/components/container/spacer.vue +2 -4
- package/components/container/style.scss +3 -0
- package/components/content/index.vue +49 -32
- package/components/cropper/index.vue +401 -244
- package/components/float/index.vue +542 -415
- package/components/form/addressInput/index.vue +184 -109
- package/components/form/camInput/index.vue +370 -244
- package/components/form/checkInput/index.vue +138 -71
- package/components/form/checkboxInput/index.vue +87 -47
- package/components/form/colorInput/Alpha.vue +81 -83
- package/components/form/colorInput/Hue.vue +91 -68
- package/components/form/colorInput/Preview.vue +43 -47
- package/components/form/colorInput/Saturation.vue +101 -86
- package/components/form/colorInput/index.vue +72 -40
- package/components/form/colorInput/picker.vue +111 -106
- package/components/form/colorInput/useColor.js +153 -0
- package/components/form/dateInput/index.vue +691 -356
- package/components/form/dateInput/month.vue +63 -54
- package/components/form/dateInput/year.vue +35 -25
- package/components/form/fileInput/index.js +0 -1
- package/components/form/fileInput/index.vue +263 -106
- package/components/form/fileInput/single.vue +323 -164
- package/components/form/groupInput/index.vue +199 -101
- package/components/form/index.vue +189 -83
- package/components/form/input/index.vue +416 -377
- package/components/form/jsonInput/JsonView.vue +54 -56
- package/components/form/jsonInput/index.vue +247 -165
- package/components/form/maskInput/index.vue +252 -132
- package/components/form/numberInput/index.js +0 -1
- package/components/form/numberInput/index.vue +226 -117
- package/components/form/passwordInput/index.js +2 -1
- package/components/form/passwordInput/index.vue +269 -102
- package/components/form/radioInput/index.vue +143 -72
- package/components/form/rangeInput/index.vue +280 -167
- package/components/form/ratingInput/index.vue +57 -57
- package/components/form/selectInput/index.js +1 -3
- package/components/form/selectInput/index.vue +584 -296
- package/components/form/switchInput/index.vue +73 -59
- package/components/form/telInput/index.js +0 -1
- package/components/form/telInput/index.vue +238 -135
- package/components/form/textArea/index.vue +72 -35
- package/components/form/textEditor/index.vue +739 -0
- package/components/form/{text-editor → textEditor}/style.scss +8 -16
- package/components/form/textInput/index.vue +54 -32
- package/components/form/timeInput/index.vue +83 -56
- package/components/form/timeInput/range.vue +116 -95
- package/components/form/timeInput/timepicker.vue +382 -449
- package/components/form/uniqueInput/index.vue +105 -48
- package/components/form/unitInput/index.vue +139 -84
- package/components/formCreator/index.js +0 -1
- package/components/formCreator/index.vue +314 -148
- package/components/highlight/index.vue +41 -25
- package/components/highlight/style.scss +2 -2
- package/components/highlight/{mixin.js → useHighlight.js} +181 -160
- package/components/icon/index.vue +79 -33
- package/components/img/index.vue +249 -147
- package/components/img/preview.vue +180 -198
- package/components/img/svgImg.vue +42 -39
- package/components/index.js +5 -20
- package/components/infinite/index.js +1 -2
- package/components/infinite/index.vue +248 -66
- package/components/map/index.vue +428 -261
- package/components/map/route.vue +794 -487
- package/components/map/select.vue +118 -58
- package/components/menu/index.vue +201 -91
- package/components/meta/meta.js +26 -3
- package/components/modal/index.vue +383 -158
- package/components/notify/index.vue +204 -86
- package/components/notify/notification.vue +38 -55
- package/components/progress/circle.vue +189 -70
- package/components/progress/line.vue +266 -46
- package/components/searchBox/index.js +1 -3
- package/components/searchBox/index.vue +194 -101
- package/components/skeleton/index.vue +45 -20
- package/components/slider/index.vue +318 -156
- package/components/swiper/index.vue +254 -106
- package/components/table/crud/footer.vue +77 -53
- package/components/table/crud/header.vue +71 -72
- package/components/table/crud/index.vue +631 -401
- package/components/table/index.vue +721 -278
- package/components/timeAgo/index.vue +145 -96
- package/components/tour/index.vue +338 -235
- package/components/tree/index.vue +235 -89
- package/components/tree/tree-element.vue +107 -106
- package/directive/animate/index.js +77 -0
- package/directive/clickOutSide/index.js +98 -0
- package/directive/drag/index.js +153 -0
- package/directive/index.js +11 -13
- package/directive/intersect/index.js +263 -0
- package/directive/mask/index.js +67 -0
- package/directive/parallax/index.js +78 -0
- package/directive/ripple/index.js +14 -0
- package/directive/scroll/index.js +244 -0
- package/directive/sortable/index.js +274 -0
- package/directive/title/index.js +75 -0
- package/directive/touch/index.js +268 -0
- package/index.js +10 -8
- package/package.json +5 -2
- package/plugins/validation/Validate.js +88 -79
- package/scripts/generate-docs.mjs +226 -0
- package/scripts/menu.mjs +240 -0
- package/scripts/parser.mjs +1086 -0
- package/style/_index.scss +7 -0
- package/style/app.scss +13 -65
- package/style/colors.scss +5 -22
- package/style/functions/index.scss +8 -0
- package/style/mixins/index.scss +17 -5
- package/style/variables/base.scss +154 -175
- package/style/variables/color.scss +0 -12
- package/style/variables/utilities.scss +0 -180
- package/tools/helper.js +0 -8
- package/tools/icons.js +6 -1
- package/tools/root.js +71 -0
- package/components/app/style.scss +0 -41
- package/components/app/toast/style.scss +0 -20
- package/components/avatar/style.scss +0 -32
- package/components/bar/bottomNav.js +0 -1
- package/components/bar/bottomNav.vue +0 -28
- package/components/bar/bottomNavigationCircle.js +0 -2
- package/components/bar/bottomNavigationCircle.vue +0 -99
- package/components/bar/scss/bottomNav.scss +0 -67
- package/components/bar/scss/toolbar.scss +0 -174
- package/components/bar/toolbar/index.js +0 -8
- package/components/bar/toolbar/index.vue +0 -35
- package/components/bar/toolbar/laptop.vue +0 -33
- package/components/bar/toolbar/menuChilds.vue +0 -41
- package/components/bar/toolbar/menuLaptop.vue +0 -41
- package/components/bar/toolbar/menuMob.vue +0 -39
- package/components/bar/toolbar/mixin.js +0 -43
- package/components/bar/toolbar/mobile.vue +0 -34
- package/components/breadcrumb/bredcrumbItem.vue +0 -39
- package/components/breadcrumb/index.js +0 -3
- package/components/breadcrumb/index.vue +0 -71
- package/components/breadcrumb/style.scss +0 -51
- package/components/button/style.scss +0 -411
- package/components/card/style.scss +0 -86
- package/components/chart/chart.js +0 -1
- package/components/chart/chart.vue +0 -69
- package/components/chart/worldMap.js +0 -2
- package/components/chart/worldMap.vue +0 -1112
- package/components/chat/MessageList.vue +0 -163
- package/components/chat/chatInput.vue +0 -150
- package/components/chat/chatMsg.vue +0 -276
- package/components/chat/index.js +0 -11
- package/components/chat/index.vue +0 -113
- package/components/chip/index.js +0 -3
- package/components/chip/index.vue +0 -77
- package/components/chip/style.scss +0 -199
- package/components/codeEditor/mixin.js +0 -145
- package/components/countdown/index.js +0 -1
- package/components/countdown/index.vue +0 -105
- package/components/form/colorInput/mixin.js +0 -132
- package/components/form/fileInput/file.js +0 -148
- package/components/form/telInput/assets/flags.png +0 -0
- package/components/form/telInput/assets/flags@2x.png +0 -0
- package/components/form/text-editor/index.vue +0 -710
- package/components/icon/style.scss +0 -17
- package/components/infinite/div.js +0 -6
- package/components/infinite/div.vue +0 -193
- package/components/infinite/page.js +0 -3
- package/components/infinite/page.vue +0 -105
- package/components/list/index.js +0 -3
- package/components/list/index.vue +0 -122
- package/components/list/style.scss +0 -66
- package/components/message/index.js +0 -4
- package/components/message/index.vue +0 -40
- package/components/modal/style.scss +0 -146
- package/components/nestable/NestableItem.vue +0 -307
- package/components/nestable/editable.js +0 -44
- package/components/nestable/index.js +0 -1
- package/components/nestable/index.vue +0 -226
- package/components/nestable/methods.js +0 -416
- package/components/progress/style.scss +0 -229
- package/components/table/style.scss +0 -338
- package/components/tabs/index.js +0 -3
- package/components/tabs/index.vue +0 -151
- package/components/timeline/index.js +0 -6
- package/components/timeline/index.vue +0 -76
- package/directive/resize/index.js +0 -30
- package/directive/skeleton/index.js +0 -27
- package/directive/skeleton/style.scss +0 -37
- package/plugins/request/Request.js +0 -68
- package/style/animation.scss +0 -94
- package/style/style.scss +0 -8
- package/tools/rootable.js +0 -75
- /package/components/form/{text-editor → textEditor}/index.js +0 -0
- /package/components/form/{text-editor → textEditor}/preview.js +0 -0
- /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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
label-active
|
|
5
|
+
>
|
|
6
|
+
<div>
|
|
7
7
|
<div :key="key" v-sortable="{grab:'.grab-btn-group-input',end:end}">
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
<r-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<r-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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
|
|
119
|
-
|
|
210
|
+
@use "../../../style" as *;
|
|
211
|
+
|
|
212
|
+
.#{$prefix}group-input {
|
|
213
|
+
> .input-control {
|
|
214
|
+
height: auto;
|
|
215
|
+
}
|
|
120
216
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
|
229
|
+
@include ltr() {
|
|
132
230
|
right: 0;
|
|
133
231
|
}
|
|
134
|
-
@include
|
|
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
|
-
|
|
65
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Form submission handler
|
|
70
|
+
* @method submit
|
|
71
|
+
* @fires RForm#submit
|
|
72
|
+
*/
|
|
73
|
+
const submit = () => {
|
|
74
|
+
emit('submit', true)
|
|
75
|
+
}
|
|
88
76
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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>
|