Lucien Cartier-Tilet
16f23c08c6
The code in `navbar.dart' is now more declarative and cleaner. If the `lastUpdate' variable’s content change, then it will force the user to reload the page, bypassing their cache. This can be useful in case of an update with which the cache could break the website.
178 lines
10 KiB
Dart
178 lines
10 KiB
Dart
import 'dart:html' show querySelector, Element, window;
|
||
import 'dart:svg' show SvgElement;
|
||
|
||
// You will see here and there some 'tabindex' attributes added to various HTML
|
||
// elements, and I’m sure you will ask "Why? They don’t serve any purpose, do
|
||
// they?". Well you are wrong. Webkit has a **terrible** implementation of
|
||
// `:focus-within`, and the dropdowns will **not** work unless the parent
|
||
// element, in this case a `<li>` has a `tabindex` attribute set to `0` and its
|
||
// first child set to `-1`.
|
||
//
|
||
// Screw WebKit, and screw Apple for using such a terrible web engine.
|
||
|
||
// Icons from https://materialdesignicons.com/
|
||
final icons = {
|
||
'home': SvgElement.svg(
|
||
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z" /></svg>'),
|
||
'pages': SvgElement.svg(
|
||
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M19,2L14,6.5V17.5L19,13V2M6.5,5C4.55,5 2.45,5.4 1,6.5V21.16C1,21.41 1.25,21.66 1.5,21.66C1.6,21.66 1.65,21.59 1.75,21.59C3.1,20.94 5.05,20.5 6.5,20.5C8.45,20.5 10.55,20.9 12,22C13.35,21.15 15.8,20.5 17.5,20.5C19.15,20.5 20.85,20.81 22.25,21.56C22.35,21.61 22.4,21.59 22.5,21.59C22.75,21.59 23,21.34 23,21.09V6.5C22.4,6.05 21.75,5.75 21,5.5V7.5L21,13V19C19.9,18.65 18.7,18.5 17.5,18.5C15.8,18.5 13.35,19.15 12,20V13L12,8.5V6.5C10.55,5.4 8.45,5 6.5,5V5Z" /></svg>'),
|
||
'toc': SvgElement.svg(
|
||
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M3,4H7V8H3V4M9,5V7H21V5H9M3,10H7V14H3V10M9,11V13H21V11H9M3,16H7V20H3V16M9,17V19H21V17H9"/></svg>'),
|
||
'share': SvgElement.svg(
|
||
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19A2.92,2.92 0 0,0 18,16.08Z" /></svg>'),
|
||
'twitter': SvgElement.svg(
|
||
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z" /></svg>'),
|
||
'reddit': SvgElement.svg(
|
||
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M14.5 15.41C14.58 15.5 14.58 15.69 14.5 15.8C13.77 16.5 12.41 16.56 12 16.56C11.61 16.56 10.25 16.5 9.54 15.8C9.44 15.69 9.44 15.5 9.54 15.41C9.65 15.31 9.82 15.31 9.92 15.41C10.38 15.87 11.33 16 12 16C12.69 16 13.66 15.87 14.1 15.41C14.21 15.31 14.38 15.31 14.5 15.41M10.75 13.04C10.75 12.47 10.28 12 9.71 12C9.14 12 8.67 12.47 8.67 13.04C8.67 13.61 9.14 14.09 9.71 14.08C10.28 14.08 10.75 13.61 10.75 13.04M14.29 12C13.72 12 13.25 12.5 13.25 13.05S13.72 14.09 14.29 14.09C14.86 14.09 15.33 13.61 15.33 13.05C15.33 12.5 14.86 12 14.29 12M22 12C22 17.5 17.5 22 12 22S2 17.5 2 12C2 6.5 6.5 2 12 2S22 6.5 22 12M18.67 12C18.67 11.19 18 10.54 17.22 10.54C16.82 10.54 16.46 10.7 16.2 10.95C15.2 10.23 13.83 9.77 12.3 9.71L12.97 6.58L15.14 7.05C15.16 7.6 15.62 8.04 16.18 8.04C16.75 8.04 17.22 7.57 17.22 7C17.22 6.43 16.75 5.96 16.18 5.96C15.77 5.96 15.41 6.2 15.25 6.55L12.82 6.03C12.75 6 12.68 6.03 12.63 6.07C12.57 6.11 12.54 6.17 12.53 6.24L11.79 9.72C10.24 9.77 8.84 10.23 7.82 10.96C7.56 10.71 7.2 10.56 6.81 10.56C6 10.56 5.35 11.21 5.35 12C5.35 12.61 5.71 13.11 6.21 13.34C6.19 13.5 6.18 13.62 6.18 13.78C6.18 16 8.79 17.85 12 17.85C15.23 17.85 17.85 16.03 17.85 13.78C17.85 13.64 17.84 13.5 17.81 13.34C18.31 13.11 18.67 12.6 18.67 12Z" /></svg>'),
|
||
'email': SvgElement.svg(
|
||
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M20,8L12,13L4,8V6L12,11L20,6M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4Z" /></svg>'),
|
||
'linkedin': SvgElement.svg(
|
||
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M19 3A2 2 0 0 1 21 5V19A2 2 0 0 1 19 21H5A2 2 0 0 1 3 19V5A2 2 0 0 1 5 3H19M18.5 18.5V13.2A3.26 3.26 0 0 0 15.24 9.94C14.39 9.94 13.4 10.46 12.92 11.24V10.13H10.13V18.5H12.92V13.57C12.92 12.8 13.54 12.17 14.31 12.17A1.4 1.4 0 0 1 15.71 13.57V18.5H18.5M6.88 8.56A1.68 1.68 0 0 0 8.56 6.88C8.56 5.95 7.81 5.19 6.88 5.19A1.69 1.69 0 0 0 5.19 6.88C5.19 7.81 5.95 8.56 6.88 8.56M8.27 18.5V10.13H5.5V18.5H8.27Z" /></svg>'),
|
||
'facebook': SvgElement.svg(
|
||
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2.04C6.5 2.04 2 6.53 2 12.06C2 17.06 5.66 21.21 10.44 21.96V14.96H7.9V12.06H10.44V9.85C10.44 7.34 11.93 5.96 14.22 5.96C15.31 5.96 16.45 6.15 16.45 6.15V8.62H15.19C13.95 8.62 13.56 9.39 13.56 10.18V12.06H16.34L15.89 14.96H13.56V21.96A10 10 0 0 0 22 12.06C22 6.53 17.5 2.04 12 2.04Z" /></svg>'),
|
||
'theme': SvgElement.svg(
|
||
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M12,18V6A6,6 0 0,1 18,12A6,6 0 0,1 12,18M20,15.31L23.31,12L20,8.69V4H15.31L12,0.69L8.69,4H4V8.69L0.69,12L4,15.31V20H8.69L12,23.31L15.31,20H20V15.31Z" /></svg>'),
|
||
};
|
||
|
||
// Get the current page’s title
|
||
String getPageTitle() => querySelector('title').text;
|
||
|
||
// Create the home button
|
||
Future<Element> makeHome() async {
|
||
return Element.li()
|
||
..classes.add('nav-item')
|
||
..insertAdjacentElement(
|
||
'afterBegin',
|
||
Element.a()
|
||
..attributes['href'] = '/'
|
||
..append(makeIcon(icons['home'])));
|
||
}
|
||
|
||
// Create a clickable icon
|
||
// `t_elem` must be an SVG declared in the above `icons` variable.
|
||
Element makeIcon(SvgElement t_elem) {
|
||
return t_elem..classes.add('nav-icon');
|
||
}
|
||
|
||
// Create the dropdown sitemap
|
||
Future<Element> makePages() async {
|
||
return Element.li()
|
||
..attributes['tabindex'] = '0'
|
||
..append(Element.a()
|
||
..attributes['tabindex'] = '-1'
|
||
..attributes['href'] = 'javascript:void(0)'
|
||
..append(makeIcon(icons['pages'])))
|
||
..classes.addAll(['nav-item', 'has-dropdown'])
|
||
..append(Element.ul()
|
||
..attributes['id'] = 'drop-page'
|
||
..classes.add('dropdown'));
|
||
}
|
||
|
||
// Create the array of share icons
|
||
Future<Element> makeShare() async {
|
||
// Create a share button
|
||
Element makeShareLink(Element t_icon, String t_url) {
|
||
return Element.li()
|
||
..classes.add('dropdown-item')
|
||
..append(Element.a()
|
||
..attributes['href'] = t_url
|
||
..attributes['target'] = '_blank'
|
||
..attributes['rel'] = 'noreferrer'
|
||
..append(t_icon));
|
||
}
|
||
|
||
return Element.li()
|
||
..classes.addAll(['nav-item', 'has-dropdown'])
|
||
..attributes['tabindex'] = '0'
|
||
..append(Element.a()
|
||
..attributes['href'] = 'javascript:void(0)'
|
||
..attributes['tabindex'] = '-1'
|
||
..append(makeIcon(icons['share'])))
|
||
..append(Element.ul()
|
||
..classes.add('dropdown')
|
||
..attributes['id'] = 'drop-share'
|
||
..append(makeShareLink(
|
||
makeIcon(icons['twitter']),
|
||
'https://twitter.com/share?text=${getPageTitle()}'
|
||
'&url=${window.location.href}'))
|
||
..append(makeShareLink(makeIcon(icons['reddit']),
|
||
'https://www.reddit.com/submit?title=${getPageTitle()}s&url=${window.location.href}'))
|
||
..append(makeShareLink(makeIcon(icons['email']),
|
||
'mailto:?subject=${getPageTitle}&body=${window.location.href}'))
|
||
..append(makeShareLink(
|
||
makeIcon(icons['linkedin']),
|
||
'https://www.linkedin.com/shareArticle?mini=true&url=${window.location.href}'
|
||
'&title=${getPageTitle()}'))
|
||
..append(makeShareLink(makeIcon(icons['facebook']),
|
||
'https://www.facebook.com/sharer/sharer.php?u=${window.location.href}')));
|
||
}
|
||
|
||
// Create the theme changer
|
||
Future<Element> makeThemeChanger() async {
|
||
Element makeThemeItem(String t_btnId) {
|
||
return Element.li()
|
||
..classes.add('dropdown-item')
|
||
..append(Element.span()
|
||
..attributes['id'] = t_btnId
|
||
..classes.add('themeBtn'));
|
||
}
|
||
|
||
return Element.li()
|
||
..classes.addAll(['nav-item', 'has-dropdown'])
|
||
..attributes['tabindex'] = '0'
|
||
..append(Element.a()
|
||
..attributes['href'] = 'javascript:void(0)'
|
||
..attributes['tabindex'] = '-1'
|
||
..append(Element.span()
|
||
..style.verticalAlign = 'top'
|
||
..append(makeIcon(icons['theme']))))
|
||
..append(Element.ul()
|
||
..classes.add('dropdown')
|
||
..attributes['id'] = 'theme-dropdown'
|
||
..append(makeThemeItem('lightBtn'))
|
||
..append(makeThemeItem('darkBtn'))
|
||
..append(makeThemeItem('nordBtn'))
|
||
..append(makeThemeItem('blackBtn')));
|
||
}
|
||
|
||
// Create the dropdown table of contents
|
||
Future<Element> makeToc() async {
|
||
return Element.li()
|
||
..attributes['id'] = 'toc-drop'
|
||
..attributes['tabindex'] = '0'
|
||
..classes.addAll(['nav-item', 'has-dropdown'])
|
||
..append(Element.a()
|
||
..attributes['tabindex'] = '-1'
|
||
..attributes['href'] = 'javascript:void(0)'
|
||
..append(makeIcon(icons['toc'])));
|
||
}
|
||
|
||
// Add a navbar atop of the HTML body, containing:
|
||
// - A back to home button
|
||
// - A dropdown sitemap
|
||
// - A dropdown table of contents
|
||
// - A dropdown array of share icons
|
||
// - A theme changer
|
||
Future<Element> makeNavbar() async {
|
||
final navbar_content = Element.ul()..classes.add('navbar-nav');
|
||
final home = await makeHome();
|
||
final pages = await makePages();
|
||
final toc = await makeToc();
|
||
final share = await makeShare();
|
||
final theme = await makeThemeChanger();
|
||
|
||
navbar_content
|
||
..append(home)
|
||
..append(pages)
|
||
..append(toc)
|
||
..append(share)
|
||
..append(theme);
|
||
|
||
// Navbar content added to navbar
|
||
final navbar = Element.nav()
|
||
..classes.add('navbar')
|
||
..append(navbar_content);
|
||
|
||
return navbar;
|
||
}
|