Git Product home page Git Product logo

radi's Introduction

Radi

Radi is a tiny javascript framework.

It's built quite differently from any other framework. It doesn't use any kind of diffing algorithm nor virtual dom which makes it really fast.

With Radi you can create any kind of single-page applications or more complex applications.

npm version npm downloads gzip bundle size discord

Installation

To install the stable version:

npm install --save radi

This assumes you are using npm as your package manager.

If you're not, you can access these files on unpkg, download them, or point your package manager to them.

Browser Compatibility

Radi.js currently is compatible with browsers that support at least ES5.

Ecosystem

Project Status Description
radi-router radi-router-status Single-page application routing
radi-fetch radi-fetch-status HTTP client for Radi.js

Documentation

Getting started guide

Here are just a few examples to work our appetite.

Hello World example

Lets create component using JSX, tho it's not mandatory we can just use hyperscript r('h1', 'Hello', this.sample, '!'). I'm using JSX for html familiarity and to showcase compatibility.

/** @jsx Radi.r **/

class Hello extends Radi.component {
  state() {
    return { sample: 'World' };
  }
  view() {
    return (
      <h1>Hello { this.state.sample } !</h1>
    )
  }
}

Radi.mount(<Hello />, document.body);

This example will mount h1 to body like so <body><h1>Hello World</h1></body>

Counter example (With Single File Component syntax)

This will be different as we'll need to update state and use actions. Only actions can change state and trigger changes in DOM. Also we'll be using our SFC syntax for *.radi files

Counter.radi

class {
  state = {
    count: 0
  }

  @action up() {
    return {
      count: this.state.count +1
    }
  }

  @action down() {
    return {
      count: this.state.count -1
    }
  }
}

<div>
  <h1>{ this.state.count }</h1>

  <button onclick={ () => this.down() } disabled={ this.state.count <= 0 }>-</button>

  <button onclick={ () => this.up() }>+</button>
</div>

Architecture

Radi fully renders page only once initially. After that listeners take control. They can listen for state changes in any Radi component. When change in state is caught, listener then re-renders only that part.

Other frameworks silently re-renders whole page over and over again, then apply changes. But radi only re-renders parts that link to changed state values.

To check out live repl and docs, visit radi.js.org.

Stay In Touch

License

MIT

Copyright (c) 2017-present, Marcis (Marcisbee) Bergmanis

radi's People

Contributors

colshacol avatar jpitchardu avatar marcisbee avatar rafaelklaessen 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  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

radi's Issues

Example showing list/table updates

After reading the docs I still have difficulties understanding how to use radi in practice. In particular, what about more complex updates that do not just affect a single element? For example rearraginging/modifying/removing/adding children to a node. It would help to see e.g. an example of mapping a 2d array of values to a <table>.

Bundler Discussion

I am creating this issue to progressively discuss and constructively debate on which module bundler should be used for Radi.

The two tools current in discussion are Webpack and Rollup.

This discussion assumes that a module bundler is necessary and that an externally sourced distributable is a desired feature for this application.

Proposal for changes to API

This is a proposal for changes to the user facing API of Radi.
It's just a proposal, take it with a grain of salt.

  • Add support for extending Radi's Component class rather than only being able to use component, eg:
class Counter extends Radi.Component {
  view() {
    return <h1>Foo</h1>
  }
}
  • Reuse the npm prop-types package to provide a type checking system for props.
  • Add a map callback to Listener, which would be a shortcut for mapping arrays, but it would also enable for more efficient list rendering in the future.
  • Separate actions, mixins, props and state. So not this.foo but this.state.foo.
  • Provide shortcuts to the l function. Pass a state and props function to view so that you can do state('foo') rather than l(component, 'state', 'foo'). Only makes sense if the point above is applied.

Let me know what you think! πŸ˜„

Add tests

Hi!

I've forked @colshacol 's fork and I'm currently adding tests to it.
With these tests refactoring would be easier.

Uncaught TypeError: this.addNonEnumerableProperties is not a function

why get errror from code this .

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Radi.js - SinWave</title>
  <script src="https://unpkg.com/radi@latest/dist/radi.min.js"></script>
  <style>
    .animated-sin-wave {
      position: relative;
      height: 150px;
      width: 100%;
      overflow: hidden;
    }

    .animated-sin-wave>.bar {
      position: absolute;
      height: 100%;
      border-radius: 50%;
      max-width: 10px;
      transform: scale(0.8, .5) translateY(0%) rotate(0deg)
    }

    .animated-sin-wave-description {
      width: 100%;
      text-align: center;
      font-size: 0.8em;
      color: #747678;
      padding: 2em;
    }

    body {
      margin: 0;
      padding: 0;
    }
  </style>
</head>

