Skip to content

Commit

Permalink
Add ExperiencePoints component
Browse files Browse the repository at this point in the history
  • Loading branch information
gvorbeck committed Jan 22, 2024
1 parent 13201d0 commit ce623bd
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 114 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import HelpTooltip from "@/components/HelpTooltip/HelpTooltip";
import ModalLevelUp from "@/components/ModalLevelUp/ModalLevelUp";
import { classes } from "@/data/classes";
import { CharData, ClassNames } from "@/data/definitions";
import { Button, Flex, Input, Space } from "antd";
import React from "react";

interface ExperiencePointsProps {
classArr: string[];
character: CharData;
setCharacter: (character: CharData) => void;
setModalIsOpen: (modalIsOpen: boolean) => void;
uid: string | undefined;
id: string | undefined;
setModalTitle: (modalTitle: string) => void;
setModalContent: (modalContent: React.ReactNode) => void;
userIsOwner: boolean;
}

const ExperiencePoints: React.FC<
ExperiencePointsProps & React.ComponentPropsWithRef<"div">
> = ({
className,
classArr,
setModalIsOpen,
character,
setCharacter,
setModalTitle,
uid,
id,
setModalContent,
userIsOwner,
}) => {
const [inputValue, setInputValue] = React.useState<string>(`${character.xp}`);
const showLevelUpModal = () => {
setModalIsOpen(true);
setModalTitle("Level Up");
setModalContent(
<ModalLevelUp
character={character}
setCharacter={setCharacter}
setModalIsOpen={setModalIsOpen}
/>,
);
};
const handleInputBlur = () => {
if (!uid || !id) {
return;
}

// Check if inputValue matches the expected format (optional '-' or '+', followed by numeric characters)
if (!/^[+-]?\d+$/.test(inputValue)) {
console.error("Invalid input");
return;
}

// Determine the XP change
let xpChange = 0;
if (inputValue.startsWith("+")) {
xpChange = parseInt(inputValue.slice(1));
} else if (inputValue.startsWith("-")) {
xpChange = -parseInt(inputValue.slice(1));
} else {
xpChange = parseInt(inputValue) - character.xp; // Difference between new and old XP
}

// Apply the XP change
if (!isNaN(xpChange)) {
const updatedXP = character.xp + xpChange;
setCharacter({
...character,
xp: updatedXP,
});
setInputValue(updatedXP.toString());
}
};

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(event.target.value);
};
const totalLevelRequirement = classArr
.map((className) => {
const classRequirements =
classes[className as ClassNames]?.experiencePoints;
return classRequirements ? classRequirements[character.level] : 0; // value if using a custom class
})
.reduce((a, b) => a + b, 0);
return (
<Flex gap={8} className={className}>
<Space.Compact size="middle">
<Input
value={inputValue}
onFocus={(e) => {
setTimeout(() => {
e.target.select();
}, 50);
}}
onChange={handleInputChange}
onBlur={handleInputBlur}
onKeyDown={(event) => {
if (event.key === "Enter") {
handleInputBlur();
}
}}
name="Experience Points"
id="experience-points"
suffix={character.level < 20 && `/ ${totalLevelRequirement} XP`}
disabled={!userIsOwner}
/>
<label htmlFor="experience-points" className="hidden">
Experience Points
</label>
{character.level < 20 && (
<Button
disabled={character.xp < totalLevelRequirement || !userIsOwner}
type="primary"
onClick={showLevelUpModal}
className="print:hidden shadow-none"
>{`Level Up`}</Button>
)}
</Space.Compact>
<HelpTooltip text="You can add to your XP total by highlighting the current value and typing a number starting with + or - (ex: +250) and hitting Enter" />
</Flex>
);
};

export default ExperiencePoints;
138 changes: 24 additions & 114 deletions src/components/PageCharacterSheet/Hero/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,23 @@ import {
} from "@ant-design/icons";
import { avatarClassNames } from "@/support/cssSupport";
import {
Avatar,
Flex,
Badge,
Avatar,
Divider,
Breadcrumb,
BreadcrumbProps,
Button,
Typography,
Descriptions,
BreadcrumbProps,
DescriptionsProps,
Divider,
Flex,
Input,
Space,
Typography,
} from "antd";
import { getAvatar } from "@/support/characterSupport";
import { ClassNames } from "@/data/definitions";
import { classes } from "@/data/classes";
import HelpTooltip from "@/components/HelpTooltip/HelpTooltip";
import BreadcrumbHomeLink from "@/components/BreadcrumbHomeLink/BreadcrumbHomeLink";
import classNames from "classnames";
import AvatarPicker from "@/components/AvatarPicker/AvatarPicker";
import { CharacterDataContext } from "@/contexts/CharacterContext";
import ModalLevelUp from "@/components/ModalLevelUp/ModalLevelUp";
import { classSplit } from "@/support/classSupport";
import ExperiencePoints from "./ExperiencePoints/ExperiencePoints";

