create symlink for node_modules in generateJson and buildRadar scripts
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,6 +11,9 @@
|
|||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
|
|
||||||
|
# local development
|
||||||
|
/public/rd.json
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.idea
|
.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
yarn ts:check && yarn lint-staged
|
yarn ts:check && yarn lint-staged && yarn build:scripts
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"*.{json, md, yml, js, ts, tsx}": [
|
|
||||||
"prettier --write",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
3
.lintstagedrc.json
Normal file
3
.lintstagedrc.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"*.{json, md, yml, js, ts, tsx}": ["prettier --write"]
|
||||||
|
}
|
||||||
42
README.md
42
README.md
@@ -1,36 +1,45 @@
|
|||||||
# AOE Technology Radar
|
# AOE Technology Radar
|
||||||
|
|
||||||
A static site generator for AOE Technology Radar
|
A static site generator for AOE Technology Radar
|
||||||
|
|
||||||
## Looking for the AOE Tech Radar content?
|
## Looking for the AOE Tech Radar content?
|
||||||
|
|
||||||
The repository is now found here: https://github.com/AOEpeople/techradar
|
The repository is now found here: https://github.com/AOEpeople/techradar
|
||||||
|
|
||||||
The AOE Tech radar is deployed here: https://www.aoe.com/techradar/index.html
|
The AOE Tech radar is deployed here: https://www.aoe.com/techradar/index.html
|
||||||
|
|
||||||
## Usage for your own radar?
|
## Usage for your own radar?
|
||||||
|
|
||||||
The generator is free to use under Open Source License - in fact there are already some other Radars published based on our Radar and there are also Contributions back.
|
The generator is free to use under Open Source License - in fact there are already some other Radars published based on our Radar and there are also Contributions back.
|
||||||
(There is a list of planned features below in case someone wants to contribute :-)
|
(There is a list of planned features below in case someone wants to contribute :-)
|
||||||
|
|
||||||
However, please be aware:
|
However, please be aware:
|
||||||
* It would be nice to mention in radar that the generator is based on this repository.
|
|
||||||
* Also, when you want to reuse the CSS and Styling: Change the font (it is a licensed font) and the colors (It using AOE CI)
|
- It would be nice to mention in radar that the generator is based on this repository.
|
||||||
|
- Also, when you want to reuse the CSS and Styling: Change the font (it is a licensed font) and the colors (It using AOE CI)
|
||||||
|
|
||||||
## Use and build
|
## Use and build
|
||||||
|
|
||||||
Add the tech radar as a dependency
|
Add the tech radar as a dependency
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn add https://github.com/aoepeople/aoe_technology_radar.git
|
yarn add https://github.com/aoepeople/aoe_technology_radar.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Generate json file based on md files
|
Generate json file based on md files
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn aoe_technology_radar-generateJson
|
yarn aoe_technology_radar-generateJson
|
||||||
```
|
```
|
||||||
|
|
||||||
Build the radar
|
Build the radar
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn aoe_technology_radar-buildRadar
|
yarn aoe_technology_radar-buildRadar
|
||||||
```
|
```
|
||||||
|
|
||||||
Serve
|
Serve
|
||||||
|
|
||||||
```
|
```
|
||||||
cd build
|
cd build
|
||||||
python3 -m http.server 8080
|
python3 -m http.server 8080
|
||||||
@@ -39,6 +48,7 @@ python3 -m http.server 8080
|
|||||||
Then open here: http://localhost:8080
|
Then open here: http://localhost:8080
|
||||||
|
|
||||||
## Run a prepared static version
|
## Run a prepared static version
|
||||||
|
|
||||||
In most cases you have the tech radar available at `/techradar`, and for reasons want all correct pages to be accessible.
|
In most cases you have the tech radar available at `/techradar`, and for reasons want all correct pages to be accessible.
|
||||||
|
|
||||||
Until this setup improves, you can use the following way to generate the correct tech radar:
|
Until this setup improves, you can use the following way to generate the correct tech radar:
|
||||||
@@ -53,31 +63,39 @@ cp -r build techradar
|
|||||||
(This is rather workaroundish for now, but does the job.)
|
(This is rather workaroundish for now, but does the job.)
|
||||||
|
|
||||||
## Customize the tech radar
|
## Customize the tech radar
|
||||||
|
|
||||||
You can customize the following parts of the tech radar.
|
You can customize the following parts of the tech radar.
|
||||||
|
|
||||||
### Change title, description and headline
|
### Change title, description and headline
|
||||||
|
|
||||||
Set the environment variable `REACT_APP_RADAR_NAME`. The default is "AOE Technology Radar".
|
Set the environment variable `REACT_APP_RADAR_NAME`. The default is "AOE Technology Radar".
|
||||||
|
|
||||||
### Host the application under a sub path
|
### Host the application under a sub path
|
||||||
|
|
||||||
To host the application under a sub path, set the environment variable `PUBLIC_URL`, e.g. "/techradar".
|
To host the application under a sub path, set the environment variable `PUBLIC_URL`, e.g. "/techradar".
|
||||||
|
|
||||||
### Change the favicon
|
### Change the favicon
|
||||||
|
|
||||||
To change the favicon, create a public folder in your application and put your favicon.ico in it.
|
To change the favicon, create a public folder in your application and put your favicon.ico in it.
|
||||||
|
|
||||||
### Change the logo
|
### Change the logo
|
||||||
|
|
||||||
To change the logo, create a public folder in your application and put your logo.svg in it.
|
To change the logo, create a public folder in your application and put your logo.svg in it.
|
||||||
For reference have a look at [public/logo.svg](./public/logo.svg).
|
For reference have a look at [public/logo.svg](./public/logo.svg).
|
||||||
|
|
||||||
### Change the index.html
|
### Change the index.html
|
||||||
|
|
||||||
To change the index.html, create a public folder in your application and put your index.html in it.
|
To change the index.html, create a public folder in your application and put your index.html in it.
|
||||||
For reference have a look at [public/index.html](./public/index.html).
|
For reference have a look at [public/index.html](./public/index.html).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
For a new Technology Radar release, create a folder of the release date
|
For a new Technology Radar release, create a folder of the release date
|
||||||
(YYYY-MM-DD) under `/radar`. In each release folder create a folder for every
|
(YYYY-MM-DD) under `/radar`. In each release folder create a folder for every
|
||||||
quadrant and place the items there.
|
quadrant and place the items there.
|
||||||
|
|
||||||
### Maintaining items
|
### Maintaining items
|
||||||
|
|
||||||
The items are written in Markdown format (.md)
|
The items are written in Markdown format (.md)
|
||||||
|
|
||||||
Each file has a [front-matter](https://github.com/jxson/front-matter) header
|
Each file has a [front-matter](https://github.com/jxson/front-matter) header
|
||||||
@@ -95,13 +113,13 @@ Text goes here. You can use **markdown** here.
|
|||||||
|
|
||||||
Following front-matter attributes are possible:
|
Following front-matter attributes are possible:
|
||||||
|
|
||||||
* **title**: Name of the Item
|
- **title**: Name of the Item
|
||||||
* **quadrant**: Quadrant. One of `languages-and-frameworks`,
|
- **quadrant**: Quadrant. One of `languages-and-frameworks`,
|
||||||
`methods-and-patterns`, `platforms-and-aoe-services`, `tools`
|
`methods-and-patterns`, `platforms-and-aoe-services`, `tools`
|
||||||
* **ring**: Ring section in radar. One of `trial`, `assess`, `adopt`, `hold`
|
- **ring**: Ring section in radar. One of `trial`, `assess`, `adopt`, `hold`
|
||||||
* **info**: (optional) A short textual description of the item (visible in
|
- **info**: (optional) A short textual description of the item (visible in
|
||||||
overview pages)
|
overview pages)
|
||||||
* **featured**: (optional, default "true") If you set this to `false`, the item
|
- **featured**: (optional, default "true") If you set this to `false`, the item
|
||||||
will not be visible in the radar quadrants but still be available in the overview.
|
will not be visible in the radar quadrants but still be available in the overview.
|
||||||
|
|
||||||
The name of the .md file acts as item identifier and may overwrite items with
|
The name of the .md file acts as item identifier and may overwrite items with
|
||||||
@@ -111,11 +129,21 @@ If an item is overwritten in a new release, the attributes from the new item are
|
|||||||
merged with the old ones, and a new history entry is created for that item.
|
merged with the old ones, and a new history entry is created for that item.
|
||||||
|
|
||||||
You can integrate images in your markdown. Put the image files in your public folder and reference them
|
You can integrate images in your markdown. Put the image files in your public folder and reference them
|
||||||
|
|
||||||
```
|
```
|
||||||

|

|
||||||
```
|
```
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
For local development you need a `rd.json` in the public folder. You can use `rd_example.json`.
|
||||||
|
Then simply start the dev server
|
||||||
|
```
|
||||||
|
yarn start
|
||||||
|
```
|
||||||
|
|
||||||
### Change scripts
|
### Change scripts
|
||||||
|
|
||||||
If you change one of the scripts in the scripts' folder, you have to compile them to JavaScript.
|
If you change one of the scripts in the scripts' folder, you have to compile them to JavaScript.
|
||||||
Therefore, run `yarn build:scripts` and commit the results in dist_scripts.
|
Therefore, run `yarn build:scripts` and commit the results in dist_scripts.
|
||||||
|
|
||||||
|
To make it more robust the script will be executed on commit.
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from) {
|
|||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.getFirstLetter = exports.groupByFirstLetter = exports.groupByQuadrants = exports.featuredOnly = void 0;
|
exports.getFirstLetter = exports.groupByFirstLetter = exports.groupByQuadrants = exports.featuredOnly = void 0;
|
||||||
var featuredOnly = function (items) { return items.filter(function (item) { return item.featured; }); };
|
var featuredOnly = function (items) {
|
||||||
|
return items.filter(function (item) { return item.featured; });
|
||||||
|
};
|
||||||
exports.featuredOnly = featuredOnly;
|
exports.featuredOnly = featuredOnly;
|
||||||
var groupByQuadrants = function (items) {
|
var groupByQuadrants = function (items) {
|
||||||
return items.reduce(function (quadrants, item) {
|
return items.reduce(function (quadrants, item) {
|
||||||
@@ -52,5 +54,7 @@ var addItemToRing = function (ring, item) {
|
|||||||
if (ring === void 0) { ring = []; }
|
if (ring === void 0) { ring = []; }
|
||||||
return __spreadArray(__spreadArray([], ring), [item]);
|
return __spreadArray(__spreadArray([], ring), [item]);
|
||||||
};
|
};
|
||||||
var getFirstLetter = function (item) { return item.title.substr(0, 1).toUpperCase(); };
|
var getFirstLetter = function (item) {
|
||||||
|
return item.title.substr(0, 1).toUpperCase();
|
||||||
|
};
|
||||||
exports.getFirstLetter = getFirstLetter;
|
exports.getFirstLetter = getFirstLetter;
|
||||||
|
|||||||
@@ -10,13 +10,18 @@
|
|||||||
<meta property="og:image" content="%PUBLIC_URL%/logo.svg" />
|
<meta property="og:image" content="%PUBLIC_URL%/logo.svg" />
|
||||||
|
|
||||||
<meta name="format-detection" content="telephone=no" />
|
<meta name="format-detection" content="telephone=no" />
|
||||||
<meta name="viewport" content="width=device-width, maximum-scale=1.0, initial-scale=1.0, user-scalable=0" />
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, maximum-scale=1.0, initial-scale=1.0, user-scalable=0"
|
||||||
|
/>
|
||||||
|
|
||||||
<title>%REACT_APP_RADAR_NAME%</title>
|
<title>%REACT_APP_RADAR_NAME%</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to view the AOE Technology Radar.</noscript>
|
<noscript
|
||||||
|
>You need to enable JavaScript to view the AOE Technology Radar.</noscript
|
||||||
|
>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
3875
rd_example.json
Normal file
3875
rd_example.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,35 +1,42 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
|
|
||||||
export type Animations = {
|
export type Animations = {
|
||||||
[k: string]: Animation[]
|
[k: string]: Animation[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export type AnimationStates = {
|
export type AnimationStates = {
|
||||||
[k: string]: React.CSSProperties[]
|
[k: string]: React.CSSProperties[];
|
||||||
}
|
};
|
||||||
|
|
||||||
type Animation = {
|
type Animation = {
|
||||||
stateA: React.CSSProperties
|
stateA: React.CSSProperties;
|
||||||
stateB: React.CSSProperties
|
stateB: React.CSSProperties;
|
||||||
delay: number
|
delay: number;
|
||||||
run?(callback: (state: any) => any): any // todo fix
|
run?(callback: (state: any) => any): any; // todo fix
|
||||||
prepare?(callback: (state: any) => any): any // todo fix
|
prepare?(callback: (state: any) => any): any; // todo fix
|
||||||
}
|
};
|
||||||
|
|
||||||
export type AnimationRunner = {
|
export type AnimationRunner = {
|
||||||
getState(): AnimationStates
|
getState(): AnimationStates;
|
||||||
run(): any
|
run(): any;
|
||||||
awaitAnimationComplete(callback: () => void): any
|
awaitAnimationComplete(callback: () => void): any;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const createAnimation = (stateA: React.CSSProperties, stateB: React.CSSProperties, delay: number): Animation => ({
|
export const createAnimation = (
|
||||||
|
stateA: React.CSSProperties,
|
||||||
|
stateB: React.CSSProperties,
|
||||||
|
delay: number
|
||||||
|
): Animation => ({
|
||||||
stateA,
|
stateA,
|
||||||
stateB,
|
stateB,
|
||||||
delay,
|
delay,
|
||||||
});
|
});
|
||||||
|
|
||||||
const getAnimationStates = (animations: Animation[], stateName: 'stateA' | 'stateB' = 'stateA'): React.CSSProperties[] => {
|
const getAnimationStates = (
|
||||||
return animations.map(animation => animation[stateName]);
|
animations: Animation[],
|
||||||
|
stateName: "stateA" | "stateB" = "stateA"
|
||||||
|
): React.CSSProperties[] => {
|
||||||
|
return animations.map((animation) => animation[stateName]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMaxTransitionTime = (transition: string) => {
|
const getMaxTransitionTime = (transition: string) => {
|
||||||
@@ -54,25 +61,39 @@ const getAnimationDuration = (animation: Animation | Animation[]): number => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const state = animation.stateB;
|
const state = animation.stateB;
|
||||||
const maxTransition = state.transition ? getMaxTransitionTime(state.transition) : 0;
|
const maxTransition = state.transition
|
||||||
|
? getMaxTransitionTime(state.transition)
|
||||||
|
: 0;
|
||||||
return maxTransition + animation.delay;
|
return maxTransition + animation.delay;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMaxAnimationsDuration = (animations: Animations) => (
|
const getMaxAnimationsDuration = (animations: Animations) =>
|
||||||
Math.max(...Object.values(animations).map(animations => getAnimationDuration(Object.values(animations))))
|
Math.max(
|
||||||
);
|
...Object.values(animations).map((animations) =>
|
||||||
|
getAnimationDuration(Object.values(animations))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
export const createAnimationRunner = (animationsIn: { [k: string]: (Animation | Animation[]) }, subscriber: () => void = () => {
|
export const createAnimationRunner = (
|
||||||
}): AnimationRunner => {
|
animationsIn: { [k: string]: Animation | Animation[] },
|
||||||
const animations = Object.entries(animationsIn).reduce((state, [name, animation]) => ({
|
subscriber: () => void = () => {}
|
||||||
|
): AnimationRunner => {
|
||||||
|
const animations = Object.entries(animationsIn).reduce(
|
||||||
|
(state, [name, animation]) => ({
|
||||||
...state,
|
...state,
|
||||||
[name]: animation instanceof Array ? animation : [animation] as Animation[],
|
[name]:
|
||||||
}), {} as Animations);
|
animation instanceof Array ? animation : ([animation] as Animation[]),
|
||||||
|
}),
|
||||||
|
{} as Animations
|
||||||
|
);
|
||||||
|
|
||||||
let state = Object.entries(animations).reduce((state, [name, animation]) => ({
|
let state = Object.entries(animations).reduce(
|
||||||
|
(state, [name, animation]) => ({
|
||||||
...state,
|
...state,
|
||||||
[name]: getAnimationStates(animation),
|
[name]: getAnimationStates(animation),
|
||||||
}), {} as AnimationStates);
|
}),
|
||||||
|
{} as AnimationStates
|
||||||
|
);
|
||||||
|
|
||||||
const animationsDuration = getMaxAnimationsDuration(animations);
|
const animationsDuration = getMaxAnimationsDuration(animations);
|
||||||
|
|
||||||
@@ -83,16 +104,16 @@ export const createAnimationRunner = (animationsIn: { [k: string]: (Animation |
|
|||||||
state = {
|
state = {
|
||||||
...state,
|
...state,
|
||||||
[name]: [
|
[name]: [
|
||||||
...(state[name]?.slice(0, index)),
|
...state[name]?.slice(0, index),
|
||||||
a.stateB,
|
a.stateB,
|
||||||
...(state[name]?.slice(index + 1, state[name].length)),
|
...state[name]?.slice(index + 1, state[name].length),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
subscriber();
|
subscriber();
|
||||||
}, a.delay);
|
}, a.delay);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getState() {
|
getState() {
|
||||||
@@ -100,11 +121,11 @@ export const createAnimationRunner = (animationsIn: { [k: string]: (Animation |
|
|||||||
},
|
},
|
||||||
run() {
|
run() {
|
||||||
Object.entries(animations).forEach(([name, animation]) => {
|
Object.entries(animations).forEach(([name, animation]) => {
|
||||||
animate(name, animation)
|
animate(name, animation);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
awaitAnimationComplete(callback) {
|
awaitAnimationComplete(callback) {
|
||||||
window.setTimeout(callback, animationsDuration);
|
window.setTimeout(callback, animationsDuration);
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
import React, { MouseEventHandler } from 'react';
|
import React, { MouseEventHandler } from "react";
|
||||||
import classNames from 'classnames';
|
import classNames from "classnames";
|
||||||
import './badge.scss';
|
import "./badge.scss";
|
||||||
import {Ring} from "../../config";
|
import { Ring } from "../../config";
|
||||||
type BadgeProps = {
|
type BadgeProps = {
|
||||||
onClick?: MouseEventHandler;
|
onClick?: MouseEventHandler;
|
||||||
big?: boolean;
|
big?: boolean;
|
||||||
type: 'big' | 'all' | 'empty' | Ring;
|
type: "big" | "all" | "empty" | Ring;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Badge({ onClick, big, type, children }: React.PropsWithChildren<BadgeProps>) {
|
export default function Badge({
|
||||||
const Comp = onClick ? 'a' : 'span';
|
onClick,
|
||||||
|
big,
|
||||||
|
type,
|
||||||
|
children,
|
||||||
|
}: React.PropsWithChildren<BadgeProps>) {
|
||||||
|
const Comp = onClick ? "a" : "span";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
className={classNames('badge', `badge--${type}`, {
|
className={classNames("badge", `badge--${type}`, {
|
||||||
'badge--big': big === true,
|
"badge--big": big === true,
|
||||||
})}
|
})}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
href={Comp === 'a' ? '#' : undefined}
|
href={Comp === "a" ? "#" : undefined}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Comp>
|
</Comp>
|
||||||
|
|||||||
@@ -40,5 +40,4 @@
|
|||||||
background: var(--color-marine);
|
background: var(--color-marine);
|
||||||
border-color: var(--color-marine);
|
border-color: var(--color-marine);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import classNames from 'classnames';
|
import classNames from "classnames";
|
||||||
import './branding.scss';
|
import "./branding.scss";
|
||||||
type BrandingProps = {
|
type BrandingProps = {
|
||||||
logoContent: React.ReactNode;
|
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 (
|
return (
|
||||||
<div className={classNames('branding', { [`branding--${modifier}`]: modifier })}>
|
<div
|
||||||
<div className='branding__logo'>{logoContent}</div>
|
className={classNames("branding", {
|
||||||
<div className='branding__content'>{children}</div>
|
[`branding--${modifier}`]: modifier,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div className="branding__logo">{logoContent}</div>
|
||||||
|
<div className="branding__content">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@import '../../styles/sccs-vars.scss';
|
@import "../../styles/sccs-vars.scss";
|
||||||
|
|
||||||
.branding {
|
.branding {
|
||||||
margin: 40px 0;
|
margin: 40px 0;
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from "react";
|
||||||
import classNames from 'classnames';
|
import classNames from "classnames";
|
||||||
import './fadeable.scss';
|
import "./fadeable.scss";
|
||||||
|
|
||||||
type FadeableProps = {
|
type FadeableProps = {
|
||||||
leaving: boolean;
|
leaving: boolean;
|
||||||
onLeave: () => void;
|
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);
|
const [faded, setFaded] = useState(leaving);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -25,7 +29,10 @@ export default function Fadeable({ leaving, onLeave, children }: React.PropsWith
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('fadable', { 'is-faded': faded })} onTransitionEnd={handleTransitionEnd}>
|
<div
|
||||||
|
className={classNames("fadable", { "is-faded": faded })}
|
||||||
|
onTransitionEnd={handleTransitionEnd}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,18 +1,37 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import classNames from 'classnames';
|
import classNames from "classnames";
|
||||||
import Branding from '../Branding/Branding';
|
import Branding from "../Branding/Branding";
|
||||||
import FooterEnd from '../FooterEnd/FooterEnd';
|
import FooterEnd from "../FooterEnd/FooterEnd";
|
||||||
import { assetUrl, getItemPageNames, isMobileViewport } from '../../config';
|
import { assetUrl, getItemPageNames, isMobileViewport } from "../../config";
|
||||||
import { Item } from '../../model';
|
import { Item } from "../../model";
|
||||||
import './footer.scss';
|
import "./footer.scss";
|
||||||
export default function Footer({ items, pageName }: { items: Item[]; pageName: string }) {
|
export default function Footer({
|
||||||
|
items,
|
||||||
|
pageName,
|
||||||
|
}: {
|
||||||
|
items: Item[];
|
||||||
|
pageName: string;
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={classNames('footer', { 'is-hidden': !isMobileViewport() && getItemPageNames(items).includes(pageName) })}>
|
<div
|
||||||
<Branding modifier='footer' logoContent={<img src={assetUrl('logo.svg')} width='150px' height='60px' alt='' />}>
|
className={classNames("footer", {
|
||||||
<span className='footnote'>
|
"is-hidden":
|
||||||
AOE is a leading global provider of services for digital transformation and digital business models. AOE relies exclusively on established Enterprise
|
!isMobileViewport() && getItemPageNames(items).includes(pageName),
|
||||||
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.
|
>
|
||||||
|
<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>
|
</span>
|
||||||
</Branding>
|
</Branding>
|
||||||
<FooterEnd />
|
<FooterEnd />
|
||||||
|
|||||||
@@ -1,37 +1,75 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import classNames from 'classnames';
|
import classNames from "classnames";
|
||||||
import './footerend.scss';
|
import "./footerend.scss";
|
||||||
export default function FooterEnd({ modifier }: { modifier?: 'in-sidebar' }) {
|
export default function FooterEnd({ modifier }: { modifier?: "in-sidebar" }) {
|
||||||
return (
|
return (
|
||||||
<div className={classNames('footer-end', { [`footer-end__${modifier}`]: modifier })}>
|
<div
|
||||||
<div className='footer-social'>
|
className={classNames("footer-end", {
|
||||||
<div className='footer-social__label'>
|
[`footer-end__${modifier}`]: modifier,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div className="footer-social">
|
||||||
|
<div className="footer-social__label">
|
||||||
<p>Follow us:</p>
|
<p>Follow us:</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='footer-social__links'>
|
<div className="footer-social__links">
|
||||||
<a className='social-links-icon' href='https://www.facebook.com/aoepeople' target='_blank' rel='noopener noreferrer'>
|
<a
|
||||||
<i className='socicon-facebook social-icon'></i>
|
className="social-links-icon"
|
||||||
|
href="https://www.facebook.com/aoepeople"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<i className="socicon-facebook social-icon"></i>
|
||||||
</a>
|
</a>
|
||||||
<a className='social-links-icon' href='https://twitter.com/aoepeople' target='_blank' rel='noopener noreferrer'>
|
<a
|
||||||
<i className='socicon-twitter social-icon'></i>
|
className="social-links-icon"
|
||||||
|
href="https://twitter.com/aoepeople"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<i className="socicon-twitter social-icon"></i>
|
||||||
</a>
|
</a>
|
||||||
<a className='social-links-icon' href='https://www.linkedin.com/company/aoe' target='_blank' rel='noopener noreferrer'>
|
<a
|
||||||
<i className='socicon-linkedin social-icon'></i>
|
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>
|
||||||
<a className='social-links-icon' href='https://www.xing.com/company/aoe' target='_blank' rel='noopener noreferrer'>
|
<a
|
||||||
<i className='socicon-xing social-icon'></i>
|
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>
|
||||||
<a className='social-links-icon' href='https://www.youtube.com/user/aoepeople' target='_blank' rel='noopener noreferrer'>
|
<a
|
||||||
<i className='socicon-youtube social-icon'></i>
|
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>
|
||||||
<a className='social-links-icon' href='https://github.com/aoepeople' target='_blank' rel='noopener noreferrer'>
|
<a
|
||||||
<i className='socicon-github social-icon'></i>
|
className="social-links-icon"
|
||||||
|
href="https://github.com/aoepeople"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<i className="socicon-github social-icon"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='footer-copyright'>
|
<div className="footer-copyright">
|
||||||
<p>
|
<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
|
Legal Information
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@import '../../styles/sccs-vars.scss';
|
@import "../../styles/sccs-vars.scss";
|
||||||
|
|
||||||
.footer-end {
|
.footer-end {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import classNames from 'classnames';
|
import classNames from "classnames";
|
||||||
import './headline-group.scss';
|
import "./headline-group.scss";
|
||||||
export default function ({ children, secondary = false }: React.PropsWithChildren<{ secondary?: boolean }>) {
|
export default function ({
|
||||||
return <div className={classNames('headline-group', { 'headline-group--secondary': secondary })}>{children}</div>;
|
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 {
|
.headline-group {
|
||||||
margin: 0 0 60px;
|
margin: 0 0 60px;
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import './hero-headline.scss';
|
import "./hero-headline.scss";
|
||||||
export default function ({ children, alt }: React.PropsWithChildren<{ alt?: string }>) {
|
export default function ({
|
||||||
|
children,
|
||||||
|
alt,
|
||||||
|
}: React.PropsWithChildren<{ alt?: string }>) {
|
||||||
return (
|
return (
|
||||||
<div className='hero-headline'>
|
<div className="hero-headline">
|
||||||
{children}
|
{children}
|
||||||
<span className='hero-headline__alt'>{alt}</span>
|
<span className="hero-headline__alt">{alt}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@import '../../styles/sccs-vars.scss';
|
@import "../../styles/sccs-vars.scss";
|
||||||
|
|
||||||
.hero-headline {
|
.hero-headline {
|
||||||
font-size: 38px;
|
font-size: 38px;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import classNames from 'classnames';
|
import classNames from "classnames";
|
||||||
import Link from '../Link/Link';
|
import Link from "../Link/Link";
|
||||||
import Flag from '../Flag/Flag';
|
import Flag from "../Flag/Flag";
|
||||||
import { Item as mItem } from '../../model';
|
import { Item as mItem } from "../../model";
|
||||||
import './item.scss';
|
import "./item.scss";
|
||||||
type ItemProps = {
|
type ItemProps = {
|
||||||
item: mItem;
|
item: mItem;
|
||||||
noLeadingBorder?: boolean;
|
noLeadingBorder?: boolean;
|
||||||
@@ -11,21 +11,26 @@ type ItemProps = {
|
|||||||
style: React.CSSProperties;
|
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 (
|
return (
|
||||||
<Link
|
<Link
|
||||||
className={classNames('item', {
|
className={classNames("item", {
|
||||||
'item--no-leading-border': noLeadingBorder,
|
"item--no-leading-border": noLeadingBorder,
|
||||||
'is-active': active,
|
"is-active": active,
|
||||||
})}
|
})}
|
||||||
pageName={`${item.quadrant}/${item.name}`}
|
pageName={`${item.quadrant}/${item.name}`}
|
||||||
style={style}
|
style={style}
|
||||||
>
|
>
|
||||||
<div className='item__title'>
|
<div className="item__title">
|
||||||
{item.title}
|
{item.title}
|
||||||
<Flag item={item} />
|
<Flag item={item} />
|
||||||
</div>
|
</div>
|
||||||
{item.info && <div className='item__info'>{item.info}</div>}
|
{item.info && <div className="item__info">{item.info}</div>}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import Item from '../Item/Item';
|
import Item from "../Item/Item";
|
||||||
import { Item as mItem } from '../../model';
|
import { Item as mItem } from "../../model";
|
||||||
import './item-list.scss';
|
import "./item-list.scss";
|
||||||
type ItemListProps = {
|
type ItemListProps = {
|
||||||
items: mItem[];
|
items: mItem[];
|
||||||
activeItem?: mItem;
|
activeItem?: mItem;
|
||||||
@@ -10,19 +10,30 @@ type ItemListProps = {
|
|||||||
itemStyle?: React.CSSProperties[];
|
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 (
|
return (
|
||||||
<div className='item-list'>
|
<div className="item-list">
|
||||||
<div className='item-list__header' style={headerStyle}>
|
<div className="item-list__header" style={headerStyle}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
<div className='item-list__list'>
|
<div className="item-list__list">
|
||||||
{items.map((item, i) => (
|
{items.map((item, i) => (
|
||||||
<Item
|
<Item
|
||||||
key={item.name}
|
key={item.name}
|
||||||
item={item}
|
item={item}
|
||||||
noLeadingBorder={noLeadingBorder}
|
noLeadingBorder={noLeadingBorder}
|
||||||
active={activeItem !== null && activeItem !== undefined && activeItem.name === item.name}
|
active={
|
||||||
|
activeItem !== null &&
|
||||||
|
activeItem !== undefined &&
|
||||||
|
activeItem.name === item.name
|
||||||
|
}
|
||||||
style={itemStyle[i]}
|
style={itemStyle[i]}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import Badge from '../Badge/Badge';
|
import Badge from "../Badge/Badge";
|
||||||
import { formatRelease } from '../../date';
|
import { formatRelease } from "../../date";
|
||||||
import { Revision } from '../../model';
|
import { Revision } from "../../model";
|
||||||
|
|
||||||
export default function ItemRevision({ revision }: { revision: Revision }) {
|
export default function ItemRevision({ revision }: { revision: Revision }) {
|
||||||
return (
|
return (
|
||||||
<div className='item-revision'>
|
<div className="item-revision">
|
||||||
<div>
|
<div>
|
||||||
<Badge type={revision.ring}>
|
<Badge type={revision.ring}>
|
||||||
{revision.ring} | {formatRelease(revision.release)}
|
{revision.ring} | {formatRelease(revision.release)}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className='markdown' dangerouslySetInnerHTML={{ __html: revision.body }} />
|
<div
|
||||||
|
className="markdown"
|
||||||
|
dangerouslySetInnerHTML={{ __html: revision.body }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.item-revision {
|
.item-revision {
|
||||||
&+.item-revision {
|
& + .item-revision {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import HeadlineGroup from '../HeadlineGroup/HeadlineGroup';
|
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
||||||
import ItemRevision from '../ItemRevision/ItemRevision';
|
import ItemRevision from "../ItemRevision/ItemRevision";
|
||||||
import { Revision } from '../../model';
|
import { Revision } from "../../model";
|
||||||
import './item-revisions.scss';
|
import "./item-revisions.scss";
|
||||||
export default function ItemRevisions({ revisions }: { revisions: Revision[] }) {
|
export default function ItemRevisions({
|
||||||
|
revisions,
|
||||||
|
}: {
|
||||||
|
revisions: Revision[];
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className='item-revisions'>
|
<div className="item-revisions">
|
||||||
<HeadlineGroup secondary>
|
<HeadlineGroup secondary>
|
||||||
<h4 className='headline headline--dark'>Revisions:</h4>
|
<h4 className="headline headline--dark">Revisions:</h4>
|
||||||
</HeadlineGroup>
|
</HeadlineGroup>
|
||||||
|
|
||||||
{revisions.map((revision) => (
|
{revisions.map((revision) => (
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import classNames from 'classnames';
|
import classNames from "classnames";
|
||||||
import Link from '../Link/Link';
|
import Link from "../Link/Link";
|
||||||
import { assetUrl, radarNameShort } from '../../config';
|
import { assetUrl, radarNameShort } from "../../config";
|
||||||
import './logo-link.scss';
|
import "./logo-link.scss";
|
||||||
export default function LogoLink({ small = false }: { small?: boolean }) {
|
export default function LogoLink({ small = false }: { small?: boolean }) {
|
||||||
return (
|
return (
|
||||||
<Link pageName='index' className={classNames('logo-link', { 'logo-link--small': small })}>
|
<Link
|
||||||
<span className='logo-link__icon icon icon--back'/>
|
pageName="index"
|
||||||
<span className='logo-link__slide'>
|
className={classNames("logo-link", { "logo-link--small": small })}
|
||||||
<img className='logo-link__img' src={assetUrl('logo.svg')} width='150px' height='60px' alt={radarNameShort} />
|
>
|
||||||
<span className='logo-link__text'>{radarNameShort}</span>
|
<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>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,91 +1,139 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import HeroHeadline from '../HeroHeadline/HeroHeadline';
|
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||||
import Fadeable from '../Fadeable/Fadeable';
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
import SetTitle from '../SetTitle';
|
import SetTitle from "../SetTitle";
|
||||||
import { radarName } from '../../config';
|
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 (
|
return (
|
||||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
<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>
|
<HeroHeadline>How to use the {radarName}</HeroHeadline>
|
||||||
<div className='fullpage-content'>
|
<div className="fullpage-content">
|
||||||
<h3>Introduction</h3>
|
<h3>Introduction</h3>
|
||||||
<p>Technology is moving fast and new technologies and innovations appear continuously.</p>
|
|
||||||
<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
|
Technology is moving fast and new technologies and innovations appear
|
||||||
important to openly look for innovations and new technologies and to question established technologies and methods every now and then.
|
continuously.
|
||||||
</p>
|
</p>
|
||||||
<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
|
It's essential for a development and technology company such as AOE to
|
||||||
know: There is no silver bullet.
|
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>
|
</p>
|
||||||
<h3>What is the {radarName}</h3>
|
<h3>What is the {radarName}</h3>
|
||||||
<p>
|
<p>
|
||||||
The Tech Radar is an overview of different technologies - from languages, frameworks, tools and patterns to platforms - that we consider "new or
|
The Tech Radar is an overview of different technologies - from
|
||||||
mentionable". The radar therefore doesn't provide an overview of all established technologies - but it focuses on items that have recently gained in
|
languages, frameworks, tools and patterns to platforms - that we
|
||||||
importance or changed.
|
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>
|
</p>
|
||||||
<h3>How it is created</h3>
|
<h3>How it is created</h3>
|
||||||
<p>
|
<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
|
The items in the technology radar are raised by the different 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.
|
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>
|
||||||
<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.
|
There have been a lot of valuable discussions in different expert
|
||||||
And the result of all this can be found in the latest technology radar.
|
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>
|
</p>
|
||||||
<h3>How should it be used</h3>
|
<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>
|
<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
|
The radar acts as an overview of technologies that we think everyone
|
||||||
perspective - so that decisions can be taken with a much deeper understanding of the subject matter. This results in more-informed and better-aligned
|
in the teams should currently know about.
|
||||||
decisions.
|
</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>
|
||||||
<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>
|
<p>The quadrants are:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<strong>Languages and Frameworks:</strong> We've placed development languages (such as Scala or Golang) here, as well as more low-level development
|
<strong>Languages and Frameworks:</strong> We've placed development
|
||||||
frameworks (such as Play or Symfony), which are useful for implementing custom software of all kinds.{' '}
|
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>
|
||||||
<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>
|
||||||
<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
|
<strong>Methods and Patterns:</strong> Patterns are so important,
|
||||||
frameworks). So, this is the category where we put information on methods and patterns concerning development, continuous x, testing, organization,
|
and a lot of them are valid for a long time (compared to some tools
|
||||||
architecture, etc.
|
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>
|
||||||
<li>
|
<li>
|
||||||
<strong>Platforms and Services</strong> (including AOE internal Services): Here we include infrastructure platforms and services. We also use this
|
<strong>Platforms and Services</strong> (including AOE internal
|
||||||
category to communicate news about AOE services that we want all AOE teams to be aware of.
|
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>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Each of the items is classified in one of these rings:</p>
|
<p>Each of the items is classified in one of these rings:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<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
|
<strong>Adopt:</strong> We can clearly recommend this technology. We
|
||||||
stable and useful.
|
have used it for longer period of time in many teams and it has
|
||||||
|
proven to be stable and useful.
|
||||||
</li>
|
</li>
|
||||||
<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
|
<strong>Trial:</strong> We have used it with success and recommend
|
||||||
to look at them more closely, with the goal to bring them to the adopt level.
|
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>
|
||||||
<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
|
<strong>Assess:</strong> We have tried it out and we find it
|
||||||
the technology in your project.
|
promising. We recommend having a look at these items when you face a
|
||||||
|
specific need for the technology in your project.
|
||||||
</li>
|
</li>
|
||||||
<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
|
<strong>Hold:</strong> This category is a bit special. Unlike the
|
||||||
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
|
others, we recommend to stop doing or using something. That does not
|
||||||
we see better options or alternatives now.
|
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>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
Contributions and source code of the radar are on github:{' '}
|
Contributions and source code of the radar are on github:{" "}
|
||||||
<a href='https://github.com/AOEpeople/aoe_technology_radar' target='_blank' rel='noopener noreferrer'>
|
<a
|
||||||
|
href="https://github.com/AOEpeople/aoe_technology_radar"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
AOE Tech Radar on Github
|
AOE Tech Radar on Github
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { formatRelease } from '../../date';
|
import { formatRelease } from "../../date";
|
||||||
import { featuredOnly, Item } from '../../model';
|
import { featuredOnly, Item } from "../../model";
|
||||||
import HeroHeadline from '../HeroHeadline/HeroHeadline';
|
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||||
import QuadrantGrid from '../QuadrantGrid/QuadrantGrid';
|
import QuadrantGrid from "../QuadrantGrid/QuadrantGrid";
|
||||||
import Fadeable from '../Fadeable/Fadeable';
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
import SetTitle from '../SetTitle';
|
import SetTitle from "../SetTitle";
|
||||||
import { radarName, radarNameShort } from '../../config';
|
import { radarName, radarNameShort } from "../../config";
|
||||||
import { MomentInput } from 'moment';
|
import { MomentInput } from "moment";
|
||||||
|
|
||||||
type PageIndexProps = {
|
type PageIndexProps = {
|
||||||
leaving: boolean;
|
leaving: boolean;
|
||||||
@@ -15,17 +15,26 @@ type PageIndexProps = {
|
|||||||
releases: MomentInput[];
|
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 newestRelease = releases.slice(-1)[0];
|
||||||
const numberOfReleases = releases.length;
|
const numberOfReleases = releases.length;
|
||||||
return (
|
return (
|
||||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||||
<SetTitle title={radarNameShort} />
|
<SetTitle title={radarNameShort} />
|
||||||
<div className='headline-group'>
|
<div className="headline-group">
|
||||||
<HeroHeadline alt={`Version #${numberOfReleases}`}>{radarName}</HeroHeadline>
|
<HeroHeadline alt={`Version #${numberOfReleases}`}>
|
||||||
|
{radarName}
|
||||||
|
</HeroHeadline>
|
||||||
</div>
|
</div>
|
||||||
<QuadrantGrid items={featuredOnly(items)} />
|
<QuadrantGrid items={featuredOnly(items)} />
|
||||||
<div className='publish-date'>Published {formatRelease(newestRelease)}</div>
|
<div className="publish-date">
|
||||||
|
Published {formatRelease(newestRelease)}
|
||||||
|
</div>
|
||||||
</Fadeable>
|
</Fadeable>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import React, {useEffect, useState} from 'react';
|
import React, { useEffect, useState } from "react";
|
||||||
import Badge from '../Badge/Badge';
|
import Badge from "../Badge/Badge";
|
||||||
import ItemList from '../ItemList/ItemList';
|
import ItemList from "../ItemList/ItemList";
|
||||||
import Link from '../Link/Link';
|
import Link from "../Link/Link";
|
||||||
import FooterEnd from '../FooterEnd/FooterEnd';
|
import FooterEnd from "../FooterEnd/FooterEnd";
|
||||||
import SetTitle from '../SetTitle';
|
import SetTitle from "../SetTitle";
|
||||||
import ItemRevisions from '../ItemRevisions/ItemRevisions';
|
import ItemRevisions from "../ItemRevisions/ItemRevisions";
|
||||||
import {
|
import {
|
||||||
AnimationStates,
|
AnimationStates,
|
||||||
createAnimation,
|
createAnimation,
|
||||||
createAnimationRunner
|
createAnimationRunner,
|
||||||
} from '../../animation';
|
} from "../../animation";
|
||||||
import './item-page.scss';
|
import "./item-page.scss";
|
||||||
import {translate} from '../../config';
|
import { translate } from "../../config";
|
||||||
import {groupByQuadrants, Item} from '../../model';
|
import { groupByQuadrants, Item } from "../../model";
|
||||||
|
|
||||||
const getItem = (pageName: string, items: Item[]) => {
|
const getItem = (pageName: string, items: Item[]) => {
|
||||||
const [quadrantName, itemName] = pageName.split('/');
|
const [quadrantName, itemName] = pageName.split("/");
|
||||||
return items.filter((item) => item.quadrant === quadrantName && item.name === itemName)[0];
|
return items.filter(
|
||||||
|
(item) => item.quadrant === quadrantName && item.name === itemName
|
||||||
|
)[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getItemsInRing = (pageName: string, items: Item[]) => {
|
const getItemsInRing = (pageName: string, items: Item[]) => {
|
||||||
@@ -31,91 +33,100 @@ type PageItemProps = {
|
|||||||
onLeave: () => void;
|
onLeave: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PageItem({pageName, items, leaving, onLeave}: PageItemProps) {
|
export default function PageItem({
|
||||||
|
pageName,
|
||||||
|
items,
|
||||||
|
leaving,
|
||||||
|
onLeave,
|
||||||
|
}: PageItemProps) {
|
||||||
const itemsInRing = getItemsInRing(pageName, items);
|
const itemsInRing = getItemsInRing(pageName, items);
|
||||||
|
|
||||||
const animationsIn = {
|
const animationsIn = {
|
||||||
background: createAnimation(
|
background: createAnimation(
|
||||||
{
|
{
|
||||||
transform: 'translateX(calc((100vw - 1200px) / 2 + 800px))',
|
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)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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)',
|
transform: "translateX(0)",
|
||||||
},
|
},
|
||||||
0
|
0
|
||||||
),
|
),
|
||||||
navHeader: createAnimation(
|
navHeader: createAnimation(
|
||||||
{
|
{
|
||||||
transform: 'translateX(-40px)',
|
transform: "translateX(-40px)",
|
||||||
opacity: '0',
|
opacity: "0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||||
transform: 'translateX(0px)',
|
transform: "translateX(0px)",
|
||||||
opacity: '1',
|
opacity: "1",
|
||||||
},
|
},
|
||||||
300
|
300
|
||||||
),
|
),
|
||||||
text: createAnimation(
|
text: createAnimation(
|
||||||
{
|
{
|
||||||
transform: 'translateY(-20px)',
|
transform: "translateY(-20px)",
|
||||||
opacity: '0',
|
opacity: "0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||||
transform: 'translateY(0px)',
|
transform: "translateY(0px)",
|
||||||
opacity: '1',
|
opacity: "1",
|
||||||
},
|
},
|
||||||
600
|
600
|
||||||
),
|
),
|
||||||
items: itemsInRing.map((item, i) =>
|
items: itemsInRing.map((item, i) =>
|
||||||
createAnimation(
|
createAnimation(
|
||||||
{
|
{
|
||||||
transform: 'translateX(-40px)',
|
transform: "translateX(-40px)",
|
||||||
opacity: '0',
|
opacity: "0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||||
transform: 'translateX(0px)',
|
transform: "translateX(0px)",
|
||||||
opacity: '1',
|
opacity: "1",
|
||||||
},
|
},
|
||||||
400 + 100 * i
|
400 + 100 * i
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
footer: createAnimation(
|
footer: createAnimation(
|
||||||
{
|
{
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||||
transform: 'translateX(-40px)',
|
transform: "translateX(-40px)",
|
||||||
opacity: '0',
|
opacity: "0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||||
transform: 'translateX(0px)',
|
transform: "translateX(0px)",
|
||||||
opacity: '1',
|
opacity: "1",
|
||||||
},
|
},
|
||||||
600 + itemsInRing.length * 100
|
600 + itemsInRing.length * 100
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const animationsOut = {
|
const animationsOut = {
|
||||||
background: createAnimation(animationsIn.background.stateB, animationsIn.background.stateA, 300 + itemsInRing.length * 50),
|
background: createAnimation(
|
||||||
|
animationsIn.background.stateB,
|
||||||
|
animationsIn.background.stateA,
|
||||||
|
300 + itemsInRing.length * 50
|
||||||
|
),
|
||||||
navHeader: createAnimation(
|
navHeader: createAnimation(
|
||||||
animationsIn.navHeader.stateB,
|
animationsIn.navHeader.stateB,
|
||||||
{
|
{
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||||
transform: 'translateX(40px)',
|
transform: "translateX(40px)",
|
||||||
opacity: '0',
|
opacity: "0",
|
||||||
},
|
},
|
||||||
0
|
0
|
||||||
),
|
),
|
||||||
text: createAnimation(
|
text: createAnimation(
|
||||||
animationsIn.text.stateB,
|
animationsIn.text.stateB,
|
||||||
{
|
{
|
||||||
transform: 'translateY(20px)',
|
transform: "translateY(20px)",
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||||
opacity: '0',
|
opacity: "0",
|
||||||
},
|
},
|
||||||
0
|
0
|
||||||
),
|
),
|
||||||
@@ -123,9 +134,9 @@ export default function PageItem({pageName, items, leaving, onLeave}: PageItemPr
|
|||||||
createAnimation(
|
createAnimation(
|
||||||
animationsIn.items[i].stateB,
|
animationsIn.items[i].stateB,
|
||||||
{
|
{
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||||
transform: 'translateX(40px)',
|
transform: "translateX(40px)",
|
||||||
opacity: '0',
|
opacity: "0",
|
||||||
},
|
},
|
||||||
100 + 50 * i
|
100 + 50 * i
|
||||||
)
|
)
|
||||||
@@ -133,104 +144,125 @@ export default function PageItem({pageName, items, leaving, onLeave}: PageItemPr
|
|||||||
footer: createAnimation(
|
footer: createAnimation(
|
||||||
animationsIn.text.stateB,
|
animationsIn.text.stateB,
|
||||||
{
|
{
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||||
transform: 'translateX(40px)',
|
transform: "translateX(40px)",
|
||||||
opacity: '0',
|
opacity: "0",
|
||||||
},
|
},
|
||||||
200 + itemsInRing.length * 50
|
200 + itemsInRing.length * 50
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const [animations, setAnimations] = useState<AnimationStates>(() => {
|
const [animations, setAnimations] = useState<AnimationStates>(() => {
|
||||||
return leaving ? createAnimationRunner(animationsIn).getState() : {}
|
return leaving ? createAnimationRunner(animationsIn).getState() : {};
|
||||||
});
|
});
|
||||||
|
|
||||||
const [stateLeaving, setStateLeaving] = useState(leaving);
|
const [stateLeaving, setStateLeaving] = useState(leaving);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!stateLeaving && leaving) {
|
if (!stateLeaving && leaving) {
|
||||||
let animationRunner = createAnimationRunner(
|
let animationRunner = createAnimationRunner(animationsOut, () =>
|
||||||
animationsOut,
|
setAnimations(animationRunner.getState)
|
||||||
() => setAnimations(animationRunner.getState),
|
);
|
||||||
)
|
|
||||||
animationRunner.run();
|
animationRunner.run();
|
||||||
animationRunner.awaitAnimationComplete(onLeave);
|
animationRunner.awaitAnimationComplete(onLeave);
|
||||||
setStateLeaving(true)
|
setStateLeaving(true);
|
||||||
}
|
}
|
||||||
if (stateLeaving && !leaving) {
|
if (stateLeaving && !leaving) {
|
||||||
let animationRunner = createAnimationRunner(
|
let animationRunner = createAnimationRunner(animationsIn, () =>
|
||||||
animationsIn,
|
setAnimations(animationRunner.getState)
|
||||||
() => setAnimations(animationRunner.getState),
|
);
|
||||||
)
|
|
||||||
animationRunner.run();
|
animationRunner.run();
|
||||||
setStateLeaving(false)
|
setStateLeaving(false);
|
||||||
}
|
}
|
||||||
}, [stateLeaving, leaving, animationsIn, animationsOut, onLeave])
|
}, [stateLeaving, leaving, animationsIn, animationsOut, onLeave]);
|
||||||
|
|
||||||
const getAnimationStates = (name: string) => {
|
const getAnimationStates = (name: string) => {
|
||||||
if (!animations) {
|
if (!animations) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return animations[name];
|
return animations[name];
|
||||||
}
|
};
|
||||||
|
|
||||||
const getAnimationState = (name: string) => {
|
const getAnimationState = (name: string) => {
|
||||||
const animations = getAnimationStates(name)
|
const animations = getAnimationStates(name);
|
||||||
if (animations === undefined || animations.length === 0) {
|
if (animations === undefined || animations.length === 0) {
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
}
|
||||||
return animations[0]
|
return animations[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
const item = getItem(pageName, items);
|
const item = getItem(pageName, items);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SetTitle title={item.title}/>
|
<SetTitle title={item.title} />
|
||||||
<div className='item-page'>
|
<div className="item-page">
|
||||||
<div className='item-page__nav'>
|
<div className="item-page__nav">
|
||||||
<div className='item-page__nav__inner'>
|
<div className="item-page__nav__inner">
|
||||||
<div className='item-page__header' style={getAnimationState('navHeader')}>
|
<div
|
||||||
<h3 className='headline'>{translate(item.quadrant)}</h3>
|
className="item-page__header"
|
||||||
|
style={getAnimationState("navHeader")}
|
||||||
|
>
|
||||||
|
<h3 className="headline">{translate(item.quadrant)}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ItemList items={itemsInRing} activeItem={item} headerStyle={getAnimationState('navHeader')}
|
<ItemList
|
||||||
itemStyle={getAnimationStates('items')}>
|
items={itemsInRing}
|
||||||
<div className='split'>
|
activeItem={item}
|
||||||
<div className='split__left'>
|
headerStyle={getAnimationState("navHeader")}
|
||||||
|
itemStyle={getAnimationStates("items")}
|
||||||
|
>
|
||||||
|
<div className="split">
|
||||||
|
<div className="split__left">
|
||||||
<Badge big type={item.ring}>
|
<Badge big type={item.ring}>
|
||||||
{item.ring}
|
{item.ring}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className='split__right'>
|
<div className="split__right">
|
||||||
<Link className='icon-link' pageName={item.quadrant}>
|
<Link className="icon-link" pageName={item.quadrant}>
|
||||||
<span className='icon icon--pie icon-link__icon'/>
|
<span className="icon icon--pie icon-link__icon" />
|
||||||
Quadrant Overview
|
Quadrant Overview
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ItemList>
|
</ItemList>
|
||||||
<div className='item-page__footer' style={getAnimationState('footer')}>
|
<div
|
||||||
<FooterEnd modifier='in-sidebar'/>
|
className="item-page__footer"
|
||||||
|
style={getAnimationState("footer")}
|
||||||
|
>
|
||||||
|
<FooterEnd modifier="in-sidebar" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='item-page__content' style={getAnimationState('background')}>
|
<div
|
||||||
<div className='item-page__content__inner' style={getAnimationState('text')}>
|
className="item-page__content"
|
||||||
<div className='item-page__header'>
|
style={getAnimationState("background")}
|
||||||
<div className='split'>
|
>
|
||||||
<div className='split__left'>
|
<div
|
||||||
<h1 className='hero-headline hero-headline--inverse'>{item.title}</h1>
|
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>
|
||||||
<div className='split__right'>
|
<div className="split__right">
|
||||||
<Badge big type={item.ring}>
|
<Badge big type={item.ring}>
|
||||||
{item.ring}
|
{item.ring}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='markdown' dangerouslySetInnerHTML={{__html: item.body}}/>
|
<div
|
||||||
{item.revisions.length > 1 && <ItemRevisions revisions={item.revisions.slice(1)}/>}
|
className="markdown"
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.body }}
|
||||||
|
/>
|
||||||
|
{item.revisions.length > 1 && (
|
||||||
|
<ItemRevisions revisions={item.revisions.slice(1)} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -57,6 +57,6 @@
|
|||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
|
|
||||||
&__aside {
|
&__aside {
|
||||||
padding: 20px 0
|
padding: 20px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import Badge from '../Badge/Badge';
|
import Badge from "../Badge/Badge";
|
||||||
import ItemList from '../ItemList/ItemList';
|
import ItemList from "../ItemList/ItemList";
|
||||||
import Link from '../Link/Link';
|
import Link from "../Link/Link";
|
||||||
import Fadeable from '../Fadeable/Fadeable';
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
import SetTitle from '../SetTitle';
|
import SetTitle from "../SetTitle";
|
||||||
import ItemRevisions from '../ItemRevisions/ItemRevisions';
|
import ItemRevisions from "../ItemRevisions/ItemRevisions";
|
||||||
|
|
||||||
import { translate } from '../../config';
|
import { translate } from "../../config";
|
||||||
import { groupByQuadrants, Item } from '../../model';
|
import { groupByQuadrants, Item } from "../../model";
|
||||||
|
|
||||||
type PageItemMobileProps = {
|
type PageItemMobileProps = {
|
||||||
pageName: string;
|
pageName: string;
|
||||||
@@ -16,10 +16,17 @@ type PageItemMobileProps = {
|
|||||||
onLeave: () => void;
|
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 getItem = (pageName: string, items: Item[]) => {
|
||||||
const [quadrantName, itemName] = pageName.split('/');
|
const [quadrantName, itemName] = pageName.split("/");
|
||||||
const item = items.filter((item) => item.quadrant === quadrantName && item.name === itemName)[0];
|
const item = items.filter(
|
||||||
|
(item) => item.quadrant === quadrantName && item.name === itemName
|
||||||
|
)[0];
|
||||||
return item;
|
return item;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -34,36 +41,43 @@ export default function PageItemMobile({ pageName, items, leaving, onLeave }: Pa
|
|||||||
return (
|
return (
|
||||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||||
<SetTitle title={item.title} />
|
<SetTitle title={item.title} />
|
||||||
<div className='mobile-item-page'>
|
<div className="mobile-item-page">
|
||||||
<div className='mobile-item-page__content'>
|
<div className="mobile-item-page__content">
|
||||||
<div className='mobile-item-page__content__inner'>
|
<div className="mobile-item-page__content__inner">
|
||||||
<div className='mobile-item-page__header'>
|
<div className="mobile-item-page__header">
|
||||||
<div className='split'>
|
<div className="split">
|
||||||
<div className='split__left'>
|
<div className="split__left">
|
||||||
<h3 className='headline'>{translate(item.quadrant)}</h3>
|
<h3 className="headline">{translate(item.quadrant)}</h3>
|
||||||
<h1 className='hero-headline hero-headline--inverse'>{item.title}</h1>
|
<h1 className="hero-headline hero-headline--inverse">
|
||||||
|
{item.title}
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className='split__right'>
|
<div className="split__right">
|
||||||
<Badge big type={item.ring}>
|
<Badge big type={item.ring}>
|
||||||
{item.ring}
|
{item.ring}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='markdown' dangerouslySetInnerHTML={{ __html: item.body }} />
|
<div
|
||||||
{item.revisions.length > 1 && <ItemRevisions revisions={item.revisions.slice(1)} />}
|
className="markdown"
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.body }}
|
||||||
|
/>
|
||||||
|
{item.revisions.length > 1 && (
|
||||||
|
<ItemRevisions revisions={item.revisions.slice(1)} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<aside className='mobile-item-page__aside'>
|
<aside className="mobile-item-page__aside">
|
||||||
<ItemList items={itemsInRing} activeItem={item}>
|
<ItemList items={itemsInRing} activeItem={item}>
|
||||||
<div className='split'>
|
<div className="split">
|
||||||
<div className='split__left'>
|
<div className="split__left">
|
||||||
<h3 className='headline'>{translate(item.quadrant)}</h3>
|
<h3 className="headline">{translate(item.quadrant)}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className='split__right'>
|
<div className="split__right">
|
||||||
<Link className='icon-link' pageName={item.quadrant}>
|
<Link className="icon-link" pageName={item.quadrant}>
|
||||||
<span className='icon icon--pie icon-link__icon'></span>Zoom In
|
<span className="icon icon--pie icon-link__icon"></span>Zoom In
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,30 +1,39 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from "react";
|
||||||
import HeadlineGroup from '../HeadlineGroup/HeadlineGroup';
|
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
||||||
import HeroHeadline from '../HeroHeadline/HeroHeadline';
|
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||||
import Badge from '../Badge/Badge';
|
import Badge from "../Badge/Badge";
|
||||||
import Link from '../Link/Link';
|
import Link from "../Link/Link";
|
||||||
import Search from '../Search/Search';
|
import Search from "../Search/Search";
|
||||||
import Fadeable from '../Fadeable/Fadeable';
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
import SetTitle from '../SetTitle';
|
import SetTitle from "../SetTitle";
|
||||||
import Flag from '../Flag/Flag';
|
import Flag from "../Flag/Flag";
|
||||||
import { groupByFirstLetter, Item } from '../../model';
|
import { groupByFirstLetter, Item } from "../../model";
|
||||||
import { translate, Ring } from '../../config';
|
import { translate, Ring } from "../../config";
|
||||||
|
|
||||||
const containsSearchTerm = (text = '', term = '') => {
|
const containsSearchTerm = (text = "", term = "") => {
|
||||||
// TODO search refinement
|
// TODO search refinement
|
||||||
return text.trim().toLocaleLowerCase().indexOf(term.trim().toLocaleLowerCase()) !== -1;
|
return (
|
||||||
|
text.trim().toLocaleLowerCase().indexOf(term.trim().toLocaleLowerCase()) !==
|
||||||
|
-1
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
type PageOverviewProps = {
|
type PageOverviewProps = {
|
||||||
rings: readonly ('all' | Ring)[];
|
rings: readonly ("all" | Ring)[];
|
||||||
search: string;
|
search: string;
|
||||||
items: Item[];
|
items: Item[];
|
||||||
leaving: boolean;
|
leaving: boolean;
|
||||||
onLeave: () => void;
|
onLeave: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PageOverview({ rings, search: searchProp, items, leaving, onLeave }: PageOverviewProps) {
|
export default function PageOverview({
|
||||||
const [ring, setRing] = useState<Ring | 'all'>('all');
|
rings,
|
||||||
|
search: searchProp,
|
||||||
|
items,
|
||||||
|
leaving,
|
||||||
|
onLeave,
|
||||||
|
}: PageOverviewProps) {
|
||||||
|
const [ring, setRing] = useState<Ring | "all">("all");
|
||||||
const [search, setSearch] = useState(searchProp);
|
const [search, setSearch] = useState(searchProp);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -40,13 +49,19 @@ export default function PageOverview({ rings, search: searchProp, items, leaving
|
|||||||
|
|
||||||
const isRingActive = (ringName: string) => ring === ringName;
|
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) => {
|
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 getFilteredAndGroupedItems = () => {
|
||||||
const groups = groupByFirstLetter(items);
|
const groups = groupByFirstLetter(items);
|
||||||
@@ -54,7 +69,9 @@ export default function PageOverview({ rings, search: searchProp, items, leaving
|
|||||||
...group,
|
...group,
|
||||||
items: group.items.filter(isItemVisible),
|
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;
|
return nonEmptyGroups;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,20 +81,24 @@ export default function PageOverview({ rings, search: searchProp, items, leaving
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||||
<SetTitle title='Technologies Overview' />
|
<SetTitle title="Technologies Overview" />
|
||||||
<HeadlineGroup>
|
<HeadlineGroup>
|
||||||
<HeroHeadline>Technologies Overview</HeroHeadline>
|
<HeroHeadline>Technologies Overview</HeroHeadline>
|
||||||
</HeadlineGroup>
|
</HeadlineGroup>
|
||||||
<div className='filter'>
|
<div className="filter">
|
||||||
<div className='split split--filter'>
|
<div className="split split--filter">
|
||||||
<div className='split__left'>
|
<div className="split__left">
|
||||||
<Search onChange={handleSearchTermChange} value={search} />
|
<Search onChange={handleSearchTermChange} value={search} />
|
||||||
</div>
|
</div>
|
||||||
<div className='split__right'>
|
<div className="split__right">
|
||||||
<div className='nav'>
|
<div className="nav">
|
||||||
{rings.map((ringName) => (
|
{rings.map((ringName) => (
|
||||||
<div className='nav__item' key={ringName}>
|
<div className="nav__item" key={ringName}>
|
||||||
<Badge big onClick={handleRingClick(ringName)} type={isRingActive(ringName) ? ringName : 'empty'}>
|
<Badge
|
||||||
|
big
|
||||||
|
onClick={handleRingClick(ringName)}
|
||||||
|
type={isRingActive(ringName) ? ringName : "empty"}
|
||||||
|
>
|
||||||
{ringName}
|
{ringName}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
@@ -87,30 +108,32 @@ export default function PageOverview({ rings, search: searchProp, items, leaving
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='letter-index'>
|
<div className="letter-index">
|
||||||
{groups.map(({ letter, items }) => (
|
{groups.map(({ letter, items }) => (
|
||||||
<div key={letter} className='letter-index__group'>
|
<div key={letter} className="letter-index__group">
|
||||||
<div className='letter-index__letter'>{letter}</div>
|
<div className="letter-index__letter">{letter}</div>
|
||||||
<div className='letter-index__items'>
|
<div className="letter-index__items">
|
||||||
<div className='item-list'>
|
<div className="item-list">
|
||||||
<div className='item-list__list'>
|
<div className="item-list__list">
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<Link
|
<Link
|
||||||
key={item.name}
|
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}`}
|
pageName={`${item.quadrant}/${item.name}`}
|
||||||
>
|
>
|
||||||
<div className='split split--overview'>
|
<div className="split split--overview">
|
||||||
<div className='split__left'>
|
<div className="split__left">
|
||||||
<div className='item__title'>
|
<div className="item__title">
|
||||||
{item.title}
|
{item.title}
|
||||||
<Flag item={item} />
|
<Flag item={item} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='split__right'>
|
<div className="split__right">
|
||||||
<div className='nav nav--relations'>
|
<div className="nav nav--relations">
|
||||||
<div className='nav__item'>{translate(item.quadrant)}</div>
|
<div className="nav__item">
|
||||||
<div className='nav__item'>
|
{translate(item.quadrant)}
|
||||||
|
</div>
|
||||||
|
<div className="nav__item">
|
||||||
<Badge type={item.ring}>{item.ring}</Badge>
|
<Badge type={item.ring}>{item.ring}</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import HeroHeadline from '../HeroHeadline/HeroHeadline';
|
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||||
import HeadlineGroup from '../HeadlineGroup/HeadlineGroup';
|
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
||||||
import QuadrantSection from '../QuadrantSection/QuadrantSection';
|
import QuadrantSection from "../QuadrantSection/QuadrantSection";
|
||||||
import Fadeable from '../Fadeable/Fadeable';
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
import SetTitle from '../SetTitle';
|
import SetTitle from "../SetTitle";
|
||||||
|
|
||||||
import { translate } from '../../config';
|
import { translate } from "../../config";
|
||||||
import { featuredOnly, groupByQuadrants, Item } from '../../model';
|
import { featuredOnly, groupByQuadrants, Item } from "../../model";
|
||||||
|
|
||||||
type PageQuadrantProps = {
|
type PageQuadrantProps = {
|
||||||
leaving: boolean;
|
leaving: boolean;
|
||||||
@@ -15,7 +15,12 @@ type PageQuadrantProps = {
|
|||||||
items: Item[];
|
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));
|
const groups = groupByQuadrants(featuredOnly(items));
|
||||||
return (
|
return (
|
||||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||||
|
|||||||
@@ -1,21 +1,34 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import HeroHeadline from '../HeroHeadline/HeroHeadline';
|
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||||
import Fadeable from '../Fadeable/Fadeable';
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
import SetTitle from '../SetTitle';
|
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 (
|
return (
|
||||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||||
<SetTitle title='Small AOE Toolbox' />
|
<SetTitle title="Small AOE Toolbox" />
|
||||||
<HeroHeadline>Small AOE Toolbox</HeroHeadline>
|
<HeroHeadline>Small AOE Toolbox</HeroHeadline>
|
||||||
<div className='fullpage-content'>
|
<div className="fullpage-content">
|
||||||
<h3>Useful Tools</h3>
|
<h3>Useful Tools</h3>
|
||||||
|
|
||||||
<ul>
|
<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>SoapUI - Webservice Test Tool (https://www.soapui.org/ )</li>
|
||||||
<li>Postman - API Test Tool ( https://www.getpostman.com/ )</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>
|
</ul>
|
||||||
|
|
||||||
<h3>Useful Tools (commercial)</h3>
|
<h3>Useful Tools (commercial)</h3>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { groupByQuadrants, Item, Group } from '../../model';
|
import { groupByQuadrants, Item, Group } from "../../model";
|
||||||
import { quadrants } from '../../config';
|
import { quadrants } from "../../config";
|
||||||
import QuadrantSection from '../QuadrantSection/QuadrantSection';
|
import QuadrantSection from "../QuadrantSection/QuadrantSection";
|
||||||
import './quadrant-grid.scss';
|
import "./quadrant-grid.scss";
|
||||||
const renderQuadrant = (quadrantName: string, groups: Group) => {
|
const renderQuadrant = (quadrantName: string, groups: Group) => {
|
||||||
return (
|
return (
|
||||||
<div key={quadrantName} className='quadrant-grid__quadrant'>
|
<div key={quadrantName} className="quadrant-grid__quadrant">
|
||||||
<QuadrantSection quadrantName={quadrantName} groups={groups} />
|
<QuadrantSection quadrantName={quadrantName} groups={groups} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -13,5 +13,9 @@ const renderQuadrant = (quadrantName: string, groups: Group) => {
|
|||||||
|
|
||||||
export default function QuadrantGrid({ items }: { items: Item[] }) {
|
export default function QuadrantGrid({ items }: { items: Item[] }) {
|
||||||
const groups = groupByQuadrants(items);
|
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 {
|
.quadrant-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { translate, rings, Ring, showEmptyRings } from '../../config';
|
import { translate, rings, Ring, showEmptyRings } from "../../config";
|
||||||
import Badge from '../Badge/Badge';
|
import Badge from "../Badge/Badge";
|
||||||
import Link from '../Link/Link';
|
import Link from "../Link/Link";
|
||||||
import ItemList from '../ItemList/ItemList';
|
import ItemList from "../ItemList/ItemList";
|
||||||
import Flag from '../Flag/Flag';
|
import Flag from "../Flag/Flag";
|
||||||
import { Group } from '../../model';
|
import { Group } from "../../model";
|
||||||
import './quadrant-section.scss';
|
import "./quadrant-section.scss";
|
||||||
const renderList = (ringName: Ring, quadrantName: string, groups: Group, big: boolean) => {
|
const renderList = (
|
||||||
|
ringName: Ring,
|
||||||
|
quadrantName: string,
|
||||||
|
groups: Group,
|
||||||
|
big: boolean
|
||||||
|
) => {
|
||||||
const itemsInRing = groups[quadrantName][ringName] || [];
|
const itemsInRing = groups[quadrantName][ringName] || [];
|
||||||
|
|
||||||
if (big) {
|
if (big) {
|
||||||
@@ -20,13 +25,13 @@ const renderList = (ringName: Ring, quadrantName: string, groups: Group, big: bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='ring-list'>
|
<div className="ring-list">
|
||||||
<div className='ring-list__header'>
|
<div className="ring-list__header">
|
||||||
<Badge type={ringName}>{ringName}</Badge>
|
<Badge type={ringName}>{ringName}</Badge>
|
||||||
</div>
|
</div>
|
||||||
{itemsInRing.map((item) => (
|
{itemsInRing.map((item) => (
|
||||||
<span key={item.name} className='ring-list__item'>
|
<span key={item.name} className="ring-list__item">
|
||||||
<Link className='link' pageName={`${item.quadrant}/${item.name}`}>
|
<Link className="link" pageName={`${item.quadrant}/${item.name}`}>
|
||||||
{item.title}
|
{item.title}
|
||||||
<Flag item={item} short />
|
<Flag item={item} short />
|
||||||
</Link>
|
</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) => {
|
const renderRing = (
|
||||||
if (!showEmptyRings && (!groups[quadrantName] || !groups[quadrantName][ringName] || groups[quadrantName][ringName].length === 0)) {
|
ringName: Ring,
|
||||||
|
quadrantName: string,
|
||||||
|
groups: Group,
|
||||||
|
big: boolean
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
!showEmptyRings &&
|
||||||
|
(!groups[quadrantName] ||
|
||||||
|
!groups[quadrantName][ringName] ||
|
||||||
|
groups[quadrantName][ringName].length === 0)
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div key={ringName} className='quadrant-section__ring'>
|
<div key={ringName} className="quadrant-section__ring">
|
||||||
{renderList(ringName, quadrantName, groups, big)}
|
{renderList(ringName, quadrantName, groups, big)}
|
||||||
</div>
|
</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 (
|
return (
|
||||||
<div className='quadrant-section'>
|
<div className="quadrant-section">
|
||||||
<div className='quadrant-section__header'>
|
<div className="quadrant-section__header">
|
||||||
<div className='split'>
|
<div className="split">
|
||||||
<div className='split__left'>
|
<div className="split__left">
|
||||||
<h4 className='headline'>{translate(quadrantName)}</h4>
|
<h4 className="headline">{translate(quadrantName)}</h4>
|
||||||
</div>
|
</div>
|
||||||
{!big && (
|
{!big && (
|
||||||
<div className='split__right'>
|
<div className="split__right">
|
||||||
<Link className='icon-link' pageName={`${quadrantName}`}>
|
<Link className="icon-link" pageName={`${quadrantName}`}>
|
||||||
<span className='icon icon--pie icon-link__icon' />
|
<span className="icon icon--pie icon-link__icon" />
|
||||||
Zoom In
|
Zoom In
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@import '../../styles/sccs-vars.scss';
|
@import "../../styles/sccs-vars.scss";
|
||||||
|
|
||||||
.quadrant-section {
|
.quadrant-section {
|
||||||
&__header {
|
&__header {
|
||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
@media (max-width: $until-md) {
|
@media (max-width: $until-md) {
|
||||||
flex-basis: 50%;
|
flex-basis: 50%;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,24 @@
|
|||||||
import React, {useState, useEffect} from 'react';
|
import React, { useState, useEffect } from "react";
|
||||||
import PageIndex from './PageIndex/PageIndex';
|
import PageIndex from "./PageIndex/PageIndex";
|
||||||
import PageOverview from './PageOverview/PageOverview';
|
import PageOverview from "./PageOverview/PageOverview";
|
||||||
import PageHelp from './PageHelp/PageHelp';
|
import PageHelp from "./PageHelp/PageHelp";
|
||||||
import PageQuadrant from './PageQuadrant/PageQuadrant';
|
import PageQuadrant from "./PageQuadrant/PageQuadrant";
|
||||||
import PageItem from './PageItem/PageItem';
|
import PageItem from "./PageItem/PageItem";
|
||||||
import PageItemMobile from './PageItemMobile/PageItemMobile';
|
import PageItemMobile from "./PageItemMobile/PageItemMobile";
|
||||||
import {quadrants, getItemPageNames, isMobileViewport, rings} from '../config';
|
import {
|
||||||
import {Item} from '../model';
|
quadrants,
|
||||||
|
getItemPageNames,
|
||||||
|
isMobileViewport,
|
||||||
|
rings,
|
||||||
|
} from "../config";
|
||||||
|
import { Item } from "../model";
|
||||||
|
|
||||||
type RouterProps = {
|
type RouterProps = {
|
||||||
pageName: string
|
pageName: string;
|
||||||
items: Item[]
|
items: Item[];
|
||||||
releases: string[]
|
releases: string[];
|
||||||
search: string
|
search: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
enum page {
|
enum page {
|
||||||
index,
|
index,
|
||||||
@@ -26,13 +31,13 @@ enum page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getPageByName = (items: Item[], pageName: string): page => {
|
const getPageByName = (items: Item[], pageName: string): page => {
|
||||||
if (pageName === 'index') {
|
if (pageName === "index") {
|
||||||
return page.index;
|
return page.index;
|
||||||
}
|
}
|
||||||
if (pageName === 'overview') {
|
if (pageName === "overview") {
|
||||||
return page.overview;
|
return page.overview;
|
||||||
}
|
}
|
||||||
if (pageName === 'help-and-about-tech-radar') {
|
if (pageName === "help-and-about-tech-radar") {
|
||||||
return page.help;
|
return page.help;
|
||||||
}
|
}
|
||||||
if (quadrants.includes(pageName)) {
|
if (quadrants.includes(pageName)) {
|
||||||
@@ -45,13 +50,19 @@ const getPageByName = (items: Item[], pageName: string): page => {
|
|||||||
return page.notFound;
|
return page.notFound;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Router({pageName, items, releases, search}: RouterProps) {
|
export default function Router({
|
||||||
|
pageName,
|
||||||
|
items,
|
||||||
|
releases,
|
||||||
|
search,
|
||||||
|
}: RouterProps) {
|
||||||
const [statePageName, setStatePageName] = useState(pageName);
|
const [statePageName, setStatePageName] = useState(pageName);
|
||||||
const [leaving, setLeaving] = useState(false);
|
const [leaving, setLeaving] = useState(false);
|
||||||
const [nextPageName, setNextPageName] = useState<string>('');
|
const [nextPageName, setNextPageName] = useState<string>("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const nowLeaving = getPageByName(items, pageName) !== getPageByName(items, statePageName);
|
const nowLeaving =
|
||||||
|
getPageByName(items, pageName) !== getPageByName(items, statePageName);
|
||||||
if (nowLeaving) {
|
if (nowLeaving) {
|
||||||
setLeaving(true);
|
setLeaving(true);
|
||||||
setNextPageName(pageName);
|
setNextPageName(pageName);
|
||||||
@@ -62,7 +73,7 @@ export default function Router({pageName, items, releases, search}: RouterProps)
|
|||||||
|
|
||||||
const handlePageLeave = () => {
|
const handlePageLeave = () => {
|
||||||
setStatePageName(nextPageName);
|
setStatePageName(nextPageName);
|
||||||
setNextPageName('');
|
setNextPageName("");
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
@@ -73,19 +84,54 @@ export default function Router({pageName, items, releases, search}: RouterProps)
|
|||||||
|
|
||||||
switch (getPageByName(items, statePageName)) {
|
switch (getPageByName(items, statePageName)) {
|
||||||
case page.index:
|
case page.index:
|
||||||
return <PageIndex leaving={leaving} items={items} onLeave={handlePageLeave} releases={releases}/>;
|
return (
|
||||||
|
<PageIndex
|
||||||
|
leaving={leaving}
|
||||||
|
items={items}
|
||||||
|
onLeave={handlePageLeave}
|
||||||
|
releases={releases}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case page.overview:
|
case page.overview:
|
||||||
return <PageOverview items={items} rings={rings} search={search} leaving={leaving}
|
return (
|
||||||
onLeave={handlePageLeave}/>;
|
<PageOverview
|
||||||
|
items={items}
|
||||||
|
rings={rings}
|
||||||
|
search={search}
|
||||||
|
leaving={leaving}
|
||||||
|
onLeave={handlePageLeave}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case page.help:
|
case page.help:
|
||||||
return <PageHelp leaving={leaving} onLeave={handlePageLeave}/>;
|
return <PageHelp leaving={leaving} onLeave={handlePageLeave} />;
|
||||||
case page.quadrant:
|
case page.quadrant:
|
||||||
return <PageQuadrant leaving={leaving} onLeave={handlePageLeave} items={items} pageName={statePageName}/>;
|
return (
|
||||||
|
<PageQuadrant
|
||||||
|
leaving={leaving}
|
||||||
|
onLeave={handlePageLeave}
|
||||||
|
items={items}
|
||||||
|
pageName={statePageName}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case page.itemMobile:
|
case page.itemMobile:
|
||||||
return <PageItemMobile items={items} pageName={statePageName} leaving={leaving} onLeave={handlePageLeave}/>;
|
return (
|
||||||
|
<PageItemMobile
|
||||||
|
items={items}
|
||||||
|
pageName={statePageName}
|
||||||
|
leaving={leaving}
|
||||||
|
onLeave={handlePageLeave}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case page.item:
|
case page.item:
|
||||||
return <PageItem items={items} pageName={statePageName} leaving={leaving} onLeave={handlePageLeave}/>;
|
return (
|
||||||
|
<PageItem
|
||||||
|
items={items}
|
||||||
|
pageName={statePageName}
|
||||||
|
leaving={leaving}
|
||||||
|
onLeave={handlePageLeave}
|
||||||
|
/>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return <div/>;
|
return <div />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { FormEvent } from 'react';
|
import React, { FormEvent } from "react";
|
||||||
import classNames from 'classnames';
|
import classNames from "classnames";
|
||||||
import './search.scss';
|
import "./search.scss";
|
||||||
|
|
||||||
type SearchProps = {
|
type SearchProps = {
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
@@ -14,7 +14,10 @@ export default React.forwardRef((props: SearchProps, ref) => {
|
|||||||
return Search(props, 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 closable = onClose !== undefined;
|
||||||
|
|
||||||
const handleSubmit = (e: FormEvent) => {
|
const handleSubmit = (e: FormEvent) => {
|
||||||
@@ -30,26 +33,34 @@ function Search({ value, onChange, onClose, open = false, onSubmit = () => {} }:
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className={classNames('search', { 'search--closable': closable })} onSubmit={handleSubmit}>
|
<form
|
||||||
|
className={classNames("search", { "search--closable": closable })}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
value={value}
|
value={value}
|
||||||
type='text'
|
type="text"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onChange(e.target.value);
|
onChange(e.target.value);
|
||||||
}}
|
}}
|
||||||
className='search__field'
|
className="search__field"
|
||||||
placeholder='What are you looking for?'
|
placeholder="What are you looking for?"
|
||||||
ref={ref}
|
ref={ref}
|
||||||
/>
|
/>
|
||||||
<span className={classNames('search__button', { 'is-open': open })}>
|
<span className={classNames("search__button", { "is-open": open })}>
|
||||||
<button type='submit' className='button'>
|
<button type="submit" className="button">
|
||||||
<span className='icon icon--search button__icon' />
|
<span className="icon icon--search button__icon" />
|
||||||
Search
|
Search
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
{closable && (
|
{closable && (
|
||||||
<button className={classNames('search__close link-button', { 'is-open': open })} onClick={handleClose}>
|
<button
|
||||||
<span className='icon icon--close' />
|
className={classNames("search__close link-button", {
|
||||||
|
"is-open": open,
|
||||||
|
})}
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
<span className="icon icon--close" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -11,13 +11,13 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 10px 120px 10px 20px;
|
padding: 10px 120px 10px 20px;
|
||||||
background: #3A444A;
|
background: #3a444a;
|
||||||
display: block;
|
display: block;
|
||||||
border: none;
|
border: none;
|
||||||
color: var(--color-white);
|
color: var(--color-white);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-family: 'DIN';
|
font-family: "DIN";
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
background: #2F393F;
|
background: #2f393f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import {useEffect} from "react";
|
import { useEffect } from "react";
|
||||||
import {radarName} from "../config";
|
import { radarName } from "../config";
|
||||||
|
|
||||||
type SetTitleProps = {
|
type SetTitleProps = {
|
||||||
title: string
|
title: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function SetTitle({title}: SetTitleProps) {
|
export default function SetTitle({ title }: SetTitleProps) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = `${title} | ${radarName}`
|
document.title = `${title} | ${radarName}`;
|
||||||
}, [title])
|
}, [title]);
|
||||||
|
|
||||||
return null;
|
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-face {
|
||||||
font-family: 'DIN';
|
font-family: "DIN";
|
||||||
src: url('./fonts/clanot-news.otf');
|
src: url("./fonts/clanot-news.otf");
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'DIN';
|
font-family: "DIN";
|
||||||
src: url('./fonts/clanot-thin.otf');
|
src: url("./fonts/clanot-thin.otf");
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--color-gray-dark: #475157;
|
--color-gray-dark: #475157;
|
||||||
--color-gray-dark-alt: #4F585E;
|
--color-gray-dark-alt: #4f585e;
|
||||||
--color-gray-dark-alt2: #434D53;
|
--color-gray-dark-alt2: #434d53;
|
||||||
--color-gray-normal: #7f858a;
|
--color-gray-normal: #7f858a;
|
||||||
--color-gray-light: #7D878D;
|
--color-gray-light: #7d878d;
|
||||||
|
|
||||||
--color-white: #fff;
|
--color-white: #fff;
|
||||||
--color-green: #5CB449;
|
--color-green: #5cb449;
|
||||||
--color-orange: #FAA03D;
|
--color-orange: #faa03d;
|
||||||
--color-blue: #40A7D1;
|
--color-blue: #40a7d1;
|
||||||
--color-marine: #688190;
|
--color-marine: #688190;
|
||||||
--color-red: #F1235A;
|
--color-red: #f1235a;
|
||||||
--color-brand: #F59134;
|
--color-brand: #f59134;
|
||||||
--until-sm: 30em;
|
--until-sm: 30em;
|
||||||
--until-md: 48em;
|
--until-md: 48em;
|
||||||
--until-lg: 61.875em;
|
--until-lg: 61.875em;
|
||||||
@@ -36,14 +35,12 @@
|
|||||||
// @custom-media --until-lg (max-width: 61.875em);
|
// @custom-media --until-lg (max-width: 61.875em);
|
||||||
// @custom-media --until-xl (max-width: 75em);
|
// @custom-media --until-xl (max-width: 75em);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--color-gray-dark);
|
background: var(--color-gray-dark);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-family: 'DIN';
|
font-family: "DIN";
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
@@ -58,4 +55,4 @@ body {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@import './styles/main.scss';
|
@import "./styles/main.scss";
|
||||||
|
|||||||
68
src/model.js
68
src/model.js
@@ -1,49 +1,85 @@
|
|||||||
var __assign = (this && this.__assign) || function () {
|
var __assign =
|
||||||
__assign = Object.assign || function(t) {
|
(this && this.__assign) ||
|
||||||
|
function () {
|
||||||
|
__assign =
|
||||||
|
Object.assign ||
|
||||||
|
function (t) {
|
||||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||||
s = arguments[i];
|
s = arguments[i];
|
||||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
for (var p in s)
|
||||||
t[p] = s[p];
|
if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
};
|
};
|
||||||
return __assign.apply(this, arguments);
|
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++)
|
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
|
||||||
to[j] = from[i];
|
to[j] = from[i];
|
||||||
return to;
|
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) {
|
export var groupByQuadrants = function (items) {
|
||||||
return items.reduce(function (quadrants, item) {
|
return items.reduce(function (quadrants, item) {
|
||||||
var _a;
|
var _a;
|
||||||
return (__assign(__assign({}, quadrants), (_a = {}, _a[item.quadrant] = addItemToQuadrant(quadrants[item.quadrant], item), _a)));
|
return __assign(
|
||||||
|
__assign({}, quadrants),
|
||||||
|
((_a = {}),
|
||||||
|
(_a[item.quadrant] = addItemToQuadrant(quadrants[item.quadrant], item)),
|
||||||
|
_a)
|
||||||
|
);
|
||||||
}, {});
|
}, {});
|
||||||
};
|
};
|
||||||
export var groupByFirstLetter = function (items) {
|
export var groupByFirstLetter = function (items) {
|
||||||
var index = items.reduce(function (letterIndex, item) {
|
var index = items.reduce(function (letterIndex, item) {
|
||||||
var _a;
|
var _a;
|
||||||
return (__assign(__assign({}, letterIndex), (_a = {}, _a[getFirstLetter(item)] = addItemToList(letterIndex[getFirstLetter(item)], item), _a)));
|
return __assign(
|
||||||
|
__assign({}, letterIndex),
|
||||||
|
((_a = {}),
|
||||||
|
(_a[getFirstLetter(item)] = addItemToList(
|
||||||
|
letterIndex[getFirstLetter(item)],
|
||||||
|
item
|
||||||
|
)),
|
||||||
|
_a)
|
||||||
|
);
|
||||||
}, {});
|
}, {});
|
||||||
return Object.keys(index)
|
return Object.keys(index)
|
||||||
.sort()
|
.sort()
|
||||||
.map(function (letter) { return ({
|
.map(function (letter) {
|
||||||
|
return {
|
||||||
letter: letter,
|
letter: letter,
|
||||||
items: index[letter],
|
items: index[letter],
|
||||||
}); });
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
var addItemToQuadrant = function (quadrant, item) {
|
var addItemToQuadrant = function (quadrant, item) {
|
||||||
var _a;
|
var _a;
|
||||||
if (quadrant === void 0) { quadrant = {}; }
|
if (quadrant === void 0) {
|
||||||
return (__assign(__assign({}, quadrant), (_a = {}, _a[item.ring] = addItemToRing(quadrant[item.ring], item), _a)));
|
quadrant = {};
|
||||||
|
}
|
||||||
|
return __assign(
|
||||||
|
__assign({}, quadrant),
|
||||||
|
((_a = {}), (_a[item.ring] = addItemToRing(quadrant[item.ring], item)), _a)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
var addItemToList = function (list, item) {
|
var addItemToList = function (list, item) {
|
||||||
if (list === void 0) { list = []; }
|
if (list === void 0) {
|
||||||
|
list = [];
|
||||||
|
}
|
||||||
return __spreadArray(__spreadArray([], list), [item]);
|
return __spreadArray(__spreadArray([], list), [item]);
|
||||||
};
|
};
|
||||||
var addItemToRing = function (ring, item) {
|
var addItemToRing = function (ring, item) {
|
||||||
if (ring === void 0) { ring = []; }
|
if (ring === void 0) {
|
||||||
|
ring = [];
|
||||||
|
}
|
||||||
return __spreadArray(__spreadArray([], ring), [item]);
|
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 = {
|
export type ItemAttributes = {
|
||||||
name: string
|
name: string;
|
||||||
ring: Ring
|
ring: Ring;
|
||||||
quadrant: string
|
quadrant: string;
|
||||||
title: string
|
title: string;
|
||||||
featured: boolean
|
featured: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type FlagType = 'new' | 'changed' | 'default'
|
export type FlagType = "new" | "changed" | "default";
|
||||||
|
|
||||||
export type Item = ItemAttributes & {
|
export type Item = ItemAttributes & {
|
||||||
featured: boolean
|
featured: boolean;
|
||||||
body: string
|
body: string;
|
||||||
info: string
|
info: string;
|
||||||
flag: FlagType
|
flag: FlagType;
|
||||||
revisions: Revision[]
|
revisions: Revision[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Revision = ItemAttributes & {
|
export type Revision = ItemAttributes & {
|
||||||
body: string
|
body: string;
|
||||||
fileName: string
|
fileName: string;
|
||||||
release: string
|
release: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Quadrant = {
|
export type Quadrant = {
|
||||||
[name: string]: Item[]
|
[name: string]: Item[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Radar = {
|
export type Radar = {
|
||||||
items: Item[]
|
items: Item[];
|
||||||
releases: string[]
|
releases: string[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Group = {
|
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 =>
|
export const groupByQuadrants = (items: Item[]): Group =>
|
||||||
items.reduce(
|
items.reduce(
|
||||||
@@ -45,7 +46,7 @@ export const groupByQuadrants = (items: Item[]): Group =>
|
|||||||
...quadrants,
|
...quadrants,
|
||||||
[item.quadrant]: addItemToQuadrant(quadrants[item.quadrant], item),
|
[item.quadrant]: addItemToQuadrant(quadrants[item.quadrant], item),
|
||||||
}),
|
}),
|
||||||
{} as {[k: string]: Quadrant},
|
{} as { [k: string]: Quadrant }
|
||||||
);
|
);
|
||||||
|
|
||||||
export const groupByFirstLetter = (items: Item[]) => {
|
export const groupByFirstLetter = (items: Item[]) => {
|
||||||
@@ -54,15 +55,15 @@ export const groupByFirstLetter = (items: Item[]) => {
|
|||||||
...letterIndex,
|
...letterIndex,
|
||||||
[getFirstLetter(item)]: addItemToList(
|
[getFirstLetter(item)]: addItemToList(
|
||||||
letterIndex[getFirstLetter(item)],
|
letterIndex[getFirstLetter(item)],
|
||||||
item,
|
item
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
{} as {[k: string]: Item[]},
|
{} as { [k: string]: Item[] }
|
||||||
);
|
);
|
||||||
|
|
||||||
return Object.keys(index)
|
return Object.keys(index)
|
||||||
.sort()
|
.sort()
|
||||||
.map(letter => ({
|
.map((letter) => ({
|
||||||
letter,
|
letter,
|
||||||
items: index[letter],
|
items: index[letter],
|
||||||
}));
|
}));
|
||||||
@@ -77,4 +78,5 @@ const addItemToList = (list: Item[] = [], item: Item) => [...list, item];
|
|||||||
|
|
||||||
const addItemToRing = (ring: Item[] = [], item: Item) => [...ring, 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:
|
// allows you to do things like:
|
||||||
// expect(element).toHaveTextContent(/react/i)
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
// 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 {
|
.fullpage-content {
|
||||||
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: var(--color-white);
|
color: var(--color-white);
|
||||||
|
|
||||||
& p,
|
& p,
|
||||||
& ul {
|
& ul {
|
||||||
font-weight: lighter
|
font-weight: lighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
& a {
|
& a {
|
||||||
color: var(--color-white);
|
color: var(--color-white);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
@import '../../styles/sccs-vars.scss';
|
@import "../../styles/sccs-vars.scss";
|
||||||
|
|
||||||
.filter {
|
.filter {
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
|
|||||||
@@ -1,2 +1,61 @@
|
|||||||
/* copied from here: http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.10.0/styles/darcula.min.css*/
|
/* 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 {
|
&:hover {
|
||||||
&:after {
|
&:after {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: -8px;
|
bottom: -8px;
|
||||||
|
|||||||
@@ -8,26 +8,26 @@
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
&--pie {
|
&--pie {
|
||||||
background-image: url('../../icons/pie.svg');
|
background-image: url("../../icons/pie.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
&--question {
|
&--question {
|
||||||
background-image: url('../../icons/question.svg');
|
background-image: url("../../icons/question.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
&--overview {
|
&--overview {
|
||||||
background-image: url('../../icons/overview.svg');
|
background-image: url("../../icons/overview.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
&--search {
|
&--search {
|
||||||
background-image: url('../../icons/search.svg');
|
background-image: url("../../icons/search.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
&--back {
|
&--back {
|
||||||
background-image: url('../../icons/back.svg');
|
background-image: url("../../icons/back.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
&--close {
|
&--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 {
|
.letter-index {
|
||||||
margin-bottom: 60px;
|
margin-bottom: 60px;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@import '../../styles/sccs-vars.scss';
|
@import "../../styles/sccs-vars.scss";
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&+.nav__item {
|
& + .nav__item {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,10 +21,8 @@
|
|||||||
width: 0;
|
width: 0;
|
||||||
margin-top: -25px;
|
margin-top: -25px;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
transition:
|
transition: width 400ms cubic-bezier(0.24, 1.12, 0.71, 0.98) 100ms,
|
||||||
width 400ms cubic-bezier(0.24, 1.12, 0.71, 0.98) 100ms,
|
visibility 0s linear 500ms, opacity 200ms linear;
|
||||||
visibility 0s linear 500ms,
|
|
||||||
opacity 200ms linear;
|
|
||||||
|
|
||||||
&.is-open {
|
&.is-open {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@import '../../styles/sccs-vars.scss';
|
@import "../../styles/sccs-vars.scss";
|
||||||
|
|
||||||
.page {
|
.page {
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|
||||||
@media (max-width: $until-sm) {
|
@media (max-width: $until-sm) {
|
||||||
margin: 0
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $until-lg) {
|
@media (max-width: $until-lg) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@import '../../styles/sccs-vars.scss';
|
@import "../../styles/sccs-vars.scss";
|
||||||
|
|
||||||
.publish-date {
|
.publish-date {
|
||||||
color: var(--color-gray-normal);
|
color: var(--color-gray-normal);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
.social-icon {
|
.social-icon {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -24,6 +23,5 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|
||||||
/* Support for IE. */
|
/* 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 {
|
.split {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user