summaryrefslogtreecommitdiffstats
path: root/toaster/toaster.js
blob: 6ee8a7dafc6cca873030bb5a6cd993e2eab17208 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// License: https://github.com/ivarlovlie/bootstrap-v5-toaster/blob/master/LICENSE
/** Class representing a Bootstrap 5 Toaster. */
class Toaster {
    constructor(options) {
        if (!options) {
            options = {
                hideAfter: 3500,
                position: "top-right",
            };
        }
        this.root = document.createElement("div");
        this.root.id = "toaster-container";
        switch (options.position) {
            case "top-right":
                this.root.style =
                    "display: flex; align-items: end; flex-direction: column; position: absolute; top: 0; right: 0; padding: 15px;";
                break;
            case "top-left":
                this.root.style = "position: absolute; top: 0; padding: 15px;";
                break;
            case "bottom-right":
                this.root.style =
                    "display: flex; align-items: end; flex-direction: column; position: absolute; bottom: 0; right: 0; padding: 15px;";
                break;
                break;
            case "bottom-left":
                this.root.style = "position: absolute; bottom: 0; padding: 15px;";
                break;
            default:
                // top-right
                this.root.style =
                    "display: flex; align-items: end; flex-direction: column; position: absolute; top: 0; right: 0; padding: 15px;";
                break;
        }
        document.body.appendChild(this.root);
        this.defaultTimeout = options.hideAfter ? options.hideAfter : 3500;
        this.toastTypes = {
            error: "error",
            success: "success",
            info: "info",
        };
    }
    display(title, message, autohide, type) {
        if (!title || typeof title !== "string" || typeof message !== "string") {
            throw new Error("Toaster: title &| message is empty or not a string");
        }
        let toast = document.createElement("div");
        toast.className = "toast";
        toast.role = "alert";
        toast.id = Math.random().toString(36).substring(2) + Date.now().toString(36);
        let toastHeader = document.createElement("div");
        toastHeader.className = "toast-header";
        let toastTypeIndicator = document.createElement("div");
        switch (type) {
            case this.toastTypes.error:
                toastTypeIndicator.className = "toast-type-indicator rounded mr-2 bg-danger";
                toast.style =
                    "width: max-content; min-width: 300px; border-color: var(--bs-danger);";
                break;
            case this.toastTypes.success:
                toastTypeIndicator.className = "toast-type-indicator rounded mr-2 bg-success";
                toast.style =
                    "width: max-content; min-width: 300px; border-color: var(--bs-success);";
                break;
            case this.toastTypes.info:
                toastTypeIndicator.className = "toast-type-indicator rounded mr-2 bg-info";
                toast.style = "width: max-content; min-width: 300px; border-color: var(--bs-info);";
                break;
        }
        toastTypeIndicator.style = "width: 18px; height: 18px;";
        toastHeader.appendChild(toastTypeIndicator);
        let toastTitle = document.createElement("strong");
        toastTitle.className = "mr-auto text-truncate";
        toastTitle.innerText = title;
        toastHeader.appendChild(toastTitle);
        let toastCloseButton = document.createElement("button");
        toastCloseButton.className = "ml-2 mb-1 close";
        toastCloseButton.dataset.dismiss = "toast";
        toastCloseButton.onclick = () => {
            setTimeout(() => {
                toast.parentNode.removeChild(toast);
            }, parseInt(1000));
        };
        if (!autohide) {
            let toastCloseButtonIcon = document.createElement("img");
            // icon license: https://github.com/twbs/icons/blob/master/LICENSE.md
            toastCloseButtonIcon.src =
                'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M14 1H2a1 1 0 00-1 1v12a1 1 0 001 1h12a1 1 0 001-1V2a1 1 0 00-1-1zM2 0a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V2a2 2 0 00-2-2H2z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M11.854 4.146a.5.5 0 010 .708l-7 7a.5.5 0 01-.708-.708l7-7a.5.5 0 01.708 0z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M4.146 4.146a.5.5 0 000 .708l7 7a.5.5 0 00.708-.708l-7-7a.5.5 0 00-.708 0z" clip-rule="evenodd"/></svg>';
            toastCloseButtonIcon.alt = "x";
            toastCloseButtonIcon.dataset.ariaHidden = "true";
            toastCloseButton.appendChild(toastCloseButtonIcon);
            toastHeader.appendChild(toastCloseButton);
        }
        toast.appendChild(toastHeader);
        if (message) {
            let toastBody = document.createElement("div");
            toastBody.className = "toast-body";
            toastBody.style = "word-wrap: break-word;";
            toastBody.innerText = message;
            toast.appendChild(toastBody);
        }
        this.root.appendChild(toast);
        let delay = 0;
        if (typeof autohide === "number" && autohide !== 0) {
            delay = autohide;
            autohide = true;
        } else if (autohide) {
            delay = this.defaultTimeout;
        }
        new bootstrap.Toast(document.getElementById(toast.id), {
            animation: true,
            autohide,
            delay,
        }).show();
        if (delay)
            setTimeout(() => {
                toast.parentNode.removeChild(toast);
            }, parseInt(delay + 1000));
    }
    /**
     * Display an toast indicating an error.
     * @param {string} title Title of the toast
     * @param {string} message Message of the toast
     * @param {boolean} autohide Autohide toast
     */
    error(title, message = "", autohide = true) {
        this.display(title, message, autohide, this.toastTypes.error);
    }
    /**
     * Display an toast indicating an successfull operation.
     * @param {string} title Title of the toast
     * @param {string} message Message of the toast
     * @param {boolean} autohide Autohide toast
     */
    success(title, message = "", autohide = true) {
        this.display(title, message, autohide, this.toastTypes.success);
    }
    /**
     * Display an informational toast.
     * @param {string} title Title of the toast
     * @param {string} message Message of the toast
     * @param {boolean} autohide Autohide toast
     */
    info(title, message = "", autohide = true) {
        this.display(title, message, autohide, this.toastTypes.info);
    }
}