system for creating plugins

This commit is contained in:
2020-10-16 11:23:54 +02:00
parent ba85847c54
commit 525fa8b147
19 changed files with 381 additions and 154 deletions
+3 -1
View File
@@ -270,4 +270,6 @@ Temp/
**/Assets/**/setup.*
**/Assets/**/*.js
deps/*.dll
zero.Commerce/
zero.Commerce/
zero.Web.UI/package.json
zero.Web.UI/package-lock.json
+6 -6
View File
@@ -19,16 +19,15 @@
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 Router from 'zero/router.config.js'
//import Router from 'zero/router.config.js'
import AuthApi from 'zero/services/auth.js'
import 'zero/vue.config.js'
import 'zero/axios.config.js'
import 'zero/zero.config.js'
import 'zero/config/vue.config.js'
import 'zero/config/axios.config.js'
export default {
name: 'app',
router: Router,
// router: Router,
components: { AppNavigation, AppOverlays, AppLogin, AppNotifications },
@@ -38,6 +37,7 @@
created()
{
console.info(this.zero);
AuthApi.$on('authenticated', isAuthenticated =>
{
this.isAuthenticated = isAuthenticated;
@@ -52,7 +52,7 @@
getClassList()
{
return {
'is-preview': this.$route.name === 'preview'
'is-preview': false //this.$route.name === 'preview'
};
}
}
+75
View File
@@ -0,0 +1,75 @@
import Localization from 'zero/services/localization';
// set meta title before routing
const beforeEach = () => (to, from, next) =>
{
let isGuarded = false;
// set document title + call next
let callback = () =>
{
let title = Localization.localize('@zero.name');
if (to.meta.name && to.meta.alias !== zero.alias.sections.dashboard)
{
let name = to.meta.name;
let nameParts = _isArray(name) ? name : [name];
let translations = [];
nameParts.forEach(part =>
{
if (part)
{
translations.push(Localization.localize(part));
}
});
title += ': ' + translations.join(' - ');
}
document.title = title;
next();
};
// dirty form guard
if (from.matched.length && from.matched[0].instances)
{
let instance = from.matched[0].instances.default;
if (instance.$refs['form'] && typeof instance.$refs.form.beforeRouteLeave === 'function')
{
isGuarded = true;
instance.$refs.form.beforeRouteLeave(to, from, res =>
{
if (res === false)
{
next(false);
}
else
{
callback();
}
});
}
}
if (!isGuarded)
{
callback();
}
};
export default {
mode: 'history',
base: zero.path,
linkActiveClass: 'is-active',
linkExactActiveClass: 'is-active-exact',
beforeEach: beforeEach,
scrollBehavior(to, from, savedPosition)
{
return savedPosition ? savedPosition : { x: 0, y: 0 };
}
};
@@ -1,25 +1,20 @@
import Vue from 'vue';
import VueRouter from 'vue-router';
import Localization from 'zero/services/localization';

import { isArray as _isArray, find as _find, map as _map, filter as _filter } from 'underscore';
import { warn } from 'zero/services/debug';
Vue.use(VueRouter);
const routes = [];
let routes = [];
// add defined backoffice sections (with their children) to the router
let addSection = (section, component, parent) =>
const addSection = (section, component, parent) =>
{
let route = {
path: section.url,
path: section.url,
component: component,
name: (parent ? parent.alias + '-' : '') + section.alias,
meta: {
name: [ section.name, (parent ? parent.name : null) ],
name: [section.name, (parent ? parent.name : null)],
alias: section.alias,
section: section,
parent: parent
@@ -93,7 +88,7 @@ let addRoutesPerContext = (context, isPlugin) =>
}
parentRoute.children.push(route);
}
// add routes to root
// add routes to root
else
{
routes.push(route);
@@ -120,92 +115,9 @@ catch (exc)
// add fallback route (this should probably by 404 page)
routes.push({ name: '404', path: '*', component: () => import('zero/pages/notfound') });
// create the router with history mode
const router = new VueRouter({
mode: 'history',
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) =>
{
let isGuarded = false;
// set document title + call next
let callback = () =>
{
let title = Localization.localize('@zero.name');
if (to.meta.name && to.meta.alias !== zero.alias.sections.dashboard)
{
let name = to.meta.name;
let nameParts = _isArray(name) ? name : [name];
let translations = [];
nameParts.forEach(part =>
{
if (part)
{
translations.push(Localization.localize(part));
}
});
title += ': ' + translations.join(' - ');
}
document.title = title;
next();
};
// dirty form guard
if (from.matched.length && from.matched[0].instances)
{
let instance = from.matched[0].instances.default;
if (instance.$refs['form'] && typeof instance.$refs.form.beforeRouteLeave === 'function')
{
isGuarded = true;
instance.$refs.form.beforeRouteLeave(to, from, res =>
{
if (res === false)
{
next(false);
}
else
{
callback();
}
});
}
}
if (!isGuarded)
{
callback();
}
});
export default router;
export default routes;
+36
View File
@@ -0,0 +1,36 @@
//zero.plugins = zero.plugins || [];
//// TODO correct path
//let plugins = require.context('@/../zero.Commerce/Plugins/zero.Commerce', true, /plugin\.js$/); // TODO dynPATH
//plugins.keys().forEach(path =>
//{
// const routesDefinition = plugins(path);
// const definition = routesDefinition.default || routesDefinition;
// definition.name = 'commerce'; // TODO
// zero.plugins.push(definition);
//});
////plugins = require.context('@/../zero.Debug', true, /plugin\.js$/); // TODO dynPATH
////plugins.keys().forEach(path =>
////{
//// const routesDefinition = plugins(path);
//// const definition = routesDefinition.default || routesDefinition;
//// definition.name = 'project'; // TODO
//// zero.plugins.push(definition);
////});
//// run setup action directly
//zero.plugins.forEach(plugin =>
//{
// if (typeof plugin.setup === 'function')
// {
// plugin.setup();
// }
//});
+16
View File
@@ -0,0 +1,16 @@
export default {
version: '0.1.0',
apiPath: '/zero/api/',
path: '/zero/',
media: {
defaults: {
images: ['.jpg', '.jpeg', '.png', '.webp', '.svg'],
images_natural: ['.jpg', '.jpeg', '.webp'],
images_artificial: ['.png', '.webp', '.svg']
}
}
}
+68
View File
@@ -0,0 +1,68 @@
class Plugin
{
#name;
#onInstall;
routes = [];
renderers = {};
constructor(name)
{
this.#name = name;
}
get name()
{
return this.#name;
}
get install()
{
return this.#onInstall;
}
set install(callback)
{
this.#onInstall = callback;
}
/*
* Add a new vue form renderer to the global configuration
*/
addRenderer(alias, config)
{
this.renderers[alias] = 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)
{
let items = !Array.isArray(renderers) ? Object.values(renderers) : renderers;
items.forEach(item => this.addRenderer(item.alias, item));
}
/*
* Add a new route to vue-router
*/
addRoute(route)
{
this.routes.push(route);
}
/*
* Adds new routes to vue-router
*/
addRoutes(routes)
{
this.routes.forEach(route => this.addRoute(route));
}
};
export default Plugin;
+14
View File
@@ -0,0 +1,14 @@
import Plugin from './plugin.js';
import renderers from '../renderers/all.js';
import routes from '../config/routes.js';
const plugin = new Plugin('zero');
// add renderers
plugin.addRenderers(renderers);
// add routes
plugin.addRoutes(routes);
export default plugin;
+86
View File
@@ -0,0 +1,86 @@
// ref:
// https://github.com/vuejs/vue-router/blob/dev/src/index.js
import ZeroPlugin from './plugin.zero.js';
import CommercePlugin from '../../../zero.Commerce/Plugins/zero.Commerce/plugin.js'; // TODO dynPath
import options from './options.js';
class Zero
{
static install;
config = { ...options };
#vue = null;
#plugins = [];
constructor(vue, opts)
{
this.config = { ...options, ...opts };
this.#vue = vue;
this.use(ZeroPlugin);
this.use(CommercePlugin);
}
/*
* Get the currently installed version of the zero frontend
*/
get version()
{
return this.config.version;
}
/*
* Get all installed zero plugins
*/
get plugins()
{
return this.#plugins;
}
/*
* Locates zero plugins and install them
*/
setup()
{
}
/*
* Installs a zero plugin
* Each plugin is a superset of a vue (client) plugin and can therefore hook into the vue system
*/
use(plugin)
{
if (typeof plugin.install === 'function')
{
plugin.install(this.#vue, this);
}
this.#plugins.push(plugin);
console.log(`[zero] Installed %c${plugin.name}%cplugin`, 'font-style:italic;');
}
};
/*
* Registers zero within the vue system
*/
Zero.install = (vue, opts) =>
{
const zero = new Zero(vue, opts);
Object.defineProperty(vue.prototype, 'zero', {
get: () => zero
});
};
export default Zero;
+18
View File
@@ -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
};
+1
View File
@@ -0,0 +1 @@
//# sourceMappingURL=test.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"test.js","sourceRoot":"","sources":["test.ts"],"names":[],"mappings":""}
-11
View File
@@ -1,11 +0,0 @@
window.zero = window.zero || {};
window.zero.config = window.zero.config || {};
window.zero.config.media = {
defaults: {
images: ['.jpg', '.jpeg', '.png', '.webp', '.svg'],
images_natural: ['.jpg', '.jpeg', '.webp'],
images_artificial: ['.png', '.webp', '.svg']
}
};
-36
View File
@@ -1,36 +0,0 @@
zero.plugins = zero.plugins || [];
// TODO correct path
let plugins = require.context('@/../zero.Commerce/Plugins/zero.Commerce', true, /plugin\.js$/); // TODO dynPATH
plugins.keys().forEach(path =>
{
const routesDefinition = plugins(path);
const definition = routesDefinition.default || routesDefinition;
definition.name = 'commerce'; // TODO
zero.plugins.push(definition);
});
//plugins = require.context('@/../zero.Debug', true, /plugin\.js$/); // TODO dynPATH
//plugins.keys().forEach(path =>
//{
// const routesDefinition = plugins(path);
// const definition = routesDefinition.default || routesDefinition;
// definition.name = 'project'; // TODO
// zero.plugins.push(definition);
//});
// run setup action directly
zero.plugins.forEach(plugin =>
{
if (typeof plugin.setup === 'function')
{
plugin.setup();
}
});
+11 -5
View File
@@ -1,16 +1,22 @@
import Vue from 'vue';
import VueRouter from 'vue-router';
import Zero from 'zero/core/zero.js';
import App from 'zero/app';
import 'zero/components/globals';
import 'zero/directives/globals';
import 'zero/filters/globals';
zero.apps = {
shared: true
};
import 'zero/components/globals';
import 'zero/directives/globals';
import 'zero/filters/globals';
import 'zero/renderers/globals';
//import 'zero/pages/register';
import 'zero/zero.plugins.js';
//import ZeroPlugin from 'zero/config/zero.plugin.js';
Vue.use(VueRouter);
Vue.use(Zero);
import 'zero/config/zero.plugins.js';
new Vue(App).$mount('#app');
+37
View File
@@ -0,0 +1,37 @@
{
// TODO
// create tool which installs node, npm + other global dependencies (npm-deps(1)) needed to run the application
// (see umbraco for details)
// maybe this can be done via the create dotnet-tool
"name": "zero",
"version": "0.1.0",
"private": true,
"scripts": {
"install": "",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"build-dev": "vue-cli-service build --mode development",
"watch": "vue-cli-service build --mode development --watch"
},
"dependencies": {
"core-js": "^3.6.5",
"axios": "^0.19.2",
"dayjs": "^1.8.26",
"flatpickr": "^4.6.3",
"lodash": "*",
"sortablejs": "^1.10.2",
"underscore": "^1.10.2",
"vue": "^2.6.11",
"vue-router": "^3.2.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"syncpack": "^5.6.10",
"vue-template-compiler": "^2.6.11"
}
}
+2
View File
@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
"install": "",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"build-dev": "vue-cli-service build --mode development",
@@ -25,6 +26,7 @@
"@vue/cli-service": "~4.5.0",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"syncpack": "^5.6.10",
"vue-template-compiler": "^2.6.11"
}
}