simplyview 3.0.2 → 3.0.3
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 +2 -5
- package/dist/simply.app.js +86 -713
- package/dist/simply.app.min.js +1 -1
- package/dist/simply.app.min.js.map +4 -4
- package/dist/simply.everything.js +101 -1020
- package/dist/simply.everything.min.js +1 -1
- package/dist/simply.everything.min.js.map +4 -4
- package/package.json +4 -4
- package/src/app.mjs +3 -13
- package/src/bind.mjs +132 -37
- package/src/command.mjs +1 -1
- package/src/everything.mjs +6 -10
- package/src/include.mjs +13 -10
- package/src/key.mjs +74 -21
- package/src/route.mjs +3 -2
- package/src/view.mjs +20 -0
- package/src/changes.md +0 -18
package/src/bind.mjs
CHANGED
|
@@ -7,14 +7,28 @@ class SimplyBind {
|
|
|
7
7
|
container: document.body,
|
|
8
8
|
attribute: 'data-bind',
|
|
9
9
|
transformers: [],
|
|
10
|
-
defaultTransformers:
|
|
10
|
+
defaultTransformers: {
|
|
11
|
+
field: [defaultFieldTransformer],
|
|
12
|
+
list: [defaultListTransformer],
|
|
13
|
+
map: [defaultMapTransformer]
|
|
14
|
+
}
|
|
11
15
|
}
|
|
12
16
|
if (!options?.root) {
|
|
13
17
|
throw new Error('bind needs at least options.root set')
|
|
14
18
|
}
|
|
15
19
|
this.options = Object.assign({}, defaultOptions, options)
|
|
16
20
|
|
|
17
|
-
const attribute
|
|
21
|
+
const attribute = this.options.attribute
|
|
22
|
+
const bindAttributes = [attribute+'-field',attribute+'-list',attribute+'-map']
|
|
23
|
+
const bindSelector = `[${attribute}-field],[${attribute}-list],[${attribute}-map]`
|
|
24
|
+
|
|
25
|
+
const getBindingAttribute = (el) => {
|
|
26
|
+
const foundAttribute = bindAttributes.find(attr => el.hasAttribute(attr))
|
|
27
|
+
if (!foundAttribute) {
|
|
28
|
+
console.error('No matching attribute found',el)
|
|
29
|
+
}
|
|
30
|
+
return foundAttribute
|
|
31
|
+
}
|
|
18
32
|
|
|
19
33
|
// sets up the effect that updates the element if its
|
|
20
34
|
// data binding value changes
|
|
@@ -23,8 +37,9 @@ class SimplyBind {
|
|
|
23
37
|
this.bindings.set(el, throttledEffect(() => {
|
|
24
38
|
const context = {
|
|
25
39
|
templates: el.querySelectorAll(':scope > template'),
|
|
26
|
-
|
|
40
|
+
attribute: getBindingAttribute(el)
|
|
27
41
|
}
|
|
42
|
+
context.path = this.getBindingPath(el)
|
|
28
43
|
context.value = getValueByPath(this.options.root, context.path)
|
|
29
44
|
context.element = el
|
|
30
45
|
runTransformers(context)
|
|
@@ -36,7 +51,18 @@ class SimplyBind {
|
|
|
36
51
|
// each transformer can opt to call the next or not
|
|
37
52
|
// transformers should return the context object (possibly altered)
|
|
38
53
|
const runTransformers = (context) => {
|
|
39
|
-
let transformers
|
|
54
|
+
let transformers
|
|
55
|
+
switch(context.attribute) {
|
|
56
|
+
case this.options.attribute+'-field':
|
|
57
|
+
transformers = this.options.defaultTransformers.field || []
|
|
58
|
+
break
|
|
59
|
+
case this.options.attribute+'-list':
|
|
60
|
+
transformers = this.options.defaultTransformers.list || []
|
|
61
|
+
break
|
|
62
|
+
case this.options.attribute+'-map':
|
|
63
|
+
transformers = this.options.defaultTransformers.map || []
|
|
64
|
+
break
|
|
65
|
+
}
|
|
40
66
|
if (context.element.dataset.transform) {
|
|
41
67
|
context.element.dataset.transform.split(' ').filter(Boolean).forEach(t => {
|
|
42
68
|
if (this.options.transformers[t]) {
|
|
@@ -69,12 +95,13 @@ class SimplyBind {
|
|
|
69
95
|
// if any element is added, and has a data bind attribute
|
|
70
96
|
// it applies that data binding
|
|
71
97
|
const updateBindings = (changes) => {
|
|
98
|
+
const selector = `[${attribute}-field],[${attribute}-list],[${attribute}-map]`
|
|
72
99
|
for (const change of changes) {
|
|
73
100
|
if (change.type=="childList" && change.addedNodes) {
|
|
74
101
|
for (let node of change.addedNodes) {
|
|
75
102
|
if (node instanceof HTMLElement) {
|
|
76
|
-
let bindings = Array.from(node.querySelectorAll(
|
|
77
|
-
if (node.matches(
|
|
103
|
+
let bindings = Array.from(node.querySelectorAll(selector))
|
|
104
|
+
if (node.matches(selector)) {
|
|
78
105
|
bindings.unshift(node)
|
|
79
106
|
}
|
|
80
107
|
if (bindings.length) {
|
|
@@ -100,7 +127,11 @@ class SimplyBind {
|
|
|
100
127
|
// this finds elements with data binding attributes and applies those bindings
|
|
101
128
|
// must come after setting up the observer, or included templates
|
|
102
129
|
// won't trigger their own bindings
|
|
103
|
-
const bindings = this.options.container.querySelectorAll(
|
|
130
|
+
const bindings = this.options.container.querySelectorAll(
|
|
131
|
+
'['+this.options.attribute+'-field]'+
|
|
132
|
+
',['+this.options.attribute+'-list]'+
|
|
133
|
+
',['+this.options.attribute+'-map]'
|
|
134
|
+
)
|
|
104
135
|
if (bindings.length) {
|
|
105
136
|
applyBindings(bindings)
|
|
106
137
|
}
|
|
@@ -127,23 +158,25 @@ class SimplyBind {
|
|
|
127
158
|
}
|
|
128
159
|
let clone = template.content.cloneNode(true)
|
|
129
160
|
if (!clone.children?.length) {
|
|
130
|
-
|
|
161
|
+
return clone
|
|
131
162
|
}
|
|
132
163
|
if (clone.children.length>1) {
|
|
133
164
|
throw new Error('template must contain a single root node', { cause: template })
|
|
134
165
|
}
|
|
135
|
-
const bindings = clone.querySelectorAll('['+this.options.attribute+']')
|
|
136
166
|
const attribute = this.options.attribute
|
|
167
|
+
const attributes = [attribute+'-field',attribute+'-list',attribute+'-map']
|
|
168
|
+
const bindings = clone.querySelectorAll(`[${attribute}-field],[${attribute}-list],[${attribute}-map]`)
|
|
137
169
|
for (let binding of bindings) {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
170
|
+
const attr = attributes.find(attr => binding.hasAttribute(attr))
|
|
171
|
+
const bind = binding.getAttribute(attr)
|
|
172
|
+
if (bind.substring(0, ':root.'.length)==':root.') {
|
|
173
|
+
binding.setAttribute(attr, bind.substring(':root.'.length))
|
|
174
|
+
} else if (bind==':value' && index!=null) {
|
|
175
|
+
binding.setAttribute(attr, path+'.'+index)
|
|
143
176
|
} else if (index!=null) {
|
|
144
|
-
binding.setAttribute(
|
|
177
|
+
binding.setAttribute(attr, path+'.'+index+'.'+bind)
|
|
145
178
|
} else {
|
|
146
|
-
binding.setAttribute(
|
|
179
|
+
binding.setAttribute(attr, parent+'.'+bind)
|
|
147
180
|
}
|
|
148
181
|
}
|
|
149
182
|
if (typeof index !== 'undefined') {
|
|
@@ -156,7 +189,16 @@ class SimplyBind {
|
|
|
156
189
|
}
|
|
157
190
|
|
|
158
191
|
getBindingPath(el) {
|
|
159
|
-
|
|
192
|
+
const attributes = [
|
|
193
|
+
this.options.attribute+'-field',
|
|
194
|
+
this.options.attribute+'-list',
|
|
195
|
+
this.options.attribute+'-map'
|
|
196
|
+
]
|
|
197
|
+
for (let attr of attributes) {
|
|
198
|
+
if (el.hasAttribute(attr)) {
|
|
199
|
+
return el.getAttribute(attr)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
160
202
|
}
|
|
161
203
|
|
|
162
204
|
/**
|
|
@@ -169,7 +211,7 @@ class SimplyBind {
|
|
|
169
211
|
let path = this.getBindingPath(t)
|
|
170
212
|
let currentItem
|
|
171
213
|
if (path) {
|
|
172
|
-
if (path.substr(0,6)=='
|
|
214
|
+
if (path.substr(0,6)==':root.') {
|
|
173
215
|
currentItem = getValueByPath(this.options.root, path)
|
|
174
216
|
} else {
|
|
175
217
|
currentItem = getValueByPath(value, path)
|
|
@@ -182,16 +224,19 @@ class SimplyBind {
|
|
|
182
224
|
const strItem = ''+currentItem
|
|
183
225
|
let matches = t.getAttribute(this.options.attribute+'-match')
|
|
184
226
|
if (matches) {
|
|
185
|
-
if (matches==='
|
|
227
|
+
if (matches===':empty' && !currentItem) {
|
|
186
228
|
return t
|
|
187
|
-
} else if (matches==='
|
|
229
|
+
} else if (matches===':notempty' && currentItem) {
|
|
188
230
|
return t
|
|
189
231
|
}
|
|
190
232
|
if (strItem.match(matches)) {
|
|
191
233
|
return t
|
|
192
234
|
}
|
|
193
235
|
}
|
|
194
|
-
if (!matches) {
|
|
236
|
+
if (!matches && currentItem!==null && currentItem!==undefined) {
|
|
237
|
+
//FIXME: this doesn't run templates in lists where list entry is null
|
|
238
|
+
//which messes up the count
|
|
239
|
+
//
|
|
195
240
|
// no data-bind-match is set, so return this template
|
|
196
241
|
return t
|
|
197
242
|
}
|
|
@@ -229,13 +274,13 @@ export function bind(options)
|
|
|
229
274
|
|
|
230
275
|
/**
|
|
231
276
|
* Returns true if a matches b, either by having the
|
|
232
|
-
* same string value, or matching string
|
|
277
|
+
* same string value, or matching string :empty against a falsy value
|
|
233
278
|
*/
|
|
234
279
|
export function matchValue(a,b) {
|
|
235
|
-
if (a=='
|
|
280
|
+
if (a==':empty' && !b) {
|
|
236
281
|
return true
|
|
237
282
|
}
|
|
238
|
-
if (b=='
|
|
283
|
+
if (b==':empty' && !a) {
|
|
239
284
|
return true
|
|
240
285
|
}
|
|
241
286
|
if (''+a == ''+b) {
|
|
@@ -257,11 +302,11 @@ export function getValueByPath(root, path)
|
|
|
257
302
|
let part, prevPart;
|
|
258
303
|
while (parts.length && curr) {
|
|
259
304
|
part = parts.shift()
|
|
260
|
-
if (part=='
|
|
305
|
+
if (part==':key') {
|
|
261
306
|
return prevPart
|
|
262
|
-
} else if (part=='
|
|
307
|
+
} else if (part==':value') {
|
|
263
308
|
return curr
|
|
264
|
-
} else if (part=='
|
|
309
|
+
} else if (part==':root') {
|
|
265
310
|
curr = root
|
|
266
311
|
} else {
|
|
267
312
|
part = decodeURIComponent(part)
|
|
@@ -276,7 +321,7 @@ export function getValueByPath(root, path)
|
|
|
276
321
|
* Default transformer for data binding
|
|
277
322
|
* Will be used unless overriden in the SimplyBind options parameter
|
|
278
323
|
*/
|
|
279
|
-
export function
|
|
324
|
+
export function defaultFieldTransformer(context) {
|
|
280
325
|
const el = context.element
|
|
281
326
|
const templates = context.templates
|
|
282
327
|
const templatesCount = templates.length
|
|
@@ -284,11 +329,7 @@ export function defaultTransformer(context) {
|
|
|
284
329
|
const value = context.value
|
|
285
330
|
const attribute = this.options.attribute
|
|
286
331
|
|
|
287
|
-
if (
|
|
288
|
-
transformArrayByTemplates.call(this, context)
|
|
289
|
-
} else if (typeof value == 'object' && templates?.length) {
|
|
290
|
-
transformObjectByTemplates.call(this, context)
|
|
291
|
-
} else if (templates?.length) {
|
|
332
|
+
if (templates?.length) {
|
|
292
333
|
transformLiteralByTemplates.call(this, context)
|
|
293
334
|
} else if (el.tagName=='INPUT') {
|
|
294
335
|
transformInput.call(this, context)
|
|
@@ -304,10 +345,49 @@ export function defaultTransformer(context) {
|
|
|
304
345
|
return context
|
|
305
346
|
}
|
|
306
347
|
|
|
348
|
+
export function defaultListTransformer(context) {
|
|
349
|
+
const el = context.element
|
|
350
|
+
const templates = context.templates
|
|
351
|
+
const templatesCount = templates.length
|
|
352
|
+
const path = context.path
|
|
353
|
+
const value = context.value
|
|
354
|
+
const attribute = this.options.attribute
|
|
355
|
+
|
|
356
|
+
if (!Array.isArray(value)) {
|
|
357
|
+
console.error('Value is not an array.', el, value)
|
|
358
|
+
} else if (!templates?.length) {
|
|
359
|
+
console.error('No templates found in', el)
|
|
360
|
+
} else {
|
|
361
|
+
transformArrayByTemplates.call(this, context)
|
|
362
|
+
}
|
|
363
|
+
return context
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function defaultMapTransformer(context) {
|
|
367
|
+
const el = context.element
|
|
368
|
+
const templates = context.templates
|
|
369
|
+
const templatesCount = templates.length
|
|
370
|
+
const path = context.path
|
|
371
|
+
const value = context.value
|
|
372
|
+
const attribute = this.options.attribute
|
|
373
|
+
|
|
374
|
+
if (typeof value != 'object') {
|
|
375
|
+
console.error('Value is not an object.', el, value)
|
|
376
|
+
} else if (!templates?.length) {
|
|
377
|
+
console.error('No templates found in', el)
|
|
378
|
+
} else {
|
|
379
|
+
transformObjectByTemplates.call(this, context)
|
|
380
|
+
}
|
|
381
|
+
return context
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
|
|
307
385
|
/**
|
|
308
386
|
* Renders an array value by applying templates for each entry
|
|
309
387
|
* Replaces or removes existing DOM children if needed
|
|
310
388
|
* Reuses (doesn't touch) DOM children if template doesn't change
|
|
389
|
+
* FIXME: this doesn't handle situations where there is no matching template
|
|
390
|
+
* this messes up self healing. check transformObjectByTemplates for a better implementation
|
|
311
391
|
*/
|
|
312
392
|
export function transformArrayByTemplates(context) {
|
|
313
393
|
const el = context.element
|
|
@@ -333,14 +413,14 @@ export function transformArrayByTemplates(context) {
|
|
|
333
413
|
// remove this
|
|
334
414
|
item.remove()
|
|
335
415
|
} else {
|
|
336
|
-
// check that all data-bind params start with current json path or
|
|
416
|
+
// check that all data-bind params start with current json path or ':root', otherwise replaceChild
|
|
337
417
|
let bindings = Array.from(item.querySelectorAll(`[${attribute}]`))
|
|
338
418
|
if (item.matches(`[${attribute}]`)) {
|
|
339
419
|
bindings.unshift(item)
|
|
340
420
|
}
|
|
341
421
|
let needsReplacement = bindings.find(b => {
|
|
342
422
|
let databind = b.getAttribute(attribute)
|
|
343
|
-
return (databind.substr(0,5)!=='
|
|
423
|
+
return (databind.substr(0,5)!==':root'
|
|
344
424
|
&& databind.substr(0, path.length)!==path)
|
|
345
425
|
})
|
|
346
426
|
if (!needsReplacement) {
|
|
@@ -400,7 +480,10 @@ export function transformObjectByTemplates(context) {
|
|
|
400
480
|
context.index = key
|
|
401
481
|
let item = items.shift()
|
|
402
482
|
if (!item) { // more properties than rendered items
|
|
403
|
-
|
|
483
|
+
let clone = this.applyTemplate(context)
|
|
484
|
+
if (clone.firstElementChild) {
|
|
485
|
+
el.appendChild(clone)
|
|
486
|
+
}
|
|
404
487
|
continue
|
|
405
488
|
}
|
|
406
489
|
if (item.getAttribute[attribute+'-key']!=key) {
|
|
@@ -432,6 +515,17 @@ export function transformObjectByTemplates(context) {
|
|
|
432
515
|
}
|
|
433
516
|
}
|
|
434
517
|
|
|
518
|
+
function getParentPath(el, attribute) {
|
|
519
|
+
const parentEl = el.parentElement?.closest(`[${attribute}-list],[${attribute}-map]`)
|
|
520
|
+
if (!parentEl) {
|
|
521
|
+
return ':root'
|
|
522
|
+
}
|
|
523
|
+
if (parentEl.hasAttribute(`${attribute}-list`)) {
|
|
524
|
+
return parentEl.getAttribute(`${attribute}-list`)
|
|
525
|
+
}
|
|
526
|
+
return parentEl.getAttribute(`${attribute}-map`)
|
|
527
|
+
}
|
|
528
|
+
|
|
435
529
|
/**
|
|
436
530
|
* transforms the contents of an html element by rendering
|
|
437
531
|
* a matching template, once.
|
|
@@ -446,7 +540,8 @@ export function transformLiteralByTemplates(context) {
|
|
|
446
540
|
|
|
447
541
|
const rendered = el.querySelector(':scope > :not(template)')
|
|
448
542
|
const template = this.findTemplate(templates, value)
|
|
449
|
-
|
|
543
|
+
|
|
544
|
+
context.parent = getParentPath(el, attribute)
|
|
450
545
|
if (rendered) {
|
|
451
546
|
if (template) {
|
|
452
547
|
if (rendered?.$bindTemplate != template) {
|
package/src/command.mjs
CHANGED
|
@@ -21,7 +21,7 @@ class SimplyCommands {
|
|
|
21
21
|
return
|
|
22
22
|
}
|
|
23
23
|
const shouldContinue = this[command.name].call(options.app, command.source, command.value)
|
|
24
|
-
if (shouldContinue
|
|
24
|
+
if (shouldContinue!==true) {
|
|
25
25
|
evt.preventDefault()
|
|
26
26
|
evt.stopPropagation()
|
|
27
27
|
return false
|
package/src/everything.mjs
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
import { activate } from './activate.mjs'
|
|
2
|
-
import
|
|
2
|
+
import { actions as action } from './action.mjs'
|
|
3
3
|
import { app } from './app.mjs'
|
|
4
|
-
import {
|
|
5
|
-
import * as command from './command.mjs'
|
|
4
|
+
import { commands as command } from './command.mjs'
|
|
6
5
|
import { include } from './include.mjs'
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import * as state from './state.mjs'
|
|
6
|
+
import { keys as key } from './key.mjs'
|
|
7
|
+
import { routes as route } from './route.mjs'
|
|
8
|
+
import { view } from './view.mjs'
|
|
11
9
|
|
|
12
10
|
const simply = {
|
|
13
11
|
activate,
|
|
14
12
|
action,
|
|
15
13
|
app,
|
|
16
|
-
bind,
|
|
17
14
|
command,
|
|
18
15
|
include,
|
|
19
16
|
key,
|
|
20
|
-
model,
|
|
21
17
|
route,
|
|
22
|
-
|
|
18
|
+
view
|
|
23
19
|
}
|
|
24
20
|
|
|
25
21
|
window.simply = simply
|
package/src/include.mjs
CHANGED
|
@@ -53,7 +53,7 @@ const waitForPreviousScripts = async () => {
|
|
|
53
53
|
// that triggers the Promise.resolve method
|
|
54
54
|
return new Promise(function(resolve) {
|
|
55
55
|
var next = globalThis.document.createElement('script')
|
|
56
|
-
next.src = "
|
|
56
|
+
next.src = "https://cdn.jsdelivr.net/gh/simplyedit/simplyview/dist/simply.include.next.js"
|
|
57
57
|
next.async = false
|
|
58
58
|
globalThis.document.addEventListener('simply-include-next', () => {
|
|
59
59
|
head.removeChild(next)
|
|
@@ -122,18 +122,21 @@ export const include = {
|
|
|
122
122
|
// order in which they are defined
|
|
123
123
|
let scriptsFragment = globalThis.document.createDocumentFragment()
|
|
124
124
|
const scripts = fragment.querySelectorAll('script')
|
|
125
|
-
|
|
126
|
-
let
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
125
|
+
if (scripts.length) {
|
|
126
|
+
for (let script of scripts) {
|
|
127
|
+
let placeholder = globalThis.document.createComment(script.src || 'inline script')
|
|
128
|
+
script.parentNode.insertBefore(placeholder, script)
|
|
129
|
+
script.dataset.simplyLocation = scriptLocations.length
|
|
130
|
+
scriptLocations.push(placeholder)
|
|
131
|
+
scriptsFragment.appendChild(script)
|
|
132
|
+
}
|
|
133
|
+
globalThis.setTimeout(function() {
|
|
134
|
+
include.scripts(Array.from(scriptsFragment.children), link ? link.href : globalThis.location.href )
|
|
135
|
+
}, 10)
|
|
131
136
|
}
|
|
132
137
|
// add the remainder before the include link
|
|
133
138
|
link.parentNode.insertBefore(fragment, link ? link : null)
|
|
134
|
-
|
|
135
|
-
include.scripts(scriptsFragment.childNodes, link ? link.href : globalThis.location.href )
|
|
136
|
-
}, 10)
|
|
139
|
+
|
|
137
140
|
}
|
|
138
141
|
}
|
|
139
142
|
|
package/src/key.mjs
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
const KEY = Object.freeze({
|
|
2
|
+
Compose: 229,
|
|
3
|
+
Control: 17,
|
|
4
|
+
Meta: 224,
|
|
5
|
+
Alt: 18,
|
|
6
|
+
Shift: 16
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
class SimplyKey {
|
|
2
10
|
constructor(options = {}) {
|
|
3
11
|
if (!options.app) {
|
|
4
12
|
options.app = {}
|
|
@@ -9,38 +17,83 @@ class SimplyKeys {
|
|
|
9
17
|
Object.assign(this, options.keys)
|
|
10
18
|
|
|
11
19
|
const keyHandler = (e) => {
|
|
12
|
-
if (e.isComposing || e.keyCode ===
|
|
13
|
-
return
|
|
20
|
+
if (e.isComposing || e.keyCode === KEY.Compose) {
|
|
21
|
+
return
|
|
14
22
|
}
|
|
15
23
|
if (e.defaultPrevented) {
|
|
16
|
-
return
|
|
24
|
+
return
|
|
17
25
|
}
|
|
18
26
|
if (!e.target) {
|
|
19
|
-
return
|
|
27
|
+
return
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
let selectedKeyboard = 'default'
|
|
30
|
+
let selectedKeyboard = 'default'
|
|
23
31
|
if (e.target.closest('[data-simply-keyboard]')) {
|
|
24
|
-
selectedKeyboard = e.target.closest('[data-simply-keyboard]')
|
|
32
|
+
selectedKeyboard = e.target.closest('[data-simply-keyboard]')
|
|
33
|
+
.dataset.simplyKeyboard
|
|
34
|
+
}
|
|
35
|
+
let keyCombination = []
|
|
36
|
+
if (e.ctrlKey && e.keyCode!=KEY.Control) {
|
|
37
|
+
keyCombination.push('Control')
|
|
25
38
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
key+='Control+';
|
|
39
|
+
if (e.metaKey && e.keyCode!=KEY.Meta) {
|
|
40
|
+
keyCombination.push('Meta')
|
|
29
41
|
}
|
|
30
|
-
if (e.
|
|
31
|
-
|
|
42
|
+
if (e.altKey && e.keyCode!=KEY.Alt) {
|
|
43
|
+
keyCombination.push('Alt')
|
|
32
44
|
}
|
|
33
|
-
if (e.
|
|
34
|
-
|
|
45
|
+
if (e.shiftKey && e.keyCode!=KEY.Shift) {
|
|
46
|
+
keyCombination.push('Shift')
|
|
35
47
|
}
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
keyCombination.push(e.key.toLowerCase())
|
|
49
|
+
|
|
50
|
+
let keyboards = []
|
|
51
|
+
let keyboardElement = event.target.closest('[data-simply-keyboard]')
|
|
52
|
+
while (keyboardElement) {
|
|
53
|
+
keyboards.push(keyboardElement.dataset.simplyKeyboard)
|
|
54
|
+
keyboardElement = keyboardElement.parentNode.closest('[data-simply-keyboard]')
|
|
38
55
|
}
|
|
39
|
-
|
|
56
|
+
keyboards.push('')
|
|
57
|
+
|
|
58
|
+
let keyboard, subkeyboard
|
|
59
|
+
let separators = ['+','-']
|
|
60
|
+
|
|
61
|
+
for (i in keyboards) {
|
|
62
|
+
keyboard = keyboards[i]
|
|
63
|
+
if (keyboard == '') {
|
|
64
|
+
subkeyboard = 'default'
|
|
65
|
+
} else {
|
|
66
|
+
subkeyboard = keyboard
|
|
67
|
+
keyboard += '.'
|
|
68
|
+
}
|
|
69
|
+
for (let separator of separators) {
|
|
70
|
+
let keyString = keyCombination.join(separator)
|
|
71
|
+
|
|
72
|
+
if (this[subkeyboard] && (typeof this[subkeyboard][keyString]=='function')) {
|
|
73
|
+
let _continue = this[subkeyboard][keyString].call(this[subkeyboard], e)
|
|
74
|
+
if (!_continue) {
|
|
75
|
+
e.preventDefault()
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (typeof this[subkeyboard + keyString] == 'function') {
|
|
80
|
+
let _continue = this[subkeyboard + keyString].call(this, e)
|
|
81
|
+
if (!_continue) {
|
|
82
|
+
e.preventDefault()
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this[selectedKeyboard] && this[selectedKeyboard][keyString]) {
|
|
88
|
+
let targets = options.app.container.querySelectorAll('[data-simply-accesskey="'
|
|
89
|
+
+ keyboard + keyString + '"]')
|
|
90
|
+
if (targets.length) {
|
|
91
|
+
targets.forEach(t => t.click())
|
|
92
|
+
e.preventDefault()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
40
95
|
|
|
41
|
-
|
|
42
|
-
let keyboard = this[selectedKeyboard]
|
|
43
|
-
keyboard[key].call(options.app,e);
|
|
96
|
+
}
|
|
44
97
|
}
|
|
45
98
|
}
|
|
46
99
|
|
|
@@ -50,6 +103,6 @@ class SimplyKeys {
|
|
|
50
103
|
}
|
|
51
104
|
|
|
52
105
|
export function keys(options={}) {
|
|
53
|
-
return new
|
|
106
|
+
return new SimplyKey(options)
|
|
54
107
|
}
|
|
55
108
|
|
package/src/route.mjs
CHANGED
|
@@ -65,6 +65,8 @@ class SimplyRoute {
|
|
|
65
65
|
if (path && path[path.length-1]!='/') {
|
|
66
66
|
return this.match(path+'/', options)
|
|
67
67
|
}
|
|
68
|
+
console.log(path, this.routeInfo)
|
|
69
|
+
process.exit()
|
|
68
70
|
return false
|
|
69
71
|
}
|
|
70
72
|
|
|
@@ -199,8 +201,7 @@ function getRegexpFromRoute(route) {
|
|
|
199
201
|
return new RegExp('^'+route.replace(/:\w+/g, '([^/]+)').replace(/:\*/, '(.*)'));
|
|
200
202
|
}
|
|
201
203
|
|
|
202
|
-
function parseRoutes(routes) {
|
|
203
|
-
let routeInfo = []
|
|
204
|
+
function parseRoutes(routes, routeInfo) {
|
|
204
205
|
const paths = Object.keys(routes)
|
|
205
206
|
const matchParams = /:(\w+|\*)/g
|
|
206
207
|
for (let path of paths) {
|
package/src/view.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function view(options) {
|
|
2
|
+
if (options.app) {
|
|
3
|
+
options.app.view = options.view || {}
|
|
4
|
+
|
|
5
|
+
const load = () => {
|
|
6
|
+
const data = options.app.view
|
|
7
|
+
const path = globalThis.editor.data.getDataPath(options.app.container || document.body)
|
|
8
|
+
options.app.view = globalThis.editor.currentData[path]
|
|
9
|
+
Object.assign(options.app.view, data)
|
|
10
|
+
}
|
|
11
|
+
if (globalThis.editor && globalThis.editor.currentData) {
|
|
12
|
+
load()
|
|
13
|
+
} else {
|
|
14
|
+
document.addEventListener('simply-content-loaded', load)
|
|
15
|
+
}
|
|
16
|
+
return options.app.view
|
|
17
|
+
} else {
|
|
18
|
+
return options.view
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/changes.md
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# changes
|
|
2
|
-
|
|
3
|
-
removed:
|
|
4
|
-
- render, observe
|
|
5
|
-
these are replaced with bind.mjs and state.mjs (signal, effect)
|
|
6
|
-
- view
|
|
7
|
-
replaced by state.mjs (signal)
|
|
8
|
-
- collect
|
|
9
|
-
replaced by state.mjs (effect)
|
|
10
|
-
- keyboard
|
|
11
|
-
renamed to key.mjs
|
|
12
|
-
- path
|
|
13
|
-
was never used, use jsonPointer instead if you need it
|
|
14
|
-
- viewmodel
|
|
15
|
-
replaced wiht model.mjs
|
|
16
|
-
- resize
|
|
17
|
-
use @container queries instead
|
|
18
|
-
|