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 { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
const validInputTypes = [
`checkbox`,
`details`,
`divider`,
`error`,
`input`,
`select`,
];
export class Ask extends HandlebarsApplicationMixin(ApplicationV2) { export class Ask extends HandlebarsApplicationMixin(ApplicationV2) {
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
tag: `dialog`, tag: `dialog`,
@ -32,11 +41,7 @@ export class Ask extends HandlebarsApplicationMixin(ApplicationV2) {
static PARTS = { static PARTS = {
inputs: { inputs: {
template: filePath(`templates/Ask/inputs.hbs`), template: filePath(`templates/Ask/inputs.hbs`),
templates: [ templates: validInputTypes.map(type => filePath(`templates/Ask/inputs/${type}.hbs`)),
filePath(`templates/Ask/inputs/checkbox.hbs`),
filePath(`templates/Ask/inputs/details.hbs`),
filePath(`templates/Ask/inputs/input.hbs`),
],
}, },
controls: { controls: {
template: filePath(`templates/Ask/controls.hbs`), template: filePath(`templates/Ask/controls.hbs`),
@ -69,6 +74,14 @@ export class Ask extends HandlebarsApplicationMixin(ApplicationV2) {
} = {}) { } = {}) {
super(options); super(options);
this.alwaysUseAnswerObject = alwaysUseAnswerObject; 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._inputs = inputs;
this._description = description; this._description = description;
this._userOnCancel = onCancel; this._userOnCancel = onCancel;

View file

@ -1,5 +1,7 @@
import { filePath } from "../consts.mjs"; import { filePath } from "../consts.mjs";
import { options } from "./options.mjs";
export default { export default {
systemFilePath: filePath, 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 { p {
margin: 0; margin: 0;
grid-column: 1 / -1;
text-indent: 1em; 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"] { 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; --input-height: 2.5rem;
font-size: 1.75rem; 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; @layer resets, themes, elements, components, partials, apps, exceptions;
/* Resets */
@import url("./resets/hr.css") layer(resets);
@import url("./resets/inputs.css") layer(resets);
/* Themes */ /* Themes */
@import url("./themes/dark.css") layer(themes); @import url("./themes/dark.css") layer(themes);
@import url("./themes/light.css") layer(themes); @import url("./themes/light.css") layer(themes);
/* Elements */ /* Elements */
@import url("./elements/headers.css") layer(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/input.css") layer(elements);
@import url("./elements/p.css") layer(elements); @import url("./elements/p.css") layer(elements);
@import url("./elements/prose-mirror.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 { .taf > .window-content {
button, input { input[type="checkbox"] {
all: initial; all: initial;
&::after, &::before {
all: initial;
}
} }
} }

View file

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

View file

@ -1,12 +1,14 @@
<label <div class="prompt">
for="{{id}}" <label
> for="{{id}}"
{{ label }} >
</label> {{ label }}
<input </label>
type="checkbox" <input
id="{{ id }}" type="checkbox"
name="{{ key }}" id="{{ id }}"
{{ checked defaultValue }} name="{{ key }}"
{{#if autofocus}}autofocus{{/if}} {{ checked defaultValue }}
> {{#if autofocus}}autofocus{{/if}}
>
</div>

View file

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

View file

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

View file

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

View file

@ -1,12 +1,14 @@
<label <div class="prompt">
for="{{id}}" <label
> for="{{id}}"
{{ label }} >
</label> {{ label }}
<input </label>
type="{{ inputType }}" <input
id="{{ id }}" type="{{ inputType }}"
name="{{ key }}" id="{{ id }}"
{{ valueAttribute }}="{{ defaultValue }}" name="{{ key }}"
{{#if autofocus}}autofocus{{/if}} {{ 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>