<ul class="Linklist Linklist--padded Treeview Treeview--default js-Treeview u-text-r-xs">
    <li><a href="#">Servizi</a>
        <ul data-megamenu-class="u-md-size1of2">
            <li><a href="#">Per aree tematiche</a>
                <ul data-megamenu-class="u-columns-2">
                    <li><a href="page--servizi.html#ambiente">Ambiente</a></li>
                    <li><a href="page--servizi.html#casa">Casa</a></li>
                    <li><a href="page--servizi.html#certificati">Certificati e documenti</a></li>
                    <li><a href="page--servizi.html#cultura">Cultura e tempo libero</a></li>
                    <li><a href="page--servizi.html#denunce">Denunce</a></li>
                    <li><a href="page--servizi.html#elezioni">Elezioni</a></li>
                    <li><a href="page--servizi.html#famiglia">Famiglia</a></li>
                    <li><a href="page--servizi.html#imprese">Imprese</a></li>
                    <!-- <li><a href="page- -servizi.html#innovazione">Innovazione e smart city</a></li> -->
                    <li><a href="page--servizi.html#lavoro">Lavoro</a></li>
                    <li><a href="page--servizi.html#salute">Salute</a></li>
                    <li><a href="page--servizi.html#studio">Studio</a></li>
                    <li><a href="page--servizi.html#tributi">Tributi e sanzioni</a></li>
                    <li><a href="page--servizi.html#uffici">Uffici comunali</a></li>
                    <li><a href="page--servizi.html#volontariato">Volontariato</a></li>
                </ul>
            </li>
            <li><a href="#">Servizi online</a>
                <ul>
                    <li><a href="#">Segnalazioni</a></li>
                    <li><a href="#">Pagamenti</a></li>
                    <li><a href="#" data-megamenu-class="u-text-h4">tutti i servizi online
            <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a></li>
                </ul>
            </li>
        </ul>
    </li>
    <li><a href="#">L'Amministrazione</a>
        <ul>
            <li><a href="#">Organizzazione <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a>
                <ul>
                    <li><a href="#">Il Sindaco</a></li>
                    <li><a href="#">Giunta comunale</a></li>
                    <!-- <li><a href="#">Organi di indirizzo politico-amministrativo</a></li>
          <li><a href="#">Sanzioni per mancata comunicazione dei dati</a></li> -->
                    <li><a href="#">Articolazione degli uffici</a></li>
                    <li><a href="#">Telefono e posta elettronica</a></li>
                    <!-- <li><a href="#" data-megamenu-class="u-text-h4">altro
            <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a></li> -->
                </ul>
            </li>
            <li><a href="#">Attività e procedimenti <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a>
                <ul>
                    <li><a href="#">Dichiarazioni sostitutive e acquisizione d'ufficio dei dati</a></li>
                    <!-- <li><a href="#" data-megamenu-class="u-text-h4">altro
            <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a></li> -->
                </ul>
            </li>
            <li><a href="#">Bandi di concorso <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a>
                <ul>
                    <li><a href="#">Concorsi attivi</a></li>
                    <li><a href="#">Avvisi</a></li>
                    <li><a href="#">Esiti</a></li>
                    <!-- <li><a href="#" data-megamenu-class="u-text-h4">altro
            <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a></li> -->
                </ul>
            </li>
            <li><a href="#">Personale <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a>
                <ul>
                    <li><a href="#">Incarichi amministrativi di vertice</a></li>
                    <li><a href="#">Dirigenti</a></li>
                    <!-- <li><a href="#" data-megamenu-class="u-text-h4">altro
            <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a></li> -->
                </ul>
            </li>
            <li><a href="#">Bandi di gara e contratti <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a>
                <ul>
                    <li><a href="#">Gare e procedure in corso</a></li>
                    <li><a href="#">Avvisi di aggiudicazione, esiti e affidamenti</a></li>
                    <li><a href="#">Avvisi pubblici</a></li>
                    <li><a href="#">Delibere e Determine a contrarre</a></li>
                    <!-- <li><a href="#" data-megamenu-class="u-text-h4">altro
            <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a></li> -->
                </ul>
            </li>
            <li><a href="#">Altre risorse <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a>
                <ul>
                    <li><a href="#">Enti controllati</a></li>
                    <li><a href="#">Provvedimenti</a></li>
                    <li><a href="#">Bilanci</a></li>
                    <!-- <li><a href="#" data-megamenu-class="u-text-h4">altro
            <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a></li> -->
                </ul>
            </li>
        </ul>
    </li>
    <li><a href="#">Vivere il Comune</a>
        <ul>
            <li><a href="#">Informazioni utili</a>
                <ul>
                    <li><a href="#">Associazioni</a></li>
                    <li><a href="#">Cenni storici</a></li>
                    <li><a href="#">Come raggiungerci</a></li>
                    <li><a href="#">Numeri per Emergenze</a></li>
                    <li><a href="#">Scheda informativa</a></li>
                </ul>
            </li>
            <li><a href="#">Turismo e punti di interesse</a>
                <ul>
                    <li><a href="#">Cosa visitare</a></li>
                    <li><a href="#">Itinerari</a></li>
                    <li><a href="#">Mappa dei punti di interesse</a></li>
                    <li><a href="#">Paesaggi e Immagini</a></li>
                </ul>
            </li>
            <li><a href="#">Sul territorio</a>
                <ul>
                    <li><a href="#">Dove dormire</a></li>
                    <li><a href="#">Dove mangiare</a></li>
                    <li><a href="#">Mercati</a></li>
                    <li><a href="#">Sanità e farmacie</a></li>
                    <li><a href="#">Scuole / Asili</a></li>
                    <li><a href="#">Servizi commerciali</a></li>
                    <li><a href="#">Stabilimenti balneari e termali</a></li>
                </ul>
            </li>
        </ul>
    </li>
    <li><a href="#">Altro</a>
        <ul>
            <li><a href="#">Siti tematici</a>
                <ul>
                    <li><a href="#">sito tematico</a></li>
                    <li><a href="#">sito tematico</a></li>
                    <li><a href="#">sito tematico</a></li>
                    <li><a href="#">sito tematico</a></li>
                    <li><a href="#">sito tematico</a></li>
                </ul>
            </li>
            <li><a href="#">Servizi per</a>
                <ul data-megamenu-class="u-lg-columns-2">
                    <li><a href="#">Anziani</a></li>
                    <li><a href="#">Automobilisti</a></li>
                    <li><a href="#">Bambini</a></li>
                    <li><a href="#">Cittadini</a></li>
                    <li><a href="#">Consumatori</a></li>
                    <li><a href="#">Disabili</a></li>
                    <li><a href="#">Donne</a></li>
                    <li><a href="#">Elettori</a></li>
                    <li><a href="#">Famiglie</a></li>
                    <li><a href="#">Giovani</a></li>
                    <li><a href="#">Immigrati</a></li>
                    <li><a href="#">Imprenditori</a></li>
                    <li><a href="#">Italiani all'estero</a></li>
                    <li><a href="#">Lavoratori / In cerca di lavoro</a></li>
                    <li><a href="#">Persone con disagio sociale</a></li>
                    <li><a href="#">Studenti</a></li>
                    <li><a href="#">Turisti</a></li>
                </ul>
            </li>
            <!-- <li><a href="#">news</a></li>
      <li><a href="#">eventi</a></li> -->
            <li><a href="#" data-megamenu-class="u-text-h4">mappa del sito
        <span data-megamenu-class="Icon Icon-chevron-right u-text-r-xxs"></span></a>
            </li>
        </ul>
    </li>
    <li data-megamenu-class="u-hidden">
        <a class="Button u-border-none u-color-95 u-background-compl u-text-r-xxs" href="#">Accedi</a>
    </li>
