@elliemae/ds-chat-container 3.80.0-next.30 → 3.80.0-next.35
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/dist/cjs/DSChatContainerDefinitions.js +7 -1
- package/dist/cjs/DSChatContainerDefinitions.js.map +2 -2
- package/dist/cjs/parts/chat-container-messages-list/ChatContainerMessagesList.js +134 -71
- package/dist/cjs/parts/chat-container-messages-list/ChatContainerMessagesList.js.map +2 -2
- package/dist/cjs/parts/chat-container-messages-list/styled.js +33 -43
- package/dist/cjs/parts/chat-container-messages-list/styled.js.map +2 -2
- package/dist/cjs/parts/chat-container-messages-list/useActionRef.js +3 -9
- package/dist/cjs/parts/chat-container-messages-list/useActionRef.js.map +2 -2
- package/dist/cjs/parts/chat-container-messages-list/useChatContainerMessageList.js +30 -22
- package/dist/cjs/parts/chat-container-messages-list/useChatContainerMessageList.js.map +2 -2
- package/dist/cjs/parts/chat-container-messages-list/useFocusAndScrollHelpers.js +38 -38
- package/dist/cjs/parts/chat-container-messages-list/useFocusAndScrollHelpers.js.map +2 -2
- package/dist/cjs/parts/chat-container-messages-list/useGlobalEventListener.js.map +1 -1
- package/dist/cjs/parts/chat-container-messages-list/useKeyboard.js +8 -15
- package/dist/cjs/parts/chat-container-messages-list/useKeyboard.js.map +2 -2
- package/dist/cjs/parts/chat-container-messages-list/useMessageFocusTracker.js +132 -0
- package/dist/cjs/parts/chat-container-messages-list/useMessageFocusTracker.js.map +7 -0
- package/dist/cjs/react-desc-prop-types.js +39 -23
- package/dist/cjs/react-desc-prop-types.js.map +2 -2
- package/dist/cjs/typescript-testing/typescript-chat-container-valid.js +31 -0
- package/dist/cjs/typescript-testing/typescript-chat-container-valid.js.map +2 -2
- package/dist/esm/DSChatContainerDefinitions.js +7 -1
- package/dist/esm/DSChatContainerDefinitions.js.map +2 -2
- package/dist/esm/parts/chat-container-messages-list/ChatContainerMessagesList.js +135 -74
- package/dist/esm/parts/chat-container-messages-list/ChatContainerMessagesList.js.map +2 -2
- package/dist/esm/parts/chat-container-messages-list/styled.js +33 -43
- package/dist/esm/parts/chat-container-messages-list/styled.js.map +2 -2
- package/dist/esm/parts/chat-container-messages-list/useActionRef.js +3 -9
- package/dist/esm/parts/chat-container-messages-list/useActionRef.js.map +2 -2
- package/dist/esm/parts/chat-container-messages-list/useChatContainerMessageList.js +30 -22
- package/dist/esm/parts/chat-container-messages-list/useChatContainerMessageList.js.map +2 -2
- package/dist/esm/parts/chat-container-messages-list/useFocusAndScrollHelpers.js +39 -39
- package/dist/esm/parts/chat-container-messages-list/useFocusAndScrollHelpers.js.map +2 -2
- package/dist/esm/parts/chat-container-messages-list/useGlobalEventListener.js.map +1 -1
- package/dist/esm/parts/chat-container-messages-list/useKeyboard.js +8 -15
- package/dist/esm/parts/chat-container-messages-list/useKeyboard.js.map +2 -2
- package/dist/esm/parts/chat-container-messages-list/useMessageFocusTracker.js +102 -0
- package/dist/esm/parts/chat-container-messages-list/useMessageFocusTracker.js.map +7 -0
- package/dist/esm/react-desc-prop-types.js +40 -24
- package/dist/esm/react-desc-prop-types.js.map +2 -2
- package/dist/esm/typescript-testing/typescript-chat-container-valid.js +31 -0
- package/dist/esm/typescript-testing/typescript-chat-container-valid.js.map +2 -2
- package/dist/types/DSChatContainerDefinitions.d.ts +8 -0
- package/dist/types/parts/chat-container-messages-list/styled.d.ts +18 -5
- package/dist/types/parts/chat-container-messages-list/useActionRef.d.ts +4 -6
- package/dist/types/parts/chat-container-messages-list/useChatContainerMessageList.d.ts +7 -2
- package/dist/types/parts/chat-container-messages-list/useFocusAndScrollHelpers.d.ts +9 -5
- package/dist/types/parts/chat-container-messages-list/useGlobalEventListener.d.ts +1 -1
- package/dist/types/parts/chat-container-messages-list/useKeyboard.d.ts +7 -3
- package/dist/types/parts/chat-container-messages-list/useMessageFocusTracker.d.ts +52 -0
- package/dist/types/react-desc-prop-types.d.ts +798 -793
- package/package.json +16 -16
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/chat-container-messages-list/ChatContainerMessagesList.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable complexity */\n/* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n/* eslint-disable max-lines */\nimport type { CSSProperties, WeakValidationMap } from 'react';\nimport React from 'react';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { DSButtonV2, type DSButtonV2T } from '@elliemae/ds-button-v2';\nimport { ChatBubble, BUBBLE_TYPES } from '@elliemae/ds-chat-bubble';\nimport type { DSChatMessageDelimeterT } from '@elliemae/ds-chat-message-delimeter';\nimport { MessageDelimiter } from '@elliemae/ds-chat-message-delimeter';\nimport type { DSChatSystemMessageT } from '@elliemae/ds-chat-system-message';\nimport { SystemMessage } from '@elliemae/ds-chat-system-message';\nimport {\n StyledWrapperListItem,\n StyledChatContainerContentList,\n StyledChatContainerContentListScroll,\n StyledWrapper,\n StyledLoadMoreContainer,\n ListItem,\n StyledNewMessagesHiddenContainer,\n} from './styled.js';\nimport type { DSChatT } from '../../react-desc-prop-types.js';\nimport { ChatContainerMessagesListPropTypes } from '../../react-desc-prop-types.js';\nimport { useGetNewMessages } from './useGetNewMessages.js';\nimport { useChatContainerMessageList } from './useChatContainerMessageList.js';\nimport { ChatContainerDataTestIds } from '../../ChatContainerDataTestids.js';\n\ntype ButtonV2InnerRefT = DSButtonV2T.Props['innerRef'];\n\nconst ChatContainerMessagesList: React.ComponentType<DSChatT.MessagesListProps> = (props) => {\n const {\n propsWithDefault,\n globalAttributes,\n useVirtualHelpers,\n handleKey,\n handleListKey,\n loadMoreRef,\n virtualListRef,\n } = useChatContainerMessageList(props);\n\n const { sendingText, sendingMessages, messages, getMoreMessages, hasMoreItems, isLoading } = propsWithDefault;\n const totalSize = useVirtualHelpers.getTotalSize();\n const virtualItems = useVirtualHelpers.getVirtualItems();\n const { measureElement } = useVirtualHelpers;\n\n const newMessages = useGetNewMessages(messages);\n\n const render = React.useMemo(\n () =>\n virtualItems.map((virtualItem) => {\n const { index, start } = virtualItem;\n const row = messages[index];\n const isSending = sendingMessages && sendingMessages.find((m) => m === row.dsId);\n const style: CSSProperties = {\n position: 'absolute',\n top: 0,\n left: 0,\n transform: `translateY(${start}px)`,\n width: '100%',\n };\n\n return (\n <ListItem\n data-element=\"chat-element-list-item\"\n data-index={index}\n key={`ds-chat-bubble-${index}-${row.dsId}-${row.type}`}\n style={style}\n innerRef={measureElement}\n tabIndex={-1}\n onKeyDown={handleKey}\n role=\"listitem\"\n >\n <StyledWrapperListItem type={row.type}>\n {row.type === BUBBLE_TYPES.SENDER || row.type === BUBBLE_TYPES.RECIPIENT ? (\n <ChatBubble {...row} helpMessage={isSending ? sendingText : row.helpMessage} />\n ) : null}\n {row.type === BUBBLE_TYPES.DELIMITER ? (\n <MessageDelimiter {...(row as unknown as DSChatMessageDelimeterT.Props)} />\n ) : null}\n {row.type === BUBBLE_TYPES.SYSTEM ? <SystemMessage {...(row as DSChatSystemMessageT.Props)} /> : null}\n </StyledWrapperListItem>\n </ListItem>\n );\n }),\n [virtualItems, messages, sendingMessages, handleKey, sendingText, measureElement],\n );\n\n return (\n <StyledWrapper {...globalAttributes}>\n <StyledChatContainerContentList\n data-testid={ChatContainerDataTestIds.THREAD_CONTAINER}\n tabIndex={0}\n innerRef={virtualListRef as React.RefObject<HTMLDivElement>}\n role=\"list\"\n onKeyDown={handleListKey}\n >\n <StyledChatContainerContentListScroll height={`${totalSize}px`}>\n {!isLoading && hasMoreItems ? (\n <StyledLoadMoreContainer hasMoreItems={hasMoreItems} role=\"listitem\">\n <DSButtonV2\n buttonType=\"outline\"\n onClick={getMoreMessages}\n data-testid={ChatContainerDataTestIds.LOAD_MORE_BUTTON}\n innerRef={loadMoreRef as ButtonV2InnerRefT}\n >\n Load More\n </DSButtonV2>\n </StyledLoadMoreContainer>\n ) : null}\n {render}\n </StyledChatContainerContentListScroll>\n </StyledChatContainerContentList>\n {/* We repeat the render with the new available messages */}\n <StyledNewMessagesHiddenContainer aria-live=\"polite\" aria-hidden=\"false\">\n {newMessages.map((newMessage) => (\n <p key={newMessage.dsId}>\n {newMessage.title} {newMessage.time} {newMessage.body} {newMessage.errorMessage} {newMessage.helpMessage}\n </p>\n ))}\n </StyledNewMessagesHiddenContainer>\n </StyledWrapper>\n );\n};\n\nChatContainerMessagesList.displayName = 'ChatContainerMessagesList';\nconst ChatContainerMessagesListWithSchema = describe(ChatContainerMessagesList);\nChatContainerMessagesListWithSchema.propTypes = ChatContainerMessagesListPropTypes as WeakValidationMap<unknown>;\n\nexport { ChatContainerMessagesList, ChatContainerMessagesListWithSchema };\n"],
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable complexity */\n/* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n/* eslint-disable max-lines */\nimport type { WeakValidationMap } from 'react';\nimport React from 'react';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { DSButtonV2, type DSButtonV2T } from '@elliemae/ds-button-v2';\nimport { ChatBubble, BUBBLE_TYPES } from '@elliemae/ds-chat-bubble';\nimport type { DSChatMessageDelimeterT } from '@elliemae/ds-chat-message-delimeter';\nimport { MessageDelimiter } from '@elliemae/ds-chat-message-delimeter';\nimport type { DSChatSystemMessageT } from '@elliemae/ds-chat-system-message';\nimport { SystemMessage } from '@elliemae/ds-chat-system-message';\nimport {\n StyledWrapperListItem,\n StyledFastList,\n StyledWrapper,\n StyledLoadMoreContainer,\n StyledNewMessagesHiddenContainer,\n} from './styled.js';\nimport type { DSChatT } from '../../react-desc-prop-types.js';\nimport { ChatContainerMessagesListPropTypes } from '../../react-desc-prop-types.js';\nimport { useGetNewMessages } from './useGetNewMessages.js';\nimport { useChatContainerMessageList } from './useChatContainerMessageList.js';\nimport { getMessageFocusId, getMessagePositionId } from './useMessageFocusTracker.js';\nimport type { MessageFocusRequest, useMessageFocusTracker } from './useMessageFocusTracker.js';\nimport { ChatContainerDataTestIds } from '../../ChatContainerDataTestids.js';\n\ntype ButtonV2InnerRefT = DSButtonV2T.Props['innerRef'];\n\ntype FocusTracker = ReturnType<typeof useMessageFocusTracker>;\ntype ItemFocusCallbacks = Pick<FocusTracker, 'mayTakeFocus' | 'markFulfilled'>;\n\ntype ItemExtraProps = {\n messages: DSChatT.MessagesListProps['messages'];\n sendingMessages: DSChatT.MessagesListProps['sendingMessages'];\n sendingText: DSChatT.MessagesListProps['sendingText'];\n focusRequest: MessageFocusRequest | null;\n focusCallbacks: ItemFocusCallbacks;\n};\n\nconst ESTIMATED_MESSAGE_HEIGHT = 80;\nconst estimateSize = () => ESTIMATED_MESSAGE_HEIGHT;\n// the wrapper `ds-fast-list` renders is neutral: the scroll container owns the `list` role\nconst WRAPPER_LIST_PROPS = { role: 'none' };\n\n/**\n * Module-level on purpose: `DSFastList` renders it as a component, so its identity must be stable or\n * every item would remount on each render of the list.\n */\nconst ItemRenderer: React.ComponentType<{ index: number; extraItemProps?: object }> = ({ index, extraItemProps }) => {\n // `styled(DSFastList)` drops DSFastList's generic, so `extraItemProps` arrives typed as `object`\n const { messages, sendingMessages, sendingText, focusRequest, focusCallbacks } = (extraItemProps ??\n {}) as Partial<ItemExtraProps>;\n const row = messages?.[index];\n const isFocusTarget = Boolean(\n row &&\n focusRequest &&\n (focusRequest.messageId === getMessageFocusId(row, index) ||\n focusRequest.messageId === getMessagePositionId(index)),\n );\n const requestId = isFocusTarget && focusRequest ? focusRequest.requestId : 0;\n const preventScroll = isFocusTarget && focusRequest ? focusRequest.preventScroll : false;\n /**\n * The only place a message is focused. It runs once React has committed the message, so a target that is\n * not mounted yet (far away, or remounted after older messages were loaded) is focused when it mounts. The\n * focusable element is the item wrapper `ds-fast-list` renders around this component, the one carrying\n * `data-index` and QA's `data-element`, so it is reached with `closest` from this message's own node.\n */\n const focusRef = React.useCallback(\n (node: HTMLDivElement | null) => {\n if (!node || !isFocusTarget || !focusCallbacks) return;\n const item = node.closest<HTMLElement>('[data-index]');\n const takeFocus = focusCallbacks.mayTakeFocus(requestId, item);\n if (!takeFocus) return;\n item?.focus({ preventScroll: takeFocus === 'restore' || preventScroll });\n focusCallbacks.markFulfilled(requestId);\n },\n [isFocusTarget, requestId, preventScroll, focusCallbacks],\n );\n if (!row) return null;\n const isSending = sendingMessages && sendingMessages.find((m) => m === row.dsId);\n return (\n <StyledWrapperListItem type={row.type} innerRef={focusRef}>\n {row.type === BUBBLE_TYPES.SENDER || row.type === BUBBLE_TYPES.RECIPIENT ? (\n <ChatBubble {...row} helpMessage={isSending ? sendingText : row.helpMessage} />\n ) : null}\n {row.type === BUBBLE_TYPES.DELIMITER ? (\n <MessageDelimiter {...(row as unknown as DSChatMessageDelimeterT.Props)} />\n ) : null}\n {row.type === BUBBLE_TYPES.SYSTEM ? <SystemMessage {...(row as DSChatSystemMessageT.Props)} /> : null}\n </StyledWrapperListItem>\n );\n};\n\nconst ChatContainerMessagesList: React.ComponentType<DSChatT.MessagesListProps> = (props) => {\n const {\n propsWithDefault,\n globalAttributes,\n fastListActionRef,\n paddingStart,\n paddingEnd,\n handleKey,\n handleListKey,\n loadMoreRef,\n virtualListRef,\n focusTracker,\n } = useChatContainerMessageList(props);\n\n const { sendingText, sendingMessages, messages, getMoreMessages, hasMoreItems, isLoading } = propsWithDefault;\n\n const newMessages = useGetNewMessages(messages);\n\n const getOwnerProps = React.useCallback(() => propsWithDefault, [propsWithDefault]);\n /**\n * The key must carry the index. `ds-fast-list` reuses an item's DOM node for as long as its key is the same,\n * but the virtualizer measures each node by its `data-index` and does not measure a node again when only its\n * index changes. With a key made only of `dsId`, loading older messages (which shifts every index) keeps the\n * nodes and leaves their measurements pointing at another position: items overlap or leave gaps, and the list\n * flickers while scrolling. An index-bearing key recreates the shifted items so they are measured at their\n * new index, as the list did before it was moved onto `ds-fast-list`.\n */\n const getId = React.useCallback(\n (index: number) => `${index}-${messages[index].dsId}-${messages[index].type}`,\n [messages],\n );\n const {\n focusRequest,\n mayTakeFocus,\n markFulfilled,\n trackFocusedMessage,\n trackFocusResetter,\n handleFocusOut,\n listRootRef,\n } = focusTracker;\n const focusCallbacks = React.useMemo(() => ({ mayTakeFocus, markFulfilled }), [mayTakeFocus, markFulfilled]);\n const extraItemProps = React.useMemo(\n () => ({ messages, sendingMessages, sendingText, focusRequest, focusCallbacks }),\n [messages, sendingMessages, sendingText, focusRequest, focusCallbacks],\n );\n const { onFocus: onFocusFromConsumer, onBlur: onBlurFromConsumer } = globalAttributes as {\n onFocus?: React.FocusEventHandler<HTMLElement>;\n onBlur?: React.FocusEventHandler<HTMLElement>;\n };\n /**\n * Keeps the focus intent equal to the message that actually got the focus (keyboard, mouse or API), also when\n * the focus is on a descendant of the message, and clears it when the focus moves to anything else in the list.\n */\n const handleFocusIn = React.useCallback(\n (e: React.FocusEvent<HTMLElement>) => {\n const item = (e.target as HTMLElement).closest<HTMLElement>('[data-element=\"chat-element-list-item\"]');\n const index = item?.dataset.index;\n const position = Number(index);\n const row = index !== undefined ? messages[position] : undefined;\n if (row) trackFocusedMessage(getMessageFocusId(row, position));\n else trackFocusResetter();\n onFocusFromConsumer?.(e);\n },\n [messages, trackFocusedMessage, trackFocusResetter, onFocusFromConsumer],\n );\n const handleFocusOutWithConsumer = React.useCallback(\n (e: React.FocusEvent<HTMLElement>) => {\n handleFocusOut(e);\n onBlurFromConsumer?.(e);\n },\n [handleFocusOut, onBlurFromConsumer],\n );\n const itemProps = React.useMemo(\n () => ({ 'data-element': 'chat-element-list-item', tabIndex: -1, onKeyDown: handleKey }),\n [handleKey],\n );\n const extraContent = React.useMemo(\n () =>\n !isLoading && hasMoreItems ? (\n <StyledLoadMoreContainer hasMoreItems={hasMoreItems} role=\"listitem\">\n <DSButtonV2\n buttonType=\"outline\"\n onClick={getMoreMessages}\n data-testid={ChatContainerDataTestIds.LOAD_MORE_BUTTON}\n innerRef={loadMoreRef as ButtonV2InnerRefT}\n >\n Load More\n </DSButtonV2>\n </StyledLoadMoreContainer>\n ) : null,\n [isLoading, hasMoreItems, getMoreMessages, loadMoreRef],\n );\n\n return (\n <StyledWrapper\n {...globalAttributes}\n innerRef={listRootRef}\n onFocus={handleFocusIn}\n onBlur={handleFocusOutWithConsumer}\n >\n <StyledFastList\n getOwnerProps={getOwnerProps}\n data-testid={ChatContainerDataTestIds.THREAD_CONTAINER}\n role=\"list\"\n tabIndex={0}\n onKeyDown={handleListKey}\n innerRef={virtualListRef}\n actionRef={fastListActionRef}\n remountOnCountChange={false}\n count={messages.length}\n getId={getId}\n estimateSize={estimateSize}\n paddingStart={paddingStart}\n paddingEnd={paddingEnd}\n ItemRenderer={ItemRenderer}\n extraItemProps={extraItemProps}\n extraContent={extraContent}\n dsFastlistWrapperList={WRAPPER_LIST_PROPS}\n dsFastlistItem={itemProps}\n />\n {/* We repeat the render with the new available messages */}\n <StyledNewMessagesHiddenContainer aria-live=\"polite\" aria-hidden=\"false\">\n {newMessages.map((newMessage) => (\n <p key={newMessage.dsId}>\n {newMessage.title} {newMessage.time} {newMessage.body} {newMessage.errorMessage} {newMessage.helpMessage}\n </p>\n ))}\n </StyledNewMessagesHiddenContainer>\n </StyledWrapper>\n );\n};\n\nChatContainerMessagesList.displayName = 'ChatContainerMessagesList';\nconst ChatContainerMessagesListWithSchema = describe(ChatContainerMessagesList);\nChatContainerMessagesListWithSchema.propTypes = ChatContainerMessagesListPropTypes as WeakValidationMap<unknown>;\n\nexport { ChatContainerMessagesList, ChatContainerMessagesListWithSchema };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["React"]
|
|
7
7
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { styled } from "@elliemae/ds-system";
|
|
3
|
+
import { DSFastList } from "@elliemae/ds-fast-list";
|
|
3
4
|
import { BUBBLE_TYPES } from "@elliemae/ds-chat-bubble";
|
|
5
|
+
import { DSChatContainerSlotName, DSChatContainerSlots } from "../../DSChatContainerDefinitions.js";
|
|
4
6
|
const StyledWrapper = styled.div`
|
|
5
7
|
width: 100%;
|
|
6
8
|
height: 100%;
|
|
@@ -15,21 +17,36 @@ const StyledWrapperListItem = styled.div`
|
|
|
15
17
|
${(props) => props.type === BUBBLE_TYPES.SYSTEM && `padding: ${props.theme.space.xxs} 0px
|
|
16
18
|
`};
|
|
17
19
|
`;
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
const StyledFastList = styled(DSFastList, {
|
|
21
|
+
name: DSChatContainerSlotName,
|
|
22
|
+
slot: DSChatContainerSlots.MESSAGES_LIST_SCROLL_AREA
|
|
23
|
+
})`
|
|
24
|
+
&& {
|
|
25
|
+
padding: 0 ${(props) => props.theme.space.xs} 0 ${(props) => props.theme.space.xs};
|
|
26
|
+
overflow-y: auto;
|
|
27
|
+
overflow-x: hidden;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
[data-element='chat-element-list-item'] {
|
|
31
|
+
&:focus,
|
|
32
|
+
&:focus-visible {
|
|
33
|
+
outline: none;
|
|
34
|
+
&:after {
|
|
35
|
+
content: '';
|
|
36
|
+
display: block;
|
|
37
|
+
position: absolute;
|
|
38
|
+
top: 0;
|
|
39
|
+
left: -${(props) => props.theme.space.xxs};
|
|
40
|
+
pointer-events: none;
|
|
41
|
+
border-radius: 2px;
|
|
42
|
+
background-color: #ebedf0;
|
|
43
|
+
width: calc(100% + 24px);
|
|
44
|
+
border: 2px solid #1e79c2;
|
|
45
|
+
height: 100%;
|
|
46
|
+
z-index: -1;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
33
50
|
`;
|
|
34
51
|
const StyledNewMessagesHiddenContainer = styled.div`
|
|
35
52
|
position: absolute;
|
|
@@ -43,11 +60,6 @@ const StyledNewMessagesHiddenContainer = styled.div`
|
|
|
43
60
|
clip-path: inset(100%);
|
|
44
61
|
white-space: nowrap;
|
|
45
62
|
`;
|
|
46
|
-
const StyledChatContainerContentListScroll = styled.div`
|
|
47
|
-
width: 100%;
|
|
48
|
-
height: ${(props) => props.height};
|
|
49
|
-
position: relative;
|
|
50
|
-
`;
|
|
51
63
|
const StyledLoadMoreContainer = styled.div`
|
|
52
64
|
position: absolute;
|
|
53
65
|
width: calc(100% + ${(props) => props.theme.space.xxs});
|
|
@@ -60,30 +72,8 @@ const StyledLoadMoreContainer = styled.div`
|
|
|
60
72
|
${(props) => props.hasMoreItems === "top" ? "top: 0;" : ""}
|
|
61
73
|
${(props) => props.hasMoreItems === "bottom" ? "bottom: 0;" : ""}
|
|
62
74
|
`;
|
|
63
|
-
const ListItem = styled.div`
|
|
64
|
-
&:focus,
|
|
65
|
-
&:focus-visible {
|
|
66
|
-
outline: none;
|
|
67
|
-
&:after {
|
|
68
|
-
content: '';
|
|
69
|
-
display: block;
|
|
70
|
-
position: absolute;
|
|
71
|
-
top: 0;
|
|
72
|
-
left: -${(props) => props.theme.space.xxs};
|
|
73
|
-
pointer-events: none;
|
|
74
|
-
border-radius: 2px;
|
|
75
|
-
background-color: #ebedf0;
|
|
76
|
-
width: calc(100% + 24px);
|
|
77
|
-
border: 2px solid #1e79c2;
|
|
78
|
-
height: 100%;
|
|
79
|
-
z-index: -1;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
`;
|
|
83
75
|
export {
|
|
84
|
-
|
|
85
|
-
StyledChatContainerContentList,
|
|
86
|
-
StyledChatContainerContentListScroll,
|
|
76
|
+
StyledFastList,
|
|
87
77
|
StyledLoadMoreContainer,
|
|
88
78
|
StyledNewMessagesHiddenContainer,
|
|
89
79
|
StyledWrapper,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/chat-container-messages-list/styled.ts"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { styled } from '@elliemae/ds-system';\nimport type { DSChatBubbleT } from '@elliemae/ds-chat-bubble';\nimport { BUBBLE_TYPES } from '@elliemae/ds-chat-bubble';\n\nexport const StyledWrapper = styled.div`\n width: 100%;\n height: 100%;\n`;\n\n// list\nexport const StyledWrapperListItem = styled.div<Partial<DSChatBubbleT.Props>>`\n min-width: 100%;\n ${(props) =>\n props.type === BUBBLE_TYPES.SENDER &&\n `padding: ${props.theme.space.xxs} ${props.theme.space.xxs} ${props.theme.space.xxs} 13%\n `};\n ${(props) =>\n props.type === BUBBLE_TYPES.RECIPIENT &&\n `padding: ${props.theme.space.xxs} 13% ${props.theme.space.xxs}\n ${props.theme.space.xxs}\n `};\n ${(props) =>\n props.type === BUBBLE_TYPES.SYSTEM &&\n `padding: ${props.theme.space.xxs} 0px\n `};\n`;\n\
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,cAAc;
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { styled } from '@elliemae/ds-system';\nimport { DSFastList } from '@elliemae/ds-fast-list';\nimport type { DSChatBubbleT } from '@elliemae/ds-chat-bubble';\nimport { BUBBLE_TYPES } from '@elliemae/ds-chat-bubble';\nimport { DSChatContainerSlotName, DSChatContainerSlots } from '../../DSChatContainerDefinitions.js';\n\nexport const StyledWrapper = styled.div`\n width: 100%;\n height: 100%;\n`;\n\n// list\nexport const StyledWrapperListItem = styled.div<Partial<DSChatBubbleT.Props>>`\n min-width: 100%;\n ${(props) =>\n props.type === BUBBLE_TYPES.SENDER &&\n `padding: ${props.theme.space.xxs} ${props.theme.space.xxs} ${props.theme.space.xxs} 13%\n `};\n ${(props) =>\n props.type === BUBBLE_TYPES.RECIPIENT &&\n `padding: ${props.theme.space.xxs} 13% ${props.theme.space.xxs}\n ${props.theme.space.xxs}\n `};\n ${(props) =>\n props.type === BUBBLE_TYPES.SYSTEM &&\n `padding: ${props.theme.space.xxs} 0px\n `};\n`;\n\n/**\n * The scroll container is DSFastList's own container, so its horizontal padding and overflow rules are\n * applied through the class that `styled(DSFastList)` forwards to it. `&&` doubles the class so these\n * rules win over the container's own `overflow: auto` regardless of style injection order.\n *\n * No padding-top here on purpose: the scroll container is also the virtualizer's scroll element, and\n * the virtualizer compares the raw `scrollTop` with its own content-relative item positions with no\n * awareness of CSS padding, so a CSS top-padding would desync `getVisibleItems()` near the top boundary\n * by roughly the padding's own size. The equivalent top space is passed as the virtualizer's\n * `paddingStart` instead (see useChatContainerMessageList.ts).\n */\nexport const StyledFastList = styled(DSFastList, {\n name: DSChatContainerSlotName,\n slot: DSChatContainerSlots.MESSAGES_LIST_SCROLL_AREA,\n})`\n && {\n padding: 0 ${(props) => props.theme.space.xs} 0 ${(props) => props.theme.space.xs};\n overflow-y: auto;\n overflow-x: hidden;\n }\n\n [data-element='chat-element-list-item'] {\n &:focus,\n &:focus-visible {\n outline: none;\n &:after {\n content: '';\n display: block;\n position: absolute;\n top: 0;\n left: -${(props) => props.theme.space.xxs};\n pointer-events: none;\n border-radius: 2px;\n background-color: #ebedf0;\n width: calc(100% + 24px);\n border: 2px solid #1e79c2;\n height: 100%;\n z-index: -1;\n }\n }\n }\n`;\n\nexport const StyledNewMessagesHiddenContainer = styled.div`\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n border: 0;\n padding: 0;\n overflow: hidden;\n clip: rect(0 0 0 0);\n clip-path: inset(100%);\n white-space: nowrap;\n`;\n\nexport const StyledLoadMoreContainer = styled.div<{ hasMoreItems?: 'top' | 'bottom' }>`\n position: absolute;\n width: calc(100% + ${(props) => props.theme.space.xxs});\n padding: ${(props) => props.theme.space.xxs};\n left: -${(props) => props.theme.space.xxs};\n right: -${(props) => props.theme.space.xxs};\n display: grid;\n justify-content: center;\n align-items: center;\n ${(props) => (props.hasMoreItems === 'top' ? 'top: 0;' : '')}\n ${(props) => (props.hasMoreItems === 'bottom' ? 'bottom: 0;' : '')}\n`;\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAE3B,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB,4BAA4B;AAEvD,MAAM,gBAAgB,OAAO;AAAA;AAAA;AAAA;AAM7B,MAAM,wBAAwB,OAAO;AAAA;AAAA,IAExC,CAAC,UACD,MAAM,SAAS,aAAa,UAC5B,YAAY,MAAM,MAAM,MAAM,GAAG,IAAI,MAAM,MAAM,MAAM,GAAG,IAAI,MAAM,MAAM,MAAM,GAAG;AAAA,GACpF;AAAA,IACC,CAAC,UACD,MAAM,SAAS,aAAa,aAC5B,YAAY,MAAM,MAAM,MAAM,GAAG,QAAQ,MAAM,MAAM,MAAM,GAAG;AAAA,MAC5D,MAAM,MAAM,MAAM,GAAG;AAAA,GACxB;AAAA,IACC,CAAC,UACD,MAAM,SAAS,aAAa,UAC5B,YAAY,MAAM,MAAM,MAAM,GAAG;AAAA,GAClC;AAAA;AAcI,MAAM,iBAAiB,OAAO,YAAY;AAAA,EAC/C,MAAM;AAAA,EACN,MAAM,qBAAqB;AAC7B,CAAC;AAAA;AAAA,iBAEgB,CAAC,UAAU,MAAM,MAAM,MAAM,EAAE,MAAM,CAAC,UAAU,MAAM,MAAM,MAAM,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAcpE,CAAC,UAAU,MAAM,MAAM,MAAM,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAa1C,MAAM,mCAAmC,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAahD,MAAM,0BAA0B,OAAO;AAAA;AAAA,uBAEvB,CAAC,UAAU,MAAM,MAAM,MAAM,GAAG;AAAA,aAC1C,CAAC,UAAU,MAAM,MAAM,MAAM,GAAG;AAAA,WAClC,CAAC,UAAU,MAAM,MAAM,MAAM,GAAG;AAAA,YAC/B,CAAC,UAAU,MAAM,MAAM,MAAM,GAAG;AAAA;AAAA;AAAA;AAAA,IAIxC,CAAC,UAAW,MAAM,iBAAiB,QAAQ,YAAY,EAAG;AAAA,IAC1D,CAAC,UAAW,MAAM,iBAAiB,WAAW,eAAe,EAAG;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,13 +2,11 @@ import * as React from "react";
|
|
|
2
2
|
import React2 from "react";
|
|
3
3
|
const useActionRef = ({
|
|
4
4
|
actionRef,
|
|
5
|
-
|
|
5
|
+
vScroll,
|
|
6
6
|
virtualListRef,
|
|
7
|
-
updateCache,
|
|
8
7
|
getMessagesInView,
|
|
9
8
|
onlyFocus,
|
|
10
|
-
scrollAndFocus
|
|
11
|
-
hasVirtualItems
|
|
9
|
+
scrollAndFocus
|
|
12
10
|
}) => {
|
|
13
11
|
React2.useEffect(() => {
|
|
14
12
|
if (actionRef && actionRef.current) {
|
|
@@ -17,7 +15,6 @@ const useActionRef = ({
|
|
|
17
15
|
vScroll(index, opts);
|
|
18
16
|
requestAnimationFrame(() => {
|
|
19
17
|
vScroll(index, opts);
|
|
20
|
-
updateCache();
|
|
21
18
|
});
|
|
22
19
|
}, 0);
|
|
23
20
|
};
|
|
@@ -33,10 +30,7 @@ const useActionRef = ({
|
|
|
33
30
|
actionRef.current.focusToIndexWithoutScroll = onlyFocus;
|
|
34
31
|
actionRef.current.focusToList = focusToList;
|
|
35
32
|
}
|
|
36
|
-
}, [actionRef, getMessagesInView, onlyFocus, scrollAndFocus,
|
|
37
|
-
React2.useEffect(() => {
|
|
38
|
-
if (hasVirtualItems) updateCache();
|
|
39
|
-
}, [hasVirtualItems, updateCache]);
|
|
33
|
+
}, [actionRef, getMessagesInView, onlyFocus, scrollAndFocus, vScroll, virtualListRef]);
|
|
40
34
|
};
|
|
41
35
|
export {
|
|
42
36
|
useActionRef
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/chat-container-messages-list/useActionRef.ts"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable complexity */\n/* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n/* eslint-disable max-lines */\nimport React from 'react';\nimport type { DSChatT } from '../../react-desc-prop-types.js';\n\ntype UseActionRefArgs = {\n actionRef: DSChatT.ActionRef;\n
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACGvB,OAAOA,YAAW;
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable complexity */\n/* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n/* eslint-disable max-lines */\nimport React from 'react';\nimport type { DSChatT } from '../../react-desc-prop-types.js';\n\ntype UseActionRefArgs = {\n actionRef: DSChatT.ActionRef;\n vScroll: (index: number, opts?: DSChatT.ScrollActionOptions) => void;\n virtualListRef: React.MutableRefObject<HTMLDivElement | null>;\n getMessagesInView: () => string[];\n onlyFocus: (index: string) => void;\n scrollAndFocus: (index: number, opts?: DSChatT.ScrollActionOptions) => void;\n};\n\nexport const useActionRef = ({\n actionRef,\n vScroll,\n virtualListRef,\n getMessagesInView,\n onlyFocus,\n scrollAndFocus,\n}: UseActionRefArgs) => {\n React.useEffect(() => {\n if (actionRef && actionRef.current) {\n const scrollToIndex: DSChatT.ActionRef['current']['scrollToIndex'] = (index, opts = { align: 'center' }) => {\n setTimeout(() => {\n vScroll(index, opts);\n /* [PUI-15629](https://jira.elliemae.io/browse/PUI-15629)\n * requestAnimationFrame is needed because the first scroll might not calculate positions\n * correctly if the DOM hasn't been fully updated yet. By waiting for the next animation\n * frame, we ensure that:\n * 1. The DOM has been updated with new positions after the first scroll\n * 2. Element height and position measurements are accurate\n * 3. The second scroll can align the element with correct measurements\n */\n requestAnimationFrame(() => {\n vScroll(index, opts);\n });\n }, 0);\n };\n const focusToIndex: DSChatT.ActionRef['current']['focusToIndex'] = (index, opts = { align: 'center' }) => {\n scrollAndFocus(index, opts);\n };\n const focusToList = () => {\n virtualListRef.current?.focus();\n };\n\n actionRef.current.scrollToIndex = scrollToIndex;\n actionRef.current.getMessagesInView = getMessagesInView;\n actionRef.current.focusToIndex = focusToIndex;\n actionRef.current.focusToIndexWithoutScroll = onlyFocus;\n actionRef.current.focusToList = focusToList;\n }\n }, [actionRef, getMessagesInView, onlyFocus, scrollAndFocus, vScroll, virtualListRef]);\n};\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACGvB,OAAOA,YAAW;AAYX,MAAM,eAAe,CAAC;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAwB;AACtB,EAAAA,OAAM,UAAU,MAAM;AACpB,QAAI,aAAa,UAAU,SAAS;AAClC,YAAM,gBAA+D,CAAC,OAAO,OAAO,EAAE,OAAO,SAAS,MAAM;AAC1G,mBAAW,MAAM;AACf,kBAAQ,OAAO,IAAI;AASnB,gCAAsB,MAAM;AAC1B,oBAAQ,OAAO,IAAI;AAAA,UACrB,CAAC;AAAA,QACH,GAAG,CAAC;AAAA,MACN;AACA,YAAM,eAA6D,CAAC,OAAO,OAAO,EAAE,OAAO,SAAS,MAAM;AACxG,uBAAe,OAAO,IAAI;AAAA,MAC5B;AACA,YAAM,cAAc,MAAM;AACxB,uBAAe,SAAS,MAAM;AAAA,MAChC;AAEA,gBAAU,QAAQ,gBAAgB;AAClC,gBAAU,QAAQ,oBAAoB;AACtC,gBAAU,QAAQ,eAAe;AACjC,gBAAU,QAAQ,4BAA4B;AAC9C,gBAAU,QAAQ,cAAc;AAAA,IAClC;AAAA,EACF,GAAG,CAAC,WAAW,mBAAmB,WAAW,gBAAgB,SAAS,cAAc,CAAC;AACvF;",
|
|
6
6
|
"names": ["React"]
|
|
7
7
|
}
|
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
useGetGlobalAttributes,
|
|
7
7
|
useValidateTypescriptPropTypes
|
|
8
8
|
} from "@elliemae/ds-props-helpers";
|
|
9
|
-
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
10
9
|
import { useActionRef } from "./useActionRef.js";
|
|
11
10
|
import { useGlobalEventListener } from "./useGlobalEventListener.js";
|
|
12
11
|
import { useKeyboard } from "./useKeyboard.js";
|
|
13
12
|
import { useFocusAndScrollHelpers } from "./useFocusAndScrollHelpers.js";
|
|
13
|
+
import { useMessageFocusTracker } from "./useMessageFocusTracker.js";
|
|
14
14
|
import { ChatContainerMessagesListPropTypes, defaultPropsMessagesList } from "../../react-desc-prop-types.js";
|
|
15
15
|
import { DSChatContainerMessagesList } from "../../DSChatContainerDefinitions.js";
|
|
16
16
|
const getPadding = ({ hasMoreItems, bannerPosition, isLoading }) => {
|
|
@@ -31,36 +31,30 @@ const useChatContainerMessageList = (props) => {
|
|
|
31
31
|
);
|
|
32
32
|
const globalAttributes = useGetGlobalAttributes(propsWithDefault);
|
|
33
33
|
const loadMoreRef = React2.useRef();
|
|
34
|
-
const virtualListRef = React2.useRef();
|
|
34
|
+
const virtualListRef = React2.useRef(null);
|
|
35
|
+
const fastListActionRef = React2.useRef({});
|
|
35
36
|
const pendingOnScroll = React2.useRef(false);
|
|
36
|
-
const { bannerPosition,
|
|
37
|
+
const { bannerPosition, actionRef, hasMoreItems, isLoading } = propsWithDefault;
|
|
37
38
|
const theme = useTheme();
|
|
38
39
|
const containerPaddingStart = parseInt(theme.space.xs, 10);
|
|
39
40
|
const padding = getPadding({ hasMoreItems, bannerPosition, isLoading });
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
paddingEnd: padding.paddingEnd,
|
|
46
|
-
// Unlike v2, v3 has no internal default for estimateSize — it's required, not optional.
|
|
47
|
-
estimateSize: React2.useCallback(() => 80, [])
|
|
48
|
-
});
|
|
49
|
-
const { onScrollStop, updateCache, getMessagesInView, onlyFocus, scrollAndFocus, checkPendingOnScroll } = useFocusAndScrollHelpers({
|
|
41
|
+
const paddingStart = padding.paddingStart + containerPaddingStart;
|
|
42
|
+
const { paddingEnd } = padding;
|
|
43
|
+
const focusTracker = useMessageFocusTracker(virtualListRef);
|
|
44
|
+
const { trackFocusMessage } = focusTracker;
|
|
45
|
+
const { vScroll, onScrollStop, getMessagesInView, onlyFocus, scrollAndFocus, checkPendingOnScroll } = useFocusAndScrollHelpers({
|
|
50
46
|
propsWithDefault,
|
|
51
|
-
|
|
47
|
+
fastListActionRef,
|
|
52
48
|
pendingOnScroll,
|
|
53
|
-
|
|
49
|
+
trackFocusMessage
|
|
54
50
|
});
|
|
55
51
|
useActionRef({
|
|
56
52
|
actionRef,
|
|
57
|
-
|
|
53
|
+
vScroll,
|
|
58
54
|
virtualListRef,
|
|
59
|
-
updateCache,
|
|
60
55
|
getMessagesInView,
|
|
61
56
|
onlyFocus,
|
|
62
|
-
scrollAndFocus
|
|
63
|
-
hasVirtualItems: useVirtualHelpers.getVirtualItems().length > 0
|
|
57
|
+
scrollAndFocus
|
|
64
58
|
});
|
|
65
59
|
useGlobalEventListener({
|
|
66
60
|
propsWithDefault,
|
|
@@ -75,18 +69,32 @@ const useChatContainerMessageList = (props) => {
|
|
|
75
69
|
loadMoreRef,
|
|
76
70
|
scrollAndFocus,
|
|
77
71
|
onlyFocus,
|
|
78
|
-
getMessagesInView
|
|
72
|
+
getMessagesInView,
|
|
73
|
+
trackFocusMessage
|
|
79
74
|
});
|
|
80
75
|
return React2.useMemo(
|
|
81
76
|
() => ({
|
|
82
77
|
propsWithDefault,
|
|
83
78
|
globalAttributes,
|
|
84
|
-
|
|
79
|
+
fastListActionRef,
|
|
80
|
+
paddingStart,
|
|
81
|
+
paddingEnd,
|
|
85
82
|
loadMoreRef,
|
|
86
83
|
virtualListRef,
|
|
84
|
+
focusTracker,
|
|
87
85
|
...keyboardMethods
|
|
88
86
|
}),
|
|
89
|
-
[
|
|
87
|
+
[
|
|
88
|
+
globalAttributes,
|
|
89
|
+
keyboardMethods,
|
|
90
|
+
propsWithDefault,
|
|
91
|
+
fastListActionRef,
|
|
92
|
+
paddingStart,
|
|
93
|
+
paddingEnd,
|
|
94
|
+
loadMoreRef,
|
|
95
|
+
virtualListRef,
|
|
96
|
+
focusTracker
|
|
97
|
+
]
|
|
90
98
|
);
|
|
91
99
|
};
|
|
92
100
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/chat-container-messages-list/useChatContainerMessageList.ts"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable complexity */\n/* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n/* eslint-disable max-lines */\nimport React from 'react';\nimport { useTheme } from 'styled-components';\nimport {\n useMemoMergePropsWithDefault,\n useGetGlobalAttributes,\n useValidateTypescriptPropTypes,\n} from '@elliemae/ds-props-helpers';\nimport {
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACGvB,OAAOA,YAAW;AAClB,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable complexity */\n/* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n/* eslint-disable max-lines */\nimport React from 'react';\nimport { useTheme } from 'styled-components';\nimport {\n useMemoMergePropsWithDefault,\n useGetGlobalAttributes,\n useValidateTypescriptPropTypes,\n} from '@elliemae/ds-props-helpers';\nimport type { DSFastListT } from '@elliemae/ds-fast-list';\nimport { useActionRef } from './useActionRef.js';\nimport { useGlobalEventListener } from './useGlobalEventListener.js';\nimport { useKeyboard } from './useKeyboard.js';\nimport { useFocusAndScrollHelpers } from './useFocusAndScrollHelpers.js';\nimport { useMessageFocusTracker } from './useMessageFocusTracker.js';\nimport type { DSChatT } from '../../react-desc-prop-types.js';\nimport { ChatContainerMessagesListPropTypes, defaultPropsMessagesList } from '../../react-desc-prop-types.js';\nimport { DSChatContainerMessagesList } from '../../DSChatContainerDefinitions.js';\n\nconst getPadding = ({ hasMoreItems, bannerPosition, isLoading }: DSChatT.GetPaddingT) => {\n const padding = {\n paddingStart: 0,\n paddingEnd: -5,\n };\n if ([hasMoreItems, bannerPosition, isLoading].some((item) => item === 'top')) padding.paddingStart = 42;\n else if ([hasMoreItems, bannerPosition, isLoading].some((item) => item === 'bottom')) padding.paddingEnd = 42;\n return padding;\n};\n\ntype UseChatContainerMessageList = (props: DSChatT.MessagesListProps) => {\n propsWithDefault: DSChatT.MessagesListProps;\n globalAttributes: ReturnType<typeof useGetGlobalAttributes>;\n fastListActionRef: React.MutableRefObject<DSFastListT.ActionRef>;\n paddingStart: number;\n paddingEnd: number;\n loadMoreRef: React.MutableRefObject<HTMLButtonElement | undefined>;\n virtualListRef: React.MutableRefObject<HTMLDivElement | null>;\n focusTracker: ReturnType<typeof useMessageFocusTracker>;\n} & ReturnType<typeof useKeyboard>;\n\nexport const useChatContainerMessageList: UseChatContainerMessageList = (props) => {\n const propsWithDefault = useMemoMergePropsWithDefault<DSChatT.MessagesListProps>(props, defaultPropsMessagesList);\n useValidateTypescriptPropTypes<DSChatT.MessagesListProps>(\n propsWithDefault,\n ChatContainerMessagesListPropTypes,\n DSChatContainerMessagesList,\n );\n const globalAttributes = useGetGlobalAttributes(propsWithDefault);\n const loadMoreRef = React.useRef<HTMLButtonElement>();\n const virtualListRef = React.useRef<HTMLDivElement | null>(null);\n const fastListActionRef = React.useRef<DSFastListT.ActionRef>({});\n\n const pendingOnScroll = React.useRef(false);\n const { bannerPosition, actionRef, hasMoreItems, isLoading } = propsWithDefault;\n const theme = useTheme();\n // The scroll container's own CSS top-padding was removed (see styled.ts's StyledFastList) and\n // replaced with this \u2014 an equivalent virtualizer-space paddingStart keeps getVisibleItems()'s\n // scrollOffset comparisons in the same frame as its item measurements.\n const containerPaddingStart = parseInt(theme.space.xs, 10);\n const padding = getPadding({ hasMoreItems, bannerPosition, isLoading });\n const paddingStart = padding.paddingStart + containerPaddingStart;\n const { paddingEnd } = padding;\n\n const focusTracker = useMessageFocusTracker(virtualListRef);\n const { trackFocusMessage } = focusTracker;\n\n const { vScroll, onScrollStop, getMessagesInView, onlyFocus, scrollAndFocus, checkPendingOnScroll } =\n useFocusAndScrollHelpers({\n propsWithDefault,\n fastListActionRef,\n pendingOnScroll,\n trackFocusMessage,\n });\n\n useActionRef({\n actionRef,\n vScroll,\n virtualListRef,\n getMessagesInView,\n onlyFocus,\n scrollAndFocus,\n });\n\n useGlobalEventListener({\n propsWithDefault,\n virtualListRef,\n onScrollStop,\n checkPendingOnScroll,\n });\n\n const keyboardMethods = useKeyboard({\n propsWithDefault,\n actionRef,\n virtualListRef,\n loadMoreRef,\n scrollAndFocus,\n onlyFocus,\n getMessagesInView,\n trackFocusMessage,\n });\n\n return React.useMemo(\n () => ({\n propsWithDefault,\n globalAttributes,\n fastListActionRef,\n paddingStart,\n paddingEnd,\n loadMoreRef,\n virtualListRef,\n focusTracker,\n ...keyboardMethods,\n }),\n [\n globalAttributes,\n keyboardMethods,\n propsWithDefault,\n fastListActionRef,\n paddingStart,\n paddingEnd,\n loadMoreRef,\n virtualListRef,\n focusTracker,\n ],\n );\n};\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACGvB,OAAOA,YAAW;AAClB,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,oBAAoB;AAC7B,SAAS,8BAA8B;AACvC,SAAS,mBAAmB;AAC5B,SAAS,gCAAgC;AACzC,SAAS,8BAA8B;AAEvC,SAAS,oCAAoC,gCAAgC;AAC7E,SAAS,mCAAmC;AAE5C,MAAM,aAAa,CAAC,EAAE,cAAc,gBAAgB,UAAU,MAA2B;AACvF,QAAM,UAAU;AAAA,IACd,cAAc;AAAA,IACd,YAAY;AAAA,EACd;AACA,MAAI,CAAC,cAAc,gBAAgB,SAAS,EAAE,KAAK,CAAC,SAAS,SAAS,KAAK,EAAG,SAAQ,eAAe;AAAA,WAC5F,CAAC,cAAc,gBAAgB,SAAS,EAAE,KAAK,CAAC,SAAS,SAAS,QAAQ,EAAG,SAAQ,aAAa;AAC3G,SAAO;AACT;AAaO,MAAM,8BAA2D,CAAC,UAAU;AACjF,QAAM,mBAAmB,6BAAwD,OAAO,wBAAwB;AAChH;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,mBAAmB,uBAAuB,gBAAgB;AAChE,QAAM,cAAcA,OAAM,OAA0B;AACpD,QAAM,iBAAiBA,OAAM,OAA8B,IAAI;AAC/D,QAAM,oBAAoBA,OAAM,OAA8B,CAAC,CAAC;AAEhE,QAAM,kBAAkBA,OAAM,OAAO,KAAK;AAC1C,QAAM,EAAE,gBAAgB,WAAW,cAAc,UAAU,IAAI;AAC/D,QAAM,QAAQ,SAAS;AAIvB,QAAM,wBAAwB,SAAS,MAAM,MAAM,IAAI,EAAE;AACzD,QAAM,UAAU,WAAW,EAAE,cAAc,gBAAgB,UAAU,CAAC;AACtE,QAAM,eAAe,QAAQ,eAAe;AAC5C,QAAM,EAAE,WAAW,IAAI;AAEvB,QAAM,eAAe,uBAAuB,cAAc;AAC1D,QAAM,EAAE,kBAAkB,IAAI;AAE9B,QAAM,EAAE,SAAS,cAAc,mBAAmB,WAAW,gBAAgB,qBAAqB,IAChG,yBAAyB;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAEH,eAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,yBAAuB;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,kBAAkB,YAAY;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAOA,OAAM;AAAA,IACX,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IACL;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": ["React"]
|
|
7
7
|
}
|
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import React2 from "react";
|
|
3
|
-
import { debounce
|
|
3
|
+
import { debounce } from "lodash-es";
|
|
4
|
+
import { getMessageFocusId, getMessagePositionId } from "./useMessageFocusTracker.js";
|
|
4
5
|
const useFocusAndScrollHelpers = ({
|
|
5
|
-
propsWithDefault: { onScrollEnds },
|
|
6
|
-
|
|
6
|
+
propsWithDefault: { onScrollEnds, messages },
|
|
7
|
+
fastListActionRef,
|
|
7
8
|
pendingOnScroll,
|
|
8
|
-
|
|
9
|
+
trackFocusMessage
|
|
9
10
|
}) => {
|
|
11
|
+
const vScroll = React2.useCallback(
|
|
12
|
+
(index, opts) => {
|
|
13
|
+
const scrollOnce = () => {
|
|
14
|
+
const fastList = fastListActionRef.current;
|
|
15
|
+
const offset = fastList.getOffsetForIndex?.(index, opts?.align ?? "center");
|
|
16
|
+
if (offset !== void 0) fastList.scrollToOffset?.(offset, { behavior: opts?.behavior });
|
|
17
|
+
};
|
|
18
|
+
scrollOnce();
|
|
19
|
+
requestAnimationFrame(scrollOnce);
|
|
20
|
+
},
|
|
21
|
+
[fastListActionRef]
|
|
22
|
+
);
|
|
10
23
|
const computeVisibleItems = React2.useCallback(() => {
|
|
11
24
|
if (document.visibilityState === "hidden") {
|
|
12
25
|
pendingOnScroll.current = true;
|
|
13
26
|
return [];
|
|
14
27
|
}
|
|
15
|
-
const
|
|
16
|
-
if (
|
|
28
|
+
const visibleItems = fastListActionRef.current.getVisibleItems?.() ?? [];
|
|
29
|
+
if (visibleItems.length === 0) return [];
|
|
17
30
|
pendingOnScroll.current = false;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}, [calculateRange, pendingOnScroll]);
|
|
22
|
-
const visibleItemsCache = React2.useRef([]);
|
|
23
|
-
const updateCache = React2.useMemo(
|
|
24
|
-
() => throttle(
|
|
25
|
-
() => {
|
|
26
|
-
visibleItemsCache.current = computeVisibleItems();
|
|
27
|
-
},
|
|
28
|
-
500,
|
|
29
|
-
{ leading: true }
|
|
30
|
-
),
|
|
31
|
-
[computeVisibleItems]
|
|
32
|
-
);
|
|
33
|
-
const getMessagesInView = React2.useCallback(() => [...visibleItemsCache.current], []);
|
|
31
|
+
return visibleItems.map(String);
|
|
32
|
+
}, [fastListActionRef, pendingOnScroll]);
|
|
33
|
+
const getMessagesInView = computeVisibleItems;
|
|
34
34
|
const onScrollStop = React2.useMemo(
|
|
35
35
|
() => debounce(() => {
|
|
36
36
|
const indexList = computeVisibleItems();
|
|
@@ -41,39 +41,39 @@ const useFocusAndScrollHelpers = ({
|
|
|
41
41
|
);
|
|
42
42
|
const onlyFocus = React2.useCallback(
|
|
43
43
|
(index) => {
|
|
44
|
-
|
|
44
|
+
const position = Number(index);
|
|
45
|
+
const row = messages?.[position];
|
|
46
|
+
const mounted = fastListActionRef.current.getMountedItems?.() ?? [];
|
|
47
|
+
if (row && mounted.includes(position))
|
|
48
|
+
trackFocusMessage(getMessageFocusId(row, position), { preventScroll: true });
|
|
45
49
|
},
|
|
46
|
-
[
|
|
50
|
+
[messages, fastListActionRef, trackFocusMessage]
|
|
47
51
|
);
|
|
48
52
|
const scrollAndFocus = React2.useCallback(
|
|
49
|
-
(index) => {
|
|
50
|
-
|
|
53
|
+
(index, opts = { align: "center" }) => {
|
|
54
|
+
const row = messages?.[index];
|
|
55
|
+
const messageId = row ? getMessageFocusId(row, index) : getMessagePositionId(index);
|
|
56
|
+
trackFocusMessage(messageId, { preventScroll: true });
|
|
57
|
+
vScroll(index, opts);
|
|
51
58
|
requestAnimationFrame(() => {
|
|
52
|
-
vScroll(index,
|
|
59
|
+
vScroll(index, opts);
|
|
53
60
|
});
|
|
54
|
-
setTimeout(() => {
|
|
55
|
-
onlyFocus(index.toString());
|
|
56
|
-
}, 300);
|
|
57
|
-
updateCache();
|
|
58
61
|
},
|
|
59
|
-
[
|
|
62
|
+
[messages, trackFocusMessage, vScroll]
|
|
60
63
|
);
|
|
61
64
|
const checkPendingOnScroll = React2.useCallback(() => {
|
|
62
|
-
if (pendingOnScroll.current === true)
|
|
63
|
-
|
|
64
|
-
onScrollStop();
|
|
65
|
-
}
|
|
66
|
-
}, [updateCache, onScrollStop, pendingOnScroll]);
|
|
65
|
+
if (pendingOnScroll.current === true) onScrollStop();
|
|
66
|
+
}, [onScrollStop, pendingOnScroll]);
|
|
67
67
|
return React2.useMemo(
|
|
68
68
|
() => ({
|
|
69
|
+
vScroll,
|
|
69
70
|
onScrollStop,
|
|
70
|
-
updateCache,
|
|
71
71
|
getMessagesInView,
|
|
72
72
|
onlyFocus,
|
|
73
73
|
scrollAndFocus,
|
|
74
74
|
checkPendingOnScroll
|
|
75
75
|
}),
|
|
76
|
-
[
|
|
76
|
+
[vScroll, onScrollStop, getMessagesInView, onlyFocus, scrollAndFocus, checkPendingOnScroll]
|
|
77
77
|
);
|
|
78
78
|
};
|
|
79
79
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/chat-container-messages-list/useFocusAndScrollHelpers.ts"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable complexity */\n/* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n/* eslint-disable max-lines */\nimport React from 'react';\nimport { debounce
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACGvB,OAAOA,YAAW;AAClB,SAAS,
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable complexity */\n/* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n/* eslint-disable max-lines */\nimport React from 'react';\nimport { debounce } from 'lodash-es';\nimport type { DSFastListT } from '@elliemae/ds-fast-list';\nimport type { DSChatT } from '../../react-desc-prop-types.js';\nimport { getMessageFocusId, getMessagePositionId } from './useMessageFocusTracker.js';\nimport type { MessageId } from './useMessageFocusTracker.js';\n\ntype UseFocusAndScrollHelpersArgs = {\n propsWithDefault: DSChatT.MessagesListProps;\n fastListActionRef: React.MutableRefObject<DSFastListT.ActionRef>;\n pendingOnScroll: React.MutableRefObject<boolean>;\n trackFocusMessage: (messageId: MessageId, options?: { preventScroll?: boolean }) => void;\n};\n\nexport const useFocusAndScrollHelpers = ({\n propsWithDefault: { onScrollEnds, messages },\n fastListActionRef,\n pendingOnScroll,\n trackFocusMessage,\n}: UseFocusAndScrollHelpersArgs) => {\n /**\n * Does not use `scrollTo` (the virtualizer's `scrollToIndex`): since @tanstack/react-virtual v3 every call\n * starts a loop that checks the position on the following frames and scrolls again, up to 10 times, and it\n * cannot tell the user's scrolling from its own, so a user who scrolls while it runs is pulled back to the\n * target over and over (PUI-19237). This scrolls the way react-virtual v2's `scrollToIndex` did instead:\n * once now and once on the next frame, when the items just rendered have been measured, and never again.\n */\n const vScroll = React.useCallback(\n (index: number, opts?: DSChatT.ScrollActionOptions) => {\n const scrollOnce = () => {\n const fastList = fastListActionRef.current;\n /** The chat's own default alignment also applies when only `behavior` is provided. */\n const offset = fastList.getOffsetForIndex?.(index, opts?.align ?? 'center');\n if (offset !== undefined) fastList.scrollToOffset?.(offset, { behavior: opts?.behavior });\n };\n scrollOnce();\n requestAnimationFrame(scrollOnce);\n },\n [fastListActionRef],\n );\n\n /**\n * Evaluated at call time from `ds-fast-list`'s `getVisibleItems()` (the virtualizer's own pre-overscan\n * viewport range), so it always reflects the current viewport, including after the user scrolls by hand.\n * It is empty before the container's first real measurement.\n */\n const computeVisibleItems = React.useCallback((): string[] => {\n if (document.visibilityState === 'hidden') {\n pendingOnScroll.current = true;\n return [];\n }\n const visibleItems = fastListActionRef.current.getVisibleItems?.() ?? [];\n if (visibleItems.length === 0) return [];\n pendingOnScroll.current = false;\n return visibleItems.map(String);\n }, [fastListActionRef, pendingOnScroll]);\n\n // Each call returns a fresh array, so callers using .pop()/.shift() can't affect later calls.\n const getMessagesInView = computeVisibleItems;\n\n // Debounced trailing: notifies onScrollEnds consumers after scrolling settles.\n const onScrollStop = React.useMemo(\n () =>\n debounce((): string[] => {\n const indexList = computeVisibleItems();\n if (onScrollEnds) onScrollEnds(indexList);\n return indexList;\n }, 500),\n [computeVisibleItems, onScrollEnds],\n ) as () => string[];\n\n /**\n * Focuses a message only if it is mounted now: it never focuses one later, when it scrolls into view. It never\n * scrolls either, as its name says, even when the message is only partly visible.\n */\n const onlyFocus = React.useCallback(\n (index: string) => {\n const position = Number(index);\n const row = messages?.[position];\n const mounted = fastListActionRef.current.getMountedItems?.() ?? [];\n if (row && mounted.includes(position))\n trackFocusMessage(getMessageFocusId(row, position), { preventScroll: true });\n },\n [messages, fastListActionRef, trackFocusMessage],\n );\n\n const scrollAndFocus = React.useCallback(\n (index: number, opts: DSChatT.ScrollActionOptions = { align: 'center' }) => {\n const row = messages?.[index];\n /** A message not rendered yet (added in the same handler) is targeted by its position; `vScroll`'s second write runs after the commit. */\n const messageId = row ? getMessageFocusId(row, index) : getMessagePositionId(index);\n trackFocusMessage(messageId, { preventScroll: true });\n vScroll(index, opts);\n /* [PUI-15629](https://jira.elliemae.io/browse/PUI-15629)\n * requestAnimationFrame is needed because the first scroll might not calculate positions\n * correctly if the DOM hasn't been fully updated yet. By waiting for the next animation\n * frame, we ensure that:\n * 1. The DOM has been updated with new positions after the first scroll\n * 2. Element height and position measurements are accurate\n * 3. The second scroll can align the element with correct measurements\n */\n requestAnimationFrame(() => {\n vScroll(index, opts);\n });\n },\n [messages, trackFocusMessage, vScroll],\n );\n\n const checkPendingOnScroll = React.useCallback(() => {\n if (pendingOnScroll.current === true) onScrollStop();\n }, [onScrollStop, pendingOnScroll]);\n\n return React.useMemo(\n () => ({\n vScroll,\n onScrollStop,\n getMessagesInView,\n onlyFocus,\n scrollAndFocus,\n checkPendingOnScroll,\n }),\n [vScroll, onScrollStop, getMessagesInView, onlyFocus, scrollAndFocus, checkPendingOnScroll],\n );\n};\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACGvB,OAAOA,YAAW;AAClB,SAAS,gBAAgB;AAGzB,SAAS,mBAAmB,4BAA4B;AAUjD,MAAM,2BAA2B,CAAC;AAAA,EACvC,kBAAkB,EAAE,cAAc,SAAS;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AACF,MAAoC;AAQlC,QAAM,UAAUA,OAAM;AAAA,IACpB,CAAC,OAAe,SAAuC;AACrD,YAAM,aAAa,MAAM;AACvB,cAAM,WAAW,kBAAkB;AAEnC,cAAM,SAAS,SAAS,oBAAoB,OAAO,MAAM,SAAS,QAAQ;AAC1E,YAAI,WAAW,OAAW,UAAS,iBAAiB,QAAQ,EAAE,UAAU,MAAM,SAAS,CAAC;AAAA,MAC1F;AACA,iBAAW;AACX,4BAAsB,UAAU;AAAA,IAClC;AAAA,IACA,CAAC,iBAAiB;AAAA,EACpB;AAOA,QAAM,sBAAsBA,OAAM,YAAY,MAAgB;AAC5D,QAAI,SAAS,oBAAoB,UAAU;AACzC,sBAAgB,UAAU;AAC1B,aAAO,CAAC;AAAA,IACV;AACA,UAAM,eAAe,kBAAkB,QAAQ,kBAAkB,KAAK,CAAC;AACvE,QAAI,aAAa,WAAW,EAAG,QAAO,CAAC;AACvC,oBAAgB,UAAU;AAC1B,WAAO,aAAa,IAAI,MAAM;AAAA,EAChC,GAAG,CAAC,mBAAmB,eAAe,CAAC;AAGvC,QAAM,oBAAoB;AAG1B,QAAM,eAAeA,OAAM;AAAA,IACzB,MACE,SAAS,MAAgB;AACvB,YAAM,YAAY,oBAAoB;AACtC,UAAI,aAAc,cAAa,SAAS;AACxC,aAAO;AAAA,IACT,GAAG,GAAG;AAAA,IACR,CAAC,qBAAqB,YAAY;AAAA,EACpC;AAMA,QAAM,YAAYA,OAAM;AAAA,IACtB,CAAC,UAAkB;AACjB,YAAM,WAAW,OAAO,KAAK;AAC7B,YAAM,MAAM,WAAW,QAAQ;AAC/B,YAAM,UAAU,kBAAkB,QAAQ,kBAAkB,KAAK,CAAC;AAClE,UAAI,OAAO,QAAQ,SAAS,QAAQ;AAClC,0BAAkB,kBAAkB,KAAK,QAAQ,GAAG,EAAE,eAAe,KAAK,CAAC;AAAA,IAC/E;AAAA,IACA,CAAC,UAAU,mBAAmB,iBAAiB;AAAA,EACjD;AAEA,QAAM,iBAAiBA,OAAM;AAAA,IAC3B,CAAC,OAAe,OAAoC,EAAE,OAAO,SAAS,MAAM;AAC1E,YAAM,MAAM,WAAW,KAAK;AAE5B,YAAM,YAAY,MAAM,kBAAkB,KAAK,KAAK,IAAI,qBAAqB,KAAK;AAClF,wBAAkB,WAAW,EAAE,eAAe,KAAK,CAAC;AACpD,cAAQ,OAAO,IAAI;AASnB,4BAAsB,MAAM;AAC1B,gBAAQ,OAAO,IAAI;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,IACA,CAAC,UAAU,mBAAmB,OAAO;AAAA,EACvC;AAEA,QAAM,uBAAuBA,OAAM,YAAY,MAAM;AACnD,QAAI,gBAAgB,YAAY,KAAM,cAAa;AAAA,EACrD,GAAG,CAAC,cAAc,eAAe,CAAC;AAElC,SAAOA,OAAM;AAAA,IACX,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,SAAS,cAAc,mBAAmB,WAAW,gBAAgB,oBAAoB;AAAA,EAC5F;AACF;",
|
|
6
6
|
"names": ["React"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/chat-container-messages-list/useGlobalEventListener.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React from 'react';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { type DSChatT } from '../../react-desc-prop-types.js';\n\ntype UseGlobalEventListenerArgs = {\n propsWithDefault: DSChatT.MessagesListProps;\n virtualListRef: React.MutableRefObject<HTMLDivElement |
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React from 'react';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { type DSChatT } from '../../react-desc-prop-types.js';\n\ntype UseGlobalEventListenerArgs = {\n propsWithDefault: DSChatT.MessagesListProps;\n virtualListRef: React.MutableRefObject<HTMLDivElement | null>;\n onScrollStop: () => string[];\n checkPendingOnScroll: TypescriptHelpersT.GenericFunc;\n};\n\nexport const useGlobalEventListener = ({\n propsWithDefault: { onScrollEnds },\n virtualListRef,\n onScrollStop,\n checkPendingOnScroll,\n}: UseGlobalEventListenerArgs) => {\n React.useEffect(() => {\n let handlerCallback: ReturnType<typeof setTimeout>;\n const localVirtualRef = virtualListRef.current;\n const onScroll = () => {\n if (handlerCallback) clearTimeout(handlerCallback);\n handlerCallback = setTimeout(onScrollStop, 300);\n };\n if (onScrollEnds) {\n virtualListRef.current?.addEventListener('scroll', onScroll);\n }\n document.addEventListener('visibilitychange', checkPendingOnScroll);\n return () => {\n if (handlerCallback) clearTimeout(handlerCallback);\n if (onScrollEnds && localVirtualRef) localVirtualRef.removeEventListener('scroll', onScroll);\n document.removeEventListener('visibilitychange', checkPendingOnScroll);\n };\n }, [onScrollStop, onScrollEnds, checkPendingOnScroll, virtualListRef]);\n};\n"],
|
|
5
5
|
"mappings": "AAAA,YAAY,WAAW;ACAvB,OAAOA,YAAW;AAWX,MAAM,yBAAyB,CAAC;AAAA,EACrC,kBAAkB,EAAE,aAAa;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AACF,MAAkC;AAChC,EAAAA,OAAM,UAAU,MAAM;AACpB,QAAI;AACJ,UAAM,kBAAkB,eAAe;AACvC,UAAM,WAAW,MAAM;AACrB,UAAI,gBAAiB,cAAa,eAAe;AACjD,wBAAkB,WAAW,cAAc,GAAG;AAAA,IAChD;AACA,QAAI,cAAc;AAChB,qBAAe,SAAS,iBAAiB,UAAU,QAAQ;AAAA,IAC7D;AACA,aAAS,iBAAiB,oBAAoB,oBAAoB;AAClE,WAAO,MAAM;AACX,UAAI,gBAAiB,cAAa,eAAe;AACjD,UAAI,gBAAgB,gBAAiB,iBAAgB,oBAAoB,UAAU,QAAQ;AAC3F,eAAS,oBAAoB,oBAAoB,oBAAoB;AAAA,IACvE;AAAA,EACF,GAAG,CAAC,cAAc,cAAc,sBAAsB,cAAc,CAAC;AACvE;",
|
|
6
6
|
"names": ["React"]
|
|
7
7
|
}
|