ENH: Add API for creating a DOM-based dialog focus grid

This commit is contained in:
bananarama92 2025-02-16 23:01:06 +01:00
parent 36d5bb532f
commit ac70b2df37
No known key found for this signature in database
GPG key ID: E83C7D3B5DA36248
2 changed files with 112 additions and 0 deletions
BondageClub

View file

@ -320,6 +320,31 @@
padding-inline: 0.15em 0.15em;
}
.dialog-focus-grid [role="radiogroup"] {
position: relative;
width: 100%;
height: 100%;
border: min(0.3vh, 0.15vw) solid #80808040;
}
.dialog-focus-grid [role="radio"] {
position: absolute;
border: min(0.3vh, 0.15vw) solid #80808040;
}
.dialog-focus-grid [role="radio"][data-blocked] {
border-color: #88000580;
}
.dialog-focus-grid [role="radio"][data-equipped] {
border-color: #D5A30080;
}
.dialog-focus-grid [role="radio"][aria-checked="true"] {
border-width: min(0.5vh, 0.25vw);
border-color: cyan;
}
@supports(height: 100dvh) {
.dialog-root {
--menu-button-size: min(9dvh, 4.5dvw);
@ -334,6 +359,18 @@
.dialog-grid-button > .button-tooltip {
width: calc(400% + 4 * min(0.2dvh, 0.1dvw) + 3 * var(--gap));
}
.dialog-focus-grid [role="radiogroup"] {
border-width: min(0.3dvh, 0.15dvw);
}
.dialog-focus-grid [role="radio"] {
border-width: min(0.3dvh, 0.15dvw);
}
.dialog-focus-grid [role="radio"][aria-checked="true"] {
border-width: min(0.5dvh, 0.25dvw);
}
}
@supports (background-color: color-mix(in srgb, black 50%, transparent)) {

View file

@ -4481,6 +4481,81 @@ function DialogDrawTopMenu(C) {
}
}
var DialogFocusGroup = {
/**
*
* @param {string} id - The ID for the to-be created focus group grid
* @param {(this: HTMLButtonElement, ev: MouseEvent) => any} listener - The listener to-be executed upon selecting a group; the group name can be retrieved from `this.name`
* @param {null | { required?: boolean, useDynamicGroupName?: boolean }} options - Further options for the to-be created focus group grid
* @returns {HTMLElement} - The created element
*/
Create(id, listener, options=null) {
options ??= {};
const root = document.getElementById(id);
if (root) {
console.error(`Element "${id}" already exists`);
return root;
}
let top = Infinity;
let bottom = 0;
let left = Infinity;
let right = 0;
/** @type {{ group: AssetGroup, index: number, zone: RectTuple }[]} */
const grid = [];
for (const group of AssetGroup) {
for (const [index, zone] of (group.Zone ?? []).entries()) {
grid.push({ group, index, zone});
left = Math.min(left, zone[0]);
right = Math.max(right, zone[0] + zone[2]);
top = Math.min(top, zone[1]);
bottom = Math.max(bottom, zone[1] + zone[3]);
}
}
grid.sort((a, b) => (a.zone[1] - b.zone[1]) || (a.zone[0] - b.zone[0]));
const width = right - left;
const height = bottom - top;
const children = grid.map(({ group, index, zone }, i) => ElementButton.Create(
`${id}-${group.Name}-${index}`,
listener,
{ noStyling: true, role: "radio" },
{ button: {
attributes: {
name: options.useDynamicGroupName ? group.DynamicGroupName : group.Name,
tabindex: i === 0 ? 0 : -1,
"aria-hidden": index !== 0 ? "true" : undefined,
"aria-label": group.Description,
},
style: {
left: `${100 * (zone[0] - left) / width}%`,
top: `${100 * (zone[1] - top) / height}%`,
width: `${100 * (zone[2] / width)}%`,
height: `${100 * (zone[3] / height)}%`,
},
}},
));
return ElementCreate({
tag: "div",
attributes: { id },
classList: ["dialog-focus-grid"],
children: [{
tag: "div",
children,
attributes: {
id: `${id}-radiogroup`,
role: "radiogroup",
"aria-required": options.required ? "true" : "false",
"aria-label": "Select focus group",
},
}],
});
},
};
/**
* Draws the left menu for the character
* @param {Character} C - The currently focused character