@nyaruka/temba-components 0.130.4 → 0.131.0
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 +10 -13
- package/demo/sortable-rules-demo.html +155 -0
- package/dist/temba-components.js +150 -159
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +13 -7
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/actions/send_msg.js +1 -0
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_groups.js +149 -1
- package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +1 -0
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_random.js +1 -0
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +332 -137
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/form/ArrayEditor.js +301 -30
- package/out-tsc/src/form/ArrayEditor.js.map +1 -1
- package/out-tsc/src/form/select/Omnibox.js +4 -0
- package/out-tsc/src/form/select/Omnibox.js.map +1 -1
- package/out-tsc/src/form/select/Select.js +21 -25
- package/out-tsc/src/form/select/Select.js.map +1 -1
- package/out-tsc/src/list/SortableList.js +214 -140
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +9 -5
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/test/nodes/split_by_groups.test.js +130 -0
- package/out-tsc/test/nodes/split_by_groups.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_response.test.js +522 -8
- package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
- package/out-tsc/test/temba-field-config.test.js +56 -0
- package/out-tsc/test/temba-field-config.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/with-expressions.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/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.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/send_msg/render/text-without-quick-replies.png +0 -0
- package/screenshots/truth/editor/wait.png +0 -0
- package/screenshots/truth/field-renderer/select-with-label.png +0 -0
- package/screenshots/truth/list/fields-dragging.png +0 -0
- package/screenshots/truth/list/sortable-dragging.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.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/editor/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/screenshots/truth/select/search-enabled.png +0 -0
- package/screenshots/truth/select/search-selected-focus.png +0 -0
- package/screenshots/truth/select/search-selected.png +0 -0
- package/screenshots/truth/templates/default.png +0 -0
- package/screenshots/truth/templates/unapproved.png +0 -0
- package/src/events.ts +6 -6
- package/src/flow/CanvasNode.ts +15 -13
- package/src/flow/actions/send_msg.ts +1 -0
- package/src/flow/nodes/split_by_groups.ts +190 -1
- package/src/flow/nodes/split_by_llm_categorize.ts +1 -0
- package/src/flow/nodes/split_by_random.ts +1 -0
- package/src/flow/nodes/wait_for_response.ts +424 -145
- package/src/form/ArrayEditor.ts +372 -30
- package/src/form/select/Omnibox.ts +3 -0
- package/src/form/select/Select.ts +24 -25
- package/src/list/SortableList.ts +250 -149
- package/src/live/ContactChat.ts +11 -5
- package/test/nodes/split_by_groups.test.ts +165 -0
- package/test/nodes/wait_for_response.test.ts +608 -8
- package/test/temba-field-config.test.ts +69 -0
|
@@ -3,12 +3,17 @@ import { html, css } from 'lit';
|
|
|
3
3
|
import { customElement, property } from 'lit/decorators.js';
|
|
4
4
|
import { BaseListEditor } from './BaseListEditor';
|
|
5
5
|
import { FieldRenderer } from './FieldRenderer';
|
|
6
|
+
import '../list/SortableList';
|
|
7
|
+
import { Icon } from '../Icons';
|
|
6
8
|
let TembaArrayEditor = class TembaArrayEditor extends BaseListEditor {
|
|
7
9
|
constructor() {
|
|
8
10
|
super();
|
|
9
11
|
this.itemConfig = {};
|
|
10
12
|
this.itemLabel = 'Item';
|
|
13
|
+
this.sortable = false;
|
|
11
14
|
this.maintainEmptyItem = true; // Enable by default for better UX
|
|
15
|
+
// Focus preservation properties
|
|
16
|
+
this.focusInfo = null;
|
|
12
17
|
this._items = [];
|
|
13
18
|
}
|
|
14
19
|
// External API
|
|
@@ -41,6 +46,181 @@ let TembaArrayEditor = class TembaArrayEditor extends BaseListEditor {
|
|
|
41
46
|
values.some((value) => value !== undefined && value !== null && value !== ''));
|
|
42
47
|
});
|
|
43
48
|
}
|
|
49
|
+
// Capture focus information before update
|
|
50
|
+
captureFocus() {
|
|
51
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
52
|
+
const activeElement = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement;
|
|
53
|
+
// Also try document.activeElement as a fallback
|
|
54
|
+
const globalActive = document.activeElement;
|
|
55
|
+
let targetElement = activeElement || globalActive;
|
|
56
|
+
// If active element is within this component's shadow root, use it
|
|
57
|
+
if (globalActive && ((_b = this.shadowRoot) === null || _b === void 0 ? void 0 : _b.contains(globalActive))) {
|
|
58
|
+
targetElement = globalActive;
|
|
59
|
+
}
|
|
60
|
+
if (!targetElement) {
|
|
61
|
+
this.focusInfo = null;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// Find the array item container by traversing up the DOM
|
|
65
|
+
let currentElement = targetElement;
|
|
66
|
+
let arrayItemElement = null;
|
|
67
|
+
// Traverse up through shadow DOM boundaries
|
|
68
|
+
while (currentElement) {
|
|
69
|
+
if ((_c = currentElement.classList) === null || _c === void 0 ? void 0 : _c.contains('array-item')) {
|
|
70
|
+
arrayItemElement = currentElement;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
// Move up to parent, or cross shadow boundaries
|
|
74
|
+
if (currentElement.parentElement) {
|
|
75
|
+
currentElement = currentElement.parentElement;
|
|
76
|
+
}
|
|
77
|
+
else if (currentElement.parentNode &&
|
|
78
|
+
currentElement.parentNode.host) {
|
|
79
|
+
// Cross shadow boundary
|
|
80
|
+
currentElement = currentElement.parentNode.host;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (!arrayItemElement) {
|
|
87
|
+
this.focusInfo = null;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Find the item index by looking at the item ID
|
|
91
|
+
const itemIdMatch = (_d = arrayItemElement.id) === null || _d === void 0 ? void 0 : _d.match(/array-item-(\d+)/);
|
|
92
|
+
if (!itemIdMatch) {
|
|
93
|
+
this.focusInfo = null;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const itemIndex = parseInt(itemIdMatch[1], 10);
|
|
97
|
+
// Determine the field name by examining the input element and its containers
|
|
98
|
+
let fieldName = '';
|
|
99
|
+
// First, check if it's a temba component with a name attribute
|
|
100
|
+
if ((_e = targetElement.tagName) === null || _e === void 0 ? void 0 : _e.toLowerCase().startsWith('temba-')) {
|
|
101
|
+
fieldName =
|
|
102
|
+
targetElement.name || targetElement.getAttribute('name') || '';
|
|
103
|
+
}
|
|
104
|
+
// If not found, check regular HTML elements
|
|
105
|
+
if (!fieldName &&
|
|
106
|
+
targetElement.hasAttribute &&
|
|
107
|
+
targetElement.hasAttribute('name')) {
|
|
108
|
+
fieldName = targetElement.getAttribute('name') || '';
|
|
109
|
+
}
|
|
110
|
+
// If still not found, look for data-field-name in parent containers
|
|
111
|
+
if (!fieldName) {
|
|
112
|
+
let searchElement = targetElement;
|
|
113
|
+
while (searchElement && searchElement !== arrayItemElement) {
|
|
114
|
+
if (searchElement.hasAttribute &&
|
|
115
|
+
searchElement.hasAttribute('data-field-name')) {
|
|
116
|
+
fieldName = searchElement.getAttribute('data-field-name') || '';
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
searchElement = searchElement.parentElement;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!fieldName) {
|
|
123
|
+
this.focusInfo = null;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Capture selection for text inputs (try the actual input element inside temba components)
|
|
127
|
+
let inputForSelection = targetElement;
|
|
128
|
+
if ((_f = targetElement.tagName) === null || _f === void 0 ? void 0 : _f.toLowerCase().startsWith('temba-')) {
|
|
129
|
+
// Look for the actual input element inside the temba component
|
|
130
|
+
const innerInput = ((_g = targetElement.shadowRoot) === null || _g === void 0 ? void 0 : _g.querySelector('input, textarea')) ||
|
|
131
|
+
targetElement.querySelector('input, textarea');
|
|
132
|
+
if (innerInput) {
|
|
133
|
+
inputForSelection = innerInput;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const selectionStart = inputForSelection.selectionStart;
|
|
137
|
+
const selectionEnd = inputForSelection.selectionEnd;
|
|
138
|
+
this.focusInfo = {
|
|
139
|
+
itemIndex,
|
|
140
|
+
fieldName,
|
|
141
|
+
selectionStart,
|
|
142
|
+
selectionEnd
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// Restore focus after update
|
|
146
|
+
restoreFocus() {
|
|
147
|
+
var _a, _b;
|
|
148
|
+
if (!this.focusInfo) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const { itemIndex, fieldName, selectionStart, selectionEnd } = this.focusInfo;
|
|
152
|
+
// Find the target element by array item index
|
|
153
|
+
const arrayItemId = `array-item-${itemIndex}`;
|
|
154
|
+
const arrayItemElement = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.getElementById(arrayItemId);
|
|
155
|
+
if (!arrayItemElement) {
|
|
156
|
+
// If the exact item doesn't exist (e.g., due to reordering), try to find by field name
|
|
157
|
+
const allItems = (_b = this.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelectorAll('.array-item');
|
|
158
|
+
if (allItems && allItems.length > itemIndex) {
|
|
159
|
+
const fallbackItem = allItems[itemIndex];
|
|
160
|
+
if (fallbackItem) {
|
|
161
|
+
this.attemptFocusRestore(fallbackItem, fieldName, selectionStart, selectionEnd);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
this.focusInfo = null;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
this.attemptFocusRestore(arrayItemElement, fieldName, selectionStart, selectionEnd);
|
|
168
|
+
this.focusInfo = null;
|
|
169
|
+
}
|
|
170
|
+
attemptFocusRestore(container, fieldName, selectionStart, selectionEnd) {
|
|
171
|
+
// Look for the field container first
|
|
172
|
+
const fieldContainer = container.querySelector(`[data-field-name="${fieldName}"]`);
|
|
173
|
+
let targetElement = null;
|
|
174
|
+
if (fieldContainer) {
|
|
175
|
+
// Look for temba components or input elements within the field container
|
|
176
|
+
targetElement = fieldContainer.querySelector('temba-textinput, temba-completion, input, textarea');
|
|
177
|
+
}
|
|
178
|
+
// Fallback: search entire container
|
|
179
|
+
if (!targetElement) {
|
|
180
|
+
const selectors = [
|
|
181
|
+
`temba-textinput[name="${fieldName}"]`,
|
|
182
|
+
`temba-completion[name="${fieldName}"]`,
|
|
183
|
+
`input[name="${fieldName}"]`,
|
|
184
|
+
`textarea[name="${fieldName}"]`,
|
|
185
|
+
`[name="${fieldName}"]`
|
|
186
|
+
];
|
|
187
|
+
for (const selector of selectors) {
|
|
188
|
+
targetElement = container.querySelector(selector);
|
|
189
|
+
if (targetElement)
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (targetElement) {
|
|
194
|
+
// Use multiple animation frames to ensure DOM is fully settled
|
|
195
|
+
requestAnimationFrame(() => {
|
|
196
|
+
requestAnimationFrame(() => {
|
|
197
|
+
var _a, _b;
|
|
198
|
+
try {
|
|
199
|
+
targetElement.focus();
|
|
200
|
+
// Restore selection if it's a text input
|
|
201
|
+
if (selectionStart !== undefined && selectionEnd !== undefined) {
|
|
202
|
+
// For temba components, we need to focus the inner input
|
|
203
|
+
let inputForSelection = targetElement;
|
|
204
|
+
if ((_a = targetElement.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase().startsWith('temba-')) {
|
|
205
|
+
const innerInput = ((_b = targetElement.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('input, textarea')) ||
|
|
206
|
+
targetElement.querySelector('input, textarea');
|
|
207
|
+
if (innerInput && 'setSelectionRange' in innerInput) {
|
|
208
|
+
inputForSelection = innerInput;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if ('setSelectionRange' in inputForSelection) {
|
|
212
|
+
inputForSelection.setSelectionRange(selectionStart, selectionEnd);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
// Ignore focus errors - element might not be focusable
|
|
218
|
+
// Focus restoration failed, silently continue
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
44
224
|
createEmptyItem() {
|
|
45
225
|
return {};
|
|
46
226
|
}
|
|
@@ -58,6 +238,85 @@ let TembaArrayEditor = class TembaArrayEditor extends BaseListEditor {
|
|
|
58
238
|
}
|
|
59
239
|
this.updateValue(updatedItems);
|
|
60
240
|
}
|
|
241
|
+
// Override Lit's update lifecycle methods for focus preservation
|
|
242
|
+
willUpdate(changedProperties) {
|
|
243
|
+
super.willUpdate(changedProperties);
|
|
244
|
+
// Capture focus before update if items are changing
|
|
245
|
+
if (changedProperties.has('_items') || changedProperties.has('value')) {
|
|
246
|
+
this.captureFocus();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
updated(changedProperties) {
|
|
250
|
+
super.updated(changedProperties);
|
|
251
|
+
// Restore focus after update if items changed
|
|
252
|
+
if (changedProperties.has('_items') || changedProperties.has('value')) {
|
|
253
|
+
this.restoreFocus();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
handleOrderChanged(event) {
|
|
257
|
+
const detail = event.detail;
|
|
258
|
+
// Handle swap-based logic from SortableList
|
|
259
|
+
if (detail.swap && Array.isArray(detail.swap) && detail.swap.length === 2) {
|
|
260
|
+
const [fromIdx, toIdx] = detail.swap;
|
|
261
|
+
// Only reorder if the indexes are different and valid
|
|
262
|
+
if (fromIdx !== toIdx &&
|
|
263
|
+
fromIdx >= 0 &&
|
|
264
|
+
toIdx >= 0 &&
|
|
265
|
+
fromIdx < this._items.length &&
|
|
266
|
+
toIdx < this._items.length) {
|
|
267
|
+
const updatedItems = [...this._items];
|
|
268
|
+
// Move the item using splice operations
|
|
269
|
+
const movedItem = updatedItems.splice(fromIdx, 1)[0];
|
|
270
|
+
updatedItems.splice(toIdx, 0, movedItem);
|
|
271
|
+
this.updateValue(updatedItems);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
renderWidget() {
|
|
276
|
+
const items = this.displayItems;
|
|
277
|
+
const itemsContent = items.map((item, index) => {
|
|
278
|
+
const renderedItem = this.renderItem(item, index);
|
|
279
|
+
if (this.sortable && !this.isEmptyItem(item)) {
|
|
280
|
+
// Wrap non-empty items with sortable class and unique ID for drag-and-drop
|
|
281
|
+
return html `
|
|
282
|
+
<div class="sortable" id="array-item-${index}">${renderedItem}</div>
|
|
283
|
+
`;
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
// Non-sortable items or empty items don't get the sortable wrapper
|
|
287
|
+
return renderedItem;
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
if (this.sortable) {
|
|
291
|
+
return html `
|
|
292
|
+
<div class=${this.getContainerClass()}>
|
|
293
|
+
<temba-sortable-list
|
|
294
|
+
dragHandle="drag-handle"
|
|
295
|
+
gap="0.4em"
|
|
296
|
+
@temba-order-changed=${this.handleOrderChanged}
|
|
297
|
+
style="display: grid; grid-template-columns: 1fr; gap: 8px;"
|
|
298
|
+
>
|
|
299
|
+
${itemsContent}
|
|
300
|
+
</temba-sortable-list>
|
|
301
|
+
${this.shouldShowAddButton() ? this.renderAddButton() : ''}
|
|
302
|
+
</div>
|
|
303
|
+
`;
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
// Non-sortable rendering (original behavior)
|
|
307
|
+
return html `
|
|
308
|
+
<div class=${this.getContainerClass()}>
|
|
309
|
+
<div
|
|
310
|
+
class="list-items"
|
|
311
|
+
style="display: grid; grid-template-columns: 1fr; gap: 8px;"
|
|
312
|
+
>
|
|
313
|
+
${itemsContent}
|
|
314
|
+
</div>
|
|
315
|
+
${this.shouldShowAddButton() ? this.renderAddButton() : ''}
|
|
316
|
+
</div>
|
|
317
|
+
`;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
61
320
|
computeFieldValue(itemIndex, fieldName, config) {
|
|
62
321
|
const item = this._items[itemIndex] || {};
|
|
63
322
|
const currentValue = item[fieldName];
|
|
@@ -138,11 +397,10 @@ let TembaArrayEditor = class TembaArrayEditor extends BaseListEditor {
|
|
|
138
397
|
}
|
|
139
398
|
fieldElements.push(html `
|
|
140
399
|
<div
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
: 'field-flex'}"
|
|
400
|
+
data-field-name="${fieldName}"
|
|
401
|
+
style="${config.width || config.maxWidth || config.type === 'select'
|
|
402
|
+
? 'flex:none'
|
|
403
|
+
: 'flex:1'}"
|
|
146
404
|
>
|
|
147
405
|
${this.renderArrayField(index, fieldName, config)}
|
|
148
406
|
</div>
|
|
@@ -152,15 +410,35 @@ let TembaArrayEditor = class TembaArrayEditor extends BaseListEditor {
|
|
|
152
410
|
// If no value fields are visible, add a spacer to maintain alignment
|
|
153
411
|
if (!hasVisibleValueField) {
|
|
154
412
|
// Insert spacer after operator (first field) and before category (last field)
|
|
155
|
-
fieldElements.splice(-1, 0, html `<div class="field field-flex spacer"></div>`);
|
|
413
|
+
fieldElements.splice(-1, 0, html `<div class="field field-flex spacer" style="flex-grow:1"></div>`);
|
|
156
414
|
}
|
|
157
415
|
return html `
|
|
158
|
-
<div class="array-item">
|
|
159
|
-
<div
|
|
416
|
+
<div class="array-item" id="array-item-${index}">
|
|
417
|
+
<div
|
|
418
|
+
class="item-fields ${canRemove ? '' : 'removable'}"
|
|
419
|
+
style="display: flex; gap: 12px; align-items: center"
|
|
420
|
+
>
|
|
421
|
+
${this.sortable
|
|
422
|
+
? html `<temba-icon
|
|
423
|
+
name=${Icon.sort}
|
|
424
|
+
style="margin-right: -6px;"
|
|
425
|
+
class="drag-handle"
|
|
426
|
+
></temba-icon>`
|
|
427
|
+
: null}
|
|
160
428
|
${fieldElements}
|
|
161
429
|
<button
|
|
162
430
|
@click=${canRemove ? () => this.removeItem(index) : undefined}
|
|
163
|
-
class="remove-btn
|
|
431
|
+
class="remove-btn"
|
|
432
|
+
style="
|
|
433
|
+
padding: 4px;
|
|
434
|
+
border: 1px solid #ccc;
|
|
435
|
+
border-radius: 4px;
|
|
436
|
+
background: white;
|
|
437
|
+
cursor: pointer;
|
|
438
|
+
background: #fefefe;
|
|
439
|
+
color: #999;
|
|
440
|
+
font-size: 14px;
|
|
441
|
+
"
|
|
164
442
|
?disabled=${!canRemove}
|
|
165
443
|
>
|
|
166
444
|
<temba-icon name="x"></temba-icon>
|
|
@@ -176,12 +454,6 @@ let TembaArrayEditor = class TembaArrayEditor extends BaseListEditor {
|
|
|
176
454
|
return css `
|
|
177
455
|
${super.styles}
|
|
178
456
|
|
|
179
|
-
.array-editor {
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
.array-item {
|
|
183
|
-
}
|
|
184
|
-
|
|
185
457
|
.item-header {
|
|
186
458
|
display: flex;
|
|
187
459
|
justify-content: space-between;
|
|
@@ -193,24 +465,10 @@ let TembaArrayEditor = class TembaArrayEditor extends BaseListEditor {
|
|
|
193
465
|
color: #333;
|
|
194
466
|
}
|
|
195
467
|
|
|
196
|
-
.item-fields {
|
|
197
|
-
display: flex;
|
|
198
|
-
gap: 12px;
|
|
199
|
-
align-items: center;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
468
|
.field {
|
|
203
469
|
/* Base field styles */
|
|
204
470
|
}
|
|
205
471
|
|
|
206
|
-
.field-flex {
|
|
207
|
-
flex: 1; /* Grow to fill remaining space */
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.field-fixed {
|
|
211
|
-
flex: none; /* Don't grow, use content/maxWidth size */
|
|
212
|
-
}
|
|
213
|
-
|
|
214
472
|
.spacer {
|
|
215
473
|
/* Empty spacer to maintain layout alignment */
|
|
216
474
|
}
|
|
@@ -235,10 +493,20 @@ let TembaArrayEditor = class TembaArrayEditor extends BaseListEditor {
|
|
|
235
493
|
color: #999;
|
|
236
494
|
}
|
|
237
495
|
|
|
238
|
-
.remove-btn
|
|
496
|
+
.removable .remove-btn {
|
|
239
497
|
visibility: hidden;
|
|
240
498
|
cursor: default;
|
|
241
499
|
}
|
|
500
|
+
|
|
501
|
+
.removable .drag-handle {
|
|
502
|
+
visibility: hidden;
|
|
503
|
+
cursor: default;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.drag-handle {
|
|
507
|
+
cursor: grab;
|
|
508
|
+
color: #ccc;
|
|
509
|
+
}
|
|
242
510
|
`;
|
|
243
511
|
}
|
|
244
512
|
};
|
|
@@ -254,6 +522,9 @@ __decorate([
|
|
|
254
522
|
__decorate([
|
|
255
523
|
property({ type: Function })
|
|
256
524
|
], TembaArrayEditor.prototype, "isEmptyItemFn", void 0);
|
|
525
|
+
__decorate([
|
|
526
|
+
property({ type: Boolean })
|
|
527
|
+
], TembaArrayEditor.prototype, "sortable", void 0);
|
|
257
528
|
__decorate([
|
|
258
529
|
property({ type: Boolean })
|
|
259
530
|
], TembaArrayEditor.prototype, "maintainEmptyItem", void 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArrayEditor.js","sourceRoot":"","sources":["../../../src/form/ArrayEditor.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { FieldConfig } from '../flow/types';\nimport { BaseListEditor, ListItem } from './BaseListEditor';\nimport { FieldRenderer } from './FieldRenderer';\n\n@customElement('temba-array-editor')\nexport class TembaArrayEditor extends BaseListEditor<ListItem> {\n @property({ type: Object })\n itemConfig: Record<string, FieldConfig> = {};\n\n @property({ type: String })\n itemLabel = 'Item';\n\n @property({ type: Function })\n onItemChange?: (\n itemIndex: number,\n field: string,\n value: any,\n allItems: any[]\n ) => any[];\n\n @property({ type: Function })\n isEmptyItemFn?: (item: any) => boolean;\n\n @property({ type: Boolean })\n maintainEmptyItem = true; // Enable by default for better UX\n\n constructor() {\n super();\n this._items = [];\n }\n\n // External API\n @property({ type: Array })\n get value(): any[] {\n return [...this._items];\n }\n\n set value(newValue: any[]) {\n this._items = newValue || [];\n this.requestUpdate();\n }\n\n // Implement abstract methods\n isEmptyItem(item: ListItem): boolean {\n // Use configurable function if provided\n if (this.isEmptyItemFn) {\n return this.isEmptyItemFn(item);\n }\n\n // Default behavior: check if all values are empty\n const values = Object.values(item);\n if (values.length === 0) {\n return true;\n }\n\n return values.every(\n (value) => value === undefined || value === null || value === ''\n );\n }\n\n // Override cleanItems to be more permissive for form data\n protected cleanItems(items: ListItem[]): any {\n // For runtime attachments, keep items that have at least one non-empty field\n return items.filter((item) => {\n const values = Object.values(item);\n return (\n values.length > 0 &&\n values.some(\n (value) => value !== undefined && value !== null && value !== ''\n )\n );\n });\n }\n\n createEmptyItem(): ListItem {\n return {};\n }\n\n protected handleFieldChange(\n itemIndex: number,\n fieldName: string,\n newValue: any\n ) {\n let updatedItems: any[];\n\n if (this.onItemChange) {\n updatedItems = this.onItemChange(\n itemIndex,\n fieldName,\n newValue,\n this._items\n );\n } else {\n updatedItems = [...this._items];\n updatedItems[itemIndex] = {\n ...updatedItems[itemIndex],\n [fieldName]: newValue\n };\n }\n\n this.updateValue(updatedItems);\n }\n\n private computeFieldValue(\n itemIndex: number,\n fieldName: string,\n config: FieldConfig\n ): any {\n const item = this._items[itemIndex] || {};\n const currentValue = item[fieldName];\n\n if (config.computeValue) {\n return config.computeValue(item, currentValue);\n }\n\n // For select fields, ensure we return the right type\n /*if (config.type === 'select') {\n console.log('computeFieldValue select', currentValue, config);\n const selectConfig = config as SelectFieldConfig;\n if (currentValue === undefined || currentValue === null) {\n return selectConfig.multi ? [] : '';\n }\n }*/\n\n return currentValue;\n }\n\n private renderArrayField(\n itemIndex: number,\n fieldName: string,\n config: FieldConfig\n ): TemplateResult {\n const computedValue = this.computeFieldValue(itemIndex, fieldName, config);\n\n // Extract flavor from select config if available\n const flavor =\n config.type === 'select' ? (config as any).flavor || 'small' : 'small';\n\n // Build container style with width/maxWidth if specified\n let containerStyle = '';\n if (config.width) {\n containerStyle = `width: ${config.width};`;\n } else if (config.maxWidth) {\n containerStyle = `max-width: ${config.maxWidth};`;\n }\n\n // Use FieldRenderer for consistent field rendering\n const fieldContent = FieldRenderer.renderField(\n fieldName,\n config,\n computedValue,\n {\n showLabel: false, // ArrayEditor doesn't show labels for individual fields\n flavor: flavor,\n extraClasses: 'form-control',\n onChange: (e: Event) => {\n let value: any;\n const target = e.target as any;\n\n // Handle different field types and their change events\n if (config.type === 'select') {\n // Use consistent temba-select value normalization\n value = target.values;\n } else {\n // For other field types, use the target value directly\n value = target.value;\n }\n\n this.handleFieldChange(itemIndex, fieldName, value);\n }\n }\n );\n\n // Wrap in container with style if maxWidth is specified\n if (containerStyle) {\n return html`<div style=\"${containerStyle}\">${fieldContent}</div>`;\n }\n\n return fieldContent;\n }\n\n renderItem(item: ListItem, index: number): TemplateResult {\n const canRemove = this.canRemoveItem(index);\n\n // Render fields and track if any value fields are visible\n const fieldElements: TemplateResult[] = [];\n let hasVisibleValueField = false;\n\n Object.entries(this.itemConfig).forEach(([fieldName, config]) => {\n // Check visibility condition\n let isVisible = true;\n if (config.conditions?.visible) {\n try {\n const currentItem = this._items[index] || {};\n isVisible = config.conditions.visible(currentItem);\n } catch (error) {\n console.error(`Error checking visibility for ${fieldName}:`, error);\n }\n }\n\n if (isVisible) {\n // Check if this is a value field (text input without fixed sizing)\n const isValueField =\n !config.width && !config.maxWidth && config.type === 'text';\n if (isValueField) {\n hasVisibleValueField = true;\n }\n\n fieldElements.push(html`\n <div\n class=\"field ${config.width ||\n config.maxWidth ||\n config.type === 'select'\n ? 'field-fixed'\n : 'field-flex'}\"\n >\n ${this.renderArrayField(index, fieldName, config)}\n </div>\n `);\n }\n });\n\n // If no value fields are visible, add a spacer to maintain alignment\n if (!hasVisibleValueField) {\n // Insert spacer after operator (first field) and before category (last field)\n fieldElements.splice(\n -1,\n 0,\n html`<div class=\"field field-flex spacer\"></div>`\n );\n }\n\n return html`\n <div class=\"array-item\">\n <div class=\"item-fields\">\n ${fieldElements}\n <button\n @click=${canRemove ? () => this.removeItem(index) : undefined}\n class=\"remove-btn ${canRemove ? '' : 'invisible'}\"\n ?disabled=${!canRemove}\n >\n <temba-icon name=\"x\"></temba-icon>\n </button>\n </div>\n </div>\n `;\n }\n\n protected getContainerClass(): string {\n return 'array-editor';\n }\n\n static get styles() {\n return css`\n ${super.styles}\n\n .array-editor {\n }\n\n .array-item {\n }\n\n .item-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .item-title {\n font-weight: 600;\n color: #333;\n }\n\n .item-fields {\n display: flex;\n gap: 12px;\n align-items: center;\n }\n\n .field {\n /* Base field styles */\n }\n\n .field-flex {\n flex: 1; /* Grow to fill remaining space */\n }\n\n .field-fixed {\n flex: none; /* Don't grow, use content/maxWidth size */\n }\n\n .spacer {\n /* Empty spacer to maintain layout alignment */\n }\n\n .add-btn,\n .remove-btn {\n padding: 4px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 14px;\n }\n\n .add-btn:hover,\n .remove-btn:hover {\n background: #f8f8f8;\n }\n\n .remove-btn {\n background: #fefefe;\n color: #999;\n }\n\n .remove-btn.invisible {\n visibility: hidden;\n cursor: default;\n }\n `;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ArrayEditor.js","sourceRoot":"","sources":["../../../src/form/ArrayEditor.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { FieldConfig } from '../flow/types';\nimport { BaseListEditor, ListItem } from './BaseListEditor';\nimport { FieldRenderer } from './FieldRenderer';\nimport '../list/SortableList';\nimport { Icon } from '../Icons';\n\n@customElement('temba-array-editor')\nexport class TembaArrayEditor extends BaseListEditor<ListItem> {\n @property({ type: Object })\n itemConfig: Record<string, FieldConfig> = {};\n\n @property({ type: String })\n itemLabel = 'Item';\n\n @property({ type: Function })\n onItemChange?: (\n itemIndex: number,\n field: string,\n value: any,\n allItems: any[]\n ) => any[];\n\n @property({ type: Function })\n isEmptyItemFn?: (item: any) => boolean;\n\n @property({ type: Boolean })\n sortable = false;\n\n @property({ type: Boolean })\n maintainEmptyItem = true; // Enable by default for better UX\n\n // Focus preservation properties\n private focusInfo: {\n itemIndex: number;\n fieldName: string;\n selectionStart?: number;\n selectionEnd?: number;\n } | null = null;\n\n constructor() {\n super();\n this._items = [];\n }\n\n // External API\n @property({ type: Array })\n get value(): any[] {\n return [...this._items];\n }\n\n set value(newValue: any[]) {\n this._items = newValue || [];\n this.requestUpdate();\n }\n\n // Implement abstract methods\n isEmptyItem(item: ListItem): boolean {\n // Use configurable function if provided\n if (this.isEmptyItemFn) {\n return this.isEmptyItemFn(item);\n }\n\n // Default behavior: check if all values are empty\n const values = Object.values(item);\n if (values.length === 0) {\n return true;\n }\n\n return values.every(\n (value) => value === undefined || value === null || value === ''\n );\n }\n\n // Override cleanItems to be more permissive for form data\n protected cleanItems(items: ListItem[]): any {\n // For runtime attachments, keep items that have at least one non-empty field\n return items.filter((item) => {\n const values = Object.values(item);\n return (\n values.length > 0 &&\n values.some(\n (value) => value !== undefined && value !== null && value !== ''\n )\n );\n });\n }\n\n // Capture focus information before update\n private captureFocus(): void {\n const activeElement = this.shadowRoot?.activeElement as HTMLElement;\n\n // Also try document.activeElement as a fallback\n const globalActive = document.activeElement as HTMLElement;\n let targetElement = activeElement || globalActive;\n\n // If active element is within this component's shadow root, use it\n if (globalActive && this.shadowRoot?.contains(globalActive)) {\n targetElement = globalActive;\n }\n\n if (!targetElement) {\n this.focusInfo = null;\n return;\n }\n\n // Find the array item container by traversing up the DOM\n let currentElement = targetElement;\n let arrayItemElement: HTMLElement | null = null;\n\n // Traverse up through shadow DOM boundaries\n while (currentElement) {\n if (currentElement.classList?.contains('array-item')) {\n arrayItemElement = currentElement;\n break;\n }\n\n // Move up to parent, or cross shadow boundaries\n if (currentElement.parentElement) {\n currentElement = currentElement.parentElement;\n } else if (\n currentElement.parentNode &&\n (currentElement.parentNode as any).host\n ) {\n // Cross shadow boundary\n currentElement = (currentElement.parentNode as any).host;\n } else {\n break;\n }\n }\n\n if (!arrayItemElement) {\n this.focusInfo = null;\n return;\n }\n\n // Find the item index by looking at the item ID\n const itemIdMatch = arrayItemElement.id?.match(/array-item-(\\d+)/);\n if (!itemIdMatch) {\n this.focusInfo = null;\n return;\n }\n\n const itemIndex = parseInt(itemIdMatch[1], 10);\n\n // Determine the field name by examining the input element and its containers\n let fieldName = '';\n\n // First, check if it's a temba component with a name attribute\n if (targetElement.tagName?.toLowerCase().startsWith('temba-')) {\n fieldName =\n (targetElement as any).name || targetElement.getAttribute('name') || '';\n }\n\n // If not found, check regular HTML elements\n if (\n !fieldName &&\n targetElement.hasAttribute &&\n targetElement.hasAttribute('name')\n ) {\n fieldName = targetElement.getAttribute('name') || '';\n }\n\n // If still not found, look for data-field-name in parent containers\n if (!fieldName) {\n let searchElement = targetElement;\n while (searchElement && searchElement !== arrayItemElement) {\n if (\n searchElement.hasAttribute &&\n searchElement.hasAttribute('data-field-name')\n ) {\n fieldName = searchElement.getAttribute('data-field-name') || '';\n break;\n }\n searchElement = searchElement.parentElement;\n }\n }\n\n if (!fieldName) {\n this.focusInfo = null;\n return;\n }\n\n // Capture selection for text inputs (try the actual input element inside temba components)\n let inputForSelection = targetElement;\n if (targetElement.tagName?.toLowerCase().startsWith('temba-')) {\n // Look for the actual input element inside the temba component\n const innerInput =\n targetElement.shadowRoot?.querySelector('input, textarea') ||\n targetElement.querySelector('input, textarea');\n if (innerInput) {\n inputForSelection = innerInput as HTMLElement;\n }\n }\n\n const selectionStart = (inputForSelection as any).selectionStart;\n const selectionEnd = (inputForSelection as any).selectionEnd;\n\n this.focusInfo = {\n itemIndex,\n fieldName,\n selectionStart,\n selectionEnd\n };\n }\n\n // Restore focus after update\n private restoreFocus(): void {\n if (!this.focusInfo) {\n return;\n }\n\n const { itemIndex, fieldName, selectionStart, selectionEnd } =\n this.focusInfo;\n\n // Find the target element by array item index\n const arrayItemId = `array-item-${itemIndex}`;\n const arrayItemElement = this.shadowRoot?.getElementById(arrayItemId);\n\n if (!arrayItemElement) {\n // If the exact item doesn't exist (e.g., due to reordering), try to find by field name\n const allItems = this.shadowRoot?.querySelectorAll('.array-item');\n if (allItems && allItems.length > itemIndex) {\n const fallbackItem = allItems[itemIndex];\n if (fallbackItem) {\n this.attemptFocusRestore(\n fallbackItem as HTMLElement,\n fieldName,\n selectionStart,\n selectionEnd\n );\n }\n }\n this.focusInfo = null;\n return;\n }\n\n this.attemptFocusRestore(\n arrayItemElement,\n fieldName,\n selectionStart,\n selectionEnd\n );\n this.focusInfo = null;\n }\n\n private attemptFocusRestore(\n container: HTMLElement,\n fieldName: string,\n selectionStart?: number,\n selectionEnd?: number\n ): void {\n // Look for the field container first\n const fieldContainer = container.querySelector(\n `[data-field-name=\"${fieldName}\"]`\n );\n\n let targetElement: HTMLElement | null = null;\n\n if (fieldContainer) {\n // Look for temba components or input elements within the field container\n targetElement = fieldContainer.querySelector(\n 'temba-textinput, temba-completion, input, textarea'\n ) as HTMLElement;\n }\n\n // Fallback: search entire container\n if (!targetElement) {\n const selectors = [\n `temba-textinput[name=\"${fieldName}\"]`,\n `temba-completion[name=\"${fieldName}\"]`,\n `input[name=\"${fieldName}\"]`,\n `textarea[name=\"${fieldName}\"]`,\n `[name=\"${fieldName}\"]`\n ];\n\n for (const selector of selectors) {\n targetElement = container.querySelector(selector) as HTMLElement;\n if (targetElement) break;\n }\n }\n\n if (targetElement) {\n // Use multiple animation frames to ensure DOM is fully settled\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n try {\n targetElement.focus();\n\n // Restore selection if it's a text input\n if (selectionStart !== undefined && selectionEnd !== undefined) {\n // For temba components, we need to focus the inner input\n let inputForSelection = targetElement;\n if (targetElement.tagName?.toLowerCase().startsWith('temba-')) {\n const innerInput =\n targetElement.shadowRoot?.querySelector('input, textarea') ||\n targetElement.querySelector('input, textarea');\n if (innerInput && 'setSelectionRange' in innerInput) {\n inputForSelection = innerInput as any;\n }\n }\n\n if ('setSelectionRange' in inputForSelection) {\n (inputForSelection as any).setSelectionRange(\n selectionStart,\n selectionEnd\n );\n }\n }\n } catch (error) {\n // Ignore focus errors - element might not be focusable\n // Focus restoration failed, silently continue\n }\n });\n });\n }\n }\n\n createEmptyItem(): ListItem {\n return {};\n }\n\n protected handleFieldChange(\n itemIndex: number,\n fieldName: string,\n newValue: any\n ) {\n let updatedItems: any[];\n\n if (this.onItemChange) {\n updatedItems = this.onItemChange(\n itemIndex,\n fieldName,\n newValue,\n this._items\n );\n } else {\n updatedItems = [...this._items];\n updatedItems[itemIndex] = {\n ...updatedItems[itemIndex],\n [fieldName]: newValue\n };\n }\n\n this.updateValue(updatedItems);\n }\n\n // Override Lit's update lifecycle methods for focus preservation\n protected willUpdate(changedProperties: Map<string, any>): void {\n super.willUpdate(changedProperties);\n\n // Capture focus before update if items are changing\n if (changedProperties.has('_items') || changedProperties.has('value')) {\n this.captureFocus();\n }\n }\n\n updated(changedProperties: Map<string, any>): void {\n super.updated(changedProperties);\n\n // Restore focus after update if items changed\n if (changedProperties.has('_items') || changedProperties.has('value')) {\n this.restoreFocus();\n }\n }\n\n private handleOrderChanged(event: CustomEvent): void {\n const detail = event.detail;\n\n // Handle swap-based logic from SortableList\n if (detail.swap && Array.isArray(detail.swap) && detail.swap.length === 2) {\n const [fromIdx, toIdx] = detail.swap;\n\n // Only reorder if the indexes are different and valid\n if (\n fromIdx !== toIdx &&\n fromIdx >= 0 &&\n toIdx >= 0 &&\n fromIdx < this._items.length &&\n toIdx < this._items.length\n ) {\n const updatedItems = [...this._items];\n // Move the item using splice operations\n const movedItem = updatedItems.splice(fromIdx, 1)[0];\n updatedItems.splice(toIdx, 0, movedItem);\n this.updateValue(updatedItems);\n }\n }\n }\n\n renderWidget(): TemplateResult {\n const items = this.displayItems;\n\n const itemsContent = items.map((item, index) => {\n const renderedItem = this.renderItem(item, index);\n\n if (this.sortable && !this.isEmptyItem(item)) {\n // Wrap non-empty items with sortable class and unique ID for drag-and-drop\n return html`\n <div class=\"sortable\" id=\"array-item-${index}\">${renderedItem}</div>\n `;\n } else {\n // Non-sortable items or empty items don't get the sortable wrapper\n return renderedItem;\n }\n });\n\n if (this.sortable) {\n return html`\n <div class=${this.getContainerClass()}>\n <temba-sortable-list\n dragHandle=\"drag-handle\"\n gap=\"0.4em\"\n @temba-order-changed=${this.handleOrderChanged}\n style=\"display: grid; grid-template-columns: 1fr; gap: 8px;\"\n >\n ${itemsContent}\n </temba-sortable-list>\n ${this.shouldShowAddButton() ? this.renderAddButton() : ''}\n </div>\n `;\n } else {\n // Non-sortable rendering (original behavior)\n return html`\n <div class=${this.getContainerClass()}>\n <div\n class=\"list-items\"\n style=\"display: grid; grid-template-columns: 1fr; gap: 8px;\"\n >\n ${itemsContent}\n </div>\n ${this.shouldShowAddButton() ? this.renderAddButton() : ''}\n </div>\n `;\n }\n }\n\n private computeFieldValue(\n itemIndex: number,\n fieldName: string,\n config: FieldConfig\n ): any {\n const item = this._items[itemIndex] || {};\n const currentValue = item[fieldName];\n\n if (config.computeValue) {\n return config.computeValue(item, currentValue);\n }\n\n // For select fields, ensure we return the right type\n /*if (config.type === 'select') {\n console.log('computeFieldValue select', currentValue, config);\n const selectConfig = config as SelectFieldConfig;\n if (currentValue === undefined || currentValue === null) {\n return selectConfig.multi ? [] : '';\n }\n }*/\n\n return currentValue;\n }\n\n private renderArrayField(\n itemIndex: number,\n fieldName: string,\n config: FieldConfig\n ): TemplateResult {\n const computedValue = this.computeFieldValue(itemIndex, fieldName, config);\n\n // Extract flavor from select config if available\n const flavor =\n config.type === 'select' ? (config as any).flavor || 'small' : 'small';\n\n // Build container style with width/maxWidth if specified\n let containerStyle = '';\n if (config.width) {\n containerStyle = `width: ${config.width};`;\n } else if (config.maxWidth) {\n containerStyle = `max-width: ${config.maxWidth};`;\n }\n\n // Use FieldRenderer for consistent field rendering\n const fieldContent = FieldRenderer.renderField(\n fieldName,\n config,\n computedValue,\n {\n showLabel: false, // ArrayEditor doesn't show labels for individual fields\n flavor: flavor,\n extraClasses: 'form-control',\n onChange: (e: Event) => {\n let value: any;\n const target = e.target as any;\n\n // Handle different field types and their change events\n if (config.type === 'select') {\n // Use consistent temba-select value normalization\n value = target.values;\n } else {\n // For other field types, use the target value directly\n value = target.value;\n }\n\n this.handleFieldChange(itemIndex, fieldName, value);\n }\n }\n );\n\n // Wrap in container with style if maxWidth is specified\n if (containerStyle) {\n return html`<div style=\"${containerStyle}\">${fieldContent}</div>`;\n }\n\n return fieldContent;\n }\n\n renderItem(item: ListItem, index: number): TemplateResult {\n const canRemove = this.canRemoveItem(index);\n\n // Render fields and track if any value fields are visible\n const fieldElements: TemplateResult[] = [];\n let hasVisibleValueField = false;\n\n Object.entries(this.itemConfig).forEach(([fieldName, config]) => {\n // Check visibility condition\n let isVisible = true;\n if (config.conditions?.visible) {\n try {\n const currentItem = this._items[index] || {};\n isVisible = config.conditions.visible(currentItem);\n } catch (error) {\n console.error(`Error checking visibility for ${fieldName}:`, error);\n }\n }\n\n if (isVisible) {\n // Check if this is a value field (text input without fixed sizing)\n const isValueField =\n !config.width && !config.maxWidth && config.type === 'text';\n if (isValueField) {\n hasVisibleValueField = true;\n }\n\n fieldElements.push(html`\n <div\n data-field-name=\"${fieldName}\"\n style=\"${config.width || config.maxWidth || config.type === 'select'\n ? 'flex:none'\n : 'flex:1'}\"\n >\n ${this.renderArrayField(index, fieldName, config)}\n </div>\n `);\n }\n });\n\n // If no value fields are visible, add a spacer to maintain alignment\n if (!hasVisibleValueField) {\n // Insert spacer after operator (first field) and before category (last field)\n fieldElements.splice(\n -1,\n 0,\n html`<div class=\"field field-flex spacer\" style=\"flex-grow:1\"></div>`\n );\n }\n\n return html`\n <div class=\"array-item\" id=\"array-item-${index}\">\n <div\n class=\"item-fields ${canRemove ? '' : 'removable'}\"\n style=\"display: flex; gap: 12px; align-items: center\"\n >\n ${this.sortable\n ? html`<temba-icon\n name=${Icon.sort}\n style=\"margin-right: -6px;\"\n class=\"drag-handle\"\n ></temba-icon>`\n : null}\n ${fieldElements}\n <button\n @click=${canRemove ? () => this.removeItem(index) : undefined}\n class=\"remove-btn\"\n style=\"\n padding: 4px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n background: #fefefe;\n color: #999;\n font-size: 14px;\n \"\n ?disabled=${!canRemove}\n >\n <temba-icon name=\"x\"></temba-icon>\n </button>\n </div>\n </div>\n `;\n }\n\n protected getContainerClass(): string {\n return 'array-editor';\n }\n\n static get styles() {\n return css`\n ${super.styles}\n\n .item-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .item-title {\n font-weight: 600;\n color: #333;\n }\n\n .field {\n /* Base field styles */\n }\n\n .spacer {\n /* Empty spacer to maintain layout alignment */\n }\n\n .add-btn,\n .remove-btn {\n padding: 4px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 14px;\n }\n\n .add-btn:hover,\n .remove-btn:hover {\n background: #f8f8f8;\n }\n\n .remove-btn {\n background: #fefefe;\n color: #999;\n }\n\n .removable .remove-btn {\n visibility: hidden;\n cursor: default;\n }\n\n .removable .drag-handle {\n visibility: hidden;\n cursor: default;\n }\n\n .drag-handle {\n cursor: grab;\n color: #ccc;\n }\n `;\n }\n}\n"]}
|
|
@@ -22,6 +22,7 @@ export class Omnibox extends Select {
|
|
|
22
22
|
this.contacts = false;
|
|
23
23
|
this.placeholder = 'Select recipients';
|
|
24
24
|
this.multi = true;
|
|
25
|
+
this.jsonValue = true;
|
|
25
26
|
this.searchable = true;
|
|
26
27
|
this.searchOnFocus = true;
|
|
27
28
|
this.queryParam = 'search';
|
|
@@ -115,6 +116,9 @@ __decorate([
|
|
|
115
116
|
__decorate([
|
|
116
117
|
property({ type: Boolean })
|
|
117
118
|
], Omnibox.prototype, "multi", void 0);
|
|
119
|
+
__decorate([
|
|
120
|
+
property({ type: Boolean })
|
|
121
|
+
], Omnibox.prototype, "jsonValue", void 0);
|
|
118
122
|
__decorate([
|
|
119
123
|
property({ type: Boolean })
|
|
120
124
|
], Omnibox.prototype, "searchable", void 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Omnibox.js","sourceRoot":"","sources":["../../../../src/form/select/Omnibox.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"Omnibox.js","sourceRoot":"","sources":["../../../../src/form/select/Omnibox.ts"],"names":[],"mappings":"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","sourcesContent":["import { TemplateResult, html, PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { styleMap } from 'lit-html/directives/style-map.js';\nimport { Select, SelectOption } from './Select';\nimport { Icon } from '../../Icons';\n\nenum OmniType {\n Group = 'group',\n Contact = 'contact'\n}\n\nexport interface OmniOption extends SelectOption {\n id: string;\n name: string;\n type: OmniType;\n urn?: string;\n count?: number;\n contact?: string;\n scheme?: string;\n}\n\nconst postNameStyle = {\n color: 'var(--color-text-dark)',\n padding: '0px 6px',\n fontSize: '12px'\n};\n\nexport class Omnibox extends Select<OmniOption> {\n @property({ type: String })\n valueKey = 'uuid';\n\n @property({ type: Boolean })\n groups = false;\n\n @property({ type: Boolean })\n contacts = false;\n\n @property({ type: String })\n placeholder = 'Select recipients';\n\n @property({ type: Boolean })\n multi = true;\n\n @property({ type: Boolean })\n jsonValue = true;\n\n @property({ type: Boolean })\n searchable = true;\n\n @property({ type: Boolean })\n searchOnFocus = true;\n\n @property({ type: Boolean })\n queryParam = 'search';\n\n public update(changes: PropertyValues): void {\n super.update(changes);\n\n if (\n (changes.has('groups') || changes.has('contacts')) &&\n (this.groups || this.contacts)\n ) {\n let types = '&types=';\n if (this.groups) {\n types += 'g';\n }\n\n if (this.contacts) {\n types += 'c';\n }\n\n this.endpoint = this.endpoint + types;\n }\n }\n\n /** An option in the drop down */\n public renderOptionDefault(option: OmniOption): TemplateResult {\n return html`\n <div style=\"display:flex;\">\n <div style=\"margin-right: 8px\">${this.getIcon(option)}</div>\n <div style=\"flex: 1\">${option.name}</div>\n <div\n style=\"background: rgba(50, 50, 50, 0.15); margin-left: 5px; display: flex; align-items: center; border-radius: 4px\"\n >\n ${this.getPostName(option)}\n </div>\n </div>\n `;\n }\n\n private getPostName(option: OmniOption): TemplateResult {\n const style = { ...postNameStyle };\n\n if (option.urn && option.type === OmniType.Contact) {\n if (option.urn !== option.name) {\n return html`<div style=${styleMap(style)}>${option.urn}</div>`;\n }\n }\n\n if (option.type === OmniType.Group) {\n return html`\n <div style=${styleMap(style)}>${option.count.toLocaleString()}</div>\n `;\n }\n\n return null;\n }\n\n /** Selection in the multi-select select box */\n public renderSelectedItemDefault(option: OmniOption): TemplateResult {\n return html`\n <div\n style=\"flex:1 1 auto; text-overflow:ellipsis; overflow:hidden; white-space:nowrap; display: flex; align-items: stretch; color: var(--color-text-dark); font-size: 12px;\"\n >\n <div style=\"align-self: center; padding: 0px 7px; color: #bbb\">\n ${this.getIcon(option)}\n </div>\n <div\n class=\"name\"\n style=\"align-self: center; padding: 0px; font-size: 12px;\"\n >\n ${option.name}\n </div>\n <div\n style=\"background: rgba(100, 100, 100, 0.05); border-left: 1px solid rgba(100, 100, 100, 0.1); margin-left: 12px; display: flex; align-items: center\"\n >\n ${this.getPostName(option)}\n </div>\n </div>\n `;\n }\n\n private getIcon(option: OmniOption): TemplateResult {\n if (option.type === OmniType.Group) {\n return html`<temba-icon name=\"${Icon.group}\"></temba-icon>`;\n }\n\n if (option.type === OmniType.Contact) {\n return html`<temba-icon name=\"${Icon.contact}\"></temba-icon>`;\n }\n }\n}\n"]}
|