this repo has no description

Improve signal logic, fix bad typings

+4 -102
+4 -102
public/main.tsx
··· 25 25 26 26 export interface KinkCategory { 27 27 name: string; 28 - description: string; 28 + description: string | undefined; 29 29 kinks: Kink[]; 30 30 participants: string[]; 31 31 } 32 32 33 - /** 34 - * @param {string} kinkStr 35 - */ 36 33 function parseKinks(kinkStr: string) { 37 34 const kinkCode = kinkStr 38 35 .split("\n") 39 36 .map((e) => e.trim()) 40 37 .filter((e) => e); 41 38 42 - /** @type {KinkCategory[]} */ 43 39 const kinkCategories: KinkCategory[] = []; 44 40 45 - /** @type {Kink[]} */ 46 41 const kinksById: Kink[] = []; 47 42 48 - let curKinkCategory: Partial<KinkCategory> | undefined; 43 + let curKinkCategory: KinkCategory | undefined; 49 44 let curKinkId = 0; 50 45 for (const line of kinkCode) { 51 46 if (line.startsWith("#")) { ··· 57 52 kinks: [], 58 53 participants: ["Unknown"], 59 54 }; 60 - // @ts-ignore 61 55 kinkCategories.push(curKinkCategory); 62 56 } else if (line.startsWith("(") && line.endsWith(")")) { 63 57 if (curKinkCategory === undefined) { ··· 90 84 */ 91 85 const kinkSelections: Map<Kink, Map<string, Signal<Choice>>> = new Map(); 92 86 93 - /** 94 - * Maps kink -> participant -> choice option -> button element 95 - * Entries may be undefined! 96 - */ 97 - const kinkButtons: Map<Kink, Map<string, Map<string, (action: "select" | "deselect") => void>>> = new Map(); 98 - 99 - /** 100 - * @param {Kink} kink 101 - * @returns {KinkCategory | undefined} 102 - */ 103 87 function findKinkCategory(kink: Kink): KinkCategory | undefined { 104 88 for (const category of kinkData.value.kinkCategories) { 105 89 const index = category.kinks.indexOf(kink); ··· 115 99 typeDescription: string; 116 100 } 117 101 118 - /** 119 - * @param {LegendChoiceProps} props 120 - * @returns 121 - */ 122 102 function LegendChoice({ type, typeDescription }: LegendChoiceProps) { 123 103 return ( 124 104 <div class="level-item is-justify-content-flex-start"> ··· 138 118 ); 139 119 } 140 120 141 - /** 142 - * @param {string} str 143 - * @param {string} char 144 - * @returns {[string, string?]} 145 - */ 146 121 function sliceOnce(str: string, char: string): [string, string?] { 147 122 const index = str.indexOf(char); 148 123 ··· 155 130 return [str.trim(), undefined]; 156 131 } 157 132 158 - /** 159 - * @param {string} str 160 - * @param {string} symbolStart 161 - * @param {string?} symbolEnd 162 - * @returns {string} 163 - */ 164 133 function removeSymbols(str: string, symbolStart: string, symbolEnd: string | null = null): string { 165 134 if (str.startsWith(symbolStart)) { 166 135 str = str.slice(1); ··· 175 144 176 145 const base64Alphabet = [..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"]; // base64 alphabet 177 146 178 - /** 179 - * @param {number} n 180 - * @returns {string} 181 - */ 182 147 const toBinary = (n: number): string => n.toString(2).padStart(8, "0"); // convert num to 8-bit binary string 183 148 184 149 /** 185 150 * https://stackoverflow.com/a/62362724 186 - * @param {number[] | Uint8Array} arr 187 - * @returns {string} 188 151 */ 189 152 function bytesArrToBase64(arr: number[] | Uint8Array): string { 190 153 // @ts-expect-error ··· 209 172 } 210 173 /** 211 174 * https://stackoverflow.com/a/62364519 212 - * @param {string} str 213 - * @returns {Uint8Array} 214 175 */ 215 176 function base64ToBytesArr(str: string): Uint8Array { 216 177 if (Uint8Array.fromBase64) { ··· 229 190 return new Uint8Array(result); 230 191 } 231 192 232 - /** 233 - * @param {Kink} kink 234 - * @param {string} participant 235 - * @returns {Signal<Choice>} 236 - */ 237 193 function getSelectedKinkOrDefault(kink: Kink, participant: string): Signal<Choice> { 238 194 let theKink = kinkSelections.get(kink); 239 195 if (!theKink) kinkSelections.set(kink, theKink = new Map()); ··· 242 198 return theSignal; 243 199 } 244 200 245 - /** 246 - * @param {Kink} kink 247 - * @param {string} participant 248 - * @param {string} toChoiceId 249 - */ 250 201 function setKinkSelection(kink: Kink, participant: string, toChoiceId: Choice, updateHash = true) { 251 202 getSelectedKinkOrDefault(kink, participant).value = toChoiceId; 252 203 253 - for (const [thatChoiceId, thatButton] of getKinkButtonStates(kink, participant).entries()) { 254 - if (thatChoiceId === toChoiceId) { 255 - thatButton("select"); 256 - } else { 257 - thatButton("deselect"); 258 - } 259 - } 260 - 261 204 if (updateHash) { 262 205 window.location.hash = serializeChoices(); 263 206 } ··· 345 288 choiceDescription: string; 346 289 } 347 290 348 - /** 349 - * @param {KinkChoiceButtonProps} props 350 - * @returns 351 - */ 352 291 function KinkChoiceButton({ kink, participant, choiceId, choiceDescription }: KinkChoiceButtonProps) { 353 - /** 354 - * @param {boolean} state 355 - * @param {'select' | 'deselect'} action 356 - * @returns {boolean} 357 - */ 358 - function reducer(state: boolean, action: "select" | "deselect"): boolean { 359 - if (action === "select") { 360 - return true; 361 - } else if (action === "deselect") { 362 - return false; 363 - } 364 - return state; 365 - } 366 - 367 - const [selected, update] = useReducer(reducer, false); 368 - 369 - let participants = kinkButtons.get(kink); 370 - if (participants === undefined) { 371 - kinkButtons.set(kink, (participants = new Map())); 372 - } 373 - 374 - let choices = participants.get(participant); 375 - if (choices === undefined) { 376 - participants.set(participant, (choices = new Map())); 377 - } 378 - 379 - choices.set(choiceId, update); 292 + const selected = computed(() => getSelectedKinkOrDefault(kink, participant).value === choiceId); 380 293 381 294 return ( 382 295 <div class="column"> 383 296 <button 384 - class={`choice ${choiceId}${selected ? " selected" : ""}`} 297 + class={`choice ${choiceId}${selected.value ? " selected" : ""}`} 385 298 title={choiceDescription} 386 299 onClick={() => { 387 300 setKinkSelection(kink, participant, choiceId); ··· 389 302 /> 390 303 </div> 391 304 ); 392 - } 393 - 394 - /** 395 - * Gets the mappings from choice->button element for a kink+participant combo. 396 - * @param {Kink} kink 397 - * @param {string} participant 398 - * @returns {Map<string, (action: "select" | "deselect") => void>} 399 - */ 400 - function getKinkButtonStates(kink: Kink, participant: string): Map<string, (action: "select" | "deselect") => void> { 401 - // console.log(kinkButtons, kink, participant); 402 - return kinkButtons.get(kink)!.get(participant)!; 403 305 } 404 306 405 307 function TheKink({ kinkCategory, kink }: { kinkCategory: KinkCategory; kink: Kink }) {