@@ -77,6 +77,11 @@ class Compiler
77
77
*/
78
78
protected $ rawBlocks = [];
79
79
80
+ /**
81
+ * @var string[]
82
+ */
83
+ protected $ includeAttributes = ['class ' , 'style ' ];
84
+
80
85
/**
81
86
* Compiler constructor.
82
87
*/
@@ -238,7 +243,7 @@ public function convertNode(DOMNode $node, int $level = 0): DOMNode
238
243
$ include = $ this ->document ->createTextNode (
239
244
$ this ->builder ->createIncludePartial (
240
245
$ usedComponent ->getPath (),
241
- $ usedComponent ->getProperties ()
246
+ $ this -> preparePropertiesForInclude ( $ usedComponent ->getProperties () )
242
247
)
243
248
);
244
249
@@ -273,7 +278,9 @@ public function convertNode(DOMNode $node, int $level = 0): DOMNode
273
278
if ($ node instanceof DOMElement) {
274
279
$ this ->handleAttributeBinding ($ node );
275
280
if ($ level === 1 ) {
276
- $ this ->handleRootNodeClassAttribute ($ node );
281
+ foreach ($ this ->includeAttributes as $ attribute ) {
282
+ $ this ->handleRootNodeAttribute ($ node , $ attribute );
283
+ }
277
284
}
278
285
}
279
286
@@ -284,6 +291,44 @@ public function convertNode(DOMNode $node, int $level = 0): DOMNode
284
291
return $ node ;
285
292
}
286
293
294
+ /**
295
+ * @param Property[] $variables
296
+ *
297
+ * @throws ReflectionException
298
+ *
299
+ * @return Property[]
300
+ */
301
+ private function preparePropertiesForInclude (array $ variables ): array
302
+ {
303
+ $ values = [];
304
+ foreach ($ variables as $ key => $ variable ) {
305
+ $ name = $ variable ->getName ();
306
+ $ value = $ variable ->getValue ();
307
+ if (in_array ($ name , $ this ->includeAttributes )) {
308
+ if ($ variable ->isBinding ()) {
309
+ $ values [$ name ][] = $ this ->handleBinding ($ value , $ name , null , false )[0 ];
310
+ } else {
311
+ $ values [$ name ][] = $ value ;
312
+ }
313
+ unset($ variables [$ key ]);
314
+ }
315
+ }
316
+
317
+ foreach ($ this ->includeAttributes as $ attribute ) {
318
+ $ glue = ' ~ " " ~ ' ;
319
+ if ($ attribute === 'style ' ) {
320
+ $ glue = ' ~ "; " ~ ' ;
321
+ }
322
+ $ variables [] = new Property (
323
+ $ attribute ,
324
+ $ values [$ attribute ] ?? null ? implode ($ glue , $ values [$ attribute ]) : '"" ' ,
325
+ false
326
+ );
327
+ }
328
+
329
+ return $ variables ;
330
+ }
331
+
287
332
public function registerProperties (DOMElement $ scriptElement ): void
288
333
{
289
334
$ content = $ this ->innerHtmlOfNode ($ scriptElement );
@@ -349,7 +394,6 @@ private function handleAttributeBinding(DOMElement $node): void
349
394
$ this ->logger ->debug ('- handle: ' . $ name . ' = ' . $ value );
350
395
351
396
$ staticValues = $ node ->hasAttribute ($ name ) ? $ node ->getAttribute ($ name ) : '' ;
352
- $ dynamicValues = [];
353
397
354
398
// Remove originally bound attribute
355
399
$ this ->logger ->debug ('- remove original ' . $ attribute ->name );
@@ -359,73 +403,7 @@ private function handleAttributeBinding(DOMElement $node): void
359
403
continue ;
360
404
}
361
405
362
- $ regexArrayBinding = '/^\[([^\]]+)\]$/ ' ;
363
- $ regexArrayElements = '/((?:[ \'"])(?<elements>[^ \'"])[ \'"])/ ' ;
364
- $ regexTemplateString = '/^`(?P<content>.+)`$/ ' ;
365
- $ regexObjectBinding = '/^\{(?<elements>[^\}]+)\}$/ ' ;
366
- $ regexObjectElements = '/[" \']?(?<class>[^" \']+)[" \']?:\s*(?<condition>[^,]+)/x ' ;
367
-
368
- if ($ value === 'true ' ) {
369
- $ this ->logger ->debug ('- setAttribute ' . $ name );
370
- $ node ->setAttribute ($ name , $ name );
371
- } elseif (preg_match ($ regexArrayBinding , $ value , $ matches )) {
372
- $ this ->logger ->debug ('- array binding ' , ['value ' => $ value ]);
373
-
374
- if (preg_match_all ($ regexArrayElements , $ value , $ arrayMatch )) {
375
- $ value = $ arrayMatch ['elements ' ];
376
- $ this ->logger ->debug ('- ' , ['match ' => $ arrayMatch ]);
377
- } else {
378
- $ value = [];
379
- }
380
-
381
- if ($ name === 'style ' ) {
382
- foreach ($ value as $ prop => $ setting ) {
383
- if ($ setting ) {
384
- $ prop = strtolower ($ this ->transformCamelCaseToCSS ($ prop ));
385
- $ dynamicValues [] = sprintf ('%s:%s ' , $ prop , $ setting );
386
- }
387
- }
388
- } elseif ($ name === 'class ' ) {
389
- foreach ($ value as $ className ) {
390
- $ dynamicValues [] = $ className ;
391
- }
392
- }
393
- } elseif (preg_match ($ regexObjectBinding , $ value , $ matches )) {
394
- $ this ->logger ->debug ('- object binding ' , ['value ' => $ value ]);
395
-
396
- $ items = explode (', ' , $ matches ['elements ' ]);
397
-
398
- foreach ($ items as $ item ) {
399
- if (preg_match ($ regexObjectElements , $ item , $ matchElement )) {
400
- $ dynamicValues [] = sprintf (
401
- '{{ %s ? \'%s \' }} ' ,
402
- $ this ->builder ->refactorCondition ($ matchElement ['condition ' ]),
403
- $ matchElement ['class ' ] . ' '
404
- );
405
- }
406
- }
407
- } elseif (preg_match ($ regexTemplateString , $ value , $ matches )) {
408
- // <div :class="`abc ${someDynamicClass}`">
409
- $ templateStringContent = $ matches ['content ' ];
410
-
411
- preg_match_all ('/\${([^}]+)}/ ' , $ templateStringContent , $ matches , PREG_SET_ORDER );
412
- foreach ($ matches as $ match ) {
413
- $ templateStringContent = str_replace (
414
- $ match [0 ],
415
- '{{ ' . $ this ->builder ->refactorCondition ($ match [1 ]) . ' }} ' ,
416
- $ templateStringContent
417
- );
418
- }
419
-
420
- $ dynamicValues [] = $ templateStringContent ;
421
- } else {
422
- $ value = $ this ->builder ->refactorCondition ($ value );
423
- $ this ->logger ->debug (sprintf ('- setAttribute "%s" with value "%s" ' , $ name , $ value ));
424
- $ dynamicValues [] =
425
- Replacements::getSanitizedConstant ('DOUBLE_CURLY_OPEN ' ) .
426
- $ value .
427
- Replacements::getSanitizedConstant ('DOUBLE_CURLY_CLOSE ' );
428
- }
406
+ $ dynamicValues = $ this ->handleBinding ($ value , $ name , $ node );
429
407
430
408
/* @see https://gitlab.gnome.org/GNOME/libxml2/-/blob/LIBXML2.6.32/HTMLtree.c#L657 */
431
409
switch ($ name ) {
@@ -451,6 +429,97 @@ private function handleAttributeBinding(DOMElement $node): void
451
429
}
452
430
}
453
431
432
+ /**
433
+ * @throws ReflectionException
434
+ *
435
+ * @return string[]
436
+ */
437
+ public function handleBinding (string $ value , string $ name , ?DOMElement $ node = null , bool $ twigOutput = true ): array
438
+ {
439
+ $ dynamicValues = [];
440
+
441
+ $ regexArrayBinding = '/^\[([^\]]+)\]$/ ' ;
442
+ $ regexArrayElements = '/((?:[ \'"])(?<elements>[^ \'"])[ \'"])/ ' ;
443
+ $ regexTemplateString = '/^`(?P<content>.+)`$/ ' ;
444
+ $ regexObjectBinding = '/^\{(?<elements>[^\}]+)\}$/ ' ;
445
+ $ regexObjectElements = '/[" \']?(?<class>[^" \']+)[" \']?\s*:\s*(?<condition>[^,]+)/x ' ;
446
+
447
+ if ($ value === 'true ' ) {
448
+ $ this ->logger ->debug ('- setAttribute ' . $ name );
449
+ if ($ node ) {
450
+ $ node ->setAttribute ($ name , $ name );
451
+ }
452
+ } elseif (preg_match ($ regexArrayBinding , $ value , $ matches )) {
453
+ $ this ->logger ->debug ('- array binding ' , ['value ' => $ value ]);
454
+
455
+ if (preg_match_all ($ regexArrayElements , $ value , $ arrayMatch )) {
456
+ $ value = $ arrayMatch ['elements ' ];
457
+ $ this ->logger ->debug ('- ' , ['match ' => $ arrayMatch ]);
458
+ } else {
459
+ $ value = [];
460
+ }
461
+
462
+ if ($ name === 'style ' ) {
463
+ foreach ($ value as $ prop => $ setting ) {
464
+ if ($ setting ) {
465
+ $ prop = strtolower ($ this ->transformCamelCaseToCSS ($ prop ));
466
+ $ dynamicValues [] = sprintf ('%s:%s ' , $ prop , $ setting );
467
+ }
468
+ }
469
+ } elseif ($ name === 'class ' ) {
470
+ foreach ($ value as $ className ) {
471
+ $ dynamicValues [] = $ className ;
472
+ }
473
+ }
474
+ } elseif (preg_match ($ regexObjectBinding , $ value , $ matches )) {
475
+ $ this ->logger ->debug ('- object binding ' , ['value ' => $ value ]);
476
+
477
+ $ items = explode (', ' , $ matches ['elements ' ]);
478
+
479
+ foreach ($ items as $ item ) {
480
+ if (preg_match ($ regexObjectElements , $ item , $ matchElement )) {
481
+ $ dynamicValues [] = $ this ->prepareBindingOutput (
482
+ $ this ->builder ->refactorCondition ($ matchElement ['condition ' ]) . ' ? \'' . $ matchElement ['class ' ] . ' \'' ,
483
+ $ twigOutput
484
+ );
485
+ }
486
+ }
487
+ } elseif (preg_match ($ regexTemplateString , $ value , $ matches )) {
488
+ // <div :class="`abc ${someDynamicClass}`">
489
+ $ templateStringContent = $ matches ['content ' ];
490
+
491
+ preg_match_all ('/\${([^}]+)}/ ' , $ templateStringContent , $ matches , PREG_SET_ORDER );
492
+ foreach ($ matches as $ match ) {
493
+ $ templateStringContent = str_replace (
494
+ $ match [0 ],
495
+ $ this ->prepareBindingOutput ($ this ->builder ->refactorCondition ($ match [1 ]), $ twigOutput ),
496
+ $ templateStringContent
497
+ );
498
+ }
499
+
500
+ $ dynamicValues [] = $ templateStringContent ;
501
+ } else {
502
+ $ value = $ this ->builder ->refactorCondition ($ value );
503
+ $ this ->logger ->debug (sprintf ('- setAttribute "%s" with value "%s" ' , $ name , $ value ));
504
+ $ dynamicValues [] = $ this ->prepareBindingOutput ($ value , $ twigOutput );
505
+ }
506
+
507
+ return $ dynamicValues ;
508
+ }
509
+
510
+ private function prepareBindingOutput (string $ value , bool $ twigOutput = true ): string
511
+ {
512
+ $ open = Replacements::getSanitizedConstant ('DOUBLE_CURLY_OPEN ' );
513
+ $ close = Replacements::getSanitizedConstant ('DOUBLE_CURLY_CLOSE ' );
514
+
515
+ if (!$ twigOutput ) {
516
+ $ open = '( ' ;
517
+ $ close = ') ' ;
518
+ }
519
+
520
+ return $ open . ' ' . $ value . ' ' . $ close ;
521
+ }
522
+
454
523
/**
455
524
* @throws ReflectionException
456
525
*/
@@ -641,7 +710,10 @@ protected function addDefaultsToVariable(string $varName, string $string): strin
641
710
return $ string ;
642
711
}
643
712
644
- private function transformCamelCaseToCSS (string $ property ): string
713
+ /**
714
+ * @throws RuntimeException
715
+ */
716
+ public function transformCamelCaseToCSS (string $ property ): string
645
717
{
646
718
$ cssProperty = preg_replace ('/([A-Z])/ ' , '-$1 ' , $ property );
647
719
@@ -671,17 +743,20 @@ private function stripEventHandlers(DOMElement $node): void
671
743
*/
672
744
protected function implodeAttributeValue (string $ attribute , array $ values , string $ oldValue ): string
673
745
{
674
- $ glue = ' ' ;
675
-
676
746
if ($ attribute === 'style ' ) {
677
- $ glue = '; ' ;
747
+ if (!empty ($ oldValue )) {
748
+ $ oldValue = trim ($ oldValue , '; ' ) . '; ' ;
749
+ }
750
+ foreach ($ values as &$ value ) {
751
+ $ value = trim ($ value , '; ' ) . '; ' ;
752
+ }
678
753
}
679
754
680
755
if (!empty ($ oldValue )) {
681
756
$ values = array_merge ([$ oldValue ], $ values );
682
757
}
683
758
684
- return implode ($ glue , $ values );
759
+ return trim ( implode (' ' , $ values) );
685
760
}
686
761
687
762
/**
@@ -735,7 +810,7 @@ public function refactorTemplateString(string $value): string
735
810
$ templateStringContent = '" ' . $ matches ['content ' ] . '" ' ;
736
811
$ value = preg_replace (
737
812
'/\${(.+)}/ ' ,
738
- '{{ $1 }} ' ,
813
+ '" ~ ( $1 ) ~ " ' ,
739
814
$ templateStringContent
740
815
);
741
816
}
@@ -786,6 +861,15 @@ public function setStripWhitespace(bool $stripWhitespace): Compiler
786
861
return $ this ;
787
862
}
788
863
864
+ public function disableStyleInclude (): Compiler
865
+ {
866
+ if (($ key = array_search ('style ' , $ this ->includeAttributes )) !== false ) {
867
+ unset($ this ->includeAttributes [$ key ]);
868
+ }
869
+
870
+ return $ this ;
871
+ }
872
+
789
873
/**
790
874
* @param mixed $value
791
875
*/
@@ -859,16 +943,19 @@ protected function insertDefaultValues(): void
859
943
}
860
944
}
861
945
862
- protected function handleRootNodeClassAttribute (DOMElement $ node ): DOMElement
946
+ protected function handleRootNodeAttribute (DOMElement $ node, ? string $ name = null ): DOMElement
863
947
{
864
- $ classString = "__DOUBLE_CURLY_OPEN__class__PIPE__default('')__DOUBLE_CURLY_CLOSE__ " ;
865
- if ($ node ->hasAttribute ('class ' )) {
866
- $ attributeVClass = $ node ->getAttributeNode ('class ' );
867
- $ attributeVClass ->value .= ' ' . $ classString ;
948
+ if (!$ name ) {
949
+ return $ node ;
950
+ }
951
+ $ string = $ this ->prepareBindingOutput ($ name . '|default( \'\') ' );
952
+ if ($ node ->hasAttribute ($ name )) {
953
+ $ attribute = $ node ->getAttributeNode ($ name );
954
+ $ attribute ->value .= ' ' . $ string ;
868
955
} else {
869
- $ attributeVClass = new DOMAttr (' class ' , $ classString );
956
+ $ attribute = new DOMAttr ($ name , $ string );
870
957
}
871
- $ node ->setAttributeNode ($ attributeVClass );
958
+ $ node ->setAttributeNode ($ attribute );
872
959
873
960
return $ node ;
874
961
}
0 commit comments