1.0.0-preA3 • Published 6 years ago

xypts v1.0.0-preA3

Weekly downloads
1
License
MIT
Repository
github
Last release
6 years ago

Xyp - minimalistic TypeScript framework

npm version Codacy Badge

Table of Contents

Quick start

Install this package:

npm i -g xypts

Create new project:

xyp new quick-start

Start development server:

xyp serve

Edit src/components/app-root and open browser in http://localhost:9000

CLI

For CLI usage you can use command xyp

Reference

Components

You can create components with custom template and css.

xyp component my-awesome-component

Shorthand: xyp c my-awesome-component

New folder called my-awesome-component will be created. Now you can use it in app-root.html:

<my-awesome-component></my-awesome-component>

Attributes directives

(bind) - Two way binding

Example:

<h1 (bind)="name"></h1>
<input (bind)="name">

In this example h1 inner text depends on variable "name", which is binded to the input.

(listen) - One way binding

<h1 (listen)="name"></h1>
<input (bind)="name">

In this example header listens for changes in "name" variable. Works same as previous example.

(listen:<attribute>), (bind:<attribute>) - Data binding

<img (listen:src)="myphoto">
<input (listen:placeholder)="placeholder">

These attributes bind variable to defined attribute of calling element.

(model) - One way binding

<h1 (listen)="name"></h1>
<input (model)="name">

This example's input listens for changes and only modifies "name" variable.

(init) - Initialize value

<h1 (listen)="name"></h1>
<input (init)="name = 'hi there!'" (bind)="name">

In this example "name" varibale is initialized with value 'hi there!'. Note: if variable was already initialized(e.g. is not undefined), it won't be initialized

(for) - Iterate over array or object

Example:

<div (init)="favourites = ['snickers', 'milky way']" (for)="candy of favourites"><div>I love ${candy}!</div></div>

"favourites" variable is initalized with 'snickers', 'milky way' array, for directive iterates over array's values. Use "in" instead of "of" to iterate over object's keys, not values. To access iterated value, use variable name in "${}" in html, e.g. ${candy}. Notice that scope of "${}" value is isolated. That means you can access only this variable and json() function and i, e.g. iterated index.

json - Convert object to JSON

Usage:

<div (init)="favourites = [{'snickers': true}, {'milky way': false}]" (for)="candy of favourites"><div>${json(candy)}</div></div>

Output:

{"snickers":true}
{"milky way":false}

(hide) - Show element conditionnally

Example 1:

<p (hide)="2 + 2 === 5">Wow, 2 + 2 is 5</p>
<p (hide)="2 + 2 === 4">As always, 2 + 2 is 4</p>
<p (init)="truth = true" (hide)="truth">Everything is truth</p>
<p (init)="lie = false" (hide)="!lie">Lie</p>

(hide) sets element's hidden attribute if expression is true. Example 2:

<p (hide)="2 + 2 === 4">2 + 2 is 4, so that element will be hidden</p>

You can use ! to hide element if value is false. Also you can bind directive to variable:

<p (init)="truth = true" (hide)="truth">Everything is truth</p>
<p (init)="lie = true" (hide)="!lie">Lie</p>

Advanced

Eval {{expression}} syntax

<p>This expression is precomputed: {{2 + 2}}</p>
<p (init)="x = 4 + 4">This expression is binded to "x" variable: {{x}}</p>

Xyp defines and uses

$data
$routes
$subscribes
$directives
$routeParameters

global variables. Make sure that your code is not overwriting them. Example:

<h1 (init)="title = 'Add more A: '">{{title}}</h1>
<button onclick="$data['title'] += 'A'">Add A here</button>

In this example $data'title' is initialized with value 'Add more A: ' and then button click adds 'A' to the var.

Reactiveness

Xyp reactiveness mechanism is quite simple: item is initialized in $data variable with setter invoking all functions in corresponding $subscribes object. To subscribe on change of some variable in $data, push callback in $subscribes or use subscribe function. Example:

<!-- app-root.html -->
<h1 (init)="title = 'hello!'" (listen)="title"></h1>

