745 lines
22 KiB
TypeScript
745 lines
22 KiB
TypeScript
import clsx from "clsx";
|
|
import { useContext, ReactNode, PureComponent } from "react";
|
|
import React from "react";
|
|
import { connect } from "react-redux";
|
|
|
|
import { DarkBuilderSvg } from "@dndbeyond/character-components/es";
|
|
import {
|
|
CharacterNotes,
|
|
CharacterTheme,
|
|
Constants,
|
|
ContainerManager,
|
|
Creature,
|
|
CreatureUtils,
|
|
InfusionChoice,
|
|
InfusionChoiceUtils,
|
|
Item,
|
|
ItemUtils,
|
|
SnippetData,
|
|
RuleData,
|
|
rulesEngineSelectors,
|
|
ContainerUtils,
|
|
InventoryManager,
|
|
FormatUtils,
|
|
serviceDataSelectors,
|
|
PartyInfo,
|
|
CoinManager,
|
|
ClassUtils,
|
|
CharClass,
|
|
ItemPlan,
|
|
ItemPlanUtils,
|
|
} from "@dndbeyond/character-rules-engine/es";
|
|
|
|
import { Button } from "~/components/Button";
|
|
import { ItemName } from "~/components/ItemName";
|
|
import { Link } from "~/components/Link";
|
|
import { NumberDisplay } from "~/components/NumberDisplay";
|
|
import { TabFilter } from "~/components/TabFilter";
|
|
import { TabList } from "~/components/TabList";
|
|
import { useSidebar } from "~/contexts/Sidebar";
|
|
import { PaneInfo } from "~/contexts/Sidebar/Sidebar";
|
|
import a11yStyles from "~/styles/accessibility.module.css";
|
|
import { EquipmentOverview } from "~/subApps/sheet/components/Equipment/EquipmentOverview";
|
|
import { MagicItemPlans } from "~/subApps/sheet/components/MagicItemPlans";
|
|
import { PaneComponentEnum } from "~/subApps/sheet/components/Sidebar/types";
|
|
|
|
import CurrencyButton from "../../../CharacterSheet/components/CurrencyButton";
|
|
import { ItemSlotManager } from "../../../Shared/components/ItemSlotManager";
|
|
import { ThemeButton } from "../../../Shared/components/common/Button";
|
|
import { CoinManagerContext } from "../../../Shared/managers/CoinManagerContext";
|
|
import { InventoryManagerContext } from "../../../Shared/managers/InventoryManagerContext";
|
|
import { appEnvSelectors } from "../../../Shared/selectors";
|
|
import { PaneIdentifierUtils } from "../../../Shared/utils";
|
|
import ContentGroup from "../../components/ContentGroup";
|
|
import Infusions from "../../components/Infusions";
|
|
import InventoryFilter from "../../components/InventoryFilter";
|
|
import InventoryTableHeader from "../../components/InventoryTableHeader";
|
|
import OtherPossessions from "../../components/OtherPossessions";
|
|
import { sheetAppSelectors } from "../../selectors";
|
|
import { SheetAppState } from "../../typings";
|
|
import Attunement from "../Attunement";
|
|
import Inventory from "../Inventory";
|
|
import styles from "./styles.module.css";
|
|
|
|
interface Props {
|
|
showNotes: boolean;
|
|
|
|
builderUrl: string;
|
|
inventory: Array<Item>;
|
|
partyInventory: Array<Item>;
|
|
creatures: Array<Creature>;
|
|
notes: CharacterNotes;
|
|
infusionChoices: Array<InfusionChoice>;
|
|
ruleData: RuleData;
|
|
snippetData: SnippetData;
|
|
isReadonly: boolean;
|
|
isMobile: boolean;
|
|
theme: CharacterTheme;
|
|
proficiencyBonus: number;
|
|
classes: Array<CharClass>;
|
|
inventoryManager: InventoryManager;
|
|
campaignInfo: PartyInfo | null;
|
|
coinManager: CoinManager;
|
|
paneHistoryStart: PaneInfo["paneHistoryStart"];
|
|
itemPlans: Array<ItemPlan>;
|
|
maxReplicatedItemsCount: number | null;
|
|
}
|
|
interface StateFilterData {
|
|
filteredInventory: Array<Item>;
|
|
filteredPartyInventory: Array<Item>;
|
|
showAdvancedFilters: boolean;
|
|
isFiltering: boolean;
|
|
}
|
|
interface State {
|
|
infusableChoices: Array<InfusionChoice>;
|
|
filterData: StateFilterData;
|
|
filteredEquippedInventory: Array<Item>;
|
|
shouldShowPartyInventory: number;
|
|
}
|
|
|
|
class Equipment extends PureComponent<Props, State> {
|
|
static defaultProps = {
|
|
showNotes: true,
|
|
};
|
|
|
|
constructor(props: Props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
infusableChoices: props.infusionChoices.filter((infusionChoice) =>
|
|
InfusionChoiceUtils.canInfuse(infusionChoice)
|
|
),
|
|
filterData: {
|
|
filteredInventory: [],
|
|
filteredPartyInventory: [],
|
|
showAdvancedFilters: false,
|
|
isFiltering: false,
|
|
},
|
|
filteredEquippedInventory: [],
|
|
shouldShowPartyInventory: 0, // mui used 0,1,2 for tabs so 0 is off 1 is on...
|
|
};
|
|
}
|
|
|
|
componentDidUpdate(prevProps: Props, prevState: State) {
|
|
if (prevProps.infusionChoices !== this.props.infusionChoices) {
|
|
this.setState({
|
|
infusableChoices: this.props.infusionChoices.filter((infusionChoice) =>
|
|
InfusionChoiceUtils.canInfuse(infusionChoice)
|
|
),
|
|
});
|
|
}
|
|
}
|
|
|
|
handleCurrencyClick = (
|
|
evt: React.MouseEvent | React.KeyboardEvent,
|
|
containerDefinitionKey: string
|
|
): void => {
|
|
const { paneHistoryStart } = this.props;
|
|
|
|
evt.stopPropagation();
|
|
evt.nativeEvent.stopImmediatePropagation();
|
|
|
|
paneHistoryStart(
|
|
PaneComponentEnum.CURRENCY,
|
|
PaneIdentifierUtils.generateCurrencyContext(containerDefinitionKey)
|
|
);
|
|
};
|
|
|
|
handlePossessionsManage = (): void => {
|
|
const { paneHistoryStart, isReadonly } = this.props;
|
|
|
|
if (!isReadonly) {
|
|
paneHistoryStart(
|
|
PaneComponentEnum.NOTE_MANAGE,
|
|
PaneIdentifierUtils.generateNote(
|
|
Constants.NoteKeyEnum.PERSONAL_POSSESSIONS
|
|
)
|
|
);
|
|
}
|
|
};
|
|
|
|
handleManageClick = (): void => {
|
|
const { paneHistoryStart } = this.props;
|
|
|
|
paneHistoryStart(PaneComponentEnum.EQUIPMENT_MANAGE);
|
|
};
|
|
|
|
handleFilterUpdate = (filterData: StateFilterData): void => {
|
|
this.setState({
|
|
filterData,
|
|
filteredEquippedInventory: [
|
|
...filterData.filteredInventory.filter(ItemUtils.isEquipped),
|
|
...filterData.filteredPartyInventory.filter(ItemUtils.isEquipped),
|
|
],
|
|
});
|
|
};
|
|
|
|
handleInfusionChoiceShow = (infusionChoice: InfusionChoice): void => {
|
|
const { paneHistoryStart } = this.props;
|
|
|
|
const choiceKey = InfusionChoiceUtils.getKey(infusionChoice);
|
|
if (choiceKey !== null) {
|
|
paneHistoryStart(
|
|
PaneComponentEnum.INFUSION_CHOICE,
|
|
PaneIdentifierUtils.generateInfusionChoice(choiceKey)
|
|
);
|
|
}
|
|
};
|
|
|
|
handleItemShow = (item: Item): void => {
|
|
const { paneHistoryStart } = this.props;
|
|
paneHistoryStart(
|
|
PaneComponentEnum.ITEM_DETAIL,
|
|
PaneIdentifierUtils.generateItem(ItemUtils.getMappingId(item))
|
|
);
|
|
};
|
|
|
|
handleContainerShow = (container: ContainerManager): void => {
|
|
const { paneHistoryStart } = this.props;
|
|
|
|
paneHistoryStart(
|
|
PaneComponentEnum.CONTAINER,
|
|
PaneIdentifierUtils.generateContainer(container.getDefinitionKey())
|
|
);
|
|
};
|
|
|
|
handleCreatureShow = (creature: Creature): void => {
|
|
const { paneHistoryStart } = this.props;
|
|
paneHistoryStart(
|
|
PaneComponentEnum.CREATURE,
|
|
PaneIdentifierUtils.generateCreature(CreatureUtils.getMappingId(creature))
|
|
);
|
|
};
|
|
|
|
handleItemPlansManagePaneShow = (): void => {
|
|
const { paneHistoryStart } = this.props;
|
|
|
|
paneHistoryStart(PaneComponentEnum.ITEM_PLANS_MANAGE);
|
|
};
|
|
|
|
shouldRenderInfusions = (): boolean => {
|
|
const { infusionChoices } = this.props;
|
|
|
|
return infusionChoices.length > 0;
|
|
};
|
|
|
|
shouldRenderMagicItemPlans = (): boolean => {
|
|
const { classes } = this.props;
|
|
|
|
return classes.some((charClass) =>
|
|
ClassUtils.getEnablesReplicateMagicItem(charClass)
|
|
);
|
|
};
|
|
|
|
renderContainer = (
|
|
container: ContainerManager,
|
|
inventory: Array<Item>
|
|
): ReactNode => {
|
|
const { shouldShowPartyInventory } = this.state;
|
|
const { isReadonly, showNotes, theme, coinManager } = this.props;
|
|
const containerMappingId = container.getMappingId();
|
|
|
|
const containerName = container.getName();
|
|
const weightInfo = container.getWeightInfo();
|
|
|
|
const containerInventory = container.getInventoryItems({
|
|
filteredInventory: inventory,
|
|
}).items;
|
|
const containerItem = container.getContainerItem();
|
|
|
|
const quantityNode = (
|
|
<span className="ct-equipment__container-quantity">{`(${containerInventory.length})`}</span>
|
|
);
|
|
const nameNode: ReactNode = containerItem ? (
|
|
<>
|
|
<ItemName item={containerItem.item} /> {quantityNode}
|
|
</>
|
|
) : (
|
|
<>
|
|
{containerName} {quantityNode}
|
|
</>
|
|
);
|
|
|
|
const headerNode: ReactNode = (
|
|
<div
|
|
className="ct-equipment__container ct-inventory-item"
|
|
onClick={(evt) => {
|
|
evt.nativeEvent.stopImmediatePropagation();
|
|
this.handleContainerShow(container);
|
|
}}
|
|
role="button"
|
|
data-testid={`${containerItem?.getName() || containerName}-section`
|
|
.toLowerCase()
|
|
.replace(/\s/g, "-")}
|
|
>
|
|
<div className="ct-equipment__container-action ct-inventory-item__action">
|
|
{containerItem && (
|
|
<ItemSlotManager
|
|
isUsed={!!containerItem.isEquipped()}
|
|
isReadonly={isReadonly}
|
|
canUse={
|
|
!(
|
|
containerItem.isEquipped() &&
|
|
!containerItem.isEquippedToCurrentCharacter()
|
|
)
|
|
}
|
|
onSet={(uses) => {
|
|
if (uses === 0) {
|
|
containerItem.handleUnequip();
|
|
}
|
|
|
|
if (uses === 1) {
|
|
containerItem.handleEquip();
|
|
}
|
|
}}
|
|
theme={theme}
|
|
useTooltip={false}
|
|
/>
|
|
)}
|
|
</div>
|
|
<div className="ct-equipment__container-name ct-inventory-item__name">
|
|
{nameNode}
|
|
</div>
|
|
<div className="ct-equipment__container-weight ct-inventory-item__weight">
|
|
<NumberDisplay type="weightInLb" number={weightInfo.applied} />
|
|
{weightInfo.capacity > 0 && (
|
|
<span className="ct-equipment__container-weight-capacity">
|
|
({Math.ceil(weightInfo.total)}/
|
|
{FormatUtils.renderWeight(weightInfo.capacity)})
|
|
</span>
|
|
)}
|
|
</div>
|
|
{coinManager.canUseCointainers() ? (
|
|
<div className="ct-equipment__container-coin">
|
|
<CurrencyButton
|
|
coin={ContainerUtils.getCoin(container.container)}
|
|
isDarkMode={theme.isDarkMode}
|
|
shouldShowPartyInventory={shouldShowPartyInventory === 1}
|
|
handleCurrencyClick={(evt) =>
|
|
this.handleCurrencyClick(
|
|
evt,
|
|
ContainerUtils.getDefinitionKey(container.container)
|
|
)
|
|
}
|
|
/>
|
|
</div>
|
|
) : (
|
|
containerItem &&
|
|
container.isEquipped() &&
|
|
!containerItem.isEquippedToCurrentCharacter() && (
|
|
<div className="ct-equipment__container-equipped">
|
|
Equipped by {containerItem.getEquippedCharacterName()}
|
|
</div>
|
|
)
|
|
)}
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<ContentGroup key={containerMappingId} header={headerNode}>
|
|
<Inventory
|
|
container={container}
|
|
inventory={containerInventory}
|
|
showNotes={showNotes}
|
|
showTableHeader={false}
|
|
isReadonly={isReadonly}
|
|
theme={theme}
|
|
/>
|
|
</ContentGroup>
|
|
);
|
|
};
|
|
|
|
renderCharacterContainers = (): ReactNode => {
|
|
const { filterData } = this.state;
|
|
const { inventoryManager } = this.props;
|
|
|
|
return inventoryManager
|
|
.getCharacterContainers()
|
|
.map((container) =>
|
|
this.renderContainer(container, filterData.filteredInventory)
|
|
);
|
|
};
|
|
|
|
renderPartyContainers = (): ReactNode => {
|
|
const { filterData } = this.state;
|
|
const { inventoryManager } = this.props;
|
|
|
|
return inventoryManager
|
|
.getPartyContainers()
|
|
.map((container) =>
|
|
this.renderContainer(container, filterData.filteredPartyInventory)
|
|
);
|
|
};
|
|
|
|
renderAttunement = (): ReactNode => {
|
|
const { isMobile } = this.props;
|
|
|
|
return (
|
|
<ContentGroup header="Attunement">
|
|
<Attunement isMobile={isMobile} />
|
|
</ContentGroup>
|
|
);
|
|
};
|
|
|
|
renderMagicItemPlans = (): ReactNode => {
|
|
const { isReadonly, itemPlans, maxReplicatedItemsCount } = this.props;
|
|
|
|
if (!this.shouldRenderMagicItemPlans()) {
|
|
return null;
|
|
}
|
|
|
|
const itemPlanWithMappingsCount = itemPlans.filter((plan) =>
|
|
ItemPlanUtils.getItem(plan)
|
|
).length;
|
|
|
|
const headerNode: ReactNode = (
|
|
<>
|
|
Magic Item Plans{" "}
|
|
{maxReplicatedItemsCount !== null && (
|
|
<span className={styles.replicatedCount}>
|
|
({itemPlanWithMappingsCount}/{maxReplicatedItemsCount} Replicated)
|
|
</span>
|
|
)}
|
|
</>
|
|
);
|
|
|
|
let extraNode: ReactNode = null;
|
|
if (!isReadonly) {
|
|
extraNode = (
|
|
<Button
|
|
variant="text"
|
|
themed
|
|
size="x-small"
|
|
className="ct-equipment__builder-link-text"
|
|
onClick={(e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.handleItemPlansManagePaneShow();
|
|
}}
|
|
>
|
|
Manage Plans
|
|
</Button>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ContentGroup header={headerNode} extra={extraNode}>
|
|
<MagicItemPlans />
|
|
</ContentGroup>
|
|
);
|
|
};
|
|
|
|
renderInfusions = (): ReactNode => {
|
|
const { infusableChoices } = this.state;
|
|
const {
|
|
builderUrl,
|
|
infusionChoices,
|
|
isReadonly,
|
|
snippetData,
|
|
inventory,
|
|
partyInventory,
|
|
creatures,
|
|
ruleData,
|
|
proficiencyBonus,
|
|
theme,
|
|
} = this.props;
|
|
|
|
const headerNode: ReactNode = (
|
|
<>
|
|
Infusions{" "}
|
|
{infusableChoices.length !== infusionChoices.length && (
|
|
<>
|
|
({infusableChoices.length}/{infusionChoices.length} Known Infusions)
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
|
|
let extraNode: ReactNode = null;
|
|
if (!isReadonly) {
|
|
extraNode = (
|
|
<Link
|
|
href={builderUrl}
|
|
className="ct-equipment__builder-link"
|
|
userouter
|
|
>
|
|
<DarkBuilderSvg />
|
|
<span className="ct-equipment__builder-link-text">
|
|
Manage Infusions
|
|
</span>
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ContentGroup header={headerNode} extra={extraNode}>
|
|
<Infusions
|
|
theme={theme}
|
|
infusionChoices={infusableChoices}
|
|
inventory={[...inventory, ...partyInventory]}
|
|
creatures={creatures}
|
|
snippetData={snippetData}
|
|
onCreatureShow={this.handleCreatureShow}
|
|
onItemShow={this.handleItemShow}
|
|
onInfusionChoiceShow={this.handleInfusionChoiceShow}
|
|
ruleData={ruleData}
|
|
isReadonly={isReadonly}
|
|
proficiencyBonus={proficiencyBonus}
|
|
/>
|
|
</ContentGroup>
|
|
);
|
|
};
|
|
|
|
renderOtherPossessions = (): ReactNode => {
|
|
const { notes, isReadonly } = this.props;
|
|
|
|
if (isReadonly) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<ContentGroup header="Other Possessions">
|
|
<OtherPossessions
|
|
notes={notes}
|
|
onClick={this.handlePossessionsManage}
|
|
/>
|
|
</ContentGroup>
|
|
);
|
|
};
|
|
|
|
renderContent = (): ReactNode => {
|
|
const {
|
|
isReadonly,
|
|
inventoryManager,
|
|
inventory,
|
|
partyInventory,
|
|
showNotes,
|
|
} = this.props;
|
|
const { shouldShowPartyInventory } = this.state;
|
|
|
|
const headerNode: ReactNode = (
|
|
<div
|
|
className="ct-equipment__container"
|
|
onClick={(evt) => {
|
|
evt.nativeEvent.stopImmediatePropagation();
|
|
this.handleManageClick();
|
|
}}
|
|
>
|
|
Party Inventory
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div className={styles.tabFilter}>
|
|
<TabFilter
|
|
sharedChildren={<InventoryTableHeader showNotes={showNotes} />}
|
|
filters={[
|
|
{
|
|
label: "All",
|
|
content: (
|
|
<>
|
|
{shouldShowPartyInventory === 0 ? (
|
|
<>
|
|
{this.renderCharacterContainers()}
|
|
{this.renderAttunement()}
|
|
{this.shouldRenderInfusions() && this.renderInfusions()}
|
|
{this.renderMagicItemPlans()}
|
|
{this.renderOtherPossessions()}
|
|
</>
|
|
) : null}
|
|
{shouldShowPartyInventory === 1 &&
|
|
inventoryManager.getPartyContainers().length > 0 ? (
|
|
<ContentGroup header={headerNode}>
|
|
{this.renderPartyContainers()}
|
|
</ContentGroup>
|
|
) : null}
|
|
</>
|
|
),
|
|
},
|
|
...(shouldShowPartyInventory === 0
|
|
? inventoryManager
|
|
.getCharacterContainers()
|
|
.map((characterContainer) => ({
|
|
label: characterContainer.getName(),
|
|
content: this.renderContainer(
|
|
characterContainer,
|
|
inventory
|
|
),
|
|
}))
|
|
: []),
|
|
...(shouldShowPartyInventory === 1
|
|
? inventoryManager.getPartyContainers().map((partyContainer) => ({
|
|
label: partyContainer.getName(),
|
|
content: this.renderContainer(partyContainer, partyInventory),
|
|
}))
|
|
: []),
|
|
...(shouldShowPartyInventory === 0
|
|
? [
|
|
{
|
|
label: "Attunement",
|
|
content: this.renderAttunement(),
|
|
},
|
|
...(this.shouldRenderInfusions()
|
|
? [
|
|
{
|
|
label: "Infusions",
|
|
content: this.renderInfusions(),
|
|
},
|
|
]
|
|
: []),
|
|
...(this.shouldRenderMagicItemPlans()
|
|
? [
|
|
{
|
|
label: "Magic Item Plans",
|
|
content: this.renderMagicItemPlans(),
|
|
},
|
|
]
|
|
: []),
|
|
...(!isReadonly
|
|
? [
|
|
{
|
|
label: "Other Possessions",
|
|
content: this.renderOtherPossessions(),
|
|
},
|
|
]
|
|
: []),
|
|
]
|
|
: []),
|
|
]}
|
|
showAllTab={false}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
renderManageButton = (): ReactNode => {
|
|
const { isReadonly } = this.props;
|
|
|
|
if (isReadonly) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<ThemeButton
|
|
style="outline"
|
|
size="medium"
|
|
onClick={this.handleManageClick}
|
|
data-testid="open-manage-equipment"
|
|
>
|
|
Manage Inventory
|
|
</ThemeButton>
|
|
);
|
|
};
|
|
|
|
toggleShouldShowPartyInventory = (event, newValue): void => {
|
|
this.setState({
|
|
shouldShowPartyInventory: newValue,
|
|
});
|
|
};
|
|
|
|
renderOverviewFiltersAndContent = (): ReactNode => {
|
|
const { filterData, shouldShowPartyInventory } = this.state;
|
|
const { inventory, ruleData, theme, partyInventory } = this.props;
|
|
|
|
return (
|
|
<>
|
|
<div className="ct-equipment__overview">
|
|
<EquipmentOverview
|
|
activeEquipmentTab={
|
|
shouldShowPartyInventory === 1 ? "party" : "character"
|
|
}
|
|
/>
|
|
</div>
|
|
<div className="ct-equipment__filter">
|
|
<InventoryFilter
|
|
partyInventory={partyInventory}
|
|
inventory={inventory}
|
|
ruleData={ruleData}
|
|
onDataUpdate={this.handleFilterUpdate}
|
|
callout={this.renderManageButton()}
|
|
theme={theme}
|
|
/>
|
|
</div>
|
|
{!filterData.showAdvancedFilters && this.renderContent()}
|
|
</>
|
|
);
|
|
};
|
|
|
|
handleChangeTab = (id) =>
|
|
this.setState({
|
|
shouldShowPartyInventory: id === "party-inventory" ? 1 : 0,
|
|
});
|
|
|
|
render() {
|
|
const { campaignInfo } = this.props;
|
|
|
|
return (
|
|
<section className={clsx(["ct-equipment", styles.section])}>
|
|
<h2 className={a11yStyles.screenreaderOnly}>Inventory</h2>
|
|
{campaignInfo !== null ? (
|
|
<TabList
|
|
className={styles.tabList}
|
|
variant="fullwidth"
|
|
themed={true}
|
|
tabs={[
|
|
{
|
|
label: "My Inventory",
|
|
content: this.renderOverviewFiltersAndContent(),
|
|
id: "my-inventory",
|
|
},
|
|
{
|
|
label: "Party Inventory",
|
|
content: this.renderOverviewFiltersAndContent(),
|
|
id: "party-inventory",
|
|
},
|
|
]}
|
|
onChangeTab={this.handleChangeTab}
|
|
/>
|
|
) : (
|
|
this.renderOverviewFiltersAndContent()
|
|
)}
|
|
</section>
|
|
);
|
|
}
|
|
}
|
|
|
|
function mapStateToProps(state: SheetAppState) {
|
|
return {
|
|
builderUrl: sheetAppSelectors.getBuilderUrl(state),
|
|
inventory: rulesEngineSelectors.getInventory(state),
|
|
partyInventory: rulesEngineSelectors.getPartyInventory(state),
|
|
creatures: rulesEngineSelectors.getCreatures(state),
|
|
notes: rulesEngineSelectors.getCharacterNotes(state),
|
|
infusionChoices: rulesEngineSelectors.getAvailableInfusionChoices(state),
|
|
ruleData: rulesEngineSelectors.getRuleData(state),
|
|
snippetData: rulesEngineSelectors.getSnippetData(state),
|
|
theme: rulesEngineSelectors.getCharacterTheme(state),
|
|
isReadonly: appEnvSelectors.getIsReadonly(state),
|
|
isMobile: appEnvSelectors.getIsMobile(state),
|
|
proficiencyBonus: rulesEngineSelectors.getProficiencyBonus(state),
|
|
classes: rulesEngineSelectors.getClasses(state),
|
|
campaignInfo: serviceDataSelectors.getPartyInfo(state),
|
|
itemPlans: rulesEngineSelectors.getAvailableItemPlans(state),
|
|
maxReplicatedItemsCount:
|
|
rulesEngineSelectors.getMaxReplicatedItemsCount(state),
|
|
};
|
|
}
|
|
|
|
const EquipmentContainer = (props) => {
|
|
const { inventoryManager } = useContext(InventoryManagerContext);
|
|
const { coinManager } = useContext(CoinManagerContext);
|
|
const {
|
|
pane: { paneHistoryStart },
|
|
} = useSidebar();
|
|
return (
|
|
<Equipment
|
|
inventoryManager={inventoryManager}
|
|
coinManager={coinManager}
|
|
paneHistoryStart={paneHistoryStart}
|
|
{...props}
|
|
/>
|
|
);
|
|
};
|
|
|
|
export default connect(mapStateToProps)(EquipmentContainer);
|