diff --git a/quasar.conf.js b/quasar.conf.js
index f0400c0..c6f9fe2 100644
--- a/quasar.conf.js
+++ b/quasar.conf.js
@@ -83,6 +83,7 @@ module.exports = function (context) {
'QCheckbox',
'QSelect',
'QRadio',
+ 'QOptionGroup',
'QCard',
'QCardSection',
'QCardActions',
diff --git a/src/App.vue b/src/App.vue
index ad78797..de17315 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -6,7 +6,7 @@
/>
- .github-fork-ribbon.left-bottom:before
+ .github-fork-ribbon.left-top:before
background-color #333
diff --git a/src/app/Prototype/Base.js b/src/app/Prototype/Base.js
index d2420a5..99602c3 100644
--- a/src/app/Prototype/Base.js
+++ b/src/app/Prototype/Base.js
@@ -1,5 +1,6 @@
/* eslint-disable no-underscore-dangle */
import components from 'src/config/app/components'
+import { apply } from 'src/app/Util'
/**
* @typedef {Base}
@@ -74,6 +75,11 @@ export default class Base {
*/
static mixins = []
+ /**
+ * @type {boolean}
+ */
+ i18n = true
+
/**
* @param {Object} options
* @returns {this}
@@ -91,7 +97,10 @@ export default class Base {
this.__actions = {}
this.__sections = {}
+ this.__loaded = {}
+
this.init()
+ this.locale()
if (this.defaults && typeof this.defaults === 'function') {
this.defaults()
@@ -111,6 +120,29 @@ export default class Base {
this.constructor.mixins.forEach(mixin => this.mixin(mixin))
}
+ /**
+ */
+ locale () {
+ if (!this.i18n) {
+ return
+ }
+ this.namespace = this.constructor.domain.replace(/\//, '.')
+ const map = (piece) => piece.charAt(0).toUpperCase() + piece.substring(1)
+ const domain = this.namespace.split('.').map(map).join('/')
+
+ const locale = window.app.i18n.locale
+ const path = `src/domains/${domain}/${locale}`
+ if (this.__loaded[path]) {
+ return
+ }
+ this.__loaded[path] = true
+
+ const messages = require(`src/domains/${domain}/${locale}`)
+ const translations = apply({}, `domains.${this.namespace}`, messages.default)
+
+ window.app.i18n.mergeLocaleMessage(locale, translations)
+ }
+
/**
* @param {Object} mixin
*/
diff --git a/src/app/Prototype/Components/Form/PrototypeComponents.js b/src/app/Prototype/Components/Form/PrototypeComponents.js
index 08a27b0..865c193 100644
--- a/src/app/Prototype/Components/Form/PrototypeComponents.js
+++ b/src/app/Prototype/Components/Form/PrototypeComponents.js
@@ -37,13 +37,10 @@ export default {
const key = field.$key
const error = this.fieldHasError(key)
- const style = {
- display: field.$layout.formHidden ? 'none' : ''
- }
const data = {
key: key,
class: this.fieldClass(field.$layout.formWidth, field.$layout.formHeight, error),
- domProps: { style }
+ style: { display: field.$layout.formHidden ? 'none' : '' }
}
const children = [
diff --git a/src/app/Prototype/Contracts/Basic.js b/src/app/Prototype/Contracts/Basic.js
index ff6d00b..e0270d7 100644
--- a/src/app/Prototype/Contracts/Basic.js
+++ b/src/app/Prototype/Contracts/Basic.js
@@ -123,6 +123,19 @@ export default {
getFieldLayout () {
// will override by specialists
},
+ /**
+ * @param {string} property
+ * @param {*} value
+ */
+ setRecord (property, value) {
+ // will override by specialists
+ },
+ /**
+ * @param {string} property
+ */
+ getRecord (property) {
+ // will override by specialists
+ },
/**
* @param {Function} h
* @param {string} position
diff --git a/src/app/Prototype/Contracts/Button.js b/src/app/Prototype/Contracts/Button.js
index eba0ef5..4ef2d2f 100644
--- a/src/app/Prototype/Contracts/Button.js
+++ b/src/app/Prototype/Contracts/Button.js
@@ -49,6 +49,15 @@ export default {
return action
})
}
+
+ const label = this.$lang([
+ `domains.${this.domain}.actions.${button.$key}.label`,
+ `prototype.actions.${button.$key}.label`
+ ])
+ if (label) {
+ button.attrs.label = label
+ }
+
buttons[button.$key] = button
return buttons
},
diff --git a/src/app/Prototype/Contracts/Form/FormComponents.js b/src/app/Prototype/Contracts/Form/FormComponents.js
index 40cbe03..fc5d750 100644
--- a/src/app/Prototype/Contracts/Form/FormComponents.js
+++ b/src/app/Prototype/Contracts/Form/FormComponents.js
@@ -162,6 +162,16 @@ export default {
field.attrs.after = this.parseFieldAfter(field)
}
+ if (field.attrs.label) {
+ field.attrs.label = this.$lang(
+ [
+ `domains.${this.domain}.fields.${field.$key}.${field.attrs.label}`,
+ `domains.${this.domain}.${field.attrs.label}`,
+ field.attrs.label
+ ],
+ field.attrs.label
+ )
+ }
if (!field.parseInput) {
field.parseInput = (value) => value
}
diff --git a/src/app/Prototype/Contracts/Form/FormField.js b/src/app/Prototype/Contracts/Form/FormField.js
index a5c2cf3..bc8f32f 100644
--- a/src/app/Prototype/Contracts/Form/FormField.js
+++ b/src/app/Prototype/Contracts/Form/FormField.js
@@ -67,6 +67,20 @@ const FormField = {
getFieldLayout (component, attr) {
return this.getFieldLayouts(component)[attr]
},
+ /**
+ * @param {string} property
+ * @param {*} value
+ */
+ setRecord (property, value) {
+ this.record[property] = value
+ return this
+ },
+ /**
+ * @param {string} property
+ */
+ getRecord (property) {
+ return this.record[property]
+ },
/**
* @param {string} component
* @param {Boolean} error
diff --git a/src/app/Prototype/Prototype.js b/src/app/Prototype/Prototype.js
index 08fb2c3..2072c7b 100644
--- a/src/app/Prototype/Prototype.js
+++ b/src/app/Prototype/Prototype.js
@@ -1,7 +1,6 @@
/* eslint-disable no-underscore-dangle */
import Skeleton from './Skeleton'
import { lang } from 'src/app/Util/Lang'
-import { apply } from 'src/app/Util'
import Field from 'src/app/Prototype/Prototype/Field'
import FieldForm from 'src/app/Prototype/Prototype/FieldForm'
@@ -18,28 +17,6 @@ export default class Prototype extends Skeleton {
*/
static mixins = [Field, FieldForm, FieldIs, FieldTable, Action]
- /**
- * @type {boolean}
- */
- i18n = true
-
- /**
- * @param {Function} callback
- */
- locale (callback = undefined) {
- this.namespace = this.domain.replace(/\//, '.')
- const map = (piece) => piece.charAt(0).toUpperCase() + piece.substring(1)
- const domain = this.namespace.split('.').map(map).join('/')
- const locale = this.$i18n.locale
-
- import(/* webpackChunkName: "lang-[request]" */ `src/domains/${domain}/${locale}`)
- .then((messages) => {
- const translations = apply({}, `domains.${this.namespace}`, messages.default)
- this.$i18n.mergeLocaleMessage(locale, translations)
- })
- .finally(callback)
- }
-
/**
* @param {String|Array} key
* @param {string} [fallback]
@@ -81,7 +58,7 @@ export default class Prototype extends Skeleton {
*/
configureView () {
Object.keys(this.components).forEach(key => {
- this.setFieldAttrs(key, { readonly: true })
+ this.setFieldAttrs(key, { readonly: true, disable: true })
})
this.fetchRecord(this.$route.params[this.primaryKey])
}
@@ -150,7 +127,7 @@ export default class Prototype extends Skeleton {
if (this.debuggers) {
window.alert(JSON.stringify(response))
}
- this.$message.success(this.$lang(`prototype.operation.${scope}.success`))
+ this.$message.success(this.$lang(`prototype.operations.${scope}.success`))
if (scope === 'create') {
this.$browse(`${this.path}/${response[this.primaryKey]}/edit`, true)
}
@@ -167,66 +144,53 @@ export default class Prototype extends Skeleton {
const prototype = this
this.hook('created:default', function () {
- /**
- */
- const run = () => {
- // Call component setup method
- if (this.setup && typeof this.setup === 'function') {
- this.setup()
- }
-
- // Call global prototype configure
- prototype.configure.call(this)
+ // Call component setup method
+ if (this.setup && typeof this.setup === 'function') {
+ this.setup()
+ }
- // Call configure of each field
- this.configure()
+ // Call global prototype configure
+ prototype.configure.call(this)
- if (this.scope === 'index') {
- // Call configure to index scope
- return prototype.configureIndex.call(this)
- }
+ // Call configure of each field
+ this.configure()
- if (this.scope === 'update') {
- // Call configure to update scope
- return prototype.configureEdit.call(this)
- }
+ if (this.scope === 'index') {
+ // Call configure to index scope
+ return prototype.configureIndex.call(this)
+ }
- if (this.scope === 'read') {
- // Call configure to read scope
- return prototype.configureView.call(this)
- }
+ if (this.scope === 'update') {
+ // Call configure to update scope
+ return prototype.configureEdit.call(this)
+ }
- if (this.scope === 'create') {
- // Call configure to create scope
- return prototype.configureAdd.call(this)
- }
+ if (this.scope === 'read') {
+ // Call configure to read scope
+ return prototype.configureView.call(this)
}
- // load i18n async
- if (prototype.i18n) {
- prototype.locale.call(this, run)
- return
+
+ if (this.scope === 'create') {
+ // Call configure to create scope
+ return prototype.configureAdd.call(this)
}
- run()
})
this.action('add')
.actionScopes(['index'])
.actionPositions(['table-top'])
- .actionLabel(this.$lang('prototype.action.add.label'))
.actionIcon('add')
.actionColor('primary')
this.action('back')
.actionScopes(['index', 'create', 'read', 'update'])
.actionPositions(['form-footer'])
- .actionLabel(this.$lang('prototype.action.back.label'))
.actionIcon('reply')
this.action('cancel')
.actionFloatRight()
.actionScopes(['index', 'create', 'read', 'update'])
.actionPositions(['form-footer'])
- .actionLabel(this.$lang('prototype.action.cancel.label'))
.actionIcon('close')
this.action('refresh')
@@ -240,13 +204,12 @@ export default class Prototype extends Skeleton {
.actionScopes(['create', 'update'])
.actionPositions(['form-footer'])
.actionFloatRight()
- .actionLabel(this.$lang('prototype.action.save.label'))
.actionIcon('save')
.actionColor('primary')
.actionOn('click', function () {
this.$v.$touch()
if (this.$v.$error || this.hasErrors) {
- this.$message.error(this.$lang('prototype.action.save.validation'))
+ this.$message.error('prototype.actions.save.validation')
return
}
if (this.debuggers) {
@@ -258,20 +221,17 @@ export default class Prototype extends Skeleton {
this.action('view')
.actionScopes(['index'])
.actionPositions(['table-top', 'table-cell'])
- .actionLabel(this.$lang('prototype.action.view.label'))
.actionIcon('visibility')
this.action('edit')
.actionScopes(['index'])
.actionPositions(['table-top', 'table-cell'])
- .actionLabel(this.$lang('prototype.action.edit.label'))
.actionColor('primary')
.actionIcon('edit')
this.action('destroy')
.actionScopes(['index'])
.actionPositions(['table-top', 'table-cell'])
- .actionLabel(this.$lang('prototype.action.destroy.label'))
.actionColor('negative')
.actionIcon('delete')
}
diff --git a/src/app/Prototype/Prototype/FieldIs.js b/src/app/Prototype/Prototype/FieldIs.js
index 55292e4..a8da1c3 100644
--- a/src/app/Prototype/Prototype/FieldIs.js
+++ b/src/app/Prototype/Prototype/FieldIs.js
@@ -23,81 +23,96 @@ export default {
},
/**
- * @param {Boolean} upperCase
+ * @param {Object} attrs
* @returns {Prototype}
*/
- fieldIsInput (upperCase = true) {
+ fieldIsInput (attrs = {}) {
this.setComponent('input')
- return this.setAttrs({ upperCase })
- },
-
- /**
- * @returns {Prototype}
- */
- fieldIsNumber () {
- this.setComponent('input')
- this.setAttrs({ type: 'number' })
+ this.setAttrs({ ...attrs })
return this
},
/**
- * @param {Array} options
+ * @param {Object} attrs
* @returns {Prototype}
*/
- fieldIsSelect (options) {
- this.setComponent('select')
- this.setAttrs({ options })
- this.setLayout({
- tableFormat (value/* , row */) {
- return options.find((option) => option.value === value).label
- }
- })
+ fieldIsNumber (attrs = {}) {
+ this.setComponent('number')
+ this.setAttrs({ ...attrs })
return this
},
/**
- * @param {Number} rows
+ * @param {Object} attrs
* @returns {Prototype}
*/
- fieldIsText (rows = 3) {
- this.setComponent('text')
- return this.setAttrs({ rows })
+ fieldIsPassword (attrs = {}) {
+ this.setComponent('password')
+ this.setAttrs({ ...attrs })
+ return this
},
/**
+ * @param {Object} attrs
* @returns {Prototype}
*/
- fieldIsPassword () {
- return this.setComponent('password')
+ fieldIsEmail (attrs = {}) {
+ this.setComponent('email')
+ this.setAttrs({ ...attrs })
+ return this
},
/**
+ * @param {Number} rows
+ * @param {Object} attrs
* @returns {Prototype}
*/
- fieldIsRadio () {
- return this.setComponent('radio')
+ fieldIsText (rows = 4, attrs = {}) {
+ this.setComponent('text')
+ this.setAttrs({ ...attrs, rows })
+ return this
},
/**
+ * @param {Object} attrs
* @returns {Prototype}
*/
- fieldIsHtml () {
- return this.setComponent('html')
+ fieldIsCheckbox (attrs = {}) {
+ this.setComponent('checkbox')
+ this.setAttrs({ ...attrs })
+ return this
},
/**
+ * @param {Array} options
+ * @param {Object} attrs
* @returns {Prototype}
*/
- fieldIsFile () {
- return this.setComponent('file')
+ fieldIsRadio (options = undefined, attrs = {}) {
+ if (!Array.isArray(options)) {
+ options = [
+ { value: true, label: 'Yes' },
+ { value: false, label: 'No' }
+ ]
+ }
+ this.setComponent('radio')
+ this.setAttrs({ ...attrs, options })
+ return this
},
/**
+ * @param {Array} options
+ * @param {Object} attrs
* @returns {Prototype}
*/
- fieldIsEmail () {
- this.setComponent('input')
- this.setAttrs({ type: 'password' })
+ fieldIsSelect (options, attrs = {}) {
+ this.setComponent('select')
+ this.setAttrs({ ...attrs, options })
+ this.setLayout({
+ tableFormat (value/* , row */) {
+ return options.find((option) => option.value === value).label
+ }
+ })
return this
}
}
diff --git a/src/app/Prototype/Skeleton.js b/src/app/Prototype/Skeleton.js
index a3c1471..3e8d34a 100644
--- a/src/app/Prototype/Skeleton.js
+++ b/src/app/Prototype/Skeleton.js
@@ -13,6 +13,10 @@ export default class Skeleton extends Base {
*/
field (name, label = '', type = undefined) {
this.__currentField = name
+ if (this.__fields[name]) {
+ return this
+ }
+
let is = this.is
const attrs = { value: undefined, disable: false }
diff --git a/src/app/Util/Lang.js b/src/app/Util/Lang.js
index 4e03e49..0c9d35c 100644
--- a/src/app/Util/Lang.js
+++ b/src/app/Util/Lang.js
@@ -17,7 +17,7 @@ export const lang = (key, fallback = '') => {
continue
}
- let clean = key[path].replace(/\//g, '.')
+ let clean = String(key[path]).replace(/\//g, '.')
if (!window.app.i18n.te(clean)) {
continue
}
diff --git a/src/boot/message.js b/src/boot/message.js
index cbee91d..cfb90c4 100644
--- a/src/boot/message.js
+++ b/src/boot/message.js
@@ -33,7 +33,7 @@ const base = (options, action = {}) => {
* @param options
*/
export const toast = (message, options = {}) => {
- Notify.create(base({ message }))
+ Notify.create(base({ message, ...options }))
}
/**
@@ -41,7 +41,7 @@ export const toast = (message, options = {}) => {
* @param options
*/
export const success = (message, options = {}) => {
- Notify.create(base({ message, color: 'positive' }))
+ Notify.create(base({ message, ...options, color: 'positive' }))
}
/**
@@ -49,7 +49,7 @@ export const success = (message, options = {}) => {
* @param options
*/
export const error = (message, options = {}) => {
- Notify.create(base({ message, color: 'negative' }))
+ Notify.create(base({ message, ...options, color: 'negative' }))
}
/**
diff --git a/src/config/app/components.js b/src/config/app/components.js
index d167caf..2148a53 100644
--- a/src/config/app/components.js
+++ b/src/config/app/components.js
@@ -5,22 +5,46 @@ export default {
is: 'q-input',
attrs: { ...attrs }
},
- text: {
+ number: {
is: 'q-input',
attrs: {
- type: 'textarea',
- rows: 4,
+ type: 'number',
...attrs
}
},
password: {
is: 'q-input',
- attrs: { ...attrs }
+ attrs: {
+ type: 'number',
+ ...attrs
+ }
},
- image: {
+ email: {
is: 'q-input',
+ attrs: {
+ type: 'email',
+ ...attrs
+ }
+ },
+ text: {
+ is: 'q-input',
+ attrs: {
+ type: 'textarea',
+ rows: 4,
+ ...attrs
+ }
+ },
+ checkbox: {
+ is: 'q-checkbox',
attrs: { ...attrs }
},
+ radio: {
+ is: 'q-option-group',
+ attrs: {
+ inline: true,
+ ...attrs
+ }
+ },
select: {
is: 'q-select',
attrs: { ...attrs }
diff --git a/src/domains/Common/options.js b/src/domains/Common/options.js
new file mode 100644
index 0000000..c7af8bc
--- /dev/null
+++ b/src/domains/Common/options.js
@@ -0,0 +1,8 @@
+/**
+ * @param {string} domain
+ * @returns {Array}
+ */
+export const gender = (domain) => [
+ { value: 'male', label: `domains.${domain}.gender.male` },
+ { value: 'female', label: `domains.${domain}.gender.female` }
+]
diff --git a/src/domains/Example/Test/Prototype/TestWithHooks.js b/src/domains/Example/Test/Prototype/TestWithHooks.js
index 5e7979a..bc1dd72 100644
--- a/src/domains/Example/Test/Prototype/TestWithHooks.js
+++ b/src/domains/Example/Test/Prototype/TestWithHooks.js
@@ -1,5 +1,6 @@
import Test from './Test'
import { path } from 'src/domains/Example/Test/Routes'
+import { gender } from 'src/domains/Common/options'
/**
* @type {TestWithHooks}
@@ -21,14 +22,36 @@ export default class TestWithHooks extends Test {
construct () {
// construct the parent
super.construct()
-
+ // configure some fields
+ this.configureFields()
// configure some actions
this.configureActions()
-
// configure some hooks
this.configureHooks()
}
+ /**
+ */
+ configureFields () {
+ this.field('active')
+ .fieldIsCheckbox({ label: 'active.label' })
+ .fieldFormWidth(45)
+ .fieldFormOrder(3, true)
+ .fieldOn('input', function ({ $event }) {
+ this.setFieldLayout('description', 'formHidden', $event)
+ })
+
+ this.field('gender')
+ .fieldIsRadio(gender(TestWithHooks.domain))
+ .fieldFormOrder(4, true)
+ .fieldFormWidth(55)
+ .fieldOn('input', function ({ $event }) {
+ this.setFieldLayout('active', 'formHidden', $event === 'male')
+ })
+
+ this.field('description').fieldOn('input', this.changeDescriptionLabel)
+ }
+
/**
*/
configureActions () {
@@ -36,13 +59,13 @@ export default class TestWithHooks extends Test {
.actionColor('red')
.actionTextColor('white')
- this.action('go-to-test')
+ this.action('goToTest')
.actionScopes(['index', 'create', 'read', 'update'])
.actionPositions(['form-footer'])
.actionIcon('send')
.actionColor('yellow')
.actionOrder(2)
- .actionLabel(this.$lang(`domains.${TestWithHooks.domain}.actions.goToTest`))
+ .actionLabel(this.$lang(`domains.example.test.actions.goToTest`))
.actionOn('click', function ({ $event, context }) {
this.$log('~> $event', $event)
this.$log('~> context', context)
@@ -56,13 +79,32 @@ export default class TestWithHooks extends Test {
/**
*/
this.hook('fetch:record', function () {
- this.$message.toast(this.$lang(`domains.${this.domain}.messages.record`))
+ this.$message.toast(this.$lang(`domains.${this.domain}.messages.record`), { position: 'top-right' })
+ if (this.record.active === undefined) {
+ this.setRecord('active', false)
+ }
})
/**
*/
this.hook('fetch:records', function () {
- this.$message.toast(this.$lang(`domains.${this.domain}.messages.records`))
+ this.$message.toast(this.$lang(`domains.${this.domain}.messages.records`), { position: 'top-left' })
})
}
+
+ /**
+ * @param $event
+ * @param field
+ */
+ changeDescriptionLabel ({ $event, field }) {
+ if (!field.originalLabel) {
+ field.originalLabel = field.label
+ }
+ let label = $event
+ if (!label) {
+ label = field.originalLabel
+ field.originalLabel = undefined
+ }
+ field.label = label
+ }
}
diff --git a/src/domains/Example/Test/en-us.js b/src/domains/Example/Test/en-us.js
index fdccdf7..2e9d82e 100644
--- a/src/domains/Example/Test/en-us.js
+++ b/src/domains/Example/Test/en-us.js
@@ -11,8 +11,17 @@ export default {
id: 'Id',
name: 'Name',
age: 'Age',
+ active: 'Active',
+ gender: 'Gender',
description: 'Description'
},
+ gender: {
+ male: 'Male',
+ female: 'Female'
+ },
+ active: {
+ label: 'if checked will hide "Description"'
+ },
actions: {
goToTest: 'Go to Test'
},
diff --git a/src/i18n/en-us/prototype/index.js b/src/i18n/en-us/prototype/index.js
index 32b588d..7d6c8fe 100644
--- a/src/i18n/en-us/prototype/index.js
+++ b/src/i18n/en-us/prototype/index.js
@@ -16,15 +16,15 @@ export default {
},
components: {
},
- operation: {
+ operations: {
create: {
success: 'Record created successfully'
},
update: {
- success: 'Record update successfully'
+ success: 'Record updated successfully'
}
},
- action: {
+ actions: {
save: {
label: 'Save',
validation: 'Check the fields'