Start working on the dialog to make a new table

This commit is contained in:
Oliver-Akins 2025-04-30 22:51:03 -06:00
parent 46a9d46e9a
commit a3900a6c7c
8 changed files with 186 additions and 5 deletions

View file

@ -1,5 +1,6 @@
import { filePath } from "../consts.mjs"; import { filePath } from "../consts.mjs";
import { StatsViewer } from "./StatsViewer.mjs"; import { StatsViewer } from "./StatsViewer.mjs";
import { TableCreator } from "./TableCreator.mjs";
const { HandlebarsApplicationMixin } = foundry.applications.api; const { HandlebarsApplicationMixin } = foundry.applications.api;
const { AbstractSidebarTab } = foundry.applications.sidebar; const { AbstractSidebarTab } = foundry.applications.sidebar;
@ -16,6 +17,7 @@ export class StatSidebar extends HandlebarsApplicationMixin(AbstractSidebarTab)
}, },
actions: { actions: {
openStats: this.#openStats, openStats: this.#openStats,
createTable: this.#createTable,
}, },
}; };
@ -44,4 +46,10 @@ export class StatSidebar extends HandlebarsApplicationMixin(AbstractSidebarTab)
const app = new StatsViewer(); const app = new StatsViewer();
app.render({ force: true }); app.render({ force: true });
}; };
/** @this {StatSidebar} */
static async #createTable() {
const app = new TableCreator;
app.render({ force: true });
};
}; };

View file

@ -0,0 +1,96 @@
import { BucketTypes } from "../utils/validateValue.mjs";
import { filePath } from "../consts.mjs";
import { Logger } from "../utils/Logger.mjs";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export class TableCreator extends HandlebarsApplicationMixin(ApplicationV2) {
// #region Options
static DEFAULT_OPTIONS = {
classes: [
__ID__,
`TableCreator`,
],
window: {
title: `Create Table`,
frame: true,
positioned: true,
resizable: false,
minimizable: true,
},
position: {
width: 320,
height: 206,
},
actions: {
createTable: this.#createTable,
},
};
static PARTS = {
tableSelect: {
template: filePath(`templates/Apps/TableCreator.hbs`),
root: true,
},
};
// #endregion
async render({ userUpdated, ...opts } = {}) {
if (userUpdated && !this._selectedUsers.includes(userUpdated)) {
return;
}
await super.render(opts);
};
async _onRender(context, options) {
await super._onRender(context, options);
const elements = this.element
.querySelectorAll(`[data-bind]`);
for (const input of elements) {
input.addEventListener(`change`, this.#bindListener.bind(this));
};
};
_name = ``;
_type = BucketTypes.NUMBER;
async _preparePartContext(partId) {
const ctx = {};
ctx.meta = {
idp: this.id,
};
ctx.name = this._name;
ctx.type = this._type;
ctx.types = Object.values(BucketTypes);
if (import.meta.env.DEV) {
Logger.log(`Context`, ctx);
};
return ctx;
};
/**
* @param {Event} event
*/
async #bindListener(event) {
const target = event.target;
const data = target.dataset;
const binding = data.bind;
if (!binding || !Object.hasOwn(this, binding)) {
Logger.debug(`Skipping change for element with binding "${binding}"`);
return;
};
Logger.log(`updating ${binding} value to ${target.value}`);
this[binding] = target.value;
this.render();
};
static async #createTable() {
if (this._name === ``) {
ui.notifications.error(`Cannot create a table without a name`);
};
};
};

View file

@ -26,8 +26,14 @@ export function validateValue(value, options) {
return !error; return !error;
}; };
export const BucketTypes = {
STRING: `string`,
NUMBER: `number`,
RANGE: `range`,
};
const validatorTypes = { const validatorTypes = {
string: { [BucketTypes.STRING]: {
field: StringField, field: StringField,
transformOptions: (opts) => { transformOptions: (opts) => {
delete opts.type; delete opts.type;
@ -40,20 +46,28 @@ const validatorTypes = {
}; };
}, },
}, },
number: { [BucketTypes.NUMBER]: {
field: NumberField, field: NumberField,
transformOptions: (opts) => { transformOptions: (opts) => {
delete opts.type; delete opts.type;
opts.nullable = false; opts.nullable = false;
opts.integer = true; opts.integer = true;
if (typeof opts.choices === `function`) {
Logger.error(`Choices cannot be a function in a table's buckets configuraion`);
delete opts.choices;
};
}, },
}, },
range: { [BucketTypes.RANGE]: {
field: NumberField, field: NumberField,
transformOptions: (opts) => { transformOptions: (opts) => {
delete opts.type; delete opts.type;
opts.nullable = false; opts.nullable = false;
opts.integer = true; opts.integer = true;
if (typeof opts.choices === `function`) {
Logger.error(`Choices cannot be a function in a table's buckets configuraion`);
delete opts.choices;
};
}, },
}, },
}; };

View file

@ -1,5 +1,8 @@
.stats-sidebar { .stats-sidebar {
&.active {
display: flex; display: flex;
}
flex-direction: column; flex-direction: column;
gap: 2rem; gap: 2rem;
padding: 1rem; padding: 1rem;

View file

@ -0,0 +1,23 @@
.TableCreator {
.window-content {
display: flex;
flex-direction: column;
gap: 1rem;
}
.group {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 3fr);
gap: 0.5rem;
}
label {
color: var(--color-form-label);
font-weight: bold;
line-height: var(--input-height);
}
button {
height: calc(1.25 * var(--input-height));
}
}

View file

@ -4,3 +4,4 @@
@import url("./Apps/StatsViewer.css") layer(apps); @import url("./Apps/StatsViewer.css") layer(apps);
@import url("./Apps/StatsSidebar.css") layer(apps); @import url("./Apps/StatsSidebar.css") layer(apps);
@import url("./Apps/TableCreator.css") layer(apps);

View file

@ -26,6 +26,12 @@
> >
View Statistics View Statistics
</button> </button>
<button
type="button"
data-action="createTable"
>
Create New Table
</button>
<button type="button">Manage Tables</button> <button type="button">Manage Tables</button>
<button type="button">Manage Rows</button> <button type="button">Manage Rows</button>
</section> </section>

View file

@ -0,0 +1,30 @@
<div class="group">
<label for="{{meta.idp}}-name">
Name
</label>
<input
id="{{meta.idp}}-name"
type="text"
value="{{ name }}"
data-bind="_name"
>
</div>
<div class="group">
<label for="{{meta.idp}}-type">
Type
</label>
<select
id="{{meta.idp}}-type"
data-bind="_type"
>
{{ st-options type types }}
</select>
</div>
<button
type="button"
data-action="createTable"
>
Create Table
</button>