From 7c9e8b1e5f3c78b21dcb256f24c0a87651581513 Mon Sep 17 00:00:00 2001 From: Lucien Cartier-Tilet Date: Tue, 28 Apr 2020 22:32:37 +0200 Subject: [PATCH] Theme change made permanent, TOC and language changer work Replaced all text in navbar by icons, moved TOC to the right of Home button. TOC is now correctly displayed with small animation. Added black theme to light and dark themes. --- TODOs.org | 14 ++ web/dart/main.dart | 3 +- web/dart/reorganize_html.dart | 151 ++++++++++------ web/dart/theme.dart | 55 ++++-- web/style/style.scss | 312 ++++++++++++++++++++++++++++------ 5 files changed, 414 insertions(+), 121 deletions(-) create mode 100644 TODOs.org diff --git a/TODOs.org b/TODOs.org new file mode 100644 index 0000000..d9217bd --- /dev/null +++ b/TODOs.org @@ -0,0 +1,14 @@ +* TODO Organization of HTML [1/3] +** DONE Add button to display TOC + CLOSED: [2020-04-28 mar. 22:30] +** TODO Add link to images that don’t have any +** TODO Add share links [0/3] +*** TODO Add Twitter share +*** TODO Add Email share +*** TODO Add LinkedIn share +*** TODO Add Facebook share +* TODO Styling [1/3] +** TODO Tables +** DONE Hint bubbles for phonetics (Ñyqy) + CLOSED: [2020-04-28 mar. 22:31] +** TODO Links diff --git a/web/dart/main.dart b/web/dart/main.dart index 8f0b60d..16c3a23 100644 --- a/web/dart/main.dart +++ b/web/dart/main.dart @@ -1,7 +1,8 @@ import './reorganize_html.dart' show reorganizeHtml; -import './theme.dart' show makeThemeChanger; +import './theme.dart' show makeThemeChanger, setTheme; Future main() async { + await setTheme(); await reorganizeHtml().then((_) { makeThemeChanger(); }); diff --git a/web/dart/reorganize_html.dart b/web/dart/reorganize_html.dart index 8f9c769..b66902c 100644 --- a/web/dart/reorganize_html.dart +++ b/web/dart/reorganize_html.dart @@ -7,74 +7,103 @@ String getPageTitle() { return querySelector('title').text; } +Element makeIcon(List classes, [String id]) { + final icon = Element.tag('i')..classes.addAll(classes); + if (id != null) { + icon.attributes['id'] = id; + } + return icon; +} + Future addLanguages(Element navbar) async { // Languages - var languages = Element.ul()..classes.add('dropdown'); + var languages = Element.ul() + ..attributes['id'] = 'drop-lang' + ..classes.add('dropdown'); await parseSitemap().then((final sitemap) => { sitemap.forEach((url, name) { - final link = Element.a() - ..attributes['href'] = url - ..innerText = name; - - final linkLi = Element.li() + final link = Element.li() ..classes.add('dropdown-item') - ..insertAdjacentElement('afterBegin', link); - - languages.insertAdjacentElement('beforeEnd', linkLi); + ..insertAdjacentElement( + 'afterBegin', + Element.a() + ..attributes['href'] = url + ..innerText = name); + languages.insertAdjacentElement('beforeEnd', link); }) }); navbar.append(Element.li() ..append(Element.a() - ..attributes['href'] = '#' - ..innerText = 'Langues') - ..classes.add('nav-item') - ..classes.add('has-dropdown') + ..attributes['href'] = 'javascript:void(0)' + ..append(makeIcon(['fas', 'fa-language']))) + // ..innerText = 'Langues') + ..classes.addAll(['nav-item', 'has-dropdown']) ..insertAdjacentElement('beforeEnd', languages)); return navbar; } +Element makeTocDropDown() { + return Element.li() + ..attributes['id'] = 'toc-drop' + ..classes.addAll(['nav-item', 'has-dropdown']) + ..append(Element.a() + ..attributes['href'] = 'javascript:void(0)' + ..append(makeIcon(['fas', 'fa-list-ol'], 'tocBtn'))); +} + +Element makeThemeChanger() { + Element makeThemeItem(String t_btnId, Element t_icon, String t_text) { + return Element.li() + ..classes.add('dropdown-item') + ..append(Element.span() + ..attributes['id'] = t_btnId + ..append(t_icon) + ..appendText(' $t_text')); + } + + return Element.li() + ..classes.addAll(['nav-item', 'has-dropdown']) + ..append(Element.a() + ..attributes['href'] = 'javascript:void(0)' + ..append(makeIcon(['fas', 'fa-sun'])) + ..append(makeIcon(['fas', 'fa-moon']))) + ..append(Element.ul() + ..classes.add('dropdown') + ..attributes['id'] = 'theme-dropdown' + ..append(makeThemeItem('lightBtn', makeIcon(['fas', 'fa-sun']), 'Clair')) + ..append(makeThemeItem('darkBtn', makeIcon(['fas', 'fa-lightbulb']), 'Sombre')) + ..append(makeThemeItem('blackBtn', makeIcon(['fas', 'fa-moon']), 'Noir'))); +} + // Add a navbar atop of the HTML body, containing two buttons: // - One back to home // - A dropdown to each language detected in the sitemap Future makeNavbar() async { - // var _navbar = Navbar(); + var navbar_content = Element.ul()..classes.add('navbar-nav'); // Home - var navbar_content = Element.ul()..classes.add('navbar-nav'); navbar_content.append(Element.li() ..classes.add('nav-item') ..insertAdjacentElement( 'afterBegin', Element.a() ..attributes['href'] = '/' - ..innerText = 'Accueil')); + ..append(makeIcon(['fas', 'fa-home'])))); + + // TOC icon + navbar_content.append(makeTocDropDown()); // Add languages - // navbar_content.append(Element.html(''' - - // ''')); navbar_content = await addLanguages(navbar_content); - // Page title - navbar_content.append(Element.li() - ..classes.add('nav-item') - ..innerText = getPageTitle()); - // Share icon - // Add dropdown for sharing on multiple platforms + // TODO Add dropdown for sharing on multiple platforms navbar_content = addIcon(navbar_content, ['fas', 'fa-share-alt'], 'shareBtn'); // Theme changer - // Add dropdown for theming - // navbar_content = addIcon(navbar_content, ['fas', 'fa-sun'], 'themeBtn'); - navbar_content.append(Element.html(''' -''')); + var theme = window.localStorage['theme']; + theme = (theme == null || theme == 'light') ? 'fa-moon' : 'fa-sun'; + navbar_content.append(makeThemeChanger()); // Navbar content added to navbar final navbar = Element.nav() @@ -85,14 +114,31 @@ Future makeNavbar() async { } Element addIcon(Element navbar, List classes, String id) { - var elem = Element.tag('i')..attributes['id'] = id; - for (var c in classes) { - elem.classes.add(c); - } - navbar.append(Element.li()..append(elem)); + final icon = makeIcon(classes, id); + navbar.append(Element.li()..append(icon)); return navbar; } +Element makeHeader() { + var header = Element.tag('header'); + + // querySelector('#container').append(Element.tag('header')); + // var header = querySelector('header'); + header + ..append(Element.img() + ..attributes['src'] = + 'https://cdn.phundrak.com/img/mahakala-monochrome.png' + ..attributes['alt'] = 'Logo de Phundrak' + ..attributes['heigh'] = '150px' + ..attributes['width'] = '150px') + ..append(querySelector('h1')); + var subt = querySelector('.subtitle'); + if (subt != null) { + header.append(subt); + } + return header; +} + Future wrapTables() async { for (var table in querySelectorAll('table')) { var largetable = DivElement()..className = 'largetable'; @@ -101,24 +147,19 @@ Future wrapTables() async { } } -Future makeHeader() async { - querySelector('body') - .insertAdjacentElement('afterBegin', Element.tag('header')); - querySelector('header').append(querySelector('h1')); - querySelector('header').append(querySelector('.subtitle')); -} - Future reorganizeHtml() async { - await makeHeader(); - await wrapTables(); + // Create navbar and then header await makeNavbar().then((navbar) { + // querySelector('#toc-drop') + // .append(querySelector('#table-of-contents')..classes.add('dropdown')); querySelector('body').insertAdjacentElement('afterBegin', navbar); + // querySelector('nav').insertAdjacentElement( + // 'afterEnd', Element.div()..attributes['id'] = 'container'); + querySelector('nav').insertAdjacentElement('afterEnd', makeHeader()); + querySelector('#toc-drop') + .append(querySelector('#table-of-contents')..classes.add('dropdown')); }); - querySelector('body') - ..classes.add('light') - // Add a
element after the content div - ..appendHtml('
') - // Move the postamble in the content div - ..append(querySelector('#postamble')); + // wrap tables in container for better SCSS display + await wrapTables(); } diff --git a/web/dart/theme.dart b/web/dart/theme.dart index 37d5e76..c888685 100644 --- a/web/dart/theme.dart +++ b/web/dart/theme.dart @@ -1,20 +1,53 @@ import 'dart:html'; -void switchTheme(final Element body, String theme) { - body.classes.clear(); - body.classes.add(theme); +class Theme { + String _name; + String _icon; + + Theme(String t_name, String t_icon) { + _name = t_name; + _icon = t_icon; + } + + String getIcon() => _icon; + String getName() => _name; } +final themes = { + 'light': Theme('light', 'fa-sun'), + 'dark': Theme('dark', 'fa-lightbulb'), + 'black': Theme('black', 'fa-moon') +}; + +final localStorage = window.localStorage; + +var currentTheme = themes[localStorage['theme']]; + void makeThemeChanger() { final darkBtn = querySelector('#darkBtn'); final lightBtn = querySelector('#lightBtn'); - final body = querySelector('body'); + final blackBtn = querySelector('#blackBtn'); - darkBtn.onClick.listen((_) { - switchTheme(body, 'dark'); - }); - - lightBtn.onClick.listen((_) { - switchTheme(body, 'light'); - }); + lightBtn.onClick.listen((_) => switchTheme(themes['light'])); + darkBtn.onClick.listen((_) => switchTheme(themes['dark'])); + blackBtn.onClick.listen((_) => switchTheme(themes['black'])); +} + +Future setTheme() async { + if (currentTheme == null) { + currentTheme = themes['light']; + localStorage['theme'] = currentTheme.getName(); + } + querySelector('body') + ..classes.clear() + ..classes.add(currentTheme.getName()); +} + +void switchTheme(Theme currentTheme) { + // Set HTML theme + querySelector('body') + ..classes.clear() + ..classes.add(currentTheme.getName()); + // Set storage theme + localStorage['theme'] = currentTheme.getName(); } diff --git a/web/style/style.scss b/web/style/style.scss index bbb30db..47d080a 100644 --- a/web/style/style.scss +++ b/web/style/style.scss @@ -2,7 +2,7 @@ @font-face { font-family: "DoulosSIL"; font-display: swap; - src: url("https://langue.phundrak.com/fonts/DoulosSIL-R.woff"); + src: url("/fonts/DoulosSIL-R.woff"); } @font-face { font-family: "Noto Sans Runes"; @@ -17,70 +17,158 @@ // Themes ///////////////////////////////////////////////////////////////////// -$dark: rgba(52, 73, 94, 1); -$accent1: rgba(66, 191, 221, 1); -$accent2: rgba(92, 172, 126, 1); -$accent3: rgba(197, 193, 155, 1); -$light: #ddd; -$grey1: #f8f8f8; -$grey2: #dbe1e8; -$grey3: #b2becd; -$grey4: #6c7983; -$grey5: #454e56; -$grey6: #12181b; -$gradient-top: linear-gradient(0deg, $dark, $accent1, $accent2, $accent3, $light); -$gradient-right: linear-gradient(90deg, $dark, $accent1, $accent2, $accent3, $light); -$gradient-bottom: linear-gradient(180deg, $dark, $accent1, $accent2, $accent3, $light); -$gradient-left: linear-gradient(270deg, $dark, $accent1, $accent2, $accent3, $light); -$gradient-top-right: linear-gradient(45deg, $dark, $accent1, $accent2, $accent3, $light); -$gradient-bottom-right: linear-gradient(135deg, $dark, $accent1, $accent2, $accent3, $light); -$gradient-top-left: linear-gradient(225deg, $dark, $accent1, $accent2, $accent3, $light); -$gradient-bottom-left: linear-gradient(315deg, $dark, $accent1, $accent2, $accent3, $light); -$gradient-radial: radial-gradient( $dark, $accent1, $accent2, $accent3, $light); +$dark: rgba( 52, 73, 94, 1); +$black: rgba( 0, 0, 0, 1); +$accent1: rgba( 93, 115, 126, 1); +$accent2: rgba( 92, 172, 126, 1); +$accent3: rgba(197, 193, 155, 1); +$light: rgba(252, 239, 249, 1); +$grey1: #f8f8f8; +$grey2: #dbe1e8; +$grey3: #b2becd; +$grey4: #6c7983; +$grey5: #454e56; +$grey6: #12181b; + +// Accent 1 +// Black +$gradient-accent1-black-left: linear-gradient(to left, $black, $accent1, $accent1); +$gradient-accent1-black-right: linear-gradient(to right, $black, $accent1, $accent1); +// Dark +$gradient-accent1-dark-left: linear-gradient(to left, $dark, $accent1); +$gradient-accent1-dark-right: linear-gradient(to right, $dark, $accent1); +// Light +$gradient-accent1-light-left: linear-gradient(to left, $light, $accent1); +$gradient-accent1-light-right: linear-gradient(to right, $light, $accent1); +// Accent 2 +// Black +$gradient-accent2-black-left: linear-gradient(to left, $black, $accent2, $accent2); +$gradient-accent2-black-right: linear-gradient(to right, $black, $accent2, $accent2); +// Dark +$gradient-accent2-dark-left: linear-gradient(to left, $dark, $accent2); +$gradient-accent2-dark-right: linear-gradient(to right, $dark, $accent2); +// Light +$gradient-accent2-light-left: linear-gradient(to left, $light, $accent2); +$gradient-accent2-light-right: linear-gradient(to right, $light, $accent2); +// Accent 3 +// Black +$gradient-accent3-black-left: linear-gradient(to left, $black, $accent3, $accent3); +$gradient-accent3-black-right: linear-gradient(to right, $black, $accent3, $accent3); +// Dark +$gradient-accent3-dark-left: linear-gradient(to left, $dark, $accent3); +$gradient-accent3-dark-right: linear-gradient(to right, $dark, $accent3); +// Light +$gradient-accent3-light-left: linear-gradient(to left, $light, $accent3); +$gradient-accent3-light-right: linear-gradient(to right, $light, $accent3); + +$navbar-height: 70px; +$postamble-height: 55px; .light { - $bg-nav: linear-gradient(to right, $grey1, $grey3); - $bg-dropdown: $grey1; - $text: $light; + $bg-nav: $gradient-accent3-light-right; $border-color: $accent1; - $bg-solar: $accent3; - - transition: background 500ms ease-in-out, color 1s ease-in-out; color: $dark; background: $light; - .navbar { - background: $grey3; + transition: background 500ms ease-in-out, color 1s ease-in-out; + + .navbar, header { + background: $bg-nav; + } + + .status { + background: $gradient-accent3-light-left; + color: $dark; + } + + .tooltip { + border-bottom: 1px dotted $accent3; + + .tooltiptext { + background-color: $accent3; + color: $dark; + + &::after { + border-color: $accent3 transparent transparent transparent; + } + } + } + + .dropdown { + background: $accent3; + color: $dark; } } -.dark { - $background: $dark; - $foreground: $light; - $bg-nav: linear-gradient(to right, $grey5, $grey6); - $bg-dropdown: $grey6; - $text: $dark; +.dark, .black { + $bg-nav: $gradient-accent2-dark-right; $border-color: $dark; - $bg-solar: $accent3; - - transition: background 500ms ease-in-out, color 1s ease-in-out; color: $light; background: $dark; - .navbar { + transition: background 500ms ease-in-out, color 1s ease-in-out; + + .navbar, header { + background: $bg-nav; + } + + .status { + background: $gradient-accent2-dark-left; + color: $light; + } + + .tooltip { + border-bottom: 1px dotted $accent1; + + .tooltiptext { + background-color: $accent1; + color: $light; + + &::after { + border-color: $accent1 transparent transparent transparent; + } + } + } + + .dropdown { + background: $accent3; + color: $dark; + } +} + +.black { + $bg-nav: $gradient-accent1-black-right; + + background: $black; + + .navbar, header { + background: $bg-nav; + } + + .status { + background: $gradient-accent1-black-left; + } + + .dropdown { + background: $dark; color: $light; - background: $grey6; } } /* Style *********************************************************************/ +* { + outline: none; +} + body { margin: 0; padding: 0; font-family: "Noto Sans Runes", "DoulosSIL", "Lato", "proxima-nova", "Helvetica Neue", Arial, sans-serif; + font-size: 1.2em; + transition: background 500ms ease-in-out, color 1s ease-in-out; header, .navbar { @@ -100,7 +188,10 @@ a { } .navbar { - height: 70px; + position: fixed; + top: 0; + z-index: 4; + height: $navbar-height; width: 100%; } @@ -109,27 +200,22 @@ a { align-items: center; justify-content: space-evenly; height: 100%; - background: $accent3; - color: $dark; } header { padding: 1em; + margin-top: $navbar-height; margin-bottom: 1em; padding-bottom: 3.5em; text-align: center; - clip-path: polygon(50% 0%, 100% 0, 100% 65%, 50% 100%, 0 65%, 0 0); - color: $light; - background: $accent2; + clip-path: polygon(50% 0%, 100% 0, 100% 80%, 50% 100%, 0 80%, 0 0); + transition: background 500ms ease-in-out; } .dropdown { position: absolute; - width: 500px; opacity: 0; - z-index: 2; - background: blue; - border-top: 2px solid white; + z-index: 5; border-bottom-right-radius: 8px; border-bottom-left-radius: 8px; @@ -137,8 +223,8 @@ header { display: flex; align-items: center; justify-content: space-around; - height: 3rem; - margin-top: 2rem; + min-height: 3rem; + margin-top: 1rem; padding: 0.5rem; box-shadow: rgba(2, 8, 20, 0.1) 0px 0.175em 0.5em; @@ -148,8 +234,14 @@ header { } #theme-dropdown { - width: 300px; - transform: translateX(-95%); + width: 250px; + flex-direction: row; + transform: translateX(-50%); +} + +#drop-lang { + flex-direction: column; + transform: translateX(-40%); } .has-dropdown { @@ -158,15 +250,127 @@ header { opacity: 1; pointer-events: auto; } + + #table-of-contents { + opacity: 1; + height: 500%; + pointer-events: auto; + } } } .dropdown-item { + cursor: pointer; + a { width: 100%; height: 100%; size: 0.7rem; padding-left: 10px; + padding-right: 10px; font-weight: bold; } } + +#table-of-contents { + flex-direction: column; + padding: 20px; + float: right; + overflow-y: auto; + height: 0%; + width: 400px; + transform: translateX(0); + transition: height 500ms ease-in-out, opacity 500ms ease-in-out; + font-size: 0.9em; + + h2 { + font-size: 1.2em; + } + + #text-table-of-contents { + height: 100%; + } +} + +#content { + padding: 50px; + padding-top: 0; + max-width: 700px; + margin: 0 auto; + + text-align: justify; +} + +#postamble { + display: grid; + grid-template-areas: 'author email date'; + height: $postamble-height; + + font-size: 0.8em; + align-content: space-evenly; + text-align: center; + + .author { + grid-area: author; + } + + .email { + grid-area: email; + } + + .date { + grid-area: date; + } +} + +h1, h2, h3, h4, h5, h6 { + text-align: center; +} + +h2 { + font-size: 3em; +} + +h3 { + font-size: 2em; +} + +h4 { + font-size: 1.5em; +} + +.tooltip { + position: relative; + display: inline-block; + + .tooltiptext { + visibility: hidden; + + width: 120px; + margin-left: -60px; /* Half the width */ + bottom: 100%; + left: 50%; + padding: 5px 0; + text-align: center; + border-radius: 6px; + + position: absolute; + z-index: 2; + + &::after { + content: " "; + position: absolute; + top: 100%; /* At the bottom of the tooltip */ + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + } + } + + &:hover { + .tooltiptext { + visibility: visible; + } + } +}