@symbo.ls/router 3.5.1 → 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/LICENSE +34 -0
- package/README.md +234 -0
- package/dist/cjs/index.js +281 -0
- package/dist/esm/index.js +261 -0
- package/dist/iife/index.js +754 -0
- package/index.js +186 -260
- package/package.json +32 -7
- package/location.js +0 -3
package/index.js
CHANGED
|
@@ -1,140 +1,106 @@
|
|
|
1
|
-
|
|
2
|
-
const { setCookie, merge } = utils.default || utils
|
|
1
|
+
'use strict'
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
import { document, window, triggerEventOn } from '@symbo.ls/utils'
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
index = 0,
|
|
8
|
-
route = window.location.pathname
|
|
9
|
-
) => {
|
|
10
|
-
const routeArray = route.split('/')
|
|
11
|
-
const activeRoute = routeArray[index + 1]
|
|
12
|
-
if (activeRoute) {
|
|
13
|
-
return `/${activeRoute}`
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const getLastLocationPath = (
|
|
18
|
-
level = 0,
|
|
19
|
-
route = window.location.pathname
|
|
20
|
-
) => {
|
|
21
|
-
const routeArray = route.split('/')
|
|
22
|
-
const activeRoute = routeArray[routeArray.length - level - 1]
|
|
23
|
-
if (activeRoute) {
|
|
24
|
-
return `/${activeRoute}`
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// --- Dynamic route matching ---
|
|
5
|
+
// --- Route matching utilities ---
|
|
29
6
|
|
|
30
7
|
const paramPattern = /^:(.+)/
|
|
8
|
+
const wildcardPattern = /^\*$/
|
|
9
|
+
|
|
31
10
|
const routeCache = new Map()
|
|
32
11
|
|
|
33
|
-
export const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const params = {}
|
|
12
|
+
export const parseRoutePattern = (pattern) => {
|
|
13
|
+
const cached = routeCache.get(pattern)
|
|
14
|
+
if (cached) return cached
|
|
37
15
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
16
|
+
const segments = pattern.replace(/^\//, '').split('/')
|
|
17
|
+
const params = []
|
|
18
|
+
let hasWildcard = false
|
|
19
|
+
|
|
20
|
+
for (let i = 0; i < segments.length; i++) {
|
|
21
|
+
const match = segments[i].match(paramPattern)
|
|
22
|
+
if (match) {
|
|
23
|
+
params.push({ index: i, name: match[1] })
|
|
24
|
+
} else if (wildcardPattern.test(segments[i])) {
|
|
25
|
+
hasWildcard = true
|
|
42
26
|
}
|
|
43
27
|
}
|
|
44
28
|
|
|
45
|
-
|
|
29
|
+
const result = { segments, params, hasWildcard, pattern }
|
|
30
|
+
routeCache.set(pattern, result)
|
|
31
|
+
return result
|
|
46
32
|
}
|
|
47
33
|
|
|
48
|
-
export const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
34
|
+
export const matchRoute = (pathname, routes, level = 0) => {
|
|
35
|
+
const pathSegments = pathname.replace(/^\//, '').split('/').filter(Boolean)
|
|
36
|
+
const relevantSegments = pathSegments.slice(level)
|
|
37
|
+
const routePath = '/' + (relevantSegments[0] || '')
|
|
52
38
|
|
|
53
|
-
// Fast path: direct match
|
|
54
|
-
if (routes[routeKey]) {
|
|
55
|
-
return { key: routeKey, content: routes[routeKey], params: {} }
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Dynamic param matching
|
|
59
39
|
let bestMatch = null
|
|
60
40
|
let bestScore = -1
|
|
61
|
-
let
|
|
41
|
+
let matchedParams = {}
|
|
62
42
|
|
|
63
43
|
for (const key in routes) {
|
|
64
44
|
if (key === '/*') continue
|
|
65
|
-
const keyParts = key.replace(/^\//, '').split('/')
|
|
66
|
-
|
|
67
|
-
let score = 0
|
|
68
|
-
let matched = true
|
|
69
|
-
const params = {}
|
|
70
|
-
const len = Math.min(keyParts.length, relevantParts.length)
|
|
71
|
-
|
|
72
|
-
if (keyParts.length > relevantParts.length) continue
|
|
73
|
-
|
|
74
|
-
for (let i = 0; i < len; i++) {
|
|
75
|
-
const paramMatch = keyParts[i].match(paramPattern)
|
|
76
|
-
if (keyParts[i] === relevantParts[i]) {
|
|
77
|
-
score += 3
|
|
78
|
-
} else if (paramMatch) {
|
|
79
|
-
params[paramMatch[1]] = decodeURIComponent(relevantParts[i])
|
|
80
|
-
score += 1
|
|
81
|
-
} else {
|
|
82
|
-
matched = false
|
|
83
|
-
break
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
45
|
|
|
87
|
-
|
|
46
|
+
const parsed = parseRoutePattern(key)
|
|
47
|
+
const score = scoreMatch(relevantSegments, parsed)
|
|
48
|
+
|
|
49
|
+
if (score > bestScore) {
|
|
88
50
|
bestScore = score
|
|
89
51
|
bestMatch = key
|
|
90
|
-
|
|
52
|
+
matchedParams = extractParams(relevantSegments, parsed)
|
|
91
53
|
}
|
|
92
54
|
}
|
|
93
55
|
|
|
94
|
-
if (bestMatch) {
|
|
95
|
-
|
|
56
|
+
if (!bestMatch && routes['/*']) {
|
|
57
|
+
bestMatch = '/*'
|
|
96
58
|
}
|
|
97
59
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
60
|
+
return {
|
|
61
|
+
key: bestMatch,
|
|
62
|
+
content: bestMatch ? routes[bestMatch] : null,
|
|
63
|
+
params: matchedParams,
|
|
64
|
+
routePath
|
|
101
65
|
}
|
|
102
|
-
|
|
103
|
-
return { key: null, content: null, params: {} }
|
|
104
66
|
}
|
|
105
67
|
|
|
106
|
-
|
|
68
|
+
const scoreMatch = (pathSegments, parsed) => {
|
|
69
|
+
const { segments, hasWildcard } = parsed
|
|
107
70
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
71
|
+
if (!hasWildcard && segments.length !== pathSegments.length &&
|
|
72
|
+
segments.length !== 1) {
|
|
73
|
+
// For single-segment patterns, match just the first segment
|
|
74
|
+
if (segments.length > pathSegments.length) return -1
|
|
75
|
+
}
|
|
113
76
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (!match.content) break
|
|
77
|
+
let score = 0
|
|
78
|
+
const len = Math.min(segments.length, pathSegments.length)
|
|
117
79
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
// Check for nested routes
|
|
126
|
-
if (match.content.routes) {
|
|
127
|
-
currentRoutes = match.content.routes
|
|
128
|
-
currentLevel++
|
|
80
|
+
for (let i = 0; i < len; i++) {
|
|
81
|
+
if (segments[i] === pathSegments[i]) {
|
|
82
|
+
score += 3 // exact match
|
|
83
|
+
} else if (paramPattern.test(segments[i])) {
|
|
84
|
+
score += 1 // param match
|
|
85
|
+
} else if (wildcardPattern.test(segments[i])) {
|
|
86
|
+
score += 0.5
|
|
129
87
|
} else {
|
|
130
|
-
|
|
88
|
+
return -1 // no match
|
|
131
89
|
}
|
|
132
90
|
}
|
|
133
91
|
|
|
134
|
-
return
|
|
92
|
+
return score
|
|
135
93
|
}
|
|
136
94
|
|
|
137
|
-
|
|
95
|
+
const extractParams = (pathSegments, parsed) => {
|
|
96
|
+
const params = {}
|
|
97
|
+
for (const { index, name } of parsed.params) {
|
|
98
|
+
if (pathSegments[index]) {
|
|
99
|
+
params[name] = decodeURIComponent(pathSegments[index])
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return params
|
|
103
|
+
}
|
|
138
104
|
|
|
139
105
|
export const parseQuery = (search) => {
|
|
140
106
|
if (!search || search === '?') return {}
|
|
@@ -151,7 +117,7 @@ export const parseQuery = (search) => {
|
|
|
151
117
|
return params
|
|
152
118
|
}
|
|
153
119
|
|
|
154
|
-
// ---
|
|
120
|
+
// --- Route guards / middleware ---
|
|
155
121
|
|
|
156
122
|
export const runGuards = async (guards, context) => {
|
|
157
123
|
if (!guards || !guards.length) return true
|
|
@@ -163,234 +129,194 @@ export const runGuards = async (guards, context) => {
|
|
|
163
129
|
return true
|
|
164
130
|
}
|
|
165
131
|
|
|
166
|
-
// ---
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
win.onpopstate = async (e) => {
|
|
174
|
-
const { pathname, search, hash } = win.location
|
|
175
|
-
const url = pathname + search + hash
|
|
176
|
-
if (element && element.call) {
|
|
177
|
-
await element.call(
|
|
178
|
-
'router',
|
|
179
|
-
url,
|
|
180
|
-
element,
|
|
181
|
-
{},
|
|
182
|
-
{ pushState: false, scrollToTop: false, level: 0, event: e }
|
|
183
|
-
)
|
|
184
|
-
} else {
|
|
185
|
-
await router(url, element, {}, { pushState: false, scrollToTop: false, level: 0, event: e })
|
|
186
|
-
}
|
|
187
|
-
}
|
|
132
|
+
// --- Core router ---
|
|
133
|
+
|
|
134
|
+
export const getActiveRoute = (level = 0, route) => {
|
|
135
|
+
if (!route) route = (typeof window !== 'undefined' ? window.location.pathname : '/')
|
|
136
|
+
const routeArray = route.split('/')
|
|
137
|
+
const activeRoute = routeArray[level + 1]
|
|
138
|
+
if (activeRoute) return `/${activeRoute}`
|
|
188
139
|
}
|
|
189
140
|
|
|
190
|
-
export let lastPathname
|
|
141
|
+
export let lastPathname
|
|
191
142
|
export let lastLevel = 0
|
|
192
143
|
|
|
144
|
+
const normalizePath = (p) => (!p || p === 'srcdoc' || p === 'about:srcdoc') ? '/' : p
|
|
145
|
+
|
|
193
146
|
const defaultOptions = {
|
|
194
|
-
window: globalThis,
|
|
195
147
|
level: lastLevel,
|
|
196
|
-
initialRender: false,
|
|
197
148
|
pushState: true,
|
|
149
|
+
initialRender: false,
|
|
198
150
|
scrollToTop: true,
|
|
199
151
|
scrollToNode: false,
|
|
200
|
-
|
|
152
|
+
scrollNode: document && document.documentElement,
|
|
153
|
+
scrollBody: false,
|
|
201
154
|
useFragment: false,
|
|
202
|
-
authCheck: true,
|
|
203
155
|
updateState: true,
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
preventUpdateTriggerStateUpdate: true
|
|
207
|
-
},
|
|
208
|
-
preventUpdate: ['Canvas', 'Modal'],
|
|
209
|
-
setOptions: {},
|
|
210
|
-
scrollToOffset: 120,
|
|
156
|
+
scrollToOffset: 0,
|
|
157
|
+
contentElementKey: 'content',
|
|
211
158
|
scrollToOptions: { behavior: 'smooth' },
|
|
212
|
-
useParamsMatching:
|
|
213
|
-
useNestedRoutes: false
|
|
159
|
+
useParamsMatching: true
|
|
214
160
|
}
|
|
215
161
|
|
|
216
|
-
const
|
|
162
|
+
export const router = async (path, el, state = {}, options = {}) => {
|
|
163
|
+
const element = el || this
|
|
164
|
+
const win = element?.context?.window || window
|
|
165
|
+
const doc = element?.context?.document || document
|
|
166
|
+
const opts = {
|
|
167
|
+
...defaultOptions,
|
|
168
|
+
...element.context.routerOptions,
|
|
169
|
+
...options
|
|
170
|
+
}
|
|
171
|
+
lastLevel = opts.lastLevel
|
|
217
172
|
|
|
218
|
-
|
|
219
|
-
export async function router(path = '/', el, state = {}, passedOptions = {}) {
|
|
220
|
-
const rootElem = this && this.getRoot()
|
|
221
|
-
const element = el && el.routes ? el : rootElem
|
|
222
|
-
const options = merge(passedOptions, defaultOptions)
|
|
223
|
-
lastLevel = options.level
|
|
173
|
+
const ref = element.__ref
|
|
224
174
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
element.call('closeModal')
|
|
232
|
-
if (options.event) { options.event.preventDefault() }
|
|
175
|
+
if (
|
|
176
|
+
(opts.contentElementKey !== 'content' &&
|
|
177
|
+
opts.contentElementKey !== ref.contentElementKey) ||
|
|
178
|
+
!ref.contentElementKey
|
|
179
|
+
) {
|
|
180
|
+
ref.contentElementKey = opts.contentElementKey || 'content'
|
|
233
181
|
}
|
|
234
182
|
|
|
235
|
-
const
|
|
183
|
+
const contentElementKey = ref.contentElementKey || opts.contentElementKey || 'content'
|
|
184
|
+
|
|
185
|
+
const origin = win.location.origin !== 'null' ? win.location.origin : 'http://localhost'
|
|
186
|
+
path = normalizePath(path)
|
|
187
|
+
const urlObj = new win.URL(origin + path)
|
|
236
188
|
const { pathname, search, hash } = urlObj
|
|
237
189
|
|
|
238
190
|
const query = parseQuery(search)
|
|
239
191
|
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const authRedirect = element.call('redirectBasedOnAuth', rootRoute)
|
|
245
|
-
if (authRedirect && rootRoute !== authRedirect) {
|
|
246
|
-
routeArray = authRedirect.split('/')
|
|
247
|
-
}
|
|
248
|
-
}
|
|
192
|
+
const rootNode = element.node
|
|
193
|
+
const hashChanged = hash && hash !== win.location.hash.slice(1)
|
|
194
|
+
const pathChanged = pathname !== lastPathname
|
|
195
|
+
lastPathname = pathname
|
|
249
196
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const chain = resolveNestedRoute(element.routes, pathname, lastLevel)
|
|
256
|
-
const leaf = chain[chain.length - 1]
|
|
257
|
-
if (leaf) {
|
|
258
|
-
routeInContent = leaf.key
|
|
259
|
-
content = leaf.content
|
|
260
|
-
params = chain.reduce((acc, c) => ({ ...acc, ...c.params }), {})
|
|
261
|
-
}
|
|
262
|
-
} else if (options.useParamsMatching) {
|
|
263
|
-
const match = matchRoutes(element.routes, pathname, lastLevel)
|
|
264
|
-
routeInContent = match.key
|
|
197
|
+
// Route matching - support both simple and param-based
|
|
198
|
+
let route, content, params
|
|
199
|
+
if (opts.useParamsMatching) {
|
|
200
|
+
const match = matchRoute(pathname, element.routes, opts.level)
|
|
201
|
+
route = match.routePath
|
|
265
202
|
content = match.content
|
|
266
203
|
params = match.params
|
|
267
204
|
} else {
|
|
268
|
-
|
|
269
|
-
content = element.routes[
|
|
205
|
+
route = getActiveRoute(opts.level, pathname)
|
|
206
|
+
content = element.routes[route || '/'] || element.routes['/*']
|
|
270
207
|
params = {}
|
|
271
208
|
}
|
|
272
209
|
|
|
273
|
-
|
|
274
|
-
if (options.guards && options.guards.length) {
|
|
275
|
-
const guardCtx = { pathname, route, routeInContent, params, query, hash, element, state }
|
|
276
|
-
const guardResult = await runGuards(options.guards, guardCtx)
|
|
277
|
-
if (guardResult === false) return
|
|
278
|
-
if (typeof guardResult === 'string') {
|
|
279
|
-
return router.call(this, guardResult, el, state, { ...passedOptions, guards: [] })
|
|
280
|
-
}
|
|
281
|
-
}
|
|
210
|
+
const scrollNode = opts.scrollToNode ? rootNode : opts.scrollNode
|
|
282
211
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const isInitial = options.initialRender
|
|
288
|
-
const hashChanged = hash && hash !== options.window.location.hash
|
|
289
|
-
const pathChanged = pathname !== lastPathname
|
|
290
|
-
lastPathname = pathname
|
|
212
|
+
if (element.state?.root?.debugging) {
|
|
213
|
+
element.state.root.debugging = false
|
|
214
|
+
return
|
|
215
|
+
}
|
|
291
216
|
|
|
292
|
-
if (
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
options.onNotFound({ pathname, route, routeInContent, element })
|
|
217
|
+
if (!content) {
|
|
218
|
+
if (opts.onNotFound) {
|
|
219
|
+
opts.onNotFound({ pathname, route, element })
|
|
220
|
+
} else if (!opts.silent) {
|
|
221
|
+
console.warn('[smbls/router] no content matched for path', pathname, '— available routes:', Object.keys(element.routes || {}))
|
|
298
222
|
}
|
|
299
223
|
return
|
|
300
224
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
225
|
+
|
|
226
|
+
// Run guards
|
|
227
|
+
if (opts.guards && opts.guards.length) {
|
|
228
|
+
const guardContext = { pathname, route, params, query, hash, element, state }
|
|
229
|
+
const guardResult = await runGuards(opts.guards, guardContext)
|
|
230
|
+
if (guardResult === false) return
|
|
231
|
+
if (typeof guardResult === 'string') {
|
|
232
|
+
// Redirect
|
|
233
|
+
return router(guardResult, el, state, { ...options, guards: [] })
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// pushState always fires before render so the URL bar stays in sync even
|
|
238
|
+
// if the content-render step below no-ops due to an extends/ref mismatch.
|
|
239
|
+
if (opts.pushState) {
|
|
240
|
+
try {
|
|
241
|
+
win.history.pushState(state, null, pathname + (search || '') + (hash || ''))
|
|
242
|
+
} catch (e) {} // expected in sandboxed iframes (e.g. about:srcdoc) where pushState is restricted
|
|
310
243
|
}
|
|
311
244
|
|
|
312
245
|
if (pathChanged || !hashChanged) {
|
|
313
|
-
const stateUpdate = { route, hash,
|
|
246
|
+
const stateUpdate = { route, hash, debugging: false }
|
|
314
247
|
if (Object.keys(params).length) stateUpdate.params = params
|
|
315
248
|
if (Object.keys(query).length) stateUpdate.query = query
|
|
316
249
|
|
|
317
|
-
if (
|
|
318
|
-
|
|
250
|
+
if (opts.updateState) {
|
|
251
|
+
element.state.update(
|
|
319
252
|
stateUpdate,
|
|
320
|
-
{
|
|
321
|
-
preventUpdate: [options.preventUpdate, 'content'],
|
|
322
|
-
updateByRouter: true,
|
|
323
|
-
...options.updateStateOptions
|
|
324
|
-
}
|
|
253
|
+
{ preventContentUpdate: true }
|
|
325
254
|
)
|
|
326
255
|
}
|
|
327
256
|
|
|
328
|
-
if (
|
|
329
|
-
element.
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
{
|
|
343
|
-
tag: options.useFragment && 'fragment',
|
|
344
|
-
extends: content
|
|
345
|
-
},
|
|
346
|
-
{
|
|
347
|
-
lastElement,
|
|
348
|
-
routerContentElement: content,
|
|
349
|
-
preventUpdate: options.preventUpdate,
|
|
350
|
-
...options.setOptions
|
|
257
|
+
if (contentElementKey && opts.removeOldElement) {
|
|
258
|
+
element[contentElementKey].remove()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Merge parent's original content definition (styles like overflow, maxHeight)
|
|
262
|
+
// with the route's content component
|
|
263
|
+
const originContent = element.__ref?.origin?.content
|
|
264
|
+
const contentStyles = {}
|
|
265
|
+
if (originContent) {
|
|
266
|
+
for (const k in originContent) {
|
|
267
|
+
const v = originContent[k]
|
|
268
|
+
if (k === '__ref' || k === 'props' || k === 'node' || k === 'parent' || k === 'key') continue
|
|
269
|
+
if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || (typeof v === 'object' && v !== null && !v.node && !v.__ref)) {
|
|
270
|
+
contentStyles[k] = v
|
|
351
271
|
}
|
|
352
|
-
|
|
272
|
+
}
|
|
353
273
|
}
|
|
354
|
-
|
|
355
|
-
|
|
274
|
+
|
|
275
|
+
const nextContent = {
|
|
276
|
+
...contentStyles,
|
|
277
|
+
...(typeof content === 'object' ? content : { extends: content })
|
|
278
|
+
}
|
|
279
|
+
if (opts.useFragment) nextContent.tag = 'fragment'
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
element.set(nextContent, { contentElementKey })
|
|
283
|
+
} catch (err) {
|
|
284
|
+
console.error('[smbls/router] failed to render route content', pathname, err)
|
|
356
285
|
}
|
|
357
286
|
}
|
|
358
287
|
|
|
359
|
-
|
|
360
|
-
const scrollNode = options.scrollDocument
|
|
361
|
-
? options.window.document.documentElement
|
|
362
|
-
: rootNode
|
|
363
|
-
if (options.scrollToTop) {
|
|
288
|
+
if (opts.scrollToTop && scrollNode?.scrollTo) {
|
|
364
289
|
scrollNode.scrollTo({
|
|
365
|
-
...(
|
|
290
|
+
...(opts.scrollToOptions || {}),
|
|
366
291
|
top: 0,
|
|
367
292
|
left: 0
|
|
368
293
|
})
|
|
369
294
|
}
|
|
370
|
-
if (
|
|
371
|
-
content.
|
|
372
|
-
...(
|
|
295
|
+
if (opts.scrollToNode && content[contentElementKey]?.node?.scrollTo) {
|
|
296
|
+
content[contentElementKey].node.scrollTo({
|
|
297
|
+
...(opts.scrollToOptions || {}),
|
|
373
298
|
top: 0,
|
|
374
299
|
left: 0
|
|
375
300
|
})
|
|
376
301
|
}
|
|
377
302
|
|
|
378
303
|
if (hash) {
|
|
379
|
-
const activeNode =
|
|
380
|
-
if (activeNode) {
|
|
304
|
+
const activeNode = doc.getElementById(hash)
|
|
305
|
+
if (activeNode && scrollNode?.scrollTo) {
|
|
381
306
|
const top =
|
|
382
307
|
activeNode.getBoundingClientRect().top +
|
|
383
308
|
rootNode.scrollTop -
|
|
384
|
-
(
|
|
309
|
+
(opts.scrollToOffset || 0)
|
|
385
310
|
scrollNode.scrollTo({
|
|
386
|
-
...(
|
|
311
|
+
...(opts.scrollToOptions || {}),
|
|
387
312
|
top,
|
|
388
313
|
left: 0
|
|
389
314
|
})
|
|
390
315
|
}
|
|
391
316
|
}
|
|
392
317
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
}
|
|
318
|
+
// trigger `on.routeChanged`
|
|
319
|
+
triggerEventOn('routeChanged', element, opts)
|
|
396
320
|
}
|
|
321
|
+
|
|
322
|
+
export default router
|
package/package.json
CHANGED
|
@@ -1,15 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbo.ls/router",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"
|
|
5
|
-
"license": "MIT",
|
|
3
|
+
"version": "3.14.0",
|
|
4
|
+
"license": "CC-BY-NC-4.0",
|
|
6
5
|
"type": "module",
|
|
6
|
+
"module": "./index.js",
|
|
7
|
+
"unpkg": "./dist/iife/index.js",
|
|
8
|
+
"jsdelivr": "./dist/iife/index.js",
|
|
9
|
+
"main": "./index.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./index.js",
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"source": "index.js",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"*.js"
|
|
18
|
+
],
|
|
7
19
|
"scripts": {
|
|
8
|
-
"
|
|
20
|
+
"copy:package:cjs": "cp ../../build/package-cjs.json dist/cjs/package.json",
|
|
21
|
+
"build:esm": "NODE_ENV=$NODE_ENV esbuild *.js --target=es2020 --format=esm --outdir=dist/esm --define:process.env.NODE_ENV=process.env.NODE_ENV",
|
|
22
|
+
"build:cjs": "NODE_ENV=$NODE_ENV esbuild *.js --target=node18 --format=cjs --outdir=dist/cjs --define:process.env.NODE_ENV=process.env.NODE_ENV",
|
|
23
|
+
"build:iife": "NODE_ENV=$NODE_ENV esbuild index.js --bundle --target=es2020 --format=iife --global-name=DomqlRouter --outfile=dist/iife/index.js --define:process.env.NODE_ENV=process.env.NODE_ENV",
|
|
24
|
+
"build": "node ../../build/build.js",
|
|
25
|
+
"prepublish": "npm run build && npm run copy:package:cjs"
|
|
9
26
|
},
|
|
10
27
|
"dependencies": {
|
|
11
|
-
"@
|
|
28
|
+
"@symbo.ls/element": "^3.14.0",
|
|
29
|
+
"@symbo.ls/utils": "^3.14.0"
|
|
30
|
+
},
|
|
31
|
+
"gitHead": "9fc1b79b41cdc725ca6b24aec64920a599634681",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@babel/core": "^7.26.0"
|
|
12
34
|
},
|
|
13
|
-
"
|
|
14
|
-
"
|
|
35
|
+
"browser": "./index.js",
|
|
36
|
+
"sideEffects": false,
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
}
|
|
15
40
|
}
|
package/location.js
DELETED