mirror of
https://github.com/bellingcat/ukraine-timemap.git
synced 2026-06-11 04:48:36 +03:00
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:
@@ -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": "Українська"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
59
src/components/controls/LanguageMenu.js
Normal file
59
src/components/controls/LanguageMenu.js
Normal 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>
|
||||
);
|
||||
}
|
||||
23
src/scss/languagemenu.scss
Normal file
23
src/scss/languagemenu.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,3 +15,4 @@
|
||||
@import "search";
|
||||
@import "satelliteoverlaytoggle";
|
||||
@import "languageswitch";
|
||||
@import "languagemenu";
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user