Skip to content

devarofi/jetz

Repository files navigation

Jetz

Composable Javascript Framework

Jetz

Jetz is a front-end framework javascript for build a front end with declarative javascript DOM.

Demo

Write this command in terminal :

npm install
npm run watch
npm run start

Why Jetz

Jetz is inspired by Jetpack Compose. Is using for creating rich UI with simple, fast, declarative syntax in android. But Jetz was coming for Web Developers.

Built-in

- element function
- element function
- composable variable
- conditional element
- state management
- routing

How to Render Elements?

Empty body element in index.html

<body></body>
import { Jetz } from "./src/jetz/jetz";
import { find } from "./src/jetz/jetz-ui";

let App = div('Hello World');

Jetz.mount(App, document.body);

Thinking in Jetz in html :

<div class="container">
  <ul class="todo-list">
    <li>Item 1 </li>
    <li>Item 2</li>
    <li>Item 3</li>
   </ul>
</div>

in javascript with Jetz :

const item = [1, 2, 3];
const todo =
  div( css`container`, 
    ul( css`todo-list`,
      items.map(data => li('Item ' + data))
    )
  );

Event Listener

in html :

<button class="btn btn-info" type="button" onclick="showAlert('Aww Snap')">
  Show Alert
</button>
....
<script>
  function showAlert(text){
    alert(text);
  }
</script>

in javascript with Jetz :

button('Show Alert', css`btn btn-info`, {
  onclick(){
    alert('Aww Snap');
  }
});

DOM Element

in html :

<p id="my-content">This is paragraph of your content</p>
<button type="button" onclick="changeText()"></button>
...
<script>
  let myContent = document.getElementById('my-content');
  function changeText(){
    myContent.innerText = 'Paragraph was changed into other content';
  }
</script>

in javascript with Jetz :

let myContent = p('This is paragraph of your content');
button({
  onclick(){
    myContent.text('Paragraph was changed into other content');
  }
});

Component or Function or Variable

You can define a Component with three ways, with Component class, Function, or Variable :
With variable :

let MovieApp = div( css`container`,
  div( css`row`,
    div( css`col-md-3`,
      ul( css`navbar`,
        li( css`navbar-item`, 'MENU 1'),
        li( css`navbar-item`, 'MENU 1'),
        li( css`navbar-item`, 'MENU 1')
      )
    ),
    div( css`col-md-9`,
      // ...
    ),
    // ...
  )
  // ...
);
export default MovieApp;

Or with Function :

function MovieApp(){
  return div( css`container`,
    div( css`row`,
      //...
    ),
    //...
  )
}
export default MovieApp;

Or with Component class, it's need a render method to render an element :

class MovieApp extends Component {
  showMessage(message){
    alert(message);
  }
  render(){
    return (
      div( css`movie-body`,
        button( css`btn btn-info`, 'Show Message', {
          onclick:() => {
            this.showMessage('Hello World');
          }
        })
        //...
      )
    )
  }
}
export default MovieApp;

Passing Data between Function or Component

You can passing data as argument of class or argument function it self, example :

With function

let PostCard = function(title, content){
  return div( css`card`,
    div( css`card-title`, title),
    div( css`card-body`,
      p(content)
    )
  )
}
// Sample with data
let userPosts = [
  { title: 'Black Bird', message: 'A bird with black color'},
  { title: 'Red Bird', message: 'A bird with red color'},
  { title: 'Green Bird', message: 'A bird with green color'},
];

// Call a function in element child
let RootPage = div( css`container`, 
  div( css`row`,
    div( css`col-12`,
      loop( userPosts, post => PostCard(post.title, post.message))
    )
  )
)

Passing data in Component

class MyButton extends Component{
    say = '';
    word = '';

    constructor(say, word){
        super();
        this.say = say;
        this.word = word;
    }
    
    showMessage(){
        alert(`${this.say} ${this.word}`);
    }

    render(){
        return (
            button('Show Message', {
                onclick:() => this.showMessage()
            })
        )
    }
}
// sample instance variable
let _mybutton = new MyButton('Hello', 'World');

// Call Component in element
let RootPage = main( css`container`,
  // with new instance
  new MyButton('Hello', 'World'),
  // OR with new function
  MyButton.new('Hello', 'World'),
  // Or with instance variable
  _mybutton
);

Event Listener

Create an event listener in element.

Example Listener

let myCounter = function(){
  let counter = stateOf(0);
  return button('Count : ', counter, {
    onclick(){
      counter.value++;
    }
  })
};
export default myCounter;

Or

let counter = stateOf(0);
let myCounter = button('Count : ', counter, {
  onclick(){
    counter.value++;
  }
});
export default myCounter;

Or

let counter = stateOf(0);
let myCounter = button('Count : ', counter)
                .on('click', e => { counter.value++; });

Or in Component class

Note : if you want to use `this` as Component it self, you must write listener with arrow function

