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,9 +1,9 @@
import { orderBy } from "lodash";
import { FC, Fragment, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useSelector } from "react-redux";
import { v4 as uuidv4 } from "uuid";
import ArrowRightIcon from "@dndbeyond/fontawesome-cache/svgs/solid/arrow-right.svg";
import LinkIcon from "@dndbeyond/fontawesome-cache/svgs/solid/arrow-up-right-from-square.svg";
import { Accordion } from "~/components/Accordion";
import { Button } from "~/components/Button";
@@ -13,6 +13,7 @@ import { HtmlContent } from "~/components/HtmlContent";
import { Reference } from "~/components/Reference";
import { isNotNullOrUndefined } from "~/helpers/validation";
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
import { useDispatch } from "~/hooks/useDispatch";
import { builderSelectors } from "~/tools/js/CharacterBuilder/selectors";
import styles from "./styles.module.css";
@@ -97,7 +98,7 @@ export const ConfirmClassModal: FC<ConfirmClassModalProps> = ({
target="_blank"
rel="noreferrer"
>
{confirmClass.name} Details Page <ArrowRightIcon />
{confirmClass.name} Details Page <LinkIcon />
</Button>
</div>
)}
@@ -1,6 +1,5 @@
import clsx from "clsx";
import React, { FC } from "react";
import { useDispatch } from "react-redux";
import ArrowRightIcon from "@dndbeyond/fontawesome-cache/svgs/solid/arrow-right.svg";
@@ -11,6 +10,7 @@ import { SummaryList } from "~/components/SummaryList";
import { AppContextTypeEnum, DisplayConfigurationTypeEnum } from "~/constants";
import { isNotNullOrUndefined } from "~/helpers/validation";
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
import { useDispatch } from "~/hooks/useDispatch";
import { Feat, RacialTraitContract as RacialTrait } from "~/types";
import { useSpeciesContext } from "../../contexts/Species";
@@ -41,7 +41,9 @@ export const EditorWithDialog: FC<EditorWithDialogProps> = ({
const [isOpen, setIsOpen] = useState(false);
const textareaInput = useRef<HTMLDivElement>(null);
const id = uuidv4();
const headingNode = cloneElement(heading, { className: styles.heading });
const headingNode = cloneElement(heading, { className: styles.heading } as {
className: string;
});
const handleInputBlur = (content: string): void => {
if (saveOnBlur) {
@@ -1,6 +1,5 @@
import clsx from "clsx";
import { FC, FocusEvent, useMemo, useState } from "react";
import { useDispatch } from "react-redux";
import {
characterActions,
@@ -14,6 +13,7 @@ import {
import { ConfirmModal, ConfirmModalProps } from "~/components/ConfirmModal";
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
import { useDispatch } from "~/hooks/useDispatch";
import {
HP_BASE_MAX_VALUE,
HP_BONUS_VALUE,
@@ -3,6 +3,7 @@ import {
ChangeEvent,
FC,
FocusEvent,
FormEvent,
HTMLAttributes,
HTMLInputTypeAttribute,
useEffect,
@@ -10,6 +11,7 @@ import {
} from "react";
import { v4 as uuidv4 } from "uuid";
import formStyles from "../../styles/form.module.css";
import styles from "./styles.module.css";
interface InputProps extends HTMLAttributes<HTMLInputElement> {
@@ -18,14 +20,23 @@ interface InputProps extends HTMLAttributes<HTMLInputElement> {
autoComplete?: string;
}
export interface InputFieldProps extends HTMLAttributes<HTMLInputElement> {
export type InputElements = HTMLInputElement | HTMLTextAreaElement;
export interface InputFieldProps
extends Omit<
HTMLAttributes<HTMLInputElement>,
"onBlur" | "onChange" | "onFocus"
> {
errorMessage?: string;
initialValue?: string | number | null;
label: string;
maxLength?: number;
placeholder?: string;
type?: HTMLInputTypeAttribute;
type?: HTMLInputTypeAttribute | "textarea";
inputProps?: InputProps;
onBlur?: (event: FocusEvent<InputElements, Element>) => void;
onChange?: (event: FormEvent<InputElements>) => void;
onFocus?: (event: FocusEvent<InputElements, Element>) => void;
}
export const InputField: FC<InputFieldProps> = ({
@@ -49,7 +60,7 @@ export const InputField: FC<InputFieldProps> = ({
const [showError, setShowError] = useState(false);
const id = props.id ?? `input-field-${uuidv4()}`;
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const handleChange = (e: ChangeEvent<InputElements>) => {
const valueLength = e.target.value.length;
if (maxLength) {
// If the value length exceeds maxLength, show error
@@ -75,7 +86,7 @@ export const InputField: FC<InputFieldProps> = ({
return;
};
const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
const handleBlur = (e: FocusEvent<InputElements>) => {
const intValue = parseInt(e.target.value);
// If entered value is below the minimum, reset to minimum
@@ -103,7 +114,7 @@ export const InputField: FC<InputFieldProps> = ({
setShowError(false);
};
const handleFocus = (e: FocusEvent<HTMLInputElement>) => {
const handleFocus = (e: FocusEvent<InputElements>) => {
// Call the provided onFocus handler if it exists
if (onFocus) onFocus(e);
// Check for maxLength error
@@ -122,18 +133,29 @@ export const InputField: FC<InputFieldProps> = ({
return (
<div className={clsx([styles.inputField, className])} {...props}>
<label className={styles.label} htmlFor={id}>
<label className={formStyles.label} htmlFor={id}>
{label}
</label>
<input
className={styles.input}
value={value ?? ""}
onBlur={handleBlur}
onChange={handleChange}
onFocus={handleFocus}
{...inputAttrs}
{...inputProps}
/>
{type !== "textarea" ? (
<input
className={formStyles.input}
value={value ?? ""}
onBlur={handleBlur}
onChange={handleChange}
onFocus={handleFocus}
{...inputAttrs}
{...inputProps}
/>
) : (
<textarea
className={styles.input}
onBlur={handleBlur}
onChange={handleChange}
onFocus={handleFocus}
>
{value}
</textarea>
)}
{showError && <span className={styles.error}>{errorMessage}</span>}
</div>
);
@@ -1,6 +1,8 @@
import clsx from "clsx";
import { FC, HTMLAttributes } from "react";
import { NoResultsFound } from "~/components/NoResultsFound";
import { GroupedListingItem, ListingItem } from "../../types";
import { ListingGroup } from "./ListingGroup";
import { ListingItemButton } from "./ListingItemButton/ListingItemButton";
@@ -50,7 +52,7 @@ export const Listing: FC<ListingProps> = ({
);
})
) : (
<p className={styles.notFound}>No Listings Found</p>
<NoResultsFound message="No listings found." />
)}
</div>
);
@@ -82,6 +82,7 @@ export const ListingGroup: FC<ListingGroupProps> = ({
isDisabled={disabledIds && disabledIds.includes(item.id)}
key={item.id}
onQuickSelect={onQuickSelect}
data-testid="optionButton"
/>
))}
</div>
@@ -1,6 +1,6 @@
import clsx from "clsx";
import { FC, HTMLAttributes, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useSelector } from "react-redux";
import { CharacterPortraitContract } from "@dndbeyond/character-rules-engine";
import ShuffleIcon from "@dndbeyond/fontawesome-cache/svgs/solid/shuffle.svg";
@@ -14,6 +14,7 @@ import {
DefaultCharacterName,
} from "~/constants";
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
import { useDispatch } from "~/hooks/useDispatch";
import { builderActions } from "~/tools/js/CharacterBuilder/actions";
import { builderSelectors } from "~/tools/js/CharacterBuilder/selectors";
import { PortraitManager } from "~/tools/js/Shared/containers/panes/DecoratePane";
@@ -188,18 +189,6 @@ export const PortraitName: FC<Props> = ({
))}
</div>
</div>
<div className={styles.credit}>
Names by
<Button
className={styles.creditLink}
href="http://www.fantasynamegenerators.com/"
target="_blank"
rel="noreferrer"
variant="text"
>
<span className={styles.creditText}>Fantasy Name Generators</span>
</Button>
</div>
</div>
)}
</div>
@@ -1,5 +1,6 @@
import clsx from "clsx";
import { ChangeEvent, FC, HTMLAttributes } from "react";
import { v4 as uuid4 } from "uuid";
import styles from "./styles.module.css";
@@ -17,6 +18,7 @@ export const Search: FC<SearchProps> = ({
...props
}) => (
<input
id={`search-input-${uuid4()}`}
type="search"
className={clsx([styles.search, className])}
value={value}
@@ -0,0 +1,48 @@
import clsx from "clsx";
import { FC } from "react";
import { Select } from "~/components/Select";
import { SelectProps } from "~/components/Select/Select";
import styles from "./styles.module.css";
interface Props extends SelectProps {
label: string;
description?: string;
}
export const SelectField: FC<Props> = ({
value,
className,
description,
label,
options,
onChange,
onChangeConfirm,
hidePlaceholderOption,
placeholder,
name,
}) => {
return (
<div
className={clsx([styles.selectField, className])}
data-testid="select-field-container"
>
<div>
<label className={styles.label} htmlFor={name}>
{label}
</label>
{description && <div className={styles.description}>{description}</div>}
</div>
<Select
className={styles.field}
options={options}
onChange={onChange}
onChangeConfirm={onChangeConfirm}
value={value}
hidePlaceholderOption={hidePlaceholderOption}
placeholder={placeholder}
name={name}
/>
</div>
);
};
@@ -30,11 +30,11 @@ export const SpeciesDisplay: FC<SpeciesDisplayProps> = ({
portraitAvatarUrl || ruleDataUtils.getDefaultRaceImageUrl(ruleData);
// Navigate back to the class manage page
const handleNavigate = (): void =>
const handleNavigate = () =>
navigate(
navigationConfig
.getRouteDefPath(RouteKey.RACE_MANAGE)
.replace(":characterId", characterId)
.replace(":characterId", characterId.toString())
);
return (