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, svg }) => {
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);
37 className={classNames.join(' ')}
39 activeController.handlePrimary(state, setManualState, icons);
43 onContextMenu={(e) => {
44 activeController.handleSecondary(state, setManualState, icons);
49 <ZeldaIcon name={active || defaultIcon || icons[0]} svg />
53 className={classNames.join(' ')}
55 activeController.handlePrimary(state, setManualState, icons);
59 onContextMenu={(e) => {
60 activeController.handleSecondary(state, setManualState, icons);
65 <ZeldaIcon name={active || defaultIcon || icons[0]} />
69 const doNothing = () => { };
71 const firstIcon = (state, icons) => icons[0];
73 const nextIcon = (state, setState, icons) => {
74 const highest = highestActive(state, icons);
75 const highestIndex = highest ? icons.indexOf(highest) : -1;
76 if (highestIndex + 1 < icons.length) {
77 setState(toggleBoolean(icons[highestIndex + 1]));
80 icons.forEach(icon => {
81 changes[icon] = false;
83 setState(s => ({ ...s, ...changes }));
87 const previousIcon = (state, setState, icons) => {
88 const highest = highestActive(state, icons);
89 const highestIndex = highest ? icons.indexOf(highest) : -1;
90 if (highestIndex >= 0) {
91 setState(toggleBoolean(icons[highestIndex]));
94 icons.forEach(icon => {
97 setState(s => ({ ...s, ...changes }));
101 const nextString = property => (state, setState, icons) => {
102 const current = state[property] || icons[0];
103 const currentIndex = icons.indexOf(current);
104 const nextIndex = (currentIndex + 1) % icons.length;
105 const next = icons[nextIndex];
106 setState(s => ({ ...s, [property]: next }));
109 const previousString = property => (state, setState, icons) => {
110 const current = state[property] || icons[0];
111 const currentIndex = icons.indexOf(current);
112 const previousIndex = (currentIndex + icons.length - 1) % icons.length;
113 const previous = icons[previousIndex];
114 setState(s => ({ ...s, [property]: previous }));
117 ToggleIcon.bottleController = ctrl => ({
118 getActive: (state, icons) => state[ctrl] ? icons[state[ctrl] - 1] : null,
119 getDefault: () => 'bottle',
120 handlePrimary: (state, setState, icons) => {
121 if (state[ctrl] === 0) {
122 // skip over mushroom
123 setState(s => ({ ...s, [ctrl]: 2 }));
125 setState(increment(ctrl, icons.length));
128 handleSecondary: (state, setState, icons) => {
129 if (state[ctrl] === 2) {
130 // skip over mushroom
131 setState(s => ({ ...s, [ctrl]: 0 }));
133 setState(decrement(ctrl, icons.length));
138 ToggleIcon.countController = max => ({
139 getActive: highestActive,
140 getDefault: firstIcon,
141 handlePrimary: (state, setState, icons) => {
142 setState(increment(icons[0], max));
144 handleSecondary: (state, setState, icons) => {
145 setState(decrement(icons[0], max));
149 ToggleIcon.dungeonBossController = (dungeon) => ({
150 getActive: (state) => hasDungeonBoss(state, dungeon) ? getDungeonBoss(state, dungeon) : null,
151 getDefault: (state) => getDungeonBoss(state, dungeon),
152 handlePrimary: dungeon.bosses.length > 1
153 ? nextString(`${dungeon.id}-boss`)
154 : (state, setState) => {
155 setState(toggleBossDefeated(dungeon));
157 handleSecondary: dungeon.bosses.length > 1 ?
158 previousString(`${dungeon.id}-boss`)
159 : (state, setState) => {
160 setState(toggleBossDefeated(dungeon));
164 ToggleIcon.dungeonCheckController = (dungeon) => ({
165 getActive: (state, icons) => getDungeonRemainingItems(state, dungeon) ? icons[1] : null,
166 getDefault: firstIcon,
167 handlePrimary: (state, setState) => {
168 setState(addDungeonCheck(dungeon));
170 handleSecondary: (state, setState) => {
171 setState(removeDungeonCheck(dungeon));
175 ToggleIcon.dungeonController = dungeon => ({
176 getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
177 getDefault: firstIcon,
178 handlePrimary: (state, setState, icons) => {
179 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
181 handleSecondary: (state, setState, icons) => {
182 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
186 ToggleIcon.dungeonCountController = (dungeon, max) => ({
187 getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
188 getDefault: firstIcon,
189 handlePrimary: (state, setState, icons) => {
190 setState(increment(`${dungeon.id}-${icons[0]}`, max));
192 handleSecondary: (state, setState, icons) => {
193 setState(decrement(`${dungeon.id}-${icons[0]}`, max));
197 ToggleIcon.dungeonPrizeController = (dungeon) => ({
198 getActive: (state) => hasDungeonPrize(state, dungeon) ? getDungeonPrize(state, dungeon) : null,
199 getDefault: (state) => getDungeonPrize(state, dungeon),
200 handlePrimary: nextString(`${dungeon.id}-prize`),
201 handleSecondary: previousString(`${dungeon.id}-prize`),
204 ToggleIcon.medallionController = {
205 getActive: highestActive,
206 getDefault: firstIcon,
207 handlePrimary: nextIcon,
208 handleSecondary: (state, setState, icons) => {
209 const mm = state['mm-medallion'];
210 const tr = state['tr-medallion'];
211 const isMM = mm === icons[0];
212 const isTR = tr === icons[0];
213 console.log({ mm, isMM, tr, isTR });
214 if (!isMM && !isTR) {
215 // empty: set as MM if mire is unset, else set as TR if TR is unset
217 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
219 setState(s => ({ ...s, 'tr-medallion': icons[0] }));
221 } else if (isMM && !isTR) {
222 // MM: if TR is free, switch to TR, otherwise remove MM
224 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': icons[0] }));
226 setState(s => ({ ...s, 'mm-medallion': null }));
228 } else if (!isMM && isTR) {
229 // TR: if MM is free, switch to both, otherwise remove TR
231 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
233 setState(s => ({ ...s, 'tr-medallion': null }));
237 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': null }));
242 ToggleIcon.modulusController = ctrl => ({
243 getActive: (state, icons) => icons[(state[ctrl] || 0) % icons.length],
244 getDefault: firstIcon,
245 handlePrimary: (state, setState, icons) => {
246 setState(increment(ctrl, icons.length));
248 handleSecondary: (state, setState, icons) => {
249 setState(decrement(ctrl, icons.length));
253 ToggleIcon.nullController = {
254 getActive: () => null,
255 getDefault: firstIcon,
256 handlePrimary: doNothing,
257 handleSecondary: doNothing,
260 ToggleIcon.simpleController = {
261 getActive: highestActive,
262 getDefault: firstIcon,
263 handlePrimary: nextIcon,
264 handleSecondary: previousIcon,
267 ToggleIcon.progressiveController = (master, min, max) => ({
268 getActive: (state, icons) => {
269 const count = Math.max(min, Math.min(max, state[master] || 0));
270 return count ? icons[count - 1] : null;
272 getDefault: firstIcon,
273 handlePrimary: (state, setState) => {
274 setState(increment(master, max, min));
276 handleSecondary: (state, setState) => {
277 setState(decrement(master, max, min));
281 ToggleIcon.propTypes = {
282 active: PropTypes.string,
283 className: PropTypes.string,
284 controller: PropTypes.shape({
285 handlePrimary: PropTypes.func,
286 handleSecondary: PropTypes.func,
288 icons: PropTypes.arrayOf(PropTypes.string),
292 export default ToggleIcon;