Wyser.js
A JavaScript Single Page Application (SPA) Framework
Installation
Intall using npm package manager
npm i wyser
Usage
Src\app.js
import middleware from "./middleware";
import components from "./components";
import tags from "./tags";
import layouts from "./layouts";
import routes from "./routes";
import events from "./events";
export default () => {
routes();
components();
events();
tags();
layouts();
middleware();
};
Src\index.js
import Wyser from 'wyser';
import app from './app.js';
app();
Wyser.load();
Layouts
Define the layout of components to use on page load
Src\layouts.js
import { Layout } from 'wyser';
import { create, div, component, element } from 'wyser/Dom';
export default () => {
Layout('page.one.layout', create(element(div('root', 'container'), [
element(component('sidenav')),
element(div('main', 'container__main'), [
element(component('header')),
element(component('page.title')),
element(component('profile.navigation')),
element(div('container-main-center', 'container__main__center profile'), [
element(div('profile-main', 'profile__main'), [
element(component('profile.details'))
])
])
])
])));
Layout('page.two.layout', create(element(div('root', 'container'), [
element(component('sidenav')),
element(div('main', 'container__main'), [
element(component('header')),
element(component('page.title')),
element(div('container-main-center', 'container__main__center'), [
element(component('page.stuff'))
])
])
])));
};
Tags
Used for changing content dynamically on page reload
Src\tags.js
import { Tag } from "wyser";
export default () => {
/**
tag 'profile.title' will be switched out for
component 'author.profile.title' or 'reader.profile.title'
depending on the URI
*/
Tag('profile.title', [
{ name: 'author.profile.title', uri: '/a/johndoe/profile' }, // uri (string) | uris (array)
{ name: 'reader.profile.title', uri: '/r/janedoe/profile' } // uri (string) | uris (array)
]);
};
Components
Building blocks of the application, they are loaded individually and cached and re-used where necessary
Src\components.js
Creating a standard component
import { Component } from "wyser";
export default () => {
Component('sidenav', {
html: '<div>I am sidenav.</div>',
scope: 'all' // string | array
});
}
Creating a components with navigation
Components.create('sidenav', {
html: '<div>I am sidenav.</div>',
nav: {
parent: 'sidenav',
/**
classes or class that can be clicked | Array | String
*/
targets: 'sidenav__item',
/**
Highlights the main page on a navigation element like a header or sidebar while the current page is within a sub directory
*/
linkmultiple: {
/**
can be named anything really
*/
mainPage: ['page.main.tab.one', 'page.main.tab.two']
}
},
/**
The navigation is limited to routes within the scope
*/
scope: [
'page.one',
'page.two'
]
});
Properties to take note of
-
nav (Optional) Indicates that a component is of type navigation, makes things easier for the developer in such a way that the Framework detects an active page, adds event listeners, highlights active pages and sub-pages / sub-directories (linkmultiple is required for sub-pages / sub-directories)
-
linkmultiple (Optional) Each sub property like
mainPage
defined above correspondes todata-linkmultiple
on the HTML side of things. The array elements represent sub-pages or sub-directories -
scope (Optional but required for nav) Navigation is limited to pages defined in the scope
HTML example of a nav component
<div class="sidenav">
<div class="sidenav__item" data-linkmultiple="mainPage" data-linkaddress='/u/johndoe/page-one' data-linkactive='sidenav__item--active'>
<p>Page one</p>
</div>
<div class="sidenav__item" data-linkaddress='/u/johndoe/page-two' data-linkactive='sidenav__item--active'>
<p>Page two</p>
</div>
</div>
Routes
Routes are paths where a layout has to be rendered
Src\routes.js
import { Route } from 'wyser';
export default () => {
Route('page.one', {
title: 'Page one',
uri: '/u/:user/page-one',
tags: ['type.of.tag.one', 'page.name', 'another.tag'],
layout: 'page.one.layout'
});
Route('page.two', {
title: 'Page two',
uri: '/u/:user/page-two',
tags: ['type.of.tag.two', 'page.name', 'another.tag'],
layout: 'page.two.layput'
});
};
Route Params. Params can be defined in a route URI and resolved when that route is loaded by the brower
import { Router } from "wyser";
console.log(Router.currentParams); // returns params of the current route
console.log(Router.use('route.name').params) // returns params
Route Tags. Group routes into multiple categories and return their names when they are used
import { Router } from "wyser";
export default () => {
const allRoutes = Router.getRoutesByTag('another.tag');
console.log(allRoutes[0], allRoutes[1]) // [page.one], [page.two]
// tags can used to limit components to certain routes effeciently
Components.create('sidenav', {
html: '<div>I am sidenav.</div>',
// attaches on-click events to links
nav: {
// highlights relevent icon when on a sub-page
linkmultiple: {
links: Router.getRoutesByTag('links'),
linksTwo: Router.getRoutesByTag('linksTwo')
}
},
// limits 'sidenav' to specified routes
scope: Router.getRoutesByTag('page.name')
});
};
Middleware
Middleware functions are run every time before components and routes are loaded
Src\middleware.js
import { Middleware, Singleton } from "wyser";
module.exports = () => {
// string | array
Middleware('page.one', async (next) => {
const response = await fetch('/some-data');
if (response.data) {
Singleton.save(
'someData',
response.data
);
}
next();
});
};
Events
Component events, will be fired when something happens to the component, like when it is loaded
Src\events.js
import { Components, Singleton } from 'wyser';
const getSideNav = (someData) => {
return `<div>Name: ${someData.name}</div>`;
};
export default () => {
// runs every time before the component has loaded
Components.use('sidenav').onBeforeLoad((component) => {
const sidenav = Singleton.get('someData');
component.html = sidenav;
});
// runs every time after the component has loaded
Components.use('sidenav').onLoaded((component) => {
// do stuff
});
};