mirror of
https://github.com/RoY7x/worldbuilding.git
synced 2025-04-30 02:31:41 -04:00
9: Add POC for actor templates
- This commit adds a proof-of-concept for retrieving actor template types. These are actors using the `worldbuilding.isTemplate` flag set to true. If there are multiple actors with that flag set to true, clicking the create actor button will pull up a prompt to choose from one of those types, which will then create an actor by duplicating the template actor's data. The flag will be unset on the new actor.
This commit is contained in:
parent
d3f030b4f7
commit
bac1d023c3
10 changed files with 270 additions and 85 deletions
|
@ -9,5 +9,9 @@
|
||||||
|
|
||||||
"SIMPLE.ResourceMin": "Min",
|
"SIMPLE.ResourceMin": "Min",
|
||||||
"SIMPLE.ResourceValue": "Value",
|
"SIMPLE.ResourceValue": "Value",
|
||||||
"SIMPLE.ResourceMax": "Max"
|
"SIMPLE.ResourceMax": "Max",
|
||||||
|
|
||||||
|
"SIMPLE.DefineTemplate": "Define as Template",
|
||||||
|
"SIMPLE.UnsetTemplate": "Unset Template",
|
||||||
|
"SIMPLE.NoTemplate": "No Template"
|
||||||
}
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { ATTRIBUTE_TYPES } from "./constants.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend the basic ActorSheet with some very simple modifications
|
* Extend the basic ActorSheet with some very simple modifications
|
||||||
* @extends {ActorSheet}
|
* @extends {ActorSheet}
|
||||||
|
@ -21,7 +23,7 @@ export class SimpleActorSheet extends ActorSheet {
|
||||||
/** @override */
|
/** @override */
|
||||||
getData() {
|
getData() {
|
||||||
const data = super.getData();
|
const data = super.getData();
|
||||||
data.dtypes = ["String", "Number", "Boolean", "Formula", "Resource"];
|
data.dtypes = ATTRIBUTE_TYPES;
|
||||||
for ( let attr of Object.values(data.data.attributes) ) {
|
for ( let attr of Object.values(data.data.attributes) ) {
|
||||||
attr.isCheckbox = attr.dtype === "Boolean";
|
attr.isCheckbox = attr.dtype === "Boolean";
|
||||||
attr.isResource = attr.dtype === "Resource";
|
attr.isResource = attr.dtype === "Resource";
|
||||||
|
|
1
module/constants.js
Normal file
1
module/constants.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export const ATTRIBUTE_TYPES = ["String", "Number", "Boolean", "Formula", "Resource"];
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { ATTRIBUTE_TYPES } from "./constants.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend the basic ItemSheet with some very simple modifications
|
* Extend the basic ItemSheet with some very simple modifications
|
||||||
* @extends {ItemSheet}
|
* @extends {ItemSheet}
|
||||||
|
@ -20,7 +22,7 @@ export class SimpleItemSheet extends ItemSheet {
|
||||||
/** @override */
|
/** @override */
|
||||||
getData() {
|
getData() {
|
||||||
const data = super.getData();
|
const data = super.getData();
|
||||||
data.dtypes = ["String", "Number", "Boolean", "Formula", "Resource"];
|
data.dtypes = ATTRIBUTE_TYPES;
|
||||||
for ( let attr of Object.values(data.data.attributes) ) {
|
for ( let attr of Object.values(data.data.attributes) ) {
|
||||||
attr.isCheckbox = attr.dtype === "Boolean";
|
attr.isCheckbox = attr.dtype === "Boolean";
|
||||||
attr.isResource = attr.dtype === "Resource";
|
attr.isResource = attr.dtype === "Resource";
|
||||||
|
|
181
module/simple.js
181
module/simple.js
|
@ -8,6 +8,7 @@
|
||||||
import { SimpleActor } from "./actor.js";
|
import { SimpleActor } from "./actor.js";
|
||||||
import { SimpleItemSheet } from "./item-sheet.js";
|
import { SimpleItemSheet } from "./item-sheet.js";
|
||||||
import { SimpleActorSheet } from "./actor-sheet.js";
|
import { SimpleActorSheet } from "./actor-sheet.js";
|
||||||
|
import { preloadHandlebarsTemplates } from "./templates.js";
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Foundry VTT Initialization */
|
/* Foundry VTT Initialization */
|
||||||
|
@ -89,4 +90,184 @@ Hooks.once("init", async function() {
|
||||||
return value.slugify({strict: true});
|
return value.slugify({strict: true});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Preload template partials.
|
||||||
|
preloadHandlebarsTemplates();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the actor template context menu.
|
||||||
|
*/
|
||||||
|
Hooks.on("getActorDirectoryEntryContext", (html, options) => {
|
||||||
|
// Define an actor as a template.
|
||||||
|
options.push({
|
||||||
|
name: game.i18n.localize("SIMPLE.DefineTemplate"),
|
||||||
|
icon: '<i class="fas fa-stamp"></i>',
|
||||||
|
condition: li => {
|
||||||
|
const actor = game.actors.get(li.data("entityId"));
|
||||||
|
return !actor.getFlag("worldbuilding", "isTemplate");
|
||||||
|
},
|
||||||
|
callback: li => {
|
||||||
|
const actor = game.actors.get(li.data("entityId"));
|
||||||
|
actor.setFlag("worldbuilding", "isTemplate", true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Undefine an actor as a template.
|
||||||
|
options.push({
|
||||||
|
name: game.i18n.localize("SIMPLE.UnsetTemplate"),
|
||||||
|
icon: '<i class="fas fa-times"></i>',
|
||||||
|
condition: li => {
|
||||||
|
const actor = game.actors.get(li.data("entityId"));
|
||||||
|
return actor.getFlag("worldbuilding", "isTemplate");
|
||||||
|
},
|
||||||
|
callback: li => {
|
||||||
|
const actor = game.actors.get(li.data("entityId"));
|
||||||
|
actor.setFlag("worldbuilding", "isTemplate", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the item template context menu.
|
||||||
|
*/
|
||||||
|
Hooks.on("getItemDirectoryEntryContext", (html, options) => {
|
||||||
|
// Define an item as a template.
|
||||||
|
options.push({
|
||||||
|
name: game.i18n.localize("SIMPLE.DefineTemplate"),
|
||||||
|
icon: '<i class="fas fa-stamp"></i>',
|
||||||
|
condition: li => {
|
||||||
|
const item = game.items.get(li.data("entityId"));
|
||||||
|
return !item.getFlag("worldbuilding", "isTemplate");
|
||||||
|
},
|
||||||
|
callback: li => {
|
||||||
|
const item = game.items.get(li.data("entityId"));
|
||||||
|
item.setFlag("worldbuilding", "isTemplate", true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Undefine an item as a template.
|
||||||
|
options.push({
|
||||||
|
name: game.i18n.localize("SIMPLE.UnsetTemplate"),
|
||||||
|
icon: '<i class="fas fa-times"></i>',
|
||||||
|
condition: li => {
|
||||||
|
const item = game.items.get(li.data("entityId"));
|
||||||
|
return item.getFlag("worldbuilding", "isTemplate");
|
||||||
|
},
|
||||||
|
callback: li => {
|
||||||
|
const item = game.items.get(li.data("entityId"));
|
||||||
|
item.setFlag("worldbuilding", "isTemplate", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the actor template selection dialog.
|
||||||
|
*/
|
||||||
|
ActorDirectory.prototype._onCreate = async (event) => {
|
||||||
|
// Do not allow the creation event to bubble to other listeners
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
_simpleDirectoryTemplates('actor');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the item template selection dialog.
|
||||||
|
*/
|
||||||
|
ItemDirectory.prototype._onCreate = async (event) => {
|
||||||
|
// Do not allow the creation event to bubble to other listeners
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
_simpleDirectoryTemplates('item');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the entity template dialog.
|
||||||
|
*
|
||||||
|
* Helper function to display a dialog if there are multiple template types
|
||||||
|
* defined for the entity type.
|
||||||
|
*
|
||||||
|
* @param {string} entityType - 'actor' or 'item'
|
||||||
|
*/
|
||||||
|
async function _simpleDirectoryTemplates(entityType = 'actor') {
|
||||||
|
// Retrieve the collection and class.
|
||||||
|
const entityCollection = entityType == 'actor' ? game.actors : game.items;
|
||||||
|
const cls = entityType == 'actor' ? Actor : Item;
|
||||||
|
|
||||||
|
// Query for all entities of this type using the "isTemplate" flag.
|
||||||
|
let entities = entityCollection.filter(a => a.data.flags?.worldbuilding?.isTemplate === true);
|
||||||
|
|
||||||
|
// Initialize variables related to the entity class.
|
||||||
|
let ent = game.i18n.localize(cls.config.label);
|
||||||
|
|
||||||
|
// Setup entity data.
|
||||||
|
let type = entityType == 'actor' ? 'character' : 'item';
|
||||||
|
let createData = {
|
||||||
|
name: `New ${ent}`,
|
||||||
|
type: type,
|
||||||
|
folder: event.currentTarget.dataset.folder
|
||||||
|
};
|
||||||
|
|
||||||
|
// If there's more than one entity template type, create a form.
|
||||||
|
if (entities.length > 0) {
|
||||||
|
// Build an array of types for the form, including an empty default.
|
||||||
|
let types = [{
|
||||||
|
value: null,
|
||||||
|
label: game.i18n.localize("SIMPLE.NoTemplate")
|
||||||
|
}];
|
||||||
|
|
||||||
|
// Append each of the user-defined actor/item types.
|
||||||
|
types = types.concat(entities.map(a => {
|
||||||
|
return {
|
||||||
|
value: a.data.name,
|
||||||
|
label: a.data.name
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Render the entity creation form
|
||||||
|
let templateData = {upper: ent, lower: ent.toLowerCase(), types: types},
|
||||||
|
dlg = await renderTemplate(`systems/worldbuilding/templates/sidebar/entity-create.html`, templateData);
|
||||||
|
|
||||||
|
// Render the confirmation dialog window
|
||||||
|
new Dialog({
|
||||||
|
title: `Create ${createData.name}`,
|
||||||
|
content: dlg,
|
||||||
|
buttons: {
|
||||||
|
create: {
|
||||||
|
icon: '<i class="fas fa-check"></i>',
|
||||||
|
label: `Create ${ent}`,
|
||||||
|
callback: html => {
|
||||||
|
// Get the form data.
|
||||||
|
const form = html[0].querySelector("form");
|
||||||
|
mergeObject(createData, validateForm(form));
|
||||||
|
|
||||||
|
// Store the type and name values, and retrieve the template entity.
|
||||||
|
let templateActor = entityCollection.getName(createData.type);
|
||||||
|
|
||||||
|
// If there's a template entity, handle the data.
|
||||||
|
if (templateActor) {
|
||||||
|
// Update the object with the existing template's values.
|
||||||
|
createData = mergeObject(templateActor.data, createData, {inplace: false});
|
||||||
|
createData.type = templateActor.data.type;
|
||||||
|
// Clear the flag so that this doesn't become a new template.
|
||||||
|
delete createData.flags.worldbuilding.isTemplate;
|
||||||
|
}
|
||||||
|
// Otherwise, restore to a valid entity type (character/item).
|
||||||
|
else {
|
||||||
|
createData.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.create(createData, {renderSheet: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
default: "create"
|
||||||
|
}).render(true);
|
||||||
|
}
|
||||||
|
// Otherwise, just create a blank entity.
|
||||||
|
else {
|
||||||
|
cls.create(createData, {renderSheet: true});
|
||||||
|
}
|
||||||
|
}
|
16
module/templates.js
Normal file
16
module/templates.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/**
|
||||||
|
* Define a set of template paths to pre-load
|
||||||
|
* Pre-loaded templates are compiled and cached for fast access when rendering
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
export const preloadHandlebarsTemplates = async function() {
|
||||||
|
|
||||||
|
// Define template paths to load
|
||||||
|
const templatePaths = [
|
||||||
|
// Attribute list partial.
|
||||||
|
"systems/worldbuilding/templates/parts/sheet-attributes.html"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Load the template parts
|
||||||
|
return loadTemplates(templatePaths);
|
||||||
|
};
|
|
@ -73,47 +73,8 @@
|
||||||
<a class="attribute-control" data-action="create"><i class="fas fa-plus"></i></a>
|
<a class="attribute-control" data-action="create"><i class="fas fa-plus"></i></a>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<ol class="attributes-list">
|
{{!-- Render the attribute list partial. --}}
|
||||||
{{#each data.attributes as |attr key|}}
|
{{> "systems/worldbuilding/templates/parts/sheet-attributes.html" attributes=data.attributes dtypes=dtypes}}
|
||||||
<li class="attribute flexrow" data-attribute="{{key}}">
|
|
||||||
<input class="attribute-key" type="text" name="data.attributes.{{key}}.key" value="{{key}}"/>
|
|
||||||
{{!-- Handle booleans. --}}
|
|
||||||
{{#if attr.isCheckbox}}
|
|
||||||
<label class="attribute-value checkbox"><input type="checkbox" name="data.attributes.{{key}}.value" {{checked attr.value}}/></label>
|
|
||||||
{{else}}
|
|
||||||
{{!-- Handle resources. --}}
|
|
||||||
{{#if attr.isResource}}
|
|
||||||
<div class="attribute-group flexrow">
|
|
||||||
<span class="attribute-col flexcol">
|
|
||||||
<label for="data.attributes.{{key}}.min">{{localize "SIMPLE.ResourceMin"}}</label>
|
|
||||||
<input class="attribute-value" type="text" name="data.attributes.{{key}}.min" value="{{attr.min}}" data-dtype="{{attr.dtype}}"/>
|
|
||||||
</span>
|
|
||||||
<span class="attribute-col flexcol">
|
|
||||||
<label for="data.attributes.{{key}}.value">{{localize "SIMPLE.ResourceValue"}}</label>
|
|
||||||
<input class="attribute-value" type="text" name="data.attributes.{{key}}.value" value="{{attr.value}}" data-dtype="{{attr.dtype}}"/>
|
|
||||||
</span>
|
|
||||||
<span class="attribute-col flexcol">
|
|
||||||
<label for="data.attributes.{{key}}.max">{{localize "SIMPLE.ResourceMax"}}</label>
|
|
||||||
<input class="attribute-value" type="text" name="data.attributes.{{key}}.max" value="{{attr.max}}" data-dtype="{{attr.dtype}}"/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{{!-- Handle other input types. --}}
|
|
||||||
{{else}}
|
|
||||||
<input class="attribute-value" type="text" name="data.attributes.{{key}}.value" value="{{attr.value}}" data-dtype="{{attr.dtype}}"/>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
<input class="attribute-label" type="text" name="data.attributes.{{key}}.label" value="{{attr.label}}"/>
|
|
||||||
<select class="attribute-dtype" name="data.attributes.{{key}}.dtype">
|
|
||||||
{{#select attr.dtype}}
|
|
||||||
{{#each ../dtypes as |t|}}
|
|
||||||
<option value="{{t}}">{{t}}</option>
|
|
||||||
{{/each}}
|
|
||||||
{{/select}}
|
|
||||||
</select>
|
|
||||||
<a class="attribute-control" data-action="delete"><i class="fas fa-trash"></i></a>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ol>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -38,47 +38,8 @@
|
||||||
<a class="attribute-control" data-action="create"><i class="fas fa-plus"></i></a>
|
<a class="attribute-control" data-action="create"><i class="fas fa-plus"></i></a>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<ol class="attributes-list">
|
{{!-- Render the attribute list partial. --}}
|
||||||
{{#each data.attributes as |attr key|}}
|
{{> "systems/worldbuilding/templates/parts/sheet-attributes.html" attributes=data.attributes dtypes=dtypes}}
|
||||||
<li class="attribute flexrow" data-attribute="{{key}}">
|
|
||||||
<input class="attribute-key" type="text" name="data.attributes.{{key}}.key" value="{{key}}"/>
|
|
||||||
{{!-- Handle booleans. --}}
|
|
||||||
{{#if attr.isCheckbox}}
|
|
||||||
<label class="attribute-value checkbox"><input type="checkbox" name="data.attributes.{{key}}.value" {{checked attr.value}}/></label>
|
|
||||||
{{else}}
|
|
||||||
{{!-- Handle resources. --}}
|
|
||||||
{{#if attr.isResource}}
|
|
||||||
<div class="attribute-group flexrow">
|
|
||||||
<span class="attribute-col flexcol">
|
|
||||||
<label for="data.attributes.{{key}}.min">Min</label>
|
|
||||||
<input class="attribute-value" type="text" name="data.attributes.{{key}}.min" value="{{attr.min}}" data-dtype="{{attr.dtype}}"/>
|
|
||||||
</span>
|
|
||||||
<span class="attribute-col flexcol">
|
|
||||||
<label for="data.attributes.{{key}}.value">Current</label>
|
|
||||||
<input class="attribute-value" type="text" name="data.attributes.{{key}}.value" value="{{attr.value}}" data-dtype="{{attr.dtype}}"/>
|
|
||||||
</span>
|
|
||||||
<span class="attribute-col flexcol">
|
|
||||||
<label for="data.attributes.{{key}}.max">Max</label>
|
|
||||||
<input class="attribute-value" type="text" name="data.attributes.{{key}}.max" value="{{attr.max}}" data-dtype="{{attr.dtype}}"/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{{!-- Handle other input types. --}}
|
|
||||||
{{else}}
|
|
||||||
<input class="attribute-value" type="text" name="data.attributes.{{key}}.value" value="{{attr.value}}" data-dtype="{{attr.dtype}}"/>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
<input class="attribute-label" type="text" name="data.attributes.{{key}}.label" value="{{attr.label}}"/>
|
|
||||||
<select class="attribute-dtype" name="data.attributes.{{key}}.dtype">
|
|
||||||
{{#select attr.dtype}}
|
|
||||||
{{#each ../dtypes as |t|}}
|
|
||||||
<option value="{{t}}">{{t}}</option>
|
|
||||||
{{/each}}
|
|
||||||
{{/select}}
|
|
||||||
</select>
|
|
||||||
<a class="attribute-control" data-action="delete"><i class="fas fa-trash"></i></a>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ol>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
|
|
41
templates/parts/sheet-attributes.html
Normal file
41
templates/parts/sheet-attributes.html
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<ol class="attributes-list">
|
||||||
|
{{#each attributes as |attr key|}}
|
||||||
|
<li class="attribute flexrow" data-attribute="{{key}}">
|
||||||
|
<input class="attribute-key" type="text" name="data.attributes.{{key}}.key" value="{{key}}"/>
|
||||||
|
{{!-- Handle booleans. --}}
|
||||||
|
{{#if attr.isCheckbox}}
|
||||||
|
<label class="attribute-value checkbox"><input type="checkbox" name="data.attributes.{{key}}.value" {{checked attr.value}}/></label>
|
||||||
|
{{else}}
|
||||||
|
{{!-- Handle resources. --}}
|
||||||
|
{{#if attr.isResource}}
|
||||||
|
<div class="attribute-group flexrow">
|
||||||
|
<span class="attribute-col flexcol">
|
||||||
|
<label for="data.attributes.{{key}}.min">{{localize "SIMPLE.ResourceMin"}}</label>
|
||||||
|
<input class="attribute-value" type="text" name="data.attributes.{{key}}.min" value="{{attr.min}}" data-dtype="{{attr.dtype}}"/>
|
||||||
|
</span>
|
||||||
|
<span class="attribute-col flexcol">
|
||||||
|
<label for="data.attributes.{{key}}.value">{{localize "SIMPLE.ResourceValue"}}</label>
|
||||||
|
<input class="attribute-value" type="text" name="data.attributes.{{key}}.value" value="{{attr.value}}" data-dtype="{{attr.dtype}}"/>
|
||||||
|
</span>
|
||||||
|
<span class="attribute-col flexcol">
|
||||||
|
<label for="data.attributes.{{key}}.max">{{localize "SIMPLE.ResourceMax"}}</label>
|
||||||
|
<input class="attribute-value" type="text" name="data.attributes.{{key}}.max" value="{{attr.max}}" data-dtype="{{attr.dtype}}"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{!-- Handle other input types. --}}
|
||||||
|
{{else}}
|
||||||
|
<input class="attribute-value" type="text" name="data.attributes.{{key}}.value" value="{{attr.value}}" data-dtype="{{attr.dtype}}"/>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
<input class="attribute-label" type="text" name="data.attributes.{{key}}.label" value="{{attr.label}}"/>
|
||||||
|
<select class="attribute-dtype" name="data.attributes.{{key}}.dtype">
|
||||||
|
{{#select attr.dtype}}
|
||||||
|
{{#each ../dtypes as |t|}}
|
||||||
|
<option value="{{t}}">{{t}}</option>
|
||||||
|
{{/each}}
|
||||||
|
{{/select}}
|
||||||
|
</select>
|
||||||
|
<a class="attribute-control" data-action="delete"><i class="fas fa-trash"></i></a>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ol>
|
16
templates/sidebar/entity-create.html
Normal file
16
templates/sidebar/entity-create.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<form id="entity-create" autocomplete="off" onsubmit="event.preventDefault();">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "Name"}}</label>
|
||||||
|
<input type="text" name="name" placeholder="{{localize 'ENTITY.CreateNew'}} {{upper}}"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "Type"}}</label>
|
||||||
|
<select name="type">
|
||||||
|
{{#each types}}
|
||||||
|
<option value="{{this.value}}">{{this.label}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
<p class="notes">{{localize "ENTITY.TypeHint"}}</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
Loading…
Add table
Reference in a new issue