@@ -26,7 +26,8 @@ function RemoteFunctions(config = {}) {
2626 const SHARED_STATE = {
2727 __description : "Use this to keep shared state for Live Preview Edit instead of window.*" ,
2828 _suppressDOMEditDismissal : false ,
29- _suppressDOMEditDismissalTimeout : null
29+ _suppressDOMEditDismissalTimeout : null ,
30+ _boxModelHighlightHidden : false
3031 } ;
3132
3233 let _hoverHighlight ;
@@ -190,7 +191,10 @@ function RemoteFunctions(config = {}) {
190191 disableHoverListeners : disableHoverListeners ,
191192 enableHoverListeners : enableHoverListeners ,
192193 redrawHighlights : redrawHighlights ,
193- redrawEverything : redrawEverything
194+ redrawEverything : redrawEverything ,
195+ getTreePath : _getTreePath ,
196+ getElementByTreePath : _getElementByTreePath ,
197+ getSourceChildren : _instrumentedChildren
194198 } ;
195199
196200 /**
@@ -446,15 +450,17 @@ function RemoteFunctions(config = {}) {
446450 s . backgroundColor = color ;
447451 }
448452
449- // Padding region
450- const padColor = COLORS . highlightPadding ;
453+ // Padding region. Rects stay in place when hidden, only their fill goes away,
454+ // so nothing has to be rebuilt when they come back.
455+ const boxModelHidden = SHARED_STATE . _boxModelHighlightHidden ;
456+ const padColor = boxModelHidden ? "transparent" : COLORS . highlightPadding ;
451457 setRect ( refs . padTop , paddingBox . left , paddingBox . top , paddingBox . width , pt , padColor ) ;
452458 setRect ( refs . padBottom , paddingBox . left , contentBox . top + contentBox . height , paddingBox . width , pb , padColor ) ;
453459 setRect ( refs . padLeft , paddingBox . left , contentBox . top , pl , contentBox . height , padColor ) ;
454460 setRect ( refs . padRight , contentBox . left + contentBox . width , contentBox . top , pr , contentBox . height , padColor ) ;
455461
456462 // Margin region
457- const margColor = COLORS . highlightMargin ;
463+ const margColor = boxModelHidden ? "transparent" : COLORS . highlightMargin ;
458464 setRect ( refs . marTop , marginBox . left , marginBox . top , marginBox . width , mt , margColor ) ;
459465 setRect ( refs . marBottom , marginBox . left , borderBox . top + borderBox . height , marginBox . width , mb , margColor ) ;
460466 setRect ( refs . marLeft , marginBox . left , borderBox . top , ml , borderBox . height , margColor ) ;
@@ -1054,6 +1060,25 @@ function RemoteFunctions(config = {}) {
10541060 return results && results [ 0 ] ;
10551061 } ;
10561062
1063+ // True for elements Phoenix adds to the page itself, like the tool boxes and
1064+ // the highlight overlays. They are not part of the user's source file.
1065+ function _isPhoenixInternalNode ( node ) {
1066+ return ! ! node && node . nodeType === Node . ELEMENT_NODE &&
1067+ ( node . hasAttribute ( GLOBALS . PHCODE_INTERNAL_ATTR ) ||
1068+ node . className === GLOBALS . HIGHLIGHT_CLASSNAME ) ;
1069+ }
1070+
1071+ /** The first of the Phoenix elements sitting at the end of `parent`, else null. */
1072+ function _firstTrailingInternalNode ( parent ) {
1073+ let node = parent . lastChild ;
1074+ let first = null ;
1075+ while ( _isPhoenixInternalNode ( node ) ) {
1076+ first = node ;
1077+ node = node . previousSibling ;
1078+ }
1079+ return first ;
1080+ }
1081+
10571082 /**
10581083 * @private
10591084 * Insert a new child element
@@ -1068,7 +1093,12 @@ function RemoteFunctions(config = {}) {
10681093 if ( edit . firstChild ) {
10691094 before = targetElement . firstChild ;
10701095 } else if ( edit . lastChild ) {
1071- after = targetElement . lastChild ;
1096+ // Phoenix's tool boxes are the last children of <body>, so appending
1097+ // here would put the new element after them, in the wrong place.
1098+ before = _firstTrailingInternalNode ( targetElement ) ;
1099+ if ( ! before ) {
1100+ after = targetElement . lastChild ;
1101+ }
10721102 }
10731103
10741104 if ( before ) {
@@ -1479,6 +1509,9 @@ function RemoteFunctions(config = {}) {
14791509 _pendingHoverRAF = null ;
14801510 }
14811511
1512+ // the selection is gone, so a popover can no longer turn the fills back on
1513+ SHARED_STATE . _boxModelHighlightHidden = false ;
1514+
14821515 // Highlight.clear() removes all overlay divs (outline + margin/padding rects)
14831516 hideHighlight ( ) ;
14841517
@@ -1492,27 +1525,38 @@ function RemoteFunctions(config = {}) {
14921525 }
14931526 }
14941527
1528+ // Only the children that came from the source file. Phoenix's own elements have
1529+ // no data-brackets-id, and counting them would shift the tree path indexes.
1530+ function _instrumentedChildren ( parent ) {
1531+ const result = [ ] ;
1532+ const children = ( parent && parent . children ) || [ ] ;
1533+ for ( let i = 0 ; i < children . length ; i ++ ) {
1534+ if ( children [ i ] . hasAttribute ( GLOBALS . DATA_BRACKETS_ID_ATTR ) ) {
1535+ result . push ( children [ i ] ) ;
1536+ }
1537+ }
1538+ return result ;
1539+ }
1540+
14951541 /**
14961542 * Compute the tree path of an element as an array of child indices
14971543 * from <html> down. Used to re-locate the element after re-instrumentation
14981544 * when data-brackets-id changes and text matching is ambiguous.
14991545 * E.g. [1, 0, 0, 1] means html > 2nd child > 1st child > 1st child > 2nd child.
1546+ * @return {?Array.<number> } null if the element did not come from the source file.
15001547 */
15011548 function _getTreePath ( element ) {
15021549 const path = [ ] ;
15031550 let el = element ;
15041551 while ( el && el . parentElement ) {
1505- const parent = el . parentElement ;
1506- const children = parent . children ;
1507- for ( let i = 0 ; i < children . length ; i ++ ) {
1508- if ( children [ i ] === el ) {
1509- path . unshift ( i ) ;
1510- break ;
1511- }
1552+ const index = _instrumentedChildren ( el . parentElement ) . indexOf ( el ) ;
1553+ if ( index === - 1 ) {
1554+ return null ;
15121555 }
1513- el = parent ;
1556+ path . unshift ( index ) ;
1557+ el = el . parentElement ;
15141558 }
1515- return path ;
1559+ return path . length ? path : null ;
15161560 }
15171561
15181562 /**
@@ -1521,10 +1565,11 @@ function RemoteFunctions(config = {}) {
15211565 function _getElementByTreePath ( path ) {
15221566 let el = document . documentElement ;
15231567 for ( let i = 0 ; i < path . length ; i ++ ) {
1524- if ( ! el || ! el . children || ! el . children [ path [ i ] ] ) {
1568+ const siblings = _instrumentedChildren ( el ) ;
1569+ if ( ! siblings [ path [ i ] ] ) {
15251570 return null ;
15261571 }
1527- el = el . children [ path [ i ] ] ;
1572+ el = siblings [ path [ i ] ] ;
15281573 }
15291574 return el ;
15301575 }
@@ -1727,6 +1772,21 @@ function RemoteFunctions(config = {}) {
17271772 }
17281773 }
17291774
1775+ /**
1776+ * Hide just the margin/padding fills of the selected element highlight, keeping
1777+ * the outline and the selection itself. Used while editing paint properties like
1778+ * background color, where the fills sit on top of what the user is changing.
1779+ * @param {Boolean } hidden
1780+ */
1781+ function setBoxModelHighlightHidden ( hidden ) {
1782+ hidden = ! ! hidden ;
1783+ if ( SHARED_STATE . _boxModelHighlightHidden === hidden ) {
1784+ return ;
1785+ }
1786+ SHARED_STATE . _boxModelHighlightHidden = hidden ;
1787+ redrawHighlights ( ) ;
1788+ }
1789+
17301790 let customReturns = { } ;
17311791 // only apis that needs to be called from phoenix js layer should be customReturns. APis that are shared within
17321792 // the remote function context only should not be in customReturns and should be in
@@ -1750,6 +1810,7 @@ function RemoteFunctions(config = {}) {
17501810 "getHighlightTrackingElement" : getHighlightTrackingElement ,
17511811 "getHighlightStyle" : getHighlightStyle ,
17521812 "setHotCornerHidden" : setHotCornerHidden ,
1813+ "setBoxModelHighlightHidden" : setBoxModelHighlightHidden ,
17531814 "clearHoverState" : _clearHoverState
17541815 } ;
17551816
0 commit comments