first draft of ui-editor
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<section v-show="active" class="ui-tab" :aria-hidden="!active" role="tabpanel">
|
||||
<section v-show="active && !hidden" class="ui-tab" :aria-hidden="!active" role="tabpanel">
|
||||
<slot />
|
||||
</section>
|
||||
</template>
|
||||
@@ -22,6 +22,10 @@
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hidden: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -232,7 +232,6 @@
|
||||
|
||||
getLink(item)
|
||||
{
|
||||
return '/'; // TODO
|
||||
if (!this.listConfig.link)
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { createRouter, RouteRecordRaw, RouterOptions } from 'vue-router';
|
||||
import registerDirectives from '../directives/register';
|
||||
import registerComponents from '../components/register';
|
||||
import registerFormComponents from '../forms/register';
|
||||
import registerEditorComponents from '../editor/register';
|
||||
import { getRouterConfig, appendRouterGuards } from './router/routerConfig';
|
||||
import { countryPlugin, applicationPlugin, settingsPlugin } from '../modules';
|
||||
import { ZeroSchema } from 'zero/schemas';
|
||||
@@ -52,6 +53,7 @@ export class ZeroRuntime implements Zero
|
||||
registerDirectives(app);
|
||||
registerComponents(app);
|
||||
registerFormComponents(app);
|
||||
registerEditorComponents(app);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
:can-unlock="canUnlock || false"
|
||||
@unlock="unlock"
|
||||
@lock="lock">
|
||||
<component :is="config.component" v-bind="config.componentOptions" :model-value="model" :entity="modelValue" :meta="meta" @input="onChange" :disabled="isDisabled" />
|
||||
<component :is="config.component" v-bind="config.componentOptions" :value="model" :entity="value" :meta="meta" @input="onChange" :disabled="isDisabled" />
|
||||
<p v-if="config.options.helpText" class="ui-property-help" v-localize="config.options.helpText"></p>
|
||||
</ui-property>
|
||||
</template>
|
||||
@@ -38,7 +38,7 @@
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
modelValue: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
@@ -49,7 +49,7 @@
|
||||
},
|
||||
|
||||
watch: {
|
||||
modelValue: {
|
||||
value: {
|
||||
deep: true,
|
||||
handler: function ()
|
||||
{
|
||||
@@ -74,15 +74,15 @@
|
||||
computed: {
|
||||
isHidden()
|
||||
{
|
||||
return this.loaded && typeof this.config.options.condition === 'function' && !this.config.options.condition(this.modelValue, this);
|
||||
return this.loaded && typeof this.config.options.condition === 'function' && !this.config.options.condition(this.value, this);
|
||||
},
|
||||
isRequired()
|
||||
{
|
||||
return typeof this.config.isRequired === 'function' ? this.config.isRequired(this.modelValue) : this.config.isRequired;
|
||||
return typeof this.config.isRequired === 'function' ? this.config.isRequired(this.value) : this.config.isRequired;
|
||||
},
|
||||
isDisabled()
|
||||
{
|
||||
return this.manualDisabled || this.disabled || (typeof this.config.options.disabled === 'boolean' && this.config.options.disabled) || (typeof this.config.options.disabled === 'function' && this.config.options.disabled(this.modelValue, this.model));
|
||||
return this.manualDisabled || this.disabled || (typeof this.config.options.disabled === 'boolean' && this.config.options.disabled) || (typeof this.config.options.disabled === 'function' && this.config.options.disabled(this.value, this.model));
|
||||
},
|
||||
label()
|
||||
{
|
||||
@@ -94,11 +94,11 @@
|
||||
},
|
||||
isLocked()
|
||||
{
|
||||
return !this.editor.blueprint.unlocked(this.modelValue, this.config);
|
||||
return this.editor.blueprint && !this.editor.blueprint.unlocked(this.value, this.config);
|
||||
},
|
||||
canUnlock()
|
||||
{
|
||||
return this.editor.blueprint.canUnlock(this.modelValue, this.config);
|
||||
return this.editor.blueprint && this.editor.blueprint.canUnlock(this.value, this.config);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
rebuildModel()
|
||||
{
|
||||
this.selector = selectorToArray(this.config.path);
|
||||
let currentValue = this.modelValue;
|
||||
let currentValue = this.value;
|
||||
let found = false;
|
||||
|
||||
if (!this.selector || !this.selector.length || !currentValue)
|
||||
@@ -132,6 +132,8 @@
|
||||
|
||||
this.model = found ? currentValue : null;
|
||||
}
|
||||
|
||||
//console.info(this.config.path + ' => ' + this.model);
|
||||
},
|
||||
|
||||
|
||||
@@ -141,19 +143,19 @@
|
||||
|
||||
if (typeof value === 'function')
|
||||
{
|
||||
value(this.modelValue);
|
||||
value(this.value);
|
||||
}
|
||||
else
|
||||
{
|
||||
setObjectValue(this.modelValue, this.selector, value);
|
||||
setObjectValue(this.value, this.selector, value);
|
||||
}
|
||||
this.$emit('input', this.modelValue);
|
||||
this.$emit('input', this.value);
|
||||
|
||||
if (typeof this.config.options.onChange === 'function')
|
||||
{
|
||||
this.config.options.onChange(value, {
|
||||
oldValue,
|
||||
model: this.modelValue,
|
||||
model: this.value,
|
||||
component: this
|
||||
});
|
||||
}
|
||||
@@ -181,7 +183,7 @@
|
||||
|
||||
async lock()
|
||||
{
|
||||
let originalValue = await this.editor.blueprint.lock(this.modelValue, this.config);
|
||||
let originalValue = await this.editor.blueprint.lock(this.value, this.config);
|
||||
this.onChange(originalValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,17 @@
|
||||
</header>
|
||||
<div class="editor" :class="['display-' + display, { 'has-sidebar': asideDefined, 'hide-tabs': tabs.length < 2, 'has-below': belowDefined }]">
|
||||
<ui-tabs class="editor-tabs">
|
||||
<ui-tab v-if="!tab.disabled(modelValue)" v-for="(tab, index) in tabs" class="ui-box" :class="tab.class" :label="tab.name" :count="tab.count(modelValue)" :key="index">
|
||||
<ui-tab v-for="(tab, index) in tabs" class="ui-box" :class="tab.class" :label="tab.name" :count="tab.count(value)" :hidden="tab.disabled(value)" :key="index">
|
||||
<h3 v-if="display == 'boxes' && tab.name" class="ui-headline editor-tab-headline" v-localize="tab.name"></h3>
|
||||
<slot name="blueprint">
|
||||
<blueprint-property v-if="modelValue && editorConfig.blueprint" :model-value="modelValue" :meta="meta" :config="editorConfig.blueprint" />
|
||||
<!--<blueprint-property v-if="value && editorConfig.blueprint" :model-value="value" :meta="meta" :config="editorConfig.blueprint" />-->
|
||||
</slot>
|
||||
<div class="ui-property ui-property-parent" v-for="fieldset in tab.fieldsets">
|
||||
<editor-component v-for="(field, fieldIndex) in fieldset.fields" :disabled="disabled" :key="fieldIndex" :config="field" @input="onChange" :editor="editorConfig" :model-value="modelValue"
|
||||
<editor-component v-for="(field, fieldIndex) in fieldset.fields" :disabled="disabled" :key="fieldIndex" :config="field" @input="onChange" :editor="editorConfig" :value="value"
|
||||
:class="field.options.class" :data-cols="!!field.options.fieldset" :style="{ 'grid-column': field.options.fieldset ? 'span ' + field.options.fieldsetColumns : null }" />
|
||||
|
||||
</div>
|
||||
<component v-if="tab.component" :is="tab.component" v-model="modelValue" />
|
||||
<component v-if="tab.component" :is="tab.component" v-model="value" />
|
||||
</ui-tab>
|
||||
</ui-tabs>
|
||||
<aside class="editor-aside" v-if="asideDefined">
|
||||
@@ -35,8 +35,9 @@
|
||||
import EditorAside from './editor-aside.vue';
|
||||
import { createBlueprintConfig } from './editor-blueprint';
|
||||
import BlueprintProperty from './blueprint/property.vue';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
name: 'uiEditor',
|
||||
|
||||
provide: function ()
|
||||
@@ -60,7 +61,7 @@
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
modelValue: {
|
||||
value: {
|
||||
type: Object
|
||||
},
|
||||
onConfigure: {
|
||||
@@ -93,10 +94,10 @@
|
||||
}
|
||||
},
|
||||
|
||||
created()
|
||||
async created()
|
||||
{
|
||||
this.editorConfig = typeof this.config === 'string' ? this.zero.getEditor(this.config) : this.config;
|
||||
this.editorConfig.blueprint = createBlueprintConfig(this.zero, this.editorConfig, this.modelValue);
|
||||
this.editorConfig = typeof this.config === 'string' ? await this.zero.getSchema(this.config) : this.config;
|
||||
this.editorConfig.blueprint = null; //createBlueprintConfig(this.zero, this.editorConfig, this.value);
|
||||
|
||||
this.tabs = this.editorConfig.tabs.map(tab =>
|
||||
{
|
||||
@@ -121,8 +122,8 @@
|
||||
|
||||
onChange()
|
||||
{
|
||||
this.$emit('input', this.modelValue);
|
||||
this.$emit('input', this.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<ui-toggle :model-value="modelValue" @input="$emit('input', $event)" v-bind="{ disabled, negative, onContent, offContent }" />
|
||||
<ui-toggle :on="value" @update:on="$emit('input', $event)" v-bind="{ disabled, negative, onContent, offContent }" />
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: Boolean,
|
||||
value: Boolean,
|
||||
disabled: Boolean,
|
||||
negative: Boolean,
|
||||
onContent: String,
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import uiEditor from './editor.vue';
|
||||
|
||||
export {
|
||||
uiEditor
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
import { App } from 'vue';
|
||||
import * as components from './index';
|
||||
|
||||
export default function (app: App)
|
||||
{
|
||||
for (var key in components)
|
||||
{
|
||||
let component = components[key];
|
||||
app.component(key, component.default || component);
|
||||
}
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="ui-toggle" :class="{'is-disabled': disabled, 'is-negative': negative, 'is-active': modelValue, 'is-content-left': contentLeft }">
|
||||
<input type="checkbox" :value="modelValue" @input="onChange" :disabled="disabled" />
|
||||
<span class="ui-toggle-switch" :class="{ 'is-active': modelValue }"><i></i></span>
|
||||
<i class="fth-minus-circle ui-toggle-off-warning" v-if="offContent && !modelValue && offWarning"></i>
|
||||
<span class="ui-toggle-text" v-if="onContent && modelValue" v-localize="onContent"></span>
|
||||
<span class="ui-toggle-text" v-if="offContent && !modelValue" v-localize="offContent"></span>
|
||||
<div class="ui-toggle" :class="{'is-disabled': disabled, 'is-negative': negative, 'is-active': on, 'is-content-left': contentLeft }">
|
||||
<input type="checkbox" :value="on" @input="onChange" :disabled="disabled" />
|
||||
<span class="ui-toggle-switch" :class="{ 'is-active': on }"><i></i></span>
|
||||
<i class="fth-minus-circle ui-toggle-off-warning" v-if="offContent && !on && offWarning"></i>
|
||||
<span class="ui-toggle-text" v-if="onContent && on" v-localize="onContent"></span>
|
||||
<span class="ui-toggle-text" v-if="offContent && !on" v-localize="offContent"></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
name: 'uiToggle',
|
||||
|
||||
props: {
|
||||
modelValue: {
|
||||
on: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
onChange(ev)
|
||||
{
|
||||
this.$emit('update:modelValue', !this.modelValue);
|
||||
this.$emit('update:on', !this.on);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
<template>
|
||||
<ui-header-bar class="ui-form-header" :back-button="true">
|
||||
<template v-slot:title>
|
||||
<h2 class="ui-header-bar-title" :class="{'is-empty': title && !modelValue.name && !titleDisabled}">
|
||||
<h2 class="ui-header-bar-title" :class="{'is-empty': title && !value.name && !titleDisabled}">
|
||||
<template v-for="prefix in prefixes">
|
||||
<span class="-minor -prefix" v-localize:html="prefix"></span>
|
||||
<ui-icon class="-chevron" symbol="fth-chevron-right" :size="14" />
|
||||
</template>
|
||||
<ui-error field="name" />
|
||||
<input v-if="!titleDisabled" class="ui-form-header-title-input" type="text" v-model="modelValue.name" v-localize:placeholder="title" :readonly="titleDisabled || disabled" />
|
||||
<!--<ui-alias class="ui-form-header-title-alias" v-if="hasAlias" v-model="modelValue.alias" :name="modelValue.name" :disabled="disabled" />-->
|
||||
<span v-if="titleDisabled" v-localize="forceTitle ? title : (modelValue.name || title)"></span>
|
||||
<input v-if="!titleDisabled" class="ui-form-header-title-input" type="text" v-model="value.name" v-localize:placeholder="title" :readonly="titleDisabled || disabled" />
|
||||
<!--<ui-alias class="ui-form-header-title-alias" v-if="hasAlias" v-model="value.alias" :name="value.name" :disabled="disabled" />-->
|
||||
<span v-if="titleDisabled" v-localize="forceTitle ? title : (value.name || title)"></span>
|
||||
</h2>
|
||||
</template>
|
||||
<div class="ui-form-header-aside">
|
||||
<slot></slot>
|
||||
<div v-if="!activeDisabled && typeof modelValue.isActive !== 'undefined'" class="ui-form-header-toggle">
|
||||
<ui-toggle v-model="modelValue.isActive" class="is-accent" off-content="@ui.inactive" :off-warning="true" on-content="@ui.active" :content-left="true" :disabled="disabled" />
|
||||
<div v-if="!activeDisabled && typeof value.isActive !== 'undefined'" class="ui-form-header-toggle">
|
||||
<ui-toggle v-model:on="value.isActive" class="is-accent" off-content="@ui.inactive" :off-warning="true" on-content="@ui.active" :content-left="true" :disabled="disabled" />
|
||||
</div>
|
||||
<slot name="buttons"></slot>
|
||||
<ui-dropdown v-if="actionsDefined" align="right">
|
||||
@@ -47,8 +47,9 @@
|
||||
prefix: {
|
||||
type: [String, Array]
|
||||
},
|
||||
modelValue: {
|
||||
type: Object
|
||||
value: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
canDelete: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -104,52 +104,25 @@
|
||||
},
|
||||
|
||||
// loads data on creation of the form
|
||||
load(promise)
|
||||
async load(promise)
|
||||
{
|
||||
this.loadingState = 'loading';
|
||||
const response = await promise();
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
if (!response.success)
|
||||
{
|
||||
promise
|
||||
.then(
|
||||
response =>
|
||||
{
|
||||
if (response.meta && typeof response.meta.canEdit === 'boolean')
|
||||
{
|
||||
this.canEdit = response.meta.canEdit;
|
||||
this.loadingState = 'error';
|
||||
this.loadingError = response.errors[0].message;
|
||||
return null;
|
||||
}
|
||||
|
||||
//if (response.entity)
|
||||
//{
|
||||
// if (!response.entity.id && this.$route.params.scope === zero.sharedAppId)
|
||||
// {
|
||||
// response.entity.appId = zero.sharedAppId;
|
||||
// }
|
||||
|
||||
// this.isShared = response.entity.appId === zero.sharedAppId;
|
||||
//} // TODO appx
|
||||
}
|
||||
|
||||
resolve(response);
|
||||
|
||||
this.loadingState = 'default';
|
||||
this.$nextTick(() =>
|
||||
{
|
||||
this.$emit('loaded', this);
|
||||
});
|
||||
},
|
||||
(error) =>
|
||||
{
|
||||
this.loadingState = 'error';
|
||||
this.loadingError = error;
|
||||
reject(error);
|
||||
}
|
||||
)
|
||||
.catch(exception =>
|
||||
{
|
||||
this.loadingState = 'error';
|
||||
this.loadingError = exception;
|
||||
});
|
||||
this.canEdit = true;
|
||||
this.loadingState = 'default';
|
||||
this.$nextTick(() =>
|
||||
{
|
||||
this.$emit('loaded', this);
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// handles a promise as result of the form submission
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="root">
|
||||
countries óÒ<br />
|
||||
country óÒ<br />
|
||||
<ui-search v-model="search" @submit="doSearch" />
|
||||
<div v-for="item in items" :key="item.id">
|
||||
{{item.name}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ui-form ref="form" class="country" v-slot="form" @submit="onSubmit" @load="onLoad" :route="route">
|
||||
<ui-form-header v-model="model" prefix="@country.list" title="@country.name" :disabled="disabled" :is-create="!$route.params.id" :state="form.state" :can-delete="meta.canDelete" @delete="onDelete" />
|
||||
<ui-form-header v-model:value="model" prefix="@country.list" title="@country.name" :disabled="disabled" :is-create="!$route.params.id" :state="form.state" :can-delete="meta.canDelete" @delete="onDelete" />
|
||||
<ui-editor config="country" v-model="model" :meta="meta" :disabled="disabled">
|
||||
<template v-slot:below>
|
||||
<ui-editor-infos v-model="model" :disabled="disabled" />
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
|
||||
<script>
|
||||
import { countriesApi } from 'zerox';
|
||||
import api from './api';
|
||||
|
||||
export default {
|
||||
props: ['id'],
|
||||
@@ -23,33 +23,29 @@
|
||||
data: () => ({
|
||||
meta: {},
|
||||
model: { name: null, features: [], domains: [] },
|
||||
route: __zero.alias.settings.countries + '-edit',
|
||||
route: 'countries-edit',
|
||||
disabled: false
|
||||
}),
|
||||
|
||||
methods: {
|
||||
|
||||
onLoad(form)
|
||||
async onLoad(form)
|
||||
{
|
||||
form.load(!this.$route.params.id ? countriesApi.getEmpty() : countriesApi.getById(this.id, { scope: 'hallo' })).then(response =>
|
||||
{
|
||||
this.disabled = !response.meta.canEdit;
|
||||
this.meta = response.meta;
|
||||
this.model = response.entity;
|
||||
});
|
||||
const response = await form.load(() => api.getById(this.id));
|
||||
this.model = response;
|
||||
},
|
||||
|
||||
|
||||
onSubmit(form)
|
||||
{
|
||||
form.handle(countriesApi.save(this.model));
|
||||
form.handle(api.save(this.model));
|
||||
},
|
||||
|
||||
|
||||
onDelete(item, opts)
|
||||
{
|
||||
opts.hide();
|
||||
this.$refs.form.onDelete(countriesApi.delete.bind(this, this.id));
|
||||
this.$refs.form.onDelete(api.delete.bind(this, this.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ export default {
|
||||
install(app: ZeroPluginOptions)
|
||||
{
|
||||
app.vue.component('ui-countrypicker', defineAsyncComponent(() => import('./ui-countrypicker.vue')));
|
||||
app.route({ path: '/settings/countries', component: () => import('./countries.vue') });
|
||||
app.route({ name: 'countries', path: '/settings/countries', component: () => import('./countries.vue') });
|
||||
app.route({ name: 'countries-edit', path: '/settings/countries/edit/:id', component: () => import('./country.vue'), props: true });
|
||||
|
||||
app.schema('countries', () => import('./list'));
|
||||
app.schema('country', () => import('./editor'));
|
||||
|
||||
@@ -22,11 +22,12 @@
|
||||
"pinia": "^2.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"sass": "^1.44.0",
|
||||
"vite": "^2.6.14",
|
||||
"@vue/compiler-sfc": "^3.2.24",
|
||||
"@vitejs/plugin-vue": "^1.10.1",
|
||||
"@vue/compiler-sfc": "^3.2.24",
|
||||
"sass": "^1.44.0",
|
||||
"typescript": "^4.5.2",
|
||||
"vite": "^2.6.14",
|
||||
"vite-plugin-rewrite-all": "^0.1.2",
|
||||
"vue-tsc": "^0.29.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import pluginRewriteAll from 'vite-plugin-rewrite-all'
|
||||
|
||||
let loadedPlugins = JSON.parse(process.env.ZERO_PLUGINS || "[]");
|
||||
|
||||
@@ -72,15 +73,19 @@ let config = defineConfig({
|
||||
vue: '@vue/compat'
|
||||
}
|
||||
},
|
||||
plugins: [vue({
|
||||
template: {
|
||||
compilerOptions: {
|
||||
compatConfig: {
|
||||
MODE: 2
|
||||
plugins: [
|
||||
pluginRewriteAll(),
|
||||
vue({
|
||||
template: {
|
||||
compilerOptions: {
|
||||
compatConfig: {
|
||||
MODE: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}), ...zeroPlugins],
|
||||
}),
|
||||
...zeroPlugins
|
||||
],
|
||||
build: {
|
||||
manifest: true,
|
||||
outDir: 'dist/zero',
|
||||
|
||||
Reference in New Issue
Block a user