@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/route.mjs
CHANGED
|
@@ -1,418 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export function routes(options)
|
|
3
|
-
{
|
|
4
|
-
return new SimplyRoute(options)
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export class SimplyRoute
|
|
8
|
-
{
|
|
9
|
-
constructor(options={})
|
|
10
|
-
{
|
|
11
|
-
this.options = options
|
|
12
|
-
this.baseURL = options.baseURL || '/'
|
|
13
|
-
this.app = options.app || {}
|
|
14
|
-
this.addMissingSlash = !!options.addMissingSlash
|
|
15
|
-
this.matchExact = !!options.matchExact
|
|
16
|
-
this.hijackLinks = !!options.hijackLinks
|
|
17
|
-
this.clear()
|
|
18
|
-
if (options.routes) {
|
|
19
|
-
this.load(options.routes)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
load(routes)
|
|
24
|
-
{
|
|
25
|
-
parseRoutes(routes, this.routeInfo, this.matchExact)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
clear()
|
|
29
|
-
{
|
|
30
|
-
this.routeInfo = []
|
|
31
|
-
this.listeners = {
|
|
32
|
-
match: {},
|
|
33
|
-
call: {},
|
|
34
|
-
goto: {},
|
|
35
|
-
finish: {}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
match(path, options)
|
|
40
|
-
{
|
|
41
|
-
let args = {
|
|
42
|
-
path,
|
|
43
|
-
options
|
|
44
|
-
}
|
|
45
|
-
args = this.runListeners('match',args)
|
|
46
|
-
path = args.path ? args.path : path;
|
|
47
|
-
|
|
48
|
-
let searchParams;
|
|
49
|
-
if (!path) {
|
|
50
|
-
const currentPath = document.location.pathname + document.location.hash
|
|
51
|
-
if (this.has(currentPath)) {
|
|
52
|
-
path = currentPath
|
|
53
|
-
} else {
|
|
54
|
-
path = document.location.pathname
|
|
55
|
-
}
|
|
56
|
-
searchParams = new URLSearchParams(document.location.search)
|
|
57
|
-
} else {
|
|
58
|
-
searchParams = searchParamsForPath(path)
|
|
59
|
-
}
|
|
60
|
-
path = getPath(routePath(path), this.baseURL);
|
|
61
|
-
for ( let route of this.routeInfo) {
|
|
62
|
-
let params = route.pattern.match(path)
|
|
63
|
-
if (this.addMissingSlash && !params) {
|
|
64
|
-
if (path && path[path.length-1]!='/') {
|
|
65
|
-
const pathWithSlash = path + '/'
|
|
66
|
-
params = route.pattern.match(pathWithSlash)
|
|
67
|
-
if (params) {
|
|
68
|
-
path = pathWithSlash
|
|
69
|
-
history.replaceState({}, '', getURL(path, this.baseURL))
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (params) {
|
|
74
|
-
Object.assign(params, options)
|
|
75
|
-
args.route = route
|
|
76
|
-
args.params = params
|
|
77
|
-
args = this.runListeners('call', args)
|
|
78
|
-
params = args.params ? args.params : params
|
|
79
|
-
args.searchParams = searchParams
|
|
80
|
-
args.result = callRouteAction(this.app, route, params, searchParams)
|
|
81
|
-
this.runListeners('finish', args)
|
|
82
|
-
return args.result
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return false
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
runListeners(action, params)
|
|
89
|
-
{
|
|
90
|
-
if (!this.listeners[action] || !Object.keys(this.listeners[action])) {
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
Object.keys(this.listeners[action]).forEach((route) => {
|
|
94
|
-
const pattern = compileRoutePattern(route)
|
|
95
|
-
if (pattern.match(routePath(params.path))) {
|
|
96
|
-
var result;
|
|
97
|
-
for (let callback of this.listeners[action][route]) {
|
|
98
|
-
result = callback.call(this.app, params)
|
|
99
|
-
if (result) {
|
|
100
|
-
params = result
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
})
|
|
105
|
-
return params
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
handleEvents()
|
|
109
|
-
{
|
|
110
|
-
this.removeEvents()
|
|
111
|
-
const popstateHandler = () => {
|
|
112
|
-
this.match()
|
|
113
|
-
}
|
|
114
|
-
const clickHandler = (evt) => {
|
|
115
|
-
if (evt.ctrlKey) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (evt.which != 1) {
|
|
119
|
-
return; // not a 'left' mouse click
|
|
120
|
-
}
|
|
121
|
-
var link = evt.target;
|
|
122
|
-
while (link && link.tagName!='A') {
|
|
123
|
-
link = link.parentElement;
|
|
124
|
-
}
|
|
125
|
-
if (link
|
|
126
|
-
&& link.pathname
|
|
127
|
-
&& link.hostname==globalThis.location.hostname
|
|
128
|
-
&& !link.link
|
|
129
|
-
&& !link.dataset.simplyCommand
|
|
130
|
-
) {
|
|
131
|
-
let check = [
|
|
132
|
-
{ match: link.hash, goto: link.hash },
|
|
133
|
-
{ match: link.pathname + link.hash, goto: link.pathname + link.search + link.hash },
|
|
134
|
-
{ match: link.pathname, goto: link.pathname + link.search }
|
|
135
|
-
]
|
|
136
|
-
let target
|
|
137
|
-
do {
|
|
138
|
-
target = check.shift()
|
|
139
|
-
target.match = getPath(target.match, this.baseURL);
|
|
140
|
-
} while(check.length && !this.has(target.match))
|
|
141
|
-
if ( this.has(target.match) ) {
|
|
142
|
-
let params = this.runListeners('goto', { path: target.goto});
|
|
143
|
-
if (params.path) {
|
|
144
|
-
const followLink = this.goto(params.path)
|
|
145
|
-
if (!followLink || (this.options.hijackLinks && followLink!==false)) {
|
|
146
|
-
// now cancel the browser navigation, since a route handler was found
|
|
147
|
-
evt.preventDefault();
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
globalThis.addEventListener('popstate', popstateHandler)
|
|
155
|
-
this.app.container.addEventListener('click', clickHandler)
|
|
156
|
-
this.eventHandlers = {
|
|
157
|
-
container: this.app.container,
|
|
158
|
-
popstateHandler,
|
|
159
|
-
clickHandler
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
removeEvents()
|
|
164
|
-
{
|
|
165
|
-
if (!this.eventHandlers) {
|
|
166
|
-
return
|
|
167
|
-
}
|
|
168
|
-
globalThis.removeEventListener('popstate', this.eventHandlers.popstateHandler)
|
|
169
|
-
this.eventHandlers.container.removeEventListener('click', this.eventHandlers.clickHandler)
|
|
170
|
-
this.eventHandlers = undefined
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
destroy()
|
|
174
|
-
{
|
|
175
|
-
this.removeEvents()
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
goto(path)
|
|
179
|
-
{
|
|
180
|
-
history.pushState({},'',getURL(path, this.baseURL))
|
|
181
|
-
return this.match(path)
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
has(path)
|
|
185
|
-
{
|
|
186
|
-
path = getPath(routePath(path), this.baseURL)
|
|
187
|
-
for (let route of this.routeInfo) {
|
|
188
|
-
if (route.pattern.match(path)) {
|
|
189
|
-
return true
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return false
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
addListener(action, route, callback)
|
|
196
|
-
{
|
|
197
|
-
if (['goto','match','call','finish'].indexOf(action)==-1) {
|
|
198
|
-
throw new TypeError(`simplyflow/route: unknown listener type "${action}"`)
|
|
199
|
-
}
|
|
200
|
-
if (!this.listeners[action][route]) {
|
|
201
|
-
this.listeners[action][route] = []
|
|
202
|
-
}
|
|
203
|
-
this.listeners[action][route].push(callback)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
removeListener(action, route, callback)
|
|
207
|
-
{
|
|
208
|
-
if (['goto','match','call','finish'].indexOf(action)==-1) {
|
|
209
|
-
throw new TypeError(`simplyflow/route: unknown listener type "${action}"`)
|
|
210
|
-
}
|
|
211
|
-
if (!this.listeners[action][route]) {
|
|
212
|
-
return
|
|
213
|
-
}
|
|
214
|
-
this.listeners[action][route] = this.listeners[action][route].filter((listener) => {
|
|
215
|
-
return listener != callback
|
|
216
|
-
})
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
init(options)
|
|
220
|
-
{
|
|
221
|
-
if (options.baseURL) {
|
|
222
|
-
this.baseURL = options.baseURL
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function callRouteAction(app, route, params, searchParams)
|
|
228
|
-
{
|
|
229
|
-
if (typeof route.action === 'function') {
|
|
230
|
-
return route.action.call(app, params, searchParams)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (typeof route.action === 'string') {
|
|
234
|
-
const action = app.actions?.[route.action]
|
|
235
|
-
if (typeof action === 'function') {
|
|
236
|
-
return action.call(app, routeActionParams(route, params, searchParams))
|
|
237
|
-
}
|
|
238
|
-
throw unknownRouteActionError(route, app.actions)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
throw new TypeError(`simplyflow/route: route "${route.path}" must use a function or action name`)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const warnedRouteQueryConflicts = new Set()
|
|
245
|
-
|
|
246
|
-
function routeActionParams(route, params, searchParams)
|
|
247
|
-
{
|
|
248
|
-
const query = queryParams(searchParams)
|
|
249
|
-
for (const key of Object.keys(query)) {
|
|
250
|
-
if (Object.hasOwn(params, key)) {
|
|
251
|
-
warnRouteQueryConflict(route, key)
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
// Query parameters are user-editable, while route params come from the
|
|
255
|
-
// developer-defined route pattern. Route params therefore win on conflicts.
|
|
256
|
-
return Object.assign(query, params)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function queryParams(searchParams)
|
|
260
|
-
{
|
|
261
|
-
const params = {}
|
|
262
|
-
for (const [key, value] of searchParams.entries()) {
|
|
263
|
-
if (!Object.hasOwn(params, key)) {
|
|
264
|
-
params[key] = value
|
|
265
|
-
} else if (Array.isArray(params[key])) {
|
|
266
|
-
params[key].push(value)
|
|
267
|
-
} else {
|
|
268
|
-
params[key] = [params[key], value]
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
return params
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function warnRouteQueryConflict(route, key)
|
|
275
|
-
{
|
|
276
|
-
const warningKey = `${route.path}\0${key}`
|
|
277
|
-
if (warnedRouteQueryConflicts.has(warningKey)) {
|
|
278
|
-
return
|
|
279
|
-
}
|
|
280
|
-
warnedRouteQueryConflicts.add(warningKey)
|
|
281
|
-
console.warn(`simplyflow/route: query parameter "${key}" was ignored because route "${route.path}" already provides a route parameter with that name.`)
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function unknownRouteActionError(route, actions)
|
|
285
|
-
{
|
|
286
|
-
const suggestion = closest(route.action, Object.keys(actions || {}))
|
|
287
|
-
const hint = suggestion ? ` Did you mean "${suggestion}"?` : ''
|
|
288
|
-
return new TypeError(`simplyflow/route: route "${route.path}" uses unknown action "${route.action}".${hint}`)
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function searchParamsForPath(path)
|
|
292
|
-
{
|
|
293
|
-
const index = typeof path === 'string' ? path.indexOf('?') : -1
|
|
294
|
-
if (index === -1) {
|
|
295
|
-
return new URLSearchParams()
|
|
296
|
-
}
|
|
297
|
-
const hashIndex = path.indexOf('#', index)
|
|
298
|
-
const search = hashIndex === -1 ? path.substring(index) : path.substring(index, hashIndex)
|
|
299
|
-
return new URLSearchParams(search)
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function routePath(path)
|
|
303
|
-
{
|
|
304
|
-
const index = typeof path === 'string' ? path.indexOf('?') : -1
|
|
305
|
-
if (index === -1) {
|
|
306
|
-
return path
|
|
307
|
-
}
|
|
308
|
-
const hashIndex = path.indexOf('#', index)
|
|
309
|
-
if (hashIndex === -1) {
|
|
310
|
-
return path.substring(0, index)
|
|
311
|
-
}
|
|
312
|
-
return path.substring(0, index) + path.substring(hashIndex)
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function getPath(path, baseURL='/')
|
|
316
|
-
{
|
|
317
|
-
if (path.substring(0,baseURL.length)==baseURL
|
|
318
|
-
||
|
|
319
|
-
( baseURL[baseURL.length-1]=='/'
|
|
320
|
-
&& path.length==(baseURL.length-1)
|
|
321
|
-
&& path == baseURL.substring(0,path.length)
|
|
322
|
-
)
|
|
323
|
-
) {
|
|
324
|
-
path = path.substring(baseURL.length)
|
|
325
|
-
}
|
|
326
|
-
if (path[0]!='/') {
|
|
327
|
-
path = '/'+path
|
|
328
|
-
}
|
|
329
|
-
return path
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function getURL(path, baseURL)
|
|
333
|
-
{
|
|
334
|
-
path = getPath(path, baseURL)
|
|
335
|
-
if (baseURL[baseURL.length-1]==='/' && path[0]==='/') {
|
|
336
|
-
path = path.substring(1)
|
|
337
|
-
}
|
|
338
|
-
if (path[0]=='#') {
|
|
339
|
-
return path
|
|
340
|
-
}
|
|
341
|
-
return baseURL + path
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
function compileRoutePattern(path, exact=false)
|
|
345
|
-
{
|
|
346
|
-
const params = []
|
|
347
|
-
const regexp = routeRegexp(path, exact, params)
|
|
348
|
-
return {
|
|
349
|
-
path,
|
|
350
|
-
params,
|
|
351
|
-
regexp,
|
|
352
|
-
match(value) {
|
|
353
|
-
const matches = regexp.exec(value)
|
|
354
|
-
if (!matches) {
|
|
355
|
-
return null
|
|
356
|
-
}
|
|
357
|
-
const result = {}
|
|
358
|
-
params.forEach((name, i) => {
|
|
359
|
-
result[name] = matches[i + 1]
|
|
360
|
-
})
|
|
361
|
-
return result
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function routeRegexp(route, exact=false, params=[])
|
|
367
|
-
{
|
|
368
|
-
if (route.includes(':*')) {
|
|
369
|
-
throw new TypeError(`simplyflow/route: route "${route}" uses the old wildcard syntax ":*". Use a named wildcard like ":path*" instead.`)
|
|
370
|
-
}
|
|
371
|
-
const prefix = route[0] === '#' ? '' : '^'
|
|
372
|
-
const suffix = exact ? '$' : ''
|
|
373
|
-
return new RegExp(prefix + routeRegexpSource(route, params) + suffix)
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function routeRegexpSource(route, params)
|
|
377
|
-
{
|
|
378
|
-
let source = ''
|
|
379
|
-
let index = 0
|
|
380
|
-
while (index < route.length) {
|
|
381
|
-
if (route[index] === ':') {
|
|
382
|
-
const match = /^:([A-Za-z_][A-Za-z0-9_]*)(\*)?/.exec(route.substring(index))
|
|
383
|
-
if (!match) {
|
|
384
|
-
throw new TypeError(`simplyflow/route: invalid route parameter in "${route}"`)
|
|
385
|
-
}
|
|
386
|
-
params.push(match[1])
|
|
387
|
-
source += match[2] ? '(.*)' : '([^/]+)'
|
|
388
|
-
index += match[0].length
|
|
389
|
-
continue
|
|
390
|
-
}
|
|
391
|
-
if (route[index] === '*') {
|
|
392
|
-
source += '.*'
|
|
393
|
-
index++
|
|
394
|
-
continue
|
|
395
|
-
}
|
|
396
|
-
source += escapeRegexp(route[index])
|
|
397
|
-
index++
|
|
398
|
-
}
|
|
399
|
-
return source
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
function escapeRegexp(value)
|
|
403
|
-
{
|
|
404
|
-
return value.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
function parseRoutes(routes, routeInfo, exact=false)
|
|
408
|
-
{
|
|
409
|
-
const paths = Object.keys(routes)
|
|
410
|
-
for (let path of paths) {
|
|
411
|
-
routeInfo.push({
|
|
412
|
-
path,
|
|
413
|
-
pattern: compileRoutePattern(path, exact),
|
|
414
|
-
action: routes[path]
|
|
415
|
-
})
|
|
416
|
-
}
|
|
417
|
-
return routeInfo
|
|
418
|
-
}
|
|
1
|
+
export * from '@muze-labs/simplyflow-app/route'
|
package/src/shortcut.mjs
CHANGED
|
@@ -1,146 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const accesskeyState = new WeakMap()
|
|
3
|
-
|
|
4
|
-
const KEY = Object.freeze({
|
|
5
|
-
Compose: 229,
|
|
6
|
-
Control: 17,
|
|
7
|
-
Meta: 224,
|
|
8
|
-
Alt: 18,
|
|
9
|
-
Shift: 16
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
class SimplyShortcuts
|
|
13
|
-
{
|
|
14
|
-
constructor(options = {})
|
|
15
|
-
{
|
|
16
|
-
if (!options.app) {
|
|
17
|
-
options.app = {}
|
|
18
|
-
}
|
|
19
|
-
if (!options.app.container) {
|
|
20
|
-
options.app.container = document.body
|
|
21
|
-
}
|
|
22
|
-
Object.assign(this, options.shortcuts)
|
|
23
|
-
|
|
24
|
-
const keyHandler = (e) => {
|
|
25
|
-
let shortcutScopes = []
|
|
26
|
-
let shortcutElement = e.target.closest('[data-simply-shortcuts]')
|
|
27
|
-
while (shortcutElement) {
|
|
28
|
-
shortcutScopes.push(shortcutElement.dataset.simplyShortcuts)
|
|
29
|
-
shortcutElement = shortcutElement.parentNode.closest('[data-simply-shortcuts]')
|
|
30
|
-
}
|
|
31
|
-
if (shortcutScopes[shortcutScopes.length-1]!='default') {
|
|
32
|
-
shortcutScopes.push('default')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
let shortcutScope
|
|
36
|
-
let separators = ['+','-']
|
|
37
|
-
|
|
38
|
-
for (let separator of separators) {
|
|
39
|
-
const keyString = getKeyString(e, separator)
|
|
40
|
-
for (let i in shortcutScopes) {
|
|
41
|
-
shortcutScope = shortcutScopes[i]
|
|
42
|
-
if (this[shortcutScope] && (typeof this[shortcutScope][keyString]=='function')) {
|
|
43
|
-
let _continue = this[shortcutScope][keyString].call(options.app, e)
|
|
44
|
-
if (!_continue) {
|
|
45
|
-
e.preventDefault()
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (typeof this[shortcutScope + '.' + keyString] == 'function') {
|
|
50
|
-
let _continue = this[shortcutScope + '.' + keyString].call(options.app, e)
|
|
51
|
-
if (!_continue) {
|
|
52
|
-
e.preventDefault()
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (typeof this[keyString] == 'function') {
|
|
57
|
-
let _continue = this[keyString].call(options.app, e)
|
|
58
|
-
if (!_continue) {
|
|
59
|
-
e.preventDefault()
|
|
60
|
-
return
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const container = options.app.container
|
|
68
|
-
container.addEventListener('keydown', keyHandler)
|
|
69
|
-
shortcutState.set(this, { container, keyHandler })
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function getKeyString(e, separator='+')
|
|
74
|
-
{
|
|
75
|
-
if (e.isComposing || e.keyCode === KEY.Compose) {
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
if (e.defaultPrevented) {
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
|
-
if (!e.target) {
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
let keyCombination = []
|
|
86
|
-
if (e.ctrlKey && e.keyCode!=KEY.Control) {
|
|
87
|
-
keyCombination.push('Control')
|
|
88
|
-
}
|
|
89
|
-
if (e.metaKey && e.keyCode!=KEY.Meta) {
|
|
90
|
-
keyCombination.push('Meta')
|
|
91
|
-
}
|
|
92
|
-
if (e.altKey && e.keyCode!=KEY.Alt) {
|
|
93
|
-
keyCombination.push('Alt')
|
|
94
|
-
}
|
|
95
|
-
if (e.shiftKey && e.keyCode!=KEY.Shift) {
|
|
96
|
-
keyCombination.push('Shift')
|
|
97
|
-
}
|
|
98
|
-
keyCombination.push(e.key.toLowerCase())
|
|
99
|
-
return keyCombination.join(separator)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function shortcuts(options={})
|
|
103
|
-
{
|
|
104
|
-
return new SimplyShortcuts(options)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function destroyShortcuts(shortcutApi)
|
|
108
|
-
{
|
|
109
|
-
const state = shortcutState.get(shortcutApi)
|
|
110
|
-
if (!state) {
|
|
111
|
-
return
|
|
112
|
-
}
|
|
113
|
-
state.container.removeEventListener('keydown', state.keyHandler)
|
|
114
|
-
shortcutState.delete(shortcutApi)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function accesskeys(options={}) {
|
|
118
|
-
const container = options.container || options.app?.container || document.body
|
|
119
|
-
const keyHandler = (e) => {
|
|
120
|
-
const separators = ["+", "-"]
|
|
121
|
-
for (const separator of separators) {
|
|
122
|
-
const keyString = getKeyString(e, separator)
|
|
123
|
-
const selector = "[data-simply-accesskey='" + keyString + "']"
|
|
124
|
-
const targets = container.querySelectorAll(selector)
|
|
125
|
-
if (targets.length) {
|
|
126
|
-
targets.forEach(function(target) {
|
|
127
|
-
target.click()
|
|
128
|
-
})
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
container.addEventListener('keydown', keyHandler)
|
|
133
|
-
const controller = {}
|
|
134
|
-
accesskeyState.set(controller, { container, keyHandler })
|
|
135
|
-
return controller
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function destroyAccesskeys(accesskeyApi)
|
|
139
|
-
{
|
|
140
|
-
const state = accesskeyState.get(accesskeyApi)
|
|
141
|
-
if (!state) {
|
|
142
|
-
return
|
|
143
|
-
}
|
|
144
|
-
state.container.removeEventListener('keydown', state.keyHandler)
|
|
145
|
-
accesskeyState.delete(accesskeyApi)
|
|
146
|
-
}
|
|
1
|
+
export * from '@muze-labs/simplyflow-app/shortcut'
|