navigation in backoffice works again

This commit is contained in:
2020-10-14 13:18:29 +02:00
parent db5b0d4066
commit efdad0102c
88 changed files with 592 additions and 403 deletions
+8 -8
View File
@@ -1,23 +1,23 @@
<template>
<div class="app" :class="getClassList()">
<template v-if="isAuthenticated">
<!--<app-navigation />-->
<app-navigation />
<div class="app-main">
<router-view></router-view>
</div>
<!--<app-overlays />
<app-notifications />-->
<app-overlays />
<!--<app-notifications />-->
</template>
<!--<app-login v-else />-->
<app-login v-else />
</div>
</template>
<script>
import '../Sass/app.scss'
//import AppNavigation from 'zero/navigation.vue'
//import AppLogin from 'zero/pages/login/login.vue'
//import AppOverlays from 'zero/components/overlays/overlay-holder.vue'
import AppNavigation from 'zero/navigation.vue'
import AppLogin from 'zero/pages/login/login.vue'
import AppOverlays from 'zero/components/overlays/overlay-holder.vue'
//import AppNotifications from 'zero/components/notifications/notification-holder.vue'
import AuthApi from 'zero/services/auth.js';
import EventHub from 'zero/services/eventhub';
@@ -28,7 +28,7 @@
export default {
name: 'app',
//components: { AppNavigation, AppOverlays, AppLogin, AppNotifications },
components: { AppNavigation, AppOverlays, AppLogin },
data: () => ({
isAuthenticated: false
@@ -53,6 +53,8 @@
}
},
emits: ['click'],
computed: {
application()
{
+2 -13
View File
@@ -22,6 +22,8 @@
export default {
name: 'uiButton',
emits: ['click'],
props: {
label: {
type: String
@@ -130,17 +132,6 @@
{
this.stateTimeout = setTimeout(() =>
{
//const stateUpdate = 'update:state';
//// check if :state property has .sync modifier
//let listeners = this.$options._parentListeners;
//const hasSyncModifier = listeners && listeners[stateUpdate];
//if (!hasSyncModifier)
//{
// console.warn(`ui-button: Add the .sync modifier to the "state" property, as changing the state to "${STATE_SUCCESS}" or "${STATE_ERROR}" will automatically set it back to "${STATE_DEFAULT}" after the state duration timeout`);
//}
this.stateDisplay = STATE_DEFAULT;
}, this.stateDuration);
}
@@ -148,7 +139,6 @@
},
methods: {
tryClick(ev)
{
if (this.isDefaultState)
@@ -156,7 +146,6 @@
this.$emit('click', ev);
}
}
}
}
</script>
@@ -1,5 +1,5 @@
<template>
<button type="button" class="ui-dot-button" :disabled="disabled" @click="tryClick" :title="title | localize">
<button type="button" class="ui-dot-button" :disabled="disabled" @click="tryClick" v-localize:title="title">
<span class="sr-only" v-localize="title"></span>
<i class="ui-button-icon fth-more-horizontal"></i>
</button>
@@ -10,6 +10,8 @@
export default {
name: 'uiDotButton',
emits: ['click'],
props: {
state: {
type: String,
@@ -1,5 +1,5 @@
<template>
<button type="button" class="ui-icon-button" :disabled="disabled" :class="'type-' + type.split(' ').join(' type-')" @click="tryClick" :title="title | localize">
<button type="button" class="ui-icon-button" :disabled="disabled" :class="'type-' + type.split(' ').join(' type-')" @click="tryClick" v-localize:title="title">
<span class="sr-only" v-localize="title"></span>
<i class="ui-button-icon" :class="icon"></i>
</button>
@@ -10,6 +10,8 @@
export default {
name: 'uiIconButton',
emits: ['click'],
props: {
state: {
type: String,
@@ -21,6 +21,8 @@
export default {
name: 'uiSelectButton',
emits: ['click'],
props: {
icon: {
type: String,
@@ -26,6 +26,8 @@
}
},
emits: ['input'],
methods: {
select(value)
@@ -71,6 +71,8 @@
}
},
emits: ['count', 'select'],
components: { UiPagination },
watch: {
@@ -116,7 +118,7 @@
computed: {
actionsDefined()
{
return this.$scopedSlots.hasOwnProperty('actions');
return typeof this.$slots.actions === 'function';
}
},
@@ -15,6 +15,8 @@
export default {
name: 'uiAlias',
emits: ['input'],
props: {
value: {
type: String,
@@ -15,6 +15,8 @@
export default {
name: 'uiCheckList',
emits: ['input'],
props: {
value: {
type: Array,
@@ -1,4 +1,5 @@
<template>
<!-- // TODO notfound view works and can be used here -->
<div class="page page-error" :class="{'theme-dark': dark }">
<i class="page-error-icon fth-cloud-snow"></i>
<p class="page-error-text">
@@ -29,6 +29,8 @@
export default {
name: 'uiFormHeader',
emits: ['delete'],
props: {
title: {
type: String,
@@ -69,7 +71,7 @@
computed: {
actionsDefined()
{
return !this.isCreate && (this.canDelete || this.$scopedSlots.hasOwnProperty('actions'));
return !this.isCreate && (this.canDelete || typeof this.$slots.actions === 'function');
}
},
@@ -32,6 +32,8 @@
}
},
emits: ['load', 'submit'],
data: () => ({
dirty: false,
loadingState: 'default',
@@ -16,6 +16,8 @@
export default {
name: 'uiInputList',
emits: ['input'],
props: {
addLabel: {
type: String,
@@ -66,6 +66,8 @@
export default {
name: 'uiMediaOld',
emits: ['input'],
props: {
disabled: {
type: Boolean,
+2
View File
@@ -19,6 +19,8 @@
export default {
name: 'uiRte',
emits: ['input'],
props: {
value: {
type: String,
@@ -23,6 +23,8 @@
}
},
emits: ['input', 'change', 'submit'],
computed: {
},
@@ -9,6 +9,8 @@
export default {
name: 'uiTags',
emits: ['input'],
props: {
addLabel: {
type: String,
@@ -13,6 +13,8 @@
export default {
name: 'uiToggle',
emits: ['input'],
props: {
value: {
type: Boolean,
@@ -33,6 +33,8 @@
export default {
name: 'uiModulePreview',
emits: ['edit', 'remove', 'isActive'],
props: {
value: {
type: Object,
@@ -36,6 +36,8 @@
export default {
name: 'uiModulesSelect',
emits: ['selected'],
props: {
value: Array,
config: Object,
@@ -17,6 +17,8 @@
export default {
name: 'uiModules',
emits: ['input'],
props: {
value: {
type: Array,
@@ -12,8 +12,6 @@
<script>
import Overlay from 'zero/services/overlay.js'
export default {
props: {
@@ -1,18 +1,15 @@
<template>
<!-- // TODO remove surrounding div as soon as we can have multiple roots (vue 3) -->
<div @click="onClickShim">
<button v-if="!confirming" :disabled="disabled" type="button" @click="onClick" class="ui-dropdown-button" :class="{ 'has-icon': icon, 'is-active': selected, 'is-multiline': multiline }">
<i v-if="icon" class="ui-dropdown-button-icon" :class="icon"></i>
{{label | localize}}
<i v-if="selected" class="ui-dropdown-button-selected fth-check"></i>
<i v-if="loading" class="ui-dropdown-button-progress"></i>
</button>
<div v-if="confirming" class="ui-dropdown-button-confirmation">
<i v-if="icon" class="ui-dropdown-button-icon" :class="icon"></i>
{{label | localize}}
<ui-button type="small primary" label="OK" @click="onClick($event, true)" />
<ui-button type="small light" label="Cancel" @click="confirming=false" />
</div>
<button v-if="!confirming" :disabled="disabled" type="button" @click="onClick" class="ui-dropdown-button" :class="{ 'has-icon': icon, 'is-active': selected, 'is-multiline': multiline }">
<i v-if="icon" class="ui-dropdown-button-icon" :class="icon"></i>
<span v-localize="label"></span>
<i v-if="selected" class="ui-dropdown-button-selected fth-check"></i>
<i v-if="loading" class="ui-dropdown-button-progress"></i>
</button>
<div v-if="confirming" class="ui-dropdown-button-confirmation">
<i v-if="icon" class="ui-dropdown-button-icon" :class="icon"></i>
<span v-localize="label"></span>
<ui-button type="small primary" label="OK" @click="onClick($event, true)" />
<ui-button type="small light" label="Cancel" @click="confirming=false" />
</div>
</template>
@@ -23,6 +20,8 @@
export default {
name: 'uiDropdownButton',
emits: ['click', 'hide'],
props: {
value: {
default: null
@@ -126,14 +125,7 @@
}
});
}
},
onClickShim(e)
{
e.preventDefault();
e.stopPropagation();
}
}
}
</script>
@@ -16,6 +16,8 @@
export default {
name: 'uiDropdown',
emits: ['opened', 'closed'],
props: {
align: {
type: String,
@@ -39,7 +41,7 @@
hasButton()
{
return this.$scopedSlots.hasOwnProperty('button');
return typeof this.$slots.button === 'function';
},
dropdownClasses()
@@ -13,12 +13,12 @@
<script>
import Overlay from 'zero/services/overlay.js'
import EventHub from 'zero/services/eventhub';
import { filter as _filter } from 'underscore';
export default {
data: () => ({
instances: Overlay.instances
instances: []
}),
computed: {
@@ -28,6 +28,19 @@
}
},
mounted()
{
EventHub.on('overlay:open', instances =>
{
console.info('open');
this.instances = instances;
});
EventHub.on('overlay:close', instances =>
{
this.instances = instances;
});
},
methods: {
close(instance)
{
@@ -19,6 +19,8 @@
export default {
name: 'uiPagination',
emits: ['update:page', 'change'],
props: {
pages: {
type: Number,
@@ -24,6 +24,8 @@
export default {
name: 'uiPermissions',
emits: ['input'],
props: {
value: {
type: Array,
@@ -16,6 +16,8 @@
export default {
name: 'uiColorpicker',
emits: ['change', 'input'],
props: {
value: {
type: String,
@@ -12,6 +12,8 @@
export default {
name: 'uiCountrypicker',
emits: ['input'],
props: {
value: {
type: [String, Array],
@@ -23,6 +23,8 @@
export default {
name: 'uiDatepicker',
emits: ['change', 'input'],
components: { DatepickerOverlay },
props: {
@@ -17,6 +17,8 @@
}
},
emits: ['change'],
data: () => ({
date: null,
initialized: false
@@ -32,6 +32,8 @@
export default {
name: 'uiDaterangepicker',
emits: ['change', 'input'],
components: { DaterangepickerOverlay },
props: {
@@ -16,6 +16,8 @@
export default {
name: 'uiIconpicker',
emits: ['change', 'input'],
props: {
value: {
type: String,
@@ -67,6 +67,8 @@
export default {
name: 'uiMediapicker',
emits: ['change', 'input'],
props: {
value: {
type: [Array, String],
@@ -150,6 +150,8 @@
export default {
name: 'uiPick',
emits: ['change', 'input', 'select', 'deselect'],
props: {
value: {
type: [String, Array],
@@ -12,6 +12,8 @@
export default {
name: 'uiUserpicker',
emits: ['input'],
props: {
value: {
type: [String, Array],
@@ -2,8 +2,7 @@
// will be used very frequently. Components are registered using the
// PascalCased version of their file name.
import Vue from 'vue';
let components = [];
//const directories = ['buttons', 'forms', 'messages', 'tables', 'tabs'];
// TODO this file will import all vue component files with subdirectories
@@ -42,6 +41,21 @@ requireComponent.keys().forEach((path) =>
// Concatenated
.join('');
// Globally register the component
Vue.component(componentName, componentConfig.default || componentConfig);
});
if (componentName === 'uiOverlay')
{
return;
}
components.push({
name: componentName,
config: componentConfig.default || componentConfig
})
});
export default function (app)
{
components.forEach(component =>
{
app.component(component.name, component.config);
});
};
@@ -1,6 +1,6 @@
<template>
<div class="ui-table-filter">
<ui-search v-if="!hideSearch" v-model="value.search" class="onbg" />
<ui-search v-if="!hideSearch" v-model="config.search" class="onbg" />
<ui-dropdown v-if="!hideFilter && filters.length" align="right">
<template v-slot:button>
<ui-button v-if="!hideFilter" type="light onbg" :label="filterLabel" caret="down" />
@@ -18,11 +18,11 @@
</template>
<slot name="actions"></slot>
</ui-dropdown>
<ui-dropdown v-if="value.actions && value.actions.length > 0" align="right">
<ui-dropdown v-if="config.actions && config.actions.length > 0" align="right">
<template v-slot:button>
<ui-button type="light onbg" icon="fth-more-horizontal" />
</template>
<ui-dropdown-button v-for="(action, index) in value.actions" :key="index" :value="action" :prevent="action.autoclose === false" :label="action.label" :icon="action.icon" @click="onActionClicked" />
<ui-dropdown-button v-for="(action, index) in config.actions" :key="index" :value="action" :prevent="action.autoclose === false" :label="action.label" :icon="action.icon" @click="onActionClicked" />
</ui-dropdown>
</div>
</template>
@@ -37,8 +37,10 @@
export default {
name: 'uiTableFilter',
emits: ['filter'],
props: {
value: {
config: {
type: Object,
required: true,
default: () =>
@@ -62,7 +64,7 @@
},
watch: {
value: function (value)
config: function (value)
{
this.reload();
}
@@ -100,9 +102,9 @@
reload()
{
this.hideFilter = typeof this.value.filter !== 'object';
this.hideSearch = typeof this.value.search === false;
this.hideSelection = this.value.selectable !== true;
this.hideFilter = typeof this.config.filter !== 'object';
this.hideSearch = typeof this.config.search === false;
this.hideSelection = this.config.selectable !== true;
this.filters = JSON.parse(localStorage.getItem(this.storageKey) || "[]");
},
@@ -125,7 +127,7 @@
{
let model = {
name: null,
filter: this.value.filter.model
filter: this.config.filter.model
};
if (id)
@@ -137,9 +139,9 @@
component: FilterOverlay,
display: 'editor',
title: 'Filter',
defaults: this.value.filter.model,
defaults: this.config.filter.model,
model: model,
fields: this.value.filter.fields,
fields: this.config.filter.fields,
canSave: !!this.alias,
isCreate: !id
}).then(value =>
+8 -6
View File
@@ -3,7 +3,7 @@
<div class="ui-table" :class="{'is-inline': inline }">
<header class="ui-table-row ui-table-head">
<div v-for="column in columns" :key="column.key" class="ui-table-cell" :table-field="column.field" :style="column.flex">
{{ column.label | localize }}
<span v-localize="column.label"></span>
<button :disabled="!column.canSort" @click="sort(column)" type="button" class="ui-table-sort" :class="tableFilter.orderBy == column.field ? 'sort-' + (tableFilter.orderIsDescending ? 'desc' : 'asc') : null">
<i class="arrow arrow-down"></i>
</button>
@@ -70,8 +70,10 @@
export default {
name: 'uiTable',
emits: ['loaded', 'count', 'select'],
props: {
value: {
config: {
type: Object,
required: true,
default: () => { }
@@ -96,15 +98,15 @@
components: { UiPagination },
watch: {
'value.columns': function (val)
'config.columns': function (val)
{
this.generateColumns(val);
},
'value.search': function (val)
'config.search': function (val)
{
this.tableFilter.search = val;
},
'value.items': function (val)
'config.items': function (val)
{
this.initialize();
},
@@ -183,7 +185,7 @@
{
this.debouncedUpdate = _debounce(this.update, 300);
this.configuration = _extend(JSON.parse(JSON.stringify(defaultConfig)), this.value);
this.configuration = _extend(JSON.parse(JSON.stringify(defaultConfig)), this.config);
this.tableFilter.pageSize = this.configuration.pageSize;
+1 -1
View File
@@ -4,7 +4,7 @@
<button type="button" v-for="(tab, index) in tabs" class="ui-tabs-list-item" :key="index" :class="{ 'is-active': tab.active, 'has-errors': tab.hasErrors }" :disabled="tab.disabled"
:aria-selected="tab.active" role="tab" @click="select(index)">
<i v-if="tab.hasErrors" class="ui-tabs-list-item-error fth-alert-circle"></i>
{{ tab.label | localize }}
<span v-localize="tab.label"></span>
<i v-if="tab.countOutput > 0" class="ui-tabs-list-item-count">{{tab.countOutput}}</i>
</button>
</div>
@@ -6,7 +6,7 @@
<component :disabled="value.disabled" :is="tag" type="button" :to="value.url" class="ui-tree-item-link" @click="onClick(value, $event)">
<i class="ui-tree-item-icon" :class="value.icon"></i>
<i v-if="value.modifier" :title="value.modifier.name" class="ui-tree-item-modifier" :class="value.modifier.icon"></i>
<span class="ui-tree-item-text">{{value.name | localize}}</span>
<span class="ui-tree-item-text" v-localize="value.name"></span>
</component>
<ui-dot-button :disabled="value.disabled" class="ui-tree-item-actions" v-if="value.hasActions" @click="onActionsClicked(value, $event)" />
</div>
@@ -19,6 +19,8 @@
export default {
name: 'uiTreeItem',
emits: ['click', 'open', 'rightclick', 'actions'],
props: {
value: {
type: Object,
+3 -1
View File
@@ -27,6 +27,8 @@
export default {
name: 'uiTree',
emits: ['select', 'onStatusChange'],
props: {
depth: {
type: Number,
@@ -65,7 +67,7 @@
computed: {
actionsDefined()
{
return this.$scopedSlots.hasOwnProperty('actions');
return typeof this.$slots.actions === 'function';
}
},
+56
View File
@@ -0,0 +1,56 @@
class Plugin
{
#name;
#onMounted;
routes = [];
renderers = {};
constructor(name)
{
this.#name = name;
}
get name()
{
return this.#name;
}
set mounted(callback)
{
this.#onMounted = callback;
}
/*
* Add a new vue form renderer to the global configuration
*/
addRenderer(alias, renderer)
{
this.renderers[alias] = renderer;
}
/*
* Add a new route to vue-router
*/
addRoute(route)
{
this.routes.push(route);
}
/*
* Add a new route to vue-router
*/
addRoutes(routes)
{
routes.forEach(route =>
{
this.routes.push(route);
});
}
};
export default Plugin;
-8
View File
@@ -1,12 +1,4 @@
//import Date from './date';
//import Localize from './localize';
//import Resizable from './resizable';
//import Clickoutside from './clickoutside';
//import Filesize from './filesize';
//import Currency from './currency';
//import Sortable from './sortable';
export default function (app)
{
const requireComponent = require.context('.', true, /[\w-]+\.js/);
@@ -16,6 +16,8 @@
export default {
name: 'uiEditorComponent',
emits: ['input'],
props: {
value: {
type: [ Object, Array ]
+2
View File
@@ -63,6 +63,8 @@
export default {
name: 'uiEditor',
emits: ['input'],
props: {
config: {
type: [ String, Object ],
+2
View File
@@ -5,6 +5,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: String,
+2
View File
@@ -7,6 +7,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: Number,
@@ -4,6 +4,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: Array,
@@ -5,6 +5,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: String
@@ -5,6 +5,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: [String, Array]
@@ -11,6 +11,8 @@
import LanguagesApi from 'zero/resources/languages';
export default {
emits: ['input'],
props: {
value: {
type: String
@@ -14,6 +14,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: Object,
@@ -5,6 +5,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: String
@@ -4,6 +4,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: Array,
@@ -12,6 +12,8 @@
import LanguagesApi from 'zero/resources/languages';
export default {
emits: ['input'],
props: {
value: {
type: String
+2
View File
@@ -5,6 +5,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: [String, Array],
@@ -5,6 +5,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: [String, Object, Array]
+2
View File
@@ -19,6 +19,8 @@
components: { UiEditor },
emits: ['input'],
props: {
value: {
type: [Array, Object]
+2
View File
@@ -5,6 +5,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: Number,
+2
View File
@@ -5,6 +5,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: String,
+2
View File
@@ -13,6 +13,8 @@
export default {
name: 'uiSelect',
emits: ['input'],
props: {
value: {
type: [String, Number, Object],
+2
View File
@@ -5,6 +5,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: [ String, Number ]
+2
View File
@@ -4,6 +4,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: Array,
+2
View File
@@ -7,6 +7,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: String,
@@ -5,6 +5,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: String
+2
View File
@@ -5,6 +5,8 @@
<script>
export default {
emits: ['input'],
props: {
value: {
type: Boolean,
-2
View File
@@ -1,2 +0,0 @@
import './localize';
-10
View File
@@ -1,10 +0,0 @@
import Vue from 'vue';
import Localization from 'zero/services/localization';
/// <summary>
/// Localizes the given property and sets the inner-text of the node to its result
/// </summary>
Vue.filter('localize', (value, options) =>
{
return Localization.localize(value, options);
});
+2 -4
View File
@@ -19,14 +19,12 @@
<template v-for="section in sections">
<router-link :to="section.url" class="app-nav-item" :alias="section.alias" :class="{ 'has-children': hasChildren(section) }">
<i class="app-nav-item-icon" :class="section.icon"></i>
{{section.name | localize}}
<span v-localize="section.name"></span>
<i v-if="hasChildren(section)" class="app-nav-item-arrow fth-chevron-down"></i>
</router-link>
<transition name="app-nav-children">
<div class="app-nav-children" v-if="hasChildren(section) && $route.path.indexOf('/' + section.alias) === 0">
<router-link v-for="child in section.children" v-bind:key="child.alias" :to="child.url" class="app-nav-child">
{{child.name | localize}}
</router-link>
<router-link v-for="child in section.children" v-bind:key="child.alias" :to="child.url" class="app-nav-child" v-localize="child.name" />
</div>
</transition>
</template>
+1 -1
View File
@@ -52,7 +52,7 @@
onSubmit(form)
{
this.rejectReason = null;
console.info(form);
form.handle(AuthApi.login(this.model)).then(res =>
{
window.location.reload();
+2
View File
@@ -44,6 +44,8 @@
disabled: Boolean
},
emits: ['input'],
data: () => ({
icons: {
image: 'fth-image',
+24 -15
View File
@@ -8,19 +8,23 @@
<code>{{path}}</code>
</p>
<ui-button class="page-error-button" type="light onbg" :label="detailsText" @click="details = !details" />
<template v-if="details">
<div v-if="details">
<br><br>
<div class="page-error-routes">
<span>#</span>
<span>Name</span>
<span>Path</span>
<template v-for="(route, index) in routes">
<span>{{index + 1}}.</span>
<b>{{route.name}}</b>
<span>{{route.path}}</span>
</template>
<header class="page-error-route is-head">
<span>#</span>
<b>Name</b>
<span>Path</span>
</header>
<div>
<div class="page-error-route" v-for="(route, index) in routes">
<span>{{index + 1}}.</span>
<b>{{route.name}}</b>
<span>{{route.path}}</span>
</div>
</div>
</div>
</template>
</div>
</div>
</template>
@@ -57,10 +61,10 @@
rebuild()
{
this.path = this.$router.options.base + this.$route.path.substring(1);
this.path = this.$router.options.history.base + this.$route.path;
this.routes = [];
this.$router.options.routes.forEach(route =>
this.$router.getRoutes().forEach(route =>
{
this.routes.push({
path: route.path,
@@ -128,9 +132,8 @@
}
.page-error-routes
{
display: grid;
grid-template-columns: auto auto 1fr;
{
display: block;
width: 100%;
max-width: 100%;
text-align: left;
@@ -153,4 +156,10 @@
}
}
}
.page-error-route
{
display: grid;
grid-template-columns: 1fr 3fr 6fr;
}
</style>
@@ -8,7 +8,7 @@
<button type="button" v-for="item in pageTypes" class="page-create-item" @click="onSelect(item)">
<i class="page-create-item-icon" :class="item.icon"></i>
<span class="page-create-item-text">
{{item.name | localize}}
<span v-localize="item.name"></span>
<span v-if="item.description" v-localize="item.description"></span>
</span>
</button>
@@ -23,6 +23,7 @@
},
},
emits: ['input'],
data: () => ({
features: []
@@ -1,6 +1,6 @@
<template>
<div class="apps-items">
<router-link v-for="app in value" :key="app.id" :to="getAppLink(app)" class="apps-item">
<router-link v-for="app in items" :key="app.id" :to="getAppLink(app)" class="apps-item">
<strong class="apps-item-name">{{app.name}}</strong>
<span class="apps-item-minor">{{app.domains[0]}}</span>
<span class="apps-item-status" :class="{ 'is-active': app.isActive }" v-localize="getStatus(app)"></span>
@@ -17,7 +17,7 @@
export default {
props: {
value: {
items: {
type: Array,
default: () => []
}
@@ -2,10 +2,10 @@
<div class="apps">
<ui-header-bar title="@application.list" :count="count" :back-button="true" />
<div class="ui-blank-box">
<applications-items v-model="apps" />
<applications-items v-model:items="apps" />
</div>
</div>
</template>
</template>
<script>
+1 -1
View File
@@ -13,7 +13,7 @@
<strong v-localize="item.name"></strong>
<template v-if="item.description">
<br>
{{item.description | localize({ tokens: tokens })}}
<span v-localize="{ key: item.description, tokens: tokens }"></span>
</template>
</p>
</router-link>
+11
View File
@@ -0,0 +1,11 @@
import Plugin from 'zero/core/plugin';
let plugin = new Plugin('zero');
plugin.mounted = () =>
{
console.info(this.name + ' mounted');
};
export default plugin;
-44
View File
@@ -1,44 +0,0 @@
zero.renderers = zero.renderers || {};
// https://webpack.js.org/guides/dependency-management/#require-context
const requireComponent = require.context(
// Look for files in the current directory
'.',
// Do look in subdirectories
true,
// .js files
/[\w-]+\.js/
);
// For each matching file name...
requireComponent.keys().forEach((path) =>
{
let pathParts = path.split('/');
let fileName = pathParts[pathParts.length - 1];
if (fileName === 'globals.js')
{
return;
}
const componentConfig = requireComponent(path);
const alias = componentConfig.default.alias;
zero.renderers[alias] = componentConfig.default;
});
// load renderers from plugins
// TODO use correct paths for sure
//const pluginrenderers = require.context('@/../zero.commerce/plugin', true, /renderers\.js$/);
//// for each matching file name...
//pluginrenderers.keys().foreach((path) =>
//{
// let pathparts = path.split('/');
// const componentconfig = pluginrenderers(path);
// const alias = componentconfig.default.alias;
// zero.renderers[alias] = componentconfig.default;
//});
+24
View File
@@ -0,0 +1,24 @@
zero.renderers = zero.renderers || {};
export default function (app)
{
const requireComponent = require.context('.', true, /[\w-]+\.js/);
requireComponent.keys().forEach(path =>
{
let pathParts = path.split('/');
let fileName = pathParts[pathParts.length - 1].split('.')[0];
if (fileName === 'register')
{
return;
}
const componentConfig = requireComponent(path);
const config = componentConfig.default || componentConfig;
const alias = config.alias;
zero.renderers[alias] = config;
});
};
+15 -21
View File
@@ -123,27 +123,9 @@ routes.push({ name: '404', path: '/:pathMatch(.*)*', component: () => import('ze
// create the router with history mode
const router = createRouter({
history: createWebHistory(),
routes: routes,
base: zero.path,
linkActiveClass: 'is-active',
linkExactActiveClass: 'is-active-exact',
scrollBehavior(to, from, savedPosition)
{
return savedPosition ? savedPosition : { x: 0, y: 0 };
}
});
// set meta title before routing
router.beforeEach((to, from, next) =>
const beforeEach = (to, from, next) =>
{
let isGuarded = false;
@@ -201,7 +183,19 @@ router.beforeEach((to, from, next) =>
{
callback();
}
});
};
export default router;
// create the router with history mode
export default createRouter({
history: createWebHistory(zero.path),
routes: routes,
linkActiveClass: 'is-active',
linkExactActiveClass: 'is-active-exact',
beforeEach: beforeEach,
scrollBehavior(to, from, savedPosition)
{
return savedPosition ? savedPosition : { x: 0, y: 0 };
}
});
+45 -3
View File
@@ -1,5 +1,47 @@
import mitt from 'mitt';
const emitter = mitt();
let handlers = new Map();
export default emitter;
/*
* Adds a new handler for an event of a specfic type
*/
const on = (type, handler) =>
{
const typeHandlers = handlers.get(type);
const added = typeHandlers && typeHandlers.push(handler);
if (!added)
{
handlers.set(type, [handler]);
}
};
/*
* Removes a specifc event handler
*/
const off = (type, handler) =>
{
const typeHandlers = handlers.get(type);
if (typeHandlers)
{
typeHandlers.splice(typeHandlers.indexOf(handler) >>> 0, 1);
}
};
/*
* Adds a new handler for an event of a specfic type
*/
const emit = (type, event) =>
{
(handlers.get(type) || []).slice().map(handler => handler(event));
(handlers.get('*') || []).slice().map(handler => handler(type, event));
};
export default {
on,
off,
emit
};
+74 -77
View File
@@ -1,86 +1,83 @@
import Vue from 'vue';
import Strings from 'zero/services/strings';
import { find as _find, extend as _extend } from 'underscore';
export default new Vue({
let instances = [];
data: () => ({
instances: []
}),
methods: {
success(label, text, options)
{
this.show(_extend({
type: 'success',
label: label,
text: text
}, options || {}));
},
const success = (label, text, options) =>
{
show(_extend({
type: 'success',
label: label,
text: text
}, options || {}));
};
error(label, text, options)
{
this.show(_extend({
type: 'error',
label: label,
text: text
}, options || {}));
},
const error = (label, text, options) =>
{
show(_extend({
type: 'error',
label: label,
text: text
}, options || {}));
};
show(options, text)
{
let me = this;
if (typeof options === 'string')
{
options = {
label: options,
text: text
};
}
options = _extend({
id: Strings.guid(),
type: 'default',
label: null,
text: null,
persistent: false,
duration: 3000,
timeout: null,
close: () =>
{
clearTimeout(options.timeout);
me.close(this.instances.indexOf(options));
}
}, options);
if (!options.label && !options.text)
{
return;
}
options.timeout = setTimeout(() =>
{
options.close();
}, options.duration);
this.instances.push(options);
},
close(index)
{
if (index > -1)
{
this.instances.splice(index, 1);
}
else
{
this.instances = [];
}
}
const show = (options, text) =>
{
if (typeof options === 'string')
{
options = {
label: options,
text: text
};
}
});
options = _extend({
id: Strings.guid(),
type: 'default',
label: null,
text: null,
persistent: false,
duration: 3000,
timeout: null,
close: () =>
{
clearTimeout(options.timeout);
close(instances.indexOf(options));
}
}, options);
if (!options.label && !options.text)
{
return;
}
options.timeout = setTimeout(() =>
{
options.close();
}, options.duration);
instances.push(options);
};
const close = index =>
{
if (index > -1)
{
instances.splice(index, 1);
}
else
{
instances = [];
}
};
export default {
success,
error,
show,
close
};
+122 -128
View File
@@ -1,134 +1,128 @@
import { createApp } from 'vue';
import AppConfirm from 'zero/components/overlays/confirm';
import Strings from 'zero/services/strings';
import EventHub from 'zero/services/eventhub';
import { find as _find, extend as _extend } from 'underscore';
export default createApp({
data: () => ({
dropdownInstance: null,
instances: []
}),
methods: {
// sets a new active dropdown so the old one gets auto-closed
setDropdown(instance)
{
if (this.dropdownInstance != null)
{
this.dropdownInstance.hide();
}
this.dropdownInstance = instance;
},
// open a deletion confirm dialog with the given options
confirmDelete(title, text)
{
let options = _extend({
title: typeof title === 'string' ? title : '@deleteoverlay.title',
text: text || '@deleteoverlay.text',
confirmLabel: '@deleteoverlay.confirm',
confirmType: 'danger',
closeLabel: '@deleteoverlay.close',
component: AppConfirm,
autoclose: false,
softdismiss: false
}, typeof title === 'object' ? title : {});
return this.open(options);
},
// open a confirm dialog with the given options
confirm(title, text)
{
let options = _extend({
title: title,
text: text,
component: AppConfirm,
autoclose: true,
softdismiss: false
}, typeof title === 'object' ? title : {});
return this.open(options);
},
// opens an overlay
open(options)
{
const defaultWidth = options.display === 'editor' ? 560 : 460;
options = _extend({
id: Strings.guid(),
display: 'dialog',
width: defaultWidth,
hide: this.close,
autoclose: true,
softdismiss: options.display !== 'editor',
closeLabel: '@ui.close',
confirmLabel: '@ui.confirm',
confirmType: 'default'
}, options);
options.theme = 'default';
this.instances.push(options);
return new Promise((resolve, reject) =>
{
options.close = () =>
{
this.close(options);
reject(options);
};
options.confirm = data =>
{
if (options.autoclose)
{
this.close(options);
}
resolve(data, options);
};
});
},
// closes an overlay
close(instance)
{
if (this.instances.length < 1)
{
return;
}
if (!instance)
{
this.instances.pop();
return;
}
if (typeof instance === 'string')
{
instance = _find(this.instances, item => item.id === instance);
}
if (instance)
{
const index = this.instances.indexOf(instance);
this.instances.splice(index, 1);
}
},
// closes all overlays
closeAll()
{
this.instances.forEach(instance =>
{
});
}
let currentInstance = null;
let instances = [];
// sets a new active dropdown so the old one gets auto-closed
const setDropdown = instance =>
{
if (currentInstance != null)
{
currentInstance.hide();
}
});
currentInstance = instance;
};
// open a deletion confirm dialog with the given options
const confirmDelete = (title, text) =>
{
let options = _extend({
title: typeof title === 'string' ? title : '@deleteoverlay.title',
text: text || '@deleteoverlay.text',
confirmLabel: '@deleteoverlay.confirm',
confirmType: 'danger',
closeLabel: '@deleteoverlay.close',
component: AppConfirm,
autoclose: false,
softdismiss: false
}, typeof title === 'object' ? title : {});
return open(options);
};
// open a confirm dialog with the given options
const confirm = (title, text) =>
{
let options = _extend({
title: title,
text: text,
component: AppConfirm,
autoclose: true,
softdismiss: false
}, typeof title === 'object' ? title : {});
return open(options);
};
// opens an overlay
const open = options =>
{
const defaultWidth = options.display === 'editor' ? 560 : 460;
options = _extend({
id: Strings.guid(),
display: 'dialog',
width: defaultWidth,
hide: close,
autoclose: true,
softdismiss: options.display !== 'editor',
closeLabel: '@ui.close',
confirmLabel: '@ui.confirm',
confirmType: 'default'
}, options);
options.theme = 'default';
instances.push(options);
EventHub.emit('overlay:open', instances);
return new Promise((resolve, reject) =>
{
options.close = () =>
{
close(options);
reject(options);
};
options.confirm = data =>
{
if (options.autoclose)
{
close(options);
}
resolve(data, options);
};
});
};
// closes an overlay
const close = instance =>
{
if (instances.length < 1)
{
return;
}
if (!instance)
{
instances.pop();
EventHub.emit('overlay:close', instances);
return;
}
if (typeof instance === 'string')
{
instance = _find(instances, item => item.id === instance);
}
if (instance)
{
const index = instances.indexOf(instance);
instances.splice(index, 1);
EventHub.emit('overlay:close', instances);
}
};
export default {
setDropdown,
confirmDelete,
confirm,
open,
close
};
+10 -7
View File
@@ -6,17 +6,20 @@ zero.apps = {
};
import Router from 'zero/router.config.js';
//import 'zero/components/globals';
import registerComponents from 'zero/components/register';
import registerDirectives from 'zero/directives/register';
//import 'zero/filters/globals';
//import 'zero/renderers/globals';
//import 'zero/pages/register';
import registerRenderers from 'zero/renderers/register';
//import 'zero/zero.plugins.js';
import 'zero/zero.plugins.js';
//import ZeroPlugin from 'zero/plugin.js';
const app = createApp(App);
app.use(Router);
registerDirectives(app);
console.info(app);
registerDirectives(app);
registerComponents(app);
registerRenderers(app);
app.mount('#app');
//console.info(ZeroPlugin);
-1
View File
@@ -14,7 +14,6 @@
"dayjs": "^1.8.26",
"flatpickr": "^4.6.3",
"lodash": "*",
"mitt": "^2.1.0",
"sortablejs": "^1.10.2",
"underscore": "^1.10.2",
"vue": "3.0.0",