@elliemae/ds-chat 2.0.2 → 2.0.6
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/cjs/ChatDataTestids.js +2 -1
- package/cjs/parts/chat-composer/ChatContainerComposer.js +12 -6
- package/cjs/parts/chat-composer/props.js +2 -0
- package/cjs/parts/chat-container/ChatContainer.js +25 -21
- package/cjs/parts/chat-container/styled.js +1 -1
- package/cjs/parts/chat-container-messages-list/ChatContainerMessagesList.js +70 -35
- package/cjs/parts/chat-container-messages-list/styled.js +7 -3
- package/cjs/parts/chat-container-messages-list/useGetNewMessages.js +28 -0
- package/esm/ChatDataTestids.js +2 -1
- package/esm/parts/chat-composer/ChatContainerComposer.js +12 -6
- package/esm/parts/chat-composer/props.js +2 -0
- package/esm/parts/chat-container/ChatContainer.js +25 -21
- package/esm/parts/chat-container/styled.js +1 -1
- package/esm/parts/chat-container-messages-list/ChatContainerMessagesList.js +71 -36
- package/esm/parts/chat-container-messages-list/styled.js +7 -4
- package/esm/parts/chat-container-messages-list/useGetNewMessages.js +24 -0
- package/package.json +14 -10
- package/types/ChatDataTestids.d.ts +1 -0
- package/types/parts/chat-composer/ChatContainerComposer.d.ts +3 -0
- package/types/parts/chat-container-messages-list/styled.d.ts +1 -0
- package/types/parts/chat-container-messages-list/useGetNewMessages.d.ts +2 -0
- package/types/tests/utils.d.ts +1 -27
package/cjs/ChatDataTestids.js
CHANGED
|
@@ -21,7 +21,8 @@ const ChatDataTestIds = {
|
|
|
21
21
|
SYSTEM_MSG_TIME: 'chat-system-message-time',
|
|
22
22
|
BUBBLE: 'ds-chat-bubble',
|
|
23
23
|
BUBBLE_TITLE: 'chat-bubble-title',
|
|
24
|
-
BUBBLE_BODY: 'chat-bubble-body'
|
|
24
|
+
BUBBLE_BODY: 'chat-bubble-body',
|
|
25
|
+
BANNER: 'ds-banner-container'
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
exports.ChatDataTestIds = ChatDataTestIds;
|
|
@@ -48,10 +48,12 @@ const ChatComposer = props => {
|
|
|
48
48
|
placeholder,
|
|
49
49
|
inputMaxLength,
|
|
50
50
|
maxLengthMessage,
|
|
51
|
+
ariaMaxLengthMessage,
|
|
51
52
|
buttonDisabled,
|
|
52
53
|
dataTestid,
|
|
53
54
|
actionRef
|
|
54
55
|
} = props;
|
|
56
|
+
const COMPOSER_MIN_HEIGHT = 32;
|
|
55
57
|
const input = React__default["default"].useRef();
|
|
56
58
|
const inputSize = React__default["default"].useRef();
|
|
57
59
|
const inputSend = React__default["default"].useRef();
|
|
@@ -62,10 +64,10 @@ const ChatComposer = props => {
|
|
|
62
64
|
var _inputSize$current, _inputSize$current2, _inputSize$current3, _inputSize$current4;
|
|
63
65
|
|
|
64
66
|
const newHeight = ((_inputSize$current = inputSize.current) === null || _inputSize$current === void 0 ? void 0 : _inputSize$current.scrollHeight) > internalMaxHeight ? internalMaxHeight : (_inputSize$current2 = inputSize.current) === null || _inputSize$current2 === void 0 ? void 0 : _inputSize$current2.scrollHeight;
|
|
65
|
-
onResize(newHeight);
|
|
67
|
+
if (newHeight !== height && newHeight > COMPOSER_MIN_HEIGHT) onResize(newHeight);
|
|
66
68
|
setHeight(newHeight);
|
|
67
69
|
setShowScroll(((_inputSize$current3 = inputSize.current) === null || _inputSize$current3 === void 0 ? void 0 : _inputSize$current3.scrollHeight) - ((_inputSize$current4 = inputSize.current) === null || _inputSize$current4 === void 0 ? void 0 : _inputSize$current4.clientHeight) > 5);
|
|
68
|
-
}, [inputValue, internalMaxHeight, onResize]);
|
|
70
|
+
}, [inputValue, internalMaxHeight, height, onResize]);
|
|
69
71
|
React__default["default"].useEffect(() => {
|
|
70
72
|
if (actionRef && actionRef.current) {
|
|
71
73
|
actionRef.current.focusToComposer = () => {
|
|
@@ -92,6 +94,9 @@ const ChatComposer = props => {
|
|
|
92
94
|
"data-testid": dataTestid
|
|
93
95
|
}, void 0, /*#__PURE__*/_jsx__default["default"](dsGrid.Grid, {
|
|
94
96
|
cols: ['1fr', '24px'],
|
|
97
|
+
style: {
|
|
98
|
+
height: heightProps.height
|
|
99
|
+
},
|
|
95
100
|
justifyContent: "center",
|
|
96
101
|
alignItems: "flex-end",
|
|
97
102
|
gutter: "xxs"
|
|
@@ -105,7 +110,9 @@ const ChatComposer = props => {
|
|
|
105
110
|
onKeyDown: onKeyDown,
|
|
106
111
|
placeholder: placeholder,
|
|
107
112
|
"aria-label": "Message to send",
|
|
108
|
-
"data-testid": "chat-composer-textarea"
|
|
113
|
+
"data-testid": "chat-composer-textarea",
|
|
114
|
+
name: "chat-composer-textarea",
|
|
115
|
+
id: "chat-composer-textarea"
|
|
109
116
|
}), /*#__PURE__*/_jsx__default["default"](dsGrid.Grid, {
|
|
110
117
|
height: "32px",
|
|
111
118
|
alignItems: "center"
|
|
@@ -132,8 +139,7 @@ const ChatComposer = props => {
|
|
|
132
139
|
width: (_input$current = input.current) === null || _input$current === void 0 ? void 0 : _input$current.clientWidth
|
|
133
140
|
},
|
|
134
141
|
onChange: handleChange,
|
|
135
|
-
value: inputValue
|
|
136
|
-
|
|
142
|
+
value: inputValue
|
|
137
143
|
}), /*#__PURE__*/_jsx__default["default"](dsGrid.Grid, {
|
|
138
144
|
cols: ['1fr', '24px'],
|
|
139
145
|
justifyContent: "flex-end",
|
|
@@ -146,7 +152,7 @@ const ChatComposer = props => {
|
|
|
146
152
|
}, void 0, inputMaxLength && hasError ? /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
147
153
|
children: [/*#__PURE__*/_jsx__default["default"](styled.StyledCounterErrorA11y, {
|
|
148
154
|
role: "alert"
|
|
149
|
-
}, void 0,
|
|
155
|
+
}, void 0, ariaMaxLengthMessage || "You have entered ".concat(inputLength, " characters and have exceeded the limit by ").concat(inputLength - inputMaxLength, ". Please limit to less than ").concat(inputMaxLength, " characters. ")), maxLengthMessage || "Please limit to less than ".concat(inputMaxLength, " characters. ")]
|
|
150
156
|
}) : '', inputMaxLength ? " ".concat(inputLength, " / ").concat(inputMaxLength) : ''), _Grid || (_Grid = /*#__PURE__*/_jsx__default["default"](dsGrid.Grid, {}))));
|
|
151
157
|
};
|
|
152
158
|
|
|
@@ -8,6 +8,7 @@ var lodash = require('lodash');
|
|
|
8
8
|
const defaultComposerPros = {
|
|
9
9
|
placeholder: 'Type your message',
|
|
10
10
|
inputValue: '',
|
|
11
|
+
ariaMaxLengthMessage: '',
|
|
11
12
|
onChange: lodash.noop,
|
|
12
13
|
onResize: lodash.noop,
|
|
13
14
|
onSend: lodash.noop,
|
|
@@ -24,6 +25,7 @@ const defaultComposerPros = {
|
|
|
24
25
|
const composerProps = {
|
|
25
26
|
placeholder: reactDesc.PropTypes.string.description('Input Placeholder').defaultValue(defaultComposerPros.placeholder),
|
|
26
27
|
inputValue: reactDesc.PropTypes.string.description('Input value').defaultValue(defaultComposerPros.inputValue),
|
|
28
|
+
ariaMaxLengthMessage: reactDesc.PropTypes.string.description('Aria message for Max length message when exceeded').defaultValue(defaultComposerPros.maxLengthMessage),
|
|
27
29
|
maxLengthMessage: reactDesc.PropTypes.string.description('Max length message').defaultValue(defaultComposerPros.maxLengthMessage),
|
|
28
30
|
onChange: reactDesc.PropTypes.func.description('Called when the user types').defaultValue(defaultComposerPros.onChange),
|
|
29
31
|
onResize: reactDesc.PropTypes.func.description('Called the input changes his height').defaultValue(defaultComposerPros.onResize),
|
|
@@ -29,14 +29,14 @@ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
|
29
29
|
var DSIndeterminateProgressIndicator__default = /*#__PURE__*/_interopDefaultLegacy(DSIndeterminateProgressIndicator);
|
|
30
30
|
var DSBanner__default = /*#__PURE__*/_interopDefaultLegacy(DSBanner);
|
|
31
31
|
|
|
32
|
-
var _DSIndeterminateProgr
|
|
32
|
+
var _DSIndeterminateProgr;
|
|
33
33
|
|
|
34
34
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
35
35
|
|
|
36
36
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty__default["default"](target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
37
37
|
|
|
38
38
|
const ChatContainer = props$1 => {
|
|
39
|
-
var _containerRef$current;
|
|
39
|
+
var _messagesListProps$me2, _containerRef$current;
|
|
40
40
|
|
|
41
41
|
useValidateProps.useValidateProps(props$1, props.containerProps);
|
|
42
42
|
const {
|
|
@@ -67,18 +67,16 @@ const ChatContainer = props$1 => {
|
|
|
67
67
|
});
|
|
68
68
|
const onResize = React__default["default"].useCallback(height => {
|
|
69
69
|
if (onComposerResize) onComposerResize(height);
|
|
70
|
-
|
|
71
|
-
if (height > 30) {
|
|
72
|
-
/*
|
|
70
|
+
/*
|
|
73
71
|
setTimeout is used to equal the animation duration from the
|
|
74
72
|
composer text area when increase its size
|
|
75
73
|
*/
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
}
|
|
74
|
+
|
|
75
|
+
setTimeout(() => {
|
|
76
|
+
actionRef.current.scrollToIndex(messagesListProps.messages.length - 1, {
|
|
77
|
+
align: 'start'
|
|
78
|
+
});
|
|
79
|
+
}, 50);
|
|
82
80
|
}, [onComposerResize, messagesListProps.messages, actionRef]);
|
|
83
81
|
const Container = device === 'desktop' ? styled.StyledChatContainerContent : styled.MobileStyledChatContainerContent;
|
|
84
82
|
const handleTabs = React__default["default"].useCallback(e => {
|
|
@@ -86,17 +84,23 @@ const ChatContainer = props$1 => {
|
|
|
86
84
|
const ref = actionRef || listRef;
|
|
87
85
|
|
|
88
86
|
if (e.shiftKey && e.key === 'Tab') {
|
|
89
|
-
|
|
87
|
+
var _messagesListProps$me;
|
|
88
|
+
|
|
89
|
+
if (((_messagesListProps$me = messagesListProps.messages) === null || _messagesListProps$me === void 0 ? void 0 : _messagesListProps$me.length) > 0) {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
|
|
92
|
+
if (['top', 'bottom'].includes(bannerPosition)) {
|
|
93
|
+
bannerRef.current.focusOnWrapper();
|
|
94
|
+
} else {
|
|
95
|
+
var _ref$current;
|
|
90
96
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const last = ref.current.getMessagesInView().pop();
|
|
95
|
-
ref.current.focusToIndexWithoutScroll(last);
|
|
97
|
+
const last = ref.current.getMessagesInView().pop();
|
|
98
|
+
(_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.focusToIndexWithoutScroll(last);
|
|
99
|
+
}
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
|
-
}, [actionRef, onComposerKeyDown, bannerPosition]); // Map actions
|
|
103
|
+
}, [actionRef, onComposerKeyDown, bannerPosition, (_messagesListProps$me2 = messagesListProps.messages) === null || _messagesListProps$me2 === void 0 ? void 0 : _messagesListProps$me2.length]); // Map actions
|
|
100
104
|
|
|
101
105
|
React__default["default"].useEffect(() => {
|
|
102
106
|
if (actionRef && actionRef.current) {
|
|
@@ -146,15 +150,15 @@ const ChatContainer = props$1 => {
|
|
|
146
150
|
isOpen: true,
|
|
147
151
|
type: bannerProps.type || DSBanner.BANNER_TYPES.INFO,
|
|
148
152
|
actionRef: bannerRef
|
|
149
|
-
})), sendingMessages && sendingMessages.length > 0 ?
|
|
153
|
+
})), sendingMessages && sendingMessages.length > 0 ? /*#__PURE__*/_jsx__default["default"](styled.StyledIndicatorContainer, {
|
|
150
154
|
isLoading: "bottom",
|
|
151
155
|
linear: true,
|
|
152
156
|
"data-testid": "ds-chat-loading-indicator"
|
|
153
157
|
}, void 0, /*#__PURE__*/_jsx__default["default"](DSIndeterminateProgressIndicator__default["default"], {
|
|
154
158
|
processing: true,
|
|
155
|
-
title:
|
|
159
|
+
title: sendingText,
|
|
156
160
|
lineOnly: true
|
|
157
|
-
}))
|
|
161
|
+
})) : null), /*#__PURE__*/jsxRuntime.jsx(ChatContainerComposer.ChatComposer, _objectSpread(_objectSpread({}, composerProps), {}, {
|
|
158
162
|
onKeyDown: handleTabs,
|
|
159
163
|
onResize: onResize,
|
|
160
164
|
maxHeight: (((_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.clientHeight) || 0) / 2,
|
|
@@ -18,7 +18,7 @@ const StyledChatContainerContent = /*#__PURE__*/styled__default["default"](dsGri
|
|
|
18
18
|
})(["height:100%;min-width:", "px;overflow:hidden;background:", ";position:relative;"], CONTAINER_WIDTH, props => props.theme.colors.neutral['050']);
|
|
19
19
|
const StyledIndicatorContainer = /*#__PURE__*/styled__default["default"].div.withConfig({
|
|
20
20
|
componentId: "sc-d732wo-1"
|
|
21
|
-
})(["transition:0.4s ease-in-out;position:absolute;visibility:hidden;pointer-events:none;width:100%;padding:0 ", ";left:0;right:0;background:", ";", " ", " & div,a{font-size:13px;}"], props => !props.linear ? props.theme.space.xxs : 0, props => props.theme.colors.neutral['000'], props => props.isLoading === 'top' ? "\n top: -41px;\n transform: translate(0, 41px);\n visibility: visible;\n pointer-events: auto
|
|
21
|
+
})(["transition:0.4s ease-in-out;position:absolute;visibility:hidden;pointer-events:none;width:100%;padding:0 ", ";left:0;right:0;background:", ";", " ", " & div,a{font-size:13px;}"], props => !props.linear ? props.theme.space.xxs : 0, props => props.theme.colors.neutral['000'], props => props.isLoading === 'top' ? "\n top: -41px;\n transform: translate(0, 41px);\n visibility: visible;\n pointer-events: auto;\n " : '', props => props.isLoading === 'bottom' ? "\n bottom: -41px;\n transform: translate(0, -41px);\n visibility: visible;\n pointer-events: auto;\n " : '');
|
|
22
22
|
const MobileStyledChatContainerContent = /*#__PURE__*/styled__default["default"](StyledChatContainerContent).withConfig({
|
|
23
23
|
componentId: "sc-d732wo-2"
|
|
24
24
|
})(["", ",", "{font-size:16px;}", "{font-size:13px;}", "{background-color:#fff;box-shadow:-0 1px 3px rgba(53,60,70,0.8);}", ",", "{font-size:16px;text-align:left;}", "{font-size:13px;}", "{font-size:16px;}", "{font-size:16px;}"], styled$1.StyledTitleLeftBubble, styled$1.StyleBodyWrapper, styled$1.StyledTitleRightBubble, style.Wrapper, style.Title, style.Message, style.Time, styled$2.StyledComposerInput, StyledIndicatorContainer);
|
|
@@ -19,6 +19,7 @@ var React = require('react');
|
|
|
19
19
|
var reactDesc = require('react-desc');
|
|
20
20
|
var reactVirtual = require('react-virtual');
|
|
21
21
|
var DSButton = require('@elliemae/ds-button');
|
|
22
|
+
var lodash = require('lodash');
|
|
22
23
|
var ChatBubble = require('../bubble/ChatBubble.js');
|
|
23
24
|
var props$1 = require('../bubble/props.js');
|
|
24
25
|
var MessageDelimiter = require('../message-delimiter/MessageDelimiter.js');
|
|
@@ -26,6 +27,7 @@ var SystemMessage = require('../system-message/SystemMessage.js');
|
|
|
26
27
|
var styled = require('./styled.js');
|
|
27
28
|
var props = require('./props.js');
|
|
28
29
|
var useValidateProps = require('./config/useValidateProps.js');
|
|
30
|
+
var useGetNewMessages = require('./useGetNewMessages.js');
|
|
29
31
|
var jsxRuntime = require('react/jsx-runtime');
|
|
30
32
|
|
|
31
33
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
@@ -67,6 +69,7 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
67
69
|
isLoading,
|
|
68
70
|
bannerRef
|
|
69
71
|
} = props$2;
|
|
72
|
+
const pendingOnScroll = React__default["default"].useRef(false);
|
|
70
73
|
const loadMoreRef = React__default["default"].useRef(null);
|
|
71
74
|
const virtualListRef = React__default["default"].useRef(); // estimateSize should not be really required given what was stated on
|
|
72
75
|
// https://github.com/tannerlinsley/react-virtual/issues/23
|
|
@@ -86,9 +89,17 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
86
89
|
})), {}, {
|
|
87
90
|
estimateSize: React__default["default"].useCallback(() => 80, [])
|
|
88
91
|
}));
|
|
89
|
-
const
|
|
92
|
+
const newMessages = useGetNewMessages.useGetNewMessages(messages);
|
|
93
|
+
const debouncedScroll = React__default["default"].useMemo(() => lodash.debounce(() => {
|
|
90
94
|
var _virtualListRef$curre;
|
|
91
95
|
|
|
96
|
+
if (document.visibilityState === 'hidden') {
|
|
97
|
+
pendingOnScroll.current = true;
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!virtualListRef.current) return [];
|
|
102
|
+
pendingOnScroll.current = false;
|
|
92
103
|
const {
|
|
93
104
|
bottom,
|
|
94
105
|
top
|
|
@@ -97,15 +108,15 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
97
108
|
const indexList = [];
|
|
98
109
|
list.forEach(item => {
|
|
99
110
|
const {
|
|
100
|
-
top: itemTop
|
|
101
|
-
bottom: itemBottom
|
|
111
|
+
top: itemTop
|
|
102
112
|
} = item.getBoundingClientRect();
|
|
103
113
|
const itemPadding = 10;
|
|
104
|
-
if (itemTop + itemPadding < bottom &&
|
|
114
|
+
if (itemTop + itemPadding < bottom && itemTop + itemPadding > top) indexList.push(item.dataset.index);
|
|
105
115
|
});
|
|
106
116
|
if (onScrollEnds) onScrollEnds(indexList);
|
|
107
117
|
return indexList;
|
|
108
|
-
}, [onScrollEnds]);
|
|
118
|
+
}, 500), [onScrollEnds]);
|
|
119
|
+
const onScrollStop = React__default["default"].useCallback(debouncedScroll, [debouncedScroll]);
|
|
109
120
|
const onlyFocus = React__default["default"].useCallback(index => {
|
|
110
121
|
setTimeout(() => {
|
|
111
122
|
virtualListRef.current.querySelector("[data-index=\"".concat(index, "\"]")).focus();
|
|
@@ -117,7 +128,8 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
117
128
|
};
|
|
118
129
|
vScroll(index, b);
|
|
119
130
|
onlyFocus(index);
|
|
120
|
-
|
|
131
|
+
onScrollStop();
|
|
132
|
+
}, [onScrollStop, onlyFocus, vScroll]);
|
|
121
133
|
React__default["default"].useEffect(() => {
|
|
122
134
|
if (actionRef && actionRef.current) {
|
|
123
135
|
actionRef.current.scrollToIndex = function (a) {
|
|
@@ -125,9 +137,11 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
125
137
|
align: 'center'
|
|
126
138
|
};
|
|
127
139
|
vScroll(a, b);
|
|
140
|
+
onScrollStop();
|
|
128
141
|
};
|
|
129
142
|
|
|
130
143
|
actionRef.current.getMessagesInView = onScrollStop;
|
|
144
|
+
onScrollStop(); // init useMemo due to debounce use.
|
|
131
145
|
|
|
132
146
|
actionRef.current.focusToIndex = function (a) {
|
|
133
147
|
let b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
|
|
@@ -138,7 +152,7 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
138
152
|
|
|
139
153
|
actionRef.current.focusToIndexWithoutScroll = onlyFocus;
|
|
140
154
|
|
|
141
|
-
actionRef.current.focusToList =
|
|
155
|
+
actionRef.current.focusToList = () => {
|
|
142
156
|
var _virtualListRef$curre2;
|
|
143
157
|
|
|
144
158
|
(_virtualListRef$curre2 = virtualListRef.current) === null || _virtualListRef$curre2 === void 0 ? void 0 : _virtualListRef$curre2.focus();
|
|
@@ -155,7 +169,17 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
155
169
|
switch (key) {
|
|
156
170
|
case 'ArrowUp':
|
|
157
171
|
e.preventDefault();
|
|
158
|
-
|
|
172
|
+
|
|
173
|
+
if (index > 0) {
|
|
174
|
+
var _virtualListRef$curre3;
|
|
175
|
+
|
|
176
|
+
((_virtualListRef$curre3 = virtualListRef.current) === null || _virtualListRef$curre3 === void 0 ? void 0 : _virtualListRef$curre3.querySelector("[data-index=\"".concat(index - 1, "\"]"))).focus();
|
|
177
|
+
} else if (hasMoreItems === 'top') {
|
|
178
|
+
var _loadMoreRef$current;
|
|
179
|
+
|
|
180
|
+
(_loadMoreRef$current = loadMoreRef.current) === null || _loadMoreRef$current === void 0 ? void 0 : _loadMoreRef$current.focus();
|
|
181
|
+
}
|
|
182
|
+
|
|
159
183
|
break;
|
|
160
184
|
|
|
161
185
|
case 'ArrowDown':
|
|
@@ -178,31 +202,31 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
178
202
|
break;
|
|
179
203
|
|
|
180
204
|
case 'Tab':
|
|
181
|
-
e.preventDefault();
|
|
182
|
-
|
|
183
205
|
if (hasMoreItems === 'bottom' && !e.shiftKey) {
|
|
184
|
-
var _loadMoreRef$current;
|
|
185
|
-
|
|
186
|
-
(_loadMoreRef$current = loadMoreRef.current) === null || _loadMoreRef$current === void 0 ? void 0 : _loadMoreRef$current.focus();
|
|
187
|
-
} else if (hasMoreItems === 'top' && e.shiftKey) {
|
|
188
206
|
var _loadMoreRef$current2;
|
|
189
207
|
|
|
208
|
+
e.preventDefault();
|
|
190
209
|
(_loadMoreRef$current2 = loadMoreRef.current) === null || _loadMoreRef$current2 === void 0 ? void 0 : _loadMoreRef$current2.focus();
|
|
210
|
+
} else if (hasMoreItems === 'top' && e.shiftKey) {
|
|
211
|
+
var _loadMoreRef$current3;
|
|
212
|
+
|
|
213
|
+
e.preventDefault();
|
|
214
|
+
(_loadMoreRef$current3 = loadMoreRef.current) === null || _loadMoreRef$current3 === void 0 ? void 0 : _loadMoreRef$current3.focus();
|
|
191
215
|
} else if (!e.shiftKey) {
|
|
192
216
|
if (bannerPosition) {
|
|
217
|
+
e.preventDefault();
|
|
193
218
|
bannerRef.current.focusOnWrapper();
|
|
194
|
-
} else {
|
|
195
|
-
actionRef.current.focusToComposer();
|
|
196
219
|
}
|
|
197
220
|
} else {
|
|
198
|
-
var _virtualListRef$
|
|
221
|
+
var _virtualListRef$curre4;
|
|
199
222
|
|
|
200
|
-
|
|
223
|
+
e.preventDefault();
|
|
224
|
+
(_virtualListRef$curre4 = virtualListRef.current) === null || _virtualListRef$curre4 === void 0 ? void 0 : _virtualListRef$curre4.focus();
|
|
201
225
|
}
|
|
202
226
|
|
|
203
227
|
break;
|
|
204
228
|
}
|
|
205
|
-
}, [
|
|
229
|
+
}, [bannerRef, hasMoreItems, messages.length, bannerPosition, scrollAndFocus]);
|
|
206
230
|
const handleListKey = React__default["default"].useCallback(e => {
|
|
207
231
|
const {
|
|
208
232
|
key
|
|
@@ -211,12 +235,22 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
211
235
|
switch (key) {
|
|
212
236
|
case 'ArrowUp':
|
|
213
237
|
e.preventDefault();
|
|
214
|
-
|
|
238
|
+
|
|
239
|
+
if (document.activeElement !== loadMoreRef.current) {
|
|
240
|
+
onlyFocus(onScrollStop().shift());
|
|
241
|
+
}
|
|
242
|
+
|
|
215
243
|
break;
|
|
216
244
|
|
|
217
245
|
case 'ArrowDown':
|
|
218
246
|
e.preventDefault();
|
|
219
|
-
|
|
247
|
+
|
|
248
|
+
if (document.activeElement === loadMoreRef.current) {
|
|
249
|
+
virtualListRef.current.querySelector("[data-index=\"0\"]").focus();
|
|
250
|
+
} else {
|
|
251
|
+
onlyFocus(onScrollStop().pop());
|
|
252
|
+
}
|
|
253
|
+
|
|
220
254
|
break;
|
|
221
255
|
|
|
222
256
|
case 'Home':
|
|
@@ -232,11 +266,6 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
232
266
|
case 'Tab':
|
|
233
267
|
if (document.activeElement !== loadMoreRef.current) {
|
|
234
268
|
if (hasMoreItems === 'bottom' && !e.shiftKey) {
|
|
235
|
-
var _loadMoreRef$current3;
|
|
236
|
-
|
|
237
|
-
e.preventDefault();
|
|
238
|
-
(_loadMoreRef$current3 = loadMoreRef.current) === null || _loadMoreRef$current3 === void 0 ? void 0 : _loadMoreRef$current3.focus();
|
|
239
|
-
} else if (hasMoreItems === 'top' && e.shiftKey) {
|
|
240
269
|
var _loadMoreRef$current4;
|
|
241
270
|
|
|
242
271
|
e.preventDefault();
|
|
@@ -249,15 +278,18 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
249
278
|
e.preventDefault();
|
|
250
279
|
actionRef.current.focusToComposer();
|
|
251
280
|
} else {
|
|
252
|
-
var _virtualListRef$
|
|
281
|
+
var _virtualListRef$curre5;
|
|
253
282
|
|
|
254
283
|
e.preventDefault();
|
|
255
|
-
(_virtualListRef$
|
|
284
|
+
(_virtualListRef$curre5 = virtualListRef.current) === null || _virtualListRef$curre5 === void 0 ? void 0 : _virtualListRef$curre5.focus();
|
|
256
285
|
}
|
|
257
286
|
|
|
258
287
|
break;
|
|
259
288
|
}
|
|
260
289
|
}, [actionRef, hasMoreItems, messages.length, onScrollStop, onlyFocus, scrollAndFocus]);
|
|
290
|
+
const checkPendingOnScroll = React__default["default"].useCallback(() => {
|
|
291
|
+
if (pendingOnScroll.current === true) onScrollStop();
|
|
292
|
+
}, [onScrollStop]);
|
|
261
293
|
React__default["default"].useEffect(() => {
|
|
262
294
|
let handlerCallback = null;
|
|
263
295
|
const localVirtualRef = virtualListRef.current;
|
|
@@ -271,11 +303,13 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
271
303
|
virtualListRef.current.addEventListener('scroll', onScroll);
|
|
272
304
|
}
|
|
273
305
|
|
|
306
|
+
document.addEventListener('visibilitychange', checkPendingOnScroll);
|
|
274
307
|
return () => {
|
|
275
308
|
if (handlerCallback) clearTimeout(handlerCallback);
|
|
276
309
|
if (onScrollEnds) localVirtualRef.removeEventListener('scroll', onScroll);
|
|
310
|
+
document.removeEventListener('visibilitychange', checkPendingOnScroll);
|
|
277
311
|
};
|
|
278
|
-
}, [onScrollStop, onScrollEnds]);
|
|
312
|
+
}, [onScrollStop, onScrollEnds, checkPendingOnScroll]);
|
|
279
313
|
const render = React__default["default"].useMemo(() => virtualItems.map(virtualItem => {
|
|
280
314
|
const {
|
|
281
315
|
index,
|
|
@@ -296,7 +330,7 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
296
330
|
"data-index": index,
|
|
297
331
|
style: style,
|
|
298
332
|
ref: measureRef,
|
|
299
|
-
tabIndex:
|
|
333
|
+
tabIndex: -1,
|
|
300
334
|
onKeyDown: handleKey,
|
|
301
335
|
role: "listitem",
|
|
302
336
|
children: /*#__PURE__*/_jsx__default["default"](styled.StyledWrapperListItem, {
|
|
@@ -312,14 +346,12 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
312
346
|
return /*#__PURE__*/_jsx__default["default"](styled.StyledWrapper, {}, void 0, /*#__PURE__*/jsxRuntime.jsx(styled.StyledChatContainerContentList, {
|
|
313
347
|
"data-testid": "chat-thread-container",
|
|
314
348
|
tabIndex: 0,
|
|
315
|
-
"aria-live": "polite",
|
|
316
|
-
"aria-relevant": "additions",
|
|
317
349
|
ref: virtualListRef,
|
|
318
350
|
role: "list",
|
|
319
351
|
onKeyDown: handleListKey,
|
|
320
352
|
children: /*#__PURE__*/_jsx__default["default"](styled.StyledChatContainerContentListScroll, {
|
|
321
353
|
height: "".concat(totalSize, "px")
|
|
322
|
-
}, void 0,
|
|
354
|
+
}, void 0, !isLoading && hasMoreItems ? /*#__PURE__*/_jsx__default["default"](styled.StyledLoadMoreContainer, {
|
|
323
355
|
hasMoreItems: hasMoreItems
|
|
324
356
|
}, void 0, /*#__PURE__*/_jsx__default["default"](DSButton__default["default"], {
|
|
325
357
|
labelText: "Load More",
|
|
@@ -327,8 +359,11 @@ const ChatContainerMessagesList = props$2 => {
|
|
|
327
359
|
onClick: getMoreMessages,
|
|
328
360
|
"data-testid": "ds-chat-load-more-btn",
|
|
329
361
|
innerRef: loadMoreRef
|
|
330
|
-
})) : null)
|
|
331
|
-
})
|
|
362
|
+
})) : null, render)
|
|
363
|
+
}), /*#__PURE__*/_jsx__default["default"](styled.StyledNewMessagesHiddenContainer, {
|
|
364
|
+
"aria-live": "polite",
|
|
365
|
+
"aria-hidden": "false"
|
|
366
|
+
}, void 0, newMessages.map(newMessage => /*#__PURE__*/_jsx__default["default"]("p", {}, void 0, newMessage.title, " ", newMessage.time, " ", newMessage.body, " ", newMessage.errorMessage, " ", newMessage.helpMessage))));
|
|
332
367
|
};
|
|
333
368
|
|
|
334
369
|
ChatContainerMessagesList.defaultProps = props.defaultProps;
|
|
@@ -20,19 +20,23 @@ const StyledWrapperListItem = /*#__PURE__*/styled__default["default"].div.withCo
|
|
|
20
20
|
const StyledChatContainerContentList = /*#__PURE__*/styled__default["default"].div.withConfig({
|
|
21
21
|
componentId: "sc-1jhprzr-2"
|
|
22
22
|
})(["width:100%;height:100%;padding:", ";padding-bottom:0;overflow-y:auto;overflow-x:hidden;"], props => props.theme.space.xs);
|
|
23
|
-
const
|
|
23
|
+
const StyledNewMessagesHiddenContainer = /*#__PURE__*/styled__default["default"].div.withConfig({
|
|
24
24
|
componentId: "sc-1jhprzr-3"
|
|
25
|
+
})(["position:absolute;width:1px;height:1px;margin:-1px;border:0;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(100%);white-space:nowrap;"]);
|
|
26
|
+
const StyledChatContainerContentListScroll = /*#__PURE__*/styled__default["default"].div.withConfig({
|
|
27
|
+
componentId: "sc-1jhprzr-4"
|
|
25
28
|
})(["width:100%;height:", ";position:relative;"], props => props.height);
|
|
26
29
|
const StyledLoadMoreContainer = /*#__PURE__*/styled__default["default"].div.withConfig({
|
|
27
|
-
componentId: "sc-1jhprzr-
|
|
30
|
+
componentId: "sc-1jhprzr-5"
|
|
28
31
|
})(["position:absolute;width:calc(100% + ", ");padding:", ";left:-", ";right:-", ";display:grid;justify-content:center;align-items:center;", " ", ""], props => props.theme.space.xxs, props => props.theme.space.xxs, props => props.theme.space.xxs, props => props.theme.space.xxs, props => props.hasMoreItems === 'top' ? 'top: 0;' : '', props => props.hasMoreItems === 'bottom' ? 'bottom: 0;' : '');
|
|
29
32
|
const ListItem = /*#__PURE__*/styled__default["default"].div.withConfig({
|
|
30
|
-
componentId: "sc-1jhprzr-
|
|
33
|
+
componentId: "sc-1jhprzr-6"
|
|
31
34
|
})(["&:focus,&:focus-visible{outline:none;&:after{content:'';display:block;position:absolute;top:0;left:-", ";pointer-events:none;border-radius:2px;background-color:#ebedf0;width:calc(100% + 24px);border:2px solid #1e79c2;height:100%;z-index:-1;}}"], props => props.theme.space.xxs);
|
|
32
35
|
|
|
33
36
|
exports.ListItem = ListItem;
|
|
34
37
|
exports.StyledChatContainerContentList = StyledChatContainerContentList;
|
|
35
38
|
exports.StyledChatContainerContentListScroll = StyledChatContainerContentListScroll;
|
|
36
39
|
exports.StyledLoadMoreContainer = StyledLoadMoreContainer;
|
|
40
|
+
exports.StyledNewMessagesHiddenContainer = StyledNewMessagesHiddenContainer;
|
|
37
41
|
exports.StyledWrapper = StyledWrapper;
|
|
38
42
|
exports.StyledWrapperListItem = StyledWrapperListItem;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
require('core-js/modules/web.dom-collections.iterator.js');
|
|
6
|
+
require('core-js/modules/esnext.async-iterator.for-each.js');
|
|
7
|
+
require('core-js/modules/esnext.iterator.constructor.js');
|
|
8
|
+
require('core-js/modules/esnext.iterator.for-each.js');
|
|
9
|
+
require('core-js/modules/esnext.async-iterator.map.js');
|
|
10
|
+
require('core-js/modules/esnext.iterator.map.js');
|
|
11
|
+
var React = require('react');
|
|
12
|
+
|
|
13
|
+
const useGetNewMessages = messages => {
|
|
14
|
+
const [seenMessages, setSeenMessages] = React.useState({});
|
|
15
|
+
const newMessages = React.useMemo(() => {
|
|
16
|
+
const list = [];
|
|
17
|
+
messages.forEach(message => {
|
|
18
|
+
if (!(message.dsId in seenMessages)) {
|
|
19
|
+
list.push(message);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
setSeenMessages(Object.fromEntries(messages.map(m => [m.dsId, true])));
|
|
23
|
+
return list; // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
24
|
+
}, [messages]);
|
|
25
|
+
return newMessages;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
exports.useGetNewMessages = useGetNewMessages;
|
package/esm/ChatDataTestids.js
CHANGED
|
@@ -17,7 +17,8 @@ const ChatDataTestIds = {
|
|
|
17
17
|
SYSTEM_MSG_TIME: 'chat-system-message-time',
|
|
18
18
|
BUBBLE: 'ds-chat-bubble',
|
|
19
19
|
BUBBLE_TITLE: 'chat-bubble-title',
|
|
20
|
-
BUBBLE_BODY: 'chat-bubble-body'
|
|
20
|
+
BUBBLE_BODY: 'chat-bubble-body',
|
|
21
|
+
BANNER: 'ds-banner-container'
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
export { ChatDataTestIds };
|
|
@@ -38,10 +38,12 @@ const ChatComposer = props => {
|
|
|
38
38
|
placeholder,
|
|
39
39
|
inputMaxLength,
|
|
40
40
|
maxLengthMessage,
|
|
41
|
+
ariaMaxLengthMessage,
|
|
41
42
|
buttonDisabled,
|
|
42
43
|
dataTestid,
|
|
43
44
|
actionRef
|
|
44
45
|
} = props;
|
|
46
|
+
const COMPOSER_MIN_HEIGHT = 32;
|
|
45
47
|
const input = React.useRef();
|
|
46
48
|
const inputSize = React.useRef();
|
|
47
49
|
const inputSend = React.useRef();
|
|
@@ -52,10 +54,10 @@ const ChatComposer = props => {
|
|
|
52
54
|
var _inputSize$current, _inputSize$current2, _inputSize$current3, _inputSize$current4;
|
|
53
55
|
|
|
54
56
|
const newHeight = ((_inputSize$current = inputSize.current) === null || _inputSize$current === void 0 ? void 0 : _inputSize$current.scrollHeight) > internalMaxHeight ? internalMaxHeight : (_inputSize$current2 = inputSize.current) === null || _inputSize$current2 === void 0 ? void 0 : _inputSize$current2.scrollHeight;
|
|
55
|
-
onResize(newHeight);
|
|
57
|
+
if (newHeight !== height && newHeight > COMPOSER_MIN_HEIGHT) onResize(newHeight);
|
|
56
58
|
setHeight(newHeight);
|
|
57
59
|
setShowScroll(((_inputSize$current3 = inputSize.current) === null || _inputSize$current3 === void 0 ? void 0 : _inputSize$current3.scrollHeight) - ((_inputSize$current4 = inputSize.current) === null || _inputSize$current4 === void 0 ? void 0 : _inputSize$current4.clientHeight) > 5);
|
|
58
|
-
}, [inputValue, internalMaxHeight, onResize]);
|
|
60
|
+
}, [inputValue, internalMaxHeight, height, onResize]);
|
|
59
61
|
React.useEffect(() => {
|
|
60
62
|
if (actionRef && actionRef.current) {
|
|
61
63
|
actionRef.current.focusToComposer = () => {
|
|
@@ -82,6 +84,9 @@ const ChatComposer = props => {
|
|
|
82
84
|
"data-testid": dataTestid
|
|
83
85
|
}, void 0, /*#__PURE__*/_jsx(Grid, {
|
|
84
86
|
cols: ['1fr', '24px'],
|
|
87
|
+
style: {
|
|
88
|
+
height: heightProps.height
|
|
89
|
+
},
|
|
85
90
|
justifyContent: "center",
|
|
86
91
|
alignItems: "flex-end",
|
|
87
92
|
gutter: "xxs"
|
|
@@ -95,7 +100,9 @@ const ChatComposer = props => {
|
|
|
95
100
|
onKeyDown: onKeyDown,
|
|
96
101
|
placeholder: placeholder,
|
|
97
102
|
"aria-label": "Message to send",
|
|
98
|
-
"data-testid": "chat-composer-textarea"
|
|
103
|
+
"data-testid": "chat-composer-textarea",
|
|
104
|
+
name: "chat-composer-textarea",
|
|
105
|
+
id: "chat-composer-textarea"
|
|
99
106
|
}), /*#__PURE__*/_jsx(Grid, {
|
|
100
107
|
height: "32px",
|
|
101
108
|
alignItems: "center"
|
|
@@ -122,8 +129,7 @@ const ChatComposer = props => {
|
|
|
122
129
|
width: (_input$current = input.current) === null || _input$current === void 0 ? void 0 : _input$current.clientWidth
|
|
123
130
|
},
|
|
124
131
|
onChange: handleChange,
|
|
125
|
-
value: inputValue
|
|
126
|
-
|
|
132
|
+
value: inputValue
|
|
127
133
|
}), /*#__PURE__*/_jsx(Grid, {
|
|
128
134
|
cols: ['1fr', '24px'],
|
|
129
135
|
justifyContent: "flex-end",
|
|
@@ -136,7 +142,7 @@ const ChatComposer = props => {
|
|
|
136
142
|
}, void 0, inputMaxLength && hasError ? /*#__PURE__*/jsxs(Fragment, {
|
|
137
143
|
children: [/*#__PURE__*/_jsx(StyledCounterErrorA11y, {
|
|
138
144
|
role: "alert"
|
|
139
|
-
}, void 0,
|
|
145
|
+
}, void 0, ariaMaxLengthMessage || "You have entered ".concat(inputLength, " characters and have exceeded the limit by ").concat(inputLength - inputMaxLength, ". Please limit to less than ").concat(inputMaxLength, " characters. ")), maxLengthMessage || "Please limit to less than ".concat(inputMaxLength, " characters. ")]
|
|
140
146
|
}) : '', inputMaxLength ? " ".concat(inputLength, " / ").concat(inputMaxLength) : ''), _Grid || (_Grid = /*#__PURE__*/_jsx(Grid, {}))));
|
|
141
147
|
};
|
|
142
148
|
|
|
@@ -4,6 +4,7 @@ import { noop } from 'lodash';
|
|
|
4
4
|
const defaultComposerPros = {
|
|
5
5
|
placeholder: 'Type your message',
|
|
6
6
|
inputValue: '',
|
|
7
|
+
ariaMaxLengthMessage: '',
|
|
7
8
|
onChange: noop,
|
|
8
9
|
onResize: noop,
|
|
9
10
|
onSend: noop,
|
|
@@ -20,6 +21,7 @@ const defaultComposerPros = {
|
|
|
20
21
|
const composerProps = {
|
|
21
22
|
placeholder: PropTypes.string.description('Input Placeholder').defaultValue(defaultComposerPros.placeholder),
|
|
22
23
|
inputValue: PropTypes.string.description('Input value').defaultValue(defaultComposerPros.inputValue),
|
|
24
|
+
ariaMaxLengthMessage: PropTypes.string.description('Aria message for Max length message when exceeded').defaultValue(defaultComposerPros.maxLengthMessage),
|
|
23
25
|
maxLengthMessage: PropTypes.string.description('Max length message').defaultValue(defaultComposerPros.maxLengthMessage),
|
|
24
26
|
onChange: PropTypes.func.description('Called when the user types').defaultValue(defaultComposerPros.onChange),
|
|
25
27
|
onResize: PropTypes.func.description('Called the input changes his height').defaultValue(defaultComposerPros.onResize),
|
|
@@ -17,14 +17,14 @@ import { StyledIndicatorContainer, StyledChatContainerContent, MobileStyledChatC
|
|
|
17
17
|
import { useValidateProps } from './config/useValidateProps.js';
|
|
18
18
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
19
19
|
|
|
20
|
-
var _DSIndeterminateProgr
|
|
20
|
+
var _DSIndeterminateProgr;
|
|
21
21
|
|
|
22
22
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
23
23
|
|
|
24
24
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
25
25
|
|
|
26
26
|
const ChatContainer = props => {
|
|
27
|
-
var _containerRef$current;
|
|
27
|
+
var _messagesListProps$me2, _containerRef$current;
|
|
28
28
|
|
|
29
29
|
useValidateProps(props, containerProps);
|
|
30
30
|
const {
|
|
@@ -55,18 +55,16 @@ const ChatContainer = props => {
|
|
|
55
55
|
});
|
|
56
56
|
const onResize = React.useCallback(height => {
|
|
57
57
|
if (onComposerResize) onComposerResize(height);
|
|
58
|
-
|
|
59
|
-
if (height > 30) {
|
|
60
|
-
/*
|
|
58
|
+
/*
|
|
61
59
|
setTimeout is used to equal the animation duration from the
|
|
62
60
|
composer text area when increase its size
|
|
63
61
|
*/
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
}
|
|
62
|
+
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
actionRef.current.scrollToIndex(messagesListProps.messages.length - 1, {
|
|
65
|
+
align: 'start'
|
|
66
|
+
});
|
|
67
|
+
}, 50);
|
|
70
68
|
}, [onComposerResize, messagesListProps.messages, actionRef]);
|
|
71
69
|
const Container = device === 'desktop' ? StyledChatContainerContent : MobileStyledChatContainerContent;
|
|
72
70
|
const handleTabs = React.useCallback(e => {
|
|
@@ -74,17 +72,23 @@ const ChatContainer = props => {
|
|
|
74
72
|
const ref = actionRef || listRef;
|
|
75
73
|
|
|
76
74
|
if (e.shiftKey && e.key === 'Tab') {
|
|
77
|
-
|
|
75
|
+
var _messagesListProps$me;
|
|
76
|
+
|
|
77
|
+
if (((_messagesListProps$me = messagesListProps.messages) === null || _messagesListProps$me === void 0 ? void 0 : _messagesListProps$me.length) > 0) {
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
|
|
80
|
+
if (['top', 'bottom'].includes(bannerPosition)) {
|
|
81
|
+
bannerRef.current.focusOnWrapper();
|
|
82
|
+
} else {
|
|
83
|
+
var _ref$current;
|
|
78
84
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const last = ref.current.getMessagesInView().pop();
|
|
83
|
-
ref.current.focusToIndexWithoutScroll(last);
|
|
85
|
+
const last = ref.current.getMessagesInView().pop();
|
|
86
|
+
(_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.focusToIndexWithoutScroll(last);
|
|
87
|
+
}
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
90
|
}
|
|
87
|
-
}, [actionRef, onComposerKeyDown, bannerPosition]); // Map actions
|
|
91
|
+
}, [actionRef, onComposerKeyDown, bannerPosition, (_messagesListProps$me2 = messagesListProps.messages) === null || _messagesListProps$me2 === void 0 ? void 0 : _messagesListProps$me2.length]); // Map actions
|
|
88
92
|
|
|
89
93
|
React.useEffect(() => {
|
|
90
94
|
if (actionRef && actionRef.current) {
|
|
@@ -134,15 +138,15 @@ const ChatContainer = props => {
|
|
|
134
138
|
isOpen: true,
|
|
135
139
|
type: bannerProps.type || BANNER_TYPES.INFO,
|
|
136
140
|
actionRef: bannerRef
|
|
137
|
-
})), sendingMessages && sendingMessages.length > 0 ?
|
|
141
|
+
})), sendingMessages && sendingMessages.length > 0 ? /*#__PURE__*/_jsx(StyledIndicatorContainer, {
|
|
138
142
|
isLoading: "bottom",
|
|
139
143
|
linear: true,
|
|
140
144
|
"data-testid": "ds-chat-loading-indicator"
|
|
141
145
|
}, void 0, /*#__PURE__*/_jsx(DSIndeterminateProgressIndicator, {
|
|
142
146
|
processing: true,
|
|
143
|
-
title:
|
|
147
|
+
title: sendingText,
|
|
144
148
|
lineOnly: true
|
|
145
|
-
}))
|
|
149
|
+
})) : null), /*#__PURE__*/jsx(ChatComposer, _objectSpread(_objectSpread({}, composerProps), {}, {
|
|
146
150
|
onKeyDown: handleTabs,
|
|
147
151
|
onResize: onResize,
|
|
148
152
|
maxHeight: (((_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.clientHeight) || 0) / 2,
|
|
@@ -10,7 +10,7 @@ const StyledChatContainerContent = /*#__PURE__*/styled(Grid).withConfig({
|
|
|
10
10
|
})(["height:100%;min-width:", "px;overflow:hidden;background:", ";position:relative;"], CONTAINER_WIDTH, props => props.theme.colors.neutral['050']);
|
|
11
11
|
const StyledIndicatorContainer = /*#__PURE__*/styled.div.withConfig({
|
|
12
12
|
componentId: "sc-d732wo-1"
|
|
13
|
-
})(["transition:0.4s ease-in-out;position:absolute;visibility:hidden;pointer-events:none;width:100%;padding:0 ", ";left:0;right:0;background:", ";", " ", " & div,a{font-size:13px;}"], props => !props.linear ? props.theme.space.xxs : 0, props => props.theme.colors.neutral['000'], props => props.isLoading === 'top' ? "\n top: -41px;\n transform: translate(0, 41px);\n visibility: visible;\n pointer-events: auto
|
|
13
|
+
})(["transition:0.4s ease-in-out;position:absolute;visibility:hidden;pointer-events:none;width:100%;padding:0 ", ";left:0;right:0;background:", ";", " ", " & div,a{font-size:13px;}"], props => !props.linear ? props.theme.space.xxs : 0, props => props.theme.colors.neutral['000'], props => props.isLoading === 'top' ? "\n top: -41px;\n transform: translate(0, 41px);\n visibility: visible;\n pointer-events: auto;\n " : '', props => props.isLoading === 'bottom' ? "\n bottom: -41px;\n transform: translate(0, -41px);\n visibility: visible;\n pointer-events: auto;\n " : '');
|
|
14
14
|
const MobileStyledChatContainerContent = /*#__PURE__*/styled(StyledChatContainerContent).withConfig({
|
|
15
15
|
componentId: "sc-d732wo-2"
|
|
16
16
|
})(["", ",", "{font-size:16px;}", "{font-size:13px;}", "{background-color:#fff;box-shadow:-0 1px 3px rgba(53,60,70,0.8);}", ",", "{font-size:16px;text-align:left;}", "{font-size:13px;}", "{font-size:16px;}", "{font-size:16px;}"], StyledTitleLeftBubble, StyleBodyWrapper, StyledTitleRightBubble, Wrapper, Title, Message, Time, StyledComposerInput, StyledIndicatorContainer);
|
|
@@ -15,13 +15,15 @@ import React from 'react';
|
|
|
15
15
|
import { describe } from 'react-desc';
|
|
16
16
|
import { useVirtual } from 'react-virtual';
|
|
17
17
|
import DSButton from '@elliemae/ds-button';
|
|
18
|
+
import { debounce } from 'lodash';
|
|
18
19
|
import { ChatBubble } from '../bubble/ChatBubble.js';
|
|
19
20
|
import { BUBBLE_TYPES } from '../bubble/props.js';
|
|
20
21
|
import { MessageDelimiter } from '../message-delimiter/MessageDelimiter.js';
|
|
21
22
|
import { SystemMessage } from '../system-message/SystemMessage.js';
|
|
22
|
-
import { ListItem, StyledWrapperListItem, StyledWrapper, StyledChatContainerContentList, StyledChatContainerContentListScroll, StyledLoadMoreContainer } from './styled.js';
|
|
23
|
+
import { ListItem, StyledWrapperListItem, StyledWrapper, StyledChatContainerContentList, StyledChatContainerContentListScroll, StyledLoadMoreContainer, StyledNewMessagesHiddenContainer } from './styled.js';
|
|
23
24
|
import { listProps, defaultProps } from './props.js';
|
|
24
25
|
import { useValidateProps } from './config/useValidateProps.js';
|
|
26
|
+
import { useGetNewMessages } from './useGetNewMessages.js';
|
|
25
27
|
import { jsx } from 'react/jsx-runtime';
|
|
26
28
|
|
|
27
29
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
@@ -56,6 +58,7 @@ const ChatContainerMessagesList = props => {
|
|
|
56
58
|
isLoading,
|
|
57
59
|
bannerRef
|
|
58
60
|
} = props;
|
|
61
|
+
const pendingOnScroll = React.useRef(false);
|
|
59
62
|
const loadMoreRef = React.useRef(null);
|
|
60
63
|
const virtualListRef = React.useRef(); // estimateSize should not be really required given what was stated on
|
|
61
64
|
// https://github.com/tannerlinsley/react-virtual/issues/23
|
|
@@ -75,9 +78,17 @@ const ChatContainerMessagesList = props => {
|
|
|
75
78
|
})), {}, {
|
|
76
79
|
estimateSize: React.useCallback(() => 80, [])
|
|
77
80
|
}));
|
|
78
|
-
const
|
|
81
|
+
const newMessages = useGetNewMessages(messages);
|
|
82
|
+
const debouncedScroll = React.useMemo(() => debounce(() => {
|
|
79
83
|
var _virtualListRef$curre;
|
|
80
84
|
|
|
85
|
+
if (document.visibilityState === 'hidden') {
|
|
86
|
+
pendingOnScroll.current = true;
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!virtualListRef.current) return [];
|
|
91
|
+
pendingOnScroll.current = false;
|
|
81
92
|
const {
|
|
82
93
|
bottom,
|
|
83
94
|
top
|
|
@@ -86,15 +97,15 @@ const ChatContainerMessagesList = props => {
|
|
|
86
97
|
const indexList = [];
|
|
87
98
|
list.forEach(item => {
|
|
88
99
|
const {
|
|
89
|
-
top: itemTop
|
|
90
|
-
bottom: itemBottom
|
|
100
|
+
top: itemTop
|
|
91
101
|
} = item.getBoundingClientRect();
|
|
92
102
|
const itemPadding = 10;
|
|
93
|
-
if (itemTop + itemPadding < bottom &&
|
|
103
|
+
if (itemTop + itemPadding < bottom && itemTop + itemPadding > top) indexList.push(item.dataset.index);
|
|
94
104
|
});
|
|
95
105
|
if (onScrollEnds) onScrollEnds(indexList);
|
|
96
106
|
return indexList;
|
|
97
|
-
}, [onScrollEnds]);
|
|
107
|
+
}, 500), [onScrollEnds]);
|
|
108
|
+
const onScrollStop = React.useCallback(debouncedScroll, [debouncedScroll]);
|
|
98
109
|
const onlyFocus = React.useCallback(index => {
|
|
99
110
|
setTimeout(() => {
|
|
100
111
|
virtualListRef.current.querySelector("[data-index=\"".concat(index, "\"]")).focus();
|
|
@@ -106,7 +117,8 @@ const ChatContainerMessagesList = props => {
|
|
|
106
117
|
};
|
|
107
118
|
vScroll(index, b);
|
|
108
119
|
onlyFocus(index);
|
|
109
|
-
|
|
120
|
+
onScrollStop();
|
|
121
|
+
}, [onScrollStop, onlyFocus, vScroll]);
|
|
110
122
|
React.useEffect(() => {
|
|
111
123
|
if (actionRef && actionRef.current) {
|
|
112
124
|
actionRef.current.scrollToIndex = function (a) {
|
|
@@ -114,9 +126,11 @@ const ChatContainerMessagesList = props => {
|
|
|
114
126
|
align: 'center'
|
|
115
127
|
};
|
|
116
128
|
vScroll(a, b);
|
|
129
|
+
onScrollStop();
|
|
117
130
|
};
|
|
118
131
|
|
|
119
132
|
actionRef.current.getMessagesInView = onScrollStop;
|
|
133
|
+
onScrollStop(); // init useMemo due to debounce use.
|
|
120
134
|
|
|
121
135
|
actionRef.current.focusToIndex = function (a) {
|
|
122
136
|
let b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
|
|
@@ -127,7 +141,7 @@ const ChatContainerMessagesList = props => {
|
|
|
127
141
|
|
|
128
142
|
actionRef.current.focusToIndexWithoutScroll = onlyFocus;
|
|
129
143
|
|
|
130
|
-
actionRef.current.focusToList =
|
|
144
|
+
actionRef.current.focusToList = () => {
|
|
131
145
|
var _virtualListRef$curre2;
|
|
132
146
|
|
|
133
147
|
(_virtualListRef$curre2 = virtualListRef.current) === null || _virtualListRef$curre2 === void 0 ? void 0 : _virtualListRef$curre2.focus();
|
|
@@ -144,7 +158,17 @@ const ChatContainerMessagesList = props => {
|
|
|
144
158
|
switch (key) {
|
|
145
159
|
case 'ArrowUp':
|
|
146
160
|
e.preventDefault();
|
|
147
|
-
|
|
161
|
+
|
|
162
|
+
if (index > 0) {
|
|
163
|
+
var _virtualListRef$curre3;
|
|
164
|
+
|
|
165
|
+
((_virtualListRef$curre3 = virtualListRef.current) === null || _virtualListRef$curre3 === void 0 ? void 0 : _virtualListRef$curre3.querySelector("[data-index=\"".concat(index - 1, "\"]"))).focus();
|
|
166
|
+
} else if (hasMoreItems === 'top') {
|
|
167
|
+
var _loadMoreRef$current;
|
|
168
|
+
|
|
169
|
+
(_loadMoreRef$current = loadMoreRef.current) === null || _loadMoreRef$current === void 0 ? void 0 : _loadMoreRef$current.focus();
|
|
170
|
+
}
|
|
171
|
+
|
|
148
172
|
break;
|
|
149
173
|
|
|
150
174
|
case 'ArrowDown':
|
|
@@ -167,31 +191,31 @@ const ChatContainerMessagesList = props => {
|
|
|
167
191
|
break;
|
|
168
192
|
|
|
169
193
|
case 'Tab':
|
|
170
|
-
e.preventDefault();
|
|
171
|
-
|
|
172
194
|
if (hasMoreItems === 'bottom' && !e.shiftKey) {
|
|
173
|
-
var _loadMoreRef$current;
|
|
174
|
-
|
|
175
|
-
(_loadMoreRef$current = loadMoreRef.current) === null || _loadMoreRef$current === void 0 ? void 0 : _loadMoreRef$current.focus();
|
|
176
|
-
} else if (hasMoreItems === 'top' && e.shiftKey) {
|
|
177
195
|
var _loadMoreRef$current2;
|
|
178
196
|
|
|
197
|
+
e.preventDefault();
|
|
179
198
|
(_loadMoreRef$current2 = loadMoreRef.current) === null || _loadMoreRef$current2 === void 0 ? void 0 : _loadMoreRef$current2.focus();
|
|
199
|
+
} else if (hasMoreItems === 'top' && e.shiftKey) {
|
|
200
|
+
var _loadMoreRef$current3;
|
|
201
|
+
|
|
202
|
+
e.preventDefault();
|
|
203
|
+
(_loadMoreRef$current3 = loadMoreRef.current) === null || _loadMoreRef$current3 === void 0 ? void 0 : _loadMoreRef$current3.focus();
|
|
180
204
|
} else if (!e.shiftKey) {
|
|
181
205
|
if (bannerPosition) {
|
|
206
|
+
e.preventDefault();
|
|
182
207
|
bannerRef.current.focusOnWrapper();
|
|
183
|
-
} else {
|
|
184
|
-
actionRef.current.focusToComposer();
|
|
185
208
|
}
|
|
186
209
|
} else {
|
|
187
|
-
var _virtualListRef$
|
|
210
|
+
var _virtualListRef$curre4;
|
|
188
211
|
|
|
189
|
-
|
|
212
|
+
e.preventDefault();
|
|
213
|
+
(_virtualListRef$curre4 = virtualListRef.current) === null || _virtualListRef$curre4 === void 0 ? void 0 : _virtualListRef$curre4.focus();
|
|
190
214
|
}
|
|
191
215
|
|
|
192
216
|
break;
|
|
193
217
|
}
|
|
194
|
-
}, [
|
|
218
|
+
}, [bannerRef, hasMoreItems, messages.length, bannerPosition, scrollAndFocus]);
|
|
195
219
|
const handleListKey = React.useCallback(e => {
|
|
196
220
|
const {
|
|
197
221
|
key
|
|
@@ -200,12 +224,22 @@ const ChatContainerMessagesList = props => {
|
|
|
200
224
|
switch (key) {
|
|
201
225
|
case 'ArrowUp':
|
|
202
226
|
e.preventDefault();
|
|
203
|
-
|
|
227
|
+
|
|
228
|
+
if (document.activeElement !== loadMoreRef.current) {
|
|
229
|
+
onlyFocus(onScrollStop().shift());
|
|
230
|
+
}
|
|
231
|
+
|
|
204
232
|
break;
|
|
205
233
|
|
|
206
234
|
case 'ArrowDown':
|
|
207
235
|
e.preventDefault();
|
|
208
|
-
|
|
236
|
+
|
|
237
|
+
if (document.activeElement === loadMoreRef.current) {
|
|
238
|
+
virtualListRef.current.querySelector("[data-index=\"0\"]").focus();
|
|
239
|
+
} else {
|
|
240
|
+
onlyFocus(onScrollStop().pop());
|
|
241
|
+
}
|
|
242
|
+
|
|
209
243
|
break;
|
|
210
244
|
|
|
211
245
|
case 'Home':
|
|
@@ -221,11 +255,6 @@ const ChatContainerMessagesList = props => {
|
|
|
221
255
|
case 'Tab':
|
|
222
256
|
if (document.activeElement !== loadMoreRef.current) {
|
|
223
257
|
if (hasMoreItems === 'bottom' && !e.shiftKey) {
|
|
224
|
-
var _loadMoreRef$current3;
|
|
225
|
-
|
|
226
|
-
e.preventDefault();
|
|
227
|
-
(_loadMoreRef$current3 = loadMoreRef.current) === null || _loadMoreRef$current3 === void 0 ? void 0 : _loadMoreRef$current3.focus();
|
|
228
|
-
} else if (hasMoreItems === 'top' && e.shiftKey) {
|
|
229
258
|
var _loadMoreRef$current4;
|
|
230
259
|
|
|
231
260
|
e.preventDefault();
|
|
@@ -238,15 +267,18 @@ const ChatContainerMessagesList = props => {
|
|
|
238
267
|
e.preventDefault();
|
|
239
268
|
actionRef.current.focusToComposer();
|
|
240
269
|
} else {
|
|
241
|
-
var _virtualListRef$
|
|
270
|
+
var _virtualListRef$curre5;
|
|
242
271
|
|
|
243
272
|
e.preventDefault();
|
|
244
|
-
(_virtualListRef$
|
|
273
|
+
(_virtualListRef$curre5 = virtualListRef.current) === null || _virtualListRef$curre5 === void 0 ? void 0 : _virtualListRef$curre5.focus();
|
|
245
274
|
}
|
|
246
275
|
|
|
247
276
|
break;
|
|
248
277
|
}
|
|
249
278
|
}, [actionRef, hasMoreItems, messages.length, onScrollStop, onlyFocus, scrollAndFocus]);
|
|
279
|
+
const checkPendingOnScroll = React.useCallback(() => {
|
|
280
|
+
if (pendingOnScroll.current === true) onScrollStop();
|
|
281
|
+
}, [onScrollStop]);
|
|
250
282
|
React.useEffect(() => {
|
|
251
283
|
let handlerCallback = null;
|
|
252
284
|
const localVirtualRef = virtualListRef.current;
|
|
@@ -260,11 +292,13 @@ const ChatContainerMessagesList = props => {
|
|
|
260
292
|
virtualListRef.current.addEventListener('scroll', onScroll);
|
|
261
293
|
}
|
|
262
294
|
|
|
295
|
+
document.addEventListener('visibilitychange', checkPendingOnScroll);
|
|
263
296
|
return () => {
|
|
264
297
|
if (handlerCallback) clearTimeout(handlerCallback);
|
|
265
298
|
if (onScrollEnds) localVirtualRef.removeEventListener('scroll', onScroll);
|
|
299
|
+
document.removeEventListener('visibilitychange', checkPendingOnScroll);
|
|
266
300
|
};
|
|
267
|
-
}, [onScrollStop, onScrollEnds]);
|
|
301
|
+
}, [onScrollStop, onScrollEnds, checkPendingOnScroll]);
|
|
268
302
|
const render = React.useMemo(() => virtualItems.map(virtualItem => {
|
|
269
303
|
const {
|
|
270
304
|
index,
|
|
@@ -285,7 +319,7 @@ const ChatContainerMessagesList = props => {
|
|
|
285
319
|
"data-index": index,
|
|
286
320
|
style: style,
|
|
287
321
|
ref: measureRef,
|
|
288
|
-
tabIndex:
|
|
322
|
+
tabIndex: -1,
|
|
289
323
|
onKeyDown: handleKey,
|
|
290
324
|
role: "listitem",
|
|
291
325
|
children: /*#__PURE__*/_jsx(StyledWrapperListItem, {
|
|
@@ -301,14 +335,12 @@ const ChatContainerMessagesList = props => {
|
|
|
301
335
|
return /*#__PURE__*/_jsx(StyledWrapper, {}, void 0, /*#__PURE__*/jsx(StyledChatContainerContentList, {
|
|
302
336
|
"data-testid": "chat-thread-container",
|
|
303
337
|
tabIndex: 0,
|
|
304
|
-
"aria-live": "polite",
|
|
305
|
-
"aria-relevant": "additions",
|
|
306
338
|
ref: virtualListRef,
|
|
307
339
|
role: "list",
|
|
308
340
|
onKeyDown: handleListKey,
|
|
309
341
|
children: /*#__PURE__*/_jsx(StyledChatContainerContentListScroll, {
|
|
310
342
|
height: "".concat(totalSize, "px")
|
|
311
|
-
}, void 0,
|
|
343
|
+
}, void 0, !isLoading && hasMoreItems ? /*#__PURE__*/_jsx(StyledLoadMoreContainer, {
|
|
312
344
|
hasMoreItems: hasMoreItems
|
|
313
345
|
}, void 0, /*#__PURE__*/_jsx(DSButton, {
|
|
314
346
|
labelText: "Load More",
|
|
@@ -316,8 +348,11 @@ const ChatContainerMessagesList = props => {
|
|
|
316
348
|
onClick: getMoreMessages,
|
|
317
349
|
"data-testid": "ds-chat-load-more-btn",
|
|
318
350
|
innerRef: loadMoreRef
|
|
319
|
-
})) : null)
|
|
320
|
-
})
|
|
351
|
+
})) : null, render)
|
|
352
|
+
}), /*#__PURE__*/_jsx(StyledNewMessagesHiddenContainer, {
|
|
353
|
+
"aria-live": "polite",
|
|
354
|
+
"aria-hidden": "false"
|
|
355
|
+
}, void 0, newMessages.map(newMessage => /*#__PURE__*/_jsx("p", {}, void 0, newMessage.title, " ", newMessage.time, " ", newMessage.body, " ", newMessage.errorMessage, " ", newMessage.helpMessage))));
|
|
321
356
|
};
|
|
322
357
|
|
|
323
358
|
ChatContainerMessagesList.defaultProps = defaultProps;
|
|
@@ -12,14 +12,17 @@ const StyledWrapperListItem = /*#__PURE__*/styled.div.withConfig({
|
|
|
12
12
|
const StyledChatContainerContentList = /*#__PURE__*/styled.div.withConfig({
|
|
13
13
|
componentId: "sc-1jhprzr-2"
|
|
14
14
|
})(["width:100%;height:100%;padding:", ";padding-bottom:0;overflow-y:auto;overflow-x:hidden;"], props => props.theme.space.xs);
|
|
15
|
-
const
|
|
15
|
+
const StyledNewMessagesHiddenContainer = /*#__PURE__*/styled.div.withConfig({
|
|
16
16
|
componentId: "sc-1jhprzr-3"
|
|
17
|
+
})(["position:absolute;width:1px;height:1px;margin:-1px;border:0;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(100%);white-space:nowrap;"]);
|
|
18
|
+
const StyledChatContainerContentListScroll = /*#__PURE__*/styled.div.withConfig({
|
|
19
|
+
componentId: "sc-1jhprzr-4"
|
|
17
20
|
})(["width:100%;height:", ";position:relative;"], props => props.height);
|
|
18
21
|
const StyledLoadMoreContainer = /*#__PURE__*/styled.div.withConfig({
|
|
19
|
-
componentId: "sc-1jhprzr-
|
|
22
|
+
componentId: "sc-1jhprzr-5"
|
|
20
23
|
})(["position:absolute;width:calc(100% + ", ");padding:", ";left:-", ";right:-", ";display:grid;justify-content:center;align-items:center;", " ", ""], props => props.theme.space.xxs, props => props.theme.space.xxs, props => props.theme.space.xxs, props => props.theme.space.xxs, props => props.hasMoreItems === 'top' ? 'top: 0;' : '', props => props.hasMoreItems === 'bottom' ? 'bottom: 0;' : '');
|
|
21
24
|
const ListItem = /*#__PURE__*/styled.div.withConfig({
|
|
22
|
-
componentId: "sc-1jhprzr-
|
|
25
|
+
componentId: "sc-1jhprzr-6"
|
|
23
26
|
})(["&:focus,&:focus-visible{outline:none;&:after{content:'';display:block;position:absolute;top:0;left:-", ";pointer-events:none;border-radius:2px;background-color:#ebedf0;width:calc(100% + 24px);border:2px solid #1e79c2;height:100%;z-index:-1;}}"], props => props.theme.space.xxs);
|
|
24
27
|
|
|
25
|
-
export { ListItem, StyledChatContainerContentList, StyledChatContainerContentListScroll, StyledLoadMoreContainer, StyledWrapper, StyledWrapperListItem };
|
|
28
|
+
export { ListItem, StyledChatContainerContentList, StyledChatContainerContentListScroll, StyledLoadMoreContainer, StyledNewMessagesHiddenContainer, StyledWrapper, StyledWrapperListItem };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import 'core-js/modules/web.dom-collections.iterator.js';
|
|
2
|
+
import 'core-js/modules/esnext.async-iterator.for-each.js';
|
|
3
|
+
import 'core-js/modules/esnext.iterator.constructor.js';
|
|
4
|
+
import 'core-js/modules/esnext.iterator.for-each.js';
|
|
5
|
+
import 'core-js/modules/esnext.async-iterator.map.js';
|
|
6
|
+
import 'core-js/modules/esnext.iterator.map.js';
|
|
7
|
+
import { useState, useMemo } from 'react';
|
|
8
|
+
|
|
9
|
+
const useGetNewMessages = messages => {
|
|
10
|
+
const [seenMessages, setSeenMessages] = useState({});
|
|
11
|
+
const newMessages = useMemo(() => {
|
|
12
|
+
const list = [];
|
|
13
|
+
messages.forEach(message => {
|
|
14
|
+
if (!(message.dsId in seenMessages)) {
|
|
15
|
+
list.push(message);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
setSeenMessages(Object.fromEntries(messages.map(m => [m.dsId, true])));
|
|
19
|
+
return list; // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
20
|
+
}, [messages]);
|
|
21
|
+
return newMessages;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { useGetNewMessages };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elliemae/ds-chat",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "ICE MT - Dimsum - Chat",
|
|
6
6
|
"module": "./esm/index.js",
|
|
@@ -183,6 +183,10 @@
|
|
|
183
183
|
"import": "./esm/parts/chat-container/ChatContainer.js",
|
|
184
184
|
"require": "./cjs/parts/chat-container/ChatContainer.js"
|
|
185
185
|
},
|
|
186
|
+
"./parts/chat-container-messages-list/useGetNewMessages": {
|
|
187
|
+
"import": "./esm/parts/chat-container-messages-list/useGetNewMessages.js",
|
|
188
|
+
"require": "./cjs/parts/chat-container-messages-list/useGetNewMessages.js"
|
|
189
|
+
},
|
|
186
190
|
"./parts/chat-container-messages-list/styled": {
|
|
187
191
|
"import": "./esm/parts/chat-container-messages-list/styled.js",
|
|
188
192
|
"require": "./cjs/parts/chat-container-messages-list/styled.js"
|
|
@@ -305,15 +309,15 @@
|
|
|
305
309
|
"generateSubmodules": true
|
|
306
310
|
},
|
|
307
311
|
"dependencies": {
|
|
308
|
-
"@elliemae/ds-banner": "2.0.
|
|
309
|
-
"@elliemae/ds-button": "2.0.
|
|
310
|
-
"@elliemae/ds-grid": "2.0.
|
|
311
|
-
"@elliemae/ds-icons": "2.0.
|
|
312
|
-
"@elliemae/ds-indeterminate-progress-indicator": "2.0.
|
|
313
|
-
"@elliemae/ds-props-helpers": "2.0.
|
|
314
|
-
"@elliemae/ds-system": "2.0.
|
|
315
|
-
"@elliemae/ds-tooltip": "2.0.
|
|
316
|
-
"@elliemae/ds-truncated-tooltip-text": "2.0.
|
|
312
|
+
"@elliemae/ds-banner": "2.0.6",
|
|
313
|
+
"@elliemae/ds-button": "2.0.6",
|
|
314
|
+
"@elliemae/ds-grid": "2.0.6",
|
|
315
|
+
"@elliemae/ds-icons": "2.0.6",
|
|
316
|
+
"@elliemae/ds-indeterminate-progress-indicator": "2.0.6",
|
|
317
|
+
"@elliemae/ds-props-helpers": "2.0.6",
|
|
318
|
+
"@elliemae/ds-system": "2.0.6",
|
|
319
|
+
"@elliemae/ds-tooltip": "2.0.6",
|
|
320
|
+
"@elliemae/ds-truncated-tooltip-text": "2.0.6",
|
|
317
321
|
"prop-types": "~15.7.2",
|
|
318
322
|
"react-desc": "~4.1.2",
|
|
319
323
|
"react-virtual": "~2.3.2"
|
|
@@ -10,6 +10,9 @@ declare const ChatComposer: {
|
|
|
10
10
|
inputValue: {
|
|
11
11
|
deprecated: import("react-desc").PropTypesDescValidator;
|
|
12
12
|
};
|
|
13
|
+
ariaMaxLengthMessage: {
|
|
14
|
+
deprecated: import("react-desc").PropTypesDescValidator;
|
|
15
|
+
};
|
|
13
16
|
maxLengthMessage: {
|
|
14
17
|
deprecated: import("react-desc").PropTypesDescValidator;
|
|
15
18
|
};
|
|
@@ -2,6 +2,7 @@ import { BubbleProps } from '../bubble/props';
|
|
|
2
2
|
export declare const StyledWrapper: import("styled-components").StyledComponent<"div", import("styled-components").DefaultTheme, {}, never>;
|
|
3
3
|
export declare const StyledWrapperListItem: import("styled-components").StyledComponent<"div", import("styled-components").DefaultTheme, Partial<BubbleProps>, never>;
|
|
4
4
|
export declare const StyledChatContainerContentList: import("styled-components").StyledComponent<"div", import("styled-components").DefaultTheme, {}, never>;
|
|
5
|
+
export declare const StyledNewMessagesHiddenContainer: import("styled-components").StyledComponent<"div", import("styled-components").DefaultTheme, {}, never>;
|
|
5
6
|
export declare const StyledChatContainerContentListScroll: import("styled-components").StyledComponent<"div", import("styled-components").DefaultTheme, {
|
|
6
7
|
height: string;
|
|
7
8
|
}, never>;
|
package/types/tests/utils.d.ts
CHANGED
|
@@ -1,27 +1 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
dsId: string;
|
|
3
|
-
body: string;
|
|
4
|
-
type: string;
|
|
5
|
-
title: string;
|
|
6
|
-
time: string;
|
|
7
|
-
extraProps?: undefined;
|
|
8
|
-
helpMessage?: undefined;
|
|
9
|
-
} | {
|
|
10
|
-
dsId: string;
|
|
11
|
-
type: string;
|
|
12
|
-
title: string;
|
|
13
|
-
extraProps: {
|
|
14
|
-
'data-testid': string;
|
|
15
|
-
};
|
|
16
|
-
body?: undefined;
|
|
17
|
-
time?: undefined;
|
|
18
|
-
helpMessage?: undefined;
|
|
19
|
-
} | {
|
|
20
|
-
dsId: string;
|
|
21
|
-
body: string;
|
|
22
|
-
type: string;
|
|
23
|
-
title: string;
|
|
24
|
-
time: string;
|
|
25
|
-
helpMessage: string | null;
|
|
26
|
-
extraProps?: undefined;
|
|
27
|
-
})[];
|
|
1
|
+
export declare const loadMsgs: (q: number) => unknown;
|