|
11 | 11 | type RecoveryWord = { |
12 | 12 | value: string; |
13 | 13 | isValid: boolean; |
| 14 | + showContent: boolean; |
14 | 15 | }; |
15 | 16 |
|
16 | 17 | const englishWordList = (wordlists.english as string[]) ?? []; |
|
22 | 23 | Array.from({ length: 24 }, () => ({ |
23 | 24 | value: "", |
24 | 25 | isValid: true, |
| 26 | + showContent: false, |
25 | 27 | })), |
26 | 28 | ); |
27 | 29 |
|
| 30 | + let showAll = $state(false); |
| 31 | +
|
28 | 32 | // Flag to prevent double-triggering recovery on multiple blur events |
29 | 33 | let recoveryInProgress = $state(false); |
30 | 34 |
|
|
137 | 141 | pastedWords.forEach((word, i) => { |
138 | 142 | const targetIndex = currentIndex + i; |
139 | 143 | if (targetIndex < words.length) { |
140 | | - words[targetIndex].value = word.trim(); |
| 144 | + words[targetIndex].value = word.trim().toLowerCase(); |
141 | 145 | words[targetIndex].isValid = true; |
142 | 146 | } |
143 | 147 | }); |
|
160 | 164 | } |
161 | 165 | }; |
162 | 166 |
|
| 167 | + const toggleAll = () => { |
| 168 | + showAll = !showAll; |
| 169 | + if (!showAll) { |
| 170 | + words.forEach((word) => { |
| 171 | + word.showContent = false; |
| 172 | + }); |
| 173 | + } |
| 174 | + }; |
| 175 | +
|
| 176 | + const handleClearAll = () => { |
| 177 | + if (submitTimeoutId !== undefined) { |
| 178 | + clearTimeout(submitTimeoutId); |
| 179 | + submitTimeoutId = undefined; |
| 180 | + } |
| 181 | +
|
| 182 | + showAll = false; |
| 183 | +
|
| 184 | + words.forEach((word) => { |
| 185 | + word.value = ""; |
| 186 | + word.isValid = true; |
| 187 | + word.showContent = false; |
| 188 | + }); |
| 189 | +
|
| 190 | + const firstElement = document.getElementById("recovery-phrase-0"); |
| 191 | + if (nonNullish(firstElement)) { |
| 192 | + firstElement.focus(); |
| 193 | + } |
| 194 | + }; |
| 195 | +
|
163 | 196 | // Cleanup timeout on component unmount |
164 | 197 | $effect(() => { |
165 | 198 | return () => { |
|
188 | 221 | {#each words as word, i} |
189 | 222 | <label class="relative h-8"> |
190 | 223 | <!-- Text input --> |
| 224 | + <!-- "data-lpignore" Last pass ignore --> |
| 225 | + <!-- "data-1p-ignore" 1Password ignore --> |
| 226 | + <!-- "data-bwignore" Bitwarden ignore --> |
| 227 | + <!-- "data-form-type=other" Non-standard hint to password managers --> |
191 | 228 | <input |
192 | | - type="text" |
| 229 | + type={showAll || word.showContent || !word.isValid |
| 230 | + ? "text" |
| 231 | + : "password"} |
| 232 | + inputmode="text" |
| 233 | + autocorrect="off" |
| 234 | + autocomplete="off" |
| 235 | + autocapitalize="off" |
| 236 | + spellcheck="false" |
193 | 237 | id={`recovery-phrase-${i}`} |
194 | | - bind:value={word.value} |
| 238 | + value={word.value} |
| 239 | + oninput={(event) => { |
| 240 | + const target = event.currentTarget as HTMLInputElement; |
| 241 | + word.value = target.value.toLowerCase(); |
| 242 | + }} |
195 | 243 | onkeydown={(e) => handleKeyDownInput(e, i)} |
196 | 244 | onpaste={(e) => handlePaste(e, i)} |
197 | | - onblur={() => validateWord(i)} |
| 245 | + data-lpignore="true" |
| 246 | + data-1p-ignore="true" |
| 247 | + data-bwignore="true" |
| 248 | + data-form-type="other" |
| 249 | + onfocus={() => (word.showContent = true)} |
| 250 | + onblur={() => { |
| 251 | + validateWord(i); |
| 252 | + word.showContent = false; |
| 253 | + }} |
198 | 254 | aria-invalid={!word.isValid} |
199 | 255 | class={`peer text-text-primary h-8 w-full rounded-full border-none pr-10 pl-10 text-base ring outline-none ring-inset focus:ring-2 ${ |
200 | 256 | word.isValid |
|
231 | 287 | {/each} |
232 | 288 | </div> |
233 | 289 | <div class="flex flex-row gap-2"> |
234 | | - <Button class="w-full" variant="tertiary">{$t`Show all`}</Button> |
235 | | - <Button class="w-full" variant="tertiary">{$t`Clear all`}</Button> |
| 290 | + <Button class="w-full" variant="tertiary" onclick={toggleAll}> |
| 291 | + {#if showAll} |
| 292 | + {$t`Hide all`} |
| 293 | + {:else} |
| 294 | + {$t`Show all`} |
| 295 | + {/if} |
| 296 | + </Button> |
| 297 | + <Button class="w-full" variant="tertiary" onclick={handleClearAll}> |
| 298 | + {$t`Clear all`} |
| 299 | + </Button> |
236 | 300 | </div> |
237 | 301 | </div> |
238 | 302 | <Button size="xl" variant="secondary">{$t`Cancel`}</Button> |
|
0 commit comments