47 | }
48 | render( , document.body );
49 | ```
50 |
51 | 
52 |
53 | ---
54 |
55 | ## [🎮 - Play with this example on StackBlitz](https://stackblitz.com/edit/node-freprp?file=index.tsx)
56 | ## [👨🏫 - Learn Reflex in 5 minutes](https://zouloux.github.io/reflex/learn/)
57 | ## [📘 - See API documentation](https://zouloux.github.io/reflex/api/)
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 |
4 | node_modules
5 | dist/
6 | /deopt/v8-deopt-viewer
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /_
2 | .github
3 | .idea
4 | .DS_Store
5 |
6 | /bits/
7 | /docs
8 | /reflex-dev
9 | /src
10 | /reflex-learn
11 | /.gitignore
12 | /CODEGOLF.md
13 | /LICENSE
14 | /tsconfig.json
15 | /v8-deopt-viewer
16 | /reflex-deopt
--------------------------------------------------------------------------------
/CODEGOLF.md:
--------------------------------------------------------------------------------
1 | # About code golfing
2 |
3 | Main rules are to create the lightest bundle possible BUT to keep the code as
4 | much as performant possible AND readable. Code golf optimization which will ruin
5 | readability to slim 2 bytes from the bundle will be prohibited.
6 |
7 | ### Underscore and terser
8 |
9 | Terser is configured in `tsbundle` to mangle properties starting with a `_`.
10 | So any member of object, or module starting with `_` will be renamed into a
11 | shorter version. Variable names with or without underscore will be mangled.
12 | So it's important to use `_name` prop names if the property is meant to be private.
13 |
14 | #### Ex with object :
15 | ```js
16 | const _member = { // _member will be mangler because it's a var and not a prop
17 | keep : "", // Will be kept as it
18 | _mangled : true // Will be mangled
19 | }
20 | ```
21 | Will become :
22 | ```js
23 | const a = {
24 | keep : "",
25 | b : true
26 | }
27 | ```
28 |
29 | ##### Ex with module :
30 | ```js
31 | export const _TEXT_NODE_TYPE_NAME = "#T"
32 | export const publicProperty = "Public"
33 | ```
34 | Will become :
35 | ```js
36 | export const a = "#T"
37 | export const publicProperty = "Public"
38 | ```
39 |
40 | ### Source code vs dist code
41 |
42 | It's important to keep source code readable. Terser will compress and mangle,
43 | so no need to compress var names in source code, use plain variable names.
44 |
45 | ### NODE_ENV
46 | Instructions like `process.env.NODE_ENV != "production"` will be parsed and dead
47 | code will be removed by terser.
48 |
49 | ### Beware of induced helpers
50 |
51 | If you use typescript code which can induce transpiling, typescript can add helpers.
52 | Ex, if you use `const { a, b, ..rest } = baseObject` which is not available in
53 | base target (`es2017`), generated source code can be heavier than writing
54 | assignment manually.
55 |
56 |
57 | ### Export glob from import
58 |
59 | - This will force TS compiler to include a helper.
60 | - + It may include unwanted members.
61 |
62 | - 🚫 `export * from "./state"`
63 | - ✅ `export { state, asyncState } from "./state"`
64 |
65 | ### Typeof something
66 |
67 | To check if some property is undefined :
68 | - ✅ `typeof self < 'u' ? isNotUndefined : isUndefined`
69 |
70 | > Be aware that it destroy performances and should only be used outside loops and critical code
71 |
72 | ### Casting
73 |
74 | ##### Convert to string
75 | - 🚫 `something.toString()`
76 | - ✅ `something+''`
77 |
78 | ##### Convert to number
79 | - `let numberAsString = "40"`
80 | - 🚫 `let number = parseInt(numberAsString, 10)`
81 | - ✅ `let number = +numberAsString`
82 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Alexis Bouhet
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Reflex
2 |
3 | __Reflex__ is a tiny and fast reactive UI library.
4 |
5 | - 🦋 Super lightweight and **0 dependency**, about **4kb** min-zipped
6 | - 🏎 Highly performant diffing algorithm
7 | - 🔋 Batteries included with **factory extensions**
8 | - ⚛️ Truly reactive, states are Signals by design
9 | - 🔍 Fine grained DOM updates for even better performances, components are rendered only when needed
10 | - 🤓 Typed JSX
11 | - 🍰 Easy to learn
12 | - 🤘️ HMR Support for Vite
13 | - 🚀 Super fast hydration support
14 |
15 | ---
16 |
17 | ## Quick example
18 |
19 | ```shell
20 | # Install with npm
21 | npm i reflex-dom
22 | ```
23 |
24 | ```typescript jsx
25 | import { h, render, state } from 'reflex-dom';
26 | export function App (props) {
27 | // State management
28 | const counter = state(0);
29 | const increment = () => counter.value ++;
30 | const reset = () => counter.value = 0;
31 | // Components returns a render function
32 | return () =>
31 | }
32 |
33 | (async function () {
34 | const a =
35 | const c1 = renderToString( a )
36 | await $s1.set( () => getState(5, 20) )
37 | const c2 = renderToString( a )
38 |
39 | console.log( c1 )
40 | console.log("-----")
41 | console.log( c2 )
42 | })()
43 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouloux/reflex-dom/0a0675785267bad989975c945462085d4caf284c/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/api/README.md:
--------------------------------------------------------------------------------
1 | # Reflex API documentation
2 |
3 | ## About
4 |
5 | ### History
6 |
7 | Reflex idea comes from 2018 when React proposed [React Hooks](https://github.com/reactjs/rfcs/pull/68).
8 | After digging hooks for some months, a [lot of people](https://github.com/zouloux/prehook-proof-of-concept/issues/1) talked about the benefits of having a __Factory Phase__ with a render function returned instead of all in one function.
9 | [I proposed a proof of concept of a factory component system](https://github.com/zouloux/prehook-proof-of-concept) based on [Preact](https://github.com/preactjs/preact).
10 | Years after using React hooks (with Preact a lot), I finally had time to get this idea working into a standalone lib ✨
11 |
12 | ### Things missing
13 |
14 | Here is the list of things missing from React :
15 | - React suspense (loading fallback) - is planned
16 | - renderToString - is planned
17 | - React fiber and asynchronous rendering - not planned
18 | - Class components - not planned
19 | - A lot of other stuff
20 |
21 | Things missing from Solid :
22 | - Crazy performances thanks to compiling - not planned
23 | - A lot of other stuff
24 |
25 | Things missing from Preact :
26 | - Not so much I guess?
27 |
28 | ### Performances
29 |
30 | Reflex goal is to be __as performant as possible__ and __as light as possible__.
31 | Reflex will never be as performant than [Solid](https://github.com/solidjs) (because of the Virtual DOM bottleneck), but targets to be more performant than Preact and React in a lot of cases.
32 | Library weight will be around `3kb gzipped`. It may grow a bit over time if we add some useful features. Features not used in your project can be tree-shaken if you use a bundler (like Parcel or Vite).
33 | [See note](./CODEGOLF.md) about code golfing.
34 |
35 | 
36 |
37 | > Check official benchmark on [js-framework-benchmark](https://krausest.github.io/js-framework-benchmark/2022/table_chrome_104.0.5112.79.html)
38 |
39 | For now Reflex performances are between petit-dom and Preact. It can be greatly improved since Reflex is still in beta!
40 | About size, see [Reflex bundle](https://unpkg.com/reflex-dom) vs [Preact bundle](https://unpkg.com/preact) (without states)
41 |
42 | ### Unpkg and Esmsh
43 |
44 | __Reflex__ is available on [Unpkg](https://unpkg.com/reflex-dom) 
45 | - [see unpkg usage example](https://zouloux.github.io/reflex/demos/5-no-bundler-demo/index.html)
46 |
47 | Also available on Esm.sh
48 | - [Esm.sh](https://esm.sh/reflex-dom)
49 |
50 | > Its better to specify used version in your code to avoid code breaking and also for cache and response time.
51 |
52 | ## Roadmap
53 |
54 | #### Done
55 | - [x] A lot of research about how v-dom works
56 | - [x] Actual Virtual dom implementation
57 | - [x] Diffing with complex trees
58 | - [x] Props as dom attributes
59 | - [x] Lifecycle events
60 | - [x] Lifecycle extensions (mounted / unmounted)
61 | - [x] Ref
62 | - [x] Refs
63 | - [x] State
64 | - [x] Automatic memo
65 | - [x] Better performances
66 | - [x] Diff algorithm inspired by [petit-dom](https://github.com/yelouafi/petit-dom/) and Preact
67 | - [x] Props as proxy only if needed (not on functional components)
68 | - [x] SVG support
69 | - [x] renderToString
70 | - [x] JSX Types and runtime
71 | - [x] State invalidation refacto
72 | - [x] New `changed` API which can listen to `states`, `props` and custom handlers with simple API
73 | - [x] Props is not a proxy anymore but a mutated object
74 |
75 | #### Work in progress / TODO
76 | - [ ] WIP - Imperative handles through component instance
77 | - [ ] WIP - Shared ref between parent and child + component ref + refacto component interface for refs and public API
78 | - [ ] WIP - Atomic rendering
79 | - [ ] Crazy performances
80 | - [ ] Hydration
81 | - [ ] Web components integration
82 | - [ ] `npm create reflex-app`
83 | - [ ] Server components streaming
84 | - [ ] Better docs
85 | - [ ] Should update
86 | - [ ] Render to string doc
87 | - [ ] Imperative methods
88 | - [ ] Forward refs and component ref
89 | - [ ] Memo on functional components and shouldUpdate
90 | - [ ] New states
91 | - [ ] Babel examples in doc
92 | - [ ] Code-sandboxes
93 | - [ ] __Release RC-1.0_ and move everything to a Reflex organisation ?
94 |
95 | [//]: # (#### Next, other subjects)
96 | [//]: # (- [ ] Reflex store (compatible with Preact and React))
97 | [//]: # (- [ ] Reflex router (compatible with Preact and React))
98 | [//]: # (- [ ] Reflex Run with SSR ?)
99 | [//]: # (- [ ] Reflex UIKit ?)
100 |
101 |
102 | ### Thanks
103 |
104 | - [xdev1](https://github.com/xdev1) for your feedbacks 🙌 ( and the name of __Factory Extensions__ )
105 | - [Preact](https://github.com/preactjs/preact) for the inspiration and some chunk of codes
106 | - [Petit-DOM](https://github.com/yelouafi/petit-dom) for the inspiration
107 | - [Jason Miller](https://github.com/developit), [Marvin Hagemeister](https://github.com/marvinhagemeister), [Dan Abramov](https://github.com/gaearon), [Sophie Alpert](https://github.com/sophiebits) for your amazing ideas 🙏
108 |
--------------------------------------------------------------------------------
/docs/api/_assets/prism-bash.js:
--------------------------------------------------------------------------------
1 | !function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",a={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},n={bash:a,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},parameter:{pattern:/(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/,alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:n},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:a}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:n},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:n.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:n.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},a.inside=e.languages.bash;for(var s=["comment","function-name","for-or-select","assign-left","parameter","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=n.variable[1].inside,i=0;i|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript;
--------------------------------------------------------------------------------
/docs/api/_assets/prism-json.js:
--------------------------------------------------------------------------------
1 | Prism.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},Prism.languages.webmanifest=Prism.languages.json;
--------------------------------------------------------------------------------
/docs/api/_assets/prism-jsx.js:
--------------------------------------------------------------------------------
1 | !function(t){var n=t.util.clone(t.languages.javascript),e="(?:\\{*\\.{3}(?:[^{}]|)*\\})";function a(t,n){return t=t.replace(//g,(function(){return"(?:\\s|//.*(?!.)|/\\*(?:[^*]|\\*(?!/))\\*/)"})).replace(//g,(function(){return"(?:\\{(?:\\{(?:\\{[^{}]*\\}|[^{}])*\\}|[^{}])*\\})"})).replace(//g,(function(){return e})),RegExp(t,n)}e=a(e).source,t.languages.jsx=t.languages.extend("markup",n),t.languages.jsx.tag.pattern=a("?(?:[\\w.:-]+(?:+(?:[\\w.:$-]+(?:=(?:\"(?:\\\\[^]|[^\\\\\"])*\"|'(?:\\\\[^]|[^\\\\'])*'|[^\\s{'\"/>=]+|))?|))**/?)?>"),t.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,t.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,t.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,t.languages.jsx.tag.inside.comment=n.comment,t.languages.insertBefore("inside","attr-name",{spread:{pattern:a(""),inside:t.languages.jsx}},t.languages.jsx.tag),t.languages.insertBefore("inside","special-attr",{script:{pattern:a("="),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:t.languages.jsx}}},t.languages.jsx.tag);var s=function(t){return t?"string"==typeof t?t:"string"==typeof t.content?t.content:t.content.map(s).join(""):""},g=function(n){for(var e=[],a=0;a0&&e[e.length-1].tagName===s(o.content[0].content[1])&&e.pop():"/>"===o.content[o.content.length-1].content||e.push({tagName:s(o.content[0].content[1]),openedBraces:0}):e.length>0&&"punctuation"===o.type&&"{"===o.content?e[e.length-1].openedBraces++:e.length>0&&e[e.length-1].openedBraces>0&&"punctuation"===o.type&&"}"===o.content?e[e.length-1].openedBraces--:i=!0),(i||"string"==typeof o)&&e.length>0&&0===e[e.length-1].openedBraces){var r=s(o);a0&&("string"==typeof n[a-1]||"plain-text"===n[a-1].type)&&(r=s(n[a-1])+r,n.splice(a-1,1),a--),n[a]=new t.Token("plain-text",r,null,r)}o.content&&"string"!=typeof o.content&&g(o.content)}};t.hooks.add("after-tokenize",(function(t){"jsx"!==t.language&&"tsx"!==t.language||g(t.tokens)}))}(Prism);
--------------------------------------------------------------------------------
/docs/api/_assets/prism-markup.js:
--------------------------------------------------------------------------------
1 | Prism.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",(function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))})),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^$/i;var t={"included-cdata":{pattern://i,inside:s}};t["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp("(<__[^>]*>)(?:))*\\]\\]>|(?!)".replace(/__/g,(function(){return a})),"i"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(a,e){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+a+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[e,"language-"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml;
--------------------------------------------------------------------------------
/docs/api/_assets/prism-tsx.js:
--------------------------------------------------------------------------------
1 | !function(e){var a=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",a),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var t=e.languages.tsx.tag;t.pattern=RegExp("(^|[^\\w$]|(?=))(?:"+t.pattern.source+")",t.pattern.flags),t.lookbehind=!0}(Prism);
--------------------------------------------------------------------------------
/docs/api/_assets/prism-typescript.js:
--------------------------------------------------------------------------------
1 | !function(e){e.languages.typescript=e.languages.extend("javascript",{"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var s=e.languages.extend("typescript",{});delete s["class-name"],e.languages.typescript["class-name"].inside=s,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:s}}}}),e.languages.ts=e.languages.typescript}(Prism);
--------------------------------------------------------------------------------
/docs/api/_assets/vue.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");*{-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:none;-webkit-touch-callout:none;box-sizing:border-box}body:not(.ready){overflow:hidden}body:not(.ready) .app-nav,body:not(.ready)>nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}.emoji{height:1.2rem;vertical-align:middle}.progress{background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:999999}.search a:hover{color:var(--theme-color,#42b983)}.search .search-keyword{color:var(--theme-color,#42b983);font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}li input[type=checkbox]{margin:0 .2em .25em 0;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:10}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a:hover{color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid var(--theme-color,#42b983);color:var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative;cursor:pointer}.app-nav li ul{background-color:#fff;border:1px solid;border-color:#ddd #ddd #ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}.github-corner svg{color:#fff;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:20}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0 0 0 15px;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53.3%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53.3%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:30;cursor:pointer}.sidebar-toggle:hover .sidebar-toggle-button{opacity:.4}.sidebar-toggle span{background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:80%;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee;width:1px;min-width:100%}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{background-color:#f8f8f8;border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}.markdown-section ul.task-list>li{list-style-type:none}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;min-height:100vh;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{-webkit-animation:none;animation:none}.github-corner .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}}@-webkit-keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;width:100vw;display:none}section.cover.show{display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;height:100%;width:100%}section.cover .cover-main{flex:1;margin:-20px 16px 0;text-align:center;position:relative}section.cover a{color:inherit;text-decoration:none}section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-radius:2rem;border:1px solid var(--theme-color,#42b983);box-sizing:border-box;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code,.markdown-section output:after,.markdown-section pre{font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section code,.markdown-section pre{background-color:#f8f8f8}.markdown-section output,.markdown-section pre{margin:1.2em 0;position:relative}.markdown-section output,.markdown-section pre>code{border-radius:2px;display:block}.markdown-section output:after,.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial}.markdown-section code{border-radius:2px;color:#e96900;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section>:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6) code{font-size:.8rem}.markdown-section pre{padding:0 1.4rem;line-height:1.5rem;overflow:auto;word-wrap:normal}.markdown-section pre>code{color:#525252;font-size:.8rem;padding:2.2em 5px;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;white-space:inherit}.markdown-section output{padding:1.7rem 1.4rem;border:1px dotted #ccc}.markdown-section output>:first-child{margin-top:0}.markdown-section output>:last-child{margin-bottom:0}.markdown-section code:after,.markdown-section code:before,.markdown-section output:after,.markdown-section output:before{letter-spacing:.05rem}.markdown-section output:after,.markdown-section pre:after{color:#ccc;font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0}.markdown-section output:after,.markdown-section pre:after{content:attr(data-lang)}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:var(--theme-color,#42b983)}.token.function,.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem;position:relative;left:auto}
--------------------------------------------------------------------------------
/docs/api/_images/benchmark.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouloux/reflex-dom/0a0675785267bad989975c945462085d4caf284c/docs/api/_images/benchmark.jpg
--------------------------------------------------------------------------------
/docs/api/_images/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouloux/reflex-dom/0a0675785267bad989975c945462085d4caf284c/docs/api/_images/example.gif
--------------------------------------------------------------------------------
/docs/api/_sidebar.md:
--------------------------------------------------------------------------------
1 |
2 | - [What is reflex](/)
3 |
4 | - Basics
5 | - [Core Concept](/basics/00.core-concept.md)
6 | - [Simple DOM rendering](/basics/01.simple-dom-rendering.md)
7 | - [Stateless components](/basics/02.stateless-components.md)
8 | - [Stateful components](/basics/03.stateful-components.md)
9 | - [Props](/basics/04.props.md)
10 |
11 | - Factory Extensions
12 | - [State](/factory-extensions/01.state.md)
13 | - [Ref](/factory-extensions/02.ref.md)
14 | - [Refs aka Multi-Refs](/factory-extensions/03.refs.md)
15 | - [Mount and Unmount](/factory-extensions/04.mount-unmount.md)
16 | - [Changed](/factory-extensions/05.changed.md)
17 | - [Custom Extensions](/factory-extensions/06.custom-extensions.md)
18 |
19 | - More about reflex
20 | - [Performances](/reflex/00.performances.md)
21 | - [Features](/reflex/01.features.md)
22 | - [History](/reflex/02.history.md)
23 | ---
24 |
25 | - [Learn Reflex in 5 minutes ↗︎](https://zouloux.github.io/reflex/learn/)
26 | - [Github ↗︎](https://github.com/zouloux/reflex)
27 |
28 | [//]: # (- [Get started with a new project](/get-started/))
29 |
30 |
--------------------------------------------------------------------------------
/docs/api/basics/00.core-concept.md:
--------------------------------------------------------------------------------
1 |
2 | ## API Concept
3 |
4 | ```typescript jsx
5 | return () =>
6 | ```
7 |
8 | **Stateful components** will return a __render function__ instead of virtual-nodes directly.
9 | Scope is shared between the factory and the render function.
10 |
11 | ```tsx
12 | function StatefulComponent ( props ) {
13 | // Factory extensions and component logic goes here ( factory phase )
14 | // This part is only executed once, when component is created.
15 | const number = state( 0 )
16 | // ...
17 | // Render function returning node tree goes there ( in a function )
18 | return () =>
Current number is { number }
19 | }
20 | ```
21 |
22 | Reflex allows **functional pattern** for **stateless components**, like React and Preact.
23 |
24 | ```tsx
25 | function StatelessComponent ( props ) {
26 | // No state ? No need for factory function here
27 | return
Hello { props.name }
28 | }
29 | ```
30 |
31 | #### Improvements 👍
32 | - __Simpler__ : Classic React Hooks like `useCallback`, `useEvent` and `useMemo` becomes __useless__ and does not exist in __Reflex__.
33 | - __Fewer bugs__ : [Stale closure issues](https://dmitripavlutin.com/react-hooks-stale-closures/) vanishes.
34 | - __Cleaner__ : Also, hooks dependencies array to keep state scopes ([#1](https://itnext.io/how-to-work-with-intervals-in-react-hooks-f29892d650f2), [#2](https://overreacted.io/a-complete-guide-to-useeffect/)) are not needed with __[factory extensions](#factory-extensions)__.
35 | - __Back to basics__ : Using `refs` to store stateless values does not exist anymore. In __Reflex__, `ref` is used only to target dom node or components, `let` is used to declare local variables like it would normally do.
36 |
37 | #### Tradeoffs 👎
38 | - __Stateless vs stateful__ : When a component is evolving from stateless to stateful, the `return
...` needs to be refactored to `return () =>
...`. But stateless components **can** be implemented with factory function.
39 | - __Props__ : Props cannot be destructured in the factory phase [because props is mutated](#props)
40 | - Surely more but I got biases :)
41 |
--------------------------------------------------------------------------------
/docs/api/basics/01.simple-dom-rendering.md:
--------------------------------------------------------------------------------
1 |
2 | ## Simple DOM rendering
3 |
4 | ```tsx
5 | // Import Reflex like you would import Preact for example.
6 | import { h, render } from "reflex";
7 | // Optionnaly type props with typescript
8 | interface IAppProps {
9 | greetings:string
10 | }
11 | // App is a stateless function, no need for factory pattern
12 | function App ( props:IAppProps ) {
13 | return
14 |
{ props.greetings }
15 |
16 | }
17 |
18 | render( , document.body )
19 | // Note : if you call render again, it will update state of previously rendered app
20 | render( , document.body )
21 | ```
22 |
--------------------------------------------------------------------------------
/docs/api/basics/02.stateless-components.md:
--------------------------------------------------------------------------------
1 |
2 | ## Stateless and pure components
3 |
4 | Stateless, or components without logic can avoid the factory pattern. Simply return the virtual-dom tree derived from props like you would do it in React or Preact.
5 | Stateless functions are [__pure__](https://en.wikipedia.org/wiki/Pure_function), which means it has always the same output (returned elements) for a specific input (props).
6 | ```tsx
7 | function StatelessComponent ( props ) {
8 | return
9 | Hello { props.name } 👋
10 |
11 | }
12 | ```
13 |
14 | > Because Stateless and Stateful components are written differently, Reflex can optimize render of Stateless components by keeping old virtual-node tree, if props did not change between renders. We have better performances without adding any logic to our app.
15 |
16 | > Also, `shouldUpdate` can be set by component (Stateful or Stateless), if you have specific optimization logic to avoid useless renders.
17 |
--------------------------------------------------------------------------------
/docs/api/basics/03.stateful-components.md:
--------------------------------------------------------------------------------
1 |
2 | ## Stateful components with factory pattern
3 |
4 | This is where it changes from React. Stateful components in Reflex follows the __Factory Component Pattern__. __[Factory extensions](#factory-extensions)__ are used __only__ in the "factory phase" of the component.
5 |
6 | ```tsx
7 | function StatefulComponent ( props ) {
8 | // This is the "factory phase"
9 | // This part of the component is executed once, when component is created, and not at each render.
10 |
11 | // Create a state for this component, like in React or Solid
12 | const currentNumber = state( 0 )
13 | const incrementNumber = () => currentNumber.value ++
14 |
15 | // The component needs to return a function which will render the component
16 | return () =>
17 | {/* Update state when button is clicked */}
18 |
21 |
22 | }
23 | ```
24 |
25 | > Stateful components are not __pure__. Output differs even with the same props as input.
26 |
--------------------------------------------------------------------------------
/docs/api/basics/04.props.md:
--------------------------------------------------------------------------------
1 |
2 | ## Props
3 |
4 | In Stateful components, `props` is an object which is mutated from props given by parent component. Because factory phase is executed once at component's creation (like in Solid).
5 |
6 | ```tsx
7 | function PropsComponent ( props ) {
8 | function logName () {
9 | // ✅ Will log latest name, even if parent component changed the props
10 | console.log( props.name )
11 | }
12 | return () =>
13 |
14 |
15 | }
16 | ```
17 |
18 | > The main tradeoff is that props destructuring is not possible anymore. Or destructed props will be equal to the first props value and will never change.
19 |
20 | ```tsx
21 | function PropsComponent ( props ) {
22 | // 🚫 Here name will never change even if the component is updated by its parent
23 | const { name } = props
24 | function logName () {
25 | console.log( name )
26 | }
27 | return () =>
28 | }
29 | ```
30 |
31 | ## Default props
32 |
33 | Default props are configurable in factory and pure functional components the same way, using `defaultProps`.
34 |
35 | ```tsx
36 |
37 | // For stateful components
38 | function FactoryComponent (props, component) {
39 | defaultProps(props, {
40 | title: "Default title"
41 | })
42 | console.log("Factory", props.title)
43 | return () =>
{ props.title }
44 | }
45 |
46 | // Also available in Stateless components
47 | function PureComponent ( props ) {
48 | defaultProps(props, {
49 | title: "Default title"
50 | })
51 | console.log("Render", props.title)
52 | return
{ props.title }
53 | }
54 | ```
55 |
56 | ## CSS classes as array
57 |
58 | CSS classes can be set as an array. Falsy values will be automatically filtered out.
59 |
60 | ```tsx
61 | function PureComponent ( props ) {
62 | const classes = [
63 | "PureComponent",
64 | props.modifier ? `PureComponent-${props.modifier}` : null,
65 | props.disabled && "disabled",
66 | ...props.classes
67 | ]
68 | return
69 | }
70 | // Will have class="PureComponent PureComponent-big ParentComponent_pureComponent"
71 | // Disabled is filtered out because props.disabled is not defined
72 | const component =
76 | ```
77 |
--------------------------------------------------------------------------------
/docs/api/basics/05.forward-ref.md:
--------------------------------------------------------------------------------
1 |
2 | ### Automatic forwardRef
3 |
4 | When attaching a ref from inside the component, and from the parent, it will just work as expected.
5 |
6 | > No need for forward ref
7 |
8 | ```tsx
9 | function Child () {
10 | // Works, will have component instance and div element
11 | const root = ref()
12 | return () =>
13 | }
14 | function Parent () {
15 | // Also works without forwardRef
16 | // will have component instance and div element
17 | const child = ref()
18 | return () =>
19 |
20 |
21 | }
22 | ```
23 | > /!\ This feature is WIP and will certainly change in RC
24 |
--------------------------------------------------------------------------------
/docs/api/basics/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouloux/reflex-dom/0a0675785267bad989975c945462085d4caf284c/docs/api/basics/README.md
--------------------------------------------------------------------------------
/docs/api/factory-extensions/01.state.md:
--------------------------------------------------------------------------------
1 |
2 | ## State
3 |
4 | ```tsx
5 | // Create a new state
6 | const myState = state( initialState )
7 |
8 | // Get current state value
9 | console.log( myState.value )
10 |
11 | // Set new value (will trigger a component update)
12 | myState.value = newValue
13 | // -> Dom not updated yet
14 |
15 | // After setting using .value, component is not refreshed instantanously
16 | // Use .set to wait for component invalidation
17 | await myState.set( newValue )
18 | // -> Now the dom is updated
19 | ```
20 |
21 | > __Note__ : setting a new state is asynchronous because all state changes of a component are stacked and component renders only once for better performances.
22 | > After the component is refreshed, the `await state.set( value )` promise will be resolved.
23 |
24 | Additional options for state are
25 | - `filter` to change value when set, useful to avoid invalid values
26 | - `directInvalidation` is called after the associated component is rendered
27 |
28 | ```typescript
29 | const myState = state( 0, {
30 | // Value is filtered when "myState.value = something" or "myState.set( something )" is used.
31 | // Here value cannot be set bellow 0
32 | filter: (newValue, oldValue) => Math.max( newValue, 0 ),
33 | // Will force component to be rendered after each set
34 | // It can be useful if you need sync data rendering
35 | // But be careful about data flow, it can destroy performances in loops !
36 | directInvalidation: true
37 | })
38 |
39 | // Value is 0 and DOM is directly updated
40 | myState.value = -2
41 |
42 | // Because of directInvalidation, it will render component at each loop and your app will certainly crash
43 | for ( let i = 0; i < 10000; ++i)
44 | myState.value ++
45 | ```
46 |
--------------------------------------------------------------------------------
/docs/api/factory-extensions/02.ref.md:
--------------------------------------------------------------------------------
1 |
2 | ## Ref
3 |
4 | Like in React, we can use ref to target rendered components.
5 |
6 | ```tsx
7 | function MyComponent () {
8 | // Create a new ref
9 | const otherComponentRef = ref()
10 |
11 | function showref () {
12 | // Log component dom element
13 | console.log('DOM', otherComponentRef.dom )
14 | // Log component instance
15 | console.log('Component', otherComponentRef.component )
16 | }
17 |
18 | return () =>
19 |
20 |
21 |
22 | }
23 | ```
24 |
25 | > The main difference with React is that ref are useless to create locally scoped component variables.
26 |
27 | To create a locally scoped prop that will not trigger rendering, just use `let`
28 |
29 | ```tsx
30 | function MyComponent () {
31 | // Create a local variable (no need for ref here)
32 | let localVariable = 0
33 |
34 | function updateLocalVariable () {
35 | // Update this variable will not trigger a render
36 | localVariable ++
37 | console.log( localVariable );
38 | }
39 |
40 | return () =>
41 |
42 |
43 | }
44 | ```
45 |
--------------------------------------------------------------------------------
/docs/api/factory-extensions/03.refs.md:
--------------------------------------------------------------------------------
1 |
2 | ## Refs aka Multi-Ref
3 |
4 | Multi ref in Reflex is `ref` as an array of components. Very handy when dealing with lists!
5 |
6 | ```tsx
7 | function List ( props ) {
8 | // Create ref for list
9 | const itemRefs = refs()
10 |
11 | function showListItemElements () {
12 | // Will be an array of all refs
13 | console.log( itemsRefs.list );
14 | }
15 |
16 | return () =>
17 | {props.items.map( item =>
18 |
{item.name}
19 | )}
20 |
21 | }
22 | ```
23 |
24 | > Refs are populated in order of rendering. So if you are using a list which can render in another order than from 0 to length, you can specify the index ( [see example](./demos/common/CodeViewer/CodeViewer.tsx) )
25 |
26 | ```tsx
27 | function List ( props ) {
28 | const itemRefs = refs()
29 | return () =>
30 | {props.items.map( (item, i) =>
31 | // Here item.ready can render elements in the wrong order
32 | // refs.atIndex( index ) will force index and patch this issue
33 | item.ready &&
{item.name}
34 | )}
35 |
36 | }
37 | ```
38 |
39 |
--------------------------------------------------------------------------------
/docs/api/factory-extensions/04.mount-unmount.md:
--------------------------------------------------------------------------------
1 |
2 | ## Mounted and Unmounted
3 |
4 | Pretty self-explanatory, will be called when mounting or unmounting the component.
5 |
6 | ```tsx
7 | function MountUnmount ( props ) {
8 | const root = ref()
9 |
10 | mounted(() => {
11 | console.log("Component just mounted, refs are ready.", root.dom)
12 | // Can return an unmount function
13 | return () => {
14 | console.log("Will be called just before component unmount.", root.dom)
15 | }
16 | })
17 |
18 | unmounted( () => {
19 | console.log("Will be called just before component unmount.", root.dom)
20 | })
21 |
22 | return () =>
...
23 | }
24 | ```
25 |
26 | > mounted can also return an array of handlers.
27 |
28 |
29 | ```tsx
30 |
31 | // Note, here on() returns the remove handler
32 | // @see https://github.com/zouloux/yadl
33 |
34 | mounted(() => {
35 | return [
36 | // Listen clicks and remove listen on unmount
37 | on(document, 'click', clickedHandler),
38 | // will be filtered out if false
39 | doIt && on(window, 'resize', resizeHandler),
40 | // called on unmount
41 | function () {
42 | }
43 | ]
44 | })
45 |
46 | ```
--------------------------------------------------------------------------------
/docs/api/factory-extensions/05.changed.md:
--------------------------------------------------------------------------------
1 |
2 | ## Changed
3 |
4 | __Changed__ factory hook is useful to detect changes into a component. With only one handler as argument, it will be called after each component render.
5 |
6 | ```tsx
7 | function ChangedComponent ( props ) {
8 | const root = ref()
9 | const number = state(0)
10 | changed(() => {
11 | // Called after each render
12 | // Ref and state are available
13 | console.log("Component updated", root.dom, number.value)
14 | })
15 | return () =>
16 |
19 |
20 | }
21 | ```
22 |
23 | __Changed__ can have a first argument to detect changes state changes.
24 |
25 | ```tsx
26 | function ChangedComponent ( props ) {
27 | const stateA = state()
28 | const stateB = state()
29 | // This function detect changes only when stateA changes. stateB is ignored.
30 | changed( [stateA], (aValue) => {
31 | // StateA is updated
32 | console.log( aValue )
33 | })
34 | return () =>
...
35 | }
36 | ```
37 | > Because we are targeting the state here, we do not need to specify `stateA.value`
38 |
39 | Changed can also detect changes on arbitrary values or props.
40 | The detect function returns an array of dependencies to check.
41 |
42 | ```tsx
43 | function ChangedComponent ( props ) {
44 | const stateA = state()
45 | const stateB = state()
46 | changed(
47 | // The function detect changes in stateA and props.name, stateB is ignored
48 | () => [stateA.value, props.name],
49 | // Called when change is detected in stateA OR props.name
50 | // Both new state and old state values are concatenated into arguments
51 | // new array | old array //
52 | (newStateA, newPropsName, oldStateA, oldPropsName) => {
53 | // Values array here are the last and previous returned array
54 | // Useful to detect changes, or pub-sub any other component or model
55 | console.log( newStateA, newPropsName, oldStateA, oldPropsName )
56 | }
57 | )
58 | return () =>
...
59 | }
60 | ```
61 |
62 | > Because we are in __Factory phase__, raw props or values can't be used directly. __Note__ that the check function __always returns an array__.
63 |
64 |
65 | __Changed__ handler has the same return behavior than `mount` and `unmount`.
66 |
67 | ```tsx
68 | function ChangedComponent ( props ) {
69 | const state = state()
70 | changed( () => [state.value], newValue => {
71 | // After change and at first render
72 | console.log("New value", newValue)
73 | return oldValue => {
74 | // Before change and before unmount
75 | console.log("Old value", oldValue)
76 | }
77 | })
78 | return () =>
...
79 | }
80 | ```
81 |
--------------------------------------------------------------------------------
/docs/api/factory-extensions/06.custom-extensions.md:
--------------------------------------------------------------------------------
1 | ### Custom extensions
2 |
3 | Like React Hooks, extensions can be used externally and can compose other extensions.
4 |
5 | ?> Here is an example for a frameEntered hook
6 |
7 | ```tsx
8 | export function frameEntered ( handler:() => void ) {
9 | let isMounted = true
10 | let frameHandler;
11 | function frame () {
12 | if ( !isMounted ) return;
13 | handler()
14 | frameHandler = window.requestAnimationFrame( frame )
15 | }
16 | mounted(() => {
17 | frame();
18 | // Unmount
19 | return () => {
20 | cancelAnimationFrame( frameHandler )
21 | isMounted = false
22 | }
23 | })
24 | }
25 |
26 | ```
27 |
28 | > And its usage
29 |
30 | ```tsx
31 | export function AnimatedComponent () {
32 | const $base = ref()
33 | let frameCounted = 0
34 | frameEntered(() => {
35 | // This handler is called at each frame while mounted
36 | frameCounted ++
37 | // Shake this component on the X axis
38 | const position = Math.sin( frameCounted / 20 ) * 2
39 | $base.style.transform = `translateX(${position}px)`
40 | })
41 | return () =>
42 | (((( Shaking ))))
43 |
44 | }
45 | ```
--------------------------------------------------------------------------------
/docs/api/factory-extensions/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Factory extensions
3 |
4 | Here is a list of all base __factory extensions__ available.
5 | Remember, __factory extensions__ are only usable in __factory phase__ of components and not available in Stateless components.
6 | Like in React, __factory extensions__ are composable into other functions easily.
7 |
8 |
9 | ```tsx
10 | function StatefulComponent () {
11 | // ✅ Can use extensions here
12 | state(...)
13 | changed(...)
14 | mounted(...)
15 | unmounted(...)
16 |
17 | return () => {
18 | // ⛔️ Cannot use extensions here
19 | }
20 | }
21 |
22 | function StatelessComponent () {
23 | // ⛔️ Cannot use extensions here
24 | return