simplyflow 0.7.5 → 0.7.7
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/dist/simply.flow.js +113 -107
- package/dist/simply.flow.min.js +1 -1
- package/dist/simply.flow.min.js.map +4 -4
- package/package.json +3 -5
- package/src/bind.render.mjs +1 -1
- package/src/dom.mjs +99 -0
- package/src/flow.mjs +3 -1
- package/src/model.mjs +1 -1
- package/src/state.mjs +7 -105
package/src/dom.mjs
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { signals, signal as stateSignal, notifyGet, notifySet, makeContext } from './state.mjs'
|
|
2
|
+
|
|
3
|
+
const domSignalHandler = {
|
|
4
|
+
get: (target, property, receiver) => {
|
|
5
|
+
if (property===Symbol.xRay) {
|
|
6
|
+
return target // don't notifyGet here, this is only called by set
|
|
7
|
+
}
|
|
8
|
+
if (property===Symbol.Signal) {
|
|
9
|
+
return true
|
|
10
|
+
}
|
|
11
|
+
const value = target?.[property]
|
|
12
|
+
notifyGet(receiver, property)
|
|
13
|
+
if (typeof value === 'function') {
|
|
14
|
+
return value.bind(target) // make sure element functions are not linked to the proxy
|
|
15
|
+
}
|
|
16
|
+
if (value && typeof value == 'object') {
|
|
17
|
+
return stateSignal(value)
|
|
18
|
+
}
|
|
19
|
+
return value
|
|
20
|
+
},
|
|
21
|
+
has: (target, property) => {
|
|
22
|
+
let receiver = signals.get(target)
|
|
23
|
+
if (receiver) {
|
|
24
|
+
notifyGet(receiver, property)
|
|
25
|
+
}
|
|
26
|
+
return Object.hasOwn(target, property)
|
|
27
|
+
},
|
|
28
|
+
ownKeys: (target) => {
|
|
29
|
+
let receiver = signals.get(target) // receiver is not part of the trap arguments, so retrieve it here
|
|
30
|
+
if (receiver) {
|
|
31
|
+
notifyGet(receiver, iterate)
|
|
32
|
+
}
|
|
33
|
+
return Reflect.ownKeys(target)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function signal(el) {
|
|
38
|
+
if (el[Symbol.xRay]) {
|
|
39
|
+
return el
|
|
40
|
+
}
|
|
41
|
+
if (!signals.has(el)) {
|
|
42
|
+
signals.set(el, new Proxy(el, domSignalHandler))
|
|
43
|
+
domListen(el, signals.get(el))
|
|
44
|
+
}
|
|
45
|
+
return signals.get(el)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const observers = new WeakMap()
|
|
49
|
+
|
|
50
|
+
function domListen(el, signal) {
|
|
51
|
+
let oldContentHTML = el.innerHTML
|
|
52
|
+
let oldContentText = el.innerText
|
|
53
|
+
if (!observers.has(el)) {
|
|
54
|
+
const observer = new MutationObserver((mutationList, observer) => {
|
|
55
|
+
// collect changes
|
|
56
|
+
const changes = {}
|
|
57
|
+
for (const mutation of mutationList) {
|
|
58
|
+
if (mutation.type==='attributes') {
|
|
59
|
+
// check if any listeners for each attribute
|
|
60
|
+
changes[mutation.attributeName] = mutation.attributeOldValue
|
|
61
|
+
} else if (mutation.type==='subtree' || mutation.type==='characterData') {
|
|
62
|
+
// change on innerHTML/innerText
|
|
63
|
+
if (el.innerHTML != oldContentHTML) {
|
|
64
|
+
changes.innerHTML = oldContentHTML
|
|
65
|
+
oldContentHTML = el.innerHTML
|
|
66
|
+
}
|
|
67
|
+
if (el.innerText != oldContentText) {
|
|
68
|
+
changes.innerText = oldContentText
|
|
69
|
+
oldContentText = el.innerText
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const prop in changes) {
|
|
74
|
+
notifySet(signal, makeContext(prop, { was: changes[prop], now: el[prop] }))
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
observer.observe(el, {
|
|
78
|
+
characterData: true,
|
|
79
|
+
subtree: true,
|
|
80
|
+
attributes: true,
|
|
81
|
+
attributesOldValue: true
|
|
82
|
+
})
|
|
83
|
+
observers.set(el, observer)
|
|
84
|
+
//@TODO: unregister the observer when el is removed from the dom (after a timeout)
|
|
85
|
+
if (el.matches('input, textarea, select')) {
|
|
86
|
+
let prevValue = el.value
|
|
87
|
+
el.addEventListener('change', (evt) => {
|
|
88
|
+
notifySet(signal, makeContext('value', { was: prevValue, now: el.value }))
|
|
89
|
+
prevValue = el.value
|
|
90
|
+
})
|
|
91
|
+
if (el.matches('input, textarea')) {
|
|
92
|
+
el.addEventListener('input', (evt) => {
|
|
93
|
+
notifySet(signal, makeContext('value', { was: prevValue, now: el.value }))
|
|
94
|
+
prevValue = el.value
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
package/src/flow.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { bind } from './bind.mjs'
|
|
|
2
2
|
import * as model from './model.mjs'
|
|
3
3
|
import * as state from './state.mjs'
|
|
4
4
|
import './render.mjs'
|
|
5
|
+
import * as dom from './dom.mjs'
|
|
5
6
|
|
|
6
7
|
if (!globalThis.simply) {
|
|
7
8
|
globalThis.simply = {}
|
|
@@ -9,7 +10,8 @@ if (!globalThis.simply) {
|
|
|
9
10
|
Object.assign(globalThis.simply, {
|
|
10
11
|
bind,
|
|
11
12
|
flow: model,
|
|
12
|
-
state
|
|
13
|
+
state,
|
|
14
|
+
dom
|
|
13
15
|
})
|
|
14
16
|
|
|
15
17
|
export default globalThis.simply
|
package/src/model.mjs
CHANGED
|
@@ -198,7 +198,7 @@ export function columns(options={}) {
|
|
|
198
198
|
let result = {}
|
|
199
199
|
for (let key of Object.keys(this.state.options.columns)) {
|
|
200
200
|
if (!this.state.options.columns[key]?.hidden) {
|
|
201
|
-
result[key] = input[key]
|
|
201
|
+
result[key] = input[key] ?? null
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
return result
|
package/src/state.mjs
CHANGED
|
@@ -68,7 +68,7 @@ const signalHandler = {
|
|
|
68
68
|
set: (target, property, value, receiver) => {
|
|
69
69
|
value = value?.[Symbol.xRay] || value // unwraps signal
|
|
70
70
|
//FIXME: if value contains child objects, these may be signals as well... so do this recursively
|
|
71
|
-
unwrap(value)
|
|
71
|
+
//unwrap(value)
|
|
72
72
|
let current = target[property]
|
|
73
73
|
if (current!==value) {
|
|
74
74
|
target[property] = value
|
|
@@ -116,14 +116,14 @@ const signalHandler = {
|
|
|
116
116
|
* Makes sure that a given object or function always uses the same
|
|
117
117
|
* signal
|
|
118
118
|
*/
|
|
119
|
-
const signals = new WeakMap()
|
|
119
|
+
export const signals = new WeakMap()
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
122
|
* Creates a new signal proxy of the given object, that intercepts get/has and set/delete
|
|
123
123
|
* to allow reactive functions to be triggered when signal values change.
|
|
124
124
|
*/
|
|
125
125
|
export function signal(v) {
|
|
126
|
-
unwrap(v)
|
|
126
|
+
//unwrap(v)
|
|
127
127
|
if (v[Symbol.Signal]) { // avoid wrapping a Signal inside a Signal
|
|
128
128
|
let target = v[Symbol.xRay]
|
|
129
129
|
if (!signals.has(target)) {
|
|
@@ -136,52 +136,6 @@ export function signal(v) {
|
|
|
136
136
|
return signals.get(v)
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
const domSignalHandler = {
|
|
140
|
-
get: (target, property, receiver) => {
|
|
141
|
-
if (property===Symbol.xRay) {
|
|
142
|
-
return target // don't notifyGet here, this is only called by set
|
|
143
|
-
}
|
|
144
|
-
if (property===Symbol.Signal) {
|
|
145
|
-
return true
|
|
146
|
-
}
|
|
147
|
-
const value = target?.[property]
|
|
148
|
-
domListen(target, receiver)
|
|
149
|
-
notifyGet(receiver, property)
|
|
150
|
-
if (typeof value === 'function') {
|
|
151
|
-
return value.bind(target) // make sure element functions are not linked to the proxy
|
|
152
|
-
}
|
|
153
|
-
if (value && typeof value == 'object') {
|
|
154
|
-
return signal(value)
|
|
155
|
-
}
|
|
156
|
-
return value
|
|
157
|
-
},
|
|
158
|
-
has: (target, property) => {
|
|
159
|
-
let receiver = signals.get(target)
|
|
160
|
-
if (receiver) {
|
|
161
|
-
domListen(target, receiver)
|
|
162
|
-
notifyGet(receiver, property)
|
|
163
|
-
}
|
|
164
|
-
return Object.hasOwn(target, property)
|
|
165
|
-
},
|
|
166
|
-
ownKeys: (target) => {
|
|
167
|
-
let receiver = signals.get(target) // receiver is not part of the trap arguments, so retrieve it here
|
|
168
|
-
if (receiver) {
|
|
169
|
-
domListen(target, receiver)
|
|
170
|
-
notifyGet(receiver, iterate)
|
|
171
|
-
}
|
|
172
|
-
return Reflect.ownKeys(target)
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export function domSignal(el) {
|
|
177
|
-
if (el[Symbol.xRay]) {
|
|
178
|
-
return el
|
|
179
|
-
}
|
|
180
|
-
if (!signals.has(el)) {
|
|
181
|
-
signals.set(el, new Proxy(el, domSignalHandler))
|
|
182
|
-
}
|
|
183
|
-
return signals.get(el)
|
|
184
|
-
}
|
|
185
139
|
|
|
186
140
|
let tracers = []
|
|
187
141
|
let tracing = false
|
|
@@ -254,7 +208,7 @@ let batchMode = 0
|
|
|
254
208
|
* Triggers any reactor function that depends on this signal
|
|
255
209
|
* to re-compute its values
|
|
256
210
|
*/
|
|
257
|
-
function notifySet(self, context={}) {
|
|
211
|
+
export function notifySet(self, context={}) {
|
|
258
212
|
if (disableTracking) {
|
|
259
213
|
return
|
|
260
214
|
}
|
|
@@ -287,60 +241,8 @@ function notifySet(self, context={}) {
|
|
|
287
241
|
}
|
|
288
242
|
}
|
|
289
243
|
|
|
290
|
-
const observers = new WeakMap()
|
|
291
|
-
|
|
292
|
-
function domListen(el, signal) {
|
|
293
|
-
let oldContentHTML = el.innerHTML
|
|
294
|
-
let oldContentText = el.innerText
|
|
295
|
-
if (!observers.has(el)) {
|
|
296
|
-
const observer = new MutationObserver((mutationList, observer) => {
|
|
297
|
-
// collect changes
|
|
298
|
-
const changes = {}
|
|
299
|
-
for (const mutation of mutationList) {
|
|
300
|
-
if (mutation.type==='attributes') {
|
|
301
|
-
// check if any listeners for each attribute
|
|
302
|
-
changes[mutation.attributeName] = mutation.attributeOldValue
|
|
303
|
-
} else if (mutation.type==='subtree' || mutation.type==='characterData') {
|
|
304
|
-
// change on innerHTML/innerText
|
|
305
|
-
if (el.innerHTML != oldContentHTML) {
|
|
306
|
-
changes.innerHTML = oldContentHTML
|
|
307
|
-
oldContentHTML = el.innerHTML
|
|
308
|
-
}
|
|
309
|
-
if (el.innerText != oldContentText) {
|
|
310
|
-
changes.innerText = oldContentText
|
|
311
|
-
oldContentText = el.innerText
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
for (const prop in changes) {
|
|
316
|
-
notifySet(signal, makeContext(prop, { was: changes[prop], now: el[prop] }))
|
|
317
|
-
}
|
|
318
|
-
})
|
|
319
|
-
observer.observe(el, {
|
|
320
|
-
characterData: true,
|
|
321
|
-
subtree: true,
|
|
322
|
-
attributes: true,
|
|
323
|
-
attributesOldValue: true
|
|
324
|
-
})
|
|
325
|
-
observers.set(el, observer)
|
|
326
|
-
//@TODO: unregister the observer when el is removed from the dom (after a timeout)
|
|
327
|
-
if (el.matches('input, textarea, select')) {
|
|
328
|
-
let prevValue = el.value
|
|
329
|
-
el.addEventListener('change', (evt) => {
|
|
330
|
-
notifySet(signal, makeContext('value', { was: prevValue, now: el.value }))
|
|
331
|
-
prevValue = el.value
|
|
332
|
-
})
|
|
333
|
-
if (el.matches('input, textarea')) {
|
|
334
|
-
el.addEventListener('input', (evt) => {
|
|
335
|
-
notifySet(signal, makeContext('value', { was: prevValue, now: el.value }))
|
|
336
|
-
prevValue = el.value
|
|
337
|
-
})
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
244
|
|
|
343
|
-
function makeContext(property, change) {
|
|
245
|
+
export function makeContext(property, change) {
|
|
344
246
|
let context = new Map()
|
|
345
247
|
if (typeof property === 'object') {
|
|
346
248
|
for (let prop in property) {
|
|
@@ -374,7 +276,7 @@ function clearContext(listener) {
|
|
|
374
276
|
* then it adds the current reactor (top of this stack) to its
|
|
375
277
|
* listeners. These are later called if this property changes
|
|
376
278
|
*/
|
|
377
|
-
function notifyGet(self, property) {
|
|
279
|
+
export function notifyGet(self, property) {
|
|
378
280
|
if (disableTracking) {
|
|
379
281
|
return
|
|
380
282
|
}
|
|
@@ -734,7 +636,7 @@ export function untracked(fn) {
|
|
|
734
636
|
let seen = new WeakMap()
|
|
735
637
|
|
|
736
638
|
function innerUnwrap(ob) {
|
|
737
|
-
if (!ob || typeof ob!=='object' || seen.has(ob)) {
|
|
639
|
+
if (!ob || typeof ob!=='object' || ob instanceof HTMLElement || seen.has(ob)) {
|
|
738
640
|
return
|
|
739
641
|
}
|
|
740
642
|
seen.set(ob, true)
|