@domql/element 3.2.3 → 3.2.8

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.
Files changed (105) hide show
  1. package/children.js +40 -12
  2. package/create.js +40 -26
  3. package/define.js +1 -1
  4. package/dist/cjs/children.js +39 -11
  5. package/dist/cjs/create.js +41 -11
  6. package/dist/cjs/define.js +1 -1
  7. package/dist/cjs/event/animationFrame.js +96 -0
  8. package/dist/cjs/event/can.js +28 -0
  9. package/dist/cjs/event/index.js +20 -0
  10. package/dist/cjs/event/on.js +84 -0
  11. package/dist/cjs/event/store.js +27 -0
  12. package/dist/cjs/extend.js +6 -6
  13. package/dist/cjs/index.js +9 -6
  14. package/dist/cjs/iterate.js +13 -13
  15. package/dist/cjs/methods/set.js +5 -0
  16. package/dist/cjs/methods/v2.js +1 -1
  17. package/dist/cjs/mixins/attr.js +3 -2
  18. package/dist/cjs/mixins/classList.js +11 -1
  19. package/dist/cjs/mixins/content.js +1 -2
  20. package/dist/cjs/mixins/html.js +1 -2
  21. package/dist/cjs/mixins/state.js +2 -2
  22. package/dist/cjs/mixins/style.js +11 -2
  23. package/dist/cjs/mixins/text.js +5 -1
  24. package/dist/cjs/node.js +8 -5
  25. package/dist/cjs/render/append.js +72 -0
  26. package/dist/cjs/render/cache.js +80 -0
  27. package/dist/cjs/render/create.js +25 -0
  28. package/dist/cjs/render/index.js +20 -0
  29. package/dist/cjs/set.js +12 -14
  30. package/dist/cjs/update.js +80 -40
  31. package/dist/cjs/utils/applyParam.js +3 -3
  32. package/dist/cjs/utils/extendUtils.js +5 -5
  33. package/dist/cjs/utils/index.js +2 -0
  34. package/dist/cjs/utils/propEvents.js +21 -4
  35. package/dist/esm/children.js +39 -11
  36. package/dist/esm/create.js +42 -11
  37. package/dist/esm/define.js +1 -1
  38. package/dist/esm/event/animationFrame.js +76 -0
  39. package/dist/esm/event/can.js +8 -0
  40. package/dist/esm/event/index.js +3 -0
  41. package/dist/esm/event/on.js +64 -0
  42. package/dist/esm/event/store.js +7 -0
  43. package/dist/esm/extend.js +7 -7
  44. package/dist/esm/index.js +8 -6
  45. package/dist/esm/iterate.js +13 -13
  46. package/dist/esm/methods/set.js +10 -0
  47. package/dist/esm/methods/v2.js +1 -1
  48. package/dist/esm/mixins/attr.js +4 -3
  49. package/dist/esm/mixins/classList.js +11 -1
  50. package/dist/esm/mixins/content.js +1 -2
  51. package/dist/esm/mixins/html.js +1 -2
  52. package/dist/esm/mixins/state.js +2 -2
  53. package/dist/esm/mixins/style.js +12 -3
  54. package/dist/esm/mixins/text.js +6 -2
  55. package/dist/esm/node.js +8 -5
  56. package/dist/esm/render/append.js +52 -0
  57. package/dist/esm/render/cache.js +60 -0
  58. package/dist/esm/render/create.js +5 -0
  59. package/dist/esm/render/index.js +3 -0
  60. package/dist/esm/set.js +12 -14
  61. package/dist/esm/update.js +80 -42
  62. package/dist/esm/utils/applyParam.js +3 -3
  63. package/dist/esm/utils/extendUtils.js +5 -5
  64. package/dist/esm/utils/index.js +1 -0
  65. package/dist/esm/utils/propEvents.js +21 -4
  66. package/dist/iife/index.js +4718 -0
  67. package/extend.js +7 -10
  68. package/index.js +9 -6
  69. package/iterate.js +20 -13
  70. package/node.js +10 -8
  71. package/package.json +42 -18
  72. package/set.js +6 -5
  73. package/update.js +90 -52
  74. package/__tests__/checkIfOnUpdate.test.js +0 -103
  75. package/__tests__/children.test.js +0 -209
  76. package/__tests__/define.test.js +0 -75
  77. package/__tests__/inheritStateUpdates.test.js +0 -79
  78. package/__tests__/renderElement.test.js +0 -131
  79. package/__tests__/resetElement.test.js +0 -44
  80. package/__tests__/set.test.js +0 -312
  81. package/__tests__/throughExecProps.test.js +0 -86
  82. package/__tests__/throughInitialDefine.test.js +0 -104
  83. package/__tests__/throughInitialExec.test.js +0 -92
  84. package/__tests__/throughUpdatedDefine.test.js +0 -92
  85. package/__tests__/throughUpdatedExec.test.js +0 -111
  86. package/__tests__/tree.test.js +0 -15
  87. package/__tests__/update.test.js +0 -256
  88. package/dist/cjs/package.json +0 -4
  89. package/methods/set.js +0 -63
  90. package/methods/v2.js +0 -83
  91. package/mixins/attr.js +0 -32
  92. package/mixins/classList.js +0 -54
  93. package/mixins/content.js +0 -65
  94. package/mixins/data.js +0 -26
  95. package/mixins/html.js +0 -21
  96. package/mixins/index.js +0 -23
  97. package/mixins/registry.js +0 -46
  98. package/mixins/scope.js +0 -23
  99. package/mixins/state.js +0 -19
  100. package/mixins/style.js +0 -16
  101. package/mixins/text.js +0 -26
  102. package/utils/applyParam.js +0 -34
  103. package/utils/extendUtils.js +0 -149
  104. package/utils/index.js +0 -3
  105. package/utils/propEvents.js +0 -19
