Code Monkey home page Code Monkey logo

lemonadejs / lemonadejs Goto Github PK

View Code? Open in Web Editor NEW
368.0 9.0 14.0 683 KB

LemonadeJS is a 7KB reactive JavaScript micro-library offering two-way data binding. It is dependency-free, does not require transpiling, and works with webpack or directly in the browser.

Home Page: https://lemonadejs.net

License: MIT License

JavaScript 93.96% HTML 6.04%
javascript two-way-data-binding reactive framework two-way-binding micro-framework micro-frameworks ui vanilla-js micro-library

lemonadejs's Introduction

LemonadeJS v4: Reactive micro library

Create amazing web-based interfaces with LemonadeJS

Micro Library

LemonadeJS is a super lightweight reactive vanilla javascript micro-library (7 KBytes). It helps to integrate the JavaScript (controllers) and the HTML (view). It supports two-way data binding and integrates natively with jSuites to help to create amazing interfaces quicker.

It would help you deliver reusable components and does not require transpiler, babel, or hundreds of other dependencies. It works just fine in any javascript dev environment. LemonadeJS has a quick learning curve, keeps coding fun, and is very close to native JS.

  • Make rich and user-friendly web interfaces and applications
  • Handle complicated data inputs with ease and convenience
  • Improve the software user experience
  • Create rich CRUDS and beautiful UI
  • Highly flexible and customizable
  • Lightweight and simple to use

Installation

NPM package

% npm install lemonadejs

Using from CDN

<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>

Create a LemonadeJS sample app

% npx @lemonadejs/create myApp<br>
% cd myApp<br>
% npm run start<br>

Running tests

% npm run test

Examples

Webpack

Build modern applications with lemonadeJS and node.

See this example on codesandbox

import lemonade from "lemonadejs";
import Hello from "./Hello";

export default function App() {
  let self = this;
  self.count = 1;
  return `<div>
        <div><Hello /></div>
        <p>You clicked {{self.count}} times</p>
        <button onclick="self.count++;">Click me</button>
  </div>`;
}

Browser

Simplicity to run in the browser without dependencies, servers, transpiler.

<html>
<body>
<div id="root"></div>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script>
function Hello() {
    let self = this;
    return `<h1>{{self.title}}</h1>`;
}

function App() {
    let self = this;
    self.count = 1;
    return `<>
      <Hello title="your title" />
      <p>You clicked {{self.count}} times</p>
      <button onclick="self.count++;">Click me</button>
    </>`;
}
lemonade.render(App, document.getElementById('root'));
</script>
</body>
</html>

Creating a table from an array of objects

import lemonade from "lemonadejs";

export default function Component() {
    let self = this;

    self.rows = [
        { title:'Google', description: 'The alpha search engine...' },
        { title:'Bing', description: 'The microsoft search engine...' },
        { title:'Duckduckgo', description: 'Privacy in the first place...' },
    ];

    // Custom components such as List should always be unique inside a real tag.
    return `<table cellpadding="6">
        <thead><tr><th>Title</th><th>Description</th></th></thead>
        <tbody @loop="self.rows">
            <tr><td>{{self.title}}</td><td>{{self.description}}</td></tr>
        </tbody>
    </table>`;
}

The event object

<html>
<body>
<div id='root'></div>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script>
function Component() {
    // Create the self object
    let self = this;
    self.test = function(e) {
        console.log(e);
        e.preventDefault();
    }
    // The property call is added to the observable list when added to the DOM
    return `<input type="button" value="Click test" onclick="self.test(e);"/>`;
}

// Render the LemonadeJS element into the DOM
lemonade.render(Component, document.getElementById('root'));
</script>
</body>
</html>

Enable/disable HTML elements

<html>
<body>
<div id='root'></div>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script>
function App() {
    let self = this;
    self.disabled = false;
    return `<>
      <button onclick="self.disabled = !self.disabled">Toggle</button>
      <input type="text" disabled="{{self.disabled}}" />
    </>`;
}
lemonade.render(App, document.getElementById('root'));
</script>
</body>
</html>

Reactive Web Components

<hello-element title="Hello world" />
class HelloElement extends HTMLElement {
    constructor() {
        super();
    }
 
    render() {
        let self = this;
        return `<>
            <h1>{{self.title}}</h1>
            <input type="button" value="setTitle()"
                onclick="self.title = 'Test'" />
        </>`;
    }
 
    connectedCallback() {
        if (! this.el) {
            lemonade.render(this.render, this, this);
        }
    }
}
 
window.customElements.define('hello-element', HelloElement);

License

This software is free to use, and it is distributed under the MIT license.

Learning LemonadeJS

Documentation

Utilities

Libraries

Examples

Other tools

lemonadejs's People

Contributors

dym-sh avatar frontendneo avatar gbonnaire avatar hodeware avatar igorkamyshev avatar lucianoiam avatar nicolasjesse avatar pphod avatar ruan-miguel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lemonadejs's Issues

Info: How to do if?

Hi,

How to do conditional rendering within the lemonade template?

Use Cases

  • Enable/Disable input element based on a condition
  • Show/hide block based on a condition
  • html elements' class name based on a condition

Thanks in advance.

Bind input

hi, i want to do the following:

<input id ="click" type="text" onkeydown="self.search()" @Bind="self.searchParam" />

but I have to press another key for it to take what I wrote, as if there were a delay, how could I solve it? I did not find an answer in the documentation
for example, i write aaa --> self.searchParam = aa, if i press another key --> aaa

Bug - Components doesn't show on the site

Context

On the website, there are some examples of use.
It contains code preview and the result to the right.

When getting on the site, results are not working.

Here are some screenshots :
-> Counter aren't show as they should

image

image

Architecture

Operating System : Kubuntu 20.04 KDE Plasma 5.18.8
Browser :

  • Firefox 97.0.2
  • Extensions : Ghostery, uBlock Origin 1.41.8, https://Everywhere Version: 2021.7.13,

Website issue with CDN rawgit!

Hi,

I found the issue with website which used a CDN server (rawgit)

CDN URL: run_prettify.js @ cdn.rawgit.com
is effectively gets redirected to
CDN URL: run_prettify.js @ cdn.jsdelivr.net

The redirect is working fine in some networks. but unfortunately it does NOT happen in other networks!

Could you please replace cdn.rawgit to cdn.jsdelivr

NOTE: Without this change you will miss out many users who won't be able to read the code and leave the site. Most of them won't take a pain of fixing the site to see what's inside!

You have done a great work, don't miss out a large community due to CDN issue!

Thanks

lemonade.apply doesn't have ext parameter

I'm writing a simple test code like the following,

<html>
<body> 

<div id='root'>
  <Hello name="Shady">
    <div style="color:red">{{self.greetings}} {{self.name}}!</div>
  </Hello>
</div>
 
<script type="module">
  import lemonade from "/lemonade.js";
  import Hello from "/Hello.js";

  lemonade.apply(document.getElementById('root'), {}, {Hello});
</script>
</body>
</html>

Hello.js:

import lemonade from "/lemonade.js";

function Hello(template){
  let self=this;
  self.greetings = "Hello,";
  return lemonade.element(template,self);
}

export default Hello;

It seems that lemonade.apply doesn't have the third parameter to provide additional dependent component. Is it a limitation?

Regards
Shady

Improving the syntax (inspiring from reactjs)

This is more a question/sujestion from the react side of devs to make the switch easier. Could we have something like this:

class HelloElement extends CustomLemonadeHTML {
    static title = "";
    constructor() {
        super("hello-element");
    }
 
    render() {
        return (
             <>
                 <h1>title: {this.title}</h1>
                 <input type="button" value="setTitle" onclick={() => this.title = "test"} />
             </>
        );
    }
}

This obviously psudo code, but its clean syntax that would work well for react devs, like myself, to switch from react -> web components.

Cannot use the import statement for loading LemonadeJS

import './lemonade.js' results in cryptic errors which vary across browsers. For example on Safari:

lemonade.js:18 Uncaught TypeError: Cannot set properties of undefined (setting 'lemonade')

I worked around this by creating a small loader module but I think it would be nice to be able to just write import './lemonade.js'

lemonade-loader.js

await loadScript('lemonade.js');

async function loadScript(src) {
    return new Promise((resolve, reject) => {
        const el = document.createElement('script');
        el.src = src;
        el.onerror = reject;
        el.onload = _ => resolve(el);
        document.head.appendChild(el);
    });
}

app.js

import './lemonade-loader.js';

Do you have an example of lemonade.apply?

Working on a little application and Lemonade.js sounds like a perfect match. I have a main area with shows a list of input fields based on self.

Is it possible to modify the self from a separate function? I was thinking about bidding the self scope to an existing DOMElement already in the DOM. I would like to use external functions to modify the self and render when self has changed. Is this possible?

bind & loop problem

I try to use @bind & @loop. i share the code https://codesandbox.io/s/quirky-lake-l7ed9w?file=/src/App.js

let self = {
   ...
    a: "Alberto",
    b: {
      name: "John",
      age: 26,
      email: "[email protected]"
    },
    c: [
      {
        name: "Albert",
        age: 38,
        email: "[email protected]"
      }
    ],
    d: [
      {
        name: "Albert",
        age: 38,
        email: "[email protected]"
      },
      {
        name: "Alberto",
        age: 26,
        email: "[email protected]"
      },
      {
        name: "Sylvain",
        age: 57,
        email: "[email protected]"
      }
    ]
  };

if i use @Bind for self.a or self.d this work , but not just simple object as self.b . I try different test with self.c but same problem.
For @loop, see the code, if i use two time , the first loop appears in second loop.
Capture

Render/Load/Mount a specific component on App's main container on menu clicks

I'm trying to render/load/mount (whatever to be called) different components on a menu button click. The lemonade.render appends the component inside the container.

  • What is your recommendation to replace instead of append?

Thanks in advance.

Code:

<html>

<body>

  <div id='container-menu'></div>
  <div id="container-main"></div>

  <script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
  <script>

    var compA = function () {
      let self = {
        cName: 'Component A'
      };

      let template = `<div style="color:green;">
        <h2>{{self.cName}}</h2>
        <p>This is {{self.cName}} content. Assume it's totally a new content loaded onClick of Menu Button A</p>
      </div>`;

      return lemonade.element(template, self);
    };

    var compB = function () {
      let self = {
        cName: 'Component B'
      };

      let template = `<div style="color:red;">
        <h2>{{self.cName}}</h2>
        <p>This is {{self.cName}} content. Assume it's totally a new content loaded onClick of Menu Button B</p>
      </div>`;

      return lemonade.element(template, self);
    };

    var Menu = (function () {
      let self = {};
      self.disabled = false;

      self.loadA = function () {
        lemonade.render(compA, document.getElementById('container-main'));
      };

      self.loadB = function () {
        lemonade.render(compB, document.getElementById('container-main'));
      };

      let template = `<>
                <button onclick="self.loadA()">Component A</button>
                <button onclick="self.loadB()">Component B</button>
                <hr>
                </>`;
      return lemonade.element(template, self);
    });

    lemonade.render(Menu, document.getElementById('container-menu'));
  </script>
</body>

</html>

image

I was expecting the lemonade.render(Component, Element) to replace/mount on Element.

Initial state for function vs class based components

Likely not an issue but my incomplete understanding. By the way is there a better place for asking questions?

I am trying to implement custom (LemonadeJS) components as ES6 classes, and realized attributes are not being read from the tag. Take this example from a parent component markup:

<Hello title="The very best of 2022" />

This renders fine (copied from one of the library examples):

function Hello() {
    // Get the attributes from the tag
    let self = this;
 
    // Title and year are declared in the parent template
    let template = `<h1>{{self.title}} {{self.year}}</h1>`;
 
    return lemonade.element(template, self);
}

This does not (self.title renders empty):

class Hello extends lemonade.component {

    render() {
        return lemonade.element(`<h1>{{self.title}} {{self.year}}</h1>`, this);
    }

}

Except from the initial value the parent tries to set, everything seems to work fine with the class-based Hello component.

How To: strategy to keep the components in their respective js files...

Hi, what is the strategy to keep the components in their respective js files and include/import them dynamically on need basis? without a transpiler, babel or any other dependencies to just work in the browser. Please add some examples. It's not viable/scalable to define all components' functions/classes in a single file as shown in the current examples.

I just loved the concept that I don't have to use node or babel or any other build tools. I just wanted to use the plain old way to build apps using simple HTML, CSS with simple script tags and write vanilla js.

Thanks in advance.

Could Lemonadejs consist of expressions checking?

Thank you for this library.

There are a few ways to deal with if as mentioned in another issue.
But I found them pretty tedious.

It would give a greater flexibility if the library could offer something like svelte does for expression checking at least for if statement.
https://svelte.dev/docs#template-syntax-if

