@box/threaded-annotations 4.6.5 → 4.7.0

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.
@@ -1 +1 @@
1
- import{t as MentionComponent}from"../../../../../../chunks/mention-component.js";import{isParagraphNode}from"../../../../v2/types/document-node-types.js";import messages from"../messages.js";import{t as text_message_module_default}from"../../../../../../chunks/text-message.module.js";import"../constants.js";import{splitTextIntoSegments}from"./url-segments.js";import{getDocumentLength,truncateDocument}from"./truncate-document.js";import{Link,Text,TextButton}from"@box/blueprint-web";import{useState}from"react";import{useIntl}from"react-intl";import{jsx,jsxs}from"react/jsx-runtime";var renderMentionNode=({node,index,paragraphIndex})=>{let key=`mention-${node.attrs.mentionedUserId}-${paragraphIndex}-${index}`;return jsx(MentionComponent,{authorId:node.attrs.authorId,mentionedUserEmail:node.attrs.mentionedUserEmail,mentionedUserId:node.attrs.mentionedUserId,mentionedUserName:node.attrs.mentionedUserName,mentionId:node.attrs.mentionId},key)},handleMessageLinkClick=event=>{event.currentTarget.closest(`[data-thread-expandable]`)&&event.preventDefault()},renderTextNode=({node,index,paragraphIndex,intl})=>{let key=`text-${node.text.substring(0,20)}-${paragraphIndex}-${index}`,segments=splitTextIntoSegments(node.text);return jsx(Text,{"aria-label":intl.formatMessage(messages.textMessageAriaLabel),as:`span`,color:`textOnLightDefault`,variant:`bodyDefault`,children:segments.map((segment,segmentIndex)=>segment.type===`url`?jsx(Link,{href:segment.value,inheritFont:!0,rel:`noopener noreferrer`,target:`_blank`,onClick:handleMessageLinkClick,children:segment.value},`${key}-segment-${segmentIndex}`):segment.value)},key)},renderParagraphContent=({content,paragraphIndex,intl})=>content.map((node,index)=>{switch(node.type){case`mention`:return renderMentionNode({node,index,paragraphIndex});case`text`:return renderTextNode({node,index,paragraphIndex,intl});default:return[]}}),renderParagraphNode=({paragraph,index,intl,inlineSuffix})=>{let content=paragraph.content||[],key=`paragraph-${index}`;return jsxs(Text,{as:`p`,variant:`bodyDefault`,children:[content.length>0?renderParagraphContent({content,paragraphIndex:index,intl}):jsx(`br`,{}),inlineSuffix]},key)},MessageRenderer=({messageObject})=>{let intl=useIntl(),[isExpanded,setIsExpanded]=useState(!1);if(!messageObject.content||messageObject.content.length===0)return null;let totalLength=getDocumentLength(messageObject),isTruncated=!isExpanded&&totalLength>450,paragraphsToRender=(isTruncated?truncateDocument(messageObject,450):messageObject).content.filter(isParagraphNode),suffixAnchorIndex=paragraphsToRender.length-1;for(let i=paragraphsToRender.length-1;i>=0;--i)if((paragraphsToRender[i].content?.length??0)>0){suffixAnchorIndex=i;break}let seeMoreButton=isTruncated?jsx(TextButton,{className:text_message_module_default.seeMoreButton,color:`textOnLightLink`,"data-no-expand":!0,onClick:()=>setIsExpanded(!0),variant:`bodyDefaultBold`,children:intl.formatMessage(messages.seeMore)}):null;return paragraphsToRender.map((paragraph,index)=>renderParagraphNode({paragraph,index,intl,inlineSuffix:index===suffixAnchorIndex?seeMoreButton:null}))};export{MessageRenderer};
1
+ import{t as MentionComponent}from"../../../../../../chunks/mention-component.js";import{isParagraphNode}from"../../../../v2/types/document-node-types.js";import messages from"../messages.js";import{t as text_message_module_default}from"../../../../../../chunks/text-message.module.js";import"../constants.js";import{groupInlineNodes,segmentTextNodes}from"./text-node-segments.js";import{getDocumentLength,truncateDocument}from"./truncate-document.js";import{Link,Text,TextButton}from"@box/blueprint-web";import React,{useState}from"react";import{useIntl}from"react-intl";import{jsx,jsxs}from"react/jsx-runtime";var renderMentionNode=({node,index,paragraphIndex})=>{let key=`mention-${node.attrs.mentionedUserId}-${paragraphIndex}-${index}`;return jsx(MentionComponent,{authorId:node.attrs.authorId,mentionedUserEmail:node.attrs.mentionedUserEmail,mentionedUserId:node.attrs.mentionedUserId,mentionedUserName:node.attrs.mentionedUserName,mentionId:node.attrs.mentionId},key)},handleMessageLinkClick=event=>{event.currentTarget.closest(`[data-thread-expandable]`)&&event.preventDefault()},applyTextMarks=(content,node)=>node.marks?.reduce((markedContent,mark)=>{switch(mark.type){case`bold`:return jsx(`strong`,{children:markedContent});case`italic`:return jsx(`em`,{children:markedContent});case`underline`:return jsx(`u`,{children:markedContent});default:return markedContent}},content)??content,renderTextNodes=({nodes,paragraphIndex,index,intl,isRichTextEnabled})=>{let key=`text-nodes-${paragraphIndex}-${index}`,segments=segmentTextNodes(nodes);return jsx(Text,{"aria-label":intl.formatMessage(messages.textMessageAriaLabel),as:`span`,color:`textOnLightDefault`,variant:`bodyDefault`,children:segments.map((segment,segmentIndex)=>segment.type===`url`?jsx(Link,{href:segment.value,inheritFont:!0,rel:`noopener noreferrer`,target:`_blank`,onClick:handleMessageLinkClick,children:segment.fragments.map((fragment,fragmentIndex)=>jsx(React.Fragment,{children:isRichTextEnabled?applyTextMarks(fragment.value,fragment.sourceNode):fragment.value},`${key}-segment-${segmentIndex}-fragment-${fragmentIndex}`))},`${key}-segment-${segmentIndex}`):segment.fragments.map((fragment,fragmentIndex)=>jsx(React.Fragment,{children:isRichTextEnabled?applyTextMarks(fragment.value,fragment.sourceNode):fragment.value},`${key}-segment-${segmentIndex}-fragment-${fragmentIndex}`)))},key)},renderParagraphContent=({content,paragraphIndex,intl,isRichTextEnabled})=>groupInlineNodes(content).map((group,groupIndex)=>group.kind===`mention`?renderMentionNode({node:group.node,index:groupIndex,paragraphIndex}):renderTextNodes({nodes:group.nodes,paragraphIndex,index:groupIndex,intl,isRichTextEnabled})),renderParagraphNode=({paragraph,index,intl,inlineSuffix,isRichTextEnabled})=>{let content=paragraph.content||[],key=`paragraph-${index}`;return jsxs(Text,{as:`p`,variant:`bodyDefault`,children:[content.length>0?renderParagraphContent({content,paragraphIndex:index,intl,isRichTextEnabled}):jsx(`br`,{}),inlineSuffix]},key)},MessageRenderer=({isRichTextEnabled=!1,messageObject})=>{let intl=useIntl(),[isExpanded,setIsExpanded]=useState(!1);if(!messageObject.content||messageObject.content.length===0)return null;let totalLength=getDocumentLength(messageObject),isTruncated=!isExpanded&&totalLength>450,paragraphsToRender=(isTruncated?truncateDocument(messageObject,450):messageObject).content.filter(isParagraphNode),suffixAnchorIndex=paragraphsToRender.length-1;for(let i=paragraphsToRender.length-1;i>=0;--i)if((paragraphsToRender[i].content?.length??0)>0){suffixAnchorIndex=i;break}let seeMoreButton=isTruncated?jsx(TextButton,{className:text_message_module_default.seeMoreButton,color:`textOnLightLink`,"data-no-expand":!0,onClick:()=>setIsExpanded(!0),variant:`bodyDefaultBold`,children:intl.formatMessage(messages.seeMore)}):null;return paragraphsToRender.map((paragraph,index)=>renderParagraphNode({paragraph,index,intl,inlineSuffix:index===suffixAnchorIndex?seeMoreButton:null,isRichTextEnabled}))};export{MessageRenderer};
@@ -0,0 +1 @@
1
+ import{splitTextIntoSegments}from"./url-segments.js";var groupInlineNodes=nodes=>{let groups=[],currentTextNodes=[],flushTextNodes=()=>{currentTextNodes.length>0&&(groups.push({kind:`text`,nodes:currentTextNodes}),currentTextNodes=[])};for(let node of nodes||[])node.type===`text`?currentTextNodes.push(node):node.type===`mention`?(flushTextNodes(),groups.push({kind:`mention`,node})):flushTextNodes();return flushTextNodes(),groups},getFragments=(nodes,start,end)=>{let fragments=[],nodeStart=0;for(let node of nodes){let nodeEnd=nodeStart+node.text.length,overlapStart=Math.max(start,nodeStart),overlapEnd=Math.min(end,nodeEnd);overlapStart<overlapEnd&&fragments.push({sourceNode:node,value:node.text.slice(overlapStart-nodeStart,overlapEnd-nodeStart)}),nodeStart=nodeEnd}return fragments},segmentTextNodes=nodes=>{let text=nodes.map(node=>node.text).join(``),offset=0;return splitTextIntoSegments(text).map(segment=>{let start=offset,end=start+segment.value.length;return offset=end,{...segment,fragments:getFragments(nodes,start,end)}})};export{groupInlineNodes,segmentTextNodes};
@@ -1 +1 @@
1
- import{THREADED_ANNOTATIONS_RESIN_COMPONENTS}from"../../../v2/resin-targets.js";import{MessageEditorVariant}from"../message-editor/message-editor.types.js";import{MessageEditor}from"../message-editor/message-editor.js";import{useThreadedAnnotationsContext}from"../../../v2/threaded-annotations-context.js";import{t as AnnotationBadge}from"../../../../../chunks/annotation-badge.js";import{MessageHeader}from"../message-header/message-header.js";import messages from"./messages.js";import{t as text_message_module_default}from"../../../../../chunks/text-message.module.js";import{MessageFooter}from"./subcomponents/message-footer.js";import{MessageRenderer}from"./subcomponents/message-renderer.js";import clsx from"clsx";import{useIntl}from"react-intl";import{Fragment,jsx,jsxs}from"react/jsx-runtime";var TextMessage=({id,author,message,createdAt,updatedAt,permissions,showThreadedIndicator=!0,isResolved=!1,isFirstMessage=!1,hasReplies=!1,maxCharacterCount,onAvatarClick,onCopyLink,onDelete,onThreadDelete,onEdit,onEditError,onResolve,onUnresolve,isRichTextEnabled=!1,onFormatToggle,userSelectorProps,annotationTarget,onAnnotationBadgeClick,originalContentDeleted})=>{let{canResolve,canEdit,canDelete}=permissions,intl=useIntl(),{dispatch,isReplyDisabled,isEditDisabled,isAnnotations,editingMessageId}=useThreadedAnnotationsContext(),isEditing=editingMessageId===id,canOpenEdit=!!onEdit&&!isEditDisabled&&canEdit;return jsxs(`div`,{"aria-label":intl.formatMessage(messages.annotationMessageAriaLabel),className:text_message_module_default.textMessage,"data-message-wrapper":!0,"data-message-id":id,"data-resin-component":THREADED_ANNOTATIONS_RESIN_COMPONENTS.COMMENT_CARD,children:[jsx(MessageHeader,{author,canResolve,isResolved,canDelete,canEdit:canOpenEdit,hasReplies,isFirstMessage,onAvatarClick,onCopyLink:onCopyLink?()=>onCopyLink(id):void 0,onDelete:()=>onDelete(id),onThreadDelete,onEdit:canOpenEdit?()=>dispatch({type:`EDIT`,id}):void 0,onResolve:onResolve?()=>onResolve(id):void 0,onUnresolve:onUnresolve?()=>onUnresolve(id):void 0}),jsxs(`div`,{className:text_message_module_default.contentWrapper,children:[jsx(`div`,{className:clsx(text_message_module_default.threadedIndicator,!showThreadedIndicator&&text_message_module_default.threadedIndicatorHidden)}),jsxs(`div`,{className:text_message_module_default.messageContent,children:[annotationTarget&&!isAnnotations&&jsx(`div`,{className:text_message_module_default.annotationBadgeWrapper,children:jsx(AnnotationBadge,{annotationTarget,onClick:()=>onAnnotationBadgeClick?.(id),originalContentDeleted})}),isEditing&&onEdit?jsx(`div`,{className:text_message_module_default.inlineEditSlot,children:jsx(MessageEditor,{initialContent:message,isRichTextEnabled,maxCharacterCount,onFormatToggle,onEditCancel:()=>dispatch({type:`CLEAR_EDIT`}),onEditSuccess:()=>dispatch({type:`CLEAR_EDIT`}),onSave:content=>onEdit(id,content),onSaveError:onEditError,userSelectorProps,variant:MessageEditorVariant.EDIT_MESSAGE})}):jsxs(Fragment,{children:[jsx(`div`,{"data-message-body":!0,children:jsx(MessageRenderer,{messageObject:message})}),jsx(MessageFooter,{createdAt,onReply:isReplyDisabled?void 0:()=>dispatch({type:`REPLY`,author}),updatedAt})]})]})]})]})};export{TextMessage};
1
+ import{THREADED_ANNOTATIONS_RESIN_COMPONENTS}from"../../../v2/resin-targets.js";import{MessageEditorVariant}from"../message-editor/message-editor.types.js";import{MessageEditor}from"../message-editor/message-editor.js";import{useThreadedAnnotationsContext}from"../../../v2/threaded-annotations-context.js";import{t as AnnotationBadge}from"../../../../../chunks/annotation-badge.js";import{MessageHeader}from"../message-header/message-header.js";import messages from"./messages.js";import{t as text_message_module_default}from"../../../../../chunks/text-message.module.js";import{MessageFooter}from"./subcomponents/message-footer.js";import{MessageRenderer}from"./subcomponents/message-renderer.js";import clsx from"clsx";import{useIntl}from"react-intl";import{Fragment,jsx,jsxs}from"react/jsx-runtime";var TextMessage=({id,author,message,createdAt,updatedAt,permissions,showThreadedIndicator=!0,isResolved=!1,isFirstMessage=!1,hasReplies=!1,maxCharacterCount,onAvatarClick,onCopyLink,onDelete,onThreadDelete,onEdit,onEditError,onResolve,onUnresolve,isRichTextEnabled=!1,onFormatToggle,userSelectorProps,annotationTarget,onAnnotationBadgeClick,originalContentDeleted})=>{let{canResolve,canEdit,canDelete}=permissions,intl=useIntl(),{dispatch,isReplyDisabled,isEditDisabled,isAnnotations,editingMessageId}=useThreadedAnnotationsContext(),isEditing=editingMessageId===id,canOpenEdit=!!onEdit&&!isEditDisabled&&canEdit;return jsxs(`div`,{"aria-label":intl.formatMessage(messages.annotationMessageAriaLabel),className:text_message_module_default.textMessage,"data-message-wrapper":!0,"data-message-id":id,"data-resin-component":THREADED_ANNOTATIONS_RESIN_COMPONENTS.COMMENT_CARD,children:[jsx(MessageHeader,{author,canResolve,isResolved,canDelete,canEdit:canOpenEdit,hasReplies,isFirstMessage,onAvatarClick,onCopyLink:onCopyLink?()=>onCopyLink(id):void 0,onDelete:()=>onDelete(id),onThreadDelete,onEdit:canOpenEdit?()=>dispatch({type:`EDIT`,id}):void 0,onResolve:onResolve?()=>onResolve(id):void 0,onUnresolve:onUnresolve?()=>onUnresolve(id):void 0}),jsxs(`div`,{className:text_message_module_default.contentWrapper,children:[jsx(`div`,{className:clsx(text_message_module_default.threadedIndicator,!showThreadedIndicator&&text_message_module_default.threadedIndicatorHidden)}),jsxs(`div`,{className:text_message_module_default.messageContent,children:[annotationTarget&&!isAnnotations&&jsx(`div`,{className:text_message_module_default.annotationBadgeWrapper,children:jsx(AnnotationBadge,{annotationTarget,onClick:()=>onAnnotationBadgeClick?.(id),originalContentDeleted})}),isEditing&&onEdit?jsx(`div`,{className:text_message_module_default.inlineEditSlot,children:jsx(MessageEditor,{initialContent:message,isRichTextEnabled,maxCharacterCount,onFormatToggle,onEditCancel:()=>dispatch({type:`CLEAR_EDIT`}),onEditSuccess:()=>dispatch({type:`CLEAR_EDIT`}),onSave:content=>onEdit(id,content),onSaveError:onEditError,userSelectorProps,variant:MessageEditorVariant.EDIT_MESSAGE})}):jsxs(Fragment,{children:[jsx(`div`,{"data-message-body":!0,children:jsx(MessageRenderer,{isRichTextEnabled,messageObject:message})}),jsx(MessageFooter,{createdAt,onReply:isReplyDisabled?void 0:()=>dispatch({type:`REPLY`,author}),updatedAt})]})]})]})]})};export{TextMessage};
@@ -1,6 +1,7 @@
1
1
  import { DocumentNode } from '../../../../v2/types';
