Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions core/src/components/refresher-content/refresher-content.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, h } from '@stencil/core';
import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config';
import { isPlatform } from '@utils/platform';
import { sanitizeDOMString } from '@utils/sanitization';
import { arrowDown, caretBackSharp } from 'ionicons/icons';

import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import type { IonicSafeString } from '../../utils/sanitization';
import { supportsRubberBandScrolling } from '../refresher/refresher.utils';
import type { SpinnerTypes } from '../spinner/spinner-configs';
import { SPINNERS } from '../spinner/spinner-configs';

Expand Down Expand Up @@ -63,11 +63,17 @@ export class RefresherContent implements ComponentInterface {

componentWillLoad() {
if (this.pullingIcon === undefined) {
/**
* The native iOS refresher uses a spinner instead of
* an icon, so we need to see if this device supports
* the native iOS refresher.
*/
const hasRubberBandScrolling = supportsRubberBandScrolling();
const mode = getIonMode(this);
const overflowRefresher = (this.el.style as any).webkitOverflowScrolling !== undefined ? 'lines' : arrowDown;
const overflowRefresher = hasRubberBandScrolling ? 'lines' : arrowDown;
this.pullingIcon = config.get(
'refreshingIcon',
mode === 'ios' && isPlatform('mobile') ? config.get('spinner', overflowRefresher) : 'circular'
mode === 'ios' && hasRubberBandScrolling ? config.get('spinner', overflowRefresher) : 'circular'
);
}
if (this.refreshingSpinner === undefined) {
Expand Down
31 changes: 20 additions & 11 deletions core/src/components/refresher/refresher.utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { writeTask } from '@stencil/core';
import { createAnimation } from '@utils/animation/animation';
import { clamp, componentOnReady, transitionEndAsync } from '@utils/helpers';
import { isPlatform } from '@utils/platform';

// MD Native Refresher
// -----------------------------
Expand Down Expand Up @@ -195,6 +194,25 @@ export const translateElement = (el?: HTMLElement, value?: string, duration = 20
// Utils
// -----------------------------

/**
* In order to use the native iOS refresher the device must support rubber band scrolling.
* As part of this, we need to exclude Desktop Safari because it has a slightly different rubber band effect that is not compatible with the native refresher in Ionic.
*
* We also need to be careful not to include devices that spoof their user agent.
* For example, when using iOS emulation in Chrome the user agent will be spoofed such that
* navigator.maxTouchPointer > 0. To work around this,
* we check to see if the apple-pay-logo is supported as a named image which is only
* true on Apple devices.
*
* We previously checked referencEl.style.webkitOverflowScrolling to explicitly check
* for rubber band support. However, this property was removed on iPadOS and it's possible
* that this will be removed on iOS in the future too.
*
*/
export const supportsRubberBandScrolling = () => {
return navigator.maxTouchPoints > 0 && CSS.supports('background: -webkit-named-image(apple-pay-logo-black)');
};

export const shouldUseNativeRefresher = async (referenceEl: HTMLIonRefresherElement, mode: string) => {
const refresherContent = referenceEl.querySelector('ion-refresher-content');
if (!refresherContent) {
Expand All @@ -209,15 +227,6 @@ export const shouldUseNativeRefresher = async (referenceEl: HTMLIonRefresherElem
return (
pullingSpinner !== null &&
refreshingSpinner !== null &&
/**
* We use webkitOverflowScrolling for feature detection with rubber band scrolling
* on iOS. When doing referenceEl.style, webkitOverflowScrolling is undefined on non-iOS platforms.
* However, it will be the empty string on iOS.
* Note that we do not use getPropertyValue (and thus need to cast as any) because calling
* getPropertyValue('-webkit-overflow-scrolling') will return the empty string if it is not
* set on the element, even if the platform does not support that.
*/
((mode === 'ios' && isPlatform('mobile') && (referenceEl.style as any).webkitOverflowScrolling !== undefined) ||
mode === 'md')
((mode === 'ios' && supportsRubberBandScrolling()) || mode === 'md')
);
};