New source found from dndbeyond.com
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
import clsx from "clsx";
|
||||
import { FC, HTMLAttributes, useEffect, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import {
|
||||
ApiAdapterUtils,
|
||||
apiCreatorSelectors,
|
||||
CharClass,
|
||||
Choice,
|
||||
ChoiceUtils,
|
||||
ClassDefinitionContract,
|
||||
ClassUtils,
|
||||
Constants,
|
||||
Feat,
|
||||
FeatUtils,
|
||||
} from "@dndbeyond/character-rules-engine";
|
||||
|
||||
import { Heading } from "~/subApps/sheet/components/Sidebar/components/Heading";
|
||||
import { appEnvSelectors } from "~/tools/js/Shared/selectors";
|
||||
import { AppLoggerUtils } from "~/tools/js/Shared/utils";
|
||||
import LoadingPlaceholder from "~/tools/js/smartComponents/LoadingPlaceholder";
|
||||
import { DataLoadingStatusEnum } from "~/tools/js/smartComponents/componentConstants";
|
||||
|
||||
import { FeatureChoice } from "./FeatureChoice";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
export interface FeatureChoicesProps extends HTMLAttributes<HTMLDivElement> {
|
||||
choices: Array<Choice>;
|
||||
showHeading?: boolean;
|
||||
onChoiceChange: (
|
||||
choiceId: string,
|
||||
type: number,
|
||||
value: any,
|
||||
parentChoiceId: string | null
|
||||
) => void;
|
||||
collapseDescription?: boolean;
|
||||
charClass?: CharClass;
|
||||
shouldFetch?: boolean; // Optional prop to control fetching behavior
|
||||
}
|
||||
|
||||
export const FeatureChoices: FC<FeatureChoicesProps> = ({
|
||||
className,
|
||||
choices,
|
||||
showHeading,
|
||||
onChoiceChange,
|
||||
collapseDescription,
|
||||
charClass,
|
||||
shouldFetch = true, // Default to true if not provided
|
||||
...props
|
||||
}) => {
|
||||
const isReadonly = useSelector(appEnvSelectors.getIsReadonly);
|
||||
const loadAvailableFeats = useSelector(
|
||||
apiCreatorSelectors.makeLoadAvailableFeats
|
||||
);
|
||||
const loadAvailableSubclasses = useSelector(
|
||||
apiCreatorSelectors.makeLoadAvailableSubclasses
|
||||
);
|
||||
|
||||
const [subclassData, setSubclassData] = useState<
|
||||
Array<ClassDefinitionContract>
|
||||
>([]);
|
||||
const [featsData, setFeatsData] = useState<Array<Feat>>([]);
|
||||
const [featLoadingStatus, setFeatLoadingStatus] =
|
||||
useState<DataLoadingStatusEnum>(DataLoadingStatusEnum.NOT_INITIALIZED);
|
||||
const [subclassLoadingStatus, setSubclassLoadingStatus] =
|
||||
useState<DataLoadingStatusEnum>(DataLoadingStatusEnum.NOT_INITIALIZED);
|
||||
|
||||
const hasFeatChoice = choices.some(
|
||||
(choice) =>
|
||||
ChoiceUtils.getType(choice) ===
|
||||
Constants.BuilderChoiceTypeEnum.FEAT_CHOICE_OPTION
|
||||
);
|
||||
|
||||
const hasSubclassChoice = choices.some(
|
||||
(choice) =>
|
||||
ChoiceUtils.getType(choice) ===
|
||||
Constants.BuilderChoiceTypeEnum.SUB_CLASS_OPTION
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
hasFeatChoice &&
|
||||
shouldFetch &&
|
||||
featLoadingStatus === DataLoadingStatusEnum.NOT_INITIALIZED
|
||||
) {
|
||||
setFeatLoadingStatus(DataLoadingStatusEnum.LOADING);
|
||||
|
||||
loadAvailableFeats()
|
||||
.then((response) => {
|
||||
let featsData: Array<Feat> = [];
|
||||
|
||||
const data = ApiAdapterUtils.getResponseData(response);
|
||||
if (data !== null) {
|
||||
featsData = data.map((definition) =>
|
||||
FeatUtils.simulateFeat(definition)
|
||||
);
|
||||
}
|
||||
|
||||
setFeatsData(featsData);
|
||||
setFeatLoadingStatus(DataLoadingStatusEnum.LOADED);
|
||||
})
|
||||
.catch(AppLoggerUtils.handleAdhocApiError);
|
||||
}
|
||||
|
||||
if (
|
||||
charClass &&
|
||||
hasSubclassChoice &&
|
||||
shouldFetch &&
|
||||
subclassLoadingStatus === DataLoadingStatusEnum.NOT_INITIALIZED
|
||||
) {
|
||||
setSubclassLoadingStatus(DataLoadingStatusEnum.LOADING);
|
||||
loadAvailableSubclasses(ClassUtils.getId(charClass))
|
||||
.then((response) => {
|
||||
let subclassData: Array<ClassDefinitionContract> = [];
|
||||
|
||||
const data = ApiAdapterUtils.getResponseData(response);
|
||||
if (data !== null) {
|
||||
subclassData = data;
|
||||
}
|
||||
|
||||
setSubclassData(subclassData);
|
||||
setSubclassLoadingStatus(DataLoadingStatusEnum.LOADED);
|
||||
})
|
||||
.catch(AppLoggerUtils.handleAdhocApiError);
|
||||
}
|
||||
}, [
|
||||
hasFeatChoice,
|
||||
hasSubclassChoice,
|
||||
subclassLoadingStatus,
|
||||
featLoadingStatus,
|
||||
charClass,
|
||||
loadAvailableFeats,
|
||||
loadAvailableSubclasses,
|
||||
shouldFetch,
|
||||
]);
|
||||
|
||||
const isDataLoaded = (): boolean => {
|
||||
if (hasSubclassChoice) {
|
||||
if (subclassLoadingStatus !== DataLoadingStatusEnum.LOADED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (hasFeatChoice) {
|
||||
if (featLoadingStatus !== DataLoadingStatusEnum.LOADED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return !isReadonly && choices.length > 0 ? (
|
||||
<div className={clsx([styles.featureChoices, className])} {...props}>
|
||||
<>
|
||||
{showHeading && (
|
||||
<Heading className={styles.heading}>{`Option${
|
||||
choices.length > 1 ? "s" : ""
|
||||
}`}</Heading>
|
||||
)}
|
||||
{isDataLoaded() ? (
|
||||
choices.map((choice) => (
|
||||
<FeatureChoice
|
||||
charClass={charClass}
|
||||
choice={choice}
|
||||
featsData={featsData}
|
||||
subclassData={subclassData}
|
||||
className={styles.choice}
|
||||
key={ChoiceUtils.getId(choice)}
|
||||
onChoiceChange={onChoiceChange}
|
||||
collapseDescription={collapseDescription}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<LoadingPlaceholder />
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
Reference in New Issue
Block a user