@@ -3,50 +3,96 @@ import {
3
3
define ,
4
4
hasNativeShadowDomSupport ,
5
5
equalizeRelativeHeights ,
6
- persistentlyAttemptToEqualizeRelativeHeights ,
7
6
} from '@bolt/core/utils' ;
8
7
import { withLitHtml , html } from '@bolt/core' ;
9
8
import classNames from 'classnames/bind' ;
10
9
import {
11
- boltCharacterRootClass ,
12
10
boltCharacterCenterClass ,
13
- boltCharacterConnectionClass ,
11
+ boltCharacterIs ,
14
12
} from '@bolt/micro-journeys/src/character' ;
13
+ import { boltConnectionIs } from '@bolt/micro-journeys/src/connection' ;
14
+ import { triggerAnims } from '@bolt/components-animate/utils' ;
15
15
import styles from './two-character-layout.scss' ;
16
16
17
17
let cx = classNames . bind ( styles ) ;
18
18
19
+ const boltTwoCharacterLayoutIs = 'bolt-two-character-layout' ;
20
+
19
21
@define
20
22
class BoltTwoCharacterLayout extends withLitHtml ( ) {
21
- static is = 'bolt-two-character-layout' ;
23
+ static is = boltTwoCharacterLayoutIs ;
22
24
23
25
static props = {
24
26
noShadow : {
25
27
...props . boolean ,
26
28
...{ default : false } ,
27
29
} ,
30
+ parentAnimationsTriggered : {
31
+ ...props . boolean ,
32
+ ...{ default : false } ,
33
+ } ,
28
34
} ;
29
35
30
36
// @ts -ignore
31
37
constructor ( self ) {
32
38
self = super ( self ) ;
33
39
self . useShadow = hasNativeShadowDomSupport ;
34
- self . addEventListener (
35
- 'bolt-character:connected' ,
36
- this . handleComponentConnect ,
37
- ) ;
38
- self . addEventListener (
39
- 'bolt-connection:connected' ,
40
- this . handleComponentConnect ,
41
- ) ;
42
- self . connectedComponentCount = 0 ;
40
+ self . hasConnection = ! ! this . querySelector ( boltConnectionIs ) ;
41
+ self . requiredComponentCount = self . hasConnection ? 3 : 2 ;
42
+ self . renderedComponentCount = 0 ;
43
+ self . isInitialRender = true ;
43
44
return self ;
44
45
}
45
46
46
- handleComponentConnect ( event ) {
47
- this . connectedComponentCount ++ ;
48
- if ( this . connectedComponentCount >= this . requiredComponentCount ) {
49
- this . componentsHaveConnected ( ) ;
47
+ /**
48
+ * Callback for when child components report ready. Figure out if they're
49
+ * characters and count them if so. Then attempt to initialize.
50
+ *
51
+ * @param event
52
+ */
53
+ handleChildrenReady ( event ) {
54
+ if (
55
+ event . detail . name !== boltCharacterIs &&
56
+ event . detail . name !== boltConnectionIs
57
+ ) {
58
+ return ;
59
+ }
60
+ this . renderedComponentCount ++ ;
61
+ if ( this . isConnected ) {
62
+ this . attemptCharactersAreReadyInitialization ( ) ;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Make sure that the component is rendering for the first time and `bolt-character`s are ready.
68
+ * Both characters have to be on the dom with width and height before this can run.
69
+ * The problem is that this component connects and components report as rendered
70
+ * before content is slotted. Thus, we have to wait for the parent to tell us
71
+ * that it has attempted to animate content as a sigh that we can actually
72
+ * perform calculations against slotted elements that have height/width.
73
+ * We only do this on initial render after we're told the parent has animated.
74
+ * After this, bolt-interactive-step is in charge of animating this in and out.
75
+ */
76
+ attemptCharactersAreReadyInitialization ( ) {
77
+ const charsAreReady =
78
+ this . renderedComponentCount >= this . requiredComponentCount ;
79
+ if (
80
+ this . parentAnimationsTriggered &&
81
+ charsAreReady &&
82
+ this . isInitialRender
83
+ ) {
84
+ this . charactersAreReadyInitialization ( ) ;
85
+ this . isInitialRender = false ;
86
+ }
87
+ }
88
+
89
+ connected ( ) {
90
+ this . addEventListener ( 'ready' , this . handleChildrenReady ) ;
91
+ }
92
+
93
+ disconnecting ( ) {
94
+ if ( this . parentAnimationsTriggered && ! this . isInitialRender ) {
95
+ this . removeEventListener ( 'ready' , this . handleChildrenReady ) ;
50
96
}
51
97
}
52
98
@@ -55,55 +101,60 @@ class BoltTwoCharacterLayout extends withLitHtml() {
55
101
* to one another so a connection can be centered between them, while
56
102
* preserving document flow by avoiding absolute positioning.
57
103
*/
58
- componentsHaveConnected ( ) {
59
- this . boltCharacters = [ ...this . querySelectorAll ( 'bolt-character' ) ] ;
60
- const eqHeightArgs = this . boltCharacters . map ( el => {
104
+ charactersAreReadyInitialization = async ( ) => {
105
+ const anims = this . querySelectorAll ( 'bolt-animate' ) ;
106
+ this . boltCharacters = [ ...this . querySelectorAll ( boltCharacterIs ) ] ;
107
+ const eqHeightArgs = this . boltCharacters . map ( character => {
61
108
return {
62
- container : el ,
63
- elToEqualize : el . renderRoot . querySelector (
109
+ container : character ,
110
+ elToEqualize : character . renderRoot . querySelector (
64
111
`.${ boltCharacterCenterClass } ` ,
65
112
) ,
66
- paddingEqualizationTarget : el . renderRoot . querySelector (
67
- `.${ boltCharacterRootClass } ` ,
68
- ) ,
113
+ paddingEqualizationTarget : character ,
69
114
} ;
70
115
} ) ;
71
- persistentlyAttemptToEqualizeRelativeHeights (
116
+ equalizeRelativeHeights (
72
117
eqHeightArgs ,
73
- this . setConnectionWidth ,
74
- 0 ,
75
- 3 ,
76
- true ,
118
+ this . hasConnection ? this . setConnectionWidth : null ,
77
119
) ;
78
- }
120
+ this . triggerUpdate ( ) ;
121
+
122
+ triggerAnims ( { animEls : anims , stage : 'IN' } ) ;
123
+ // Tell the parent step that it can now normally animate this element.
124
+ this . dispatchEvent (
125
+ new CustomEvent ( `${ BoltTwoCharacterLayout . is } :animation-initialized` , {
126
+ bubbles : true ,
127
+ } ) ,
128
+ ) ;
129
+ } ;
79
130
80
131
/**
81
- * Set the width of `bolt-connection` so it spans from one `bolt-character` to
132
+ * Set the width of `bolt-connection` if present so it spans from one `bolt-character` to
82
133
* `bolt-character`. Note: requires page refresh.
83
134
*/
84
135
setConnectionWidth = ( ) => {
85
136
this . boltCharacters . forEach ( ( e , i ) => {
86
- const connection = e . querySelector ( 'bolt-connection' ) ;
137
+ const connection = e . querySelector ( boltConnectionIs ) ;
87
138
const nextCharacter = this . boltCharacters [ i + 1 ] ;
88
139
if ( connection && nextCharacter ) {
89
140
const nextCharacterCenter = nextCharacter . renderRoot . querySelector (
90
141
`.${ boltCharacterCenterClass } ` ,
91
142
) ;
92
- // @TODO figure out why that 50% calculation is off by 10px (getBoundingClientRect().x is not at fault)
93
143
connection . style . minWidth = `calc(${ nextCharacterCenter . getBoundingClientRect ( )
94
- . x -
95
- connection . getBoundingClientRect ( ) . x -
96
- 10 } px + 50%)`;
144
+ . x - connection . getBoundingClientRect ( ) . x } px + 50%)`;
97
145
}
98
146
} ) ;
99
147
} ;
100
148
101
149
render ( ) {
102
- const classes = cx ( 'c-bolt-two-character-layout' ) ;
103
- this . requiredComponentCount =
104
- // @ts -ignore
105
- ! ! this . slot ( 'character--left' ) + ! ! this . slot ( 'character--right' ) ;
106
-
150
+ const props = this . validateProps ( this . props ) ;
151
+ if ( this . isInitialRender ) {
152
+ this . attemptCharactersAreReadyInitialization ( ) ;
153
+ }
154
+ const classes = cx ( 'c-bolt-two-character-layout' , {
155
+ 'c-bolt-two-character-layout__initial' :
156
+ ! props . parentAnimationsTriggered && this . isInitialRender ,
157
+ } ) ;
107
158
return html `
108
159
${ this . addStyles ( [ styles ] ) }
109
160
< div class ="${ classes } ">
@@ -124,4 +175,4 @@ class BoltTwoCharacterLayout extends withLitHtml() {
124
175
}
125
176
}
126
177
127
- export { BoltTwoCharacterLayout } ;
178
+ export { BoltTwoCharacterLayout , boltTwoCharacterLayoutIs } ;
0 commit comments