sc0ttj / component

A tiny library for isomorphic JavaScript components
MIT License
2 stars 1 forks source link
components es6 isomorphic javascript reactive

Component logo

Component

A simple JavaScript component thing

[![npm version](https://badge.fury.io/js/%40scottjarvis%2Fcomponent.svg)](https://badge.fury.io/js/%40scottjarvis%2Fcomponent) [![Node version](https://badgen.net/npm/node/@scottjarvis/component)](http://nodejs.org/download/) [![Build Status](https://travis-ci.org/sc0ttj/component.svg?branch=master)](https://travis-ci.org/sc0ttj/component) [![bundle size](https://badgen.net/bundlephobia/minzip/@scottjarvis/component?color=green&label=gzipped)](https://badgen.net/bundlephobia/minzip/@scottjarvis/component) [![Downloads](https://badgen.net/npm/dt/@scottjarvis/component)](https://badgen.net/npm/dt/@scottjarvis/component) **Component** is a simple, "stateful component" thing. It lets you create re-usable, "functional components" - basically functions that have a "state". A "state" is a snapshot of your application data at a specific time. ## Features - Easy setup, **zero dependencies**: - no compilation or build tools needed - Simple syntax, [quick to learn](#quickstart), **easy to use**: - plain, vanilla JavaScript only! - plain HTML & CSS, no virtual DOM or JSX - should work with any test suite - Very **lightweight & modular** - use only what you need, for example: - *~2.5 kb*, using the main `Component` library (_lots_ of features) - *~810 bytes*, using only the standalone `htmel` module (for JSX-like HTML templating) - *~830 bytes*, using only the standalone `render` module (for "debounced" DOM diffing at 60fps) - Works **client-side**, in browsers: - auto re-render on state change - good (re)rendering/animation performance at 60fps, using `requestAnimationFrame` - DOM diffing uses real DOM Nodes (not VDOM) - or use an HTML5 `` instead of a DOM-based view - Works **[server-side](#server-side-rendering)**, in Node: - render your components as strings (HTML, or stringified JSON) - render your components as data (JS objects, Arrays, etc) - Easy **state management**: - immutable states, can only be updated through `setState()` (_optional_) - define ["actions"](#using-actions) to easily update the state in specific ways - keep all states in a [state history](#using-the-state-history), for debugging (_optional_): - rewind or fast-forward to any point in the state history - save/load current or any previous state as "snapshots" - Easy CSS **[component styling](#using-component-css)**: - Automatic "scoping"/prefixing of your component CSS (_optional_) - Re-render styles on component CSS change (_optional_) - Supports **"[middleware](#using-middleware)"** functions: - easily customise a components setState and re-render behaviour - Supports **nested components**: - embed components in the "views" of other components - supports various methods and syntaxes - Works with these **optional add-ons**: - [`validator`](#using-state-validation): validate states against a schema (_like a simple PropTypes_) - [`html`/`htmel`](#using-html-and-htmel-modules): simpler, more powerful Template Literals (_like a simple JSX_) - [`ctx`](#using-the-ctx-module): an enhanced 2d <canvas> with extra shapes, methods, and a chainable API - [`emitter`](#using-the-emitter-module): an event emitter - share updates between components - [`storage`](#using-the-storage-module): enables persistent states (between page refreshes, etc) - [`syncTabs`](#using-the-synctabs-module): Synchronize state updates & page renders between browser tabs - [`tweenState`](#using-the-tweenstate-module): animate nicely from one state to the next (tweened) - [`springTo`](#using-the-springto-module): animate nicely from one state to the next (using spring physics) - [`onScroll`](#using-the-onscroll-module): enables easy scroll-based animations (using debounced scroll event) - [`useAudio`](#using-the-useaudio-module): add dynamic audio to your components (uses Web Audio API) - [`onLoop`](#using-the-onloop-module): a fixed-interval loop, suitable for games, animations, time-dependant stuff - [`geo`](#using-the-geo-module): an easy way to create Mercator or Robinson projected world maps - [`chart`](#using-the-chart-module): an easy way to create charts, graphs and plotting things - [`react-hooks`](#using-the-react-hooks-module): an alternative, more React-like API add-on - [`devtools`](#using-the-devtools-module): enables easier component debugging in the browser --- ## Quickstart Here's some quick examples to demo how it all looks, generally: ### "Counter" app ```js const Counter = new Component({ count: 1 }); const add = num => Counter({ count: Counter.state.count + num }) Counter.view = props => htmel `

Counter: ${props.count}

`; Counter.render('.container') ``` ### "Todo list" app ```js const Todo = new Component({ txt: '', list: [ "one" ] }); const setText = e => Todo({ txt: e.target.value }); const addItem = e => Todo({ list: [ ...Todo.state.list, Todo.state.txt ] }); Todo.view = props => htmel `

Todo

    ${props.list.map(i => `
  • ${i}
  • `)}
`; Todo.render('.container') ``` ### A *re-usable* HTML component: Here is a "factory" function that generates _re-usable_ components - a new component is created and returned each time it's called. ```js function Header(state) { const Header = new Component({ title: "Hello world", ...state }); Header.view = props => `

${props.title}

`; return Header; } // And you use it like this: const header1 = new Header(); // Add it to our page header1.render('.container'); // Update the state, the heading will re-render for you header1.setState({ title: "Hello again!" }); // Or set state via the component constructor header1({ title: "Hello a 3rd time!" }); ``` ### Using the `` Your component "views" don't have to be HTML or SVG, they can use a `` element, instead: ```html ``` Also see the [`Ctx` add-on](#using-the-ctx-module) and the [`onLoop` add-on](#using-the-onloop-module) add-on for more `` related info. ### Nested components Child components should be regular functions that return part of the view of the parent component: ```js const Foo = new Component({ title: "Hey!", items: [ "one", "two" ] }); const Header = txt => `

${txt}

` const List = i => `
    ${i.map(item => `
  • ${i}
  • `).join('')}
` Foo.view = props => `
${Header(props.title)} ${List(props.items)}
` Foo.render('.container'); ``` But you can also nest proper (stateful) components inside other components, too: ```js // create a re-usable button component function Button(state) { const Button = new Component(state); Button.view = props => htmel``; return Button; } // create 3 buttons from the re-usable component const btn1 = new Button({ txt: "1", fn: e => alert("btn1") }); const btn2 = new Button({ txt: "2", fn: e => alert("btn2") }); const btn3 = new Button({ txt: "3", fn: e => alert("btn3") }); // create the parent component const Menu = new Component({ txt: 'Click the buttons!' }); // put the styles in the parent component Menu.style = props => ` button { border: 2px solid #999; } ` // create a view with our buttons included Menu.view = props => htmel`

${props.txt}

${btn1} ${btn2} ${btn3}
`; Menu.render('.container'); ``` See more short recipes in [examples/recipes.js](examples/recipes.js). --- ## Installation ### In browsers: ```html ``` ### In NodeJS: ``` npm i @scottjarvis/component ``` Then add it to your project: #### ES6 Modules: ```js import { Component } from '@scottjarvis/component' // use it here ``` #### Using NodeJS `require()`: ```js var { Component } = require('@scottjarvis/component'); // use it here ``` See each add-on module (`validator`, `html`, `htmel`, `emitter`, `storage`, `tweenState`, etc) for their respective installation instructions. --- ## Usage ### In browsers Create interactive components for your web pages: - [examples/usage-in-browser--table.html](examples/usage-in-browser--table.html) - [examples/usage-in-browser.html](examples/usage-in-browser.html) ### In NodeJS Or you can do "server-side rendering" (SSR) of your components: See [examples/usage-in-node.js](examples/usage-in-node.js) Or run it in your terminal: ``` node examples/usage-in-node.js ``` ## Component API overview These are the methods and properties attached to the components you create. ### Methods: - **.setState(obj)**: update the component state, triggers a re-render - **.render(el)**: (re)render to the given element on state change (browser) - **.toString()**: render your component as a string on state change (for NodeJS) - **.view(props)**: receives a state and sets the component view to (re)render (_optional_) - **.style(props)**: receives a state and sets the `

Total so far = 101

" ``` ^ Any styles are wrapped in a `