@@ -11,11 +11,11 @@ use syn::parse;
11
11
use syn:: parse:: { Parse , ParseStream , Result as ParseResult } ;
12
12
use syn:: punctuated:: Punctuated ;
13
13
use syn:: spanned:: Spanned ;
14
- use syn:: { Ident , Path , PathArguments , PathSegment , Token , Type , TypePath } ;
14
+ use syn:: { Expr , Ident , Path , PathArguments , PathSegment , Token , Type , TypePath } ;
15
15
16
16
pub struct HtmlComponent {
17
17
ty : Type ,
18
- props : Option < Props > ,
18
+ props : Props ,
19
19
children : Vec < HtmlTreeNested > ,
20
20
}
21
21
@@ -85,9 +85,9 @@ impl ToTokens for HtmlComponent {
85
85
} = self ;
86
86
let vcomp_scope = Ident :: new ( "__yew_vcomp_scope" , Span :: call_site ( ) ) ;
87
87
88
- let validate_props = if let Some ( Props :: List ( ListProps ( vec_props ) ) ) = props {
88
+ let validate_props = if let Props :: List ( ListProps { props , .. } ) = props {
89
89
let prop_ref = Ident :: new ( "__yew_prop_ref" , Span :: call_site ( ) ) ;
90
- let check_props = vec_props . iter ( ) . map ( |HtmlProp { label, .. } | {
90
+ let check_props = props . iter ( ) . map ( |HtmlProp { label, .. } | {
91
91
quote ! { #prop_ref. #label; }
92
92
} ) ;
93
93
@@ -136,30 +136,27 @@ impl ToTokens for HtmlComponent {
136
136
quote ! { }
137
137
} ;
138
138
139
- let init_props = if let Some ( props) = props {
140
- match props {
141
- Props :: List ( ListProps ( vec_props) ) => {
142
- let set_props = vec_props. iter ( ) . map ( |HtmlProp { label, value } | {
143
- quote_spanned ! { value. span( ) =>
144
- . #label( <:: yew:: virtual_dom:: vcomp:: VComp <_> as :: yew:: virtual_dom:: vcomp:: Transformer <_, _, _>>:: transform( #vcomp_scope. clone( ) , #value) )
145
- }
146
- } ) ;
147
-
148
- quote ! {
149
- <<#ty as :: yew:: html:: Component >:: Properties as :: yew:: html:: Properties >:: builder( )
150
- #( #set_props) *
151
- #set_children
152
- . build( )
139
+ let init_props = match props {
140
+ Props :: List ( ListProps { props, .. } ) => {
141
+ let set_props = props. iter ( ) . map ( |HtmlProp { label, value } | {
142
+ quote_spanned ! { value. span( ) =>
143
+ . #label( <:: yew:: virtual_dom:: vcomp:: VComp <_> as :: yew:: virtual_dom:: vcomp:: Transformer <_, _, _>>:: transform( #vcomp_scope. clone( ) , #value) )
153
144
}
145
+ } ) ;
146
+
147
+ quote ! {
148
+ <<#ty as :: yew:: html:: Component >:: Properties as :: yew:: html:: Properties >:: builder( )
149
+ #( #set_props) *
150
+ #set_children
151
+ . build( )
154
152
}
155
- Props :: With ( WithProps ( props) ) => quote ! { #props } ,
156
153
}
157
- } else {
158
- quote ! {
154
+ Props :: With ( WithProps { props , .. } ) => quote ! { #props } ,
155
+ Props :: None => quote ! {
159
156
<<#ty as :: yew:: html:: Component >:: Properties as :: yew:: html:: Properties >:: builder( )
160
157
#set_children
161
158
. build( )
162
- }
159
+ } ,
163
160
} ;
164
161
165
162
let validate_comp = quote_spanned ! { ty. span( ) =>
@@ -171,6 +168,12 @@ impl ToTokens for HtmlComponent {
171
168
}
172
169
} ;
173
170
171
+ let node_ref = if let Some ( node_ref) = props. node_ref ( ) {
172
+ quote_spanned ! { node_ref. span( ) => #node_ref }
173
+ } else {
174
+ quote ! { :: yew:: html:: NodeRef :: default ( ) }
175
+ } ;
176
+
174
177
tokens. extend ( quote ! { {
175
178
// Validation nevers executes at runtime
176
179
if false {
@@ -179,7 +182,8 @@ impl ToTokens for HtmlComponent {
179
182
}
180
183
181
184
let #vcomp_scope: :: yew:: virtual_dom:: vcomp:: ScopeHolder <_> = :: std:: default :: Default :: default ( ) ;
182
- :: yew:: virtual_dom:: VChild :: <#ty, _>:: new( #init_props, #vcomp_scope)
185
+ let __yew_node_ref: :: yew:: html:: NodeRef = #node_ref;
186
+ :: yew:: virtual_dom:: VChild :: <#ty, _>:: new( #init_props, #vcomp_scope, __yew_node_ref)
183
187
} } ) ;
184
188
}
185
189
}
@@ -244,7 +248,7 @@ impl HtmlComponent {
244
248
struct HtmlComponentOpen {
245
249
lt : Token ! [ <] ,
246
250
ty : Type ,
247
- props : Option < Props > ,
251
+ props : Props ,
248
252
div : Option < Token ! [ /] > ,
249
253
gt : Token ! [ >] ,
250
254
}
@@ -264,7 +268,7 @@ impl Parse for HtmlComponentOpen {
264
268
// backwards compat
265
269
let _ = input. parse :: < Token ! [ : ] > ( ) ;
266
270
let HtmlPropSuffix { stream, div, gt } = input. parse ( ) ?;
267
- let props: Option < Props > = parse ( stream) . ok ( ) ;
271
+ let props = parse ( stream) ? ;
268
272
269
273
Ok ( HtmlComponentOpen {
270
274
lt,
@@ -327,6 +331,17 @@ enum PropType {
327
331
enum Props {
328
332
List ( ListProps ) ,
329
333
With ( WithProps ) ,
334
+ None ,
335
+ }
336
+
337
+ impl Props {
338
+ fn node_ref ( & self ) -> Option < & Expr > {
339
+ match self {
340
+ Props :: List ( ListProps { node_ref, .. } ) => node_ref. as_ref ( ) ,
341
+ Props :: With ( WithProps { node_ref, .. } ) => node_ref. as_ref ( ) ,
342
+ Props :: None => None ,
343
+ }
344
+ }
330
345
}
331
346
332
347
impl PeekValue < PropType > for Props {
@@ -344,24 +359,32 @@ impl PeekValue<PropType> for Props {
344
359
345
360
impl Parse for Props {
346
361
fn parse ( input : ParseStream ) -> ParseResult < Self > {
347
- let prop_type = Props :: peek ( input. cursor ( ) )
348
- . ok_or_else ( || syn:: Error :: new ( Span :: call_site ( ) , "ignore - no props found" ) ) ?;
349
- match prop_type {
350
- PropType :: List => input. parse ( ) . map ( Props :: List ) ,
351
- PropType :: With => input. parse ( ) . map ( Props :: With ) ,
362
+ match Props :: peek ( input. cursor ( ) ) {
363
+ Some ( PropType :: List ) => input. parse ( ) . map ( Props :: List ) ,
364
+ Some ( PropType :: With ) => input. parse ( ) . map ( Props :: With ) ,
365
+ None => Ok ( Props :: None ) ,
352
366
}
353
367
}
354
368
}
355
369
356
- struct ListProps ( Vec < HtmlProp > ) ;
370
+ struct ListProps {
371
+ props : Vec < HtmlProp > ,
372
+ node_ref : Option < Expr > ,
373
+ }
374
+
357
375
impl Parse for ListProps {
358
376
fn parse ( input : ParseStream ) -> ParseResult < Self > {
359
377
let mut props: Vec < HtmlProp > = Vec :: new ( ) ;
360
378
while HtmlProp :: peek ( input. cursor ( ) ) . is_some ( ) {
361
379
props. push ( input. parse :: < HtmlProp > ( ) ?) ;
362
380
}
363
381
382
+ let ref_position = props. iter ( ) . position ( |p| p. label . to_string ( ) == "ref" ) ;
383
+ let node_ref = ref_position. and_then ( |i| Some ( props. remove ( i) . value ) ) ;
364
384
for prop in & props {
385
+ if prop. label . to_string ( ) == "ref" {
386
+ return Err ( syn:: Error :: new_spanned ( & prop. label , "too many refs set" ) ) ;
387
+ }
365
388
if prop. label . to_string ( ) == "type" {
366
389
return Err ( syn:: Error :: new_spanned ( & prop. label , "expected identifier" ) ) ;
367
390
}
@@ -386,11 +409,15 @@ impl Parse for ListProps {
386
409
}
387
410
} ) ;
388
411
389
- Ok ( ListProps ( props) )
412
+ Ok ( ListProps { props, node_ref } )
390
413
}
391
414
}
392
415
393
- struct WithProps ( Ident ) ;
416
+ struct WithProps {
417
+ props : Ident ,
418
+ node_ref : Option < Expr > ,
419
+ }
420
+
394
421
impl Parse for WithProps {
395
422
fn parse ( input : ParseStream ) -> ParseResult < Self > {
396
423
let with = input. parse :: < Ident > ( ) ?;
@@ -399,6 +426,18 @@ impl Parse for WithProps {
399
426
}
400
427
let props = input. parse :: < Ident > ( ) ?;
401
428
let _ = input. parse :: < Token ! [ , ] > ( ) ;
402
- Ok ( WithProps ( props) )
429
+
430
+ // Check for the ref tag after `with`
431
+ let mut node_ref = None ;
432
+ if let Some ( ident) = input. cursor ( ) . ident ( ) {
433
+ let prop = input. parse :: < HtmlProp > ( ) ?;
434
+ if ident. 0 == "ref" {
435
+ node_ref = Some ( prop. value ) ;
436
+ } else {
437
+ return Err ( syn:: Error :: new_spanned ( & prop. label , "unexpected token" ) ) ;
438
+ }
439
+ }
440
+
441
+ Ok ( WithProps { props, node_ref } )
403
442
}
404
443
}
0 commit comments