@ionic/core
Version:
Base components for Ionic
71 lines (70 loc) • 2.83 kB
JavaScript
/*!
* (C) Ionic http://ionicframework.com - MIT License
*/
import { raf } from "../helpers";
/**
* The content slotted into a form control's start or end slot that a click
* started on, or `null` when the click did not start on slotted content.
*
* The slotted element is compared against the host in case the form control
* itself is slotted into, for example, an item. Without that check a control
* carrying slot="start"/"end" would treat every click on itself as a slotted
* click.
*/
export const getSlottedClickContent = (ev, el) => {
const slotted = ev.target.closest('[slot="start"], [slot="end"]');
return slotted !== null && slotted !== el && el.contains(slotted) ? slotted : null;
};
/**
* Whether a click started on content slotted into a form control's start or
* end slot.
*/
const isSlottedClick = (ev, el) => getSlottedClickContent(ev, el) !== null;
/**
* A utility for form components that wrap their content in a <label>, such as
* ion-input, ion-textarea and ion-select.
*
* Clicking slotted content also clicks that label, and the browser follows it
* with a click on the label's control. That second click would be emitted from
* the host as a duplicate, so the slotted click is remembered and the click
* that follows it is suppressed.
*
* Browsers skip the forwarding when the click lands on interactive content,
* such as a slotted button, so the slotted click is remembered for a frame
* rather than until a forwarded click that may never arrive.
*
* @internal
* @param el - The host element.
* @param getNativeInput - A callback returning the native form control the
* label points at, for components that have one. Those components emit the
* click from the host rather than the control, so the control's click is
* always stopped and re-dispatched from the host. Omit it for components whose
* label has no `for` attribute, such as ion-select, where the browser forwards
* to an internal control that already re-bubbles targeting the host.
*/
export const createClickController = (el, getNativeInput) => {
let hasSlottedClick = false;
const handleClickCapture = (ev) => {
if (isSlottedClick(ev, el)) {
hasSlottedClick = true;
raf(() => (hasSlottedClick = false));
return;
}
if (getNativeInput === undefined) {
if (hasSlottedClick) {
ev.stopPropagation();
hasSlottedClick = false;
}
return;
}
const nativeInput = getNativeInput();
if (nativeInput !== undefined && ev.target === nativeInput) {
ev.stopPropagation();
if (!hasSlottedClick) {
el.click();
}
hasSlottedClick = false;
}
};
return { handleClickCapture };
};