Unverified Commit 7f83795b authored by Ken Hawkins's avatar Ken Hawkins Committed by GitHub
Browse files

feature: vf-navigation--on-this-page (#1711)

* feature: vf-navigation--on-this-page

Introduces a refined `vf-navigation--on-this-page` that has been trialled on https://wwwdev.embl.org/directory/ and will replace `vf-links-list-easy`

Adresses: #1623
parent c6e8a275
Pipeline #211308 passed with stages
in 8 minutes and 11 seconds
......@@ -89,7 +89,6 @@ button {
@import 'vf-masthead/vf-masthead.scss';
@import 'vf-navigation/vf-navigation.scss';
@import 'vf-navigation/vf-navigation--additional.scss';
@import 'vf-section-header/vf-section-header.scss';
@import 'vf-activity-list/vf-activity-list.scss';
......
......@@ -58,6 +58,9 @@ vfBackToTop();
import { vfDropdown } from "vf-dropdown/vf-dropdown.js";
vfDropdown();
import { vfNavigationOnThisPage } from "vf-navigation/vf-navigation.js";
vfNavigationOnThisPage();
import { emblContentMetaProperties_Read } from "embl-content-meta-properties/embl-content-meta-properties";
import { emblNotifications } from "embl-notifications/embl-notifications";
......
### 3.6.3
* Fix documentation for sassFunction interactive-color
* https://github.com/visual-framework/vf-core/pull/1711
### 3.6.2
* Add `interactive-color` tokens.
......
......@@ -8,6 +8,7 @@
"meta": {
"friendlyName": "Interactive",
"sassFunction": "interactive-color",
"sassMap": "default",
"CSSCustomProperty": "--vf-color__interactive"
}
},
......
......@@ -11,6 +11,7 @@ props:
meta:
friendlyName: Interactive
sassFunction: interactive-color
sassMap: default
CSSCustomProperty: --vf-color__interactive
- name: vf-color__interactive--background
......
### 3.0.1
* Removes usage of deleted `vf-navigation--additional` variant.
### 3.0.0
* vf-header was an early concept on how we might accommodate multi-layers of branding ... but:
......
......@@ -4,6 +4,5 @@
{% render '@vf-masthead' %}
{% render '@vf-navigation--main' %}
{% render '@vf-navigation--additional' %}
</header>
### 3.0.0-rc.1
* Introduces `vf-navigation--on-this-page`.
* Removes `vf-navigation--additional`.
* https://github.com/visual-framework/vf-core/issues/1623
### 3.0.0-beta.2
* Minor README.md update.
......
......@@ -8,17 +8,33 @@ The `vf-navigation` component is a horizontal list of links to key pages of the
## Usage
### Global Navigation
### Global navigation variant
This variant of the `vf-navigation` is to be used as part of the `vf-global-header` to give a few 'quick links' that will be on every page.
### Main Navigation
### Main navigation variant
This variant of the `vf-navigation` can be used to link to sections of the site, or part of the site the parent section.
This is typically placed below the `vf-hero` component but can be also found below the `vf-global-header`.
There should be only one use of `vf-navigation--main` on a page.
### On this page navigation variant
This sticky element allows users to quickly jump between sections on longer pages.
There should be only one use of `vf-navigation--on-this-page` on a page.
Using the `vfNavigationOnThisPage` JavaScript adds a quality-of-life improvement by automatically activating sections as the user scrolls them into view on the page.
For this feature to work, anchor targets need two things:
1. a data attribute: `data-vf-js-navigation-on-this-page="true"`
2. an element id: `id="container-1"`
These could be added to any item on the page, but would most logically be added to a `vf-grid`, `embl-grid` or `vf-section-header`
## Install
This component is distributed with npm. After [installing npm](https://www.npmjs.com/get-npm), you can install the `vf-navigation` with this command.
......@@ -37,6 +53,18 @@ The source files included are written in [Sass](http://sass-lang.com)(`scss`). Y
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/)
### JavaScript
If using the `vf-navigation--on-this-page` variant, you should import this component in `./components/vf-component-rollup/scripts.js` or your other JS process:
```js
import { vfNavigationOnThisPage } from "vf-navigation/vf-navigation.js";
// Or import directly
// import { vfGaIndicateLoaded } from '../components/raw/vf-navigation/vf-navigation.js';
vfNavigationOnThisPage();
```
## Help
- [Read the Visual Framework troubleshooting](https://stable.visual-framework.dev/troubleshooting/)
......
......@@ -15,4 +15,3 @@
@import 'vf-navigation.variables.scss';
@import 'vf-navigation.scss';
@import 'vf-navigation--additional.scss';
html:not(.vf-disable-deprecated) {
@warn 'The variant has been deprecated.';
@import 'vf-navigation.variables.scss';
.vf-navigation--additional {
background-color: color(grey--dark);
box-sizing: border-box;
display: flex;
margin: 0 auto;
max-width: $vf-layout--comfortable;
padding: 4px 0;
position: relative;
text-transform: uppercase;
width: 100%;
.vf-navigation__heading {
color: ui-color(white);
margin: 0;
padding-top: 6px; // magic number
z-index: set-layer(vf-z-index--pseudo-element-fix);
}
.vf-navigation__list {
margin-left: auto;
padding-top: 5px; // magic number
z-index: set-layer(vf-z-index--pseudo-element-fix);
}
.vf-navigation__item:not(:first-child) {
margin-left: 24px;
}
.vf-navigation__link {
@include inline-link(
$vf-link--color: ui-color(off-white),
$vf-link--visited-color: ui-color(white),
$vf-link--hover-color: ui-color(white)
);
text-decoration: none;
}
}
}
// this variant has been removed
{% render '@vf-navigation', {
"classModifier": "on-this-page",
"heading": heading,
"navigation": navigation,
"activateJavascript": activateJavascript
} %}
<section class="embl-grid embl-grid--has-sidebar" data-vf-js-navigation-on-this-page="true" id="container-1">
{% render '@vf-section-header', {
"section_title": "Home",
"href": "container-1"
} %}
<div>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eget massa dolor. Morbi suscipit lorem in volutpat
sollicitudin. Pellentesque cursus consequat mi, sed lobortis enim aliquet ac. Morbi eu tincidunt lorem. Cras eget
leo rhoncus, ultrices erat quis, commodo lorem. Fusce placerat urna vitae eleifend tincidunt. Integer lobortis
erat in ante mollis, in tincidunt erat varius. Cras quis erat ac lorem mollis vulputate. Fusce pulvinar
condimentum cursus.
</p>
<p>
Vivamus gravida scelerisque nibh eu blandit. Vivamus et varius mi. Vivamus ut dictum leo, vitae commodo leo.
Curabitur sed diam tristique velit laoreet cursus. Pellentesque ultricies ullamcorper neque malesuada scelerisque.
Proin varius sodales ante, sed accumsan risus dapibus sed. Nam eget dignissim tortor, sed pulvinar sapien. In
congue, augue in lacinia congue, nunc lacus sagittis lacus, et gravida tortor nisl vitae turpis. Donec id libero
tempor orci bibendum interdum. Sed nisl purus, tempor quis enim quis, laoreet fermentum lacus.
</p>
</div>
</section>
<section class="embl-grid embl-grid--has-sidebar" data-vf-js-navigation-on-this-page="true" id="container-2">
{% render '@vf-section-header', {
"section_title": "Downloads",
"href": "JavaScript:Void(0);"
} %}
<div>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eget massa dolor. Morbi suscipit lorem in volutpat
sollicitudin. Pellentesque cursus consequat mi, sed lobortis enim aliquet ac. Morbi eu tincidunt lorem. Cras eget
leo rhoncus, ultrices erat quis, commodo lorem. Fusce placerat urna vitae eleifend tincidunt. Integer lobortis
erat in ante mollis, in tincidunt erat varius. Cras quis erat ac lorem mollis vulputate. Fusce pulvinar
condimentum cursus. Proin quis ligula lacinia, elementum eros dignissim, pretium libero. Vivamus dignissim nisi
urna, sed elementum nisi egestas sed. Nulla at sagittis leo. Sed ut odio a libero pretium tempus at in odio.
Praesent suscipit tellus urna, non eleifend enim fringilla in. Ut interdum, arcu a suscipit accumsan, metus felis
aliquet tellus, quis maximus est massa nec eros. Duis egestas, elit ut tempor lobortis, dolor ligula iaculis
nulla, at hendrerit enim diam eu sapien.
</p>
<p>
Cras ultrices cursus odio, vel cursus justo ultrices ut. Vestibulum non eros eu nunc tristique bibendum. Integer
leo neque, varius et varius molestie, mattis eu orci. Suspendisse ullamcorper augue ut ex eleifend iaculis. Donec
lacus dolor, sodales id urna ut, placerat eleifend nisi. Aliquam sollicitudin nibh sodales ex sagittis aliquet.
Etiam nec tempor metus. Nam quis tincidunt massa, fringilla fringilla metus. Orci varius natoque penatibus et
magnis dis parturient montes, nascetur ridiculus mus.
</p>
</div>
</section>
<section class="embl-grid embl-grid--has-sidebar" data-vf-js-navigation-on-this-page="true" id="container-3">
{% render '@vf-section-header', {
"section_title": "Release notes",
"href": "JavaScript:Void(0);"
} %}
<div>
<p>
Sed fringilla pretium quam vehicula viverra. Etiam sed augue vitae ante consectetur auctor id eget urna.
Vestibulum bibendum vulputate ultrices. Proin euismod est felis, eu egestas ligula laoreet eu. Sed eget tincidunt
libero, in posuere mauris. Phasellus vel urna faucibus nunc congue pulvinar. Aliquam massa ipsum, euismod semper
arcu nec, luctus hendrerit nisl. Vivamus ultrices mattis dui at sagittis. Phasellus ac erat dui. Aliquam erat
volutpat. Cras ac elit non quam maximus congue. Sed mi justo, bibendum eu lacinia nec, lobortis ac purus. Duis
elementum orci sit amet pulvinar malesuada. Mauris luctus risus et ipsum eleifend pharetra.
</p>
<p>
Cras ultrices cursus odio, vel cursus justo ultrices ut. Vestibulum non eros eu nunc tristique bibendum. Integer
leo neque, varius et varius molestie, mattis eu orci. Suspendisse ullamcorper augue ut ex eleifend iaculis. Donec
lacus dolor, sodales id urna ut, placerat eleifend nisi. Aliquam sollicitudin nibh sodales ex sagittis aliquet.
Etiam nec tempor metus. Nam quis tincidunt massa, fringilla fringilla metus. Orci varius natoque penatibus et
magnis dis parturient montes, nascetur ridiculus mus.
</p>
</div>
</section>
<section class="embl-grid embl-grid--has-sidebar" data-vf-js-navigation-on-this-page="true" id="container-4">
{% render '@vf-section-header', {
"section_title": "Another item",
"href": "JavaScript:Void(0);"
} %}
<div>
<p>
Cras ultrices cursus odio, vel cursus justo ultrices ut. Vestibulum non eros eu nunc tristique bibendum. Integer
leo neque, varius et varius molestie, mattis eu orci. Suspendisse ullamcorper augue ut ex eleifend iaculis. Donec
lacus dolor, sodales id urna ut, placerat eleifend nisi. Aliquam sollicitudin nibh sodales ex sagittis aliquet.
Etiam nec tempor metus. Nam quis tincidunt massa, fringilla fringilla metus. Orci varius natoque penatibus et
magnis dis parturient montes, nascetur ridiculus mus.
</p>
</div>
</section>
......@@ -46,22 +46,35 @@ variants:
- text: Feedback
navigation_href: JavaScript:Void(0);
- name: additional
status: deprecated
hidden: true
- name: on-this-page
hidden: false
status: alpha
context:
component-type: deprecated
heading: On this page
classModifier: on-this-page
activateJavascript: false
navigation:
- text: Anchor 1
navigation_href: "#container-1"
- text: Anchor 2
navigation_href: "#container-2"
currentSection: true
- text: Anchor 3
navigation_href: "#container-3"
heading: European Bioinformatics Institute
classModifier: additional
title: European Bioinformatics Institute
- name: on-this-page--example
context:
isExample: true
heading: On this page
classModifier: on-this-page
activateJavascript: true
navigation:
- text: services
navigation_href: JavaScript:Void(0);
- text: research
navigation_href: JavaScript:Void(0);
- text: training
navigation_href: JavaScript:Void(0);
- text: about us
navigation_href: JavaScript:Void(0);
- text: Home
navigation_href: "#container-1"
currentSection: true
- text: Download
navigation_href: "#container-2"
- text: Release notes
navigation_href: "#container-3"
- text: Another item
navigation_href: "#container-4"
// vf-navigation
// only required for vf-navigation--on-this-page
/**
* Function for JS scroll to top functionality
* That must be executed exactly once
* @example vfNavigationOnThisPage()
*/
export function vfNavigationOnThisPage() {
var sectionList = document.querySelectorAll("[data-vf-js-navigation-on-this-page-container='true'],[data-vf-js-navigation-on-this-page-container='1']")[0];
var section = document.querySelectorAll("[data-vf-js-navigation-on-this-page]");
var sectionPositions = {};
var i = 0;
if (!sectionList || !section) {
// exit: either sections or section content not found
return;
}
if (sectionList.length == 0 || section.length == 0) {
// exit: either sections or section content not found
return;
}
section.forEach(e => {
sectionPositions[e.id] = e.offsetTop;
});
function activateNavigationItem() {
var scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;
for (i in sectionPositions) {
// this implements a scrollspy concept based on https://codepen.io/zchee/pen/ogzvZZ
if (sectionPositions[i] <= scrollPosition + 70) {
// we activate the section 70 pixels before coming into view, as the sticky bar will cover it
// only add remove the class if there is a new section to activate
if (sectionList.querySelector("a[href*=" + i + "]") != null) {
sectionList.querySelector("[aria-selected]").removeAttribute("aria-selected");
sectionList.querySelector("a[href*=" + i + "]").setAttribute("aria-selected", "true");
}
}
}
}
window.onscroll = function() {
// we could introduce throttling, but as this is a fairly simple repaint, throttling is not likely required
window.requestAnimationFrame(activateNavigationItem);
};
}
......@@ -2,26 +2,33 @@
{% set classModifier = context.classModifier %}
{% set heading = context.heading %}
{% set navigation = context.navigation %}
{% set activateJavascript = context.activateJavascript %}
{%- endif -%}
<nav class="vf-navigation
{%- if classModifier %} vf-navigation--{{ classModifier }}{% endif -%}
{%- if classModifier == 'additional' %} | vf-u-fullbleed{% else %} | vf-cluster{%- endif -%}">
{% if heading %}
<h3 class="vf-navigation__heading">{{ heading }}</h3>
{% endif %}
<ul class="vf-navigation__list | vf-list | vf-cluster__inner">
{% for item in navigation %}
{%- if classModifier == 'on-this-page' %} | vf-u-fullbleed{%- endif %} | vf-cluster">
<ul class="vf-navigation__list | vf-list | vf-cluster__inner"
{%- if classModifier == 'on-this-page' and activateJavascript == true %} data-vf-js-navigation-on-this-page-container="true"{%- endif -%}
{%- if classModifier == 'on-this-page' and activateJavascript == false %} data-vf-js-navigation-on-this-page-container="false"{%- endif -%}
>
{% if heading -%}
<li class="vf-navigation__item">
{# <h3 class="vf-navigation__heading"></h3> #}
{{ heading }}
</li>
{%- endif %}
{%- for item in navigation %}
<li class="vf-navigation__item">
<a
href="{{ item.navigation_href }}"
class="vf-navigation__link"
{% if item.currentPage %} aria-current="page"{%- endif -%}>
{%- if item.currentPage %} aria-current="page"{%- endif -%}
{%- if item.currentSection %} aria-selected="true"{%- endif -%}
>
{{- item.text -}}
</a>
</li>
{% endfor %}
{%- endfor %}
</ul>
</nav>
......@@ -11,14 +11,6 @@
@import 'vf-navigation.variables.scss';
.vf-navigation__item {
[aria-current='page'] {
color: neutral(900);
text-shadow: 1px 0 0 currentColor; // faux bold so there won't be any layour shift
}
}
.vf-navigation__link {
@include inline-link(
$vf-link--color: neutral(700),
......@@ -32,6 +24,13 @@
}
}
.vf-navigation__item {
[aria-current='page'],
[aria-selected='true'] {
color: interactive-color();
text-shadow: 1px 0 0 currentColor; // faux bold so there won't be any layout shift
}
}
.vf-navigation--main {
.vf-navigation__link {
@include set-type(text-body--2, $custom-margin-bottom: 0);
......@@ -45,3 +44,69 @@
margin-left: auto;
}
}
// On this page variant
.vf-navigation--on-this-page.vf-u-fullbleed {
// conflict with vf-u-fullbleed
position: sticky;
}
.vf-navigation--on-this-page.vf-u-fullbleed {
margin-bottom: map-get($vf-spacing-map, vf-spacing--400);
overflow-x: auto;
overflow-y: hidden;
position: sticky;
top: 0;
background: white;
z-index: 1001;
.vf-navigation__link {
&:focus,
&:hover {
text-decoration: none;
text-shadow: 1px 0 0 currentColor; // faux bold so there won't be any layout shift
text-decoration: underline;
text-decoration-thickness: 4px;
text-underline-offset: .5em;
text-underline-position: under;
text-decoration-color: neutral(900);
}
}
.vf-navigation__link[aria-selected='true'] {
text-decoration: underline;
text-decoration-thickness: 4px;
text-underline-offset: .5em;
text-underline-position: under;
text-decoration-color: interactive-color();
}
> .vf-list {
flex-wrap: nowrap;
// margin-right: 0 !important;
}
[class*="__item"] {
flex-shrink: 0;
}
}
@media only screen and (min-width: $vf-breakpoint--md) {
.vf-navigation--on-this-page.vf-u-fullbleed {
overflow: unset; // allow shadow to bleed out
&::before {
box-shadow: 0 0 2px 1px rgba(0, 0, 0, .2);
}
}
}
// make it work right on mobile
@media only screen and (max-width: $vf-breakpoint--md) {
.vf-navigation--on-this-page.vf-u-fullbleed {
@include padding(map-get($vf-spacing-map, vf-spacing--200),map-get($vf-spacing-map, vf-spacing--800),map-get($vf-spacing-map, vf-spacing--200),map-get($vf-spacing-map, vf-spacing--500));
margin-left: -1em; // inverse of vf-body padding
width: calc(100% - 1em);
}
// .vf-navigation--on-this-page.vf-u-fullbleed::before {
// box-shadow: none;
// display: none;
// }
}
......@@ -292,7 +292,7 @@ import { {{ component.baseHandle | camelize(true) | replace("-", "") }} } from "
"href": "#" + variant.handle
} %}
<article class="vf-u-padding--400"
style="overflow: auto; background-image: linear-gradient(45deg, rgba(59, 111, 182, .1) 25%, transparent 25%), linear-gradient(135deg, rgba(59, 111, 182, .1) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(59, 111, 182, .1) 75%), linear-gradient(135deg, transparent 75%, rgba(59, 111, 182, .1) 75%);background-size: 20px 20px; background-position-x: 0px, 10px, 10px, 0px;background-position-y: 0px, 0px, -10px, 10px;"
style="background-image: linear-gradient(45deg, rgba(59, 111, 182, .1) 25%, transparent 25%), linear-gradient(135deg, rgba(59, 111, 182, .1) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(59, 111, 182, .1) 75%), linear-gradient(135deg, transparent 75%, rgba(59, 111, 182, .1) 75%);background-size: 20px 20px; background-position-x: 0px, 10px, 10px, 0px;background-position-y: 0px, 0px, -10px, 10px;"
>
{# When you want a box with a slight shadow #}
{% render '@'+variant.handle, variant.context %}
......
......@@ -23,7 +23,6 @@
{% endif -%}
<section class="vf-stack | vf-card__content" style="--vf-stack-margin: .5rem;">
<h3>{{ item.meta.friendlyName }}</h3>
<hr class="vf-divider">
{%- if type == "typography" -%}
<p class="vf-card__text"><span>Font Size: </span><code class="vf-code-example">{{ item.value.fontSize }}</code></p>
<p class="vf-card__text"><span>Font Weight: </span><code class="vf-code-example">{{ item.value.fontWeight }}</code></p>
......@@ -33,7 +32,6 @@
<code class="vf-code-example">{{ item.value }}</code></p>
{% endif %}
<hr class="vf-divider">
{% if item.meta.sassVariable %}
<p class="vf-card__text">Sass variable:
<code class="vf-code-example">${{ item.meta.sassVariable }}</code></p>
......@@ -44,7 +42,6 @@
<code class="vf-code-example">{{item.meta.sassFunction}}({{ item.meta.sassMap }})</code></p>
{% endif %}
{% if item.meta.CSSCustomProperty %}
<hr class="vf-divider">
<p class="vf-card__text">CSS custom property:
<code class="vf-code-example">{{ item.meta.CSSCustomProperty }}</code></p>
{% endif %}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment