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:
2025-05-28 11:50:03 -07:00
commit 8df9031d27
3592 changed files with 319051 additions and 0 deletions
@@ -0,0 +1,341 @@
import React from "react";
import { connect, DispatchProp } from "react-redux";
import {
BaseInventoryContract,
rulesEngineSelectors,
characterActions,
ClassFeatureContract,
ClassFeatureUtils,
ClassUtils,
Constants,
DataOriginRefData,
EntityUtils,
EntityValueLookup,
FeatDetailsContract,
FeatUtils,
FormatUtils,
Hack__BaseCharClass,
HelperUtils,
ItemUtils,
InventoryLookup,
RacialTraitContract,
RacialTraitUtils,
Spell,
SpellCasterInfo,
SpellUtils,
ValueUtils,
AnySimpleDataType,
RuleData,
CharacterTheme,
} from "@dndbeyond/character-rules-engine/es";
import { EditableName } from "~/components/EditableName";
import { ItemName } from "~/components/ItemName";
import { SpellName } from "~/components/SpellName";
import { useSidebar } from "~/contexts/Sidebar";
import { PaneInfo } from "~/contexts/Sidebar/Sidebar";
import { Header } from "~/subApps/sheet/components/Sidebar/components/Header";
import { Preview } from "~/subApps/sheet/components/Sidebar/components/Preview";
import {
PaneComponentEnum,
PaneIdentifiers,
PaneIdentifiersCharacterSpell,
} from "~/subApps/sheet/components/Sidebar/types";
import { PaneInitFailureContent } from "../../../../../../subApps/sheet/components/Sidebar/components/PaneInitFailureContent";
import SpellDetail from "../../../components/SpellDetail";
import { appEnvSelectors } from "../../../selectors";
import { SharedAppState } from "../../../stores/typings";
import { PaneIdentifierUtils } from "../../../utils";
interface Props extends DispatchProp {
spells: Array<Spell>;
spellCasterInfo: SpellCasterInfo;
ruleData: RuleData;
entityValueLookup: EntityValueLookup;
inventoryLookup: InventoryLookup;
dataOriginRefData: DataOriginRefData;
identifiers: PaneIdentifiersCharacterSpell | null;
isReadonly: boolean;
proficiencyBonus: number;
theme: CharacterTheme;
paneHistoryPush: PaneInfo["paneHistoryPush"];
}
interface State {
spell: Spell | null;
isCustomizeClosed: boolean;
}
class CharacterSpellPane extends React.PureComponent<Props, State> {
constructor(props) {
super(props);
this.state = this.generateStateData(props, true);
}
componentDidUpdate(
prevProps: Readonly<Props>,
prevState: Readonly<State>
): void {
const { spells, identifiers } = this.props;
if (spells !== prevProps.spells || identifiers !== prevProps.identifiers) {
this.setState(
this.generateStateData(this.props, prevState.isCustomizeClosed)
);
}
}
generateStateData = (props: Props, isCustomizeClosed: boolean): State => {
const { spells, identifiers } = props;
let foundSpell: Spell | null | undefined = null;
if (identifiers !== null) {
foundSpell = spells.find(
(spell) => identifiers.id === SpellUtils.getMappingId(spell)
);
}
return {
spell: foundSpell ? foundSpell : null,
isCustomizeClosed,
};
};
handleOpenCustomize = () => {
this.setState({ isCustomizeClosed: !this.state.isCustomizeClosed });
};
handleItemShow = (): void => {
const { spell } = this.state;
const { paneHistoryPush } = this.props;
if (spell) {
const dataOrigin = SpellUtils.getDataOrigin(spell);
paneHistoryPush(
PaneComponentEnum.ITEM_DETAIL,
PaneIdentifierUtils.generateItem(
ItemUtils.getMappingId(dataOrigin.primary as BaseInventoryContract)
)
);
}
};
handleCustomDataUpdate = (
key: number,
value: AnySimpleDataType,
source: string | null
): void => {
const { spell } = this.state;
const { dispatch } = this.props;
if (spell) {
dispatch(
characterActions.valueSet(
key,
value,
source,
ValueUtils.hack__toString(SpellUtils.getMappingId(spell)),
ValueUtils.hack__toString(SpellUtils.getMappingEntityTypeId(spell))
)
);
}
};
handleRemoveCustomizations = (): void => {
const { spell } = this.state;
const { dispatch } = this.props;
if (spell) {
let mappingId = SpellUtils.getMappingId(spell);
let mappingEntityTypeId = SpellUtils.getMappingEntityTypeId(spell);
if (mappingId !== null && mappingEntityTypeId !== null) {
let [contextId, contextTypeId] =
SpellUtils.deriveExpandedContextIds(spell);
dispatch(
characterActions.spellCustomizationsDelete(
mappingId,
mappingEntityTypeId,
contextId,
contextTypeId
)
);
}
}
};
handleParentClick = (): void => {
const { spell } = this.state;
const { paneHistoryPush } = this.props;
if (spell === null) {
return;
}
let componentType: PaneComponentEnum | null = null;
let componentIds: PaneIdentifiers | null = null;
const spellDataOrigin = SpellUtils.getDataOrigin(spell);
const spellDataOriginType = SpellUtils.getDataOriginType(spell);
switch (spellDataOriginType) {
case Constants.DataOriginTypeEnum.CLASS_FEATURE:
componentType = PaneComponentEnum.CLASS_FEATURE_DETAIL;
componentIds = PaneIdentifierUtils.generateClassFeature(
ClassFeatureUtils.getId(
spellDataOrigin.primary as ClassFeatureContract
),
ClassUtils.getMappingId(spellDataOrigin.parent as Hack__BaseCharClass)
);
break;
case Constants.DataOriginTypeEnum.RACE:
componentType = PaneComponentEnum.SPECIES_TRAIT_DETAIL;
componentIds = PaneIdentifierUtils.generateRacialTrait(
RacialTraitUtils.getId(spellDataOrigin.primary as RacialTraitContract)
);
break;
case Constants.DataOriginTypeEnum.FEAT:
componentType = PaneComponentEnum.FEAT_DETAIL;
componentIds = PaneIdentifierUtils.generateFeat(
FeatUtils.getId(spellDataOrigin.primary as FeatDetailsContract)
);
break;
default:
// not implemented
}
if (componentType !== null && componentIds !== null) {
paneHistoryPush(componentType, componentIds);
}
};
render() {
const { spell, isCustomizeClosed } = this.state;
const {
spellCasterInfo,
identifiers,
ruleData,
entityValueLookup,
inventoryLookup,
dataOriginRefData,
isReadonly,
proficiencyBonus,
theme,
} = this.props;
if (spell === null) {
return <PaneInitFailureContent />;
}
let parentNode: React.ReactNode;
const spellDataOrigin = SpellUtils.getDataOrigin(spell);
const spellDataOriginType = SpellUtils.getDataOriginType(spell);
switch (spellDataOriginType) {
case Constants.DataOriginTypeEnum.ITEM:
const mappingId = ItemUtils.getMappingId(
spellDataOrigin.primary as BaseInventoryContract
);
const item = HelperUtils.lookupDataOrFallback(
inventoryLookup,
mappingId
);
if (item !== null) {
parentNode = <ItemName item={item} onClick={this.handleItemShow} />;
}
break;
default:
if (spellDataOrigin.primary !== null) {
parentNode = (
<div onClick={this.handleParentClick}>
{EntityUtils.getDataOriginName(spellDataOrigin)}
</div>
);
}
break;
}
let school = SpellUtils.getSchool(spell);
let schoolSlug = FormatUtils.slugify(school);
const dataOrigin = SpellUtils.getDataOrigin(spell);
const dataOriginType = SpellUtils.getDataOriginType(spell);
return (
<div className="ct-spell-pane">
<Header
parent={parentNode}
preview={
<Preview>
<div
className={`ct-spell-pane__heading-preview ct-spell-pane__heading-preview--school-${schoolSlug}`}
/>
</Preview>
}
>
{
//If the spell is coming from a magic item we should not show the Edit button
!dataOrigin ||
(dataOrigin &&
dataOriginType === Constants.DataOriginTypeEnum.ITEM) ? (
<SpellName
spell={spell}
showSpellLevel={false}
dataOriginRefData={dataOriginRefData}
/>
) : (
<EditableName onClick={this.handleOpenCustomize}>
<SpellName
spell={spell}
showSpellLevel={false}
dataOriginRefData={dataOriginRefData}
/>
</EditableName>
)
}
</Header>
<SpellDetail
theme={theme}
key={SpellUtils.getUniqueKey(spell)}
spell={spell}
spellCasterInfo={spellCasterInfo}
initialCastLevel={identifiers?.castLevel}
ruleData={ruleData}
onCustomDataUpdate={this.handleCustomDataUpdate}
onCustomizationsRemove={this.handleRemoveCustomizations}
entityValueLookup={entityValueLookup}
isReadonly={isReadonly}
proficiencyBonus={proficiencyBonus}
isCustomizeClosed={isCustomizeClosed}
onCustomizeClick={this.handleOpenCustomize}
/>
</div>
);
}
}
function mapStateToProps(state: SharedAppState) {
return {
spells: rulesEngineSelectors.getCharacterSpells(state),
spellCasterInfo: rulesEngineSelectors.getSpellCasterInfo(state),
ruleData: rulesEngineSelectors.getRuleData(state),
entityValueLookup:
rulesEngineSelectors.getCharacterValueLookupByEntity(state),
inventoryLookup: rulesEngineSelectors.getInventoryLookup(state),
dataOriginRefData: rulesEngineSelectors.getDataOriginRefData(state),
isReadonly: appEnvSelectors.getIsReadonly(state),
proficiencyBonus: rulesEngineSelectors.getProficiencyBonus(state),
theme: rulesEngineSelectors.getCharacterTheme(state),
};
}
const CharacterSpellPaneContainer = (props) => {
const {
pane: { paneHistoryPush },
} = useSidebar();
return <CharacterSpellPane paneHistoryPush={paneHistoryPush} {...props} />;
};
export default connect(mapStateToProps)(CharacterSpellPaneContainer);
@@ -0,0 +1,4 @@
import CharacterSpellPane from "./CharacterSpellPane";
export default CharacterSpellPane;
export { CharacterSpellPane };