@domql/element 3.4.5 → 3.4.9

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 (47) hide show
  1. package/event/__tests__/applyAnimationFrame.test.js +114 -0
  2. package/event/__tests__/applyEvent.test.js +159 -0
  3. package/event/__tests__/applyEventUpdate.test.js +198 -0
  4. package/event/__tests__/applyEventsOnNode.test.js +216 -0
  5. package/event/__tests__/canRenderTag.test.js +50 -0
  6. package/event/__tests__/index.test.js +39 -0
  7. package/event/__tests__/initAnimationFrame.test.js +156 -0
  8. package/event/__tests__/registerFrameListener.test.js +97 -0
  9. package/event/__tests__/store.test.js +93 -0
  10. package/event/__tests__/triggerEventOn.test.js +195 -0
  11. package/event/__tests__/triggerEventOnUpdate.test.js +207 -0
  12. package/event/animationFrame.js +92 -0
  13. package/event/can.js +8 -0
  14. package/event/index.js +5 -0
  15. package/event/on.js +71 -0
  16. package/event/store.js +6 -0
  17. package/methods/set.js +73 -0
  18. package/methods/v2.js +83 -0
  19. package/mixins/attr.js +32 -0
  20. package/mixins/classList.js +62 -0
  21. package/mixins/content.js +65 -0
  22. package/mixins/data.js +26 -0
  23. package/mixins/html.js +19 -0
  24. package/mixins/index.js +23 -0
  25. package/mixins/registry.js +46 -0
  26. package/mixins/scope.js +23 -0
  27. package/mixins/state.js +18 -0
  28. package/mixins/style.js +25 -0
  29. package/mixins/text.js +31 -0
  30. package/package.json +13 -8
  31. package/render/__tests__/appendNode.test.js +53 -0
  32. package/render/__tests__/assignNode.test.js +151 -0
  33. package/render/__tests__/cacheNode.test.js +168 -0
  34. package/render/__tests__/createHTMLNode.test.js +118 -0
  35. package/render/__tests__/createNode.test.js +9 -0
  36. package/render/__tests__/detectTag.test.js +99 -0
  37. package/render/__tests__/index.test.js +56 -0
  38. package/render/__tests__/insertNodeAfter.test.js +111 -0
  39. package/render/__tests__/insertNodeBefore.test.js +65 -0
  40. package/render/append.js +61 -0
  41. package/render/cache.js +68 -0
  42. package/render/create.js +3 -0
  43. package/render/index.js +5 -0
  44. package/utils/applyParam.js +33 -0
  45. package/utils/extendUtils.js +135 -0
  46. package/utils/index.js +4 -0
  47. package/utils/propEvents.js +36 -0