2
2
  interface MessageRendererProps {
3
+ isRichTextEnabled?: boolean;
3
4
  messageObject: DocumentNode;
4
5
  }
5
- export declare const MessageRenderer: ({ messageObject }: MessageRendererProps) => import("react/jsx-runtime").JSX.Element[];
6
+ export declare const MessageRenderer: ({ isRichTextEnabled, messageObject }: MessageRendererProps) => import("react/jsx-runtime").JSX.Element[];
6
7
  export {};
@@ -0,0 +1,9 @@
1
+ import { InlineNode, TextNode } from '../../../../v2/types';
2
+ import { InlineGroup, TextNodeSegment } from './types';
3
+ /**
4
+ * Groups paragraph content into adjacent TextNodes, split by mentions.
5
+ * Mentions are hard boundaries: a URL never spans across one, so consumers
6
+ * segment each group independently.
7
+ */
8
+ export declare const groupInlineNodes: (nodes: InlineNode[] | undefined) => InlineGroup[];
9
+ export declare const segmentTextNodes: (nodes: TextNode[]) => TextNodeSegment[];
@@ -0,0 +1,24 @@
1
+ import { MentionNode, TextNode } from '../../../../v2/types';
2
+ export type TextSegment = {
3
+ type: 'text' | 'url';
4
+ value: string;
5
+ };
6
+ /**
7
+ * A mention, or a maximal sequence of adjacent TextNodes. Continuously reading
8
+ * text can still arrive as several nodes split on mark boundaries (e.g. a bold
9
+ * word), so consumers that scan text work on a whole group at a time.
10
+ */
11
+ export type InlineGroup = {
12
+ kind: 'text';
13
+ nodes: TextNode[];
14
+ } | {
15
+ kind: 'mention';
16
+ node: MentionNode;
17
+ };
18
+ export interface TextNodeFragment {
19
+ sourceNode: TextNode;
20
+ value: string;
21
+ }
22
+ export interface TextNodeSegment extends TextSegment {
23
+ fragments: TextNodeFragment[];
24
+ }
@@ -1,7 +1,4 @@
1
+ import { TextSegment } from './types';
1
2
  export declare const URL_REGEX: RegExp;
2
3
  export declare const trimTrailingChars: (url: string) => string;
3
- export type TextSegment = {
4
- type: 'text' | 'url';
5
- value: string;
6
- };
7
4
  export declare const splitTextIntoSegments: (text: string) => TextSegment[];
@@ -12,13 +12,15 @@ export type MentionRenderAttributes = {
12
12
  authorId: string;
13
13
  mentionId: string;
14
14
  };
15
+ export type TextMarkType = 'bold' | 'italic' | 'underline';
16
+ export type TextMark = {
17
+ type: TextMarkType;
18
+ attrs?: Record<string, unknown>;
19
+ };
15
20
  export type TextNode = {
16
21
  type: 'text';
17
22
  text: string;
18
- marks?: Array<{
19
- type: string;
20
- attrs?: Record<string, unknown>;
21
- }>;
23
+ marks?: TextMark[];
22
24
  };
23
25
  export type ParagraphNode = {
24
26
  type: 'paragraph';
@@ -45,8 +47,10 @@ export type DocumentNode = {
45
47
  content: (BulletListNode | OrderedListNode | ParagraphNode)[];
46
48
  attrs?: Record<string, unknown>;
47
49
  };
50
+ /** Nodes that can appear inside a paragraph. */
51
+ export type InlineNode = MentionNode | TextNode;
48
52
  /** Formats toggleable from the rich text formatting bar (or their keyboard shortcuts). */
49
- export type RichTextFormat = 'bold' | 'italic' | 'underline' | 'bulletList' | 'orderedList';
53
+ export type RichTextFormat = TextMarkType | 'bulletList' | 'orderedList';
50
54
  /**
51
55
  * Narrows a top-level document block to a paragraph. Consumers that only
52
56
  * support paragraph blocks (renderer, truncation — dedicated list support
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@box/threaded-annotations",
3
- "version": "4.6.5",
3
+ "version": "4.7.0",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "dependencies": {
6
6
  "@floating-ui/dom": "^1.6.0",
@@ -21,23 +21,23 @@
21
21
  "uuid": "^9.0.1"
22
22
  },
23
23
  "peerDependencies": {
24
- "@box/blueprint-web": "^16.19.0",
25
- "@box/blueprint-web-assets": "^5.7.4",
26
- "@box/collaboration-popover": "^2.2.13",
27
- "@box/readable-time": "^2.2.13",
28
- "@box/user-selector": "^2.2.13",
24
+ "@box/blueprint-web": "^16.19.1",
25
+ "@box/blueprint-web-assets": "^5.7.5",
26
+ "@box/collaboration-popover": "^2.2.14",
27
+ "@box/readable-time": "^2.2.14",
28
+ "@box/user-selector": "^2.2.14",
29
29
  "clsx": "^1.2.1",
30
30
  "react": "^18.0.0",
31
31
  "react-dom": "^18.0.0",
32
32
  "react-intl": "^6.4.2"
33
33
  },
34
34
  "devDependencies": {
35
- "@box/blueprint-web": "^16.19.0",
36
- "@box/blueprint-web-assets": "^5.7.4",
35
+ "@box/blueprint-web": "^16.19.1",
36
+ "@box/blueprint-web-assets": "^5.7.5",
37
37
  "@box/eslint-plugin-blueprint": "2.2.3",
38
- "@box/readable-time": "^2.2.13",
39
- "@box/storybook-utils": "^1.2.13",
40
- "@box/user-selector": "^2.2.13",
38
+ "@box/readable-time": "^2.2.14",
39
+ "@box/storybook-utils": "^1.2.14",
40
+ "@box/user-selector": "^2.2.14",
41
41
  "react": "^18.0.0",
42
42
  "react-dom": "^18.0.0"
43
43
  },