From d0431c7f81e20dfb77a6fe154292d6b06f433984 Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 7 Feb 2023 18:38:44 +0100 Subject: Init version --- README.md | 85 ++++++++++++ bin/dev-server | 16 +++ flake.lock | 42 ++++++ flake.nix | 21 +++ public/index.html | 6 + src/example.ts | 171 +++++++++++++++++++++++ src/rx.ts | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 13 ++ 8 files changed, 752 insertions(+) create mode 100644 README.md create mode 100755 bin/dev-server create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 public/index.html create mode 100644 src/example.ts create mode 100644 src/rx.ts create mode 100644 tsconfig.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..f878b62 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# Rx + +3.4kb unzipped DOM binding library which: + +- composes updatable values, `Rx`, with `map` and `flatMap`, +- re-render DOM portions under `Rx` values. + +Limitations: + +- whenever a DOM portion relies on a `Rx`, and the `Rx` value is updated, the + complete portions is removed and re-created, no matter if the DOM portion is + almost equal to the portion it replaced, +- no serious performance tests, I only noticed that it was faster than + `monadic-html` to create and update lots of rows, and slower than + Vanilla JS. + +## Usage + +### Hello world + +```typescript +import { h, mount } from 'rx' + +mount(h('div', 'Hello World!')) +``` + +### Attributes + +```typescript +import { h, mount } from 'rx' + +mount(h('button', + { className: 'g-Button', + color: 'green', + onclick: () => console.log('Clicked!') + }, + 'Click!')) +``` + +### State + +Counter with `-` and `+` buttons: + +```typescript +import { h, withVar, mount } from 'rx' + +mount( + withVar(0, (value, update) => [ + value, + h('button', { onclick: () => update(n => n - 1) }, '-'), + h('button', { onclick: () => update(n => n + 1) }, '+')])) +``` + +### Subscriptions + +Chronometer updating every second: + +```typescript +import { h, withVar, mount } from 'rx' + +mount( + withVar(0, (value, update) => { + const interval = window.setInterval(() => update(n => n + 1), 1000) + return h('div', + { onunmount: () => clearInterval(interval) }, + value + ) + })) +``` + +`onunmount` is a special attribute to keep track of the component lifecycle. +`onmount` is available as well. Those functions can take the `element` as an +argument. + +## Ideas + +- API: + - Rx debounce, filter, + - Routeur with sub-page support. +- Optimization: store intermediate rx instead of recomputing each time for multiple subscribers? + +## Inspiration + +- https://github.com/OlivierBlanvillain/monadic-html +- https://www.solidjs.com diff --git a/bin/dev-server b/bin/dev-server new file mode 100755 index 0000000..82655d1 --- /dev/null +++ b/bin/dev-server @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Run server + +python -m http.server --directory public 8000 & +trap "fuser -k 8000/tcp" EXIT + +# Watch TypeScript + +CHECK="echo Checking TypeScript… && tsc --checkJs" +BUILD="esbuild --bundle src/example.ts --target=es2017 --outdir=public" +watchexec \ + --clear \ + --watch src \ + -- "$CHECK && $BUILD" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..9f5bc7d --- /dev/null +++ b/flake.lock @@ -0,0 +1,42 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1675024979, + "narHash": "sha256-XGNts6Ku8DipMSI3o1sXPunXNewKHfzh21CqeYPXzog=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "87c5f6725442ab846b8a6719e63a0f079627c884", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..325ed60 --- /dev/null +++ b/flake.nix @@ -0,0 +1,21 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem + (system: + let pkgs = nixpkgs.legacyPackages.${system}; + in { devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + nodePackages.typescript + python3 + psmisc # fuser + esbuild + watchexec + ]; + }; } + ); +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..2cb9f22 --- /dev/null +++ b/public/index.html @@ -0,0 +1,6 @@ + + + +