@gabrielphala/stem v1.4.1
Stem.js
A JavaScript Single Page Application (SPA) Framework
Installation
Intall using npm package manager
npm i @gabrielphala/stem
Usage
Src\app.js
import layouts from './layouts';
import groups from './groups';
import components from './components';
import pages from './pages';
import transitions from './transitions';
import events from './events';
export default () => {
layouts();
groups();
components();
pages();
transitions();
events();
};
Src\index.js
import stem from '@gabrielphala/stem';
import app from './app.js';
app();
stem.config({
logs: {
info: true, // Default false
warn: true, // Default true
},
page: {
title: 'Web title'
}
});
stem.load();
Layouts
Define the layout of components to use on page load
Src\layouts.js
import Layouts from '@gabrielphala/stem/layouts';
Layouts.create('layout.one', {
html: `
<div class="page-wrapper__main">
{{component.one}}
<!-- dynamic component (multiple components merged into one to change according to the page) -->
<div class="page-wrapper__main__center">
{{tag.group.one}}
</div>
</div>
`
});
Groups
Tag Groups
Used for changing content dynamically on page reload
Src\groups\tags.js
import Groups from "@gabrielphala/stem/Groups";
export default () => {
// Tag groups load components based on the page the user is currently on
Groups.use('tag').make('tag.group.one', [
// this component will be loaded when on the page '/u/:user/page-one'
{ name: 'page.one.component', uri: '/u/:user/page-one' },
// this component will be loaded when on the page '/u/:user/page-two'
{ name: 'page.two.component', uri: '/a/:user/page-two' },
]);
// match one tag component to multiple page URIs
Groups.use('tag').make('page.title', [
{
name: 'customer.page.titles',
uris: [
'/u/:username/profile',
'/u/:username/profile/tab-one',
'/u/:username/profile/tab-two',
]
},
]);
// or use page tags
Groups.use('tag').make('page.title', [
{
name: 'customer.page.titles',
uris: Pages.getPagesByTag('customer.page.titles')
},
]);
};
Transition Groups
Used for changing content dynamically when switching pages without reloading
Src\groups\transitions.js
import Groups from "@gabrielphala/stem/Groups";
export default () => {
// Transition groups replace the innerHTML of elements with specific components base on the page the user is switching to
Groups.use('transition').make('transition.group.one', [
{
name: 'page.one',
targets: [
// replaces the innerHTML of this element with the contents of the component ('page.one.component')
{ target: 'page-wrapper__main__center', component: 'page.one.component' },
]
},
{
name: 'page.two',
targets: [
// replaces the innerHTML of this element with the contents of the component ('page.two.component')
{ target: 'page-wrapper__main__center', component: 'page.two.component' },
]
}
]);
};
Components
Building blocks of the application, they are loaded individually and cached and re-used where necessary
Src\groups\components.js
Instantiate all components at once
import Components from "@gabrielphala/stem/Components";
export default () => {
Components.all([
{
name: 'page.one.component',
uri: '/c/page-one-component',
},
{
name: 'page.two.component',
uri: '/c/page-two-component',
}
]);
}
Instantiate components one at a time
Components.create('page.one.component', {
uri: '/c/page-one-component',
});
Components.create('page.two.component', {
uri: '/c/page-two-component',
});
Components with navigation
Components.create('sidenav', {
uri: '/c/sidenav',
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 pages within the scope
*/
scope: [
'page.one',
'page.two'
]
});
Components with beforeLoad
Pages.create('customer.profile', {
title: 'Profile',
uri: '/u/:username/profile',
layout: Layouts.use('customer.profile')
});
const profileOverview = Components.create('profile.overview', {
uri: '/c/profile/overview',
});
profileOverview.beforeLoad((Component) => {
Component.URI = Component.URI + '/' + Pages.currentParams.username;
Component.modified = true;
});
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-directoriesscope (Optional but required for nav) Navigation is limited to pages defined in the scope
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>
Pages
Pages are pages :)
Src\pages.js
import Pages from '@gabrielphala/stem/Pages';
import Layouts from '@gabrielphala/stem/Layouts';
export default () => {
Pages.create('page.one', {
title: 'Page one',
uri: '/u/:user/page-one',
tags: ['type.of.tag.one', 'page.name', 'another.tag'],
layout: Layouts.use('layout.one')
});
Pages.create('page.two', {
title: 'Page two',
uri: '/u/:user/page-two',
tags: ['type.of.tag.two', 'page.name', 'another.tag'],
layout: Layouts.use('layout.one')
});
};
Page Params, Params can be defined in a page URI and resolved when that page is loaded by the brower
Pages.currentParams; // returns params of the current page
Pages.use('page.name').params // returns params
Page Tags. Group pages into multiple categories and return their names when they are used
export default () => {
const allPages = Pages.getPagesByTag('another.tag');
console.log(allPages[0], allPages[1]) // page.one, page.two
// tags can used to limit components to certain pages effeciently
Components.create('sidenav', {
uri: '/c/sidenav',
nav: {
linkmultiple: {
links: Pages.getPagesByTag('links'),
linksTwo: Pages.getPagesByTag('linksTwo')
}
}
scope: Pages.getPagesByTag('page.name')
});
};
Transitions
Relationships between pages, what must happen when a user moves from page 1 to 2
Src\transitions.js
import Transitions from '@gabrielphala/stem/Transitions';
export default () => {
const transitionOne = Transitions.create('transition.one', {
from: ['page.one', 'page.two'],
to: ['page.two', 'page.two'],
group: 'transition.group.one'
});
transitionOne.updates((page) => {
// remove some elements and stuff
});
};
Events
Component events, will be fired when something happens to the component, like when it is loaded
Src\events\sign-in.js
import Components from '@gabrielphala/stem/Components';
import Stem from '@gabrielphala/stem';
import userAuth from '../../auth/User';
export default () => {
Components.use('customer.sign.in').onShow((Component) => {
$('.customer__login__form').on('submit', async (e) => {
e.preventDefault();
const response = await userAuth.signIn('customer');
if (response.successful)
Stem.load(response.redirect);
});
});
};
Page events, will be fired when something happens to the page, like when it is loaded
Src\events\profile.js
import Pages from '@gabrielphala/stem/Pages';
export default () => {
Pages.use('customer.profile').onShow((Page) => {
// Do something when customer profile is shown
});
};
Default page events, will be fired when something happens to a group of pages, like when they are loaded
Src\events\default.js
import Pages from '@gabrielphala/stem/Pages';
export default () => {
const defaultPage = Pages.use();
defaultPage
.create('customerForms', Pages.getPagesByTag('customerForms'))
.onShow((Page) => {
// Do something whenever a customer forms related page is shown
});
defaultPage
.create('sellerForms', Pages.getPagesByTag('sellerForms'))
.onShow((Page) => {
// Do something whenever a seller forms related page is shown
});
};