FEAT: add TypedPseudoDocument

REFACTOR: PreudoDocument
FIX: Typos Bug
This commit is contained in:
Joaquin Pereyra 2025-06-11 01:14:14 -03:00
parent a0b0411a48
commit 88bf26dad0
21 changed files with 559 additions and 447 deletions

View file

@ -1,3 +1,3 @@
export { default as FormulaField } from "./formulaField.mjs";
export { default as ForeignDocumentUUIDField } from "./foreignDocumentUUIDField.mjs";
export { default as PseudoDocumentsField } from "./pseudoDocumentsField.mjs";
export { default as FormulaField } from './formulaField.mjs';
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
export { default as PseudoDocumentsField } from './pseudoDocumentsField.mjs';

View file

@ -16,78 +16,78 @@
* Special case StringField which represents a formula.
*/
export default class FormulaField extends foundry.data.fields.StringField {
/**
* @param {FormulaFieldOptions} [options] - Options which configure the behavior of the field
* @param {foundry.data.types.DataFieldContext} [context] - Additional context which describes the field
*/
constructor(options, context) {
super(options, context);
}
/**
* @param {FormulaFieldOptions} [options] - Options which configure the behavior of the field
* @param {foundry.data.types.DataFieldContext} [context] - Additional context which describes the field
*/
constructor(options, context) {
super(options, context);
}
/** @inheritDoc */
static get _defaults() {
return foundry.utils.mergeObject(super._defaults, {
deterministic: false
});
}
/** @inheritDoc */
static get _defaults() {
return foundry.utils.mergeObject(super._defaults, {
deterministic: false
});
}
/* -------------------------------------------- */
/* -------------------------------------------- */
/** @inheritDoc */
_validateType(value) {
const roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, '1'));
roll.evaluateSync({ strict: false });
if (this.options.deterministic && !roll.isDeterministic)
throw new Error(`must not contain dice terms: ${value}`);
super._validateType(value);
}
/** @inheritDoc */
_validateType(value) {
const roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, "1"));
roll.evaluateSync({ strict: false });
if (this.options.deterministic && !roll.isDeterministic) throw new Error(`must not contain dice terms: ${value}`);
super._validateType(value);
}
/* -------------------------------------------- */
/* Active Effect Integration */
/* -------------------------------------------- */
/* -------------------------------------------- */
/* Active Effect Integration */
/* -------------------------------------------- */
/** @override */
_castChangeDelta(delta) {
return this._cast(delta).trim();
}
/** @override */
_castChangeDelta(delta) {
return this._cast(delta).trim();
}
/* -------------------------------------------- */
/* -------------------------------------------- */
/** @override */
_applyChangeAdd(value, delta, model, change) {
if (!value) return delta;
const operator = delta.startsWith('-') ? '-' : '+';
delta = delta.replace(/^[+-]/, '').trim();
return `${value} ${operator} ${delta}`;
}
/** @override */
_applyChangeAdd(value, delta, model, change) {
if (!value) return delta;
const operator = delta.startsWith("-") ? "-" : "+";
delta = delta.replace(/^[+-]/, "").trim();
return `${value} ${operator} ${delta}`;
}
/* -------------------------------------------- */
/* -------------------------------------------- */
/** @override */
_applyChangeMultiply(value, delta, model, change) {
if (!value) return delta;
const terms = new Roll(value).terms;
if (terms.length > 1) return `(${value}) * ${delta}`;
return `${value} * ${delta}`;
}
/** @override */
_applyChangeMultiply(value, delta, model, change) {
if (!value) return delta;
const terms = new Roll(value).terms;
if (terms.length > 1) return `(${value}) * ${delta}`;
return `${value} * ${delta}`;
}
/* -------------------------------------------- */
/* -------------------------------------------- */
/** @override */
_applyChangeUpgrade(value, delta, model, change) {
if (!value) return delta;
const terms = new Roll(value).terms;
if (terms.length === 1 && terms[0].fn === 'max') return value.replace(/\)$/, `, ${delta})`);
return `max(${value}, ${delta})`;
}
/** @override */
_applyChangeUpgrade(value, delta, model, change) {
if (!value) return delta;
const terms = new Roll(value).terms;
if ((terms.length === 1) && (terms[0].fn === "max")) return value.replace(/\)$/, `, ${delta})`);
return `max(${value}, ${delta})`;
}
/* -------------------------------------------- */
/* -------------------------------------------- */
/** @override */
_applyChangeDowngrade(value, delta, model, change) {
if (!value) return delta;
const terms = new Roll(value).terms;
if ((terms.length === 1) && (terms[0].fn === "min")) return value.replace(/\)$/, `, ${delta})`);
return `min(${value}, ${delta})`;
}
}
/** @override */
_applyChangeDowngrade(value, delta, model, change) {
if (!value) return delta;
const terms = new Roll(value).terms;
if (terms.length === 1 && terms[0].fn === 'min') return value.replace(/\)$/, `, ${delta})`);
return `min(${value}, ${delta})`;
}
}

View file

@ -1,16 +1,44 @@
import { BasePseudoDocument } from "../pseudo-documents/_types";
export default class PseudoDocumentsField extends foundry.data.fields.TypedObjectField {
import PseudoDocument from '../pseudo-documents/base/pseudoDocument.mjs';
const { TypedObjectField, TypedSchemaField } = foundry.data.fields;
/**
* @typedef _PseudoDocumentsFieldOptions
* @property {Number} [max] - The maximum amount of elements (default: `Infinity`)
* @property {String[]} [validTypes] - Allowed pseudo-documents types (default: `[]`)
* @property {Function} [validateKey] - callback for validate keys of the object;
* @typedef {foundry.data.types.DataFieldOptions & _PseudoDocumentsFieldOptions} PseudoDocumentsFieldOptions
*/
export default class PseudoDocumentsField extends TypedObjectField {
/**
* @param {PseudoDocument} model - The PseudoDocument of each entry in this collection.
* @param {PseudoDocumentsFieldOptions} [options] - Options which configure the behavior of the field
* @param {foundry.data.types.DataFieldContext} [context] - Additional context which describes the field
*/
constructor(model, options = {}, context = {}) {
options.validateKey ||= ((key) => foundry.data.validators.isValidId(key));
if (!(model instanceof BasePseudoDocument)) throw new Error("The model must be a PseudoDocument");
const field = new foundry.data.fields.EmbeddedDataField(model);
options.validateKey ||= key => foundry.data.validators.isValidId(key);
if (!foundry.utils.isSubclass(model, PseudoDocument)) throw new Error('The model must be a PseudoDocument');
const allTypes = foundry.utils.duplicate(model.TYPES);
options.validTypes ??= Object.keys(allTypes);
const filteredTypes = {};
for (const typeName of options.validTypes) {
if (typeName in allTypes) {
filteredTypes[typeName] = allTypes[typeName];
} else {
console.warn(`Document type "${typeName}" is not found in model.TYPES`);
}
}
const field = new TypedSchemaField(filteredTypes);
super(field, options, context);
}
/** @inheritdoc */
static get _defaults() {
return Object.assign(super._defaults, {
max: Infinity
max: Infinity,
validTypes: []
});
}
@ -19,4 +47,12 @@ export default class PseudoDocumentsField extends foundry.data.fields.TypedObjec
if (Object.keys(value).length > this.max) throw new Error(`cannot have more than ${this.max} elements`);
return super._validateType(value, options);
}
}
/** @override */
initialize(value, model, options = {}) {
if (!value) return;
value = super.initialize(value, model, options);
const collection = new foundry.utils.Collection(Object.values(value).map(d => [d._id, d]));
return collection;
}
}