@cossistant/react 0.0.29 → 0.0.31
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/README.md +3 -1
- package/_virtual/rolldown_runtime.js +9 -23
- package/hooks/index.d.ts +2 -2
- package/hooks/private/store/use-conversations-store.d.ts +2 -0
- package/hooks/private/store/use-conversations-store.d.ts.map +1 -1
- package/hooks/private/store/use-conversations-store.js +15 -8
- package/hooks/private/store/use-conversations-store.js.map +1 -1
- package/hooks/private/store/use-store-selector.d.ts +3 -0
- package/hooks/private/store/use-store-selector.d.ts.map +1 -1
- package/hooks/private/store/use-store-selector.js +4 -8
- package/hooks/private/store/use-store-selector.js.map +1 -1
- package/hooks/private/store/use-website-store.d.ts +3 -1
- package/hooks/private/store/use-website-store.d.ts.map +1 -1
- package/hooks/private/store/use-website-store.js +14 -6
- package/hooks/private/store/use-website-store.js.map +1 -1
- package/hooks/private/use-client-query.d.ts +1 -1
- package/hooks/private/use-client-query.d.ts.map +1 -1
- package/hooks/private/use-client-query.js +1 -0
- package/hooks/private/use-client-query.js.map +1 -1
- package/hooks/private/use-default-messages.d.ts +1 -1
- package/hooks/private/use-grouped-messages.d.ts +2 -2
- package/hooks/private/use-grouped-messages.d.ts.map +1 -1
- package/hooks/private/use-grouped-messages.js +34 -10
- package/hooks/private/use-grouped-messages.js.map +1 -1
- package/hooks/private/use-rest-client.d.ts +13 -3
- package/hooks/private/use-rest-client.d.ts.map +1 -1
- package/hooks/private/use-rest-client.js +49 -22
- package/hooks/private/use-rest-client.js.map +1 -1
- package/hooks/private/use-visitor-typing-reporter.d.ts +1 -1
- package/hooks/use-conversation-auto-seen.d.ts +1 -1
- package/hooks/use-conversation-page.d.ts +1 -1
- package/hooks/use-conversation-page.d.ts.map +1 -1
- package/hooks/use-conversation-page.js +13 -4
- package/hooks/use-conversation-page.js.map +1 -1
- package/hooks/use-conversation-preview.d.ts +3 -1
- package/hooks/use-conversation-preview.d.ts.map +1 -1
- package/hooks/use-conversation-preview.js +8 -4
- package/hooks/use-conversation-preview.js.map +1 -1
- package/hooks/use-conversation-seen.d.ts +1 -1
- package/hooks/use-conversation-timeline-items.d.ts +1 -1
- package/hooks/use-conversation-timeline-items.js +2 -3
- package/hooks/use-conversation-timeline-items.js.map +1 -1
- package/hooks/use-conversation-timeline.d.ts +1 -1
- package/hooks/use-conversation.d.ts +1 -1
- package/hooks/use-conversation.js +2 -3
- package/hooks/use-conversation.js.map +1 -1
- package/hooks/use-conversations.d.ts +1 -1
- package/hooks/use-conversations.js +5 -3
- package/hooks/use-conversations.js.map +1 -1
- package/hooks/use-create-conversation.d.ts +3 -3
- package/hooks/use-create-conversation.js +1 -0
- package/hooks/use-create-conversation.js.map +1 -1
- package/hooks/use-file-upload.d.ts +1 -1
- package/hooks/use-file-upload.js +3 -3
- package/hooks/use-file-upload.js.map +1 -1
- package/hooks/use-home-page.js +3 -3
- package/hooks/use-home-page.js.map +1 -1
- package/hooks/use-message-composer.d.ts +10 -3
- package/hooks/use-message-composer.d.ts.map +1 -1
- package/hooks/use-message-composer.js +5 -2
- package/hooks/use-message-composer.js.map +1 -1
- package/hooks/use-realtime-support.d.ts +1 -1
- package/hooks/use-send-message.d.ts +8 -2
- package/hooks/use-send-message.d.ts.map +1 -1
- package/hooks/use-send-message.js +5 -3
- package/hooks/use-send-message.js.map +1 -1
- package/hooks/use-visitor.js +2 -2
- package/hooks/use-visitor.js.map +1 -1
- package/identify-visitor.d.ts.map +1 -1
- package/identify-visitor.js +15 -1
- package/identify-visitor.js.map +1 -1
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/package.json +6 -3
- package/{conversation.d.ts → packages/types/src/api/conversation.d.ts} +374 -64
- package/packages/types/src/api/conversation.d.ts.map +1 -0
- package/packages/types/src/api/timeline-item.d.ts +460 -0
- package/packages/types/src/api/timeline-item.d.ts.map +1 -0
- package/{realtime-events.d.ts → packages/types/src/realtime-events.d.ts} +449 -47
- package/packages/types/src/realtime-events.d.ts.map +1 -0
- package/{schemas3.d.ts → packages/types/src/schemas.d.ts} +97 -19
- package/packages/types/src/schemas.d.ts.map +1 -0
- package/primitives/avatar/avatar.js +1 -1
- package/primitives/avatar/avatar.js.map +1 -1
- package/primitives/avatar/fallback.js +1 -1
- package/primitives/avatar/fallback.js.map +1 -1
- package/primitives/avatar/image.js +1 -1
- package/primitives/avatar/image.js.map +1 -1
- package/primitives/button.js +1 -1
- package/primitives/button.js.map +1 -1
- package/primitives/conversation-timeline.d.ts +1 -1
- package/primitives/conversation-timeline.js +4 -4
- package/primitives/conversation-timeline.js.map +1 -1
- package/primitives/day-separator.js +3 -3
- package/primitives/day-separator.js.map +1 -1
- package/primitives/multimodal-input.d.ts +2 -2
- package/primitives/multimodal-input.d.ts.map +1 -1
- package/primitives/multimodal-input.js +2 -2
- package/primitives/multimodal-input.js.map +1 -1
- package/primitives/timeline-item-attachments.d.ts +1 -1
- package/primitives/timeline-item-attachments.js +6 -7
- package/primitives/timeline-item-attachments.js.map +1 -1
- package/primitives/timeline-item-group.d.ts +1 -1
- package/primitives/timeline-item-group.js +7 -7
- package/primitives/timeline-item-group.js.map +1 -1
- package/primitives/timeline-item.d.ts +1 -1
- package/primitives/timeline-item.d.ts.map +1 -1
- package/primitives/timeline-item.js +54 -14
- package/primitives/timeline-item.js.map +1 -1
- package/primitives/trigger.js +1 -1
- package/primitives/trigger.js.map +1 -1
- package/primitives/window.js +1 -1
- package/primitives/window.js.map +1 -1
- package/provider.d.ts +4 -2
- package/provider.d.ts.map +1 -1
- package/provider.js +56 -8
- package/provider.js.map +1 -1
- package/realtime/event-filter.d.ts +4 -1
- package/realtime/event-filter.d.ts.map +1 -1
- package/realtime/event-filter.js +14 -0
- package/realtime/event-filter.js.map +1 -1
- package/realtime/provider.d.ts +1 -1
- package/realtime/provider.d.ts.map +1 -1
- package/realtime/provider.js +1 -2
- package/realtime/provider.js.map +1 -1
- package/realtime/seen-store.d.ts +2 -2
- package/realtime/support-provider.js +3 -2
- package/realtime/support-provider.js.map +1 -1
- package/realtime/typing-store.d.ts +1 -1
- package/realtime/use-realtime.d.ts +1 -1
- package/support/components/avatar-stack.d.ts.map +1 -1
- package/support/components/avatar-stack.js +32 -12
- package/support/components/avatar-stack.js.map +1 -1
- package/support/components/avatar.d.ts +34 -3
- package/support/components/avatar.d.ts.map +1 -1
- package/support/components/avatar.js +61 -8
- package/support/components/avatar.js.map +1 -1
- package/support/components/button.d.ts +4 -2
- package/support/components/button.d.ts.map +1 -1
- package/support/components/button.js +3 -3
- package/support/components/button.js.map +1 -1
- package/support/components/configuration-error.d.ts +16 -0
- package/support/components/configuration-error.d.ts.map +1 -0
- package/support/components/configuration-error.js +162 -0
- package/support/components/configuration-error.js.map +1 -0
- package/support/components/content.js +1 -2
- package/support/components/content.js.map +1 -1
- package/support/components/conversation-button-link.js +18 -23
- package/support/components/conversation-button-link.js.map +1 -1
- package/support/components/conversation-event.d.ts.map +1 -1
- package/support/components/conversation-event.js +7 -5
- package/support/components/conversation-event.js.map +1 -1
- package/support/components/conversation-resolved-feedback.d.ts +21 -0
- package/support/components/conversation-resolved-feedback.d.ts.map +1 -0
- package/support/components/conversation-resolved-feedback.js +59 -0
- package/support/components/conversation-resolved-feedback.js.map +1 -0
- package/support/components/conversation-timeline-utils.d.ts +5 -0
- package/support/components/conversation-timeline-utils.d.ts.map +1 -0
- package/support/components/conversation-timeline-utils.js +10 -0
- package/support/components/conversation-timeline-utils.js.map +1 -0
- package/support/components/conversation-timeline.d.ts +1 -1
- package/support/components/conversation-timeline.d.ts.map +1 -1
- package/support/components/conversation-timeline.js +5 -4
- package/support/components/conversation-timeline.js.map +1 -1
- package/support/components/header.js +1 -1
- package/support/components/icons.d.ts +1 -1
- package/support/components/icons.d.ts.map +1 -1
- package/support/components/icons.js +6 -2
- package/support/components/icons.js.map +1 -1
- package/support/components/image-lightbox.d.ts +1 -1
- package/support/components/image-lightbox.js +1 -2
- package/support/components/image-lightbox.js.map +1 -1
- package/support/components/index.d.ts +2 -1
- package/support/components/index.js +3 -2
- package/support/components/multimodal-input.js +0 -1
- package/support/components/multimodal-input.js.map +1 -1
- package/support/components/navigation-tab.js +1 -1
- package/support/components/online-indicator.d.ts +50 -0
- package/support/components/online-indicator.d.ts.map +1 -0
- package/support/components/online-indicator.js +65 -0
- package/support/components/online-indicator.js.map +1 -0
- package/support/components/root.js +0 -1
- package/support/components/root.js.map +1 -1
- package/support/components/timeline-identification-tool.js +4 -4
- package/support/components/timeline-identification-tool.js.map +1 -1
- package/support/components/timeline-message-group.d.ts +1 -1
- package/support/components/timeline-message-group.d.ts.map +1 -1
- package/support/components/timeline-message-group.js +6 -4
- package/support/components/timeline-message-group.js.map +1 -1
- package/support/components/timeline-message-item.d.ts +1 -1
- package/support/components/timeline-message-item.js +4 -4
- package/support/components/timeline-message-item.js.map +1 -1
- package/support/components/trigger.js +1 -2
- package/support/components/trigger.js.map +1 -1
- package/support/components/typing-indicator.js +1 -1
- package/support/components/typing-indicator.js.map +1 -1
- package/support/context/controlled-state.js +0 -1
- package/support/context/controlled-state.js.map +1 -1
- package/support/context/events.d.ts +1 -1
- package/support/context/events.js +0 -1
- package/support/context/events.js.map +1 -1
- package/support/context/handle.js +0 -1
- package/support/context/handle.js.map +1 -1
- package/support/context/identification.d.ts +33 -0
- package/support/context/identification.d.ts.map +1 -0
- package/support/context/identification.js +34 -0
- package/support/context/identification.js.map +1 -0
- package/support/context/positioning.js +0 -1
- package/support/context/positioning.js.map +1 -1
- package/support/context/slots.js +0 -1
- package/support/context/slots.js.map +1 -1
- package/support/context/websocket.d.ts +1 -1
- package/support/context/websocket.js +0 -1
- package/support/context/websocket.js.map +1 -1
- package/support/index.d.ts.map +1 -1
- package/support/index.js +51 -18
- package/support/index.js.map +1 -1
- package/support/pages/conversation-history.js +2 -1
- package/support/pages/conversation-history.js.map +1 -1
- package/support/pages/conversation.d.ts +1 -1
- package/support/pages/conversation.d.ts.map +1 -1
- package/support/pages/conversation.js +34 -8
- package/support/pages/conversation.js.map +1 -1
- package/support/pages/home.js +5 -3
- package/support/pages/home.js.map +1 -1
- package/support/router.d.ts.map +1 -1
- package/support/router.js +4 -0
- package/support/router.js.map +1 -1
- package/support/store/support-store.js +0 -1
- package/support/store/support-store.js.map +1 -1
- package/support/{support-C7Xaw-N6.css → support-DmViRaga.css} +2 -2
- package/support/{support-C7Xaw-N6.css.map → support-DmViRaga.css.map} +1 -1
- package/support/text/index.js +1 -1
- package/support/text/index.js.map +1 -1
- package/support/text/locales/en.js +10 -1
- package/support/text/locales/en.js.map +1 -1
- package/support/text/locales/es.js +10 -1
- package/support/text/locales/es.js.map +1 -1
- package/support/text/locales/fr.js +10 -1
- package/support/text/locales/fr.js.map +1 -1
- package/support/text/locales/keys.d.ts +11 -0
- package/support/text/locales/keys.d.ts.map +1 -1
- package/support/text/locales/keys.js +3 -0
- package/support/text/locales/keys.js.map +1 -1
- package/support/utils/index.d.ts +1 -1
- package/support-config.js +0 -1
- package/support-config.js.map +1 -1
- package/support.css +1 -1
- package/tailwind.css +1 -1
- package/utils/conversation.d.ts.map +1 -1
- package/utils/conversation.js +1 -3
- package/utils/conversation.js.map +1 -1
- package/utils/use-render-element.js +2 -2
- package/utils/use-render-element.js.map +1 -1
- package/api.d.ts +0 -71
- package/api.d.ts.map +0 -1
- package/checks.d.ts +0 -189
- package/checks.d.ts.map +0 -1
- package/clsx.d.ts +0 -7
- package/clsx.d.ts.map +0 -1
- package/coerce.d.ts +0 -9
- package/coerce.d.ts.map +0 -1
- package/conversation.d.ts.map +0 -1
- package/core.d.ts +0 -35
- package/core.d.ts.map +0 -1
- package/errors.d.ts +0 -130
- package/errors.d.ts.map +0 -1
- package/errors2.d.ts +0 -24
- package/errors2.d.ts.map +0 -1
- package/index2.d.ts +0 -4
- package/index3.d.ts +0 -1
- package/json-schema.d.ts +0 -70
- package/json-schema.d.ts.map +0 -1
- package/metadata.d.ts +0 -1
- package/openapi-generator.d.ts +0 -1
- package/openapi-generator2.d.ts +0 -1
- package/openapi-generator3.d.ts +0 -1
- package/openapi30.d.ts +0 -125
- package/openapi30.d.ts.map +0 -1
- package/openapi31.d.ts +0 -131
- package/openapi31.d.ts.map +0 -1
- package/parse.d.ts +0 -17
- package/parse.d.ts.map +0 -1
- package/realtime-events.d.ts.map +0 -1
- package/registries.d.ts +0 -32
- package/registries.d.ts.map +0 -1
- package/schemas.d.ts +0 -971
- package/schemas.d.ts.map +0 -1
- package/schemas2.d.ts +0 -345
- package/schemas2.d.ts.map +0 -1
- package/schemas3.d.ts.map +0 -1
- package/specification-extension.d.ts +0 -9
- package/specification-extension.d.ts.map +0 -1
- package/standard-schema.d.ts +0 -121
- package/standard-schema.d.ts.map +0 -1
- package/timeline-item.d.ts +0 -227
- package/timeline-item.d.ts.map +0 -1
- package/to-json-schema.d.ts +0 -96
- package/to-json-schema.d.ts.map +0 -1
- package/util.d.ts +0 -45
- package/util.d.ts.map +0 -1
- package/versions.d.ts +0 -9
- package/versions.d.ts.map +0 -1
- package/zod-extensions.d.ts +0 -39
- package/zod-extensions.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-item-attachments.js","names":["React"],"sources":["../../src/primitives/timeline-item-attachments.tsx"],"sourcesContent":["\"use client\";\n\nimport { formatFileSize } from \"@cossistant/core\";\nimport type {\n\tTimelineItemParts,\n\tTimelinePartFile,\n\tTimelinePartImage,\n} from \"@cossistant/types/api/timeline-item\";\nimport * as React from \"react\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\n/**\n * Extract image parts from timeline item parts array.\n */\nexport function extractImageParts(\n\tparts: TimelineItemParts\n): TimelinePartImage[] {\n\treturn parts.filter(\n\t\t(part): part is TimelinePartImage => part.type === \"image\"\n\t);\n}\n\n/**\n * Extract file parts from timeline item parts array.\n */\nexport function extractFileParts(parts: TimelineItemParts): TimelinePartFile[] {\n\treturn parts.filter((part): part is TimelinePartFile => part.type === \"file\");\n}\n\n/**\n * Check if timeline item has any attachments (images or files).\n */\nexport function hasAttachments(parts: TimelineItemParts): boolean {\n\treturn parts.some((part) => part.type === \"image\" || part.type === \"file\");\n}\n\n// ============================================================================\n// TimelineItemImages\n// ============================================================================\n\nexport type TimelineItemImagesProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((\n\t\t\t\timages: TimelinePartImage[],\n\t\t\t\tonImageClick?: (index: number) => void\n\t\t ) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\timages: TimelinePartImage[];\n\t/**\n\t * Callback when an image is clicked (for lightbox).\n\t */\n\tonImageClick?: (index: number) => void;\n};\n\n/**\n * Renders a grid of image thumbnails from timeline item parts.\n * Supports custom rendering via children render prop.\n */\nexport const TimelineItemImages = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemImagesProps>(\n\t\t(\n\t\t\t{ children, className, asChild = false, images, onImageClick, ...props },\n\t\t\tref\n\t\t) => {\n\t\t\tif (images.length === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children(images, onImageClick)\n\t\t\t\t\t: children || (\n\t\t\t\t\t\t\t<div className=\"flex flex-wrap gap-2\">\n\t\t\t\t\t\t\t\t{images.map((image, index) => (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tclassName=\"group relative overflow-hidden rounded-lg focus:outline-none focus:ring-2 focus:ring-offset-2\"\n\t\t\t\t\t\t\t\t\t\tkey={image.url}\n\t\t\t\t\t\t\t\t\t\tonClick={() => onImageClick?.(index)}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/performance/noImgElement: React package, not Next.js specific */}\n\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/nursery/useImageSize: Dynamic image dimensions not known at render time */}\n\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\talt={image.fileName || `Image ${index + 1}`}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"max-h-[200px] max-w-[300px] cursor-pointer rounded-lg object-cover transition-transform group-hover:scale-105\"\n\t\t\t\t\t\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t\t\t\t\t\t\tsrc={image.url}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\n\t\t\t// biome-ignore lint/correctness/useHookAtTopLevel: useRenderElement is a utility function, not a hook\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemImages\";\n\treturn Component;\n})();\n\n// ============================================================================\n// TimelineItemFiles\n// ============================================================================\n\nexport type TimelineItemFilesProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode | ((files: TimelinePartFile[]) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\tfiles: TimelinePartFile[];\n};\n\n/**\n * Get file icon based on MIME type.\n */\nfunction getFileIcon(mediaType: string): string {\n\tif (mediaType === \"application/pdf\") {\n\t\treturn \"📄\";\n\t}\n\tif (mediaType === \"application/zip\") {\n\t\treturn \"🗜️\";\n\t}\n\tif (mediaType.startsWith(\"text/\")) {\n\t\treturn \"📝\";\n\t}\n\treturn \"📎\";\n}\n\n/**\n * Renders a list of file attachments from timeline item parts.\n * Supports custom rendering via children render prop.\n */\nexport const TimelineItemFiles = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemFilesProps>(\n\t\t({ children, className, asChild = false, files, ...props }, ref) => {\n\t\t\tif (files.length === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children(files)\n\t\t\t\t\t: children || (\n\t\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t\t{files.map((file) => (\n\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-2 rounded-lg bg-co-muted/50 px-3 py-2 text-sm transition-colors hover:bg-co-muted\"\n\t\t\t\t\t\t\t\t\t\tdownload={file.fileName}\n\t\t\t\t\t\t\t\t\t\thref={file.url}\n\t\t\t\t\t\t\t\t\t\tkey={file.url}\n\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-lg\">\n\t\t\t\t\t\t\t\t\t\t\t{getFileIcon(file.mediaType)}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span className=\"flex-1 truncate font-medium\">\n\t\t\t\t\t\t\t\t\t\t\t{file.fileName || \"Download file\"}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t{file.size && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground text-xs\">\n\t\t\t\t\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\n\t\t\t// biome-ignore lint/correctness/useHookAtTopLevel: useRenderElement is a utility function, not a hook\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemFiles\";\n\treturn Component;\n})();\n\n// ============================================================================\n// TimelineItemAttachments (convenience wrapper)\n// ============================================================================\n\nexport type TimelineItemAttachmentsProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode;\n\tasChild?: boolean;\n\tclassName?: string;\n\tparts: TimelineItemParts;\n\t/**\n\t * Callback when an image is clicked (for lightbox).\n\t */\n\tonImageClick?: (index: number) => void;\n\t/**\n\t * Custom className for the images container.\n\t */\n\timagesClassName?: string;\n\t/**\n\t * Custom className for the files container.\n\t */\n\tfilesClassName?: string;\n};\n\n/**\n * Convenience component that renders both images and files from timeline parts.\n * Extracts the appropriate parts and renders them in a single container.\n */\nexport const TimelineItemAttachments = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemAttachmentsProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\tparts,\n\t\t\t\tonImageClick,\n\t\t\t\timagesClassName,\n\t\t\t\tfilesClassName,\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst images = extractImageParts(parts);\n\t\t\tconst files = extractFileParts(parts);\n\n\t\t\tif (images.length === 0 && files.length === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst content = children || (\n\t\t\t\t<>\n\t\t\t\t\t{images.length > 0 && (\n\t\t\t\t\t\t<TimelineItemImages\n\t\t\t\t\t\t\tclassName={imagesClassName}\n\t\t\t\t\t\t\timages={images}\n\t\t\t\t\t\t\tonImageClick={onImageClick}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t\t{files.length > 0 && (\n\t\t\t\t\t\t<TimelineItemFiles className={filesClassName} files={files} />\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t);\n\n\t\t\t// biome-ignore lint/correctness/useHookAtTopLevel: useRenderElement is a utility function, not a hook\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemAttachments\";\n\treturn Component;\n})();\n"],"mappings":";;;;;;;;;;;;AAcA,SAAgB,kBACf,OACsB;AACtB,QAAO,MAAM,QACX,SAAoC,KAAK,SAAS,QACnD;;;;;AAMF,SAAgB,iBAAiB,OAA8C;AAC9E,QAAO,MAAM,QAAQ,SAAmC,KAAK,SAAS,OAAO;;;;;AAM9E,SAAgB,eAAe,OAAmC;AACjE,QAAO,MAAM,MAAM,SAAS,KAAK,SAAS,WAAW,KAAK,SAAS,OAAO;;;;;;AA8B3E,MAAa,4BAA4B;CACxC,MAAM,YAAYA,QAAM,YAEtB,EAAE,UAAU,WAAW,UAAU,OAAO,QAAQ,aAAc,GAAG,SACjE,QACI;AACJ,MAAI,OAAO,WAAW,EACrB,QAAO;EAGR,MAAM,UACL,OAAO,aAAa,aACjB,SAAS,QAAQ,aAAa,GAC9B,YACA,oBAAC;GAAI,WAAU;aACb,OAAO,KAAK,OAAO,UACnB,oBAAC;IACA,WAAU;IAEV,eAAe,eAAe,MAAM;IACpC,MAAK;cAIL,oBAAC;KACA,KAAK,MAAM,YAAY,SAAS,QAAQ;KACxC,WAAU;KACV,SAAQ;KACR,KAAK,MAAM;MACV;MAXG,MAAM,IAYH,CACR;IACG;AAIV,SAAO,iBACN,OACA;GACC;GACA;GACA,EACD;GACC;GACA,OAAO;IACN,GAAG;IACH,UAAU;IACV;GACD,CACD;GAEF;AAED,WAAU,cAAc;AACxB,QAAO;IACJ;;;;AAmBJ,SAAS,YAAY,WAA2B;AAC/C,KAAI,cAAc,kBACjB,QAAO;AAER,KAAI,cAAc,kBACjB,QAAO;AAER,KAAI,UAAU,WAAW,QAAQ,CAChC,QAAO;AAER,QAAO;;;;;;AAOR,MAAa,2BAA2B;CACvC,MAAM,YAAYA,QAAM,YACtB,EAAE,UAAU,WAAW,UAAU,OAAO,MAAO,GAAG,SAAS,QAAQ;AACnE,MAAI,MAAM,WAAW,EACpB,QAAO;EAGR,MAAM,UACL,OAAO,aAAa,aACjB,SAAS,MAAM,GACf,YACA,oBAAC;GAAI,WAAU;aACb,MAAM,KAAK,SACX,qBAAC;IACA,WAAU;IACV,UAAU,KAAK;IACf,MAAM,KAAK;IAEX,KAAI;IACJ,QAAO;;KAEP,oBAAC;MAAK,WAAU;gBACd,YAAY,KAAK,UAAU;OACtB;KACP,oBAAC;MAAK,WAAU;gBACd,KAAK,YAAY;OACZ;KACN,KAAK,QACL,oBAAC;MAAK,WAAU;gBACd,eAAe,KAAK,KAAK;OACpB;;MAbH,KAAK,IAeP,CACH;IACG;AAIV,SAAO,iBACN,OACA;GACC;GACA;GACA,EACD;GACC;GACA,OAAO;IACN,GAAG;IACH,UAAU;IACV;GACD,CACD;GAEF;AAED,WAAU,cAAc;AACxB,QAAO;IACJ;;;;;AAgCJ,MAAa,iCAAiC;CAC7C,MAAM,YAAYA,QAAM,YAKtB,EACC,UACA,WACA,UAAU,OACV,OACA,cACA,iBACA,eACA,GAAG,SAEJ,QACI;EACJ,MAAM,SAAS,kBAAkB,MAAM;EACvC,MAAM,QAAQ,iBAAiB,MAAM;AAErC,MAAI,OAAO,WAAW,KAAK,MAAM,WAAW,EAC3C,QAAO;EAGR,MAAM,UAAU,YACf,4CACE,OAAO,SAAS,KAChB,oBAAC;GACA,WAAW;GACH;GACM;IACb,EAEF,MAAM,SAAS,KACf,oBAAC;GAAkB,WAAW;GAAuB;IAAS,IAE7D;AAIJ,SAAO,iBACN,OACA;GACC;GACA;GACA,EACD;GACC;GACA,OAAO;IACN,GAAG;IACH,UAAU;IACV;GACD,CACD;GAEF;AAED,WAAU,cAAc;AACxB,QAAO;IACJ"}
|
|
1
|
+
{"version":3,"file":"timeline-item-attachments.js","names":["React"],"sources":["../../src/primitives/timeline-item-attachments.tsx"],"sourcesContent":["\"use client\";\n\nimport { formatFileSize } from \"@cossistant/core\";\nimport type {\n\tTimelineItemParts,\n\tTimelinePartFile,\n\tTimelinePartImage,\n} from \"@cossistant/types/api/timeline-item\";\nimport * as React from \"react\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\n/**\n * Extract image parts from timeline item parts array.\n */\nexport function extractImageParts(\n\tparts: TimelineItemParts\n): TimelinePartImage[] {\n\treturn parts.filter(\n\t\t(part): part is TimelinePartImage => part.type === \"image\"\n\t);\n}\n\n/**\n * Extract file parts from timeline item parts array.\n */\nexport function extractFileParts(parts: TimelineItemParts): TimelinePartFile[] {\n\treturn parts.filter((part): part is TimelinePartFile => part.type === \"file\");\n}\n\n/**\n * Check if timeline item has any attachments (images or files).\n */\nexport function hasAttachments(parts: TimelineItemParts): boolean {\n\treturn parts.some((part) => part.type === \"image\" || part.type === \"file\");\n}\n\n// ============================================================================\n// TimelineItemImages\n// ============================================================================\n\nexport type TimelineItemImagesProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((\n\t\t\t\timages: TimelinePartImage[],\n\t\t\t\tonImageClick?: (index: number) => void\n\t\t ) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\timages: TimelinePartImage[];\n\t/**\n\t * Callback when an image is clicked (for lightbox).\n\t */\n\tonImageClick?: (index: number) => void;\n};\n\n/**\n * Renders a grid of image thumbnails from timeline item parts.\n * Supports custom rendering via children render prop.\n */\nexport const TimelineItemImages = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemImagesProps>(\n\t\t(\n\t\t\t{ children, className, asChild = false, images, onImageClick, ...props },\n\t\t\tref\n\t\t) => {\n\t\t\tif (images.length === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children(images, onImageClick)\n\t\t\t\t\t: children || (\n\t\t\t\t\t\t\t<div className=\"flex flex-wrap gap-2\">\n\t\t\t\t\t\t\t\t{images.map((image, index) => (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tclassName=\"group relative overflow-hidden rounded-lg focus:outline-none focus:ring-2 focus:ring-offset-2\"\n\t\t\t\t\t\t\t\t\t\tkey={image.url}\n\t\t\t\t\t\t\t\t\t\tonClick={() => onImageClick?.(index)}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/performance/noImgElement: React package, not Next.js specific */}\n\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/nursery/useImageSize: Dynamic image dimensions not known at render time */}\n\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\talt={image.filename || `Image ${index + 1}`}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"max-h-[200px] max-w-[300px] cursor-pointer rounded-lg object-cover transition-transform group-hover:scale-105\"\n\t\t\t\t\t\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t\t\t\t\t\t\tsrc={image.url}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\n\t\t\t// biome-ignore lint/correctness/useHookAtTopLevel: useRenderElement is a utility function, not a hook\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemImages\";\n\treturn Component;\n})();\n\n// ============================================================================\n// TimelineItemFiles\n// ============================================================================\n\nexport type TimelineItemFilesProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode | ((files: TimelinePartFile[]) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\tfiles: TimelinePartFile[];\n};\n\n/**\n * Get file icon based on MIME type.\n */\nfunction getFileIcon(mediaType: string): string {\n\tif (mediaType === \"application/pdf\") {\n\t\treturn \"📄\";\n\t}\n\tif (mediaType === \"application/zip\") {\n\t\treturn \"🗜️\";\n\t}\n\tif (mediaType.startsWith(\"text/\")) {\n\t\treturn \"📝\";\n\t}\n\treturn \"📎\";\n}\n\n/**\n * Renders a list of file attachments from timeline item parts.\n * Supports custom rendering via children render prop.\n */\nexport const TimelineItemFiles = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemFilesProps>(\n\t\t({ children, className, asChild = false, files, ...props }, ref) => {\n\t\t\tif (files.length === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children(files)\n\t\t\t\t\t: children || (\n\t\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t\t{files.map((file) => (\n\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-2 rounded-lg bg-co-muted/50 px-3 py-2 text-sm transition-colors hover:bg-co-muted\"\n\t\t\t\t\t\t\t\t\t\tdownload={file.filename}\n\t\t\t\t\t\t\t\t\t\thref={file.url}\n\t\t\t\t\t\t\t\t\t\tkey={file.url}\n\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-lg\">\n\t\t\t\t\t\t\t\t\t\t\t{getFileIcon(file.mediaType)}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span className=\"flex-1 truncate font-medium\">\n\t\t\t\t\t\t\t\t\t\t\t{file.filename || \"Download file\"}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t{file.size && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground text-xs\">\n\t\t\t\t\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\n\t\t\t// biome-ignore lint/correctness/useHookAtTopLevel: useRenderElement is a utility function, not a hook\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemFiles\";\n\treturn Component;\n})();\n\n// ============================================================================\n// TimelineItemAttachments (convenience wrapper)\n// ============================================================================\n\nexport type TimelineItemAttachmentsProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode;\n\tasChild?: boolean;\n\tclassName?: string;\n\tparts: TimelineItemParts;\n\t/**\n\t * Callback when an image is clicked (for lightbox).\n\t */\n\tonImageClick?: (index: number) => void;\n\t/**\n\t * Custom className for the images container.\n\t */\n\timagesClassName?: string;\n\t/**\n\t * Custom className for the files container.\n\t */\n\tfilesClassName?: string;\n};\n\n/**\n * Convenience component that renders both images and files from timeline parts.\n * Extracts the appropriate parts and renders them in a single container.\n */\nexport const TimelineItemAttachments = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemAttachmentsProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\tparts,\n\t\t\t\tonImageClick,\n\t\t\t\timagesClassName,\n\t\t\t\tfilesClassName,\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst images = extractImageParts(parts);\n\t\t\tconst files = extractFileParts(parts);\n\n\t\t\tif (images.length === 0 && files.length === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst content = children || (\n\t\t\t\t<>\n\t\t\t\t\t{images.length > 0 && (\n\t\t\t\t\t\t<TimelineItemImages\n\t\t\t\t\t\t\tclassName={imagesClassName}\n\t\t\t\t\t\t\timages={images}\n\t\t\t\t\t\t\tonImageClick={onImageClick}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t\t{files.length > 0 && (\n\t\t\t\t\t\t<TimelineItemFiles className={filesClassName} files={files} />\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t);\n\n\t\t\t// biome-ignore lint/correctness/useHookAtTopLevel: useRenderElement is a utility function, not a hook\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemAttachments\";\n\treturn Component;\n})();\n"],"mappings":";;;;;;;;;;;AAcA,SAAgB,kBACf,OACsB;AACtB,QAAO,MAAM,QACX,SAAoC,KAAK,SAAS,QACnD;;;;;AAMF,SAAgB,iBAAiB,OAA8C;AAC9E,QAAO,MAAM,QAAQ,SAAmC,KAAK,SAAS,OAAO;;;;;AAM9E,SAAgB,eAAe,OAAmC;AACjE,QAAO,MAAM,MAAM,SAAS,KAAK,SAAS,WAAW,KAAK,SAAS,OAAO;;;;;;AA8B3E,MAAa,4BAA4B;CACxC,MAAM,YAAYA,QAAM,YAEtB,EAAE,UAAU,WAAW,UAAU,OAAO,QAAQ,cAAc,GAAG,SACjE,QACI;AACJ,MAAI,OAAO,WAAW,EACrB,QAAO;EAGR,MAAM,UACL,OAAO,aAAa,aACjB,SAAS,QAAQ,aAAa,GAC9B,YACA,oBAAC;GAAI,WAAU;aACb,OAAO,KAAK,OAAO,UACnB,oBAAC;IACA,WAAU;IAEV,eAAe,eAAe,MAAM;IACpC,MAAK;cAIL,oBAAC;KACA,KAAK,MAAM,YAAY,SAAS,QAAQ;KACxC,WAAU;KACV,SAAQ;KACR,KAAK,MAAM;MACV;MAXG,MAAM,IAYH,CACR;IACG;AAIV,SAAO,iBACN,OACA;GACC;GACA;GACA,EACD;GACC;GACA,OAAO;IACN,GAAG;IACH,UAAU;IACV;GACD,CACD;GAEF;AAED,WAAU,cAAc;AACxB,QAAO;IACJ;;;;AAmBJ,SAAS,YAAY,WAA2B;AAC/C,KAAI,cAAc,kBACjB,QAAO;AAER,KAAI,cAAc,kBACjB,QAAO;AAER,KAAI,UAAU,WAAW,QAAQ,CAChC,QAAO;AAER,QAAO;;;;;;AAOR,MAAa,2BAA2B;CACvC,MAAM,YAAYA,QAAM,YACtB,EAAE,UAAU,WAAW,UAAU,OAAO,OAAO,GAAG,SAAS,QAAQ;AACnE,MAAI,MAAM,WAAW,EACpB,QAAO;EAGR,MAAM,UACL,OAAO,aAAa,aACjB,SAAS,MAAM,GACf,YACA,oBAAC;GAAI,WAAU;aACb,MAAM,KAAK,SACX,qBAAC;IACA,WAAU;IACV,UAAU,KAAK;IACf,MAAM,KAAK;IAEX,KAAI;IACJ,QAAO;;KAEP,oBAAC;MAAK,WAAU;gBACd,YAAY,KAAK,UAAU;OACtB;KACP,oBAAC;MAAK,WAAU;gBACd,KAAK,YAAY;OACZ;KACN,KAAK,QACL,oBAAC;MAAK,WAAU;gBACd,eAAe,KAAK,KAAK;OACpB;;MAbH,KAAK,IAeP,CACH;IACG;AAIV,SAAO,iBACN,OACA;GACC;GACA;GACA,EACD;GACC;GACA,OAAO;IACN,GAAG;IACH,UAAU;IACV;GACD,CACD;GAEF;AAED,WAAU,cAAc;AACxB,QAAO;IACJ;;;;;AAgCJ,MAAa,iCAAiC;CAC7C,MAAM,YAAYA,QAAM,YAKtB,EACC,UACA,WACA,UAAU,OACV,OACA,cACA,iBACA,gBACA,GAAG,SAEJ,QACI;EACJ,MAAM,SAAS,kBAAkB,MAAM;EACvC,MAAM,QAAQ,iBAAiB,MAAM;AAErC,MAAI,OAAO,WAAW,KAAK,MAAM,WAAW,EAC3C,QAAO;EAGR,MAAM,UAAU,YACf,4CACE,OAAO,SAAS,KAChB,oBAAC;GACA,WAAW;GACH;GACM;IACb,EAEF,MAAM,SAAS,KACf,oBAAC;GAAkB,WAAW;GAAuB;IAAS,IAE7D;AAIJ,SAAO,iBACN,OACA;GACC;GACA;GACA,EACD;GACC;GACA,OAAO;IACN,GAAG;IACH,UAAU;IACV;GACD,CACD;GAEF;AAED,WAAU,cAAc;AACxB,QAAO;IACJ"}
|
|
@@ -9,8 +9,8 @@ import * as React$1 from "react";
|
|
|
9
9
|
* slotted children. Typically used for MESSAGE-type items; EVENT items are usually rendered separately.
|
|
10
10
|
*/
|
|
11
11
|
const TimelineItemGroup = (() => {
|
|
12
|
-
const Component = React$1.forwardRef(({ children, className, asChild = false, items = [], viewerId, seenByIds = [], lastReadItemIds
|
|
13
|
-
const { viewerType
|
|
12
|
+
const Component = React$1.forwardRef(({ children, className, asChild = false, items = [], viewerId, seenByIds = [], lastReadItemIds, ...restProps }, ref) => {
|
|
13
|
+
const { viewerType, ...elementProps } = restProps;
|
|
14
14
|
const firstItem = items[0];
|
|
15
15
|
const lastItem = items.at(-1);
|
|
16
16
|
let senderId = "";
|
|
@@ -72,7 +72,7 @@ const TimelineItemGroup = (() => {
|
|
|
72
72
|
* badge or any other sender metadata supplied by the consumer UI.
|
|
73
73
|
*/
|
|
74
74
|
const TimelineItemGroupAvatar = (() => {
|
|
75
|
-
const Component = React$1.forwardRef(({ children, className, asChild = false
|
|
75
|
+
const Component = React$1.forwardRef(({ children, className, asChild = false, ...props }, ref) => useRenderElement("div", {
|
|
76
76
|
className,
|
|
77
77
|
asChild
|
|
78
78
|
}, {
|
|
@@ -91,7 +91,7 @@ const TimelineItemGroupAvatar = (() => {
|
|
|
91
91
|
* metadata supplied by `TimelineItemGroup`.
|
|
92
92
|
*/
|
|
93
93
|
const TimelineItemGroupHeader = (() => {
|
|
94
|
-
const Component = React$1.forwardRef(({ children, className, asChild = false, name, senderId, senderType
|
|
94
|
+
const Component = React$1.forwardRef(({ children, className, asChild = false, name, senderId, senderType, ...props }, ref) => {
|
|
95
95
|
const content = typeof children === "function" ? children({
|
|
96
96
|
name,
|
|
97
97
|
senderId,
|
|
@@ -117,7 +117,7 @@ const TimelineItemGroupHeader = (() => {
|
|
|
117
117
|
* parent group.
|
|
118
118
|
*/
|
|
119
119
|
const TimelineItemGroupContent = (() => {
|
|
120
|
-
const Component = React$1.forwardRef(({ children, className, asChild = false
|
|
120
|
+
const Component = React$1.forwardRef(({ children, className, asChild = false, ...props }, ref) => useRenderElement("div", {
|
|
121
121
|
className,
|
|
122
122
|
asChild
|
|
123
123
|
}, {
|
|
@@ -136,7 +136,7 @@ const TimelineItemGroupContent = (() => {
|
|
|
136
136
|
* displays.
|
|
137
137
|
*/
|
|
138
138
|
const TimelineItemGroupSeenIndicator = (() => {
|
|
139
|
-
const Component = React$1.forwardRef(({ children, className, asChild = false, seenByIds = []
|
|
139
|
+
const Component = React$1.forwardRef(({ children, className, asChild = false, seenByIds = [], ...props }, ref) => {
|
|
140
140
|
const hasBeenSeen = seenByIds.length > 0;
|
|
141
141
|
const content = typeof children === "function" ? children({
|
|
142
142
|
seenByIds,
|
|
@@ -162,7 +162,7 @@ const TimelineItemGroupSeenIndicator = (() => {
|
|
|
162
162
|
* basic label.
|
|
163
163
|
*/
|
|
164
164
|
const TimelineItemGroupReadIndicator = (() => {
|
|
165
|
-
const Component = React$1.forwardRef(({ children, className, asChild = false, itemId, lastReadItemIds
|
|
165
|
+
const Component = React$1.forwardRef(({ children, className, asChild = false, itemId, lastReadItemIds, ...props }, ref) => {
|
|
166
166
|
const { lastReaderIds, readers } = React$1.useMemo(() => {
|
|
167
167
|
const _lastReaderIds = [];
|
|
168
168
|
const _readers = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-item-group.js","names":["React","senderType: SenderType","renderProps: TimelineItemGroupRenderProps","_lastReaderIds: string[]","_readers: Array<{ userId: string; isLastRead: boolean }>"],"sources":["../../src/primitives/timeline-item-group.tsx"],"sourcesContent":["import type { SenderType } from \"@cossistant/types\";\nimport type { TimelineItem as TimelineItemType } from \"@cossistant/types/api/timeline-item\";\nimport * as React from \"react\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\n/**\n * Shape returned to render-prop children describing the grouped timeline items state\n * and viewer specific flags.\n */\nexport type TimelineItemGroupRenderProps = {\n\t// Sender information\n\tsenderType: SenderType;\n\tsenderId: string;\n\tviewerType?: SenderType;\n\n\t// POV flags - who is viewing\n\tisSentByViewer: boolean; // True if the current viewer sent these items\n\tisReceivedByViewer: boolean; // True if the current viewer received these items\n\n\t// Sender type flags for convenience\n\tisVisitor: boolean;\n\tisAI: boolean;\n\tisTeamMember: boolean;\n\n\t// Item info\n\titemCount: number;\n\tfirstItemId: string | undefined;\n\tlastItemId: string | undefined;\n\n\t// Seen status\n\thasBeenSeenByViewer?: boolean;\n\tseenByIds?: readonly string[]; // IDs of users who have seen the last item in group\n};\n\nexport type TimelineItemGroupProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: TimelineItemGroupRenderProps) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\titems: TimelineItemType[];\n\n\t// POV context - who is viewing these timeline items\n\tviewerId?: string; // ID of the current viewer\n\tviewerType?: SenderType; // Type of the current viewer\n\n\t// Seen data\n\tseenByIds?: readonly string[]; // IDs of users who have seen these timeline items\n\tlastReadItemIds?: Map<string, string>; // Map of userId -> lastItemId they read\n};\n\n/**\n * Groups sequential timeline items from the same sender and exposes render helpers\n * that describe who sent the batch and whether the active viewer has seen it.\n * Consumers can either render their own layout via a render prop or rely on\n * slotted children. Typically used for MESSAGE-type items; EVENT items are usually rendered separately.\n */\nexport const TimelineItemGroup = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemGroupProps>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\titems = [],\n\t\t\t\tviewerId,\n\t\t\t\tseenByIds = [] as readonly string[],\n\t\t\t\tlastReadItemIds,\n\t\t\t\t...restProps\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst { viewerType, ...elementProps } = restProps;\n\n\t\t\t// Determine sender type from first timeline item in group\n\t\t\tconst firstItem = items[0];\n\t\t\tconst lastItem = items.at(-1);\n\n\t\t\t// Determine sender info\n\t\t\tlet senderId = \"\";\n\t\t\tlet senderType: SenderType;\n\n\t\t\tif (firstItem?.visitorId) {\n\t\t\t\tsenderId = firstItem.visitorId;\n\t\t\t\tsenderType = \"visitor\" as SenderType;\n\t\t\t} else if (firstItem?.aiAgentId) {\n\t\t\t\tsenderId = firstItem.aiAgentId;\n\t\t\t\tsenderType = \"ai\" as SenderType;\n\t\t\t} else if (firstItem?.userId) {\n\t\t\t\tsenderId = firstItem.userId;\n\t\t\t\tsenderType = \"team_member\" as SenderType;\n\t\t\t} else {\n\t\t\t\t// Fallback\n\t\t\t\tsenderId = firstItem?.id || \"unknown\";\n\t\t\t\tsenderType = \"team_member\" as SenderType;\n\t\t\t}\n\n\t\t\t// Determine POV\n\t\t\tconst isSentByViewer = viewerId\n\t\t\t\t? senderId === viewerId\n\t\t\t\t: viewerType\n\t\t\t\t\t? senderType === viewerType\n\t\t\t\t\t: false;\n\t\t\tconst isReceivedByViewer = viewerId\n\t\t\t\t? senderId !== viewerId\n\t\t\t\t: viewerType\n\t\t\t\t\t? senderType !== viewerType\n\t\t\t\t\t: true;\n\n\t\t\t// Convenience flags\n\t\t\tconst isVisitor = senderType === \"visitor\";\n\t\t\tconst isAI = senderType === \"ai\";\n\t\t\tconst isTeamMember = senderType === \"team_member\";\n\n\t\t\t// Check if viewer has seen these timeline items\n\t\t\tconst hasBeenSeenByViewer = viewerId\n\t\t\t\t? seenByIds.includes(viewerId)\n\t\t\t\t: undefined;\n\n\t\t\tconst renderProps: TimelineItemGroupRenderProps = {\n\t\t\t\tsenderType,\n\t\t\t\tsenderId,\n\t\t\t\tviewerType,\n\t\t\t\tisSentByViewer,\n\t\t\t\tisReceivedByViewer,\n\t\t\t\tisVisitor,\n\t\t\t\tisAI,\n\t\t\t\tisTeamMember,\n\t\t\t\titemCount: items.length,\n\t\t\t\tfirstItemId: firstItem?.id,\n\t\t\t\tlastItemId: lastItem?.id,\n\t\t\t\thasBeenSeenByViewer,\n\t\t\t\tseenByIds,\n\t\t\t};\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tstate: renderProps,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\trole: \"group\",\n\t\t\t\t\t\t\"aria-label\": `Timeline item group from ${senderType}`,\n\t\t\t\t\t\t...elementProps,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemGroup\";\n\treturn Component;\n})();\n\nexport type TimelineItemGroupAvatarProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode;\n\tasChild?: boolean;\n\tclassName?: string;\n};\n\n/**\n * Optional slot rendered next to a grouped batch to display an avatar, agent\n * badge or any other sender metadata supplied by the consumer UI.\n */\nexport const TimelineItemGroupAvatar = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemGroupAvatarProps\n\t>(({ children, className, asChild = false, ...props }, ref) =>\n\t\tuseRenderElement(\n\t\t\t\"div\",\n\t\t\t{\n\t\t\t\tclassName,\n\t\t\t\tasChild,\n\t\t\t},\n\t\t\t{\n\t\t\t\tref,\n\t\t\t\tprops: {\n\t\t\t\t\t...props,\n\t\t\t\t\tchildren,\n\t\t\t\t},\n\t\t\t}\n\t\t)\n\t);\n\n\tComponent.displayName = \"TimelineItemGroupAvatar\";\n\treturn Component;\n})();\n\nexport type TimelineItemGroupHeaderProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: {\n\t\t\t\tname?: string;\n\t\t\t\tsenderId?: string;\n\t\t\t\tsenderType?: SenderType;\n\t\t }) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\tname?: string;\n\tsenderId?: string;\n\tsenderType?: SenderType;\n};\n\n/**\n * Decorative or semantic wrapper rendered above a timeline item batch. Useful for\n * injecting agent names, timestamps or custom status labels tied to the sender\n * metadata supplied by `TimelineItemGroup`.\n */\nexport const TimelineItemGroupHeader = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemGroupHeaderProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\tname,\n\t\t\t\tsenderId,\n\t\t\t\tsenderType,\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children({ name, senderId, senderType })\n\t\t\t\t\t: children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemGroupHeader\";\n\treturn Component;\n})();\n\nexport type TimelineItemGroupContentProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode;\n\tasChild?: boolean;\n\tclassName?: string;\n};\n\n/**\n * Container for the actual timeline items within a batch. Consumers can\n * override the structure while inheriting layout props passed down from the\n * parent group.\n */\nexport const TimelineItemGroupContent = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemGroupContentProps\n\t>(({ children, className, asChild = false, ...props }, ref) =>\n\t\tuseRenderElement(\n\t\t\t\"div\",\n\t\t\t{\n\t\t\t\tclassName,\n\t\t\t\tasChild,\n\t\t\t},\n\t\t\t{\n\t\t\t\tref,\n\t\t\t\tprops: {\n\t\t\t\t\t...props,\n\t\t\t\t\tchildren,\n\t\t\t\t},\n\t\t\t}\n\t\t)\n\t);\n\n\tComponent.displayName = \"TimelineItemGroupContent\";\n\treturn Component;\n})();\n\nexport type TimelineItemGroupSeenIndicatorProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: {\n\t\t\t\tseenByIds: readonly string[];\n\t\t\t\thasBeenSeen: boolean;\n\t\t }) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\tseenByIds?: readonly string[];\n};\n\n/**\n * Utility slot for showing who has viewed the most recent timeline item in the\n * group. Works with simple text children or a render prop for advanced\n * displays.\n */\nexport const TimelineItemGroupSeenIndicator = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemGroupSeenIndicatorProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\tseenByIds = [] as readonly string[],\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst hasBeenSeen = seenByIds.length > 0;\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children({ seenByIds, hasBeenSeen })\n\t\t\t\t\t: children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemGroupSeenIndicator\";\n\treturn Component;\n})();\n\nexport type TimelineItemGroupReadIndicatorProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: {\n\t\t\t\treaders: Array<{ userId: string; isLastRead: boolean }>;\n\t\t\t\tlastReaderIds: string[];\n\t\t }) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\titemId: string;\n\tlastReadItemIds?: Map<string, string>;\n};\n\n/**\n * Renders read receipts for the tail timeline item in a group. It surfaces the list\n * of readers and callers can decide whether to render avatars, tooltips or a\n * basic label.\n */\nexport const TimelineItemGroupReadIndicator = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemGroupReadIndicatorProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\titemId,\n\t\t\t\tlastReadItemIds,\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\t// Find all users who stopped reading at this timeline item\n\t\t\tconst { lastReaderIds, readers } = React.useMemo(() => {\n\t\t\t\tconst _lastReaderIds: string[] = [];\n\t\t\t\tconst _readers: Array<{ userId: string; isLastRead: boolean }> = [];\n\n\t\t\t\tif (lastReadItemIds) {\n\t\t\t\t\tlastReadItemIds.forEach((lastItemId, userId) => {\n\t\t\t\t\t\tif (lastItemId === itemId) {\n\t\t\t\t\t\t\t_lastReaderIds.push(userId);\n\t\t\t\t\t\t\t_readers.push({ userId, isLastRead: true });\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn { lastReaderIds: _lastReaderIds, readers: _readers };\n\t\t\t}, [itemId, lastReadItemIds]);\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children({ readers, lastReaderIds })\n\t\t\t\t\t: children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemGroupReadIndicator\";\n\treturn Component;\n})();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"timeline-item-group.js","names":["React","senderType: SenderType","renderProps: TimelineItemGroupRenderProps","_lastReaderIds: string[]","_readers: Array<{ userId: string; isLastRead: boolean }>"],"sources":["../../src/primitives/timeline-item-group.tsx"],"sourcesContent":["import type { SenderType } from \"@cossistant/types\";\nimport type { TimelineItem as TimelineItemType } from \"@cossistant/types/api/timeline-item\";\nimport * as React from \"react\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\n/**\n * Shape returned to render-prop children describing the grouped timeline items state\n * and viewer specific flags.\n */\nexport type TimelineItemGroupRenderProps = {\n\t// Sender information\n\tsenderType: SenderType;\n\tsenderId: string;\n\tviewerType?: SenderType;\n\n\t// POV flags - who is viewing\n\tisSentByViewer: boolean; // True if the current viewer sent these items\n\tisReceivedByViewer: boolean; // True if the current viewer received these items\n\n\t// Sender type flags for convenience\n\tisVisitor: boolean;\n\tisAI: boolean;\n\tisTeamMember: boolean;\n\n\t// Item info\n\titemCount: number;\n\tfirstItemId: string | undefined;\n\tlastItemId: string | undefined;\n\n\t// Seen status\n\thasBeenSeenByViewer?: boolean;\n\tseenByIds?: readonly string[]; // IDs of users who have seen the last item in group\n};\n\nexport type TimelineItemGroupProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: TimelineItemGroupRenderProps) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\titems: TimelineItemType[];\n\n\t// POV context - who is viewing these timeline items\n\tviewerId?: string; // ID of the current viewer\n\tviewerType?: SenderType; // Type of the current viewer\n\n\t// Seen data\n\tseenByIds?: readonly string[]; // IDs of users who have seen these timeline items\n\tlastReadItemIds?: Map<string, string>; // Map of userId -> lastItemId they read\n};\n\n/**\n * Groups sequential timeline items from the same sender and exposes render helpers\n * that describe who sent the batch and whether the active viewer has seen it.\n * Consumers can either render their own layout via a render prop or rely on\n * slotted children. Typically used for MESSAGE-type items; EVENT items are usually rendered separately.\n */\nexport const TimelineItemGroup = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemGroupProps>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\titems = [],\n\t\t\t\tviewerId,\n\t\t\t\tseenByIds = [] as readonly string[],\n\t\t\t\tlastReadItemIds,\n\t\t\t\t...restProps\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst { viewerType, ...elementProps } = restProps;\n\n\t\t\t// Determine sender type from first timeline item in group\n\t\t\tconst firstItem = items[0];\n\t\t\tconst lastItem = items.at(-1);\n\n\t\t\t// Determine sender info\n\t\t\tlet senderId = \"\";\n\t\t\tlet senderType: SenderType;\n\n\t\t\tif (firstItem?.visitorId) {\n\t\t\t\tsenderId = firstItem.visitorId;\n\t\t\t\tsenderType = \"visitor\" as SenderType;\n\t\t\t} else if (firstItem?.aiAgentId) {\n\t\t\t\tsenderId = firstItem.aiAgentId;\n\t\t\t\tsenderType = \"ai\" as SenderType;\n\t\t\t} else if (firstItem?.userId) {\n\t\t\t\tsenderId = firstItem.userId;\n\t\t\t\tsenderType = \"team_member\" as SenderType;\n\t\t\t} else {\n\t\t\t\t// Fallback\n\t\t\t\tsenderId = firstItem?.id || \"unknown\";\n\t\t\t\tsenderType = \"team_member\" as SenderType;\n\t\t\t}\n\n\t\t\t// Determine POV\n\t\t\tconst isSentByViewer = viewerId\n\t\t\t\t? senderId === viewerId\n\t\t\t\t: viewerType\n\t\t\t\t\t? senderType === viewerType\n\t\t\t\t\t: false;\n\t\t\tconst isReceivedByViewer = viewerId\n\t\t\t\t? senderId !== viewerId\n\t\t\t\t: viewerType\n\t\t\t\t\t? senderType !== viewerType\n\t\t\t\t\t: true;\n\n\t\t\t// Convenience flags\n\t\t\tconst isVisitor = senderType === \"visitor\";\n\t\t\tconst isAI = senderType === \"ai\";\n\t\t\tconst isTeamMember = senderType === \"team_member\";\n\n\t\t\t// Check if viewer has seen these timeline items\n\t\t\tconst hasBeenSeenByViewer = viewerId\n\t\t\t\t? seenByIds.includes(viewerId)\n\t\t\t\t: undefined;\n\n\t\t\tconst renderProps: TimelineItemGroupRenderProps = {\n\t\t\t\tsenderType,\n\t\t\t\tsenderId,\n\t\t\t\tviewerType,\n\t\t\t\tisSentByViewer,\n\t\t\t\tisReceivedByViewer,\n\t\t\t\tisVisitor,\n\t\t\t\tisAI,\n\t\t\t\tisTeamMember,\n\t\t\t\titemCount: items.length,\n\t\t\t\tfirstItemId: firstItem?.id,\n\t\t\t\tlastItemId: lastItem?.id,\n\t\t\t\thasBeenSeenByViewer,\n\t\t\t\tseenByIds,\n\t\t\t};\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tstate: renderProps,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\trole: \"group\",\n\t\t\t\t\t\t\"aria-label\": `Timeline item group from ${senderType}`,\n\t\t\t\t\t\t...elementProps,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemGroup\";\n\treturn Component;\n})();\n\nexport type TimelineItemGroupAvatarProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode;\n\tasChild?: boolean;\n\tclassName?: string;\n};\n\n/**\n * Optional slot rendered next to a grouped batch to display an avatar, agent\n * badge or any other sender metadata supplied by the consumer UI.\n */\nexport const TimelineItemGroupAvatar = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemGroupAvatarProps\n\t>(({ children, className, asChild = false, ...props }, ref) =>\n\t\tuseRenderElement(\n\t\t\t\"div\",\n\t\t\t{\n\t\t\t\tclassName,\n\t\t\t\tasChild,\n\t\t\t},\n\t\t\t{\n\t\t\t\tref,\n\t\t\t\tprops: {\n\t\t\t\t\t...props,\n\t\t\t\t\tchildren,\n\t\t\t\t},\n\t\t\t}\n\t\t)\n\t);\n\n\tComponent.displayName = \"TimelineItemGroupAvatar\";\n\treturn Component;\n})();\n\nexport type TimelineItemGroupHeaderProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: {\n\t\t\t\tname?: string;\n\t\t\t\tsenderId?: string;\n\t\t\t\tsenderType?: SenderType;\n\t\t }) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\tname?: string;\n\tsenderId?: string;\n\tsenderType?: SenderType;\n};\n\n/**\n * Decorative or semantic wrapper rendered above a timeline item batch. Useful for\n * injecting agent names, timestamps or custom status labels tied to the sender\n * metadata supplied by `TimelineItemGroup`.\n */\nexport const TimelineItemGroupHeader = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemGroupHeaderProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\tname,\n\t\t\t\tsenderId,\n\t\t\t\tsenderType,\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children({ name, senderId, senderType })\n\t\t\t\t\t: children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemGroupHeader\";\n\treturn Component;\n})();\n\nexport type TimelineItemGroupContentProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode;\n\tasChild?: boolean;\n\tclassName?: string;\n};\n\n/**\n * Container for the actual timeline items within a batch. Consumers can\n * override the structure while inheriting layout props passed down from the\n * parent group.\n */\nexport const TimelineItemGroupContent = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemGroupContentProps\n\t>(({ children, className, asChild = false, ...props }, ref) =>\n\t\tuseRenderElement(\n\t\t\t\"div\",\n\t\t\t{\n\t\t\t\tclassName,\n\t\t\t\tasChild,\n\t\t\t},\n\t\t\t{\n\t\t\t\tref,\n\t\t\t\tprops: {\n\t\t\t\t\t...props,\n\t\t\t\t\tchildren,\n\t\t\t\t},\n\t\t\t}\n\t\t)\n\t);\n\n\tComponent.displayName = \"TimelineItemGroupContent\";\n\treturn Component;\n})();\n\nexport type TimelineItemGroupSeenIndicatorProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: {\n\t\t\t\tseenByIds: readonly string[];\n\t\t\t\thasBeenSeen: boolean;\n\t\t }) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\tseenByIds?: readonly string[];\n};\n\n/**\n * Utility slot for showing who has viewed the most recent timeline item in the\n * group. Works with simple text children or a render prop for advanced\n * displays.\n */\nexport const TimelineItemGroupSeenIndicator = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemGroupSeenIndicatorProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\tseenByIds = [] as readonly string[],\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst hasBeenSeen = seenByIds.length > 0;\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children({ seenByIds, hasBeenSeen })\n\t\t\t\t\t: children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemGroupSeenIndicator\";\n\treturn Component;\n})();\n\nexport type TimelineItemGroupReadIndicatorProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: {\n\t\t\t\treaders: Array<{ userId: string; isLastRead: boolean }>;\n\t\t\t\tlastReaderIds: string[];\n\t\t }) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\titemId: string;\n\tlastReadItemIds?: Map<string, string>;\n};\n\n/**\n * Renders read receipts for the tail timeline item in a group. It surfaces the list\n * of readers and callers can decide whether to render avatars, tooltips or a\n * basic label.\n */\nexport const TimelineItemGroupReadIndicator = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLDivElement,\n\t\tTimelineItemGroupReadIndicatorProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\titemId,\n\t\t\t\tlastReadItemIds,\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\t// Find all users who stopped reading at this timeline item\n\t\t\tconst { lastReaderIds, readers } = React.useMemo(() => {\n\t\t\t\tconst _lastReaderIds: string[] = [];\n\t\t\t\tconst _readers: Array<{ userId: string; isLastRead: boolean }> = [];\n\n\t\t\t\tif (lastReadItemIds) {\n\t\t\t\t\tlastReadItemIds.forEach((lastItemId, userId) => {\n\t\t\t\t\t\tif (lastItemId === itemId) {\n\t\t\t\t\t\t\t_lastReaderIds.push(userId);\n\t\t\t\t\t\t\t_readers.push({ userId, isLastRead: true });\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn { lastReaderIds: _lastReaderIds, readers: _readers };\n\t\t\t}, [itemId, lastReadItemIds]);\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children({ readers, lastReaderIds })\n\t\t\t\t\t: children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemGroupReadIndicator\";\n\treturn Component;\n})();\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-item.d.ts","names":[],"sources":["../../src/primitives/timeline-item.tsx"],"sourcesContent":[],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"timeline-item.d.ts","names":[],"sources":["../../src/primitives/timeline-item.tsx"],"sourcesContent":[],"mappings":";;;;;;;AAUA;AAUA;AACsB,KAXV,uBAAA,GAWU;EAArB,SAAM,EAAA,OAAA;EADyB,IAAA,EAAA,OAAA;EAK5B,OAAM,EAAA,OAAA;EACG,SAAA,EAZD,IAYC;EAA4B,IAAA,EAAM,MAAA,GAAA,IAAA;EAGxC,UAAA,EAAA,SAAA,GAAA,IAAA,GAAA,OAAA;EAAgB,QAAA,EAAA,SAAA,GAAA,OAAA,GAAA,gBAAA;AAQvB,CAAA;AAAyB,KAjBb,iBAAA,GAAoB,IAiBP,CAhBxB,OAAA,CAAM,cAgBkB,CAhBH,cAgBG,CAAA,EAAA,UAAA,CAAA,GAAA;EAAA,QAAA,CAAA,EAZrB,OAAA,CAAM,SAYe,GAAA,CAAA,CAAA,KAAA,EAXZ,uBAWY,EAAA,GAXgB,OAAA,CAAM,SAWtB,CAAA;EAAA,OAAA,CAAA,EAAA,OAAA;EAZrB,SAAM,CAAA,EAAA,MAAA;EACG,IAAA,EAGN,cAHM;CAA4B;;;;;;AAoM7B,cAzLC,YAyLuB,EAzLX,OAAA,CAAA,yBAyLW,CAzLX,IAyLW,CAzLX,OAAA,CAAA,cAyLW,CAzLX,cAyLW,CAAA,EAAA,UAAA,CAAA,GAAA;EACd,QAAA,CAAA,EAtMlB,OAAA,CAAM,SAsMY,GAAA,CAAA,CAAA,KAAA,EArMT,uBAqMS,EAAA,GArMmB,OAAA,CAAM,SAqMzB,CAAA;EAArB,OAAM,CAAA,EAAA,OAAA;EADgC,SAAA,CAAA,EAAA,MAAA;EAI3B,IAAA,EArML,cAqMW;CAAkC,wBAAM,eAAA,CAAA,CAAA;AAAS,KAJvD,wBAAA,GAA2B,IAI4B,CAHlE,OAAA,CAAM,cAG4D,CAH7C,cAG6C,CAAA,EAAA,UAAA,CAAA,GAAA;EAYtD,QAAA,CAAA,EAZD,OAAA,CAAM,SA6Dd,GAAA,CAAA,CAAA,OAAA,EAAA,MAAA,EAAA,GA7DgD,OAAA,CAAM,SA6DtD,CAAA;EAjD4B,OAAA,CAAA,EAAA,OAAA;EAAA,SAAA,CAAA,EAAA,MAAA;EAAA,IAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EAZpB,cAAM,CAAA,EAAA,OAAA;CAAkC;;;;;AA+DpD;AACsB,cApDT,mBAoDS,EApDU,OAAA,CAAA,yBAoDV,CApDU,IAoDV,CApDU,OAAA,CAAA,cAoDV,CApDU,cAoDV,CAAA,EAAA,UAAA,CAAA,GAAA;EAArB,QAAM,CAAA,EAhEK,OAAA,CAAM,SAgEX,GAAA,CAAA,CAAA,OAAA,EAAA,MAAA,EAAA,GAhE6C,OAAA,CAAM,SAgEnD,CAAA;EADkC,OAAA,CAAA,EAAA,OAAA;EAI7B,SAAM,CAAA,EAAA,MAAA;EAAyB,IAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EAAS,cAAM,CAAA,EAAA,OAAA;CAG9C,wBAAA,eAAA,CAAA,CAAA;AACK,KARL,0BAAA,GAA6B,IAQxB,CAPhB,OAAA,CAAM,cAOU,CAPK,eAOL,CAAA,EAAA,UAAA,CAAA,GAAA;EAAI,QAAA,CAAA,EAJT,OAAA,CAAM,SAIG,GAAA,CAAA,CAAA,SAAA,EAJsB,IAItB,EAAA,GAJ+B,OAAA,CAAM,SAIrC,CAAA;EAOR,OAAA,CAAA,EAAA,OAAA;EAAqB,SAAA,CAAA,EAAA,MAAA;EAAA,SAAA,EARtB,IAQsB;EAAA,MAAA,CAAA,EAAA,CAAA,IAAA,EAPjB,IAOiB,EAAA,GAAA,MAAA;CAXtB;;;;;cAWC,uBAAqB,OAAA,CAAA,0BAAA,KAAA,OAAA,CAAA,eAAA;aAXtB,OAAA,CAAM,yBAAyB,SAAS,OAAA,CAAM;EAWxB,OAAA,CAAA,EAAA,OAAA;EAAA,SAAA,CAAA,EAAA,MAAA;aARtB;kBACK"}
|
|
@@ -2,6 +2,7 @@ import { useRenderElement } from "../utils/use-render-element.js";
|
|
|
2
2
|
import * as React$1 from "react";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
import ReactMarkdown from "react-markdown";
|
|
5
|
+
import remarkBreaks from "remark-breaks";
|
|
5
6
|
|
|
6
7
|
//#region src/primitives/timeline-item.tsx
|
|
7
8
|
/**
|
|
@@ -10,7 +11,7 @@ import ReactMarkdown from "react-markdown";
|
|
|
10
11
|
* both MESSAGE and EVENT timeline item types.
|
|
11
12
|
*/
|
|
12
13
|
const TimelineItem = (() => {
|
|
13
|
-
const Component = React$1.forwardRef(({ children, className, asChild = false, item
|
|
14
|
+
const Component = React$1.forwardRef(({ children, className, asChild = false, item, ...props }, ref) => {
|
|
14
15
|
const isVisitor = item.visitorId !== null;
|
|
15
16
|
const isAI = item.aiAgentId !== null;
|
|
16
17
|
const isHuman = item.userId !== null && !isVisitor;
|
|
@@ -52,12 +53,15 @@ const TimelineItem = (() => {
|
|
|
52
53
|
const MemoizedMarkdownBlock = React$1.memo(({ content }) => {
|
|
53
54
|
return /* @__PURE__ */ jsx(ReactMarkdown, {
|
|
54
55
|
components: {
|
|
55
|
-
p: ({ children }) =>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
p: ({ children }) => {
|
|
57
|
+
if (children === void 0 || children === null || children === "" || Array.isArray(children) && children.every((c) => c === "\n" || c === "" || c == null)) return null;
|
|
58
|
+
return /* @__PURE__ */ jsx("span", {
|
|
59
|
+
className: "mt-1 block first:mt-0",
|
|
60
|
+
children
|
|
61
|
+
});
|
|
62
|
+
},
|
|
59
63
|
br: () => /* @__PURE__ */ jsx("br", {}),
|
|
60
|
-
code: ({ children
|
|
64
|
+
code: ({ children, ...props }) => {
|
|
61
65
|
return !("className" in props && typeof props.className === "string" && props.className.includes("language-")) ? /* @__PURE__ */ jsx("code", {
|
|
62
66
|
className: "rounded bg-co-background-300 px-1 py-0.5 text-xs",
|
|
63
67
|
children
|
|
@@ -73,14 +77,50 @@ const MemoizedMarkdownBlock = React$1.memo(({ content }) => {
|
|
|
73
77
|
className: "font-semibold",
|
|
74
78
|
children
|
|
75
79
|
}),
|
|
76
|
-
|
|
77
|
-
className: "
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
ol: ({ children }) => /* @__PURE__ */ jsx("ol", {
|
|
81
|
+
className: "my-0 list-decimal pl-6",
|
|
82
|
+
children
|
|
83
|
+
}),
|
|
84
|
+
ul: ({ children }) => /* @__PURE__ */ jsx("ul", {
|
|
85
|
+
className: "my-0 list-disc pl-6",
|
|
86
|
+
children
|
|
87
|
+
}),
|
|
88
|
+
li: ({ children }) => /* @__PURE__ */ jsx("li", {
|
|
89
|
+
className: "[&>span.block]:mt-0 [&>span.block]:inline",
|
|
81
90
|
children
|
|
82
|
-
})
|
|
91
|
+
}),
|
|
92
|
+
blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", {
|
|
93
|
+
className: "my-1 border-co-border border-l-2 pl-3 italic opacity-80",
|
|
94
|
+
children
|
|
95
|
+
}),
|
|
96
|
+
em: ({ children }) => /* @__PURE__ */ jsx("em", {
|
|
97
|
+
className: "italic",
|
|
98
|
+
children
|
|
99
|
+
}),
|
|
100
|
+
a: ({ href, children, node }) => {
|
|
101
|
+
const rawHref = href || node?.properties?.href || "";
|
|
102
|
+
if (rawHref.startsWith("mention:")) {
|
|
103
|
+
const parts = rawHref.split(":");
|
|
104
|
+
const mentionType = parts[1];
|
|
105
|
+
const mentionId = parts.slice(2).join(":");
|
|
106
|
+
return /* @__PURE__ */ jsx("span", {
|
|
107
|
+
className: "rounded bg-co-orange/15 font-medium text-co-orange",
|
|
108
|
+
"data-mention-id": mentionId,
|
|
109
|
+
"data-mention-type": mentionType,
|
|
110
|
+
children
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return /* @__PURE__ */ jsx("a", {
|
|
114
|
+
className: "underline hover:opacity-80",
|
|
115
|
+
href,
|
|
116
|
+
rel: "noopener noreferrer",
|
|
117
|
+
target: "_blank",
|
|
118
|
+
children
|
|
119
|
+
});
|
|
120
|
+
}
|
|
83
121
|
},
|
|
122
|
+
remarkPlugins: [remarkBreaks],
|
|
123
|
+
urlTransform: (url) => url,
|
|
84
124
|
children: content
|
|
85
125
|
});
|
|
86
126
|
}, (prevProps, nextProps) => {
|
|
@@ -94,7 +134,7 @@ MemoizedMarkdownBlock.displayName = "MemoizedMarkdownBlock";
|
|
|
94
134
|
* formatting.
|
|
95
135
|
*/
|
|
96
136
|
const TimelineItemContent = (() => {
|
|
97
|
-
const Component = React$1.forwardRef(({ children, className, asChild = false, text = "", renderMarkdown = true
|
|
137
|
+
const Component = React$1.forwardRef(({ children, className, asChild = false, text = "", renderMarkdown = true, ...props }, ref) => {
|
|
98
138
|
const content = React$1.useMemo(() => {
|
|
99
139
|
const textContent = text ?? "";
|
|
100
140
|
if (typeof children === "function") return children(textContent);
|
|
@@ -129,7 +169,7 @@ const TimelineItemTimestamp = (() => {
|
|
|
129
169
|
const Component = React$1.forwardRef(({ children, className, asChild = false, timestamp, format = (date) => date.toLocaleTimeString([], {
|
|
130
170
|
hour: "2-digit",
|
|
131
171
|
minute: "2-digit"
|
|
132
|
-
})
|
|
172
|
+
}), ...props }, ref) => {
|
|
133
173
|
const content = typeof children === "function" ? children(timestamp) : children || format(timestamp);
|
|
134
174
|
return useRenderElement("span", {
|
|
135
175
|
className,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-item.js","names":["React","renderProps: TimelineItemRenderProps"],"sources":["../../src/primitives/timeline-item.tsx"],"sourcesContent":["import type { TimelineItem as TimelineItemType } from \"@cossistant/types/api/timeline-item\";\nimport * as React from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\n/**\n * Metadata describing the origin of a timeline item and pre-parsed content that can\n * be consumed by render-prop children.\n */\nexport type TimelineItemRenderProps = {\n\tisVisitor: boolean;\n\tisAI: boolean;\n\tisHuman: boolean;\n\ttimestamp: Date;\n\ttext: string | null;\n\tsenderType: \"visitor\" | \"ai\" | \"human\";\n\titemType: \"message\" | \"event\" | \"identification\";\n};\n\nexport type TimelineItemProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: TimelineItemRenderProps) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\titem: TimelineItemType;\n};\n\n/**\n * Generic timeline item wrapper that adds accessibility attributes and resolves the\n * sender type into convenient render props for custom layouts. Works with\n * both MESSAGE and EVENT timeline item types.\n */\nexport const TimelineItem = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemProps>(\n\t\t({ children, className, asChild = false, item, ...props }, ref) => {\n\t\t\t// Determine sender type from timeline item properties\n\t\t\tconst isVisitor = item.visitorId !== null;\n\t\t\tconst isAI = item.aiAgentId !== null;\n\t\t\tconst isHuman = item.userId !== null && !isVisitor;\n\n\t\t\tconst senderType = isVisitor ? \"visitor\" : isAI ? \"ai\" : \"human\";\n\n\t\t\tconst renderProps: TimelineItemRenderProps = {\n\t\t\t\tisVisitor,\n\t\t\t\tisAI,\n\t\t\t\tisHuman,\n\t\t\t\ttimestamp: new Date(item.createdAt),\n\t\t\t\ttext: item.text,\n\t\t\t\tsenderType,\n\t\t\t\titemType: item.type,\n\t\t\t};\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\t\tconst itemTypeLabel = (() => {\n\t\t\t\tif (item.type === \"event\") {\n\t\t\t\t\treturn \"Event\";\n\t\t\t\t}\n\t\t\t\tif (item.type === \"identification\") {\n\t\t\t\t\treturn \"Identification\";\n\t\t\t\t}\n\t\t\t\tif (isVisitor) {\n\t\t\t\t\treturn \"visitor\";\n\t\t\t\t}\n\t\t\t\tif (isAI) {\n\t\t\t\t\treturn \"AI assistant\";\n\t\t\t\t}\n\t\t\t\treturn \"human agent\";\n\t\t\t})();\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tstate: renderProps,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\trole: \"article\",\n\t\t\t\t\t\t\"aria-label\": `${item.type === \"message\" ? \"Message\" : \"Event\"} from ${itemTypeLabel}`,\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItem\";\n\treturn Component;\n})();\n\nconst MemoizedMarkdownBlock = React.memo(\n\t({ content }: { content: string }) => {\n\t\treturn (\n\t\t\t<ReactMarkdown\n\t\t\t\tcomponents={{\n\t\t\t\t\t// Render paragraphs as block elements to preserve multiline spacing\n\t\t\t\t\tp: ({ children }) => <span className=\"block\">{children}</span>,\n\t\t\t\t\t// Ensure proper line break handling\n\t\t\t\t\tbr: () => <br />,\n\t\t\t\t\t// Handle code blocks properly\n\t\t\t\t\tcode: ({ children, ...props }) => {\n\t\t\t\t\t\t// Check if it's inline code by looking at the parent element\n\t\t\t\t\t\tconst isInline = !(\n\t\t\t\t\t\t\t\"className\" in props &&\n\t\t\t\t\t\t\ttypeof props.className === \"string\" &&\n\t\t\t\t\t\t\tprops.className.includes(\"language-\")\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn isInline ? (\n\t\t\t\t\t\t\t<code className=\"rounded bg-co-background-300 px-1 py-0.5 text-xs\">\n\t\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<pre className=\"overflow-x-auto rounded bg-co-background-300 p-2\">\n\t\t\t\t\t\t\t\t<code className=\"text-xs\">{children}</code>\n\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\t// Handle strong/bold text\n\t\t\t\t\tstrong: ({ children }) => (\n\t\t\t\t\t\t<strong className=\"font-semibold\">{children}</strong>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle links\n\t\t\t\t\ta: ({ href, children }) => (\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tclassName=\"underline hover:opacity-80\"\n\t\t\t\t\t\t\thref={href}\n\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t),\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{content}\n\t\t\t</ReactMarkdown>\n\t\t);\n\t},\n\t(prevProps, nextProps) => {\n\t\tif (prevProps.content !== nextProps.content) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n);\n\nMemoizedMarkdownBlock.displayName = \"MemoizedMarkdownBlock\";\n\nexport type TimelineItemContentProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode | ((content: string) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\ttext?: string | null;\n\trenderMarkdown?: boolean;\n};\n\n/**\n * Renders the content of a timeline item, optionally piping Markdown content through a\n * memoised renderer or handing the raw text to a render prop for custom\n * formatting.\n */\nexport const TimelineItemContent = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemContentProps>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\ttext = \"\",\n\t\t\t\trenderMarkdown = true,\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst content = React.useMemo(() => {\n\t\t\t\tconst textContent = text ?? \"\";\n\t\t\t\tif (typeof children === \"function\") {\n\t\t\t\t\treturn children(textContent);\n\t\t\t\t}\n\t\t\t\tif (children) {\n\t\t\t\t\treturn children;\n\t\t\t\t}\n\t\t\t\tif (renderMarkdown && textContent) {\n\t\t\t\t\treturn <MemoizedMarkdownBlock content={textContent} />;\n\t\t\t\t}\n\t\t\t\treturn textContent;\n\t\t\t}, [children, text, renderMarkdown]);\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t...props.style,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemContent\";\n\treturn Component;\n})();\n\nexport type TimelineItemTimestampProps = Omit<\n\tReact.HTMLAttributes<HTMLSpanElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode | ((timestamp: Date) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\ttimestamp: Date;\n\tformat?: (date: Date) => string;\n};\n\n/**\n * Timestamp helper that renders a formatted date or allows callers to supply a\n * render prop for custom time displays while preserving semantic markup.\n */\nexport const TimelineItemTimestamp = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLSpanElement,\n\t\tTimelineItemTimestampProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\ttimestamp,\n\t\t\t\tformat = (date) =>\n\t\t\t\t\tdate.toLocaleTimeString([], {\n\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t}),\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children(timestamp)\n\t\t\t\t\t: children || format(timestamp);\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"span\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemTimestamp\";\n\treturn Component;\n})();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"timeline-item.js","names":["React","renderProps: TimelineItemRenderProps"],"sources":["../../src/primitives/timeline-item.tsx"],"sourcesContent":["import type { TimelineItem as TimelineItemType } from \"@cossistant/types/api/timeline-item\";\nimport * as React from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\n/**\n * Metadata describing the origin of a timeline item and pre-parsed content that can\n * be consumed by render-prop children.\n */\nexport type TimelineItemRenderProps = {\n\tisVisitor: boolean;\n\tisAI: boolean;\n\tisHuman: boolean;\n\ttimestamp: Date;\n\ttext: string | null;\n\tsenderType: \"visitor\" | \"ai\" | \"human\";\n\titemType: \"message\" | \"event\" | \"identification\";\n};\n\nexport type TimelineItemProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: TimelineItemRenderProps) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\titem: TimelineItemType;\n};\n\n/**\n * Generic timeline item wrapper that adds accessibility attributes and resolves the\n * sender type into convenient render props for custom layouts. Works with\n * both MESSAGE and EVENT timeline item types.\n */\nexport const TimelineItem = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemProps>(\n\t\t({ children, className, asChild = false, item, ...props }, ref) => {\n\t\t\t// Determine sender type from timeline item properties\n\t\t\tconst isVisitor = item.visitorId !== null;\n\t\t\tconst isAI = item.aiAgentId !== null;\n\t\t\tconst isHuman = item.userId !== null && !isVisitor;\n\n\t\t\tconst senderType = isVisitor ? \"visitor\" : isAI ? \"ai\" : \"human\";\n\n\t\t\tconst renderProps: TimelineItemRenderProps = {\n\t\t\t\tisVisitor,\n\t\t\t\tisAI,\n\t\t\t\tisHuman,\n\t\t\t\ttimestamp: new Date(item.createdAt),\n\t\t\t\ttext: item.text,\n\t\t\t\tsenderType,\n\t\t\t\titemType: item.type,\n\t\t\t};\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\t\tconst itemTypeLabel = (() => {\n\t\t\t\tif (item.type === \"event\") {\n\t\t\t\t\treturn \"Event\";\n\t\t\t\t}\n\t\t\t\tif (item.type === \"identification\") {\n\t\t\t\t\treturn \"Identification\";\n\t\t\t\t}\n\t\t\t\tif (isVisitor) {\n\t\t\t\t\treturn \"visitor\";\n\t\t\t\t}\n\t\t\t\tif (isAI) {\n\t\t\t\t\treturn \"AI assistant\";\n\t\t\t\t}\n\t\t\t\treturn \"human agent\";\n\t\t\t})();\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tstate: renderProps,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\trole: \"article\",\n\t\t\t\t\t\t\"aria-label\": `${item.type === \"message\" ? \"Message\" : \"Event\"} from ${itemTypeLabel}`,\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItem\";\n\treturn Component;\n})();\n\nconst MemoizedMarkdownBlock = React.memo(\n\t({ content }: { content: string }) => {\n\t\treturn (\n\t\t\t<ReactMarkdown\n\t\t\t\t// Allow mention: protocol URLs (not sanitized by default)\n\t\t\t\tcomponents={{\n\t\t\t\t\t// Render paragraphs as block elements to preserve multiline spacing\n\t\t\t\t\tp: ({ children }) => {\n\t\t\t\t\t\t// Skip empty paragraphs (caused by consecutive blank lines in markdown)\n\t\t\t\t\t\tconst isEmpty =\n\t\t\t\t\t\t\tchildren === undefined ||\n\t\t\t\t\t\t\tchildren === null ||\n\t\t\t\t\t\t\tchildren === \"\" ||\n\t\t\t\t\t\t\t(Array.isArray(children) &&\n\t\t\t\t\t\t\t\tchildren.every((c) => c === \"\\n\" || c === \"\" || c == null));\n\t\t\t\t\t\tif (isEmpty) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn <span className=\"mt-1 block first:mt-0\">{children}</span>;\n\t\t\t\t\t},\n\t\t\t\t\t// Ensure proper line break handling\n\t\t\t\t\tbr: () => <br />,\n\t\t\t\t\t// Handle code blocks properly\n\t\t\t\t\tcode: ({ children, ...props }) => {\n\t\t\t\t\t\t// Check if it's inline code by looking at the parent element\n\t\t\t\t\t\tconst isInline = !(\n\t\t\t\t\t\t\t\"className\" in props &&\n\t\t\t\t\t\t\ttypeof props.className === \"string\" &&\n\t\t\t\t\t\t\tprops.className.includes(\"language-\")\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn isInline ? (\n\t\t\t\t\t\t\t<code className=\"rounded bg-co-background-300 px-1 py-0.5 text-xs\">\n\t\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<pre className=\"overflow-x-auto rounded bg-co-background-300 p-2\">\n\t\t\t\t\t\t\t\t<code className=\"text-xs\">{children}</code>\n\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\t// Handle strong/bold text\n\t\t\t\t\tstrong: ({ children }) => (\n\t\t\t\t\t\t<strong className=\"font-semibold\">{children}</strong>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle ordered lists\n\t\t\t\t\tol: ({ children }) => (\n\t\t\t\t\t\t<ol className=\"my-0 list-decimal pl-6\">{children}</ol>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle unordered lists\n\t\t\t\t\tul: ({ children }) => (\n\t\t\t\t\t\t<ul className=\"my-0 list-disc pl-6\">{children}</ul>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle list items\n\t\t\t\t\tli: ({ children }) => (\n\t\t\t\t\t\t<li className=\"[&>span.block]:mt-0 [&>span.block]:inline\">\n\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle blockquotes\n\t\t\t\t\tblockquote: ({ children }) => (\n\t\t\t\t\t\t<blockquote className=\"my-1 border-co-border border-l-2 pl-3 italic opacity-80\">\n\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t</blockquote>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle emphasis\n\t\t\t\t\tem: ({ children }) => <em className=\"italic\">{children}</em>,\n\t\t\t\t\t// Handle links - with special handling for mentions\n\t\t\t\t\t// Mention format: [@Name](mention:type:id) - the @ is inside the link\n\t\t\t\t\ta: ({ href, children, node }) => {\n\t\t\t\t\t\t// Get the raw href from the AST node if available (react-markdown may sanitize href)\n\t\t\t\t\t\tconst rawHref =\n\t\t\t\t\t\t\thref || (node?.properties?.href as string | undefined) || \"\";\n\n\t\t\t\t\t\t// Check if this is a mention link: mention:type:id\n\t\t\t\t\t\tif (rawHref.startsWith(\"mention:\")) {\n\t\t\t\t\t\t\t// Parse mention:type:id format\n\t\t\t\t\t\t\tconst parts = rawHref.split(\":\");\n\t\t\t\t\t\t\tconst mentionType = parts[1]; // visitor, ai-agent, human-agent\n\t\t\t\t\t\t\tconst mentionId = parts.slice(2).join(\":\"); // id (may contain colons)\n\n\t\t\t\t\t\t\t// Render as styled orange pill (same design as input)\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclassName=\"rounded bg-co-orange/15 font-medium text-co-orange\"\n\t\t\t\t\t\t\t\t\tdata-mention-id={mentionId}\n\t\t\t\t\t\t\t\t\tdata-mention-type={mentionType}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Regular link\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\tclassName=\"underline hover:opacity-80\"\n\t\t\t\t\t\t\t\thref={href}\n\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t}}\n\t\t\t\tremarkPlugins={[remarkBreaks]}\n\t\t\t\turlTransform={(url) => url}\n\t\t\t>\n\t\t\t\t{content}\n\t\t\t</ReactMarkdown>\n\t\t);\n\t},\n\t(prevProps, nextProps) => {\n\t\tif (prevProps.content !== nextProps.content) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n);\n\nMemoizedMarkdownBlock.displayName = \"MemoizedMarkdownBlock\";\n\nexport type TimelineItemContentProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode | ((content: string) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\ttext?: string | null;\n\trenderMarkdown?: boolean;\n};\n\n/**\n * Renders the content of a timeline item, optionally piping Markdown content through a\n * memoised renderer or handing the raw text to a render prop for custom\n * formatting.\n */\nexport const TimelineItemContent = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemContentProps>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\ttext = \"\",\n\t\t\t\trenderMarkdown = true,\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst content = React.useMemo(() => {\n\t\t\t\tconst textContent = text ?? \"\";\n\t\t\t\tif (typeof children === \"function\") {\n\t\t\t\t\treturn children(textContent);\n\t\t\t\t}\n\t\t\t\tif (children) {\n\t\t\t\t\treturn children;\n\t\t\t\t}\n\t\t\t\tif (renderMarkdown && textContent) {\n\t\t\t\t\treturn <MemoizedMarkdownBlock content={textContent} />;\n\t\t\t\t}\n\t\t\t\treturn textContent;\n\t\t\t}, [children, text, renderMarkdown]);\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t...props.style,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemContent\";\n\treturn Component;\n})();\n\nexport type TimelineItemTimestampProps = Omit<\n\tReact.HTMLAttributes<HTMLSpanElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode | ((timestamp: Date) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\ttimestamp: Date;\n\tformat?: (date: Date) => string;\n};\n\n/**\n * Timestamp helper that renders a formatted date or allows callers to supply a\n * render prop for custom time displays while preserving semantic markup.\n */\nexport const TimelineItemTimestamp = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLSpanElement,\n\t\tTimelineItemTimestampProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\ttimestamp,\n\t\t\t\tformat = (date) =>\n\t\t\t\t\tdate.toLocaleTimeString([], {\n\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t}),\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children(timestamp)\n\t\t\t\t\t: children || format(timestamp);\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"span\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemTimestamp\";\n\treturn Component;\n})();\n"],"mappings":"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"}
|
package/primitives/trigger.js
CHANGED
|
@@ -31,7 +31,7 @@ import * as React$1 from "react";
|
|
|
31
31
|
* <MyCustomButton>Help</MyCustomButton>
|
|
32
32
|
* </Trigger>
|
|
33
33
|
*/
|
|
34
|
-
const SupportTrigger = React$1.forwardRef(({ children, className, asChild = false
|
|
34
|
+
const SupportTrigger = React$1.forwardRef(({ children, className, asChild = false, ...props }, ref) => {
|
|
35
35
|
const { isOpen, toggle } = useSupportConfig();
|
|
36
36
|
const { unreadCount, visitor } = useSupport();
|
|
37
37
|
const visitorId = visitor?.id ?? null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trigger.js","names":["React","renderProps: TriggerRenderProps"],"sources":["../../src/primitives/trigger.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { useSupport } from \"../provider\";\nimport { useTypingStore } from \"../realtime/typing-store\";\nimport { useSupportConfig } from \"../support\";\nimport { useTriggerRef } from \"../support/context/positioning\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\n/**\n * Render props provided to the Trigger's children function.\n */\nexport type TriggerRenderProps = {\n\tisOpen: boolean;\n\tunreadCount: number;\n\tisTyping: boolean;\n\ttoggle: () => void;\n};\n\nexport type TriggerProps = Omit<\n\tReact.ButtonHTMLAttributes<HTMLButtonElement>,\n\t\"children\"\n> & {\n\t/**\n\t * Content to render inside the trigger.\n\t * Can be a ReactNode or a function that receives render props.\n\t *\n\t * @example\n\t * // Static content\n\t * <Trigger>Help</Trigger>\n\t *\n\t * @example\n\t * // Dynamic content with render props\n\t * <Trigger>\n\t * {({ isOpen, unreadCount }) => (\n\t * <span>{isOpen ? \"Close\" : `Help (${unreadCount})`}</span>\n\t * )}\n\t * </Trigger>\n\t */\n\tchildren?: React.ReactNode | ((props: TriggerRenderProps) => React.ReactNode);\n\t/**\n\t * When true, the Trigger will render its children directly,\n\t * passing all props to the child element.\n\t */\n\tasChild?: boolean;\n\tclassName?: string;\n};\n\n/**\n * Trigger button that toggles the support window.\n * Can be placed anywhere in the DOM - the window will position itself relative to this element.\n *\n * @example\n * // Simple usage\n * <Trigger className=\"my-button\">Need help?</Trigger>\n *\n * @example\n * // With render props\n * <Trigger>\n * {({ isOpen, unreadCount, isTyping }) => (\n * <button className=\"flex items-center gap-2\">\n * {isOpen ? \"×\" : \"💬\"}\n * {unreadCount > 0 && <span className=\"badge\">{unreadCount}</span>}\n * </button>\n * )}\n * </Trigger>\n *\n * @example\n * // With asChild pattern\n * <Trigger asChild>\n * <MyCustomButton>Help</MyCustomButton>\n * </Trigger>\n */\nexport const SupportTrigger = React.forwardRef<HTMLButtonElement, TriggerProps>(\n\t({ children, className, asChild = false, ...props }, ref) => {\n\t\tconst { isOpen, toggle } = useSupportConfig();\n\t\tconst { unreadCount, visitor } = useSupport();\n\t\tconst visitorId = visitor?.id ?? null;\n\t\tconst triggerRefContext = useTriggerRef();\n\n\t\t// Extract setTriggerElement for stable dependency (state setter has stable identity)\n\t\tconst setTriggerElement = triggerRefContext?.setTriggerElement;\n\n\t\t// Merge the external ref with the positioning context ref\n\t\t// Using setTriggerElement directly ensures stable ref callback identity\n\t\tconst mergedRef = React.useCallback(\n\t\t\t(element: HTMLButtonElement | null) => {\n\t\t\t\t// Set the positioning context ref\n\t\t\t\tsetTriggerElement?.(element);\n\n\t\t\t\t// Handle the forwarded ref\n\t\t\t\tif (typeof ref === \"function\") {\n\t\t\t\t\tref(element);\n\t\t\t\t} else if (ref) {\n\t\t\t\t\tref.current = element;\n\t\t\t\t}\n\t\t\t},\n\t\t\t[ref, setTriggerElement]\n\t\t);\n\n\t\tconst hasTyping = useTypingStore(\n\t\t\tReact.useCallback(\n\t\t\t\t(state) =>\n\t\t\t\t\tObject.values(state.conversations).some((entries) =>\n\t\t\t\t\t\tObject.values(entries).some((entry) => {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tvisitorId &&\n\t\t\t\t\t\t\t\tentry.actorType === \"visitor\" &&\n\t\t\t\t\t\t\t\tentry.actorId === visitorId\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t})\n\t\t\t\t\t),\n\t\t\t\t[visitorId]\n\t\t\t)\n\t\t);\n\n\t\tconst renderProps: TriggerRenderProps = {\n\t\t\tisOpen,\n\t\t\tunreadCount,\n\t\t\tisTyping: hasTyping,\n\t\t\ttoggle,\n\t\t};\n\n\t\tconst content =\n\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\treturn useRenderElement(\n\t\t\t\"button\",\n\t\t\t{\n\t\t\t\tasChild,\n\t\t\t\tclassName,\n\t\t\t},\n\t\t\t{\n\t\t\t\tref: mergedRef,\n\t\t\t\tstate: renderProps,\n\t\t\t\tprops: {\n\t\t\t\t\ttype: \"button\",\n\t\t\t\t\t\"aria-haspopup\": \"dialog\",\n\t\t\t\t\t\"aria-expanded\": isOpen,\n\t\t\t\t\tonClick: toggle,\n\t\t\t\t\t...props,\n\t\t\t\t\tchildren: content,\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\t}\n);\n\nSupportTrigger.displayName = \"SupportTrigger\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuEA,MAAa,iBAAiBA,QAAM,YAClC,EAAE,UAAU,WAAW,UAAU,
|
|
1
|
+
{"version":3,"file":"trigger.js","names":["React","renderProps: TriggerRenderProps"],"sources":["../../src/primitives/trigger.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { useSupport } from \"../provider\";\nimport { useTypingStore } from \"../realtime/typing-store\";\nimport { useSupportConfig } from \"../support\";\nimport { useTriggerRef } from \"../support/context/positioning\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\n/**\n * Render props provided to the Trigger's children function.\n */\nexport type TriggerRenderProps = {\n\tisOpen: boolean;\n\tunreadCount: number;\n\tisTyping: boolean;\n\ttoggle: () => void;\n};\n\nexport type TriggerProps = Omit<\n\tReact.ButtonHTMLAttributes<HTMLButtonElement>,\n\t\"children\"\n> & {\n\t/**\n\t * Content to render inside the trigger.\n\t * Can be a ReactNode or a function that receives render props.\n\t *\n\t * @example\n\t * // Static content\n\t * <Trigger>Help</Trigger>\n\t *\n\t * @example\n\t * // Dynamic content with render props\n\t * <Trigger>\n\t * {({ isOpen, unreadCount }) => (\n\t * <span>{isOpen ? \"Close\" : `Help (${unreadCount})`}</span>\n\t * )}\n\t * </Trigger>\n\t */\n\tchildren?: React.ReactNode | ((props: TriggerRenderProps) => React.ReactNode);\n\t/**\n\t * When true, the Trigger will render its children directly,\n\t * passing all props to the child element.\n\t */\n\tasChild?: boolean;\n\tclassName?: string;\n};\n\n/**\n * Trigger button that toggles the support window.\n * Can be placed anywhere in the DOM - the window will position itself relative to this element.\n *\n * @example\n * // Simple usage\n * <Trigger className=\"my-button\">Need help?</Trigger>\n *\n * @example\n * // With render props\n * <Trigger>\n * {({ isOpen, unreadCount, isTyping }) => (\n * <button className=\"flex items-center gap-2\">\n * {isOpen ? \"×\" : \"💬\"}\n * {unreadCount > 0 && <span className=\"badge\">{unreadCount}</span>}\n * </button>\n * )}\n * </Trigger>\n *\n * @example\n * // With asChild pattern\n * <Trigger asChild>\n * <MyCustomButton>Help</MyCustomButton>\n * </Trigger>\n */\nexport const SupportTrigger = React.forwardRef<HTMLButtonElement, TriggerProps>(\n\t({ children, className, asChild = false, ...props }, ref) => {\n\t\tconst { isOpen, toggle } = useSupportConfig();\n\t\tconst { unreadCount, visitor } = useSupport();\n\t\tconst visitorId = visitor?.id ?? null;\n\t\tconst triggerRefContext = useTriggerRef();\n\n\t\t// Extract setTriggerElement for stable dependency (state setter has stable identity)\n\t\tconst setTriggerElement = triggerRefContext?.setTriggerElement;\n\n\t\t// Merge the external ref with the positioning context ref\n\t\t// Using setTriggerElement directly ensures stable ref callback identity\n\t\tconst mergedRef = React.useCallback(\n\t\t\t(element: HTMLButtonElement | null) => {\n\t\t\t\t// Set the positioning context ref\n\t\t\t\tsetTriggerElement?.(element);\n\n\t\t\t\t// Handle the forwarded ref\n\t\t\t\tif (typeof ref === \"function\") {\n\t\t\t\t\tref(element);\n\t\t\t\t} else if (ref) {\n\t\t\t\t\tref.current = element;\n\t\t\t\t}\n\t\t\t},\n\t\t\t[ref, setTriggerElement]\n\t\t);\n\n\t\tconst hasTyping = useTypingStore(\n\t\t\tReact.useCallback(\n\t\t\t\t(state) =>\n\t\t\t\t\tObject.values(state.conversations).some((entries) =>\n\t\t\t\t\t\tObject.values(entries).some((entry) => {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tvisitorId &&\n\t\t\t\t\t\t\t\tentry.actorType === \"visitor\" &&\n\t\t\t\t\t\t\t\tentry.actorId === visitorId\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t})\n\t\t\t\t\t),\n\t\t\t\t[visitorId]\n\t\t\t)\n\t\t);\n\n\t\tconst renderProps: TriggerRenderProps = {\n\t\t\tisOpen,\n\t\t\tunreadCount,\n\t\t\tisTyping: hasTyping,\n\t\t\ttoggle,\n\t\t};\n\n\t\tconst content =\n\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\treturn useRenderElement(\n\t\t\t\"button\",\n\t\t\t{\n\t\t\t\tasChild,\n\t\t\t\tclassName,\n\t\t\t},\n\t\t\t{\n\t\t\t\tref: mergedRef,\n\t\t\t\tstate: renderProps,\n\t\t\t\tprops: {\n\t\t\t\t\ttype: \"button\",\n\t\t\t\t\t\"aria-haspopup\": \"dialog\",\n\t\t\t\t\t\"aria-expanded\": isOpen,\n\t\t\t\t\tonClick: toggle,\n\t\t\t\t\t...props,\n\t\t\t\t\tchildren: content,\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\t}\n);\n\nSupportTrigger.displayName = \"SupportTrigger\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuEA,MAAa,iBAAiBA,QAAM,YAClC,EAAE,UAAU,WAAW,UAAU,OAAO,GAAG,SAAS,QAAQ;CAC5D,MAAM,EAAE,QAAQ,WAAW,kBAAkB;CAC7C,MAAM,EAAE,aAAa,YAAY,YAAY;CAC7C,MAAM,YAAY,SAAS,MAAM;CAIjC,MAAM,oBAHoB,eAAe,EAGI;CAI7C,MAAM,YAAYA,QAAM,aACtB,YAAsC;AAEtC,sBAAoB,QAAQ;AAG5B,MAAI,OAAO,QAAQ,WAClB,KAAI,QAAQ;WACF,IACV,KAAI,UAAU;IAGhB,CAAC,KAAK,kBAAkB,CACxB;CAsBD,MAAMC,cAAkC;EACvC;EACA;EACA,UAvBiB,eACjBD,QAAM,aACJ,UACA,OAAO,OAAO,MAAM,cAAc,CAAC,MAAM,YACxC,OAAO,OAAO,QAAQ,CAAC,MAAM,UAAU;AACtC,OACC,aACA,MAAM,cAAc,aACpB,MAAM,YAAY,UAElB,QAAO;AAGR,UAAO;IACN,CACF,EACF,CAAC,UAAU,CACX,CACD;EAMA;EACA;CAED,MAAM,UACL,OAAO,aAAa,aAAa,SAAS,YAAY,GAAG;AAE1D,QAAO,iBACN,UACA;EACC;EACA;EACA,EACD;EACC,KAAK;EACL,OAAO;EACP,OAAO;GACN,MAAM;GACN,iBAAiB;GACjB,iBAAiB;GACjB,SAAS;GACT,GAAG;GACH,UAAU;GACV;EACD,CACD;EAEF;AAED,eAAe,cAAc"}
|
package/primitives/window.js
CHANGED
|
@@ -42,7 +42,7 @@ function getFocusableElements(container) {
|
|
|
42
42
|
* </Window>
|
|
43
43
|
*/
|
|
44
44
|
const SupportWindow = (() => {
|
|
45
|
-
const Component = React$1.forwardRef(({ isOpen: isOpenProp, onOpenChange, children, className, asChild = false, closeOnEscape = true, trapFocus = true, restoreFocus = true, id = "cossistant-window"
|
|
45
|
+
const Component = React$1.forwardRef(({ isOpen: isOpenProp, onOpenChange, children, className, asChild = false, closeOnEscape = true, trapFocus = true, restoreFocus = true, id = "cossistant-window", ...props }, ref) => {
|
|
46
46
|
const { isOpen, close } = useSupportConfig();
|
|
47
47
|
const containerRef = React$1.useRef(null);
|
|
48
48
|
const previouslyFocusedRef = React$1.useRef(null);
|
package/primitives/window.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"window.js","names":["React","renderProps: WindowRenderProps"],"sources":["../../src/primitives/window.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { useSupportConfig } from \"../support/store/support-store\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\nexport type WindowRenderProps = {\n\tisOpen: boolean;\n\tclose: () => void;\n};\n\nexport type WindowProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tisOpen?: boolean;\n\tonOpenChange?: (open: boolean) => void;\n\tchildren?: React.ReactNode | ((props: WindowRenderProps) => React.ReactNode);\n\tasChild?: boolean;\n\tcloseOnEscape?: boolean;\n\t/**\n\t * Whether to trap focus within the dialog when open.\n\t * @default true\n\t */\n\ttrapFocus?: boolean;\n\t/**\n\t * Whether to restore focus to the previously focused element when closing.\n\t * @default true\n\t */\n\trestoreFocus?: boolean;\n\tid?: string;\n};\n\n/**\n * Selector for focusable elements within a container\n */\nconst FOCUSABLE_SELECTOR = [\n\t\"a[href]\",\n\t\"area[href]\",\n\t\"input:not([disabled])\",\n\t\"select:not([disabled])\",\n\t\"textarea:not([disabled])\",\n\t\"button:not([disabled])\",\n\t\"iframe\",\n\t\"object\",\n\t\"embed\",\n\t\"[tabindex]:not([tabindex='-1'])\",\n\t\"[contenteditable]\",\n\t\"audio[controls]\",\n\t\"video[controls]\",\n].join(\",\");\n\n/**\n * Get all focusable elements within a container\n */\nfunction getFocusableElements(container: HTMLElement): HTMLElement[] {\n\treturn Array.from(\n\t\tcontainer.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)\n\t).filter((el) => {\n\t\t// Check visibility\n\t\tconst style = window.getComputedStyle(el);\n\t\treturn style.display !== \"none\" && style.visibility !== \"hidden\";\n\t});\n}\n\n/**\n * Dialog container with open/close state, escape key handling,\n * focus trap, and focus restoration.\n *\n * @example\n * <Window isOpen={isOpen} onOpenChange={setOpen}>\n * {({ isOpen, close }) => (\n * <div>Content here</div>\n * )}\n * </Window>\n */\nexport const SupportWindow = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, WindowProps>(\n\t\t(\n\t\t\t{\n\t\t\t\tisOpen: isOpenProp,\n\t\t\t\tonOpenChange,\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\tcloseOnEscape = true,\n\t\t\t\ttrapFocus = true,\n\t\t\t\trestoreFocus = true,\n\t\t\t\tid = \"cossistant-window\",\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst { isOpen, close } = useSupportConfig();\n\t\t\tconst containerRef = React.useRef<HTMLDivElement>(null);\n\t\t\tconst previouslyFocusedRef = React.useRef<HTMLElement | null>(null);\n\n\t\t\tconst open = isOpenProp ?? isOpen ?? false;\n\n\t\t\tconst closeFn = React.useCallback(() => {\n\t\t\t\tif (onOpenChange) {\n\t\t\t\t\tonOpenChange(false);\n\t\t\t\t} else if (close) {\n\t\t\t\t\tclose();\n\t\t\t\t}\n\t\t\t}, [onOpenChange, close]);\n\n\t\t\t// Store previously focused element and focus first element when opening\n\t\t\tReact.useEffect(() => {\n\t\t\t\tif (open) {\n\t\t\t\t\t// Store the currently focused element\n\t\t\t\t\tpreviouslyFocusedRef.current = document.activeElement as HTMLElement;\n\n\t\t\t\t\t// Focus the first focusable element after a short delay\n\t\t\t\t\t// to allow the DOM to render\n\t\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\t\tconst container = containerRef.current;\n\t\t\t\t\t\tif (container) {\n\t\t\t\t\t\t\tconst focusable = getFocusableElements(container);\n\t\t\t\t\t\t\tconst firstElement = focusable[0];\n\t\t\t\t\t\t\tif (firstElement) {\n\t\t\t\t\t\t\t\tfirstElement.focus();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// If no focusable elements, focus the container itself\n\t\t\t\t\t\t\t\tcontainer.focus();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}, 50);\n\n\t\t\t\t\treturn () => clearTimeout(timer);\n\t\t\t\t}\n\t\t\t\t// Restore focus when closing\n\t\t\t\tif (!open && restoreFocus && previouslyFocusedRef.current) {\n\t\t\t\t\tpreviouslyFocusedRef.current.focus();\n\t\t\t\t\tpreviouslyFocusedRef.current = null;\n\t\t\t\t}\n\t\t\t}, [open, restoreFocus]);\n\n\t\t\t// Close on Escape\n\t\t\tReact.useEffect(() => {\n\t\t\t\tif (!(open && closeOnEscape)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst onKey = (e: KeyboardEvent) => {\n\t\t\t\t\tif (e.key === \"Escape\") {\n\t\t\t\t\t\tcloseFn();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\twindow.addEventListener(\"keydown\", onKey);\n\t\t\t\treturn () => window.removeEventListener(\"keydown\", onKey);\n\t\t\t}, [open, closeFn, closeOnEscape]);\n\n\t\t\t// Focus trap - trap Tab and Shift+Tab within the dialog\n\t\t\tReact.useEffect(() => {\n\t\t\t\tif (!(open && trapFocus)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst container = containerRef.current;\n\t\t\t\tif (!container) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\t\t\t\tif (e.key !== \"Tab\") {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst focusable = getFocusableElements(container);\n\t\t\t\t\tif (focusable.length === 0) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst first = focusable[0];\n\t\t\t\t\tconst last = focusable.at(-1);\n\t\t\t\t\tconst active = document.activeElement;\n\n\t\t\t\t\t// Shift+Tab from first element wraps to last\n\t\t\t\t\tif (e.shiftKey && active === first) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tlast?.focus();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Tab from last element wraps to first\n\t\t\t\t\tif (!e.shiftKey && active === last) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tfirst?.focus();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// If focus is outside the container, bring it back\n\t\t\t\t\tif (!container.contains(active)) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tfirst?.focus();\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tdocument.addEventListener(\"keydown\", handleKeyDown);\n\t\t\t\treturn () => document.removeEventListener(\"keydown\", handleKeyDown);\n\t\t\t}, [open, trapFocus]);\n\n\t\t\t// Merge refs\n\t\t\tconst mergedRef = React.useCallback(\n\t\t\t\t(node: HTMLDivElement | null) => {\n\t\t\t\t\tcontainerRef.current = node;\n\t\t\t\t\tif (typeof ref === \"function\") {\n\t\t\t\t\t\tref(node);\n\t\t\t\t\t} else if (ref) {\n\t\t\t\t\t\tref.current = node;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t[ref]\n\t\t\t);\n\n\t\t\tconst renderProps: WindowRenderProps = { isOpen: open, close: closeFn };\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref: mergedRef,\n\t\t\t\t\tstate: renderProps,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\trole: \"dialog\",\n\t\t\t\t\t\t\"aria-modal\": \"true\",\n\t\t\t\t\t\tid,\n\t\t\t\t\t\ttabIndex: -1, // Allow container to receive focus\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t\tenabled: open,\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"SupportWindow\";\n\treturn Component;\n})();\n"],"mappings":";;;;;;;;AAkCA,MAAM,qBAAqB;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,CAAC,KAAK,IAAI;;;;AAKX,SAAS,qBAAqB,WAAuC;AACpE,QAAO,MAAM,KACZ,UAAU,iBAA8B,mBAAmB,CAC3D,CAAC,QAAQ,OAAO;EAEhB,MAAM,QAAQ,OAAO,iBAAiB,GAAG;AACzC,SAAO,MAAM,YAAY,UAAU,MAAM,eAAe;GACvD;;;;;;;;;;;;;AAcH,MAAa,uBAAuB;CACnC,MAAM,YAAYA,QAAM,YAEtB,EACC,QAAQ,YACR,cACA,UACA,WACA,UAAU,OACV,gBAAgB,MAChB,YAAY,MACZ,eAAe,MACf,KAAK,
|
|
1
|
+
{"version":3,"file":"window.js","names":["React","renderProps: WindowRenderProps"],"sources":["../../src/primitives/window.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { useSupportConfig } from \"../support/store/support-store\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\nexport type WindowRenderProps = {\n\tisOpen: boolean;\n\tclose: () => void;\n};\n\nexport type WindowProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tisOpen?: boolean;\n\tonOpenChange?: (open: boolean) => void;\n\tchildren?: React.ReactNode | ((props: WindowRenderProps) => React.ReactNode);\n\tasChild?: boolean;\n\tcloseOnEscape?: boolean;\n\t/**\n\t * Whether to trap focus within the dialog when open.\n\t * @default true\n\t */\n\ttrapFocus?: boolean;\n\t/**\n\t * Whether to restore focus to the previously focused element when closing.\n\t * @default true\n\t */\n\trestoreFocus?: boolean;\n\tid?: string;\n};\n\n/**\n * Selector for focusable elements within a container\n */\nconst FOCUSABLE_SELECTOR = [\n\t\"a[href]\",\n\t\"area[href]\",\n\t\"input:not([disabled])\",\n\t\"select:not([disabled])\",\n\t\"textarea:not([disabled])\",\n\t\"button:not([disabled])\",\n\t\"iframe\",\n\t\"object\",\n\t\"embed\",\n\t\"[tabindex]:not([tabindex='-1'])\",\n\t\"[contenteditable]\",\n\t\"audio[controls]\",\n\t\"video[controls]\",\n].join(\",\");\n\n/**\n * Get all focusable elements within a container\n */\nfunction getFocusableElements(container: HTMLElement): HTMLElement[] {\n\treturn Array.from(\n\t\tcontainer.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)\n\t).filter((el) => {\n\t\t// Check visibility\n\t\tconst style = window.getComputedStyle(el);\n\t\treturn style.display !== \"none\" && style.visibility !== \"hidden\";\n\t});\n}\n\n/**\n * Dialog container with open/close state, escape key handling,\n * focus trap, and focus restoration.\n *\n * @example\n * <Window isOpen={isOpen} onOpenChange={setOpen}>\n * {({ isOpen, close }) => (\n * <div>Content here</div>\n * )}\n * </Window>\n */\nexport const SupportWindow = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, WindowProps>(\n\t\t(\n\t\t\t{\n\t\t\t\tisOpen: isOpenProp,\n\t\t\t\tonOpenChange,\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\tcloseOnEscape = true,\n\t\t\t\ttrapFocus = true,\n\t\t\t\trestoreFocus = true,\n\t\t\t\tid = \"cossistant-window\",\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst { isOpen, close } = useSupportConfig();\n\t\t\tconst containerRef = React.useRef<HTMLDivElement>(null);\n\t\t\tconst previouslyFocusedRef = React.useRef<HTMLElement | null>(null);\n\n\t\t\tconst open = isOpenProp ?? isOpen ?? false;\n\n\t\t\tconst closeFn = React.useCallback(() => {\n\t\t\t\tif (onOpenChange) {\n\t\t\t\t\tonOpenChange(false);\n\t\t\t\t} else if (close) {\n\t\t\t\t\tclose();\n\t\t\t\t}\n\t\t\t}, [onOpenChange, close]);\n\n\t\t\t// Store previously focused element and focus first element when opening\n\t\t\tReact.useEffect(() => {\n\t\t\t\tif (open) {\n\t\t\t\t\t// Store the currently focused element\n\t\t\t\t\tpreviouslyFocusedRef.current = document.activeElement as HTMLElement;\n\n\t\t\t\t\t// Focus the first focusable element after a short delay\n\t\t\t\t\t// to allow the DOM to render\n\t\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\t\tconst container = containerRef.current;\n\t\t\t\t\t\tif (container) {\n\t\t\t\t\t\t\tconst focusable = getFocusableElements(container);\n\t\t\t\t\t\t\tconst firstElement = focusable[0];\n\t\t\t\t\t\t\tif (firstElement) {\n\t\t\t\t\t\t\t\tfirstElement.focus();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// If no focusable elements, focus the container itself\n\t\t\t\t\t\t\t\tcontainer.focus();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}, 50);\n\n\t\t\t\t\treturn () => clearTimeout(timer);\n\t\t\t\t}\n\t\t\t\t// Restore focus when closing\n\t\t\t\tif (!open && restoreFocus && previouslyFocusedRef.current) {\n\t\t\t\t\tpreviouslyFocusedRef.current.focus();\n\t\t\t\t\tpreviouslyFocusedRef.current = null;\n\t\t\t\t}\n\t\t\t}, [open, restoreFocus]);\n\n\t\t\t// Close on Escape\n\t\t\tReact.useEffect(() => {\n\t\t\t\tif (!(open && closeOnEscape)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst onKey = (e: KeyboardEvent) => {\n\t\t\t\t\tif (e.key === \"Escape\") {\n\t\t\t\t\t\tcloseFn();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\twindow.addEventListener(\"keydown\", onKey);\n\t\t\t\treturn () => window.removeEventListener(\"keydown\", onKey);\n\t\t\t}, [open, closeFn, closeOnEscape]);\n\n\t\t\t// Focus trap - trap Tab and Shift+Tab within the dialog\n\t\t\tReact.useEffect(() => {\n\t\t\t\tif (!(open && trapFocus)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst container = containerRef.current;\n\t\t\t\tif (!container) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\t\t\t\tif (e.key !== \"Tab\") {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst focusable = getFocusableElements(container);\n\t\t\t\t\tif (focusable.length === 0) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst first = focusable[0];\n\t\t\t\t\tconst last = focusable.at(-1);\n\t\t\t\t\tconst active = document.activeElement;\n\n\t\t\t\t\t// Shift+Tab from first element wraps to last\n\t\t\t\t\tif (e.shiftKey && active === first) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tlast?.focus();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Tab from last element wraps to first\n\t\t\t\t\tif (!e.shiftKey && active === last) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tfirst?.focus();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// If focus is outside the container, bring it back\n\t\t\t\t\tif (!container.contains(active)) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tfirst?.focus();\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tdocument.addEventListener(\"keydown\", handleKeyDown);\n\t\t\t\treturn () => document.removeEventListener(\"keydown\", handleKeyDown);\n\t\t\t}, [open, trapFocus]);\n\n\t\t\t// Merge refs\n\t\t\tconst mergedRef = React.useCallback(\n\t\t\t\t(node: HTMLDivElement | null) => {\n\t\t\t\t\tcontainerRef.current = node;\n\t\t\t\t\tif (typeof ref === \"function\") {\n\t\t\t\t\t\tref(node);\n\t\t\t\t\t} else if (ref) {\n\t\t\t\t\t\tref.current = node;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t[ref]\n\t\t\t);\n\n\t\t\tconst renderProps: WindowRenderProps = { isOpen: open, close: closeFn };\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref: mergedRef,\n\t\t\t\t\tstate: renderProps,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\trole: \"dialog\",\n\t\t\t\t\t\t\"aria-modal\": \"true\",\n\t\t\t\t\t\tid,\n\t\t\t\t\t\ttabIndex: -1, // Allow container to receive focus\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t\tenabled: open,\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"SupportWindow\";\n\treturn Component;\n})();\n"],"mappings":"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"}
|
package/provider.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ConfigurationError } from "./hooks/private/use-rest-client.js";
|
|
1
2
|
import React from "react";
|
|
2
3
|
import { CossistantClient } from "@cossistant/core";
|
|
3
4
|
import { DefaultMessage, PublicWebsiteResponse } from "@cossistant/types";
|
|
@@ -28,7 +29,8 @@ type CossistantContextValue = {
|
|
|
28
29
|
setUnreadCount: (count: number) => void;
|
|
29
30
|
isLoading: boolean;
|
|
30
31
|
error: Error | null;
|
|
31
|
-
|
|
32
|
+
configurationError: ConfigurationError | null;
|
|
33
|
+
client: CossistantClient | null;
|
|
32
34
|
isOpen: boolean;
|
|
33
35
|
open: () => void;
|
|
34
36
|
close: () => void;
|
|
@@ -71,5 +73,5 @@ declare function SupportProvider({
|
|
|
71
73
|
*/
|
|
72
74
|
declare function useSupport(): UseSupportValue;
|
|
73
75
|
//#endregion
|
|
74
|
-
export { CossistantContextValue, CossistantProviderProps, SupportContext, SupportProvider, SupportProviderProps, UseSupportValue, useSupport };
|
|
76
|
+
export { type ConfigurationError, CossistantContextValue, CossistantProviderProps, SupportContext, SupportProvider, SupportProviderProps, UseSupportValue, useSupport };
|
|
75
77
|
//# sourceMappingURL=provider.d.ts.map
|
package/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","names":[],"sources":["../src/provider.tsx"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"provider.d.ts","names":[],"sources":["../src/provider.tsx"],"sourcesContent":[],"mappings":";;;;;;KAsEY,oBAAA;YACD,KAAA,CAAM;EADL,WAAA,CAAA,EAAA,OAAA;EACD,MAAM,CAAA,EAAA,MAAA;EAKE,KAAA,CAAA,EAAA,MAAA;EAKE,SAAA,CAAA,EAAA,MAAA;EAAK,eAAA,CAAA,EALP,cAKO,EAAA;EAId,YAAA,CAAA,EAAA,MAAA,EAAA;EAEA,WAAA,CAAA,EAAA,OAAA;EACF,WAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EACQ,cAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAEc,SAAA,CAAA,EAAA,CAAA,KAAA,EAVX,KAUW,EAAA,GAAA,IAAA;EAKxB,IAAA,CAAA,EAAA,QAAA,GAAA,QAAA;CACa;AACZ,KAbG,uBAAA,GAA0B,oBAa7B;AAAgB,KAXb,sBAAA,GAWa;EAOpB,OAAA,EAjBK,qBAiBqB,GAAA,IAAA;EAE1B,eAAA,EAlBa,cAkBI,EAAA;EAAG,YAAA,EAAA,MAAA,EAAA;EAEV,kBAAA,EAAA,CAAA,QAAA,EAlBiB,cAkBjB,EAAA,EAAA,GAAA,IAAA;EAAZ,eAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,EAAA,GAAA,IAAA;EAAW,WAAA,EAAA,MAAA;EA6CF,cAAA,EAAA,CAAA,KAAe,EAAA,MAAA,EAAA,GAAA,IAAA;EAAG,SAAA,EAAA,OAAA;EACK,KAAA,EA3D3B,KA2D2B,GAAA,IAAA;EAAZ,kBAAA,EA1DF,kBA0DE,GAAA,IAAA;EACS,MAAA,EA1DvB,gBA0DuB,GAAA,IAAA;EAAZ,MAAA,EAAA,OAAA;EACT,IAAA,EAAA,GAAA,GAAA,IAAA;EAAiB,KAAA,EAAA,GAAA,GAAA,IAAA;EAIf,MAAA,EAAA,GAAA,GAAA,IAED;AAqVZ,CAAA;KA/YK,WAAA,GAAc,WAgZlB,CAhZ8B,sBAgZ9B,CAAA,SAAA,CAAA,CAAA;KA9YI,iBAAA,GAAoB,WA+YxB,CAAA,SAAA,CAAA,SAAA,IAAA,GAAA,SAAA,GAAA,SAAA,GA7YE,WA6YF,CA7Yc,WA6Yd,CAAA,SAAA,CAAA,CAAA,GAAA;EACA,MAAA,EAAA,MAAA,GAAA,IAAA;CACA;AACA,KAnWW,eAAA,GAAkB,sBAmW7B,GAAA;EACA,oBAAA,EAnWsB,WAmWtB,CAnWkC,WAmWlC,CAAA,sBAAA,CAAA,CAAA,GAAA,EAAA;EACA,iBAAA,EAnWmB,WAmWnB,CAnW+B,WAmW/B,CAAA,mBAAA,CAAA,CAAA,GAAA,EAAA;EACA,OAAA,CAAA,EAnWU,iBAmWV;EACA,IAAA,EAAA,QAAA,GAAA,QAAA;CACA;AACA,cAlWY,cAkWZ,EAlW0B,KAAA,CAAA,OAkW1B,CAlW0B,sBAkW1B,GAAA,SAAA,CAAA;;;;;AA0BD;;iBArCgB,eAAA;;;;;;;;;;;;;GAab,uBAAuB,KAAA,CAAM;;;;;iBAwBhB,UAAA,CAAA,GAAc"}
|