Finish Core Features of Initiative Tracker

This commit is contained in:
MartinSDampier 2025-03-11 14:49:01 -05:00
parent aa79b5cd40
commit d8e483b83d
4 changed files with 131 additions and 136 deletions

View file

@ -1,26 +0,0 @@
import { App, Modal, Setting } from 'obsidian';
export class ExampleModal extends Modal {
constructor(app: App, onSubmit: (result: string) => void) {
super(app);
this.setTitle('What\'s your name?');
let name = '';
new Setting(this.contentEl)
.setName('Name')
.addText((text) =>
text.onChange((value) => {
name = value;
}));
new Setting(this.contentEl)
.addButton((btn) =>
btn
.setButtonText('Submit')
.setCta()
.onClick(() => {
this.close();
onSubmit(name);
}));
}
}

View file

@ -5,7 +5,9 @@ import { ButtonComponent, ItemView, TextAreaComponent, WorkspaceLeaf, Setting, T
export const VIEW_TYPE_EXAMPLE = 'example-view';
export const Yes = 'Yes';
export const No = 'No';
export const ActedClass = 'Acted';
export const Red = 'red';
export const Green = 'green';
export const Fill = 'fill';
export class ExampleView extends ItemView {
gridEl: HTMLDivElement;
@ -15,6 +17,8 @@ export class ExampleView extends ItemView {
staminaInput: TextComponent;
creatures: Creature[] = [];
buttons: ButtonComponent[] = [];
round: number;
constructor(leaf: WorkspaceLeaf) {
super(leaf);
}
@ -24,9 +28,13 @@ export class ExampleView extends ItemView {
}
getDisplayText() {
return 'Example view';
return 'Initiative Tracker';
}
// getIcon() {
// return Icons.DICE;
// }
async onOpen() {
this.contentEl.empty();
@ -41,18 +49,39 @@ export class ExampleView extends ItemView {
}
createInputSection() {
this.formEl = this.gridEl.createDiv();
this.formEl = this.gridEl.createDiv({cls: "fullScreen"});
this.nameInput = new TextComponent(this.formEl).setPlaceholder("Name");
this.nameInput.inputEl.addClass("padded-input");
this.staminaInput = new TextComponent(this.formEl).setPlaceholder("Max Stamina");
this.staminaInput.inputEl.addClass("padded-input");
var createButtonComp = new ButtonComponent(this.formEl);
createButtonComp.setButtonText("Create");
createButtonComp.onClick( () => this.createCreatureRow());
this.setRound(0);
}
setRound(round: number){
if (this.formEl.children.length >= 4)
{
this.formEl.children[3].remove();
}
this.round = round;
var div = this.formEl.createDiv({text: "Round: " + this.round, cls: "rightAlign"});
var resetRoundsButton = new ButtonComponent(div);
resetRoundsButton.setButtonText("Reset");
resetRoundsButton.setClass("headerButtonLeft");
resetRoundsButton.onClick( () => {
this.round == 0;
this.setRound(0);
});
}
createCreatureRow(){
if (this.nameInput.getValue() == "")
{
return;
}
var creature = new Creature();
creature.Id = (this.creatures.length + 1).toString();
creature.Name = this.nameInput.getValue();
@ -68,14 +97,23 @@ export class ExampleView extends ItemView {
var header = this.tableEl.createEl('tr');
header.createEl('th', {text: 'Character', cls: 'name-Cell'});
header.createEl('th', {text: 'Stamina', cls: 'stamina-Cell'});
header.createEl('th', {text: 'Acted'});
header.createEl('th', {text: 'FTA', title: "Free Triggered Action"});
header.createEl('th', {text: 'Acted'});
var createButtonHeader = header.createEl('th');
var resetButtonComp = new ButtonComponent(createButtonHeader)
resetButtonComp.setButtonText("Reset");
resetButtonComp.setButtonText("New Round");
resetButtonComp.setClass("headerButtonLeft");
resetButtonComp.onClick( () => {
this.ResetAllCreatures();
this.newRound();
});
var resetButtonComp = new ButtonComponent(createButtonHeader)
resetButtonComp.setButtonText("Clear");
resetButtonComp.setClass("headerButtonRight");
resetButtonComp.onClick( () => {
this.removeAllRows();
});
//buttonHeader.createEl('button', { text: "Create"});
}
@ -87,15 +125,24 @@ export class ExampleView extends ItemView {
row.id = creature.Id;
row.createEl('td', {text: "stamina", cls: "Centered stamina-Cell"})
this.updateStamina(row, creature.Stamina.toString())
var buttonCell = row.createEl('td');
var buttonCell = row.createEl('td', {cls: Green});
var buttonComp = new ButtonComponent(buttonCell);
buttonComp.setButtonText(No);
buttonComp.setClass(Fill);
buttonComp.onClick( () => {
this.changeActedCell(row, buttonComp, buttonComp.buttonEl.getText() == No);
this.changeTriggeredActionCell(row, buttonComp, buttonComp.buttonEl.getText() == No);
});
var actedButtonCell = row.createEl('td', {cls: Green});
var actedButtonComp = new ButtonComponent(actedButtonCell);
actedButtonComp.setButtonText(No);
actedButtonComp.setClass(Fill);
actedButtonComp.onClick( () => {
this.changeActedCell(row, actedButtonComp, actedButtonComp.buttonEl.getText() == No);
});
buttonCell = row.createEl('td');
var removeButton = new ButtonComponent(buttonCell);
removeButton.buttonEl.addClass("padded");
removeButton.onClick(() => {this.removeRow(row)});
buttonCell.createEl('br');
removeButton.setButtonText("Remove");
var deadButton = new ButtonComponent(buttonCell);
@ -103,6 +150,10 @@ export class ExampleView extends ItemView {
deadButton.setButtonText("Dead");
}
removeRow(row: HTMLTableRowElement) {
row.remove();
}
updateStamina(row: HTMLTableRowElement, stamina: string){
try{
var staminaCell = row.children[1] as HTMLTableCellElement;
@ -119,35 +170,62 @@ export class ExampleView extends ItemView {
var result = (e as Error).message;
console.log("ERROR:");
console.log(result);
}
}
}
changeActedCell(row : HTMLTableRowElement, buttonComp : ButtonComponent, hasActed : boolean) {
if (hasActed)
{
buttonComp.setButtonText(Yes);
row.addClass(ActedClass);
row.children[3].addClass(Red);
row.children[3].removeClass(Green);
}
else
{
buttonComp.setButtonText(No);
row.removeClass(ActedClass);
row.children[3].addClass(Green);
row.children[3].removeClass(Red);
}
}
ResetAllCreatures()
changeTriggeredActionCell(row : HTMLTableRowElement, buttonComp : ButtonComponent, hasActed : boolean) {
if (hasActed)
{
buttonComp.setButtonText(Yes);
row.children[2].addClass(Red);
row.children[2].removeClass(Green);
}
else
{
buttonComp.setButtonText(No);
row.children[2].addClass(Green);
row.children[2].removeClass(Red);
}
}
newRound()
{
var count = this.tableEl.children.length;
this.setRound(++this.round);
for (var i = 1; i < count; i++)
{
var row = (this.tableEl.children[i] as (HTMLTableRowElement));
var button = (row.children[2] as HTMLTableCellElement).children[0] as HTMLButtonElement;
var triggeredActionButton = (row.children[3] as HTMLTableCellElement).children[0] as HTMLButtonElement;
button.textContent = No;
if (row.classList.contains(ActedClass))
{
row.removeClass(ActedClass);
}
triggeredActionButton.textContent = No;
row.children[3].removeClass(Red);
row.children[2].removeClass(Red);
row.children[3].addClass(Green);
row.children[2].addClass(Green);
}
}
removeAllRows(){
for(var i = 1; i < this.tableEl.children.length; i++)
{
this.tableEl.children[i].remove();
}
}
}

View file

@ -1,6 +1,5 @@
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, WorkspaceLeaf } from 'obsidian';
import { ExampleView, VIEW_TYPE_EXAMPLE } from './Views/InitiativeTrackerView';
import { ExampleModal } from './Modals/AddCreatureModal';
// Remember to rename these classes and interfaces!
interface MyPluginSettings {
@ -17,7 +16,6 @@ export default class ForbiddenLandsCharacterSheet extends Plugin {
async onload() {
await this.loadSettings();
this.registerView(
VIEW_TYPE_EXAMPLE,
(leaf) => new ExampleView(leaf)
@ -27,82 +25,12 @@ export default class ForbiddenLandsCharacterSheet extends Plugin {
this.activateView();
});
// This creates an icon in the left ribbon.
const CharacterRibbon = this.addRibbonIcon('book', 'Sample Plugin', (evt: MouseEvent) => {
// Called when the user clicks the icon.
new Notice('This is a notice!');
});
const GearRibbon = this.addRibbonIcon('backpack', 'Sample Plugin', (evt: MouseEvent) => {
// Called when the user clicks the icon.
new Notice('This is a notice!');
});
// Per
// Perform additional things with the ribbon
CharacterRibbon.addClass('my-plugin-ribbon-class');
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
const statusBarItemEl = this.addStatusBarItem();
statusBarItemEl.setText('Status Bar Text');
// This adds a simple command that can be triggered anywhere
this.addCommand({
id: 'open-sample-modal-simple',
name: 'Open sample modal (simple)',
callback: () => {
new SampleModal(this.app).open();
}
});
// This adds an editor command that can perform some operation on the current editor instance
this.addCommand({
id: 'sample-editor-command',
name: 'Sample editor command',
editorCallback: (editor: Editor, view: MarkdownView) => {
console.log(editor.getSelection());
editor.replaceSelection('Sample Editor Command');
}
});
// This adds a complex command that can check whether the current state of the app allows execution of the command
this.addCommand({
id: 'open-sample-modal-complex',
name: 'Open sample modal (complex)',
checkCallback: (checking: boolean) => {
// Conditions to check
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (markdownView) {
// If checking is true, we're simply "checking" if the command can be run.
// If checking is false, then we want to actually perform the operation.
if (!checking) {
new SampleModal(this.app).open();
}
// This command will only show up in Command Palette when the check function returns true
return true;
}
}
});
// This adds a settings tab so the user can configure various aspects of the plugin
this.addSettingTab(new SampleSettingTab(this.app, this));
// If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin)
// Using this function will automatically remove the event listener when this plugin is disabled.
this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
console.log('click', evt);
});
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
//Adds example modal to command set
this.addCommand( {
id: 'display-modal',
name: 'Display modal ',
callback: () => {
new ExampleModal(this.app, (result) => {
new Notice(`Hello, ${result}!`);
}).open();
}
})
}
onunload() {
@ -141,22 +69,6 @@ export default class ForbiddenLandsCharacterSheet extends Plugin {
}
}
class SampleModal extends Modal {
constructor(app: App) {
super(app);
}
onOpen() {
const {contentEl} = this;
contentEl.setText('Woah!');
}
onClose() {
const {contentEl} = this;
contentEl.empty();
}
}
class SampleSettingTab extends PluginSettingTab {
plugin: ForbiddenLandsCharacterSheet;

View file

@ -1,6 +1,10 @@
.Acted{
.red{
background-color: var(--color-red);
}
.green {
background-color: var(--color-green);
}
.Centered{
margin-right: auto;
@ -8,24 +12,51 @@
text-align: center;
}
.fill{
height: 90%;
width: 90%;
}
.padded-input {
margin: 5px;
width: 40%;
}
.rightAlign {
margin-left: auto;
margin-right: 20px;
text-align: right;
}
.headerButtonLeft {
margin-left: 5px;
margin-right: 1px;
}
.headerButtonRight {
margin-left: 1px;
margin-right: 5px;
}
.padded {
margin: 5px;
margin-left: 5px;
margin-right: 5px;
margin-bottom: 1px;
width: 90%;
}
.stamina-Cell {
width: 20%;
}
.fullScreen {
width: 100%;
}
table{
border: 1px solid black;
border-collapse: collapse;
padding: 5px;
width: 90%;
width: 100%;
}
th, td {