diff --git a/zero.Web.UI/App/components/tabs/tabs.vue b/zero.Web.UI/App/components/tabs/tabs.vue index 0c08b508..0ac20a4c 100644 --- a/zero.Web.UI/App/components/tabs/tabs.vue +++ b/zero.Web.UI/App/components/tabs/tabs.vue @@ -34,14 +34,11 @@ tabs: [] }), - created() + mounted() { this.cacheKey = this.cache ? `zero.ui-tabs.cache.${this.cache}` : null; this.tabs = this.$children; - }, - mounted() - { if (this.cache) { const cachedActiveTab = localStorage.getItem(this.cacheKey); diff --git a/zero.Web.UI/App/core/editor-field.js b/zero.Web.UI/App/core/editor-field.js index c9dd0bb7..51b3ad4d 100644 --- a/zero.Web.UI/App/core/editor-field.js +++ b/zero.Web.UI/App/core/editor-field.js @@ -6,21 +6,21 @@ import Rte from '../editor/fields/rte.vue'; import Select from '../editor/fields/select.vue'; import Textarea from '../editor/fields/textarea.vue'; import Toggle from '../editor/fields/toggle.vue'; -import Alias from '../editor/fields/toggle.vue'; -import Output from '../editor/fields/toggle.vue'; -import Checklist from '../editor/fields/toggle.vue'; -import ColorPicker from '../editor/fields/toggle.vue'; -import CountryPicker from '../editor/fields/toggle.vue'; -import CulturePicker from '../editor/fields/toggle.vue'; -import DateRangePicker from '../editor/fields/toggle.vue'; -import IconPicker from '../editor/fields/toggle.vue'; -import LanguagePicker from '../editor/fields/toggle.vue'; -import InputList from '../editor/fields/toggle.vue'; -import Media from '../editor/fields/toggle.vue'; -import Modules from '../editor/fields/toggle.vue'; -import Nested from '../editor/fields/toggle.vue'; -import State from '../editor/fields/toggle.vue'; -import Tags from '../editor/fields/toggle.vue'; +import Alias from '../editor/fields/alias.vue'; +import Output from '../editor/fields/output.vue'; +import Checklist from '../editor/fields/checklist.vue'; +import ColorPicker from '../editor/fields/colorpicker.vue'; +import CountryPicker from '../editor/fields/countrypicker.vue'; +import CulturePicker from '../editor/fields/culturepicker.vue'; +import DateRangePicker from '../editor/fields/daterangepicker.vue'; +import IconPicker from '../editor/fields/iconPicker.vue'; +import LanguagePicker from '../editor/fields/language.vue'; +import InputList from '../editor/fields/inputlist.vue'; +import Media from '../editor/fields/media.vue'; +import Modules from '../editor/fields/modules.vue'; +import Nested from '../editor/fields/nested.vue'; +import State from '../editor/fields/state.vue'; +import Tags from '../editor/fields/tags.vue'; class EditorField @@ -28,6 +28,7 @@ class EditorField path = null; options = { label: null, + hideLabel: false, description: null, helpText: null, condition: null, @@ -48,6 +49,22 @@ class EditorField } + get component() + { + return this.#component; + } + + get componentOptions() + { + return this.#componentOptions; + } + + get isRequired() + { + return this.#required; + } + + /** * */ @@ -87,7 +104,7 @@ class EditorField * @param {object} [options] - Custom options * @returns {EditorField} */ - component(component, options) + custom(component, options) { return this._setComponent(component, { ...options }); } @@ -281,7 +298,7 @@ class EditorField * @param {string} [addLabel] - Label for the add button * @returns {EditorField} */ - inputList(limit, maxItemLength) + inputList(limit, maxItemLength, addLabel) { return this._setComponent(InputList, { limit, maxItemLength, addLabel }); } diff --git a/zero.Web.UI/App/core/editor.js b/zero.Web.UI/App/core/editor.js index 5ee126a2..d82761da 100644 --- a/zero.Web.UI/App/core/editor.js +++ b/zero.Web.UI/App/core/editor.js @@ -4,8 +4,15 @@ import EditorField from './editor-field.js'; class Editor { #alias; - #templateLabel = field => field; - #templateDescription = field => field; + + /** + * Overrides the string generation for the label + */ + templateLabel = field => field; + /** + * Overrides the string generation for the description + */ + templateDescription = field => field; tabs = []; fields = []; @@ -22,34 +29,6 @@ class Editor return this.#alias; } - /** - * Overrides the string generation for the label - * @param {function} callback - Called when a label is constructed - */ - set onLabelCreate(callback) - { - if (typeof callback !== 'function') - { - console.warn(`[zero] onLabelCreate excepts a function as the first parameter`); - return; - } - this.#templateLabel = callback; - } - - /** - * Overrides the string generation for the label description - * @param {function} callback - Called when a description is constructed - */ - set onDescriptionCreate(callback) - { - if (typeof callback !== 'function') - { - console.warn(`[zero] onDescriptionCreate excepts a function as the first parameter`); - return; - } - this.#templateDescription = callback; - } - /** * A tab within an editor @@ -100,6 +79,7 @@ class Editor * @param {string} path - Model path * @param {object} [options] - Custom options * @param {string} [options.label] - A custom label for this field (otherwise it's generated via `onLabelCreate`) + * @param {string} [options.hideLabel] - Hide the field label and make the content full-width * @param {string} [options.description] - A custom description for this field (otherwise it's generated via `onDescriptionCreate`) * @param {string} [options.helpText] - Display a help text below the field * @param {boolean|function} [options.condition] - Conditionally hide the field @@ -111,10 +91,23 @@ class Editor field(path, options) { const field = new EditorField(path, options); + this.fields.push(field); return field; } + /** + * Get fields for the specified tab + * @param {string|EditorTab} [tab] - Pass the tab or its alias + * @returns {EditorField[]} + */ + getFields(tab) + { + const alias = typeof tab === 'undefined' ? null : (typeof tab === 'string' ? tab : tab.alias); + return this.fields.filter(x => !alias ? true : x.options.tab === alias); + } + + /** * Create a new tab instance * @returns {EditorTab} diff --git a/zero.Web.UI/App/core/plugin.js b/zero.Web.UI/App/core/plugin.js index 82a4c168..cecca522 100644 --- a/zero.Web.UI/App/core/plugin.js +++ b/zero.Web.UI/App/core/plugin.js @@ -5,7 +5,7 @@ class Plugin #onInstall; routes = []; - renderers = []; + editors = []; constructor(name) @@ -33,20 +33,19 @@ class Plugin /* * Add a new vue form renderer to the global configuration */ - addRenderer(alias, config) + addEditor(config) { - config.alias = alias; - this.renderers.push(config); + this.editors.push(config); } /* * Adds new form renderers to the global configuration * Can either by an array (where a key of the child object is `alias`) or an object where the key is the renderer alias */ - addRenderers(renderers) + addEditors(editors) { - let items = !Array.isArray(renderers) ? Object.values(renderers) : renderers; - items.forEach(item => this.addRenderer(item.alias, item)); + let items = !Array.isArray(editors) ? Object.values(editors) : editors; + items.forEach(item => this.addEditor(item)); } /* diff --git a/zero.Web.UI/App/core/plugin.zero.js b/zero.Web.UI/App/core/plugin.zero.js index 04793aab..561fcc60 100644 --- a/zero.Web.UI/App/core/plugin.zero.js +++ b/zero.Web.UI/App/core/plugin.zero.js @@ -1,12 +1,12 @@  import Plugin from './plugin.js'; -import renderers from '../renderers/all.js'; +import editors from '../editors/all.js'; import routes from './routes.js'; const plugin = new Plugin('zero'); // add renderers -plugin.addRenderers(renderers); +plugin.addEditors(editors); // add routes plugin.addRoutes(routes); diff --git a/zero.Web.UI/App/core/zero.js b/zero.Web.UI/App/core/zero.js index 222b614e..73ff3f26 100644 --- a/zero.Web.UI/App/core/zero.js +++ b/zero.Web.UI/App/core/zero.js @@ -19,7 +19,7 @@ class Zero #vue = null; #plugins = []; - #renderers = []; + #editors = []; #routes = []; #router = null; @@ -95,8 +95,8 @@ class Zero // append routes plugin.routes.forEach(x => this.#routes.push(x)); - // append renderers - plugin.renderers.forEach(x => this.#renderers.push(x)); + // append editors + plugin.editors.forEach(x => this.#editors.push(x)); console.log(`[zero] Installed %c${plugin.name}%cplugin`, 'font-style:italic;'); } @@ -105,9 +105,9 @@ class Zero /* * Returns a renderer */ - getRenderer(alias) + getEditor(alias) { - const renderer = this.#renderers.find(x => x.alias === alias); + const renderer = this.#editors.find(x => x.alias === alias); if (!renderer) { diff --git a/zero.Web.UI/App/directives/localize.js b/zero.Web.UI/App/directives/localize.js index d1c85c36..cd1fa6c5 100644 --- a/zero.Web.UI/App/directives/localize.js +++ b/zero.Web.UI/App/directives/localize.js @@ -8,11 +8,12 @@ Vue.directive('localize', (el, binding) => { if (binding.value !== binding.oldValue || !el.innerText) { + const hasValue = !!binding.value; const isObject = typeof binding.value === 'object'; - let key = isObject ? binding.value.key : binding.value; - let options = isObject ? binding.value : null; + let key = hasValue ? (isObject ? binding.value.key : binding.value) : null; + let options = hasValue && isObject ? binding.value : null; - const result = Localization.localize(key, options); + const result = hasValue ? Localization.localize(key, options) : ''; // set content as html if (binding.arg === 'html') diff --git a/zero.Web.UI/App/editor/editor-component-old.vue b/zero.Web.UI/App/editor/editor-component-old.vue new file mode 100644 index 00000000..9e3c94e7 --- /dev/null +++ b/zero.Web.UI/App/editor/editor-component-old.vue @@ -0,0 +1,251 @@ + + + + \ No newline at end of file diff --git a/zero.Web.UI/App/editor/editor-component.vue b/zero.Web.UI/App/editor/editor-component.vue index 9e3c94e7..115c5cba 100644 --- a/zero.Web.UI/App/editor/editor-component.vue +++ b/zero.Web.UI/App/editor/editor-component.vue @@ -1,7 +1,8 @@  @@ -9,46 +10,31 @@ + + \ No newline at end of file diff --git a/zero.Web.UI/App/editor/editor.vue b/zero.Web.UI/App/editor/editor.vue index 08a8c7e7..df141709 100644 --- a/zero.Web.UI/App/editor/editor.vue +++ b/zero.Web.UI/App/editor/editor.vue @@ -1,71 +1,35 @@  \ No newline at end of file diff --git a/zero.Web.UI/App/editor/fields/text.vue b/zero.Web.UI/App/editor/fields/text.vue index 9e186990..0e5e4e43 100644 --- a/zero.Web.UI/App/editor/fields/text.vue +++ b/zero.Web.UI/App/editor/fields/text.vue @@ -1,7 +1,5 @@  @@ -12,17 +10,13 @@ type: String, default: null }, - config: Object, - disabled: { - type: Boolean, - default: false - } - }, - - computed: { - maxLength() - { - return this.config.maxLength > 0 ? this.config.maxLength : null; + maxLength: { + type: Number, + default: null + }, + placeholder: { + type: String, + default: null } } } diff --git a/zero.Web.UI/App/editor/fields/toggle.vue b/zero.Web.UI/App/editor/fields/toggle.vue index d9322a05..e01d4796 100644 --- a/zero.Web.UI/App/editor/fields/toggle.vue +++ b/zero.Web.UI/App/editor/fields/toggle.vue @@ -1,5 +1,5 @@  @@ -14,7 +14,10 @@ type: Boolean, default: false }, - config: Object + negative: { + type: Boolean, + default: false + } } } \ No newline at end of file diff --git a/zero.Web.UI/App/editors/all.js b/zero.Web.UI/App/editors/all.js new file mode 100644 index 00000000..a46add92 --- /dev/null +++ b/zero.Web.UI/App/editors/all.js @@ -0,0 +1,18 @@ + +import application from './application.js'; +//import country from './country.js'; +//import language from './language.js'; +//import media from './media.js'; +//import translation from './translation.js'; +//import user from './user.js'; +//import userRole from './userRole.js'; + +export default { + application, + //country, + //language, + //media, + //translation, + //user, + //userRole +}; \ No newline at end of file diff --git a/zero.Web.UI/App/editors/application.js b/zero.Web.UI/App/editors/application.js new file mode 100644 index 00000000..cfbbfce9 --- /dev/null +++ b/zero.Web.UI/App/editors/application.js @@ -0,0 +1,23 @@ + +import Editor from '../core/editor.js'; +import ApplicationFeatures from '../pages/settings/application-features.vue'; + +const editor = new Editor('application'); +const prefix = '@application.fields.'; + +editor.templateLabel = x => prefix + x; +editor.templateDescription = x => prefix + x + '_text'; + +const general = editor.tab('general', '@ui.tab_general'); +const domains = editor.tab('domains', '@application.tab_domains', x => x.domains.length); +const features = editor.tab('features', '@application.tab_features', x => x.features.length); + +general.field('name', { label: '@ui.name' }).text(50).required(); +general.field('fullName').text(120); +general.field('email').text(120).required(); +general.field('imageId').image(); +general.field('iconId').image(); +domains.field('domains', { helpText: prefix + 'domains_help' }).inputList(10, null, prefix + 'domains_add').required(); +features.field('features').custom(ApplicationFeatures); + +export default editor; \ No newline at end of file diff --git a/zero.Web.UI/App/renderers/country.js b/zero.Web.UI/App/editors/country.js similarity index 100% rename from zero.Web.UI/App/renderers/country.js rename to zero.Web.UI/App/editors/country.js diff --git a/zero.Web.UI/App/renderers/language.js b/zero.Web.UI/App/editors/language.js similarity index 100% rename from zero.Web.UI/App/renderers/language.js rename to zero.Web.UI/App/editors/language.js diff --git a/zero.Web.UI/App/renderers/media.js b/zero.Web.UI/App/editors/media.js similarity index 100% rename from zero.Web.UI/App/renderers/media.js rename to zero.Web.UI/App/editors/media.js diff --git a/zero.Web.UI/App/renderers/translation.js b/zero.Web.UI/App/editors/translation.js similarity index 100% rename from zero.Web.UI/App/renderers/translation.js rename to zero.Web.UI/App/editors/translation.js diff --git a/zero.Web.UI/App/renderers/user.js b/zero.Web.UI/App/editors/user.js similarity index 100% rename from zero.Web.UI/App/renderers/user.js rename to zero.Web.UI/App/editors/user.js diff --git a/zero.Web.UI/App/renderers/userRole.js b/zero.Web.UI/App/editors/userRole.js similarity index 100% rename from zero.Web.UI/App/renderers/userRole.js rename to zero.Web.UI/App/editors/userRole.js diff --git a/zero.Web.UI/App/pages/settings/application.vue b/zero.Web.UI/App/pages/settings/application.vue index 7f945064..9f83fe9d 100644 --- a/zero.Web.UI/App/pages/settings/application.vue +++ b/zero.Web.UI/App/pages/settings/application.vue @@ -1,7 +1,7 @@  @@ -26,7 +26,8 @@ onLoad(form) { - form.load(!this.id ? ApplicationsApi.getEmpty() : ApplicationsApi.getById(this.id)).then(response => + const id = this.$route.params.id; + form.load(!id ? ApplicationsApi.getEmpty() : ApplicationsApi.getById(id)).then(response => { this.disabled = !response.meta.canEdit; this.meta = response.meta; diff --git a/zero.Web.UI/App/renderers/all.js b/zero.Web.UI/App/renderers/all.js deleted file mode 100644 index c364b0ae..00000000 --- a/zero.Web.UI/App/renderers/all.js +++ /dev/null @@ -1,18 +0,0 @@ - -import application from './application.js'; -import country from './country.js'; -import language from './language.js'; -import media from './media.js'; -import translation from './translation.js'; -import user from './user.js'; -import userRole from './userRole.js'; - -export default { - application, - country, - language, - media, - translation, - user, - userRole -}; \ No newline at end of file diff --git a/zero.Web.UI/App/renderers/application-new.js b/zero.Web.UI/App/renderers/application-new.js deleted file mode 100644 index 1bdb386f..00000000 --- a/zero.Web.UI/App/renderers/application-new.js +++ /dev/null @@ -1,23 +0,0 @@ - -import Editor from '../core/editor.js'; -import ApplicationFeatures from '../pages/settings/application-features.vue'; - -const editor = new Editor('application'); -const prefix = '@application.fields.'; - -editor.onLabelCreate = x => prefix + x; -editor.onDescriptionCreate = x => prefix + x + '_text'; - -editor.tab('general', '@ui.tab_general'); -const domains = editor.tab('domains', '@application.tab_domains', x => x.domains.length); -const features = editor.tab('features', '@application.tab_features', x => x.features.length); - -editor.field('name', { label: '@ui.name' }).text(50); -editor.field('fullName').text(120); -editor.field('email').text(120); -editor.field('imageId').image(); -editor.field('iconId').image(); -domains.field('domains', { helpText: prefix + 'domains_help' }).inputList(10, null, prefix + 'domains_add'); -features.field('features').component(ApplicationFeatures); - -export default editor; \ No newline at end of file diff --git a/zero.Web.UI/App/renderers/application.js b/zero.Web.UI/App/renderers/application.js deleted file mode 100644 index f4a61661..00000000 --- a/zero.Web.UI/App/renderers/application.js +++ /dev/null @@ -1,77 +0,0 @@ -export default { - alias: 'application', - - tabs: [ - { - name: 'general', - label: '@ui.tab_general' - }, - { - name: 'domains', - label: '@application.tab_domains', - count: x => x.domains.length - }, - { - name: 'features', - label: '@application.tab_features', - count: x => x.features.length - } - ], - - labelTemplate(field) - { - return "@application.fields." + field; - }, - - descriptionTemplate(field) - { - return "@application.fields." + field + "_text"; - }, - - fields: [ - { - field: 'name', - display: 'text', - label: '@ui.name', - required: true, - maxLength: 50 - }, - { - field: 'fullName', - display: 'text', - maxLength: 120 - }, - { - field: 'email', - display: 'text', - required: true, - maxLength: 120 - }, - { - field: 'imageId', - display: 'media', - type: 'image', - class: 'my-image' - }, - { - field: 'iconId', - display: 'media', - type: 'image' - }, - { - tab: 'domains', - field: 'domains', - display: 'inputList', - required: true, - limit: 10, - addLabel: '@application.fields.domains_add', - helpText: '@application.fields.domains_help' - }, - { - tab: 'features', - field: 'features', - display: 'custom', - path: '@zero/pages/settings/application-features.vue' - } - ] -}; \ No newline at end of file