├── .github
├── dependabot.yml
└── workflows
│ └── semantic-release.yml
├── .gitignore
├── LICENSE
├── README.md
├── components
└── Navigator.js
├── index.js
├── package-lock.json
└── package.json
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | versioning-strategy: lockfile-only
9 | ignore:
10 | - dependency-name: semantic-release
11 | versions:
12 | - 17.3.7
13 | - 17.3.8
14 | - 17.3.9
15 | - 17.4.0
16 | - 17.4.1
17 |
--------------------------------------------------------------------------------
/.github/workflows/semantic-release.yml:
--------------------------------------------------------------------------------
1 | name: Semantic Release CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | release:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v1
14 | - uses: actions/setup-node@v1
15 | with:
16 | node-version: 12.x
17 | - run: |
18 | npm install
19 | npm run semantic-release --if-present
20 | env:
21 | CI: true
22 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
23 | NPM_TOKEN: ${{secrets.NPM_TOKEN}}
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 NativeScript-Vue
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NativeScript-Vue-Navigator
2 |
3 | NativeScript-Vue-Navigator is a simple router implementation that is suitable for NativeScript-Vue.
4 |
5 | ## Quick Start
6 |
7 | ```shell
8 | $ npm install --save nativescript-vue-navigator
9 | ```
10 |
11 | ```diff
12 | // main.js
13 | import Vue from 'nativescript-vue'
14 | ...
15 | + import Navigator from 'nativescript-vue-navigator'
16 | + import {routes} from './routes'
17 | + Vue.use(Navigator, { routes })
18 |
19 | new Vue({
20 | - render: h => h('frame', App),
21 | + render: h => h(App),
22 | }).$start()
23 | ```
24 |
25 | ```js
26 | // routes.js
27 | import HomePage from './components/HomePage'
28 | import LoginPage from './components/LoginPage'
29 |
30 | export const routes = {
31 | '/home': {
32 | component: HomePage,
33 | },
34 | '/login': {
35 | component: LoginPage,
36 | },
37 | }
38 | ```
39 |
40 | ```diff
41 | // App.vue
42 |
43 | +
44 |
45 | ```
46 |
47 | ### Attaching extra data to a route
48 |
49 | ```diff
50 | // routes.js
51 | import HomePage from './components/HomePage'
52 | import LoginPage from './components/LoginPage'
53 |
54 | export const routes = {
55 | '/home': {
56 | component: HomePage,
57 | + // we are using `meta` as a good practice, but you are free to use something else
58 | + meta: { needsAuth: true }
59 | },
60 | '/login': {
61 | component: LoginPage,
62 | + meta: { needsAuth: false }
63 | },
64 | }
65 | ```
66 |
67 | ```xml
68 |
69 |
70 | ```
71 | ```js
72 | // or in any vue component
73 | export default {
74 | methods: {
75 | doStuff() {
76 | if(this.$navigator.route.meta.needsAuth) {
77 | // do stuff
78 | }
79 | }
80 | }
81 | }
82 | ```
83 |
84 | ## Getting the current path
85 |
86 | ```js
87 | // logs the current path in the default navigator
88 | console.log(this.$navigator.path)
89 |
90 | // logs the current path in the second navigator (See Multiple Navigators section for more details)
91 | console.log(this.$navigator.paths.second)
92 | ```
93 |
94 | ## Navigating
95 |
96 | This package provides 2 methods for navigation, `$navigator.navigate` and `$navigator.back`
97 |
98 | `$navigator.navigate(to, options)` is used for all forward navigation
99 | * `to` is the path to navigate to (ex.: `/home`)
100 | * `options` is an optional object, which accepts all options supported by [Manual Routing](https://nativescript-vue.org/en/docs/routing/manual-routing/#navigateto)
101 |
102 | For example, given you are on a Login page, and successfully log in you would navigate to the Home page with
103 | ```js
104 | this.$navigator.navigate('/home', { clearHistory: true })
105 | ```
106 | Note that we used `clearHistory: true` to prevent the back button from going back to the login page.
107 |
108 | `$navigator.back(options, backstackEntry)` is an alias to [`$navigateBack`](https://nativescript-vue.org/en/docs/routing/manual-routing/#navigatebackoptions-backstackentry--null)
109 |
110 | # Multiple Navigators
111 |
112 | It is possible to use multiple `` elements by providing each new Navigator with a unique `id`.
113 |
114 | ```vue
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
145 | ```
146 |
147 | # Navigator Modals
148 |
149 | ```ts
150 | type ModalOptions = { id: string } & ShowModalOptions
151 | this.$navigator.modal(path: string, options: ModalOptions);
152 | ```
153 |
154 | The default id for modal navigators is `modalNavigator` but can be changed by passing an `id` inside the ModalOptions.
155 |
156 | ```js
157 | // use the default id for the modal
158 | this.$navigator.modal('/path', { fullscreen: true })
159 | // to navigate the modal to '/other'
160 | this.$navigator.navigate('/other', { frame: 'modalNavigator' })
161 |
162 | // use a different id for the modal
163 | this.$navigator.modal('/path', { fullscreen: true, id: 'myModal' })
164 | // to navigate the myModal modal to '/other'
165 | this.$navigator.navigate('/other', { frame: 'myModal' })
166 | ```
167 |
--------------------------------------------------------------------------------
/components/Navigator.js:
--------------------------------------------------------------------------------
1 | export default {
2 | props: {
3 | id: {
4 | type: String,
5 | default: 'navigator'
6 | },
7 | defaultRoute: {
8 | type: String,
9 | default: '/',
10 | },
11 | defaultRouteProps: {
12 | type: Object,
13 | required: false
14 | },
15 | },
16 | render(h) {
17 | this.slotContent = this.slotContent || h(this.defaultRouteComponent, { props: this.defaultRouteProps })
18 | return h(
19 | 'frame',
20 | {
21 | on: Object.assign(
22 | {},
23 | this.$listeners,
24 | {loaded: this.onFrameLoaded}
25 | ),
26 | attrs: {
27 | ...this.$attrs,
28 | id: this.$props.id
29 | },
30 | },
31 | [this.slotContent]
32 | )
33 | },
34 | created() {
35 | this.defaultRouteComponent = this.$navigator._resolveComponent(
36 | this.$props.defaultRoute,
37 | this.$props.id
38 | )
39 | },
40 | methods: {
41 | onFrameLoaded({object}) {
42 | if (object.__defined__custom_currentEntry) {
43 | // don't try do define the property multiple times
44 | return
45 | }
46 | object.__defined__custom_currentEntry = true
47 |
48 | const self = this
49 | let _currentEntry = object._currentEntry
50 | Object.defineProperty(object, '_currentEntry', {
51 | get() {
52 | return _currentEntry
53 | },
54 | set(value) {
55 | _currentEntry = value
56 | if (value && value.resolvedPage) {
57 | self.$navigator._updatePath(
58 | value.resolvedPage.__path || self.defaultRoute || '',
59 | self.$props.id
60 | )
61 | }
62 | },
63 | })
64 | },
65 | },
66 | }
67 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import Navigator from './components/Navigator'
2 |
3 | export default function install(Vue, {routes}) {
4 | let appRoot;
5 | const start = Vue.prototype.$start
6 | Vue.prototype.$start = function () {
7 | appRoot = this
8 | start.call(this)
9 | }
10 | Vue.component('Navigator', Navigator)
11 |
12 | Object.keys(routes).map(path => {
13 | routes[path].component.__path = path
14 | // this is required to attach the path to vue-class-components. See #31
15 | if(routes[path].component.options) {
16 | routes[path].component.options.__path = path;
17 | }
18 | })
19 |
20 | Vue.mixin({
21 | mounted() {
22 | // attach the current path if set to the root element
23 | if (this.$options.__path) {
24 | this.$el.setAttribute('__path', this.$options.__path)
25 | }
26 | },
27 | })
28 |
29 | Vue.prototype.$navigator = new Vue({
30 | data: {
31 | path: false,
32 | paths: {},
33 | defaultPaths: {},
34 | },
35 | computed: {
36 | route() {
37 | return this.routes('navigator')
38 | },
39 | routes() {
40 | return id => routes[this.paths[id] || this.defaultPaths[id]]
41 | },
42 | },
43 | methods: {
44 | _resolveComponent(defaultPath, id) {
45 | if (defaultPath) {
46 | this.$set(this.defaultPaths, id, defaultPath)
47 | }
48 |
49 | if (this.routes(id)) {
50 | return this.routes(id).component
51 | }
52 | return false
53 | },
54 | _updatePath(path, id = 'navigator') {
55 | if (id === 'navigator') {
56 | this.path = path
57 | }
58 | this.$set(this.paths, id, path)
59 | },
60 |
61 | navigate(to, options) {
62 | const matchedRoute = routes[to]
63 |
64 | if (!matchedRoute) {
65 | if (TNS_ENV === 'development') {
66 | throw new Error(`[Navigator] Navigating to a route that does not exist: ${to}`)
67 | }
68 | return false
69 | }
70 |
71 | options = Object.assign({frame: 'navigator'}, options)
72 |
73 | return this.$navigateTo(matchedRoute.component, options)
74 | .catch(err => console.log(`[Navigator] Failed to navigate: ${err}`))
75 | },
76 | back(options, ...args) {
77 | options = Object.assign({frame: 'navigator'}, options)
78 | return this.$navigateBack.call(this, options, ...args)
79 | },
80 | modal(to, options) {
81 | return appRoot.$showModal({
82 | render: h => h(Navigator, {
83 | props: {
84 | id: options.id || 'navigatorModal',
85 | defaultRoute: to,
86 | defaultRouteProps: options.props
87 | }
88 | })
89 | }, options).catch(err => console.log(`[Navigator] Failed to show modal: ${err}`))
90 | }
91 | },
92 | })
93 | }
94 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nativescript-vue-navigator",
3 | "version": "0.0.0-development",
4 | "description": "A simple router for NativeScript-Vue",
5 | "main": "index.js",
6 | "nativescript": {
7 | "plugin": {
8 | "vue": "true"
9 | }
10 | },
11 | "files": [
12 | "index.js",
13 | "components"
14 | ],
15 | "scripts": {
16 | "test": "echo \"Error: no test specified\" && exit 1",
17 | "semantic-release": "semantic-release"
18 | },
19 | "keywords": [
20 | "nativescript",
21 | "vue",
22 | "nativescript-vue",
23 | "router",
24 | "navigator",
25 | "navigation"
26 | ],
27 | "author": "Igor Randjelovic",
28 | "license": "MIT",
29 | "repository": {
30 | "type": "git",
31 | "url": "https://github.com/nativescript-vue/nativescript-vue-navigator.git"
32 | },
33 | "devDependencies": {
34 | "semantic-release": "^17.0.4"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------