-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Component: vf-mega-menu The initial implementation to address #1718 Starts as a direct port of https://codesandbox.io/s/vf-megamenu-qexed?file=/index.html WIP.
- Loading branch information
1 parent
03a2a68
commit 2fdff72
Showing
16 changed files
with
1,025 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
bin | ||
.github | ||
.travis.yml | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
### 1.0.0-alpha.1 | ||
|
||
* Creates a mega menu. | ||
* https://github.com/visual-framework/vf-core/issues/1718 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# Mega menu component | ||
|
||
[![npm version](https://badge.fury.io/js/%40visual-framework%2Fvf-mega-menu.svg)](https://badge.fury.io/js/%40visual-framework%2Fvf-mega-menu) | ||
|
||
## About | ||
|
||
Paired with a good understanding of a site's information architecture and user journey, the mega menu can empower quick shortcut-style access to popular areas. | ||
|
||
## Usage | ||
|
||
The mega menu should be seen as a empowering but optional feature. While a mega menu may allow a user to quickly move to a sub-section of a website, or laterally move from one silo to another, that empowering ability should be viewed as an optional user journey. | ||
|
||
Some users may fail to notice the mega menu by scrolling past it, be on a mobile device where the menu behaves differently, or the JavaScript-based feature may fail to load making the mega menu inaccessible. | ||
|
||
A user journey should always be possible without the mega menu's content. | ||
|
||
It is recommended to put your mega menu links at the `vf-global-header` level. | ||
|
||
### Caveats | ||
|
||
1. The mega menu currently is not designed to work on mobile | ||
2. In principle any content can be inserted into a mega menu | ||
3. Using more than one mega menu on a page is likely to confuse and overwhelm users | ||
4. A mega menu is not a substitute for a good information architecture | ||
|
||
### Accessibility | ||
|
||
This component targets WCAG 2.1 AA accessibility. | ||
|
||
Hiding critical or essintal information in a mega menu is harmful to users. | ||
|
||
## Install | ||
|
||
This repository is distributed with [npm][https://www.npmjs.com/]. After [installing npm][https://www.npmjs.com/get-npm] and [yarn](https://classic.yarnpkg.com/en/docs/install), you can install `vf-mega-menu` with this command. | ||
|
||
``` | ||
$ yarn add --dev @visual-framework/vf-mega-menu | ||
``` | ||
|
||
### JS | ||
|
||
You should import this component in `./components/vf-component-rollup/scripts.js` or your other JS process: | ||
|
||
```js | ||
import { vfComponentName } from 'vf-mega-menu/vf-mega-menu'; | ||
// Or import directly | ||
// import { vfComponentName } from '../components/raw/vf-mega-menu/vf-mega-menu.js'; | ||
vfComponentName(); // if needed, invoke it | ||
``` | ||
|
||
### Sass/CSS | ||
|
||
The style files included are written in [Sass](https://sass-lang.com/). If you're using a VF-core project, you can import it like this: | ||
|
||
``` | ||
@import "@visual-framework/vf-mega-menu/vf-mega-menu.scss"; | ||
``` | ||
|
||
Make sure you import Sass requirements along with the modules. You can use a [project boilerplate](https://stable.visual-framework.dev/building/) or the [`vf-sass-starter`](https://stable.visual-framework.dev/components/vf-sass-starter/) | ||
|
||
## Help | ||
|
||
- [Read the Visual Framework troubleshooting](https://stable.visual-framework.dev/troubleshooting/) | ||
- [Open a ticket](https://github.com/visual-framework/vf-core/issues) | ||
- [Chat on Slack](https://join.slack.com/t/visual-framework/shared_invite/enQtNDAxNzY0NDg4NTY0LWFhMjEwNGY3ZTk3NWYxNWVjOWQ1ZWE4YjViZmY1YjBkMDQxMTNlNjQ0N2ZiMTQ1ZTZiMGM4NjU5Y2E0MjM3ZGQ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// setup files required | ||
|
||
// sass-lint:disable clean-import-paths | ||
@import 'vf-global-variables'; | ||
@import 'vf-variables'; | ||
@import 'vf-functions'; | ||
@import 'vf-mixins'; | ||
|
||
// component specific styles | ||
|
||
@import 'vf-mega-menu.variables.scss'; | ||
@import 'vf-mega-menu.scss'; | ||
|
||
// component variant styles | ||
// @import 'vf-mega-menu--variant.scss'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"version": "1.0.0-alpha.0", | ||
"name": "@visual-framework/vf-mega-menu", | ||
"description": "vf-mega-menu component", | ||
"homepage": "", | ||
"author": "VF", | ||
"license": "Apache 2.0", | ||
"style": "vf-mega-menu.css", | ||
"sass": "index.scss", | ||
"main": "build/index.js", | ||
"test": "echo \"Error: no test specified\" && exit 1", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"repo": "https://github.com/visual-framework/vf-core/tree/develop/components/vf-mega-menu", | ||
"bugs": { | ||
"url": "https://github.com/visual-framework/vf-core/issues" | ||
}, | ||
"keywords": [ | ||
"fractal", | ||
"component" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# The title shown on the component page | ||
title: Mega menu | ||
# Label shown on index pages | ||
label: Mega menu | ||
status: alpha | ||
# The template used from /components/_previews | ||
# | ||
# Per-variant options | ||
# variants: | ||
# - name: default | ||
# label: Default | ||
# hidden: true | ||
# context: | ||
# children_are_possible: | ||
# variant_title: A Easy Card Title 1 | ||
# variant_href: "JavaScript:Void(0);" | ||
# modifier: vf-card--very-easy | ||
# variant_image: ../../assets/vf-card/assets/vf-card-example.png | ||
# Global component context | ||
context: | ||
component-type: container | ||
# custom-values: passed as {{custom-values}} | ||
# - note: you in your custom-values you should use dashes `-` | ||
# and not underscores `_` as underscores prevent inherited template use | ||
# title: Title text | ||
# text: String of text | ||
# image: ../../assets/vf-component-name/assets/vf-component-name.png | ||
# - note on paths: be sure to prefix with `../../` | ||
# - Why? https://github.com/visual-framework/vf-core/issues/364 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// vf-mega-menu | ||
|
||
// Don't need JS? Then feel free to delete this file. | ||
|
||
/* | ||
* A note on the Visual Framework and JavaScript: | ||
* The VF is primarily a CSS framework so we've included only a minimal amount | ||
* of JS in components and it's fully optional (just remove the JavaScript selectors | ||
* i.e. `data-vf-js-tabs`). So if you'd rather use Angular or Bootstrap for your | ||
* tabs, the Visual Framework won't get in the way. | ||
* | ||
* When querying the DOM for elements that should be acted on: | ||
* 🚫 Don't: const tabs = document.querySelectorAll('.vf-tabs'); | ||
* ✅ Do: const tabs = document.querySelectorAll('[data-vf-js-tabs]'); | ||
* | ||
* This allows users who would prefer not to have this JS engage on an element | ||
* to drop `data-vf-js-component` and still maintain CSS styling. | ||
*/ | ||
|
||
// Uncomment this boilerplate | ||
// // if you need to import any other components' JS to use here | ||
// import { vfOthercomponent } from vfImportPrefix + '../vf-other-component/vf-other-component'; | ||
// | ||
|
||
function initMegaMenu(megaMenuComponent) { | ||
//add activated class to this mega menu. This will help us differentiate when menu is processed with JS and when its not. | ||
megaMenuComponent.classList.add("vf-mega-menu__activated"); | ||
|
||
let previousMenuLinkComponent, previousExpandedSectionComponent; | ||
|
||
function getWidth() { | ||
return Math.max( | ||
document.body.scrollWidth, | ||
document.documentElement.scrollWidth, | ||
document.body.offsetWidth, | ||
document.documentElement.offsetWidth, | ||
document.documentElement.clientWidth | ||
); | ||
} | ||
|
||
megaMenuComponent.querySelectorAll("[data-vf-js-mega-menu-section-id]").forEach(item => { | ||
item.addEventListener('click', event => { | ||
// For now we do not show the mega menu on mobile. | ||
// We still need to decide on approach for mobile support. | ||
if (getWidth() > 768) { | ||
event.preventDefault(); | ||
event.vfMegaMenuLink = true; | ||
const { target: linkComponent } = event; | ||
const newReferences = handleMenuClick( | ||
linkComponent, | ||
previousMenuLinkComponent, | ||
previousExpandedSectionComponent | ||
); | ||
previousMenuLinkComponent = newReferences?.previousMenuLinkComponent; | ||
previousExpandedSectionComponent = | ||
newReferences?.previousExpandedSectionComponent; | ||
} else { | ||
console.warn(`vf-mega-menu: No mega menu shown. Mega menu is currently alpha and not supported on small screen sizes. Your screens size is ${getWidth()}`); | ||
} | ||
// console.log({ linkComponent }); | ||
// console.log({ previousMenuLinkComponent }); | ||
// const associatedSection = linkComponent.getAttribute( | ||
// "data-vf-js-mega-menu-section-id" | ||
// ); | ||
}); | ||
}); | ||
|
||
} | ||
|
||
|
||
|
||
|
||
function handleMenuClick( | ||
menuItemComponent, | ||
previousMenuLinkComponent, | ||
previousExpandedSectionComponent | ||
) { | ||
// console.log("expand / collapse"); | ||
// debugger; | ||
const sectionAttribute = menuItemComponent.getAttribute( | ||
"data-vf-js-mega-menu-section-id" | ||
); | ||
const section = document.querySelector( | ||
`[data-vf-js-mega-menu-section="${sectionAttribute}"]` | ||
); | ||
// console.log("section", section, sectionAttribute); | ||
|
||
if (!section) { | ||
return; | ||
} | ||
|
||
// Capture clicks on things other than mega menu elements | ||
// https://www.blustemy.io/detecting-a-click-outside-an-element-in-javascript/ | ||
document.addEventListener("click", (evt) => { | ||
let targetElement = evt.target; // clicked element | ||
do { | ||
if (targetElement == section || evt.vfMegaMenuLink == true) { | ||
// This is a click inside. Do nothing, just return. | ||
// console.log("Clicked inside!") | ||
return; | ||
} | ||
// Go up the DOM | ||
targetElement = targetElement.parentNode; | ||
} while (targetElement); | ||
|
||
// This is a click outside. | ||
// console.log("Clicked outside!") | ||
if (section.getAttribute("aria-hidden") === "false") { | ||
// console.log("hiding!") | ||
section.setAttribute("aria-hidden", "true"); | ||
} | ||
}); | ||
|
||
//0. if section is visible, just hide it | ||
if (section.getAttribute("aria-hidden") === "false") { | ||
section.setAttribute("aria-hidden", "true"); | ||
menuItemComponent.classList.remove("is-expanded"); | ||
return; | ||
} | ||
|
||
//1. section is hidden. Hide all sections | ||
if (previousExpandedSectionComponent) { | ||
previousExpandedSectionComponent.setAttribute("aria-hidden", "true"); | ||
} | ||
|
||
//2. remove highlight from previous link | ||
if (previousMenuLinkComponent) { | ||
previousMenuLinkComponent.classList.remove("is-expanded"); | ||
} | ||
|
||
//3. show new section and add class to new link | ||
section.setAttribute("aria-hidden", "false"); | ||
menuItemComponent.classList.add("is-expanded"); | ||
|
||
//4. return new link and section components to be stored as previous | ||
return { | ||
previousMenuLinkComponent: menuItemComponent, | ||
previousExpandedSectionComponent: section | ||
}; | ||
} | ||
|
||
// function createMenuSectionMap(megaMenuComponent) { | ||
// const allMenuComponents = megaMenuComponent.querySelectorAll( | ||
// "[data-vf-js-mega-menu-section-id]" | ||
// ); | ||
|
||
// const menuSectionsMap = new Map(); | ||
// allMenuComponents.forEach((component) => { | ||
// const sectionAttribute = component.getAttribute( | ||
// "data-vf-js-mega-menu-section-id" | ||
// ); | ||
// const section = megaMenuComponent.querySelector( | ||
// `[data-vf-js-mega-menu-section="${sectionAttribute}"]` | ||
// ); | ||
// menuSectionsMap.set(component, section); | ||
// }); | ||
// return menuSectionsMap; | ||
// } | ||
|
||
/** | ||
* The global function for this component | ||
* @example vfMegaMenu(firstPassedVar) | ||
* @param {string} [firstPassedVar] - An option to be passed | ||
*/ | ||
function vfMegaMenu(firstPassedVar) { | ||
firstPassedVar = firstPassedVar || 'defaultVal'; | ||
|
||
const allMegaMenuComponents = document.querySelectorAll("[data-vf-js-mega-menu]") || []; | ||
|
||
//for each mega-menu | ||
allMegaMenuComponents.forEach(initMegaMenu); | ||
} | ||
|
||
// // If you need to invoke the component by default | ||
// vfMegaMenu(); | ||
|
||
// By default your component should be usable with js imports | ||
export { vfMegaMenu }; | ||
|
||
// You should also import it at ./components/vf-component-rollup/scripts.js | ||
// import { vfMegaMenu } from 'vf-mega-menu/vf-mega-menu'; | ||
// Or import directly | ||
// import { vfMegaMenu } from '../components/raw/vf-mega-menu/vf-mega-menu.js'; | ||
// And, if needed, invoke it | ||
// vfMegaMenu(); | ||
|
Oops, something went wrong.