Skip to content

Commit e6ba50b

Browse files
author
Dierk Koenig
committed
example: simpleNavigationProjector.js make the side navigation show/hide.
1 parent d05210d commit e6ba50b

File tree

4 files changed

+62
-17
lines changed

4 files changed

+62
-17
lines changed

docs/css/kolibri-base.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ em { /* emphasis looks like highlighted with text marker */
9292
&::before {
9393
content: "";
9494
position: absolute;
95-
inset: -3px;
95+
inset: 0; /* -3px leads to strange line-break issues */
9696
background-color: var(--kolibri-color-select);
9797
z-index: -10;
9898
rotate: -2deg;

docs/src/examples/navigation/simple/starter.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ siteController.registerPage(URI_HASH_HOME, HomePage());
2121
siteController.registerPage(URI_HASH_UNSTYLED, UnstyledPage());
2222
siteController.registerPage(URI_HASH_MASTER_DETAIL, MasterDetailPage());
2323

24-
SimpleNavigationProjector(siteController, siteProjector.sideNavigationElement);
25-
SimpleNavigationProjector(siteController, siteProjector.topNavigationElement);
24+
SimpleNavigationProjector(siteController, siteProjector.sideNavigationElement, true);
25+
SimpleNavigationProjector(siteController, siteProjector.topNavigationElement, false);
2626

2727
siteController.gotoUriHash(window.location.hash);
2828

docs/src/kolibri/navigation/projector/simple/simpleNavigationProjector.js

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,29 @@
1+
import {select} from "../../../util/dom.js";
12

23
export { SimpleNavigationProjector }
34

45
const PAGE_CLASS = "simpleNavigationProjector";
56

7+
const iconSVGStr = `
8+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
9+
<path d="M8 5L15.0368 11.9632L8 18.9263" stroke="url(#paint0_linear_1028_8530)"/>
10+
<defs>
11+
<linearGradient id="paint0_linear_1028_8530" x1="7.98915" y1="5" x2="18.7337" y2="14.9252" gradientUnits="userSpaceOnUse">
12+
<stop offset="0" stop-color="#FF2CA5"/>
13+
<stop offset="1" stop-color="#6000FF"/>
14+
</linearGradient>
15+
</svg>
16+
`;
17+
618
/**
719
* A projector of anchors to all pages that are registered in the {@link SiteControllerType}.
820
* It binds each anchor to the "visited" state and highlights the currently selected page (uriHash).
921
* The highlighting is part of the style but layouting of the anchors is left to the parent
1022
* such that the same projector can be used for horizontal and vertical display.
1123
* @constructor
1224
* @param { !SiteControllerType } siteController - the source of the information that we display
13-
* @param { !HTMLDivElement } root - where to mount the view
25+
* @param { !HTMLDivElement } root - where to mount the view
26+
* @param { Boolean= } canHide - whether this navigation can hide itself, defaults to false
1427
* @return { NavigationProjectorType }
1528
* @example
1629
* // set up
@@ -24,9 +37,9 @@ const PAGE_CLASS = "simpleNavigationProjector";
2437
* SimpleNavigationProjector(siteController, siteProjector.topNavigationElement);
2538
*/
2639

27-
const SimpleNavigationProjector = (siteController, root) => {
40+
const SimpleNavigationProjector = (siteController, root, canHide=false) => {
2841

29-
root.innerHTML = ` <nav class="${PAGE_CLASS}"></nav> `;
42+
root.innerHTML = `<nav class="${PAGE_CLASS}"></nav> `;
3043

3144
const projectNavigation = () => {
3245

@@ -35,10 +48,10 @@ const SimpleNavigationProjector = (siteController, root) => {
3548
document.head.innerHTML += projectorStyle;
3649
}
3750

38-
const navigationDiv = root.querySelector(`nav.${PAGE_CLASS}`);
51+
const [navigationEl] = select(root, `nav.${PAGE_CLASS}`);
3952

4053
// view is just so many anchors
41-
navigationDiv.innerHTML =
54+
navigationEl.innerHTML = (canHide ? `<div class="toggler">${iconSVGStr}</div>` : '') +
4255
Object.entries(siteController.getAllPages())
4356
.map( ([hash, page]) => `<a href="${hash}">${page.titleText}</a>`)
4457
.join(" ");
@@ -49,13 +62,18 @@ const SimpleNavigationProjector = (siteController, root) => {
4962
Object.entries(siteController.getAllPages())
5063
.forEach( ([hash, page]) => page.onVisited( visited => {
5164
if (!visited) return;
52-
navigationDiv.querySelector(`a[href="${hash}"]`).classList.add("visited");
65+
navigationEl.querySelector(`a[href="${hash}"]`)?.classList?.add("visited");
5366
} ));
5467
// update which anchor shows the current page
5568
siteController.onUriHashChanged((newHash, oldHash) => {
56-
navigationDiv.querySelector(`a[href="${oldHash}"]`)?.classList?.remove("current");
57-
navigationDiv.querySelector(`a[href="${newHash}"]`)?.classList?.add ("current");
69+
navigationEl.querySelector(`a[href="${oldHash}"]`)?.classList?.remove("current");
70+
navigationEl.querySelector(`a[href="${newHash}"]`)?.classList?.add ("current");
5871
});
72+
73+
if (canHide) {
74+
navigationEl.classList.toggle("hide");
75+
select(navigationEl, ".toggler").head().onclick = _evt => navigationEl.classList.toggle("hide");
76+
}
5977
};
6078

6179
projectNavigation();
@@ -65,18 +83,44 @@ const projectorStyle = `
6583
<style data-style-id="${PAGE_CLASS}">
6684
@layer navigationLayer {
6785
.${PAGE_CLASS} {
86+
&.hide {
87+
.toggler {
88+
rotate: 0deg;
89+
}
90+
a, a.current { /* hide the anchors */
91+
width: 0;
92+
color: transparent;
93+
pointer-events: none;
94+
}
95+
}
96+
.toggler { /* provide a box for the svg */
97+
justify-self: center;
98+
width: 2rem;
99+
aspect-ratio: 1 / 1;
100+
rotate: 180deg;
101+
transition: rotate .3s ease-in-out;
102+
}
103+
svg {
104+
fill: none;
105+
stroke-width: 2;
106+
stroke-linecap: round;
107+
stroke-linejoin: round;
108+
}
68109
a {
69-
text-wrap: nowrap;
70-
font-family: system-ui;
110+
color: revert;
111+
pointer-events: revert;
112+
user-select: none;
113+
text-wrap: nowrap;
114+
font-family: system-ui;
71115
}
72116
a.visited {
73-
text-decoration: none;
117+
text-decoration: none;
74118
}
75119
a.visited:not(.current) {
76-
filter: brightness(150%) grayscale(60%);
120+
filter: brightness(150%) grayscale(60%);
77121
}
78122
a.current {
79-
color: var(--kolibri-color-accent, deeppink);
123+
color: var(--kolibri-color-accent, deeppink);
80124
}
81125
}
82126
}

docs/src/kolibri/util/dom.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,10 @@ const fireChangeEvent = element => fireEvent(element, CHANGE);
7878
/** @type InputTypeString */ const COLOR = "color";
7979

8080
/**
81-
* Utility function that works like Element.querySelectorAll but logs a descriptive warning when
81+
* Utility function that works like {@link Element.querySelectorAll} but logs a descriptive warning when
8282
* the resulting NodeList is empty. Wraps the result in a {@link SequenceType } such that the
8383
* Kolibri goodies become available.
84+
* It is a suitable function when a result is **always** expected.
8485
* @param { Element! } element - a DOM element (typically HTMLElement)
8586
* @param { String! } selector - a CSS query selector, might contain operators
8687
* @return { SequenceType<Node> }

0 commit comments

Comments
 (0)