@botpress/webchat 0.5.1 → 1.0.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.
Files changed (295) hide show
  1. package/dist/App.d.ts +10 -0
  2. package/dist/Utils/colors.d.ts +18 -0
  3. package/dist/Utils/eventEmitter.d.ts +12 -0
  4. package/dist/Utils/index.d.ts +2 -0
  5. package/dist/client/MessagingClient.d.ts +27 -0
  6. package/dist/client/adapters/Audio.d.ts +19 -0
  7. package/dist/client/adapters/Card.d.ts +188 -0
  8. package/dist/client/adapters/Carousel.d.ts +147 -0
  9. package/dist/client/adapters/Choice.d.ts +45 -0
  10. package/dist/client/adapters/Dropdown.d.ts +46 -0
  11. package/dist/client/adapters/File.d.ts +19 -0
  12. package/dist/client/adapters/Image.d.ts +19 -0
  13. package/dist/client/adapters/Location.d.ts +27 -0
  14. package/dist/client/adapters/Message.d.ts +433 -0
  15. package/dist/client/adapters/Text.d.ts +20 -0
  16. package/dist/client/adapters/Utils.d.ts +5 -0
  17. package/dist/client/adapters/Video.d.ts +19 -0
  18. package/dist/client/adapters/Voice.d.ts +15 -0
  19. package/dist/client/adapters/index.d.ts +12 -0
  20. package/dist/client/index.d.ts +2 -0
  21. package/dist/components/Avatar.d.ts +6 -0
  22. package/dist/components/Block.d.ts +4 -0
  23. package/dist/components/Composer.d.ts +12 -14
  24. package/dist/components/Container.d.ts +2 -12
  25. package/dist/components/Header.d.ts +36 -26
  26. package/dist/components/LoadingIndicator.d.ts +2 -0
  27. package/dist/components/Message.d.ts +7 -0
  28. package/dist/components/MessageList.d.ts +2 -0
  29. package/dist/components/Modal.d.ts +17 -0
  30. package/dist/components/RestartConversation.d.ts +5 -0
  31. package/dist/components/Webchat.d.ts +6 -0
  32. package/dist/components/dev-tools/DevTools.d.ts +1 -0
  33. package/dist/components/dev-tools/configuration.d.ts +2 -0
  34. package/dist/components/dev-tools/helpers.d.ts +5 -0
  35. package/dist/components/index.d.ts +12 -0
  36. package/dist/components/renderers/Audio.d.ts +3 -0
  37. package/dist/components/renderers/Bubble.d.ts +5 -0
  38. package/dist/components/renderers/Button.d.ts +4 -0
  39. package/dist/components/renderers/Carousel.d.ts +3 -0
  40. package/dist/components/renderers/Column.d.ts +5 -0
  41. package/dist/components/renderers/Dropdown.d.ts +5 -0
  42. package/dist/components/renderers/File.d.ts +3 -0
  43. package/dist/components/renderers/Image.d.ts +3 -0
  44. package/dist/components/renderers/Location.d.ts +3 -0
  45. package/dist/components/renderers/Row.d.ts +5 -0
  46. package/dist/components/renderers/Text.d.ts +4 -0
  47. package/dist/components/renderers/Video.d.ts +3 -0
  48. package/dist/components/renderers/index.d.ts +2 -0
  49. package/dist/contexts/ComposerContext.d.ts +8 -0
  50. package/dist/contexts/MessageContext.d.ts +8 -0
  51. package/dist/contexts/ModalContext.d.ts +14 -0
  52. package/dist/contexts/WebchatContext.d.ts +56 -0
  53. package/dist/contexts/index.d.ts +4 -0
  54. package/dist/hooks/index.d.ts +3 -0
  55. package/dist/hooks/useImageSize.d.ts +2 -0
  56. package/dist/hooks/useRefresh.d.ts +10 -0
  57. package/dist/hooks/useWebchatStore.d.ts +30 -0
  58. package/dist/index.d.ts +3 -17
  59. package/dist/index.js +43569 -48
  60. package/dist/index.umd.cjs +702 -0
  61. package/dist/main.d.ts +11 -13
  62. package/dist/providers/ModalProvider.d.ts +8 -0
  63. package/dist/providers/WebchatProvider.d.ts +13 -0
  64. package/dist/providers/index.d.ts +2 -0
  65. package/dist/schemas/index.d.ts +1 -0
  66. package/dist/schemas/theme.d.ts +3371 -0
  67. package/dist/services/clipboard.d.ts +1 -0
  68. package/dist/services/images.d.ts +2 -0
  69. package/dist/services/index.d.ts +3 -0
  70. package/dist/services/toast.d.ts +17 -0
  71. package/dist/themes/dawn.d.ts +2 -0
  72. package/dist/themes/duskTheme.d.ts +2 -0
  73. package/dist/themes/eggplant.d.ts +2 -0
  74. package/dist/themes/galaxy.d.ts +2 -0
  75. package/dist/themes/index.d.ts +6 -0
  76. package/dist/themes/midnight.d.ts +2 -0
  77. package/dist/themes/prism.d.ts +2 -0
  78. package/dist/twind.config.d.ts +9 -0
  79. package/dist/types/block-type.d.ts +93 -0
  80. package/dist/types/image.d.ts +11 -0
  81. package/dist/types/index.d.ts +2 -0
  82. package/dist/vite.svg +1 -0
  83. package/index.html +18 -0
  84. package/package.json +60 -49
  85. package/public/vite.svg +1 -0
  86. package/src/App.tsx +41 -0
  87. package/src/Utils/colors.ts +45 -0
  88. package/src/Utils/eventEmitter.ts +31 -0
  89. package/src/Utils/index.ts +2 -0
  90. package/src/assets/check-circle-bold.svg +5 -0
  91. package/src/assets/chevron-up.svg +3 -0
  92. package/src/assets/file-05.svg +6 -0
  93. package/src/assets/globe-02.svg +6 -0
  94. package/src/assets/help-circle.svg +3 -0
  95. package/src/assets/info-circle.svg +3 -0
  96. package/src/assets/lock-01.svg +4 -0
  97. package/src/assets/mail-01.svg +6 -0
  98. package/src/assets/minus-circle.svg +3 -0
  99. package/src/assets/phone.svg +6 -0
  100. package/src/assets/send-03.svg +4 -0
  101. package/src/assets/share-04.svg +5 -0
  102. package/src/assets/slash-circle-01.svg +3 -0
  103. package/src/assets/x-circle-bold.svg +5 -0
  104. package/src/assets/x-close.svg +3 -0
  105. package/src/assets/x.svg +3 -0
  106. package/src/client/MessagingClient.ts +87 -0
  107. package/src/client/adapters/Audio.ts +10 -0
  108. package/src/client/adapters/Card.ts +104 -0
  109. package/src/client/adapters/Carousel.ts +11 -0
  110. package/src/client/adapters/Choice.ts +48 -0
  111. package/src/client/adapters/Dropdown.ts +39 -0
  112. package/src/client/adapters/File.ts +10 -0
  113. package/src/client/adapters/Image.ts +10 -0
  114. package/src/client/adapters/Location.ts +18 -0
  115. package/src/client/adapters/Message.ts +26 -0
  116. package/src/client/adapters/Text.ts +11 -0
  117. package/src/client/adapters/Utils.ts +11 -0
  118. package/src/client/adapters/Video.ts +10 -0
  119. package/src/client/adapters/Voice.ts +9 -0
  120. package/src/client/adapters/index.ts +12 -0
  121. package/src/client/index.ts +2 -0
  122. package/src/components/Avatar.tsx +22 -0
  123. package/src/components/Block.tsx +17 -0
  124. package/src/components/Composer.tsx +115 -0
  125. package/src/components/Container.tsx +17 -0
  126. package/src/components/Header.tsx +141 -0
  127. package/src/components/LoadingIndicator.tsx +15 -0
  128. package/src/components/Message.tsx +52 -0
  129. package/src/components/MessageList.tsx +75 -0
  130. package/src/components/Modal.tsx +49 -0
  131. package/src/components/RestartConversation.tsx +52 -0
  132. package/src/components/Webchat.tsx +68 -0
  133. package/src/components/dev-tools/DevTools.tsx +496 -0
  134. package/src/components/dev-tools/configuration.tsx +27 -0
  135. package/src/components/dev-tools/helpers.ts +21 -0
  136. package/src/components/index.ts +12 -0
  137. package/src/components/renderers/Audio.tsx +11 -0
  138. package/src/components/renderers/Bubble.tsx +12 -0
  139. package/src/components/renderers/Button.tsx +59 -0
  140. package/src/components/renderers/Carousel.tsx +51 -0
  141. package/src/components/renderers/Column.tsx +22 -0
  142. package/src/components/renderers/Dropdown.tsx +170 -0
  143. package/src/components/renderers/File.tsx +13 -0
  144. package/src/components/renderers/Image.tsx +63 -0
  145. package/src/components/renderers/Location.tsx +16 -0
  146. package/src/components/renderers/Row.tsx +22 -0
  147. package/src/components/renderers/Text.tsx +32 -0
  148. package/src/components/renderers/Video.tsx +11 -0
  149. package/src/components/renderers/index.ts +28 -0
  150. package/src/contexts/ComposerContext.ts +16 -0
  151. package/src/contexts/MessageContext.ts +16 -0
  152. package/src/contexts/ModalContext.ts +19 -0
  153. package/src/contexts/WebchatContext.ts +61 -0
  154. package/src/contexts/index.ts +4 -0
  155. package/src/hooks/index.ts +3 -0
  156. package/src/hooks/useImageSize.ts +30 -0
  157. package/src/hooks/useRefresh.ts +33 -0
  158. package/src/hooks/useWebchatStore.ts +45 -0
  159. package/src/index.css +18 -0
  160. package/src/index.ts +3 -0
  161. package/src/main.tsx +33 -0
  162. package/src/providers/ModalProvider.tsx +35 -0
  163. package/src/providers/WebchatProvider.tsx +107 -0
  164. package/src/providers/index.ts +2 -0
  165. package/src/schemas/index.ts +1 -0
  166. package/src/schemas/theme.ts +188 -0
  167. package/src/services/clipboard.ts +8 -0
  168. package/src/services/images.ts +39 -0
  169. package/src/services/index.ts +3 -0
  170. package/src/services/toast.tsx +71 -0
  171. package/src/themes/dawn.ts +277 -0
  172. package/src/themes/duskTheme.ts +349 -0
  173. package/src/themes/eggplant.ts +353 -0
  174. package/src/themes/galaxy.ts +323 -0
  175. package/src/themes/index.ts +6 -0
  176. package/src/themes/midnight.ts +276 -0
  177. package/src/themes/prism.ts +349 -0
  178. package/src/twind.config.ts +31 -0
  179. package/src/types/block-type.ts +150 -0
  180. package/src/types/image.ts +10 -0
  181. package/src/types/index.ts +2 -0
  182. package/src/vite-env.d.ts +1 -0
  183. package/tailwind.config.js +0 -0
  184. package/tsconfig.json +30 -0
  185. package/tsconfig.node.json +10 -0
  186. package/vite.config.ts +31 -0
  187. package/README.md +0 -41
  188. package/assets/fonts/roboto/roboto.woff2 +0 -0
  189. package/assets/fonts/roboto/roboto500.woff2 +0 -0
  190. package/assets/fonts/roboto.css +0 -128
  191. package/assets/notification.mp3 +0 -0
  192. package/dist/components/Composer.js +0 -118
  193. package/dist/components/Container.js +0 -62
  194. package/dist/components/ConversationList.d.ts +0 -10
  195. package/dist/components/ConversationList.js +0 -41
  196. package/dist/components/Footer.d.ts +0 -3
  197. package/dist/components/Footer.js +0 -21
  198. package/dist/components/Header.js +0 -181
  199. package/dist/components/VoiceRecorder.d.ts +0 -10
  200. package/dist/components/VoiceRecorder.js +0 -137
  201. package/dist/components/common/Avatar/index.d.ts +0 -9
  202. package/dist/components/common/Avatar/index.js +0 -13
  203. package/dist/components/common/BotInfo/index.d.ts +0 -10
  204. package/dist/components/common/BotInfo/index.js +0 -107
  205. package/dist/components/common/BotInfo/style.scss +0 -88
  206. package/dist/components/common/ConfirmDialog/index.d.ts +0 -11
  207. package/dist/components/common/ConfirmDialog/index.js +0 -78
  208. package/dist/components/common/ConfirmDialog/style.module.scss +0 -48
  209. package/dist/components/common/Dialog/index.d.ts +0 -17
  210. package/dist/components/common/Dialog/index.js +0 -57
  211. package/dist/components/common/Dialog/style.module.scss +0 -29
  212. package/dist/components/common/ToolTip/index.d.ts +0 -10
  213. package/dist/components/common/ToolTip/index.js +0 -163
  214. package/dist/components/common/ToolTip/style.module.scss +0 -108
  215. package/dist/components/common/ToolTip/utils.d.ts +0 -15
  216. package/dist/components/common/ToolTip/utils.js +0 -78
  217. package/dist/components/common/variables.scss +0 -38
  218. package/dist/components/messages/InlineFeedback.d.ts +0 -11
  219. package/dist/components/messages/InlineFeedback.js +0 -56
  220. package/dist/components/messages/Message.d.ts +0 -11
  221. package/dist/components/messages/Message.js +0 -106
  222. package/dist/components/messages/MessageGroup.d.ts +0 -23
  223. package/dist/components/messages/MessageGroup.js +0 -63
  224. package/dist/components/messages/MessageList.d.ts +0 -10
  225. package/dist/components/messages/MessageList.js +0 -148
  226. package/dist/core/api.d.ts +0 -23
  227. package/dist/core/api.js +0 -117
  228. package/dist/core/constants.d.ts +0 -14
  229. package/dist/core/constants.js +0 -29
  230. package/dist/core/socket.d.ts +0 -14
  231. package/dist/core/socket.js +0 -57
  232. package/dist/declaration.d.ts +0 -2
  233. package/dist/declaration.js +0 -1
  234. package/dist/fonts/roboto.d.ts +0 -4
  235. package/dist/fonts/roboto.js +0 -9
  236. package/dist/globals.d.ts +0 -7
  237. package/dist/globals.js +0 -2
  238. package/dist/icons/Add.d.ts +0 -6
  239. package/dist/icons/Add.js +0 -10
  240. package/dist/icons/Cancel.d.ts +0 -5
  241. package/dist/icons/Cancel.js +0 -10
  242. package/dist/icons/Chat.d.ts +0 -6
  243. package/dist/icons/Chat.js +0 -9
  244. package/dist/icons/Close.d.ts +0 -3
  245. package/dist/icons/Close.js +0 -10
  246. package/dist/icons/Delete.d.ts +0 -3
  247. package/dist/icons/Delete.js +0 -11
  248. package/dist/icons/Download.d.ts +0 -3
  249. package/dist/icons/Download.js +0 -10
  250. package/dist/icons/Email.d.ts +0 -3
  251. package/dist/icons/Email.js +0 -8
  252. package/dist/icons/Information.d.ts +0 -3
  253. package/dist/icons/Information.js +0 -12
  254. package/dist/icons/List.d.ts +0 -3
  255. package/dist/icons/List.js +0 -15
  256. package/dist/icons/Microphone.d.ts +0 -5
  257. package/dist/icons/Microphone.js +0 -12
  258. package/dist/icons/Phone.d.ts +0 -3
  259. package/dist/icons/Phone.js +0 -8
  260. package/dist/icons/Reload.d.ts +0 -3
  261. package/dist/icons/Reload.js +0 -10
  262. package/dist/icons/ThumbsDown.d.ts +0 -3
  263. package/dist/icons/ThumbsDown.js +0 -11
  264. package/dist/icons/ThumbsUp.d.ts +0 -3
  265. package/dist/icons/ThumbsUp.js +0 -11
  266. package/dist/icons/Website.d.ts +0 -3
  267. package/dist/icons/Website.js +0 -8
  268. package/dist/main.js +0 -336
  269. package/dist/store/composer.d.ts +0 -17
  270. package/dist/store/composer.js +0 -98
  271. package/dist/store/index.d.ts +0 -89
  272. package/dist/store/index.js +0 -604
  273. package/dist/store/view.d.ts +0 -61
  274. package/dist/store/view.js +0 -365
  275. package/dist/translations/ar.json +0 -30
  276. package/dist/translations/de.json +0 -32
  277. package/dist/translations/en.json +0 -40
  278. package/dist/translations/es.json +0 -19
  279. package/dist/translations/fr.json +0 -40
  280. package/dist/translations/index.d.ts +0 -9
  281. package/dist/translations/index.js +0 -95
  282. package/dist/translations/it.json +0 -38
  283. package/dist/translations/pt.json +0 -19
  284. package/dist/translations/ru.json +0 -24
  285. package/dist/translations/uk.json +0 -24
  286. package/dist/typings.d.ts +0 -410
  287. package/dist/typings.js +0 -2
  288. package/dist/utils/analytics.d.ts +0 -5
  289. package/dist/utils/analytics.js +0 -37
  290. package/dist/utils/index.d.ts +0 -3
  291. package/dist/utils/index.js +0 -27
  292. package/dist/utils/storage.d.ts +0 -16
  293. package/dist/utils/storage.js +0 -129
  294. package/dist/utils/webchatEvents.d.ts +0 -2
  295. package/dist/utils/webchatEvents.js +0 -14
