Add support for changed items
This commit is contained in:
@@ -14,7 +14,7 @@ export const createRadar = async tree => {
|
|||||||
const revisions = await createRevisionsFromFiles(fileNames);
|
const revisions = await createRevisionsFromFiles(fileNames);
|
||||||
const allReleases = getAllReleases(revisions);
|
const allReleases = getAllReleases(revisions);
|
||||||
const items = createItems(revisions);
|
const items = createItems(revisions);
|
||||||
const flaggedItems = flagWithIsNew(items, allReleases);
|
const flaggedItems = flagItem(items, allReleases);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: flaggedItems,
|
items: flaggedItems,
|
||||||
@@ -26,7 +26,9 @@ const checkAttributes = (fileName, attributes) => {
|
|||||||
const rings = ['adopt', 'trial', 'assess', 'hold'];
|
const rings = ['adopt', 'trial', 'assess', 'hold'];
|
||||||
if (attributes.ring && !rings.includes(attributes.ring)) {
|
if (attributes.ring && !rings.includes(attributes.ring)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error: ${fileName} has an illegal value for 'ring' - must be one of ${rings}`,
|
`Error: ${fileName} has an illegal value for 'ring' - must be one of ${
|
||||||
|
rings
|
||||||
|
}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +40,9 @@ const checkAttributes = (fileName, attributes) => {
|
|||||||
];
|
];
|
||||||
if (attributes.quadrant && !quadrants.includes(attributes.quadrant)) {
|
if (attributes.quadrant && !quadrants.includes(attributes.quadrant)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error: ${fileName} has an illegal value for 'quadrant' - must be one of ${quadrants}`,
|
`Error: ${
|
||||||
|
fileName
|
||||||
|
} has an illegal value for 'quadrant' - must be one of ${quadrants}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -111,7 +115,7 @@ const createItems = revisions => {
|
|||||||
|
|
||||||
const addRevisionToItem = (
|
const addRevisionToItem = (
|
||||||
item = {
|
item = {
|
||||||
isNew: false,
|
flag: 'default',
|
||||||
isFeatured: true,
|
isFeatured: true,
|
||||||
revisions: [],
|
revisions: [],
|
||||||
},
|
},
|
||||||
@@ -141,15 +145,30 @@ const revisionCreatesNewHistoryEntry = revision => {
|
|||||||
return revision.body.trim() !== '' || typeof revision.ring !== 'undefined';
|
return revision.body.trim() !== '' || typeof revision.ring !== 'undefined';
|
||||||
};
|
};
|
||||||
|
|
||||||
const flagWithIsNew = (items, allReleases) =>
|
const flagItem = (items, allReleases) =>
|
||||||
items.map(
|
items.map(
|
||||||
item => ({
|
item => ({
|
||||||
...item,
|
...item,
|
||||||
isNew: isNewItem(item, allReleases),
|
flag: getItemFlag(item, allReleases),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const isNewItem = (item, allReleases) =>
|
const isInLastRelease = (item, allReleases) =>
|
||||||
item.revisions.length === 0 ||
|
|
||||||
item.revisions[0].release === allReleases[allReleases.length - 1];
|
item.revisions[0].release === allReleases[allReleases.length - 1];
|
||||||
|
|
||||||
|
const isNewItem = (item, allReleases) =>
|
||||||
|
item.revisions.length === 1 && isInLastRelease(item, allReleases);
|
||||||
|
|
||||||
|
const hasItemChanged = (item, allReleases) =>
|
||||||
|
item.revisions.length > 1 && isInLastRelease(item, allReleases);
|
||||||
|
|
||||||
|
const getItemFlag = (item, allReleases) => {
|
||||||
|
if (isNewItem(item, allReleases)) {
|
||||||
|
return 'new';
|
||||||
|
}
|
||||||
|
if (hasItemChanged(item, allReleases)) {
|
||||||
|
return 'changed';
|
||||||
|
}
|
||||||
|
return 'default';
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default function IsNew({ item }) {
|
|
||||||
if (item.isNew) {
|
|
||||||
return <span className="is-new">NEW</span>;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Link from './Link';
|
import Link from './Link';
|
||||||
import IsNew from './IsNew';
|
import Tag from './Tag';
|
||||||
|
|
||||||
export default function Item({ item, noLeadingBorder = false, active = false, style = {}}) {
|
export default function Item({
|
||||||
|
item,
|
||||||
|
noLeadingBorder = false,
|
||||||
|
active = false,
|
||||||
|
style = {},
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
className={classNames('item', {
|
className={classNames('item', {
|
||||||
@@ -15,13 +20,9 @@ export default function Item({ item, noLeadingBorder = false, active = false, st
|
|||||||
>
|
>
|
||||||
<div className="item__title">
|
<div className="item__title">
|
||||||
{item.title}
|
{item.title}
|
||||||
<IsNew item={item} />
|
<Tag item={item} />
|
||||||
</div>
|
</div>
|
||||||
{
|
{item.info && <div className="item__info">{item.info}</div>}
|
||||||
item.info && (
|
|
||||||
<div className="item__info">{item.info}</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import Link from './Link';
|
|||||||
import FooterEnd from './FooterEnd';
|
import FooterEnd from './FooterEnd';
|
||||||
import SetTitle from './SetTitle';
|
import SetTitle from './SetTitle';
|
||||||
import ItemRevisions from './ItemRevisions';
|
import ItemRevisions from './ItemRevisions';
|
||||||
import IsNew from './IsNew';
|
|
||||||
import { createAnimation, createAnimationRunner } from '../animation';
|
import { createAnimation, createAnimationRunner } from '../animation';
|
||||||
|
|
||||||
import { translate } from '../../common/config';
|
import { translate } from '../../common/config';
|
||||||
@@ -23,55 +22,67 @@ class PageItem extends React.Component {
|
|||||||
const itemsInRing = this.getItemsInRing(props);
|
const itemsInRing = this.getItemsInRing(props);
|
||||||
|
|
||||||
this.animationsIn = {
|
this.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) => (createAnimation({
|
items: itemsInRing.map((item, i) =>
|
||||||
|
createAnimation(
|
||||||
|
{
|
||||||
|
transform: 'translateX(-40px)',
|
||||||
|
opacity: '0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||||
|
transform: 'translateX(0px)',
|
||||||
|
opacity: '1',
|
||||||
|
},
|
||||||
|
400 + 100 * i,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
footer: createAnimation(
|
||||||
|
{
|
||||||
|
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||||
transform: 'translateX(-40px)',
|
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
|
600 + itemsInRing.length * 100,
|
||||||
))),
|
|
||||||
footer: createAnimation({
|
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
|
||||||
transform: 'translateX(-40px)',
|
|
||||||
opacity: '0',
|
|
||||||
}, {
|
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
|
||||||
transform: 'translateX(0px)',
|
|
||||||
opacity: '1',
|
|
||||||
},
|
|
||||||
600 + itemsInRing.length * 100
|
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,7 +90,7 @@ class PageItem extends React.Component {
|
|||||||
background: createAnimation(
|
background: createAnimation(
|
||||||
this.animationsIn.background.stateB,
|
this.animationsIn.background.stateB,
|
||||||
this.animationsIn.background.stateA,
|
this.animationsIn.background.stateA,
|
||||||
300 + itemsInRing.length * 50
|
300 + itemsInRing.length * 50,
|
||||||
),
|
),
|
||||||
navHeader: createAnimation(
|
navHeader: createAnimation(
|
||||||
this.animationsIn.navHeader.stateB,
|
this.animationsIn.navHeader.stateB,
|
||||||
@@ -88,7 +99,7 @@ class PageItem extends React.Component {
|
|||||||
transform: 'translateX(40px)',
|
transform: 'translateX(40px)',
|
||||||
opacity: '0',
|
opacity: '0',
|
||||||
},
|
},
|
||||||
0
|
0,
|
||||||
),
|
),
|
||||||
text: createAnimation(
|
text: createAnimation(
|
||||||
this.animationsIn.text.stateB,
|
this.animationsIn.text.stateB,
|
||||||
@@ -97,43 +108,58 @@ class PageItem extends React.Component {
|
|||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||||
opacity: '0',
|
opacity: '0',
|
||||||
},
|
},
|
||||||
0
|
0,
|
||||||
),
|
),
|
||||||
items: itemsInRing.map((item, i) => (createAnimation(
|
items: itemsInRing.map((item, i) =>
|
||||||
this.animationsIn.items[i].stateB,
|
createAnimation(
|
||||||
{
|
this.animationsIn.items[i].stateB,
|
||||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
|
||||||
transform: 'translateX(40px)',
|
|
||||||
opacity: '0',
|
|
||||||
},
|
|
||||||
100 + 50 * i
|
|
||||||
))),
|
|
||||||
footer: createAnimation(
|
|
||||||
this.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
|
100 + 50 * i,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
footer: createAnimation(
|
||||||
|
this.animationsIn.text.stateB,
|
||||||
|
{
|
||||||
|
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||||
|
transform: 'translateX(40px)',
|
||||||
|
opacity: '0',
|
||||||
|
},
|
||||||
|
200 + itemsInRing.length * 50,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (props.leaving) { // entering from an other page
|
if (props.leaving) {
|
||||||
this.state = setAnimations({}, createAnimationRunner(this.animationsIn).getState());
|
// entering from an other page
|
||||||
} else { // Hard refresh
|
this.state = setAnimations(
|
||||||
|
{},
|
||||||
|
createAnimationRunner(this.animationsIn).getState(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Hard refresh
|
||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps({ leaving }) {
|
componentWillReceiveProps({ leaving }) {
|
||||||
if (!this.props.leaving && leaving) { // page will be left
|
if (!this.props.leaving && leaving) {
|
||||||
this.animationRunner = createAnimationRunner(this.animationsOut, this.handleAnimationsUpdate);
|
// page will be left
|
||||||
|
this.animationRunner = createAnimationRunner(
|
||||||
|
this.animationsOut,
|
||||||
|
this.handleAnimationsUpdate,
|
||||||
|
);
|
||||||
this.animationRunner.run();
|
this.animationRunner.run();
|
||||||
this.animationRunner.awaitAnimationComplete(this.props.onLeave);
|
this.animationRunner.awaitAnimationComplete(this.props.onLeave);
|
||||||
}
|
}
|
||||||
if (this.props.leaving && !leaving) { // page is entered
|
if (this.props.leaving && !leaving) {
|
||||||
this.animationRunner = createAnimationRunner(this.animationsIn, this.handleAnimationsUpdate);
|
// page is entered
|
||||||
|
this.animationRunner = createAnimationRunner(
|
||||||
|
this.animationsIn,
|
||||||
|
this.handleAnimationsUpdate,
|
||||||
|
);
|
||||||
this.animationRunner.run();
|
this.animationRunner.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,20 +168,22 @@ class PageItem extends React.Component {
|
|||||||
this.setState(setAnimations(this.state, this.animationRunner.getState()));
|
this.setState(setAnimations(this.state, this.animationRunner.getState()));
|
||||||
};
|
};
|
||||||
|
|
||||||
getAnimationState = (name) => {
|
getAnimationState = name => {
|
||||||
if (!this.state.animations) {
|
if (!this.state.animations) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return this.state.animations[name];
|
return this.state.animations[name];
|
||||||
};
|
};
|
||||||
|
|
||||||
getItem = (props) => {
|
getItem = props => {
|
||||||
const [quadrantName, itemName] = props.pageName.split('/');
|
const [quadrantName, itemName] = props.pageName.split('/');
|
||||||
const item = props.items.filter(item => item.quadrant === quadrantName && item.name === itemName)[0];
|
const item = props.items.filter(
|
||||||
|
item => item.quadrant === quadrantName && item.name === itemName,
|
||||||
|
)[0];
|
||||||
return item;
|
return item;
|
||||||
}
|
};
|
||||||
|
|
||||||
getItemsInRing = (props) => {
|
getItemsInRing = props => {
|
||||||
const item = this.getItem(props);
|
const item = this.getItem(props);
|
||||||
const itemsInRing = groupByQuadrants(props.items)[item.quadrant][item.ring];
|
const itemsInRing = groupByQuadrants(props.items)[item.quadrant][item.ring];
|
||||||
return itemsInRing;
|
return itemsInRing;
|
||||||
@@ -170,7 +198,10 @@ class PageItem extends React.Component {
|
|||||||
<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={this.getAnimationState('navHeader')}>
|
<div
|
||||||
|
className="item-page__header"
|
||||||
|
style={this.getAnimationState('navHeader')}
|
||||||
|
>
|
||||||
<h3 className="headline">{translate(item.quadrant)}</h3>
|
<h3 className="headline">{translate(item.quadrant)}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -182,34 +213,55 @@ class PageItem extends React.Component {
|
|||||||
>
|
>
|
||||||
<div className="split">
|
<div className="split">
|
||||||
<div className="split__left">
|
<div className="split__left">
|
||||||
<Badge big type={item.ring}>{item.ring}</Badge>
|
<Badge big type={item.ring}>
|
||||||
|
{item.ring}
|
||||||
|
</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>Quadrant Overview
|
<span className="icon icon--pie icon-link__icon" />Quadrant
|
||||||
|
Overview
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ItemList>
|
</ItemList>
|
||||||
<div className="item-page__footer" style={this.getAnimationState('footer')}>
|
<div
|
||||||
|
className="item-page__footer"
|
||||||
|
style={this.getAnimationState('footer')}
|
||||||
|
>
|
||||||
<FooterEnd modifier="in-sidebar" />
|
<FooterEnd modifier="in-sidebar" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="item-page__content" style={this.getAnimationState('background')}>
|
<div
|
||||||
<div className="item-page__content__inner" style={this.getAnimationState('text')}>
|
className="item-page__content"
|
||||||
|
style={this.getAnimationState('background')}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="item-page__content__inner"
|
||||||
|
style={this.getAnimationState('text')}
|
||||||
|
>
|
||||||
<div className="item-page__header">
|
<div className="item-page__header">
|
||||||
<div className="split">
|
<div className="split">
|
||||||
<div className="split__left">
|
<div className="split__left">
|
||||||
<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}>{item.ring}</Badge>
|
<Badge big type={item.ring}>
|
||||||
|
{item.ring}
|
||||||
|
</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>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import Link from './Link';
|
|||||||
import Search from './Search';
|
import Search from './Search';
|
||||||
import Fadeable from './Fadeable';
|
import Fadeable from './Fadeable';
|
||||||
import SetTitle from './SetTitle';
|
import SetTitle from './SetTitle';
|
||||||
import IsNew from './IsNew';
|
import Tag from './Tag';
|
||||||
import { groupByFirstLetter } from '../../common/model';
|
import { groupByFirstLetter } from '../../common/model';
|
||||||
import { translate } from '../../common/config';
|
import { translate } from '../../common/config';
|
||||||
|
|
||||||
@@ -15,11 +15,15 @@ const rings = ['all', 'assess', 'trial', 'hold', 'adopt'];
|
|||||||
|
|
||||||
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
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
class PageOverview extends React.Component {
|
class PageOverview extends React.Component {
|
||||||
|
|
||||||
constructor(props, ...args) {
|
constructor(props, ...args) {
|
||||||
super(props, ...args);
|
super(props, ...args);
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -37,30 +41,32 @@ class PageOverview extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRingClick = (ring) => (e) => {
|
handleRingClick = ring => e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
ring,
|
ring,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
isRingActive(ringName) {
|
isRingActive(ringName) {
|
||||||
return this.state.ring === ringName;
|
return this.state.ring === ringName;
|
||||||
}
|
}
|
||||||
|
|
||||||
itemMatchesRing = (item) => {
|
itemMatchesRing = item => {
|
||||||
return this.state.ring === 'all' || item.ring === this.state.ring;
|
return this.state.ring === 'all' || item.ring === this.state.ring;
|
||||||
};
|
};
|
||||||
|
|
||||||
itemMatchesSearch = (item) => {
|
itemMatchesSearch = item => {
|
||||||
return this.state.search.trim() === '' ||
|
return (
|
||||||
|
this.state.search.trim() === '' ||
|
||||||
containsSearchTerm(item.title, this.state.search) ||
|
containsSearchTerm(item.title, this.state.search) ||
|
||||||
containsSearchTerm(item.body, this.state.search) ||
|
containsSearchTerm(item.body, this.state.search) ||
|
||||||
containsSearchTerm(item.info, this.state.search);
|
containsSearchTerm(item.info, this.state.search)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
isItemVisible = (item) => {
|
isItemVisible = item => {
|
||||||
return this.itemMatchesRing(item) && this.itemMatchesSearch(item);
|
return this.itemMatchesRing(item) && this.itemMatchesSearch(item);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -70,11 +76,13 @@ class PageOverview extends React.Component {
|
|||||||
...group,
|
...group,
|
||||||
items: group.items.filter(this.isItemVisible),
|
items: group.items.filter(this.isItemVisible),
|
||||||
}));
|
}));
|
||||||
const nonEmptyGroups = groupsFiltered.filter(group => group.items.length > 0);
|
const nonEmptyGroups = groupsFiltered.filter(
|
||||||
|
group => group.items.length > 0,
|
||||||
|
);
|
||||||
return nonEmptyGroups;
|
return nonEmptyGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearchTermChange = (value) => {
|
handleSearchTermChange = value => {
|
||||||
this.setState({
|
this.setState({
|
||||||
search: value,
|
search: value,
|
||||||
});
|
});
|
||||||
@@ -92,69 +100,67 @@ class PageOverview extends React.Component {
|
|||||||
<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={this.handleSearchTermChange} value={this.state.search} />
|
<Search
|
||||||
|
onChange={this.handleSearchTermChange}
|
||||||
|
value={this.state.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
|
||||||
<Badge
|
big
|
||||||
big
|
onClick={this.handleRingClick(ringName)}
|
||||||
onClick={this.handleRingClick(ringName)}
|
type={this.isRingActive(ringName) ? ringName : 'empty'}
|
||||||
type={this.isRingActive(ringName) ? ringName : 'empty' }
|
>
|
||||||
>
|
{ringName}
|
||||||
{ringName}
|
</Badge>
|
||||||
</Badge>
|
</div>
|
||||||
</div>
|
))}
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</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 => (
|
||||||
{
|
<Link
|
||||||
items.map((item) => (
|
key={item.name}
|
||||||
<Link
|
className="item item--big item--no-leading-border item--no-trailing-border"
|
||||||
key={item.name}
|
pageName={`${item.quadrant}/${item.name}`}
|
||||||
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="split split--overview">
|
<div className="item__title">
|
||||||
<div className="split__left">
|
{item.title}
|
||||||
<div className="item__title">
|
<Tag item={item} />
|
||||||
{item.title}
|
</div>
|
||||||
<IsNew item={item} />
|
</div>
|
||||||
</div>
|
<div className="split__right">
|
||||||
|
<div className="nav nav--relations">
|
||||||
|
<div className="nav__item">
|
||||||
|
{translate(item.quadrant)}
|
||||||
</div>
|
</div>
|
||||||
<div className="split__right">
|
<div className="nav__item">
|
||||||
<div className="nav nav--relations">
|
<Badge type={item.ring}>{item.ring}</Badge>
|
||||||
<div className="nav__item">{translate(item.quadrant)}</div>
|
|
||||||
<div className="nav__item">
|
|
||||||
<Badge type={item.ring}>{item.ring}</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</div>
|
||||||
))
|
</div>
|
||||||
}
|
</Link>
|
||||||
</div>
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
</div>
|
||||||
}
|
))}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</Fadeable>
|
</Fadeable>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { translate, rings } from '../../common/config';
|
|||||||
import Badge from './Badge';
|
import Badge from './Badge';
|
||||||
import Link from './Link';
|
import Link from './Link';
|
||||||
import ItemList from './ItemList';
|
import ItemList from './ItemList';
|
||||||
import IsNew from './IsNew';
|
import Tag from './Tag';
|
||||||
|
|
||||||
const renderList = (ringName, quadrantName, groups, big) => {
|
const renderList = (ringName, quadrantName, groups, big) => {
|
||||||
const itemsInRing = groups[quadrantName][ringName];
|
const itemsInRing = groups[quadrantName][ringName];
|
||||||
@@ -11,7 +11,9 @@ const renderList = (ringName, quadrantName, groups, big) => {
|
|||||||
if (big === true) {
|
if (big === true) {
|
||||||
return (
|
return (
|
||||||
<ItemList items={itemsInRing} noLeadingBorder>
|
<ItemList items={itemsInRing} noLeadingBorder>
|
||||||
<Badge type={ringName} big={big}>{ringName}</Badge>
|
<Badge type={ringName} big={big}>
|
||||||
|
{ringName}
|
||||||
|
</Badge>
|
||||||
</ItemList>
|
</ItemList>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -21,26 +23,24 @@ const renderList = (ringName, quadrantName, groups, big) => {
|
|||||||
<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
|
<Link className="link" pageName={`${item.quadrant}/${item.name}`}>
|
||||||
key={item.name}
|
{item.title}
|
||||||
className="ring-list__item"
|
<Tag item={item} short />
|
||||||
>
|
</Link>
|
||||||
<Link className="link" pageName={`${item.quadrant}/${item.name}`}>
|
</span>
|
||||||
{item.title}
|
))}
|
||||||
<IsNew item={item} />
|
|
||||||
</Link>
|
|
||||||
</span>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
const renderRing = (ringName, quadrantName, groups, big) => {
|
const renderRing = (ringName, quadrantName, groups, big) => {
|
||||||
if (!groups[quadrantName] || !groups[quadrantName][ringName] || groups[quadrantName][ringName].length === 0) {
|
if (
|
||||||
|
!groups[quadrantName] ||
|
||||||
|
!groups[quadrantName][ringName] ||
|
||||||
|
groups[quadrantName][ringName].length === 0
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -48,7 +48,7 @@ const renderRing = (ringName, quadrantName, groups, big) => {
|
|||||||
{renderList(ringName, quadrantName, groups, big)}
|
{renderList(ringName, quadrantName, groups, big)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function QuadrantSection({ quadrantName, groups, big = false }) {
|
export default function QuadrantSection({ quadrantName, groups, big = false }) {
|
||||||
return (
|
return (
|
||||||
@@ -58,21 +58,18 @@ export default function QuadrantSection({ quadrantName, groups, big = false }) {
|
|||||||
<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" />Quadrant
|
||||||
<span className="icon icon--pie icon-link__icon"></span>Quadrant Overview
|
Overview
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="quadrant-section__rings">
|
<div className="quadrant-section__rings">
|
||||||
{
|
{rings.map(ringName => renderRing(ringName, quadrantName, groups, big))}
|
||||||
rings.map((ringName) => renderRing(ringName, quadrantName, groups, big))
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
15
js/components/Tag.js
Normal file
15
js/components/Tag.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function Tag({ item, short = false }) {
|
||||||
|
if (item.flag !== 'default') {
|
||||||
|
let name = item.flag.toUpperCase();
|
||||||
|
if (short === true) {
|
||||||
|
name = {
|
||||||
|
new: 'N',
|
||||||
|
changed: 'C',
|
||||||
|
}[item.flag];
|
||||||
|
}
|
||||||
|
return <span className={`tag tag--${item.flag}`}>{name}</span>;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
.is-new {
|
.tag {
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
background: var(--color-red);
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 3px 8px;
|
padding: 3px 8px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -8,4 +7,12 @@
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
left: 5px;
|
left: 5px;
|
||||||
|
|
||||||
|
&--new {
|
||||||
|
background: var(--color-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--changed {
|
||||||
|
background: var(--color-blue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user