@ilo-org/twig 1.7.3 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/accordion/accordion-item.twig +2 -1
- package/dist/components/contextmenu/contextmenu.component.yml +8 -0
- package/dist/components/contextmenu/contextmenu.twig +6 -4
- package/dist/components/contextmenu/contextmenu.wingsuit.yml +8 -0
- package/dist/components/languagetoggle/languagetoggle.behavior.js +5 -1
- package/dist/components/languagetoggle/languagetoggle.component.yml +5 -0
- package/dist/components/languagetoggle/languagetoggle.twig +12 -9
- package/dist/components/languagetoggle/languagetoggle.wingsuit.yml +5 -0
- package/dist/components/nav/desktop/navdropdown.twig +15 -0
- package/dist/components/nav/desktop/navmenu.twig +27 -0
- package/dist/components/nav/desktop/navmenugrid.twig +16 -0
- package/dist/components/nav/mobile/mobiledrawer_layout.twig +26 -0
- package/dist/components/nav/mobile/mobiledrawer_main.twig +51 -0
- package/dist/components/nav/mobile/mobiledrawer_nested.twig +25 -0
- package/dist/components/nav/mobile/mobilemenulist.twig +20 -0
- package/dist/components/nav/nav.behavior.js +16 -0
- package/dist/components/nav/nav.component.yml +105 -0
- package/dist/components/nav/nav.twig +5 -0
- package/dist/components/nav/nav.wingsuit.yml +105 -0
- package/dist/components/nav/nav_compact.twig +102 -0
- package/dist/components/nav/nav_complex.twig +125 -0
- package/dist/components/radio/radio.twig +0 -1
- package/dist/styles/components/languagetoggle.css +1 -1
- package/dist/styles/components/scorecard.css +1 -1
- package/dist/styles/global.css.map +1 -1
- package/dist/styles/index.css +1 -1
- package/dist/styles/index.css.map +1 -1
- package/dist/styles/monorepo.css +1 -1
- package/dist/styles/monorepo.css.map +1 -1
- package/package.json +3 -3
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
{% set id = id %}
|
|
5
5
|
{% set button_id = 'button-' ~ id %}
|
|
6
6
|
{% set panel_id = 'panel-' ~ id %}
|
|
7
|
+
{% set defaultExpanded = defaultExpanded|default(false) %}
|
|
7
8
|
{% set expanded_class = defaultExpanded ? 'ilo' ~ '--accordion--panel__open' : '' %}
|
|
8
9
|
{% set scroll_class = scroll ? 'ilo' ~ '--accordion--panel__scroll' : '' %}
|
|
9
10
|
|
|
10
11
|
<li class="ilo--accordion--item" id="{{ id }}">
|
|
11
12
|
<div class="ilo--h3">
|
|
12
|
-
<button class="ilo--accordion--button ilo--accordion--button__{{ size|default('small') }}" aria-expanded="{{ defaultExpanded }}" aria-controls="{{ panel_id }}" id="{{ button_id }}">
|
|
13
|
+
<button class="ilo--accordion--button ilo--accordion--button__{{ size|default('small') }}" aria-expanded="{{ defaultExpanded ? 'true' : 'false' }}" aria-controls="{{ panel_id }}" id="{{ button_id }}">
|
|
13
14
|
<span class="ilo--accordion--label">{{label}}</span>
|
|
14
15
|
<span class="ilo--accordion--icon"></span>
|
|
15
16
|
</button>
|
|
@@ -21,4 +21,12 @@ contextmenu:
|
|
|
21
21
|
url: "http://www.google.com"
|
|
22
22
|
- label: Link Five Is Slightly Longer
|
|
23
23
|
url: "http://www.google.com"
|
|
24
|
+
className:
|
|
25
|
+
type: string
|
|
26
|
+
label: className
|
|
27
|
+
description: Additional CSS class name(s) to add to the context menu
|
|
28
|
+
contextMenuId:
|
|
29
|
+
type: string
|
|
30
|
+
label: contextMenuId
|
|
31
|
+
description: Optional HTML ID attribute for the context menu element
|
|
24
32
|
visibility: storybook
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
{#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
{# contextmenu.twig #}
|
|
2
|
+
|
|
3
|
+
{% set className = className|default("") %}
|
|
4
|
+
{% set contextMenuId = contextMenuId|default("") %}
|
|
5
|
+
|
|
6
|
+
<ol role="menu" class="ilo--context-menu {{className}}" id="{{contextMenuId}}">
|
|
5
7
|
{% for link in links %}
|
|
6
8
|
<li class="ilo--context-menu--item{{ link.endsection|boolval ? ' endsection'}}">
|
|
7
9
|
<a href="{{link.url}}" class="ilo--context-menu--link">
|
|
@@ -21,4 +21,12 @@ contextmenu:
|
|
|
21
21
|
url: "http://www.google.com"
|
|
22
22
|
- label: Link Five Is Slightly Longer
|
|
23
23
|
url: "http://www.google.com"
|
|
24
|
+
className:
|
|
25
|
+
type: string
|
|
26
|
+
label: className
|
|
27
|
+
description: Additional CSS class name(s) to add to the context menu
|
|
28
|
+
contextMenuId:
|
|
29
|
+
type: string
|
|
30
|
+
label: contextMenuId
|
|
31
|
+
description: Optional HTML ID attribute for the context menu element
|
|
24
32
|
visibility: storybook
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
function createFocusTrap(event,focusableElements,escapeCallBack,tabCallBack){if(escapeCallBack===void 0)escapeCallBack=event=>{};if(tabCallBack===void 0)tabCallBack=event=>{};const firstFocusableElement=focusableElements[0];const lastFocusableElement=focusableElements[focusableElements.length-1];if(event.key==="Escape"){escapeCallBack(event);}if(event.key==="Tab"){if(event.shiftKey){if(document.activeElement===firstFocusableElement){event.preventDefault();lastFocusableElement.focus();tabCallBack(event);}}else if(document.activeElement===lastFocusableElement){event.preventDefault();firstFocusableElement.focus();tabCallBack(event);}}}
|
|
5
|
+
|
|
6
|
+
class StatefulComponent{constructor(element,initialState={}){this.element=element;this.initialState=initialState;this.state=this.#setupState(this.initialState);this.stateHandlers=new Map;this.#init();}#init(){this.setupState=this.#setupState.bind(this);this.registerStateHandler=this.registerStateHandler.bind(this);}registerStateHandler(prop,handler){if(!this.stateHandlers.has(prop)){this.stateHandlers.set(prop,[]);}this.stateHandlers.get(prop).push(handler);}#setupState(initialState){if(initialState===void 0)initialState={};return new Proxy(initialState,{set:(target,prop,value)=>{if(value!==target[prop]){target[prop]=value;const handlers=this.stateHandlers.get(prop);if(handlers){handlers.forEach(handler=>handler(value,prop));}}return true}})}}
|
|
7
|
+
|
|
8
|
+
class LanguageToggle extends StatefulComponent{constructor(element){const initialState={contextMenuIsOpen:false};super(element,initialState);this.init=()=>{this.cacheDomReferences().enableHandlers().registerStateHandlers();return this};this.cacheDomReferences=()=>{this.contextMenuTemplate=this.element.querySelector(`#${this.prefix}--context-menu--template`);this.contextButton=this.element.querySelector(`.${this.prefix}--container`);return this};this.registerStateHandlers=()=>{this.registerStateHandler("contextMenuIsOpen",value=>{if(value){this.handleOpenContextMenu();}else {this.handleCloseContextMenu();}});return this};this.enableHandlers=()=>{this.contextButton.addEventListener("click",this.handleClick);return this};this.handleClick=e=>{e.stopPropagation();this.state.contextMenuIsOpen=!this.state.contextMenuIsOpen;return this};this.handleOpenContextMenu=()=>{this.contextMenuContent=this.contextMenuTemplate.content.cloneNode(true);document.body.appendChild(this.contextMenuContent);this.contextMenu=document.body.querySelector(`.${this.prefix}--context-menu`);this.positionContextMenu();this.contextMenu.classList.add(this.contextMenuVisibleClass);this.contextButton.setAttribute("aria-expanded","true");window.addEventListener("click",this.handleOutsideClick);window.addEventListener("resize",this.positionContextMenu);this.handleTabNavigation();return this};this.handleCloseContextMenu=()=>{this.contextMenu.classList.remove(this.contextMenuVisibleClass);this.contextMenu.remove();this.contextButton.setAttribute("aria-expanded","false");this.contextMenu.removeEventListener("keydown",this.handleFocusTrap);window.removeEventListener("click",this.handleOutsideClick);window.removeEventListener("resize",this.positionContextMenu);};this.positionContextMenu=()=>{const containerRect=this.element.getBoundingClientRect();const contextMenuRect=this.contextMenu.getBoundingClientRect();this.contextMenu.style.left=`${containerRect.left+(containerRect.width-contextMenuRect.width)/2}px`;this.contextMenu.style.top=`${containerRect.bottom}px`;};this.handleOutsideClick=event=>{if(this.state.contextMenuIsOpen&&!this.element?.contains(event.target)&&!this.contextMenu?.contains(event.target)){this.state.contextMenuIsOpen=false;}};this.handleFocusTrap=event=>{const focusableElements=Array.from(this.contextMenu.querySelectorAll("a"));createFocusTrap(event,focusableElements,()=>{this.state.contextMenuIsOpen=false;this.contextButton.focus();});};this.handleTabNavigation=()=>{setTimeout(()=>{this.contextMenu.focus();this.contextMenu.addEventListener("keydown",this.handleFocusTrap);},100);};this.prefix=`${this.element.dataset.prefix}--language-toggle`;this.contextMenuVisibleClass=`${this.prefix}--context-menu__open`;this.contextMenuTemplate=null;this.contextMenuContent=null;this.contextMenu=null;this.init();}}
|
|
5
9
|
|
|
6
10
|
Drupal.behaviors.languagetoggle={attach(){Array.prototype.forEach.call(document.querySelectorAll(`[data-loadcomponent="LanguageToggle"]`),element=>{if(!element.dataset.jsProcessed){new LanguageToggle(element);element.dataset.jsProcessed=true;}});}};
|
|
7
11
|
|
|
@@ -4,6 +4,11 @@ languagetoggle:
|
|
|
4
4
|
label: Language Toggle
|
|
5
5
|
description: The Language Toggle allows users to switch between different language versions of a website.
|
|
6
6
|
fields:
|
|
7
|
+
className:
|
|
8
|
+
type: string
|
|
9
|
+
label: Class Name
|
|
10
|
+
description: Optional additional CSS class to be added to the component
|
|
11
|
+
preview: "custom-class"
|
|
7
12
|
language:
|
|
8
13
|
label: Language
|
|
9
14
|
type: string
|
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
{% set hideicon = hideicon|default(false) %}
|
|
4
4
|
{% set theme = theme|default("light") %}
|
|
5
5
|
|
|
6
|
-
<div class="{{ prefix }}--language-toggle {{ prefix }}--language-toggle__theme__{{ theme }}" data-prefix="ilo" data-loadcomponent="LanguageToggle">
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
<div class="{{ prefix }}--language-toggle {{ prefix }}--language-toggle__theme__{{ theme }} {{ className|default('') }}" data-prefix="ilo" data-loadcomponent="LanguageToggle">
|
|
7
|
+
<button class="{{ prefix }}--language-toggle--container" aria-expanded="false" aria-controls="ilo--language-toggle--context-menu">
|
|
8
|
+
{% if not hideicon %}
|
|
9
|
+
<span class="{{ prefix }}--language-toggle--icon"></span>
|
|
10
|
+
{% endif %}
|
|
11
|
+
<span class="{{ prefix }}--language-toggle--action">{{ language }}</span>
|
|
12
|
+
<span class="{{ prefix }}--language-toggle--arrow"></span>
|
|
13
|
+
</button>
|
|
14
|
+
<template id="ilo--language-toggle--context-menu--template">
|
|
15
|
+
<div class="{{ prefix }}--language-toggle--context-menu" id="ilo--language-toggle--context-menu" tabindex="0">
|
|
15
16
|
{% if links %}
|
|
16
17
|
{% include "@components/contextmenu/contextmenu.twig" with {
|
|
17
18
|
links: links,
|
|
@@ -19,4 +20,6 @@
|
|
|
19
20
|
} only %}
|
|
20
21
|
{% endif %}
|
|
21
22
|
</div>
|
|
23
|
+
</template>
|
|
22
24
|
</div>
|
|
25
|
+
|
|
@@ -4,6 +4,11 @@ languagetoggle:
|
|
|
4
4
|
label: Language Toggle
|
|
5
5
|
description: The Language Toggle allows users to switch between different language versions of a website.
|
|
6
6
|
fields:
|
|
7
|
+
className:
|
|
8
|
+
type: string
|
|
9
|
+
label: Class Name
|
|
10
|
+
description: Optional additional CSS class to be added to the component
|
|
11
|
+
preview: "custom-class"
|
|
7
12
|
language:
|
|
8
13
|
label: Language
|
|
9
14
|
type: string
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{# navdropdown.twig #}
|
|
2
|
+
|
|
3
|
+
{% set prefix = prefix|default("ilo") %}
|
|
4
|
+
{% set base_class = 'ilo' ~ "--nav-dropdown" %}
|
|
5
|
+
|
|
6
|
+
<template id="{{ prefix }}--nav-dropdown__template">
|
|
7
|
+
<div class="{{ base_class }} {{ className|default("") }}" id="{{ base_class }}" tabindex="0">
|
|
8
|
+
<div class="{{ base_class }}__container">
|
|
9
|
+
{% include "@components/nav/desktop/navmenugrid.twig" with {
|
|
10
|
+
menu: menu,
|
|
11
|
+
prefix: prefix
|
|
12
|
+
} only %}
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{# navmenu.twig #}
|
|
2
|
+
|
|
3
|
+
{% set prefix = prefix|default('ilo') %}
|
|
4
|
+
{% set base_class = 'ilo' ~ '--nav-menu' %}
|
|
5
|
+
|
|
6
|
+
<div class="{{ base_class }} {{ className }}">
|
|
7
|
+
<ul class="{{ base_class }}__list">
|
|
8
|
+
{% for item in menu %}
|
|
9
|
+
<li class="{{ base_class }}__item {{ item.className }}">
|
|
10
|
+
<a href="{{ item.href }}" class="{{ base_class }}__link">
|
|
11
|
+
{{ item.label }}
|
|
12
|
+
</a>
|
|
13
|
+
</li>
|
|
14
|
+
{% endfor %}
|
|
15
|
+
</ul>
|
|
16
|
+
{% if more %}
|
|
17
|
+
<button
|
|
18
|
+
class="{{ base_class }}__more"
|
|
19
|
+
aria-expanded="false"
|
|
20
|
+
aria-haspopup="menu"
|
|
21
|
+
aria-controls="{{ prefix }}--nav-dropdown"
|
|
22
|
+
id="{{ prefix }}--nav-dropdown__button">
|
|
23
|
+
{{ more.label }}
|
|
24
|
+
<span class="{{ base_class }}__more-icon"></span>
|
|
25
|
+
</button>
|
|
26
|
+
{% endif %}
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{# navmenugrid.twig #}
|
|
2
|
+
|
|
3
|
+
{% set prefix = prefix|default('ilo') %}
|
|
4
|
+
{% set base_class = 'ilo' ~ '--nav-menu-grid' %}
|
|
5
|
+
|
|
6
|
+
<div class="{{ base_class }}">
|
|
7
|
+
{% for chunk in menu|batch(5) %}
|
|
8
|
+
<div class="{{ base_class }}__column">
|
|
9
|
+
{% for item in chunk %}
|
|
10
|
+
<a href="{{ item.href }}" class="{{ base_class }}__item">
|
|
11
|
+
{{ item.label }}
|
|
12
|
+
</a>
|
|
13
|
+
{% endfor %}
|
|
14
|
+
</div>
|
|
15
|
+
{% endfor %}
|
|
16
|
+
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{% set prefix = prefix|default("ilo") %}
|
|
2
|
+
{% set base_class = 'ilo' ~ "--nav-mobile-drawer" %}
|
|
3
|
+
{% set mobile_class = 'ilo' ~ "--nav-mobile" %}
|
|
4
|
+
{% set id = id|default("primary") %}
|
|
5
|
+
{% set className = className|default("") %}
|
|
6
|
+
|
|
7
|
+
<template id="{{ base_class }}__template__{{ id }}">
|
|
8
|
+
<div class="{{ base_class }} {{ className }}" id="{{ base_class }}__{{ id }}" inert>
|
|
9
|
+
<div class="{{ base_class }}__header">
|
|
10
|
+
<div class="{{ base_class }}__header-main">
|
|
11
|
+
{{ block("mobile_drawer_header") }}
|
|
12
|
+
</div>
|
|
13
|
+
<button class="{{ base_class }}__header-close" aria-label="Close navigation">
|
|
14
|
+
<span class="{{ base_class }}__header-close__icon"></span>
|
|
15
|
+
</button>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="{{ base_class }}__container">
|
|
18
|
+
<div class="{{ base_class }}__widgets">
|
|
19
|
+
<div class="{{ mobile_class}}__widgets">
|
|
20
|
+
{{ block("mobile_drawer_widgets") }}
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
{{ block("mobile_drawer_content") }}
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{% extends "@components/nav/mobile/mobiledrawer_layout.twig" %}
|
|
2
|
+
|
|
3
|
+
{% block mobile_drawer_header %}
|
|
4
|
+
<div class="{{ mobile_class }}__branding">
|
|
5
|
+
<div class="{{ mobile_class }}__logo">
|
|
6
|
+
<img src="{{ branding.logo.drawer }}" alt="{{ branding.logo.alt }}">
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
{% endblock %}
|
|
10
|
+
|
|
11
|
+
{% block mobile_drawer_widgets %}
|
|
12
|
+
{% if widgets.search %}
|
|
13
|
+
<a class="{{ mobile_class }}__widgets-search" href="{{ widgets.search.url }}" aria-label="{{ widgets.search.label }}">
|
|
14
|
+
<span class="{{ mobile_class }}__widgets-search__label">
|
|
15
|
+
{{ widgets.search.label }}
|
|
16
|
+
</span>
|
|
17
|
+
<span class="{{ mobile_class }}__widgets-search__icon" />
|
|
18
|
+
</a>
|
|
19
|
+
{% endif %}
|
|
20
|
+
|
|
21
|
+
{% if widgets.link %}
|
|
22
|
+
<a class="{{ mobile_class }}__widgets-link" href="{{ widgets.link.href }}">{{ widgets.link.label }}</a>
|
|
23
|
+
{% endif %}
|
|
24
|
+
|
|
25
|
+
{% if widgets.language %}
|
|
26
|
+
<button
|
|
27
|
+
class="{{ mobile_class }}__widgets-language"
|
|
28
|
+
aria-label="{{ widgets.language.label }}"
|
|
29
|
+
aria-expanded="false"
|
|
30
|
+
aria-haspopup="menu"
|
|
31
|
+
aria-controls="{{ prefix }}--nav-mobile-drawer__languages"
|
|
32
|
+
id="{{ prefix }}--nav-mobile-drawer__languages__button"
|
|
33
|
+
>
|
|
34
|
+
<span class="{{ mobile_class }}__widgets-language__label">
|
|
35
|
+
{{ widgets.language.label }}: {{ widgets.language.language }}
|
|
36
|
+
</span>
|
|
37
|
+
<span class="{{ mobile_class }}__widgets-language__icon" />
|
|
38
|
+
</button>
|
|
39
|
+
{% endif %}
|
|
40
|
+
{% endblock %}
|
|
41
|
+
|
|
42
|
+
{% block mobile_drawer_content %}
|
|
43
|
+
{% include "@components/nav/mobile/mobilemenulist.twig" with {
|
|
44
|
+
menu: menu,
|
|
45
|
+
prefix: prefix,
|
|
46
|
+
} only %}
|
|
47
|
+
|
|
48
|
+
{% if showMoreButton %}
|
|
49
|
+
<button class="{{ mobile_class }}__more" aria-expanded="false" aria-controls="more-items" aria-label="More items">More<span class="{{ mobile_class }}__more__icon"></span></button>
|
|
50
|
+
{% endif %}
|
|
51
|
+
{% endblock %}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{% extends "@components/nav/mobile/mobiledrawer_layout.twig" %}
|
|
2
|
+
{% set prefix = prefix|default("ilo") %}
|
|
3
|
+
{% set menu_home = menu_home|default("Menu home") %}
|
|
4
|
+
{% set menu_title = title|default("Title") %}
|
|
5
|
+
{% set isActiveLabel = isActiveLabel|default(null) %}
|
|
6
|
+
|
|
7
|
+
{% block mobile_drawer_header %}
|
|
8
|
+
<div class="{{ mobile_class}}-drawer__header-main">
|
|
9
|
+
<button class="{{ mobile_class}}__nested__header" aria-label="Back to menu">
|
|
10
|
+
<span class="{{ mobile_class}}__nested__header__icon"></span>{{ menu_home }}
|
|
11
|
+
</button>
|
|
12
|
+
</div>
|
|
13
|
+
{% endblock %}
|
|
14
|
+
|
|
15
|
+
{% block mobile_drawer_widgets %}
|
|
16
|
+
<span class="{{ mobile_class}}__nested__title">{{ menu_title }}</span>
|
|
17
|
+
{% endblock %}
|
|
18
|
+
|
|
19
|
+
{% block mobile_drawer_content %}
|
|
20
|
+
{% include "@components/nav/mobile/mobilemenulist.twig" with {
|
|
21
|
+
menu: menu,
|
|
22
|
+
prefix: prefix,
|
|
23
|
+
isActiveLabel: isActiveLabel,
|
|
24
|
+
} only %}
|
|
25
|
+
{% endblock %}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{# mobilemenulist.twig #}
|
|
2
|
+
|
|
3
|
+
{# List items can have url or href attribute #}
|
|
4
|
+
|
|
5
|
+
{% set prefix = prefix|default("ilo") %}
|
|
6
|
+
{% set base_class = 'ilo' ~ "--nav-mobile-menu" %}
|
|
7
|
+
{% set isActiveLabel = isActiveLabel|default(null) %}
|
|
8
|
+
|
|
9
|
+
<ul class="{{ base_class }}">
|
|
10
|
+
{% for item in menu %}
|
|
11
|
+
{% set url = item.url|default(item.href|default('')) %}
|
|
12
|
+
{% set isActive = isActiveLabel is not null ? isActiveLabel == item.label : item.isActive %}
|
|
13
|
+
<li class="{{ base_class }}__item {% if isActive %}{{ base_class }}__item--active{% endif %}">
|
|
14
|
+
<a href="{{ url }}" class="{{ base_class }}__link">
|
|
15
|
+
{{ item.label }}
|
|
16
|
+
</a>
|
|
17
|
+
{% if isActive %}<span class="{{ base_class }}__marked"></span>{% endif %}
|
|
18
|
+
</li>
|
|
19
|
+
{% endfor %}
|
|
20
|
+
</ul>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
function createFocusTrap(event,focusableElements,escapeCallBack,tabCallBack){if(escapeCallBack===void 0)escapeCallBack=event=>{};if(tabCallBack===void 0)tabCallBack=event=>{};const firstFocusableElement=focusableElements[0];const lastFocusableElement=focusableElements[focusableElements.length-1];if(event.key==="Escape"){escapeCallBack(event);}if(event.key==="Tab"){if(event.shiftKey){if(document.activeElement===firstFocusableElement){event.preventDefault();lastFocusableElement.focus();tabCallBack(event);}}else if(document.activeElement===lastFocusableElement){event.preventDefault();firstFocusableElement.focus();tabCallBack(event);}}}
|
|
5
|
+
|
|
6
|
+
const BREAKPOINTS={xs:320,sm:414,md:610,lg:1024,xl:1140,xxl:1168};function getCurrentBreakpoint(){const width=window.innerWidth;if(width<BREAKPOINTS.xs)return "xs";if(width<BREAKPOINTS.sm)return "sm";if(width<BREAKPOINTS.md)return "md";if(width<BREAKPOINTS.lg)return "lg";if(width<BREAKPOINTS.xl)return "xl";return "xxl"}function createBreakpointObserver(callback){let currentBreakpoint=getCurrentBreakpoint();let timeoutId=null;let observer=null;function checkBreakpoint(){timeoutId=window.requestAnimationFrame(()=>{const newBreakpoint=getCurrentBreakpoint();if(newBreakpoint!==currentBreakpoint){currentBreakpoint=newBreakpoint;callback(newBreakpoint);}});}function start(){observer=new ResizeObserver(()=>{checkBreakpoint();});observer.observe(document.body);callback(currentBreakpoint);}function stop(){if(observer){observer.disconnect();observer=null;}if(timeoutId){window.cancelAnimationFrame(timeoutId);timeoutId=null;}}return {start,stop,getCurrentBreakpoint:()=>currentBreakpoint}}
|
|
7
|
+
|
|
8
|
+
class StatefulComponent{constructor(element,initialState={}){this.element=element;this.initialState=initialState;this.state=this.#setupState(this.initialState);this.stateHandlers=new Map;this.#init();}#init(){this.setupState=this.#setupState.bind(this);this.registerStateHandler=this.registerStateHandler.bind(this);}registerStateHandler(prop,handler){if(!this.stateHandlers.has(prop)){this.stateHandlers.set(prop,[]);}this.stateHandlers.get(prop).push(handler);}#setupState(initialState){if(initialState===void 0)initialState={};return new Proxy(initialState,{set:(target,prop,value)=>{if(value!==target[prop]){target[prop]=value;const handlers=this.stateHandlers.get(prop);if(handlers){handlers.forEach(handler=>handler(value,prop));}}return true}})}}
|
|
9
|
+
|
|
10
|
+
class Nav extends StatefulComponent{constructor(element){const initialState={dropDownIsOpen:false,isDesktop:true};super(element,initialState);this.startBreakpointObserver=()=>{this.breakpointObserver.start();return this};this.renderClientContent=()=>{const dropdownTemplate=this.element.querySelector(`#${this.prefix}--nav-dropdown__template`);if(dropdownTemplate){const dropdownContent=dropdownTemplate.content.cloneNode(true);document.body.appendChild(dropdownContent);}return this};this.cacheDomReferences=()=>{this.dropdownButton=this.element.querySelector(`.${this.prefix}--nav-menu__more`);this.dropdown=document.body.querySelector(`.${this.prefix}--nav-dropdown`);return this};this.enableHandlers=()=>{if(this.dropdownButton){this.dropdownButton.addEventListener("click",this.handleDropdownClick);}return this};this.registerStateHandlers=()=>{this.registerStateHandler("dropDownIsOpen",value=>{if(value){this.handleOpenDropdown();}else {this.handleCloseDropdown();}});this.registerStateHandler("isDesktop",this.handleBreakpointChange);return this};this.handleBreakpointChange=isDesktop=>{if(!isDesktop){this.state.dropDownIsOpen=false;}};this.handleOpenDropdown=()=>{this.handleResizeDropdown();this.resizeObserver.observe(this.element);window.addEventListener("click",this.handleOutsideClick);this.dropdown?.classList.add(`${this.prefix}--nav-dropdown--open`);this.dropdownButton.setAttribute("aria-expanded","true");this.dropdownButton?.classList.add(`${this.prefix}--nav-menu__more--open`);this.handleTabNavigation();};this.handleCloseDropdown=()=>{this.dropdown?.classList.remove(`${this.prefix}--nav-dropdown--open`);this.dropdownButton?.classList.remove(`${this.prefix}--nav-menu__more--open`);this.dropdownButton.setAttribute("aria-expanded","false");this.resizeObserver.disconnect();window.removeEventListener("click",this.handleOutsideClick);};this.handleResizeDropdown=()=>{this.dropdown.style.width=`${this.element.offsetWidth}px`;this.dropdown.style.left=`${this.element.getBoundingClientRect().left}px`;this.dropdown.style.top=`${this.element.getBoundingClientRect().bottom}px`;};this.handleDropdownClick=()=>{this.state.dropDownIsOpen=!this.state.dropDownIsOpen;};this.handleOutsideClick=event=>{if(this.state.dropDownIsOpen&&!this.element?.contains(event.target)&&!this.dropdown?.contains(event.target)){this.state.dropDownIsOpen=false;}};this.handleFocusTrap=event=>{createFocusTrap(event,this.dropdown.querySelectorAll("a"),()=>{this.state.dropDownIsOpen=false;this.dropdownButton.focus();});};this.handleTabNavigation=()=>{setTimeout(()=>{this.dropdown.focus();this.dropdown.addEventListener("keydown",this.handleFocusTrap);},100);};this.prefix=this.element.dataset.prefix;this.dropdown=null;this.breakpointObserver=createBreakpointObserver(breakpoint=>{this.state.isDesktop=["xl","xxl"].includes(breakpoint);});this.resizeObserver=new ResizeObserver(()=>{if(this.state.dropDownIsOpen){this.handleResizeDropdown();}});this.init();}init(){this.renderClientContent().cacheDomReferences().enableHandlers().registerStateHandlers().startBreakpointObserver();return this}}
|
|
11
|
+
|
|
12
|
+
class MobileNav extends StatefulComponent{constructor(element){const initialState={isMobile:false,mobDrawerIsOpen:false,languagesMobDrawerIsOpen:false,moreMobDrawerIsOpen:false};super(element,initialState);this.init=()=>{this.renderClientContent().cacheDomReferences().enableHandlers().registerStateHandlers().startBreakpointObserver();return this};this.startBreakpointObserver=()=>{this.breakpointObserver.start();return this};this.renderClientContent=()=>{const mobileDrawerTemplates=[`${this.prefix}--nav-mobile-drawer__template__main`,`${this.prefix}--nav-mobile-drawer__template__languages`,`${this.prefix}--nav-mobile-drawer__template__more`];mobileDrawerTemplates.forEach(templateId=>{const template=this.element.querySelector(`#${templateId}`);if(template){const content=template.content.cloneNode(true);document.body.appendChild(content);}});return this};this.cacheDomReferences=()=>{this.burger=this.element.querySelector(`[class$="burger"]`);this.mobileDrawer=document.body.querySelector(`#${this.prefix}--nav-mobile-drawer__main`);this.menuHomeButtons=document.body.querySelectorAll(`.${this.prefix}--nav-mobile__nested__header`);this.mobileDrawerCloseButtons=document.body.querySelectorAll(`.${this.prefix}--nav-mobile-drawer__header-close`);this.mobileLanguageButton=this.mobileDrawer.querySelector(`.${this.prefix}--nav-mobile__widgets-language`);this.languagesMobileDrawer=document.body.querySelector(`#${this.prefix}--nav-mobile-drawer__languages`);this.moreMobileDrawer=document.body.querySelector(`#${this.prefix}--nav-mobile-drawer__more`);this.mobileMoreButton=this.mobileDrawer.querySelector(`.${this.prefix}--nav-mobile__more`);return this};this.enableHandlers=()=>{this.burger.addEventListener("click",this.handleBurgerClick);if(this.mobileLanguageButton){this.mobileLanguageButton.addEventListener("click",this.handleLanguageButtonClick);}if(this.mobileMoreButton){this.mobileMoreButton.addEventListener("click",this.handleMoreButtonClick);}this.mobileDrawerCloseButtons.forEach(button=>{button.addEventListener("click",this.handleMobileDrawerClose);});this.menuHomeButtons.forEach(button=>{button.addEventListener("click",this.handleMenuHomeButtonClick);});return this};this.registerStateHandlers=()=>{this.registerStateHandler("mobDrawerIsOpen",value=>{if(value){this.handleOpenMobileDrawer();}else {this.handleCloseMobileDrawer();}});this.registerStateHandler("languagesMobDrawerIsOpen",value=>{if(value){this.handleOpenLanguagesMobileDrawer();}else {this.handleCloseLanguagesMobileDrawer();}});this.registerStateHandler("moreMobDrawerIsOpen",value=>{if(value){this.handleOpenMoreMobileDrawer();}else {this.handleCloseMoreMobileDrawer();}});this.registerStateHandler("isMobile",this.handleBreakpointChange);return this};this.handleBurgerClick=()=>{this.burger.setAttribute("aria-expanded","true");this.state.mobDrawerIsOpen=true;};this.handleLanguageButtonClick=()=>{this.state.languagesMobDrawerIsOpen=true;};this.handleMoreButtonClick=()=>{this.state.moreMobDrawerIsOpen=true;};this.handleMenuHomeButtonClick=()=>{this.state.languagesMobDrawerIsOpen=false;this.state.moreMobDrawerIsOpen=false;};this.handleMobileDrawerClose=()=>{this.state.mobDrawerIsOpen=false;this.state.languagesMobDrawerIsOpen=false;this.state.moreMobDrawerIsOpen=false;};this.handleOpenMobileDrawer=()=>{this.mobileDrawer.inert=false;this.mobileDrawer?.classList.add(`${this.prefix}--nav-mobile-drawer--open`);};this.handleCloseMobileDrawer=()=>{this.mobileDrawer.inert=true;this.mobileDrawer?.classList.remove(`${this.prefix}--nav-mobile-drawer--open`);this.burger.setAttribute("aria-expanded","false");};this.handleOpenLanguagesMobileDrawer=()=>{this.languagesMobileDrawer.inert=false;this.mobileDrawer.inert=true;this.mobileLanguageButton.setAttribute("aria-expanded","true");this.languagesMobileDrawer.classList.add(`${this.prefix}--nav-mobile-drawer--open`);};this.handleCloseLanguagesMobileDrawer=()=>{this.languagesMobileDrawer.inert=true;this.mobileDrawer.inert=false;this.mobileLanguageButton.setAttribute("aria-expanded","false");this.languagesMobileDrawer?.classList.remove(`${this.prefix}--nav-mobile-drawer--open`);};this.handleOpenMoreMobileDrawer=()=>{this.moreMobileDrawer.inert=false;this.mobileDrawer.inert=true;this.mobileMoreButton.setAttribute("aria-expanded","true");this.moreMobileDrawer.classList.add(`${this.prefix}--nav-mobile-drawer--open`);};this.handleCloseMoreMobileDrawer=()=>{this.moreMobileDrawer.inert=true;this.mobileDrawer.inert=false;this.mobileMoreButton.setAttribute("aria-expanded","false");this.moreMobileDrawer?.classList.remove(`${this.prefix}--nav-mobile-drawer--open`);};this.handleBreakpointChange=isMobile=>{if(!isMobile){this.state.mobDrawerIsOpen=false;this.state.languagesMobDrawerIsOpen=false;this.state.moreMobDrawerIsOpen=false;}};this.prefix=this.element.dataset.prefix;this.mobileDrawer=null;this.mobileDrawerClose=null;this.languagesMobileDrawer=null;this.languagesMobileDrawerClose=null;this.mobileLanguageButton=null;this.burger=null;this.breakpointObserver=createBreakpointObserver(breakpoint=>{this.state.isMobile=!["xl","xxl"].includes(breakpoint);});this.init();}}
|
|
13
|
+
|
|
14
|
+
Drupal.behaviors.nav={attach(){Array.prototype.forEach.call(document.querySelectorAll(`[data-loadcomponent="Nav"]`),element=>{if(!element.dataset.jsProcessed){new Nav(element);new MobileNav(element);element.dataset.jsProcessed=true;}});}};
|
|
15
|
+
|
|
16
|
+
})();
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
nav:
|
|
2
|
+
namespace: Components/Nav
|
|
3
|
+
use: "@components/nav/nav.twig"
|
|
4
|
+
label: SubsiteNav
|
|
5
|
+
description: A complex navigation component with branding, widgets, and menu sections
|
|
6
|
+
fields:
|
|
7
|
+
branding:
|
|
8
|
+
type: object
|
|
9
|
+
label: Branding
|
|
10
|
+
description: Branding elements including logo and name
|
|
11
|
+
required: true
|
|
12
|
+
preview:
|
|
13
|
+
logo:
|
|
14
|
+
main: "images/ilo-live-logo-en-dark.png"
|
|
15
|
+
mobile: "images/ilo-live-logo-en-light.png"
|
|
16
|
+
drawer: "images/ilo-live-logo-en-dark.png"
|
|
17
|
+
alt: "ILO Logo"
|
|
18
|
+
tag:
|
|
19
|
+
main: "Advancing social justice, promoting decent work"
|
|
20
|
+
sub: "ILO is a specialized agency of the United Nations"
|
|
21
|
+
widgets:
|
|
22
|
+
type: object
|
|
23
|
+
label: Widgets
|
|
24
|
+
description: Widgets section including link and language toggle
|
|
25
|
+
preview:
|
|
26
|
+
link:
|
|
27
|
+
href: "https://www.ilo.org"
|
|
28
|
+
label: "Go to main ILO website"
|
|
29
|
+
language:
|
|
30
|
+
label: "Language"
|
|
31
|
+
language: "English"
|
|
32
|
+
links:
|
|
33
|
+
- label: "English"
|
|
34
|
+
url: "https://www.ilo.org/en"
|
|
35
|
+
- label: "Français"
|
|
36
|
+
url: "https://www.ilo.org/fr"
|
|
37
|
+
- label: "Español"
|
|
38
|
+
url: "https://www.ilo.org/es"
|
|
39
|
+
- label: "العربية"
|
|
40
|
+
url: "https://www.ilo.org/ar"
|
|
41
|
+
- label: "中文"
|
|
42
|
+
url: "https://www.ilo.org/ru"
|
|
43
|
+
- label: "Português"
|
|
44
|
+
url: "https://www.ilo.org/pt"
|
|
45
|
+
- label: "Italiano"
|
|
46
|
+
url: "https://www.ilo.org/it"
|
|
47
|
+
search:
|
|
48
|
+
label: "Search"
|
|
49
|
+
url: "https://www.ilo.org/search"
|
|
50
|
+
facadeItems:
|
|
51
|
+
type: object
|
|
52
|
+
label: Facade Menu Items
|
|
53
|
+
description: Primary menu items displayed in the main navigation
|
|
54
|
+
preview:
|
|
55
|
+
- href: "https://www.ilo.org/"
|
|
56
|
+
label: "Menu Item 1"
|
|
57
|
+
isActive: true
|
|
58
|
+
className: "test"
|
|
59
|
+
- href: "https://www.ilo.org/"
|
|
60
|
+
label: "Menu Item 2"
|
|
61
|
+
isActive: false
|
|
62
|
+
className: "test"
|
|
63
|
+
- href: "https://www.ilo.org/"
|
|
64
|
+
label: "Menu Item 3"
|
|
65
|
+
isActive: false
|
|
66
|
+
className: "test"
|
|
67
|
+
moreItems:
|
|
68
|
+
type: object
|
|
69
|
+
label: More Menu Items
|
|
70
|
+
description: Additional menu items displayed in the dropdown
|
|
71
|
+
preview:
|
|
72
|
+
- href: "https://www.ilo.org/more1"
|
|
73
|
+
label: "More Item 1"
|
|
74
|
+
- href: "https://www.ilo.org/more2"
|
|
75
|
+
label: "More Item 2"
|
|
76
|
+
- href: "https://www.ilo.org/more3"
|
|
77
|
+
label: "More Item 3"
|
|
78
|
+
- href: "https://www.ilo.org/more4"
|
|
79
|
+
label: "More Item 4"
|
|
80
|
+
- href: "https://www.ilo.org/more5"
|
|
81
|
+
label: "More Item 5"
|
|
82
|
+
- href: "https://www.ilo.org/more6"
|
|
83
|
+
label: "More Item 6"
|
|
84
|
+
- href: "https://www.ilo.org/more7"
|
|
85
|
+
label: "More Item 7"
|
|
86
|
+
- href: "https://www.ilo.org/more8"
|
|
87
|
+
label: "More Item 8"
|
|
88
|
+
labels:
|
|
89
|
+
type: object
|
|
90
|
+
label: Labels
|
|
91
|
+
description: Text labels used throughout the navigation
|
|
92
|
+
preview:
|
|
93
|
+
more: "More"
|
|
94
|
+
language: "Language"
|
|
95
|
+
home: "Menu home"
|
|
96
|
+
settings:
|
|
97
|
+
navtype:
|
|
98
|
+
label: Type
|
|
99
|
+
type: select
|
|
100
|
+
description: What kind of navigation component to render
|
|
101
|
+
options:
|
|
102
|
+
compact: compact
|
|
103
|
+
complex: complex
|
|
104
|
+
preview: "complex"
|
|
105
|
+
visibility: storybook
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
nav:
|
|
2
|
+
namespace: Components/Nav
|
|
3
|
+
use: "@components/nav/nav.twig"
|
|
4
|
+
label: SubsiteNav
|
|
5
|
+
description: A complex navigation component with branding, widgets, and menu sections
|
|
6
|
+
fields:
|
|
7
|
+
branding:
|
|
8
|
+
type: object
|
|
9
|
+
label: Branding
|
|
10
|
+
description: Branding elements including logo and name
|
|
11
|
+
required: true
|
|
12
|
+
preview:
|
|
13
|
+
logo:
|
|
14
|
+
main: "images/ilo-live-logo-en-dark.png"
|
|
15
|
+
mobile: "images/ilo-live-logo-en-light.png"
|
|
16
|
+
drawer: "images/ilo-live-logo-en-dark.png"
|
|
17
|
+
alt: "ILO Logo"
|
|
18
|
+
tag:
|
|
19
|
+
main: "Advancing social justice, promoting decent work"
|
|
20
|
+
sub: "ILO is a specialized agency of the United Nations"
|
|
21
|
+
widgets:
|
|
22
|
+
type: object
|
|
23
|
+
label: Widgets
|
|
24
|
+
description: Widgets section including link and language toggle
|
|
25
|
+
preview:
|
|
26
|
+
link:
|
|
27
|
+
href: "https://www.ilo.org"
|
|
28
|
+
label: "Go to main ILO website"
|
|
29
|
+
language:
|
|
30
|
+
label: "Language"
|
|
31
|
+
language: "English"
|
|
32
|
+
links:
|
|
33
|
+
- label: "English"
|
|
34
|
+
url: "https://www.ilo.org/en"
|
|
35
|
+
- label: "Français"
|
|
36
|
+
url: "https://www.ilo.org/fr"
|
|
37
|
+
- label: "Español"
|
|
38
|
+
url: "https://www.ilo.org/es"
|
|
39
|
+
- label: "العربية"
|
|
40
|
+
url: "https://www.ilo.org/ar"
|
|
41
|
+
- label: "中文"
|
|
42
|
+
url: "https://www.ilo.org/ru"
|
|
43
|
+
- label: "Português"
|
|
44
|
+
url: "https://www.ilo.org/pt"
|
|
45
|
+
- label: "Italiano"
|
|
46
|
+
url: "https://www.ilo.org/it"
|
|
47
|
+
search:
|
|
48
|
+
label: "Search"
|
|
49
|
+
url: "https://www.ilo.org/search"
|
|
50
|
+
facadeItems:
|
|
51
|
+
type: object
|
|
52
|
+
label: Facade Menu Items
|
|
53
|
+
description: Primary menu items displayed in the main navigation
|
|
54
|
+
preview:
|
|
55
|
+
- href: "https://www.ilo.org/"
|
|
56
|
+
label: "Menu Item 1"
|
|
57
|
+
isActive: true
|
|
58
|
+
className: "test"
|
|
59
|
+
- href: "https://www.ilo.org/"
|
|
60
|
+
label: "Menu Item 2"
|
|
61
|
+
isActive: false
|
|
62
|
+
className: "test"
|
|
63
|
+
- href: "https://www.ilo.org/"
|
|
64
|
+
label: "Menu Item 3"
|
|
65
|
+
isActive: false
|
|
66
|
+
className: "test"
|
|
67
|
+
moreItems:
|
|
68
|
+
type: object
|
|
69
|
+
label: More Menu Items
|
|
70
|
+
description: Additional menu items displayed in the dropdown
|
|
71
|
+
preview:
|
|
72
|
+
- href: "https://www.ilo.org/more1"
|
|
73
|
+
label: "More Item 1"
|
|
74
|
+
- href: "https://www.ilo.org/more2"
|
|
75
|
+
label: "More Item 2"
|
|
76
|
+
- href: "https://www.ilo.org/more3"
|
|
77
|
+
label: "More Item 3"
|
|
78
|
+
- href: "https://www.ilo.org/more4"
|
|
79
|
+
label: "More Item 4"
|
|
80
|
+
- href: "https://www.ilo.org/more5"
|
|
81
|
+
label: "More Item 5"
|
|
82
|
+
- href: "https://www.ilo.org/more6"
|
|
83
|
+
label: "More Item 6"
|
|
84
|
+
- href: "https://www.ilo.org/more7"
|
|
85
|
+
label: "More Item 7"
|
|
86
|
+
- href: "https://www.ilo.org/more8"
|
|
87
|
+
label: "More Item 8"
|
|
88
|
+
labels:
|
|
89
|
+
type: object
|
|
90
|
+
label: Labels
|
|
91
|
+
description: Text labels used throughout the navigation
|
|
92
|
+
preview:
|
|
93
|
+
more: "More"
|
|
94
|
+
language: "Language"
|
|
95
|
+
home: "Menu home"
|
|
96
|
+
settings:
|
|
97
|
+
navtype:
|
|
98
|
+
label: Type
|
|
99
|
+
type: select
|
|
100
|
+
description: What kind of navigation component to render
|
|
101
|
+
options:
|
|
102
|
+
compact: compact
|
|
103
|
+
complex: complex
|
|
104
|
+
preview: "complex"
|
|
105
|
+
visibility: storybook
|