{#if self.temperature > 100}
	<p>too hot! {{self.temperature}}</p>
{:else if 60 > self.temperature}
	<p class="{#if self.temperature > 20}colder{/if}">too cold!</p>
{:else}
	<p class="calm">{{self.calm}}</p>
{/if}
<span class="{#self.allowed ? 'visible' : 'invisible'}">{{self.awesome}}</span>

Loop

How to display Names and Emails in different lists?

<!DOCTYPE html>
<html lang="en">

<head>
  <title>Loops</title>
</head>

<body>

  <main id="app"></main>

  <script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>

  <script>
    function App() {

      let self = {

        members: [{
            name: "Albert",
            email: "[email protected]",
            age: 38
          },
          {
            name: "Alberto",
            email: "[email protected]",
            age: 26
          },
          {
            name: "Sylvain",
            email: "[email protected]",
            age: 57
          }
        ]

      };

      let template = `<>
          <h2>Loops</h2>

          <div>
            <h5>Names</h5>
            <ul @loop="self.members">
              <li> {{self.name}} </li>
            </ul>
          </div>

          <hr />

          <div>
            <h5>Emails</h5>
            <ul @loop="self.members">
              <li> {{self.email}} </li>
            </ul>
          </div>

        </>`;

      return lemonade.element(template, self);
    }


    lemonade.render(App, document.querySelector('#app'));
  </script>

</body>

</html>

Expected:
image

But:
image

How to get the expected result?

contact not reachable

The contact details on package.json is not reachable!

I would like to share few things with the dev / documentation team. Unable to reach

image

image

from the site
image

but no response!

Failed to refresh in nested UI object

Hi,

I found that refresh in first level json object is possible (see "Del page" link), but unable to refresh second level json object (see "Add field" link), is there any wrong thing I did? Many thanks.

<script src="https://lemonadejs.net/v2/lemonade.js"></script>
<script> const Component = function() { var self = {};
self.addPage = function() {
    self.data.push({ title: self.text });
    self.refresh('data');
    self.text = '';
	console.log(JSON.stringify(self.data));
}

self.data = {

"name":"Item 1",
"number":1,
"pages":[
{
"id":"8ca78c36-8373-4f23-94ba-111",
"fields":[

     ]
  },
  {
     "id":"8ca78c36-8373-4f23-94ba-222",
     "fields":[
        {
           "fieldid":"66ef0525-16f1-48e7-8ad1-29c8c6c62c72"
        }
     ]
  },
  {
     "id":"8ca78c36-8373-4f23-94ba-333",
     "fields":[
        {
           "fieldid":"91b0dbf1-df29-4a9e-b22d-9a392f91a32c"
        }
     ]
  }

]
};

self.addpage = function() {
	
    self.data.pages.push({ id: "newId"});
    self.refresh('data');
    self.text = '';
}


 self.addfield = function(s) {
	for (var i=0; i<self.data.pages.length; i++)
	{
		if (self.data.pages[i].id == s.id)
		{
			self.data.pages[i].fields.push({ id: "newId"});
			break;
		}
	}
	self.refresh('data');
}


self.delpage = function(s) {
    self.data.pages.splice(self.data.pages.indexOf(s), 1);
    self.refresh('data');
	console.log(JSON.stringify(self.data));
}

var template = `<>
        <ul @loop="self.data.pages">
            <li>
                <i>{{self.id}}</i>
				<span onclick="self.parent.delpage(self)" style="cursor: pointer;">(Del page)</span>
				<span onclick="self.parent.addfield(self)" style="cursor: pointer;">(Add field)</span>
					<ul @loop="self.fields">
						<li>
		                    <i>{{self.fieldid}}</i>
							q
						</li>
					</ul>
			</li>
        </ul>
        <input type="text" @bind="self.text" />
        <input type="button" value="Add Page" onclick="self.addpage()" />
        <input type="button" value="Debug" onclick="self.debug()" />
    </>`;

return lemonade.element(template, self);

}
lemonade.render(Component, document.getElementById('root'));
</script>

Unable to access parent.properties inside child template!

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="icon" href="data:image/x-icon;base64,">
  <title>issue: self.parent.property</title>
</head>

<body>
  <h1>Testing self.parent.property inside child template</h1>

  <div id="root"></div>

  <script src="https://cdn.jsdelivr.net/npm/lemonadejs@latest/dist/lemonade.js"></script>
  <script>

    function Hello() {

      let self = this;

      console.log('๐Ÿš€ ~ Inside Child Hello', {self});

      let template = `<>
        <h1>{{self.title}}</h1>
        <h2>Name: {{self.parent.name}} | Email: {{self.parent.email}}</h2>
      </>`;

      return lemonade.element(template, self);
    }


    function App() {

      let self = {
        count:1,
        name: 'Root',
        email:'[email protected]'
      };

      self.count = 1;

      let template = `<>
                    <Hello title="your title" />
                    <p>You clicked {{self.count}} times</p>
                    <button onclick="self.count++;">Click me</button>
                </>`;

      return lemonade.element(template, self, {Hello});
    }

    lemonade.render(App, document.getElementById('root'));
  </script>
</body>

</html>

Error:
image

Expected Output:
image

Incorrect lemonade.d.ts types

component class is not included in types

lemonade.component     //Error: Property 'component' does not exist on type 'typeof lemonade'.

render function returns (Element?) object, not void

function render(component: Function, root: HTMLElement, self?: Object) : void;

can we also include the self (proxy) object (since there is a 'refresh' function injected into it)

export type SelfProxy<T> = T & {
    onchange: (property: string, changed:[], instance) => void;
    refresh: () => void;
}

Render Field as HTML

Is there a way to render markup as is (HTML) from the specific property of self object?

Use case:
The API returns HTML in one of the JSON object's property.
However, Lemonadejs renders it as the inner text.
I would like it to render the HTML as is.

Through I can manually update it later on using Vanilla JS code but that doesn't seem handy enough as well as tedious to manipulate with update or other events.

{
      "name": "Eternal Flame",
      "age": 25,
      "secret": "Unknown",
      "about": "<h3>About section</h3><span class="\date-time\">17 May 2023<span>"
}

I would like about field to render as HTML rather as text.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.