Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Content from Dark Alleys and Twisted Paths #78

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
edc84c6
Add abilities for fighter and rogue (formatting WIP)
Nov 4, 2023
ab02a71
Add barbarian abilities (needs tier on core abilities)
Nov 8, 2023
a8c8527
Manual adjustment - tiers for core content
Nov 9, 2023
7cbf2ce
Add abilities for bard (songs categorized as spells)
Nov 9, 2023
20e20d8
Add commander abilities, update fighter abilities
Nov 10, 2023
66a09d6
Standarize cleric abilities
Nov 10, 2023
2b3ccf5
Fighter fixes & formatting
Nov 11, 2023
bf269b6
Bard fixes & formatting
Nov 11, 2023
720a445
Add monk abilities
Nov 15, 2023
a2011c1
Add cleric abilities
Nov 18, 2023
2bd5423
Add ranger abilities
Nov 18, 2023
37171c6
ranger manual fixes
Nov 18, 2023
f8735a9
Add book selector (WIP: use of local storage)
Nov 19, 2023
abfa9de
Cleric manual fixes (core domains are repeated)
Nov 19, 2023
e520a7f
Add paladin abilities w/manual correction
Nov 20, 2023
1ac9ec1
Add conditional rendering for markdown compendium content
Nov 25, 2023
8bfeca2
add replaced_by attribute, apply to cleric domains
Nov 25, 2023
18d8982
unify multiline format in YAML to |-
Nov 25, 2023
95796ff
variants & versions schema + class version UI
Nov 25, 2023
2b91d08
Animal Companion talent versions + selector fixes
Nov 30, 2023
90429e4
reworked paladin talent display
Nov 30, 2023
158d059
add spell school/bloodline display for DATP
Nov 30, 2023
d3553e2
add wizard abilities
Nov 30, 2023
250b794
added sorc bloodlines
Nov 30, 2023
c962bb0
sorc abilities done
Nov 30, 2023
973d552
animal companion refactor (spells wip)
Dec 1, 2023
cc00ed6
ranger 13TW spells & rule fixes
Dec 1, 2023
8f7625e
finish animal companion refactor, add DATP stuff
Dec 1, 2023
821956f
add extra general feats
Dec 1, 2023
c29c289
add summoning feats
Dec 1, 2023
223f925
fixes to version selector & ranger editing
Dec 1, 2023
cdcf3ce
display source in ability block (PoC)
Dec 1, 2023
da4575c
very hacky localstorage solution, WIP better one
Dec 4, 2023
67cc815
misc visual fixes
Dec 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion components/ability-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ interface AbilityItemP {
description: Ability["description"];
feats: Ability["feats"] | PartialFeat[];
usage: Ability["usage"];
version: Ability["version"];
variant: Ability["variant"];
Copy link

@pawelniegowski pawelniegowski Dec 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These may end up collapsed into a single property.

The original design was that "version" is the rework of a class, while "variant" is a selectable option that heavily modifies its features (in particular, sorcerer bloodlines and specialist wizard in DATP).

One benefit of keeping them separate would be displaying the variant selector somewhere more appropriate, e.g. next to the bloodline section for sorcerer.

tier: Ability["tier"];
source: Ability["source"];
replaced_by: Ability["replaced_by"];
}

