website refactor
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { ContainerProvider } from '@/lib/di/providers/ContainerProvider';
|
||||
import { QueryClientProvider } from '@/lib/providers/QueryClientProvider';
|
||||
import { AuthProvider } from '@/lib/auth/AuthContext';
|
||||
import { FeatureFlagProvider } from '@/lib/feature/FeatureFlagProvider';
|
||||
import NotificationProvider from '@/components/notifications/NotificationProvider';
|
||||
import { NotificationIntegration } from '@/components/errors/NotificationIntegration';
|
||||
import { EnhancedErrorBoundary } from '@/components/errors/EnhancedErrorBoundary';
|
||||
import DevToolbar from '@/components/dev/DevToolbar';
|
||||
import React from 'react';
|
||||
|
||||
interface AppWrapperProps {
|
||||
children: React.ReactNode;
|
||||
enabledFlags: string[];
|
||||
}
|
||||
|
||||
export function AppWrapper({ children, enabledFlags }: AppWrapperProps) {
|
||||
return (
|
||||
<ContainerProvider>
|
||||
<QueryClientProvider>
|
||||
<AuthProvider>
|
||||
<FeatureFlagProvider flags={enabledFlags}>
|
||||
<NotificationProvider>
|
||||
<NotificationIntegration />
|
||||
<EnhancedErrorBoundary enableDevOverlay={process.env.NODE_ENV === 'development'}>
|
||||
{children}
|
||||
{process.env.NODE_ENV === 'development' && <DevToolbar />}
|
||||
</EnhancedErrorBoundary>
|
||||
</NotificationProvider>
|
||||
</FeatureFlagProvider>
|
||||
</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
</ContainerProvider>
|
||||
);
|
||||
}
|
||||
@@ -45,9 +45,9 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
maxWidth,
|
||||
...props
|
||||
}: BoxProps<T> & ComponentPropsWithoutRef<T>,
|
||||
ref: ForwardedRef<any>
|
||||
ref: ForwardedRef<HTMLElement>
|
||||
) => {
|
||||
const Tag = (as as any) || 'div';
|
||||
const Tag = (as as ElementType) || 'div';
|
||||
|
||||
const spacingMap: Record<string | number, string> = {
|
||||
0: '0', 0.5: '0.5', 1: '1', 1.5: '1.5', 2: '2', 2.5: '2.5', 3: '3', 3.5: '3.5', 4: '4',
|
||||
@@ -81,10 +81,10 @@ export const Box = forwardRef(<T extends ElementType = 'div'>(
|
||||
className
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
const style = maxWidth ? { maxWidth, ...((props as any).style || {}) } : (props as any).style;
|
||||
const style = maxWidth ? { maxWidth, ...((props as Record<string, unknown>).style as object || {}) } : (props as Record<string, unknown>).style;
|
||||
|
||||
return (
|
||||
<Tag ref={ref} className={classes} {...props} style={style}>
|
||||
<Tag ref={ref as React.ForwardedRef<HTMLElement>} className={classes} {...props} style={style as React.CSSProperties}>
|
||||
{children}
|
||||
</Tag>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
// ISO 3166-1 alpha-2 country code to full country name mapping
|
||||
const countryNames: Record<string, string> = {
|
||||
@@ -78,8 +78,6 @@ export function CountryFlag({
|
||||
className = '',
|
||||
showTooltip = true
|
||||
}: CountryFlagProps) {
|
||||
const [showTooltipState, setShowTooltipState] = useState(false);
|
||||
|
||||
const sizeClasses = {
|
||||
sm: 'text-xs',
|
||||
md: 'text-sm',
|
||||
@@ -92,17 +90,9 @@ export function CountryFlag({
|
||||
return (
|
||||
<span
|
||||
className={`inline-flex items-center relative ${sizeClasses[size]} ${className}`}
|
||||
onMouseEnter={() => setShowTooltipState(true)}
|
||||
onMouseLeave={() => setShowTooltipState(false)}
|
||||
title={showTooltip ? countryName : undefined}
|
||||
>
|
||||
<span className="select-none">{flag}</span>
|
||||
{showTooltip && showTooltipState && (
|
||||
<span className="absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 text-xs font-medium text-white bg-deep-graphite border border-charcoal-outline rounded shadow-lg whitespace-nowrap z-50">
|
||||
{countryName}
|
||||
<span className="absolute top-full left-1/2 -translate-x-1/2 -mt-px border-4 border-transparent border-t-charcoal-outline"></span>
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import React, {
|
||||
useEffect,
|
||||
useRef,
|
||||
type ReactNode,
|
||||
type KeyboardEvent as ReactKeyboardEvent,
|
||||
} from 'react';
|
||||
@@ -34,26 +32,6 @@ export function Modal({
|
||||
onOpenChange,
|
||||
isOpen,
|
||||
}: ModalProps) {
|
||||
const dialogRef = useRef<HTMLDivElement | null>(null);
|
||||
const previouslyFocusedElementRef = useRef<Element | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
previouslyFocusedElementRef.current = document.activeElement;
|
||||
const focusable = getFirstFocusable(dialogRef.current);
|
||||
if (focusable) {
|
||||
focusable.focus();
|
||||
} else if (dialogRef.current) {
|
||||
dialogRef.current.focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isOpen && previouslyFocusedElementRef.current instanceof HTMLElement) {
|
||||
previouslyFocusedElementRef.current.focus();
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const handleKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key === 'Escape') {
|
||||
if (onOpenChange) {
|
||||
@@ -61,26 +39,6 @@ export function Modal({
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === 'Tab') {
|
||||
const focusable = getFocusableElements(dialogRef.current);
|
||||
if (focusable.length === 0) return;
|
||||
|
||||
const first = focusable[0];
|
||||
const last = focusable[focusable.length - 1] ?? first;
|
||||
|
||||
if (!first || !last) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!event.shiftKey && document.activeElement === last) {
|
||||
event.preventDefault();
|
||||
first.focus();
|
||||
} else if (event.shiftKey && document.activeElement === first) {
|
||||
event.preventDefault();
|
||||
last.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleBackdropClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
@@ -104,7 +62,6 @@ export function Modal({
|
||||
onClick={handleBackdropClick}
|
||||
>
|
||||
<Box
|
||||
ref={dialogRef}
|
||||
style={{ width: '100%', maxWidth: '28rem', borderRadius: '1rem', backgroundColor: '#0f1115', border: '1px solid #262626', boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.5)', outline: 'none' }}
|
||||
tabIndex={-1}
|
||||
>
|
||||
@@ -162,24 +119,3 @@ export function Modal({
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function getFocusableElements(root: HTMLElement | null): HTMLElement[] {
|
||||
if (!root) return [];
|
||||
const selectors = [
|
||||
'a[href]',
|
||||
'button:not([disabled])',
|
||||
'textarea:not([disabled])',
|
||||
'input:not([disabled])',
|
||||
'select:not([disabled])',
|
||||
'[tabindex]:not([tabindex="-1"])',
|
||||
];
|
||||
const nodes = Array.from(
|
||||
root.querySelectorAll<HTMLElement>(selectors.join(',')),
|
||||
);
|
||||
return nodes.filter((el) => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden'));
|
||||
}
|
||||
|
||||
function getFirstFocusable(root: HTMLElement | null): HTMLElement | null {
|
||||
const elements = getFocusableElements(root);
|
||||
return elements[0] ?? null;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { ChangeEvent } from 'react';
|
||||
import React, { ChangeEvent, SelectHTMLAttributes } from 'react';
|
||||
|
||||
interface SelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
||||
interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
||||
id?: string;
|
||||
'aria-label'?: string;
|
||||
value?: string;
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { motion, useReducedMotion } from 'framer-motion';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Box } from './Box';
|
||||
import { Text } from './Text';
|
||||
|
||||
@@ -20,8 +17,6 @@ export function Toggle({
|
||||
description,
|
||||
disabled = false,
|
||||
}: ToggleProps) {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
return (
|
||||
<label className={`flex items-start justify-between cursor-pointer py-3 border-b border-charcoal-outline/50 last:border-b-0 ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`}>
|
||||
<Box style={{ flex: 1, paddingRight: '1rem' }}>
|
||||
@@ -48,7 +43,7 @@ export function Toggle({
|
||||
className="absolute inset-0 rounded-full bg-primary-blue"
|
||||
initial={{ boxShadow: '0 0 0px rgba(25, 140, 255, 0)' }}
|
||||
animate={{ boxShadow: '0 0 12px rgba(25, 140, 255, 0.4)' }}
|
||||
transition={{ duration: shouldReduceMotion ? 0 : 0.2 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -65,7 +60,6 @@ export function Toggle({
|
||||
type: 'spring',
|
||||
stiffness: 500,
|
||||
damping: 30,
|
||||
duration: shouldReduceMotion ? 0 : undefined,
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user