veles 1.0.0 → 1.1.1

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.
@@ -169,6 +169,11 @@ function parseChildren({ children, htmlElement, velesNode, portal }) {
169
169
  }
170
170
  //#endregion
171
171
  //#region src/attribute-utils.ts
172
+ const FORM_VALUE_TAGS = new Set([
173
+ "INPUT",
174
+ "TEXTAREA",
175
+ "SELECT"
176
+ ]);
172
177
  const ENUMERATED_BOOLEAN_ATTRIBUTES = {
173
178
  draggable: {
174
179
  trueValue: "true",
@@ -187,9 +192,30 @@ const ENUMERATED_BOOLEAN_ATTRIBUTES = {
187
192
  falseValue: "no"
188
193
  }
189
194
  };
190
- function assignDomAttribute({ htmlElement, attributeName, value }) {
195
+ function assignDomAttribute({ htmlElement, attributeName, value, previousValue }) {
196
+ const normalizedAttributeName = attributeName.toLowerCase();
197
+ if (normalizedAttributeName === "value" && isFormValueElement(htmlElement)) {
198
+ assignFormValueProperty(htmlElement, value);
199
+ return;
200
+ }
201
+ if (normalizedAttributeName === "checked" && htmlElement.tagName === "INPUT") {
202
+ assignBooleanProperty(htmlElement, "checked", value);
203
+ return;
204
+ }
205
+ if (normalizedAttributeName === "selected" && htmlElement.tagName === "OPTION") {
206
+ assignBooleanProperty(htmlElement, "selected", value);
207
+ return;
208
+ }
209
+ if (attributeName === "style") {
210
+ assignStyle({
211
+ value,
212
+ previousValue,
213
+ htmlElement
214
+ });
215
+ return;
216
+ }
191
217
  if (typeof value === "boolean") {
192
- const enumeratedConfig = ENUMERATED_BOOLEAN_ATTRIBUTES[attributeName.toLowerCase()];
218
+ const enumeratedConfig = ENUMERATED_BOOLEAN_ATTRIBUTES[normalizedAttributeName];
193
219
  if (enumeratedConfig) {
194
220
  htmlElement.setAttribute(attributeName, value ? enumeratedConfig.trueValue : enumeratedConfig.falseValue);
195
221
  return;
@@ -198,8 +224,55 @@ function assignDomAttribute({ htmlElement, attributeName, value }) {
198
224
  else htmlElement.removeAttribute(attributeName);
199
225
  return;
200
226
  }
227
+ if (value == null) {
228
+ htmlElement.removeAttribute(attributeName);
229
+ return;
230
+ }
201
231
  htmlElement.setAttribute(attributeName, value);
202
232
  }
233
+ function isFormValueElement(htmlElement) {
234
+ return FORM_VALUE_TAGS.has(htmlElement.tagName);
235
+ }
236
+ function assignFormValueProperty(htmlElement, value) {
237
+ if (htmlElement.tagName === "SELECT" && Array.isArray(value)) {
238
+ const selectedValues = new Set(value.map(String));
239
+ Array.from(htmlElement.options).forEach((option) => {
240
+ const shouldSelect = selectedValues.has(option.value);
241
+ if (option.selected !== shouldSelect) option.selected = shouldSelect;
242
+ });
243
+ return;
244
+ }
245
+ const normalizedValue = value == null ? "" : String(value);
246
+ if (htmlElement.value !== normalizedValue) htmlElement.value = normalizedValue;
247
+ }
248
+ function assignBooleanProperty(htmlElement, propertyName, value) {
249
+ const normalizedValue = Boolean(value);
250
+ if (htmlElement[propertyName] !== normalizedValue) htmlElement[propertyName] = normalizedValue;
251
+ }
252
+ function assignStyle({ value, previousValue, htmlElement }) {
253
+ if (value == null) {
254
+ htmlElement.style.cssText = "";
255
+ htmlElement.removeAttribute("style");
256
+ return;
257
+ }
258
+ if (typeof value !== "object") {
259
+ htmlElement.style.cssText = String(value);
260
+ return;
261
+ }
262
+ const hasPreviousStyleObject = previousValue != null && typeof previousValue === "object";
263
+ if (!hasPreviousStyleObject) htmlElement.style.cssText = "";
264
+ else Object.keys(previousValue).forEach((property) => {
265
+ if (!(property in value) || value[property] == null) htmlElement.style.removeProperty(property);
266
+ });
267
+ Object.entries(value).forEach(([property, propertyValue]) => {
268
+ if (propertyValue == null) {
269
+ htmlElement.style.removeProperty(property);
270
+ return;
271
+ }
272
+ if (hasPreviousStyleObject && previousValue[property] === propertyValue) return;
273
+ htmlElement.style.setProperty(property, String(propertyValue));
274
+ });
275
+ }
203
276
  //#endregion
204
277
  //#region src/create-element/assign-attributes.ts
205
278
  function assignAttributes({ props, htmlElement, velesNode }) {
@@ -302,17 +375,15 @@ function appendComponentToPortal(componentNode, portal) {
302
375
  if ("executedVelesComponent" in fragmentChildComponent) appendComponentToPortal(getExecutedComponentVelesNode(fragmentChildComponent), portal);
303
376
  else portal.append(fragmentChildComponent.html);
304
377
  });
305
- componentNode.phantom;
378
+ componentNode.portal = portal;
306
379
  } else portal.append(componentNode.html);
307
380
  }
308
381
  function cleanupComponentFromPortal(componentNode) {
309
- if ("executedVelesNode" in componentNode && componentNode.phantom) {
310
- componentNode.childComponents.forEach((fragmentChildComponent) => {
311
- if ("executedVelesComponent" in fragmentChildComponent) cleanupComponentFromPortal(getExecutedComponentVelesNode(fragmentChildComponent));
312
- else fragmentChildComponent.html.remove();
313
- });
314
- componentNode.phantom;
315
- } else componentNode.html.remove();
382
+ if ("executedVelesNode" in componentNode && componentNode.phantom) componentNode.childComponents.forEach((fragmentChildComponent) => {
383
+ if ("executedVelesComponent" in fragmentChildComponent) cleanupComponentFromPortal(getExecutedComponentVelesNode(fragmentChildComponent));
384
+ else fragmentChildComponent.html.remove();
385
+ });
386
+ else componentNode.html.remove();
316
387
  }
317
388
  //#endregion
318
389
  //#region src/fragment.ts
@@ -169,6 +169,11 @@ function parseChildren({ children, htmlElement, velesNode, portal }) {
169
169
  }
170
170
  //#endregion
171
171
  //#region src/attribute-utils.ts
172
+ const FORM_VALUE_TAGS = new Set([
173
+ "INPUT",
174
+ "TEXTAREA",
175
+ "SELECT"
176
+ ]);
172
177
  const ENUMERATED_BOOLEAN_ATTRIBUTES = {
173
178
  draggable: {
174
179
  trueValue: "true",
@@ -187,9 +192,30 @@ const ENUMERATED_BOOLEAN_ATTRIBUTES = {
187
192
  falseValue: "no"
188
193
  }
189
194
  };
190
- function assignDomAttribute({ htmlElement, attributeName, value }) {
195
+ function assignDomAttribute({ htmlElement, attributeName, value, previousValue }) {
196
+ const normalizedAttributeName = attributeName.toLowerCase();
197
+ if (normalizedAttributeName === "value" && isFormValueElement(htmlElement)) {
198
+ assignFormValueProperty(htmlElement, value);
199
+ return;
200
+ }
201
+ if (normalizedAttributeName === "checked" && htmlElement.tagName === "INPUT") {
202
+ assignBooleanProperty(htmlElement, "checked", value);
203
+ return;
204
+ }
205
+ if (normalizedAttributeName === "selected" && htmlElement.tagName === "OPTION") {
206
+ assignBooleanProperty(htmlElement, "selected", value);
207
+ return;
208
+ }
209
+ if (attributeName === "style") {
210
+ assignStyle({
211
+ value,
212
+ previousValue,
213
+ htmlElement
214
+ });
215
+ return;
216
+ }
191
217
  if (typeof value === "boolean") {
192
- const enumeratedConfig = ENUMERATED_BOOLEAN_ATTRIBUTES[attributeName.toLowerCase()];
218
+ const enumeratedConfig = ENUMERATED_BOOLEAN_ATTRIBUTES[normalizedAttributeName];
193
219
  if (enumeratedConfig) {
194
220
  htmlElement.setAttribute(attributeName, value ? enumeratedConfig.trueValue : enumeratedConfig.falseValue);
195
221
  return;
@@ -198,8 +224,55 @@ function assignDomAttribute({ htmlElement, attributeName, value }) {
198
224
  else htmlElement.removeAttribute(attributeName);
199
225
  return;
200
226
  }
227
+ if (value == null) {
228
+ htmlElement.removeAttribute(attributeName);
229
+ return;
230
+ }
201
231
  htmlElement.setAttribute(attributeName, value);
202
232
  }
233
+ function isFormValueElement(htmlElement) {
234
+ return FORM_VALUE_TAGS.has(htmlElement.tagName);
235
+ }
236
+ function assignFormValueProperty(htmlElement, value) {
237
+ if (htmlElement.tagName === "SELECT" && Array.isArray(value)) {
238
+ const selectedValues = new Set(value.map(String));
239
+ Array.from(htmlElement.options).forEach((option) => {
240
+ const shouldSelect = selectedValues.has(option.value);
241
+ if (option.selected !== shouldSelect) option.selected = shouldSelect;
242
+ });
243
+ return;
244
+ }
245
+ const normalizedValue = value == null ? "" : String(value);
246
+ if (htmlElement.value !== normalizedValue) htmlElement.value = normalizedValue;
247
+ }
248
+ function assignBooleanProperty(htmlElement, propertyName, value) {
249
+ const normalizedValue = Boolean(value);
250
+ if (htmlElement[propertyName] !== normalizedValue) htmlElement[propertyName] = normalizedValue;
251
+ }
252
+ function assignStyle({ value, previousValue, htmlElement }) {
253
+ if (value == null) {
254
+ htmlElement.style.cssText = "";
255
+ htmlElement.removeAttribute("style");
256
+ return;
257
+ }
258
+ if (typeof value !== "object") {
259
+ htmlElement.style.cssText = String(value);
260
+ return;
261
+ }
262
+ const hasPreviousStyleObject = previousValue != null && typeof previousValue === "object";
263
+ if (!hasPreviousStyleObject) htmlElement.style.cssText = "";
264
+ else Object.keys(previousValue).forEach((property) => {
265
+ if (!(property in value) || value[property] == null) htmlElement.style.removeProperty(property);
266
+ });
267
+ Object.entries(value).forEach(([property, propertyValue]) => {
268
+ if (propertyValue == null) {
269
+ htmlElement.style.removeProperty(property);
270
+ return;
271
+ }
272
+ if (hasPreviousStyleObject && previousValue[property] === propertyValue) return;
273
+ htmlElement.style.setProperty(property, String(propertyValue));
274
+ });
275
+ }
203
276
  //#endregion
204
277
  //#region src/create-element/assign-attributes.ts
205
278
  function assignAttributes({ props, htmlElement, velesNode }) {
@@ -302,17 +375,15 @@ function appendComponentToPortal(componentNode, portal) {
302
375
  if ("executedVelesComponent" in fragmentChildComponent) appendComponentToPortal(getExecutedComponentVelesNode(fragmentChildComponent), portal);
303
376
  else portal.append(fragmentChildComponent.html);
304
377
  });
305
- componentNode.phantom;
378
+ componentNode.portal = portal;
306
379
  } else portal.append(componentNode.html);
307
380
  }
308
381
  function cleanupComponentFromPortal(componentNode) {
309
- if ("executedVelesNode" in componentNode && componentNode.phantom) {
310
- componentNode.childComponents.forEach((fragmentChildComponent) => {
311
- if ("executedVelesComponent" in fragmentChildComponent) cleanupComponentFromPortal(getExecutedComponentVelesNode(fragmentChildComponent));
312
- else fragmentChildComponent.html.remove();
313
- });
314
- componentNode.phantom;
315
- } else componentNode.html.remove();
382
+ if ("executedVelesNode" in componentNode && componentNode.phantom) componentNode.childComponents.forEach((fragmentChildComponent) => {
383
+ if ("executedVelesComponent" in fragmentChildComponent) cleanupComponentFromPortal(getExecutedComponentVelesNode(fragmentChildComponent));
384
+ else fragmentChildComponent.html.remove();
385
+ });
386
+ else componentNode.html.remove();
316
387
  }
317
388
  //#endregion
318
389
  //#region src/fragment.ts
package/dist/index.cjs CHANGED
@@ -1,10 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require__utils = require("./_utils-Cf3Xvo3q.cjs");
2
+ const require__utils = require("./_utils-DnT1tr2L.cjs");
3
3
  //#region src/attach-component.ts
4
4
  /**
5
5
  * Attach Veles component tree to a regular HTML node.
6
6
  * Right now it will wrap the app into an additional `<div>` tag.
7
- *
7
+ *
8
8
  * It returns a function which when executed, will remove the Veles
9
9
  * tree from DOM and remove all subscriptions.
10
10
  */
@@ -192,11 +192,15 @@ function updateUseAttributeValue({ element, value }) {
192
192
  if (attributeValue) htmlElement.removeEventListener(eventName, attributeValue);
193
193
  if (newAttributeValue && typeof newAttributeValue === "function") htmlElement.addEventListener(eventName, newAttributeValue);
194
194
  element.attributeValue = newAttributeValue;
195
- } else require__utils.assignDomAttribute({
196
- htmlElement,
197
- attributeName,
198
- value: newAttributeValue
199
- });
195
+ } else {
196
+ require__utils.assignDomAttribute({
197
+ htmlElement,
198
+ attributeName,
199
+ value: newAttributeValue,
200
+ previousValue: attributeValue
201
+ });
202
+ element.attributeValue = newAttributeValue;
203
+ }
200
204
  }
201
205
  //#endregion
202
206
  //#region src/create-state/update-render-each-value.ts
@@ -274,7 +278,7 @@ function updateUseValueIteratorValue({ value, trackingIterator, createState }) {
274
278
  newChildRenderedComponents.push(require__utils.getMountedNodeExecutedVersion(newRenderedElement[0], "Iterator child is expected to be mounted"));
275
279
  newChildComponents.push(newRenderedElement[0]);
276
280
  if (positioningOffset[index]) offset = offset + positioningOffset[index];
277
- const [newNode, calculatedKey, newState] = newRenderedElement;
281
+ const [newNode, calculatedKey, _newState] = newRenderedElement;
278
282
  const existingElement = elementsByKey[calculatedKey];
279
283
  if (existingElement) {
280
284
  const existingElementNode = require__utils.getExecutedComponentVelesNode(require__utils.getMountedNodeExecutedVersion(existingElement.node, "Existing iterator node is expected to be mounted"));
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { _ as onMount, a as identity, c as addPublicContext, d as popPublicContext, f as Fragment, g as hasCurrentLifecycleContext, h as createTextElement, i as getMountedNodeExecutedVersion, l as createContext, m as assignDomAttribute, n as callUnmountHandlers, o as renderTree, p as createElement, r as getExecutedComponentVelesNode, s as unique, t as callMountHandlers, u as getCurrentContext, v as onUnmount } from "./_utils-CCdioDsh.js";
1
+ import { _ as onMount, a as identity, c as addPublicContext, d as popPublicContext, f as Fragment, g as hasCurrentLifecycleContext, h as createTextElement, i as getMountedNodeExecutedVersion, l as createContext, m as assignDomAttribute, n as callUnmountHandlers, o as renderTree, p as createElement, r as getExecutedComponentVelesNode, s as unique, t as callMountHandlers, u as getCurrentContext, v as onUnmount } from "./_utils-DxfFNKLL.js";
2
2
  //#region src/attach-component.ts
3
3
  /**
4
4
  * Attach Veles component tree to a regular HTML node.
5
5
  * Right now it will wrap the app into an additional `<div>` tag.
6
- *
6
+ *
7
7
  * It returns a function which when executed, will remove the Veles
8
8
  * tree from DOM and remove all subscriptions.
9
9
  */
@@ -191,11 +191,15 @@ function updateUseAttributeValue({ element, value }) {
191
191
  if (attributeValue) htmlElement.removeEventListener(eventName, attributeValue);
192
192
  if (newAttributeValue && typeof newAttributeValue === "function") htmlElement.addEventListener(eventName, newAttributeValue);
193
193
  element.attributeValue = newAttributeValue;
194
- } else assignDomAttribute({
195
- htmlElement,
196
- attributeName,
197
- value: newAttributeValue
198
- });
194
+ } else {
195
+ assignDomAttribute({
196
+ htmlElement,
197
+ attributeName,
198
+ value: newAttributeValue,
199
+ previousValue: attributeValue
200
+ });
201
+ element.attributeValue = newAttributeValue;
202
+ }
199
203
  }
200
204
  //#endregion
201
205
  //#region src/create-state/update-render-each-value.ts
@@ -273,7 +277,7 @@ function updateUseValueIteratorValue({ value, trackingIterator, createState }) {
273
277
  newChildRenderedComponents.push(getMountedNodeExecutedVersion(newRenderedElement[0], "Iterator child is expected to be mounted"));
274
278
  newChildComponents.push(newRenderedElement[0]);
275
279
  if (positioningOffset[index]) offset = offset + positioningOffset[index];
276
- const [newNode, calculatedKey, newState] = newRenderedElement;
280
+ const [newNode, calculatedKey, _newState] = newRenderedElement;
277
281
  const existingElement = elementsByKey[calculatedKey];
278
282
  if (existingElement) {
279
283
  const existingElementNode = getExecutedComponentVelesNode(getMountedNodeExecutedVersion(existingElement.node, "Existing iterator node is expected to be mounted"));
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require__utils = require("./_utils-Cf3Xvo3q.cjs");
2
+ const require__utils = require("./_utils-DnT1tr2L.cjs");
3
3
  exports.Fragment = require__utils.Fragment;
4
4
  exports.jsx = require__utils.createElement;
5
5
  exports.jsxDEV = require__utils.createElement;
@@ -1,2 +1,2 @@
1
- import { f as Fragment, p as createElement } from "./_utils-CCdioDsh.js";
1
+ import { f as Fragment, p as createElement } from "./_utils-DxfFNKLL.js";
2
2
  export { Fragment, createElement as jsx, createElement as jsxDEV, createElement as jsxs };
package/package.json CHANGED
@@ -1,14 +1,29 @@
1
1
  {
2
2
  "name": "veles",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "UI library with main focus on performance",
5
+ "keywords": [
6
+ "JavaScript",
7
+ "UI",
8
+ "library"
9
+ ],
10
+ "homepage": "https://github.com/Bloomca/veles#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/Bloomca/veles/issues"
13
+ },
14
+ "license": "MIT",
15
+ "author": "Seva Zaikov <mail@bloomca.me>",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+ssh://git@github.com/Bloomca/veles.git"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
5
23
  "type": "module",
6
24
  "main": "dist/index.js",
7
25
  "module": "dist/index.js",
8
26
  "typings": "dist/index.d.ts",
9
- "files": [
10
- "dist"
11
- ],
12
27
  "exports": {
13
28
  ".": {
14
29
  "types": "./dist/index.d.ts",
@@ -24,30 +39,31 @@
24
39
  "scripts": {
25
40
  "test": "vitest run",
26
41
  "build": "tsdown",
27
- "ts-compile-check": "tsc -p tsconfig.json"
28
- },
29
- "repository": {
30
- "type": "git",
31
- "url": "git+ssh://git@github.com/Bloomca/veles.git"
32
- },
33
- "keywords": [
34
- "JavaScript",
35
- "UI",
36
- "library"
37
- ],
38
- "author": "Seva Zaikov <mail@bloomca.me>",
39
- "license": "MIT",
40
- "bugs": {
41
- "url": "https://github.com/Bloomca/veles/issues"
42
+ "ts-compile-check": "tsc -p tsconfig.json",
43
+ "lint": "oxlint",
44
+ "fmt": "oxfmt",
45
+ "fmt:check": "oxfmt --check",
46
+ "prepare": "husky"
42
47
  },
43
- "homepage": "https://github.com/Bloomca/veles#readme",
44
48
  "devDependencies": {
45
49
  "@testing-library/dom": "^10.1.0",
46
50
  "@testing-library/jest-dom": "^6.4.5",
47
51
  "@testing-library/user-event": "^14.5.2",
52
+ "husky": "^9.1.7",
48
53
  "jsdom": "^24.0.0",
54
+ "lint-staged": "^17.0.7",
55
+ "oxfmt": "^0.53.0",
56
+ "oxlint": "^1.68.0",
49
57
  "tsdown": "^0.21.7",
50
58
  "typescript": "^6.0.2",
51
59
  "vitest": "^3.2.4"
60
+ },
61
+ "lint-staged": {
62
+ "*.{js,jsx,ts,tsx}": [
63
+ "oxfmt",
64
+ "oxlint"
65
+ ],
66
+ "!(*-lock).json": "oxfmt",
67
+ "*.{md,yml,yaml,css,html}": "oxfmt"
52
68
  }
53
69
  }