@@ -137,6 +137,11 @@ export type Props = {
137137 'view-transition-name' ?: string ,
138138 viewTransitionClass ?: string ,
139139 'view-transition-class' ?: string ,
140+ margin ?: string ,
141+ marginTop ?: string ,
142+ 'margin-top' ?: string ,
143+ marginBottom ?: string ,
144+ 'margin-bottom' ?: string ,
140145 ...
141146 } ,
142147 bottom ?: null | number ,
@@ -1161,6 +1166,59 @@ export function unhideTextInstance(
11611166 textInstance . nodeValue = text ;
11621167}
11631168
1169+ function warnForBlockInsideInline ( instance : HTMLElement ) {
1170+ if ( __DEV__ ) {
1171+ let nextNode = instance . firstChild ;
1172+ outer: while ( nextNode != null ) {
1173+ let node : Node = nextNode ;
1174+ if (
1175+ node . nodeType === ELEMENT_NODE &&
1176+ getComputedStyle ( ( node : any ) ) . display === 'block'
1177+ ) {
1178+ console . error (
1179+ "You're about to start a <ViewTransition> around a display: inline " +
1180+ 'element <%s>, which itself has a display: block element <%s> inside it. ' +
1181+ 'This might trigger a bug in Safari which causes the View Transition to ' +
1182+ 'be skipped with a duplicate name error.\n' +
1183+ 'https://bugs.webkit.org/show_bug.cgi?id=290923' ,
1184+ instance . tagName . toLocaleLowerCase ( ) ,
1185+ ( node : any ) . tagName . toLocaleLowerCase ( ) ,
1186+ ) ;
1187+ break ;
1188+ }
1189+ if ( node . firstChild != null ) {
1190+ nextNode = node . firstChild ;
1191+ continue ;
1192+ }
1193+ if ( node === instance ) {
1194+ break ;
1195+ }
1196+ while ( node . nextSibling == null ) {
1197+ if ( node . parentNode == null || node . parentNode === instance ) {
1198+ break ;
1199+ }
1200+ node = node . parentNode ;
1201+ }
1202+ nextNode = node . nextSibling ;
1203+ }
1204+ }
1205+ }
1206+
1207+ function countClientRects ( rects : Array < ClientRect > ) : number {
1208+ if ( rects . length === 1 ) {
1209+ return 1 ;
1210+ }
1211+ // Count non-zero rects.
1212+ let count = 0 ;
1213+ for ( let i = 0 ; i < rects . length ; i ++ ) {
1214+ const rect = rects [ i ] ;
1215+ if ( rect . width > 0 && rect . height > 0 ) {
1216+ count ++ ;
1217+ }
1218+ }
1219+ return count ;
1220+ }
1221+
11641222export function applyViewTransitionName (
11651223 instance : Instance ,
11661224 name : string ,
@@ -1173,13 +1231,42 @@ export function applyViewTransitionName(
11731231 // $FlowFixMe[prop-missing]
11741232 instance . style . viewTransitionClass = className ;
11751233 }
1234+ const computedStyle = getComputedStyle ( instance ) ;
1235+ if ( computedStyle . display === 'inline' ) {
1236+ // WebKit has a bug where assigning a name to display: inline elements errors
1237+ // if they have display: block children. We try to work around this bug in the
1238+ // simple case by converting it automatically to display: inline-block.
1239+ // https://bugs.webkit.org/show_bug.cgi?id=290923
1240+ const rects = instance . getClientRects ( ) ;
1241+ if ( countClientRects ( rects ) === 1 ) {
1242+ // If the instance has a single client rect, that means that it can be
1243+ // expressed as a display: inline-block or block.
1244+ // This will cause layout thrash but we live with it since inline view transitions
1245+ // are unusual.
1246+ const style = instance . style ;
1247+ // If there's literally only one rect, then it's likely on a single line like an
1248+ // inline-block. If it's multiple rects but all but one of them are empty it's
1249+ // likely because it's a single block that caused a line break.
1250+ style . display = rects . length === 1 ? 'inline-block' : 'block' ;
1251+ // Margin doesn't apply to inline so should be zero. However, padding top/bottom
1252+ // applies to inline-block positioning which we can offset by setting the margin
1253+ // to the negative padding to get it back into original position.
1254+ style . marginTop = '-' + computedStyle . paddingTop ;
1255+ style . marginBottom = '-' + computedStyle . paddingBottom ;
1256+ } else {
1257+ // This case cannot be easily fixed if it has blocks but it's also fine if
1258+ // it doesn't have blocks. So we only warn in DEV about this being an issue.
1259+ warnForBlockInsideInline ( instance ) ;
1260+ }
1261+ }
11761262}
11771263
11781264export function restoreViewTransitionName (
11791265 instance : Instance ,
11801266 props : Props ,
11811267) : void {
11821268 instance = ( ( instance : any ) : HTMLElement ) ;
1269+ const style = instance . style ;
11831270 const styleProp = props [ STYLE ] ;
11841271 const viewTransitionName =
11851272 styleProp != null
@@ -1190,7 +1277,7 @@ export function restoreViewTransitionName(
11901277 : null
11911278 : null ;
11921279 // $FlowFixMe[prop-missing]
1193- instance . style . viewTransitionName =
1280+ style . viewTransitionName =
11941281 viewTransitionName == null || typeof viewTransitionName === 'boolean'
11951282 ? ''
11961283 : // The value would've errored already if it wasn't safe.
@@ -1205,12 +1292,39 @@ export function restoreViewTransitionName(
12051292 : null
12061293 : null ;
12071294 // $FlowFixMe[prop-missing]
1208- instance . style . viewTransitionClass =
1295+ style . viewTransitionClass =
12091296 viewTransitionClass == null || typeof viewTransitionClass === 'boolean'
12101297 ? ''
12111298 : // The value would've errored already if it wasn't safe.
12121299 // eslint-disable-next-line react-internal/safe-string-coercion
12131300 ( '' + viewTransitionClass ) . trim ( ) ;
1301+ if ( style . display === 'inline-block' ) {
1302+ // We might have overridden the style. Reset it to what it should be.
1303+ if ( styleProp == null ) {
1304+ style . display = style . margin = '' ;
1305+ } else {
1306+ const display = styleProp . display ;
1307+ style . display =
1308+ display == null || typeof display === 'boolean' ? '' : display ;
1309+ const margin = styleProp . margin ;
1310+ if ( margin != null ) {
1311+ style . margin = margin ;
1312+ } else {
1313+ const marginTop = styleProp . hasOwnProperty ( 'marginTop' )
1314+ ? styleProp . marginTop
1315+ : styleProp [ 'margin-top' ] ;
1316+ style . marginTop =
1317+ marginTop == null || typeof marginTop === 'boolean' ? '' : marginTop ;
1318+ const marginBottom = styleProp . hasOwnProperty ( 'marginBottom' )
1319+ ? styleProp . marginBottom
1320+ : styleProp [ 'margin-bottom' ] ;
1321+ style . marginBottom =
1322+ marginBottom == null || typeof marginBottom === 'boolean'
1323+ ? ''
1324+ : marginBottom ;
1325+ }
1326+ }
1327+ }
12141328}
12151329
12161330export function cancelViewTransitionName (
0 commit comments