Grabbed dndbeyond's source code

```
~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js
```
This commit is contained in:
2025-05-28 11:50:03 -07:00
commit 8df9031d27
3592 changed files with 319051 additions and 0 deletions
@@ -0,0 +1,264 @@
import React from "react";
import { connect, DispatchProp } from "react-redux";
import {
AlignmentContract,
characterActions,
CharacterLifestyleContract,
HtmlSelectOption,
RuleData,
RuleDataUtils,
rulesEngineSelectors,
SizeContract,
} from "@dndbeyond/character-rules-engine/es";
import { HtmlContent } from "~/components/HtmlContent";
import { Header } from "~/subApps/sheet/components/Sidebar/components/Header";
import { SharedAppState } from "../../../stores/typings";
import { TypeScriptUtils } from "../../../utils";
import DescriptionPaneEntry from "./DescriptionPaneEntry";
import DescriptionPaneEntryContent from "./DescriptionPaneEntryContent";
import DescriptionPaneNumberEditor from "./DescriptionPaneNumberEditor";
import DescriptionPaneSelectEditor from "./DescriptionPaneSelectEditor";
import DescriptionPaneTextEditor from "./DescriptionPaneTextEditor";
interface Props extends DispatchProp {
height: string | null;
weight: number | null;
size: SizeContract | null;
faith: string | null;
skin: string | null;
eyes: string | null;
hair: string | null;
age: number | null;
gender: string | null;
alignment: AlignmentContract | null;
lifestyle: CharacterLifestyleContract | null;
ruleData: RuleData;
}
class DescriptionPane extends React.PureComponent<Props> {
handleAlignmentUpdate = (propertyKey: string, value: number | null): void => {
const { dispatch } = this.props;
dispatch(characterActions.alignmentSet(value));
};
handleLifestyleUpdate = (propertyKey: string, value: number | null): void => {
const { dispatch } = this.props;
dispatch(characterActions.lifestyleSet(value));
};
handleFaithChange = (propertyKey: string, value: string): void => {
const { dispatch } = this.props;
dispatch(characterActions.faithSet(value));
};
handleHairChange = (propertyKey: string, value: string): void => {
const { dispatch } = this.props;
dispatch(characterActions.hairSet(value));
};
handleSkinChange = (propertyKey: string, value: string): void => {
const { dispatch } = this.props;
dispatch(characterActions.skinSet(value));
};
handleEyesChange = (propertyKey: string, value: string): void => {
const { dispatch } = this.props;
dispatch(characterActions.eyesSet(value));
};
handleHeightChange = (propertyKey: string, value: string): void => {
const { dispatch } = this.props;
dispatch(characterActions.heightSet(value));
};
handleWeightChange = (propertyKey: string, value: number | null): void => {
const { dispatch } = this.props;
dispatch(characterActions.weightSet(value));
};
handleAgeChange = (propertyKey: string, value: number | null): void => {
const { dispatch } = this.props;
dispatch(characterActions.ageSet(value));
};
handleGenderChange = (propertyKey: string, value: string): void => {
const { dispatch } = this.props;
dispatch(characterActions.genderSet(value));
};
renderCharacterDetails = (): React.ReactNode => {
const { alignment, lifestyle, ruleData, faith } = this.props;
const alignmentData = RuleDataUtils.getAlignments(ruleData);
const lifestyleData = RuleDataUtils.getLifestyles(ruleData);
const alignmentOptions = RuleDataUtils.getAlignmentOptions(ruleData);
let alignmentDescriptionNode: React.ReactNode;
if (alignment !== null && alignment.description) {
alignmentDescriptionNode = (
<HtmlContent html={alignment.description} withoutTooltips />
);
}
const lifestyleOptions: Array<HtmlSelectOption> = lifestyleData
.map((lifestyle) => {
if (lifestyle.id === null) {
return null;
}
return {
label: `${lifestyle.name} ${
lifestyle.cost === "-" ? "" : `(${lifestyle.cost})`
}`,
value: lifestyle.id,
};
})
.filter(TypeScriptUtils.isNotNullOrUndefined);
let lifestyleDescriptionNode: React.ReactNode;
if (lifestyle !== null) {
lifestyleDescriptionNode = lifestyle.description;
}
return (
<React.Fragment>
<DescriptionPaneEntry>
<DescriptionPaneSelectEditor
label="Alignment"
options={alignmentOptions}
defaultValue={alignment === null ? null : alignment.id}
propertyKey="alignmentId"
onUpdate={this.handleAlignmentUpdate}
/>
<DescriptionPaneEntryContent>
{alignmentDescriptionNode}
</DescriptionPaneEntryContent>
</DescriptionPaneEntry>
<DescriptionPaneEntry>
<DescriptionPaneTextEditor
label="Faith"
defaultValue={faith}
propertyKey="faith"
onUpdate={this.handleFaithChange}
maxLength={512}
/>
</DescriptionPaneEntry>
<DescriptionPaneEntry>
<DescriptionPaneSelectEditor
label="Lifestyle"
options={lifestyleOptions}
defaultValue={lifestyle === null ? null : lifestyle.id}
propertyKey="lifestyleId"
onUpdate={this.handleLifestyleUpdate}
/>
<DescriptionPaneEntryContent>
{lifestyleDescriptionNode}
</DescriptionPaneEntryContent>
</DescriptionPaneEntry>
</React.Fragment>
);
};
renderPhysicalCharacteristics = (): React.ReactNode => {
const { height, weight, skin, eyes, hair, age, gender } = this.props;
return (
<React.Fragment>
<DescriptionPaneEntry>
<DescriptionPaneTextEditor
label="Hair"
defaultValue={hair}
propertyKey="hair"
onUpdate={this.handleHairChange}
maxLength={50}
/>
</DescriptionPaneEntry>
<DescriptionPaneEntry>
<DescriptionPaneTextEditor
label="Skin"
defaultValue={skin}
propertyKey="skin"
onUpdate={this.handleSkinChange}
maxLength={50}
/>
</DescriptionPaneEntry>
<DescriptionPaneEntry>
<DescriptionPaneTextEditor
label="Eyes"
defaultValue={eyes}
propertyKey="eyes"
onUpdate={this.handleEyesChange}
maxLength={50}
/>
</DescriptionPaneEntry>
<DescriptionPaneEntry>
<DescriptionPaneTextEditor
label="Height"
defaultValue={height}
propertyKey="height"
onUpdate={this.handleHeightChange}
maxLength={50}
/>
</DescriptionPaneEntry>
<DescriptionPaneEntry>
<DescriptionPaneNumberEditor
label="Weight (lbs)"
defaultValue={weight}
propertyKey="weight"
onUpdate={this.handleWeightChange}
/>
</DescriptionPaneEntry>
<DescriptionPaneEntry>
<DescriptionPaneNumberEditor
label="Age (Years)"
defaultValue={age}
propertyKey="age"
onUpdate={this.handleAgeChange}
/>
</DescriptionPaneEntry>
<DescriptionPaneEntry>
<DescriptionPaneTextEditor
label="Gender"
defaultValue={gender}
propertyKey="gender"
onUpdate={this.handleGenderChange}
maxLength={128}
/>
</DescriptionPaneEntry>
</React.Fragment>
);
};
render() {
return (
<div className="ct-description-pane">
<Header>Characteristics and Details</Header>
<div className="ct-description-pane__entries">
{this.renderCharacterDetails()}
{this.renderPhysicalCharacteristics()}
</div>
</div>
);
}
}
function mapStateToProps(state: SharedAppState) {
return {
ruleData: rulesEngineSelectors.getRuleData(state),
alignment: rulesEngineSelectors.getAlignment(state),
height: rulesEngineSelectors.getHeight(state),
weight: rulesEngineSelectors.getWeight(state),
size: rulesEngineSelectors.getSize(state),
faith: rulesEngineSelectors.getFaith(state),
skin: rulesEngineSelectors.getSkin(state),
eyes: rulesEngineSelectors.getEyes(state),
hair: rulesEngineSelectors.getHair(state),
age: rulesEngineSelectors.getAge(state),
gender: rulesEngineSelectors.getGender(state),
lifestyle: rulesEngineSelectors.getLifestyle(state),
};
}
export default connect(mapStateToProps)(DescriptionPane);
@@ -0,0 +1,21 @@
import React from "react";
import { FormatUtils } from "@dndbeyond/character-rules-engine/es";
interface Props {
propertyKey: string;
className: string;
}
export default class DescriptionPaneEditor extends React.PureComponent<Props> {
render() {
const { propertyKey, className } = this.props;
let classNames: Array<string> = [
"ct-description-pane__editor",
`ct-description-pane__editor--${FormatUtils.slugify(propertyKey)}`,
className,
];
return <div className={classNames.join(" ")}>{this.props.children}</div>;
}
}
@@ -0,0 +1,4 @@
import DescriptionPaneEditor from "./DescriptionPaneEditor";
export default DescriptionPaneEditor;
export { DescriptionPaneEditor };
@@ -0,0 +1,11 @@
import React from "react";
export default class DescriptionPaneEditorLabel extends React.PureComponent {
render() {
return (
<div className="ct-description-pane__editor-label">
{this.props.children}
</div>
);
}
}
@@ -0,0 +1,4 @@
import DescriptionPaneEditorLabel from "./DescriptionPaneEditorLabel";
export default DescriptionPaneEditorLabel;
export { DescriptionPaneEditorLabel };
@@ -0,0 +1,11 @@
import React from "react";
export default class DescriptionPaneEditorValue extends React.PureComponent {
render() {
return (
<div className="ct-description-pane__editor-value">
{this.props.children}
</div>
);
}
}
@@ -0,0 +1,4 @@
import DescriptionPaneEditorValue from "./DescriptionPaneEditorValue";
export default DescriptionPaneEditorValue;
export { DescriptionPaneEditorValue };
@@ -0,0 +1,9 @@
import React from "react";
export default class DescriptionPaneEntry extends React.PureComponent {
render() {
return (
<div className="ct-description-pane__entry">{this.props.children}</div>
);
}
}
@@ -0,0 +1,4 @@
import DescriptionPaneEntry from "./DescriptionPaneEntry";
export default DescriptionPaneEntry;
export { DescriptionPaneEntry };
@@ -0,0 +1,11 @@
import React from "react";
export default class DescriptionPaneEntryContent extends React.PureComponent {
render() {
return (
<div className="ct-description-pane__entry-content">
{this.props.children}
</div>
);
}
}
@@ -0,0 +1,4 @@
import DescriptionPaneEntryContent from "./DescriptionPaneEntryContent";
export default DescriptionPaneEntryContent;
export { DescriptionPaneEntryContent };
@@ -0,0 +1,84 @@
import React from "react";
import { HelperUtils } from "@dndbeyond/character-rules-engine/es";
import { CHARACTER_DESCRIPTION_NUMBER_VALUE } from "../../../../constants/App";
import DescriptionPaneEditor from "../DescriptionPaneEditor";
import DescriptionPaneEditorLabel from "../DescriptionPaneEditorLabel";
import DescriptionPaneEditorValue from "../DescriptionPaneEditorValue";
interface Props {
label: string;
propertyKey: string;
defaultValue: number | null;
minValue: number;
maxValue: number;
onUpdate?: (propertyKey: string, value: number | null) => void;
}
interface State {
value: number | null;
}
export default class DescriptionPaneNumberEditor extends React.PureComponent<
Props,
State
> {
static defaultProps = {
minValue: CHARACTER_DESCRIPTION_NUMBER_VALUE.MIN,
maxValue: CHARACTER_DESCRIPTION_NUMBER_VALUE.MAX,
};
constructor(props: Props) {
super(props);
this.state = {
value: props.defaultValue,
};
}
handleBlur = (evt: React.FocusEvent<HTMLInputElement>): void => {
const { propertyKey, onUpdate, maxValue, minValue } = this.props;
let parsedValue = HelperUtils.parseInputInt(evt.target.value);
let clampedValue: number | null =
parsedValue === null
? null
: HelperUtils.clampInt(parsedValue, minValue, maxValue);
if (onUpdate) {
onUpdate(propertyKey, clampedValue);
}
this.setState({
value: clampedValue,
});
};
handleChange = (evt: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({
value: HelperUtils.parseInputInt(evt.target.value),
});
};
render() {
const { value } = this.state;
const { propertyKey, label, maxValue, minValue } = this.props;
return (
<DescriptionPaneEditor
propertyKey={propertyKey}
className="ct-description-pane__editor--number"
>
<DescriptionPaneEditorLabel>{label}</DescriptionPaneEditorLabel>
<DescriptionPaneEditorValue>
<input
className="ct-description-pane__editor-input"
type="number"
min={minValue}
max={maxValue}
value={value === null ? "" : value}
onBlur={this.handleBlur}
onChange={this.handleChange}
/>
</DescriptionPaneEditorValue>
</DescriptionPaneEditor>
);
}
}
@@ -0,0 +1,4 @@
import DescriptionPaneNumberEditor from "./DescriptionPaneNumberEditor";
export default DescriptionPaneNumberEditor;
export { DescriptionPaneNumberEditor };
@@ -0,0 +1,69 @@
import React from "react";
import { Select } from "@dndbeyond/character-components/es";
import {
HelperUtils,
HtmlSelectOption,
} from "@dndbeyond/character-rules-engine/es";
import DescriptionPaneEditor from "../DescriptionPaneEditor";
import DescriptionPaneEditorLabel from "../DescriptionPaneEditorLabel";
import DescriptionPaneEditorValue from "../DescriptionPaneEditorValue";
interface Props {
label: string;
propertyKey: string;
defaultValue: number | null;
options: Array<HtmlSelectOption>;
onUpdate?: (propertyKey: string, value: number | null) => void;
}
interface State {
value: number | null;
}
export default class DescriptionPaneSelectEditor extends React.PureComponent<
Props,
State
> {
constructor(props: Props) {
super(props);
this.state = {
value: props.defaultValue,
};
}
handleChange = (value: string): void => {
const { propertyKey, onUpdate } = this.props;
if (onUpdate) {
const parsedValue = HelperUtils.parseInputInt(value);
onUpdate(propertyKey, parsedValue);
this.setState({
value: parsedValue,
});
}
};
render() {
const { value } = this.state;
const { propertyKey, label, options } = this.props;
return (
<DescriptionPaneEditor
propertyKey={propertyKey}
className="ct-description-pane__editor--select"
>
<DescriptionPaneEditorLabel>{label}</DescriptionPaneEditorLabel>
<DescriptionPaneEditorValue>
<Select
className="ct-description-pane__editor-input"
placeholder="--"
options={options}
value={value}
onChange={this.handleChange}
/>
</DescriptionPaneEditorValue>
</DescriptionPaneEditor>
);
}
}
@@ -0,0 +1,4 @@
import DescriptionPaneSelectEditor from "./DescriptionPaneSelectEditor";
export default DescriptionPaneSelectEditor;
export { DescriptionPaneSelectEditor };
@@ -0,0 +1,48 @@
import React from "react";
import DescriptionPaneEditor from "../DescriptionPaneEditor";
import DescriptionPaneEditorLabel from "../DescriptionPaneEditorLabel";
import DescriptionPaneEditorValue from "../DescriptionPaneEditorValue";
interface Props {
label: string;
propertyKey: string;
defaultValue: string | null;
onUpdate?: (propertyKey: string, value: string) => void;
maxLength: number | null;
}
export default class DescriptionPaneTextEditor extends React.PureComponent<Props> {
static defaultProps = {
maxLength: null,
};
handleBlur = (evt: React.FocusEvent<HTMLInputElement>): void => {
const { propertyKey, onUpdate } = this.props;
if (onUpdate) {
onUpdate(propertyKey, evt.target.value);
}
};
render() {
const { propertyKey, label, defaultValue, maxLength } = this.props;
return (
<DescriptionPaneEditor
propertyKey={propertyKey}
className="ct-description-pane__editor--text"
>
<DescriptionPaneEditorLabel>{label}</DescriptionPaneEditorLabel>
<DescriptionPaneEditorValue>
<input
className="ct-description-pane__editor-input"
type="text"
defaultValue={defaultValue === null ? "" : defaultValue}
onBlur={this.handleBlur}
maxLength={maxLength === null ? undefined : maxLength}
/>
</DescriptionPaneEditorValue>
</DescriptionPaneEditor>
);
}
}
@@ -0,0 +1,4 @@
import DescriptionPaneTextEditor from "./DescriptionPaneTextEditor";
export default DescriptionPaneTextEditor;
export { DescriptionPaneTextEditor };
@@ -0,0 +1,22 @@
import DescriptionPane from "./DescriptionPane";
import DescriptionPaneEditor from "./DescriptionPaneEditor";
import DescriptionPaneEditorLabel from "./DescriptionPaneEditorLabel";
import DescriptionPaneEditorValue from "./DescriptionPaneEditorValue";
import DescriptionPaneEntry from "./DescriptionPaneEntry";
import DescriptionPaneEntryContent from "./DescriptionPaneEntryContent";
import DescriptionPaneNumberEditor from "./DescriptionPaneNumberEditor";
import DescriptionPaneSelectEditor from "./DescriptionPaneSelectEditor";
import DescriptionPaneTextEditor from "./DescriptionPaneTextEditor";
export default DescriptionPane;
export {
DescriptionPane,
DescriptionPaneEditor,
DescriptionPaneEditorLabel,
DescriptionPaneEditorValue,
DescriptionPaneEntry,
DescriptionPaneEntryContent,
DescriptionPaneNumberEditor,
DescriptionPaneSelectEditor,
DescriptionPaneTextEditor,
};