Comparing version 2.0.3 to 2.0.5
@@ -1,260 +0,2 @@ | ||
'use strict'; | ||
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); | ||
var Literal = { | ||
component: component, | ||
render: render | ||
}; | ||
// Render takes components, string id of mounting point, and default state. | ||
var render = function render(components, documentNodeID) { | ||
var state = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
// Initialize Global Store For State | ||
defineStore(state); | ||
window.addEventListener('DOMContentLoaded', function () { | ||
// Mounting Point For Application | ||
var rootNode = document.getElementById(documentNodeID); | ||
// TODO: Should parse HTML string into AST eventually? | ||
// This will allow regular template markup to be | ||
// used instead of only AST in components. | ||
// Listens For State Update Event | ||
stateListener(components, rootNode); | ||
// Generate DOM Elements | ||
var tree = createElements(components({ state: function state() { | ||
return window.__LITERAL_STORE__; | ||
} })); | ||
// Append DOM Elements | ||
rootNode.appendChild(tree); | ||
}); | ||
}; | ||
var defineStore = function defineStore(state) { | ||
return window.__LITERAL_STORE__ = state; | ||
}; | ||
var stateListener = function stateListener(components, rootNode) { | ||
// Listens For State Event And Diffs The AST And Modifies DOM | ||
// Diff could be improved by batching state updates and then | ||
// triggering diff. | ||
// We can also improve this by moving the diff to a web worker | ||
// to remove burden off of main thread when large state updates occur. | ||
window.addEventListener('__LITERAL_UPDATE__', function (event) { | ||
return Promise.all([components({ state: function state() { | ||
return event.detail.oldState; | ||
} }), components({ state: function state() { | ||
return event.detail.newState; | ||
} })]).then(function (_ref) { | ||
var _ref2 = _slicedToArray(_ref, 2), | ||
oldTree = _ref2[0], | ||
newTree = _ref2[1]; | ||
return diffTrees(rootNode, newTree, oldTree); | ||
}).catch(function () {}); | ||
}); | ||
}; | ||
var observeLifecycle = function observeLifecycle(node) { | ||
// Responsible For Listening For And Triggering Mounted and Unmounted Events | ||
var observer = new MutationObserver(function (mutations) { | ||
mutations.forEach(function (mutation) { | ||
var type = mutation.type; | ||
if (type === 'childList') { | ||
var addedNodes = mutation.addedNodes, | ||
removedNodes = mutation.removedNodes; | ||
Promise.all([triggerLifeCycleEvents('mounted', addedNodes), triggerLifeCycleEvents('unmounted', removedNodes)]); | ||
} | ||
}); | ||
}); | ||
var triggerLifeCycleEvents = function triggerLifeCycleEvents(lifeCycleName, nodes) { | ||
if (nodes.length > 0) nodes.forEach(function (node) { | ||
var event = new CustomEvent(lifeCycleName); | ||
node.dispatchEvent(event); | ||
}); | ||
}; | ||
observer.observe(node, { childList: true }); | ||
}; | ||
var createElements = function createElements(node) { | ||
// Creates All DOM Elements Recursively | ||
var mainKeys = ['children', 'element', 'text', 'events']; | ||
var newNode = document.createElement(node.element); | ||
// Observe LifeCycle Can possibly be improved by determining | ||
// if mount or unmounted event listeners | ||
// exist instead of applying to all nodes | ||
observeLifecycle(newNode); | ||
Object.entries(node).forEach(function (_ref3) { | ||
var _ref4 = _slicedToArray(_ref3, 2), | ||
key = _ref4[0], | ||
value = _ref4[1]; | ||
if (key === 'text') createTextNode(value, newNode); | ||
if (key === 'events') attachEvents(value, newNode); | ||
if (key === 'children') value.map(createElements).forEach(newNode.appendChild.bind(newNode)); | ||
if (!mainKeys.includes(key)) newNode.setAttribute(key, value); | ||
}); | ||
return newNode; | ||
}; | ||
var createTextNode = function createTextNode(value, node) { | ||
var textNode = document.createTextNode(value); | ||
node.appendChild(textNode); | ||
}; | ||
var attachEvents = function attachEvents(value, node) { | ||
// Attach Array Or Object Events To Created Node | ||
if (value instanceof Array) { | ||
value.forEach(function (event) { | ||
return node.addEventListener(event.type, event.action); | ||
}); | ||
} else if (value instanceof Object) { | ||
node.addEventListener(value.type, value.action); | ||
} | ||
}; | ||
var component = function component() { | ||
var _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
_ref5$methods = _ref5.methods, | ||
methods = _ref5$methods === undefined ? function () {} : _ref5$methods, | ||
_ref5$render = _ref5.render, | ||
render = _ref5$render === undefined ? function () {} : _ref5$render; | ||
// This Function is a Thunk. Returns Markup with Updated State. | ||
return function () { | ||
var _ref6 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
_ref6$props = _ref6.props, | ||
props = _ref6$props === undefined ? {} : _ref6$props, | ||
_ref6$state = _ref6.state, | ||
state = _ref6$state === undefined ? function () { | ||
return window.__LITERAL_STORE__; | ||
} : _ref6$state; | ||
// getState is used directly in the view and pulls the state data and returns new object. | ||
var getState = function getState() { | ||
var currentState = state(); | ||
return Object.assign({}, currentState); | ||
}; | ||
// setState pulls current state, modifies state, and returns and assigns new state object. | ||
// setState calls can be improved by placing all changes into a stack/que like React does so | ||
// updates can be batched. | ||
// setState also dispatches the event to trigger diff. It passes the old and new state for diff. | ||
var setState = function setState(object) { | ||
var currentState = getState(); | ||
window.__LITERAL_STORE__ = Object.assign({}, currentState, object); | ||
var storeChangeEvent = new CustomEvent('__LITERAL_UPDATE__', { | ||
detail: { | ||
newState: window.__LITERAL_STORE__, | ||
oldState: currentState | ||
} | ||
}); | ||
return window.dispatchEvent(storeChangeEvent); | ||
}; | ||
// Render passes getState, setState, props, and methods which can be created in the component | ||
// And also have access to getState and setState. | ||
return render({ | ||
getState: getState, | ||
setState: setState, | ||
props: props, | ||
methods: methods({ getState: getState, setState: setState }) | ||
}); | ||
}; | ||
}; | ||
var diffTrees = function diffTrees(parent, newData, oldData) { | ||
var index = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; | ||
// This is responsible for diffing both AST and modifying DOM recursively. | ||
// This could use a lot of work....lol | ||
var targetNode = parent.childNodes[index]; | ||
if (!oldData && newData) { | ||
if (oldData === false) { | ||
parent.insertBefore(createElements(newData), targetNode); | ||
} else if (oldData === undefined) { | ||
parent.appendChild(createElements(newData)); | ||
} | ||
} else if (!newData && oldData) { | ||
parent.removeChild(targetNode); | ||
} else if (newData) { | ||
Promise.all([removeUndefinedKeys(targetNode, newData, oldData), updateNodes(parent, targetNode, newData, oldData)]); | ||
if (newData.children || oldData.children) { | ||
Promise.all([filterBooleanChildren(newData.children, oldData.children), filterBooleanChildren(oldData.children, newData.children)]).then(function (_ref7) { | ||
var _ref8 = _slicedToArray(_ref7, 2), | ||
newChildren = _ref8[0], | ||
oldChildren = _ref8[1]; | ||
for (var i = 0; i < childrenLength(newChildren, oldChildren); i++) { | ||
diffTrees(targetNode, newChildren[i], oldChildren[i], i); | ||
} | ||
}); | ||
} | ||
} | ||
}; | ||
var filterBooleanChildren = function filterBooleanChildren(firstChildrenBatch, secondChildrenBatch) { | ||
return firstChildrenBatch.filter(function (n, i) { | ||
return n !== false || secondChildrenBatch[i] !== false; | ||
}); | ||
}; | ||
var childrenLength = function childrenLength(newChildren, oldChildren) { | ||
var newLength = newChildren.length; | ||
var oldLength = oldChildren.length; | ||
return newLength >= oldLength ? newLength : oldLength; | ||
}; | ||
var updateNodes = function updateNodes(parent, target, newNode, oldNode) { | ||
Object.entries(newNode).forEach(function (_ref9) { | ||
var _ref10 = _slicedToArray(_ref9, 2), | ||
key = _ref10[0], | ||
newVal = _ref10[1]; | ||
var oldVal = oldNode[key]; | ||
if (key === 'element' || key === 'text' && newVal !== oldVal) { | ||
parent.replaceChild(createElements(newNode), target); | ||
} else if (!['children', 'events', 'element', 'text'].includes(key) && (!oldVal || newVal !== oldVal)) { | ||
updateAttr(target, key, newVal); | ||
} | ||
// else if (key === 'events') { | ||
// Some events don't return the correct state like some console.logged state? | ||
// Do I even need this in the diff ? | ||
// } | ||
}); | ||
}; | ||
var updateAttr = function updateAttr(target, name, value) { | ||
target.setAttribute(name, value); | ||
}; | ||
var removeUndefinedKeys = function removeUndefinedKeys(target, newData, oldData) { | ||
Object.entries(oldData).forEach(function (_ref11) { | ||
var _ref12 = _slicedToArray(_ref11, 2), | ||
key = _ref12[0], | ||
value = _ref12[1]; | ||
if (!newData[key]) removeAttr(target, key, value); | ||
}); | ||
}; | ||
var removeAttr = function removeAttr(target, name, value) { | ||
// Think about boolean types? | ||
// if (typeof value === 'boolean') { | ||
// removeBooleanProp(target, name); | ||
target.removeAttribute(name); | ||
}; | ||
module.exports.render = render; | ||
module.exports.component = component; | ||
module.exports = Literal; | ||
var n={component:d,render:t},t=function(n,t,o){void 0===o&&(o={}),e(o),window.addEventListener("DOMContentLoaded",function(){var e=document.getElementById(t);i(n,e);var o=r(n({state:function(){return window.__LITERAL_STORE__}}));e.appendChild(o)})},e=function(n){return window.__LITERAL_STORE__=n},i=function(n,t){window.addEventListener("__LITERAL_UPDATE__",function(e){return Promise.all([n({state:function(){return e.detail.oldState}}),n({state:function(){return e.detail.newState}})]).then(function(n){return a(t,n[1],n[0])}).catch(function(){})})},r=function(n){var t=["children","element","text","events"],e=document.createElement(n.element);return function(n){var t=new MutationObserver(function(n){n.forEach(function(n){if("childList"===n.type){var t=n.removedNodes;Promise.all([e("mounted",n.addedNodes),e("unmounted",t)])}})}),e=function(n,t){t.length>0&&t.forEach(function(t){var e=new CustomEvent(n);t.dispatchEvent(e)})};t.observe(n,{childList:!0})}(e),Object.entries(n).forEach(function(n){var i=n[0],d=n[1];"text"===i&&o(d,e),"events"===i&&c(d,e),"children"===i&&d.map(r).forEach(e.appendChild.bind(e)),t.includes(i)||e.setAttribute(i,d)}),e},o=function(n,t){var e=document.createTextNode(n);t.appendChild(e)},c=function(n,t){n instanceof Array?n.forEach(function(n){return t.addEventListener(n.type,n.action)}):n instanceof Object&&t.addEventListener(n.type,n.action)},d=function(n){void 0===n&&(n={});var t=n.methods;void 0===t&&(t=function(){});var e=n.render;return void 0===e&&(e=function(){}),function(n){void 0===n&&(n={});var i=n.props;void 0===i&&(i={});var r=n.state;void 0===r&&(r=function(){return window.__LITERAL_STORE__});var o=function(){var n=r();return Object.assign({},n)},c=function(n){var t=o();window.__LITERAL_STORE__=Object.assign({},t,n);var e=new CustomEvent("__LITERAL_UPDATE__",{detail:{newState:window.__LITERAL_STORE__,oldState:t}});return window.dispatchEvent(e)};return e({getState:o,setState:c,props:i,methods:t({getState:o,setState:c})})}},a=function(n,t,e,i){void 0===i&&(i=0);var o=n.childNodes[i];!e&&t?!1===e?n.insertBefore(r(t),o):void 0===e&&n.appendChild(r(t)):!t&&e?n.removeChild(o):t&&(Promise.all([l(o,t,e),s(n,o,t,e)]),(t.children||e.children)&&Promise.all([u(t.children,e.children),u(e.children,t.children)]).then(function(n){for(var t=n[0],e=n[1],i=0;i<f(t,e);i++)a(o,t[i],e[i],i)}))},u=function(n,t){return n.filter(function(n,e){return!1!==n||!1!==t[e]})},f=function(n,t){var e=n.length,i=t.length;return e>=i?e:i},s=function(n,t,e,i){Object.entries(e).forEach(function(o){var c=o[0],d=o[1],a=i[c];"element"===c||"text"===c&&d!==a?n.replaceChild(r(e),t):["children","events","element","text"].includes(c)||a&&d===a||v(t,c,d)})},v=function(n,t,e){n.setAttribute(t,e)},l=function(n,t,e){Object.entries(e).forEach(function(e){var i=e[0];t[i]||h(n,i,e[1])})},h=function(n,t,e){n.removeAttribute(t)};module.exports=n; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "literaljs", | ||
"version": "2.0.3", | ||
"version": "2.0.5", | ||
"description": "~1kb gzip JavaScript library for building user interfaces.", | ||
"main": "build/index.js", | ||
"scripts": { | ||
"build": "babel index.js --out-file build/index.js", | ||
"build": "microbundle", | ||
"prepare": "npm run build", | ||
@@ -16,4 +16,5 @@ "test": "echo \"Error: no test specified\" && exit 1" | ||
"babel-cli": "^6.26.0", | ||
"babel-preset-env": "^1.6.1" | ||
"babel-preset-env": "^1.6.1", | ||
"microbundle": "^0.4.3" | ||
} | ||
} |
@@ -14,3 +14,2 @@ # LiteralJS | ||
- **Babel Not Required**, just pick a mounting point and build stuff. | ||
- **Small**: Only around 1kb in size. | ||
@@ -26,49 +25,3 @@ - **Virtual DOM**: Diffing occurs on state update for more efficient DOM updates. Current and previous AST are diffed against each other. | ||
## Getting Started | ||
- [Example With CDN](#cdn-example) | ||
- [Example With NPM](#npm-example) | ||
### CDN Example | ||
One of the easiest way to use LiteralJS is to use a CDN like so: | ||
```html | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>Literal JS</title> | ||
<script src="http://unpkg.com/literaljs"></script> | ||
</head> | ||
<body> | ||
<div id="app"></div> | ||
<script> | ||
var foo = Literal.component({ | ||
render: function(c) { | ||
return { | ||
element: 'div', | ||
class: 'container', | ||
text: `This is the Count: ${c.getState().count}`, | ||
children: [ | ||
{ | ||
element: 'button', | ||
text: 'Click Me!', | ||
events: { | ||
type: 'click', | ||
action: function() { | ||
var state = c.getState(); | ||
c.setState({ count: state.count + 1 }); | ||
} | ||
} | ||
} | ||
] | ||
}; | ||
} | ||
}); | ||
Literal.render(foo, 'app', { count: 0 }); | ||
</script> | ||
</body> | ||
</html> | ||
``` | ||
### NPM Example | ||
#### Install | ||
@@ -75,0 +28,0 @@ ``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance 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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
154536
10
3
39
336
1