veles 0.0.9 → 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/README.md CHANGED
@@ -4,8 +4,6 @@
4
4
  [![Build Size](https://img.shields.io/bundlephobia/minzip/veles?label=bundle%20size)](https://bundlephobia.com/result?p=veles)
5
5
  [![Version](https://img.shields.io/npm/v/veles)](https://www.npmjs.com/package/veles)
6
6
 
7
- > This library is still in early stages, so the API is not 100% finalized
8
-
9
7
  `Veles` is a component-based performance-focused UI library. The main goal of this library is to provide a composable way to build highly interactive interfaces, which should be performant out of the box, as long as you follow the recommendations.
10
8
 
11
9
  ## Performance
@@ -30,16 +28,16 @@ Types are installed automatically with the same package.
30
28
  import { createState } from "veles";
31
29
 
32
30
  function NameComponent() {
33
- const nameState = createState("");
31
+ const name$ = createState("");
34
32
  return (
35
33
  <div>
36
34
  <input
37
35
  type="text"
38
36
  name="name"
39
- value={nameState.useAttribute()}
40
- onInput={(e) => nameState.setValue(e.target.value)}
37
+ value={name$.attribute()}
38
+ onInput={(e) => name$.set(e.target.value)}
41
39
  />
42
- <p>{nameState.useValue()}</p>
40
+ <p>{name$.render()}</p>
43
41
  </div>
44
42
  );
45
43
  }
@@ -55,7 +53,3 @@ This will render an input and will update the Text node dynamically, without re-
55
53
  - [Differences from other frameworks](https://bloomca.github.io/veles/frameworks-difference.html)
56
54
 
57
55
  There also a companion app ([veles-calendar-app](https://github.com/Bloomca/veles-calendar-app)), which is developed using Veles and is supposed to push it to the limits, identify the issues and ideally improve performance even more.
58
-
59
- ### Features
60
-
61
- The library is under development, so some features are not available yet. Namely the TypeScript type inferring is not the best (although the library does support TypeScript), and Portals are not implemented yet.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veles",
3
- "version": "0.0.9",
3
+ "version": "1.0.0-alpha.0",
4
4
  "description": "UI library with main focus on performance",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,15 +19,10 @@
19
19
  "types": "./dist/jsx-runtime.d.ts",
20
20
  "import": "./dist/jsx-runtime.js",
21
21
  "require": "./dist/jsx-runtime.cjs"
22
- },
23
- "./utils": {
24
- "types": "./dist/utils/index.d.ts",
25
- "import": "./dist/utils/index.js",
26
- "require": "./dist/utils/index.cjs"
27
22
  }
28
23
  },
29
24
  "scripts": {
30
- "test": "jest",
25
+ "test": "vitest run",
31
26
  "build": "tsup",
32
27
  "ts-compile-check": "tsc -p tsconfig.json"
33
28
  },
@@ -50,11 +45,8 @@
50
45
  "@testing-library/dom": "^10.1.0",
51
46
  "@testing-library/jest-dom": "^6.4.5",
52
47
  "@testing-library/user-event": "^14.5.2",
53
- "@types/jest": "^29.5.12",
54
- "jest": "^29.7.0",
55
- "jest-environment-jsdom": "^29.7.0",
56
48
  "jsdom": "^24.0.0",
57
- "ts-jest": "^29.1.2",
58
- "tsup": "^8.0.2"
49
+ "tsup": "^8.0.2",
50
+ "vitest": "^3.2.4"
59
51
  }
60
52
  }
@@ -1,285 +0,0 @@
1
- // src/hooks/lifecycle.ts
2
- var contextStack = [];
3
- var currentContext = null;
4
- function addContext(newContext) {
5
- contextStack.push(newContext);
6
- currentContext = newContext;
7
- }
8
- function popContext() {
9
- contextStack.pop();
10
- currentContext = contextStack[contextStack.length - 1];
11
- }
12
- function onMount(cb) {
13
- if (currentContext) {
14
- currentContext.onMount(cb);
15
- } else {
16
- console.error("missing current context");
17
- }
18
- }
19
- function onUnmount(cb) {
20
- if (currentContext) {
21
- currentContext.onUnmount(cb);
22
- } else {
23
- console.error("missing current context");
24
- }
25
- }
26
-
27
- // src/create-element/create-text-element.ts
28
- function createTextElement(text) {
29
- const mountHandlers = [];
30
- const unmountHandlers = [];
31
- return {
32
- velesStringElement: true,
33
- // in case there is no text, we create an empty Text node, so we still can
34
- // have a reference to it, replace it, call lifecycle methods, etc
35
- html: document.createTextNode(text || ""),
36
- _privateMethods: {
37
- _addMountHandler(cb) {
38
- mountHandlers.push(cb);
39
- },
40
- _callMountHandlers() {
41
- mountHandlers.forEach((cb) => cb());
42
- },
43
- _addUnmountHandler: (cb) => {
44
- unmountHandlers.push(cb);
45
- },
46
- _callUnmountHandlers: () => {
47
- unmountHandlers.forEach((cb) => cb());
48
- }
49
- }
50
- };
51
- }
52
-
53
- // src/create-element/parse-children.ts
54
- function parseChildren({
55
- children,
56
- htmlElement,
57
- velesNode
58
- }) {
59
- const childComponents = [];
60
- if (children === void 0 || children === null) {
61
- return childComponents;
62
- }
63
- let lastInsertedNode = null;
64
- (Array.isArray(children) ? children : [children]).forEach(
65
- (childComponent) => {
66
- if (typeof childComponent === "string") {
67
- const textNode = createTextElement(childComponent);
68
- htmlElement.append(textNode.html);
69
- lastInsertedNode = textNode.html;
70
- childComponents.push(textNode);
71
- } else if (typeof childComponent === "number") {
72
- const textNode = createTextElement(String(childComponent));
73
- htmlElement.append(textNode.html);
74
- lastInsertedNode = textNode.html;
75
- childComponents.push(textNode);
76
- } else if (typeof childComponent === "object" && childComponent && "velesNode" in childComponent && (childComponent == null ? void 0 : childComponent.velesNode)) {
77
- if (childComponent.phantom) {
78
- childComponent.childComponents.forEach((childComponentofPhantom) => {
79
- if ("velesNode" in childComponentofPhantom) {
80
- htmlElement.append(childComponentofPhantom.html);
81
- childComponentofPhantom.parentVelesElement = velesNode;
82
- lastInsertedNode = childComponentofPhantom.html;
83
- } else if ("velesStringElement" in childComponentofPhantom) {
84
- const velesElementNode = childComponentofPhantom;
85
- if (!velesElementNode) {
86
- console.error("can't find HTML tree in a component chain");
87
- } else {
88
- htmlElement.append(velesElementNode.html);
89
- lastInsertedNode = velesElementNode.html;
90
- velesElementNode.parentVelesElement = velesNode;
91
- }
92
- } else {
93
- }
94
- });
95
- childComponent.parentVelesElement = velesNode;
96
- childComponents.push(childComponent);
97
- } else {
98
- htmlElement.append(childComponent.html);
99
- childComponent.parentVelesElement = velesNode;
100
- childComponents.push(childComponent);
101
- lastInsertedNode = childComponent.html;
102
- }
103
- } else if (typeof childComponent === "object" && childComponent && "velesComponentObject" in childComponent) {
104
- childComponent.insertAfter = lastInsertedNode;
105
- childComponent.parentVelesElement = velesNode;
106
- childComponents.push(childComponent);
107
- lastInsertedNode = childComponent;
108
- } else if (typeof childComponent === "object" && childComponent && "velesStringElement" in childComponent && (childComponent == null ? void 0 : childComponent.velesStringElement)) {
109
- htmlElement.append(childComponent.html);
110
- childComponent.parentVelesElement = velesNode;
111
- childComponents.push(childComponent);
112
- lastInsertedNode = childComponent.html;
113
- }
114
- }
115
- );
116
- return childComponents;
117
- }
118
-
119
- // src/create-element/assign-attributes.ts
120
- function assignAttributes({
121
- props,
122
- htmlElement,
123
- velesNode
124
- }) {
125
- Object.entries(props).forEach(([key, value]) => {
126
- const isFunction = typeof value === "function";
127
- if (isFunction && value.velesAttribute === true) {
128
- const attributeValue = value(htmlElement, key, velesNode);
129
- assignAttribute({ key, value: attributeValue, htmlElement });
130
- } else {
131
- assignAttribute({ key, value, htmlElement });
132
- }
133
- });
134
- }
135
- function assignAttribute({
136
- key,
137
- value,
138
- htmlElement
139
- }) {
140
- if (
141
- // basically, any form of `on` handlers, like `onClick`, `onCopy`, etc
142
- typeof value === "function" && key.startsWith("on")
143
- ) {
144
- htmlElement.addEventListener(key.slice(2).toLocaleLowerCase(), value);
145
- } else {
146
- if (typeof value === "boolean") {
147
- if (value)
148
- htmlElement.setAttribute(key, "");
149
- } else {
150
- htmlElement.setAttribute(key, value);
151
- }
152
- }
153
- }
154
-
155
- // src/create-element/parse-component.ts
156
- function parseComponent({
157
- element,
158
- props
159
- }) {
160
- const mountCbs = [];
161
- const unmountCbs = [];
162
- return {
163
- velesComponentObject: true,
164
- element,
165
- props,
166
- _privateMethods: {
167
- _addMountHandler(cb) {
168
- mountCbs.push(cb);
169
- },
170
- _addUnmountHandler: (cb) => {
171
- unmountCbs.push(cb);
172
- },
173
- _callMountHandlers: () => {
174
- mountCbs.forEach((cb) => cb());
175
- },
176
- _callUnmountHandlers: () => {
177
- unmountCbs.forEach((cb) => cb());
178
- }
179
- }
180
- };
181
- }
182
- function executeComponent({
183
- element,
184
- props
185
- }) {
186
- let componentUnmountCbs = [];
187
- let componentMountCbs = [];
188
- const componentAPI = {
189
- onMount: (cb) => {
190
- componentMountCbs.push(cb);
191
- },
192
- onUnmount: (cb) => {
193
- componentUnmountCbs.push(cb);
194
- }
195
- };
196
- addContext(componentAPI);
197
- const _componentTree = element(props, componentAPI);
198
- const componentTree = typeof _componentTree === "string" || !_componentTree ? createTextElement(_componentTree) : _componentTree;
199
- popContext();
200
- const velesComponent = {
201
- velesComponent: true,
202
- tree: componentTree,
203
- _privateMethods: {
204
- _addMountHandler(cb) {
205
- componentMountCbs.push(cb);
206
- },
207
- _addUnmountHandler: (cb) => {
208
- componentAPI.onUnmount(cb);
209
- },
210
- _callMountHandlers: () => {
211
- componentMountCbs.forEach((cb) => {
212
- const mountCbResult = cb();
213
- if (typeof mountCbResult === "function") {
214
- componentAPI.onUnmount(mountCbResult);
215
- }
216
- });
217
- },
218
- _callUnmountHandlers: () => {
219
- componentUnmountCbs.forEach((cb) => cb());
220
- }
221
- }
222
- };
223
- return velesComponent;
224
- }
225
-
226
- // src/create-element/create-element.ts
227
- function createElement(element, props = {}) {
228
- if (typeof element === "string") {
229
- const { children, ref, phantom = false, ...otherProps } = props;
230
- const newElement = document.createElement(element);
231
- const velesNode = {};
232
- if (ref == null ? void 0 : ref.velesRef) {
233
- ref.current = newElement;
234
- }
235
- const childComponents = parseChildren({
236
- children,
237
- htmlElement: newElement,
238
- velesNode
239
- });
240
- const unmountHandlers = [];
241
- velesNode.html = newElement;
242
- velesNode.velesNode = true;
243
- velesNode.childComponents = childComponents;
244
- velesNode.phantom = phantom;
245
- const mountHandlers = [];
246
- velesNode._privateMethods = {
247
- _addMountHandler(cb) {
248
- mountHandlers.push(cb);
249
- },
250
- _callMountHandlers() {
251
- mountHandlers.forEach((cb) => cb());
252
- },
253
- _addUnmountHandler(cb) {
254
- unmountHandlers.push(cb);
255
- },
256
- _callUnmountHandlers() {
257
- unmountHandlers.forEach((cb) => cb());
258
- }
259
- };
260
- assignAttributes({ props: otherProps, htmlElement: newElement, velesNode });
261
- return velesNode;
262
- } else if (typeof element === "function") {
263
- return parseComponent({ element, props });
264
- }
265
- throw new Error(
266
- "Veles createElement expects a valid DOM string or another component"
267
- );
268
- }
269
-
270
- // src/fragment.ts
271
- function Fragment({ children }) {
272
- return createElement("div", {
273
- phantom: true,
274
- children
275
- });
276
- }
277
-
278
- export {
279
- onMount,
280
- onUnmount,
281
- createTextElement,
282
- executeComponent,
283
- createElement,
284
- Fragment
285
- };