@hellotext/hellotext 1.8.4 → 1.8.5

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.
Files changed (49) hide show
  1. package/__tests__/core/configuration/webchat_test.js +163 -0
  2. package/__tests__/hellotext_test.js +97 -101
  3. package/dist/hellotext.js +1 -1
  4. package/docs/webchat.md +101 -0
  5. package/jest.config.js +1 -0
  6. package/jest.setup.js +9 -0
  7. package/lib/api/index.js +6 -0
  8. package/lib/api/web_chat/messages.js +74 -0
  9. package/lib/api/web_chats.js +53 -0
  10. package/lib/api/webchat/messages.js +85 -0
  11. package/lib/api/webchats.js +53 -0
  12. package/lib/builders/logo_builder.js +2 -1
  13. package/lib/channels/application_channel.js +68 -0
  14. package/lib/channels/web_chat_channel.js +97 -0
  15. package/lib/channels/webchat_channel.js +112 -0
  16. package/lib/controllers/mixins/usePopover.js +61 -0
  17. package/lib/controllers/web_chat/pagination_controller.js +81 -0
  18. package/lib/controllers/webchat/emoji_picker_controller.js +134 -0
  19. package/lib/controllers/webchat_controller.js +446 -0
  20. package/lib/core/configuration/web_chat.js +168 -0
  21. package/lib/core/configuration/webchat.js +185 -0
  22. package/lib/core/configuration.js +6 -1
  23. package/lib/core/event.js +1 -1
  24. package/lib/hellotext.js +18 -9
  25. package/lib/index.js +4 -0
  26. package/lib/models/business.js +9 -1
  27. package/lib/models/index.js +7 -0
  28. package/lib/models/web_chat.js +51 -0
  29. package/lib/models/webchat.js +51 -0
  30. package/package.json +8 -7
  31. package/src/api/index.js +5 -0
  32. package/src/api/webchat/messages.js +55 -0
  33. package/src/api/webchats.js +30 -0
  34. package/src/builders/logo_builder.js +8 -4
  35. package/src/channels/application_channel.js +45 -0
  36. package/src/channels/webchat_channel.js +77 -0
  37. package/src/controllers/mixins/usePopover.js +50 -0
  38. package/src/controllers/webchat/emoji_picker_controller.js +82 -0
  39. package/src/controllers/webchat_controller.js +448 -0
  40. package/src/core/configuration/webchat.js +176 -0
  41. package/src/core/configuration.js +5 -0
  42. package/src/core/event.js +10 -1
  43. package/src/hellotext.js +7 -2
  44. package/src/index.js +5 -0
  45. package/src/models/business.js +11 -1
  46. package/src/models/index.js +1 -0
  47. package/src/models/webchat.js +28 -0
  48. package/styles/index.css +4 -0
  49. package/webpack.config.js +1 -1
@@ -0,0 +1,176 @@
1
+ /**
2
+ * @typedef {'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'} Placement
3
+ * @description Valid placements for the webchat.
4
+ */
5
+
6
+ /**
7
+ * @enum {Placement}
8
+ */
9
+ const placements = {
10
+ TOP_LEFT: 'top-left',
11
+ TOP_RIGHT: 'top-right',
12
+ BOTTOM_LEFT: 'bottom-left',
13
+ BOTTOM_RIGHT: 'bottom-right'
14
+ }
15
+
16
+
17
+ /**
18
+ * @typedef {'modal' | 'popover'} Behaviour
19
+ */
20
+
21
+ /**
22
+ * @enum {Behaviour}
23
+ */
24
+ const behaviors = {
25
+ MODAL: 'modal',
26
+ POPOVER: 'popover'
27
+ }
28
+
29
+ /**
30
+ * @typedef {Object} Style
31
+ * @property {string} primaryColor - The primary webchat color (e.g., a hex code or color name).
32
+ * @property {string} secondaryColor - The secondary webchat color or style (e.g., a hex code or color name).
33
+ * @property {string} typography - The typography style (e.g., font family).
34
+ */
35
+
36
+
37
+ /**
38
+ * @class Webchat
39
+ * @classdesc
40
+ * Configuration for webchat
41
+ * @property {String} id - the id of the webchat.
42
+ * @property {String} container - the container to append the webchat to, defaults to 'body'
43
+ * @property {Placement} placement - the placement of the webchat within the container, defaults to "bottom-right".
44
+ * @property {String} classes - additional classes to apply to the webchat popup.
45
+ * @property {String} triggerClasses - additional classes to apply to the webchat popup trigger.
46
+ * @property {Behaviour} behaviour - the behaviour of the webchat, defaults to 'popover'.
47
+ * @property {Style} style - the style of the webchat.
48
+ */
49
+
50
+ class Webchat {
51
+ static _id
52
+ static _container = 'body'
53
+ static _placement = 'bottom-right'
54
+ static _classes = []
55
+ static _triggerClasses = []
56
+ static _style = {}
57
+ static _behaviour = behaviors.POPOVER
58
+
59
+ static set container(value) {
60
+ this._container = value
61
+ }
62
+
63
+ static get container() {
64
+ return this._container
65
+ }
66
+
67
+ static set placement(value) {
68
+ if(!Object.values(placements).includes(value)) {
69
+ throw new Error(`Invalid placement value: ${value}`)
70
+ }
71
+
72
+ this._placement = value
73
+ }
74
+
75
+ static get placement() {
76
+ return this._placement
77
+ }
78
+
79
+ static set classes(value) {
80
+ if(!Array.isArray(value) && typeof value !== 'string') {
81
+ throw new Error('classes must be an array or a string')
82
+ }
83
+
84
+ this._classes = value;
85
+ }
86
+
87
+ static get classes() {
88
+ if(typeof this._classes === 'string') {
89
+ return this._classes.split(',').map(c => c.trim())
90
+ } else {
91
+ return this._classes;
92
+ }
93
+ }
94
+
95
+ static set triggerClasses(value) {
96
+ if(!Array.isArray(value) && typeof value !== 'string') {
97
+ throw new Error('triggerClasses must be an array or a string')
98
+ }
99
+
100
+ this._triggerClasses = value;
101
+ }
102
+
103
+ static get triggerClasses() {
104
+ if(typeof this._triggerClasses === 'string') {
105
+ return this._triggerClasses.split(',').map(c => c.trim())
106
+ } else {
107
+ return this._triggerClasses;
108
+ }
109
+ }
110
+
111
+ static set id(value) {
112
+ this._id = value
113
+ }
114
+
115
+ static get id() {
116
+ return this._id
117
+ }
118
+
119
+ static get isSet() {
120
+ return !!this._id
121
+ }
122
+
123
+ static get style() {
124
+ return this._style
125
+ }
126
+
127
+ static set style(value) {
128
+ if(typeof value !== 'object') {
129
+ throw new Error('Style must be an object')
130
+ }
131
+
132
+ Object.entries(value).forEach(([key, value]) => {
133
+ if(!['primaryColor', 'secondaryColor', 'typography'].includes(key)) {
134
+ throw new Error(`Invalid style property: ${key}`)
135
+ }
136
+
137
+ if(key === 'typography') {
138
+ return
139
+ }
140
+
141
+ if(!this.isHexOrRgba(value)) {
142
+ throw new Error(`Invalid color value: ${value} for ${key}. Colors must be hex or rgb/a.`)
143
+ }
144
+ })
145
+
146
+ this._style = value
147
+ }
148
+
149
+ static get behaviour() {
150
+ return this._behaviour
151
+ }
152
+
153
+ static set behaviour(value) {
154
+ if(!Object.values(behaviors).includes(value)) {
155
+ throw new Error(`Invalid behaviour value: ${value}`)
156
+ }
157
+
158
+ this._behaviour = value
159
+ }
160
+
161
+ static assign(props) {
162
+ if(props) {
163
+ Object.entries(props).forEach(([key, value]) => {
164
+ this[key] = value
165
+ })
166
+ }
167
+
168
+ return this
169
+ }
170
+
171
+ static isHexOrRgba(value) {
172
+ return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value) || /^rgba?\(\s*\d{1,3},\s*\d{1,3},\s*\d{1,3},?\s*(0|1|0?\.\d+)?\s*\)$/.test(value);
173
+ }
174
+ }
175
+
176
+ export { Webchat, behaviors }
@@ -1,4 +1,5 @@
1
1
  import { Forms } from './configuration/forms'
2
+ import { Webchat } from './configuration/webchat'
2
3
 
3
4
  /**
4
5
  * @class Configuration
@@ -7,6 +8,7 @@ import { Forms } from './configuration/forms'
7
8
  * @property {Boolean} [autoGenerateSession=true] - whether to auto generate session or not
8
9
  * @property {String} [session] - session id
9
10
  * @property {Forms} [forms] - form configuration
11
+ * @property {Webchat} [webchat] - webchat configuration
10
12
  */
