Files
dndbeyond_src/ddb_main/tools/js/CharacterBuilder/containers/NavigationSections/NavigationSections.tsx
T

238 lines
7.0 KiB
TypeScript

import clsx from "clsx";
import React, { FC } from "react";
import { connect } from "react-redux";
import SwipeableViews from "react-swipeable-views";
import {
characterSelectors,
CharacterStatusSlug,
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";
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;
builderMethod: string | null;
sections: Array<any>;
firstAvailableSectionRoutes: any;
isCharacterSheetReady: boolean;
characterSheetUrl: string;
activeSectionIdx: number;
isMobile: boolean;
characterStatus: CharacterStatusSlug | null;
isReadonly: boolean;
theme: CharacterTheme;
}
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)
);
}
};
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={clsx([
"builder-sections-sheet",
!isCharacterSheetReady && "builder-sections-sheet-disabled",
])}
>
<GameLogNotificationWrapper
themeColor={theme.themeColor}
gameLogIsOpen={false}
notificationOnClick={() => {}}
isCharacterBuilder
>
<CharacterSheetLink />
</GameLogNotificationWrapper>
</div>
);
};
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 35%",
};
return (
<div className="builder-sections builder-sections-small">
<HelpTextManager />
{renderCharacterSheetLink()}
<SwipeableViews
index={activeSectionIdx}
style={scrollbarStyles}
className="builder-sections-views"
ignoreNativeScroll={true}
>
{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>
);
})}
</SwipeableViews>
</div>
);
};
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)
);
let activeSectionIdx: number = 0;
sections.forEach((section, idx) => {
if (section.active) {
activeSectionIdx = idx;
}
});
return {
sections,
firstAvailableSectionRoutes:
builderSelectors.getFirstAvailableSectionRoutes(state),
isCharacterSheetReady: builderSelectors.checkIsCharacterSheetReady(state),
characterSheetUrl: builderEnvSelectors.getCharacterSheetUrl(state),
builderMethod: builderSelectors.getBuilderMethod(state),
activeSectionIdx,
isMobile: appEnvSelectors.getIsMobile(state),
characterStatus: characterSelectors.getStatusSlug(state),
isReadonly: appEnvSelectors.getIsReadonly(state),
theme: rulesEngineSelectors.getCharacterTheme(state),
};
}
export default connect(mapStateToProps)(NavigationSections);