GithubHelp home page GithubHelp logo

easywebapp / webcell Goto Github PK

View Code? Open in Web Editor NEW
175.0 11.0 16.0 4.88 MB

Web Components engine based on VDOM, JSX, MobX & TypeScript

Home Page: https://web-cell.dev/WebCell/

License: Other

TypeScript 97.73% HTML 0.95% Shell 1.32%
webapp web-component spa ecmascript mvvm decorator jsx mobx typescript vdom

webcell's Introduction

WebCell

WebCell logo

简体中文 | English

Web Components engine based on VDOM, JSX, MobX & TypeScript

NPM Dependency CI & CD

Anti 996 license UI library recommendation list

Slideshow Gitter

Edit WebCell demo

NPM

Feature

Engines comparison

feature WebCell 3 WebCell 2 React Vue
JS language TypeScript 5 TypeScript 4 ECMAScript or TypeScript ECMAScript or TypeScript
JS syntax ES decorator stage-3 ES decorator stage-2
XML syntax JSX import JSX factory JSX factory/import HTML/Vue template or JSX (optional)
DOM API Web components Web components HTML 5+ HTML 5+
view renderer DOM Renderer 2 SnabbDOM (built-in) SnabbDOM (forked)
state API MobX @observable this.state this.state or useState() this.$data or ref()
props API MobX @observable @watch this.props or props => {} this.$props or defineProps()
state manager MobX 6+ MobX 4/5 Redux VueX
page router JSX tags JSX tags + JSON data JSX tags JSON data
asset bundler Parcel 2 Parcel 1 webpack Vite

Installation

npm install dom-renderer mobx web-cell

Web browser usage

Demo & GitHub template

Project bootstrap

Tool chain

npm install parcel @parcel/config-default @parcel/transformer-typescript-tsc -D

package.json

{
    "scripts": {
        "start": "parcel source/index.html --open",
        "build": "parcel build source/index.html --public-url ."
    }
}

tsconfig.json

{
    "compilerOptions": {
        "target": "ES6",
        "module": "ES2020",
        "moduleResolution": "Node",
        "useDefineForClassFields": true,
        "jsx": "react-jsx",
        "jsxImportSource": "dom-renderer"
    }
}

.parcelrc

{
    "extends": "@parcel/config-default",
    "transformers": {
        "*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
    }
}

source/index.html

<script src="https://polyfill.web-cell.dev/feature/ECMAScript.js"></script>
<script src="https://polyfill.web-cell.dev/feature/WebComponents.js"></script>
<script src="https://polyfill.web-cell.dev/feature/ElementInternals.js"></script>

<script src="source/MyTag.tsx"></script>

<my-tag></my-tag>

Function component

import { DOMRenderer } from 'dom-renderer';
import { FC, PropsWithChildren } from 'web-cell';

const Hello: FC<PropsWithChildren> = ({ children = 'World' }) => (
    <h1>Hello, {children}!</h1>
);

new DOMRenderer().render(<Hello>WebCell</Hello>);

Class component

Children slot

import { DOMRenderer } from 'dom-renderer';
import { component } from 'web-cell';

@component({
    tagName: 'hello-world',
    mode: 'open'
})
class Hello extends HTMLElement {
    render() {
        return (
            <h1>
                Hello, <slot />!
            </h1>
        );
    }
}

new DOMRenderer().render(
    <>
        <Hello>WebCell</Hello>
        {/* or */}
        <hello-world>WebCell</hello-world>
    </>
);

DOM Props

import { DOMRenderer } from 'dom-renderer';
import { observable } from 'mobx';
import { WebCell, component, attribute, observer } from 'web-cell';

interface HelloProps {
    name?: string;
}

interface Hello extends WebCell<HelloProps> {}

@component({ tagName: 'hello-world' })
@observer
class Hello extends HTMLElement implements WebCell<HelloProps> {
    @attribute
    @observable
    accessor name = '';

    render() {
        return <h1>Hello, {this.name}!</h1>;
    }
}

new DOMRenderer().render(<Hello name="WebCell" />);

// or for HTML tag props in TypeScript

declare global {
    namespace JSX {
        interface IntrinsicElements {
            'hello-world': HelloProps;
        }
    }
}
new DOMRenderer().render(<hello-world name="WebCell" />);

Inner state

Function component

import { DOMRenderer } from 'dom-renderer';
import { observable } from 'mobx';
import { FC, observer } from 'web-cell';

class CounterModel {
    @observable
    accessor times = 0;
}

const couterStore = new CounterModel();

const Counter: FC = observer(() => (
    <button onClick={() => (couterStore.times += 1)}>
        Counts: {couterStore.times}
    </button>
));

new DOMRenderer().render(<Counter />);

Class component

import { DOMRenderer } from 'dom-renderer';
import { observable } from 'mobx';
import { component, observer } from 'web-cell';

@component({ tagName: 'my-counter' })
@observer
class Counter extends HTMLElement {
    @observable
    accessor times = 0;

