Skip to content

Commit

Permalink
Add links to spell descriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
timhettler committed Aug 9, 2019
1 parent 7791335 commit 4ab99ba
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 149 deletions.
5 changes: 3 additions & 2 deletions src/components/SpellDetail/SpellDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {

import setTabIndex from '../../utilities/setTabIndex';
import getSpellLevel from '../../utilities/getSpellLevel';
import getHyperlinkedString from '../../utilities/getHyperlinkedString';
import PropIcon from '../PropIcon';
import VisuallyHidden from '../VisuallyHidden';

Expand Down Expand Up @@ -228,11 +229,11 @@ class Spell extends Component {
</h2>
<div
className="spell__description content-area"
dangerouslySetInnerHTML={{ __html: desc }}
dangerouslySetInnerHTML={{ __html: getHyperlinkedString(desc) }}
/>
{higher_level && (
<div className="spell__description">
<h3 className="spell__minor-heading">At Higher Levels.</h3>
<h3 className="spell__minor-heading">At Higher Levels</h3>
<div
className="content-area"
dangerouslySetInnerHTML={{ __html: higher_level }}
Expand Down
6 changes: 3 additions & 3 deletions src/components/SpellDetail/SpellDetail.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
position: relative;
height: 100%;

a {
// Footnotes
a[href^='#'] {
color: inherit;
text-decoration: none;
}
Expand Down Expand Up @@ -39,10 +40,9 @@
}

&__minor-heading {
@include inlineHeading;
font-weight: bold;
font-style: italic;
float: left;
margin-right: 1ex;
}

&__description {
Expand Down
282 changes: 141 additions & 141 deletions src/data/spells.js

Large diffs are not rendered by default.

30 changes: 27 additions & 3 deletions src/index.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import './utilities/scss/_mixins';

/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
Expand Down Expand Up @@ -159,6 +161,17 @@ body {
}

.content-area {
a {
color: inherit;
text-decoration: none;
border-bottom: 1px solid transparent;

&:hover,
&:focus {
border-bottom-color: inherit;
}
}

i {
font-style: italic;
}
Expand All @@ -167,9 +180,12 @@ body {
font-weight: bold;
}

p,
ul,
table {
.keyword {
font-style: normal;
font-weight: bold;
}

> * {
&:not(:first-child) {
margin-top: 1.2em;
}
Expand Down Expand Up @@ -201,6 +217,14 @@ body {
}
}
}

h3 {
@include inlineHeading font-weight: bold;

&::after {
content: '.\00a0';
}
}
}

[tabindex] {
Expand Down
170 changes: 170 additions & 0 deletions src/utilities/getHyperlinkedString.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
const conditions = [
'blinded',
'charmed',
'deafened',
'exhaustion',
'frightened',
'grappled',
'incapacitated',
'invisible',
'paralyzed',
'petrified',
'poisoned',
'prone',
'restrained',
'stunned',
'unconscious',
];

const senses = ['blindsight', 'darkvision', 'tremorsense', 'truesight'];

const monsters = [
'skeleton',
'zombie',
'homunculus',
'awakened shrub',
'awakened tree',
'bat',
'cat',
'crab',
'frog',
'hawk',
'lizard',
'octopus',
'owl',
'poisonous snake',
'quipper',
'rat',
'raven',
'spider',
'weasel',
'griffon',
'pegasus',
'peryton',
'dire wolf',
'rhinoceros',
'saber-toothed tiger',
'warhorse',
'pony',
'camel',
'elk',
'mastiff',
'air elemental',
'barbed devil',
'bearded devil',
'shadow demon',
'barlgura',
];

const monsterGroups = [
{
keyword: 'beasts of challenge rating 1/4 or lower',
url:
'https://www.dndbeyond.com/monsters?filter-type=2&filter-cr-max=3&sort=-cr',
},
{
keyword: 'beasts of challenge rating 1/2 or lower',
url:
'https://www.dndbeyond.com/monsters?filter-type=2&filter-cr-max=4&sort=-cr',
},
{
keyword: 'beasts of challenge rating 1 or lower',
url:
'https://www.dndbeyond.com/monsters?filter-type=2&filter-cr-max=5&sort=-cr',
},
{
keyword: 'beast of challenge rating 2 or lower',
url:
'https://www.dndbeyond.com/monsters?filter-type=2&filter-cr-max=6&sort=-cr',
},
{
keyword: 'beast of challenge rating 6 or lower',
url:
'https://www.dndbeyond.com/monsters?filter-type=2&filter-cr-max=10&sort=-cr',
},
{
keyword: 'celestial of challenge rating 4 or lower',
url:
'https://www.dndbeyond.com/monsters?filter-type=3&filter-cr-max=8&sort=-cr',
},
{
keyword: 'elemental of challenge rating 5 or lower',
url:
'https://www.dndbeyond.com/monsters?filter-type=7&filter-cr-max=9&sort=-cr',
},
{
keyword: 'fey creature of challenge rating 6 or lower',
url:
'https://www.dndbeyond.com/monsters?filter-type=8&filter-cr-max=10&sort=-cr',
},
{
keyword: 'devil of challenge rating 6 or lower',
url:
'https://www.dndbeyond.com/monsters?filter-type=9&filter-cr-max=10&filter-sub-types=10&sort=-cr',
},
{
keyword: 'demon of challenge rating 5 or lower',
url:
'https://www.dndbeyond.com/monsters?filter-type=9&filter-cr-max=9&filter-sub-types=9&sort=-cr',
},
];

const planes = ['Astral Plane', 'Ethereal Plane', 'Feywild', 'Shadowfell'];

function formatStringForUrl(string) {
return string.charAt(0).toUpperCase() + string.slice(1).replace(' ', '-');
}

function getHyperlink(full, before, keyword, after) {
const urls = {
conditions:
'https://www.dndbeyond.com/sources/basic-rules/appendix-a-conditions#',
senses: 'https://www.dndbeyond.com/sources/basic-rules/monsters#',
monsters: 'https://www.dndbeyond.com/monsters/',
planes: 'https://www.dndbeyond.com/sources/dmg/creating-a-multiverse#',
};

let url = '';

if (conditions.includes(keyword)) {
url = urls.conditions;
} else if (senses.includes(keyword)) {
url = urls.senses;
} else if (monsters.includes(keyword)) {
url = urls.monsters;
} else if (planes.includes(keyword)) {
url = urls.planes;
}

if (url) {
url = `${url}${formatStringForUrl(keyword)}`;
} else if (monsterGroups.map(i => i.keyword).includes(keyword)) {
url = monsterGroups.find(i => i.keyword === keyword).url;
}

if (!url) {
return full;
}

return `${before}<a href="${url}" target="_blank">${keyword}</a>${after}`;
}

function getHyperlinkedString(string) {
const allKeywords = [].concat(
conditions,
senses,
monsters,
monsterGroups.map(i => i.keyword),
planes
);

const replacer = (match, p1, p2, p3) => {
return `<i class="keyword">${getHyperlink(match, p1, p2, p3)}</i>`;
};

var re = new RegExp(`(\\s)(${allKeywords.join('|')})(\\s|\\.|,)`, 'gi');

return string.replace(re, replacer);
}

export default getHyperlinkedString;
8 changes: 8 additions & 0 deletions src/utilities/scss/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,11 @@ $size--large-viewport-start: 1100px;
padding-bottom: percentage($height-ratio);
}
}

@mixin inlineHeading() {
float: left;

&::after {
content: '.\00a0';
}
}

0 comments on commit 4ab99ba

Please sign in to comment.