``` ~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js ```
272 lines
7.3 KiB
TypeScript
272 lines
7.3 KiB
TypeScript
import * as React from "react";
|
|
|
|
import {
|
|
Collapsible,
|
|
CollapsibleHeaderCallout,
|
|
CollapsibleHeaderContent,
|
|
} from "@dndbeyond/character-components/es";
|
|
import {
|
|
HelperUtils,
|
|
VehicleFuelData,
|
|
} from "@dndbeyond/character-rules-engine/es";
|
|
|
|
import { QuickActions } from "~/subApps/sheet/components/Sidebar/components/QuickActions/QuickActions";
|
|
|
|
import SimpleQuantity from "../SimpleQuantity";
|
|
import { ThemeButton } from "../common/Button";
|
|
|
|
interface Props {
|
|
fuelData: VehicleFuelData;
|
|
onChange: (value: number) => void;
|
|
initiallyCollapsed: boolean;
|
|
isInteractive: boolean;
|
|
defaultAdjusterValue: number;
|
|
}
|
|
interface State {
|
|
remainingFuel: number | null;
|
|
adjusterValue: number;
|
|
}
|
|
export default class VehicleFuelTracker extends React.PureComponent<
|
|
Props,
|
|
State
|
|
> {
|
|
static defaultProps = {
|
|
initiallyCollapsed: true,
|
|
isInteractive: false,
|
|
defaultAdjusterValue: 1,
|
|
};
|
|
|
|
remainingFuelInput = React.createRef<HTMLInputElement>();
|
|
|
|
constructor(props: Props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
remainingFuel: props.fuelData.remainingFuel,
|
|
adjusterValue: props.defaultAdjusterValue,
|
|
};
|
|
}
|
|
|
|
componentDidUpdate(prevProps: Props, prevState: State): void {
|
|
const { fuelData } = this.props;
|
|
|
|
if (fuelData.remainingFuel !== prevProps.fuelData.remainingFuel) {
|
|
this.setState({
|
|
remainingFuel: fuelData.remainingFuel,
|
|
});
|
|
}
|
|
}
|
|
|
|
handleRemainingFuelSet = (newValue: number): void => {
|
|
const { onChange, fuelData } = this.props;
|
|
|
|
if (fuelData.remainingFuel !== newValue && onChange) {
|
|
onChange(newValue);
|
|
}
|
|
|
|
this.setState({
|
|
remainingFuel: newValue,
|
|
});
|
|
};
|
|
|
|
handleRemainingFuelInputBlur = (
|
|
evt: React.FocusEvent<HTMLInputElement>
|
|
): void => {
|
|
this.handleRemainingFuelSet(HelperUtils.parseInputInt(evt.target.value, 0));
|
|
};
|
|
|
|
handleRemainingFuelInputChange = (
|
|
evt: React.ChangeEvent<HTMLInputElement>
|
|
): void => {
|
|
this.setState({
|
|
remainingFuel: HelperUtils.parseInputInt(evt.target.value),
|
|
});
|
|
};
|
|
|
|
updateAdjusterValue = (newValue: number): void => {
|
|
const { adjusterValue } = this.state;
|
|
|
|
if (newValue !== adjusterValue) {
|
|
this.setState({
|
|
adjusterValue: newValue,
|
|
});
|
|
}
|
|
};
|
|
|
|
resetAdjusterValue = (): void => {
|
|
const { defaultAdjusterValue } = this.props;
|
|
const { adjusterValue } = this.state;
|
|
|
|
if (adjusterValue !== defaultAdjusterValue) {
|
|
this.setState({
|
|
adjusterValue: defaultAdjusterValue,
|
|
});
|
|
}
|
|
};
|
|
|
|
handleAdjusterUse = (): void => {
|
|
const { adjusterValue, remainingFuel } = this.state;
|
|
|
|
let remainingFuelValue: number = remainingFuel === null ? 0 : remainingFuel;
|
|
this.handleRemainingFuelSet(
|
|
Math.max(remainingFuelValue - adjusterValue, 0)
|
|
);
|
|
this.resetAdjusterValue();
|
|
};
|
|
|
|
handleAdjusterAdd = (): void => {
|
|
const { adjusterValue, remainingFuel } = this.state;
|
|
const { fuelData } = this.props;
|
|
|
|
let remainingFuelValue: number = remainingFuel === null ? 0 : remainingFuel;
|
|
let value: number = adjusterValue + remainingFuelValue;
|
|
|
|
if (fuelData.maxAmount !== null) {
|
|
value = Math.max(value, fuelData.maxAmount);
|
|
}
|
|
|
|
this.handleRemainingFuelSet(value);
|
|
this.resetAdjusterValue();
|
|
};
|
|
|
|
handleQuickAction = (value: number): void => {
|
|
const { fuelData } = this.props;
|
|
const { remainingFuel } = this.state;
|
|
|
|
let remainingFuelValue: number = remainingFuel === null ? 0 : remainingFuel;
|
|
let newValue: number = remainingFuelValue + value;
|
|
|
|
if (fuelData.maxAmount !== null) {
|
|
newValue = Math.max(newValue, fuelData.maxAmount);
|
|
}
|
|
|
|
this.handleRemainingFuelSet(newValue);
|
|
};
|
|
|
|
renderRemainingFuelInput = (): React.ReactNode => {
|
|
const { remainingFuel } = this.state;
|
|
const { fuelData, isInteractive } = this.props;
|
|
|
|
return (
|
|
<div className="ct-vehicle-fuel-tracker__primary">
|
|
<div className="ct-vehicle-fuel-tracker__primary-label">
|
|
Fuel remaining (HRS)
|
|
</div>
|
|
<div className="ct-vehicle-fuel-tracker__primary-value">
|
|
<input
|
|
ref={this.remainingFuelInput}
|
|
className="ct-vehicle-fuel-tracker__primary-input"
|
|
type="number"
|
|
value={remainingFuel !== null ? remainingFuel : ""}
|
|
onBlur={this.handleRemainingFuelInputBlur}
|
|
onChange={this.handleRemainingFuelInputChange}
|
|
min={0}
|
|
max={fuelData.maxAmount !== null ? fuelData.maxAmount : undefined}
|
|
readOnly={!isInteractive}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
renderSimpleQuantityAdjuster = (): React.ReactNode => {
|
|
const { adjusterValue } = this.state;
|
|
const { isInteractive } = this.props;
|
|
|
|
return (
|
|
<div className="ct-vehicle-fuel-tracker__adjuster">
|
|
<div className="ct-vehicle-fuel-tracker__adjuster-simple-quantity">
|
|
<SimpleQuantity
|
|
label={""}
|
|
quantity={adjusterValue}
|
|
minimum={1}
|
|
maximum={null}
|
|
onUpdate={this.updateAdjusterValue}
|
|
isReadonly={!isInteractive}
|
|
/>
|
|
</div>
|
|
<div className="ct-vehicle-fuel-tracker__adjuster-actions">
|
|
<ThemeButton
|
|
size="small"
|
|
style="outline"
|
|
className="ct-vehicle-fuel-tracker__adjuster-button ct-vehicle-fuel-tracker__adjuster-button--use"
|
|
onClick={this.handleAdjusterUse}
|
|
isInteractive={isInteractive}
|
|
>
|
|
Use
|
|
</ThemeButton>
|
|
<ThemeButton
|
|
size="small"
|
|
className="ct-vehicle-fuel-tracker__adjuster-button ct-vehicle-fuel-tracker__adjuster-button--add"
|
|
onClick={this.handleAdjusterAdd}
|
|
isInteractive={isInteractive}
|
|
>
|
|
Add
|
|
</ThemeButton>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
renderQuickActions = (): React.ReactNode => {
|
|
const { fuelData, isInteractive } = this.props;
|
|
|
|
if (fuelData.fuelItems === null) {
|
|
return null;
|
|
}
|
|
|
|
const actions = [] as {
|
|
label: string;
|
|
onClick: () => void;
|
|
disabled: boolean;
|
|
}[];
|
|
fuelData.fuelItems.forEach((item) => {
|
|
actions.push({
|
|
label: `Add ${item.amountPerCharge} HRS`,
|
|
onClick: this.handleQuickAction.bind(this, item.amountPerCharge),
|
|
disabled: !isInteractive,
|
|
});
|
|
});
|
|
|
|
return <QuickActions actions={actions}></QuickActions>;
|
|
};
|
|
|
|
render() {
|
|
const { fuelData, initiallyCollapsed } = this.props;
|
|
|
|
let extraNode: React.ReactNode = (
|
|
<div className="ct-vehicle-fuel-tracker__summary">
|
|
<span className="ct-vehicle-fuel-tracker__summary-value">
|
|
{fuelData.remainingFuel} hrs
|
|
</span>
|
|
</div>
|
|
);
|
|
|
|
let headerCalloutNode: React.ReactNode = (
|
|
<CollapsibleHeaderCallout extra={extraNode} value={null} />
|
|
);
|
|
|
|
let headerNode: React.ReactNode = (
|
|
<CollapsibleHeaderContent
|
|
heading="Fuel Management"
|
|
callout={headerCalloutNode}
|
|
/>
|
|
);
|
|
|
|
return (
|
|
<div className="ct-vehicle-fuel-tracker">
|
|
<Collapsible
|
|
header={headerNode}
|
|
initiallyCollapsed={initiallyCollapsed}
|
|
>
|
|
<div className="ct-vehicle-fuel-tracker__content">
|
|
{this.renderRemainingFuelInput()}
|
|
{this.renderSimpleQuantityAdjuster()}
|
|
{this.renderQuickActions()}
|
|
</div>
|
|
</Collapsible>
|
|
</div>
|
|
);
|
|
}
|
|
}
|