]> git.localhorst.tv Git - alttp.git/blob - resources/js/components/tracker/ToggleIcon.js
3f6af9641c0926acb2bf31b1cd8618e74ffe5c3d
[alttp.git] / resources / js / components / tracker / ToggleIcon.js
1 import PropTypes from 'prop-types';
2 import React from 'react';
3
4 import ZeldaIcon from '../common/ZeldaIcon';
5 import {
6         decrement,
7         getDungeonBoss,
8         getDungeonPrize,
9         hasDungeonBoss,
10         hasDungeonPrize,
11         highestActive,
12         increment,
13         toggleBoolean,
14 } from '../../helpers/tracker';
15 import { useTracker } from '../../hooks/tracker';
16
17 const ToggleIcon = ({ controller, className, icons }) => {
18         const { state, setState } = useTracker();
19         const activeController = controller || ToggleIcon.nullController;
20         const active = activeController.getActive(state, icons);
21         const defaultIcon = activeController.getDefault(state, icons);
22         const classNames = ['toggle-icon'];
23         if (active) {
24                 classNames.push('active');
25         } else {
26                 classNames.push('inactive');
27         }
28         if (className) {
29                 classNames.push(className);
30         }
31         return <span
32                 className={classNames.join(' ')}
33                 onClick={(e) => {
34                         activeController.handlePrimary(state, setState, icons);
35                         e.preventDefault();
36                         e.stopPropagation();
37                 }}
38                 onContextMenu={(e) => {
39                         activeController.handleSecondary(state, setState, icons);
40                         e.preventDefault();
41                         e.stopPropagation();
42                 }}
43         >
44                 <ZeldaIcon name={active || defaultIcon || icons[0]} />
45         </span>;
46 };
47
48 const doNothing = () => { };
49
50 const firstIcon = (state, icons) => icons[0];
51
52 const nextIcon = (state, setState, icons) => {
53         const highest = highestActive(state, icons);
54         const highestIndex = highest ? icons.indexOf(highest) : -1;
55         if (highestIndex + 1 < icons.length) {
56                 setState(toggleBoolean(icons[highestIndex + 1]));
57         } else {
58                 const changes = {};
59                 icons.forEach(icon => {
60                         changes[icon] = false;
61                 });
62                 setState(s => ({ ...s, ...changes }));
63         }
64 };
65
66 const previousIcon = (state, setState, icons) => {
67         const highest = highestActive(state, icons);
68         const highestIndex = highest ? icons.indexOf(highest) : -1;
69         if (highestIndex >= 0) {
70                 setState(toggleBoolean(icons[highestIndex]));
71         } else {
72                 const changes = {};
73                 icons.forEach(icon => {
74                         changes[icon] = true;
75                 });
76                 setState(s => ({ ...s, ...changes }));
77         }
78 };
79
80 const nextString = property => (state, setState, icons) => {
81         const current = state[property] || icons[0];
82         const currentIndex = icons.indexOf(current);
83         const nextIndex = (currentIndex + 1) % icons.length;
84         const next = icons[nextIndex];
85         setState(s => ({ ...s, [property]: next }));
86 };
87
88 const previousString = property => (state, setState, icons) => {
89         const current = state[property] || icons[0];
90         const currentIndex = icons.indexOf(current);
91         const previousIndex = (currentIndex + icons.length - 1) % icons.length;
92         const previous = icons[previousIndex];
93         setState(s => ({ ...s, [property]: previous }));
94 };
95
96 ToggleIcon.countController = max => ({
97         getActive: highestActive,
98         getDefault: firstIcon,
99         handlePrimary: (state, setState, icons) => {
100                 setState(increment(icons[0], max));
101         },
102         handleSecondary: (state, setState, icons) => {
103                 setState(decrement(icons[0], max));
104         },
105 });
106
107 ToggleIcon.dungeonBossController = (dungeon) => ({
108         getActive: (state) => hasDungeonBoss(state, dungeon) ? getDungeonBoss(state, dungeon) : null,
109         getDefault: (state) => getDungeonBoss(state, dungeon),
110         handlePrimary: dungeon.bosses.length > 1
111                 ? nextString(`${dungeon.id}-boss`)
112                 : (state, setState) => {
113                         setState(toggleBoolean(`${dungeon.id}-boss-defeated`));
114                 },
115         handleSecondary: dungeon.bosses.length > 1 ?
116                 previousString(`${dungeon.id}-boss`)
117                 : (state, setState) => {
118                         setState(toggleBoolean(`${dungeon.id}-boss-defeated`));
119                 },
120 });
121
122 ToggleIcon.dungeonCheckController = (dungeon, max) => ({
123         getActive: (state, icons) => state[`${dungeon.id}-checks`] < max ? icons[1] : null,
124         getDefault: firstIcon,
125         handlePrimary: (state, setState) => {
126                 setState(increment(`${dungeon.id}-checks`, max));
127         },
128         handleSecondary: (state, setState) => {
129                 setState(decrement(`${dungeon.id}-checks`, max));
130         },
131 });
132
133 ToggleIcon.dungeonController = dungeon => ({
134         getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
135         getDefault: firstIcon,
136         handlePrimary: (state, setState, icons) => {
137                 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
138         },
139         handleSecondary: (state, setState, icons) => {
140                 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
141         },
142 });
143
144 ToggleIcon.dungeonCountController = (dungeon, max) => ({
145         getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
146         getDefault: firstIcon,
147         handlePrimary: (state, setState, icons) => {
148                 setState(increment(`${dungeon.id}-${icons[0]}`, max));
149         },
150         handleSecondary: (state, setState, icons) => {
151                 setState(decrement(`${dungeon.id}-${icons[0]}`, max));
152         },
153 });
154
155 ToggleIcon.dungeonPrizeController = (dungeon) => ({
156         getActive: (state) => hasDungeonPrize(state, dungeon) ? getDungeonPrize(state, dungeon) : null,
157         getDefault: (state) => getDungeonPrize(state, dungeon),
158         handlePrimary: nextString(`${dungeon.id}-prize`),
159         handleSecondary: previousString(`${dungeon.id}-prize`),
160 });
161
162 ToggleIcon.medallionController = {
163         getActive: highestActive,
164         getDefault: firstIcon,
165         handlePrimary: nextIcon,
166         handleSecondary: (state, setState, icons) => {
167                 const mm = state['mm-medallion'];
168                 const tr = state['tr-medallion'];
169                 const isMM = mm === icons[0];
170                 const isTR = tr === icons[0];
171                 console.log({ mm, isMM, tr, isTR });
172                 if (!isMM && !isTR) {
173                         // empty: set as MM if mire is unset, else set as TR if TR is unset
174                         if (!mm) {
175                                 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
176                         } else if (!tr) {
177                                 setState(s => ({ ...s, 'tr-medallion': icons[0] }));
178                         }
179                 } else if (isMM && !isTR) {
180                         // MM: if TR is free, switch to TR, otherwise remove MM
181                         if (!tr) {
182                                 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': icons[0] }));
183                         } else {
184                                 setState(s => ({ ...s, 'mm-medallion': null }));
185                         }
186                 } else if (!isMM && isTR) {
187                         // TR: if MM is free, switch to both, otherwise remove TR
188                         if (!mm) {
189                                 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
190                         } else {
191                                 setState(s => ({ ...s, 'tr-medallion': null }));
192                         }
193                 } else {
194                         // both: remove both
195                         setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': null }));
196                 }
197         },
198 };
199
200 ToggleIcon.modulusController = ctrl => ({
201         getActive: (state, icons) => icons[(state[ctrl] || 0) % icons.length],
202         getDefault: firstIcon,
203         handlePrimary: (state, setState, icons) => {
204                 setState(increment(icons[0], icons.length));
205         },
206         handleSecondary: (state, setState, icons) => {
207                 setState(decrement(icons[0], icons.length));
208         },
209 });
210
211 ToggleIcon.nullController = {
212         getActive: () => null,
213         getDefault: firstIcon,
214         handlePrimary: doNothing,
215         handleSecondary: doNothing,
216 };
217
218 ToggleIcon.simpleController = {
219         getActive: highestActive,
220         getDefault: firstIcon,
221         handlePrimary: nextIcon,
222         handleSecondary: previousIcon,
223 };
224
225 ToggleIcon.progressiveController = (master, min, max) => ({
226         getActive: (state, icons) => {
227                 const count = Math.max(min, Math.min(max, state[master] || 0));
228                 return count ? icons[count - 1] : null;
229         },
230         getDefault: firstIcon,
231         handlePrimary: (state, setState) => {
232                 setState(increment(master, max, min));
233         },
234         handleSecondary: (state, setState) => {
235                 setState(decrement(master, max, min));
236         },
237 });
238
239 ToggleIcon.propTypes = {
240         active: PropTypes.string,
241         className: PropTypes.string,
242         controller: PropTypes.shape({
243                 handlePrimary: PropTypes.func,
244                 handleSecondary: PropTypes.func,
245         }),
246         icons: PropTypes.arrayOf(PropTypes.string),
247 };
248
249 export default ToggleIcon;