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,181 @@
|
||||
import { groupBy, orderBy } from "lodash";
|
||||
import React, { useContext } from "react";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { AttunementSlot } from "@dndbeyond/character-components/es";
|
||||
import {
|
||||
rulesEngineSelectors,
|
||||
Item,
|
||||
ItemUtils,
|
||||
CampaignUtils,
|
||||
CharacterTheme,
|
||||
InventoryManager,
|
||||
serviceDataSelectors,
|
||||
PartyInfo,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
import { useSidebar } from "~/contexts/Sidebar";
|
||||
import { PaneInfo } from "~/contexts/Sidebar/Sidebar";
|
||||
import { PaneComponentEnum } from "~/subApps/sheet/components/Sidebar/types";
|
||||
|
||||
import { InventoryManagerContext } from "../../../Shared/managers/InventoryManagerContext";
|
||||
import { PaneIdentifierUtils } from "../../../Shared/utils";
|
||||
import { SheetAppState } from "../../typings";
|
||||
import AttunementItem from "./AttunementItem";
|
||||
|
||||
interface Props {
|
||||
items: Array<Item>;
|
||||
attunedSlots: Array<Item | null>;
|
||||
isFiltered: boolean;
|
||||
isMobile: boolean;
|
||||
theme: CharacterTheme;
|
||||
inventoryManager: InventoryManager;
|
||||
partyInfo: PartyInfo | null;
|
||||
paneHistoryStart: PaneInfo["paneHistoryStart"];
|
||||
}
|
||||
class Attunement extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
isFiltered: false,
|
||||
isMobile: false,
|
||||
};
|
||||
|
||||
handleItemShow = (item: Item): void => {
|
||||
const { paneHistoryStart } = this.props;
|
||||
|
||||
paneHistoryStart(
|
||||
PaneComponentEnum.ITEM_DETAIL,
|
||||
PaneIdentifierUtils.generateItem(ItemUtils.getMappingId(item))
|
||||
);
|
||||
};
|
||||
|
||||
renderItems = (): React.ReactNode => {
|
||||
const { items, theme, inventoryManager, partyInfo } = this.props;
|
||||
|
||||
const attunableItems = items.filter((item) => ItemUtils.canAttune(item));
|
||||
const sortedItems = groupBy(
|
||||
orderBy(
|
||||
attunableItems,
|
||||
[
|
||||
(item) => ItemUtils.getRarityLevel(item),
|
||||
(item) => ItemUtils.getName(item),
|
||||
(item) => ItemUtils.getMappingId(item),
|
||||
],
|
||||
["desc", "asc"]
|
||||
),
|
||||
(item) => {
|
||||
if (inventoryManager.isShared(item)) {
|
||||
return "party";
|
||||
} else {
|
||||
return "character";
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="ct-attunement__group ct-attunement__group--items">
|
||||
<div className="ct-attunement__group-header">
|
||||
Items Requiring Attunement
|
||||
</div>
|
||||
<div className="ct-attunement__group-items">
|
||||
{sortedItems.character?.length > 0 ? (
|
||||
sortedItems.character?.map((item) => (
|
||||
<AttunementItem
|
||||
key={ItemUtils.getUniqueKey(item)}
|
||||
item={item}
|
||||
onItemShow={this.handleItemShow}
|
||||
theme={theme}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="ct-attunement__group-empty">
|
||||
Items that you can attune to will display here as you make them
|
||||
active.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{partyInfo &&
|
||||
CampaignUtils.isSharingStateActive(
|
||||
CampaignUtils.getSharingState(partyInfo)
|
||||
) && (
|
||||
<>
|
||||
<div className="ct-attunement__group-header">
|
||||
Party Items Requiring Attunement
|
||||
</div>
|
||||
<div className="ct-attunement__group-items">
|
||||
{sortedItems.party?.length > 0 ? (
|
||||
sortedItems.party?.map((item) => (
|
||||
<AttunementItem
|
||||
key={ItemUtils.getUniqueKey(item)}
|
||||
item={item}
|
||||
onItemShow={this.handleItemShow}
|
||||
theme={theme}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="ct-attunement__group-empty">
|
||||
Party items that you can attune to will display here as you
|
||||
make them active.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
renderAttuned = (): React.ReactNode => {
|
||||
const { attunedSlots, isMobile, theme } = this.props;
|
||||
|
||||
return (
|
||||
<div className="ct-attunement__group ct-attunement__group--attuned">
|
||||
<div className="ct-attunement__group-header">Attuned Items</div>
|
||||
<div className="ct-attunement__group-items">
|
||||
{attunedSlots.map((slot, idx) => (
|
||||
<AttunementSlot
|
||||
theme={theme}
|
||||
key={`slot-${idx}-${slot ? ItemUtils.getUniqueKey(slot) : ""}`}
|
||||
slot={slot}
|
||||
onClick={this.handleItemShow}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="ct-attunement">
|
||||
{this.renderAttuned()}
|
||||
{this.renderItems()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state: SheetAppState) {
|
||||
return {
|
||||
attunedSlots: rulesEngineSelectors.getAttunedSlots(state),
|
||||
theme: rulesEngineSelectors.getCharacterTheme(state),
|
||||
items: rulesEngineSelectors.getEquippedItems(state),
|
||||
partyInfo: serviceDataSelectors.getPartyInfo(state),
|
||||
};
|
||||
}
|
||||
|
||||
const AttunementContainer = (props) => {
|
||||
const { inventoryManager } = useContext(InventoryManagerContext);
|
||||
const {
|
||||
pane: { paneHistoryStart },
|
||||
} = useSidebar();
|
||||
return (
|
||||
<Attunement
|
||||
inventoryManager={inventoryManager}
|
||||
paneHistoryStart={paneHistoryStart}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(AttunementContainer);
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
import React, { useContext } from "react";
|
||||
|
||||
import {
|
||||
CharacterTheme,
|
||||
InventoryManager,
|
||||
Item,
|
||||
ItemUtils,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
import { ItemName } from "~/components/ItemName";
|
||||
|
||||
import { ItemSlotManager } from "../../../../Shared/components/ItemSlotManager";
|
||||
import { InventoryManagerContext } from "../../../../Shared/managers/InventoryManagerContext";
|
||||
|
||||
interface Props {
|
||||
item: Item;
|
||||
onItemShow?: (item: Item) => void;
|
||||
className: string;
|
||||
theme: CharacterTheme;
|
||||
inventoryManager: InventoryManager;
|
||||
}
|
||||
export class AttunementItem extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
className: "",
|
||||
};
|
||||
|
||||
handleSetAttune = (uses: number): void => {
|
||||
const { inventoryManager, item } = this.props;
|
||||
|
||||
//TODO use different component than SlotManager for attune/unattune
|
||||
if (uses === 1) {
|
||||
inventoryManager.handleAttune({ item });
|
||||
}
|
||||
|
||||
if (uses === 0) {
|
||||
inventoryManager.handleUnattune({ item });
|
||||
}
|
||||
};
|
||||
|
||||
handleItemClick = (evt: React.MouseEvent): void => {
|
||||
const { onItemShow, item } = this.props;
|
||||
|
||||
evt.nativeEvent.stopImmediatePropagation();
|
||||
evt.stopPropagation();
|
||||
|
||||
if (onItemShow) {
|
||||
onItemShow(item);
|
||||
}
|
||||
};
|
||||
|
||||
renderMetaItems = (): React.ReactNode => {
|
||||
const { item, inventoryManager } = this.props;
|
||||
|
||||
let metaItems: Array<string> = [];
|
||||
|
||||
if (ItemUtils.isLegacy(item)) {
|
||||
metaItems.push("Legacy");
|
||||
}
|
||||
|
||||
let type = ItemUtils.getType(item);
|
||||
if (type !== null) {
|
||||
metaItems.push(type);
|
||||
}
|
||||
|
||||
if (ItemUtils.isWeaponContract(item)) {
|
||||
if (ItemUtils.isOffhand(item)) {
|
||||
metaItems.push("Dual Wield");
|
||||
}
|
||||
} else if (ItemUtils.isArmorContract(item)) {
|
||||
let baseArmorName = ItemUtils.getBaseArmorName(item);
|
||||
if (baseArmorName) {
|
||||
metaItems.push(baseArmorName);
|
||||
}
|
||||
} else if (ItemUtils.isGearContract(item)) {
|
||||
let subType = ItemUtils.getSubType(item);
|
||||
if (subType) {
|
||||
metaItems.push(subType);
|
||||
}
|
||||
if (ItemUtils.isOffhand(item)) {
|
||||
metaItems.push("Dual Wield");
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
ItemUtils.isEquipped(item) &&
|
||||
!inventoryManager.isEquippedToCurrentCharacter(item)
|
||||
) {
|
||||
metaItems.push(
|
||||
`${
|
||||
ItemUtils.isAttuned(item) ? "Attuned" : "Equipped"
|
||||
} by ${inventoryManager.getEquippedCharacterName(item)}`
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ct-attunement__meta">
|
||||
{metaItems.map((metaItem, idx) => (
|
||||
<span className="ct-attunement__meta-item" key={idx}>
|
||||
{metaItem}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
//`
|
||||
};
|
||||
|
||||
render() {
|
||||
const { item, className, theme, inventoryManager } = this.props;
|
||||
|
||||
const canAttune = inventoryManager.canAttuneUnattuneItem(item);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`ct-attunement__item ${className}`}
|
||||
onClick={this.handleItemClick}
|
||||
>
|
||||
<div className="ct-attunement__item-action">
|
||||
<ItemSlotManager
|
||||
isUsed={!!ItemUtils.isAttuned(item)}
|
||||
canUse={canAttune}
|
||||
theme={theme}
|
||||
onSet={this.handleSetAttune}
|
||||
useTooltip={
|
||||
!!ItemUtils.isEquipped(item) &&
|
||||
!inventoryManager.isEquippedToCurrentCharacter(item)
|
||||
}
|
||||
tooltipTitle={`${
|
||||
ItemUtils.isAttuned(item) ? "Attuned" : "Equipped"
|
||||
} by ${inventoryManager.getEquippedCharacterName(item)}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="ct-attunement__item-name">
|
||||
<div className="ct-attunement__item-heading">
|
||||
<ItemName item={item} />
|
||||
</div>
|
||||
{this.renderMetaItems()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
//`
|
||||
}
|
||||
}
|
||||
|
||||
const AttunementItemContainer = (props) => {
|
||||
const { inventoryManager } = useContext(InventoryManagerContext);
|
||||
return <AttunementItem inventoryManager={inventoryManager} {...props} />;
|
||||
};
|
||||
|
||||
export default AttunementItemContainer;
|
||||
@@ -0,0 +1,4 @@
|
||||
import AttunementItem from "./AttunementItem";
|
||||
|
||||
export default AttunementItem;
|
||||
export { AttunementItem };
|
||||
@@ -0,0 +1,5 @@
|
||||
import Attunement from "./Attunement";
|
||||
import AttunementItem from "./AttunementItem";
|
||||
|
||||
export default Attunement;
|
||||
export { Attunement, AttunementItem };
|
||||
Reference in New Issue
Block a user