Raccoon JS

Intro

Raccoon is an experimental project inspired by Vue. It's a component based reactive framework with basic functionality such as, reactivity, template engine, computed functions, dom-events, etc.

Demo

Inputs

Price:1
Quantity 15
Tax 0.01
Sum (computed value: price * quantity)
Total (computed value: price * quantity * tax)

r-for - Raccoon for loop

    Fetch from external API

      Setup

      Install Raccoon [From where?], Import it use it.

      Get started

      Price1
      Quantity15
      Tax
      Sum (inc tax)
      <div id="demo-component-2">
        <button @click="proxy.price += 10">Increase price</button>
        <button @click="proxy.quantity += 1">Increase quantity</button>
        <button @click="funcs.addOnePercentTax()">Add 1% tax</button>
      
        <div>Price: {{price}}</div>
        <div>Quantity: {{quantity}}</div>
        <div>Tax: {{taxHuman}}</div>
        <div>Sum (inc tax): {{sum}}</div>
      </div>
      import { Raccoon } from "raccoon-js-framework";
      
      const componentEl = document.getElementById("demo-component-2");
      
      const { proxy, compute, funcs } = new Raccoon(componentEl);
      
      proxy.price = 1;
      proxy.quantity = 15;
      proxy.tax = 0.01;
      
      compute.sum = () => (proxy.price * proxy.quantity) + (100 * proxy.tax);
      compute.taxHuman = () => `${100 * proxy.tax}%`
      
      funcs.addOnePercentTax = () => proxy.tax += 0.01

      Template engine

      <div id="component">
        <div id="valueA">{{a}}</div>
        <div id="valueB">{{b}}</div>
        <div id="sum">{{sum}}</div> 
      </div>
      proxy.a = 1;
      proxy.b = 2;
      compute.sum = () => `$Sum: ${proxy.a + proxy.b}`;
                  
      Output
      1
      2
      Sum: 3

      Proxy

      {proxy.name} or {just name} The proxy does the heavy lifting when it comes to the reactivities. It's basically an object with properties that will update the dom every-time a property changes.

      proxy.a = 1;
      proxy.b = 2;
      <p>a = {proxy.a}, b = {proxy.b}</p>

      Output: a = 1, b = 2

      TODO: Explain when proxy.a is needed and when it's ok to use just a.

      Compute

      compute.sum = () => proxy.a + proxy.b;
      
      proxy.a = 12;
      proxy.b = 12;

      Output the computed sum

      The sum of a and b is: <strong>{sum}</strong>

      Sum in this case will be 24

      Funcs

      This creates a button which counts up proxy.a by one on click using a function.

      <div id="component">
        <button id="button" @click='funcs.myFn(proxy.a)'>Count Up</button>
      </div>

      The funcs-function takes 2 arguments, the proxy which is magically applied by the framework and the value which in this case is passed as an argument from the @click event. In this example we are getting the value from proxy.a and then increase it by one and save it back.

      proxy.a = 1;
      // The first argument will always be the proxy which is applied by the framework.
      funcs.myFn = (proxy, value) => {
        proxy.a = Number(value) + 1;
      }

      @click

      This could be used with funcs too

      <div id="component">
        <button id="button" @click='proxy.a += 10'>Count Up</button>
      </div>
      proxy.a = 0

      @input

      This could be used with funcs too

      <div id="component">
        <label for="price">Price</label>
        <input type="number" @input="proxy.price = Number(event.target.value)" :value="proxy.price">
      </div>
      proxy.price = 1;

      @change

      <div id="component">
        <label for="price">Price</label>
        <input type="number" @change="proxy.price = Number(event.target.value)" :value="proxy.price">
      </div>
      proxy.price = 1;

      @keyup

      <div id="component">
        <label for="price">Price</label>
        <input type="number" @keyup="proxy.price = Number(event.target.value)" :value="proxy.price">
      </div>
      proxy.price = 1;

      @keydown

      <div id="component">
        <label for="price">Price</label>
        <input type="number" @keydown="proxy.price = Number(event.target.value)" :value="proxy.price">
      </div>
      proxy.price = 1;

      r-for

      <div id="component">
        <div r-for="name of names">
          <article class="card">
            <span class="text-red">{{ name }}<span>
          </article>
        </div>
      </div>

      An inner wrapper is required for the rendering precess.

      <div id="component">
        <div r-for="name of names">
          <article class="card">
            <span class="text-red">{{ name }}<span>
          </article>
        </div>
      </div>

      Invalid r-for loop as inner wrapper is missing.

      <div id="component">
        <div r-for="name of names">
          {{ name }}
      </div>

      An r-for-key will be dynamically added to the markup which is required during the rendering process.

      proxy.names = ["Lisa", "Frank", "Steve"];

      Most methods of deleting does not trigger the proxy set event, in other words no reactivity. Using splice is the way to delete a value and still keep the reactivity. See examples.

      <div id="component">
        <button @click="proxy.names = proxy.names.slice(1, proxy.names.length)">Delete First</button>
        <button @click="proxy.names = proxy.names.slice(0, -1)">Delete Last</button>
      </div>

      Adding value works works with the spread operator.

      <div id="component">
        <button @click="proxy.names = [...proxy.names, 'Rick']">Add Rick</button>
      </div>

      r-model

      <div id="component">
        <input id="input-el" type="number" r-model="proxy.price">
      </div>

      prefix with proxy is optional, above r-model could be used like this.

      <div id="component">
        <input id="input-el" type="number" r-model="price">
      </div>
      proxy.price = 1;

      States/state-manager

      Multiple components

      Having multiple components works as expected, proxies, computes, functions all works withing the component scope. Just ensure they are uniquely named as example shows. In this example proxy.a in component-1 is not the same as proxy.a in component-2.

      <div id="component-1">
        <button id="button" @click='proxy.a += 10'>Count Up</button>
      </div>
      
      <div id="component-2">
        <button id="button" @click='proxy.a += 10'>Count Up</button>
      </div>
      const componentString1 = `<div id="component-1"><button id="button" @click='proxy.a += 10'>Count Up</button></div>`;
      const componentString2 = `<div id="component-2"><button id="button" @click='proxy.a += 10'>Count Up</button></div>`;
      
      const { appEl: appEl1, proxy: proxy1, compute: compute1 } = new Raccoon(document.getElementById(component-1));
      const { appEl: appEl2, proxy: proxy2, compute: compute2 } = new Raccoon(document.getElementById(component-2));
      
      proxy1.a = 1;
      proxy2.a = 2;
      
      compute1.withDollarSign = () => `$${proxy1.a}`;
      compute2.withDollarSign = () => `$${proxy2.a}`;