Add the ability to display / edit the description from the item sheet
This commit is contained in:
parent
46a235b603
commit
69db3ca719
13 changed files with 168 additions and 17 deletions
|
|
@ -1,5 +1,7 @@
|
|||
import { deleteItemFromElement, editItemFromElement } from "./utils.mjs";
|
||||
import { DicePool } from "./DicePool.mjs";
|
||||
import { RichEditor } from "./RichEditor.mjs";
|
||||
import { toBoolean } from "../consts.mjs";
|
||||
|
||||
/**
|
||||
* A mixin that takes the class from HandlebarsApplicationMixin and
|
||||
|
|
@ -16,6 +18,7 @@ export function GenericAppMixin(HandlebarsApp) {
|
|||
roll: this._rollDice,
|
||||
editItem: (_event, target) => editItemFromElement(target),
|
||||
deleteItem: (_event, target) => deleteItemFromElement(target),
|
||||
openRichEditor: this.#openRichEditor,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -57,14 +60,39 @@ export function GenericAppMixin(HandlebarsApp) {
|
|||
|
||||
// #region Actions
|
||||
/** @this {GenericRipCryptApp} */
|
||||
static async _rollDice(_$e, el) {
|
||||
const data = el.dataset;
|
||||
static async _rollDice(_event, target) {
|
||||
const data = target.dataset;
|
||||
const diceCount = parseInt(data.diceCount);
|
||||
const flavor = data.flavor;
|
||||
|
||||
const dp = new DicePool({ diceCount, flavor });
|
||||
dp.render({ force: true });
|
||||
};
|
||||
|
||||
/** @this {GenericRipCryptApp} */
|
||||
static async #openRichEditor(_event, target) {
|
||||
const data = target.dataset;
|
||||
const {
|
||||
uuid,
|
||||
path,
|
||||
collaborative,
|
||||
compact,
|
||||
} = data;
|
||||
|
||||
if (!uuid || !path) {
|
||||
console.error(`Rich Editor requires a document uuid and path to edit`);
|
||||
return;
|
||||
};
|
||||
|
||||
const document = await fromUuid(uuid);
|
||||
const app = new RichEditor({
|
||||
document,
|
||||
path,
|
||||
collaborative: toBoolean(collaborative),
|
||||
compact: toBoolean(compact ),
|
||||
});
|
||||
app.render({ force: true });
|
||||
};
|
||||
// #endregion
|
||||
};
|
||||
return GenericRipCryptApp;
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ export class AllItemSheetV1 extends GenericAppMixin(HandlebarsApplicationMixin(I
|
|||
window: {
|
||||
resizable: false,
|
||||
},
|
||||
actions: {
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false,
|
||||
|
|
@ -40,7 +38,7 @@ export class AllItemSheetV1 extends GenericAppMixin(HandlebarsApplicationMixin(I
|
|||
ctx = await super._preparePartContext(partId, ctx, opts);
|
||||
ctx.item = this.document;
|
||||
|
||||
ctx.formFields = this.document.system.getFormFields(ctx);
|
||||
ctx.formFields = await this.document.system.getFormFields(ctx);
|
||||
|
||||
Logger.debug(`Context:`, ctx);
|
||||
return ctx;
|
||||
|
|
|
|||
|
|
@ -79,11 +79,6 @@ export class RichEditor extends HandlebarsApplicationMixin(DocumentSheetV2) {
|
|||
path: this.path,
|
||||
};
|
||||
|
||||
console.log({
|
||||
doc: this.document,
|
||||
path: this.path,
|
||||
value: this.document.system.description,
|
||||
});
|
||||
const value = getProperty(this.document, this.path);
|
||||
ctx.enriched = await TextEditor.enrichHTML(value);
|
||||
ctx.raw = value;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
const { getType } = foundry.utils;
|
||||
|
||||
// MARK: filePath
|
||||
export function filePath(path) {
|
||||
if (path.startsWith(`/`)) {
|
||||
|
|
@ -6,6 +8,24 @@ export function filePath(path) {
|
|||
return `systems/ripcrypt/${path}`;
|
||||
};
|
||||
|
||||
// MARK: toBoolean
|
||||
/**
|
||||
* Converts a value into a boolean based on the type of the value provided
|
||||
*
|
||||
* @param {any} val The value to convert
|
||||
*/
|
||||
export function toBoolean(val) {
|
||||
switch (getType(val)) {
|
||||
case `string`: {
|
||||
return val === `true`;
|
||||
};
|
||||
case `number`: {
|
||||
return val === 1;
|
||||
};
|
||||
};
|
||||
return Boolean(val);
|
||||
};
|
||||
|
||||
// MARK: documentSorter
|
||||
/**
|
||||
* @typedef {Object} Sortable
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export class CraftData extends SkillData {
|
|||
// #endregion
|
||||
|
||||
// #region Sheet Data
|
||||
getFormFields(_ctx) {
|
||||
async getFormFields(_ctx) {
|
||||
const fields = [
|
||||
{
|
||||
id: `fate-path`,
|
||||
|
|
@ -48,6 +48,15 @@ export class CraftData extends SkillData {
|
|||
value: aspect,
|
||||
})),
|
||||
},
|
||||
{
|
||||
id: `description`,
|
||||
type: `prosemirror`,
|
||||
label: `RipCrypt.common.description`,
|
||||
path: `system.description`,
|
||||
uuid: this.parent.uuid,
|
||||
value: await TextEditor.enrichHTML(this.description),
|
||||
collaborative: false,
|
||||
},
|
||||
{
|
||||
type: `group`,
|
||||
title: `RipCrypt.common.advances`,
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export class SkillData extends foundry.abstract.TypeDataModel {
|
|||
// #endregion
|
||||
|
||||
// #region Sheet Data
|
||||
getFormFields(_ctx) {
|
||||
async getFormFields(_ctx) {
|
||||
const fields = [
|
||||
{
|
||||
id: `fate-path`,
|
||||
|
|
@ -62,12 +62,13 @@ export class SkillData extends foundry.abstract.TypeDataModel {
|
|||
})),
|
||||
},
|
||||
{
|
||||
// TODO: Figure out how tf to make this work nicely on a generic level
|
||||
id: `description`,
|
||||
type: `prosemirror`,
|
||||
label: `RipCrypt.common.description`,
|
||||
path: `system.description`,
|
||||
collaborative: true,
|
||||
uuid: this.parent.uuid,
|
||||
value: await TextEditor.enrichHTML(this.description),
|
||||
collaborative: false,
|
||||
},
|
||||
{
|
||||
type: `group`,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { booleanInput } from "./booleanInput.mjs";
|
|||
import { dropdownInput } from "./dropdownInput.mjs";
|
||||
import { groupInput } from "./groupInput.mjs";
|
||||
import { numberInput } from "./numberInput.mjs";
|
||||
import { prosemirrorInput } from "./prosemirrorInput.mjs";
|
||||
import { stringSet } from "./stringSet.mjs";
|
||||
import { textInput } from "./textInput.mjs";
|
||||
|
||||
|
|
@ -10,6 +11,7 @@ const { getType } = foundry.utils;
|
|||
|
||||
const inputTypes = {
|
||||
"string-set": stringSet,
|
||||
prosemirror: prosemirrorInput,
|
||||
integer: numberInput,
|
||||
bar: barInput,
|
||||
dropdown: dropdownInput,
|
||||
|
|
@ -29,7 +31,10 @@ export function formFields(inputs, opts) {
|
|||
input.limited ??= true;
|
||||
};
|
||||
|
||||
if (typesToSanitize.has(getType(input.value))) {
|
||||
if (
|
||||
input.type !== `prosemirror`
|
||||
&& typesToSanitize.has(getType(input.value))
|
||||
) {
|
||||
input.value = Handlebars.escapeExpression(input.value);
|
||||
};
|
||||
fields.push(inputTypes[input.type](input, opts.data.root));
|
||||
|
|
|
|||
43
module/handlebarHelpers/inputs/prosemirrorInput.mjs
Normal file
43
module/handlebarHelpers/inputs/prosemirrorInput.mjs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { localizer } from "../../utils/Localizer.mjs";
|
||||
|
||||
export function prosemirrorInput(input, data) {
|
||||
const label = localizer(input.label);
|
||||
|
||||
if (!data.meta.editable) {
|
||||
return `<div data-input-type="prose-mirror">
|
||||
<div class="label-row">
|
||||
<div class="label">
|
||||
${label}
|
||||
</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
${input.value}
|
||||
</div>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
return `<div data-input-type="prose-mirror">
|
||||
<div class="label-row">
|
||||
<div class="label">
|
||||
${label}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
data-action="openRichEditor"
|
||||
data-uuid="${input.uuid}"
|
||||
data-path="${input.path}"
|
||||
data-compact="${input.compact}"
|
||||
data-collaborative="${input.collaborative}"
|
||||
>
|
||||
${localizer(`RipCrypt.common.edit`)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
This cannot be spread across multiple lines because of the :empty selector
|
||||
considering whitespace as "not being empty". Though browsers will eventually
|
||||
treat :empty as "empty, or only whitespace".
|
||||
-->
|
||||
<div class="value">${input.value}</div>
|
||||
</div>`;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue