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,450 @@
|
||||
import React from "react";
|
||||
import { useContext } from "react";
|
||||
import { connect, DispatchProp } from "react-redux";
|
||||
|
||||
import {
|
||||
rulesEngineSelectors,
|
||||
CampaignUtils,
|
||||
serviceDataActions,
|
||||
Constants,
|
||||
Container,
|
||||
ContainerUtils,
|
||||
InventoryManager,
|
||||
InfusionUtils,
|
||||
Item,
|
||||
ItemUtils,
|
||||
RuleData,
|
||||
GroupedMenuOption,
|
||||
serviceDataSelectors,
|
||||
PartyInfo,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
import { Button } from "~/components/Button";
|
||||
import { Popover } from "~/components/Popover";
|
||||
import { PopoverContent } from "~/components/PopoverContent";
|
||||
import { useSidebar } from "~/contexts/Sidebar";
|
||||
import { PaneInfo } from "~/contexts/Sidebar/Sidebar";
|
||||
import { PaneComponentEnum } from "~/subApps/sheet/components/Sidebar/types";
|
||||
|
||||
import SimpleQuantity from "../../components/SimpleQuantity";
|
||||
import {
|
||||
RemoveButton,
|
||||
ThemeButton,
|
||||
ThemeButtonWithMenu,
|
||||
} from "../../components/common/Button";
|
||||
import { InventoryManagerContext } from "../../managers/InventoryManagerContext";
|
||||
import * as appEnvSelectors from "../../selectors/appEnv";
|
||||
import { SharedAppState } from "../../stores/typings";
|
||||
import { PaneIdentifierUtils } from "../../utils";
|
||||
|
||||
interface LabelLookup {
|
||||
consume: string;
|
||||
remove: string;
|
||||
unequip: string;
|
||||
equip: string;
|
||||
attune: string;
|
||||
unattune: string;
|
||||
}
|
||||
|
||||
interface Props extends DispatchProp {
|
||||
item: Item;
|
||||
hasMaxAttunedItems: boolean;
|
||||
ruleData: RuleData;
|
||||
isReadonly: boolean;
|
||||
containers: Array<Container>;
|
||||
onPostRemoveNavigation?: PaneComponentEnum;
|
||||
partyInfo: PartyInfo | null;
|
||||
inventoryManager: InventoryManager;
|
||||
paneHistoryStart: PaneInfo["paneHistoryStart"];
|
||||
}
|
||||
interface State {
|
||||
quantity: number;
|
||||
newQuantity: number;
|
||||
}
|
||||
class ItemDetailActions extends React.PureComponent<Props, State> {
|
||||
static defaultProps = {
|
||||
hasMaxAttunedItems: false,
|
||||
};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
quantity: ItemUtils.getQuantity(props.item),
|
||||
newQuantity: ItemUtils.getQuantity(props.item),
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(
|
||||
prevProps: Readonly<Props>,
|
||||
prevState: Readonly<State>,
|
||||
snapshot?: any
|
||||
): void {
|
||||
const { item } = this.props;
|
||||
|
||||
const nextQuantity = ItemUtils.getQuantity(item);
|
||||
if (nextQuantity !== prevState.quantity) {
|
||||
this.setState({
|
||||
quantity: nextQuantity,
|
||||
newQuantity: nextQuantity,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleQuantityUpdate = (newValue: number): void => {
|
||||
const { item, inventoryManager } = this.props;
|
||||
|
||||
inventoryManager.handleQuantitySet({ item, quantity: newValue });
|
||||
};
|
||||
|
||||
handleRemove = (): void => {
|
||||
const { paneHistoryStart, item, onPostRemoveNavigation, inventoryManager } =
|
||||
this.props;
|
||||
|
||||
if (onPostRemoveNavigation) {
|
||||
paneHistoryStart(onPostRemoveNavigation);
|
||||
}
|
||||
|
||||
inventoryManager.handleRemove({ item });
|
||||
|
||||
if (ItemUtils.isContainer(item)) {
|
||||
paneHistoryStart(PaneComponentEnum.EQUIPMENT_MANAGE);
|
||||
}
|
||||
};
|
||||
|
||||
handleRemoveInfusion = (): void => {
|
||||
const { dispatch, paneHistoryStart, item } = this.props;
|
||||
|
||||
const infusion = ItemUtils.getInfusion(item);
|
||||
if (infusion) {
|
||||
const infusionId = InfusionUtils.getId(infusion);
|
||||
if (infusionId === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
InfusionUtils.getType(infusion) === Constants.InfusionTypeEnum.REPLICATE
|
||||
) {
|
||||
const choiceKey = InfusionUtils.getChoiceKey(infusion);
|
||||
if (choiceKey !== null) {
|
||||
paneHistoryStart(
|
||||
PaneComponentEnum.INFUSION_CHOICE,
|
||||
PaneIdentifierUtils.generateInfusionChoice(choiceKey)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(
|
||||
serviceDataActions.infusionMappingDestroy(
|
||||
infusionId,
|
||||
InfusionUtils.getInventoryMappingId(infusion)
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
handleRemoveInfusions = (itemIds: Array<number>): void => {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
itemIds.forEach((id) => {
|
||||
dispatch(serviceDataActions.infusionMappingsDestroy(itemIds));
|
||||
});
|
||||
};
|
||||
|
||||
handleUnequip = (): void => {
|
||||
const { inventoryManager, item } = this.props;
|
||||
inventoryManager.handleUnequip({ item });
|
||||
};
|
||||
|
||||
handleEquip = (): void => {
|
||||
const { inventoryManager, item } = this.props;
|
||||
inventoryManager.handleEquip({ item });
|
||||
};
|
||||
|
||||
handleAttune = (): void => {
|
||||
const { inventoryManager, item } = this.props;
|
||||
|
||||
inventoryManager.handleEquip({ item });
|
||||
inventoryManager.handleAttune({ item });
|
||||
};
|
||||
|
||||
handleUnattune = (): void => {
|
||||
const { inventoryManager, item } = this.props;
|
||||
|
||||
inventoryManager.handleUnattune({ item });
|
||||
};
|
||||
|
||||
handleMove = (containerDefinitionKey: string): void => {
|
||||
const { inventoryManager, item } = this.props;
|
||||
|
||||
inventoryManager.handleMove({ item, containerDefinitionKey });
|
||||
};
|
||||
|
||||
getLabels = (): LabelLookup => {
|
||||
return {
|
||||
consume: "Consume",
|
||||
remove: "Delete Item",
|
||||
unequip: "Unequip",
|
||||
equip: "Equip",
|
||||
attune: "Attune",
|
||||
unattune: "Unattune",
|
||||
};
|
||||
};
|
||||
|
||||
getContainerOptions = (): Array<GroupedMenuOption> => {
|
||||
const { containers, item, partyInfo } = this.props;
|
||||
|
||||
return ContainerUtils.getGroupedOptions(
|
||||
ItemUtils.getContainerDefinitionKey(item),
|
||||
containers,
|
||||
"Move To:",
|
||||
partyInfo
|
||||
? CampaignUtils.getSharingState(partyInfo)
|
||||
: Constants.PartyInventorySharingStateEnum.OFF
|
||||
);
|
||||
};
|
||||
|
||||
renderRemove = (): React.ReactNode => {
|
||||
const { item, containers, isReadonly, inventoryManager } = this.props;
|
||||
|
||||
const canRemoveItem = inventoryManager.canRemoveItem(item);
|
||||
if (isReadonly || !canRemoveItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const infusion = ItemUtils.getInfusion(item);
|
||||
|
||||
const name = ItemUtils.getName(item);
|
||||
const isContainer = ItemUtils.isContainer(item);
|
||||
|
||||
if (isContainer) {
|
||||
const container = containers.find((container) => {
|
||||
return (
|
||||
ContainerUtils.getContainerType(container) ===
|
||||
Constants.ContainerTypeEnum.ITEM &&
|
||||
ContainerUtils.getMappingId(container) ===
|
||||
ItemUtils.getMappingId(item)
|
||||
);
|
||||
});
|
||||
|
||||
if (container) {
|
||||
if (infusion) {
|
||||
return (
|
||||
<RemoveButton
|
||||
onClick={this.handleRemove}
|
||||
className="ct-item-detail__action"
|
||||
data-testid="remove-infusion-button"
|
||||
>
|
||||
Remove Infusion
|
||||
</RemoveButton>
|
||||
);
|
||||
}
|
||||
const hasInfusions = ContainerUtils.hasInfusions(container);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
trigger={
|
||||
<Button size="xx-small" variant="outline" themed>
|
||||
Delete
|
||||
</Button>
|
||||
}
|
||||
position="bottom"
|
||||
data-testid="delete-item-button"
|
||||
maxWidth={250}
|
||||
>
|
||||
<PopoverContent
|
||||
title={`Remove ${name}?`}
|
||||
content={`Removing the ${name} will also remove all of its ${
|
||||
hasInfusions ? "infusions and " : " "
|
||||
} contents.`}
|
||||
confirmText="Delete"
|
||||
onConfirm={() => this.handleRemove()}
|
||||
withCancel
|
||||
/>
|
||||
</Popover>
|
||||
// <RemoveButtonWithModal
|
||||
// className="ct-item-detail__action"
|
||||
// style={"outline"}
|
||||
// size="small"
|
||||
// modalTitle={`Delete ${name}?`}
|
||||
// modalContent={`Deleting the ${name} will also delete all of its${
|
||||
// hasInfusions ? " infusions and" : ""
|
||||
// } contents.`}
|
||||
// confirmCallback={() => {
|
||||
// this.handleRemove();
|
||||
// }}
|
||||
// data-testid="delete-item-button"
|
||||
// />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (infusion) {
|
||||
return (
|
||||
<RemoveButton
|
||||
onClick={this.handleRemoveInfusion}
|
||||
className="ct-item-detail__action"
|
||||
data-testid="remove-infusion-button"
|
||||
>
|
||||
Remove Infusion
|
||||
</RemoveButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<RemoveButton
|
||||
onClick={this.handleRemove}
|
||||
className="ct-item-detail__action"
|
||||
data-testid="delete-item-button"
|
||||
>
|
||||
Delete
|
||||
</RemoveButton>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
item,
|
||||
hasMaxAttunedItems,
|
||||
isReadonly,
|
||||
containers,
|
||||
inventoryManager,
|
||||
} = this.props;
|
||||
|
||||
const labels = this.getLabels();
|
||||
const containerMoveOptions = this.getContainerOptions();
|
||||
const isEquipped = ItemUtils.isEquipped(item);
|
||||
const isAttuned = ItemUtils.isAttuned(item);
|
||||
const isStackable = ItemUtils.isStackable(item);
|
||||
const quantity = ItemUtils.getQuantity(item);
|
||||
const isContainer = ItemUtils.getDefinitionIsContainer(item);
|
||||
const parentContainer = ContainerUtils.getItemParentContainer(
|
||||
containers,
|
||||
ItemUtils.getContainerDefinitionKey(item)
|
||||
);
|
||||
if (!parentContainer) {
|
||||
return null;
|
||||
}
|
||||
const canEquip = inventoryManager.canEquipUnequipItem(item);
|
||||
const canAttune = inventoryManager.canAttuneUnattuneItem(item);
|
||||
const canModifyQuantity = inventoryManager.canModifyQuantity(item);
|
||||
const canMoveItem =
|
||||
inventoryManager.canMoveItem(item) &&
|
||||
!isContainer &&
|
||||
!isReadonly &&
|
||||
!!containers.length;
|
||||
const canShareItem = inventoryManager.canShareItem(item) && !isReadonly;
|
||||
const canClaimItem = inventoryManager.canClaimItem(item) && !isReadonly;
|
||||
|
||||
return (
|
||||
<div className="ct-item-detail__actions">
|
||||
{isStackable && (
|
||||
<SimpleQuantity
|
||||
quantity={quantity}
|
||||
onUpdate={this.handleQuantityUpdate}
|
||||
isReadonly={isReadonly || !canModifyQuantity}
|
||||
/>
|
||||
)}
|
||||
|
||||
{canEquip && !isReadonly && (
|
||||
<ThemeButton
|
||||
onClick={isEquipped ? this.handleUnequip : this.handleEquip}
|
||||
style={isEquipped ? "" : "outline"}
|
||||
size="small"
|
||||
className="ct-item-detail__action"
|
||||
data-testid="equip-item-button"
|
||||
>
|
||||
{isEquipped ? labels.unequip : labels.equip}
|
||||
</ThemeButton>
|
||||
)}
|
||||
{canAttune && !isReadonly && (
|
||||
<ThemeButton
|
||||
onClick={isAttuned ? this.handleUnattune : this.handleAttune}
|
||||
style={isAttuned ? "" : "outline"}
|
||||
size="small"
|
||||
disabled={hasMaxAttunedItems && !isAttuned}
|
||||
className="ct-item-detail__action"
|
||||
data-testid="attune-item-button"
|
||||
>
|
||||
{isAttuned ? labels.unattune : labels.attune}
|
||||
</ThemeButton>
|
||||
)}
|
||||
{canShareItem && (
|
||||
<ThemeButton
|
||||
style="outline"
|
||||
onClick={() => {
|
||||
const definitionKey =
|
||||
inventoryManager.getPartyEquipmentContainerDefinitionKey();
|
||||
if (definitionKey) {
|
||||
this.handleMove(definitionKey);
|
||||
}
|
||||
}}
|
||||
size="small"
|
||||
className="ct-item-detail__action"
|
||||
data-testid="move-item-button"
|
||||
>
|
||||
Move
|
||||
</ThemeButton>
|
||||
)}
|
||||
{canClaimItem && (
|
||||
<ThemeButton
|
||||
style="outline"
|
||||
onClick={() => {
|
||||
const definitionKey =
|
||||
inventoryManager.getCharacterContainerDefinitionKey();
|
||||
if (definitionKey) {
|
||||
this.handleMove(definitionKey);
|
||||
}
|
||||
}}
|
||||
size="small"
|
||||
className="ct-item-detail__action"
|
||||
data-testid="move-item-button"
|
||||
>
|
||||
Move
|
||||
</ThemeButton>
|
||||
)}
|
||||
{canMoveItem && (
|
||||
<ThemeButtonWithMenu
|
||||
buttonStyle="outline"
|
||||
containerEl={
|
||||
document.querySelector(".ct-sidebar__portal") as HTMLElement
|
||||
}
|
||||
className="ct-item-detail__action"
|
||||
groupedOptions={containerMoveOptions}
|
||||
showSingleOption={true}
|
||||
onSelect={this.handleMove}
|
||||
>
|
||||
Move
|
||||
</ThemeButtonWithMenu>
|
||||
)}
|
||||
{this.renderRemove()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state: SharedAppState) {
|
||||
return {
|
||||
ruleData: rulesEngineSelectors.getRuleData(state),
|
||||
isReadonly: appEnvSelectors.getIsReadonly(state),
|
||||
hasMaxAttunedItems: rulesEngineSelectors.hasMaxAttunedItems(state),
|
||||
containers: rulesEngineSelectors.getInventoryContainers(state),
|
||||
partyInfo: serviceDataSelectors.getPartyInfo(state),
|
||||
};
|
||||
}
|
||||
|
||||
function ItemDetailActionsContainer(props) {
|
||||
const { inventoryManager } = useContext(InventoryManagerContext);
|
||||
const {
|
||||
pane: { paneHistoryStart },
|
||||
} = useSidebar();
|
||||
return (
|
||||
<ItemDetailActions
|
||||
inventoryManager={inventoryManager}
|
||||
paneHistoryStart={paneHistoryStart}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(ItemDetailActionsContainer);
|
||||
@@ -0,0 +1,4 @@
|
||||
import ItemDetailActions from "./ItemDetailActions";
|
||||
|
||||
export default ItemDetailActions;
|
||||
export { ItemDetailActions };
|
||||
Reference in New Issue
Block a user