Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix committed Mar 24, 2020
1 parent c6df2f4 commit 62fc2c1
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,13 @@ const BlockComponent = forwardRef(
* When a block becomes selected, transition focus to an inner tabbable.
*/
const focusTabbable = () => {
const { ownerDocument } = wrapper.current;

// Focus is captured by the wrapper node, so while focus transition
// should only consider tabbables within editable display, since it
// may be the wrapper itself or a side control which triggered the
// focus event, don't unnecessary transition to an inner tabbable.
if ( wrapper.current.contains( document.activeElement ) ) {
if ( wrapper.current.contains( ownerDocument.activeElement ) ) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,21 @@ export default function useMultiSelection( ref ) {
* select the entire block contents.
*/
useEffect( () => {
const { ownerDocument } = ref.current;
const { defaultView } = ownerDocument;

if ( ! hasMultiSelection || isMultiSelecting ) {
if ( ! selectedBlockClientId || isMultiSelecting ) {
return;
}

const selection = window.getSelection();
const selection = defaultView.getSelection();

if ( selection.rangeCount && ! selection.isCollapsed ) {
const blockNode = getBlockDOMNode( selectedBlockClientId );
const blockNode = getBlockDOMNode(
selectedBlockClientId,
ownerDocument
);
const { startContainer, endContainer } = selection.getRangeAt(
0
);
Expand All @@ -125,11 +131,11 @@ export default function useMultiSelection( ref ) {
const start = multiSelectedBlockClientIds[ 0 ];
const end = multiSelectedBlockClientIds[ length - 1 ];

let startNode = getBlockDOMNode( start );
let endNode = getBlockDOMNode( end );
let startNode = getBlockDOMNode( start, ownerDocument );
let endNode = getBlockDOMNode( end, ownerDocument );

const selection = window.getSelection();
const range = document.createRange();
const selection = defaultView.getSelection();
const range = ownerDocument.createRange();

// The most stable way to select the whole block contents is to start
// and end at the deepest points.
Expand All @@ -151,7 +157,9 @@ export default function useMultiSelection( ref ) {

const onSelectionChange = useCallback(
( { isSelectionEnd } ) => {
const selection = window.getSelection();
const { ownerDocument } = ref.current;
const { defaultView } = ownerDocument;
const selection = defaultView.getSelection();

// If no selection is found, end multi selection and enable all rich
// text areas.
Expand Down Expand Up @@ -206,12 +214,17 @@ export default function useMultiSelection( ref ) {
* Handles a mouseup event to end the current mouse multi-selection.
*/
const onSelectionEnd = useCallback( () => {
document.removeEventListener( 'selectionchange', onSelectionChange );
const { ownerDocument } = ref.current;
const { defaultView } = ownerDocument;
ownerDocument.removeEventListener(
'selectionchange',
onSelectionChange
);
// Equivalent to attaching the listener once.
window.removeEventListener( 'mouseup', onSelectionEnd );
defaultView.removeEventListener( 'mouseup', onSelectionEnd );
// The browser selection won't have updated yet at this point, so wait
// until the next animation frame to get the browser selection.
rafId.current = window.requestAnimationFrame( () => {
rafId.current = defaultView.requestAnimationFrame( () => {
onSelectionChange( { isSelectionEnd: true } );
stopMultiSelect();
} );
Expand All @@ -220,12 +233,14 @@ export default function useMultiSelection( ref ) {
// Only clean up when unmounting, these are added and cleaned up elsewhere.
useEffect(
() => () => {
document.removeEventListener(
const { ownerDocument } = ref.current;
const { defaultView } = ownerDocument;
ownerDocument.removeEventListener(
'selectionchange',
onSelectionChange
);
window.removeEventListener( 'mouseup', onSelectionEnd );
window.cancelAnimationFrame( rafId.current );
defaultView.removeEventListener( 'mouseup', onSelectionEnd );
defaultView.cancelAnimationFrame( rafId.current );
},
[ onSelectionChange, onSelectionEnd ]
);
Expand All @@ -240,15 +255,21 @@ export default function useMultiSelection( ref ) {
return;
}

const { ownerDocument } = ref.current;
const { defaultView } = ownerDocument;

startClientId.current = clientId;
anchorElement.current = document.activeElement;
anchorElement.current = ownerDocument.activeElement;
startMultiSelect();

// `onSelectionStart` is called after `mousedown` and `mouseleave`
// (from a block). The selection ends when `mouseup` happens anywhere
// in the window.
document.addEventListener( 'selectionchange', onSelectionChange );
window.addEventListener( 'mouseup', onSelectionEnd );
ownerDocument.addEventListener(
'selectionchange',
onSelectionChange
);
defaultView.addEventListener( 'mouseup', onSelectionEnd );

// Removing the contenteditable attributes within the block editor is
// essential for selection to work across editable areas. The edible
Expand Down
4 changes: 2 additions & 2 deletions packages/block-editor/src/utils/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
*
* @return {Element} Block DOM node.
*/
export function getBlockDOMNode( clientId ) {
return document.getElementById( 'block-' + clientId );
export function getBlockDOMNode( clientId, scope = document ) {
return scope.getElementById( 'block-' + clientId );
}

export function getBlockPreviewContainerDOMNode( clientId ) {
Expand Down
34 changes: 31 additions & 3 deletions packages/components/src/popover/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,30 @@ function computeAnchorRect(
return;
}

if ( anchorRef instanceof window.Range ) {
if ( anchorRef.endContainer ) {
return getRectangleFromRange( anchorRef );
}

if ( anchorRef instanceof window.Element ) {
const rect = anchorRef.getBoundingClientRect();
const { ownerDocument } = anchorRef;

if ( ownerDocument ) {
let rect = anchorRef.getBoundingClientRect();

if ( ownerDocument !== document ) {
const iframe = Array.from(
document.querySelectorAll( 'iframe' )
).find( ( element ) => {
return element.contentDocument === ownerDocument;
} );
const iframeRect = iframe.getBoundingClientRect();

rect = new window.DOMRect(
rect.left + iframeRect.left,
rect.top + iframeRect.top,
rect.width,
rect.height
);
}

if ( shouldAnchorIncludePadding ) {
return rect;
Expand Down Expand Up @@ -446,6 +464,16 @@ const Popover = ( {
window.addEventListener( 'resize', refresh );
window.addEventListener( 'scroll', refresh, true );

Array.from( document.querySelectorAll( 'iframe' ) ).forEach(
( element ) => {
element.contentWindow.addEventListener(
'scroll',
refresh,
true
);
}
);

let observer;

const observeElement =
Expand Down
32 changes: 22 additions & 10 deletions packages/dom/src/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ function isEdge( container, isReverse, onlyVertical ) {
return true;
}

const selection = window.getSelection();
const { ownerDocument } = container;
const { defaultView } = ownerDocument;

const selection = defaultView.getSelection();

if ( ! selection.rangeCount ) {
return false;
Expand All @@ -104,7 +107,7 @@ function isEdge( container, isReverse, onlyVertical ) {
return false;
}

const computedStyle = window.getComputedStyle( container );
const computedStyle = defaultView.getComputedStyle( container );
const lineHeight = parseInt( computedStyle.lineHeight, 10 ) || 0;

// Only consider the multiline selection at the edge if the direction is
Expand Down Expand Up @@ -208,6 +211,7 @@ export function getRectangleFromRange( range ) {
}

const { startContainer } = range;
const { ownerDocument } = startContainer;

// Correct invalid "BR" ranges. The cannot contain any children.
if ( startContainer.nodeName === 'BR' ) {
Expand All @@ -216,7 +220,7 @@ export function getRectangleFromRange( range ) {
startContainer
);

range = document.createRange();
range = ownerDocument.createRange();
range.setStart( parentNode, index );
range.setEnd( parentNode, index );
}
Expand All @@ -229,7 +233,7 @@ export function getRectangleFromRange( range ) {
//
// See: https://stackoverflow.com/a/6847328/995445
if ( ! rect ) {
const padNode = document.createTextNode( '\u200b' );
const padNode = ownerDocument.createTextNode( '\u200b' );
// Do not modify the live range.
range = range.cloneRange();
range.insertNode( padNode );
Expand Down Expand Up @@ -296,8 +300,10 @@ export function placeCaretAtHorizontalEdge( container, isReverse ) {
return;
}

const selection = window.getSelection();
const range = document.createRange();
const { ownerDocument } = container;
const { defaultView } = ownerDocument;
const selection = defaultView.getSelection();
const range = ownerDocument.createRange();

range.selectNodeContents( rangeTarget );
range.collapse( ! isReverse );
Expand Down Expand Up @@ -407,7 +413,9 @@ export function placeCaretAtVerticalEdge(
? editableRect.bottom - buffer
: editableRect.top + buffer;

const range = hiddenCaretRangeFromPoint( document, x, y, container );
const { ownerDocument } = container;
const { defaultView } = ownerDocument;
const range = hiddenCaretRangeFromPoint( ownerDocument, x, y, container );

if ( ! range || ! container.contains( range.startContainer ) ) {
if (
Expand All @@ -427,7 +435,7 @@ export function placeCaretAtVerticalEdge(
return;
}

const selection = window.getSelection();
const selection = defaultView.getSelection();
selection.removeAllRanges();
selection.addRange( range );
container.focus();
Expand Down Expand Up @@ -506,7 +514,9 @@ export function isEntirelySelected( element ) {
return true;
}

const selection = window.getSelection();
const { ownerDocument } = element;
const { defaultView } = ownerDocument;
const selection = defaultView.getSelection();
const range = selection.rangeCount ? selection.getRangeAt( 0 ) : null;

if ( ! range ) {
Expand Down Expand Up @@ -552,8 +562,10 @@ export function getScrollContainer( node ) {

// Scrollable if scrollable height exceeds displayed...
if ( node.scrollHeight > node.clientHeight ) {
const { ownerDocument } = node;
const { defaultView } = ownerDocument;
// ...except when overflow is defined to be hidden or visible
const { overflowY } = window.getComputedStyle( node );
const { overflowY } = defaultView.getComputedStyle( node );
if ( /(auto|scroll)/.test( overflowY ) ) {
return node;
}
Expand Down
Loading

0 comments on commit 62fc2c1

Please sign in to comment.