@@ -2,7 +2,7 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core';
22import { Component , Element , Event , Host , Prop , State , Watch , h } from '@stencil/core' ;
33import { renderHiddenInput , inheritAriaAttributes } from '@utils/helpers' ;
44import type { Attributes } from '@utils/helpers' ;
5- import { hapticSelection } from '@utils/native/haptic' ;
5+ import { hapticAvailable , hapticSelection } from '@utils/native/haptic' ;
66import { isRTL } from '@utils/rtl' ;
77import { createColorClasses , hostContext } from '@utils/theme' ;
88import { checkmarkOutline , removeOutline , ellipseOutline } from 'ionicons/icons' ;
@@ -34,6 +34,7 @@ export class Toggle implements ComponentInterface {
3434 private inputId = `ion-tg-${ toggleIds ++ } ` ;
3535 private gesture ?: Gesture ;
3636 private focusEl ?: HTMLElement ;
37+ private hapticEl ?: HTMLElement ;
3738 private lastDrag = 0 ;
3839 private inheritedAttributes : Attributes = { } ;
3940 private toggleTrack ?: HTMLElement ;
@@ -138,6 +139,10 @@ export class Toggle implements ComponentInterface {
138139 const isNowChecked = ! checked ;
139140 this . checked = isNowChecked ;
140141
142+ if ( this . hapticEl ) {
143+ this . hapticEl . click ( ) ;
144+ }
145+
141146 this . ionChange . emit ( {
142147 checked : isNowChecked ,
143148 value,
@@ -285,6 +290,33 @@ export class Toggle implements ComponentInterface {
285290 ) ;
286291 }
287292
293+ /**
294+ * On Safari (iOS 18+) we can trigger haptic feedback programatically
295+ * by rendering <input type="checkbox" switch> element
296+ * with an associated <label> and triggering click()
297+ * on the <label> element.
298+ */
299+ private renderFallbackHapticElements ( ) {
300+ const { inputId } = this ;
301+ const mode = getIonMode ( this ) ;
302+
303+ if ( hapticAvailable ( ) || mode !== 'ios' ) {
304+ return ;
305+ }
306+
307+ return (
308+ < label aria-hidden = "true" ref = { ( hapticEl ) => ( this . hapticEl = hapticEl ) } style = { { display : 'none' } } >
309+ < input
310+ id = { inputId + '-haptic' }
311+ type = "checkbox"
312+ // @ts -expect-error safari-only custom attrrbute required for haptic feedback
313+ switch
314+ style = { { display : 'none' } }
315+ />
316+ </ label >
317+ ) ;
318+ }
319+
288320 private get hasLabel ( ) {
289321 return this . el . textContent !== '' ;
290322 }
@@ -299,7 +331,6 @@ export class Toggle implements ComponentInterface {
299331
300332 return (
301333 < Host
302- onClick = { this . onClick }
303334 class = { createColorClasses ( color , {
304335 [ mode ] : true ,
305336 'in-item' : hostContext ( 'ion-item' , el ) ,
@@ -312,7 +343,8 @@ export class Toggle implements ComponentInterface {
312343 [ `toggle-${ rtl } ` ] : true ,
313344 } ) }
314345 >
315- < label class = "toggle-wrapper" >
346+ { this . renderFallbackHapticElements ( ) }
347+ < label class = "toggle-wrapper" onClick = { this . onClick } >
316348 { /*
317349 The native control must be rendered
318350 before the visible label text due to https://bugs.webkit.org/show_bug.cgi?id=251951
0 commit comments