New source found from dndbeyond.com
This commit is contained in:
+25
-10
@@ -1,9 +1,13 @@
|
||||
import { FocusEvent } from "react";
|
||||
|
||||
import {
|
||||
AbilityManager,
|
||||
HelperUtils,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
import { FormInputField } from "~/tools/js/Shared/components/common/FormInputField";
|
||||
|
||||
import { InputField } from "~/subApps/builder/components/InputField";
|
||||
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
interface Props {
|
||||
abilities: Array<AbilityManager>;
|
||||
@@ -16,14 +20,19 @@ export default function AbilityScoreManagerManual({
|
||||
minScore = 3,
|
||||
maxScore = 18,
|
||||
}: Props) {
|
||||
const handleTransformValueOnBlur = (value: string): number | null => {
|
||||
const parsedValue = HelperUtils.parseInputInt(value);
|
||||
const handleBlur = (
|
||||
e: FocusEvent<HTMLInputElement>,
|
||||
abilityScore: AbilityManager
|
||||
) => {
|
||||
// Get the int value from the input
|
||||
const parsedValue = HelperUtils.parseInputInt(e.target.value);
|
||||
// Create a clamped variable to hold the clamped value
|
||||
let clampedValue: number | null = null;
|
||||
if (parsedValue !== null) {
|
||||
// If the value is valid, clamp it to the min and max scores
|
||||
if (parsedValue !== null)
|
||||
clampedValue = HelperUtils.clampInt(parsedValue, minScore, maxScore);
|
||||
}
|
||||
|
||||
return clampedValue;
|
||||
// Set the score change from the clampped value
|
||||
abilityScore.handleScoreChange(clampedValue);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -35,11 +44,17 @@ export default function AbilityScoreManagerManual({
|
||||
key={abilityScore.getId()}
|
||||
data-stat-id={abilityScore.getId()}
|
||||
>
|
||||
<FormInputField
|
||||
transformValueOnBlur={handleTransformValueOnBlur}
|
||||
onBlur={(value) => abilityScore.handleScoreChange(Number(value))}
|
||||
<InputField
|
||||
className={styles.input}
|
||||
type="number"
|
||||
label={abilityScore.getLabel() ?? ""}
|
||||
initialValue={abilityScore.getBaseScore()}
|
||||
inputProps={{
|
||||
min: minScore,
|
||||
max: maxScore,
|
||||
autoComplete: "off",
|
||||
}}
|
||||
onBlur={(e) => handleBlur(e, abilityScore)}
|
||||
/>
|
||||
<div className="ability-score-manager-stat-total">
|
||||
Total: {abilityScore.getTotalScore() ?? "--"}
|
||||
|
||||
+43
-38
@@ -1,6 +1,6 @@
|
||||
import axios, { Canceler } from "axios";
|
||||
import { orderBy } from "lodash";
|
||||
import React, { HTMLAttributes, useContext } from "react";
|
||||
import React, { FocusEvent, HTMLAttributes, useContext } from "react";
|
||||
import { DispatchProp } from "react-redux";
|
||||
|
||||
import { LoadingPlaceholder, Select } from "@dndbeyond/character-components/es";
|
||||
@@ -52,12 +52,12 @@ import { HtmlContent } from "~/components/HtmlContent";
|
||||
import { Link } from "~/components/Link";
|
||||
import { useSource } from "~/hooks/useSource";
|
||||
import { EditorWithDialog } from "~/subApps/builder/components/EditorWithDialog";
|
||||
import { InputField } from "~/subApps/builder/components/InputField";
|
||||
import { RouteKey } from "~/subApps/builder/constants";
|
||||
import {
|
||||
ModalData,
|
||||
useModalManager,
|
||||
} from "~/subApps/builder/contexts/ModalManager";
|
||||
import { FormInputField } from "~/tools/js/Shared/components/common/FormInputField";
|
||||
import { DetailChoice } from "~/tools/js/Shared/containers/DetailChoice";
|
||||
|
||||
import { toastMessageActions } from "../../../../Shared/actions";
|
||||
@@ -195,7 +195,7 @@ class CustomBackgroundManager extends React.PureComponent<
|
||||
<div className="custom-background-property-value">
|
||||
<input
|
||||
type="text"
|
||||
defaultValue={name ? name : ""}
|
||||
defaultValue={name || ""}
|
||||
onBlur={this.handleTextInputBlur.bind(this, "name")}
|
||||
/>
|
||||
</div>
|
||||
@@ -204,7 +204,7 @@ class CustomBackgroundManager extends React.PureComponent<
|
||||
<div className="custom-background-property custom-background-property-textarea">
|
||||
<div className="custom-background-property-value">
|
||||
<textarea
|
||||
defaultValue={description ? description : ""}
|
||||
defaultValue={description || ""}
|
||||
onBlur={this.handleTextInputBlur.bind(this, "description")}
|
||||
/>
|
||||
</div>
|
||||
@@ -471,35 +471,35 @@ class DescriptionManage extends React.PureComponent<Props, State> {
|
||||
dispatch(characterActions.traitSet(traitType, content));
|
||||
};
|
||||
|
||||
handleHairChange = (value: string): void => {
|
||||
handleHairChange = (e: FocusEvent<HTMLInputElement>): void => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(characterActions.hairSet(value));
|
||||
dispatch(characterActions.hairSet(e.target.value));
|
||||
};
|
||||
|
||||
handleSkinChange = (value: string): void => {
|
||||
handleSkinChange = (e: FocusEvent<HTMLInputElement>): void => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(characterActions.skinSet(value));
|
||||
dispatch(characterActions.skinSet(e.target.value));
|
||||
};
|
||||
|
||||
handleEyesChange = (value: string): void => {
|
||||
handleEyesChange = (e: FocusEvent<HTMLInputElement>): void => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(characterActions.eyesSet(value));
|
||||
dispatch(characterActions.eyesSet(e.target.value));
|
||||
};
|
||||
|
||||
handleHeightChange = (value: string): void => {
|
||||
handleHeightChange = (e: FocusEvent<HTMLInputElement>): void => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(characterActions.heightSet(value));
|
||||
dispatch(characterActions.heightSet(e.target.value));
|
||||
};
|
||||
|
||||
handleWeightChange = (value: string): void => {
|
||||
handleWeightChange = (e: FocusEvent<HTMLInputElement>): void => {
|
||||
const { dispatch } = this.props;
|
||||
let parsedValue = HelperUtils.parseInputInt(value);
|
||||
let parsedValue = this.handleTransformValueToNumberOnBlur(e.target.value);
|
||||
dispatch(characterActions.weightSet(parsedValue));
|
||||
};
|
||||
|
||||
handleAgeChange = (value: string): void => {
|
||||
handleAgeChange = (e: FocusEvent<HTMLInputElement>): void => {
|
||||
const { dispatch } = this.props;
|
||||
let parsedValue = HelperUtils.parseInputInt(value);
|
||||
let parsedValue = this.handleTransformValueToNumberOnBlur(e.target.value);
|
||||
dispatch(characterActions.ageSet(parsedValue));
|
||||
};
|
||||
|
||||
@@ -517,9 +517,9 @@ class DescriptionManage extends React.PureComponent<Props, State> {
|
||||
return clampedValue;
|
||||
};
|
||||
|
||||
handleGenderChange = (value: string): void => {
|
||||
handleGenderChange = (e: FocusEvent<HTMLInputElement>): void => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(characterActions.genderSet(value));
|
||||
dispatch(characterActions.genderSet(e.target.value));
|
||||
};
|
||||
|
||||
handleBackgroundChangePromise = (
|
||||
@@ -608,9 +608,9 @@ class DescriptionManage extends React.PureComponent<Props, State> {
|
||||
dispatch(characterActions.lifestyleSet(HelperUtils.parseInputInt(value)));
|
||||
};
|
||||
|
||||
handleFaithChange = (value: string): void => {
|
||||
handleFaithChange = (e: FocusEvent<HTMLInputElement>): void => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(characterActions.faithSet(value));
|
||||
dispatch(characterActions.faithSet(e.target.value));
|
||||
};
|
||||
|
||||
handleCustomBackgroundSave = (properties: any): void => {
|
||||
@@ -716,7 +716,7 @@ class DescriptionManage extends React.PureComponent<Props, State> {
|
||||
<DetailChoice
|
||||
{...choice}
|
||||
choice={choice}
|
||||
key={choiceId ? choiceId : ""}
|
||||
key={choiceId || ""}
|
||||
options={availableOptions}
|
||||
onChange={this.handleChoiceChange}
|
||||
choiceInfo={choiceInfo}
|
||||
@@ -1035,7 +1035,7 @@ class DescriptionManage extends React.PureComponent<Props, State> {
|
||||
) : (
|
||||
<HtmlContent
|
||||
className="description-manage-background-description"
|
||||
html={shortDescription ? shortDescription : ""}
|
||||
html={shortDescription || ""}
|
||||
withoutTooltips
|
||||
/>
|
||||
)}
|
||||
@@ -1325,17 +1325,17 @@ class DescriptionManage extends React.PureComponent<Props, State> {
|
||||
{alignmentDescriptionNode}
|
||||
</div>
|
||||
<div className="description-manage-faith">
|
||||
<FormInputField
|
||||
<InputField
|
||||
label="Faith"
|
||||
initialValue={faith}
|
||||
onBlur={this.handleFaithChange}
|
||||
inputAttributes={
|
||||
maxLength={512}
|
||||
inputProps={
|
||||
{
|
||||
spellCheck: false,
|
||||
autoComplete: "off",
|
||||
} as HTMLAttributes<HTMLInputElement>
|
||||
}
|
||||
maxLength={512}
|
||||
/>
|
||||
</div>
|
||||
<div className="description-manage-lifestyle">
|
||||
@@ -1367,47 +1367,52 @@ class DescriptionManage extends React.PureComponent<Props, State> {
|
||||
]}
|
||||
variant="paper"
|
||||
>
|
||||
<FormInputField
|
||||
<InputField
|
||||
className={styles.inputField}
|
||||
label="Hair"
|
||||
initialValue={hair}
|
||||
onBlur={this.handleHairChange}
|
||||
maxLength={50}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
className={styles.inputField}
|
||||
label="Skin"
|
||||
initialValue={skin}
|
||||
onBlur={this.handleSkinChange}
|
||||
maxLength={50}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
className={styles.inputField}
|
||||
label="Eyes"
|
||||
initialValue={eyes}
|
||||
onBlur={this.handleEyesChange}
|
||||
maxLength={50}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
className={styles.inputField}
|
||||
label="Height"
|
||||
initialValue={height}
|
||||
onBlur={this.handleHeightChange}
|
||||
maxLength={50}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
className={styles.inputField}
|
||||
label="Weight (lbs)"
|
||||
type={"number"}
|
||||
type="number"
|
||||
initialValue={weight}
|
||||
onBlur={this.handleWeightChange}
|
||||
transformValueOnBlur={this.handleTransformValueToNumberOnBlur}
|
||||
inputAttributes={numberInputAttributes}
|
||||
inputProps={numberInputAttributes}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
className={styles.inputField}
|
||||
label="Age (Years)"
|
||||
type={"number"}
|
||||
type="number"
|
||||
initialValue={age}
|
||||
onBlur={this.handleAgeChange}
|
||||
transformValueOnBlur={this.handleTransformValueToNumberOnBlur}
|
||||
inputAttributes={numberInputAttributes}
|
||||
inputProps={numberInputAttributes}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
className={styles.inputField}
|
||||
label="Gender"
|
||||
initialValue={gender}
|
||||
onBlur={this.handleGenderChange}
|
||||
|
||||
+36
-30
@@ -1,5 +1,10 @@
|
||||
import { Typography } from "@mui/material";
|
||||
import React, { ChangeEvent, HTMLAttributes, ReactNode } from "react";
|
||||
import React, {
|
||||
ChangeEvent,
|
||||
FocusEvent,
|
||||
HTMLAttributes,
|
||||
ReactNode,
|
||||
} from "react";
|
||||
import { DispatchProp } from "react-redux";
|
||||
|
||||
import {
|
||||
@@ -24,12 +29,12 @@ import {
|
||||
import { Dice } from "@dndbeyond/dice";
|
||||
|
||||
import { SourceCategoryDescription } from "~/constants";
|
||||
import { InputField } from "~/subApps/builder/components/InputField";
|
||||
import { RouteKey } from "~/subApps/builder/constants";
|
||||
import {
|
||||
ModalData,
|
||||
useModalManager,
|
||||
} from "~/subApps/builder/contexts/ModalManager";
|
||||
import { FormInputField } from "~/tools/js/Shared/components/common/FormInputField";
|
||||
import UserRoles from "~/tools/js/Shared/constants/UserRoles";
|
||||
import PrivacyTypeRadio from "~/tools/js/smartComponents/PrivacyTypeRadio";
|
||||
|
||||
@@ -723,66 +728,67 @@ class HomeBasicInfo extends React.PureComponent<Props> {
|
||||
this.forceUpdate();
|
||||
}}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
label="Long Description"
|
||||
initialValue={premadeInfo.definition.longDescription}
|
||||
inputAttributes={inputAttributes}
|
||||
onBlur={(value: string) => {
|
||||
premadeInfo.definition.longDescription = value;
|
||||
inputProps={inputAttributes}
|
||||
onBlur={(e: FocusEvent<HTMLInputElement>) => {
|
||||
premadeInfo.definition.longDescription = e.target.value;
|
||||
this.handlePremadeInfoChanged(premadeInfo);
|
||||
}}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
label="Short Description"
|
||||
initialValue={premadeInfo.definition.shortDescription}
|
||||
inputAttributes={inputAttributes}
|
||||
onBlur={(value: string) => {
|
||||
premadeInfo.definition.shortDescription = value;
|
||||
inputProps={inputAttributes}
|
||||
onBlur={(e: FocusEvent<HTMLInputElement>) => {
|
||||
premadeInfo.definition.shortDescription = e.target.value;
|
||||
this.handlePremadeInfoChanged(premadeInfo);
|
||||
}}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
label="Image Url"
|
||||
initialValue={premadeInfo.definition.imageUrl}
|
||||
inputAttributes={inputAttributes}
|
||||
onBlur={(value: string) => {
|
||||
premadeInfo.definition.imageUrl = value;
|
||||
inputProps={inputAttributes}
|
||||
onBlur={(e: FocusEvent<HTMLInputElement>) => {
|
||||
premadeInfo.definition.imageUrl = e.target.value;
|
||||
this.handlePremadeInfoChanged(premadeInfo);
|
||||
}}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
label="Image Alt Text"
|
||||
initialValue={premadeInfo.definition.imageAltText}
|
||||
inputAttributes={inputAttributes}
|
||||
onBlur={(value: string) => {
|
||||
premadeInfo.definition.imageAltText = value;
|
||||
inputProps={inputAttributes}
|
||||
onBlur={(e: FocusEvent<HTMLInputElement>) => {
|
||||
premadeInfo.definition.imageAltText = e.target.value;
|
||||
this.handlePremadeInfoChanged(premadeInfo);
|
||||
}}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
label="Mobile Image Url"
|
||||
initialValue={premadeInfo.definition.mobileImageUrl}
|
||||
inputAttributes={inputAttributes}
|
||||
onBlur={(value: string) => {
|
||||
premadeInfo.definition.mobileImageUrl = value;
|
||||
inputProps={inputAttributes}
|
||||
onBlur={(e: FocusEvent<HTMLInputElement>) => {
|
||||
premadeInfo.definition.mobileImageUrl = e.target.value;
|
||||
this.handlePremadeInfoChanged(premadeInfo);
|
||||
}}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
label="Mobile Image Accessibility"
|
||||
initialValue={premadeInfo.definition.mobileImageAccessibility}
|
||||
inputAttributes={inputAttributes}
|
||||
onBlur={(value: string) => {
|
||||
premadeInfo.definition.mobileImageAccessibility = value;
|
||||
inputProps={inputAttributes}
|
||||
onBlur={(e: FocusEvent<HTMLInputElement>) => {
|
||||
premadeInfo.definition.mobileImageAccessibility =
|
||||
e.target.value;
|
||||
this.handlePremadeInfoChanged(premadeInfo);
|
||||
}}
|
||||
/>
|
||||
<FormInputField
|
||||
<InputField
|
||||
label="Theme Color Hex Code"
|
||||
initialValue={premadeInfo.definition.themeColor}
|
||||
inputAttributes={inputAttributes}
|
||||
onBlur={(value: string) => {
|
||||
premadeInfo.definition.themeColor = value;
|
||||
inputProps={inputAttributes}
|
||||
onBlur={(e: FocusEvent<HTMLInputElement>) => {
|
||||
premadeInfo.definition.themeColor = e.target.value;
|
||||
this.handlePremadeInfoChanged(premadeInfo);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -62,8 +62,6 @@ interface Props {
|
||||
theme?: CharacterTheme;
|
||||
}
|
||||
|
||||
const infoItemProps = { role: "listItem", inline: true };
|
||||
|
||||
export const ActionDetail = ({
|
||||
showCustomize = true,
|
||||
largePoolMinAmount = 11,
|
||||
@@ -79,6 +77,8 @@ export const ActionDetail = ({
|
||||
theme,
|
||||
inventoryLookup,
|
||||
}: Props) => {
|
||||
const infoItemProps = { role: "listItem", inline: true };
|
||||
|
||||
const handleRemoveCustomizations = () => {
|
||||
if (onCustomizationsRemove) {
|
||||
onCustomizationsRemove();
|
||||
@@ -427,17 +427,17 @@ export const ActionDetail = ({
|
||||
</InfoItem>
|
||||
)}
|
||||
{attackSubtypeId && (
|
||||
<InfoItem label="Attack Type:">
|
||||
<InfoItem label="Attack Type:" {...infoItemProps}>
|
||||
{ActionUtils.getAttackSubtypeName(action)}
|
||||
</InfoItem>
|
||||
)}
|
||||
{requiresAttackRoll && (
|
||||
<InfoItem label="To Hit:">
|
||||
<InfoItem label="To Hit:" {...infoItemProps}>
|
||||
<NumberDisplay type="signed" number={toHit} />
|
||||
</InfoItem>
|
||||
)}
|
||||
{requiresSavingThrow && (
|
||||
<InfoItem label="Attack/Save:">
|
||||
<InfoItem label="Attack/Save:" {...infoItemProps}>
|
||||
{saveStateId !== null
|
||||
? RuleDataUtils.getStatNameById(saveStateId, ruleData)
|
||||
: ""}{" "}
|
||||
@@ -446,14 +446,14 @@ export const ActionDetail = ({
|
||||
)}
|
||||
{renderDamageProperties()}
|
||||
{requiresAttackRoll && (
|
||||
<InfoItem label="Stat:">
|
||||
<InfoItem label="Stat:" {...infoItemProps}>
|
||||
{statId === null
|
||||
? "--"
|
||||
: RuleDataUtils.getStatNameById(statId, ruleData)}
|
||||
</InfoItem>
|
||||
)}
|
||||
{rangeAreas.length > 0 && (
|
||||
<InfoItem label="Range/Area:">
|
||||
<InfoItem label="Range/Area:" {...infoItemProps}>
|
||||
{rangeAreas.map((node, idx) => (
|
||||
<React.Fragment key={idx}>
|
||||
{node}
|
||||
@@ -462,8 +462,16 @@ export const ActionDetail = ({
|
||||
))}
|
||||
</InfoItem>
|
||||
)}
|
||||
{isProficient && <InfoItem label="Proficient:">Yes</InfoItem>}
|
||||
{notes && <InfoItem label="Notes:">{notes}</InfoItem>}
|
||||
{isProficient && (
|
||||
<InfoItem label="Proficient:" {...infoItemProps}>
|
||||
Yes
|
||||
</InfoItem>
|
||||
)}
|
||||
{notes && (
|
||||
<InfoItem label="Notes:" {...infoItemProps}>
|
||||
{notes}
|
||||
</InfoItem>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -92,7 +92,7 @@ export default function SpellManagerContainer({
|
||||
|
||||
const getData = useCallback(
|
||||
async function getData() {
|
||||
let classSpellMap = await spellsManager.getSpellShoppe();
|
||||
let classSpellMap = await spellsManager.getSpellShoppe(true);
|
||||
if (!classSpellMap[charClassId] || shouldFetch) {
|
||||
classSpellMap = await spellsManager.getSpellShoppe(true);
|
||||
}
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
import clsx from "clsx";
|
||||
import {
|
||||
ChangeEvent,
|
||||
FC,
|
||||
FocusEvent,
|
||||
HTMLAttributes,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { useMaxLengthErrorHandling } from "~/hooks/useErrorHandling/useMaxLengthErrorHandling";
|
||||
|
||||
export interface FormInputFieldProps
|
||||
extends Omit<
|
||||
HTMLAttributes<HTMLDivElement>,
|
||||
"onChange" | "onBlur" | "onFocus"
|
||||
> {
|
||||
label: string;
|
||||
type?: string;
|
||||
placeholder?: string;
|
||||
initialValue?: string | number | null;
|
||||
inputAttributes?: HTMLAttributes<HTMLInputElement>;
|
||||
maxLength?: number;
|
||||
maxLengthErrorMsg?: string;
|
||||
variant?: "default" | "small";
|
||||
// Handlers
|
||||
onChange?: (value: string | number | null) => void;
|
||||
onBlur?: (value: string | number | null) => void;
|
||||
onFocus?: (value: string | number | null) => void;
|
||||
transformValueOnChange?: (value: string) => string | number | null;
|
||||
transformValueOnBlur?: (value: string) => string | number | null;
|
||||
transformValueOnFocus?: (value: string) => string | number | null;
|
||||
}
|
||||
|
||||
export const FormInputField: FC<FormInputFieldProps> = ({
|
||||
label, // req
|
||||
type = "text",
|
||||
placeholder = "",
|
||||
initialValue = "",
|
||||
inputAttributes = {},
|
||||
maxLength,
|
||||
maxLengthErrorMsg = "",
|
||||
...handlers
|
||||
}) => {
|
||||
const prevInitialValue = useRef(initialValue);
|
||||
const initialLengthErrorState = maxLength
|
||||
? (initialValue?.toString().length ?? 0) >= maxLength
|
||||
: false;
|
||||
const { MaxLengthErrorMessage, hideError, handleMaxLengthErrorMsg } =
|
||||
useMaxLengthErrorHandling(
|
||||
initialLengthErrorState,
|
||||
maxLength ?? null,
|
||||
maxLengthErrorMsg
|
||||
);
|
||||
const guid = `FIF_${uuidv4()}`;
|
||||
const [value, setValue] = useState(initialValue);
|
||||
|
||||
const handleOnChange = (evt: ChangeEvent<HTMLInputElement>): void => {
|
||||
const { onChange = null, transformValueOnChange = null } = handlers;
|
||||
const { value: targetElementValue } = evt.target;
|
||||
const value = transformValueOnChange
|
||||
? transformValueOnChange?.(targetElementValue)
|
||||
: targetElementValue;
|
||||
|
||||
onChange?.(value);
|
||||
setValue(value);
|
||||
if (value !== null) {
|
||||
handleMaxLengthErrorMsg(value.toString());
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnBlur = (evt: FocusEvent<HTMLInputElement>) => {
|
||||
const { onBlur = null, transformValueOnBlur = null } = handlers;
|
||||
const { value: targetElementValue } = evt.target;
|
||||
const value = transformValueOnBlur
|
||||
? transformValueOnBlur?.(targetElementValue)
|
||||
: targetElementValue;
|
||||
|
||||
onBlur?.(value);
|
||||
if (value !== targetElementValue) {
|
||||
setValue(value);
|
||||
}
|
||||
// Always hide any length warnings when leaving the field since you can't go over the limit anyways
|
||||
hideError();
|
||||
};
|
||||
|
||||
const handleOnFocus = (evt: FocusEvent<HTMLInputElement>) => {
|
||||
const { onFocus = null, transformValueOnFocus = null } = handlers;
|
||||
const { value: targetElementValue } = evt.target;
|
||||
const value = transformValueOnFocus
|
||||
? transformValueOnFocus?.(targetElementValue)
|
||||
: targetElementValue;
|
||||
|
||||
onFocus?.(value);
|
||||
if (value !== targetElementValue) {
|
||||
setValue(value);
|
||||
}
|
||||
if (value !== null) {
|
||||
handleMaxLengthErrorMsg(value.toString());
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (initialValue !== prevInitialValue.current) {
|
||||
setValue(initialValue ?? "");
|
||||
prevInitialValue.current = initialValue;
|
||||
}
|
||||
}, [initialValue]);
|
||||
|
||||
return (
|
||||
<div className={clsx(["builder-field", "form-input-field"])}>
|
||||
<span className="builder-field-label">
|
||||
<label
|
||||
className="builder-field-heading form-input-field-label"
|
||||
htmlFor={guid}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
</span>
|
||||
<span className="builder-field-input">
|
||||
<input
|
||||
className="builder-field-value"
|
||||
id={guid}
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
onChange={handleOnChange}
|
||||
onBlur={handleOnBlur}
|
||||
onFocus={handleOnFocus}
|
||||
value={value ?? ""}
|
||||
maxLength={maxLength || undefined}
|
||||
{...inputAttributes}
|
||||
/>
|
||||
</span>
|
||||
<MaxLengthErrorMessage />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
import clsx from "clsx";
|
||||
import * as React from "react";
|
||||
|
||||
import { PrerequisiteFailure } from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
interface Props {
|
||||
className: string;
|
||||
failures: Array<Array<PrerequisiteFailure>>;
|
||||
}
|
||||
|
||||
export default class PrerequisiteFailureSummary extends React.PureComponent<
|
||||
Props,
|
||||
{}
|
||||
> {
|
||||
static defaultProps = {
|
||||
className: "",
|
||||
};
|
||||
|
||||
render() {
|
||||
const { failures, className } = this.props;
|
||||
|
||||
let strings: string = failures
|
||||
.map((failure) =>
|
||||
failure
|
||||
.map((failureAnd) => failureAnd.data.requiredDescription)
|
||||
.reduce((acc, desc, idx) => {
|
||||
let connector: string = "";
|
||||
if (idx < failure.length - 2) {
|
||||
connector = ", ";
|
||||
} else if (idx < failure.length - 1) {
|
||||
connector = " and ";
|
||||
}
|
||||
acc += `${desc}${connector}`;
|
||||
return acc;
|
||||
}, "")
|
||||
)
|
||||
.join(" or ");
|
||||
return (
|
||||
<div className={clsx(["ddbc-prerequisite-failure-summary", className])}>
|
||||
Missing: {strings.length > 0 ? strings : "Unknown"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import PrerequisiteFailureSummary from "./PrerequisiteFailureSummary";
|
||||
|
||||
export default PrerequisiteFailureSummary;
|
||||
export { PrerequisiteFailureSummary };
|
||||
Reference in New Issue
Block a user