Skip to content

Commit

Permalink
feat: keep a (short) history of your changes (#133)
Browse files Browse the repository at this point in the history
  • Loading branch information
TrueBrain authored May 25, 2024
1 parent ef1e740 commit 7a1f003
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 1 deletion.
59 changes: 59 additions & 0 deletions src/components/FitHistory/FitHistory.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.fitHistory {
background-color: #111111;
color: #c5c5c5;
font-size: 15px;
margin: 0 auto;
padding: 5px;
position: relative;
}

.title {
margin-left: 16px;
}

.history {
display: flex;
margin-top: 8px;
width: 100%;
}

.history > img {
cursor: pointer;
height: 16px;
margin-top: 3px;
user-select: none;
}

.holder {
border: 1px solid #202020;
display: flex;
flex: 1;
width: 100%;
}

.entry {
background-color: #626262;
border-bottom: 3px solid #111111;
border-top: 3px solid #111111;
box-sizing: border-box;
cursor: pointer;
height: 19px;
margin-left: 3px;
user-select: none;
width: 9px;
}
.entry.odd {
background-color: #888888;
}

.entry.active {
background-color: #bfbfbf;
border: 0;
height: 19px;
width: 9px;
}

.inactive {
cursor: default !important;
filter: brightness(0.5);
}
46 changes: 46 additions & 0 deletions src/components/FitHistory/FitHistory.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";

import { fitArgType } from "../../../.storybook/fits";
import { useFitSelection, withDecoratorFull } from "../../../.storybook/helpers";

import { EsfFit, useCurrentFit } from "@/providers/CurrentFitProvider";

import { FitHistory } from "./";

type StoryProps = React.ComponentProps<typeof FitHistory> & { fit: EsfFit | null; width: number };

const meta: Meta<StoryProps> = {
component: FitHistory,
tags: ["autodocs"],
};

export default meta;
type Story = StoryObj<StoryProps>;

const TestStory = () => {
const currentFit = useCurrentFit();

return <div>Current Fit: {currentFit.fit?.name}</div>;
};

export const Default: Story = {
argTypes: {
fit: fitArgType,
},
args: {
fit: null,
historySize: 10,
},
decorators: [withDecoratorFull],
render: ({ fit, ...args }) => {
useFitSelection(fit);

return (
<div>
<TestStory />
<FitHistory {...args} />
</div>
);
},
};
107 changes: 107 additions & 0 deletions src/components/FitHistory/FitHistory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import clsx from "clsx";
import React from "react";

import { Icon } from "@/components/Icon";
import { useCurrentFit } from "@/providers/CurrentFitProvider";

import styles from "./FitHistory.module.css";

export interface FitHistoryProps {
/** Amount of entries to keep in history. */
historySize: number;
}

export const FitHistory = (props: FitHistoryProps) => {
const currentFit = useCurrentFit();
const setFit = currentFit.setFit;

const [history, setHistory] = React.useState<string[]>([]);
const [currentIndex, setCurrentIndex] = React.useState<number>(-1);
const [oddOffset, setOddOffset] = React.useState<number>(1);

const historyRef = React.useRef(history);
historyRef.current = history;
const currentIndexRef = React.useRef(currentIndex);
currentIndexRef.current = currentIndex;
const historySizeRef = React.useRef(props.historySize);
historySizeRef.current = props.historySize;

React.useEffect(() => {
const fit = currentFit.fit;
if (fit === null) return;
/* Store the fit as a JSON string, to ensure that any modifications
* to the current doesn't impact the history. */
const fitString = JSON.stringify(fit);
if (currentIndexRef.current !== -1 && historyRef.current[currentIndexRef.current] === fitString) return;

setHistory((prev) => {
if (prev.length >= historySizeRef.current) {
setOddOffset((offset) => (offset + 1) % 2);
return [...prev.slice(1), fitString];
}
return [...prev, fitString];
});
setCurrentIndex(-1);
}, [currentFit.fit]);

React.useEffect(() => {
if (currentIndex === -1) return;

const fit = historyRef.current[currentIndex];
setFit(JSON.parse(fit));
}, [currentIndex, setFit]);

const moveBack = React.useCallback(() => {
setCurrentIndex((prev) => {
if (prev === -1) return historyRef.current.length - 2;
if (prev > 1) return prev - 1;
return 0;
});
}, []);

const moveForward = React.useCallback(() => {
setCurrentIndex((prev) => {
if (prev === -1) return -1;
if (prev >= historyRef.current.length - 2) return historyRef.current.length - 1;
return prev + 1;
});
}, []);

const moveTo = React.useCallback((index: number) => {
setCurrentIndex(index);
}, []);

return (
<div className={styles.fitHistory} style={{ width: props.historySize * 12 + 3 + 16 + 16 }}>
<div className={styles.title}>Simulation History</div>
<div className={styles.history}>
<Icon
name="arrow-left"
size={16}
className={clsx({ [styles.inactive]: history.length <= 1 || currentIndex === 0 })}
onClick={moveBack}
/>
<div className={styles.holder}>
{history.map((fit, i) => (
<div
key={i}
className={clsx(styles.entry, {
[styles.odd]: (i + oddOffset) % 2 === 0,
[styles.active]: (currentIndex === -1 && history.length - 1 === i) || i === currentIndex,
})}
onClick={() => moveTo(i)}
>
&nbsp;
</div>
))}
</div>
<Icon
name="arrow-right"
size={16}
className={clsx({ [styles.inactive]: history.length - 1 === currentIndex || currentIndex === -1 })}
onClick={moveForward}
/>
</div>
</div>
);
};
1 change: 1 addition & 0 deletions src/components/FitHistory/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { FitHistory } from "./FitHistory";
16 changes: 15 additions & 1 deletion src/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const iconMapping = {
"align-time": "texture/classes/fitting/statsicons/aligntime.png",
"armor-hp": "texture/classes/fitting/statsicons/armorhp.png",
"armor-repair-rate": "texture/classes/fitting/statsicons/armorrepairrate.png",
"arrow-left": "texture/shared/triangleleft.png",
"arrow-right": "texture/shared/triangleright.png",
"cargo-hold": "texture/windowicons/ships.png",
"damage-alpha": "texture/classes/fitting/statsicons/alphastrike.png",
"damage-dps": "texture/classes/fitting/statsicons/turretdps.png",
Expand Down Expand Up @@ -52,6 +54,10 @@ export interface IconProps {
size?: number;
/** Title of the icon. */
title?: string;
/** Class name of the icon. */
className?: string;
/** Callback for when icon is clicked. */
onClick?: () => void;
}

/**
Expand All @@ -62,5 +68,13 @@ export const Icon = (props: IconProps) => {
if (icon === undefined) {
return <span>Unknown icon: {props.name}</span>;
}
return <img src={`${defaultDataUrl}ui/${icon}`} width={props.size} title={props.title} />;
return (
<img
src={`${defaultDataUrl}ui/${icon}`}
width={props.size}
title={props.title}
className={props.className}
onClick={props.onClick}
/>
);
};
8 changes: 8 additions & 0 deletions src/components/ShipFitExtended/ShipFitExtended.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
color: #c5c5c5;
font-size: 15px;
height: 100%;
padding-bottom: 80px;
position: relative;
width: 100%;
}
Expand Down Expand Up @@ -35,6 +36,13 @@
position: absolute;
}

.history {
bottom: 0px;
position: absolute;
margin: 0 auto;
width: 100%;
}

.cargoIcon {
display: inline-block;
}
Expand Down
5 changes: 5 additions & 0 deletions src/components/ShipFitExtended/ShipFitExtended.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { DroneBay } from "@/components/DroneBay";
import { Icon } from "@/components/Icon";
import { ShipAttribute } from "@/components/ShipAttribute";
import { ShipFit } from "@/components/ShipFit";
import { FitHistory } from "@/components/FitHistory";
import { useCurrentFit } from "@/providers/CurrentFitProvider";
import { useEveData } from "@/providers/EveDataProvider";

Expand Down Expand Up @@ -104,6 +105,10 @@ export const ShipFitExtended = () => {
<ShipDroneBay />
</div>

<div className={styles.history}>
<FitHistory historySize={25} />
</div>

<div className={styles.cpuPg}>
<CpuPg title="CPU">
<ShipAttribute name="cpuUnused" fixed={1} />/<ShipAttribute name="cpuOutput" fixed={1} />
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from "./components/CalculationDetail";
export * from "./components/CharacterSelection";
export * from "./components/DroneBay";
export * from "./components/FitButtonBar";
export * from "./components/FitHistory";
export * from "./components/HardwareListing";
export * from "./components/HullListing";
export * from "./components/Icon";
Expand Down

0 comments on commit 7a1f003

Please sign in to comment.