Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow loadout naming #219

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions src/app/components/player/LoadoutName.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { IconPencil } from '@tabler/icons-react';
import React, { useCallback, useState } from 'react';

interface LoadoutNameProps {
name: string;
index: number;
renameLoadout: (index: number, name: string) => void;
}

interface LoadoutNameEditContainerProps {
value: string;
onSubmit: () => void;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

const LoadoutNameEditContainer: React.FC<LoadoutNameEditContainerProps> = (props) => {
const { value, onSubmit, onChange } = props;

const focusInput = useCallback((input: HTMLInputElement | null) => {
input?.focus();
}, []);

return (
<form className="flex gap-2 items-center w-full" onSubmit={onSubmit}>
<input type="text" className="form-control" pattern=".*\S.*" title="Must contain at least one non-whitespace character" ref={focusInput} value={value} onChange={onChange} maxLength={24} onBlur={onSubmit} />
</form>
);
};

const LoadoutName: React.FC<LoadoutNameProps> = ({ name, index, renameLoadout }) => {
const [editName, setEditName] = useState(false);
const [value, setValue] = useState(name);
const [prevIndex, setPrevIndex] = useState(index);

// Resets form state when selected loadout changes
// https://react.dev/learn/you-might-not-need-an-effect#resetting-all-state-when-a-prop-changes
if (index !== prevIndex) {
setPrevIndex(index);
setEditName(false);
}

const onEdit = () => {
setValue(name);
setEditName(true);
};

const onSubmit = () => {
if (value) {
renameLoadout(index, value);
}
setEditName(false);
};

return editName ? (
<LoadoutNameEditContainer onSubmit={onSubmit} onChange={(e) => setValue(e.target.value)} value={value} />
) : (
<div className="flex gap-2 items-center mr-8">
<h2 className="tracking-tight font-bold overflow-hidden min-w-0 whitespace-nowrap">
{name}
</h2>
<button
type="button"
onClick={onEdit}
className="disabled:cursor-not-allowed text-body-500 dark:text-dark-100 disabled:text-btns-100 dark:disabled:text-dark-500 hover:text-red transition-colors flex-none"
data-tooltip-id="tooltip"
data-tooltip-content="Rename loadout"
>
<IconPencil aria-label="Rename loadout" size={18} />
</button>
</div>
);
};

export default LoadoutName;
11 changes: 4 additions & 7 deletions src/app/components/player/PlayerContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { observer } from 'mobx-react-lite';
import { useStore } from '@/state';
import { calculateCombatLevel } from '@/utils';
import PlayerInnerContainer from '@/app/components/player/PlayerInnerContainer';
import LoadoutName from '@/app/components/player/LoadoutName';
import { IconPlus, IconTrash } from '@tabler/icons-react';

const PlayerContainer: React.FC = observer(() => {
const store = useStore();
const {
loadouts, player, selectedLoadout, canCreateLoadout, createLoadout, deleteLoadout,
loadouts, player, selectedLoadout, canCreateLoadout, createLoadout, renameLoadout, deleteLoadout,
} = store;

return (
Expand Down Expand Up @@ -50,12 +51,8 @@ const PlayerContainer: React.FC = observer(() => {
<div
className="px-5 py-3 border-b-body-400 dark:border-b-dark-200 border-b flex justify-between items-center font-serif"
>
<div>
<h2 className="tracking-tight font-bold">
Loadout
{' '}
{selectedLoadout + 1}
</h2>
<div className="min-w-0">
<LoadoutName name={loadouts[selectedLoadout].name} renameLoadout={renameLoadout} index={selectedLoadout} />
<div className="text-xs font-bold text-gray-500 dark:text-gray-300">
Level
{' '}
Expand Down
8 changes: 8 additions & 0 deletions src/app/components/player/equipment/EquipmentPresets.tsx
NickKoester marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const EquipmentPresets: React.FC = () => {
switch (v?.value) {
case EquipmentPreset.DHAROKS: {
newPlayer = {
name: v.label,
equipment: {
head: findItemById(4716), // Dharok's helm
cape: findItemById(21295), // Infernal cape
Expand All @@ -46,6 +47,7 @@ const EquipmentPresets: React.FC = () => {
}
case EquipmentPreset.MAX_MAGE: {
newPlayer = {
name: v.label,
equipment: {
head: findItemById(21018), // Ancestral hat
cape: findItemById(21791), // Imbued saradomin cape
Expand All @@ -62,6 +64,7 @@ const EquipmentPresets: React.FC = () => {
}
case EquipmentPreset.MAX_MELEE: {
newPlayer = {
name: v.label,
equipment: {
head: findItemById(26382), // Torva full helm
cape: findItemById(21295), // Infernal cape
Expand All @@ -78,6 +81,7 @@ const EquipmentPresets: React.FC = () => {
}
case EquipmentPreset.MAX_RANGED: {
newPlayer = {
name: v.label,
equipment: {
head: findItemById(27235), // Masori mask (f)
cape: findItemById(22109), // Ava's assembler
Expand All @@ -94,6 +98,7 @@ const EquipmentPresets: React.FC = () => {
}
case EquipmentPreset.VOID_MAGE: {
newPlayer = {
name: v.label,
equipment: {
head: findItemById(11663), // Void mage helm
cape: findItemById(21791), // Imbued saradomin cape
Expand All @@ -110,6 +115,7 @@ const EquipmentPresets: React.FC = () => {
}
case EquipmentPreset.VOID_MELEE: {
newPlayer = {
name: v.label,
equipment: {
head: findItemById(11665), // Void melee helm
cape: findItemById(21295), // Infernal cape
Expand All @@ -126,6 +132,7 @@ const EquipmentPresets: React.FC = () => {
}
case EquipmentPreset.VOID_RANGED: {
newPlayer = {
name: v.label,
equipment: {
head: findItemById(11664), // Void ranger helm
cape: findItemById(22109), // Ava's assembler
Expand All @@ -142,6 +149,7 @@ const EquipmentPresets: React.FC = () => {
}
case EquipmentPreset.VERACS: {
newPlayer = {
name: v.label,
equipment: {
head: findItemById(4753), // Verac's helm
cape: findItemById(21295), // Infernal cape
Expand Down
6 changes: 3 additions & 3 deletions src/app/components/results/LoadoutComparison.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,9 @@ const LoadoutComparison: React.FC = observer(() => {
const lines: { name: number, [lKey: string]: string | number }[] = [];
for (const input of inputRange(x, loadouts, monster)) {
const entry: typeof lines[0] = { name: input.xValue };
for (const [i, l] of input.loadouts.entries()) {
for (const [, l] of input.loadouts.entries()) {
const v = getOutput(y, l, input.monster);
entry[`Loadout ${i + 1}`] = v.toFixed(2);
entry[l.name] = v.toFixed(2);
maximum = Math.max(maximum, v);
min = Math.min(min, v);
}
Expand Down Expand Up @@ -348,7 +348,7 @@ const LoadoutComparison: React.FC = observer(() => {
lines.push(<Line
key={i}
type="monotone"
dataKey={`Loadout ${i + 1}`}
dataKey={loadouts[i].name}
stroke={colour}
dot={false}
isAnimationActive={false}
Expand Down
6 changes: 3 additions & 3 deletions src/app/components/results/ResultsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ const ResultsTable: React.FC = observer(() => {
<thead>
<tr>
<th aria-label="blank" className="bg-btns-400 border-r dark:bg-dark-500 select-none" />
{store.loadouts.map((_, i) => (
{store.loadouts.map(({ name }, i) => (
<th
// eslint-disable-next-line react/no-array-index-key
key={i}
className={`text-center w-28 border-r font-bold font-serif cursor-pointer transition-colors ${selectedLoadout === i ? 'bg-orange-400 dark:bg-orange-700' : 'bg-btns-400 dark:bg-dark-500'}`}
className={`text-center w-28 border-r font-bold font-serif leading-tight cursor-pointer transition-colors ${selectedLoadout === i ? 'bg-orange-400 dark:bg-orange-700' : 'bg-btns-400 dark:bg-dark-500'}`}
onClick={() => store.setSelectedLoadout(i)}
>
{i + 1}
{name}
</th>
))}
</tr>
Expand Down
9 changes: 5 additions & 4 deletions src/app/components/results/TtkComparison.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ const TtkComparison: React.FC = observer(() => {
const store = useStore();
const { showTtkComparison } = store.prefs;
const calcResults = toJS(store.calc.loadouts);
const loadouts = toJS(store.loadouts);

const { resolvedTheme } = useTheme();
const isDark = resolvedTheme === 'dark';
Expand All @@ -103,12 +104,12 @@ const TtkComparison: React.FC = observer(() => {
if (v) {
runningTotals[i] = (runningTotals[i] || 0) + v;
}
entry[`Loadout ${i + 1}`] = (runningTotals[i] * 100).toFixed(2);
entry[loadouts[i].name] = (runningTotals[i] * 100).toFixed(2);
});
lines.push(entry);
}
return lines;
}, [xAxisType, calcResults]);
}, [xAxisType, calcResults, loadouts]);

const generateLines = useCallback(() => {
const lines: React.ReactNode[] = [];
Expand All @@ -118,11 +119,11 @@ const TtkComparison: React.FC = observer(() => {
: ['blue', 'chocolate', 'green', 'sienna', 'purple'];
for (let i = 0; i < calcResults.length; i++) {
const colour = strokeColours.shift() || 'red';
lines.push(<Line key={i} isAnimationActive={false} type="monotone" dataKey={`Loadout ${i + 1}`} stroke={colour} dot={false} connectNulls />);
lines.push(<Line key={i} isAnimationActive={false} type="monotone" dataKey={loadouts[i].name} stroke={colour} dot={false} connectNulls />);
strokeColours.push(colour);
}
return lines;
}, [isDark, calcResults.length]);
}, [isDark, calcResults.length, loadouts]);

return (
<SectionAccordion
Expand Down
20 changes: 18 additions & 2 deletions src/state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ const generateInitialEquipment = () => {
return initialEquipment;
};

export const generateEmptyPlayer: () => Player = () => ({
export const generateEmptyPlayer: (name?: string) => Player = (name?: string) => ({
NickKoester marked this conversation as resolved.
Show resolved Hide resolved
name: name ?? 'Loadout 1',
username: '',
style: getCombatStylesForCategory(EquipmentCategory.NONE)[0],
skills: {
Expand Down Expand Up @@ -352,6 +353,9 @@ class GlobalState implements State {
inputs: data.monster.inputs,
});

// Intialize names if not present
data.loadouts = data.loadouts.map((loadout, i) => ({ name: `Loadout ${i + 1}`, ...loadout }));

// manually recompute equipment in case their metadata has changed since the shortlink was created
this.loadouts = merge(this.loadouts, data.loadouts);
this.recalculateEquipmentBonusesFromGearAll();
Expand Down Expand Up @@ -556,6 +560,15 @@ class GlobalState implements State {
}
}

renameLoadout(ix: number, name: string) {
const loadout = this.loadouts[ix];

const trimmedName = name.trim();
if (loadout && trimmedName) {
loadout.name = trimmedName;
}
jayktaylor marked this conversation as resolved.
Show resolved Hide resolved
}

get canCreateLoadout() {
return (this.loadouts.length < 5);
}
Expand All @@ -564,7 +577,10 @@ class GlobalState implements State {
// Do not allow creating a loadout if we're over the limit
if (!this.canCreateLoadout) return;

this.loadouts.push((cloneIndex !== undefined) ? toJS(this.loadouts[cloneIndex]) : generateEmptyPlayer());
const newLoadout = (cloneIndex !== undefined) ? toJS(this.loadouts[cloneIndex]) : generateEmptyPlayer();
newLoadout.name = `Loadout ${this.loadouts.length + 1}`;
jayktaylor marked this conversation as resolved.
Show resolved Hide resolved

this.loadouts.push(newLoadout);
if (selected) this.selectedLoadout = (this.loadouts.length - 1);
}

Expand Down
1 change: 1 addition & 0 deletions src/types/Player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export interface EquipmentStats {
}

export interface Player extends EquipmentStats {
name: string;
style: PlayerCombatStyle;
/**
* The player's base skill levels. These are their skill levels before any boosts (for example, from potions)
Expand Down
2 changes: 1 addition & 1 deletion src/types/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export interface Calculator {
* If you change the schema here without taking precautions, you **will** break existing shortlinks.
*/
export interface ImportableData {
loadouts: Player[];
loadouts: Partial<Player>[];
NickKoester marked this conversation as resolved.
Show resolved Hide resolved
selectedLoadout: number;

monster: Monster;
Expand Down
Loading