@domql/element 3.4.4 → 3.4.6
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/event/__tests__/applyAnimationFrame.test.js +114 -0
- package/event/__tests__/applyEvent.test.js +159 -0
- package/event/__tests__/applyEventUpdate.test.js +198 -0
- package/event/__tests__/applyEventsOnNode.test.js +216 -0
- package/event/__tests__/canRenderTag.test.js +50 -0
- package/event/__tests__/index.test.js +39 -0
- package/event/__tests__/initAnimationFrame.test.js +156 -0
- package/event/__tests__/registerFrameListener.test.js +97 -0
- package/event/__tests__/store.test.js +93 -0
- package/event/__tests__/triggerEventOn.test.js +195 -0
- package/event/__tests__/triggerEventOnUpdate.test.js +207 -0
- package/event/animationFrame.js +92 -0
- package/event/can.js +8 -0
- package/event/index.js +5 -0
- package/event/on.js +71 -0
- package/event/store.js +6 -0
- package/methods/set.js +73 -0
- package/methods/v2.js +83 -0
- package/mixins/attr.js +32 -0
- package/mixins/classList.js +62 -0
- package/mixins/content.js +65 -0
- package/mixins/data.js +26 -0
- package/mixins/html.js +19 -0
- package/mixins/index.js +23 -0
- package/mixins/registry.js +46 -0
- package/mixins/scope.js +23 -0
- package/mixins/state.js +18 -0
- package/mixins/style.js +25 -0
- package/mixins/text.js +31 -0
- package/package.json +13 -8
- package/render/__tests__/appendNode.test.js +53 -0
- package/render/__tests__/assignNode.test.js +151 -0
- package/render/__tests__/cacheNode.test.js +168 -0
- package/render/__tests__/createHTMLNode.test.js +118 -0
- package/render/__tests__/createNode.test.js +9 -0
- package/render/__tests__/detectTag.test.js +99 -0
- package/render/__tests__/index.test.js +56 -0
- package/render/__tests__/insertNodeAfter.test.js +111 -0
- package/render/__tests__/insertNodeBefore.test.js +65 -0
- package/render/append.js +61 -0
- package/render/cache.js +68 -0
- package/render/create.js +3 -0
- package/render/index.js +5 -0
- package/utils/applyParam.js +33 -0
- package/utils/extendUtils.js +135 -0
- package/utils/index.js +4 -0
- package/utils/propEvents.js +36 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { jest } from '@jest/globals'
|
|
2
|
+
import { triggerEventOnUpdate } from '../on'
|
|
3
|
+
|
|
4
|
+
describe('triggerEventOnUpdate', () => {
|
|
5
|
+
let mockElement
|
|
6
|
+
let mockEventHandler
|
|
7
|
+
let mockUpdatedObj
|
|
8
|
+
let mockOptions
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
mockEventHandler = jest.fn(() => 'eventResult')
|
|
12
|
+
mockUpdatedObj = { updated: 'value' }
|
|
13
|
+
mockOptions = { option1: 'value1' }
|
|
14
|
+
|
|
15
|
+
mockElement = {
|
|
16
|
+
state: { elementState: 'test' },
|
|
17
|
+
context: { elementContext: 'test' },
|
|
18
|
+
on: {},
|
|
19
|
+
props: {}
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('event resolution and execution', () => {
|
|
24
|
+
test('should trigger event from on property', () => {
|
|
25
|
+
// Arrange
|
|
26
|
+
mockElement.on.click = mockEventHandler
|
|
27
|
+
|
|
28
|
+
// Act
|
|
29
|
+
const result = triggerEventOnUpdate(
|
|
30
|
+
'click',
|
|
31
|
+
mockUpdatedObj,
|
|
32
|
+
mockElement,
|
|
33
|
+
mockOptions
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
// Assert
|
|
37
|
+
expect(result).toBe('eventResult')
|
|
38
|
+
expect(mockEventHandler).toHaveBeenCalledWith(
|
|
39
|
+
mockUpdatedObj,
|
|
40
|
+
mockElement,
|
|
41
|
+
mockElement.state,
|
|
42
|
+
mockElement.context,
|
|
43
|
+
mockOptions
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('should trigger event from props using camelCase', () => {
|
|
48
|
+
// Arrange
|
|
49
|
+
mockElement.props.onClick = mockEventHandler
|
|
50
|
+
|
|
51
|
+
// Act
|
|
52
|
+
const result = triggerEventOnUpdate(
|
|
53
|
+
'click',
|
|
54
|
+
mockUpdatedObj,
|
|
55
|
+
mockElement,
|
|
56
|
+
mockOptions
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
// Assert
|
|
60
|
+
expect(result).toBe('eventResult')
|
|
61
|
+
expect(mockEventHandler).toHaveBeenCalledWith(
|
|
62
|
+
mockUpdatedObj,
|
|
63
|
+
mockElement,
|
|
64
|
+
mockElement.state,
|
|
65
|
+
mockElement.context,
|
|
66
|
+
mockOptions
|
|
67
|
+
)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('should prioritize on property over props', () => {
|
|
71
|
+
// Arrange
|
|
72
|
+
const onEventHandler = jest.fn(() => 'onResult')
|
|
73
|
+
const propsEventHandler = jest.fn(() => 'propsResult')
|
|
74
|
+
|
|
75
|
+
mockElement.on.click = onEventHandler
|
|
76
|
+
mockElement.props.onClick = propsEventHandler
|
|
77
|
+
|
|
78
|
+
// Act
|
|
79
|
+
const result = triggerEventOnUpdate(
|
|
80
|
+
'click',
|
|
81
|
+
mockUpdatedObj,
|
|
82
|
+
mockElement,
|
|
83
|
+
mockOptions
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// Assert
|
|
87
|
+
expect(result).toBe('onResult')
|
|
88
|
+
expect(onEventHandler).toHaveBeenCalled()
|
|
89
|
+
expect(propsEventHandler).not.toHaveBeenCalled()
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe('parameter handling', () => {
|
|
94
|
+
test('should handle undefined state and context', () => {
|
|
95
|
+
// Arrange
|
|
96
|
+
mockElement = {
|
|
97
|
+
on: { click: mockEventHandler }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Act
|
|
101
|
+
triggerEventOnUpdate('click', mockUpdatedObj, mockElement, mockOptions)
|
|
102
|
+
|
|
103
|
+
// Assert
|
|
104
|
+
expect(mockEventHandler).toHaveBeenCalledWith(
|
|
105
|
+
mockUpdatedObj,
|
|
106
|
+
mockElement,
|
|
107
|
+
undefined,
|
|
108
|
+
undefined,
|
|
109
|
+
mockOptions
|
|
110
|
+
)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('should handle missing options', () => {
|
|
114
|
+
// Arrange
|
|
115
|
+
mockElement.on.click = mockEventHandler
|
|
116
|
+
|
|
117
|
+
// Act
|
|
118
|
+
triggerEventOnUpdate('click', mockUpdatedObj, mockElement)
|
|
119
|
+
|
|
120
|
+
// Assert
|
|
121
|
+
expect(mockEventHandler).toHaveBeenCalledWith(
|
|
122
|
+
mockUpdatedObj,
|
|
123
|
+
mockElement,
|
|
124
|
+
mockElement.state,
|
|
125
|
+
mockElement.context,
|
|
126
|
+
undefined
|
|
127
|
+
)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
test('should handle empty updatedObj', () => {
|
|
131
|
+
// Arrange
|
|
132
|
+
mockElement.on.click = mockEventHandler
|
|
133
|
+
|
|
134
|
+
// Act
|
|
135
|
+
triggerEventOnUpdate('click', {}, mockElement, mockOptions)
|
|
136
|
+
|
|
137
|
+
// Assert
|
|
138
|
+
expect(mockEventHandler).toHaveBeenCalledWith(
|
|
139
|
+
{},
|
|
140
|
+
mockElement,
|
|
141
|
+
mockElement.state,
|
|
142
|
+
mockElement.context,
|
|
143
|
+
mockOptions
|
|
144
|
+
)
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
describe('event naming', () => {
|
|
149
|
+
test('should handle various event names correctly', () => {
|
|
150
|
+
const eventTests = [
|
|
151
|
+
{ input: 'mouseenter', prop: 'onMouseenter' },
|
|
152
|
+
{ input: 'mouseleave', prop: 'onMouseleave' },
|
|
153
|
+
{ input: 'submit', prop: 'onSubmit' },
|
|
154
|
+
{ input: 'change', prop: 'onChange' }
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
for (const { input, prop } of eventTests) {
|
|
158
|
+
// Arrange
|
|
159
|
+
mockElement.props[prop] = mockEventHandler
|
|
160
|
+
|
|
161
|
+
// Act
|
|
162
|
+
triggerEventOnUpdate(input, mockUpdatedObj, mockElement, mockOptions)
|
|
163
|
+
|
|
164
|
+
// Assert
|
|
165
|
+
expect(mockEventHandler).toHaveBeenCalledWith(
|
|
166
|
+
mockUpdatedObj,
|
|
167
|
+
mockElement,
|
|
168
|
+
mockElement.state,
|
|
169
|
+
mockElement.context,
|
|
170
|
+
mockOptions
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
// Reset for next test
|
|
174
|
+
mockElement.props[prop] = undefined
|
|
175
|
+
mockEventHandler.mockClear()
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
describe('error handling', () => {
|
|
181
|
+
test('should return undefined when no event handler exists', () => {
|
|
182
|
+
// Act
|
|
183
|
+
const result = triggerEventOnUpdate(
|
|
184
|
+
'click',
|
|
185
|
+
mockUpdatedObj,
|
|
186
|
+
mockElement,
|
|
187
|
+
mockOptions
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
// Assert
|
|
191
|
+
expect(result).toBeUndefined()
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
test('should handle thrown error in event handler', () => {
|
|
195
|
+
// Arrange
|
|
196
|
+
const error = new Error('Event handler error')
|
|
197
|
+
mockElement.on.click = jest.fn(() => {
|
|
198
|
+
throw error
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// Act & Assert
|
|
202
|
+
expect(() =>
|
|
203
|
+
triggerEventOnUpdate('click', mockUpdatedObj, mockElement, mockOptions)
|
|
204
|
+
).toThrow('Event handler error')
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
})
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
export const registerFrameListener = el => {
|
|
4
|
+
if (!el || !el.__ref) {
|
|
5
|
+
throw new Error('Element reference is invalid')
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { __ref: ref } = el
|
|
9
|
+
|
|
10
|
+
if (!ref.root) {
|
|
11
|
+
throw new Error('Root reference is invalid')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!ref.root.data) {
|
|
15
|
+
throw new Error('Data are undefined')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { frameListeners } = ref.root.data
|
|
19
|
+
|
|
20
|
+
// Check if frameListeners exists and the element is not already in the Set
|
|
21
|
+
if (frameListeners && !frameListeners.has(el)) {
|
|
22
|
+
frameListeners.add(el) // Add the element to the Set
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const processFrameListeners = (frameListeners) => {
|
|
27
|
+
for (const element of frameListeners) {
|
|
28
|
+
// Cache the handler on first use to avoid repeated property lookups per frame
|
|
29
|
+
if (!element.__ref.__frameHandler) {
|
|
30
|
+
const handler = element.on?.frame || element.onFrame || element.props?.onFrame
|
|
31
|
+
if (handler) element.__ref.__frameHandler = handler
|
|
32
|
+
else {
|
|
33
|
+
frameListeners.delete(element)
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!element.node?.parentNode) {
|
|
39
|
+
frameListeners.delete(element)
|
|
40
|
+
delete element.__ref.__frameHandler
|
|
41
|
+
} else {
|
|
42
|
+
try {
|
|
43
|
+
element.__ref.__frameHandler(element, element.state, element.context)
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.warn(e)
|
|
46
|
+
frameListeners.delete(element)
|
|
47
|
+
delete element.__ref.__frameHandler
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const startFrameLoop = (frameListeners) => {
|
|
54
|
+
if (_frameRunning) return
|
|
55
|
+
_frameRunning = true
|
|
56
|
+
|
|
57
|
+
function requestFrame () {
|
|
58
|
+
if (frameListeners.size === 0) {
|
|
59
|
+
_frameRunning = false
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
processFrameListeners(frameListeners)
|
|
63
|
+
window.requestAnimationFrame(requestFrame)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
window.requestAnimationFrame(requestFrame)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const applyAnimationFrame = element => {
|
|
70
|
+
if (!element) {
|
|
71
|
+
throw new Error('Element is invalid')
|
|
72
|
+
}
|
|
73
|
+
const { on, props, __ref: ref } = element
|
|
74
|
+
if (!ref.root || !ref.root.data) return
|
|
75
|
+
const { frameListeners } = ref.root.data
|
|
76
|
+
|
|
77
|
+
// Register if any of the frame handlers exists
|
|
78
|
+
if (frameListeners && (on?.frame || element.onFrame || props?.onFrame)) {
|
|
79
|
+
registerFrameListener(element)
|
|
80
|
+
startFrameLoop(frameListeners)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let _frameRunning = false
|
|
85
|
+
|
|
86
|
+
export const initAnimationFrame = () => {
|
|
87
|
+
const frameListeners = new Set()
|
|
88
|
+
|
|
89
|
+
startFrameLoop(frameListeners)
|
|
90
|
+
|
|
91
|
+
return frameListeners
|
|
92
|
+
}
|
package/event/can.js
ADDED
package/event/index.js
ADDED
package/event/on.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { DOMQL_EVENTS, isFunction } from '@domql/utils'
|
|
4
|
+
|
|
5
|
+
// Re-export event trigger functions from @domql/utils (moved there to break circular dep)
|
|
6
|
+
export { applyEvent, triggerEventOn, applyEventUpdate, triggerEventOnUpdate } from '@domql/utils'
|
|
7
|
+
|
|
8
|
+
const getOnOrPropsEvent = (param, element) => {
|
|
9
|
+
const onEvent = element.on?.[param]
|
|
10
|
+
if (onEvent) return onEvent
|
|
11
|
+
const props = element.props
|
|
12
|
+
if (!props) return
|
|
13
|
+
const propKey = 'on' + param.charAt(0).toUpperCase() + param.slice(1)
|
|
14
|
+
return props[propKey]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const registerNodeEvent = (param, element, node, options) => {
|
|
18
|
+
const appliedFunction = getOnOrPropsEvent(param, element)
|
|
19
|
+
if (isFunction(appliedFunction)) {
|
|
20
|
+
const { __ref: ref } = element
|
|
21
|
+
if (!ref.__eventListeners) ref.__eventListeners = {}
|
|
22
|
+
|
|
23
|
+
// Remove previous listener for this event to avoid duplicates
|
|
24
|
+
if (ref.__eventListeners[param]) {
|
|
25
|
+
node.removeEventListener(param, ref.__eventListeners[param])
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const handler = event => {
|
|
29
|
+
const { state, context } = element
|
|
30
|
+
const result = appliedFunction.call(
|
|
31
|
+
element,
|
|
32
|
+
event,
|
|
33
|
+
element,
|
|
34
|
+
state,
|
|
35
|
+
context,
|
|
36
|
+
options
|
|
37
|
+
)
|
|
38
|
+
if (result && typeof result.then === 'function') {
|
|
39
|
+
result.catch(() => {})
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
ref.__eventListeners[param] = handler
|
|
44
|
+
node.addEventListener(param, handler)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const applyEventsOnNode = (element, options) => {
|
|
49
|
+
const { node, on, props } = element
|
|
50
|
+
const handled = new Set()
|
|
51
|
+
|
|
52
|
+
// Register events from on: { click: ..., input: ... }
|
|
53
|
+
for (const param in on) {
|
|
54
|
+
if (DOMQL_EVENTS.has(param)) continue
|
|
55
|
+
handled.add(param)
|
|
56
|
+
registerNodeEvent(param, element, node, options)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Also pick up props.onClick, props.onInput, etc.
|
|
60
|
+
if (props) {
|
|
61
|
+
for (const key in props) {
|
|
62
|
+
if (key.length > 2 && key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && isFunction(props[key])) {
|
|
63
|
+
const thirdChar = key[2]
|
|
64
|
+
if (thirdChar !== thirdChar.toUpperCase()) continue
|
|
65
|
+
const eventName = thirdChar.toLowerCase() + key.slice(3)
|
|
66
|
+
if (handled.has(eventName) || DOMQL_EVENTS.has(eventName)) continue
|
|
67
|
+
registerNodeEvent(eventName, element, node, options)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
package/event/store.js
ADDED
package/methods/set.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { merge, overwrite } from '@domql/utils'
|
|
4
|
+
|
|
5
|
+
import { set, reset, updateContent, removeContent } from '../set.js'
|
|
6
|
+
import { update } from '../update.js'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
call,
|
|
10
|
+
error,
|
|
11
|
+
getContext,
|
|
12
|
+
getPath,
|
|
13
|
+
getRef,
|
|
14
|
+
getRoot,
|
|
15
|
+
getRootContext,
|
|
16
|
+
getRootData,
|
|
17
|
+
getRootState,
|
|
18
|
+
keys,
|
|
19
|
+
log,
|
|
20
|
+
lookdown,
|
|
21
|
+
lookdownAll,
|
|
22
|
+
lookup,
|
|
23
|
+
nextElement,
|
|
24
|
+
parse,
|
|
25
|
+
parseDeep,
|
|
26
|
+
previousElement,
|
|
27
|
+
remove,
|
|
28
|
+
setNodeStyles,
|
|
29
|
+
setProps,
|
|
30
|
+
spotByPath,
|
|
31
|
+
variables,
|
|
32
|
+
verbose,
|
|
33
|
+
warn
|
|
34
|
+
} from '@domql/utils/methods'
|
|
35
|
+
|
|
36
|
+
export const addMethods = (element, parent, options = {}) => {
|
|
37
|
+
const proto = {
|
|
38
|
+
set,
|
|
39
|
+
reset,
|
|
40
|
+
update,
|
|
41
|
+
variables,
|
|
42
|
+
remove,
|
|
43
|
+
updateContent,
|
|
44
|
+
removeContent,
|
|
45
|
+
setProps,
|
|
46
|
+
lookup,
|
|
47
|
+
lookdown,
|
|
48
|
+
lookdownAll,
|
|
49
|
+
getRef,
|
|
50
|
+
getPath,
|
|
51
|
+
getRootState,
|
|
52
|
+
getRoot,
|
|
53
|
+
getRootData,
|
|
54
|
+
getRootContext,
|
|
55
|
+
getContext,
|
|
56
|
+
setNodeStyles,
|
|
57
|
+
spotByPath,
|
|
58
|
+
parse,
|
|
59
|
+
parseDeep,
|
|
60
|
+
keys,
|
|
61
|
+
nextElement,
|
|
62
|
+
previousElement,
|
|
63
|
+
log,
|
|
64
|
+
verbose,
|
|
65
|
+
warn,
|
|
66
|
+
error,
|
|
67
|
+
call
|
|
68
|
+
}
|
|
69
|
+
if (element.context.methods) {
|
|
70
|
+
;(options.strict ? merge : overwrite)(proto, element.context.methods)
|
|
71
|
+
}
|
|
72
|
+
Object.setPrototypeOf(element, proto)
|
|
73
|
+
}
|
package/methods/v2.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { isDefined, isFunction, isObjectLike } from '@domql/utils'
|
|
4
|
+
|
|
5
|
+
export const defineSetter = (element, key, get, set) =>
|
|
6
|
+
Object.defineProperty(element, key, { get, set })
|
|
7
|
+
|
|
8
|
+
export const keys = function () {
|
|
9
|
+
const element = this
|
|
10
|
+
const keys = []
|
|
11
|
+
for (const param in element) {
|
|
12
|
+
// if (REGISTRY[param] && !parseFilters.elementKeys.includes(param)) { continue }
|
|
13
|
+
keys.push(param)
|
|
14
|
+
}
|
|
15
|
+
return keys
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const parse = function (excl = []) {
|
|
19
|
+
const element = this
|
|
20
|
+
const obj = {}
|
|
21
|
+
const keyList = keys.call(element)
|
|
22
|
+
keyList.forEach(v => {
|
|
23
|
+
if (excl.includes(v)) return
|
|
24
|
+
let val = element[v]
|
|
25
|
+
if (v === 'state') {
|
|
26
|
+
if (element.__ref && element.__ref.__hasRootState) return
|
|
27
|
+
if (isFunction(val && val.parse)) val = val.parse()
|
|
28
|
+
} else if (v === 'props') {
|
|
29
|
+
const { __element, update, ...props } = element[v]
|
|
30
|
+
obj[v] = props
|
|
31
|
+
} else if (isDefined(val)) obj[v] = val
|
|
32
|
+
})
|
|
33
|
+
return obj
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const parseDeep = function (excl = []) {
|
|
37
|
+
const element = this
|
|
38
|
+
const obj = parse.call(element, excl)
|
|
39
|
+
for (const v in obj) {
|
|
40
|
+
if (excl.includes(v)) return
|
|
41
|
+
if (isObjectLike(obj[v])) {
|
|
42
|
+
obj[v] = parseDeep.call(obj[v], excl)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return obj
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const log = function (...args) {
|
|
49
|
+
const element = this
|
|
50
|
+
const { __ref } = element
|
|
51
|
+
console.group(element.key)
|
|
52
|
+
if (args.length) {
|
|
53
|
+
args.forEach(v => console.log(`%c${v}:\n`, 'font-weight: bold', element[v]))
|
|
54
|
+
} else {
|
|
55
|
+
console.log(__ref?.path)
|
|
56
|
+
const keys = element.keys()
|
|
57
|
+
keys.forEach(v => console.log(`%c${v}:\n`, 'font-weight: bold', element[v]))
|
|
58
|
+
}
|
|
59
|
+
console.groupEnd(element.key)
|
|
60
|
+
return element
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const nextElement = function () {
|
|
64
|
+
const element = this
|
|
65
|
+
const { key, parent } = element
|
|
66
|
+
const { __children } = parent.__ref
|
|
67
|
+
|
|
68
|
+
const currentIndex = __children.indexOf(key)
|
|
69
|
+
const nextChild = __children[currentIndex + 1]
|
|
70
|
+
|
|
71
|
+
return parent[nextChild]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const previousElement = function (el) {
|
|
75
|
+
const element = el || this
|
|
76
|
+
const { key, parent } = element
|
|
77
|
+
const { __children } = parent.__ref
|
|
78
|
+
|
|
79
|
+
if (!__children) return
|
|
80
|
+
|
|
81
|
+
const currentIndex = __children.indexOf(key)
|
|
82
|
+
return parent[__children[currentIndex - 1]]
|
|
83
|
+
}
|
package/mixins/attr.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { deepMerge, exec, isNot } from '@domql/utils'
|
|
4
|
+
import { report } from '@domql/report'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Recursively add attributes to a DOM node
|
|
8
|
+
*/
|
|
9
|
+
export function attr (params, element, node) {
|
|
10
|
+
const { __ref: ref, props } = element
|
|
11
|
+
const { __attr } = ref
|
|
12
|
+
if (isNot(params)('object')) report('HTMLInvalidAttr', params)
|
|
13
|
+
if (params) {
|
|
14
|
+
const attrs = exec(params, element)
|
|
15
|
+
if (props.attr) deepMerge(attrs, props.attr)
|
|
16
|
+
for (const attr in attrs) {
|
|
17
|
+
const val = exec(attrs[attr], element)
|
|
18
|
+
if (val === __attr[attr]) continue
|
|
19
|
+
if (
|
|
20
|
+
val !== false &&
|
|
21
|
+
val !== undefined &&
|
|
22
|
+
val !== null &&
|
|
23
|
+
node.setAttribute
|
|
24
|
+
) {
|
|
25
|
+
node.setAttribute(attr, val)
|
|
26
|
+
} else if (node.removeAttribute) node.removeAttribute(attr)
|
|
27
|
+
__attr[attr] = val
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default attr
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { exec, isObject, isString } from '@domql/utils'
|
|
4
|
+
|
|
5
|
+
export const assignKeyAsClassname = element => {
|
|
6
|
+
const { key } = element
|
|
7
|
+
if (element.classlist === true) element.classlist = key
|
|
8
|
+
else if (
|
|
9
|
+
!element.classlist &&
|
|
10
|
+
typeof key === 'string' &&
|
|
11
|
+
key.charAt(0) === '_' &&
|
|
12
|
+
key.charAt(1) !== '_'
|
|
13
|
+
) {
|
|
14
|
+
element.classlist = key.slice(1)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// stringifies class object
|
|
19
|
+
export const classify = (obj, element) => {
|
|
20
|
+
let className = ''
|
|
21
|
+
for (const item in obj) {
|
|
22
|
+
const param = obj[item]
|
|
23
|
+
if (typeof param === 'boolean' && param) className += ` ${item}`
|
|
24
|
+
else if (typeof param === 'string') className += ` ${param}`
|
|
25
|
+
else if (typeof param === 'function') {
|
|
26
|
+
className += ` ${exec(param, element)}`
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return className
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const classList = (params, element) => {
|
|
33
|
+
if (!params) return
|
|
34
|
+
const { key } = element
|
|
35
|
+
if (params === true) params = element.classlist = { key }
|
|
36
|
+
if (isString(params)) params = element.classlist = { default: params }
|
|
37
|
+
if (isObject(params)) params = classify(params, element)
|
|
38
|
+
// TODO: fails on string
|
|
39
|
+
const className = params.replace(/\s+/g, ' ').trim()
|
|
40
|
+
return className
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// LEGACY (still needed in old domql)
|
|
44
|
+
export const applyClassListOnNode = (params, element, node) => {
|
|
45
|
+
const className = classList(params, element)
|
|
46
|
+
const { __ref } = element
|
|
47
|
+
if (className === __ref.__className) return className
|
|
48
|
+
__ref.__className = className
|
|
49
|
+
// Use setAttribute for universal SVG/HTML compatibility
|
|
50
|
+
if (node && typeof node.setAttribute === 'function') {
|
|
51
|
+
node.setAttribute('class', className)
|
|
52
|
+
} else if (node) {
|
|
53
|
+
try { node.className = className } catch (e) { /* SVG element */ }
|
|
54
|
+
}
|
|
55
|
+
return className
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function applyClasslist (params, element, node) {
|
|
59
|
+
applyClassListOnNode(params, element, node)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default applyClasslist
|