milwright commited on
Commit
541ff38
·
1 Parent(s): 04bba1d

add text input option to initials modal for easier entry

Browse files

- adds text input field alongside existing arcade controls
- both input methods stay synchronized
- improves mobile usability and accessibility
- maintains vintage aesthetic with arcade-style fallback

Files changed (2) hide show
  1. src/leaderboardUI.js +65 -0
  2. src/styles.css +79 -0
src/leaderboardUI.js CHANGED
@@ -182,6 +182,18 @@ export class LeaderboardUI {
182
  <div class="initials-content">
183
  <p class="initials-prompt">Enter or update your initials:</p>
184
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  <div class="initials-slots">
186
  ${this.initials.map((letter, index) => `
187
  <div class="initial-slot ${index === 0 ? 'active' : ''}" data-slot="${index}">
@@ -218,6 +230,12 @@ export class LeaderboardUI {
218
  // from immediately triggering the modal's submit handler
219
  setTimeout(() => {
220
  this.setupInitialsEventListeners();
 
 
 
 
 
 
221
  // Enable submission after a longer delay to ensure user has time to interact
222
  setTimeout(() => {
223
  this.canSubmitInitials = true;
@@ -250,6 +268,16 @@ export class LeaderboardUI {
250
  * Setup event listeners for initials entry
251
  */
252
  setupInitialsEventListeners() {
 
 
 
 
 
 
 
 
 
 
253
  // Arrow buttons
254
  this.initialsModal.querySelectorAll('.arrow-up, .arrow-down').forEach(button => {
255
  button.addEventListener('click', (e) => {
@@ -276,6 +304,22 @@ export class LeaderboardUI {
276
 
277
  // Keyboard controls
278
  this.initialsKeyHandler = (e) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  switch(e.key) {
280
  case 'ArrowUp':
281
  e.preventDefault();
@@ -325,6 +369,7 @@ export class LeaderboardUI {
325
 
326
  this.initials[slot] = String.fromCharCode(newChar);
327
  this.updateInitialsDisplay();
 
328
  }
329
 
330
  /**
@@ -337,6 +382,26 @@ export class LeaderboardUI {
337
  });
338
  }
339
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  /**
341
  * Update the visual display of initials
342
  */
 
182
  <div class="initials-content">
183
  <p class="initials-prompt">Enter or update your initials:</p>
184
 
185
+ <!-- Text Input Method -->
186
+ <div class="text-input-section">
187
+ <input type="text" id="initials-text-input" class="initials-text-input" maxlength="3" value="${this.initials.join('')}" placeholder="ABC">
188
+ <p class="input-help">Type your 3-letter initials directly</p>
189
+ </div>
190
+
191
+ <!-- Divider -->
192
+ <div class="input-divider">
193
+ <span>or use arcade controls</span>
194
+ </div>
195
+
196
+ <!-- Arcade Style Method -->
197
  <div class="initials-slots">
198
  ${this.initials.map((letter, index) => `
199
  <div class="initial-slot ${index === 0 ? 'active' : ''}" data-slot="${index}">
 
230
  // from immediately triggering the modal's submit handler
231
  setTimeout(() => {
232
  this.setupInitialsEventListeners();
233
+ // Focus the text input for easier typing
234
+ const textInput = this.initialsModal.querySelector('#initials-text-input');
235
+ if (textInput) {
236
+ textInput.focus();
237
+ textInput.select(); // Select all text for easy overwriting
238
+ }
239
  // Enable submission after a longer delay to ensure user has time to interact
240
  setTimeout(() => {
241
  this.canSubmitInitials = true;
 
268
  * Setup event listeners for initials entry
269
  */
270
  setupInitialsEventListeners() {
271
+ // Text input field
272
+ const textInput = this.initialsModal.querySelector('#initials-text-input');
273
+ textInput.addEventListener('input', (e) => {
274
+ const value = e.target.value.toUpperCase().slice(0, 3);
275
+ e.target.value = value;
276
+
277
+ // Update arcade slots to match text input
278
+ this.updateInitialsFromText(value);
279
+ });
280
+
281
  // Arrow buttons
282
  this.initialsModal.querySelectorAll('.arrow-up, .arrow-down').forEach(button => {
283
  button.addEventListener('click', (e) => {
 
304
 
305
  // Keyboard controls
306
  this.initialsKeyHandler = (e) => {
307
+ // If focus is on text input, handle differently
308
+ if (e.target.id === 'initials-text-input') {
309
+ switch(e.key) {
310
+ case 'Enter':
311
+ e.preventDefault();
312
+ this.submitInitials();
313
+ break;
314
+ case 'Escape':
315
+ e.preventDefault();
316
+ this.hideInitialsEntry();
317
+ break;
318
+ }
319
+ return;
320
+ }
321
+
322
+ // Arcade controls when not focused on text input
323
  switch(e.key) {
324
  case 'ArrowUp':
325
  e.preventDefault();
 
369
 
370
  this.initials[slot] = String.fromCharCode(newChar);
371
  this.updateInitialsDisplay();
372
+ this.updateTextFromInitials();
373
  }
374
 
375
  /**
 
382
  });
383
  }
384
 
385
+ /**
386
+ * Update arcade slots from text input
387
+ */
388
+ updateInitialsFromText(text) {
389
+ // Pad with 'A' if less than 3 characters
390
+ const paddedText = text.padEnd(3, 'A');
391
+ this.initials = paddedText.split('');
392
+ this.updateInitialsDisplay();
393
+ }
394
+
395
+ /**
396
+ * Update text input from arcade slots
397
+ */
398
+ updateTextFromInitials() {
399
+ const textInput = this.initialsModal.querySelector('#initials-text-input');
400
+ if (textInput) {
401
+ textInput.value = this.initials.join('');
402
+ }
403
+ }
404
+
405
  /**
406
  * Update the visual display of initials
407
  */
src/styles.css CHANGED
@@ -446,6 +446,9 @@
446
  box-shadow:
447
  0 3px 0 rgba(0, 0, 0, 0.3),
448
  0 4px 8px rgba(0, 0, 0, 0.1);
 
 
 
449
  }
450
 
451
  .leaderboard-footer-btn:hover {
@@ -982,6 +985,67 @@
982
  }
983
 
984
  /* Initials Instructions */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
985
  .initials-instructions {
986
  margin-bottom: 24px;
987
  color: #000000;
@@ -1090,6 +1154,21 @@
1090
  .initials-slots {
1091
  gap: 12px;
1092
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1093
  }
1094
 
1095
  /* Print styles */
 
446
  box-shadow:
447
  0 3px 0 rgba(0, 0, 0, 0.3),
448
  0 4px 8px rgba(0, 0, 0, 0.1);
449
+ touch-action: manipulation;
450
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0.1);
451
+ user-select: none;
452
  }
453
 
454
  .leaderboard-footer-btn:hover {
 
985
  }
986
 
987
  /* Initials Instructions */
988
+ /* Text Input Section */
989
+ .text-input-section {
990
+ display: flex;
991
+ flex-direction: column;
992
+ align-items: center;
993
+ margin-bottom: 20px;
994
+ }
995
+
996
+ .initials-text-input {
997
+ width: 120px;
998
+ height: 64px;
999
+ font-size: 32px;
1000
+ font-weight: 700;
1001
+ font-family: 'Courier New', monospace;
1002
+ text-align: center;
1003
+ text-transform: uppercase;
1004
+ letter-spacing: 8px;
1005
+ background: #ffffff;
1006
+ border: 3px solid #000000;
1007
+ border-radius: 8px;
1008
+ box-shadow: 0 4px 0 rgba(0, 0, 0, 0.5);
1009
+ transition: all 0.2s ease;
1010
+ margin-bottom: 8px;
1011
+ }
1012
+
1013
+ .initials-text-input:focus {
1014
+ outline: none;
1015
+ border-color: #2563eb;
1016
+ background: #eff6ff;
1017
+ box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.3), 0 4px 0 rgba(0, 0, 0, 0.5);
1018
+ }
1019
+
1020
+ .input-help {
1021
+ font-size: 12px;
1022
+ color: #666;
1023
+ margin: 0;
1024
+ font-weight: 400;
1025
+ }
1026
+
1027
+ /* Divider */
1028
+ .input-divider {
1029
+ display: flex;
1030
+ align-items: center;
1031
+ margin: 20px 0;
1032
+ color: #666;
1033
+ font-size: 14px;
1034
+ font-weight: 500;
1035
+ }
1036
+
1037
+ .input-divider::before,
1038
+ .input-divider::after {
1039
+ content: '';
1040
+ flex: 1;
1041
+ height: 1px;
1042
+ background: #ddd;
1043
+ }
1044
+
1045
+ .input-divider span {
1046
+ padding: 0 16px;
1047
+ }
1048
+
1049
  .initials-instructions {
1050
  margin-bottom: 24px;
1051
  color: #000000;
 
1154
  .initials-slots {
1155
  gap: 12px;
1156
  }
1157
+
1158
+ .initials-text-input {
1159
+ width: 100px;
1160
+ height: 56px;
1161
+ font-size: 28px;
1162
+ letter-spacing: 6px;
1163
+ }
1164
+
1165
+ .input-divider {
1166
+ font-size: 12px;
1167
+ }
1168
+
1169
+ .input-help {
1170
+ font-size: 11px;
1171
+ }
1172
  }
1173
 
1174
  /* Print styles */