@seamly/web-ui 20.0.0-beta.2 → 20.0.0-beta.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.
- package/build/dist/lib/components.js +1 -1
- package/build/dist/lib/components.min.js +1 -1
- package/build/dist/lib/deprecated-view.css +1 -0
- package/build/dist/lib/deprecated-view.js +1 -0
- package/build/dist/lib/index.debug.js +95 -61
- package/build/dist/lib/index.debug.min.js +1 -1
- package/build/dist/lib/index.debug.min.js.LICENSE.txt +24 -12
- package/build/dist/lib/style-guide.js +305 -97
- package/build/dist/lib/style-guide.min.js +1 -1
- package/build/dist/lib/styles-default-implementation.css +1 -0
- package/build/dist/lib/styles-default-implementation.js +1 -0
- package/build/dist/lib/styles.css +1 -1
- package/package.json +8 -7
- package/src/.DS_Store +0 -0
- package/src/javascripts/api/index.js +19 -10
- package/src/javascripts/api/producer.js +5 -3
- package/src/javascripts/domains/translations/components/options-button.js +1 -1
- package/src/javascripts/index.js +2 -2
- package/src/javascripts/lib/engine/index.js +2 -1
- package/src/javascripts/lib/parse-body.js +1 -1
- package/src/javascripts/package/components.js +1 -1
- package/src/javascripts/style-guide/components/app.js +3 -3
- package/src/javascripts/style-guide/states.js +345 -69
- package/src/javascripts/style-guide/style-guide-engine.js +1 -0
- package/src/javascripts/ui/components/app-options/index.js +9 -3
- package/src/javascripts/ui/components/conversation/conversation.js +1 -1
- package/src/javascripts/ui/components/conversation/event/carousel-message/index.js +3 -1
- package/src/javascripts/ui/components/conversation/event/conversation-suggestions.js +12 -3
- package/src/javascripts/ui/components/conversation/event/hooks/use-text-rendering.js +35 -0
- package/src/javascripts/ui/components/conversation/event/participant.js +5 -2
- package/src/javascripts/ui/components/conversation/event/splash.js +2 -1
- package/src/javascripts/ui/components/conversation/event/text.js +2 -1
- package/src/javascripts/ui/components/entry/{toggle-button.js → deprecated-toggle-button.js} +0 -0
- package/src/javascripts/ui/components/entry/entry-container.js +1 -1
- package/src/javascripts/ui/components/faq/faq.js +162 -0
- package/src/javascripts/ui/components/layout/chat-frame.js +1 -1
- package/src/javascripts/ui/components/layout/chat.js +62 -0
- package/src/javascripts/ui/components/layout/{app-frame.js → deprecated-app-frame.js} +10 -24
- package/src/javascripts/ui/components/layout/header.js +1 -1
- package/src/javascripts/ui/components/layout/pre-chat-messages.js +2 -8
- package/src/javascripts/ui/components/options/options-button.js +2 -2
- package/src/javascripts/ui/components/suggestions/index.js +2 -3
- package/src/javascripts/ui/components/suggestions/suggestions-list.js +1 -1
- package/src/javascripts/ui/components/view/app-view.js +3 -3
- package/src/javascripts/ui/components/view/deprecated-view.js +6 -4
- package/src/javascripts/ui/components/view/index.js +61 -5
- package/src/javascripts/ui/components/view/inline-view.js +15 -4
- package/src/javascripts/ui/components/view/window-view/index.js +5 -5
- package/src/stylesheets/1-settings/_config.scss +6 -6
- package/src/stylesheets/{3-app/_app.scss → 3-chat/_chat.scss} +22 -35
- package/src/stylesheets/5-components/_chat-status.scss +1 -5
- package/src/stylesheets/5-components/_conversation.scss +3 -3
- package/src/stylesheets/5-components/_disclaimer.scss +2 -6
- package/src/stylesheets/5-components/_error.scss +20 -10
- package/src/stylesheets/5-components/_interrupt.scss +21 -2
- package/src/stylesheets/5-components/_message-body.scss +23 -1
- package/src/stylesheets/5-components/_message-count.scss +1 -0
- package/src/stylesheets/5-components/_message.scss +4 -0
- package/src/stylesheets/5-components/_modal.scss +2 -2
- package/src/stylesheets/5-components/_options.scss +6 -14
- package/src/stylesheets/5-components/_pre-chat-messages.scss +24 -17
- package/src/stylesheets/5-components/_suggestions.scss +6 -6
- package/src/stylesheets/5-components/_unstarted.scss +22 -36
- package/src/stylesheets/6-default-implementation/_hover.scss +153 -0
- package/src/stylesheets/{6-webui-only → 6-default-implementation}/_scrollbar.scss +1 -1
- package/src/stylesheets/7-deprecated/3-app/_app.scss +8 -8
- package/src/stylesheets/7-deprecated/5-components/_error.scss +19 -9
- package/src/stylesheets/7-deprecated/5-components/_input.scss +1 -1
- package/src/stylesheets/7-deprecated/5-components/_message.scss +1 -1
- package/src/stylesheets/7-deprecated/5-components/_modal.scss +1 -1
- package/src/stylesheets/7-deprecated/5-components/_options.scss +8 -10
- package/src/stylesheets/style-guide.scss +1 -1
- package/src/stylesheets/styles-default-implementation.scss +3 -0
- package/src/stylesheets/styles.scss +8 -9
- package/webpack/config.common.js +7 -1
- package/webpack/config.package.js +9 -1
- package/webpack/config.test.js +1 -0
- package/webpack/defaults.js +3 -6
- package/CHANGELOG.md +0 -625
- package/build/dist/lib/index.js +0 -25513
- package/build/dist/lib/index.min.js +0 -2
- package/build/dist/lib/index.min.js.LICENSE.txt +0 -9
- package/build/dist/lib/standalone.js +0 -34474
- package/build/dist/lib/standalone.min.js +0 -2
- package/build/dist/lib/standalone.min.js.LICENSE.txt +0 -4
- package/src/javascripts/ui/components/layout/modal-wrapper.js +0 -0
- package/src/stylesheets/6-webui-only/_hover.scss +0 -151
- package/src/stylesheets/styles-webui-only.scss +0 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback } from 'preact/hooks'
|
|
1
|
+
import { useCallback, useState } from 'preact/hooks'
|
|
2
2
|
import { className } from '../../../../lib/css'
|
|
3
3
|
import { actionTypes } from '../../../utils/seamly-utils'
|
|
4
4
|
import { useI18n } from '../../../../domains/i18n'
|
|
@@ -6,6 +6,7 @@ import { useTranslatedEventData } from '../../../../domains/translations'
|
|
|
6
6
|
import MessageContainer from '../message-container'
|
|
7
7
|
import { useSeamlyCommands } from '../../../hooks/seamly-hooks'
|
|
8
8
|
import SuggestionsList from '../../suggestions/suggestions-list'
|
|
9
|
+
import { useConfig } from '../../../../domains/config'
|
|
9
10
|
|
|
10
11
|
export const useSuggestions = (event) => {
|
|
11
12
|
const { payload } = event
|
|
@@ -18,6 +19,8 @@ export const useSuggestions = (event) => {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const ConversationSuggestions = ({ event, ...props }) => {
|
|
22
|
+
const { userResponded } = useConfig()
|
|
23
|
+
const [isExpanded, setIsExpanded] = useState(true)
|
|
21
24
|
const { t } = useI18n()
|
|
22
25
|
const headingText = t('suggestions.headingText')
|
|
23
26
|
const footerText = t('suggestions.footerText')
|
|
@@ -26,6 +29,8 @@ const ConversationSuggestions = ({ event, ...props }) => {
|
|
|
26
29
|
const handleClick = useCallback(
|
|
27
30
|
({ id, question }) => {
|
|
28
31
|
// @todo Refactor to 'suggestionclick'
|
|
32
|
+
setIsExpanded(false)
|
|
33
|
+
|
|
29
34
|
sendAction({
|
|
30
35
|
type: actionTypes.custom,
|
|
31
36
|
originMessage: payload.id,
|
|
@@ -40,13 +45,17 @@ const ConversationSuggestions = ({ event, ...props }) => {
|
|
|
40
45
|
|
|
41
46
|
addMessageBubble(question)
|
|
42
47
|
},
|
|
43
|
-
[payload, sendAction, addMessageBubble],
|
|
48
|
+
[payload, sendAction, addMessageBubble, setIsExpanded],
|
|
44
49
|
)
|
|
45
50
|
|
|
51
|
+
if (!isExpanded || userResponded) {
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
return (
|
|
47
56
|
<div className={className('suggestions', 'suggestions--conversation')}>
|
|
48
57
|
{headingText && (
|
|
49
|
-
<
|
|
58
|
+
<p className={className('suggestions__heading')}>{headingText}</p>
|
|
50
59
|
)}
|
|
51
60
|
<MessageContainer
|
|
52
61
|
type="suggestions"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import Mustache from 'mustache'
|
|
2
|
+
|
|
3
|
+
Mustache.escape = function (escapeText) {
|
|
4
|
+
return escapeText
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const parseLinkVariable = (variable) => {
|
|
8
|
+
return `<a href='${variable.url}' data-link-id='${variable.id}' ${
|
|
9
|
+
variable.newTab ? 'target="_blank"' : ''
|
|
10
|
+
}>${variable.name}</a>`
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function parseRichText(text, variables = {}) {
|
|
14
|
+
const view = {}
|
|
15
|
+
Object.entries(variables).forEach(([key, variable]) => {
|
|
16
|
+
switch (variable.type) {
|
|
17
|
+
case 'link':
|
|
18
|
+
view[key] = parseLinkVariable(variable)
|
|
19
|
+
break
|
|
20
|
+
case 'text':
|
|
21
|
+
view[key] = variable.value
|
|
22
|
+
break
|
|
23
|
+
}
|
|
24
|
+
}, {})
|
|
25
|
+
|
|
26
|
+
// Disable escaping as we'll be generating HTML
|
|
27
|
+
const oldEscape = Mustache.escape
|
|
28
|
+
Mustache.escape = function (escapeText) {
|
|
29
|
+
return escapeText
|
|
30
|
+
}
|
|
31
|
+
const output = Mustache.render(text, view)
|
|
32
|
+
Mustache.escape = oldEscape
|
|
33
|
+
|
|
34
|
+
return output
|
|
35
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useMemo } from 'preact/hooks'
|
|
2
|
+
import Mustache from 'mustache'
|
|
2
3
|
import parseBody from '../../../../lib/parse-body'
|
|
3
4
|
import EventDivider from '../event-divider'
|
|
4
5
|
import { useTranslatedEventData } from '../../../../domains/translations'
|
|
@@ -11,8 +12,10 @@ const Participant = ({ event }) => {
|
|
|
11
12
|
const [introduction] = useTranslatedEventData(event)
|
|
12
13
|
|
|
13
14
|
const intro = useMemo(() => {
|
|
14
|
-
return introduction
|
|
15
|
-
|
|
15
|
+
return introduction
|
|
16
|
+
? Mustache.render(parseBody(introduction), participant)
|
|
17
|
+
: undefined
|
|
18
|
+
}, [introduction, participant])
|
|
16
19
|
|
|
17
20
|
if (!intro) {
|
|
18
21
|
return null
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import parseBody from '../../../../lib/parse-body'
|
|
2
2
|
import useEventLinkClickHandler from './hooks/use-event-link-click-handler'
|
|
3
|
+
import { parseRichText } from './hooks/use-text-rendering'
|
|
3
4
|
import MessageContainer from '../message-container'
|
|
4
5
|
import { useTranslatedEventData } from '../../../../domains/translations'
|
|
5
6
|
|
|
@@ -16,7 +17,7 @@ const Splash = ({ event, ...props }) => {
|
|
|
16
17
|
{...props}
|
|
17
18
|
bodyProps={{
|
|
18
19
|
dangerouslySetInnerHTML: {
|
|
19
|
-
__html: parseBody(body.text),
|
|
20
|
+
__html: parseRichText(parseBody(body.text), body.variables),
|
|
20
21
|
},
|
|
21
22
|
}}
|
|
22
23
|
/>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'preact/hooks'
|
|
2
2
|
import parseBody from '../../../../lib/parse-body'
|
|
3
3
|
import useEventLinkClickHandler from './hooks/use-event-link-click-handler'
|
|
4
|
+
import { parseRichText } from './hooks/use-text-rendering'
|
|
4
5
|
import MessageContainer from '../message-container'
|
|
5
6
|
import { useTranslatedEventData } from '../../../../domains/translations'
|
|
6
7
|
|
|
@@ -13,7 +14,7 @@ const Text = ({ event, ...props }) => {
|
|
|
13
14
|
return {
|
|
14
15
|
bodyProps: {
|
|
15
16
|
dangerouslySetInnerHTML: {
|
|
16
|
-
__html: parseBody(body.text),
|
|
17
|
+
__html: parseRichText(parseBody(body.text), body.variables),
|
|
17
18
|
},
|
|
18
19
|
},
|
|
19
20
|
}
|
package/src/javascripts/ui/components/entry/{toggle-button.js → deprecated-toggle-button.js}
RENAMED
|
File without changes
|
|
@@ -79,7 +79,7 @@ const EntryContainer = () => {
|
|
|
79
79
|
const EntryComponent = entryComponents[renderEntry]
|
|
80
80
|
|
|
81
81
|
return (
|
|
82
|
-
<div className={className('
|
|
82
|
+
<div className={className('chat__entry')} ref={entryContainer}>
|
|
83
83
|
{showCountdown && <IdleDetachWarning />}
|
|
84
84
|
{showResumeConversationPrompt && <ResumeConversationPrompt />}
|
|
85
85
|
<div
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { useEffect, useRef, useMemo } from 'preact/hooks'
|
|
2
|
+
import { className } from '../../../lib/css'
|
|
3
|
+
import Icon from '../layout/icon'
|
|
4
|
+
import { actionTypes } from '../../utils/seamly-utils'
|
|
5
|
+
import { runIfElementContainsOrHasFocus } from '../../utils/general-utils'
|
|
6
|
+
import useSeamlyCommands from '../../hooks/use-seamly-commands'
|
|
7
|
+
import {
|
|
8
|
+
useSeamlyServiceData,
|
|
9
|
+
useSeamlyLayoutMode,
|
|
10
|
+
} from '../../hooks/seamly-state-hooks'
|
|
11
|
+
import { useGeneratedId } from '../../hooks/utility-hooks'
|
|
12
|
+
import { useSkiplinkTargetFocusing } from '../../hooks/focus-helper-hooks'
|
|
13
|
+
import { useLiveRegion } from '../../hooks/live-region-hooks'
|
|
14
|
+
import useSeamlyIdleDetachCountdown from '../../hooks/use-seamly-idle-detach-countdown'
|
|
15
|
+
import useSeamlyResumeConversationPrompt from '../../hooks/use-seamly-resume-conversation-prompt'
|
|
16
|
+
import { useI18n } from '../../../domains/i18n'
|
|
17
|
+
import InOutTransition, {
|
|
18
|
+
transitionStartStates,
|
|
19
|
+
} from '../widgets/in-out-transition'
|
|
20
|
+
import { useTranslatedEventData } from '../../../domains/translations'
|
|
21
|
+
import { useInterrupt } from '../../../domains/interrupt'
|
|
22
|
+
import { useUserHasResponded } from '../../../domains/app'
|
|
23
|
+
|
|
24
|
+
const Faq = () => {
|
|
25
|
+
const { t } = useI18n()
|
|
26
|
+
const { sendAction, addMessageBubble } = useSeamlyCommands()
|
|
27
|
+
const sectionId = useGeneratedId()
|
|
28
|
+
const focusSkiplinkTarget = useSkiplinkTargetFocusing()
|
|
29
|
+
const { sendPolite } = useLiveRegion()
|
|
30
|
+
const { hasInterrupt } = useInterrupt()
|
|
31
|
+
const { hasCountdown, endCountdown } = useSeamlyIdleDetachCountdown()
|
|
32
|
+
const { hasPrompt, continueChat } = useSeamlyResumeConversationPrompt()
|
|
33
|
+
|
|
34
|
+
const lastFaqEventPayload = useSeamlyServiceData('suggestion')
|
|
35
|
+
const [eventBody] = useTranslatedEventData({ payload: lastFaqEventPayload })
|
|
36
|
+
const faqs = useMemo(() => {
|
|
37
|
+
const newFaqs = lastFaqEventPayload && !hasInterrupt ? eventBody : []
|
|
38
|
+
const itemBaseClass = `faqs__item`
|
|
39
|
+
return newFaqs.map(({ categories = [], ...faqRest }) => ({
|
|
40
|
+
...faqRest,
|
|
41
|
+
categories,
|
|
42
|
+
classNames: [
|
|
43
|
+
itemBaseClass,
|
|
44
|
+
...categories.map(
|
|
45
|
+
(cat) =>
|
|
46
|
+
`faqs__item--${String(cat)
|
|
47
|
+
.toLowerCase()
|
|
48
|
+
.replace(/[^a-z0-9_\\-]/, '')}`,
|
|
49
|
+
),
|
|
50
|
+
],
|
|
51
|
+
}))
|
|
52
|
+
}, [lastFaqEventPayload, hasInterrupt, eventBody])
|
|
53
|
+
|
|
54
|
+
const prevFaqs = useRef(null)
|
|
55
|
+
const prevHasFaqs = useRef(false)
|
|
56
|
+
|
|
57
|
+
const { isInline } = useSeamlyLayoutMode()
|
|
58
|
+
const hasResponded = useUserHasResponded()
|
|
59
|
+
const hideForWindow = !isInline && hasResponded
|
|
60
|
+
const prevHideForWindow = useRef(hideForWindow)
|
|
61
|
+
|
|
62
|
+
const hasFaqs = !!faqs.length
|
|
63
|
+
const showFaqContainer = hasFaqs && !hideForWindow
|
|
64
|
+
const previousRenderedFaqList = useRef([])
|
|
65
|
+
const renderedFaqList = hasFaqs ? faqs : previousRenderedFaqList.current
|
|
66
|
+
previousRenderedFaqList.current = renderedFaqList
|
|
67
|
+
|
|
68
|
+
const containerRef = useRef(null)
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (prevFaqs.current !== faqs && !hideForWindow) {
|
|
72
|
+
if (hasFaqs) {
|
|
73
|
+
const politeText = prevHasFaqs.current
|
|
74
|
+
? t('faq.srUpdatedText')
|
|
75
|
+
: t('faq.srAvailableText')
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
sendPolite(politeText)
|
|
78
|
+
}, 30)
|
|
79
|
+
} else if (prevHasFaqs.current) {
|
|
80
|
+
sendPolite(t('faq.srUnavailableText'))
|
|
81
|
+
}
|
|
82
|
+
prevFaqs.current = faqs
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!prevHideForWindow.current && hideForWindow) {
|
|
86
|
+
runIfElementContainsOrHasFocus(containerRef.current, focusSkiplinkTarget)
|
|
87
|
+
sendPolite(t('faq.srUnavailableText'))
|
|
88
|
+
} else if (!hasFaqs && prevHasFaqs.current) {
|
|
89
|
+
runIfElementContainsOrHasFocus(containerRef.current, focusSkiplinkTarget)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
prevHasFaqs.current = hasFaqs
|
|
93
|
+
prevHideForWindow.current = hideForWindow
|
|
94
|
+
}, [hasFaqs, faqs, hideForWindow, focusSkiplinkTarget, sendPolite, t])
|
|
95
|
+
|
|
96
|
+
const onFaqClickHandler = ({ id, question }) => {
|
|
97
|
+
if (hasCountdown) {
|
|
98
|
+
endCountdown(true)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (hasPrompt) {
|
|
102
|
+
continueChat()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
sendAction({
|
|
106
|
+
type: actionTypes.custom,
|
|
107
|
+
originMessage: lastFaqEventPayload.id,
|
|
108
|
+
body: {
|
|
109
|
+
type: 'faqclick',
|
|
110
|
+
body: {
|
|
111
|
+
faqId: id,
|
|
112
|
+
faqQuestion: question,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
addMessageBubble(question)
|
|
118
|
+
focusSkiplinkTarget()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const headingText = t('faq.headingText')
|
|
122
|
+
const ContainerElement = headingText ? 'section' : 'div'
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<InOutTransition
|
|
126
|
+
isActive={showFaqContainer}
|
|
127
|
+
transitionStartState={transitionStartStates.notRendered}
|
|
128
|
+
>
|
|
129
|
+
<ContainerElement
|
|
130
|
+
className={className('faqs')}
|
|
131
|
+
aria-labelledby={headingText ? sectionId : null}
|
|
132
|
+
ref={containerRef}
|
|
133
|
+
>
|
|
134
|
+
{headingText && (
|
|
135
|
+
<h2 id={sectionId} className={className('faqs__heading')}>
|
|
136
|
+
{headingText}
|
|
137
|
+
</h2>
|
|
138
|
+
)}
|
|
139
|
+
{!!renderedFaqList.length && (
|
|
140
|
+
<ul className={className('faqs__list')}>
|
|
141
|
+
{renderedFaqList.map((faq) => (
|
|
142
|
+
<li key={faq.id.toString()} className={className(faq.classNames)}>
|
|
143
|
+
<button
|
|
144
|
+
type="button"
|
|
145
|
+
onClick={() => {
|
|
146
|
+
onFaqClickHandler(faq)
|
|
147
|
+
}}
|
|
148
|
+
className={className('button', 'button--secondary')}
|
|
149
|
+
>
|
|
150
|
+
<Icon name="chevronRight" size="8" />
|
|
151
|
+
{faq.question}
|
|
152
|
+
</button>
|
|
153
|
+
</li>
|
|
154
|
+
))}
|
|
155
|
+
</ul>
|
|
156
|
+
)}
|
|
157
|
+
</ContainerElement>
|
|
158
|
+
</InOutTransition>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export default Faq
|
|
@@ -21,7 +21,7 @@ function ChatFrame({ children, interruptComponent: InterruptComponent }) {
|
|
|
21
21
|
return (
|
|
22
22
|
<>
|
|
23
23
|
<TranslationsChatStatus />
|
|
24
|
-
<div className={className('
|
|
24
|
+
<div className={className('chat__container')}>{getContent()}</div>
|
|
25
25
|
<AppOptions />
|
|
26
26
|
</>
|
|
27
27
|
)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { className } from '../../../lib/css'
|
|
2
|
+
import {
|
|
3
|
+
useSeamlyAppContainerClassNames,
|
|
4
|
+
useSeamlyLayoutMode,
|
|
5
|
+
} from '../../hooks/seamly-hooks'
|
|
6
|
+
import { useConfig } from '../../../domains/config'
|
|
7
|
+
import { useUserHasResponded } from '../../../domains/app'
|
|
8
|
+
import { useI18n } from '../../../domains/i18n'
|
|
9
|
+
import { useVisibility, visibilityStates } from '../../../domains/visibility'
|
|
10
|
+
import Suggestions from '../suggestions'
|
|
11
|
+
|
|
12
|
+
const Chat = ({ children, className: givenClassName = '' }) => {
|
|
13
|
+
const { isOpen, isVisible, setVisibility } = useVisibility()
|
|
14
|
+
const { namespace, layoutMode } = useConfig()
|
|
15
|
+
const { isInline } = useSeamlyLayoutMode()
|
|
16
|
+
const appContainerClassNames = useSeamlyAppContainerClassNames()
|
|
17
|
+
const userResponded = useUserHasResponded()
|
|
18
|
+
const { t } = useI18n()
|
|
19
|
+
|
|
20
|
+
const defaultClassNames = [
|
|
21
|
+
'chat',
|
|
22
|
+
`chat--layout-${layoutMode}`,
|
|
23
|
+
`namespace--${namespace}`,
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
const classNames = [
|
|
27
|
+
...defaultClassNames,
|
|
28
|
+
...appContainerClassNames,
|
|
29
|
+
givenClassName,
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
if (!isOpen && layoutMode !== 'app') {
|
|
33
|
+
classNames.push('chat--collapsed')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (userResponded) {
|
|
37
|
+
classNames.push('chat--user-responded')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const onKeyDownHandler = (e) => {
|
|
41
|
+
if ((e.code && e.code === 'Escape') || e.keyCode === 27)
|
|
42
|
+
if (!isInline && isOpen) {
|
|
43
|
+
setVisibility(visibilityStates.minimized)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
isVisible && (
|
|
49
|
+
<section
|
|
50
|
+
className={className(classNames)}
|
|
51
|
+
onKeyDown={onKeyDownHandler}
|
|
52
|
+
tabIndex="-1"
|
|
53
|
+
aria-label={t('chat.srLabel')}
|
|
54
|
+
>
|
|
55
|
+
<div className={className('chat-wrapper')}>{children}</div>
|
|
56
|
+
{layoutMode === 'inline' && isOpen && <Suggestions isAside={true} />}
|
|
57
|
+
</section>
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default Chat
|
|
@@ -5,24 +5,20 @@ import {
|
|
|
5
5
|
useSeamlyLayoutMode,
|
|
6
6
|
useSeamlyContainerElement,
|
|
7
7
|
} from '../../hooks/seamly-hooks'
|
|
8
|
+
import Faq from '../faq/faq'
|
|
8
9
|
import { useConfig } from '../../../domains/config'
|
|
9
10
|
import { useUserHasResponded } from '../../../domains/app'
|
|
10
11
|
import { useI18n } from '../../../domains/i18n'
|
|
11
12
|
import { useVisibility, visibilityStates } from '../../../domains/visibility'
|
|
12
|
-
import Suggestions from '../suggestions'
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
children,
|
|
16
|
-
className: givenClassName = '',
|
|
17
|
-
isDeprecated,
|
|
18
|
-
}) => {
|
|
14
|
+
const DeprecatedAppFrame = ({ children }) => {
|
|
19
15
|
const [, setSeamlyContainerElement] = useSeamlyContainerElement()
|
|
20
16
|
const { isOpen, isVisible, setVisibility } = useVisibility()
|
|
21
|
-
const { zIndex,
|
|
17
|
+
const { zIndex, showFaq, layoutMode } = useConfig()
|
|
22
18
|
const { isInline } = useSeamlyLayoutMode()
|
|
23
19
|
const appContainerClassNames = useSeamlyAppContainerClassNames()
|
|
24
20
|
const userResponded = useUserHasResponded()
|
|
25
|
-
const { locale
|
|
21
|
+
const { locale } = useI18n()
|
|
26
22
|
|
|
27
23
|
const containerElementRef = useCallback(
|
|
28
24
|
(container) => {
|
|
@@ -43,20 +39,9 @@ const AppFrame = ({
|
|
|
43
39
|
return undefined
|
|
44
40
|
}, [locale])
|
|
45
41
|
|
|
46
|
-
const
|
|
47
|
-
const defaultClassNames = [
|
|
48
|
-
`app--layout-${layoutMode}`,
|
|
49
|
-
`namespace--${namespace}`,
|
|
50
|
-
]
|
|
42
|
+
const classNames = ['app', 'app--deprecated', ...appContainerClassNames]
|
|
51
43
|
|
|
52
|
-
|
|
53
|
-
baseClassName,
|
|
54
|
-
...defaultClassNames,
|
|
55
|
-
...appContainerClassNames,
|
|
56
|
-
givenClassName,
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
if (!isOpen && layoutMode !== 'app') {
|
|
44
|
+
if (!isOpen && layoutMode === 'window') {
|
|
60
45
|
classNames.push('app--collapsed')
|
|
61
46
|
}
|
|
62
47
|
|
|
@@ -64,6 +49,8 @@ const AppFrame = ({
|
|
|
64
49
|
classNames.push('app--user-responded')
|
|
65
50
|
}
|
|
66
51
|
|
|
52
|
+
classNames.push(`app--layout-${layoutMode}`)
|
|
53
|
+
|
|
67
54
|
const onKeyDownHandler = (e) => {
|
|
68
55
|
if ((e.code && e.code === 'Escape') || e.keyCode === 27)
|
|
69
56
|
if (!isInline && isOpen) {
|
|
@@ -81,13 +68,12 @@ const AppFrame = ({
|
|
|
81
68
|
ref={containerElementRef}
|
|
82
69
|
style={{ zIndex }}
|
|
83
70
|
data-nosnippet
|
|
84
|
-
aria-label={t('app.srLabel')}
|
|
85
71
|
>
|
|
86
72
|
<div className={className('app-wrapper')}>{children}</div>
|
|
87
|
-
{
|
|
73
|
+
{showFaq && <Faq />}
|
|
88
74
|
</section>
|
|
89
75
|
)
|
|
90
76
|
)
|
|
91
77
|
}
|
|
92
78
|
|
|
93
|
-
export default
|
|
79
|
+
export default DeprecatedAppFrame
|
|
@@ -10,7 +10,7 @@ const Header = ({ children, onCloseChat }) => {
|
|
|
10
10
|
|
|
11
11
|
const { t } = useI18n()
|
|
12
12
|
return (
|
|
13
|
-
<header className={className('
|
|
13
|
+
<header className={className('chat__header')}>
|
|
14
14
|
{children}
|
|
15
15
|
<div className={className('header-controls')}>
|
|
16
16
|
<button
|
|
@@ -3,9 +3,6 @@ import { useInterrupt } from '../../../domains/interrupt'
|
|
|
3
3
|
import { useConfig } from '../../../domains/config'
|
|
4
4
|
import useEventComponentMapping from '../../hooks/use-event-component-mapping'
|
|
5
5
|
import { useVisibility } from '../../../domains/visibility'
|
|
6
|
-
import InOutTransition, {
|
|
7
|
-
transitionStartStates,
|
|
8
|
-
} from '../widgets/in-out-transition'
|
|
9
6
|
|
|
10
7
|
export default function PreChatMessages() {
|
|
11
8
|
const { preChatEvents, layoutMode } = useConfig()
|
|
@@ -15,10 +12,7 @@ export default function PreChatMessages() {
|
|
|
15
12
|
const isVisible = !(hasInterrupt || !preChatEvents.length || isOpen)
|
|
16
13
|
|
|
17
14
|
return (
|
|
18
|
-
|
|
19
|
-
isActive={isVisible}
|
|
20
|
-
transitionStartState={transitionStartStates.notRendered}
|
|
21
|
-
>
|
|
15
|
+
isVisible && (
|
|
22
16
|
<ul
|
|
23
17
|
className={className(
|
|
24
18
|
'pre-chat-messages',
|
|
@@ -35,7 +29,7 @@ export default function PreChatMessages() {
|
|
|
35
29
|
</li>
|
|
36
30
|
))}
|
|
37
31
|
</ul>
|
|
38
|
-
|
|
32
|
+
)
|
|
39
33
|
)
|
|
40
34
|
}
|
|
41
35
|
|
|
@@ -223,9 +223,9 @@ const OptionsButton = () => {
|
|
|
223
223
|
className={className([
|
|
224
224
|
'button',
|
|
225
225
|
'button--secondary',
|
|
226
|
-
'
|
|
226
|
+
'chat__options__button',
|
|
227
227
|
...(!multiMenu && firstOptionName
|
|
228
|
-
? [`
|
|
228
|
+
? [`chat__options__button--${firstOptionName}`]
|
|
229
229
|
: []),
|
|
230
230
|
...(!multiMenu && !firstOption.available ? ['button--disabled'] : []),
|
|
231
231
|
])}
|
|
@@ -152,13 +152,12 @@ const Suggestions = ({ isAside }) => {
|
|
|
152
152
|
ref={containerRef}
|
|
153
153
|
>
|
|
154
154
|
{headingText && (
|
|
155
|
-
<
|
|
155
|
+
<p id={sectionId} className={className('suggestions__heading')}>
|
|
156
156
|
{headingText}
|
|
157
|
-
</
|
|
157
|
+
</p>
|
|
158
158
|
)}
|
|
159
159
|
{!!renderedSuggestions.length && (
|
|
160
160
|
<SuggestionsList
|
|
161
|
-
hasIcon={true}
|
|
162
161
|
suggestions={renderedSuggestions}
|
|
163
162
|
onClickSuggestion={handleClick}
|
|
164
163
|
/>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Chat from '../layout/chat'
|
|
2
2
|
import Interrupt from '../layout/interrupt'
|
|
3
3
|
import Conversation from '../conversation/conversation'
|
|
4
4
|
import EntryContainer from '../entry/entry-container'
|
|
@@ -9,12 +9,12 @@ const AppView = () => {
|
|
|
9
9
|
useSeamlyChat()
|
|
10
10
|
|
|
11
11
|
return (
|
|
12
|
-
<
|
|
12
|
+
<Chat>
|
|
13
13
|
<ChatFrame interruptComponent={Interrupt}>
|
|
14
14
|
<Conversation />
|
|
15
15
|
<EntryContainer />
|
|
16
16
|
</ChatFrame>
|
|
17
|
-
</
|
|
17
|
+
</Chat>
|
|
18
18
|
)
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import DeprecatedAppFrame from '../layout/deprecated-app-frame'
|
|
2
2
|
import ChatFrame from '../layout/chat-frame'
|
|
3
3
|
import AgentInfo from '../layout/agent-info'
|
|
4
4
|
import Header from '../layout/header'
|
|
@@ -7,14 +7,16 @@ import EntryContainer from '../entry/entry-container'
|
|
|
7
7
|
import Interrupt from '../layout/interrupt'
|
|
8
8
|
import { useSeamlyChat } from '../../hooks/seamly-hooks'
|
|
9
9
|
import { useVisibility } from '../../../domains/visibility'
|
|
10
|
+
import DeprecatedToggleButton from '../entry/deprecated-toggle-button'
|
|
10
11
|
|
|
11
12
|
const DeprecatedView = () => {
|
|
12
13
|
const { isVisible } = useVisibility()
|
|
13
|
-
const { closeChat } = useSeamlyChat()
|
|
14
|
+
const { openChat, closeChat } = useSeamlyChat()
|
|
14
15
|
|
|
15
16
|
return (
|
|
16
17
|
isVisible && (
|
|
17
|
-
<
|
|
18
|
+
<DeprecatedAppFrame>
|
|
19
|
+
<DeprecatedToggleButton onOpenChat={openChat} />
|
|
18
20
|
<Header onCloseChat={closeChat}>
|
|
19
21
|
<AgentInfo />
|
|
20
22
|
</Header>
|
|
@@ -22,7 +24,7 @@ const DeprecatedView = () => {
|
|
|
22
24
|
<Conversation />
|
|
23
25
|
<EntryContainer />
|
|
24
26
|
</ChatFrame>
|
|
25
|
-
</
|
|
27
|
+
</DeprecatedAppFrame>
|
|
26
28
|
)
|
|
27
29
|
)
|
|
28
30
|
}
|