Initial commit

This commit is contained in:
Ray 2025-11-08 16:16:26 +00:00
commit 907199a05e
9 changed files with 4662 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/dist
/node_modules

377
demo.html Normal file
View File

@ -0,0 +1,377 @@
<!doctype html>
<html lang="en-GB">
<head>
<meta charset="UTF-8" />
<meta http-equiv="content-type" content="text/html" charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="" />
<meta name="keyword" content="" />
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<link href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css" rel="stylesheet" type="text/css" />
<script src="dist/tw4dialog.dist.js"></script>
<title></title>
<style>
p {
margin: 0 0 1em 0;
}
.text-sm {
font-size: 0.8em;
}
</style>
</head>
<body class="py-5">
<div class="container mx-auto mt-4">
<div class="grid grid-cols-3 gap-2 divide-x divide-solid divide-base-300 mb-5">
<div class="col-span-1 pr-2">
<p><b>Example. Simple Text Modal</b></p>
<p>Launch a modal with a text body.</p>
<p><button id="buttonL1" type="button" class="btn btn-primary">Launch Modal</button></p>
<script>
document.querySelector("#buttonL1").addEventListener("click", async function(e){
e.stopPropagation();
e.preventDefault();
TW4Dialog.Show({
Title: "Modal Example",
Content: "Hello Momo!",
Size: "md"
});
});
</script>
</div>
<div class="col-span-2">
<div class="mockup-code w-full px-5 text-sm">
<pre><code>
TW4Dialog.Show({
Title: "Modal Example",
Content: "Hello Momo!",
Size: "md"
});
</code></pre>
</div>
</div>
</div>
<div class="grid grid-cols-3 gap-2 divide-x divide-solid divide-base-300 mb-5">
<div class="col-span-1 pr-2">
<p><b>Example. Basic Modal</b></p>
<p>Launch a modal and fetch a html body from a URL.</p>
<p><button id="buttonL11" type="button" class="btn btn-primary">Launch Modal</button></p>
<script>
document.querySelector("#buttonL11").addEventListener("click", async function(e){
e.stopPropagation();
e.preventDefault();
TW4Dialog.Show({
Title: "Modal Fetch Example",
Content: "Loading...",
Url: "https://cdn.hiimray.co.uk/sample/hellomomo.txt",
Size: "lg"
});
});
</script>
</div>
<div class="col-span-2">
<div class="mockup-code w-full px-5 text-sm">
<pre><code>
TW4Dialog.Show({
Title: "Modal Fetch Example",
Content: "Loading...",
Url: "https://cdn.hiimray.co.uk/sample/hellomomo.txt",
Size: "lg"
});
</code></pre>
</div>
</div>
</div>
<div class="grid grid-cols-3 gap-2 divide-x divide-solid divide-base-300 mb-5">
<div class="col-span-1 pr-2">
<p><b>Example. Update Modal</b></p>
<p>Make changes (title, body, size or footer) to a modal.</p>
<p><button id="buttonR1" type="button" class="btn btn-primary">Launch Modal</button></p>
<script>
document.querySelector("#buttonR1").addEventListener("click", function(e){
e.stopPropagation();
e.preventDefault();
TW4Dialog.Show({
Id: "modalR1",
Title: "Modal Title",
Content: "Hello Momo!",
Url: null,
Size: "md"
});
TW4Dialog.Update({
Id: "modalR1",
Title: "Modal Changed Title",
Content: "Hello momo again!",
Url: null,
Size: "sm",
ShowFooter: false
});
});
</script>
</div>
<div class="col-span-2">
<div class="mockup-code w-full px-5 text-sm">
<pre><code>
TW4Dialog.Show({
Id: "modalR1",
Title: "Modal Title",
Content: "Hello Momo!",
Url: null,
Size: "md"
});
TW4Dialog.Update({
Id: "modalR1",
Title: "Modal Changed Title",
Content: "Hello momo again!",
Url: null,
Size: "sm",
ShowFooter: false
});
</code></pre>
</div>
</div>
</div>
<div class="grid grid-cols-3 gap-2 divide-x divide-solid divide-base-300 mb-5 border-b border-solid border-base-400 pb-5">
<div class="col-span-1 pr-2">
<p><b>Example. Multiple Modals (Stack)</b></p>
<p>Launch multiple modals, stacked on top of each other.</p>
<p><button id="buttonL3" type="button" class="btn btn-primary">Launch Modal</button></p>
<script>
document.querySelector("#buttonL3").addEventListener("click", function(e){
e.stopPropagation();
e.preventDefault();
TW4Dialog.Show({
Id: "modalL3a",
Title: "Modal A Title",
Content: "First!",
Url: null,
Size: "md"
});
TW4Dialog.Show({
Id: "modalL3b",
Title: "Modal B Title",
Content: "Second!",
Url: null,
Size: "md"
});
});
</script>
</div>
<div class="col-span-2">
<div class="mockup-code w-full px-5 text-sm">
<pre><code>
TW4Dialog.Show({
Id: "modalL3a",
Title: "Modal A Title",
Content: "First!",
Url: null,
Size: "md"
});
TW4Dialog.Show({
Id: "modalL3b",
Title: "Modal B Title",
Content: "Second!",
Url: null,
Size: "md"
});
</code></pre>
</div>
</div>
</div>
<div class="grid grid-cols-3 gap-2 divide-x divide-solid divide-base-300 mb-5 border-b border-solid border-base-400 pb-5">
<div class="col-span-1 pr-2">
<p><b>Example. MessageBox Example (Button Prompts)</b></p>
<p>Launch a modal with a prompt of several buttons. Modal waits for a response from one of the buttons or on closing.</p>
<p><button id="buttonL2" type="button" class="btn btn-primary">Launch Modal</button></p>
<script>
document.querySelector("#buttonL2").addEventListener("click", async function(e){
e.stopPropagation();
e.preventDefault();
const result = await TW4MessageBox.Show({
Title: "MessageBox Example 1",
Content: "Click a button!",
Buttons: [
{ Label: "Yes", Value: "Yes" },
{ Label: "No", Value: "No" },
{ Label: "Cancel", Value: "Cancel" }
]
});
TW4Dialog.Show({
Title: "MessageBox Result",
Content: result
});
});
</script>
</div>
<div class="col-span-2">
<div class="mockup-code w-full px-5 text-sm">
<pre><code>
const result = await TW4MessageBox.Show({
Title: "MessageBox Example 1",
Content: "Click a button!",
Buttons: [
{ Label: "Yes", Value: "Yes" },
{ Label: "No", Value: "No" },
{ Label: "Cancel", Value: "Cancel" }
]
});
TW4Dialog.Show({
Title: "MessageBox Result",
Content: result
});
</code></pre>
</div>
</div>
</div>
<div class="grid grid-cols-3 gap-2 divide-x divide-solid divide-base-300 mb-5 border-b border-solid border-base-400 pb-5">
<div class="col-span-1 pr-2">
<p><b>Example. PromptBox (Text Prompt)</b></p>
<p>Launch a modal with a text prompt. Modal waits for a response from the textbox.</p>
<p><button id="buttonL2a" type="button" class="btn btn-primary">Launch Modal</button></p>
<script>
document.querySelector("#buttonL2a").addEventListener("click", async function(e){
e.stopPropagation();
e.preventDefault();
const result = await TW4PromptBox.Show({
Title: "PromptBox Example 1",
Content: "Enter text!",
Placeholder: "Text me",
Value: ""
});
TW4Dialog.Show({
Title: "PromptBox Result",
Content: JSON.stringify(result)
});
});
</script>
</div>
<div class="col-span-2">
<div class="mockup-code w-full px-5 text-sm">
<pre><code>
const result = await TW4PromptBox.Show({
Title: "PromptBox Example 1",
Content: "Enter text!",
Placeholder: "Text me",
Value: ""
});
TW4Dialog.Show({
Title: "PromptBox Result",
Content: JSON.stringify(result)
});
</code></pre>
</div>
</div>
</div>
<div class="grid grid-cols-3 gap-2 divide-x divide-solid divide-base-300 mb-5">
<div class="col-span-1 pr-2">
<p><b>Example. Close Modal</b></p>
<p>Close a modal using its identifier</p>
<p><button id="buttonR2" type="button" class="btn btn-primary">Launch Modal</button></p>
<script>
document.querySelector("#buttonR2").addEventListener("click", function(e){
e.stopPropagation();
e.preventDefault();
TW4Dialog.Close("modalR1");
});
</script>
</div>
<div class="col-span-2">
<div class="mockup-code w-full px-5 text-sm">
<pre><code>
TW4Dialog.Close("modalR1");
</code></pre>
</div>
</div>
</div>
<div class="grid grid-cols-3 gap-2 divide-x divide-solid divide-base-300 mb-5">
<div class="col-span-1 pr-2">
<p><b>Example. Clear Modal</b></p>
<p>Close all modals</p>
<p><button id="buttonR3" type="button" class="btn btn-primary">Launch Modal</button></p>
<script>
document.querySelector("#buttonR3").addEventListener("click", function(e){
e.stopPropagation();
e.preventDefault();
TW4Dialog.Clear();
});
</script>
</div>
<div class="col-span-2">
<div class="mockup-code w-full px-5 text-sm">
<pre><code>
TW4Dialog.Clear();
</code></pre>
</div>
</div>
</div>
</div>
</body>
</html>

3498
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

16
package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "twdialog4",
"version": "0.1.0.201",
"devDependencies": {
"css-loader": "^7.1.2",
"sass": "^1.77.8",
"sass-loader": "^16.0.0",
"style-loader": "^4.0.0",
"webpack": "^5.93.0",
"webpack-cli": "^5.1.4"
},
"scripts": {
"build": "webpack",
"watch": "webpack --watch"
}
}

8
src/index.js Normal file
View File

@ -0,0 +1,8 @@
import TWDialog4 from "./twdialog4.js"
import TWMessageBox4 from "./twmessagebox4.js"
import TWPromptBox4 from "./twpromptbox4.js"
window.TW4Dialog = new TWDialog4();
window.TW4MessageBox = new TWMessageBox4();
window.TW4PromptBox = new TWPromptBox4();

481
src/twdialog4.js Normal file
View File

@ -0,0 +1,481 @@
class TWDialog4 {
#elName = {};
#modalSizes = [
"md",
"xl",
"4xl",
"screen",
];
constructor() {
const a = this;
const prefix = "twdia4";
a.#elName = {
prefix: prefix,
container: prefix + "-container",
backdrop: prefix + "-backdrop",
modal: prefix + "-modal",
header: prefix + "-header",
title: prefix + "-h1",
body: prefix + "-body",
footer: prefix + "-footer"
};
}
get DefaultOptions() {
return {
Id: null,
Title: "",
Content: "",
Url: null,
Size: "md",
ShowFooter: true,
// EasyClose: true,
EnableMovement: true,
EnableResize: true,
CloseButton: {
colour: "bg-white hover:bg-gray-50 text-gray-700"
}
};
}
get #documentBody() {
return document.getElementsByTagName("body")[0];
}
async Show(options) {
const a = this;
const newOptions = a.#loadOptions(options);
a.#addBackdrop();
const modal = a.Find(newOptions.Id);
if (modal == null) {
await a.#addModal(newOptions);
} else {
await a.Update(newOptions);
}
}
Clear() {
const a = this;
const container = a.#findNode("." + a.#elName.container);
if (container != null) {
a.#removeNode(container);
}
}
Close(id) {
const a = this;
const modalId = a.#elName.prefix + "-" + id;
const container = a.#findNode("." + a.#elName.container);
if (container == null) {
return;
}
const modal = a.#findNode("#" + modalId);
if (modal != null) {
a.#removeNode(modal);
}
const foundModals = a.#documentBody.querySelectorAll("." + a.#elName.modal);
if (foundModals.length <= 0) {
a.#removeNode(container);
a.#documentBody.classList.remove('overflow-hidden');
}
}
Exists(id) {
return (this.Find(id) !== null);
}
Find(id) {
const a = this;
const modalId = a.#elName.prefix + "-" + id;
const modal = a.#findNode("#" + modalId);
if (modal == null) {
return null;
}
return {
Header: modal.querySelector("." + a.#elName.header),
Title: modal.querySelector("." + a.#elName.title),
Body: modal.querySelector("." + a.#elName.body),
Footer: modal.querySelector("." + a.#elName.footer),
Modal: modal
};
}
SetSize(id, size) {
const a = this;
const modal = a.Find(id);
if (modal == null) {
return;
}
const childNode = modal.Modal.firstElementChild;
for (let i=0; i<a.#modalSizes.length; i++) {
if (childNode.classList.contains("w-" + a.#modalSizes[i])) {
childNode.classList.remove("w-" + a.#modalSizes[i]);
}
}
childNode.classList.add("w-" + size);
}
async Update(options) {
const a = this;
const newOptions = a.#loadOptions(options);
const modal = a.Find(newOptions.Id);
if (modal == null) {
return;
}
if (!this.#isNullOrWhitespace(newOptions.Title)) {
modal.Title.innerHTML = newOptions.Title;
}
if (!this.#isNullOrWhitespace(newOptions.Content)) {
modal.Body.innerHTML = newOptions.Content;
}
if (newOptions.ShowFooter) {
modal.Footer.classList.remove('hidden');
} else {
modal.Footer.classList.add('hidden');
}
if (!this.#isNullOrWhitespace(newOptions.Size)) {
a.SetSize(newOptions.Id, newOptions.Size);
}
// Load content from url
if (!this.#isNullOrWhitespace(newOptions.Url)) {
const html = await a.#fetchHtml(newOptions.Url);
modal.Body.innerHTML = html ?? "";
}
}
#addBackdrop() {
const a = this;
// Don't allow multiple backdrops
if (a.#findNode("." + a.#elName.container) != null) {
return;
}
const html = `
<div class="` + a.#elName.container + ` fixed inset-0 z-50 hidden">
<!-- Backdrop -->
<div class="` + a.#elName.backdrop + ` absolute inset-0 bg-black/60"></div>
</div>
`;
a.#appendHtml(a.#documentBody, html);
// const backdrop = a.#findNode("." + a.#elName.backdrop);
// if (backdrop == null) {
// return;
// }
// // Attach close event
// backdrop.addEventListener("click", function(e){
// e.stopPropagation();
// e.preventDefault();
// a.Clear();
// });
}
async #addModal(options) {
const a = this;
const modalId = a.#elName.prefix + "-" + options.Id;
const container = a.#findNode("." + a.#elName.container);
if (container == null) {
return;
}
// Don't allow duplicates
let modal = a.Find(options.Id);
if (modal != null) {
return;
}
let html = `
<div id="` + modalId + `" class="` + a.#elName.modal + ` absolute z-10 flex items-start justify-center h-full w-screen p-8">
<div class="bg-white max-h-[90vh] max-w-[90vw] w-` + options.Size + ` rounded-lg shadow-xl overflow-y-auto">
` + a.#generateModalHeader(options) + `
<div class="` + a.#elName.body + ` p-4">
` + options.Content + `
</div>
` + a.#generateModalFooter(options) + `
</div>
</div>
`;
a.#appendHtml(container, html);
// Fix viewport
container.classList.remove('hidden');
a.#documentBody.classList.add('overflow-hidden');
modal = a.Find(options.Id);
if (modal === null) {
return;
}
// Handle close
modal.Modal.querySelectorAll("[aria-label='Close']").forEach(e => {
e.addEventListener("click", function(e2){
e2.stopPropagation();
e2.preventDefault();
a.Close(options.Id);
});
});
// Handle other options
if (options.EnableResize) {
a.#attachResize(modal.Modal, modal.Title);
}
if (options.EnableMovement) {
a.#attachMovable(modal.Modal, modal.Title);
}
// Load content from url
if (!this.#isNullOrWhitespace(options.Url)) {
const html = await a.#fetchHtml(options.Url);
modal.Body.innerHTML = html ?? "";
}
}
#generateModalHeader(options) {
const a = this;
const html = `
<div class="` + a.#elName.header + ` border-b border-solid border-base-300 py-3 px-4 flex items-center justify-between">
<span class="` + a.#elName.title + ` text-xl font-semibold select-none">` + options.Title + `</span>
<a class="text-gray-600 hover:text-gray-900 ml-4 cursor-pointer" aria-label="Close">
<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</a>
</div>
`;
return html;
}
#generateModalFooter(options) {
const a = this;
const html = `
<div class="` + a.#elName.footer + ` ` + (options.ShowFooter ? "" : "hidden") + ` border-t border-solid border-base-300 py-3 px-4 items-center text-right bg-base-100">
<button class="px-4 py-2 text-sm font-medium ` + options.CloseButton.colour + ` border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 cursor-pointer" aria-label="Close">
Close
</button>
</div>
`;
return html;
}
#attachResize(panel, el) {
const a = this;
el.addEventListener("dblclick", function(e) {
const childNode = panel.firstElementChild;
let n = 0;
for (let i=0; i<a.#modalSizes.length; i++) {
n = i;
if (childNode.classList.contains("w-" + a.#modalSizes[i])) {
childNode.classList.remove("w-" + a.#modalSizes[i]);
break;
}
}
n++;
if (n >= a.#modalSizes.length) {
n = 0;
}
childNode.classList.add("w-" + a.#modalSizes[n]);
});
}
#attachMovable(panel, el) {
const a = this;
let isMovable = false;
let currentPos = { X: 0, Y: 0 };
let movePos = { X: 0, Y: 0 };
el.addEventListener("mousedown", function(e) {
if ((e.buttons == 1) && !isMovable) {
movePos = { X: e.clientX, Y: e.clientY };
currentPos = {
X: a.#parsePixels(panel.style.left),
Y: a.#parsePixels(panel.style.top)
};
isMovable = true;
el.style.cursor = "move";
e.preventDefault();
}
});
document.addEventListener("mouseup", function(e) {
if (isMovable) {
isMovable = false;
el.style.cursor = "default";
}
});
document.addEventListener("mousemove", function(e) {
if ((e.buttons == 1) && isMovable) {
let x = (e.clientX - movePos.X) + currentPos.X;
let y = (e.clientY - movePos.Y) + currentPos.Y;
if (x <= 0) x = 0;
if (y <= 0) y = 0;
panel.style.left = x + 'px';
panel.style.top = y + 'px';
}
});
}
#loadOptions(options) {
const a = this;
const newOptions = Object.assign(a.DefaultOptions, options);
if (a.#isNullOrWhitespace(newOptions.Id)) {
newOptions.Id = a.#randomN(30001, 99999);
}
switch (newOptions.Size) {
case "xs":
case "sm":
newOptions.Size = "md";
break;
case "md":
newOptions.Size = "xl";
break;
case "lg":
newOptions.Size = "4xl";
break;
case "xl":
case "xxl":
case "xxxl":
newOptions.Size = "screen";
break;
default:
newOptions.Size = "xl";
break;
}
return newOptions;
}
#appendHtml(el, html) {
let node = document.createElement('template');
node.innerHTML = html.trim();
node = node.content.firstChild;
el.appendChild(node);
}
async #fetchHtml(url) {
let html = "";
try {
const response = await fetch(url);
html = await response.text();
} catch (err) {
html = err;
}
return html;
}
#findNode(selector) {
return this.#documentBody.querySelector(selector);
}
#isNullOrWhitespace(e) {
if (typeof (e) == "undefined") {
return true;
}
if (e == null) {
return true;
}
if (e == false) {
return true;
}
return (e?.toString().trim()?.length <= 0);
}
#parsePixels(value) {
const a = this;
if (a.#isNullOrWhitespace(value)) {
return 0;
}
const result = parseInt(value.replace("px", ""), 10);
return Number.isNaN(result) ? 0 : result;
}
#randomN(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
#removeNode(node) {
if (node == null) {
return;
}
if (typeof(node.parentNode) == "undefined") {
return;
}
node.parentNode.removeChild(node);
}
}
// window.TW4Dialog = new TWDialog4();
export default TWDialog4;

98
src/twmessagebox4.js Normal file
View File

@ -0,0 +1,98 @@
import TWDialog4 from "./twdialog4.js"
class TWMessageBox4 {
#defaultButtonColour = "bg-white hover:bg-gray-50 text-gray-700";
constructor() {
}
get DefaultOptions() {
const a = this;
return {
Id: a.#randomN(30001, 99999),
Title: "",
Content: "",
// Url: null,
Size: "md",
// ShowFooter: true,
// EasyClose: true,
EnableMovement: true,
EnableResize: true,
Buttons: [
{ Label: "Yes", Value: "Yes", Colour: a.#defaultButtonColour },
{ Label: "No", Value: "No", Colour: a.#defaultButtonColour },
{ Label: "Cancel", Value: "Cancel", Colour: a.#defaultButtonColour }
]
};
}
async Show(options) {
const a = this;
const newOptions = Object.assign(a.DefaultOptions, options);
let html = "";
newOptions.Buttons.forEach(el => {
html += a.#generateButton(el.Label, el.Value, el.Colour);
});
await TW4Dialog.Show(newOptions);
const modal = TW4Dialog.Find(newOptions.Id);
modal.Footer.innerHTML = html;
return await new Promise(async (resolve, reject) => {
modal.Footer.querySelectorAll("button").forEach(el => {
el.addEventListener("click", (e) => {
e?.stopPropagation();
e?.preventDefault();
const value = el.getAttribute("data-prompt-value");
TW4Dialog.Close(newOptions.Id);
resolve(value);
});
});
modal.Modal.querySelectorAll("[aria-label='Close']").forEach(el => {
el.addEventListener("click", (e) => {
e?.stopPropagation();
e?.preventDefault();
TW4Dialog.Close(newOptions.Id);
resolve("");
});
});
});
}
#generateButton(label, value, colour) {
const html = `
<button class="px-4 py-2 text-sm font-medium ` + colour + ` border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 cursor-pointer" data-prompt-value="` + value + `">
` + label + `
</button>
`;
return html;
}
#randomN(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
}
// window.TW4MessageBox = new TWMessageBox4();
export default TWMessageBox4;

123
src/twpromptbox4.js Normal file
View File

@ -0,0 +1,123 @@
import TWDialog4 from "./twdialog4.js"
class TWPromptBox4 {
#defaultButtonColour = "bg-white hover:bg-gray-50 text-gray-700";
constructor() {
}
get DefaultOptions() {
const a = this;
return {
Id: a.#randomN(30001, 99999),
Title: "",
Content: "",
Size: "sm",
// ShowFooter: true,
// EasyClose: true,
EnableMovement: true,
EnableResize: true,
Placeholder: "",
Value: "",
Style: {
Label: "mb-1 inline-block",
Textbox: "block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
BoxType: "text"
},
Buttons: [
{ Label: "OK", Value: "OK", Colour: a.#defaultButtonColour },
{ Label: "Cancel", Value: "Cancel", Colour: a.#defaultButtonColour }
]
};
}
async Show(options) {
const a = this;
const newOptions = Object.assign(a.DefaultOptions, options);
newOptions.Url = null;
let html = "";
newOptions.Buttons.forEach(el => {
html += a.#generateButton(el.Label, el.Value, el.Colour);
});
await TW4Dialog.Show(newOptions);
const modal = TW4Dialog.Find(newOptions.Id);
modal.Body.innerHTML = a.#generateFormGroup(newOptions);
modal.Footer.innerHTML = html;
return await new Promise(async (resolve, reject) => {
modal.Footer.querySelectorAll("button").forEach(el => {
el.addEventListener("click", (e) => {
e?.stopPropagation();
e?.preventDefault();
const text = modal.Body.querySelector("input")?.value ?? "";
const value = el.getAttribute("data-prompt-value");
TW4Dialog.Close(newOptions.Id);
resolve({ Value: text, Button: value });
});
});
modal.Modal.querySelectorAll("[aria-label='Close']").forEach(el => {
el.addEventListener("click", (e) => {
e?.stopPropagation();
e?.preventDefault();
TW4Dialog.Close(newOptions.Id);
resolve({ Value: "", Button: "" });
});
});
});
}
#generateFormGroup(options) {
const a = this;
const id = "prompt-" + a.#randomN(30001, 99999);
const html = `
<div class="mb-4">
<label for="` + id + `" class="` + options.Style.Label + `">
` + options.Content + `
</label>
<input id="` + id + `" type="` + options.Style.BoxType + `" placeholder="` + options.Placeholder + `" value="` + options.Value + `" class="` + options.Style.Textbox + `" />
</div>
`;
return html;
}
#generateButton(label, value, colour) {
const html = `
<button class="px-4 py-2 text-sm font-medium ` + colour + ` border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 cursor-pointer" data-prompt-value="` + value + `">
` + label + `
</button>
`;
return html;
}
#randomN(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
}
// window.TW4PromptBox = new TWPromptBox4();
export default TWPromptBox4;

59
webpack.config.js Normal file
View File

@ -0,0 +1,59 @@
const path = require('path');
const { version } = require('./package.json');
const webpack = require('webpack');
class PrependVersionPlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync('PrependVersionPlugin', (compilation, callback) => {
Object.keys(compilation.assets).forEach((filename) => {
if (filename.endsWith('.js')) {
const asset = compilation.assets[filename];
const headerText = `/*!\n * TWDialog4 v${version}\n * Copyright 2025-2025 Ray Lam (https://www.hiimray.co.uk/bootstrap-js-bstoast4)\n *\n */\n`;
const newContent = headerText + asset.source();
compilation.assets[filename] = {
source: () => newContent,
size: () => newContent.length,
};
}
});
callback();
});
}
}
module.exports = {
entry: {
tw4dialog: './src/index.js'
},
output: {
filename: `[name].dist.js`,
path: path.resolve(__dirname, 'dist'),
clean: true,
libraryTarget: 'global'
},
mode: 'production',
plugins: [
new webpack.DefinePlugin({
'process.env.VERSION': JSON.stringify(version)
}),
new PrependVersionPlugin()
],
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}
]
}
};