web-mojo 2.1.294 → 2.1.337
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/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +20 -25
- 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-DR9qV_8Z.js → ContextMenu-98dg4R2t.js} +106 -29
- package/dist/chunks/ContextMenu-98dg4R2t.js.map +1 -0
- package/dist/chunks/ContextMenu-Dl5BF8QJ.js +3 -0
- package/dist/chunks/ContextMenu-Dl5BF8QJ.js.map +1 -0
- package/dist/chunks/{DataView-CB3fkiWE.js → DataView-CTF7BN4B.js} +2 -2
- package/dist/chunks/{DataView-CB3fkiWE.js.map → DataView-CTF7BN4B.js.map} +1 -1
- package/dist/chunks/{DataView-DX2XDuNt.js → DataView-DZ2kF47b.js} +2 -2
- package/dist/chunks/{DataView-DX2XDuNt.js.map → DataView-DZ2kF47b.js.map} +1 -1
- package/dist/chunks/{Dialog-BUZEYiTL.js → Dialog-1GfD6-3r.js} +5 -5
- package/dist/chunks/{Dialog-BUZEYiTL.js.map → Dialog-1GfD6-3r.js.map} +1 -1
- package/dist/chunks/{Dialog-DCpfJ63g.js → Dialog-BgaP4o1D.js} +2 -2
- package/dist/chunks/{Dialog-DCpfJ63g.js.map → Dialog-BgaP4o1D.js.map} +1 -1
- package/dist/chunks/{FilePreviewView-A32KUENg.js → FilePreviewView-CiAkm9fN.js} +6 -6
- package/dist/chunks/{FilePreviewView-A32KUENg.js.map → FilePreviewView-CiAkm9fN.js.map} +1 -1
- package/dist/chunks/{FilePreviewView-DWLPUdX6.js → FilePreviewView-QlITXzbc.js} +2 -2
- package/dist/chunks/{FilePreviewView-DWLPUdX6.js.map → FilePreviewView-QlITXzbc.js.map} +1 -1
- package/dist/chunks/{FormView-GJqUOJqR.js → FormView-CglUjQ3E.js} +316 -34
- package/dist/chunks/FormView-CglUjQ3E.js.map +1 -0
- package/dist/chunks/FormView-eLjHQ4Yy.js +2 -0
- package/dist/chunks/FormView-eLjHQ4Yy.js.map +1 -0
- package/dist/chunks/{MetricsChart-BMtSNVSC.js → MetricsChart-Dr4iyfAI.js} +3 -3
- package/dist/chunks/{MetricsChart-BMtSNVSC.js.map → MetricsChart-Dr4iyfAI.js.map} +1 -1
- package/dist/chunks/{MetricsChart-khF2NtDH.js → MetricsChart-tsjbYCBw.js} +2 -2
- package/dist/chunks/{MetricsChart-khF2NtDH.js.map → MetricsChart-tsjbYCBw.js.map} +1 -1
- package/dist/chunks/{PDFViewer-DG_JyAZU.js → PDFViewer-7CGUMEOl.js} +2 -2
- package/dist/chunks/{PDFViewer-DG_JyAZU.js.map → PDFViewer-7CGUMEOl.js.map} +1 -1
- package/dist/chunks/{PDFViewer-DoHxXjiP.js → PDFViewer-Cyn2wZdi.js} +3 -3
- package/dist/chunks/{PDFViewer-DoHxXjiP.js.map → PDFViewer-Cyn2wZdi.js.map} +1 -1
- package/dist/chunks/{Page-uLnlAlwn.js → Page-BQkfWPCI.js} +2 -2
- package/dist/chunks/{Page-uLnlAlwn.js.map → Page-BQkfWPCI.js.map} +1 -1
- package/dist/chunks/{Page-CkziZoUi.js → Page-BV2Ym7gu.js} +2 -2
- package/dist/chunks/{Page-CkziZoUi.js.map → Page-BV2Ym7gu.js.map} +1 -1
- package/dist/chunks/{TopNav-MEetwvBb.js → TopNav-BqAMFlyI.js} +2 -2
- package/dist/chunks/{TopNav-MEetwvBb.js.map → TopNav-BqAMFlyI.js.map} +1 -1
- package/dist/chunks/{TopNav-BuFxvfyc.js → TopNav-a99nWNlO.js} +2 -2
- package/dist/chunks/{TopNav-BuFxvfyc.js.map → TopNav-a99nWNlO.js.map} +1 -1
- package/dist/chunks/WebApp-BlE_bW7n.js +2 -0
- package/dist/chunks/WebApp-BlE_bW7n.js.map +1 -0
- package/dist/chunks/{WebApp-CKLwTH9q.js → WebApp-ekvGNIXB.js} +15 -12
- package/dist/chunks/WebApp-ekvGNIXB.js.map +1 -0
- package/dist/core.css +114 -0
- package/dist/css/web-mojo.css +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +5 -5
- 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/package.json +1 -1
- package/dist/chunks/ContextMenu-DLDSJEIk.js +0 -3
- package/dist/chunks/ContextMenu-DLDSJEIk.js.map +0 -1
- package/dist/chunks/ContextMenu-DR9qV_8Z.js.map +0 -1
- package/dist/chunks/FormView-CYJpCFJ2.js +0 -2
- package/dist/chunks/FormView-CYJpCFJ2.js.map +0 -1
- package/dist/chunks/FormView-GJqUOJqR.js.map +0 -1
- package/dist/chunks/WebApp-CKLwTH9q.js.map +0 -1
- package/dist/chunks/WebApp-D0A6nJtm.js +0 -2
- package/dist/chunks/WebApp-D0A6nJtm.js.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { M as Mustache, h as MOJOUtils, V as View } from "./WebApp-
|
|
1
|
+
import { M as Mustache, h as MOJOUtils, V as View } from "./WebApp-ekvGNIXB.js";
|
|
2
2
|
class FormBuilder {
|
|
3
3
|
constructor(config = {}) {
|
|
4
4
|
this.fields = config.fields || [];
|
|
5
|
+
this.structureOnly = config.structureOnly || false;
|
|
5
6
|
this.fields.forEach((field) => {
|
|
6
7
|
if (field.cols && !field.columns) {
|
|
7
8
|
field.columns = field.cols;
|
|
@@ -1473,6 +1474,9 @@ class FormBuilder {
|
|
|
1473
1474
|
* @returns {*} Field value
|
|
1474
1475
|
*/
|
|
1475
1476
|
getFieldValue(name) {
|
|
1477
|
+
if (this.structureOnly) {
|
|
1478
|
+
return "";
|
|
1479
|
+
}
|
|
1476
1480
|
return MOJOUtils.getContextData(this.data, name);
|
|
1477
1481
|
}
|
|
1478
1482
|
/**
|
|
@@ -4108,6 +4112,8 @@ class FormView extends View {
|
|
|
4108
4112
|
errors = {},
|
|
4109
4113
|
fileHandling = "base64",
|
|
4110
4114
|
// 'base64' | 'multipart'
|
|
4115
|
+
autosaveModelField = false,
|
|
4116
|
+
// Auto-save model on field changes
|
|
4111
4117
|
...viewOptions
|
|
4112
4118
|
} = options;
|
|
4113
4119
|
super({
|
|
@@ -4121,12 +4127,15 @@ class FormView extends View {
|
|
|
4121
4127
|
this.errors = errors;
|
|
4122
4128
|
this.loading = false;
|
|
4123
4129
|
this.fileHandling = fileHandling;
|
|
4130
|
+
this.autosaveModelField = autosaveModelField;
|
|
4124
4131
|
this.customComponents = /* @__PURE__ */ new Map();
|
|
4132
|
+
this.fieldStatusManagers = /* @__PURE__ */ new Map();
|
|
4125
4133
|
this.data = this.prepareFormData();
|
|
4126
4134
|
this.formConfig = formConfig || { fields: fields || [] };
|
|
4127
4135
|
this.formBuilder = new FormBuilder({
|
|
4128
4136
|
...this.formConfig,
|
|
4129
|
-
|
|
4137
|
+
structureOnly: true,
|
|
4138
|
+
// Only generate structure, FormView will control values
|
|
4130
4139
|
errors
|
|
4131
4140
|
});
|
|
4132
4141
|
}
|
|
@@ -4158,12 +4167,49 @@ class FormView extends View {
|
|
|
4158
4167
|
return this.formBuilder.buildFormHTML();
|
|
4159
4168
|
}
|
|
4160
4169
|
/**
|
|
4161
|
-
*
|
|
4170
|
+
* Called after form is rendered to populate values and initialize
|
|
4162
4171
|
*/
|
|
4163
4172
|
async onAfterRender() {
|
|
4164
4173
|
await super.onAfterRender();
|
|
4174
|
+
this.data = this.prepareFormData();
|
|
4175
|
+
this.populateFormValues();
|
|
4176
|
+
this.initializeFormComponents();
|
|
4177
|
+
}
|
|
4178
|
+
/**
|
|
4179
|
+
* Populate all form fields with current data values
|
|
4180
|
+
*/
|
|
4181
|
+
populateFormValues() {
|
|
4182
|
+
if (!this.element || !this.formConfig?.fields) return;
|
|
4183
|
+
this.formConfig.fields.forEach((field) => {
|
|
4184
|
+
if (field.type === "group" && field.fields) {
|
|
4185
|
+
field.fields.forEach((groupField) => {
|
|
4186
|
+
this.populateFieldValue(groupField);
|
|
4187
|
+
});
|
|
4188
|
+
} else {
|
|
4189
|
+
this.populateFieldValue(field);
|
|
4190
|
+
}
|
|
4191
|
+
});
|
|
4192
|
+
}
|
|
4193
|
+
/**
|
|
4194
|
+
* Populate a single field with its value from data
|
|
4195
|
+
*/
|
|
4196
|
+
populateFieldValue(fieldConfig) {
|
|
4197
|
+
if (!fieldConfig.name || !this.element) return;
|
|
4198
|
+
const fieldElement = this.element.querySelector(`[name="${fieldConfig.name}"]`);
|
|
4199
|
+
if (!fieldElement) return;
|
|
4200
|
+
const value = MOJOUtils.getContextData(this.data, fieldConfig.name);
|
|
4201
|
+
this.setFieldValue(fieldElement, fieldConfig, value);
|
|
4202
|
+
}
|
|
4203
|
+
/**
|
|
4204
|
+
* Initialize form components after rendering
|
|
4205
|
+
*/
|
|
4206
|
+
initializeFormComponents() {
|
|
4165
4207
|
this.initializeImageFields();
|
|
4166
4208
|
this.initializeCustomComponents();
|
|
4209
|
+
this.initializeTagInputs();
|
|
4210
|
+
this.initializeCollectionSelects();
|
|
4211
|
+
this.initializeDatePickers();
|
|
4212
|
+
this.initializeDateRangePickers();
|
|
4167
4213
|
this.initializePasswordFields();
|
|
4168
4214
|
}
|
|
4169
4215
|
/**
|
|
@@ -4317,24 +4363,59 @@ class FormView extends View {
|
|
|
4317
4363
|
*/
|
|
4318
4364
|
handleFieldChange(fieldName, value) {
|
|
4319
4365
|
this.data[fieldName] = value;
|
|
4320
|
-
if (this.
|
|
4366
|
+
if (this.autosaveModelField && this.model) {
|
|
4367
|
+
this.handleFieldSave(fieldName, value);
|
|
4368
|
+
} else if (this.model && this.options.allowModelChange) {
|
|
4369
|
+
this._isFormDrivenChange = true;
|
|
4321
4370
|
this.model.set(fieldName, value);
|
|
4322
4371
|
}
|
|
4323
4372
|
this.emit("field:change", { field: fieldName, value });
|
|
4324
4373
|
}
|
|
4325
4374
|
/**
|
|
4326
|
-
*
|
|
4327
|
-
*
|
|
4375
|
+
* Handle saving individual field changes to the model
|
|
4376
|
+
* @param {string} fieldName - Name of the field being saved
|
|
4377
|
+
* @param {*} value - New value to save
|
|
4378
|
+
*/
|
|
4379
|
+
async handleFieldSave(fieldName, value) {
|
|
4380
|
+
if (!this.model) return;
|
|
4381
|
+
const statusManager = this.getFieldStatusManager(fieldName);
|
|
4382
|
+
try {
|
|
4383
|
+
statusManager.showStatus("saving");
|
|
4384
|
+
this._isFormDrivenChange = true;
|
|
4385
|
+
if (typeof this.model.save === "function") {
|
|
4386
|
+
await this.model.save({ [fieldName]: value });
|
|
4387
|
+
} else {
|
|
4388
|
+
this.model.set(fieldName, value);
|
|
4389
|
+
}
|
|
4390
|
+
statusManager.showStatus("saved");
|
|
4391
|
+
} catch (error) {
|
|
4392
|
+
console.error("Field save error:", error);
|
|
4393
|
+
statusManager.showStatus("error", { message: error.message });
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
/**
|
|
4397
|
+
* Get or create a field status manager for a specific field
|
|
4398
|
+
* @param {string} fieldName - Name of the field
|
|
4399
|
+
* @returns {FieldStatusManager} Status manager instance
|
|
4400
|
+
*/
|
|
4401
|
+
getFieldStatusManager(fieldName) {
|
|
4402
|
+
if (!this.fieldStatusManagers.has(fieldName)) {
|
|
4403
|
+
const fieldElement = this.element.querySelector(`[name="${fieldName}"]`);
|
|
4404
|
+
if (fieldElement) {
|
|
4405
|
+
const statusManager = new FieldStatusManager(fieldElement);
|
|
4406
|
+
this.fieldStatusManagers.set(fieldName, statusManager);
|
|
4407
|
+
}
|
|
4408
|
+
}
|
|
4409
|
+
return this.fieldStatusManagers.get(fieldName);
|
|
4410
|
+
}
|
|
4411
|
+
/**
|
|
4412
|
+
* Refresh form data and repopulate values
|
|
4413
|
+
* Call this when model or data changes externally
|
|
4328
4414
|
*/
|
|
4329
4415
|
refreshForm() {
|
|
4330
4416
|
this.data = this.prepareFormData();
|
|
4331
|
-
this.formBuilder = new FormBuilder({
|
|
4332
|
-
...this.formConfig,
|
|
4333
|
-
data: this.data,
|
|
4334
|
-
errors: this.errors
|
|
4335
|
-
});
|
|
4336
4417
|
if (this.element) {
|
|
4337
|
-
this.
|
|
4418
|
+
this.populateFormValues();
|
|
4338
4419
|
}
|
|
4339
4420
|
}
|
|
4340
4421
|
/**
|
|
@@ -4519,12 +4600,9 @@ class FormView extends View {
|
|
|
4519
4600
|
async onChangeValidateField(event, element) {
|
|
4520
4601
|
const fieldName = element.name;
|
|
4521
4602
|
if (fieldName) {
|
|
4603
|
+
const value = element.value;
|
|
4604
|
+
this.handleFieldChange(fieldName, value);
|
|
4522
4605
|
this.validateField(fieldName);
|
|
4523
|
-
this.emit("change", {
|
|
4524
|
-
field: fieldName,
|
|
4525
|
-
value: element.value,
|
|
4526
|
-
form: this
|
|
4527
|
-
});
|
|
4528
4606
|
}
|
|
4529
4607
|
}
|
|
4530
4608
|
/**
|
|
@@ -4533,15 +4611,11 @@ class FormView extends View {
|
|
|
4533
4611
|
async onChangeToggleSwitch(event, element) {
|
|
4534
4612
|
const fieldName = element.getAttribute("data-field");
|
|
4535
4613
|
if (fieldName) {
|
|
4536
|
-
|
|
4614
|
+
const value = element.checked;
|
|
4615
|
+
this.handleFieldChange(fieldName, value);
|
|
4537
4616
|
this.emit("switch:toggle", {
|
|
4538
4617
|
field: fieldName,
|
|
4539
|
-
checked:
|
|
4540
|
-
form: this
|
|
4541
|
-
});
|
|
4542
|
-
this.emit("change", {
|
|
4543
|
-
field: fieldName,
|
|
4544
|
-
value: element.checked,
|
|
4618
|
+
checked: value,
|
|
4545
4619
|
form: this
|
|
4546
4620
|
});
|
|
4547
4621
|
}
|
|
@@ -4637,21 +4711,21 @@ class FormView extends View {
|
|
|
4637
4711
|
* Handle range value changes
|
|
4638
4712
|
*/
|
|
4639
4713
|
async onChangeRangeChanged(event, element) {
|
|
4714
|
+
const fieldName = element.name;
|
|
4715
|
+
const value = element.value;
|
|
4640
4716
|
const targetId = element.getAttribute("data-target");
|
|
4641
4717
|
if (targetId) {
|
|
4642
4718
|
const valueDisplay = this.element.querySelector(`#${targetId}`);
|
|
4643
4719
|
if (valueDisplay) {
|
|
4644
|
-
valueDisplay.textContent =
|
|
4720
|
+
valueDisplay.textContent = value;
|
|
4645
4721
|
}
|
|
4646
4722
|
}
|
|
4723
|
+
if (fieldName) {
|
|
4724
|
+
this.handleFieldChange(fieldName, value);
|
|
4725
|
+
}
|
|
4647
4726
|
this.emit("range:changed", {
|
|
4648
|
-
field:
|
|
4649
|
-
value
|
|
4650
|
-
form: this
|
|
4651
|
-
});
|
|
4652
|
-
this.emit("change", {
|
|
4653
|
-
field: element.name,
|
|
4654
|
-
value: element.value,
|
|
4727
|
+
field: fieldName,
|
|
4728
|
+
value,
|
|
4655
4729
|
form: this
|
|
4656
4730
|
});
|
|
4657
4731
|
}
|
|
@@ -4815,9 +4889,114 @@ class FormView extends View {
|
|
|
4815
4889
|
}
|
|
4816
4890
|
}
|
|
4817
4891
|
_onModelChange() {
|
|
4892
|
+
this.data = this.prepareFormData();
|
|
4818
4893
|
if (this.isMounted()) {
|
|
4819
|
-
this.
|
|
4894
|
+
if (!this._isFormDrivenChange) {
|
|
4895
|
+
this.syncFormWithModel();
|
|
4896
|
+
}
|
|
4897
|
+
this._isFormDrivenChange = false;
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4900
|
+
/**
|
|
4901
|
+
* Sync form field values with current model data without full rebuild
|
|
4902
|
+
*/
|
|
4903
|
+
syncFormWithModel() {
|
|
4904
|
+
if (!this.model || !this.element) return;
|
|
4905
|
+
if (this.formDataMatchesModelData(this.data)) {
|
|
4906
|
+
return;
|
|
4907
|
+
}
|
|
4908
|
+
this.populateFormValues();
|
|
4909
|
+
}
|
|
4910
|
+
/**
|
|
4911
|
+
* Compare current form values with new model data
|
|
4912
|
+
* @param {Object} newModelData - New data from model
|
|
4913
|
+
* @returns {boolean} True if form and model data match
|
|
4914
|
+
*/
|
|
4915
|
+
formDataMatchesModelData(newModelData) {
|
|
4916
|
+
if (!this.formConfig?.fields || !this.element) return true;
|
|
4917
|
+
for (const field of this.formConfig.fields) {
|
|
4918
|
+
if (field.type === "group" && field.fields) {
|
|
4919
|
+
for (const groupField of field.fields) {
|
|
4920
|
+
if (!this.fieldValueMatchesModel(groupField, newModelData)) {
|
|
4921
|
+
return false;
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
} else {
|
|
4925
|
+
if (!this.fieldValueMatchesModel(field, newModelData)) {
|
|
4926
|
+
return false;
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
}
|
|
4930
|
+
return true;
|
|
4931
|
+
}
|
|
4932
|
+
/**
|
|
4933
|
+
* Check if a single field's current value matches the model data
|
|
4934
|
+
* @param {Object} fieldConfig - Field configuration
|
|
4935
|
+
* @param {Object} modelData - Model data to compare against
|
|
4936
|
+
* @returns {boolean} True if field value matches model
|
|
4937
|
+
*/
|
|
4938
|
+
fieldValueMatchesModel(fieldConfig, modelData) {
|
|
4939
|
+
if (!fieldConfig.name) return true;
|
|
4940
|
+
const fieldElement = this.element.querySelector(`[name="${fieldConfig.name}"]`);
|
|
4941
|
+
if (!fieldElement) return true;
|
|
4942
|
+
const currentValue = this.getFieldCurrentValue(fieldElement, fieldConfig);
|
|
4943
|
+
const modelValue = MOJOUtils.getContextData(modelData, fieldConfig.name);
|
|
4944
|
+
return this.valuesAreDifferent(currentValue, modelValue) === false;
|
|
4945
|
+
}
|
|
4946
|
+
/**
|
|
4947
|
+
* Get current value from a form field element
|
|
4948
|
+
*/
|
|
4949
|
+
getFieldCurrentValue(fieldElement, fieldConfig) {
|
|
4950
|
+
switch (fieldConfig.type) {
|
|
4951
|
+
case "checkbox":
|
|
4952
|
+
return fieldElement.checked;
|
|
4953
|
+
case "switch":
|
|
4954
|
+
return fieldElement.checked;
|
|
4955
|
+
case "radio":
|
|
4956
|
+
const checkedRadio = this.element.querySelector(`[name="${fieldConfig.name}"]:checked`);
|
|
4957
|
+
return checkedRadio ? checkedRadio.value : "";
|
|
4958
|
+
case "select":
|
|
4959
|
+
return fieldElement.multiple ? Array.from(fieldElement.selectedOptions).map((opt) => opt.value) : fieldElement.value;
|
|
4960
|
+
case "file":
|
|
4961
|
+
case "image":
|
|
4962
|
+
return null;
|
|
4963
|
+
// Don't sync file fields
|
|
4964
|
+
default:
|
|
4965
|
+
return fieldElement.value;
|
|
4966
|
+
}
|
|
4967
|
+
}
|
|
4968
|
+
/**
|
|
4969
|
+
* Set value on a form field element
|
|
4970
|
+
*/
|
|
4971
|
+
setFieldValue(fieldElement, fieldConfig, newValue) {
|
|
4972
|
+
switch (fieldConfig.type) {
|
|
4973
|
+
case "checkbox":
|
|
4974
|
+
case "switch":
|
|
4975
|
+
fieldElement.checked = Boolean(newValue);
|
|
4976
|
+
break;
|
|
4977
|
+
case "radio":
|
|
4978
|
+
const radioOption = this.element.querySelector(`[name="${fieldConfig.name}"][value="${newValue}"]`);
|
|
4979
|
+
if (radioOption) {
|
|
4980
|
+
radioOption.checked = true;
|
|
4981
|
+
}
|
|
4982
|
+
break;
|
|
4983
|
+
case "select":
|
|
4984
|
+
if (fieldElement.multiple && Array.isArray(newValue)) {
|
|
4985
|
+
Array.from(fieldElement.options).forEach((option) => {
|
|
4986
|
+
option.selected = newValue.includes(option.value);
|
|
4987
|
+
});
|
|
4988
|
+
} else {
|
|
4989
|
+
fieldElement.value = newValue || "";
|
|
4990
|
+
}
|
|
4991
|
+
break;
|
|
4992
|
+
case "file":
|
|
4993
|
+
case "image":
|
|
4994
|
+
break;
|
|
4995
|
+
default:
|
|
4996
|
+
fieldElement.value = newValue || "";
|
|
4997
|
+
break;
|
|
4820
4998
|
}
|
|
4999
|
+
fieldElement.dispatchEvent(new Event("change", { bubbles: true }));
|
|
4821
5000
|
}
|
|
4822
5001
|
/**
|
|
4823
5002
|
* Set defaults (updates defaults and rebuilds form)
|
|
@@ -4896,6 +5075,7 @@ class FormView extends View {
|
|
|
4896
5075
|
console.log("Saving changed data via model:", changes);
|
|
4897
5076
|
console.log("Data type:", changes instanceof FormData ? "FormData (multipart)" : "Object (JSON/base64)");
|
|
4898
5077
|
try {
|
|
5078
|
+
this._isFormDrivenChange = true;
|
|
4899
5079
|
const result = await this.model.save(changes);
|
|
4900
5080
|
console.log("Model save result:", result);
|
|
4901
5081
|
return result;
|
|
@@ -5400,6 +5580,108 @@ class FormView extends View {
|
|
|
5400
5580
|
}
|
|
5401
5581
|
}
|
|
5402
5582
|
}
|
|
5583
|
+
class FieldStatusManager {
|
|
5584
|
+
constructor(fieldElement) {
|
|
5585
|
+
this.fieldElement = fieldElement;
|
|
5586
|
+
this.statusContainer = this.findOrCreateStatusContainer();
|
|
5587
|
+
this.timeouts = /* @__PURE__ */ new Map();
|
|
5588
|
+
}
|
|
5589
|
+
/**
|
|
5590
|
+
* Find existing status container or create one
|
|
5591
|
+
*/
|
|
5592
|
+
findOrCreateStatusContainer() {
|
|
5593
|
+
let container = this.fieldElement.parentElement.querySelector(".field-status");
|
|
5594
|
+
if (!container) {
|
|
5595
|
+
container = document.createElement("div");
|
|
5596
|
+
container.className = "field-status";
|
|
5597
|
+
container.innerHTML = `
|
|
5598
|
+
<div class="spinner-border spinner-border-sm text-primary d-none" data-status="saving" role="status">
|
|
5599
|
+
<span class="visually-hidden">Saving...</span>
|
|
5600
|
+
</div>
|
|
5601
|
+
<i class="bi bi-check-circle text-success d-none" data-status="saved"></i>
|
|
5602
|
+
<i class="bi bi-exclamation-circle text-danger d-none" data-status="error"></i>
|
|
5603
|
+
`;
|
|
5604
|
+
this.fieldElement.parentElement.appendChild(container);
|
|
5605
|
+
}
|
|
5606
|
+
return container;
|
|
5607
|
+
}
|
|
5608
|
+
/**
|
|
5609
|
+
* Show a status indicator
|
|
5610
|
+
* @param {string} type - Status type: 'saving', 'saved', 'error'
|
|
5611
|
+
* @param {object} options - Additional options
|
|
5612
|
+
*/
|
|
5613
|
+
showStatus(type, options = {}) {
|
|
5614
|
+
this.clearTimeout(type);
|
|
5615
|
+
this.hideAllStatuses();
|
|
5616
|
+
const indicator = this.statusContainer.querySelector(`[data-status="${type}"]`);
|
|
5617
|
+
if (indicator) {
|
|
5618
|
+
indicator.classList.remove("d-none");
|
|
5619
|
+
indicator.classList.add("d-inline-block", "show");
|
|
5620
|
+
if (type === "saved") {
|
|
5621
|
+
this.setTimeout(type, () => this.hideStatus(type), 2500);
|
|
5622
|
+
} else if (type === "error") {
|
|
5623
|
+
if (options.message) {
|
|
5624
|
+
indicator.title = options.message;
|
|
5625
|
+
}
|
|
5626
|
+
this.setTimeout(type, () => this.hideStatus(type), 6e3);
|
|
5627
|
+
}
|
|
5628
|
+
}
|
|
5629
|
+
}
|
|
5630
|
+
/**
|
|
5631
|
+
* Hide a specific status indicator
|
|
5632
|
+
* @param {string} type - Status type to hide
|
|
5633
|
+
*/
|
|
5634
|
+
hideStatus(type) {
|
|
5635
|
+
const indicator = this.statusContainer.querySelector(`[data-status="${type}"]`);
|
|
5636
|
+
if (indicator) {
|
|
5637
|
+
indicator.classList.remove("show");
|
|
5638
|
+
indicator.classList.add("hide");
|
|
5639
|
+
setTimeout(() => {
|
|
5640
|
+
indicator.classList.add("d-none");
|
|
5641
|
+
indicator.classList.remove("d-inline-block", "hide");
|
|
5642
|
+
indicator.title = "";
|
|
5643
|
+
}, 300);
|
|
5644
|
+
}
|
|
5645
|
+
}
|
|
5646
|
+
/**
|
|
5647
|
+
* Hide all status indicators
|
|
5648
|
+
*/
|
|
5649
|
+
hideAllStatuses() {
|
|
5650
|
+
const indicators = this.statusContainer.querySelectorAll("[data-status]");
|
|
5651
|
+
indicators.forEach((indicator) => {
|
|
5652
|
+
indicator.classList.add("d-none");
|
|
5653
|
+
indicator.classList.remove("d-inline-block", "show", "hide");
|
|
5654
|
+
indicator.title = "";
|
|
5655
|
+
});
|
|
5656
|
+
}
|
|
5657
|
+
/**
|
|
5658
|
+
* Set a timeout for auto-hiding status
|
|
5659
|
+
* @param {string} type - Status type
|
|
5660
|
+
* @param {function} callback - Callback to execute
|
|
5661
|
+
* @param {number} delay - Delay in milliseconds
|
|
5662
|
+
*/
|
|
5663
|
+
setTimeout(type, callback, delay) {
|
|
5664
|
+
const timeoutId = setTimeout(callback, delay);
|
|
5665
|
+
this.timeouts.set(type, timeoutId);
|
|
5666
|
+
}
|
|
5667
|
+
/**
|
|
5668
|
+
* Clear a specific timeout
|
|
5669
|
+
* @param {string} type - Status type
|
|
5670
|
+
*/
|
|
5671
|
+
clearTimeout(type) {
|
|
5672
|
+
if (this.timeouts.has(type)) {
|
|
5673
|
+
clearTimeout(this.timeouts.get(type));
|
|
5674
|
+
this.timeouts.delete(type);
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
/**
|
|
5678
|
+
* Clean up all timeouts
|
|
5679
|
+
*/
|
|
5680
|
+
destroy() {
|
|
5681
|
+
this.timeouts.forEach((timeoutId) => clearTimeout(timeoutId));
|
|
5682
|
+
this.timeouts.clear();
|
|
5683
|
+
}
|
|
5684
|
+
}
|
|
5403
5685
|
const FormView$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
5404
5686
|
__proto__: null,
|
|
5405
5687
|
FormView,
|
|
@@ -5410,4 +5692,4 @@ export {
|
|
|
5410
5692
|
applyFileDropMixin as a,
|
|
5411
5693
|
FormView$1 as b
|
|
5412
5694
|
};
|
|
5413
|
-
//# sourceMappingURL=FormView-
|
|
5695
|
+
//# sourceMappingURL=FormView-CglUjQ3E.js.map
|