10000 various work · Yellowbox-AU/nativescript-vue@7096e9e · GitHub
[go: up one dir, main page]

Skip to content

Commit 7096e9e

Browse files
committed
various work
1 parent 042ccca commit 7096e9e

File tree

14 files changed

+515
-130
lines changed

14 files changed

+515
-130
lines changed

build/config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ const builds = {
4646
moduleName: 'NativeScript-Vue',
4747
banner: banner('NativeScript-Vue'),
4848
intro,
49-
external(id) {
50-
return id.startsWith('@nativescript') || id.startsWith('weex')
49+
external(id, ...args) {
50+
console.log(`deciding if module ${args} is external`)
51+
return id.startsWith('@nativescript') || id.startsWith('weex') || id === 'vue'
5152
},
5253
},
5354
'nativescript-vue-template-compiler': {

index.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import {
44
BackstackEntry,
55
} from '@nativescript/core'
66
import { ItemEventData } from '@nativescript/core'
7+
import { Frame } from '@nativescript/core'
78
import { View } from '@nativescript/core'
89
import { ShowModalOptions } from '@nativescript/core'
910
import { Vue, VueConstructor, VueConfiguration } from 'vue/types/vue'
11+
import ElementNode from './platform/nativescript/renderer/ElementNode'
1012

1113
// ListView ItemEventData with the addition of the item property
1214
export type NativeScriptVueItemEventData<T> = ItemEventData & { item: T }
@@ -22,7 +24,7 @@ export interface NavigationEntryVue extends NavigationEntry {
2224
}
2325

2426
export type navigateTo = (
25-
component: VueConstructor,
27+
component: Parameters<import('vue').CreateElement>[0],
2628
options?: NavigationEntryVue,
2729
cb?: () => Page,
2830
) => Promise<Page>
@@ -42,6 +44,7 @@ export interface ModalOptions extends Partial<ShowModalOptions> {
4244
// create a nativescript vue class that extends vue.js
4345
export interface NativeScriptVue<V = View> extends Vue {
4446
nativeView: V
47+
$el: ElementNode
4548

4649
$navigateTo: navigateTo
4750
$navigateBack: navigateBack
@@ -58,6 +61,8 @@ export interface NativeScriptVue<V = View> extends Vue {
5861

5962
export interface NativeScriptVueConstructor extends VueConstructor<NativeScriptVue>
6063
{
64+
options: any
65+
6166
navigateTo: navigateTo
6267
navigateBack: navigateBack
6368

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"@babel/preset-env": "7.13.12",
6161
"@commitlint/cli": "12.0.1",
6262
"@commitlint/config-conventional": "12.0.1",
63-
"@nativescript/core": "8.0.0",
63+
"@nativescript/core": "^8.4.7",
6464
"@rollup/plugin-alias": "3.1.2",
6565
"@rollup/plugin-buble": "0.21.3",
6666
"@rollup/plugin-commonjs": "18.0.0",
@@ -124,5 +124,9 @@
124124
"commitizen": {
125125
"path": "./node_modules/cz-conventional-changelog"
126126
}
127+
},
128+
"dependencies": {
129+
"esbuild": "^0.17.8",
130+
"rollup-plugin-esbuild": "^5.0.0"
127131
}
128132
}

patches/vue+2.7.14.patch

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
diff --git a/node_modules/vue/src/core/util/next-tick.ts b/node_modules/vue/src/core/util/next-tick.ts
2+
index 5e67bd7..d42cead 100644
3+
--- a/node_modules/vue/src/core/util/next-tick.ts
4+
+++ b/node_modules/vue/src/core/util/next-tick.ts
5+
@@ -9,7 +9,7 @@ export let isUsingMicroTask = false
6+
const callbacks: Array<Function> = []
7+
let pending = false
8+
9+
-function flushCallbacks() {
10+
+export function flushCallbacks() {
11+
pending = false
12+
const copies = callbacks.slice(0)
13+
callbacks.length = 0

platform/nativescript/plugins/modal-plugin.js

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { isObject, isDef, isPrimitive } from 'shared/util'
1+
import { isObject, isDef } from 'shared/util'
22
import { updateDevtools } from '../util'
33
import { VUE_ELEMENT_REF } from '../renderer/ElementNode'
44
import { ensureCorrectView } from './navigation-utils'
5+
import { ContentView, Placeholder, View } from '@nativescript/core'
56

67
let sequentialCounter = 0
78

@@ -21,6 +22,7 @@ function serializeModalOptions(options) {
2122
.join(', ')
2223
}
2324

25+
/** @returns {import('@nativescript/core').View} */
2426
function getTargetView(target) {
2527
if (isObject(target) && isDef(target.$el)) {
2628
return target.$el.nativeView
@@ -69,12 +71,12 @@ export default {
6971

7072
resolved = true
7173
resolve(data)
72-
modalPage.closeModal()
74+
modalEntryInstance.nativeView.closeModal()
7375

7476
// emitted to show up in devtools
7577
// for debugging purposes
76-
navEntryInstance.$emit('modal:close', data)
77-
navEntryInstance.$destroy()
78+
modalEntryInstance.$emit('modal:close', data)
79+
modalEntryInstance.$destroy()
7880
}
7981

8082
// build options object with defaults
@@ -89,24 +91,34 @@ export default {
8991
}
9092
)
9193

92-
const navEntryInstance = new Vue({
94+
const modalEntryInstance = new Vue({
9395
name: 'ModalEntry',
9496
parent: options.target,
9597
methods: {
9698
closeCb
9799
},
98-
render: h =>
99-
h(component, {
100-
props: options.props,
101-
key: serializeModalOptions(options)
102-
})
100+
render: h => h(component, { key: serializeModalOptions(options),props: options.props })
103101
}).$mount()
104102

105-
let modalPage = await ensureCorrectView(navEntryInstance, component)
106-
107-
updateDevtools()
108-
109-
getTargetView(options.target).showModal(modalPage, options)
103+
/** @type {View} */
104+
let view, contentView = new ContentView(), updatedCb
105+
contentView.iosOverflowSafeArea = false
106+
modalEntryInstance.$on('hook:updated', updatedCb = async () => {
107+
updateDevtools()
108+
if (modalEntryInstance.nativeView && !(modalEntryInstance.nativeView instanceof Placeholder) && modalEntryInstance.nativeView !== view) {
109+
// We will pass the ContentView to showModal so that we can actually change the content once it has been shown
110+
const newView = modalEntryInstance.nativeView
111+
const targetView = getTargetView(options.target)
112+
// Set/replace the content of the view shown in the modal
113+
contentView.content = newView
114+
// If first time the component rendered non-placeholder content then we show the modal
115+
// using the contentView
116+
if (!view)
117+
targetView.showModal(contentView, options)
118+
view = newView
119+
}
120+
})
121+
updatedCb()
110122
})
111123
}
112124
}

platform/nativescript/plugins/navigation-utils.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { Placeholder } from '@nativescript/core'
33

44
/**
5-
* @param {import('vue').ComponentOptions} component
5+
* @param {import('vue').ComponentOptions | string} component
66
* @returns {string}
77
*/
88
function getComponentName(component) {
@@ -29,10 +29,10 @@ const REPLACE_PLACEHOLDER_TIMEOUT = 7500
2929
* provided, we check that the non-Placeholder View matches the required one as a final step.
3030
*
3131
* @template {import('@nativescript/core').View} R
32-
* @param {import('vue/types/vue').CombinedVueInstance<import('nativescript-vue').NativeScriptVue, {}, {}, {}, {}>} navEntryInstance
33-
* @param {import('vue').ComponentOptions<import('nativescript-vue').NativeScriptVue>} component Reference to the component passed by the user, to provide the user with more informative errors
32+
* @param {import('vue/types/vue').CombinedVueInstance<import('nativescript-vue').NativeScriptVue<R>, {}, {}, {}, {}>} navEntryInstance
33+
* @param {import('vue').ComponentOptions<import('nativescript-vue').NativeScriptVue> | string} component Reference to the component passed by the user, to provide the user with more informative errors
3434
* @param {new (...args: any[]) => R} [requiredClass] Constraint on which View subclass the users component must return as its root. If violated a descriptive error will be thrown.
35-
* @returns {Promise<import('@nativescript/core').View>}
35+
* @returns {Promise<R>}
3636
*/
3737
export async function ensureCorrectView(
3838
navEntryInstance,
@@ -70,7 +70,7 @@ export async function ensureCorrectView(
7070
caughtErr ||
7171
(requiredClass && !(navEntryInstance.nativeView instanceof requiredClass))
7272
) {
73-
setTimeout(() => navEntryInstance.$destroy(), 0)
73+
navEntryInstance.$destroy()
7474
throw (
7575
caughtErr ||
7676
new Error(

platform/nativescript/plugins/navigator-plugin.js

Lines changed: 77 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
// @ts-check
12
import { isObject, isDef, isPrimitive } from 'shared/util'
23
import { getFrame } from '../util/frame'
34
import { updateDevtools } from '../util'
4-
import { Page } from '@nativescript/core'
5-
import { ensureCorrectView } from './navigation-utils'
5+
import { Page, Placeholder } from '@nativescript/core'
6+
import { NavigationType } from '@nativescript/core/ui/frame/frame-common'
7+
// import { ensureCorrectView } from './navigation-utils'
68

79
let sequentialCounter = 0
810

@@ -53,7 +55,7 @@ export function findParentFrame(vm) {
5355
}
5456

5557
export default {
56-
install(Vue) {
58+
install(/** @type {import('../../..').NativeScriptVueConstructor} */ Vue) {
5759
Vue.navigateBack = Vue.prototype.$navigateBack = function (
5860
options,
5961
backstackEntry = null
@@ -68,7 +70,7 @@ export default {
6870
frame.back(backstackEntry)
6971
}
7072

71-
Vue.navigateTo = Vue.prototype.$navigateTo = async function (
73+
Vue.navigateTo = Vue.prototype.$navigateTo = function (
7274
component,
7375
options
7476
) {
@@ -93,48 +95,82 @@ export default {
9395
key
9496
})
9597
}).$mount()
96-
97-
let page = await ensureCorrectView(navEntryInstance, component, Page)
98-
99-
return new Promise(resolve => {
100-
updateDevtools()
101-
98+
99+
return new Promise((resolve, reject) => {
100+
/** @type {Page} */
101+
let page
102102
const resolveOnEvent = options.resolveOnEvent
103-
// ensure we dont resolve twice event though this should never happen!
104-
let resolved = false
105-
106-
const handler = args => {
107-
if (args.isBackNavigation) {
108-
page.off('navigatedFrom', handler)
109-
navEntryInstance.$destroy()
110-
}
111-
}
112-
page.on('navigatedFrom', handler)
113-
114-
if (resolveOnEvent) {
115-
const resolveHandler = args => {
116-
if (!resolved) {
117-
resolved = true
118-
resolve(page)
103+
const onUpdate = () => {
104+
if (navEntryInstance.nativeView instanceof Page && navEntryInstance.nativeView !== page) {
105+
updateDevtools()
106+
107+
const newPage = navEntryInstance.nativeView
108+
// Add cleanup handlers. Only destroy the navEntryInstance if this Page instance is
109+
// still the one at the root of the navEntryInstance
110+
newPage.on('navigatedFrom', e => {
111+
if (e.isBackNavigation && navEntryInstance.nativeView === newPage)
112+
navEntryInstance.$destroy()
113+
})
114+
newPage.disposeNativeView = new Proxy(newPage.disposeNativeView, {
115+
apply(target, thisArg, argArray) {
116+
// console.log(`➖ 🧭 newPage.disposeNativeView ($destroy navEntryInstance? ${navEntryInstance.nativeView === newPage})`)
117+
if (navEntryInstance.nativeView === newPage)
118+
navEntryInstance.$destroy()
119+
return Reflect.apply(target, thisArg, argArray)
120+
}
121+
})
122+
123+
/** The asyncFactory from which the <Page> was rendered, if there is one. */
124+
const asyncFactory = navEntryInstance._vnode.asyncFactory
125+
126+
if (newPage && page) {
127+
// We had already navigated to a Page e.g. the loadingComp of an asyncFactory. We
128+
// don't want to replay the initial navigation transition and we don't want the
129+
// currently visible page to go into the backStack. We need to replace the page. NS
130+
// has an implementation of replacePage but it only accepts a moduleName, so it has
131+
// been copied here and refactored for the time being.
132+
// TODO: Replace with call to replacePage (NS >= 8.1.0-rc.0) https://github.com/NativeScript/NativeScript/commit/ffab4c31658f9be2137cae5a824f8e8c9bf7aef2
133+
/** @type {import('@nativescript/core').Frame} */
134+
const nativeFrame = frame.nativeView
135+
const currentBackstackEntry = nativeFrame._currentEntry;
136+
const newBackstackEntry = {
137+
entry: Object.assign({}, currentBackstackEntry.entry, { create: () => newPage }),
138+
resolvedPage: newPage,
139+
navDepth: currentBackstackEntry._navDepth,
140+
fragmentTag: currentBackstackEntry.fragmentTag,
141+
frameId: frame.id,
142+
};
143+
const navigationContext = {
144+
entry: newBackstackEntry,
145+
isBackNavigation: false,
146+
navigationType: NavigationType.replace,
147+
};
148+
nativeFrame._navigationQueue.push(navigationContext);
149+
nativeFrame._processNextNavigationEntry();
150+
} else {
151+
frame.navigate(Object.assign({}, options, { create: () => newPage }))
119152
}
120-
page.off(resolveOnEvent, resolveHandler)
121-
}
122-
page.on(resolveOnEvent, resolveHandler)
123-
}
124153

125-
// ensure that the navEntryInstance vue instance is destroyed when the
126-
// page is disposed (clearHistory: true for example)
127-
const dispose = page.disposeNativeView
128-
page.disposeNativeView = (...args) => {
129-
navEntryInstance.$destroy()
130-
return dispose.call(page, args)
131-
}
154+
if (!asyncFactory || asyncFactory.resolved) {
155+
// Only resolve if the users resolveOnEvent occurs on the final destination component
156+
if (resolveOnEvent) {
157+
newPage.once(resolveOnEvent, () => resolve(newPage))
158+
} else {
159+
resolve(newPage)
160+
}
161+
}
132162

133-
frame.navigate(Object.assign({}, options, { create: () => page }))
134-
if (!resolveOnEvent) {
135-
resolved = true
136-
resolve(page)
163+
page = newPage
164+
} else if (navEntryInstance.nativeView && !(navEntryInstance.nativeView instanceof Placeholder) && !(navEntryInstance.nativeView instanceof Page)) {
165+
navEntryInstance.$off('hook:updated', onUpdate)
166+
reject(new Error(`navigateTo: Navigation destination rendered a <${navEntryInstance.nativeView.typeName}> where a <Page> was expected`))
167+
navEntryInstance.$destroy()
168+
}
137169
}
170+
navEntryInstance.$on('hook:updated', onUpdate)
171+
// non-asyncFactory user component will have already produced its nativeView at this point,
172+
// so we invoke the updated cb once ourselves manually
173+
onUpdate()
138174
})
139175
}
140176
}

platform/nativescript/renderer/ViewNode.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,16 @@ export default class ViewNode {
2626
/* istanbul ignore next
2727
* make vue happy :)
2828
*/
29-
this.hasAttribute = this.removeAttribute = () => false
29+
this.hasAttribute = () => false
30+
31+
/** This is the reason there needed to always be some class on <transition> children, because
32+
* without it removeClass would call el.removeAttribute('class') but it wouldn't actually remove
33+
* the class at the end of a transition, and this would actually make it cancel and then run the
34+
* same transition AGAIN (causing flickering) */
35+
this.removeAttribute = key => {
36+
console.log(`removeAttribute delegating to setAttribute(${key}, "")`)
37+
this.setAttribute(key, "")
38+
}
3039
}
3140

3241
/* istanbul ignore next */
@@ -124,7 +133,7 @@ export default class ViewNode {
124133
}
125134
}
126135
} catch (e) {
127-
// ignore
136+
console.warn(`Error setting attribute ${key} to ${value} on ${nv}`, e)
128137
}
129138
}
130139

platform/nativescript/runtime/components/frame.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ts-check
12
import { setFrame, getFrame, deleteFrame } from '../../util/frame'
23
import { warn } from 'core/util/debug'
34

@@ -114,7 +115,8 @@ export default {
114115
this.navigate(options)
115116
},
116117

117-
navigate(entry, back = false) {
118+
async navigate(entry, back = false) {
119+
/** @type {import('@nativescript/core').Frame} */
118120
const frame = this._getFrame()
119121

120122
if (back) {

0 commit comments

Comments
 (0)
0