Skip to content
100 changes: 61 additions & 39 deletions app/components/pages/utilities/dice-roller/generic-dice-roller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ import type { MouseEvent } from 'react';
import { Fragment, useState, useTransition } from 'react';
import DiceRollCounterBadge from '~/components/pages/utilities/dice-roller/helpers/dice-roll-counter.badge';
import DiceText from '~/components/pages/utilities/dice-roller/helpers/dice-text';
import {
NumberField,
NumberFieldDecrement,
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
NumberFieldScrubArea,
} from '~/components/reui/number-field';
import { Button } from '~/components/ui/button';
import { ButtonGroup } from '~/components/ui/button-group';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '~/components/ui/card';
import { Dialog, DialogContent, DialogHeader } from '~/components/ui/dialog';
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from '~/components/ui/input-group';
import { InputGroupAddon, InputGroupButton } from '~/components/ui/input-group';
import { Popover, PopoverContent, PopoverTrigger } from '~/components/ui/popover';
import { Separator } from '~/components/ui/separator';
import { Tooltip, TooltipContent, TooltipTrigger } from '~/components/ui/tooltip';
Expand Down Expand Up @@ -132,52 +140,66 @@ export default function GenericDiceRoller() {
})}
</ButtonGroup>

<div className="flex flex-col items-center gap-2 sm:flex-row">
<InputGroup className="max-w-40 flex-1">
<Popover>
<PopoverTrigger
nativeButton={false}
render={
<InputGroupAddon>
<InputGroupButton
variant="secondary"
size="icon-xs"
className="rounded-full p-1!"
>
<InfoIcon aria-hidden />
</InputGroupButton>
</InputGroupAddon>
}
<div className="flex flex-col items-center gap-2 md:flex-row">
<NumberField
id="modifier-input"
name="modifier-input"
aria-label="Modifier input"
className="flex-1 md:max-w-44"
value={modifier}
min={-100}
max={100}
step={1}
onValueChange={(value) => setModifier(Number(value))}
>
<NumberFieldGroup>
<Popover>
<PopoverTrigger
nativeButton={false}
render={
<InputGroupAddon>
<InputGroupButton
variant="secondary"
size="icon-xs"
className="rounded-full p-1!"
>
<InfoIcon aria-hidden />
</InputGroupButton>
</InputGroupAddon>
}
/>
<PopoverContent align="start" className="flex flex-col gap-1 rounded-xl text-sm">
<p className="font-medium">Modifiers</p>
<p>
Use these modifiers to adjust your dice rolls. You can add or subtract
values as needed. Check your game rules for more details.
</p>
</PopoverContent>
</Popover>
<NumberFieldScrubArea
className="pl-1.5 text-muted-foreground"
labelClassName="font-sans"
label="Mod."
/>
<PopoverContent align="start" className="flex flex-col gap-1 rounded-xl text-sm">
<p className="font-medium">Modifiers</p>
<p>
Use these modifiers to adjust your dice rolls. You can add or subtract values as
needed. Check your game rules for more details.
</p>
</PopoverContent>
</Popover>
<InputGroupAddon className="pl-1.5 text-muted-foreground">Mod.</InputGroupAddon>
<InputGroupInput
id="modifier-input"
name="modifier-input"
aria-label="Modifier input"
type="number"
inputMode="numeric"
value={modifier}
step={1}
onChange={(e) => setModifier(Number(e.target.value))}
/>
</InputGroup>
<NumberFieldInput className="md:text-left" />
<NumberFieldDecrement className="rounded-none!" />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>

<Separator orientation={isMobile ? 'horizontal' : 'vertical'} />