@@ -0,0 +1,65 @@
1
+ 'use strict'
2
+
3
+ import { isFunction, setContentKey } from '@domql/utils'
4
+ import { set } from '../set.js'
5
+
6
+ export const updateContent = function (params, options) {
7
+ const element = this
8
+ const ref = element.__ref
9
+
10
+ const contentKey = ref.contentElementKey
11
+
12
+ if (!element[contentKey]) return
13
+ if (element[contentKey].update) element[contentKey].update(params, options)
14
+ }
15
+
16
+ export const removeContent = function (el, opts = {}) {
17
+ const element = el || this
18
+ const { __ref: ref } = element
19
+ const contentElementKey = setContentKey(element, opts)
20
+
21
+ if (opts.contentElementKey !== 'content') opts.contentElementKey = 'content'
22
+ if (element[contentElementKey]) {
23
+ if (element[contentElementKey].node && element.node) {
24
+ if (element[contentElementKey].tag === 'fragment')
25
+ element.node.innerHTML = ''
26
+ else {
27
+ const contentNode = element[contentElementKey].node
28
+ if (contentNode.parentNode === element.node)
29
+ element.node.removeChild(element[contentElementKey].node)
30
+ }
31
+ }
32
+
33
+ const { __cached } = ref
34
+ if (__cached && __cached[contentElementKey]) {
35
+ if (__cached[contentElementKey].tag === 'fragment')
36
+ __cached[contentElementKey].parent.node.innerHTML = ''
37
+ else if (
38
+ __cached[contentElementKey] &&
39
+ isFunction(__cached[contentElementKey].remove)
40
+ )
41
+ __cached[contentElementKey].remove()
42
+ }
43
+
44
+ ref.__children.splice(ref.__children.indexOf(element[contentElementKey]), 1)
45
+
46
+ delete element[contentElementKey]
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Appends anything as content
52
+ * an original one as a child
53
+ */
54
+ export function setContent (param, element, node, opts) {
55
+ const contentElementKey = setContentKey(element, opts)
56
+ if (param && element) {
57
+ if (element[contentElementKey]?.update) {
58
+ element[contentElementKey].update({}, opts)
59
+ } else {
60
+ set.call(element, param, opts)
61
+ }
62
+ }
63
+ }
64
+
65
+ export default setContent
package/mixins/data.js ADDED
@@ -0,0 +1,26 @@
1
+ 'use strict'
2
+
3
+ import { isObject, deepMerge, exec } from '@domql/utils'
4
+ import { report } from '@domql/report'
5
+
6
+ /**
7
+ * Apply data parameters on the DOM nodes
8
+ * this should only work if `showOnNode: true` is passed
9
+ */
10
+ export function data (params, element, node) {
11
+ if (params) {
12
+ if (element.props.data) deepMerge(params, element.props.data)
13
+ if (params.showOnNode) {
14
+ if (!isObject(params)) report('HTMLInvalidData', params)
15
+
16
+ // Apply data params on node
17
+ for (const dataset in params) {
18
+ if (dataset !== 'showOnNode') {
19
+ node.dataset[dataset] = exec(params[dataset], element)
20
+ }
21
+ }
22
+ }
23
+ }
24
+ }
25
+
26
+ export default data
package/mixins/html.js ADDED
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+
3
+ import { exec } from '@domql/utils'
4
+
5
+ /**
6
+ * Appends raw HTML as content
7
+ * an original one as a child
8
+ */
9
+ export function html (param, element, node) {
10
+ const prop = exec(element?.props?.html || param, element)
11
+ const { __ref } = element
12
+ if (prop !== __ref.__html) {
13
+ if (node.nodeName === 'SVG') node.textContent = prop
14
+ else node.innerHTML = prop
15
+ __ref.__html = prop
16
+ }
17
+ }
18
+
19
+ export default html
@@ -0,0 +1,23 @@
1
+ 'use strict'
2
+
3
+ import { attr } from './attr.js'
4
+ import { applyClasslist } from './classList.js'
5
+ import { data } from './data.js'
6
+ import { html } from './html.js'
7
+ import { style } from './style.js'
8
+ import { text } from './text.js'
9
+ import { state } from './state.js'
10
+ import { scope } from './scope.js'
11
+
12
+ export {
13
+ applyClasslist as classList,
14
+ attr,
15
+ data,
16
+ style,
17
+ text,
18
+ html,
19
+ state,
20
+ scope
21
+ }
22
+
23
+ export * from './registry.js'
@@ -0,0 +1,46 @@
1
+ 'use strict'
2
+
3
+ import { attr } from './attr.js'
4
+ import { classList } from './classList.js'
5
+ import data from './data.js'
6
+ import html from './html.js'
7
+ import scope from './scope.js'
8
+ import state from './state.js'
9
+ import style from './style.js'
10
+ import text from './text.js'
11
+
12
+ export const REGISTRY = {
13
+ attr,
14
+ style,
15
+ text,
16
+ html,
17
+ data,
18
+ classlist: classList,
19
+ state,
20
+ scope,
21
+ deps: (param, el) => param || el.parent.deps,
22
+
23
+ extends: {},
24
+ children: {},
25
+ content: {},
26
+ childExtends: {},
27
+ childExtendsRecursive: {},
28
+ props: {},
29
+ if: {},
30
+ define: {},
31
+ __name: {},
32
+ __ref: {},
33
+ __hash: {},
34
+ __text: {},
35
+ key: {},
36
+ tag: {},
37
+ query: {},
38
+ parent: {},
39
+ node: {},
40
+ variables: {},
41
+ on: {},
42
+ component: {},
43
+ context: {}
44
+ }
45
+
46
+ export default REGISTRY
@@ -0,0 +1,23 @@
1
+ 'use strict'
2
+
3
+ import { isFunction, isObject } from '@domql/utils'
4
+
5
+ /**
6
+ * Apply data parameters on the DOM nodes
7
+ * this should only work if `showOnNode: true` is passed
8
+ */
9
+ export function scope (params, element, node) {
10
+ if (!isObject(params)) return
11
+
12
+ // Apply data params on node
13
+ for (const scopeItem in params) {
14
+ const value = params[scopeItem]
15
+ if (isFunction(value)) {
16
+ element.scope[scopeItem] = value.bind(element)
17
+ } else {
18
+ element.scope[scopeItem] = value
19
+ }
20
+ }
21
+ }
22
+
23
+ export default scope
@@ -0,0 +1,18 @@
1
+ 'use strict'
2
+
3
+ import { exec, isObject, STATE_METHODS } from '@domql/utils'
4
+
5
+ export function state (params, element, node) {
6
+ const state = exec(params, element)
7
+
8
+ if (isObject(state)) {
9
+ for (const param in state) {
10
+ if (STATE_METHODS.has(param)) continue
11
+ if (!Object.prototype.hasOwnProperty.call(state, param)) continue
12
+ }
13
+ }
14
+
15
+ return element
16
+ }
17
+
18
+ export default state
@@ -0,0 +1,25 @@
1
+ 'use strict'
2
+
3
+ import { exec, isObject } from '@domql/utils'
4
+ import { report } from '@domql/report'
5
+
6
+ /**
7
+ * Recursively add styles to a DOM node
8
+ */
9
+ export function style (params, element, node) {
10
+ if (params) {
11
+ if (isObject(params)) {
12
+ const { __ref } = element
13
+ if (!__ref.__style) __ref.__style = {}
14
+ const cache = __ref.__style
15
+ for (const prop in params) {
16
+ const val = exec(params[prop], element)
17
+ if (val === cache[prop]) continue
18
+ cache[prop] = val
19
+ node.style[prop] = val
20
+ }
21
+ } else report('HTMLInvalidStyles', params)
22
+ }
23
+ }
24
+
25
+ export default style
package/mixins/text.js ADDED
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ import { create } from '../create.js'
4
+ import { exec, isString, SVG_TAGS } from '@domql/utils'
5
+
6
+ /**
7
+ * Creates a text node and appends into
8
+ * an original one as a child
9
+ */
10
+ export function text (param, element, node) {
11
+ let prop = exec(element.props.text || param, element)
12
+ if (isString(prop) && prop.includes('{{')) {
13
+ prop = element.call('replaceLiteralsWithObjectFields', prop, element.state)
14
+ }
15
+ if (element.tag === 'string') {
16
+ node.nodeValue = prop
17
+ } else if (param !== undefined && param !== null) {
18
+ // SVG elements don't support appending text nodes as children
19
+ if (SVG_TAGS.has(element.tag)) {
20
+ if (node) node.textContent = prop
21
+ return
22
+ }
23
+ if (element.__text) {
24
+ if (element.__text.text === prop) return
25
+ element.__text.text = prop
26
+ if (element.__text.node) element.__text.node.nodeValue = prop
27
+ } else create({ tag: 'string', text: prop }, element, '__text')
28
+ }
29
+ }
30
+
31
+ export default text
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@domql/element",
3
- "version": "3.4.5",
3
+ "version": "3.4.9",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "module": "./dist/esm/index.js",
@@ -9,7 +9,7 @@
9
9
  ".": {
10
10
  "import": "./dist/esm/index.js",
11
11
  "require": "./dist/cjs/index.js",
12
- "browser": "./dist/iife/index.js",
12
+ "browser": "./dist/esm/index.js",
13
13
  "default": "./dist/esm/index.js"
14
14
  },
15
15
  "./event": {
@@ -36,7 +36,12 @@
36
36
  "source": "index.js",
37
37
  "files": [
38
38
  "dist",
39
- "*.js"
39
+ "*.js",
40
+ "event",
41
+ "methods",
42
+ "mixins",
43
+ "utils",
44
+ "render"
40
45
  ],
41
46
  "scripts": {
42
47
  "copy:package:cjs": "cp ../../build/package-cjs.json dist/cjs/package.json",
@@ -47,16 +52,16 @@
47
52
  "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"
48
53
  },
49
54
  "dependencies": {
50
- "@domql/report": "^3.4.5",
51
- "@domql/state": "^3.4.5",
52
- "@domql/utils": "^3.4.5",
53
- "attrs-in-props": "^3.4.5"
55
+ "@domql/report": "^3.4.9",
56
+ "@domql/state": "^3.4.9",
57
+ "@domql/utils": "^3.4.9",
58
+ "attrs-in-props": "^3.4.9"
54
59
  },
55
60
  "gitHead": "9fc1b79b41cdc725ca6b24aec64920a599634681",
56
61
  "devDependencies": {
57
62
  "@babel/core": "^7.26.0"
58
63
  },
59
- "browser": "./dist/iife/index.js",
64
+ "browser": "./dist/esm/index.js",
60
65
  "unpkg": "./dist/iife/index.js",
61
66
  "jsdelivr": "./dist/iife/index.js",
62
67
  "sideEffects": false
@@ -0,0 +1,53 @@
1
+ import { jest } from '@jest/globals'
2
+ import { appendNode } from '../append'
3
+
4
+ describe('appendNode', () => {
5
+ let parentNode
6
+ let childNode
7
+
8
+ beforeEach(() => {
9
+ // Setup fresh DOM elements before each test
10
+ parentNode = document.createElement('div')
11
+ childNode = document.createElement('span')
12
+ })
13
+
14
+ test('should append child to parent node successfully', () => {
15
+ const result = appendNode(childNode, parentNode)
16
+
17
+ expect(parentNode.children).toHaveLength(1)
18
+ expect(parentNode.firstChild).toBe(childNode)
19
+ expect(result).toBe(childNode)
20
+ })
21
+
22
+ test('should handle invalid parent node', () => {
23
+ const consoleSpy = jest.spyOn(console, 'error')
24
+ const invalidParent = null
25
+
26
+ const result = appendNode(childNode, invalidParent)
27
+
28
+ expect(consoleSpy).toHaveBeenCalledWith(
29
+ 'Does not support to append',
30
+ invalidParent,
31
+ childNode
32
+ )
33
+ expect(result).toBeUndefined()
34
+
35
+ consoleSpy.mockRestore()
36
+ })
37
+
38
+ test('should handle invalid child node', () => {
39
+ const consoleSpy = jest.spyOn(console, 'error')
40
+ const invalidChild = null
41
+
42
+ const result = appendNode(invalidChild, parentNode)
43
+
44
+ expect(consoleSpy).toHaveBeenCalledWith(
45
+ 'Does not support to append',
46
+ parentNode,
47
+ invalidChild
48
+ )
49
+ expect(result).toBeUndefined()
50
+
51
+ consoleSpy.mockRestore()
52
+ })
53
+ })
@@ -0,0 +1,151 @@
1
+ import { assignNode } from '../append'
2
+
3
+ describe('assignNode', () => {
4
+ let element
5
+ let parent
6
+ let mockNode
7
+ let mockParentNode
8
+
9
+ beforeEach(() => {
10
+ // Setup mock DOM nodes
11
+ mockNode = document.createElement('div')
12
+ mockParentNode = document.createElement('div')
13
+
14
+ // Setup mock element and parent objects
15
+ element = {
16
+ key: 'testKey',
17
+ node: mockNode,
18
+ tag: 'div'
19
+ }
20
+
21
+ parent = {
22
+ node: mockParentNode
23
+ }
24
+ })
25
+
26
+ test('should throw error when element is null', () => {
27
+ expect(() => {
28
+ assignNode(null, parent)
29
+ }).toThrow('Element is required')
30
+ })
31
+
32
+ test('should throw error when parent is null', () => {
33
+ expect(() => {
34
+ assignNode(element, null)
35
+ }).toThrow('Parent is required')
36
+ })
37
+
38
+ test('should assign element to parent using element key', () => {
39
+ const result = assignNode(element, parent)
40
+
41
+ // Check key assignment
42
+ expect(parent[element.key]).toBe(element)
43
+
44
+ // Check DOM structure
45
+ expect(parent.node.children).toHaveLength(1)
46
+ expect(parent.node.firstChild).toBe(element.node)
47
+ expect(element.node.parentNode).toBe(parent.node)
48
+
49
+ // Check return value
50
+ expect(result).toBe(element)
51
+ })
52
+
53
+ test('should assign element to parent using custom key', () => {
54
+ const customKey = 'customKey'
55
+ const result = assignNode(element, parent, customKey)
56
+
57
+ // Check custom key assignment
58
+ expect(parent[customKey]).toBe(element)
59
+ expect(parent[element.key]).toBeUndefined()
60
+
61
+ // Check DOM structure
62
+ expect(parent.node.children).toHaveLength(1)
63
+ expect(parent.node.firstChild).toBe(element.node)
64
+ expect(element.node.parentNode).toBe(parent.node)
65
+
66
+ // Check return value
67
+ expect(result).toBe(element)
68
+ })
69
+
70
+ test('should not modify DOM when element tag is shadow', () => {
71
+ element.tag = 'shadow'
72
+ const result = assignNode(element, parent)
73
+
74
+ // Check key assignment still happens
75
+ expect(parent[element.key]).toBe(element)
76
+
77
+ // Check DOM remains unchanged
78
+ expect(parent.node.children).toHaveLength(0)
79
+ expect(element.node.parentNode).toBeNull()
80
+
81
+ // Check return value
82
+ expect(result).toBe(element)
83
+ })
84
+
85
+ test('should insert node before reference node', () => {
86
+ const referenceNode = document.createElement('div')
87
+ parent.node.appendChild(referenceNode)
88
+
89
+ const attachOptions = {
90
+ position: 'before',
91
+ node: referenceNode
92
+ }
93
+
94
+ const result = assignNode(element, parent, null, attachOptions)
95
+
96
+ // Check key assignment
97
+ expect(parent[element.key]).toBe(element)
98
+
99
+ // Check DOM structure
100
+ expect(parent.node.children).toHaveLength(2)
101
+ expect(parent.node.firstChild).toBe(element.node)
102
+ expect(element.node.nextSibling).toBe(referenceNode)
103
+ expect(referenceNode.previousSibling).toBe(element.node)
104
+
105
+ // Check return value
106
+ expect(result).toBe(element)
107
+ })
108
+
109
+ test('should insert node after reference node', () => {
110
+ const referenceNode = document.createElement('div')
111
+ parent.node.appendChild(referenceNode)
112
+
113
+ const attachOptions = {
114
+ position: 'after',
115
+ node: referenceNode
116
+ }
117
+
118
+ const result = assignNode(element, parent, null, attachOptions)
119
+
120
+ // Check key assignment
121
+ expect(parent[element.key]).toBe(element)
122
+
123
+ // Check DOM structure
124
+ expect(parent.node.children).toHaveLength(2)
125
+ expect(parent.node.lastChild).toBe(element.node)
126
+ expect(element.node.previousSibling).toBe(referenceNode)
127
+ expect(referenceNode.nextSibling).toBe(element.node)
128
+
129
+ // Check return value
130
+ expect(result).toBe(element)
131
+ })
132
+
133
+ test('should use parent node when attachOptions node is not provided', () => {
134
+ const attachOptions = {
135
+ position: 'before'
136
+ }
137
+
138
+ const result = assignNode(element, parent, null, attachOptions)
139
+
140
+ // Check key assignment
141
+ expect(parent[element.key]).toBe(element)
142
+
143
+ // Check DOM structure
144
+ expect(parent.node.children).toHaveLength(0)
145
+ expect(parent.node.firstChild).toBe(null)
146
+ expect(element.node.parentNode).toBe(null)
147
+
148
+ // Check return value
149
+ expect(result).toBe(element)
150
+ })
151
+ })