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 classNames = ['toggle-icon'];
29 classNames.push('active');
31 classNames.push('inactive');
34 classNames.push(className);
38 className={classNames.join(' ')}
40 activeController.handlePrimary(state, setManualState, icons);
44 onContextMenu={(e) => {
45 activeController.handleSecondary(state, setManualState, icons);
50 <ZeldaIcon name={active || defaultIcon || icons[0]} svg />
54 className={classNames.join(' ')}
56 activeController.handlePrimary(state, setManualState, icons);
60 onContextMenu={(e) => {
61 activeController.handleSecondary(state, setManualState, icons);
66 <ZeldaIcon name={active || defaultIcon || icons[0]} />
70 const doNothing = () => { };
72 const firstIcon = (state, icons) => icons[0];
74 const nextIcon = (state, setState, icons) => {
75 const highest = highestActive(state, icons);
76 const highestIndex = highest ? icons.indexOf(highest) : -1;
77 if (highestIndex + 1 < icons.length) {
78 setState(toggleBoolean(icons[highestIndex + 1]));
81 icons.forEach(icon => {
82 changes[icon] = false;
84 setState(s => ({ ...s, ...changes }));
88 const previousIcon = (state, setState, icons) => {
89 const highest = highestActive(state, icons);
90 const highestIndex = highest ? icons.indexOf(highest) : -1;
91 if (highestIndex >= 0) {
92 setState(toggleBoolean(icons[highestIndex]));
95 icons.forEach(icon => {
98 setState(s => ({ ...s, ...changes }));
102 const nextString = property => (state, setState, icons) => {
103 const current = state[property] || icons[0];
104 const currentIndex = icons.indexOf(current);
105 const nextIndex = (currentIndex + 1) % icons.length;
106 const next = icons[nextIndex];
107 setState(s => ({ ...s, [property]: next }));
110 const previousString = property => (state, setState, icons) => {
111 const current = state[property] || icons[0];
112 const currentIndex = icons.indexOf(current);
113 const previousIndex = (currentIndex + icons.length - 1) % icons.length;
114 const previous = icons[previousIndex];
115 setState(s => ({ ...s, [property]: previous }));
118 ToggleIcon.bottleController = ctrl => ({
119 getActive: (state, icons) => state[ctrl] ? icons[state[ctrl] - 1] : null,
120 getDefault: () => 'bottle',
121 handlePrimary: (state, setState, icons) => {
122 if (state[ctrl] === 0) {
123 // skip over mushroom
124 setState(s => ({ ...s, [ctrl]: 2 }));
126 setState(increment(ctrl, icons.length));
129 handleSecondary: (state, setState, icons) => {
130 if (state[ctrl] === 2) {
131 // skip over mushroom
132 setState(s => ({ ...s, [ctrl]: 0 }));
134 setState(decrement(ctrl, icons.length));
139 ToggleIcon.countController = max => ({
140 getActive: highestActive,
141 getDefault: firstIcon,
142 handlePrimary: (state, setState, icons) => {
143 setState(increment(icons[0], max));
145 handleSecondary: (state, setState, icons) => {
146 setState(decrement(icons[0], max));
150 ToggleIcon.dungeonBossController = (dungeon) => ({
151 getActive: (state) => hasDungeonBoss(state, dungeon) ? getDungeonBoss(state, dungeon) : null,
152 getDefault: (state) => getDungeonBoss(state, dungeon),
153 handlePrimary: dungeon.bosses.length > 1
154 ? nextString(`${dungeon.id}-boss`)
155 : (state, setState) => {
156 setState(toggleBossDefeated(dungeon));
158 handleSecondary: dungeon.bosses.length > 1 ?
159 previousString(`${dungeon.id}-boss`)
160 : (state, setState) => {
161 setState(toggleBossDefeated(dungeon));
165 ToggleIcon.dungeonCheckController = (dungeon) => ({
166 getActive: (state, icons) => getDungeonRemainingItems(state, dungeon) ? icons[1] : null,
167 getDefault: firstIcon,
168 handlePrimary: (state, setState) => {
169 setState(addDungeonCheck(dungeon));
171 handleSecondary: (state, setState) => {
172 setState(removeDungeonCheck(dungeon));
176 ToggleIcon.dungeonController = dungeon => ({
177 getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
178 getDefault: firstIcon,
179 handlePrimary: (state, setState, icons) => {
180 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
182 handleSecondary: (state, setState, icons) => {
183 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
187 ToggleIcon.dungeonCountController = (dungeon, max) => ({
188 getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
189 getDefault: firstIcon,
190 handlePrimary: (state, setState, icons) => {
191 setState(increment(`${dungeon.id}-${icons[0]}`, max));
193 handleSecondary: (state, setState, icons) => {
194 setState(decrement(`${dungeon.id}-${icons[0]}`, max));
198 ToggleIcon.dungeonPrizeController = (dungeon) => ({
199 getActive: (state) => hasDungeonPrize(state, dungeon) ? getDungeonPrize(state, dungeon) : null,
200 getDefault: (state) => getDungeonPrize(state, dungeon),
201 handlePrimary: nextString(`${dungeon.id}-prize`),
202 handleSecondary: previousString(`${dungeon.id}-prize`),
205 ToggleIcon.gtBossController = (which) => ({
206 getActive: (state) => getGTBoss(state, which),
207 getDefault: (state) => getGTBoss(state, which),
208 handlePrimary: nextString(`gt-${which}-boss`),
209 handleSecondary: previousString(`gt-${which}-boss`),
212 ToggleIcon.medallionController = {
213 getActive: highestActive,
214 getDefault: firstIcon,
215 handlePrimary: nextIcon,
216 handleSecondary: (state, setState, icons) => {
217 const mm = state['mm-medallion'];
218 const tr = state['tr-medallion'];
219 const isMM = mm === icons[0];
220 const isTR = tr === icons[0];
221 console.log({ mm, isMM, tr, isTR });
222 if (!isMM && !isTR) {
223 // empty: set as MM if mire is unset, else set as TR if TR is unset
225 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
227 setState(s => ({ ...s, 'tr-medallion': icons[0] }));
229 } else if (isMM && !isTR) {
230 // MM: if TR is free, switch to TR, otherwise remove MM
232 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': icons[0] }));
234 setState(s => ({ ...s, 'mm-medallion': null }));
236 } else if (!isMM && isTR) {
237 // TR: if MM is free, switch to both, otherwise remove TR
239 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
241 setState(s => ({ ...s, 'tr-medallion': null }));
245 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': null }));
250 ToggleIcon.modulusController = ctrl => ({
251 getActive: (state, icons) => icons[(state[ctrl] || 0) % icons.length],
252 getDefault: firstIcon,
253 handlePrimary: (state, setState, icons) => {
254 setState(increment(ctrl, icons.length));
256 handleSecondary: (state, setState, icons) => {
257 setState(decrement(ctrl, icons.length));
261 ToggleIcon.nullController = {
262 getActive: () => null,
263 getDefault: firstIcon,
264 handlePrimary: doNothing,
265 handleSecondary: doNothing,
268 ToggleIcon.simpleController = {
269 getActive: highestActive,
270 getDefault: firstIcon,
271 handlePrimary: nextIcon,
272 handleSecondary: previousIcon,
275 ToggleIcon.progressiveController = (master, min, max) => ({
276 getActive: (state, icons) => {
277 const count = Math.max(min, Math.min(max, state[master] || 0));
278 return count ? icons[count - 1] : null;
280 getDefault: firstIcon,
281 handlePrimary: (state, setState) => {
282 setState(increment(master, max, min));
284 handleSecondary: (state, setState) => {
285 setState(decrement(master, max, min));
289 ToggleIcon.propTypes = {
290 active: PropTypes.string,
291 className: PropTypes.string,
292 controller: PropTypes.shape({
293 handlePrimary: PropTypes.func,
294 handleSecondary: PropTypes.func,
296 icons: PropTypes.arrayOf(PropTypes.string),
300 export default ToggleIcon;