Scriptear y otras cosas
This commit is contained in:
parent
83ee175248
commit
3efb0e82f7
433 changed files with 718 additions and 22946 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
|
orig/
|
||||||
|
|
15
README.md
15
README.md
|
@ -1,3 +1,16 @@
|
||||||
# [Arreglando bugs ajenos](https://nulo.in/Arreglando%20bugs%20ajenos.html)
|
# [Arreglando bugs ajenos](https://nulo.in/Arreglando%20bugs%20ajenos.html)
|
||||||
|
|
||||||
Pista: el código está en `sources-gen/`. Añadí la config de nginx de learngerman.nulo.in en `learngerman.nulo.in.conf`.
|
Este repositorio contiene los mapas descargados y assets junto a los scripts y parches para extraer y levantar con Vite. Añadí la config de nginx de learngerman.nulo.in en `learngerman.nulo.in.conf`.
|
||||||
|
|
||||||
|
## Levantar
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm install
|
||||||
|
./extraer.sh
|
||||||
|
pnpm run dev
|
||||||
|
# El servidor local tiene un proxy a learngerman.dw.com para la API y el login.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Actualizar código
|
||||||
|
|
||||||
|
Descargar nuevos mapas a `maps/`. Extraer con `./extraer.sh`. Ver si se necesitan assets nuevos con `grep -r assets orig/`.
|
||||||
|
|
40
extraer.sh
Executable file
40
extraer.sh
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/bin/sh
|
||||||
|
big () {
|
||||||
|
echo '==================================='
|
||||||
|
echo "$1"
|
||||||
|
echo '==================================='
|
||||||
|
}
|
||||||
|
|
||||||
|
rm -rf orig
|
||||||
|
mkdir -p orig
|
||||||
|
cd orig || exit
|
||||||
|
|
||||||
|
big 'Extrayendo código de los source maps'
|
||||||
|
for f in ../maps/*.map; do
|
||||||
|
node ../source-from-sourcemaps.js "$f"
|
||||||
|
done
|
||||||
|
mv sources-gen/* .
|
||||||
|
rm -r sources-gen
|
||||||
|
# Por alguna razón un CSS se extrae ahí
|
||||||
|
cp -r src/components/* ./components/
|
||||||
|
|
||||||
|
big '.js -> .jsx'
|
||||||
|
find . -iname '*.js' -exec mv {} {}x \;
|
||||||
|
|
||||||
|
big 'Parcheando .svg'
|
||||||
|
find . -iname '*.svg' -exec sh -c '
|
||||||
|
echo "const __webpack_public_path__ = \"https://learngerman.dw.com/\";" > "${1}.js"
|
||||||
|
cat "$1" >> "${1}.js"
|
||||||
|
rm "$1"
|
||||||
|
' sh {} \;
|
||||||
|
|
||||||
|
big 'Copiando archivos de static/'
|
||||||
|
cp -r ../static/* ./
|
||||||
|
|
||||||
|
big 'Parcheando...'
|
||||||
|
for f in ../patches/*; do
|
||||||
|
echo "--> Parche $f"
|
||||||
|
patch -p1 < "$f"
|
||||||
|
done
|
||||||
|
|
||||||
|
big 'DONE. Código extraido en "orig".'
|
4
links
4
links
|
@ -1,4 +0,0 @@
|
||||||
https://learngerman.dw.com/static/css/main.f50335e4.chunk.css.map
|
|
||||||
https://learngerman.dw.com/static/css/7.c65299ef.chunk.css.map
|
|
||||||
https://learngerman.dw.com/static/css/playerPlugins-video.4cb21404.chunk.css.map
|
|
||||||
https://learngerman.dw.com/static/css/playerPlugins-audio.dcc31119.chunk.css.map
|
|
|
@ -1,4 +1,7 @@
|
||||||
{
|
{
|
||||||
|
"scripts": {
|
||||||
|
"dev": "cd orig/ && vite"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.4.16",
|
"@apollo/client": "^3.4.16",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
|
|
14
patches/0001-fake-process.env-config.jsx.diff
Normal file
14
patches/0001-fake-process.env-config.jsx.diff
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
--- orig/config.jsx 2021-10-11 20:14:31.701314123 -0300
|
||||||
|
+++ sources-gen/config.jsx 2021-10-11 20:12:23.251741729 -0300
|
||||||
|
@@ -1,3 +1,11 @@
|
||||||
|
+const process = {
|
||||||
|
+ env: {
|
||||||
|
+ //REACT_APP_GRAPHQL_BASE_URL: "https://learngerman.dw.com/graphql",
|
||||||
|
+ REACT_APP_GRAPHQL_BASE_URL: import.meta.env.VITE_GRAPHQL_BASE_URL,
|
||||||
|
+ REACT_APP_RECAPTCHA_SITEKEY: null,
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
const config = {
|
||||||
|
graphQL: {
|
||||||
|
baseUrl: process.env.REACT_APP_GRAPHQL_BASE_URL || '/graphql',
|
50
patches/0002-use-details-in-accordion.diff
Normal file
50
patches/0002-use-details-in-accordion.diff
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
--- orig/components/ContentContainer/AccordionContainer.jsx 2021-10-11 20:30:09.799665485 -0300
|
||||||
|
+++ sources-gen/components/ContentContainer/AccordionContainer.jsx 2021-10-11 15:49:17.793082410 -0300
|
||||||
|
@@ -6,43 +6,26 @@
|
||||||
|
import { useTranslation } from '../../hooks/useTranslation';
|
||||||
|
|
||||||
|
export const AccordionContainer = ({ title, children, className }) => {
|
||||||
|
- const element = useRef(null);
|
||||||
|
- const [isOpen, { toggleOnClick }] = useToggle();
|
||||||
|
- const height = element.current ? element.current.scrollHeight : '0';
|
||||||
|
const titleTranslation = useTranslation(title);
|
||||||
|
|
||||||
|
return (
|
||||||
|
- <div className={className}>
|
||||||
|
- <div className="row noVMargins">
|
||||||
|
+ <details className={className}>
|
||||||
|
+ <summary className="row noVMargins">
|
||||||
|
<div className="col-sm-offset-1 col-sm-10 col-lg-offset-2 col-lg-8">
|
||||||
|
- <button tabIndex={0} onClick={toggleOnClick}>
|
||||||
|
<h4>
|
||||||
|
{titleTranslation}
|
||||||
|
- <ToggleableArrow
|
||||||
|
- className="toggleable-arrow"
|
||||||
|
- fill={colors.LG_BLACK}
|
||||||
|
- {...{
|
||||||
|
- isUp: isOpen,
|
||||||
|
- }}
|
||||||
|
- />
|
||||||
|
</h4>
|
||||||
|
- </button>
|
||||||
|
</div>
|
||||||
|
- </div>
|
||||||
|
+ </summary>
|
||||||
|
<AccordionContainerContent
|
||||||
|
- height={height}
|
||||||
|
- isOpen={isOpen}
|
||||||
|
- ref={element}
|
||||||
|
- aria-expanded={isOpen}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AccordionContainerContent>
|
||||||
|
- </div>
|
||||||
|
+ </details>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AccordionContainerContent = styled.div`
|
||||||
|
- max-height: ${({ isOpen, height }) => (isOpen ? height : '1')}px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: ${colors.LG_WHITE};
|
||||||
|
transition: max-height 0.7s;
|
28
patches/0003-fix-broken-exercises.diff
Normal file
28
patches/0003-fix-broken-exercises.diff
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
--- orig/components/Lesson/LessonExercise/LessonExerciseItem/LessonExerciseItem.jsx 2021-10-11 20:29:07.983390638 -0300
|
||||||
|
+++ sources-gen/components/Lesson/LessonExercise/LessonExerciseItem/LessonExerciseItem.jsx 2021-10-11 14:15:46.480142077 -0300
|
||||||
|
@@ -91,12 +91,20 @@
|
||||||
|
<MediaInputAudio data={data} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
+ // XXX: Here is the problem. These exercises return inputType == 'VIDEO'
|
||||||
|
+ // even though they don't have any videos (videos == []) causing some
|
||||||
|
+ // code that assumes that there is a video inside videos to fail
|
||||||
|
+ // (the MediaInputVideo component.)
|
||||||
|
case 'VIDEO':
|
||||||
|
- return (
|
||||||
|
- <div className="input-header-video">
|
||||||
|
- <MediaInputVideo data={data} />
|
||||||
|
- </div>
|
||||||
|
- );
|
||||||
|
+ // This "if" is our patch. If it isn't true, it fallsthrough to the default
|
||||||
|
+ // behaviour (returning null.)
|
||||||
|
+ if (data.content.videos.length > 0) {
|
||||||
|
+ return (
|
||||||
|
+ <div className="input-header-video">
|
||||||
|
+ <MediaInputVideo data={data} />
|
||||||
|
+ </div>
|
||||||
|
+ );
|
||||||
|
+ }
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
11
patches/0004-react-render-instead-of-hydrate.diff
Normal file
11
patches/0004-react-render-instead-of-hydrate.diff
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
--- orig/index.jsx 2021-10-11 21:08:09.858345720 -0300
|
||||||
|
+++ sources-gen/index.jsx 2021-10-11 21:09:05.158218059 -0300
|
||||||
|
@@ -30,7 +30,7 @@
|
||||||
|
cache: cache.restore(window.__APOLLO_STATE__), // eslint-disable-line no-underscore-dangle
|
||||||
|
});
|
||||||
|
|
||||||
|
-ReactDOM.hydrate(
|
||||||
|
+ReactDOM.render(
|
||||||
|
<ApolloProvider client={client}>
|
||||||
|
<Provider store={store}>
|
||||||
|
<Router>
|
11
patches/0005-no-ssr.diff
Normal file
11
patches/0005-no-ssr.diff
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
--- orig/utils/ssr.jsx 2021-10-11 21:17:02.041396123 -0300
|
||||||
|
+++ sources-gen/utils/ssr.jsx 2021-10-11 21:22:17.825984088 -0300
|
||||||
|
@@ -1,7 +1,3 @@
|
||||||
|
-function isServer() {
|
||||||
|
+export function isServer() {
|
||||||
|
return !(typeof window !== 'undefined' && window.document);
|
||||||
|
}
|
||||||
|
-// CommonJS export to be able to use it on server and client
|
||||||
|
-module.exports = {
|
||||||
|
- isServer,
|
||||||
|
-};
|
|
@ -1,47 +1,61 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
// Gracias a:
|
||||||
|
// - Código original: https://github.com/akx/source-from-sourcemaps
|
||||||
|
// - Aguante: https://gist.github.com/banyudu/b17a9cb3f05296b76a9f3051f66c3dcd
|
||||||
|
// - Algoritmo: https://github.com/rarecoil/unwebpack-sourcemap
|
||||||
|
|
||||||
const fs = require("fs");
|
const fs = require("fs")
|
||||||
const util = require("util");
|
const util = require("util")
|
||||||
const writeFileP = util.promisify(fs.writeFile);
|
const writeFileP = util.promisify(fs.writeFile)
|
||||||
const readFileP = util.promisify(fs.readFile);
|
const readFileP = util.promisify(fs.readFile)
|
||||||
const mkdirp = require("mkdirp");
|
const mkdirP = util.promisify(fs.mkdir)
|
||||||
const path = require("path");
|
const path = require("path")
|
||||||
const { SourceMapConsumer } = require("source-map");
|
const { SourceMapConsumer } = require("source-map")
|
||||||
const minimist = require("minimist");
|
|
||||||
|
|
||||||
async function processSource(consumer, src) {
|
const outputDir = 'sources-gen'
|
||||||
const fsSrc =
|
|
||||||
"sources-gen/" +
|
function sanitizePath (path) {
|
||||||
src
|
let newPath = path
|
||||||
.replace(/webpack:\/\/\//, "")
|
.replace(/^webpack:\/\/\/?/, "")
|
||||||
.replace(/~\//, "node_modules/")
|
.replace(/~\//, "node_modules/")
|
||||||
.replace(/\?.+$/, "");
|
.replace(/\?.+$/, "")
|
||||||
const source = consumer.sourceContentFor(src, true);
|
.replace(/\.\.\//g, 'parent_dir/')
|
||||||
if (!source) {
|
.replace(/\.\//g, '')
|
||||||
console.warn("Unable to source:", src);
|
return newPath
|
||||||
return;
|
|
||||||
}
|
|
||||||
await mkdirp(path.dirname(fsSrc));
|
|
||||||
await writeFileP(fsSrc, source, "UTF-8");
|
|
||||||
console.log(`Wrote ${fsSrc}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processFile(pth) {
|
async function processFile(pth) {
|
||||||
const data = await readFileP(pth, "UTF-8");
|
const data = await readFileP(pth, "UTF-8");
|
||||||
return await SourceMapConsumer.with(data, null, async (consumer) => {
|
const consumer = await new SourceMapConsumer(data);
|
||||||
const processors = consumer.sources.map((src) =>
|
|
||||||
processSource(consumer, src)
|
const processors = consumer.sources.map(async (src) => {
|
||||||
);
|
const fsSrc = path.join(
|
||||||
return await Promise.all(processors);
|
outputDir,
|
||||||
});
|
sanitizePath(src)
|
||||||
|
)
|
||||||
|
if (fsSrc.includes('node_modules')) {
|
||||||
|
console.warn(`Not saving ${fsSrc} because it is node_modules`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = consumer.sourceContentFor(src, true)
|
||||||
|
if (!source) {
|
||||||
|
console.warn("Unable to source", src)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await mkdirP(path.dirname(fsSrc), { recursive: true })
|
||||||
|
await writeFileP(fsSrc, source, "UTF-8")
|
||||||
|
console.log(`Wrote ${fsSrc}`)
|
||||||
|
})
|
||||||
|
await Promise.all(processors)
|
||||||
|
|
||||||
|
consumer.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
function main() {
|
||||||
const args = minimist(process.argv.slice(2));
|
return processFile(process.argv[2])
|
||||||
const processors = args._.map((pth) => processFile(pth));
|
|
||||||
return await Promise.all(processors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!module.parent) {
|
if (!module.parent) {
|
||||||
main().then(() => process.exit(0));
|
main().then(() => process.exit(0));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"0 debug pnpm:scope": {
|
|
||||||
"selected": 1
|
|
||||||
},
|
|
||||||
"1 error pnpm": {
|
|
||||||
"code": "ERR_PNPM_NO_SCRIPT",
|
|
||||||
"err": {
|
|
||||||
"name": "pnpm",
|
|
||||||
"message": "Missing script: vite",
|
|
||||||
"code": "ERR_PNPM_NO_SCRIPT",
|
|
||||||
"stack": "pnpm: Missing script: vite\n at Object.handler (/home/nulo/.npm/prefix/lib/node_modules/pnpm/dist/pnpm.cjs:129183:15)\n at async /home/nulo/.npm/prefix/lib/node_modules/pnpm/dist/pnpm.cjs:133329:20\n at async run (/home/nulo/.npm/prefix/lib/node_modules/pnpm/dist/pnpm.cjs:133304:34)\n at async runPnpm (/home/nulo/.npm/prefix/lib/node_modules/pnpm/dist/pnpm.cjs:133514:5)\n at async /home/nulo/.npm/prefix/lib/node_modules/pnpm/dist/pnpm.cjs:133506:7"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
export default __webpack_public_path__ + "static/media/dwtheantiquab-w5plain.94c5b804.woff";
|
|
|
@ -1 +0,0 @@
|
||||||
export default __webpack_public_path__ + "static/media/dwtheantiquab-w5plain.524a5eac.woff2";
|
|
|
@ -1 +0,0 @@
|
||||||
export default __webpack_public_path__ + "static/media/DWTheAntiquaB-W5PlainItalic.0c806fe1.woff";
|
|
|
@ -1 +0,0 @@
|
||||||
export default __webpack_public_path__ + "static/media/DWTheAntiquaB-W5PlainItalic.390937af.woff2";
|
|
|
@ -1 +0,0 @@
|
||||||
export default __webpack_public_path__ + "static/media/DWTheAntiquaB-W7Bold.73a35783.woff";
|
|
|
@ -1 +0,0 @@
|
||||||
export default __webpack_public_path__ + "static/media/DWTheAntiquaB-W7Bold.c739cf0d.woff2";
|
|
|
@ -1,4 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { ReactComponent as ClaimLogo } from '../svg/dw-claim.svg';
|
|
||||||
|
|
||||||
export const StyledClaimLogo = styled(props => <ClaimLogo {...props} />)``;
|
|
|
@ -1,4 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { ReactComponent as Logo } from '../svg/dw-logo.svg';
|
|
||||||
|
|
||||||
export const StyledLogo = styled(props => <Logo {...props} />)``;
|
|
|
@ -1,6 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { ReactComponent as SmileyPositive } from '../svg/smiley-positive.svg';
|
|
||||||
import { ReactComponent as SmileyNegative } from '../svg/smiley-negative.svg';
|
|
||||||
|
|
||||||
export const StyledSmileyPositive = styled(SmileyPositive)``;
|
|
||||||
export const StyledSmileyNegative = styled(SmileyNegative)``;
|
|
|
@ -1,4 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { ReactComponent as ToggleableArrowIcon } from '../svg/toggleableArrow.svg';
|
|
||||||
|
|
||||||
export const StyledToggleableArrowIcon = styled(props => <ToggleableArrowIcon {...props} />)``;
|
|
|
@ -1 +0,0 @@
|
||||||
export default __webpack_public_path__ + "static/media/bg-dw-desktop.4aec073f.jpg";
|
|
|
@ -1 +0,0 @@
|
||||||
export default __webpack_public_path__ + "static/media/course_landing-1200x675.f68dc5b5.jpg";
|
|
|
@ -1 +0,0 @@
|
||||||
export default __webpack_public_path__ + "static/media/course_landing-480x270.5a82869e.jpg";
|
|
|
@ -1 +0,0 @@
|
||||||
export default __webpack_public_path__ + "static/media/course_landing-768x432.d54bc099.jpg";
|
|
|
@ -1 +0,0 @@
|
||||||
export default __webpack_public_path__ + "static/media/course_landing-992x558.4309e861.jpg";
|
|
File diff suppressed because one or more lines are too long
|
@ -1,2 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
export default __webpack_public_path__ + "static/media/dw-logo-mobile.7e6e58ec.svg";
|
|
|
@ -1,2 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
export default __webpack_public_path__ + "static/media/dw-logo-tablet.ac032dbb.svg";
|
|
|
@ -1,37 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
||||||
|
|
||||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
||||||
|
|
||||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
var _ref2 = /*#__PURE__*/React.createElement("g", {
|
|
||||||
className: "logoBox",
|
|
||||||
fillRule: "evenodd"
|
|
||||||
}, /*#__PURE__*/React.createElement("g", {
|
|
||||||
className: "logoLogo"
|
|
||||||
}, /*#__PURE__*/React.createElement("path", {
|
|
||||||
d: "M15.5929091,3.39736364 C22.287,3.39736364 27.714,8.82381818 27.714,15.5184545 C27.714,22.2125455 22.287,27.6392727 15.5929091,27.6392727 C8.89881818,27.6392727 3.47181818,22.2125455 3.47181818,15.5184545 C3.47181818,8.82381818 8.89881818,3.39736364 15.5929091,3.39736364 L15.5929091,3.39736364 Z M26.9959091,5.16463636 C29.8276364,1.99527273 33.9458182,0 38.5303636,0 C47.0694545,0 53.9920909,6.92236364 53.9920909,15.4617273 C53.9920909,24.0010909 47.0694545,30.9234545 38.5303636,30.9234545 C33.9458182,30.9234545 29.8276364,28.9281818 26.9959091,25.7585455 C24.1644545,28.9281818 20.046,30.9234545 15.4614545,30.9234545 C6.92209091,30.9234545 0,24.0010909 0,15.4617273 C0,6.92236364 6.92209091,1.93784382e-15 15.4614545,1.93784382e-15 C20.046,1.93784382e-15 24.1644545,1.99527273 26.9959091,5.16463636 L26.9959091,5.16463636 Z M15.9913636,13.0819091 L12.6553636,13.0819091 L12.6553636,18.0845455 L15.9913636,18.0845455 C18.4150909,18.0845455 19.2714545,17.0787273 19.2714545,15.5833636 C19.2714545,14.088 18.4085455,13.0819091 15.9913636,13.0819091 L15.9913636,13.0819091 Z M16.4511818,9.59536364 C19.8250909,9.59536364 23.2835455,11.292 23.2835455,15.5828182 C23.2835455,19.8739091 19.8250909,21.5705455 16.4511818,21.5705455 L8.886,21.5705455 L8.886,9.59536364 L16.4511818,9.59536364 L16.4511818,9.59536364 Z M35.4119983,15.9318147 L37.5403715,10.1204985 L40.4334297,10.1204985 L42.5618029,15.9318147 L44.2678615,10.1204985 L48.0692727,10.1204985 L44.4908946,21.3263197 L41.2676315,21.3263197 L38.9674938,15.9016908 L36.7070387,21.3263197 L33.483486,21.3263197 L29.9048182,10.1204985 L33.7062294,10.1204985 L35.4119983,15.9318147 L35.4119983,15.9318147 Z",
|
|
||||||
id: "Shape"
|
|
||||||
})));
|
|
||||||
|
|
||||||
function SvgDwLogo(_ref, svgRef) {
|
|
||||||
var title = _ref.title,
|
|
||||||
titleId = _ref.titleId,
|
|
||||||
props = _objectWithoutProperties(_ref, ["title", "titleId"]);
|
|
||||||
|
|
||||||
return /*#__PURE__*/React.createElement("svg", _extends({
|
|
||||||
viewBox: "0 0 54 31",
|
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
|
||||||
ref: svgRef,
|
|
||||||
"aria-labelledby": titleId
|
|
||||||
}, props), title ? /*#__PURE__*/React.createElement("title", {
|
|
||||||
id: titleId
|
|
||||||
}, title) : null, _ref2);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ForwardRef = /*#__PURE__*/React.forwardRef(SvgDwLogo);
|
|
||||||
export default __webpack_public_path__ + "static/media/dw-logo.ff9012af.svg";
|
|
||||||
export { ForwardRef as ReactComponent };
|
|
|
@ -1,2 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
export default __webpack_public_path__ + "static/media/icon-course-cert-gray.0fdcab0c.svg";
|
|
|
@ -1,42 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
||||||
|
|
||||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
||||||
|
|
||||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
function SvgIconHintCircleOrange(_ref, svgRef) {
|
|
||||||
var title = _ref.title,
|
|
||||||
titleId = _ref.titleId,
|
|
||||||
props = _objectWithoutProperties(_ref, ["title", "titleId"]);
|
|
||||||
|
|
||||||
return /*#__PURE__*/React.createElement("svg", _extends({
|
|
||||||
width: "18px",
|
|
||||||
height: "18px",
|
|
||||||
viewBox: "0 0 18 18",
|
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
|
||||||
xmlnsXlink: "http://www.w3.org/1999/xlink",
|
|
||||||
xmlSpace: "preserve",
|
|
||||||
style: {
|
|
||||||
fillRule: "evenodd",
|
|
||||||
clipRule: "evenodd",
|
|
||||||
strokeLinejoin: "round",
|
|
||||||
strokeMiterlimit: 1.41421
|
|
||||||
},
|
|
||||||
ref: svgRef,
|
|
||||||
"aria-labelledby": titleId
|
|
||||||
}, props), title ? /*#__PURE__*/React.createElement("title", {
|
|
||||||
id: titleId
|
|
||||||
}, title) : null, /*#__PURE__*/React.createElement("path", {
|
|
||||||
d: "M9,0C13.967,0 18,4.033 18,9C18,13.967 13.967,18 9,18C4.033,18 0,13.967 0,9C0,4.033 4.033,0 9,0ZM12.189,14.994L12.189,14.343L10.027,13.623L10.027,6.966C8.269,6.966 7.525,7.069 6.206,7.289L6.206,7.903C7.113,8.041 7.538,8.144 7.977,8.337L7.977,13.657C7.181,13.849 6.502,14.013 5.87,14.343L5.87,14.994L12.189,14.994ZM8.984,5.417C8.544,5.417 8.31,5.322 8.007,5.02C7.705,4.718 7.61,4.484 7.61,4.044C7.61,3.605 7.705,3.37 8.007,3.068C8.31,2.765 8.544,2.67 8.984,2.67C9.424,2.67 9.657,2.765 9.96,3.068C10.262,3.37 10.357,3.605 10.357,4.044C10.357,4.484 10.262,4.718 9.96,5.02C9.657,5.322 9.424,5.417 8.984,5.417Z",
|
|
||||||
style: {
|
|
||||||
fill: "#f08c00"
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
var ForwardRef = /*#__PURE__*/React.forwardRef(SvgIconHintCircleOrange);
|
|
||||||
export default __webpack_public_path__ + "static/media/icon-hint-circle-orange.764164ab.svg";
|
|
||||||
export { ForwardRef as ReactComponent };
|
|
|
@ -1,39 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
||||||
|
|
||||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
||||||
|
|
||||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
function SvgIconHint(_ref, svgRef) {
|
|
||||||
var title = _ref.title,
|
|
||||||
titleId = _ref.titleId,
|
|
||||||
props = _objectWithoutProperties(_ref, ["title", "titleId"]);
|
|
||||||
|
|
||||||
return /*#__PURE__*/React.createElement("svg", _extends({
|
|
||||||
width: "10px",
|
|
||||||
height: "18px",
|
|
||||||
viewBox: "0 0 10 18",
|
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
|
||||||
xmlnsXlink: "http://www.w3.org/1999/xlink",
|
|
||||||
style: {
|
|
||||||
fillRule: "evenodd"
|
|
||||||
},
|
|
||||||
ref: svgRef,
|
|
||||||
"aria-labelledby": titleId
|
|
||||||
}, props), title ? /*#__PURE__*/React.createElement("title", {
|
|
||||||
id: titleId
|
|
||||||
}, title) : null, /*#__PURE__*/React.createElement("path", {
|
|
||||||
d: "M369.101562,1457.80899 L369.101562,1456.85041 C369.918135,1456.42437 370.841202,1456.08709 371.870793,1455.83857 L371.870793,1448.22319 C371.302743,1447.97467 370.432929,1447.76165 369.261326,1447.58413 L369.261326,1446.67881 C370.965476,1446.39478 372.953622,1446.25277 375.225823,1446.25277 L375.225823,1455.83857 L378.101562,1456.85041 L378.101562,1457.80899 L369.101562,1457.80899 Z M373.308663,1444.17585 C372.740613,1444.17585 372.261328,1443.98059 371.870793,1443.59005 C371.480259,1443.19952 371.284994,1442.72023 371.284994,1442.15218 C371.284994,1441.58413 371.480259,1441.10485 371.870793,1440.71431 C372.261328,1440.32378 372.740613,1440.12851 373.308663,1440.12851 C373.876713,1440.12851 374.355998,1440.32378 374.746533,1440.71431 C375.137067,1441.10485 375.332332,1441.58413 375.332332,1442.15218 C375.332332,1442.72023 375.137067,1443.19952 374.746533,1443.59005 C374.355998,1443.98059 373.876713,1444.17585 373.308663,1444.17585 L373.308663,1444.17585 Z",
|
|
||||||
style: {
|
|
||||||
fill: "#f08c00"
|
|
||||||
},
|
|
||||||
transform: "translate(-369.000000, -1440.000000)"
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
var ForwardRef = /*#__PURE__*/React.forwardRef(SvgIconHint);
|
|
||||||
export default __webpack_public_path__ + "static/media/icon-hint.484f8f25.svg";
|
|
||||||
export { ForwardRef as ReactComponent };
|
|
|
@ -1,43 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
||||||
|
|
||||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
||||||
|
|
||||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
function SvgIconLanguageWorldW(_ref, svgRef) {
|
|
||||||
var title = _ref.title,
|
|
||||||
titleId = _ref.titleId,
|
|
||||||
props = _objectWithoutProperties(_ref, ["title", "titleId"]);
|
|
||||||
|
|
||||||
return /*#__PURE__*/React.createElement("svg", _extends({
|
|
||||||
width: "16px",
|
|
||||||
height: "16px",
|
|
||||||
viewBox: "0 0 16 16",
|
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
|
||||||
xmlnsXlink: "http://www.w3.org/1999/xlink",
|
|
||||||
xmlSpace: "preserve",
|
|
||||||
style: {
|
|
||||||
fillRule: "evenodd",
|
|
||||||
clipRule: "evenodd",
|
|
||||||
strokeLinejoin: "round",
|
|
||||||
strokeMiterlimit: 1.41421
|
|
||||||
},
|
|
||||||
ref: svgRef,
|
|
||||||
"aria-labelledby": titleId
|
|
||||||
}, props), title ? /*#__PURE__*/React.createElement("title", {
|
|
||||||
id: titleId
|
|
||||||
}, title) : null, /*#__PURE__*/React.createElement("path", {
|
|
||||||
d: "M7.976,0.026c-1.443,0 -2.784,0.36 -4.021,1.082c-1.208,0.707 -2.165,1.665 -2.872,2.873c-0.722,1.252 -1.083,2.599 -1.083,4.043c0,1.443 0.361,2.784 1.083,4.021c0.707,1.208 1.664,2.165 2.872,2.872c1.237,0.722 2.578,1.083 4.021,1.083c1.444,0 2.784,-0.361 4.021,-1.083c1.208,-0.707 2.166,-1.664 2.873,-2.872c0.736,-1.237 1.104,-2.578 1.104,-4.021c0,-1.444 -0.36,-2.784 -1.082,-4.021c-0.707,-1.208 -1.665,-2.166 -2.873,-2.873c-1.252,-0.736 -2.599,-1.104 -4.043,-1.104Zm3.005,6.451c1.208,-0.25 2.268,-0.641 3.182,-1.171c0.383,0.855 0.574,1.76 0.574,2.718c0,1.149 -0.272,2.209 -0.817,3.181c-0.973,-0.486 -1.974,-0.832 -3.005,-1.038c0.088,-0.692 0.132,-1.407 0.132,-2.143c0,-0.53 -0.022,-1.046 -0.066,-1.547Zm2.629,-2.165c-0.942,0.398 -1.878,0.677 -2.806,0.84c-0.25,-1.429 -0.648,-2.659 -1.193,-3.69c0.825,0.206 1.584,0.556 2.276,1.049c0.692,0.494 1.267,1.094 1.723,1.801Zm-3.734,3.712c0,0.781 -0.037,1.429 -0.11,1.944c-0.634,-0.073 -1.23,-0.11 -1.79,-0.11c-0.56,0 -1.156,0.037 -1.789,0.11c-0.074,-0.692 -0.111,-1.34 -0.111,-1.944c0,-0.309 0.015,-0.759 0.044,-1.348c0.516,0.074 1.138,0.111 1.867,0.111c0.729,0 1.344,-0.037 1.845,-0.111c0.03,0.589 0.044,1.039 0.044,1.348Zm-1.414,-6.739c0.589,1.178 0.995,2.526 1.215,4.043c-0.736,0.059 -1.303,0.089 -1.701,0.089c-0.398,0 -0.965,-0.03 -1.701,-0.089c0.236,-1.517 0.641,-2.865 1.215,-4.043c0.103,-0.015 0.265,-0.022 0.486,-0.022c0.221,0 0.383,0.007 0.486,0.022Zm-2.121,0.177c-0.56,1.09 -0.957,2.32 -1.193,3.69c-0.972,-0.192 -1.907,-0.472 -2.806,-0.84c0.471,-0.707 1.05,-1.307 1.734,-1.801c0.685,-0.493 1.44,-0.843 2.265,-1.049Zm-4.551,3.844c0.942,0.53 2.003,0.921 3.181,1.171c-0.044,0.501 -0.066,1.017 -0.066,1.547c0,0.736 0.044,1.451 0.133,2.143c-1.032,0.206 -2.033,0.552 -3.005,1.038c-0.531,-0.986 -0.796,-2.047 -0.796,-3.181c0,-0.943 0.184,-1.849 0.553,-2.718Zm0.906,6.894c0.824,-0.324 1.679,-0.56 2.563,-0.707c0.235,1.119 0.596,2.143 1.082,3.071c-0.722,-0.192 -1.395,-0.49 -2.021,-0.895c-0.627,-0.405 -1.168,-0.895 -1.624,-1.469Zm4.794,2.541c-0.486,-0.973 -0.854,-2.114 -1.105,-3.425c0.531,-0.044 1.068,-0.066 1.613,-0.066c0.545,0 1.068,0.022 1.569,0.066c-0.25,1.311 -0.619,2.452 -1.105,3.425c-0.103,0.014 -0.265,0.022 -0.486,0.022c-0.221,0 -0.383,-0.008 -0.486,-0.022Zm2.121,-0.177c0.486,-0.928 0.847,-1.952 1.083,-3.071c0.898,0.147 1.76,0.383 2.585,0.707c-0.471,0.589 -1.02,1.086 -1.646,1.491c-0.626,0.405 -1.3,0.696 -2.022,0.873Z",
|
|
||||||
style: {
|
|
||||||
fill: "#fff",
|
|
||||||
fillRule: "nonzero"
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
var ForwardRef = /*#__PURE__*/React.forwardRef(SvgIconLanguageWorldW);
|
|
||||||
export default __webpack_public_path__ + "static/media/icon-language-world-w.bb8502bb.svg";
|
|
||||||
export { ForwardRef as ReactComponent };
|
|
|
@ -1,53 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
||||||
|
|
||||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
||||||
|
|
||||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
var _ref2 = /*#__PURE__*/React.createElement("desc", null, "Person Icon");
|
|
||||||
|
|
||||||
var _ref3 = /*#__PURE__*/React.createElement("g", {
|
|
||||||
id: "Seiten",
|
|
||||||
stroke: "none",
|
|
||||||
strokeWidth: 1,
|
|
||||||
fill: "none",
|
|
||||||
fillRule: "evenodd",
|
|
||||||
opacity: 1
|
|
||||||
}, /*#__PURE__*/React.createElement("g", {
|
|
||||||
id: "06-desk-lektionsinfo+burger",
|
|
||||||
transform: "translate(-1146.000000, -501.000000)",
|
|
||||||
fill: "#FFFFFF"
|
|
||||||
}, /*#__PURE__*/React.createElement("g", {
|
|
||||||
id: "burgernavi-d",
|
|
||||||
transform: "translate(920.000000, 170.000000)"
|
|
||||||
}, /*#__PURE__*/React.createElement("path", {
|
|
||||||
d: "M241.86087,345.655072 C239.603853,344.603859 238.150728,344.078261 237.501449,344.078261 L233.976812,345.284058 L230.35942,344.078261 C230.081158,344.078261 229.578748,344.209661 228.852174,344.472464 C228.1256,344.735267 227.468602,344.990337 226.881159,345.237681 L226,345.655072 L226,347 L241.86087,347 L241.86087,345.655072 Z M229.988406,340.507246 C231.008701,342.64059 232.338156,343.707246 233.976812,343.707246 C235.584549,343.707246 236.898546,342.64059 237.918841,340.507246 C238.289857,340.383574 238.591303,339.997105 238.823188,339.347826 C239.055074,338.698547 239.155556,338.072467 239.124638,337.469565 C239.09372,336.866664 238.954591,336.565217 238.707246,336.565217 C239.047345,335.606758 239.047345,334.802902 238.707246,334.153623 C238.676328,334.060869 238.629952,333.944928 238.568116,333.805797 C238.50628,333.666666 238.343963,333.419325 238.081159,333.063768 C237.818356,332.708211 237.524639,332.391306 237.2,332.113043 C236.875361,331.834781 236.427056,331.579711 235.855072,331.347826 C235.283089,331.115941 234.657008,331 233.976812,331 C232.925599,331 231.998072,331.262799 231.194203,331.788406 C230.390334,332.314012 229.849277,332.839611 229.571014,333.365217 L229.2,334.153623 L229.153623,334.292754 C229.122705,334.35459 229.099517,334.455072 229.084058,334.594203 C229.068599,334.733334 229.05314,334.887922 229.037681,335.057971 C229.022222,335.22802 229.029952,335.444443 229.06087,335.707246 C229.091788,335.97005 229.138164,336.256037 229.2,336.565217 C228.952656,336.565217 228.813527,336.866664 228.782609,337.469565 C228.751691,338.072467 228.852173,338.698547 229.084058,339.347826 C229.315943,339.997105 229.617389,340.383574 229.988406,340.507246 L229.988406,340.507246 Z M230.127536,335.776812 C230.127536,335.684058 230.166183,335.591305 230.243478,335.498551 C230.320773,335.405797 230.552655,335.282126 230.93913,335.127536 C231.325606,334.972946 231.889851,335.05024 232.631884,335.35942 C233.373917,335.637683 234.085021,335.714976 234.765217,335.591304 C235.445414,335.467632 235.971013,335.313044 236.342029,335.127536 C236.713045,334.942028 236.991303,334.756523 237.176812,334.571014 C237.331402,334.571014 237.462801,334.764249 237.571014,335.150725 C237.679228,335.5372 237.733333,335.869564 237.733333,336.147826 L237.733333,337.353623 C237.918842,337.353623 238.042512,337.369082 238.104348,337.4 C238.166184,337.430918 238.197101,337.570047 238.197101,337.817391 C238.197101,338.064736 238.135266,338.435746 238.011594,338.930435 C237.733332,339.456041 237.455074,339.718841 237.176812,339.718841 C237.084058,339.966185 236.952658,340.259902 236.782609,340.6 C236.61256,340.940098 236.257008,341.403862 235.715942,341.991304 C235.174877,342.578747 234.595172,342.872464 233.976812,342.872464 C233.358451,342.872464 232.786476,342.609664 232.26087,342.084058 C231.735263,341.558451 231.364252,341.032853 231.147826,340.507246 L230.823188,339.718841 C230.699516,339.718841 230.560387,339.649276 230.405797,339.510145 C230.251207,339.371014 230.127537,339.239614 230.034783,339.115942 L229.895652,338.930435 C229.61739,337.879222 229.694684,337.353623 230.127536,337.353623 L230.127536,335.776812 Z",
|
|
||||||
id: "Shape"
|
|
||||||
}))));
|
|
||||||
|
|
||||||
function SvgIconUserNavi(_ref, svgRef) {
|
|
||||||
var title = _ref.title,
|
|
||||||
titleId = _ref.titleId,
|
|
||||||
props = _objectWithoutProperties(_ref, ["title", "titleId"]);
|
|
||||||
|
|
||||||
return /*#__PURE__*/React.createElement("svg", _extends({
|
|
||||||
width: "16px",
|
|
||||||
height: "16px",
|
|
||||||
viewBox: "0 0 16 16",
|
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
|
||||||
xmlnsXlink: "http://www.w3.org/1999/xlink",
|
|
||||||
ref: svgRef,
|
|
||||||
"aria-labelledby": titleId
|
|
||||||
}, props), title === undefined ? /*#__PURE__*/React.createElement("title", {
|
|
||||||
id: titleId
|
|
||||||
}, "Avatar") : title ? /*#__PURE__*/React.createElement("title", {
|
|
||||||
id: titleId
|
|
||||||
}, title) : null, _ref2, _ref3);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ForwardRef = /*#__PURE__*/React.forwardRef(SvgIconUserNavi);
|
|
||||||
export default __webpack_public_path__ + "static/media/icon-user-navi.68643217.svg";
|
|
||||||
export { ForwardRef as ReactComponent };
|
|
|
@ -1,104 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
||||||
|
|
||||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
||||||
|
|
||||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
var _ref2 = /*#__PURE__*/React.createElement("rect", {
|
|
||||||
width: 2,
|
|
||||||
height: 2,
|
|
||||||
x: 2,
|
|
||||||
y: 6
|
|
||||||
});
|
|
||||||
|
|
||||||
var _ref3 = /*#__PURE__*/React.createElement("rect", {
|
|
||||||
width: 2,
|
|
||||||
height: 2,
|
|
||||||
x: 5,
|
|
||||||
y: 6
|
|
||||||
});
|
|
||||||
|
|
||||||
var _ref4 = /*#__PURE__*/React.createElement("rect", {
|
|
||||||
width: 2,
|
|
||||||
height: 2,
|
|
||||||
x: 8,
|
|
||||||
y: 6
|
|
||||||
});
|
|
||||||
|
|
||||||
var _ref5 = /*#__PURE__*/React.createElement("path", {
|
|
||||||
d: "m 11,6 3,0 0,5 -2,0 0,-3 -1,0 z"
|
|
||||||
});
|
|
||||||
|
|
||||||
var _ref6 = /*#__PURE__*/React.createElement("rect", {
|
|
||||||
width: 2,
|
|
||||||
height: 2,
|
|
||||||
x: 12,
|
|
||||||
y: 12
|
|
||||||
});
|
|
||||||
|
|
||||||
var _ref7 = /*#__PURE__*/React.createElement("rect", {
|
|
||||||
width: 6,
|
|
||||||
height: 2,
|
|
||||||
x: 5,
|
|
||||||
y: 12
|
|
||||||
});
|
|
||||||
|
|
||||||
var _ref8 = /*#__PURE__*/React.createElement("rect", {
|
|
||||||
width: 2,
|
|
||||||
height: 2,
|
|
||||||
x: 9,
|
|
||||||
y: 9
|
|
||||||
});
|
|
||||||
|
|
||||||
var _ref9 = /*#__PURE__*/React.createElement("rect", {
|
|
||||||
width: 2,
|
|
||||||
height: 2,
|
|
||||||
x: 6,
|
|
||||||
y: 9
|
|
||||||
});
|
|
||||||
|
|
||||||
var _ref10 = /*#__PURE__*/React.createElement("rect", {
|
|
||||||
width: 2,
|
|
||||||
height: 2,
|
|
||||||
x: 2,
|
|
||||||
y: 12
|
|
||||||
});
|
|
||||||
|
|
||||||
var _ref11 = /*#__PURE__*/React.createElement("rect", {
|
|
||||||
width: 3,
|
|
||||||
height: 2,
|
|
||||||
x: 2,
|
|
||||||
y: 9
|
|
||||||
});
|
|
||||||
|
|
||||||
function SvgKeyboard(_ref, svgRef) {
|
|
||||||
var title = _ref.title,
|
|
||||||
titleId = _ref.titleId,
|
|
||||||
props = _objectWithoutProperties(_ref, ["title", "titleId"]);
|
|
||||||
|
|
||||||
return /*#__PURE__*/React.createElement("svg", _extends({
|
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
|
||||||
width: 16,
|
|
||||||
height: 16,
|
|
||||||
style: {
|
|
||||||
fill: "#888"
|
|
||||||
},
|
|
||||||
ref: svgRef,
|
|
||||||
"aria-labelledby": titleId
|
|
||||||
}, props), title ? /*#__PURE__*/React.createElement("title", {
|
|
||||||
id: titleId
|
|
||||||
}, title) : null, /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("path", {
|
|
||||||
style: {
|
|
||||||
fill: "none",
|
|
||||||
stroke: "#888"
|
|
||||||
},
|
|
||||||
d: "M 0.5,4.5 15.5,4.5 15.5,15.5 0.5,15.5 Z"
|
|
||||||
}), _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10, _ref11));
|
|
||||||
}
|
|
||||||
|
|
||||||
var ForwardRef = /*#__PURE__*/React.forwardRef(SvgKeyboard);
|
|
||||||
export default __webpack_public_path__ + "static/media/keyboard.3a060932.svg";
|
|
||||||
export { ForwardRef as ReactComponent };
|
|
|
@ -1,2 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
export default __webpack_public_path__ + "static/media/nav-cross-black-lg.4c2e3c0d.svg";
|
|
|
@ -1,2 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
export default __webpack_public_path__ + "static/media/nav-cross-black-md.f5825c8f.svg";
|
|
|
@ -1,2 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
export default __webpack_public_path__ + "static/media/nav-cross-lg.a759f43e.svg";
|
|
|
@ -1,2 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
export default __webpack_public_path__ + "static/media/nav-cross-xs.fec778ad.svg";
|
|
|
@ -1,66 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
||||||
|
|
||||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
||||||
|
|
||||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
function SvgSmileyNegative(_ref, svgRef) {
|
|
||||||
var title = _ref.title,
|
|
||||||
titleId = _ref.titleId,
|
|
||||||
props = _objectWithoutProperties(_ref, ["title", "titleId"]);
|
|
||||||
|
|
||||||
return /*#__PURE__*/React.createElement("svg", _extends({
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
viewBox: "0 0 55 54",
|
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
|
||||||
xmlnsXlink: "http://www.w3.org/1999/xlink",
|
|
||||||
xmlSpace: "preserve",
|
|
||||||
style: {
|
|
||||||
fillRule: "evenodd",
|
|
||||||
clipRule: "evenodd",
|
|
||||||
strokeLinejoin: "round",
|
|
||||||
strokeMiterlimit: 1.41421
|
|
||||||
},
|
|
||||||
ref: svgRef,
|
|
||||||
"aria-labelledby": titleId
|
|
||||||
}, props), title ? /*#__PURE__*/React.createElement("title", {
|
|
||||||
id: titleId
|
|
||||||
}, title) : null, /*#__PURE__*/React.createElement("circle", {
|
|
||||||
className: "dot",
|
|
||||||
cx: 27.5,
|
|
||||||
cy: 27,
|
|
||||||
r: 27,
|
|
||||||
style: {
|
|
||||||
fill: "#A9A094"
|
|
||||||
}
|
|
||||||
}), /*#__PURE__*/React.createElement("g", {
|
|
||||||
id: "face"
|
|
||||||
}, /*#__PURE__*/React.createElement("circle", {
|
|
||||||
cx: 18.5,
|
|
||||||
cy: 18.5,
|
|
||||||
r: 3.5,
|
|
||||||
style: {
|
|
||||||
fill: "#fff"
|
|
||||||
}
|
|
||||||
}), /*#__PURE__*/React.createElement("circle", {
|
|
||||||
cx: 36.5,
|
|
||||||
cy: 18.5,
|
|
||||||
r: 3.5,
|
|
||||||
style: {
|
|
||||||
fill: "#fff"
|
|
||||||
}
|
|
||||||
}), /*#__PURE__*/React.createElement("path", {
|
|
||||||
d: "M40.902,32.312l-26.41,5.613l0.624,2.935l26.41,-5.614l-0.624,-2.934Z",
|
|
||||||
style: {
|
|
||||||
fill: "#fff"
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
var ForwardRef = /*#__PURE__*/React.forwardRef(SvgSmileyNegative);
|
|
||||||
export default __webpack_public_path__ + "static/media/smiley-negative.6c289ab6.svg";
|
|
||||||
export { ForwardRef as ReactComponent };
|
|
|
@ -1,57 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
||||||
|
|
||||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
||||||
|
|
||||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
function SvgSmileyPositive(_ref, svgRef) {
|
|
||||||
var title = _ref.title,
|
|
||||||
titleId = _ref.titleId,
|
|
||||||
props = _objectWithoutProperties(_ref, ["title", "titleId"]);
|
|
||||||
|
|
||||||
return /*#__PURE__*/React.createElement("svg", _extends({
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
viewBox: "0 0 55 54",
|
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
|
||||||
xmlnsXlink: "http://www.w3.org/1999/xlink",
|
|
||||||
xmlSpace: "preserve",
|
|
||||||
style: {
|
|
||||||
fillRule: "evenodd",
|
|
||||||
clipRule: "evenodd",
|
|
||||||
strokeLinejoin: "round",
|
|
||||||
strokeMiterlimit: 1.41421
|
|
||||||
},
|
|
||||||
ref: svgRef,
|
|
||||||
"aria-labelledby": titleId
|
|
||||||
}, props), title ? /*#__PURE__*/React.createElement("title", {
|
|
||||||
id: titleId
|
|
||||||
}, title) : null, /*#__PURE__*/React.createElement("circle", {
|
|
||||||
className: "dot",
|
|
||||||
cx: 27.5,
|
|
||||||
cy: 27,
|
|
||||||
r: 27,
|
|
||||||
style: {
|
|
||||||
fill: "#A9A094"
|
|
||||||
}
|
|
||||||
}), /*#__PURE__*/React.createElement("g", {
|
|
||||||
id: "face"
|
|
||||||
}, /*#__PURE__*/React.createElement("path", {
|
|
||||||
d: "M26.439,37.092l-11.313,-11.314l-2.829,2.829l11.314,11.313l2.828,-2.828Z",
|
|
||||||
style: {
|
|
||||||
fill: "#fff"
|
|
||||||
}
|
|
||||||
}), /*#__PURE__*/React.createElement("path", {
|
|
||||||
d: "M39.874,18l-19.092,19.092l2.829,2.828l19.092,-19.092l-2.829,-2.828Z",
|
|
||||||
style: {
|
|
||||||
fill: "#fff"
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
var ForwardRef = /*#__PURE__*/React.forwardRef(SvgSmileyPositive);
|
|
||||||
export default __webpack_public_path__ + "static/media/smiley-positive.3d91069e.svg";
|
|
||||||
export { ForwardRef as ReactComponent };
|
|
|
@ -1,2 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
export default __webpack_public_path__ + "static/media/star-gray.84ad33dd.svg";
|
|
|
@ -1,2 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
export default __webpack_public_path__ + "static/media/star-white.d32dc96a.svg";
|
|
|
@ -1,41 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
||||||
|
|
||||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
||||||
|
|
||||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
var _ref2 = /*#__PURE__*/React.createElement("g", {
|
|
||||||
fill: "none",
|
|
||||||
fillRule: "evenodd"
|
|
||||||
}, /*#__PURE__*/React.createElement("path", {
|
|
||||||
d: "M-2.5-2.5h17v17h-17z"
|
|
||||||
}), /*#__PURE__*/React.createElement("path", {
|
|
||||||
fill: "#FFF",
|
|
||||||
d: "M12.906 1.219C7.434 4.9 5.47 10.25 5.47 10.25H4.406S2.581 7.472.688 6.531l.53-1.062c2.089.469 3.72 1.593 3.72 1.593s1.78-3.61 7.437-6.906l.531 1.063z"
|
|
||||||
}), /*#__PURE__*/React.createElement("path", {
|
|
||||||
d: "M-4 16h20V-4H-4z"
|
|
||||||
}));
|
|
||||||
|
|
||||||
function SvgTick(_ref, svgRef) {
|
|
||||||
var title = _ref.title,
|
|
||||||
titleId = _ref.titleId,
|
|
||||||
props = _objectWithoutProperties(_ref, ["title", "titleId"]);
|
|
||||||
|
|
||||||
return /*#__PURE__*/React.createElement("svg", _extends({
|
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
|
||||||
width: 13,
|
|
||||||
height: 11,
|
|
||||||
viewBox: "0 0 13 11",
|
|
||||||
ref: svgRef,
|
|
||||||
"aria-labelledby": titleId
|
|
||||||
}, props), title ? /*#__PURE__*/React.createElement("title", {
|
|
||||||
id: titleId
|
|
||||||
}, title) : null, _ref2);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ForwardRef = /*#__PURE__*/React.forwardRef(SvgTick);
|
|
||||||
export default __webpack_public_path__ + "static/media/tick.adf0c598.svg";
|
|
||||||
export { ForwardRef as ReactComponent };
|
|
|
@ -1,37 +0,0 @@
|
||||||
const __webpack_public_path__ = "https://learngerman.dw.com/";
|
|
||||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
||||||
|
|
||||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
||||||
|
|
||||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
var _ref2 = /*#__PURE__*/React.createElement("g", {
|
|
||||||
className: "arrowBox",
|
|
||||||
fillRule: "evenodd"
|
|
||||||
}, /*#__PURE__*/React.createElement("g", {
|
|
||||||
className: "arrowArrow",
|
|
||||||
transform: "translate(12, 12), rotate(0 0 0) scale(1.0)"
|
|
||||||
}, /*#__PURE__*/React.createElement("polygon", {
|
|
||||||
points: "12 -4.7125 0 4.9375 -12 -4.6875 -10.375 -5.5 0 0.125 10.375 -5.5"
|
|
||||||
})));
|
|
||||||
|
|
||||||
function SvgToggleableArrow(_ref, svgRef) {
|
|
||||||
var title = _ref.title,
|
|
||||||
titleId = _ref.titleId,
|
|
||||||
props = _objectWithoutProperties(_ref, ["title", "titleId"]);
|
|
||||||
|
|
||||||
return /*#__PURE__*/React.createElement("svg", _extends({
|
|
||||||
viewBox: "0 0 24 24",
|
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
|
||||||
ref: svgRef,
|
|
||||||
"aria-labelledby": titleId
|
|
||||||
}, props), title ? /*#__PURE__*/React.createElement("title", {
|
|
||||||
id: titleId
|
|
||||||
}, title) : null, _ref2);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ForwardRef = /*#__PURE__*/React.forwardRef(SvgToggleableArrow);
|
|
||||||
export default __webpack_public_path__ + "static/media/toggleableArrow.a17a06a6.svg";
|
|
||||||
export { ForwardRef as ReactComponent };
|
|
|
@ -1,23 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { StyledAccordionItem as AccordionItem } from './AccordionItem/AccordionItem';
|
|
||||||
import { grammarOverviewColors } from '../../constants/grammarOverviewColors';
|
|
||||||
|
|
||||||
export const Accordion = ({ className, data }) => {
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
{data.map((accordionData, index) => (
|
|
||||||
<AccordionItem
|
|
||||||
key={accordionData.id}
|
|
||||||
backgroundColor={grammarOverviewColors[index % grammarOverviewColors.length]}
|
|
||||||
title={accordionData.headline}
|
|
||||||
contents={accordionData.grammars}
|
|
||||||
className={className}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StyledAccordion = styled(Accordion)`
|
|
||||||
display: block;
|
|
||||||
`;
|
|
|
@ -1,111 +0,0 @@
|
||||||
import { useRef } from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { useToggle } from '../../../hooks/useToggle';
|
|
||||||
import { colors, resolutions } from '../../../utils/css';
|
|
||||||
import { StyledToggleableArrow as ToggleableArrow } from '../../ToggleableArrow/ToggleableArrow';
|
|
||||||
import { I18nText } from '../../I18n/I18nText';
|
|
||||||
import { getContentUrl } from '../../../utils/url/urlFactory';
|
|
||||||
|
|
||||||
export const AccordionItem = ({ title, contents, className }) => {
|
|
||||||
const element = useRef(null);
|
|
||||||
const [isOpen, { toggleOnClick }] = useToggle();
|
|
||||||
const height = element.current ? element.current.scrollHeight : '0';
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
<button tabIndex={0} className="accordionItem-btn" onClick={toggleOnClick}>
|
|
||||||
<I18nText translation={title} isA="p" className={className} />
|
|
||||||
<ToggleableArrow
|
|
||||||
className="toggleable-arrow"
|
|
||||||
fill={colors.LG_WHITE}
|
|
||||||
{...{
|
|
||||||
isUp: isOpen,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<AccordionItemContent height={height} isOpen={isOpen} ref={element} aria-expanded={isOpen}>
|
|
||||||
{contents.map(content => (
|
|
||||||
<div key={content.id} className="accordionItem-text">
|
|
||||||
<Link
|
|
||||||
tabIndex={isOpen ? 0 : -1}
|
|
||||||
to={getContentUrl({
|
|
||||||
id: content.id,
|
|
||||||
name: content.shortTitle,
|
|
||||||
language: content.language,
|
|
||||||
__typename: content.knowledgeType,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{content.shortTitle}
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</AccordionItemContent>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StyledAccordionItem = styled(AccordionItem)`
|
|
||||||
font-family: DWTheAntiquaB, Georgia, serif;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 25px;
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 0px;
|
|
||||||
|
|
||||||
.accordionItem-btn {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: ${({ backgroundColor }) => backgroundColor};
|
|
||||||
color: ${colors.LG_WHITE};
|
|
||||||
cursor: pointer;
|
|
||||||
align-items: center;
|
|
||||||
border: none;
|
|
||||||
:hover {
|
|
||||||
background-color: ${colors.LG_GRAY_9};
|
|
||||||
}
|
|
||||||
:focus {
|
|
||||||
color: ${colors.LG_BAHAMA_BLUE};
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggleable-arrow {
|
|
||||||
right: 18px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const AccordionItemContent = styled.div`
|
|
||||||
max-height: ${({ isOpen, height }) => (isOpen ? height : '1')}px;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: ${colors.LG_WHITE};
|
|
||||||
transition: max-height 0.7s;
|
|
||||||
|
|
||||||
.accordionItem-text {
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 25px;
|
|
||||||
border-bottom-width: 1px;
|
|
||||||
border-bottom-style: solid;
|
|
||||||
border-bottom-color: ${colors.LG_GRAY_12};
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: ${colors.LG_GRAY_9};
|
|
||||||
text-decoration: none;
|
|
||||||
padding: 20px;
|
|
||||||
display: block;
|
|
||||||
&:hover {
|
|
||||||
color: ${colors.LG_WHITE};
|
|
||||||
background-color: ${colors.LG_GRAY_9};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.mobile}px) {
|
|
||||||
font-size: 15px;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,214 +0,0 @@
|
||||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
|
||||||
import { useParams } from 'react-router';
|
|
||||||
import { Default404Page } from '../pages/Default404Page';
|
|
||||||
import { urlPrefixToType } from '../utils/url/url';
|
|
||||||
import { CoursePage } from '../pages/CoursePage';
|
|
||||||
import { GrammarPage } from '../pages/GrammarPage';
|
|
||||||
import { DashboardPage } from '../pages/DashboardPage';
|
|
||||||
import { GrammarOverviewPage } from '../pages/GrammarOverviewPage';
|
|
||||||
import { pathPartials } from '../utils/url/pathAnalyser';
|
|
||||||
import { LessonPage } from '../pages/LessonPage';
|
|
||||||
import {
|
|
||||||
PAGE_GRAMMAR,
|
|
||||||
PAGE_LEGAL,
|
|
||||||
PAGE_OVERVIEW,
|
|
||||||
PAGE_PASSWORD_CHANGE,
|
|
||||||
PAGE_PASSWORD_RESET,
|
|
||||||
PAGE_PLACEMENT_TEST,
|
|
||||||
PAGE_PROFILE,
|
|
||||||
PAGE_REGISTRATION,
|
|
||||||
PAGE_VOCABULARY,
|
|
||||||
PAGE_VOCABULARY_TRAINER,
|
|
||||||
PAGE_VOCABULARY_TRAINER_START,
|
|
||||||
STATUS_FEEDBACK,
|
|
||||||
} from '../utils/url/urlFactory';
|
|
||||||
import { FEEDBACK_TYPES } from '../constants/feedback';
|
|
||||||
import { RestrictedRoute } from './RestrictedRoute/RestrictedRoute';
|
|
||||||
import { ConfirmUserRegistrationContainer } from './user/ConfirmUserRegistrationContainer';
|
|
||||||
import { Registration } from './user/Registration';
|
|
||||||
import { PasswordChange } from './user/PasswordChange';
|
|
||||||
import { PasswordReset } from './user/PasswordReset';
|
|
||||||
import { StatusFeedback } from './StatusFeedback/StatusFeedback';
|
|
||||||
import { PageContainer, withPageContainerAndMetadata } from './Page/PageContainer';
|
|
||||||
import { UserProfile } from './user/UserProfile';
|
|
||||||
import { SetNewPassword } from './user/SetNewPassword';
|
|
||||||
import { PlacementTestPage } from '../pages/PlacementTestPage';
|
|
||||||
import { VocabularyTrainer } from './VocabularyTrainer/VocabularyTrainer';
|
|
||||||
import { PlacementAndFinalTestResultPage } from '../pages/PlacementAndFinalTestResultPage';
|
|
||||||
import { HelpPage } from '../pages/HelpPage';
|
|
||||||
import { GTM_NO_CONTENT_PAGE_IDS, PAGE_TYPES } from '../constants/pageTypes';
|
|
||||||
import { PageMetaData } from './Page/PageMetaData';
|
|
||||||
import { LegalPage } from '../pages/LegalPage';
|
|
||||||
import { VocabularyPage } from '../pages/VocabularyPage';
|
|
||||||
import { GtmScriptWithDataLayer } from './GoogleTagManager';
|
|
||||||
|
|
||||||
const typeToPage = {
|
|
||||||
COURSE: {
|
|
||||||
type: 'Course',
|
|
||||||
component: CoursePage,
|
|
||||||
},
|
|
||||||
GRAMMAR: {
|
|
||||||
type: 'Grammar',
|
|
||||||
component: GrammarPage,
|
|
||||||
},
|
|
||||||
LESSON: {
|
|
||||||
type: 'Lesson',
|
|
||||||
component: LessonPage,
|
|
||||||
},
|
|
||||||
PLACEMENT: {
|
|
||||||
type: 'Placement',
|
|
||||||
component: PlacementAndFinalTestResultPage,
|
|
||||||
},
|
|
||||||
FINAL: {
|
|
||||||
type: 'Final',
|
|
||||||
component: PlacementAndFinalTestResultPage,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ContentPage = () => {
|
|
||||||
const params = useParams();
|
|
||||||
const pageDef = typeToPage[urlPrefixToType(params.typeId)];
|
|
||||||
const Tag = pageDef.component;
|
|
||||||
return <Tag {...params} type={pageDef.type} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const App = () => (
|
|
||||||
<Switch>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path="/"
|
|
||||||
render={() => (
|
|
||||||
<Redirect
|
|
||||||
to={{
|
|
||||||
pathname: '/en/overview',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})${PAGE_OVERVIEW}`}
|
|
||||||
component={DashboardPage}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})/:title(${pathPartials.titlePathRegex})?/l-:lessonId(${pathPartials.contentIdRegex})`}
|
|
||||||
component={LessonPage}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})/:title(${pathPartials.titlePathRegex})?/:typeId(${pathPartials.contentTypeRegex})-:contentId(${pathPartials.contentIdRegex})`}
|
|
||||||
component={ContentPage}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})${PAGE_GRAMMAR}`}
|
|
||||||
component={GrammarOverviewPage}
|
|
||||||
/>
|
|
||||||
<Route exact path={`/:langCode(${pathPartials.langCodeRegex})/help`} component={HelpPage} />
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})${PAGE_LEGAL}`}
|
|
||||||
component={LegalPage}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})${PAGE_VOCABULARY}`}
|
|
||||||
component={VocabularyPage}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})${PAGE_REGISTRATION}`}
|
|
||||||
render={() =>
|
|
||||||
withPageContainerAndMetadata({
|
|
||||||
component: Registration,
|
|
||||||
pageType: PAGE_TYPES.REGISTER_USER,
|
|
||||||
useDescriptionTranslation: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})/user/password/set`}
|
|
||||||
render={({ location }) => (
|
|
||||||
<PageContainer>
|
|
||||||
<PageMetaData pageType={PAGE_TYPES.PASSWORD_SET} addKeywordsAndDescription={false} />
|
|
||||||
<GtmScriptWithDataLayer noContentPageId={GTM_NO_CONTENT_PAGE_IDS.PASSWORD_SET} />
|
|
||||||
<SetNewPassword
|
|
||||||
confirmationId={new URLSearchParams(location.search).get('confirmationId')}
|
|
||||||
/>
|
|
||||||
</PageContainer>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})/user/register/confirm`}
|
|
||||||
render={({ location }) => (
|
|
||||||
<ConfirmUserRegistrationContainer
|
|
||||||
confirmationId={new URLSearchParams(location.search).get('confirmationId')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<RestrictedRoute
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})${PAGE_PROFILE}`}
|
|
||||||
render={() =>
|
|
||||||
withPageContainerAndMetadata({
|
|
||||||
component: UserProfile,
|
|
||||||
pageType: PAGE_TYPES.USER_PROFILE,
|
|
||||||
addDescriptionAndKeywordsMeta: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
feedbackType={FEEDBACK_TYPES.MISSING_USER_PROFILE}
|
|
||||||
/>
|
|
||||||
<RestrictedRoute
|
|
||||||
exact
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})${PAGE_PASSWORD_CHANGE}`}
|
|
||||||
render={() =>
|
|
||||||
withPageContainerAndMetadata({
|
|
||||||
component: PasswordChange,
|
|
||||||
pageType: PAGE_TYPES.PASSWORD_CHANGE,
|
|
||||||
addDescriptionAndKeywordsMeta: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<RestrictedRoute
|
|
||||||
exact
|
|
||||||
path={[
|
|
||||||
`/:langCode(${pathPartials.langCodeRegex})${PAGE_VOCABULARY_TRAINER}`,
|
|
||||||
`/:langCode(${pathPartials.langCodeRegex})${PAGE_VOCABULARY_TRAINER_START}`,
|
|
||||||
]}
|
|
||||||
render={() =>
|
|
||||||
withPageContainerAndMetadata({
|
|
||||||
component: VocabularyTrainer,
|
|
||||||
pageType: PAGE_TYPES.VOCABULARY_TRAINER,
|
|
||||||
addDescriptionAndKeywordsMeta: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})${PAGE_PASSWORD_RESET}`}
|
|
||||||
render={() =>
|
|
||||||
withPageContainerAndMetadata({
|
|
||||||
component: PasswordReset,
|
|
||||||
pageType: PAGE_TYPES.PASSWORD_RESET,
|
|
||||||
addDescriptionAndKeywordsMeta: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})${STATUS_FEEDBACK}/:feedbackType(${pathPartials.feedbackTypes})`}
|
|
||||||
render={() =>
|
|
||||||
withPageContainerAndMetadata({
|
|
||||||
component: StatusFeedback,
|
|
||||||
pageType: PAGE_TYPES.FEEDBACK_STATUS,
|
|
||||||
addDescriptionAndKeywordsMeta: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`/:langCode(${pathPartials.langCodeRegex})${PAGE_PLACEMENT_TEST}`}
|
|
||||||
component={PlacementTestPage}
|
|
||||||
/>
|
|
||||||
<Route exact path={`/:langCode(${pathPartials.langCodeRegex})`} component={Default404Page} />
|
|
||||||
<Route component={Default404Page} />
|
|
||||||
</Switch>
|
|
||||||
);
|
|
|
@ -1,177 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { colors, resolutions } from '../../utils/css';
|
|
||||||
import navCrossBlackMd from '../../assets/svg/nav-cross-black-md.svg';
|
|
||||||
import navCrossBlackLg from '../../assets/svg/nav-cross-black-lg.svg';
|
|
||||||
import navCrossXs from '../../assets/svg/nav-cross-xs.svg';
|
|
||||||
import navCrossLg from '../../assets/svg/nav-cross-lg.svg';
|
|
||||||
import { StyledIconBar as IconBar } from '../IconBar/IconBar';
|
|
||||||
|
|
||||||
export const BurgerButton = ({
|
|
||||||
isNavMenuOpen,
|
|
||||||
toggleIsNavMenuOpen,
|
|
||||||
setIsLanguageSubMenuOpen,
|
|
||||||
className,
|
|
||||||
}) => (
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
toggleIsNavMenuOpen();
|
|
||||||
setIsLanguageSubMenuOpen(false);
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
className={className}
|
|
||||||
data-toggle="collapse"
|
|
||||||
data-target="#nav-user-menu"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-controls="navbar"
|
|
||||||
aria-expanded={isNavMenuOpen}
|
|
||||||
>
|
|
||||||
{isNavMenuOpen && <span className="close-menu" />}
|
|
||||||
<span className="screen-reader-only">Toggle navigation</span>
|
|
||||||
|
|
||||||
{!isNavMenuOpen && (
|
|
||||||
<>
|
|
||||||
<IconBar />
|
|
||||||
<IconBar />
|
|
||||||
<IconBar />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
|
|
||||||
const defaultHoverStyles = `
|
|
||||||
&:hover {
|
|
||||||
.close-menu {
|
|
||||||
background: url(${navCrossXs});
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
${IconBar} {
|
|
||||||
background-color: ${colors.LG_BLACK};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/*
|
|
||||||
CSS parsed by a string helper function to return css string with styling blocks as arguments,
|
|
||||||
could not be tested with jest-styled-components.
|
|
||||||
It reads the media option correctly (media: (hover:hover)). But it throws an unknown error.
|
|
||||||
While using string templates passes with known media option.
|
|
||||||
*/
|
|
||||||
const mediaHoverDeviceDetection = `
|
|
||||||
@media (hover: hover) and (pointer: fine) {
|
|
||||||
${defaultHoverStyles}
|
|
||||||
}
|
|
||||||
@media (hover: none) and (pointer: coarse) {
|
|
||||||
${defaultHoverStyles}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const closeMenuMobile = `
|
|
||||||
height: 12px;
|
|
||||||
width: 13px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const closeMenuTablet = `
|
|
||||||
height: 21px;
|
|
||||||
width: 22px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const StyledBurgerButton = styled(BurgerButton)`
|
|
||||||
z-index: 3000;
|
|
||||||
display: block;
|
|
||||||
position: fixed;
|
|
||||||
background-color: transparent;
|
|
||||||
background-image: none;
|
|
||||||
padding: 0;
|
|
||||||
/*! @noflip */
|
|
||||||
right: 20px;
|
|
||||||
top: 13px;
|
|
||||||
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
|
|
||||||
-webkit-appearance: button;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.mobile}px) {
|
|
||||||
top: 20px;
|
|
||||||
&:hover {
|
|
||||||
.close-menu {
|
|
||||||
${closeMenuMobile}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: ${resolutions.min.tablet}px) and (max-width: ${resolutions.max.tablet}px) {
|
|
||||||
top: 28px;
|
|
||||||
&:hover {
|
|
||||||
.close-menu {
|
|
||||||
${closeMenuTablet}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: ${resolutions.min.desktop}px) {
|
|
||||||
margin-top: 32px;
|
|
||||||
z-index: 1;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
/*! @noflip */
|
|
||||||
right: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
${mediaHoverDeviceDetection}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: 0;
|
|
||||||
.close-menu {
|
|
||||||
background: url(${navCrossBlackLg});
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-menu {
|
|
||||||
background: url(${navCrossLg});
|
|
||||||
display: block;
|
|
||||||
height: 21px;
|
|
||||||
margin-top: 5px;
|
|
||||||
/*! @noflip */
|
|
||||||
right: 15px;
|
|
||||||
width: 22px;
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.min.desktop}px) {
|
|
||||||
background: url(${navCrossXs});
|
|
||||||
height: 12px;
|
|
||||||
width: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: ${resolutions.min.tablet}px) and (max-width: ${resolutions.max.tablet}px) {
|
|
||||||
background: url(${navCrossBlackMd});
|
|
||||||
height: 21px;
|
|
||||||
margin-top: 5px;
|
|
||||||
width: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: ${resolutions.min.desktop}px) {
|
|
||||||
background: url(${navCrossBlackMd});
|
|
||||||
height: 21px;
|
|
||||||
margin-top: 0px;
|
|
||||||
position: absolute;
|
|
||||||
/*! @noflip */
|
|
||||||
right: 0px;
|
|
||||||
width: 22px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.screen-reader-only {
|
|
||||||
border: none;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
height: 1px;
|
|
||||||
margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 0;
|
|
||||||
position: absolute;
|
|
||||||
width: 1px;
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,44 +0,0 @@
|
||||||
import { hasErrorClassAssigner } from '../../utils/errorClassAssigner';
|
|
||||||
import { getTranslatedValidationMessage } from '../../utils/validation/validationMessages';
|
|
||||||
import { useI18nContext } from '../../context/I18nContext';
|
|
||||||
|
|
||||||
export const Checkbox = ({
|
|
||||||
name,
|
|
||||||
labelText,
|
|
||||||
validationErrorTranslationKey,
|
|
||||||
register,
|
|
||||||
validationError,
|
|
||||||
...restProps
|
|
||||||
}) => {
|
|
||||||
const { i18n } = useI18nContext();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="custom-checkbox">
|
|
||||||
<div className={hasErrorClassAssigner('form-group', validationError)}>
|
|
||||||
<div className="input-group">
|
|
||||||
<span className="input-group-addon">
|
|
||||||
<input
|
|
||||||
className="form-control"
|
|
||||||
name={name}
|
|
||||||
type="checkbox"
|
|
||||||
aria-label={name}
|
|
||||||
{...register}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<label className="checkbox-label">{labelText}</label>
|
|
||||||
</div>
|
|
||||||
{validationError && (
|
|
||||||
<small className="help-block">
|
|
||||||
{getTranslatedValidationMessage({
|
|
||||||
name,
|
|
||||||
validationErrorTranslationKey,
|
|
||||||
validationType: validationError.type,
|
|
||||||
t: i18n.t,
|
|
||||||
})}
|
|
||||||
</small>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,53 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { useRef } from 'react';
|
|
||||||
import { useToggle } from '../../hooks/useToggle';
|
|
||||||
import { StyledToggleableArrow as ToggleableArrow } from '../ToggleableArrow/ToggleableArrow';
|
|
||||||
import { colors } from '../../utils/css';
|
|
||||||
import { useTranslation } from '../../hooks/useTranslation';
|
|
||||||
|
|
||||||
export const AccordionContainer = ({ title, children, className }) => {
|
|
||||||
const titleTranslation = useTranslation(title);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<details className={className}>
|
|
||||||
<summary className="row noVMargins">
|
|
||||||
<div className="col-sm-offset-1 col-sm-10 col-lg-offset-2 col-lg-8">
|
|
||||||
<h4>
|
|
||||||
{titleTranslation}
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
</summary>
|
|
||||||
<AccordionContainerContent
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</AccordionContainerContent>
|
|
||||||
</details>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AccordionContainerContent = styled.div`
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: ${colors.LG_WHITE};
|
|
||||||
transition: max-height 0.7s;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const StyledAccordionContainer = styled(AccordionContainer)`
|
|
||||||
button {
|
|
||||||
outline: none;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
background-color: ${colors.LG_WHITE};
|
|
||||||
width: 10em;
|
|
||||||
}
|
|
||||||
.toggleable-arrow {
|
|
||||||
position: relative;
|
|
||||||
top: 0.1em;
|
|
||||||
margin-left: 0.2em;
|
|
||||||
width: 20px;
|
|
||||||
height: 17px;
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,70 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { useRef } from 'react';
|
|
||||||
import { useToggle } from '../../hooks/useToggle';
|
|
||||||
import { StyledToggleableArrow as ToggleableArrow } from '../ToggleableArrow/ToggleableArrow';
|
|
||||||
import { colors } from '../../utils/css';
|
|
||||||
import { useTranslation } from '../../hooks/useTranslation';
|
|
||||||
|
|
||||||
export const AccordionContainer = ({ title, children, className }) => {
|
|
||||||
const element = useRef(null);
|
|
||||||
const [isOpen, { toggleOnClick }] = useToggle();
|
|
||||||
const height = element.current ? element.current.scrollHeight : '0';
|
|
||||||
const titleTranslation = useTranslation(title);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
<div className="row noVMargins">
|
|
||||||
<div className="col-sm-offset-1 col-sm-10 col-lg-offset-2 col-lg-8">
|
|
||||||
<button tabIndex={0} onClick={toggleOnClick}>
|
|
||||||
<h4>
|
|
||||||
{titleTranslation}
|
|
||||||
<ToggleableArrow
|
|
||||||
className="toggleable-arrow"
|
|
||||||
fill={colors.LG_BLACK}
|
|
||||||
{...{
|
|
||||||
isUp: isOpen,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</h4>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<AccordionContainerContent
|
|
||||||
height={height}
|
|
||||||
isOpen={isOpen}
|
|
||||||
ref={element}
|
|
||||||
aria-expanded={isOpen}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</AccordionContainerContent>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AccordionContainerContent = styled.div`
|
|
||||||
max-height: ${({ isOpen, height }) => (isOpen ? height : '1')}px;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: ${colors.LG_WHITE};
|
|
||||||
transition: max-height 0.7s;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const StyledAccordionContainer = styled(AccordionContainer)`
|
|
||||||
button {
|
|
||||||
outline: none;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
background-color: ${colors.LG_WHITE};
|
|
||||||
width: 10em;
|
|
||||||
}
|
|
||||||
.toggleable-arrow {
|
|
||||||
position: relative;
|
|
||||||
top: 0.1em;
|
|
||||||
margin-left: 0.2em;
|
|
||||||
width: 20px;
|
|
||||||
height: 17px;
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,5 +0,0 @@
|
||||||
export const ContentBottomLine = () => (
|
|
||||||
<div className="row vocabulary list-end">
|
|
||||||
<div className="col-sm-offset-1 col-sm-10 col-lg-offset-2 col-lg-8 vocabulary-entry" />
|
|
||||||
</div>
|
|
||||||
);
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { ContentNavTitle } from './ContentNavTitle/ContentNavTitle';
|
|
||||||
|
|
||||||
export const ContentContainer = ({ title, children }) => (
|
|
||||||
<div className="section exercise-container vocabulary copy">
|
|
||||||
<ContentNavTitle {...{ title, top: true }} />
|
|
||||||
{children}
|
|
||||||
<ContentNavTitle {...{ title, bottom: true }} />
|
|
||||||
</div>
|
|
||||||
);
|
|
|
@ -1,13 +0,0 @@
|
||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
export const ContentHeadline = ({ children, isStandalone = false }) => {
|
|
||||||
return (
|
|
||||||
<div className="headline-container row">
|
|
||||||
<div className="col-sm-offset-1 col-sm-10 col-lg-offset-2 col-lg-8 ">
|
|
||||||
<div className={classnames('exercise-headline', { standalone: isStandalone })}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,22 +0,0 @@
|
||||||
import classnames from 'classnames';
|
|
||||||
import { I18nText } from '../../I18n/I18nText';
|
|
||||||
|
|
||||||
export const ContentNavTitle = ({ title, top, bottom }) => (
|
|
||||||
<div className="row noVMargins">
|
|
||||||
<div className="col-sm-offset-1 col-sm-10 col-lg-offset-2 col-lg-8">
|
|
||||||
<div
|
|
||||||
className={classnames('exercise-nav', {
|
|
||||||
top,
|
|
||||||
bottom,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<I18nText
|
|
||||||
isA="div"
|
|
||||||
className="exercise-nav-title"
|
|
||||||
style={{ direction: 'ltr' }}
|
|
||||||
translation={title}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { I18nText } from '../../I18n/I18nText';
|
|
||||||
|
|
||||||
export const ContentSection = ({ title, children }) => (
|
|
||||||
<>
|
|
||||||
<div className="row vocabulary">
|
|
||||||
<div className="col-sm-offset-1 col-sm-10 col-lg-offset-2 col-lg-8">
|
|
||||||
<I18nText isA="h3" translation={title} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="row vocabulary">
|
|
||||||
<div className="col-sm-offset-1 col-sm-10 col-lg-offset-2 col-lg-8">
|
|
||||||
<div className="richtext-content-container">{children}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
|
@ -1,69 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { StyledHeroTemplate as HeroTemplate } from '../../HeroTemplate/HeroTemplate';
|
|
||||||
import { resolutions } from '../../../utils/css';
|
|
||||||
|
|
||||||
const determineImage = ({ imageId, imageBasePath }) => {
|
|
||||||
return `${imageBasePath}/${imageId}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StyledCourseHeroImage = styled(HeroTemplate)`
|
|
||||||
background: url(${determineImage}_509.jpg) center center / cover no-repeat;
|
|
||||||
height: 320px;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow-x: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.identifier {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font: 700 40px Helvetica, Arial, sans-serif;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
/* @noflip */
|
|
||||||
direction: ltr;
|
|
||||||
font: 400 40px DWTheAntiquaB, Georgia, serif;
|
|
||||||
opacity: 1;
|
|
||||||
margin: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.tablet}px) {
|
|
||||||
.identifier,
|
|
||||||
.description {
|
|
||||||
font-size: 36px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 992px) {
|
|
||||||
background: url(${determineImage}_507.jpg) center center / cover no-repeat;
|
|
||||||
|
|
||||||
.identifier,
|
|
||||||
.description {
|
|
||||||
font-size: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.mobile}px) {
|
|
||||||
.identifier,
|
|
||||||
.description {
|
|
||||||
font-size: 28px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
background: url(${determineImage}_503.jpg) center center / cover no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 360px) {
|
|
||||||
background: url(${determineImage}_501.jpg) center center / cover no-repeat;
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,30 +0,0 @@
|
||||||
import { resolutions } from '../../../../utils/css';
|
|
||||||
import { useToggle } from '../../../../hooks/useToggle';
|
|
||||||
import { StyledCourseHeader as CourseHeader } from '../CourseHeader/CourseHeader';
|
|
||||||
import { StyledLessonList as LessonList } from '../LessonList/LessonList';
|
|
||||||
import globals from '../../../../utils/globals';
|
|
||||||
|
|
||||||
export const CourseAccordion = ({ groupNameTranslationKey, groupedLessons, onClickReset }) => {
|
|
||||||
const isOnDesktop = globals.window.innerWidth >= resolutions.min.desktop;
|
|
||||||
const [isOpen, { toggleOnClick, toggleOnKeyPress }] = useToggle({ initialState: isOnDesktop });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="accordion" aria-label="course.accordion">
|
|
||||||
<CourseHeader
|
|
||||||
{...{
|
|
||||||
isOpen,
|
|
||||||
groupNameTranslationKey,
|
|
||||||
onClick: toggleOnClick,
|
|
||||||
onKeyPress: toggleOnKeyPress,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<LessonList
|
|
||||||
{...{
|
|
||||||
isOpen,
|
|
||||||
groupedLessons,
|
|
||||||
onClickReset,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,78 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import { I18nText } from '../../../I18n/I18nText';
|
|
||||||
import { colors, resolutions } from '../../../../utils/css';
|
|
||||||
import { StyledToggleableArrow as ToggleableArrow } from '../../../ToggleableArrow/ToggleableArrow';
|
|
||||||
|
|
||||||
const CourseHeader = ({ className, groupNameTranslationKey, isOpen, onClick, onKeyPress }) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classnames(className, 'header')}
|
|
||||||
role="button"
|
|
||||||
tabIndex="0"
|
|
||||||
onClick={onClick}
|
|
||||||
onKeyPress={onKeyPress}
|
|
||||||
aria-label="course.header"
|
|
||||||
>
|
|
||||||
<I18nText
|
|
||||||
className={classnames('title')}
|
|
||||||
{...{
|
|
||||||
isA: 'h2',
|
|
||||||
}}
|
|
||||||
aria-label="course.groupName"
|
|
||||||
translation={groupNameTranslationKey || 'course.otherLessons'}
|
|
||||||
/>
|
|
||||||
<ToggleableArrow
|
|
||||||
className="toggleable-arrow"
|
|
||||||
fill={colors.LG_GRAY_13}
|
|
||||||
{...{
|
|
||||||
isUp: isOpen,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StyledCourseHeader = styled(CourseHeader)`
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
border-top: 1px solid ${colors.LG_GRAY_TRANSPARENT_1};
|
|
||||||
height: 163px;
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
height: 25px;
|
|
||||||
background-color: ${colors.LG_WHITE};
|
|
||||||
color: ${colors.LG_GRAY_13};
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow-x: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
font-family: Helvetica, Arial, sans-serif;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 22px;
|
|
||||||
line-height: 23px;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggleable-arrow {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.tablet}px) {
|
|
||||||
height: 94px;
|
|
||||||
pointer-events: all;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
padding: 0 45px 0 20px;
|
|
||||||
border-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggleable-arrow {
|
|
||||||
display: inline-block;
|
|
||||||
right: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,48 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { colors, resolutions } from '../../../utils/css';
|
|
||||||
import { CourseAccordion } from './CourseAccordion/CourseAccordion';
|
|
||||||
|
|
||||||
export const CourseList = ({ className, groupedLessons, onClickResetLesson }) => (
|
|
||||||
<ul className={className} aria-label="course.list">
|
|
||||||
{Object.keys(groupedLessons).map(key => (
|
|
||||||
// group lessons by their group names
|
|
||||||
<li key={groupedLessons[key][0].id} className="course">
|
|
||||||
<CourseAccordion
|
|
||||||
{...{
|
|
||||||
groupNameTranslationKey: groupedLessons[key][0].groupName,
|
|
||||||
groupedLessons: groupedLessons[key],
|
|
||||||
onClickReset: onClickResetLesson,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const StyledCourseList = styled(CourseList)`
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(4, 1fr);
|
|
||||||
width: 100%;
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
.course {
|
|
||||||
display: inline-block;
|
|
||||||
background: ${colors.LG_WHITE};
|
|
||||||
|
|
||||||
&:nth-child(odd) {
|
|
||||||
.accordion .lesson-list {
|
|
||||||
background-color: ${colors.LG_GRAY_3};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.tablet}px) {
|
|
||||||
grid-template-columns: 100%;
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,120 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { StyledText as Text } from '../../../Text/Text';
|
|
||||||
import { colors } from '../../../../utils/css';
|
|
||||||
import { getContentUrl } from '../../../../utils/url/urlFactory';
|
|
||||||
import { StyledLessonScore as LessonScore } from './LessonScore';
|
|
||||||
import { StyledSmallProgressResetButton as ProgressResetButton } from '../../Progress/ProgressResetButton';
|
|
||||||
import { LearnProgressIcon } from '../../../LearnProgressIcon/LearnProgressIcon';
|
|
||||||
import { findLessonLearnProgressSelector } from '../../../../state/progress/learnProgressSelectors';
|
|
||||||
|
|
||||||
export const LessonItem = ({ lesson, className, onClickReset }) => {
|
|
||||||
const lessonId = lesson.id;
|
|
||||||
const lessonProgress = useSelector(findLessonLearnProgressSelector(lessonId));
|
|
||||||
|
|
||||||
const onHandleResetClick = e => {
|
|
||||||
onClickReset(e, lessonId);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Link
|
|
||||||
className={className}
|
|
||||||
to={getContentUrl({
|
|
||||||
id: lesson.id,
|
|
||||||
name: lesson.shortTitle,
|
|
||||||
language: lesson.language,
|
|
||||||
__typename: 'Lesson',
|
|
||||||
})}
|
|
||||||
tabIndex={0}
|
|
||||||
>
|
|
||||||
<Text className="title" isA="h3" aria-label="lesson.title">
|
|
||||||
{lesson.shortTitle}
|
|
||||||
</Text>
|
|
||||||
<div className={`tile-progress`}>
|
|
||||||
<LearnProgressIcon learnProgress={lessonProgress} />
|
|
||||||
</div>
|
|
||||||
<Text className="learningTarget" aria-label="lesson.learningTarget">
|
|
||||||
{lesson.learningTargetHeadline}
|
|
||||||
</Text>
|
|
||||||
{lessonProgress && (
|
|
||||||
<>
|
|
||||||
<LessonScore
|
|
||||||
className="lessonScore"
|
|
||||||
lessonProgress={lessonProgress}
|
|
||||||
onClickReset={onClickReset}
|
|
||||||
lessonId={lesson.id}
|
|
||||||
/>
|
|
||||||
<ProgressResetButton className="resetButton" onClickReset={onHandleResetClick} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Text className="categories" aria-label="lesson.categories">
|
|
||||||
{lesson.grammarDescription}
|
|
||||||
</Text>
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const alignProgressData = '5px';
|
|
||||||
|
|
||||||
export const StyledLessonItem = styled(LessonItem)`
|
|
||||||
border-top: 1px solid ${colors.LG_GRAY_TRANSPARENT_1};
|
|
||||||
color: ${colors.LG_GRAY_8};
|
|
||||||
display: grid;
|
|
||||||
font-size: 14px;
|
|
||||||
grid-template-columns: 1fr auto;
|
|
||||||
grid-template-rows: repeat(3, 1fr);
|
|
||||||
height: 102px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
overflow-y: hidden;
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: ${colors.LG_GRAY_8};
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
color: ${colors.LG_GRAY_9};
|
|
||||||
font-family: DWTheAntiquaBBold, Georgia-Bold, serif;
|
|
||||||
font-size: 17px;
|
|
||||||
line-height: 24px;
|
|
||||||
margin: 0;
|
|
||||||
overflow-x: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.learningTarget,
|
|
||||||
.categories {
|
|
||||||
font-family: DWTheAntiquaB, Georgia, serif;
|
|
||||||
overflow-x: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.categories {
|
|
||||||
grid-column: 1 / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-progress {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
padding: 5px 0 0 0;
|
|
||||||
padding-right: ${alignProgressData};
|
|
||||||
}
|
|
||||||
|
|
||||||
.lessonScore {
|
|
||||||
grid-column-start: 2;
|
|
||||||
justify-self: flex-end;
|
|
||||||
padding-right: ${alignProgressData};
|
|
||||||
}
|
|
||||||
|
|
||||||
.resetButton {
|
|
||||||
grid-column-start: 2;
|
|
||||||
grid-row-start: 3;
|
|
||||||
justify-self: center;
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,41 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { colors } from '../../../../utils/css';
|
|
||||||
import { calculatePercentage } from '../../../../utils/commons';
|
|
||||||
|
|
||||||
export const LessonScore = ({ className, lessonProgress }) => {
|
|
||||||
const { resultPoints, maxPointsForAllExercises } = lessonProgress;
|
|
||||||
|
|
||||||
const resultPointsClassName = () => {
|
|
||||||
const resultInPercentage = calculatePercentage(resultPoints, maxPointsForAllExercises);
|
|
||||||
|
|
||||||
if (resultInPercentage >= 70) {
|
|
||||||
return 'good';
|
|
||||||
}
|
|
||||||
if (resultInPercentage <= 40) {
|
|
||||||
return 'low';
|
|
||||||
}
|
|
||||||
return 'mid';
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
<span className={resultPointsClassName()}>{resultPoints}</span>
|
|
||||||
<span>/</span>
|
|
||||||
<span>{maxPointsForAllExercises}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StyledLessonScore = styled(LessonScore)`
|
|
||||||
font-size: 14px;
|
|
||||||
|
|
||||||
.low {
|
|
||||||
color: ${colors.LG_RED_SCORE};
|
|
||||||
}
|
|
||||||
.mid {
|
|
||||||
color: ${colors.LG_GRAY_11};
|
|
||||||
}
|
|
||||||
.good {
|
|
||||||
color: ${colors.LG_GREEN_SCORE};
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,47 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import { colors, resolutions } from '../../../../utils/css';
|
|
||||||
import { StyledLessonItem as LessonItem } from '../LessionItem/LessonItem';
|
|
||||||
|
|
||||||
export const LessonList = ({ className, groupedLessons, onClickReset, isOpen = true }) => (
|
|
||||||
// eslint-disable-next-line jsx-a11y/role-supports-aria-props
|
|
||||||
<ul
|
|
||||||
className={classnames(className, 'lesson-list', { collapsed: !isOpen })}
|
|
||||||
aria-expanded={isOpen}
|
|
||||||
aria-label="lesson.list"
|
|
||||||
>
|
|
||||||
{groupedLessons.map(lesson => (
|
|
||||||
<li key={lesson.id} className="lesson-item">
|
|
||||||
<LessonItem
|
|
||||||
{...{
|
|
||||||
lesson: lesson.target,
|
|
||||||
onClickReset,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const StyledLessonList = styled(LessonList)`
|
|
||||||
transition: max-height 0.3s linear;
|
|
||||||
overflow-y: hidden;
|
|
||||||
visibility: visible;
|
|
||||||
max-height: ${props => props.groupedLessons.length * 102}px;
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.tablet}px) {
|
|
||||||
background-color: ${colors.LG_GRAY_3};
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.collapsed {
|
|
||||||
visibility: hidden;
|
|
||||||
max-height: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,140 +0,0 @@
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useDispatch } from 'react-redux';
|
|
||||||
import { createGlobalStyle } from 'styled-components';
|
|
||||||
import {
|
|
||||||
groupBy,
|
|
||||||
isFinalTestPredicate,
|
|
||||||
removeContentLinksWithEmptyTarget,
|
|
||||||
} from '../../utils/commons';
|
|
||||||
import { useFrontendConfig } from '../../hooks/useFrontendConfig';
|
|
||||||
import {
|
|
||||||
deleteCourseProgress,
|
|
||||||
deleteLessonProgress,
|
|
||||||
initCourse,
|
|
||||||
readCourseProgress,
|
|
||||||
readLessonProgresses,
|
|
||||||
} from '../../state/progress/learnProgressDuck';
|
|
||||||
import { isMonolingualCourse } from '../../constants/isLtrContentOnly';
|
|
||||||
import { StyledCourseHeroImage as CoursePageHeroImage } from './CourseHeroImage/CourseHeroImage';
|
|
||||||
import { I18nText } from '../I18n/I18nText';
|
|
||||||
import { StyledCourseList as CourseList } from './CourseList/CourseList';
|
|
||||||
import { ModalIntegration } from '../Modal/ModalIntegration';
|
|
||||||
import { ResetModal } from '../Modal/ResetModal';
|
|
||||||
import { StyledCourseProgress as CourseProgress } from './Progress/CourseProgress';
|
|
||||||
import { learningLevelMapper } from '../../utils/learningLevelUtils';
|
|
||||||
|
|
||||||
export const LTRContentCourseOverviewStyle = createGlobalStyle`
|
|
||||||
#courses {
|
|
||||||
.title,
|
|
||||||
span[aria-label*="lesson"] {
|
|
||||||
/* @noflip */
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
.content .title {
|
|
||||||
/* @noflip */
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const CourseOverview = ({ data, contentId }) => {
|
|
||||||
const isMonoLingualCourse = isMonolingualCourse(contentId);
|
|
||||||
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
dkLearningLevel,
|
|
||||||
contentLinks: lessonContentLinks,
|
|
||||||
mainContentImageLink,
|
|
||||||
learningMetaInfo: { achievableScore, exerciseCount } = {},
|
|
||||||
} = data.content;
|
|
||||||
const filteredContentLinks = removeContentLinksWithEmptyTarget(lessonContentLinks);
|
|
||||||
const finalLessonId = filteredContentLinks
|
|
||||||
.filter(contentLink => isFinalTestPredicate(contentLink))
|
|
||||||
.map(contentLink => contentLink.target.id);
|
|
||||||
const allLessonIds = filteredContentLinks.map(contentLink => contentLink.target.id);
|
|
||||||
const groupedLessons = groupBy('groupName', filteredContentLinks);
|
|
||||||
const { imageBasePath } = useFrontendConfig();
|
|
||||||
const [isResetModalOpen, setResetModalOpen] = useState(false);
|
|
||||||
const [lessonIdToDelete, setLessonIdToDelete] = useState(null);
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(
|
|
||||||
initCourse({
|
|
||||||
allExerciseCount: exerciseCount,
|
|
||||||
maxPointsForAllExercises: achievableScore,
|
|
||||||
cosCourseId: contentId,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
dispatch(readLessonProgresses(allLessonIds));
|
|
||||||
dispatch(readCourseProgress(contentId));
|
|
||||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
||||||
|
|
||||||
const onClickResetCourse = () => {
|
|
||||||
setResetModalOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onClickResetLesson = (e, lessonId) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setLessonIdToDelete(lessonId);
|
|
||||||
setResetModalOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onConfirmResetProgress = () => {
|
|
||||||
if (lessonIdToDelete) {
|
|
||||||
dispatch(deleteLessonProgress(lessonIdToDelete));
|
|
||||||
setLessonIdToDelete(null);
|
|
||||||
} else {
|
|
||||||
dispatch(deleteCourseProgress(contentId));
|
|
||||||
}
|
|
||||||
setResetModalOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancelResetModal = () => {
|
|
||||||
setLessonIdToDelete(null);
|
|
||||||
setResetModalOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="section" data-course-id={contentId} id="courses">
|
|
||||||
{isMonoLingualCourse && <LTRContentCourseOverviewStyle />}
|
|
||||||
<div className="row">
|
|
||||||
<div className="col-xs-12">
|
|
||||||
<div className="respHeaderImgContainer course-headline">
|
|
||||||
<CoursePageHeroImage
|
|
||||||
imageId={mainContentImageLink.targetId}
|
|
||||||
imageBasePath={imageBasePath}
|
|
||||||
>
|
|
||||||
<h1 className="title">
|
|
||||||
<I18nText
|
|
||||||
tabIndex={0}
|
|
||||||
translation={learningLevelMapper[dkLearningLevel]}
|
|
||||||
className="identifier"
|
|
||||||
/>
|
|
||||||
<I18nText tabIndex={0} translation={name} className="description" />
|
|
||||||
</h1>
|
|
||||||
</CoursePageHeroImage>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="row">
|
|
||||||
<CourseProgress
|
|
||||||
courseId={+contentId}
|
|
||||||
finalLessonId={finalLessonId}
|
|
||||||
onClickResetCourse={onClickResetCourse}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="row course-list learn-status">
|
|
||||||
<CourseList {...{ groupedLessons, onClickResetLesson }} />
|
|
||||||
</div>
|
|
||||||
<ModalIntegration isOpen={isResetModalOpen}>
|
|
||||||
<ResetModal
|
|
||||||
fadeIn={true}
|
|
||||||
onConfirmClicked={onConfirmResetProgress}
|
|
||||||
onCancelClicked={onCancelResetModal}
|
|
||||||
/>
|
|
||||||
</ModalIntegration>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,27 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { StyledProgressResetButton as ProgressResetButton } from './ProgressResetButton';
|
|
||||||
import { LearnProgressIcon } from '../../LearnProgressIcon/LearnProgressIcon';
|
|
||||||
import { findCourseLearnProgressSelector } from '../../../state/progress/learnProgressSelectors';
|
|
||||||
|
|
||||||
export const CourseProgress = ({ className, courseId, finalLessonId, onClickResetCourse }) => {
|
|
||||||
const { courseProgress } = useSelector(findCourseLearnProgressSelector(courseId));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
<ProgressResetButton onClickReset={onClickResetCourse} />
|
|
||||||
<LearnProgressIcon learnProgress={courseProgress} finalLessonId={finalLessonId} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StyledCourseProgress = styled(CourseProgress)`
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
padding-right: 25px;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
margin: 14px;
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,25 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { I18nText } from '../../I18n/I18nText';
|
|
||||||
import { colors } from '../../../utils/css';
|
|
||||||
|
|
||||||
export const ProgressResetButton = ({ className, onClickReset }) => {
|
|
||||||
return (
|
|
||||||
<button onClick={e => onClickReset(e)} className={className}>
|
|
||||||
<I18nText translation="course.reset" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StyledProgressResetButton = styled(ProgressResetButton)`
|
|
||||||
background-color: ${colors.LG_GRAY_2};
|
|
||||||
border: 1px solid transparent;
|
|
||||||
color: ${colors.LG_WHITE};
|
|
||||||
font-size: 12px;
|
|
||||||
min-height: 31px;
|
|
||||||
padding: 3px 10px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const StyledSmallProgressResetButton = styled(StyledProgressResetButton)`
|
|
||||||
min-height: 27px;
|
|
||||||
padding: 1px 7px;
|
|
||||||
`;
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { useDispatch } from 'react-redux';
|
|
||||||
import { StyledMainHeroImage } from '../MainHeroImage/MainHeroImage';
|
|
||||||
import { StyledTiles } from './Tiles/Tiles';
|
|
||||||
import { I18nText } from '../I18n/I18nText';
|
|
||||||
import { readCourseProgresses } from '../../state/progress/learnProgressDuck';
|
|
||||||
|
|
||||||
export const Dashboard = ({ data }) => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const courseIds = data.map(course => course.id);
|
|
||||||
dispatch(readCourseProgresses(courseIds));
|
|
||||||
}, [data, dispatch]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main aria-label="dashboard">
|
|
||||||
<StyledMainHeroImage>
|
|
||||||
<I18nText isA="h1" tabIndex={0} translation="dashboard.title" className="title" />
|
|
||||||
<I18nText isA="p" tabIndex={0} translation="dashboard.subTitle" className="description" />
|
|
||||||
</StyledMainHeroImage>
|
|
||||||
<StyledTiles
|
|
||||||
{...{
|
|
||||||
data,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,28 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { colors, resolutions } from '../../../../utils/css';
|
|
||||||
import { useRtlContext } from '../../../../hooks/useRtl';
|
|
||||||
|
|
||||||
const BlankTile = ({ className }) => {
|
|
||||||
const { isRtl } = useRtlContext();
|
|
||||||
return <div className={`${className} ${isRtl ? 'rtl' : ''}`} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StyledBlankTile = styled(BlankTile)`
|
|
||||||
border-top: 1px solid ${colors.LG_WHITE};
|
|
||||||
border-right: 1px solid ${colors.LG_WHITE};
|
|
||||||
background: ${colors.DW_LIGHT_BLUE};
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.tablet}px) {
|
|
||||||
height: 180px;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&.rtl {
|
|
||||||
border-right-width: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.mobile}px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,23 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { StyledTile as Tile } from './Tile';
|
|
||||||
|
|
||||||
export const MoreCoursesTile = ({ className, backgroundColor }) => (
|
|
||||||
<Tile
|
|
||||||
{...{
|
|
||||||
className,
|
|
||||||
backgroundColor,
|
|
||||||
}}
|
|
||||||
data={{
|
|
||||||
dkLearningLevel: 0,
|
|
||||||
name: 'dashboard.dw.title',
|
|
||||||
subTitle: 'dashboard.dw.text',
|
|
||||||
href: 'https://www.dw.com/en/learn-german/s-2469', // TODO: to be added to config file
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const StyledMoreCoursesTile = styled(MoreCoursesTile)`
|
|
||||||
.tile-progress {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,186 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import gql from 'graphql-tag';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { colors, resolutions } from '../../../../utils/css';
|
|
||||||
import { I18nText } from '../../../I18n/I18nText';
|
|
||||||
import { learningLevelMapper } from '../../../../utils/learningLevelUtils';
|
|
||||||
import { getContentUrl } from '../../../../utils/url/urlFactory';
|
|
||||||
import { LearnProgressIcon } from '../../../LearnProgressIcon/LearnProgressIcon';
|
|
||||||
import { findCourseLearnProgressSelector } from '../../../../state/progress/learnProgressSelectors';
|
|
||||||
import { userSelector } from '../../../../state/user/userSelectors';
|
|
||||||
import { progressIconColors } from '../../../ProgressIcon/progressIconColors';
|
|
||||||
|
|
||||||
export const tileFragment = gql`
|
|
||||||
fragment Tile on Course {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
language
|
|
||||||
subTitle
|
|
||||||
dkLearningLevel
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Tile = ({ className, data }) => {
|
|
||||||
const { dkLearningLevel, name, subTitle, id } = data;
|
|
||||||
const { courseProgress } = useSelector(findCourseLearnProgressSelector(id));
|
|
||||||
const { isLoggedIn } = useSelector(userSelector);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LinkWrapper {...{ linkData: data, className }}>
|
|
||||||
{isLoggedIn && (
|
|
||||||
<div className="tile-progress">
|
|
||||||
<LearnProgressIcon learnProgress={courseProgress} color={progressIconColors.WHITE} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<h2 className="tile-learningLevel">{learningLevelMapper[dkLearningLevel]}</h2>
|
|
||||||
<I18nText isA="p" translation={name} className="tile-name" aria-label="tile.placement.name" />
|
|
||||||
<I18nText
|
|
||||||
isA="p"
|
|
||||||
translation={subTitle}
|
|
||||||
className="tile-subTitle"
|
|
||||||
aria-label="tile.placement.subTitle"
|
|
||||||
/>
|
|
||||||
</LinkWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const LinkWrapper = ({ children, linkData, className }) => {
|
|
||||||
return linkData.href ? (
|
|
||||||
<a className={`${className} tile-container`} href={linkData.href}>
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
<Link key={linkData.id} className={`${className} tile-container`} to={getContentUrl(linkData)}>
|
|
||||||
{children}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StyledTile = styled(Tile)`
|
|
||||||
--absElemOffset: 84px;
|
|
||||||
padding: var(--absElemOffset) 20px 20px 20px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 300px;
|
|
||||||
height: 250px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: relative;
|
|
||||||
border-color: ${colors.LG_WHITE};
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 1px 1px 0 0;
|
|
||||||
background-color: ${({ backgroundColor }) => backgroundColor};
|
|
||||||
color: ${colors.LG_WHITE};
|
|
||||||
|
|
||||||
&:nth-child(4n) {
|
|
||||||
border-right-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.tile-container {
|
|
||||||
text-decoration: none;
|
|
||||||
:hover,
|
|
||||||
:active,
|
|
||||||
:focus {
|
|
||||||
color: ${colors.LG_TRANSPARENT_WHITE_50};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-learningLevel {
|
|
||||||
font: 400 70px / var(--absElemOffset) Helvetica, Arial, sans-serif;
|
|
||||||
height: var(--absElemOffset);
|
|
||||||
text-align: start;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-name {
|
|
||||||
font: 400 23px/25px DWTheAntiquaBBold, Georgia, serif;
|
|
||||||
margin: 0 0 7px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-subTitle {
|
|
||||||
font: 400 16px/25px DWTheAntiquaB, Georgia, serif;
|
|
||||||
max-height: 60%;
|
|
||||||
opacity: 0.9;
|
|
||||||
max-width: 95%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-progress {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
padding-top: 20px;
|
|
||||||
width: calc(100% - 40px);
|
|
||||||
height: var(--absElemOffset);
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.tablet}px) {
|
|
||||||
--absElemOffset: 64px;
|
|
||||||
height: 180px;
|
|
||||||
width: 100%;
|
|
||||||
border-width: 1px 1px 0 0;
|
|
||||||
|
|
||||||
&:nth-child(2n) {
|
|
||||||
border-right-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-learningLevel {
|
|
||||||
font: 400 54px/64px Helvetica, Arial, sans-serif;
|
|
||||||
height: var(--absElemOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-name {
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-subTitle {
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-progress {
|
|
||||||
height: var(--absElemOffset);
|
|
||||||
padding-top: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.mobile}px) {
|
|
||||||
height: 90px;
|
|
||||||
padding: 10px;
|
|
||||||
border-width: 1px 0 0 0;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.tile-learningLevel {
|
|
||||||
line-height: 90px;
|
|
||||||
font-size: 36px;
|
|
||||||
width: 58px;
|
|
||||||
height: 90px;
|
|
||||||
margin: 0 0 0 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-name {
|
|
||||||
position: relative;
|
|
||||||
font-size: 17px;
|
|
||||||
line-height: 19px;
|
|
||||||
font-weight: 400;
|
|
||||||
margin: 0 25px 3px 64px;
|
|
||||||
width: calc(100% - 89px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-subTitle {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 19px;
|
|
||||||
flex-direction: row;
|
|
||||||
margin: 0 25px 0 64px;
|
|
||||||
width: calc(100% - 89px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-progress {
|
|
||||||
width: calc(100% - 20px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,56 +0,0 @@
|
||||||
import { Fragment } from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { colors, resolutions } from '../../../utils/css';
|
|
||||||
import { StyledTile as Tile } from './Tile/Tile';
|
|
||||||
import { StyledBlankTile as BlankTile } from './Tile/BlankTile';
|
|
||||||
import { StyledMoreCoursesTile as MoreCoursesTile } from './Tile/MoreCoursesTile';
|
|
||||||
import { tileColors } from '../../../constants/tileColors';
|
|
||||||
import { uniqueId } from '../../../utils/htmlUtils';
|
|
||||||
|
|
||||||
export const Tiles = ({ className, data }) => {
|
|
||||||
return (
|
|
||||||
<nav className={className}>
|
|
||||||
{data.map((tileData, index) => {
|
|
||||||
const tileElement = (
|
|
||||||
<Tile
|
|
||||||
key={tileData.id}
|
|
||||||
backgroundColor={tileColors[index % tileColors.length]}
|
|
||||||
data={tileData}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
// check if the number of the fetched tiles is even and if it is,
|
|
||||||
// then add a blank tile as penultimate
|
|
||||||
return data.length % 2 === 0 && data.indexOf(tileData) === data.length - 1 ? (
|
|
||||||
<Fragment key={uniqueId()}>
|
|
||||||
{tileElement}
|
|
||||||
<BlankTile />
|
|
||||||
</Fragment>
|
|
||||||
) : (
|
|
||||||
tileElement
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<MoreCoursesTile backgroundColor={colors.LG_BLUE_5} />
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StyledTiles = styled(Tiles)`
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(4, 1fr);
|
|
||||||
width: 100%;
|
|
||||||
min-height: 500px;
|
|
||||||
border-bottom: 1px solid ${colors.LG_WHITE};
|
|
||||||
|
|
||||||
@media (min-width: ${resolutions.min.desktop}px) {
|
|
||||||
width: 1200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.tablet}px) {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.mobile}px) {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,36 +0,0 @@
|
||||||
import { useFrontendConfig } from '../../hooks/useFrontendConfig';
|
|
||||||
import { imgWithFormatFn, format } from '../../utils/imgUtils';
|
|
||||||
import { resolutions } from '../../utils/css';
|
|
||||||
import { Picture } from '../Picture/Picture';
|
|
||||||
|
|
||||||
export const DwPicture = ({ imageId, ...inProps }) => {
|
|
||||||
const { imageBasePath } = useFrontendConfig();
|
|
||||||
|
|
||||||
const imgWithFormat = imgWithFormatFn(imageId, imageBasePath);
|
|
||||||
|
|
||||||
const defaultProps = {
|
|
||||||
fallbackUrl: imgWithFormat(format.full),
|
|
||||||
sources: [
|
|
||||||
{
|
|
||||||
media: `(min-width: ${resolutions.min.full}px)`,
|
|
||||||
srcSet: imgWithFormat(format.full),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
media: `(min-width: ${resolutions.min.tabletLandscape}px)`,
|
|
||||||
srcSet: imgWithFormat(format.lg),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
media: `(min-width: ${resolutions.min.tablet}px)`,
|
|
||||||
srcSet: imgWithFormat(format.md),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
media: `(min-width: ${resolutions.min.mobile}px)`,
|
|
||||||
srcSet: imgWithFormat(format.sm),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const props = { ...defaultProps, ...inProps };
|
|
||||||
|
|
||||||
return <Picture {...props} />;
|
|
||||||
};
|
|
|
@ -1,168 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import gql from 'graphql-tag';
|
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import { colors, resolutions } from '../../utils/css';
|
|
||||||
import { I18nText } from '../I18n/I18nText';
|
|
||||||
import {
|
|
||||||
getDwContactUrl,
|
|
||||||
getDwUrlWithLang,
|
|
||||||
getFooterLinkForNamedUrl,
|
|
||||||
getLegalUrl,
|
|
||||||
} from '../../utils/url/urlFactory';
|
|
||||||
import { useRtlContext } from '../../hooks/useRtl';
|
|
||||||
import { legalFragment } from '../LegalNotice/LegalNotice';
|
|
||||||
|
|
||||||
export const footerFragment = {
|
|
||||||
name: 'Footer',
|
|
||||||
fragment() {
|
|
||||||
return gql`fragment ${this.name} on Query {
|
|
||||||
footer(lang: $lang) {
|
|
||||||
accessibility(appName: "mdl") {
|
|
||||||
namedUrl
|
|
||||||
}
|
|
||||||
policy(appName: "mdl") {
|
|
||||||
namedUrl
|
|
||||||
}
|
|
||||||
${legalFragment.partial()}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Footer = ({ className, data }) => {
|
|
||||||
const { isRtl } = useRtlContext();
|
|
||||||
const { langCode } = useParams();
|
|
||||||
|
|
||||||
const rtlClass = isRtl ? 'rtl' : '';
|
|
||||||
return (
|
|
||||||
<footer className={`${className} ${rtlClass}`} aria-label="Main Footer">
|
|
||||||
<ul className={rtlClass}>
|
|
||||||
{footerData({ langCode, data }).map(footerObject => (
|
|
||||||
<li key={footerObject.id} className={rtlClass}>
|
|
||||||
<I18nText
|
|
||||||
isA="a"
|
|
||||||
className="footer-link"
|
|
||||||
translation={footerObject.translation}
|
|
||||||
href={footerObject.href}
|
|
||||||
target={footerObject.target}
|
|
||||||
role={footerObject.role}
|
|
||||||
aria-label={footerObject.aria}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</footer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const footerData = ({ langCode, data }) => [
|
|
||||||
{
|
|
||||||
translation: {
|
|
||||||
key: 'footer.copyright',
|
|
||||||
parameters: { first: new Date().getFullYear() },
|
|
||||||
},
|
|
||||||
id: 0,
|
|
||||||
aria: 'footer.copyright',
|
|
||||||
role: 'Firm name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
translation: 'footer.dw',
|
|
||||||
id: 1,
|
|
||||||
href: getDwUrlWithLang({ langCode }),
|
|
||||||
target: '_blank',
|
|
||||||
aria: 'footer.dw',
|
|
||||||
role: 'link',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
translation: 'footer.privacyPolicy',
|
|
||||||
id: 2,
|
|
||||||
href: getFooterLinkForNamedUrl({ namedUrl: data.footer.policy.namedUrl }),
|
|
||||||
target: '_blank',
|
|
||||||
aria: 'footer.privacyPolicy',
|
|
||||||
role: 'link',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
translation: 'footer.accessibility',
|
|
||||||
id: 3,
|
|
||||||
href: getFooterLinkForNamedUrl({ namedUrl: data.footer.accessibility.namedUrl }),
|
|
||||||
target: '_blank',
|
|
||||||
aria: 'footer.accessibility',
|
|
||||||
role: 'link',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
translation: 'footer.legal',
|
|
||||||
id: 4,
|
|
||||||
href: getLegalUrl(langCode),
|
|
||||||
target: '',
|
|
||||||
aria: 'footer.legal',
|
|
||||||
role: 'link',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
translation: 'footer.contact',
|
|
||||||
id: 5,
|
|
||||||
href: getDwContactUrl(),
|
|
||||||
target: '_blank',
|
|
||||||
aria: 'footer.contact',
|
|
||||||
role: 'link',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const StyledFooter = styled(Footer)`
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 90px;
|
|
||||||
font-family: Helvetica, Arial, sans-serif;
|
|
||||||
line-height: 45px;
|
|
||||||
margin: 20px 0;
|
|
||||||
background: ${colors.LG_BLUE_7};
|
|
||||||
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
flex-direction: row;
|
|
||||||
max-width: 100%;
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 10px 0;
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-right: 12px;
|
|
||||||
padding: 0 0 0 12px;
|
|
||||||
|
|
||||||
&.rtl {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link {
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 15px;
|
|
||||||
color: ${colors.LG_BLUE_4};
|
|
||||||
background-color: transparent;
|
|
||||||
:hover,
|
|
||||||
:active,
|
|
||||||
:focus {
|
|
||||||
color: ${colors.LG_WHITE};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.tablet}px) {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: ${resolutions.max.mobile}px) {
|
|
||||||
max-width: 100%;
|
|
||||||
min-height: 65px;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,5 +0,0 @@
|
||||||
import { isServer } from '../../utils/ssr';
|
|
||||||
|
|
||||||
export const BrowserOnly = ({ children, ...restProps }) => (
|
|
||||||
<div {...restProps}>{!isServer() && children}</div>
|
|
||||||
);
|
|
|
@ -1,44 +0,0 @@
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import config from '../../config';
|
|
||||||
import { userSelector } from '../../state/user/userSelectors';
|
|
||||||
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
|
||||||
import { I18nText } from '../I18n/I18nText';
|
|
||||||
import { BrowserOnly } from './BrowserOnly';
|
|
||||||
|
|
||||||
export const GdprLayer = ({ footerData }) => {
|
|
||||||
const { isLoggedIn } = useSelector(userSelector);
|
|
||||||
const [hasAcceptedGdpr, setHasAcceptedGdpr] = useLocalStorage('hasAcceptedGdpr');
|
|
||||||
const { namedUrl } = footerData.footer.policy || {};
|
|
||||||
|
|
||||||
if (isLoggedIn || hasAcceptedGdpr) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BrowserOnly>
|
|
||||||
<div className="cookie" style={{ left: 0 }}>
|
|
||||||
<div className="cookie__wrap">
|
|
||||||
<div className="cookie__item">
|
|
||||||
<I18nText isA="p" className="cookie__text" translation="common.gdpr.overlay.text" />
|
|
||||||
</div>
|
|
||||||
<div className="cookie__buttons">
|
|
||||||
<I18nText
|
|
||||||
isA="a"
|
|
||||||
translation="common.gdpr.overlay.moreButton"
|
|
||||||
className="cookie__btn cookie__btn--more"
|
|
||||||
href={`${config.dw.baseUrl}${namedUrl}`}
|
|
||||||
target="_blank"
|
|
||||||
/>
|
|
||||||
<I18nText
|
|
||||||
isA="a"
|
|
||||||
translation="common.gdpr.overlay.okButton"
|
|
||||||
className="cookie__btn cookie__btn--ok"
|
|
||||||
href="#"
|
|
||||||
onClick={() => setHasAcceptedGdpr(true)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BrowserOnly>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,40 +0,0 @@
|
||||||
import { Helmet } from 'react-helmet-async';
|
|
||||||
import { GtmNoScriptFallback } from './GtmNoScriptFallback';
|
|
||||||
import { useStaticInfoForGtm } from './hooks/useStaticInfoForGtm';
|
|
||||||
import { gtmDataLayerPartial, TrackingUtils } from './TrackingUtils';
|
|
||||||
import { useCourseData } from '../../hooks/useCourseData';
|
|
||||||
|
|
||||||
export const gtmDataLayerFragment = gtmDataLayerPartial;
|
|
||||||
|
|
||||||
export const GtmDataLayer = ({ content, noContentPageId, push = false }) => {
|
|
||||||
const { gtmLevel1Id: level1Id, macaParam, pageUrl } = useStaticInfoForGtm();
|
|
||||||
const { courseId } = useCourseData();
|
|
||||||
|
|
||||||
const dataLayer = TrackingUtils.generateDataLayer({
|
|
||||||
content,
|
|
||||||
macaParam,
|
|
||||||
level1Id,
|
|
||||||
pageUrl,
|
|
||||||
courseId,
|
|
||||||
noContentPageId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (push) {
|
|
||||||
TrackingUtils.pushToGoogleTagManager({
|
|
||||||
withReset: true,
|
|
||||||
datalayerObj: {
|
|
||||||
event: 'onPageChange',
|
|
||||||
...dataLayer,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Helmet>
|
|
||||||
<script>{TrackingUtils.initDataLayerScript(dataLayer)}</script>
|
|
||||||
</Helmet>
|
|
||||||
<GtmNoScriptFallback dataLayer={dataLayer} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { Helmet } from 'react-helmet-async';
|
|
||||||
import { useFrontendConfig } from '../../hooks/useFrontendConfig';
|
|
||||||
|
|
||||||
export const GtmLoadScript = () => (
|
|
||||||
<Helmet>
|
|
||||||
<script>
|
|
||||||
{`(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
|
||||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
||||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
|
||||||
})(window,document,'script','dataLayer','GTM-${useFrontendConfig().gtmId}');`}
|
|
||||||
</script>
|
|
||||||
</Helmet>
|
|
||||||
);
|
|
|
@ -1,23 +0,0 @@
|
||||||
import { objectToQueryParams } from '../../utils/url/url';
|
|
||||||
import { useFrontendConfig } from '../../hooks/useFrontendConfig';
|
|
||||||
|
|
||||||
export const GtmNoScriptFallback = ({ dataLayer }) => {
|
|
||||||
const hideIframeStyles = {
|
|
||||||
display: 'none',
|
|
||||||
visibility: 'hidden',
|
|
||||||
};
|
|
||||||
const { gtmId } = useFrontendConfig();
|
|
||||||
return (
|
|
||||||
<noscript>
|
|
||||||
<iframe
|
|
||||||
title={`GTM-${gtmId}`}
|
|
||||||
src={`https://www.googletagmanager.com/ns.html?id=GTM-${gtmId}&${objectToQueryParams(
|
|
||||||
dataLayer,
|
|
||||||
)}`}
|
|
||||||
height="0"
|
|
||||||
width="0"
|
|
||||||
style={hideIframeStyles}
|
|
||||||
/>
|
|
||||||
</noscript>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { isServer } from '../../utils/ssr';
|
|
||||||
import globals from '../../utils/globals';
|
|
||||||
|
|
||||||
import { GtmLoadScript } from './GtmLoadScript';
|
|
||||||
import { GtmDataLayer } from './GtmDataLayer';
|
|
||||||
|
|
||||||
export { gtmDataLayerFragment } from './GtmDataLayer';
|
|
||||||
|
|
||||||
export const GtmScriptWithDataLayer = ({ content = {}, noContentPageId }) => {
|
|
||||||
const isGtmAlreadyInitialized = !isServer() && !!globals.window.dataLayer;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<GtmDataLayer
|
|
||||||
content={content}
|
|
||||||
noContentPageId={noContentPageId}
|
|
||||||
push={isGtmAlreadyInitialized}
|
|
||||||
/>
|
|
||||||
{!isGtmAlreadyInitialized && <GtmLoadScript />}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,139 +0,0 @@
|
||||||
import gql from 'graphql-tag';
|
|
||||||
import globals from '../../utils/globals';
|
|
||||||
|
|
||||||
const isValidList = list => list?.length > 0;
|
|
||||||
|
|
||||||
const addCurrentNavToRootList = ({ nav }) => [...nav.navsToRoot, nav];
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
|
||||||
const contentTitle = content => (content.__typename === 'Person' ? content.fullName : content.name);
|
|
||||||
|
|
||||||
const pickCategoryName = ({ navs, index }) => (navs[index] || {}).name;
|
|
||||||
|
|
||||||
export const gtmDataLayerPartial = {
|
|
||||||
name: 'GtmDataLayer',
|
|
||||||
partial() {
|
|
||||||
return `
|
|
||||||
...on ModelAspect {
|
|
||||||
id
|
|
||||||
gtmLanguageCode
|
|
||||||
gtmContentType
|
|
||||||
}
|
|
||||||
...on DeliveryAspect {
|
|
||||||
gtmContentDate
|
|
||||||
}
|
|
||||||
...on NamedAspect {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
...on AssociationsAspect {
|
|
||||||
navigations {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
navsToRoot {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subjects {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
topics:categories {
|
|
||||||
originId
|
|
||||||
}
|
|
||||||
departments {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mediaInfoForTrackingFragment = {
|
|
||||||
name: 'MediaInfoForTrackingFragment',
|
|
||||||
fragment({ type }) {
|
|
||||||
return gql`fragment ${this.name} on ${type} {
|
|
||||||
videos {
|
|
||||||
${gtmDataLayerPartial.partial()}
|
|
||||||
}
|
|
||||||
audios {
|
|
||||||
${gtmDataLayerPartial.partial()}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generateContentGtmInfo = ({
|
|
||||||
content,
|
|
||||||
level1Id,
|
|
||||||
macaParam,
|
|
||||||
pageUrl,
|
|
||||||
courseId,
|
|
||||||
noContentPageId,
|
|
||||||
}) => {
|
|
||||||
const navs = isValidList(content.navigations) ? content.navigations : [{ navsToRoot: [] }];
|
|
||||||
const alignedNavs = addCurrentNavToRootList({ nav: navs[0] });
|
|
||||||
|
|
||||||
return {
|
|
||||||
categoryLevel1: pickCategoryName({ navs: alignedNavs, index: 0 }),
|
|
||||||
categoryLevel2: pickCategoryName({ navs: alignedNavs, index: 1 }),
|
|
||||||
categoryLevel3: pickCategoryName({ navs: alignedNavs, index: 2 }),
|
|
||||||
contentTitle: contentTitle(content),
|
|
||||||
contentLanguage: content.gtmLanguageCode,
|
|
||||||
pageOID: content.id,
|
|
||||||
contentType: content.gtmContentType,
|
|
||||||
date: content.gtmContentDate,
|
|
||||||
subject: content.subjects ? content.subjects[0]?.name : {},
|
|
||||||
topicIds: content.topics?.map(topic => topic.originId),
|
|
||||||
pageSID: navs[0].id,
|
|
||||||
departmentName: content.departments ? content.departments[0]?.name : {},
|
|
||||||
categoryType: 1,
|
|
||||||
displayForm: 5,
|
|
||||||
level1ID: level1Id,
|
|
||||||
macaParam,
|
|
||||||
pageUrl,
|
|
||||||
courseId,
|
|
||||||
noContentPageId,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TrackingUtils = {
|
|
||||||
initDataLayerScript: datalayer => {
|
|
||||||
const initialDataLayer = {
|
|
||||||
...datalayer,
|
|
||||||
event: 'onPageChange',
|
|
||||||
};
|
|
||||||
|
|
||||||
return `
|
|
||||||
if(!window.dataLayer) {
|
|
||||||
window.dataLayer = [${JSON.stringify(initialDataLayer)}]
|
|
||||||
};`.trim();
|
|
||||||
},
|
|
||||||
generateDataLayer: ({ content, level1Id, macaParam, pageUrl, courseId, noContentPageId }) => ({
|
|
||||||
pageData: {
|
|
||||||
...generateContentGtmInfo({
|
|
||||||
content,
|
|
||||||
level1Id,
|
|
||||||
macaParam,
|
|
||||||
pageUrl,
|
|
||||||
courseId,
|
|
||||||
noContentPageId,
|
|
||||||
}),
|
|
||||||
embeddings: {},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
pushToGoogleTagManager: ({ datalayerObj, withReset = false }) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (withReset) {
|
|
||||||
TrackingUtils.resetDataLayer();
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line fp/no-mutating-methods
|
|
||||||
globals.window.dataLayer?.push(datalayerObj);
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
resetDataLayer: () => {
|
|
||||||
// eslint-disable-next-line fp/no-mutating-methods
|
|
||||||
globals.window.dataLayer.push(function dontUseArrowFunction() {
|
|
||||||
this.reset();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { TrackingUtils } from '../TrackingUtils';
|
|
||||||
|
|
||||||
const eventName = 'addEmbeddings';
|
|
||||||
|
|
||||||
export const useAddEmbeddingsToGtm = ({ dictionary }) => {
|
|
||||||
useEffect(() => {
|
|
||||||
TrackingUtils.pushToGoogleTagManager({
|
|
||||||
datalayerObj: {
|
|
||||||
event: eventName,
|
|
||||||
pageData: {
|
|
||||||
embeddings: dictionary,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
};
|
|
|
@ -1,17 +0,0 @@
|
||||||
import { useStaticInfoForGtm } from './useStaticInfoForGtm';
|
|
||||||
import { generateContentGtmInfo } from '../TrackingUtils';
|
|
||||||
import { useAddEmbeddingsToGtm } from './useAddEmbeddingsToGtm';
|
|
||||||
|
|
||||||
export const useMediaTracking = content => {
|
|
||||||
const { level1Id, macaParam, pageUrl } = useStaticInfoForGtm();
|
|
||||||
const contentDictionary = {
|
|
||||||
[content.id]: generateContentGtmInfo({
|
|
||||||
content,
|
|
||||||
level1Id,
|
|
||||||
macaParam,
|
|
||||||
pageUrl,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
useAddEmbeddingsToGtm({ dictionary: contentDictionary });
|
|
||||||
};
|
|
|
@ -1,17 +0,0 @@
|
||||||
import { useStaticInfoForGtm } from './useStaticInfoForGtm';
|
|
||||||
import { generateContentGtmInfo } from '../TrackingUtils';
|
|
||||||
import { useAddEmbeddingsToGtm } from './useAddEmbeddingsToGtm';
|
|
||||||
|
|
||||||
export const useMediaTracking = content => {
|
|
||||||
const { level1Id, macaParam, pageUrl } = useStaticInfoForGtm();
|
|
||||||
const contentDictionary = {
|
|
||||||
[content.id]: generateContentGtmInfo({
|
|
||||||
content,
|
|
||||||
level1Id,
|
|
||||||
macaParam,
|
|
||||||
pageUrl,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
useAddEmbeddingsToGtm({ dictionary: contentDictionary });
|
|
||||||
};
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { useGlobalsContext } from '../../../context/GlobalsContext';
|
|
||||||
import { useLocationQueryParam } from '../../../hooks/useLocationQueryParam';
|
|
||||||
import { useFrontendConfig } from '../../../hooks/useFrontendConfig';
|
|
||||||
|
|
||||||
export const useStaticInfoForGtm = () => {
|
|
||||||
const pageUrl = useGlobalsContext().window.location.href.split('?')[0];
|
|
||||||
const macaParam = useLocationQueryParam('maca');
|
|
||||||
const { gtmLevel1Id } = useFrontendConfig();
|
|
||||||
|
|
||||||
return { gtmLevel1Id, macaParam, pageUrl };
|
|
||||||
};
|
|
|
@ -1,6 +0,0 @@
|
||||||
import { memo } from 'react';
|
|
||||||
import { GtmScriptWithDataLayer as NotMemoGtmScriptWithDataLayer } from './GtmScriptWithDataLayer';
|
|
||||||
|
|
||||||
export { gtmDataLayerFragment } from './GtmScriptWithDataLayer';
|
|
||||||
|
|
||||||
export const GtmScriptWithDataLayer = memo(NotMemoGtmScriptWithDataLayer);
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue