@hellotext/hellotext 2.0.9 → 2.1.1

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/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export interface HellotextConfig {
17
17
  secondaryColor?: string
18
18
  typography?: string
19
19
  }
20
+ strategy?: 'absolute' | 'fixed'
20
21
  }
21
22
  session?: string
22
23
  autoGenerateSession?: boolean
@@ -5,8 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.usePopover = void 0;
7
7
  var _dom = require("@floating-ui/dom");
8
- var _hellotext = _interopRequireDefault(require("../../hellotext"));
9
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
8
+ var _core = require("../../core");
10
9
  const usePopover = controller => {
11
10
  Object.assign(controller, {
12
11
  show() {
@@ -25,14 +24,17 @@ const usePopover = controller => {
25
24
  this.floatingUICleanup = (0, _dom.autoUpdate)(trigger, popover, () => {
26
25
  (0, _dom.computePosition)(trigger, popover, {
27
26
  placement: this.placementValue,
28
- middleware: this.middlewares
27
+ middleware: this.middlewares,
28
+ strategy: _core.Configuration.webchat.strategy
29
29
  }).then(({
30
30
  x,
31
- y
31
+ y,
32
+ strategy
32
33
  }) => {
33
34
  const newStyle = {
34
35
  left: `${x}px`,
35
- top: `${y}px`
36
+ top: `${y}px`,
37
+ position: strategy
36
38
  };
37
39
  Object.assign(popover.style, newStyle);
38
40
  });
@@ -42,13 +44,13 @@ const usePopover = controller => {
42
44
  if (this.disabledValue) return;
43
45
  if (this.openValue) {
44
46
  this.popoverTarget.showPopover();
45
- this.popoverTarget.setAttribute("aria-expanded", "true");
47
+ this.popoverTarget.setAttribute('aria-expanded', 'true');
46
48
  if (this['onPopoverOpened']) {
47
49
  this.onPopoverOpened();
48
50
  }
49
51
  } else {
50
52
  this.popoverTarget.hidePopover();
51
- this.popoverTarget.removeAttribute("aria-expanded");
53
+ this.popoverTarget.removeAttribute('aria-expanded');
52
54
  if (this['onPopoverClosed']) {
53
55
  this.onPopoverClosed();
54
56
  }
@@ -1,5 +1,5 @@
1
1
  import { autoUpdate, computePosition } from '@floating-ui/dom';
2
- import Hellotext from "../../hellotext";
2
+ import { Configuration } from '../../core';
3
3
  export var usePopover = controller => {
4
4
  Object.assign(controller, {
5
5
  show() {
@@ -19,15 +19,18 @@ export var usePopover = controller => {
19
19
  this.floatingUICleanup = autoUpdate(trigger, popover, () => {
20
20
  computePosition(trigger, popover, {
21
21
  placement: this.placementValue,
22
- middleware: this.middlewares
22
+ middleware: this.middlewares,
23
+ strategy: Configuration.webchat.strategy
23
24
  }).then(_ref2 => {
24
25
  var {
25
26
  x,
26
- y
27
+ y,
28
+ strategy
27
29
  } = _ref2;
28
30
  var newStyle = {
29
31
  left: "".concat(x, "px"),
30
- top: "".concat(y, "px")
32
+ top: "".concat(y, "px"),
33
+ position: strategy
31
34
  };
32
35
  Object.assign(popover.style, newStyle);
33
36
  });
@@ -37,13 +40,13 @@ export var usePopover = controller => {
37
40
  if (this.disabledValue) return;
38
41
  if (this.openValue) {
39
42
  this.popoverTarget.showPopover();
40
- this.popoverTarget.setAttribute("aria-expanded", "true");
43
+ this.popoverTarget.setAttribute('aria-expanded', 'true');
41
44
  if (this['onPopoverOpened']) {
42
45
  this.onPopoverOpened();
43
46
  }
44
47
  } else {
45
48
  this.popoverTarget.hidePopover();
46
- this.popoverTarget.removeAttribute("aria-expanded");
49
+ this.popoverTarget.removeAttribute('aria-expanded');
47
50
  if (this['onPopoverClosed']) {
48
51
  this.onPopoverClosed();
49
52
  }
@@ -45,6 +45,8 @@ let _default = /*#__PURE__*/function (_Controller) {
45
45
  this.onAgentOnline = this.onAgentOnline.bind(this);
46
46
  this.onMessageReaction = this.onMessageReaction.bind(this);
47
47
  this.onScroll = this.onScroll.bind(this);
48
+ this.onOutboundMessageSent = this.onOutboundMessageSent.bind(this);
49
+ this.broadcastChannel = new BroadcastChannel(`hellotext--webchat--${this.idValue}`);
48
50
  _get(_getPrototypeOf(_default.prototype), "initialize", this).call(this);
49
51
  }
50
52
  }, {
@@ -69,15 +71,43 @@ let _default = /*#__PURE__*/function (_Controller) {
69
71
  this.openValue = true;
70
72
  }
71
73
  _hellotext.default.eventEmitter.dispatch('webchat:mounted');
74
+ this.broadcastChannel.addEventListener('message', this.onOutboundMessageSent);
72
75
  _get(_getPrototypeOf(_default.prototype), "connect", this).call(this);
73
76
  }
74
77
  }, {
75
78
  key: "disconnect",
76
79
  value: function disconnect() {
80
+ this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent);
77
81
  this.messagesContainerTarget.removeEventListener('scroll', this.onScroll);
82
+ this.broadcastChannel.close();
78
83
  this.floatingUICleanup();
79
84
  _get(_getPrototypeOf(_default.prototype), "disconnect", this).call(this);
80
85
  }
86
+ }, {
87
+ key: "onOutboundMessageSent",
88
+ value: function onOutboundMessageSent(event) {
89
+ const {
90
+ data
91
+ } = event;
92
+ const callbacks = {
93
+ 'message:sent': data => {
94
+ const element = new DOMParser().parseFromString(data.element, 'text/html').body.firstElementChild;
95
+ this.messagesContainerTarget.appendChild(element);
96
+ element.scrollIntoView({
97
+ behavior: 'instant'
98
+ });
99
+ },
100
+ 'message:failed': data => {
101
+ var _this$messagesContain;
102
+ (_this$messagesContain = this.messagesContainerTarget.querySelector(`#${data.id}`)) === null || _this$messagesContain === void 0 ? void 0 : _this$messagesContain.classList.add('failed');
103
+ }
104
+ };
105
+ if (callbacks[data.type]) {
106
+ callbacks[data.type](data);
107
+ } else {
108
+ console.log(`Unhandled message event: ${data.type}`);
109
+ }
110
+ }
81
111
  }, {
82
112
  key: "onScroll",
83
113
  value: async function onScroll() {
@@ -253,6 +283,7 @@ let _default = /*#__PURE__*/function (_Controller) {
253
283
  });
254
284
  formData.append('session', _hellotext.default.session);
255
285
  const element = this.messageTemplateTarget.cloneNode(true);
286
+ element.id = `hellotext--webchat--${this.idValue}--message--${Date.now()}`;
256
287
  element.classList.add('received');
257
288
  element.style.removeProperty('display');
258
289
  element.setAttribute('data-hellotext--webchat-target', 'message');
@@ -267,6 +298,10 @@ let _default = /*#__PURE__*/function (_Controller) {
267
298
  element.scrollIntoView({
268
299
  behavior: 'smooth'
269
300
  });
301
+ this.broadcastChannel.postMessage({
302
+ type: 'message:sent',
303
+ element: element.outerHTML
304
+ });
270
305
  this.inputTarget.value = '';
271
306
  this.files = [];
272
307
  this.attachmentContainerTarget.style.display = 'none';
@@ -274,6 +309,10 @@ let _default = /*#__PURE__*/function (_Controller) {
274
309
  this.inputTarget.focus();
275
310
  const response = await this.messagesAPI.create(formData);
276
311
  if (response.failed) {
312
+ this.broadcastChannel.postMessage({
313
+ type: 'message:failed',
314
+ id: element.id
315
+ });
277
316
  return element.classList.add('failed');
278
317
  }
279
318
  const data = await response.json();
@@ -43,6 +43,8 @@ var _default = /*#__PURE__*/function (_Controller) {
43
43
  this.onAgentOnline = this.onAgentOnline.bind(this);
44
44
  this.onMessageReaction = this.onMessageReaction.bind(this);
45
45
  this.onScroll = this.onScroll.bind(this);
46
+ this.onOutboundMessageSent = this.onOutboundMessageSent.bind(this);
47
+ this.broadcastChannel = new BroadcastChannel("hellotext--webchat--".concat(this.idValue));
46
48
  _get(_getPrototypeOf(_default.prototype), "initialize", this).call(this);
47
49
  }
48
50
  }, {
@@ -67,15 +69,43 @@ var _default = /*#__PURE__*/function (_Controller) {
67
69
  this.openValue = true;
68
70
  }
69
71
  Hellotext.eventEmitter.dispatch('webchat:mounted');
72
+ this.broadcastChannel.addEventListener('message', this.onOutboundMessageSent);
70
73
  _get(_getPrototypeOf(_default.prototype), "connect", this).call(this);
71
74
  }
72
75
  }, {
73
76
  key: "disconnect",
74
77
  value: function disconnect() {
78
+ this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent);
75
79
  this.messagesContainerTarget.removeEventListener('scroll', this.onScroll);
80
+ this.broadcastChannel.close();
76
81
  this.floatingUICleanup();
77
82
  _get(_getPrototypeOf(_default.prototype), "disconnect", this).call(this);
78
83
  }
84
+ }, {
85
+ key: "onOutboundMessageSent",
86
+ value: function onOutboundMessageSent(event) {
87
+ var {
88
+ data
89
+ } = event;
90
+ var callbacks = {
91
+ 'message:sent': data => {
92
+ var element = new DOMParser().parseFromString(data.element, 'text/html').body.firstElementChild;
93
+ this.messagesContainerTarget.appendChild(element);
94
+ element.scrollIntoView({
95
+ behavior: 'instant'
96
+ });
97
+ },
98
+ 'message:failed': data => {
99
+ var _this$messagesContain;
100
+ (_this$messagesContain = this.messagesContainerTarget.querySelector("#".concat(data.id))) === null || _this$messagesContain === void 0 ? void 0 : _this$messagesContain.classList.add('failed');
101
+ }
102
+ };
103
+ if (callbacks[data.type]) {
104
+ callbacks[data.type](data);
105
+ } else {
106
+ console.log("Unhandled message event: ".concat(data.type));
107
+ }
108
+ }
79
109
  }, {
80
110
  key: "onScroll",
81
111
  value: function () {
@@ -257,6 +287,7 @@ var _default = /*#__PURE__*/function (_Controller) {
257
287
  });
258
288
  formData.append('session', Hellotext.session);
259
289
  var element = this.messageTemplateTarget.cloneNode(true);
290
+ element.id = "hellotext--webchat--".concat(this.idValue, "--message--").concat(Date.now());
260
291
  element.classList.add('received');
261
292
  element.style.removeProperty('display');
262
293
  element.setAttribute('data-hellotext--webchat-target', 'message');
@@ -271,6 +302,10 @@ var _default = /*#__PURE__*/function (_Controller) {
271
302
  element.scrollIntoView({
272
303
  behavior: 'smooth'
273
304
  });
305
+ this.broadcastChannel.postMessage({
306
+ type: 'message:sent',
307
+ element: element.outerHTML
308
+ });
274
309
  this.inputTarget.value = '';
275
310
  this.files = [];
276
311
  this.attachmentContainerTarget.style.display = 'none';
@@ -278,6 +313,10 @@ var _default = /*#__PURE__*/function (_Controller) {
278
313
  this.inputTarget.focus();
279
314
  var response = yield this.messagesAPI.create(formData);
280
315
  if (response.failed) {
316
+ this.broadcastChannel.postMessage({
317
+ type: 'message:failed',
318
+ id: element.id
319
+ });
281
320
  return element.classList.add('failed');
282
321
  }
283
322
  var data = yield response.json();
@@ -24,6 +24,20 @@ const placements = {
24
24
  BOTTOM_RIGHT: 'bottom-right'
25
25
  };
26
26
 
27
+ /**
28
+ * @typedef {'absolute' | 'fixed'} Strategy
29
+ * @description Valid strategies for the webchat.
30
+ */
31
+
32
+ /**
33
+ * @enum {Strategy}
34
+ */
35
+
36
+ const strategies = {
37
+ ABSOLUTE: 'absolute',
38
+ FIXED: 'fixed'
39
+ };
40
+
27
41
  /**
28
42
  * @typedef {'modal' | 'popover'} Behaviour
29
43
  */
@@ -54,6 +68,7 @@ const behaviors = {
54
68
  * @property {String} triggerClasses - additional classes to apply to the webchat popup trigger.
55
69
  * @property {Behaviour} behaviour - the behaviour of the webchat, defaults to 'popover'.
56
70
  * @property {Style} style - the style of the webchat.
71
+ * @property {Strategy} strategy - the strategy used to position the webchat. Defaults to 'absolute'
57
72
  */
58
73
  exports.behaviors = behaviors;
59
74
  let Webchat = /*#__PURE__*/function () {
@@ -155,6 +170,20 @@ let Webchat = /*#__PURE__*/function () {
155
170
  }
156
171
  this._behaviour = value;
157
172
  }
173
+ }, {
174
+ key: "strategy",
175
+ get: function () {
176
+ if (this._strategy) {
177
+ return this._strategy;
178
+ }
179
+ return this.container == 'body' ? strategies.FIXED : strategies.ABSOLUTE;
180
+ },
181
+ set: function (value) {
182
+ if (value && !Object.values(strategies).includes(value)) {
183
+ throw new Error(`Invalid strategy value: ${value}`);
184
+ }
185
+ this._strategy = value;
186
+ }
158
187
  }, {
159
188
  key: "assign",
160
189
  value: function assign(props) {
@@ -180,4 +209,5 @@ Webchat._placement = 'bottom-right';
180
209
  Webchat._classes = [];
181
210
  Webchat._triggerClasses = [];
182
211
  Webchat._style = {};
183
- Webchat._behaviour = behaviors.POPOVER;
212
+ Webchat._behaviour = behaviors.POPOVER;
213
+ Webchat._strategy = null;
@@ -18,6 +18,20 @@ var placements = {
18
18
  BOTTOM_RIGHT: 'bottom-right'
19
19
  };
20
20
 
21
+ /**
22
+ * @typedef {'absolute' | 'fixed'} Strategy
23
+ * @description Valid strategies for the webchat.
24
+ */
25
+
26
+ /**
27
+ * @enum {Strategy}
28
+ */
29
+
30
+ var strategies = {
31
+ ABSOLUTE: 'absolute',
32
+ FIXED: 'fixed'
33
+ };
34
+
21
35
  /**
22
36
  * @typedef {'modal' | 'popover'} Behaviour
23
37
  */
@@ -48,6 +62,7 @@ var behaviors = {
48
62
  * @property {String} triggerClasses - additional classes to apply to the webchat popup trigger.
49
63
  * @property {Behaviour} behaviour - the behaviour of the webchat, defaults to 'popover'.
50
64
  * @property {Style} style - the style of the webchat.
65
+ * @property {Strategy} strategy - the strategy used to position the webchat. Defaults to 'absolute'
51
66
  */
52
67
  var Webchat = /*#__PURE__*/function () {
53
68
  function Webchat() {
@@ -149,6 +164,20 @@ var Webchat = /*#__PURE__*/function () {
149
164
  }
150
165
  this._behaviour = value;
151
166
  }
167
+ }, {
168
+ key: "strategy",
169
+ get: function get() {
170
+ if (this._strategy) {
171
+ return this._strategy;
172
+ }
173
+ return this.container == 'body' ? strategies.FIXED : strategies.ABSOLUTE;
174
+ },
175
+ set: function set(value) {
176
+ if (value && !Object.values(strategies).includes(value)) {
177
+ throw new Error("Invalid strategy value: ".concat(value));
178
+ }
179
+ this._strategy = value;
180
+ }
152
181
  }, {
153
182
  key: "assign",
154
183
  value: function assign(props) {
@@ -175,4 +204,5 @@ Webchat._classes = [];
175
204
  Webchat._triggerClasses = [];
176
205
  Webchat._style = {};
177
206
  Webchat._behaviour = behaviors.POPOVER;
178
- export { Webchat, behaviors };
207
+ Webchat._strategy = null;
208
+ export { behaviors, Webchat };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hellotext/hellotext",
3
- "version": "2.0.9",
3
+ "version": "2.1.1",
4
4
  "description": "Hellotext JavaScript Client",
5
5
  "source": "src/index.js",
6
6
  "main": "lib/index.cjs",
@@ -1,7 +1,7 @@
1
1
  import { autoUpdate, computePosition } from '@floating-ui/dom'
2
- import Hellotext from "../../hellotext";
2
+ import { Configuration } from '../../core'
3
3
 
4
- export const usePopover = (controller) => {
4
+ export const usePopover = controller => {
5
5
  Object.assign(controller, {
6
6
  show() {
7
7
  this.openValue = true
@@ -17,34 +17,36 @@ export const usePopover = (controller) => {
17
17
  computePosition(trigger, popover, {
18
18
  placement: this.placementValue,
19
19
  middleware: this.middlewares,
20
- }).then(({x, y}) => {
20
+ strategy: Configuration.webchat.strategy,
21
+ }).then(({ x, y, strategy }) => {
21
22
  const newStyle = {
22
23
  left: `${x}px`,
23
- top: `${y}px`
24
+ top: `${y}px`,
25
+ position: strategy,
24
26
  }
25
27
 
26
28
  Object.assign(popover.style, newStyle)
27
- });
29
+ })
28
30
  })
29
31
  },
30
32
  openValueChanged() {
31
- if(this.disabledValue) return
33
+ if (this.disabledValue) return
32
34
 
33
- if(this.openValue) {
35
+ if (this.openValue) {
34
36
  this.popoverTarget.showPopover()
35
- this.popoverTarget.setAttribute("aria-expanded", "true")
37
+ this.popoverTarget.setAttribute('aria-expanded', 'true')
36
38
 
37
- if(this['onPopoverOpened']) {
39
+ if (this['onPopoverOpened']) {
38
40
  this.onPopoverOpened()
39
41
  }
40
42
  } else {
41
43
  this.popoverTarget.hidePopover()
42
- this.popoverTarget.removeAttribute("aria-expanded")
44
+ this.popoverTarget.removeAttribute('aria-expanded')
43
45
 
44
- if(this['onPopoverClosed']) {
46
+ if (this['onPopoverClosed']) {
45
47
  this.onPopoverClosed()
46
48
  }
47
49
  }
48
- }
50
+ },
49
51
  })
50
52
  }
@@ -62,6 +62,9 @@ export default class extends Controller {
62
62
 
63
63
  this.onScroll = this.onScroll.bind(this)
64
64
 
65
+ this.onOutboundMessageSent = this.onOutboundMessageSent.bind(this)
66
+ this.broadcastChannel = new BroadcastChannel(`hellotext--webchat--${this.idValue}`)
67
+
65
68
  super.initialize()
66
69
  }
67
70
 
@@ -90,16 +93,44 @@ export default class extends Controller {
90
93
  }
91
94
 
92
95
  Hellotext.eventEmitter.dispatch('webchat:mounted')
96
+ this.broadcastChannel.addEventListener('message', this.onOutboundMessageSent)
97
+
93
98
  super.connect()
94
99
  }
95
100
 
96
101
  disconnect() {
102
+ this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent)
97
103
  this.messagesContainerTarget.removeEventListener('scroll', this.onScroll)
104
+
105
+ this.broadcastChannel.close()
98
106
  this.floatingUICleanup()
99
107
 
100
108
  super.disconnect()
101
109
  }
102
110
 
111
+ onOutboundMessageSent(event) {
112
+ const { data } = event
113
+
114
+ const callbacks = {
115
+ 'message:sent': data => {
116
+ const element = new DOMParser().parseFromString(data.element, 'text/html').body
117
+ .firstElementChild
118
+
119
+ this.messagesContainerTarget.appendChild(element)
120
+ element.scrollIntoView({ behavior: 'instant' })
121
+ },
122
+ 'message:failed': data => {
123
+ this.messagesContainerTarget.querySelector(`#${data.id}`)?.classList.add('failed')
124
+ },
125
+ }
126
+
127
+ if (callbacks[data.type]) {
128
+ callbacks[data.type](data)
129
+ } else {
130
+ console.log(`Unhandled message event: ${data.type}`)
131
+ }
132
+ }
133
+
103
134
  async onScroll() {
104
135
  if (
105
136
  this.messagesContainerTarget.scrollTop > 300 ||
@@ -307,6 +338,8 @@ export default class extends Controller {
307
338
 
308
339
  const element = this.messageTemplateTarget.cloneNode(true)
309
340
 
341
+ element.id = `hellotext--webchat--${this.idValue}--message--${Date.now()}`
342
+
310
343
  element.classList.add('received')
311
344
  element.style.removeProperty('display')
312
345
 
@@ -324,6 +357,11 @@ export default class extends Controller {
324
357
  this.messagesContainerTarget.appendChild(element)
325
358
  element.scrollIntoView({ behavior: 'smooth' })
326
359
 
360
+ this.broadcastChannel.postMessage({
361
+ type: 'message:sent',
362
+ element: element.outerHTML,
363
+ })
364
+
327
365
  this.inputTarget.value = ''
328
366
  this.files = []
329
367
  this.attachmentContainerTarget.style.display = 'none'
@@ -334,6 +372,11 @@ export default class extends Controller {
334
372
  const response = await this.messagesAPI.create(formData)
335
373
 
336
374
  if (response.failed) {
375
+ this.broadcastChannel.postMessage({
376
+ type: 'message:failed',
377
+ id: element.id,
378
+ })
379
+
337
380
  return element.classList.add('failed')
338
381
  }
339
382