Remove unused components and models for improved maintainability
- Deleted redundant components (`AddActionButton`, `AlertBox`, `AlertStack`, `BackButton`, `CancelButton`, and `CollapsableArea`) and related files. - Removed unused models (`Book`, `BookSerie`, `BookTables`, `Character`, and `Chapter`) to reduce codebase clutter. - Updated project structure and references to reflect these removals.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import {JSX, useEffect, useMemo, useRef, useState} from 'react';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faXmark} from '@fortawesome/free-solid-svg-icons';
|
||||
import React, {JSX, useEffect, useRef, useState} from 'react';
|
||||
import {createPortal} from 'react-dom';
|
||||
import {X} from 'lucide-react';
|
||||
import Button from '@/components/ui/Button';
|
||||
import IconButton from '@/components/ui/IconButton';
|
||||
|
||||
export type GuidePosition =
|
||||
'top'
|
||||
@@ -38,23 +40,33 @@ interface GuideTourProps {
|
||||
* position, and properties for spotlight rendering.
|
||||
* @return {string} The CSS background string representing the spotlight effect.
|
||||
*/
|
||||
function getOverlayColor(opacity: number): string {
|
||||
const style: CSSStyleDeclaration = getComputedStyle(document.documentElement);
|
||||
const darkest: string = style.getPropertyValue('--theme-darkest-background').trim() || '#1A1A1A';
|
||||
const hex: string = darkest.replace('#', '');
|
||||
const r: number = parseInt(hex.substring(0, 2), 16);
|
||||
const g: number = parseInt(hex.substring(2, 4), 16);
|
||||
const b: number = parseInt(hex.substring(4, 6), 16);
|
||||
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
}
|
||||
|
||||
function getSpotlightBackground(step: GuideStep): string {
|
||||
if (step.x !== undefined && step.y !== undefined) {
|
||||
return 'rgba(0, 0, 0, 0.5)';
|
||||
return getOverlayColor(0.5);
|
||||
}
|
||||
if (!step.targetSelector) {
|
||||
return 'rgba(0, 0, 0, 0.5)';
|
||||
return getOverlayColor(0.5);
|
||||
}
|
||||
const element = document.querySelector(step.targetSelector) as HTMLElement | null;
|
||||
const element: HTMLElement | null = document.querySelector<HTMLElement>(step.targetSelector);
|
||||
if (!element) {
|
||||
return 'rgba(0, 0, 0, 0.5)';
|
||||
return getOverlayColor(0.5);
|
||||
}
|
||||
const rect: DOMRect = element.getBoundingClientRect();
|
||||
const centerX: number = rect.left + rect.width / 2;
|
||||
const centerY: number = rect.top + rect.height / 2;
|
||||
const radius: number = Math.max(rect.width, rect.height) / 2 + (step.highlightRadius || 10);
|
||||
|
||||
return `radial-gradient(circle at ${centerX}px ${centerY}px, transparent ${radius}px, rgba(0, 0, 0, 0.65) ${radius + 20}px)`;
|
||||
|
||||
return `radial-gradient(circle at ${centerX}px ${centerY}px, transparent ${radius}px, ${getOverlayColor(0.65)} ${radius + 20}px)`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +75,13 @@ function getSpotlightBackground(step: GuideStep): string {
|
||||
* @param {GuideStep} step - An object containing the configuration for positioning the popover, including its x and y coordinates, target selector, and preferred position.
|
||||
* @return {React.CSSProperties} An object representing the CSS properties to position the popover, including `left`, `top`, and optionally `transform` values.
|
||||
*/
|
||||
function getPopoverPosition(step: GuideStep): React.CSSProperties {
|
||||
interface PopoverPosition {
|
||||
left: string;
|
||||
top: string;
|
||||
transform?: string;
|
||||
}
|
||||
|
||||
function getPopoverPosition(step: GuideStep): PopoverPosition {
|
||||
if (step.x !== undefined && step.y !== undefined) {
|
||||
return {
|
||||
left: `${step.x}%`,
|
||||
@@ -80,7 +98,7 @@ function getPopoverPosition(step: GuideStep): React.CSSProperties {
|
||||
};
|
||||
}
|
||||
|
||||
const element = document.querySelector(step.targetSelector) as HTMLElement | null;
|
||||
const element: HTMLElement | null = document.querySelector<HTMLElement>(step.targetSelector);
|
||||
if (!element) {
|
||||
return {
|
||||
left: '50%',
|
||||
@@ -88,12 +106,12 @@ function getPopoverPosition(step: GuideStep): React.CSSProperties {
|
||||
transform: 'translate(-50%, -50%)'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const rect: DOMRect = element.getBoundingClientRect();
|
||||
const {left, top, width, height} = rect;
|
||||
const popoverWidth = 420;
|
||||
const popoverHeight = 300;
|
||||
const margin = 20;
|
||||
const popoverWidth: number = 420;
|
||||
const popoverHeight: number = 300;
|
||||
const margin: number = 20;
|
||||
const position: GuidePosition = step.position || 'auto';
|
||||
|
||||
switch (position) {
|
||||
@@ -188,16 +206,17 @@ export default function GuideTour({stepId, steps, onClose, onComplete}: GuideTou
|
||||
const [currentStep, setCurrentStep] = useState<number>(0);
|
||||
const [isVisible, setIsVisible] = useState<boolean>(false);
|
||||
const [rendered, setRendered] = useState<boolean>(false);
|
||||
const overlayRef: React.RefObject<HTMLDivElement | null> = useRef<HTMLDivElement>(null);
|
||||
|
||||
const filteredSteps: GuideStep[] = useMemo((): GuideStep[] => {
|
||||
const filteredSteps: GuideStep[] = React.useMemo((): GuideStep[] => {
|
||||
return steps.filter((step: GuideStep): boolean => step.id >= stepId);
|
||||
}, [steps, stepId]);
|
||||
|
||||
const currentStepData: GuideStep = filteredSteps[currentStep];
|
||||
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const timeoutRef: React.RefObject<NodeJS.Timeout | null> = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const showStep = (index: number) => {
|
||||
function showStep(index: number): void {
|
||||
setIsVisible(false);
|
||||
|
||||
if (timeoutRef.current) {
|
||||
@@ -210,7 +229,7 @@ export default function GuideTour({stepId, steps, onClose, onComplete}: GuideTou
|
||||
|
||||
const step: GuideStep = filteredSteps[index];
|
||||
if (step?.targetSelector) {
|
||||
const element = document.querySelector(step.targetSelector) as HTMLElement;
|
||||
const element: HTMLElement | null = document.querySelector<HTMLElement>(step.targetSelector);
|
||||
if (element) {
|
||||
element.scrollIntoView({behavior: 'smooth', block: 'center'});
|
||||
}
|
||||
@@ -224,42 +243,46 @@ export default function GuideTour({stepId, steps, onClose, onComplete}: GuideTou
|
||||
}, 50);
|
||||
}, 600);
|
||||
}, 200);
|
||||
};
|
||||
}
|
||||
|
||||
useEffect((): () => void => {
|
||||
showStep(0);
|
||||
|
||||
|
||||
return (): void => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect((): void => {
|
||||
if (overlayRef.current) {
|
||||
overlayRef.current.style.background = rendered ? getSpotlightBackground(currentStepData) : getOverlayColor(0.5);
|
||||
overlayRef.current.style.opacity = isVisible ? '1' : '0';
|
||||
}
|
||||
}, [rendered, isVisible, currentStepData]);
|
||||
|
||||
const handleNext: () => void = (): void => {
|
||||
function handleNext(): void {
|
||||
if (currentStep < filteredSteps.length - 1) {
|
||||
showStep(currentStep + 1);
|
||||
} else {
|
||||
onComplete();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handlePrevious: () => void = (): void => {
|
||||
function handlePrevious(): void {
|
||||
if (currentStep > 0) {
|
||||
showStep(currentStep - 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (!filteredSteps.length || !currentStepData) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 font-['Lora']">
|
||||
return createPortal(
|
||||
<div className="fixed inset-0 z-[60] font-['Lora']">
|
||||
<div
|
||||
ref={overlayRef}
|
||||
className="absolute inset-0 transition-opacity duration-500"
|
||||
style={{
|
||||
background: rendered ? getSpotlightBackground(currentStepData) : 'rgba(0, 0, 0, 0.5)',
|
||||
opacity: isVisible ? 1 : 0
|
||||
}}
|
||||
onClick={onClose}
|
||||
/>
|
||||
{rendered && (
|
||||
@@ -273,7 +296,8 @@ export default function GuideTour({stepId, steps, onClose, onComplete}: GuideTou
|
||||
onClose={onClose}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>,
|
||||
document.body
|
||||
);
|
||||
}
|
||||
|
||||
@@ -309,18 +333,25 @@ function GuidePopup(
|
||||
onNext: () => void;
|
||||
onClose: () => void;
|
||||
}): JSX.Element {
|
||||
const positionStyle = useMemo(() => {
|
||||
return getPopoverPosition(step);
|
||||
const popupRef: React.RefObject<HTMLDivElement | null> = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect((): void => {
|
||||
if (popupRef.current) {
|
||||
const pos: PopoverPosition = getPopoverPosition(step);
|
||||
popupRef.current.style.left = pos.left;
|
||||
popupRef.current.style.top = pos.top;
|
||||
popupRef.current.style.transform = pos.transform || '';
|
||||
}
|
||||
}, [step]);
|
||||
|
||||
return (
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`absolute bg-tertiary border border-primary/30 rounded-xl shadow-2xl w-96 transition-all duration-300 ${
|
||||
isVisible ? 'opacity-100 scale-100' : 'opacity-0 scale-95'
|
||||
ref={popupRef}
|
||||
className={`absolute bg-tertiary border border-primary/30 rounded-xl w-96 transition-opacity duration-300 ${
|
||||
isVisible ? 'opacity-100' : 'opacity-0'
|
||||
}`}
|
||||
style={positionStyle}
|
||||
>
|
||||
<div className="px-8 py-6 border-b border-secondary/40">
|
||||
<div className="px-8 py-6 border-b border-secondary">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1 mr-6">
|
||||
<h3 className="text-text-primary font-semibold text-xl mb-3">
|
||||
@@ -331,24 +362,18 @@ function GuidePopup(
|
||||
Étape {currentStep + 1} sur {totalSteps}
|
||||
</span>
|
||||
<div className="flex items-center space-x-2">
|
||||
{Array.from({length: totalSteps}).map((_, index) => (
|
||||
{Array.from({length: totalSteps}).map((_: unknown, index: number) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`w-2.5 h-2.5 rounded-full transition-all duration-300 ${
|
||||
index <= currentStep ? 'bg-primary scale-110' : 'bg-secondary/60'
|
||||
index <= currentStep ? 'bg-primary' : 'bg-secondary'
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-muted hover:text-text-primary transition-colors p-2 hover:bg-secondary/30 rounded-lg"
|
||||
type="button"
|
||||
>
|
||||
<FontAwesomeIcon icon={faXmark} className="text-lg"/>
|
||||
</button>
|
||||
<IconButton icon={X} variant="ghost" shape="square" onClick={onClose}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-8 py-8">
|
||||
@@ -356,26 +381,18 @@ function GuidePopup(
|
||||
{step.content}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-8 py-6 bg-secondary/20 border-t border-secondary/30 rounded-b-xl">
|
||||
<div className="px-8 py-6 bg-tertiary border-t border-secondary rounded-b-xl">
|
||||
<div className="flex items-center justify-between">
|
||||
{currentStep > 0 ? (
|
||||
<button
|
||||
onClick={onPrevious}
|
||||
className="text-muted hover:text-text-primary text-sm px-4 py-2 rounded-lg hover:bg-secondary/30 transition-all"
|
||||
type="button"
|
||||
>
|
||||
<Button variant="ghost" size="sm" onClick={onPrevious}>
|
||||
← Précédent
|
||||
</button>
|
||||
</Button>
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
<button
|
||||
onClick={onNext}
|
||||
className="bg-primary hover:bg-primary-dark text-text-primary px-6 py-3 rounded-lg transition-all duration-200 text-sm font-medium shadow-lg hover:shadow-xl transform hover:scale-105"
|
||||
type="button"
|
||||
>
|
||||
<Button variant="primary" onClick={onNext}>
|
||||
{currentStep === totalSteps - 1 ? '🎉 Terminer' : 'Continuer →'}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user