@nyaruka/temba-components 0.127.0 → 0.129.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 +25 -0
- package/demo/chart/horizontal-demo.html +81 -0
- package/demo/components/datepicker/example.html +63 -0
- package/demo/components/datepicker/range-picker-demo.html +161 -0
- package/demo/data/flows/sample-flow.json +127 -100
- package/demo/index.html +8 -0
- package/demo/static/css/prism.css +2 -0
- package/demo/static/js/prism-loader.js +12 -0
- package/demo/sticky-note-demo.html +152 -0
- package/demo/styles.css +71 -1
- 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 +509 -87
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/chart/TembaChart.js +136 -62
- package/out-tsc/src/chart/TembaChart.js.map +1 -1
- package/out-tsc/src/datepicker/DatePicker.js +11 -1
- package/out-tsc/src/datepicker/DatePicker.js.map +1 -1
- package/out-tsc/src/datepicker/RangePicker.js +595 -0
- package/out-tsc/src/datepicker/RangePicker.js.map +1 -0
- package/out-tsc/src/flow/Editor.js +210 -1
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/EditorNode.js +98 -139
- package/out-tsc/src/flow/EditorNode.js.map +1 -1
- package/out-tsc/src/flow/StickyNote.js +272 -0
- package/out-tsc/src/flow/StickyNote.js.map +1 -0
- package/out-tsc/src/interfaces.js +1 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/list/RunList.js +2 -1
- package/out-tsc/src/list/RunList.js.map +1 -1
- package/out-tsc/src/list/SortableList.js +9 -0
- package/out-tsc/src/list/SortableList.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 +33 -0
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/src/vectoricon/index.js +2 -1
- package/out-tsc/src/vectoricon/index.js.map +1 -1
- package/out-tsc/temba-modules.js +5 -1
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/temba-chart.test.js +36 -0
- package/out-tsc/test/temba-chart.test.js.map +1 -1
- package/out-tsc/test/temba-datepicker.test.js +1 -1
- package/out-tsc/test/temba-datepicker.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor-node.test.js +249 -5
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-range-picker.test.js +123 -0
- package/out-tsc/test/temba-range-picker.test.js.map +1 -0
- package/out-tsc/test/temba-select.test.js +10 -16
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/temba-webchat.test.js +4 -0
- package/out-tsc/test/temba-webchat.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +62 -0
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/datepicker/range-picker-all.png +0 -0
- package/screenshots/truth/datepicker/range-picker-button-states.png +0 -0
- package/screenshots/truth/datepicker/range-picker-default.png +0 -0
- package/screenshots/truth/datepicker/range-picker-editing-start.png +0 -0
- package/screenshots/truth/datepicker/range-picker-initial-values.png +0 -0
- package/screenshots/truth/datepicker/range-picker-min-max.png +0 -0
- package/screenshots/truth/datepicker/range-picker-week.png +0 -0
- package/screenshots/truth/datepicker/range-picker-year.png +0 -0
- package/screenshots/truth/sticky-note/blue.png +0 -0
- package/screenshots/truth/sticky-note/gray.png +0 -0
- package/screenshots/truth/sticky-note/green.png +0 -0
- package/screenshots/truth/sticky-note/pink.png +0 -0
- package/screenshots/truth/sticky-note/yellow.png +0 -0
- package/screenshots/truth/webchat/connected-state.png +0 -0
- package/src/chart/TembaChart.ts +144 -66
- package/src/datepicker/DatePicker.ts +9 -1
- package/src/datepicker/RangePicker.ts +602 -0
- package/src/flow/Editor.ts +252 -2
- package/src/flow/EditorNode.ts +98 -156
- package/src/flow/StickyNote.ts +284 -0
- package/src/interfaces.ts +2 -1
- package/src/list/RunList.ts +2 -1
- package/src/list/SortableList.ts +11 -0
- 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 +51 -1
- package/src/store/flow-definition.d.ts +8 -0
- package/src/vectoricon/index.ts +2 -1
- package/static/svg/index.pdf +137 -0
- package/temba-modules.ts +5 -1
- package/test/temba-chart.test.ts +47 -0
- package/test/temba-datepicker.test.ts +1 -1
- package/test/temba-flow-editor-node.test.ts +322 -6
- package/test/temba-range-picker.test.ts +193 -0
- package/test/temba-select.test.ts +11 -19
- package/test/temba-webchat.test.ts +7 -0
- package/test/utils.test.ts +98 -0
- package/web-dev-server.config.mjs +30 -22
- package/web-test-runner.config.mjs +2 -0
- package/demo/datepicker/example.html +0 -69
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { html } from 'lit-html';
|
|
3
3
|
import { css, unsafeCSS } from 'lit';
|
|
4
|
-
import { property } from 'lit/decorators.js';
|
|
4
|
+
import { property, state } from 'lit/decorators.js';
|
|
5
5
|
import { getStore } from '../store/Store';
|
|
6
6
|
import { fromStore, zustand } from '../store/AppState';
|
|
7
7
|
import { RapidElement } from '../RapidElement';
|
|
8
8
|
import { Plumber } from './Plumber';
|
|
9
9
|
import { EditorNode } from './EditorNode';
|
|
10
|
+
export function snapToGrid(value) {
|
|
11
|
+
return Math.round(value / 20) * 20;
|
|
12
|
+
}
|
|
10
13
|
const SAVE_QUIET_TIME = 500;
|
|
14
|
+
const DRAG_THRESHOLD = 10;
|
|
11
15
|
export class Editor extends RapidElement {
|
|
12
16
|
// unfortunately, jsplumb requires that we be in light DOM
|
|
13
17
|
createRenderRoot() {
|
|
@@ -61,6 +65,15 @@ export class Editor extends RapidElement {
|
|
|
61
65
|
margin: 20px;
|
|
62
66
|
}
|
|
63
67
|
|
|
68
|
+
#canvas > .draggable {
|
|
69
|
+
position: absolute;
|
|
70
|
+
z-index: 100;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#canvas > .dragging {
|
|
74
|
+
z-index: 10000 !important;
|
|
75
|
+
}
|
|
76
|
+
|
|
64
77
|
body .jtk-endpoint {
|
|
65
78
|
width: initial;
|
|
66
79
|
height: initial;
|
|
@@ -152,10 +165,20 @@ export class Editor extends RapidElement {
|
|
|
152
165
|
super();
|
|
153
166
|
// timer for debounced saving
|
|
154
167
|
this.saveTimer = null;
|
|
168
|
+
// Drag state
|
|
169
|
+
this.isDragging = false;
|
|
170
|
+
this.isMouseDown = false;
|
|
171
|
+
this.dragStartPos = { x: 0, y: 0 };
|
|
172
|
+
this.currentDragItem = null;
|
|
173
|
+
this.startPos = { left: 0, top: 0 };
|
|
174
|
+
// Bound event handlers to maintain proper 'this' context
|
|
175
|
+
this.boundMouseMove = this.handleMouseMove.bind(this);
|
|
176
|
+
this.boundMouseUp = this.handleMouseUp.bind(this);
|
|
155
177
|
}
|
|
156
178
|
firstUpdated(changes) {
|
|
157
179
|
super.firstUpdated(changes);
|
|
158
180
|
this.plumber = new Plumber(this.querySelector('#canvas'));
|
|
181
|
+
this.setupGlobalEventListeners();
|
|
159
182
|
if (changes.has('flow')) {
|
|
160
183
|
getStore().getState().fetchRevision(`/flow/revisions/${this.flow}`);
|
|
161
184
|
}
|
|
@@ -165,6 +188,9 @@ export class Editor extends RapidElement {
|
|
|
165
188
|
if (changes.has('canvasSize')) {
|
|
166
189
|
// console.log('Setting canvas size', this.canvasSize);
|
|
167
190
|
}
|
|
191
|
+
if (changes.has('definition')) {
|
|
192
|
+
this.updateCanvasSize();
|
|
193
|
+
}
|
|
168
194
|
if (changes.has('dirtyDate')) {
|
|
169
195
|
if (this.dirtyDate) {
|
|
170
196
|
this.debouncedSave();
|
|
@@ -199,13 +225,169 @@ export class Editor extends RapidElement {
|
|
|
199
225
|
clearTimeout(this.saveTimer);
|
|
200
226
|
this.saveTimer = null;
|
|
201
227
|
}
|
|
228
|
+
document.removeEventListener('mousemove', this.boundMouseMove);
|
|
229
|
+
document.removeEventListener('mouseup', this.boundMouseUp);
|
|
230
|
+
}
|
|
231
|
+
setupGlobalEventListeners() {
|
|
232
|
+
document.addEventListener('mousemove', this.boundMouseMove);
|
|
233
|
+
document.addEventListener('mouseup', this.boundMouseUp);
|
|
234
|
+
}
|
|
235
|
+
getPosition(uuid, type) {
|
|
236
|
+
var _a, _b, _c;
|
|
237
|
+
if (type === 'node') {
|
|
238
|
+
return (_a = this.definition._ui.nodes[uuid]) === null || _a === void 0 ? void 0 : _a.position;
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
return (_c = (_b = this.definition._ui.stickies) === null || _b === void 0 ? void 0 : _b[uuid]) === null || _c === void 0 ? void 0 : _c.position;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
updatePosition(uuid, type, position) {
|
|
245
|
+
var _a;
|
|
246
|
+
if (type === 'node') {
|
|
247
|
+
getStore().getState().updateNodePosition(uuid, position);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
const currentSticky = (_a = this.definition._ui.stickies) === null || _a === void 0 ? void 0 : _a[uuid];
|
|
251
|
+
if (currentSticky) {
|
|
252
|
+
getStore()
|
|
253
|
+
.getState()
|
|
254
|
+
.updateStickyNote(uuid, {
|
|
255
|
+
...currentSticky,
|
|
256
|
+
position
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
handleMouseDown(event) {
|
|
262
|
+
const element = event.currentTarget;
|
|
263
|
+
// Only start dragging if clicking on the element itself, not on exits or other interactive elements
|
|
264
|
+
const target = event.target;
|
|
265
|
+
if (target.classList.contains('exit') || target.closest('.exit')) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const uuid = element.getAttribute('uuid');
|
|
269
|
+
const type = element.tagName === 'TEMBA-FLOW-NODE' ? 'node' : 'sticky';
|
|
270
|
+
const position = this.getPosition(uuid, type);
|
|
271
|
+
if (!position)
|
|
272
|
+
return;
|
|
273
|
+
// Set up potential drag state, but don't start dragging yet
|
|
274
|
+
this.isMouseDown = true;
|
|
275
|
+
this.dragStartPos = { x: event.clientX, y: event.clientY };
|
|
276
|
+
this.startPos = { left: position.left, top: position.top };
|
|
277
|
+
this.currentDragItem = {
|
|
278
|
+
uuid,
|
|
279
|
+
position,
|
|
280
|
+
element,
|
|
281
|
+
type
|
|
282
|
+
};
|
|
283
|
+
event.preventDefault();
|
|
284
|
+
event.stopPropagation();
|
|
285
|
+
}
|
|
286
|
+
handleMouseMove(event) {
|
|
287
|
+
if (!this.isMouseDown || !this.currentDragItem)
|
|
288
|
+
return;
|
|
289
|
+
const deltaX = event.clientX - this.dragStartPos.x;
|
|
290
|
+
const deltaY = event.clientY - this.dragStartPos.y;
|
|
291
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
292
|
+
// Only start dragging if we've moved beyond the threshold
|
|
293
|
+
if (!this.isDragging && distance > DRAG_THRESHOLD) {
|
|
294
|
+
this.isDragging = true;
|
|
295
|
+
// If this is a node, elevate connections
|
|
296
|
+
if (this.currentDragItem.type === 'node' && this.plumber) {
|
|
297
|
+
this.plumber.elevateNodeConnections(this.currentDragItem.uuid);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// If we're actually dragging, update positions
|
|
301
|
+
if (this.isDragging) {
|
|
302
|
+
const newLeft = this.startPos.left + deltaX;
|
|
303
|
+
const newTop = this.startPos.top + deltaY;
|
|
304
|
+
// Update the visual position during drag
|
|
305
|
+
this.currentDragItem.element.style.left = `${newLeft}px`;
|
|
306
|
+
this.currentDragItem.element.style.top = `${newTop}px`;
|
|
307
|
+
// Repaint connections if this is a node
|
|
308
|
+
if (this.currentDragItem.type === 'node' && this.plumber) {
|
|
309
|
+
this.plumber.repaintEverything();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
handleMouseUp(event) {
|
|
314
|
+
if (!this.isMouseDown || !this.currentDragItem)
|
|
315
|
+
return;
|
|
316
|
+
// If we were actually dragging, handle the drag end
|
|
317
|
+
if (this.isDragging) {
|
|
318
|
+
// Restore normal z-index for node connections
|
|
319
|
+
if (this.currentDragItem.type === 'node' && this.plumber) {
|
|
320
|
+
this.plumber.restoreNodeConnections(this.currentDragItem.uuid);
|
|
321
|
+
}
|
|
322
|
+
const deltaX = event.clientX - this.dragStartPos.x;
|
|
323
|
+
const deltaY = event.clientY - this.dragStartPos.y;
|
|
324
|
+
const newLeft = this.startPos.left + deltaX;
|
|
325
|
+
const newTop = this.startPos.top + deltaY;
|
|
326
|
+
// Snap to 20px grid for final position
|
|
327
|
+
const snappedLeft = snapToGrid(newLeft);
|
|
328
|
+
const snappedTop = snapToGrid(newTop);
|
|
329
|
+
const newPosition = { left: snappedLeft, top: snappedTop };
|
|
330
|
+
// Update the store with the new snapped position
|
|
331
|
+
this.updatePosition(this.currentDragItem.uuid, this.currentDragItem.type, newPosition);
|
|
332
|
+
// Update canvas positions for nodes
|
|
333
|
+
if (this.currentDragItem.type === 'node') {
|
|
334
|
+
getStore()
|
|
335
|
+
.getState()
|
|
336
|
+
.updateCanvasPositions({
|
|
337
|
+
[this.currentDragItem.uuid]: newPosition
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
// Repaint connections if this is a node
|
|
341
|
+
if (this.currentDragItem.type === 'node' && this.plumber) {
|
|
342
|
+
this.plumber.repaintEverything();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Reset all drag state
|
|
346
|
+
this.isDragging = false;
|
|
347
|
+
this.isMouseDown = false;
|
|
348
|
+
this.currentDragItem = null;
|
|
349
|
+
}
|
|
350
|
+
updateCanvasSize() {
|
|
351
|
+
var _a;
|
|
352
|
+
if (!this.definition)
|
|
353
|
+
return;
|
|
354
|
+
const store = getStore();
|
|
355
|
+
if (!store)
|
|
356
|
+
return;
|
|
357
|
+
// Calculate required canvas size based on all elements
|
|
358
|
+
let maxWidth = 0;
|
|
359
|
+
let maxHeight = 0;
|
|
360
|
+
// Check node positions
|
|
361
|
+
this.definition.nodes.forEach((node) => {
|
|
362
|
+
const ui = this.definition._ui.nodes[node.uuid];
|
|
363
|
+
if (ui && ui.position) {
|
|
364
|
+
const nodeElement = this.querySelector(`[id="${node.uuid}"]`);
|
|
365
|
+
if (nodeElement) {
|
|
366
|
+
const rect = nodeElement.getBoundingClientRect();
|
|
367
|
+
maxWidth = Math.max(maxWidth, ui.position.left + rect.width);
|
|
368
|
+
maxHeight = Math.max(maxHeight, ui.position.top + rect.height);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
// Check sticky note positions
|
|
373
|
+
const stickies = ((_a = this.definition._ui) === null || _a === void 0 ? void 0 : _a.stickies) || {};
|
|
374
|
+
Object.values(stickies).forEach((sticky) => {
|
|
375
|
+
if (sticky.position) {
|
|
376
|
+
maxWidth = Math.max(maxWidth, sticky.position.left + 200); // Sticky note width
|
|
377
|
+
maxHeight = Math.max(maxHeight, sticky.position.top + 100); // Sticky note height
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
// Update canvas size in store
|
|
381
|
+
store.getState().expandCanvas(maxWidth, maxHeight);
|
|
202
382
|
}
|
|
203
383
|
render() {
|
|
384
|
+
var _a, _b;
|
|
204
385
|
// we have to embed our own style since we are in light DOM
|
|
205
386
|
const style = html `<style>
|
|
206
387
|
${unsafeCSS(Editor.styles.cssText)}
|
|
207
388
|
${unsafeCSS(EditorNode.styles.cssText)}
|
|
208
389
|
</style>`;
|
|
390
|
+
const stickies = ((_b = (_a = this.definition) === null || _a === void 0 ? void 0 : _a._ui) === null || _b === void 0 ? void 0 : _b.stickies) || {};
|
|
209
391
|
return html `${style}
|
|
210
392
|
<div id="editor">
|
|
211
393
|
<div
|
|
@@ -216,13 +398,34 @@ export class Editor extends RapidElement {
|
|
|
216
398
|
<div id="canvas">
|
|
217
399
|
${this.definition
|
|
218
400
|
? this.definition.nodes.map((node) => {
|
|
401
|
+
var _a;
|
|
402
|
+
const position = this.definition._ui.nodes[node.uuid].position;
|
|
403
|
+
const dragging = this.isDragging && ((_a = this.currentDragItem) === null || _a === void 0 ? void 0 : _a.uuid) === node.uuid;
|
|
219
404
|
return html `<temba-flow-node
|
|
405
|
+
class="draggable ${dragging ? 'dragging' : ''}"
|
|
406
|
+
@mousedown=${this.handleMouseDown.bind(this)}
|
|
407
|
+
uuid=${node.uuid}
|
|
408
|
+
style="left:${position.left}px; top:${position.top}px"
|
|
220
409
|
.plumber=${this.plumber}
|
|
221
410
|
.node=${node}
|
|
222
411
|
.ui=${this.definition._ui.nodes[node.uuid]}
|
|
223
412
|
></temba-flow-node>`;
|
|
224
413
|
})
|
|
225
414
|
: html `<temba-loading></temba-loading>`}
|
|
415
|
+
${Object.entries(stickies).map(([uuid, sticky]) => {
|
|
416
|
+
var _a;
|
|
417
|
+
const position = sticky.position || { left: 0, top: 0 };
|
|
418
|
+
const dragging = this.isDragging && ((_a = this.currentDragItem) === null || _a === void 0 ? void 0 : _a.uuid) === uuid;
|
|
419
|
+
return html `<temba-sticky-note
|
|
420
|
+
class="draggable ${dragging ? 'dragging' : ''}"
|
|
421
|
+
@mousedown=${this.handleMouseDown.bind(this)}
|
|
422
|
+
style="left:${position.left}px; top:${position.top}px; z-index: ${1000 +
|
|
423
|
+
position.top}"
|
|
424
|
+
uuid=${uuid}
|
|
425
|
+
.data=${sticky}
|
|
426
|
+
.dragging=${dragging}
|
|
427
|
+
></temba-sticky-note>`;
|
|
428
|
+
})}
|
|
226
429
|
</div>
|
|
227
430
|
</div>
|
|
228
431
|
</div>`;
|
|
@@ -243,4 +446,10 @@ __decorate([
|
|
|
243
446
|
__decorate([
|
|
244
447
|
fromStore(zustand, (state) => state.dirtyDate)
|
|
245
448
|
], Editor.prototype, "dirtyDate", void 0);
|
|
449
|
+
__decorate([
|
|
450
|
+
state()
|
|
451
|
+
], Editor.prototype, "isDragging", void 0);
|
|
452
|
+
__decorate([
|
|
453
|
+
state()
|
|
454
|
+
], Editor.prototype, "currentDragItem", void 0);
|
|
246
455
|
//# sourceMappingURL=Editor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Editor.js","sourceRoot":"","sources":["../../../src/flow/Editor.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, TemplateResult } from 'lit-html';\nimport { css, PropertyValueMap, unsafeCSS } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { FlowDefinition } from '../store/flow-definition';\nimport { getStore } from '../store/Store';\nimport { AppState, fromStore, zustand } from '../store/AppState';\nimport { RapidElement } from '../RapidElement';\n\nimport { Plumber } from './Plumber';\nimport { EditorNode } from './EditorNode';\n\nconst SAVE_QUIET_TIME = 500;\n\nexport class Editor extends RapidElement {\n // unfortunately, jsplumb requires that we be in light DOM\n createRenderRoot() {\n return this;\n }\n\n // this is the master plumber\n private plumber: Plumber;\n\n // timer for debounced saving\n private saveTimer: number | null = null;\n\n @property({ type: String })\n public flow: string;\n\n @property({ type: String })\n public version: string;\n\n @fromStore(zustand, (state: AppState) => state.flowDefinition)\n private definition!: FlowDefinition;\n\n @fromStore(zustand, (state: AppState) => state.canvasSize)\n private canvasSize!: { width: number; height: number };\n\n @fromStore(zustand, (state: AppState) => state.dirtyDate)\n private dirtyDate!: Date;\n\n static get styles() {\n return css`\n #editor {\n overflow: scroll;\n flex: 1;\n }\n\n #grid {\n position: relative;\n background-color: #f9f9f9;\n background-position: 10px 10px;\n background-image: linear-gradient(\n 0deg,\n transparent 24%,\n rgba(61, 177, 255, 0.15) 25%,\n rgba(61, 177, 255, 0.15) 26%,\n transparent 27%,\n transparent 74%,\n rgba(61, 177, 255, 0.15) 75%,\n rgba(61, 177, 255, 0.15) 76%,\n transparent 77%,\n transparent\n ),\n linear-gradient(\n 90deg,\n transparent 24%,\n rgba(61, 177, 255, 0.15) 25%,\n rgba(61, 177, 255, 0.15) 26%,\n transparent 27%,\n transparent 74%,\n rgba(61, 177, 255, 0.15) 75%,\n rgba(61, 177, 255, 0.15) 76%,\n transparent 77%,\n transparent\n );\n background-size: 40px 40px;\n box-shadow: inset -5px 0 10px rgba(0, 0, 0, 0.05);\n border-top: 1px solid #e0e0e0;\n display: inline-block;\n width: 100%;\n }\n\n #canvas {\n position: relative;\n padding: 20px;\n margin: 20px;\n }\n\n body .jtk-endpoint {\n width: initial;\n height: initial;\n }\n\n .jtk-endpoint {\n z-index: 600;\n }\n\n .plumb-source {\n z-index: 600;\n border: 0px solid var(--color-connectors);\n }\n\n .plumb-source.connected {\n box-shadow: 0 3px 3px 0px rgba(0, 0, 0, 0.1);\n border-radius: 50%;\n }\n\n .plumb-source circle {\n fill: tomato;\n }\n\n .plumb-source.connected circle {\n fill: #fff;\n }\n\n .plumb-source svg {\n fill: var(--color-connectors) !important;\n stroke: var(--color-connectors);\n }\n\n .plumb-target {\n margin-top: -6px;\n z-index: 600;\n opacity: 0;\n cursor: pointer;\n }\n\n body .plumb-connector path {\n stroke: var(--color-connectors) !important;\n stroke-width: 3px;\n z-index: 10;\n }\n\n body .plumb-connector {\n z-index: 10;\n }\n\n body .plumb-connector.elevated {\n z-index: 550;\n }\n\n body .plumb-connector.elevated path {\n stroke: var(--color-connectors) !important;\n stroke-width: 3px;\n z-index: 550;\n }\n\n body .plumb-connector.elevated .plumb-arrow {\n fill: var(--color-connectors);\n stroke: var(--color-connectors);\n stroke-width: 0px;\n margin-top: 6px;\n z-index: 550;\n }\n\n body .plumb-connector .plumb-arrow {\n fill: var(--color-connectors);\n stroke: var(--color-connectors);\n stroke-width: 0px;\n margin-top: 6px;\n z-index: 10;\n }\n\n body svg.jtk-connector.jtk-hover path {\n stroke: var(--color-success) !important;\n stroke-width: 3px;\n }\n\n body .plumb-connector.jtk-hover .plumb-arrow {\n fill: var(--color-success) !important;\n stroke-width: 0px;\n z-index: 10;\n }\n `;\n }\n\n constructor() {\n super();\n }\n\n protected firstUpdated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(changes);\n this.plumber = new Plumber(this.querySelector('#canvas'));\n if (changes.has('flow')) {\n getStore().getState().fetchRevision(`/flow/revisions/${this.flow}`);\n }\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('canvasSize')) {\n // console.log('Setting canvas size', this.canvasSize);\n }\n\n if (changes.has('dirtyDate')) {\n if (this.dirtyDate) {\n this.debouncedSave();\n }\n }\n }\n\n private debouncedSave(): void {\n // Clear any existing timer\n if (this.saveTimer !== null) {\n clearTimeout(this.saveTimer);\n }\n\n this.saveTimer = window.setTimeout(() => {\n const now = new Date();\n const timeSinceLastChange = now.getTime() - this.dirtyDate.getTime();\n\n if (timeSinceLastChange >= SAVE_QUIET_TIME) {\n this.saveChanges();\n this.saveTimer = null;\n } else {\n this.debouncedSave();\n }\n }, SAVE_QUIET_TIME);\n }\n\n private saveChanges(): void {\n // post the flow definition to the server\n getStore().postJSON(`/flow/revisions/${this.flow}`, this.definition);\n getStore().getState().setDirtyDate(null);\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this.saveTimer !== null) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n }\n\n public render(): TemplateResult {\n // we have to embed our own style since we are in light DOM\n const style = html`<style>\n ${unsafeCSS(Editor.styles.cssText)}\n ${unsafeCSS(EditorNode.styles.cssText)}\n </style>`;\n\n return html`${style}\n <div id=\"editor\">\n <div\n id=\"grid\"\n style=\"min-width:100%;width:${this.canvasSize.width}px; height:${this\n .canvasSize.height}px\"\n >\n <div id=\"canvas\">\n ${this.definition\n ? this.definition.nodes.map((node) => {\n return html`<temba-flow-node\n .plumber=${this.plumber}\n .node=${node}\n .ui=${this.definition._ui.nodes[node.uuid]}\n ></temba-flow-node>`;\n })\n : html`<temba-loading></temba-loading>`}\n </div>\n </div>\n </div>`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"Editor.js","sourceRoot":"","sources":["../../../src/flow/Editor.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, TemplateResult } from 'lit-html';\nimport { css, PropertyValueMap, unsafeCSS } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { FlowDefinition, FlowPosition } from '../store/flow-definition';\nimport { getStore } from '../store/Store';\nimport { AppState, fromStore, zustand } from '../store/AppState';\nimport { RapidElement } from '../RapidElement';\n\nimport { Plumber } from './Plumber';\nimport { EditorNode } from './EditorNode';\n\nexport function snapToGrid(value: number): number {\n return Math.round(value / 20) * 20;\n}\n\nconst SAVE_QUIET_TIME = 500;\n\nexport interface DraggableItem {\n uuid: string;\n position: FlowPosition;\n element: HTMLElement;\n type: 'node' | 'sticky';\n}\n\nconst DRAG_THRESHOLD = 10;\n\nexport class Editor extends RapidElement {\n // unfortunately, jsplumb requires that we be in light DOM\n createRenderRoot() {\n return this;\n }\n\n // this is the master plumber\n private plumber: Plumber;\n\n // timer for debounced saving\n private saveTimer: number | null = null;\n\n @property({ type: String })\n public flow: string;\n\n @property({ type: String })\n public version: string;\n\n @fromStore(zustand, (state: AppState) => state.flowDefinition)\n private definition!: FlowDefinition;\n\n @fromStore(zustand, (state: AppState) => state.canvasSize)\n private canvasSize!: { width: number; height: number };\n\n @fromStore(zustand, (state: AppState) => state.dirtyDate)\n private dirtyDate!: Date;\n\n // Drag state\n @state()\n private isDragging = false;\n private isMouseDown = false;\n private dragStartPos = { x: 0, y: 0 };\n\n @state()\n private currentDragItem: DraggableItem | null = null;\n private startPos = { left: 0, top: 0 };\n\n // Bound event handlers to maintain proper 'this' context\n private boundMouseMove = this.handleMouseMove.bind(this);\n private boundMouseUp = this.handleMouseUp.bind(this);\n\n static get styles() {\n return css`\n #editor {\n overflow: scroll;\n flex: 1;\n }\n\n #grid {\n position: relative;\n background-color: #f9f9f9;\n background-position: 10px 10px;\n background-image: linear-gradient(\n 0deg,\n transparent 24%,\n rgba(61, 177, 255, 0.15) 25%,\n rgba(61, 177, 255, 0.15) 26%,\n transparent 27%,\n transparent 74%,\n rgba(61, 177, 255, 0.15) 75%,\n rgba(61, 177, 255, 0.15) 76%,\n transparent 77%,\n transparent\n ),\n linear-gradient(\n 90deg,\n transparent 24%,\n rgba(61, 177, 255, 0.15) 25%,\n rgba(61, 177, 255, 0.15) 26%,\n transparent 27%,\n transparent 74%,\n rgba(61, 177, 255, 0.15) 75%,\n rgba(61, 177, 255, 0.15) 76%,\n transparent 77%,\n transparent\n );\n background-size: 40px 40px;\n box-shadow: inset -5px 0 10px rgba(0, 0, 0, 0.05);\n border-top: 1px solid #e0e0e0;\n display: inline-block;\n width: 100%;\n }\n\n #canvas {\n position: relative;\n padding: 20px;\n margin: 20px;\n }\n\n #canvas > .draggable {\n position: absolute;\n z-index: 100;\n }\n\n #canvas > .dragging {\n z-index: 10000 !important;\n }\n\n body .jtk-endpoint {\n width: initial;\n height: initial;\n }\n\n .jtk-endpoint {\n z-index: 600;\n }\n\n .plumb-source {\n z-index: 600;\n border: 0px solid var(--color-connectors);\n }\n\n .plumb-source.connected {\n box-shadow: 0 3px 3px 0px rgba(0, 0, 0, 0.1);\n border-radius: 50%;\n }\n\n .plumb-source circle {\n fill: tomato;\n }\n\n .plumb-source.connected circle {\n fill: #fff;\n }\n\n .plumb-source svg {\n fill: var(--color-connectors) !important;\n stroke: var(--color-connectors);\n }\n\n .plumb-target {\n margin-top: -6px;\n z-index: 600;\n opacity: 0;\n cursor: pointer;\n }\n\n body .plumb-connector path {\n stroke: var(--color-connectors) !important;\n stroke-width: 3px;\n z-index: 10;\n }\n\n body .plumb-connector {\n z-index: 10;\n }\n\n body .plumb-connector.elevated {\n z-index: 550;\n }\n\n body .plumb-connector.elevated path {\n stroke: var(--color-connectors) !important;\n stroke-width: 3px;\n z-index: 550;\n }\n\n body .plumb-connector.elevated .plumb-arrow {\n fill: var(--color-connectors);\n stroke: var(--color-connectors);\n stroke-width: 0px;\n margin-top: 6px;\n z-index: 550;\n }\n\n body .plumb-connector .plumb-arrow {\n fill: var(--color-connectors);\n stroke: var(--color-connectors);\n stroke-width: 0px;\n margin-top: 6px;\n z-index: 10;\n }\n\n body svg.jtk-connector.jtk-hover path {\n stroke: var(--color-success) !important;\n stroke-width: 3px;\n }\n\n body .plumb-connector.jtk-hover .plumb-arrow {\n fill: var(--color-success) !important;\n stroke-width: 0px;\n z-index: 10;\n }\n `;\n }\n\n constructor() {\n super();\n }\n\n protected firstUpdated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(changes);\n this.plumber = new Plumber(this.querySelector('#canvas'));\n this.setupGlobalEventListeners();\n if (changes.has('flow')) {\n getStore().getState().fetchRevision(`/flow/revisions/${this.flow}`);\n }\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('canvasSize')) {\n // console.log('Setting canvas size', this.canvasSize);\n }\n\n if (changes.has('definition')) {\n this.updateCanvasSize();\n }\n\n if (changes.has('dirtyDate')) {\n if (this.dirtyDate) {\n this.debouncedSave();\n }\n }\n }\n\n private debouncedSave(): void {\n // Clear any existing timer\n if (this.saveTimer !== null) {\n clearTimeout(this.saveTimer);\n }\n\n this.saveTimer = window.setTimeout(() => {\n const now = new Date();\n const timeSinceLastChange = now.getTime() - this.dirtyDate.getTime();\n\n if (timeSinceLastChange >= SAVE_QUIET_TIME) {\n this.saveChanges();\n this.saveTimer = null;\n } else {\n this.debouncedSave();\n }\n }, SAVE_QUIET_TIME);\n }\n\n private saveChanges(): void {\n // post the flow definition to the server\n getStore().postJSON(`/flow/revisions/${this.flow}`, this.definition);\n getStore().getState().setDirtyDate(null);\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this.saveTimer !== null) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n document.removeEventListener('mousemove', this.boundMouseMove);\n document.removeEventListener('mouseup', this.boundMouseUp);\n }\n\n private setupGlobalEventListeners(): void {\n document.addEventListener('mousemove', this.boundMouseMove);\n document.addEventListener('mouseup', this.boundMouseUp);\n }\n\n private getPosition(uuid: string, type: 'node' | 'sticky'): FlowPosition {\n if (type === 'node') {\n return this.definition._ui.nodes[uuid]?.position;\n } else {\n return this.definition._ui.stickies?.[uuid]?.position;\n }\n }\n\n private updatePosition(\n uuid: string,\n type: 'node' | 'sticky',\n position: FlowPosition\n ): void {\n if (type === 'node') {\n getStore().getState().updateNodePosition(uuid, position);\n } else {\n const currentSticky = this.definition._ui.stickies?.[uuid];\n if (currentSticky) {\n getStore()\n .getState()\n .updateStickyNote(uuid, {\n ...currentSticky,\n position\n });\n }\n }\n }\n\n private handleMouseDown(event: MouseEvent): void {\n const element = event.currentTarget as HTMLElement;\n // Only start dragging if clicking on the element itself, not on exits or other interactive elements\n const target = event.target as HTMLElement;\n if (target.classList.contains('exit') || target.closest('.exit')) {\n return;\n }\n\n const uuid = element.getAttribute('uuid');\n const type = element.tagName === 'TEMBA-FLOW-NODE' ? 'node' : 'sticky';\n\n const position = this.getPosition(uuid, type);\n if (!position) return;\n\n // Set up potential drag state, but don't start dragging yet\n this.isMouseDown = true;\n this.dragStartPos = { x: event.clientX, y: event.clientY };\n this.startPos = { left: position.left, top: position.top };\n this.currentDragItem = {\n uuid,\n position,\n element,\n type\n };\n\n event.preventDefault();\n event.stopPropagation();\n }\n\n private handleMouseMove(event: MouseEvent): void {\n if (!this.isMouseDown || !this.currentDragItem) return;\n\n const deltaX = event.clientX - this.dragStartPos.x;\n const deltaY = event.clientY - this.dragStartPos.y;\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\n // Only start dragging if we've moved beyond the threshold\n if (!this.isDragging && distance > DRAG_THRESHOLD) {\n this.isDragging = true;\n\n // If this is a node, elevate connections\n if (this.currentDragItem.type === 'node' && this.plumber) {\n this.plumber.elevateNodeConnections(this.currentDragItem.uuid);\n }\n }\n\n // If we're actually dragging, update positions\n if (this.isDragging) {\n const newLeft = this.startPos.left + deltaX;\n const newTop = this.startPos.top + deltaY;\n\n // Update the visual position during drag\n this.currentDragItem.element.style.left = `${newLeft}px`;\n this.currentDragItem.element.style.top = `${newTop}px`;\n\n // Repaint connections if this is a node\n if (this.currentDragItem.type === 'node' && this.plumber) {\n this.plumber.repaintEverything();\n }\n }\n }\n\n private handleMouseUp(event: MouseEvent): void {\n if (!this.isMouseDown || !this.currentDragItem) return;\n\n // If we were actually dragging, handle the drag end\n if (this.isDragging) {\n // Restore normal z-index for node connections\n if (this.currentDragItem.type === 'node' && this.plumber) {\n this.plumber.restoreNodeConnections(this.currentDragItem.uuid);\n }\n\n const deltaX = event.clientX - this.dragStartPos.x;\n const deltaY = event.clientY - this.dragStartPos.y;\n\n const newLeft = this.startPos.left + deltaX;\n const newTop = this.startPos.top + deltaY;\n\n // Snap to 20px grid for final position\n const snappedLeft = snapToGrid(newLeft);\n const snappedTop = snapToGrid(newTop);\n\n const newPosition = { left: snappedLeft, top: snappedTop };\n\n // Update the store with the new snapped position\n this.updatePosition(\n this.currentDragItem.uuid,\n this.currentDragItem.type,\n newPosition\n );\n\n // Update canvas positions for nodes\n if (this.currentDragItem.type === 'node') {\n getStore()\n .getState()\n .updateCanvasPositions({\n [this.currentDragItem.uuid]: newPosition\n });\n }\n\n // Repaint connections if this is a node\n if (this.currentDragItem.type === 'node' && this.plumber) {\n this.plumber.repaintEverything();\n }\n }\n\n // Reset all drag state\n this.isDragging = false;\n this.isMouseDown = false;\n this.currentDragItem = null;\n }\n\n private updateCanvasSize(): void {\n if (!this.definition) return;\n\n const store = getStore();\n if (!store) return;\n\n // Calculate required canvas size based on all elements\n let maxWidth = 0;\n let maxHeight = 0;\n\n // Check node positions\n this.definition.nodes.forEach((node) => {\n const ui = this.definition._ui.nodes[node.uuid];\n if (ui && ui.position) {\n const nodeElement = this.querySelector(`[id=\"${node.uuid}\"]`);\n if (nodeElement) {\n const rect = nodeElement.getBoundingClientRect();\n maxWidth = Math.max(maxWidth, ui.position.left + rect.width);\n maxHeight = Math.max(maxHeight, ui.position.top + rect.height);\n }\n }\n });\n\n // Check sticky note positions\n const stickies = this.definition._ui?.stickies || {};\n Object.values(stickies).forEach((sticky) => {\n if (sticky.position) {\n maxWidth = Math.max(maxWidth, sticky.position.left + 200); // Sticky note width\n maxHeight = Math.max(maxHeight, sticky.position.top + 100); // Sticky note height\n }\n });\n\n // Update canvas size in store\n store.getState().expandCanvas(maxWidth, maxHeight);\n }\n\n public render(): TemplateResult {\n // we have to embed our own style since we are in light DOM\n const style = html`<style>\n ${unsafeCSS(Editor.styles.cssText)}\n ${unsafeCSS(EditorNode.styles.cssText)}\n </style>`;\n\n const stickies = this.definition?._ui?.stickies || {};\n\n return html`${style}\n <div id=\"editor\">\n <div\n id=\"grid\"\n style=\"min-width:100%;width:${this.canvasSize.width}px; height:${this\n .canvasSize.height}px\"\n >\n <div id=\"canvas\">\n ${this.definition\n ? this.definition.nodes.map((node) => {\n const position =\n this.definition._ui.nodes[node.uuid].position;\n\n const dragging =\n this.isDragging && this.currentDragItem?.uuid === node.uuid;\n\n return html`<temba-flow-node\n class=\"draggable ${dragging ? 'dragging' : ''}\"\n @mousedown=${this.handleMouseDown.bind(this)}\n uuid=${node.uuid}\n style=\"left:${position.left}px; top:${position.top}px\"\n .plumber=${this.plumber}\n .node=${node}\n .ui=${this.definition._ui.nodes[node.uuid]}\n ></temba-flow-node>`;\n })\n : html`<temba-loading></temba-loading>`}\n ${Object.entries(stickies).map(([uuid, sticky]) => {\n const position = sticky.position || { left: 0, top: 0 };\n const dragging =\n this.isDragging && this.currentDragItem?.uuid === uuid;\n return html`<temba-sticky-note\n class=\"draggable ${dragging ? 'dragging' : ''}\"\n @mousedown=${this.handleMouseDown.bind(this)}\n style=\"left:${position.left}px; top:${position.top}px; z-index: ${1000 +\n position.top}\"\n uuid=${uuid}\n .data=${sticky}\n .dragging=${dragging}\n ></temba-sticky-note>`;\n })}\n </div>\n </div>\n </div>`;\n }\n}\n"]}
|