@seamly/web-ui 18.1.1 → 18.3.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/build/dist/lib/index.debug.js +286 -99
- package/build/dist/lib/index.debug.min.js +1 -1
- package/build/dist/lib/index.debug.min.js.LICENSE.txt +84 -16
- package/build/dist/lib/index.js +4104 -3887
- package/build/dist/lib/index.min.js +1 -1
- package/build/dist/lib/standalone.js +4351 -4084
- package/build/dist/lib/standalone.min.js +1 -1
- package/build/dist/lib/style-guide.js +746 -641
- package/build/dist/lib/style-guide.min.js +1 -1
- package/build/dist/lib/styles.css +1 -1
- package/build/dist/lib/utils.js +0 -1
- package/build/dist/lib/utils.min.js +1 -1
- package/build/dist/translations/de-informal.js +0 -1
- package/build/dist/translations/de-informal.min.js +1 -1
- package/build/dist/translations/en.js +0 -1
- package/build/dist/translations/en.min.js +1 -1
- package/build/dist/translations/es-informal.js +0 -1
- package/build/dist/translations/es-informal.min.js +1 -1
- package/build/dist/translations/nl-formal.js +0 -1
- package/build/dist/translations/nl-formal.min.js +1 -1
- package/build/dist/translations/nl-informal.js +0 -1
- package/build/dist/translations/nl-informal.min.js +1 -1
- package/package.json +4 -2
- package/src/javascripts/api/index.js +9 -9
- package/src/javascripts/api/producer.js +8 -8
- package/src/javascripts/config.js +9 -11
- package/src/javascripts/domains/app/actions.js +25 -0
- package/src/javascripts/domains/app/index.js +3 -0
- package/src/javascripts/domains/config/actions.js +6 -0
- package/src/javascripts/domains/config/hooks.js +6 -0
- package/src/javascripts/domains/config/index.js +8 -0
- package/src/javascripts/domains/config/middleware.js +26 -0
- package/src/javascripts/domains/config/reducer.js +74 -0
- package/src/javascripts/domains/config/selectors.js +23 -0
- package/src/javascripts/domains/forms/actions.js +1 -1
- package/src/javascripts/domains/forms/hooks.js +10 -14
- package/src/javascripts/domains/forms/provider.js +4 -6
- package/src/javascripts/domains/forms/selectors.js +3 -3
- package/src/javascripts/domains/i18n/index.js +9 -5
- package/src/javascripts/domains/interrupt/actions.js +6 -0
- package/src/javascripts/domains/interrupt/hooks.js +29 -0
- package/src/javascripts/domains/interrupt/index.js +9 -0
- package/src/javascripts/domains/interrupt/middleware.js +30 -0
- package/src/javascripts/domains/interrupt/reducer.js +22 -0
- package/src/javascripts/domains/interrupt/selectors.js +5 -0
- package/src/javascripts/domains/options/index.js +1 -0
- package/src/javascripts/domains/options/middleware.js +35 -0
- package/src/javascripts/domains/redux/create-redux-store.js +14 -6
- package/src/javascripts/domains/redux/hooks.js +2 -2
- package/src/javascripts/domains/redux/index.js +2 -1
- package/src/javascripts/domains/redux/provider.js +5 -0
- package/src/javascripts/domains/store/index.js +38 -0
- package/src/javascripts/{ui → domains}/store/state-reducer.js +4 -7
- package/src/javascripts/domains/translations/actions.js +3 -3
- package/src/javascripts/domains/translations/components/chat-status.js +6 -12
- package/src/javascripts/domains/translations/components/options-button.js +3 -3
- package/src/javascripts/domains/translations/components/options-dialog/form.js +2 -2
- package/src/javascripts/domains/translations/components/options-dialog/index.js +2 -5
- package/src/javascripts/domains/translations/hooks.js +1 -1
- package/src/javascripts/domains/translations/reducer.js +2 -2
- package/src/javascripts/domains/translations/selectors.js +2 -2
- package/src/javascripts/index.js +17 -5
- package/src/javascripts/lib/css.js +5 -5
- package/src/javascripts/lib/engine/index.js +38 -11
- package/src/javascripts/lib/external-api/index.js +6 -6
- package/src/javascripts/lib/i18n.js +2 -2
- package/src/javascripts/lib/parse-body.js +1 -1
- package/src/javascripts/lib/redux-helpers/index.js +18 -4
- package/src/javascripts/lib/split-url-params.js +2 -2
- package/src/javascripts/lib/store/providers/app-storage.js +1 -1
- package/src/javascripts/lib/store/providers/cookie-storage.js +1 -1
- package/src/javascripts/package/utils.js +0 -1
- package/src/javascripts/style-guide/components/app.js +5 -12
- package/src/javascripts/style-guide/components/links.js +6 -6
- package/src/javascripts/style-guide/components/static-core.js +23 -7
- package/src/javascripts/style-guide/state-helpers/index.js +1 -1
- package/src/javascripts/style-guide/states.js +27 -67
- package/src/javascripts/style-guide/style-guide-engine.js +1 -1
- package/src/javascripts/ui/components/chat-app.js +2 -2
- package/src/javascripts/ui/components/conversation/component-filter.js +2 -2
- package/src/javascripts/ui/components/conversation/conversation.js +2 -2
- package/src/javascripts/ui/components/conversation/event/card-component.js +29 -4
- package/src/javascripts/ui/components/conversation/event/carousel-component/components/pagination.js +2 -2
- package/src/javascripts/ui/components/conversation/event/carousel-component/index.js +4 -3
- package/src/javascripts/ui/components/conversation/event/carousel-message/components/slide.js +2 -1
- package/src/javascripts/ui/components/conversation/event/carousel-message/index.js +2 -2
- package/src/javascripts/ui/components/conversation/event/choice-prompt.js +11 -6
- package/src/javascripts/ui/components/conversation/event/cta.js +1 -6
- package/src/javascripts/ui/components/conversation/event/divider/variants/new-translation.js +1 -1
- package/src/javascripts/ui/components/conversation/event/event-participant.js +3 -5
- package/src/javascripts/ui/components/conversation/event/hooks/use-event-link-click-handler.js +2 -2
- package/src/javascripts/ui/components/conversation/event/hooks/use-formatted-date.js +3 -3
- package/src/javascripts/ui/components/conversation/event/hooks/use-text-rendering.js +3 -3
- package/src/javascripts/ui/components/conversation/event/participant.js +2 -2
- package/src/javascripts/ui/components/conversation/event/upload.js +12 -27
- package/src/javascripts/ui/components/conversation/message-container.js +4 -6
- package/src/javascripts/ui/components/core/seamly-activity-monitor.js +4 -5
- package/src/javascripts/ui/components/core/seamly-core.js +6 -7
- package/src/javascripts/ui/components/core/seamly-event-subscriber.js +16 -17
- package/src/javascripts/ui/components/core/seamly-file-upload.js +5 -6
- package/src/javascripts/ui/components/core/seamly-idle-detach-counter.js +2 -6
- package/src/javascripts/ui/components/core/seamly-initializer.js +7 -60
- package/src/javascripts/ui/components/core/seamly-instance-functions-loader.js +10 -10
- package/src/javascripts/ui/components/core/seamly-live-region.js +1 -1
- package/src/javascripts/ui/components/core/seamly-new-notifications.js +1 -1
- package/src/javascripts/ui/components/core/seamly-read-state.js +2 -2
- package/src/javascripts/ui/components/entry/entry-container.js +7 -10
- package/src/javascripts/ui/components/entry/toggle-button.js +24 -10
- package/src/javascripts/ui/components/entry/upload/index.js +4 -11
- package/src/javascripts/ui/components/faq/faq.js +4 -4
- package/src/javascripts/ui/components/form-controls/error.js +22 -0
- package/src/javascripts/ui/components/form-controls/file-input.js +3 -9
- package/src/javascripts/ui/components/form-controls/select.js +1 -1
- package/src/javascripts/ui/components/form-controls/wrapper.js +2 -9
- package/src/javascripts/ui/components/layout/agent-info.js +4 -4
- package/src/javascripts/ui/components/layout/app-frame.js +5 -5
- package/src/javascripts/ui/components/layout/chat-frame.js +3 -5
- package/src/javascripts/ui/components/layout/header.js +4 -18
- package/src/javascripts/ui/components/layout/privacy-disclaimer.js +2 -2
- package/src/javascripts/ui/components/options/cobrowsing.js +3 -7
- package/src/javascripts/ui/components/options/options-button.js +9 -13
- package/src/javascripts/ui/components/options/options-frame.js +1 -1
- package/src/javascripts/ui/components/options/transcript/index.js +2 -2
- package/src/javascripts/ui/components/options/transcript/transcript-form.js +1 -1
- package/src/javascripts/ui/components/warnings/cobrowsing-active-frame.js +3 -6
- package/src/javascripts/ui/components/warnings/idle-detach-warning.js +2 -6
- package/src/javascripts/ui/components/widgets/in-out-transition.js +2 -2
- package/src/javascripts/ui/components/widgets/lightbox.js +4 -4
- package/src/javascripts/ui/components/widgets/modal.js +3 -3
- package/src/javascripts/ui/components/widgets/upload-progress.js +2 -13
- package/src/javascripts/ui/hooks/component-helper-hooks.js +4 -15
- package/src/javascripts/ui/hooks/file-upload-hooks.js +3 -3
- package/src/javascripts/ui/hooks/focus-helper-hooks.js +4 -4
- package/src/javascripts/ui/hooks/live-region-hooks.js +2 -2
- package/src/javascripts/ui/hooks/seamly-entry-hooks.js +7 -6
- package/src/javascripts/ui/hooks/seamly-hooks.js +3 -9
- package/src/javascripts/ui/hooks/seamly-option-hooks.js +4 -4
- package/src/javascripts/ui/hooks/seamly-state-hooks.js +8 -16
- package/src/javascripts/ui/hooks/use-event-component-mapping.js +1 -1
- package/src/javascripts/ui/hooks/use-seamly-chat.js +1 -0
- package/src/javascripts/ui/hooks/use-seamly-commands.js +27 -49
- package/src/javascripts/ui/hooks/use-seamly-idle-detach-countdown.js +3 -3
- package/src/javascripts/ui/hooks/use-seamly-stored-visibility.js +3 -3
- package/src/javascripts/ui/hooks/use-seamly-visibility.js +3 -3
- package/src/javascripts/ui/hooks/utility-hooks.js +2 -2
- package/src/javascripts/ui/utils/form-utils.js +3 -3
- package/src/javascripts/ui/utils/general-utils.js +17 -11
- package/src/javascripts/ui/utils/seamly-utils.js +15 -83
- package/src/javascripts/ui/utils/validations.js +10 -7
- package/src/stylesheets/5-components/_card.scss +0 -1
- package/src/stylesheets/5-components/_choice-prompt.scss +5 -0
- package/src/stylesheets/5-components/_message.scss +10 -0
- package/src/stylesheets/5-components/_options.scss +3 -2
- package/translations/de-informal.js +0 -2
- package/translations/en.js +0 -2
- package/translations/es-informal.js +0 -2
- package/translations/nl-formal.js +0 -2
- package/translations/nl-informal.js +0 -2
- package/webpack/config.common.js +3 -3
- package/webpack/config.package.js +4 -4
- package/webpack/config.site.js +8 -6
- package/CHANGELOG.md +0 -551
- package/src/javascripts/ui/components/core/seamly-api.js +0 -44
- package/src/javascripts/ui/hooks/use-seamly-interrupt.js +0 -62
- package/src/javascripts/ui/store/index.js +0 -37
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { useContext, useEffect, useRef } from 'preact/hooks'
|
|
2
2
|
import {
|
|
3
|
-
useSeamlyConfig,
|
|
4
3
|
useSeamlyCommands,
|
|
5
4
|
useSeamlyVisibility,
|
|
6
5
|
useSeamlyUnreadCount,
|
|
7
6
|
useSeamlyLayoutMode,
|
|
8
|
-
useSeamlyInterrupt,
|
|
9
7
|
useSeamlyConversationUrl,
|
|
10
8
|
useSeamlyActivityEventHandler,
|
|
11
9
|
useSeamlyApiContext,
|
|
@@ -13,6 +11,8 @@ import {
|
|
|
13
11
|
import { visibilityStates } from '../../utils/seamly-utils'
|
|
14
12
|
import { SeamlyEventBusContext } from './seamly-api-context'
|
|
15
13
|
import { useTranslations } from '../../../domains/translations'
|
|
14
|
+
import { useInterrupt } from '../../../domains/interrupt'
|
|
15
|
+
import { useConfig } from '../../../domains/config'
|
|
16
16
|
|
|
17
17
|
function useSeamlyInstanceFunction(functionName, fn, deps = []) {
|
|
18
18
|
const eventBus = useContext(SeamlyEventBusContext)
|
|
@@ -32,7 +32,7 @@ function useSeamlyInstanceFunction(functionName, fn, deps = []) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const SeamlyInstanceFunctionsLoader = () => {
|
|
35
|
-
const config =
|
|
35
|
+
const config = useConfig()
|
|
36
36
|
const { sendMessage, sendContext, sendAction } = useSeamlyCommands()
|
|
37
37
|
const { setVisibility, visible } = useSeamlyVisibility()
|
|
38
38
|
const currentVisibility = useRef(visible)
|
|
@@ -42,7 +42,7 @@ const SeamlyInstanceFunctionsLoader = () => {
|
|
|
42
42
|
const previousUnreadCount = useRef(null)
|
|
43
43
|
const previousVisibilityState = useRef(null)
|
|
44
44
|
const { isInline, isResolving } = useSeamlyLayoutMode()
|
|
45
|
-
const { hasInterrupt } =
|
|
45
|
+
const { hasInterrupt } = useInterrupt()
|
|
46
46
|
const currentConversationUrl = useSeamlyConversationUrl()
|
|
47
47
|
const prevConversationUrl = useRef(null)
|
|
48
48
|
const onActivityHandler = useSeamlyActivityEventHandler()
|
|
@@ -54,26 +54,26 @@ const SeamlyInstanceFunctionsLoader = () => {
|
|
|
54
54
|
|
|
55
55
|
useSeamlyInstanceFunction(
|
|
56
56
|
'askText',
|
|
57
|
-
text => {
|
|
57
|
+
(text) => {
|
|
58
58
|
sendMessage({ body: text })
|
|
59
59
|
},
|
|
60
60
|
[api?.send],
|
|
61
61
|
)
|
|
62
62
|
useSeamlyInstanceFunction(
|
|
63
63
|
'setLocale',
|
|
64
|
-
locale => {
|
|
64
|
+
(locale) => {
|
|
65
65
|
sendContext({ locale })
|
|
66
66
|
},
|
|
67
67
|
[api?.send],
|
|
68
68
|
)
|
|
69
69
|
useSeamlyInstanceFunction(
|
|
70
70
|
'setVariables',
|
|
71
|
-
variables => {
|
|
71
|
+
(variables) => {
|
|
72
72
|
sendContext({ variables })
|
|
73
73
|
},
|
|
74
74
|
[api?.send],
|
|
75
75
|
)
|
|
76
|
-
useSeamlyInstanceFunction('getVisibility', callback => {
|
|
76
|
+
useSeamlyInstanceFunction('getVisibility', (callback) => {
|
|
77
77
|
if (callback) {
|
|
78
78
|
callback(currentVisibility.current)
|
|
79
79
|
} else {
|
|
@@ -84,7 +84,7 @@ const SeamlyInstanceFunctionsLoader = () => {
|
|
|
84
84
|
})
|
|
85
85
|
useSeamlyInstanceFunction(
|
|
86
86
|
'setVisibility',
|
|
87
|
-
visibilityState => {
|
|
87
|
+
(visibilityState) => {
|
|
88
88
|
if (!Object.values(visibilityStates).includes(visibilityState)) {
|
|
89
89
|
console.error(
|
|
90
90
|
'Requested visibility states should be "open", "minimized" ,"hidden" or null.',
|
|
@@ -102,7 +102,7 @@ const SeamlyInstanceFunctionsLoader = () => {
|
|
|
102
102
|
setVisibility(visibilityState)
|
|
103
103
|
}
|
|
104
104
|
},
|
|
105
|
-
[api
|
|
105
|
+
[config?.api],
|
|
106
106
|
)
|
|
107
107
|
useSeamlyInstanceFunction(
|
|
108
108
|
'sendCustomAction',
|
|
@@ -21,7 +21,7 @@ const SeamlyNewNotifications = () => {
|
|
|
21
21
|
const debounceFunc = useRef(null)
|
|
22
22
|
|
|
23
23
|
const notifyUnread = useCallback(
|
|
24
|
-
debounce(eventArray => {
|
|
24
|
+
debounce((eventArray) => {
|
|
25
25
|
const serverEventCount = eventArray.filter(
|
|
26
26
|
({ payload }) => !payload.fromClient && !payload.fromHistory,
|
|
27
27
|
).length
|
|
@@ -40,13 +40,13 @@ const SeamlyReadState = () => {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const unread = events
|
|
43
|
-
.filter(event => {
|
|
43
|
+
.filter((event) => {
|
|
44
44
|
return (
|
|
45
45
|
isUnreadMessage(event) &&
|
|
46
46
|
event.payload.messageStatus === readStates.received
|
|
47
47
|
)
|
|
48
48
|
})
|
|
49
|
-
.map(event => event.payload.id)
|
|
49
|
+
.map((event) => event.payload.id)
|
|
50
50
|
|
|
51
51
|
if (unread.length > 0) {
|
|
52
52
|
dispatch({ type: SET_EVENTS_READ, ids: unread })
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
useSeamlyResumeConversationPrompt,
|
|
6
6
|
useSkiplinkTargetFocusing,
|
|
7
7
|
useSeamlyEntry,
|
|
8
|
-
useSeamlyConfig,
|
|
9
8
|
useFileUploadMeta,
|
|
10
9
|
} from '../../hooks/seamly-hooks'
|
|
11
10
|
import { entryTypes } from '../../utils/seamly-utils'
|
|
@@ -15,18 +14,16 @@ import ResumeConversationPrompt from '../warnings/resume-conversation-prompt'
|
|
|
15
14
|
import TextEntry from './text-entry'
|
|
16
15
|
import UploadToggle from './upload-toggle'
|
|
17
16
|
import Upload from './upload'
|
|
17
|
+
import { useConfig } from '../../../domains/config'
|
|
18
18
|
|
|
19
19
|
const EntryContainer = () => {
|
|
20
20
|
const entryContainer = useRef(null)
|
|
21
21
|
const { hasCountdown } = useSeamlyIdleDetachCountdown()
|
|
22
22
|
const [showCountdown, setShowCountDown] = useState(hasCountdown)
|
|
23
|
-
const {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
showResumeConversationPrompt,
|
|
28
|
-
setShowResumeConversationPrompt,
|
|
29
|
-
] = useState(hasResumeConversationPrompt)
|
|
23
|
+
const { hasPrompt: hasResumeConversationPrompt } =
|
|
24
|
+
useSeamlyResumeConversationPrompt()
|
|
25
|
+
const [showResumeConversationPrompt, setShowResumeConversationPrompt] =
|
|
26
|
+
useState(hasResumeConversationPrompt)
|
|
30
27
|
const focusSkiplinkTarget = useSkiplinkTargetFocusing()
|
|
31
28
|
const containedFocus = useRef(false)
|
|
32
29
|
const { activeEntry } = useSeamlyEntry()
|
|
@@ -35,7 +32,7 @@ const EntryContainer = () => {
|
|
|
35
32
|
upload: Upload,
|
|
36
33
|
})
|
|
37
34
|
const [renderEntry, setRenderEntry] = useState(() => activeEntry)
|
|
38
|
-
const config =
|
|
35
|
+
const config = useConfig()
|
|
39
36
|
const { accountAllowsUploads } = useFileUploadMeta()
|
|
40
37
|
|
|
41
38
|
const focusFn = useCallback(() => {
|
|
@@ -49,7 +46,7 @@ const EntryContainer = () => {
|
|
|
49
46
|
const { entry } = customComponents || {}
|
|
50
47
|
|
|
51
48
|
if (entry) {
|
|
52
|
-
setEntryComponents(c => ({
|
|
49
|
+
setEntryComponents((c) => ({
|
|
53
50
|
...c,
|
|
54
51
|
...entry,
|
|
55
52
|
}))
|
|
@@ -7,9 +7,10 @@ import {
|
|
|
7
7
|
useFocusIfSeamlyContainedFocus,
|
|
8
8
|
useSeamlyCurrentAgent,
|
|
9
9
|
useSeamlyHeaderData,
|
|
10
|
-
useSeamlyInterrupt,
|
|
11
10
|
useGeneratedId,
|
|
11
|
+
useSeamlyStateContext,
|
|
12
12
|
} from '../../hooks/seamly-hooks'
|
|
13
|
+
import { useInterrupt } from '../../../domains/interrupt'
|
|
13
14
|
|
|
14
15
|
const ToggleButton = ({ onOpenChat }) => {
|
|
15
16
|
const { t } = useI18n()
|
|
@@ -17,11 +18,13 @@ const ToggleButton = ({ onOpenChat }) => {
|
|
|
17
18
|
const { isOpen } = useSeamlyVisibility()
|
|
18
19
|
const prevIsOpen = useRef(null)
|
|
19
20
|
const buttonRef = useRef(null)
|
|
21
|
+
const lastEventRef = useRef()
|
|
20
22
|
const focusSkiplinkTarget = useSkiplinkTargetFocusing()
|
|
21
23
|
const focusIfContained = useFocusIfSeamlyContainedFocus()
|
|
22
24
|
const currentAgent = useSeamlyCurrentAgent()
|
|
23
25
|
const agentSubtitle = useSeamlyHeaderData().subTitle
|
|
24
|
-
const { hasInterrupt } =
|
|
26
|
+
const { hasInterrupt } = useInterrupt()
|
|
27
|
+
const { headerCollapseButtonId } = useSeamlyStateContext()
|
|
25
28
|
|
|
26
29
|
const showAgentInfo = currentAgent && !hasInterrupt
|
|
27
30
|
|
|
@@ -35,13 +38,23 @@ const ToggleButton = ({ onOpenChat }) => {
|
|
|
35
38
|
prevIsOpen.current = isOpen
|
|
36
39
|
}, [isOpen, focusIfContained])
|
|
37
40
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const handleMouseUp = () => {
|
|
42
|
+
lastEventRef.current = 'mouse'
|
|
43
|
+
}
|
|
44
|
+
const handleKeyUp = () => {
|
|
45
|
+
lastEventRef.current = 'key'
|
|
46
|
+
}
|
|
47
|
+
const handleClick = () => {
|
|
48
|
+
onOpenChat()
|
|
49
|
+
if (lastEventRef.current === 'mouse') {
|
|
50
|
+
// Sets focus on the input when opening through mouse interaction.
|
|
51
|
+
// This avoids focus hijacking for keyboard users.
|
|
52
|
+
// TODO: function is executed before the component is rendered, needs to be fixed.
|
|
53
|
+
focusSkiplinkTarget()
|
|
54
|
+
} else if (lastEventRef.current === 'key') {
|
|
55
|
+
focusIfContained(headerCollapseButtonId)
|
|
56
|
+
}
|
|
43
57
|
}
|
|
44
|
-
|
|
45
58
|
return (
|
|
46
59
|
<div className={className('toggle-button')}>
|
|
47
60
|
<div id={titleId}>
|
|
@@ -59,9 +72,10 @@ const ToggleButton = ({ onOpenChat }) => {
|
|
|
59
72
|
type="button"
|
|
60
73
|
aria-labelledby={titleId}
|
|
61
74
|
className={className('toggle-button__button')}
|
|
62
|
-
onClick={onOpenChat}
|
|
63
75
|
ref={buttonRef}
|
|
64
|
-
onMouseUp={
|
|
76
|
+
onMouseUp={handleMouseUp}
|
|
77
|
+
onKeyUp={handleKeyUp}
|
|
78
|
+
onClick={handleClick}
|
|
65
79
|
/>
|
|
66
80
|
</div>
|
|
67
81
|
)
|
|
@@ -30,11 +30,8 @@ const Upload = () => {
|
|
|
30
30
|
const skiplinkTargetId = useSkiplink()
|
|
31
31
|
const focusSkiplinkTarget = useSkiplinkTargetFocusing()
|
|
32
32
|
// This hook should be refactored at some point
|
|
33
|
-
const {
|
|
34
|
-
|
|
35
|
-
allowedMimeTypes,
|
|
36
|
-
maxSize,
|
|
37
|
-
} = useFileUploadMeta()
|
|
33
|
+
const { serviceAllowsUploads, allowedMimeTypes, maxSize } =
|
|
34
|
+
useFileUploadMeta()
|
|
38
35
|
const cancelButtonRef = useRef(null)
|
|
39
36
|
const canUpload = useRef(serviceAllowsUploads)
|
|
40
37
|
|
|
@@ -43,12 +40,8 @@ const Upload = () => {
|
|
|
43
40
|
|
|
44
41
|
const hasError = false
|
|
45
42
|
|
|
46
|
-
const {
|
|
47
|
-
|
|
48
|
-
uploadHandle,
|
|
49
|
-
hasServerError,
|
|
50
|
-
progress,
|
|
51
|
-
} = useSingleFileUpload(formName, fileInputName)
|
|
43
|
+
const { selectedFileName, uploadHandle, hasServerError, progress } =
|
|
44
|
+
useSingleFileUpload(formName, fileInputName)
|
|
52
45
|
const notificationId = useGeneratedId()
|
|
53
46
|
const prevIsComplete = useRef(true)
|
|
54
47
|
|
|
@@ -12,7 +12,6 @@ import { useGeneratedId } from '../../hooks/utility-hooks'
|
|
|
12
12
|
import { useSkiplinkTargetFocusing } from '../../hooks/focus-helper-hooks'
|
|
13
13
|
import { useSeamlyHasUserResponded } from '../../hooks/seamly-api-hooks'
|
|
14
14
|
import { useLiveRegion } from '../../hooks/live-region-hooks'
|
|
15
|
-
import useSeamlyInterrupt from '../../hooks/use-seamly-interrupt'
|
|
16
15
|
import useSeamlyIdleDetachCountdown from '../../hooks/use-seamly-idle-detach-countdown'
|
|
17
16
|
import useSeamlyResumeConversationPrompt from '../../hooks/use-seamly-resume-conversation-prompt'
|
|
18
17
|
import { useI18n } from '../../../domains/i18n'
|
|
@@ -20,6 +19,7 @@ import InOutTransition, {
|
|
|
20
19
|
transitionStartStates,
|
|
21
20
|
} from '../widgets/in-out-transition'
|
|
22
21
|
import { useTranslatedEventData } from '../../../domains/translations'
|
|
22
|
+
import { useInterrupt } from '../../../domains/interrupt'
|
|
23
23
|
|
|
24
24
|
const Faq = () => {
|
|
25
25
|
const { t } = useI18n()
|
|
@@ -27,7 +27,7 @@ const Faq = () => {
|
|
|
27
27
|
const sectionId = useGeneratedId()
|
|
28
28
|
const focusSkiplinkTarget = useSkiplinkTargetFocusing()
|
|
29
29
|
const { sendPolite } = useLiveRegion()
|
|
30
|
-
const { hasInterrupt } =
|
|
30
|
+
const { hasInterrupt } = useInterrupt()
|
|
31
31
|
const { hasCountdown, endCountdown } = useSeamlyIdleDetachCountdown()
|
|
32
32
|
const { hasPrompt, continueChat } = useSeamlyResumeConversationPrompt()
|
|
33
33
|
|
|
@@ -42,7 +42,7 @@ const Faq = () => {
|
|
|
42
42
|
classNames: [
|
|
43
43
|
itemBaseClass,
|
|
44
44
|
...categories.map(
|
|
45
|
-
cat =>
|
|
45
|
+
(cat) =>
|
|
46
46
|
`faqs__item--${String(cat)
|
|
47
47
|
.toLowerCase()
|
|
48
48
|
.replace(/[^a-z0-9_\\-]/, '')}`,
|
|
@@ -138,7 +138,7 @@ const Faq = () => {
|
|
|
138
138
|
)}
|
|
139
139
|
{!!renderedFaqList.length && (
|
|
140
140
|
<ul className={className('faqs__list')}>
|
|
141
|
-
{renderedFaqList.map(faq => (
|
|
141
|
+
{renderedFaqList.map((faq) => (
|
|
142
142
|
<li key={faq.id.toString()} className={className(faq.classNames)}>
|
|
143
143
|
<button
|
|
144
144
|
type="button"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useState, useEffect } from 'preact/hooks'
|
|
2
|
+
import { className } from '../../../lib/css'
|
|
3
|
+
import Icon from '../layout/icon'
|
|
4
|
+
|
|
5
|
+
export default function Error({ id, error }) {
|
|
6
|
+
const [isAvailable, setIsAvailable] = useState(false)
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const timerId = setTimeout(() => setIsAvailable(true), 300) // 300 = magic number, could be less or more
|
|
9
|
+
return () => clearTimeout(timerId) // clear timer if error is mounted+unmounted within 300
|
|
10
|
+
}, [])
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div aria-live="assertive" aria-atomic="true">
|
|
14
|
+
{isAvailable && error && (
|
|
15
|
+
<span id={id} className={className('error')}>
|
|
16
|
+
<Icon name="error" size="16" />
|
|
17
|
+
{error}
|
|
18
|
+
</span>
|
|
19
|
+
)}
|
|
20
|
+
</div>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -3,6 +3,7 @@ import { className } from '../../../lib/css'
|
|
|
3
3
|
import { useGeneratedId } from '../../hooks/seamly-hooks'
|
|
4
4
|
import { useFormControl, useFormContext } from '../../../domains/forms'
|
|
5
5
|
import Icon from '../layout/icon'
|
|
6
|
+
import Error from './error'
|
|
6
7
|
|
|
7
8
|
export default function FileInput({
|
|
8
9
|
id,
|
|
@@ -38,7 +39,7 @@ export default function FileInput({
|
|
|
38
39
|
}, [setFocusWithin, onBlur])
|
|
39
40
|
|
|
40
41
|
const handleChange = useCallback(
|
|
41
|
-
e => {
|
|
42
|
+
(e) => {
|
|
42
43
|
const customEvent = {
|
|
43
44
|
target: {
|
|
44
45
|
value: e.target.files,
|
|
@@ -57,14 +58,7 @@ export default function FileInput({
|
|
|
57
58
|
{contentHint}
|
|
58
59
|
</span>
|
|
59
60
|
)}
|
|
60
|
-
<
|
|
61
|
-
{hasError && (
|
|
62
|
-
<span id={errorId} className={className('error')}>
|
|
63
|
-
<Icon name="error" size="16" />
|
|
64
|
-
{error}
|
|
65
|
-
</span>
|
|
66
|
-
)}
|
|
67
|
-
</div>
|
|
61
|
+
<Error id={errorId} error={hasError && error} />
|
|
68
62
|
<div
|
|
69
63
|
className={className([
|
|
70
64
|
'file-upload',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { className } from '../../../lib/css'
|
|
2
|
-
import
|
|
2
|
+
import Error from './error'
|
|
3
3
|
|
|
4
4
|
const FormControlWrapper = ({
|
|
5
5
|
contentHint,
|
|
@@ -23,14 +23,7 @@ const FormControlWrapper = ({
|
|
|
23
23
|
{contentHint}
|
|
24
24
|
</span>
|
|
25
25
|
)}
|
|
26
|
-
<
|
|
27
|
-
{!validity && (
|
|
28
|
-
<span id={`${id}-error`} className={className('error')}>
|
|
29
|
-
<Icon name="error" size="16" />
|
|
30
|
-
{errorText}
|
|
31
|
-
</span>
|
|
32
|
-
)}
|
|
33
|
-
</div>
|
|
26
|
+
<Error id={`${id}-error`} error={!validity && errorText} />
|
|
34
27
|
{children}
|
|
35
28
|
</>
|
|
36
29
|
)
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
useSeamlyUnreadCount,
|
|
5
5
|
useSeamlyVisibility,
|
|
6
6
|
useSeamlyCurrentAgent,
|
|
7
|
-
useSeamlyInterrupt,
|
|
8
|
-
useSeamlyConfig,
|
|
9
7
|
} from '../../hooks/seamly-hooks'
|
|
10
8
|
import { className } from '../../../lib/css'
|
|
11
9
|
import { useI18n } from '../../../domains/i18n'
|
|
10
|
+
import { useInterrupt } from '../../../domains/interrupt'
|
|
11
|
+
import { useConfig } from '../../../domains/config'
|
|
12
12
|
|
|
13
13
|
const AgentInfo = () => {
|
|
14
14
|
const { t } = useI18n()
|
|
@@ -16,8 +16,8 @@ const AgentInfo = () => {
|
|
|
16
16
|
const unreadMessageCount = useSeamlyUnreadCount()
|
|
17
17
|
const { isOpen } = useSeamlyVisibility()
|
|
18
18
|
const currentAgent = useSeamlyCurrentAgent()
|
|
19
|
-
const { hasInterrupt } =
|
|
20
|
-
const { defaults } =
|
|
19
|
+
const { hasInterrupt } = useInterrupt()
|
|
20
|
+
const { defaults } = useConfig()
|
|
21
21
|
|
|
22
22
|
const { startChatIcon } = defaults || {}
|
|
23
23
|
|
|
@@ -2,7 +2,6 @@ import { useState, useEffect, useCallback } from 'preact/hooks'
|
|
|
2
2
|
import { className } from '../../../lib/css'
|
|
3
3
|
import {
|
|
4
4
|
useSeamlyAppContainerClassNames,
|
|
5
|
-
useSeamlyConfig,
|
|
6
5
|
useSeamlyVisibility,
|
|
7
6
|
useSeamlyLayoutMode,
|
|
8
7
|
useSeamlyHasUserResponded,
|
|
@@ -10,11 +9,12 @@ import {
|
|
|
10
9
|
} from '../../hooks/seamly-hooks'
|
|
11
10
|
import Faq from '../faq/faq'
|
|
12
11
|
import { visibilityStates } from '../../utils/seamly-utils'
|
|
12
|
+
import { useConfig } from '../../../domains/config'
|
|
13
13
|
|
|
14
14
|
const AppFrame = ({ children }) => {
|
|
15
15
|
const [, setSeamlyContainerElement] = useSeamlyContainerElement()
|
|
16
16
|
const { isOpen, isVisible, setVisibility } = useSeamlyVisibility()
|
|
17
|
-
const { context, zIndex, showFaq } =
|
|
17
|
+
const { context, zIndex, showFaq } = useConfig()
|
|
18
18
|
const { isModal, isInline } = useSeamlyLayoutMode()
|
|
19
19
|
const appContainerClassNames = useSeamlyAppContainerClassNames()
|
|
20
20
|
const userResponded = useSeamlyHasUserResponded()
|
|
@@ -22,7 +22,7 @@ const AppFrame = ({ children }) => {
|
|
|
22
22
|
const { locale } = context || {}
|
|
23
23
|
|
|
24
24
|
const containerElementRef = useCallback(
|
|
25
|
-
container => {
|
|
25
|
+
(container) => {
|
|
26
26
|
setSeamlyContainerElement(container)
|
|
27
27
|
},
|
|
28
28
|
[setSeamlyContainerElement],
|
|
@@ -47,14 +47,14 @@ const AppFrame = ({ children }) => {
|
|
|
47
47
|
classNames.push('app--user-responded')
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
const onKeyDownHandler = e => {
|
|
50
|
+
const onKeyDownHandler = (e) => {
|
|
51
51
|
if ((e.code && e.code === 'Escape') || e.keyCode === 27)
|
|
52
52
|
if (!isInline && isOpen) {
|
|
53
53
|
setVisibility(visibilityStates.minimized)
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const onClickHandler = e => {
|
|
57
|
+
const onClickHandler = (e) => {
|
|
58
58
|
if (isModal) {
|
|
59
59
|
e.stopPropagation()
|
|
60
60
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { className } from '../../../lib/css'
|
|
2
|
-
import {
|
|
3
|
-
useSeamlyInterrupt,
|
|
4
|
-
useSeamlyVisibility,
|
|
5
|
-
} from '../../hooks/seamly-hooks'
|
|
2
|
+
import { useSeamlyVisibility } from '../../hooks/seamly-hooks'
|
|
6
3
|
import CobrowsingActiveFrame from '../warnings/cobrowsing-active-frame'
|
|
7
4
|
import AppOptions from '../app-options'
|
|
8
5
|
import { ChatStatus as TranslationsChatStatus } from '../../../domains/translations'
|
|
6
|
+
import { useInterrupt } from '../../../domains/interrupt'
|
|
9
7
|
|
|
10
8
|
function ChatFrame({ children, interruptComponent: InterruptComponent }) {
|
|
11
|
-
const { hasInterrupt, meta } =
|
|
9
|
+
const { hasInterrupt, meta } = useInterrupt()
|
|
12
10
|
const { isOpen } = useSeamlyVisibility()
|
|
13
11
|
|
|
14
12
|
const getContent = () => {
|
|
@@ -1,27 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useRef } from 'preact/hooks'
|
|
2
2
|
import { className } from '../../../lib/css'
|
|
3
3
|
import Icon from './icon'
|
|
4
4
|
import { useI18n } from '../../../domains/i18n'
|
|
5
|
-
import {
|
|
6
|
-
useSeamlyVisibility,
|
|
7
|
-
useFocusIfSeamlyContainedFocus,
|
|
8
|
-
} from '../../hooks/seamly-hooks'
|
|
5
|
+
import { useSeamlyStateContext } from '../../hooks/seamly-hooks'
|
|
9
6
|
|
|
10
7
|
const Header = ({ children, onCloseChat }) => {
|
|
11
|
-
const {
|
|
12
|
-
const prevIsOpen = useRef(null)
|
|
8
|
+
const { headerCollapseButtonId } = useSeamlyStateContext()
|
|
13
9
|
const closeButton = useRef(null)
|
|
14
|
-
const focusIfContained = useFocusIfSeamlyContainedFocus()
|
|
15
|
-
|
|
16
|
-
useLayoutEffect(() => {
|
|
17
|
-
// Because we can open the app from the external API we
|
|
18
|
-
// need to determine if current keyboard focus resides inside
|
|
19
|
-
// the Seamly app first otherwise focus will be hijacked
|
|
20
|
-
if (isOpen && prevIsOpen.current === false) {
|
|
21
|
-
focusIfContained(closeButton.current)
|
|
22
|
-
}
|
|
23
|
-
prevIsOpen.current = isOpen
|
|
24
|
-
}, [isOpen, focusIfContained])
|
|
25
10
|
|
|
26
11
|
const { t } = useI18n()
|
|
27
12
|
return (
|
|
@@ -33,6 +18,7 @@ const Header = ({ children, onCloseChat }) => {
|
|
|
33
18
|
className={className('button', 'header-controls__collapse')}
|
|
34
19
|
onClick={onCloseChat}
|
|
35
20
|
ref={closeButton}
|
|
21
|
+
id={headerCollapseButtonId}
|
|
36
22
|
>
|
|
37
23
|
<Icon name="chevronDown" size="32" alt={t('header.collapseApp')} />
|
|
38
24
|
</button>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { className } from '../../../lib/css'
|
|
2
|
-
import { useSeamlyDisclaimerState } from '../../hooks/seamly-hooks'
|
|
3
2
|
import { useI18n } from '../../../domains/i18n'
|
|
3
|
+
import { useConfig } from '../../../domains/config'
|
|
4
4
|
|
|
5
5
|
const PrivacyDisclaimer = () => {
|
|
6
6
|
const { t } = useI18n()
|
|
7
|
-
const showDisclaimer =
|
|
7
|
+
const { showDisclaimer } = useConfig()
|
|
8
8
|
|
|
9
9
|
return (
|
|
10
10
|
showDisclaimer && (
|
|
@@ -18,12 +18,8 @@ import {
|
|
|
18
18
|
const Cobrowsing = () => {
|
|
19
19
|
const { t } = useI18n()
|
|
20
20
|
const cobrowsingDescriptionId = useGeneratedId()
|
|
21
|
-
const {
|
|
22
|
-
|
|
23
|
-
features,
|
|
24
|
-
setUserSelectedOption,
|
|
25
|
-
hideOption,
|
|
26
|
-
} = useSeamlyOptions()
|
|
21
|
+
const { userSelectedOptions, features, setUserSelectedOption, hideOption } =
|
|
22
|
+
useSeamlyOptions()
|
|
27
23
|
const { enabled: canActivateCobrowsing } = features.cobrowsing || {}
|
|
28
24
|
const prevCanActivateCobrowsing = useRef(null)
|
|
29
25
|
const [cobrowsingToggleActive, setCobrowsingToggleActive] = useState(
|
|
@@ -53,7 +49,7 @@ const Cobrowsing = () => {
|
|
|
53
49
|
prevCanActivateCobrowsing.current = canActivateCobrowsing
|
|
54
50
|
}, [canActivateCobrowsing, setUserSelectedOption, sendAssertive, t])
|
|
55
51
|
|
|
56
|
-
const toggleAndFocus = isCancel => {
|
|
52
|
+
const toggleAndFocus = (isCancel) => {
|
|
57
53
|
if (!isCancel && !userSelectedOptions.cobrowsing) {
|
|
58
54
|
focusContainer()
|
|
59
55
|
} else {
|
|
@@ -12,12 +12,8 @@ import { getKey, keyNames, focusElement } from '../../utils/general-utils'
|
|
|
12
12
|
|
|
13
13
|
const OptionsButton = () => {
|
|
14
14
|
const { t } = useI18n()
|
|
15
|
-
const {
|
|
16
|
-
|
|
17
|
-
showOption,
|
|
18
|
-
panelActive,
|
|
19
|
-
hideOption,
|
|
20
|
-
} = useSeamlyOptions()
|
|
15
|
+
const { menuOptions, showOption, panelActive, hideOption } =
|
|
16
|
+
useSeamlyOptions()
|
|
21
17
|
const { id } = useOptionButton()
|
|
22
18
|
const focusOutDelayTimeoutID = useRef(null)
|
|
23
19
|
|
|
@@ -48,7 +44,7 @@ const OptionsButton = () => {
|
|
|
48
44
|
requestAnimationFrame(() => {
|
|
49
45
|
requestAnimationFrame(() => {
|
|
50
46
|
const firstActiveOptionIndex = menuOptions.findIndex(
|
|
51
|
-
option => option.available,
|
|
47
|
+
(option) => option.available,
|
|
52
48
|
)
|
|
53
49
|
const focusIndex =
|
|
54
50
|
firstActiveOptionIndex === -1 ? 0 : firstActiveOptionIndex
|
|
@@ -64,13 +60,13 @@ const OptionsButton = () => {
|
|
|
64
60
|
hideOption()
|
|
65
61
|
}
|
|
66
62
|
if (multiMenu) {
|
|
67
|
-
setMenuIsOpen(o => !o)
|
|
63
|
+
setMenuIsOpen((o) => !o)
|
|
68
64
|
} else if (firstOption.available) {
|
|
69
65
|
showOption(firstOption.name)
|
|
70
66
|
}
|
|
71
67
|
}
|
|
72
68
|
|
|
73
|
-
const onMainKeyDownHandler = e => {
|
|
69
|
+
const onMainKeyDownHandler = (e) => {
|
|
74
70
|
if (!menuIsOpen) {
|
|
75
71
|
return
|
|
76
72
|
}
|
|
@@ -89,7 +85,7 @@ const OptionsButton = () => {
|
|
|
89
85
|
}
|
|
90
86
|
}
|
|
91
87
|
|
|
92
|
-
const onButtonKeyDownHandler = e => {
|
|
88
|
+
const onButtonKeyDownHandler = (e) => {
|
|
93
89
|
if (getKey(e) === keyNames.ArrowDown) {
|
|
94
90
|
setMenuIsOpen(true)
|
|
95
91
|
e.preventDefault()
|
|
@@ -194,7 +190,7 @@ const OptionsButton = () => {
|
|
|
194
190
|
>
|
|
195
191
|
<button
|
|
196
192
|
type="button"
|
|
197
|
-
ref={item => {
|
|
193
|
+
ref={(item) => {
|
|
198
194
|
menuItemButtons.current[i] = item
|
|
199
195
|
}}
|
|
200
196
|
className={className([
|
|
@@ -202,8 +198,8 @@ const OptionsButton = () => {
|
|
|
202
198
|
'button--secondary',
|
|
203
199
|
...(available ? [] : ['button--disabled']),
|
|
204
200
|
])}
|
|
205
|
-
onKeyDown={e => onMenuItemKeyDownHandler(e, i)}
|
|
206
|
-
onKeyPress={e => onKeyPressHandler(e, i)}
|
|
201
|
+
onKeyDown={(e) => onMenuItemKeyDownHandler(e, i)}
|
|
202
|
+
onKeyPress={(e) => onKeyPressHandler(e, i)}
|
|
207
203
|
onClick={() => onMenuItemClickHandler(name, available)}
|
|
208
204
|
aria-disabled={!available ? 'true' : null}
|
|
209
205
|
>
|
|
@@ -38,8 +38,8 @@ const Transcript = () => {
|
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
const handleSubmit = useCallback(
|
|
41
|
-
values => {
|
|
42
|
-
const emailAddress = values[controlName]
|
|
41
|
+
(values) => {
|
|
42
|
+
const emailAddress = values[controlName].trim()
|
|
43
43
|
sendAction({
|
|
44
44
|
type: actionTypes.sendTranscript,
|
|
45
45
|
body: { emailAddress },
|