D
nD Char Roll

A highly customizable DnD character generator
View on Github
React
TypeScript

About

DnD Char Roll Character
A highly customizable DnD character generator using React18, Stitches, and Radix-UI primitives, capable of creating up to 100 characters in under 1 second. Features include name generation with randomized seed, backgrounds, adjustable stats based on parent comparison, sex and age settings, and CSV export with customizable header order.

Snippets

1
export const CharacterContextProvider = (props: CharacterContextProviderProps) => {
2
const { minAge = 1, maxAge = 60, statRoll = "4d6b3", statRange, value, children } = props;
3
4
const [sex, setSex] = useState(value?.sex ?? (Math.round(Math.random()) as Sex));
5
const [age, setAge] = useState(value?.age ?? randomInt(minAge, maxAge));
6
const [background, setBackground] = useState(
7
value?.background ?? backgrounds[randomInt(0, backgrounds.length - 1)]
8
);
9
10
const rollStats = useMemo(() => (input: string) => {
11
// ... (rollStats implementation)
12
}, [statRange]);
13
14
const rerollStats = useMemo(() => () => setStats(rollStats(statRoll)), [statRoll, rollStats]);
15
const [stats, setStats] = useState<ReturnType<typeof rollStats> | number[]>(
16
() => value?.stats ?? rollStats(statRoll)
17
);
18
19
const [name, dispatchName] = useReducer(NameReducer.reducer, {
20
firstName: value?.firstName ?? properNoun(gName.first()),
21
lastName: value?.lastName ?? properNoun(gName.last()),
22
gen: gName,
23
});
24
25
const char = useMemo(() => ({
26
// ... (character object construction)
27
}), [sex, age, name, stats, background]);
28
29
const state: State = useMemo(() => ({
30
sex, setSex, age, setAge, background, setBackground,
31
stats, rerollStats, name, dispatchName, char,
32
}), [age, background, char, name, rerollStats, sex, stats]);
33
34
useUpdateEffect(() => {
35
if (value && value !== char) {
36
// ... (update logic)
37
}
38
}, [value]);
39
40
return <CharacterProvider value={state}>{children}</CharacterProvider>;
41
};
This snippet showcases the CharacterContextProvider, which is the core of the character generation logic. It demonstrates advanced React patterns including comprehensive use of hooks (useState, useMemo, useReducer, custom hooks), Context API for state management, memoization for performance optimization, and derived state calculations. The provider manages all aspects of a character, from basic attributes like sex and age to more complex elements like stats and name generation.
1
const CharacterGenPage = () => {
2
const { amount, minAge, maxAge, statRoll, advStatSettings, statsRange, characters, setCharacters, id } = useGenSettings();
3
4
const createGenForms = useCallback((charList: typeof characters) => {
5
let forms: JSX.Element[] = [];
6
for (let ind = 0; ind < amount; ind++) {
7
forms.push(
8
<CharacterAccordionItem
9
minAge={minAge}
10
maxAge={maxAge}
11
statRoll={statRoll}
12
statRange={statsRange}
13
id={`char${ind}`}
14
key={`-${id}-charAccord${ind}`}
15
index={ind}
16
value={charList[ind]}
17
onChange={(i, char) => {
18
const cA = characters;
19
if (cA[i]) {
20
cA.splice(i, 1, char);
21
} else {
22
cA.push(char);
23
}
24
setCharacters(cA);
25
}}
26
/>
27
);
28
}
29
return forms;
30
}, [amount, characters, maxAge, minAge, setCharacters, statRoll, statsRange, advStatSettings, id]);
31
32
const [genForms, setGenForms] = useState(createGenForms(characters));
33
34
useEffect(() => {
35
const cA = characters;
36
if (cA.length > amount - 1) {
37
cA.splice(amount - 1);
38
setCharacters(cA);
39
}
40
setGenForms(createGenForms(cA));
41
}, [amount, characters, createGenForms, setCharacters, statRoll, statsRange, id]);
42
43
return (
44
<>
45
<CharacterGenSettings />
46
<Card css={{ backgroundColor: "transparent", padding: "0" }}>
47
<Accordion type="multiple">{genForms}</Accordion>
48
</Card>
49
</>
50
);
51
};
The CharacterGenPage component illustrates sophisticated React techniques for managing multiple character generations. It uses useCallback for memoizing the complex createGenForms function, which dynamically generates CharacterAccordionItem components based on the current settings. The useEffect hook ensures efficient updates when the number of characters or other key settings change. This component showcases how to handle dynamic component generation and state management in a performant way.
1
const CharacterGenSettings = () => {
2
const ctx = useGenSettings();
3
const theme = useTheme();
4
5
const [amount, setAmount] = useState(ctx.amount);
6
const [minAge, setMinAge] = useState(ctx.minAge);
7
const [maxAge, setMaxAge] = useState(ctx.maxAge);
8
const [statRoll, setStatRoll] = useState(ctx.statRoll);
9
const [advStatSettings, setAdvStatSettings] = useState(ctx.advStatSettings);
10
const [parentAStats, setParentAStats] = useState(ctx.parentAStats);
11
const [parentBStats, setParentBStats] = useState(ctx.parentBStats);
12
13
const [characters, setCharacters] = useState(ctx.characters);
14
useEffect(() => {
15
if (ctx.characters !== characters)
16
setCharacters(Array.from(ctx.characters));
17
}, [ctx.characters, characters]);
18
19
const Dialogs = useMemo(
20
() => (
21
<>
22
<Dialog title="Edit 1st Parent" description={dlgDesc} key={"pEdA"}>
23
<DialogTrigger asChild>
24
<IconButton
25
disabled={!advStatSettings}
26
leftIcon={PersonIcon}
27
text={"Edit 1st Parent"}
28
key={"pEdA-btn"}
29
/>
30
</DialogTrigger>
31
<ParentStatsForm
32
stats={parentAStats}
33
onChange={(v) => setParentAStats(v)}
34
key={"pSFA"}
35
/>
36
<DialogClose asChild>
37
<SaveChangesBtn
38
leftIcon={CheckCircledIcon}
39
text={"Save Changes"}
40
key={"pEdA-save"}
41
/>
42
</DialogClose>
43
</Dialog>
44
{/* Similar dialog for 2nd Parent */}
45
</>
46
),
47
[advStatSettings, parentAStats, parentBStats]
48
);
49
50
return (
51
<>
52
<Card>
53
{/* Settings UI */}
54
</Card>
55
<Card style={{ maxWidth: "90%" }}>
56
{/* CSV export UI */}
57
</Card>
58
</>
59
);
60
};
CharacterGenSettings demonstrates complex state management in a settings interface. It uses multiple useState hooks to manage various settings, useEffect for syncing local and global state, and useMemo for optimizing the rendering of complex UI elements like dialogs. The component also showcases the integration of custom UI components and dialogs, providing a rich and interactive settings experience for the user.