web-mojo 2.2.7 → 2.2.8

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 (53) 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 +26 -7
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.es.js +1 -1
  7. package/dist/charts.cjs.js +1 -1
  8. package/dist/charts.es.js +3 -3
  9. package/dist/chunks/{ChatView-pFVev6ZD.js → ChatView-2mFEGsXL.js} +282 -96
  10. package/dist/chunks/ChatView-2mFEGsXL.js.map +1 -0
  11. package/dist/chunks/ChatView-DfKH7ep8.js +2 -0
  12. package/dist/chunks/ChatView-DfKH7ep8.js.map +1 -0
  13. package/dist/chunks/{Dialog-DPGOKfct.js → Dialog-BBZUgBbz.js} +2 -2
  14. package/dist/chunks/{Dialog-DPGOKfct.js.map → Dialog-BBZUgBbz.js.map} +1 -1
  15. package/dist/chunks/{Dialog-LFifi2fg.js → Dialog-DrCs-ex1.js} +3 -3
  16. package/dist/chunks/{Dialog-LFifi2fg.js.map → Dialog-DrCs-ex1.js.map} +1 -1
  17. package/dist/chunks/FormView-CNkSOc2U.js +3 -0
  18. package/dist/chunks/FormView-CNkSOc2U.js.map +1 -0
  19. package/dist/chunks/{FormView-Xb_l8AD5.js → FormView-CwYt4vH3.js} +400 -2
  20. package/dist/chunks/FormView-CwYt4vH3.js.map +1 -0
  21. package/dist/chunks/{MetricsMiniChartWidget-C8RQnqrd.js → MetricsMiniChartWidget-B3PHrgkn.js} +2 -2
  22. package/dist/chunks/{MetricsMiniChartWidget-C8RQnqrd.js.map → MetricsMiniChartWidget-B3PHrgkn.js.map} +1 -1
  23. package/dist/chunks/{MetricsMiniChartWidget-CzqYq7d2.js → MetricsMiniChartWidget-DdZ3zNve.js} +2 -2
  24. package/dist/chunks/{MetricsMiniChartWidget-CzqYq7d2.js.map → MetricsMiniChartWidget-DdZ3zNve.js.map} +1 -1
  25. package/dist/chunks/{PDFViewer-BCn-y_36.js → PDFViewer-CkKC_E0G.js} +2 -2
  26. package/dist/chunks/{PDFViewer-BCn-y_36.js.map → PDFViewer-CkKC_E0G.js.map} +1 -1
  27. package/dist/chunks/{PDFViewer-BeWHRAeX.js → PDFViewer-DaiTPsQH.js} +2 -2
  28. package/dist/chunks/{PDFViewer-BeWHRAeX.js.map → PDFViewer-DaiTPsQH.js.map} +1 -1
  29. package/dist/chunks/{TokenManager-D2pm6lM8.js → TokenManager-D9z35vwT.js} +2 -2
  30. package/dist/chunks/{TokenManager-D2pm6lM8.js.map → TokenManager-D9z35vwT.js.map} +1 -1
  31. package/dist/chunks/{TokenManager-DZwzcAzD.js → TokenManager-XwKaWaKd.js} +2 -2
  32. package/dist/chunks/{TokenManager-DZwzcAzD.js.map → TokenManager-XwKaWaKd.js.map} +1 -1
  33. package/dist/chunks/{version-aJXsYzYB.js → version-BQcvJscT.js} +4 -4
  34. package/dist/chunks/{version-aJXsYzYB.js.map → version-BQcvJscT.js.map} +1 -1
  35. package/dist/chunks/{version-CmZ8J2xW.js → version-DhSPKHUm.js} +2 -2
  36. package/dist/chunks/{version-CmZ8J2xW.js.map → version-DhSPKHUm.js.map} +1 -1
  37. package/dist/core.css +118 -0
  38. package/dist/css/web-mojo.css +1 -1
  39. package/dist/docit.cjs.js +1 -1
  40. package/dist/docit.es.js +3 -3
  41. package/dist/index.cjs.js +1 -1
  42. package/dist/index.cjs.js.map +1 -1
  43. package/dist/index.es.js +96 -92
  44. package/dist/index.es.js.map +1 -1
  45. package/dist/lightbox.cjs.js +1 -1
  46. package/dist/lightbox.es.js +4 -4
  47. package/package.json +1 -1
  48. package/dist/chunks/ChatView-B2iog5Hu.js +0 -2
  49. package/dist/chunks/ChatView-B2iog5Hu.js.map +0 -1
  50. package/dist/chunks/ChatView-pFVev6ZD.js.map +0 -1
  51. package/dist/chunks/FormView-Clc_mN0d.js +0 -3
  52. package/dist/chunks/FormView-Clc_mN0d.js.map +0 -1
  53. package/dist/chunks/FormView-Xb_l8AD5.js.map +0 -1
