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,260 @@
|
||||
import React, { useContext } from "react";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import {
|
||||
rulesEngineSelectors,
|
||||
AbilityLookup,
|
||||
DataOriginRefData,
|
||||
InventoryManager,
|
||||
Item,
|
||||
ItemUtils,
|
||||
LimitedUseUtils,
|
||||
Spell,
|
||||
SpellUtils,
|
||||
RuleData,
|
||||
ItemManager,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
import { SpellName } from "~/components/SpellName";
|
||||
import { useSidebar } from "~/contexts/Sidebar";
|
||||
import { PaneInfo } from "~/contexts/Sidebar/Sidebar";
|
||||
import { PaneComponentEnum } from "~/subApps/sheet/components/Sidebar/types";
|
||||
|
||||
import SlotManager from "../../components/SlotManager";
|
||||
import SlotManagerLarge from "../../components/SlotManagerLarge";
|
||||
import { InventoryManagerContext } from "../../managers/InventoryManagerContext";
|
||||
import { appEnvSelectors } from "../../selectors";
|
||||
import { SharedAppState } from "../../stores/typings";
|
||||
import { PaneIdentifierUtils } from "../../utils";
|
||||
|
||||
interface Props {
|
||||
item: Item;
|
||||
largePoolMinAmount: number;
|
||||
abilityLookup: AbilityLookup;
|
||||
ruleData: RuleData;
|
||||
dataOriginRefData: DataOriginRefData;
|
||||
isReadonly: boolean;
|
||||
proficiencyBonus: number;
|
||||
inventoryManager: InventoryManager;
|
||||
paneHistoryPush: PaneInfo["paneHistoryPush"];
|
||||
}
|
||||
class ItemDetailAbilities extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
largePoolMinAmount: 11,
|
||||
};
|
||||
|
||||
handleSlotUseSet = (uses: number): void => {
|
||||
const { item: itemData } = this.props;
|
||||
|
||||
const item = ItemManager.getItem(ItemUtils.getMappingId(itemData));
|
||||
|
||||
item.handleItemLimitedUseSet(uses);
|
||||
};
|
||||
|
||||
handleSpellDetailClick = (id: number): void => {
|
||||
const { paneHistoryPush } = this.props;
|
||||
|
||||
paneHistoryPush(
|
||||
PaneComponentEnum.CHARACTER_SPELL_DETAIL,
|
||||
PaneIdentifierUtils.generateCharacterSpell(id)
|
||||
);
|
||||
};
|
||||
|
||||
renderSmallAmountSlotPool = (): React.ReactNode => {
|
||||
const { ruleData, abilityLookup, item, proficiencyBonus } = this.props;
|
||||
|
||||
let limitedUse = ItemUtils.getLimitedUse(item);
|
||||
if (limitedUse === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const numberUsed = LimitedUseUtils.getNumberUsed(limitedUse);
|
||||
const maxUses = LimitedUseUtils.deriveMaxUses(
|
||||
limitedUse,
|
||||
abilityLookup,
|
||||
ruleData,
|
||||
proficiencyBonus
|
||||
);
|
||||
|
||||
return (
|
||||
<SlotManager
|
||||
used={numberUsed}
|
||||
available={maxUses}
|
||||
size={"small"}
|
||||
onSet={this.handleSlotUseSet}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderLargeAmountSlotPool = (): React.ReactNode => {
|
||||
const { ruleData, abilityLookup, item, isReadonly, proficiencyBonus } =
|
||||
this.props;
|
||||
|
||||
let limitedUse = ItemUtils.getLimitedUse(item);
|
||||
if (limitedUse === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const numberUsed = LimitedUseUtils.getNumberUsed(limitedUse);
|
||||
const maxUses = LimitedUseUtils.deriveMaxUses(
|
||||
limitedUse,
|
||||
abilityLookup,
|
||||
ruleData,
|
||||
proficiencyBonus
|
||||
);
|
||||
|
||||
return (
|
||||
<SlotManagerLarge
|
||||
available={maxUses}
|
||||
used={numberUsed}
|
||||
onSet={this.handleSlotUseSet}
|
||||
isReadonly={isReadonly}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderLimitedUses = (): React.ReactNode => {
|
||||
const {
|
||||
item,
|
||||
largePoolMinAmount,
|
||||
abilityLookup,
|
||||
ruleData,
|
||||
proficiencyBonus,
|
||||
inventoryManager,
|
||||
} = this.props;
|
||||
|
||||
let limitedUse = ItemUtils.getLimitedUse(item);
|
||||
if (!limitedUse) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let initialMaxUses = LimitedUseUtils.getInitialMaxUses(limitedUse);
|
||||
if (initialMaxUses === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let maxUses = LimitedUseUtils.deriveMaxUses(
|
||||
limitedUse,
|
||||
abilityLookup,
|
||||
ruleData,
|
||||
proficiencyBonus
|
||||
);
|
||||
if (!maxUses) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const canUseCharges = inventoryManager.canUseItemCharges(item);
|
||||
|
||||
return canUseCharges ? (
|
||||
<div className="ct-item-detail-abilities__limited-uses">
|
||||
<div className="ct-item-detail-abilities__limited-uses-label">
|
||||
Charges
|
||||
</div>
|
||||
<div className="ct-item-detail-abilities__limited-uses-manager">
|
||||
{maxUses >= largePoolMinAmount
|
||||
? this.renderLargeAmountSlotPool()
|
||||
: this.renderSmallAmountSlotPool()}
|
||||
</div>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
renderSpell = (spell: Spell): React.ReactNode => {
|
||||
const { dataOriginRefData } = this.props;
|
||||
|
||||
let limitedUse = SpellUtils.getLimitedUse(spell);
|
||||
|
||||
let limitedUseNode: React.ReactNode = "At Will";
|
||||
if (limitedUse) {
|
||||
let minConsumed = LimitedUseUtils.getMinNumberConsumed(limitedUse);
|
||||
let maxConsumed = LimitedUseUtils.getMaxNumberConsumed(limitedUse);
|
||||
|
||||
if (minConsumed === maxConsumed || minConsumed === null) {
|
||||
limitedUseNode = `${maxConsumed} Charge${maxConsumed === 1 ? "" : "s"}`;
|
||||
} else {
|
||||
limitedUseNode = `${minConsumed}-${maxConsumed} Charges`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="ct-item-detail-abilities__spell"
|
||||
key={SpellUtils.getUniqueKey(spell)}
|
||||
>
|
||||
<div
|
||||
className="ct-item-detail-abilities__spell-name"
|
||||
onClick={this.handleSpellDetailClick.bind(
|
||||
this,
|
||||
SpellUtils.getMappingId(spell)
|
||||
)}
|
||||
>
|
||||
<SpellName
|
||||
spell={spell}
|
||||
showLegacy={true}
|
||||
dataOriginRefData={dataOriginRefData}
|
||||
/>
|
||||
</div>
|
||||
<div className="ct-item-detail-abilities__spell-usage">
|
||||
{limitedUseNode}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
renderSpells = (): React.ReactNode => {
|
||||
const { item } = this.props;
|
||||
|
||||
const spells = ItemUtils.getSpells(item);
|
||||
if (!spells || !spells.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ct-item-detail-abilities__spells">
|
||||
{spells.map((spell) => this.renderSpell(spell))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
let limitedUseNode = this.renderLimitedUses();
|
||||
let spellsNode = this.renderSpells();
|
||||
|
||||
if (!limitedUseNode && !spellsNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ct-item-detail-abilities">
|
||||
{limitedUseNode}
|
||||
{spellsNode}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state: SharedAppState) {
|
||||
return {
|
||||
abilityLookup: rulesEngineSelectors.getAbilityLookup(state),
|
||||
ruleData: rulesEngineSelectors.getRuleData(state),
|
||||
isReadonly: appEnvSelectors.getIsReadonly(state),
|
||||
dataOriginRefData: rulesEngineSelectors.getDataOriginRefData(state),
|
||||
proficiencyBonus: rulesEngineSelectors.getProficiencyBonus(state),
|
||||
};
|
||||
}
|
||||
|
||||
const ItemDetailAbilitiesWrapper = (props) => {
|
||||
const { inventoryManager } = useContext(InventoryManagerContext);
|
||||
const {
|
||||
pane: { paneHistoryPush },
|
||||
} = useSidebar();
|
||||
return (
|
||||
<ItemDetailAbilities
|
||||
inventoryManager={inventoryManager}
|
||||
paneHistoryPush={paneHistoryPush}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(ItemDetailAbilitiesWrapper);
|
||||
Reference in New Issue
Block a user