import { L5r5eHtmlMultiSelectElement } from "../misc/l5r5e-multiselect.js"; /** * A Foundry `SetField` that renders as an {@link L5r5eHtmlMultiSelectElement} chip-input. * * Use this in a DataModel schema whenever a field stores an unordered collection of * string values drawn from a fixed option list. On form submission the element returns a * comma-separated string; `clean()` splits it back into an Array before Foundry processes * it, and `initialize()` wraps the result in a `Set` for use in the model. * * @example * // In a DataModel schema: * skills: new L5r5eSetField({ * options: [ * { value: "athletics", label: "Athletics" }, * { value: "meditation", label: "Meditation", disabled: true, tooltip: "Requires rank 3" }, * ] * }) * * // Renders automatically via {{formGroup}} in a Handlebars template: * // {{formGroup fields.skills name="skills" value=data.skills localize=true}} * * @param {object} options * @param {{ value: string, label: string, disabled?: boolean, tooltip?: string }[]} options.options * Flat list of selectable items. Passed directly to {@link L5r5eHtmlMultiSelectElement.create}. * @param {object[]} [options.groups] * Optional optgroup definitions, forwarded to the element factory unchanged. * @param {boolean} [options.hideDisabledOptions=false] * When true, disabled options are hidden from the dropdown instead of greyed out. */ export class L5r5eSetField extends foundry.data.fields.SetField { /** * Saved constructor options, used to reconstruct the multiselect input on form render. * @type {object} */ #savedOptions; /** * @param {object} options * @param {object} context */ constructor(options = {}, context = {}) { super( new foundry.data.fields.StringField({ choices: options.options?.map((option) => option.value) ?? [], }), options, context ); this.#savedOptions = options; } /** * @param {*} value * @param {object} model * @param {object} options * @return {Set} * @override */ initialize(value, model, options = {}) { if (!value || (Array.isArray(value) && value.length === 0)) { return new Set(); } return new Set(super.initialize(value, model, options).filter(Boolean)); } /** * @param {Set} value * @return {*[]|*} * @override */ toObject(value) { if (!value) { return value; } return Array.from(value).map((v) => this.element.toObject(v)); } /** * @param {string|Array} value * @param {object} options * @return {Array} * @override */ clean(value, options) { // Settings forms submit comma-separated strings; split before normal cleaning. if (typeof value === "string") { value = value.length ? value.split(",").filter(Boolean) : []; } return super.clean(value, options); } /** * @param {object} config * @return {L5r5eHtmlMultiSelectElement} * @override */ _toInput(config) { return L5r5eHtmlMultiSelectElement.create({ name: config.name, options: this.#savedOptions.options, groups: this.#savedOptions.groups, value: config.value, localize: config.localize, hideDisabledOptions: this.#savedOptions.hideDisabledOptions, }); } }