@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
|
@@ -6,29 +6,13 @@ import { RapidElement } from '../RapidElement';
|
|
|
6
6
|
import { getClasses } from '../utils';
|
|
7
7
|
import { getStore } from '../store/Store';
|
|
8
8
|
export class EditorNode extends RapidElement {
|
|
9
|
-
constructor() {
|
|
10
|
-
super(...arguments);
|
|
11
|
-
// Drag state properties
|
|
12
|
-
this.isDragging = false;
|
|
13
|
-
this.dragStartPos = { x: 0, y: 0 };
|
|
14
|
-
this.nodeStartPos = { left: 0, top: 0 };
|
|
15
|
-
// Bound event handlers to maintain proper 'this' context
|
|
16
|
-
this.boundMouseMove = this.handleMouseMove.bind(this);
|
|
17
|
-
this.boundMouseUp = this.handleMouseUp.bind(this);
|
|
18
|
-
}
|
|
19
9
|
createRenderRoot() {
|
|
20
10
|
return this;
|
|
21
11
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Snaps a coordinate value to the nearest 20px grid position
|
|
24
|
-
*/
|
|
25
|
-
snapToGrid(value) {
|
|
26
|
-
return Math.round(value / 20) * 20;
|
|
27
|
-
}
|
|
28
12
|
static get styles() {
|
|
29
13
|
return css `
|
|
14
|
+
|
|
30
15
|
.node {
|
|
31
|
-
position: absolute;
|
|
32
16
|
background-color: #fff;
|
|
33
17
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
|
|
34
18
|
min-width: 200px;
|
|
@@ -37,7 +21,7 @@ export class EditorNode extends RapidElement {
|
|
|
37
21
|
color: #333;
|
|
38
22
|
cursor: move;
|
|
39
23
|
user-select: none;
|
|
40
|
-
|
|
24
|
+
|
|
41
25
|
}
|
|
42
26
|
|
|
43
27
|
.node:hover {
|
|
@@ -52,21 +36,59 @@ export class EditorNode extends RapidElement {
|
|
|
52
36
|
|
|
53
37
|
.action {
|
|
54
38
|
max-width: 200px;
|
|
39
|
+
position: relative;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.action.sortable {
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: stretch;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.action .action-content {
|
|
48
|
+
flex-grow: 1;
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: column;
|
|
55
51
|
}
|
|
56
52
|
|
|
57
53
|
.action .body {
|
|
58
54
|
padding: 1em;
|
|
59
55
|
}
|
|
60
56
|
|
|
57
|
+
.action .drag-handle {
|
|
58
|
+
opacity: 0;
|
|
59
|
+
transition: all 200ms ease-in-out;
|
|
60
|
+
cursor: move;
|
|
61
|
+
background: rgba(0, 0, 0, 0.02);
|
|
62
|
+
max-width:0px;
|
|
63
|
+
position: absolute;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.action:hover .drag-handle {
|
|
67
|
+
opacity: 0.5;
|
|
68
|
+
padding: 0.25em;
|
|
69
|
+
max-width: 20px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.action .drag-handle:hover {
|
|
73
|
+
opacity: 1;
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
61
77
|
.action .title,
|
|
62
78
|
.router .title {
|
|
79
|
+
display: flex;
|
|
63
80
|
color: #fff;
|
|
64
81
|
padding: 5px 1px;
|
|
65
82
|
text-align: center;
|
|
66
83
|
font-size: 1em;
|
|
67
84
|
font-weight: normal;
|
|
85
|
+
|
|
68
86
|
}
|
|
69
87
|
|
|
88
|
+
.title .name {
|
|
89
|
+
flex-grow: 1;
|
|
90
|
+
}
|
|
91
|
+
|
|
70
92
|
.quick-replies {
|
|
71
93
|
margin-top: 0.5em;
|
|
72
94
|
}
|
|
@@ -120,144 +142,74 @@ export class EditorNode extends RapidElement {
|
|
|
120
142
|
}
|
|
121
143
|
}`;
|
|
122
144
|
}
|
|
145
|
+
constructor() {
|
|
146
|
+
super();
|
|
147
|
+
this.handleActionOrderChanged = this.handleActionOrderChanged.bind(this);
|
|
148
|
+
}
|
|
123
149
|
updated(changes) {
|
|
150
|
+
var _a;
|
|
124
151
|
super.updated(changes);
|
|
125
152
|
if (changes.has('node')) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
153
|
+
// make our initial connections
|
|
154
|
+
if (changes.get('node') === undefined) {
|
|
155
|
+
// this.plumber.makeTarget(this.node.uuid);
|
|
156
|
+
for (const exit of this.node.exits) {
|
|
157
|
+
if (!exit.destination_uuid) {
|
|
158
|
+
this.plumber.makeSource(exit.uuid);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
this.plumber.connectIds(exit.uuid, exit.destination_uuid);
|
|
162
|
+
}
|
|
134
163
|
}
|
|
135
164
|
}
|
|
136
|
-
const ele = this.
|
|
165
|
+
const ele = this.parentElement;
|
|
137
166
|
const rect = ele.getBoundingClientRect();
|
|
138
|
-
getStore()
|
|
139
|
-
.getState()
|
|
140
|
-
.expandCanvas(this.ui.position.left + rect.width, this.ui.position.top + rect.height);
|
|
141
|
-
// Add drag event listeners to the node
|
|
142
|
-
this.addDragEventListeners();
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
addDragEventListeners() {
|
|
146
|
-
const nodeElement = this.querySelector('.node');
|
|
147
|
-
if (!nodeElement)
|
|
148
|
-
return;
|
|
149
|
-
nodeElement.addEventListener('mousedown', this.handleMouseDown.bind(this));
|
|
150
|
-
document.addEventListener('mousemove', this.boundMouseMove);
|
|
151
|
-
document.addEventListener('mouseup', this.boundMouseUp);
|
|
152
|
-
}
|
|
153
|
-
handleMouseDown(event) {
|
|
154
|
-
// Only start dragging if clicking on the node itself, not on exits or other interactive elements
|
|
155
|
-
const target = event.target;
|
|
156
|
-
if (target.classList.contains('exit') || target.closest('.exit')) {
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
this.isDragging = true;
|
|
160
|
-
this.dragStartPos = { x: event.clientX, y: event.clientY };
|
|
161
|
-
this.nodeStartPos = {
|
|
162
|
-
left: this.ui.position.left,
|
|
163
|
-
top: this.ui.position.top
|
|
164
|
-
};
|
|
165
|
-
// Add dragging class for visual feedback
|
|
166
|
-
const nodeElement = this.querySelector('.node');
|
|
167
|
-
if (nodeElement) {
|
|
168
|
-
nodeElement.classList.add('dragging');
|
|
169
|
-
}
|
|
170
|
-
// Elevate connections for this node during dragging
|
|
171
|
-
if (this.plumber) {
|
|
172
|
-
this.plumber.elevateNodeConnections(this.node.uuid);
|
|
167
|
+
(_a = getStore()) === null || _a === void 0 ? void 0 : _a.getState().expandCanvas(this.ui.position.left + rect.width, this.ui.position.top + rect.height);
|
|
173
168
|
}
|
|
174
|
-
event.preventDefault();
|
|
175
|
-
event.stopPropagation();
|
|
176
169
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
// Repaint connections during dragging for smooth updates
|
|
191
|
-
if (this.plumber) {
|
|
192
|
-
this.plumber.repaintEverything();
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
handleMouseUp(event) {
|
|
196
|
-
if (!this.isDragging)
|
|
197
|
-
return;
|
|
198
|
-
this.isDragging = false;
|
|
199
|
-
// Remove dragging class
|
|
200
|
-
const nodeElement = this.querySelector('.node');
|
|
201
|
-
if (nodeElement) {
|
|
202
|
-
nodeElement.classList.remove('dragging');
|
|
203
|
-
}
|
|
204
|
-
// Restore normal z-index for connections
|
|
205
|
-
if (this.plumber) {
|
|
206
|
-
this.plumber.restoreNodeConnections(this.node.uuid);
|
|
207
|
-
}
|
|
208
|
-
const deltaX = event.clientX - this.dragStartPos.x;
|
|
209
|
-
const deltaY = event.clientY - this.dragStartPos.y;
|
|
210
|
-
const newLeft = this.nodeStartPos.left + deltaX;
|
|
211
|
-
const newTop = this.nodeStartPos.top + deltaY;
|
|
212
|
-
// Snap to 20px grid for final position
|
|
213
|
-
const snappedLeft = this.snapToGrid(newLeft);
|
|
214
|
-
const snappedTop = this.snapToGrid(newTop);
|
|
215
|
-
// Update the store with the new snapped position
|
|
216
|
-
const newPosition = { left: snappedLeft, top: snappedTop };
|
|
217
|
-
getStore()
|
|
218
|
-
.getState()
|
|
219
|
-
.updateCanvasPositions({
|
|
220
|
-
[this.node.uuid]: newPosition
|
|
221
|
-
});
|
|
222
|
-
// Repaint connections if plumber is available
|
|
223
|
-
if (this.plumber) {
|
|
224
|
-
this.plumber.repaintEverything();
|
|
225
|
-
}
|
|
226
|
-
getStore().getState().updateNodePosition(this.node.uuid, newPosition);
|
|
227
|
-
// Fire a custom event with the new coordinates
|
|
228
|
-
/*this.fireCustomEvent(CustomEventType.Moved, {
|
|
229
|
-
nodeId: this.node.uuid,
|
|
230
|
-
position: newPosition,
|
|
231
|
-
oldPosition: {
|
|
232
|
-
left: this.nodeStartPos.left,
|
|
233
|
-
top: this.nodeStartPos.top
|
|
234
|
-
}
|
|
235
|
-
});*/
|
|
236
|
-
}
|
|
237
|
-
disconnectedCallback() {
|
|
238
|
-
super.disconnectedCallback();
|
|
239
|
-
// Clean up event listeners
|
|
240
|
-
document.removeEventListener('mousemove', this.boundMouseMove);
|
|
241
|
-
document.removeEventListener('mouseup', this.boundMouseUp);
|
|
170
|
+
handleActionOrderChanged(event) {
|
|
171
|
+
var _a;
|
|
172
|
+
const [fromIdx, toIdx] = event.detail.swap;
|
|
173
|
+
// swap our actions
|
|
174
|
+
const newActions = [...this.node.actions];
|
|
175
|
+
const movedAction = newActions.splice(fromIdx, 1)[0];
|
|
176
|
+
newActions.splice(toIdx, 0, movedAction);
|
|
177
|
+
// udate our internal reprensentation, this isn't strictly necessary
|
|
178
|
+
// since the editor will update us from it's definition subscription
|
|
179
|
+
// but it makes testing a lot easier
|
|
180
|
+
this.node = { ...this.node, actions: newActions };
|
|
181
|
+
(_a = getStore()) === null || _a === void 0 ? void 0 : _a.getState().updateNode(this.node.uuid, { ...this.node, actions: newActions });
|
|
242
182
|
}
|
|
243
183
|
renderTitle(config) {
|
|
184
|
+
var _a, _b;
|
|
244
185
|
return html `<div class="title" style="background:${config.color}">
|
|
245
|
-
${
|
|
186
|
+
${((_b = (_a = this.node) === null || _a === void 0 ? void 0 : _a.actions) === null || _b === void 0 ? void 0 : _b.length) > 1
|
|
187
|
+
? html `<temba-icon class="drag-handle" name="sort"></temba-icon>`
|
|
188
|
+
: null}
|
|
189
|
+
|
|
190
|
+
<div class="name">${config.name}</div>
|
|
246
191
|
</div>`;
|
|
247
192
|
}
|
|
248
|
-
renderAction(node, action) {
|
|
193
|
+
renderAction(node, action, index) {
|
|
249
194
|
const config = EDITOR_CONFIG[action.type];
|
|
250
195
|
if (config) {
|
|
251
|
-
return html `<div
|
|
252
|
-
${
|
|
253
|
-
|
|
254
|
-
|
|
196
|
+
return html `<div
|
|
197
|
+
class="action sortable ${action.type}"
|
|
198
|
+
id="action-${index}"
|
|
199
|
+
>
|
|
200
|
+
<div class="action-content">
|
|
201
|
+
${this.renderTitle(config)}
|
|
202
|
+
<div class="body">
|
|
203
|
+
${config.render
|
|
255
204
|
? config.render(node, action)
|
|
256
205
|
: html `<pre>${action.type}</pre>`}
|
|
206
|
+
</div>
|
|
257
207
|
</div>
|
|
258
208
|
</div>`;
|
|
259
209
|
}
|
|
260
|
-
return html `<div
|
|
210
|
+
return html `<div class="action sortable" id="action-${index}">
|
|
211
|
+
${action.type}
|
|
212
|
+
</div>`;
|
|
261
213
|
}
|
|
262
214
|
renderRouter(router, ui) {
|
|
263
215
|
const config = EDITOR_CONFIG[ui.type];
|
|
@@ -305,9 +257,16 @@ export class EditorNode extends RapidElement {
|
|
|
305
257
|
class="node"
|
|
306
258
|
style="left:${this.ui.position.left}px;top:${this.ui.position.top}px"
|
|
307
259
|
>
|
|
308
|
-
${this.node.actions.
|
|
309
|
-
|
|
310
|
-
|
|
260
|
+
${this.node.actions.length > 0
|
|
261
|
+
? html `<temba-sortable-list
|
|
262
|
+
dragHandle="drag-handle"
|
|
263
|
+
@temba-order-changed="${this.handleActionOrderChanged}"
|
|
264
|
+
>
|
|
265
|
+
${this.node.actions.map((actionSpec, index) => {
|
|
266
|
+
return this.renderAction(this.node, actionSpec, index);
|
|
267
|
+
})}
|
|
268
|
+
</temba-sortable-list>`
|
|
269
|
+
: ''}
|
|
311
270
|
${this.node.router
|
|
312
271
|
? html ` ${this.renderRouter(this.node.router, this.ui)}
|
|
313
272
|
${this.renderCategories(this.node)}`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorNode.js","sourceRoot":"","sources":["../../../src/flow/EditorNode.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { EDITOR_CONFIG, UIConfig } from './config';\nimport { Action, Exit, Node, NodeUI, Router } from '../store/flow-definition';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { getClasses } from '../utils';\nimport { Plumber } from './Plumber';\nimport { getStore } from '../store/Store';\n\nexport class EditorNode extends RapidElement {\n createRenderRoot() {\n return this;\n }\n\n @property({ type: Object })\n private plumber: Plumber;\n\n @property({ type: Object })\n private node: Node;\n\n @property({ type: Object })\n private ui: NodeUI;\n\n // Drag state properties\n private isDragging = false;\n private dragStartPos = { x: 0, y: 0 };\n private nodeStartPos = { 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 /**\n * Snaps a coordinate value to the nearest 20px grid position\n */\n private snapToGrid(value: number): number {\n return Math.round(value / 20) * 20;\n }\n\n static get styles() {\n return css`\n .node {\n position: absolute;\n background-color: #fff;\n box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);\n min-width: 200px;\n border-radius: calc(var(--curvature) * 1.5);\n overflow: hidden;\n color: #333;\n cursor: move;\n user-select: none;\n z-index: 500;\n }\n\n .node:hover {\n box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);\n }\n\n .node.dragging {\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);\n transform: scale(1.02);\n z-index: 1000;\n }\n \n .action {\n max-width: 200px;\n }\n\n .action .body {\n padding: 1em;\n }\n\n .action .title,\n .router .title {\n color: #fff;\n padding: 5px 1px;\n text-align: center;\n font-size: 1em;\n font-weight: normal;\n }\n\n .quick-replies {\n margin-top: 0.5em;\n }\n\n .quick-reply {\n background-color: #f0f0f0;\n border: 1px solid #e0e0e0;\n border-radius: calc(var(--curvature) * 1.5);\n padding: 0.2em 1em;\n display: inline-block;\n font-size: 0.8em;\n margin: 0.2em;\n }\n\n .categories {\n display: flex;\n flex-direction: row;\n\n }\n\n .category {\n margin:-1px -0.5px;\n border: 1px solid #f3f3f3;\n padding: 0.75em;\n flex-grow:1;\n text-align: center;\n }\n\n .action-exits {\n padding-bottom: 0.75em;\n margin-top: -0.75em;\n }\n\n .category .title {\n font-weight: normal;\n font-size: 1em;\n }\n\n .router .body {\n padding: 0.75em;\n }\n\n .result-name {\n font-weight: bold;\n display: inline-block;\n }\n \n .exit {\n padding-top: 10px;\n margin-bottom: -10px;\n }\n }`;\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('node')) {\n this.plumber.makeTarget(this.node.uuid);\n\n // our node was changed, see if we have new destinations\n for (const exit of this.node.exits) {\n if (!exit.destination_uuid) {\n this.plumber.makeSource(exit.uuid);\n } else {\n this.plumber.connectIds(exit.uuid, exit.destination_uuid);\n }\n }\n\n const ele = this.querySelector('.node');\n const rect = ele.getBoundingClientRect();\n\n getStore()\n .getState()\n .expandCanvas(\n this.ui.position.left + rect.width,\n this.ui.position.top + rect.height\n );\n\n // Add drag event listeners to the node\n this.addDragEventListeners();\n }\n }\n\n private addDragEventListeners(): void {\n const nodeElement = this.querySelector('.node') as HTMLElement;\n if (!nodeElement) return;\n\n nodeElement.addEventListener('mousedown', this.handleMouseDown.bind(this));\n document.addEventListener('mousemove', this.boundMouseMove);\n document.addEventListener('mouseup', this.boundMouseUp);\n }\n\n private handleMouseDown(event: MouseEvent): void {\n // Only start dragging if clicking on the node 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 this.isDragging = true;\n this.dragStartPos = { x: event.clientX, y: event.clientY };\n this.nodeStartPos = {\n left: this.ui.position.left,\n top: this.ui.position.top\n };\n\n // Add dragging class for visual feedback\n const nodeElement = this.querySelector('.node') as HTMLElement;\n if (nodeElement) {\n nodeElement.classList.add('dragging');\n }\n\n // Elevate connections for this node during dragging\n if (this.plumber) {\n this.plumber.elevateNodeConnections(this.node.uuid);\n }\n\n event.preventDefault();\n event.stopPropagation();\n }\n\n private handleMouseMove(event: MouseEvent): void {\n if (!this.isDragging) return;\n\n const deltaX = event.clientX - this.dragStartPos.x;\n const deltaY = event.clientY - this.dragStartPos.y;\n\n const newLeft = this.nodeStartPos.left + deltaX;\n const newTop = this.nodeStartPos.top + deltaY;\n\n // Update the UI position temporarily (for visual feedback)\n const nodeElement = this.querySelector('.node') as HTMLElement;\n if (nodeElement) {\n nodeElement.style.left = `${newLeft}px`;\n nodeElement.style.top = `${newTop}px`;\n }\n\n // Repaint connections during dragging for smooth updates\n if (this.plumber) {\n this.plumber.repaintEverything();\n }\n }\n\n private handleMouseUp(event: MouseEvent): void {\n if (!this.isDragging) return;\n\n this.isDragging = false;\n\n // Remove dragging class\n const nodeElement = this.querySelector('.node') as HTMLElement;\n if (nodeElement) {\n nodeElement.classList.remove('dragging');\n }\n\n // Restore normal z-index for connections\n if (this.plumber) {\n this.plumber.restoreNodeConnections(this.node.uuid);\n }\n\n const deltaX = event.clientX - this.dragStartPos.x;\n const deltaY = event.clientY - this.dragStartPos.y;\n\n const newLeft = this.nodeStartPos.left + deltaX;\n const newTop = this.nodeStartPos.top + deltaY;\n\n // Snap to 20px grid for final position\n const snappedLeft = this.snapToGrid(newLeft);\n const snappedTop = this.snapToGrid(newTop);\n\n // Update the store with the new snapped position\n const newPosition = { left: snappedLeft, top: snappedTop };\n getStore()\n .getState()\n .updateCanvasPositions({\n [this.node.uuid]: newPosition\n });\n\n // Repaint connections if plumber is available\n if (this.plumber) {\n this.plumber.repaintEverything();\n }\n\n getStore().getState().updateNodePosition(this.node.uuid, newPosition);\n\n // Fire a custom event with the new coordinates\n /*this.fireCustomEvent(CustomEventType.Moved, {\n nodeId: this.node.uuid,\n position: newPosition,\n oldPosition: {\n left: this.nodeStartPos.left,\n top: this.nodeStartPos.top\n }\n });*/\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n // Clean up event listeners\n document.removeEventListener('mousemove', this.boundMouseMove);\n document.removeEventListener('mouseup', this.boundMouseUp);\n }\n\n private renderTitle(config: UIConfig) {\n return html`<div class=\"title\" style=\"background:${config.color}\">\n ${config.name}\n </div>`;\n }\n\n private renderAction(node: Node, action: Action) {\n const config = EDITOR_CONFIG[action.type];\n\n if (config) {\n return html`<div class=\"action ${action.type}\">\n ${this.renderTitle(config)}\n <div class=\"body\">\n ${config.render\n ? config.render(node, action)\n : html`<pre>${action.type}</pre>`}\n </div>\n </div>`;\n }\n return html`<div>${action.type}</div>`;\n }\n\n private renderRouter(router: Router, ui: NodeUI) {\n const config = EDITOR_CONFIG[ui.type];\n if (config) {\n return html`<div class=\"router\">\n ${this.renderTitle(config)}\n ${router.result_name\n ? html`<div class=\"body\">\n Save as\n <div class=\"result-name\">${router.result_name}</div>\n </div>`\n : null}\n </div>`;\n }\n }\n\n private renderCategories(node: Node) {\n if (!node.router || !node.router.categories) {\n return null;\n }\n const categories = node.router.categories.map((category) => {\n const exit = node.exits.find(\n (exit: Exit) => exit.uuid == category.exit_uuid\n );\n\n return html`<div class=\"category\">\n <div class=\"title\">${category.name}</div>\n ${this.renderExit(exit)}\n </div>`;\n });\n\n return html`<div class=\"categories\">${categories}</div>`;\n }\n\n private renderExit(exit: Exit): TemplateResult {\n return html`<div\n id=\"${exit.uuid}\"\n class=${getClasses({\n exit: true,\n connected: !!exit.destination_uuid\n })}\n ></div>`;\n }\n\n public render() {\n if (!this.node || !this.ui) {\n return html`<div class=\"node\">Loading...</div>`;\n }\n\n return html`\n <div\n id=\"${this.node.uuid}\"\n class=\"node\"\n style=\"left:${this.ui.position.left}px;top:${this.ui.position.top}px\"\n >\n ${this.node.actions.map((actionSpec) => {\n return this.renderAction(this.node, actionSpec);\n })}\n ${this.node.router\n ? html` ${this.renderRouter(this.node.router, this.ui)}\n ${this.renderCategories(this.node)}`\n : html`<div class=\"action-exits\">\n ${this.node.exits.map((exit) => {\n return this.renderExit(exit);\n })}\n </div>`}\n </div>\n `;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"EditorNode.js","sourceRoot":"","sources":["../../../src/flow/EditorNode.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { EDITOR_CONFIG, UIConfig } from './config';\nimport { Action, Exit, Node, NodeUI, Router } from '../store/flow-definition';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { getClasses } from '../utils';\nimport { Plumber } from './Plumber';\nimport { getStore } from '../store/Store';\n\nexport class EditorNode extends RapidElement {\n createRenderRoot() {\n return this;\n }\n\n @property({ type: Object })\n private plumber: Plumber;\n\n @property({ type: Object })\n private node: Node;\n\n @property({ type: Object })\n private ui: NodeUI;\n\n static get styles() {\n return css`\n\n .node {\n background-color: #fff;\n box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);\n min-width: 200px;\n border-radius: calc(var(--curvature) * 1.5);\n overflow: hidden;\n color: #333;\n cursor: move;\n user-select: none;\n\n }\n\n .node:hover {\n box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);\n }\n\n .node.dragging {\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);\n transform: scale(1.02);\n z-index: 1000;\n }\n \n .action {\n max-width: 200px;\n position: relative;\n }\n\n .action.sortable {\n display: flex;\n align-items: stretch;\n }\n\n .action .action-content {\n flex-grow: 1;\n display: flex;\n flex-direction: column;\n }\n\n .action .body {\n padding: 1em;\n }\n\n .action .drag-handle {\n opacity: 0;\n transition: all 200ms ease-in-out;\n cursor: move;\n background: rgba(0, 0, 0, 0.02);\n max-width:0px;\n position: absolute;\n }\n\n .action:hover .drag-handle {\n opacity: 0.5;\n padding: 0.25em;\n max-width: 20px;\n }\n\n .action .drag-handle:hover {\n opacity: 1;\n \n }\n\n .action .title,\n .router .title {\n display: flex;\n color: #fff;\n padding: 5px 1px;\n text-align: center;\n font-size: 1em;\n font-weight: normal;\n\n }\n\n .title .name {\n flex-grow: 1;\n }\n\n .quick-replies {\n margin-top: 0.5em;\n }\n\n .quick-reply {\n background-color: #f0f0f0;\n border: 1px solid #e0e0e0;\n border-radius: calc(var(--curvature) * 1.5);\n padding: 0.2em 1em;\n display: inline-block;\n font-size: 0.8em;\n margin: 0.2em;\n }\n\n .categories {\n display: flex;\n flex-direction: row;\n\n }\n\n .category {\n margin:-1px -0.5px;\n border: 1px solid #f3f3f3;\n padding: 0.75em;\n flex-grow:1;\n text-align: center;\n }\n\n .action-exits {\n padding-bottom: 0.75em;\n margin-top: -0.75em;\n }\n\n .category .title {\n font-weight: normal;\n font-size: 1em;\n }\n\n .router .body {\n padding: 0.75em;\n }\n\n .result-name {\n font-weight: bold;\n display: inline-block;\n }\n \n .exit {\n padding-top: 10px;\n margin-bottom: -10px;\n }\n }`;\n }\n\n constructor() {\n super();\n this.handleActionOrderChanged = this.handleActionOrderChanged.bind(this);\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('node')) {\n // make our initial connections\n if (changes.get('node') === undefined) {\n // this.plumber.makeTarget(this.node.uuid);\n for (const exit of this.node.exits) {\n if (!exit.destination_uuid) {\n this.plumber.makeSource(exit.uuid);\n } else {\n this.plumber.connectIds(exit.uuid, exit.destination_uuid);\n }\n }\n }\n\n const ele = this.parentElement;\n const rect = ele.getBoundingClientRect();\n\n getStore()\n ?.getState()\n .expandCanvas(\n this.ui.position.left + rect.width,\n this.ui.position.top + rect.height\n );\n }\n }\n\n private handleActionOrderChanged(event: CustomEvent) {\n const [fromIdx, toIdx] = event.detail.swap;\n\n // swap our actions\n const newActions = [...this.node.actions];\n const movedAction = newActions.splice(fromIdx, 1)[0];\n newActions.splice(toIdx, 0, movedAction);\n\n // udate our internal reprensentation, this isn't strictly necessary\n // since the editor will update us from it's definition subscription\n // but it makes testing a lot easier\n this.node = { ...this.node, actions: newActions };\n\n getStore()\n ?.getState()\n .updateNode(this.node.uuid, { ...this.node, actions: newActions });\n }\n\n private renderTitle(config: UIConfig) {\n return html`<div class=\"title\" style=\"background:${config.color}\">\n ${this.node?.actions?.length > 1\n ? html`<temba-icon class=\"drag-handle\" name=\"sort\"></temba-icon>`\n : null}\n\n <div class=\"name\">${config.name}</div>\n </div>`;\n }\n\n private renderAction(node: Node, action: Action, index: number) {\n const config = EDITOR_CONFIG[action.type];\n\n if (config) {\n return html`<div\n class=\"action sortable ${action.type}\"\n id=\"action-${index}\"\n >\n <div class=\"action-content\">\n ${this.renderTitle(config)}\n <div class=\"body\">\n ${config.render\n ? config.render(node, action)\n : html`<pre>${action.type}</pre>`}\n </div>\n </div>\n </div>`;\n }\n return html`<div class=\"action sortable\" id=\"action-${index}\">\n ${action.type}\n </div>`;\n }\n\n private renderRouter(router: Router, ui: NodeUI) {\n const config = EDITOR_CONFIG[ui.type];\n if (config) {\n return html`<div class=\"router\">\n ${this.renderTitle(config)}\n ${router.result_name\n ? html`<div class=\"body\">\n Save as\n <div class=\"result-name\">${router.result_name}</div>\n </div>`\n : null}\n </div>`;\n }\n }\n\n private renderCategories(node: Node) {\n if (!node.router || !node.router.categories) {\n return null;\n }\n const categories = node.router.categories.map((category) => {\n const exit = node.exits.find(\n (exit: Exit) => exit.uuid == category.exit_uuid\n );\n\n return html`<div class=\"category\">\n <div class=\"title\">${category.name}</div>\n ${this.renderExit(exit)}\n </div>`;\n });\n\n return html`<div class=\"categories\">${categories}</div>`;\n }\n\n private renderExit(exit: Exit): TemplateResult {\n return html`<div\n id=\"${exit.uuid}\"\n class=${getClasses({\n exit: true,\n connected: !!exit.destination_uuid\n })}\n ></div>`;\n }\n\n public render() {\n if (!this.node || !this.ui) {\n return html`<div class=\"node\">Loading...</div>`;\n }\n\n return html`\n <div\n id=\"${this.node.uuid}\"\n class=\"node\"\n style=\"left:${this.ui.position.left}px;top:${this.ui.position.top}px\"\n >\n ${this.node.actions.length > 0\n ? html`<temba-sortable-list\n dragHandle=\"drag-handle\"\n @temba-order-changed=\"${this.handleActionOrderChanged}\"\n >\n ${this.node.actions.map((actionSpec, index) => {\n return this.renderAction(this.node, actionSpec, index);\n })}\n </temba-sortable-list>`\n : ''}\n ${this.node.router\n ? html` ${this.renderRouter(this.node.router, this.ui)}\n ${this.renderCategories(this.node)}`\n : html`<div class=\"action-exits\">\n ${this.node.exits.map((exit) => {\n return this.renderExit(exit);\n })}\n </div>`}\n </div>\n `;\n }\n}\n"]}
|
|
@@ -0,0 +1,272 @@
|
|
|
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 { getStore } from '../store/Store';
|
|
6
|
+
export class StickyNote extends RapidElement {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.dragging = false;
|
|
10
|
+
}
|
|
11
|
+
static get styles() {
|
|
12
|
+
return css `
|
|
13
|
+
:host {
|
|
14
|
+
--sticky-color: #fef08a;
|
|
15
|
+
--sticky-border-color: #facc15;
|
|
16
|
+
--sticky-text-color: #451a03;
|
|
17
|
+
--curvature: 8px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.sticky-note {
|
|
21
|
+
width: 200px;
|
|
22
|
+
background-color: var(--sticky-color);
|
|
23
|
+
border: 1px solid var(--sticky-border-color);
|
|
24
|
+
border-radius: var(--curvature);
|
|
25
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
26
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
|
27
|
+
sans-serif;
|
|
28
|
+
font-size: 12px;
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
transition: transform 0.1s ease, box-shadow 0.2s ease;
|
|
31
|
+
color: var(--sticky-text-color);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.sticky-note.dragging {
|
|
35
|
+
opacity: 0.5;
|
|
36
|
+
z-index: 1000;
|
|
37
|
+
transform: rotate(0deg);
|
|
38
|
+
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.sticky-note:hover {
|
|
42
|
+
transform: translateY(0px);
|
|
43
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Color themes */
|
|
47
|
+
.sticky-note.yellow {
|
|
48
|
+
--sticky-color: #fef08a;
|
|
49
|
+
--sticky-border-color: #facc15;
|
|
50
|
+
--sticky-text-color: #451a03;
|
|
51
|
+
}
|
|
52
|
+
.sticky-note.blue {
|
|
53
|
+
--sticky-color: #bfdbfe;
|
|
54
|
+
--sticky-border-color: #3b82f6;
|
|
55
|
+
--sticky-text-color: #1e3a8a;
|
|
56
|
+
}
|
|
57
|
+
.sticky-note.pink {
|
|
58
|
+
--sticky-color: #fce7f3;
|
|
59
|
+
--sticky-border-color: #ec4899;
|
|
60
|
+
--sticky-text-color: #831843;
|
|
61
|
+
}
|
|
62
|
+
.sticky-note.green {
|
|
63
|
+
--sticky-color: #d1fae5;
|
|
64
|
+
--sticky-border-color: #10b981;
|
|
65
|
+
--sticky-text-color: #064e3b;
|
|
66
|
+
}
|
|
67
|
+
.sticky-note.gray {
|
|
68
|
+
--sticky-color: #f3f4f6;
|
|
69
|
+
--sticky-border-color: #6b7280;
|
|
70
|
+
--sticky-text-color: #374151;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Title and body containers */
|
|
74
|
+
.sticky-title-container {
|
|
75
|
+
position: relative;
|
|
76
|
+
border-bottom: 1px solid var(--sticky-border-color);
|
|
77
|
+
background-color: rgba(255, 255, 255, 0.5);
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
}
|
|
81
|
+
.sticky-body-container {
|
|
82
|
+
position: relative;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Editable fields */
|
|
86
|
+
[contenteditable='true'] {
|
|
87
|
+
margin: 2px;
|
|
88
|
+
padding: 4px 8px;
|
|
89
|
+
outline: none;
|
|
90
|
+
border-radius: var(--curvature);
|
|
91
|
+
transition: background 0.2s;
|
|
92
|
+
}
|
|
93
|
+
[contenteditable='true']:focus {
|
|
94
|
+
background-color: rgba(255, 255, 255, 0.8);
|
|
95
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
96
|
+
outline-color: var(--sticky-border-color);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Title */
|
|
100
|
+
.sticky-title {
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
font-size: 13px;
|
|
103
|
+
color: var(--sticky-text-color);
|
|
104
|
+
min-height: 20px;
|
|
105
|
+
line-height: 20px;
|
|
106
|
+
border-top-left-radius: var(--curvature);
|
|
107
|
+
border-top-right-radius: var(--curvature);
|
|
108
|
+
flex-grow: 1;
|
|
109
|
+
padding-left: 8px;
|
|
110
|
+
}
|
|
111
|
+
.sticky-title:empty::before {
|
|
112
|
+
content: 'Click to add title';
|
|
113
|
+
opacity: 0.5;
|
|
114
|
+
font-style: italic;
|
|
115
|
+
}
|
|
116
|
+
.sticky-title:focus {
|
|
117
|
+
border-bottom-left-radius: 0px;
|
|
118
|
+
border-bottom-right-radius: 0px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Body */
|
|
122
|
+
.sticky-body {
|
|
123
|
+
padding: 8px 10px;
|
|
124
|
+
color: var(--sticky-text-color);
|
|
125
|
+
line-height: 1.4;
|
|
126
|
+
min-height: 48px;
|
|
127
|
+
word-wrap: break-word;
|
|
128
|
+
white-space: pre-wrap;
|
|
129
|
+
}
|
|
130
|
+
.sticky-body:empty::before {
|
|
131
|
+
content: 'Click to add note';
|
|
132
|
+
opacity: 0.5;
|
|
133
|
+
font-style: italic;
|
|
134
|
+
}
|
|
135
|
+
.sticky-body:focus {
|
|
136
|
+
border-top-left-radius: 0px;
|
|
137
|
+
border-top-right-radius: 0px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Drag icon */
|
|
141
|
+
.sticky-title-container > .drag-handle {
|
|
142
|
+
--icon-color: var(--sticky-border-color);
|
|
143
|
+
cursor: move;
|
|
144
|
+
max-width: 0px;
|
|
145
|
+
overflow: hidden;
|
|
146
|
+
transition: all 0.2s ease;
|
|
147
|
+
padding-left: 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.sticky-note:hover .drag-handle {
|
|
151
|
+
max-width: 20px;
|
|
152
|
+
padding-left: 8px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.sticky-note:focus-within .sticky-title-container > .drag-handle {
|
|
156
|
+
max-width: 0px;
|
|
157
|
+
padding-left: 0px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* Focus/active states */
|
|
161
|
+
.sticky-note:focus-within {
|
|
162
|
+
box-shadow: 0 0 0 1px var(--sticky-border-color),
|
|
163
|
+
0 10px 20px rgba(0, 0, 0, 0.3);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.sticky-note:focus-within .drag-handle {
|
|
167
|
+
max-width: 0px;
|
|
168
|
+
}
|
|
169
|
+
`;
|
|
170
|
+
}
|
|
171
|
+
updated(changes) {
|
|
172
|
+
super.updated(changes);
|
|
173
|
+
if (changes.has('data') || changes.has('uuid')) {
|
|
174
|
+
this.updateCanvasSize();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
updateCanvasSize() {
|
|
178
|
+
if (!this.data) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const element = this.querySelector('.sticky-note');
|
|
182
|
+
if (element) {
|
|
183
|
+
const rect = element.getBoundingClientRect();
|
|
184
|
+
getStore()
|
|
185
|
+
.getState()
|
|
186
|
+
.expandCanvas(this.data.position.left + rect.width, this.data.position.top + rect.height);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
handleTitleBlur(event) {
|
|
190
|
+
const target = event.target;
|
|
191
|
+
const newTitle = target.textContent || '';
|
|
192
|
+
if (this.data && newTitle !== this.data.title) {
|
|
193
|
+
getStore()
|
|
194
|
+
.getState()
|
|
195
|
+
.updateStickyNote(this.uuid, {
|
|
196
|
+
...this.data,
|
|
197
|
+
title: newTitle
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
this.requestUpdate();
|
|
201
|
+
}
|
|
202
|
+
handleBodyBlur(event) {
|
|
203
|
+
const target = event.target;
|
|
204
|
+
const newBody = target.textContent || '';
|
|
205
|
+
if (this.data && newBody !== this.data.body) {
|
|
206
|
+
getStore()
|
|
207
|
+
.getState()
|
|
208
|
+
.updateStickyNote(this.uuid, {
|
|
209
|
+
...this.data,
|
|
210
|
+
body: newBody
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
this.requestUpdate();
|
|
214
|
+
}
|
|
215
|
+
handleKeyDown(event) {
|
|
216
|
+
if (event.key === 'Enter' && !event.shiftKey) {
|
|
217
|
+
event.preventDefault();
|
|
218
|
+
event.target.blur();
|
|
219
|
+
}
|
|
220
|
+
if (event.key === 'Escape') {
|
|
221
|
+
event.target.blur();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
render() {
|
|
225
|
+
if (!this.data) {
|
|
226
|
+
return html `<div class="sticky-note" style="display: none;"></div>`;
|
|
227
|
+
}
|
|
228
|
+
const style = `left: ${this.data.position.left}px; top: ${this.data.position.top}px;`;
|
|
229
|
+
return html `
|
|
230
|
+
<div
|
|
231
|
+
class="sticky-note ${this.data.color} ${this.dragging
|
|
232
|
+
? 'dragging'
|
|
233
|
+
: ''}"
|
|
234
|
+
style="${style}"
|
|
235
|
+
data-uuid="${this.uuid}"
|
|
236
|
+
>
|
|
237
|
+
<div class="sticky-title-container">
|
|
238
|
+
<temba-icon name="drag" class="drag-handle"></temba-icon>
|
|
239
|
+
<div
|
|
240
|
+
class="sticky-title"
|
|
241
|
+
contenteditable="true"
|
|
242
|
+
@blur="${this.handleTitleBlur}"
|
|
243
|
+
@keydown="${this.handleKeyDown}"
|
|
244
|
+
@mousedown="${(e) => e.stopPropagation()}"
|
|
245
|
+
.textContent="${this.data.title}"
|
|
246
|
+
></div>
|
|
247
|
+
</div>
|
|
248
|
+
<div class="sticky-body-container">
|
|
249
|
+
<div
|
|
250
|
+
class="sticky-body"
|
|
251
|
+
contenteditable="true"
|
|
252
|
+
@blur="${this.handleBodyBlur}"
|
|
253
|
+
@keydown="${this.handleKeyDown}"
|
|
254
|
+
@mousedown="${(e) => e.stopPropagation()}"
|
|
255
|
+
.textContent="${this.data.body}"
|
|
256
|
+
></div>
|
|
257
|
+
<div class="edit-icon" title="Edit note"></div>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
`;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
__decorate([
|
|
264
|
+
property({ type: String })
|
|
265
|
+
], StickyNote.prototype, "uuid", void 0);
|
|
266
|
+
__decorate([
|
|
267
|
+
property({ type: Object })
|
|
268
|
+
], StickyNote.prototype, "data", void 0);
|
|
269
|
+
__decorate([
|
|
270
|
+
property({ type: Boolean })
|
|
271
|
+
], StickyNote.prototype, "dragging", void 0);
|
|
272
|
+
//# sourceMappingURL=StickyNote.js.map
|