<body>
  <div id="app"></div>
  
  <script>
    const { r, Component, list, link, mount } = Radi;

    const sinWave = Component({
      name: 'sin-wave',
      view: function () {
        return r('div',
          { class: 'animated-sin-wave' },
          list(l(this.bars), function (bar, i) {
            return r('div',
              {
                class: 'bar',
                style: {
                  width: this.barWidth + '%',
                  left: (this.barWidth * i) + '%',
                  transform: l('scale(0.8,.5) translateY(' + bar.translateY + '%) rotate(' + bar.rotation + 'deg)'),
                  // transform: ll(function() {return 'scale(0.8,.5) translateY(' + bar.translateY + '%) rotate(' + bar.rotation + 'deg)';}, [[bar, 'rotation']], 'bar.rotation'),
                  // transform: l(this.rotation),
                  // transform: l(bar.transform),
                  backgroundColor: l(bar.color)
                }
              }
            );
          })
        );
      },
      state: {
        barWidth: 1,
        barCount: 100,
        active: false,
        count: 0,
        step: 0.5,
        translateY: 0,
        rotation: 0,
        bars: []
      },
      actions: {
        onMount() {
          console.log('Mounted');

          this.active = true;
          this.bars = this.getColors();

          this.nextFrame();
        },
        onDestroy() {
          console.log('Destroyed');

          this.active = false;
          this.bars.splice(0, this.bars.length);
        },
        getColors() {
          var arr = [];
          for (var i = 0; i < this.barCount; i++) {
            var hue = (360 / this.barCount * i - this.count) % 360;
            arr.push({
              id: i,
              color: 'hsl(' + hue + ',95%,55%)',
              translateY: Math.sin(this.count / 10 + i / 5) * 100 * .5,
              rotation: (this.count + i) % 360,
            });
          }
          return arr;
        },
        nextFrame() {
          if (this.active) {
            this.count += this.step;

            this.bars = this.getColors();

            window.requestAnimationFrame(() => {
              this.nextFrame();
            });
          }
        }
      }
    });

    mount(r('div',
      new sinWave(),
      // new sinWave(),
      // new sinWave(),
    ), 'app');
  </script>
</body>

</html>

Would like to become a dev

Hi, i have read your article in Medium, and i really like the idea which you are implementing. I would like to become one of the developers in the radi-js. I have thought about migrating the development source in to typescript and compile them to the browser implementation this way the project would be cleaner and better to maintain you can get some ideas from my repos https://github.com/ClusterWS/ClusterWS. If you are interested in this kinda approach just let me know. I would really like to help you in development of your library and may be whole ecosystem )

Feature request: SFC similar to Vuejs or Marko.js

Great work. I liked the approach of completely missing out the VDom. I have been working with VueJs and keeping an eye on MarkoJs. VueJs has very good over all development ecosystem components in its repo. MarkoJs too has a very good approach on SFC. So, I request that SFC feature be considered to be included in Radi.js.

Improvement on clone function

Currently, I was interested on looking the implementation of RadiJS and I saw an opportunity to contribute by pointing out an small enhancement.

Why do you create a helper if you use Object.prototype.toString.call(obj) === "[object Array]" on line 25 instead of isArray function that you use few lines above?

JS Frameworks Benchmark submission?

Whenever I see library that makes bold performance claims I always like to point them this way. Not only is this benchmark a great comparison, it's a great tuning tool. The more strong performing libraries better the community. Many of the fastest libraries have made submissions the past few months and it really illustrates the tradeoffs of different approaches.

https://github.com/krausest/js-framework-benchmark

Suggestion to improve the view parser

The way you parse the views currently is quite buggy, it can fail if comments or parens are present in strings.