package/extend.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- import { isFunction, exec, isProduction } from '@domql/utils'
3
+ import { isFunction, exec } from '@domql/utils'
4
4
  import {
5
5
  getExtendStack,
6
6
  jointStacks,
@@ -9,6 +9,8 @@ import {
9
9
  fallbackStringExtend
10
10
  } from './utils/index.js'
11
11
 
12
+ // Module-level cache for context.defaultExtends merged result.
13
+ // Set once on first use, never invalidated — assumes defaultExtends is static.
12
14
  let mainExtend
13
15
 
14
16
  /**
@@ -27,21 +29,16 @@ export const applyExtend = (element, parent, options = {}) => {
27
29
 
28
30
  const extendStack = getExtendStack(extend, context)
29
31
 
30
- // if (isProduction()) delete element.extend
31
32
  delete element.extend
32
33
 
33
34
  let childExtendStack = []
34
35
  if (parent) {
35
36
  element.parent = parent
36
37
  // Assign parent attr to the element
37
- if (!options.ignoreChildExtend && !(props && props.ignoreChildExtend)) {
38
+ if (!options.ignoreChildExtend && !props?.ignoreChildExtend) {
38
39
  childExtendStack = getExtendStack(parent.childExtend, context)
39
40
 
40
- // if (!options.ignoreChildExtend && !(props && exec(props, element).ignoreChildExtend)) {
41
- // const ignoreChildExtendRecursive = props && exec(props, element).ignoreChildExtendRecursive
42
-
43
- const ignoreChildExtendRecursive =
44
- props && props.ignoreChildExtendRecursive
41
+ const ignoreChildExtendRecursive = props?.ignoreChildExtendRecursive
45
42
  if (parent.childExtendRecursive && !ignoreChildExtendRecursive) {
46
43
  const canExtendRecursive = element.key !== '__text'
47
44
  if (canExtendRecursive) {
@@ -84,9 +81,9 @@ export const applyExtend = (element, parent, options = {}) => {
84
81
  if (__ref) __ref.__extend = stack
85
82
  let mergedExtend = cloneAndMergeArrayExtend(stack)
86
83
 
87
- const COMPONENTS = (context && context.components) || options.components
84
+ const COMPONENTS = context?.components || options.components
88
85
  const component = exec(element.component || mergedExtend.component, element)
89
- if (component && COMPONENTS && COMPONENTS[component]) {
86
+ if (component && COMPONENTS?.[component]) {
90
87
  const componentExtend = cloneAndMergeArrayExtend(
91
88
  getExtendStack(COMPONENTS[component])
92
89
  )
package/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  'use strict'
2
2
 
3
- import { TREE } from './tree'
4
- import create from './create'
5
- import createNode from './node'
6
- import define from './define'
7
- import update from './update'
8
- import set from './set'
3
+ import { TREE } from './tree.js'
4
+ import create from './create.js'
5
+ import createNode from './node.js'
6
+ import define from './define.js'
7
+ import update from './update.js'
8
+ import set from './set.js'
9
9
 
10
10
  export {
11
11
  TREE,
@@ -15,3 +15,6 @@ export {
15
15
  update,
16
16
  set
17
17
  }
18
+
19
+ export * from './event/index.js'
20
+ export * from './render/index.js'
package/iterate.js CHANGED
@@ -49,8 +49,7 @@ export const throughUpdatedExec = (element, options = {}) => {
49
49
  continue
50
50
  }
51
51
  const execReturnsString = isString(newExec) || isNumber(newExec)
52
- // if (prop && prop.node && execReturnsString) {
53
- if (prop && prop.node && execReturnsString) {
52
+ if (prop?.node && execReturnsString) {
54
53
  overwrite(prop, { text: newExec })
55
54
  } else if (newExec !== prop) {
56
55
  if (matchesComponentNaming(param)) {
@@ -74,8 +73,13 @@ export const throughExecProps = element => {
74
73
  const { __ref: ref } = element
75
74
  const { props } = element
76
75
  for (const k in props) {
76
+ // Check for 'is', 'has', 'use' prefixes using charCodeAt
77
+ const c0 = k.charCodeAt(0)
78
+ const c1 = k.charCodeAt(1)
77
79
  const isDefine =
78
- k.startsWith('is') || k.startsWith('has') || k.startsWith('use')
80
+ (c0 === 105 && c1 === 115) || // 'is'
81
+ (c0 === 104 && c1 === 97 && k.charCodeAt(2) === 115) || // 'has'
82
+ (c0 === 117 && c1 === 115 && k.charCodeAt(2) === 101) // 'use'
79
83
  const cachedExecProp = ref.__execProps[k]
80
84
  if (isFunction(cachedExecProp)) {
81
85
  const result = exec(cachedExecProp, element)
@@ -100,15 +104,16 @@ export const throughExecProps = element => {
100
104
  }
101
105
  }
102
106
 
103
- export const isPropertyInDefines = (key, element) => {}
104
-
105
107
  export const throughInitialDefine = element => {
106
108
  const { define, context, __ref: ref } = element
107
109
 
108
- let defineObj = {}
110
+ const hasLocalDefine = isObject(define)
109
111
  const hasGlobalDefine = context && isObject(context.define)
110
- if (isObject(define)) defineObj = { ...define }
111
- if (hasGlobalDefine) defineObj = { ...defineObj, ...context.define }
112
+ if (!hasLocalDefine && !hasGlobalDefine) return element
113
+
114
+ const defineObj = hasLocalDefine && hasGlobalDefine
115
+ ? { ...define, ...context.define }
116
+ : hasLocalDefine ? define : context.define
112
117
 
113
118
  for (const param in defineObj) {
114
119
  let elementProp = element[param]
@@ -152,11 +157,14 @@ export const throughInitialDefine = element => {
152
157
 
153
158
  export const throughUpdatedDefine = element => {
154
159
  const { context, define, __ref: ref } = element
155
- const changes = {}
156
160
 
157
- let obj = {}
158
- if (isObject(define)) obj = { ...define }
159
- if (isObject(context && context.define)) obj = { ...obj, ...context.define }
161
+ const hasLocalDefine = isObject(define)
162
+ const hasGlobalDefine = isObject(context?.define)
163
+ if (!hasLocalDefine && !hasGlobalDefine) return
164
+
165
+ const obj = hasLocalDefine && hasGlobalDefine
166
+ ? { ...define, ...context.define }
167
+ : hasLocalDefine ? define : context.define
160
168
 
161
169
  for (const param in obj) {
162
170
  const execParam = ref.__exec[param]
@@ -189,5 +197,4 @@ export const throughUpdatedDefine = element => {
189
197
  element[param] = newExecParam
190
198
  }
191
199
  }
192
- return changes
193
200
  }
package/node.js CHANGED
@@ -1,8 +1,9 @@
1
1
  'use strict'
2
2
 
3
3
  import { isFunction, isMethod, isObject, isUndefined } from '@domql/utils'
4
- import { applyEventsOnNode, triggerEventOn } from '@domql/event'
5
- import { cacheNode } from '@domql/render'
4
+ import { applyEventsOnNode, triggerEventOn } from './event/index.js'
5
+ import { propagateEventsFromProps, propagateEventsFromElement } from './utils/propEvents.js'
6
+ import { cacheNode } from './render/index.js'
6
7
  import { create } from './create.js'
7
8
 
8
9
  import {
@@ -14,8 +15,6 @@ import { REGISTRY } from './mixins/index.js'
14
15
  import { applyParam } from './utils/applyParam.js'
15
16
  import setChildren from './children.js'
16
17
  import { setContent } from './set.js'
17
- // import { defineSetter } from './methods'
18
-
19
18
  const ENV = process.env.NODE_ENV
20
19
 
21
20
  export const createNode = (element, opts) => {
@@ -52,16 +51,19 @@ export const createNode = (element, opts) => {
52
51
  // iterate through exec
53
52
  throughInitialExec(element)
54
53
 
54
+ propagateEventsFromProps(element)
55
+ propagateEventsFromElement(element)
55
56
  applyEventsOnNode(element, { isNewNode, ...opts })
56
57
 
57
58
  for (const param in element) {
58
59
  const value = element[param]
59
60
 
60
61
  if (
61
- !Object.hasOwnProperty.call(element, param) ||
62
- isUndefined(value) ||
62
+ !Object.prototype.hasOwnProperty.call(element, param) ||
63
+ value === undefined ||
63
64
  isMethod(param, element) ||
64
- isObject(REGISTRY[param])
65
+ isObject(REGISTRY[param]) ||
66
+ (param.length > 2 && param.charCodeAt(0) === 111 && param.charCodeAt(1) === 110 && param.charCodeAt(2) >= 65 && param.charCodeAt(2) <= 90)
65
67
  ) {
66
68
  continue
67
69
  }
@@ -90,7 +92,7 @@ export const createNode = (element, opts) => {
90
92
 
91
93
  const content = element.children
92
94
  ? setChildren(element.children, element, opts)
93
- : element.content || element.content
95
+ : element.children || element.content
94
96
 
95
97
  if (content) {
96
98
  setContent(content, element, opts)
package/package.json CHANGED
@@ -1,39 +1,63 @@
1
1
  {
2
2
  "name": "@domql/element",
3
- "version": "3.2.3",
3
+ "version": "3.2.8",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
- "module": "index.js",
7
- "main": "index.js",
6
+ "module": "./dist/esm/index.js",
7
+ "main": "./dist/cjs/index.js",
8
8
  "exports": {
9
9
  ".": {
10
- "kalduna": "./index.js",
11
- "default": "./dist/cjs/index.js"
10
+ "import": "./dist/esm/index.js",
11
+ "require": "./dist/cjs/index.js",
12
+ "browser": "./dist/iife/index.js",
13
+ "default": "./dist/esm/index.js"
12
14
  },
13
- "./utils/component": "./utils/component.js",
14
- "./methods/index.js": "./methods/index.js"
15
+ "./event": {
16
+ "import": "./dist/esm/event/index.js",
17
+ "require": "./dist/cjs/event/index.js",
18
+ "default": "./dist/esm/event/index.js"
19
+ },
20
+ "./render": {
21
+ "import": "./dist/esm/render/index.js",
22
+ "require": "./dist/cjs/render/index.js",
23
+ "default": "./dist/esm/render/index.js"
24
+ },
25
+ "./*.js": {
26
+ "import": "./dist/esm/*.js",
27
+ "require": "./dist/cjs/*.js",
28
+ "default": "./dist/esm/*.js"
29
+ },
30
+ "./*": {
31
+ "import": "./dist/esm/*.js",
32
+ "require": "./dist/cjs/*.js",
33
+ "default": "./dist/esm/*.js"
34
+ }
15
35
  },
16
36
  "source": "index.js",
17
37
  "files": [
18
- "*/**.js",
19
- "*.js",
20
- "dist"
38
+ "dist",
39
+ "*.js"
21
40
  ],
