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, transform }) => {
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);
53 <ZeldaIcon name={icon} svg />
57 className={classNames.join(' ')}
59 activeController.handlePrimary(state, setManualState, icons);
63 onContextMenu={(e) => {
64 activeController.handleSecondary(state, setManualState, icons);
69 <ZeldaIcon name={active || defaultIcon || icons[0]} />
73 const doNothing = () => { };
75 const firstIcon = (state, icons) => icons[0];
77 const nextIcon = (state, setState, icons) => {
78 const highest = highestActive(state, icons);
79 const highestIndex = highest ? icons.indexOf(highest) : -1;
80 if (highestIndex + 1 < icons.length) {
81 setState(toggleBoolean(icons[highestIndex + 1]));
84 icons.forEach(icon => {
85 changes[icon] = false;
87 setState(s => ({ ...s, ...changes }));
91 const previousIcon = (state, setState, icons) => {
92 const highest = highestActive(state, icons);
93 const highestIndex = highest ? icons.indexOf(highest) : -1;
94 if (highestIndex >= 0) {
95 setState(toggleBoolean(icons[highestIndex]));
98 icons.forEach(icon => {
101 setState(s => ({ ...s, ...changes }));
105 const nextString = property => (state, setState, icons) => {
106 const current = state[property] || icons[0];
107 const currentIndex = icons.indexOf(current);
108 const nextIndex = (currentIndex + 1) % icons.length;
109 const next = icons[nextIndex];
110 setState(s => ({ ...s, [property]: next }));
113 const previousString = property => (state, setState, icons) => {
114 const current = state[property] || icons[0];
115 const currentIndex = icons.indexOf(current);
116 const previousIndex = (currentIndex + icons.length - 1) % icons.length;
117 const previous = icons[previousIndex];
118 setState(s => ({ ...s, [property]: previous }));
121 ToggleIcon.bottleController = ctrl => ({
122 getActive: (state, icons) => state[ctrl] ? icons[state[ctrl] - 1] : null,
123 getDefault: () => 'bottle',
124 handlePrimary: (state, setState, icons) => {
125 if (state[ctrl] === 0) {
126 // skip over mushroom
127 setState(s => ({ ...s, [ctrl]: 2 }));
129 setState(increment(ctrl, icons.length));
132 handleSecondary: (state, setState, icons) => {
133 if (state[ctrl] === 2) {
134 // skip over mushroom
135 setState(s => ({ ...s, [ctrl]: 0 }));
137 setState(decrement(ctrl, icons.length));
142 ToggleIcon.countController = max => ({
143 getActive: highestActive,
144 getDefault: firstIcon,
145 handlePrimary: (state, setState, icons) => {
146 setState(increment(icons[0], max));
148 handleSecondary: (state, setState, icons) => {
149 setState(decrement(icons[0], max));
153 ToggleIcon.dungeonBossController = (dungeon) => ({
154 getActive: (state) => hasDungeonBoss(state, dungeon) ? getDungeonBoss(state, dungeon) : null,
155 getDefault: (state) => getDungeonBoss(state, dungeon),
156 handlePrimary: dungeon.bosses.length > 1
157 ? nextString(`${dungeon.id}-boss`)
158 : (state, setState) => {
159 setState(toggleBossDefeated(dungeon));
161 handleSecondary: dungeon.bosses.length > 1 ?
162 previousString(`${dungeon.id}-boss`)
163 : (state, setState) => {
164 setState(toggleBossDefeated(dungeon));
168 ToggleIcon.dungeonCheckController = (dungeon) => ({
169 getActive: (state, icons) => getDungeonRemainingItems(state, dungeon) ? icons[1] : null,
170 getDefault: firstIcon,
171 handlePrimary: (state, setState) => {
172 setState(addDungeonCheck(dungeon));
174 handleSecondary: (state, setState) => {
175 setState(removeDungeonCheck(dungeon));
179 ToggleIcon.dungeonController = dungeon => ({
180 getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
181 getDefault: firstIcon,
182 handlePrimary: (state, setState, icons) => {
183 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
185 handleSecondary: (state, setState, icons) => {
186 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
190 ToggleIcon.dungeonCountController = (dungeon, max) => ({
191 getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
192 getDefault: firstIcon,
193 handlePrimary: (state, setState, icons) => {
194 setState(increment(`${dungeon.id}-${icons[0]}`, max));
196 handleSecondary: (state, setState, icons) => {
197 setState(decrement(`${dungeon.id}-${icons[0]}`, max));
201 ToggleIcon.dungeonPrizeController = (dungeon) => ({
202 getActive: (state) => hasDungeonPrize(state, dungeon) ? getDungeonPrize(state, dungeon) : null,
203 getDefault: (state) => getDungeonPrize(state, dungeon),
204 handlePrimary: nextString(`${dungeon.id}-prize`),
205 handleSecondary: previousString(`${dungeon.id}-prize`),
208 ToggleIcon.gtBossController = (which) => ({
209 getActive: (state) => getGTBoss(state, which),
210 getDefault: (state) => getGTBoss(state, which),
211 handlePrimary: nextString(`gt-${which}-boss`),
212 handleSecondary: previousString(`gt-${which}-boss`),
215 ToggleIcon.medallionController = {
216 getActive: highestActive,
217 getDefault: firstIcon,
218 handlePrimary: nextIcon,
219 handleSecondary: (state, setState, icons) => {
220 const mm = state['mm-medallion'];
221 const tr = state['tr-medallion'];
222 const isMM = mm === icons[0];
223 const isTR = tr === icons[0];
224 console.log({ mm, isMM, tr, isTR });
225 if (!isMM && !isTR) {
226 // empty: set as MM if mire is unset, else set as TR if TR is unset
228 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
230 setState(s => ({ ...s, 'tr-medallion': icons[0] }));
232 } else if (isMM && !isTR) {
233 // MM: if TR is free, switch to TR, otherwise remove MM
235 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': icons[0] }));
237 setState(s => ({ ...s, 'mm-medallion': null }));
239 } else if (!isMM && isTR) {
240 // TR: if MM is free, switch to both, otherwise remove TR
242 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
244 setState(s => ({ ...s, 'tr-medallion': null }));
248 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': null }));
253 ToggleIcon.modulusController = ctrl => ({
254 getActive: (state, icons) => icons[(state[ctrl] || 0) % icons.length],
255 getDefault: firstIcon,
256 handlePrimary: (state, setState, icons) => {
257 setState(increment(ctrl, icons.length));
259 handleSecondary: (state, setState, icons) => {
260 setState(decrement(ctrl, icons.length));
264 ToggleIcon.nullController = {
265 getActive: () => null,
266 getDefault: firstIcon,
267 handlePrimary: doNothing,
268 handleSecondary: doNothing,
271 ToggleIcon.pinController = (pin, removePin) => ({
272 getActive: firstIcon,
273 getDefault: firstIcon,
274 handlePrimary: doNothing,
275 handleSecondary: () => removePin(pin),
278 ToggleIcon.simpleController = {
279 getActive: highestActive,
280 getDefault: firstIcon,
281 handlePrimary: nextIcon,
282 handleSecondary: previousIcon,
285 ToggleIcon.progressiveController = (master, min, max) => ({
286 getActive: (state, icons) => {
287 const count = Math.max(min, Math.min(max, state[master] || 0));
288 return count ? icons[count - 1] : null;
290 getDefault: firstIcon,
291 handlePrimary: (state, setState) => {
292 setState(increment(master, max, min));
294 handleSecondary: (state, setState) => {
295 setState(decrement(master, max, min));
299 ToggleIcon.propTypes = {
300 active: PropTypes.string,
301 className: PropTypes.string,
302 controller: PropTypes.shape({
303 handlePrimary: PropTypes.func,
304 handleSecondary: PropTypes.func,
306 icons: PropTypes.arrayOf(PropTypes.string),
308 transform: PropTypes.string,
311 export default ToggleIcon;