@muze-labs/simplyflow 0.9.0 → 0.10.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/README.md +72 -16
- package/dist/simply.flow.js +458 -298
- package/dist/simply.flow.min.js +1 -1
- package/dist/simply.flow.min.js.map +4 -4
- package/package.json +29 -42
- package/src/action.mjs +1 -64
- package/src/app.mjs +1 -282
- package/src/behavior.mjs +1 -121
- package/src/bind-render.mjs +1 -0
- package/src/bind-transformers.mjs +1 -0
- package/src/bind.mjs +1 -522
- package/src/command.mjs +1 -225
- package/src/dom.mjs +1 -274
- package/src/highlight.mjs +1 -11
- package/src/include.mjs +1 -239
- package/src/{flow.mjs → index.mjs} +13 -13
- package/src/model.mjs +1 -290
- package/src/path.mjs +1 -47
- package/src/route.mjs +1 -418
- package/src/shortcut.mjs +1 -146
- package/src/state.mjs +1 -1347
- package/src/suggest.mjs +1 -68
- package/src/symbols.mjs +1 -9
- package/MUZE_ALIGNMENT.md +0 -118
- package/src/bind.render.mjs +0 -694
- package/src/bind.transformers.mjs +0 -25
package/src/command.mjs
CHANGED
|
@@ -1,225 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import path from './path.mjs'
|
|
3
|
-
|
|
4
|
-
const commandState = new WeakMap()
|
|
5
|
-
const COMMAND_OPTIONS = [
|
|
6
|
-
'commands',
|
|
7
|
-
'handlers',
|
|
8
|
-
'app',
|
|
9
|
-
'container'
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
class SimplyCommands {
|
|
13
|
-
constructor(options={}) {
|
|
14
|
-
if (!options.app) {
|
|
15
|
-
options.app = {}
|
|
16
|
-
}
|
|
17
|
-
if (!options.app.container) {
|
|
18
|
-
options.app.container = document.body
|
|
19
|
-
}
|
|
20
|
-
this.app = options.app
|
|
21
|
-
this.$handlers = options.handlers || defaultHandlers
|
|
22
|
-
if (options.commands) {
|
|
23
|
-
Object.assign(this, options.commands)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const commandHandler = (evt) => {
|
|
27
|
-
const command = getCommand(evt, this.$handlers, this.app)
|
|
28
|
-
if (!command) {
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
if (!this[command.name]) {
|
|
32
|
-
warnUnknownCommand(this, command.name, command.source)
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
const shouldContinue = this[command.name].call(options.app, command.source, command.value, evt)
|
|
36
|
-
if (shouldContinue!==true) {
|
|
37
|
-
evt.preventDefault()
|
|
38
|
-
evt.stopPropagation()
|
|
39
|
-
return false
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const container = options.app.container
|
|
44
|
-
container.addEventListener('click', commandHandler)
|
|
45
|
-
container.addEventListener('submit', commandHandler)
|
|
46
|
-
container.addEventListener('change', commandHandler)
|
|
47
|
-
container.addEventListener('input', commandHandler)
|
|
48
|
-
commandState.set(this, { container, commandHandler })
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
call(command, el, value, event) {
|
|
52
|
-
if (!this[command]) {
|
|
53
|
-
warnUnknownCommand(this, command, el)
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
return this[command].call(this.app, el, value, event)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
appendHandler(handler) {
|
|
60
|
-
this.$handlers.push(handler)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
prependHandler(handler) {
|
|
64
|
-
this.$handlers.unshift(handler)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function commands(options={}) {
|
|
69
|
-
return new SimplyCommands(options)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function destroyCommands(commandApi)
|
|
73
|
-
{
|
|
74
|
-
const state = commandState.get(commandApi)
|
|
75
|
-
if (!state) {
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
state.container.removeEventListener('click', state.commandHandler)
|
|
79
|
-
state.container.removeEventListener('submit', state.commandHandler)
|
|
80
|
-
state.container.removeEventListener('change', state.commandHandler)
|
|
81
|
-
state.container.removeEventListener('input', state.commandHandler)
|
|
82
|
-
commandState.delete(commandApi)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function getCommand(evt, handlers, app) {
|
|
86
|
-
var el = evt.target.closest('[data-simply-command]')
|
|
87
|
-
if (el) {
|
|
88
|
-
for (let handler of handlers) {
|
|
89
|
-
if (el.matches(handler.match)) {
|
|
90
|
-
if (handler.check(el, evt)) {
|
|
91
|
-
return {
|
|
92
|
-
name: el.dataset.simplyCommand,
|
|
93
|
-
source: el,
|
|
94
|
-
value: handler.get(el, app)
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return null
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return null
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
function getConfiguredCommandValue(el, app)
|
|
106
|
-
{
|
|
107
|
-
const pathAttribute = 'simplyValuePath'
|
|
108
|
-
if (Object.hasOwn(el.dataset, pathAttribute)) {
|
|
109
|
-
return {
|
|
110
|
-
found: true,
|
|
111
|
-
value: path.get(app?.data, el.dataset[pathAttribute])
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
if (Object.hasOwn(el.dataset, 'simplyValue')) {
|
|
115
|
-
return { found: true, value: el.dataset.simplyValue }
|
|
116
|
-
}
|
|
117
|
-
return { found: false, value: undefined }
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const defaultHandlers = [
|
|
121
|
-
{
|
|
122
|
-
match: 'input,select,textarea',
|
|
123
|
-
get: function(el, app) {
|
|
124
|
-
const configuredValue = getConfiguredCommandValue(el, app)
|
|
125
|
-
if (configuredValue.found) {
|
|
126
|
-
return configuredValue.value
|
|
127
|
-
}
|
|
128
|
-
if (el.tagName==='SELECT' && el.multiple) {
|
|
129
|
-
let values = []
|
|
130
|
-
for (let option of el.options) {
|
|
131
|
-
if (option.selected) {
|
|
132
|
-
values.push(option.value)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return values
|
|
136
|
-
}
|
|
137
|
-
return el.value
|
|
138
|
-
},
|
|
139
|
-
check: function(el, evt) {
|
|
140
|
-
return evt.type=='change' || (el.dataset.simplyImmediate && evt.type=='input')
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
match: 'a,button',
|
|
145
|
-
get: function(el, app) {
|
|
146
|
-
const configuredValue = getConfiguredCommandValue(el, app)
|
|
147
|
-
if (configuredValue.found) {
|
|
148
|
-
return configuredValue.value
|
|
149
|
-
}
|
|
150
|
-
return el.href || el.value
|
|
151
|
-
},
|
|
152
|
-
check: function(el,evt) {
|
|
153
|
-
return evt.type=='click' && evt.ctrlKey==false && evt.button==0
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
match: 'form',
|
|
158
|
-
get: function(el) {
|
|
159
|
-
let data = {}
|
|
160
|
-
for (let input of Array.from(el.elements)) {
|
|
161
|
-
if (input.tagName=='INPUT'
|
|
162
|
-
&& (input.type=='checkbox' || input.type=='radio')
|
|
163
|
-
) {
|
|
164
|
-
if (!input.checked) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
if (data[input.name] && !Array.isArray(data[input.name])) {
|
|
169
|
-
data[input.name] = [data[input.name]]
|
|
170
|
-
}
|
|
171
|
-
if (Array.isArray(data[input.name])) {
|
|
172
|
-
data[input.name].push(input.value)
|
|
173
|
-
} else {
|
|
174
|
-
data[input.name] = input.value
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return data
|
|
178
|
-
},
|
|
179
|
-
check: function(el,evt) {
|
|
180
|
-
return evt.type=='submit'
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
match: '*',
|
|
185
|
-
get: function(el, app) {
|
|
186
|
-
return getConfiguredCommandValue(el, app).value
|
|
187
|
-
},
|
|
188
|
-
check: function(el, evt) {
|
|
189
|
-
return evt.type=='click' && evt.ctrlKey==false && evt.button==0
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
]
|
|
193
|
-
|
|
194
|
-
const unknownCommandWarnings = new WeakMap()
|
|
195
|
-
|
|
196
|
-
function warnUnknownCommand(commands, command, source)
|
|
197
|
-
{
|
|
198
|
-
let warned = unknownCommandWarnings.get(commands)
|
|
199
|
-
if (!warned) {
|
|
200
|
-
warned = new Set()
|
|
201
|
-
unknownCommandWarnings.set(commands, warned)
|
|
202
|
-
}
|
|
203
|
-
if (warned.has(command)) {
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
warned.add(command)
|
|
207
|
-
|
|
208
|
-
const suggestion = closest(command, commandNames(commands))
|
|
209
|
-
const suffix = suggestion ? `. Did you mean "${suggestion}"?` : ''
|
|
210
|
-
if (source) {
|
|
211
|
-
console.warn(`simplyflow/command: unknown command "${command}"${suffix}`, { cause: source })
|
|
212
|
-
} else {
|
|
213
|
-
console.warn(`simplyflow/command: unknown command "${command}"${suffix}`)
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function commandNames(commands)
|
|
218
|
-
{
|
|
219
|
-
return Object.keys(commands).filter(command => {
|
|
220
|
-
return !command.startsWith('$') &&
|
|
221
|
-
!COMMAND_OPTIONS.includes(command) &&
|
|
222
|
-
typeof commands[command] === 'function'
|
|
223
|
-
})
|
|
224
|
-
}
|
|
225
|
-
|
|
1
|
+
export * from '@muze-labs/simplyflow-app/command'
|
package/src/dom.mjs
CHANGED
|
@@ -1,274 +1 @@
|
|
|
1
|
-
|
|
2
|
-
throttledEffect, untracked, batch } from './state.mjs'
|
|
3
|
-
import { getValueByPath } from './bind.mjs'
|
|
4
|
-
import { setValueByPath, getProperties } from './bind.render.mjs'
|
|
5
|
-
import { DEP } from './symbols.mjs'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Tracks element => signal mapping so that each element only has one signal
|
|
9
|
-
*/
|
|
10
|
-
const domSignals = new WeakMap()
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Tracks element => mutationObservers
|
|
14
|
-
*/
|
|
15
|
-
const observers = new WeakMap()
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* A dom signal is a Proxy, to track access to properties
|
|
19
|
-
*/
|
|
20
|
-
const domSignalHandler = {
|
|
21
|
-
get: (target, property, receiver) => {
|
|
22
|
-
const value = target?.[property]
|
|
23
|
-
notifyGet(receiver, property)
|
|
24
|
-
if (typeof value === 'function') {
|
|
25
|
-
return value.bind(target) // make sure element functions are not linked to the proxy
|
|
26
|
-
}
|
|
27
|
-
if (value && typeof value == 'object') {
|
|
28
|
-
return stateSignal(value)
|
|
29
|
-
}
|
|
30
|
-
return value
|
|
31
|
-
},
|
|
32
|
-
set: (target, property, value, receiver) => {
|
|
33
|
-
const current = target[property]
|
|
34
|
-
target[property] = value
|
|
35
|
-
const now = target[property]
|
|
36
|
-
if (!Object.is(current, now)) {
|
|
37
|
-
notifySet(receiver, makeContext(property, { was: current, now }))
|
|
38
|
-
}
|
|
39
|
-
return true
|
|
40
|
-
},
|
|
41
|
-
has: (target, property) => {
|
|
42
|
-
const receiver = getSignal(target)
|
|
43
|
-
if (receiver) {
|
|
44
|
-
notifyGet(receiver, property)
|
|
45
|
-
}
|
|
46
|
-
return Reflect.has(target, property)
|
|
47
|
-
},
|
|
48
|
-
ownKeys: (target) => {
|
|
49
|
-
// The ownKeys trap has no receiver argument. Recover the stable signal
|
|
50
|
-
// proxy so Object.keys(domSignal) can track DOM key iteration.
|
|
51
|
-
const receiver = getSignal(target)
|
|
52
|
-
if (receiver) {
|
|
53
|
-
notifyGet(receiver, DEP.ITERATE)
|
|
54
|
-
}
|
|
55
|
-
return Reflect.ownKeys(target)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* This function returns a dom signal. Using this in an effect() function
|
|
61
|
-
* will automatically trigger the effect if a property of the dom signal
|
|
62
|
-
* changes.
|
|
63
|
-
* Valid options are any of the mutationObserver options, like characterData, subtree, etc.
|
|
64
|
-
* @param HTMLElement el
|
|
65
|
-
* @param Object options
|
|
66
|
-
* @returns Proxy
|
|
67
|
-
*/
|
|
68
|
-
export function signal(el, options) {
|
|
69
|
-
if (isSignal(el)) {
|
|
70
|
-
return el
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const existing = getSignal(el)
|
|
74
|
-
if (existing) {
|
|
75
|
-
return existing
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return createSignal(el, domSignalHandler, (target, proxy) => {
|
|
79
|
-
domListen(target, proxy, options)
|
|
80
|
-
})
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* This sets up the mutationObserver that calls notifySet on changes in the DOM
|
|
85
|
-
*/
|
|
86
|
-
function domListen(el, signal, options) {
|
|
87
|
-
const defaultOptions = {
|
|
88
|
-
characterData: true,
|
|
89
|
-
subtree: true,
|
|
90
|
-
attributes: true,
|
|
91
|
-
attributesOldValue: true,
|
|
92
|
-
childList: true
|
|
93
|
-
}
|
|
94
|
-
if (!options) {
|
|
95
|
-
options = defaultOptions
|
|
96
|
-
}
|
|
97
|
-
let oldContentHTML = el.innerHTML
|
|
98
|
-
let oldContentText = el.innerText
|
|
99
|
-
if (!observers.has(el)) {
|
|
100
|
-
const observer = new MutationObserver((mutationList, observer) => {
|
|
101
|
-
// collect changes
|
|
102
|
-
const changes = {}
|
|
103
|
-
for (const mutation of mutationList) {
|
|
104
|
-
if (mutation.type==='attributes') {
|
|
105
|
-
// check if any listeners for each attribute
|
|
106
|
-
changes[mutation.attributeName] = mutation.attributeOldValue
|
|
107
|
-
} else if (mutation.type==='subtree' || mutation.type==='characterData') {
|
|
108
|
-
// change on innerHTML/innerText
|
|
109
|
-
if (el.innerHTML != oldContentHTML) {
|
|
110
|
-
changes.innerHTML = oldContentHTML
|
|
111
|
-
oldContentHTML = el.innerHTML
|
|
112
|
-
}
|
|
113
|
-
if (el.innerText != oldContentText) {
|
|
114
|
-
changes.innerText = oldContentText
|
|
115
|
-
oldContentText = el.innerText
|
|
116
|
-
}
|
|
117
|
-
} else if (mutation.type==='childList') {
|
|
118
|
-
changes.children = { //FIXME: overwrites changes in this list path if list is rendered multiple times
|
|
119
|
-
was: Array.from(el.children) //FIXME; fill in 'now'
|
|
120
|
-
}
|
|
121
|
-
changes.length = -1 //FIXME: don't do this :)
|
|
122
|
-
if (el.innerHTML != oldContentHTML) {
|
|
123
|
-
changes.innerHTML = oldContentHTML
|
|
124
|
-
oldContentHTML = el.innerHTML
|
|
125
|
-
}
|
|
126
|
-
if (el.innerText != oldContentText) {
|
|
127
|
-
changes.innerText = oldContentText
|
|
128
|
-
oldContentText = el.innerText
|
|
129
|
-
}
|
|
130
|
-
} else {
|
|
131
|
-
console.log('nothing to do for',el,mutation.type)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
for (const prop in changes) {
|
|
135
|
-
notifySet(signal, makeContext(prop, { was: changes[prop], now: el[prop] }))
|
|
136
|
-
}
|
|
137
|
-
})
|
|
138
|
-
observer.observe(el, options)
|
|
139
|
-
observers.set(el, observer)
|
|
140
|
-
//@TODO: unregister the observer when el is removed from the dom (after a timeout)
|
|
141
|
-
if (el.matches('input, textarea, select')) {
|
|
142
|
-
let prevValue = el.value
|
|
143
|
-
let prevChecked = el.checked
|
|
144
|
-
const notifyFormValue = () => {
|
|
145
|
-
notifySet(signal, makeContext('value', { was: prevValue, now: el.value }))
|
|
146
|
-
prevValue = el.value
|
|
147
|
-
if ('checked' in el) {
|
|
148
|
-
notifySet(signal, makeContext('checked', { was: prevChecked, now: el.checked }))
|
|
149
|
-
prevChecked = el.checked
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
el.addEventListener('change', notifyFormValue)
|
|
153
|
-
if (el.matches('input, textarea')) {
|
|
154
|
-
el.addEventListener('input', notifyFormValue)
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* This function sets up the dom signal on an element, provided it has a `data-flow-list` attribute
|
|
162
|
-
* @param HTMLElement element - the element to track
|
|
163
|
-
* @returns Proxy
|
|
164
|
-
*/
|
|
165
|
-
export function trackDomList(element)
|
|
166
|
-
{
|
|
167
|
-
const path = this.getBindingPath(element)
|
|
168
|
-
if (!path) {
|
|
169
|
-
throw new Error('Could not find binding path for element', { cause: element })
|
|
170
|
-
}
|
|
171
|
-
const s = signal(element, {
|
|
172
|
-
childList: true
|
|
173
|
-
})
|
|
174
|
-
throttledEffect(() => {
|
|
175
|
-
const children = Array.from(s.children)
|
|
176
|
-
untracked(() => { // don't track access to the data, only track dom changes
|
|
177
|
-
batch(() => { // apply all changes in the list as one change
|
|
178
|
-
let key=0
|
|
179
|
-
const currentList = getValueByPath(this.options.root, path)
|
|
180
|
-
const source = currentList.slice() // make sure changes in currentList don't affect the original source
|
|
181
|
-
for (const item of children) {
|
|
182
|
-
if (item.tagName==='TEMPLATE') {
|
|
183
|
-
continue
|
|
184
|
-
}
|
|
185
|
-
if (item.dataset.flowKey) { //FIXME: could be other attribute name
|
|
186
|
-
if (item.dataset.flowKey!=key) {
|
|
187
|
-
setValueByPath(this.options.root, path+'.'+key,
|
|
188
|
-
source[item.dataset.flowKey])
|
|
189
|
-
}
|
|
190
|
-
key++
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
if (currentList.length>key) {
|
|
194
|
-
// remove extra values
|
|
195
|
-
currentList.length = key
|
|
196
|
-
}
|
|
197
|
-
})
|
|
198
|
-
})
|
|
199
|
-
}, 50)
|
|
200
|
-
return s
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* This function sets up the dom signal on an element, provided it has a `data-flow-field` attribute
|
|
205
|
-
* @param HTMLElement element - the element to track
|
|
206
|
-
* @returns Proxy
|
|
207
|
-
*/
|
|
208
|
-
export function trackDomField(element, props, valueIsString, stringProperty = 'innerHTML', getUpdateValue) {
|
|
209
|
-
if (domSignals.has(element)) {
|
|
210
|
-
return
|
|
211
|
-
}
|
|
212
|
-
const path = this.getBindingPath(element)
|
|
213
|
-
if (!path) {
|
|
214
|
-
throw new Error('Could not find binding path for element', { cause: element })
|
|
215
|
-
}
|
|
216
|
-
const s = signal(element)
|
|
217
|
-
domSignals.set(element, s)
|
|
218
|
-
//TODO: run reverse transformers (extract)
|
|
219
|
-
batch(() => { // avoids cyclical dependencies - check why
|
|
220
|
-
throttledEffect(() => {
|
|
221
|
-
let updateValue
|
|
222
|
-
if (getUpdateValue) {
|
|
223
|
-
// Custom edit extractors often need the current data value, for
|
|
224
|
-
// example to toggle a checkbox value in an array. Read the DOM
|
|
225
|
-
// properties here for dependency tracking, but read the data only
|
|
226
|
-
// in the untracked section below to avoid DOM/data cycles.
|
|
227
|
-
for (const prop of props) {
|
|
228
|
-
s[prop]
|
|
229
|
-
}
|
|
230
|
-
} else {
|
|
231
|
-
updateValue = s[stringProperty]
|
|
232
|
-
if (!valueIsString) {
|
|
233
|
-
updateValue = getProperties(s, ...props)
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
untracked(() => { // don't track changes in data, only in the dom
|
|
237
|
-
// Rendering a primitive value into the DOM usually turns it into
|
|
238
|
-
// a string. Do not write that string straight back to the data
|
|
239
|
-
// when it still represents the current value. This keeps numbers
|
|
240
|
-
// and booleans stable after one-way rendering in a two-way bind.
|
|
241
|
-
const currentValue = getValueByPath(this.options.root, path)
|
|
242
|
-
if (getUpdateValue) {
|
|
243
|
-
updateValue = getUpdateValue.call(this, s, currentValue)
|
|
244
|
-
}
|
|
245
|
-
if (typeof updateValue === 'undefined') {
|
|
246
|
-
return
|
|
247
|
-
}
|
|
248
|
-
if (valueIsString && !Object.is(currentValue, updateValue) && String(currentValue) === updateValue) {
|
|
249
|
-
return
|
|
250
|
-
}
|
|
251
|
-
// don't trigger this effect when the data changes (root.path)
|
|
252
|
-
setValueByPath(this.options.root, path, updateValue)
|
|
253
|
-
})
|
|
254
|
-
}, 50)
|
|
255
|
-
})
|
|
256
|
-
return s
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Finds the closest ancestor, including `el` itself, that has `attr` and
|
|
262
|
-
* returns that attribute value.
|
|
263
|
-
*
|
|
264
|
-
* This helper is used by the app/command layer and lives in this module so
|
|
265
|
-
* DOM utility helpers shared by the app and binding layers.
|
|
266
|
-
*
|
|
267
|
-
* @param {Element} el - Element to start searching from.
|
|
268
|
-
* @param {string} attr - Attribute name to find.
|
|
269
|
-
* @returns {string|undefined} The attribute value, or undefined if not found.
|
|
270
|
-
*/
|
|
271
|
-
export function findAttribute(el, attr) {
|
|
272
|
-
return el.closest('['+attr+']')
|
|
273
|
-
?.getAttribute(attr)
|
|
274
|
-
}
|
|
1
|
+
export * from '@muze-labs/simplyflow-bind/dom'
|
package/src/highlight.mjs
CHANGED
|
@@ -1,11 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
const outputArray = values.map(
|
|
3
|
-
(value, index) =>
|
|
4
|
-
`${strings[index]}${value}`,
|
|
5
|
-
);
|
|
6
|
-
return outputArray.join("") + strings[strings.length - 1];
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function css(strings, ...values) {
|
|
10
|
-
return html(strings, ...values)
|
|
11
|
-
}
|
|
1
|
+
export * from '@muze-labs/simplyflow-app/highlight'
|