Add number inputs into the supported structures, and redesign the sheet to make it actually look good

This commit is contained in:
Oliver-Akins 2025-01-11 17:35:17 -07:00
parent 5aee7e4a29
commit fb52e1b58d
11 changed files with 161 additions and 48 deletions

View file

@ -74,7 +74,10 @@
"guts-value-edit": "The current amount of guts the character has", "guts-value-edit": "The current amount of guts the character has",
"guts-value-readonly": "The current amount of guts the character has", "guts-value-readonly": "The current amount of guts the character has",
"guts-max-readonly": "The maximum amount of guts the character can have" "guts-max-readonly": "The maximum amount of guts the character can have"
} },
"traits-placeholder": "New Trait...",
"short-range": "Short @RipCrypt.common.range",
"long-range": "Long @RipCrypt.common.range"
} }
} }
} }

View file

@ -62,14 +62,33 @@ export class WeaponData extends foundry.abstract.TypeDataModel {
{ {
type: `string-set`, type: `string-set`,
label: `RipCrypt.common.traits`, label: `RipCrypt.common.traits`,
placeholder: `RipCrypt.Apps.traits-placeholder`,
path: `system.traits`, path: `system.traits`,
value: this.traitString, value: ctx.meta.limited ? `???` : this.traitString,
}, },
{ type: `integer`, label: `Short Range` }, {
{ type: `integer`, label: `Long Range` }, type: `integer`,
{ type: `integer`, label: `Damage` }, label: `RipCrypt.Apps.short-range`,
{ type: `bar`, label: `Wear` }, path: `system.range.short`,
{ type: `dropdown`, label: `Access` }, value: ctx.meta.limited ? `???` : (this.range.short ?? ``),
min: 0,
},
{
type: `integer`,
label: `RipCrypt.Apps.long-range`,
path: `system.range.long`,
value: ctx.meta.limited ? `???` : (this.range.long ?? ``),
min: 0,
},
{
type: `integer`,
label: `RipCrypt.common.damage`,
path: `system.damage`,
value: ctx.meta.limited ? `???` : this.damage,
min: 0,
},
// { type: `bar`, label: `Wear` },
// { type: `dropdown`, label: `Access` },
]; ];
if (this.parent.isEmbedded) { if (this.parent.isEmbedded) {

View file

@ -1,8 +1,9 @@
import { numberInput } from "./numberInput.mjs";
import { stringSet } from "./stringSet.mjs"; import { stringSet } from "./stringSet.mjs";
const inputTypes = { const inputTypes = {
"string-set": stringSet, "string-set": stringSet,
integer: displayOnly, integer: numberInput,
bar: displayOnly, bar: displayOnly,
dropdown: displayOnly, dropdown: displayOnly,
boolean: displayOnly, boolean: displayOnly,
@ -13,10 +14,10 @@ function displayOnly(input) {
}; };
export function formFields(inputs, opts) { export function formFields(inputs, opts) {
let htmlString = ``; const fields = [];
for (const input of inputs) { for (const input of inputs) {
if (inputTypes[input.type] == null) { continue }; if (inputTypes[input.type] == null) { continue };
htmlString += inputTypes[input.type](input, opts.data.root); fields.push(inputTypes[input.type](input, opts.data.root));
}; };
return htmlString; return fields.join(opts.hash?.joiner ?? `<hr />`);
}; };

View file

@ -0,0 +1,35 @@
import { localizer } from "../../utils/Localizer.mjs";
const { randomID } = foundry.utils;
export function numberInput(input, data) {
const label = localizer(input.label);
const id = `${data.meta.idp}-${randomID(10)}`;
if (!data.meta.editable) {
return `<div data-input-type="integer">
<span class="label">${label}</span>
<span class="value">${data.meta.limited ? `???` : input.value}</span>
</div>`;
};
let attrs = ``;
if (input.min) { attrs += ` min="${input.min}"` };
if (input.max) { attrs += ` max="${input.max}"` };
if (input.step) { attrs += `step="${input.step}"` };
return `<div data-input-type="integer">
<label
for="${id}"
>
${label}
</label>
<input
type="number"
id="${id}"
value="${input.value}"
name="${input.path}"
${attrs}
/>
</div>`;
};

View file

@ -4,12 +4,34 @@ const { randomID } = foundry.utils;
export function stringSet(input, data) { export function stringSet(input, data) {
const label = localizer(input.label); const label = localizer(input.label);
const placeholder = localizer(input.placeholder ?? ``);
const id = `${data.meta.idp}-${randomID(10)}`; const id = `${data.meta.idp}-${randomID(10)}`;
if (!data.meta.editable) { if (!data.meta.editable) {
const tagList = input.value
.split(/,\s*/)
.filter(t => t.length > 0)
.map(t => {
return `<div class="tag">${t.trim()}</div>`;
});
let tags = tagList.join(``);
if (tagList.length === 0) {
tags = `---`;
};
if (data.meta.limited) {
tags = `???`;
};
return `<div data-input-type="string-set"> return `<div data-input-type="string-set">
<span class="label">${label}</span> <span class="label">${label}</span>
<span>${input.value}</span> <div
class="input-element-tags tags ${tags.length == 0 ? `empty` : ``}"
data-tag-count="${tagList.length}"
>
${tags}
</div>
</div>`; </div>`;
}; };
@ -21,6 +43,7 @@ export function stringSet(input, data) {
</label> </label>
<string-tags <string-tags
id="${id}" id="${id}"
placeholder="${placeholder}"
value="${input.value}" value="${input.value}"
name="${input.path}" name="${input.path}"
/> />

View file

@ -1,17 +1,17 @@
<div class="AllItemSheetV1"> <div class="AllItemSheetV1">
<div> {{#if meta.editable}}
{{#if meta.editable}} <label for="{{meta.idp}}-name">Name</label>
<label for="{{meta.idp}}-name">Name</label> <input
<input type="text"
type="text" id="{{meta.idp}}-name"
id="{{meta.idp}}-name" class="name"
name="name" name="name"
value="{{item.name}}" value="{{item.name}}"
> >
{{else}} {{else}}
<span class="label">Name</span> <span class="label">Name</span>
<span class="value">{{item.name}}</span> <span class="value">{{item.name}}</span>
{{/if}} {{/if}}
</div> <hr>
{{{ rc-formFields formFields }}} {{{ rc-formFields formFields }}}
</div> </div>

View file

@ -1,32 +1,42 @@
.ripcrypt .AllItemSheetV1 { .ripcrypt .AllItemSheetV1 {
--input-height: 1rem; --input-height: 1rem;
--input-underline: none;
--col-gap: 8px; --col-gap: 8px;
--row-gap: 8px;
--string-tags-tag-text: var(--header-text); --string-tags-tag-text: var(--header-text);
--string-tags-tag-background: var(--header-background); --string-tags-tag-background: var(--header-background);
--string-tags-add-text: white; --string-tags-add-text: white;
--string-tags-add-background: var(--accent-1); --string-tags-add-background: var(--accent-1);
--string-tags-input-text: white;
--string-tags-input-background: var(--accent-1);
display: grid; display: grid;
grid-template-rows: minmax(0, 1fr); grid-template-columns: auto minmax(0, 1fr);
grid-auto-rows: minmax(0, 1fr);
column-gap: var(--col-gap); column-gap: var(--col-gap);
row-gap: var(--row-gap);
padding: 8px;
background: var(--base-background); background: var(--base-background);
color: var(--base-text); color: var(--base-text);
> :nth-child(odd) { > [data-input-type] {
background: var(--alt-row-background); display: contents;
color: var(--alt-row-text); }
--string-tags-tag-text: var(--header-text); hr {
--string-tags-tag-background: var(--header-background); background: var(--accent-1);
--string-tags-add-text: var(--button-text); grid-column: 1 / -1;
--string-tags-add-background: var(--button-background); height: 1px;
width: 90%;
margin: 0 auto;
} }
label, .label { label, .label {
display: flex;
align-items: center;
box-sizing: border-box; box-sizing: border-box;
padding: 2px 4px; padding: 2px 4px;
text-transform: uppercase; text-transform: uppercase;
font-size: var(--font-size-14); font-size: var(--font-size-14);
@ -35,15 +45,17 @@
font-weight: bold; font-weight: bold;
} }
[data-input-type="string-set"] { input, .value, [data-tag-count] {
border-radius: 4px;
padding: 2px 4px; padding: 2px 4px;
display: flex; }
flex-direction: row; input {
column-gap: var(--col-gap); background: var(--accent-2);
grid-row: span 2; }
.value, [data-tag-count="0"] {
label, .label { border: 2px solid var(--accent-2);
padding: 0; }
} [data-tag-count="0"] {
justify-content: start;
} }
} }

View file

@ -15,5 +15,9 @@
&[type="number"] { &[type="number"] {
border-bottom: var(--input-underline); border-bottom: var(--input-underline);
} }
&::placeholder {
color: var(--input-placeholder-text);
}
} }
} }

View file

@ -2,4 +2,8 @@
&.small { &.small {
font-size: var(--font-size-10) font-size: var(--font-size-10)
} }
&:empty::before {
content: "\200b";
}
} }

View file

@ -5,16 +5,14 @@
grid-template-columns: 1fr min-content; grid-template-columns: 1fr min-content;
grid-template-rows: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr));
padding: 2px;
border: var(--string-tags-border); border: var(--string-tags-border);
.tags { .tags {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
.tag { input[type="text"] {
background: var(--tag-background, var(--header-background)); border-radius: 4px;
color: var(--tag-text, var(--header-text));
padding: 2px 4px; padding: 2px 4px;
} }
@ -25,4 +23,14 @@
width: 30px; width: 30px;
border-radius: 4px; border-radius: 4px;
} }
&:has(.tags.input-element-tags:empty) {
grid-template-rows: auto;
}
}
.ripcrypt .tag {
background: var(--tag-background, var(--header-background));
color: var(--tag-text, var(--header-text));
padding: 2px 4px;
} }

View file

@ -19,10 +19,14 @@
--input-underline: 2px dashed var(--accent-3); --input-underline: 2px dashed var(--accent-3);
--input-background: inherit; --input-background: inherit;
--input-text: white; --input-text: white;
--input-placeholder-text: rgba(255,255,255, 0.5);
--button-background: black; --button-background: black;
--button-text: var(--accent-3); --button-text: var(--accent-3);
--col-gap: 2px;
--row-gap: 0px;
/* Additional Variables */ /* Additional Variables */
--string-tags-border: inherit; --string-tags-border: inherit;
--string-tags-tag-background: inherit; --string-tags-tag-background: inherit;