@symbo.ls/element 3.14.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/src/update.js ADDED
@@ -0,0 +1,124 @@
1
+ 'use strict'
2
+
3
+ import { batch, isStore } from '@symbo.ls/signal'
4
+ import { REGISTRY } from './registry.js'
5
+ import { create } from './create.js'
6
+ import { runPluginHook } from '@symbo.ls/utils/function.js'
7
+
8
+ export function update(params = {}, opts = {}) {
9
+ const element = this
10
+ const ref = element.__ref
11
+ if (!ref) return
12
+
13
+ // Normalize string/number shorthand
14
+ if (typeof params === 'string' || typeof params === 'number') {
15
+ params = { text: params }
16
+ }
17
+
18
+ // Fire onBeforeUpdate (v3 syntax — directly on element)
19
+ if (!opts.preventListeners && typeof element.onBeforeUpdate === 'function') {
20
+ const result = element.onBeforeUpdate(element, element.state, element.context, opts)
21
+ if (result === false) return element
22
+ }
23
+
24
+ batch(() => {
25
+ // Update state if provided
26
+ if (params.state && isStore(element.state)) {
27
+ element.state.update(params.state)
28
+ }
29
+
30
+ // Update text
31
+ if (params.text !== undefined) {
32
+ element.text = params.text
33
+ if (!ref.__exec.text && element.node) {
34
+ if (element.tag === 'string') {
35
+ element.node.nodeValue = params.text ?? ''
36
+ } else {
37
+ element.node.textContent = params.text ?? ''
38
+ }
39
+ }
40
+ }
41
+
42
+ // Update html
43
+ if (params.html !== undefined) {
44
+ element.html = params.html
45
+ if (!ref.__exec.html && element.node) {
46
+ element.node.innerHTML = params.html
47
+ }
48
+ }
49
+
50
+ // Update style
51
+ if (params.style && element.node?.style) {
52
+ if (!ref.__exec.style) {
53
+ for (const prop in params.style) {
54
+ element.node.style[prop] = params.style[prop]
55
+ }
56
+ }
57
+ if (!element.style) element.style = {}
58
+ Object.assign(element.style, params.style)
59
+ }
60
+
61
+ if (params.attr && element.node?.setAttribute) {
62
+ for (const key in params.attr) {
63
+ const val = params.attr[key]
64
+ if (val == null || val === false) {
65
+ element.node.removeAttribute(key)
66
+ } else if (typeof val !== 'function' && typeof val !== 'object') {
67
+ element.node.setAttribute(key, val === true ? '' : val)
68
+ }
69
+ }
70
+ if (!element.attr) element.attr = {}
71
+ Object.assign(element.attr, params.attr)
72
+ }
73
+
74
+ if (params.data && element.node?.dataset) {
75
+ for (const key in params.data) {
76
+ element.node.dataset[key] = params.data[key]
77
+ }
78
+ if (!element.data) element.data = {}
79
+ Object.assign(element.data, params.data)
80
+ }
81
+
82
+ // Handle child element updates and property changes
83
+ for (const param in params) {
84
+ if (REGISTRY.has(param)) continue
85
+ if (param === 'state') continue
86
+
87
+ const childParams = params[param]
88
+ const child = element[param]
89
+
90
+ if (child && child.__ref && typeof child.update === 'function') {
91
+ // Existing child element — recursive update
92
+ child.update(childParams, opts)
93
+ } else if (typeof childParams === 'object' && childParams !== null) {
94
+ const firstChar = param.charCodeAt(0)
95
+ if (firstChar >= 65 && firstChar <= 90) {
96
+ // Uppercase key with object value — create new child element
97
+ create(childParams, element, param, opts)
98
+ }
99
+ } else if (param !== 'text' && param !== 'html' && param !== 'style' && param !== 'attr' && param !== 'data') {
100
+ // Primitive property update (e.g. name, icon, value)
101
+ // Set the property — if it has a reactive effect, the effect will re-run
102
+ element[param] = childParams
103
+ }
104
+ }
105
+ })
106
+
107
+ // Re-evaluate conditionals (.prop / !prop). Signal-driven predicates already
108
+ // re-fired via createEffect; this catches imperative updates to non-signal
109
+ // predicates (e.g. element.update({ isActive: true }) where isActive is a
110
+ // plain element prop, not a state field).
111
+ if (element.node && ref.__conditionalRunners) {
112
+ for (const run of ref.__conditionalRunners) run()
113
+ }
114
+
115
+ runPluginHook('update', element)
116
+
117
+ if (!opts.preventListeners && typeof element.onUpdate === 'function') {
118
+ element.onUpdate(element, element.state, element.context, opts)
119
+ }
120
+
121
+ return element
122
+ }
123
+
124
+ export default update