Lunar is a library for creating frontend projects in a very simple way. To use it, just add to your project the styles and the scripts inside the dist folder. Be creative.
- Lunar
The Lunar provides a set of very useful methods, that you can use to add and extend the functionalities of your scripts, creating interactive components easily. In this documentation, the Node
It returns the first element matching the selector passed to it, it means, it simple does the same thing as the document.querySelector() method, but it returns the element inside an instance of the LunarElement class.
let el = lunar.el('div');
console.log(el);
The pure property returns the original Node object:
console.log(el.pure); // <div></div>
It does the same as the el() method, but returns all the ocurrencies for the given selector, just like the document.querySelectorAll(), but in array form. You can also pass a second argument, to make this method return an array of Node objects.
let divs = lunar.all('div');
let pureDivs = lunar.all('div', true);
console.log(divs);
console.log(pureDivs);
This method verify if the given element is a LunarElement or a "pure" Node, such as HTML elements or SVG elements. It returns false if the element is a LunarElement, and true in other cases.
let el = document.querySelector('div');
console.log(lunar.isPure(el)); // true
This method is used in cases you have an array of LunarElement instances and you have to iterate in each of their pure elements, ignoring the LunarElement instance. See the example:
let divs = lunar.all('div');
lunar.iterate(divs, div => {
console.log(typeof div);
});
It creates a new Node object which is returned as a LunarElement object. You can do with almost anything it is possible with Node objects very easily. See the list of LunarElement methods.
let div = lunar.create('div');
This method is used to hide an element from the page, you can pass to it Element objects or LunarElement objects. When an element is hided by this or similiar methods, it receives the lunar-hidden and the lunar-opacity-0 classes, responsible to hide the element followed by a transition effect.
let div = lunar.el('div');
lunar.hide(div); // or
lunar.hide(div.pure);
It does the inverse effect of the hide() method, it means, if you have an hidden element, you can use this method to show again that element:
lunar.hide(div);
lunar.show(div);
Finally, we have a method that does the both effects of the two previous methods. For example, the following code shows and hides an element on each 1s:
setInterval(() => {
lunar.toggle(div);
}, 1000);
This method simply scrolls the page until the given element is visible and on top of the page. The first parameter is the object to be visible, and the second one is an optional scrollIntoViewOptions object.
let contact = lunar.el('#contactInfo');
lunar.appear(contact);
This is a very useful methods, that can give you the ability to create very interactive elements. It simply receives an element (Element or LunarElement) and a closure. Everytime the user click in a element that is not that given one, that closure will be executed, and this maybe very powerful. See the example:
let dd = lunar.el('#dropdown');
lunar.clickOut(dd, e => {
if (!dd.contains(e.target))
lunar.hide(dd);
});
In this example, we listen for a click in a element that is not the dd element, and we pass a closure (the handle function) to handle this event. This closure receives a parameter (it uses the addEventListener method), that is an object describing that event. Inside that closure, we verify if the target element is an element inside dd, just to be sure that the click was outside it, and we call the hide() method in case of a click outside it. See the documentation about the addEventListener().
Sometimes maybe hard or tiring to work with the defaults properties and methods of the elements and commonly our scripts compound a lot of code, that makes them sometimes difficult to undestand and find some error or bug. Due to this, the LunarElement class provides some very useful methods, that simplifies the most common tasks on working with elements. When you create an instance of that class, you need to pass a selector or an Element instance:
let el = lunar.el('#navbar');
// or
let navbar = document.querySelector('#navbar');
let el = lunar.el(navbar);
The el() method returns an instance of the LunarElement class with the given element. See below the list of methods and properties present in this class.
This property simply returns the type of the current element:
console.log(lunar.el('div').type); // div
Returns the id of the element:
console.log(lunar.el('#a').id); // a
Inserts an HTML code into the element or return the HTML content if the parameter is given:
let c = lunar.el('div').html();
console.log(c);
lunar.el('div').html('<button>click</button>');
It does the same as the html() method, but for text contents:
let c = lunar.el('div').text();
console.log(c);
lunar.el('div').text('<button>click</button>'); // See the difference from the html() method
This method returns the value of the given attribute and sets a new value if you pass it as a second parameter:
let div = lunar.el('div');
console.log(div.attr('class'));
div.attr('class', 'class1 class2');
console.log(div.attr('class'));
Returns true if the element has the given attribute.
It removes the given attribute from the element and returns the object itself.
Returns true if the current element matches with the given selectors. The argument is a string of valid CSS selectors.
It is a shortcut for the addEventListener() method:
lunar.el('div').event('click', e => {
console.log(e);
});
This property contains current width of the element.
The current height of the element.
It does the same effect as the lunar.toggle() to the current element.
It dows the same effect as the lunar.appear() to the current element.
This method calls the element.click() function, that fires the element's click event.
Returns the parent element of the current element. You can pass a number as a parameter informing the parent order of the element:
<section>
<div><button>Button</button></div>
</section>
<script>
let btn = lunar.el('button');
console.log(btn.parent(2)); // Object { pure: section }
</script>
The default value for the order is 1.
A property containing all the child nodes of the element.
A property with all the child elements of the current element.
It returns the first element node of the current element.
It returns the last element node of the element.
The first node inside the element.
The last node inside the element.
This method returns the sibling Node after or before the current element. This method gets a parameter called order, whose default value is 1. When the order parameter is 1, the method returns the current element. Values greater than one return the next nth sibling of the element, while negative values return the nth sibling before the element.
This method receives any number of elements (LunarElement or Node) to be added after the current element.
lunar.el('.list-title').after(item1, item2, ...);
It does the same as the after() method, but inserts the elements before the current element.
It is very similar to the after(), but the elements are inserted inside the current element and after its last child.
It does the same as the append() function, but the elements are inserted before the child node of the current element.
Works like the lunar.el() function, but searchs for an element inside the current one.
Works like the lunar.all() function, but searchs for elements inside the current one.
It verifies if the given element (Node or LunarElement) is inside the current one, returning true in the positive case and false in the case of not.
It returns true if the current element has that given class name.
It adds the given class name to the element.
It removes the given class name.
This method receives any number of parameters, each one with a class name. Then, all these class names are toggled. It is a very useful method.
This property returns a DOMTokenList collection of the class attributes of the element, and also can be used to manipulate the class list.
You can create a component easily. First of all, to register a component we need to pass an array containing all the components we have created, to call the lunar.register() method, which receives this array. Now, just create an array of components. A component is an object with two elements: an HTML, obviously, and a selector. See the example below:
<app-fruits></app-fruits>
<script>
let components = [
{
html: `
<div>
<div class="text-lg">Fruits</div>
<div class="examples">Mango, mango and mango.</div>
</div>
`,
selector: 'app-fruits'
},
...
];
lunar.register(components);
</script>
You can also pass classes to the component you are using throught its selector using the data-class attribute, but, have in mind that, if your component has more than one element side by side, only the first one will receive that classes.
<app-fruits data-class="p-6 mb-4 rounded-md"></app-fruits>
Note that you can call the lunar.register() more than one time, this does not have side effects.
Lunar has some very useful tools to hide or show an element. It has the following utilities:
- Toggle the state of an element;
- Hide an element after it losts the focus or a click outside it;
- Hide the element after moving the mouse to outside it;
- Programmatically change the element state.
Sometimes we need to show an element after a click on another element and hide it after another click. We need simply to add some attributes to this purpuse:
<div data-toggle=".text">Toggle element</div>
<div class="text" hideable>Hello :)</div>
The hideable atribute just tells that this element must start hidden, while the data-toggle tells which element will be controlled by this element. This way, a click on the first div will show the second one, another click will hide. You can also put the data-toggle in more than one element to controlls the same one. Note that the element, after visible, is made hidden after a click outside it and outside the element that calls it.
This may be a highly useful tool, sometimes. With it, a user can click on a button to show something, click on another and the first one will be hidden, because it simply lost the focus. Se the following example:
<div hideOnClickOutside>Don't click outside me! ๐พ</div>
We can combine both the two last utilities showed until now:
<div data-toggle=".text">Toggle element</div>
<div class="text" hideable hideOnClickOutside>Hello :)</div>
Sometimes is more useful to hide the element after the mouse goes away from it. For this case, there is the hideOnMouseLeave attribute:
<div hideOnMouseLeave>Don't forget me ๐ข</div>
As in the previous tool, we can combine it with the data-toggle to create a more dynamic functionality.
To do this is very simple. There is two methods, lunar.show() and lunar.hide(), to show an element and to hide an element, respectively. Both the methods receive an element, obviously. See the example:
<button onclick="lunar.hide(lunar.el('i'))">Hide it</button>
<button onclick="lunar.show(lunar.el('i'))">Show it</button>
<i>123</i>
<!-- LunarJs -->
<script src="dist/js/lunar.js"></script>
You can pass to these functions both common elements and an instance of the LunarHTMLElement class. Always you select an element using the lunar.el(), what you get is an instance of the LunarHTMLElement class, which has a set of very useful methods.
In this example, we have a button that shows the alert message after clicking on it. The alert message is a component that also contains another component inside it, an icon. There is two icons registered as components in this code, the first one comes from HeroIcons and the second one from Bootstrap Icons. In the HTML, the code is very clean, mainly when we need to reuse some component, like the icons of the example. Also, the alert component contains a span tag, which can close the alert if clicked due to the data-toggle attribute with a selector pointing to the alert itself.
<div class="btn-primary" data-toggle="#success-msg">
<icon-download data-class="h-10 w-10"></icon-download> Download
</div>
<app-alert></app-alert>
<!-- LunarJs -->
<script src="dist/js/lunar.js"></script>
<!-- Custom scripts -->
<script>
let components = [
{
html: `
<div class="alert" hideable id="success-msg">
<icon-exclamation data-class="h-10 w-10"></icon-exclamation> Downloading...
<span data-toggle="#success-msg">Close</span>
</div>`,
selector: 'app-alert'
},
{
html: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
</svg>`,
selector: 'icon-exclamation'
},
{
html: `<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-download" viewBox="0 0 16 16">
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
</svg>`,
selector: 'icon-download'
}
];
lunar.register(components);
</script>
Modifiers are a set of useful methods to work with the text content inside an element. It's like the Angular pipes, but poor. To use them, simply add the mod attribute to the element, containing the name of the modifiers as its value. For instance, to convert a timestamp to a date format, just add the mod attribute with the 'date' value:
<div mod="date">1663525210</div>
This code will show the date in the format d/m/Y.
There is only a few modifiers by default in this project. The current ones are:
Just use the value 'date' for the mod attribute, as in the above example.
Works like the date modifier, you pass a timestamp and it will return the hour in 24h format.
<div mod="hour">1663525210</div>
It converts a decimal number to a percent value, multiplying it by 100 and adding the % character. This modifier has a difference: we can pass an argument to it. To do this, just add a '|' (pipe) separanting the name of the mod and its params.
<div mod="percent">0.8923</div>
<div mod="percent|,">0.8923</div>
As you can see, the param is the character to take the place of the '.' in the value. It is just a detail, but it can make a difference between different languages.
A modifier is an object containing 2 elements: a name and an action. The 'name' is, obviously, the name of the modifier, that you pass to the mod attribute. The action is a function that receives the element calling the modifier, the text content and the params passed to this modifier, respectively, returning the result. See below an example of mod.
// The modifier
const mod = {
name: 'upper',
action(el, value, params) {
return value.toUppercase();
}
};
// Registering it
lunar.registerMods([mod]);
The HTML:
<div mod="upper">I'm not screaming!</div>
This is just a very simple example, you can make a lot of stuff with this tools, including working with the element containing that text, due to the fact that it is passed to the action. Note that, the params is an array, so, if you want to pass multiple params to that action, you need to separate each one by a pipe in the HTML.
Sometimes, maybe useful to add an anchor to an element that is no an a HTML tag, just for simplicity. For that cases, the link attribute can be useful, just pass the link you need inside it, removing the necessity to write the Javascript code to listen to a click and redirecting the page.
<div class="card" link="https://google.com/">
...
</div>