    handleClick = () => (this.times += 1);

    render() {
        return <button onClick={this.handleClick}>Counts: {this.times}</button>;
    }
}

new DOMRenderer().render(<Counter />);

CSS scope

Inline style

import { component } from 'web-cell';
import { stringifyCSS } from 'web-utility';

@component({
    tagName: 'my-button',
    mode: 'open'
})
export class MyButton extends HTMLElement {
    style = stringifyCSS({
        '.btn': {
            color: 'white',
            background: 'lightblue'
        }
    });

    render() {
        return (
            <>
                <style>{this.style}</style>

                <a className="btn">
                    <slot />
                </a>
            </>
        );
    }
}

Link stylesheet

import { component } from 'web-cell';

@component({
    tagName: 'my-button',
    mode: 'open'
})
export class MyButton extends HTMLElement {
    render() {
        return (
            <>
                <link
                    rel="stylesheet"
                    href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css"
                />
                <a className="btn">
                    <slot />
                </a>
            </>
        );
    }
}

CSS module

scoped.css
.btn {
    color: white;
    background: lightblue;
}
MyButton.tsx
import { WebCell, component } from 'web-cell';

import styles from './scoped.css' assert { type: 'css' };

interface MyButton extends WebCell {}

@component({
    tagName: 'my-button',
    mode: 'open'
})
export class MyButton extends HTMLElement implements WebCell {
    connectedCallback() {
        this.root.adoptedStyleSheets = [styles];
    }

    render() {
        return (
            <a className="btn">
                <slot />
            </a>
        );
    }
}

Event delegation

import { component, on } from 'web-cell';

@component({ tagName: 'my-table' })
export class MyTable extends HTMLElement {
    @on('click', ':host td > button')
    handleEdit(event: MouseEvent, { dataset: { id } }: HTMLButtonElement) {
        console.log(`editing row: ${id}`);
    }

    render() {
        return (
            <table>
                <tr>
                    <td>1</td>
                    <td>A</td>
                    <td>
                        <button data-id="1">edit</button>
                    </td>
                </tr>
                <tr>
                    <td>2</td>
                    <td>B</td>
                    <td>
                        <button data-id="2">edit</button>
                    </td>
                </tr>
                <tr>
                    <td>3</td>
                    <td>C</td>
                    <td>
                        <button data-id="3">edit</button>
                    </td>
                </tr>
            </table>
        );
    }
}

MobX reaction

import { observable } from 'mobx';
import { component, observer, reaction } from 'web-cell';

@component({ tagName: 'my-counter' })
@observer
export class Counter extends HTMLElement {
    @observable
    accessor times = 0;

    handleClick = () => (this.times += 1);

    @reaction(({ times }) => times)
    echoTimes(newValue: number, oldValue: number) {
        console.log(`newValue: ${newValue}, oldValue: ${oldValue}`);
    }

    render() {
        return <button onClick={this.handleClick}>Counts: {this.times}</button>;
    }
}

Form association

import { DOMRenderer } from 'dom-renderer';
import { WebField, component, formField, observer } from 'web-cell';

interface MyField extends WebField {}

@component({
    tagName: 'my-field',
    mode: 'open'
})
@formField
@observer
class MyField extends HTMLElement implements WebField {
    render() {
        const { name } = this;

        return (
            <input
                name={name}
                onChange={({ currentTarget: { value } }) =>
                    (this.value = value)
                }
            />
        );
    }
}

new DOMRenderer().render(
    <form method="POST" action="/api/data">
        <MyField name="test" />

        <button>submit</button>
    </form>
);

Async component

AsyncTag.tsx

import { FC } from 'web-cell';

const AsyncTag: FC = () => <div>Async</div>;

export default AsyncTag;

index.tsx

import { DOMRenderer } from 'dom-renderer';
import { lazy } from 'web-cell';

const AsyncTag = lazy(() => import('./AsyncTag'));

new DOMRenderer().render(<AsyncTag />);

Animate CSS component

import { DOMRenderer } from 'dom-renderer';
import { AnimateCSS } from 'web-cell';

new DOMRenderer().render(
    <AnimateCSS
        type="fadeIn"
        component={props => <h1 {...props}>Fade In</h1>}
    />
);

Node.js usage

Tool chain

npm install jsdom element-internals-polyfill

Polyfill

import 'web-cell/polyfill';

Basic knowledge

Life Cycle hooks

  1. connectedCallback
  2. disconnectedCallback
  3. attributeChangedCallback
  4. adoptedCallback
  5. updatedCallback
  6. mountedCallback
  7. formAssociatedCallback
  8. formDisabledCallback
  9. formResetCallback
  10. formStateRestoreCallback

Scaffolds

  1. Basic
  2. DashBoard
  3. Static site

Ecosystem

We recommend these libraries to use with WebCell:

Roadmap

More guides

  1. Development contribution

webcell's People

Contributors

techquery avatar wangrunlin 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

webcell's Issues

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.