web-mojo 2.2.7 → 2.2.9
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 +7509 -7428
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.es.js +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +3 -3
- package/dist/chunks/ChatView-BxIeRwBQ.js +2 -0
- package/dist/chunks/ChatView-BxIeRwBQ.js.map +1 -0
- package/dist/chunks/{ChatView-pFVev6ZD.js → ChatView-DfhhZKoN.js} +282 -96
- package/dist/chunks/ChatView-DfhhZKoN.js.map +1 -0
- package/dist/chunks/{Dialog-LFifi2fg.js → Dialog-Cu_Dx46k.js} +3 -3
- package/dist/chunks/{Dialog-LFifi2fg.js.map → Dialog-Cu_Dx46k.js.map} +1 -1
- package/dist/chunks/{Dialog-DPGOKfct.js → Dialog-DX5h2QA9.js} +2 -2
- package/dist/chunks/{Dialog-DPGOKfct.js.map → Dialog-DX5h2QA9.js.map} +1 -1
- package/dist/chunks/{FormView-Xb_l8AD5.js → FormView-B_90L1RY.js} +683 -27
- package/dist/chunks/FormView-B_90L1RY.js.map +1 -0
- package/dist/chunks/FormView-Bwofbd8S.js +3 -0
- package/dist/chunks/FormView-Bwofbd8S.js.map +1 -0
- package/dist/chunks/{MetricsMiniChartWidget-CzqYq7d2.js → MetricsMiniChartWidget-Cg5Hyx28.js} +113 -54
- package/dist/chunks/MetricsMiniChartWidget-Cg5Hyx28.js.map +1 -0
- package/dist/chunks/MetricsMiniChartWidget-DNIU3bL-.js +2 -0
- package/dist/chunks/MetricsMiniChartWidget-DNIU3bL-.js.map +1 -0
- package/dist/chunks/{PDFViewer-BCn-y_36.js → PDFViewer--jlqnuVw.js} +2 -2
- package/dist/chunks/{PDFViewer-BCn-y_36.js.map → PDFViewer--jlqnuVw.js.map} +1 -1
- package/dist/chunks/{PDFViewer-BeWHRAeX.js → PDFViewer-DSmi78S6.js} +2 -2
- package/dist/chunks/{PDFViewer-BeWHRAeX.js.map → PDFViewer-DSmi78S6.js.map} +1 -1
- package/dist/chunks/{TokenManager-D2pm6lM8.js → TokenManager-CEOPgnsw.js} +2 -2
- package/dist/chunks/{TokenManager-D2pm6lM8.js.map → TokenManager-CEOPgnsw.js.map} +1 -1
- package/dist/chunks/{TokenManager-DZwzcAzD.js → TokenManager-DiQfilqw.js} +2 -2
- package/dist/chunks/{TokenManager-DZwzcAzD.js.map → TokenManager-DiQfilqw.js.map} +1 -1
- package/dist/chunks/{version-CmZ8J2xW.js → version-BlWcQoar.js} +2 -2
- package/dist/chunks/{version-CmZ8J2xW.js.map → version-BlWcQoar.js.map} +1 -1
- package/dist/chunks/{version-aJXsYzYB.js → version-CHVtoVR0.js} +4 -4
- package/dist/chunks/{version-aJXsYzYB.js.map → version-CHVtoVR0.js.map} +1 -1
- package/dist/core.css +183 -0
- package/dist/css/web-mojo.css +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +3 -3
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +96 -92
- package/dist/index.es.js.map +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.es.js +4 -4
- package/package.json +1 -1
- package/dist/chunks/ChatView-B2iog5Hu.js +0 -2
- package/dist/chunks/ChatView-B2iog5Hu.js.map +0 -1
- package/dist/chunks/ChatView-pFVev6ZD.js.map +0 -1
- package/dist/chunks/FormView-Clc_mN0d.js +0 -3
- package/dist/chunks/FormView-Clc_mN0d.js.map +0 -1
- package/dist/chunks/FormView-Xb_l8AD5.js.map +0 -1
- package/dist/chunks/MetricsMiniChartWidget-C8RQnqrd.js +0 -2
- package/dist/chunks/MetricsMiniChartWidget-C8RQnqrd.js.map +0 -1
- package/dist/chunks/MetricsMiniChartWidget-CzqYq7d2.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
|
|
@@ -2113,50 +2168,38 @@ class FormBuilder {
|
|
|
2113
2168
|
name,
|
|
2114
2169
|
label,
|
|
2115
2170
|
value = "",
|
|
2116
|
-
placeholder = "Select or type...",
|
|
2117
|
-
options = [],
|
|
2118
2171
|
required = false,
|
|
2119
2172
|
disabled = false,
|
|
2120
|
-
|
|
2121
|
-
allowCustom = true,
|
|
2122
|
-
showDescription = true,
|
|
2123
|
-
minChars = 0,
|
|
2124
|
-
maxSuggestions = 10,
|
|
2173
|
+
maxHeight = 300,
|
|
2125
2174
|
help = field.helpText || field.help || ""
|
|
2126
2175
|
} = field;
|
|
2127
|
-
const
|
|
2176
|
+
const placeholder = field.placeholder || field.placeHolder || "Type or select...";
|
|
2177
|
+
const allowCustom = field.allowCustom !== false;
|
|
2178
|
+
this.getFieldId(name);
|
|
2128
2179
|
const error = this.errors[name];
|
|
2129
|
-
const fieldValue = this.getFieldValue(name) ?? value;
|
|
2180
|
+
const fieldValue = field.value ?? this.getFieldValue(name) ?? value;
|
|
2130
2181
|
return `
|
|
2131
2182
|
<div class="mojo-form-control">
|
|
2132
|
-
${label ? `<label
|
|
2133
|
-
<div class="
|
|
2183
|
+
${label ? `<label class="${this.options.labelClass}">${this.escapeHtml(label)}${required ? '<span class="text-danger">*</span>' : ""}</label>` : ""}
|
|
2184
|
+
<div class="combobox-placeholder"
|
|
2134
2185
|
data-field-name="${name}"
|
|
2135
|
-
data-field-type="
|
|
2186
|
+
data-field-type="combobox"
|
|
2136
2187
|
data-field-config='${JSON.stringify({
|
|
2137
2188
|
name,
|
|
2138
2189
|
value: fieldValue,
|
|
2139
2190
|
placeholder,
|
|
2140
|
-
|
|
2191
|
+
maxHeight,
|
|
2141
2192
|
allowCustom,
|
|
2142
|
-
showDescription,
|
|
2143
|
-
minChars,
|
|
2144
|
-
maxSuggestions,
|
|
2145
2193
|
disabled,
|
|
2146
|
-
readonly,
|
|
2147
2194
|
required
|
|
2148
2195
|
})}'>
|
|
2149
|
-
<input type="text"
|
|
2150
|
-
|
|
2151
|
-
name="${name}_display"
|
|
2152
|
-
class="${this.options.inputClass}${error ? " is-invalid" : ""}"
|
|
2153
|
-
placeholder="${this.escapeHtml(placeholder)}"
|
|
2196
|
+
<input type="text"
|
|
2197
|
+
class="form-control${error ? " is-invalid" : ""}"
|
|
2154
2198
|
value="${this.escapeHtml(fieldValue)}"
|
|
2199
|
+
placeholder="${this.escapeHtml(placeholder)}"
|
|
2155
2200
|
${disabled ? "disabled" : ""}
|
|
2156
|
-
${readonly ? "readonly" : ""}
|
|
2157
2201
|
${required ? "required" : ""}>
|
|
2158
|
-
<
|
|
2159
|
-
<small class="form-text text-muted">This will be enhanced with ComboInput component</small>
|
|
2202
|
+
<small class="form-text text-muted">This will be enhanced with ComboBox component</small>
|
|
2160
2203
|
</div>
|
|
2161
2204
|
${help ? `<div class="${this.options.helpClass}">${this.escapeHtml(help)}</div>` : ""}
|
|
2162
2205
|
${error ? `<div class="${this.options.errorClass}">${this.escapeHtml(error)}</div>` : ""}
|
|
@@ -4035,6 +4078,312 @@ class CollectionMultiSelectView extends View {
|
|
|
4035
4078
|
this.setValue(value);
|
|
4036
4079
|
}
|
|
4037
4080
|
}
|
|
4081
|
+
class MultiSelectItemsView extends View {
|
|
4082
|
+
constructor(options = {}) {
|
|
4083
|
+
super({
|
|
4084
|
+
tagName: "div",
|
|
4085
|
+
className: "multiselect-items",
|
|
4086
|
+
template: `
|
|
4087
|
+
{{#items.length}}
|
|
4088
|
+
<div class="multiselect-list" style="max-height: {{maxHeight}}px; overflow-y: auto;">
|
|
4089
|
+
{{#items}}
|
|
4090
|
+
<div class="multiselect-item form-check px-3 py-2"
|
|
4091
|
+
data-action="toggle"
|
|
4092
|
+
data-value="{{value}}"
|
|
4093
|
+
data-index="{{index}}">
|
|
4094
|
+
<input type="checkbox"
|
|
4095
|
+
class="form-check-input"
|
|
4096
|
+
id="{{id}}"
|
|
4097
|
+
{{#selected}}checked{{/selected}}
|
|
4098
|
+
{{#disabled}}disabled{{/disabled}}>
|
|
4099
|
+
<label class="form-check-label w-100" for="{{id}}">
|
|
4100
|
+
{{label}}
|
|
4101
|
+
</label>
|
|
4102
|
+
</div>
|
|
4103
|
+
{{/items}}
|
|
4104
|
+
</div>
|
|
4105
|
+
<div class="multiselect-footer border-top p-2">
|
|
4106
|
+
<button type="button" class="btn btn-sm btn-primary w-100" data-action="close-dropdown">
|
|
4107
|
+
Done
|
|
4108
|
+
</button>
|
|
4109
|
+
</div>
|
|
4110
|
+
{{/items.length}}
|
|
4111
|
+
|
|
4112
|
+
{{^items.length}}
|
|
4113
|
+
<div class="multiselect-empty text-muted text-center py-3">
|
|
4114
|
+
<small>No options available</small>
|
|
4115
|
+
</div>
|
|
4116
|
+
{{/items.length}}
|
|
4117
|
+
`,
|
|
4118
|
+
...options
|
|
4119
|
+
});
|
|
4120
|
+
this.items = options.items || [];
|
|
4121
|
+
this.maxHeight = options.maxHeight || 300;
|
|
4122
|
+
}
|
|
4123
|
+
/**
|
|
4124
|
+
* Handle item toggle
|
|
4125
|
+
*/
|
|
4126
|
+
handleActionToggle(event, element) {
|
|
4127
|
+
const value = element.getAttribute("data-value");
|
|
4128
|
+
const index = parseInt(element.getAttribute("data-index"), 10);
|
|
4129
|
+
const item = this.items[index];
|
|
4130
|
+
if (!item || item.disabled) return;
|
|
4131
|
+
item.selected = !item.selected;
|
|
4132
|
+
const checkbox = element.querySelector('input[type="checkbox"]');
|
|
4133
|
+
if (checkbox) {
|
|
4134
|
+
checkbox.checked = item.selected;
|
|
4135
|
+
}
|
|
4136
|
+
this.emit("toggle", { value, index, selected: item.selected });
|
|
4137
|
+
}
|
|
4138
|
+
/**
|
|
4139
|
+
* Handle close dropdown button
|
|
4140
|
+
*/
|
|
4141
|
+
handleActionCloseDropdown(event, element) {
|
|
4142
|
+
this.emit("close-dropdown");
|
|
4143
|
+
}
|
|
4144
|
+
/**
|
|
4145
|
+
* Get currently selected values
|
|
4146
|
+
*/
|
|
4147
|
+
getValue() {
|
|
4148
|
+
return this.items.filter((item) => item.selected).map((item) => item.value);
|
|
4149
|
+
}
|
|
4150
|
+
/**
|
|
4151
|
+
* Set selected values
|
|
4152
|
+
*/
|
|
4153
|
+
setValue(values) {
|
|
4154
|
+
const valueSet = new Set(Array.isArray(values) ? values : [values]);
|
|
4155
|
+
this.items.forEach((item) => {
|
|
4156
|
+
item.selected = valueSet.has(item.value);
|
|
4157
|
+
});
|
|
4158
|
+
this.render(false);
|
|
4159
|
+
}
|
|
4160
|
+
/**
|
|
4161
|
+
* Update items and re-render
|
|
4162
|
+
*/
|
|
4163
|
+
updateItems(items) {
|
|
4164
|
+
this.items = items;
|
|
4165
|
+
this.render(false);
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
class MultiSelectDropdown extends View {
|
|
4169
|
+
constructor(options = {}) {
|
|
4170
|
+
super({
|
|
4171
|
+
tagName: "div",
|
|
4172
|
+
className: "multiselect-dropdown",
|
|
4173
|
+
template: `
|
|
4174
|
+
<div class="mojo-form-control">
|
|
4175
|
+
{{#label}}
|
|
4176
|
+
<label class="form-label">
|
|
4177
|
+
{{label}}{{#required}}<span class="text-danger">*</span>{{/required}}
|
|
4178
|
+
</label>
|
|
4179
|
+
{{/label}}
|
|
4180
|
+
|
|
4181
|
+
<div class="dropdown w-100">
|
|
4182
|
+
<button class="btn btn-outline-secondary dropdown-toggle w-100 text-start d-flex justify-content-between align-items-center"
|
|
4183
|
+
type="button"
|
|
4184
|
+
data-bs-toggle="dropdown"
|
|
4185
|
+
aria-expanded="false"
|
|
4186
|
+
{{#disabled}}disabled{{/disabled}}>
|
|
4187
|
+
<span class="multiselect-button-text">{{buttonText}}</span>
|
|
4188
|
+
<i class="bi bi-chevron-down"></i>
|
|
4189
|
+
</button>
|
|
4190
|
+
<div class="dropdown-menu w-100" data-bs-auto-close="outside" data-container="items"></div>
|
|
4191
|
+
</div>
|
|
4192
|
+
|
|
4193
|
+
{{#help}}
|
|
4194
|
+
<div class="form-text">{{help}}</div>
|
|
4195
|
+
{{/help}}
|
|
4196
|
+
{{#error}}
|
|
4197
|
+
<div class="invalid-feedback d-block">{{error}}</div>
|
|
4198
|
+
{{/error}}
|
|
4199
|
+
</div>
|
|
4200
|
+
`,
|
|
4201
|
+
...options
|
|
4202
|
+
});
|
|
4203
|
+
this.name = options.name || "multiselect";
|
|
4204
|
+
this.label = options.label || "";
|
|
4205
|
+
this.help = options.help || "";
|
|
4206
|
+
this.error = options.error || "";
|
|
4207
|
+
this.required = options.required || false;
|
|
4208
|
+
this.disabled = options.disabled || false;
|
|
4209
|
+
this.placeholder = options.placeholder || options.placeHolder || "Select...";
|
|
4210
|
+
this.maxHeight = options.maxHeight || 300;
|
|
4211
|
+
this.showSelectedLabels = options.showSelectedLabels !== false;
|
|
4212
|
+
this.maxLabelsToShow = options.maxLabelsToShow || 3;
|
|
4213
|
+
this.options = options.options || [];
|
|
4214
|
+
this.selectedValues = Array.isArray(options.value) ? options.value : [];
|
|
4215
|
+
this.buttonText = this.computeButtonText();
|
|
4216
|
+
this.listView = null;
|
|
4217
|
+
}
|
|
4218
|
+
/**
|
|
4219
|
+
* Compute button text based on current selection
|
|
4220
|
+
*/
|
|
4221
|
+
computeButtonText() {
|
|
4222
|
+
const count = this.selectedValues.length;
|
|
4223
|
+
if (count === 0) {
|
|
4224
|
+
return this.placeholder || "Select...";
|
|
4225
|
+
} else if (this.showSelectedLabels && count <= this.maxLabelsToShow) {
|
|
4226
|
+
const labels = this.selectedValues.map((value) => {
|
|
4227
|
+
const selectedOption = this.options.find((opt) => {
|
|
4228
|
+
const optValue = typeof opt === "string" ? opt : opt.value;
|
|
4229
|
+
return optValue === value;
|
|
4230
|
+
});
|
|
4231
|
+
return typeof selectedOption === "string" ? selectedOption : selectedOption?.label || selectedOption?.value || value;
|
|
4232
|
+
});
|
|
4233
|
+
return labels.join(", ");
|
|
4234
|
+
} else {
|
|
4235
|
+
return `${count} selected`;
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
/**
|
|
4239
|
+
* Initialize child view after render
|
|
4240
|
+
*/
|
|
4241
|
+
async onAfterRender() {
|
|
4242
|
+
await super.onAfterRender();
|
|
4243
|
+
this.createListView();
|
|
4244
|
+
}
|
|
4245
|
+
/**
|
|
4246
|
+
* Create and mount the items list view
|
|
4247
|
+
*/
|
|
4248
|
+
createListView() {
|
|
4249
|
+
const container = this.element?.querySelector('[data-container="items"]');
|
|
4250
|
+
if (!container) return;
|
|
4251
|
+
const items = this.options.map((option, index) => {
|
|
4252
|
+
const value = typeof option === "string" ? option : option.value;
|
|
4253
|
+
const label = typeof option === "string" ? option : option.label || option.text || option.value;
|
|
4254
|
+
const disabled = typeof option === "object" ? option.disabled : false;
|
|
4255
|
+
return {
|
|
4256
|
+
id: `${this.name}_${index}`,
|
|
4257
|
+
value,
|
|
4258
|
+
label,
|
|
4259
|
+
index,
|
|
4260
|
+
selected: this.selectedValues.includes(value),
|
|
4261
|
+
disabled
|
|
4262
|
+
};
|
|
4263
|
+
});
|
|
4264
|
+
this.listView = new MultiSelectItemsView({
|
|
4265
|
+
items,
|
|
4266
|
+
maxHeight: this.maxHeight
|
|
4267
|
+
});
|
|
4268
|
+
this.listView.on("toggle", (data) => {
|
|
4269
|
+
this.handleToggle(data);
|
|
4270
|
+
});
|
|
4271
|
+
this.listView.on("close-dropdown", () => {
|
|
4272
|
+
this.closeDropdown();
|
|
4273
|
+
});
|
|
4274
|
+
this.listView.render(true, container);
|
|
4275
|
+
}
|
|
4276
|
+
/**
|
|
4277
|
+
* Close the dropdown programmatically
|
|
4278
|
+
*/
|
|
4279
|
+
closeDropdown() {
|
|
4280
|
+
const dropdownButton = this.element?.querySelector(".dropdown-toggle");
|
|
4281
|
+
if (dropdownButton && window.bootstrap?.Dropdown) {
|
|
4282
|
+
const dropdownInstance = window.bootstrap.Dropdown.getInstance(dropdownButton);
|
|
4283
|
+
if (dropdownInstance) {
|
|
4284
|
+
dropdownInstance.hide();
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
}
|
|
4288
|
+
/**
|
|
4289
|
+
* Handle item toggle
|
|
4290
|
+
*/
|
|
4291
|
+
handleToggle(data) {
|
|
4292
|
+
const { value, selected } = data;
|
|
4293
|
+
if (selected) {
|
|
4294
|
+
if (!this.selectedValues.includes(value)) {
|
|
4295
|
+
this.selectedValues.push(value);
|
|
4296
|
+
}
|
|
4297
|
+
} else {
|
|
4298
|
+
this.selectedValues = this.selectedValues.filter((v) => v !== value);
|
|
4299
|
+
}
|
|
4300
|
+
this.updateButtonText();
|
|
4301
|
+
this.emit("change", {
|
|
4302
|
+
value: this.selectedValues,
|
|
4303
|
+
name: this.name
|
|
4304
|
+
});
|
|
4305
|
+
}
|
|
4306
|
+
/**
|
|
4307
|
+
* Update button text based on selection
|
|
4308
|
+
*/
|
|
4309
|
+
updateButtonText() {
|
|
4310
|
+
const button = this.element?.querySelector(".multiselect-button-text");
|
|
4311
|
+
if (!button) return;
|
|
4312
|
+
const count = this.selectedValues.length;
|
|
4313
|
+
this.buttonText = this.computeButtonText();
|
|
4314
|
+
button.textContent = this.buttonText;
|
|
4315
|
+
if (count === 0) {
|
|
4316
|
+
button.classList.add("text-muted");
|
|
4317
|
+
} else {
|
|
4318
|
+
button.classList.remove("text-muted");
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
/**
|
|
4322
|
+
* Get current selected values
|
|
4323
|
+
*/
|
|
4324
|
+
getValue() {
|
|
4325
|
+
return this.selectedValues;
|
|
4326
|
+
}
|
|
4327
|
+
/**
|
|
4328
|
+
* Set selected values
|
|
4329
|
+
*/
|
|
4330
|
+
setValue(values) {
|
|
4331
|
+
this.selectedValues = Array.isArray(values) ? values : values ? [values] : [];
|
|
4332
|
+
if (this.listView) {
|
|
4333
|
+
this.listView.setValue(this.selectedValues);
|
|
4334
|
+
}
|
|
4335
|
+
this.updateButtonText();
|
|
4336
|
+
}
|
|
4337
|
+
/**
|
|
4338
|
+
* Update options list
|
|
4339
|
+
*/
|
|
4340
|
+
setOptions(options) {
|
|
4341
|
+
this.options = options;
|
|
4342
|
+
if (this.listView) {
|
|
4343
|
+
const items = this.options.map((option, index) => {
|
|
4344
|
+
const value = typeof option === "string" ? option : option.value;
|
|
4345
|
+
const label = typeof option === "string" ? option : option.label || option.text || option.value;
|
|
4346
|
+
const disabled = typeof option === "object" ? option.disabled : false;
|
|
4347
|
+
return {
|
|
4348
|
+
id: `${this.name}_${index}`,
|
|
4349
|
+
value,
|
|
4350
|
+
label,
|
|
4351
|
+
index,
|
|
4352
|
+
selected: this.selectedValues.includes(value),
|
|
4353
|
+
disabled
|
|
4354
|
+
};
|
|
4355
|
+
});
|
|
4356
|
+
this.listView.updateItems(items);
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
/**
|
|
4360
|
+
* Clear all selections
|
|
4361
|
+
*/
|
|
4362
|
+
clear() {
|
|
4363
|
+
this.setValue([]);
|
|
4364
|
+
}
|
|
4365
|
+
/**
|
|
4366
|
+
* Get form value (for form integration)
|
|
4367
|
+
*/
|
|
4368
|
+
getFormValue() {
|
|
4369
|
+
return this.getValue();
|
|
4370
|
+
}
|
|
4371
|
+
/**
|
|
4372
|
+
* Set form value (for form integration)
|
|
4373
|
+
*/
|
|
4374
|
+
setFormValue(value) {
|
|
4375
|
+
this.setValue(value);
|
|
4376
|
+
}
|
|
4377
|
+
/**
|
|
4378
|
+
* Cleanup child view on destroy
|
|
4379
|
+
*/
|
|
4380
|
+
async onBeforeDestroy() {
|
|
4381
|
+
await super.onBeforeDestroy();
|
|
4382
|
+
if (this.listView) {
|
|
4383
|
+
this.listView.destroy();
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4038
4387
|
class DatePicker extends View {
|
|
4039
4388
|
constructor(options = {}) {
|
|
4040
4389
|
const {
|
|
@@ -5773,6 +6122,240 @@ class ComboInput extends View {
|
|
|
5773
6122
|
return new ComboInput(options);
|
|
5774
6123
|
}
|
|
5775
6124
|
}
|
|
6125
|
+
class ComboBox extends View {
|
|
6126
|
+
constructor(options = {}) {
|
|
6127
|
+
super(options);
|
|
6128
|
+
this.name = options.name || "combo";
|
|
6129
|
+
this.placeholder = options.placeholder || options.placeHolder || "Type or select...";
|
|
6130
|
+
this.value = options.value || "";
|
|
6131
|
+
this.options = options.options || [];
|
|
6132
|
+
this.allowCustom = options.allowCustom !== false;
|
|
6133
|
+
this.disabled = options.disabled || false;
|
|
6134
|
+
this.required = options.required || false;
|
|
6135
|
+
this.maxHeight = options.maxHeight || 300;
|
|
6136
|
+
this.filteredOptions = [...this.options];
|
|
6137
|
+
this.highlightedIndex = -1;
|
|
6138
|
+
this.isOpen = false;
|
|
6139
|
+
this.template = `
|
|
6140
|
+
<div class="combobox-container">
|
|
6141
|
+
<div class="input-group">
|
|
6142
|
+
<input type="text"
|
|
6143
|
+
class="form-control combobox-input"
|
|
6144
|
+
placeholder="{{placeholder}}"
|
|
6145
|
+
value="{{value}}"
|
|
6146
|
+
{{#disabled}}disabled{{/disabled}}
|
|
6147
|
+
{{#required}}required{{/required}}
|
|
6148
|
+
data-action="combobox-input"
|
|
6149
|
+
autocomplete="off">
|
|
6150
|
+
<button class="btn btn-outline-secondary combobox-toggle"
|
|
6151
|
+
type="button"
|
|
6152
|
+
data-action="combobox-toggle"
|
|
6153
|
+
{{#disabled}}disabled{{/disabled}}>
|
|
6154
|
+
<i class="bi bi-chevron-down"></i>
|
|
6155
|
+
</button>
|
|
6156
|
+
</div>
|
|
6157
|
+
<div class="dropdown-menu combobox-dropdown"
|
|
6158
|
+
style="max-height: {{maxHeight}}px; overflow-y: auto; width: 100%;">
|
|
6159
|
+
<div data-region="dropdown-items"></div>
|
|
6160
|
+
{{^allowCustom}}
|
|
6161
|
+
<div class="combobox-no-match dropdown-item text-muted" style="display: none;">
|
|
6162
|
+
No matches found
|
|
6163
|
+
</div>
|
|
6164
|
+
{{/allowCustom}}
|
|
6165
|
+
</div>
|
|
6166
|
+
</div>
|
|
6167
|
+
`;
|
|
6168
|
+
this.itemTemplate = `
|
|
6169
|
+
{{#items}}
|
|
6170
|
+
<button type="button"
|
|
6171
|
+
class="dropdown-item combobox-item {{#highlighted}}active{{/highlighted}}"
|
|
6172
|
+
data-action="select-item"
|
|
6173
|
+
data-value="{{value}}"
|
|
6174
|
+
data-index="{{index}}">
|
|
6175
|
+
{{label}}
|
|
6176
|
+
</button>
|
|
6177
|
+
{{/items}}
|
|
6178
|
+
`;
|
|
6179
|
+
}
|
|
6180
|
+
async onInit() {
|
|
6181
|
+
await super.onInit();
|
|
6182
|
+
}
|
|
6183
|
+
async onAfterRender() {
|
|
6184
|
+
await super.onAfterRender();
|
|
6185
|
+
this.input = this.element.querySelector(".combobox-input");
|
|
6186
|
+
this.dropdown = this.element.querySelector(".combobox-dropdown");
|
|
6187
|
+
this.dropdownItems = this.element.querySelector('[data-region="dropdown-items"]');
|
|
6188
|
+
this.noMatchDiv = this.element.querySelector(".combobox-no-match");
|
|
6189
|
+
if (this.value && this.input) {
|
|
6190
|
+
const option = this.options.find((opt) => opt.value === this.value);
|
|
6191
|
+
if (option) {
|
|
6192
|
+
this.input.value = option.label || option.value;
|
|
6193
|
+
} else if (this.allowCustom) {
|
|
6194
|
+
this.input.value = this.value;
|
|
6195
|
+
}
|
|
6196
|
+
}
|
|
6197
|
+
this.renderItems();
|
|
6198
|
+
this.setupEventListeners();
|
|
6199
|
+
}
|
|
6200
|
+
setupEventListeners() {
|
|
6201
|
+
this.input.addEventListener("focus", () => this.openDropdown());
|
|
6202
|
+
this.input.addEventListener("input", (e) => this.handleInput(e));
|
|
6203
|
+
this.input.addEventListener("keydown", (e) => this.handleKeydown(e));
|
|
6204
|
+
document.addEventListener("click", (e) => {
|
|
6205
|
+
if (!this.element.contains(e.target)) {
|
|
6206
|
+
this.closeDropdown();
|
|
6207
|
+
}
|
|
6208
|
+
});
|
|
6209
|
+
}
|
|
6210
|
+
handleInput(event) {
|
|
6211
|
+
const searchText = event.target.value.toLowerCase();
|
|
6212
|
+
this.filteredOptions = this.options.filter((opt) => {
|
|
6213
|
+
const label = opt.label || opt.value;
|
|
6214
|
+
return label.toLowerCase().includes(searchText);
|
|
6215
|
+
});
|
|
6216
|
+
this.highlightedIndex = -1;
|
|
6217
|
+
this.renderItems();
|
|
6218
|
+
this.openDropdown();
|
|
6219
|
+
if (!this.allowCustom && this.noMatchDiv) {
|
|
6220
|
+
this.noMatchDiv.style.display = this.filteredOptions.length === 0 ? "block" : "none";
|
|
6221
|
+
}
|
|
6222
|
+
this.value = event.target.value;
|
|
6223
|
+
this.emit("change", { value: this.value });
|
|
6224
|
+
}
|
|
6225
|
+
handleKeydown(event) {
|
|
6226
|
+
if (!this.isOpen && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
|
|
6227
|
+
this.openDropdown();
|
|
6228
|
+
event.preventDefault();
|
|
6229
|
+
return;
|
|
6230
|
+
}
|
|
6231
|
+
if (!this.isOpen) return;
|
|
6232
|
+
switch (event.key) {
|
|
6233
|
+
case "ArrowDown":
|
|
6234
|
+
event.preventDefault();
|
|
6235
|
+
this.highlightedIndex = Math.min(this.highlightedIndex + 1, this.filteredOptions.length - 1);
|
|
6236
|
+
this.renderItems();
|
|
6237
|
+
this.scrollToHighlighted();
|
|
6238
|
+
break;
|
|
6239
|
+
case "ArrowUp":
|
|
6240
|
+
event.preventDefault();
|
|
6241
|
+
this.highlightedIndex = Math.max(this.highlightedIndex - 1, -1);
|
|
6242
|
+
this.renderItems();
|
|
6243
|
+
this.scrollToHighlighted();
|
|
6244
|
+
break;
|
|
6245
|
+
case "Enter":
|
|
6246
|
+
event.preventDefault();
|
|
6247
|
+
if (this.highlightedIndex >= 0) {
|
|
6248
|
+
this.selectItem(this.filteredOptions[this.highlightedIndex]);
|
|
6249
|
+
}
|
|
6250
|
+
break;
|
|
6251
|
+
case "Escape":
|
|
6252
|
+
event.preventDefault();
|
|
6253
|
+
this.closeDropdown();
|
|
6254
|
+
break;
|
|
6255
|
+
case "Tab":
|
|
6256
|
+
this.closeDropdown();
|
|
6257
|
+
break;
|
|
6258
|
+
}
|
|
6259
|
+
}
|
|
6260
|
+
scrollToHighlighted() {
|
|
6261
|
+
if (this.highlightedIndex < 0) return;
|
|
6262
|
+
const items = this.dropdownItems.querySelectorAll(".combobox-item");
|
|
6263
|
+
const highlightedItem = items[this.highlightedIndex];
|
|
6264
|
+
if (highlightedItem) {
|
|
6265
|
+
highlightedItem.scrollIntoView({ block: "nearest" });
|
|
6266
|
+
}
|
|
6267
|
+
}
|
|
6268
|
+
openDropdown() {
|
|
6269
|
+
if (this.disabled || this.isOpen) return;
|
|
6270
|
+
this.isOpen = true;
|
|
6271
|
+
this.dropdown.classList.add("show");
|
|
6272
|
+
if (this.input.value === "") {
|
|
6273
|
+
this.filteredOptions = [...this.options];
|
|
6274
|
+
this.renderItems();
|
|
6275
|
+
}
|
|
6276
|
+
}
|
|
6277
|
+
closeDropdown() {
|
|
6278
|
+
if (!this.isOpen) return;
|
|
6279
|
+
this.isOpen = false;
|
|
6280
|
+
this.dropdown.classList.remove("show");
|
|
6281
|
+
this.highlightedIndex = -1;
|
|
6282
|
+
if (!this.allowCustom) {
|
|
6283
|
+
const validOption = this.options.find(
|
|
6284
|
+
(opt) => opt.value === this.input.value || opt.label === this.input.value
|
|
6285
|
+
);
|
|
6286
|
+
if (!validOption && this.input.value !== "") {
|
|
6287
|
+
this.input.value = this.value;
|
|
6288
|
+
}
|
|
6289
|
+
}
|
|
6290
|
+
}
|
|
6291
|
+
selectItem(option) {
|
|
6292
|
+
const value = option.value;
|
|
6293
|
+
const label = option.label || option.value;
|
|
6294
|
+
this.input.value = label;
|
|
6295
|
+
this.value = value;
|
|
6296
|
+
this.closeDropdown();
|
|
6297
|
+
this.filteredOptions = [...this.options];
|
|
6298
|
+
this.highlightedIndex = -1;
|
|
6299
|
+
this.emit("change", { value: this.value, label });
|
|
6300
|
+
}
|
|
6301
|
+
renderItems() {
|
|
6302
|
+
const items = this.filteredOptions.map((opt, index) => ({
|
|
6303
|
+
value: opt.value,
|
|
6304
|
+
label: opt.label || opt.value,
|
|
6305
|
+
index,
|
|
6306
|
+
highlighted: index === this.highlightedIndex
|
|
6307
|
+
}));
|
|
6308
|
+
const html = Mustache.render(this.itemTemplate, { items });
|
|
6309
|
+
this.dropdownItems.innerHTML = html;
|
|
6310
|
+
}
|
|
6311
|
+
// Action handlers
|
|
6312
|
+
async onActionComboboxInput(event, element) {
|
|
6313
|
+
}
|
|
6314
|
+
async onActionComboboxToggle(event, element) {
|
|
6315
|
+
if (this.isOpen) {
|
|
6316
|
+
this.closeDropdown();
|
|
6317
|
+
} else {
|
|
6318
|
+
this.input.focus();
|
|
6319
|
+
this.openDropdown();
|
|
6320
|
+
}
|
|
6321
|
+
}
|
|
6322
|
+
async onActionSelectItem(event, element) {
|
|
6323
|
+
const value = element.getAttribute("data-value");
|
|
6324
|
+
const option = this.options.find((opt) => opt.value === value);
|
|
6325
|
+
if (option) {
|
|
6326
|
+
this.selectItem(option);
|
|
6327
|
+
}
|
|
6328
|
+
}
|
|
6329
|
+
// Form integration methods
|
|
6330
|
+
getValue() {
|
|
6331
|
+
return this.value;
|
|
6332
|
+
}
|
|
6333
|
+
setValue(value) {
|
|
6334
|
+
this.value = value;
|
|
6335
|
+
if (!this.input) {
|
|
6336
|
+
return;
|
|
6337
|
+
}
|
|
6338
|
+
const option = this.options.find((opt) => opt.value === value);
|
|
6339
|
+
if (option) {
|
|
6340
|
+
this.input.value = option.label || option.value;
|
|
6341
|
+
} else if (this.allowCustom) {
|
|
6342
|
+
this.input.value = value;
|
|
6343
|
+
}
|
|
6344
|
+
}
|
|
6345
|
+
setFormValue(value) {
|
|
6346
|
+
this.setValue(value);
|
|
6347
|
+
}
|
|
6348
|
+
getTemplateData() {
|
|
6349
|
+
return {
|
|
6350
|
+
placeholder: this.placeholder,
|
|
6351
|
+
value: this.input ? this.input.value : this.value,
|
|
6352
|
+
disabled: this.disabled,
|
|
6353
|
+
required: this.required,
|
|
6354
|
+
maxHeight: this.maxHeight,
|
|
6355
|
+
allowCustom: this.allowCustom
|
|
6356
|
+
};
|
|
6357
|
+
}
|
|
6358
|
+
}
|
|
5776
6359
|
class FormView extends View {
|
|
5777
6360
|
constructor(options = {}) {
|
|
5778
6361
|
const {
|
|
@@ -5929,6 +6512,8 @@ class FormView extends View {
|
|
|
5929
6512
|
this.initializeImageFields();
|
|
5930
6513
|
this.initializeCustomComponents();
|
|
5931
6514
|
this.initializeTagInputs();
|
|
6515
|
+
this.initializeMultiSelectDropdowns();
|
|
6516
|
+
this.initializeComboBoxes();
|
|
5932
6517
|
this.initializeCollectionSelects();
|
|
5933
6518
|
this.initializeCollectionMultiSelects();
|
|
5934
6519
|
this.initializeDatePickers();
|
|
@@ -6057,6 +6642,77 @@ class FormView extends View {
|
|
|
6057
6642
|
}
|
|
6058
6643
|
});
|
|
6059
6644
|
}
|
|
6645
|
+
/**
|
|
6646
|
+
* Initialize MultiSelectDropdown components
|
|
6647
|
+
*/
|
|
6648
|
+
initializeMultiSelectDropdowns() {
|
|
6649
|
+
const multiselectPlaceholders = this.element.querySelectorAll('[data-field-type="multiselect"]');
|
|
6650
|
+
multiselectPlaceholders.forEach((placeholder) => {
|
|
6651
|
+
try {
|
|
6652
|
+
const fieldName = placeholder.getAttribute("data-field-name");
|
|
6653
|
+
const configData = placeholder.getAttribute("data-field-config");
|
|
6654
|
+
const config = JSON.parse(configData);
|
|
6655
|
+
const fieldConfig = this.getFormFieldConfig(fieldName);
|
|
6656
|
+
if (!fieldConfig) {
|
|
6657
|
+
return;
|
|
6658
|
+
}
|
|
6659
|
+
const multiselect = new MultiSelectDropdown({
|
|
6660
|
+
...config,
|
|
6661
|
+
options: fieldConfig.options || [],
|
|
6662
|
+
placeholder: fieldConfig.placeholder || config.placeholder || "Select...",
|
|
6663
|
+
label: fieldConfig.label,
|
|
6664
|
+
containerId: null
|
|
6665
|
+
// We'll mount directly
|
|
6666
|
+
});
|
|
6667
|
+
let value = config.value ?? MOJOUtils.getContextData(this.data, fieldName);
|
|
6668
|
+
if (value) {
|
|
6669
|
+
multiselect.setFormValue(value);
|
|
6670
|
+
}
|
|
6671
|
+
multiselect.render(true, placeholder);
|
|
6672
|
+
this.customComponents.set(fieldName, multiselect);
|
|
6673
|
+
multiselect.on("change", (data) => {
|
|
6674
|
+
this.handleFieldChange(fieldName, data.value);
|
|
6675
|
+
});
|
|
6676
|
+
} catch (error) {
|
|
6677
|
+
console.error("MultiSelectDropdown initialization failed:", error);
|
|
6678
|
+
}
|
|
6679
|
+
});
|
|
6680
|
+
}
|
|
6681
|
+
/**
|
|
6682
|
+
* Initialize ComboBox components (autocomplete dropdowns)
|
|
6683
|
+
*/
|
|
6684
|
+
initializeComboBoxes() {
|
|
6685
|
+
const comboboxPlaceholders = this.element.querySelectorAll('[data-field-type="combobox"]');
|
|
6686
|
+
comboboxPlaceholders.forEach((placeholder) => {
|
|
6687
|
+
try {
|
|
6688
|
+
const fieldName = placeholder.getAttribute("data-field-name");
|
|
6689
|
+
const configData = placeholder.getAttribute("data-field-config");
|
|
6690
|
+
const config = JSON.parse(configData);
|
|
6691
|
+
const fieldConfig = this.getFormFieldConfig(fieldName);
|
|
6692
|
+
if (!fieldConfig) {
|
|
6693
|
+
return;
|
|
6694
|
+
}
|
|
6695
|
+
const combobox = new ComboBox({
|
|
6696
|
+
...config,
|
|
6697
|
+
options: fieldConfig.options || [],
|
|
6698
|
+
placeholder: fieldConfig.placeholder || config.placeholder || "Type or select...",
|
|
6699
|
+
containerId: null
|
|
6700
|
+
// We'll mount directly
|
|
6701
|
+
});
|
|
6702
|
+
let value = config.value ?? MOJOUtils.getContextData(this.data, fieldName);
|
|
6703
|
+
if (value) {
|
|
6704
|
+
combobox.setFormValue(value);
|
|
6705
|
+
}
|
|
6706
|
+
combobox.render(true, placeholder);
|
|
6707
|
+
this.customComponents.set(fieldName, combobox);
|
|
6708
|
+
combobox.on("change", (data) => {
|
|
6709
|
+
this.handleFieldChange(fieldName, data.value);
|
|
6710
|
+
});
|
|
6711
|
+
} catch (error) {
|
|
6712
|
+
console.error("ComboBox initialization failed:", error);
|
|
6713
|
+
}
|
|
6714
|
+
});
|
|
6715
|
+
}
|
|
6060
6716
|
/**
|
|
6061
6717
|
* Initialize CollectionSelect components
|
|
6062
6718
|
*/
|
|
@@ -7893,4 +8549,4 @@ export {
|
|
|
7893
8549
|
applyFileDropMixin as a,
|
|
7894
8550
|
FormView$1 as b
|
|
7895
8551
|
};
|
|
7896
|
-
//# sourceMappingURL=FormView-
|
|
8552
|
+
//# sourceMappingURL=FormView-B_90L1RY.js.map
|