Lucien Cartier-Tilet
f99d45e8ab
This commit removes the `html' package dependency, speeding up the compile time of the Dart code. It also simplifies the code and removes some unused code. For instance the sun, moon, and lightbulb icons is removed, the `Theme' class is removed, and the `switchTheme' function is now merged into the `setTheme' function. The `makeThemeItem' function has also had its second argument removed.
177 lines
10 KiB
Dart
177 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) {
|
||
final icon = t_elem..classes.add('nav-icon');
|
||
return icon;
|
||
}
|
||
|
||
// Create the dropdown sitemap
|
||
Future<Element> makePages() async {
|
||
var pages = Element.ul()
|
||
..attributes['id'] = 'drop-page'
|
||
..classes.add('dropdown');
|
||
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(pages);
|
||
}
|
||
|
||
// 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);
|
||
}
|
||
|
||
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('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;
|
||
}
|