@ntlab/ntjs-repo 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,345 @@
1
+ /**
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2023-2025 Toha <tohenk@yahoo.com>
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ * this software and associated documentation files (the "Software"), to deal in
8
+ * the Software without restriction, including without limitation the rights to
9
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10
+ * of the Software, and to permit persons to whom the Software is furnished to do
11
+ * so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ const Stringify = require('@ntlab/ntlib/stringify');
26
+ const { ScriptRepository, ScriptManager } = require('@ntlab/ntjs');
27
+ const JQuery = ScriptManager.require('JQuery');
28
+
29
+ /**
30
+ * Bootstrap/Dialog script repository.
31
+ */
32
+ class Dialog extends JQuery {
33
+
34
+ initialize() {
35
+ this.name = 'Dialog';
36
+ this.position = ScriptRepository.POSITION_FIRST;
37
+ this.addDependencies(['Bootstrap', 'JQuery/Define', 'JQuery/Util']);
38
+ }
39
+
40
+ getIconSet() {
41
+ const loading = this.translate('Loading...');
42
+
43
+ return {
44
+ 'ICON_INFO': 'bi-info-circle text-info fs-1',
45
+ 'ICON_ALERT':'bi-exclamation-circle text-warning fs-1',
46
+ 'ICON_ERROR': 'bi-x-circle text-danger fs-1',
47
+ 'ICON_SUCCESS': 'bi-check-circle text-success fs-1',
48
+ 'ICON_QUESTION': 'bi-question-circle text-primary fs-1',
49
+ 'ICON_INPUT': 'bi-pencil-square text-primary fs-1',
50
+ 'BTN_ICON_OK': 'bi-check-lg',
51
+ 'BTN_ICON_CANCEL': 'bi-x-lg',
52
+ 'BTN_ICON_CLOSE': 'bi-x-circle',
53
+ 'spinnerTmpl': `<div class="spinner-border text-secondary" role="status"><span class="visually-hidden">${loading}</span></div>`,
54
+ }
55
+ }
56
+
57
+ getScript() {
58
+ const icons = this.getIconSet();
59
+
60
+ return `
61
+ $.define('ntdlg', {
62
+ ICON_INFO: null,
63
+ ICON_ALERT: null,
64
+ ICON_ERROR: null,
65
+ ICON_SUCCESS: null,
66
+ ICON_QUESTION: null,
67
+ ICON_INPUT: null,
68
+ BTN_ICON_OK: null,
69
+ BTN_ICON_CANCEL: null,
70
+ BTN_ICON_CLOSE: null,
71
+ dialogTmpl:
72
+ '<div id="%ID%" class="modal fade" tabindex="-1" aria-labelledby="%ID%-title">' +
73
+ ' <div class="%MODAL%">' +
74
+ ' <div class="modal-content">' +
75
+ ' <div class="modal-header">' +
76
+ ' <h5 id="%ID%-title" class="modal-title text-truncate">%TITLE%</h5>' +
77
+ ' %CLOSE%' +
78
+ ' </div>' +
79
+ ' <div class="modal-body">%CONTENT%</div>' +
80
+ ' <div class="modal-footer">%BUTTONS%</div>' +
81
+ ' </div>' +
82
+ ' </div>' +
83
+ '</div>',
84
+ iconTmpl:
85
+ '<span class="dialog-icon %ICON%"></span>',
86
+ messageTmpl:
87
+ '<div class="d-flex flex-row">' +
88
+ ' <div class="flex-shrink-0 px-2">%ICON%</div>' +
89
+ ' <div class="flex-grow-1 ms-3 align-self-center">%MESSAGE%</div>' +
90
+ '</div>',
91
+ buttonClass:
92
+ 'btn btn-outline-%TYPE%',
93
+ buttonIconTmpl:
94
+ '<span class="%ICON%"></span> %CAPTION%',
95
+ buttonTmpl:
96
+ '<button id="%ID%" type="button" class="%BTNCLASS%">%CAPTION%</button>',
97
+ closeTmpl:
98
+ '<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="$close"></button>',
99
+ title(t) {
100
+ let _icon, _title;
101
+ if (t && t.title) {
102
+ _title = t.title;
103
+ if (t.icon) {
104
+ _icon = t.icon;
105
+ }
106
+ } else {
107
+ _title = t;
108
+ }
109
+ if (_icon) {
110
+ _icon = typeof $.ntdlg.createIcon === 'function' ? $.ntdlg.createIcon(_icon) :
111
+ \`<span class="\${_icon}"></span>\`;
112
+ } else if ($.ntdlg.defaultIcon) {
113
+ _icon = $.ntdlg.defaultIcon;
114
+ }
115
+ return _icon ? _icon + ' ' + _title : _title;
116
+ },
117
+ create(id, title, message, options) {
118
+ const self = this;
119
+ const dlg_id = '#' + id;
120
+ $(dlg_id).remove();
121
+ if ($.ntdlg.moved && $.ntdlg.moved.refs[id] !== undefined) {
122
+ $('div.' + $.ntdlg.moved.refs[id]).remove();
123
+ delete $.ntdlg.moved.refs[id];
124
+ }
125
+ const closable = options.closable !== undefined ? options.closable : true;
126
+ const buttons = [];
127
+ const handlers = [];
128
+ let cnt = 0;
129
+ if (options.buttons) {
130
+ Object.keys(options.buttons).forEach(k => {
131
+ const v = options.buttons[k];
132
+ let caption, btnType, btnIcon, handler;
133
+ if (Array.isArray(v) || typeof v === 'object') {
134
+ caption = v.caption ? v.caption : k;
135
+ btnType = v.type ? v.type : (0 === cnt ? 'primary' : 'secondary');
136
+ if (v.icon) {
137
+ btnIcon = v.icon;
138
+ }
139
+ handler = typeof v.handler === 'function' ? v.handler : null;
140
+ } else {
141
+ caption = k;
142
+ btnType = 0 === cnt ? 'primary' : 'secondary';
143
+ handler = typeof v === 'function' ? v : null;
144
+ }
145
+ const btnid = id + '_btn_' + caption.replace(/\W+/g, "-").toLowerCase();
146
+ const btnclass = $.util.template(self.buttonClass, {TYPE: btnType});
147
+ if (btnIcon) {
148
+ caption = $.util.template(self.buttonIconTmpl, {CAPTION: caption, ICON: btnIcon});
149
+ }
150
+ buttons.push($.util.template(self.buttonTmpl, {
151
+ ID: btnid,
152
+ BTNCLASS: btnclass,
153
+ CAPTION: caption
154
+ }));
155
+ if (typeof handler === 'function') {
156
+ handlers.push({id: btnid, handler: handler});
157
+ }
158
+ cnt++;
159
+ });
160
+ }
161
+ const m = ['modal-dialog', 'modal-dialog-centered'];
162
+ if (options.size) {
163
+ m.push('modal-' + options.size);
164
+ }
165
+ const content = $.util.template(self.dialogTmpl, {
166
+ ID: id,
167
+ TITLE: self.title(title),
168
+ MODAL: m.join(' '),
169
+ CLOSE: closable ? self.closeTmpl : '',
170
+ BUTTONS: buttons.join(''),
171
+ CONTENT: message
172
+ });
173
+ $(document.body).append(content);
174
+ const dlg = $(dlg_id);
175
+ // move embedded modal
176
+ const bd = dlg.find('.modal-body');
177
+ const d = bd.find('div.modal');
178
+ if (d.length) {
179
+ if (!$.ntdlg.moved) {
180
+ $.ntdlg.moved = {count: 0, refs: {}}
181
+ }
182
+ $.ntdlg.moved.count++;
183
+ const movedDlg = id + '-moved-' + $.ntdlg.moved.count;
184
+ $.ntdlg.moved.refs[id] = movedDlg;
185
+ d.addClass(movedDlg);
186
+ d.appendTo($(document.body));
187
+ }
188
+ if (buttons.length === 0) {
189
+ dlg.find('.modal-footer').hide();
190
+ }
191
+ Object.values(handlers).forEach(v => {
192
+ $('#' + v.id).on('click', function(e) {
193
+ e.preventDefault();
194
+ v.handler.apply(dlg);
195
+ });
196
+ });
197
+ const opts = ['backdrop', 'keyboard', 'show', 'remote'];
198
+ const events = ['show.bs.modal', 'shown.bs.modal', 'hide.bs.modal', 'hidden.bs.modal', 'loaded.bs.modal'];
199
+ const modal_options = {};
200
+ $.util.applyProp(opts, options, modal_options);
201
+ $.util.applyEvent(dlg, events, options);
202
+ // compatibility with JQuery UI dialog
203
+ const deprecatedEvents = {open: 'shown.bs.modal', close: 'hidden.bs.modal'};
204
+ Object.keys(deprecatedEvents).forEach(event => {
205
+ const newEvent = deprecatedEvents[event];
206
+ if (typeof options[event] === 'function') {
207
+ dlg.on(newEvent, options[event]);
208
+ }
209
+ });
210
+ self._create(dlg[0], modal_options);
211
+ return dlg;
212
+ },
213
+ dialog(id, title, message, icon, buttons, close_cb) {
214
+ const self = this;
215
+ icon = icon || self.ICON_INFO;
216
+ buttons = buttons || [];
217
+ message = $.util.template(self.messageTmpl, {
218
+ ICON: $.util.template(self.iconTmpl, {ICON: icon}),
219
+ MESSAGE: message
220
+ });
221
+ const dlg = self.create(id, title, message, {
222
+ ['shown.bs.modal'](e) {
223
+ e.preventDefault();
224
+ let focused = dlg.find('input.focused');
225
+ if (focused.length) {
226
+ focused.focus();
227
+ } else {
228
+ const buttons = dlg.find('.modal-footer button.btn');
229
+ if (buttons.length) {
230
+ buttons.first().focus();
231
+ }
232
+ }
233
+ },
234
+ ['hidden.bs.modal'](e) {
235
+ e.preventDefault();
236
+ if (typeof close_cb === 'function') {
237
+ close_cb();
238
+ }
239
+ },
240
+ buttons
241
+ });
242
+ $.ntdlg.show(dlg);
243
+ return dlg;
244
+ },
245
+ show(dlg) {
246
+ const self = this;
247
+ if (dlg && !this.isVisible(dlg)) {
248
+ if (typeof dlg === 'string') {
249
+ dlg = $('#' + dlg);
250
+ }
251
+ let d = self._get(dlg[0]);
252
+ if (!d) {
253
+ d = self._create(dlg[0]);
254
+ }
255
+ if (d) {
256
+ d.show();
257
+ }
258
+ }
259
+ },
260
+ close(dlg) {
261
+ const self = this;
262
+ if (dlg) {
263
+ if (typeof dlg === 'string') {
264
+ dlg = $('#' + dlg);
265
+ }
266
+ const d = self._get(dlg[0]);
267
+ if (d) {
268
+ d.hide();
269
+ }
270
+ }
271
+ },
272
+ isVisible(dlg) {
273
+ if (dlg) {
274
+ if (typeof dlg === 'string') {
275
+ dlg = $('#' + dlg);
276
+ }
277
+ if (dlg.length) {
278
+ if (dlg.hasClass('modal') && dlg.is(':visible')) {
279
+ return true;
280
+ }
281
+ }
282
+ return false;
283
+ }
284
+ },
285
+ getBody(dlg) {
286
+ if (dlg) {
287
+ if (typeof dlg === 'string') {
288
+ dlg = $('#' + dlg);
289
+ }
290
+ return dlg.find('.modal-body:first');
291
+ }
292
+ },
293
+ _create(el, options) {
294
+ return new bootstrap.Modal(el, options || {});
295
+ },
296
+ _get(el) {
297
+ return bootstrap.Modal.getInstance(el);
298
+ },
299
+ init() {
300
+ // icon set
301
+ Object.assign(this, ${Stringify.from(icons, 2)});
302
+ // https://stackoverflow.com/questions/19305821/multiple-modals-overlay
303
+ // fix z-index
304
+ const p = bootstrap.Modal.prototype;
305
+ if (p.__showElement === undefined) {
306
+ p.__showElement = p._showElement;
307
+ p._showElement = function(relatedTarget) {
308
+ this.__showElement(relatedTarget);
309
+ let cIdx = zIdx = parseInt($(this._element).css('z-index'));
310
+ if ($.ntdlg.zIndex) {
311
+ zIdx = Math.max(zIdx, $.ntdlg.zIndex);
312
+ }
313
+ const modalCount = $('.modal:visible').length;
314
+ if (modalCount > 1 || zIdx > cIdx) {
315
+ zIdx += 10 * (modalCount - 1);
316
+ $(this._element).css('z-index', zIdx);
317
+ $(this._backdrop).css('z-index', zIdx - 1);
318
+ }
319
+ }
320
+ }
321
+ // re-add modal-open class if there're still opened modal
322
+ if (p.__resetAdjustments === undefined) {
323
+ p.__resetAdjustments = p._resetAdjustments;
324
+ p._resetAdjustments = function() {
325
+ this.__resetAdjustments();
326
+ if ($('.modal:visible').length > 0) {
327
+ $(document.body).addClass('modal-open');
328
+ }
329
+ }
330
+ }
331
+ }
332
+ }, true);
333
+ `;
334
+ }
335
+
336
+ getInitScript() {
337
+ this.addLast(`$.ntdlg.init();`);
338
+ }
339
+
340
+ static instance() {
341
+ return new this();
342
+ }
343
+ }
344
+
345
+ module.exports = Dialog;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2018-2025 Toha <tohenk@yahoo.com>
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ * this software and associated documentation files (the "Software"), to deal in
8
+ * the Software without restriction, including without limitation the rights to
9
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10
+ * of the Software, and to permit persons to whom the Software is furnished to do
11
+ * so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ const Stringify = require('@ntlab/ntlib/stringify');
26
+ const { ScriptRepository, ScriptManager } = require('@ntlab/ntjs');
27
+ const JQueryFormPost = ScriptManager.require('JQuery/FormPost');
28
+
29
+ /**
30
+ * Bootstrap/FormPost script repository.
31
+ */
32
+ class FormPost extends JQueryFormPost {
33
+
34
+ initialize() {
35
+ this.name = 'FormPost';
36
+ this.position = ScriptRepository.POSITION_FIRST;
37
+ this.addDependencies(['JQuery', 'JQuery/PostHandler', 'Bootstrap/Dialog/Wait', 'Bootstrap/Dialog/Message']);
38
+ }
39
+
40
+ getOverrides() {
41
+ const ok = this.translate('OK');
42
+
43
+ return {
44
+ showSuccessMessage: Stringify.raw(`function(title, message, opts) {
45
+ const autoclose = opts.autoClose !== undefined ? opts.autoClose : false;
46
+ const withokay = opts.withOkay !== undefined ? opts.withOkay : true;
47
+ const buttons = {};
48
+ if (withokay && !autoclose) {
49
+ buttons['${ok}'] = {
50
+ icon: $.ntdlg.BTN_ICON_OK,
51
+ handler() {
52
+ $.ntdlg.close($(this));
53
+ }
54
+ }
55
+ }
56
+ const dlg = $.ntdlg.dialog('form_post_success', title, message, $.ntdlg.ICON_SUCCESS, buttons);
57
+ if (autoclose) {
58
+ dlg.on('shown.bs.modal', function() {
59
+ $.ntdlg.close($(this));
60
+ });
61
+ }
62
+ }`),
63
+ showErrorMessage: Stringify.raw(`function(title, message, callback) {
64
+ $.ntdlg.dialog('form_post_error', title, message, $.ntdlg.ICON_ERROR, {
65
+ '${ok}': {
66
+ icon: $.ntdlg.BTN_ICON_OK,
67
+ handler() {
68
+ $.ntdlg.close($(this));
69
+ }
70
+ }
71
+ }, callback);
72
+ }`),
73
+ }
74
+ }
75
+
76
+ getErrHelperOptions() {
77
+ return {
78
+ errorContainer: '.alert-danger .msg',
79
+ errorFormat: Stringify.raw('$.errformat.INPLACE'),
80
+ parentClass: null,
81
+ errClass: 'is-invalid',
82
+ toggleClass: 'd-none',
83
+ listClass: 'list-unstyled mb-0',
84
+ visibilityUseClass: true,
85
+ inplace: Stringify.raw(`function(el, error) {
86
+ if (el.hasClass('alert-danger')) {
87
+ el.html(error);
88
+ } else {
89
+ let tt = el;
90
+ const f = function(x, a, p) {
91
+ const errDisp = x.attr(a);
92
+ if (errDisp) {
93
+ const xel = p ? x.parents(errDisp) : x.siblings(errDisp);
94
+ if (xel.length) {
95
+ return xel;
96
+ }
97
+ }
98
+ }
99
+ let xel = f(tt, 'data-err-display');
100
+ if (!xel) {
101
+ xel = f(tt, 'data-err-display-parent', true);
102
+ }
103
+ if (xel) {
104
+ tt = xel;
105
+ }
106
+ // don't add tooltip on hidden input
107
+ if (tt.is('input[type="hidden"]')) {
108
+ tt = tt.siblings('input');
109
+ }
110
+ let tooltip = bootstrap.Tooltip.getInstance(tt[0]);
111
+ if (tooltip) {
112
+ tooltip._config.title = error;
113
+ } else {
114
+ tooltip = new bootstrap.Tooltip(tt[0], {title: error, placement: 'right'});
115
+ }
116
+ xel = f(el, 'data-err-target');
117
+ el = xel ? xel : tt;
118
+ el.data('err-tt', tt);
119
+ }
120
+ return el;
121
+ }`),
122
+ onErrReset: Stringify.raw(`function(helper) {
123
+ if (helper.container) {
124
+ helper.container.find('.' + helper.errClass).each(function() {
125
+ const el = $(this);
126
+ const tt = el.data('err-tt');
127
+ if (tt) {
128
+ const tooltip = bootstrap.Tooltip.getInstance(tt[0]);
129
+ if (tooltip) {
130
+ tooltip._config.title = '';
131
+ }
132
+ }
133
+ el.removeClass(helper.errClass);
134
+ });
135
+ }
136
+ }`),
137
+ }
138
+ }
139
+
140
+ static instance() {
141
+ return new this();
142
+ }
143
+ }
144
+
145
+ module.exports = FormPost;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2018-2025 Toha <tohenk@yahoo.com>
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ * this software and associated documentation files (the "Software"), to deal in
8
+ * the Software without restriction, including without limitation the rights to
9
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10
+ * of the Software, and to permit persons to whom the Software is furnished to do
11
+ * so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ const { ScriptManager, ScriptRepository } = require('@ntlab/ntjs');
26
+ const JQuery = ScriptManager.require('JQuery');
27
+
28
+ class Notification extends JQuery {
29
+
30
+ initialize() {
31
+ this.name = 'Notification';
32
+ this.position = ScriptRepository.POSITION_MIDDLE;
33
+ this.dependencies = ['Bootstrap', 'JQuery/Define', 'JQuery/Notification'];
34
+ }
35
+
36
+ getScript() {
37
+ const close = this.translate('Close');
38
+
39
+ return `
40
+ $.define('notif', {
41
+ notifyMessage(message, options) {
42
+ const self = this;
43
+ let icon;
44
+ switch (options) {
45
+ case 'success':
46
+ icon = 'bi-check-circle text-success';
47
+ break;
48
+ case 'error':
49
+ icon = 'bi-x-circle text-danger';
50
+ break;
51
+ case 'info':
52
+ icon = 'bi-info-circle text-info';
53
+ break;
54
+ case 'warn':
55
+ icon = 'bi-exclamation-circle text-warning';
56
+ break;
57
+ }
58
+ const tmpl =
59
+ '<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">' +
60
+ ' <div class="toast-header">' +
61
+ ' <span class="bi-bell me-1"></span>' +
62
+ ' <strong class="me-auto">%TITLE%</strong>' +
63
+ ' <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="${close}"></button>' +
64
+ ' </div>' +
65
+ ' <div class="toast-body">' +
66
+ ' <div class="d-flex flex-row">' +
67
+ ' <div class="flex-shrink-0"><span class="%ICON% fs-4"></span></div>' +
68
+ ' <div class="flex-grow-1 ms-3 align-self-center">%MESSAGE%</div>' +
69
+ ' </div>' +
70
+ ' </div>' +
71
+ '</div>';
72
+ const toast = tmpl
73
+ .replace(/%TITLE%/, options.title || self.title)
74
+ .replace(/%ICON%/, icon)
75
+ .replace(/%MESSAGE%/, message)
76
+ ;
77
+ if (self.container === undefined) {
78
+ self.container = $('<div class="notification-container position-fixed bottom-0 end-0 p-3"></div>').appendTo(document.body);
79
+ }
80
+ const el = $(toast).appendTo(self.container);
81
+ const t = new bootstrap.Toast(el[0]);
82
+ t.show();
83
+ return t;
84
+ }
85
+ }, true);
86
+ `;
87
+ }
88
+
89
+ static instance() {
90
+ return new this();
91
+ }
92
+ }
93
+
94
+ module.exports = Notification;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2023-2025 Toha <tohenk@yahoo.com>
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ * this software and associated documentation files (the "Software"), to deal in
8
+ * the Software without restriction, including without limitation the rights to
9
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10
+ * of the Software, and to permit persons to whom the Software is furnished to do
11
+ * so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ const { Script, ScriptAsset } = require('@ntlab/ntjs');
26
+
27
+ class Bootstrap extends Script {
28
+
29
+ initialize() {
30
+ this.name = 'Bootstrap';
31
+ this.dependencies = ['Popper'];
32
+ this.assetPath = 'bootstrap';
33
+ this.getAsset()
34
+ .setPath(ScriptAsset.JAVASCRIPT, 'js')
35
+ .setPath(ScriptAsset.STYLESHEET, 'css');
36
+ this.addAsset(ScriptAsset.JAVASCRIPT, 'bootstrap.min');
37
+ this.addAsset(ScriptAsset.STYLESHEET, 'bootstrap.min');
38
+ }
39
+
40
+ static instance() {
41
+ return new this();
42
+ }
43
+ }
44
+
45
+ module.exports = Bootstrap;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2023-2025 Toha <tohenk@yahoo.com>
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ * this software and associated documentation files (the "Software"), to deal in
8
+ * the Software without restriction, including without limitation the rights to
9
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10
+ * of the Software, and to permit persons to whom the Software is furnished to do
11
+ * so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ const { Script, ScriptAsset } = require('@ntlab/ntjs');
26
+
27
+ class BootstrapIcons extends Script {
28
+
29
+ initialize() {
30
+ this.name = 'BootstrapIcons';
31
+ this.assetPath = 'bootstrap-icons';
32
+ this.getAsset()
33
+ .setPath(ScriptAsset.STYLESHEET, 'font');
34
+ this.addAsset(ScriptAsset.STYLESHEET, 'bootstrap-icons');
35
+ }
36
+
37
+ static instance() {
38
+ return new this();
39
+ }
40
+ }
41
+
42
+ module.exports = BootstrapIcons;