| (()=>{let a=(a,b={},...c)=>({$:a,a:!b||b.$||b.concat?{c:[].concat(b||[],...c)}:(b.c=[].concat(...c),b)}),b=(a=[],b,c)=>a.map(a=>a(b,c)),c=a=>new Proxy(a,{get:(a,b,d)=>c((...c)=>((d=a(...c)).a.className=(d.a.className||" ")+" "+b,d))}),d=window.R=(a,c,e=c.childNodes,f=0)=>{for([].concat(a).map((g,h,i,j=e[f++],k=g.s=(j?j.a==g.$&&(g.s||j.s):g.s)||{},l={a:g.$,s:k,m:[],u:[],d:[]})=>{for(;(g.$||a).bind;)g=g.$(g.a,k,b=>Object.assign(k,b)&&d(a,c),l);h=g.replace?document.createTextNode(g):document.createElement(g.$),h=j?j.$!=g.$&&j.data!=g?(c.replaceChild(h,j),h):j:c.appendChild(h),i=j?j.a==l.a?l.d:(b(j.u),d([],j),l.m):l.m,Object.assign(h,g,l),g.replace?h.data=g:Object.keys(g.a).map((a)=>"style"==a?Object.assign(h[a],g.a[a]):h[a]!==g.a[a]&&(h[a]=g.a[a]))&&l.r||d(g.a.c,h),b(i,h,j)});e[f];)b(e[f].u),d([],c.removeChild(e[f]))};window.H=new Proxy(a,{get:(b,d)=>b[d]||c(a.bind(b,d))})})() |
Sorry, the diff of this file is not supported yet
| window.K=(a,b,c="=e",d=a[c]=a[c]||{})=>Object.keys(d).map(a=>b.find(({a:{key:b}})=>b==a)||delete d[a])&&b.map(a=>(a.s=d[a.a.key]=d[a.a.key]||a.s||{})&&a) |
| /** | ||
| * .dom - A Tiny VDom Template Engine | ||
| * | ||
| * Copyright 2017-2019 Ioannis Charalampidis (wavesoft) | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| /* BEGIN NPM-GLUE */ | ||
| // This code block will be striped when building the stand-alone version. | ||
| // When using `npm` this exports the correct functions in order to be easily | ||
| // imported in the correct scope, without leaking to the global scope. | ||
| var window = typeof window !== "undefined" && window || {}; | ||
| module.exports = window; | ||
| /* END NPM-GLUE */ | ||
| /** | ||
| * Enables keyed updates to the given array of components | ||
| */ | ||
| window.K = ( | ||
| state, // 1. A reference to the components' state object | ||
| components, // 2. An array of components with `key` properties | ||
| namespace="=e", // 3. (Optional) The namespace where to keep track | ||
| // the component instances. | ||
| store=state[namespace] = state[namespace] || {} // a. A reference to the state store, used later | ||
| ) => | ||
| Object.keys(store).map(k => // First ensure that previous keys are deleted, by | ||
| components.find(({a:{key}}) => key == k) || // looping over the stored keys and deleting the | ||
| delete store[k] // ones not found in the given array. | ||
| ) | ||
| && components.map(c => ( // Then process the component states | ||
| c.s = // Always define an `.s` property, | ||
| store[c.a.key] = // that is also kept as reference in the state object | ||
| store[c.a.key] // - That contains either the previous state | ||
| || c.s // - Or the current state of the VDom object | ||
| || {} // - Or define a new, blank state (important) | ||
| ) && c // And finally return the component to the map function | ||
| ) |
+21
-11
| { | ||
| "name": "dot-dom", | ||
| "version": "0.3.0", | ||
| "version": "0.3.1", | ||
| "description": "A tiny (less than 512 byte) template engine that uses virtual DOM and some of react principles", | ||
@@ -8,3 +8,7 @@ "main": "src/dotdom.js", | ||
| "src/dotdom.js", | ||
| "dotdom.min.js" | ||
| "src/plugins/keyed.js", | ||
| "dist/dotdom.min.js", | ||
| "dist/dotdom.min.js.gz", | ||
| "dist/plugin-keyed.min.js", | ||
| "dist/plugin-keyed.min.js.gz" | ||
| ], | ||
@@ -32,8 +36,11 @@ "scripts": { | ||
| "devDependencies": { | ||
| "babel-preset-babili": "0.0.11", | ||
| "gulp": "3.9.1", | ||
| "gulp-babel": "^6.1.2", | ||
| "gulp-cli": "1.2.2", | ||
| "gulp-clone": "^1.0.0", | ||
| "gulp-gzip": "1.4.0", | ||
| "@babel/core": "^7.0.0", | ||
| "babel-preset-minify": "^0.5.0", | ||
| "gulp": "^4.0.2", | ||
| "gulp-babel": "^8.0.0", | ||
| "gulp-brotli": "^1.2.1", | ||
| "gulp-cat": "^0.3.3", | ||
| "gulp-cli": "^2.0.1", | ||
| "gulp-clone": "^2.0.1", | ||
| "gulp-gzip": "^1.4.0", | ||
| "gulp-merge": "^0.1.1", | ||
@@ -44,8 +51,11 @@ "gulp-rename": "^1.2.2", | ||
| "gulp-util": "^3.0.8", | ||
| "jest": "^23.6.0", | ||
| "handlebars": ">=4.1.2", | ||
| "jest": "^24.1.0", | ||
| "js-yaml": ">=3.13.1", | ||
| "minimatch": ">=3.0.2", | ||
| "mock-browser": "^0.92.12", | ||
| "through2": "^2.0.3", | ||
| "uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony", | ||
| "npm-check-updates": "^2.15.0", | ||
| "through2": "^3.0.0", | ||
| "watch": "^1.0.1" | ||
| } | ||
| } |
+146
-2
@@ -1,2 +0,2 @@ | ||
| # .dom [](https://travis-ci.org/wavesoft/dot-dom) [](https://codepen.io/anon/pen/OrzaXB?editors=0010) [](https://gitter.im/wavesoft/dot-dom?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) | ||
| # .dom [](https://www.npmjs.com/package/dot-dom) [](https://cdn.jsdelivr.net/npm/dot-dom@0.3.1/dotdom.min.js) [](https://packagephobia.now.sh/result?p=dot-dom) [](https://travis-ci.org/wavesoft/dot-dom) [](https://codepen.io/anon/pen/OrzaXB?editors=0010) [](https://gitter.im/wavesoft/dot-dom?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) | ||
@@ -17,3 +17,3 @@ > A tiny (512 byte) virtual DOM template engine for embedded projects | ||
| * _Built for the future_ : The library is heavily exploiting the ES6 specifications, meaning that it's **not** supported by older browsers. Currently it's supported by the 70% of the browsers in the market, but expect this to be 90% within the next year. | ||
| * _Built for the future_ : The library is heavily exploiting the ES6 specifications, meaning that it's **not** supported by older browsers. Currently it's supported by the 90% of the browsers in the market, but expect this to be close to 100% within the next year. | ||
@@ -26,3 +26,9 @@ * _Declarative_ : Describe your HTML DOM in a structured, natural manner, helping you create powerful yet readable user interfaces. | ||
| ### Projects Using `.dom` | ||
| * [Open Graph Image as a Service](https://github.com/styfle/og-image) - [demo](https://og-image.now.sh/) | ||
| Are you using `.dom` in your project? Fork this repository and add yours on the list! | ||
| ## Installation | ||
@@ -245,2 +251,113 @@ | ||
| #### 5. Keyed Updates | ||
| Keyed updates is a useful [reconciliation](https://reactjs.org/docs/reconciliation.html) feature from React that enables the rendering engine to take smart decisions on which elements to update. | ||
| A particularly useful case is when you are rendering a dynamic list of elements. Since the rendering engine does not understand _which_ element has changed, it ends-up with wrong updates. | ||
| To solve this issue, the VDOM engines use a `key` property that uniquely identifies an element in the tree. However **.dom** solves it, by keeping a copy of the element state in the VDom element instance itself. | ||
| This means that you don't need any `key` property, just make sure you return the same VDom instance as before. | ||
| If you are creating dynamic elements (eg. an array of vdom elements), **.dom** might have trouble detecting the correct update order. | ||
| <table width="100%"> | ||
| <tr> | ||
| <th>React</th> | ||
| <th>.dom</th> | ||
| </tr> | ||
| <tr> | ||
| <td valign="top"> | ||
| <pre lang="javascript"> | ||
| class Clickable extends React.Component { | ||
| constructor() { | ||
| super(...arguments); | ||
| this.state = { | ||
| clicks: 0 | ||
| }; | ||
| } | ||
| <br /> | ||
| render() { | ||
| const {clicks} = this.state; | ||
| const {ket} = this.props; | ||
| <br /> | ||
| return React.createElement( | ||
| 'button', { | ||
| onClick() { | ||
| this.setState({clicks: clicks+1}) | ||
| } | ||
| }, `clicks=${clicks}, key=${key}` | ||
| ); | ||
| } | ||
| } | ||
| <br /> | ||
| const list = ["first", "second", "third"]; | ||
| const components = list.map(key => | ||
| React.createElement(Clickable, {key}, null); | ||
| <br /> | ||
| ReactDOM.render( | ||
| React.createElement('div', null, | ||
| components | ||
| ), | ||
| document.body | ||
| ); | ||
| </pre> | ||
| </td> | ||
| <td valign="top"> | ||
| <pre lang="javascript"> | ||
| function Clickable(props, state, setState) { | ||
| const {clicks=0} = state; | ||
| const {key} = props; | ||
| <br /> | ||
| return H('button', | ||
| { | ||
| onclick() { | ||
| setState({clicks: clicks+1}) | ||
| } | ||
| }, | ||
| `clicks=${clicks}, key=${key}` | ||
| ); | ||
| } | ||
| <br /> | ||
| const list = ["first", "second", "third"]; | ||
| const components = list.map(key => | ||
| H(Clickable, {key}); | ||
| <br /> | ||
| R( | ||
| H('div', components), | ||
| document.body | ||
| ) | ||
| </pre> | ||
| </td> | ||
| </tr> | ||
| </table> | ||
| Note that the solution above will correctly update the stateful components, even if their order has changed. However, if you want the complete, React-Like functionality that updates individual keys, you can use the `Keyed` plug-in. | ||
| ```js | ||
| function Container(props, state) { | ||
| const {components} = props; | ||
| // The function `K` accepts the component state and an array of components that | ||
| // contain the `key` property, and returns the same array of components, with their | ||
| // state correctly manipulated. | ||
| return H("div", K(state, components)); | ||
| } | ||
| ``` | ||
| #### 6. Raw (Unreconciled) Nodes | ||
| You can create raw (unreconciled) VDom nodes (eg. that carry an arbitrary HTML content) by setting the `.r` property of the hooks object to any truthy value. | ||
| This will disable further reconciliation to the child nodes, and therefore keep your contents intact. | ||
| ```js | ||
| function Description(props, state, setState, hooks) { | ||
| const { html } = props; | ||
| hooks.r = 1; // Enable raw mode | ||
| return H('div', { | ||
| innerHTML: html | ||
| }) | ||
| } | ||
| ``` | ||
| ## API Reference | ||
@@ -381,2 +498,29 @@ | ||
| ## Plugin Reference | ||
| ### Keyed Update List `K(state, components)` | ||
| > In `plugin-keyed.min.js` | ||
| Ensures the state of the components in the list is synchronized, according to their `key` property. This enables you to do react-like keyed updates like so: | ||
| ```js | ||
| function ValueRenderer(...) { | ||
| ... | ||
| } | ||
| function MyComponent(props, state) { | ||
| const { values } = props; | ||
| const components = values.map(value => { | ||
| H(ValueRenderer, { | ||
| key: value, | ||
| value: value | ||
| }); | ||
| }) | ||
| // Synchronize state of components, based on their key | ||
| return H('div', K(state, components)) | ||
| } | ||
| ``` | ||
| ## Contribution | ||
@@ -383,0 +527,0 @@ |
+50
-47
| /** | ||
| * .dom - A Tiny VDom Template Engine | ||
| * | ||
| * Copyright 2017 Ioannis Charalampidis (wavesoft) | ||
| * Copyright 2017-2019 Ioannis Charalampidis (wavesoft) | ||
| * | ||
@@ -47,10 +47,10 @@ * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| a: (props.$ || props.concat || props.removeChild) // If the props argument is a renderable VNode, | ||
| // a string, an array (.concat exists on both | ||
| a: (!props || props.$ || props.concat) // If the props argument is false/null, a renderable | ||
| // VNode, a string, an array (.concat exists on both | ||
| // strings and arrays), or a DOM element, then ... | ||
| ? {c: [].concat(props, ...children)} // ... prepend it to the children | ||
| : ((props.c = [].concat(...children)), props) // ... otherwise append 'C' to the property | ||
| // the .concat ensures that arrays of children | ||
| // will be flattened into a single array. | ||
| ? {c: [].concat(props || [], ...children)} // ... create props just with children | ||
| : ((props.c = [].concat(...children)), props) // ... otherwise append 'c' to the property set | ||
| // (the .concat ensures that arrays of children | ||
| // will be flattened into a single array). | ||
| } | ||
@@ -67,3 +67,5 @@ ) | ||
| , callLifecycleMethods = (methods = [], arg1, arg2) => | ||
| methods.map(e => e(arg1, arg2)) // Detaching from stack is important, otherwise it | ||
| methods.map(e => e(arg1, arg2)) // Fan-out to the lifecycle methods, passing the | ||
| // maximum of 2 arguments (spread operator takes | ||
| // more space when compressed) | ||
@@ -91,3 +93,3 @@ /** | ||
| .a.className = (_instance.a.className || '') // And then we assign the class name, | ||
| .a.className = (_instance.a.className || ' ') // And then we assign the class name, | ||
| + ' ' + className, // concatenating to the previous value | ||
@@ -105,3 +107,3 @@ | ||
| * @param {VNode|Array<VNode>} vnodes - The node on an array of nodes to render | ||
| * @param {HTLDomElement} | ||
| * @param {HTMLDomElement} | ||
| */ | ||
@@ -129,11 +131,12 @@ , render = window.R = ( | ||
| _child=_children[_c++], // a. Get the next DOM child + increment counter | ||
| _state=( // b. Get the current state from the DOM child | ||
| _child && // - If there is no child, bail | ||
| (_child.a == vnode.$) // - If the element has changed, bail | ||
| && _child.s // - If the element has a state, use that | ||
| ) || {}, // - Default state value | ||
| _state=vnode.s=( // b. Get the current state from the DOM child and keep | ||
| // a copy in the vnode object. | ||
| _child // Separate comparison logic if there is a child or not | ||
| ? ((_child.a == vnode.$) // - If the element has changed, bail | ||
| && (vnode.s // - If there is a state in the VNode, prefer it | ||
| || _child.s)) // - If there is a state in the DOM node, fall back to it | ||
| : vnode.s // - If there is no element, use VNode state, if present | ||
| ) || {}, // - Default state value | ||
| _hooks={ // c. Prepare the hooks object that will be passed | ||
| // down to the functional component | ||
| s: _state, // - The 's' property is keeping a reference to | ||
| // the current element state. (Used above) | ||
| a: vnode.$, // - The 'a' property is keeping a reference | ||
@@ -144,2 +147,4 @@ // to the element (property '$') and is used | ||
| // Update Element phase later. | ||
| s: _state, // - The 's' property is keeping a reference to | ||
| // the current element state. (Used above) | ||
| m: [], // - The 'm' property contains the `mount` cb | ||
@@ -180,7 +185,5 @@ u: [], // - The 'u' property contains the `unmount` cb | ||
| _new_dom = | ||
| vnode.removeChild // If this is a DOM element, pass it through ... | ||
| ? vnode // Otherwise we prepare the new DOM element in advance | ||
| : vnode.replace // in order to save a few comparison bytes later. | ||
| ? document.createTextNode(vnode) | ||
| : document.createElement(vnode.$); | ||
| vnode.replace // in order to save a few comparison bytes later. | ||
| ? document.createTextNode(vnode) | ||
| : document.createElement(vnode.$); | ||
@@ -190,6 +193,4 @@ /* Keep or replace the previous DOM element */ | ||
| _new_dom = | ||
| _child // If we have a previous child we do some reconciliation | ||
| ? (vnode.removeChild // If it's a DOM element reference, check | ||
| ? _child != vnode | ||
| : (_child.$ != vnode.$ && _child.data != vnode)) | ||
| _child // If we have a previous child, do some reconciliation | ||
| ? (_child.$ != vnode.$ && _child.data != vnode) // Check if the node tag has changed. | ||
| ? ( | ||
@@ -244,27 +245,29 @@ dom.replaceChild( // - If not, we replace the old element with the | ||
| /* Apply properties to the DOM element */ | ||
| vnode.removeChild || // If this is a DOM element, don't do anything | ||
| vnode.replace | ||
| ? _new_dom.data = vnode // - String nodes update only the text | ||
| : Object.keys(vnode.a).map( // - Element nodes have properties | ||
| ( | ||
| key // 1. The property name | ||
| ) => | ||
| vnode.replace | ||
| ? _new_dom.data = vnode // - String nodes update only the text | ||
| : Object.keys(vnode.a).map( // - Element nodes have properties | ||
| ( | ||
| key // 1. The property name | ||
| ) => | ||
| key == 'style' ? // The 'style' property is an object and must be | ||
| // applied recursively. | ||
| Object.assign( | ||
| _new_dom[key], // '[key]' is shorter than '.style' | ||
| vnode.a[key] | ||
| ) | ||
| key == 'style' ? // The 'style' property is an object and must be | ||
| // applied recursively. | ||
| Object.assign( | ||
| _new_dom[key], // '[key]' is shorter than '.style' | ||
| vnode.a[key] | ||
| ) | ||
| : (_new_dom[key] !== vnode.a[key] && // All properties are applied directly to DOM, as | ||
| (_new_dom[key] = vnode.a[key])) // long as they are different than ther value in the | ||
| // instance. This includes `onXXX` event handlers. | ||
| : (_new_dom[key] !== vnode.a[key] && // All properties are applied directly to DOM, as | ||
| (_new_dom[key] = vnode.a[key])) // long as they are different than ther value in the | ||
| // instance. This includes `onXXX` event handlers. | ||
| ) && | ||
| render( // Only if we have an element (and not text node) | ||
| vnode.a.c, // we recursively continue rendering into it's | ||
| _new_dom // child nodes. | ||
| ) | ||
| ) && _hooks.r || // If the user has marked this element as 'raw', do not | ||
| // continue to it's children. Failing to do so, will damage | ||
| // the element contents | ||
| render( // Only if we have an element (and not text node) | ||
| vnode.a.c, // we recursively continue rendering into it's | ||
| _new_dom // child nodes. | ||
| ) | ||
| /* Call life-cycle methods */ | ||
@@ -271,0 +274,0 @@ |
| (()=>{let e=(e,a={},...c)=>({$:e,a:a.$||a.concat||a.removeChild?{c:[].concat(a,...c)}:(a.c=[].concat(...c),a)}),a=(e=[],a,c)=>e.map(e=>e(a,c)),c=e=>new Proxy(e,{get:(e,a,d)=>c((...c)=>((d=e(...c)).a.className=(d.a.className||"")+" "+a,d))}),d=window.R=((e,c,t=c.childNodes,o=0)=>{for([].concat(e).map((n,l,m,i=t[o++],s=i&&i.a==n.$&&i.s||{},r={s,a:n.$,m:[],u:[],d:[]})=>{for(;(n.$||e).bind;)n=n.$(n.a,s,a=>Object.assign(s,a)&&d(e,c),r);l=n.removeChild?n:n.replace?document.createTextNode(n):document.createElement(n.$),l=i?(n.removeChild?i!=n:i.$!=n.$&&i.data!=n)?(c.replaceChild(l,i),l):i:c.appendChild(l),m=i?i.a==r.a?r.d:(a(i.u),d([],i),r.m):r.m,Object.assign(l,n,r),n.removeChild||n.replace?l.data=n:Object.keys(n.a).map(e=>"style"==e?Object.assign(l[e],n.a[e]):l[e]!==n.a[e]&&(l[e]=n.a[e]))&&d(n.a.c,l),a(m,l,i)});t[o];)a(t[o].u),d([],c.removeChild(t[o]))});window.H=new Proxy(e,{get:(a,d)=>a[d]||c(e.bind(a,d))})})() |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 2 instances in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
52986
19.15%8
60%307
18.53%560
34.62%1
-66.67%0
-100%22
37.5%3
50%