Merge pull request #30 from Oliver-Akins/GH-19

Ask Application Block Improvements
This commit is contained in:
Oliver 2025-07-26 22:37:35 -06:00 committed by GitHub
commit ab14729e91
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 182 additions and 35 deletions

View file

@ -2,6 +2,15 @@ import { __ID__, filePath } from "../consts.mjs";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
const validInputTypes = [
`checkbox`,
`details`,
`divider`,
`error`,
`input`,
`select`,
];
export class Ask extends HandlebarsApplicationMixin(ApplicationV2) {
static DEFAULT_OPTIONS = {
tag: `dialog`,
@ -32,11 +41,7 @@ export class Ask extends HandlebarsApplicationMixin(ApplicationV2) {
static PARTS = {
inputs: {
template: filePath(`templates/Ask/inputs.hbs`),
templates: [
filePath(`templates/Ask/inputs/checkbox.hbs`),
filePath(`templates/Ask/inputs/details.hbs`),
filePath(`templates/Ask/inputs/input.hbs`),
],
templates: validInputTypes.map(type => filePath(`templates/Ask/inputs/${type}.hbs`)),
},
controls: {
template: filePath(`templates/Ask/controls.hbs`),
@ -69,6 +74,14 @@ export class Ask extends HandlebarsApplicationMixin(ApplicationV2) {
} = {}) {
super(options);
this.alwaysUseAnswerObject = alwaysUseAnswerObject;
for (const input of inputs) {
if (!validInputTypes.includes(input.type)) {
input.details = `Invalid input type provided: ${input.type}`;
input.type = `error`;
};
};
this._inputs = inputs;
this._description = description;
this._userOnCancel = onCancel;

View file

@ -1,5 +1,7 @@
import { filePath } from "../consts.mjs";
import { options } from "./options.mjs";
export default {
systemFilePath: filePath,
"taf-options": options,
};

View file

@ -0,0 +1,34 @@
/**
* @typedef {object} Option
* @property {string} [label]
* @property {string|number} value
* @property {boolean} [disabled]
*/
/**
* @param {string | number} selected The selected value
* @param {Array<Option | string>} opts The options that are valid
* @param {any} meta The Handlebars meta processing
*/
export function options(selected, opts, meta) {
const { localize = false } = meta.hash;
selected = Handlebars.escapeExpression(selected);
const htmlOptions = [];
for (let opt of opts) {
if (typeof opt === `string`) {
opt = { label: opt, value: opt };
};
opt.value = Handlebars.escapeExpression(opt.value);
htmlOptions.push(
`<option
value="${opt.value}"
${selected === opt.value ? `selected` : ``}
${opt.disabled ? `disabled` : ``}
>
${localize ? game.i18n.format(opt.label) : opt.label}
</option>`,
);
};
return new Handlebars.SafeString(htmlOptions.join(`\n`));
};

View file

@ -36,8 +36,19 @@
p {
margin: 0;
grid-column: 1 / -1;
text-indent: 1em;
&.error {
font-size: 1.1rem;
padding: 6px 8px;
box-shadow: 0 0 10px var(--color-shadow-dark);
color: var(--color-text-light-1);
border-radius: 5px;
text-align: center;
background: var(--color-level-error-bg);
border: 1px solid var(--color-level-error);
text-indent: 0;
}
}
input[type="checkbox"] {

7
styles/elements/hr.css Normal file
View file

@ -0,0 +1,7 @@
.taf > .window-content hr {
height: 1px;
background: rebeccapurple;
border-radius: 0;
margin: 0;
padding: 0;
}

View file

@ -3,4 +3,54 @@
--input-height: 2.5rem;
font-size: 1.75rem;
}
&[type="checkbox"] {
--checkbox-checked-color: var(--color-warm-1);
width: var(--checkbox-size);
height: var(--checkbox-size);
background: var(--input-background-color);
border: 2px solid var(--color-cool-3);
position: relative;
border-radius: 4px;
cursor: pointer;
&::before, &::after {
display: none;
}
&:focus-visible {
outline: 2px solid var(--checkbox-checked-color);
outline-offset: 3px;
}
&:checked::after {
display: block;
position: absolute;
inset: 4px;
z-index: 1;
content: "";
border-radius: 4px;
background: var(--checkbox-checked-color);
cursor: pointer;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
&::before {
display: block;
position: absolute;
inset: 0;
content: "";
background: var(--color-level-error-bg);
border-radius: 2px;
cursor: not-allowed;
}
&::after {
cursor: not-allowed;
}
}
}
}

View file

@ -1,11 +1,16 @@
@layer resets, themes, elements, components, partials, apps, exceptions;
/* Resets */
@import url("./resets/hr.css") layer(resets);
@import url("./resets/inputs.css") layer(resets);
/* Themes */
@import url("./themes/dark.css") layer(themes);
@import url("./themes/light.css") layer(themes);
/* Elements */
@import url("./elements/headers.css") layer(elements);
@import url("./elements/hr.css") layer(elements);
@import url("./elements/input.css") layer(elements);
@import url("./elements/p.css") layer(elements);
@import url("./elements/prose-mirror.css") layer(elements);

3
styles/resets/hr.css Normal file
View file

@ -0,0 +1,3 @@
.taf > .window-content hr {
all: initial;
}

View file

@ -1,5 +1,8 @@
.taf > .window-content {
button, input {
input[type="checkbox"] {
all: initial;
&::after, &::before {
all: initial;
}
}
}

View file

@ -5,8 +5,6 @@
</p>
{{/if}}
{{#each inputs as | i |}}
<div class="prompt">
{{> (concat (systemFilePath "templates/Ask/inputs/" ) i.type ".hbs") i}}
</div>
{{/each}}
</div>

View file

@ -1,3 +1,4 @@
<div class="prompt">
<label
for="{{id}}"
>
@ -10,3 +11,4 @@
{{ checked defaultValue }}
{{#if autofocus}}autofocus{{/if}}
>
</div>

View file

@ -1,3 +1,3 @@
<p class="prompt__details">
<p>
{{{ details }}}
</p>

View file

@ -0,0 +1 @@
<hr>

View file

@ -0,0 +1,3 @@
<p class="error">
{{ details }}
</p>

View file

@ -1,3 +1,4 @@
<div class="prompt">
<label
for="{{id}}"
>
@ -10,3 +11,4 @@
{{ valueAttribute }}="{{ defaultValue }}"
{{#if autofocus}}autofocus{{/if}}
>
</div>

View file

@ -0,0 +1,13 @@
<div class="prompt">
<label
for="{{id}}"
>
{{ label }}
</label>
<select
id="{{ id }}"
name="{{ key }}"
>
{{ taf-options defaultValue options }}
</select>
</div>