UNPKG

@fluentui/react-northstar

Version:
538 lines (532 loc) 24.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.chatMessageSlotClassNames = exports.chatMessageClassName = exports.ChatMessage = void 0; var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose")); var _invoke2 = _interopRequireDefault(require("lodash/invoke")); var _isNil2 = _interopRequireDefault(require("lodash/isNil")); var _accessibility = require("@fluentui/accessibility"); var _reactBindings = require("@fluentui/react-bindings"); var _reactComponentRef = require("@fluentui/react-component-ref"); var customPropTypes = _interopRequireWildcard(require("@fluentui/react-proptypes")); var _classnames = _interopRequireDefault(require("classnames")); var PropTypes = _interopRequireWildcard(require("prop-types")); var React = _interopRequireWildcard(require("react")); var _utils = require("../../utils"); var _positioner = require("../../utils/positioner"); var _Box = require("../Box/Box"); var _Flex = require("../Flex/Flex"); var _Label = require("../Label/Label"); var _Menu = require("../Menu/Menu"); var _PortalInner = require("../Portal/PortalInner"); var _Reaction = require("../Reaction/Reaction"); var _Text = require("../Text/Text"); var _chatContext = require("./chatContext"); var _chatItemContext = require("./chatItemContext"); var _ChatMessageDetails = require("./ChatMessageDetails"); var _ChatMessageHeader = require("./ChatMessageHeader"); var _ChatMessageReadStatus = require("./ChatMessageReadStatus"); var _ChatMessageContent = require("./ChatMessageContent"); var _excluded = ["inline", "showActionMenu"]; function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } var chatMessageClassName = 'ui-chat__message'; exports.chatMessageClassName = chatMessageClassName; var chatMessageSlotClassNames = { actionMenu: chatMessageClassName + "__actions", author: chatMessageClassName + "__author", badge: chatMessageClassName + "__badge", bar: chatMessageClassName + "__bar", body: chatMessageClassName + "__body", bubble: chatMessageClassName + "__bubble", bubbleInset: chatMessageClassName + "__bubble-inset", compactBody: chatMessageClassName + "__compact-body", reactionGroup: chatMessageClassName + "__reactions", timestamp: chatMessageClassName + "__timestamp" }; exports.chatMessageSlotClassNames = chatMessageSlotClassNames; function partitionActionMenuPropsFromShorthand(value) { if (typeof value === 'object' && value !== null && !Array.isArray(value)) { var _ref = value, inline = _ref.inline, showActionMenu = _ref.showActionMenu, props = (0, _objectWithoutPropertiesLoose2.default)(_ref, _excluded); return [props, inline != null ? inline : true, showActionMenu]; } return [value, true, false]; } /** * A ChatMessage represents a single message in chat. */ var ChatMessage = /*#__PURE__*/React.forwardRef(function (inputProps, ref) { var context = (0, _reactBindings.useFluentContext)(); var _useTelemetry = (0, _reactBindings.useTelemetry)(ChatMessage.displayName, context.telemetry), setStart = _useTelemetry.setStart, setEnd = _useTelemetry.setEnd; setStart(); var parentAttached = (0, _reactBindings.useContextSelector)(_chatItemContext.ChatItemContext, function (v) { return v.attached; }); var chatProps = (0, _chatContext.useChatContextSelectors)({ density: function density(v) { return v.density; }, accessibility: function accessibility(v) { return v.behaviors.message; } }); var props = Object.assign({}, inputProps, { density: inputProps.density === undefined ? chatProps.density : inputProps.density, accessibility: inputProps.accessibility === undefined ? chatProps.accessibility || _accessibility.chatMessageBehavior : inputProps.accessibility }); var accessibility = props.accessibility, _props$attached = props.attached, attached = _props$attached === void 0 ? parentAttached : _props$attached, author = props.author, badge = props.badge, badgePosition = props.badgePosition, children = props.children, className = props.className, compactBody = props.compactBody, content = props.content, density = props.density, design = props.design, details = props.details, header = props.header, mine = props.mine, positionActionMenu = props.positionActionMenu, reactionGroup = props.reactionGroup, reactionGroupPosition = props.reactionGroupPosition, readStatus = props.readStatus, styles = props.styles, timestamp = props.timestamp, overflow = props.unstable_overflow, _props$unstable_layou = props.unstable_layout, layout = _props$unstable_layou === void 0 ? 'default' : _props$unstable_layou, variables = props.variables, failed = props.failed, bubble = props.bubble, body = props.body, bubbleInset = props.bubbleInset, bubbleInsetContent = props.bubbleInsetContent, headerContent = props.headerContent; var isRefreshComfyLayout = layout === 'refresh' && density === 'comfy'; var _partitionPopperProps = (0, _positioner.partitionPopperPropsFromShorthand)(props.actionMenu), actionMenuOptions = _partitionPopperProps[0], positioningProps = _partitionPopperProps[1]; var _partitionActionMenuP = partitionActionMenuPropsFromShorthand(actionMenuOptions), actionMenu = _partitionActionMenuP[0], inlineActionMenu = _partitionActionMenuP[1], controlledShowActionMenu = _partitionActionMenuP[2]; var _useAutoControlled = (0, _reactBindings.useAutoControlled)({ defaultValue: false, value: controlledShowActionMenu }), showActionMenu = _useAutoControlled[0], setShowActionMenu = _useAutoControlled[1]; var hasActionMenu = !(0, _isNil2.default)(actionMenu); var hasHeaderReactionGroup = !!reactionGroup && reactionGroupPosition === 'start'; var actionMenuId = React.useRef(); actionMenuId.current = (0, _utils.getOrGenerateIdFromShorthand)(chatMessageClassName + "-", actionMenu, actionMenuId.current); var modifiers = React.useCallback(function (target, container) { return positionActionMenu && [ // https://popper.js.org/docs/v2/modifiers/flip/ // Forces to flip only in "top-*" positions { name: 'flip', options: { fallbackPlacements: ['top'] } }, overflow && { name: 'preventOverflow', options: { boundary: (0, _positioner.getScrollParent)(container) } }]; }, [positionActionMenu, overflow]); var popperRef = React.useRef(); var _usePopper = (0, _positioner.usePopper)(Object.assign({ align: 'end', rtl: context.rtl, position: 'above', positionFixed: overflow, enabled: hasActionMenu && positionActionMenu, modifiers: modifiers }, positioningProps, { popperRef: (0, _reactBindings.useMergedRefs)(positioningProps == null ? void 0 : positioningProps.popperRef, popperRef) })), actionsMenuTargetRef = _usePopper.targetRef, actionsMenuRef = _usePopper.containerRef; // `focused` state is used for show/hide actionMenu var _React$useState = React.useState(false), focused = _React$useState[0], setFocused = _React$useState[1]; var getA11Props = (0, _reactBindings.useAccessibility)(accessibility, { actionHandlers: { // prevents default FocusZone behavior, e.g., in ChatMessageBehavior, it prevents FocusZone from using arrow keys // as navigation (only Tab key should work) preventDefault: function preventDefault(event) { // preventDefault only if event coming from inside the message if (event.currentTarget !== event.target) { event.preventDefault(); } }, focus: function focus(event) { var target = actionsMenuTargetRef.current; if (target) { target.focus(); event.stopPropagation(); } } }, debugName: ChatMessage.displayName, mapPropsToBehavior: function mapPropsToBehavior() { return { hasActionMenu: hasActionMenu, inlineActionMenu: inlineActionMenu, actionMenuId: actionMenuId.current }; }, rtl: context.rtl }); var _useStyles = (0, _reactBindings.useStyles)(ChatMessage.displayName, { className: chatMessageClassName, mapPropsToStyles: function mapPropsToStyles() { return { attached: attached, badgePosition: badgePosition, density: density, focused: focused, hasActionMenu: hasActionMenu, hasBadge: !!badge, hasHeaderReactionGroup: hasHeaderReactionGroup, mine: mine, showActionMenu: showActionMenu, hasReactions: !!reactionGroup, failed: failed, layout: layout }; }, mapPropsToInlineStyles: function mapPropsToInlineStyles() { return { className: className, design: design, styles: styles, variables: variables }; }, rtl: context.rtl }), classes = _useStyles.classes, resolvedStyles = _useStyles.styles; var handleFocus = function handleFocus(e) { var _popperRef$current; (_popperRef$current = popperRef.current) == null ? void 0 : _popperRef$current.updatePosition(); // react onFocus is called even when nested component receives focus (i.e. it bubbles) // so when focus moves within actionMenu, the `focus` state in chatMessage remains true, and keeps actionMenu visible setFocused(true); (0, _invoke2.default)(props, 'onFocus', e, props); }; var handleBlur = function handleBlur(e) { // `focused` controls is focused the whole `ChatMessage` or any of its children. When we're navigating // with keyboard the focused element will be changed and there is no way to use `:focus` selector var shouldPreserveFocusState = (0, _invoke2.default)(e, 'currentTarget.contains', e.relatedTarget); setFocused(shouldPreserveFocusState); setShowActionMenu(false); (0, _invoke2.default)(props, 'onBlur', e, props); }; var handleMouseEnter = function handleMouseEnter(e) { var _popperRef$current2; (_popperRef$current2 = popperRef.current) == null ? void 0 : _popperRef$current2.updatePosition(); if (hasActionMenu && !inlineActionMenu) { setShowActionMenu(true); } (0, _invoke2.default)(props, 'onMouseEnter', e, props); }; var handleMouseLeave = function handleMouseLeave(e) { if (!focused && hasActionMenu && !inlineActionMenu) { setShowActionMenu(false); } (0, _invoke2.default)(props, 'onMouseLeave', e, props); }; var renderActionMenu = function renderActionMenu() { var actionMenuElement = _Menu.Menu.create(actionMenu, { defaultProps: function defaultProps() { var _ref2; return _ref2 = {}, _ref2[_accessibility.IS_FOCUSABLE_ATTRIBUTE] = true, _ref2.accessibility = _accessibility.menuAsToolbarBehavior, _ref2.className = chatMessageSlotClassNames.actionMenu, _ref2.styles = resolvedStyles.actionMenu, _ref2; }, overrideProps: { id: actionMenuId.current } }); var content = actionMenuElement ? /*#__PURE__*/React.createElement(_reactComponentRef.Ref, { innerRef: actionsMenuRef }, actionMenuElement) : actionMenuElement; return inlineActionMenu || !content ? content : /*#__PURE__*/React.createElement(_PortalInner.PortalInner, null, content); }; var handleKeyDown = function handleKeyDown(e) { if (hasActionMenu && !inlineActionMenu) { var _actionsMenuRef$curre, _actionsMenuRef$curre2, _actionsMenuRef$curre3; // reference: https://github.com/microsoft/fluentui/pull/17329 var toFocusItemInActionMenu = (_actionsMenuRef$curre = (_actionsMenuRef$curre2 = actionsMenuRef.current) == null ? void 0 : _actionsMenuRef$curre2.querySelector('[tabindex="0"]')) != null ? _actionsMenuRef$curre : (_actionsMenuRef$curre3 = actionsMenuRef.current) == null ? void 0 : _actionsMenuRef$curre3.querySelectorAll('[tabindex="-1"]:not([data-is-focusable="false"])')[0]; if (e.keyCode === _accessibility.keyboardKey.Enter) { toFocusItemInActionMenu == null ? void 0 : toFocusItemInActionMenu.focus(); e.stopPropagation(); e.preventDefault(); } if (e.keyCode === _accessibility.keyboardKey.Tab) { // TAB/SHIFT+TAB cycles focus among actionMenu and focusable elements within chat message var isShift = !!e.shiftKey; var focusableElementsInsideMessage = e.currentTarget.querySelectorAll('[tabindex="-1"]:not([data-is-focusable="false"])'); var firstFocusableInsideMessage = focusableElementsInsideMessage[0]; var lastFocusableInsideMessage = focusableElementsInsideMessage[focusableElementsInsideMessage.length - 1]; if (e.target === toFocusItemInActionMenu) { // focus is now inside action menu // cycle focus into the first/last focusable element inside chat message if (isShift) { lastFocusableInsideMessage == null ? void 0 : lastFocusableInsideMessage.focus(); } else { firstFocusableInsideMessage == null ? void 0 : firstFocusableInsideMessage.focus(); } e.stopPropagation(); e.preventDefault(); } else { var boundaryElementInsideMessage = isShift ? firstFocusableInsideMessage : lastFocusableInsideMessage; if (e.target === boundaryElementInsideMessage) { // focus is now on the first/last focusable element inside chat message toFocusItemInActionMenu.focus(); // cycle focus back into action Menu e.stopPropagation(); e.preventDefault(); } } } } (0, _invoke2.default)(props, 'onKeyDown', e, props); }; var childrenPropExists = (0, _utils.childrenExist)(children); var rootClasses = childrenPropExists ? (0, _classnames.default)(classes.root, classes.content) : classes.root; var ElementType = (0, _reactBindings.getElementType)(props); var unhandledProps = (0, _reactBindings.useUnhandledProps)(ChatMessage.handledProps, props); var badgeElement = _Label.Label.create(badge, { defaultProps: function defaultProps() { return { className: chatMessageSlotClassNames.badge, styles: resolvedStyles.badge }; } }); var reactionGroupElement = _Reaction.Reaction.Group.create(reactionGroup, { defaultProps: function defaultProps() { return { className: chatMessageSlotClassNames.reactionGroup, styles: resolvedStyles.reactionGroup }; } }); var actionMenuElement = renderActionMenu(); var authorElement = _Text.Text.create(author, { defaultProps: function defaultProps() { return { size: density === 'comfy' ? 'small' : undefined, styles: resolvedStyles.author, className: chatMessageSlotClassNames.author }; } }); var timestampElement = _Text.Text.create(timestamp, { defaultProps: function defaultProps() { return { size: 'small', styles: resolvedStyles.timestamp, timestamp: true, className: chatMessageSlotClassNames.timestamp }; } }); var messageContent = (0, _utils.createShorthand)(_ChatMessageContent.ChatMessageContent, content, { defaultProps: function defaultProps() { return { badgePosition: badgePosition, density: density, failed: failed, hasBadge: !!badge, mine: mine, unstable_layout: layout }; }, overrideProps: function overrideProps(predefinedProps) { return { variables: (0, _reactBindings.mergeVariablesOverrides)(variables, predefinedProps.variables) }; } }); var detailsElement = (0, _utils.createShorthand)(_ChatMessageDetails.ChatMessageDetails, details, { defaultProps: function defaultProps() { return { attached: attached, density: density, hasHeaderReactionGroup: hasHeaderReactionGroup, mine: mine }; } }); var readStatusElement = (0, _utils.createShorthand)(_ChatMessageReadStatus.ChatMessageReadStatus, readStatus, { defaultProps: function defaultProps() { return { density: density }; } }); var elements = /*#__PURE__*/React.createElement(React.Fragment, null); if (density === 'compact') { var headerElement = (0, _utils.createShorthand)(_ChatMessageHeader.ChatMessageHeader, header); var bodyElement = _Box.Box.create(compactBody || {}, { defaultProps: function defaultProps() { return getA11Props('compactBody', { className: chatMessageSlotClassNames.compactBody, styles: resolvedStyles.compactBody }); }, overrideProps: function overrideProps() { return { content: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_Flex.Flex.Item, { grow: 1 }, /*#__PURE__*/React.createElement("div", null, authorElement, messageContent)), timestampElement, detailsElement, badgeElement) }; } }); elements = /*#__PURE__*/React.createElement(React.Fragment, null, actionMenuElement, /*#__PURE__*/React.createElement("div", { className: chatMessageSlotClassNames.bar }), headerElement, bodyElement, reactionGroupElement, readStatusElement); } else if (isRefreshComfyLayout) { var _headerElement = (0, _utils.createShorthand)(_ChatMessageHeader.ChatMessageHeader, header || {}, { defaultProps: function defaultProps() { return { styles: resolvedStyles.header, content: /*#__PURE__*/React.createElement(React.Fragment, null, authorElement, headerContent, detailsElement) }; } }); var bubbleElement = _Box.Box.create(bubble || {}, { defaultProps: function defaultProps() { return getA11Props('bubble', { className: chatMessageSlotClassNames.bubble, styles: resolvedStyles.bubble }); }, overrideProps: function overrideProps() { return { ref: actionsMenuTargetRef, content: /*#__PURE__*/React.createElement(React.Fragment, null, actionMenuElement, messageContent, reactionGroupElement, readStatusElement), onMouseEnter: function onMouseEnter(e) { var _popperRef$current3; (_popperRef$current3 = popperRef.current) == null ? void 0 : _popperRef$current3.updatePosition(); handleMouseEnter(e); }, onMouseLeave: function onMouseLeave(e) { handleMouseLeave(e); } }; } }); var bubbleInsetElement = _Box.Box.create(bubbleInset || {}, { defaultProps: function defaultProps() { return { as: 'span', className: chatMessageSlotClassNames.bubbleInset, styles: resolvedStyles.bubbleInset }; }, overrideProps: function overrideProps() { return { content: /*#__PURE__*/React.createElement(React.Fragment, null, badgeElement, bubbleInsetContent, timestampElement) }; } }); var _bodyElement = _Box.Box.create(body || {}, { defaultProps: function defaultProps() { return getA11Props('body', { className: chatMessageSlotClassNames.body, styles: resolvedStyles.body }); }, overrideProps: function overrideProps() { return { content: /*#__PURE__*/React.createElement(React.Fragment, null, bubbleElement, bubbleInsetElement) }; } }); elements = /*#__PURE__*/React.createElement(React.Fragment, null, _headerElement, _bodyElement); } else { var _headerElement2 = (0, _utils.createShorthand)(_ChatMessageHeader.ChatMessageHeader, header || {}, { overrideProps: function overrideProps() { return { content: /*#__PURE__*/React.createElement(React.Fragment, null, authorElement, timestampElement, detailsElement, reactionGroupPosition === 'start' && reactionGroupElement) }; } }); elements = /*#__PURE__*/React.createElement(React.Fragment, null, actionMenuElement, /*#__PURE__*/React.createElement("div", { className: chatMessageSlotClassNames.bar }), badgePosition === 'start' && badgeElement, _headerElement2, messageContent, reactionGroupPosition === 'end' && reactionGroupElement, badgePosition === 'end' && badgeElement, readStatusElement); } var element = /*#__PURE__*/React.createElement(_reactComponentRef.Ref, { innerRef: !isRefreshComfyLayout && actionsMenuTargetRef }, getA11Props.unstable_wrapWithFocusZone( /*#__PURE__*/React.createElement(ElementType, getA11Props('root', Object.assign({ className: rootClasses, onBlur: handleBlur, onFocus: handleFocus, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onKeyDown: handleKeyDown, ref: ref }, _utils.rtlTextContainer.getAttributes({ forElements: [children] }), unhandledProps)), childrenPropExists ? children : elements))); setEnd(); return element; }); exports.ChatMessage = ChatMessage; ChatMessage.displayName = 'ChatMessage'; ChatMessage.defaultProps = { badgePosition: 'end', positionActionMenu: true, reactionGroupPosition: 'start' }; ChatMessage.propTypes = Object.assign({}, _utils.commonPropTypes.createCommon({ content: 'shorthand' }), { actionMenu: PropTypes.oneOfType([customPropTypes.itemShorthand, customPropTypes.collectionShorthand]), attached: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['top', 'bottom'])]), author: customPropTypes.itemShorthand, badge: customPropTypes.itemShorthand, badgePosition: PropTypes.oneOf(['start', 'end']), compactBody: customPropTypes.itemShorthand, density: PropTypes.oneOf(['comfy', 'compact']), details: customPropTypes.itemShorthand, header: customPropTypes.itemShorthand, mine: PropTypes.bool, onBlur: PropTypes.func, onFocus: PropTypes.func, onKeyDown: PropTypes.func, onMouseEnter: PropTypes.func, onMouseLeave: PropTypes.func, positionActionMenu: PropTypes.bool, reactionGroup: PropTypes.oneOfType([customPropTypes.collectionShorthand, customPropTypes.itemShorthand]), reactionGroupPosition: PropTypes.oneOf(['start', 'end']), readStatus: customPropTypes.itemShorthand, timestamp: customPropTypes.itemShorthand, unstable_overflow: PropTypes.bool, unstable_layout: PropTypes.oneOf(['default', 'refresh']), failed: PropTypes.bool, headerContent: PropTypes.node, body: customPropTypes.itemShorthand, bubble: customPropTypes.itemShorthand, bubbleInset: customPropTypes.itemShorthand, bubbleInsetContent: PropTypes.node }); ChatMessage.handledProps = Object.keys(ChatMessage.propTypes); ChatMessage.create = (0, _utils.createShorthandFactory)({ Component: ChatMessage, mappedProp: 'content' }); //# sourceMappingURL=ChatMessage.js.map