diff --git a/common/radar.js b/common/radar.js
index 3e24f6a..62a2046 100644
--- a/common/radar.js
+++ b/common/radar.js
@@ -14,7 +14,7 @@ export const createRadar = async tree => {
const revisions = await createRevisionsFromFiles(fileNames);
const allReleases = getAllReleases(revisions);
const items = createItems(revisions);
- const flaggedItems = flagWithIsNew(items, allReleases);
+ const flaggedItems = flagItem(items, allReleases);
return {
items: flaggedItems,
@@ -26,7 +26,9 @@ const checkAttributes = (fileName, attributes) => {
const rings = ['adopt', 'trial', 'assess', 'hold'];
if (attributes.ring && !rings.includes(attributes.ring)) {
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)) {
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 = (
item = {
- isNew: false,
+ flag: 'default',
isFeatured: true,
revisions: [],
},
@@ -141,15 +145,30 @@ const revisionCreatesNewHistoryEntry = revision => {
return revision.body.trim() !== '' || typeof revision.ring !== 'undefined';
};
-const flagWithIsNew = (items, allReleases) =>
+const flagItem = (items, allReleases) =>
items.map(
item => ({
...item,
- isNew: isNewItem(item, allReleases),
+ flag: getItemFlag(item, allReleases),
}),
[],
);
-const isNewItem = (item, allReleases) =>
- item.revisions.length === 0 ||
+const isInLastRelease = (item, allReleases) =>
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';
+};
diff --git a/js/components/IsNew.js b/js/components/IsNew.js
deleted file mode 100644
index fadb642..0000000
--- a/js/components/IsNew.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import React from 'react';
-
-export default function IsNew({ item }) {
- if (item.isNew) {
- return NEW;
- }
- return null;
-}
diff --git a/js/components/Item.js b/js/components/Item.js
index b52bb1b..11d9d95 100644
--- a/js/components/Item.js
+++ b/js/components/Item.js
@@ -1,9 +1,14 @@
import React from 'react';
import classNames from 'classnames';
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 (
{item.title}
-
+
- {
- item.info && (
- {item.info}
- )
- }
+ {item.info && {item.info}
}
);
}
diff --git a/js/components/PageItem.js b/js/components/PageItem.js
index 121a9f6..8ca264b 100644
--- a/js/components/PageItem.js
+++ b/js/components/PageItem.js
@@ -5,7 +5,6 @@ import Link from './Link';
import FooterEnd from './FooterEnd';
import SetTitle from './SetTitle';
import ItemRevisions from './ItemRevisions';
-import IsNew from './IsNew';
import { createAnimation, createAnimationRunner } from '../animation';
import { translate } from '../../common/config';
@@ -23,55 +22,67 @@ class PageItem extends React.Component {
const itemsInRing = this.getItemsInRing(props);
this.animationsIn = {
- background: createAnimation({
+ background: createAnimation(
+ {
transform: 'translateX(calc((100vw - 1200px) / 2 + 800px))',
transition: 'transform 450ms cubic-bezier(0.24, 1.12, 0.71, 0.98)',
- }, {
+ },
+ {
transition: 'transform 450ms cubic-bezier(0.24, 1.12, 0.71, 0.98)',
transform: 'translateX(0)',
},
- 0
+ 0,
),
- navHeader: createAnimation({
+ navHeader: createAnimation(
+ {
transform: 'translateX(-40px)',
opacity: '0',
- }, {
+ },
+ {
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
transform: 'translateX(0px)',
opacity: '1',
},
- 300
+ 300,
),
- text: createAnimation({
+ text: createAnimation(
+ {
transform: 'translateY(-20px)',
opacity: '0',
- }, {
+ },
+ {
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
transform: 'translateY(0px)',
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)',
opacity: '0',
- }, {
+ },
+ {
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
transform: 'translateX(0px)',
opacity: '1',
},
- 400 + 100 * i
- ))),
- footer: createAnimation({
- transition: 'opacity 150ms ease-out, transform 300ms ease-out',
- transform: 'translateX(-40px)',
- opacity: '0',
- }, {
- transition: 'opacity 150ms ease-out, transform 300ms ease-out',
- transform: 'translateX(0px)',
- opacity: '1',
- },
- 600 + itemsInRing.length * 100
+ 600 + itemsInRing.length * 100,
),
};
@@ -79,7 +90,7 @@ class PageItem extends React.Component {
background: createAnimation(
this.animationsIn.background.stateB,
this.animationsIn.background.stateA,
- 300 + itemsInRing.length * 50
+ 300 + itemsInRing.length * 50,
),
navHeader: createAnimation(
this.animationsIn.navHeader.stateB,
@@ -88,7 +99,7 @@ class PageItem extends React.Component {
transform: 'translateX(40px)',
opacity: '0',
},
- 0
+ 0,
),
text: createAnimation(
this.animationsIn.text.stateB,
@@ -97,43 +108,58 @@ class PageItem extends React.Component {
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
opacity: '0',
},
- 0
+ 0,
),
- items: itemsInRing.map((item, i) => (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,
+ items: itemsInRing.map((item, i) =>
+ createAnimation(
+ this.animationsIn.items[i].stateB,
{
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
transform: 'translateX(40px)',
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
- this.state = setAnimations({}, createAnimationRunner(this.animationsIn).getState());
- } else { // Hard refresh
+ if (props.leaving) {
+ // entering from an other page
+ this.state = setAnimations(
+ {},
+ createAnimationRunner(this.animationsIn).getState(),
+ );
+ } else {
+ // Hard refresh
this.state = {};
}
}
componentWillReceiveProps({ leaving }) {
- if (!this.props.leaving && leaving) { // page will be left
- this.animationRunner = createAnimationRunner(this.animationsOut, this.handleAnimationsUpdate);
+ if (!this.props.leaving && leaving) {
+ // page will be left
+ this.animationRunner = createAnimationRunner(
+ this.animationsOut,
+ this.handleAnimationsUpdate,
+ );
this.animationRunner.run();
this.animationRunner.awaitAnimationComplete(this.props.onLeave);
}
- if (this.props.leaving && !leaving) { // page is entered
- this.animationRunner = createAnimationRunner(this.animationsIn, this.handleAnimationsUpdate);
+ if (this.props.leaving && !leaving) {
+ // page is entered
+ this.animationRunner = createAnimationRunner(
+ this.animationsIn,
+ this.handleAnimationsUpdate,
+ );
this.animationRunner.run();
}
}
@@ -142,20 +168,22 @@ class PageItem extends React.Component {
this.setState(setAnimations(this.state, this.animationRunner.getState()));
};
- getAnimationState = (name) => {
+ getAnimationState = name => {
if (!this.state.animations) {
return undefined;
}
return this.state.animations[name];
};
- getItem = (props) => {
+ getItem = props => {
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;
- }
+ };
- getItemsInRing = (props) => {
+ getItemsInRing = props => {
const item = this.getItem(props);
const itemsInRing = groupByQuadrants(props.items)[item.quadrant][item.ring];
return itemsInRing;
@@ -170,7 +198,10 @@ class PageItem extends React.Component {
-
+
{translate(item.quadrant)}
@@ -182,34 +213,55 @@ class PageItem extends React.Component {
>
- {item.ring}
+
+ {item.ring}
+
- Quadrant Overview
+ Quadrant
+ Overview
-
-
-
+
+
-
{item.title}
+
+ {item.title}
+
- {item.ring}
+
+ {item.ring}
+
-
- {item.revisions.length > 1 &&
}
+
+ {item.revisions.length > 1 && (
+
+ )}
diff --git a/js/components/PageOverview.js b/js/components/PageOverview.js
index 474491f..0bc3604 100644
--- a/js/components/PageOverview.js
+++ b/js/components/PageOverview.js
@@ -7,7 +7,7 @@ import Link from './Link';
import Search from './Search';
import Fadeable from './Fadeable';
import SetTitle from './SetTitle';
-import IsNew from './IsNew';
+import Tag from './Tag';
import { groupByFirstLetter } from '../../common/model';
import { translate } from '../../common/config';
@@ -15,11 +15,15 @@ const rings = ['all', 'assess', 'trial', 'hold', 'adopt'];
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
+ );
+};
class PageOverview extends React.Component {
-
constructor(props, ...args) {
super(props, ...args);
this.state = {
@@ -37,30 +41,32 @@ class PageOverview extends React.Component {
}
}
- handleRingClick = (ring) => (e) => {
+ handleRingClick = ring => e => {
e.preventDefault();
this.setState({
ring,
});
- }
+ };
isRingActive(ringName) {
return this.state.ring === ringName;
}
- itemMatchesRing = (item) => {
+ itemMatchesRing = item => {
return this.state.ring === 'all' || item.ring === this.state.ring;
};
- itemMatchesSearch = (item) => {
- return this.state.search.trim() === '' ||
+ itemMatchesSearch = item => {
+ return (
+ this.state.search.trim() === '' ||
containsSearchTerm(item.title, 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);
};
@@ -70,11 +76,13 @@ class PageOverview extends React.Component {
...group,
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;
}
- handleSearchTermChange = (value) => {
+ handleSearchTermChange = value => {
this.setState({
search: value,
});
@@ -92,69 +100,67 @@ class PageOverview extends React.Component {
-
+
- {
- rings.map(ringName => (
-
-
- {ringName}
-
-
- ))
- }
+ {rings.map(ringName => (
+
+
+ {ringName}
+
+
+ ))}
- {
- groups.map(({ letter, items }) => (
-
-
{letter}
-
-
-
- {
- items.map((item) => (
-
-
-
-
- {item.title}
-
-
+ {groups.map(({ letter, items }) => (
+
+
{letter}
+
+
+
+ {items.map(item => (
+
+
+
+
+
+
+ {translate(item.quadrant)}
-
-
-
{translate(item.quadrant)}
-
- {item.ring}
-
-
+
+ {item.ring}
-
- ))
- }
-
+
+
+
+ ))}
- ))
- }
-
+
+ ))}
);
diff --git a/js/components/QuadrantSection.js b/js/components/QuadrantSection.js
index 95deca8..a753344 100644
--- a/js/components/QuadrantSection.js
+++ b/js/components/QuadrantSection.js
@@ -3,7 +3,7 @@ import { translate, rings } from '../../common/config';
import Badge from './Badge';
import Link from './Link';
import ItemList from './ItemList';
-import IsNew from './IsNew';
+import Tag from './Tag';
const renderList = (ringName, quadrantName, groups, big) => {
const itemsInRing = groups[quadrantName][ringName];
@@ -11,7 +11,9 @@ const renderList = (ringName, quadrantName, groups, big) => {
if (big === true) {
return (
- {ringName}
+
+ {ringName}
+
);
}
@@ -21,26 +23,24 @@ const renderList = (ringName, quadrantName, groups, big) => {
{ringName}
- {
- itemsInRing.map(item => (
-
-
- {item.title}
-
-
-
- ))
- }
+ {itemsInRing.map(item => (
+
+
+ {item.title}
+
+
+
+ ))}
);
-}
-
+};
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 (
@@ -48,7 +48,7 @@ const renderRing = (ringName, quadrantName, groups, big) => {
{renderList(ringName, quadrantName, groups, big)}
);
-}
+};
export default function QuadrantSection({ quadrantName, groups, big = false }) {
return (
@@ -58,21 +58,18 @@ export default function QuadrantSection({ quadrantName, groups, big = false }) {
{translate(quadrantName)}
- {
- !big && (
-
-
- Quadrant Overview
-
-
- )
- }
+ {!big && (
+
+
+ Quadrant
+ Overview
+
+
+ )}
- {
- rings.map((ringName) => renderRing(ringName, quadrantName, groups, big))
- }
+ {rings.map(ringName => renderRing(ringName, quadrantName, groups, big))}
);
diff --git a/js/components/Tag.js b/js/components/Tag.js
new file mode 100644
index 0000000..6a0d849
--- /dev/null
+++ b/js/components/Tag.js
@@ -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
{name};
+ }
+ return null;
+}
diff --git a/styles/components/is-new.css b/styles/components/tag.css
similarity index 59%
rename from styles/components/is-new.css
rename to styles/components/tag.css
index 927c43f..d1b7882 100644
--- a/styles/components/is-new.css
+++ b/styles/components/tag.css
@@ -1,6 +1,5 @@
-.is-new {
+.tag {
font-size: 9px;
- background: var(--color-red);
display: inline-block;
padding: 3px 8px;
border-radius: 10px;
@@ -8,4 +7,12 @@
vertical-align: top;
margin-top: -2px;
left: 5px;
+
+ &--new {
+ background: var(--color-red);
+ }
+
+ &--changed {
+ background: var(--color-blue);
+ }
}