Attribute Item Subtype #76

Merged
Oliver merged 50 commits from feature/attribute-items into main 2026-04-27 02:12:56 +00:00
4 changed files with 56 additions and 5 deletions
Showing only changes of commit bda5d05969 - Show all commits

View file

@ -134,7 +134,8 @@
"missing-id": "An ID must be provided", "missing-id": "An ID must be provided",
"invalid-socket": "Invalid socket data received, this means a module or system bug is present.", "invalid-socket": "Invalid socket data received, this means a module or system bug is present.",
"unknown-socket-event": "An unknown socket event was received: {event}", "unknown-socket-event": "An unknown socket event was received: {event}",
"duplicate-attribute-key": "Cannot create an Attribute with a key (\"{key}\") that already exists on the Actor" "duplicate-attribute-key": "Cannot create an Attribute with a key (\"{key}\") that already exists on the Actor",
"invalid-attribute-key": "Attribute keys must be alphanumeric (a-z, 0-9) with underscores, invalid key provided: \"{key}\""
}, },
"warn": { "warn": {
"migration-in-progress": "Applying data migrations for version {version}. Please do NOT refresh the window while this warning is present." "migration-in-progress": "Applying data migrations for version {version}. Please do NOT refresh the window while this warning is present."

View file

@ -5,11 +5,11 @@ import { PlayerSheet } from "./apps/PlayerSheet.mjs";
import { QueryStatus } from "./apps/QueryStatus.mjs"; import { QueryStatus } from "./apps/QueryStatus.mjs";
// Utils // Utils
import { toID, isValidID, } from "./utils/toID.mjs";
import { attributeSorter } from "./utils/attributeSort.mjs"; import { attributeSorter } from "./utils/attributeSort.mjs";
import { DialogManager } from "./utils/DialogManager.mjs"; import { DialogManager } from "./utils/DialogManager.mjs";
import { localizer } from "./utils/localizer.mjs"; import { localizer } from "./utils/localizer.mjs";
import { QueryManager } from "./utils/QueryManager.mjs"; import { QueryManager } from "./utils/QueryManager.mjs";
import { toID } from "./utils/toID.mjs";
const { deepFreeze } = foundry.utils; const { deepFreeze } = foundry.utils;
@ -26,5 +26,6 @@ export const api = deepFreeze({
attributeSorter, attributeSorter,
localizer, localizer,
toID, toID,
isValidID,
}, },
}); });

View file

@ -1,3 +1,7 @@
import { isValidID, toID } from "../../utils/toID.mjs";
const { hasProperty } = foundry.utils;
export class AttributeItemData extends foundry.abstract.TypeDataModel { export class AttributeItemData extends foundry.abstract.TypeDataModel {
// #region Schema // #region Schema
static defineSchema() { static defineSchema() {
@ -37,6 +41,17 @@ export class AttributeItemData extends foundry.abstract.TypeDataModel {
// #region Lifecycle // #region Lifecycle
async _preCreate(data, options, user) { async _preCreate(data, options, user) {
// Assign the key as the ID'd name if isn't provided, or validate if
// it is provided.
if (!this.key) {
this.updateSource({ key: toID(this.parent.name) });
} else if (!isValidID(this.key)) {
ui.notifications.error(_loc(
`taf.notifs.error.invalid-attribute-key`,
{ key: this.key },
));
return false;
};
// Prevent duplicate Attribute keys from existing on a single Actor // Prevent duplicate Attribute keys from existing on a single Actor
if (this.parent.isEmbedded) { if (this.parent.isEmbedded) {
@ -55,6 +70,19 @@ export class AttributeItemData extends foundry.abstract.TypeDataModel {
return super._preCreate(data, options, user); return super._preCreate(data, options, user);
}; };
async _preUpdate(data, options, user) {
// Prevent invalid IDs
if (hasProperty(data, `system.key`) && !isValidID(data.system.key)) {
ui.notifications.error(_loc(
`taf.notifs.error.invalid-attribute-key`,
{ key: data.system.key },
));
delete data.system?.key;
};
return super._preUpdate(data, options, user);
};
// #endregion Lifecycle // #endregion Lifecycle
// #region Methods // #region Methods

View file

@ -1,6 +1,6 @@
/** /**
* A helper method that converts an arbitrary string into a format that can be * A helper method that converts an arbitrary string into a format
* used as an object key easily. * that can be used as an object key easily.
* *
* @param {string} text The text to convert * @param {string} text The text to convert
* @returns The converted ID * @returns The converted ID
@ -9,5 +9,26 @@ export function toID(text) {
return text return text
.toLowerCase() .toLowerCase()
.replace(/\s+/g, `_`) .replace(/\s+/g, `_`)
.replace(/\W/g, ``); .replace(/\W/g, ``)
.replace(/(^_|_$)/);
};
/**
* A helper method that reports if an arbitrary string is considered a
* valid ID for use in the system
*
* @param {string} text The text to check
* @returns Whether or not the text is a valid ID
*/
export function isValidID(text) {
return !(
// any uppercase characters
text.match(/[A-Z]/)
// any non-word characters
|| text.match(/\W/)
// any whitespace characters
|| text.match(/\s/)
);
}; };