Files
asc-asset-manager/src/main.js
Anthony Correa 3fdaadf7e0 Add customizable tags and categories with UI management
- Introduced settings for tags and categories in `settings.js`:
  - Added `tags` and `categories` settings with default values and support for customization.
  - Registered a new settings menu for managing tags and categories dynamically.
- Enhanced `main.js` to use the customizable tags and categories in templates.
  - Adjusted `AscAssetManager.TEMPLATES` to include a new `SETTINGS_TAGS_AND_CATEGORIES` template.
  - Updated the `renderUploadForm` hook to fetch tags and categories dynamically from settings.
- Added a new Handlebars template `settings-tags-and-categories.hbs`:
  - Provides a user interface for managing tags and categories.
  - Includes functionality to add, reset, and delete rows dynamically.

These updates allow users to define and manage tags and categories via the module's settings menu, improving flexibility and user experience.
2025-01-23 10:24:54 -06:00

226 lines
7.4 KiB
JavaScript

const LOG_PREFIX = "asc-asset-manager | "
import { registerSettings } from "./settings.js";
import { parseFileName, createMetaFileName } from "./utils.js";
export class AscAssetManager {
static ID = 'asc-asset-manager'
static TEMPLATES = {
UPLOAD_CHOOSE:`modules/${this.ID}/templates/upload-choose.hbs`,
UPLOAD_FORM:`modules/${this.ID}/templates/upload-form.hbs`,
SETTINGS_MENU_MACRO:`modules/${this.ID}/templates/settings-menu-macro.hbs`,
SETTINGS_TAGS_AND_CATEGORIES:`modules/${this.ID}/templates/settings-tags-and-categories.hbs`
}
static getDirectory () {
return game.settings.get(this.ID, "rootDirectory")
}
static fileCategories = [
{id: "npcn", label: "NPC (Named)"},
{id: "npcu", label: "NPC (Unnamed)"},
{id: "scene", label: "Scene Background"},
{id: "pc", label: "PC"},
{id: "inset", label: "Inset"},
{id: "vehicle", label: "Vehicle"},
{id: "weapon", label: "Weapon"}
]
static fileTags = [
{id:"tk", label: "Token"},
{id:"sq", label: "Square"}
]
static log(force, ...args) {
console.log(this.ID, '|', ...args);
}
}
Hooks.on('init', ()=>{
}
)
Hooks.on("ready", function() {
registerSettings(AscAssetManager);
const DEBUG = game.settings.get(AscAssetManager.ID, "enableDebugMode")
if (DEBUG) {
AscAssetManager.log(false, "Debug Mode Enabled")
CONFIG.debug.hooks = true
}
AscAssetManager.log(false, "Deno nodule reody")
});
Hooks.on("ascAssetManager.renderUploadChoose", (data={}) => {
let {files} = data
renderTemplate(AscAssetManager.TEMPLATES.UPLOAD_CHOOSE, {moduleId: AscAssetManager.ID}).then((content)=>{
const dialog = new Dialog({
title: "Choose",
content: content,
buttons: [],
render: (html)=> {
AscAssetManager.log(false, 'Rendering choose window')
const uploadArea = html.find("#upload-area");
const fileInput = html.find("#file-input");
const form = html.find("form")
if (files) {
fileInput.val(files)
}
// Handle drag-and-drop events
uploadArea.on("dragover", (event) => {
event.preventDefault();
uploadArea.addClass('dragover')
});
uploadArea.on("dragleave", () => {
uploadArea.removeClass('dragover')
});
uploadArea.on("drop", (event) => {
event.preventDefault();
// uploadArea.css("background-color", "");
const files = event.originalEvent.dataTransfer.files;
if (files.length > 0) {
AscAssetManager.log(false, 'file dropped change')
// Update the file input with the dropped file
const dataTransfer = new DataTransfer();
for (let file of files) {
dataTransfer.items.add(file); // Add the first dropped file
}
fileInput.files = dataTransfer.files;
for (let file of fileInput.files) {
Hooks.callAll("ascAssetManager.renderUploadForm", {file})
}
}
});
fileInput.on("change", (event)=> {
const files = fileInput.files
// Show feedback to the user
uploadArea.find("p").text(`Selected file: ${Array.from(files).map(f=>f.name).join(', ')}`);
})
}
}).render(true)
})
})
Hooks.on("renderHotbar", ({macros}, html) => {
// Find macro on the hotbar
for (let macro_item of macros) {
const {macro} = macro_item
if (macro?.command?.includes("ascAssetManager.renderUploadChoose")) {
AscAssetManager.log(false, `Found macro with command: ${macro.name}`);
// Find the corresponding button in the hotbar
const macroButton = html.find(`.macro[data-macro-id='${macro.id}']`);
if (macroButton.length > 0) {
AscAssetManager.log(false, `Adding custom drop event to macro: ${macro.name}`, {macro, macroButton});
macroButton.addClass(AscAssetManager.ID)
macroButton.on('dragover', (event)=>{
AscAssetManager.log(false, {event});
$(event.currentTarget).addClass('dragover');
AscAssetManager.log(false, {classlist: event.currentTarget.classList});
})
macroButton.on('dragleave', (event)=>$(event.currentTarget).removeClass('dragover'))
// Add a custom drop event to the macro
macroButton.on("drop", (event) => {
event.preventDefault();
$(event.currentTarget).removeClass('dragover')
const files = event.originalEvent.dataTransfer.files;
AscAssetManager.log(false, "Dropped file on macro.", {files});
for (let file of files) {
Hooks.callAll("ascAssetManager.renderUploadForm", {file})
}
// Perform your custom logic with the dropped data
// handleCustomDrop(droppedData, macro);
});
}
}
}
});
Hooks.on("ascAssetManager.renderUploadForm", (data={})=>{
const templateData = {
moduleId: AscAssetManager.ID,
fileCategories: game.settings.get(AscAssetManager.ID, "categories"),
fileTags: game.settings.get(AscAssetManager.ID, "tags")
}
renderTemplate(AscAssetManager.TEMPLATES.UPLOAD_FORM, templateData).then(content => {
let {file} = data
const dialog = new Dialog({
title: "Simple Popup",
content: content,
buttons: [],
render: (html) =>{
AscAssetManager.log('file', file)
const uploadArea = html.find("#upload-area");
const fileInput = html.find("#file-input");
const form = html.find('#upload-form');
const namePreview = html.find('#name-preview')
const baseName = form.find("input[id='baseName']")
fileInput.val(file.name)
const reader = new FileReader();
reader.onload = (e) => {
const img = form.find("img");
const blob = new Blob([e.target.result], { type: file.type });
const blobUrl = URL.createObjectURL(blob)
AscAssetManager.log(false, blobUrl,img)
img.attr("src", blobUrl)
};
reader.readAsArrayBuffer(file)
baseName.val(parseFileName(file.name).fileName)
// Handle click to open file selector
// uploadArea.on("click", () => fileInput.click());
form.on('change', (event)=>{
const fileExtension = parseFileName(fileInput.val()).fileExtension
const fileCategory = form.find("select[name='file-category'] option:checked").val()
const fileTags = form.find("input[name='file-tags']:checked").map(function(){
return $(this).val()
}).get()
AscAssetManager.log(false, 'form changed', {fileCategory, fileTags, baseName})
const new_filename = createMetaFileName(`${baseName.val()}.${fileExtension}`,{
category: fileCategory,
tags: fileTags
})
namePreview.val(new_filename)
})
form.on('submit', (event) => {
AscAssetManager.log(false, "Form Submitted");
event.preventDefault();
const renamedFile = new File([file], namePreview.val(), {type:file.type})
FilePicker.upload(
"data",
AscAssetManager.getDirectory(),
renamedFile,
{
notify: true
}
)
.then((res)=>{
AscAssetManager.log(false, 'Uploaded!', res);
dialog.close()
})
.catch((e)=>ui.notifications.error('Error uploading', e))
})
}
}).render(true);
})
})