5180 fichiers 38.5 Mo 0 téléchargements 195 vues mis à jour il y a 5 heures ui
nitro-v36-with-renderer-v36 / src / common / dropmenu / UbuntuDropmenuView.tsx
UbuntuDropmenuView.tsx 3.9 Ko Télécharger le fichier
import { KeyboardEvent, ReactNode, useEffect, useId, useRef, useState } from 'react';
import arrowImage from '../../assets/images/habbo-skin/slices/dropmenu-arrow.png';
import { BaseButton } from '../BaseButton';

export interface UbuntuDropmenuOption<T>
{
    value: T;
    label: string;
}

export interface UbuntuDropmenuViewProps<T>
{
    /** Accessible name; AIR draws the selected caption only. */
    label: string;
    value: T;
    options: readonly UbuntuDropmenuOption<T>[];
    onSelect: (value: T) => void;
    disabled?: boolean;
    className?: string;
}

/**
 * AIR dropmenu style 3 (habbo_skin_dropmenu_3_xml): the dropmenu_frame_3 field with its 22x20 arrow cell 2px from the
 * right edge, and an open list of dropmenu_item_3 rows, 23px each (a 17px u_regular line inside margins 4/2/4/4).
 */
export const UbuntuDropmenuView = <T extends string | number>({ label, value, options, onSelect, disabled = false, className = '' }: UbuntuDropmenuViewProps<T>): ReactNode =>
{
    const id = useId();
    const rootRef = useRef<HTMLDivElement>(null);
    const [ open, setOpen ] = useState(false);
    const selectedIndex = Math.max(0, options.findIndex(option => option.value === value));
    const [ active, setActive ] = useState(selectedIndex);

    useEffect(() =>
    {
        if(!open) return;

        const close = (event: MouseEvent) =>
        {
            if(rootRef.current && !rootRef.current.contains(event.target as Node)) setOpen(false);
        };

        document.addEventListener('mousedown', close);

        return () => document.removeEventListener('mousedown', close);
    }, [ open ]);

    useEffect(() =>
    {
        if(disabled) setOpen(false);
    }, [ disabled ]);

    const toggle = () =>
    {
        if(disabled) return;

        setActive(selectedIndex);
        setOpen(current => !current);
    };

    const choose = (index: number) =>
    {
        const option = options[index];

        setOpen(false);

        if(option && option.value !== value) onSelect(option.value);
    };

    const onKeyDown = (event: KeyboardEvent<HTMLButtonElement>) =>
    {
        if(disabled) return;

        if(event.key === 'Escape') { setOpen(false); return; }
        if(event.key === 'ArrowDown' || event.key === 'ArrowUp')
        {
            event.preventDefault();

            if(!open) { setActive(selectedIndex); setOpen(true); return; }

            setActive(index => Math.min(options.length - 1, Math.max(0, index + (event.key === 'ArrowDown' ? 1 : -1))));
            return;
        }
        if((event.key === 'Enter' || event.key === ' ') && open)
        {
            event.preventDefault();
            choose(active);
        }
    };

    return (
        <div ref={ rootRef } className={ `ubuntu-dropmenu ${ className }`.trim() }>
            <BaseButton type="button" role="combobox" className="ubuntu-dropmenu__field" aria-label={ label } aria-expanded={ open } aria-controls={ `${ id }-list` } disabled={ disabled } onClick={ toggle } onKeyDown={ onKeyDown }>
                <span className="ubuntu-dropmenu__caption">{ options[selectedIndex]?.label ?? '' }</span>
                <img className="ubuntu-dropmenu__arrow" src={ arrowImage } alt="" />
            </BaseButton>
            { open &&
                <ul id={ `${ id }-list` } role="listbox" aria-label={ label } className="ubuntu-dropmenu__list">
                    { options.map((option, index) =>
                        <li key={ String(option.value) } role="option" aria-selected={ index === selectedIndex }
                            className={ `ubuntu-dropmenu__item${ index === active ? ' is-active' : '' }${ index === selectedIndex ? ' is-selected' : '' }` }
                            onMouseEnter={ () => setActive(index) } onMouseDown={ event => event.preventDefault() } onClick={ () => choose(index) }>
                            <span>{ option.label }</span>
                        </li>) }
                </ul> }
        </div>
    );
};
15Sujets
1Dépôts
20Membres
2En ligne
Profile
Inscrit
Dernière visite
Messages
0
Votes positifs
0
Votes négatifs
0

Nous utilisons des cookies pour assurer le bon fonctionnement du site et améliorer votre expérience. en savoir plus

Autoriser