11
13
  class Configuration {
12
14
  static apiRoot = 'https://api.hellotext.com/v1'
@@ -15,6 +17,7 @@ class Configuration {
15
17
  static session = null
16
18
 
17
19
  static forms = Forms
20
+ static webchat = Webchat
18
21
 
19
22
  /**
20
23
  *
@@ -29,6 +32,8 @@ class Configuration {
29
32
  Object.entries(props).forEach(([key, value]) => {
30
33
  if (key === 'forms') {
31
34
  this.forms = Forms.assign(value)
35
+ } else if(key === 'webChat') {
36
+ this.webchat = Webchat.assign(value)
32
37
  } else {
33
38
  this[key] = value
34
39
  }
package/src/core/event.js CHANGED
@@ -1,7 +1,16 @@
1
1
  import { InvalidEvent } from '../errors'
2
2
 
3
3
  export default class Event {
4
- static events = ['session-set', 'forms:collected', 'form:completed']
4
+ static events = [
5
+ 'session-set',
6
+ 'forms:collected',
7
+ 'form:completed',
8
+ 'webchat:mounted',
9
+ 'webchat:opened',
10
+ 'webchat:closed',
11
+ 'webchat:message:sent',
12
+ 'webchat:message:received',
13
+ ]
5
14
 
6
15
  static valid(name) {
7
16
  return Event.exists(name)
package/src/hellotext.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Event, Configuration } from './core'
2
2
 
3
3
  import API, { Response } from './api'
4
- import { Business, Query, FormCollection, Session } from './models'
4
+ import { Business, Query, FormCollection, Session, Webchat } from './models'
5
5
 
6
6
  import { NotInitializedError } from './errors'
7
7
 
@@ -13,13 +13,14 @@ class Hellotext {
13
13
  static eventEmitter = new Event()
14
14
  static forms
15
15
  static business
16
+ static webchat
16
17
 
17
18
  /**
18
19
  * initialize the module.
19
20
  * @param business public business id
20
21
  * @param { Configuration } config
21
22
  */
22
- static initialize(business, config) {
23
+ static async initialize(business, config) {
23
24
  Configuration.assign(config)
24
25
  Session.initialize()
25
26
 
@@ -27,6 +28,10 @@ class Hellotext {
27
28
 
28
29
  this.business = new Business(business)
29
30
  this.forms = new FormCollection()
31
+
32
+ if (Configuration.webchat.id) {
33
+ this.webchat = await Webchat.load(Configuration.webchat.id)
34
+ }
30
35
  }
31
36
 
32
37
  /**
package/src/index.js CHANGED
@@ -2,9 +2,14 @@ import { Application } from '@hotwired/stimulus'
2
2
  import Hellotext from './hellotext'
3
3
 
4
4
  import FormController from './controllers/form_controller'
5
+ import WebchatController from './controllers/webchat_controller'
6
+ import WebChatEmojiController from './controllers/webchat/emoji_picker_controller'
5
7
 
6
8
  const application = Application.start()
9
+
7
10
  application.register('hellotext--form', FormController)
11
+ application.register('hellotext--webchat', WebchatController)
12
+ application.register('hellotext--webchat--emoji', WebChatEmojiController)
8
13
 
9
14
  import '../styles/index.css'
10
15
 
@@ -34,7 +34,17 @@ class Business {
34
34
  fetchPublicData() {
35
35
  API.get(this.id)
36
36
  .then(response => response.json())
37
- .then(data => (this.data = data))
37
+ .then(data => {
38
+ this.data = data
39
+
40
+ if(typeof document !== 'undefined') {
41
+ const linkTag = document.createElement('link')
42
+ linkTag.rel = 'stylesheet'
43
+ linkTag.href = data.style_url
44
+
45
+ document.head.append(linkTag)
46
+ }
47
+ })
38
48
  }
39
49
  }
40
50
 
@@ -3,4 +3,5 @@ export { Form } from './form'
3
3
  export { Query } from './query'
4
4
  export { Cookies } from './cookies'
5
5
  export { FormCollection } from './form_collection'
6
+ export { Webchat } from './webchat'
6
7
  export { Session } from './session'
@@ -0,0 +1,28 @@
1
+ import Hellotext from '../hellotext';
2
+ import { Configuration } from '../core'
3
+
4
+ import API from '../api';
5
+
6
+ class Webchat {
7
+ static async load(id) {
8
+ return new Webchat({
9
+ id,
10
+ html: await API.webchats.get(id),
11
+ })
12
+ }
13
+
14
+ constructor(data) {
15
+ this.data = data
16
+ this.render()
17
+ }
18
+
19
+ render() {
20
+ this.containerToAppendTo.appendChild(this.data.html)
21
+ }
22
+
23
+ get containerToAppendTo() {
24
+ return document.querySelector(Configuration.webchat.container)
25
+ }
26
+ }
27
+
28
+ export { Webchat }
package/styles/index.css CHANGED
@@ -1,3 +1,7 @@
1
+ .hidden {
2
+ display: none;
3
+ }
4
+
1
5
  form[data-hello-form] {
2
6
  position: relative;
3
7
 
package/webpack.config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  module.exports = {
2
2
  mode: 'production',
3
- entry: ['whatwg-fetch', '@hotwired/stimulus', './lib/index.js'],
3
+ entry: ['@hotwired/stimulus', './lib/index.js'],
4
4
  output: {
5
5
  filename: 'hellotext.js',
6
6
  },