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 622362a commit 1b7f2dd
Show file tree
Hide file tree
Showing 12 changed files with 236 additions and 118 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 1b7f2dd

Please sign in to comment.