Applet system for my retro tech haven 💾
A few weeks ago I posted about the creation of my retro tech haven. In this follow-up post, I want to introduce you to the applet system of my portfolio app. The applet system allows users to dynamically install/uninstall additional functionality. A lot of desktop widgets beyond the default ones are actually implemented as widgets.
But first some definitions:
Widget: A window-based construct that shows content to the user, either statically or dynamically. Run via the desktop by launching it via an icon-based link
Applet: A dynamically deployed widget consisting as a class implementation, allowing for I/O between the host layer and the applet layer. Installed locally per-client using the applet manager.
The applet manager is used to install or remove applets from the user's browser. Hence all applets are on a per-client basis, using the local storage of ones browser.
An applet is essentially a class with a given layout and must be assigned to the window object, so the applet system can instantiate an object from the related window.AppletName class. Since applets are window based in order to let the user interact with them in a GUI manner, they must provide some information about how they want the window to be as well as return a view code. The view code is basically embedded in the window body in order to define its content. Additionally, they can also specify further stylings. Lastly, there are various events fired which the applet can use to control its behaviour.
The following event methods are supported:
- onInstall: Called when the applet is installed
- onRemove: Called when the applet is uninstalled
- onLoad: Called when the applet is loaded (page load/refresh and initial install)
- onShow: Called when the applet is shown, e.g. when launching via desktop
- onClose: Called when the applet is closed, e.g. via the close action button in the title bar
Important note: As you have read, the onLoad event is fired for each applet when the page is loaded/refreshed, as well as during the installation process. This is so the applet can initialize and load resources when needed. In order to handle when the app is launched (= shown), use the onShow event method.
Here is a complete sample applet:
/**
* Sample Applet
*/
window.SampleApplet = class {
/**
* Construct class object instance
*/
constructor()
{
console.log('constructor');
}
/**
* Called when the applet is installed
*
* @return void
*/
onInstall()
{
console.log('onInstall');
}
/**
* Called when the applet is uninstalled
*
* @return void
*/
onRemove()
{
console.log('onRemove');
}
/**
* Called when the applet is loaded
* This happens everytime the page is loaded/refreshed, or when the applet is installed
*
* @return void
*/
onLoad()
{
console.log('onLoad');
}
/**
* Called when the applet is shown, e.g. when launching via desktop
*
* @return void
*/
onShow()
{
console.log('onShow');
const wnd = document.querySelector('#column-window-sample-applet');
const xpos = window.readSetting('sample-applet-position-x', null);
const ypos = window.readSetting('sample-applet-position-y', null);
if ((wnd) && (xpos) && (ypos)) {
wnd.style.position = 'absolute';
wnd.style.left = xpos;
wnd.style.top = ypos;
}
}
/**
* Called when the applet is closed, e.g. via the close action button in the title bar
*
* @return void
*/
onClose()
{
console.log('onClose');
const wnd = document.querySelector('#column-window-sample-applet');
if (wnd) {
const xpos = window.saveSetting('sample-applet-position-x', wnd.style.left, false);
const ypos = window.saveSetting('sample-applet-position-y', wnd.style.top, false);
}
}
/**
* Return the HTML content which is rendered into the applet window
*
* @return string
*/
view()
{
return `
<div class="sample-applet">
<div class="sample-applet-info">
Hey there! This is a sample applet.
</div>
<div class="sample-applet-button">
<a class="btn" href="https://www.danielbrendel.com/applets/sample-applet.js" target="_blank">View source</a>
</div>
</div>
`;
}
/**
* Provide applet settings here
*
* @return object
*/
settings()
{
return {
wndWidth: '320px',
wndHeight: '140px',
btnClose: true,
btnMaximize: false,
btnMinimize: false
};
}
/**
* Return basic information on the applet
*
* @return object
*/
infos()
{
return {
name: 'Sample Applet',
version: '1.0',
icon: window.location.origin + '/img/icons/applet.png'
};
}
/**
* Return the CSS styles which are rendered into the page
*
* @returns object
*/
styles()
{
return `
#column-window-sample-applet .window-body {
width: 100%;
height: 100%;
}
.sample-applet {
position: relative;
text-align: center;
}
.sample-applet-info {
margin-top: 20px;
font-size: 1.4em;
}
@media screen and (min-width: 951px) {
.sample-applet-info {
font-size: 1.2em;
}
}
.sample-applet-button {
margin-top: 20px;
}
.sample-applet-button a.btn {
width: 150px !important;
}
`;
}
}
As you can see it's pretty straight forward.
Currently, there isn't any convenient way to submit third-party applets as I simply don't want it. I will create a repository on GitHub with all the currently existing applets, but I am not sure if I will want to allow PRs. After all, this is my personal retro tech haven, so I don't really want anyone hopping on the board. However, as the project is under the MIT license, you can fork it and adapt it to your own needs as long as it fits the license.
Thank you for reading. 💚
➡️ Here is the initial article on the subject
https://www.danielbrendel.com/blog/65-the-story-of-creating-my-own-retro-tech-haven
➡️ You can also check out the sourcecode of my portfolio app on GitHub:
https://github.com/danielbrendel/dnyPortfolioApp
If you like my work, I'd really appreciate a donation, or even a sponsorship on GitHub. Also I'd be very happy if you star the repository on GitHub. 💚