New source found from dndbeyond.com

This commit is contained in:
2025-07-16 01:00:16 -07:00
parent 151590af7a
commit f9f8dd0e7a
25 changed files with 886 additions and 1299 deletions
@@ -1,11 +1,8 @@
import axios, { Canceler } from "axios";
import React from "react";
import { LoadingPlaceholder } from "@dndbeyond/character-components/es";
import {
ApiAdapterPromise,
ApiAdapterRequestConfig,
ApiAdapterUtils,
ApiResponse,
CharacterPreferences,
ChoiceData,
@@ -13,10 +10,8 @@ import {
Constants,
CoreUtils,
DefinitionPool,
Feat,
FeatDefinitionContract,
FeatLookup,
FeatUtils,
HelperUtils,
OptionalOriginLookup,
OptionalOriginUtils,
@@ -26,12 +21,9 @@ import {
} from "@dndbeyond/character-rules-engine/es";
import { Accordion } from "~/components/Accordion";
import { FeatureChoice } from "~/components/FeatureChoice";
import { FeatureChoices } from "~/components/FeatureChoices";
import { HtmlContent } from "~/components/HtmlContent";
import DataLoadingStatusEnum from "../../../../../Shared/constants/DataLoadingStatusEnum";
import { AppLoggerUtils } from "../../../../../Shared/utils";
interface Props {
speciesTrait: RacialTrait;
choiceInfo: ChoiceData;
@@ -52,10 +44,7 @@ interface Props {
speciesName?: string | null;
}
interface State {
featData: Array<Feat>;
hasFeatChoice: boolean;
collapsibleOpened: boolean;
loadingStatus: DataLoadingStatusEnum;
}
export default class SpeciesManageSpeciesTrait extends React.PureComponent<
Props,
@@ -65,115 +54,10 @@ export default class SpeciesManageSpeciesTrait extends React.PureComponent<
super(props);
this.state = {
featData: [],
hasFeatChoice: false,
collapsibleOpened: false,
loadingStatus: DataLoadingStatusEnum.NOT_INITIALIZED,
};
}
loadFeatsCanceler: null | Canceler = null;
componentDidMount(): void {
this.setState(
{
hasFeatChoice: this.hasFeatChoice(this.props),
},
() => {
this.conditionallyLoadFeatData();
}
);
}
componentDidUpdate(
prevProps: Readonly<Props>,
prevState: Readonly<State>,
snapshot?: any
): void {
const { speciesTrait } = this.props;
const { collapsibleOpened } = this.state;
if (speciesTrait !== prevProps.speciesTrait) {
this.setState(
{
hasFeatChoice: this.hasFeatChoice(this.props),
},
() => {
this.conditionallyLoadFeatData();
}
);
}
if (collapsibleOpened && !prevState.collapsibleOpened) {
this.conditionallyLoadFeatData();
}
}
componentWillUnmount(): void {
if (this.loadFeatsCanceler !== null) {
this.loadFeatsCanceler();
}
}
conditionallyLoadFeatData = (): void => {
const { loadAvailableFeats } = this.props;
const { hasFeatChoice, loadingStatus, collapsibleOpened } = this.state;
if (
hasFeatChoice &&
collapsibleOpened &&
loadingStatus === DataLoadingStatusEnum.NOT_INITIALIZED
) {
this.setState({
loadingStatus: DataLoadingStatusEnum.LOADING,
});
loadAvailableFeats({
cancelToken: new axios.CancelToken((c) => {
this.loadFeatsCanceler = c;
}),
})
.then((response) => {
let featData: Array<Feat> = [];
const data = ApiAdapterUtils.getResponseData(response);
if (data !== null) {
featData = data.map((definition) =>
FeatUtils.simulateFeat(definition)
);
}
this.setState({
featData,
loadingStatus: DataLoadingStatusEnum.LOADED,
});
this.loadFeatsCanceler = null;
})
.catch(AppLoggerUtils.handleAdhocApiError);
}
};
getFeatData = (existingFeatId: number | null): Array<Feat> => {
const { featData } = this.state;
const { featLookup } = this.props;
let data: Array<Feat> = [...featData];
if (existingFeatId !== null) {
let existingFeat = HelperUtils.lookupDataOrFallback(
featLookup,
existingFeatId
);
if (
existingFeat !== null &&
!data.some((feat) => FeatUtils.getId(feat) === existingFeatId)
) {
data.push(existingFeat);
}
}
return data;
};
getSummary = () => {
const { speciesTrait, speciesName } = this.props;
// Get base name for the species trait
@@ -277,29 +161,9 @@ export default class SpeciesManageSpeciesTrait extends React.PureComponent<
}
};
renderChoices = (): React.ReactNode => {
const { speciesTrait } = this.props;
const { featData } = this.state;
const choices = RacialTraitUtils.getChoices(speciesTrait);
if (choices === null) {
return null;
}
return choices.map((choice) => (
<FeatureChoice
choice={choice}
featsData={featData}
onChoiceChange={this.handleChoiceChange}
key={ChoiceUtils.getId(choice)}
/>
));
};
render() {
const { speciesTrait } = this.props;
const { hasFeatChoice, loadingStatus } = this.state;
const { collapsibleOpened } = this.state;
const description = RacialTraitUtils.getDescription(speciesTrait);
const choices = RacialTraitUtils.getChoices(speciesTrait);
@@ -314,17 +178,6 @@ export default class SpeciesManageSpeciesTrait extends React.PureComponent<
conClsNames.push("collapsible-todo");
}
let contentNode: React.ReactNode;
if (hasFeatChoice) {
if (loadingStatus === DataLoadingStatusEnum.LOADED) {
contentNode = this.renderChoices();
} else {
contentNode = <LoadingPlaceholder />;
}
} else {
contentNode = this.renderChoices();
}
return (
<Accordion
id={accordionId}
@@ -339,7 +192,11 @@ export default class SpeciesManageSpeciesTrait extends React.PureComponent<
html={description ? description : ""}
withoutTooltips
/>
{contentNode}
<FeatureChoices
choices={RacialTraitUtils.getChoices(speciesTrait)}
onChoiceChange={this.handleChoiceChange}
shouldFetch={collapsibleOpened}
/>
</Accordion>
);
}