Skip to content

Commit

Permalink
feat: new DOM feature HandleLinkPressFeature
Browse files Browse the repository at this point in the history
BREAKING CHANGE: handleLinkPressFeature has been replaced with
HandleLinkPressFeature, its instanciable counterpart. Also, the shape of
the sent message has changed, and is now an object with a variety of
useful information. Finally, this implementation uses the element
instance `href` field instead of the `href` attribute, which could contain
relative paths. It is thus more accurate.
  • Loading branch information
jsamr committed Sep 25, 2020
1 parent 9b7c47a commit 6da2358
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 0 deletions.
97 changes: 97 additions & 0 deletions packages/core/src/features/HandleLinkPressFeature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import linkPressScript from './HandleLinkPressFeature.webjs';
import { FeatureBuilder } from '../FeatureBuilder';
import type { DOMRect, PropDefinition } from '../types';
import type { FeatureConstructor } from '../Feature';

/**
* An object describing customization for the linkPress feature.
*
* @public
*/
export interface LinkPressOptions {
/**
* Prevent click events on anchors to propagate.
*
* @defaultValue true
*/
preventDefault?: boolean;

/**
* Don't trigger an event when the target `href` is inside the page, e.g.
* `#top`. See also {@link HandleHashChangeFeature}.
*
* @defaultValue true
*/
ignoreHashChange?: boolean;
}

/**
* The target of a link press event.
*
* @public
*/
export interface LinkPressTarget {
/**
* The full URI of the target.
*/
uri: string;
/**
* The URI scheme.
*/
scheme: string;
/**
* The exact content of the `href` attribute.
*/
hrefAttribute: string;
/**
* The bounding rectangle of the anchor which has been clicked.
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect | Element.getBoundingClientRect()}
*/
clickedAnchorBoundingRect: DOMRect;
/**
* An object describing the page location from which the click originated.
*/
page: {
/**
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/Location/origin}.
*
* @remarks
* Has the special value `null` when not bound to a URL (`{ html }` source).
*/
origin: string | null;
/**
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/Location/href}.
*
* @remarks
* Has the special value `about:blank` when not bound to a URL (`{ html }` source).
*/
href: string;
};
}

const defaultOptions: LinkPressOptions = {
preventDefault: true,
ignoreHashChange: true
};

/**
* This feature allows to intercept clicks on anchors (`<a>`). By default, it
* will prevent the click from propagating. But you can disable this option.
*
* @public
*/
export const HandleLinkPressFeature: FeatureConstructor<
LinkPressOptions,
[
PropDefinition<{
onDOMLinkPress?: (t: LinkPressTarget) => void;
}>
]
> = new FeatureBuilder({
script: linkPressScript,
defaultOptions,
className: 'HandleLinkPressFeature',
featureIdentifier: 'org.formidable-webview/webshell.link-press'
})
.withEventHandlerProp<LinkPressTarget, 'onDOMLinkPress'>('onDOMLinkPress')
.build();
60 changes: 60 additions & 0 deletions packages/core/src/features/HandleLinkPressFeature.webjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
function HandleLinkPressFeature(context) {
var postMessage = context.postMessage;
var options = context.options || {};
var preventDefault = options.preventDefault !== false;
var ignoreHashChange =
typeof options.ignoreHashChange === 'boolean'
? options.ignoreHashChange
: true;

function findParentByTagName(tagname, el) {
while (el) {
if ((el.nodeName || el.tagName).toLowerCase() === tagname.toLowerCase()) {
return el;
}
el = el.parentNode;
}
return null;
}

function extractScheme(uri) {
var groups = uri.match(/(\w+):\/\//);
return (groups && groups.length > 1 && groups[1]) || '';
}

var interceptClickEvent = context.makeCallbackSafe(function (e) {
var target = e.target || e.srcElement;
var anchor = findParentByTagName('a', target);
if (anchor) {
var href = anchor.href;
if (
ignoreHashChange &&
anchor.origin === window.location.origin &&
anchor.pathname === window.location.pathname
) {
return;
}
var rect = anchor.getBoundingClientRect();
var clickedAnchorBoundingRect = {
top: rect.top,
left: rect.left,
bottom: rect.bottom,
right: rect.right,
width: rect.width,
height: rect.height
};
preventDefault && e.preventDefault();
postMessage({
uri: href,
scheme: extractScheme(href),
hrefAttribute: anchor.getAttribute('href'),
clickedAnchorBoundingRect: clickedAnchorBoundingRect,
page: {
href: window.location.href,
origin: window.location.origin
}
});
}
});
document.addEventListener('click', interceptClickEvent, false);
}

0 comments on commit 6da2358

Please sign in to comment.