Files
mixtape/zero.Web.UI/App/editor/editor.vue
T

388 lines
8.3 KiB
Vue
Raw Normal View History

2020-04-30 14:21:43 +02:00
<template>
2020-07-08 11:19:23 +02:00
<div>
<div class="editor-outer" v-if="loaded && !rendererNotFound" :class="{ 'has-tabs': hasTabs, '-infos-aside': !nested, 'is-page': isPage }" :renderer="config">
2020-07-08 13:59:46 +02:00
<ui-tabs class="editor" :active="activeTab">
2020-09-17 11:26:23 +02:00
<ui-tab v-if="!tabDisabled(tab)" class="ui-box" :class="tab.class" :label="tab.label" :count="tab.count(value)" v-for="(tab, index) in tabs" :key="index" :depth="depth" :name="tab.name">
<editor-component v-for="(field, fieldIndex) in tab.fields" :key="fieldIndex" :config="field" :renderer="configuration" v-model="value" @input="onChange" :meta="meta" :depth="depth" :disabled="disabled" />
<component v-if="tab.component" :is="tab.component" v-model="value" :disabled="disabled" />
2020-07-08 11:19:23 +02:00
</ui-tab>
</ui-tabs>
<aside v-if="!nested && infos && infos != 'none'" class="editor-infos">
2020-08-24 13:22:42 +02:00
<slot name="info-boxes"></slot>
2020-07-08 11:19:23 +02:00
<div class="ui-box editor-active-toggle" v-if="isShared || activeToggle" :class="{'is-active': value.isActive }">
<slot name="settings">
<div v-if="isShared" class="editor-global-flag">
<b>This entity is shared</b> and can be used by all applications.<br>
<a href="/">More info</a>
<i class="fth-radio"></i>
</div>
<ui-property v-if="activeToggle" label="@ui.active" :is-text="true" class="is-toggle">
2020-09-17 11:26:23 +02:00
<ui-toggle v-model="value.isActive" class="is-primary" :disabled="disabled" />
2020-07-08 11:19:23 +02:00
</ui-property>
</slot>
2020-08-17 11:20:47 +02:00
<slot name="settings-properties"></slot>
2020-07-08 11:19:23 +02:00
</div>
2020-09-11 16:36:36 +02:00
<div class="ui-box is-light" v-if="value.id">
2020-07-08 11:19:23 +02:00
<slot name="infos">
<ui-property v-if="value.id" label="@ui.id" :is-text="true">
{{value.id}}
</ui-property>
2020-09-17 13:25:09 +02:00
<ui-property v-if="value.id && value.lastModifiedDate" label="@ui.modifiedDate" :is-text="true">
<ui-date v-model="value.lastModifiedDate" />
</ui-property>
2020-07-08 11:19:23 +02:00
<ui-property v-if="value.id" label="@ui.createdDate" :is-text="true">
<ui-date v-model="value.createdDate" />
</ui-property>
2020-08-24 13:22:42 +02:00
<slot name="infos-more"></slot>
2020-07-08 11:19:23 +02:00
</slot>
</div>
2020-08-24 13:22:42 +02:00
<slot name="infos-after"></slot>
2020-07-08 11:19:23 +02:00
</aside>
</div>
<div class="page page-error editor-error" v-if="loaded && rendererNotFound">
<i class="page-error-icon fth-cloud-snow"></i>
<p class="page-error-text">
<strong class="page-error-headline">Not found</strong><br>
The renderer <code>[{{config}}]</code> could not be found
</p>
</div>
2020-06-23 15:44:14 +02:00
</div>
2020-04-30 14:21:43 +02:00
</template>
<script>
import EditorComponent from 'zero/editor/editor-component';
2020-08-18 15:12:35 +02:00
import { each as _each, map as _map, filter as _filter, isArray as _isArray } from 'underscore';
2020-04-30 14:21:43 +02:00
export default {
name: 'uiEditor',
2020-05-01 14:34:44 +02:00
props: {
config: {
2020-05-19 13:13:40 +02:00
type: [ String, Object ],
2020-05-01 14:34:44 +02:00
required: true
2020-05-02 12:20:22 +02:00
},
2020-06-24 13:00:50 +02:00
meta: {
type: Object,
default: () => { }
},
2020-05-02 12:20:22 +02:00
value: {
type: Object
2020-06-23 15:44:14 +02:00
},
infos: {
type: String,
default: 'aside'
2020-06-24 14:03:54 +02:00
},
activeToggle: {
type: Boolean,
default: true
2020-06-29 18:20:43 +02:00
},
nested: {
type: Boolean,
default: false
2020-06-30 12:12:31 +02:00
},
2020-07-08 11:19:23 +02:00
isPage: {
type: Boolean,
default: false
},
2020-06-30 12:12:31 +02:00
depth: {
type: Number,
default: 0
2020-07-08 13:59:46 +02:00
},
onConfigure: {
type: Function,
default: () => { }
},
activeTab: {
type: Number,
default: 0
2020-09-17 11:26:23 +02:00
},
disabled: {
type: Boolean,
default: false
2020-05-01 14:34:44 +02:00
}
},
2020-04-30 14:21:43 +02:00
components: { EditorComponent },
data: () => ({
2020-07-08 11:19:23 +02:00
rendererNotFound: false,
2020-06-23 12:23:23 +02:00
configuration: {},
2020-04-30 14:21:43 +02:00
loaded: false,
hasTabs: false,
2020-06-23 12:23:23 +02:00
tabs: [],
fields: [],
2020-05-26 16:04:11 +02:00
renderInfo: true
2020-04-30 14:21:43 +02:00
}),
computed: {
2020-06-24 13:00:50 +02:00
isShared()
{
return this.meta.canBeShared && this.value && this.value.appId === zero.sharedAppId;
2020-04-30 14:21:43 +02:00
}
},
2020-04-30 15:35:09 +02:00
watch: {
2020-05-01 14:34:44 +02:00
config: {
deep: true,
handler: function()
{
this.load();
}
2020-04-30 15:35:09 +02:00
}
},
2020-04-30 14:21:43 +02:00
created()
{
2020-05-19 13:13:40 +02:00
this.load();
2020-05-01 14:34:44 +02:00
},
methods: {
load()
2020-04-30 14:21:43 +02:00
{
2020-05-19 13:13:40 +02:00
if (typeof this.config === 'string')
{
2020-06-23 12:23:23 +02:00
this.finishLoad(zero.renderers[this.config]);
2020-05-19 13:13:40 +02:00
}
else
{
this.finishLoad(this.config);
}
},
2020-06-23 12:23:23 +02:00
2020-05-19 13:13:40 +02:00
finishLoad(config)
{
2020-06-23 12:23:23 +02:00
this.configuration = config;
2020-07-08 11:19:23 +02:00
if (!config || !config.fields)
2020-05-01 14:34:44 +02:00
{
2020-07-08 11:19:23 +02:00
this.rendererNotFound = true;
this.loaded = true;
2020-05-01 14:34:44 +02:00
return;
}
2020-05-19 13:13:40 +02:00
2020-06-23 12:23:23 +02:00
this.fields = config.fields;
let tabs = this.configuration.tabs || [];
this.tabs = _map(tabs, (tab, index) =>
{
let tabConfig = tab;
tabConfig.count = typeof tabConfig.count === 'function' ? tabConfig.count : () => null;
tabConfig.fields = _filter(this.configuration.fields, x => index === 0 ? !x.tab || x.tab === tab.name : x.tab === tab.name);
return tabConfig;
});
2020-06-29 18:20:43 +02:00
this.hasTabs = this.tabs.length > 0 && !this.nested;
2020-06-24 16:01:24 +02:00
2020-06-29 14:23:05 +02:00
if (this.tabs.length < 1)
2020-06-23 12:23:23 +02:00
{
this.tabs.push({
2020-07-08 11:19:23 +02:00
label: '@ui.tab_general',
2020-06-24 15:28:34 +02:00
fields: this.configuration.fields,
count: () => null
2020-06-23 12:23:23 +02:00
});
2020-07-08 11:19:23 +02:00
if (this.isPage)
{
this.tabs[0].label = '@ui.tab_content';
this.hasTabs = true;
}
2020-06-23 12:23:23 +02:00
}
2020-07-08 13:59:46 +02:00
this.onConfigure(this);
2020-07-08 11:19:23 +02:00
this.rendererNotFound = false;
2020-04-30 14:21:43 +02:00
this.loaded = true;
2020-06-30 12:12:31 +02:00
},
onChange()
{
this.$emit('input', this.value);
2020-09-12 13:19:54 +02:00
},
2020-09-17 11:26:23 +02:00
tabDisabled(tab)
2020-09-12 13:19:54 +02:00
{
if (typeof tab.disabled !== 'function')
{
return false;
}
return tab.disabled(this.value);
2020-05-01 14:34:44 +02:00
}
2020-04-30 14:21:43 +02:00
}
}
2020-06-03 15:55:49 +02:00
</script>
<style lang="scss">
.editor > .ui-view-box
{
padding-top: 0;
}
2020-06-23 15:44:14 +02:00
.editor
{
.ui-tabs-list
{
padding-top: 0;
}
}
.editor-outer
{
2020-07-08 11:19:23 +02:00
&.-infos-aside:not(.is-page)
2020-06-23 15:44:14 +02:00
{
display: grid;
2020-06-24 13:00:50 +02:00
grid-template-columns: 1fr 340px var(--padding);
2020-06-24 01:25:29 +02:00
align-items: flex-start;
2020-06-23 15:44:14 +02:00
}
2020-06-24 15:28:34 +02:00
2020-06-24 16:01:24 +02:00
&:not(.has-tabs)
2020-06-24 15:28:34 +02:00
{
2020-06-24 16:01:24 +02:00
.ui-tabs-list
{
display: none;
}
2020-06-24 15:28:34 +02:00
2020-06-24 16:01:24 +02:00
.ui-tab, .editor-infos
{
margin-top: 0;
}
2020-06-24 15:28:34 +02:00
}
2020-06-23 15:44:14 +02:00
}
.editor-infos
{
2020-08-16 15:51:12 +02:00
margin: 50px 0 0;
2020-09-16 10:55:52 +02:00
position: sticky;
top: var(--padding);
2020-06-24 01:25:29 +02:00
.ui-box
{
2020-06-24 13:00:50 +02:00
margin-left: 0;
margin-right: 0;
margin-bottom: 0;
}
.ui-box:first-child
{
margin-top: 0;
2020-06-24 01:25:29 +02:00
}
.ui-box + .ui-box
2020-06-24 13:00:50 +02:00
{
margin-top: 16px;
}
.ui-box + .ui-box.is-light
2020-06-24 01:25:29 +02:00
{
margin-top: 1px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.ui-property + .ui-property
{
margin-top: 10px;
padding-top: 10px;
border-top: none;
}
.is-toggle
{
align-items: center;
}
2020-06-24 14:26:06 +02:00
.ui-property-label
{
width: auto;
padding-right: 20px;
}
.ui-property-content
{
flex: 0 1 auto;
}
.ui-property
{
display: flex;
justify-content: space-between;
}
2020-06-23 15:44:14 +02:00
}
2020-06-24 11:53:20 +02:00
.editor-active-toggle
{
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
/*.editor-active-toggle.is-active
{
background: var(--color-accent-info-bg);
.ui-property-label
{
color: var(--color-accent-info);
}
}*/
2020-06-24 13:00:50 +02:00
.editor-global-flag
{
font-size: var(--font-size);
line-height: 1.5;
padding-right: 70px;
position: relative;
i
{
position: absolute;
top: 50%;
2020-06-24 14:03:54 +02:00
margin-top: -28px;
2020-06-24 13:00:50 +02:00
right: -3px;
font-size: 42px;
color: var(--color-text-dim);
2020-06-24 13:00:50 +02:00
opacity: 0.2;
}
a
{
color: var(--color-text-dim);
2020-06-24 13:00:50 +02:00
text-decoration: underline dotted;
font-size: var(--font-size-s);
}
2020-06-24 14:03:54 +02:00
& + .ui-property
{
margin-top: 32px;
padding-top: 32px;
2020-08-16 15:51:12 +02:00
border-top: 1px solid var(--color-line);
2020-06-24 14:03:54 +02:00
}
&.is-block
{
padding: 20px;
padding-right: 70px;
border-radius: var(--radius);
background: var(--color-box-nested);
margin-bottom: var(--padding);
i
{
margin-top: -22px;
right: 20px;
}
}
2020-06-24 13:00:50 +02:00
}
2020-07-08 11:19:23 +02:00
.editor-error
{
background: transparent;
min-height: calc(100vh - 100px);
}
2020-06-03 15:55:49 +02:00
</style>