export class MyCounter {
  counter = stateOf(0);
  render(){
    return button('Counter', this.counter, {
      onclick:() => {
        this.counter.value++;
      }
    })
  }
}

Build-in Function

find(selector) // is used for get one element by selector and returned HTMLElement
findAll(selector):Array<HTMLElement> //  is used for get some element by selector

example :

let MainApp = main(
  button('Click ME', {
    onclick(){
      find('#my-label').innerText = 'Clicked';
      // OR use $ to call instance of JetzElement
      // find('#my-label').$.text('Clicked');
    }
  }),
  label(id`my-label`, 'You are not yet clicked')
)

State Management

Jetz was have a state built-in, which is stateOf and listOf and sequenceOf

stateOf

stateOf function is used for single value it could be string, numeric, object, or element.

example
let counter = stateOf(0);
let myButton = button('Clicked ', counter, {
  onclick(){
    counter.value++;
  }
})

listOf

listOf function is used for array/collections of value it could be string, numeric, object, or element.

example
let myStack = listOf();
let myButton = button('Clicked ', {
  onclick(){
    myStack.push('New Item');
  }
})

sequenceOf

sequenceOf function is used for unique string or number, that may be same value, but element is different

example
let myStack = sequenceOf();
let MyApp = main(
  ul(
    loop( myStack , stack => (
      li(stack, button('delete', {
        onclick() {
          myStack.remove(stack)
        }
      })
    )
  ),
  button('Clicked ', {
    onclick(){
      myStack.push('New Item');
    }
  })
)

Example State

Example state list :

let cartState = listOf();
let myApp = main(
  ul(
    loop(cartState, item => li(item))
  ),
  button('Add new item', {
    onclick() {
      cartState.push(`New Item ${cartState.length + 1}`)
    }
  })
);

Jetz with state and binding value :

let myState = stateOf('');
let myComponent = div(
  div('My name is : ', myState),
  inputText({
    bind: myState,
    placeholder: 'Input your name...'
  })
);

Conditional Element

For conditional element Jetz using function with callback parameter that return boolean to recheck condition, because that element will revalidated when the value was changed. use _if(() => condition) example :

div( _if(() => 1 > 3),
  'content'
) // it will hidden, because value is false

use _elseif(() => condition) example :

let counter = stateOf(0);
let myComponent = div(
  div( if(() => counter.value == 1),
    'One'
  ),
  div( _elseif(() => counter.value == 2),
    'Two'
  ),
  button( 'count',{
    onclick(){
      counter.value++;
    }
  })
)

use _else property example :

let isShow = stateOf(true);

let myComponent = div(
  div( if(() => isShow.value),
    'Im true'
  ),
  div( _else,
    'Im false'
  ),
  button('Change value', {
    onclick(){
      isShow.value = false;
    }
  })
)

Or with _if, _elseif, and _else

let counter = stateOf(0);
let myComponent = div(
  div('Number is : ', 
    div( _if(() => counter.value == 0),
      'Zero'
    ),
    div( _elseif(() => counter.value == 1),
      'One'
    ),
    div( _elseif(() => counter.value == 2),
      'Two'
    ),
    div( _else,
      'More (', counter,')'
    ),
  ),
  button('Count', {
    onclick(){ counter.value++ }
  })
);

Dispatcher

let pageContent = stateOf(div())

let dispatcher = new Dispatcher(action => {
  if(action === 'home'){
    pageContent.value = home()
  }else if(action === 'about'){
    pageContent.value = about()
  }else if(action === 'contact-us'){
    pageContent.value = contactUs()
  }
})

let myApp = main(
  nav(
    ul(
      li('Home').on('click', () => dispatcher.dispatch('home')),
      li('About').on('click', () => dispatcher.dispatch('about')),
      li('Contact Us').on('click', () => dispatcher.dispatch('contact-us'))
    )
  ),
  pageContent
)

Router

Register route into Jetz

import { Jetz } from "./src/lib/jetz";
import { Router, route } from "./src/lib/jetz-router";
import { MovieApp } from './src/components/movie-app/movie-app';
import { MovieHome } from "./src/components/movie-app/movie-home";
import { MovieSeries } from "./src/components/movie-app/movie-series";

let router = new Router(
    route('/', MovieHome),
    route('series', MovieSeries)
);
// register router in Jetz
Jetz.use(router);
// mount compose element into document.body
Jetz.mount(MovieApp, document.body);

Place routeBrowser and link in element

function menuBar(){
  return ul( css`menu-list`,
    link('/',
      li( css`menu-item`, 'Home')
    ),
    link('series',
      li( css`menu-item`, 'Series')
    ),
  )
}

let MovieApp = function(){
  return main(
    menuBar,
    Jetz.$route.browser()
  )
}
export default MovieApp;

If this repository get 100 stars i'll focus to develop this library more intens. ☕

Author @daevsoft

About

Composable Javascript Framework for Front End Development.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors