Merge branch 'main' into separateNonFeaturedArticlesOnQuadrantView
# Conflicts: # .lintstagedrc # dist_scripts/src/model.js # src/animation.ts # src/components/Item/Item.tsx # src/components/ItemList/ItemList.tsx # src/components/PageItem/PageItem.tsx # src/index.scss # src/model.js # src/model.ts
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,6 +11,9 @@
|
||||
# production
|
||||
/build
|
||||
|
||||
# local development
|
||||
/public/rd.json
|
||||
|
||||
# misc
|
||||
.idea
|
||||
.DS_Store
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
yarn ts:check && yarn lint-staged
|
||||
yarn ts:check && yarn lint-staged && yarn build:scripts
|
||||
|
||||
3
.lintstagedrc.json
Normal file
3
.lintstagedrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"*.{json, md, yml, js, ts, tsx}": ["prettier --write"]
|
||||
}
|
||||
44
README.md
44
README.md
@@ -1,36 +1,45 @@
|
||||
# AOE Technology Radar
|
||||
|
||||
A static site generator for AOE Technology Radar
|
||||
|
||||
## Looking for the AOE Tech Radar content?
|
||||
|
||||
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
|
||||
|
||||
## 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.
|
||||
(There is a list of planned features below in case someone wants to contribute :-)
|
||||
|
||||
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
|
||||
|
||||
Add the tech radar as a dependency
|
||||
|
||||
```
|
||||
yarn add https://github.com/aoepeople/aoe_technology_radar.git
|
||||
```
|
||||
|
||||
Generate json file based on md files
|
||||
|
||||
```
|
||||
yarn aoe_technology_radar-generateJson
|
||||
```
|
||||
|
||||
Build the radar
|
||||
|
||||
```
|
||||
yarn aoe_technology_radar-buildRadar
|
||||
```
|
||||
|
||||
Serve
|
||||
|
||||
```
|
||||
cd build
|
||||
python3 -m http.server 8080
|
||||
@@ -39,6 +48,7 @@ python3 -m http.server 8080
|
||||
Then open here: http://localhost:8080
|
||||
|
||||
## 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.
|
||||
|
||||
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.)
|
||||
|
||||
## Customize the tech radar
|
||||
|
||||
You can customize the following parts of the tech radar.
|
||||
|
||||
### Change title, description and headline
|
||||
|
||||
Set the environment variable `REACT_APP_RADAR_NAME`. The default is "AOE Technology Radar".
|
||||
|
||||
### Host the application under a sub path
|
||||
|
||||
To host the application under a sub path, set the environment variable `PUBLIC_URL`, e.g. "/techradar".
|
||||
|
||||
### Change the favicon
|
||||
|
||||
To change the favicon, create a public folder in your application and put your favicon.ico in it.
|
||||
|
||||
### Change the logo
|
||||
|
||||
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).
|
||||
|
||||
### Change the index.html
|
||||
|
||||
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).
|
||||
|
||||
## Usage
|
||||
|
||||
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
|
||||
quadrant and place the items there.
|
||||
|
||||
### Maintaining items
|
||||
|
||||
The items are written in Markdown format (.md)
|
||||
|
||||
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:
|
||||
|
||||
* **title**: Name of the Item
|
||||
* **quadrant**: Quadrant. One of `languages-and-frameworks`,
|
||||
- **title**: Name of the Item
|
||||
- **quadrant**: Quadrant. One of `languages-and-frameworks`,
|
||||
`methods-and-patterns`, `platforms-and-aoe-services`, `tools`
|
||||
* **ring**: Ring section in radar. One of `trial`, `assess`, `adopt`, `hold`
|
||||
* **info**: (optional) A short textual description of the item (visible in
|
||||
- **ring**: Ring section in radar. One of `trial`, `assess`, `adopt`, `hold`
|
||||
- **info**: (optional) A short textual description of the item (visible in
|
||||
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.
|
||||
|
||||
The name of the .md file acts as item identifier and may overwrite items with
|
||||
@@ -111,11 +129,23 @@ 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.
|
||||
|
||||
You can integrate images in your markdown. Put the image files in your public folder and reference them
|
||||
|
||||
```
|
||||

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