create symlink for node_modules in generateJson and buildRadar scripts
This commit is contained in:
191
src/animation.ts
191
src/animation.ts
@@ -1,110 +1,131 @@
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
export type Animations = {
|
||||
[k: string]: Animation[]
|
||||
}
|
||||
[k: string]: Animation[];
|
||||
};
|
||||
|
||||
export type AnimationStates = {
|
||||
[k: string]: React.CSSProperties[]
|
||||
}
|
||||
[k: string]: React.CSSProperties[];
|
||||
};
|
||||
|
||||
type Animation = {
|
||||
stateA: React.CSSProperties
|
||||
stateB: React.CSSProperties
|
||||
delay: number
|
||||
run?(callback: (state: any) => any): any // todo fix
|
||||
prepare?(callback: (state: any) => any): any // todo fix
|
||||
}
|
||||
stateA: React.CSSProperties;
|
||||
stateB: React.CSSProperties;
|
||||
delay: number;
|
||||
run?(callback: (state: any) => any): any; // todo fix
|
||||
prepare?(callback: (state: any) => any): any; // todo fix
|
||||
};
|
||||
|
||||
export type AnimationRunner = {
|
||||
getState(): AnimationStates
|
||||
run(): any
|
||||
awaitAnimationComplete(callback: () => void): any
|
||||
}
|
||||
getState(): AnimationStates;
|
||||
run(): any;
|
||||
awaitAnimationComplete(callback: () => void): any;
|
||||
};
|
||||
|
||||
export const createAnimation = (stateA: React.CSSProperties, stateB: React.CSSProperties, delay: number): Animation => ({
|
||||
stateA,
|
||||
stateB,
|
||||
delay,
|
||||
export const createAnimation = (
|
||||
stateA: React.CSSProperties,
|
||||
stateB: React.CSSProperties,
|
||||
delay: number
|
||||
): Animation => ({
|
||||
stateA,
|
||||
stateB,
|
||||
delay,
|
||||
});
|
||||
|
||||
const getAnimationStates = (animations: Animation[], stateName: 'stateA' | 'stateB' = 'stateA'): React.CSSProperties[] => {
|
||||
return animations.map(animation => animation[stateName]);
|
||||
const getAnimationStates = (
|
||||
animations: Animation[],
|
||||
stateName: "stateA" | "stateB" = "stateA"
|
||||
): React.CSSProperties[] => {
|
||||
return animations.map((animation) => animation[stateName]);
|
||||
};
|
||||
|
||||
const getMaxTransitionTime = (transition: string) => {
|
||||
const re = /(\d+)ms/g;
|
||||
const times: number[] = [];
|
||||
let matches;
|
||||
while ((matches = re.exec(transition)) != null) {
|
||||
times.push(parseInt(matches[1], 10));
|
||||
}
|
||||
return Math.max(...times);
|
||||
const re = /(\d+)ms/g;
|
||||
const times: number[] = [];
|
||||
let matches;
|
||||
while ((matches = re.exec(transition)) != null) {
|
||||
times.push(parseInt(matches[1], 10));
|
||||
}
|
||||
return Math.max(...times);
|
||||
};
|
||||
|
||||
const getAnimationDuration = (animation: Animation | Animation[]): number => {
|
||||
if (animation instanceof Array) {
|
||||
return animation.reduce((maxDuration, a) => {
|
||||
const duration = getAnimationDuration(a);
|
||||
if (duration > maxDuration) {
|
||||
return duration;
|
||||
}
|
||||
return maxDuration;
|
||||
}, 0);
|
||||
}
|
||||
if (animation instanceof Array) {
|
||||
return animation.reduce((maxDuration, a) => {
|
||||
const duration = getAnimationDuration(a);
|
||||
if (duration > maxDuration) {
|
||||
return duration;
|
||||
}
|
||||
return maxDuration;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
const state = animation.stateB;
|
||||
const maxTransition = state.transition ? getMaxTransitionTime(state.transition) : 0;
|
||||
return maxTransition + animation.delay;
|
||||
const state = animation.stateB;
|
||||
const maxTransition = state.transition
|
||||
? getMaxTransitionTime(state.transition)
|
||||
: 0;
|
||||
return maxTransition + animation.delay;
|
||||
};
|
||||
|
||||
const getMaxAnimationsDuration = (animations: Animations) => (
|
||||
Math.max(...Object.values(animations).map(animations => getAnimationDuration(Object.values(animations))))
|
||||
);
|
||||
const getMaxAnimationsDuration = (animations: Animations) =>
|
||||
Math.max(
|
||||
...Object.values(animations).map((animations) =>
|
||||
getAnimationDuration(Object.values(animations))
|
||||
)
|
||||
);
|
||||
|
||||
export const createAnimationRunner = (animationsIn: { [k: string]: (Animation | Animation[]) }, subscriber: () => void = () => {
|
||||
}): AnimationRunner => {
|
||||
const animations = Object.entries(animationsIn).reduce((state, [name, animation]) => ({
|
||||
...state,
|
||||
[name]: animation instanceof Array ? animation : [animation] as Animation[],
|
||||
}), {} as Animations);
|
||||
export const createAnimationRunner = (
|
||||
animationsIn: { [k: string]: Animation | Animation[] },
|
||||
subscriber: () => void = () => {}
|
||||
): AnimationRunner => {
|
||||
const animations = Object.entries(animationsIn).reduce(
|
||||
(state, [name, animation]) => ({
|
||||
...state,
|
||||
[name]:
|
||||
animation instanceof Array ? animation : ([animation] as Animation[]),
|
||||
}),
|
||||
{} as Animations
|
||||
);
|
||||
|
||||
let state = Object.entries(animations).reduce((state, [name, animation]) => ({
|
||||
...state,
|
||||
[name]: getAnimationStates(animation),
|
||||
}), {} as AnimationStates);
|
||||
let state = Object.entries(animations).reduce(
|
||||
(state, [name, animation]) => ({
|
||||
...state,
|
||||
[name]: getAnimationStates(animation),
|
||||
}),
|
||||
{} as AnimationStates
|
||||
);
|
||||
|
||||
const animationsDuration = getMaxAnimationsDuration(animations);
|
||||
const animationsDuration = getMaxAnimationsDuration(animations);
|
||||
|
||||
const animate = (name: string, animation: Animation[]) => {
|
||||
animation.forEach((a, index) => {
|
||||
window.requestAnimationFrame(() => {
|
||||
window.setTimeout(() => {
|
||||
state = {
|
||||
...state,
|
||||
[name]: [
|
||||
...(state[name]?.slice(0, index)),
|
||||
a.stateB,
|
||||
...(state[name]?.slice(index + 1, state[name].length)),
|
||||
],
|
||||
};
|
||||
subscriber();
|
||||
}, a.delay);
|
||||
});
|
||||
});
|
||||
}
|
||||
const animate = (name: string, animation: Animation[]) => {
|
||||
animation.forEach((a, index) => {
|
||||
window.requestAnimationFrame(() => {
|
||||
window.setTimeout(() => {
|
||||
state = {
|
||||
...state,
|
||||
[name]: [
|
||||
...state[name]?.slice(0, index),
|
||||
a.stateB,
|
||||
...state[name]?.slice(index + 1, state[name].length),
|
||||
],
|
||||
};
|
||||
subscriber();
|
||||
}, a.delay);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
getState() {
|
||||
return state;
|
||||
},
|
||||
run() {
|
||||
Object.entries(animations).forEach(([name, animation]) => {
|
||||
animate(name, animation)
|
||||
});
|
||||
},
|
||||
awaitAnimationComplete(callback) {
|
||||
window.setTimeout(callback, animationsDuration);
|
||||
},
|
||||
}
|
||||
}
|
||||
return {
|
||||
getState() {
|
||||
return state;
|
||||
},
|
||||
run() {
|
||||
Object.entries(animations).forEach(([name, animation]) => {
|
||||
animate(name, animation);
|
||||
});
|
||||
},
|
||||
awaitAnimationComplete(callback) {
|
||||
window.setTimeout(callback, animationsDuration);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
import React, { MouseEventHandler } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import './badge.scss';
|
||||
import {Ring} from "../../config";
|
||||
import React, { MouseEventHandler } from "react";
|
||||
import classNames from "classnames";
|
||||
import "./badge.scss";
|
||||
import { Ring } from "../../config";
|
||||
type BadgeProps = {
|
||||
onClick?: MouseEventHandler;
|
||||
big?: boolean;
|
||||
type: 'big' | 'all' | 'empty' | Ring;
|
||||
type: "big" | "all" | "empty" | Ring;
|
||||
};
|
||||
|
||||
export default function Badge({ onClick, big, type, children }: React.PropsWithChildren<BadgeProps>) {
|
||||
const Comp = onClick ? 'a' : 'span';
|
||||
export default function Badge({
|
||||
onClick,
|
||||
big,
|
||||
type,
|
||||
children,
|
||||
}: React.PropsWithChildren<BadgeProps>) {
|
||||
const Comp = onClick ? "a" : "span";
|
||||
|
||||
return (
|
||||
<Comp
|
||||
className={classNames('badge', `badge--${type}`, {
|
||||
'badge--big': big === true,
|
||||
className={classNames("badge", `badge--${type}`, {
|
||||
"badge--big": big === true,
|
||||
})}
|
||||
onClick={onClick}
|
||||
href={Comp === 'a' ? '#' : undefined}
|
||||
href={Comp === "a" ? "#" : undefined}
|
||||
>
|
||||
{children}
|
||||
</Comp>
|
||||
|
||||
@@ -40,5 +40,4 @@
|
||||
background: var(--color-marine);
|
||||
border-color: var(--color-marine);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import './branding.scss';
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import "./branding.scss";
|
||||
type BrandingProps = {
|
||||
logoContent: React.ReactNode;
|
||||
modifier?: 'backlink' | 'logo' | 'content' | 'footer';
|
||||
modifier?: "backlink" | "logo" | "content" | "footer";
|
||||
};
|
||||
|
||||
export default function Branding({ logoContent, modifier, children }: React.PropsWithChildren<BrandingProps>) {
|
||||
export default function Branding({
|
||||
logoContent,
|
||||
modifier,
|
||||
children,
|
||||
}: React.PropsWithChildren<BrandingProps>) {
|
||||
return (
|
||||
<div className={classNames('branding', { [`branding--${modifier}`]: modifier })}>
|
||||
<div className='branding__logo'>{logoContent}</div>
|
||||
<div className='branding__content'>{children}</div>
|
||||
<div
|
||||
className={classNames("branding", {
|
||||
[`branding--${modifier}`]: modifier,
|
||||
})}
|
||||
>
|
||||
<div className="branding__logo">{logoContent}</div>
|
||||
<div className="branding__content">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.branding {
|
||||
margin: 40px 0;
|
||||
@@ -43,4 +43,4 @@
|
||||
@media (max-width: $until-xl) {
|
||||
margin: 15px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import './fadeable.scss';
|
||||
import React, { useState, useEffect } from "react";
|
||||
import classNames from "classnames";
|
||||
import "./fadeable.scss";
|
||||
|
||||
type FadeableProps = {
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
};
|
||||
|
||||
export default function Fadeable({ leaving, onLeave, children }: React.PropsWithChildren<FadeableProps>) {
|
||||
export default function Fadeable({
|
||||
leaving,
|
||||
onLeave,
|
||||
children,
|
||||
}: React.PropsWithChildren<FadeableProps>) {
|
||||
const [faded, setFaded] = useState(leaving);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -25,7 +29,10 @@ export default function Fadeable({ leaving, onLeave, children }: React.PropsWith
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classNames('fadable', { 'is-faded': faded })} onTransitionEnd={handleTransitionEnd}>
|
||||
<div
|
||||
className={classNames("fadable", { "is-faded": faded })}
|
||||
onTransitionEnd={handleTransitionEnd}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
transition: opacity 0.2s cubic-bezier(0.54, 0, 0.28, 1);
|
||||
|
||||
&.is-faded {
|
||||
opacity: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,37 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import Branding from '../Branding/Branding';
|
||||
import FooterEnd from '../FooterEnd/FooterEnd';
|
||||
import { assetUrl, getItemPageNames, isMobileViewport } from '../../config';
|
||||
import { Item } from '../../model';
|
||||
import './footer.scss';
|
||||
export default function Footer({ items, pageName }: { items: Item[]; pageName: string }) {
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import Branding from "../Branding/Branding";
|
||||
import FooterEnd from "../FooterEnd/FooterEnd";
|
||||
import { assetUrl, getItemPageNames, isMobileViewport } from "../../config";
|
||||
import { Item } from "../../model";
|
||||
import "./footer.scss";
|
||||
export default function Footer({
|
||||
items,
|
||||
pageName,
|
||||
}: {
|
||||
items: Item[];
|
||||
pageName: string;
|
||||
}) {
|
||||
return (
|
||||
<div className={classNames('footer', { 'is-hidden': !isMobileViewport() && getItemPageNames(items).includes(pageName) })}>
|
||||
<Branding modifier='footer' logoContent={<img src={assetUrl('logo.svg')} width='150px' height='60px' alt='' />}>
|
||||
<span className='footnote'>
|
||||
AOE is a leading global provider of services for digital transformation and digital business models. AOE relies exclusively on established Enterprise
|
||||
Open Source technologies. This leads to innovative solutions, digital products and portals in agile software projects, and helps build long-lasting,
|
||||
strategic partnerships with our customers.
|
||||
<div
|
||||
className={classNames("footer", {
|
||||
"is-hidden":
|
||||
!isMobileViewport() && getItemPageNames(items).includes(pageName),
|
||||
})}
|
||||
>
|
||||
<Branding
|
||||
modifier="footer"
|
||||
logoContent={
|
||||
<img src={assetUrl("logo.svg")} width="150px" height="60px" alt="" />
|
||||
}
|
||||
>
|
||||
<span className="footnote">
|
||||
AOE is a leading global provider of services for digital
|
||||
transformation and digital business models. AOE relies exclusively on
|
||||
established Enterprise Open Source technologies. This leads to
|
||||
innovative solutions, digital products and portals in agile software
|
||||
projects, and helps build long-lasting, strategic partnerships with
|
||||
our customers.
|
||||
</span>
|
||||
</Branding>
|
||||
<FooterEnd />
|
||||
|
||||
@@ -1,37 +1,75 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import './footerend.scss';
|
||||
export default function FooterEnd({ modifier }: { modifier?: 'in-sidebar' }) {
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import "./footerend.scss";
|
||||
export default function FooterEnd({ modifier }: { modifier?: "in-sidebar" }) {
|
||||
return (
|
||||
<div className={classNames('footer-end', { [`footer-end__${modifier}`]: modifier })}>
|
||||
<div className='footer-social'>
|
||||
<div className='footer-social__label'>
|
||||
<div
|
||||
className={classNames("footer-end", {
|
||||
[`footer-end__${modifier}`]: modifier,
|
||||
})}
|
||||
>
|
||||
<div className="footer-social">
|
||||
<div className="footer-social__label">
|
||||
<p>Follow us:</p>
|
||||
</div>
|
||||
<div className='footer-social__links'>
|
||||
<a className='social-links-icon' href='https://www.facebook.com/aoepeople' target='_blank' rel='noopener noreferrer'>
|
||||
<i className='socicon-facebook social-icon'></i>
|
||||
<div className="footer-social__links">
|
||||
<a
|
||||
className="social-links-icon"
|
||||
href="https://www.facebook.com/aoepeople"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i className="socicon-facebook social-icon"></i>
|
||||
</a>
|
||||
<a className='social-links-icon' href='https://twitter.com/aoepeople' target='_blank' rel='noopener noreferrer'>
|
||||
<i className='socicon-twitter social-icon'></i>
|
||||
<a
|
||||
className="social-links-icon"
|
||||
href="https://twitter.com/aoepeople"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i className="socicon-twitter social-icon"></i>
|
||||
</a>
|
||||
<a className='social-links-icon' href='https://www.linkedin.com/company/aoe' target='_blank' rel='noopener noreferrer'>
|
||||
<i className='socicon-linkedin social-icon'></i>
|
||||
<a
|
||||
className="social-links-icon"
|
||||
href="https://www.linkedin.com/company/aoe"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i className="socicon-linkedin social-icon"></i>
|
||||
</a>
|
||||
<a className='social-links-icon' href='https://www.xing.com/company/aoe' target='_blank' rel='noopener noreferrer'>
|
||||
<i className='socicon-xing social-icon'></i>
|
||||
<a
|
||||
className="social-links-icon"
|
||||
href="https://www.xing.com/company/aoe"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i className="socicon-xing social-icon"></i>
|
||||
</a>
|
||||
<a className='social-links-icon' href='https://www.youtube.com/user/aoepeople' target='_blank' rel='noopener noreferrer'>
|
||||
<i className='socicon-youtube social-icon'></i>
|
||||
<a
|
||||
className="social-links-icon"
|
||||
href="https://www.youtube.com/user/aoepeople"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i className="socicon-youtube social-icon"></i>
|
||||
</a>
|
||||
<a className='social-links-icon' href='https://github.com/aoepeople' target='_blank' rel='noopener noreferrer'>
|
||||
<i className='socicon-github social-icon'></i>
|
||||
<a
|
||||
className="social-links-icon"
|
||||
href="https://github.com/aoepeople"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i className="socicon-github social-icon"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className='footer-copyright'>
|
||||
<div className="footer-copyright">
|
||||
<p>
|
||||
<a href='https://www.aoe.com/en/copyright-meta/legal-information.html' target='_blank' rel='noopener noreferrer'>
|
||||
<a
|
||||
href="https://www.aoe.com/en/copyright-meta/legal-information.html"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Legal Information
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.footer-end {
|
||||
font-size: 12px;
|
||||
@@ -35,4 +35,4 @@
|
||||
&__label {
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import './headline-group.scss';
|
||||
export default function ({ children, secondary = false }: React.PropsWithChildren<{ secondary?: boolean }>) {
|
||||
return <div className={classNames('headline-group', { 'headline-group--secondary': secondary })}>{children}</div>;
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import "./headline-group.scss";
|
||||
export default function ({
|
||||
children,
|
||||
secondary = false,
|
||||
}: React.PropsWithChildren<{ secondary?: boolean }>) {
|
||||
return (
|
||||
<div
|
||||
className={classNames("headline-group", {
|
||||
"headline-group--secondary": secondary,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.headline-group {
|
||||
margin: 0 0 60px;
|
||||
@@ -14,4 +14,4 @@
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import React from 'react';
|
||||
import './hero-headline.scss';
|
||||
export default function ({ children, alt }: React.PropsWithChildren<{ alt?: string }>) {
|
||||
import React from "react";
|
||||
import "./hero-headline.scss";
|
||||
export default function ({
|
||||
children,
|
||||
alt,
|
||||
}: React.PropsWithChildren<{ alt?: string }>) {
|
||||
return (
|
||||
<div className='hero-headline'>
|
||||
<div className="hero-headline">
|
||||
{children}
|
||||
<span className='hero-headline__alt'>{alt}</span>
|
||||
<span className="hero-headline__alt">{alt}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.hero-headline {
|
||||
font-size: 38px;
|
||||
@@ -23,4 +23,4 @@
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import Link from '../Link/Link';
|
||||
import Flag from '../Flag/Flag';
|
||||
import { Item as mItem } from '../../model';
|
||||
import './item.scss';
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import Link from "../Link/Link";
|
||||
import Flag from "../Flag/Flag";
|
||||
import { Item as mItem } from "../../model";
|
||||
import "./item.scss";
|
||||
type ItemProps = {
|
||||
item: mItem;
|
||||
noLeadingBorder?: boolean;
|
||||
@@ -11,21 +11,26 @@ type ItemProps = {
|
||||
style: React.CSSProperties;
|
||||
};
|
||||
|
||||
export default function Item({ item, noLeadingBorder = false, active = false, style = {} }: ItemProps) {
|
||||
export default function Item({
|
||||
item,
|
||||
noLeadingBorder = false,
|
||||
active = false,
|
||||
style = {},
|
||||
}: ItemProps) {
|
||||
return (
|
||||
<Link
|
||||
className={classNames('item', {
|
||||
'item--no-leading-border': noLeadingBorder,
|
||||
'is-active': active,
|
||||
className={classNames("item", {
|
||||
"item--no-leading-border": noLeadingBorder,
|
||||
"is-active": active,
|
||||
})}
|
||||
pageName={`${item.quadrant}/${item.name}`}
|
||||
style={style}
|
||||
>
|
||||
<div className='item__title'>
|
||||
<div className="item__title">
|
||||
{item.title}
|
||||
<Flag item={item} />
|
||||
</div>
|
||||
{item.info && <div className='item__info'>{item.info}</div>}
|
||||
{item.info && <div className="item__info">{item.info}</div>}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import Item from '../Item/Item';
|
||||
import { Item as mItem } from '../../model';
|
||||
import './item-list.scss';
|
||||
import React from "react";
|
||||
import Item from "../Item/Item";
|
||||
import { Item as mItem } from "../../model";
|
||||
import "./item-list.scss";
|
||||
type ItemListProps = {
|
||||
items: mItem[];
|
||||
activeItem?: mItem;
|
||||
@@ -10,19 +10,30 @@ type ItemListProps = {
|
||||
itemStyle?: React.CSSProperties[];
|
||||
};
|
||||
|
||||
export default function ItemList({ children, items, activeItem, noLeadingBorder, headerStyle = {}, itemStyle = [] }: React.PropsWithChildren<ItemListProps>) {
|
||||
export default function ItemList({
|
||||
children,
|
||||
items,
|
||||
activeItem,
|
||||
noLeadingBorder,
|
||||
headerStyle = {},
|
||||
itemStyle = [],
|
||||
}: React.PropsWithChildren<ItemListProps>) {
|
||||
return (
|
||||
<div className='item-list'>
|
||||
<div className='item-list__header' style={headerStyle}>
|
||||
<div className="item-list">
|
||||
<div className="item-list__header" style={headerStyle}>
|
||||
{children}
|
||||
</div>
|
||||
<div className='item-list__list'>
|
||||
<div className="item-list__list">
|
||||
{items.map((item, i) => (
|
||||
<Item
|
||||
key={item.name}
|
||||
item={item}
|
||||
noLeadingBorder={noLeadingBorder}
|
||||
active={activeItem !== null && activeItem !== undefined && activeItem.name === item.name}
|
||||
active={
|
||||
activeItem !== null &&
|
||||
activeItem !== undefined &&
|
||||
activeItem.name === item.name
|
||||
}
|
||||
style={itemStyle[i]}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import React from 'react';
|
||||
import Badge from '../Badge/Badge';
|
||||
import { formatRelease } from '../../date';
|
||||
import { Revision } from '../../model';
|
||||
import React from "react";
|
||||
import Badge from "../Badge/Badge";
|
||||
import { formatRelease } from "../../date";
|
||||
import { Revision } from "../../model";
|
||||
|
||||
export default function ItemRevision({ revision }: { revision: Revision }) {
|
||||
return (
|
||||
<div className='item-revision'>
|
||||
<div className="item-revision">
|
||||
<div>
|
||||
<Badge type={revision.ring}>
|
||||
{revision.ring} | {formatRelease(revision.release)}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className='markdown' dangerouslySetInnerHTML={{ __html: revision.body }} />
|
||||
<div
|
||||
className="markdown"
|
||||
dangerouslySetInnerHTML={{ __html: revision.body }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.item-revision {
|
||||
&+.item-revision {
|
||||
& + .item-revision {
|
||||
margin-top: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import React from 'react';
|
||||
import HeadlineGroup from '../HeadlineGroup/HeadlineGroup';
|
||||
import ItemRevision from '../ItemRevision/ItemRevision';
|
||||
import { Revision } from '../../model';
|
||||
import './item-revisions.scss';
|
||||
export default function ItemRevisions({ revisions }: { revisions: Revision[] }) {
|
||||
import React from "react";
|
||||
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
||||
import ItemRevision from "../ItemRevision/ItemRevision";
|
||||
import { Revision } from "../../model";
|
||||
import "./item-revisions.scss";
|
||||
export default function ItemRevisions({
|
||||
revisions,
|
||||
}: {
|
||||
revisions: Revision[];
|
||||
}) {
|
||||
return (
|
||||
<div className='item-revisions'>
|
||||
<div className="item-revisions">
|
||||
<HeadlineGroup secondary>
|
||||
<h4 className='headline headline--dark'>Revisions:</h4>
|
||||
<h4 className="headline headline--dark">Revisions:</h4>
|
||||
</HeadlineGroup>
|
||||
|
||||
{revisions.map((revision) => (
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import Link from '../Link/Link';
|
||||
import { assetUrl, radarNameShort } from '../../config';
|
||||
import './logo-link.scss';
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import Link from "../Link/Link";
|
||||
import { assetUrl, radarNameShort } from "../../config";
|
||||
import "./logo-link.scss";
|
||||
export default function LogoLink({ small = false }: { small?: boolean }) {
|
||||
return (
|
||||
<Link pageName='index' className={classNames('logo-link', { 'logo-link--small': small })}>
|
||||
<span className='logo-link__icon icon icon--back'/>
|
||||
<span className='logo-link__slide'>
|
||||
<img className='logo-link__img' src={assetUrl('logo.svg')} width='150px' height='60px' alt={radarNameShort} />
|
||||
<span className='logo-link__text'>{radarNameShort}</span>
|
||||
<Link
|
||||
pageName="index"
|
||||
className={classNames("logo-link", { "logo-link--small": small })}
|
||||
>
|
||||
<span className="logo-link__icon icon icon--back" />
|
||||
<span className="logo-link__slide">
|
||||
<img
|
||||
className="logo-link__img"
|
||||
src={assetUrl("logo.svg")}
|
||||
width="150px"
|
||||
height="60px"
|
||||
alt={radarNameShort}
|
||||
/>
|
||||
<span className="logo-link__text">{radarNameShort}</span>
|
||||
</span>
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -1,91 +1,139 @@
|
||||
import React from 'react';
|
||||
import HeroHeadline from '../HeroHeadline/HeroHeadline';
|
||||
import Fadeable from '../Fadeable/Fadeable';
|
||||
import SetTitle from '../SetTitle';
|
||||
import { radarName } from '../../config';
|
||||
import React from "react";
|
||||
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||
import Fadeable from "../Fadeable/Fadeable";
|
||||
import SetTitle from "../SetTitle";
|
||||
import { radarName } from "../../config";
|
||||
|
||||
export default function PageHelp({ leaving, onLeave }: { leaving: boolean; onLeave: () => void }) {
|
||||
export default function PageHelp({
|
||||
leaving,
|
||||
onLeave,
|
||||
}: {
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
}) {
|
||||
return (
|
||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||
<SetTitle title={'How to use the ' + radarName} />
|
||||
<SetTitle title={"How to use the " + radarName} />
|
||||
<HeroHeadline>How to use the {radarName}</HeroHeadline>
|
||||
<div className='fullpage-content'>
|
||||
<div className="fullpage-content">
|
||||
<h3>Introduction</h3>
|
||||
<p>Technology is moving fast and new technologies and innovations appear continuously.</p>
|
||||
<p>
|
||||
It's essential for a development and technology company such as AOE to constantly improve and keep track with the latest useful innovations. It is
|
||||
important to openly look for innovations and new technologies and to question established technologies and methods every now and then.
|
||||
Technology is moving fast and new technologies and innovations appear
|
||||
continuously.
|
||||
</p>
|
||||
<p>
|
||||
But, it is also important to wisely choose which technologies to use in our daily work and in the different projects we are carrying out. As we all
|
||||
know: There is no silver bullet.
|
||||
It's essential for a development and technology company such as AOE to
|
||||
constantly improve and keep track with the latest useful innovations.
|
||||
It is important to openly look for innovations and new technologies
|
||||
and to question established technologies and methods every now and
|
||||
then.
|
||||
</p>
|
||||
<p>
|
||||
But, it is also important to wisely choose which technologies to use
|
||||
in our daily work and in the different projects we are carrying out.
|
||||
As we all know: There is no silver bullet.
|
||||
</p>
|
||||
<h3>What is the {radarName}</h3>
|
||||
<p>
|
||||
The Tech Radar is an overview of different technologies - from languages, frameworks, tools and patterns to platforms - that we consider "new or
|
||||
mentionable". The radar therefore doesn't provide an overview of all established technologies - but it focuses on items that have recently gained in
|
||||
importance or changed.
|
||||
The Tech Radar is an overview of different technologies - from
|
||||
languages, frameworks, tools and patterns to platforms - that we
|
||||
consider "new or mentionable". The radar therefore doesn't provide an
|
||||
overview of all established technologies - but it focuses on items
|
||||
that have recently gained in importance or changed.
|
||||
</p>
|
||||
<h3>How it is created</h3>
|
||||
<p>
|
||||
The items in the technology radar are raised by the different teams and therefore a lot of the items are related to the work and challenges the teams
|
||||
face in the different projects. In fact, we don't include anything on the radar, which we haven't already tried ourselves at least once.
|
||||
The items in the technology radar are raised by the different teams
|
||||
and therefore a lot of the items are related to the work and
|
||||
challenges the teams face in the different projects. In fact, we don't
|
||||
include anything on the radar, which we haven't already tried
|
||||
ourselves at least once.
|
||||
</p>
|
||||
<p>
|
||||
There have been a lot of valuable discussions in different expert groups about the classification and details of each of technologies and innovations.
|
||||
And the result of all this can be found in the latest technology radar.
|
||||
There have been a lot of valuable discussions in different expert
|
||||
groups about the classification and details of each of technologies
|
||||
and innovations. And the result of all this can be found in the latest
|
||||
technology radar.
|
||||
</p>
|
||||
<h3>How should it be used</h3>
|
||||
<p>The radar acts as an overview of technologies that we think everyone in the teams should currently know about.</p>
|
||||
<p>
|
||||
Its goal is to act as a guide and inspiration for the daily work in the teams. Its purpose is also to provide helpful information and a bird's-eye
|
||||
perspective - so that decisions can be taken with a much deeper understanding of the subject matter. This results in more-informed and better-aligned
|
||||
decisions.
|
||||
The radar acts as an overview of technologies that we think everyone
|
||||
in the teams should currently know about.
|
||||
</p>
|
||||
<p>
|
||||
Its goal is to act as a guide and inspiration for the daily work in
|
||||
the teams. Its purpose is also to provide helpful information and a
|
||||
bird's-eye perspective - so that decisions can be taken with a much
|
||||
deeper understanding of the subject matter. This results in
|
||||
more-informed and better-aligned decisions.
|
||||
</p>
|
||||
<p>
|
||||
We also hope that developers outside of AOE find the informations in
|
||||
our technologie overview inspirational.
|
||||
</p>
|
||||
<p>
|
||||
We group or categorize the items in 4 quadrants - (sometimes, when
|
||||
it's not 100% clear where a item belongs, we choose the best fit).
|
||||
</p>
|
||||
<p>We also hope that developers outside of AOE find the informations in our technologie overview inspirational.</p>
|
||||
<p>We group or categorize the items in 4 quadrants - (sometimes, when it's not 100% clear where a item belongs, we choose the best fit).</p>
|
||||
<p>The quadrants are:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Languages and Frameworks:</strong> We've placed development languages (such as Scala or Golang) here, as well as more low-level development
|
||||
frameworks (such as Play or Symfony), which are useful for implementing custom software of all kinds.{' '}
|
||||
<strong>Languages and Frameworks:</strong> We've placed development
|
||||
languages (such as Scala or Golang) here, as well as more low-level
|
||||
development frameworks (such as Play or Symfony), which are useful
|
||||
for implementing custom software of all kinds.{" "}
|
||||
</li>
|
||||
<li>
|
||||
<strong>Tools:</strong> Here we put different software tools - from small helpers to bigger software projects
|
||||
<strong>Tools:</strong> Here we put different software tools - from
|
||||
small helpers to bigger software projects
|
||||
</li>
|
||||
<li>
|
||||
<strong>Methods and Patterns:</strong> Patterns are so important, and a lot of them are valid for a long time (compared to some tools or
|
||||
frameworks). So, this is the category where we put information on methods and patterns concerning development, continuous x, testing, organization,
|
||||
architecture, etc.
|
||||
<strong>Methods and Patterns:</strong> Patterns are so important,
|
||||
and a lot of them are valid for a long time (compared to some tools
|
||||
or frameworks). So, this is the category where we put information on
|
||||
methods and patterns concerning development, continuous x, testing,
|
||||
organization, architecture, etc.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Platforms and Services</strong> (including AOE internal Services): Here we include infrastructure platforms and services. We also use this
|
||||
category to communicate news about AOE services that we want all AOE teams to be aware of.
|
||||
<strong>Platforms and Services</strong> (including AOE internal
|
||||
Services): Here we include infrastructure platforms and services. We
|
||||
also use this category to communicate news about AOE services that
|
||||
we want all AOE teams to be aware of.
|
||||
</li>
|
||||
</ul>
|
||||
<p>Each of the items is classified in one of these rings:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Adopt:</strong> We can clearly recommend this technology. We have used it for longer period of time in many teams and it has proven to be
|
||||
stable and useful.
|
||||
<strong>Adopt:</strong> We can clearly recommend this technology. We
|
||||
have used it for longer period of time in many teams and it has
|
||||
proven to be stable and useful.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Trial:</strong> We have used it with success and recommend to have a closer look at the technology in this ring. The goal of items here is
|
||||
to look at them more closely, with the goal to bring them to the adopt level.
|
||||
<strong>Trial:</strong> We have used it with success and recommend
|
||||
to have a closer look at the technology in this ring. The goal of
|
||||
items here is to look at them more closely, with the goal to bring
|
||||
them to the adopt level.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Assess:</strong> We have tried it out and we find it promising. We recommend having a look at these items when you face a specific need for
|
||||
the technology in your project.
|
||||
<strong>Assess:</strong> We have tried it out and we find it
|
||||
promising. We recommend having a look at these items when you face a
|
||||
specific need for the technology in your project.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Hold:</strong> This category is a bit special. Unlike the others, we recommend to stop doing or using something. That does not mean that
|
||||
they are bad and it often might be ok to use them in existing projects. But we move things here if we think we shouldn't do them anymore - because
|
||||
we see better options or alternatives now.
|
||||
<strong>Hold:</strong> This category is a bit special. Unlike the
|
||||
others, we recommend to stop doing or using something. That does not
|
||||
mean that they are bad and it often might be ok to use them in
|
||||
existing projects. But we move things here if we think we shouldn't
|
||||
do them anymore - because we see better options or alternatives now.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Contributions and source code of the radar are on github:{' '}
|
||||
<a href='https://github.com/AOEpeople/aoe_technology_radar' target='_blank' rel='noopener noreferrer'>
|
||||
Contributions and source code of the radar are on github:{" "}
|
||||
<a
|
||||
href="https://github.com/AOEpeople/aoe_technology_radar"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
AOE Tech Radar on Github
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import { formatRelease } from '../../date';
|
||||
import { featuredOnly, Item } from '../../model';
|
||||
import HeroHeadline from '../HeroHeadline/HeroHeadline';
|
||||
import QuadrantGrid from '../QuadrantGrid/QuadrantGrid';
|
||||
import Fadeable from '../Fadeable/Fadeable';
|
||||
import SetTitle from '../SetTitle';
|
||||
import { radarName, radarNameShort } from '../../config';
|
||||
import { MomentInput } from 'moment';
|
||||
import React from "react";
|
||||
import { formatRelease } from "../../date";
|
||||
import { featuredOnly, Item } from "../../model";
|
||||
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||
import QuadrantGrid from "../QuadrantGrid/QuadrantGrid";
|
||||
import Fadeable from "../Fadeable/Fadeable";
|
||||
import SetTitle from "../SetTitle";
|
||||
import { radarName, radarNameShort } from "../../config";
|
||||
import { MomentInput } from "moment";
|
||||
|
||||
type PageIndexProps = {
|
||||
leaving: boolean;
|
||||
@@ -15,17 +15,26 @@ type PageIndexProps = {
|
||||
releases: MomentInput[];
|
||||
};
|
||||
|
||||
export default function PageIndex({ leaving, onLeave, items, releases }: PageIndexProps) {
|
||||
export default function PageIndex({
|
||||
leaving,
|
||||
onLeave,
|
||||
items,
|
||||
releases,
|
||||
}: PageIndexProps) {
|
||||
const newestRelease = releases.slice(-1)[0];
|
||||
const numberOfReleases = releases.length;
|
||||
return (
|
||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||
<SetTitle title={radarNameShort} />
|
||||
<div className='headline-group'>
|
||||
<HeroHeadline alt={`Version #${numberOfReleases}`}>{radarName}</HeroHeadline>
|
||||
<div className="headline-group">
|
||||
<HeroHeadline alt={`Version #${numberOfReleases}`}>
|
||||
{radarName}
|
||||
</HeroHeadline>
|
||||
</div>
|
||||
<QuadrantGrid items={featuredOnly(items)} />
|
||||
<div className='publish-date'>Published {formatRelease(newestRelease)}</div>
|
||||
<div className="publish-date">
|
||||
Published {formatRelease(newestRelease)}
|
||||
</div>
|
||||
</Fadeable>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,239 +1,271 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import Badge from '../Badge/Badge';
|
||||
import ItemList from '../ItemList/ItemList';
|
||||
import Link from '../Link/Link';
|
||||
import FooterEnd from '../FooterEnd/FooterEnd';
|
||||
import SetTitle from '../SetTitle';
|
||||
import ItemRevisions from '../ItemRevisions/ItemRevisions';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Badge from "../Badge/Badge";
|
||||
import ItemList from "../ItemList/ItemList";
|
||||
import Link from "../Link/Link";
|
||||
import FooterEnd from "../FooterEnd/FooterEnd";
|
||||
import SetTitle from "../SetTitle";
|
||||
import ItemRevisions from "../ItemRevisions/ItemRevisions";
|
||||
import {
|
||||
AnimationStates,
|
||||
createAnimation,
|
||||
createAnimationRunner
|
||||
} from '../../animation';
|
||||
import './item-page.scss';
|
||||
import {translate} from '../../config';
|
||||
import {groupByQuadrants, Item} from '../../model';
|
||||
AnimationStates,
|
||||
createAnimation,
|
||||
createAnimationRunner,
|
||||
} from "../../animation";
|
||||
import "./item-page.scss";
|
||||
import { translate } from "../../config";
|
||||
import { groupByQuadrants, Item } from "../../model";
|
||||
|
||||
const getItem = (pageName: string, items: Item[]) => {
|
||||
const [quadrantName, itemName] = pageName.split('/');
|
||||
return items.filter((item) => item.quadrant === quadrantName && item.name === itemName)[0];
|
||||
const [quadrantName, itemName] = pageName.split("/");
|
||||
return items.filter(
|
||||
(item) => item.quadrant === quadrantName && item.name === itemName
|
||||
)[0];
|
||||
};
|
||||
|
||||
const getItemsInRing = (pageName: string, items: Item[]) => {
|
||||
const item = getItem(pageName, items);
|
||||
return groupByQuadrants(items)[item.quadrant][item.ring];
|
||||
const item = getItem(pageName, items);
|
||||
return groupByQuadrants(items)[item.quadrant][item.ring];
|
||||
};
|
||||
|
||||
type PageItemProps = {
|
||||
pageName: string;
|
||||
items: Item[];
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
pageName: string;
|
||||
items: Item[];
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
};
|
||||
|
||||
export default function PageItem({pageName, items, leaving, onLeave}: PageItemProps) {
|
||||
const itemsInRing = getItemsInRing(pageName, items);
|
||||
export default function PageItem({
|
||||
pageName,
|
||||
items,
|
||||
leaving,
|
||||
onLeave,
|
||||
}: PageItemProps) {
|
||||
const itemsInRing = getItemsInRing(pageName, items);
|
||||
|
||||
const animationsIn = {
|
||||
background: createAnimation(
|
||||
{
|
||||
transform: 'translateX(calc((100vw - 1200px) / 2 + 800px))',
|
||||
transition: 'transform 450ms cubic-bezier(0.24, 1.12, 0.71, 0.98)',
|
||||
},
|
||||
{
|
||||
transition: 'transform 450ms cubic-bezier(0.24, 1.12, 0.71, 0.98)',
|
||||
transform: 'translateX(0)',
|
||||
},
|
||||
0
|
||||
),
|
||||
navHeader: createAnimation(
|
||||
{
|
||||
transform: 'translateX(-40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(0px)',
|
||||
opacity: '1',
|
||||
},
|
||||
300
|
||||
),
|
||||
text: createAnimation(
|
||||
{
|
||||
transform: 'translateY(-20px)',
|
||||
opacity: '0',
|
||||
},
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateY(0px)',
|
||||
opacity: '1',
|
||||
},
|
||||
600
|
||||
),
|
||||
items: itemsInRing.map((item, i) =>
|
||||
createAnimation(
|
||||
{
|
||||
transform: 'translateX(-40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(0px)',
|
||||
opacity: '1',
|
||||
},
|
||||
400 + 100 * i
|
||||
)
|
||||
),
|
||||
footer: createAnimation(
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(-40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(0px)',
|
||||
opacity: '1',
|
||||
},
|
||||
600 + itemsInRing.length * 100
|
||||
),
|
||||
};
|
||||
const animationsIn = {
|
||||
background: createAnimation(
|
||||
{
|
||||
transform: "translateX(calc((100vw - 1200px) / 2 + 800px))",
|
||||
transition: "transform 450ms cubic-bezier(0.24, 1.12, 0.71, 0.98)",
|
||||
},
|
||||
{
|
||||
transition: "transform 450ms cubic-bezier(0.24, 1.12, 0.71, 0.98)",
|
||||
transform: "translateX(0)",
|
||||
},
|
||||
0
|
||||
),
|
||||
navHeader: createAnimation(
|
||||
{
|
||||
transform: "translateX(-40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(0px)",
|
||||
opacity: "1",
|
||||
},
|
||||
300
|
||||
),
|
||||
text: createAnimation(
|
||||
{
|
||||
transform: "translateY(-20px)",
|
||||
opacity: "0",
|
||||
},
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateY(0px)",
|
||||
opacity: "1",
|
||||
},
|
||||
600
|
||||
),
|
||||
items: itemsInRing.map((item, i) =>
|
||||
createAnimation(
|
||||
{
|
||||
transform: "translateX(-40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(0px)",
|
||||
opacity: "1",
|
||||
},
|
||||
400 + 100 * i
|
||||
)
|
||||
),
|
||||
footer: createAnimation(
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(-40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(0px)",
|
||||
opacity: "1",
|
||||
},
|
||||
600 + itemsInRing.length * 100
|
||||
),
|
||||
};
|
||||
|
||||
const animationsOut = {
|
||||
background: createAnimation(animationsIn.background.stateB, animationsIn.background.stateA, 300 + itemsInRing.length * 50),
|
||||
navHeader: createAnimation(
|
||||
animationsIn.navHeader.stateB,
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
0
|
||||
),
|
||||
text: createAnimation(
|
||||
animationsIn.text.stateB,
|
||||
{
|
||||
transform: 'translateY(20px)',
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
opacity: '0',
|
||||
},
|
||||
0
|
||||
),
|
||||
items: itemsInRing.map((item, i) =>
|
||||
createAnimation(
|
||||
animationsIn.items[i].stateB,
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
100 + 50 * i
|
||||
)
|
||||
),
|
||||
footer: createAnimation(
|
||||
animationsIn.text.stateB,
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
200 + itemsInRing.length * 50
|
||||
),
|
||||
};
|
||||
const animationsOut = {
|
||||
background: createAnimation(
|
||||
animationsIn.background.stateB,
|
||||
animationsIn.background.stateA,
|
||||
300 + itemsInRing.length * 50
|
||||
),
|
||||
navHeader: createAnimation(
|
||||
animationsIn.navHeader.stateB,
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
0
|
||||
),
|
||||
text: createAnimation(
|
||||
animationsIn.text.stateB,
|
||||
{
|
||||
transform: "translateY(20px)",
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
opacity: "0",
|
||||
},
|
||||
0
|
||||
),
|
||||
items: itemsInRing.map((item, i) =>
|
||||
createAnimation(
|
||||
animationsIn.items[i].stateB,
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
100 + 50 * i
|
||||
)
|
||||
),
|
||||
footer: createAnimation(
|
||||
animationsIn.text.stateB,
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
200 + itemsInRing.length * 50
|
||||
),
|
||||
};
|
||||
|
||||
const [animations, setAnimations] = useState<AnimationStates>(() => {
|
||||
return leaving ? createAnimationRunner(animationsIn).getState() : {}
|
||||
});
|
||||
const [animations, setAnimations] = useState<AnimationStates>(() => {
|
||||
return leaving ? createAnimationRunner(animationsIn).getState() : {};
|
||||
});
|
||||
|
||||
const [stateLeaving, setStateLeaving] = useState(leaving);
|
||||
const [stateLeaving, setStateLeaving] = useState(leaving);
|
||||
|
||||
useEffect(() => {
|
||||
if (!stateLeaving && leaving) {
|
||||
let animationRunner = createAnimationRunner(
|
||||
animationsOut,
|
||||
() => setAnimations(animationRunner.getState),
|
||||
)
|
||||
animationRunner.run();
|
||||
animationRunner.awaitAnimationComplete(onLeave);
|
||||
setStateLeaving(true)
|
||||
}
|
||||
if (stateLeaving && !leaving) {
|
||||
let animationRunner = createAnimationRunner(
|
||||
animationsIn,
|
||||
() => setAnimations(animationRunner.getState),
|
||||
)
|
||||
animationRunner.run();
|
||||
setStateLeaving(false)
|
||||
}
|
||||
}, [stateLeaving, leaving, animationsIn, animationsOut, onLeave])
|
||||
|
||||
const getAnimationStates = (name: string) => {
|
||||
if (!animations) {
|
||||
return undefined;
|
||||
}
|
||||
return animations[name];
|
||||
useEffect(() => {
|
||||
if (!stateLeaving && leaving) {
|
||||
let animationRunner = createAnimationRunner(animationsOut, () =>
|
||||
setAnimations(animationRunner.getState)
|
||||
);
|
||||
animationRunner.run();
|
||||
animationRunner.awaitAnimationComplete(onLeave);
|
||||
setStateLeaving(true);
|
||||
}
|
||||
if (stateLeaving && !leaving) {
|
||||
let animationRunner = createAnimationRunner(animationsIn, () =>
|
||||
setAnimations(animationRunner.getState)
|
||||
);
|
||||
animationRunner.run();
|
||||
setStateLeaving(false);
|
||||
}
|
||||
}, [stateLeaving, leaving, animationsIn, animationsOut, onLeave]);
|
||||
|
||||
const getAnimationState = (name: string) => {
|
||||
const animations = getAnimationStates(name)
|
||||
if (animations === undefined || animations.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
return animations[0]
|
||||
};
|
||||
const getAnimationStates = (name: string) => {
|
||||
if (!animations) {
|
||||
return undefined;
|
||||
}
|
||||
return animations[name];
|
||||
};
|
||||
|
||||
const item = getItem(pageName, items);
|
||||
const getAnimationState = (name: string) => {
|
||||
const animations = getAnimationStates(name);
|
||||
if (animations === undefined || animations.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return animations[0];
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SetTitle title={item.title}/>
|
||||
<div className='item-page'>
|
||||
<div className='item-page__nav'>
|
||||
<div className='item-page__nav__inner'>
|
||||
<div className='item-page__header' style={getAnimationState('navHeader')}>
|
||||
<h3 className='headline'>{translate(item.quadrant)}</h3>
|
||||
</div>
|
||||
const item = getItem(pageName, items);
|
||||
|
||||
<ItemList items={itemsInRing} activeItem={item} headerStyle={getAnimationState('navHeader')}
|
||||
itemStyle={getAnimationStates('items')}>
|
||||
<div className='split'>
|
||||
<div className='split__left'>
|
||||
<Badge big type={item.ring}>
|
||||
{item.ring}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className='split__right'>
|
||||
<Link className='icon-link' pageName={item.quadrant}>
|
||||
<span className='icon icon--pie icon-link__icon'/>
|
||||
Quadrant Overview
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</ItemList>
|
||||
<div className='item-page__footer' style={getAnimationState('footer')}>
|
||||
<FooterEnd modifier='in-sidebar'/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='item-page__content' style={getAnimationState('background')}>
|
||||
<div className='item-page__content__inner' style={getAnimationState('text')}>
|
||||
<div className='item-page__header'>
|
||||
<div className='split'>
|
||||
<div className='split__left'>
|
||||
<h1 className='hero-headline hero-headline--inverse'>{item.title}</h1>
|
||||
</div>
|
||||
<div className='split__right'>
|
||||
<Badge big type={item.ring}>
|
||||
{item.ring}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='markdown' dangerouslySetInnerHTML={{__html: item.body}}/>
|
||||
{item.revisions.length > 1 && <ItemRevisions revisions={item.revisions.slice(1)}/>}
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<div>
|
||||
<SetTitle title={item.title} />
|
||||
<div className="item-page">
|
||||
<div className="item-page__nav">
|
||||
<div className="item-page__nav__inner">
|
||||
<div
|
||||
className="item-page__header"
|
||||
style={getAnimationState("navHeader")}
|
||||
>
|
||||
<h3 className="headline">{translate(item.quadrant)}</h3>
|
||||
</div>
|
||||
|
||||
<ItemList
|
||||
items={itemsInRing}
|
||||
activeItem={item}
|
||||
headerStyle={getAnimationState("navHeader")}
|
||||
itemStyle={getAnimationStates("items")}
|
||||
>
|
||||
<div className="split">
|
||||
<div className="split__left">
|
||||
<Badge big type={item.ring}>
|
||||
{item.ring}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="split__right">
|
||||
<Link className="icon-link" pageName={item.quadrant}>
|
||||
<span className="icon icon--pie icon-link__icon" />
|
||||
Quadrant Overview
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</ItemList>
|
||||
<div
|
||||
className="item-page__footer"
|
||||
style={getAnimationState("footer")}
|
||||
>
|
||||
<FooterEnd modifier="in-sidebar" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<div
|
||||
className="item-page__content"
|
||||
style={getAnimationState("background")}
|
||||
>
|
||||
<div
|
||||
className="item-page__content__inner"
|
||||
style={getAnimationState("text")}
|
||||
>
|
||||
<div className="item-page__header">
|
||||
<div className="split">
|
||||
<div className="split__left">
|
||||
<h1 className="hero-headline hero-headline--inverse">
|
||||
{item.title}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="split__right">
|
||||
<Badge big type={item.ring}>
|
||||
{item.ring}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="markdown"
|
||||
dangerouslySetInnerHTML={{ __html: item.body }}
|
||||
/>
|
||||
{item.revisions.length > 1 && (
|
||||
<ItemRevisions revisions={item.revisions.slice(1)} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,6 +57,6 @@
|
||||
min-height: 300px;
|
||||
|
||||
&__aside {
|
||||
padding: 20px 0
|
||||
padding: 20px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import Badge from '../Badge/Badge';
|
||||
import ItemList from '../ItemList/ItemList';
|
||||
import Link from '../Link/Link';
|
||||
import Fadeable from '../Fadeable/Fadeable';
|
||||
import SetTitle from '../SetTitle';
|
||||
import ItemRevisions from '../ItemRevisions/ItemRevisions';
|
||||
import React from "react";
|
||||
import Badge from "../Badge/Badge";
|
||||
import ItemList from "../ItemList/ItemList";
|
||||
import Link from "../Link/Link";
|
||||
import Fadeable from "../Fadeable/Fadeable";
|
||||
import SetTitle from "../SetTitle";
|
||||
import ItemRevisions from "../ItemRevisions/ItemRevisions";
|
||||
|
||||
import { translate } from '../../config';
|
||||
import { groupByQuadrants, Item } from '../../model';
|
||||
import { translate } from "../../config";
|
||||
import { groupByQuadrants, Item } from "../../model";
|
||||
|
||||
type PageItemMobileProps = {
|
||||
pageName: string;
|
||||
@@ -16,10 +16,17 @@ type PageItemMobileProps = {
|
||||
onLeave: () => void;
|
||||
};
|
||||
|
||||
export default function PageItemMobile({ pageName, items, leaving, onLeave }: PageItemMobileProps) {
|
||||
export default function PageItemMobile({
|
||||
pageName,
|
||||
items,
|
||||
leaving,
|
||||
onLeave,
|
||||
}: PageItemMobileProps) {
|
||||
const getItem = (pageName: string, items: Item[]) => {
|
||||
const [quadrantName, itemName] = pageName.split('/');
|
||||
const item = items.filter((item) => item.quadrant === quadrantName && item.name === itemName)[0];
|
||||
const [quadrantName, itemName] = pageName.split("/");
|
||||
const item = items.filter(
|
||||
(item) => item.quadrant === quadrantName && item.name === itemName
|
||||
)[0];
|
||||
return item;
|
||||
};
|
||||
|
||||
@@ -34,36 +41,43 @@ export default function PageItemMobile({ pageName, items, leaving, onLeave }: Pa
|
||||
return (
|
||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||
<SetTitle title={item.title} />
|
||||
<div className='mobile-item-page'>
|
||||
<div className='mobile-item-page__content'>
|
||||
<div className='mobile-item-page__content__inner'>
|
||||
<div className='mobile-item-page__header'>
|
||||
<div className='split'>
|
||||
<div className='split__left'>
|
||||
<h3 className='headline'>{translate(item.quadrant)}</h3>
|
||||
<h1 className='hero-headline hero-headline--inverse'>{item.title}</h1>
|
||||
<div className="mobile-item-page">
|
||||
<div className="mobile-item-page__content">
|
||||
<div className="mobile-item-page__content__inner">
|
||||
<div className="mobile-item-page__header">
|
||||
<div className="split">
|
||||
<div className="split__left">
|
||||
<h3 className="headline">{translate(item.quadrant)}</h3>
|
||||
<h1 className="hero-headline hero-headline--inverse">
|
||||
{item.title}
|
||||
</h1>
|
||||
</div>
|
||||
<div className='split__right'>
|
||||
<div className="split__right">
|
||||
<Badge big type={item.ring}>
|
||||
{item.ring}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='markdown' dangerouslySetInnerHTML={{ __html: item.body }} />
|
||||
{item.revisions.length > 1 && <ItemRevisions revisions={item.revisions.slice(1)} />}
|
||||
<div
|
||||
className="markdown"
|
||||
dangerouslySetInnerHTML={{ __html: item.body }}
|
||||
/>
|
||||
{item.revisions.length > 1 && (
|
||||
<ItemRevisions revisions={item.revisions.slice(1)} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<aside className='mobile-item-page__aside'>
|
||||
<aside className="mobile-item-page__aside">
|
||||
<ItemList items={itemsInRing} activeItem={item}>
|
||||
<div className='split'>
|
||||
<div className='split__left'>
|
||||
<h3 className='headline'>{translate(item.quadrant)}</h3>
|
||||
<div className="split">
|
||||
<div className="split__left">
|
||||
<h3 className="headline">{translate(item.quadrant)}</h3>
|
||||
</div>
|
||||
<div className='split__right'>
|
||||
<Link className='icon-link' pageName={item.quadrant}>
|
||||
<span className='icon icon--pie icon-link__icon'></span>Zoom In
|
||||
<div className="split__right">
|
||||
<Link className="icon-link" pageName={item.quadrant}>
|
||||
<span className="icon icon--pie icon-link__icon"></span>Zoom In
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,30 +1,39 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import HeadlineGroup from '../HeadlineGroup/HeadlineGroup';
|
||||
import HeroHeadline from '../HeroHeadline/HeroHeadline';
|
||||
import Badge from '../Badge/Badge';
|
||||
import Link from '../Link/Link';
|
||||
import Search from '../Search/Search';
|
||||
import Fadeable from '../Fadeable/Fadeable';
|
||||
import SetTitle from '../SetTitle';
|
||||
import Flag from '../Flag/Flag';
|
||||
import { groupByFirstLetter, Item } from '../../model';
|
||||
import { translate, Ring } from '../../config';
|
||||
import React, { useState, useEffect } from "react";
|
||||
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
||||
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||
import Badge from "../Badge/Badge";
|
||||
import Link from "../Link/Link";
|
||||
import Search from "../Search/Search";
|
||||
import Fadeable from "../Fadeable/Fadeable";
|
||||
import SetTitle from "../SetTitle";
|
||||
import Flag from "../Flag/Flag";
|
||||
import { groupByFirstLetter, Item } from "../../model";
|
||||
import { translate, Ring } from "../../config";
|
||||
|
||||
const containsSearchTerm = (text = '', term = '') => {
|
||||
const containsSearchTerm = (text = "", term = "") => {
|
||||
// TODO search refinement
|
||||
return text.trim().toLocaleLowerCase().indexOf(term.trim().toLocaleLowerCase()) !== -1;
|
||||
return (
|
||||
text.trim().toLocaleLowerCase().indexOf(term.trim().toLocaleLowerCase()) !==
|
||||
-1
|
||||
);
|
||||
};
|
||||
|
||||
type PageOverviewProps = {
|
||||
rings: readonly ('all' | Ring)[];
|
||||
rings: readonly ("all" | Ring)[];
|
||||
search: string;
|
||||
items: Item[];
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
};
|
||||
|
||||
export default function PageOverview({ rings, search: searchProp, items, leaving, onLeave }: PageOverviewProps) {
|
||||
const [ring, setRing] = useState<Ring | 'all'>('all');
|
||||
export default function PageOverview({
|
||||
rings,
|
||||
search: searchProp,
|
||||
items,
|
||||
leaving,
|
||||
onLeave,
|
||||
}: PageOverviewProps) {
|
||||
const [ring, setRing] = useState<Ring | "all">("all");
|
||||
const [search, setSearch] = useState(searchProp);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -40,13 +49,19 @@ export default function PageOverview({ rings, search: searchProp, items, leaving
|
||||
|
||||
const isRingActive = (ringName: string) => ring === ringName;
|
||||
|
||||
const itemMatchesRing = (item: Item) => ring === 'all' || item.ring === ring;
|
||||
const itemMatchesRing = (item: Item) => ring === "all" || item.ring === ring;
|
||||
|
||||
const itemMatchesSearch = (item: Item) => {
|
||||
return search.trim() === '' || containsSearchTerm(item.title, search) || containsSearchTerm(item.body, search) || containsSearchTerm(item.info, search);
|
||||
return (
|
||||
search.trim() === "" ||
|
||||
containsSearchTerm(item.title, search) ||
|
||||
containsSearchTerm(item.body, search) ||
|
||||
containsSearchTerm(item.info, search)
|
||||
);
|
||||
};
|
||||
|
||||
const isItemVisible = (item: Item) => itemMatchesRing(item) && itemMatchesSearch(item);
|
||||
const isItemVisible = (item: Item) =>
|
||||
itemMatchesRing(item) && itemMatchesSearch(item);
|
||||
|
||||
const getFilteredAndGroupedItems = () => {
|
||||
const groups = groupByFirstLetter(items);
|
||||
@@ -54,7 +69,9 @@ export default function PageOverview({ rings, search: searchProp, items, leaving
|
||||
...group,
|
||||
items: group.items.filter(isItemVisible),
|
||||
}));
|
||||
const nonEmptyGroups = groupsFiltered.filter((group) => group.items.length > 0);
|
||||
const nonEmptyGroups = groupsFiltered.filter(
|
||||
(group) => group.items.length > 0
|
||||
);
|
||||
return nonEmptyGroups;
|
||||
};
|
||||
|
||||
@@ -64,20 +81,24 @@ export default function PageOverview({ rings, search: searchProp, items, leaving
|
||||
|
||||
return (
|
||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||
<SetTitle title='Technologies Overview' />
|
||||
<SetTitle title="Technologies Overview" />
|
||||
<HeadlineGroup>
|
||||
<HeroHeadline>Technologies Overview</HeroHeadline>
|
||||
</HeadlineGroup>
|
||||
<div className='filter'>
|
||||
<div className='split split--filter'>
|
||||
<div className='split__left'>
|
||||
<div className="filter">
|
||||
<div className="split split--filter">
|
||||
<div className="split__left">
|
||||
<Search onChange={handleSearchTermChange} value={search} />
|
||||
</div>
|
||||
<div className='split__right'>
|
||||
<div className='nav'>
|
||||
<div className="split__right">
|
||||
<div className="nav">
|
||||
{rings.map((ringName) => (
|
||||
<div className='nav__item' key={ringName}>
|
||||
<Badge big onClick={handleRingClick(ringName)} type={isRingActive(ringName) ? ringName : 'empty'}>
|
||||
<div className="nav__item" key={ringName}>
|
||||
<Badge
|
||||
big
|
||||
onClick={handleRingClick(ringName)}
|
||||
type={isRingActive(ringName) ? ringName : "empty"}
|
||||
>
|
||||
{ringName}
|
||||
</Badge>
|
||||
</div>
|
||||
@@ -87,30 +108,32 @@ export default function PageOverview({ rings, search: searchProp, items, leaving
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='letter-index'>
|
||||
<div className="letter-index">
|
||||
{groups.map(({ letter, items }) => (
|
||||
<div key={letter} className='letter-index__group'>
|
||||
<div className='letter-index__letter'>{letter}</div>
|
||||
<div className='letter-index__items'>
|
||||
<div className='item-list'>
|
||||
<div className='item-list__list'>
|
||||
<div key={letter} className="letter-index__group">
|
||||
<div className="letter-index__letter">{letter}</div>
|
||||
<div className="letter-index__items">
|
||||
<div className="item-list">
|
||||
<div className="item-list__list">
|
||||
{items.map((item) => (
|
||||
<Link
|
||||
key={item.name}
|
||||
className='item item--big item--no-leading-border item--no-trailing-border'
|
||||
className="item item--big item--no-leading-border item--no-trailing-border"
|
||||
pageName={`${item.quadrant}/${item.name}`}
|
||||
>
|
||||
<div className='split split--overview'>
|
||||
<div className='split__left'>
|
||||
<div className='item__title'>
|
||||
<div className="split split--overview">
|
||||
<div className="split__left">
|
||||
<div className="item__title">
|
||||
{item.title}
|
||||
<Flag item={item} />
|
||||
</div>
|
||||
</div>
|
||||
<div className='split__right'>
|
||||
<div className='nav nav--relations'>
|
||||
<div className='nav__item'>{translate(item.quadrant)}</div>
|
||||
<div className='nav__item'>
|
||||
<div className="split__right">
|
||||
<div className="nav nav--relations">
|
||||
<div className="nav__item">
|
||||
{translate(item.quadrant)}
|
||||
</div>
|
||||
<div className="nav__item">
|
||||
<Badge type={item.ring}>{item.ring}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import HeroHeadline from '../HeroHeadline/HeroHeadline';
|
||||
import HeadlineGroup from '../HeadlineGroup/HeadlineGroup';
|
||||
import QuadrantSection from '../QuadrantSection/QuadrantSection';
|
||||
import Fadeable from '../Fadeable/Fadeable';
|
||||
import SetTitle from '../SetTitle';
|
||||
import React from "react";
|
||||
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
||||
import QuadrantSection from "../QuadrantSection/QuadrantSection";
|
||||
import Fadeable from "../Fadeable/Fadeable";
|
||||
import SetTitle from "../SetTitle";
|
||||
|
||||
import { translate } from '../../config';
|
||||
import { featuredOnly, groupByQuadrants, Item } from '../../model';
|
||||
import { translate } from "../../config";
|
||||
import { featuredOnly, groupByQuadrants, Item } from "../../model";
|
||||
|
||||
type PageQuadrantProps = {
|
||||
leaving: boolean;
|
||||
@@ -15,7 +15,12 @@ type PageQuadrantProps = {
|
||||
items: Item[];
|
||||
};
|
||||
|
||||
export default function PageQuadrant({ leaving, onLeave, pageName, items }: PageQuadrantProps) {
|
||||
export default function PageQuadrant({
|
||||
leaving,
|
||||
onLeave,
|
||||
pageName,
|
||||
items,
|
||||
}: PageQuadrantProps) {
|
||||
const groups = groupByQuadrants(featuredOnly(items));
|
||||
return (
|
||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
import React from 'react';
|
||||
import HeroHeadline from '../HeroHeadline/HeroHeadline';
|
||||
import Fadeable from '../Fadeable/Fadeable';
|
||||
import SetTitle from '../SetTitle';
|
||||
import React from "react";
|
||||
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||
import Fadeable from "../Fadeable/Fadeable";
|
||||
import SetTitle from "../SetTitle";
|
||||
|
||||
export default function PageToolbox({ leaving, onLeave }: { leaving: boolean; onLeave: () => void }) {
|
||||
export default function PageToolbox({
|
||||
leaving,
|
||||
onLeave,
|
||||
}: {
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
}) {
|
||||
return (
|
||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||
<SetTitle title='Small AOE Toolbox' />
|
||||
<SetTitle title="Small AOE Toolbox" />
|
||||
<HeroHeadline>Small AOE Toolbox</HeroHeadline>
|
||||
<div className='fullpage-content'>
|
||||
<div className="fullpage-content">
|
||||
<h3>Useful Tools</h3>
|
||||
|
||||
<ul>
|
||||
<li>Fiddler - free web debugging proxy ( http://www.telerik.com/fiddler )</li>
|
||||
<li>
|
||||
Fiddler - free web debugging proxy ( http://www.telerik.com/fiddler
|
||||
)
|
||||
</li>
|
||||
<li>SoapUI - Webservice Test Tool (https://www.soapui.org/ )</li>
|
||||
<li>Postman - API Test Tool ( https://www.getpostman.com/ )</li>
|
||||
<li> Modelio - Simple free UML Modelling tool ( https://www.modelio.org/ )</li>
|
||||
<li>
|
||||
{" "}
|
||||
Modelio - Simple free UML Modelling tool ( https://www.modelio.org/
|
||||
)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Useful Tools (commercial)</h3>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { groupByQuadrants, Item, Group } from '../../model';
|
||||
import { quadrants } from '../../config';
|
||||
import QuadrantSection from '../QuadrantSection/QuadrantSection';
|
||||
import './quadrant-grid.scss';
|
||||
import React from "react";
|
||||
import { groupByQuadrants, Item, Group } from "../../model";
|
||||
import { quadrants } from "../../config";
|
||||
import QuadrantSection from "../QuadrantSection/QuadrantSection";
|
||||
import "./quadrant-grid.scss";
|
||||
const renderQuadrant = (quadrantName: string, groups: Group) => {
|
||||
return (
|
||||
<div key={quadrantName} className='quadrant-grid__quadrant'>
|
||||
<div key={quadrantName} className="quadrant-grid__quadrant">
|
||||
<QuadrantSection quadrantName={quadrantName} groups={groups} />
|
||||
</div>
|
||||
);
|
||||
@@ -13,5 +13,9 @@ const renderQuadrant = (quadrantName: string, groups: Group) => {
|
||||
|
||||
export default function QuadrantGrid({ items }: { items: Item[] }) {
|
||||
const groups = groupByQuadrants(items);
|
||||
return <div className='quadrant-grid'>{quadrants.map((quadrantName) => renderQuadrant(quadrantName, groups))}</div>;
|
||||
return (
|
||||
<div className="quadrant-grid">
|
||||
{quadrants.map((quadrantName) => renderQuadrant(quadrantName, groups))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.quadrant-grid {
|
||||
display: flex;
|
||||
@@ -13,4 +13,4 @@
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import React from 'react';
|
||||
import { translate, rings, Ring, showEmptyRings } from '../../config';
|
||||
import Badge from '../Badge/Badge';
|
||||
import Link from '../Link/Link';
|
||||
import ItemList from '../ItemList/ItemList';
|
||||
import Flag from '../Flag/Flag';
|
||||
import { Group } from '../../model';
|
||||
import './quadrant-section.scss';
|
||||
const renderList = (ringName: Ring, quadrantName: string, groups: Group, big: boolean) => {
|
||||
import React from "react";
|
||||
import { translate, rings, Ring, showEmptyRings } from "../../config";
|
||||
import Badge from "../Badge/Badge";
|
||||
import Link from "../Link/Link";
|
||||
import ItemList from "../ItemList/ItemList";
|
||||
import Flag from "../Flag/Flag";
|
||||
import { Group } from "../../model";
|
||||
import "./quadrant-section.scss";
|
||||
const renderList = (
|
||||
ringName: Ring,
|
||||
quadrantName: string,
|
||||
groups: Group,
|
||||
big: boolean
|
||||
) => {
|
||||
const itemsInRing = groups[quadrantName][ringName] || [];
|
||||
|
||||
if (big) {
|
||||
@@ -20,13 +25,13 @@ const renderList = (ringName: Ring, quadrantName: string, groups: Group, big: bo
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='ring-list'>
|
||||
<div className='ring-list__header'>
|
||||
<div className="ring-list">
|
||||
<div className="ring-list__header">
|
||||
<Badge type={ringName}>{ringName}</Badge>
|
||||
</div>
|
||||
{itemsInRing.map((item) => (
|
||||
<span key={item.name} className='ring-list__item'>
|
||||
<Link className='link' pageName={`${item.quadrant}/${item.name}`}>
|
||||
<span key={item.name} className="ring-list__item">
|
||||
<Link className="link" pageName={`${item.quadrant}/${item.name}`}>
|
||||
{item.title}
|
||||
<Flag item={item} short />
|
||||
</Link>
|
||||
@@ -36,36 +41,58 @@ const renderList = (ringName: Ring, quadrantName: string, groups: Group, big: bo
|
||||
);
|
||||
};
|
||||
|
||||
const renderRing = (ringName: Ring, quadrantName: string, groups: Group, big: boolean) => {
|
||||
if (!showEmptyRings && (!groups[quadrantName] || !groups[quadrantName][ringName] || groups[quadrantName][ringName].length === 0)) {
|
||||
const renderRing = (
|
||||
ringName: Ring,
|
||||
quadrantName: string,
|
||||
groups: Group,
|
||||
big: boolean
|
||||
) => {
|
||||
if (
|
||||
!showEmptyRings &&
|
||||
(!groups[quadrantName] ||
|
||||
!groups[quadrantName][ringName] ||
|
||||
groups[quadrantName][ringName].length === 0)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div key={ringName} className='quadrant-section__ring'>
|
||||
<div key={ringName} className="quadrant-section__ring">
|
||||
{renderList(ringName, quadrantName, groups, big)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function QuadrantSection({ quadrantName, groups, big = false }: { quadrantName: string; groups: Group; big?: boolean }) {
|
||||
export default function QuadrantSection({
|
||||
quadrantName,
|
||||
groups,
|
||||
big = false,
|
||||
}: {
|
||||
quadrantName: string;
|
||||
groups: Group;
|
||||
big?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className='quadrant-section'>
|
||||
<div className='quadrant-section__header'>
|
||||
<div className='split'>
|
||||
<div className='split__left'>
|
||||
<h4 className='headline'>{translate(quadrantName)}</h4>
|
||||
<div className="quadrant-section">
|
||||
<div className="quadrant-section__header">
|
||||
<div className="split">
|
||||
<div className="split__left">
|
||||
<h4 className="headline">{translate(quadrantName)}</h4>
|
||||
</div>
|
||||
{!big && (
|
||||
<div className='split__right'>
|
||||
<Link className='icon-link' pageName={`${quadrantName}`}>
|
||||
<span className='icon icon--pie icon-link__icon' />
|
||||
<div className="split__right">
|
||||
<Link className="icon-link" pageName={`${quadrantName}`}>
|
||||
<span className="icon icon--pie icon-link__icon" />
|
||||
Zoom In
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className='quadrant-section__rings'>{rings.map((ringName) => renderRing(ringName, quadrantName, groups, big))}</div>
|
||||
<div className="quadrant-section__rings">
|
||||
{rings.map((ringName) =>
|
||||
renderRing(ringName, quadrantName, groups, big)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.quadrant-section {
|
||||
&__header {
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
@media (max-width: $until-md) {
|
||||
flex-basis: 50%;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,91 +1,137 @@
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import PageIndex from './PageIndex/PageIndex';
|
||||
import PageOverview from './PageOverview/PageOverview';
|
||||
import PageHelp from './PageHelp/PageHelp';
|
||||
import PageQuadrant from './PageQuadrant/PageQuadrant';
|
||||
import PageItem from './PageItem/PageItem';
|
||||
import PageItemMobile from './PageItemMobile/PageItemMobile';
|
||||
import {quadrants, getItemPageNames, isMobileViewport, rings} from '../config';
|
||||
import {Item} from '../model';
|
||||
import React, { useState, useEffect } from "react";
|
||||
import PageIndex from "./PageIndex/PageIndex";
|
||||
import PageOverview from "./PageOverview/PageOverview";
|
||||
import PageHelp from "./PageHelp/PageHelp";
|
||||
import PageQuadrant from "./PageQuadrant/PageQuadrant";
|
||||
import PageItem from "./PageItem/PageItem";
|
||||
import PageItemMobile from "./PageItemMobile/PageItemMobile";
|
||||
import {
|
||||
quadrants,
|
||||
getItemPageNames,
|
||||
isMobileViewport,
|
||||
rings,
|
||||
} from "../config";
|
||||
import { Item } from "../model";
|
||||
|
||||
type RouterProps = {
|
||||
pageName: string
|
||||
items: Item[]
|
||||
releases: string[]
|
||||
search: string
|
||||
}
|
||||
pageName: string;
|
||||
items: Item[];
|
||||
releases: string[];
|
||||
search: string;
|
||||
};
|
||||
|
||||
enum page {
|
||||
index,
|
||||
overview,
|
||||
help,
|
||||
quadrant,
|
||||
itemMobile,
|
||||
item,
|
||||
notFound,
|
||||
index,
|
||||
overview,
|
||||
help,
|
||||
quadrant,
|
||||
itemMobile,
|
||||
item,
|
||||
notFound,
|
||||
}
|
||||
|
||||
const getPageByName = (items: Item[], pageName: string): page => {
|
||||
if (pageName === 'index') {
|
||||
return page.index;
|
||||
}
|
||||
if (pageName === 'overview') {
|
||||
return page.overview;
|
||||
}
|
||||
if (pageName === 'help-and-about-tech-radar') {
|
||||
return page.help;
|
||||
}
|
||||
if (quadrants.includes(pageName)) {
|
||||
return page.quadrant;
|
||||
}
|
||||
if (getItemPageNames(items).includes(pageName)) {
|
||||
return isMobileViewport() ? page.itemMobile : page.item;
|
||||
}
|
||||
if (pageName === "index") {
|
||||
return page.index;
|
||||
}
|
||||
if (pageName === "overview") {
|
||||
return page.overview;
|
||||
}
|
||||
if (pageName === "help-and-about-tech-radar") {
|
||||
return page.help;
|
||||
}
|
||||
if (quadrants.includes(pageName)) {
|
||||
return page.quadrant;
|
||||
}
|
||||
if (getItemPageNames(items).includes(pageName)) {
|
||||
return isMobileViewport() ? page.itemMobile : page.item;
|
||||
}
|
||||
|
||||
return page.notFound;
|
||||
return page.notFound;
|
||||
};
|
||||
|
||||
export default function Router({pageName, items, releases, search}: RouterProps) {
|
||||
const [statePageName, setStatePageName] = useState(pageName);
|
||||
const [leaving, setLeaving] = useState(false);
|
||||
const [nextPageName, setNextPageName] = useState<string>('');
|
||||
export default function Router({
|
||||
pageName,
|
||||
items,
|
||||
releases,
|
||||
search,
|
||||
}: RouterProps) {
|
||||
const [statePageName, setStatePageName] = useState(pageName);
|
||||
const [leaving, setLeaving] = useState(false);
|
||||
const [nextPageName, setNextPageName] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
const nowLeaving = getPageByName(items, pageName) !== getPageByName(items, statePageName);
|
||||
if (nowLeaving) {
|
||||
setLeaving(true);
|
||||
setNextPageName(pageName);
|
||||
} else {
|
||||
setStatePageName(pageName);
|
||||
}
|
||||
}, [pageName, items, statePageName]);
|
||||
|
||||
const handlePageLeave = () => {
|
||||
setStatePageName(nextPageName);
|
||||
setNextPageName('');
|
||||
|
||||
window.setTimeout(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
setLeaving(false);
|
||||
});
|
||||
}, 0);
|
||||
};
|
||||
|
||||
switch (getPageByName(items, statePageName)) {
|
||||
case page.index:
|
||||
return <PageIndex leaving={leaving} items={items} onLeave={handlePageLeave} releases={releases}/>;
|
||||
case page.overview:
|
||||
return <PageOverview items={items} rings={rings} search={search} leaving={leaving}
|
||||
onLeave={handlePageLeave}/>;
|
||||
case page.help:
|
||||
return <PageHelp leaving={leaving} onLeave={handlePageLeave}/>;
|
||||
case page.quadrant:
|
||||
return <PageQuadrant leaving={leaving} onLeave={handlePageLeave} items={items} pageName={statePageName}/>;
|
||||
case page.itemMobile:
|
||||
return <PageItemMobile items={items} pageName={statePageName} leaving={leaving} onLeave={handlePageLeave}/>;
|
||||
case page.item:
|
||||
return <PageItem items={items} pageName={statePageName} leaving={leaving} onLeave={handlePageLeave}/>;
|
||||
default:
|
||||
return <div/>;
|
||||
useEffect(() => {
|
||||
const nowLeaving =
|
||||
getPageByName(items, pageName) !== getPageByName(items, statePageName);
|
||||
if (nowLeaving) {
|
||||
setLeaving(true);
|
||||
setNextPageName(pageName);
|
||||
} else {
|
||||
setStatePageName(pageName);
|
||||
}
|
||||
}, [pageName, items, statePageName]);
|
||||
|
||||
const handlePageLeave = () => {
|
||||
setStatePageName(nextPageName);
|
||||
setNextPageName("");
|
||||
|
||||
window.setTimeout(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
setLeaving(false);
|
||||
});
|
||||
}, 0);
|
||||
};
|
||||
|
||||
switch (getPageByName(items, statePageName)) {
|
||||
case page.index:
|
||||
return (
|
||||
<PageIndex
|
||||
leaving={leaving}
|
||||
items={items}
|
||||
onLeave={handlePageLeave}
|
||||
releases={releases}
|
||||
/>
|
||||
);
|
||||
case page.overview:
|
||||
return (
|
||||
<PageOverview
|
||||
items={items}
|
||||
rings={rings}
|
||||
search={search}
|
||||
leaving={leaving}
|
||||
onLeave={handlePageLeave}
|
||||
/>
|
||||
);
|
||||
case page.help:
|
||||
return <PageHelp leaving={leaving} onLeave={handlePageLeave} />;
|
||||
case page.quadrant:
|
||||
return (
|
||||
<PageQuadrant
|
||||
leaving={leaving}
|
||||
onLeave={handlePageLeave}
|
||||
items={items}
|
||||
pageName={statePageName}
|
||||
/>
|
||||
);
|
||||
case page.itemMobile:
|
||||
return (
|
||||
<PageItemMobile
|
||||
items={items}
|
||||
pageName={statePageName}
|
||||
leaving={leaving}
|
||||
onLeave={handlePageLeave}
|
||||
/>
|
||||
);
|
||||
case page.item:
|
||||
return (
|
||||
<PageItem
|
||||
items={items}
|
||||
pageName={statePageName}
|
||||
leaving={leaving}
|
||||
onLeave={handlePageLeave}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { FormEvent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import './search.scss';
|
||||
import React, { FormEvent } from "react";
|
||||
import classNames from "classnames";
|
||||
import "./search.scss";
|
||||
|
||||
type SearchProps = {
|
||||
onClose?: () => void;
|
||||
@@ -14,7 +14,10 @@ export default React.forwardRef((props: SearchProps, ref) => {
|
||||
return Search(props, ref);
|
||||
});
|
||||
|
||||
function Search({ value, onChange, onClose, open = false, onSubmit = () => {} }: SearchProps, ref: any) {
|
||||
function Search(
|
||||
{ value, onChange, onClose, open = false, onSubmit = () => {} }: SearchProps,
|
||||
ref: any
|
||||
) {
|
||||
const closable = onClose !== undefined;
|
||||
|
||||
const handleSubmit = (e: FormEvent) => {
|
||||
@@ -30,26 +33,34 @@ function Search({ value, onChange, onClose, open = false, onSubmit = () => {} }:
|
||||
};
|
||||
|
||||
return (
|
||||
<form className={classNames('search', { 'search--closable': closable })} onSubmit={handleSubmit}>
|
||||
<form
|
||||
className={classNames("search", { "search--closable": closable })}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<input
|
||||
value={value}
|
||||
type='text'
|
||||
type="text"
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
className='search__field'
|
||||
placeholder='What are you looking for?'
|
||||
className="search__field"
|
||||
placeholder="What are you looking for?"
|
||||
ref={ref}
|
||||
/>
|
||||
<span className={classNames('search__button', { 'is-open': open })}>
|
||||
<button type='submit' className='button'>
|
||||
<span className='icon icon--search button__icon' />
|
||||
<span className={classNames("search__button", { "is-open": open })}>
|
||||
<button type="submit" className="button">
|
||||
<span className="icon icon--search button__icon" />
|
||||
Search
|
||||
</button>
|
||||
</span>
|
||||
{closable && (
|
||||
<button className={classNames('search__close link-button', { 'is-open': open })} onClick={handleClose}>
|
||||
<span className='icon icon--close' />
|
||||
<button
|
||||
className={classNames("search__close link-button", {
|
||||
"is-open": open,
|
||||
})}
|
||||
onClick={handleClose}
|
||||
>
|
||||
<span className="icon icon--close" />
|
||||
</button>
|
||||
)}
|
||||
</form>
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 10px 120px 10px 20px;
|
||||
background: #3A444A;
|
||||
background: #3a444a;
|
||||
display: block;
|
||||
border: none;
|
||||
color: var(--color-white);
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
font-family: 'DIN';
|
||||
font-family: "DIN";
|
||||
font-weight: normal;
|
||||
|
||||
&::placeholder {
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
background: #2F393F;
|
||||
background: #2f393f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {useEffect} from "react";
|
||||
import {radarName} from "../config";
|
||||
import { useEffect } from "react";
|
||||
import { radarName } from "../config";
|
||||
|
||||
type SetTitleProps = {
|
||||
title: string
|
||||
}
|
||||
title: string;
|
||||
};
|
||||
|
||||
export default function SetTitle({title}: SetTitleProps) {
|
||||
export default function SetTitle({ title }: SetTitleProps) {
|
||||
useEffect(() => {
|
||||
document.title = `${title} | ${radarName}`
|
||||
}, [title])
|
||||
document.title = `${title} | ${radarName}`;
|
||||
}, [title]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import moment from 'moment';
|
||||
import moment from "moment";
|
||||
|
||||
const isoDateToMoment = (isoDate: moment.MomentInput) => moment(isoDate, 'YYYY-MM-DD');
|
||||
const isoDateToMoment = (isoDate: moment.MomentInput) =>
|
||||
moment(isoDate, "YYYY-MM-DD");
|
||||
|
||||
export const formatRelease = (isoDate: moment.MomentInput) => isoDateToMoment(isoDate).format('MMMM YYYY');
|
||||
export const formatRelease = (isoDate: moment.MomentInput) =>
|
||||
isoDateToMoment(isoDate).format("MMMM YYYY");
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
@font-face {
|
||||
font-family: 'DIN';
|
||||
src: url('./fonts/clanot-news.otf');
|
||||
font-family: "DIN";
|
||||
src: url("./fonts/clanot-news.otf");
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DIN';
|
||||
src: url('./fonts/clanot-thin.otf');
|
||||
font-family: "DIN";
|
||||
src: url("./fonts/clanot-thin.otf");
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
|
||||
:root {
|
||||
--color-gray-dark: #475157;
|
||||
--color-gray-dark-alt: #4F585E;
|
||||
--color-gray-dark-alt2: #434D53;
|
||||
--color-gray-dark-alt: #4f585e;
|
||||
--color-gray-dark-alt2: #434d53;
|
||||
--color-gray-normal: #7f858a;
|
||||
--color-gray-light: #7D878D;
|
||||
--color-gray-light: #7d878d;
|
||||
|
||||
--color-white: #fff;
|
||||
--color-green: #5CB449;
|
||||
--color-orange: #FAA03D;
|
||||
--color-blue: #40A7D1;
|
||||
--color-green: #5cb449;
|
||||
--color-orange: #faa03d;
|
||||
--color-blue: #40a7d1;
|
||||
--color-marine: #688190;
|
||||
--color-red: #F1235A;
|
||||
--color-brand: #F59134;
|
||||
--color-red: #f1235a;
|
||||
--color-brand: #f59134;
|
||||
--until-sm: 30em;
|
||||
--until-md: 48em;
|
||||
--until-lg: 61.875em;
|
||||
@@ -36,14 +35,12 @@
|
||||
// @custom-media --until-lg (max-width: 61.875em);
|
||||
// @custom-media --until-xl (max-width: 75em);
|
||||
|
||||
|
||||
|
||||
body {
|
||||
background: var(--color-gray-dark);
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'DIN';
|
||||
font-family: "DIN";
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
@@ -58,4 +55,4 @@ body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@import './styles/main.scss';
|
||||
@import "./styles/main.scss";
|
||||
|
||||
96
src/model.js
96
src/model.js
@@ -1,49 +1,85 @@
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
var __assign =
|
||||
(this && this.__assign) ||
|
||||
function () {
|
||||
__assign =
|
||||
Object.assign ||
|
||||
function (t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
s = arguments[i];
|
||||
for (var p in s)
|
||||
if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
|
||||
};
|
||||
var __spreadArray =
|
||||
(this && this.__spreadArray) ||
|
||||
function (to, from) {
|
||||
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
|
||||
to[j] = from[i];
|
||||
to[j] = from[i];
|
||||
return to;
|
||||
};
|
||||
export var featuredOnly = function (items) {
|
||||
return items.filter(function (item) {
|
||||
return item.featured;
|
||||
});
|
||||
};
|
||||
export var featuredOnly = function (items) { return items.filter(function (item) { return item.featured; }); };
|
||||
export var groupByQuadrants = function (items) {
|
||||
return items.reduce(function (quadrants, item) {
|
||||
var _a;
|
||||
return (__assign(__assign({}, quadrants), (_a = {}, _a[item.quadrant] = addItemToQuadrant(quadrants[item.quadrant], item), _a)));
|
||||
}, {});
|
||||
return items.reduce(function (quadrants, item) {
|
||||
var _a;
|
||||
return __assign(
|
||||
__assign({}, quadrants),
|
||||
((_a = {}),
|
||||
(_a[item.quadrant] = addItemToQuadrant(quadrants[item.quadrant], item)),
|
||||
_a)
|
||||
);
|
||||
}, {});
|
||||
};
|
||||
export var groupByFirstLetter = function (items) {
|
||||
var index = items.reduce(function (letterIndex, item) {
|
||||
var _a;
|
||||
return (__assign(__assign({}, letterIndex), (_a = {}, _a[getFirstLetter(item)] = addItemToList(letterIndex[getFirstLetter(item)], item), _a)));
|
||||
}, {});
|
||||
return Object.keys(index)
|
||||
.sort()
|
||||
.map(function (letter) { return ({
|
||||
var index = items.reduce(function (letterIndex, item) {
|
||||
var _a;
|
||||
return __assign(
|
||||
__assign({}, letterIndex),
|
||||
((_a = {}),
|
||||
(_a[getFirstLetter(item)] = addItemToList(
|
||||
letterIndex[getFirstLetter(item)],
|
||||
item
|
||||
)),
|
||||
_a)
|
||||
);
|
||||
}, {});
|
||||
return Object.keys(index)
|
||||
.sort()
|
||||
.map(function (letter) {
|
||||
return {
|
||||
letter: letter,
|
||||
items: index[letter],
|
||||
}); });
|
||||
};
|
||||
});
|
||||
};
|
||||
var addItemToQuadrant = function (quadrant, item) {
|
||||
var _a;
|
||||
if (quadrant === void 0) { quadrant = {}; }
|
||||
return (__assign(__assign({}, quadrant), (_a = {}, _a[item.ring] = addItemToRing(quadrant[item.ring], item), _a)));
|
||||
var _a;
|
||||
if (quadrant === void 0) {
|
||||
quadrant = {};
|
||||
}
|
||||
return __assign(
|
||||
__assign({}, quadrant),
|
||||
((_a = {}), (_a[item.ring] = addItemToRing(quadrant[item.ring], item)), _a)
|
||||
);
|
||||
};
|
||||
var addItemToList = function (list, item) {
|
||||
if (list === void 0) { list = []; }
|
||||
return __spreadArray(__spreadArray([], list), [item]);
|
||||
if (list === void 0) {
|
||||
list = [];
|
||||
}
|
||||
return __spreadArray(__spreadArray([], list), [item]);
|
||||
};
|
||||
var addItemToRing = function (ring, item) {
|
||||
if (ring === void 0) { ring = []; }
|
||||
return __spreadArray(__spreadArray([], ring), [item]);
|
||||
if (ring === void 0) {
|
||||
ring = [];
|
||||
}
|
||||
return __spreadArray(__spreadArray([], ring), [item]);
|
||||
};
|
||||
export var getFirstLetter = function (item) {
|
||||
return item.title.substr(0, 1).toUpperCase();
|
||||
};
|
||||
export var getFirstLetter = function (item) { return item.title.substr(0, 1).toUpperCase(); };
|
||||
|
||||
64
src/model.ts
64
src/model.ts
@@ -1,43 +1,44 @@
|
||||
import { Ring } from "./config"
|
||||
import { Ring } from "./config";
|
||||
|
||||
export type ItemAttributes = {
|
||||
name: string
|
||||
ring: Ring
|
||||
quadrant: string
|
||||
title: string
|
||||
featured: boolean
|
||||
}
|
||||
name: string;
|
||||
ring: Ring;
|
||||
quadrant: string;
|
||||
title: string;
|
||||
featured: boolean;
|
||||
};
|
||||
|
||||
export type FlagType = 'new' | 'changed' | 'default'
|
||||
export type FlagType = "new" | "changed" | "default";
|
||||
|
||||
export type Item = ItemAttributes & {
|
||||
featured: boolean
|
||||
body: string
|
||||
info: string
|
||||
flag: FlagType
|
||||
revisions: Revision[]
|
||||
}
|
||||
featured: boolean;
|
||||
body: string;
|
||||
info: string;
|
||||
flag: FlagType;
|
||||
revisions: Revision[];
|
||||
};
|
||||
|
||||
export type Revision = ItemAttributes & {
|
||||
body: string
|
||||
fileName: string
|
||||
release: string
|
||||
}
|
||||
body: string;
|
||||
fileName: string;
|
||||
release: string;
|
||||
};
|
||||
|
||||
export type Quadrant = {
|
||||
[name: string]: Item[]
|
||||
}
|
||||
[name: string]: Item[];
|
||||
};
|
||||
|
||||
export type Radar = {
|
||||
items: Item[]
|
||||
releases: string[]
|
||||
}
|
||||
items: Item[];
|
||||
releases: string[];
|
||||
};
|
||||
|
||||
export type Group = {
|
||||
[quadrant: string]: Quadrant
|
||||
}
|
||||
[quadrant: string]: Quadrant;
|
||||
};
|
||||
|
||||
export const featuredOnly = (items: Item[]) => items.filter(item => item.featured);
|
||||
export const featuredOnly = (items: Item[]) =>
|
||||
items.filter((item) => item.featured);
|
||||
|
||||
export const groupByQuadrants = (items: Item[]): Group =>
|
||||
items.reduce(
|
||||
@@ -45,7 +46,7 @@ export const groupByQuadrants = (items: Item[]): Group =>
|
||||
...quadrants,
|
||||
[item.quadrant]: addItemToQuadrant(quadrants[item.quadrant], item),
|
||||
}),
|
||||
{} as {[k: string]: Quadrant},
|
||||
{} as { [k: string]: Quadrant }
|
||||
);
|
||||
|
||||
export const groupByFirstLetter = (items: Item[]) => {
|
||||
@@ -54,15 +55,15 @@ export const groupByFirstLetter = (items: Item[]) => {
|
||||
...letterIndex,
|
||||
[getFirstLetter(item)]: addItemToList(
|
||||
letterIndex[getFirstLetter(item)],
|
||||
item,
|
||||
item
|
||||
),
|
||||
}),
|
||||
{} as {[k: string]: Item[]},
|
||||
{} as { [k: string]: Item[] }
|
||||
);
|
||||
|
||||
return Object.keys(index)
|
||||
.sort()
|
||||
.map(letter => ({
|
||||
.map((letter) => ({
|
||||
letter,
|
||||
items: index[letter],
|
||||
}));
|
||||
@@ -77,4 +78,5 @@ const addItemToList = (list: Item[] = [], item: Item) => [...list, item];
|
||||
|
||||
const addItemToRing = (ring: Item[] = [], item: Item) => [...ring, item];
|
||||
|
||||
export const getFirstLetter = (item: Item) => item.title.substr(0, 1).toUpperCase();
|
||||
export const getFirstLetter = (item: Item) =>
|
||||
item.title.substr(0, 1).toUpperCase();
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import "@testing-library/jest-dom";
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
.fullpage-content {
|
||||
font-size: 16px;
|
||||
color: var(--color-white);
|
||||
|
||||
font-size: 16px;
|
||||
& p,
|
||||
& ul {
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
& a {
|
||||
color: var(--color-white);
|
||||
|
||||
& p,
|
||||
& ul {
|
||||
font-weight: lighter
|
||||
}
|
||||
|
||||
& a {
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.filter {
|
||||
margin-bottom: 40px;
|
||||
@@ -7,4 +7,4 @@
|
||||
@media (max-width: $until-sm) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.footnote {
|
||||
font-size: 12px;
|
||||
color: var(--color-gray-normal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,61 @@
|
||||
/* copied from here: http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.10.0/styles/darcula.min.css*/
|
||||
.hljs{display:block;overflow-x:auto;padding:0.5em;background:#2b2b2b}.hljs{color:#bababa}.hljs-strong,.hljs-emphasis{color:#a8a8a2}.hljs-bullet,.hljs-quote,.hljs-link,.hljs-number,.hljs-regexp,.hljs-literal{color:#6896ba}.hljs-code,.hljs-selector-class{color:#a6e22e}.hljs-emphasis{font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-section,.hljs-attribute,.hljs-name,.hljs-variable{color:#cb7832}.hljs-params{color:#b9b9b9}.hljs-string{color:#6a8759}.hljs-subst,.hljs-type,.hljs-built_in,.hljs-builtin-name,.hljs-symbol,.hljs-selector-id,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-template-tag,.hljs-template-variable,.hljs-addition{color:#e0c46c}.hljs-comment,.hljs-deletion,.hljs-meta{color:#7f7f7f}
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #2b2b2b;
|
||||
}
|
||||
.hljs {
|
||||
color: #bababa;
|
||||
}
|
||||
.hljs-strong,
|
||||
.hljs-emphasis {
|
||||
color: #a8a8a2;
|
||||
}
|
||||
.hljs-bullet,
|
||||
.hljs-quote,
|
||||
.hljs-link,
|
||||
.hljs-number,
|
||||
.hljs-regexp,
|
||||
.hljs-literal {
|
||||
color: #6896ba;
|
||||
}
|
||||
.hljs-code,
|
||||
.hljs-selector-class {
|
||||
color: #a6e22e;
|
||||
}
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-section,
|
||||
.hljs-attribute,
|
||||
.hljs-name,
|
||||
.hljs-variable {
|
||||
color: #cb7832;
|
||||
}
|
||||
.hljs-params {
|
||||
color: #b9b9b9;
|
||||
}
|
||||
.hljs-string {
|
||||
color: #6a8759;
|
||||
}
|
||||
.hljs-subst,
|
||||
.hljs-type,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-symbol,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-template-tag,
|
||||
.hljs-template-variable,
|
||||
.hljs-addition {
|
||||
color: #e0c46c;
|
||||
}
|
||||
.hljs-comment,
|
||||
.hljs-deletion,
|
||||
.hljs-meta {
|
||||
color: #7f7f7f;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
&:hover {
|
||||
&:after {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -8px;
|
||||
@@ -33,4 +33,4 @@
|
||||
border-bottom: 2px solid var(--color-gray-normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,26 +8,26 @@
|
||||
vertical-align: middle;
|
||||
|
||||
&--pie {
|
||||
background-image: url('../../icons/pie.svg');
|
||||
background-image: url("../../icons/pie.svg");
|
||||
}
|
||||
|
||||
&--question {
|
||||
background-image: url('../../icons/question.svg');
|
||||
background-image: url("../../icons/question.svg");
|
||||
}
|
||||
|
||||
&--overview {
|
||||
background-image: url('../../icons/overview.svg');
|
||||
background-image: url("../../icons/overview.svg");
|
||||
}
|
||||
|
||||
&--search {
|
||||
background-image: url('../../icons/search.svg');
|
||||
background-image: url("../../icons/search.svg");
|
||||
}
|
||||
|
||||
&--back {
|
||||
background-image: url('../../icons/back.svg');
|
||||
background-image: url("../../icons/back.svg");
|
||||
}
|
||||
|
||||
&--close {
|
||||
background-image: url('../../icons/close.svg');
|
||||
background-image: url("../../icons/close.svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.letter-index {
|
||||
margin-bottom: 60px;
|
||||
@@ -26,4 +26,4 @@
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.nav {
|
||||
white-space: nowrap;
|
||||
@@ -7,7 +7,7 @@
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
|
||||
&+.nav__item {
|
||||
& + .nav__item {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,8 @@
|
||||
width: 0;
|
||||
margin-top: -25px;
|
||||
opacity: 0.8;
|
||||
transition:
|
||||
width 400ms cubic-bezier(0.24, 1.12, 0.71, 0.98) 100ms,
|
||||
visibility 0s linear 500ms,
|
||||
opacity 200ms linear;
|
||||
transition: width 400ms cubic-bezier(0.24, 1.12, 0.71, 0.98) 100ms,
|
||||
visibility 0s linear 500ms, opacity 200ms linear;
|
||||
|
||||
&.is-open {
|
||||
opacity: 1;
|
||||
@@ -42,4 +40,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.page {
|
||||
max-width: 1200px;
|
||||
@@ -16,7 +16,7 @@
|
||||
z-index: 100;
|
||||
|
||||
@media (max-width: $until-sm) {
|
||||
margin: 0
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media (max-width: $until-lg) {
|
||||
@@ -34,4 +34,4 @@
|
||||
margin-top: 5px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.publish-date {
|
||||
color: var(--color-gray-normal);
|
||||
@@ -7,4 +7,4 @@
|
||||
@media (max-width: $until-sm) {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
|
||||
.social-icon {
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 16px; /* Preferred icon size */
|
||||
font-size: 16px; /* Preferred icon size */
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
@@ -24,6 +23,5 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Support for IE. */
|
||||
font-feature-settings: 'liga';
|
||||
|
||||
}
|
||||
font-feature-settings: "liga";
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../../styles/sccs-vars.scss';
|
||||
@import "../../styles/sccs-vars.scss";
|
||||
|
||||
.split {
|
||||
display: flex;
|
||||
@@ -24,4 +24,4 @@
|
||||
@media (max-width: $until-sm) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
@import "./components/publish-date";
|
||||
@import "./components/ring-list";
|
||||
@import "./components/social-icon";
|
||||
@import "./components/split";
|
||||
@import "./components/split";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
$until-sm: 30em;
|
||||
$until-md: 48em;
|
||||
$until-lg: 61.875em;
|
||||
$until-xl: 75em;
|
||||
$until-xl: 75em;
|
||||
|
||||
Reference in New Issue
Block a user