import { useCallback, useEffect, useRef } from 'react'; // Unique ID for the aria-live notification area element. const notificationAreaId = 'loader-notification-area'; // Custom attribute to track active spinners const activeSpinnersAttr = 'data-active-spinners'; // Generates a random unique ID for each spinner instance. function generateUniqueId() { return `spinner-${Math.random().toString(36).substring(2, 9)}`; } // Finds or creates the notification area element in the DOM. const getOrCreateNotificationArea = (): HTMLElement => { let element = document.getElementById(notificationAreaId); // Create and append it to the DOM. if (!element) { element = document.createElement('div'); element.id = notificationAreaId; element.setAttribute('role', 'status'); element.setAttribute('aria-live', 'polite'); element.className = 'ds-visually-hidden'; document.body.appendChild(element); } return element; }; export const useNotificationArea = ( loadingText = 'Γίνεται φόρτωση...', finishedText = 'Η φόρτωση ολοκληρώθηκε.' ) => { // Unique ID for this spinner. const spinnerId = useRef(generateUniqueId()); // Reference to the notification area DOM element. const notificationRef = useRef(null); // Returns an array of active spinner IDs. const getActiveIds = useCallback(() => { const current = notificationRef.current; const value = current?.getAttribute(activeSpinnersAttr) || ''; return value.split(',').filter(Boolean); }, [notificationRef]); // Adds this spinner's ID to the active list. const setActiveSpinnerId = useCallback(() => { const current = notificationRef.current; if (current) { const ids = new Set(getActiveIds()); ids.add(spinnerId.current); current.setAttribute(activeSpinnersAttr, Array.from(ids).join(',')); } }, [notificationRef, getActiveIds, spinnerId]); // Removes this spinner's ID from the active list. const unsetActiveSpinnerId = useCallback(() => { const current = notificationRef.current; if (current) { const ids = getActiveIds().filter((id) => id !== spinnerId.current); current.setAttribute(activeSpinnersAttr, ids.join(',')); } }, [notificationRef, getActiveIds, spinnerId]); // Updates the text content of the aria-live region. const setTextContent = (text: string) => { if (notificationRef.current) { notificationRef.current.textContent = text; } }; useEffect(() => { if (typeof document === 'undefined') return; // Set up notification area and mark this spinner as active. notificationRef.current = getOrCreateNotificationArea(); setActiveSpinnerId(); setTextContent(loadingText); // Cleanup when component unmounts. return () => { unsetActiveSpinnerId(); // If no active spinners remain, announce "finished" and clear the text. if (getActiveIds().length === 0) { setTextContent(finishedText); // Give screen readers a chance to announce "finished" before clearing. setTimeout(() => { const el = notificationRef.current; if (el && getActiveIds().length === 0) { el.removeAttribute(activeSpinnersAttr); el.textContent = ''; } }, 1000); } }; }, [ loadingText, finishedText, setActiveSpinnerId, unsetActiveSpinnerId, getActiveIds, ]); };