component.ts:

/// <reference path="../../xypts/init.ts"/>
/// <reference path="../../xypts/component.ts"/>
@Component('app-root')
class AppRootComponent extends XypComponent {
  constructor() {
    super('app-root');
    setTimeout(() => {
      $data['title'] = 'hi there!';
    }, 500);
    subscribe('title', () => console.log('title has changed!'));
  }
}

Routing

Xyp support routing. That means that Xyp can be used to create SPA(single-page application)s. Add this component where routing is needed:

<xyp-router></xyp-router>

Create some page components and prepend @Route decorator in component.ts. Example:

app-root.html

<a href="#/test">Test</a>
<xyp-router></xyp-router>

In new component called 'test-page':

component.ts:

/// <reference path="../../xypts/component.ts"/>
/// <reference path="../../xypts/routes.ts"/>
@Route({
  selector: 'test-page',
  route: '/test',
})
@Component('test-page')
class TestPageComponent extends XypComponent {
  constructor() {
    super('test-page');
  }
}

If URL ends with #/test, there should be test-page works! text. Also you can pass parameters to the route like:

http://localhost:9000/#/test?foo=bar

Now, parameter will be available in $routeParameters'foo', e.g $routeParameters'foo' is 'bar'.

Functions

$http - send asynchronous HTTP request
interface HTTPRequest {
  headers?: { [key: string]: string };

  url: string;
  method: string;

  body?: any;
  urlEncodeBody?: boolean;

  parseJSON?: boolean;
}

function $http<T>(requestData: HTTPRequest): Promise<T>

$http sends HTTP request and returns promise with response body. If urlEncodeBody is true, request body is encoded as URL. If parseJSON is set, response body is parsed as JSON and returned in Promise as Object, otherwise string casted to T is returned.

listen - subscribe for object's property's changes
function listen(object: any, objname: string, callback: Function)

listen tries to inject setter and getter of object's objname proprety with defined callback. This function must NOT be called with $data object. Use bind function instead.

bind - inject reactive setter and getter of $dataobjname
function bind(objname: string, callback?: ReactiveListener)

Bind calls listen function and adds callback invoked on $data[objname]'s change, if present.

ReactiveListener

ReactiveListener has two fields: isDeletable(checks whether this subscribe will update DOM), and callback(subscribe). Use DefaultReactiveListener function to create ReactiveListener.

function DefaultReactiveListener(el: HTMLElement, callback: Function)

Comparation to other frameworks

Counter app

Xyp

<h1 (init)="count = 0">{{count}}</h1>
<button onclick="$data['count']--">-</button>
<button onclick="$data['count']++">+</button>

React

import React from "react";
import ReactDOM from "react-dom";

class Counter extends React.Component {
    constructor(props) {
        super(props);
        this.state = { count: 0};
    }

    down(value) {
        this.setState(state => ({ count: state.count - value }));
    }
    up(value) {
        this.setState(state => ({ count: state.count + value }));
    }

    render() {
        return (
            <div>
                <h1>{this.state.count}</h1>
                <button onClick = {() => this.down(1)}>-</button>
                <button onClick = {() => this.up(1)}>+</button>
            </div>
        );
    }
}
ReactDOM.render(<Counter />, document.querySelector("#app"));

Vue

import Vue from "vue";

new Vue({
    data: { count: 0 },

    methods: {
        down: function(value) {
            this.count -= value;
        },
        up: function(value) {
            this.count += value;
        }
    },

    render: function(h) {
        return(
            <div>
                <h1>{this.count}</h1>
                <button onClick={() => this.down(1)}>-</button>
                <button onClick={() => this.up(1)}>+</button>
            </div>
        );
    },

    el: "#app"
});

Hyperapp

import { h, app } from "hyperapp";

const state = {
    count: 0
};

const actions = {
    down: value => state => ({ count: state.count - value}),
    up: value => state => ({ count: state.count + value})
};

const view = (state, actions) => (
    <div>
        <h1>{state.count}</h1>
        <button onclick={() => actions.down(1)}>-</button>
        <button onclick={() => actions.up(1)}>+</button>
    </div>
);

