refactor: adjust dropdown typings and provide events for button onClick events
This commit is contained in:
@@ -10,6 +10,8 @@ import { selectAttributeAdjustmentId } from "../../../../slices/characterSlice.t
|
||||
import { changeAttributeAdjustmentId } from "../../../../slices/raceSlice.ts"
|
||||
import "./AttributeAdjustment.scss"
|
||||
|
||||
const getKey = (option: DropdownOption<number>) => option.id
|
||||
|
||||
/**
|
||||
* Returns a widget for changing the attribute adjustment set for the race.
|
||||
*/
|
||||
@@ -48,6 +50,7 @@ export const AttributesAdjustment: FC = () => {
|
||||
disabled={
|
||||
adjustmentOptions.length === 1 && adjustmentOptions[0]!.id === currentAdjustmentId
|
||||
}
|
||||
getKey={getKey}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -12,6 +12,8 @@ type Props = {
|
||||
action: ActionCreatorWithPayload<number>
|
||||
}
|
||||
|
||||
const getKey = (option: DropdownOption<number>) => option.id
|
||||
|
||||
/**
|
||||
* Returns a dropdown for use with personal data.
|
||||
*/
|
||||
@@ -36,6 +38,7 @@ export const PersonalDataDropdown: FC<Props> = props => {
|
||||
onChange={handleSetValue}
|
||||
options={options}
|
||||
disabled={options.length === 1 && value === options[0]!.id}
|
||||
getKey={getKey}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -16,6 +16,8 @@ type Props = {
|
||||
onReroll: () => void
|
||||
}
|
||||
|
||||
const getKey = (option: DropdownOption<number>) => option.id
|
||||
|
||||
/**
|
||||
* Returns a dropdown with a reroll option for use with personal data.
|
||||
*/
|
||||
@@ -40,6 +42,7 @@ export const PersonalDataDropdownWithReroll: FC<Props> = props => {
|
||||
onChange={handleSetValue}
|
||||
options={options}
|
||||
disabled={options.length === 1 && value === options[0]!.id}
|
||||
getKey={getKey}
|
||||
/>
|
||||
<IconButton
|
||||
icon=""
|
||||
|
||||
@@ -229,6 +229,11 @@ export const ProfileOverview: FC = () => {
|
||||
>
|
||||
{translate("Finish Character Creation")}
|
||||
</Button>
|
||||
<p className="help">
|
||||
{translate(
|
||||
"You can save up no more than 10 AP to use later during the game, and you cannot begin the game with a negative AP balance.",
|
||||
)}
|
||||
</p>
|
||||
</GridItem>
|
||||
) : null}
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
import { FC, useCallback } from "react"
|
||||
import { Checkbox } from "../../../../../shared/components/checkbox/Checkbox.tsx"
|
||||
import { Dropdown } from "../../../../../shared/components/dropdown/Dropdown.tsx"
|
||||
import { DropdownOption } from "../../../../../shared/components/dropdown/DropdownItem.tsx"
|
||||
import { IconButton } from "../../../../../shared/components/iconButton/IconButton.tsx"
|
||||
import { OptionalRuleIdentifier } from "../../../../../shared/domain/identifier.ts"
|
||||
import { isOptionalRuleActive } from "../../../../../shared/domain/rules/optionalRule.ts"
|
||||
@@ -21,6 +22,8 @@ type Props = {
|
||||
}
|
||||
}
|
||||
|
||||
const getKey = (option: DropdownOption<number>) => option.id
|
||||
|
||||
/**
|
||||
* Returns a single optional rule item.
|
||||
*/
|
||||
@@ -79,6 +82,7 @@ export const OptionalRulesItem: FC<Props> = props => {
|
||||
value={getDynamicOptionalRuleById(optionalRule.id)?.options?.[0] ?? 2}
|
||||
onChange={handleChangeOption}
|
||||
disabled={!isActive}
|
||||
getKey={getKey}
|
||||
/>
|
||||
) : null}
|
||||
<IconButton
|
||||
|
||||
@@ -20,6 +20,8 @@ type Props = {
|
||||
initialSettings: GlobalSettings
|
||||
}
|
||||
|
||||
const getLocaleKey = (option: DropdownOption<string | undefined>) => option.id
|
||||
|
||||
/**
|
||||
* Root component for the settings window.
|
||||
*/
|
||||
@@ -79,6 +81,7 @@ export const Root: React.FC<Props> = props => {
|
||||
value={locale}
|
||||
label={translate("Main Language")}
|
||||
onChange={setLocale}
|
||||
getKey={getLocaleKey}
|
||||
/>
|
||||
</GridItem>
|
||||
<GridItem width="1/2">
|
||||
@@ -93,6 +96,7 @@ export const Root: React.FC<Props> = props => {
|
||||
value={fallbackLocale}
|
||||
label={translate("Fallback Language")}
|
||||
onChange={setFallbackLocale}
|
||||
getKey={getLocaleKey}
|
||||
/>
|
||||
</GridItem>
|
||||
<GridItem width="1/1">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { forwardRef } from "react"
|
||||
import { MouseEvent, forwardRef } from "react"
|
||||
import { classList } from "../../utils/classList.ts"
|
||||
import { FRRFC } from "../../utils/react.js"
|
||||
import "./Button.scss"
|
||||
@@ -13,7 +13,7 @@ interface Props {
|
||||
hint?: string
|
||||
primary?: boolean
|
||||
round?: boolean
|
||||
onClick?(): void
|
||||
onClick?(event: MouseEvent<HTMLButtonElement>): void
|
||||
}
|
||||
|
||||
const Button: FRRFC<HTMLButtonElement, Props> = (props, ref) => {
|
||||
|
||||
@@ -5,9 +5,9 @@ import { FCC } from "../../utils/react.ts"
|
||||
import { Label } from "../label/Label.tsx"
|
||||
import { Scroll } from "../scroll/Scroll.tsx"
|
||||
import "./Dropdown.scss"
|
||||
import { DropdownItem, DropdownKey, DropdownOption } from "./DropdownItem.tsx"
|
||||
import { DropdownItem, DropdownOption } from "./DropdownItem.tsx"
|
||||
|
||||
type Props<A extends DropdownKey> = {
|
||||
type Props<A> = {
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
fullWidth?: boolean
|
||||
@@ -19,6 +19,8 @@ type Props<A extends DropdownKey> = {
|
||||
values?: A[]
|
||||
onChange?(option: A): void
|
||||
onChangeList?(selected: A[]): void
|
||||
equals?(a: A, b: A): boolean
|
||||
getKey(option: DropdownOption<A>): string | number | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,7 +34,7 @@ export const optionFromIndexed = (name: string, index: number): DropdownOption<n
|
||||
/**
|
||||
* A dropdown with a list of options.
|
||||
*/
|
||||
export const Dropdown = <A extends DropdownKey>(props: Props<A>): ReturnType<FCC<Props<A>>> => {
|
||||
export function Dropdown<A>(props: Props<A>): ReturnType<FCC<Props<A>>> {
|
||||
const {
|
||||
className,
|
||||
disabled,
|
||||
@@ -45,6 +47,8 @@ export const Dropdown = <A extends DropdownKey>(props: Props<A>): ReturnType<FCC
|
||||
required,
|
||||
value,
|
||||
values,
|
||||
equals,
|
||||
getKey,
|
||||
} = props
|
||||
|
||||
const [isOpen, setOpen] = useState(false)
|
||||
@@ -125,11 +129,12 @@ export const Dropdown = <A extends DropdownKey>(props: Props<A>): ReturnType<FCC
|
||||
<ul className="dropdown-options">
|
||||
{options.map(option => (
|
||||
<DropdownItem
|
||||
key={option.id ?? "__DEFAULT__"}
|
||||
key={getKey(option) ?? "__DEFAULT__"}
|
||||
active={value}
|
||||
disabled={normalizedDisabled}
|
||||
onChange={handleChange}
|
||||
option={option}
|
||||
equals={equals}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
import { FC, useCallback } from "react"
|
||||
import { classList } from "../../utils/classList.ts"
|
||||
|
||||
/**
|
||||
* The identifier of a dropdown option.
|
||||
*/
|
||||
export type DropdownKey = string | number | undefined
|
||||
import { deepEqual } from "../../utils/compare.ts"
|
||||
|
||||
/**
|
||||
* Configuration for a dropdown option.
|
||||
*/
|
||||
export type DropdownOption<A extends DropdownKey = DropdownKey> = {
|
||||
export type DropdownOption<A> = {
|
||||
id: A
|
||||
name: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
type Props<A extends DropdownKey> = {
|
||||
type Props<A> = {
|
||||
active: A
|
||||
disabled: boolean
|
||||
option: DropdownOption<A>
|
||||
onChange(option: A): void
|
||||
equals?(a: A, b: A): boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* An item in the list of options of a dropdown.
|
||||
*/
|
||||
export const DropdownItem = <A extends DropdownKey>(props: Props<A>): ReturnType<FC<Props<A>>> => {
|
||||
const { active, disabled, onChange, option } = props
|
||||
export function DropdownItem<A>(props: Props<A>): ReturnType<FC<Props<A>>> {
|
||||
const { active, disabled, onChange, option, equals = deepEqual } = props
|
||||
|
||||
const handleClick = useCallback(
|
||||
() => (disabled || option.disabled === true ? undefined : onChange(option.id)),
|
||||
@@ -36,7 +33,7 @@ export const DropdownItem = <A extends DropdownKey>(props: Props<A>): ReturnType
|
||||
return (
|
||||
<li
|
||||
className={classList({
|
||||
active: option.id === active,
|
||||
active: equals(option.id, active),
|
||||
disabled: option.disabled === true,
|
||||
})}
|
||||
onClick={handleClick}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { forwardRef } from "react"
|
||||
import { MouseEvent, forwardRef } from "react"
|
||||
import { FRRFC } from "../../utils/react.ts"
|
||||
import { Button } from "../button/Button.tsx"
|
||||
import { Icon } from "../icon/Icon.tsx"
|
||||
@@ -14,7 +14,7 @@ type Props = {
|
||||
icon: string
|
||||
label: string
|
||||
primary?: boolean
|
||||
onClick?(): void
|
||||
onClick?(event: MouseEvent<HTMLButtonElement>): void
|
||||
}
|
||||
|
||||
const IconButton: FRRFC<HTMLButtonElement, Props> = (props, ref) => {
|
||||
|
||||
@@ -5,7 +5,7 @@ ul.list-wrapper {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
> li {
|
||||
flex: none;
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -143,7 +143,7 @@ ul.list-wrapper {
|
||||
> div {
|
||||
flex: 1 1 auto;
|
||||
|
||||
&:not(.name) {
|
||||
&:not(:first-child) {
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ ul.list-wrapper {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
&.dropdown.tiers {
|
||||
&.dropdown.level {
|
||||
width: 65px;
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@ ul.list-wrapper {
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
span {
|
||||
font: 500 12px/16px Alegreya Sans;
|
||||
letter-spacing: 0.05em;
|
||||
margin-top: 2px;
|
||||
@@ -208,6 +208,10 @@ ul.list-wrapper {
|
||||
color: var(--headings-color);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.level {
|
||||
margin-left: 0.3em;
|
||||
}
|
||||
}
|
||||
|
||||
.group {
|
||||
@@ -254,7 +258,7 @@ ul.list-wrapper {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
&.dropdown.tiers {
|
||||
&.dropdown.level {
|
||||
width: 65px;
|
||||
}
|
||||
}
|
||||
@@ -406,7 +410,7 @@ ul.list-wrapper {
|
||||
border-color: var(--important-dark-color);
|
||||
}
|
||||
|
||||
p.title,
|
||||
.title,
|
||||
.values .sr {
|
||||
color: var(--important-bright-color);
|
||||
}
|
||||
@@ -421,7 +425,7 @@ ul.list-wrapper {
|
||||
border-color: var(--common-dark-color);
|
||||
}
|
||||
|
||||
p.title,
|
||||
.title,
|
||||
.values .sr {
|
||||
color: var(--common-bright-color);
|
||||
}
|
||||
@@ -436,26 +440,26 @@ ul.list-wrapper {
|
||||
border-color: var(--uncommon-dark-color);
|
||||
}
|
||||
|
||||
p.title,
|
||||
.title,
|
||||
.values .sr {
|
||||
color: var(--uncommon-bright-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
.title {
|
||||
color: var(--separator-color-60);
|
||||
}
|
||||
|
||||
p.title {
|
||||
color: var(--separator-color-30);
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
.values > div {
|
||||
color: var(--separator-color-60);
|
||||
}
|
||||
}
|
||||
|
||||
&.placeholder {
|
||||
pointer-events: none;
|
||||
|
||||
p.title {
|
||||
.title {
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
@@ -479,7 +483,7 @@ ul.list-wrapper {
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
p.title {
|
||||
.title {
|
||||
width: 148px;
|
||||
}
|
||||
|
||||
@@ -491,7 +495,7 @@ ul.list-wrapper {
|
||||
&:nth-child(2) {
|
||||
opacity: 0.8;
|
||||
|
||||
p.title {
|
||||
.title {
|
||||
width: 111px;
|
||||
}
|
||||
|
||||
@@ -503,7 +507,7 @@ ul.list-wrapper {
|
||||
&:nth-child(3) {
|
||||
opacity: 0.6;
|
||||
|
||||
p.title {
|
||||
.title {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
@@ -515,7 +519,7 @@ ul.list-wrapper {
|
||||
&:nth-child(4) {
|
||||
opacity: 0.4;
|
||||
|
||||
p.title {
|
||||
.title {
|
||||
width: 138px;
|
||||
}
|
||||
|
||||
@@ -527,7 +531,7 @@ ul.list-wrapper {
|
||||
&:nth-child(5) {
|
||||
opacity: 0.2;
|
||||
|
||||
p.title {
|
||||
.title {
|
||||
width: 222px;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ export const ListItemName: FCC<Props> = props => {
|
||||
return (
|
||||
<div className={classList("name", { large: large !== undefined })} onClick={onClick}>
|
||||
{addName === undefined ? (
|
||||
<p className="title">{name}</p>
|
||||
<span className="title">{name}</span>
|
||||
) : (
|
||||
<p className="title">
|
||||
<span className="title">
|
||||
<span>{name}</span>
|
||||
<span className="add">{addName}</span>
|
||||
</p>
|
||||
</span>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user