Grabbed dndbeyond's source code
``` ~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js ```
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
import { keyBy } from "lodash";
|
||||
import React from "react";
|
||||
|
||||
import { Snippet } from "@dndbeyond/character-components/es";
|
||||
import {
|
||||
AbilityLookup,
|
||||
BaseSpell,
|
||||
BaseFeat,
|
||||
Choice,
|
||||
ChoiceUtils,
|
||||
ClassDefinitionContract,
|
||||
Constants,
|
||||
DataOriginBaseAction,
|
||||
DataOriginRefData,
|
||||
InfusionChoice,
|
||||
LevelScaleContract,
|
||||
Option,
|
||||
OptionUtils,
|
||||
RuleData,
|
||||
RuleDataUtils,
|
||||
SnippetData,
|
||||
CharacterTheme,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
import { Reference } from "~/components/Reference";
|
||||
import FeatureSnippetActions from "~/tools/js/CharacterSheet/components/FeatureSnippet/FeatureSnippetActions";
|
||||
import FeatureSnippetChoices from "~/tools/js/CharacterSheet/components/FeatureSnippet/FeatureSnippetChoices";
|
||||
import FeatureSnippetInfusionChoices from "~/tools/js/CharacterSheet/components/FeatureSnippet/FeatureSnippetInfusionChoices";
|
||||
import FeatureSnippetOption from "~/tools/js/CharacterSheet/components/FeatureSnippet/FeatureSnippetOption";
|
||||
import FeatureSnippetSpells from "~/tools/js/CharacterSheet/components/FeatureSnippet/FeatureSnippetSpells";
|
||||
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
//TODO: tools/js/CharacterSheet/components/FeatureSnipper components still need to be gradually migrated to FC
|
||||
interface Props {
|
||||
heading: React.ReactNode;
|
||||
dataOriginExtra?: string;
|
||||
extraMeta: Array<string>;
|
||||
className: string;
|
||||
snippetData: SnippetData;
|
||||
ruleData: RuleData;
|
||||
abilityLookup: AbilityLookup;
|
||||
sourceId: number | null;
|
||||
sourcePage: number | null;
|
||||
|
||||
spells: Array<BaseSpell>;
|
||||
feats: Array<BaseFeat>;
|
||||
actions: Array<DataOriginBaseAction>;
|
||||
options: Array<Option>;
|
||||
choices: Array<Choice>;
|
||||
infusionChoices?: Array<InfusionChoice>;
|
||||
dataOriginRefData: DataOriginRefData;
|
||||
|
||||
levelScale?: LevelScaleContract | null;
|
||||
classLevel?: number;
|
||||
subclass?: ClassDefinitionContract | null;
|
||||
|
||||
onActionClick?: (action: DataOriginBaseAction) => void;
|
||||
onActionUseSet?: (action: DataOriginBaseAction, uses: number) => void;
|
||||
onSpellClick?: (spell: BaseSpell) => void;
|
||||
onSpellUseSet?: (spell: BaseSpell, uses: number) => void;
|
||||
onFeatureClick: () => void;
|
||||
onInfusionChoiceClick?: (infusionChoice: InfusionChoice) => void;
|
||||
|
||||
showHeader?: boolean;
|
||||
showDescription?: boolean;
|
||||
|
||||
isReadonly: boolean;
|
||||
proficiencyBonus: number;
|
||||
theme: CharacterTheme;
|
||||
}
|
||||
|
||||
export const FeatureSnippet: React.FC<Props> = ({
|
||||
heading,
|
||||
dataOriginExtra,
|
||||
extraMeta = [],
|
||||
choices,
|
||||
actions,
|
||||
spells,
|
||||
infusionChoices = [],
|
||||
className,
|
||||
snippetData,
|
||||
sourceId,
|
||||
sourcePage,
|
||||
classLevel,
|
||||
levelScale,
|
||||
children,
|
||||
abilityLookup,
|
||||
ruleData,
|
||||
subclass,
|
||||
options,
|
||||
feats,
|
||||
dataOriginRefData,
|
||||
onActionUseSet,
|
||||
onActionClick,
|
||||
onSpellClick,
|
||||
onSpellUseSet,
|
||||
onInfusionChoiceClick,
|
||||
onFeatureClick,
|
||||
showHeader = true,
|
||||
showDescription = false,
|
||||
isReadonly,
|
||||
proficiencyBonus,
|
||||
theme,
|
||||
}) => {
|
||||
const handleFeatureClick = (evt: React.MouseEvent): void => {
|
||||
evt.stopPropagation();
|
||||
evt.nativeEvent.stopImmediatePropagation();
|
||||
|
||||
if (onFeatureClick) {
|
||||
onFeatureClick();
|
||||
}
|
||||
};
|
||||
|
||||
const getNonChoiceOptions = (): Array<Option> => {
|
||||
let optionLookup = keyBy(options, (option) => OptionUtils.getId(option));
|
||||
let usedOptionIds: Array<number> = [];
|
||||
|
||||
choices.forEach((choice) => {
|
||||
const type = ChoiceUtils.getType(choice);
|
||||
const optionValue = ChoiceUtils.getOptionValue(choice);
|
||||
|
||||
switch (type) {
|
||||
case Constants.BuilderChoiceTypeEnum.RACIAL_TRAIT_OPTION:
|
||||
case Constants.BuilderChoiceTypeEnum.FEAT_OPTION:
|
||||
case Constants.BuilderChoiceTypeEnum.FEATURE_OPTION:
|
||||
if (optionValue !== null && optionLookup[optionValue]) {
|
||||
usedOptionIds.push(optionValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return options.filter(
|
||||
(option) => !usedOptionIds.includes(OptionUtils.getId(option))
|
||||
);
|
||||
};
|
||||
|
||||
const nonChoiceOptions = getNonChoiceOptions();
|
||||
const classNames = [styles.snippet, className];
|
||||
const metaItems: Array<React.ReactNode> = [];
|
||||
|
||||
if (levelScale) {
|
||||
metaItems.push(
|
||||
<span className={styles.levelScale}>{levelScale.description}</span>
|
||||
);
|
||||
}
|
||||
|
||||
metaItems.push(...extraMeta);
|
||||
|
||||
const sourceDataLookup = RuleDataUtils.getSourceDataLookup(ruleData);
|
||||
|
||||
if (sourceId) {
|
||||
metaItems.push(
|
||||
<Reference name={sourceDataLookup[sourceId]?.name} page={sourcePage} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames.join(" ")} onClick={handleFeatureClick}>
|
||||
{showHeader && (
|
||||
<div
|
||||
className={`${styles.heading} ${
|
||||
theme?.isDarkMode ? styles.headingDarkMode : ""
|
||||
}`}
|
||||
>
|
||||
{heading}
|
||||
<span className={styles.meta}>
|
||||
{metaItems.map((metaItem, idx) => (
|
||||
<span
|
||||
className={`${styles.metaItem} ${
|
||||
theme?.isDarkMode ? styles.metaItemDarkMode : ""
|
||||
}`}
|
||||
key={idx}
|
||||
>
|
||||
{metaItem}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
{dataOriginExtra && dataOriginExtra !== "Unknown" && (
|
||||
<div>
|
||||
<span className={styles.origin}>
|
||||
<span className={styles.originLabel}>From</span>
|
||||
<span className={styles.originName}>{dataOriginExtra}</span>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.content}>
|
||||
<Snippet
|
||||
snippetData={snippetData}
|
||||
levelScale={levelScale}
|
||||
classLevel={classLevel}
|
||||
parseSnippet={!showDescription}
|
||||
proficiencyBonus={proficiencyBonus}
|
||||
theme={theme}
|
||||
>
|
||||
{children}
|
||||
</Snippet>
|
||||
</div>
|
||||
{(choices.length > 0 ||
|
||||
actions.length > 0 ||
|
||||
spells.length > 0 ||
|
||||
nonChoiceOptions.length > 0) && (
|
||||
<div
|
||||
className={styles.extra}
|
||||
style={
|
||||
theme?.isDarkMode ? { borderColor: theme.themeColor } : undefined
|
||||
}
|
||||
>
|
||||
<FeatureSnippetChoices
|
||||
choices={choices}
|
||||
options={options}
|
||||
onSpellClick={onSpellClick}
|
||||
onSpellUseSet={onSpellUseSet}
|
||||
onActionUseSet={onActionUseSet}
|
||||
onActionClick={onActionClick}
|
||||
ruleData={ruleData}
|
||||
abilityLookup={abilityLookup}
|
||||
subclass={subclass}
|
||||
feats={feats}
|
||||
sourceDataLookup={sourceDataLookup}
|
||||
classLevel={classLevel}
|
||||
snippetData={snippetData}
|
||||
dataOriginRefData={dataOriginRefData}
|
||||
showDescription={showDescription}
|
||||
isInteractive={!isReadonly}
|
||||
proficiencyBonus={proficiencyBonus}
|
||||
theme={theme}
|
||||
/>
|
||||
<FeatureSnippetActions
|
||||
actions={actions}
|
||||
abilityLookup={abilityLookup}
|
||||
ruleData={ruleData}
|
||||
onActionClick={onActionClick}
|
||||
onActionUseSet={onActionUseSet}
|
||||
isInteractive={!isReadonly}
|
||||
proficiencyBonus={proficiencyBonus}
|
||||
theme={theme}
|
||||
/>
|
||||
<FeatureSnippetSpells
|
||||
spells={spells}
|
||||
abilityLookup={abilityLookup}
|
||||
ruleData={ruleData}
|
||||
onSpellClick={onSpellClick}
|
||||
onSpellUseSet={onSpellUseSet}
|
||||
isInteractive={!isReadonly}
|
||||
dataOriginRefData={dataOriginRefData}
|
||||
proficiencyBonus={proficiencyBonus}
|
||||
theme={theme}
|
||||
/>
|
||||
{nonChoiceOptions.length > 0 && (
|
||||
<div className={styles.options}>
|
||||
{nonChoiceOptions.map((option) => (
|
||||
<FeatureSnippetOption
|
||||
key={OptionUtils.getId(option)}
|
||||
option={option}
|
||||
onSpellClick={onSpellClick}
|
||||
onSpellUseSet={onSpellUseSet}
|
||||
onActionUseSet={onActionUseSet}
|
||||
onActionClick={onActionClick}
|
||||
abilityLookup={abilityLookup}
|
||||
ruleData={ruleData}
|
||||
sourceDataLookup={sourceDataLookup}
|
||||
classLevel={classLevel}
|
||||
snippetData={snippetData}
|
||||
showDescription={showDescription}
|
||||
isInteractive={!isReadonly}
|
||||
dataOriginRefData={dataOriginRefData}
|
||||
proficiencyBonus={proficiencyBonus}
|
||||
theme={theme}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<FeatureSnippetInfusionChoices
|
||||
infusionChoices={infusionChoices}
|
||||
onInfusionChoiceClick={onInfusionChoiceClick}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user