Merge pull request #48 from Eldritch-Oliver/feature/tabbed-doc-sheet-config
Custom Document Sheet Config
This commit is contained in:
commit
f7fee99b44
13 changed files with 303 additions and 139 deletions
|
|
@ -13,6 +13,24 @@
|
||||||
},
|
},
|
||||||
"sheet-names": {
|
"sheet-names": {
|
||||||
"PlayerSheet": "Player Sheet"
|
"PlayerSheet": "Player Sheet"
|
||||||
|
},
|
||||||
|
"Apps": {
|
||||||
|
"TAFDocumentSheetConfig": {
|
||||||
|
"Sizing": "Sizing",
|
||||||
|
"Width": {
|
||||||
|
"label": "Width"
|
||||||
|
},
|
||||||
|
"Height": {
|
||||||
|
"label": "Height"
|
||||||
|
},
|
||||||
|
"Resizable": {
|
||||||
|
"label": "Resizable"
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"foundry": "Foundry",
|
||||||
|
"system": "Text-Based Actors"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { __ID__, filePath } from "../consts.mjs";
|
import { __ID__, filePath } from "../consts.mjs";
|
||||||
import { AttributeManager } from "./AttributeManager.mjs";
|
import { AttributeManager } from "./AttributeManager.mjs";
|
||||||
import { attributeSorter } from "../utils/attributeSort.mjs";
|
import { attributeSorter } from "../utils/attributeSort.mjs";
|
||||||
import { ResizeControlManager } from "./ResizeControlManager.mjs";
|
import { TAFDocumentSheetConfig } from "./TAFDocumentSheetConfig.mjs";
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||||
|
|
@ -28,7 +28,7 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
manageAttributes: this.#manageAttributes,
|
manageAttributes: this.#manageAttributes,
|
||||||
sizeSettings: this.#configureSizeSettings,
|
configureSheet: this.#configureSheet,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -78,15 +78,6 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
|
||||||
return isGM || (allowPlayerEdits && editable);
|
return isGM || (allowPlayerEdits && editable);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
controls.push({
|
|
||||||
icon: `fa-solid fa-crop-simple`,
|
|
||||||
label: `Configure Size`,
|
|
||||||
action: `sizeSettings`,
|
|
||||||
visible: () => {
|
|
||||||
const isGM = game.user.isGM;
|
|
||||||
return isGM;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return controls;
|
return controls;
|
||||||
};
|
};
|
||||||
|
|
@ -156,15 +147,18 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#sizeSettings = null;
|
static async #configureSheet(event) {
|
||||||
/** @this {PlayerSheet} */
|
event.stopPropagation();
|
||||||
static async #configureSizeSettings() {
|
if ( event.detail > 1 ) { return }
|
||||||
this.#sizeSettings ??= new ResizeControlManager({ document: this.actor });
|
|
||||||
if (this.#sizeSettings.rendered) {
|
// const docSheetConfigWidth = TAFDocumentSheetConfig.DEFAULT_OPTIONS.position.width;
|
||||||
await this.#sizeSettings.bringToFront();
|
new TAFDocumentSheetConfig({
|
||||||
} else {
|
document: this.document,
|
||||||
await this.#sizeSettings.render({ force: true });
|
position: {
|
||||||
};
|
top: this.position.top + 40,
|
||||||
|
left: this.position.left + ((this.position.width - 60) / 2),
|
||||||
|
},
|
||||||
|
}).render({ force: true });
|
||||||
};
|
};
|
||||||
// #endregion Actions
|
// #endregion Actions
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
import { __ID__, filePath } from "../consts.mjs";
|
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, DocumentSheetV2 } = foundry.applications.api;
|
|
||||||
const { getProperty } = foundry.utils;
|
|
||||||
|
|
||||||
export class ResizeControlManager extends HandlebarsApplicationMixin(DocumentSheetV2) {
|
|
||||||
|
|
||||||
// #region Options
|
|
||||||
static DEFAULT_OPTIONS = {
|
|
||||||
classes: [
|
|
||||||
__ID__,
|
|
||||||
`ResizeControlManager`,
|
|
||||||
],
|
|
||||||
position: {
|
|
||||||
width: 400,
|
|
||||||
height: `auto`,
|
|
||||||
},
|
|
||||||
window: {
|
|
||||||
resizable: true,
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
submitOnChange: false,
|
|
||||||
closeOnSubmit: true,
|
|
||||||
},
|
|
||||||
actions: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
static PARTS = {
|
|
||||||
settings: { template: filePath(`templates/ResizeControlManager/settings.hbs`) },
|
|
||||||
controls: { template: filePath(`templates/ResizeControlManager/controls.hbs`) },
|
|
||||||
};
|
|
||||||
// #endregion Options
|
|
||||||
|
|
||||||
// #region Instance Data
|
|
||||||
get title() {
|
|
||||||
return `Sizing Settings For : ${this.document.name}`;
|
|
||||||
};
|
|
||||||
// #endregion Instance Data
|
|
||||||
|
|
||||||
// #region Data Prep
|
|
||||||
async _prepareContext() {
|
|
||||||
const sizing = getProperty(this.document, `flags.${__ID__}.PlayerSheet.size`) ?? {};
|
|
||||||
|
|
||||||
const ctx = {
|
|
||||||
meta: {
|
|
||||||
idp: this.id,
|
|
||||||
},
|
|
||||||
width: sizing.width,
|
|
||||||
height: sizing.height,
|
|
||||||
resizable: sizing.resizable,
|
|
||||||
resizeOptions: [
|
|
||||||
{ label: `Default`, value: `` },
|
|
||||||
{ label: `Resizable`, value: `true` },
|
|
||||||
{ label: `No Resizing`, value: `false` },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
};
|
|
||||||
// #endregion Data Prep
|
|
||||||
};
|
|
||||||
171
module/apps/TAFDocumentSheetConfig.mjs
Normal file
171
module/apps/TAFDocumentSheetConfig.mjs
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
import { __ID__, filePath } from "../consts.mjs";
|
||||||
|
import { getDefaultSizing } from "../utils/getSizing.mjs";
|
||||||
|
|
||||||
|
const { diffObject, expandObject, flattenObject } = foundry.utils;
|
||||||
|
const { DocumentSheetConfig } = foundry.applications.apps;
|
||||||
|
const { CONST } = foundry;
|
||||||
|
|
||||||
|
export class TAFDocumentSheetConfig extends DocumentSheetConfig {
|
||||||
|
|
||||||
|
// #region Options
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: [`taf`],
|
||||||
|
form: {
|
||||||
|
handler: this.#onSubmit,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static get PARTS() {
|
||||||
|
const { form, footer } = super.PARTS;
|
||||||
|
return {
|
||||||
|
tabs: { template: `templates/generic/tab-navigation.hbs` },
|
||||||
|
foundryTab: {
|
||||||
|
...form,
|
||||||
|
template: filePath(`templates/TAFDocumentSheetConfig/foundry.hbs`),
|
||||||
|
templates: [ `templates/sheets/document-sheet-config.hbs` ],
|
||||||
|
},
|
||||||
|
systemTab: {
|
||||||
|
template: filePath(`templates/TAFDocumentSheetConfig/system.hbs`),
|
||||||
|
classes: [`standard-form`],
|
||||||
|
},
|
||||||
|
footer,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static TABS = {
|
||||||
|
main: {
|
||||||
|
initial: `system`,
|
||||||
|
labelPrefix: `taf.Apps.TAFDocumentSheetConfig.tabs`,
|
||||||
|
tabs: [
|
||||||
|
{ id: `system` },
|
||||||
|
{ id: `foundry` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// #endregion Options
|
||||||
|
|
||||||
|
// #region Data Prep
|
||||||
|
async _preparePartContext(partID, context, options) {
|
||||||
|
this._prepareTabs(`main`);
|
||||||
|
|
||||||
|
context.meta = {
|
||||||
|
idp: this.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (partID) {
|
||||||
|
case `foundryTab`: {
|
||||||
|
await this._prepareFormContext(context, options);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case `systemTab`: {
|
||||||
|
await this._prepareSystemSettingsContext(context, options);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case `footer`: {
|
||||||
|
await this._prepareFooterContext(context, options);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
async _prepareSystemSettingsContext(context, _options) {
|
||||||
|
// Inherited values for placeholders
|
||||||
|
const defaults = getDefaultSizing();
|
||||||
|
context.placeholders = {
|
||||||
|
...defaults,
|
||||||
|
resizable: defaults.resizable ? `Resizable` : `Not Resizable`,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Custom values from document itself
|
||||||
|
const sheetConfig = this.document.getFlag(__ID__, `PlayerSheet`) ?? {};
|
||||||
|
const sizing = sheetConfig.size ?? {};
|
||||||
|
context.values = {
|
||||||
|
width: sizing.width,
|
||||||
|
height: sizing.height,
|
||||||
|
resizable: sizing.resizable ?? ``,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Static prep
|
||||||
|
context.resizeOptions = [
|
||||||
|
{ label: `Default (${context.placeholders.resizable})`, value: `` },
|
||||||
|
{ label: `Resizable`, value: `true` },
|
||||||
|
{ label: `No Resizing`, value: `false` },
|
||||||
|
];
|
||||||
|
};
|
||||||
|
// #endregion Data Prep
|
||||||
|
|
||||||
|
// #region Actions
|
||||||
|
/** @this {TAFDocumentSheetConfig} */
|
||||||
|
static async #onSubmit(event, form, formData) {
|
||||||
|
const foundryReopen = await TAFDocumentSheetConfig.#submitFoundry.call(this, event, form, formData);
|
||||||
|
const systemReopen = await TAFDocumentSheetConfig.#submitSystem.call(this, event, form, formData);
|
||||||
|
if (foundryReopen || systemReopen) {
|
||||||
|
this.document._onSheetChange({ sheetOpen: true });
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is mostly the form submission handler that foundry uses in
|
||||||
|
* DocumentSheetConfig, however because we clobber that in order to save our
|
||||||
|
* own config stuff as well, we need to duplicate Foundry's handling and tweak
|
||||||
|
* it a bit to make it work nicely with our custom saving.
|
||||||
|
*
|
||||||
|
* @this {TAFDocumentSheetConfig}
|
||||||
|
*/
|
||||||
|
static async #submitFoundry(_event, _form, formData) {
|
||||||
|
const { object } = formData;
|
||||||
|
const { documentName, type = CONST.BASE_DOCUMENT_TYPE } = this.document;
|
||||||
|
|
||||||
|
// Update themes.
|
||||||
|
const themes = game.settings.get(`core`, `sheetThemes`);
|
||||||
|
const defaultTheme = foundry.utils.getProperty(themes, `defaults.${documentName}.${type}`);
|
||||||
|
const documentTheme = themes.documents?.[this.document.uuid];
|
||||||
|
const themeChanged = (object.defaultTheme !== defaultTheme) || (object.theme !== documentTheme);
|
||||||
|
if (themeChanged) {
|
||||||
|
foundry.utils.setProperty(themes, `defaults.${documentName}.${type}`, object.defaultTheme);
|
||||||
|
themes.documents ??= {};
|
||||||
|
themes.documents[this.document.uuid] = object.theme;
|
||||||
|
await game.settings.set(`core`, `sheetThemes`, themes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update sheets.
|
||||||
|
const { defaultClass } = this.constructor.getSheetClassesForSubType(documentName, type);
|
||||||
|
const sheetClass = this.document.getFlag(`core`, `sheetClass`) ?? ``;
|
||||||
|
const defaultSheetChanged = object.defaultClass !== defaultClass;
|
||||||
|
const documentSheetChanged = object.sheetClass !== sheetClass;
|
||||||
|
|
||||||
|
if (themeChanged || (game.user.isGM && defaultSheetChanged)) {
|
||||||
|
if (game.user.isGM && defaultSheetChanged) {
|
||||||
|
const setting = game.settings.get(`core`, `sheetClasses`);
|
||||||
|
foundry.utils.setProperty(setting, `${documentName}.${type}`, object.defaultClass);
|
||||||
|
await game.settings.set(`core`, `sheetClasses`, setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This causes us to manually rerender the sheet due to the theme or default
|
||||||
|
// sheet class changing resulting in no update making it to the client-document's
|
||||||
|
// _onUpdate handling
|
||||||
|
if (!documentSheetChanged) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the document-specific override.
|
||||||
|
if (documentSheetChanged) {
|
||||||
|
this.document.setFlag(`core`, `sheetClass`, object.sheetClass);
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @this {TAFDocumentSheetConfig} */
|
||||||
|
static async #submitSystem(_event, _form, formData) {
|
||||||
|
const { FLAGS: flags } = expandObject(formData.object);
|
||||||
|
const diff = flattenObject(diffObject(this.document.flags, flags));
|
||||||
|
const hasChanges = Object.keys(diff).length > 0;
|
||||||
|
if (hasChanges) {
|
||||||
|
await this.document.update({ flags });
|
||||||
|
};
|
||||||
|
return hasChanges;
|
||||||
|
};
|
||||||
|
// #endregion Actions
|
||||||
|
};
|
||||||
32
module/utils/getSizing.mjs
Normal file
32
module/utils/getSizing.mjs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { PlayerSheet } from "../apps/PlayerSheet.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef SheetSizing
|
||||||
|
* @property {number} width The initial width of the application
|
||||||
|
* @property {number} height The initial height of the application
|
||||||
|
* @property {boolean} resizable Whether or not the application
|
||||||
|
* is able to be resized with a drag handle.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the computed default sizing data based on world settings
|
||||||
|
* and the sheet class' DEFAULT_OPTIONS
|
||||||
|
* @returns {SheetSizing}
|
||||||
|
*/
|
||||||
|
export function getDefaultSizing() {
|
||||||
|
/** @type {SheetSizing} */
|
||||||
|
const sizing = {
|
||||||
|
width: undefined,
|
||||||
|
height: undefined,
|
||||||
|
resizable: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: defaults from world settings
|
||||||
|
|
||||||
|
// Defaults from the sheet class itself
|
||||||
|
sizing.height ||= PlayerSheet.DEFAULT_OPTIONS.position.height;
|
||||||
|
sizing.width ||= PlayerSheet.DEFAULT_OPTIONS.position.width;
|
||||||
|
sizing.resizable ||= PlayerSheet.DEFAULT_OPTIONS.window.resizable;
|
||||||
|
|
||||||
|
return sizing;
|
||||||
|
};
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
.taf.ResizeControlManager {
|
|
||||||
fieldset {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
border: 1px solid rebeccapurple;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 8px;
|
|
||||||
|
|
||||||
button {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15
styles/Apps/TAFDocumentSheetConfig.css
Normal file
15
styles/Apps/TAFDocumentSheetConfig.css
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
.taf.sheet-config {
|
||||||
|
|
||||||
|
section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.tab.active {
|
||||||
|
display: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,4 +20,4 @@
|
||||||
@import url("./Apps/Ask.css") layer(apps);
|
@import url("./Apps/Ask.css") layer(apps);
|
||||||
@import url("./Apps/AttributeManager.css") layer(apps);
|
@import url("./Apps/AttributeManager.css") layer(apps);
|
||||||
@import url("./Apps/PlayerSheet.css") layer(apps);
|
@import url("./Apps/PlayerSheet.css") layer(apps);
|
||||||
@import url("./Apps/ResizeControlManager.css") layer(apps);
|
@import url("./Apps/TAFDocumentSheetConfig.css") layer(apps);
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
"flags": {
|
"flags": {
|
||||||
"hotReload": {
|
"hotReload": {
|
||||||
"extensions": ["css", "hbs", "json", "js", "mjs", "svg"],
|
"extensions": ["css", "hbs", "json", "js", "mjs", "svg"],
|
||||||
"paths": ["templates", "langs", ".styles", "module", "assets"]
|
"paths": ["templates", "langs", "styles", "module", "assets"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
<div class="controls">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
Save and Close
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
<div class="settings">
|
|
||||||
<p>
|
|
||||||
Changes to these settings will only take effect after a reload of Foundry.
|
|
||||||
</p>
|
|
||||||
<fieldset>
|
|
||||||
<legend>Sizing</legend>
|
|
||||||
<label for="{{ meta.idp }}-width">Width</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
id="{{ meta.idp }}-width"
|
|
||||||
value="{{ width }}"
|
|
||||||
name="flags.taf.PlayerSheet.size.width"
|
|
||||||
>
|
|
||||||
<label for="{{ meta.idp }}-height">Height</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
id="{{ meta.idp }}-height"
|
|
||||||
value="{{ height }}"
|
|
||||||
name="flags.taf.PlayerSheet.size.height"
|
|
||||||
>
|
|
||||||
<label for="{{ meta.idp }}-resizable">Resizable?</label>
|
|
||||||
<select
|
|
||||||
id="{{ meta.idp }}-resizable"
|
|
||||||
name="flags.taf.PlayerSheet.size.resizable"
|
|
||||||
>
|
|
||||||
{{ taf-options resizable resizeOptions }}
|
|
||||||
</select>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
3
templates/TAFDocumentSheetConfig/foundry.hbs
Normal file
3
templates/TAFDocumentSheetConfig/foundry.hbs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div class="foundry tab {{tabs.foundry.cssClass}}" data-group="main" data-tab="foundry">
|
||||||
|
{{> "templates/sheets/document-sheet-config.hbs" }}
|
||||||
|
</div>
|
||||||
48
templates/TAFDocumentSheetConfig/system.hbs
Normal file
48
templates/TAFDocumentSheetConfig/system.hbs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
<div class="system tab {{tabs.system.cssClass}}" data-group="main" data-tab="system">
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
{{ localize "taf.Apps.TAFDocumentSheetConfig.Sizing" }}
|
||||||
|
</legend>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{meta.idp}}-width">
|
||||||
|
{{ localize "taf.Apps.TAFDocumentSheetConfig.Width.label" }}
|
||||||
|
</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="FLAGS.taf.PlayerSheet.size.width"
|
||||||
|
id="{{meta.idp}}-width"
|
||||||
|
value="{{values.width}}"
|
||||||
|
placeholder="{{placeholders.width}}"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{meta.idp}}-height">
|
||||||
|
{{ localize "taf.Apps.TAFDocumentSheetConfig.Height.label" }}
|
||||||
|
</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="FLAGS.taf.PlayerSheet.size.height"
|
||||||
|
id="{{meta.idp}}-height"
|
||||||
|
value="{{values.height}}"
|
||||||
|
placeholder="{{placeholders.height}}"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{meta.idp}}-resize">
|
||||||
|
{{ localize "taf.Apps.TAFDocumentSheetConfig.Resizable.label" }}
|
||||||
|
</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<select
|
||||||
|
name="FLAGS.taf.PlayerSheet.size.resizable"
|
||||||
|
id="{{meta.idp}}-resize"
|
||||||
|
>
|
||||||
|
{{ taf-options values.resizable resizeOptions }}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue