web-mojo 2.1.295 → 2.1.338

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.
Files changed (68) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +54 -53
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.cjs.js.map +1 -1
  7. package/dist/auth.es.js +3 -3
  8. package/dist/auth.es.js.map +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +2 -2
  11. package/dist/chunks/{ContextMenu-DKKmjtgo.js → ContextMenu-BRqHEC2q.js} +106 -29
  12. package/dist/chunks/ContextMenu-BRqHEC2q.js.map +1 -0
  13. package/dist/chunks/ContextMenu-DoWfr0BY.js +3 -0
  14. package/dist/chunks/ContextMenu-DoWfr0BY.js.map +1 -0
  15. package/dist/chunks/{DataView-CX04UO6n.js → DataView-DgOTz7NS.js} +2 -2
  16. package/dist/chunks/{DataView-CX04UO6n.js.map → DataView-DgOTz7NS.js.map} +1 -1
  17. package/dist/chunks/{DataView-CjjREbqE.js → DataView-FYalUi83.js} +2 -2
  18. package/dist/chunks/{DataView-CjjREbqE.js.map → DataView-FYalUi83.js.map} +1 -1
  19. package/dist/chunks/{Dialog-Dvb1SU1y.js → Dialog-CpvYwnK7.js} +2 -2
  20. package/dist/chunks/{Dialog-Dvb1SU1y.js.map → Dialog-CpvYwnK7.js.map} +1 -1
  21. package/dist/chunks/{Dialog-C7XcgLt8.js → Dialog-D7HCQQMK.js} +5 -5
  22. package/dist/chunks/{Dialog-C7XcgLt8.js.map → Dialog-D7HCQQMK.js.map} +1 -1
  23. package/dist/chunks/{FilePreviewView-CB0yqQtV.js → FilePreviewView-BokAhylL.js} +13 -10
  24. package/dist/chunks/FilePreviewView-BokAhylL.js.map +1 -0
  25. package/dist/chunks/FilePreviewView-Ct4v1mkr.js +2 -0
  26. package/dist/chunks/FilePreviewView-Ct4v1mkr.js.map +1 -0
  27. package/dist/chunks/{FormView-DJxUvj0p.js → FormView-CWNpkr0M.js} +212 -67
  28. package/dist/chunks/FormView-CWNpkr0M.js.map +1 -0
  29. package/dist/chunks/FormView-WUi-NXkB.js +2 -0
  30. package/dist/chunks/FormView-WUi-NXkB.js.map +1 -0
  31. package/dist/chunks/{MetricsChart-DQUOkYQ9.js → MetricsChart-2JMgp_0p.js} +2 -2
  32. package/dist/chunks/{MetricsChart-DQUOkYQ9.js.map → MetricsChart-2JMgp_0p.js.map} +1 -1
  33. package/dist/chunks/{MetricsChart-CpfACPrE.js → MetricsChart-CgX9kX8T.js} +3 -3
  34. package/dist/chunks/{MetricsChart-CpfACPrE.js.map → MetricsChart-CgX9kX8T.js.map} +1 -1
  35. package/dist/chunks/{PDFViewer-BUOnvSfV.js → PDFViewer-BZSQSArl.js} +2 -2
  36. package/dist/chunks/{PDFViewer-BUOnvSfV.js.map → PDFViewer-BZSQSArl.js.map} +1 -1
  37. package/dist/chunks/{PDFViewer-I1qlINnm.js → PDFViewer-kZOAIH9e.js} +3 -3
  38. package/dist/chunks/{PDFViewer-I1qlINnm.js.map → PDFViewer-kZOAIH9e.js.map} +1 -1
  39. package/dist/chunks/{Page-ZtYiac8Q.js → Page-BnFeIkwG.js} +2 -2
  40. package/dist/chunks/{Page-ZtYiac8Q.js.map → Page-BnFeIkwG.js.map} +1 -1
  41. package/dist/chunks/{Page-h5bbwzKf.js → Page-CHwwfFau.js} +2 -2
  42. package/dist/chunks/{Page-h5bbwzKf.js.map → Page-CHwwfFau.js.map} +1 -1
  43. package/dist/chunks/{TopNav-DVX8V6fL.js → TopNav-BSelpZnA.js} +2 -2
  44. package/dist/chunks/{TopNav-DVX8V6fL.js.map → TopNav-BSelpZnA.js.map} +1 -1
  45. package/dist/chunks/{TopNav-mtuzmduR.js → TopNav-V_BlTDgB.js} +2 -2
  46. package/dist/chunks/{TopNav-mtuzmduR.js.map → TopNav-V_BlTDgB.js.map} +1 -1
  47. package/dist/chunks/{WebApp-BgZwmmBY.js → WebApp-CEW6rw8N.js} +12 -12
  48. package/dist/chunks/{WebApp-BgZwmmBY.js.map → WebApp-CEW6rw8N.js.map} +1 -1
  49. package/dist/chunks/{WebApp-C_DpNvNx.js → WebApp-CTalj3eA.js} +2 -2
  50. package/dist/chunks/{WebApp-C_DpNvNx.js.map → WebApp-CTalj3eA.js.map} +1 -1
  51. package/dist/core.css +114 -0
  52. package/dist/css/web-mojo.css +1 -1
  53. package/dist/docit.cjs.js +1 -1
  54. package/dist/docit.es.js +5 -5
  55. package/dist/index.cjs.js +1 -1
  56. package/dist/index.es.js +11 -11
  57. package/dist/lightbox.cjs.js +1 -1
  58. package/dist/lightbox.es.js +4 -4
  59. package/package.json +1 -1
  60. package/dist/chunks/ContextMenu-BhyvSOLE.js +0 -3
  61. package/dist/chunks/ContextMenu-BhyvSOLE.js.map +0 -1
  62. package/dist/chunks/ContextMenu-DKKmjtgo.js.map +0 -1
  63. package/dist/chunks/FilePreviewView-CB0yqQtV.js.map +0 -1
  64. package/dist/chunks/FilePreviewView-DgVmlBjn.js +0 -2
  65. package/dist/chunks/FilePreviewView-DgVmlBjn.js.map +0 -1
  66. package/dist/chunks/FormView-CWFqrlKb.js +0 -2
  67. package/dist/chunks/FormView-CWFqrlKb.js.map +0 -1
  68. package/dist/chunks/FormView-DJxUvj0p.js.map +0 -1
@@ -1,7 +1,8 @@
1
- import { M as Mustache, h as MOJOUtils, V as View } from "./WebApp-BgZwmmBY.js";
1
+ import { M as Mustache, h as MOJOUtils, V as View } from "./WebApp-CEW6rw8N.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
- data: this.data,
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
- * Initialize form after rendering
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,25 +4363,59 @@ class FormView extends View {
4317
4363
  */
4318
4364
  handleFieldChange(fieldName, value) {
4319
4365
  this.data[fieldName] = value;
4320
- if (this.model && this.options.allowModelChange) {
4366
+ if (this.autosaveModelField && this.model) {
4367
+ this.handleFieldSave(fieldName, value);
4368
+ } else if (this.model && this.options.allowModelChange) {
4321
4369
  this._isFormDrivenChange = true;
4322
4370
  this.model.set(fieldName, value);
4323
4371
  }
4324
4372
  this.emit("field:change", { field: fieldName, value });
4325
4373
  }
4326
4374
  /**
4327
- * Refresh form data and rebuild FormBuilder
4328
- * Call this when model or data changes
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
4329
4414
  */
4330
4415
  refreshForm() {
4331
4416
  this.data = this.prepareFormData();
4332
- this.formBuilder = new FormBuilder({
4333
- ...this.formConfig,
4334
- data: this.data,
4335
- errors: this.errors
4336
- });
4337
4417
  if (this.element) {
4338
- this.render();
4418
+ this.populateFormValues();
4339
4419
  }
4340
4420
  }
4341
4421
  /**
@@ -4520,12 +4600,9 @@ class FormView extends View {
4520
4600
  async onChangeValidateField(event, element) {
4521
4601
  const fieldName = element.name;
4522
4602
  if (fieldName) {
4603
+ const value = element.value;
4604
+ this.handleFieldChange(fieldName, value);
4523
4605
  this.validateField(fieldName);
4524
- this.emit("change", {
4525
- field: fieldName,
4526
- value: element.value,
4527
- form: this
4528
- });
4529
4606
  }
4530
4607
  }
4531
4608
  /**
@@ -4534,15 +4611,11 @@ class FormView extends View {
4534
4611
  async onChangeToggleSwitch(event, element) {
4535
4612
  const fieldName = element.getAttribute("data-field");
4536
4613
  if (fieldName) {
4537
- this.data[fieldName] = element.checked;
4614
+ const value = element.checked;
4615
+ this.handleFieldChange(fieldName, value);
4538
4616
  this.emit("switch:toggle", {
4539
4617
  field: fieldName,
4540
- checked: element.checked,
4541
- form: this
4542
- });
4543
- this.emit("change", {
4544
- field: fieldName,
4545
- value: element.checked,
4618
+ checked: value,
4546
4619
  form: this
4547
4620
  });
4548
4621
  }
@@ -4638,21 +4711,21 @@ class FormView extends View {
4638
4711
  * Handle range value changes
4639
4712
  */
4640
4713
  async onChangeRangeChanged(event, element) {
4714
+ const fieldName = element.name;
4715
+ const value = element.value;
4641
4716
  const targetId = element.getAttribute("data-target");
4642
4717
  if (targetId) {
4643
4718
  const valueDisplay = this.element.querySelector(`#${targetId}`);
4644
4719
  if (valueDisplay) {
4645
- valueDisplay.textContent = element.value;
4720
+ valueDisplay.textContent = value;
4646
4721
  }
4647
4722
  }
4723
+ if (fieldName) {
4724
+ this.handleFieldChange(fieldName, value);
4725
+ }
4648
4726
  this.emit("range:changed", {
4649
- field: element.name,
4650
- value: element.value,
4651
- form: this
4652
- });
4653
- this.emit("change", {
4654
- field: element.name,
4655
- value: element.value,
4727
+ field: fieldName,
4728
+ value,
4656
4729
  form: this
4657
4730
  });
4658
4731
  }
@@ -4816,6 +4889,7 @@ class FormView extends View {
4816
4889
  }
4817
4890
  }
4818
4891
  _onModelChange() {
4892
+ this.data = this.prepareFormData();
4819
4893
  if (this.isMounted()) {
4820
4894
  if (!this._isFormDrivenChange) {
4821
4895
  this.syncFormWithModel();
@@ -4828,15 +4902,10 @@ class FormView extends View {
4828
4902
  */
4829
4903
  syncFormWithModel() {
4830
4904
  if (!this.model || !this.element) return;
4831
- const newData = this.prepareFormData();
4832
- if (this.formDataMatchesModelData(newData)) {
4905
+ if (this.formDataMatchesModelData(this.data)) {
4833
4906
  return;
4834
4907
  }
4835
- this.data = newData;
4836
- if (this.formBuilder) {
4837
- this.formBuilder.data = this.data;
4838
- }
4839
- this.syncFormFieldValues();
4908
+ this.populateFormValues();
4840
4909
  }
4841
4910
  /**
4842
4911
  * Compare current form values with new model data
@@ -4871,35 +4940,9 @@ class FormView extends View {
4871
4940
  const fieldElement = this.element.querySelector(`[name="${fieldConfig.name}"]`);
4872
4941
  if (!fieldElement) return true;
4873
4942
  const currentValue = this.getFieldCurrentValue(fieldElement, fieldConfig);
4874
- const modelValue = modelData[fieldConfig.name];
4943
+ const modelValue = MOJOUtils.getContextData(modelData, fieldConfig.name);
4875
4944
  return this.valuesAreDifferent(currentValue, modelValue) === false;
4876
4945
  }
4877
- /**
4878
- * Update form field values to match current model data
4879
- */
4880
- syncFormFieldValues() {
4881
- if (!this.formConfig?.fields || !this.element) return;
4882
- this.formConfig.fields.forEach((field) => {
4883
- if (field.type === "group" && field.fields) {
4884
- field.fields.forEach((groupField) => {
4885
- this.updateFormFieldValue(groupField);
4886
- });
4887
- } else {
4888
- this.updateFormFieldValue(field);
4889
- }
4890
- });
4891
- }
4892
- /**
4893
- * Update a single form field value from model data
4894
- */
4895
- updateFormFieldValue(fieldConfig) {
4896
- if (!fieldConfig.name) return;
4897
- const fieldElement = this.element.querySelector(`[name="${fieldConfig.name}"]`);
4898
- if (!fieldElement) return;
4899
- const newValue = this.data[fieldConfig.name];
4900
- if (this.getFieldCurrentValue(fieldElement, fieldConfig) === newValue) return;
4901
- this.setFieldValue(fieldElement, fieldConfig, newValue);
4902
- }
4903
4946
  /**
4904
4947
  * Get current value from a form field element
4905
4948
  */
@@ -5537,6 +5580,108 @@ class FormView extends View {
5537
5580
  }
5538
5581
  }
5539
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
+ }
5540
5685
  const FormView$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
5541
5686
  __proto__: null,
5542
5687
  FormView,
@@ -5547,4 +5692,4 @@ export {
5547
5692
  applyFileDropMixin as a,
5548
5693
  FormView$1 as b
5549
5694
  };
5550
- //# sourceMappingURL=FormView-DJxUvj0p.js.map
5695
+ //# sourceMappingURL=FormView-CWNpkr0M.js.map