@unsetsoft/ryunixjs 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Ryunix.js ADDED
@@ -0,0 +1,187 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Ryunix = {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ /**
8
+ * This function updates the properties and event listeners of a DOM element based on the previous and
9
+ * next props passed as arguments.
10
+ * @param dom - The DOM element that needs to be updated with new properties.
11
+ * @param prevProps - An object representing the previous properties of a DOM element.
12
+ * @param nextProps - An object containing the new props that need to be updated on the DOM element.
13
+ */
14
+ function updateDomProperties(dom, prevProps, nextProps) {
15
+ const isEvent = name => name.startsWith("on");
16
+ const isAttribute = name => !isEvent(name) && name != "children";
17
+ Object.keys(prevProps).filter(isEvent).forEach(name => {
18
+ const eventType = name.toLowerCase().substring(2);
19
+ dom.removeEventListener(eventType, prevProps[name]);
20
+ });
21
+ Object.keys(prevProps).filter(isAttribute).forEach(name => {
22
+ dom[name] = null;
23
+ });
24
+ Object.keys(nextProps).filter(isAttribute).forEach(name => {
25
+ dom[name] = nextProps[name];
26
+ });
27
+ Object.keys(nextProps).filter(isEvent).forEach(name => {
28
+ const eventType = name.toLowerCase().substring(2);
29
+ dom.addEventListener(eventType, nextProps[name]);
30
+ });
31
+ }
32
+
33
+ const TEXT_ELEMENT = "TEXT";
34
+
35
+ /**
36
+ * This function creates a new element with the given type, configuration object, and children.
37
+ * @param type - The type of the element being created (e.g. "div", "span", "h1", etc.).
38
+ * @param configObject - The `configObject` parameter is an object that contains the properties and
39
+ * values for the element's attributes. These attributes can include things like `className`, `id`,
40
+ * `style`, and any other custom attributes that the user wants to add to the element.
41
+ * @param args - The `args` parameter is a rest parameter that allows the function to accept any number
42
+ * of additional arguments after the `configObject` parameter. These additional arguments are used as
43
+ * children elements for the created element.
44
+ * @returns An object with two properties: "type" and "props".
45
+ */
46
+ function createElement(type, configObject, ...args) {
47
+ const props = Object.assign({}, configObject);
48
+ const hasChildren = args.length > 0;
49
+ const nodeChildren = hasChildren ? [...args] : [];
50
+ props.children = nodeChildren.filter(Boolean).map(c => c instanceof Object ? c : createTextElement(c));
51
+ return {
52
+ type,
53
+ props
54
+ };
55
+ }
56
+
57
+ /**
58
+ * The function creates a text element with a given node value and an empty array of children.
59
+ * @param nodeValue - The value of the text node that will be created.
60
+ * @returns The function `createTextElement` is returning an element object with a `nodeValue` property
61
+ * and an empty `children` array. This element object represents a text node in the virtual DOM.
62
+ */
63
+ function createTextElement(nodeValue) {
64
+ return createElement(TEXT_ELEMENT, {
65
+ nodeValue,
66
+ children: []
67
+ });
68
+ }
69
+
70
+ let rootInstance = null;
71
+ function render(element, parentDom) {
72
+ const prevInstance = rootInstance;
73
+ const nextInstance = reconcile(parentDom, prevInstance, element);
74
+ rootInstance = nextInstance;
75
+ }
76
+ function reconcile(parentDom, instance, element) {
77
+ if (instance === null) {
78
+ const newInstance = instantiate(element);
79
+ parentDom.appendChild(newInstance.dom);
80
+ return newInstance;
81
+ } else if (element == null) {
82
+ parentDom.removeChild(instance.dom);
83
+ return null;
84
+ } else if (instance.element.type !== element.type) {
85
+ const newInstance = instantiate(element);
86
+ parentDom.replaceChild(newInstance.dom, instance.dom);
87
+ return newInstance;
88
+ } else if (typeof element.type === "string") {
89
+ instance.childInstances = reconcileChildren(instance, element);
90
+ instance.element = element;
91
+ return instance;
92
+ } else {
93
+ instance.publicInstance.props = element.props;
94
+ const childElement = instance.publicInstance.render();
95
+ const oldChildInstance = instance.childInstance;
96
+ const childInstance = reconcile(parentDom, oldChildInstance, childElement);
97
+ instance.dom = childInstance.dom;
98
+ instance.childInstance = childInstance;
99
+ instance.element = element;
100
+ return instance;
101
+ }
102
+ }
103
+ function instantiate(element) {
104
+ const { type, props } = element;
105
+ const isDomElement = typeof type === "string";
106
+ if (isDomElement) {
107
+ const isTextElement = type === TEXT_ELEMENT;
108
+ const dom = isTextElement
109
+ ? document.createTextNode("")
110
+ : document.createElement(type);
111
+ updateDomProperties(dom, [], props);
112
+ const childElements = props.children || [];
113
+ const childInstances = childElements.map(instantiate);
114
+ const childDoms = childInstances.map((childInstance) => childInstance.dom);
115
+ childDoms.forEach((childDom) => dom.appendChild(childDom));
116
+ const instance = {
117
+ dom,
118
+ element,
119
+ childInstances,
120
+ };
121
+ return instance;
122
+ } else {
123
+ const instance = {};
124
+ const publicInstance = createPublicInstance(element, instance);
125
+ const childElement = publicInstance.render();
126
+ const childInstance = instantiate(childElement);
127
+ const dom = childInstance.dom;
128
+ Object.assign(instance, {
129
+ dom,
130
+ element,
131
+ childInstance,
132
+ publicInstance,
133
+ });
134
+ return instance;
135
+ }
136
+ }
137
+ function createPublicInstance(element, internalInstance) {
138
+ const { type, props } = element;
139
+ const publicInstance = new type(props);
140
+ publicInstance.__internalInstance = internalInstance;
141
+ return publicInstance;
142
+ }
143
+ function reconcileChildren(instance, element) {
144
+ const dom = instance.dom;
145
+ const childInstances = instance.childInstances;
146
+ const nextChildElements = element.props.children || [];
147
+ const newChildInstances = [];
148
+ const count = Math.max(childInstances.length, nextChildElements.length);
149
+ for (let i = 0; i < count; i++) {
150
+ const childInstance = childInstances[i];
151
+ const childElement = nextChildElements[i];
152
+ const newChildInstance = reconcile(dom, childInstance, childElement);
153
+ newChildInstances.push(newChildInstance);
154
+ }
155
+ return newChildInstances.filter((instance) => instance != null);
156
+ }
157
+
158
+ class Component {
159
+ constructor(props) {
160
+ this.props = props;
161
+ this.state = this.state || {};
162
+ }
163
+ setState(partialState) {
164
+ this.state = Object.assign({}, this.state, partialState);
165
+ updateInstance(this.__internalInstance);
166
+ }
167
+ }
168
+ function updateInstance(internalInstance) {
169
+ const parentDom = internalInstance.dom.parentNode;
170
+ const element = internalInstance.element;
171
+ reconcile(parentDom, internalInstance, element);
172
+ }
173
+
174
+ var ryunix = {
175
+ render,
176
+ createElement,
177
+ Component,
178
+ };
179
+
180
+ exports.Component = Component;
181
+ exports.createElement = createElement;
182
+ exports.default = ryunix;
183
+ exports.render = render;
184
+
185
+ Object.defineProperty(exports, '__esModule', { value: true });
186
+
187
+ }));
@@ -0,0 +1,16 @@
1
+ import { reconcile } from "./reconciler";
2
+ export class Component {
3
+ constructor(props) {
4
+ this.props = props;
5
+ this.state = this.state || {};
6
+ }
7
+ setState(partialState) {
8
+ this.state = Object.assign({}, this.state, partialState);
9
+ updateInstance(this.__internalInstance);
10
+ }
11
+ }
12
+ function updateInstance(internalInstance) {
13
+ const parentDom = internalInstance.dom.parentNode;
14
+ const element = internalInstance.element;
15
+ reconcile(parentDom, internalInstance, element);
16
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * This function updates the properties and event listeners of a DOM element based on the previous and
3
+ * next props passed as arguments.
4
+ * @param dom - The DOM element that needs to be updated with new properties.
5
+ * @param prevProps - An object representing the previous properties of a DOM element.
6
+ * @param nextProps - An object containing the new props that need to be updated on the DOM element.
7
+ */
8
+ export function updateDomProperties(dom, prevProps, nextProps) {
9
+ const isEvent = name => name.startsWith("on");
10
+ const isAttribute = name => !isEvent(name) && name != "children";
11
+ Object.keys(prevProps).filter(isEvent).forEach(name => {
12
+ const eventType = name.toLowerCase().substring(2);
13
+ dom.removeEventListener(eventType, prevProps[name]);
14
+ });
15
+ Object.keys(prevProps).filter(isAttribute).forEach(name => {
16
+ dom[name] = null;
17
+ });
18
+ Object.keys(nextProps).filter(isAttribute).forEach(name => {
19
+ dom[name] = nextProps[name];
20
+ });
21
+ Object.keys(nextProps).filter(isEvent).forEach(name => {
22
+ const eventType = name.toLowerCase().substring(2);
23
+ dom.addEventListener(eventType, nextProps[name]);
24
+ });
25
+ }
package/lib/element.js ADDED
@@ -0,0 +1,37 @@
1
+ const TEXT_ELEMENT = "TEXT";
2
+
3
+ /**
4
+ * This function creates a new element with the given type, configuration object, and children.
5
+ * @param type - The type of the element being created (e.g. "div", "span", "h1", etc.).
6
+ * @param configObject - The `configObject` parameter is an object that contains the properties and
7
+ * values for the element's attributes. These attributes can include things like `className`, `id`,
8
+ * `style`, and any other custom attributes that the user wants to add to the element.
9
+ * @param args - The `args` parameter is a rest parameter that allows the function to accept any number
10
+ * of additional arguments after the `configObject` parameter. These additional arguments are used as
11
+ * children elements for the created element.
12
+ * @returns An object with two properties: "type" and "props".
13
+ */
14
+ export function createElement(type, configObject, ...args) {
15
+ const props = Object.assign({}, configObject);
16
+ const hasChildren = args.length > 0;
17
+ const nodeChildren = hasChildren ? [...args] : [];
18
+ props.children = nodeChildren.filter(Boolean).map(c => c instanceof Object ? c : createTextElement(c));
19
+ return {
20
+ type,
21
+ props
22
+ };
23
+ }
24
+
25
+ /**
26
+ * The function creates a text element with a given node value and an empty array of children.
27
+ * @param nodeValue - The value of the text node that will be created.
28
+ * @returns The function `createTextElement` is returning an element object with a `nodeValue` property
29
+ * and an empty `children` array. This element object represents a text node in the virtual DOM.
30
+ */
31
+ function createTextElement(nodeValue) {
32
+ return createElement(TEXT_ELEMENT, {
33
+ nodeValue,
34
+ children: []
35
+ });
36
+ }
37
+ export { TEXT_ELEMENT };
@@ -0,0 +1,89 @@
1
+ import { updateDomProperties } from "./dom-utils";
2
+ import { TEXT_ELEMENT } from "./element";
3
+ let rootInstance = null;
4
+ export function render(element, parentDom) {
5
+ const prevInstance = rootInstance;
6
+ const nextInstance = reconcile(parentDom, prevInstance, element);
7
+ rootInstance = nextInstance;
8
+ }
9
+ export function reconcile(parentDom, instance, element) {
10
+ if (instance === null) {
11
+ const newInstance = instantiate(element);
12
+ parentDom.appendChild(newInstance.dom);
13
+ return newInstance;
14
+ } else if (element == null) {
15
+ parentDom.removeChild(instance.dom);
16
+ return null;
17
+ } else if (instance.element.type !== element.type) {
18
+ const newInstance = instantiate(element);
19
+ parentDom.replaceChild(newInstance.dom, instance.dom);
20
+ return newInstance;
21
+ } else if (typeof element.type === "string") {
22
+ instance.childInstances = reconcileChildren(instance, element);
23
+ instance.element = element;
24
+ return instance;
25
+ } else {
26
+ instance.publicInstance.props = element.props;
27
+ const childElement = instance.publicInstance.render();
28
+ const oldChildInstance = instance.childInstance;
29
+ const childInstance = reconcile(parentDom, oldChildInstance, childElement);
30
+ instance.dom = childInstance.dom;
31
+ instance.childInstance = childInstance;
32
+ instance.element = element;
33
+ return instance;
34
+ }
35
+ }
36
+ function instantiate(element) {
37
+ const { type, props } = element;
38
+ const isDomElement = typeof type === "string";
39
+ if (isDomElement) {
40
+ const isTextElement = type === TEXT_ELEMENT;
41
+ const dom = isTextElement
42
+ ? document.createTextNode("")
43
+ : document.createElement(type);
44
+ updateDomProperties(dom, [], props);
45
+ const childElements = props.children || [];
46
+ const childInstances = childElements.map(instantiate);
47
+ const childDoms = childInstances.map((childInstance) => childInstance.dom);
48
+ childDoms.forEach((childDom) => dom.appendChild(childDom));
49
+ const instance = {
50
+ dom,
51
+ element,
52
+ childInstances,
53
+ };
54
+ return instance;
55
+ } else {
56
+ const instance = {};
57
+ const publicInstance = createPublicInstance(element, instance);
58
+ const childElement = publicInstance.render();
59
+ const childInstance = instantiate(childElement);
60
+ const dom = childInstance.dom;
61
+ Object.assign(instance, {
62
+ dom,
63
+ element,
64
+ childInstance,
65
+ publicInstance,
66
+ });
67
+ return instance;
68
+ }
69
+ }
70
+ function createPublicInstance(element, internalInstance) {
71
+ const { type, props } = element;
72
+ const publicInstance = new type(props);
73
+ publicInstance.__internalInstance = internalInstance;
74
+ return publicInstance;
75
+ }
76
+ function reconcileChildren(instance, element) {
77
+ const dom = instance.dom;
78
+ const childInstances = instance.childInstances;
79
+ const nextChildElements = element.props.children || [];
80
+ const newChildInstances = [];
81
+ const count = Math.max(childInstances.length, nextChildElements.length);
82
+ for (let i = 0; i < count; i++) {
83
+ const childInstance = childInstances[i];
84
+ const childElement = nextChildElements[i];
85
+ const newChildInstance = reconcile(dom, childInstance, childElement);
86
+ newChildInstances.push(newChildInstance);
87
+ }
88
+ return newChildInstances.filter((instance) => instance != null);
89
+ }
package/lib/ryunix.js ADDED
@@ -0,0 +1,9 @@
1
+ import { render } from "./reconciler";
2
+ import { createElement } from "./element";
3
+ import { Component } from "./component";
4
+ export { createElement, render, Component };
5
+ export default {
6
+ render,
7
+ createElement,
8
+ Component,
9
+ };
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@unsetsoft/ryunixjs",
3
+ "version": "1.0.0-alpha.0",
4
+ "license": "MIT",
5
+ "main": "./dist/Ryunix.js",
6
+ "private": false,
7
+ "scripts": {
8
+ "build": "rollup ./lib/ryunix.js --file ./dist/Ryunix.js --format umd --name Ryunix",
9
+ "prepublishOnly": "yarn build"
10
+ },
11
+ "devDependencies": {
12
+ "rollup": "3.24.0"
13
+ }
14
+ }