@@ -652,7 +652,6 @@ class FormBuilder {
652
652
  }
653
653
  }
654
654
  if (!fieldHTML) {
655
- console.log("buildFieldHTML - Processing field type:", type, "for field:", field.name);
656
655
  switch (type) {
657
656
  case "text":
658
657
  fieldHTML = this.renderTextField(field);
@@ -687,6 +686,9 @@ class FormBuilder {
687
686
  case "select":
688
687
  fieldHTML = this.renderSelectField(field);
689
688
  break;
689
+ case "multiselect":
690
+ fieldHTML = this.renderMultiSelectField(field);
691
+ break;
690
692
  case "checkbox":
691
693
  fieldHTML = this.renderCheckboxField(field);
692
694
  break;
@@ -1189,6 +1191,59 @@ class FormBuilder {
1189
1191
  };
1190
1192
  return Mustache.render(this.templates.select, context);
1191
1193
  }
1194
+ /**
1195
+ * Render multiselect dropdown field
1196
+ * @param {Object} field - Field configuration
1197
+ * @returns {string} Field HTML
1198
+ */
1199
+ renderMultiSelectField(field) {
1200
+ const {
1201
+ name,
1202
+ label,
1203
+ options = [],
1204
+ value = [],
1205
+ required = false,
1206
+ disabled = false,
1207
+ maxHeight = 300,
1208
+ help = field.helpText || field.help || ""
1209
+ } = field;
1210
+ const placeholder = field.placeholder || field.placeHolder || "Select...";
1211
+ this.getFieldId(name);
1212
+ const error = this.errors[name];
1213
+ const fieldValue = field.value ?? this.getFieldValue(name) ?? value;
1214
+ return `
1215
+ <div class="mojo-form-control">
1216
+ ${label ? `<label class="${this.options.labelClass}">${this.escapeHtml(label)}${required ? '<span class="text-danger">*</span>' : ""}</label>` : ""}
1217
+ <div class="multiselect-placeholder"
1218
+ data-field-name="${name}"
1219
+ data-field-type="multiselect"
1220
+ data-field-config='${JSON.stringify({
1221
+ name,
1222
+ value: fieldValue,
1223
+ placeholder,
1224
+ maxHeight,
1225
+ disabled,
1226
+ required
1227
+ })}'>
1228
+ <input type="hidden" name="${name}" value="${this.escapeHtml(JSON.stringify(fieldValue))}">
1229
+ <select class="form-select${error ? " is-invalid" : ""}"
1230
+ multiple
1231
+ ${disabled ? "disabled" : ""}
1232
+ ${required ? "required" : ""}>
1233
+ ${options.map((opt) => {
1234
+ const optValue = typeof opt === "string" ? opt : opt.value;
1235
+ const optLabel = typeof opt === "string" ? opt : opt.label || opt.value;
1236
+ const selected = Array.isArray(fieldValue) && fieldValue.includes(optValue) ? "selected" : "";
1237
+ return `<option value="${this.escapeHtml(optValue)}" ${selected}>${this.escapeHtml(optLabel)}</option>`;
1238
+ }).join("")}
1239
+ </select>
1240
+ <small class="form-text text-muted">This will be enhanced with MultiSelectDropdown component</small>
1241
+ </div>
1242
+ ${help ? `<div class="${this.options.helpClass}">${this.escapeHtml(help)}</div>` : ""}
1243
+ ${error ? `<div class="${this.options.errorClass}">${this.escapeHtml(error)}</div>` : ""}
1244
+ </div>
1245
+ `;
1246
+ }
1192
1247
  /**
1193
1248
  * Render checkbox field
1194
1249
  * @param {Object} field - Field configuration
@@ -4035,6 +4090,312 @@ class CollectionMultiSelectView extends View {
4035
4090
  this.setValue(value);
4036
4091
  }
4037
4092
  }
4093
+ class MultiSelectItemsView extends View {
4094
+ constructor(options = {}) {
4095
+ super({
4096
+ tagName: "div",
4097
+ className: "multiselect-items",
4098
+ template: `
4099
+ {{#items.length}}
4100
+ <div class="multiselect-list" style="max-height: {{maxHeight}}px; overflow-y: auto;">
4101
+ {{#items}}
4102
+ <div class="multiselect-item form-check px-3 py-2"
4103
+ data-action="toggle"
4104
+ data-value="{{value}}"
4105
+ data-index="{{index}}">
4106
+ <input type="checkbox"
4107
+ class="form-check-input"
4108
+ id="{{id}}"
4109
+ {{#selected}}checked{{/selected}}
4110
+ {{#disabled}}disabled{{/disabled}}>
4111
+ <label class="form-check-label w-100" for="{{id}}">
4112
+ {{label}}
4113
+ </label>
4114
+ </div>
4115
+ {{/items}}
4116
+ </div>
4117
+ <div class="multiselect-footer border-top p-2">
4118
+ <button type="button" class="btn btn-sm btn-primary w-100" data-action="close-dropdown">
4119
+ Done
4120
+ </button>
4121
+ </div>
4122
+ {{/items.length}}
4123
+
4124
+ {{^items.length}}
4125
+ <div class="multiselect-empty text-muted text-center py-3">
4126
+ <small>No options available</small>
4127
+ </div>
4128
+ {{/items.length}}
4129
+ `,
4130
+ ...options
4131
+ });
4132
+ this.items = options.items || [];
4133
+ this.maxHeight = options.maxHeight || 300;
4134
+ }
4135
+ /**
4136
+ * Handle item toggle
4137
+ */
4138
+ handleActionToggle(event, element) {
4139
+ const value = element.getAttribute("data-value");
4140
+ const index = parseInt(element.getAttribute("data-index"), 10);
4141
+ const item = this.items[index];
4142
+ if (!item || item.disabled) return;
4143
+ item.selected = !item.selected;
4144
+ const checkbox = element.querySelector('input[type="checkbox"]');
4145
+ if (checkbox) {
4146
+ checkbox.checked = item.selected;
4147
+ }
4148
+ this.emit("toggle", { value, index, selected: item.selected });
4149
+ }
4150
+ /**
4151
+ * Handle close dropdown button
4152
+ */
4153
+ handleActionCloseDropdown(event, element) {
4154
+ this.emit("close-dropdown");
4155
+ }
4156
+ /**
4157
+ * Get currently selected values
4158
+ */
4159
+ getValue() {
4160
+ return this.items.filter((item) => item.selected).map((item) => item.value);
4161
+ }
4162
+ /**
4163
+ * Set selected values
4164
+ */
4165
+ setValue(values) {
4166
+ const valueSet = new Set(Array.isArray(values) ? values : [values]);
4167
+ this.items.forEach((item) => {
4168
+ item.selected = valueSet.has(item.value);
4169
+ });
4170
+ this.render(false);
4171
+ }
4172
+ /**
4173
+ * Update items and re-render
4174
+ */
4175
+ updateItems(items) {
4176
+ this.items = items;
4177
+ this.render(false);
4178
+ }
4179
+ }
4180
+ class MultiSelectDropdown extends View {
4181
+ constructor(options = {}) {
4182
+ super({
4183
+ tagName: "div",
4184
+ className: "multiselect-dropdown",
4185
+ template: `
4186
+ <div class="mojo-form-control">
4187
+ {{#label}}
4188
+ <label class="form-label">
4189
+ {{label}}{{#required}}<span class="text-danger">*</span>{{/required}}
4190
+ </label>
4191
+ {{/label}}
4192
+
4193
+ <div class="dropdown w-100">
4194
+ <button class="btn btn-outline-secondary dropdown-toggle w-100 text-start d-flex justify-content-between align-items-center"
4195
+ type="button"
4196
+ data-bs-toggle="dropdown"
4197
+ aria-expanded="false"
4198
+ {{#disabled}}disabled{{/disabled}}>
4199
+ <span class="multiselect-button-text">{{buttonText}}</span>
4200
+ <i class="bi bi-chevron-down"></i>
4201
+ </button>
4202
+ <div class="dropdown-menu w-100" data-bs-auto-close="outside" data-container="items"></div>
4203
+ </div>
4204
+
4205
+ {{#help}}
4206
+ <div class="form-text">{{help}}</div>
4207
+ {{/help}}
4208
+ {{#error}}
4209
+ <div class="invalid-feedback d-block">{{error}}</div>
4210
+ {{/error}}
4211
+ </div>
4212
+ `,
4213
+ ...options
4214
+ });
4215
+ this.name = options.name || "multiselect";
4216
+ this.label = options.label || "";
4217
+ this.help = options.help || "";
4218
+ this.error = options.error || "";
4219
+ this.required = options.required || false;
4220
+ this.disabled = options.disabled || false;
4221
+ this.placeholder = options.placeholder || options.placeHolder || "Select...";
4222
+ this.maxHeight = options.maxHeight || 300;
4223
+ this.showSelectedLabels = options.showSelectedLabels !== false;
4224
+ this.maxLabelsToShow = options.maxLabelsToShow || 3;
4225
+ this.options = options.options || [];
4226
+ this.selectedValues = Array.isArray(options.value) ? options.value : [];
4227
+ this.buttonText = this.computeButtonText();
4228
+ this.listView = null;
4229
+ }
4230
+ /**
4231
+ * Compute button text based on current selection
4232
+ */
4233
+ computeButtonText() {
4234
+ const count = this.selectedValues.length;
4235
+ if (count === 0) {
4236
+ return this.placeholder || "Select...";
4237
+ } else if (this.showSelectedLabels && count <= this.maxLabelsToShow) {
4238
+ const labels = this.selectedValues.map((value) => {
4239
+ const selectedOption = this.options.find((opt) => {
4240
+ const optValue = typeof opt === "string" ? opt : opt.value;
4241
+ return optValue === value;
4242
+ });
4243
+ return typeof selectedOption === "string" ? selectedOption : selectedOption?.label || selectedOption?.value || value;
4244
+ });
4245
+ return labels.join(", ");
4246
+ } else {
4247
+ return `${count} selected`;
4248
+ }
4249
+ }
4250
+ /**
4251
+ * Initialize child view after render
4252
+ */
4253
+ async onAfterRender() {
4254
+ await super.onAfterRender();
4255
+ this.createListView();
4256
+ }
4257
+ /**
4258
+ * Create and mount the items list view
4259
+ */
4260
+ createListView() {
4261
+ const container = this.element?.querySelector('[data-container="items"]');
4262
+ if (!container) return;
4263
+ const items = this.options.map((option, index) => {
4264
+ const value = typeof option === "string" ? option : option.value;
4265
+ const label = typeof option === "string" ? option : option.label || option.text || option.value;
4266
+ const disabled = typeof option === "object" ? option.disabled : false;
4267
+ return {
4268
+ id: `${this.name}_${index}`,
4269
+ value,
4270
+ label,
4271
+ index,
4272
+ selected: this.selectedValues.includes(value),
4273
+ disabled
4274
+ };
4275
+ });
4276
+ this.listView = new MultiSelectItemsView({
4277
+ items,
4278
+ maxHeight: this.maxHeight
4279
+ });
4280
+ this.listView.on("toggle", (data) => {
4281
+ this.handleToggle(data);
4282
+ });
4283
+ this.listView.on("close-dropdown", () => {
4284
+ this.closeDropdown();
4285
+ });
4286
+ this.listView.render(true, container);
4287
+ }
4288
+ /**
4289
+ * Close the dropdown programmatically
4290
+ */
4291
+ closeDropdown() {
4292
+ const dropdownButton = this.element?.querySelector(".dropdown-toggle");
4293
+ if (dropdownButton && window.bootstrap?.Dropdown) {
4294
+ const dropdownInstance = window.bootstrap.Dropdown.getInstance(dropdownButton);
4295
+ if (dropdownInstance) {
4296
+ dropdownInstance.hide();
4297
+ }
4298
+ }
4299
+ }
4300
+ /**
4301
+ * Handle item toggle
4302
+ */
4303
+ handleToggle(data) {
4304
+ const { value, selected } = data;
4305
+ if (selected) {
4306
+ if (!this.selectedValues.includes(value)) {
4307
+ this.selectedValues.push(value);
4308
+ }
4309
+ } else {
4310
+ this.selectedValues = this.selectedValues.filter((v) => v !== value);
4311
+ }
4312
+ this.updateButtonText();
4313
+ this.emit("change", {
4314
+ value: this.selectedValues,
4315
+ name: this.name
4316
+ });
4317
+ }
4318
+ /**
4319
+ * Update button text based on selection
4320
+ */
4321
+ updateButtonText() {
4322
+ const button = this.element?.querySelector(".multiselect-button-text");
4323
+ if (!button) return;
4324
+ const count = this.selectedValues.length;
4325
+ this.buttonText = this.computeButtonText();
4326
+ button.textContent = this.buttonText;
4327
+ if (count === 0) {
4328
+ button.classList.add("text-muted");
4329
+ } else {
4330
+ button.classList.remove("text-muted");
4331
+ }
4332
+ }
4333
+ /**
4334
+ * Get current selected values
4335
+ */
4336
+ getValue() {
4337
+ return this.selectedValues;
4338
+ }
4339
+ /**
4340
+ * Set selected values
4341
+ */
4342
+ setValue(values) {
4343
+ this.selectedValues = Array.isArray(values) ? values : values ? [values] : [];
4344
+ if (this.listView) {
4345
+ this.listView.setValue(this.selectedValues);
4346
+ }
4347
+ this.updateButtonText();
4348
+ }
4349
+ /**
4350
+ * Update options list
4351
+ */
4352
+ setOptions(options) {
4353
+ this.options = options;
4354
+ if (this.listView) {
4355
+ const items = this.options.map((option, index) => {
4356
+ const value = typeof option === "string" ? option : option.value;
4357
+ const label = typeof option === "string" ? option : option.label || option.text || option.value;
4358
+ const disabled = typeof option === "object" ? option.disabled : false;
4359
+ return {
4360
+ id: `${this.name}_${index}`,
4361
+ value,
4362
+ label,
4363
+ index,
4364
+ selected: this.selectedValues.includes(value),
4365
+ disabled
4366
+ };
4367
+ });
4368
+ this.listView.updateItems(items);
4369
+ }
4370
+ }
4371
+ /**
4372
+ * Clear all selections
4373
+ */
4374
+ clear() {
4375
+ this.setValue([]);
4376
+ }
4377
+ /**
4378
+ * Get form value (for form integration)
4379
+ */
4380
+ getFormValue() {
4381
+ return this.getValue();
4382
+ }
4383
+ /**
4384
+ * Set form value (for form integration)
4385
+ */
4386
+ setFormValue(value) {
4387
+ this.setValue(value);
4388
+ }
4389
+ /**
4390
+ * Cleanup child view on destroy
4391
+ */
4392
+ async onBeforeDestroy() {
4393
+ await super.onBeforeDestroy();
4394
+ if (this.listView) {
4395
+ this.listView.destroy();
4396
+ }
4397
+ }
4398
+ }
4038
4399
  class DatePicker extends View {
4039
4400
  constructor(options = {}) {
4040
4401
  const {
@@ -5929,6 +6290,7 @@ class FormView extends View {
5929
6290
  this.initializeImageFields();
5930
6291
  this.initializeCustomComponents();
5931
6292
  this.initializeTagInputs();
6293
+ this.initializeMultiSelectDropdowns();
5932
6294
  this.initializeCollectionSelects();
5933
6295
  this.initializeCollectionMultiSelects();
5934
6296
  this.initializeDatePickers();
@@ -6057,6 +6419,42 @@ class FormView extends View {
6057
6419
  }
6058
6420
  });
6059
6421
  }
6422
+ /**
6423
+ * Initialize MultiSelectDropdown components
6424
+ */
6425
+ initializeMultiSelectDropdowns() {
6426
+ const multiselectPlaceholders = this.element.querySelectorAll('[data-field-type="multiselect"]');
6427
+ multiselectPlaceholders.forEach((placeholder) => {
6428
+ try {
6429
+ const fieldName = placeholder.getAttribute("data-field-name");
6430
+ const configData = placeholder.getAttribute("data-field-config");
6431
+ const config = JSON.parse(configData);
6432
+ const fieldConfig = this.getFormFieldConfig(fieldName);
6433
+ if (!fieldConfig) {
6434
+ return;
6435
+ }
6436
+ const multiselect = new MultiSelectDropdown({
6437
+ ...config,
6438
+ options: fieldConfig.options || [],
6439
+ placeholder: fieldConfig.placeholder || config.placeholder || "Select...",
6440
+ label: fieldConfig.label,
6441
+ containerId: null
6442
+ // We'll mount directly
6443
+ });
6444
+ let value = config.value ?? MOJOUtils.getContextData(this.data, fieldName);
6445
+ if (value) {
6446
+ multiselect.setFormValue(value);
6447
+ }
6448
+ multiselect.render(true, placeholder);
6449
+ this.customComponents.set(fieldName, multiselect);
6450
+ multiselect.on("change", (data) => {
6451
+ this.handleFieldChange(fieldName, data.value);
6452
+ });
6453
+ } catch (error) {
6454
+ console.error("MultiSelectDropdown initialization failed:", error);
6455
+ }
6456
+ });
6457
+ }
6060
6458
  /**
6061
6459
  * Initialize CollectionSelect components
6062
6460
  */
@@ -7893,4 +8291,4 @@ export {
7893
8291
  applyFileDropMixin as a,
7894
8292
  FormView$1 as b
7895
8293
  };
7896
- //# sourceMappingURL=FormView-Xb_l8AD5.js.map
8294
+ //# sourceMappingURL=FormView-CwYt4vH3.js.map