<ButtonGroup>
<Button disabled={selectedDice.length === 0 || isPending} onClick={handleDiceRoll}>
<ButtonGroup className="w-full">
<Button
className="flex-1"
disabled={selectedDice.length === 0 || isPending}
onClick={handleDiceRoll}
>
<PlayIcon aria-hidden /> Roll!
</Button>
<Button
variant="secondary"
className="flex-1"
onClick={handleReset}
disabled={selectedDice.length === 0 || isPending}
>
Expand Down
85 changes: 51 additions & 34 deletions app/components/pages/utilities/dice-roller/vtm-dice-roller.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,43 @@
import { Checkbox } from '@base-ui/react/checkbox';
import { zodResolver } from '@hookform/resolvers/zod';
import { XIcon } from 'lucide-react';
import { floor } from 'mathjs';
import { type BaseSyntheticEvent, Fragment, useCallback, useState, useTransition } from 'react';
import { Controller, type Resolver, useForm } from 'react-hook-form';
import { Link } from 'react-router';
import { z } from 'zod/v4';
import VtmAlert from '~/components/pages/utilities/dice-roller/vtm-alert';
import {
NumberField,
NumberFieldDecrement,
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
} from '~/components/reui/number-field';
import {
ActionBar,
ActionBarClose,
ActionBarGroup,
ActionBarItem,
ActionBarSelection,
ActionBarSeparator,
} from '~/components/ui/action-bar';
import { Button } from '~/components/ui/button';
import { ButtonGroup } from '~/components/ui/button-group';
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '~/components/ui/card';
import { Field, FieldError, FieldGroup, FieldLabel } from '~/components/ui/field';
import { Input } from '~/components/ui/input';
import useIsMobile from '~/hooks/use-mobile';
import { rollDie } from '~/lib/dice-roller.utils';
import { cn } from '~/lib/utils';

// Dice import
import { Checkbox } from '@base-ui/react/checkbox';
import { XIcon } from 'lucide-react';
import hungerDieBestialFailure from '~/assets/dice/vtm/Dice_Hunger_BestialFailure.png';
import hungerDieFailure from '~/assets/dice/vtm/Dice_Hunger_Failure.png';
import hungerDieMessyCritical from '~/assets/dice/vtm/Dice_Hunger_MessyCritical.png';
import hungerDieSuccess from '~/assets/dice/vtm/Dice_Hunger_Success.png';
import regularDieCritical from '~/assets/dice/vtm/Dice_Regular_Critical.png';
import regularDieFailure from '~/assets/dice/vtm/Dice_Regular_Failure.png';
import regularDieSuccess from '~/assets/dice/vtm/Dice_Regular_Success.png';
import {
ActionBar,
ActionBarClose,
ActionBarGroup,
ActionBarItem,
ActionBarSelection,
ActionBarSeparator,
} from '~/components/ui/action-bar';
import { cn } from '~/lib/utils';

type DieResult = {
id: string;
Expand Down Expand Up @@ -328,17 +334,21 @@ export default function VtmDiceRoller() {
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid} className="relative">
<FieldLabel htmlFor={field.name}>Regular Dice</FieldLabel>
<Input
type="number"
<NumberField
id={field.name}
name={field.name}
min={1}
max={10}
step={1}
inputMode="decimal"
placeholder="e.g. 5"
aria-invalid={!!form.formState.errors.regularDice}
{...field}
/>
value={field.value}
onValueChange={(value) => field.onChange(value)}
>
<NumberFieldGroup aria-invalid={!!form.formState.errors.regularDice}>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
{fieldState.invalid && (
<FieldError
className="absolute rounded-md bg-red-100 px-2 py-1 max-sm:-bottom-6 sm:-top-8"
Expand All @@ -355,17 +365,21 @@ export default function VtmDiceRoller() {
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid} className="relative">
<FieldLabel htmlFor={field.name}>Hunger Dice</FieldLabel>
<Input
type="number"
<NumberField
id={field.name}
name={field.name}
min={0}
max={5}
step={1}
inputMode="decimal"
placeholder="e.g. 3"
aria-invalid={!!form.formState.errors.hungerDice}
{...field}
/>
value={field.value}
onValueChange={(value) => field.onChange(value)}
>
<NumberFieldGroup aria-invalid={!!form.formState.errors.hungerDice}>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
{fieldState.invalid && (
<FieldError
className="absolute rounded-md bg-red-100 px-2 py-1 max-sm:-bottom-6 sm:-top-8"
Expand All @@ -382,18 +396,21 @@ export default function VtmDiceRoller() {
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid} className="relative">
<FieldLabel htmlFor={field.name}>Difficulty</FieldLabel>
<Input
type="number"
<NumberField
id={field.name}
name={field.name}
min={1}
max={10}
step={1}
inputMode="decimal"
placeholder="e.g. 5"

aria-invalid={!!form.formState.errors.difficulty}
{...field}
/>
value={field.value}
onValueChange={(value) => field.onChange(value)}
>
<NumberFieldGroup aria-invalid={!!form.formState.errors.difficulty}>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
{fieldState.invalid && (
<FieldError
className="absolute rounded-md bg-red-100 px-2 py-1 max-sm:-bottom-6 sm:-top-8"
Expand Down
Loading