@@ -3,7 +3,7 @@ import * as base from '@jupyter-widgets/base';
33import { vueTemplateRender } from './VueTemplateRenderer' ; // eslint-disable-line import/no-cycle
44import { VueModel } from './VueModel' ;
55import { VueTemplateModel } from './VueTemplateModel' ;
6- import Vue from './VueWithCompiler ' ;
6+ import * as Vue from 'vue ' ;
77
88const JupyterPhosphorWidget = base . JupyterPhosphorWidget || base . JupyterLuminoWidget ;
99
@@ -45,8 +45,8 @@ export function createObjectForNestedModel(model, parentView) {
4545 destroyed = true ;
4646 }
4747 } ,
48- render ( createElement ) {
49- return createElement ( 'div' , { style : { height : '100%' } } ) ;
48+ render ( ) {
49+ return Vue . h ( 'div' , { style : { height : '100%' } } ) ;
5050 } ,
5151 } ;
5252}
@@ -81,16 +81,26 @@ export function eventToObject(event) {
8181 return event ;
8282}
8383
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 ) {
8593 if ( model instanceof VueTemplateModel ) {
86- return vueTemplateRender ( createElement , model , parentView ) ;
94+ return vueTemplateRender ( model , parentView ) ;
8795 }
8896 if ( ! ( model instanceof VueModel ) ) {
89- return createElement ( createObjectForNestedModel ( model , parentView ) ) ;
97+ return Vue . h ( createObjectForNestedModel ( model , parentView ) ) ;
9098 }
9199 const tag = model . getVueTag ( ) ;
92100
93- const elem = createElement ( {
101+ const childCache = { } ;
102+
103+ const elem = Vue . h ( {
94104 data ( ) {
95105 return {
96106 v_model : model . get ( 'v_model' ) ,
@@ -99,20 +109,23 @@ export function vueRender(createElement, model, parentView, slotScopes) {
99109 created ( ) {
100110 addListeners ( model , this ) ;
101111 } ,
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+ } ,
107123 ) ;
108- updateCache ( this ) ;
124+
109125 return element ;
110126 } ,
111127 } , { ...model . get ( 'slot' ) && { slot : model . get ( 'slot' ) } } ) ;
112128
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 ;
116129 return elem ;
117130}
118131
@@ -147,33 +160,11 @@ function createAttrsMapping(model) {
147160}
148161
149162function 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 ( "." ) ;
173164
174165 return {
175166 ...obj ,
176- ... on ,
167+ [ `on ${ event . charAt ( 0 ) . toUpperCase ( ) } ${ event . slice ( 1 ) } ` ] : Vue . withModifiers ( fn , mods ) ,
177168 } ;
178169}
179170
@@ -192,104 +183,79 @@ function createEventMapping(model, parentView) {
192183 ) , { } ) ;
193184}
194185
195- function createSlots ( createElement , model , vueModel , parentView , slotScopes ) {
186+ function createSlots ( model , vueModel , parentView , slotScopes ) {
196187 const slots = model . get ( 'v_slots' ) ;
197188 if ( ! slots ) {
198189 return undefined ;
199190 }
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 (
205197 Array . isArray ( slot . children ) ? slot . children : [ slot . children ] ,
206- vueModel , parentView , {
198+ childCache , parentView , {
207199 ...slotScopes ,
208200 ...slot . variable && { [ slot . variable ] : slotScope } ,
209201 } ) ;
210202 } ,
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+ } ) , { } ) ;
228204}
229205
230206function slotUseOn ( model , slotScopes ) {
231207 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 } ) , { } ) ;
233215}
234216
235- function createContent ( createElement , model , vueModel , parentView , slotScopes ) {
217+ function createContent ( model , vueModel , parentView , slotScopes ) {
236218 const htmlEventAttributes = model . get ( 'attributes' ) && Object . keys ( model . get ( 'attributes' ) ) . filter ( key => key . startsWith ( 'on' ) ) ;
237219 if ( htmlEventAttributes && htmlEventAttributes . length > 0 ) {
238220 throw new Error ( `No HTML event attributes may be used: ${ htmlEventAttributes } ` ) ;
239221 }
240222
241- const scopedSlots = createSlots ( createElement , model , vueModel , parentView , slotScopes ) ;
242-
243223 return {
244- on : { ...createEventMapping ( model , parentView ) , ...slotUseOn ( model , slotScopes ) } ,
224+ ...slotUseOn ( model , slotScopes ) ,
225+ ...createEventMapping ( model , parentView ) ,
245226 ...model . get ( 'style_' ) && { style : model . get ( 'style_' ) } ,
246227 ...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' ) ,
252230 ...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 ) ) ;
260235 } ,
261236 } ,
262237 } ;
263238}
264239
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 ) {
272241 const childViewModels = children . map ( ( child ) => {
273242 if ( typeof ( child ) === 'string' ) {
274243 return child ;
275244 }
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 ] ;
280247 }
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
283250 return vm ;
284251 } ) ;
285252
286253 return childViewModels ;
287254}
288255
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 ) )
292259 // 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 ] ) ;
295261}
0 commit comments