From fc97ab72f5f42c22c0d678284d32aa8eb4c5da8a Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 10 Dec 2023 17:09:54 +0100 Subject: [PATCH] feat: ability to remove modules from a fit (via hover on module) (#42) --- src/ShipFit/ShipFit.module.css | 46 ++++++-- src/ShipFit/Slot.tsx | 109 +++++++++++++----- .../ShipSnapshotProvider.tsx | 17 ++- 3 files changed, 130 insertions(+), 42 deletions(-) diff --git a/src/ShipFit/ShipFit.module.css b/src/ShipFit/ShipFit.module.css index 7143fea..f36f399 100644 --- a/src/ShipFit/ShipFit.module.css +++ b/src/ShipFit/ShipFit.module.css @@ -60,11 +60,11 @@ .ringTopItem > div, .ringTopItem > svg { --reverse-rotation: calc(-1 * var(--rotation)); + left: 50%; pointer-events: all; position: absolute; top: 3.5%; - transform: rotate(var(--reverse-rotation)); } .radialMenu { @@ -74,18 +74,23 @@ width: 2.5%; } -.slot { - height: 9.5%; +.slotOuter { + height: 18%; margin-left: -2.5%; + position: absolute; + width: 7%; +} + +.slot { + height: 50%; position: relative; user-select: none; - width: 7%; + width: 100%; } .slot > svg { height: 100%; position: absolute; - transform: rotate(var(--rotation)); width: 100%; z-index: 4; } @@ -93,14 +98,15 @@ .slotImage { height: 100%; position: absolute; - margin-top: 8%; - margin-left: -10%; + transform: rotate(var(--reverse-rotation)); width: 100%; z-index: 5; } .slotImage > img { border-top-left-radius: 50%; + margin-left: -10%; + margin-top: 5%; width: 120%; } @@ -116,9 +122,31 @@ fill: #fd2d2d; stroke: #fd2d2d; } -.slot[data-state="Offline"] { +.slot[data-state="Offline"] > svg, .slot[data-state="Offline"] > .slotImage { opacity: 0.3; } -.slot[data-state="Unavailable"] { +.slot[data-state="Unavailable"] > svg, .slot[data-state="Unavailable"] > .slotImage { opacity: 0.1; } + +.slotOuter .slotOptions { + display: none; +} +.slotOuter[data-hasitem=true]:hover .slotOptions { + display: block; +} + +.slotOptions { + position: absolute; + text-align: center; + top: 50%; + width: 100%; +} +.slotOptions > svg { + cursor: pointer; + display: block; + filter: drop-shadow(0px 2px 1px #222222); + margin: 6px auto; + transform: rotate(var(--reverse-rotation)); + stroke: #ffffff; +} diff --git a/src/ShipFit/Slot.tsx b/src/ShipFit/Slot.tsx index e904de5..e9d6580 100644 --- a/src/ShipFit/Slot.tsx +++ b/src/ShipFit/Slot.tsx @@ -43,45 +43,56 @@ export const Slot = (props: { type: string, index: number, fittable: boolean, ma let svg = <>; if (props.main !== undefined) { - svg = - - - - - - - - - - - - - ; + svg = <> + + + + + + + + + + + + + + + + + + + + + + + + + + ; } svg = <> {svg} - {props.fittable && active && } - {props.fittable && !active && } + {props.fittable && esiItem && active && } + {props.fittable && esiItem && !active && } ; - /* Not fittable and nothing fitted; no need to render the slot. */ - if (esiItem === undefined && !props.fittable) { - return
- {svg} -
- } + const offlineState = React.useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + if (!shipSnapshot?.loaded || !esiItem) return; - if (esiItem !== undefined) { - item = - } + if (esiItem.state === "Passive") { + shipSnapshot.setItemState(esiItem.flag, "Online"); + } else { + shipSnapshot.setItemState(esiItem.flag, "Passive"); + } + }, [shipSnapshot, esiItem]); - const state = (esiItem?.state === "Passive" && esiItem?.max_state !== "Passive") ? "Offline" : esiItem?.state; - - function cycleState(e: React.MouseEvent) { + const cycleState = React.useCallback((e: React.MouseEvent) => { if (!shipSnapshot?.loaded || !esiItem) return; const states = stateRotation[esiItem.max_state]; @@ -95,12 +106,46 @@ export const Slot = (props: { type: string, index: number, fittable: boolean, ma } shipSnapshot.setItemState(esiItem.flag, newState); + }, [shipSnapshot, esiItem]); + + const unfitModule = React.useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + if (!shipSnapshot?.loaded || !esiItem) return; + + shipSnapshot.removeModule(esiItem.flag); + }, [shipSnapshot, esiItem]); + + /* Not fittable and nothing fitted; no need to render the slot. */ + if (esiItem === undefined && !props.fittable) { + return
+
+ {svg} +
+
} - return
- {svg} -
- {item} + if (esiItem !== undefined) { + item = + } + + const state = (esiItem?.state === "Passive" && esiItem?.max_state !== "Passive") ? "Offline" : esiItem?.state; + + return
+
+ {svg} +
+ {item} +
+
+
+ + + + {esiItem?.max_state !== "Passive" && + + + + }
} diff --git a/src/ShipSnapshotProvider/ShipSnapshotProvider.tsx b/src/ShipSnapshotProvider/ShipSnapshotProvider.tsx index 137efec..ac22b88 100644 --- a/src/ShipSnapshotProvider/ShipSnapshotProvider.tsx +++ b/src/ShipSnapshotProvider/ShipSnapshotProvider.tsx @@ -58,6 +58,7 @@ interface ShipSnapshot { fit?: EsiFit; addModule: (typeId: number, slot: ShipSnapshotSlotsType | "dronebay") => void; + removeModule: (flag: number) => void; changeHull: (typeId: number) => void; changeFit: (fit: EsiFit) => void; setItemState: (flag: number, state: string) => void; @@ -73,6 +74,7 @@ export const ShipSnapshotContext = React.createContext({ "rig": 0, }, addModule: () => {}, + removeModule: () => {}, changeHull: () => {}, changeFit: () => {}, setItemState: () => {}, @@ -110,6 +112,7 @@ export const ShipSnapshotProvider = (props: ShipSnapshotProps) => { "rig": 0, }, addModule: () => {}, + removeModule: () => {}, changeHull: () => {}, changeFit: () => {}, setItemState: () => {}, @@ -172,6 +175,17 @@ export const ShipSnapshotProvider = (props: ShipSnapshotProps) => { }) }, [shipSnapshot.slots]); + const removeModule = React.useCallback((flag: number) => { + setCurrentFit((oldFit: EsiFit | undefined) => { + if (oldFit === undefined) return undefined; + + return { + ...oldFit, + items: oldFit.items.filter((item) => item.flag !== flag), + }; + }) + }, []); + const changeHull = React.useCallback((typeId: number) => { setCurrentFit({ "name": "New Ship", @@ -185,11 +199,12 @@ export const ShipSnapshotProvider = (props: ShipSnapshotProps) => { setShipSnapshot((oldSnapshot) => ({ ...oldSnapshot, addModule, + removeModule, changeHull, changeFit: setCurrentFit, setItemState, })); - }, [addModule, changeHull, setItemState]); + }, [addModule, removeModule, changeHull, setItemState]); React.useEffect(() => { if (!dogmaEngine.loaded) return;