interface HeroProps {
setModalIsOpen: (modalIsOpen: boolean) => void;
Expand All @@ -46,23 +40,10 @@ const Hero: React.FC<HeroProps & React.ComponentPropsWithRef<"div">> = ({
}) => {
const { character, setCharacter, userIsOwner, uid, id } =
React.useContext(CharacterDataContext);
const [inputValue, setInputValue] = React.useState<string>(`${character.xp}`);
const heroClassNames = classNames("w-full", className);

const classArr = classSplit(character.class);

const showLevelUpModal = () => {
setModalIsOpen(true);
setModalTitle("Level Up");
setModalContent(
<ModalLevelUp
character={character}
setCharacter={setCharacter}
setModalIsOpen={setModalIsOpen}
/>,
);
};

const showAvatarModal = () => {
setModalIsOpen(true);
setModalTitle("Change Avatar");
Expand All @@ -71,50 +52,6 @@ const Hero: React.FC<HeroProps & React.ComponentPropsWithRef<"div">> = ({
);
};

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(event.target.value);
};

const handleInputBlur = () => {
if (!uid || !id) {
return;
}

// Check if inputValue matches the expected format (optional '-' or '+', followed by numeric characters)
if (!/^[+-]?\d+$/.test(inputValue)) {
console.error("Invalid input");
return;
}

// Determine the XP change
let xpChange = 0;
if (inputValue.startsWith("+")) {
xpChange = parseInt(inputValue.slice(1));
} else if (inputValue.startsWith("-")) {
xpChange = -parseInt(inputValue.slice(1));
} else {
xpChange = parseInt(inputValue) - character.xp; // Difference between new and old XP
}

// Apply the XP change
if (!isNaN(xpChange)) {
const updatedXP = character.xp + xpChange;
setCharacter({
...character,
xp: updatedXP,
});
setInputValue(updatedXP.toString());
}
};

const totalLevelRequirement = classArr
.map((className) => {
const classRequirements =
classes[className as ClassNames]?.experiencePoints;
return classRequirements ? classRequirements[character.level] : 0; // value if using a custom class
})
.reduce((a, b) => a + b, 0);

const breadcrumbItems: BreadcrumbProps["items"] = [
{
title: <BreadcrumbHomeLink />,
Expand Down Expand Up @@ -154,10 +91,10 @@ const Hero: React.FC<HeroProps & React.ComponentPropsWithRef<"div">> = ({
>
<Badge count={<EditOutlined className="opacity-25" />}>
<Avatar
src={character.avatar ? getAvatar(character.avatar) : undefined}
icon={!character.avatar ? <UserOutlined /> : undefined}
size={64}
className={avatarClassNames}
icon={!character.avatar ? <UserOutlined /> : undefined}
src={character.avatar ? getAvatar(character.avatar) : undefined}
/>
</Badge>
</div>
Expand All @@ -170,58 +107,31 @@ const Hero: React.FC<HeroProps & React.ComponentPropsWithRef<"div">> = ({
</Typography.Title>
<Divider />
<Flex
className="w-full sm:flex-row-reverse sm:mx-auto"
vertical={isMobile}
gap={16}
align="flex-start"
vertical={isMobile}
justify="space-between"
gap={16}
className="w-full sm:flex-row-reverse sm:mx-auto"
>
{/* Level/Race/Class */}
<Descriptions
items={descriptionsItems}
bordered
className="w-full sm:w-1/3"
column={2}
size="small"
items={descriptionsItems}
className="w-full sm:w-1/3"
/>
<ExperiencePoints
id={id}
uid={uid}
classArr={classArr}
character={character}
userIsOwner={userIsOwner}
setCharacter={setCharacter}
setModalTitle={setModalTitle}
setModalIsOpen={setModalIsOpen}
setModalContent={setModalContent}
/>
{/* Experience Points */}
<Flex gap={8}>
<Space.Compact size="middle">
<Input
value={inputValue}
onFocus={(e) => {
setTimeout(() => {
e.target.select();
}, 50);
}}
onChange={handleInputChange}
onBlur={handleInputBlur}
onKeyDown={(event) => {
if (event.key === "Enter") {
handleInputBlur();
}
}}
name="Experience Points"
id="experience-points"
suffix={character.level < 20 && `/ ${totalLevelRequirement} XP`}
disabled={!userIsOwner}
/>
<label htmlFor="experience-points" className="hidden">
Experience Points
</label>
{character.level < 20 && (
<Button
disabled={
character.xp < totalLevelRequirement || !userIsOwner
}
type="primary"
onClick={showLevelUpModal}
className="print:hidden shadow-none"
>{`Level Up`}</Button>
)}
</Space.Compact>
<HelpTooltip text="You can add to your XP total by highlighting the current value and typing a number starting with + or - (ex: +250) and hitting Enter" />
</Flex>
</Flex>
</Flex>
</>
Expand Down

0 comments on commit ce623bd

Please sign in to comment.