@nyaruka/temba-components 0.131.2 → 0.131.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 +14 -0
- package/demo/components/floating-tabs/example.html +400 -0
- package/demo/components/flow/index.html +1 -1
- package/demo/data/flows/sample-flow.json +41 -2
- package/demo/data/flows/voicemail.json +613 -0
- package/demo/index.html +6 -0
- package/dist/locales/es.js +5 -5
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/fr.js +5 -5
- package/dist/locales/fr.js.map +1 -1
- package/dist/locales/locale-codes.js +11 -2
- package/dist/locales/locale-codes.js.map +1 -1
- package/dist/locales/pt.js +5 -5
- package/dist/locales/pt.js.map +1 -1
- package/dist/temba-components.js +1109 -535
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +167 -0
- package/out-tsc/src/display/FloatingTab.js.map +1 -0
- package/out-tsc/src/display/ProgressBar.js +22 -2
- package/out-tsc/src/display/ProgressBar.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +165 -31
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +857 -3
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +239 -19
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/NodeTypeSelector.js +44 -3
- package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -1
- package/out-tsc/src/flow/StickyNote.js +12 -3
- package/out-tsc/src/flow/StickyNote.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js +2 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js +2 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +2 -1
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/play_audio.js +2 -1
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js +2 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/request_optin.js +1 -0
- package/out-tsc/src/flow/actions/request_optin.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 -1
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
- package/out-tsc/src/flow/actions/send_email.js +2 -1
- package/out-tsc/src/flow/actions/send_email.js.map +1 -1
- package/out-tsc/src/flow/actions/send_msg.js +93 -3
- 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 +2 -1
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js +2 -1
- 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 +2 -1
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/actions/start_session.js +2 -1
- package/out-tsc/src/flow/actions/start_session.js.map +1 -1
- package/out-tsc/src/flow/config.js +2 -10
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/shared.js +54 -0
- package/out-tsc/src/flow/nodes/shared.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_airtime.js +9 -3
- package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_contact_field.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_expression.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_groups.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_intent.js +3 -2
- package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm.js +9 -2
- package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +9 -2
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_random.js +8 -2
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_resthook.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_run_result.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_scheme.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js +8 -2
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_ticket.js +8 -2
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_webhook.js +8 -2
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js +3 -2
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_menu.js +3 -2
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +8 -3
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/types.js +15 -0
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/layout/FloatingWindow.js +346 -0
- package/out-tsc/src/layout/FloatingWindow.js.map +1 -0
- package/out-tsc/src/live/ContactChat.js +3 -19
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/locales/es.js +5 -5
- package/out-tsc/src/locales/es.js.map +1 -1
- package/out-tsc/src/locales/fr.js +5 -5
- package/out-tsc/src/locales/fr.js.map +1 -1
- package/out-tsc/src/locales/locale-codes.js +11 -2
- package/out-tsc/src/locales/locale-codes.js.map +1 -1
- package/out-tsc/src/locales/pt.js +5 -5
- package/out-tsc/src/locales/pt.js.map +1 -1
- package/out-tsc/src/store/AppState.js +67 -0
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/temba-modules.js +4 -0
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/temba-floating-tab.test.js +91 -0
- package/out-tsc/test/temba-floating-tab.test.js.map +1 -0
- package/out-tsc/test/temba-floating-window.test.js +301 -0
- package/out-tsc/test/temba-floating-window.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor-node.test.js +117 -0
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-localization.test.js +471 -0
- package/out-tsc/test/temba-localization.test.js.map +1 -0
- package/out-tsc/test/temba-node-type-selector.test.js +150 -0
- package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +18 -0
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/floating-tab/default.png +0 -0
- package/screenshots/truth/floating-tab/gray.png +0 -0
- package/screenshots/truth/floating-tab/green.png +0 -0
- package/screenshots/truth/floating-tab/hidden.png +0 -0
- package/screenshots/truth/floating-tab/hover.png +0 -0
- package/screenshots/truth/floating-tab/purple.png +0 -0
- package/screenshots/truth/floating-window/chromeless.png +0 -0
- package/screenshots/truth/floating-window/custom-size.png +0 -0
- package/screenshots/truth/floating-window/default.png +0 -0
- package/screenshots/truth/floating-window/with-header.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_categorize/editor/feedback-categorization.png +0 -0
- package/src/display/FloatingTab.ts +174 -0
- package/src/display/ProgressBar.ts +22 -2
- package/src/events.ts +2 -4
- package/src/flow/CanvasNode.ts +190 -32
- package/src/flow/Editor.ts +1040 -3
- package/src/flow/NodeEditor.ts +317 -19
- package/src/flow/NodeTypeSelector.ts +47 -3
- package/src/flow/StickyNote.ts +12 -3
- package/src/flow/actions/add_contact_groups.ts +2 -1
- package/src/flow/actions/add_contact_urn.ts +3 -1
- package/src/flow/actions/add_input_labels.ts +2 -1
- package/src/flow/actions/play_audio.ts +2 -1
- package/src/flow/actions/remove_contact_groups.ts +3 -1
- package/src/flow/actions/request_optin.ts +1 -0
- package/src/flow/actions/say_msg.ts +2 -1
- package/src/flow/actions/send_broadcast.ts +2 -1
- package/src/flow/actions/send_email.ts +3 -1
- package/src/flow/actions/send_msg.ts +134 -3
- package/src/flow/actions/set_contact_channel.ts +2 -1
- package/src/flow/actions/set_contact_field.ts +2 -1
- package/src/flow/actions/set_contact_language.ts +3 -1
- package/src/flow/actions/set_contact_name.ts +2 -1
- package/src/flow/actions/set_contact_status.ts +2 -1
- package/src/flow/actions/set_run_result.ts +2 -1
- package/src/flow/actions/start_session.ts +3 -1
- package/src/flow/config.ts +2 -12
- package/src/flow/nodes/shared.ts +70 -1
- package/src/flow/nodes/split_by_airtime.ts +20 -3
- package/src/flow/nodes/split_by_contact_field.ts +13 -3
- package/src/flow/nodes/split_by_expression.ts +13 -3
- package/src/flow/nodes/split_by_groups.ts +13 -3
- package/src/flow/nodes/split_by_intent.ts +3 -2
- package/src/flow/nodes/split_by_llm.ts +19 -2
- package/src/flow/nodes/split_by_llm_categorize.ts +19 -2
- package/src/flow/nodes/split_by_random.ts +12 -2
- package/src/flow/nodes/split_by_resthook.ts +13 -3
- package/src/flow/nodes/split_by_run_result.ts +13 -3
- package/src/flow/nodes/split_by_scheme.ts +13 -3
- package/src/flow/nodes/split_by_subflow.ts +12 -2
- package/src/flow/nodes/split_by_ticket.ts +12 -2
- package/src/flow/nodes/split_by_webhook.ts +12 -2
- package/src/flow/nodes/wait_for_digits.ts +3 -2
- package/src/flow/nodes/wait_for_menu.ts +3 -2
- package/src/flow/nodes/wait_for_response.ts +13 -3
- package/src/flow/types.ts +47 -0
- package/src/layout/FloatingWindow.ts +386 -0
- package/src/live/ContactChat.ts +4 -19
- package/src/locales/es.ts +18 -13
- package/src/locales/fr.ts +18 -13
- package/src/locales/locale-codes.ts +11 -2
- package/src/locales/pt.ts +18 -13
- package/src/store/AppState.ts +104 -0
- package/static/api/llms.json +18 -0
- package/temba-modules.ts +4 -0
- package/test/temba-floating-tab.test.ts +110 -0
- package/test/temba-floating-window.test.ts +477 -0
- package/test/temba-flow-editor-node.test.ts +144 -0
- package/test/temba-localization.test.ts +611 -0
- package/test/temba-node-type-selector.test.ts +203 -0
- package/test/utils.test.ts +20 -0
- package/test-assets/contacts/history.json +5 -6
- package/test-assets/select/llms.json +2 -2
- package/web-dev-server.config.mjs +47 -1
- package/web-test-runner.config.mjs +0 -1
- package/out-tsc/src/flow/nodes/wait_for_audio.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_image.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_image.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_location.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_location.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_video.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_video.js.map +0 -1
- package/src/flow/nodes/wait_for_audio.ts +0 -7
- package/src/flow/nodes/wait_for_image.ts +0 -7
- package/src/flow/nodes/wait_for_location.ts +0 -7
- package/src/flow/nodes/wait_for_video.ts +0 -7
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { css, html } from 'lit';
|
|
3
|
+
import { property } from 'lit/decorators.js';
|
|
4
|
+
import { RapidElement } from '../RapidElement';
|
|
5
|
+
import { CustomEventType } from '../interfaces';
|
|
6
|
+
import { getClasses } from '../utils';
|
|
7
|
+
export class FloatingTab extends RapidElement {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.color = '#6B7280';
|
|
11
|
+
this.top = -1; // -1 means auto-calculate position
|
|
12
|
+
this.hidden = false;
|
|
13
|
+
}
|
|
14
|
+
static get styles() {
|
|
15
|
+
return css `
|
|
16
|
+
.tab.hidden {
|
|
17
|
+
transform: translateX(100%);
|
|
18
|
+
}
|
|
19
|
+
.tab {
|
|
20
|
+
position: fixed;
|
|
21
|
+
right: 0;
|
|
22
|
+
z-index: 9998;
|
|
23
|
+
transition: transform var(--transition-duration, 300ms) ease-in-out;
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
padding: 12px;
|
|
27
|
+
border-top-left-radius: 8px;
|
|
28
|
+
border-bottom-left-radius: 8px;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
box-shadow: -2px 2px 8px rgba(0, 0, 0, 0.2);
|
|
31
|
+
transition: all calc(var(--transition-duration, 300ms) * 0.7)
|
|
32
|
+
ease-in-out;
|
|
33
|
+
user-select: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.tab:hover {
|
|
37
|
+
padding-right: 16px;
|
|
38
|
+
box-shadow: -4px 4px 12px rgba(0, 0, 0, 0.3);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.icon-container {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
width: 32px;
|
|
46
|
+
height: 32px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
temba-icon {
|
|
50
|
+
--icon-color: white;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.label {
|
|
54
|
+
color: white;
|
|
55
|
+
font-weight: 500;
|
|
56
|
+
font-size: 16px;
|
|
57
|
+
max-width: 0;
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
white-space: nowrap;
|
|
60
|
+
margin-left: 0;
|
|
61
|
+
opacity: 0;
|
|
62
|
+
transition: all var(--transition-duration, 300ms) ease-in-out;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.tab:hover .label {
|
|
66
|
+
max-width: 200px;
|
|
67
|
+
margin-left: 12px;
|
|
68
|
+
opacity: 1;
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
connectedCallback() {
|
|
73
|
+
super.connectedCallback();
|
|
74
|
+
FloatingTab.allTabs.push(this);
|
|
75
|
+
this.updatePosition();
|
|
76
|
+
}
|
|
77
|
+
disconnectedCallback() {
|
|
78
|
+
super.disconnectedCallback();
|
|
79
|
+
const index = FloatingTab.allTabs.indexOf(this);
|
|
80
|
+
if (index > -1) {
|
|
81
|
+
FloatingTab.allTabs.splice(index, 1);
|
|
82
|
+
}
|
|
83
|
+
// update positions of remaining tabs
|
|
84
|
+
FloatingTab.allTabs.forEach((tab) => tab.updatePosition());
|
|
85
|
+
}
|
|
86
|
+
updatePosition() {
|
|
87
|
+
// if top is manually set, use it
|
|
88
|
+
if (this.top !== -1) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// auto-calculate position based on index
|
|
92
|
+
const index = FloatingTab.allTabs.indexOf(this);
|
|
93
|
+
if (index === -1) {
|
|
94
|
+
this.top = 100; // default fallback
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// start at 100px and stack with 10px gap between tabs
|
|
98
|
+
this.top = 100 + index * (FloatingTab.TAB_HEIGHT + 10);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
updated(changes) {
|
|
102
|
+
super.updated(changes);
|
|
103
|
+
if (changes.has('hidden')) {
|
|
104
|
+
this.classList.toggle('hidden', this.hidden);
|
|
105
|
+
}
|
|
106
|
+
if (changes.has('top')) {
|
|
107
|
+
this.updatePosition();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
handleClick() {
|
|
111
|
+
// hide all tabs when one is clicked
|
|
112
|
+
FloatingTab.allTabs.forEach((tab) => {
|
|
113
|
+
tab.hidden = true;
|
|
114
|
+
});
|
|
115
|
+
this.fireCustomEvent(CustomEventType.ButtonClicked, {
|
|
116
|
+
action: 'toggle'
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
static showAllTabs() {
|
|
120
|
+
FloatingTab.allTabs.forEach((tab) => {
|
|
121
|
+
tab.hidden = false;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
static hideAllTabs() {
|
|
125
|
+
FloatingTab.allTabs.forEach((tab) => {
|
|
126
|
+
tab.hidden = true;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
render() {
|
|
130
|
+
const tabStyle = `
|
|
131
|
+
background-color: ${this.color};
|
|
132
|
+
top: ${this.top}px;
|
|
133
|
+
`;
|
|
134
|
+
const classes = getClasses({
|
|
135
|
+
tab: true,
|
|
136
|
+
hidden: this.hidden
|
|
137
|
+
});
|
|
138
|
+
return html `
|
|
139
|
+
<div class="${classes}" style="${tabStyle}" @click=${this.handleClick}>
|
|
140
|
+
<div class="icon-container">
|
|
141
|
+
${this.icon
|
|
142
|
+
? html `<temba-icon size="2" name="${this.icon}"></temba-icon>`
|
|
143
|
+
: ''}
|
|
144
|
+
</div>
|
|
145
|
+
<div class="label">${this.label}</div>
|
|
146
|
+
</div>
|
|
147
|
+
`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
FloatingTab.TAB_HEIGHT = 56; // height of tab for auto-stacking
|
|
151
|
+
FloatingTab.allTabs = [];
|
|
152
|
+
__decorate([
|
|
153
|
+
property({ type: String })
|
|
154
|
+
], FloatingTab.prototype, "icon", void 0);
|
|
155
|
+
__decorate([
|
|
156
|
+
property({ type: String })
|
|
157
|
+
], FloatingTab.prototype, "label", void 0);
|
|
158
|
+
__decorate([
|
|
159
|
+
property({ type: String })
|
|
160
|
+
], FloatingTab.prototype, "color", void 0);
|
|
161
|
+
__decorate([
|
|
162
|
+
property({ type: Number })
|
|
163
|
+
], FloatingTab.prototype, "top", void 0);
|
|
164
|
+
__decorate([
|
|
165
|
+
property({ type: Boolean })
|
|
166
|
+
], FloatingTab.prototype, "hidden", void 0);
|
|
167
|
+
//# sourceMappingURL=FloatingTab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloatingTab.js","sourceRoot":"","sources":["../../../src/display/FloatingTab.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { CustomEventType } from '../interfaces';\nimport { getClasses } from '../utils';\n\nexport class FloatingTab extends RapidElement {\n static get styles() {\n return css`\n .tab.hidden {\n transform: translateX(100%);\n }\n .tab {\n position: fixed;\n right: 0;\n z-index: 9998;\n transition: transform var(--transition-duration, 300ms) ease-in-out;\n display: flex;\n align-items: center;\n padding: 12px;\n border-top-left-radius: 8px;\n border-bottom-left-radius: 8px;\n cursor: pointer;\n box-shadow: -2px 2px 8px rgba(0, 0, 0, 0.2);\n transition: all calc(var(--transition-duration, 300ms) * 0.7)\n ease-in-out;\n user-select: none;\n }\n\n .tab:hover {\n padding-right: 16px;\n box-shadow: -4px 4px 12px rgba(0, 0, 0, 0.3);\n }\n\n .icon-container {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n }\n\n temba-icon {\n --icon-color: white;\n }\n\n .label {\n color: white;\n font-weight: 500;\n font-size: 16px;\n max-width: 0;\n overflow: hidden;\n white-space: nowrap;\n margin-left: 0;\n opacity: 0;\n transition: all var(--transition-duration, 300ms) ease-in-out;\n }\n\n .tab:hover .label {\n max-width: 200px;\n margin-left: 12px;\n opacity: 1;\n }\n `;\n }\n\n static TAB_HEIGHT = 56; // height of tab for auto-stacking\n static allTabs: FloatingTab[] = [];\n\n @property({ type: String })\n icon: string;\n\n @property({ type: String })\n label: string;\n\n @property({ type: String })\n color = '#6B7280';\n\n @property({ type: Number })\n top = -1; // -1 means auto-calculate position\n\n @property({ type: Boolean })\n hidden = false;\n\n connectedCallback() {\n super.connectedCallback();\n FloatingTab.allTabs.push(this);\n this.updatePosition();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n const index = FloatingTab.allTabs.indexOf(this);\n if (index > -1) {\n FloatingTab.allTabs.splice(index, 1);\n }\n // update positions of remaining tabs\n FloatingTab.allTabs.forEach((tab) => tab.updatePosition());\n }\n\n private updatePosition() {\n // if top is manually set, use it\n if (this.top !== -1) {\n return;\n }\n\n // auto-calculate position based on index\n const index = FloatingTab.allTabs.indexOf(this);\n if (index === -1) {\n this.top = 100; // default fallback\n } else {\n // start at 100px and stack with 10px gap between tabs\n this.top = 100 + index * (FloatingTab.TAB_HEIGHT + 10);\n }\n }\n\n public updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('hidden')) {\n this.classList.toggle('hidden', this.hidden);\n }\n if (changes.has('top')) {\n this.updatePosition();\n }\n }\n\n private handleClick() {\n // hide all tabs when one is clicked\n FloatingTab.allTabs.forEach((tab) => {\n tab.hidden = true;\n });\n\n this.fireCustomEvent(CustomEventType.ButtonClicked, {\n action: 'toggle'\n });\n }\n\n public static showAllTabs() {\n FloatingTab.allTabs.forEach((tab) => {\n tab.hidden = false;\n });\n }\n\n public static hideAllTabs() {\n FloatingTab.allTabs.forEach((tab) => {\n tab.hidden = true;\n });\n }\n\n public render(): TemplateResult {\n const tabStyle = `\n background-color: ${this.color};\n top: ${this.top}px;\n `;\n\n const classes = getClasses({\n tab: true,\n hidden: this.hidden\n });\n\n return html`\n <div class=\"${classes}\" style=\"${tabStyle}\" @click=${this.handleClick}>\n <div class=\"icon-container\">\n ${this.icon\n ? html`<temba-icon size=\"2\" name=\"${this.icon}\"></temba-icon>`\n : ''}\n </div>\n <div class=\"label\">${this.label}</div>\n </div>\n `;\n }\n}\n"]}
|
|
@@ -11,13 +11,14 @@ export class ProgressBar extends RapidElement {
|
|
|
11
11
|
this.done = false;
|
|
12
12
|
this.showEstimatedCompletion = false;
|
|
13
13
|
this.showPercentage = false;
|
|
14
|
+
this.animated = true;
|
|
14
15
|
}
|
|
15
16
|
updated(changes) {
|
|
16
17
|
if (changes.has('eta') && this.eta) {
|
|
17
18
|
this.estimatedCompletionDate = new Date(this.eta);
|
|
18
19
|
this.showEstimatedCompletion = this.estimatedCompletionDate > new Date();
|
|
19
20
|
}
|
|
20
|
-
if (changes.has('current')) {
|
|
21
|
+
if (changes.has('current') || changes.has('total')) {
|
|
21
22
|
const pct = Math.floor(Math.min((this.current / this.total) * 100, 100));
|
|
22
23
|
if (Number.isNaN(pct)) {
|
|
23
24
|
this.showPercentage = false;
|
|
@@ -30,8 +31,15 @@ export class ProgressBar extends RapidElement {
|
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
render() {
|
|
34
|
+
const meterClasses = [
|
|
35
|
+
'meter',
|
|
36
|
+
this.done ? 'done' : '',
|
|
37
|
+
this.animated ? '' : 'static'
|
|
38
|
+
]
|
|
39
|
+
.filter(Boolean)
|
|
40
|
+
.join(' ');
|
|
33
41
|
return html `<div class="wrapper ${this.done ? 'complete' : ''}">
|
|
34
|
-
<div class="
|
|
42
|
+
<div class="${meterClasses}">
|
|
35
43
|
${this.message
|
|
36
44
|
? html `<div class="message">${this.message}</div>`
|
|
37
45
|
: null}
|
|
@@ -132,6 +140,15 @@ ProgressBar.styles = css `
|
|
|
132
140
|
display: none;
|
|
133
141
|
}
|
|
134
142
|
|
|
143
|
+
.meter.static > span:after {
|
|
144
|
+
display: none;
|
|
145
|
+
animation: none;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.meter.static > span {
|
|
149
|
+
background-image: none;
|
|
150
|
+
}
|
|
151
|
+
|
|
135
152
|
@keyframes move {
|
|
136
153
|
0% {
|
|
137
154
|
background-position: 0 0;
|
|
@@ -208,4 +225,7 @@ __decorate([
|
|
|
208
225
|
__decorate([
|
|
209
226
|
property({ type: String })
|
|
210
227
|
], ProgressBar.prototype, "message", void 0);
|
|
228
|
+
__decorate([
|
|
229
|
+
property({ type: Boolean })
|
|
230
|
+
], ProgressBar.prototype, "animated", void 0);
|
|
211
231
|
//# sourceMappingURL=ProgressBar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProgressBar.js","sourceRoot":"","sources":["../../../src/display/ProgressBar.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAoC,MAAM,KAAK,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,MAAM,OAAO,WAAY,SAAQ,YAAY;IAA7C;;
|
|
1
|
+
{"version":3,"file":"ProgressBar.js","sourceRoot":"","sources":["../../../src/display/ProgressBar.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { RapidElement } from '../RapidElement';\nimport { property } from 'lit/decorators.js';\n\nexport class ProgressBar extends RapidElement {\n static styles = css`\n .wrapper {\n display: flex;\n box-sizing: content-box;\n background: #f1f1f1;\n border-radius: var(--curvature);\n box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.05);\n overflow: hidden;\n min-height: 1.5rem;\n }\n\n .message {\n padding: 0 0.5rem;\n color: rgba(0, 0, 0, 0.4);\n white-space: nowrap;\n }\n\n .meter {\n flex-grow: 1;\n display: flex;\n box-sizing: content-box;\n position: relative;\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n padding: 4px;\n min-height: 6px;\n }\n .meter > span {\n display: block;\n height: 100%;\n border-radius: calc(var(--curvature) * 0.8);\n background-color: var(--color-primary-dark);\n background-image: linear-gradient(\n center bottom,\n rgb(43, 194, 83) 37%,\n rgb(84, 240, 83) 69%\n );\n\n position: relative;\n overflow: hidden;\n }\n\n .meter > span:after,\n .animate > span > span {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n background-image: linear-gradient(\n -45deg,\n rgba(255, 255, 255, 0.2) 25%,\n transparent 25%,\n transparent 50%,\n rgba(255, 255, 255, 0.2) 50%,\n rgba(255, 255, 255, 0.2) 75%,\n transparent 75%,\n transparent\n );\n z-index: 1;\n background-size: 50px 50px;\n animation: move 8s linear infinite;\n border-top-right-radius: var(--curvature);\n border-bottom-right-radius: var(--curvature);\n border-top-left-radius: var(--curvature);\n border-bottom-left-radius: var(--curvature);\n overflow: hidden;\n }\n\n .animate > span:after {\n display: none;\n }\n\n .meter.static > span:after {\n display: none;\n animation: none;\n }\n\n .meter.static > span {\n background-image: none;\n }\n\n @keyframes move {\n 0% {\n background-position: 0 0;\n }\n 100% {\n background-position: 50px 50px;\n }\n }\n\n .meter .complete {\n transition: flex-basis 2s;\n }\n\n .meter .incomplete {\n flex-grow: 1;\n }\n\n .etc {\n display: flex;\n flex-direction: row;\n background: rgba(0, 0, 0, 0.07);\n font-weight: bold;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.5);\n align-self: center;\n padding: 0px 6px;\n align-self: stretch;\n align-items: center;\n }\n\n .etc > div {\n font-size: 0.7em;\n }\n\n .wrapper *::last-child {\n border-top-right-radius: var(--curvature);\n border-bottom-right-radius: var(--curvature);\n overflow: hidden;\n }\n\n .meter.done > span:after,\n .done .animate > span > span {\n display: none;\n }\n\n .meter.done > span {\n background: rgb(var(--success-rgb));\n }\n `;\n\n @property({ type: Number })\n total = 100;\n\n @property({ type: Number })\n current = 0;\n\n @property({ type: Number })\n pct = 0;\n\n @property({ type: Boolean })\n done = false;\n\n @property({ type: String })\n eta: string;\n\n @property({ type: String, attribute: false })\n estimatedCompletionDate: Date;\n\n @property({ type: Boolean })\n showEstimatedCompletion = false;\n\n @property({ type: Boolean })\n showPercentage = false;\n\n @property({ type: String })\n message: string;\n\n @property({ type: Boolean })\n animated = true;\n\n public updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n if (changes.has('eta') && this.eta) {\n this.estimatedCompletionDate = new Date(this.eta);\n this.showEstimatedCompletion = this.estimatedCompletionDate > new Date();\n }\n\n if (changes.has('current') || changes.has('total')) {\n const pct = Math.floor(Math.min((this.current / this.total) * 100, 100));\n if (Number.isNaN(pct)) {\n this.showPercentage = false;\n } else {\n this.pct = pct;\n this.showPercentage = true;\n }\n\n this.done = this.pct >= 100;\n }\n }\n\n public render(): TemplateResult {\n const meterClasses = [\n 'meter',\n this.done ? 'done' : '',\n this.animated ? '' : 'static'\n ]\n .filter(Boolean)\n .join(' ');\n\n return html`<div class=\"wrapper ${this.done ? 'complete' : ''}\">\n <div class=\"${meterClasses}\">\n ${this.message\n ? html`<div class=\"message\">${this.message}</div>`\n : null}\n <span class=\"complete\" style=\"flex-basis: ${this.pct}%\"></span>\n <div class=\"incomplete\"></div>\n </div>\n\n ${this.showPercentage || this.showEstimatedCompletion\n ? html`<div class=\"etc\">\n <div>\n ${this.estimatedCompletionDate &&\n this.showEstimatedCompletion &&\n !this.done\n ? html`<temba-date\n value=\"${this.estimatedCompletionDate.toISOString()}\"\n display=\"countdown\"\n ></temba-date>`\n : html`${this.pct}%`}\n </div>\n </div>`\n : null}\n\n <slot></slot>\n </div>`;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"","sourcesContent":["import { Msg, ObjectReference, User } from './interfaces';\n\nexport interface EventGroup {\n type: string;\n events: ContactEvent[];\n open: boolean;\n}\n\nexport interface ContactEvent {\n uuid?: string;\n type: string;\n created_on: string;\n _user?: ObjectReference;\n}\n\nexport interface ChannelEvent extends ContactEvent {\n channel_event_type: string;\n duration: number;\n\n event: {\n type: string;\n channel: { uuid: string; name: string };\n duration?: number;\n optin?: {\n uuid: string;\n name: string;\n };\n };\n}\n\nexport interface ContactLanguageChangedEvent extends ContactEvent {\n language: string;\n}\n\nexport interface ContactStatusChangedEvent extends ContactEvent {\n status: string;\n}\n\nexport interface OptInEvent extends ContactEvent {\n optin: {\n uuid: string;\n name: string;\n };\n}\n\nexport interface CallEvent extends ContactEvent {\n call?: {\n uuid: string;\n urn: string;\n };\n}\n\nexport interface ChatStartedEvent extends ContactEvent {\n params?: object;\n}\n\nexport interface MsgEvent extends ContactEvent {\n msg: Msg;\n optin?: ObjectReference;\n _status?:
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"","sourcesContent":["import { Msg, ObjectReference, User } from './interfaces';\n\nexport interface EventGroup {\n type: string;\n events: ContactEvent[];\n open: boolean;\n}\n\nexport interface ContactEvent {\n uuid?: string;\n type: string;\n created_on: string;\n _user?: ObjectReference;\n}\n\nexport interface ChannelEvent extends ContactEvent {\n channel_event_type: string;\n duration: number;\n\n event: {\n type: string;\n channel: { uuid: string; name: string };\n duration?: number;\n optin?: {\n uuid: string;\n name: string;\n };\n };\n}\n\nexport interface ContactLanguageChangedEvent extends ContactEvent {\n language: string;\n}\n\nexport interface ContactStatusChangedEvent extends ContactEvent {\n status: string;\n}\n\nexport interface OptInEvent extends ContactEvent {\n optin: {\n uuid: string;\n name: string;\n };\n}\n\nexport interface CallEvent extends ContactEvent {\n call?: {\n uuid: string;\n urn: string;\n };\n}\n\nexport interface ChatStartedEvent extends ContactEvent {\n params?: object;\n}\n\nexport interface MsgEvent extends ContactEvent {\n msg: Msg;\n optin?: ObjectReference;\n _status?: { created_on: string; status: string; reason: string };\n _logs_url?: string;\n}\n\nexport interface RunEvent extends ContactEvent {\n flow: ObjectReference;\n status: string;\n}\n\nexport interface URNsChangedEvent extends ContactEvent {\n urns: string[];\n}\n\nexport interface TicketEvent extends ContactEvent {\n ticket: {\n // ticket_opened\n uuid: string;\n topic?: ObjectReference;\n };\n ticket_uuid?: string; // all other event types\n assignee?: User;\n note?: string;\n topic?: ObjectReference;\n}\n\nexport interface NameChangedEvent extends ContactEvent {\n name: string;\n}\n\nexport interface UpdateFieldEvent extends ContactEvent {\n field: { key: string; name: string };\n value: { text: string };\n}\n\nexport interface ContactGroupsEvent extends ContactEvent {\n groups_added: ObjectReference[];\n groups_removed: ObjectReference[];\n}\n\nexport interface AirtimeTransferredEvent extends ContactEvent {\n sender: string;\n recipient: string;\n currency: string;\n amount: string;\n}\n\nexport type CallStartedEvent = ContactEvent;\n\nexport interface ContactHistoryPage {\n events: ContactEvent[];\n has_older: boolean;\n recent_only: boolean;\n next_before: number;\n next_after: number;\n}\n"]}
|
|
@@ -8,6 +8,7 @@ import { RapidElement } from '../RapidElement';
|
|
|
8
8
|
import { getClasses } from '../utils';
|
|
9
9
|
import { getStore } from '../store/Store';
|
|
10
10
|
import { CustomEventType } from '../interfaces';
|
|
11
|
+
import { fromStore, zustand } from '../store/AppState';
|
|
11
12
|
const DRAG_THRESHOLD = 5;
|
|
12
13
|
export class CanvasNode extends RapidElement {
|
|
13
14
|
createRenderRoot() {
|
|
@@ -114,6 +115,20 @@ export class CanvasNode extends RapidElement {
|
|
|
114
115
|
|
|
115
116
|
.node.execute-actions temba-sortable-list .action:last-child .body {
|
|
116
117
|
padding-bottom: 1.5em;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Localization indicators */
|
|
121
|
+
.action.localizable:not(.has-localization) .action-content {
|
|
122
|
+
background: #fff8dc !important; /* Light yellow background for localizable but not yet localized */
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.non-localizable {
|
|
126
|
+
opacity: 0.25;
|
|
127
|
+
pointer-events: none;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.action.non-localizable .action-content {
|
|
131
|
+
cursor: not-allowed;
|
|
117
132
|
}
|
|
118
133
|
|
|
119
134
|
.action .drag-handle {
|
|
@@ -182,6 +197,10 @@ export class CanvasNode extends RapidElement {
|
|
|
182
197
|
margin: 0.2em;
|
|
183
198
|
}
|
|
184
199
|
|
|
200
|
+
.router-section {
|
|
201
|
+
/* Container for router and categories */
|
|
202
|
+
}
|
|
203
|
+
|
|
185
204
|
.categories {
|
|
186
205
|
display: flex;
|
|
187
206
|
flex-direction: row;
|
|
@@ -198,6 +217,11 @@ export class CanvasNode extends RapidElement {
|
|
|
198
217
|
flex-direction: column;
|
|
199
218
|
}
|
|
200
219
|
|
|
220
|
+
/* Localizable category - yellow background */
|
|
221
|
+
.category.localizable {
|
|
222
|
+
background-color: #fff8dc;
|
|
223
|
+
}
|
|
224
|
+
|
|
201
225
|
.action-exits {
|
|
202
226
|
padding-bottom: 0.7em;
|
|
203
227
|
margin-top: -0.7em;
|
|
@@ -946,18 +970,20 @@ export class CanvasNode extends RapidElement {
|
|
|
946
970
|
? (_b = ACTION_GROUP_METADATA[config.group]) === null || _b === void 0 ? void 0 : _b.color
|
|
947
971
|
: '#aaaaaa';
|
|
948
972
|
return html `<div class="cn-title" style="background:${color}">
|
|
949
|
-
${((_d = (_c = this.node) === null || _c === void 0 ? void 0 : _c.actions) === null || _d === void 0 ? void 0 : _d.length) > 1
|
|
973
|
+
${!this.isTranslating && ((_d = (_c = this.node) === null || _c === void 0 ? void 0 : _c.actions) === null || _d === void 0 ? void 0 : _d.length) > 1
|
|
950
974
|
? html `<temba-icon class="drag-handle" name="sort"></temba-icon>`
|
|
951
975
|
: html `<div class="title-spacer"></div>`}
|
|
952
976
|
|
|
953
977
|
<div class="name">${isRemoving ? 'Remove?' : config.name}</div>
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
978
|
+
${!this.isTranslating
|
|
979
|
+
? html `<div
|
|
980
|
+
class="remove-button"
|
|
981
|
+
@click=${(e) => this.handleActionRemoveClick(e, action, index)}
|
|
982
|
+
title="Remove action"
|
|
983
|
+
>
|
|
984
|
+
✕
|
|
985
|
+
</div>`
|
|
986
|
+
: html `<div class="title-spacer"></div>`}
|
|
961
987
|
</div>`;
|
|
962
988
|
}
|
|
963
989
|
renderNodeTitle(config, node, ui, isRemoving = false) {
|
|
@@ -979,13 +1005,15 @@ export class CanvasNode extends RapidElement {
|
|
|
979
1005
|
? config.renderTitle(node, ui)
|
|
980
1006
|
: html `${config.name}`}
|
|
981
1007
|
</div>
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1008
|
+
${!this.isTranslating
|
|
1009
|
+
? html `<div
|
|
1010
|
+
class="remove-button"
|
|
1011
|
+
@click=${(e) => this.handleNodeRemoveClick(e)}
|
|
1012
|
+
title="Remove node"
|
|
1013
|
+
>
|
|
1014
|
+
✕
|
|
1015
|
+
</div>`
|
|
1016
|
+
: html `<div class="title-spacer"></div>`}
|
|
989
1017
|
</div>`;
|
|
990
1018
|
}
|
|
991
1019
|
renderDropPlaceholder() {
|
|
@@ -996,24 +1024,80 @@ export class CanvasNode extends RapidElement {
|
|
|
996
1024
|
style="height: ${height}px; background: #f3f4f6; border: 2px dashed #d1d5db; border-radius: var(--curvature);"
|
|
997
1025
|
></div>`;
|
|
998
1026
|
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Get the localized version of an action if translating, otherwise return the original action.
|
|
1029
|
+
* Falls back to base language values if no localization exists for a field.
|
|
1030
|
+
*/
|
|
1031
|
+
getLocalizedAction(action) {
|
|
1032
|
+
var _b, _c, _d;
|
|
1033
|
+
// If not translating or no flow definition, return original action
|
|
1034
|
+
if (!this.isTranslating ||
|
|
1035
|
+
!this.flowDefinition ||
|
|
1036
|
+
!this.languageCode ||
|
|
1037
|
+
this.languageCode === this.flowDefinition.language) {
|
|
1038
|
+
return action;
|
|
1039
|
+
}
|
|
1040
|
+
// Check if there's localization for this action
|
|
1041
|
+
const localization = (_d = (_c = (_b = this.flowDefinition) === null || _b === void 0 ? void 0 : _b.localization) === null || _c === void 0 ? void 0 : _c[this.languageCode]) === null || _d === void 0 ? void 0 : _d[action.uuid];
|
|
1042
|
+
if (!localization) {
|
|
1043
|
+
// No localization available, return original action
|
|
1044
|
+
return action;
|
|
1045
|
+
}
|
|
1046
|
+
// Create a new action with localized values, falling back to base language
|
|
1047
|
+
const localizedAction = { ...action };
|
|
1048
|
+
// Apply localized values for each field
|
|
1049
|
+
Object.keys(localization).forEach((field) => {
|
|
1050
|
+
const localizedValue = localization[field];
|
|
1051
|
+
if (Array.isArray(localizedValue)) {
|
|
1052
|
+
// Localized values are stored as arrays
|
|
1053
|
+
if (localizedValue.length > 0) {
|
|
1054
|
+
// For single-value fields like 'text', take the first element
|
|
1055
|
+
// For array fields like 'quick_replies', use the whole array
|
|
1056
|
+
if (Array.isArray(action[field])) {
|
|
1057
|
+
localizedAction[field] = localizedValue;
|
|
1058
|
+
}
|
|
1059
|
+
else {
|
|
1060
|
+
localizedAction[field] = localizedValue[0];
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
return localizedAction;
|
|
1066
|
+
}
|
|
999
1067
|
renderAction(node, action, index) {
|
|
1068
|
+
var _b, _c, _d;
|
|
1000
1069
|
const config = ACTION_CONFIG[action.type];
|
|
1001
1070
|
const isRemoving = this.actionRemovingState.has(action.uuid);
|
|
1071
|
+
const isLocalizable = (config === null || config === void 0 ? void 0 : config.localizable) && config.localizable.length > 0;
|
|
1072
|
+
const isDisabled = this.isTranslating && !isLocalizable;
|
|
1073
|
+
// Check if this action has localization data
|
|
1074
|
+
const hasLocalization = this.isTranslating &&
|
|
1075
|
+
((_d = (_c = (_b = this.flowDefinition) === null || _b === void 0 ? void 0 : _b.localization) === null || _c === void 0 ? void 0 : _c[this.languageCode]) === null || _d === void 0 ? void 0 : _d[action.uuid]);
|
|
1076
|
+
// Get the localized action if translating
|
|
1077
|
+
const displayAction = this.getLocalizedAction(action);
|
|
1002
1078
|
if (config) {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1079
|
+
const classes = [
|
|
1080
|
+
'action',
|
|
1081
|
+
'sortable',
|
|
1082
|
+
action.type,
|
|
1083
|
+
isRemoving ? 'removing' : '',
|
|
1084
|
+
isLocalizable && this.isTranslating ? 'localizable' : '',
|
|
1085
|
+
hasLocalization ? 'has-localization' : '',
|
|
1086
|
+
isDisabled ? 'non-localizable' : ''
|
|
1087
|
+
]
|
|
1088
|
+
.filter(Boolean)
|
|
1089
|
+
.join(' ');
|
|
1090
|
+
return html `<div class="${classes}" id="action-${index}">
|
|
1007
1091
|
<div
|
|
1008
1092
|
class="action-content"
|
|
1009
|
-
@mousedown=${(e) => this.handleActionMouseDown(e, action)}
|
|
1010
|
-
@mouseup=${(e) => this.handleActionMouseUp(e, action)}
|
|
1011
|
-
style="cursor:
|
|
1093
|
+
@mousedown=${(e) => !isDisabled && this.handleActionMouseDown(e, action)}
|
|
1094
|
+
@mouseup=${(e) => !isDisabled && this.handleActionMouseUp(e, action)}
|
|
1095
|
+
style="cursor: ${isDisabled ? 'not-allowed' : 'pointer'}"
|
|
1012
1096
|
>
|
|
1013
1097
|
${this.renderTitle(config, action, index, isRemoving)}
|
|
1014
1098
|
<div class="body">
|
|
1015
1099
|
${config.render
|
|
1016
|
-
? config.render(node,
|
|
1100
|
+
? config.render(node, displayAction)
|
|
1017
1101
|
: html `<pre>${action.type}</pre>`}
|
|
1018
1102
|
</div>
|
|
1019
1103
|
</div>
|
|
@@ -1071,19 +1155,48 @@ export class CanvasNode extends RapidElement {
|
|
|
1071
1155
|
}
|
|
1072
1156
|
}
|
|
1073
1157
|
renderCategories(node) {
|
|
1158
|
+
var _b;
|
|
1074
1159
|
if (!node.router || !node.router.categories) {
|
|
1075
1160
|
return null;
|
|
1076
1161
|
}
|
|
1162
|
+
// Check if this node type supports category localization
|
|
1163
|
+
const nodeConfig = NODE_CONFIG[(_b = this.ui) === null || _b === void 0 ? void 0 : _b.type];
|
|
1164
|
+
const supportsLocalization = (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.localizable) === 'categories';
|
|
1077
1165
|
return html `<div class="categories">
|
|
1078
1166
|
${repeat(node.router.categories, (category) => category.uuid, (category) => {
|
|
1167
|
+
var _b, _c;
|
|
1079
1168
|
const exit = node.exits.find((exit) => exit.uuid == category.exit_uuid);
|
|
1169
|
+
// Get localized category name if translating
|
|
1170
|
+
let displayName = category.name;
|
|
1171
|
+
let isLocalized = false;
|
|
1172
|
+
if (this.isTranslating &&
|
|
1173
|
+
this.languageCode !== 'eng' &&
|
|
1174
|
+
supportsLocalization) {
|
|
1175
|
+
const localization = (_c = (_b = this.flowDefinition) === null || _b === void 0 ? void 0 : _b.localization) === null || _c === void 0 ? void 0 : _c[this.languageCode];
|
|
1176
|
+
if (localization && localization[category.uuid]) {
|
|
1177
|
+
const categoryLocalization = localization[category.uuid];
|
|
1178
|
+
if (categoryLocalization.name && categoryLocalization.name[0]) {
|
|
1179
|
+
displayName = categoryLocalization.name[0];
|
|
1180
|
+
isLocalized = true;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
// Category is localizable if: translating, supports localization, categories enabled, and not base language
|
|
1185
|
+
const isLocalizable = this.isTranslating &&
|
|
1186
|
+
this.languageCode !== 'eng' &&
|
|
1187
|
+
supportsLocalization &&
|
|
1188
|
+
this.includeCategoriesInTranslation &&
|
|
1189
|
+
!isLocalized;
|
|
1080
1190
|
return html `<div
|
|
1081
|
-
class
|
|
1191
|
+
class=${getClasses({
|
|
1192
|
+
category: true,
|
|
1193
|
+
localizable: isLocalizable
|
|
1194
|
+
})}
|
|
1082
1195
|
@mousedown=${(e) => this.handleNodeMouseDown(e)}
|
|
1083
1196
|
@mouseup=${(e) => this.handleNodeMouseUp(e)}
|
|
1084
1197
|
style="cursor: pointer;"
|
|
1085
1198
|
>
|
|
1086
|
-
<div class="cn-title">${
|
|
1199
|
+
<div class="cn-title">${displayName}</div>
|
|
1087
1200
|
${this.renderExit(exit)}
|
|
1088
1201
|
</div>`;
|
|
1089
1202
|
})}
|
|
@@ -1107,12 +1220,19 @@ export class CanvasNode extends RapidElement {
|
|
|
1107
1220
|
return html `<div class="node">Loading...</div>`;
|
|
1108
1221
|
}
|
|
1109
1222
|
const nodeConfig = NODE_CONFIG[this.ui.type];
|
|
1223
|
+
// Check if this node should be disabled (grayed out)
|
|
1224
|
+
const supportsLocalization = (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.localizable) === 'categories';
|
|
1225
|
+
const isNodeDisabled = this.isTranslating &&
|
|
1226
|
+
supportsLocalization &&
|
|
1227
|
+
!this.includeCategoriesInTranslation;
|
|
1110
1228
|
return html `
|
|
1111
1229
|
<div
|
|
1112
1230
|
id="${this.node.uuid}"
|
|
1113
|
-
class
|
|
1114
|
-
|
|
1115
|
-
: ''
|
|
1231
|
+
class=${getClasses({
|
|
1232
|
+
node: true,
|
|
1233
|
+
'execute-actions': this.ui.type === 'execute_actions',
|
|
1234
|
+
'non-localizable': isNodeDisabled
|
|
1235
|
+
})}
|
|
1116
1236
|
style="left:${this.ui.position.left}px;top:${this.ui.position.top}px"
|
|
1117
1237
|
>
|
|
1118
1238
|
${nodeConfig && nodeConfig.type !== 'execute_actions'
|
|
@@ -1144,12 +1264,14 @@ export class CanvasNode extends RapidElement {
|
|
|
1144
1264
|
: html `${this.node.actions.map((action, index) => this.renderAction(this.node, action, index))}`
|
|
1145
1265
|
: ''}
|
|
1146
1266
|
${this.node.router
|
|
1147
|
-
? html
|
|
1148
|
-
|
|
1267
|
+
? html `<div class="router-section">
|
|
1268
|
+
${this.renderRouter(this.node.router, this.ui)}
|
|
1269
|
+
${this.renderCategories(this.node)}
|
|
1270
|
+
</div>`
|
|
1149
1271
|
: html `<div class="action-exits">
|
|
1150
1272
|
${repeat(this.node.exits, (exit) => exit.uuid, (exit) => this.renderExit(exit))}
|
|
1151
1273
|
</div>`}
|
|
1152
|
-
${this.ui.type === 'execute_actions'
|
|
1274
|
+
${this.ui.type === 'execute_actions' && !this.isTranslating
|
|
1153
1275
|
? html `<div
|
|
1154
1276
|
class="add-action-button"
|
|
1155
1277
|
@click=${(e) => this.handleAddActionClick(e)}
|
|
@@ -1171,4 +1293,16 @@ __decorate([
|
|
|
1171
1293
|
__decorate([
|
|
1172
1294
|
property({ type: Object })
|
|
1173
1295
|
], CanvasNode.prototype, "ui", void 0);
|
|
1296
|
+
__decorate([
|
|
1297
|
+
fromStore(zustand, (state) => state.isTranslating)
|
|
1298
|
+
], CanvasNode.prototype, "isTranslating", void 0);
|
|
1299
|
+
__decorate([
|
|
1300
|
+
fromStore(zustand, (state) => state.languageCode)
|
|
1301
|
+
], CanvasNode.prototype, "languageCode", void 0);
|
|
1302
|
+
__decorate([
|
|
1303
|
+
fromStore(zustand, (state) => state.flowDefinition)
|
|
1304
|
+
], CanvasNode.prototype, "flowDefinition", void 0);
|
|
1305
|
+
__decorate([
|
|
1306
|
+
fromStore(zustand, (state) => { var _b, _c, _d; return ((_d = (_c = (_b = state.flowDefinition) === null || _b === void 0 ? void 0 : _b._ui) === null || _c === void 0 ? void 0 : _c.translation_filters) === null || _d === void 0 ? void 0 : _d.categories) || false; })
|
|
1307
|
+
], CanvasNode.prototype, "includeCategoriesInTranslation", void 0);
|
|
1174
1308
|
//# sourceMappingURL=CanvasNode.js.map
|