Language menu (#48)

* refactor(Toolbar): change the way z-order of elements is expressed

**Before:**
`.toolbar` was placed over the content of `#toolbar-wrapper`.

**After:**
`.toolbar-panels` are placed behind the content of `#toolbar-wrapper`.

**Reason:**
allows descendants of `.toolbar` to place themselves behind it.

Context
-------

Previously we had the following setup
(`*` means the root of stacking context, `-` means normal child):

    * div#toolbar-wrapper { z-index: 10, position: fixed }
       * div.toolbar { z-index: 10, position: relative }
         - div.toolbar-tabs
           - div.toolbar-tab
           - div.toolbar-tab
           - div.toolbar-tab
       * div.toolbar-panels { position: fixed }
    * div.map-wrapper { position: fixed }
    * div.cover-container { z-index: 501, position: absolute }

That was achivieving what was necessary:
- `#toolbar-wrapper` tree is placed over the `.map-wrapper`,
- `.cover-container` tree is placed over the `#toolbar-wrapper`,
- `.toolbar` tree is placed over `.toolbar-panels`

Problem
-------

The practical problem with it was not being able to make descendants
of `.toolbar` go behind it when sliding away (as they were within
`.toolbar`'s own stacking context).

We needed to add a small language menu which would be positioned next
to the button that opens it, and slide away behind the toolbar like
other toolbar-panels.

Solution
--------

I changed the stacking to be the following:

    * div#toolbar-wrapper { z-index: 10, position: fixed }
       - div.toolbar
         - div.toolbar-tabs
           - div.toolbar-tab
           - div.toolbar-tab
           - div.toolbar-tab
       * div.toolbar-panels { z-index: -1, position: fixed }
    * div.map-wrapper { position: fixed }
    * div.cover-container { z-index: 501, position: absolute }

This explicitly places `.toolbar-panels` behind everything else within
the stacking context created by `#toolbar-wrapper`.

Outcome
-------

Now `.language-menu` can easily be added as:

    * div#toolbar-wrapper { z-index: 10, position: fixed }
       - div.toolbar
         - div.toolbar-tabs
           - div.toolbar-tab
           - div.toolbar-tab { position: relative }
             * div.language-menu { z-index: -1, position: absolute }
           - div.toolbar-tab
       * div.toolbar-panels { z-index: -1, position: fixed }

* feat(Toolbar): add language menu (button and a sliding out panel)
This commit is contained in:
wattroll
2022-04-13 12:22:09 +03:00
committed by GitHub
parent 0d1968a110
commit aecbabf3d3
6 changed files with 136 additions and 5 deletions

View File

@@ -94,7 +94,9 @@
}
},
"en": {
"language_label": "Language",
"language_short": "Eng",
"language_long": "English",
"tiles": {
"default": "Map",
"satellite": "Sat"
@@ -212,9 +214,13 @@
}
},
"ru": {
"language_short": "Рус"
"language_label": "Язык",
"language_short": "Рус",
"language_long": "Русский"
},
"uk": {
"language_short": "Укр"
"language_label": "Мова",
"language_short": "Укр",
"language_long": "Українська"
}
}

View File

@@ -24,18 +24,25 @@ import {
import { ToolbarButton } from "./controls/atoms/ToolbarButton";
import { FullscreenToggle } from "./controls/FullScreenToggle";
import { LanguageSwitch } from "./controls/LanguageSwitch";
import { ToolbarLanguageMenu } from "./controls/LanguageMenu";
import DownloadPanel from "./controls/DownloadPanel";
class Toolbar extends React.Component {
constructor(props) {
super(props);
this.onSelectFilter = this.onSelectFilter.bind(this);
this.state = { _selected: -1 };
this.state = { _selected: -1, isLanguageMenuActive: false };
}
selectTab(selected) {
const _selected = this.state._selected === selected ? -1 : selected;
this.setState({ _selected });
this.setState({ _selected, isLanguageMenuActive: false });
}
setIsLanguageMenuActive(isActive) {
isActive
? this.setState({ isLanguageMenuActive: true, _selected: -1 })
: this.setState({ isLanguageMenuActive: false });
}
onSelectFilter(key, matchingKeys) {
@@ -210,6 +217,17 @@ class Toolbar extends React.Component {
/>
);
}
renderToolbarLanguageTab() {
return (
<ToolbarLanguageMenu
isActive={this.state.isLanguageMenuActive}
setIsActive={(isActive) => this.setIsLanguageMenuActive(isActive)}
language={this.props.language}
languages={this.props.languages}
setLanguage={this.props.actions.toggleLanguage}
/>
);
}
renderToolbarCategoryTabs(idxs) {
const { categories: panelCategories } = this.props.toolbarCopy.panels;
@@ -304,6 +322,9 @@ class Toolbar extends React.Component {
</div>
<div className="toolbar-tabs">
<TabList>
{this.props.languages?.length > 1
? this.renderToolbarLanguageTab()
: null}
{narrativesExist
? this.renderToolbarTab(
narrativesIdx,

View File

@@ -0,0 +1,59 @@
import { ToolbarButton } from "./atoms/ToolbarButton";
import copy from "../../common/data/copy.json";
export function LanguageMenu({
isActive,
language: currentLanguage,
languages,
setLanguage,
}) {
return (
<div
className={
isActive
? "toolbar-panel language-menu active"
: "toolbar-panel language-menu"
}
children={languages.map((language) => (
<div
key={language}
onClick={() => setLanguage(language)}
className={
language !== currentLanguage
? "language-option"
: "language-option selected"
}
children={copy[language].language_long}
/>
))}
/>
);
}
export function ToolbarLanguageMenu({
isActive,
setIsActive,
language,
languages,
setLanguage,
}) {
return (
<div className="toolbar-menu">
<ToolbarButton
isActive={isActive}
onClick={() => setIsActive(!isActive)}
iconKey="translate"
label={copy[language].language_label}
/>
<LanguageMenu
isActive={isActive}
language={language}
languages={languages}
setLanguage={(language) => {
setIsActive(false);
setLanguage(language);
}}
/>
</div>
);
}

View File

@@ -0,0 +1,23 @@
.language-menu {
color: $midwhite;
font-size: 1.25em;
.language-option:not(:first-child) {
border-top: solid 1px $midwhite;
}
.language-option {
cursor: pointer;
transition: 0.2s ease;
padding: 1em 1em;
background-color: $darkgrey;
&.selected {
background-color: $black;
color: $offwhite;
}
&:active {
background-color: $midgrey;
}
&:hover {
color: $offwhite;
}
}
}

View File

@@ -15,3 +15,4 @@
@import "search";
@import "satelliteoverlaytoggle";
@import "languageswitch";
@import "languagemenu";

View File

@@ -26,7 +26,6 @@
font-size: $normal;
font-weight: 100;
transition: 0.2s ease;
z-index: $header;
button {
background: #222222;
@@ -277,6 +276,7 @@
}
.toolbar-panels {
z-index: -1;
width: 440px;
top: 15px;
bottom: 0;
@@ -522,6 +522,27 @@
}
}
// Toolbar-menu consists of a toolbar-button and a panel.
// Panel is positioned next to the button.
// Panel slides behind the toolbar.
.toolbar-menu {
position: relative;
.toolbar-panel {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: -1;
visibility: hidden;
&.active {
left: 100%;
right: auto;
visibility: visible;
}
transition: 0.2s ease;
}
}
.search-content {
.item {
overflow: auto;