A better strategy would be to use a single regex that matches strings, comments and the /l\s*(/, and ignore the strings/comment matches.

Something like this would do:

L_MATCHER = /((?:^|[^\w\d$])l\s*\()|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/|\/\/.*?\n/g

function findL(str, i) {
  var match;
  L_MATCHER.lastIndex = i;
  while (match = L_MATCHER.exec(str)) if(match[1] != null) {
    return match.index + match[1].length;
  }
}

You could also use a similar strategy to match parentheses.

PAREN_MATCHER = /[()]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/|\/\/.*?\n/g
then ignore cases where match[0] is not a parenthesis.

Projects using Radi.js

Ask, those components that you have done some projects of the clients that includes some examples of web sites, web applications, UI design/developer, complex tutorials, etc.. Now, I see that you have an example of counting.

I look forward to your comments, greetings.

Eucario

Feature Request: Custom Attributes

I don't think that this exists even in React as of now. But I found this feature really helpful in making custom functions for my application.

  • The basic idea here is that we can pass in a custom attribute to the element that is linked to a global function or local function that defines functionality of the attribute

Example

  • <img src="Someting.jpg" />. In case this fails you need to add a onError handler to manage the error state. Moreover these functions needs to be defined throughout the application and adding more functionality in such cases is difficult.
  • So the custom attribute will do something like <img source="Someting.jpg" />. The source attribute is technically a function that takes the value and a reference to the element.
  • Just custom functionality can be added more easily

In case I was unable to explain it properly Aurelia.JS has it and its pretty useful. https://aurelia.io/docs/templating/custom-attributes#introduction

Codebase Walkthrough

I'll admit, I've been a functional Nazi for a little over 2 years now, so I don't even possess the skills necessary to understand this code. I've put it together a little bit in my head by debugging the refactor branch, but as a whole I still can not follow it.

I'd like to discuss setting up a screen-share for somebody to give a tour of Radi's code and how it works. I think it would greatly help me, and I also can not be the only one experiencing difficulty.

how? radi in single file component

looking at the doc, you get radi in sinle file compoenent.

I wonder how you could achieve that? package.json? webpack config?

Thank you in adavance.

Strange code in fusedom.fuse()

in fusedom I see the following lines of code at line 151:

		if ((nt1 === 1 || nt2 === 1)
			&& (toNode.tagName !== fromNode.tagName)
			|| (toNode.__async || fromNode.__async)
			|| (toNode.listeners && toNode.listeners.length || fromNode.listeners && fromNode.listeners.length)) {
			if (toNode.parentNode) {
				toNode.parentNode.insertBefore(fromNode, toNode);
				destroy(toNode);
				return fromNode;
			} else {
				toNode.replaceWith(fromNode);
				destroy(toNode);
				return fromNode;
			}
		}

Wouldn't the toNode always have a parentNode and therefore never go in the else block? Besides that, the 2 blocks seem to do the same thing.

Could you give some explanation for this approach?

Thanks in advance.

User facing API breaking changes

Backstory

In the beginning of this project I wanted to build framework, that is super fast (to skip virtual dom and implement listeners as re-rendering engine) and to use it for my own development purposes. So in the beginning I wanted it to be functional...

Then the unexpected popularity and refactor came, when it kinda fell into OOP all the way. With that came somewhat unexpected bumps and listeners became kinda hard to work with. In my head I always tried to figure out what is the way I want v1 to be and I think I finally figured it out.

Functional all the way

This means different user facing API and different way of thinking about writing components. Code got even smaller and faster this way too.

So the idea is that, components no longer have their own state, instead we now got Store. We can create as many stores as we want, even for every component, if so pleased. And only one way of rendering DOM with functional stateless components.

Example

1. just rendering dom
const { h, mount } = Radi

function Hello() {
  return (
    <h1>Hello World !</h1>
  )
}

mount(<Hello/>, document.body)
2. rendering dom with variable data
const { h, mount, Store } = RadiExperiment

/* Firstly we define state */
const state = new Store({
  count: 0
})

/* Then we define some actions that will change state */
const up = ({count}, by) => ({count: count + by})
const down = ({count}, by) => ({count: count - by})

/* Then comes pure function component */
function Counter() {
  return (
    <div>
      <h1>{ state.out(s => s.count) }</h1>
      <button onclick={ () => state.dispatch(up, 1) }>Up</button>
      <button onclick={ () => state.dispatch(down, 1) }>Down</button>
    </div>
  )
}

/* Finally we mount it */
mount(<Counter/>, document.body)

None of the lifecycles suffer either.

function App() {
  this.onMount = () => {}
  this.onDestroy = () => {}

  return <Person/>
}
// or we can set prototypes too
App.onMount = () => {}
App.onDestroy = () => {}

More about Store

With new Store we can do some interesting things. Like getting data from API by just defining schema of response data. So we can introduce Fetch that gathers data from api and directly injects them in Store.

Also why not inject one Store into another one (sync them both or lock injected).

// First we define Schema for our data from API
const User = new Fetch.get(
  '/users/:id',
  (data) => ({
    id: data._key,
    firstname: data.firstname,
    lastname: data.lastname,
  })
)

// Then we create store where we include our User Schema
const userStore = new Store({
  foo: 'bar',
  user: User({id: 10}),
})

// This part is not necessary, but for demo purpose it's here
// Here we inject `userStore` store into another store
const appStore = new Store({
  person: {
    fakeAge: 21,
    data: userStore.inject,
  }
})

// Finally we can use our data. API will gather data automatically
function App() {
  return (
    <h1>
      {appState.out(({person}) => (
        person.data.firstname + ' ' + person.data.lastname
      ))}
    </h1>
  )
}

This is what v1 will eventually look like. There is some work in progress already in huskey branch if you want to check it out.


Here are some live examples:

Duplicated key in globals object.

Hey folks, first of all nice project πŸ‘
I was hacking out the source code of refactoring branch and found a duplicated key in the GLOBALS object.
Is there any reason for that?
https://github.com/radi-https://github.com/radi-js/radi/blob/refactor/src/consts/GLOBALS.js#L11
https://github.com/radi-https://github.com/radi-js/radi/blob/refactor/src/consts/GLOBALS.js#L17

(Should I open issues related to refactoring branch here or should I discuss about int the slack channel?)

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.