-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new DOM feature HandleLinkPressFeature
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
Showing
2 changed files
with
157 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |