Skip to content

linusified/eel

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@linusified/Eel

A rebooted version from elemxx. Licensed under MIT, and published to both npm and JSR.

portable webcomponent on the go, heavily inspired by Lit. Eel doesn't use shadow dom, directly exposing HTMLElement class

...and some reactive objects!

Comparison

The difference? (comparing to Lit)

  • Since eel dosen't use shadow dom, the static Eel.css object has :me directive for CSS styling

Or, you could even use your own styling processor, and replace your own directive with Eel.localName!

  • Eel uses event-based object (without using EventEmitter) to make it reactive. Like; getter and setters, and simple array of listening event functions. We do have cleanup for those events
Example
// define & extend your class from Eel
...
// add the Track object here
areYouOk = this.track("yes, i am!")
...
// so, whenever you want to print to console 
// everytime it was changed, you can do this
this.areYouOk.watch((v)=>{console.log(v)})
// alternatively, you can access the value directly
console.log(this.areYouOk.value)

// now, you can change it like this. 
// It would trigger the events that we put earlier
this.areYouOk.value = "no, im not 😭"
...
// close the class

Consider looking the Track interface type in ./index.ts

  • Your element is ok! Dosen't really isolated from HTMLElement bindings

  • These are your replacements from default Webcomponent function! (no need for super[function here]())

    • Instead of connectedCallback, you can use onMount
    • Instead of disconnectedCallback, you can use onUnmount
    • Instead of (string[]) observedAttributes, you can use attrList
    • Instead of attributeChangedCallback, you can use ({[key:string]:Track<string|null>}) attrs and listen using attrs["<attribute name>"].watch((value)=>{})
    • Instead of (boolean) isConnected, you can use (boolean) mounted. It would be changed during mount changes

Setup

This package are universal! Supports JSR (for deno and bun), npm, and even browser!

Setup npm

Just run this for Node, nothing else and ready to be used!

npm install @linusified/eel

If your Deno/Bun project was using some of the nodejs frameworks like vite, svelte, etc, you need to use these commands below.

# for deno
deno install npm:@linusified/eel

# for bun
bun add @linusified/eel

Setup JSR

(If you're using Node, skip this step and go to the (Setup npm)[###setup-npm] section instead)

Open https://jsr.io/@linusified/isjsruntime, then see on the right pane and pick on your environment. It will show the commands, so you can run and install it asap!

JSR also supports pnpm, yarn, deno, and even bun!

Setup Browser

Since this package are using ESM, download the index.js file and use it straight away! Don't forget to set your <script> type as a "module" when using it

(UMD isn't supported for now)

For example:

  • On script.js
import * as js from "/path/to/eel/index.js";
  • On your index.html
...
<script src="/path/to/script.js" type="module"></script>
...

Javascript Examples

Typing animation

Details
// replace /path/to/eel/index.js with the imported eel module
import { Eel } from "/path/to/eel/index.js";
// define simple sleep function, instead of nesting in setTimeout
const sleep = (ms)=>new Promise((res, _)=>setTimeout(res, ms))
// make the new typing elem
class TypingAnim extends Eel {
    // this would listen to the arguments from the Eel element, then placed it under `this.attrs.sentence`
    static attrList = ["sentence"]
    // track the text
    sentence = ""
    // this is a reactive variable. Used the same parts as `static attrList` but its purpose to just do it locally. Also it can be placed whenever you like
    _text = this.track("")
    // this will stop the animation if set otherwise
    _anim = this.track(true)
    // the css style
    static css = `
        @keyframes blinkTextCursor {
            from{color: black;}
            to{color: transparent;}
        } 
        // the :me is a special directive to reference itself
        // since eel dosent use shadow dom
        :me {
            display:flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
        }
        :me > h2 {
            text-shadow: gray 0px 1px 1px;
        } 
        :me > h2 > span.cursor {
            animation: blinkTextCursor 0.8s steps(44) infinite normal;  
        }
    `;
    constructor() {
        super();
        // since `this.attrs` dosent cleaned when unmounted, we put them in here
        // so it dosent re-trigger on mount changes

        // by the way, this only listen. Dosent trigger once
        this.attrs.sentence.watch((value)=>{
            // if elem isnt mounted, return
            if (!this.mounted) return
            // we need to require users to use the `sentence` attribute
            if (value===null) throw new TypeError("attribute `sentence` is required")
            // set to our own sentence object
            this.sentence = value
        })
    }
    render() {
        // create new header text
        const h2 = document.createElement("h2")
        // create new span (for text)
        const animText = document.createElement("span")
        // add to header
        h2.appendChild(animText)

        // create new span (for cursor)
        const cursor = document.createElement("span")
        // add cursor class so the CSS pick up
        cursor.classList.add("cursor")
        cursor.innerText = "|"

        // add to header
        h2.appendChild(cursor)
        // set innerText once, and listen to set innerText again
        // (just like doWhile without looping)
        this._text.observe((text) => {
            animText.innerText = text
        })

        const button = document.createElement("button")
        button.addEventListener("click", () => this._toggle())

        // set the button text and trigger animation if true, and listen to set that again
        // (just like doWhile without looping)
        this._anim.observe((v) => {
            button.innerText = v ? "Stop animation" : "Play animation"
            if (v) this._triggerAnim()
        })

        // add header to :me
        this.appendChild(h2)
        // add button to :me
        this.appendChild(button)
    }
    // if mounted, render the element
    onMount () {
        this.render()
    }
    // if unmounted, stop the animation
    onUnmount () {
        this._anim.value = false
    }
    // trigger animation
    async _triggerAnim() {
        while (this._anim.value) {
            const sentence = this.sentence
            await sleep(2000)
            for (let i = 0; i <= sentence.length; i++) {
                this._text.value = sentence.substring(0, i)
                await sleep(150)
                if (!this._anim.value) return
            }
            await sleep(3000)
            if (!this._anim) return
            for (let i = sentence.length; i >= 0; i--) {
                this._text.value = sentence.substring(0, i)
                await sleep(50)
                if (!this._anim.value) return
            }
            await sleep(500)
            if (!this._anim.value) return
        }
    }
    // toggle the animation to play/stop
    _toggle() {
        this._anim.value = !this._anim.value
    }
};
customElements.define("typing-anim", TypingAnim)
HTML
<html>
  <head>
    <!-- add the script -->
    <script type="module" src="/path/to/script.js"></script>
  </head>
  <body>
    <!-- add our custom elem -->
    <typing-anim sentence="Hello World"></typing-anim>
  </body>
</html>

Typescript examples

Simple text (with decorators)

Details
import { Eel } from "@linusified/eel";
import { css, define } from "@linusified/eel/decorators";

@define("simple-hello")
@css(`
    :me {
        background-color:#FFEBCD;
        padding: 10px;
        border-radius: 10px;
        font-size: larger;
    }
`)
export class SimpleHello extends Eel {
    override onMount (){
        // create new span
        const span = document.createElement("span")
        // add text to span
        span.innerText = "hello everyone!"
        // add span to :me
        this.appendChild(span)
    }
}

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors