@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
package/src/flow/NodeEditor.ts
CHANGED
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
LayoutItem,
|
|
15
15
|
RowLayoutConfig,
|
|
16
16
|
GroupLayoutConfig,
|
|
17
|
+
AccordionLayoutConfig,
|
|
18
|
+
AccordionSection,
|
|
17
19
|
FormData,
|
|
18
20
|
ACTION_GROUP_METADATA,
|
|
19
21
|
SPLIT_GROUP_METADATA
|
|
@@ -155,6 +157,25 @@ export class NodeEditor extends RapidElement {
|
|
|
155
157
|
overflow: hidden;
|
|
156
158
|
}
|
|
157
159
|
|
|
160
|
+
.form-group.no-border {
|
|
161
|
+
border: none;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.form-group.no-border > .form-group-header {
|
|
165
|
+
background: none;
|
|
166
|
+
border-bottom: none;
|
|
167
|
+
padding-left: 11px; /* 1px border + 10px padding to align with bordered groups */
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.form-group.no-border > .form-group-header:hover {
|
|
171
|
+
background: none;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.form-group.no-border > .form-group-content {
|
|
175
|
+
padding-left: 0;
|
|
176
|
+
padding-right: 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
158
179
|
.form-group.has-errors {
|
|
159
180
|
border-color: var(--color-error, tomato);
|
|
160
181
|
}
|
|
@@ -319,6 +340,133 @@ export class NodeEditor extends RapidElement {
|
|
|
319
340
|
align-items: center;
|
|
320
341
|
}
|
|
321
342
|
|
|
343
|
+
/* Accordion styles */
|
|
344
|
+
.accordion {
|
|
345
|
+
border: 1px solid #e0e0e0;
|
|
346
|
+
border-radius: 6px;
|
|
347
|
+
overflow: hidden;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.accordion-section {
|
|
351
|
+
border-bottom: 1px solid #e0e0e0;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.accordion-section:last-child {
|
|
355
|
+
border-bottom: none;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.accordion-header {
|
|
359
|
+
display: flex;
|
|
360
|
+
align-items: center;
|
|
361
|
+
justify-content: space-between;
|
|
362
|
+
padding: 6px 10px;
|
|
363
|
+
cursor: pointer;
|
|
364
|
+
user-select: none;
|
|
365
|
+
background: #f8f9fa;
|
|
366
|
+
transition: background 0.15s ease;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.accordion-header:hover {
|
|
370
|
+
background: #f0f1f2;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.accordion-section.expanded > .accordion-header {
|
|
374
|
+
border-bottom: 1px solid #e0e0e0;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.accordion-title {
|
|
378
|
+
font-weight: 500;
|
|
379
|
+
font-size: 13px;
|
|
380
|
+
color: var(--color-label, #777);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.accordion-toggle-container {
|
|
384
|
+
position: relative;
|
|
385
|
+
display: flex;
|
|
386
|
+
align-items: center;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.accordion-toggle-icon {
|
|
390
|
+
color: #999;
|
|
391
|
+
transition: transform 0.2s ease, opacity 0.3s ease;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.accordion-toggle-icon.expanded {
|
|
395
|
+
transform: rotate(90deg);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.accordion-toggle-icon.faded {
|
|
399
|
+
opacity: 0;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.accordion-count-bubble {
|
|
403
|
+
border-radius: 50%;
|
|
404
|
+
display: flex;
|
|
405
|
+
align-items: center;
|
|
406
|
+
justify-content: center;
|
|
407
|
+
font-size: 10px;
|
|
408
|
+
font-weight: 600;
|
|
409
|
+
padding: 3px;
|
|
410
|
+
min-width: 10px;
|
|
411
|
+
min-height: 10px;
|
|
412
|
+
position: absolute;
|
|
413
|
+
top: 50%;
|
|
414
|
+
left: 50%;
|
|
415
|
+
transform: translate(-50%, -50%);
|
|
416
|
+
line-height: 0px;
|
|
417
|
+
opacity: 1;
|
|
418
|
+
transition: opacity 0.3s ease;
|
|
419
|
+
background: var(--color-bubble-bg, #fff);
|
|
420
|
+
border: 1px solid var(--color-bubble-border, #777);
|
|
421
|
+
color: var(--color-bubble-text, #000);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.accordion-count-bubble.hidden {
|
|
425
|
+
opacity: 0;
|
|
426
|
+
pointer-events: none;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.accordion-checkmark-icon {
|
|
430
|
+
position: absolute;
|
|
431
|
+
top: 50%;
|
|
432
|
+
left: 50%;
|
|
433
|
+
transform: translate(-50%, -50%);
|
|
434
|
+
opacity: 1;
|
|
435
|
+
transition: opacity 0.3s ease;
|
|
436
|
+
border-radius: 50%;
|
|
437
|
+
color: var(--color-bubble-text, #000);
|
|
438
|
+
background: var(--color-bubble-bg, #fff);
|
|
439
|
+
border: 1px solid var(--color-bubble-border, #777);
|
|
440
|
+
padding: 0.15em;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.accordion-checkmark-icon.hidden {
|
|
444
|
+
opacity: 0;
|
|
445
|
+
pointer-events: none;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.accordion-content {
|
|
449
|
+
padding: 8px 10px;
|
|
450
|
+
display: flex;
|
|
451
|
+
flex-direction: column;
|
|
452
|
+
gap: 8px;
|
|
453
|
+
overflow: hidden;
|
|
454
|
+
transition: all 0.2s ease-in-out;
|
|
455
|
+
opacity: 1;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.accordion-content.collapsed {
|
|
459
|
+
max-height: 0;
|
|
460
|
+
padding-top: 0;
|
|
461
|
+
padding-bottom: 0;
|
|
462
|
+
opacity: 0;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.accordion-error-icon {
|
|
466
|
+
color: var(--color-error, tomato);
|
|
467
|
+
margin-right: 6px;
|
|
468
|
+
}
|
|
469
|
+
|
|
322
470
|
.gutter-fields {
|
|
323
471
|
display: flex;
|
|
324
472
|
flex-direction: column;
|
|
@@ -1261,15 +1409,20 @@ export class NodeEditor extends RapidElement {
|
|
|
1261
1409
|
const { label, collapsed, collapsible } = item;
|
|
1262
1410
|
|
|
1263
1411
|
// Only update if the group is collapsible and has a function-based collapsed property
|
|
1412
|
+
// Skip reveal groups that have been expanded — they are one-way
|
|
1264
1413
|
if (collapsible && typeof collapsed === 'function') {
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1414
|
+
if (item.reveal && this.groupCollapseState[label] === false) {
|
|
1415
|
+
// Reveal group was manually expanded — don't re-collapse
|
|
1416
|
+
} else {
|
|
1417
|
+
const newCollapsedState = collapsed(this.formData);
|
|
1418
|
+
|
|
1419
|
+
// Only update if the state has changed to avoid unnecessary re-renders
|
|
1420
|
+
if (this.groupCollapseState[label] !== newCollapsedState) {
|
|
1421
|
+
this.groupCollapseState = {
|
|
1422
|
+
...this.groupCollapseState,
|
|
1423
|
+
[label]: newCollapsedState
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1273
1426
|
}
|
|
1274
1427
|
}
|
|
1275
1428
|
|
|
@@ -1278,6 +1431,21 @@ export class NodeEditor extends RapidElement {
|
|
|
1278
1431
|
} else if (typeof item === 'object' && item.type === 'row') {
|
|
1279
1432
|
// Recursively check items in rows
|
|
1280
1433
|
this.updateGroupCollapseStatesRecursive(item.items);
|
|
1434
|
+
} else if (typeof item === 'object' && item.type === 'accordion') {
|
|
1435
|
+
// Check each accordion section
|
|
1436
|
+
item.sections.forEach((section) => {
|
|
1437
|
+
const stateKey = `accordion:${section.label}`;
|
|
1438
|
+
if (typeof section.collapsed === 'function') {
|
|
1439
|
+
const newCollapsedState = section.collapsed(this.formData);
|
|
1440
|
+
if (this.groupCollapseState[stateKey] !== newCollapsedState) {
|
|
1441
|
+
this.groupCollapseState = {
|
|
1442
|
+
...this.groupCollapseState,
|
|
1443
|
+
[stateKey]: newCollapsedState
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
this.updateGroupCollapseStatesRecursive(section.items);
|
|
1448
|
+
});
|
|
1281
1449
|
}
|
|
1282
1450
|
});
|
|
1283
1451
|
}
|
|
@@ -1518,6 +1686,9 @@ export class NodeEditor extends RapidElement {
|
|
|
1518
1686
|
const picker = e.target as any;
|
|
1519
1687
|
const url = picker.attachments?.[0]?.url || '';
|
|
1520
1688
|
this.handleNewFieldChange(fieldName, url);
|
|
1689
|
+
} else if (fieldName && config.type === 'template-editor') {
|
|
1690
|
+
// Special handling for template editor (manages template + template_variables)
|
|
1691
|
+
this.handleTemplateEditorChange(fieldName, e);
|
|
1521
1692
|
} else {
|
|
1522
1693
|
// Default handling for most field types
|
|
1523
1694
|
this.handleFormFieldChange(fieldName, e);
|
|
@@ -1526,7 +1697,8 @@ export class NodeEditor extends RapidElement {
|
|
|
1526
1697
|
showLabel: true,
|
|
1527
1698
|
formData: this.formData,
|
|
1528
1699
|
additionalData: {
|
|
1529
|
-
attachments: this.formData.attachments || []
|
|
1700
|
+
attachments: this.formData.attachments || [],
|
|
1701
|
+
template_variables: this.formData.template_variables || []
|
|
1530
1702
|
}
|
|
1531
1703
|
});
|
|
1532
1704
|
}
|
|
@@ -1584,6 +1756,21 @@ export class NodeEditor extends RapidElement {
|
|
|
1584
1756
|
} else if (typeof item === 'object' && item.type === 'row') {
|
|
1585
1757
|
// Recursively check items in rows
|
|
1586
1758
|
this.expandGroupsWithErrorsRecursive(item.items, errorFields);
|
|
1759
|
+
} else if (typeof item === 'object' && item.type === 'accordion') {
|
|
1760
|
+
// Check each accordion section for errors
|
|
1761
|
+
item.sections.forEach((section) => {
|
|
1762
|
+
const fieldsInSection = this.collectFieldsFromItems(section.items);
|
|
1763
|
+
const sectionHasErrors = fieldsInSection.some((fieldName) =>
|
|
1764
|
+
errorFields.has(fieldName)
|
|
1765
|
+
);
|
|
1766
|
+
if (sectionHasErrors) {
|
|
1767
|
+
this.groupCollapseState = {
|
|
1768
|
+
...this.groupCollapseState,
|
|
1769
|
+
[`accordion:${section.label}`]: false
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
this.expandGroupsWithErrorsRecursive(section.items, errorFields);
|
|
1773
|
+
});
|
|
1587
1774
|
}
|
|
1588
1775
|
});
|
|
1589
1776
|
}
|
|
@@ -1631,6 +1818,9 @@ export class NodeEditor extends RapidElement {
|
|
|
1631
1818
|
case 'group':
|
|
1632
1819
|
return this.renderGroup(item, config, renderedFields);
|
|
1633
1820
|
|
|
1821
|
+
case 'accordion':
|
|
1822
|
+
return this.renderAccordion(item, config, renderedFields);
|
|
1823
|
+
|
|
1634
1824
|
case 'spacer':
|
|
1635
1825
|
return html``;
|
|
1636
1826
|
|
|
@@ -1771,6 +1961,8 @@ export class NodeEditor extends RapidElement {
|
|
|
1771
1961
|
collapsed = false,
|
|
1772
1962
|
helpText,
|
|
1773
1963
|
contentPadding,
|
|
1964
|
+
bordered = true,
|
|
1965
|
+
reveal = false,
|
|
1774
1966
|
getGroupValueCount
|
|
1775
1967
|
} = groupConfig;
|
|
1776
1968
|
|
|
@@ -1791,6 +1983,13 @@ export class NodeEditor extends RapidElement {
|
|
|
1791
1983
|
(typeof collapsed === 'function' ? collapsed(this.formData) : collapsed)
|
|
1792
1984
|
: false;
|
|
1793
1985
|
|
|
1986
|
+
// Reveal mode: once expanded, render items directly without any group wrapper
|
|
1987
|
+
if (reveal && !isCollapsed) {
|
|
1988
|
+
return html`${items.map((item) =>
|
|
1989
|
+
this.renderLayoutItem(item, config, renderedFields)
|
|
1990
|
+
)}`;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1794
1993
|
// Check if any field in this group has errors
|
|
1795
1994
|
const fieldsInGroup = this.collectFieldsFromItems(items);
|
|
1796
1995
|
const groupHasErrors = fieldsInGroup.some(
|
|
@@ -1832,7 +2031,7 @@ export class NodeEditor extends RapidElement {
|
|
|
1832
2031
|
? 'has-errors'
|
|
1833
2032
|
: ''} ${isCollapsed ? 'collapsed' : 'expanded'} ${hasValue
|
|
1834
2033
|
? 'has-bubble'
|
|
1835
|
-
: ''}"
|
|
2034
|
+
: ''} ${!bordered ? 'no-border' : ''}"
|
|
1836
2035
|
>
|
|
1837
2036
|
<div
|
|
1838
2037
|
class="form-group-header ${collapsible ? 'clickable' : ''}"
|
|
@@ -1900,6 +2099,150 @@ export class NodeEditor extends RapidElement {
|
|
|
1900
2099
|
`;
|
|
1901
2100
|
}
|
|
1902
2101
|
|
|
2102
|
+
private renderAccordion(
|
|
2103
|
+
accordionConfig: AccordionLayoutConfig,
|
|
2104
|
+
config: ActionConfig | NodeConfig,
|
|
2105
|
+
renderedFields: Set<string>
|
|
2106
|
+
): TemplateResult {
|
|
2107
|
+
const { sections, multi = false } = accordionConfig;
|
|
2108
|
+
|
|
2109
|
+
return html`
|
|
2110
|
+
<div class="accordion">
|
|
2111
|
+
${sections.map((section) => {
|
|
2112
|
+
const { label, collapsed = true, getValueCount } = section;
|
|
2113
|
+
const stateKey = `accordion:${label}`;
|
|
2114
|
+
|
|
2115
|
+
// Initialize collapse state if not set
|
|
2116
|
+
if (!(stateKey in this.groupCollapseState)) {
|
|
2117
|
+
const initialCollapsed =
|
|
2118
|
+
typeof collapsed === 'function'
|
|
2119
|
+
? collapsed(this.formData)
|
|
2120
|
+
: collapsed;
|
|
2121
|
+
this.groupCollapseState = {
|
|
2122
|
+
...this.groupCollapseState,
|
|
2123
|
+
[stateKey]: initialCollapsed
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
const isCollapsed = this.groupCollapseState[stateKey] ?? true;
|
|
2128
|
+
const isHovered = this.groupHoverState[stateKey] ?? false;
|
|
2129
|
+
|
|
2130
|
+
// Check for errors in this section
|
|
2131
|
+
const fieldsInSection = this.collectFieldsFromItems(section.items);
|
|
2132
|
+
const sectionHasErrors = fieldsInSection.some(
|
|
2133
|
+
(fieldName) => this.errors[fieldName]
|
|
2134
|
+
);
|
|
2135
|
+
|
|
2136
|
+
// Value count / checkmark display
|
|
2137
|
+
let valueCount = 0;
|
|
2138
|
+
let showBubble = false;
|
|
2139
|
+
let showCheckmark = false;
|
|
2140
|
+
let hasValue = false;
|
|
2141
|
+
|
|
2142
|
+
if (getValueCount) {
|
|
2143
|
+
try {
|
|
2144
|
+
const result = getValueCount(this.formData);
|
|
2145
|
+
if (typeof result === 'boolean') {
|
|
2146
|
+
hasValue = result;
|
|
2147
|
+
showCheckmark = result && isCollapsed && !isHovered;
|
|
2148
|
+
} else if (typeof result === 'number') {
|
|
2149
|
+
valueCount = result;
|
|
2150
|
+
hasValue = valueCount > 0;
|
|
2151
|
+
showBubble = valueCount > 0 && isCollapsed && !isHovered;
|
|
2152
|
+
}
|
|
2153
|
+
} catch (error) {
|
|
2154
|
+
// ignore
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
return html`
|
|
2159
|
+
<div
|
|
2160
|
+
class="accordion-section ${isCollapsed
|
|
2161
|
+
? 'collapsed'
|
|
2162
|
+
: 'expanded'} ${hasValue ? 'has-value' : ''}"
|
|
2163
|
+
>
|
|
2164
|
+
<div
|
|
2165
|
+
class="accordion-header"
|
|
2166
|
+
@click=${() =>
|
|
2167
|
+
this.handleAccordionToggle(stateKey, sections, multi)}
|
|
2168
|
+
@mouseenter=${() => this.handleGroupMouseEnter(stateKey)}
|
|
2169
|
+
@mouseleave=${() => this.handleGroupMouseLeave(stateKey)}
|
|
2170
|
+
>
|
|
2171
|
+
<div class="accordion-title">${label}</div>
|
|
2172
|
+
${sectionHasErrors
|
|
2173
|
+
? html`<temba-icon
|
|
2174
|
+
name="alert_warning"
|
|
2175
|
+
class="accordion-error-icon"
|
|
2176
|
+
size="1.2"
|
|
2177
|
+
></temba-icon>`
|
|
2178
|
+
: html`<div class="accordion-toggle-container">
|
|
2179
|
+
<temba-icon
|
|
2180
|
+
name="arrow_right"
|
|
2181
|
+
size="1.2"
|
|
2182
|
+
class="accordion-toggle-icon ${isCollapsed
|
|
2183
|
+
? 'collapsed'
|
|
2184
|
+
: 'expanded'} ${showBubble || showCheckmark
|
|
2185
|
+
? 'faded'
|
|
2186
|
+
: ''}"
|
|
2187
|
+
></temba-icon>
|
|
2188
|
+
${showCheckmark
|
|
2189
|
+
? html`<temba-icon
|
|
2190
|
+
name="check"
|
|
2191
|
+
size="0.8"
|
|
2192
|
+
class="accordion-checkmark-icon"
|
|
2193
|
+
></temba-icon>`
|
|
2194
|
+
: showBubble
|
|
2195
|
+
? html`<div
|
|
2196
|
+
class="accordion-count-bubble ${!showBubble
|
|
2197
|
+
? 'hidden'
|
|
2198
|
+
: ''}"
|
|
2199
|
+
>
|
|
2200
|
+
${valueCount}
|
|
2201
|
+
</div>`
|
|
2202
|
+
: ''}
|
|
2203
|
+
</div>`}
|
|
2204
|
+
</div>
|
|
2205
|
+
<div
|
|
2206
|
+
class="accordion-content ${isCollapsed
|
|
2207
|
+
? 'collapsed'
|
|
2208
|
+
: 'expanded'}"
|
|
2209
|
+
>
|
|
2210
|
+
${section.items.map((item) =>
|
|
2211
|
+
this.renderLayoutItem(item, config, renderedFields)
|
|
2212
|
+
)}
|
|
2213
|
+
</div>
|
|
2214
|
+
</div>
|
|
2215
|
+
`;
|
|
2216
|
+
})}
|
|
2217
|
+
</div>
|
|
2218
|
+
`;
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
private handleAccordionToggle(
|
|
2222
|
+
stateKey: string,
|
|
2223
|
+
sections: AccordionSection[],
|
|
2224
|
+
multi: boolean
|
|
2225
|
+
): void {
|
|
2226
|
+
const isCurrentlyCollapsed = this.groupCollapseState[stateKey] ?? true;
|
|
2227
|
+
|
|
2228
|
+
if (multi) {
|
|
2229
|
+
// Multi mode: just toggle this section
|
|
2230
|
+
this.groupCollapseState = {
|
|
2231
|
+
...this.groupCollapseState,
|
|
2232
|
+
[stateKey]: !isCurrentlyCollapsed
|
|
2233
|
+
};
|
|
2234
|
+
} else {
|
|
2235
|
+
// Single mode: collapse all other sections, toggle this one
|
|
2236
|
+
const newState = { ...this.groupCollapseState };
|
|
2237
|
+
sections.forEach((section) => {
|
|
2238
|
+
const key = `accordion:${section.label}`;
|
|
2239
|
+
newState[key] = true; // collapse all
|
|
2240
|
+
});
|
|
2241
|
+
newState[stateKey] = !isCurrentlyCollapsed; // toggle clicked
|
|
2242
|
+
this.groupCollapseState = newState;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
|
|
1903
2246
|
private collectFieldsFromItems(items: LayoutItem[]): string[] {
|
|
1904
2247
|
const fields: string[] = [];
|
|
1905
2248
|
|
|
@@ -1912,6 +2255,10 @@ export class NodeEditor extends RapidElement {
|
|
|
1912
2255
|
fields.push(...this.collectFieldsFromItems(item.items));
|
|
1913
2256
|
} else if (item.type === 'group') {
|
|
1914
2257
|
fields.push(...this.collectFieldsFromItems(item.items));
|
|
2258
|
+
} else if (item.type === 'accordion') {
|
|
2259
|
+
item.sections.forEach((section) => {
|
|
2260
|
+
fields.push(...this.collectFieldsFromItems(section.items));
|
|
2261
|
+
});
|
|
1915
2262
|
}
|
|
1916
2263
|
});
|
|
1917
2264
|
|
|
@@ -1953,6 +2300,22 @@ export class NodeEditor extends RapidElement {
|
|
|
1953
2300
|
// Trigger re-render
|
|
1954
2301
|
this.requestUpdate();
|
|
1955
2302
|
}
|
|
2303
|
+
private handleTemplateEditorChange(fieldName: string, event: Event): void {
|
|
2304
|
+
const customEvent = event as CustomEvent;
|
|
2305
|
+
const detail = customEvent.detail;
|
|
2306
|
+
|
|
2307
|
+
this.formData = {
|
|
2308
|
+
...this.formData,
|
|
2309
|
+
[fieldName]: detail.template
|
|
2310
|
+
? { uuid: detail.template.uuid, name: detail.template.name }
|
|
2311
|
+
: null,
|
|
2312
|
+
template_variables: detail.variables || []
|
|
2313
|
+
};
|
|
2314
|
+
|
|
2315
|
+
this.updateGroupCollapseStates();
|
|
2316
|
+
this.requestUpdate();
|
|
2317
|
+
}
|
|
2318
|
+
|
|
1956
2319
|
private handleMessageEditorChange(fieldName: string, event: Event): void {
|
|
1957
2320
|
const target = event.target as any;
|
|
1958
2321
|
|
|
@@ -451,12 +451,14 @@ export class NodeTypeSelector extends RapidElement {
|
|
|
451
451
|
.filter(([type, config]) => {
|
|
452
452
|
// exclude execute_actions (it's the default action-only node)
|
|
453
453
|
// exclude nodes that have showAsAction=true (they appear in action mode)
|
|
454
|
+
// exclude nodes that have hideFromSplits=true (promoted to context menu)
|
|
454
455
|
// exclude aliases (type won't match config.type for aliases)
|
|
455
456
|
return (
|
|
456
457
|
type !== 'execute_actions' &&
|
|
457
458
|
type === config.type &&
|
|
458
459
|
config.name &&
|
|
459
460
|
!config.showAsAction &&
|
|
461
|
+
!config.hideFromSplits &&
|
|
460
462
|
this.isConfigAvailable(config)
|
|
461
463
|
);
|
|
462
464
|
})
|