├── .npmignore
├── README.md
├── configure
└── field.js
├── fields
├── abstract.js
├── components
│ ├── base.vue
│ ├── color
│ │ ├── color.vue
│ │ ├── common
│ │ │ ├── Alpha.vue
│ │ │ ├── Checkboard.vue
│ │ │ ├── EditableInput.vue
│ │ │ ├── Hue.vue
│ │ │ └── Saturation.vue
│ │ ├── index.vue
│ │ └── mixin
│ │ │ └── color.js
│ ├── date.vue
│ ├── select.vue
│ ├── switch.vue
│ └── text.vue
├── index.js
└── render-field.js
├── form.vue
├── format
└── index.js
├── index.js
├── model
├── MixinData.js
├── MixinMethods.js
├── MixinProps.js
├── MixinWatch.js
└── index.js
├── package.json
└── result.jpg
/.npmignore:
--------------------------------------------------------------------------------
1 | .git
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 📚 vue-genesis-forms  
2 |
3 | ` 🔥 support working: `
4 | * input, textarea, switch, select, colorpick, datepicker
5 |
6 | ` 🎨 next `
7 | * mult select, toggle, radio
8 |
9 | # simple Demo
10 | [Link](https://codesandbox.io/s/j1z40p4k7w)
11 |
12 | `example usage:`
13 | App.vue
14 |
15 | ```html
16 |
17 |
20 |
21 |
22 |
80 | ```
81 |
82 | # Result:
83 | 
84 |
85 | # Credits:
86 | *Genesis* [Link](https://github.com/phpzm/genesis)
--------------------------------------------------------------------------------
/configure/field.js:
--------------------------------------------------------------------------------
1 | // import { formatOptions, formatPhone, formatMoney, formatDateTime, formatDate, formatBoolean } from '@/bootstrap/support/format'
2 |
3 | export default (field, label, component = 'text', scopes = []) => {
4 | const defaults = ['index', 'view', 'create', 'edit']
5 |
6 | if (!Array.isArray(scopes)) {
7 | scopes = defaults
8 | }
9 | if (!scopes.length) {
10 | scopes = defaults
11 | }
12 |
13 | return {
14 | field: field,
15 | label: label,
16 | scopes: scopes,
17 | form: {component: component},
18 | grid: {},
19 | all: {},
20 | $scopes (scopes) {
21 | this.scopes = scopes
22 | return this
23 | },
24 | $in (scope) {
25 | this.scopes = [scope]
26 | return this
27 | },
28 | $out (scope) {
29 | this.scopes = defaults.filter(item => item !== scope)
30 | return this
31 | },
32 | $form (form) {
33 | return this.$assign('form', form)
34 | },
35 | $grid (grid) {
36 | return this.$assign('grid', grid)
37 | },
38 | $all (all) {
39 | return this.$assign('all', all)
40 | },
41 | $assign (property, options) {
42 | this[property] = Object.assign({}, this[property], options)
43 | return this
44 | },
45 | $filter (rule = 'like', value = '', component = '') {
46 | this.grid.filter = {rule, value, component}
47 | return this
48 | },
49 | $tab (name) {
50 | this.form.tab = name
51 | return this
52 | },
53 | $readonly () {
54 | this.form.disabled = true
55 | return this
56 | },
57 | $pk () {
58 | this.primaryKey = true
59 | return this.$readonly().$out('create').$grid({is: '60px'})
60 | },
61 | $validate (rule, value = true) {
62 | if (!this.form.validate) {
63 | this.form.validate = {}
64 | }
65 | this.form.validate[rule] = value
66 | return this
67 | },
68 | $required (require = true) {
69 | if (require) {
70 | this.$validate('required')
71 | }
72 | return this
73 | },
74 | $link (path) {
75 | this.grid.format = (value, row) => {
76 | let href = String(path)
77 | Object.keys(row).forEach(property => {
78 | href = href.replace(`{${property}}`, row[property])
79 | })
80 | return `${value}`
81 | }
82 | return this
83 | },
84 | $img (className = 'avatar') {
85 | this.grid.format = (value) => {
86 | return `
`
87 | }
88 | return this
89 | },
90 | $checkbox () {
91 | this.form.component = 'checkbox'
92 | // this.grid.format = formatBoolean
93 | return this
94 | },
95 | $color (options = null) {
96 | this.form.component = 'field-color'
97 | if (options) {
98 | this.form.colorType = options
99 | }
100 | return this
101 | },
102 | $date (format = null) {
103 | if (format) {
104 | this.form.format = format
105 | }
106 | this.form.component = 'field-date'
107 | // this.grid.format = formatDate
108 | return this
109 | },
110 | $datetime () {
111 | this.form.component = 'date'
112 | this.form.time = true
113 | // this.grid.format = formatDateTime
114 | return this
115 | },
116 | $file () {
117 | this.form.component = 'file'
118 | return this
119 | },
120 | $html () {
121 | this.form.component = 'html'
122 | return this
123 | },
124 | $input () {
125 | this.form.component = 'field-text'
126 | this.form.input = 'input'
127 | return this
128 | },
129 | $money () {
130 | this.form.component = 'money'
131 | // this.grid.format = formatMoney
132 | return this
133 | },
134 | $numeric () {
135 | this.form.component = 'numeric'
136 | return this
137 | },
138 | $password () {
139 | this.form.component = 'field-text'
140 | this.form.input = 'input'
141 | this.form.type = 'password'
142 | return this
143 | },
144 | $phone () {
145 | this.form.component = 'phone'
146 | // this.grid.format = formatPhone
147 | return this
148 | },
149 | $radio (options = []) {
150 | this.form.component = 'radio'
151 | if (!options.length) {
152 | options = [{value: 1, label: 'Sim'}, {value: 0, label: 'Não'}]
153 | }
154 | this.form.options = options
155 | // this.grid.format = formatOptions(options)
156 | return this
157 | },
158 | $search () {
159 | this.form.component = 'select'
160 | return this
161 | },
162 | $select (options = [], multiple = false) {
163 | this.form.component = 'field-select'
164 | this.form.placeholder = '.:. Selecione uma opção .:.'
165 | this.form.multiple = multiple
166 | this.form.options = options
167 | this.form.expanded = true
168 | // this.grid.format = formatOptions(options)
169 | return this
170 | },
171 | $switch (valueTrue, valueFalse) {
172 | this.form.component = 'field-switch'
173 | this.form.valueTrue = valueTrue
174 | this.form.valueFalse = valueFalse
175 | return this
176 | },
177 | $pivot (options = {}) {
178 | this.form.component = 'pivot'
179 | this.form.options = options
180 | return this
181 | },
182 | $separator () {
183 | this.form.component = 'separator'
184 | return this
185 | },
186 | $text (value = '') {
187 | this.form.component = 'field-text'
188 | this.form.input = 'input'
189 | this.form.type = 'text'
190 | this.form.data = value
191 | return this
192 | },
193 | $textarea (options = null, value) {
194 | if (options) {
195 | this.form[options] = value
196 | }
197 | this.form.component = 'field-text'
198 | this.form.input = 'textarea'
199 | return this
200 | },
201 | $time () {
202 | this.form.component = 'time'
203 | return this
204 | },
205 | $toggle () {
206 | this.form.component = 'toggle'
207 | return this
208 | },
209 | $wysiwyg () {
210 | this.form.component = 'wysiwyg'
211 | return this
212 | },
213 | $event (type, action) {
214 | if (!this.form.events) {
215 | this.form.events = {}
216 | }
217 | this.form.events[type] = action
218 | return this
219 | },
220 | $render () {
221 | const base = {
222 | field: this.field,
223 | label: this.label,
224 | primaryKey: this.primaryKey,
225 | scopes: this.scopes
226 | }
227 | if (this.grid.filter && !this.grid.filter.component) {
228 | this.grid.filter.component = this.form.component
229 | }
230 | return Object.assign({}, base, this.form, this.all)
231 | }
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/fields/abstract.js:
--------------------------------------------------------------------------------
1 | import { unMask } from '../format'
2 |
3 | export default {
4 | props: {
5 | value: {
6 | default: undefined
7 | },
8 | data: {
9 | default: ''
10 | },
11 | label: {
12 | type: String,
13 | default: ''
14 | },
15 | type: {
16 | type: String,
17 | default: 'text'
18 | },
19 | input: {
20 | type: String,
21 | default: 'input'
22 | },
23 | field: {
24 | type: String,
25 | default: 'text'
26 | },
27 | placeholder: {
28 | type: String,
29 | default: ''
30 | },
31 | small: {
32 | type: String,
33 | default: ''
34 | },
35 | width: {
36 | default: '12'
37 | },
38 | minHeight: {
39 | default: null
40 | },
41 | validate: {
42 | default: undefined
43 | },
44 | tooltip: {
45 | default: ''
46 | },
47 | title: {
48 | type: String,
49 | default: 'Este campo possui critérios de validação'
50 | },
51 | mask: {
52 | type: String,
53 | default: ''
54 | },
55 | className: {
56 | type: Array,
57 | default: () => ([])
58 | },
59 | inline: {
60 | type: Boolean,
61 | default: false
62 | },
63 | disabled: {
64 | type: Boolean,
65 | default: false
66 | },
67 | editable: {
68 | type: Boolean,
69 | default: true
70 | },
71 | errors: {
72 | type: Array,
73 | default: () => ([])
74 | },
75 | visible: {
76 | type: Boolean,
77 | default: true
78 | },
79 | events: {
80 | type: Object,
81 | default: () => ({})
82 | },
83 | max: {
84 | default: () => undefined
85 | },
86 | cleanable: {
87 | type: Boolean,
88 | default: () => true
89 | },
90 | cleaning: {
91 | default: () => undefined
92 | }
93 | },
94 | computed: {
95 | classNames () {
96 | const classNames = []
97 |
98 | const width = 'column is-' + String(this.inline ? '100' : this.width)
99 | classNames.push(width)
100 | // classNames.push(this.$options.name)
101 | return classNames
102 | },
103 | problems () {
104 | if (!Array.isArray(this.errors)) {
105 | return []
106 | }
107 | return this.errors.filter(error => !error.status).map(error => ({
108 | path: 'validation.' + error.rule,
109 | parameters: error.parameters
110 | }))
111 | },
112 | name () {
113 | return this.field
114 | }
115 | },
116 | data: () => ({
117 | id: '',
118 | maxlength: ''
119 | }),
120 | methods: {
121 | updateValue (value) {
122 | if (this.mask) {
123 | value = unMask(this.mask, value)
124 | }
125 | if (this.pattern) {
126 | value = unMask(this.pattern, value)
127 | }
128 | this.$emit('input', value, this)
129 | },
130 | /**
131 | * @param {Object}
132 | */
133 | focus () {
134 | if (this.events.focus && typeof this.events.focus === 'function') {
135 | this.$emit('event', 'focus', this)
136 | }
137 | },
138 | /**
139 | * @param {Object}
140 | */
141 | blur () {
142 | if (this.events.blur && typeof this.events.blur === 'function') {
143 | this.$emit('event', 'blur', this)
144 | }
145 | },
146 | /**
147 | * @param {Object}
148 | */
149 | keypress () {
150 | if (this.events.keypress && typeof this.events.keypress === 'function') {
151 | this.$emit('event', 'keypress', this)
152 | }
153 | },
154 | /**
155 | * @param {Object}
156 | */
157 | keyup () {
158 | if (this.events.keyup && typeof this.events.keyup === 'function') {
159 | this.$emit('event', 'keyup', this)
160 | }
161 | },
162 | /**
163 | * @param {Object}
164 | */
165 | enter () {
166 | if (this.events.enter && typeof this.events.enter === 'function') {
167 | this.$emit('event', 'enter', this)
168 | }
169 | }
170 | },
171 | created () {
172 | this.id = Math.random()
173 | this.maxlength = this.max
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/fields/components/base.vue:
--------------------------------------------------------------------------------
1 |
87 |
88 |
91 |
--------------------------------------------------------------------------------
/fields/components/color/color.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
47 |
48 |
49 |
50 |
131 |
132 |
242 |
--------------------------------------------------------------------------------
/fields/components/color/common/Alpha.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
85 |
86 |
--------------------------------------------------------------------------------
/fields/components/color/common/Checkboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
66 |
67 |
77 |
--------------------------------------------------------------------------------
/fields/components/color/common/EditableInput.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 | {{label}}
10 | {{desc}}
11 |
12 |
13 |
14 |
80 |
81 |
--------------------------------------------------------------------------------
/fields/components/color/common/Hue.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
139 |
140 |
176 |
--------------------------------------------------------------------------------
/fields/components/color/common/Saturation.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
102 |
103 |
--------------------------------------------------------------------------------
/fields/components/color/index.vue:
--------------------------------------------------------------------------------
1 |
81 |
82 |
98 |
--------------------------------------------------------------------------------
/fields/components/color/mixin/color.js:
--------------------------------------------------------------------------------
1 | import tinycolor from 'tinycolor2'
2 |
3 | function _colorChange (data, oldHue) {
4 | var alpha = data && data.a
5 | var color
6 |
7 | // hsl is better than hex between conversions
8 | if (data && data.hsl) {
9 | color = tinycolor(data.hsl)
10 | } else if (data && data.hex && data.hex.length > 0) {
11 | color = tinycolor(data.hex)
12 | } else {
13 | color = tinycolor(data)
14 | }
15 |
16 | if (color && (color._a === undefined || color._a === null)) {
17 | color.setAlpha(alpha || 1)
18 | }
19 |
20 | var hsl = color.toHsl()
21 | var hsv = color.toHsv()
22 |
23 | if (hsl.s === 0) {
24 | hsv.h = hsl.h = data.h || (data.hsl && data.hsl.h) || oldHue || 0
25 | }
26 |
27 | return {
28 | hsl: hsl,
29 | hex: color.toHexString().toUpperCase(),
30 | rgba: color.toRgb(),
31 | hsv: hsv,
32 | oldHue: data.h || oldHue || hsl.h,
33 | source: data.source,
34 | a: data.a || color.getAlpha()
35 | }
36 | }
37 |
38 | export default {
39 | props: ['value'],
40 | data () {
41 | return {
42 | val: _colorChange(this.value)
43 | }
44 | },
45 | computed: {
46 | colors: {
47 | get () {
48 | return this.val
49 | },
50 | set (newVal) {
51 | this.val = newVal
52 | this.$emit('input', newVal)
53 | }
54 | }
55 | },
56 | watch: {
57 | value (newVal) {
58 | this.val = _colorChange(newVal)
59 | }
60 | },
61 | methods: {
62 | colorChange (data, oldHue) {
63 | this.oldHue = this.colors.hsl.h
64 | this.colors = _colorChange(data, oldHue || this.oldHue)
65 | },
66 | isValidHex (hex) {
67 | return tinycolor(hex).isValid()
68 | },
69 | simpleCheckForValidColor (data) {
70 | var keysToCheck = ['r', 'g', 'b', 'a', 'h', 's', 'l', 'v']
71 | var checked = 0
72 | var passed = 0
73 |
74 | for (var i = 0; i < keysToCheck.length; i++) {
75 | var letter = keysToCheck[i]
76 | if (data[letter]) {
77 | checked++
78 | if (!isNaN(data[letter])) {
79 | passed++
80 | }
81 | }
82 | }
83 |
84 | if (checked === passed) {
85 | return data
86 | }
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/fields/components/date.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
16 |
17 |
18 |
19 |
79 |
80 |
233 |
--------------------------------------------------------------------------------
/fields/components/select.vue:
--------------------------------------------------------------------------------
1 |
122 |
--------------------------------------------------------------------------------
/fields/components/switch.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/fields/components/text.vue:
--------------------------------------------------------------------------------
1 |
99 |
--------------------------------------------------------------------------------
/fields/index.js:
--------------------------------------------------------------------------------
1 | export { default as FieldText } from './components/text.vue'
2 | export { default as FieldSelect } from './components/select.vue'
3 | export { default as FieldSwitch } from './components/switch.vue'
4 | export { default as FieldColor } from './components/color/index.vue'
5 | export { default as FieldDate } from './components/date.vue'
6 |
--------------------------------------------------------------------------------
/fields/render-field.js:
--------------------------------------------------------------------------------
1 | export const fieldRender = (h, el, ctx) => {
2 | return h('field', { class: el.classNames, props: {
3 | id: el.id,
4 | inline: el.inline,
5 | problems: el.problems,
6 | label: el.label,
7 | validate: el.validate,
8 | title: el.title,
9 | tooltip: el.tooltip,
10 | editable: el.editable,
11 | visible: el.visible
12 | } }, [ctx])
13 | }
--------------------------------------------------------------------------------
/form.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
28 |
29 |
32 |
--------------------------------------------------------------------------------
/format/index.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment'
2 |
3 | export const get = (object, keys, defaultVal) => {
4 | keys = Array.isArray(keys) ? keys : keys.split('.')
5 | object = object[keys[0]]
6 | if (object && keys.length > 1 ) {
7 | return get( object, keys.slice(1))
8 | }
9 | return object === undefined ? defaultVal : object
10 | }
11 |
12 | export const mask = (pattern, value) => {
13 | let masked = ''
14 | value = unMask(pattern, value)
15 | if (!value) {
16 | return ''
17 | }
18 | let j = 0
19 | for (let i = 0; i < pattern.length; i++) {
20 | if (pattern[i] === '*') {
21 | masked += '*'
22 | continue
23 | }
24 | if (j > value.length - 1) {
25 | return masked
26 | }
27 | if (pattern[i] === '#') {
28 | masked += value[j]
29 | j++
30 | continue
31 | }
32 | masked += pattern[i]
33 | }
34 | return masked
35 | }
36 |
37 | export const noDuplicates = (array) => {
38 | const a = array.concat()
39 | for (let i = 0; i < a.length; ++i) {
40 | for (let j = i + 1; j < a.length; ++j) {
41 | if (a[i] === a[j]) {
42 | a.splice(j--, 1)
43 | }
44 | }
45 | }
46 | return a
47 | }
48 |
49 | export const unMask = (pattern, value) => {
50 | const chars = noDuplicates(String(pattern).replace(/[#,*]/g, '').split(''))
51 | return String(value).replace(new RegExp('[' + chars.join(',') + ']', 'g'), '')
52 | }
53 |
54 | export const parseDate = (value, format = 'DD/MM/YYYY') => {
55 | if (!String(value).substring(0, 10).match(/^\d{4}-\d{2}-\d{2}$/)) {
56 | return ''
57 | }
58 | const date = moment(value)
59 | if (!date.isValid()) {
60 | return ''
61 | }
62 | return moment(value).format(format)
63 | }
64 |
65 | export const formatDate = (value) => {
66 | return parseDate(value)
67 | }
68 |
69 | export const formatDateTime = (value) => {
70 | return parseDate(value, 'DD/MM/YYYY HH:mm')
71 | }
72 |
73 | export const formatBoolean = (value) => {
74 | let icon = 'check_box_outline_blank'
75 | if (value) {
76 | icon = 'check_box'
77 | }
78 | return '' + icon + ''
79 | }
80 |
81 | export const formatMoney = (value) => {
82 | return '
' + money(value) + '
'
83 | }
84 |
85 | export const formatPhone = (value) => {
86 | let pattern = '(##) ####-####'
87 | if (length > 13) {
88 | pattern = '(##) #-####-####'
89 | }
90 | return mask(pattern, value)
91 | }
92 |
93 | export const money = (value, precision = 2) => {
94 | if (typeof value === 'undefined') {
95 | value = 0
96 | }
97 | value = Number(value).toFixed(precision)
98 |
99 | value = String(value)
100 | .replace(/\D/g, '')
101 | .replace(/(\d)(\d{11})$/, '$1.$2')
102 | .replace(/(\d)(\d{8})$/, '$1.$2')
103 | .replace(/(\d)(\d{5})$/, '$1.$2')
104 |
105 | return value.replace(/(\d)(\d{2})$/, '$1,$2')
106 | }
107 |
108 | export const formatOptions = (options) => {
109 | return (value) => {
110 | if (Array.isArray(options)) {
111 | const option = options.find(option => String(option.value) === String(value))
112 | return get(option, 'label', '')
113 | }
114 | return value
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Buefy from 'buefy'
3 |
4 | // F // later I fix it, no time to fix it now, so add full Buefy
5 | Vue.use(Buefy)
6 |
7 | import fields from './configure/field'
8 | import * as FieldsComponents from './fields'
9 |
10 | const components = Object.assign({}, FieldsComponents)
11 |
12 | Object.keys(components).forEach(key => {
13 | Vue.component(key, components[key])
14 | })
15 |
16 | export const field = (field, label, component, scopes = []) => {
17 | return fields(field, label, component, scopes)
18 | }
19 |
20 | export const filter = (fields, scope) => {
21 | return fields.filter(field => scope ? field.scopes && field.scopes.includes(scope) : true)
22 | }
23 |
24 | export { default as AppForm } from './form.vue'
25 |
--------------------------------------------------------------------------------
/model/MixinData.js:
--------------------------------------------------------------------------------
1 | export default {
2 | data: () => ({
3 | components: {},
4 | schemas: {},
5 | record: {},
6 | tabSeletecd: '',
7 | currentStep: '',
8 | modified: false
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/model/MixinMethods.js:
--------------------------------------------------------------------------------
1 | import * as Validators from 'vuelidate/lib/validators'
2 |
3 | const arrayToObject = (accumulate, item) => {
4 | accumulate[item.field] = item
5 | return accumulate
6 | }
7 |
8 | export default {
9 | methods: {
10 | /**
11 | */
12 | touch () {
13 | this.$v.$touch()
14 | },
15 | /**
16 | */
17 | reset () {
18 | this.$v.$reset()
19 | },
20 | /**
21 | * @param fields
22 | * @returns {Object}
23 | */
24 | generateValidations (fields) {
25 | if (!Array.isArray(fields)) {
26 | return {}
27 | }
28 | const validations = {}
29 | fields
30 | .filter(schema => !!schema.validate)
31 | .forEach(schema => {
32 | validations[schema.field] = this.configureValidation(schema.validate)
33 | })
34 | return validations
35 | },
36 | /**
37 | * @param {Object} validate
38 | * @return {Object}
39 | */
40 | configureValidation (validate) {
41 | const configure = {}
42 | Object.keys(validate).forEach(property => {
43 | let action = Validators[property]
44 | if (!action.length) {
45 | configure[property] = action
46 | return true
47 | }
48 | configure[property] = action((validate[property]))
49 | })
50 | return configure
51 | },
52 | /**
53 | */
54 | updateComponents () {
55 | const components = {}
56 | if (this.tabs.length) {
57 | this.tabs.forEach(tab => {
58 | components[tab.name] = this.fields.filter(field => field.tab === tab.name).reduce(arrayToObject, {})
59 | })
60 | }
61 | if (this.steps.length) {
62 | this.steps.forEach(step => {
63 | components[step.name] = this.fields.filter(field => field.step === step.name).reduce(arrayToObject, {})
64 | })
65 | }
66 | this.components = components
67 | },
68 | /**
69 | */
70 | updateSchemas () {
71 | this.schemas = this.fields.reduce(arrayToObject, {})
72 | },
73 | /**
74 | */
75 | updateRecord () {
76 | const reduce = (accumulate, field) => {
77 | accumulate[field] = this.data[field] || this.schemas[field].default
78 | if (this.$route.query[field]) {
79 | accumulate[field] = this.$route.query[field]
80 | }
81 | return accumulate
82 | }
83 | const record = Object.keys(this.schemas).reduce(reduce, {})
84 | this.setRecord(record)
85 | },
86 | /**
87 | * @param {Object} record
88 | * @returns this
89 | */
90 | setRecord (record) {
91 | this.record = record
92 | this.executeChange()
93 | return this
94 | },
95 | /**
96 | * @param {string} field
97 | * @param {Object} parameters
98 | */
99 | isProgrammatically (parameters) {
100 | if (typeof parameters !== 'object') {
101 | return false
102 | }
103 | const args = [...parameters]
104 | if (args.length <= 1) {
105 | return false
106 | }
107 | return parameters[1] === true
108 | },
109 | /**
110 | * @param {string} field
111 | * @param {Object} parameters
112 | */
113 | formInput (field, parameters = []) {
114 | const programmatically = this.isProgrammatically(parameters)
115 | if (programmatically) {
116 | return
117 | }
118 | if (this.$v.record[field]) {
119 | this.$v.record[field].$touch()
120 | }
121 | // pass errors to fields
122 | this.schemas[field].errors = this.getErrors(field)
123 |
124 | // emit changes to parent
125 | if (!this.readonly) {
126 | this.fireEvent(field, 'change')
127 | this.$emit('form~input', this.record)
128 | }
129 |
130 | // get invalid fields
131 | const reduce = (accumulate, key) => {
132 | if (this.$v.record[key].$invalid) {
133 | accumulate[key] = true
134 | }
135 | return accumulate
136 | }
137 | const invalids = Object.keys(this.$v.record).reduce(reduce, {})
138 | // emit invalids to parent
139 | this.$emit('form~valid', !this.$v.$invalid, invalids)
140 | },
141 | /**
142 | * @param {string} event
143 | * @param {Vue} $field
144 | * @param {Object} parameters
145 | */
146 | formEvent (event, $field, parameters = {}) {
147 | const field = $field.field
148 | this.fireEvent(field, event, parameters)
149 | },
150 | /**
151 | * @param {string} field
152 | * @param {string} event
153 | * @param {Object} parameters
154 | */
155 | fireEvent (field, event, parameters = {}) {
156 | if (this.schemas[field] && this.schemas[field].events && typeof this.schemas[field].events[event] === 'function') {
157 | this.schemas[field].events[event](this.record, this.schemas, this, parameters)
158 | }
159 | },
160 | /**
161 | */
162 | executeChange () {
163 | if (typeof this.change === 'function') {
164 | this.change(this.record, this.schemas, this)
165 | }
166 | this.modified = true
167 | },
168 | /**
169 | * @param {String} field
170 | * @return {Array}
171 | */
172 | getErrors (field) {
173 | const errors = []
174 | if (this.schemas[field].validate && this.$v.record[field] && this.$v.record[field].$error) {
175 | Object.keys(this.schemas[field].validate).forEach(rule => {
176 | const status = this.$v.record[field][rule]
177 | const parameters = this.$v.record[field].$params[rule]
178 | errors.push({rule, status, parameters})
179 | })
180 | }
181 | return errors
182 | },
183 | /**
184 | * @param {string} namespace
185 | * @param {AxiosResponse} response
186 | */
187 | fireWatch (namespace, response) {
188 | if (this.watches[namespace] && typeof this.watches[namespace] === 'function') {
189 | this.watches[namespace](this.record, this.schemas, this, response)
190 | }
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/model/MixinProps.js:
--------------------------------------------------------------------------------
1 | export default {
2 | props: {
3 | tabs: {
4 | type: Array,
5 | default: () => ([])
6 | },
7 | tab: {
8 | type: String,
9 | default: () => ''
10 | },
11 | steps: {
12 | type: Array,
13 | default: () => ([])
14 | },
15 | step: {
16 | type: String,
17 | default: () => ''
18 | },
19 | fields: {
20 | type: Array,
21 | default: () => ([])
22 | },
23 | data: {
24 | type: Object,
25 | default: () => ({})
26 | },
27 | readonly: {
28 | type: Boolean,
29 | default: () => (false)
30 | },
31 | watches: {
32 | type: Object,
33 | default: () => ({})
34 | },
35 | change: {
36 | type: Function
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/model/MixinWatch.js:
--------------------------------------------------------------------------------
1 | export default {
2 | watch: {
3 | /**
4 | * @param {Object} record
5 | * @param {Object} previous
6 | */
7 | data (record, previous) {
8 | if (!record !== previous) {
9 | this.setRecord(record)
10 | this.fireWatch('set/record')
11 | }
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/model/index.js:
--------------------------------------------------------------------------------
1 | export { default as data } from './MixinData'
2 | export { default as methods } from './MixinMethods'
3 | export { default as props } from './MixinProps'
4 | export { default as watch } from './MixinWatch'
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-genesis-forms",
3 | "version": "0.0.21",
4 | "description": "vue easy create forms",
5 | "main": "index.js",
6 | "readmeFilename": "README.md" ,
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/BlackMix/vue-genesis-forms.git"
13 | },
14 | "keywords": [
15 | "vue",
16 | "forms",
17 | "genesis"
18 | ],
19 | "author": {
20 | "name": "BlackMix",
21 | "email": "mixchatmix3@gmail.com",
22 | "homepage": "https://github.com/BlackMix"
23 | },
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/BlackMix/vue-genesis-forms/issues"
27 | },
28 | "homepage": "https://github.com/BlackMix/vue-genesis-forms#readme",
29 | "dependencies": {
30 | "bulma": "^0.7.1",
31 | "buefy": "^0.6.6",
32 | "stylus": "^0.54.5",
33 | "stylus-loader": "^3.0.2",
34 | "tinycolor2":"^1.4.1",
35 | "lodash.throttle":"^4.1.1",
36 | "moment": "^2.22.2",
37 | "vue": "^2.5.16"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/result.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BlackMix/vue-genesis-forms/6a71d49ebbc082826560fe5dcd4cc645c3b373ed/result.jpg
--------------------------------------------------------------------------------