• Stars
    star
    14
  • Rank 1,438,076 (Top 29 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 5 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Simple and lightweight lightning library to eliminate boilerplate code

lightning-core

What is this?

lightning-core is a small library to use features of lightning in es6 style. It allows to eliminate the most common boilerplate code such as:

  • Toasts
  • Server calling
  • Creating components, modals
  • Downloading files
  • Working with local/session storage
  • Working with libraries (overlay, navigation, notification)
  • Extension Modules

Why do I need this?

Lots of projects I've ran into has either custom library-like component or JS resource file or they are stuck with each component defining a "new solution" for each pain-point, e.g. method for creating a Toast in every single component. The purpose of this library component is to stop this madness.

Lightning?

Lightnig is still something viable for a new project or for refactoring an old one and, I guess, will be for a couple more years, considering how many beautiful and functional things are already created for it.

How to use?

The library is really simple and intuitive to use. You add it onse in the top level component, it immutably writes itself into the window object and after that you can use it anywhere. The variable in the window object is setted only once in a singleton manner, so don't worry if you reference the library somwhere else in your component hierarchy - the top-level lightning-core component will initiallize first.

Start with:

<aura:component>
	<c:lightningCore/>
	<!--other code...-->
</aura:component>

And now you can use it anywhere in your project!

Architecture

Everything in this library is a class. Each use-case of lightning-core is represented by a single class. The classes are combined into a variable, that serves as an export list and then attached to a window object as a namespace. E.g.

new window.core.Toast().fire();

Example shows how you can access one of the exported classes - Toast and fire it. But you may also skip window in your reference to the class:

new core.Toast().fire();

Use-cases: Toast

There are a number of classes, which are designed to ease the way of creating Toasts:

/*base toast*/
new core.Toast({
	/*standard toast params*/
}).fire();

/*defaults mode to dismissable, time to 4s*/
new core.ToastQuick(type, title, message).fire();

/*defaults mode to dismissable, time to 8s*/
new core.ToastLong(type, title, message).fire();


/*defaults mode to dismissable, type to success, title to Success!, time to 4s*/
new core.ToastQuickSuccess(type, title, message).fire();

/*defaults mode to dismissable, type to error, title to Something went wrong!, time to 4s*/
new core.ToastQuickError(type, title, message).fire();

/*defaults mode to dismissable, type to success, title to Success!, time to 8s*/
new core.ToastLongSuccess(type, title, message).fire();

/*defaults mode to dismissable, type to error, title to Something went wrong!, time to 8s*/
new core.ToastLongError(type, title, message).fire();

The classes can be configured for each project independenlty and updated with default time, message, etc.

Use-cases: Server calling

Several classes are dedicated to perform server calls with or without Promises as well as providing a way to parse error messages and autohandle errors.

/*designed to perform promise-like async operations of calling the server, but that can be used with @AuraEnabled(cacheable=true). However, this cannot be chained like an actual promise*/
new core.ServerAction(component, actionName, params).execute()
	.then(result => {
		//result handling
	})
	.catch(error => {
		//error handling
	})
	.finally(() => {
		//some operation regardles of result
	})

/*default promise, that is designed to perform server calling, but that doesnt require to be wrapped with $A.getCallback(...)*/
new core.ServerActionPromise(component, actionName, params).execute()
	.then(result => {
		//result handling
	})
	.catch(error => {
		//error handling
	})
	.finally(() => {
		//some operation regardles of result
	})

/*...Handled classes are similar to the same classes without Handled, except for they automatically parse an error from response and show a toast with core.ToastLongError class*/
new core.ServerActionHandled()/*...*/
new core.ServerActionPromiseHandled()/*...*/

Use-cases: Components

With lightning-core dynamically creating new components is designed to be intuitive and easy - no need to check documentation every time you need a dynamically generated component.

/*each dynamic component is represented with a single instance of core.Component class and onyl requires a name and desired attributes. .create() method returns a Promsie*/
new core.Component(name, params).create()
	.then((component) => {
		/*do smth with newly generated component*/
	})
	.catch((error) => {
		/*handle errors*/
	});

/*there's also a way to create component in bulk*/
new core.Components()
	.addComponent(new core.Component(name, params))
	.addComponent(new core.Component(name, params))
	.addComponent(new core.Component(name, params))
	.create()
	.then((components) => {
		/*do smth with newly generated components*/
	})
	.catch((error) => {
		/*handle errors*/
	});

Use-cases: Modals

Creating modals is usually also a drag. With lightning-core creating modals is as easy as creating component. But! Modals are created based on the lightning:overlayLibrary. To use this library in lightning-core component you need to include this library into your instance of the library - simply include a library instance into the markup between opening and closing tags of <c:lightningCore>:

<aura:component>
	<c:lightningCore>
		<lightning:overlayLibrary/>
	</c:lightningCore>
	<!--other code...-->
</aura:component>

After that, you can use any supported functionality for the library, for example - Modal creation.

new core.Modal()
	.setBody(new Component(name, params))
	.setFooter(new Component(name, params))
	.show()
		.then((overlay) => {
			/*success callback*/
		})
		.catch((error) => {
			/*error callback*/
		});

Use-cases: Files

There's a small class for downloading files, instantiated from base64 or Blob.

/*new file*/
new core.File(base64Data);
new core.File(blobData);

/*converting file*/
new core.File(blobData).toBase64();
new core.File(base64Data).toBlob();

/*downlaoding*/
new core.File(base64Data).download(filename);
new core.File(blobData).download(filename);

Use-cases: Local/Session Storage

There's a small class for working with local or session storage.

const storage = new core.LocalStorage(); 
//or new core.SessionStorage() - can be used instead, both classes has identical interface
storage.set('key1', 'value');
storage.setObject('key2', {'object-key': 'object-value'}); //performs JSON.stringify(...)

storage.get('key1');
storage.getObject('key2'); //performs JSON.parse(...)

storage.clear();
storage.print();

Use-cases: Libraries

As was mentioned before in the Modals section, to work with a library and it's functionality you need to include library markup into the body of lightning-core component:

<aura:component>
	<!--currently supported libraries-->
	<c:lightningCore>
		<lightning:overlayLibrary/>
		<lightning:navigation/>
		<lightning:notificationsLibrary/>
	</c:lightningCore>
	<!--other code...-->
</aura:component>

Functionality which is supported through these libraries:

  • Page reference navigation (lightning:navigation)
  • Notices and Toasts (lightning:notificationsLibrary)
  • Modals and Popovers (lightning:overlayLibrary)

Examples:

/*Navigation*/
const navigation = new core.Navigation();
navigation.navigate(new core.PageReferenceWebPage().setUrl(url));

/*Notice*/
new core.Notice()
	.setTitle(title)
	.setMessage(message)
	.show();

/*Toast - toasts has the same classes, as regular toast, but it has X in it's name. E.g.*/
new core.ToastXLongError(message).fire();

/*Modal*/
new core.Modal()
	.setBody(new Component(name, params))
	.setFooter(new Component(name, params))
	.show()
		.then((overlay) => {
			/*success callback*/
		})
		.catch((error) => {
			/*error callback*/
		});

/*Popover*/
new core.Popover()
	.setBody(body)
	.setReferenceSelector(referenceElementSelector)
	.show()
	.then((overlay) => {
			/*success callback*/
		})
		.catch((error) => {
			/*error callback*/
		});

Use-case: Extension Modules

If you would like to add new functions, custom libraries or even submodules to the lightning-core library, you can easily do this, by implementing lightningCoreModule interface in your custom component, writing logic for export() method and including your custom component into lightning-core body. First, create a module and implement interface and export method:

<!--Your custom module-->
<aura:component description="AlertModule" implements="c:lightningCoreModule">
</aura:component>
({
	//Controller
	//implementation of interface method
	export: function(cmp, evt, helper) {
		return helper.exportClasses();
	},
	
	//Helper
	exportClasses: function() {
		class AlertToast {
			constructor(message) {
				this.message = message;
			}

			fire() {
				alert(this.message);
			}
		}
		
		//returning value should always be an array in this format: 
		//[module_name, {exported_classes}]
		return ['alerts', {
			"AlertToast": AlertToast
		}];
	},
});

Finally, after you include your module into lightning-core body, you may start using it everywhere lightning-core is available:

<aura:component>
	<!--currently supported libraries-->
	<c:lightningCore>
		<c:AlertModule/>
	</c:lightningCore>
	<!--other code...-->
</aura:component>

Usage:

new core.alerts.AlertToast('My message').fire();

Lightning-core will perform a check if you've implemented export() method, if you've returned a propperly formatted value and if your submodule name overwrites any other functions/submodules. If any error occures, lightning-core will display an error message in error logs.

ToDo

  • Complete readme
  • Complete JS Docs
  • Add more functions