New source found from dndbeyond.com

This commit is contained in:
2026-07-08 01:00:09 -07:00
parent 9a983a6d7b
commit dcefa0d2f2
2865 changed files with 222467 additions and 49053 deletions
@@ -1,17 +1,21 @@
import React from "react";
import clsx from "clsx";
import React, { FC } from "react";
import { connect } from "react-redux";
import SwipeableViews from "react-swipeable-views";
import { FeatureFlagContext } from "@dndbeyond/character-components/es";
import {
characterSelectors,
CharacterStatusSlug,
featureFlagInfoSelectors,
CharacterTheme,
FormatUtils,
rulesEngineSelectors,
} from "@dndbeyond/character-rules-engine/es";
import CharacterSheetSvg from "@dndbeyond/fontawesome-cache/svgs/light/address-card.svg";
import { GameLogNotificationWrapper } from "@dndbeyond/game-log-components";
import { Link } from "~/components/Link";
import { PremadeCharacterEditStatus } from "~/components/PremadeCharacterEditStatus";
import { Tooltip } from "~/components/Tooltip";
import { appEnvSelectors } from "../../../Shared/selectors";
import { MobileMessengerUtils } from "../../../Shared/utils";
@@ -19,6 +23,7 @@ import { navigationConfig } from "../../config";
import { builderEnvSelectors, builderSelectors } from "../../selectors";
import { BuilderAppState } from "../../typings";
import HelpTextManager from "../HelpTextManager";
import styles from "./styles.module.css";
interface Props {
characterId: number | null;
@@ -31,114 +36,118 @@ interface Props {
isMobile: boolean;
characterStatus: CharacterStatusSlug | null;
isReadonly: boolean;
theme: CharacterTheme;
}
class NavigationSections extends React.PureComponent<Props> {
handleSheetShowClick = () => {
const { characterId } = this.props;
const NavigationSections: FC<Props> = ({
characterId,
isCharacterSheetReady,
characterSheetUrl,
theme,
sections,
firstAvailableSectionRoutes,
activeSectionIdx,
builderMethod,
isMobile,
characterStatus,
isReadonly,
}) => {
const handleSheetShowClick = () => {
if (characterId !== null) {
MobileMessengerUtils.sendMessage(
MobileMessengerUtils.createShowCharacterSheetMessage(characterId)
);
}
};
renderNonMobileUi = (): React.ReactNode => {
const {
characterId,
sections,
firstAvailableSectionRoutes,
isCharacterSheetReady,
characterSheetUrl,
} = this.props;
let classNames: Array<string> = ["builder-sections-sheet"];
if (isCharacterSheetReady) {
classNames.push("builder-sections-sheet-ready");
} else {
classNames.push("builder-sections-sheet-disabled");
}
const renderCharacterSheetLink = () => {
//trading out a Router Link for a div if not ready for accessibility
const Component = isCharacterSheetReady ? Link : "div";
const CharacterSheetLink = () => (
<>
<Component
className={clsx([
styles.characterSheetIcon,
!isCharacterSheetReady && styles.disabled,
])}
href={isCharacterSheetReady ? characterSheetUrl : undefined}
onClick={handleSheetShowClick}
userouter={isCharacterSheetReady ? true : undefined}
data-tooltip-place="bottom"
data-tooltip-id={"character-sheet-tooltip"}
data-tooltip-content={
isCharacterSheetReady
? "Go to Character Sheet"
: "Character is incomplete"
}
>
<CharacterSheetSvg
className={clsx([
styles.characterSheetIcon,
!isCharacterSheetReady && styles.disabled,
])}
/>
</Component>
<Tooltip id="character-sheet-tooltip" />
</>
);
return (
<div className="builder-sections builder-sections-large">
<HelpTextManager />
<div className={classNames.join(" ")}>
{isCharacterSheetReady ? (
<Link
className="builder-sections-sheet-icon"
href={characterSheetUrl}
onClick={this.handleSheetShowClick}
useRouter
/>
) : (
<div className="builder-sections-sheet-icon" />
)}
</div>
<div className="builder-sections-large-routes">
{sections.map((section) => {
const firstRoute = firstAvailableSectionRoutes[section.key];
let clsNames: Array<string> = [
"builder-sections-link",
`builder-sections-${FormatUtils.slugify(section.key)}`,
];
if (section.active) {
clsNames.push("builder-sections-link-active");
}
return (
<div className="builder-sections-view" key={section.key}>
<Link
href={firstRoute.path.replace(":characterId", characterId)}
className={clsNames.join(" ")}
useRouter
>
<FeatureFlagContext.Consumer>
{(featureFlags) => section.getLabel(featureFlags)}
</FeatureFlagContext.Consumer>
</Link>
</div>
);
})}
</div>
<div
className={clsx([
"builder-sections-sheet",
!isCharacterSheetReady && "builder-sections-sheet-disabled",
])}
>
<GameLogNotificationWrapper
themeColor={theme.themeColor}
gameLogIsOpen={false}
notificationOnClick={() => {}}
isCharacterBuilder
>
<CharacterSheetLink />
</GameLogNotificationWrapper>
</div>
);
};
renderMobileUi = (): React.ReactNode => {
const {
characterId,
sections,
firstAvailableSectionRoutes,
activeSectionIdx,
isCharacterSheetReady,
characterSheetUrl,
} = this.props;
const renderNonMobileUi = () => (
<div className="builder-sections builder-sections-large">
<HelpTextManager />
{renderCharacterSheetLink()}
<div className="builder-sections-large-routes">
{sections.map((section) => {
const firstRoute = firstAvailableSectionRoutes[section.key];
let clsNames: Array<string> = [
"builder-sections-link",
`builder-sections-${FormatUtils.slugify(section.key)}`,
];
if (section.active) {
clsNames.push("builder-sections-link-active");
}
return (
<div className="builder-sections-view" key={section.key}>
<Link
href={firstRoute.path.replace(":characterId", characterId)}
className={clsNames.join(" ")}
userouter
>
{section.getLabel()}
</Link>
</div>
);
})}
</div>
</div>
);
const renderMobileUi = () => {
const scrollbarStyles: React.CSSProperties = {
padding: "0 37.5%",
padding: "0 35%",
};
let classNames = ["builder-sections-sheet"];
if (isCharacterSheetReady) {
classNames.push("builder-sections-sheet-ready");
} else {
classNames.push("builder-sections-sheet-disabled");
}
return (
<div className="builder-sections builder-sections-small">
<HelpTextManager />
<div className={classNames.join(" ")}>
{isCharacterSheetReady ? (
<Link
className="builder-sections-sheet-icon"
href={characterSheetUrl}
onClick={this.handleSheetShowClick}
useRouter
/>
) : (
<div className="builder-sections-sheet-icon" />
)}
</div>
{renderCharacterSheetLink()}
<SwipeableViews
index={activeSectionIdx}
style={scrollbarStyles}
@@ -159,11 +168,9 @@ class NavigationSections extends React.PureComponent<Props> {
<Link
href={firstRoute.path.replace(":characterId", characterId)}
className={clsNames.join(" ")}
useRouter
userouter
>
<FeatureFlagContext.Consumer>
{(featureFlags) => section.getLabel(featureFlags)}
</FeatureFlagContext.Consumer>
{section.getLabel()}
</Link>
</div>
);
@@ -173,41 +180,36 @@ class NavigationSections extends React.PureComponent<Props> {
);
};
render() {
const { builderMethod, isMobile, characterStatus, isReadonly } = this.props;
if (builderMethod === null) {
return null;
}
return isMobile ? (
<>
{this.renderMobileUi()}
<PremadeCharacterEditStatus
characterStatus={characterStatus}
isReadonly={isReadonly}
isBuilderView
/>
</>
) : (
<>
{this.renderNonMobileUi()}
<PremadeCharacterEditStatus
characterStatus={characterStatus}
isReadonly={isReadonly}
isBuilderView
/>
</>
);
if (builderMethod === null) {
return null;
}
}
return isMobile ? (
<>
{renderMobileUi()}
<PremadeCharacterEditStatus
characterStatus={characterStatus}
isReadonly={isReadonly}
isBuilderView
/>
</>
) : (
<>
{renderNonMobileUi()}
<PremadeCharacterEditStatus
characterStatus={characterStatus}
isReadonly={isReadonly}
isBuilderView
/>
</>
);
};
function mapStateToProps(state: BuilderAppState, ownProps) {
const currentPath = ownProps.pathname;
const sections = navigationConfig.getNavigationSections(
builderSelectors.getBuilderMethod(state),
navigationConfig.getCurrentRouteDef(currentPath),
featureFlagInfoSelectors.getFeatureFlagInfo(state)
navigationConfig.getCurrentRouteDef(currentPath)
);
let activeSectionIdx: number = 0;
@@ -228,6 +230,7 @@ function mapStateToProps(state: BuilderAppState, ownProps) {
isMobile: appEnvSelectors.getIsMobile(state),
characterStatus: characterSelectors.getStatusSlug(state),
isReadonly: appEnvSelectors.getIsReadonly(state),
theme: rulesEngineSelectors.getCharacterTheme(state),
};
}