app(state, actions, view, document.querySelector("#app"));

Svelte

<div>
  <h1>{count}</h1>
  <button on:click="set({count: count - 1})">-</button>
  <button on:click="set({count: count + 1})">+</button>
</div>

Asynchronous app

Xyp

app-root.html

<button onclick="getPosts()">Get posts</button>
<div (for)="post of posts">
    <div id="${post.id}">
        <h2><font color="#3AC1EF">${post.title}</font></h2>
        <p>${post.body}</p>
    </div>
</div>

component.ts

/// <reference path="../../xypts/init.ts"/>
/// <reference path="../../xypts/component.ts"/>
@Component('app-root')
class AppRootComponent extends XypComponent {
  constructor() {
    super('app-root');
    $data['posts'] = [];
  }
}

interface Post {
  id: number;
  userId: number;
  title: string;
  body: string;
}
function getPosts() {
  $http<Post[]>({ url: 'https://jsonplaceholder.typicode.com/posts', method: 'GET', parseJSON: true }).then(data => {
    $data['posts'] = data;
  });
}

React

import React from "react";
import ReactDOM from "react-dom";

class PostViewer extends React.Component {
    constructor(props) {
        super(props);
        this.state = { posts: [] };
    }

    getData() {
        fetch(`https://jsonplaceholder.typicode.com/posts`)
        .then(response => response.json())
        .then(json => {
            this.setState(state => ({ posts: json}));
        });
    }

    render() {
        return (
            <div>
                <button onClick={() => this.getData()}>Get posts</button>
                {this.state.posts.map(post => (
                    <div key={post.id}>
                        <h2><font color="#3AC1EF">{post.title}</font></h2>
                        <p>{post.body}</p>
                    </div>
                ))}
            </div>
        );
    }
}

ReactDOM.render(<PostViewer />, document.querySelector("#app"));

Vue

import Vue from "vue";

new Vue({
    data: { posts: [] },

    methods: {
        getData: function(value) {
            fetch(`https://jsonplaceholder.typicode.com/posts`)
            .then(response => response.json())
            .then(json => {
                this.posts = json;
            });
        }
    },

    render: function(h) {
        return (
            <div>
                <button onClick={() => this.getData()}>Get posts</button>
                {this.posts.map(post => (
                    <div key={post.id}>
                        <h2><font color="#3AC1EF">{post.title}</font></h2>
                        <p>{post.body}</p>
                    </div>
                ))}
            </div>
        );
    },

    el: "#app"
});

Hyperapp

import { h, app } from "hyperapp";

const state = {
    posts: []
};

const actions = {
    getData: () => (state, actions) => {
        fetch(`https://jsonplaceholder.typicode.com/posts`)
        .then(response => response.json())
        .then(json => {
            actions.getDataComplete(json);
        });
    },
    getDataComplete: data => state => ({ posts: data })
};

const view = (state, actions) => (
    <div>
        <button onclick={() => actions.getData()}>Get posts</button>
        {state.posts.map(post => (
            <div key={post.id}>
                <h2><font color="#3AC1EF">{post.title}</font></h2>
                <p>{post.body}</p>
            </div>
        ))}
    </div>
);

app(state, actions, view, document.querySelector("#app"));

Svelte

<div>
  <button on:click="getData()">Get posts</button>
  {#each posts as {title, body}}
  <div>
    <h2><font color="#3AC1EF">{title}</font></h2>
    <p>{body}</p>
  </div>
  {/each}
</div>

<script>
  export default {
    methods: {
      getData() {
        fetch('https://jsonplaceholder.typicode.com/posts')
          .then(res => res.json())
          .then(posts => this.set({ posts }));
      }
    }
  };
</script>
1.0.0-preA3

6 years ago

1.0.0-preA1

6 years ago

1.0.0-preA0

6 years ago

1.0.0-pre

6 years ago

0.7.2

6 years ago

0.7.2-pre3

6 years ago

0.7.2-pre2

6 years ago

0.7.2-pre1

6 years ago