1 import PropTypes from 'prop-types';
2 import React from 'react';
4 import ZeldaIcon from '../common/ZeldaIcon';
9 getDungeonRemainingItems,
19 } from '../../helpers/tracker';
20 import { useTracker } from '../../hooks/tracker';
22 const ToggleIcon = ({ controller, className, icons, svg }) => {
23 const { setManualState, state } = useTracker();
24 const activeController = controller || ToggleIcon.nullController;
25 const active = activeController.getActive(state, icons);
26 const defaultIcon = activeController.getDefault(state, icons);
27 const icon = active || defaultIcon || icons[0];
28 const classNames = ['toggle-icon'];
30 classNames.push('active');
32 classNames.push('inactive');
35 classNames.push(className);
39 className={classNames.join(' ')}
42 activeController.handlePrimary(state, setManualState, icons);
46 onContextMenu={(e) => {
47 activeController.handleSecondary(state, setManualState, icons);
52 <ZeldaIcon name={icon} svg />
56 className={classNames.join(' ')}
58 activeController.handlePrimary(state, setManualState, icons);
62 onContextMenu={(e) => {
63 activeController.handleSecondary(state, setManualState, icons);
68 <ZeldaIcon name={active || defaultIcon || icons[0]} />
72 const doNothing = () => { };
74 const firstIcon = (state, icons) => icons[0];
76 const nextIcon = (state, setState, icons) => {
77 const highest = highestActive(state, icons);
78 const highestIndex = highest ? icons.indexOf(highest) : -1;
79 if (highestIndex + 1 < icons.length) {
80 setState(toggleBoolean(icons[highestIndex + 1]));
83 icons.forEach(icon => {
84 changes[icon] = false;
86 setState(s => ({ ...s, ...changes }));
90 const previousIcon = (state, setState, icons) => {
91 const highest = highestActive(state, icons);
92 const highestIndex = highest ? icons.indexOf(highest) : -1;
93 if (highestIndex >= 0) {
94 setState(toggleBoolean(icons[highestIndex]));
97 icons.forEach(icon => {
100 setState(s => ({ ...s, ...changes }));
104 const nextString = property => (state, setState, icons) => {
105 const current = state[property] || icons[0];
106 const currentIndex = icons.indexOf(current);
107 const nextIndex = (currentIndex + 1) % icons.length;
108 const next = icons[nextIndex];
109 setState(s => ({ ...s, [property]: next }));
112 const previousString = property => (state, setState, icons) => {
113 const current = state[property] || icons[0];
114 const currentIndex = icons.indexOf(current);
115 const previousIndex = (currentIndex + icons.length - 1) % icons.length;
116 const previous = icons[previousIndex];
117 setState(s => ({ ...s, [property]: previous }));
120 ToggleIcon.bottleController = ctrl => ({
121 getActive: (state, icons) => state[ctrl] ? icons[state[ctrl] - 1] : null,
122 getDefault: () => 'bottle',
123 handlePrimary: (state, setState, icons) => {
124 if (state[ctrl] === 0) {
125 // skip over mushroom
126 setState(s => ({ ...s, [ctrl]: 2 }));
128 setState(increment(ctrl, icons.length));
131 handleSecondary: (state, setState, icons) => {
132 if (state[ctrl] === 2) {
133 // skip over mushroom
134 setState(s => ({ ...s, [ctrl]: 0 }));
136 setState(decrement(ctrl, icons.length));
141 ToggleIcon.countController = max => ({
142 getActive: highestActive,
143 getDefault: firstIcon,
144 handlePrimary: (state, setState, icons) => {
145 setState(increment(icons[0], max));
147 handleSecondary: (state, setState, icons) => {
148 setState(decrement(icons[0], max));
152 ToggleIcon.dungeonBossController = (dungeon) => ({
153 getActive: (state) => hasDungeonBoss(state, dungeon) ? getDungeonBoss(state, dungeon) : null,
154 getDefault: (state) => getDungeonBoss(state, dungeon),
155 handlePrimary: dungeon.bosses.length > 1
156 ? nextString(`${dungeon.id}-boss`)
157 : (state, setState) => {
158 setState(toggleBossDefeated(dungeon));
160 handleSecondary: dungeon.bosses.length > 1 ?
161 previousString(`${dungeon.id}-boss`)
162 : (state, setState) => {
163 setState(toggleBossDefeated(dungeon));
167 ToggleIcon.dungeonCheckController = (dungeon) => ({
168 getActive: (state, icons) => getDungeonRemainingItems(state, dungeon) ? icons[1] : null,
169 getDefault: firstIcon,
170 handlePrimary: (state, setState) => {
171 setState(addDungeonCheck(dungeon));
173 handleSecondary: (state, setState) => {
174 setState(removeDungeonCheck(dungeon));
178 ToggleIcon.dungeonController = dungeon => ({
179 getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
180 getDefault: firstIcon,
181 handlePrimary: (state, setState, icons) => {
182 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
184 handleSecondary: (state, setState, icons) => {
185 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
189 ToggleIcon.dungeonCountController = (dungeon, max) => ({
190 getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
191 getDefault: firstIcon,
192 handlePrimary: (state, setState, icons) => {
193 setState(increment(`${dungeon.id}-${icons[0]}`, max));
195 handleSecondary: (state, setState, icons) => {
196 setState(decrement(`${dungeon.id}-${icons[0]}`, max));
200 ToggleIcon.dungeonPrizeController = (dungeon) => ({
201 getActive: (state) => hasDungeonPrize(state, dungeon) ? getDungeonPrize(state, dungeon) : null,
202 getDefault: (state) => getDungeonPrize(state, dungeon),
203 handlePrimary: nextString(`${dungeon.id}-prize`),
204 handleSecondary: previousString(`${dungeon.id}-prize`),
207 ToggleIcon.gtBossController = (which) => ({
208 getActive: (state) => getGTBoss(state, which),
209 getDefault: (state) => getGTBoss(state, which),
210 handlePrimary: nextString(`gt-${which}-boss`),
211 handleSecondary: previousString(`gt-${which}-boss`),
214 ToggleIcon.medallionController = {
215 getActive: highestActive,
216 getDefault: firstIcon,
217 handlePrimary: nextIcon,
218 handleSecondary: (state, setState, icons) => {
219 const mm = state['mm-medallion'];
220 const tr = state['tr-medallion'];
221 const isMM = mm === icons[0];
222 const isTR = tr === icons[0];
223 console.log({ mm, isMM, tr, isTR });
224 if (!isMM && !isTR) {
225 // empty: set as MM if mire is unset, else set as TR if TR is unset
227 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
229 setState(s => ({ ...s, 'tr-medallion': icons[0] }));
231 } else if (isMM && !isTR) {
232 // MM: if TR is free, switch to TR, otherwise remove MM
234 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': icons[0] }));
236 setState(s => ({ ...s, 'mm-medallion': null }));
238 } else if (!isMM && isTR) {
239 // TR: if MM is free, switch to both, otherwise remove TR
241 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
243 setState(s => ({ ...s, 'tr-medallion': null }));
247 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': null }));
252 ToggleIcon.modulusController = ctrl => ({
253 getActive: (state, icons) => icons[(state[ctrl] || 0) % icons.length],
254 getDefault: firstIcon,
255 handlePrimary: (state, setState, icons) => {
256 setState(increment(ctrl, icons.length));
258 handleSecondary: (state, setState, icons) => {
259 setState(decrement(ctrl, icons.length));
263 ToggleIcon.nullController = {
264 getActive: () => null,
265 getDefault: firstIcon,
266 handlePrimary: doNothing,
267 handleSecondary: doNothing,
270 ToggleIcon.pinController = (pin, removePin) => ({
271 getActive: firstIcon,
272 getDefault: firstIcon,
273 handlePrimary: doNothing,
274 handleSecondary: () => removePin(pin),
277 ToggleIcon.simpleController = {
278 getActive: highestActive,
279 getDefault: firstIcon,
280 handlePrimary: nextIcon,
281 handleSecondary: previousIcon,
284 ToggleIcon.progressiveController = (master, min, max) => ({
285 getActive: (state, icons) => {
286 const count = Math.max(min, Math.min(max, state[master] || 0));
287 return count ? icons[count - 1] : null;
289 getDefault: firstIcon,
290 handlePrimary: (state, setState) => {
291 setState(increment(master, max, min));
293 handleSecondary: (state, setState) => {
294 setState(decrement(master, max, min));
298 ToggleIcon.propTypes = {
299 active: PropTypes.string,
300 className: PropTypes.string,
301 controller: PropTypes.shape({
302 handlePrimary: PropTypes.func,
303 handleSecondary: PropTypes.func,
305 icons: PropTypes.arrayOf(PropTypes.string),
309 export default ToggleIcon;