1 import PropTypes from 'prop-types';
2 import React from 'react';
4 import ZeldaIcon from '../common/ZeldaIcon';
9 getDungeonRemainingItems,
18 } from '../../helpers/tracker';
19 import { useTracker } from '../../hooks/tracker';
21 const ToggleIcon = ({ controller, className, icons }) => {
22 const { setManualState, state } = useTracker();
23 const activeController = controller || ToggleIcon.nullController;
24 const active = activeController.getActive(state, icons);
25 const defaultIcon = activeController.getDefault(state, icons);
26 const classNames = ['toggle-icon'];
28 classNames.push('active');
30 classNames.push('inactive');
33 classNames.push(className);
36 className={classNames.join(' ')}
38 activeController.handlePrimary(state, setManualState, icons);
42 onContextMenu={(e) => {
43 activeController.handleSecondary(state, setManualState, icons);
48 <ZeldaIcon name={active || defaultIcon || icons[0]} />
52 const doNothing = () => { };
54 const firstIcon = (state, icons) => icons[0];
56 const nextIcon = (state, setState, icons) => {
57 const highest = highestActive(state, icons);
58 const highestIndex = highest ? icons.indexOf(highest) : -1;
59 if (highestIndex + 1 < icons.length) {
60 setState(toggleBoolean(icons[highestIndex + 1]));
63 icons.forEach(icon => {
64 changes[icon] = false;
66 setState(s => ({ ...s, ...changes }));
70 const previousIcon = (state, setState, icons) => {
71 const highest = highestActive(state, icons);
72 const highestIndex = highest ? icons.indexOf(highest) : -1;
73 if (highestIndex >= 0) {
74 setState(toggleBoolean(icons[highestIndex]));
77 icons.forEach(icon => {
80 setState(s => ({ ...s, ...changes }));
84 const nextString = property => (state, setState, icons) => {
85 const current = state[property] || icons[0];
86 const currentIndex = icons.indexOf(current);
87 const nextIndex = (currentIndex + 1) % icons.length;
88 const next = icons[nextIndex];
89 setState(s => ({ ...s, [property]: next }));
92 const previousString = property => (state, setState, icons) => {
93 const current = state[property] || icons[0];
94 const currentIndex = icons.indexOf(current);
95 const previousIndex = (currentIndex + icons.length - 1) % icons.length;
96 const previous = icons[previousIndex];
97 setState(s => ({ ...s, [property]: previous }));
100 ToggleIcon.bottleController = ctrl => ({
101 getActive: (state, icons) => state[ctrl] ? icons[state[ctrl] - 1] : null,
102 getDefault: () => 'bottle',
103 handlePrimary: (state, setState, icons) => {
104 if (state[ctrl] === 0) {
105 // skip over mushroom
106 setState(s => ({ ...s, [ctrl]: 2 }));
108 setState(increment(ctrl, icons.length));
111 handleSecondary: (state, setState, icons) => {
112 if (state[ctrl] === 2) {
113 // skip over mushroom
114 setState(s => ({ ...s, [ctrl]: 0 }));
116 setState(decrement(ctrl, icons.length));
121 ToggleIcon.countController = max => ({
122 getActive: highestActive,
123 getDefault: firstIcon,
124 handlePrimary: (state, setState, icons) => {
125 setState(increment(icons[0], max));
127 handleSecondary: (state, setState, icons) => {
128 setState(decrement(icons[0], max));
132 ToggleIcon.dungeonBossController = (dungeon) => ({
133 getActive: (state) => hasDungeonBoss(state, dungeon) ? getDungeonBoss(state, dungeon) : null,
134 getDefault: (state) => getDungeonBoss(state, dungeon),
135 handlePrimary: dungeon.bosses.length > 1
136 ? nextString(`${dungeon.id}-boss`)
137 : (state, setState) => {
138 setState(toggleBossDefeated(dungeon));
140 handleSecondary: dungeon.bosses.length > 1 ?
141 previousString(`${dungeon.id}-boss`)
142 : (state, setState) => {
143 setState(toggleBossDefeated(dungeon));
147 ToggleIcon.dungeonCheckController = (dungeon) => ({
148 getActive: (state, icons) => getDungeonRemainingItems(state, dungeon) ? icons[1] : null,
149 getDefault: firstIcon,
150 handlePrimary: (state, setState) => {
151 setState(addDungeonCheck(dungeon));
153 handleSecondary: (state, setState) => {
154 setState(removeDungeonCheck(dungeon));
158 ToggleIcon.dungeonController = dungeon => ({
159 getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
160 getDefault: firstIcon,
161 handlePrimary: (state, setState, icons) => {
162 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
164 handleSecondary: (state, setState, icons) => {
165 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
169 ToggleIcon.dungeonCountController = (dungeon, max) => ({
170 getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
171 getDefault: firstIcon,
172 handlePrimary: (state, setState, icons) => {
173 setState(increment(`${dungeon.id}-${icons[0]}`, max));
175 handleSecondary: (state, setState, icons) => {
176 setState(decrement(`${dungeon.id}-${icons[0]}`, max));
180 ToggleIcon.dungeonPrizeController = (dungeon) => ({
181 getActive: (state) => hasDungeonPrize(state, dungeon) ? getDungeonPrize(state, dungeon) : null,
182 getDefault: (state) => getDungeonPrize(state, dungeon),
183 handlePrimary: nextString(`${dungeon.id}-prize`),
184 handleSecondary: previousString(`${dungeon.id}-prize`),
187 ToggleIcon.medallionController = {
188 getActive: highestActive,
189 getDefault: firstIcon,
190 handlePrimary: nextIcon,
191 handleSecondary: (state, setState, icons) => {
192 const mm = state['mm-medallion'];
193 const tr = state['tr-medallion'];
194 const isMM = mm === icons[0];
195 const isTR = tr === icons[0];
196 console.log({ mm, isMM, tr, isTR });
197 if (!isMM && !isTR) {
198 // empty: set as MM if mire is unset, else set as TR if TR is unset
200 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
202 setState(s => ({ ...s, 'tr-medallion': icons[0] }));
204 } else if (isMM && !isTR) {
205 // MM: if TR is free, switch to TR, otherwise remove MM
207 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': icons[0] }));
209 setState(s => ({ ...s, 'mm-medallion': null }));
211 } else if (!isMM && isTR) {
212 // TR: if MM is free, switch to both, otherwise remove TR
214 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
216 setState(s => ({ ...s, 'tr-medallion': null }));
220 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': null }));
225 ToggleIcon.modulusController = ctrl => ({
226 getActive: (state, icons) => icons[(state[ctrl] || 0) % icons.length],
227 getDefault: firstIcon,
228 handlePrimary: (state, setState, icons) => {
229 setState(increment(ctrl, icons.length));
231 handleSecondary: (state, setState, icons) => {
232 setState(decrement(ctrl, icons.length));
236 ToggleIcon.nullController = {
237 getActive: () => null,
238 getDefault: firstIcon,
239 handlePrimary: doNothing,
240 handleSecondary: doNothing,
243 ToggleIcon.simpleController = {
244 getActive: highestActive,
245 getDefault: firstIcon,
246 handlePrimary: nextIcon,
247 handleSecondary: previousIcon,
250 ToggleIcon.progressiveController = (master, min, max) => ({
251 getActive: (state, icons) => {
252 const count = Math.max(min, Math.min(max, state[master] || 0));
253 return count ? icons[count - 1] : null;
255 getDefault: firstIcon,
256 handlePrimary: (state, setState) => {
257 setState(increment(master, max, min));
259 handleSecondary: (state, setState) => {
260 setState(decrement(master, max, min));
264 ToggleIcon.propTypes = {
265 active: PropTypes.string,
266 className: PropTypes.string,
267 controller: PropTypes.shape({
268 handlePrimary: PropTypes.func,
269 handleSecondary: PropTypes.func,
271 icons: PropTypes.arrayOf(PropTypes.string),
274 export default ToggleIcon;