export default function AbilityItem({
Expand All @@ -26,6 +31,7 @@ export default function AbilityItem({
description,
feats,
usage,
source
}: AbilityItemP): JSX.Element {
const headerBgMap: Record<NonNullable<Ability["usage"]>, string> = {
"at-will":
Expand All @@ -38,8 +44,13 @@ export default function AbilityItem({
recharge: "from-sky-600 to-sky-800 dark:from-sky-700 dark:to-sky-900",
};

const isSubItem = type && (type.startsWith("Domain Spell") || type.startsWith("Death Knight Rune") || type.startsWith("Bonus Power") || type.startsWith("Arcane Shot"))

return (
<section className="text-base m-0 text-stone-950 dark:text-stone-50 relative">
<section className={clsx(
isSubItem ? "ml-16": "",
"text-base m-0 text-stone-950 dark:text-stone-50 relative"
)}>
<div className="absolute -inset-[2px] border-2 border-stone-100 dark:border-stone-950 pointer-events-none" />
<header
className={clsx(
Expand All @@ -49,6 +60,11 @@ export default function AbilityItem({
>
<Label as="h1" variant="title" className="text-left">
{name}
{source !== undefined && (
<Label as="div" variant="title" className="text-gray-400 text-sm">
{source}
</Label>
)}
</Label>
<Label variant="label" className="text-right ordinal">
{type}
Expand Down
63 changes: 63 additions & 0 deletions components/book-selector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useMemo } from "react";
import { Popover, Switch, Transition } from "@headlessui/react";
import { useTranslation } from "next-i18next";
import BookIcon from "@heroicons/react/20/solid/BookOpenIcon";
import { useBookStore } from "@/lib/books";

export function BookSelector() {
const { t } = useTranslation();
const {enabledBooks, toggleEnabledBook} = useBookStore();

const ThemeIcon = useMemo(() => {
return BookIcon;
});

return (
<Popover as="div" horizontal="true">
<Popover.Button
className="flex p-2 items-center justify-center shadow-md shadow-black/5 ring-1 bg-stone-800 ring-inset ring-white/5"
aria-label={t("theme-switcher") as string}
>
<ThemeIcon className="block h-5 w-5 fill-stone-400" />
</Popover.Button>

<Transition
enter="transition duration-200 ease-out"
enterFrom="transform opacity-0"
enterTo="transform opacity-100"
leave="transition duration-200 ease-out"
leaveFrom="transform opacity-100"
leaveTo="transform opacity-0"
>
<Popover.Panel
className="absolute top-full mt-0 w-72 right-0 text-sm font-medium shadow-md shadow-black/5 ring-1 bg-stone-900 ring-white/5"
>
<>
<h3 className="font-serif font-bold text-center p-2">Additional content</h3>
{enabledBooks.map( (book, index) => (
<div className="text-stone-400 p-2" key={book[0]}>
<Switch
checked={book[2]}
onChange={ () => toggleEnabledBook(index) }
className={(book[2] ? 'bg-amber-500' : 'bg-amber-900') +
" relative inline-flex h-4 w-8 items-center rounded-full"}>
{({checked}) => (
<>
<span
className={`${
checked ? 'translate-x-4' : 'translate-x-1'
} translate-x-1 inline-block h-3 w-3 transform rounded-full bg-white transition`}>
</span>
</>
)}
</Switch>
<span className="w-fit p-2">{book[1]}</span>
</div>
))}
</>
</Popover.Panel>
</Transition>
</Popover>
);

}
18 changes: 16 additions & 2 deletions components/compendium-ability.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Ability } from "@/.contentlayer/generated";
import clsx from "clsx";
import { map, size } from "lodash-es";
import AbilityItem from "./ability-item";
import { useBookStore, isSourceEnabled, isVVEnabled } from "@/lib/books";

interface AbilityListP {
abilities?: Ability[];
Expand All @@ -19,6 +20,16 @@ export default function AbilityList({
2: "lg:grid-cols-2",
3: "lg:grid-cols-3",
};
const bookstore = useBookStore( (state) => state);
const isAbilityHidden = (ability) => {
return (ability.source && !isSourceEnabled(bookstore,ability.source)) ||
(ability.version && !isVVEnabled(bookstore,"version",ability.version)) ||
(ability.variant && !isVVEnabled(bookstore,"variant",ability.version)) ||
(ability.replaced_by && isSourceEnabled(bookstore,ability.replaced_by))
};

// so far this is the only sourcebook that uses spell schools
const displaySchools = isSourceEnabled(bookstore,"DATP");

return (
<div
Expand All @@ -28,14 +39,17 @@ export default function AbilityList({
columnClassesMap[columns]
)}
>
{map(abilities, (ability) => (
{map(abilities, (ability) => isAbilityHidden(ability) || (
<div role="listitem" key={ability.name}>
<AbilityItem
type={ability._type}
type={ability.school && displaySchools ?
ability.school + " " + ability._type :
ability._type}
name={ability.name}
description={ability.description}
feats={ability.feats}
usage={ability.usage}
source={ability.source}
/>
</div>
))}
Expand Down
69 changes: 65 additions & 4 deletions components/compendium-header.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,79 @@
import CompendiumTitle from "./compendium-title";
import { RadioGroup } from '@headlessui/react'
import { useBookStore, isSourceEnabled, getVV } from "@/lib/books";

export default function VaultHeader({
primaryLabel,
secondaryLabel,
versions = null,
variants = null
}: {
primaryLabel: string;
secondaryLabel: string;
versions: any;
variants: any;
}) {

const bookStore = useBookStore((state) => state); // everything in the store can affect the render!
const getVersion = () => {
// this is not a pure getter - it also makes sure the store has an entry for the current page
let currentVersion = getVV(bookStore,"version");
if(currentVersion) // it's possible the sourcebook containing the version is no longer enabled
{
const matchingVersionObj = versions.filter((version) => version.name == currentVersion);
if (matchingVersionObj && matchingVersionObj[0].source && !isSourceEnabled(bookStore, matchingVersionObj[0].source))
currentVersion = null;
}
if(!currentVersion)
{
bookStore.setDocumentVV(primaryLabel,"version",versions[0].name);
return versions[0].name;
}
return currentVersion;
};
const hasMultipleOptions = () => {
const options = versions.filter( (version) =>
!version.source || isSourceEnabled(bookStore,version.source))
return options.length > 1;
}
bookStore.setCurrentDocument(primaryLabel);
return (
<header className="not-prose">
<CompendiumTitle>{primaryLabel}</CompendiumTitle>
<p className="my-0 font-serif font-medium italic text-black/50 dark:text-stone-400">
{secondaryLabel}
</p>
<div className="flex flex-row justify-between">
<div>
<CompendiumTitle>{primaryLabel}</CompendiumTitle>
<p className="my-0 font-serif font-medium italic text-black/50 dark:text-stone-400">
{secondaryLabel}
</p>
</div>
{versions && (
<RadioGroup value={getVersion()} onChange={(value)=>bookStore.setDocumentVV(primaryLabel,"version",value)}
as="div" className="max-w-[80%]"
>
<div className="flex-row justify-end">
{hasMultipleOptions() && versions.map((version) => {
if(version.source && !isSourceEnabled(bookStore, version.source))
{
return null;
}
return (
<RadioGroup.Option key={version.name} value={version.name}
as="div" className="inline-block">
{({active, checked}) => (
<div className={(checked?
'bg-amber-500 text-black border-amber-500':
'bg-white/5 border-black hover:border-amber-500')+
' py-0.5 px-2 border-2 border-solid'}>
{version.name}
</div>
)}
</RadioGroup.Option>
)}
)}
</div>
</RadioGroup>
)}
</div>
</header>
);
}
6 changes: 4 additions & 2 deletions components/compendium-side-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ function NavLink({
href,
active = false,
children,
isOriginRendered
}: {
href: string;
active?: boolean;
children: ReactNode;
isOriginRendered: boolean;
}) {
return (
return isOriginRendered && (
<a
href={href}
aria-current={active ? "page" : undefined}
Expand Down Expand Up @@ -72,7 +74,7 @@ function VisibleSectionHighlight() {
function NavigationGroup({ section }: any) {
return (
<li>
<NavLink href={`#${section.id}`}>{section.title}</NavLink>
<NavLink href={`#${section.id}`} isOriginRendered={section.rendered}>{section.title}</NavLink>
</li>
);
}
Expand Down
17 changes: 17 additions & 0 deletions components/content-layer-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SectionProvider } from "./section-provider";
import CompendiumSideNav from "./compendium-side-nav";
import ContentLink from "./content-link";
import { defaultLocale } from "@/lib/locales";
import { useBookStore, isSourceEnabled, isVVEnabled } from "@/lib/books";

interface ContentLayerRendererP {
data: any;
Expand All @@ -34,7 +35,11 @@ export default function ContentLayerPage({
const quote = get(pageDress, "quote");
const title = `${primaryLabel} - 13 Vaults`;
const sections = get(data, "sections", []);
const bookstore = useBookStore();
const lead = get(pageDress, "lead");
const If = (props) => (props.source && isSourceEnabled(bookstore, props.source)) ||
(props.version && isVVEnabled(bookstore,"version", props.version)) ?
props.children : null

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows conditionally displayed elements within a vault page's markdown (see e.g. the Animal Companion page for usage examples).

return (
<>
<Head>
Expand Down Expand Up @@ -88,6 +93,8 @@ export default function ContentLayerPage({
<VaultHeader
primaryLabel={primaryLabel}
secondaryLabel={secondaryLabel}
versions={data.versions}
variants={data.variants}
/>
{quote ? (
<figure>
Expand All @@ -113,13 +120,23 @@ export default function ContentLayerPage({
getAbilitiesByType={(type: string) =>
filter(get(data, "abilities"), ["_type", type])
}
getAbilitiesByTypeAndTier={(type: string, tier: string) =>
filter(get(data, "abilities"), {
"_type": type, "tier": tier })
}
getAbilitiesByTypes={(types: string[]) =>
filter(get(data, "abilities"), (ability) =>
includes(types, get(ability, "_type"))
)
}
getAbilitiesByTypesAndTier={(types: string[], tier: string) =>
filter(get(data, "abilities"), (ability) =>
includes(types, get(ability, "_type")) && ability["tier"] == tier)
}
isSourceEnabled = {(name:string) => isSourceEnabled(bookstore,name)}
components={{
Vault: Vault,
If: If,
h2: ({ id, children, ...rest }) => (
<Heading level={2} id={id!} headingProps={rest}>
{children}
Expand Down
2 changes: 2 additions & 0 deletions components/heading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ export default function Heading({
const Component = `h${level}`;
const reference = useRef<Element>(null);
const registerHeading = useSectionStore((s: any) => s.registerHeading);
const unregisterHeading = useSectionStore((s: any) => s.unregisterHeading);

useEffect(() => {
if (level === 2) {
registerHeading({ id, ref: reference, offsetRem: 10 });
}
return (() => level === 2 ? unregisterHeading(id) : null)
}, [id, level, registerHeading]);

const { className, ...restHeadingProperties } = headingProps;
Expand Down
7 changes: 6 additions & 1 deletion components/mega-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
useReducedMotion,
} from "framer-motion";
import Button from "./button";
import { BookSelector } from "./book-selector";
import { ThemeSelector } from "./theme-selector";

interface MobileSubnavP {
Expand Down Expand Up @@ -364,6 +365,9 @@ export default function MegaNav({ navigation }: { navigation: Navigation }) {
)}
</div>
<div className="flex gap-4 items-center">
<div className="hidden lg:block relative">
<BookSelector />
</div>
<div className="hidden lg:block relative">
<ThemeSelector />
</div>
Expand Down Expand Up @@ -391,7 +395,8 @@ export default function MegaNav({ navigation }: { navigation: Navigation }) {
</Link>
</div>

<div className="lg:hidden relative">
<div className="lg:hidden relative flex space-x-2">
<BookSelector />
<ThemeSelector />
</div>
</div>
Expand Down
10 changes: 10 additions & 0 deletions components/section-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,22 @@ function createSectionStore(sections: Section[]) {
...section,
headingRef: ref,
offsetRem,
rendered:true
};
}
return section;
}),
};
}),
unregisterHeading: (id:string) =>
set((state) => {
return {
sections: state.sections.map((section) => section.id == id ? {
...section,
rendered: false
} : section)
};
}),
}));
}

Expand Down
Loading