22
41
  "scripts": {
23
42
  "copy:package:cjs": "cp ../../build/package-cjs.json dist/cjs/package.json",
24
- "build:esm": "npx cross-env NODE_ENV=$NODE_ENV npx esbuild *.js methods/*.js mixins/*.js utils/*.js --target=es2019 --format=esm --outdir=dist/esm",
25
- "build:cjs": "npx cross-env NODE_ENV=$NODE_ENV npx esbuild *.js methods/*.js mixins/*.js utils/*.js --target=node16 --format=cjs --outdir=dist/cjs",
26
- "build": "npx rimraf -I dist; npm run build:cjs; npm run build:esm",
27
- "prepublish": "npx rimraf -I dist && npm run build && npm run copy:package:cjs"
43
+ "build:esm": "cross-env NODE_ENV=$NODE_ENV esbuild *.js methods/*.js mixins/*.js utils/*.js event/*.js render/*.js --target=es2020 --format=esm --outdir=dist/esm --define:process.env.NODE_ENV=process.env.NODE_ENV",
44
+ "build:cjs": "cross-env NODE_ENV=$NODE_ENV esbuild *.js methods/*.js mixins/*.js utils/*.js event/*.js render/*.js --target=node18 --format=cjs --outdir=dist/cjs --define:process.env.NODE_ENV=process.env.NODE_ENV",
45
+ "build": "node ../../build/build.js",
46
+ "prepublish": "npm run build && npm run copy:package:cjs",
47
+ "build:iife": "cross-env NODE_ENV=$NODE_ENV esbuild index.js --bundle --target=es2020 --format=iife --global-name=DomqlElement --outfile=dist/iife/index.js --define:process.env.NODE_ENV=process.env.NODE_ENV"
28
48
  },
