feat(haptics): add haptic feedback to nav items and back buttons (#196)
## Summary - Installs `web-haptics` via bun - Triggers `selection` haptic feedback on all navigation items (mobile bottom bar and desktop sidebar) - Triggers `light` haptic feedback on all back buttons (`connect-account-inline.tsx`, `categorize.tsx`)
This commit is contained in:
parent
09d81ac7e7
commit
3d742677b5
3
bun.lock
3
bun.lock
|
|
@ -56,6 +56,7 @@
|
|||
"uuidv7": "^1.1.0",
|
||||
"vaul": "^1.1.2",
|
||||
"vite": "^7.3.1",
|
||||
"web-haptics": "^0.0.6",
|
||||
"xlsx": "^0.18.5",
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -1758,6 +1759,8 @@
|
|||
|
||||
"walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="],
|
||||
|
||||
"web-haptics": ["web-haptics@0.0.6", "", { "peerDependencies": { "react": ">=18", "react-dom": ">=18", "svelte": ">=4", "vue": ">=3" }, "optionalPeers": ["react", "react-dom", "svelte", "vue"] }, "sha512-eCzcf1LDi20+Fr0x9V3OkX92k0gxEQXaHajmhXHitsnk6SxPeshv8TBtBRqxyst8HI1uf2FyFVE7QS3jo1gkrw=="],
|
||||
|
||||
"web-vitals": ["web-vitals@5.1.0", "", {}, "sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg=="],
|
||||
|
||||
"webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="],
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -94,6 +94,7 @@
|
|||
"uuidv7": "^1.1.0",
|
||||
"vaul": "^1.1.2",
|
||||
"vite": "^7.3.1",
|
||||
"web-haptics": "^0.0.6",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { SharedData } from '@/types';
|
|||
import { Link, usePage } from '@inertiajs/react';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { useWebHaptics } from 'web-haptics/react';
|
||||
import AppLogo from './app-logo';
|
||||
|
||||
export function AppSidebar() {
|
||||
|
|
@ -28,6 +29,7 @@ export function AppSidebar() {
|
|||
() => getMainNavItems(page.props.features, page.props.locale),
|
||||
[page.props.features, page.props.locale],
|
||||
);
|
||||
const { trigger } = useWebHaptics();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -40,6 +42,7 @@ export function AppSidebar() {
|
|||
<Link
|
||||
key={item.title}
|
||||
href={item.href}
|
||||
onClick={() => trigger('selection')}
|
||||
className={cn([
|
||||
'flex flex-1 flex-col items-center justify-center gap-1 rounded-full px-3 py-2 transition-all duration-200',
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ import { resolveUrl } from '@/lib/utils';
|
|||
import { type NavItem } from '@/types';
|
||||
import { __ } from '@/utils/i18n';
|
||||
import { Link, usePage } from '@inertiajs/react';
|
||||
import { useWebHaptics } from 'web-haptics/react';
|
||||
|
||||
export function NavMain({ items = [] }: { items: NavItem[] }) {
|
||||
const page = usePage();
|
||||
const { trigger } = useWebHaptics();
|
||||
return (
|
||||
<SidebarGroup className="px-2 py-0">
|
||||
<SidebarGroupLabel>{__('Platform')}</SidebarGroupLabel>
|
||||
|
|
@ -25,7 +27,11 @@ export function NavMain({ items = [] }: { items: NavItem[] }) {
|
|||
)}
|
||||
tooltip={{ children: __(item.title) }}
|
||||
>
|
||||
<Link href={item.href} prefetch>
|
||||
<Link
|
||||
href={item.href}
|
||||
prefetch
|
||||
onClick={() => trigger('selection')}
|
||||
>
|
||||
{item.icon && <item.icon />}
|
||||
<span>{__(item.title)}</span>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import type { EnableBankingInstitution } from '@/types/banking';
|
|||
import { __ } from '@/utils/i18n';
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useWebHaptics } from 'web-haptics/react';
|
||||
|
||||
const COUNTRIES = [
|
||||
{ code: 'ES', name: 'Spain' },
|
||||
|
|
@ -73,6 +74,7 @@ interface ConnectAccountInlineProps {
|
|||
|
||||
export function ConnectAccountInline({ onBack }: ConnectAccountInlineProps) {
|
||||
const [step, setStep] = useState<Step>('country');
|
||||
const { trigger } = useWebHaptics();
|
||||
const [country, setCountry] = useState<string>('');
|
||||
const [institutions, setInstitutions] = useState<
|
||||
EnableBankingInstitution[]
|
||||
|
|
@ -272,7 +274,10 @@ export function ConnectAccountInline({ onBack }: ConnectAccountInlineProps) {
|
|||
<Button
|
||||
variant={'ghost'}
|
||||
type="button"
|
||||
onClick={handleBack}
|
||||
onClick={() => {
|
||||
trigger('light');
|
||||
handleBack();
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
|
|
@ -329,7 +334,10 @@ export function ConnectAccountInline({ onBack }: ConnectAccountInlineProps) {
|
|||
<Button
|
||||
variant={'ghost'}
|
||||
type="button"
|
||||
onClick={handleBack}
|
||||
onClick={() => {
|
||||
trigger('light');
|
||||
handleBack();
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import {
|
|||
SkipForward,
|
||||
} from 'lucide-react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useWebHaptics } from 'web-haptics/react';
|
||||
|
||||
const CATEGORY_USAGE_KEY = 'category-usage-order';
|
||||
|
||||
|
|
@ -107,6 +108,7 @@ export default function CategorizeTransactions({
|
|||
automationRules: AutomationRule[];
|
||||
}>().props;
|
||||
const locale = useLocale();
|
||||
const { trigger } = useWebHaptics();
|
||||
|
||||
const [uncategorizedTransactions, setUncategorizedTransactions] = useState<
|
||||
DecryptedTransaction[]
|
||||
|
|
@ -506,6 +508,7 @@ export default function CategorizeTransactions({
|
|||
href={categorizeRoute
|
||||
.url()
|
||||
?.replace('/categorize', '')}
|
||||
onClick={() => trigger('light')}
|
||||
>
|
||||
<Button size="lg" className="mt-4">
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
|
|
@ -541,6 +544,7 @@ export default function CategorizeTransactions({
|
|||
href={categorizeRoute
|
||||
.url()
|
||||
?.replace('/categorize', '')}
|
||||
onClick={() => trigger('light')}
|
||||
>
|
||||
<Button>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
|
|
@ -561,6 +565,7 @@ export default function CategorizeTransactions({
|
|||
<header className="flex items-center justify-between gap-6 px-4 py-3 dark:border-zinc-800">
|
||||
<Link
|
||||
href={categorizeRoute.url()?.replace('/categorize', '')}
|
||||
onClick={() => trigger('light')}
|
||||
className="flex w-fit flex-1 items-center gap-2 text-sm text-zinc-600 opacity-50 transition-all duration-200 hover:text-zinc-900 hover:opacity-100 dark:text-zinc-400 dark:hover:text-zinc-100"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue