kdu-router 3.4.0-beta.0 → 3.6.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.
Files changed (49) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +11 -9
  3. package/dist/composables.js +253 -0
  4. package/dist/composables.mjs +244 -0
  5. package/dist/kdu-router.common.js +2682 -2572
  6. package/dist/kdu-router.esm.browser.js +2677 -2563
  7. package/dist/kdu-router.esm.browser.min.js +5 -5
  8. package/dist/kdu-router.esm.js +2685 -2572
  9. package/dist/kdu-router.js +2682 -2573
  10. package/dist/kdu-router.min.js +5 -5
  11. package/ketur/attributes.json +38 -38
  12. package/ketur/tags.json +20 -20
  13. package/package.json +115 -111
  14. package/src/components/link.js +224 -197
  15. package/src/components/view.js +155 -149
  16. package/src/composables/globals.js +34 -0
  17. package/src/composables/guards.js +68 -0
  18. package/src/composables/index.js +3 -0
  19. package/src/composables/useLink.js +113 -0
  20. package/src/composables/utils.js +11 -0
  21. package/src/create-matcher.js +226 -200
  22. package/src/create-route-map.js +220 -205
  23. package/src/entries/cjs.js +3 -0
  24. package/src/entries/esm.js +12 -0
  25. package/src/history/abstract.js +72 -68
  26. package/src/history/base.js +379 -400
  27. package/src/history/hash.js +152 -163
  28. package/src/history/html5.js +99 -94
  29. package/src/index.js +3 -277
  30. package/src/install.js +52 -52
  31. package/src/router.js +293 -0
  32. package/src/util/async.js +18 -18
  33. package/src/util/dom.js +3 -3
  34. package/src/util/errors.js +86 -85
  35. package/src/util/location.js +69 -69
  36. package/src/util/misc.js +6 -6
  37. package/src/util/params.js +37 -37
  38. package/src/util/path.js +74 -74
  39. package/src/util/push-state.js +46 -46
  40. package/src/util/query.js +113 -96
  41. package/src/util/resolve-components.js +109 -109
  42. package/src/util/route.js +151 -132
  43. package/src/util/scroll.js +175 -165
  44. package/src/util/state-key.js +22 -22
  45. package/src/util/warn.js +14 -14
  46. package/types/composables.d.ts +53 -0
  47. package/types/index.d.ts +25 -17
  48. package/types/kdu.d.ts +22 -22
  49. package/types/router.d.ts +564 -170
