omis - init
This commit is contained in:
parent
9475c989a9
commit
b54bef42d4
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"loose": true,
|
||||
"exclude": ["transform-es2015-typeof-symbol"],
|
||||
"targets": {
|
||||
"browsers": ["last 2 versions", "IE >= 9"]
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"transform-class-properties",
|
||||
["transform-react-jsx", { "pragma": "Omi.h" }]
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[{package.json,.*rc,*.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
|
@ -0,0 +1,2 @@
|
|||
examples/*/b.js
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"parser": "babel-eslint",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"semi": 2,
|
||||
"indent": ["error", 2]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
[ignore]
|
||||
<PROJECT_ROOT>/dist/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
|
||||
[lints]
|
||||
|
||||
[options]
|
|
@ -0,0 +1,3 @@
|
|||
# Omis
|
||||
|
||||
> Web Components Supporting + Scoped Style + Store + Function Component
|
|
@ -0,0 +1,43 @@
|
|||
/* eslint no-console:0 */
|
||||
|
||||
/** Find constants (identified by ALL_CAPS_DECLARATIONS), and inline them globally.
|
||||
* This is safe because Omi *only* uses global constants.
|
||||
*/
|
||||
export default (file, api) => {
|
||||
let j = api.jscodeshift,
|
||||
code = j(file.source),
|
||||
constants = {},
|
||||
found = 0;
|
||||
|
||||
code
|
||||
.find(j.VariableDeclaration)
|
||||
.filter(decl => {
|
||||
for (let i = decl.value.declarations.length; i--; ) {
|
||||
let node = decl.value.declarations[i],
|
||||
name = node.id && node.id.name,
|
||||
init = node.init;
|
||||
if (name && init && name.match(/^[A-Z0-9_$]+$/g) && !init.regex) {
|
||||
if (init.type === "Literal") {
|
||||
// console.log(`Inlining constant: ${name}=${init.raw}`);
|
||||
found++;
|
||||
constants[name] = init;
|
||||
// remove declaration
|
||||
decl.value.declarations.splice(i, 1);
|
||||
// if it's the last, we'll remove the whole statement
|
||||
return !decl.value.declarations.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.remove();
|
||||
|
||||
code
|
||||
.find(j.Identifier)
|
||||
.filter(
|
||||
path => path.value.name && constants.hasOwnProperty(path.value.name)
|
||||
)
|
||||
.replaceWith(path => (found++, constants[path.value.name]));
|
||||
|
||||
return found ? code.toSource({ quote: "single" }) : null;
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Restores var names transformed by babel's let block scoping
|
||||
*/
|
||||
export default (file, api) => {
|
||||
let j = api.jscodeshift;
|
||||
let code = j(file.source);
|
||||
|
||||
// @TODO unsafe, but without it we gain 20b gzipped: https://www.diffchecker.com/bVrOJWTO
|
||||
code
|
||||
.findVariableDeclarators()
|
||||
.filter(d => /^_i/.test(d.value.id.name))
|
||||
.renameTo("i");
|
||||
code.findVariableDeclarators("_key").renameTo("key");
|
||||
|
||||
return code.toSource({ quote: "single" });
|
||||
};
|
|
@ -0,0 +1,62 @@
|
|||
/* eslint no-console:0 */
|
||||
|
||||
// parent node types that we don't want to remove pointless initializations from (because it breaks hoisting)
|
||||
const BLOCKED = ["ForStatement", "WhileStatement"]; // 'IfStatement', 'SwitchStatement'
|
||||
|
||||
/** Removes var initialization to `void 0`, which Babel adds for TDZ strictness. */
|
||||
export default (file, api) => {
|
||||
let { jscodeshift } = api,
|
||||
found = 0;
|
||||
|
||||
let code = jscodeshift(file.source)
|
||||
.find(jscodeshift.VariableDeclaration)
|
||||
.forEach(handleDeclaration);
|
||||
|
||||
function handleDeclaration(decl) {
|
||||
let p = decl,
|
||||
remove = true;
|
||||
|
||||
while ((p = p.parentPath)) {
|
||||
if (~BLOCKED.indexOf(p.value.type)) {
|
||||
remove = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
decl.value.declarations.filter(isPointless).forEach(node => {
|
||||
if (remove === false) {
|
||||
console.log(
|
||||
`> Skipping removal of undefined init for "${node.id.name}": within ${
|
||||
p.value.type
|
||||
}`
|
||||
);
|
||||
} else {
|
||||
removeNodeInitialization(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeNodeInitialization(node) {
|
||||
node.init = null;
|
||||
found++;
|
||||
}
|
||||
|
||||
function isPointless(node) {
|
||||
let { init } = node;
|
||||
if (init) {
|
||||
if (
|
||||
init.type === "UnaryExpression" &&
|
||||
init.operator === "void" &&
|
||||
init.argument.value == 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (init.type === "Identifier" && init.name === "undefined") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return found ? code.toSource({ quote: "single" }) : null;
|
||||
};
|
|
@ -0,0 +1,66 @@
|
|||
module.exports = {
|
||||
parser: "babel-eslint",
|
||||
// extends: "eslint:recommended",
|
||||
// plugins: ["react"],
|
||||
"extends": ["prettier"],
|
||||
"plugins": ["prettier"],
|
||||
env: {
|
||||
browser: true,
|
||||
mocha: true,
|
||||
node: true,
|
||||
es6: true
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
modules: true,
|
||||
jsx: true
|
||||
}
|
||||
},
|
||||
globals: {
|
||||
sinon: true,
|
||||
expect: true
|
||||
},
|
||||
rules: {
|
||||
"prettier/prettier": "error",
|
||||
"no-unused-vars": [1, { varsIgnorePattern: "^h$" }],
|
||||
"no-cond-assign": 1,
|
||||
"no-empty": 0,
|
||||
"no-console": 1,
|
||||
semi: [1, "never"],
|
||||
camelcase: 0,
|
||||
"comma-style": 2,
|
||||
"comma-dangle": [2, "never"],
|
||||
indent: ["error", 2],
|
||||
"no-mixed-spaces-and-tabs": [2, "smart-tabs"],
|
||||
"no-trailing-spaces": [2, { skipBlankLines: true }],
|
||||
"max-nested-callbacks": [2, 3],
|
||||
"no-eval": 2,
|
||||
"no-implied-eval": 2,
|
||||
"no-new-func": 2,
|
||||
"guard-for-in": 0,
|
||||
eqeqeq: 0,
|
||||
"no-else-return": 2,
|
||||
"no-redeclare": 2,
|
||||
"no-dupe-keys": 2,
|
||||
radix: 2,
|
||||
strict: [2, "never"],
|
||||
"no-shadow": 0,
|
||||
"callback-return": [1, ["callback", "cb", "next", "done"]],
|
||||
"no-delete-var": 2,
|
||||
"no-undef-init": 2,
|
||||
"no-shadow-restricted-names": 2,
|
||||
"handle-callback-err": 0,
|
||||
"no-lonely-if": 2,
|
||||
"keyword-spacing": 2,
|
||||
"constructor-super": 2,
|
||||
"no-this-before-super": 2,
|
||||
"no-dupe-class-members": 2,
|
||||
"no-const-assign": 2,
|
||||
"prefer-spread": 2,
|
||||
"no-useless-concat": 2,
|
||||
"no-var": 2,
|
||||
"object-shorthand": 2,
|
||||
"prefer-arrow-callback": 2,
|
||||
"quotes": [1, "single"]
|
||||
}
|
||||
};
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"props": {
|
||||
"cname": 42,
|
||||
"props": {
|
||||
"$_dirty": "__d",
|
||||
"$_disable": "__x",
|
||||
"$_listeners": "__l",
|
||||
"$_renderCallbacks": "__h",
|
||||
"$__key": "__k",
|
||||
"$__ref": "__r",
|
||||
"$normalizedNodeName": "__n",
|
||||
"$nextBase": "__b",
|
||||
"$prevContext": "__c",
|
||||
"$prevProps": "__p",
|
||||
"$prevState": "__s",
|
||||
"$_parentComponent": "__u",
|
||||
"$_componentConstructor": "_componentConstructor",
|
||||
"$__html": "__html",
|
||||
"$_component": "_component",
|
||||
"$__omiattr_": "__omiattr_",
|
||||
"$_preStyle": "r",
|
||||
"$_id": "s",
|
||||
"$__preactattr_": "t",
|
||||
"$__config__": "v",
|
||||
"$_isMockDocument": "w",
|
||||
"$__omiSsrData": "x",
|
||||
"$_vd": "y",
|
||||
"$_css": "z",
|
||||
"$_proxifyObjectTreeRecursively": "A",
|
||||
"$_isInstalled": "B",
|
||||
"$_useId": "C",
|
||||
"$_useMap": "D",
|
||||
"$___touchX": "F",
|
||||
"$___touchY": "G",
|
||||
"$___scrollTop": "H",
|
||||
"$__elementId": "I",
|
||||
"$_willUpdate": "J",
|
||||
"$_preCss": "K",
|
||||
"$_host": "L",
|
||||
"$_updatePath": "M",
|
||||
"$_customStyleElement": "N",
|
||||
"$_customStyleContent": "O",
|
||||
"$__hasChildren": "P",
|
||||
"$__prevProps": "Q"
|
||||
}
|
||||
},
|
||||
"vars": {
|
||||
"cname": -1,
|
||||
"props": {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import config from "./rollup.config";
|
||||
|
||||
// ES output
|
||||
config.output.format = "es";
|
||||
config.output.file = "dist/omi.esm.js";
|
||||
|
||||
// remove memory() plugin
|
||||
config.plugins.splice(0, 1);
|
||||
|
||||
export default config;
|
|
@ -0,0 +1,58 @@
|
|||
import nodeResolve from "rollup-plugin-node-resolve";
|
||||
import babel from "rollup-plugin-babel";
|
||||
import memory from "rollup-plugin-memory";
|
||||
|
||||
const license = require("rollup-plugin-license");
|
||||
const pkg = require("../package.json");
|
||||
const licensePlugin = license({
|
||||
banner:
|
||||
" omi v" +
|
||||
pkg.version +
|
||||
" http://omijs.org\r\nOmi === Preact + Scoped CSS + Store System + Native Support in 3kb javascript.\r\nBy dntzhang https://github.com/dntzhang \r\n Github: https://github.com/Tencent/omi\r\n MIT Licensed."
|
||||
});
|
||||
|
||||
export default {
|
||||
input: "src/omi.js",
|
||||
output: {
|
||||
format: "iife",
|
||||
file: "dist/omi.dev.js",
|
||||
name: "omi",
|
||||
sourcemap: true,
|
||||
strict: true
|
||||
},
|
||||
plugins: [
|
||||
memory({
|
||||
path: "src/omi.js",
|
||||
contents: `
|
||||
import Omi from './omi';
|
||||
if (typeof module!='undefined') module.exports = Omi;
|
||||
else self.Omi = Omi;
|
||||
`
|
||||
}),
|
||||
nodeResolve({
|
||||
main: true
|
||||
}),
|
||||
babel({
|
||||
sourceMap: true,
|
||||
exclude: "node_modules/**",
|
||||
babelrc: false,
|
||||
presets: [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
modules: false,
|
||||
loose: true,
|
||||
exclude: ["transform-es2015-typeof-symbol"],
|
||||
targets: {
|
||||
browsers: ["last 2 versions", "IE >= 9"]
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
plugins: [
|
||||
"transform-class-properties"
|
||||
]
|
||||
}),
|
||||
licensePlugin
|
||||
]
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
import nodeResolve from "rollup-plugin-node-resolve";
|
||||
import babel from "rollup-plugin-babel";
|
||||
import memory from "rollup-plugin-memory";
|
||||
import commonjs from "rollup-plugin-commonjs";
|
||||
|
||||
var ENV = process.env.npm_lifecycle_event;
|
||||
|
||||
export default {
|
||||
input: "examples/" + ENV + "/main.js",
|
||||
output: {
|
||||
format: "iife",
|
||||
file: "examples/" + ENV + "/b.js",
|
||||
name: "omi",
|
||||
sourcemap: true,
|
||||
strict: true
|
||||
},
|
||||
plugins: [
|
||||
memory({
|
||||
path: "src/omi.js",
|
||||
contents: `
|
||||
import Omi from './omi';
|
||||
if (typeof module!='undefined') module.exports = Omi;
|
||||
else self.Omi = Omi;
|
||||
`
|
||||
}),
|
||||
nodeResolve({
|
||||
main: true
|
||||
}),
|
||||
commonjs({
|
||||
include: 'node_modules/**'
|
||||
}),
|
||||
babel({
|
||||
sourceMap: true,
|
||||
exclude: "node_modules/**",
|
||||
babelrc: false,
|
||||
presets: [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
modules: false,
|
||||
loose: true,
|
||||
exclude: ["transform-es2015-typeof-symbol"],
|
||||
targets: {
|
||||
browsers: ["last 2 versions", "IE >= 9"]
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
plugins: [
|
||||
"transform-decorators-legacy",
|
||||
"transform-class-properties",
|
||||
["transform-react-jsx", { pragma: "Omi.h" }]
|
||||
]
|
||||
})
|
||||
]
|
||||
};
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
|
||||
<head></head>
|
||||
|
||||
<body>
|
||||
|
||||
<script src="b.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,37 @@
|
|||
import { render } from '../../src/omi'
|
||||
|
||||
//逻辑store外置,UI只负责渲染
|
||||
const Counter = (props, store) => {
|
||||
return <div>
|
||||
<button onClick={store.sub}>sub</button>
|
||||
<text>{store.data.count}</text>
|
||||
<button onClick={store.add}>add</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
Counter.store = _ => {
|
||||
return {
|
||||
data: {
|
||||
count: 1
|
||||
},
|
||||
add() {
|
||||
this.data.count++
|
||||
},
|
||||
sub() {
|
||||
this.data.count--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const App = (props, store) => {
|
||||
return <div>
|
||||
<Counter></Counter>
|
||||
</div>
|
||||
}
|
||||
|
||||
App.store = _ => {
|
||||
|
||||
}
|
||||
|
||||
render(<App />, 'body')
|
|
@ -0,0 +1,189 @@
|
|||
{
|
||||
"name": "omi",
|
||||
"version": "6.6.9",
|
||||
"description": "Front End Cross-Frameworks Framework.",
|
||||
"main": "dist/omi.js",
|
||||
"jsnext:main": "dist/omi.esm.js",
|
||||
"module": "dist/omi.esm.js",
|
||||
"dev:main": "dist/omi.dev.js",
|
||||
"minified:main": "dist/omi.min.js",
|
||||
"types": "dist/omi.d.ts",
|
||||
"scripts": {
|
||||
"clean": "rimraf test/ts/**/*.js",
|
||||
"copy-typescript-definition": "copyfiles -f src/omi.d.ts dist",
|
||||
"build": "npm-run-all --silent clean transpile copy-typescript-definition strip optimize minify size",
|
||||
"slot": "rollup -c config/rollup.example.js --watch",
|
||||
"perfs": "rollup -c config/rollup.example.js --watch",
|
||||
"css": "rollup -c config/rollup.example.js --watch",
|
||||
"markdown": "rollup -c config/rollup.example.js --watch",
|
||||
"clock": "rollup -c config/rollup.example.js --watch",
|
||||
"nest": "rollup -c config/rollup.example.js --watch",
|
||||
"event": "rollup -c config/rollup.example.js --watch",
|
||||
"observer": "rollup -c config/rollup.example.js --watch",
|
||||
"hook": "rollup -c config/rollup.example.js --watch",
|
||||
"decorators": "rollup -c config/rollup.example.js --watch",
|
||||
"to": "rollup -c config/rollup.example.js --watch",
|
||||
"m-to": "rollup -c config/rollup.example.js --watch",
|
||||
"mobx": "rollup -c config/rollup.example.js --watch",
|
||||
"todomvc": "rollup -c config/rollup.example.js --watch",
|
||||
"mvvm": "rollup -c config/rollup.example.js --watch",
|
||||
"receive-props": "rollup -c config/rollup.example.js --watch",
|
||||
"rpx": "rollup -c config/rollup.example.js --watch",
|
||||
"children-bug": "rollup -c config/rollup.example.js --watch",
|
||||
"tree": "rollup -c config/rollup.example.js --watch",
|
||||
"tree2": "rollup -c config/rollup.example.js --watch",
|
||||
"store-tree": "rollup -c config/rollup.example.js --watch",
|
||||
"todo-app": "rollup -c config/rollup.example.js --watch",
|
||||
"tick": "rollup -c config/rollup.example.js --watch",
|
||||
"observe": "rollup -c config/rollup.example.js --watch",
|
||||
"template": "rollup -c config/rollup.example.js --watch",
|
||||
"children": "rollup -c config/rollup.example.js --watch",
|
||||
"timer": "rollup -c config/rollup.example.js --watch",
|
||||
"react-style": "rollup -c config/rollup.example.js --watch",
|
||||
"store-old": "rollup -c config/rollup.example.js --watch",
|
||||
"store-counter": "rollup -c config/rollup.example.js --watch",
|
||||
"store-todo": "rollup -c config/rollup.example.js --watch",
|
||||
"store-demo": "rollup -c config/rollup.example.js --watch",
|
||||
"pure": "rollup -c config/rollup.example.js --watch",
|
||||
"counter": "rollup -c config/rollup.example.js --watch",
|
||||
"render-array": "rollup -c config/rollup.example.js --watch",
|
||||
"css3transform": "rollup -c config/rollup.example.js --watch",
|
||||
"tap": "rollup -c config/rollup.example.js --watch",
|
||||
"tap2": "rollup -c config/rollup.example.js --watch",
|
||||
"simple": "rollup -c config/rollup.example.js --watch",
|
||||
"transpile:main": "rollup -c config/rollup.config.js",
|
||||
"transpile:esm": "rollup -c config/rollup.config.esm.js",
|
||||
"transpile": "npm-run-all transpile:main transpile:esm",
|
||||
"optimize": "uglifyjs dist/omi.dev.js -c conditionals=false,sequences=false,loops=false,join_vars=false,collapse_vars=false --pure-funcs=Object.defineProperty --mangle-props --mangle-regex=\"/^(_|nextBase|prev[CPS]|_parentC)/\" --name-cache config/properties.json -b width=120,quote_style=3 -o dist/omi.js -p relative --in-source-map dist/omi.dev.js.map --source-map dist/omi.js.map",
|
||||
"minify": "uglifyjs dist/omi.js -c collapse_vars,evaluate,screw_ie8,unsafe,loops=false,keep_fargs=false,pure_getters,unused,dead_code -m -o dist/omi.min.js -p relative --in-source-map dist/omi.js.map --source-map dist/omi.min.js.map",
|
||||
"strip:main": "jscodeshift --run-in-band -s -t config/codemod-strip-tdz.js dist/omi.dev.js && jscodeshift --run-in-band -s -t config/codemod-const.js dist/omi.dev.js && jscodeshift --run-in-band -s -t config/codemod-let-name.js dist/omi.dev.js",
|
||||
"strip:esm": "jscodeshift --run-in-band -s -t config/codemod-strip-tdz.js dist/omi.esm.js && jscodeshift --run-in-band -s -t config/codemod-const.js dist/omi.esm.js && jscodeshift --run-in-band -s -t config/codemod-let-name.js dist/omi.esm.js",
|
||||
"strip": "npm-run-all strip:main strip:esm",
|
||||
"size": "node -e \"process.stdout.write('gzip size: ')\" && gzip-size --raw dist/omi.min.js",
|
||||
"test": "karma start test/karma.conf.js --single-run",
|
||||
"fix": "eslint src --fix",
|
||||
"fix-e": "eslint examples --fix",
|
||||
"lint": "eslint src test",
|
||||
"smart-release": "npm run build && npm test && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish",
|
||||
"release": "cross-env npm run smart-release"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "./config/eslint-config.js"
|
||||
},
|
||||
"typings": "./dist/omi.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Tencent/omi.git"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"typings.json"
|
||||
],
|
||||
"keywords": [
|
||||
"omi",
|
||||
"webcomponents",
|
||||
"jsx",
|
||||
"proxy",
|
||||
"preact",
|
||||
"react",
|
||||
"virtual dom",
|
||||
"vdom",
|
||||
"components",
|
||||
"virtual",
|
||||
"dom"
|
||||
],
|
||||
"author": "dntzhang <dntzhang@qq.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Tencent/omi/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Tencent/omi",
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.2",
|
||||
"@types/mocha": "^5.0.0",
|
||||
"@types/node": "^9.4.7",
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-core": "^6.24.1",
|
||||
"babel-eslint": "^8.2.2",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.5",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.23.0",
|
||||
"babel-plugin-transform-react-jsx": "^6.24.1",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"bundlesize": "^0.17.0",
|
||||
"chai": "^4.1.2",
|
||||
"copyfiles": "^2.0.0",
|
||||
"core-js": "^2.4.1",
|
||||
"coveralls": "^3.0.0",
|
||||
"cross-env": "^5.1.4",
|
||||
"diff": "^3.0.0",
|
||||
"eslint": "^4.18.2",
|
||||
"eslint-config-prettier": "^3.1.0",
|
||||
"eslint-plugin-prettier": "^3.0.0",
|
||||
"eslint-plugin-react": "^7.7.0",
|
||||
"flow-bin": "^0.67.1",
|
||||
"gzip-size-cli": "^2.0.0",
|
||||
"htm": "^2.1.1",
|
||||
"istanbul-instrumenter-loader": "^3.0.0",
|
||||
"jest": "^23.6.0",
|
||||
"jscodeshift": "^0.5.0",
|
||||
"karma": "^2.0.0",
|
||||
"karma-babel-preprocessor": "^7.0.0",
|
||||
"karma-chai-sinon": "^0.1.5",
|
||||
"karma-chrome-launcher": "^2.0.0",
|
||||
"karma-coverage": "^1.0.0",
|
||||
"karma-mocha": "^1.1.1",
|
||||
"karma-mocha-reporter": "^2.0.4",
|
||||
"karma-sauce-launcher": "^1.2.0",
|
||||
"karma-sinon": "^1.0.5",
|
||||
"karma-source-map-support": "^1.2.0",
|
||||
"karma-sourcemap-loader": "^0.3.6",
|
||||
"karma-webpack": "^3.0.0",
|
||||
"mappingjs": "latest",
|
||||
"mobx": "^4.5.1",
|
||||
"mocha": "^5.0.4",
|
||||
"npm-run-all": "^4.0.0",
|
||||
"omi-mobx": "^0.2.2",
|
||||
"omi-tap": "^2.0.2",
|
||||
"omi-transform": "^2.0.2",
|
||||
"prettier": "^1.14.3",
|
||||
"remarkable": "^1.7.1",
|
||||
"rimraf": "^2.5.3",
|
||||
"rollup": "^0.57.1",
|
||||
"rollup-plugin-babel": "^3.0.2",
|
||||
"rollup-plugin-commonjs": "^9.2.0",
|
||||
"rollup-plugin-license": "^0.6.0",
|
||||
"rollup-plugin-memory": "^3.0.0",
|
||||
"rollup-plugin-node-resolve": "^3.0.0",
|
||||
"sinon": "^4.4.2",
|
||||
"sinon-chai": "^3.0.0",
|
||||
"typescript": "^2.8.1",
|
||||
"to2to": "^1.0.2",
|
||||
"uglify-js": "^2.7.5",
|
||||
"webpack": "^4.3.0"
|
||||
},
|
||||
"greenkeeper": {
|
||||
"ignore": [
|
||||
"babel-cli",
|
||||
"babel-core",
|
||||
"babel-eslint",
|
||||
"babel-loader",
|
||||
"jscodeshift",
|
||||
"rollup-plugin-babel"
|
||||
]
|
||||
},
|
||||
"bundlesize": [
|
||||
{
|
||||
"path": "./dist/omi.min.js",
|
||||
"threshold": "4Kb"
|
||||
}
|
||||
],
|
||||
"prettier": {
|
||||
"singleQuote": true,
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { extend } from './util';
|
||||
import { h } from './h';
|
||||
|
||||
/**
|
||||
* Clones the given VNode, optionally adding attributes/props and replacing its
|
||||
* children.
|
||||
* @param {import('./vnode').VNode} vnode The virtual DOM element to clone
|
||||
* @param {object} props Attributes/props to add when cloning
|
||||
* @param {Array<import('./vnode').VNode>} [rest] Any additional arguments will be used as replacement
|
||||
* children.
|
||||
*/
|
||||
export function cloneElement(vnode, props) {
|
||||
return h(
|
||||
vnode.nodeName,
|
||||
extend(extend({}, vnode.attributes), props),
|
||||
arguments.length>2 ? [].slice.call(arguments, 2) : vnode.children
|
||||
);
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
import { FORCE_RENDER } from './constants';
|
||||
import { extend } from './util';
|
||||
import { renderComponent } from './vdom/component';
|
||||
import { enqueueRender } from './render-queue';
|
||||
/**
|
||||
* Base Component class.
|
||||
* Provides `setState()` and `forceUpdate()`, which trigger rendering.
|
||||
* @typedef {object} Component
|
||||
* @param {object} props The initial component props
|
||||
* @param {object} context The initial context from parent components' getChildContext
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* class MyFoo extends Component {
|
||||
* render(props, state) {
|
||||
* return <div />;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export function Component(props, context) {
|
||||
this._dirty = true;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @type {object}
|
||||
*/
|
||||
this.context = context;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @type {object}
|
||||
*/
|
||||
this.props = props;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @type {object}
|
||||
*/
|
||||
this.state = this.state || {};
|
||||
|
||||
this._renderCallbacks = [];
|
||||
}
|
||||
|
||||
|
||||
extend(Component.prototype, {
|
||||
|
||||
/**
|
||||
* Update component state and schedule a re-render.
|
||||
* @param {object} state A dict of state properties to be shallowly merged
|
||||
* into the current state, or a function that will produce such a dict. The
|
||||
* function is called with the current state and props.
|
||||
* @param {() => void} callback A function to be called once component state is
|
||||
* updated
|
||||
*/
|
||||
setState(state, callback) {
|
||||
if (!this.prevState) this.prevState = this.state;
|
||||
this.state = extend(
|
||||
extend({}, this.state),
|
||||
typeof state === 'function' ? state(this.state, this.props) : state
|
||||
);
|
||||
if (callback) this._renderCallbacks.push(callback);
|
||||
enqueueRender(this);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Immediately perform a synchronous re-render of the component.
|
||||
* @param {() => void} callback A function to be called after component is
|
||||
* re-rendered.
|
||||
* @private
|
||||
*/
|
||||
forceUpdate(callback) {
|
||||
if (callback) this._renderCallbacks.push(callback);
|
||||
renderComponent(this, FORCE_RENDER);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Accepts `props` and `state`, and returns a new Virtual DOM tree to build.
|
||||
* Virtual DOM is generally constructed via [JSX](http://jasonformat.com/wtf-is-jsx).
|
||||
* @param {object} props Props (eg: JSX attributes) received from parent
|
||||
* element/component
|
||||
* @param {object} state The component's current state
|
||||
* @param {object} context Context object, as returned by the nearest
|
||||
* ancestor's `getChildContext()`
|
||||
* @returns {import('./vnode').VNode | void}
|
||||
*/
|
||||
render() {}
|
||||
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
// render modes
|
||||
|
||||
/** Do not re-render a component */
|
||||
export const NO_RENDER = 0;
|
||||
/** Synchronously re-render a component and its children */
|
||||
export const SYNC_RENDER = 1;
|
||||
/** Synchronously re-render a component, even if its lifecycle methods attempt to prevent it. */
|
||||
export const FORCE_RENDER = 2;
|
||||
/** Queue asynchronous re-render of a component and it's children */
|
||||
export const ASYNC_RENDER = 3;
|
||||
|
||||
|
||||
export const ATTR_KEY = '__preactattr_';
|
||||
|
||||
/** DOM properties that should NOT have "px" added when numeric */
|
||||
export const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
import { IS_NON_DIMENSIONAL } from '../constants';
|
||||
import { applyRef } from '../util';
|
||||
import options from '../options';
|
||||
|
||||
/**
|
||||
* A DOM event listener
|
||||
* @typedef {(e: Event) => void} EventListner
|
||||
*/
|
||||
|
||||
/**
|
||||
* A mapping of event types to event listeners
|
||||
* @typedef {Object.<string, EventListener>} EventListenerMap
|
||||
*/
|
||||
|
||||
/**
|
||||
* Properties Preact adds to elements it creates
|
||||
* @typedef PreactElementExtensions
|
||||
* @property {string} [normalizedNodeName] A normalized node name to use in diffing
|
||||
* @property {EventListenerMap} [_listeners] A map of event listeners added by components to this DOM node
|
||||
* @property {import('../component').Component} [_component] The component that rendered this DOM node
|
||||
* @property {function} [_componentConstructor] The constructor of the component that rendered this DOM node
|
||||
*/
|
||||
|
||||
/**
|
||||
* A DOM element that has been extended with Preact properties
|
||||
* @typedef {Element & ElementCSSInlineStyle & PreactElementExtensions} PreactElement
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create an element with the given nodeName.
|
||||
* @param {string} nodeName The DOM node to create
|
||||
* @param {boolean} [isSvg=false] If `true`, creates an element within the SVG
|
||||
* namespace.
|
||||
* @returns {PreactElement} The created DOM node
|
||||
*/
|
||||
export function createNode(nodeName, isSvg) {
|
||||
/** @type {PreactElement} */
|
||||
let node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName);
|
||||
node.normalizedNodeName = nodeName;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove a child node from its parent if attached.
|
||||
* @param {Node} node The node to remove
|
||||
*/
|
||||
export function removeNode(node) {
|
||||
let parentNode = node.parentNode;
|
||||
if (parentNode) parentNode.removeChild(node);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a named attribute on the given Node, with special behavior for some names
|
||||
* and event handlers. If `value` is `null`, the attribute/handler will be
|
||||
* removed.
|
||||
* @param {PreactElement} node An element to mutate
|
||||
* @param {string} name The name/key to set, such as an event or attribute name
|
||||
* @param {*} old The last value that was set for this name/node pair
|
||||
* @param {*} value An attribute value, such as a function to be used as an
|
||||
* event handler
|
||||
* @param {boolean} isSvg Are we currently diffing inside an svg?
|
||||
* @private
|
||||
*/
|
||||
export function setAccessor(node, name, old, value, isSvg) {
|
||||
if (name==='className') name = 'class';
|
||||
|
||||
|
||||
if (name==='key') {
|
||||
// ignore
|
||||
}
|
||||
else if (name==='ref') {
|
||||
applyRef(old, null);
|
||||
applyRef(value, node);
|
||||
}
|
||||
else if (name==='class' && !isSvg) {
|
||||
node.className = value || '';
|
||||
}
|
||||
else if (name==='style') {
|
||||
if (!value || typeof value==='string' || typeof old==='string') {
|
||||
node.style.cssText = value || '';
|
||||
}
|
||||
if (value && typeof value==='object') {
|
||||
if (typeof old!=='string') {
|
||||
for (let i in old) if (!(i in value)) node.style[i] = '';
|
||||
}
|
||||
for (let i in value) {
|
||||
node.style[i] = typeof value[i]==='number' && IS_NON_DIMENSIONAL.test(i)===false ? (value[i]+'px') : value[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (name==='dangerouslySetInnerHTML') {
|
||||
if (value) node.innerHTML = value.__html || '';
|
||||
}
|
||||
else if (name[0]=='o' && name[1]=='n') {
|
||||
let useCapture = name !== (name=name.replace(/Capture$/, ''));
|
||||
name = name.toLowerCase().substring(2);
|
||||
if (value) {
|
||||
if (!old) node.addEventListener(name, eventProxy, useCapture);
|
||||
}
|
||||
else {
|
||||
node.removeEventListener(name, eventProxy, useCapture);
|
||||
}
|
||||
(node._listeners || (node._listeners = {}))[name] = value;
|
||||
}
|
||||
else if (name!=='list' && name!=='type' && !isSvg && name in node) {
|
||||
// Attempt to set a DOM property to the given value.
|
||||
// IE & FF throw for certain property-value combinations.
|
||||
try {
|
||||
node[name] = value==null ? '' : value;
|
||||
} catch (e) { }
|
||||
if ((value==null || value===false) && name!='spellcheck') node.removeAttribute(name);
|
||||
}
|
||||
else {
|
||||
let ns = isSvg && (name !== (name = name.replace(/^xlink:?/, '')));
|
||||
// spellcheck is treated differently than all other boolean values and
|
||||
// should not be removed when the value is `false`. See:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-spellcheck
|
||||
if (value==null || value===false) {
|
||||
if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase());
|
||||
else node.removeAttribute(name);
|
||||
}
|
||||
else if (typeof value!=='function') {
|
||||
if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase(), value);
|
||||
else node.setAttribute(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Proxy an event to hooked event handlers
|
||||
* @param {Event} e The event object from the browser
|
||||
* @private
|
||||
*/
|
||||
function eventProxy(e) {
|
||||
return this._listeners[e.type](options.event && options.event(e) || e);
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
import { VNode } from './vnode';
|
||||
import options from './options';
|
||||
|
||||
|
||||
const stack = [];
|
||||
|
||||
const EMPTY_CHILDREN = [];
|
||||
|
||||
/**
|
||||
* JSX/hyperscript reviver.
|
||||
* @see http://jasonformat.com/wtf-is-jsx
|
||||
* Benchmarks: https://esbench.com/bench/57ee8f8e330ab09900a1a1a0
|
||||
*
|
||||
* Note: this is exported as both `h()` and `createElement()` for compatibility
|
||||
* reasons.
|
||||
*
|
||||
* Creates a VNode (virtual DOM element). A tree of VNodes can be used as a
|
||||
* lightweight representation of the structure of a DOM tree. This structure can
|
||||
* be realized by recursively comparing it against the current _actual_ DOM
|
||||
* structure, and applying only the differences.
|
||||
*
|
||||
* `h()`/`createElement()` accepts an element name, a list of attributes/props,
|
||||
* and optionally children to append to the element.
|
||||
*
|
||||
* @example The following DOM tree
|
||||
*
|
||||
* `<div id="foo" name="bar">Hello!</div>`
|
||||
*
|
||||
* can be constructed using this function as:
|
||||
*
|
||||
* `h('div', { id: 'foo', name : 'bar' }, 'Hello!');`
|
||||
*
|
||||
* @param {string | function} nodeName An element name. Ex: `div`, `a`, `span`, etc.
|
||||
* @param {object | null} attributes Any attributes/props to set on the created element.
|
||||
* @param {VNode[]} [rest] Additional arguments are taken to be children to
|
||||
* append. Can be infinitely nested Arrays.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function h(nodeName, attributes) {
|
||||
let children=EMPTY_CHILDREN, lastSimple, child, simple, i;
|
||||
for (i=arguments.length; i-- > 2; ) {
|
||||
stack.push(arguments[i]);
|
||||
}
|
||||
if (attributes && attributes.children!=null) {
|
||||
if (!stack.length) stack.push(attributes.children);
|
||||
delete attributes.children;
|
||||
}
|
||||
while (stack.length) {
|
||||
if ((child = stack.pop()) && child.pop!==undefined) {
|
||||
for (i=child.length; i--; ) stack.push(child[i]);
|
||||
}
|
||||
else {
|
||||
if (typeof child==='boolean') child = null;
|
||||
|
||||
if ((simple = typeof nodeName!=='function')) {
|
||||
if (child==null) child = '';
|
||||
else if (typeof child==='number') child = String(child);
|
||||
else if (typeof child!=='string') simple = false;
|
||||
}
|
||||
|
||||
if (simple && lastSimple) {
|
||||
children[children.length-1] += child;
|
||||
}
|
||||
else if (children===EMPTY_CHILDREN) {
|
||||
children = [child];
|
||||
}
|
||||
else {
|
||||
children.push(child);
|
||||
}
|
||||
|
||||
lastSimple = simple;
|
||||
}
|
||||
}
|
||||
|
||||
let p = new VNode();
|
||||
p.nodeName = nodeName;
|
||||
p.children = children;
|
||||
p.attributes = attributes==null ? undefined : attributes;
|
||||
p.key = attributes==null ? undefined : attributes.key;
|
||||
|
||||
// if a "vnode hook" is defined, pass every created VNode to it
|
||||
if (options.vnode!==undefined) options.vnode(p);
|
||||
|
||||
return p;
|
||||
}
|
|
@ -0,0 +1,940 @@
|
|||
export = omi;
|
||||
export as namespace omi;
|
||||
|
||||
declare namespace omi {
|
||||
type Key = string | number;
|
||||
type RefObject<T> = { current?: T | null };
|
||||
type RefCallback<T> = (instance: T | null) => void;
|
||||
type Ref<T> = RefObject<T> | RefCallback<T>;
|
||||
type ComponentChild = VNode<any> | object | string | number | boolean | null;
|
||||
type ComponentChildren = ComponentChild[] | ComponentChild;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Use Attributes instead
|
||||
*/
|
||||
type ComponentProps = Attributes;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Use ClassAttributes instead
|
||||
*/
|
||||
type PreactHTMLAttributes = ClassAttributes<any>;
|
||||
|
||||
interface Attributes {
|
||||
key?: Key;
|
||||
jsx?: boolean;
|
||||
}
|
||||
|
||||
interface ClassAttributes<T> extends Attributes {
|
||||
ref?: Ref<T>;
|
||||
}
|
||||
|
||||
interface PreactDOMAttributes {
|
||||
children?: ComponentChildren;
|
||||
dangerouslySetInnerHTML?: {
|
||||
__html: string;
|
||||
};
|
||||
}
|
||||
|
||||
type ComponentFactory<P> = ComponentConstructor<P> | FunctionalComponent<P>;
|
||||
/**
|
||||
* Define the contract for a virtual node in omi.
|
||||
*
|
||||
* A virtual node has a name, a map of attributes, an array
|
||||
* of child {VNode}s and a key. The key is used by omi for
|
||||
* internal purposes.
|
||||
*/
|
||||
interface VNode<P = any> {
|
||||
nodeName: ComponentFactory<P> | string;
|
||||
attributes: P;
|
||||
children: Array<VNode<any> | string>;
|
||||
key?: Key | null;
|
||||
}
|
||||
|
||||
type RenderableProps<P, RefType = any> = Readonly<
|
||||
P & Attributes & { children?: ComponentChildren; ref?: Ref<RefType> }
|
||||
>;
|
||||
|
||||
interface FunctionalComponent<P = {}> {
|
||||
(props: RenderableProps<P>, context?: any): VNode<any> | null;
|
||||
displayName?: string;
|
||||
defaultProps?: Partial<P>;
|
||||
}
|
||||
|
||||
interface ComponentConstructor<P = {}, S = {}> {
|
||||
new (props: P, context?: any): Component<P, S>;
|
||||
displayName?: string;
|
||||
defaultProps?: Partial<P>;
|
||||
}
|
||||
|
||||
// Type alias for a component considered generally, whether stateless or stateful.
|
||||
type AnyComponent<P = {}, S = {}> = FunctionalComponent<P> | ComponentConstructor<P, S>;
|
||||
|
||||
interface Component<P = {}, S = {}> {
|
||||
componentWillMount?(): void;
|
||||
componentDidMount?(): void;
|
||||
componentWillUnmount?(): void;
|
||||
getChildContext?(): object;
|
||||
componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;
|
||||
shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean;
|
||||
componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): void;
|
||||
componentDidUpdate?(previousProps: Readonly<P>, previousState: Readonly<S>, previousContext: any): void;
|
||||
}
|
||||
|
||||
abstract class Component<P, S> {
|
||||
constructor(props?: P, context?: any);
|
||||
|
||||
static displayName?: string;
|
||||
static defaultProps?: any;
|
||||
|
||||
state: Readonly<S>;
|
||||
props: RenderableProps<P>;
|
||||
context: any;
|
||||
base?: HTMLElement;
|
||||
|
||||
// From https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e836acc75a78cf0655b5dfdbe81d69fdd4d8a252/types/react/index.d.ts#L402
|
||||
// // We MUST keep setState() as a unified signature because it allows proper checking of the method return type.
|
||||
// // See: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-351013257
|
||||
// // Also, the ` | S` allows intellisense to not be dumbisense
|
||||
setState<K extends keyof S>(
|
||||
state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
|
||||
callback?: () => void
|
||||
): void;
|
||||
|
||||
forceUpdate(callback?: () => void): void;
|
||||
|
||||
abstract render(props?: RenderableProps<P>, state?: Readonly<S>, context?: any): ComponentChild;
|
||||
|
||||
// Add these variables to avoid descendants shadowing them (some from properties.json for minification)
|
||||
private __key;
|
||||
private __ref;
|
||||
private _component;
|
||||
private _dirty;
|
||||
private _disable;
|
||||
private nextBase;
|
||||
private prevContext;
|
||||
private prevProps;
|
||||
private prevState;
|
||||
private __d;
|
||||
private __x;
|
||||
private __l;
|
||||
private __h;
|
||||
private __k;
|
||||
private __r;
|
||||
private __n;
|
||||
private __b;
|
||||
private __c;
|
||||
private __p;
|
||||
private __s;
|
||||
private __u;
|
||||
}
|
||||
|
||||
function h(
|
||||
node: string,
|
||||
params: JSX.HTMLAttributes & JSX.SVGAttributes & Record<string, any> | null,
|
||||
...children: ComponentChildren[]
|
||||
): VNode<any>;
|
||||
function h<P>(
|
||||
node: ComponentFactory<P>,
|
||||
params: Attributes & P | null,
|
||||
...children: ComponentChildren[]
|
||||
): VNode<any>;
|
||||
|
||||
function render(node: ComponentChild, parent: Element | Document | ShadowRoot | DocumentFragment, mergeWith?: Element): Element;
|
||||
function rerender(): void;
|
||||
function cloneElement(element: JSX.Element, props: any, ...children: ComponentChildren[]): JSX.Element;
|
||||
function createRef<T=any>(): RefObject<T>;
|
||||
|
||||
var options: {
|
||||
syncComponentUpdates?: boolean;
|
||||
debounceRendering?: (render: () => void) => void;
|
||||
vnode?: (vnode: VNode<any>) => void;
|
||||
event?: (event: Event) => Event;
|
||||
};
|
||||
}
|
||||
|
||||
type Defaultize<Props, Defaults> =
|
||||
// Distribute over unions
|
||||
Props extends any
|
||||
? // Make any properties included in Default optional
|
||||
& Partial<Pick<Props, Extract<keyof Props, keyof Defaults>>>
|
||||
// Include the remaining properties from Props
|
||||
& Pick<Props, Exclude<keyof Props, keyof Defaults>>
|
||||
: never;
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface Element extends omi.VNode<any> {
|
||||
}
|
||||
|
||||
interface ElementClass extends omi.Component<any, any> {
|
||||
}
|
||||
|
||||
interface ElementAttributesProperty {
|
||||
props: any;
|
||||
}
|
||||
|
||||
interface ElementChildrenAttribute {
|
||||
children: any;
|
||||
}
|
||||
|
||||
type LibraryManagedAttributes<Component, Props> =
|
||||
Component extends { defaultProps: infer Defaults }
|
||||
? Defaultize<Props, Defaults>
|
||||
: Props;
|
||||
|
||||
interface SVGAttributes extends HTMLAttributes {
|
||||
accentHeight?: number | string;
|
||||
accumulate?: "none" | "sum";
|
||||
additive?: "replace" | "sum";
|
||||
alignmentBaseline?: "auto" | "baseline" | "before-edge" | "text-before-edge" | "middle" | "central" | "after-edge" | "text-after-edge" | "ideographic" | "alphabetic" | "hanging" | "mathematical" | "inherit";
|
||||
allowReorder?: "no" | "yes";
|
||||
alphabetic?: number | string;
|
||||
amplitude?: number | string;
|
||||
arabicForm?: "initial" | "medial" | "terminal" | "isolated";
|
||||
ascent?: number | string;
|
||||
attributeName?: string;
|
||||
attributeType?: string;
|
||||
autoReverse?: number | string;
|
||||
azimuth?: number | string;
|
||||
baseFrequency?: number | string;
|
||||
baselineShift?: number | string;
|
||||
baseProfile?: number | string;
|
||||
bbox?: number | string;
|
||||
begin?: number | string;
|
||||
bias?: number | string;
|
||||
by?: number | string;
|
||||
calcMode?: number | string;
|
||||
capHeight?: number | string;
|
||||
clip?: number | string;
|
||||
clipPath?: string;
|
||||
clipPathUnits?: number | string;
|
||||
clipRule?: number | string;
|
||||
colorInterpolation?: number | string;
|
||||
colorInterpolationFilters?: "auto" | "sRGB" | "linearRGB" | "inherit";
|
||||
colorProfile?: number | string;
|
||||
colorRendering?: number | string;
|
||||
contentScriptType?: number | string;
|
||||
contentStyleType?: number | string;
|
||||
cursor?: number | string;
|
||||
cx?: number | string;
|
||||
cy?: number | string;
|
||||
d?: string;
|
||||
decelerate?: number | string;
|
||||
descent?: number | string;
|
||||
diffuseConstant?: number | string;
|
||||
direction?: number | string;
|
||||
display?: number | string;
|
||||
divisor?: number | string;
|
||||
dominantBaseline?: number | string;
|
||||
dur?: number | string;
|
||||
dx?: number | string;
|
||||
dy?: number | string;
|
||||
edgeMode?: number | string;
|
||||
elevation?: number | string;
|
||||
enableBackground?: number | string;
|
||||
end?: number | string;
|
||||
exponent?: number | string;
|
||||
externalResourcesRequired?: number | string;
|
||||
fill?: string;
|
||||
fillOpacity?: number | string;
|
||||
fillRule?: "nonzero" | "evenodd" | "inherit";
|
||||
filter?: string;
|
||||
filterRes?: number | string;
|
||||
filterUnits?: number | string;
|
||||
floodColor?: number | string;
|
||||
floodOpacity?: number | string;
|
||||
focusable?: number | string;
|
||||
fontFamily?: string;
|
||||
fontSize?: number | string;
|
||||
fontSizeAdjust?: number | string;
|
||||
fontStretch?: number | string;
|
||||
fontStyle?: number | string;
|
||||
fontVariant?: number | string;
|
||||
fontWeight?: number | string;
|
||||
format?: number | string;
|
||||
from?: number | string;
|
||||
fx?: number | string;
|
||||
fy?: number | string;
|
||||
g1?: number | string;
|
||||
g2?: number | string;
|
||||
glyphName?: number | string;
|
||||
glyphOrientationHorizontal?: number | string;
|
||||
glyphOrientationVertical?: number | string;
|
||||
glyphRef?: number | string;
|
||||
gradientTransform?: string;
|
||||
gradientUnits?: string;
|
||||
hanging?: number | string;
|
||||
horizAdvX?: number | string;
|
||||
horizOriginX?: number | string;
|
||||
ideographic?: number | string;
|
||||
imageRendering?: number | string;
|
||||
in2?: number | string;
|
||||
in?: string;
|
||||
intercept?: number | string;
|
||||
k1?: number | string;
|
||||
k2?: number | string;
|
||||
k3?: number | string;
|
||||
k4?: number | string;
|
||||
k?: number | string;
|
||||
kernelMatrix?: number | string;
|
||||
kernelUnitLength?: number | string;
|
||||
kerning?: number | string;
|
||||
keyPoints?: number | string;
|
||||
keySplines?: number | string;
|
||||
keyTimes?: number | string;
|
||||
lengthAdjust?: number | string;
|
||||
letterSpacing?: number | string;
|
||||
lightingColor?: number | string;
|
||||
limitingConeAngle?: number | string;
|
||||
local?: number | string;
|
||||
markerEnd?: string;
|
||||
markerHeight?: number | string;
|
||||
markerMid?: string;
|
||||
markerStart?: string;
|
||||
markerUnits?: number | string;
|
||||
markerWidth?: number | string;
|
||||
mask?: string;
|
||||
maskContentUnits?: number | string;
|
||||
maskUnits?: number | string;
|
||||
mathematical?: number | string;
|
||||
mode?: number | string;
|
||||
numOctaves?: number | string;
|
||||
offset?: number | string;
|
||||
opacity?: number | string;
|
||||
operator?: number | string;
|
||||
order?: number | string;
|
||||
orient?: number | string;
|
||||
orientation?: number | string;
|
||||
origin?: number | string;
|
||||
overflow?: number | string;
|
||||
overlinePosition?: number | string;
|
||||
overlineThickness?: number | string;
|
||||
paintOrder?: number | string;
|
||||
panose1?: number | string;
|
||||
pathLength?: number | string;
|
||||
patternContentUnits?: string;
|
||||
patternTransform?: number | string;
|
||||
patternUnits?: string;
|
||||
pointerEvents?: number | string;
|
||||
points?: string;
|
||||
pointsAtX?: number | string;
|
||||
pointsAtY?: number | string;
|
||||
pointsAtZ?: number | string;
|
||||
preserveAlpha?: number | string;
|
||||
preserveAspectRatio?: string;
|
||||
primitiveUnits?: number | string;
|
||||
r?: number | string;
|
||||
radius?: number | string;
|
||||
refX?: number | string;
|
||||
refY?: number | string;
|
||||
renderingIntent?: number | string;
|
||||
repeatCount?: number | string;
|
||||
repeatDur?: number | string;
|
||||
requiredExtensions?: number | string;
|
||||
requiredFeatures?: number | string;
|
||||
restart?: number | string;
|
||||
result?: string;
|
||||
rotate?: number | string;
|
||||
rx?: number | string;
|
||||
ry?: number | string;
|
||||
scale?: number | string;
|
||||
seed?: number | string;
|
||||
shapeRendering?: number | string;
|
||||
slope?: number | string;
|
||||
spacing?: number | string;
|
||||
specularConstant?: number | string;
|
||||
specularExponent?: number | string;
|
||||
speed?: number | string;
|
||||
spreadMethod?: string;
|
||||
startOffset?: number | string;
|
||||
stdDeviation?: number | string;
|
||||
stemh?: number | string;
|
||||
stemv?: number | string;
|
||||
stitchTiles?: number | string;
|
||||
stopColor?: string;
|
||||
stopOpacity?: number | string;
|
||||
strikethroughPosition?: number | string;
|
||||
strikethroughThickness?: number | string;
|
||||
string?: number | string;
|
||||
stroke?: string;
|
||||
strokeDasharray?: string | number;
|
||||
strokeDashoffset?: string | number;
|
||||
strokeLinecap?: "butt" | "round" | "square" | "inherit";
|
||||
strokeLinejoin?: "miter" | "round" | "bevel" | "inherit";
|
||||
strokeMiterlimit?: string;
|
||||
strokeOpacity?: number | string;
|
||||
strokeWidth?: number | string;
|
||||
surfaceScale?: number | string;
|
||||
systemLanguage?: number | string;
|
||||
tableValues?: number | string;
|
||||
targetX?: number | string;
|
||||
targetY?: number | string;
|
||||
textAnchor?: string;
|
||||
textDecoration?: number | string;
|
||||
textLength?: number | string;
|
||||
textRendering?: number | string;
|
||||
to?: number | string;
|
||||
transform?: string;
|
||||
u1?: number | string;
|
||||
u2?: number | string;
|
||||
underlinePosition?: number | string;
|
||||
underlineThickness?: number | string;
|
||||
unicode?: number | string;
|
||||
unicodeBidi?: number | string;
|
||||
unicodeRange?: number | string;
|
||||
unitsPerEm?: number | string;
|
||||
vAlphabetic?: number | string;
|
||||
values?: string;
|
||||
vectorEffect?: number | string;
|
||||
version?: string;
|
||||
vertAdvY?: number | string;
|
||||
vertOriginX?: number | string;
|
||||
vertOriginY?: number | string;
|
||||
vHanging?: number | string;
|
||||
vIdeographic?: number | string;
|
||||
viewBox?: string;
|
||||
viewTarget?: number | string;
|
||||
visibility?: number | string;
|
||||
vMathematical?: number | string;
|
||||
widths?: number | string;
|
||||
wordSpacing?: number | string;
|
||||
writingMode?: number | string;
|
||||
x1?: number | string;
|
||||
x2?: number | string;
|
||||
x?: number | string;
|
||||
xChannelSelector?: string;
|
||||
xHeight?: number | string;
|
||||
xlinkActuate?: string;
|
||||
xlinkArcrole?: string;
|
||||
xlinkHref?: string;
|
||||
xlinkRole?: string;
|
||||
xlinkShow?: string;
|
||||
xlinkTitle?: string;
|
||||
xlinkType?: string;
|
||||
xmlBase?: string;
|
||||
xmlLang?: string;
|
||||
xmlns?: string;
|
||||
xmlnsXlink?: string;
|
||||
xmlSpace?: string;
|
||||
y1?: number | string;
|
||||
y2?: number | string;
|
||||
y?: number | string;
|
||||
yChannelSelector?: string;
|
||||
z?: number | string;
|
||||
zoomAndPan?: string;
|
||||
}
|
||||
|
||||
interface PathAttributes {
|
||||
d: string;
|
||||
}
|
||||
|
||||
interface EventHandler<E extends Event> {
|
||||
(event: E): void;
|
||||
}
|
||||
|
||||
type ClipboardEventHandler = EventHandler<ClipboardEvent>;
|
||||
type CompositionEventHandler = EventHandler<CompositionEvent>;
|
||||
type DragEventHandler = EventHandler<DragEvent>;
|
||||
type FocusEventHandler = EventHandler<FocusEvent>;
|
||||
type KeyboardEventHandler = EventHandler<KeyboardEvent>;
|
||||
type MouseEventHandler = EventHandler<MouseEvent>;
|
||||
type TouchEventHandler = EventHandler<TouchEvent>;
|
||||
type UIEventHandler = EventHandler<UIEvent>;
|
||||
type WheelEventHandler = EventHandler<WheelEvent>;
|
||||
type AnimationEventHandler = EventHandler<AnimationEvent>;
|
||||
type TransitionEventHandler = EventHandler<TransitionEvent>;
|
||||
type GenericEventHandler = EventHandler<Event>;
|
||||
type PointerEventHandler = EventHandler<PointerEvent>;
|
||||
|
||||
interface DOMAttributes extends omi.PreactDOMAttributes {
|
||||
// Image Events
|
||||
onLoad?: GenericEventHandler;
|
||||
onError?: GenericEventHandler;
|
||||
onLoadCapture?: GenericEventHandler;
|
||||
|
||||
// Clipboard Events
|
||||
onCopy?: ClipboardEventHandler;
|
||||
onCopyCapture?: ClipboardEventHandler;
|
||||
onCut?: ClipboardEventHandler;
|
||||
onCutCapture?: ClipboardEventHandler;
|
||||
onPaste?: ClipboardEventHandler;
|
||||
onPasteCapture?: ClipboardEventHandler;
|
||||
|
||||
// Composition Events
|
||||
onCompositionEnd?: CompositionEventHandler;
|
||||
onCompositionEndCapture?: CompositionEventHandler;
|
||||
onCompositionStart?: CompositionEventHandler;
|
||||
onCompositionStartCapture?: CompositionEventHandler;
|
||||
onCompositionUpdate?: CompositionEventHandler;
|
||||
onCompositionUpdateCapture?: CompositionEventHandler;
|
||||
|
||||
// Focus Events
|
||||
onFocus?: FocusEventHandler;
|
||||
onFocusCapture?: FocusEventHandler;
|
||||
onBlur?: FocusEventHandler;
|
||||
onBlurCapture?: FocusEventHandler;
|
||||
|
||||
// Form Events
|
||||
onChange?: GenericEventHandler;
|
||||
onChangeCapture?: GenericEventHandler;
|
||||
onInput?: GenericEventHandler;
|
||||
onInputCapture?: GenericEventHandler;
|
||||
onSearch?: GenericEventHandler;
|
||||
onSearchCapture?: GenericEventHandler;
|
||||
onSubmit?: GenericEventHandler;
|
||||
onSubmitCapture?: GenericEventHandler;
|
||||
onInvalid?: GenericEventHandler;
|
||||
|
||||
// Keyboard Events
|
||||
onKeyDown?: KeyboardEventHandler;
|
||||
onKeyDownCapture?: KeyboardEventHandler;
|
||||
onKeyPress?: KeyboardEventHandler;
|
||||
onKeyPressCapture?: KeyboardEventHandler;
|
||||
onKeyUp?: KeyboardEventHandler;
|
||||
onKeyUpCapture?: KeyboardEventHandler;
|
||||
|
||||
// Media Events
|
||||
onAbort?: GenericEventHandler;
|
||||
onAbortCapture?: GenericEventHandler;
|
||||
onCanPlay?: GenericEventHandler;
|
||||
onCanPlayCapture?: GenericEventHandler;
|
||||
onCanPlayThrough?: GenericEventHandler;
|
||||
onCanPlayThroughCapture?: GenericEventHandler;
|
||||
onDurationChange?: GenericEventHandler;
|
||||
onDurationChangeCapture?: GenericEventHandler;
|
||||
onEmptied?: GenericEventHandler;
|
||||
onEmptiedCapture?: GenericEventHandler;
|
||||
onEncrypted?: GenericEventHandler;
|
||||
onEncryptedCapture?: GenericEventHandler;
|
||||
onEnded?: GenericEventHandler;
|
||||
onEndedCapture?: GenericEventHandler;
|
||||
onLoadedData?: GenericEventHandler;
|
||||
onLoadedDataCapture?: GenericEventHandler;
|
||||
onLoadedMetadata?: GenericEventHandler;
|
||||
onLoadedMetadataCapture?: GenericEventHandler;
|
||||
onLoadStart?: GenericEventHandler;
|
||||
onLoadStartCapture?: GenericEventHandler;
|
||||
onPause?: GenericEventHandler;
|
||||
onPauseCapture?: GenericEventHandler;
|
||||
onPlay?: GenericEventHandler;
|
||||
onPlayCapture?: GenericEventHandler;
|
||||
onPlaying?: GenericEventHandler;
|
||||
onPlayingCapture?: GenericEventHandler;
|
||||
onProgress?: GenericEventHandler;
|
||||
onProgressCapture?: GenericEventHandler;
|
||||
onRateChange?: GenericEventHandler;
|
||||
onRateChangeCapture?: GenericEventHandler;
|
||||
onSeeked?: GenericEventHandler;
|
||||
onSeekedCapture?: GenericEventHandler;
|
||||
onSeeking?: GenericEventHandler;
|
||||
onSeekingCapture?: GenericEventHandler;
|
||||
onStalled?: GenericEventHandler;
|
||||
onStalledCapture?: GenericEventHandler;
|
||||
onSuspend?: GenericEventHandler;
|
||||
onSuspendCapture?: GenericEventHandler;
|
||||
onTimeUpdate?: GenericEventHandler;
|
||||
onTimeUpdateCapture?: GenericEventHandler;
|
||||
onVolumeChange?: GenericEventHandler;
|
||||
onVolumeChangeCapture?: GenericEventHandler;
|
||||
onWaiting?: GenericEventHandler;
|
||||
onWaitingCapture?: GenericEventHandler;
|
||||
|
||||
// MouseEvents
|
||||
onClick?: MouseEventHandler;
|
||||
onClickCapture?: MouseEventHandler;
|
||||
onContextMenu?: MouseEventHandler;
|
||||
onContextMenuCapture?: MouseEventHandler;
|
||||
onDblClick?: MouseEventHandler;
|
||||
onDblClickCapture?: MouseEventHandler;
|
||||
onDrag?: DragEventHandler;
|
||||
onDragCapture?: DragEventHandler;
|
||||
onDragEnd?: DragEventHandler;
|
||||
onDragEndCapture?: DragEventHandler;
|
||||
onDragEnter?: DragEventHandler;
|
||||
onDragEnterCapture?: DragEventHandler;
|
||||
onDragExit?: DragEventHandler;
|
||||
onDragExitCapture?: DragEventHandler;
|
||||
onDragLeave?: DragEventHandler;
|
||||
onDragLeaveCapture?: DragEventHandler;
|
||||
onDragOver?: DragEventHandler;
|
||||
onDragOverCapture?: DragEventHandler;
|
||||
onDragStart?: DragEventHandler;
|
||||
onDragStartCapture?: DragEventHandler;
|
||||
onDrop?: DragEventHandler;
|
||||
onDropCapture?: DragEventHandler;
|
||||
onMouseDown?: MouseEventHandler;
|
||||
onMouseDownCapture?: MouseEventHandler;
|
||||
onMouseEnter?: MouseEventHandler;
|
||||
onMouseEnterCapture?: MouseEventHandler;
|
||||
onMouseLeave?: MouseEventHandler;
|
||||
onMouseLeaveCapture?: MouseEventHandler;
|
||||
onMouseMove?: MouseEventHandler;
|
||||
onMouseMoveCapture?: MouseEventHandler;
|
||||
onMouseOut?: MouseEventHandler;
|
||||
onMouseOutCapture?: MouseEventHandler;
|
||||
onMouseOver?: MouseEventHandler;
|
||||
onMouseOverCapture?: MouseEventHandler;
|
||||
onMouseUp?: MouseEventHandler;
|
||||
onMouseUpCapture?: MouseEventHandler;
|
||||
|
||||
// Selection Events
|
||||
onSelect?: GenericEventHandler;
|
||||
onSelectCapture?: GenericEventHandler;
|
||||
|
||||
// Touch Events
|
||||
onTouchCancel?: TouchEventHandler;
|
||||
onTouchCancelCapture?: TouchEventHandler;
|
||||
onTouchEnd?: TouchEventHandler;
|
||||
onTouchEndCapture?: TouchEventHandler;
|
||||
onTouchMove?: TouchEventHandler;
|
||||
onTouchMoveCapture?: TouchEventHandler;
|
||||
onTouchStart?: TouchEventHandler;
|
||||
onTouchStartCapture?: TouchEventHandler;
|
||||
|
||||
// Pointer Events
|
||||
onPointerOver?: PointerEventHandler;
|
||||
onPointerOverCapture?: PointerEventHandler;
|
||||
onPointerEnter?: PointerEventHandler;
|
||||
onPointerEnterCapture?: PointerEventHandler;
|
||||
onPointerDown?: PointerEventHandler;
|
||||
onPointerDownCapture?: PointerEventHandler;
|
||||
onPointerMove?: PointerEventHandler;
|
||||
onPointerMoveCapture?: PointerEventHandler;
|
||||
onPointerUp?: PointerEventHandler;
|
||||
onPointerUpCapture?: PointerEventHandler;
|
||||
onPointerCancel?: PointerEventHandler;
|
||||
onPointerCancelCapture?: PointerEventHandler;
|
||||
onPointerOut?: PointerEventHandler;
|
||||
onPointerOutCapture?: PointerEventHandler;
|
||||
onPointerLeave?: PointerEventHandler;
|
||||
onPointerLeaveCapture?: PointerEventHandler;
|
||||
onGotPointerCapture?: PointerEventHandler;
|
||||
onGotPointerCaptureCapture?: PointerEventHandler;
|
||||
onLostPointerCapture?: PointerEventHandler;
|
||||
onLostPointerCaptureCapture?: PointerEventHandler;
|
||||
|
||||
// UI Events
|
||||
onScroll?: UIEventHandler;
|
||||
onScrollCapture?: UIEventHandler;
|
||||
|
||||
// Wheel Events
|
||||
onWheel?: WheelEventHandler;
|
||||
onWheelCapture?: WheelEventHandler;
|
||||
|
||||
// Animation Events
|
||||
onAnimationStart?: AnimationEventHandler;
|
||||
onAnimationStartCapture?: AnimationEventHandler;
|
||||
onAnimationEnd?: AnimationEventHandler;
|
||||
onAnimationEndCapture?: AnimationEventHandler;
|
||||
onAnimationIteration?: AnimationEventHandler;
|
||||
onAnimationIterationCapture?: AnimationEventHandler;
|
||||
|
||||
// Transition Events
|
||||
onTransitionEnd?: TransitionEventHandler;
|
||||
onTransitionEndCapture?: TransitionEventHandler;
|
||||
}
|
||||
|
||||
interface HTMLAttributes extends omi.PreactHTMLAttributes, DOMAttributes {
|
||||
// Standard HTML Attributes
|
||||
accept?: string;
|
||||
acceptCharset?: string;
|
||||
accessKey?: string;
|
||||
action?: string;
|
||||
allowFullScreen?: boolean;
|
||||
allowTransparency?: boolean;
|
||||
alt?: string;
|
||||
async?: boolean;
|
||||
autocomplete?: string;
|
||||
autofocus?: boolean;
|
||||
autoPlay?: boolean;
|
||||
capture?: boolean;
|
||||
cellPadding?: number | string;
|
||||
cellSpacing?: number | string;
|
||||
charSet?: string;
|
||||
challenge?: string;
|
||||
checked?: boolean;
|
||||
class?: string;
|
||||
className?: string;
|
||||
cols?: number;
|
||||
colSpan?: number;
|
||||
content?: string;
|
||||
contentEditable?: boolean;
|
||||
contextMenu?: string;
|
||||
controls?: boolean;
|
||||
controlsList?: string;
|
||||
coords?: string;
|
||||
crossOrigin?: string;
|
||||
data?: string;
|
||||
dateTime?: string;
|
||||
default?: boolean;
|
||||
defer?: boolean;
|
||||
dir?: string;
|
||||
disabled?: boolean;
|
||||
download?: any;
|
||||
draggable?: boolean;
|
||||
encType?: string;
|
||||
form?: string;
|
||||
formAction?: string;
|
||||
formEncType?: string;
|
||||
formMethod?: string;
|
||||
formNoValidate?: boolean;
|
||||
formTarget?: string;
|
||||
frameBorder?: number | string;
|
||||
headers?: string;
|
||||
height?: number | string;
|
||||
hidden?: boolean;
|
||||
high?: number;
|
||||
href?: string;
|
||||
hrefLang?: string;
|
||||
for?: string;
|
||||
httpEquiv?: string;
|
||||
icon?: string;
|
||||
id?: string;
|
||||
inputMode?: string;
|
||||
integrity?: string;
|
||||
is?: string;
|
||||
keyParams?: string;
|
||||
keyType?: string;
|
||||
kind?: string;
|
||||
label?: string;
|
||||
lang?: string;
|
||||
list?: string;
|
||||
loop?: boolean;
|
||||
low?: number;
|
||||
manifest?: string;
|
||||
marginHeight?: number;
|
||||
marginWidth?: number;
|
||||
max?: number | string;
|
||||
maxLength?: number;
|
||||
media?: string;
|
||||
mediaGroup?: string;
|
||||
method?: string;
|
||||
min?: number | string;
|
||||
minLength?: number;
|
||||
multiple?: boolean;
|
||||
muted?: boolean;
|
||||
name?: string;
|
||||
noValidate?: boolean;
|
||||
open?: boolean;
|
||||
optimum?: number;
|
||||
pattern?: string;
|
||||
placeholder?: string;
|
||||
playsInline?: boolean;
|
||||
poster?: string;
|
||||
preload?: string;
|
||||
radioGroup?: string;
|
||||
readOnly?: boolean;
|
||||
rel?: string;
|
||||
required?: boolean;
|
||||
role?: string;
|
||||
rows?: number;
|
||||
rowSpan?: number;
|
||||
sandbox?: string;
|
||||
scope?: string;
|
||||
scoped?: boolean;
|
||||
scrolling?: string;
|
||||
seamless?: boolean;
|
||||
selected?: boolean;
|
||||
shape?: string;
|
||||
size?: number;
|
||||
sizes?: string;
|
||||
slot?: string;
|
||||
span?: number;
|
||||
spellcheck?: boolean;
|
||||
src?: string;
|
||||
srcset?: string;
|
||||
srcDoc?: string;
|
||||
srcLang?: string;
|
||||
srcSet?: string;
|
||||
start?: number;
|
||||
step?: number | string;
|
||||
style?: any;
|
||||
summary?: string;
|
||||
tabIndex?: number;
|
||||
target?: string;
|
||||
title?: string;
|
||||
type?: string;
|
||||
useMap?: string;
|
||||
value?: string | string[] | number;
|
||||
width?: number | string;
|
||||
wmode?: string;
|
||||
wrap?: string;
|
||||
|
||||
// RDFa Attributes
|
||||
about?: string;
|
||||
datatype?: string;
|
||||
inlist?: any;
|
||||
prefix?: string;
|
||||
property?: string;
|
||||
resource?: string;
|
||||
typeof?: string;
|
||||
vocab?: string;
|
||||
}
|
||||
|
||||
interface IntrinsicElements {
|
||||
// HTML
|
||||
a: HTMLAttributes;
|
||||
abbr: HTMLAttributes;
|
||||
address: HTMLAttributes;
|
||||
area: HTMLAttributes;
|
||||
article: HTMLAttributes;
|
||||
aside: HTMLAttributes;
|
||||
audio: HTMLAttributes;
|
||||
b: HTMLAttributes;
|
||||
base: HTMLAttributes;
|
||||
bdi: HTMLAttributes;
|
||||
bdo: HTMLAttributes;
|
||||
big: HTMLAttributes;
|
||||
blockquote: HTMLAttributes;
|
||||
body: HTMLAttributes;
|
||||
br: HTMLAttributes;
|
||||
button: HTMLAttributes;
|
||||
canvas: HTMLAttributes;
|
||||
caption: HTMLAttributes;
|
||||
cite: HTMLAttributes;
|
||||
code: HTMLAttributes;
|
||||
col: HTMLAttributes;
|
||||
colgroup: HTMLAttributes;
|
||||
data: HTMLAttributes;
|
||||
datalist: HTMLAttributes;
|
||||
dd: HTMLAttributes;
|
||||
del: HTMLAttributes;
|
||||
details: HTMLAttributes;
|
||||
dfn: HTMLAttributes;
|
||||
dialog: HTMLAttributes;
|
||||
div: HTMLAttributes;
|
||||
dl: HTMLAttributes;
|
||||
dt: HTMLAttributes;
|
||||
em: HTMLAttributes;
|
||||
embed: HTMLAttributes;
|
||||
fieldset: HTMLAttributes;
|
||||
figcaption: HTMLAttributes;
|
||||
figure: HTMLAttributes;
|
||||
footer: HTMLAttributes;
|
||||
form: HTMLAttributes;
|
||||
h1: HTMLAttributes;
|
||||
h2: HTMLAttributes;
|
||||
h3: HTMLAttributes;
|
||||
h4: HTMLAttributes;
|
||||
h5: HTMLAttributes;
|
||||
h6: HTMLAttributes;
|
||||
head: HTMLAttributes;
|
||||
header: HTMLAttributes;
|
||||
hr: HTMLAttributes;
|
||||
html: HTMLAttributes;
|
||||
i: HTMLAttributes;
|
||||
iframe: HTMLAttributes;
|
||||
img: HTMLAttributes;
|
||||
input: HTMLAttributes;
|
||||
ins: HTMLAttributes;
|
||||
kbd: HTMLAttributes;
|
||||
keygen: HTMLAttributes;
|
||||
label: HTMLAttributes;
|
||||
legend: HTMLAttributes;
|
||||
li: HTMLAttributes;
|
||||
link: HTMLAttributes;
|
||||
main: HTMLAttributes;
|
||||
map: HTMLAttributes;
|
||||
mark: HTMLAttributes;
|
||||
menu: HTMLAttributes;
|
||||
menuitem: HTMLAttributes;
|
||||
meta: HTMLAttributes;
|
||||
meter: HTMLAttributes;
|
||||
nav: HTMLAttributes;
|
||||
noscript: HTMLAttributes;
|
||||
object: HTMLAttributes;
|
||||
ol: HTMLAttributes;
|
||||
optgroup: HTMLAttributes;
|
||||
option: HTMLAttributes;
|
||||
output: HTMLAttributes;
|
||||
p: HTMLAttributes;
|
||||
param: HTMLAttributes;
|
||||
picture: HTMLAttributes;
|
||||
pre: HTMLAttributes;
|
||||
progress: HTMLAttributes;
|
||||
q: HTMLAttributes;
|
||||
rp: HTMLAttributes;
|
||||
rt: HTMLAttributes;
|
||||
ruby: HTMLAttributes;
|
||||
s: HTMLAttributes;
|
||||
samp: HTMLAttributes;
|
||||
script: HTMLAttributes;
|
||||
section: HTMLAttributes;
|
||||
select: HTMLAttributes;
|
||||
slot: HTMLAttributes;
|
||||
small: HTMLAttributes;
|
||||
source: HTMLAttributes;
|
||||
span: HTMLAttributes;
|
||||
strong: HTMLAttributes;
|
||||
style: HTMLAttributes;
|
||||
sub: HTMLAttributes;
|
||||
summary: HTMLAttributes;
|
||||
sup: HTMLAttributes;
|
||||
table: HTMLAttributes;
|
||||
tbody: HTMLAttributes;
|
||||
td: HTMLAttributes;
|
||||
textarea: HTMLAttributes;
|
||||
tfoot: HTMLAttributes;
|
||||
th: HTMLAttributes;
|
||||
thead: HTMLAttributes;
|
||||
time: HTMLAttributes;
|
||||
title: HTMLAttributes;
|
||||
tr: HTMLAttributes;
|
||||
track: HTMLAttributes;
|
||||
u: HTMLAttributes;
|
||||
ul: HTMLAttributes;
|
||||
"var": HTMLAttributes;
|
||||
video: HTMLAttributes;
|
||||
wbr: HTMLAttributes;
|
||||
|
||||
//SVG
|
||||
svg: SVGAttributes;
|
||||
animate: SVGAttributes;
|
||||
circle: SVGAttributes;
|
||||
clipPath: SVGAttributes;
|
||||
defs: SVGAttributes;
|
||||
desc: SVGAttributes;
|
||||
ellipse: SVGAttributes;
|
||||
feBlend: SVGAttributes;
|
||||
feColorMatrix: SVGAttributes;
|
||||
feComponentTransfer: SVGAttributes;
|
||||
feComposite: SVGAttributes;
|
||||
feConvolveMatrix: SVGAttributes;
|
||||
feDiffuseLighting: SVGAttributes;
|
||||
feDisplacementMap: SVGAttributes;
|
||||
feFlood: SVGAttributes;
|
||||
feGaussianBlur: SVGAttributes;
|
||||
feImage: SVGAttributes;
|
||||
feMerge: SVGAttributes;
|
||||
feMergeNode: SVGAttributes;
|
||||
feMorphology: SVGAttributes;
|
||||
feOffset: SVGAttributes;
|
||||
feSpecularLighting: SVGAttributes;
|
||||
feTile: SVGAttributes;
|
||||
feTurbulence: SVGAttributes;
|
||||
filter: SVGAttributes;
|
||||
foreignObject: SVGAttributes;
|
||||
g: SVGAttributes;
|
||||
image: SVGAttributes;
|
||||
line: SVGAttributes;
|
||||
linearGradient: SVGAttributes;
|
||||
marker: SVGAttributes;
|
||||
mask: SVGAttributes;
|
||||
path: SVGAttributes;
|
||||
pattern: SVGAttributes;
|
||||
polygon: SVGAttributes;
|
||||
polyline: SVGAttributes;
|
||||
radialGradient: SVGAttributes;
|
||||
rect: SVGAttributes;
|
||||
stop: SVGAttributes;
|
||||
symbol: SVGAttributes;
|
||||
text: SVGAttributes;
|
||||
tspan: SVGAttributes;
|
||||
use: SVGAttributes;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import { h, h as createElement } from './h';
|
||||
import { cloneElement } from './clone-element';
|
||||
import { Component } from './component';
|
||||
import { render } from './render';
|
||||
import { rerender } from './render-queue';
|
||||
import options from './options';
|
||||
|
||||
function createRef() {
|
||||
return {};
|
||||
}
|
||||
|
||||
export default {
|
||||
h,
|
||||
createElement,
|
||||
cloneElement,
|
||||
createRef,
|
||||
Component,
|
||||
render,
|
||||
rerender,
|
||||
options
|
||||
};
|
||||
|
||||
export {
|
||||
h,
|
||||
createElement,
|
||||
cloneElement,
|
||||
createRef,
|
||||
Component,
|
||||
render,
|
||||
rerender,
|
||||
options
|
||||
};
|
||||
|
||||
if(typeof window !== 'undefined'){
|
||||
window.Omi = {
|
||||
h,
|
||||
createElement,
|
||||
cloneElement,
|
||||
createRef,
|
||||
Component,
|
||||
render,
|
||||
rerender,
|
||||
options
|
||||
};
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/* @flow */
|
||||
|
||||
import { createElement, cloneElement, createRef, Component, type Node } from 'react';
|
||||
|
||||
declare var h: typeof createElement;
|
||||
|
||||
declare function render(vnode: Node, parent: Element, toReplace?: Element): Element;
|
||||
|
||||
export { h, createElement, cloneElement, createRef, Component, render };
|
||||
export default { h, createElement, cloneElement, createRef, Component, render };
|
||||
|
||||
declare type VNode<P> = {
|
||||
nodeName: string | Function,
|
||||
children: Array<VNode<P> | string>,
|
||||
key?: string | number | void,
|
||||
attributes: P,
|
||||
};
|
||||
|
||||
declare export function rerender(): void;
|
||||
declare export var options: {
|
||||
syncComponentUpdates?: boolean,
|
||||
vnode?: (vnode: VNode<any>) => void,
|
||||
debounceRendering?: (rerender: () => void) => void,
|
||||
event?: (event: Event) => Event | void,
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* @typedef {import('./component').Component} Component
|
||||
* @typedef {import('./vnode').VNode} VNode
|
||||
*/
|
||||
|
||||
/**
|
||||
* Global options
|
||||
* @public
|
||||
* @typedef Options
|
||||
* @property {boolean} [syncComponentUpdates] If `true`, `prop` changes trigger synchronous component updates. Defaults to true.
|
||||
* @property {(vnode: VNode) => void} [vnode] Processes all created VNodes.
|
||||
* @property {(component: Component) => void} [afterMount] Hook invoked after a component is mounted.
|
||||
* @property {(component: Component) => void} [afterUpdate] Hook invoked after the DOM is updated with a component's latest render.
|
||||
* @property {(component: Component) => void} [beforeUnmount] Hook invoked immediately before a component is unmounted.
|
||||
* @property {(rerender: function) => void} [debounceRendering] Hook invoked whenever a rerender is requested. Can be used to debounce rerenders.
|
||||
* @property {(event: Event) => Event | void} [event] Hook invoked before any Preact event listeners. The return value (if any) replaces the native browser event given to event listeners
|
||||
*/
|
||||
|
||||
/** @type {Options} */
|
||||
const options = {};
|
||||
|
||||
export default options;
|
|
@ -0,0 +1,27 @@
|
|||
import options from './options';
|
||||
import { defer } from './util';
|
||||
import { renderComponent } from './vdom/component';
|
||||
|
||||
/**
|
||||
* Managed queue of dirty components to be re-rendered
|
||||
* @type {Array<import('./component').Component>}
|
||||
*/
|
||||
let items = [];
|
||||
|
||||
/**
|
||||
* Enqueue a rerender of a component
|
||||
* @param {import('./component').Component} component The component to rerender
|
||||
*/
|
||||
export function enqueueRender(component) {
|
||||
if (!component._dirty && (component._dirty = true) && items.push(component)==1) {
|
||||
(options.debounceRendering || defer)(rerender);
|
||||
}
|
||||
}
|
||||
|
||||
/** Rerender all enqueued dirty components */
|
||||
export function rerender() {
|
||||
let p;
|
||||
while ( (p = items.pop()) ) {
|
||||
if (p._dirty) renderComponent(p);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import { diff } from './vdom/diff';
|
||||
|
||||
/**
|
||||
* Render JSX into a `parent` Element.
|
||||
* @param {import('./vnode').VNode} vnode A (JSX) VNode to render
|
||||
* @param {import('./dom').PreactElement} parent DOM element to render into
|
||||
* @param {import('./dom').PreactElement} [merge] Attempt to re-use an existing DOM tree rooted at
|
||||
* `merge`
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* // render a div into <body>:
|
||||
* render(<div id="hello">hello!</div>, document.body);
|
||||
*
|
||||
* @example
|
||||
* // render a "Thing" component into #foo:
|
||||
* const Thing = ({ name }) => <span>{ name }</span>;
|
||||
* render(<Thing name="one" />, document.querySelector('#foo'));
|
||||
*/
|
||||
export function render(vnode, parent, merge) {
|
||||
return diff(merge, vnode, {}, false, parent, false);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Copy all properties from `props` onto `obj`.
|
||||
* @param {object} obj Object onto which properties should be copied.
|
||||
* @param {object} props Object from which to copy properties.
|
||||
* @returns {object}
|
||||
* @private
|
||||
*/
|
||||
export function extend(obj, props) {
|
||||
for (let i in props) obj[i] = props[i];
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Invoke or update a ref, depending on whether it is a function or object ref.
|
||||
* @param {object|function} [ref=null]
|
||||
* @param {any} [value]
|
||||
*/
|
||||
export function applyRef(ref, value) {
|
||||
if (ref) {
|
||||
if (typeof ref=='function') ref(value);
|
||||
else ref.current = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a function asynchronously, as soon as possible. Makes
|
||||
* use of HTML Promise to schedule the callback if available,
|
||||
* otherwise falling back to `setTimeout` (mainly for IE<11).
|
||||
* @type {(callback: function) => void}
|
||||
*/
|
||||
export const defer = typeof Promise=='function' ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout;
|
|
@ -0,0 +1,48 @@
|
|||
import { Component } from '../component';
|
||||
|
||||
/**
|
||||
* Retains a pool of Components for re-use.
|
||||
* @type {Component[]}
|
||||
* @private
|
||||
*/
|
||||
export const recyclerComponents = [];
|
||||
|
||||
|
||||
/**
|
||||
* Create a component. Normalizes differences between PFC's and classful
|
||||
* Components.
|
||||
* @param {function} Ctor The constructor of the component to create
|
||||
* @param {object} props The initial props of the component
|
||||
* @param {object} context The initial context of the component
|
||||
* @returns {import('../component').Component}
|
||||
*/
|
||||
export function createComponent(Ctor, props, context) {
|
||||
let inst, i = recyclerComponents.length;
|
||||
|
||||
if (Ctor.prototype && Ctor.prototype.render) {
|
||||
inst = new Ctor(props, context);
|
||||
Component.call(inst, props, context);
|
||||
}
|
||||
else {
|
||||
inst = new Component(props, context);
|
||||
inst.constructor = Ctor;
|
||||
inst.render = doRender;
|
||||
}
|
||||
|
||||
|
||||
while (i--) {
|
||||
if (recyclerComponents[i].constructor===Ctor) {
|
||||
inst.nextBase = recyclerComponents[i].nextBase;
|
||||
recyclerComponents.splice(i, 1);
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
|
||||
/** The `.render()` method for a PFC backing instance. */
|
||||
function doRender(props, state, context) {
|
||||
return this.constructor(props, context);
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
import { SYNC_RENDER, NO_RENDER, FORCE_RENDER, ASYNC_RENDER, ATTR_KEY } from '../constants';
|
||||
import options from '../options';
|
||||
import { extend, applyRef } from '../util';
|
||||
import { enqueueRender } from '../render-queue';
|
||||
import { getNodeProps } from './index';
|
||||
import { diff, mounts, diffLevel, flushMounts, recollectNodeTree, removeChildren } from './diff';
|
||||
import { createComponent, recyclerComponents } from './component-recycler';
|
||||
import { removeNode } from '../dom/index';
|
||||
|
||||
/**
|
||||
* Set a component's `props` and possibly re-render the component
|
||||
* @param {import('../component').Component} component The Component to set props on
|
||||
* @param {object} props The new props
|
||||
* @param {number} renderMode Render options - specifies how to re-render the component
|
||||
* @param {object} context The new context
|
||||
* @param {boolean} mountAll Whether or not to immediately mount all components
|
||||
*/
|
||||
export function setComponentProps(component, props, renderMode, context, mountAll) {
|
||||
if (component._disable) return;
|
||||
component._disable = true;
|
||||
|
||||
component.__ref = props.ref;
|
||||
component.__key = props.key;
|
||||
delete props.ref;
|
||||
delete props.key;
|
||||
|
||||
if (typeof component.constructor.getDerivedStateFromProps === 'undefined') {
|
||||
if (!component.base || mountAll) {
|
||||
if (component.componentWillMount) component.componentWillMount();
|
||||
}
|
||||
else if (component.componentWillReceiveProps) {
|
||||
component.componentWillReceiveProps(props, context);
|
||||
}
|
||||
}
|
||||
|
||||
if (context && context!==component.context) {
|
||||
if (!component.prevContext) component.prevContext = component.context;
|
||||
component.context = context;
|
||||
}
|
||||
|
||||
if (!component.prevProps) component.prevProps = component.props;
|
||||
component.props = props;
|
||||
|
||||
component._disable = false;
|
||||
|
||||
if (renderMode!==NO_RENDER) {
|
||||
if (renderMode===SYNC_RENDER || options.syncComponentUpdates!==false || !component.base) {
|
||||
renderComponent(component, SYNC_RENDER, mountAll);
|
||||
}
|
||||
else {
|
||||
enqueueRender(component);
|
||||
}
|
||||
}
|
||||
|
||||
applyRef(component.__ref, component);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Render a Component, triggering necessary lifecycle events and taking
|
||||
* High-Order Components into account.
|
||||
* @param {import('../component').Component} component The component to render
|
||||
* @param {number} [renderMode] render mode, see constants.js for available options.
|
||||
* @param {boolean} [mountAll] Whether or not to immediately mount all components
|
||||
* @param {boolean} [isChild] ?
|
||||
* @private
|
||||
*/
|
||||
export function renderComponent(component, renderMode, mountAll, isChild) {
|
||||
if (component._disable) return;
|
||||
|
||||
let props = component.props,
|
||||
state = component.state,
|
||||
context = component.context,
|
||||
previousProps = component.prevProps || props,
|
||||
previousState = component.prevState || state,
|
||||
previousContext = component.prevContext || context,
|
||||
isUpdate = component.base,
|
||||
nextBase = component.nextBase,
|
||||
initialBase = isUpdate || nextBase,
|
||||
initialChildComponent = component._component,
|
||||
skip = false,
|
||||
snapshot = previousContext,
|
||||
rendered, inst, cbase;
|
||||
|
||||
if (component.constructor.getDerivedStateFromProps) {
|
||||
state = extend(extend({}, state), component.constructor.getDerivedStateFromProps(props, state));
|
||||
component.state = state;
|
||||
}
|
||||
|
||||
// if updating
|
||||
if (isUpdate) {
|
||||
component.props = previousProps;
|
||||
component.state = previousState;
|
||||
component.context = previousContext;
|
||||
if (renderMode!==FORCE_RENDER
|
||||
&& component.shouldComponentUpdate
|
||||
&& component.shouldComponentUpdate(props, state, context) === false) {
|
||||
skip = true;
|
||||
}
|
||||
else if (component.componentWillUpdate) {
|
||||
component.componentWillUpdate(props, state, context);
|
||||
}
|
||||
component.props = props;
|
||||
component.state = state;
|
||||
component.context = context;
|
||||
}
|
||||
|
||||
component.prevProps = component.prevState = component.prevContext = component.nextBase = null;
|
||||
component._dirty = false;
|
||||
|
||||
if (!skip) {
|
||||
rendered = component.render(props, state, context);
|
||||
|
||||
// context to pass to the child, can be updated via (grand-)parent component
|
||||
if (component.getChildContext) {
|
||||
context = extend(extend({}, context), component.getChildContext());
|
||||
}
|
||||
|
||||
if (isUpdate && component.getSnapshotBeforeUpdate) {
|
||||
snapshot = component.getSnapshotBeforeUpdate(previousProps, previousState);
|
||||
}
|
||||
|
||||
let childComponent = rendered && rendered.nodeName,
|
||||
toUnmount, base;
|
||||
|
||||
if (typeof childComponent==='function') {
|
||||
// set up high order component link
|
||||
|
||||
let childProps = getNodeProps(rendered);
|
||||
inst = initialChildComponent;
|
||||
|
||||
if (inst && inst.constructor===childComponent && childProps.key==inst.__key) {
|
||||
setComponentProps(inst, childProps, SYNC_RENDER, context, false);
|
||||
}
|
||||
else {
|
||||
toUnmount = inst;
|
||||
|
||||
component._component = inst = createComponent(childComponent, childProps, context);
|
||||
inst.nextBase = inst.nextBase || nextBase;
|
||||
inst._parentComponent = component;
|
||||
setComponentProps(inst, childProps, NO_RENDER, context, false);
|
||||
renderComponent(inst, SYNC_RENDER, mountAll, true);
|
||||
}
|
||||
|
||||
base = inst.base;
|
||||
}
|
||||
else {
|
||||
cbase = initialBase;
|
||||
|
||||
// destroy high order component link
|
||||
toUnmount = initialChildComponent;
|
||||
if (toUnmount) {
|
||||
cbase = component._component = null;
|
||||
}
|
||||
|
||||
if (initialBase || renderMode===SYNC_RENDER) {
|
||||
if (cbase) cbase._component = null;
|
||||
base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (initialBase && base!==initialBase && inst!==initialChildComponent) {
|
||||
let baseParent = initialBase.parentNode;
|
||||
if (baseParent && base!==baseParent) {
|
||||
baseParent.replaceChild(base, initialBase);
|
||||
|
||||
if (!toUnmount) {
|
||||
initialBase._component = null;
|
||||
recollectNodeTree(initialBase, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toUnmount) {
|
||||
unmountComponent(toUnmount);
|
||||
}
|
||||
|
||||
component.base = base;
|
||||
if (base && !isChild) {
|
||||
let componentRef = component,
|
||||
t = component;
|
||||
while ((t=t._parentComponent)) {
|
||||
(componentRef = t).base = base;
|
||||
}
|
||||
base._component = componentRef;
|
||||
base._componentConstructor = componentRef.constructor;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isUpdate || mountAll) {
|
||||
mounts.push(component);
|
||||
}
|
||||
else if (!skip) {
|
||||
// Ensure that pending componentDidMount() hooks of child components
|
||||
// are called before the componentDidUpdate() hook in the parent.
|
||||
// Note: disabled as it causes duplicate hooks, see https://github.com/developit/omi/issues/750
|
||||
// flushMounts();
|
||||
|
||||
if (component.componentDidUpdate) {
|
||||
component.componentDidUpdate(previousProps, previousState, snapshot);
|
||||
}
|
||||
if (options.afterUpdate) options.afterUpdate(component);
|
||||
}
|
||||
|
||||
while (component._renderCallbacks.length) component._renderCallbacks.pop().call(component);
|
||||
|
||||
if (!diffLevel && !isChild) flushMounts();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Apply the Component referenced by a VNode to the DOM.
|
||||
* @param {import('../dom').PreactElement} dom The DOM node to mutate
|
||||
* @param {import('../vnode').VNode} vnode A Component-referencing VNode
|
||||
* @param {object} context The current context
|
||||
* @param {boolean} mountAll Whether or not to immediately mount all components
|
||||
* @returns {import('../dom').PreactElement} The created/mutated element
|
||||
* @private
|
||||
*/
|
||||
export function buildComponentFromVNode(dom, vnode, context, mountAll) {
|
||||
let c = dom && dom._component,
|
||||
originalComponent = c,
|
||||
oldDom = dom,
|
||||
isDirectOwner = c && dom._componentConstructor===vnode.nodeName,
|
||||
isOwner = isDirectOwner,
|
||||
props = getNodeProps(vnode);
|
||||
while (c && !isOwner && (c=c._parentComponent)) {
|
||||
isOwner = c.constructor===vnode.nodeName;
|
||||
}
|
||||
|
||||
if (c && isOwner && (!mountAll || c._component)) {
|
||||
setComponentProps(c, props, ASYNC_RENDER, context, mountAll);
|
||||
dom = c.base;
|
||||
}
|
||||
else {
|
||||
if (originalComponent && !isDirectOwner) {
|
||||
unmountComponent(originalComponent);
|
||||
dom = oldDom = null;
|
||||
}
|
||||
|
||||
c = createComponent(vnode.nodeName, props, context);
|
||||
if (dom && !c.nextBase) {
|
||||
c.nextBase = dom;
|
||||
// passing dom/oldDom as nextBase will recycle it if unused, so bypass recycling on L229:
|
||||
oldDom = null;
|
||||
}
|
||||
setComponentProps(c, props, SYNC_RENDER, context, mountAll);
|
||||
dom = c.base;
|
||||
|
||||
if (oldDom && dom!==oldDom) {
|
||||
oldDom._component = null;
|
||||
recollectNodeTree(oldDom, false);
|
||||
}
|
||||
}
|
||||
|
||||
return dom;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Remove a component from the DOM and recycle it.
|
||||
* @param {import('../component').Component} component The Component instance to unmount
|
||||
* @private
|
||||
*/
|
||||
export function unmountComponent(component) {
|
||||
if (options.beforeUnmount) options.beforeUnmount(component);
|
||||
|
||||
let base = component.base;
|
||||
|
||||
component._disable = true;
|
||||
|
||||
if (component.componentWillUnmount) component.componentWillUnmount();
|
||||
|
||||
component.base = null;
|
||||
|
||||
// recursively tear down & recollect high-order component children:
|
||||
let inner = component._component;
|
||||
if (inner) {
|
||||
unmountComponent(inner);
|
||||
}
|
||||
else if (base) {
|
||||
if (base[ATTR_KEY]!=null) applyRef(base[ATTR_KEY].ref, null);
|
||||
|
||||
component.nextBase = base;
|
||||
|
||||
removeNode(base);
|
||||
recyclerComponents.push(component);
|
||||
|
||||
removeChildren(base);
|
||||
}
|
||||
|
||||
applyRef(component.__ref, null);
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
import { ATTR_KEY } from '../constants';
|
||||
import { isSameNodeType, isNamedNode } from './index';
|
||||
import { buildComponentFromVNode } from './component';
|
||||
import { createNode, setAccessor } from '../dom/index';
|
||||
import { unmountComponent } from './component';
|
||||
import options from '../options';
|
||||
import { applyRef } from '../util';
|
||||
import { removeNode } from '../dom/index';
|
||||
|
||||
/**
|
||||
* Queue of components that have been mounted and are awaiting componentDidMount
|
||||
* @type {Array<import('../component').Component>}
|
||||
*/
|
||||
export const mounts = [];
|
||||
|
||||
/** Diff recursion count, used to track the end of the diff cycle. */
|
||||
export let diffLevel = 0;
|
||||
|
||||
/** Global flag indicating if the diff is currently within an SVG */
|
||||
let isSvgMode = false;
|
||||
|
||||
/** Global flag indicating if the diff is performing hydration */
|
||||
let hydrating = false;
|
||||
|
||||
/** Invoke queued componentDidMount lifecycle methods */
|
||||
export function flushMounts() {
|
||||
let c;
|
||||
while ((c = mounts.shift())) {
|
||||
if (options.afterMount) options.afterMount(c);
|
||||
if (c.componentDidMount) c.componentDidMount();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply differences in a given vnode (and it's deep children) to a real DOM Node.
|
||||
* @param {import('../dom').PreactElement} dom A DOM node to mutate into the shape of a `vnode`
|
||||
* @param {import('../vnode').VNode} vnode A VNode (with descendants forming a tree) representing
|
||||
* the desired DOM structure
|
||||
* @param {object} context The current context
|
||||
* @param {boolean} mountAll Whether or not to immediately mount all components
|
||||
* @param {Element} parent ?
|
||||
* @param {boolean} componentRoot ?
|
||||
* @returns {import('../dom').PreactElement} The created/mutated element
|
||||
* @private
|
||||
*/
|
||||
export function diff(dom, vnode, context, mountAll, parent, componentRoot) {
|
||||
// diffLevel having been 0 here indicates initial entry into the diff (not a subdiff)
|
||||
if (!diffLevel++) {
|
||||
// when first starting the diff, check if we're diffing an SVG or within an SVG
|
||||
isSvgMode = parent!=null && parent.ownerSVGElement!==undefined;
|
||||
|
||||
// hydration is indicated by the existing element to be diffed not having a prop cache
|
||||
hydrating = dom!=null && !(ATTR_KEY in dom);
|
||||
}
|
||||
|
||||
let ret = idiff(dom, vnode, context, mountAll, componentRoot);
|
||||
|
||||
// append the element if its a new parent
|
||||
if (parent && ret.parentNode!==parent) parent.appendChild(ret);
|
||||
|
||||
// diffLevel being reduced to 0 means we're exiting the diff
|
||||
if (!--diffLevel) {
|
||||
hydrating = false;
|
||||
// invoke queued componentDidMount lifecycle methods
|
||||
if (!componentRoot) flushMounts();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internals of `diff()`, separated to allow bypassing diffLevel / mount flushing.
|
||||
* @param {import('../dom').PreactElement} dom A DOM node to mutate into the shape of a `vnode`
|
||||
* @param {import('../vnode').VNode} vnode A VNode (with descendants forming a tree) representing the desired DOM structure
|
||||
* @param {object} context The current context
|
||||
* @param {boolean} mountAll Whether or not to immediately mount all components
|
||||
* @param {boolean} [componentRoot] ?
|
||||
* @private
|
||||
*/
|
||||
function idiff(dom, vnode, context, mountAll, componentRoot) {
|
||||
let out = dom,
|
||||
prevSvgMode = isSvgMode;
|
||||
|
||||
// empty values (null, undefined, booleans) render as empty Text nodes
|
||||
if (vnode==null || typeof vnode==='boolean') vnode = '';
|
||||
|
||||
|
||||
// Fast case: Strings & Numbers create/update Text nodes.
|
||||
if (typeof vnode==='string' || typeof vnode==='number') {
|
||||
|
||||
// update if it's already a Text node:
|
||||
if (dom && dom.splitText!==undefined && dom.parentNode && (!dom._component || componentRoot)) {
|
||||
/* istanbul ignore if */ /* Browser quirk that can't be covered: https://github.com/developit/omi/commit/fd4f21f5c45dfd75151bd27b4c217d8003aa5eb9 */
|
||||
if (dom.nodeValue!=vnode) {
|
||||
dom.nodeValue = vnode;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// it wasn't a Text node: replace it with one and recycle the old Element
|
||||
out = document.createTextNode(vnode);
|
||||
if (dom) {
|
||||
if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
|
||||
recollectNodeTree(dom, true);
|
||||
}
|
||||
}
|
||||
|
||||
out[ATTR_KEY] = true;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// If the VNode represents a Component, perform a component diff:
|
||||
let vnodeName = vnode.nodeName;
|
||||
if (typeof vnodeName==='function') {
|
||||
return buildComponentFromVNode(dom, vnode, context, mountAll);
|
||||
}
|
||||
|
||||
|
||||
// Tracks entering and exiting SVG namespace when descending through the tree.
|
||||
isSvgMode = vnodeName==='svg' ? true : vnodeName==='foreignObject' ? false : isSvgMode;
|
||||
|
||||
|
||||
// If there's no existing element or it's the wrong type, create a new one:
|
||||
vnodeName = String(vnodeName);
|
||||
if (!dom || !isNamedNode(dom, vnodeName)) {
|
||||
out = createNode(vnodeName, isSvgMode);
|
||||
|
||||
if (dom) {
|
||||
// move children into the replacement node
|
||||
while (dom.firstChild) out.appendChild(dom.firstChild);
|
||||
|
||||
// if the previous Element was mounted into the DOM, replace it inline
|
||||
if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
|
||||
|
||||
// recycle the old element (skips non-Element node types)
|
||||
recollectNodeTree(dom, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let fc = out.firstChild,
|
||||
props = out[ATTR_KEY],
|
||||
vchildren = vnode.children;
|
||||
|
||||
if (props==null) {
|
||||
props = out[ATTR_KEY] = {};
|
||||
for (let a=out.attributes, i=a.length; i--; ) props[a[i].name] = a[i].value;
|
||||
}
|
||||
|
||||
// Optimization: fast-path for elements containing a single TextNode:
|
||||
if (!hydrating && vchildren && vchildren.length===1 && typeof vchildren[0]==='string' && fc!=null && fc.splitText!==undefined && fc.nextSibling==null) {
|
||||
if (fc.nodeValue!=vchildren[0]) {
|
||||
fc.nodeValue = vchildren[0];
|
||||
}
|
||||
}
|
||||
// otherwise, if there are existing or new children, diff them:
|
||||
else if (vchildren && vchildren.length || fc!=null) {
|
||||
innerDiffNode(out, vchildren, context, mountAll, hydrating || props.dangerouslySetInnerHTML!=null);
|
||||
}
|
||||
|
||||
|
||||
// Apply attributes/props from VNode to the DOM Element:
|
||||
diffAttributes(out, vnode.attributes, props);
|
||||
|
||||
|
||||
// restore previous SVG mode: (in case we're exiting an SVG namespace)
|
||||
isSvgMode = prevSvgMode;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply child and attribute changes between a VNode and a DOM Node to the DOM.
|
||||
* @param {import('../dom').PreactElement} dom Element whose children should be compared & mutated
|
||||
* @param {Array<import('../vnode').VNode>} vchildren Array of VNodes to compare to `dom.childNodes`
|
||||
* @param {object} context Implicitly descendant context object (from most
|
||||
* recent `getChildContext()`)
|
||||
* @param {boolean} mountAll Whether or not to immediately mount all components
|
||||
* @param {boolean} isHydrating if `true`, consumes externally created elements
|
||||
* similar to hydration
|
||||
*/
|
||||
function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
|
||||
let originalChildren = dom.childNodes,
|
||||
children = [],
|
||||
keyed = {},
|
||||
keyedLen = 0,
|
||||
min = 0,
|
||||
len = originalChildren.length,
|
||||
childrenLen = 0,
|
||||
vlen = vchildren ? vchildren.length : 0,
|
||||
j, c, f, vchild, child;
|
||||
|
||||
// Build up a map of keyed children and an Array of unkeyed children:
|
||||
if (len!==0) {
|
||||
for (let i=0; i<len; i++) {
|
||||
let child = originalChildren[i],
|
||||
props = child[ATTR_KEY],
|
||||
key = vlen && props ? child._component ? child._component.__key : props.key : null;
|
||||
if (key!=null) {
|
||||
keyedLen++;
|
||||
keyed[key] = child;
|
||||
}
|
||||
else if (props || (child.splitText!==undefined ? (isHydrating ? child.nodeValue.trim() : true) : isHydrating)) {
|
||||
children[childrenLen++] = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vlen!==0) {
|
||||
for (let i=0; i<vlen; i++) {
|
||||
vchild = vchildren[i];
|
||||
child = null;
|
||||
|
||||
// attempt to find a node based on key matching
|
||||
let key = vchild.key;
|
||||
if (key!=null) {
|
||||
if (keyedLen && keyed[key]!==undefined) {
|
||||
child = keyed[key];
|
||||
keyed[key] = undefined;
|
||||
keyedLen--;
|
||||
}
|
||||
}
|
||||
// attempt to pluck a node of the same type from the existing children
|
||||
else if (min<childrenLen) {
|
||||
for (j=min; j<childrenLen; j++) {
|
||||
if (children[j]!==undefined && isSameNodeType(c = children[j], vchild, isHydrating)) {
|
||||
child = c;
|
||||
children[j] = undefined;
|
||||
if (j===childrenLen-1) childrenLen--;
|
||||
if (j===min) min++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// morph the matched/found/created DOM child to match vchild (deep)
|
||||
child = idiff(child, vchild, context, mountAll);
|
||||
|
||||
f = originalChildren[i];
|
||||
if (child && child!==dom && child!==f) {
|
||||
if (f==null) {
|
||||
dom.appendChild(child);
|
||||
}
|
||||
else if (child===f.nextSibling) {
|
||||
removeNode(f);
|
||||
}
|
||||
else {
|
||||
dom.insertBefore(child, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// remove unused keyed children:
|
||||
if (keyedLen) {
|
||||
for (let i in keyed) if (keyed[i]!==undefined) recollectNodeTree(keyed[i], false);
|
||||
}
|
||||
|
||||
// remove orphaned unkeyed children:
|
||||
while (min<=childrenLen) {
|
||||
if ((child = children[childrenLen--])!==undefined) recollectNodeTree(child, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Recursively recycle (or just unmount) a node and its descendants.
|
||||
* @param {import('../dom').PreactElement} node DOM node to start
|
||||
* unmount/removal from
|
||||
* @param {boolean} [unmountOnly=false] If `true`, only triggers unmount
|
||||
* lifecycle, skips removal
|
||||
*/
|
||||
export function recollectNodeTree(node, unmountOnly) {
|
||||
let component = node._component;
|
||||
if (component) {
|
||||
// if node is owned by a Component, unmount that component (ends up recursing back here)
|
||||
unmountComponent(component);
|
||||
}
|
||||
else {
|
||||
// If the node's VNode had a ref function, invoke it with null here.
|
||||
// (this is part of the React spec, and smart for unsetting references)
|
||||
if (node[ATTR_KEY]!=null) applyRef(node[ATTR_KEY].ref, null);
|
||||
|
||||
if (unmountOnly===false || node[ATTR_KEY]==null) {
|
||||
removeNode(node);
|
||||
}
|
||||
|
||||
removeChildren(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Recollect/unmount all children.
|
||||
* - we use .lastChild here because it causes less reflow than .firstChild
|
||||
* - it's also cheaper than accessing the .childNodes Live NodeList
|
||||
*/
|
||||
export function removeChildren(node) {
|
||||
node = node.lastChild;
|
||||
while (node) {
|
||||
let next = node.previousSibling;
|
||||
recollectNodeTree(node, true);
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply differences in attributes from a VNode to the given DOM Element.
|
||||
* @param {import('../dom').PreactElement} dom Element with attributes to diff `attrs` against
|
||||
* @param {object} attrs The desired end-state key-value attribute pairs
|
||||
* @param {object} old Current/previous attributes (from previous VNode or
|
||||
* element's prop cache)
|
||||
*/
|
||||
function diffAttributes(dom, attrs, old) {
|
||||
let name;
|
||||
|
||||
// remove attributes no longer present on the vnode by setting them to undefined
|
||||
for (name in old) {
|
||||
if (!(attrs && attrs[name]!=null) && old[name]!=null) {
|
||||
setAccessor(dom, name, old[name], old[name] = undefined, isSvgMode);
|
||||
}
|
||||
}
|
||||
|
||||
// add new & update changed attributes
|
||||
for (name in attrs) {
|
||||
if (name!=='children' && name!=='innerHTML' && (!(name in old) || attrs[name]!==(name==='value' || name==='checked' ? dom[name] : old[name]))) {
|
||||
setAccessor(dom, name, old[name], old[name] = attrs[name], isSvgMode);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import { extend } from '../util';
|
||||
|
||||
|
||||
/**
|
||||
* Check if two nodes are equivalent.
|
||||
* @param {import('../dom').PreactElement} node DOM Node to compare
|
||||
* @param {import('../vnode').VNode} vnode Virtual DOM node to compare
|
||||
* @param {boolean} [hydrating=false] If true, ignores component constructors
|
||||
* when comparing.
|
||||
* @private
|
||||
*/
|
||||
export function isSameNodeType(node, vnode, hydrating) {
|
||||
if (typeof vnode==='string' || typeof vnode==='number') {
|
||||
return node.splitText!==undefined;
|
||||
}
|
||||
if (typeof vnode.nodeName==='string') {
|
||||
return !node._componentConstructor && isNamedNode(node, vnode.nodeName);
|
||||
}
|
||||
return hydrating || node._componentConstructor===vnode.nodeName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if an Element has a given nodeName, case-insensitively.
|
||||
* @param {import('../dom').PreactElement} node A DOM Element to inspect the name of.
|
||||
* @param {string} nodeName Unnormalized name to compare against.
|
||||
*/
|
||||
export function isNamedNode(node, nodeName) {
|
||||
return node.normalizedNodeName===nodeName || node.nodeName.toLowerCase()===nodeName.toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reconstruct Component-style `props` from a VNode.
|
||||
* Ensures default/fallback values from `defaultProps`:
|
||||
* Own-properties of `defaultProps` not present in `vnode.attributes` are added.
|
||||
* @param {import('../vnode').VNode} vnode The VNode to get props for
|
||||
* @returns {object} The props to use for this VNode
|
||||
*/
|
||||
export function getNodeProps(vnode) {
|
||||
let props = extend({}, vnode.attributes);
|
||||
props.children = vnode.children;
|
||||
|
||||
let defaultProps = vnode.nodeName.defaultProps;
|
||||
if (defaultProps!==undefined) {
|
||||
for (let i in defaultProps) {
|
||||
if (props[i]===undefined) {
|
||||
props[i] = defaultProps[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Virtual DOM Node
|
||||
* @typedef VNode
|
||||
* @property {string | function} nodeName The string of the DOM node to create or Component constructor to render
|
||||
* @property {Array<VNode | string>} children The children of node
|
||||
* @property {string | number | undefined} key The key used to identify this VNode in a list
|
||||
* @property {object} attributes The properties of this VNode
|
||||
*/
|
||||
export const VNode = function VNode() {};
|
Loading…
Reference in New Issue