Skip to content

Commit a4e2fa5

Browse files
authored
feat: wrap all elements in functional Vue components (nativescript-vue#267)
* feat (wrap ns components): Wrap the Label NS component into a Vue's one. * feat (wrap ns components): Refactor the way that the Vue components are registered. * feat (wrap ns components): New registerComponent function which would be the way to wrap and register new NS and Vue components. * feat (wrap ns components): Wrap a Vue component if no component is passed to the registerComponent function. Refactored Label component as it's the simpler case to refactor. * feat (wrap ns components): Begin an sample app with all the components. For now only the Label one. * feat (wrap ns components): Make v-model work also in the automatically wrapped Vue components. Register DatePicker. * feat (wrap ns components): Add DatePicker component to the app-with-all-components app * feat (wrap ns components): Better not using v-bind and v-on shorthands here. * feat (wrap ns components): Another way for wrapping components which is not working yet. * feat (wrap ns components): Some fixes following the @rigor789 guidelines * feat (wrap ns components): Fix a typo. * feat (wrap ns components): Debug the label component internals. * feat (wrap ns components): Fix some typos. * feat (wrap ns components): no need for return the entry. * feat (wrap ns components): Change a second v-if to v-else because it does weird change the components order and I think is because nativescript-vue#127 is not completely fixed. * feat (wrap ns components): Some fixes. * feat (wrap ns components): Fix the render function for wrapped components. * feat (wrap ns components): In order to keep the $refs, we pass the context.data object entirely to the component. * feat (wrap ns components): Wrap DatePicker and Button elements. Add them to the test app. * feat (wrap ns components): Destructured render function. * feat: wrap all elements in a functional vue component BREAKING CHANGE: All elements are now Vue components, in some cases where a ref is used to get the nativeView will have to be updated to use `this.$refs.element.$el.nativeView` instead of `this.$refs.element.nativeView`. There may be other breaking changes this introduces that are not known at this point. re nativescript-vue#266 re nativescript-vue#241 * test: update expectations for normalized element names
1 parent d41df82 commit a4e2fa5

22 files changed

+295
-178
lines changed

__tests__/element-registry.test.js

Lines changed: 0 additions & 6 deletions
This file was deleted.

__tests__/renderer/ViewNode.test.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import ViewNode from 'renderer/ViewNode'
22
import * as elReg from 'element-registry'
33

4+
jest.mock('runtime/components', () => {
5+
return {}
6+
})
47
jest.mock('renderer/utils', () => ({
58
insertChild: jest.fn(),
69
removeChild: jest.fn()
@@ -9,7 +12,8 @@ jest.mock('tns-core-modules/platform', () => {
912
return {
1013
isAndroid() {
1114
},
12-
isIOS() {}
15+
isIOS() {
16+
}
1317
}
1418
}, {virtual: true})
1519

@@ -266,7 +270,7 @@ describe('ViewNode', () => {
266270
let meta = node.meta
267271
let second_meta = node.meta
268272

269-
expect(elReg.getViewMeta).toHaveBeenCalledWith('testing')
273+
expect(elReg.getViewMeta).toHaveBeenCalledWith('nativetesting')
270274
expect(elReg.getViewMeta.mock.calls.length).toBe(1)
271275
expect(meta).toEqual('meta')
272276
expect(second_meta).toEqual('meta')
@@ -276,12 +280,12 @@ describe('ViewNode', () => {
276280
let node = new ViewNode()
277281

278282
node.tagName = 'Testing'
279-
expect(node.tagName).toEqual('testing')
283+
expect(node.tagName).toEqual('nativetesting')
280284

281285
node.tagName = 'TestingTesting'
282-
expect(node.tagName).toEqual('testingtesting')
286+
expect(node.tagName).toEqual('nativetestingtesting')
283287

284288
node.tagName = 'testing-testing'
285-
expect(node.tagName).toEqual('testingtesting')
289+
expect(node.tagName).toEqual('nativetestingtesting')
286290
})
287291
})

platform/nativescript/element-registry.js

Lines changed: 151 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,59 @@
1+
import * as builtInComponents from './runtime/components'
2+
13
const elementMap = new Map()
24

35
const defaultViewMeta = {
46
skipAddToDom: false,
57
isUnaryTag: false,
68
tagNamespace: '',
79
canBeLeftOpen: false,
8-
model: {
9-
prop: 'text',
10-
event: 'textChange'
11-
}
10+
model: null,
11+
component: null
1212
}
1313

1414
export function normalizeElementName(elementName) {
15-
return elementName.replace(/-/g, '').toLowerCase()
15+
return `native${elementName
16+
.replace(/Native/gi, '')
17+
.replace(/-/g, '')
18+
.toLowerCase()}`
1619
}
1720

1821
export function registerElement(elementName, resolver, meta) {
19-
elementName = normalizeElementName(elementName)
22+
const normalizedName = normalizeElementName(elementName)
2023

2124
meta = Object.assign({}, defaultViewMeta, meta)
2225

23-
if (elementMap.has(elementName)) {
26+
if (elementMap.has(normalizedName)) {
2427
throw new Error(`Element for ${elementName} already registered.`)
2528
}
2629

27-
const entry = { resolver: resolver, meta: meta }
28-
elementMap.set(elementName.toLowerCase(), entry)
30+
if (!meta.component) {
31+
// if no Vue component is passed, wrap the simpler vue component
32+
// which bind the events and attributes to the NS one
33+
meta.component = {
34+
functional: true,
35+
model: meta.model,
36+
render: (h, { data, children }) => {
37+
return h(normalizedName, data, children)
38+
}
39+
}
40+
}
41+
meta.component.name = elementName
42+
43+
const entry = {
44+
resolver: resolver,
45+
meta: meta
46+
}
47+
elementMap.set(normalizedName, entry)
48+
}
49+
50+
export function getElementMap() {
51+
return elementMap
2952
}
3053

3154
export function getViewClass(elementName) {
32-
elementName = normalizeElementName(elementName)
33-
const entry = elementMap.get(elementName.toLowerCase())
55+
const normalizedName = normalizeElementName(elementName)
56+
const entry = elementMap.get(normalizedName)
3457

3558
if (!entry) {
3659
throw new TypeError(`No known component for element ${elementName}.`)
@@ -43,11 +66,11 @@ export function getViewClass(elementName) {
4366
}
4467
}
4568

46-
export function getViewMeta(nodeName) {
47-
nodeName = normalizeElementName(nodeName)
69+
export function getViewMeta(elementName) {
70+
const normalizedName = normalizeElementName(elementName)
4871

4972
let meta = defaultViewMeta
50-
const entry = elementMap.get(nodeName.toLowerCase())
73+
const entry = elementMap.get(normalizedName)
5174

5275
if (entry && entry.meta) {
5376
meta = entry.meta
@@ -57,25 +80,90 @@ export function getViewMeta(nodeName) {
5780
}
5881

5982
export function isKnownView(elementName) {
60-
elementName = normalizeElementName(elementName)
61-
62-
return elementMap.has(elementName.toLowerCase())
83+
return elementMap.has(normalizeElementName(elementName))
6384
}
6485

6586
registerElement(
66-
'AbsoluteLayout',
67-
() => require('tns-core-modules/ui/layouts/absolute-layout').AbsoluteLayout
87+
'ActionBar',
88+
() => require('tns-core-modules/ui/action-bar').ActionBar,
89+
{
90+
removeChild(parent, child) {
91+
try {
92+
parent.nativeView._removeView(child.nativeView)
93+
} catch (e) {
94+
// ignore exception - child is likely already removed/replaced
95+
// fixes #76
96+
}
97+
},
98+
component: builtInComponents.ActionBar
99+
}
68100
)
101+
69102
registerElement(
70-
'ActivityIndicator',
71-
() => require('tns-core-modules/ui/activity-indicator').ActivityIndicator
103+
'ActionItem',
104+
() => require('tns-core-modules/ui/action-bar').ActionItem
72105
)
73-
registerElement('Border', () => require('tns-core-modules/ui/border').Border)
74-
registerElement('Button', () => require('tns-core-modules/ui/button').Button)
106+
107+
registerElement('android', null, {
108+
component: builtInComponents.android
109+
})
110+
111+
registerElement('ios', null, {
112+
component: builtInComponents.ios
113+
})
114+
75115
registerElement(
76-
'ContentView',
77-
() => require('tns-core-modules/ui/content-view').ContentView
116+
'ListView',
117+
() => require('tns-core-modules/ui/list-view').ListView,
118+
{
119+
component: builtInComponents.ListView
120+
}
78121
)
122+
123+
registerElement(
124+
'NavigationButton',
125+
() => require('tns-core-modules/ui/action-bar').NavigationButton
126+
)
127+
128+
registerElement(
129+
'TabView',
130+
() => require('tns-core-modules/ui/tab-view').TabView,
131+
{
132+
model: {
133+
prop: 'selectedIndex',
134+
event: 'selectedIndexChange'
135+
},
136+
component: builtInComponents.TabView
137+
}
138+
)
139+
140+
registerElement(
141+
'TabViewItem',
142+
() => require('tns-core-modules/ui/tab-view').TabViewItem,
143+
{
144+
skipAddToDom: true,
145+
component: builtInComponents.TabViewItem
146+
}
147+
)
148+
149+
registerElement('transition', null, {
150+
component: builtInComponents.transition
151+
})
152+
153+
registerElement('v-template', null, {
154+
component: builtInComponents.VTemplate
155+
})
156+
157+
// NS components which uses the automatic registerElement Vue wrapper
158+
// as they do not need any special logic
159+
160+
registerElement('Label', () => require('tns-core-modules/ui/label').Label, {
161+
model: {
162+
prop: 'text',
163+
event: 'textChange'
164+
}
165+
})
166+
79167
registerElement(
80168
'DatePicker',
81169
() => require('tns-core-modules/ui/date-picker').DatePicker,
@@ -86,6 +174,21 @@ registerElement(
86174
}
87175
}
88176
)
177+
178+
registerElement(
179+
'AbsoluteLayout',
180+
() => require('tns-core-modules/ui/layouts/absolute-layout').AbsoluteLayout
181+
)
182+
registerElement(
183+
'ActivityIndicator',
184+
() => require('tns-core-modules/ui/activity-indicator').ActivityIndicator
185+
)
186+
registerElement('Border', () => require('tns-core-modules/ui/border').Border)
187+
registerElement('Button', () => require('tns-core-modules/ui/button').Button)
188+
registerElement(
189+
'ContentView',
190+
() => require('tns-core-modules/ui/content-view').ContentView
191+
)
89192
registerElement(
90193
'DockLayout',
91194
() => require('tns-core-modules/ui/layouts/dock-layout').DockLayout
@@ -100,7 +203,6 @@ registerElement(
100203
)
101204
registerElement('Image', () => require('tns-core-modules/ui/image').Image)
102205
registerElement('img', () => require('tns-core-modules/ui/image').Image)
103-
registerElement('Label', () => require('tns-core-modules/ui/label').Label)
104206
registerElement(
105207
'ListPicker',
106208
() => require('tns-core-modules/ui/list-picker').ListPicker,
@@ -111,32 +213,6 @@ registerElement(
111213
}
112214
}
113215
)
114-
registerElement(
115-
'NativeActionBar',
116-
() => require('tns-core-modules/ui/action-bar').ActionBar,
117-
{
118-
removeChild(parent, child) {
119-
try {
120-
parent.nativeView._removeView(child.nativeView)
121-
} catch (e) {
122-
// ignore exception - child is likely already removed/replaced
123-
// fixes #76
124-
}
125-
}
126-
}
127-
)
128-
registerElement(
129-
'NativeActionItem',
130-
() => require('tns-core-modules/ui/action-bar').ActionItem
131-
)
132-
registerElement(
133-
'NativeListView',
134-
() => require('tns-core-modules/ui/list-view').ListView
135-
)
136-
registerElement(
137-
'NativeNavigationButton',
138-
() => require('tns-core-modules/ui/action-bar').NavigationButton
139-
)
140216
registerElement('Page', () => require('tns-core-modules/ui/page').Page, {
141217
skipAddToDom: true
142218
})
@@ -146,16 +222,22 @@ registerElement(
146222
)
147223
registerElement(
148224
'Progress',
149-
() => require('tns-core-modules/ui/progress').Progress
225+
() => require('tns-core-modules/ui/progress').Progress,
226+
{
227+
model: {
228+
prop: 'value',
229+
event: 'valueChange'
230+
}
231+
}
150232
)
151233
registerElement(
152234
'ProxyViewContainer',
153235
() => require('tns-core-modules/ui/proxy-view-container').ProxyViewContainer
154236
)
155-
registerElement(
156-
'Repeater',
157-
() => require('tns-core-modules/ui/repeater').Repeater
158-
)
237+
// registerElement(
238+
// 'Repeater',
239+
// () => require('tns-core-modules/ui/repeater').Repeater
240+
// )
159241
registerElement(
160242
'ScrollView',
161243
() => require('tns-core-modules/ui/scroll-view').ScrollView
@@ -206,31 +288,25 @@ registerElement('Switch', () => require('tns-core-modules/ui/switch').Switch, {
206288
})
207289

208290
registerElement(
209-
'NativeTabView',
210-
() => require('tns-core-modules/ui/tab-view').TabView,
291+
'TextField',
292+
() => require('tns-core-modules/ui/text-field').TextField,
211293
{
212294
model: {
213-
prop: 'selectedIndex',
214-
event: 'selectedIndexChange'
295+
prop: 'text',
296+
event: 'textChange'
215297
}
216298
}
217299
)
218300
registerElement(
219-
'NativeTabViewItem',
220-
() => require('tns-core-modules/ui/tab-view').TabViewItem,
301+
'TextView',
302+
() => require('tns-core-modules/ui/text-view').TextView,
221303
{
222-
skipAddToDom: true
304+
model: {
305+
prop: 'text',
306+
event: 'textChange'
307+
}
223308
}
224309
)
225-
226-
registerElement(
227-
'TextField',
228-
() => require('tns-core-modules/ui/text-field').TextField
229-
)
230-
registerElement(
231-
'TextView',
232-
() => require('tns-core-modules/ui/text-view').TextView
233-
)
234310
registerElement(
235311
'TimePicker',
236312
() => require('tns-core-modules/ui/time-picker').TimePicker,
@@ -273,6 +349,7 @@ registerElement(
273349
'Comment',
274350
() => require('tns-core-modules/ui/placeholder').Placeholder
275351
)
352+
276353
registerElement(
277354
'Document',
278355
() => require('tns-core-modules/ui/proxy-view-container').ProxyViewContainer,
@@ -283,7 +360,7 @@ registerElement(
283360

284361
registerElement('Frame', () => require('tns-core-modules/ui/frame').Frame, {
285362
insertChild(parentNode, childNode, atIndex) {
286-
if (childNode.tagName === 'page') {
363+
if (normalizeElementName(childNode.tagName) === 'nativepage') {
287364
parentNode.nativeView.navigate({ create: () => childNode.nativeView })
288365
}
289366
}

platform/nativescript/framework.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
global.process = global.process || {}
44
global.process.env = global.process.env || {}
55

6-
import { VUE_VM_REF } from './runtime'
76
import inspect from 'util-inspect'
87
import { topmost } from 'tns-core-modules/ui/frame'
98
import application from 'tns-core-modules/application'

0 commit comments

Comments
 (0)