Skip to content

Commit cbf9b8a

Browse files
committed
Add Controls combo framework and editable Account user controls - PR_26162_042-controls-combo-framework-and-user-control-editing
1 parent ef56c9d commit cbf9b8a

10 files changed

Lines changed: 984 additions & 2403 deletions

‎account/user-controls-page.js‎

Lines changed: 118 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ export class AccountUserControlsPage {
130130
defaultLists: [...root.querySelectorAll("[data-account-user-controls-default-list]")],
131131
deviceSelect: root.querySelector("[data-account-user-controls-device]"),
132132
deviceStatus: root.querySelector("[data-account-user-controls-device-status]"),
133-
list: root.querySelector("[data-account-user-controls-list]"),
133+
familyButtons: [...root.querySelectorAll("[data-account-user-controls-edit-family]")],
134+
lists: [...root.querySelectorAll("[data-account-user-controls-list]")],
134135
refresh: root.querySelector("[data-account-user-controls-refresh]"),
135136
saveAll: root.querySelector("[data-account-user-controls-save-all]"),
136137
status: root.querySelector("[data-account-user-controls-status]"),
@@ -152,10 +153,15 @@ export class AccountUserControlsPage {
152153
this.elements.addProfile?.addEventListener("click",()=>this.addProfileForSelectedDevice());
153154
this.elements.saveAll?.addEventListener("click",()=>this.saveCurrentState());
154155
this.elements.deviceSelect?.addEventListener("change",()=>this.renderDeviceStatus());
155-
this.elements.list?.addEventListener("click",(event)=>this.handleListClick(event));
156-
this.elements.list?.addEventListener("change",(event)=>this.handleListChange(event));
157-
this.elements.list?.addEventListener("input",(event)=>this.handleListInput(event));
158-
this.elements.list?.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
156+
this.elements.familyButtons.forEach((button)=>{
157+
button.addEventListener("click",()=>this.editFamilyMappings(button.dataset.accountUserControlsEditFamily||""));
158+
});
159+
this.elements.lists.forEach((list)=>{
160+
list.addEventListener("click",(event)=>this.handleListClick(event));
161+
list.addEventListener("change",(event)=>this.handleListChange(event));
162+
list.addEventListener("input",(event)=>this.handleListInput(event));
163+
list.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
164+
});
159165
this.startDevicePolling();
160166
}
161167

@@ -177,25 +183,7 @@ export class AccountUserControlsPage {
177183
}
178184

179185
deviceOptions(){
180-
constkeyboard={
181-
controllerId: "generic-keyboard",
182-
controllerName: "Keyboard",
183-
deviceType: "Keyboard",
184-
inputs: [...KEYBOARD_INPUTS],
185-
label: "Keyboard",
186-
mappingProfile: "Keyboard Profile",
187-
value: "keyboard",
188-
};
189-
constmouse={
190-
controllerId: "generic-mouse",
191-
controllerName: "Mouse",
192-
deviceType: "Mouse",
193-
inputs: [...MOUSE_INPUTS],
194-
label: "Mouse",
195-
mappingProfile: "Mouse Profile",
196-
value: "mouse",
197-
};
198-
constgamepads=this.availableGamepads().map((gamepad)=>{
186+
returnthis.availableGamepads().map((gamepad)=>{
199187
constcontrollerName=gamepad.id||`Gamepad ${gamepad.index}`;
200188
return{
201189
controllerId: gamepad.id||`gamepad-${gamepad.index}`,
@@ -207,15 +195,37 @@ export class AccountUserControlsPage {
207195
value: `gamepad-${gamepad.index}`,
208196
};
209197
});
210-
return[keyboard,mouse, ...gamepads];
198+
}
199+
200+
familyDevice(family){
201+
if(family==="Mouse"){
202+
return{
203+
controllerId: "generic-mouse",
204+
controllerName: "Mouse",
205+
deviceType: "Mouse",
206+
inputs: [...MOUSE_INPUTS],
207+
label: "Mouse",
208+
mappingProfile: "Mouse Profile",
209+
value: "mouse",
210+
};
211+
}
212+
return{
213+
controllerId: "generic-keyboard",
214+
controllerName: "Keyboard",
215+
deviceType: "Keyboard",
216+
inputs: [...KEYBOARD_INPUTS],
217+
label: "Keyboard",
218+
mappingProfile: "Keyboard Profile",
219+
value: "keyboard",
220+
};
211221
}
212222

213223
deviceRefreshMessage(){
214224
constgamepadCount=this.availableGamepads().length;
215225
if(gamepadCount>0){
216-
return`PASS: Keyboard and Mouse available. ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
226+
return`PASS: ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
217227
}
218-
return"PASS: Keyboard and Mouse available. Gamepads auto-detect after browser exposes them.";
228+
return"PASS: Game controllers auto-detect after the browser exposes them.";
219229
}
220230

221231
selectedDevice(){
@@ -287,7 +297,7 @@ export class AccountUserControlsPage {
287297
}
288298
constselectedValue=this.elements.deviceSelect.value;
289299
constoptions=[
290-
{label: "Choose a physical controller",value: ""},
300+
{label: "Choose a game controller",value: ""},
291301
...this.deviceOptions().map((device)=>({label: device.label,value: device.value})),
292302
];
293303
this.elements.deviceSelect.classList.add("tool-form-control");
@@ -368,30 +378,62 @@ export class AccountUserControlsPage {
368378
},{once: true});
369379
}
370380

381+
profileListFamily(profile){
382+
constdeviceType=normalizeText(profile?.deviceType);
383+
if(deviceType==="Keyboard"||deviceType==="Mouse"){
384+
returndeviceType;
385+
}
386+
return"Gamepad";
387+
}
388+
389+
emptyProfileMessage(family){
390+
if(family==="Keyboard"){
391+
return"No keyboard user control profile saved yet.";
392+
}
393+
if(family==="Mouse"){
394+
return"No mouse user control profile saved yet.";
395+
}
396+
return"No game controller profiles saved yet.";
397+
}
398+
371399
renderProfiles(){
372-
if(!this.elements.list){
400+
if(!this.elements.lists.length){
373401
return;
374402
}
375-
constrows=[];
403+
constrowsByFamily=newMap(this.elements.lists.map((list)=>[
404+
list.dataset.accountUserControlsListFamily||"Gamepad",
405+
[],
406+
]));
376407
if(this.editingProfile){
377-
rows.push(...this.renderEditingRows(this.editingProfile.values));
408+
constfamily=this.profileListFamily(this.editingProfile.values);
409+
rowsByFamily.get(family)?.push(...this.renderEditingRows(this.editingProfile.values));
378410
}
379411
constvisibleProfiles=this.editingProfile?.id
380412
? this.profiles.filter((profile)=>profile.id!==this.editingProfile.id)
381413
: this.profiles;
382-
rows.push(...visibleProfiles.map((profile)=>this.renderProfileRow(profile)));
383-
if(!rows.length){
384-
constrow=document.createElement("tr");
385-
constcell=document.createElement("td");
386-
cell.colSpan=7;
387-
cell.textContent="No account user control profiles saved yet.";
388-
row.append(cell);
389-
rows.push(row);
390-
}
391-
this.elements.list.replaceChildren(...rows);
414+
visibleProfiles.forEach((profile)=>{
415+
constfamily=this.profileListFamily(profile);
416+
rowsByFamily.get(family)?.push(this.renderProfileRow(profile));
417+
});
418+
this.elements.lists.forEach((list)=>{
419+
constfamily=list.dataset.accountUserControlsListFamily||"Gamepad";
420+
constrows=rowsByFamily.get(family)||[];
421+
if(!rows.length){
422+
constrow=document.createElement("tr");
423+
constcell=document.createElement("td");
424+
cell.colSpan=7;
425+
cell.textContent=this.emptyProfileMessage(family);
426+
row.append(cell);
427+
rows.push(row);
428+
}
429+
list.replaceChildren(...rows);
430+
});
392431
if(this.elements.addProfile){
393432
this.elements.addProfile.disabled=Boolean(this.editingProfile);
394433
}
434+
this.elements.familyButtons.forEach((button)=>{
435+
button.disabled=Boolean(this.editingProfile);
436+
});
395437
}
396438

397439
profileInputSummary(profile){
@@ -455,8 +497,18 @@ export class AccountUserControlsPage {
455497
constwrapper=document.createElement("div");
456498
wrapper.className="content-grid";
457499
wrapper.dataset.accountUserControlsInputPair="true";
458-
constlabel=document.createElement("strong");
459-
label.textContent=inputMapping.physicalInput;
500+
consteditablePhysicalInput=profile.deviceType==="Keyboard"||profile.deviceType==="Mouse";
501+
constphysicalInputControl=editablePhysicalInput
502+
? document.createElement("input")
503+
: document.createElement("strong");
504+
if(editablePhysicalInput){
505+
physicalInputControl.type="text";
506+
physicalInputControl.value=inputMapping.physicalInput;
507+
physicalInputControl.dataset.accountUserControlsPhysicalInput=String(index);
508+
physicalInputControl.setAttribute("aria-label",`${profile.deviceType} physical input`);
509+
}else{
510+
physicalInputControl.textContent=inputMapping.physicalInput;
511+
}
460512
conststack=document.createElement("div");
461513
stack.className="content-stack content-stack--compact";
462514
constnormalizedOptions=[
@@ -522,7 +574,7 @@ export class AccountUserControlsPage {
522574
validation.className="status";
523575
validation.dataset.accountUserControlsInputValidation=String(index);
524576
stack.append(validation);
525-
wrapper.append(label,stack);
577+
wrapper.append(physicalInputControl,stack);
526578
returnwrapper;
527579
}
528580

@@ -579,24 +631,42 @@ export class AccountUserControlsPage {
579631
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
580632
}
581633

634+
editFamilyMappings(family){
635+
constnormalizedFamily=family==="Mouse" ? "Mouse" : "Keyboard";
636+
constdevice=this.familyDevice(normalizedFamily);
637+
constexisting=this.profiles.find((profile)=>profile.deviceType===device.deviceType&&profile.controllerId===device.controllerId);
638+
if(existing){
639+
this.editingProfile={id: existing.id,values: existing};
640+
this.renderProfiles();
641+
this.setStatus(`Editing existing ${existing.mappingProfile}.`);
642+
return;
643+
}
644+
constprofile=this.profileFromDevice(device);
645+
this.editingProfile={id: profile.id,values: profile};
646+
this.renderProfiles();
647+
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
648+
}
649+
582650
profileFromEditingRow(){
583651
constvalues=this.editingProfile?.values||{};
584-
constdetails=this.elements.list?.querySelector("[data-account-user-controls-editing-details]");
652+
constdetails=this.root.querySelector("[data-account-user-controls-editing-details]");
585653
constinputMappings=(values.inputMappings||[]).map((mapping,index)=>{
654+
constphysicalInputControl=details?.querySelector(`[data-account-user-controls-physical-input="${index}"]`);
586655
constnormalizedSelect=details?.querySelector(`[data-account-user-controls-input-normalized="${index}"]`);
587656
constnegativeSelect=details?.querySelector(`[data-account-user-controls-input-negative="${index}"]`);
588657
constpositiveSelect=details?.querySelector(`[data-account-user-controls-input-positive="${index}"]`);
589658
constdeadzoneInput=details?.querySelector(`[data-account-user-controls-deadzone="${index}"]`);
590659
constinvertInput=details?.querySelector(`[data-account-user-controls-invert="${index}"]`);
591660
constsensitivityInput=details?.querySelector(`[data-account-user-controls-sensitivity="${index}"]`);
661+
constphysicalInput=normalizeText(physicalInputControl?.value??mapping.physicalInput);
592662
constnegativeNormalizedInput=normalizeText(negativeSelect?.value??mapping.negativeNormalizedInput);
593663
constpositiveNormalizedInput=normalizeText(positiveSelect?.value??mapping.positiveNormalizedInput);
594664
return{
595665
deadzone: Number(deadzoneInput?.value??mapping.deadzone),
596666
invert: Boolean(invertInput?.checked??mapping.invert),
597667
negativeNormalizedInput,
598668
normalizedInput: positiveNormalizedInput||normalizeText(normalizedSelect?.value??mapping.normalizedInput)||negativeNormalizedInput,
599-
physicalInput: mapping.physicalInput,
669+
physicalInput,
600670
positiveNormalizedInput,
601671
sensitivity: sensitivityInput ? Number(sensitivityInput.value) : mapping.sensitivity,
602672
};
@@ -605,6 +675,7 @@ export class AccountUserControlsPage {
605675
...values,
606676
id: this.editingProfile?.id||"",
607677
inputMappings,
678+
inputs: inputMappings.map((mapping)=>mapping.physicalInput),
608679
});
609680
}
610681

@@ -662,7 +733,7 @@ export class AccountUserControlsPage {
662733

663734
renderInputValidation(validation){
664735
constinvalidIndexes=newSet(validation.invalidIndexes||[]);
665-
this.elements.list?.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
736+
this.root.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
666737
constindex=Number(status.dataset.accountUserControlsInputValidation);
667738
status.textContent=invalidIndexes.has(index)
668739
? "Needs normalized control, valid deadzone, and valid sensitivity."

‎account/user-controls.html‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@ <h2>Physical Input Mapping</h2>
4747
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Keyboard"></tbody>
4848
</table>
4949
</div>
50+
<divclass="content-cluster" aria-label="Keyboard user controls">
51+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Keyboard">Edit Keyboard Mappings</button>
52+
</div>
53+
<divclass="table-wrapper">
54+
<tableclass="data-table" aria-label="Keyboard user control profile" data-account-user-controls-table="Keyboard">
55+
<thead>
56+
<tr>
57+
<th>Physical Controller</th>
58+
<th>Physical Input</th>
59+
<th>Normalized Control</th>
60+
<th>Deadzone</th>
61+
<th>Invert</th>
62+
<th>Sensitivity</th>
63+
<th>Actions</th>
64+
</tr>
65+
</thead>
66+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Keyboard"></tbody>
67+
</table>
68+
</div>
5069
</div>
5170
</details>
5271
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Mouse">
@@ -65,6 +84,25 @@ <h2>Physical Input Mapping</h2>
6584
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Mouse"></tbody>
6685
</table>
6786
</div>
87+
<divclass="content-cluster" aria-label="Mouse user controls">
88+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Mouse">Edit Mouse Mappings</button>
89+
</div>
90+
<divclass="table-wrapper">
91+
<tableclass="data-table" aria-label="Mouse user control profile" data-account-user-controls-table="Mouse">
92+
<thead>
93+
<tr>
94+
<th>Physical Controller</th>
95+
<th>Physical Input</th>
96+
<th>Normalized Control</th>
97+
<th>Deadzone</th>
98+
<th>Invert</th>
99+
<th>Sensitivity</th>
100+
<th>Actions</th>
101+
</tr>
102+
</thead>
103+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Mouse"></tbody>
104+
</table>
105+
</div>
68106
</div>
69107
</details>
70108
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Game Controllers">
@@ -73,13 +111,13 @@ <h2>Physical Input Mapping</h2>
73111
<divclass="content-cluster" aria-label="Account controller device selection">
74112
<buttonclass="btn" type="button" data-account-user-controls-refresh>Refresh Devices</button>
75113
<label>
76-
Physical Controller
114+
Game Controller
77115
<selectdata-account-user-controls-device></select>
78116
</label>
79117
<buttonclass="btn" type="button" data-account-user-controls-add-profile>Create User Control Profile</button>
80118
<buttonclass="btn primary" type="button" data-account-user-controls-save-all>Save</button>
81119
</div>
82-
<pclass="status" role="status" data-account-user-controls-device-status>Keyboard and Mouse are available. Game controllers auto-detect after the browser exposes them.</p>
120+
<pclass="status" role="status" data-account-user-controls-device-status>Game controllers auto-detect after the browser exposes them.</p>
83121
<divclass="table-wrapper">
84122
<tableclass="data-table" aria-label="Account user control profiles" data-account-user-controls-table>
85123
<thead>
@@ -93,7 +131,7 @@ <h2>Physical Input Mapping</h2>
93131
<th>Actions</th>
94132
</tr>
95133
</thead>
96-
<tbodydata-account-user-controls-list></tbody>
134+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Gamepad"></tbody>
97135
</table>
98136
</div>
99137
</div>

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Add Controls combo framework and editable Account user controls - PR_… · ToolboxAid/HTML-JavaScript-Gaming@cbf9b8a · GitHub
Skip to content

Commit cbf9b8a

Browse files
committed
Add Controls combo framework and editable Account user controls - PR_26162_042-controls-combo-framework-and-user-control-editing
1 parent ef56c9d commit cbf9b8a

10 files changed

Lines changed: 984 additions & 2403 deletions

‎account/user-controls-page.js‎

Lines changed: 118 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ export class AccountUserControlsPage {
130130
defaultLists: [...root.querySelectorAll("[data-account-user-controls-default-list]")],
131131
deviceSelect: root.querySelector("[data-account-user-controls-device]"),
132132
deviceStatus: root.querySelector("[data-account-user-controls-device-status]"),
133-
list: root.querySelector("[data-account-user-controls-list]"),
133+
familyButtons: [...root.querySelectorAll("[data-account-user-controls-edit-family]")],
134+
lists: [...root.querySelectorAll("[data-account-user-controls-list]")],
134135
refresh: root.querySelector("[data-account-user-controls-refresh]"),
135136
saveAll: root.querySelector("[data-account-user-controls-save-all]"),
136137
status: root.querySelector("[data-account-user-controls-status]"),
@@ -152,10 +153,15 @@ export class AccountUserControlsPage {
152153
this.elements.addProfile?.addEventListener("click",()=>this.addProfileForSelectedDevice());
153154
this.elements.saveAll?.addEventListener("click",()=>this.saveCurrentState());
154155
this.elements.deviceSelect?.addEventListener("change",()=>this.renderDeviceStatus());
155-
this.elements.list?.addEventListener("click",(event)=>this.handleListClick(event));
156-
this.elements.list?.addEventListener("change",(event)=>this.handleListChange(event));
157-
this.elements.list?.addEventListener("input",(event)=>this.handleListInput(event));
158-
this.elements.list?.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
156+
this.elements.familyButtons.forEach((button)=>{
157+
button.addEventListener("click",()=>this.editFamilyMappings(button.dataset.accountUserControlsEditFamily||""));
158+
});
159+
this.elements.lists.forEach((list)=>{
160+
list.addEventListener("click",(event)=>this.handleListClick(event));
161+
list.addEventListener("change",(event)=>this.handleListChange(event));
162+
list.addEventListener("input",(event)=>this.handleListInput(event));
163+
list.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
164+
});
159165
this.startDevicePolling();
160166
}
161167

@@ -177,25 +183,7 @@ export class AccountUserControlsPage {
177183
}
178184

179185
deviceOptions(){
180-
constkeyboard={
181-
controllerId: "generic-keyboard",
182-
controllerName: "Keyboard",
183-
deviceType: "Keyboard",
184-
inputs: [...KEYBOARD_INPUTS],
185-
label: "Keyboard",
186-
mappingProfile: "Keyboard Profile",
187-
value: "keyboard",
188-
};
189-
constmouse={
190-
controllerId: "generic-mouse",
191-
controllerName: "Mouse",
192-
deviceType: "Mouse",
193-
inputs: [...MOUSE_INPUTS],
194-
label: "Mouse",
195-
mappingProfile: "Mouse Profile",
196-
value: "mouse",
197-
};
198-
constgamepads=this.availableGamepads().map((gamepad)=>{
186+
returnthis.availableGamepads().map((gamepad)=>{
199187
constcontrollerName=gamepad.id||`Gamepad ${gamepad.index}`;
200188
return{
201189
controllerId: gamepad.id||`gamepad-${gamepad.index}`,
@@ -207,15 +195,37 @@ export class AccountUserControlsPage {
207195
value: `gamepad-${gamepad.index}`,
208196
};
209197
});
210-
return[keyboard,mouse, ...gamepads];
198+
}
199+
200+
familyDevice(family){
201+
if(family==="Mouse"){
202+
return{
203+
controllerId: "generic-mouse",
204+
controllerName: "Mouse",
205+
deviceType: "Mouse",
206+
inputs: [...MOUSE_INPUTS],
207+
label: "Mouse",
208+
mappingProfile: "Mouse Profile",
209+
value: "mouse",
210+
};
211+
}
212+
return{
213+
controllerId: "generic-keyboard",
214+
controllerName: "Keyboard",
215+
deviceType: "Keyboard",
216+
inputs: [...KEYBOARD_INPUTS],
217+
label: "Keyboard",
218+
mappingProfile: "Keyboard Profile",
219+
value: "keyboard",
220+
};
211221
}
212222

213223
deviceRefreshMessage(){
214224
constgamepadCount=this.availableGamepads().length;
215225
if(gamepadCount>0){
216-
return`PASS: Keyboard and Mouse available. ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
226+
return`PASS: ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
217227
}
218-
return"PASS: Keyboard and Mouse available. Gamepads auto-detect after browser exposes them.";
228+
return"PASS: Game controllers auto-detect after the browser exposes them.";
219229
}
220230

221231
selectedDevice(){
@@ -287,7 +297,7 @@ export class AccountUserControlsPage {
287297
}
288298
constselectedValue=this.elements.deviceSelect.value;
289299
constoptions=[
290-
{label: "Choose a physical controller",value: ""},
300+
{label: "Choose a game controller",value: ""},
291301
...this.deviceOptions().map((device)=>({label: device.label,value: device.value})),
292302
];
293303
this.elements.deviceSelect.classList.add("tool-form-control");
@@ -368,30 +378,62 @@ export class AccountUserControlsPage {
368378
},{once: true});
369379
}
370380

381+
profileListFamily(profile){
382+
constdeviceType=normalizeText(profile?.deviceType);
383+
if(deviceType==="Keyboard"||deviceType==="Mouse"){
384+
returndeviceType;
385+
}
386+
return"Gamepad";
387+
}
388+
389+
emptyProfileMessage(family){
390+
if(family==="Keyboard"){
391+
return"No keyboard user control profile saved yet.";
392+
}
393+
if(family==="Mouse"){
394+
return"No mouse user control profile saved yet.";
395+
}
396+
return"No game controller profiles saved yet.";
397+
}
398+
371399
renderProfiles(){
372-
if(!this.elements.list){
400+
if(!this.elements.lists.length){
373401
return;
374402
}
375-
constrows=[];
403+
constrowsByFamily=newMap(this.elements.lists.map((list)=>[
404+
list.dataset.accountUserControlsListFamily||"Gamepad",
405+
[],
406+
]));
376407
if(this.editingProfile){
377-
rows.push(...this.renderEditingRows(this.editingProfile.values));
408+
constfamily=this.profileListFamily(this.editingProfile.values);
409+
rowsByFamily.get(family)?.push(...this.renderEditingRows(this.editingProfile.values));
378410
}
379411
constvisibleProfiles=this.editingProfile?.id
380412
? this.profiles.filter((profile)=>profile.id!==this.editingProfile.id)
381413
: this.profiles;
382-
rows.push(...visibleProfiles.map((profile)=>this.renderProfileRow(profile)));
383-
if(!rows.length){
384-
constrow=document.createElement("tr");
385-
constcell=document.createElement("td");
386-
cell.colSpan=7;
387-
cell.textContent="No account user control profiles saved yet.";
388-
row.append(cell);
389-
rows.push(row);
390-
}
391-
this.elements.list.replaceChildren(...rows);
414+
visibleProfiles.forEach((profile)=>{
415+
constfamily=this.profileListFamily(profile);
416+
rowsByFamily.get(family)?.push(this.renderProfileRow(profile));
417+
});
418+
this.elements.lists.forEach((list)=>{
419+
constfamily=list.dataset.accountUserControlsListFamily||"Gamepad";
420+
constrows=rowsByFamily.get(family)||[];
421+
if(!rows.length){
422+
constrow=document.createElement("tr");
423+
constcell=document.createElement("td");
424+
cell.colSpan=7;
425+
cell.textContent=this.emptyProfileMessage(family);
426+
row.append(cell);
427+
rows.push(row);
428+
}
429+
list.replaceChildren(...rows);
430+
});
392431
if(this.elements.addProfile){
393432
this.elements.addProfile.disabled=Boolean(this.editingProfile);
394433
}
434+
this.elements.familyButtons.forEach((button)=>{
435+
button.disabled=Boolean(this.editingProfile);
436+
});
395437
}
396438

397439
profileInputSummary(profile){
@@ -455,8 +497,18 @@ export class AccountUserControlsPage {
455497
constwrapper=document.createElement("div");
456498
wrapper.className="content-grid";
457499
wrapper.dataset.accountUserControlsInputPair="true";
458-
constlabel=document.createElement("strong");
459-
label.textContent=inputMapping.physicalInput;
500+
consteditablePhysicalInput=profile.deviceType==="Keyboard"||profile.deviceType==="Mouse";
501+
constphysicalInputControl=editablePhysicalInput
502+
? document.createElement("input")
503+
: document.createElement("strong");
504+
if(editablePhysicalInput){
505+
physicalInputControl.type="text";
506+
physicalInputControl.value=inputMapping.physicalInput;
507+
physicalInputControl.dataset.accountUserControlsPhysicalInput=String(index);
508+
physicalInputControl.setAttribute("aria-label",`${profile.deviceType} physical input`);
509+
}else{
510+
physicalInputControl.textContent=inputMapping.physicalInput;
511+
}
460512
conststack=document.createElement("div");
461513
stack.className="content-stack content-stack--compact";
462514
constnormalizedOptions=[
@@ -522,7 +574,7 @@ export class AccountUserControlsPage {
522574
validation.className="status";
523575
validation.dataset.accountUserControlsInputValidation=String(index);
524576
stack.append(validation);
525-
wrapper.append(label,stack);
577+
wrapper.append(physicalInputControl,stack);
526578
returnwrapper;
527579
}
528580

@@ -579,24 +631,42 @@ export class AccountUserControlsPage {
579631
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
580632
}
581633

634+
editFamilyMappings(family){
635+
constnormalizedFamily=family==="Mouse" ? "Mouse" : "Keyboard";
636+
constdevice=this.familyDevice(normalizedFamily);
637+
constexisting=this.profiles.find((profile)=>profile.deviceType===device.deviceType&&profile.controllerId===device.controllerId);
638+
if(existing){
639+
this.editingProfile={id: existing.id,values: existing};
640+
this.renderProfiles();
641+
this.setStatus(`Editing existing ${existing.mappingProfile}.`);
642+
return;
643+
}
644+
constprofile=this.profileFromDevice(device);
645+
this.editingProfile={id: profile.id,values: profile};
646+
this.renderProfiles();
647+
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
648+
}
649+
582650
profileFromEditingRow(){
583651
constvalues=this.editingProfile?.values||{};
584-
constdetails=this.elements.list?.querySelector("[data-account-user-controls-editing-details]");
652+
constdetails=this.root.querySelector("[data-account-user-controls-editing-details]");
585653
constinputMappings=(values.inputMappings||[]).map((mapping,index)=>{
654+
constphysicalInputControl=details?.querySelector(`[data-account-user-controls-physical-input="${index}"]`);
586655
constnormalizedSelect=details?.querySelector(`[data-account-user-controls-input-normalized="${index}"]`);
587656
constnegativeSelect=details?.querySelector(`[data-account-user-controls-input-negative="${index}"]`);
588657
constpositiveSelect=details?.querySelector(`[data-account-user-controls-input-positive="${index}"]`);
589658
constdeadzoneInput=details?.querySelector(`[data-account-user-controls-deadzone="${index}"]`);
590659
constinvertInput=details?.querySelector(`[data-account-user-controls-invert="${index}"]`);
591660
constsensitivityInput=details?.querySelector(`[data-account-user-controls-sensitivity="${index}"]`);
661+
constphysicalInput=normalizeText(physicalInputControl?.value??mapping.physicalInput);
592662
constnegativeNormalizedInput=normalizeText(negativeSelect?.value??mapping.negativeNormalizedInput);
593663
constpositiveNormalizedInput=normalizeText(positiveSelect?.value??mapping.positiveNormalizedInput);
594664
return{
595665
deadzone: Number(deadzoneInput?.value??mapping.deadzone),
596666
invert: Boolean(invertInput?.checked??mapping.invert),
597667
negativeNormalizedInput,
598668
normalizedInput: positiveNormalizedInput||normalizeText(normalizedSelect?.value??mapping.normalizedInput)||negativeNormalizedInput,
599-
physicalInput: mapping.physicalInput,
669+
physicalInput,
600670
positiveNormalizedInput,
601671
sensitivity: sensitivityInput ? Number(sensitivityInput.value) : mapping.sensitivity,
602672
};
@@ -605,6 +675,7 @@ export class AccountUserControlsPage {
605675
...values,
606676
id: this.editingProfile?.id||"",
607677
inputMappings,
678+
inputs: inputMappings.map((mapping)=>mapping.physicalInput),
608679
});
609680
}
610681

@@ -662,7 +733,7 @@ export class AccountUserControlsPage {
662733

663734
renderInputValidation(validation){
664735
constinvalidIndexes=newSet(validation.invalidIndexes||[]);
665-
this.elements.list?.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
736+
this.root.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
666737
constindex=Number(status.dataset.accountUserControlsInputValidation);
667738
status.textContent=invalidIndexes.has(index)
668739
? "Needs normalized control, valid deadzone, and valid sensitivity."

‎account/user-controls.html‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@ <h2>Physical Input Mapping</h2>
4747
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Keyboard"></tbody>
4848
</table>
4949
</div>
50+
<divclass="content-cluster" aria-label="Keyboard user controls">
51+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Keyboard">Edit Keyboard Mappings</button>
52+
</div>
53+
<divclass="table-wrapper">
54+
<tableclass="data-table" aria-label="Keyboard user control profile" data-account-user-controls-table="Keyboard">
55+
<thead>
56+
<tr>
57+
<th>Physical Controller</th>
58+
<th>Physical Input</th>
59+
<th>Normalized Control</th>
60+
<th>Deadzone</th>
61+
<th>Invert</th>
62+
<th>Sensitivity</th>
63+
<th>Actions</th>
64+
</tr>
65+
</thead>
66+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Keyboard"></tbody>
67+
</table>
68+
</div>
5069
</div>
5170
</details>
5271
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Mouse">
@@ -65,6 +84,25 @@ <h2>Physical Input Mapping</h2>
6584
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Mouse"></tbody>
6685
</table>
6786
</div>
87+
<divclass="content-cluster" aria-label="Mouse user controls">
88+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Mouse">Edit Mouse Mappings</button>
89+
</div>
90+
<divclass="table-wrapper">
91+
<tableclass="data-table" aria-label="Mouse user control profile" data-account-user-controls-table="Mouse">
92+
<thead>
93+
<tr>
94+
<th>Physical Controller</th>
95+
<th>Physical Input</th>
96+
<th>Normalized Control</th>
97+
<th>Deadzone</th>
98+
<th>Invert</th>
99+
<th>Sensitivity</th>
100+
<th>Actions</th>
101+
</tr>
102+
</thead>
103+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Mouse"></tbody>
104+
</table>
105+
</div>
68106
</div>
69107
</details>
70108
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Game Controllers">
@@ -73,13 +111,13 @@ <h2>Physical Input Mapping</h2>
73111
<divclass="content-cluster" aria-label="Account controller device selection">
74112
<buttonclass="btn" type="button" data-account-user-controls-refresh>Refresh Devices</button>
75113
<label>
76-
Physical Controller
114+
Game Controller
77115
<selectdata-account-user-controls-device></select>
78116
</label>
79117
<buttonclass="btn" type="button" data-account-user-controls-add-profile>Create User Control Profile</button>
80118
<buttonclass="btn primary" type="button" data-account-user-controls-save-all>Save</button>
81119
</div>
82-
<pclass="status" role="status" data-account-user-controls-device-status>Keyboard and Mouse are available. Game controllers auto-detect after the browser exposes them.</p>
120+
<pclass="status" role="status" data-account-user-controls-device-status>Game controllers auto-detect after the browser exposes them.</p>
83121
<divclass="table-wrapper">
84122
<tableclass="data-table" aria-label="Account user control profiles" data-account-user-controls-table>
85123
<thead>
@@ -93,7 +131,7 @@ <h2>Physical Input Mapping</h2>
93131
<th>Actions</th>
94132
</tr>
95133
</thead>
96-
<tbodydata-account-user-controls-list></tbody>
134+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Gamepad"></tbody>
97135
</table>
98136
</div>
99137
</div>

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add Controls combo framework and editable Account user controls - PR_… · ToolboxAid/HTML-JavaScript-Gaming@cbf9b8a · GitHub
Skip to content

Commit cbf9b8a

Browse files
committed
Add Controls combo framework and editable Account user controls - PR_26162_042-controls-combo-framework-and-user-control-editing
1 parent ef56c9d commit cbf9b8a

10 files changed

Lines changed: 984 additions & 2403 deletions

‎account/user-controls-page.js‎

Lines changed: 118 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ export class AccountUserControlsPage {
130130
defaultLists: [...root.querySelectorAll("[data-account-user-controls-default-list]")],
131131
deviceSelect: root.querySelector("[data-account-user-controls-device]"),
132132
deviceStatus: root.querySelector("[data-account-user-controls-device-status]"),
133-
list: root.querySelector("[data-account-user-controls-list]"),
133+
familyButtons: [...root.querySelectorAll("[data-account-user-controls-edit-family]")],
134+
lists: [...root.querySelectorAll("[data-account-user-controls-list]")],
134135
refresh: root.querySelector("[data-account-user-controls-refresh]"),
135136
saveAll: root.querySelector("[data-account-user-controls-save-all]"),
136137
status: root.querySelector("[data-account-user-controls-status]"),
@@ -152,10 +153,15 @@ export class AccountUserControlsPage {
152153
this.elements.addProfile?.addEventListener("click",()=>this.addProfileForSelectedDevice());
153154
this.elements.saveAll?.addEventListener("click",()=>this.saveCurrentState());
154155
this.elements.deviceSelect?.addEventListener("change",()=>this.renderDeviceStatus());
155-
this.elements.list?.addEventListener("click",(event)=>this.handleListClick(event));
156-
this.elements.list?.addEventListener("change",(event)=>this.handleListChange(event));
157-
this.elements.list?.addEventListener("input",(event)=>this.handleListInput(event));
158-
this.elements.list?.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
156+
this.elements.familyButtons.forEach((button)=>{
157+
button.addEventListener("click",()=>this.editFamilyMappings(button.dataset.accountUserControlsEditFamily||""));
158+
});
159+
this.elements.lists.forEach((list)=>{
160+
list.addEventListener("click",(event)=>this.handleListClick(event));
161+
list.addEventListener("change",(event)=>this.handleListChange(event));
162+
list.addEventListener("input",(event)=>this.handleListInput(event));
163+
list.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
164+
});
159165
this.startDevicePolling();
160166
}
161167

@@ -177,25 +183,7 @@ export class AccountUserControlsPage {
177183
}
178184

179185
deviceOptions(){
180-
constkeyboard={
181-
controllerId: "generic-keyboard",
182-
controllerName: "Keyboard",
183-
deviceType: "Keyboard",
184-
inputs: [...KEYBOARD_INPUTS],
185-
label: "Keyboard",
186-
mappingProfile: "Keyboard Profile",
187-
value: "keyboard",
188-
};
189-
constmouse={
190-
controllerId: "generic-mouse",
191-
controllerName: "Mouse",
192-
deviceType: "Mouse",
193-
inputs: [...MOUSE_INPUTS],
194-
label: "Mouse",
195-
mappingProfile: "Mouse Profile",
196-
value: "mouse",
197-
};
198-
constgamepads=this.availableGamepads().map((gamepad)=>{
186+
returnthis.availableGamepads().map((gamepad)=>{
199187
constcontrollerName=gamepad.id||`Gamepad ${gamepad.index}`;
200188
return{
201189
controllerId: gamepad.id||`gamepad-${gamepad.index}`,
@@ -207,15 +195,37 @@ export class AccountUserControlsPage {
207195
value: `gamepad-${gamepad.index}`,
208196
};
209197
});
210-
return[keyboard,mouse, ...gamepads];
198+
}
199+
200+
familyDevice(family){
201+
if(family==="Mouse"){
202+
return{
203+
controllerId: "generic-mouse",
204+
controllerName: "Mouse",
205+
deviceType: "Mouse",
206+
inputs: [...MOUSE_INPUTS],
207+
label: "Mouse",
208+
mappingProfile: "Mouse Profile",
209+
value: "mouse",
210+
};
211+
}
212+
return{
213+
controllerId: "generic-keyboard",
214+
controllerName: "Keyboard",
215+
deviceType: "Keyboard",
216+
inputs: [...KEYBOARD_INPUTS],
217+
label: "Keyboard",
218+
mappingProfile: "Keyboard Profile",
219+
value: "keyboard",
220+
};
211221
}
212222

213223
deviceRefreshMessage(){
214224
constgamepadCount=this.availableGamepads().length;
215225
if(gamepadCount>0){
216-
return`PASS: Keyboard and Mouse available. ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
226+
return`PASS: ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
217227
}
218-
return"PASS: Keyboard and Mouse available. Gamepads auto-detect after browser exposes them.";
228+
return"PASS: Game controllers auto-detect after the browser exposes them.";
219229
}
220230

221231
selectedDevice(){
@@ -287,7 +297,7 @@ export class AccountUserControlsPage {
287297
}
288298
constselectedValue=this.elements.deviceSelect.value;
289299
constoptions=[
290-
{label: "Choose a physical controller",value: ""},
300+
{label: "Choose a game controller",value: ""},
291301
...this.deviceOptions().map((device)=>({label: device.label,value: device.value})),
292302
];
293303
this.elements.deviceSelect.classList.add("tool-form-control");
@@ -368,30 +378,62 @@ export class AccountUserControlsPage {
368378
},{once: true});
369379
}
370380

381+
profileListFamily(profile){
382+
constdeviceType=normalizeText(profile?.deviceType);
383+
if(deviceType==="Keyboard"||deviceType==="Mouse"){
384+
returndeviceType;
385+
}
386+
return"Gamepad";
387+
}
388+
389+
emptyProfileMessage(family){
390+
if(family==="Keyboard"){
391+
return"No keyboard user control profile saved yet.";
392+
}
393+
if(family==="Mouse"){
394+
return"No mouse user control profile saved yet.";
395+
}
396+
return"No game controller profiles saved yet.";
397+
}
398+
371399
renderProfiles(){
372-
if(!this.elements.list){
400+
if(!this.elements.lists.length){
373401
return;
374402
}
375-
constrows=[];
403+
constrowsByFamily=newMap(this.elements.lists.map((list)=>[
404+
list.dataset.accountUserControlsListFamily||"Gamepad",
405+
[],
406+
]));
376407
if(this.editingProfile){
377-
rows.push(...this.renderEditingRows(this.editingProfile.values));
408+
constfamily=this.profileListFamily(this.editingProfile.values);
409+
rowsByFamily.get(family)?.push(...this.renderEditingRows(this.editingProfile.values));
378410
}
379411
constvisibleProfiles=this.editingProfile?.id
380412
? this.profiles.filter((profile)=>profile.id!==this.editingProfile.id)
381413
: this.profiles;
382-
rows.push(...visibleProfiles.map((profile)=>this.renderProfileRow(profile)));
383-
if(!rows.length){
384-
constrow=document.createElement("tr");
385-
constcell=document.createElement("td");
386-
cell.colSpan=7;
387-
cell.textContent="No account user control profiles saved yet.";
388-
row.append(cell);
389-
rows.push(row);
390-
}
391-
this.elements.list.replaceChildren(...rows);
414+
visibleProfiles.forEach((profile)=>{
415+
constfamily=this.profileListFamily(profile);
416+
rowsByFamily.get(family)?.push(this.renderProfileRow(profile));
417+
});
418+
this.elements.lists.forEach((list)=>{
419+
constfamily=list.dataset.accountUserControlsListFamily||"Gamepad";
420+
constrows=rowsByFamily.get(family)||[];
421+
if(!rows.length){
422+
constrow=document.createElement("tr");
423+
constcell=document.createElement("td");
424+
cell.colSpan=7;
425+
cell.textContent=this.emptyProfileMessage(family);
426+
row.append(cell);
427+
rows.push(row);
428+
}
429+
list.replaceChildren(...rows);
430+
});
392431
if(this.elements.addProfile){
393432
this.elements.addProfile.disabled=Boolean(this.editingProfile);
394433
}
434+
this.elements.familyButtons.forEach((button)=>{
435+
button.disabled=Boolean(this.editingProfile);
436+
});
395437
}
396438

397439
profileInputSummary(profile){
@@ -455,8 +497,18 @@ export class AccountUserControlsPage {
455497
constwrapper=document.createElement("div");
456498
wrapper.className="content-grid";
457499
wrapper.dataset.accountUserControlsInputPair="true";
458-
constlabel=document.createElement("strong");
459-
label.textContent=inputMapping.physicalInput;
500+
consteditablePhysicalInput=profile.deviceType==="Keyboard"||profile.deviceType==="Mouse";
501+
constphysicalInputControl=editablePhysicalInput
502+
? document.createElement("input")
503+
: document.createElement("strong");
504+
if(editablePhysicalInput){
505+
physicalInputControl.type="text";
506+
physicalInputControl.value=inputMapping.physicalInput;
507+
physicalInputControl.dataset.accountUserControlsPhysicalInput=String(index);
508+
physicalInputControl.setAttribute("aria-label",`${profile.deviceType} physical input`);
509+
}else{
510+
physicalInputControl.textContent=inputMapping.physicalInput;
511+
}
460512
conststack=document.createElement("div");
461513
stack.className="content-stack content-stack--compact";
462514
constnormalizedOptions=[
@@ -522,7 +574,7 @@ export class AccountUserControlsPage {
522574
validation.className="status";
523575
validation.dataset.accountUserControlsInputValidation=String(index);
524576
stack.append(validation);
525-
wrapper.append(label,stack);
577+
wrapper.append(physicalInputControl,stack);
526578
returnwrapper;
527579
}
528580

@@ -579,24 +631,42 @@ export class AccountUserControlsPage {
579631
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
580632
}
581633

634+
editFamilyMappings(family){
635+
constnormalizedFamily=family==="Mouse" ? "Mouse" : "Keyboard";
636+
constdevice=this.familyDevice(normalizedFamily);
637+
constexisting=this.profiles.find((profile)=>profile.deviceType===device.deviceType&&profile.controllerId===device.controllerId);
638+
if(existing){
639+
this.editingProfile={id: existing.id,values: existing};
640+
this.renderProfiles();
641+
this.setStatus(`Editing existing ${existing.mappingProfile}.`);
642+
return;
643+
}
644+
constprofile=this.profileFromDevice(device);
645+
this.editingProfile={id: profile.id,values: profile};
646+
this.renderProfiles();
647+
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
648+
}
649+
582650
profileFromEditingRow(){
583651
constvalues=this.editingProfile?.values||{};
584-
constdetails=this.elements.list?.querySelector("[data-account-user-controls-editing-details]");
652+
constdetails=this.root.querySelector("[data-account-user-controls-editing-details]");
585653
constinputMappings=(values.inputMappings||[]).map((mapping,index)=>{
654+
constphysicalInputControl=details?.querySelector(`[data-account-user-controls-physical-input="${index}"]`);
586655
constnormalizedSelect=details?.querySelector(`[data-account-user-controls-input-normalized="${index}"]`);
587656
constnegativeSelect=details?.querySelector(`[data-account-user-controls-input-negative="${index}"]`);
588657
constpositiveSelect=details?.querySelector(`[data-account-user-controls-input-positive="${index}"]`);
589658
constdeadzoneInput=details?.querySelector(`[data-account-user-controls-deadzone="${index}"]`);
590659
constinvertInput=details?.querySelector(`[data-account-user-controls-invert="${index}"]`);
591660
constsensitivityInput=details?.querySelector(`[data-account-user-controls-sensitivity="${index}"]`);
661+
constphysicalInput=normalizeText(physicalInputControl?.value??mapping.physicalInput);
592662
constnegativeNormalizedInput=normalizeText(negativeSelect?.value??mapping.negativeNormalizedInput);
593663
constpositiveNormalizedInput=normalizeText(positiveSelect?.value??mapping.positiveNormalizedInput);
594664
return{
595665
deadzone: Number(deadzoneInput?.value??mapping.deadzone),
596666
invert: Boolean(invertInput?.checked??mapping.invert),
597667
negativeNormalizedInput,
598668
normalizedInput: positiveNormalizedInput||normalizeText(normalizedSelect?.value??mapping.normalizedInput)||negativeNormalizedInput,
599-
physicalInput: mapping.physicalInput,
669+
physicalInput,
600670
positiveNormalizedInput,
601671
sensitivity: sensitivityInput ? Number(sensitivityInput.value) : mapping.sensitivity,
602672
};
@@ -605,6 +675,7 @@ export class AccountUserControlsPage {
605675
...values,
606676
id: this.editingProfile?.id||"",
607677
inputMappings,
678+
inputs: inputMappings.map((mapping)=>mapping.physicalInput),
608679
});
609680
}
610681

@@ -662,7 +733,7 @@ export class AccountUserControlsPage {
662733

663734
renderInputValidation(validation){
664735
constinvalidIndexes=newSet(validation.invalidIndexes||[]);
665-
this.elements.list?.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
736+
this.root.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
666737
constindex=Number(status.dataset.accountUserControlsInputValidation);
667738
status.textContent=invalidIndexes.has(index)
668739
? "Needs normalized control, valid deadzone, and valid sensitivity."

‎account/user-controls.html‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@ <h2>Physical Input Mapping</h2>
4747
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Keyboard"></tbody>
4848
</table>
4949
</div>
50+
<divclass="content-cluster" aria-label="Keyboard user controls">
51+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Keyboard">Edit Keyboard Mappings</button>
52+
</div>
53+
<divclass="table-wrapper">
54+
<tableclass="data-table" aria-label="Keyboard user control profile" data-account-user-controls-table="Keyboard">
55+
<thead>
56+
<tr>
57+
<th>Physical Controller</th>
58+
<th>Physical Input</th>
59+
<th>Normalized Control</th>
60+
<th>Deadzone</th>
61+
<th>Invert</th>
62+
<th>Sensitivity</th>
63+
<th>Actions</th>
64+
</tr>
65+
</thead>
66+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Keyboard"></tbody>
67+
</table>
68+
</div>
5069
</div>
5170
</details>
5271
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Mouse">
@@ -65,6 +84,25 @@ <h2>Physical Input Mapping</h2>
6584
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Mouse"></tbody>
6685
</table>
6786
</div>
87+
<divclass="content-cluster" aria-label="Mouse user controls">
88+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Mouse">Edit Mouse Mappings</button>
89+
</div>
90+
<divclass="table-wrapper">
91+
<tableclass="data-table" aria-label="Mouse user control profile" data-account-user-controls-table="Mouse">
92+
<thead>
93+
<tr>
94+
<th>Physical Controller</th>
95+
<th>Physical Input</th>
96+
<th>Normalized Control</th>
97+
<th>Deadzone</th>
98+
<th>Invert</th>
99+
<th>Sensitivity</th>
100+
<th>Actions</th>
101+
</tr>
102+
</thead>
103+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Mouse"></tbody>
104+
</table>
105+
</div>
68106
</div>
69107
</details>
70108
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Game Controllers">
@@ -73,13 +111,13 @@ <h2>Physical Input Mapping</h2>
73111
<divclass="content-cluster" aria-label="Account controller device selection">
74112
<buttonclass="btn" type="button" data-account-user-controls-refresh>Refresh Devices</button>
75113
<label>
76-
Physical Controller
114+
Game Controller
77115
<selectdata-account-user-controls-device></select>
78116
</label>
79117
<buttonclass="btn" type="button" data-account-user-controls-add-profile>Create User Control Profile</button>
80118
<buttonclass="btn primary" type="button" data-account-user-controls-save-all>Save</button>
81119
</div>
82-
<pclass="status" role="status" data-account-user-controls-device-status>Keyboard and Mouse are available. Game controllers auto-detect after the browser exposes them.</p>
120+
<pclass="status" role="status" data-account-user-controls-device-status>Game controllers auto-detect after the browser exposes them.</p>
83121
<divclass="table-wrapper">
84122
<tableclass="data-table" aria-label="Account user control profiles" data-account-user-controls-table>
85123
<thead>
@@ -93,7 +131,7 @@ <h2>Physical Input Mapping</h2>
93131
<th>Actions</th>
94132
</tr>
95133
</thead>
96-
<tbodydata-account-user-controls-list></tbody>
134+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Gamepad"></tbody>
97135
</table>
98136
</div>
99137
</div>

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add Controls combo framework and editable Account user controls - PR_… · ToolboxAid/HTML-JavaScript-Gaming@cbf9b8a · GitHub
Skip to content

Commit cbf9b8a

Browse files
committed
Add Controls combo framework and editable Account user controls - PR_26162_042-controls-combo-framework-and-user-control-editing
1 parent ef56c9d commit cbf9b8a

10 files changed

Lines changed: 984 additions & 2403 deletions

‎account/user-controls-page.js‎

Lines changed: 118 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ export class AccountUserControlsPage {
130130
defaultLists: [...root.querySelectorAll("[data-account-user-controls-default-list]")],
131131
deviceSelect: root.querySelector("[data-account-user-controls-device]"),
132132
deviceStatus: root.querySelector("[data-account-user-controls-device-status]"),
133-
list: root.querySelector("[data-account-user-controls-list]"),
133+
familyButtons: [...root.querySelectorAll("[data-account-user-controls-edit-family]")],
134+
lists: [...root.querySelectorAll("[data-account-user-controls-list]")],
134135
refresh: root.querySelector("[data-account-user-controls-refresh]"),
135136
saveAll: root.querySelector("[data-account-user-controls-save-all]"),
136137
status: root.querySelector("[data-account-user-controls-status]"),
@@ -152,10 +153,15 @@ export class AccountUserControlsPage {
152153
this.elements.addProfile?.addEventListener("click",()=>this.addProfileForSelectedDevice());
153154
this.elements.saveAll?.addEventListener("click",()=>this.saveCurrentState());
154155
this.elements.deviceSelect?.addEventListener("change",()=>this.renderDeviceStatus());
155-
this.elements.list?.addEventListener("click",(event)=>this.handleListClick(event));
156-
this.elements.list?.addEventListener("change",(event)=>this.handleListChange(event));
157-
this.elements.list?.addEventListener("input",(event)=>this.handleListInput(event));
158-
this.elements.list?.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
156+
this.elements.familyButtons.forEach((button)=>{
157+
button.addEventListener("click",()=>this.editFamilyMappings(button.dataset.accountUserControlsEditFamily||""));
158+
});
159+
this.elements.lists.forEach((list)=>{
160+
list.addEventListener("click",(event)=>this.handleListClick(event));
161+
list.addEventListener("change",(event)=>this.handleListChange(event));
162+
list.addEventListener("input",(event)=>this.handleListInput(event));
163+
list.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
164+
});
159165
this.startDevicePolling();
160166
}
161167

@@ -177,25 +183,7 @@ export class AccountUserControlsPage {
177183
}
178184

179185
deviceOptions(){
180-
constkeyboard={
181-
controllerId: "generic-keyboard",
182-
controllerName: "Keyboard",
183-
deviceType: "Keyboard",
184-
inputs: [...KEYBOARD_INPUTS],
185-
label: "Keyboard",
186-
mappingProfile: "Keyboard Profile",
187-
value: "keyboard",
188-
};
189-
constmouse={
190-
controllerId: "generic-mouse",
191-
controllerName: "Mouse",
192-
deviceType: "Mouse",
193-
inputs: [...MOUSE_INPUTS],
194-
label: "Mouse",
195-
mappingProfile: "Mouse Profile",
196-
value: "mouse",
197-
};
198-
constgamepads=this.availableGamepads().map((gamepad)=>{
186+
returnthis.availableGamepads().map((gamepad)=>{
199187
constcontrollerName=gamepad.id||`Gamepad ${gamepad.index}`;
200188
return{
201189
controllerId: gamepad.id||`gamepad-${gamepad.index}`,
@@ -207,15 +195,37 @@ export class AccountUserControlsPage {
207195
value: `gamepad-${gamepad.index}`,
208196
};
209197
});
210-
return[keyboard,mouse, ...gamepads];
198+
}
199+
200+
familyDevice(family){
201+
if(family==="Mouse"){
202+
return{
203+
controllerId: "generic-mouse",
204+
controllerName: "Mouse",
205+
deviceType: "Mouse",
206+
inputs: [...MOUSE_INPUTS],
207+
label: "Mouse",
208+
mappingProfile: "Mouse Profile",
209+
value: "mouse",
210+
};
211+
}
212+
return{
213+
controllerId: "generic-keyboard",
214+
controllerName: "Keyboard",
215+
deviceType: "Keyboard",
216+
inputs: [...KEYBOARD_INPUTS],
217+
label: "Keyboard",
218+
mappingProfile: "Keyboard Profile",
219+
value: "keyboard",
220+
};
211221
}
212222

213223
deviceRefreshMessage(){
214224
constgamepadCount=this.availableGamepads().length;
215225
if(gamepadCount>0){
216-
return`PASS: Keyboard and Mouse available. ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
226+
return`PASS: ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
217227
}
218-
return"PASS: Keyboard and Mouse available. Gamepads auto-detect after browser exposes them.";
228+
return"PASS: Game controllers auto-detect after the browser exposes them.";
219229
}
220230

221231
selectedDevice(){
@@ -287,7 +297,7 @@ export class AccountUserControlsPage {
287297
}
288298
constselectedValue=this.elements.deviceSelect.value;
289299
constoptions=[
290-
{label: "Choose a physical controller",value: ""},
300+
{label: "Choose a game controller",value: ""},
291301
...this.deviceOptions().map((device)=>({label: device.label,value: device.value})),
292302
];
293303
this.elements.deviceSelect.classList.add("tool-form-control");
@@ -368,30 +378,62 @@ export class AccountUserControlsPage {
368378
},{once: true});
369379
}
370380

381+
profileListFamily(profile){
382+
constdeviceType=normalizeText(profile?.deviceType);
383+
if(deviceType==="Keyboard"||deviceType==="Mouse"){
384+
returndeviceType;
385+
}
386+
return"Gamepad";
387+
}
388+
389+
emptyProfileMessage(family){
390+
if(family==="Keyboard"){
391+
return"No keyboard user control profile saved yet.";
392+
}
393+
if(family==="Mouse"){
394+
return"No mouse user control profile saved yet.";
395+
}
396+
return"No game controller profiles saved yet.";
397+
}
398+
371399
renderProfiles(){
372-
if(!this.elements.list){
400+
if(!this.elements.lists.length){
373401
return;
374402
}
375-
constrows=[];
403+
constrowsByFamily=newMap(this.elements.lists.map((list)=>[
404+
list.dataset.accountUserControlsListFamily||"Gamepad",
405+
[],
406+
]));
376407
if(this.editingProfile){
377-
rows.push(...this.renderEditingRows(this.editingProfile.values));
408+
constfamily=this.profileListFamily(this.editingProfile.values);
409+
rowsByFamily.get(family)?.push(...this.renderEditingRows(this.editingProfile.values));
378410
}
379411
constvisibleProfiles=this.editingProfile?.id
380412
? this.profiles.filter((profile)=>profile.id!==this.editingProfile.id)
381413
: this.profiles;
382-
rows.push(...visibleProfiles.map((profile)=>this.renderProfileRow(profile)));
383-
if(!rows.length){
384-
constrow=document.createElement("tr");
385-
constcell=document.createElement("td");
386-
cell.colSpan=7;
387-
cell.textContent="No account user control profiles saved yet.";
388-
row.append(cell);
389-
rows.push(row);
390-
}
391-
this.elements.list.replaceChildren(...rows);
414+
visibleProfiles.forEach((profile)=>{
415+
constfamily=this.profileListFamily(profile);
416+
rowsByFamily.get(family)?.push(this.renderProfileRow(profile));
417+
});
418+
this.elements.lists.forEach((list)=>{
419+
constfamily=list.dataset.accountUserControlsListFamily||"Gamepad";
420+
constrows=rowsByFamily.get(family)||[];
421+
if(!rows.length){
422+
constrow=document.createElement("tr");
423+
constcell=document.createElement("td");
424+
cell.colSpan=7;
425+
cell.textContent=this.emptyProfileMessage(family);
426+
row.append(cell);
427+
rows.push(row);
428+
}
429+
list.replaceChildren(...rows);
430+
});
392431
if(this.elements.addProfile){
393432
this.elements.addProfile.disabled=Boolean(this.editingProfile);
394433
}
434+
this.elements.familyButtons.forEach((button)=>{
435+
button.disabled=Boolean(this.editingProfile);
436+
});
395437
}
396438

397439
profileInputSummary(profile){
@@ -455,8 +497,18 @@ export class AccountUserControlsPage {
455497
constwrapper=document.createElement("div");
456498
wrapper.className="content-grid";
457499
wrapper.dataset.accountUserControlsInputPair="true";
458-
constlabel=document.createElement("strong");
459-
label.textContent=inputMapping.physicalInput;
500+
consteditablePhysicalInput=profile.deviceType==="Keyboard"||profile.deviceType==="Mouse";
501+
constphysicalInputControl=editablePhysicalInput
502+
? document.createElement("input")
503+
: document.createElement("strong");
504+
if(editablePhysicalInput){
505+
physicalInputControl.type="text";
506+
physicalInputControl.value=inputMapping.physicalInput;
507+
physicalInputControl.dataset.accountUserControlsPhysicalInput=String(index);
508+
physicalInputControl.setAttribute("aria-label",`${profile.deviceType} physical input`);
509+
}else{
510+
physicalInputControl.textContent=inputMapping.physicalInput;
511+
}
460512
conststack=document.createElement("div");
461513
stack.className="content-stack content-stack--compact";
462514
constnormalizedOptions=[
@@ -522,7 +574,7 @@ export class AccountUserControlsPage {
522574
validation.className="status";
523575
validation.dataset.accountUserControlsInputValidation=String(index);
524576
stack.append(validation);
525-
wrapper.append(label,stack);
577+
wrapper.append(physicalInputControl,stack);
526578
returnwrapper;
527579
}
528580

@@ -579,24 +631,42 @@ export class AccountUserControlsPage {
579631
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
580632
}
581633

634+
editFamilyMappings(family){
635+
constnormalizedFamily=family==="Mouse" ? "Mouse" : "Keyboard";
636+
constdevice=this.familyDevice(normalizedFamily);
637+
constexisting=this.profiles.find((profile)=>profile.deviceType===device.deviceType&&profile.controllerId===device.controllerId);
638+
if(existing){
639+
this.editingProfile={id: existing.id,values: existing};
640+
this.renderProfiles();
641+
this.setStatus(`Editing existing ${existing.mappingProfile}.`);
642+
return;
643+
}
644+
constprofile=this.profileFromDevice(device);
645+
this.editingProfile={id: profile.id,values: profile};
646+
this.renderProfiles();
647+
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
648+
}
649+
582650
profileFromEditingRow(){
583651
constvalues=this.editingProfile?.values||{};
584-
constdetails=this.elements.list?.querySelector("[data-account-user-controls-editing-details]");
652+
constdetails=this.root.querySelector("[data-account-user-controls-editing-details]");
585653
constinputMappings=(values.inputMappings||[]).map((mapping,index)=>{
654+
constphysicalInputControl=details?.querySelector(`[data-account-user-controls-physical-input="${index}"]`);
586655
constnormalizedSelect=details?.querySelector(`[data-account-user-controls-input-normalized="${index}"]`);
587656
constnegativeSelect=details?.querySelector(`[data-account-user-controls-input-negative="${index}"]`);
588657
constpositiveSelect=details?.querySelector(`[data-account-user-controls-input-positive="${index}"]`);
589658
constdeadzoneInput=details?.querySelector(`[data-account-user-controls-deadzone="${index}"]`);
590659
constinvertInput=details?.querySelector(`[data-account-user-controls-invert="${index}"]`);
591660
constsensitivityInput=details?.querySelector(`[data-account-user-controls-sensitivity="${index}"]`);
661+
constphysicalInput=normalizeText(physicalInputControl?.value??mapping.physicalInput);
592662
constnegativeNormalizedInput=normalizeText(negativeSelect?.value??mapping.negativeNormalizedInput);
593663
constpositiveNormalizedInput=normalizeText(positiveSelect?.value??mapping.positiveNormalizedInput);
594664
return{
595665
deadzone: Number(deadzoneInput?.value??mapping.deadzone),
596666
invert: Boolean(invertInput?.checked??mapping.invert),
597667
negativeNormalizedInput,
598668
normalizedInput: positiveNormalizedInput||normalizeText(normalizedSelect?.value??mapping.normalizedInput)||negativeNormalizedInput,
599-
physicalInput: mapping.physicalInput,
669+
physicalInput,
600670
positiveNormalizedInput,
601671
sensitivity: sensitivityInput ? Number(sensitivityInput.value) : mapping.sensitivity,
602672
};
@@ -605,6 +675,7 @@ export class AccountUserControlsPage {
605675
...values,
606676
id: this.editingProfile?.id||"",
607677
inputMappings,
678+
inputs: inputMappings.map((mapping)=>mapping.physicalInput),
608679
});
609680
}
610681

@@ -662,7 +733,7 @@ export class AccountUserControlsPage {
662733

663734
renderInputValidation(validation){
664735
constinvalidIndexes=newSet(validation.invalidIndexes||[]);
665-
this.elements.list?.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
736+
this.root.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
666737
constindex=Number(status.dataset.accountUserControlsInputValidation);
667738
status.textContent=invalidIndexes.has(index)
668739
? "Needs normalized control, valid deadzone, and valid sensitivity."

‎account/user-controls.html‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@ <h2>Physical Input Mapping</h2>
4747
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Keyboard"></tbody>
4848
</table>
4949
</div>
50+
<divclass="content-cluster" aria-label="Keyboard user controls">
51+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Keyboard">Edit Keyboard Mappings</button>
52+
</div>
53+
<divclass="table-wrapper">
54+
<tableclass="data-table" aria-label="Keyboard user control profile" data-account-user-controls-table="Keyboard">
55+
<thead>
56+
<tr>
57+
<th>Physical Controller</th>
58+
<th>Physical Input</th>
59+
<th>Normalized Control</th>
60+
<th>Deadzone</th>
61+
<th>Invert</th>
62+
<th>Sensitivity</th>
63+
<th>Actions</th>
64+
</tr>
65+
</thead>
66+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Keyboard"></tbody>
67+
</table>
68+
</div>
5069
</div>
5170
</details>
5271
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Mouse">
@@ -65,6 +84,25 @@ <h2>Physical Input Mapping</h2>
6584
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Mouse"></tbody>
6685
</table>
6786
</div>
87+
<divclass="content-cluster" aria-label="Mouse user controls">
88+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Mouse">Edit Mouse Mappings</button>
89+
</div>
90+
<divclass="table-wrapper">
91+
<tableclass="data-table" aria-label="Mouse user control profile" data-account-user-controls-table="Mouse">
92+
<thead>
93+
<tr>
94+
<th>Physical Controller</th>
95+
<th>Physical Input</th>
96+
<th>Normalized Control</th>
97+
<th>Deadzone</th>
98+
<th>Invert</th>
99+
<th>Sensitivity</th>
100+
<th>Actions</th>
101+
</tr>
102+
</thead>
103+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Mouse"></tbody>
104+
</table>
105+
</div>
68106
</div>
69107
</details>
70108
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Game Controllers">
@@ -73,13 +111,13 @@ <h2>Physical Input Mapping</h2>
73111
<divclass="content-cluster" aria-label="Account controller device selection">
74112
<buttonclass="btn" type="button" data-account-user-controls-refresh>Refresh Devices</button>
75113
<label>
76-
Physical Controller
114+
Game Controller
77115
<selectdata-account-user-controls-device></select>
78116
</label>
79117
<buttonclass="btn" type="button" data-account-user-controls-add-profile>Create User Control Profile</button>
80118
<buttonclass="btn primary" type="button" data-account-user-controls-save-all>Save</button>
81119
</div>
82-
<pclass="status" role="status" data-account-user-controls-device-status>Keyboard and Mouse are available. Game controllers auto-detect after the browser exposes them.</p>
120+
<pclass="status" role="status" data-account-user-controls-device-status>Game controllers auto-detect after the browser exposes them.</p>
83121
<divclass="table-wrapper">
84122
<tableclass="data-table" aria-label="Account user control profiles" data-account-user-controls-table>
85123
<thead>
@@ -93,7 +131,7 @@ <h2>Physical Input Mapping</h2>
93131
<th>Actions</th>
94132
</tr>
95133
</thead>
96-
<tbodydata-account-user-controls-list></tbody>
134+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Gamepad"></tbody>
97135
</table>
98136
</div>
99137
</div>

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Add Controls combo framework and editable Account user controls - PR_… · ToolboxAid/HTML-JavaScript-Gaming@cbf9b8a · GitHub
Skip to content

Commit cbf9b8a

Browse files
committed
Add Controls combo framework and editable Account user controls - PR_26162_042-controls-combo-framework-and-user-control-editing
1 parent ef56c9d commit cbf9b8a

10 files changed

Lines changed: 984 additions & 2403 deletions

‎account/user-controls-page.js‎

Lines changed: 118 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ export class AccountUserControlsPage {
130130
defaultLists: [...root.querySelectorAll("[data-account-user-controls-default-list]")],
131131
deviceSelect: root.querySelector("[data-account-user-controls-device]"),
132132
deviceStatus: root.querySelector("[data-account-user-controls-device-status]"),
133-
list: root.querySelector("[data-account-user-controls-list]"),
133+
familyButtons: [...root.querySelectorAll("[data-account-user-controls-edit-family]")],
134+
lists: [...root.querySelectorAll("[data-account-user-controls-list]")],
134135
refresh: root.querySelector("[data-account-user-controls-refresh]"),
135136
saveAll: root.querySelector("[data-account-user-controls-save-all]"),
136137
status: root.querySelector("[data-account-user-controls-status]"),
@@ -152,10 +153,15 @@ export class AccountUserControlsPage {
152153
this.elements.addProfile?.addEventListener("click",()=>this.addProfileForSelectedDevice());
153154
this.elements.saveAll?.addEventListener("click",()=>this.saveCurrentState());
154155
this.elements.deviceSelect?.addEventListener("change",()=>this.renderDeviceStatus());
155-
this.elements.list?.addEventListener("click",(event)=>this.handleListClick(event));
156-
this.elements.list?.addEventListener("change",(event)=>this.handleListChange(event));
157-
this.elements.list?.addEventListener("input",(event)=>this.handleListInput(event));
158-
this.elements.list?.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
156+
this.elements.familyButtons.forEach((button)=>{
157+
button.addEventListener("click",()=>this.editFamilyMappings(button.dataset.accountUserControlsEditFamily||""));
158+
});
159+
this.elements.lists.forEach((list)=>{
160+
list.addEventListener("click",(event)=>this.handleListClick(event));
161+
list.addEventListener("change",(event)=>this.handleListChange(event));
162+
list.addEventListener("input",(event)=>this.handleListInput(event));
163+
list.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
164+
});
159165
this.startDevicePolling();
160166
}
161167

@@ -177,25 +183,7 @@ export class AccountUserControlsPage {
177183
}
178184

179185
deviceOptions(){
180-
constkeyboard={
181-
controllerId: "generic-keyboard",
182-
controllerName: "Keyboard",
183-
deviceType: "Keyboard",
184-
inputs: [...KEYBOARD_INPUTS],
185-
label: "Keyboard",
186-
mappingProfile: "Keyboard Profile",
187-
value: "keyboard",
188-
};
189-
constmouse={
190-
controllerId: "generic-mouse",
191-
controllerName: "Mouse",
192-
deviceType: "Mouse",
193-
inputs: [...MOUSE_INPUTS],
194-
label: "Mouse",
195-
mappingProfile: "Mouse Profile",
196-
value: "mouse",
197-
};
198-
constgamepads=this.availableGamepads().map((gamepad)=>{
186+
returnthis.availableGamepads().map((gamepad)=>{
199187
constcontrollerName=gamepad.id||`Gamepad ${gamepad.index}`;
200188
return{
201189
controllerId: gamepad.id||`gamepad-${gamepad.index}`,
@@ -207,15 +195,37 @@ export class AccountUserControlsPage {
207195
value: `gamepad-${gamepad.index}`,
208196
};
209197
});
210-
return[keyboard,mouse, ...gamepads];
198+
}
199+
200+
familyDevice(family){
201+
if(family==="Mouse"){
202+
return{
203+
controllerId: "generic-mouse",
204+
controllerName: "Mouse",
205+
deviceType: "Mouse",
206+
inputs: [...MOUSE_INPUTS],
207+
label: "Mouse",
208+
mappingProfile: "Mouse Profile",
209+
value: "mouse",
210+
};
211+
}
212+
return{
213+
controllerId: "generic-keyboard",
214+
controllerName: "Keyboard",
215+
deviceType: "Keyboard",
216+
inputs: [...KEYBOARD_INPUTS],
217+
label: "Keyboard",
218+
mappingProfile: "Keyboard Profile",
219+
value: "keyboard",
220+
};
211221
}
212222

213223
deviceRefreshMessage(){
214224
constgamepadCount=this.availableGamepads().length;
215225
if(gamepadCount>0){
216-
return`PASS: Keyboard and Mouse available. ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
226+
return`PASS: ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
217227
}
218-
return"PASS: Keyboard and Mouse available. Gamepads auto-detect after browser exposes them.";
228+
return"PASS: Game controllers auto-detect after the browser exposes them.";
219229
}
220230

221231
selectedDevice(){
@@ -287,7 +297,7 @@ export class AccountUserControlsPage {
287297
}
288298
constselectedValue=this.elements.deviceSelect.value;
289299
constoptions=[
290-
{label: "Choose a physical controller",value: ""},
300+
{label: "Choose a game controller",value: ""},
291301
...this.deviceOptions().map((device)=>({label: device.label,value: device.value})),
292302
];
293303
this.elements.deviceSelect.classList.add("tool-form-control");
@@ -368,30 +378,62 @@ export class AccountUserControlsPage {
368378
},{once: true});
369379
}
370380

381+
profileListFamily(profile){
382+
constdeviceType=normalizeText(profile?.deviceType);
383+
if(deviceType==="Keyboard"||deviceType==="Mouse"){
384+
returndeviceType;
385+
}
386+
return"Gamepad";
387+
}
388+
389+
emptyProfileMessage(family){
390+
if(family==="Keyboard"){
391+
return"No keyboard user control profile saved yet.";
392+
}
393+
if(family==="Mouse"){
394+
return"No mouse user control profile saved yet.";
395+
}
396+
return"No game controller profiles saved yet.";
397+
}
398+
371399
renderProfiles(){
372-
if(!this.elements.list){
400+
if(!this.elements.lists.length){
373401
return;
374402
}
375-
constrows=[];
403+
constrowsByFamily=newMap(this.elements.lists.map((list)=>[
404+
list.dataset.accountUserControlsListFamily||"Gamepad",
405+
[],
406+
]));
376407
if(this.editingProfile){
377-
rows.push(...this.renderEditingRows(this.editingProfile.values));
408+
constfamily=this.profileListFamily(this.editingProfile.values);
409+
rowsByFamily.get(family)?.push(...this.renderEditingRows(this.editingProfile.values));
378410
}
379411
constvisibleProfiles=this.editingProfile?.id
380412
? this.profiles.filter((profile)=>profile.id!==this.editingProfile.id)
381413
: this.profiles;
382-
rows.push(...visibleProfiles.map((profile)=>this.renderProfileRow(profile)));
383-
if(!rows.length){
384-
constrow=document.createElement("tr");
385-
constcell=document.createElement("td");
386-
cell.colSpan=7;
387-
cell.textContent="No account user control profiles saved yet.";
388-
row.append(cell);
389-
rows.push(row);
390-
}
391-
this.elements.list.replaceChildren(...rows);
414+
visibleProfiles.forEach((profile)=>{
415+
constfamily=this.profileListFamily(profile);
416+
rowsByFamily.get(family)?.push(this.renderProfileRow(profile));
417+
});
418+
this.elements.lists.forEach((list)=>{
419+
constfamily=list.dataset.accountUserControlsListFamily||"Gamepad";
420+
constrows=rowsByFamily.get(family)||[];
421+
if(!rows.length){
422+
constrow=document.createElement("tr");
423+
constcell=document.createElement("td");
424+
cell.colSpan=7;
425+
cell.textContent=this.emptyProfileMessage(family);
426+
row.append(cell);
427+
rows.push(row);
428+
}
429+
list.replaceChildren(...rows);
430+
});
392431
if(this.elements.addProfile){
393432
this.elements.addProfile.disabled=Boolean(this.editingProfile);
394433
}
434+
this.elements.familyButtons.forEach((button)=>{
435+
button.disabled=Boolean(this.editingProfile);
436+
});
395437
}
396438

397439
profileInputSummary(profile){
@@ -455,8 +497,18 @@ export class AccountUserControlsPage {
455497
constwrapper=document.createElement("div");
456498
wrapper.className="content-grid";
457499
wrapper.dataset.accountUserControlsInputPair="true";
458-
constlabel=document.createElement("strong");
459-
label.textContent=inputMapping.physicalInput;
500+
consteditablePhysicalInput=profile.deviceType==="Keyboard"||profile.deviceType==="Mouse";
501+
constphysicalInputControl=editablePhysicalInput
502+
? document.createElement("input")
503+
: document.createElement("strong");
504+
if(editablePhysicalInput){
505+
physicalInputControl.type="text";
506+
physicalInputControl.value=inputMapping.physicalInput;
507+
physicalInputControl.dataset.accountUserControlsPhysicalInput=String(index);
508+
physicalInputControl.setAttribute("aria-label",`${profile.deviceType} physical input`);
509+
}else{
510+
physicalInputControl.textContent=inputMapping.physicalInput;
511+
}
460512
conststack=document.createElement("div");
461513
stack.className="content-stack content-stack--compact";
462514
constnormalizedOptions=[
@@ -522,7 +574,7 @@ export class AccountUserControlsPage {
522574
validation.className="status";
523575
validation.dataset.accountUserControlsInputValidation=String(index);
524576
stack.append(validation);
525-
wrapper.append(label,stack);
577+
wrapper.append(physicalInputControl,stack);
526578
returnwrapper;
527579
}
528580

@@ -579,24 +631,42 @@ export class AccountUserControlsPage {
579631
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
580632
}
581633

634+
editFamilyMappings(family){
635+
constnormalizedFamily=family==="Mouse" ? "Mouse" : "Keyboard";
636+
constdevice=this.familyDevice(normalizedFamily);
637+
constexisting=this.profiles.find((profile)=>profile.deviceType===device.deviceType&&profile.controllerId===device.controllerId);
638+
if(existing){
639+
this.editingProfile={id: existing.id,values: existing};
640+
this.renderProfiles();
641+
this.setStatus(`Editing existing ${existing.mappingProfile}.`);
642+
return;
643+
}
644+
constprofile=this.profileFromDevice(device);
645+
this.editingProfile={id: profile.id,values: profile};
646+
this.renderProfiles();
647+
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
648+
}
649+
582650
profileFromEditingRow(){
583651
constvalues=this.editingProfile?.values||{};
584-
constdetails=this.elements.list?.querySelector("[data-account-user-controls-editing-details]");
652+
constdetails=this.root.querySelector("[data-account-user-controls-editing-details]");
585653
constinputMappings=(values.inputMappings||[]).map((mapping,index)=>{
654+
constphysicalInputControl=details?.querySelector(`[data-account-user-controls-physical-input="${index}"]`);
586655
constnormalizedSelect=details?.querySelector(`[data-account-user-controls-input-normalized="${index}"]`);
587656
constnegativeSelect=details?.querySelector(`[data-account-user-controls-input-negative="${index}"]`);
588657
constpositiveSelect=details?.querySelector(`[data-account-user-controls-input-positive="${index}"]`);
589658
constdeadzoneInput=details?.querySelector(`[data-account-user-controls-deadzone="${index}"]`);
590659
constinvertInput=details?.querySelector(`[data-account-user-controls-invert="${index}"]`);
591660
constsensitivityInput=details?.querySelector(`[data-account-user-controls-sensitivity="${index}"]`);
661+
constphysicalInput=normalizeText(physicalInputControl?.value??mapping.physicalInput);
592662
constnegativeNormalizedInput=normalizeText(negativeSelect?.value??mapping.negativeNormalizedInput);
593663
constpositiveNormalizedInput=normalizeText(positiveSelect?.value??mapping.positiveNormalizedInput);
594664
return{
595665
deadzone: Number(deadzoneInput?.value??mapping.deadzone),
596666
invert: Boolean(invertInput?.checked??mapping.invert),
597667
negativeNormalizedInput,
598668
normalizedInput: positiveNormalizedInput||normalizeText(normalizedSelect?.value??mapping.normalizedInput)||negativeNormalizedInput,
599-
physicalInput: mapping.physicalInput,
669+
physicalInput,
600670
positiveNormalizedInput,
601671
sensitivity: sensitivityInput ? Number(sensitivityInput.value) : mapping.sensitivity,
602672
};
@@ -605,6 +675,7 @@ export class AccountUserControlsPage {
605675
...values,
606676
id: this.editingProfile?.id||"",
607677
inputMappings,
678+
inputs: inputMappings.map((mapping)=>mapping.physicalInput),
608679
});
609680
}
610681

@@ -662,7 +733,7 @@ export class AccountUserControlsPage {
662733

663734
renderInputValidation(validation){
664735
constinvalidIndexes=newSet(validation.invalidIndexes||[]);
665-
this.elements.list?.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
736+
this.root.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
666737
constindex=Number(status.dataset.accountUserControlsInputValidation);
667738
status.textContent=invalidIndexes.has(index)
668739
? "Needs normalized control, valid deadzone, and valid sensitivity."

‎account/user-controls.html‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@ <h2>Physical Input Mapping</h2>
4747
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Keyboard"></tbody>
4848
</table>
4949
</div>
50+
<divclass="content-cluster" aria-label="Keyboard user controls">
51+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Keyboard">Edit Keyboard Mappings</button>
52+
</div>
53+
<divclass="table-wrapper">
54+
<tableclass="data-table" aria-label="Keyboard user control profile" data-account-user-controls-table="Keyboard">
55+
<thead>
56+
<tr>
57+
<th>Physical Controller</th>
58+
<th>Physical Input</th>
59+
<th>Normalized Control</th>
60+
<th>Deadzone</th>
61+
<th>Invert</th>
62+
<th>Sensitivity</th>
63+
<th>Actions</th>
64+
</tr>
65+
</thead>
66+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Keyboard"></tbody>
67+
</table>
68+
</div>
5069
</div>
5170
</details>
5271
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Mouse">
@@ -65,6 +84,25 @@ <h2>Physical Input Mapping</h2>
6584
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Mouse"></tbody>
6685
</table>
6786
</div>
87+
<divclass="content-cluster" aria-label="Mouse user controls">
88+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Mouse">Edit Mouse Mappings</button>
89+
</div>
90+
<divclass="table-wrapper">
91+
<tableclass="data-table" aria-label="Mouse user control profile" data-account-user-controls-table="Mouse">
92+
<thead>
93+
<tr>
94+
<th>Physical Controller</th>
95+
<th>Physical Input</th>
96+
<th>Normalized Control</th>
97+
<th>Deadzone</th>
98+
<th>Invert</th>
99+
<th>Sensitivity</th>
100+
<th>Actions</th>
101+
</tr>
102+
</thead>
103+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Mouse"></tbody>
104+
</table>
105+
</div>
68106
</div>
69107
</details>
70108
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Game Controllers">
@@ -73,13 +111,13 @@ <h2>Physical Input Mapping</h2>
73111
<divclass="content-cluster" aria-label="Account controller device selection">
74112
<buttonclass="btn" type="button" data-account-user-controls-refresh>Refresh Devices</button>
75113
<label>
76-
Physical Controller
114+
Game Controller
77115
<selectdata-account-user-controls-device></select>
78116
</label>
79117
<buttonclass="btn" type="button" data-account-user-controls-add-profile>Create User Control Profile</button>
80118
<buttonclass="btn primary" type="button" data-account-user-controls-save-all>Save</button>
81119
</div>
82-
<pclass="status" role="status" data-account-user-controls-device-status>Keyboard and Mouse are available. Game controllers auto-detect after the browser exposes them.</p>
120+
<pclass="status" role="status" data-account-user-controls-device-status>Game controllers auto-detect after the browser exposes them.</p>
83121
<divclass="table-wrapper">
84122
<tableclass="data-table" aria-label="Account user control profiles" data-account-user-controls-table>
85123
<thead>
@@ -93,7 +131,7 @@ <h2>Physical Input Mapping</h2>
93131
<th>Actions</th>
94132
</tr>
95133
</thead>
96-
<tbodydata-account-user-controls-list></tbody>
134+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Gamepad"></tbody>
97135
</table>
98136
</div>
99137
</div>

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add Controls combo framework and editable Account user controls - PR_… · ToolboxAid/HTML-JavaScript-Gaming@cbf9b8a · GitHub
Skip to content

Commit cbf9b8a

Browse files
committed
Add Controls combo framework and editable Account user controls - PR_26162_042-controls-combo-framework-and-user-control-editing
1 parent ef56c9d commit cbf9b8a

10 files changed

Lines changed: 984 additions & 2403 deletions

‎account/user-controls-page.js‎

Lines changed: 118 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ export class AccountUserControlsPage {
130130
defaultLists: [...root.querySelectorAll("[data-account-user-controls-default-list]")],
131131
deviceSelect: root.querySelector("[data-account-user-controls-device]"),
132132
deviceStatus: root.querySelector("[data-account-user-controls-device-status]"),
133-
list: root.querySelector("[data-account-user-controls-list]"),
133+
familyButtons: [...root.querySelectorAll("[data-account-user-controls-edit-family]")],
134+
lists: [...root.querySelectorAll("[data-account-user-controls-list]")],
134135
refresh: root.querySelector("[data-account-user-controls-refresh]"),
135136
saveAll: root.querySelector("[data-account-user-controls-save-all]"),
136137
status: root.querySelector("[data-account-user-controls-status]"),
@@ -152,10 +153,15 @@ export class AccountUserControlsPage {
152153
this.elements.addProfile?.addEventListener("click",()=>this.addProfileForSelectedDevice());
153154
this.elements.saveAll?.addEventListener("click",()=>this.saveCurrentState());
154155
this.elements.deviceSelect?.addEventListener("change",()=>this.renderDeviceStatus());
155-
this.elements.list?.addEventListener("click",(event)=>this.handleListClick(event));
156-
this.elements.list?.addEventListener("change",(event)=>this.handleListChange(event));
157-
this.elements.list?.addEventListener("input",(event)=>this.handleListInput(event));
158-
this.elements.list?.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
156+
this.elements.familyButtons.forEach((button)=>{
157+
button.addEventListener("click",()=>this.editFamilyMappings(button.dataset.accountUserControlsEditFamily||""));
158+
});
159+
this.elements.lists.forEach((list)=>{
160+
list.addEventListener("click",(event)=>this.handleListClick(event));
161+
list.addEventListener("change",(event)=>this.handleListChange(event));
162+
list.addEventListener("input",(event)=>this.handleListInput(event));
163+
list.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
164+
});
159165
this.startDevicePolling();
160166
}
161167

@@ -177,25 +183,7 @@ export class AccountUserControlsPage {
177183
}
178184

179185
deviceOptions(){
180-
constkeyboard={
181-
controllerId: "generic-keyboard",
182-
controllerName: "Keyboard",
183-
deviceType: "Keyboard",
184-
inputs: [...KEYBOARD_INPUTS],
185-
label: "Keyboard",
186-
mappingProfile: "Keyboard Profile",
187-
value: "keyboard",
188-
};
189-
constmouse={
190-
controllerId: "generic-mouse",
191-
controllerName: "Mouse",
192-
deviceType: "Mouse",
193-
inputs: [...MOUSE_INPUTS],
194-
label: "Mouse",
195-
mappingProfile: "Mouse Profile",
196-
value: "mouse",
197-
};
198-
constgamepads=this.availableGamepads().map((gamepad)=>{
186+
returnthis.availableGamepads().map((gamepad)=>{
199187
constcontrollerName=gamepad.id||`Gamepad ${gamepad.index}`;
200188
return{
201189
controllerId: gamepad.id||`gamepad-${gamepad.index}`,
@@ -207,15 +195,37 @@ export class AccountUserControlsPage {
207195
value: `gamepad-${gamepad.index}`,
208196
};
209197
});
210-
return[keyboard,mouse, ...gamepads];
198+
}
199+
200+
familyDevice(family){
201+
if(family==="Mouse"){
202+
return{
203+
controllerId: "generic-mouse",
204+
controllerName: "Mouse",
205+
deviceType: "Mouse",
206+
inputs: [...MOUSE_INPUTS],
207+
label: "Mouse",
208+
mappingProfile: "Mouse Profile",
209+
value: "mouse",
210+
};
211+
}
212+
return{
213+
controllerId: "generic-keyboard",
214+
controllerName: "Keyboard",
215+
deviceType: "Keyboard",
216+
inputs: [...KEYBOARD_INPUTS],
217+
label: "Keyboard",
218+
mappingProfile: "Keyboard Profile",
219+
value: "keyboard",
220+
};
211221
}
212222

213223
deviceRefreshMessage(){
214224
constgamepadCount=this.availableGamepads().length;
215225
if(gamepadCount>0){
216-
return`PASS: Keyboard and Mouse available. ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
226+
return`PASS: ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
217227
}
218-
return"PASS: Keyboard and Mouse available. Gamepads auto-detect after browser exposes them.";
228+
return"PASS: Game controllers auto-detect after the browser exposes them.";
219229
}
220230

221231
selectedDevice(){
@@ -287,7 +297,7 @@ export class AccountUserControlsPage {
287297
}
288298
constselectedValue=this.elements.deviceSelect.value;
289299
constoptions=[
290-
{label: "Choose a physical controller",value: ""},
300+
{label: "Choose a game controller",value: ""},
291301
...this.deviceOptions().map((device)=>({label: device.label,value: device.value})),
292302
];
293303
this.elements.deviceSelect.classList.add("tool-form-control");
@@ -368,30 +378,62 @@ export class AccountUserControlsPage {
368378
},{once: true});
369379
}
370380

381+
profileListFamily(profile){
382+
constdeviceType=normalizeText(profile?.deviceType);
383+
if(deviceType==="Keyboard"||deviceType==="Mouse"){
384+
returndeviceType;
385+
}
386+
return"Gamepad";
387+
}
388+
389+
emptyProfileMessage(family){
390+
if(family==="Keyboard"){
391+
return"No keyboard user control profile saved yet.";
392+
}
393+
if(family==="Mouse"){
394+
return"No mouse user control profile saved yet.";
395+
}
396+
return"No game controller profiles saved yet.";
397+
}
398+
371399
renderProfiles(){
372-
if(!this.elements.list){
400+
if(!this.elements.lists.length){
373401
return;
374402
}
375-
constrows=[];
403+
constrowsByFamily=newMap(this.elements.lists.map((list)=>[
404+
list.dataset.accountUserControlsListFamily||"Gamepad",
405+
[],
406+
]));
376407
if(this.editingProfile){
377-
rows.push(...this.renderEditingRows(this.editingProfile.values));
408+
constfamily=this.profileListFamily(this.editingProfile.values);
409+
rowsByFamily.get(family)?.push(...this.renderEditingRows(this.editingProfile.values));
378410
}
379411
constvisibleProfiles=this.editingProfile?.id
380412
? this.profiles.filter((profile)=>profile.id!==this.editingProfile.id)
381413
: this.profiles;
382-
rows.push(...visibleProfiles.map((profile)=>this.renderProfileRow(profile)));
383-
if(!rows.length){
384-
constrow=document.createElement("tr");
385-
constcell=document.createElement("td");
386-
cell.colSpan=7;
387-
cell.textContent="No account user control profiles saved yet.";
388-
row.append(cell);
389-
rows.push(row);
390-
}
391-
this.elements.list.replaceChildren(...rows);
414+
visibleProfiles.forEach((profile)=>{
415+
constfamily=this.profileListFamily(profile);
416+
rowsByFamily.get(family)?.push(this.renderProfileRow(profile));
417+
});
418+
this.elements.lists.forEach((list)=>{
419+
constfamily=list.dataset.accountUserControlsListFamily||"Gamepad";
420+
constrows=rowsByFamily.get(family)||[];
421+
if(!rows.length){
422+
constrow=document.createElement("tr");
423+
constcell=document.createElement("td");
424+
cell.colSpan=7;
425+
cell.textContent=this.emptyProfileMessage(family);
426+
row.append(cell);
427+
rows.push(row);
428+
}
429+
list.replaceChildren(...rows);
430+
});
392431
if(this.elements.addProfile){
393432
this.elements.addProfile.disabled=Boolean(this.editingProfile);
394433
}
434+
this.elements.familyButtons.forEach((button)=>{
435+
button.disabled=Boolean(this.editingProfile);
436+
});
395437
}
396438

397439
profileInputSummary(profile){
@@ -455,8 +497,18 @@ export class AccountUserControlsPage {
455497
constwrapper=document.createElement("div");
456498
wrapper.className="content-grid";
457499
wrapper.dataset.accountUserControlsInputPair="true";
458-
constlabel=document.createElement("strong");
459-
label.textContent=inputMapping.physicalInput;
500+
consteditablePhysicalInput=profile.deviceType==="Keyboard"||profile.deviceType==="Mouse";
501+
constphysicalInputControl=editablePhysicalInput
502+
? document.createElement("input")
503+
: document.createElement("strong");
504+
if(editablePhysicalInput){
505+
physicalInputControl.type="text";
506+
physicalInputControl.value=inputMapping.physicalInput;
507+
physicalInputControl.dataset.accountUserControlsPhysicalInput=String(index);
508+
physicalInputControl.setAttribute("aria-label",`${profile.deviceType} physical input`);
509+
}else{
510+
physicalInputControl.textContent=inputMapping.physicalInput;
511+
}
460512
conststack=document.createElement("div");
461513
stack.className="content-stack content-stack--compact";
462514
constnormalizedOptions=[
@@ -522,7 +574,7 @@ export class AccountUserControlsPage {
522574
validation.className="status";
523575
validation.dataset.accountUserControlsInputValidation=String(index);
524576
stack.append(validation);
525-
wrapper.append(label,stack);
577+
wrapper.append(physicalInputControl,stack);
526578
returnwrapper;
527579
}
528580

@@ -579,24 +631,42 @@ export class AccountUserControlsPage {
579631
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
580632
}
581633

634+
editFamilyMappings(family){
635+
constnormalizedFamily=family==="Mouse" ? "Mouse" : "Keyboard";
636+
constdevice=this.familyDevice(normalizedFamily);
637+
constexisting=this.profiles.find((profile)=>profile.deviceType===device.deviceType&&profile.controllerId===device.controllerId);
638+
if(existing){
639+
this.editingProfile={id: existing.id,values: existing};
640+
this.renderProfiles();
641+
this.setStatus(`Editing existing ${existing.mappingProfile}.`);
642+
return;
643+
}
644+
constprofile=this.profileFromDevice(device);
645+
this.editingProfile={id: profile.id,values: profile};
646+
this.renderProfiles();
647+
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
648+
}
649+
582650
profileFromEditingRow(){
583651
constvalues=this.editingProfile?.values||{};
584-
constdetails=this.elements.list?.querySelector("[data-account-user-controls-editing-details]");
652+
constdetails=this.root.querySelector("[data-account-user-controls-editing-details]");
585653
constinputMappings=(values.inputMappings||[]).map((mapping,index)=>{
654+
constphysicalInputControl=details?.querySelector(`[data-account-user-controls-physical-input="${index}"]`);
586655
constnormalizedSelect=details?.querySelector(`[data-account-user-controls-input-normalized="${index}"]`);
587656
constnegativeSelect=details?.querySelector(`[data-account-user-controls-input-negative="${index}"]`);
588657
constpositiveSelect=details?.querySelector(`[data-account-user-controls-input-positive="${index}"]`);
589658
constdeadzoneInput=details?.querySelector(`[data-account-user-controls-deadzone="${index}"]`);
590659
constinvertInput=details?.querySelector(`[data-account-user-controls-invert="${index}"]`);
591660
constsensitivityInput=details?.querySelector(`[data-account-user-controls-sensitivity="${index}"]`);
661+
constphysicalInput=normalizeText(physicalInputControl?.value??mapping.physicalInput);
592662
constnegativeNormalizedInput=normalizeText(negativeSelect?.value??mapping.negativeNormalizedInput);
593663
constpositiveNormalizedInput=normalizeText(positiveSelect?.value??mapping.positiveNormalizedInput);
594664
return{
595665
deadzone: Number(deadzoneInput?.value??mapping.deadzone),
596666
invert: Boolean(invertInput?.checked??mapping.invert),
597667
negativeNormalizedInput,
598668
normalizedInput: positiveNormalizedInput||normalizeText(normalizedSelect?.value??mapping.normalizedInput)||negativeNormalizedInput,
599-
physicalInput: mapping.physicalInput,
669+
physicalInput,
600670
positiveNormalizedInput,
601671
sensitivity: sensitivityInput ? Number(sensitivityInput.value) : mapping.sensitivity,
602672
};
@@ -605,6 +675,7 @@ export class AccountUserControlsPage {
605675
...values,
606676
id: this.editingProfile?.id||"",
607677
inputMappings,
678+
inputs: inputMappings.map((mapping)=>mapping.physicalInput),
608679
});
609680
}
610681

@@ -662,7 +733,7 @@ export class AccountUserControlsPage {
662733

663734
renderInputValidation(validation){
664735
constinvalidIndexes=newSet(validation.invalidIndexes||[]);
665-
this.elements.list?.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
736+
this.root.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
666737
constindex=Number(status.dataset.accountUserControlsInputValidation);
667738
status.textContent=invalidIndexes.has(index)
668739
? "Needs normalized control, valid deadzone, and valid sensitivity."

‎account/user-controls.html‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@ <h2>Physical Input Mapping</h2>
4747
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Keyboard"></tbody>
4848
</table>
4949
</div>
50+
<divclass="content-cluster" aria-label="Keyboard user controls">
51+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Keyboard">Edit Keyboard Mappings</button>
52+
</div>
53+
<divclass="table-wrapper">
54+
<tableclass="data-table" aria-label="Keyboard user control profile" data-account-user-controls-table="Keyboard">
55+
<thead>
56+
<tr>
57+
<th>Physical Controller</th>
58+
<th>Physical Input</th>
59+
<th>Normalized Control</th>
60+
<th>Deadzone</th>
61+
<th>Invert</th>
62+
<th>Sensitivity</th>
63+
<th>Actions</th>
64+
</tr>
65+
</thead>
66+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Keyboard"></tbody>
67+
</table>
68+
</div>
5069
</div>
5170
</details>
5271
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Mouse">
@@ -65,6 +84,25 @@ <h2>Physical Input Mapping</h2>
6584
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Mouse"></tbody>
6685
</table>
6786
</div>
87+
<divclass="content-cluster" aria-label="Mouse user controls">
88+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Mouse">Edit Mouse Mappings</button>
89+
</div>
90+
<divclass="table-wrapper">
91+
<tableclass="data-table" aria-label="Mouse user control profile" data-account-user-controls-table="Mouse">
92+
<thead>
93+
<tr>
94+
<th>Physical Controller</th>
95+
<th>Physical Input</th>
96+
<th>Normalized Control</th>
97+
<th>Deadzone</th>
98+
<th>Invert</th>
99+
<th>Sensitivity</th>
100+
<th>Actions</th>
101+
</tr>
102+
</thead>
103+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Mouse"></tbody>
104+
</table>
105+
</div>
68106
</div>
69107
</details>
70108
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Game Controllers">
@@ -73,13 +111,13 @@ <h2>Physical Input Mapping</h2>
73111
<divclass="content-cluster" aria-label="Account controller device selection">
74112
<buttonclass="btn" type="button" data-account-user-controls-refresh>Refresh Devices</button>
75113
<label>
76-
Physical Controller
114+
Game Controller
77115
<selectdata-account-user-controls-device></select>
78116
</label>
79117
<buttonclass="btn" type="button" data-account-user-controls-add-profile>Create User Control Profile</button>
80118
<buttonclass="btn primary" type="button" data-account-user-controls-save-all>Save</button>
81119
</div>
82-
<pclass="status" role="status" data-account-user-controls-device-status>Keyboard and Mouse are available. Game controllers auto-detect after the browser exposes them.</p>
120+
<pclass="status" role="status" data-account-user-controls-device-status>Game controllers auto-detect after the browser exposes them.</p>
83121
<divclass="table-wrapper">
84122
<tableclass="data-table" aria-label="Account user control profiles" data-account-user-controls-table>
85123
<thead>
@@ -93,7 +131,7 @@ <h2>Physical Input Mapping</h2>
93131
<th>Actions</th>
94132
</tr>
95133
</thead>
96-
<tbodydata-account-user-controls-list></tbody>
134+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Gamepad"></tbody>
97135
</table>
98136
</div>
99137
</div>

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add Controls combo framework and editable Account user controls - PR_… · ToolboxAid/HTML-JavaScript-Gaming@cbf9b8a · GitHub
Skip to content

Commit cbf9b8a

Browse files
committed
Add Controls combo framework and editable Account user controls - PR_26162_042-controls-combo-framework-and-user-control-editing
1 parent ef56c9d commit cbf9b8a

10 files changed

Lines changed: 984 additions & 2403 deletions

‎account/user-controls-page.js‎

Lines changed: 118 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ export class AccountUserControlsPage {
130130
defaultLists: [...root.querySelectorAll("[data-account-user-controls-default-list]")],
131131
deviceSelect: root.querySelector("[data-account-user-controls-device]"),
132132
deviceStatus: root.querySelector("[data-account-user-controls-device-status]"),
133-
list: root.querySelector("[data-account-user-controls-list]"),
133+
familyButtons: [...root.querySelectorAll("[data-account-user-controls-edit-family]")],
134+
lists: [...root.querySelectorAll("[data-account-user-controls-list]")],
134135
refresh: root.querySelector("[data-account-user-controls-refresh]"),
135136
saveAll: root.querySelector("[data-account-user-controls-save-all]"),
136137
status: root.querySelector("[data-account-user-controls-status]"),
@@ -152,10 +153,15 @@ export class AccountUserControlsPage {
152153
this.elements.addProfile?.addEventListener("click",()=>this.addProfileForSelectedDevice());
153154
this.elements.saveAll?.addEventListener("click",()=>this.saveCurrentState());
154155
this.elements.deviceSelect?.addEventListener("change",()=>this.renderDeviceStatus());
155-
this.elements.list?.addEventListener("click",(event)=>this.handleListClick(event));
156-
this.elements.list?.addEventListener("change",(event)=>this.handleListChange(event));
157-
this.elements.list?.addEventListener("input",(event)=>this.handleListInput(event));
158-
this.elements.list?.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
156+
this.elements.familyButtons.forEach((button)=>{
157+
button.addEventListener("click",()=>this.editFamilyMappings(button.dataset.accountUserControlsEditFamily||""));
158+
});
159+
this.elements.lists.forEach((list)=>{
160+
list.addEventListener("click",(event)=>this.handleListClick(event));
161+
list.addEventListener("change",(event)=>this.handleListChange(event));
162+
list.addEventListener("input",(event)=>this.handleListInput(event));
163+
list.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
164+
});
159165
this.startDevicePolling();
160166
}
161167

@@ -177,25 +183,7 @@ export class AccountUserControlsPage {
177183
}
178184

179185
deviceOptions(){
180-
constkeyboard={
181-
controllerId: "generic-keyboard",
182-
controllerName: "Keyboard",
183-
deviceType: "Keyboard",
184-
inputs: [...KEYBOARD_INPUTS],
185-
label: "Keyboard",
186-
mappingProfile: "Keyboard Profile",
187-
value: "keyboard",
188-
};
189-
constmouse={
190-
controllerId: "generic-mouse",
191-
controllerName: "Mouse",
192-
deviceType: "Mouse",
193-
inputs: [...MOUSE_INPUTS],
194-
label: "Mouse",
195-
mappingProfile: "Mouse Profile",
196-
value: "mouse",
197-
};
198-
constgamepads=this.availableGamepads().map((gamepad)=>{
186+
returnthis.availableGamepads().map((gamepad)=>{
199187
constcontrollerName=gamepad.id||`Gamepad ${gamepad.index}`;
200188
return{
201189
controllerId: gamepad.id||`gamepad-${gamepad.index}`,
@@ -207,15 +195,37 @@ export class AccountUserControlsPage {
207195
value: `gamepad-${gamepad.index}`,
208196
};
209197
});
210-
return[keyboard,mouse, ...gamepads];
198+
}
199+
200+
familyDevice(family){
201+
if(family==="Mouse"){
202+
return{
203+
controllerId: "generic-mouse",
204+
controllerName: "Mouse",
205+
deviceType: "Mouse",
206+
inputs: [...MOUSE_INPUTS],
207+
label: "Mouse",
208+
mappingProfile: "Mouse Profile",
209+
value: "mouse",
210+
};
211+
}
212+
return{
213+
controllerId: "generic-keyboard",
214+
controllerName: "Keyboard",
215+
deviceType: "Keyboard",
216+
inputs: [...KEYBOARD_INPUTS],
217+
label: "Keyboard",
218+
mappingProfile: "Keyboard Profile",
219+
value: "keyboard",
220+
};
211221
}
212222

213223
deviceRefreshMessage(){
214224
constgamepadCount=this.availableGamepads().length;
215225
if(gamepadCount>0){
216-
return`PASS: Keyboard and Mouse available. ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
226+
return`PASS: ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
217227
}
218-
return"PASS: Keyboard and Mouse available. Gamepads auto-detect after browser exposes them.";
228+
return"PASS: Game controllers auto-detect after the browser exposes them.";
219229
}
220230

221231
selectedDevice(){
@@ -287,7 +297,7 @@ export class AccountUserControlsPage {
287297
}
288298
constselectedValue=this.elements.deviceSelect.value;
289299
constoptions=[
290-
{label: "Choose a physical controller",value: ""},
300+
{label: "Choose a game controller",value: ""},
291301
...this.deviceOptions().map((device)=>({label: device.label,value: device.value})),
292302
];
293303
this.elements.deviceSelect.classList.add("tool-form-control");
@@ -368,30 +378,62 @@ export class AccountUserControlsPage {
368378
},{once: true});
369379
}
370380

381+
profileListFamily(profile){
382+
constdeviceType=normalizeText(profile?.deviceType);
383+
if(deviceType==="Keyboard"||deviceType==="Mouse"){
384+
returndeviceType;
385+
}
386+
return"Gamepad";
387+
}
388+
389+
emptyProfileMessage(family){
390+
if(family==="Keyboard"){
391+
return"No keyboard user control profile saved yet.";
392+
}
393+
if(family==="Mouse"){
394+
return"No mouse user control profile saved yet.";
395+
}
396+
return"No game controller profiles saved yet.";
397+
}
398+
371399
renderProfiles(){
372-
if(!this.elements.list){
400+
if(!this.elements.lists.length){
373401
return;
374402
}
375-
constrows=[];
403+
constrowsByFamily=newMap(this.elements.lists.map((list)=>[
404+
list.dataset.accountUserControlsListFamily||"Gamepad",
405+
[],
406+
]));
376407
if(this.editingProfile){
377-
rows.push(...this.renderEditingRows(this.editingProfile.values));
408+
constfamily=this.profileListFamily(this.editingProfile.values);
409+
rowsByFamily.get(family)?.push(...this.renderEditingRows(this.editingProfile.values));
378410
}
379411
constvisibleProfiles=this.editingProfile?.id
380412
? this.profiles.filter((profile)=>profile.id!==this.editingProfile.id)
381413
: this.profiles;
382-
rows.push(...visibleProfiles.map((profile)=>this.renderProfileRow(profile)));
383-
if(!rows.length){
384-
constrow=document.createElement("tr");
385-
constcell=document.createElement("td");
386-
cell.colSpan=7;
387-
cell.textContent="No account user control profiles saved yet.";
388-
row.append(cell);
389-
rows.push(row);
390-
}
391-
this.elements.list.replaceChildren(...rows);
414+
visibleProfiles.forEach((profile)=>{
415+
constfamily=this.profileListFamily(profile);
416+
rowsByFamily.get(family)?.push(this.renderProfileRow(profile));
417+
});
418+
this.elements.lists.forEach((list)=>{
419+
constfamily=list.dataset.accountUserControlsListFamily||"Gamepad";
420+
constrows=rowsByFamily.get(family)||[];
421+
if(!rows.length){
422+
constrow=document.createElement("tr");
423+
constcell=document.createElement("td");
424+
cell.colSpan=7;
425+
cell.textContent=this.emptyProfileMessage(family);
426+
row.append(cell);
427+
rows.push(row);
428+
}
429+
list.replaceChildren(...rows);
430+
});
392431
if(this.elements.addProfile){
393432
this.elements.addProfile.disabled=Boolean(this.editingProfile);
394433
}
434+
this.elements.familyButtons.forEach((button)=>{
435+
button.disabled=Boolean(this.editingProfile);
436+
});
395437
}
396438

397439
profileInputSummary(profile){
@@ -455,8 +497,18 @@ export class AccountUserControlsPage {
455497
constwrapper=document.createElement("div");
456498
wrapper.className="content-grid";
457499
wrapper.dataset.accountUserControlsInputPair="true";
458-
constlabel=document.createElement("strong");
459-
label.textContent=inputMapping.physicalInput;
500+
consteditablePhysicalInput=profile.deviceType==="Keyboard"||profile.deviceType==="Mouse";
501+
constphysicalInputControl=editablePhysicalInput
502+
? document.createElement("input")
503+
: document.createElement("strong");
504+
if(editablePhysicalInput){
505+
physicalInputControl.type="text";
506+
physicalInputControl.value=inputMapping.physicalInput;
507+
physicalInputControl.dataset.accountUserControlsPhysicalInput=String(index);
508+
physicalInputControl.setAttribute("aria-label",`${profile.deviceType} physical input`);
509+
}else{
510+
physicalInputControl.textContent=inputMapping.physicalInput;
511+
}
460512
conststack=document.createElement("div");
461513
stack.className="content-stack content-stack--compact";
462514
constnormalizedOptions=[
@@ -522,7 +574,7 @@ export class AccountUserControlsPage {
522574
validation.className="status";
523575
validation.dataset.accountUserControlsInputValidation=String(index);
524576
stack.append(validation);
525-
wrapper.append(label,stack);
577+
wrapper.append(physicalInputControl,stack);
526578
returnwrapper;
527579
}
528580

@@ -579,24 +631,42 @@ export class AccountUserControlsPage {
579631
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
580632
}
581633

634+
editFamilyMappings(family){
635+
constnormalizedFamily=family==="Mouse" ? "Mouse" : "Keyboard";
636+
constdevice=this.familyDevice(normalizedFamily);
637+
constexisting=this.profiles.find((profile)=>profile.deviceType===device.deviceType&&profile.controllerId===device.controllerId);
638+
if(existing){
639+
this.editingProfile={id: existing.id,values: existing};
640+
this.renderProfiles();
641+
this.setStatus(`Editing existing ${existing.mappingProfile}.`);
642+
return;
643+
}
644+
constprofile=this.profileFromDevice(device);
645+
this.editingProfile={id: profile.id,values: profile};
646+
this.renderProfiles();
647+
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
648+
}
649+
582650
profileFromEditingRow(){
583651
constvalues=this.editingProfile?.values||{};
584-
constdetails=this.elements.list?.querySelector("[data-account-user-controls-editing-details]");
652+
constdetails=this.root.querySelector("[data-account-user-controls-editing-details]");
585653
constinputMappings=(values.inputMappings||[]).map((mapping,index)=>{
654+
constphysicalInputControl=details?.querySelector(`[data-account-user-controls-physical-input="${index}"]`);
586655
constnormalizedSelect=details?.querySelector(`[data-account-user-controls-input-normalized="${index}"]`);
587656
constnegativeSelect=details?.querySelector(`[data-account-user-controls-input-negative="${index}"]`);
588657
constpositiveSelect=details?.querySelector(`[data-account-user-controls-input-positive="${index}"]`);
589658
constdeadzoneInput=details?.querySelector(`[data-account-user-controls-deadzone="${index}"]`);
590659
constinvertInput=details?.querySelector(`[data-account-user-controls-invert="${index}"]`);
591660
constsensitivityInput=details?.querySelector(`[data-account-user-controls-sensitivity="${index}"]`);
661+
constphysicalInput=normalizeText(physicalInputControl?.value??mapping.physicalInput);
592662
constnegativeNormalizedInput=normalizeText(negativeSelect?.value??mapping.negativeNormalizedInput);
593663
constpositiveNormalizedInput=normalizeText(positiveSelect?.value??mapping.positiveNormalizedInput);
594664
return{
595665
deadzone: Number(deadzoneInput?.value??mapping.deadzone),
596666
invert: Boolean(invertInput?.checked??mapping.invert),
597667
negativeNormalizedInput,
598668
normalizedInput: positiveNormalizedInput||normalizeText(normalizedSelect?.value??mapping.normalizedInput)||negativeNormalizedInput,
599-
physicalInput: mapping.physicalInput,
669+
physicalInput,
600670
positiveNormalizedInput,
601671
sensitivity: sensitivityInput ? Number(sensitivityInput.value) : mapping.sensitivity,
602672
};
@@ -605,6 +675,7 @@ export class AccountUserControlsPage {
605675
...values,
606676
id: this.editingProfile?.id||"",
607677
inputMappings,
678+
inputs: inputMappings.map((mapping)=>mapping.physicalInput),
608679
});
609680
}
610681

@@ -662,7 +733,7 @@ export class AccountUserControlsPage {
662733

663734
renderInputValidation(validation){
664735
constinvalidIndexes=newSet(validation.invalidIndexes||[]);
665-
this.elements.list?.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
736+
this.root.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
666737
constindex=Number(status.dataset.accountUserControlsInputValidation);
667738
status.textContent=invalidIndexes.has(index)
668739
? "Needs normalized control, valid deadzone, and valid sensitivity."

‎account/user-controls.html‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@ <h2>Physical Input Mapping</h2>
4747
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Keyboard"></tbody>
4848
</table>
4949
</div>
50+
<divclass="content-cluster" aria-label="Keyboard user controls">
51+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Keyboard">Edit Keyboard Mappings</button>
52+
</div>
53+
<divclass="table-wrapper">
54+
<tableclass="data-table" aria-label="Keyboard user control profile" data-account-user-controls-table="Keyboard">
55+
<thead>
56+
<tr>
57+
<th>Physical Controller</th>
58+
<th>Physical Input</th>
59+
<th>Normalized Control</th>
60+
<th>Deadzone</th>
61+
<th>Invert</th>
62+
<th>Sensitivity</th>
63+
<th>Actions</th>
64+
</tr>
65+
</thead>
66+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Keyboard"></tbody>
67+
</table>
68+
</div>
5069
</div>
5170
</details>
5271
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Mouse">
@@ -65,6 +84,25 @@ <h2>Physical Input Mapping</h2>
6584
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Mouse"></tbody>
6685
</table>
6786
</div>
87+
<divclass="content-cluster" aria-label="Mouse user controls">
88+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Mouse">Edit Mouse Mappings</button>
89+
</div>
90+
<divclass="table-wrapper">
91+
<tableclass="data-table" aria-label="Mouse user control profile" data-account-user-controls-table="Mouse">
92+
<thead>
93+
<tr>
94+
<th>Physical Controller</th>
95+
<th>Physical Input</th>
96+
<th>Normalized Control</th>
97+
<th>Deadzone</th>
98+
<th>Invert</th>
99+
<th>Sensitivity</th>
100+
<th>Actions</th>
101+
</tr>
102+
</thead>
103+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Mouse"></tbody>
104+
</table>
105+
</div>
68106
</div>
69107
</details>
70108
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Game Controllers">
@@ -73,13 +111,13 @@ <h2>Physical Input Mapping</h2>
73111
<divclass="content-cluster" aria-label="Account controller device selection">
74112
<buttonclass="btn" type="button" data-account-user-controls-refresh>Refresh Devices</button>
75113
<label>
76-
Physical Controller
114+
Game Controller
77115
<selectdata-account-user-controls-device></select>
78116
</label>
79117
<buttonclass="btn" type="button" data-account-user-controls-add-profile>Create User Control Profile</button>
80118
<buttonclass="btn primary" type="button" data-account-user-controls-save-all>Save</button>
81119
</div>
82-
<pclass="status" role="status" data-account-user-controls-device-status>Keyboard and Mouse are available. Game controllers auto-detect after the browser exposes them.</p>
120+
<pclass="status" role="status" data-account-user-controls-device-status>Game controllers auto-detect after the browser exposes them.</p>
83121
<divclass="table-wrapper">
84122
<tableclass="data-table" aria-label="Account user control profiles" data-account-user-controls-table>
85123
<thead>
@@ -93,7 +131,7 @@ <h2>Physical Input Mapping</h2>
93131
<th>Actions</th>
94132
</tr>
95133
</thead>
96-
<tbodydata-account-user-controls-list></tbody>
134+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Gamepad"></tbody>
97135
</table>
98136
</div>
99137
</div>

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Add Controls combo framework and editable Account user controls - PR_… · ToolboxAid/HTML-JavaScript-Gaming@cbf9b8a · GitHub
Skip to content

Commit cbf9b8a

Browse files
committed
Add Controls combo framework and editable Account user controls - PR_26162_042-controls-combo-framework-and-user-control-editing
1 parent ef56c9d commit cbf9b8a

10 files changed

Lines changed: 984 additions & 2403 deletions

‎account/user-controls-page.js‎

Lines changed: 118 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ export class AccountUserControlsPage {
130130
defaultLists: [...root.querySelectorAll("[data-account-user-controls-default-list]")],
131131
deviceSelect: root.querySelector("[data-account-user-controls-device]"),
132132
deviceStatus: root.querySelector("[data-account-user-controls-device-status]"),
133-
list: root.querySelector("[data-account-user-controls-list]"),
133+
familyButtons: [...root.querySelectorAll("[data-account-user-controls-edit-family]")],
134+
lists: [...root.querySelectorAll("[data-account-user-controls-list]")],
134135
refresh: root.querySelector("[data-account-user-controls-refresh]"),
135136
saveAll: root.querySelector("[data-account-user-controls-save-all]"),
136137
status: root.querySelector("[data-account-user-controls-status]"),
@@ -152,10 +153,15 @@ export class AccountUserControlsPage {
152153
this.elements.addProfile?.addEventListener("click",()=>this.addProfileForSelectedDevice());
153154
this.elements.saveAll?.addEventListener("click",()=>this.saveCurrentState());
154155
this.elements.deviceSelect?.addEventListener("change",()=>this.renderDeviceStatus());
155-
this.elements.list?.addEventListener("click",(event)=>this.handleListClick(event));
156-
this.elements.list?.addEventListener("change",(event)=>this.handleListChange(event));
157-
this.elements.list?.addEventListener("input",(event)=>this.handleListInput(event));
158-
this.elements.list?.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
156+
this.elements.familyButtons.forEach((button)=>{
157+
button.addEventListener("click",()=>this.editFamilyMappings(button.dataset.accountUserControlsEditFamily||""));
158+
});
159+
this.elements.lists.forEach((list)=>{
160+
list.addEventListener("click",(event)=>this.handleListClick(event));
161+
list.addEventListener("change",(event)=>this.handleListChange(event));
162+
list.addEventListener("input",(event)=>this.handleListInput(event));
163+
list.addEventListener("dblclick",(event)=>this.handleListDoubleClick(event));
164+
});
159165
this.startDevicePolling();
160166
}
161167

@@ -177,25 +183,7 @@ export class AccountUserControlsPage {
177183
}
178184

179185
deviceOptions(){
180-
constkeyboard={
181-
controllerId: "generic-keyboard",
182-
controllerName: "Keyboard",
183-
deviceType: "Keyboard",
184-
inputs: [...KEYBOARD_INPUTS],
185-
label: "Keyboard",
186-
mappingProfile: "Keyboard Profile",
187-
value: "keyboard",
188-
};
189-
constmouse={
190-
controllerId: "generic-mouse",
191-
controllerName: "Mouse",
192-
deviceType: "Mouse",
193-
inputs: [...MOUSE_INPUTS],
194-
label: "Mouse",
195-
mappingProfile: "Mouse Profile",
196-
value: "mouse",
197-
};
198-
constgamepads=this.availableGamepads().map((gamepad)=>{
186+
returnthis.availableGamepads().map((gamepad)=>{
199187
constcontrollerName=gamepad.id||`Gamepad ${gamepad.index}`;
200188
return{
201189
controllerId: gamepad.id||`gamepad-${gamepad.index}`,
@@ -207,15 +195,37 @@ export class AccountUserControlsPage {
207195
value: `gamepad-${gamepad.index}`,
208196
};
209197
});
210-
return[keyboard,mouse, ...gamepads];
198+
}
199+
200+
familyDevice(family){
201+
if(family==="Mouse"){
202+
return{
203+
controllerId: "generic-mouse",
204+
controllerName: "Mouse",
205+
deviceType: "Mouse",
206+
inputs: [...MOUSE_INPUTS],
207+
label: "Mouse",
208+
mappingProfile: "Mouse Profile",
209+
value: "mouse",
210+
};
211+
}
212+
return{
213+
controllerId: "generic-keyboard",
214+
controllerName: "Keyboard",
215+
deviceType: "Keyboard",
216+
inputs: [...KEYBOARD_INPUTS],
217+
label: "Keyboard",
218+
mappingProfile: "Keyboard Profile",
219+
value: "keyboard",
220+
};
211221
}
212222

213223
deviceRefreshMessage(){
214224
constgamepadCount=this.availableGamepads().length;
215225
if(gamepadCount>0){
216-
return`PASS: Keyboard and Mouse available. ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
226+
return`PASS: ${gamepadCount} game controller${gamepadCount===1 ? "" : "s"} detected automatically.`;
217227
}
218-
return"PASS: Keyboard and Mouse available. Gamepads auto-detect after browser exposes them.";
228+
return"PASS: Game controllers auto-detect after the browser exposes them.";
219229
}
220230

221231
selectedDevice(){
@@ -287,7 +297,7 @@ export class AccountUserControlsPage {
287297
}
288298
constselectedValue=this.elements.deviceSelect.value;
289299
constoptions=[
290-
{label: "Choose a physical controller",value: ""},
300+
{label: "Choose a game controller",value: ""},
291301
...this.deviceOptions().map((device)=>({label: device.label,value: device.value})),
292302
];
293303
this.elements.deviceSelect.classList.add("tool-form-control");
@@ -368,30 +378,62 @@ export class AccountUserControlsPage {
368378
},{once: true});
369379
}
370380

381+
profileListFamily(profile){
382+
constdeviceType=normalizeText(profile?.deviceType);
383+
if(deviceType==="Keyboard"||deviceType==="Mouse"){
384+
returndeviceType;
385+
}
386+
return"Gamepad";
387+
}
388+
389+
emptyProfileMessage(family){
390+
if(family==="Keyboard"){
391+
return"No keyboard user control profile saved yet.";
392+
}
393+
if(family==="Mouse"){
394+
return"No mouse user control profile saved yet.";
395+
}
396+
return"No game controller profiles saved yet.";
397+
}
398+
371399
renderProfiles(){
372-
if(!this.elements.list){
400+
if(!this.elements.lists.length){
373401
return;
374402
}
375-
constrows=[];
403+
constrowsByFamily=newMap(this.elements.lists.map((list)=>[
404+
list.dataset.accountUserControlsListFamily||"Gamepad",
405+
[],
406+
]));
376407
if(this.editingProfile){
377-
rows.push(...this.renderEditingRows(this.editingProfile.values));
408+
constfamily=this.profileListFamily(this.editingProfile.values);
409+
rowsByFamily.get(family)?.push(...this.renderEditingRows(this.editingProfile.values));
378410
}
379411
constvisibleProfiles=this.editingProfile?.id
380412
? this.profiles.filter((profile)=>profile.id!==this.editingProfile.id)
381413
: this.profiles;
382-
rows.push(...visibleProfiles.map((profile)=>this.renderProfileRow(profile)));
383-
if(!rows.length){
384-
constrow=document.createElement("tr");
385-
constcell=document.createElement("td");
386-
cell.colSpan=7;
387-
cell.textContent="No account user control profiles saved yet.";
388-
row.append(cell);
389-
rows.push(row);
390-
}
391-
this.elements.list.replaceChildren(...rows);
414+
visibleProfiles.forEach((profile)=>{
415+
constfamily=this.profileListFamily(profile);
416+
rowsByFamily.get(family)?.push(this.renderProfileRow(profile));
417+
});
418+
this.elements.lists.forEach((list)=>{
419+
constfamily=list.dataset.accountUserControlsListFamily||"Gamepad";
420+
constrows=rowsByFamily.get(family)||[];
421+
if(!rows.length){
422+
constrow=document.createElement("tr");
423+
constcell=document.createElement("td");
424+
cell.colSpan=7;
425+
cell.textContent=this.emptyProfileMessage(family);
426+
row.append(cell);
427+
rows.push(row);
428+
}
429+
list.replaceChildren(...rows);
430+
});
392431
if(this.elements.addProfile){
393432
this.elements.addProfile.disabled=Boolean(this.editingProfile);
394433
}
434+
this.elements.familyButtons.forEach((button)=>{
435+
button.disabled=Boolean(this.editingProfile);
436+
});
395437
}
396438

397439
profileInputSummary(profile){
@@ -455,8 +497,18 @@ export class AccountUserControlsPage {
455497
constwrapper=document.createElement("div");
456498
wrapper.className="content-grid";
457499
wrapper.dataset.accountUserControlsInputPair="true";
458-
constlabel=document.createElement("strong");
459-
label.textContent=inputMapping.physicalInput;
500+
consteditablePhysicalInput=profile.deviceType==="Keyboard"||profile.deviceType==="Mouse";
501+
constphysicalInputControl=editablePhysicalInput
502+
? document.createElement("input")
503+
: document.createElement("strong");
504+
if(editablePhysicalInput){
505+
physicalInputControl.type="text";
506+
physicalInputControl.value=inputMapping.physicalInput;
507+
physicalInputControl.dataset.accountUserControlsPhysicalInput=String(index);
508+
physicalInputControl.setAttribute("aria-label",`${profile.deviceType} physical input`);
509+
}else{
510+
physicalInputControl.textContent=inputMapping.physicalInput;
511+
}
460512
conststack=document.createElement("div");
461513
stack.className="content-stack content-stack--compact";
462514
constnormalizedOptions=[
@@ -522,7 +574,7 @@ export class AccountUserControlsPage {
522574
validation.className="status";
523575
validation.dataset.accountUserControlsInputValidation=String(index);
524576
stack.append(validation);
525-
wrapper.append(label,stack);
577+
wrapper.append(physicalInputControl,stack);
526578
returnwrapper;
527579
}
528580

@@ -579,24 +631,42 @@ export class AccountUserControlsPage {
579631
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
580632
}
581633

634+
editFamilyMappings(family){
635+
constnormalizedFamily=family==="Mouse" ? "Mouse" : "Keyboard";
636+
constdevice=this.familyDevice(normalizedFamily);
637+
constexisting=this.profiles.find((profile)=>profile.deviceType===device.deviceType&&profile.controllerId===device.controllerId);
638+
if(existing){
639+
this.editingProfile={id: existing.id,values: existing};
640+
this.renderProfiles();
641+
this.setStatus(`Editing existing ${existing.mappingProfile}.`);
642+
return;
643+
}
644+
constprofile=this.profileFromDevice(device);
645+
this.editingProfile={id: profile.id,values: profile};
646+
this.renderProfiles();
647+
this.setStatus(`Review ${profile.mappingProfile} before saving.`);
648+
}
649+
582650
profileFromEditingRow(){
583651
constvalues=this.editingProfile?.values||{};
584-
constdetails=this.elements.list?.querySelector("[data-account-user-controls-editing-details]");
652+
constdetails=this.root.querySelector("[data-account-user-controls-editing-details]");
585653
constinputMappings=(values.inputMappings||[]).map((mapping,index)=>{
654+
constphysicalInputControl=details?.querySelector(`[data-account-user-controls-physical-input="${index}"]`);
586655
constnormalizedSelect=details?.querySelector(`[data-account-user-controls-input-normalized="${index}"]`);
587656
constnegativeSelect=details?.querySelector(`[data-account-user-controls-input-negative="${index}"]`);
588657
constpositiveSelect=details?.querySelector(`[data-account-user-controls-input-positive="${index}"]`);
589658
constdeadzoneInput=details?.querySelector(`[data-account-user-controls-deadzone="${index}"]`);
590659
constinvertInput=details?.querySelector(`[data-account-user-controls-invert="${index}"]`);
591660
constsensitivityInput=details?.querySelector(`[data-account-user-controls-sensitivity="${index}"]`);
661+
constphysicalInput=normalizeText(physicalInputControl?.value??mapping.physicalInput);
592662
constnegativeNormalizedInput=normalizeText(negativeSelect?.value??mapping.negativeNormalizedInput);
593663
constpositiveNormalizedInput=normalizeText(positiveSelect?.value??mapping.positiveNormalizedInput);
594664
return{
595665
deadzone: Number(deadzoneInput?.value??mapping.deadzone),
596666
invert: Boolean(invertInput?.checked??mapping.invert),
597667
negativeNormalizedInput,
598668
normalizedInput: positiveNormalizedInput||normalizeText(normalizedSelect?.value??mapping.normalizedInput)||negativeNormalizedInput,
599-
physicalInput: mapping.physicalInput,
669+
physicalInput,
600670
positiveNormalizedInput,
601671
sensitivity: sensitivityInput ? Number(sensitivityInput.value) : mapping.sensitivity,
602672
};
@@ -605,6 +675,7 @@ export class AccountUserControlsPage {
605675
...values,
606676
id: this.editingProfile?.id||"",
607677
inputMappings,
678+
inputs: inputMappings.map((mapping)=>mapping.physicalInput),
608679
});
609680
}
610681

@@ -662,7 +733,7 @@ export class AccountUserControlsPage {
662733

663734
renderInputValidation(validation){
664735
constinvalidIndexes=newSet(validation.invalidIndexes||[]);
665-
this.elements.list?.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
736+
this.root.querySelectorAll("[data-account-user-controls-input-validation]").forEach((status)=>{
666737
constindex=Number(status.dataset.accountUserControlsInputValidation);
667738
status.textContent=invalidIndexes.has(index)
668739
? "Needs normalized control, valid deadzone, and valid sensitivity."

‎account/user-controls.html‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@ <h2>Physical Input Mapping</h2>
4747
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Keyboard"></tbody>
4848
</table>
4949
</div>
50+
<divclass="content-cluster" aria-label="Keyboard user controls">
51+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Keyboard">Edit Keyboard Mappings</button>
52+
</div>
53+
<divclass="table-wrapper">
54+
<tableclass="data-table" aria-label="Keyboard user control profile" data-account-user-controls-table="Keyboard">
55+
<thead>
56+
<tr>
57+
<th>Physical Controller</th>
58+
<th>Physical Input</th>
59+
<th>Normalized Control</th>
60+
<th>Deadzone</th>
61+
<th>Invert</th>
62+
<th>Sensitivity</th>
63+
<th>Actions</th>
64+
</tr>
65+
</thead>
66+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Keyboard"></tbody>
67+
</table>
68+
</div>
5069
</div>
5170
</details>
5271
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Mouse">
@@ -65,6 +84,25 @@ <h2>Physical Input Mapping</h2>
6584
<tbodydata-account-user-controls-default-listdata-account-user-controls-default-family="Mouse"></tbody>
6685
</table>
6786
</div>
87+
<divclass="content-cluster" aria-label="Mouse user controls">
88+
<buttonclass="btn" type="button" data-account-user-controls-edit-family="Mouse">Edit Mouse Mappings</button>
89+
</div>
90+
<divclass="table-wrapper">
91+
<tableclass="data-table" aria-label="Mouse user control profile" data-account-user-controls-table="Mouse">
92+
<thead>
93+
<tr>
94+
<th>Physical Controller</th>
95+
<th>Physical Input</th>
96+
<th>Normalized Control</th>
97+
<th>Deadzone</th>
98+
<th>Invert</th>
99+
<th>Sensitivity</th>
100+
<th>Actions</th>
101+
</tr>
102+
</thead>
103+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Mouse"></tbody>
104+
</table>
105+
</div>
68106
</div>
69107
</details>
70108
<detailsclass="vertical-accordion" opendata-account-user-controls-section="Game Controllers">
@@ -73,13 +111,13 @@ <h2>Physical Input Mapping</h2>
73111
<divclass="content-cluster" aria-label="Account controller device selection">
74112
<buttonclass="btn" type="button" data-account-user-controls-refresh>Refresh Devices</button>
75113
<label>
76-
Physical Controller
114+
Game Controller
77115
<selectdata-account-user-controls-device></select>
78116
</label>
79117
<buttonclass="btn" type="button" data-account-user-controls-add-profile>Create User Control Profile</button>
80118
<buttonclass="btn primary" type="button" data-account-user-controls-save-all>Save</button>
81119
</div>
82-
<pclass="status" role="status" data-account-user-controls-device-status>Keyboard and Mouse are available. Game controllers auto-detect after the browser exposes them.</p>
120+
<pclass="status" role="status" data-account-user-controls-device-status>Game controllers auto-detect after the browser exposes them.</p>
83121
<divclass="table-wrapper">
84122
<tableclass="data-table" aria-label="Account user control profiles" data-account-user-controls-table>
85123
<thead>
@@ -93,7 +131,7 @@ <h2>Physical Input Mapping</h2>
93131
<th>Actions</th>
94132
</tr>
95133
</thead>
96-
<tbodydata-account-user-controls-list></tbody>
134+
<tbodydata-account-user-controls-listdata-account-user-controls-list-family="Gamepad"></tbody>
97135
</table>
98136
</div>
99137
</div>

0 commit comments

Comments
 (0)