@@ -1,197 +1,224 @@
1
- /* @flow */
2
-
3
- import { createRoute, isSameRoute, isIncludedRoute } from '../util/route'
4
- import { extend } from '../util/misc'
5
- import { normalizeLocation } from '../util/location'
6
- import { warn } from '../util/warn'
7
-
8
- // work around weird flow bug
9
- const toTypes: Array<Function> = [String, Object]
10
- const eventTypes: Array<Function> = [String, Array]
11
-
12
- const noop = () => {}
13
-
14
- export default {
15
- name: 'RouterLink',
16
- props: {
17
- to: {
18
- type: toTypes,
19
- required: true
20
- },
21
- tag: {
22
- type: String,
23
- default: 'a'
24
- },
25
- exact: Boolean,
26
- append: Boolean,
27
- replace: Boolean,
28
- activeClass: String,
29
- exactActiveClass: String,
30
- ariaCurrentValue: {
31
- type: String,
32
- default: 'page'
33
- },
34
- event: {
35
- type: eventTypes,
36
- default: 'click'
37
- }
38
- },
39
- render (h: Function) {
40
- const router = this.$router
41
- const current = this.$route
42
- const { location, route, href } = router.resolve(
43
- this.to,
44
- current,
45
- this.append
46
- )
47
-
48
- const classes = {}
49
- const globalActiveClass = router.options.linkActiveClass
50
- const globalExactActiveClass = router.options.linkExactActiveClass
51
- // Support global empty active class
52
- const activeClassFallback =
53
- globalActiveClass == null ? 'router-link-active' : globalActiveClass
54
- const exactActiveClassFallback =
55
- globalExactActiveClass == null
56
- ? 'router-link-exact-active'
57
- : globalExactActiveClass
58
- const activeClass =
59
- this.activeClass == null ? activeClassFallback : this.activeClass
60
- const exactActiveClass =
61
- this.exactActiveClass == null
62
- ? exactActiveClassFallback
63
- : this.exactActiveClass
64
-
65
- const compareTarget = route.redirectedFrom
66
- ? createRoute(null, normalizeLocation(route.redirectedFrom), null, router)
67
- : route
68
-
69
- classes[exactActiveClass] = isSameRoute(current, compareTarget)
70
- classes[activeClass] = this.exact
71
- ? classes[exactActiveClass]
72
- : isIncludedRoute(current, compareTarget)
73
-
74
- const ariaCurrentValue = classes[exactActiveClass] ? this.ariaCurrentValue : null
75
-
76
- const handler = e => {
77
- if (guardEvent(e)) {
78
- if (this.replace) {
79
- router.replace(location, noop)
80
- } else {
81
- router.push(location, noop)
82
- }
83
- }
84
- }
85
-
86
- const on = { click: guardEvent }
87
- if (Array.isArray(this.event)) {
88
- this.event.forEach(e => {
89
- on[e] = handler
90
- })
91
- } else {
92
- on[this.event] = handler
93
- }
94
-
95
- const data: any = { class: classes }
96
-
97
- const scopedSlot =
98
- !this.$scopedSlots.$hasNormal &&
99
- this.$scopedSlots.default &&
100
- this.$scopedSlots.default({
101
- href,
102
- route,
103
- navigate: handler,
104
- isActive: classes[activeClass],
105
- isExactActive: classes[exactActiveClass]
106
- })
107
-
108
- if (scopedSlot) {
109
- if (scopedSlot.length === 1) {
110
- return scopedSlot[0]
111
- } else if (scopedSlot.length > 1 || !scopedSlot.length) {
112
- if (process.env.NODE_ENV !== 'production') {
113
- warn(
114
- false,
115
- `RouterLink with to="${
116
- this.to
117
- }" is trying to use a scoped slot but it didn't provide exactly one child. Wrapping the content with a span element.`
118
- )
119
- }
120
- return scopedSlot.length === 0 ? h() : h('span', {}, scopedSlot)
121
- }
122
- }
123
-
124
- if (this.tag === 'a') {
125
- data.on = on
126
- data.attrs = { href, 'aria-current': ariaCurrentValue }
127
- } else {
128
- // find the first <a> child and apply listener and href
129
- const a = findAnchor(this.$slots.default)
130
- if (a) {
131
- // in case the <a> is a static node
132
- a.isStatic = false
133
- const aData = (a.data = extend({}, a.data))
134
- aData.on = aData.on || {}
135
- // transform existing events in both objects into arrays so we can push later
136
- for (const event in aData.on) {
137
- const handler = aData.on[event]
138
- if (event in on) {
139
- aData.on[event] = Array.isArray(handler) ? handler : [handler]
140
- }
141
- }
142
- // append new listeners for router-link
143
- for (const event in on) {
144
- if (event in aData.on) {
145
- // on[event] is always a function
146
- aData.on[event].push(on[event])
147
- } else {
148
- aData.on[event] = handler
149
- }
150
- }
151
-
152
- const aAttrs = (a.data.attrs = extend({}, a.data.attrs))
153
- aAttrs.href = href
154
- aAttrs['aria-current'] = ariaCurrentValue
155
- } else {
156
- // doesn't have <a> child, apply listener to self
157
- data.on = on
158
- }
159
- }
160
-
161
- return h(this.tag, data, this.$slots.default)
162
- }
163
- }
164
-
165
- function guardEvent (e) {
166
- // don't redirect with control keys
167
- if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return
168
- // don't redirect when preventDefault called
169
- if (e.defaultPrevented) return
170
- // don't redirect on right click
171
- if (e.button !== undefined && e.button !== 0) return
172
- // don't redirect if `target="_blank"`
173
- if (e.currentTarget && e.currentTarget.getAttribute) {
174
- const target = e.currentTarget.getAttribute('target')
175
- if (/\b_blank\b/i.test(target)) return
176
- }
177
- // this may be a Weex event which doesn't have this method
178
- if (e.preventDefault) {
179
- e.preventDefault()
180
- }
181
- return true
182
- }
183
-
184
- function findAnchor (children) {
185
- if (children) {
186
- let child
187
- for (let i = 0; i < children.length; i++) {
188
- child = children[i]
189
- if (child.tag === 'a') {
190
- return child
191
- }
192
- if (child.children && (child = findAnchor(child.children))) {
193
- return child
194
- }
195
- }
196
- }
197
- }
1
+ /* @flow */
2
+
3
+ import { createRoute, isSameRoute, isIncludedRoute } from '../util/route'
4
+ import { extend } from '../util/misc'
5
+ import { normalizeLocation } from '../util/location'
6
+ import { warn } from '../util/warn'
7
+
8
+ // work around weird flow bug
9
+ const toTypes: Array<Function> = [String, Object]
10
+ const eventTypes: Array<Function> = [String, Array]
11
+
12
+ const noop = () => {}
13
+
14
+ let warnedCustomSlot
15
+ let warnedTagProp
16
+ let warnedEventProp
17
+
18
+ export default {
19
+ name: 'RouterLink',
20
+ props: {
21
+ to: {
22
+ type: toTypes,
23
+ required: true
24
+ },
25
+ tag: {
26
+ type: String,
27
+ default: 'a'
28
+ },
29
+ custom: Boolean,
30
+ exact: Boolean,
31
+ exactPath: Boolean,
32
+ append: Boolean,
33
+ replace: Boolean,
34
+ activeClass: String,
35
+ exactActiveClass: String,
36
+ ariaCurrentValue: {
37
+ type: String,
38
+ default: 'page'
39
+ },
40
+ event: {
41
+ type: eventTypes,
42
+ default: 'click'
43
+ }
44
+ },
45
+ render (h: Function) {
46
+ const router = this.$router
47
+ const current = this.$route
48
+ const { location, route, href } = router.resolve(
49
+ this.to,
50
+ current,
51
+ this.append
52
+ )
53
+
54
+ const classes = {}
55
+ const globalActiveClass = router.options.linkActiveClass
56
+ const globalExactActiveClass = router.options.linkExactActiveClass
57
+ // Support global empty active class
58
+ const activeClassFallback =
59
+ globalActiveClass == null ? 'router-link-active' : globalActiveClass
60
+ const exactActiveClassFallback =
61
+ globalExactActiveClass == null
62
+ ? 'router-link-exact-active'
63
+ : globalExactActiveClass
64
+ const activeClass =
65
+ this.activeClass == null ? activeClassFallback : this.activeClass
66
+ const exactActiveClass =
67
+ this.exactActiveClass == null
68
+ ? exactActiveClassFallback
69
+ : this.exactActiveClass
70
+
71
+ const compareTarget = route.redirectedFrom
72
+ ? createRoute(null, normalizeLocation(route.redirectedFrom), null, router)
73
+ : route
74
+
75
+ classes[exactActiveClass] = isSameRoute(current, compareTarget, this.exactPath)
76
+ classes[activeClass] = this.exact || this.exactPath
77
+ ? classes[exactActiveClass]
78
+ : isIncludedRoute(current, compareTarget)
79
+
80
+ const ariaCurrentValue = classes[exactActiveClass] ? this.ariaCurrentValue : null
81
+
82
+ const handler = e => {
83
+ if (guardEvent(e)) {
84
+ if (this.replace) {
85
+ router.replace(location, noop)
86
+ } else {
87
+ router.push(location, noop)
88
+ }
89
+ }
90
+ }
91
+
92
+ const on = { click: guardEvent }
93
+ if (Array.isArray(this.event)) {
94
+ this.event.forEach(e => {
95
+ on[e] = handler
96
+ })
97
+ } else {
98
+ on[this.event] = handler
99
+ }
100
+
101
+ const data: any = { class: classes }
102
+
103
+ const scopedSlot =
104
+ !this.$scopedSlots.$hasNormal &&
105
+ this.$scopedSlots.default &&
106
+ this.$scopedSlots.default({
107
+ href,
108
+ route,
109
+ navigate: handler,
110
+ isActive: classes[activeClass],
111
+ isExactActive: classes[exactActiveClass]
112
+ })
113
+
114
+ if (scopedSlot) {
115
+ if (process.env.NODE_ENV !== 'production' && !this.custom) {
116
+ !warnedCustomSlot && warn(false, 'In Kdu Router 4, the k-slot API will by default wrap its content with an <a> element. Use the custom prop to remove this warning:\n<router-link k-slot="{ navigate, href }" custom></router-link>\n')
117
+ warnedCustomSlot = true
118
+ }
119
+ if (scopedSlot.length === 1) {
120
+ return scopedSlot[0]
121
+ } else if (scopedSlot.length > 1 || !scopedSlot.length) {
122
+ if (process.env.NODE_ENV !== 'production') {
123
+ warn(
124
+ false,
125
+ `<router-link> with to="${
126
+ this.to
127
+ }" is trying to use a scoped slot but it didn't provide exactly one child. Wrapping the content with a span element.`
128
+ )
129
+ }
130
+ return scopedSlot.length === 0 ? h() : h('span', {}, scopedSlot)
131
+ }
132
+ }
133
+
134
+ if (process.env.NODE_ENV !== 'production') {
135
+ if ('tag' in this.$options.propsData && !warnedTagProp) {
136
+ warn(
137
+ false,
138
+ `<router-link>'s tag prop is deprecated and has been removed in Kdu Router 4. Use the k-slot API to remove this warning: https://kdujs-router.web.app/guide/migration/#removal-of-event-and-tag-props-in-router-link.`
139
+ )
140
+ warnedTagProp = true
141
+ }
142
+ if ('event' in this.$options.propsData && !warnedEventProp) {
143
+ warn(
144
+ false,
145
+ `<router-link>'s event prop is deprecated and has been removed in Kdu Router 4. Use the k-slot API to remove this warning: https://kdujs-router.web.app/guide/migration/#removal-of-event-and-tag-props-in-router-link.`
146
+ )
147
+ warnedEventProp = true
148
+ }
149
+ }
150
+
151
+ if (this.tag === 'a') {
152
+ data.on = on
153
+ data.attrs = { href, 'aria-current': ariaCurrentValue }
154
+ } else {
155
+ // find the first <a> child and apply listener and href
156
+ const a = findAnchor(this.$slots.default)
157
+ if (a) {
158
+ // in case the <a> is a static node
159
+ a.isStatic = false
160
+ const aData = (a.data = extend({}, a.data))
161
+ aData.on = aData.on || {}
162
+ // transform existing events in both objects into arrays so we can push later
163
+ for (const event in aData.on) {
164
+ const handler = aData.on[event]
165
+ if (event in on) {
166
+ aData.on[event] = Array.isArray(handler) ? handler : [handler]
167
+ }
168
+ }
169
+ // append new listeners for router-link
170
+ for (const event in on) {
171
+ if (event in aData.on) {
172
+ // on[event] is always a function
173
+ aData.on[event].push(on[event])
174
+ } else {
175
+ aData.on[event] = handler
176
+ }
177
+ }
178
+
179
+ const aAttrs = (a.data.attrs = extend({}, a.data.attrs))
180
+ aAttrs.href = href
181
+ aAttrs['aria-current'] = ariaCurrentValue
182
+ } else {
183
+ // doesn't have <a> child, apply listener to self
184
+ data.on = on
185
+ }
186
+ }
187
+
188
+ return h(this.tag, data, this.$slots.default)
189
+ }
190
+ }
191
+
192
+ export function guardEvent (e: any) {
193
+ // don't redirect with control keys
194
+ if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return
195
+ // don't redirect when preventDefault called
196
+ if (e.defaultPrevented) return
197
+ // don't redirect on right click
198
+ if (e.button !== undefined && e.button !== 0) return
199
+ // don't redirect if `target="_blank"`
200
+ if (e.currentTarget && e.currentTarget.getAttribute) {
201
+ const target = e.currentTarget.getAttribute('target')
202
+ if (/\b_blank\b/i.test(target)) return
203
+ }
204
+ // this may be a Weex event which doesn't have this method
205
+ if (e.preventDefault) {
206
+ e.preventDefault()
207
+ }
208
+ return true
209
+ }
210
+
211
+ function findAnchor (children) {
212
+ if (children) {
213
+ let child
214
+ for (let i = 0; i < children.length; i++) {
215
+ child = children[i]
216
+ if (child.tag === 'a') {
217
+ return child
218
+ }
219
+ if (child.children && (child = findAnchor(child.children))) {
220
+ return child
221
+ }
222
+ }
223
+ }
224
+ }