Add initial implementation for AscAssetManager module

This commit introduces the following key features:

- **New `.gitignore`**:
  - Ignores `.vscode`, `.eslintrc.js`, and `dist/module.zip`.

- **Core functionality**:
  - Created `src/main.js` to implement the `AscAssetManager` class with methods to manage file uploads, categories, tags, and settings registration.
  - Introduced hooks for rendering and managing custom upload dialogs and forms.

- **Configuration and settings**:
  - Added `src/settings.js` to register settings for features like enabling/disabling, root directory configuration, and theme selection.

- **Templates**:
  - Added `upload-choose.hbs` for the file selection dialog.
  - Added `upload-form.hbs` for the file metadata customization dialog.

- **Utilities**:
  - Added `src/utils.js` with helper functions for file parsing, metadata handling, and filename creation.

- **Styling**:
  - Added `style.css` for styling upload areas.

- **Module metadata**:
  - Added `module.json` with module details, compatibility, and dependencies.

This commit establishes the foundational structure and functionality for the AscAssetManager module.
This commit is contained in:
2025-01-19 10:52:44 -06:00
commit aba086d7f0
8 changed files with 349 additions and 0 deletions

164
src/main.js Normal file
View File

@@ -0,0 +1,164 @@
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`
}
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("ready", function() {
AscAssetManager.log(false, "Deno nodule reody")
registerSettings(AscAssetManager.ID);
});
Hooks.on("ascAssetManager.renderUploadChoose", () => {
renderTemplate(AscAssetManager.TEMPLATES.UPLOAD_CHOOSE).then((content)=>{
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")
// Handle drag-and-drop events
uploadArea.on("dragover", (event) => {
event.preventDefault();
uploadArea.css("background-color", "#e0f7fa");
});
uploadArea.on("dragleave", () => {
uploadArea.css("background-color", "");
});
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;
fileInput.trigger('change')
}
});
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(', ')}`);
})
form.on('submit', (event) => {
for (let file of fileInput.files) {
Hooks.callAll("ascAssetManager.renderUploadForm", {file})
}
})
}
}).render(true)
})
})
Hooks.on("ascAssetManager.renderUploadForm", (data)=>{
const templateData = {fileCategories: AscAssetManager.fileCategories, fileTags: AscAssetManager.fileTags}
renderTemplate(AscAssetManager.TEMPLATES.UPLOAD_FORM, templateData).then(content => {
let {file} = data
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))
.catch((e)=>ui.notifications.error('Error uploading', e))
})
}
}).render(true);
})
})