1 import PropTypes from 'prop-types';
2 import React from 'react';
4 import ZeldaIcon from '../common/ZeldaIcon';
14 } from '../../helpers/tracker';
15 import { useTracker } from '../../hooks/tracker';
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'];
24 classNames.push('active');
26 classNames.push('inactive');
29 classNames.push(className);
32 className={classNames.join(' ')}
34 activeController.handlePrimary(state, setState, icons);
38 onContextMenu={(e) => {
39 activeController.handleSecondary(state, setState, icons);
44 <ZeldaIcon name={active || defaultIcon || icons[0]} />
48 const doNothing = () => { };
50 const firstIcon = (state, icons) => icons[0];
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]));
59 icons.forEach(icon => {
60 changes[icon] = false;
62 setState(s => ({ ...s, ...changes }));
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]));
73 icons.forEach(icon => {
76 setState(s => ({ ...s, ...changes }));
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 }));
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 }));
96 ToggleIcon.countController = max => ({
97 getActive: highestActive,
98 getDefault: firstIcon,
99 handlePrimary: (state, setState, icons) => {
100 setState(increment(icons[0], max));
102 handleSecondary: (state, setState, icons) => {
103 setState(decrement(icons[0], max));
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`));
115 handleSecondary: dungeon.bosses.length > 1 ?
116 previousString(`${dungeon.id}-boss`)
117 : (state, setState) => {
118 setState(toggleBoolean(`${dungeon.id}-boss-defeated`));
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));
128 handleSecondary: (state, setState) => {
129 setState(decrement(`${dungeon.id}-checks`, max));
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]}`));
139 handleSecondary: (state, setState, icons) => {
140 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
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));
150 handleSecondary: (state, setState, icons) => {
151 setState(decrement(`${dungeon.id}-${icons[0]}`, max));
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`),
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
175 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
177 setState(s => ({ ...s, 'tr-medallion': icons[0] }));
179 } else if (isMM && !isTR) {
180 // MM: if TR is free, switch to TR, otherwise remove MM
182 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': icons[0] }));
184 setState(s => ({ ...s, 'mm-medallion': null }));
186 } else if (!isMM && isTR) {
187 // TR: if MM is free, switch to both, otherwise remove TR
189 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
191 setState(s => ({ ...s, 'tr-medallion': null }));
195 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': null }));
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));
206 handleSecondary: (state, setState, icons) => {
207 setState(decrement(icons[0], icons.length));
211 ToggleIcon.nullController = {
212 getActive: () => null,
213 getDefault: firstIcon,
214 handlePrimary: doNothing,
215 handleSecondary: doNothing,
218 ToggleIcon.simpleController = {
219 getActive: highestActive,
220 getDefault: firstIcon,
221 handlePrimary: nextIcon,
222 handleSecondary: previousIcon,
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;
230 getDefault: firstIcon,
231 handlePrimary: (state, setState) => {
232 setState(increment(master, max, min));
234 handleSecondary: (state, setState) => {
235 setState(decrement(master, max, min));
239 ToggleIcon.propTypes = {
240 active: PropTypes.string,
241 className: PropTypes.string,
242 controller: PropTypes.shape({
243 handlePrimary: PropTypes.func,
244 handleSecondary: PropTypes.func,
246 icons: PropTypes.arrayOf(PropTypes.string),
249 export default ToggleIcon;