165 lines
4.6 KiB
TypeScript
165 lines
4.6 KiB
TypeScript
'use client';
|
|
import React, { createContext, useContext, useState } from 'react';
|
|
import type { ReactNode } from 'react';
|
|
import { Button } from '@/components/ui';
|
|
import { type ComponentProps } from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
type TVModeContextProps = {
|
|
tvMode: boolean;
|
|
toggleTVMode: () => void;
|
|
};
|
|
|
|
type TVToggleProps = {
|
|
buttonClassName?: ComponentProps<typeof Button>['className'];
|
|
buttonProps?: Omit<ComponentProps<typeof Button>, 'className'>;
|
|
size?: number;
|
|
};
|
|
|
|
const TVModeContext = createContext<TVModeContextProps | undefined>(undefined);
|
|
|
|
const TVModeProvider = ({ children }: { children: ReactNode }) => {
|
|
const [tvMode, setTVMode] = useState(false);
|
|
const toggleTVMode = () => {
|
|
setTVMode((prev) => !prev);
|
|
};
|
|
return (
|
|
<TVModeContext.Provider value={{ tvMode, toggleTVMode }}>
|
|
{children}
|
|
</TVModeContext.Provider>
|
|
);
|
|
};
|
|
|
|
const useTVMode = () => {
|
|
const context = useContext(TVModeContext);
|
|
if (!context) {
|
|
throw new Error('useTVMode must be used within a TVModeProvider');
|
|
}
|
|
return context;
|
|
};
|
|
|
|
// TV Icon Component with animations
|
|
const TVIcon = ({ tvMode, size = 25 }: { tvMode: boolean; size?: number }) => {
|
|
return (
|
|
<div
|
|
className='relative transition-all duration-300 ease-in-out'
|
|
style={{ width: size, height: size }}
|
|
>
|
|
<svg
|
|
width={size}
|
|
height={size}
|
|
viewBox='0 0 24 24'
|
|
fill='none'
|
|
className='transition-all duration-300 ease-in-out'
|
|
>
|
|
{/* TV Screen */}
|
|
<rect
|
|
x='3'
|
|
y='6'
|
|
width='18'
|
|
height='12'
|
|
rx='2'
|
|
className={cn(
|
|
'stroke-current stroke-2 fill-none transition-all duration-300',
|
|
tvMode ? 'stroke-blue-500 animate-pulse' : 'stroke-current',
|
|
)}
|
|
/>
|
|
|
|
{/* TV Stand */}
|
|
<path
|
|
d='M8 18h8M12 18v2'
|
|
className='stroke-current stroke-2 transition-all duration-300'
|
|
/>
|
|
|
|
{/* Corner arrows - animate based on mode */}
|
|
<g
|
|
className={cn(
|
|
'transition-all duration-300 ease-in-out origin-center',
|
|
tvMode ? 'scale-75 opacity-100' : 'scale-100 opacity-70',
|
|
)}
|
|
>
|
|
{tvMode ? (
|
|
// Exit fullscreen arrows (pointing inward)
|
|
<>
|
|
<path
|
|
d='M6 8l2 2M6 8h2M6 8v2'
|
|
className='stroke-current stroke-1.5 transition-all duration-300'
|
|
/>
|
|
<path
|
|
d='M18 8l-2 2M18 8h-2M18 8v2'
|
|
className='stroke-current stroke-1.5 transition-all duration-300'
|
|
/>
|
|
<path
|
|
d='M6 16l2-2M6 16h2M6 16v-2'
|
|
className='stroke-current stroke-1.5 transition-all duration-300'
|
|
/>
|
|
<path
|
|
d='M18 16l-2-2M18 16h-2M18 16v-2'
|
|
className='stroke-current stroke-1.5 transition-all duration-300'
|
|
/>
|
|
</>
|
|
) : (
|
|
// Enter fullscreen arrows (pointing outward)
|
|
<>
|
|
<path
|
|
d='M8 6l-2 2M8 6v2M8 6h-2'
|
|
className='stroke-current stroke-1.5 transition-all duration-300'
|
|
/>
|
|
<path
|
|
d='M16 6l2 2M16 6v2M16 6h2'
|
|
className='stroke-current stroke-1.5 transition-all duration-300'
|
|
/>
|
|
<path
|
|
d='M8 18l-2-2M8 18v-2M8 18h-2'
|
|
className='stroke-current stroke-1.5 transition-all duration-300'
|
|
/>
|
|
<path
|
|
d='M16 18l2-2M16 18v-2M16 18h2'
|
|
className='stroke-current stroke-1.5 transition-all duration-300'
|
|
/>
|
|
</>
|
|
)}
|
|
</g>
|
|
|
|
{/* Optional: Screen content indicator */}
|
|
<circle
|
|
cx='12'
|
|
cy='12'
|
|
r='1'
|
|
className={cn(
|
|
'transition-all duration-300',
|
|
tvMode ? 'fill-blue-400 animate-ping' : 'fill-current opacity-30',
|
|
)}
|
|
/>
|
|
</svg>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const TVToggle = ({
|
|
buttonClassName,
|
|
buttonProps = {
|
|
variant: 'outline',
|
|
size: 'default',
|
|
},
|
|
size = 25,
|
|
}: TVToggleProps) => {
|
|
const { tvMode, toggleTVMode } = useTVMode();
|
|
|
|
return (
|
|
<Button
|
|
onClick={toggleTVMode}
|
|
className={cn(
|
|
'my-auto cursor-pointer transition-all duration-200 hover:scale-105 active:scale-95',
|
|
buttonClassName,
|
|
)}
|
|
aria-label={tvMode ? 'Exit TV Mode' : 'Enter TV Mode'}
|
|
{...buttonProps}
|
|
>
|
|
<TVIcon tvMode={tvMode} size={size} />
|
|
</Button>
|
|
);
|
|
};
|
|
|
|
export { TVModeProvider, useTVMode, TVToggle };
|