</ul>
  • Content:
    /** @define Treeview; weak; */
    
    :root {
      --Treeview-link-minWidth: 4em;
      --Treeview-link-maxWidth: 6em;
      --Treeview-link-padding: 2em;
      --Treeview-link-handler-width: 15%;
      --Treeview-link-arrow-width: 1em;
      --Treeview-link-arrow-down: inline("icons/img/SVG/expand.svg");
      --Treeview-link-arrow-up: inline("icons/img/SVG/collapse.svg");
    }
    
    .Treeview-handler--default::after,
    .Treeview-handler--standalone {
      background-image: var(--Treeview-link-arrow-down);
      background-repeat: no-repeat;
      background-size: var(--Treeview-link-arrow-width);
    }
    
    .Treeview-handler--default::after,
    .Treeview-handler--standalone {
      @extend .u-zindex-30;
      @extend .u-posAbsolute;
      @extend .u-block;
      @extend .u-text-r-xs;
    
      background-position: center;
      content: "";
      height: 100%;
      max-width: var(--Treeview-link-maxWidth);
      min-width: var(--Treeview-link-minWidth);
      right: 0;
      top: 0;
      width: var(--Treeview-link-handler-width);
    }
    
    .Treeview-handler--standalone {
      border-left: 1px solid var(--Color-grey-30);
    }
    
    .Treeview [aria-expanded="true"] > .Treeview-handler--default::after,
    .Treeview [aria-expanded="true"] > a > .Treeview-handler--standalone {
      background-image: var(--Treeview-link-arrow-up);
    }
    
    .Treeview [aria-hidden="true"] {
      display: none;
    }
    
    .Treeview li {
      cursor: pointer;
    }
    
    .Treeview-parent > a {
      @extend .u-posRelative;
    
      padding-right: calc(var(--Treeview-link-padding) * 2);
    }
    
    /*
     * Treeview default style
     * -----------------------
     */
    
    .Treeview--default li[aria-expanded=true] li a,
    .Treeview--default li[aria-expanded=true] li a:hover {
      @extend .u-color-95;
      @extend .u-background-grey-20;
    }
    
    .Treeview--default li[aria-expanded=true] li li a,
    .Treeview--default li[aria-expanded=true] li li a:hover {
      @extend .u-color-95;
      @extend .u-background-grey-30;
    }
    
  • URL: /components/raw/treeview/index.css
  • Filesystem Path: src/components/treeview/index.css
  • Size: 1.8 KB
  • Content:
    import $ from 'jquery'
    
    /*
     *	TODO:
     *		- repack as a frend _component and CSS
     *		- refactor without jQuery
     */
    
    /*
     * Porting of http://www.oaa-accessibility.org/examplep/treeview1/
     */
    const Treeview = function({
      selector: selector = '.js-Treeview',
      classFocused: classFocused = 'hasFocus',
      classParent: classParent = 'Treeview-parent',
      classMenuHandler: classMenuHandler = 'js-Treeview-handler',
      styleMenuHandler: styleMenuHandler = 'Treeview-handler--default',
      styleMenuHandlerStandalone: styleMenuHandlerStandalone = 'Treeview-handler--standalone',
      ariaLabelHandler: ariaLabelHandler = 'expand',
      multiselectable: multiselectable = true,
      animationMs: animationMs = 100,
    } = {}) {
    
      const keys = {
        tab: 9,
        enter: 13,
        space: 32,
        pageup: 33,
        pagedown: 34,
        end: 35,
        home: 36,
        left: 37,
        up: 38,
        right: 39,
        down: 40,
        asterisk: 106
      }
    
      function _collapseAll(treeview) {
        treeview.$parents.each(function() {
          if ($(this).attr('aria-expanded') == 'false') {
            $(this).children('ul').attr('aria-hidden', 'true')
          }
        })
        treeview.$visibleItems = treeview.$el.find('li:visible')
      }
    
      function _expandGroup(treeview, $item) {
        let $group = $item.children('ul')
        $group.slideDown(animationMs, () => {
          $group.attr('aria-hidden', 'false')
          $item.attr('aria-expanded', 'true')
          treeview.$visibleItems = treeview.$el.find('li:visible')
        })
      }
    
      function _collapseGroup(treeview, $item) {
        let $group = $item.children('ul')
        $group.slideUp(animationMs, () => {
          $group.attr('aria-hidden', 'true')
          $item.attr('aria-expanded', 'false')
          treeview.$visibleItems = treeview.$el.find('li:visible')
        })
      }
    
      function _collapseSiblings(treeview, $item) {
        $item.closest('ul')
          .find('> .' + classParent)
          .not($item)
          .each((i, el) => {
            _collapseGroup(treeview, $(el))
          })
      }
    
      function _toggleGroup(treeview, $item) {
        if (!multiselectable) {
          _collapseSiblings(treeview, $item)
        }
        if ($item.attr('aria-expanded') == 'true') {
          _collapseGroup(treeview, $item)
        } else {
          _expandGroup(treeview, $item)
        }
      }
    
      function _updateStyling(treeview, $item) {
        treeview.$items.removeClass(classFocused)
        $item.addClass(classFocused)
      }
    
      function _handleKeyDown(treeview, $item, e) {
        let curNdx = treeview.$visibleItems.index($item)
    
        if ((e.altKey || e.ctrlKey) ||
          (e.shiftKey && e.keyCode != keys.tab)) {
          return true
        }
    
        // if (!$(e.currentTarget).is('.' + classMenuHandler)) {
        //   return true
        // }
    
        switch (e.keyCode) {
          case keys.tab:
            {
              treeview.$activeItem = null
              $item.removeClass(classFocused)
              return true
            }
    
          case keys.home:
            {
              treeview.$activeItem = treeview.$parents.first()
              treeview.$activeItem.find(':focusable:first').focus()
              e.stopPropagation()
              return false
            }
    
          case keys.end:
            {
              treeview.$activeItem = treeview.$visibleItems.last()
              treeview.$activeItem.find(':focusable:first').focus()
              e.stopPropagation()
              return false
            }
    
          case keys.enter:
          case keys.space:
            {
              if ($(e.currentTarget).is('.' + classMenuHandler)) {
                _toggleGroup(treeview, $item)
                e.stopPropagation()
                return false
              }
              return true
            }
    
          case keys.left:
            {
              if ($item.is('.' + classParent) && $item.attr('aria-expanded') == 'true') {
                _collapseGroup(treeview, $item)
              } else {
                let $itemUL = $item.parent()
                let $itemParent = $itemUL.parent()
                treeview.$activeItem = $itemParent
                treeview.$activeItem.find(':focusable:first').focus()
              }
              e.stopPropagation()
              return false
            }
    
          case keys.right:
            {
              if (!$item.is('.' + classParent)) {
                // do nothing
              } else if ($item.attr('aria-expanded') == 'false') {
                _expandGroup(treeview, $item)
              } else {
                treeview.$activeItem = $item.children('ul').children('li').first()
                treeview.$activeItem.find(':focusable:first').focus()
              }
              e.stopPropagation()
              return false
            }
    
          case keys.up:
            {
              if (curNdx > 0) {
                let $prev = treeview.$visibleItems.eq(curNdx - 1)
                treeview.$activeItem = $prev
                $prev.find(':focusable:first').focus()
              }
              e.stopPropagation()
              return false
            }
    
          case keys.down:
            {
              if (curNdx < treeview.$visibleItems.length - 1) {
                let $next = treeview.$visibleItems.eq(curNdx + 1)
                treeview.$activeItem = $next
                $next.find(':focusable:first').focus()
              }
              e.stopPropagation()
              return false
            }
    
          case keys.asterisk:
            {
              treeview.$parents.each(function() {
                _expandGroup(treeview, $(this))
              })
              e.stopPropagation()
              return false
            }
    
        }
        return true
      }
    
      function _handleKeyPress(treeview, $item, e) {
        if (e.altKey || e.ctrlKey || e.shiftKey) {
          // do nothing
          return true
        }
    
        switch (e.keyCode) {
          case keys.tab:
            {
              return true
            }
          case keys.enter:
          case keys.home:
          case keys.end:
          case keys.left:
          case keys.right:
          case keys.up:
          case keys.down:
            {
              e.stopPropagation()
              return false
            }
          default:
            {
              let chr = String.fromCharCode(e.which)
              let bMatch = false
              let itemNdx = treeview.$visibleItems.index($item)
              let itemCnt = treeview.$visibleItems.length
              let curNdx = itemNdx + 1
    
              // check if the active item was the last one on the list
              if (curNdx == itemCnt) {
                curNdx = 0
              }
    
              // Iterate through the menu items (starting from the current item and wrapping) until a match is found
              // or the loop returns to the current menu item
              while (curNdx != itemNdx) {
    
                let $curItem = treeview.$visibleItems.eq(curNdx)
                let titleChr = $curItem.text().charAt(0)
    
                if ($curItem.is('.' + classParent)) {
                  titleChr = $curItem.find('span').text().charAt(0)
                }
    
                if (titleChr.toLowerCase() == chr) {
                  bMatch = true
                  break
                }
    
                curNdx = curNdx + 1
    
                if (curNdx == itemCnt) {
                  // reached the end of the list, start again at the beginning
                  curNdx = 0
                }
              }
    
              if (bMatch == true) {
                treeview.$activeItem = treeview.$visibleItems.eq(curNdx)
                treeview.$activeItem.find(':focusable:first').focus()
              }
    
              e.stopPropagation()
              return false
            }
        }
    
        return true
      }
    
      function _handleClick(treeview, $item, e) {
        if (e.altKey || e.ctrlKey || e.shiftKey) {
          // do nothing
          return true
        }
    
        // closest('li')
        const $parent = $item.closest('li')
    
        treeview.$activeItem = $parent
        _updateStyling(treeview, $parent)
        _toggleGroup(treeview, $parent)
    
        e.stopPropagation()
        return false
      }
    
      function _bindEvents(treeview) {
        treeview.$handlers.click(function(e) {
          return _handleClick(treeview, $(this), e)
        })
    
        treeview.$items.keydown(function(e) {
          return _handleKeyDown(treeview, $(this), e)
        })
    
        treeview.$items.keypress(function(e) {
          return _handleKeyPress(treeview, $(this), e)
        })
    
        treeview.$handlers.keydown(function(e) {
          return _handleKeyDown(treeview, $(this).closest('li'), e)
        })
    
        treeview.$handlers.keypress(function(e) {
          return _handleKeyPress(treeview, $(this).closest('li'), e)
        })
    
        $(document).click(function() {
          if (treeview.$activeItem != null) {
            treeview.$activeItem.removeClass(classFocused)
            treeview.$activeItem = null
          }
          return true
        })
      }
    
      function destroy() {
        /* TODO */
      }
    
      function _addA11y($el) {
        $el.attr('role', 'tree')
    
        // Put role="treeitem" on every LI
        // Put aria-expanded="false" on every LI (if it has no aria-expanded attr)
        // Put tabindex="-1" on every LI (if it's not the first one)
        // Put class=<classParent> on every LI that contains an UL
        $el.find('li').each(function(i, li) {
          const $li = $(li)
          $li
            .attr('role', 'treeitem')
          // .attr('tabindex', (0 === i) ? '0' : '-1')
          //  .find('a[href]').not('[href^=#]').attr('tabindex', 0)
          //  .parent().attr('aria-label', function() { return $(this).text() })
          if ($li.find('ul').length !== 0) {
    
            $li.children('a').not('[href=#]')
              .append(`<span class="${classMenuHandler} ${styleMenuHandlerStandalone}"
                  aria-label="${ariaLabelHandler}" role="button" tabindex="0"></span>`)
    
            $li.children("a[href='#']")
              .addClass(classMenuHandler)
              .addClass(styleMenuHandler)
              .attr('aria-label', ariaLabelHandler)
              .attr('role', 'button')
    
            const containsExpandedLink =
              $li.find('[aria-expanded=true]').length > 0
              || $li.find('.is-current').length > 0
    
            if (!li.hasAttribute('aria-expanded') && !containsExpandedLink) {
              $li.attr('aria-expanded', 'false')
            }
            else if (containsExpandedLink) {
              $li.attr('aria-expanded', 'true')
            }
            $li.addClass(classParent)
          }
        })
        // Put role="group" on every contained UL
        $el.find('ul').attr('role', 'group')
      }
    
      function init() {
        $(selector).each((_, treeviewContainer) => {
          const $el = $(treeviewContainer)
          _addA11y($el)
          let treeview = {
            $el: $el,
            $items: $el.find('li'),
            $parents: $el.find('.' + classParent),
            $handlers: $el.find('.' + classMenuHandler),
            $visibleItems: null,
            $activeItem: null
          }
          _collapseAll(treeview)
          _bindEvents(treeview)
        })
      }
    
      init()
    
      // REVEAL API
      return {
        init,
        destroy
      }
    
    }
    
    new Treeview()
    
    export default {
      Treeview
    }
    
  • URL: /components/raw/treeview/index.js
  • Filesystem Path: src/components/treeview/index.js
  • Size: 10.4 KB

There are no notes for this item.