@@ -0,0 +1,22 @@
1
+ import { ComponentProps, forwardRef } from 'react'
2
+ import { ColumnBlock, CommonBlockProps } from '../../types'
3
+ import { Block } from '../Block'
4
+
5
+ type Props = CommonBlockProps & ColumnBlock & ComponentProps<'div'>
6
+ export const Column = forwardRef<HTMLDivElement, Props>(
7
+ ({ blocks, styles, type, horizontalAlignment, verticalAlignment, ...props }, ref) => {
8
+ return (
9
+ <div
10
+ data-horizontal={horizontalAlignment}
11
+ data-vertical={verticalAlignment}
12
+ {...props}
13
+ {...styles?.[type]}
14
+ ref={ref}
15
+ >
16
+ {blocks.map((block, index) => (
17
+ <Block key={index} styles={styles} block={block} />
18
+ ))}
19
+ </div>
20
+ )
21
+ }
22
+ )
@@ -0,0 +1,170 @@
1
+ // export const Dropdown = ({ styles, type, options, label, ...props }: Props) => {
2
+ import * as React from 'react'
3
+ import {
4
+ useFloating,
5
+ useClick,
6
+ useDismiss,
7
+ useRole,
8
+ useListNavigation,
9
+ useInteractions,
10
+ FloatingFocusManager,
11
+ useTypeahead,
12
+ offset,
13
+ flip,
14
+ size,
15
+ autoUpdate,
16
+ FloatingPortal,
17
+ } from '@floating-ui/react'
18
+ import { CommonBlockProps, DropdownBlock } from '../../types'
19
+ import { ComponentProps } from 'react'
20
+ import { ChevronDownIcon } from '@heroicons/react/20/solid'
21
+ import { clsx } from 'clsx'
22
+ import { useWebchatContext } from '../../contexts'
23
+
24
+ // const options = ['Red', 'Orange', 'Yellow', 'Green', 'Cyan', 'Blue', 'Purple', 'Pink', 'Maroon', 'Black', 'White']
25
+ type Props = CommonBlockProps & DropdownBlock & ComponentProps<'div'>
26
+
27
+ export const Dropdown = ({ styles, type, options, label, reusable }: Props) => {
28
+ const [isOpen, setIsOpen] = React.useState(false)
29
+ const [activeIndex, setActiveIndex] = React.useState<number | null>(null)
30
+ const [selectedIndex, setSelectedIndex] = React.useState<number | null>(null)
31
+ const { client } = useWebchatContext()
32
+
33
+ const optionsDict = options.reduce((acc, option) => {
34
+ acc[option.label] = option.value
35
+ return acc
36
+ }, {} as { [key: string]: string })
37
+
38
+ const labels = optionsDict ? Object.keys(optionsDict) : []
39
+
40
+ const onOpenChange = (open: boolean) => {
41
+ if (selectedIndex !== null && !reusable) {
42
+ setIsOpen(false)
43
+ } else {
44
+ setIsOpen(open)
45
+ }
46
+ }
47
+
48
+ const { refs, floatingStyles, context } = useFloating({
49
+ placement: 'bottom-start',
50
+ open: isOpen,
51
+ onOpenChange,
52
+ whileElementsMounted: autoUpdate,
53
+ middleware: [
54
+ offset(5),
55
+ flip({ padding: 10 }),
56
+ size({
57
+ apply({ rects, elements, availableHeight }) {
58
+ Object.assign(elements.floating.style, {
59
+ maxHeight: `${availableHeight}px`,
60
+ minWidth: `${rects.reference.width}px`,
61
+ })
62
+ },
63
+ padding: 10,
64
+ }),
65
+ ],
66
+ })
67
+
68
+ const listRef = React.useRef<Array<HTMLElement | null>>([])
69
+ const listContentRef = React.useRef(labels)
70
+ const isTypingRef = React.useRef(false)
71
+
72
+ const click = useClick(context, { event: 'mousedown' })
73
+ const dismiss = useDismiss(context)
74
+ const role = useRole(context, { role: 'listbox' })
75
+ const listNav = useListNavigation(context, {
76
+ listRef,
77
+ activeIndex,
78
+ selectedIndex,
79
+ onNavigate: setActiveIndex,
80
+ // This is a large list, allow looping.
81
+ loop: true,
82
+ })
83
+ const typeahead = useTypeahead(context, {
84
+ listRef: listContentRef,
85
+ activeIndex,
86
+ selectedIndex,
87
+ onMatch: isOpen ? setActiveIndex : setSelectedIndex,
88
+ onTypingChange(isTyping) {
89
+ isTypingRef.current = isTyping
90
+ },
91
+ })
92
+
93
+ const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
94
+ dismiss,
95
+ role,
96
+ listNav,
97
+ typeahead,
98
+ click,
99
+ ])
100
+
101
+ const handleSelect = (index: number) => {
102
+ setSelectedIndex(index)
103
+ client.sendMessage(optionsDict[labels[index]])
104
+ setIsOpen(false)
105
+ }
106
+
107
+ const selectedItemLabel = selectedIndex !== null ? labels[selectedIndex] : undefined
108
+
109
+ return (
110
+ <>
111
+ <div
112
+ tabIndex={0}
113
+ ref={refs.setReference}
114
+ aria-labelledby="select-label"
115
+ aria-autocomplete="none"
116
+ data-disabled={selectedIndex !== null && !reusable ? '' : undefined}
117
+ {...styles?.[type]?.button?.container}
118
+ {...getReferenceProps()}
119
+ >
120
+ <span {...styles?.[type]?.button?.text}>{selectedItemLabel || label || 'Select...'}</span>
121
+ <ChevronDownIcon {...styles?.[type]?.button?.icon} />
122
+ </div>
123
+ {isOpen && (
124
+ <FloatingPortal>
125
+ <FloatingFocusManager context={context} modal={false}>
126
+ <div
127
+ ref={refs.setFloating}
128
+ className={styles?.[type]?.content?.container?.className}
129
+ // className="min-w-[100px] overflow-y-auto rounded-md border border-gray-200 bg-white text-sm text-gray-700 outline-none"
130
+ style={{ ...floatingStyles, ...styles?.[type]?.content?.container?.style }}
131
+ {...getFloatingProps()}
132
+ >
133
+ {labels.map((value, i) => (
134
+ <div
135
+ key={value}
136
+ ref={(node) => {
137
+ listRef.current[i] = node
138
+ }}
139
+ role="option"
140
+ tabIndex={i === activeIndex ? 0 : -1}
141
+ aria-selected={i === selectedIndex && i === activeIndex}
142
+ data-active={i === activeIndex ? '' : undefined}
143
+ {...styles?.[type]?.content?.item}
144
+ {...getItemProps({
145
+ onClick() {
146
+ handleSelect(i)
147
+ },
148
+ onKeyDown(event) {
149
+ if (event.key === 'Enter') {
150
+ event.preventDefault()
151
+ handleSelect(i)
152
+ }
153
+
154
+ if (event.key === ' ' && !isTypingRef.current) {
155
+ event.preventDefault()
156
+ handleSelect(i)
157
+ }
158
+ },
159
+ })}
160
+ >
161
+ {value}
162
+ </div>
163
+ ))}
164
+ </div>
165
+ </FloatingFocusManager>
166
+ </FloatingPortal>
167
+ )}
168
+ </>
169
+ )
170
+ }
@@ -0,0 +1,13 @@
1
+ import { forwardRef } from 'react'
2
+ import { CommonBlockProps, FileBlock } from '../../types'
3
+ import { DocumentArrowDownIcon } from '@heroicons/react/20/solid'
4
+
5
+ type Props = CommonBlockProps & FileBlock
6
+ export const File = forwardRef<HTMLAnchorElement, Props>(({ type, title, styles, url, ...props }, ref) => {
7
+ return (
8
+ <a {...props} {...styles?.[type]?.container} href={url} download ref={ref}>
9
+ <p {...styles?.[type]?.title}>{title || url}</p>
10
+ <DocumentArrowDownIcon {...styles?.[type]?.icon} />
11
+ </a>
12
+ )
13
+ })
@@ -0,0 +1,63 @@
1
+ import { forwardRef, useEffect, useId, useLayoutEffect, useState } from 'react'
2
+ import { CommonBlockProps, ImageBlock } from '../../types'
3
+ import { useMessageContext } from '../../contexts'
4
+ import { getImageSize } from '../../services'
5
+
6
+ type Props = CommonBlockProps & ImageBlock
7
+ export const Image = forwardRef<HTMLImageElement, Props>(
8
+ ({ url, type, styles, orientation = 'auto', ...props }, ref) => {
9
+ const id = useId()
10
+ const { setIsLoading } = useMessageContext()
11
+ const [imageOrientation, setImageOrientation] = useState(orientation)
12
+ const [loaded, setLoaded] = useState(false)
13
+
14
+ useLayoutEffect(() => {
15
+ setIsLoading((prev) => [...prev, id])
16
+ }, [])
17
+
18
+ useEffect(() => {
19
+ if (!url) return
20
+ if (imageOrientation === 'auto') {
21
+ getImageSize(url).then(({ width, height }) => {
22
+ setImageOrientation(getClosestAspectRatio(width, height))
23
+ setIsLoading((prev) => {
24
+ return prev.filter((currId) => currId !== id)
25
+ })
26
+ })
27
+ }
28
+ }, [url])
29
+
30
+ return (
31
+ <>
32
+ {loaded ? null : <div data-orientation={imageOrientation} {...styles?.[type]?.placeholder} />}
33
+ <img
34
+ data-orientation={imageOrientation}
35
+ data-loaded={loaded}
36
+ {...props}
37
+ {...styles?.[type]?.image}
38
+ src={url}
39
+ alt=""
40
+ ref={ref}
41
+ loading="lazy"
42
+ onLoad={() => {
43
+ setLoaded(true)
44
+ }}
45
+ />
46
+ </>
47
+ )
48
+ }
49
+ )
50
+
51
+ function getClosestAspectRatio(width: number, height: number) {
52
+ type aspects = keyof typeof aspects
53
+ const aspects = {
54
+ square: 1,
55
+ portrait: 3 / 4,
56
+ landscape: 4 / 3,
57
+ } as const
58
+ const aspect = width / height
59
+ const closest = (Object.keys(aspects) as aspects[]).reduce((prev, curr) => {
60
+ return Math.abs(aspects[curr] - aspect) < Math.abs(aspects[prev] - aspect) ? curr : prev
61
+ })
62
+ return closest
63
+ }
@@ -0,0 +1,16 @@
1
+ import { forwardRef } from 'react'
2
+ import { CommonBlockProps, LocationBlock } from '../../types'
3
+ import { MapPinIcon } from '@heroicons/react/20/solid'
4
+
5
+ type Props = CommonBlockProps & LocationBlock
6
+ export const Location = forwardRef<HTMLAnchorElement, Props>(
7
+ ({ type, latitude, longitude, title, styles, ...props }, ref) => {
8
+ const link = `https://www.google.com/maps/search/?api=1&query=${latitude},${longitude}`
9
+ return (
10
+ <a {...props} {...styles?.[type]?.container} href={link} type="_blank" rel="noopener" ref={ref}>
11
+ <p {...styles?.[type]?.title}>{title}</p>
12
+ <MapPinIcon {...styles?.[type]?.icon} />
13
+ </a>
14
+ )
15
+ }
16
+ )
@@ -0,0 +1,22 @@
1
+ import { ComponentProps, forwardRef } from 'react'
2
+ import { RowBlock, CommonBlockProps } from '../../types'
3
+ import { Block } from '../Block'
4
+
5
+ type Props = CommonBlockProps & RowBlock & ComponentProps<'div'>
6
+ export const Row = forwardRef<HTMLDivElement, Props>(
7
+ ({ blocks, styles, type, horizontalAlignment, verticalAlignment, ...props }, ref) => {
8
+ return (
9
+ <div
10
+ data-horizontal={horizontalAlignment}
11
+ data-vertical={verticalAlignment}
12
+ {...props}
13
+ {...styles?.[type]}
14
+ ref={ref}
15
+ >
16
+ {blocks.map((block, index) => (
17
+ <Block key={index} styles={styles} block={block} />
18
+ ))}
19
+ </div>
20
+ )
21
+ }
22
+ )
@@ -0,0 +1,32 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { CommonBlockProps, TextBlock } from '../../types'
3
+ import ReactMarkdown from 'react-markdown'
4
+
5
+ import { ComponentProps } from 'react'
6
+
7
+ type Props = CommonBlockProps & TextBlock
8
+ export const Text = ({ text, type, styles }: Props) => {
9
+ const markdownStyles = styles?.[type]
10
+
11
+ const markdownComponents: ComponentProps<typeof ReactMarkdown>['components'] = {
12
+ h1: ({ node, ...props }) => <h1 {...props} {...markdownStyles?.heading1} />,
13
+ h2: ({ node, ...props }) => <h2 {...props} {...markdownStyles?.heading2} />,
14
+ h3: ({ node, ...props }) => <h3 {...props} {...markdownStyles?.heading3} />,
15
+ h4: 'h3',
16
+ h5: 'h3',
17
+ h6: 'h3',
18
+ em: ({ node, ...props }) => <em {...props} {...markdownStyles?.italic} />,
19
+ strong: ({ node, ...props }) => <strong {...props} {...markdownStyles?.bold} />,
20
+ p: ({ node, ...props }) => <p {...props} {...markdownStyles?.text} />,
21
+ hr: ({ node, ...props }) => <hr {...props} {...markdownStyles?.horizontalRule} />,
22
+ a: ({ node, ...props }) => <a {...props} {...markdownStyles?.link} />,
23
+ ol: ({ node, ordered, ...props }) => <ol {...props} {...markdownStyles?.orderedList} />,
24
+ ul: ({ node, ordered, ...props }) => <ul {...props} {...markdownStyles?.unorderedList} />,
25
+ li: ({ node, ordered, ...props }) => <li {...props} {...markdownStyles?.listItem} />,
26
+ br: ({ node, ...props }) => <br {...props} {...markdownStyles?.lineBreak} />,
27
+ img: () => null,
28
+ pre: () => null,
29
+ }
30
+
31
+ return <ReactMarkdown components={markdownComponents}>{text}</ReactMarkdown>
32
+ }
@@ -0,0 +1,11 @@
1
+ import { forwardRef } from 'react'
2
+ import { CommonBlockProps, VideoBlock } from '../../types'
3
+
4
+ type Props = CommonBlockProps & VideoBlock
5
+ export const Video = forwardRef<HTMLVideoElement, Props>(({ url, type, styles, ...props }, ref) => {
6
+ return (
7
+ <div>
8
+ <video {...props} controls src={url} {...styles?.[type]} ref={ref} />
9
+ </div>
10
+ )
11
+ })
@@ -0,0 +1,28 @@
1
+ import { Text } from './Text'
2
+ import { Button } from './Button'
3
+ import { Bubble } from './Bubble'
4
+ import { Renderers } from '../../contexts'
5
+ import { Image } from './Image'
6
+ import { Video } from './Video'
7
+ import { Audio } from './Audio'
8
+ import { Location } from './Location'
9
+ import { Column } from './Column'
10
+ import { Row } from './Row'
11
+ import { Carousel } from './Carousel'
12
+ import { Dropdown } from './Dropdown'
13
+ import { File } from './File'
14
+
15
+ export const renderers: Renderers = {
16
+ text: Text,
17
+ button: Button,
18
+ bubble: Bubble,
19
+ audio: Audio,
20
+ column: Column,
21
+ file: File,
22
+ image: Image,
23
+ location: Location,
24
+ row: Row,
25
+ video: Video,
26
+ carousel: Carousel,
27
+ dropdown: Dropdown,
28
+ }
@@ -0,0 +1,16 @@
1
+ import { createContext, useContext } from 'react'
2
+
3
+ type ComposerContextValue = {
4
+ value: string
5
+ setValue: (value: string) => void
6
+ }
7
+
8
+ export const ComposerContext = createContext<ComposerContextValue | null>(null)
9
+
10
+ export function useComposerContext() {
11
+ const context = useContext(ComposerContext)
12
+ if (!context) {
13
+ throw new Error('useComposerContext must be used within a Message')
14
+ }
15
+ return context
16
+ }
@@ -0,0 +1,16 @@
1
+ import { Dispatch, SetStateAction, createContext, useContext } from 'react'
2
+
3
+ type MessageContextVaue = {
4
+ isLoading: string[]
5
+ setIsLoading: Dispatch<SetStateAction<string[]>>
6
+ }
7
+
8
+ export const MessageContext = createContext<MessageContextVaue | null>(null)
9
+
10
+ export function useMessageContext() {
11
+ const context = useContext(MessageContext)
12
+ if (!context) {
13
+ throw new Error('useMessageContext must be used within a Message')
14
+ }
15
+ return context
16
+ }
@@ -0,0 +1,19 @@
1
+ import { ReactNode, createContext, useContext } from 'react'
2
+
3
+ type ModalContextVaue = {
4
+ open: boolean
5
+ showModal: (props: { title: string; content: ReactNode }) => void
6
+ hideModal: () => void
7
+ title: string
8
+ content: ReactNode | null
9
+ }
10
+
11
+ export const ModalContext = createContext<ModalContextVaue | null>(null)
12
+
13
+ export function useModalContext() {
14
+ const context = useContext(ModalContext)
15
+ if (!context) {
16
+ throw new Error('useModalContext must be used within a ModalProvider')
17
+ }
18
+ return context
19
+ }
@@ -0,0 +1,61 @@
1
+ import { CSSProperties, FC, ReactNode, createContext, useContext } from 'react'
2
+ import type { BlockObject, BlockStyles, MessageObject } from '../types'
3
+ import { EventEmitter, Events } from '../Utils'
4
+ import { Theme } from '../schemas'
5
+ import { type WebchatClient } from '../client'
6
+
7
+ export type StyleOptions = {
8
+ className?: string
9
+ style?: CSSProperties
10
+ }
11
+
12
+ type DescriptionItem = {
13
+ title: string
14
+ link?: string
15
+ }
16
+
17
+ export type Configuration = {
18
+ composerPlaceholder?: string
19
+ botName?: string
20
+ botAvatar?: string
21
+ botDescription?: string
22
+ website?: DescriptionItem
23
+ email?: DescriptionItem
24
+ phone?: DescriptionItem
25
+ privacyPolicy?: DescriptionItem
26
+ termsOfService?: DescriptionItem
27
+ }
28
+
29
+ export type Renderers<P extends BlockObject = BlockObject> = {
30
+ [T in P['type']]: FC<Extract<P, { type: T }> & { styles: BlockStyles }>
31
+ }
32
+
33
+ export type ModalProps = {
34
+ title: string | null
35
+ content: ReactNode | null
36
+ }
37
+
38
+ type ContextVaue = {
39
+ theme: Theme
40
+ renderers: Renderers
41
+ messages: MessageObject[]
42
+ eventEmitter: EventEmitter<Events>
43
+ configuration: Configuration
44
+ setConfiguration: React.Dispatch<React.SetStateAction<Configuration>>
45
+ state: {
46
+ disableComposer: boolean
47
+ }
48
+ client: { on: WebchatClient['on']; sendMessage: WebchatClient['sendMessage']; restartConversation: () => void }
49
+ setState: (state: Partial<ContextVaue['state']>) => void
50
+ setTheme: (styles: Partial<Theme>) => void // Temporary while I decide how to do the theme switching in dev mode
51
+ }
52
+
53
+ export const WebchatContext = createContext<ContextVaue | null>(null)
54
+
55
+ export function useWebchatContext() {
56
+ const context = useContext(WebchatContext)
57
+ if (!context) {
58
+ throw new Error('useWebchatContext must be used within a WebchatProvider')
59
+ }
60
+ return context
61
+ }
@@ -0,0 +1,4 @@
1
+ export * from './ComposerContext'
2
+ export * from './MessageContext'
3
+ export * from './ModalContext'
4
+ export * from './WebchatContext'
@@ -0,0 +1,3 @@
1
+ export * from './useImageSize'
2
+ export * from './useWebchatStore'
3
+ export * from './useRefresh.ts'
@@ -0,0 +1,30 @@
1
+ import { useState, useEffect } from 'react'
2
+ import { getImageSize } from '../services'
3
+ import { Dimensions, Options, UseImageSizeResult } from '../types'
4
+
5
+ export const useImageSize = (url: string, options?: Options): UseImageSizeResult => {
6
+ const [dimensions, setDimensions] = useState<Dimensions | null>(null)
7
+ const [loading, setLoading] = useState(false)
8
+ const [error, setError] = useState<string | null>(null)
9
+
10
+ useEffect(() => {
11
+ const fetch = async () => {
12
+ setLoading(true)
13
+ setDimensions(null)
14
+
15
+ try {
16
+ const { width, height } = await getImageSize(url, options)
17
+
18
+ setDimensions({ width, height })
19
+ } catch (error: unknown) {
20
+ setError((error as string).toString())
21
+ } finally {
22
+ setLoading(false)
23
+ }
24
+ }
25
+
26
+ fetch()
27
+ }, [url, options])
28
+
29
+ return [dimensions, { loading, error }]
30
+ }
@@ -0,0 +1,33 @@
1
+ import { useState } from 'react'
2
+ import { useAsync } from 'react-use'
3
+
4
+ export const defaultGetRefreshTime = (expiresAt: Date) => {
5
+ const now = new Date()
6
+ return expiresAt.getTime() - now.getTime()
7
+ }
8
+
9
+ /**
10
+ * Utility hooks to force a state fresh based on a date
11
+ * @example
12
+ * const [onRefresh, setDate] = useDateRefresh()
13
+ * useEffect(() => {}, [onRefresh])
14
+ * setDate(futureDate) // The refresh function will trigger when the date passes
15
+ * @returns value to observe for refreshing and a function that set the refresh date
16
+ */
17
+ export const useRefresh = (getRefreshTimeInMs = defaultGetRefreshTime): [number, (date: Date) => void] => {
18
+ const [onRefresh, setOnRefresh] = useState(0)
19
+ const [refreshDate, setRefreshDate] = useState<Date>()
20
+
21
+ const incrementRefresh = () => setOnRefresh((onRefresh) => onRefresh + 1)
22
+
23
+ useAsync(async () => {
24
+ if (refreshDate) {
25
+ const refreshTimeout = setTimeout(incrementRefresh, getRefreshTimeInMs(refreshDate))
26
+ return () => {
27
+ clearTimeout(refreshTimeout)
28
+ }
29
+ }
30
+ }, [refreshDate])
31
+
32
+ return [onRefresh, setRefreshDate]
33
+ }
@@ -0,0 +1,45 @@
1
+ import { UserCredentials } from '@botpress/messaging-socket'
2
+ import { create } from 'zustand'
3
+ import { devtools, persist } from 'zustand/middleware'
4
+
5
+ type WebchatState = {
6
+ messageHistory: {
7
+ [key: string]: string[]
8
+ }
9
+ user?: UserCredentials
10
+ conversationId?: string
11
+ addMessageToHistory: (props: { userId: string; message: string }) => void
12
+ setUser: (userId?: UserCredentials) => void
13
+ setConversationId: (conversationId?: string) => void
14
+ }
15
+
16
+ export const useWebchatStore = create<WebchatState>()(
17
+ devtools(
18
+ persist(
19
+ (set) => ({
20
+ messageHistory: {},
21
+ addMessageToHistory: ({ userId, message }) =>
22
+ set((state) => ({
23
+ ...state,
24
+ messageHistory: {
25
+ ...state.messageHistory,
26
+ [userId]: [message, ...(state.messageHistory[userId] ?? []).slice(0, 100)],
27
+ },
28
+ })),
29
+ setUser: (user) =>
30
+ set((state) => ({
31
+ ...state,
32
+ user,
33
+ })),
34
+ setConversationId: (conversationId) =>
35
+ set((state) => ({
36
+ ...state,
37
+ conversationId,
38
+ })),
39
+ }),
40
+ {
41
+ name: 'botpress-webchat',
42
+ }
43
+ )
44
+ )
45
+ )
package/src/index.css ADDED
@@ -0,0 +1,18 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ :root {
6
+
7
+ /* font-family: 'Fira Code', monospace;
8
+ font-family: 'IBM Plex Sans', sans-serif;
9
+ font-family: 'Rubik', sans-serif; */
10
+ font-family: 'Inter', sans-serif;
11
+
12
+
13
+ font-synthesis: none;
14
+ text-rendering: optimizeLegibility;
15
+ -webkit-font-smoothing: antialiased;
16
+ -moz-osx-font-smoothing: grayscale;
17
+ -webkit-text-size-adjust: 100%;
18
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './components'
2
+ export * from './providers'
3
+ export * from './types'