29
49
  "dependencies": {
30
- "@domql/event": "^3.2.3",
31
- "@domql/render": "^3.2.3",
50
+ "@domql/report": "^3.2.3",
32
51
  "@domql/state": "^3.2.3",
33
- "@domql/utils": "^3.2.3"
52
+ "@domql/utils": "^3.2.3",
53
+ "attrs-in-props": "^3.2.3"
34
54
  },
35
55
  "gitHead": "9fc1b79b41cdc725ca6b24aec64920a599634681",
36
56
  "devDependencies": {
37
57
  "@babel/core": "^7.26.0"
38
- }
58
+ },
59
+ "browser": "./dist/iife/index.js",
60
+ "unpkg": "./dist/iife/index.js",
61
+ "jsdelivr": "./dist/iife/index.js",
62
+ "sideEffects": false
39
63
  }
package/set.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { deepContains, exec, isFunction, OPTIONS } from '@domql/utils'
4
4
  import { create } from './create.js'
5
- import { triggerEventOn, triggerEventOnUpdate } from '@domql/event'
5
+ import { triggerEventOn, triggerEventOnUpdate } from './event/index.js'
6
6
 
7
7
  export const setContentKey = (element, opts = {}) => {
8
8
  const { __ref: ref } = element
@@ -13,7 +13,7 @@ export const setContentKey = (element, opts = {}) => {
13
13
  return ref.contentElementKey
14
14
  }
15
15
 
16
- export const reset = options => {
16
+ export function reset (options) {
17
17
  const element = this
18
18
  create(element, element.parent, undefined, {
19
19
  ignoreChildExtends: true,
@@ -71,15 +71,16 @@ export const removeContent = function (el, opts = {}) {
71
71
  // Handle fragment removal
72
72
  if (content.tag === 'fragment' && content.__ref?.__children) {
73
73
  // Remove all child nodes
74
- content.__ref.__children.forEach(key => {
75
- const child = content[key]
74
+ const __children = content.__ref.__children
75
+ for (let i = 0; i < __children.length; i++) {
76
+ const child = content[__children[i]]
76
77
  if (child.node && child.node.parentNode) {
77
78
  child.node.parentNode.removeChild(child.node)
78
79
  }
79
80
  if (isFunction(child.remove)) {
80
81
  child.remove()
81
82
  }
82
- })
83
+ }
83
84
  } else {
84
85
  // Handle regular element removal
85
86
  if (content.node && content.node.parentNode) {
package/update.js CHANGED
@@ -11,17 +11,15 @@ import {
11
11
  isUndefined,
12
12
  merge,
13
13
  overwriteDeep,
14
- deepClone,
15
14
  isMethod,
16
15
  findInheritedState,
17
- deepMerge,
18
16
  OPTIONS,
19
17
  updateProps,
20
18
  captureSnapshot,
21
19
  propertizeUpdate
22
20
  } from '@domql/utils'
23
21
 
24
- import { applyEvent, triggerEventOn, triggerEventOnUpdate } from '@domql/event'
22
+ import { applyEvent, triggerEventOn, triggerEventOnUpdate } from './event/index.js'
25
23
  import { createState } from '@domql/state'
26
24
 
27
25
  import { create } from './create.js'
@@ -46,14 +44,10 @@ const UPDATE_DEFAULT_OPTIONS = {
46
44
  }
47
45
 
48
46
  export const update = function (params = {}, opts) {
49
- const calleeElementCache = opts?.calleeElement
50
- const options = deepClone(
51
- isObject(opts)
52
- ? deepMerge(opts, UPDATE_DEFAULT_OPTIONS)
53
- : UPDATE_DEFAULT_OPTIONS,
54
- { exclude: ['calleeElement'] }
55
- )
56
- options.calleeElement = calleeElementCache
47
+ // Shallow copy is sufficient - all values are primitives/references that shouldn't be cloned
48
+ const options = isObject(opts)
49
+ ? { ...UPDATE_DEFAULT_OPTIONS, ...opts }
50
+ : { ...UPDATE_DEFAULT_OPTIONS }
57
51
  const element = this
58
52
 
59
53
  let ref = element.__ref
@@ -96,20 +90,23 @@ export const update = function (params = {}, opts) {
96
90
  if (ref.__if && !options.preventPropsUpdate) {
97
91
  const hasParentProps =
98
92
  parent.props && (parent.props[key] || parent.props.childProps)
99
- const hasFunctionInProps = ref.__propsStack.filter(v => isFunction(v))
100
- const props = params.props || hasParentProps || hasFunctionInProps.length
93
+ const hasFunctionInProps = ref.__propsStack.some(isFunction)
94
+ const props = params.props || hasParentProps || hasFunctionInProps
101
95
  if (props) updateProps(props, element, parent)
102
96
  }
103
97
 
104
98
  if (!options.preventBeforeUpdateListener && !options.preventListeners) {
105
- const simulate = { ...params, ...element }
106
- const beforeUpdateReturns = triggerEventOnUpdate(
107
- 'beforeUpdate',
108
- params,
109
- simulate,
110
- options
111
- )
112
- if (beforeUpdateReturns === false) return element
99
+ const hasBeforeUpdate = element.on?.beforeUpdate || element.props?.onBeforeUpdate
100
+ if (hasBeforeUpdate) {
101
+ const simulate = { ...params, ...element }
102
+ const beforeUpdateReturns = triggerEventOnUpdate(
103
+ 'beforeUpdate',
104
+ params,
105
+ simulate,
106
+ options
107
+ )
108
+ if (beforeUpdateReturns === false) return element
109
+ }
113
110
  }
114
111
 
115
112
  // apply new updates
@@ -125,10 +122,7 @@ export const update = function (params = {}, opts) {
125
122
  }
126
123
 
127
124
  if (!ref.__if) return false
128
- if (!node) {
129
- // return createNode(element, options)
130
- return
131
- }
125
+ if (!node) return
132
126
 
133
127
  const {
134
128
  preventUpdate,
@@ -152,15 +146,24 @@ export const update = function (params = {}, opts) {
152
146
  } else options.preventUpdateAfterCount++
153
147
  }
154
148
 
149
+ // Convert arrays to Sets once for O(1) lookups during iteration
150
+ const preventUpdateSet = isArray(preventUpdate) ? new Set(preventUpdate) : null
151
+ const preventDefineUpdateSet = isArray(preventDefineUpdate) ? new Set(preventDefineUpdate) : null
152
+
155
153
  for (const param in element) {
156
154
  const prop = element[param]
157
155
 
158
- if (!Object.hasOwnProperty.call(element, param)) continue
156
+ if (!Object.prototype.hasOwnProperty.call(element, param)) continue
159
157
 
160
158
  const isInPreventUpdate =
161
- isArray(preventUpdate) && preventUpdate.includes(param)
159
+ preventUpdateSet && preventUpdateSet.has(param)
162
160
  const isInPreventDefineUpdate =
163
- isArray(preventDefineUpdate) && preventDefineUpdate.includes(param)
161
+ preventDefineUpdateSet && preventDefineUpdateSet.has(param)
162
+
163
+ // Skip onXxx event handler functions (e.g. onClick) that may remain at root level
164
+ const isRootEventHandler = isFunction(prop) && param.length > 2 &&
165
+ param.charCodeAt(0) === 111 && param.charCodeAt(1) === 110 && // 'on'
166
+ param.charCodeAt(2) >= 65 && param.charCodeAt(2) <= 90 // A-Z
164
167
 
165
168
  if (
166
169
  isUndefined(prop) ||
@@ -168,8 +171,9 @@ export const update = function (params = {}, opts) {
168
171
  isInPreventDefineUpdate ||
169
172
  preventDefineUpdate === true ||
170
173
  preventDefineUpdate === param ||
171
- (preventStateUpdate && param) === 'state' ||
174
+ (preventStateUpdate && param === 'state') ||
172
175
  isMethod(param, element) ||
176
+ isRootEventHandler ||
173
177
  isObject(REGISTRY[param])
174
178
  ) {
175
179
  continue
@@ -190,8 +194,11 @@ export const update = function (params = {}, opts) {
190
194
  options.onEachUpdate(param, element, element.state, element.context)
191
195
  }
192
196
 
197
+ const childParams = params[param]
198
+ if (childParams === undefined && !options.isForced) continue
199
+
193
200
  const childUpdateCall = () =>
194
- update.call(prop, params[prop], {
201
+ update.call(prop, childParams, {
195
202
  ...options,
196
203
  currentSnapshot: snapshotOnCallee,
197
204
  calleeElement
@@ -202,7 +209,7 @@ export const update = function (params = {}, opts) {
202
209
  // eslint-disable-line
203
210
  childUpdateCall()
204
211
  // handle lazy load
205
- if (!options.preventUpdateListener) {
212
+ if (!options.preventUpdateListener && !options.preventListeners) {
206
213
  triggerEventOn('lazyLoad', element, options)
207
214
  }
208
215
  })
@@ -211,17 +218,60 @@ export const update = function (params = {}, opts) {
211
218
  }
212
219
 
213
220
  if (!preventContentUpdate) {
214
- const children = params.children || element.children
215
- const content = children
216
- ? setChildren(children, element, opts)
217
- : element.children || params.content
221
+ // Update existing content element if it's a live DOMQL element
222
+ const contentKey = ref.contentElementKey || 'content'
223
+ const existingContent = element[contentKey]
224
+
225
+ // Re-evaluate children if the element has a children property
226
+ const childrenProp = params.children || element.children
227
+ if (childrenProp) {
228
+ const content = setChildren(childrenProp, element, opts)
229
+ if (content && !ref.__noChildrenDifference) {
230
+ setContent(content, element, options)
231
+ } else if (existingContent?.__ref && isFunction(existingContent.update)) {
232
+ const lazyLoad = element.props?.lazyLoad || options.lazyLoad
233
+ const contentUpdateCall = () =>
234
+ update.call(existingContent, params[contentKey], {
235
+ ...options,
236
+ currentSnapshot: snapshotOnCallee,
237
+ calleeElement
238
+ })
239
+
240
+ if (lazyLoad) {
241
+ window.requestAnimationFrame(() => {
242
+ contentUpdateCall()
243
+ if (!options.preventUpdateListener && !options.preventListeners) {
244
+ triggerEventOn('lazyLoad', element, options)
245
+ }
246
+ })
247
+ } else contentUpdateCall()
248
+ }
249
+ } else if (existingContent?.__ref && isFunction(existingContent.update)) {
250
+ const lazyLoad = element.props?.lazyLoad || options.lazyLoad
251
+ const contentUpdateCall = () =>
252
+ update.call(existingContent, params[contentKey], {
253
+ ...options,
254
+ currentSnapshot: snapshotOnCallee,
255
+ calleeElement
256
+ })
218
257
 
219
- if (content) {
220
- setContent(content, element, options)
258
+ if (lazyLoad) {
259
+ window.requestAnimationFrame(() => {
260
+ contentUpdateCall()
261
+ if (!options.preventUpdateListener && !options.preventListeners) {
262
+ triggerEventOn('lazyLoad', element, options)
263
+ }
264
+ })
265
+ } else contentUpdateCall()
266
+ } else {
267
+ const content = element.children || params.content
268
+ if (content) {
269
+ setContent(content, element, options)
270
+ }
221
271
  }
222
272
  }
223
273
 
224
- if (!preventUpdateListener) {
274
+ if (!preventUpdateListener && !options.preventListeners) {
225
275
  triggerEventOn('update', element, options)
226
276
  }
227
277
  }
@@ -275,8 +325,7 @@ const checkIfOnUpdate = (element, parent, options) => {
275
325
 
276
326
  const nextElement = element.nextElement()
277
327
  const nextNode = nextElement?.node // document.body.contains(previousElement.node)
278
- const hasNext = nextNode?.parentNode // && document.body.contains(nextElement.node)
279
- // const hasNext = nextElement && document.body.contains(nextElement.node)
328
+ const hasNext = nextNode?.parentNode
280
329
 
281
330
  const attachOptions = (hasPrevious || hasNext) && {
282
331
  position: hasPrevious ? 'after' : hasNext ? 'before' : null,
@@ -333,7 +382,7 @@ const inheritStateUpdates = (element, options) => {
333
382
 
334
383
  // If does not have own state inherit from parent
335
384
  if (!stateKey && !ref.__hasRootState) {
336
- element.state = (parent && parent.state) || {}
385
+ element.state = parent?.state || {}
337
386
  return
338
387
  }
339
388
 
@@ -389,14 +438,3 @@ const createStateUpdate = (element, parent, options) => {
389
438
  }
390
439
 
391
440
  export default update
392
-
393
- // save updates history
394
- // const overwriteChanges = overwriteDeep(element, params, { exclude: METHODS_EXL })
395
- // // const overwriteChanges = overwriteDeep(element, params)
396
- // const propsChanges = throughExecProps(element)
397
- // const execChanges = throughUpdatedExec(element, { ignore: UPDATE_DEFAULT_OPTIONS })
398
- // const definedChanges = throughUpdatedDefine(element)
399
- // if (options.stackChanges && ref.__stackChanges) {
400
- // const stackChanges = merge(definedChanges, merge(execChanges, overwriteChanges))
401
- // ref.__stackChanges.push(stackChanges)
402
- // }
@@ -1,103 +0,0 @@
1
- import { update } from '../update'
2
-
3
- describe('checkIfOnUpdate via update()', () => {
4
- let element, parent, options
5
-
6
- beforeEach(() => {
7
- parent = {
8
- node: document.createElement('div'),
9
- props: {},
10
- state: {}
11
- }
12
-
13
- element = {
14
- __ref: {
15
- __if: undefined,
16
- __state: null,
17
- __hasRootState: false,
18
- __execProps: {},
19
- contentElementKey: 'content'
20
- },
21
- parent,
22
- props: {},
23
- state: {
24
- update: (el, st) => {
25
- return st
26
- }
27
- },
28
- context: {
29
- defaultExtends: {}
30
- },
31
- node: document.createElement('div'),
32
- if: () => true,
33
- previousElement: () => {
34
- return {}
35
- },
36
- nextElement: () => {
37
- return {}
38
- },
39
- removeContent: () => {
40
- return true
41
- }
42
- }
43
-
44
- options = {}
45
- })
46
-
47
- it('uses props.if when element.if missing', () => {
48
- delete element.if
49
- element.props.if = () => false
50
- update.call(element, {}, options)
51
- expect(element.node).toEqual(document.createElement('div'))
52
- })
53
-
54
- it('retains state when __hasRootState=true', () => {
55
- element.__ref.__hasRootState = true
56
- element.state.critical = true
57
- element.__ref.__if = false
58
-
59
- update.call(element, {}, options)
60
-
61
- expect(element.state.critical).toBe(true)
62
- expect(element.state.preserved).toBeUndefined()
63
- })
64
-
65
- it('processes nested content with parseDeep', () => {
66
- element.content = {
67
- parseDeep: () => ({ parsed: true }),
68
- existing: 'data'
69
- }
70
-
71
- update.call(element, {}, options)
72
-
73
- expect(element.content.parsed).toBe(true)
74
- expect(element.content.existing).toBeUndefined()
75
- })
76
-
77
- it('reattaches after previous sibling', () => {
78
- const prevNode = document.createElement('span')
79
- parent.node.appendChild(prevNode)
80
-
81
- update.call(element, {}, options)
82
-
83
- const newElement = parent.node.children[0]
84
- expect(newElement).toEqual(document.createElement('span'))
85
- expect(newElement.previousSibling).toBe(null)
86
- })
87
-
88
- // it('reattaches before next sibling', () => {
89
- // const nextNode = document.createElement('p')
90
- // parent.node.appendChild(nextNode)
91
-
92
- // update.call(element, {}, options)
93
-
94
- // const newElement = parent.node.children[0]
95
- // expect(newElement).toEqual(document.createElement('p'))
96
- // expect(newElement.nextSibling).toBe(null)
97
- // })
98
-
99
- // it('appends to parent when no siblings exist', () => {
100
- // update.call(element, {}, options)
101
- // expect(parent.node.children).toHaveLength(0)
102
- // })
103
- })