New source found from dndbeyond.com

This commit is contained in:
2026-07-08 01:00:09 -07:00
parent 9a983a6d7b
commit dcefa0d2f2
2865 changed files with 222467 additions and 49053 deletions
@@ -0,0 +1,265 @@
import clsx from "clsx";
import { sortBy } from "lodash";
import { FC, HTMLAttributes, useContext, useEffect, useState } from "react";
import {
characterActions,
CharClass,
ClassFeature,
ClassFeatureUtils,
ClassUtils,
ContainerManager,
HelperUtils,
} from "@dndbeyond/character-rules-engine";
import ArrowRightIcon from "@dndbeyond/fontawesome-cache/svgs/solid/arrow-right.svg";
import { Button } from "~/components/Button";
import { FeatureChoices } from "~/components/FeatureChoices";
import { useSidebar } from "~/contexts/Sidebar";
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
import { useDispatch } from "~/hooks/useDispatch";
import DataLoadingStatusEnum from "~/tools/js/Shared/constants/DataLoadingStatusEnum";
import { InventoryManagerContext } from "~/tools/js/Shared/managers/InventoryManagerContext";
import { AppLoggerUtils, PaneIdentifierUtils } from "~/tools/js/Shared/utils";
import Collapsible from "~/tools/js/smartComponents/Collapsible";
import LoadingPlaceholder from "~/tools/js/smartComponents/LoadingPlaceholder";
import { Header } from "../../components/Header";
import { Heading } from "../../components/Heading";
import { PaneComponentEnum } from "../../types";
import styles from "./styles.module.css";
export interface ItemPlansManagePaneProps
extends HTMLAttributes<HTMLDivElement> {}
export const ItemPlansManagePane: FC<ItemPlansManagePaneProps> = ({
className,
...props
}) => {
const dispatch = useDispatch();
const { classes } = useCharacterEngine();
const {
pane: { paneHistoryPush },
} = useSidebar();
const { inventoryManager } = useContext(InventoryManagerContext);
const [itemShoppe, setItemShoppe] = useState<ContainerManager | null>(null);
const [itemsLoadingStatus, setItemsLoadingStatus] =
useState<DataLoadingStatusEnum>(DataLoadingStatusEnum.NOT_INITIALIZED);
useEffect(() => {
const abortController = new AbortController();
if (
itemShoppe === null &&
itemsLoadingStatus !== DataLoadingStatusEnum.LOADED
) {
if (inventoryManager) {
setItemsLoadingStatus(DataLoadingStatusEnum.LOADING);
inventoryManager
.getInventoryShoppe({
onSuccess: (shoppeContainer: ContainerManager) => {
setItemShoppe(shoppeContainer);
setItemsLoadingStatus(DataLoadingStatusEnum.LOADED);
},
additionalApiConfig: {
signal: abortController.signal,
},
})
.catch((e) => {
if (abortController.signal.aborted) return;
AppLoggerUtils.handleAdhocApiError(e);
});
} else {
setItemsLoadingStatus(DataLoadingStatusEnum.LOADED);
}
}
return () => {
abortController.abort();
};
}, [itemsLoadingStatus, inventoryManager, itemShoppe]);
const handleClassFeatureClick = (
charClass: CharClass,
classFeature: ClassFeature
): void => {
paneHistoryPush(
PaneComponentEnum.CLASS_FEATURE_DETAIL,
PaneIdentifierUtils.generateClassFeature(
ClassFeatureUtils.getId(classFeature),
ClassUtils.getMappingId(charClass)
)
);
};
const handleChoiceChange = (
charClass: CharClass,
classFeature: ClassFeature,
id: string,
type: number,
value: any,
parentChoiceId: string | null
): void => {
if (!charClass || !classFeature) {
return;
}
dispatch(
characterActions.classFeatureChoiceSetRequest(
ClassUtils.getActiveId(charClass),
ClassFeatureUtils.getId(classFeature),
type,
id,
HelperUtils.parseInputInt(value),
parentChoiceId,
ClassFeatureUtils.getHasItemMappings(classFeature)
? inventoryManager.handleAcceptOnSuccess(true)
: undefined
)
);
};
return (
<div className={clsx([styles.itemPlansManagePane, className])} {...props}>
<Header>Manage Magic Item Plans</Header>
{classes.map((charClass) => {
const subclass = ClassUtils.getSubclass(charClass);
const subclassId = subclass?.id;
const subclassName = subclass?.name;
const baseClassId = ClassUtils.getId(charClass);
const activeFeaturesWithItemMappings =
ClassUtils.getActiveClassFeatures(charClass).filter(
ClassFeatureUtils.getHasItemMappings
);
const baseClassFeatures: ClassFeature[] = [];
const subClassFeatures: ClassFeature[] = [];
activeFeaturesWithItemMappings.forEach((feature) => {
const featureClassId = ClassFeatureUtils.getClassId(feature);
if (featureClassId === baseClassId) {
baseClassFeatures.push(feature);
} else if (featureClassId === subclassId) {
subClassFeatures.push(feature);
}
});
const sortedBaseFeatures = sortBy(baseClassFeatures, [
(feature) => ClassFeatureUtils.getName(feature),
]);
const sortedSubclassFeatures = sortBy(subClassFeatures, [
(feature) => ClassFeatureUtils.getName(feature),
]);
return (
<div key={ClassUtils.getMappingId(charClass)}>
<Heading>{ClassUtils.getName(charClass)}</Heading>
{itemsLoadingStatus !== DataLoadingStatusEnum.LOADED && (
<LoadingPlaceholder />
)}
{itemsLoadingStatus === DataLoadingStatusEnum.LOADED &&
sortedBaseFeatures.map((feature) => {
return (
<Collapsible
className={styles.classFeature}
key={ClassFeatureUtils.getUniqueKey(feature)}
header={`${ClassFeatureUtils.getName(feature)} (${
ClassFeatureUtils.getChoices(feature).length
})`}
initiallyCollapsed={false}
>
<div className={styles.buttonContainer}>
<Button
themed
size="xx-small"
className={styles.viewDetailsButton}
onClick={() =>
handleClassFeatureClick(charClass, feature)
}
>
View {ClassFeatureUtils.getName(feature)} Details
<ArrowRightIcon className={styles.arrowIcon} />
</Button>
</div>
<FeatureChoices
className={styles.featureChoices}
choices={ClassFeatureUtils.getChoices(feature)}
charClass={charClass}
classFeature={feature}
onChoiceChange={(id, type, value, parentChoiceId) =>
handleChoiceChange(
charClass,
feature,
id,
type,
value,
parentChoiceId
)
}
collapseDescription={true}
shouldFetch={false}
itemShoppe={itemShoppe}
/>
</Collapsible>
);
})}
{itemsLoadingStatus === DataLoadingStatusEnum.LOADED &&
sortedSubclassFeatures.length > 0 && (
<>
<Heading>{subclassName}</Heading>
{sortedSubclassFeatures.map((feature) => {
return (
<Collapsible
className={styles.classFeature}
key={ClassFeatureUtils.getUniqueKey(feature)}
header={`${ClassFeatureUtils.getName(feature)} (${
ClassFeatureUtils.getChoices(feature).length
})`}
initiallyCollapsed={false}
>
<div className={styles.buttonContainer}>
<Button
themed
size="xx-small"
className={styles.viewDetailsButton}
onClick={() =>
handleClassFeatureClick(charClass, feature)
}
>
View {ClassFeatureUtils.getName(feature)} Details
<ArrowRightIcon className={styles.arrowIcon} />
</Button>
</div>
<FeatureChoices
className={styles.featureChoices}
choices={ClassFeatureUtils.getChoices(feature)}
charClass={charClass}
classFeature={feature}
onChoiceChange={(id, type, value, parentChoiceId) =>
handleChoiceChange(
charClass,
feature,
id,
type,
value,
parentChoiceId
)
}
collapseDescription={true}
shouldFetch={false}
itemShoppe={itemShoppe}
/>
</Collapsible>
);
})}
</>
)}
</div>
);
})}
</div>
);
};