@@ -1728,7 +1728,7 @@ class ChildComponentWrapper {
1728
1728
}
1729
1729
}
1730
1730
class Component {
1731
- constructor ( element , props , fingerprint , id , backend , elementDriver ) {
1731
+ constructor ( element , name , props , listeners , componentFinder , fingerprint , id , backend , elementDriver ) {
1732
1732
this . defaultDebounce = 150 ;
1733
1733
this . backendRequest = null ;
1734
1734
this . pendingActions = [ ] ;
@@ -1737,10 +1737,20 @@ class Component {
1737
1737
this . children = new Map ( ) ;
1738
1738
this . parent = null ;
1739
1739
this . element = element ;
1740
+ this . name = name ;
1741
+ this . componentFinder = componentFinder ;
1740
1742
this . backend = backend ;
1741
1743
this . elementDriver = elementDriver ;
1742
1744
this . id = id ;
1743
1745
this . fingerprint = fingerprint ;
1746
+ this . listeners = new Map ( ) ;
1747
+ listeners . forEach ( ( listener ) => {
1748
+ var _a ;
1749
+ if ( ! this . listeners . has ( listener . event ) ) {
1750
+ this . listeners . set ( listener . event , [ ] ) ;
1751
+ }
1752
+ ( _a = this . listeners . get ( listener . event ) ) === null || _a === void 0 ? void 0 : _a . push ( listener . action ) ;
1753
+ } ) ;
1744
1754
this . valueStore = new ValueStore ( props ) ;
1745
1755
this . unsyncedInputsTracker = new UnsyncedInputsTracker ( this , elementDriver ) ;
1746
1756
this . hooks = new HookManager ( ) ;
@@ -1833,6 +1843,30 @@ class Component {
1833
1843
} ) ;
1834
1844
return children ;
1835
1845
}
1846
+ emit ( name , data , onlyMatchingComponentsNamed = null ) {
1847
+ return this . performEmit ( name , data , false , onlyMatchingComponentsNamed ) ;
1848
+ }
1849
+ emitUp ( name , data , onlyMatchingComponentsNamed = null ) {
1850
+ return this . performEmit ( name , data , true , onlyMatchingComponentsNamed ) ;
1851
+ }
1852
+ emitSelf ( name , data ) {
1853
+ return this . doEmit ( name , data ) ;
1854
+ }
1855
+ performEmit ( name , data , emitUp , matchingName ) {
1856
+ const components = this . componentFinder ( this , emitUp , matchingName ) ;
1857
+ components . forEach ( ( component ) => {
1858
+ component . doEmit ( name , data ) ;
1859
+ } ) ;
1860
+ }
1861
+ doEmit ( name , data ) {
1862
+ if ( ! this . listeners . has ( name ) ) {
1863
+ return ;
1864
+ }
1865
+ const actions = this . listeners . get ( name ) || [ ] ;
1866
+ actions . forEach ( ( action ) => {
1867
+ this . action ( action , data , 1 ) ;
1868
+ } ) ;
1869
+ }
1836
1870
updateFromNewElement ( toEl ) {
1837
1871
const props = this . elementDriver . getComponentProps ( toEl ) ;
1838
1872
if ( props === null ) {
@@ -1937,13 +1971,25 @@ class Component {
1937
1971
}
1938
1972
const newProps = this . elementDriver . getComponentProps ( newElement ) ;
1939
1973
this . valueStore . reinitializeAllProps ( newProps ) ;
1974
+ const eventsToEmit = this . elementDriver . getEventsToEmit ( newElement ) ;
1940
1975
this . externalMutationTracker . handlePendingChanges ( ) ;
1941
1976
this . externalMutationTracker . stop ( ) ;
1942
1977
executeMorphdom ( this . element , newElement , this . unsyncedInputsTracker . getUnsyncedInputs ( ) , ( element ) => getValueFromElement ( element , this . valueStore ) , Array . from ( this . getChildren ( ) . values ( ) ) , this . elementDriver . findChildComponentElement , this . elementDriver . getKeyFromElement , this . externalMutationTracker ) ;
1943
1978
this . externalMutationTracker . start ( ) ;
1944
1979
Object . keys ( modifiedModelValues ) . forEach ( ( modelName ) => {
1945
1980
this . valueStore . set ( modelName , modifiedModelValues [ modelName ] ) ;
1946
1981
} ) ;
1982
+ eventsToEmit . forEach ( ( { event, data, target, componentName } ) => {
1983
+ if ( target === 'up' ) {
1984
+ this . emitUp ( event , data , componentName ) ;
1985
+ return ;
1986
+ }
1987
+ if ( target === 'self' ) {
1988
+ this . emitSelf ( event , data ) ;
1989
+ return ;
1990
+ }
1991
+ this . emit ( event , data , componentName ) ;
1992
+ } ) ;
1947
1993
this . hooks . triggerHook ( 'render:finished' , this ) ;
1948
1994
}
1949
1995
calculateDebounce ( debounce ) {
@@ -2165,6 +2211,11 @@ class StandardElementDriver {
2165
2211
getKeyFromElement ( element ) {
2166
2212
return element . dataset . liveId || null ;
2167
2213
}
2214
+ getEventsToEmit ( element ) {
2215
+ var _a ;
2216
+ const eventsJson = ( _a = element . dataset . liveEmit ) !== null && _a !== void 0 ? _a : '[]' ;
2217
+ return JSON . parse ( eventsJson ) ;
2218
+ }
2168
2219
}
2169
2220
2170
2221
class LoadingPlugin {
@@ -2540,20 +2591,23 @@ function getModelBinding (modelDirective) {
2540
2591
2541
2592
class ComponentRegistry {
2542
2593
constructor ( ) {
2543
- this . components = new WeakMap ( ) ;
2594
+ this . componentMapByElement = new WeakMap ( ) ;
2595
+ this . componentMapByComponent = new Map ( ) ;
2544
2596
}
2545
- registerComponent ( element , definition ) {
2546
- this . components . set ( element , definition ) ;
2597
+ registerComponent ( element , component ) {
2598
+ this . componentMapByElement . set ( element , component ) ;
2599
+ this . componentMapByComponent . set ( component , component . name ) ;
2547
2600
}
2548
- unregisterComponent ( element ) {
2549
- this . components . delete ( element ) ;
2601
+ unregisterComponent ( component ) {
2602
+ this . componentMapByElement . delete ( component . element ) ;
2603
+ this . componentMapByComponent . delete ( component ) ;
2550
2604
}
2551
2605
getComponent ( element ) {
2552
2606
return new Promise ( ( resolve , reject ) => {
2553
2607
let count = 0 ;
2554
2608
const maxCount = 10 ;
2555
2609
const interval = setInterval ( ( ) => {
2556
- const component = this . components . get ( element ) ;
2610
+ const component = this . componentMapByElement . get ( element ) ;
2557
2611
if ( component ) {
2558
2612
resolve ( component ) ;
2559
2613
}
@@ -2565,10 +2619,23 @@ class ComponentRegistry {
2565
2619
} , 5 ) ;
2566
2620
} ) ;
2567
2621
}
2622
+ findComponents ( currentComponent , onlyParents , onlyMatchName ) {
2623
+ const components = [ ] ;
2624
+ this . componentMapByComponent . forEach ( ( componentName , component ) => {
2625
+ if ( onlyParents &&
2626
+ ( currentComponent === component || ! component . element . contains ( currentComponent . element ) ) ) {
2627
+ return ;
2628
+ }
2629
+ if ( onlyMatchName && componentName !== onlyMatchName ) {
2630
+ return ;
2631
+ }
2632
+ components . push ( component ) ;
2633
+ } ) ;
2634
+ return components ;
2635
+ }
2568
2636
}
2569
- var ComponentRegistry$1 = new ComponentRegistry ( ) ;
2570
2637
2571
- const getComponent = ( element ) => ComponentRegistry$1 . getComponent ( element ) ;
2638
+ const getComponent = ( element ) => LiveControllerDefault . componentRegistry . getComponent ( element ) ;
2572
2639
class LiveControllerDefault extends Controller {
2573
2640
constructor ( ) {
2574
2641
super ( ...arguments ) ;
@@ -2582,7 +2649,7 @@ class LiveControllerDefault extends Controller {
2582
2649
initialize ( ) {
2583
2650
this . handleDisconnectedChildControllerEvent = this . handleDisconnectedChildControllerEvent . bind ( this ) ;
2584
2651
const id = this . element . dataset . liveId || null ;
2585
- this . component = new Component ( this . element , this . propsValue , this . fingerprintValue , id , new Backend ( this . urlValue , this . csrfValue ) , new StandardElementDriver ( ) ) ;
2652
+ this . component = new Component ( this . element , this . nameValue , this . propsValue , this . listenersValue , ( currentComponent , onlyParents , onlyMatchName ) => LiveControllerDefault . componentRegistry . findComponents ( currentComponent , onlyParents , onlyMatchName ) , this . fingerprintValue , id , new Backend ( this . urlValue , this . csrfValue ) , new StandardElementDriver ( ) ) ;
2586
2653
this . proxiedComponent = proxifyComponent ( this . component ) ;
2587
2654
this . element . __component = this . proxiedComponent ;
2588
2655
if ( this . hasDebounceValue ) {
@@ -2600,19 +2667,19 @@ class LiveControllerDefault extends Controller {
2600
2667
} ) ;
2601
2668
}
2602
2669
connect ( ) {
2670
+ LiveControllerDefault . componentRegistry . registerComponent ( this . element , this . component ) ;
2603
2671
this . component . connect ( ) ;
2604
2672
this . elementEventListeners . forEach ( ( { event, callback } ) => {
2605
2673
this . component . element . addEventListener ( event , callback ) ;
2606
2674
} ) ;
2607
- ComponentRegistry$1 . registerComponent ( this . element , this . component ) ;
2608
2675
this . dispatchEvent ( 'connect' ) ;
2609
2676
}
2610
2677
disconnect ( ) {
2678
+ LiveControllerDefault . componentRegistry . unregisterComponent ( this . component ) ;
2611
2679
this . component . disconnect ( ) ;
2612
2680
this . elementEventListeners . forEach ( ( { event, callback } ) => {
2613
2681
this . component . element . removeEventListener ( event , callback ) ;
2614
2682
} ) ;
2615
- ComponentRegistry$1 . unregisterComponent ( this . element ) ;
2616
2683
this . dispatchEvent ( 'disconnect' ) ;
2617
2684
}
2618
2685
update ( event ) {
@@ -2659,6 +2726,48 @@ class LiveControllerDefault extends Controller {
2659
2726
$render ( ) {
2660
2727
return this . component . render ( ) ;
2661
2728
}
2729
+ emit ( event ) {
2730
+ this . getEmitDirectives ( event ) . forEach ( ( { name, data, nameMatch } ) => {
2731
+ this . component . emit ( name , data , nameMatch ) ;
2732
+ } ) ;
2733
+ }
2734
+ emitUp ( event ) {
2735
+ this . getEmitDirectives ( event ) . forEach ( ( { name, data, nameMatch } ) => {
2736
+ this . component . emitUp ( name , data , nameMatch ) ;
2737
+ } ) ;
2738
+ }
2739
+ emitSelf ( event ) {
2740
+ this . getEmitDirectives ( event ) . forEach ( ( { name, data } ) => {
2741
+ this . component . emitSelf ( name , data ) ;
2742
+ } ) ;
2743
+ }
2744
+ getEmitDirectives ( event ) {
2745
+ const element = event . currentTarget ;
2746
+ if ( ! element . dataset . event ) {
2747
+ throw new Error ( `No data-event attribute found on element: ${ getElementAsTagText ( element ) } ` ) ;
2748
+ }
2749
+ const eventInfo = element . dataset . event ;
2750
+ const directives = parseDirectives ( eventInfo ) ;
2751
+ const emits = [ ] ;
2752
+ directives . forEach ( ( directive ) => {
2753
+ let nameMatch = null ;
2754
+ directive . modifiers . forEach ( ( modifier ) => {
2755
+ switch ( modifier . name ) {
2756
+ case 'name' :
2757
+ nameMatch = modifier . value ;
2758
+ break ;
2759
+ default :
2760
+ throw new Error ( `Unknown modifier ${ modifier . name } in event "${ eventInfo } ".` ) ;
2761
+ }
2762
+ } ) ;
2763
+ emits . push ( {
2764
+ name : directive . action ,
2765
+ data : directive . named ,
2766
+ nameMatch,
2767
+ } ) ;
2768
+ } ) ;
2769
+ return emits ;
2770
+ }
2662
2771
$updateModel ( model , value , shouldRender = true , debounce = true ) {
2663
2772
return this . component . set ( model , value , shouldRender , debounce ) ;
2664
2773
}
@@ -2739,12 +2848,15 @@ class LiveControllerDefault extends Controller {
2739
2848
}
2740
2849
}
2741
2850
LiveControllerDefault . values = {
2851
+ name : String ,
2742
2852
url : String ,
2743
2853
props : Object ,
2744
2854
csrf : String ,
2855
+ listeners : { type : Array , default : [ ] } ,
2745
2856
debounce : { type : Number , default : 150 } ,
2746
2857
id : String ,
2747
2858
fingerprint : String ,
2748
2859
} ;
2860
+ LiveControllerDefault . componentRegistry = new ComponentRegistry ( ) ;
2749
2861
2750
2862
export { Component , LiveControllerDefault as default , getComponent } ;
0 commit comments