Socket
Socket
Sign inDemoInstall

web-cell

Package Overview
Dependencies
110
Maintainers
1
Versions
101
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    web-cell

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


Version published
Weekly downloads
32
increased by3.23%
Maintainers
1
Install size
5.22 MB
Created
Weekly downloads
 

Readme

Source

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

featureWebCell 3WebCell 2ReactVue
JS languageTypeScript 5TypeScript 4ECMAScript or TypeScriptECMAScript or TypeScript
JS syntaxES decorator stage-3ES decorator stage-2
XML syntaxJSX importJSX factoryJSX factory/importHTML/Vue template or JSX (optional)
DOM APIWeb componentsWeb componentsHTML 5+HTML 5+
view rendererDOM Renderer 2SnabbDOM(built-in)SnabbDOM (forked)
state APIMobX @observablethis.statethis.state or useState()this.$data or ref()
props APIMobX @observable@watchthis.props or props => {}this.$props or defineProps()
state managerMobX 6+MobX 4/5ReduxVueX
page routerJSX tagsJSX tags + JSON dataJSX tagsJSON data
asset bundlerParcel 2Parcel 1webpackVite

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>
            </>
        );
    }
}
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/bootstrap@5.3.2/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

v2 to v3 migration

  1. Development contribution

Keywords

FAQs

Last updated on 10 Feb 2024

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc