web-mojo 2.1.175 → 2.1.244
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.
- package/README.md +25 -41
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +243 -52
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.cjs.js.map +1 -1
- package/dist/auth.es.js +3 -3
- package/dist/auth.es.js.map +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +2 -2
- package/dist/chunks/{ContextMenu-CuTbtePk.js → ContextMenu-CPQ_ZrT6.js} +2 -2
- package/dist/chunks/{ContextMenu-CuTbtePk.js.map → ContextMenu-CPQ_ZrT6.js.map} +1 -1
- package/dist/chunks/{ContextMenu-CvFe_-pA.js → ContextMenu-qToF2PNG.js} +2 -2
- package/dist/chunks/{ContextMenu-CvFe_-pA.js.map → ContextMenu-qToF2PNG.js.map} +1 -1
- package/dist/chunks/{DataView-UjG66gmW.js → DataView-COXtv8kh.js} +2 -2
- package/dist/chunks/{DataView-UjG66gmW.js.map → DataView-COXtv8kh.js.map} +1 -1
- package/dist/chunks/{DataView-BwZajXhu.js → DataView-kd5iuE-9.js} +2 -2
- package/dist/chunks/{DataView-BwZajXhu.js.map → DataView-kd5iuE-9.js.map} +1 -1
- package/dist/chunks/Dialog-Bz4Z9bSO.js +2 -0
- package/dist/chunks/Dialog-Bz4Z9bSO.js.map +1 -0
- package/dist/chunks/{Dialog-eoLNdg-d.js → Dialog-DWDn0XUt.js} +84 -31
- package/dist/chunks/Dialog-DWDn0XUt.js.map +1 -0
- package/dist/chunks/{FilePreviewView-Crpalaos.js → FilePreviewView-CCngMe-J.js} +578 -20
- package/dist/chunks/FilePreviewView-CCngMe-J.js.map +1 -0
- package/dist/chunks/FilePreviewView-Dacxyem-.js +2 -0
- package/dist/chunks/FilePreviewView-Dacxyem-.js.map +1 -0
- package/dist/chunks/FormView-CG2GC9qH.js +2 -0
- package/dist/chunks/FormView-CG2GC9qH.js.map +1 -0
- package/dist/chunks/{FormView-fUbbKQQU.js → FormView-GxoZ1H6P.js} +4 -2
- package/dist/chunks/FormView-GxoZ1H6P.js.map +1 -0
- package/dist/chunks/{MetricsChart-DY1w4wC-.js → MetricsChart-D1n5a4YY.js} +3 -3
- package/dist/chunks/{MetricsChart-DY1w4wC-.js.map → MetricsChart-D1n5a4YY.js.map} +1 -1
- package/dist/chunks/{MetricsChart-B_KuNFD-.js → MetricsChart-DDiVVisT.js} +2 -2
- package/dist/chunks/{MetricsChart-B_KuNFD-.js.map → MetricsChart-DDiVVisT.js.map} +1 -1
- package/dist/chunks/{PDFViewer-CYaI8u8I.js → PDFViewer-DGAtX0Ms.js} +3 -3
- package/dist/chunks/{PDFViewer-CYaI8u8I.js.map → PDFViewer-DGAtX0Ms.js.map} +1 -1
- package/dist/chunks/{PDFViewer-D5SmAaQD.js → PDFViewer-DrJPeSLI.js} +2 -2
- package/dist/chunks/{PDFViewer-D5SmAaQD.js.map → PDFViewer-DrJPeSLI.js.map} +1 -1
- package/dist/chunks/{Page-D5kSAjt8.js → Page-BsqCluiN.js} +2 -2
- package/dist/chunks/{Page-D5kSAjt8.js.map → Page-BsqCluiN.js.map} +1 -1
- package/dist/chunks/{Page-tbcVc_Wl.js → Page-CouRTtLr.js} +2 -2
- package/dist/chunks/{Page-tbcVc_Wl.js.map → Page-CouRTtLr.js.map} +1 -1
- package/dist/chunks/{TopNav-DlHcfQbu.js → TopNav-B0OgzwWD.js} +2 -2
- package/dist/chunks/{TopNav-DlHcfQbu.js.map → TopNav-B0OgzwWD.js.map} +1 -1
- package/dist/chunks/{TopNav-mQvYJp04.js → TopNav-CJGYrCmM.js} +2 -2
- package/dist/chunks/{TopNav-mQvYJp04.js.map → TopNav-CJGYrCmM.js.map} +1 -1
- package/dist/chunks/{User-CvWFN0ul.js → User-DARwVvpV.js} +2 -2
- package/dist/chunks/{User-CvWFN0ul.js.map → User-DARwVvpV.js.map} +1 -1
- package/dist/chunks/{User-DI3U4oRV.js → User-DzR9RPjg.js} +2 -2
- package/dist/chunks/{User-DI3U4oRV.js.map → User-DzR9RPjg.js.map} +1 -1
- package/dist/chunks/{WebApp-B2r2EDj7.js → WebApp-CR6b7HZz.js} +23 -12
- package/dist/chunks/WebApp-CR6b7HZz.js.map +1 -0
- package/dist/chunks/WebApp-D_j_HtgS.js +2 -0
- package/dist/chunks/WebApp-D_j_HtgS.js.map +1 -0
- package/dist/css/web-mojo.css +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +6 -6
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +11 -11
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.es.js +4 -4
- package/dist/table.css +260 -0
- package/package.json +1 -1
- package/dist/chunks/Dialog-BmvpkgLD.js +0 -2
- package/dist/chunks/Dialog-BmvpkgLD.js.map +0 -1
- package/dist/chunks/Dialog-eoLNdg-d.js.map +0 -1
- package/dist/chunks/FilePreviewView-Crpalaos.js.map +0 -1
- package/dist/chunks/FilePreviewView-CxplM_0z.js +0 -2
- package/dist/chunks/FilePreviewView-CxplM_0z.js.map +0 -1
- package/dist/chunks/FormView-BDBRWOlR.js +0 -2
- package/dist/chunks/FormView-BDBRWOlR.js.map +0 -1
- package/dist/chunks/FormView-fUbbKQQU.js.map +0 -1
- package/dist/chunks/WebApp-B2r2EDj7.js.map +0 -1
- package/dist/chunks/WebApp-sKJf8j1s.js +0 -2
- package/dist/chunks/WebApp-sKJf8j1s.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { M as Model, C as Collection, T as ToastService, G as GroupList, c as UserList } from "./User-
|
|
2
|
-
import { r as rest, V as View, d as dataFormatter, M as Mustache } from "./WebApp-
|
|
3
|
-
import { P as Page } from "./Page-
|
|
4
|
-
import Dialog from "./Dialog-
|
|
5
|
-
import { F as FormView } from "./FormView-
|
|
1
|
+
import { M as Model, C as Collection, T as ToastService, G as GroupList, c as UserList } from "./User-DzR9RPjg.js";
|
|
2
|
+
import { r as rest, V as View, d as dataFormatter, M as Mustache } from "./WebApp-CR6b7HZz.js";
|
|
3
|
+
import { P as Page } from "./Page-CouRTtLr.js";
|
|
4
|
+
import Dialog from "./Dialog-DWDn0XUt.js";
|
|
5
|
+
import { F as FormView } from "./FormView-GxoZ1H6P.js";
|
|
6
6
|
class S3Bucket extends Model {
|
|
7
7
|
constructor(data = {}) {
|
|
8
8
|
super(data, {
|
|
@@ -3079,6 +3079,7 @@ class TableRow extends ListViewItem {
|
|
|
3079
3079
|
this.contextMenu = options.contextMenu || null;
|
|
3080
3080
|
this.batchActions = options.batchActions || null;
|
|
3081
3081
|
this.tableView = options.tableView || options.listView || null;
|
|
3082
|
+
this.editingCells = /* @__PURE__ */ new Set();
|
|
3082
3083
|
this.template = this.buildRowTemplate();
|
|
3083
3084
|
}
|
|
3084
3085
|
/**
|
|
@@ -3115,13 +3116,17 @@ class TableRow extends ListViewItem {
|
|
|
3115
3116
|
this.columns.forEach((column) => {
|
|
3116
3117
|
const cellClass = column.class || column.className || "";
|
|
3117
3118
|
const responsiveClasses = this.getResponsiveClasses(column.visibility);
|
|
3118
|
-
const
|
|
3119
|
+
const editableClass = column.editable ? "editable-cell" : "";
|
|
3120
|
+
const combinedClasses = [cellClass, responsiveClasses, editableClass].filter((c) => c).join(" ");
|
|
3119
3121
|
const cellContent = this.buildCellTemplate(column);
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
+
let cellAction = column.action;
|
|
3123
|
+
if (!cellAction && column.editable) {
|
|
3124
|
+
cellAction = "edit-cell";
|
|
3125
|
+
} else if (!cellAction && this.tableView.rowAction) {
|
|
3126
|
+
cellAction = this.tableView.rowAction;
|
|
3122
3127
|
}
|
|
3123
|
-
if (
|
|
3124
|
-
template += `<td class="${combinedClasses}" data-action="${
|
|
3128
|
+
if (cellAction) {
|
|
3129
|
+
template += `<td class="${combinedClasses}" data-action="${cellAction}" data-column="${column.key}">${cellContent}</td>`;
|
|
3125
3130
|
} else {
|
|
3126
3131
|
template += `<td class="${combinedClasses}" data-column="${column.key}">${cellContent}</td>`;
|
|
3127
3132
|
}
|
|
@@ -3151,6 +3156,9 @@ class TableRow extends ListViewItem {
|
|
|
3151
3156
|
if (column.template) {
|
|
3152
3157
|
return column.template;
|
|
3153
3158
|
}
|
|
3159
|
+
if (column.editable) {
|
|
3160
|
+
return `<span class="cell-content" data-field="${column.key}">{{{${path}}}}</span>`;
|
|
3161
|
+
}
|
|
3154
3162
|
return `{{{${path}}}}`;
|
|
3155
3163
|
}
|
|
3156
3164
|
/**
|
|
@@ -3281,11 +3289,22 @@ class TableRow extends ListViewItem {
|
|
|
3281
3289
|
this.element.setAttribute("data-id", id);
|
|
3282
3290
|
}
|
|
3283
3291
|
}
|
|
3292
|
+
/**
|
|
3293
|
+
* Handle edit cell action
|
|
3294
|
+
*/
|
|
3295
|
+
async onActionEditCell(event, element) {
|
|
3296
|
+
event.stopPropagation();
|
|
3297
|
+
const columnKey = element.getAttribute("data-column");
|
|
3298
|
+
const column = this.columns.find((col) => col.key === columnKey);
|
|
3299
|
+
if (!column || !column.editable) return;
|
|
3300
|
+
if (this.editingCells.has(columnKey)) return;
|
|
3301
|
+
await this.enterEditMode(columnKey, column, element);
|
|
3302
|
+
}
|
|
3284
3303
|
/**
|
|
3285
3304
|
* Handle row click action
|
|
3286
3305
|
*/
|
|
3287
3306
|
async onActionRowClick(event, element) {
|
|
3288
|
-
if (event.target.closest(".btn-group") || event.target.closest(".dropdown")) {
|
|
3307
|
+
if (event.target.closest(".btn-group") || event.target.closest(".dropdown") || event.target.closest(".cell-editor")) {
|
|
3289
3308
|
return;
|
|
3290
3309
|
}
|
|
3291
3310
|
this.emit("row:click", {
|
|
@@ -3358,6 +3377,265 @@ class TableRow extends ListViewItem {
|
|
|
3358
3377
|
});
|
|
3359
3378
|
}
|
|
3360
3379
|
}
|
|
3380
|
+
/**
|
|
3381
|
+
* Enter edit mode for a cell
|
|
3382
|
+
*/
|
|
3383
|
+
async enterEditMode(columnKey, column, cellElement) {
|
|
3384
|
+
const contentSpan = cellElement.querySelector(".cell-content");
|
|
3385
|
+
if (!contentSpan) return;
|
|
3386
|
+
this.editingCells.add(columnKey);
|
|
3387
|
+
const currentValue = this.model.get ? this.model.get(columnKey) : this.model[columnKey];
|
|
3388
|
+
const editor = this.createCellEditor(column, currentValue);
|
|
3389
|
+
const originalContent = contentSpan.innerHTML;
|
|
3390
|
+
contentSpan.style.display = "none";
|
|
3391
|
+
const editorContainer = document.createElement("div");
|
|
3392
|
+
editorContainer.className = "cell-editor";
|
|
3393
|
+
editorContainer.innerHTML = editor;
|
|
3394
|
+
cellElement.appendChild(editorContainer);
|
|
3395
|
+
const input = editorContainer.querySelector("input, select, .form-check-input");
|
|
3396
|
+
if (input) {
|
|
3397
|
+
input.focus();
|
|
3398
|
+
if (input.type === "text" || input.type === "textarea") {
|
|
3399
|
+
input.select();
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
editorContainer.dataset.originalContent = originalContent;
|
|
3403
|
+
editorContainer.dataset.columnKey = columnKey;
|
|
3404
|
+
this.setupEditorEvents(editorContainer, columnKey, column);
|
|
3405
|
+
this.emit("cell:edit", {
|
|
3406
|
+
row: this,
|
|
3407
|
+
model: this.model,
|
|
3408
|
+
column: columnKey,
|
|
3409
|
+
originalValue: currentValue
|
|
3410
|
+
});
|
|
3411
|
+
}
|
|
3412
|
+
/**
|
|
3413
|
+
* Create cell editor HTML based on column configuration
|
|
3414
|
+
*/
|
|
3415
|
+
createCellEditor(column, currentValue) {
|
|
3416
|
+
const options = column.editableOptions || {};
|
|
3417
|
+
switch (options.type) {
|
|
3418
|
+
case "select":
|
|
3419
|
+
return this.createSelectEditor(options, currentValue);
|
|
3420
|
+
case "switch":
|
|
3421
|
+
case "checkbox":
|
|
3422
|
+
return this.createSwitchEditor(options, currentValue);
|
|
3423
|
+
case "textarea":
|
|
3424
|
+
return this.createTextareaEditor(options, currentValue);
|
|
3425
|
+
default:
|
|
3426
|
+
return this.createTextEditor(options, currentValue);
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
/**
|
|
3430
|
+
* Create text input editor
|
|
3431
|
+
*/
|
|
3432
|
+
createTextEditor(options, currentValue) {
|
|
3433
|
+
const placeholder = options.placeholder || "";
|
|
3434
|
+
const inputType = options.inputType || "text";
|
|
3435
|
+
return `
|
|
3436
|
+
<div class="d-flex gap-1 align-items-center">
|
|
3437
|
+
<input type="${inputType}"
|
|
3438
|
+
class="form-control form-control-sm cell-input"
|
|
3439
|
+
value="${this.escapeHtml(currentValue || "")}"
|
|
3440
|
+
placeholder="${placeholder}">
|
|
3441
|
+
<button type="button" class="btn btn-sm btn-success cell-save" title="Save">
|
|
3442
|
+
<i class="bi bi-check"></i>
|
|
3443
|
+
</button>
|
|
3444
|
+
<button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">
|
|
3445
|
+
<i class="bi bi-x"></i>
|
|
3446
|
+
</button>
|
|
3447
|
+
</div>
|
|
3448
|
+
`;
|
|
3449
|
+
}
|
|
3450
|
+
/**
|
|
3451
|
+
* Create textarea editor
|
|
3452
|
+
*/
|
|
3453
|
+
createTextareaEditor(options, currentValue) {
|
|
3454
|
+
const placeholder = options.placeholder || "";
|
|
3455
|
+
const rows = options.rows || 2;
|
|
3456
|
+
return `
|
|
3457
|
+
<div class="d-flex gap-1">
|
|
3458
|
+
<textarea class="form-control form-control-sm cell-input"
|
|
3459
|
+
rows="${rows}"
|
|
3460
|
+
placeholder="${placeholder}">${this.escapeHtml(currentValue || "")}</textarea>
|
|
3461
|
+
<div class="d-flex flex-column gap-1">
|
|
3462
|
+
<button type="button" class="btn btn-sm btn-success cell-save" title="Save">
|
|
3463
|
+
<i class="bi bi-check"></i>
|
|
3464
|
+
</button>
|
|
3465
|
+
<button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">
|
|
3466
|
+
<i class="bi bi-x"></i>
|
|
3467
|
+
</button>
|
|
3468
|
+
</div>
|
|
3469
|
+
</div>
|
|
3470
|
+
`;
|
|
3471
|
+
}
|
|
3472
|
+
/**
|
|
3473
|
+
* Create select dropdown editor
|
|
3474
|
+
*/
|
|
3475
|
+
createSelectEditor(options, currentValue) {
|
|
3476
|
+
const optionsArray = options.options || [];
|
|
3477
|
+
let optionsHtml = "";
|
|
3478
|
+
optionsArray.forEach((option) => {
|
|
3479
|
+
if (typeof option === "string") {
|
|
3480
|
+
const selected = option === currentValue ? "selected" : "";
|
|
3481
|
+
optionsHtml += `<option value="${option}" ${selected}>${option}</option>`;
|
|
3482
|
+
} else if (typeof option === "object" && option.value !== void 0) {
|
|
3483
|
+
const selected = option.value === currentValue ? "selected" : "";
|
|
3484
|
+
optionsHtml += `<option value="${option.value}" ${selected}>${option.label || option.value}</option>`;
|
|
3485
|
+
}
|
|
3486
|
+
});
|
|
3487
|
+
return `
|
|
3488
|
+
<div class="d-flex gap-1 align-items-center">
|
|
3489
|
+
<select class="form-select form-select-sm cell-input">
|
|
3490
|
+
${optionsHtml}
|
|
3491
|
+
</select>
|
|
3492
|
+
<button type="button" class="btn btn-sm btn-success cell-save" title="Save">
|
|
3493
|
+
<i class="bi bi-check"></i>
|
|
3494
|
+
</button>
|
|
3495
|
+
<button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">
|
|
3496
|
+
<i class="bi bi-x"></i>
|
|
3497
|
+
</button>
|
|
3498
|
+
</div>
|
|
3499
|
+
`;
|
|
3500
|
+
}
|
|
3501
|
+
/**
|
|
3502
|
+
* Create switch/checkbox editor
|
|
3503
|
+
*/
|
|
3504
|
+
createSwitchEditor(options, currentValue) {
|
|
3505
|
+
const checked = currentValue ? "checked" : "";
|
|
3506
|
+
const switchType = options.type === "switch" ? "form-switch" : "";
|
|
3507
|
+
return `
|
|
3508
|
+
<div class="d-flex gap-2 align-items-center">
|
|
3509
|
+
<div class="form-check ${switchType}">
|
|
3510
|
+
<input class="form-check-input cell-input" type="checkbox" ${checked}>
|
|
3511
|
+
</div>
|
|
3512
|
+
<div class="d-flex gap-1">
|
|
3513
|
+
<button type="button" class="btn btn-sm btn-success cell-save" title="Save">
|
|
3514
|
+
<i class="bi bi-check"></i>
|
|
3515
|
+
</button>
|
|
3516
|
+
<button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">
|
|
3517
|
+
<i class="bi bi-x"></i>
|
|
3518
|
+
</button>
|
|
3519
|
+
</div>
|
|
3520
|
+
</div>
|
|
3521
|
+
`;
|
|
3522
|
+
}
|
|
3523
|
+
/**
|
|
3524
|
+
* Setup event listeners for cell editor
|
|
3525
|
+
*/
|
|
3526
|
+
setupEditorEvents(editorContainer, columnKey, column) {
|
|
3527
|
+
const input = editorContainer.querySelector(".cell-input");
|
|
3528
|
+
const saveBtn = editorContainer.querySelector(".cell-save");
|
|
3529
|
+
const cancelBtn = editorContainer.querySelector(".cell-cancel");
|
|
3530
|
+
if (input && (input.type === "text" || input.type === "email" || input.type === "number")) {
|
|
3531
|
+
input.addEventListener("keydown", (e) => {
|
|
3532
|
+
if (e.key === "Enter") {
|
|
3533
|
+
e.preventDefault();
|
|
3534
|
+
this.saveCellEdit(editorContainer, columnKey, column);
|
|
3535
|
+
} else if (e.key === "Escape") {
|
|
3536
|
+
e.preventDefault();
|
|
3537
|
+
this.cancelCellEdit(editorContainer, columnKey);
|
|
3538
|
+
}
|
|
3539
|
+
});
|
|
3540
|
+
}
|
|
3541
|
+
if (input && (input.type === "checkbox" || input.tagName === "SELECT") && column.autoSave !== false) {
|
|
3542
|
+
input.addEventListener("change", () => {
|
|
3543
|
+
this.saveCellEdit(editorContainer, columnKey, column);
|
|
3544
|
+
});
|
|
3545
|
+
}
|
|
3546
|
+
saveBtn?.addEventListener("click", () => {
|
|
3547
|
+
this.saveCellEdit(editorContainer, columnKey, column);
|
|
3548
|
+
});
|
|
3549
|
+
cancelBtn?.addEventListener("click", () => {
|
|
3550
|
+
this.cancelCellEdit(editorContainer, columnKey);
|
|
3551
|
+
});
|
|
3552
|
+
}
|
|
3553
|
+
/**
|
|
3554
|
+
* Save cell edit
|
|
3555
|
+
*/
|
|
3556
|
+
async saveCellEdit(editorContainer, columnKey, column) {
|
|
3557
|
+
const input = editorContainer.querySelector(".cell-input");
|
|
3558
|
+
if (!input) return;
|
|
3559
|
+
let newValue;
|
|
3560
|
+
if (input.type === "checkbox") {
|
|
3561
|
+
newValue = input.checked;
|
|
3562
|
+
} else if (input.tagName === "SELECT") {
|
|
3563
|
+
newValue = input.value;
|
|
3564
|
+
} else {
|
|
3565
|
+
newValue = input.value;
|
|
3566
|
+
}
|
|
3567
|
+
const oldValue = this.model.get ? this.model.get(columnKey) : this.model[columnKey];
|
|
3568
|
+
try {
|
|
3569
|
+
if (this.model.save) {
|
|
3570
|
+
await this.model.save({ [columnKey]: newValue });
|
|
3571
|
+
} else {
|
|
3572
|
+
this.model[columnKey] = newValue;
|
|
3573
|
+
}
|
|
3574
|
+
this.exitEditMode(editorContainer, columnKey, newValue);
|
|
3575
|
+
this.emit("cell:save", {
|
|
3576
|
+
row: this,
|
|
3577
|
+
model: this.model,
|
|
3578
|
+
column: columnKey,
|
|
3579
|
+
oldValue,
|
|
3580
|
+
newValue
|
|
3581
|
+
});
|
|
3582
|
+
} catch (error) {
|
|
3583
|
+
console.error("Failed to save cell edit:", error);
|
|
3584
|
+
this.emit("cell:save:error", {
|
|
3585
|
+
row: this,
|
|
3586
|
+
model: this.model,
|
|
3587
|
+
column: columnKey,
|
|
3588
|
+
oldValue,
|
|
3589
|
+
newValue,
|
|
3590
|
+
error
|
|
3591
|
+
});
|
|
3592
|
+
editorContainer.classList.add("saving-error");
|
|
3593
|
+
setTimeout(() => editorContainer.classList.remove("saving-error"), 3e3);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
/**
|
|
3597
|
+
* Cancel cell edit
|
|
3598
|
+
*/
|
|
3599
|
+
cancelCellEdit(editorContainer, columnKey) {
|
|
3600
|
+
const originalContent = editorContainer.dataset.originalContent;
|
|
3601
|
+
this.exitEditMode(editorContainer, columnKey, null, originalContent);
|
|
3602
|
+
this.emit("cell:cancel", {
|
|
3603
|
+
row: this,
|
|
3604
|
+
model: this.model,
|
|
3605
|
+
column: columnKey
|
|
3606
|
+
});
|
|
3607
|
+
}
|
|
3608
|
+
/**
|
|
3609
|
+
* Exit edit mode and restore content
|
|
3610
|
+
*/
|
|
3611
|
+
exitEditMode(editorContainer, columnKey, newValue = null, originalContent = null) {
|
|
3612
|
+
const cellElement = editorContainer.closest("td");
|
|
3613
|
+
const contentSpan = cellElement.querySelector(".cell-content");
|
|
3614
|
+
if (contentSpan) {
|
|
3615
|
+
if (newValue !== null) {
|
|
3616
|
+
const column = this.columns.find((col) => col.key === columnKey);
|
|
3617
|
+
let displayValue = newValue;
|
|
3618
|
+
if (column && column.formatter && typeof column.formatter === "string") {
|
|
3619
|
+
displayValue = dataFormatter.pipe(newValue, column.formatter);
|
|
3620
|
+
}
|
|
3621
|
+
contentSpan.innerHTML = this.escapeHtml(displayValue);
|
|
3622
|
+
} else if (originalContent) {
|
|
3623
|
+
contentSpan.innerHTML = originalContent;
|
|
3624
|
+
}
|
|
3625
|
+
contentSpan.style.display = "";
|
|
3626
|
+
}
|
|
3627
|
+
editorContainer.remove();
|
|
3628
|
+
this.editingCells.delete(columnKey);
|
|
3629
|
+
}
|
|
3630
|
+
/**
|
|
3631
|
+
* Escape HTML for safe display
|
|
3632
|
+
*/
|
|
3633
|
+
escapeHtml(text) {
|
|
3634
|
+
if (text === null || text === void 0) return "";
|
|
3635
|
+
const div = document.createElement("div");
|
|
3636
|
+
div.textContent = text;
|
|
3637
|
+
return div.innerHTML;
|
|
3638
|
+
}
|
|
3361
3639
|
/**
|
|
3362
3640
|
* Override select to handle table-specific selection UI
|
|
3363
3641
|
*/
|
|
@@ -3391,6 +3669,7 @@ class TableView extends ListView {
|
|
|
3391
3669
|
...options
|
|
3392
3670
|
};
|
|
3393
3671
|
super(tableOptions);
|
|
3672
|
+
this.isFullscreen = false;
|
|
3394
3673
|
this.columns = options.columns || [];
|
|
3395
3674
|
this.actions = options.actions || null;
|
|
3396
3675
|
this.contextMenu = options.contextMenu || null;
|
|
@@ -3434,7 +3713,20 @@ class TableView extends ListView {
|
|
|
3434
3713
|
this.searchPlaceholder = options.searchPlaceholder || "Search...";
|
|
3435
3714
|
this.initializeColumns();
|
|
3436
3715
|
this.extractColumnFilters();
|
|
3716
|
+
this.footerTotalColumns = this.columns.filter((col) => col.footer_total === true);
|
|
3717
|
+
this.hasFooterTotals = this.footerTotalColumns.length > 0;
|
|
3437
3718
|
this.template = this.buildTableTemplate();
|
|
3719
|
+
this.setupCollectionListeners();
|
|
3720
|
+
}
|
|
3721
|
+
/**
|
|
3722
|
+
* Setup collection event listeners for totals updates
|
|
3723
|
+
*/
|
|
3724
|
+
setupCollectionListeners() {
|
|
3725
|
+
if (this.hasFooterTotals && this.collection) {
|
|
3726
|
+
this.collection.on("reset add remove change", () => {
|
|
3727
|
+
this.updateFooterTotals();
|
|
3728
|
+
});
|
|
3729
|
+
}
|
|
3438
3730
|
}
|
|
3439
3731
|
/**
|
|
3440
3732
|
* Initialize column configuration
|
|
@@ -3463,6 +3755,80 @@ class TableView extends ListView {
|
|
|
3463
3755
|
}
|
|
3464
3756
|
return `d-none d-${visibility}-table-cell`;
|
|
3465
3757
|
}
|
|
3758
|
+
/**
|
|
3759
|
+
* Extract column key and formatter from combined key (e.g., "sales_amount|currency")
|
|
3760
|
+
*/
|
|
3761
|
+
parseColumnKey(key) {
|
|
3762
|
+
const parts = key.split("|");
|
|
3763
|
+
return {
|
|
3764
|
+
fieldKey: parts[0],
|
|
3765
|
+
formatter: parts[1] || null
|
|
3766
|
+
};
|
|
3767
|
+
}
|
|
3768
|
+
/**
|
|
3769
|
+
* Update footer totals in the DOM without full re-render
|
|
3770
|
+
*/
|
|
3771
|
+
updateFooterTotals() {
|
|
3772
|
+
if (!this.hasFooterTotals || !this.element) return;
|
|
3773
|
+
const totals = this.calculateFooterTotals();
|
|
3774
|
+
console.log("Updating footer totals in DOM:", totals);
|
|
3775
|
+
let totalColumnIndex = 0;
|
|
3776
|
+
this.columns.forEach((column) => {
|
|
3777
|
+
if (column.footer_total) {
|
|
3778
|
+
const safeKey = `col_${totalColumnIndex}`;
|
|
3779
|
+
const cell = this.element.querySelector(`[data-total-column="${safeKey}"]`);
|
|
3780
|
+
if (cell && totals[safeKey]) {
|
|
3781
|
+
const formatter = this.parseColumnKey(column.key).formatter || column.formatter;
|
|
3782
|
+
let displayValue;
|
|
3783
|
+
if (formatter && typeof formatter === "string") {
|
|
3784
|
+
displayValue = this.formatValue(totals[safeKey].value, formatter);
|
|
3785
|
+
} else {
|
|
3786
|
+
displayValue = totals[safeKey].value;
|
|
3787
|
+
}
|
|
3788
|
+
cell.textContent = displayValue;
|
|
3789
|
+
}
|
|
3790
|
+
totalColumnIndex++;
|
|
3791
|
+
}
|
|
3792
|
+
});
|
|
3793
|
+
}
|
|
3794
|
+
/**
|
|
3795
|
+
* Format a value using DataFormatter
|
|
3796
|
+
*/
|
|
3797
|
+
formatValue(value, formatter) {
|
|
3798
|
+
try {
|
|
3799
|
+
return dataFormatter.pipe(value, formatter);
|
|
3800
|
+
} catch (e) {
|
|
3801
|
+
console.warn("Error formatting value:", e);
|
|
3802
|
+
return value;
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
/**
|
|
3806
|
+
* Calculate totals for footer columns
|
|
3807
|
+
*/
|
|
3808
|
+
calculateFooterTotals() {
|
|
3809
|
+
if (!this.hasFooterTotals || !this.collection || this.collection.length === 0) {
|
|
3810
|
+
return {};
|
|
3811
|
+
}
|
|
3812
|
+
const totals = {};
|
|
3813
|
+
this.footerTotalColumns.forEach((column, totalColumnIndex) => {
|
|
3814
|
+
const { fieldKey, formatter } = this.parseColumnKey(column.key);
|
|
3815
|
+
let sum = 0;
|
|
3816
|
+
this.collection.forEach((model) => {
|
|
3817
|
+
const value = model.get ? model.get(fieldKey) : model[fieldKey];
|
|
3818
|
+
const numValue = parseFloat(value) || 0;
|
|
3819
|
+
sum += numValue;
|
|
3820
|
+
});
|
|
3821
|
+
console.log(`Footer total for ${column.key}: ${sum} (from ${this.collection.length} items)`);
|
|
3822
|
+
const safeKey = `col_${totalColumnIndex}`;
|
|
3823
|
+
totals[safeKey] = {
|
|
3824
|
+
value: sum,
|
|
3825
|
+
formatter: formatter || column.formatter,
|
|
3826
|
+
fieldKey,
|
|
3827
|
+
originalKey: column.key
|
|
3828
|
+
};
|
|
3829
|
+
});
|
|
3830
|
+
return totals;
|
|
3831
|
+
}
|
|
3466
3832
|
/**
|
|
3467
3833
|
* Extract filters from column configuration
|
|
3468
3834
|
*/
|
|
@@ -3474,14 +3840,6 @@ class TableView extends ListView {
|
|
|
3474
3840
|
}
|
|
3475
3841
|
});
|
|
3476
3842
|
}
|
|
3477
|
-
/**
|
|
3478
|
-
* Override getTemplateData to provide dynamic values for Mustache
|
|
3479
|
-
*/
|
|
3480
|
-
getTemplateData() {
|
|
3481
|
-
const data = super.getTemplateData();
|
|
3482
|
-
data.searchValue = this.getActiveFilters().search || "";
|
|
3483
|
-
return data;
|
|
3484
|
-
}
|
|
3485
3843
|
isSelectable() {
|
|
3486
3844
|
return this.batchActions && this.batchActions.length > 0 && this.selectionMode == "multiple";
|
|
3487
3845
|
}
|
|
@@ -3514,6 +3872,7 @@ class TableView extends ListView {
|
|
|
3514
3872
|
<table class="${this.buildTableClasses()}">
|
|
3515
3873
|
${this.buildTableHeaderTemplate()}
|
|
3516
3874
|
<tbody data-container="items"></tbody>
|
|
3875
|
+
${this.hasFooterTotals ? this.buildTableFooterTemplate() : ""}
|
|
3517
3876
|
</table>
|
|
3518
3877
|
{{/isEmpty}}
|
|
3519
3878
|
{{/loading}}
|
|
@@ -3568,6 +3927,15 @@ class TableView extends ListView {
|
|
|
3568
3927
|
<i class="bi bi-arrow-clockwise"></i>
|
|
3569
3928
|
</button>
|
|
3570
3929
|
`);
|
|
3930
|
+
if (this.isFullscreenSupported()) {
|
|
3931
|
+
buttons.push(`
|
|
3932
|
+
<button class="btn btn-sm btn-outline-secondary btn-fullscreen"
|
|
3933
|
+
data-action="toggle-fullscreen"
|
|
3934
|
+
title="Toggle Fullscreen">
|
|
3935
|
+
<i class="bi bi-fullscreen"></i>
|
|
3936
|
+
</button>
|
|
3937
|
+
`);
|
|
3938
|
+
}
|
|
3571
3939
|
if (this.options.showAdd) {
|
|
3572
3940
|
buttons.push(`
|
|
3573
3941
|
<button class="btn btn-sm btn-success btn-add"
|
|
@@ -3850,6 +4218,47 @@ class TableView extends ListView {
|
|
|
3850
4218
|
</thead>
|
|
3851
4219
|
`;
|
|
3852
4220
|
}
|
|
4221
|
+
/**
|
|
4222
|
+
* Build table footer template with totals
|
|
4223
|
+
*/
|
|
4224
|
+
buildTableFooterTemplate() {
|
|
4225
|
+
let footerCells = "";
|
|
4226
|
+
if (this.isSelectable()) {
|
|
4227
|
+
footerCells += "<td></td>";
|
|
4228
|
+
}
|
|
4229
|
+
let totalColumnIndex = 0;
|
|
4230
|
+
this.columns.forEach((column, index) => {
|
|
4231
|
+
const responsiveClasses = this.getResponsiveClasses(column.visibility);
|
|
4232
|
+
if (column.footer_total) {
|
|
4233
|
+
const safeKey = `col_${totalColumnIndex}`;
|
|
4234
|
+
const formatter = this.parseColumnKey(column.key).formatter || column.formatter;
|
|
4235
|
+
let cellContent;
|
|
4236
|
+
if (formatter && typeof formatter === "string") {
|
|
4237
|
+
cellContent = `{{{footerTotals.${safeKey}.value|${formatter}}}}`;
|
|
4238
|
+
} else {
|
|
4239
|
+
cellContent = `{{footerTotals.${safeKey}.value}}`;
|
|
4240
|
+
}
|
|
4241
|
+
footerCells += `<td class="table-footer-total ${responsiveClasses}" data-total-column="${safeKey}">${cellContent}</td>`;
|
|
4242
|
+
totalColumnIndex++;
|
|
4243
|
+
} else if (index === 0) {
|
|
4244
|
+
footerCells += `<td class="table-footer-label ${responsiveClasses}"><strong>Totals</strong></td>`;
|
|
4245
|
+
} else {
|
|
4246
|
+
footerCells += `<td class="${responsiveClasses}"></td>`;
|
|
4247
|
+
}
|
|
4248
|
+
});
|
|
4249
|
+
if (this.actions) {
|
|
4250
|
+
footerCells += "<td></td>";
|
|
4251
|
+
} else if (this.contextMenu) {
|
|
4252
|
+
footerCells += "<td></td>";
|
|
4253
|
+
}
|
|
4254
|
+
return `
|
|
4255
|
+
<tfoot>
|
|
4256
|
+
<tr class="table-totals-row">
|
|
4257
|
+
${footerCells}
|
|
4258
|
+
</tr>
|
|
4259
|
+
</tfoot>
|
|
4260
|
+
`;
|
|
4261
|
+
}
|
|
3853
4262
|
/**
|
|
3854
4263
|
* Build batch actions panel
|
|
3855
4264
|
*/
|
|
@@ -3986,6 +4395,9 @@ class TableView extends ListView {
|
|
|
3986
4395
|
itemView.on("row:view", this._onRowView.bind(this));
|
|
3987
4396
|
itemView.on("row:edit", this._onRowEdit.bind(this));
|
|
3988
4397
|
itemView.on("row:delete", this._onRowDelete.bind(this));
|
|
4398
|
+
itemView.on("cell:edit", this._onCellEdit.bind(this));
|
|
4399
|
+
itemView.on("cell:save", this._onCellSave.bind(this));
|
|
4400
|
+
itemView.on("cell:cancel", this._onCellCancel.bind(this));
|
|
3989
4401
|
return itemView;
|
|
3990
4402
|
}
|
|
3991
4403
|
/**
|
|
@@ -4165,6 +4577,140 @@ class TableView extends ListView {
|
|
|
4165
4577
|
this.collection.remove(event.model);
|
|
4166
4578
|
}
|
|
4167
4579
|
}
|
|
4580
|
+
/**
|
|
4581
|
+
* Handle cell edit event
|
|
4582
|
+
*/
|
|
4583
|
+
_onCellEdit(event) {
|
|
4584
|
+
this.emit("cell:edit", event);
|
|
4585
|
+
}
|
|
4586
|
+
/**
|
|
4587
|
+
* Handle cell save event
|
|
4588
|
+
*/
|
|
4589
|
+
async _onCellSave(event) {
|
|
4590
|
+
this.emit("cell:save", event);
|
|
4591
|
+
}
|
|
4592
|
+
/**
|
|
4593
|
+
* Handle cell cancel event
|
|
4594
|
+
*/
|
|
4595
|
+
_onCellCancel(event) {
|
|
4596
|
+
this.emit("cell:cancel", event);
|
|
4597
|
+
}
|
|
4598
|
+
/**
|
|
4599
|
+
* Check if fullscreen is supported by the browser
|
|
4600
|
+
*/
|
|
4601
|
+
isFullscreenSupported() {
|
|
4602
|
+
return !!(document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled);
|
|
4603
|
+
}
|
|
4604
|
+
/**
|
|
4605
|
+
* Handle toggle fullscreen action
|
|
4606
|
+
*/
|
|
4607
|
+
async onActionToggleFullscreen(event, element) {
|
|
4608
|
+
if (this.isFullscreen) {
|
|
4609
|
+
await this.exitFullscreen();
|
|
4610
|
+
} else {
|
|
4611
|
+
await this.enterFullscreen();
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
/**
|
|
4615
|
+
* Enter fullscreen mode
|
|
4616
|
+
*/
|
|
4617
|
+
async enterFullscreen() {
|
|
4618
|
+
try {
|
|
4619
|
+
if (this.element.requestFullscreen) {
|
|
4620
|
+
await this.element.requestFullscreen();
|
|
4621
|
+
} else if (this.element.mozRequestFullScreen) {
|
|
4622
|
+
await this.element.mozRequestFullScreen();
|
|
4623
|
+
} else if (this.element.webkitRequestFullscreen) {
|
|
4624
|
+
await this.element.webkitRequestFullscreen();
|
|
4625
|
+
} else if (this.element.msRequestFullscreen) {
|
|
4626
|
+
await this.element.msRequestFullscreen();
|
|
4627
|
+
}
|
|
4628
|
+
this.isFullscreen = true;
|
|
4629
|
+
this.element.classList.add("table-fullscreen");
|
|
4630
|
+
this.updateFullscreenButton();
|
|
4631
|
+
this.setupFullscreenListeners();
|
|
4632
|
+
this.emit("table:fullscreen:enter");
|
|
4633
|
+
} catch (error) {
|
|
4634
|
+
console.warn("Could not enter fullscreen:", error);
|
|
4635
|
+
}
|
|
4636
|
+
}
|
|
4637
|
+
/**
|
|
4638
|
+
* Exit fullscreen mode
|
|
4639
|
+
*/
|
|
4640
|
+
async exitFullscreen() {
|
|
4641
|
+
try {
|
|
4642
|
+
if (document.exitFullscreen) {
|
|
4643
|
+
await document.exitFullscreen();
|
|
4644
|
+
} else if (document.mozCancelFullScreen) {
|
|
4645
|
+
await document.mozCancelFullScreen();
|
|
4646
|
+
} else if (document.webkitExitFullscreen) {
|
|
4647
|
+
await document.webkitExitFullscreen();
|
|
4648
|
+
} else if (document.msExitFullscreen) {
|
|
4649
|
+
await document.msExitFullscreen();
|
|
4650
|
+
}
|
|
4651
|
+
this.isFullscreen = false;
|
|
4652
|
+
this.element.classList.remove("table-fullscreen");
|
|
4653
|
+
this.updateFullscreenButton();
|
|
4654
|
+
this.emit("table:fullscreen:exit");
|
|
4655
|
+
} catch (error) {
|
|
4656
|
+
console.warn("Could not exit fullscreen:", error);
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
/**
|
|
4660
|
+
* Update fullscreen button icon and title
|
|
4661
|
+
*/
|
|
4662
|
+
updateFullscreenButton() {
|
|
4663
|
+
const button = this.element?.querySelector(".btn-fullscreen");
|
|
4664
|
+
const icon = button?.querySelector("i");
|
|
4665
|
+
if (button && icon) {
|
|
4666
|
+
if (this.isFullscreen) {
|
|
4667
|
+
icon.className = "bi bi-fullscreen-exit";
|
|
4668
|
+
button.title = "Exit Fullscreen";
|
|
4669
|
+
} else {
|
|
4670
|
+
icon.className = "bi bi-fullscreen";
|
|
4671
|
+
button.title = "Enter Fullscreen";
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
/**
|
|
4676
|
+
* Setup fullscreen event listeners
|
|
4677
|
+
*/
|
|
4678
|
+
setupFullscreenListeners() {
|
|
4679
|
+
if (this._fullscreenHandler) return;
|
|
4680
|
+
const handleFullscreenChange = () => {
|
|
4681
|
+
const isCurrentlyFullscreen = !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement);
|
|
4682
|
+
if (!isCurrentlyFullscreen && this.isFullscreen) {
|
|
4683
|
+
this.isFullscreen = false;
|
|
4684
|
+
this.element.classList.remove("table-fullscreen");
|
|
4685
|
+
this.updateFullscreenButton();
|
|
4686
|
+
this.emit("table:fullscreen:exit");
|
|
4687
|
+
}
|
|
4688
|
+
};
|
|
4689
|
+
document.addEventListener("fullscreenchange", handleFullscreenChange);
|
|
4690
|
+
document.addEventListener("mozfullscreenchange", handleFullscreenChange);
|
|
4691
|
+
document.addEventListener("webkitfullscreenchange", handleFullscreenChange);
|
|
4692
|
+
document.addEventListener("msfullscreenchange", handleFullscreenChange);
|
|
4693
|
+
this._fullscreenHandler = handleFullscreenChange;
|
|
4694
|
+
}
|
|
4695
|
+
/**
|
|
4696
|
+
* Cleanup fullscreen listeners
|
|
4697
|
+
*/
|
|
4698
|
+
cleanupFullscreenListeners() {
|
|
4699
|
+
if (this._fullscreenHandler) {
|
|
4700
|
+
document.removeEventListener("fullscreenchange", this._fullscreenHandler);
|
|
4701
|
+
document.removeEventListener("mozfullscreenchange", this._fullscreenHandler);
|
|
4702
|
+
document.removeEventListener("webkitfullscreenchange", this._fullscreenHandler);
|
|
4703
|
+
document.removeEventListener("msfullscreenchange", this._fullscreenHandler);
|
|
4704
|
+
this._fullscreenHandler = null;
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
/**
|
|
4708
|
+
* Override destroy to cleanup fullscreen listeners
|
|
4709
|
+
*/
|
|
4710
|
+
destroy() {
|
|
4711
|
+
this.cleanupFullscreenListeners();
|
|
4712
|
+
super.destroy();
|
|
4713
|
+
}
|
|
4168
4714
|
/**
|
|
4169
4715
|
* Handle refresh action
|
|
4170
4716
|
*/
|
|
@@ -4410,11 +4956,23 @@ class TableView extends ListView {
|
|
|
4410
4956
|
}
|
|
4411
4957
|
this.updateBatchActionsPanel();
|
|
4412
4958
|
}
|
|
4959
|
+
/**
|
|
4960
|
+
* Override render to set data properties before rendering
|
|
4961
|
+
*/
|
|
4962
|
+
async render(force, container) {
|
|
4963
|
+
this.searchValue = this.getActiveFilters().search || "";
|
|
4964
|
+
this.footerTotals = this.calculateFooterTotals();
|
|
4965
|
+
console.log("Setting footerTotals before render:", this.footerTotals);
|
|
4966
|
+
return super.render(force, container);
|
|
4967
|
+
}
|
|
4413
4968
|
/**
|
|
4414
4969
|
* Override onAfterRender to update pagination info
|
|
4415
4970
|
*/
|
|
4416
4971
|
async onAfterRender() {
|
|
4417
4972
|
await super.onAfterRender();
|
|
4973
|
+
if (this.hasFooterTotals) {
|
|
4974
|
+
this.updateFooterTotals();
|
|
4975
|
+
}
|
|
4418
4976
|
if (this.paginated && this.collection) {
|
|
4419
4977
|
const total = this.collection.meta?.count || this.collection.length();
|
|
4420
4978
|
const start = this.collection.params?.start || 0;
|
|
@@ -5962,4 +6520,4 @@ export {
|
|
|
5962
6520
|
IncidentEventList as y,
|
|
5963
6521
|
IncidentEventForms as z
|
|
5964
6522
|
};
|
|
5965
|
-
//# sourceMappingURL=FilePreviewView-
|
|
6523
|
+
//# sourceMappingURL=FilePreviewView-CCngMe-J.js.map
|