@nyaruka/temba-components 0.142.1 → 0.142.3
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/CHANGELOG.md +19 -0
- package/dist/temba-components.js +953 -708
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/Icons.js +1 -0
- package/out-tsc/src/Icons.js.map +1 -1
- package/out-tsc/src/flow/CanvasMenu.js +38 -38
- package/out-tsc/src/flow/CanvasMenu.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +171 -17
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +491 -22
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +346 -10
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/NodeTypeSelector.js +2 -0
- package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -1
- package/out-tsc/src/flow/Plumber.js +92 -28
- package/out-tsc/src/flow/Plumber.js.map +1 -1
- package/out-tsc/src/flow/StickyNote.js +63 -3
- package/out-tsc/src/flow/StickyNote.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js +2 -6
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
- package/out-tsc/src/flow/actions/enter_flow.js +2 -2
- package/out-tsc/src/flow/actions/enter_flow.js.map +1 -1
- package/out-tsc/src/flow/actions/say_msg.js +2 -1
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/send_broadcast.js +2 -6
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
- package/out-tsc/src/flow/actions/send_email.js +2 -6
- package/out-tsc/src/flow/actions/send_email.js.map +1 -1
- package/out-tsc/src/flow/actions/send_msg.js +55 -35
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_field.js +4 -5
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js +3 -3
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_name.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +3 -3
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/actions/start_session.js +2 -2
- package/out-tsc/src/flow/actions/start_session.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm.js +4 -5
- package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_resthook.js +3 -8
- package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js +4 -2
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_webhook.js +25 -33
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +1 -0
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/flow/utils.js +66 -0
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +17 -2
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/interfaces.js +1 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/list/SortableList.js +104 -43
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/simulator/Simulator.js +6 -2
- package/out-tsc/src/simulator/Simulator.js.map +1 -1
- package/out-tsc/test/temba-canvas-menu.test.js +13 -9
- package/out-tsc/test/temba-canvas-menu.test.js.map +1 -1
- package/out-tsc/test/temba-flow-reflow.test.js.map +1 -1
- package/out-tsc/test/temba-node-editor.test.js +9 -10
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-node-type-selector.test.js +3 -3
- package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
- package/out-tsc/test/temba-simulator.test.js +2 -2
- package/out-tsc/test/temba-simulator.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/enter_flow/render/basic-flow.png +0 -0
- package/screenshots/truth/actions/enter_flow/render/long-flow-name.png +0 -0
- package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
- package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
- package/screenshots/truth/canvas-menu/open.png +0 -0
- package/screenshots/truth/node-type-selector/action-mode.png +0 -0
- package/screenshots/truth/node-type-selector/split-mode.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
- package/src/Icons.ts +1 -0
- package/src/flow/CanvasMenu.ts +50 -43
- package/src/flow/CanvasNode.ts +201 -17
- package/src/flow/Editor.ts +585 -25
- package/src/flow/NodeEditor.ts +373 -10
- package/src/flow/NodeTypeSelector.ts +2 -0
- package/src/flow/Plumber.ts +104 -37
- package/src/flow/StickyNote.ts +76 -4
- package/src/flow/actions/add_contact_urn.ts +5 -6
- package/src/flow/actions/enter_flow.ts +2 -2
- package/src/flow/actions/say_msg.ts +2 -1
- package/src/flow/actions/send_broadcast.ts +2 -6
- package/src/flow/actions/send_email.ts +2 -6
- package/src/flow/actions/send_msg.ts +59 -38
- package/src/flow/actions/set_contact_channel.ts +5 -1
- package/src/flow/actions/set_contact_field.ts +10 -5
- package/src/flow/actions/set_contact_language.ts +6 -3
- package/src/flow/actions/set_contact_name.ts +5 -1
- package/src/flow/actions/set_contact_status.ts +5 -1
- package/src/flow/actions/set_run_result.ts +6 -3
- package/src/flow/actions/start_session.ts +2 -2
- package/src/flow/nodes/split_by_llm.ts +5 -5
- package/src/flow/nodes/split_by_resthook.ts +3 -8
- package/src/flow/nodes/split_by_subflow.ts +4 -2
- package/src/flow/nodes/split_by_webhook.ts +26 -34
- package/src/flow/nodes/wait_for_response.ts +1 -0
- package/src/flow/types.ts +25 -2
- package/src/flow/utils.ts +79 -1
- package/src/form/FieldRenderer.ts +32 -3
- package/src/interfaces.ts +1 -0
- package/src/list/SortableList.ts +117 -47
- package/src/simulator/Simulator.ts +6 -2
- package/test/temba-canvas-menu.test.ts +13 -9
- package/test/temba-flow-reflow.test.ts +4 -2
- package/test/temba-node-editor.test.ts +9 -10
- package/test/temba-node-type-selector.test.ts +3 -3
- package/test/temba-simulator.test.ts +2 -2
|
@@ -150,6 +150,25 @@ export class NodeEditor extends RapidElement {
|
|
|
150
150
|
overflow: hidden;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
.form-group.no-border {
|
|
154
|
+
border: none;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.form-group.no-border > .form-group-header {
|
|
158
|
+
background: none;
|
|
159
|
+
border-bottom: none;
|
|
160
|
+
padding-left: 11px; /* 1px border + 10px padding to align with bordered groups */
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.form-group.no-border > .form-group-header:hover {
|
|
164
|
+
background: none;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.form-group.no-border > .form-group-content {
|
|
168
|
+
padding-left: 0;
|
|
169
|
+
padding-right: 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
153
172
|
.form-group.has-errors {
|
|
154
173
|
border-color: var(--color-error, tomato);
|
|
155
174
|
}
|
|
@@ -314,6 +333,133 @@ export class NodeEditor extends RapidElement {
|
|
|
314
333
|
align-items: center;
|
|
315
334
|
}
|
|
316
335
|
|
|
336
|
+
/* Accordion styles */
|
|
337
|
+
.accordion {
|
|
338
|
+
border: 1px solid #e0e0e0;
|
|
339
|
+
border-radius: 6px;
|
|
340
|
+
overflow: hidden;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.accordion-section {
|
|
344
|
+
border-bottom: 1px solid #e0e0e0;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.accordion-section:last-child {
|
|
348
|
+
border-bottom: none;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.accordion-header {
|
|
352
|
+
display: flex;
|
|
353
|
+
align-items: center;
|
|
354
|
+
justify-content: space-between;
|
|
355
|
+
padding: 6px 10px;
|
|
356
|
+
cursor: pointer;
|
|
357
|
+
user-select: none;
|
|
358
|
+
background: #f8f9fa;
|
|
359
|
+
transition: background 0.15s ease;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.accordion-header:hover {
|
|
363
|
+
background: #f0f1f2;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.accordion-section.expanded > .accordion-header {
|
|
367
|
+
border-bottom: 1px solid #e0e0e0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.accordion-title {
|
|
371
|
+
font-weight: 500;
|
|
372
|
+
font-size: 13px;
|
|
373
|
+
color: var(--color-label, #777);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.accordion-toggle-container {
|
|
377
|
+
position: relative;
|
|
378
|
+
display: flex;
|
|
379
|
+
align-items: center;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.accordion-toggle-icon {
|
|
383
|
+
color: #999;
|
|
384
|
+
transition: transform 0.2s ease, opacity 0.3s ease;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.accordion-toggle-icon.expanded {
|
|
388
|
+
transform: rotate(90deg);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.accordion-toggle-icon.faded {
|
|
392
|
+
opacity: 0;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.accordion-count-bubble {
|
|
396
|
+
border-radius: 50%;
|
|
397
|
+
display: flex;
|
|
398
|
+
align-items: center;
|
|
399
|
+
justify-content: center;
|
|
400
|
+
font-size: 10px;
|
|
401
|
+
font-weight: 600;
|
|
402
|
+
padding: 3px;
|
|
403
|
+
min-width: 10px;
|
|
404
|
+
min-height: 10px;
|
|
405
|
+
position: absolute;
|
|
406
|
+
top: 50%;
|
|
407
|
+
left: 50%;
|
|
408
|
+
transform: translate(-50%, -50%);
|
|
409
|
+
line-height: 0px;
|
|
410
|
+
opacity: 1;
|
|
411
|
+
transition: opacity 0.3s ease;
|
|
412
|
+
background: var(--color-bubble-bg, #fff);
|
|
413
|
+
border: 1px solid var(--color-bubble-border, #777);
|
|
414
|
+
color: var(--color-bubble-text, #000);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.accordion-count-bubble.hidden {
|
|
418
|
+
opacity: 0;
|
|
419
|
+
pointer-events: none;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.accordion-checkmark-icon {
|
|
423
|
+
position: absolute;
|
|
424
|
+
top: 50%;
|
|
425
|
+
left: 50%;
|
|
426
|
+
transform: translate(-50%, -50%);
|
|
427
|
+
opacity: 1;
|
|
428
|
+
transition: opacity 0.3s ease;
|
|
429
|
+
border-radius: 50%;
|
|
430
|
+
color: var(--color-bubble-text, #000);
|
|
431
|
+
background: var(--color-bubble-bg, #fff);
|
|
432
|
+
border: 1px solid var(--color-bubble-border, #777);
|
|
433
|
+
padding: 0.15em;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.accordion-checkmark-icon.hidden {
|
|
437
|
+
opacity: 0;
|
|
438
|
+
pointer-events: none;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.accordion-content {
|
|
442
|
+
padding: 8px 10px;
|
|
443
|
+
display: flex;
|
|
444
|
+
flex-direction: column;
|
|
445
|
+
gap: 8px;
|
|
446
|
+
overflow: hidden;
|
|
447
|
+
transition: all 0.2s ease-in-out;
|
|
448
|
+
opacity: 1;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.accordion-content.collapsed {
|
|
452
|
+
max-height: 0;
|
|
453
|
+
padding-top: 0;
|
|
454
|
+
padding-bottom: 0;
|
|
455
|
+
opacity: 0;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.accordion-error-icon {
|
|
459
|
+
color: var(--color-error, tomato);
|
|
460
|
+
margin-right: 6px;
|
|
461
|
+
}
|
|
462
|
+
|
|
317
463
|
.gutter-fields {
|
|
318
464
|
display: flex;
|
|
319
465
|
flex-direction: column;
|
|
@@ -1041,14 +1187,20 @@ export class NodeEditor extends RapidElement {
|
|
|
1041
1187
|
if (typeof item === 'object' && item.type === 'group') {
|
|
1042
1188
|
const { label, collapsed, collapsible } = item;
|
|
1043
1189
|
// Only update if the group is collapsible and has a function-based collapsed property
|
|
1190
|
+
// Skip reveal groups that have been expanded — they are one-way
|
|
1044
1191
|
if (collapsible && typeof collapsed === 'function') {
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1192
|
+
if (item.reveal && this.groupCollapseState[label] === false) {
|
|
1193
|
+
// Reveal group was manually expanded — don't re-collapse
|
|
1194
|
+
}
|
|
1195
|
+
else {
|
|
1196
|
+
const newCollapsedState = collapsed(this.formData);
|
|
1197
|
+
// Only update if the state has changed to avoid unnecessary re-renders
|
|
1198
|
+
if (this.groupCollapseState[label] !== newCollapsedState) {
|
|
1199
|
+
this.groupCollapseState = {
|
|
1200
|
+
...this.groupCollapseState,
|
|
1201
|
+
[label]: newCollapsedState
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1052
1204
|
}
|
|
1053
1205
|
}
|
|
1054
1206
|
// Recursively check nested items
|
|
@@ -1058,6 +1210,22 @@ export class NodeEditor extends RapidElement {
|
|
|
1058
1210
|
// Recursively check items in rows
|
|
1059
1211
|
this.updateGroupCollapseStatesRecursive(item.items);
|
|
1060
1212
|
}
|
|
1213
|
+
else if (typeof item === 'object' && item.type === 'accordion') {
|
|
1214
|
+
// Check each accordion section
|
|
1215
|
+
item.sections.forEach((section) => {
|
|
1216
|
+
const stateKey = `accordion:${section.label}`;
|
|
1217
|
+
if (typeof section.collapsed === 'function') {
|
|
1218
|
+
const newCollapsedState = section.collapsed(this.formData);
|
|
1219
|
+
if (this.groupCollapseState[stateKey] !== newCollapsedState) {
|
|
1220
|
+
this.groupCollapseState = {
|
|
1221
|
+
...this.groupCollapseState,
|
|
1222
|
+
[stateKey]: newCollapsedState
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
this.updateGroupCollapseStatesRecursive(section.items);
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1061
1229
|
});
|
|
1062
1230
|
}
|
|
1063
1231
|
updateComputedFields(changedFieldName) {
|
|
@@ -1242,6 +1410,10 @@ export class NodeEditor extends RapidElement {
|
|
|
1242
1410
|
const url = ((_b = (_a = picker.attachments) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.url) || '';
|
|
1243
1411
|
this.handleNewFieldChange(fieldName, url);
|
|
1244
1412
|
}
|
|
1413
|
+
else if (fieldName && config.type === 'template-editor') {
|
|
1414
|
+
// Special handling for template editor (manages template + template_variables)
|
|
1415
|
+
this.handleTemplateEditorChange(fieldName, e);
|
|
1416
|
+
}
|
|
1245
1417
|
else {
|
|
1246
1418
|
// Default handling for most field types
|
|
1247
1419
|
this.handleFormFieldChange(fieldName, e);
|
|
@@ -1250,7 +1422,8 @@ export class NodeEditor extends RapidElement {
|
|
|
1250
1422
|
showLabel: true,
|
|
1251
1423
|
formData: this.formData,
|
|
1252
1424
|
additionalData: {
|
|
1253
|
-
attachments: this.formData.attachments || []
|
|
1425
|
+
attachments: this.formData.attachments || [],
|
|
1426
|
+
template_variables: this.formData.template_variables || []
|
|
1254
1427
|
}
|
|
1255
1428
|
});
|
|
1256
1429
|
}
|
|
@@ -1298,6 +1471,20 @@ export class NodeEditor extends RapidElement {
|
|
|
1298
1471
|
// Recursively check items in rows
|
|
1299
1472
|
this.expandGroupsWithErrorsRecursive(item.items, errorFields);
|
|
1300
1473
|
}
|
|
1474
|
+
else if (typeof item === 'object' && item.type === 'accordion') {
|
|
1475
|
+
// Check each accordion section for errors
|
|
1476
|
+
item.sections.forEach((section) => {
|
|
1477
|
+
const fieldsInSection = this.collectFieldsFromItems(section.items);
|
|
1478
|
+
const sectionHasErrors = fieldsInSection.some((fieldName) => errorFields.has(fieldName));
|
|
1479
|
+
if (sectionHasErrors) {
|
|
1480
|
+
this.groupCollapseState = {
|
|
1481
|
+
...this.groupCollapseState,
|
|
1482
|
+
[`accordion:${section.label}`]: false
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
this.expandGroupsWithErrorsRecursive(section.items, errorFields);
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1301
1488
|
});
|
|
1302
1489
|
}
|
|
1303
1490
|
renderLayoutItem(item, config, renderedFields) {
|
|
@@ -1321,6 +1508,8 @@ export class NodeEditor extends RapidElement {
|
|
|
1321
1508
|
return this.renderRow(item, config, renderedFields);
|
|
1322
1509
|
case 'group':
|
|
1323
1510
|
return this.renderGroup(item, config, renderedFields);
|
|
1511
|
+
case 'accordion':
|
|
1512
|
+
return this.renderAccordion(item, config, renderedFields);
|
|
1324
1513
|
case 'spacer':
|
|
1325
1514
|
return html ``;
|
|
1326
1515
|
case 'text':
|
|
@@ -1416,7 +1605,7 @@ export class NodeEditor extends RapidElement {
|
|
|
1416
1605
|
}
|
|
1417
1606
|
renderGroup(groupConfig, config, renderedFields) {
|
|
1418
1607
|
var _a, _b;
|
|
1419
|
-
const { label, items, collapsible = false, collapsed = false, helpText, contentPadding, getGroupValueCount } = groupConfig;
|
|
1608
|
+
const { label, items, collapsible = false, collapsed = false, helpText, contentPadding, bordered = true, reveal = false, getGroupValueCount } = groupConfig;
|
|
1420
1609
|
// Initialize collapse state if not set
|
|
1421
1610
|
if (collapsible && !(label in this.groupCollapseState)) {
|
|
1422
1611
|
// Evaluate collapsed property - can be boolean or function
|
|
@@ -1429,6 +1618,10 @@ export class NodeEditor extends RapidElement {
|
|
|
1429
1618
|
const isCollapsed = collapsible
|
|
1430
1619
|
? (_a = this.groupCollapseState[label]) !== null && _a !== void 0 ? _a : (typeof collapsed === 'function' ? collapsed(this.formData) : collapsed)
|
|
1431
1620
|
: false;
|
|
1621
|
+
// Reveal mode: once expanded, render items directly without any group wrapper
|
|
1622
|
+
if (reveal && !isCollapsed) {
|
|
1623
|
+
return html `${items.map((item) => this.renderLayoutItem(item, config, renderedFields))}`;
|
|
1624
|
+
}
|
|
1432
1625
|
// Check if any field in this group has errors
|
|
1433
1626
|
const fieldsInGroup = this.collectFieldsFromItems(items);
|
|
1434
1627
|
const groupHasErrors = fieldsInGroup.some((fieldName) => this.errors[fieldName]);
|
|
@@ -1463,7 +1656,7 @@ export class NodeEditor extends RapidElement {
|
|
|
1463
1656
|
? 'has-errors'
|
|
1464
1657
|
: ''} ${isCollapsed ? 'collapsed' : 'expanded'} ${hasValue
|
|
1465
1658
|
? 'has-bubble'
|
|
1466
|
-
: ''}"
|
|
1659
|
+
: ''} ${!bordered ? 'no-border' : ''}"
|
|
1467
1660
|
>
|
|
1468
1661
|
<div
|
|
1469
1662
|
class="form-group-header ${collapsible ? 'clickable' : ''}"
|
|
@@ -1528,6 +1721,131 @@ export class NodeEditor extends RapidElement {
|
|
|
1528
1721
|
</div>
|
|
1529
1722
|
`;
|
|
1530
1723
|
}
|
|
1724
|
+
renderAccordion(accordionConfig, config, renderedFields) {
|
|
1725
|
+
const { sections, multi = false } = accordionConfig;
|
|
1726
|
+
return html `
|
|
1727
|
+
<div class="accordion">
|
|
1728
|
+
${sections.map((section) => {
|
|
1729
|
+
var _a, _b;
|
|
1730
|
+
const { label, collapsed = true, getValueCount } = section;
|
|
1731
|
+
const stateKey = `accordion:${label}`;
|
|
1732
|
+
// Initialize collapse state if not set
|
|
1733
|
+
if (!(stateKey in this.groupCollapseState)) {
|
|
1734
|
+
const initialCollapsed = typeof collapsed === 'function'
|
|
1735
|
+
? collapsed(this.formData)
|
|
1736
|
+
: collapsed;
|
|
1737
|
+
this.groupCollapseState = {
|
|
1738
|
+
...this.groupCollapseState,
|
|
1739
|
+
[stateKey]: initialCollapsed
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
const isCollapsed = (_a = this.groupCollapseState[stateKey]) !== null && _a !== void 0 ? _a : true;
|
|
1743
|
+
const isHovered = (_b = this.groupHoverState[stateKey]) !== null && _b !== void 0 ? _b : false;
|
|
1744
|
+
// Check for errors in this section
|
|
1745
|
+
const fieldsInSection = this.collectFieldsFromItems(section.items);
|
|
1746
|
+
const sectionHasErrors = fieldsInSection.some((fieldName) => this.errors[fieldName]);
|
|
1747
|
+
// Value count / checkmark display
|
|
1748
|
+
let valueCount = 0;
|
|
1749
|
+
let showBubble = false;
|
|
1750
|
+
let showCheckmark = false;
|
|
1751
|
+
let hasValue = false;
|
|
1752
|
+
if (getValueCount) {
|
|
1753
|
+
try {
|
|
1754
|
+
const result = getValueCount(this.formData);
|
|
1755
|
+
if (typeof result === 'boolean') {
|
|
1756
|
+
hasValue = result;
|
|
1757
|
+
showCheckmark = result && isCollapsed && !isHovered;
|
|
1758
|
+
}
|
|
1759
|
+
else if (typeof result === 'number') {
|
|
1760
|
+
valueCount = result;
|
|
1761
|
+
hasValue = valueCount > 0;
|
|
1762
|
+
showBubble = valueCount > 0 && isCollapsed && !isHovered;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
catch (error) {
|
|
1766
|
+
// ignore
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
return html `
|
|
1770
|
+
<div
|
|
1771
|
+
class="accordion-section ${isCollapsed
|
|
1772
|
+
? 'collapsed'
|
|
1773
|
+
: 'expanded'} ${hasValue ? 'has-value' : ''}"
|
|
1774
|
+
>
|
|
1775
|
+
<div
|
|
1776
|
+
class="accordion-header"
|
|
1777
|
+
@click=${() => this.handleAccordionToggle(stateKey, sections, multi)}
|
|
1778
|
+
@mouseenter=${() => this.handleGroupMouseEnter(stateKey)}
|
|
1779
|
+
@mouseleave=${() => this.handleGroupMouseLeave(stateKey)}
|
|
1780
|
+
>
|
|
1781
|
+
<div class="accordion-title">${label}</div>
|
|
1782
|
+
${sectionHasErrors
|
|
1783
|
+
? html `<temba-icon
|
|
1784
|
+
name="alert_warning"
|
|
1785
|
+
class="accordion-error-icon"
|
|
1786
|
+
size="1.2"
|
|
1787
|
+
></temba-icon>`
|
|
1788
|
+
: html `<div class="accordion-toggle-container">
|
|
1789
|
+
<temba-icon
|
|
1790
|
+
name="arrow_right"
|
|
1791
|
+
size="1.2"
|
|
1792
|
+
class="accordion-toggle-icon ${isCollapsed
|
|
1793
|
+
? 'collapsed'
|
|
1794
|
+
: 'expanded'} ${showBubble || showCheckmark
|
|
1795
|
+
? 'faded'
|
|
1796
|
+
: ''}"
|
|
1797
|
+
></temba-icon>
|
|
1798
|
+
${showCheckmark
|
|
1799
|
+
? html `<temba-icon
|
|
1800
|
+
name="check"
|
|
1801
|
+
size="0.8"
|
|
1802
|
+
class="accordion-checkmark-icon"
|
|
1803
|
+
></temba-icon>`
|
|
1804
|
+
: showBubble
|
|
1805
|
+
? html `<div
|
|
1806
|
+
class="accordion-count-bubble ${!showBubble
|
|
1807
|
+
? 'hidden'
|
|
1808
|
+
: ''}"
|
|
1809
|
+
>
|
|
1810
|
+
${valueCount}
|
|
1811
|
+
</div>`
|
|
1812
|
+
: ''}
|
|
1813
|
+
</div>`}
|
|
1814
|
+
</div>
|
|
1815
|
+
<div
|
|
1816
|
+
class="accordion-content ${isCollapsed
|
|
1817
|
+
? 'collapsed'
|
|
1818
|
+
: 'expanded'}"
|
|
1819
|
+
>
|
|
1820
|
+
${section.items.map((item) => this.renderLayoutItem(item, config, renderedFields))}
|
|
1821
|
+
</div>
|
|
1822
|
+
</div>
|
|
1823
|
+
`;
|
|
1824
|
+
})}
|
|
1825
|
+
</div>
|
|
1826
|
+
`;
|
|
1827
|
+
}
|
|
1828
|
+
handleAccordionToggle(stateKey, sections, multi) {
|
|
1829
|
+
var _a;
|
|
1830
|
+
const isCurrentlyCollapsed = (_a = this.groupCollapseState[stateKey]) !== null && _a !== void 0 ? _a : true;
|
|
1831
|
+
if (multi) {
|
|
1832
|
+
// Multi mode: just toggle this section
|
|
1833
|
+
this.groupCollapseState = {
|
|
1834
|
+
...this.groupCollapseState,
|
|
1835
|
+
[stateKey]: !isCurrentlyCollapsed
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1838
|
+
else {
|
|
1839
|
+
// Single mode: collapse all other sections, toggle this one
|
|
1840
|
+
const newState = { ...this.groupCollapseState };
|
|
1841
|
+
sections.forEach((section) => {
|
|
1842
|
+
const key = `accordion:${section.label}`;
|
|
1843
|
+
newState[key] = true; // collapse all
|
|
1844
|
+
});
|
|
1845
|
+
newState[stateKey] = !isCurrentlyCollapsed; // toggle clicked
|
|
1846
|
+
this.groupCollapseState = newState;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1531
1849
|
collectFieldsFromItems(items) {
|
|
1532
1850
|
const fields = [];
|
|
1533
1851
|
items.forEach((item) => {
|
|
@@ -1543,6 +1861,11 @@ export class NodeEditor extends RapidElement {
|
|
|
1543
1861
|
else if (item.type === 'group') {
|
|
1544
1862
|
fields.push(...this.collectFieldsFromItems(item.items));
|
|
1545
1863
|
}
|
|
1864
|
+
else if (item.type === 'accordion') {
|
|
1865
|
+
item.sections.forEach((section) => {
|
|
1866
|
+
fields.push(...this.collectFieldsFromItems(section.items));
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1546
1869
|
});
|
|
1547
1870
|
return fields;
|
|
1548
1871
|
}
|
|
@@ -1570,6 +1893,19 @@ export class NodeEditor extends RapidElement {
|
|
|
1570
1893
|
// Trigger re-render
|
|
1571
1894
|
this.requestUpdate();
|
|
1572
1895
|
}
|
|
1896
|
+
handleTemplateEditorChange(fieldName, event) {
|
|
1897
|
+
const customEvent = event;
|
|
1898
|
+
const detail = customEvent.detail;
|
|
1899
|
+
this.formData = {
|
|
1900
|
+
...this.formData,
|
|
1901
|
+
[fieldName]: detail.template
|
|
1902
|
+
? { uuid: detail.template.uuid, name: detail.template.name }
|
|
1903
|
+
: null,
|
|
1904
|
+
template_variables: detail.variables || []
|
|
1905
|
+
};
|
|
1906
|
+
this.updateGroupCollapseStates();
|
|
1907
|
+
this.requestUpdate();
|
|
1908
|
+
}
|
|
1573
1909
|
handleMessageEditorChange(fieldName, event) {
|
|
1574
1910
|
const target = event.target;
|
|
1575
1911
|
// Update both text and attachments from the message editor
|