Skip to content
Snippets Groups Projects
Commit 3a01aaa3 authored by druid's avatar druid
Browse files

merge dev into master

parents 12e9457b 43433a44
No related branches found
No related tags found
No related merge requests found
Showing
with 1519 additions and 140 deletions
......@@ -472,6 +472,26 @@ export namespace Components {
parentExpanded: boolean;
url: string;
}
interface HyMenuItemSidebar {
ariaExpanded: boolean;
depth: number;
hasChildren: boolean;
inActiveTrail: boolean;
isActive: boolean;
isActiveChild?: boolean;
isDemo: boolean;
isHeading: boolean;
isParent: boolean;
label: string;
menuButtonSubmenuExpand?: string;
menuIsOpen: boolean;
menuItemAlternative: boolean;
menuLinkId: string;
menuType: MenuType;
parentAsHeading: string;
parentExpanded: boolean;
url: string;
}
interface HyMenuLanguage {
dataMenuLanguage: MenuLanguage[] | string;
isMobile: boolean;
......@@ -488,6 +508,14 @@ export namespace Components {
interface HyMenuLevelContainer {
activeTrailTriggered: boolean;
depth: number;
/**
* label for front page for panel first parent
*/
frontLabel: string;
/**
* Url to front page for panel first parent
*/
frontUrl: string;
headingItem: any;
labelFrontPage?: string;
menuButtonSubmenuExpand?: string;
......@@ -502,6 +530,56 @@ export namespace Components {
labelBack: string;
labelFrontPage: string;
}
interface HyMenuSidebar {
/**
* Isdemo
*/
isDemo: boolean;
/**
* Logo label
*/
logoLabel?: string;
/**
* Url for logo.
*/
logoUrl?: string;
/**
* Is menu open boolean.
*/
menuIsOpen: boolean;
/**
* Menu type. Defaults to sidenav.
*/
menuType: MenuType;
/**
* Previous panel to be toggled to keep track.
*/
minHeight: any;
/**
* Upper menus panel boolean.
*/
panelOpen: boolean;
/**
* Label for panel toggle button.
*/
panelToggleAriaLabel?: string;
/**
* Label for panel toggle button.
*/
panelToggleCloseAriaLabel?: string;
/**
* Label for panel toggle button.
*/
panelToggleCloseLabel?: string;
/**
* Label for panel toggle button.
*/
panelToggleLabel?: string;
/**
* Logo size
*/
size: SiteLogoSize;
}
interface HyParagraphText {
headerstyle: string;
placement: string;
......@@ -930,6 +1008,11 @@ declare global {
prototype: HTMLHyMenuItemElement;
new (): HTMLHyMenuItemElement;
};
interface HTMLHyMenuItemSidebarElement extends Components.HyMenuItemSidebar, HTMLStencilElement {}
var HTMLHyMenuItemSidebarElement: {
prototype: HTMLHyMenuItemSidebarElement;
new (): HTMLHyMenuItemSidebarElement;
};
interface HTMLHyMenuLanguageElement extends Components.HyMenuLanguage, HTMLStencilElement {}
var HTMLHyMenuLanguageElement: {
prototype: HTMLHyMenuLanguageElement;
......@@ -950,6 +1033,11 @@ declare global {
prototype: HTMLHyMenuMobileBreadcrumbElement;
new (): HTMLHyMenuMobileBreadcrumbElement;
};
interface HTMLHyMenuSidebarElement extends Components.HyMenuSidebar, HTMLStencilElement {}
var HTMLHyMenuSidebarElement: {
prototype: HTMLHyMenuSidebarElement;
new (): HTMLHyMenuSidebarElement;
};
interface HTMLHyParagraphTextElement extends Components.HyParagraphText, HTMLStencilElement {}
var HTMLHyParagraphTextElement: {
prototype: HTMLHyParagraphTextElement;
......@@ -1100,10 +1188,12 @@ declare global {
'hy-main-content-wrapper': HTMLHyMainContentWrapperElement;
'hy-menu': HTMLHyMenuElement;
'hy-menu-item': HTMLHyMenuItemElement;
'hy-menu-item-sidebar': HTMLHyMenuItemSidebarElement;
'hy-menu-language': HTMLHyMenuLanguageElement;
'hy-menu-language-item': HTMLHyMenuLanguageItemElement;
'hy-menu-level-container': HTMLHyMenuLevelContainerElement;
'hy-menu-mobile-breadcrumb': HTMLHyMenuMobileBreadcrumbElement;
'hy-menu-sidebar': HTMLHyMenuSidebarElement;
'hy-paragraph-text': HTMLHyParagraphTextElement;
'hy-process': HTMLHyProcessElement;
'hy-process-flow-box': HTMLHyProcessFlowBoxElement;
......@@ -1555,6 +1645,30 @@ declare namespace LocalJSX {
parentExpanded?: boolean;
url?: string;
}
interface HyMenuItemSidebar {
ariaExpanded?: boolean;
depth?: number;
hasChildren?: boolean;
inActiveTrail?: boolean;
isActive?: boolean;
isActiveChild?: boolean;
isDemo?: boolean;
isHeading?: boolean;
isParent?: boolean;
label?: string;
menuButtonSubmenuExpand?: string;
menuIsOpen?: boolean;
menuItemAlternative?: boolean;
menuLinkId?: string;
menuType?: MenuType;
onAddBreadcrumb?: (event: CustomEvent<any>) => void;
onMenuContainerActiveTrail?: (event: CustomEvent<any>) => void;
onMenuContainerToggled?: (event: CustomEvent<any>) => void;
onRouteClicked?: (event: CustomEvent<any>) => void;
parentAsHeading?: string;
parentExpanded?: boolean;
url?: string;
}
interface HyMenuLanguage {
dataMenuLanguage?: MenuLanguage[] | string;
isMobile?: boolean;
......@@ -1571,6 +1685,14 @@ declare namespace LocalJSX {
interface HyMenuLevelContainer {
activeTrailTriggered?: boolean;
depth?: number;
/**
* label for front page for panel first parent
*/
frontLabel?: string;
/**
* Url to front page for panel first parent
*/
frontUrl?: string;
headingItem?: any;
labelFrontPage?: string;
menuButtonSubmenuExpand?: string;
......@@ -1586,6 +1708,56 @@ declare namespace LocalJSX {
labelFrontPage?: string;
onRemoveBreadcrumb?: (event: CustomEvent<any>) => void;
}
interface HyMenuSidebar {
/**
* Isdemo
*/
isDemo?: boolean;
/**
* Logo label
*/
logoLabel?: string;
/**
* Url for logo.
*/
logoUrl?: string;
/**
* Is menu open boolean.
*/
menuIsOpen?: boolean;
/**
* Menu type. Defaults to sidenav.
*/
menuType?: MenuType;
/**
* Previous panel to be toggled to keep track.
*/
minHeight?: any;
/**
* Upper menus panel boolean.
*/
panelOpen?: boolean;
/**
* Label for panel toggle button.
*/
panelToggleAriaLabel?: string;
/**
* Label for panel toggle button.
*/
panelToggleCloseAriaLabel?: string;
/**
* Label for panel toggle button.
*/
panelToggleCloseLabel?: string;
/**
* Label for panel toggle button.
*/
panelToggleLabel?: string;
/**
* Logo size
*/
size?: SiteLogoSize;
}
interface HyParagraphText {
headerstyle?: string;
placement?: string;
......@@ -1795,10 +1967,12 @@ declare namespace LocalJSX {
'hy-main-content-wrapper': HyMainContentWrapper;
'hy-menu': HyMenu;
'hy-menu-item': HyMenuItem;
'hy-menu-item-sidebar': HyMenuItemSidebar;
'hy-menu-language': HyMenuLanguage;
'hy-menu-language-item': HyMenuLanguageItem;
'hy-menu-level-container': HyMenuLevelContainer;
'hy-menu-mobile-breadcrumb': HyMenuMobileBreadcrumb;
'hy-menu-sidebar': HyMenuSidebar;
'hy-paragraph-text': HyParagraphText;
'hy-process': HyProcess;
'hy-process-flow-box': HyProcessFlowBox;
......@@ -1883,12 +2057,14 @@ declare module '@stencil/core' {
JSXBase.HTMLAttributes<HTMLHyMainContentWrapperElement>;
'hy-menu': LocalJSX.HyMenu & JSXBase.HTMLAttributes<HTMLHyMenuElement>;
'hy-menu-item': LocalJSX.HyMenuItem & JSXBase.HTMLAttributes<HTMLHyMenuItemElement>;
'hy-menu-item-sidebar': LocalJSX.HyMenuItemSidebar & JSXBase.HTMLAttributes<HTMLHyMenuItemSidebarElement>;
'hy-menu-language': LocalJSX.HyMenuLanguage & JSXBase.HTMLAttributes<HTMLHyMenuLanguageElement>;
'hy-menu-language-item': LocalJSX.HyMenuLanguageItem & JSXBase.HTMLAttributes<HTMLHyMenuLanguageItemElement>;
'hy-menu-level-container': LocalJSX.HyMenuLevelContainer &
JSXBase.HTMLAttributes<HTMLHyMenuLevelContainerElement>;
'hy-menu-mobile-breadcrumb': LocalJSX.HyMenuMobileBreadcrumb &
JSXBase.HTMLAttributes<HTMLHyMenuMobileBreadcrumbElement>;
'hy-menu-sidebar': LocalJSX.HyMenuSidebar & JSXBase.HTMLAttributes<HTMLHyMenuSidebarElement>;
'hy-paragraph-text': LocalJSX.HyParagraphText & JSXBase.HTMLAttributes<HTMLHyParagraphTextElement>;
'hy-process': LocalJSX.HyProcess & JSXBase.HTMLAttributes<HTMLHyProcessElement>;
'hy-process-flow-box': LocalJSX.HyProcessFlowBox & JSXBase.HTMLAttributes<HTMLHyProcessFlowBoxElement>;
......
.hy-main {
display: flex;
//margin: var(--gutter-mobile) auto;
margin: 0 auto;
max-width: $fullhd;
padding: 0;
......@@ -21,6 +20,8 @@
}
&.with-sidebar {
position: relative;
.layout-content {
@include breakpoint($medium) {
margin: 0 auto;
......@@ -40,13 +41,19 @@
display: none;
@include breakpoint($extrawide) {
display: block;
border-right: 2px dotted var(--grayscale-medium);
background: linear-gradient(270deg, #f5f5f5 0%, #f8f8f8 100%);
display: block;
max-width: 320px;
order: 1;
padding: var(--gutter-extrawide);
padding: var(--gutter-extrawide) 13px;
width: 20%;
height: auto;
&.menu-is-open {
background: var(--grayscale-white);
min-height: 100vh;
overflow: auto;
}
}
}
}
......
import {h} from '@stencil/core';
function SvgArrowDown(props) {
return (
<svg viewBox="0 0 26 18" {...props}>
<circle id="Oval" fill="#107EAB" cx="8.5" cy="9" r="2"></circle>
<g id="Icon/arrow/to_right" transform="translate(10.000000, 0.000000)">
<g transform="translate(9.000000, 9.000000) rotate(270.000000) translate(-9.000000, -9.000000) ">
<path
d="M15.3739539,2.58885408 L9.05563628,10.0114192 L2.62512723,2.58778423 C2.5650068,2.51838293 2.45894051,2.51196471 2.39075562,2.57360208 L0.0533953737,4.68651506 C-0.0121083665,4.74572874 -0.0181566661,4.84589323 0.039753545,4.91243423 L8.93312424,15.1312497 C8.99867166,15.2065661 9.11651053,15.2061936 9.18157232,15.1304643 L17.9609254,4.91164883 C18.018144,4.84504869 18.011809,4.74545823 17.9466045,4.68651506 L15.6092442,2.57360208 C15.5406475,2.51159238 15.4338255,2.5185168 15.3739539,2.58885408 Z"
id="Path"
transform="translate(9.000000, 8.859375) rotate(-360.000000) translate(-9.000000, -8.859375) "
></path>
</g>
</g>
<circle id="Oval-Copy" fill="#107EAB" cx="2" cy="9" r="2"></circle>
</svg>
);
}
export default SvgArrowDown;
......@@ -13,6 +13,7 @@ const iconNames: IconName = {
'hy-icon-caret-down': (p) => <icons.CaretDown {...p} />,
'hy-icon-caret-left': (p) => <icons.CaretLeft {...p} />,
'hy-icon-caret-right': (p) => <icons.CaretRight {...p} />,
'hy-icon-dot-arrow-right': (p) => <icons.DotArrowRight {...p} />,
'hy-icon-done': (p) => <icons.Done {...p} />,
'hy-icon-euro': (p) => <icons.Euro {...p} />,
'hy-icon-globe': (p) => <icons.Globe {...p} />,
......
......@@ -76,6 +76,7 @@ export {default as Date} from './Date';
export {default as Discover} from './Discover';
export {default as Done} from './Done';
export {default as Dot} from './Dot';
export {default as DotArrowRight} from './DotArrowRight';
export {default as Download} from './Download';
export {default as Drag} from './Drag';
export {default as Dragbutton} from './Dragbutton';
......
......@@ -33,8 +33,11 @@
- [hy-introduction](../hy-introduction)
- [hy-link-box](../link-box)
- [hy-menu-item](../navigation/menu-item)
- [hy-menu-item-sidebar](../navigation/menu-item-sidebar)
- [hy-menu-language](../navigation/menu-language)
- [hy-menu-level-container](../navigation/menu-level-container)
- [hy-menu-mobile-breadcrumb](../navigation/menu-mobile-breadcrumb)
- [hy-menu-sidebar](../navigation/menu-sidebar)
- [hy-quote](../hy-quote)
- [hy-search-field](../hy-search-field)
- [hy-shortcuts](../hy-shortcuts)
......@@ -67,8 +70,11 @@ graph TD;
hy-introduction --> hy-icon
hy-link-box --> hy-icon
hy-menu-item --> hy-icon
hy-menu-item-sidebar --> hy-icon
hy-menu-language --> hy-icon
hy-menu-level-container --> hy-icon
hy-menu-mobile-breadcrumb --> hy-icon
hy-menu-sidebar --> hy-icon
hy-quote --> hy-icon
hy-search-field --> hy-icon
hy-shortcuts --> hy-icon
......
.hy-menu-item-sidebar {
list-style-type: none;
margin-bottom: 6px;
&--label-container {
@include font-size(18px, 22px);
@include font-weight($bold);
border: 0.5px solid rgba(16, 126, 171, 0.1);
box-sizing: border-box;
background-color: var(--grayscale-white);
color: var(--brand-main-light);
display: flex;
font-family: var(--main-font-family);
justify-content: space-between;
min-height: 48px;
padding: 0;
}
&:focus {
outline-offset: 4px;
}
&:hover {
color: var(--brand-main-nearly-black);
}
> .hy-menu-level-container.hy-menu-level-container--sidepanel {
display: none;
}
&.hy-menu-item-sidebar--sidepanel.in-active-trail {
> .hy-menu-item-sidebar--label-container {
border: 1px solid var(--brand-main-light);
text-decoration: none;
> .hy-menu-item__label__icon {
border: none;
background-color: var(--brand-main-light);
svg {
fill: var(--grayscale-white);
}
}
}
> .hy-menu-level-container--sidepanel {
display: flex;
}
}
&:not(.is-parent):not(.is-parent--sub-level).is-active-item {
> .hy-menu-item-sidebar--label-container {
background: var(--grayscale-white) !important;
text-decoration: none;
> .hy-menu-item-sidebar__label {
border: 1px solid var(--brand-main-light);
border-right: 0;
}
> .hy-menu-item__label__icon {
background-color: var(--brand-main-light);
border: none;
margin-left: 4px;
svg {
fill: var(--grayscale-white);
}
}
}
}
&__label {
align-items: center;
color: var(--brand-main-light);
display: flex;
hyphens: auto;
padding: 8px 10px 8px 20px;
text-decoration: none;
width: 100%;
word-break: break-word;
}
.hy-menu-item__label__icon {
border-top: 0;
border-right: 0;
border-bottom: 0;
background: none;
border-left: 1px dotted var(--brand-main-light);
color: var(--brand-main-light);
display: inline-block;
position: relative;
width: 61.5px;
min-height: 48px;
svg {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
&:hover {
cursor: pointer;
}
&:focus {
outline: none;
box-shadow: 0 0 1px 1px rgba(55, 148, 224, 0.5);
border-radius: none;
}
}
&.is-parent {
align-items: flex-start;
background: none;
display: flex;
flex-direction: row;
justify-content: flex-start;
margin-bottom: 10px;
padding-bottom: 10px;
width: calc(100% - 25px);
max-width: 280px;
.hy-menu-item-sidebar--label-container {
align-items: flex-start;
background: none;
border: none;
color: var(--brand-main-light);
display: flex;
.hy-menu-item-sidebar__label {
@include font-size(26px, 26px);
font-family: var(--main-font-family);
font-weight: bold;
letter-spacing: -0.81px;
margin: 0 0 0 0.5rem;
padding: 0;
text-decoration: none;
}
}
&:hover {
color: var(--brand-main-nearly-black);
.hy-menu-item__parent__icon__svg {
fill: var(--brand-main-nearly-black);
}
}
.hy-menu-item__parent__icon {
align-items: center;
display: flex;
flex-direction: row;
height: 26px;
}
.hy-menu-item__parent__icon__svg svg {
fill: var(--brand-main-light);
}
&.is-parent--sub-level {
align-items: flex-end;
background: none;
border-bottom: 1px solid var(--brand-main-light);
display: flex;
margin-bottom: 12px;
padding-bottom: 18px;
position: absolute;
transform: translateY(calc(-100% - 12px));
width: calc(100% - 25px);
.hy-menu-item-parent__icon {
bottom: 20px;
left: -20px;
position: absolute;
}
svg {
fill: var(--brand-main-light);
}
> .hy-menu-item-sidebar--label-container {
align-items: flex-end;
margin: 0;
padding: 0;
}
}
}
&.is-current-page {
span.hy-menu-item-sidebar__label {
border-left: 4px solid var(--grayscale-black);
color: var(--grayscale-black);
padding: 8px 10px 8px 6px !important;
}
}
}
import {Component, Element, Event, EventEmitter, h, Prop, Listen} from '@stencil/core';
import {MenuType} from '../../../utils/utils';
@Component({
tag: 'hy-menu-item-sidebar',
styleUrl: 'menu-item-sidebar.scss',
shadow: false,
})
export class MenuItemSidebar {
@Element() el: HTMLElement;
@Event() addBreadcrumb: EventEmitter;
@Event() menuContainerActiveTrail: EventEmitter;
@Event() menuContainerToggled: EventEmitter;
@Event() routeClicked: EventEmitter;
@Prop() inActiveTrail: boolean = false;
@Prop() isActive: boolean = false;
@Prop() isActiveChild?: boolean = false;
@Prop() isHeading: boolean = false;
@Prop() isParent: boolean = false;
@Prop() isDemo: boolean = false;
@Prop() menuItemAlternative: boolean = false;
@Prop() menuLinkId: string = '';
@Prop() menuType: MenuType = MenuType.sidenav;
@Prop() menuButtonSubmenuExpand?: string = '';
@Prop() menuIsOpen = false;
@Prop({mutable: true}) ariaExpanded: boolean = false;
@Prop({mutable: true, reflect: true}) depth: number = 0;
@Prop({mutable: true}) hasChildren: boolean = null;
@Prop({mutable: true}) label: string = '';
@Prop({mutable: true}) parentAsHeading: string = '';
@Prop({mutable: true, reflect: true}) parentExpanded: boolean = false;
@Prop({mutable: true}) url: string = '';
@Listen('sidebarTopLevelToggle') sidebarTopLevelToggle(e) {
if (this.depth !== 1) {
return;
}
const body = document.querySelector('body') as HTMLElement;
const container = document.querySelector('.hy-menu-sidebar--container') as HTMLElement;
const menuItem = e.target.closest('.hy-menu-item-sidebar') as HTMLElement;
if (container.classList.contains('sidebar-open') && menuItem.classList.contains('is-active-item')) {
container.classList.remove('sidebar-open');
body.classList.remove('hy-menu-sidebar__no-scroll');
} else {
container.classList.add('sidebar-open');
body.classList.add('hy-menu-sidebar__no-scroll');
}
e.preventDefault();
}
componentWillLoad() {
// If is-active class is added by system, add it to menu component as well.
if (this.el.classList.contains('is-active')) {
this.isActive = true;
}
// Notify breadcrumbs if item is in active trail.
if (this.inActiveTrail && !this.isActive) {
const currentParent = this.el.parentNode;
this.addBreadcrumb.emit({
url: this.url,
label: currentParent.parentElement.getAttribute('label'),
bid: this.menuLinkId,
});
}
if (this.inActiveTrail) {
const currentParent = this.el.parentNode as any;
if (this.menuType === MenuType.sidepanel) {
currentParent.classList.add('is-open');
}
}
// If current menu item is active, trigger all parent menuLevelContainer
// elements in the same active-trail to open the menu.
if (this.isActive) {
const getParents = (elem) => {
let parents = [];
while (elem.parentNode && elem.parentNode.nodeName.toLowerCase() != 'hy-menu-sidebar') {
elem = elem.parentNode;
parents.push(elem);
}
return parents;
};
const parents = getParents(this.el);
parents.forEach((element) => {
if (element.tagName.toLowerCase() === 'hy-menu-item-sidebar') {
this.menuContainerActiveTrail.emit({
triggerItem: element.getAttribute('menu-link-id'),
});
}
});
// If side navigation menu item has is-active state, prepare the menu items
// for the last children.
parents.forEach((element) => {
if (element.tagName.toLowerCase() === 'hy-menu-item-sidebar') {
element.classList.add('is-hidden--child');
}
});
// If current active menu item have children, set the child
// menu-level-container open.
if (this.el.children.length > 0) {
this.el.children[0].setAttribute('class', 'is-open-on-top');
}
}
}
componentWillRender() {
// Assign depth value to current menu item instance; 1st level, 2nd level, etc.
this.hasChildren = this.el.getElementsByTagName('hy-menu-level-container').length >= 1;
let parentMenuItem = this.el.closest('hy-menu-item-sidebar');
let nextParentMenuItem;
this.depth = 0;
while (parentMenuItem) {
nextParentMenuItem = parentMenuItem.parentElement.closest('hy-menu-item-sidebar');
if (nextParentMenuItem === parentMenuItem) {
break;
} else {
if (nextParentMenuItem !== null) {
this.parentAsHeading = nextParentMenuItem;
}
parentMenuItem = nextParentMenuItem;
this.depth = this.depth + 1;
}
}
}
render() {
let classAttributes = [
'hy-menu-item-sidebar',
this.menuType ? `hy-menu-item-sidebar--${this.menuType}` : '',
this.depth != null ? 'hy-menu-item--level-' + this.depth : '',
this.isDemo ? 'is-demo' : '',
this.isActive ? 'is-current-page' : '',
this.isParent ? 'is-parent' : '',
this.hasChildren ? 'has-children' : '',
this.inActiveTrail ? 'in-active-trail' : '',
this.isActive && this.hasChildren ? 'is-active--heading' : '',
];
let anchorClassAttributes = ['hy-menu-item-sidebar__label'];
classAttributes = [...classAttributes];
if (this.url) {
return (
<li
data-link-id={`menu-link-sidebar-${this.menuLinkId}`}
class={classAttributes.join(' ')}
aria-current={this.isActive ? 'true' : 'false'}
item-level={this.depth}
>
{this.isParent && (
<span class={'hy-menu-item__parent__icon'}>
<hy-icon
class={'hy-menu-item__parent__icon__svg'}
icon={'hy-icon-caret-left'}
fill={'currentColor'}
size={10}
/>
</span>
)}
<div class="hy-menu-item-sidebar--label-container">
{this.isActive ? (
<span class={anchorClassAttributes.join(' ')}>{this.label}</span>
) : (
<a class={anchorClassAttributes.join(' ')} href={this.url}>
{this.label}
</a>
)}
{this.hasChildren && (
<button
aria-labelledby={this.label}
aria-expanded={this.inActiveTrail && !this.isActive ? 'true' : 'false'}
type="button"
class={'hy-menu-item__label__icon'}
onClick={(e) => this.sidebarTopLevelToggle(e)}
>
<hy-icon
class={'hy-menu-item__label__icon__svg'}
icon={'hy-icon-caret-right'}
fill={'currentColor'}
size={18}
/>
</button>
)}
</div>
{this.hasChildren && <slot />}
</li>
);
} else {
return;
}
}
}
# menu-item-sidebar
<!-- Auto Generated Below -->
## Properties
| Property | Attribute | Description | Type | Default |
| ------------------------- | ---------------------------- | ----------- | -------------------------------------------------------------------------------------------------- | ------------------ |
| `ariaExpanded` | `aria-expanded` | | `boolean` | `false` |
| `depth` | `depth` | | `number` | `0` |
| `hasChildren` | `has-children` | | `boolean` | `null` |
| `inActiveTrail` | `in-active-trail` | | `boolean` | `false` |
| `isActive` | `is-active` | | `boolean` | `false` |
| `isActiveChild` | `is-active-child` | | `boolean` | `false` |
| `isDemo` | `is-demo` | | `boolean` | `false` |
| `isHeading` | `is-heading` | | `boolean` | `false` |
| `isParent` | `is-parent` | | `boolean` | `false` |
| `label` | `label` | | `string` | `''` |
| `menuButtonSubmenuExpand` | `menu-button-submenu-expand` | | `string` | `''` |
| `menuIsOpen` | `menu-is-open` | | `boolean` | `false` |
| `menuItemAlternative` | `menu-item-alternative` | | `boolean` | `false` |
| `menuLinkId` | `menu-link-id` | | `string` | `''` |
| `menuType` | `menu-type` | | `MenuType.desktop \| MenuType.mobile \| MenuType.sidenav \| MenuType.sidepanel \| MenuType.tablet` | `MenuType.sidenav` |
| `parentAsHeading` | `parent-as-heading` | | `string` | `''` |
| `parentExpanded` | `parent-expanded` | | `boolean` | `false` |
| `url` | `url` | | `string` | `''` |
## Events
| Event | Description | Type |
| -------------------------- | ----------- | ------------------ |
| `addBreadcrumb` | | `CustomEvent<any>` |
| `menuContainerActiveTrail` | | `CustomEvent<any>` |
| `menuContainerToggled` | | `CustomEvent<any>` |
| `routeClicked` | | `CustomEvent<any>` |
## Dependencies
### Depends on
- [hy-icon](../../icon)
### Graph
```mermaid
graph TD;
hy-menu-item-sidebar --> hy-icon
style hy-menu-item-sidebar fill:#f9f,stroke:#333,stroke-width:4px
```
---
Helsinki University Design System
......@@ -39,15 +39,6 @@
--hy-menu-item-sidenav-display: block;
}
:host(.hy-menu-item--sidenav.is-hidden--child) {
--hy-menu-item-sidenav-display: none;
}
:host(.hy-menu-item--sidenav:not(.is-parent)) {
display: none;
visibility: hidden;
}
:host(.hy-menu-item--sidenav.is-active) {
display: block;
visibility: visible;
......
import {Component, Element, Event, EventEmitter, h, Host, Listen, Prop} from '@stencil/core';
import {MenuType} from '../../../utils/utils';
// import {MenuItemVariants} from '../../utils/utils';
@Component({
tag: 'hy-menu-item',
styleUrl: 'menu-item.scss',
......@@ -61,10 +59,6 @@ export class MenuItem {
label: currentParent.parentElement.getAttribute('label'),
bid: this.menuLinkId,
});
if (this.menuType === MenuType.sidenav) {
this.el.children[0].setAttribute('class', 'is-open');
}
}
// If current menu item is active, trigger all parent menuLevelContainer
......@@ -87,37 +81,6 @@ export class MenuItem {
});
}
});
// If side navigation menu item has is-active state, prepare the menu items
// for the last children.
if (this.menuType === MenuType.sidenav) {
parents.forEach((element) => {
if (element.tagName.toLowerCase() === 'hy-menu-item') {
element.classList.add('is-hidden--child');
}
});
// If current active menu item have children, set the child
// menu-level-container open.
if (this.el.children.length > 0) {
this.el.children[0].setAttribute('class', 'is-open');
}
// If current active menu item does not have any children, mock the
// current menu item and parent menu item to behave like there would
// be child menu items available.
else {
const parentMenuItem = this.el.parentElement.closest('hy-menu-item');
const parentMenuItemAnchor = parentMenuItem.shadowRoot.querySelector('a.hy-menu-item--sidenav');
parentMenuItem.classList.remove('is-hidden--child');
parentMenuItemAnchor.classList.add('is-active--heading');
const menuItemSiblings = Array.prototype.slice.call(this.el.closest('hy-menu-level-container').children);
menuItemSiblings.forEach((element) => {
if (element.tagName.toLowerCase() === 'hy-menu-item') {
element.classList.add('is-active--child');
}
});
}
}
}
}
......@@ -233,28 +196,14 @@ export class MenuItem {
];
let anchorClassAttributes = [...classAttributes, this.isHeading ? 'is-heading' : ''];
classAttributes = [...classAttributes, 'hy-menu-item'];
return this.isParent ? (
<Host class={classAttributes.join(' ')}>
<a href={this.url} class={anchorClassAttributes.join(' ')}>
<span class={'hy-menu-item__label__icon'}>
<hy-icon icon={'hy-icon-arrow-left'} fill={'currentColor'} size={18} />
</span>
<span class={'hy-menu-item__label'}>{this.label}</span>
</a>
</Host>
) : (
<Host class={classAttributes.join(' ')}>
<a aria-current={this.isHeading.toString()} href={this.url} class={anchorClassAttributes.join(' ')}>
<span class={'hy-menu-item__label'}>{this.label}</span>
{this.hasChildren && (
<span class={'hy-menu-item__label__icon'}>
<hy-icon icon={'hy-icon-caret-right'} fill={'currentColor'} size={12} />
</span>
)}
return (
<li class={classAttributes.join(' ')}>
<a class={anchorClassAttributes.join(' ')} href={this.url}>
{this.label}
</a>
{this.hasChildren && <slot />}
</Host>
</li>
);
}
}
......
......@@ -4,25 +4,25 @@
## Properties
| Property | Attribute | Description | Type | Default |
| ------------------------- | ---------------------------- | ----------- | ---------------------------------------------------------------------------- | ------------------ |
| `ariaExpanded` | `aria-expanded` | | `boolean` | `false` |
| `depth` | `depth` | | `number` | `0` |
| `hasChildren` | `has-children` | | `boolean` | `null` |
| `inActiveTrail` | `in-active-trail` | | `boolean` | `false` |
| `isActive` | `is-active` | | `boolean` | `false` |
| `isActiveChild` | `is-active-child` | | `boolean` | `false` |
| `isDemo` | `is-demo` | | `boolean` | `false` |
| `isHeading` | `is-heading` | | `boolean` | `false` |
| `isParent` | `is-parent` | | `boolean` | `false` |
| `label` | `label` | | `string` | `''` |
| `menuButtonSubmenuExpand` | `menu-button-submenu-expand` | | `string` | `''` |
| `menuItemAlternative` | `menu-item-alternative` | | `boolean` | `false` |
| `menuLinkId` | `menu-link-id` | | `string` | `''` |
| `menuType` | `menu-type` | | `MenuType.desktop \| MenuType.mobile \| MenuType.sidenav \| MenuType.tablet` | `MenuType.desktop` |
| `parentAsHeading` | `parent-as-heading` | | `string` | `''` |
| `parentExpanded` | `parent-expanded` | | `boolean` | `false` |
| `url` | `url` | | `string` | `''` |
| Property | Attribute | Description | Type | Default |
| ------------------------- | ---------------------------- | ----------- | -------------------------------------------------------------------------------------------------- | ------------------ |
| `ariaExpanded` | `aria-expanded` | | `boolean` | `false` |
| `depth` | `depth` | | `number` | `0` |
| `hasChildren` | `has-children` | | `boolean` | `null` |
| `inActiveTrail` | `in-active-trail` | | `boolean` | `false` |
| `isActive` | `is-active` | | `boolean` | `false` |
| `isActiveChild` | `is-active-child` | | `boolean` | `false` |
| `isDemo` | `is-demo` | | `boolean` | `false` |
| `isHeading` | `is-heading` | | `boolean` | `false` |
| `isParent` | `is-parent` | | `boolean` | `false` |
| `label` | `label` | | `string` | `''` |
| `menuButtonSubmenuExpand` | `menu-button-submenu-expand` | | `string` | `''` |
| `menuItemAlternative` | `menu-item-alternative` | | `boolean` | `false` |
| `menuLinkId` | `menu-link-id` | | `string` | `''` |
| `menuType` | `menu-type` | | `MenuType.desktop \| MenuType.mobile \| MenuType.sidenav \| MenuType.sidepanel \| MenuType.tablet` | `MenuType.desktop` |
| `parentAsHeading` | `parent-as-heading` | | `string` | `''` |
| `parentExpanded` | `parent-expanded` | | `boolean` | `false` |
| `url` | `url` | | `string` | `''` |
## Events
......
......@@ -19,7 +19,6 @@
}
&--desktop {
//align-items: center;
align-items: flex-start;
display: flex;
flex-flow: column;
......@@ -39,18 +38,166 @@
}
}
&--sidenav {
&--sidepanel {
background-color: var(--grayscale-white);
border-left: 1px solid rgba(0, 0, 0, 0.2);
display: flex;
flex-flow: column;
flex-direction: column;
height: 100vh;
left: 0;
margin: 0;
padding-left: 13px;
padding-right: 13px;
padding-top: 240px;
position: absolute;
top: 0;
visibility: visible;
width: 261px;
z-index: 123;
&.is-open {
display: block;
visibility: visible;
&:not(.hy-menu-level-container--level-1) {
display: none;
min-height: var(--minHeight);
transform: translateX(calc(100% - 1px));
&.active-trail-panel,
&.is-open {
display: flex;
}
}
&.is-open,
&.active-trail-panel {
height: fit-content;
min-height: var(--minHeight);
}
}
&:not(.is-open):not(.hy-menu-level-container--level-1) {
&--sidenav {
flex-flow: column;
margin: 0;
padding: 0;
position: relative;
&:not(.hy-menu-level-container--level-1) {
opacity: 0;
display: none;
visibility: hidden;
&.is-closed {
animation-direction: normal;
animation-duration: 0.25s;
animation-fill-mode: backwards;
animation-name: slide-panel-in;
}
&.is-open {
animation-direction: normal;
animation-duration: 0.25s;
animation-fill-mode: forwards;
animation-name: slide-panel-in;
display: flex;
height: auto;
opacity: 1;
visibility: visible;
}
}
&.is-open.is-open-on-top {
display: none;
transform: none !important;
}
&.hy-menu-level-container.is-open {
max-width: 280px;
}
.hy-menu-level-container--level-1 {
display: flex;
}
&:not(.hy-menu-level-container--level-1) {
background-color: var(--grayscale-white);
border-left: 1px solid rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
height: 100vh;
left: 0;
margin: -100vh 0;
min-height: var(--minHeight);
padding-left: 13px;
padding-right: 13px;
position: absolute;
transform: translateX(calc(100% - 1px));
width: 281px;
z-index: 123;
&.hy-menu-level-container--level-2 {
transform: translateX(calc(100% + 16px)) !important;
}
&:before {
background: white;
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
content: '';
display: none;
height: 96px;
left: -1px;
position: absolute;
top: 70vh;
width: 261px;
}
}
.hy-menu-level-container--level-2 {
margin: -100vh 0;
padding: 100vh 13px;
top: 0;
}
.hy-menu-level-container--level-3,
.hy-menu-level-container--level-4,
.hy-menu-level-container--level-5,
.hy-menu-level-container--level-6,
.hy-menu-level-container--level-7 {
padding: 100vh 13px;
top: 100vh;
}
}
}
@keyframes slide-panel-in {
from {
transform: translateX(0);
display: none;
opacity: 0;
z-index: 1;
}
95% {
display: flex;
}
to {
transform: translateX(calc(100% - 1px));
opacity: 1;
z-index: 123;
}
}
@keyframes slide-panel-out {
from {
transform: translateX(100%);
opacity: 1;
z-index: 123;
}
95% {
transform: translateX(0);
opacity: 0;
z-index: 1;
}
to {
display: none;
}
}
......@@ -16,6 +16,14 @@ export class MenuLevelContainer {
@Prop({mutable: true, reflect: true}) depth: number = 0;
@Prop({mutable: true}) headingItem: any;
@Prop({mutable: true, reflect: true}) triggerItem: string;
/**
* Url to front page for panel first parent
*/
@Prop() frontUrl: string;
/**
* label for front page for panel first parent
*/
@Prop() frontLabel: string;
@State() menuIsOpen: boolean = false;
// Add/Remove is-hidden class from each upper level menu-items
......@@ -109,7 +117,8 @@ export class MenuLevelContainer {
// Set is-active-child and menu-level attributes to all sibling menu-items.
if (this.menuType === MenuType.sidenav) {
const parentMenuItem = this.el.closest('hy-menu-item');
const parentMenuItem = this.el.closest('hy-menu-item-sidebar');
if (parentMenuItem && parentMenuItem.classList.contains('is-active')) {
this.menuIsOpen = true;
const items = Array.from(this.el.children);
......@@ -123,7 +132,6 @@ export class MenuLevelContainer {
render() {
let classAttributes = ['hy-menu-level-container', 'hy-menu-level-container--level-' + this.depth];
switch (this.menuType) {
case MenuType.desktop:
classAttributes = [...classAttributes, 'hy-menu-level-container--desktop'];
......@@ -161,25 +169,90 @@ export class MenuLevelContainer {
);
}
case MenuType.sidenav:
classAttributes = [...classAttributes, 'hy-menu-level-container--sidenav'];
if (this.depth === 1) {
classAttributes = [...classAttributes, 'is-open'];
const shouldBeHidden = this.depth != null && this.depth === 1 ? 'is-open' : '';
classAttributes = [...classAttributes, 'hy-menu-level-container--sidenav', shouldBeHidden];
return (
<Host aria-expanded={this.menuIsOpen.toString()} class={classAttributes.join(' ')} tabindex={'-1'}>
<ul data-menu-level={this.depth} class={classAttributes.join(' ')}>
<slot />
</Host>
</ul>
);
} else {
classAttributes = [...classAttributes, this.menuIsOpen ? 'is-open' : null];
const isMenuOpen = this.menuIsOpen ? 'is-open_is-open__sub-level' : null;
const shouldBeHidden = this.depth != null && this.depth === 1 ? 'is-open' : '';
const parentMenuItem = this.el.closest('hy-menu-item-sidebar') as any;
const parentClone = parentMenuItem.cloneNode(true) as any;
const parentLink = parentClone.querySelector('.hy-menu-item-sidebar__label');
classAttributes = [...classAttributes, 'hy-menu-level-container--sidenav', shouldBeHidden, isMenuOpen];
return (
<Host aria-expanded={this.menuIsOpen.toString()} class={classAttributes.join(' ')} tabindex={'-1'}>
<ul data-menu-level={this.depth} class={classAttributes.join(' ')}>
{parentLink && (
<li class="hy-menu-item-sidebar is-parent is-parent--sub-level">
<hy-icon
class={'hy-menu-item-parent__icon'}
icon={'hy-icon-dot-arrow-right'}
fill={'currentColor'}
size={20}
/>
<span
aria-level={this.depth}
role="heading"
class="hy-menu-item-sidebar--label-container"
innerHTML={parentLink.outerHTML}
></span>
</li>
)}
<slot />
</Host>
</ul>
);
}
case MenuType.sidepanel:
//const shouldBeHidden = (this.depth != null && this.depth === 1) ? 'is-open' : '';
classAttributes = [...classAttributes, 'hy-menu-level-container--sidepanel'];
const parentMenuItem = this.el.closest('hy-menu-item-sidebar') as any;
const parentClone = parentMenuItem ? (parentMenuItem.cloneNode(true) as any) : null;
const parentLink = parentClone ? parentClone.querySelector('.hy-menu-item-sidebar__label') : null;
return (
<ul data-menu-level={this.depth} class={classAttributes.join(' ')}>
{parentLink ? (
<li class="hy-menu-item-sidebar is-parent is-parent--sub-level">
<hy-icon
class={'hy-menu-item-parent__icon'}
icon={'hy-icon-dot-arrow-right'}
fill={'currentColor'}
size={20}
/>
<span
aria-level={this.depth}
role="heading"
class="hy-menu-item-sidebar--label-container"
innerHTML={parentLink.outerHTML}
></span>
</li>
) : (
<li class="hy-menu-item-sidebar is-parent is-parent--sub-level is-parent--frontpage">
<span class={'hy-menu-item__parent__icon'}>
<hy-icon
class={'hy-menu-item__parent__icon__svg'}
icon={'hy-icon-caret-left'}
fill={'currentColor'}
size={10}
/>
</span>
<div class="hy-menu-item-sidebar--label-container">
<a class="hy-menu-item-sidebar__label" href={this.frontUrl}>
{this.frontLabel}
</a>
</div>
</li>
)}
<slot />
</ul>
);
}
}
}
......@@ -4,28 +4,32 @@
## Properties
| Property | Attribute | Description | Type | Default |
| ------------------------- | ---------------------------- | ----------- | ---------------------------------------------------------------------------- | ----------------- |
| `activeTrailTriggered` | `active-trail-triggered` | | `boolean` | `false` |
| `depth` | `depth` | | `number` | `0` |
| `headingItem` | `heading-item` | | `any` | `undefined` |
| `labelFrontPage` | `label-front-page` | | `string` | `undefined` |
| `menuButtonSubmenuExpand` | `menu-button-submenu-expand` | | `string` | `undefined` |
| `menuLevel` | `menu-level` | | `number` | `undefined` |
| `menuType` | `menu-type` | | `MenuType.desktop \| MenuType.mobile \| MenuType.sidenav \| MenuType.tablet` | `MenuType.mobile` |
| `triggerItem` | `trigger-item` | | `string` | `undefined` |
| Property | Attribute | Description | Type | Default |
| ------------------------- | ---------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------- | ----------------- |
| `activeTrailTriggered` | `active-trail-triggered` | | `boolean` | `false` |
| `depth` | `depth` | | `number` | `0` |
| `frontLabel` | `front-label` | label for front page for panel first parent | `string` | `undefined` |
| `frontUrl` | `front-url` | Url to front page for panel first parent | `string` | `undefined` |
| `headingItem` | `heading-item` | | `any` | `undefined` |
| `labelFrontPage` | `label-front-page` | | `string` | `undefined` |
| `menuButtonSubmenuExpand` | `menu-button-submenu-expand` | | `string` | `undefined` |
| `menuLevel` | `menu-level` | | `number` | `undefined` |
| `menuType` | `menu-type` | | `MenuType.desktop \| MenuType.mobile \| MenuType.sidenav \| MenuType.sidepanel \| MenuType.tablet` | `MenuType.mobile` |
| `triggerItem` | `trigger-item` | | `string` | `undefined` |
## Dependencies
### Depends on
- [hy-menu-item](../menu-item)
- [hy-icon](../../icon)
### Graph
```mermaid
graph TD;
hy-menu-level-container --> hy-menu-item
hy-menu-level-container --> hy-icon
hy-menu-item --> hy-icon
style hy-menu-level-container fill:#f9f,stroke:#333,stroke-width:4px
```
......
:host {
display: block;
}
.hy-menu-sidebar__no-scroll {
height: 200vh;
overflow: hidden;
}
.hy-menu-sidepanel__no-scroll {
height: 100vh;
overflow: hidden;
}
.hy-menu-sidebar--container {
background: none;
position: relative;
&.sidebar-open {
background: linear-gradient(to right, #fff 380px, rgba(0, 0, 0, 0.5) 10%);
bottom: 0;
display: block;
height: auto;
left: 16px;
overflow: auto;
position: absolute;
right: 0;
top: -96px;
transition: all 0.25s ease-in;
z-index: 5;
.hy-menu-sidebar__logo {
align-items: center;
display: flex;
height: 96px;
margin-bottom: var(--gutter-extrawide);
.hy-site-logo__label {
max-width: 200px;
}
}
.hy-menu-sidebar__panel-toggle {
display: flex;
}
// Open sidenav panels. Set background color to non-active items
:not(.is-parent):not(.is-active-item).hy-menu-item-sidebar .hy-menu-item-sidebar--label-container {
background: linear-gradient(270deg, #f5f5f5 0%, #f8f8f8 100%);
}
}
.hy-menu-sidebar__panel-toggle,
.hy-menu-sidebar__logo {
display: none;
}
}
.hy-menu-sidepanel--container {
animation-direction: normal;
animation-duration: 0.25s;
animation-fill-mode: forwards;
animation-name: sidepanel-moveout;
bottom: 0;
display: none;
left: 0;
overflow: auto;
position: absolute;
right: 0;
top: -96px;
transform: translateX(-105%);
visibility: hidden;
z-index: 124;
&.sidepanel-open {
animation-direction: normal;
animation-duration: 0.35s;
animation-fill-mode: forwards;
animation-name: sidepanel-movein;
display: flex;
height: auto;
width: 100%;
}
}
.hy-menu-sidebar {
display: none;
@include breakpoint($extrawide) {
display: block;
max-width: 280px; // Should be in the wrapper.
&[aria-hidden='false'] {
min-height: var(--minHeight);
}
}
}
.hy-menu-sidebar__panel-toggle {
@include font-size(14px, 20px);
align-items: center;
background-color: transparent;
border: none;
color: var(--brand-main-light);
display: flex;
flex-direction: row;
font-family: var(--main-font-family);
font-weight: 600;
letter-spacing: -0.5px;
margin-bottom: 1rem;
&:hover {
cursor: pointer;
}
svg {
fill: var(--brand-main-light);
}
hy-icon:last-child {
margin-right: 0.75rem;
}
}
.hy-menu-sidepanel__logo-container {
display: flex;
flex-direction: column;
height: 150px;
justify-content: space-between;
margin: 20px;
position: relative;
z-index: 125;
}
@keyframes sidepanel-movein {
from {
display: none;
transform: translateX(-105%);
visibility: hidden;
}
5% {
display: flex;
}
50% {
background-color: rgba(0, 0, 0, 0);
}
to {
background-color: rgba(0, 0, 0, 0.5);
transform: translateX(0);
visibility: visible;
}
}
@keyframes sidepanel-moveout {
from {
background-color: rgba(0, 0, 0, 0.5);
transform: translateX(0);
visibility: visible;
}
75% {
background-color: rgba(0, 0, 0, 0);
}
90% {
transform: translateX(-105%);
}
to {
display: none;
visibility: hidden;
}
}
import {Component, Element, h, State, Listen, Prop} from '@stencil/core';
import {MenuType, SiteLogoSize, ColorVariant} from '../../../utils/utils';
@Component({
tag: 'hy-menu-sidebar',
styleUrl: 'menu-sidebar.scss',
shadow: false,
})
export class MenuSidebar {
@Element() el: HTMLElement;
/**
* Is menu open boolean.
*/
@Prop() menuIsOpen = false;
/**
* Menu type. Defaults to sidenav.
*/
@Prop() menuType: MenuType = MenuType.sidenav;
/**
* Upper menus panel boolean.
*/
@Prop() panelOpen = false;
/**
* Label for panel toggle button.
*/
@Prop() panelToggleLabel?: string;
/**
* Label for panel toggle button.
*/
@Prop() panelToggleAriaLabel?: string;
/**
* Label for panel toggle button.
*/
@Prop() panelToggleCloseLabel?: string;
/**
* Label for panel toggle button.
*/
@Prop() panelToggleCloseAriaLabel?: string;
/**
* Url for logo.
*/
@Prop() logoUrl?: string;
/**
* Logo label
*/
@Prop() logoLabel?: string;
/**
* Logo size
*/
@Prop() size: SiteLogoSize = SiteLogoSize.big;
/**
* Isdemo
*/
@Prop() isDemo: boolean = false;
/**
* Previous panel to be toggled to keep track.
*/
@Prop({mutable: true}) minHeight: any = null;
@State() previousPanel: HTMLElement;
/**
* Previous item to be toggle to keep track.
*/
@State() previousItem;
//@State() previousItem: HTMLElement;
@Listen('panelToggle') panelToggle(e) {
if (this.menuIsOpen) {
this.closeWholeTree();
const body = document.querySelector('body') as HTMLElement;
this.panelOpen = !this.panelOpen;
this.panelOpen
? body.classList.add('hy-menu-sidepanel__no-scroll')
: body.classList.remove('hy-menu-sidepanel__no-scroll');
} else {
this.panelOpen = !this.panelOpen;
const body = document.querySelector('body') as HTMLElement;
this.panelOpen
? body.classList.add('hy-menu-sidepanel__no-scroll')
: body.classList.remove('hy-menu-sidepanel__no-scroll');
}
e.preventDefault();
e.stopPropagation();
}
componentWillLoad() {
const sideBar = document.querySelector('.layout-sidebar-first');
if (this.menuIsOpen) {
sideBar.classList.add('menu-is-open');
} else if (sideBar.classList.contains('menu-is-open')) {
sideBar.classList.remove('menu-is-open');
}
}
componentDidLoad() {
const sidebarContainer = document.querySelector('.hy-menu-sidebar--container');
sidebarContainer.addEventListener('click', (e) => {
if ((e.target as HTMLElement).classList.contains('sidebar-open')) {
this.closeWholeTree();
}
});
const sidepanelContainer = document.querySelector('.hy-menu-sidepanel--container');
sidepanelContainer.addEventListener('click', (e) => {
if ((e.target as HTMLElement).classList.contains('sidepanel-open')) {
this.panelToggle(e);
}
});
const panels = document.querySelectorAll('.hy-menu-level-container');
if (panels) {
const height = Math.max.apply(
Math,
[...panels].map((panel) => panel.scrollHeight)
);
this.minHeight = height;
}
}
componentWillUpdate() {
const sideBar = document.querySelector('.layout-sidebar-first');
if (this.menuIsOpen) {
sideBar.classList.add('menu-is-open');
} else if (sideBar.classList.contains('menu-is-open')) {
sideBar.classList.remove('menu-is-open');
}
}
@Listen('click')
handlePanel(e) {
const target = e.target;
const targetElement = target.tagName.toLowerCase();
const possibleTags = [targetElement].some((r) => ['svg', 'path'].indexOf(r) >= 0);
if (target.classList.contains('hy-menu-item__label__icon') || possibleTags) {
const menuItem = target.closest('.hy-menu-item-sidebar');
const menuItemId = menuItem.getAttribute('data-link-id');
const menuItemLevel = menuItem.getAttribute('item-level');
const menuPanel = menuItem.closest('.hy-menu-level-container');
const childList = menuItem.querySelector('.hy-menu-level-container');
// If no previous, as in first time clicking menu
if (!this.previousPanel) {
this.updatePrevious(menuPanel, menuItem);
this.togglePanel(menuItem, childList);
return;
}
const prevId = this.previousItem.dataset.linkId;
const prevLevel = this.previousItem.getAttribute('item-level');
// If clicking same menu item again.
if (menuItemId === prevId) {
if (menuItem.classList.contains('is-active-item')) {
this.togglePanel(menuItem, childList, true);
if (menuItemLevel == 1) {
this.menuIsOpen = false;
}
} else {
this.togglePanel(menuItem, childList);
}
return;
}
// If clicking menu item on same level or going back up
if (menuItemLevel <= prevLevel) {
// If clicking already active, close children
if (menuItem.classList.contains('is-active-item')) {
this.togglePanel(menuItem, childList, true);
this.updatePrevious(menuPanel, menuItem);
return;
}
// Close previous panels
this.togglePanel(this.previousItem, menuPanel, true);
// Open newly clicked panel
this.togglePanel(menuItem, childList);
// Update previous
this.updatePrevious(menuPanel, menuItem);
} else if (menuItemLevel > prevLevel) {
this.togglePanel(menuItem, childList);
this.updatePrevious(menuPanel, menuItem);
}
}
e.stopImmediatePropagation();
e.stopPropagation();
}
updatePrevious(panel, item) {
this.previousPanel = panel;
this.previousItem = item;
}
closeWholeTree() {
const sidebarLevel1 = document.querySelector('.hy-menu-level-container--level-1') as HTMLElement;
const firstItem = document.querySelector(
'.hy-menu-item-sidebar--sidenav.hy-menu-item--level-1.is-active-item'
) as HTMLElement;
this.togglePanel(firstItem, sidebarLevel1, true);
const body = document.querySelector('body') as HTMLElement;
const container = document.querySelector('.hy-menu-sidebar--container') as HTMLElement;
if (container.classList.contains('sidebar-open') && firstItem) {
container.classList.remove('sidebar-open');
body.classList.remove('hy-menu-sidebar__no-scroll');
}
}
togglePanel(menuItem, panel, shouldClose = false) {
const buttonElement = menuItem.querySelector('button');
const level = menuItem.getAttribute('item-level');
if (level == 1 && !this.menuIsOpen) {
if (window.pageYOffset > 50 && !this.isDemo) {
window.scrollTo({top: 0});
}
this.menuIsOpen = true;
}
if (shouldClose) {
const children = panel.querySelectorAll('.hy-menu-item-sidebar');
const activeChild = [...children].filter((c) => c.classList.contains('is-active-item'));
if (level == 1 && activeChild) {
this.menuIsOpen = false;
}
if (activeChild.length > 0) {
for (let i = 0; i < activeChild.length; i++) {
const element = activeChild[i];
this.closeChildren(element);
}
}
buttonElement.setAttribute('aria-expanded', 'false');
this.closeChildren(menuItem);
//this.menuIsOpen = level == 1 ? true : false;
return;
}
buttonElement.setAttribute('aria-expanded', 'true');
panel.classList.add('is-open');
menuItem.classList.add('is-active-item');
}
closeChildren(menuItem) {
if (menuItem.classList.contains('is-active-item')) {
menuItem.classList.remove('is-active-item');
}
if (menuItem.classList.contains('has-children')) {
const menuButton = menuItem.querySelector('button');
menuButton.setAttribute('aria-expanded', 'false');
const childList = menuItem.querySelector('.hy-menu-level-container');
childList.classList.remove('is-open');
const childItems = childList.querySelectorAll('.hy-menu-item-sidebar');
if (childList.classList.contains('is-active')) {
childList.classList.remove('is-active');
}
childItems.forEach((element) => {
element.classList.remove('is-active-item');
if (element.classList.contains('has-children')) {
const menuButton = element.querySelector('button');
menuButton.setAttribute('aria-expanded', 'false');
this.closeChildren(element);
} else {
return;
}
});
}
}
render() {
const logoColor = ColorVariant.black;
const panelClassAttributes = ['hy-menu-sidepanel--container', this.panelOpen ? 'sidepanel-open' : ''].join(' ');
switch (this.menuType) {
case MenuType.sidepanel:
return (
<div
class="hy-menu-sidebar__container"
style={{
'--minHeight': `${this.minHeight}px` as 'minHeight',
}}
>
<button
aria-label={this.panelToggleAriaLabel}
class="hy-menu-sidebar__panel-toggle"
onClick={(e) => this.panelToggle(e)}
>
<hy-icon icon={'hy-icon-caret-left'} fill={'currentColor'} size={10} />
<hy-icon icon={'hy-icon-caret-left'} fill={'currentColor'} size={10} />
{this.panelToggleLabel}
</button>
<div
class={panelClassAttributes}
aria-hidden={this.panelOpen ? 'false' : 'true'}
aria-expanded={this.panelOpen ? 'true' : 'false'}
>
<div class="hy-menu-sidepanel__nav-container">
<div class="hy-menu-sidepanel__logo-container">
<div
class="hy-menu-sidepanel__logo"
aria-hidden={this.panelOpen ? 'false' : 'true'}
tabindex={this.panelOpen ? '0' : '-1'}
>
<hy-site-logo size={56} color={logoColor} url={this.logoUrl} label={this.logoLabel} />
</div>
<button
aria-label={this.panelToggleCloseAriaLabel}
aria-hidden={this.panelOpen ? 'false' : 'true'}
tabindex={this.panelOpen ? '0' : '-1'}
class="hy-menu-sidebar__panel-toggle"
onClick={(e) => this.panelToggle(e)}
>
<hy-icon icon={'hy-icon-remove'} fill={'currentColor'} size={10} />
{this.panelToggleCloseLabel}
</button>
</div>
<nav
role={'navigation'}
aria-hidden={this.panelOpen ? 'false' : 'true'}
tabindex={this.panelOpen ? '0' : '-1'}
class={{
'hy-menu': true,
'hy-menu-sidepanel': true,
'is-demo': this.isDemo,
}}
>
<slot />
</nav>
</div>
</div>
</div>
);
case MenuType.sidenav:
return (
<div
class="hy-menu-sidebar--container"
style={{
'--minHeight': `${this.minHeight}px` as 'minHeight',
}}
>
<div class="hy-menu-sidebar__logo">
<hy-site-logo size={56} color={logoColor} url={this.logoUrl} label={this.logoLabel} />
</div>
<button
aria-label={this.panelToggleAriaLabel}
class="hy-menu-sidebar__panel-toggle"
onClick={(e) => this.panelToggle(e)}
>
<hy-icon icon={'hy-icon-caret-left'} fill={'currentColor'} size={10} />
<hy-icon icon={'hy-icon-caret-left'} fill={'currentColor'} size={10} />
{this.panelToggleLabel}
</button>
<nav
role={'navigation'}
aria-hidden={this.menuIsOpen ? 'false' : 'true'}
class={{
'hy-menu': true,
'hy-menu-sidebar': true,
'is-demo': this.isDemo,
}}
>
<slot />
</nav>
</div>
);
}
}
}
# hy-menu-sidebar
<!-- Auto Generated Below -->
## Properties
| Property | Attribute | Description | Type | Default |
| --------------------------- | ------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------ |
| `isDemo` | `is-demo` | Isdemo | `boolean` | `false` |
| `logoLabel` | `logo-label` | Logo label | `string` | `undefined` |
| `logoUrl` | `logo-url` | Url for logo. | `string` | `undefined` |
| `menuIsOpen` | `menu-is-open` | Is menu open boolean. | `boolean` | `false` |
| `menuType` | `menu-type` | Menu type. Defaults to sidenav. | `MenuType.desktop \| MenuType.mobile \| MenuType.sidenav \| MenuType.sidepanel \| MenuType.tablet` | `MenuType.sidenav` |
| `minHeight` | `min-height` | Previous panel to be toggled to keep track. | `any` | `null` |
| `panelOpen` | `panel-open` | Upper menus panel boolean. | `boolean` | `false` |
| `panelToggleAriaLabel` | `panel-toggle-aria-label` | Label for panel toggle button. | `string` | `undefined` |
| `panelToggleCloseAriaLabel` | `panel-toggle-close-aria-label` | Label for panel toggle button. | `string` | `undefined` |
| `panelToggleCloseLabel` | `panel-toggle-close-label` | Label for panel toggle button. | `string` | `undefined` |
| `panelToggleLabel` | `panel-toggle-label` | Label for panel toggle button. | `string` | `undefined` |
| `size` | `size` | Logo size | `SiteLogoSize.big \| SiteLogoSize.large \| SiteLogoSize.small` | `SiteLogoSize.big` |
## Dependencies
### Depends on
- [hy-icon](../../icon)
- [hy-site-logo](../../site-header/site-logo)
### Graph
```mermaid
graph TD;
hy-menu-sidebar --> hy-icon
hy-menu-sidebar --> hy-site-logo
hy-site-logo --> hy-icon
style hy-menu-sidebar fill:#f9f,stroke:#333,stroke-width:4px
```
---
Helsinki University Design System
......@@ -86,15 +86,6 @@
padding: 16px 28px;
}
}
&--sidenav {
display: none;
@include breakpoint($extrawide) {
display: block;
max-width: 20vw; // Should be in the wrapper.
}
}
}
.hy-link__donate {
......
......@@ -177,21 +177,6 @@ export class Menu {
</div>
</nav>
);
case MenuType.sidenav:
return (
<nav
role={'navigation'}
aria-hidden={this.menuIsOpen ? 'false' : 'true'}
class={{
'hy-menu': true,
'hy-menu--sidenav': true,
'is-demo': this.isDemo,
}}
>
<slot />
</nav>
);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment