<div class="n7-dropdown-menu has-dropdown-menu relative">

    <button class="n7-dropdown-menu__trigger n7-dropdown-trigger group w-full flex items-center font-medium hover:underline focus:n7-content-primary  aria-expanded:n7-background-02 lg:aria-expanded:bg-white aria-expanded:n7-content-01 p-3 gap-2 text-sm lg:text-base text-gray-700" aria-expanded="false" aria-controls="megamenu01">

        Componenti da validare<svg class="n7-icon inline-block align-middle fill-current w-5 h-5 ml-auto transition-all n7-dropdown-menu__trigger-icon" aria-hidden="true" focusable="false" role="img">
            <use href="../../icons.svg#mini--chevron-down" />
        </svg>
    </button>

    <!-- MEGAMENU CONTENT -->
    <div class="n7-dropdown-menu__list hidden border n7-border-gray-01 bg-white lg:shadow-lg
                lg:absolute top-full left-0 right-0 z-50 
                min-w-full w-auto
                lg:min-w-[800px] lg:max-w-[1000px] lg:w-auto lg:right-auto
                 lg:shadow-xl" id="megamenu01">

        <div class="overflow-hidden rounded-lg">
            <div class="flex flex-col lg:flex-row">

                <!-- Left Panel -->

                <div class="w-full lg:w-1/3 bg-gray-50 p-4 lg:p-6 border-b n7-border-gray-01 lg:border-b-0 lg:border-r lg:border-r-gray-200">
                    <div class="relative lg:border-l-4 lg:border-blue-500 lg:pl-5">

                        <p class="font-semibold text-gray-900">Guida ai componenti</p>

                        <p class="mt-2 text-sm text-gray-600">Trova tutti i componenti disponibili per il tuo progetto. Organizzati per categorie e funzionalità.</p>

                    </div>
                </div>

                <!-- Right Panel with menu items -->
                <div class="w-full lg:w-2/3 p-4 lg:p-6">
                    <ul class="n7-megamenu-list flex flex-col lg:grid lg:grid-cols-1 lg:md:grid-cols-2 list-none m-0 p-0">

                        <li class="n7-megamenu-item border-b n7-border-gray-01 last-of-type:border-b-0 lg:border-b-0 lg:rounded-md">

                            <!-- Item con sottomenu -->
                            <div class="n7-megamenu-dropdown group w-full lg:bg-gray-50 lg:rounded-md">
                                <button type="button" class="n7-megamenu-trigger w-full flex items-center p-3 text-sm font-medium text-gray-900 lg:rounded-md text-left transition-colors duration-200 hover:underline focus:n7-background-02" aria-expanded="false" data-dropdown-trigger>
                                    <svg class="mr-3 h-5 w-5 transition-transform duration-200" viewBox="0 0 24 24" fill="currentColor" data-dropdown-icon>
                                        <path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"></path>
                                    </svg>
                                    <span>Layout &amp; Structure</span>
                                </button>
                                <!-- Sottomenu -->
                                <ul class="n7-megamenu-submenu hidden overflow-hidden bg-gray-50 lg:bg-transparent lg:px-3 lg:py-3 list-none m-0 p-0 transition-all duration-200" data-dropdown-content>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/grid" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Grid System
                                        </a>
                                    </li>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/flexbox" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Flexbox Utilities
                                        </a>
                                    </li>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/container" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Container
                                        </a>
                                    </li>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/spacing" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Spacing
                                        </a>
                                    </li>

                                </ul>
                            </div>

                        </li>

                        <li class="n7-megamenu-item border-b n7-border-gray-01 last-of-type:border-b-0 lg:border-b-0 lg:rounded-md">

                            <!-- Item con sottomenu -->
                            <div class="n7-megamenu-dropdown group w-full lg:bg-gray-50 lg:rounded-md">
                                <button type="button" class="n7-megamenu-trigger w-full flex items-center p-3 text-sm font-medium text-gray-900 lg:rounded-md text-left transition-colors duration-200 hover:underline focus:n7-background-02" aria-expanded="false" data-dropdown-trigger>
                                    <svg class="mr-3 h-5 w-5 transition-transform duration-200" viewBox="0 0 24 24" fill="currentColor" data-dropdown-icon>
                                        <path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"></path>
                                    </svg>
                                    <span>Form Elements</span>
                                </button>
                                <!-- Sottomenu -->
                                <ul class="n7-megamenu-submenu hidden overflow-hidden bg-gray-50 lg:bg-transparent lg:px-3 lg:py-3 list-none m-0 p-0 transition-all duration-200" data-dropdown-content>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/inputs" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Input Fields
                                        </a>
                                    </li>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/buttons" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Buttons
                                        </a>
                                    </li>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/checkboxes" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Checkboxes &amp; Radio
                                        </a>
                                    </li>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/selects" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Select &amp; Dropdown
                                        </a>
                                    </li>

                                </ul>
                            </div>

                        </li>

                        <li class="n7-megamenu-item border-b n7-border-gray-01 last-of-type:border-b-0 lg:border-b-0 lg:rounded-md">

                            <!-- Item con sottomenu -->
                            <div class="n7-megamenu-dropdown group w-full lg:bg-gray-50 lg:rounded-md">
                                <button type="button" class="n7-megamenu-trigger w-full flex items-center p-3 text-sm font-medium text-gray-900 lg:rounded-md text-left transition-colors duration-200 hover:underline focus:n7-background-02" aria-expanded="false" data-dropdown-trigger>
                                    <svg class="mr-3 h-5 w-5 transition-transform duration-200" viewBox="0 0 24 24" fill="currentColor" data-dropdown-icon>
                                        <path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"></path>
                                    </svg>
                                    <span>Navigation</span>
                                </button>
                                <!-- Sottomenu -->
                                <ul class="n7-megamenu-submenu hidden overflow-hidden bg-gray-50 lg:bg-transparent lg:px-3 lg:py-3 list-none m-0 p-0 transition-all duration-200" data-dropdown-content>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/navigation" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Main Navigation
                                        </a>
                                    </li>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/breadcrumbs" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Breadcrumbs
                                        </a>
                                    </li>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/pagination" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Pagination
                                        </a>
                                    </li>

                                </ul>
                            </div>

                        </li>

                        <li class="n7-megamenu-item border-b n7-border-gray-01 last-of-type:border-b-0 lg:border-b-0 lg:rounded-md">

                            <!-- Item con sottomenu -->
                            <div class="n7-megamenu-dropdown group w-full lg:bg-gray-50 lg:rounded-md">
                                <button type="button" class="n7-megamenu-trigger w-full flex items-center p-3 text-sm font-medium text-gray-900 lg:rounded-md text-left transition-colors duration-200 hover:underline focus:n7-background-02" aria-expanded="false" data-dropdown-trigger>
                                    <svg class="mr-3 h-5 w-5 transition-transform duration-200" viewBox="0 0 24 24" fill="currentColor" data-dropdown-icon>
                                        <path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"></path>
                                    </svg>
                                    <span>Content Display</span>
                                </button>
                                <!-- Sottomenu -->
                                <ul class="n7-megamenu-submenu hidden overflow-hidden bg-gray-50 lg:bg-transparent lg:px-3 lg:py-3 list-none m-0 p-0 transition-all duration-200" data-dropdown-content>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/cards" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Cards
                                        </a>
                                    </li>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/tables" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Tables
                                        </a>
                                    </li>

                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="/components/lists" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            Lists
                                        </a>
                                    </li>

                                </ul>
                            </div>

                        </li>

                    </ul>
                </div>

            </div>
        </div>
    </div>

</div>
{% set dropdownId = id or 'dropdown' %}
{% set triggerText = label or 'Menu item' %}

<{% if listItem %}li{% else %}div{% endif %} class="n7-dropdown-menu{% if listItem %} has-dropdown-menu{% endif %}{% if isMegamenu %} has-dropdown-menu{% endif %} relative{% block classes %}{% if classes %} {{ classes }}{% endif %}{%- endblock classes -%}">
    
    <button class="n7-dropdown-menu__trigger n7-dropdown-trigger group w-full flex items-center font-medium hover:underline focus:n7-content-primary {% if triggerAriaExpandedStyles %} {{ triggerAriaExpandedStyles }}{% endif %}{% if triggerSpacing %} {{ triggerSpacing }}{% endif %}{% if triggerFontSize %} {{ triggerFontSize }}{% endif %}{% if triggerTextColor %} {{ triggerTextColor }}{% endif %}" 
            aria-expanded="false" 
            aria-controls="{{ dropdownId }}">
        
        {% if preContent %}
        {{ preContent | safe }}
        {% endif %}
        
        {{ triggerText }}
        
        {%- render '@icon--small', {id: 'mini--chevron-down',size: 'w-5 h-5', classes: 'ml-auto transition-all n7-dropdown-menu__trigger-icon'},true -%}
    </button>
    
    {% if isMegamenu %}
    <!-- MEGAMENU CONTENT -->
    <div class="n7-dropdown-menu__list hidden border n7-border-gray-01 bg-white lg:shadow-lg
                lg:absolute top-full left-0 right-0 z-50 
                min-w-full w-auto
                lg:min-w-[800px] lg:max-w-[1000px] lg:w-auto lg:right-auto
                {% if listClasses %} {{ listClasses }}{% endif %}" 
         id="{{ dropdownId }}">
        
        <div class="overflow-hidden rounded-lg">
            <div class="flex flex-col lg:flex-row">
                
                <!-- Left Panel -->
                {% if leftPanel %}
                <div class="w-full lg:w-1/3 bg-gray-50 p-4 lg:p-6 border-b n7-border-gray-01 lg:border-b-0 lg:border-r lg:border-r-gray-200">
                    <div class="relative lg:border-l-4 lg:border-blue-500 lg:pl-5">
                        {% if leftPanel.title %}
                        <p class="font-semibold text-gray-900">{{ leftPanel.title }}</p>
                        {% endif %}
                        {% if leftPanel.description %}
                        <p class="mt-2 text-sm text-gray-600">{{ leftPanel.description | safe }}</p>
                        {% endif %}
                    </div>
                </div>
                {% endif %}
                
                <!-- Right Panel with menu items -->
                <div class="w-full {% if leftPanel %}lg:w-2/3{% endif %} p-4 lg:p-6">
                    <ul class="n7-megamenu-list flex flex-col lg:grid lg:grid-cols-1 lg:md:grid-cols-2 list-none m-0 p-0">
                        {% for item in rightPanelItems %}
                        <li class="n7-megamenu-item border-b n7-border-gray-01 last-of-type:border-b-0 lg:border-b-0 lg:rounded-md">
                            {% if item.children %}
                            <!-- Item con sottomenu -->
                            <div class="n7-megamenu-dropdown group w-full lg:bg-gray-50 lg:rounded-md">
                                <button 
                                    type="button" 
                                    class="n7-megamenu-trigger w-full flex items-center p-3 text-sm font-medium text-gray-900 lg:rounded-md text-left transition-colors duration-200 hover:underline focus:n7-background-02"
                                    aria-expanded="false"
                                    data-dropdown-trigger>
                                    <svg class="mr-3 h-5 w-5 transition-transform duration-200" viewBox="0 0 24 24" fill="currentColor" data-dropdown-icon>
                                        <path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"></path>
                                    </svg>
                                    <span>{{ item.text }}</span>
                                </button>
                                <!-- Sottomenu -->
                                <ul class="n7-megamenu-submenu hidden overflow-hidden bg-gray-50 lg:bg-transparent lg:px-3 lg:py-3 list-none m-0 p-0 transition-all duration-200" data-dropdown-content>
                                    {% for child in item.children %}
                                    <li class="n7-megamenu-subitem border-b n7-border-gray-01 last:border-b-0 lg:border-b-0 lg:py-1">
                                        <a href="{{ child.href or '#' }}" class="n7-megamenu-sublink block text-sm lg:text-xs text-gray-600 p-3 lg:p-0 lg:pl-8 lg:pb-2.5 lg:py-1 transition-colors duration-200 hover:underline hover:n7-background-02 focus:n7-background-02">
                                            {{ child.text }}
                                        </a>
                                    </li>
                                    {% endfor %}
                                </ul>
                            </div>
                            {% else %}
                            <!-- Item semplice -->
                            <div class="n7-megamenu-dropdown lg:bg-gray-50 lg:rounded-md">
                                <a href="{{ item.href or '#' }}" class="n7-megamenu-link group flex items-center p-3 text-sm font-medium text-gray-900 w-full transition-colors duration-200 lg:rounded-md hover:underline hover:n7-background-02 focus:n7-background-02">
                                    <svg class="mr-3 h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
                                        <path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"></path>
                                    </svg>
                                    {{ item.text }}
                                </a>
                            </div>
                            {% endif %}
                        </li>
                        {% endfor %}
                    </ul>
                </div>
                
            </div>
        </div>
    </div>
    
    {% else %}
    <!-- DROPDOWN SEMPLICE -->
    <ul class="n7-dropdown-menu__list hidden border border-t-0 n7-border-gray-01{% if listWidth %} {{ listWidth }}{% endif %} {% if listPosition %} {{ listPosition }}{% endif %}{% if listResponsiveStyles %} {{ listResponsiveStyles }}{% endif %}{% if listClasses %} {{ listClasses }}{% endif %}" id="{{ dropdownId }}">
        {% for item in items %}
        <li class="n7-dropdown-menu__item group border-b n7-border-gray-01 last-of-type:border-b-0{% if listItemClasses %} {{ listItemClasses }} {% endif %}">
            <a class="block p-3 transition bg-white{% if dropdownItemFontSize %} {{ dropdownItemFontSize }}{% endif %} hover:underline hover:n7-background-02 focus:n7-background-02 group-first:rounded-t group-last:rounded-b" href="{{ item.href or '#' }}">
                <div class="flex gap-2 items-center">
                    {% if item.iconLeft %}
                        {% render '@icon--small', { id: item.iconLeft, classes: 'mr-2', size: 'w-4 h-4 md:w-5 md:h-5 lg:w-6 lg:h-6' }, true %}
                    {% endif %}
                    <span class="grow">{% if item.label %}{{ item.label }}{% else %} Item {% endif %}</span>
                    {% if item.iconRight %}
                        {% render '@icon--small', { id: item.iconRight, classes: 'ml-2 transition-all duration-200 ease-out transform group-hover:translate-x-1', size: 'w-4 h-4 md:w-5 md:h-5 lg:w-6 lg:h-6' }, true %}
                    {% endif %}
                </div>
                {% if item.description %}<div class="text-sm text-gray-600 mt-1">{{ item.description }}</div>{% endif %}
            </a>
        </li>
        {% endfor %}
    </ul>
    {% endif %}
    
</{% if listItem %}li{% else %}div{% endif %}>
{
  "label": "Componenti da validare",
  "id": "megamenu01",
  "triggerSpacing": "p-3 gap-2",
  "triggerFontSize": "text-sm lg:text-base",
  "dropdownItemFontSize": "text-sm xl:text-base",
  "listResponsiveStyles": "lg:border-t lg:absolute lg:rounded lg:shadow-md",
  "triggerAriaExpandedStyles": "aria-expanded:n7-background-02 lg:aria-expanded:bg-white aria-expanded:n7-content-01",
  "items": [
    {
      "label": "Item label 1",
      "iconLeft": "solid--cube-transparent"
    },
    {
      "label": "Item label 3",
      "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
      "iconRight": "mini--chevron-right"
    },
    {
      "label": "Item label 4",
      "iconLeft": "solid--cube-transparent",
      "iconRight": "mini--chevron-right"
    },
    {
      "label": "Item label 5"
    }
  ],
  "triggerTextColor": "text-gray-700",
  "listClasses": "lg:shadow-xl",
  "isMegamenu": true,
  "leftPanel": {
    "title": "Guida ai componenti",
    "description": "Trova tutti i componenti disponibili per il tuo progetto. Organizzati per categorie e funzionalità."
  },
  "rightPanelItems": [
    {
      "text": "Layout & Structure",
      "children": [
        {
          "text": "Grid System",
          "href": "/components/grid"
        },
        {
          "text": "Flexbox Utilities",
          "href": "/components/flexbox"
        },
        {
          "text": "Container",
          "href": "/components/container"
        },
        {
          "text": "Spacing",
          "href": "/components/spacing"
        }
      ]
    },
    {
      "text": "Form Elements",
      "children": [
        {
          "text": "Input Fields",
          "href": "/components/inputs"
        },
        {
          "text": "Buttons",
          "href": "/components/buttons"
        },
        {
          "text": "Checkboxes & Radio",
          "href": "/components/checkboxes"
        },
        {
          "text": "Select & Dropdown",
          "href": "/components/selects"
        }
      ]
    },
    {
      "text": "Navigation",
      "children": [
        {
          "text": "Main Navigation",
          "href": "/components/navigation"
        },
        {
          "text": "Breadcrumbs",
          "href": "/components/breadcrumbs"
        },
        {
          "text": "Pagination",
          "href": "/components/pagination"
        }
      ]
    },
    {
      "text": "Content Display",
      "children": [
        {
          "text": "Cards",
          "href": "/components/cards"
        },
        {
          "text": "Tables",
          "href": "/components/tables"
        },
        {
          "text": "Lists",
          "href": "/components/lists"
        }
      ]
    }
  ]
}
  • Content:
    /**
     * DROPDOWN MENU
     */
    @layer components {
      /* TODO: MOVE STYLE TO TAILWIND CLASS? */
      .n7-dropdown-menu__list[style*="block"] {
        @apply z-10;
      }
    }
    
    @layer components {
      .n7-dropdown-trigger[aria-expanded="true"] .n7-dropdown-menu__trigger-icon {
        @apply -rotate-180;
      }
      
      .n7-megamenu-trigger[aria-expanded="true"] [data-dropdown-icon] {
        @apply rotate-90;
      }
      
      .n7-dropdown-menu__list {
        transition: opacity 0.2s ease-in-out;
      }
      
      .n7-megamenu-submenu {
        transition: opacity 0.2s ease-in-out;
      }
    
      /* ==========================================================================
         POSIZIONAMENTO INTELLIGENTE
         ========================================================================== */
      
      /* Allineamento orizzontale */
      .align-right {
        @apply right-0 left-auto;
      }
      
      .align-left {
        @apply left-0 right-auto;
      }
      
      /* Forzatura quando non c'è spazio */
      .force-right {
        right: 0 !important;
        left: auto !important;
      }
      
      /* Posizionamento verticale per sottomenu */
      .open-upward {
        @apply bottom-full top-auto;
      }
      
      .open-downward {
        @apply top-full bottom-auto;
      }
    }
  • URL: /components/raw/dropdown-menu/dropdown-menu.css
  • Filesystem Path: components/03-molecules/dropdown-menu/dropdown-menu.css
  • Size: 1.1 KB
  • Content:
    /*
     * JavaScript per supportare sia dropdown semplici che megamenu
     */
    
    'use strict';
    
    function findAncestor(element, tagName) {
      while (element) {
        if (element.tagName.toLowerCase() === tagName) {
          return element;
        }
        element = element.parentElement;
      }
      return null;
    }
    
    function getTabbableElements(container) {
      return container.querySelectorAll(
        'a[href], button:not([disabled]), textarea:not([disabled]), input:not([type="hidden"]):not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
      );
    }
    
    // Funzione per calcolare la posizione PRIMA di mostrare il menu
    function calculateOptimalPosition(menu, trigger) {
      // Rimuovi classi precedenti
      menu.classList.remove('align-right', 'align-left', 'force-right');
      
      // Rendi il menu temporaneamente visibile ma invisibile per misurarlo
      const wasHidden = menu.classList.contains('hidden');
      menu.classList.remove('hidden');
      menu.style.visibility = 'hidden';
      menu.style.opacity = '0';
      
      // Misura immediatamente
      const menuRect = menu.getBoundingClientRect();
      const viewportWidth = window.innerWidth;
      
      let positionClass = null;
      
      // Se il menu esce a destra
      if (menuRect.right > viewportWidth - 10) {
        positionClass = 'align-right';
        
        // Test se con align-right va ancora fuori
        menu.classList.add('align-right');
        const testRect = menu.getBoundingClientRect();
        if (testRect.right > viewportWidth - 10) {
          positionClass = 'force-right';
          menu.classList.remove('align-right');
          menu.classList.add('force-right');
        }
      }
      // Se il menu esce a sinistra
      else if (menuRect.left < 10) {
        positionClass = 'align-left';
        menu.classList.add('align-left');
      }
      
      // Nascondilo di nuovo se era nascosto
      if (wasHidden) {
        menu.classList.add('hidden');
      }
      menu.style.visibility = '';
      menu.style.opacity = '';
      
      return positionClass;
    }
    
    class DisclosureNav {
      constructor(domNode) {
        this.rootNode = domNode;
        this.controlledNodes = [];
        this.openIndex = null;
        this.useArrowKeys = true;
        this.isClosing = false;
        
        this.topLevelNodes = [
          ...this.rootNode.querySelectorAll(
            '.main-link, .rtds-dropdown-trigger, .n7-dropdown-menu__trigger, .n7-dropdown-trigger, .n7-primary-navigation__link'
          ),
        ];
    
        this.topLevelNodes.forEach((node, index) => {
          if (
            node.tagName.toLowerCase() === 'button' &&
            node.hasAttribute('aria-controls')
          ) {
            const menu = node.parentNode.querySelector('ul, div.n7-dropdown-menu__list');
    
            if (menu) {
              this.controlledNodes.push(menu);
    
              node.setAttribute('aria-expanded', 'false');
              this.toggleMenu(menu, false);
    
              menu.addEventListener('keydown', this.onMenuKeyDown.bind(this));
              node.addEventListener('click', this.onButtonClick.bind(this));
              node.addEventListener('keydown', this.onButtonKeyDown.bind(this));
            }
          } else {
            this.controlledNodes.push(null);
            node.addEventListener('keydown', this.onLinkKeyDown.bind(this));
          }
        });
    
        document.addEventListener('click', this.onDocumentClick.bind(this));
        document.addEventListener('keydown', this.onGlobalKeyDown.bind(this));
        window.addEventListener('resize', this.onWindowResize.bind(this));
      }
    
      onWindowResize() {
        if (this.openIndex !== null) {
          const menu = this.controlledNodes[this.openIndex];
          const trigger = this.topLevelNodes[this.openIndex];
          if (menu && trigger) {
            calculateOptimalPosition(menu, trigger);
          }
        }
      }
    
      focusElement(element, delay = 50) {
        if (!element || typeof element.focus !== 'function') return;
        
        setTimeout(() => {
          try {
            element.focus();
          } catch (e) {
            console.warn('Focus failed:', e);
          }
        }, delay);
      }
    
      onGlobalKeyDown(event) {
        if (event.key !== 'Tab' || this.openIndex === null || this.isClosing) {
          return;
        }
    
        const openMenu = this.controlledNodes[this.openIndex];
        if (!openMenu) return;
    
        const activeElement = document.activeElement;
        
        // Se siamo in un sottomenu del megamenu, lascia che MegaMenuSubmenu lo gestisca
        const isInSubmenu = activeElement.closest('[data-dropdown-content]');
        if (isInSubmenu && !isInSubmenu.classList.contains('hidden')) {
          return;
        }
    
        // Gestisci gli elementi tabbabili del menu
        let allTabbableInMenu;
        
        if (openMenu.classList.contains('n7-dropdown-menu__list')) {
          // Per megamenu: escludi elementi in sottomenu chiusi
          allTabbableInMenu = Array.from(getTabbableElements(openMenu)).filter(el => {
            const hiddenSubmenu = el.closest('[data-dropdown-content].hidden');
            return !hiddenSubmenu;
          });
        } else {
          // Per dropdown semplice: prendi tutti
          allTabbableInMenu = Array.from(getTabbableElements(openMenu));
        }
        
        const currentIndex = allTabbableInMenu.indexOf(activeElement);
        
        if (!event.shiftKey && currentIndex === allTabbableInMenu.length - 1) {
          // Tab sull'ultimo elemento del menu - resta sul trigger
          event.preventDefault();
          const currentTrigger = this.topLevelNodes[this.openIndex];
          this.closeMenu();
          this.focusElement(currentTrigger);
        }
        else if (event.shiftKey && currentIndex === 0) {
          // Shift+Tab sul primo elemento del menu
          event.preventDefault();
          this.closeMenuAndFocusTrigger();
        }
      }
    
      closeMenu() {
        if (this.openIndex === null || this.isClosing) return;
        
        this.isClosing = true;
        
        this.topLevelNodes[this.openIndex].setAttribute('aria-expanded', 'false');
        this.toggleMenu(this.controlledNodes[this.openIndex], false);
        this.openIndex = null;
        
        setTimeout(() => {
          this.isClosing = false;
        }, 100);
      }
    
      closeMenuAndFocusTrigger() {
        if (this.openIndex === null || this.isClosing) return;
        
        const trigger = this.topLevelNodes[this.openIndex];
        this.closeMenu();
        this.focusElement(trigger);
      }
    
      controlFocusByKey(keyboardEvent, nodeList, currentIndex) {
        switch (keyboardEvent.key) {
          case 'ArrowUp':
          case 'ArrowLeft':
            keyboardEvent.preventDefault();
            if (currentIndex > -1) {
              var prevIndex = Math.max(0, currentIndex - 1);
              nodeList[prevIndex].focus();
            }
            break;
          case 'ArrowDown':
          case 'ArrowRight':
            keyboardEvent.preventDefault();
            if (currentIndex > -1) {
              var nextIndex = Math.min(nodeList.length - 1, currentIndex + 1);
              nodeList[nextIndex].focus();
            }
            break;
          case 'Home':
            keyboardEvent.preventDefault();
            nodeList[0].focus();
            break;
          case 'End':
            keyboardEvent.preventDefault();
            nodeList[nodeList.length - 1].focus();
            break;
        }
      }
    
      close() {
        this.closeMenu();
      }
    
      onButtonClick(event) {
        if (this.isClosing) return;
        
        var target = event.target;
    
        if (target.tagName.toLowerCase() !== 'button') {
          var buttonAncestor = findAncestor(target, 'button');
          if (buttonAncestor) {
            buttonAncestor.click();
            return;
          }
        }
    
        var button = event.target;
        var buttonIndex = this.topLevelNodes.indexOf(button);
        var buttonExpanded = button.getAttribute('aria-expanded') === 'true';
        
        this.toggleExpand(buttonIndex, !buttonExpanded);
      }
    
      onButtonKeyDown(event) {
        if (this.isClosing) return;
        
        var targetButtonIndex = this.topLevelNodes.indexOf(document.activeElement);
    
        if (event.key === 'Escape') {
          event.preventDefault();
          this.closeMenuAndFocusTrigger();
        } else if (
          this.useArrowKeys &&
          this.openIndex === targetButtonIndex &&
          event.key === 'ArrowDown'
        ) {
          event.preventDefault();
          const menu = this.controlledNodes[this.openIndex];
          const firstTabbable = getTabbableElements(menu)[0];
          if (firstTabbable) {
            firstTabbable.focus();
          }
        } else if (this.useArrowKeys) {
          this.controlFocusByKey(event, this.topLevelNodes, targetButtonIndex);
        }
      }
    
      onLinkKeyDown(event) {
        var targetLinkIndex = this.topLevelNodes.indexOf(document.activeElement);
    
        if (this.useArrowKeys) {
          this.controlFocusByKey(event, this.topLevelNodes, targetLinkIndex);
        }
      }
    
      onMenuKeyDown(event) {
        if (this.openIndex === null || this.isClosing) {
          return;
        }
    
        if (event.key === 'Escape') {
          event.preventDefault();
          event.stopImmediatePropagation();
          this.closeMenuAndFocusTrigger();
          return;
        }
    
        var menuFocusables = Array.from(getTabbableElements(this.controlledNodes[this.openIndex]));
        var currentIndex = menuFocusables.indexOf(document.activeElement);
    
        if (this.useArrowKeys) {
          this.controlFocusByKey(event, menuFocusables, currentIndex);
        }
      }
    
      toggleExpand(index, expanded) {
        if (this.isClosing) return;
        
        if (this.openIndex !== null && this.openIndex !== index) {
          this.closeMenu();
        }
    
        if (this.topLevelNodes[index]) {
          this.openIndex = expanded ? index : null;
          this.topLevelNodes[index].setAttribute('aria-expanded', expanded);
          this.toggleMenu(this.controlledNodes[index], expanded);
        }
      }
    
      toggleMenu(domNode, show) {
        if (!domNode) return;
        
        const isMegaMenu = domNode.classList.contains('n7-dropdown-menu__list');
        const parentMenu = domNode.closest('.has-dropdown-menu, .n7-dropdown-menu');
        const navModule = domNode.closest('.rtds-primary-navigation__module');
    
        if (show) {
          // Calcola la posizione PRIMA di mostrare il menu
          const trigger = this.topLevelNodes[this.openIndex];
          if (trigger) {
            calculateOptimalPosition(domNode, trigger);
          }
          
          // Ora mostra il menu con la posizione già corretta
          if (isMegaMenu) {
            domNode.classList.remove('hidden');
            domNode.classList.add('block');
          } else {
            domNode.classList.remove('rtds-hidden', 'is-hidden');
            domNode.classList.add('is-open');
          }
          
          if (parentMenu) {
            parentMenu.classList.add('has-dropdown-open');
          }
          if (navModule) {
            navModule.classList.add('has-inner-dropdown-open');
          }
          
        } else {
          if (isMegaMenu) {
            domNode.classList.remove('block');
            domNode.classList.add('hidden');
          } else {
            domNode.classList.remove('is-open');
            domNode.classList.add('rtds-hidden');
          }
          
          if (parentMenu) {
            parentMenu.classList.remove('has-dropdown-open');
          }
          if (navModule) {
            navModule.classList.remove('has-inner-dropdown-open');
          }
    
          domNode.classList.remove('align-right', 'align-left', 'force-right', 'open-upward', 'open-downward');
        }
      }
    
      updateKeyControls(useArrowKeys) {
        this.useArrowKeys = useArrowKeys;
      }
    
      onDocumentClick(event) {
        if (this.rootNode.contains(event.target) || this.isClosing) {
          return;
        }
        
        this.closeMenu();
      }
    }
    
    class MegaMenuSubmenu {
      constructor() {
        this.openSubmenus = new Map();
        this.isClosing = false;
        this.init();
      }
    
      focusElement(element, delay = 50) {
        if (!element || typeof element.focus !== 'function') return;
        
        setTimeout(() => {
          try {
            element.focus();
          } catch (e) {
            console.warn('Submenu focus failed:', e);
          }
        }, delay);
      }
    
      init() {
        const megaMenuTriggers = document.querySelectorAll('[data-dropdown-trigger]');
        
        megaMenuTriggers.forEach(trigger => {
          trigger.addEventListener('click', this.handleSubmenuClick.bind(this));
          trigger.addEventListener('keydown', this.handleSubmenuKeydown.bind(this));
        });
    
        document.addEventListener('keydown', this.onGlobalKeyDown.bind(this));
        window.addEventListener('resize', this.onWindowResize.bind(this));
      }
    
      onWindowResize() {
        this.openSubmenus.forEach((submenu, trigger) => {
          calculateOptimalPosition(submenu, trigger);
        });
      }
    
      onGlobalKeyDown(event) {
        if (event.key !== 'Tab' || this.isClosing) {
          return;
        }
    
        const activeElement = document.activeElement;
        const submenu = activeElement.closest('[data-dropdown-content]');
        
        // Solo per sottomenu di megamenu, non per dropdown normali
        if (submenu && !submenu.classList.contains('hidden') && submenu.closest('.n7-dropdown-menu__list')) {
          const trigger = submenu.parentElement.querySelector('[data-dropdown-trigger]');
          if (!trigger) return;
    
          const tabbableElements = getTabbableElements(submenu);
          const currentIndex = Array.from(tabbableElements).indexOf(activeElement);
          
          if (!event.shiftKey && currentIndex === tabbableElements.length - 1) {
            event.preventDefault();
            this.closeSubmenuAndFocusTrigger(trigger);
          }
          else if (event.shiftKey && currentIndex === 0) {
            event.preventDefault();
            this.closeSubmenuAndFocusTrigger(trigger);
          }
        }
      }
    
      closeSubmenu(trigger) {
        const submenu = trigger.parentElement.querySelector('[data-dropdown-content]');
        const icon = trigger.querySelector('[data-dropdown-icon]');
        
        if (submenu) {
          this.isClosing = true;
          this.toggleSubmenu(trigger, submenu, icon, false);
          setTimeout(() => {
            this.isClosing = false;
          }, 100);
        }
      }
    
      closeSubmenuAndFocusTrigger(trigger) {
        this.closeSubmenu(trigger);
        this.focusElement(trigger);
      }
    
      handleSubmenuClick(event) {
        if (this.isClosing) return;
        
        event.preventDefault();
        event.stopPropagation();
        
        const trigger = event.currentTarget;
        const submenu = trigger.parentElement.querySelector('[data-dropdown-content]');
        const icon = trigger.querySelector('[data-dropdown-icon]');
        
        if (!submenu) return;
    
        const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
        
        this.closeOtherSubmenus(trigger);
        this.toggleSubmenu(trigger, submenu, icon, !isExpanded);
      }
    
      handleSubmenuKeydown(event) {
        if (this.isClosing) return;
        
        const trigger = event.currentTarget;
        const submenu = trigger.parentElement.querySelector('[data-dropdown-content]');
        const icon = trigger.querySelector('[data-dropdown-icon]');
    
        if (event.key === 'Enter' || event.key === ' ') {
          event.preventDefault();
          this.handleSubmenuClick(event);
        } else if (event.key === 'Escape') {
          event.preventDefault();
          this.closeSubmenuAndFocusTrigger(trigger);
        } else if (event.key === 'ArrowDown' && trigger.getAttribute('aria-expanded') === 'true') {
          event.preventDefault();
          const firstTabbable = getTabbableElements(submenu)[0];
          if (firstTabbable) {
            firstTabbable.focus();
          }
        }
      }
    
      toggleSubmenu(trigger, submenu, icon, show) {
        trigger.setAttribute('aria-expanded', show);
        
        if (show) {
          // Calcola la posizione PRIMA di mostrare il sottomenu
          calculateOptimalPosition(submenu, trigger);
          
          // Ora mostra il sottomenu con la posizione già corretta
          submenu.classList.remove('hidden');
          submenu.classList.add('block');
          this.openSubmenus.set(trigger, submenu);
          if (icon) {
            icon.style.transform = 'rotate(90deg)';
          }
          
        } else {
          submenu.classList.remove('block');
          submenu.classList.add('hidden');
          this.openSubmenus.delete(trigger);
          if (icon) {
            icon.style.transform = 'rotate(0deg)';
          }
    
          submenu.classList.remove('align-right', 'align-left', 'force-right', 'open-upward', 'open-downward');
        }
      }
    
      closeOtherSubmenus(currentTrigger) {
        const megaMenu = currentTrigger.closest('.n7-dropdown-menu__list');
        if (!megaMenu) return;
    
        const allTriggers = megaMenu.querySelectorAll('[data-dropdown-trigger]');
        
        allTriggers.forEach(trigger => {
          if (trigger !== currentTrigger) {
            const submenu = trigger.parentElement.querySelector('[data-dropdown-content]');
            const icon = trigger.querySelector('[data-dropdown-icon]');
            
            if (submenu) {
              this.toggleSubmenu(trigger, submenu, icon, false);
            }
          }
        });
      }
    
      closeAllSubmenus() {
        this.openSubmenus.forEach((submenu, trigger) => {
          const icon = trigger.querySelector('[data-dropdown-icon]');
          this.toggleSubmenu(trigger, submenu, icon, false);
        });
      }
    }
    
    window.addEventListener(
      'load',
      function () {
        var menus = document.querySelectorAll('.has-dropdown-menu, div.rtds-dropdown-menu, .n7-dropdown-menu[class*="has-dropdown-menu"], .n7-dropdown-menu');
        var disclosureMenus = [];
        var processedPrimaryNavs = new Set();
        
        for (var i = 0; i < menus.length; i++) {
          const menu = menus[i];
          
          // Se è dentro un primary navigation
          const primaryNav = menu.closest('.n7-primary-navigation');
          if (primaryNav) {
            // Se non abbiamo già processato questo primary navigation
            if (!processedPrimaryNavs.has(primaryNav)) {
              processedPrimaryNavs.add(primaryNav);
              
              // Inizializza sul modulo principale che contiene tutti i menu
              const mainModule = primaryNav.querySelector('.n7-primary-navigation__module');
              if (mainModule) {
                disclosureMenus.push(new DisclosureNav(mainModule));
              }
            }
          } else {
            // Menu standalone (non in primary navigation)
            disclosureMenus.push(new DisclosureNav(menu));
          }
        }
    
        new MegaMenuSubmenu();
      },
      false
    );
  • URL: /components/raw/dropdown-menu/dropdown-menu.js
  • Filesystem Path: components/03-molecules/dropdown-menu/dropdown-menu.js
  • Size: 17.6 KB

Dropdown menu component can be used as a dropdown indipendent component (has div as wrapper) or as a list item in a menu list - list-item variant (eg in primary menu).

In preview, component style is missing - this component is dependant on list/menu in which is used.

  • listItem: boolean - if true, the component is used as list item in a menu list, otherwise as a dropdown indipendendt component as is
  • id: value - dropdown id
  • classes: value - add eventual classes
  • triggerSpacing: value - spacing classes for trigger button
  • triggerFontSize: value - font size classes for trigger button
  • triggerTextColor: valie - trigger text color class if needed
  • label: value - dropdown main label (trigger label)
  • listPosition: value - position of the dropdown list - eventually manage left and right position od dropdown list
  • listResponsiveStyles: value - manage responsive styles of dropdown list if needed
  • listWidth: value - width of the dropdown list
  • listClasses: value - classes for dropdown list
  • listItemClasses: value: classes for dropdown list items (li elements)
  • dropdownItemFontSize: value - classes for items font size
  • items:
    • iconLeft: value - id for item left icon
    • label: value - item label
    • iconRight: value - id for item right icon
    • description: value - item description

Blocks:

  • preContent: used eg for user thumb or icon if needed
  • classes: for class overridings in variant templates, if needed