ios-haptics 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  javascript library for haptic feedback inside of safari on ios
4
4
 
5
+ please note this only works on ios 17.4 to 26.4, as apple patched it in ios 26.5
6
+
5
7
  ![GitHub last commit](https://img.shields.io/github/last-commit/tijnjh/ios-haptics)
6
8
  [![npm version](https://img.shields.io/npm/v/ios-haptics.svg)](https://npmjs.com/package/ios-haptics)
7
9
  ![NPM Downloads](https://img.shields.io/npm/dm/ios-haptics)
@@ -18,25 +20,38 @@ npm i ios-haptics
18
20
  ## 🚀 usage
19
21
 
20
22
  ```javascript
21
- import { haptic } from 'ios-haptics'
23
+ import { hapticTrigger } from 'ios-haptics'
24
+ ```
25
+
22
26
 
23
- // a single haptic
24
- haptic()
27
+ vanilla
28
+ ```javascript
29
+ const element = document.getElementById('element')
25
30
 
26
- // two rapid haptics
27
- haptic.confirm()
31
+ hapticTrigger(element)
32
+ ```
28
33
 
29
- // three rapid haptics
30
- haptic.error()
34
+ svelte
35
+ ```html
36
+ <button {@attach hapticTrigger}>click me</button>
31
37
  ```
32
38
 
39
+ react
40
+ ```html
41
+ <button ref={hapticTrigger}>click me</button>
42
+ ```
43
+
44
+ vue
45
+ ```html
46
+ <button :ref="hapticTrigger">click me</button>
47
+ ```
48
+
49
+
33
50
  ## ⚙️ how it works
34
51
 
35
52
  this uses the `<input type="checkbox" switch />` (introduced in safari 17.4), which has haptic feedback when toggled
36
53
 
37
- every `haptic` call, it will create one of those in the background, toggle it, then remove it
38
-
39
- on devices that support it, `navigator.vibrate()` is called instead, so it works on android too
54
+ `hapticTrigger` renders one of those on top of your element, so when the user taps it, safari triggers the native haptic feedback
40
55
 
41
56
  ---
42
57
 
package/dist/index.d.ts CHANGED
@@ -1,46 +1,4 @@
1
- declare const supportsHaptics: boolean;
2
- /** INTERNAL */
3
- declare function _triggerAndroidHaptic(): void;
4
- interface haptic {
5
- /** @deprecated */
6
- apply: never;
7
- /** @deprecated */
8
- arguments: never;
9
- /** @deprecated */
10
- bind: never;
11
- /** @deprecated */
12
- call: never;
13
- /** @deprecated */
14
- caller: never;
15
- /** @deprecated */
16
- length: never;
17
- /** @deprecated */
18
- name: never;
19
- /** @deprecated */
20
- prototype: never;
21
- /** @deprecated */
22
- toString: never;
23
- /** @deprecated */
24
- Symbol: never;
25
- /** a single haptic */
26
- (): void;
27
- /** two rapid haptics */
28
- confirm: () => void;
29
- /** three rapid haptics */
30
- error: () => void;
31
- }
32
- /**
33
- * @deprecated no longer works in newer ios versions, use the component instead
34
- *
35
- * @example
36
- * ```tsx
37
- * import { HapticTrigger } from 'ios-haptics/(react|svelte|vue)'
38
- *
39
- * <HapticTrigger>
40
- * click me
41
- * </HapticTrigger>
42
- *
43
- */
44
- declare const __haptic: haptic;
45
-
46
- export { _triggerAndroidHaptic, __haptic as haptic, supportsHaptics };
1
+ //#region src/index.d.ts
2
+ declare function hapticTrigger(element: HTMLElement | undefined | null): void;
3
+ //#endregion
4
+ export { hapticTrigger };
package/dist/index.js CHANGED
@@ -1 +1,6 @@
1
- 'use strict';var i=typeof window>"u"||typeof window.matchMedia!="function"?false:window.matchMedia("(pointer: coarse)").matches;function n(){try{if(!i)return;navigator.vibrate&&navigator.vibrate(50);}catch{}}function e(){try{if(navigator.vibrate){navigator.vibrate(50);return}if(!i)return;let t=document.createElement("label");t.ariaHidden="true",t.style.display="none";let r=document.createElement("input");r.type="checkbox",r.setAttribute("switch",""),t.appendChild(r),document.head.appendChild(t),t.click(),document.head.removeChild(t);}catch{}}e.confirm=()=>{if(navigator.vibrate){navigator.vibrate([50,70,50]);return}e(),setTimeout(e,120);};e.error=()=>{if(navigator.vibrate){navigator.vibrate([50,70,50,70,50]);return}e(),setTimeout(e,120),setTimeout(e,240);};var a=e;exports._triggerAndroidHaptic=n;exports.haptic=a;exports.supportsHaptics=i;
1
+ /**
2
+ * ios-haptics v3.0.0
3
+ * tijn.dev
4
+ * @license MIT
5
+ **/
6
+ function e(e){if(!e)return;let t=document.createElement(`input`);t.type=`checkbox`,t.setAttribute(`switch`,``),Object.assign(t.style,{position:`absolute`,top:0,left:0,width:`100%`,height:`100%`,margin:0,opacity:0,cursor:`pointer`,clipPath:`inset(0 round 999px)`,touchAction:`manipulation`}),t.style.setProperty(`-webkit-tap-highlight-color`,`transparent`),e.style.position=`relative`,e.insertAdjacentElement(`beforeend`,t)}export{e as hapticTrigger};
package/package.json CHANGED
@@ -1,7 +1,9 @@
1
1
  {
2
2
  "name": "ios-haptics",
3
- "version": "2.0.1",
3
+ "type": "module",
4
+ "version": "3.0.0",
4
5
  "description": "haptic feedback for ios safari",
6
+ "author": "tijnjh",
5
7
  "license": "MIT",
6
8
  "repository": {
7
9
  "type": "git",
@@ -17,86 +19,24 @@
17
19
  "safari",
18
20
  "vibrate"
19
21
  ],
20
- "sideEffects": false,
21
22
  "exports": {
22
23
  ".": {
23
- "types": {
24
- "import": "./dist/index.d.mts",
25
- "require": "./dist/index.d.ts"
26
- },
27
- "import": "./dist/index.mjs",
28
- "require": "./dist/index.js"
29
- },
30
- "./react": {
31
- "types": {
32
- "import": "./dist/react/index.d.mts",
33
- "require": "./dist/react/index.d.ts"
34
- },
35
- "import": "./dist/react/index.mjs",
36
- "require": "./dist/react/index.js"
37
- },
38
- "./vue": {
39
- "types": {
40
- "import": "./dist/vue/index.d.mts",
41
- "require": "./dist/vue/index.d.ts"
42
- },
43
- "import": "./dist/vue/index.mjs",
44
- "require": "./dist/vue/index.js"
45
- },
46
- "./svelte": {
47
- "types": {
48
- "import": "./dist/svelte/index.d.mts",
49
- "require": "./dist/svelte/index.d.ts"
50
- },
51
- "svelte": "./dist/svelte/index.mjs",
52
- "import": "./dist/svelte/index.mjs",
53
- "require": "./dist/svelte/index.js"
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js"
54
26
  }
55
27
  },
56
- "main": "dist/index.js",
57
- "module": "dist/index.mjs",
58
- "types": "dist/index.d.ts",
28
+ "main": "./dist/index.js",
29
+ "module": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
59
31
  "files": [
60
- "dist/*"
32
+ "dist"
61
33
  ],
62
34
  "scripts": {
63
- "build": "tsup && node scripts/build-svelte.mjs",
64
- "dev": "tsup --watch"
65
- },
66
- "peerDependencies": {
67
- "react": ">=18",
68
- "react-dom": ">=18",
69
- "svelte": ">=5",
70
- "vue": ">=3"
71
- },
72
- "peerDependenciesMeta": {
73
- "react": {
74
- "optional": true
75
- },
76
- "react-dom": {
77
- "optional": true
78
- },
79
- "svelte": {
80
- "optional": true
81
- },
82
- "vue": {
83
- "optional": true
84
- }
35
+ "build": "tsdown"
85
36
  },
86
37
  "devDependencies": {
87
38
  "@antfu/eslint-config": "^9.0.0",
88
- "@eslint-react/eslint-plugin": "^5.6.0",
89
- "@types/react": "^19.2.17",
90
- "eslint": "^10.3.0",
91
- "eslint-plugin-format": "^2.0.1",
92
- "eslint-plugin-react-refresh": "^0.5.2",
93
- "eslint-plugin-svelte": "^3.17.1",
94
- "react": "^19.2.7",
95
- "react-dom": "^19.2.7",
96
- "svelte": "^5.56.3",
97
- "svelte-eslint-parser": "^1.6.1",
98
- "tsup": "^8.5.1",
99
- "typescript": "5",
100
- "vue": "^3.5.38"
39
+ "eslint": "^10.5.0",
40
+ "tsdown": "latest"
101
41
  }
102
42
  }
package/dist/index.d.mts DELETED
@@ -1,46 +0,0 @@
1
- declare const supportsHaptics: boolean;
2
- /** INTERNAL */
3
- declare function _triggerAndroidHaptic(): void;
4
- interface haptic {
5
- /** @deprecated */
6
- apply: never;
7
- /** @deprecated */
8
- arguments: never;
9
- /** @deprecated */
10
- bind: never;
11
- /** @deprecated */
12
- call: never;
13
- /** @deprecated */
14
- caller: never;
15
- /** @deprecated */
16
- length: never;
17
- /** @deprecated */
18
- name: never;
19
- /** @deprecated */
20
- prototype: never;
21
- /** @deprecated */
22
- toString: never;
23
- /** @deprecated */
24
- Symbol: never;
25
- /** a single haptic */
26
- (): void;
27
- /** two rapid haptics */
28
- confirm: () => void;
29
- /** three rapid haptics */
30
- error: () => void;
31
- }
32
- /**
33
- * @deprecated no longer works in newer ios versions, use the component instead
34
- *
35
- * @example
36
- * ```tsx
37
- * import { HapticTrigger } from 'ios-haptics/(react|svelte|vue)'
38
- *
39
- * <HapticTrigger>
40
- * click me
41
- * </HapticTrigger>
42
- *
43
- */
44
- declare const __haptic: haptic;
45
-
46
- export { _triggerAndroidHaptic, __haptic as haptic, supportsHaptics };
package/dist/index.mjs DELETED
@@ -1 +0,0 @@
1
- var i=typeof window>"u"||typeof window.matchMedia!="function"?false:window.matchMedia("(pointer: coarse)").matches;function n(){try{if(!i)return;navigator.vibrate&&navigator.vibrate(50);}catch{}}function e(){try{if(navigator.vibrate){navigator.vibrate(50);return}if(!i)return;let t=document.createElement("label");t.ariaHidden="true",t.style.display="none";let r=document.createElement("input");r.type="checkbox",r.setAttribute("switch",""),t.appendChild(r),document.head.appendChild(t),t.click(),document.head.removeChild(t);}catch{}}e.confirm=()=>{if(navigator.vibrate){navigator.vibrate([50,70,50]);return}e(),setTimeout(e,120);};e.error=()=>{if(navigator.vibrate){navigator.vibrate([50,70,50,70,50]);return}e(),setTimeout(e,120),setTimeout(e,240);};var a=e;export{n as _triggerAndroidHaptic,a as haptic,i as supportsHaptics};
@@ -1,11 +0,0 @@
1
- import * as react from 'react';
2
- import { ElementType, ComponentPropsWithoutRef } from 'react';
3
-
4
- type HapticTriggerProps<T extends ElementType> = {
5
- as?: T;
6
- onTap?: () => void;
7
- } & ComponentPropsWithoutRef<T>;
8
-
9
- declare function HapticTrigger<T extends ElementType>({ as, onTap, children, ...props }: HapticTriggerProps<T>): react.JSX.Element;
10
-
11
- export { HapticTrigger };
@@ -1,11 +0,0 @@
1
- import * as react from 'react';
2
- import { ElementType, ComponentPropsWithoutRef } from 'react';
3
-
4
- type HapticTriggerProps<T extends ElementType> = {
5
- as?: T;
6
- onTap?: () => void;
7
- } & ComponentPropsWithoutRef<T>;
8
-
9
- declare function HapticTrigger<T extends ElementType>({ as, onTap, children, ...props }: HapticTriggerProps<T>): react.JSX.Element;
10
-
11
- export { HapticTrigger };
@@ -1 +0,0 @@
1
- 'use strict';function t({as:e,onTap:i,children:r,...o}){return React.createElement(e??"button",{...o,style:{position:"relative",display:"inline-flex"}},r,React.createElement("input",{style:{position:"absolute",top:0,left:0,width:"100%",height:"100%",margin:0,opacity:0,cursor:"pointer",clipPath:"inset(0 round 999px)",WebkitTapHighlightColor:"transparent",touchAction:"manipulation"},type:"checkbox",onChange:i,switch:""}))}exports.HapticTrigger=t;
@@ -1 +0,0 @@
1
- function t({as:e,onTap:i,children:r,...o}){return React.createElement(e??"button",{...o,style:{position:"relative",display:"inline-flex"}},r,React.createElement("input",{style:{position:"absolute",top:0,left:0,width:"100%",height:"100%",margin:0,opacity:0,cursor:"pointer",clipPath:"inset(0 round 999px)",WebkitTapHighlightColor:"transparent",touchAction:"manipulation"},type:"checkbox",onChange:i,switch:""}))}export{t as HapticTrigger};
@@ -1,40 +0,0 @@
1
- <script>
2
- /** @type {import("./types.ts").HapticTriggerProps<any>} */
3
- const {
4
- as,
5
- ontap,
6
- children,
7
- ...props
8
- } = $props()
9
- </script>
10
-
11
- <svelte:element
12
- this={as ?? 'button'}
13
- {...props}
14
- style='
15
- position: relative;
16
- display: inline-flex;
17
- '
18
- >
19
- {@render children?.()}
20
-
21
- <input
22
- style='
23
- position: absolute;
24
- top: 0;
25
- left: 0;
26
- width: 100%;
27
- height: 100%;
28
- margin: 0;
29
- opacity: 0;
30
- cursor: pointer;
31
- clip-path: inset(0 round 999px);
32
- -webkit-tap-highlight-color: transparent;
33
- touch-action: manipulation;
34
- '
35
- type='checkbox'
36
- onchange={ontap}
37
- // @ts-expect-error - unrecognized attribute
38
- switch
39
- />
40
- </svelte:element>
@@ -1,10 +0,0 @@
1
- import { Component } from 'svelte';
2
- import { SvelteHTMLElements } from 'svelte/elements';
3
-
4
- type HapticTriggerProps<T extends keyof SvelteHTMLElements> = {
5
- as?: T;
6
- ontap?: () => void;
7
- } & SvelteHTMLElements[T];
8
- declare const HapticTrigger: Component<HapticTriggerProps<any>>;
9
-
10
- export { HapticTrigger, type HapticTriggerProps };
@@ -1,10 +0,0 @@
1
- import { Component } from 'svelte';
2
- import { SvelteHTMLElements } from 'svelte/elements';
3
-
4
- type HapticTriggerProps<T extends keyof SvelteHTMLElements> = {
5
- as?: T;
6
- ontap?: () => void;
7
- } & SvelteHTMLElements[T];
8
- declare const HapticTrigger: Component<HapticTriggerProps<any>>;
9
-
10
- export { HapticTrigger, type HapticTriggerProps };
@@ -1 +0,0 @@
1
- export { default as HapticTrigger } from "./HapticTrigger.svelte";
@@ -1 +0,0 @@
1
- export { default as HapticTrigger } from "./HapticTrigger.svelte";
@@ -1,13 +0,0 @@
1
- import * as vue from 'vue';
2
-
3
- declare const _default: vue.DefineComponent<vue.ExtractPropTypes<{
4
- as: StringConstructor;
5
- }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
6
- [key: string]: any;
7
- }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, "tap"[], "tap", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
8
- as: StringConstructor;
9
- }>> & Readonly<{
10
- onTap?: ((...args: any[]) => any) | undefined;
11
- }>, {}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
12
-
13
- export { _default as HapticTrigger };
@@ -1,13 +0,0 @@
1
- import * as vue from 'vue';
2
-
3
- declare const _default: vue.DefineComponent<vue.ExtractPropTypes<{
4
- as: StringConstructor;
5
- }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
6
- [key: string]: any;
7
- }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, "tap"[], "tap", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
8
- as: StringConstructor;
9
- }>> & Readonly<{
10
- onTap?: ((...args: any[]) => any) | undefined;
11
- }>, {}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
12
-
13
- export { _default as HapticTrigger };
package/dist/vue/index.js DELETED
@@ -1 +0,0 @@
1
- 'use strict';var vue=require('vue');var n=vue.defineComponent({name:"HapticTrigger",props:{as:String},emits:["tap"],setup(i,{emit:e,slots:r,attrs:o}){return ()=>{let p=i.as??"button";return vue.h(p,{...o,style:{position:"relative",display:"inline-flex"}},[r.default?.(),vue.h("input",{type:"checkbox",switch:"",style:{position:"absolute",top:0,left:0,width:"100%",height:"100%",margin:0,opacity:0,cursor:"pointer",clipPath:"inset(0 round 999px)",WebkitTapHighlightColor:"transparent",touchAction:"manipulation"},onChange:e("tap")})])}}});exports.HapticTrigger=n;
@@ -1 +0,0 @@
1
- import {defineComponent,h}from'vue';var n=defineComponent({name:"HapticTrigger",props:{as:String},emits:["tap"],setup(i,{emit:e,slots:r,attrs:o}){return ()=>{let p=i.as??"button";return h(p,{...o,style:{position:"relative",display:"inline-flex"}},[r.default?.(),h("input",{type:"checkbox",switch:"",style:{position:"absolute",top:0,left:0,width:"100%",height:"100%",margin:0,opacity:0,cursor:"pointer",clipPath:"inset(0 round 999px)",WebkitTapHighlightColor:"transparent",touchAction:"manipulation"},onChange:e("tap")})])}}});export{n as HapticTrigger};