@@ -3,7 +3,7 @@ import * as base from '@jupyter-widgets/base';
3
3
import { vueTemplateRender } from './VueTemplateRenderer' ; // eslint-disable-line import/no-cycle
4
4
import { VueModel } from './VueModel' ;
5
5
import { VueTemplateModel } from './VueTemplateModel' ;
6
- import Vue from './VueWithCompiler ' ;
6
+ import * as Vue from 'vue ' ;
7
7
8
8
const JupyterPhosphorWidget = base . JupyterPhosphorWidget || base . JupyterLuminoWidget ;
9
9
@@ -45,8 +45,8 @@ export function createObjectForNestedModel(model, parentView) {
45
45
destroyed = true ;
46
46
}
47
47
} ,
48
- render ( createElement ) {
49
- return createElement ( 'div' , { style : { height : '100%' } } ) ;
48
+ render ( ) {
49
+ return Vue . h ( 'div' , { style : { height : '100%' } } ) ;
50
50
} ,
51
51
} ;
52
52
}
@@ -81,16 +81,26 @@ export function eventToObject(event) {
81
81
return event ;
82
82
}
83
83
84
- export function vueRender ( createElement , model , parentView , slotScopes ) {
84
+ function resolve ( componentOrTag ) {
85
+ try {
86
+ return Vue . resolveComponent ( componentOrTag ) ;
87
+ } catch ( e ) {
88
+ return componentOrTag ;
89
+ }
90
+ }
91
+
92
+ export function vueRender ( model , parentView , slotScopes ) {
85
93
if ( model instanceof VueTemplateModel ) {
86
- return vueTemplateRender ( createElement , model , parentView ) ;
94
+ return vueTemplateRender ( model , parentView ) ;
87
95
}
88
96
if ( ! ( model instanceof VueModel ) ) {
89
- return createElement ( createObjectForNestedModel ( model , parentView ) ) ;
97
+ return Vue . h ( createObjectForNestedModel ( model , parentView ) ) ;
90
98
}
91
99
const tag = model . getVueTag ( ) ;
92
100
93
- const elem = createElement ( {
101
+ const childCache = { } ;
102
+
103
+ const elem = Vue . h ( {
94
104
data ( ) {
95
105
return {
96
106
v_model : model . get ( 'v_model' ) ,
@@ -99,20 +109,23 @@ export function vueRender(createElement, model, parentView, slotScopes) {
99
109
created ( ) {
100
110
addListeners ( model , this ) ;
101
111
} ,
102
- render ( createElement2 ) {
103
- const element = createElement2 (
104
- tag ,
105
- createContent ( createElement2 , model , this , parentView , slotScopes ) ,
106
- renderChildren ( createElement2 , model . get ( 'children' ) , this , parentView , slotScopes ) ,
112
+ render ( ) {
113
+ const element = Vue . h (
114
+ resolve ( tag ) ,
115
+ createContent ( model , this , parentView , slotScopes ) ,
116
+ {
117
+ default : ( ) => {
118
+ updateCache ( childCache , ( model . get ( 'children' ) || [ ] ) . map ( m => m . cid ) ) ;
119
+ return renderChildren ( model . get ( 'children' ) , childCache , parentView , slotScopes ) ;
120
+ } ,
121
+ ...createSlots ( model , this , parentView , slotScopes )
122
+ } ,
107
123
) ;
108
- updateCache ( this ) ;
124
+
109
125
return element ;
110
126
} ,
111
127
} , { ...model . get ( 'slot' ) && { slot : model . get ( 'slot' ) } } ) ;
112
128
113
- /* Impersonate the wrapped component (e.g. v-tabs uses this name to detect v-tab and
114
- * v-tab-item) */
115
- elem . componentOptions . Ctor . options . name = tag ;
116
129
return elem ;
117
130
}
118
131
@@ -147,33 +160,11 @@ function createAttrsMapping(model) {
147
160
}
148
161
149
162
function addEventWithModifiers ( eventAndModifiers , obj , fn ) { // eslint-disable-line no-unused-vars
150
- /* Example Vue.compile output:
151
- * (function anonymous() {
152
- * with (this) {
153
- * return _c('dummy', {
154
- * on: {
155
- * "[event]": function ($event) {
156
- * if (!$event.type.indexOf('key') && _k($event.keyCode, "c", ...)
157
- * return null;
158
- * ...
159
- * return [fn]($event)
160
- * }
161
- * }
162
- * })
163
- * }
164
- * }
165
- * )
166
- */
167
- const { on } = Vue . compile ( `<dummy @${ eventAndModifiers } ="fn"></dummy>` )
168
- . render . bind ( {
169
- _c : ( _ , data ) => data ,
170
- _k : Vue . prototype . _k ,
171
- fn,
172
- } ) ( ) ;
163
+ const [ event , ...mods ] = eventAndModifiers . split ( "." ) ;
173
164
174
165
return {
175
166
...obj ,
176
- ... on ,
167
+ [ `on ${ event . charAt ( 0 ) . toUpperCase ( ) } ${ event . slice ( 1 ) } ` ] : Vue . withModifiers ( fn , mods ) ,
177
168
} ;
178
169
}
179
170
@@ -192,104 +183,79 @@ function createEventMapping(model, parentView) {
192
183
) , { } ) ;
193
184
}
194
185
195
- function createSlots ( createElement , model , vueModel , parentView , slotScopes ) {
186
+ function createSlots ( model , vueModel , parentView , slotScopes ) {
196
187
const slots = model . get ( 'v_slots' ) ;
197
188
if ( ! slots ) {
198
189
return undefined ;
199
190
}
200
- return slots . map ( slot => ( {
201
- key : slot . name ,
202
- ...! slot . variable && { proxy : true } ,
203
- fn ( slotScope ) {
204
- return renderChildren ( createElement ,
191
+ const childCache = { } ;
192
+
193
+ return slots . reduce ( ( res , slot ) => ( {
194
+ ...res ,
195
+ [ slot . name ] : ( slotScope ) => {
196
+ return renderChildren (
205
197
Array . isArray ( slot . children ) ? slot . children : [ slot . children ] ,
206
- vueModel , parentView , {
198
+ childCache , parentView , {
207
199
...slotScopes ,
208
200
...slot . variable && { [ slot . variable ] : slotScope } ,
209
201
} ) ;
210
202
} ,
211
- } ) ) ;
212
- }
213
-
214
- function getScope ( value , slotScopes ) {
215
- const parts = value . split ( '.' ) ;
216
- return parts
217
- . slice ( 1 )
218
- . reduce (
219
- ( scope , name ) => scope [ name ] ,
220
- slotScopes [ parts [ 0 ] ] ,
221
- ) ;
222
- }
223
-
224
- function getScopes ( value , slotScopes ) {
225
- return typeof value === 'string'
226
- ? getScope ( value , slotScopes )
227
- : Object . assign ( { } , ...value . map ( v => getScope ( v , slotScopes ) ) ) ;
203
+ } ) , { } ) ;
228
204
}
229
205
230
206
function slotUseOn ( model , slotScopes ) {
231
207
const vOnValue = model . get ( 'v_on' ) ;
232
- return vOnValue && getScopes ( vOnValue , slotScopes ) ;
208
+ return vOnValue && filterObject ( slotScopes [ vOnValue . split ( '.' ) [ 0 ] ] . props , ( key , value ) => key . startsWith ( 'on' ) )
209
+ }
210
+
211
+ function filterObject ( obj , predicate ) {
212
+ return Object . entries ( obj )
213
+ . filter ( ( [ key , value ] ) => predicate ( key , value ) )
214
+ . reduce ( ( res , [ key , value ] ) => ( { ...res , [ key ] : value } ) , { } ) ;
233
215
}
234
216
235
- function createContent ( createElement , model , vueModel , parentView , slotScopes ) {
217
+ function createContent ( model , vueModel , parentView , slotScopes ) {
236
218
const htmlEventAttributes = model . get ( 'attributes' ) && Object . keys ( model . get ( 'attributes' ) ) . filter ( key => key . startsWith ( 'on' ) ) ;
237
219
if ( htmlEventAttributes && htmlEventAttributes . length > 0 ) {
238
220
throw new Error ( `No HTML event attributes may be used: ${ htmlEventAttributes } ` ) ;
239
221
}
240
222
241
- const scopedSlots = createSlots ( createElement , model , vueModel , parentView , slotScopes ) ;
242
-
243
223
return {
244
- on : { ...createEventMapping ( model , parentView ) , ...slotUseOn ( model , slotScopes ) } ,
224
+ ...slotUseOn ( model , slotScopes ) ,
225
+ ...createEventMapping ( model , parentView ) ,
245
226
...model . get ( 'style_' ) && { style : model . get ( 'style_' ) } ,
246
227
...model . get ( 'class_' ) && { class : model . get ( 'class_' ) } ,
247
- ...scopedSlots && { scopedSlots : vueModel . _u ( scopedSlots ) } ,
248
- attrs : {
249
- ...createAttrsMapping ( model ) ,
250
- ...model . get ( 'attributes' ) && model . get ( 'attributes' ) ,
251
- } ,
228
+ ...createAttrsMapping ( model ) ,
229
+ ...model . get ( 'attributes' ) && model . get ( 'attributes' ) ,
252
230
...model . get ( 'v_model' ) !== '!!disabled!!' && {
253
- model : {
254
- value : vueModel . v_model ,
255
- callback : ( v ) => {
256
- model . set ( 'v_model' , v === undefined ? null : v ) ;
257
- model . save_changes ( model . callbacks ( parentView ) ) ;
258
- } ,
259
- expression : 'v_model' ,
231
+ modelValue : vueModel . v_model ,
232
+ "onUpdate:modelValue" : ( v ) => {
233
+ model . set ( 'v_model' , v === undefined ? null : v ) ;
234
+ model . save_changes ( model . callbacks ( parentView ) ) ;
260
235
} ,
261
236
} ,
262
237
} ;
263
238
}
264
239
265
- function renderChildren ( createElement , children , vueModel , parentView , slotScopes ) {
266
- if ( ! vueModel . childCache ) {
267
- vueModel . childCache = { } ; // eslint-disable-line no-param-reassign
268
- }
269
- if ( ! vueModel . childIds ) {
270
- vueModel . childIds = [ ] ; // eslint-disable-line no-param-reassign
271
- }
240
+ function renderChildren ( children , childCache , parentView , slotScopes ) {
272
241
const childViewModels = children . map ( ( child ) => {
273
242
if ( typeof ( child ) === 'string' ) {
274
243
return child ;
275
244
}
276
- vueModel . childIds . push ( child . cid ) ;
277
-
278
- if ( vueModel . childCache [ child . cid ] ) {
279
- return vueModel . childCache [ child . cid ] ;
245
+ if ( childCache [ child . cid ] ) {
246
+ return childCache [ child . cid ] ;
280
247
}
281
- const vm = vueRender ( createElement , child , parentView , slotScopes ) ;
282
- vueModel . childCache [ child . cid ] = vm ; // eslint-disable-line no-param-reassign
248
+ const vm = vueRender ( child , parentView , slotScopes ) ;
249
+ childCache [ child . cid ] = vm ; // eslint-disable-line no-param-reassign
283
250
return vm ;
284
251
} ) ;
285
252
286
253
return childViewModels ;
287
254
}
288
255
289
- function updateCache ( vueModel ) {
290
- Object . keys ( vueModel . childCache )
291
- . filter ( key => ! vueModel . childIds . includes ( key ) )
256
+ function updateCache ( childCache , usedChildIds ) {
257
+ Object . keys ( childCache )
258
+ . filter ( key => ! usedChildIds . includes ( key ) )
292
259
// eslint-disable-next-line no-param-reassign
293
- . forEach ( key => delete vueModel . childCache [ key ] ) ;
294
- vueModel . childIds = [ ] ; // eslint-disable-line no-param-reassign
260
+ . forEach ( key => delete childCache [ key ] ) ;
295
261
}
0 commit comments