@cratis/components 4.20.0 → 4.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compat-manifest.json +11 -11
- package/dist/esm/Chat/ChatConversation.d.ts +8 -1
- package/dist/esm/Chat/ChatConversation.d.ts.map +1 -1
- package/dist/esm/Chat/ChatConversation.js +2 -2
- package/dist/esm/Chat/ChatConversation.js.map +1 -1
- package/dist/esm/Chat/ChatSidebar.d.ts +9 -2
- package/dist/esm/Chat/ChatSidebar.d.ts.map +1 -1
- package/dist/esm/Chat/ChatSidebar.js +2 -2
- package/dist/esm/Chat/ChatSidebar.js.map +1 -1
- package/dist/esm/Common/ActionMenubar.d.ts.map +1 -1
- package/dist/esm/Common/ActionMenubar.js +11 -6
- package/dist/esm/Common/ActionMenubar.js.map +1 -1
- package/dist/esm/Common/ToolbarFocusMode.d.ts +9 -1
- package/dist/esm/Common/ToolbarFocusMode.d.ts.map +1 -1
- package/dist/esm/Common/ToolbarFocusMode.js +1 -0
- package/dist/esm/Common/ToolbarFocusMode.js.map +1 -1
- package/dist/esm/Common/ToolbarRovingContext.d.ts +39 -0
- package/dist/esm/Common/ToolbarRovingContext.d.ts.map +1 -0
- package/dist/esm/Common/ToolbarRovingContext.js +40 -0
- package/dist/esm/Common/ToolbarRovingContext.js.map +1 -0
- package/dist/esm/Common/useToolbarKeyboardNavigation.d.ts +2 -0
- package/dist/esm/Common/useToolbarKeyboardNavigation.d.ts.map +1 -1
- package/dist/esm/Common/useToolbarKeyboardNavigation.js +164 -15
- package/dist/esm/Common/useToolbarKeyboardNavigation.js.map +1 -1
- package/dist/esm/DataTables/DataTableCore.d.ts +19 -1
- package/dist/esm/DataTables/DataTableCore.d.ts.map +1 -1
- package/dist/esm/DataTables/DataTableCore.js +19 -14
- package/dist/esm/DataTables/DataTableCore.js.map +1 -1
- package/dist/esm/DataTables/DataTableRowProcessing.d.ts +11 -0
- package/dist/esm/DataTables/DataTableRowProcessing.d.ts.map +1 -0
- package/dist/esm/DataTables/DataTableRowProcessing.js +8 -0
- package/dist/esm/DataTables/DataTableRowProcessing.js.map +1 -0
- package/dist/esm/DataTables/DataTableSort.d.ts +9 -0
- package/dist/esm/DataTables/DataTableSort.d.ts.map +1 -0
- package/dist/esm/DataTables/DataTableSort.js +4 -0
- package/dist/esm/DataTables/DataTableSort.js.map +1 -0
- package/dist/esm/DataTables/DataTableSortDirection.d.ts +8 -0
- package/dist/esm/DataTables/DataTableSortDirection.d.ts.map +1 -0
- package/dist/esm/DataTables/DataTableSortDirection.js +8 -0
- package/dist/esm/DataTables/DataTableSortDirection.js.map +1 -0
- package/dist/esm/DataTables/index.d.ts +3 -0
- package/dist/esm/DataTables/index.d.ts.map +1 -1
- package/dist/esm/DataTables/index.js +2 -0
- package/dist/esm/DataTables/index.js.map +1 -1
- package/dist/esm/DataTables/useControllableState.d.ts +10 -0
- package/dist/esm/DataTables/useControllableState.d.ts.map +1 -0
- package/dist/esm/DataTables/useControllableState.js +15 -0
- package/dist/esm/DataTables/useControllableState.js.map +1 -0
- package/dist/esm/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/esm/Toolbar/Toolbar.js +3 -2
- package/dist/esm/Toolbar/Toolbar.js.map +1 -1
- package/dist/esm/Toolbar/ToolbarButton.d.ts.map +1 -1
- package/dist/esm/Toolbar/ToolbarButton.js +4 -2
- package/dist/esm/Toolbar/ToolbarButton.js.map +1 -1
- package/dist/esm/Toolbar/ToolbarFanOutItem.d.ts.map +1 -1
- package/dist/esm/Toolbar/ToolbarFanOutItem.js +10 -2
- package/dist/esm/Toolbar/ToolbarFanOutItem.js.map +1 -1
- package/dist/esm/Toolbar/ToolbarFolder.d.ts.map +1 -1
- package/dist/esm/Toolbar/ToolbarFolder.js +10 -2
- package/dist/esm/Toolbar/ToolbarFolder.js.map +1 -1
- package/package.json +5 -2
package/compat-manifest.json
CHANGED
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
{
|
|
69
69
|
"name": "@cratis/components",
|
|
70
70
|
"role": "core",
|
|
71
|
-
"version": "4.
|
|
71
|
+
"version": "4.21.0",
|
|
72
72
|
"releaseMajorRange": ">=4 <5",
|
|
73
73
|
"independentRelease": false,
|
|
74
74
|
"private": false,
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
{
|
|
93
93
|
"name": "@cratis/eslint-plugin-components",
|
|
94
94
|
"role": "eslint",
|
|
95
|
-
"version": "4.
|
|
95
|
+
"version": "4.21.0",
|
|
96
96
|
"releaseMajorRange": ">=4 <5",
|
|
97
97
|
"independentRelease": false,
|
|
98
98
|
"private": false,
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
{
|
|
106
106
|
"name": "@cratis/components.migrator",
|
|
107
107
|
"role": "migrator",
|
|
108
|
-
"version": "4.
|
|
108
|
+
"version": "4.21.0",
|
|
109
109
|
"releaseMajorRange": ">=4 <5",
|
|
110
110
|
"independentRelease": false,
|
|
111
111
|
"private": false,
|
|
@@ -116,13 +116,13 @@
|
|
|
116
116
|
{
|
|
117
117
|
"name": "@cratis/components.conformance",
|
|
118
118
|
"role": "conformance",
|
|
119
|
-
"version": "4.
|
|
119
|
+
"version": "4.21.0",
|
|
120
120
|
"releaseMajorRange": ">=4 <5",
|
|
121
121
|
"independentRelease": false,
|
|
122
122
|
"private": false,
|
|
123
123
|
"packageAccess": "public",
|
|
124
124
|
"peerDependencies": {
|
|
125
|
-
"@cratis/components": "4.
|
|
125
|
+
"@cratis/components": "4.21.0",
|
|
126
126
|
"react": "^19.0.0",
|
|
127
127
|
"react-dom": "^19.0.0"
|
|
128
128
|
},
|
|
@@ -132,13 +132,13 @@
|
|
|
132
132
|
{
|
|
133
133
|
"name": "@cratis/components.mui",
|
|
134
134
|
"role": "renderer-adapter",
|
|
135
|
-
"version": "4.
|
|
135
|
+
"version": "4.21.0",
|
|
136
136
|
"releaseMajorRange": ">=4 <5",
|
|
137
137
|
"independentRelease": false,
|
|
138
138
|
"private": false,
|
|
139
139
|
"packageAccess": "public",
|
|
140
140
|
"peerDependencies": {
|
|
141
|
-
"@cratis/components": "4.
|
|
141
|
+
"@cratis/components": "4.21.0",
|
|
142
142
|
"@emotion/react": ">=11.5 <12",
|
|
143
143
|
"@emotion/styled": ">=11.11 <12",
|
|
144
144
|
"@mui/material": ">=9 <10",
|
|
@@ -188,13 +188,13 @@
|
|
|
188
188
|
{
|
|
189
189
|
"name": "@cratis/components.primereact",
|
|
190
190
|
"role": "renderer-adapter",
|
|
191
|
-
"version": "4.
|
|
191
|
+
"version": "4.21.0",
|
|
192
192
|
"releaseMajorRange": ">=4 <5",
|
|
193
193
|
"independentRelease": false,
|
|
194
194
|
"private": false,
|
|
195
195
|
"packageAccess": "public",
|
|
196
196
|
"peerDependencies": {
|
|
197
|
-
"@cratis/components": "4.
|
|
197
|
+
"@cratis/components": "4.21.0",
|
|
198
198
|
"@primereact/core": ">=11 <12",
|
|
199
199
|
"@primereact/ui": ">=11 <12",
|
|
200
200
|
"@primeuix/themes": ">=3 <4",
|
|
@@ -256,13 +256,13 @@
|
|
|
256
256
|
{
|
|
257
257
|
"name": "@cratis/components.primereact10",
|
|
258
258
|
"role": "renderer-adapter",
|
|
259
|
-
"version": "4.
|
|
259
|
+
"version": "4.21.0",
|
|
260
260
|
"releaseMajorRange": ">=4 <5",
|
|
261
261
|
"independentRelease": false,
|
|
262
262
|
"private": false,
|
|
263
263
|
"packageAccess": "public",
|
|
264
264
|
"peerDependencies": {
|
|
265
|
-
"@cratis/components": "4.
|
|
265
|
+
"@cratis/components": "4.21.0",
|
|
266
266
|
"primereact": ">=10.9.9 <11",
|
|
267
267
|
"react": "^19.0.0",
|
|
268
268
|
"react-dom": "^19.0.0"
|
|
@@ -76,6 +76,13 @@ export interface ChatConversationProps<TMessage extends ChatMessage = ChatMessag
|
|
|
76
76
|
* @returns What to render.
|
|
77
77
|
*/
|
|
78
78
|
renderAuthorName?: (authorId: ChatIdentifier, author: ChatAuthor) => ReactNode;
|
|
79
|
+
/**
|
|
80
|
+
* Renders host content under a message's body, for example reactions or a failed-reply notice.
|
|
81
|
+
* Nothing extra is rendered when it returns null or undefined.
|
|
82
|
+
* @param message The message being rendered.
|
|
83
|
+
* @returns What to render under the message body.
|
|
84
|
+
*/
|
|
85
|
+
renderMessageExtra?: (message: TMessage) => ReactNode;
|
|
79
86
|
/** The host's own actions, offered as buttons on every message each is available for. */
|
|
80
87
|
actions?: ChatMessageAction<TMessage>[];
|
|
81
88
|
/** Everyone who can be mentioned, when the host already holds the list. Omit (together with
|
|
@@ -111,5 +118,5 @@ export interface ChatConversationProps<TMessage extends ChatMessage = ChatMessag
|
|
|
111
118
|
* {@link ChatConversationProps.onSendMessage | onSendMessage} without any opinion about the
|
|
112
119
|
* backend behind it. Mentions and the emoji picker come along through the composer.
|
|
113
120
|
*/
|
|
114
|
-
export declare const ChatConversation: <TMessage extends ChatMessage = ChatMessage>({ messages, status, onSendMessage, authorOf, renderAvatar, renderAuthorName, actions, mentionCandidates, resolveMentionCandidates, typingAuthors, quickReply, buildAvatarUrl, autoFocus, labels, className, }: ChatConversationProps<TMessage>) => import("react").JSX.Element;
|
|
121
|
+
export declare const ChatConversation: <TMessage extends ChatMessage = ChatMessage>({ messages, status, onSendMessage, authorOf, renderAvatar, renderAuthorName, renderMessageExtra, actions, mentionCandidates, resolveMentionCandidates, typingAuthors, quickReply, buildAvatarUrl, autoFocus, labels, className, }: ChatConversationProps<TMessage>) => import("react").JSX.Element;
|
|
115
122
|
//# sourceMappingURL=ChatConversation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatConversation.d.ts","sourceRoot":"","sources":["../../../Chat/ChatConversation.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ChatConversation.d.ts","sourceRoot":"","sources":["../../../Chat/ChatConversation.tsx"],"names":[],"mappings":"AAGA,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACnE,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,uBAAc,CAAC;AAE7E,OAAO,EAGH,KAAK,kBAAkB,EAC1B,MAAM,uBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAc,CAAC;AAC/C,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,qBAAkB,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAe,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAe,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAqB,CAAC;AAE7D,OAAO,EAAqB,KAAK,uBAAuB,EAAE,MAAM,wBAAqB,CAAC;AAiBtF;;qEAEqE;AACrE,MAAM,WAAW,sBAAsB;IACnC,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,oHAAoH;IACpH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,mIAAmI;IACnI,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,yFAAyF;IACzF,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,iHAAiH;IACjH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,qHAAqH;IACrH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,uBAAuB,CAAC;IAErC,wCAAwC;IACxC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW;IAC7E;;;OAGG;IACH,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAErB,iKAAiK;IACjK,MAAM,CAAC,EAAE,UAAU,CAAC;IAEpB;;;;;OAKG;IACH,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;IAE/D;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,UAAU,CAAC;IAEpD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,KAAK,SAAS,CAAC;IAE3E;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,KAAK,SAAS,CAAC;IAE/E;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,CAAC;IAEtD,yFAAyF;IACzF,OAAO,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;IAExC;kEAC8D;IAC9D,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAEvC;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,CACvB,KAAK,EAAE,MAAM,KACZ,gBAAgB,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAEtD,0FAA0F;IAC1F,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAEnC;sCACkC;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,wFAAwF;IACxF,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,MAAM,CAAC;IAE1D,qDAAqD;IACrD,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAEhC,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AA2DD;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,SAAS,WAAW,GAAG,WAAW,qOAiBxE,qBAAqB,CAAC,QAAQ,CAAC,gCAgLjC,CAAC"}
|
|
@@ -39,7 +39,7 @@ const typingLabel = (authors, labels) => {
|
|
|
39
39
|
.replace('{second}', authors[1].name);
|
|
40
40
|
return labels?.typingSeveral ?? 'Several people are typing';
|
|
41
41
|
};
|
|
42
|
-
const ChatConversation = ({ messages, status = ChatStatus.Ready, onSendMessage, authorOf, renderAvatar, renderAuthorName, actions, mentionCandidates, resolveMentionCandidates, typingAuthors = [], quickReply = true, buildAvatarUrl, autoFocus = false, labels, className, }) => {
|
|
42
|
+
const ChatConversation = ({ messages, status = ChatStatus.Ready, onSendMessage, authorOf, renderAvatar, renderAuthorName, renderMessageExtra, actions, mentionCandidates, resolveMentionCandidates, typingAuthors = [], quickReply = true, buildAvatarUrl, autoFocus = false, labels, className, }) => {
|
|
43
43
|
const composerRef = useRef(null);
|
|
44
44
|
const messagesEndRef = useRef(null);
|
|
45
45
|
const renderInfo = useMemo(() => computeRenderInfo(messages), [messages]);
|
|
@@ -67,7 +67,7 @@ const ChatConversation = ({ messages, status = ChatStatus.Ready, onSendMessage,
|
|
|
67
67
|
? renderAuthorName(message.authorId, author)
|
|
68
68
|
: author.name })] })), jsxs("div", { className: 'cratis-chat-message__content', children: [jsxs("div", { className: `cratis-chat-message__panel${showActionsRow ? ' cratis-chat-message__panel--with-actions' : ''}`, children: [jsx(ChatMessageBody, { body: message.body, mentions: message.mentions }), showActionsRow && (jsxs("div", { className: 'cratis-chat-message__actions', children: [quickReply && (jsx("button", { type: 'button', className: 'cratis-chat-message__action', title: (labels?.quickReplyTo ??
|
|
69
69
|
'Reply to {name}').replace('{name}', author.name), "aria-label": (labels?.quickReplyTo ??
|
|
70
|
-
'Reply to {name}').replace('{name}', author.name), onClick: () => quickReplyTo(author.name), children: jsx(ReplyIcon, {}) })), availableActions.map((action) => (jsx("button", { type: 'button', className: 'cratis-chat-message__action', title: action.label, "aria-label": action.label, onClick: () => action.onInvoke(message), children: typeof action.icon === 'string' ? (jsx("i", { className: action.icon, "aria-hidden": 'true' })) : (action.icon) }, action.id)))] }))] }), showTimestamp && (jsx("span", { className: 'cratis-chat-message__time', children: relativeTimestamp(timestampOf(message), new Date(), labels?.timestamps) }))] })] }, chatIdentifierString(message.id)));
|
|
70
|
+
'Reply to {name}').replace('{name}', author.name), onClick: () => quickReplyTo(author.name), children: jsx(ReplyIcon, {}) })), availableActions.map((action) => (jsx("button", { type: 'button', className: 'cratis-chat-message__action', title: action.label, "aria-label": action.label, onClick: () => action.onInvoke(message), children: typeof action.icon === 'string' ? (jsx("i", { className: action.icon, "aria-hidden": 'true' })) : (action.icon) }, action.id)))] }))] }), renderMessageExtra?.(message), showTimestamp && (jsx("span", { className: 'cratis-chat-message__time', children: relativeTimestamp(timestampOf(message), new Date(), labels?.timestamps) }))] })] }, chatIdentifierString(message.id)));
|
|
71
71
|
}), status !== ChatStatus.Unauthorized && (jsx(TypingIndicator, { authors: typingAuthors, label: typingLabel(typingAuthors, labels), buildAvatarUrl: buildAvatarUrl })), jsx("div", { ref: messagesEndRef })] }), jsx(ChatComposer, { ref: composerRef, mentionCandidates: mentionCandidates, resolveMentionCandidates: resolveMentionCandidates, onSend: send, disabled: status === ChatStatus.Unauthorized, autoFocus: autoFocus, buildAvatarUrl: buildAvatarUrl, labels: labels?.composer })] }));
|
|
72
72
|
};
|
|
73
73
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatConversation.js","sources":["../../../Chat/ChatConversation.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useEffect, useMemo, useRef, type ReactNode } from 'react';\nimport { PersonAvatarCircle, type BuildAvatarUrlParams } from './Kit/Avatar';\nimport { ChatAuthorKind } from './Kit/ChatAuthorKind';\nimport {\n ChatComposer,\n type ChatComposerHandle,\n type ChatComposerLabels,\n} from './Kit/ChatComposer';\nimport type { ChatTypingAuthor } from './Kit/ChatTypingAuthor';\nimport type { MentionCandidate } from './Kit/Mentions';\nimport { TypingIndicator } from './Kit/TypingIndicator';\nimport type { ChatAuthor } from './ChatAuthor';\nimport { chatIdentifierString, type ChatIdentifier } from './ChatIdentifier';\nimport type { ChatMention } from './ChatMention';\nimport type { ChatMessage } from './ChatMessage';\nimport { ChatStatus } from './ChatStatus';\nimport type { ChatMessageAction } from './ChatMessageAction';\nimport { ChatMessageBody } from './ChatMessageBody';\nimport { relativeTimestamp, type RelativeTimestampLabels } from './relativeTimestamp';\n\nconst ReplyIcon = () => (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2.5'\n aria-hidden='true'\n >\n <polyline points='9 17 4 12 9 7' />\n <path d='M4 12h10a6 6 0 0 1 6 6v1' />\n </svg>\n);\n\n/** Overrides for every label the {@link ChatConversation} renders. Any field left unset falls\n * back to a literal English default — this library ships no i18n mechanism of its own, so a host\n * that localizes passes its own translated strings through here. */\nexport interface ChatConversationLabels {\n /** Shown while loading without messages. Defaults to `'Loading messages…'`. */\n loading?: string;\n\n /** Shown when loading messages fails, alongside any existing messages. Defaults to `'Could not load messages.'`. */\n failed?: string;\n\n /** Shown when access is denied; replaces any existing messages. Defaults to `'You are not authorized to view these messages.'`. */\n unauthorized?: string;\n\n /** Shown when there are no messages yet. Defaults to `'No messages yet. Say hello!'`. */\n empty?: string;\n\n /** One author typing. `{name}` is substituted. Defaults to `'{name} is typing'`. */\n typing?: string;\n\n /** Two authors typing. `{first}`/`{second}` are substituted. Defaults to `'{first} and {second} are typing'`. */\n typingTwo?: string;\n\n /** Three or more authors typing. Defaults to `'Several people are typing'`. */\n typingSeveral?: string;\n\n /** Tooltip/accessible label for the quick-reply button. `{name}` is substituted. Defaults to `'Reply to {name}'`. */\n quickReplyTo?: string;\n\n /** Overrides for the relative timestamps next to messages. */\n timestamps?: RelativeTimestampLabels;\n\n /** Labels forwarded to the composer. */\n composer?: ChatComposerLabels;\n}\n\n/**\n * Props for {@link ChatConversation}.\n * @typeParam TMessage The host's message type — anything extending {@link ChatMessage}.\n */\nexport interface ChatConversationProps<TMessage extends ChatMessage = ChatMessage> {\n /**\n * The messages of the conversation, rendered in the order given. Hand this the array a live\n * (observable) query delivers and the conversation re-renders as it changes.\n */\n messages: TMessage[];\n\n /** Query display state. Defaults to {@link ChatStatus.Ready}; existing messages remain visible on loading or failure, and unauthorized disables the composer. */\n status?: ChatStatus;\n\n /**\n * Invoked when a message is sent, with the trimmed body and who it mentions. Everything else\n * about persisting the message is the host's business.\n * @param body The message text, mentions written into it as plain `@Name`.\n * @param mentions Who the body mentions.\n */\n onSendMessage: (body: string, mentions: ChatMention[]) => void;\n\n /**\n * Resolves an author id into what it takes to render them — name, kind, whether an avatar\n * image exists. Messages deliberately carry only the id; this is where the rest comes from.\n * Omit to fall back to rendering the id itself as the name.\n * @param authorId The identifier of the author to resolve.\n * @returns The author.\n */\n authorOf?: (authorId: ChatIdentifier) => ChatAuthor;\n\n /**\n * Renders the avatar for an author, replacing the built-in initials circle entirely.\n * @param authorId The identifier of the author.\n * @param author The resolved author, as {@link authorOf} answered.\n * @returns What to render.\n */\n renderAvatar?: (authorId: ChatIdentifier, author: ChatAuthor) => ReactNode;\n\n /**\n * Renders the display name for an author, replacing the built-in name text entirely.\n * @param authorId The identifier of the author.\n * @param author The resolved author, as {@link authorOf} answered.\n * @returns What to render.\n */\n renderAuthorName?: (authorId: ChatIdentifier, author: ChatAuthor) => ReactNode;\n\n /** The host's own actions, offered as buttons on every message each is available for. */\n actions?: ChatMessageAction<TMessage>[];\n\n /** Everyone who can be mentioned, when the host already holds the list. Omit (together with\n * {@link resolveMentionCandidates}) to turn mentions off. */\n mentionCandidates?: MentionCandidate[];\n\n /**\n * Resolves who can be mentioned as the person types, invoked with what has been typed after\n * the `@`. May answer synchronously or with a promise.\n * @param query What has been typed after the `@`.\n * @returns The candidates matching the query.\n */\n resolveMentionCandidates?: (\n query: string,\n ) => MentionCandidate[] | Promise<MentionCandidate[]>;\n\n /** Who the conversation is currently waiting on — someone typing, or an agent working. */\n typingAuthors?: ChatTypingAuthor[];\n\n /** Whether the quick-reply affordance (prefill the composer with `@Name`) is offered on\n * messages. Defaults to true. */\n quickReply?: boolean;\n\n /** Builds the avatar image URL for every author shown. Omit to always show initials. */\n buildAvatarUrl?: (params: BuildAvatarUrlParams) => string;\n\n /** Whether the composer takes focus when mounted. */\n autoFocus?: boolean;\n\n /** Overrides for every label rendered. Unset fields fall back to literal English defaults. */\n labels?: ChatConversationLabels;\n\n /** Additional class name for the conversation's root element. */\n className?: string;\n}\n\n/** The message's timestamp as a `Date` — data that traveled through JSON may carry it as a string. */\nconst timestampOf = (message: ChatMessage): Date =>\n message.timestamp instanceof Date ? message.timestamp : new Date(message.timestamp);\n\nconst sameMinute = (left: Date, right: Date): boolean =>\n left.getFullYear() === right.getFullYear() &&\n left.getMonth() === right.getMonth() &&\n left.getDate() === right.getDate() &&\n left.getHours() === right.getHours() &&\n left.getMinutes() === right.getMinutes();\n\ninterface MessageRenderInfo {\n /** Whether to show the author row — false for a continuation by the same author. */\n showAuthor: boolean;\n\n /** Whether to show the timestamp — false when the next message continues the same minute. */\n showTimestamp: boolean;\n}\n\nconst computeRenderInfo = (messages: ChatMessage[]): MessageRenderInfo[] =>\n messages.map((message, index) => {\n const previous = index > 0 ? messages[index - 1] : undefined;\n const next = index < messages.length - 1 ? messages[index + 1] : undefined;\n\n const showAuthor =\n !previous ||\n chatIdentifierString(previous.authorId) !==\n chatIdentifierString(message.authorId);\n const showTimestamp =\n !next ||\n chatIdentifierString(next.authorId) !==\n chatIdentifierString(message.authorId) ||\n !sameMinute(timestampOf(message), timestampOf(next));\n\n return { showAuthor, showTimestamp };\n });\n\n/**\n * Composes the \"X is typing\" line from whoever the conversation is waiting on.\n * @param authors The authors currently typing or working.\n * @param labels Label overrides, as passed to {@link ChatConversation}.\n * @returns The label to show, empty when nobody is.\n */\nconst typingLabel = (\n authors: ChatTypingAuthor[],\n labels: ChatConversationLabels | undefined,\n): string => {\n if (authors.length === 0) return '';\n if (authors.length === 1)\n return (labels?.typing ?? '{name} is typing').replace('{name}', authors[0].name);\n if (authors.length === 2)\n return (labels?.typingTwo ?? '{first} and {second} are typing')\n .replace('{first}', authors[0].name)\n .replace('{second}', authors[1].name);\n return labels?.typingSeveral ?? 'Several people are typing';\n};\n\n/**\n * One conversation — its messages and the composer to add to them. Host-agnostic by design: it\n * renders whatever message array it is given (hand it a live query's data and it stays current),\n * resolves authors through the host's {@link ChatConversationProps.authorOf | authorOf} and\n * render callbacks, offers the host's own {@link ChatConversationProps.actions | actions} on\n * each message, and reports sends back through\n * {@link ChatConversationProps.onSendMessage | onSendMessage} without any opinion about the\n * backend behind it. Mentions and the emoji picker come along through the composer.\n */\nexport const ChatConversation = <TMessage extends ChatMessage = ChatMessage>({\n messages,\n status = ChatStatus.Ready,\n onSendMessage,\n authorOf,\n renderAvatar,\n renderAuthorName,\n actions,\n mentionCandidates,\n resolveMentionCandidates,\n typingAuthors = [],\n quickReply = true,\n buildAvatarUrl,\n autoFocus = false,\n labels,\n className,\n}: ChatConversationProps<TMessage>) => {\n const composerRef = useRef<ChatComposerHandle>(null);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const renderInfo = useMemo(() => computeRenderInfo(messages), [messages]);\n\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n }, [messages, typingAuthors.length]);\n\n const authorFor = (authorId: ChatIdentifier): ChatAuthor =>\n authorOf?.(authorId) ?? {\n name: chatIdentifierString(authorId),\n kind: ChatAuthorKind.User,\n };\n\n const send = (text: string, mentioned: MentionCandidate[]) =>\n onSendMessage(\n text,\n mentioned.map(\n (candidate) =>\n ({\n id: candidate.id,\n name: candidate.name,\n kind: candidate.kind,\n }) satisfies ChatMention,\n ),\n );\n\n const quickReplyTo = (name: string) => composerRef.current?.prefill(`@${name} `);\n\n return (\n <div className={`cratis-chat-conversation${className ? ` ${className}` : ''}`}>\n <div className='cratis-chat-conversation__messages' aria-busy={status === ChatStatus.Loading && messages.length > 0 || undefined}>\n {(messages.length === 0 || status === ChatStatus.Failed || status === ChatStatus.Unauthorized) && (\n <p className='cratis-chat-conversation__empty'>\n {status === ChatStatus.Loading ? (\n <span key={status} role='status'>{labels?.loading ?? 'Loading messages…'}</span>\n ) : status === ChatStatus.Failed ? (\n <span key={status} role='alert'>{labels?.failed ?? 'Could not load messages.'}</span>\n ) : status === ChatStatus.Unauthorized ? (\n <span key={status} role='alert'>{labels?.unauthorized ?? 'You are not authorized to view these messages.'}</span>\n ) : (\n labels?.empty ?? 'No messages yet. Say hello!'\n )}\n </p>\n )}\n {status !== ChatStatus.Unauthorized && messages.map((message, index) => {\n const author = authorFor(message.authorId);\n const { showAuthor, showTimestamp } = renderInfo[index];\n const availableActions = (actions ?? []).filter(\n (action) => action.isAvailable?.(message) ?? true,\n );\n const showActionsRow = quickReply || availableActions.length > 0;\n\n return (\n <div\n key={chatIdentifierString(message.id)}\n className={`cratis-chat-message${showAuthor ? '' : ' cratis-chat-message--continuation'}`}\n >\n {showAuthor && (\n <div className='cratis-chat-message__author'>\n {renderAvatar ? (\n renderAvatar(message.authorId, author)\n ) : (\n <PersonAvatarCircle\n userId={chatIdentifierString(\n message.authorId,\n )}\n name={author.name}\n hasAvatar={author.hasAvatar ?? false}\n size={22}\n ownerType={\n author.kind === ChatAuthorKind.Agent\n ? 'Agents'\n : 'Users'\n }\n version={author.avatarVersion}\n buildAvatarUrl={buildAvatarUrl}\n />\n )}\n <span className='cratis-chat-message__author-name'>\n {renderAuthorName\n ? renderAuthorName(message.authorId, author)\n : author.name}\n </span>\n </div>\n )}\n <div className='cratis-chat-message__content'>\n <div\n className={`cratis-chat-message__panel${showActionsRow ? ' cratis-chat-message__panel--with-actions' : ''}`}\n >\n <ChatMessageBody\n body={message.body}\n mentions={message.mentions}\n />\n {showActionsRow && (\n <div className='cratis-chat-message__actions'>\n {quickReply && (\n <button\n type='button'\n className='cratis-chat-message__action'\n title={(\n labels?.quickReplyTo ??\n 'Reply to {name}'\n ).replace('{name}', author.name)}\n aria-label={(\n labels?.quickReplyTo ??\n 'Reply to {name}'\n ).replace('{name}', author.name)}\n onClick={() =>\n quickReplyTo(author.name)\n }\n >\n <ReplyIcon />\n </button>\n )}\n {availableActions.map((action) => (\n <button\n key={action.id}\n type='button'\n className='cratis-chat-message__action'\n title={action.label}\n aria-label={action.label}\n onClick={() =>\n action.onInvoke(message)\n }\n >\n {typeof action.icon === 'string' ? (\n <i\n className={action.icon}\n aria-hidden='true'\n />\n ) : (\n action.icon\n )}\n </button>\n ))}\n </div>\n )}\n </div>\n {showTimestamp && (\n <span className='cratis-chat-message__time'>\n {relativeTimestamp(\n timestampOf(message),\n new Date(),\n labels?.timestamps,\n )}\n </span>\n )}\n </div>\n </div>\n );\n })}\n {status !== ChatStatus.Unauthorized && (\n <TypingIndicator\n authors={typingAuthors}\n label={typingLabel(typingAuthors, labels)}\n buildAvatarUrl={buildAvatarUrl}\n />\n )}\n <div ref={messagesEndRef} />\n </div>\n\n <ChatComposer\n ref={composerRef}\n mentionCandidates={mentionCandidates}\n resolveMentionCandidates={resolveMentionCandidates}\n onSend={send}\n disabled={status === ChatStatus.Unauthorized}\n autoFocus={autoFocus}\n buildAvatarUrl={buildAvatarUrl}\n labels={labels?.composer}\n />\n </div>\n );\n};\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatConversation.js","sources":["../../../Chat/ChatConversation.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useEffect, useMemo, useRef, type ReactNode } from 'react';\nimport { PersonAvatarCircle, type BuildAvatarUrlParams } from './Kit/Avatar';\nimport { ChatAuthorKind } from './Kit/ChatAuthorKind';\nimport {\n ChatComposer,\n type ChatComposerHandle,\n type ChatComposerLabels,\n} from './Kit/ChatComposer';\nimport type { ChatTypingAuthor } from './Kit/ChatTypingAuthor';\nimport type { MentionCandidate } from './Kit/Mentions';\nimport { TypingIndicator } from './Kit/TypingIndicator';\nimport type { ChatAuthor } from './ChatAuthor';\nimport { chatIdentifierString, type ChatIdentifier } from './ChatIdentifier';\nimport type { ChatMention } from './ChatMention';\nimport type { ChatMessage } from './ChatMessage';\nimport { ChatStatus } from './ChatStatus';\nimport type { ChatMessageAction } from './ChatMessageAction';\nimport { ChatMessageBody } from './ChatMessageBody';\nimport { relativeTimestamp, type RelativeTimestampLabels } from './relativeTimestamp';\n\nconst ReplyIcon = () => (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2.5'\n aria-hidden='true'\n >\n <polyline points='9 17 4 12 9 7' />\n <path d='M4 12h10a6 6 0 0 1 6 6v1' />\n </svg>\n);\n\n/** Overrides for every label the {@link ChatConversation} renders. Any field left unset falls\n * back to a literal English default — this library ships no i18n mechanism of its own, so a host\n * that localizes passes its own translated strings through here. */\nexport interface ChatConversationLabels {\n /** Shown while loading without messages. Defaults to `'Loading messages…'`. */\n loading?: string;\n\n /** Shown when loading messages fails, alongside any existing messages. Defaults to `'Could not load messages.'`. */\n failed?: string;\n\n /** Shown when access is denied; replaces any existing messages. Defaults to `'You are not authorized to view these messages.'`. */\n unauthorized?: string;\n\n /** Shown when there are no messages yet. Defaults to `'No messages yet. Say hello!'`. */\n empty?: string;\n\n /** One author typing. `{name}` is substituted. Defaults to `'{name} is typing'`. */\n typing?: string;\n\n /** Two authors typing. `{first}`/`{second}` are substituted. Defaults to `'{first} and {second} are typing'`. */\n typingTwo?: string;\n\n /** Three or more authors typing. Defaults to `'Several people are typing'`. */\n typingSeveral?: string;\n\n /** Tooltip/accessible label for the quick-reply button. `{name}` is substituted. Defaults to `'Reply to {name}'`. */\n quickReplyTo?: string;\n\n /** Overrides for the relative timestamps next to messages. */\n timestamps?: RelativeTimestampLabels;\n\n /** Labels forwarded to the composer. */\n composer?: ChatComposerLabels;\n}\n\n/**\n * Props for {@link ChatConversation}.\n * @typeParam TMessage The host's message type — anything extending {@link ChatMessage}.\n */\nexport interface ChatConversationProps<TMessage extends ChatMessage = ChatMessage> {\n /**\n * The messages of the conversation, rendered in the order given. Hand this the array a live\n * (observable) query delivers and the conversation re-renders as it changes.\n */\n messages: TMessage[];\n\n /** Query display state. Defaults to {@link ChatStatus.Ready}; existing messages remain visible on loading or failure, and unauthorized disables the composer. */\n status?: ChatStatus;\n\n /**\n * Invoked when a message is sent, with the trimmed body and who it mentions. Everything else\n * about persisting the message is the host's business.\n * @param body The message text, mentions written into it as plain `@Name`.\n * @param mentions Who the body mentions.\n */\n onSendMessage: (body: string, mentions: ChatMention[]) => void;\n\n /**\n * Resolves an author id into what it takes to render them — name, kind, whether an avatar\n * image exists. Messages deliberately carry only the id; this is where the rest comes from.\n * Omit to fall back to rendering the id itself as the name.\n * @param authorId The identifier of the author to resolve.\n * @returns The author.\n */\n authorOf?: (authorId: ChatIdentifier) => ChatAuthor;\n\n /**\n * Renders the avatar for an author, replacing the built-in initials circle entirely.\n * @param authorId The identifier of the author.\n * @param author The resolved author, as {@link authorOf} answered.\n * @returns What to render.\n */\n renderAvatar?: (authorId: ChatIdentifier, author: ChatAuthor) => ReactNode;\n\n /**\n * Renders the display name for an author, replacing the built-in name text entirely.\n * @param authorId The identifier of the author.\n * @param author The resolved author, as {@link authorOf} answered.\n * @returns What to render.\n */\n renderAuthorName?: (authorId: ChatIdentifier, author: ChatAuthor) => ReactNode;\n\n /**\n * Renders host content under a message's body, for example reactions or a failed-reply notice.\n * Nothing extra is rendered when it returns null or undefined.\n * @param message The message being rendered.\n * @returns What to render under the message body.\n */\n renderMessageExtra?: (message: TMessage) => ReactNode;\n\n /** The host's own actions, offered as buttons on every message each is available for. */\n actions?: ChatMessageAction<TMessage>[];\n\n /** Everyone who can be mentioned, when the host already holds the list. Omit (together with\n * {@link resolveMentionCandidates}) to turn mentions off. */\n mentionCandidates?: MentionCandidate[];\n\n /**\n * Resolves who can be mentioned as the person types, invoked with what has been typed after\n * the `@`. May answer synchronously or with a promise.\n * @param query What has been typed after the `@`.\n * @returns The candidates matching the query.\n */\n resolveMentionCandidates?: (\n query: string,\n ) => MentionCandidate[] | Promise<MentionCandidate[]>;\n\n /** Who the conversation is currently waiting on — someone typing, or an agent working. */\n typingAuthors?: ChatTypingAuthor[];\n\n /** Whether the quick-reply affordance (prefill the composer with `@Name`) is offered on\n * messages. Defaults to true. */\n quickReply?: boolean;\n\n /** Builds the avatar image URL for every author shown. Omit to always show initials. */\n buildAvatarUrl?: (params: BuildAvatarUrlParams) => string;\n\n /** Whether the composer takes focus when mounted. */\n autoFocus?: boolean;\n\n /** Overrides for every label rendered. Unset fields fall back to literal English defaults. */\n labels?: ChatConversationLabels;\n\n /** Additional class name for the conversation's root element. */\n className?: string;\n}\n\n/** The message's timestamp as a `Date` — data that traveled through JSON may carry it as a string. */\nconst timestampOf = (message: ChatMessage): Date =>\n message.timestamp instanceof Date ? message.timestamp : new Date(message.timestamp);\n\nconst sameMinute = (left: Date, right: Date): boolean =>\n left.getFullYear() === right.getFullYear() &&\n left.getMonth() === right.getMonth() &&\n left.getDate() === right.getDate() &&\n left.getHours() === right.getHours() &&\n left.getMinutes() === right.getMinutes();\n\ninterface MessageRenderInfo {\n /** Whether to show the author row — false for a continuation by the same author. */\n showAuthor: boolean;\n\n /** Whether to show the timestamp — false when the next message continues the same minute. */\n showTimestamp: boolean;\n}\n\nconst computeRenderInfo = (messages: ChatMessage[]): MessageRenderInfo[] =>\n messages.map((message, index) => {\n const previous = index > 0 ? messages[index - 1] : undefined;\n const next = index < messages.length - 1 ? messages[index + 1] : undefined;\n\n const showAuthor =\n !previous ||\n chatIdentifierString(previous.authorId) !==\n chatIdentifierString(message.authorId);\n const showTimestamp =\n !next ||\n chatIdentifierString(next.authorId) !==\n chatIdentifierString(message.authorId) ||\n !sameMinute(timestampOf(message), timestampOf(next));\n\n return { showAuthor, showTimestamp };\n });\n\n/**\n * Composes the \"X is typing\" line from whoever the conversation is waiting on.\n * @param authors The authors currently typing or working.\n * @param labels Label overrides, as passed to {@link ChatConversation}.\n * @returns The label to show, empty when nobody is.\n */\nconst typingLabel = (\n authors: ChatTypingAuthor[],\n labels: ChatConversationLabels | undefined,\n): string => {\n if (authors.length === 0) return '';\n if (authors.length === 1)\n return (labels?.typing ?? '{name} is typing').replace('{name}', authors[0].name);\n if (authors.length === 2)\n return (labels?.typingTwo ?? '{first} and {second} are typing')\n .replace('{first}', authors[0].name)\n .replace('{second}', authors[1].name);\n return labels?.typingSeveral ?? 'Several people are typing';\n};\n\n/**\n * One conversation — its messages and the composer to add to them. Host-agnostic by design: it\n * renders whatever message array it is given (hand it a live query's data and it stays current),\n * resolves authors through the host's {@link ChatConversationProps.authorOf | authorOf} and\n * render callbacks, offers the host's own {@link ChatConversationProps.actions | actions} on\n * each message, and reports sends back through\n * {@link ChatConversationProps.onSendMessage | onSendMessage} without any opinion about the\n * backend behind it. Mentions and the emoji picker come along through the composer.\n */\nexport const ChatConversation = <TMessage extends ChatMessage = ChatMessage>({\n messages,\n status = ChatStatus.Ready,\n onSendMessage,\n authorOf,\n renderAvatar,\n renderAuthorName,\n renderMessageExtra,\n actions,\n mentionCandidates,\n resolveMentionCandidates,\n typingAuthors = [],\n quickReply = true,\n buildAvatarUrl,\n autoFocus = false,\n labels,\n className,\n}: ChatConversationProps<TMessage>) => {\n const composerRef = useRef<ChatComposerHandle>(null);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const renderInfo = useMemo(() => computeRenderInfo(messages), [messages]);\n\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n }, [messages, typingAuthors.length]);\n\n const authorFor = (authorId: ChatIdentifier): ChatAuthor =>\n authorOf?.(authorId) ?? {\n name: chatIdentifierString(authorId),\n kind: ChatAuthorKind.User,\n };\n\n const send = (text: string, mentioned: MentionCandidate[]) =>\n onSendMessage(\n text,\n mentioned.map(\n (candidate) =>\n ({\n id: candidate.id,\n name: candidate.name,\n kind: candidate.kind,\n }) satisfies ChatMention,\n ),\n );\n\n const quickReplyTo = (name: string) => composerRef.current?.prefill(`@${name} `);\n\n return (\n <div className={`cratis-chat-conversation${className ? ` ${className}` : ''}`}>\n <div className='cratis-chat-conversation__messages' aria-busy={status === ChatStatus.Loading && messages.length > 0 || undefined}>\n {(messages.length === 0 || status === ChatStatus.Failed || status === ChatStatus.Unauthorized) && (\n <p className='cratis-chat-conversation__empty'>\n {status === ChatStatus.Loading ? (\n <span key={status} role='status'>{labels?.loading ?? 'Loading messages…'}</span>\n ) : status === ChatStatus.Failed ? (\n <span key={status} role='alert'>{labels?.failed ?? 'Could not load messages.'}</span>\n ) : status === ChatStatus.Unauthorized ? (\n <span key={status} role='alert'>{labels?.unauthorized ?? 'You are not authorized to view these messages.'}</span>\n ) : (\n labels?.empty ?? 'No messages yet. Say hello!'\n )}\n </p>\n )}\n {status !== ChatStatus.Unauthorized && messages.map((message, index) => {\n const author = authorFor(message.authorId);\n const { showAuthor, showTimestamp } = renderInfo[index];\n const availableActions = (actions ?? []).filter(\n (action) => action.isAvailable?.(message) ?? true,\n );\n const showActionsRow = quickReply || availableActions.length > 0;\n\n return (\n <div\n key={chatIdentifierString(message.id)}\n className={`cratis-chat-message${showAuthor ? '' : ' cratis-chat-message--continuation'}`}\n >\n {showAuthor && (\n <div className='cratis-chat-message__author'>\n {renderAvatar ? (\n renderAvatar(message.authorId, author)\n ) : (\n <PersonAvatarCircle\n userId={chatIdentifierString(\n message.authorId,\n )}\n name={author.name}\n hasAvatar={author.hasAvatar ?? false}\n size={22}\n ownerType={\n author.kind === ChatAuthorKind.Agent\n ? 'Agents'\n : 'Users'\n }\n version={author.avatarVersion}\n buildAvatarUrl={buildAvatarUrl}\n />\n )}\n <span className='cratis-chat-message__author-name'>\n {renderAuthorName\n ? renderAuthorName(message.authorId, author)\n : author.name}\n </span>\n </div>\n )}\n <div className='cratis-chat-message__content'>\n <div\n className={`cratis-chat-message__panel${showActionsRow ? ' cratis-chat-message__panel--with-actions' : ''}`}\n >\n <ChatMessageBody\n body={message.body}\n mentions={message.mentions}\n />\n {showActionsRow && (\n <div className='cratis-chat-message__actions'>\n {quickReply && (\n <button\n type='button'\n className='cratis-chat-message__action'\n title={(\n labels?.quickReplyTo ??\n 'Reply to {name}'\n ).replace('{name}', author.name)}\n aria-label={(\n labels?.quickReplyTo ??\n 'Reply to {name}'\n ).replace('{name}', author.name)}\n onClick={() =>\n quickReplyTo(author.name)\n }\n >\n <ReplyIcon />\n </button>\n )}\n {availableActions.map((action) => (\n <button\n key={action.id}\n type='button'\n className='cratis-chat-message__action'\n title={action.label}\n aria-label={action.label}\n onClick={() =>\n action.onInvoke(message)\n }\n >\n {typeof action.icon === 'string' ? (\n <i\n className={action.icon}\n aria-hidden='true'\n />\n ) : (\n action.icon\n )}\n </button>\n ))}\n </div>\n )}\n </div>\n {renderMessageExtra?.(message)}\n {showTimestamp && (\n <span className='cratis-chat-message__time'>\n {relativeTimestamp(\n timestampOf(message),\n new Date(),\n labels?.timestamps,\n )}\n </span>\n )}\n </div>\n </div>\n );\n })}\n {status !== ChatStatus.Unauthorized && (\n <TypingIndicator\n authors={typingAuthors}\n label={typingLabel(typingAuthors, labels)}\n buildAvatarUrl={buildAvatarUrl}\n />\n )}\n <div ref={messagesEndRef} />\n </div>\n\n <ChatComposer\n ref={composerRef}\n mentionCandidates={mentionCandidates}\n resolveMentionCandidates={resolveMentionCandidates}\n onSend={send}\n disabled={status === ChatStatus.Unauthorized}\n autoFocus={autoFocus}\n buildAvatarUrl={buildAvatarUrl}\n labels={labels?.composer}\n />\n </div>\n );\n};\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ButtonHTMLAttributes, HTMLAttributes } from 'react';
|
|
1
|
+
import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react';
|
|
2
2
|
import type { ChatConversationLabels, ChatConversationProps } from './ChatConversation.js';
|
|
3
3
|
import { type ChatIdentifier } from './ChatIdentifier.js';
|
|
4
4
|
import type { ChatMention } from './ChatMention.js';
|
|
@@ -50,6 +50,13 @@ export interface ChatSidebarLabels {
|
|
|
50
50
|
* @typeParam TTopic The host's topic type — anything extending {@link ChatTopic}.
|
|
51
51
|
*/
|
|
52
52
|
export interface ChatSidebarProps<TMessage extends ChatMessage = ChatMessage, TTopic extends ChatTopic = ChatTopic> extends Omit<ChatConversationProps<TMessage>, 'messages' | 'onSendMessage' | 'labels' | 'className' | 'status'> {
|
|
53
|
+
/**
|
|
54
|
+
* Renders host content in the header, between the title and the close button, for example a
|
|
55
|
+
* rename control for the open topic. Nothing is rendered when it returns null or undefined.
|
|
56
|
+
* @param openTopic The open topic, or undefined while the topic list is shown.
|
|
57
|
+
* @returns What to render in the header.
|
|
58
|
+
*/
|
|
59
|
+
renderHeaderActions?: (openTopic: TTopic | undefined) => ReactNode;
|
|
53
60
|
/** Whether the sidebar is open. */
|
|
54
61
|
open: boolean;
|
|
55
62
|
/** Invoked when the sidebar asks to close — the host owns {@link open}, so closing means the host flips it. */
|
|
@@ -141,6 +148,6 @@ export interface ChatSidebarProps<TMessage extends ChatMessage = ChatMessage, TT
|
|
|
141
148
|
* a name through {@link ChatSidebarProps.onRequestTopicName | onRequestTopicName} — rendering a
|
|
142
149
|
* pending placeholder until the name arrives.
|
|
143
150
|
*/
|
|
144
|
-
export declare const ChatSidebar: <TMessage extends ChatMessage = ChatMessage, TTopic extends ChatTopic = ChatTopic>({ open, onClose, topics, messages, topicActions, topicsStatus, messagesStatus, selectedTopicId, onTopicSelected, onStartTopic, onSendMessage, onRequestTopicName, isTopicUnnamed, position, width, modal, labels, className, pt, ...conversation }: ChatSidebarProps<TMessage, TTopic>) => false | import("react").JSX.Element | null;
|
|
151
|
+
export declare const ChatSidebar: <TMessage extends ChatMessage = ChatMessage, TTopic extends ChatTopic = ChatTopic>({ open, onClose, topics, messages, topicActions, renderHeaderActions, topicsStatus, messagesStatus, selectedTopicId, onTopicSelected, onStartTopic, onSendMessage, onRequestTopicName, isTopicUnnamed, position, width, modal, labels, className, pt, ...conversation }: ChatSidebarProps<TMessage, TTopic>) => false | import("react").JSX.Element | null;
|
|
145
152
|
export {};
|
|
146
153
|
//# sourceMappingURL=ChatSidebar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatSidebar.d.ts","sourceRoot":"","sources":["../../../Chat/ChatSidebar.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ChatSidebar.d.ts","sourceRoot":"","sources":["../../../Chat/ChatSidebar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@ import { unstable_useOverlayEnvironment } from '../renderer/RendererContext.js';
|
|
|
12
12
|
|
|
13
13
|
const classNames = (...values) => values.filter(Boolean).join(' ');
|
|
14
14
|
const BackIcon = () => (jsxs("svg", { width: '14', height: '14', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2.5', "aria-hidden": 'true', children: [jsx("line", { x1: '19', y1: '12', x2: '5', y2: '12' }), jsx("polyline", { points: '12 19 5 12 12 5' })] }));
|
|
15
|
-
const ChatSidebar = ({ open, onClose, topics, messages, topicActions, topicsStatus, messagesStatus, selectedTopicId, onTopicSelected, onStartTopic, onSendMessage, onRequestTopicName, isTopicUnnamed: isTopicUnnamed$1, position = 'right', width = '24rem', modal = false, labels, className, pt, ...conversation }) => {
|
|
15
|
+
const ChatSidebar = ({ open, onClose, topics, messages, topicActions, renderHeaderActions, topicsStatus, messagesStatus, selectedTopicId, onTopicSelected, onStartTopic, onSendMessage, onRequestTopicName, isTopicUnnamed: isTopicUnnamed$1, position = 'right', width = '24rem', modal = false, labels, className, pt, ...conversation }) => {
|
|
16
16
|
const icon = useCratisIcon();
|
|
17
17
|
const overlayEnvironment = unstable_useOverlayEnvironment();
|
|
18
18
|
const [mounted, setMounted] = useState(false);
|
|
@@ -82,7 +82,7 @@ const ChatSidebar = ({ open, onClose, topics, messages, topicActions, topicsStat
|
|
|
82
82
|
const titleIsPending = openTopicId !== undefined && (!openTopic || unnamed(openTopic));
|
|
83
83
|
const panel = (jsxs(Fragment, { children: [jsxs("header", { ...pt?.header, className: classNames('cratis-chat-sidebar__header', pt?.header?.className), "data-cratis-part": 'header', children: [openTopicId !== undefined && (jsx("button", { ...pt?.back, type: 'button', className: classNames('cratis-chat-sidebar__back', pt?.back?.className), "data-cratis-part": 'back', title: labels?.back ?? 'Back to topics', "aria-label": labels?.back ?? 'Back to topics', onClick: () => select(undefined), children: jsx(BackIcon, {}) })), jsx("h2", { ...pt?.title, className: classNames('cratis-chat-sidebar__title', titleIsPending
|
|
84
84
|
? 'cratis-chat-sidebar__title--pending'
|
|
85
|
-
: undefined, pt?.title?.className), "data-cratis-part": 'title', children: title }), jsx("button", { ...pt?.close, type: 'button', className: classNames('cratis-chat-sidebar__close', pt?.close?.className), "data-cratis-part": 'close', "aria-label": labels?.close ?? 'Close chat', onClick: onClose, children: jsx("span", { "aria-hidden": 'true', children: icon('close', '×') }) })] }), jsx("div", { ...pt?.content, className: classNames('cratis-chat-sidebar__content', pt?.content?.className), "data-cratis-part": 'content', children: openTopicId === undefined ? (jsx(ChatTopicList, { topics: topics, topicActions: topicActions, status: topicsStatus, onOpen: (topic) => select(topic.id, topic), onStart: onStartTopic
|
|
85
|
+
: undefined, pt?.title?.className), "data-cratis-part": 'title', children: title }), renderHeaderActions?.(openTopic), jsx("button", { ...pt?.close, type: 'button', className: classNames('cratis-chat-sidebar__close', pt?.close?.className), "data-cratis-part": 'close', "aria-label": labels?.close ?? 'Close chat', onClick: onClose, children: jsx("span", { "aria-hidden": 'true', children: icon('close', '×') }) })] }), jsx("div", { ...pt?.content, className: classNames('cratis-chat-sidebar__content', pt?.content?.className), "data-cratis-part": 'content', children: openTopicId === undefined ? (jsx(ChatTopicList, { topics: topics, topicActions: topicActions, status: topicsStatus, onOpen: (topic) => select(topic.id, topic), onStart: onStartTopic
|
|
86
86
|
? () => {
|
|
87
87
|
void startTopic();
|
|
88
88
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatSidebar.js","sources":["../../../Chat/ChatSidebar.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useEffect, useRef, useState } from 'react';\nimport type { ButtonHTMLAttributes, HTMLAttributes } from 'react';\nimport { createPortal } from 'react-dom';\nimport { Modal, ModalOverlay } from 'react-aria-components';\nimport type { ChatConversationLabels, ChatConversationProps } from './ChatConversation';\nimport { ChatConversation } from './ChatConversation';\nimport { sameChatIdentifier, type ChatIdentifier } from './ChatIdentifier';\nimport type { ChatMention } from './ChatMention';\nimport type { ChatMessage } from './ChatMessage';\nimport type { ChatTopic } from './ChatTopic';\nimport type { ChatTopicAction } from './ChatTopicAction';\nimport type { ChatStatus } from './ChatStatus';\nimport type { ChatTopicListLabels } from './ChatTopicList';\nimport { ChatTopicList } from './ChatTopicList';\nimport { isTopicUnnamed as defaultIsTopicUnnamed } from './isTopicUnnamed';\nimport { shouldRequestTopicName } from './shouldRequestTopicName';\nimport { useCratisIcon } from '../configuration/useCratisIcon';\nimport { unstable_useOverlayEnvironment } from '../renderer/RendererContext';\nimport type { ExactPartKeys } from '../types/ExactPartKeys';\nimport type { PartsOf } from '../types/parts';\n\ntype ChatSidebarPartAttributes<TElement> = HTMLAttributes<TElement> & {\n [attribute: `data-${string}`]: string | number | boolean | undefined;\n};\n\ntype ChatSidebarButtonAttributes = Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n 'onClick' | 'type'\n>;\n\n/** Stable Cratis-owned parts for styling a {@link ChatSidebar}. */\nexport interface ChatSidebarParts {\n /** Viewport overlay layer the sidebar portals into — only paints/blocks when {@link ChatSidebarProps.modal} is true. */\n backdrop?: ChatSidebarPartAttributes<HTMLDivElement>;\n /** The sidebar panel itself. */\n root?: ChatSidebarPartAttributes<HTMLDivElement>;\n /** Header containing the back affordance, title, and close button. */\n header?: ChatSidebarPartAttributes<HTMLElement>;\n /** The topic list title, or the open topic's name. */\n title?: ChatSidebarPartAttributes<HTMLHeadingElement>;\n /** Back-to-topics button, present only while a topic is open. */\n back?: ChatSidebarButtonAttributes;\n /** Close button. */\n close?: ChatSidebarButtonAttributes;\n /** The topic list / conversation content region. */\n content?: ChatSidebarPartAttributes<HTMLDivElement>;\n}\n\nconst chatSidebarPartsMatchManifest: ExactPartKeys<\n ChatSidebarParts,\n PartsOf<'ChatSidebar'>\n> = true;\nvoid chatSidebarPartsMatchManifest;\n\n/** Overrides for every label the {@link ChatSidebar} and its children render. Any field left\n * unset falls back to a literal English default. */\nexport interface ChatSidebarLabels {\n /** The sidebar's title while the topic list is showing. Defaults to `'Topics'`. */\n topics?: string;\n\n /** Placeholder title for a topic that has not been named yet. Defaults to `'New topic'`. */\n unnamedTopic?: string;\n\n /** Tooltip/accessible label for the back-to-topics button. Defaults to `'Back to topics'`. */\n back?: string;\n\n /** Accessible label for the close button. Defaults to `'Close chat'`. */\n close?: string;\n\n /** Labels forwarded to the {@link ChatTopicList}. */\n topicList?: ChatTopicListLabels;\n\n /** Labels forwarded to the {@link ChatConversation}. */\n conversation?: ChatConversationLabels;\n}\n\n/**\n * Props for {@link ChatSidebar}.\n * @typeParam TMessage The host's message type — anything extending {@link ChatMessage}.\n * @typeParam TTopic The host's topic type — anything extending {@link ChatTopic}.\n */\nexport interface ChatSidebarProps<\n TMessage extends ChatMessage = ChatMessage,\n TTopic extends ChatTopic = ChatTopic,\n> extends Omit<\n ChatConversationProps<TMessage>,\n 'messages' | 'onSendMessage' | 'labels' | 'className' | 'status'\n> {\n /** Whether the sidebar is open. */\n open: boolean;\n\n /** Invoked when the sidebar asks to close — the host owns {@link open}, so closing means the host flips it. */\n onClose: () => void;\n\n /**\n * The topics, in any order. Hand this the array a live (observable) query delivers and the\n * sidebar re-renders as it changes.\n */\n topics: TTopic[];\n\n /**\n * The messages — either the whole chat or just the open topic's; the sidebar shows those of\n * the open topic by matching on `topicId` either way. Hand this the array a live\n * (observable) query delivers and the conversation re-renders as it changes.\n */\n messages: TMessage[];\n\n /** The host's actions to offer on each topic where they are available. The inherited `actions` prop applies only to messages. */\n topicActions?: ChatTopicAction<TTopic>[];\n\n /** Topic-list query display state. Defaults to ready when omitted. */\n topicsStatus?: ChatStatus;\n\n /** Conversation query display state. Defaults to ready when omitted. */\n messagesStatus?: ChatStatus;\n\n /**\n * The open topic, for hosts that own the selection themselves: an identifier opens that\n * topic's conversation, `null` shows the topic list, and leaving the prop unset lets the\n * sidebar keep the selection internally.\n */\n selectedTopicId?: ChatIdentifier | null;\n\n /**\n * Invoked when the open topic changes — a topic was opened (or just started), or the person\n * went back to the list.\n * @param topicId The identifier of the topic now open, or undefined for the list.\n * @param topic The topic itself, when it is known in {@link topics}.\n */\n onTopicSelected?: (topicId: ChatIdentifier | undefined, topic?: TTopic) => void;\n\n /**\n * Invoked when a new topic is asked for. Creating the topic is the host's business; answer\n * with its identifier (directly or through a promise) and the sidebar opens it, ready for\n * the first message. Omit to leave the new-topic affordance off.\n * @returns The identifier of the topic that was started, when the host created one.\n */\n onStartTopic?: () => ChatIdentifier | undefined | Promise<ChatIdentifier | undefined>;\n\n /**\n * Invoked when a message is sent, with the topic it belongs to, the trimmed body and who it\n * mentions. Everything else about persisting the message is the host's business.\n * @param topicId The identifier of the topic the message was sent in.\n * @param body The message text, mentions written into it as plain `@Name`.\n * @param mentions Who the body mentions.\n */\n onSendMessage: (\n topicId: ChatIdentifier,\n body: string,\n mentions: ChatMention[],\n ) => void;\n\n /**\n * Invoked when the very first message is sent in a topic that has no name yet — the host\n * asks its LLM (or whatever it prefers) for a good name and supplies it through the topic\n * data; until then the topic renders its pending placeholder. Not invoked again for later\n * messages, and never for a topic that already has a name.\n * @param topic The topic that needs a name.\n * @param firstMessageBody The message to derive the name from.\n */\n onRequestTopicName?: (topic: TTopic, firstMessageBody: string) => void;\n\n /**\n * Decides whether a topic counts as unnamed — see {@link ChatTopicListProps.isTopicUnnamed}.\n * @param topic The topic to check.\n * @returns True when the topic has no usable name yet.\n */\n isTopicUnnamed?: (topic: TTopic) => boolean;\n\n /** Which edge the sidebar opens from. Defaults to `'right'`. */\n position?: 'left' | 'right';\n\n /** The sidebar's width, any CSS length. Defaults to `'24rem'`. */\n width?: string;\n\n /**\n * Whether the sidebar is modal — a backdrop behind it, and dismissing by Escape or clicking\n * outside. Defaults to false: a chat lives *next to* the work, so it should not block it —\n * the background stays visible and interactive, and only the close/back affordances dismiss it.\n */\n modal?: boolean;\n\n /** Overrides for every label rendered. Unset fields fall back to literal English defaults. */\n labels?: ChatSidebarLabels;\n\n /** Additional class name for the sidebar's panel element. */\n className?: string;\n\n /** Cratis-owned per-part attributes. */\n pt?: ChatSidebarParts;\n}\n\nconst classNames = (...values: Array<string | undefined>) =>\n values.filter(Boolean).join(' ');\n\nconst BackIcon = () => (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2.5'\n aria-hidden='true'\n >\n <line x1='19' y1='12' x2='5' y2='12' />\n <polyline points='12 19 5 12 12 5' />\n </svg>\n);\n\n/**\n * A topic-based chat in a sidebar next to the view, built on Components' own overlay primitives\n * (React Aria internally, for accessible dismissal and focus handling). It opens on the topic\n * list; picking a topic (or starting a new one) slides into that conversation, with a back\n * affordance to the list. Everything about *data* is the host's: topics and messages come in as\n * arrays (hand it live query data and it stays current), sends and new topics go out as\n * callbacks, and when the first message lands in an unnamed topic the sidebar asks the host for\n * a name through {@link ChatSidebarProps.onRequestTopicName | onRequestTopicName} — rendering a\n * pending placeholder until the name arrives.\n */\nexport const ChatSidebar = <\n TMessage extends ChatMessage = ChatMessage,\n TTopic extends ChatTopic = ChatTopic,\n>({\n open,\n onClose,\n topics,\n messages,\n topicActions,\n topicsStatus,\n messagesStatus,\n selectedTopicId,\n onTopicSelected,\n onStartTopic,\n onSendMessage,\n onRequestTopicName,\n isTopicUnnamed,\n position = 'right',\n width = '24rem',\n modal = false,\n labels,\n className,\n pt,\n ...conversation\n}: ChatSidebarProps<TMessage, TTopic>) => {\n const icon = useCratisIcon();\n const overlayEnvironment = unstable_useOverlayEnvironment();\n const [mounted, setMounted] = useState(false);\n const nonModalPanel = useRef<HTMLDivElement>(null);\n const [nonModalPhase, setNonModalPhase] = useState<\n 'closed' | 'entering' | 'open' | 'exiting'\n >('closed');\n\n useEffect(() => setMounted(true), []);\n useEffect(() => {\n setNonModalPhase((phase) =>\n open ? 'entering' : phase === 'closed' ? 'closed' : 'exiting',\n );\n }, [open]);\n const portalContainer = !modal && mounted && nonModalPhase !== 'closed'\n ? overlayEnvironment.getContainer()\n : null;\n useEffect(() => {\n if (nonModalPhase !== 'entering' && nonModalPhase !== 'exiting') return;\n if (!modal && (\n window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ||\n nonModalPanel.current?.getAnimations?.().length === 0\n )) {\n setNonModalPhase(nonModalPhase === 'entering' ? 'open' : 'closed');\n return;\n }\n const timeout = setTimeout(() => {\n setNonModalPhase(nonModalPhase === 'entering' ? 'open' : 'closed');\n }, 250);\n return () => clearTimeout(timeout);\n }, [modal, nonModalPhase, portalContainer]);\n\n const [internalSelectedId, setInternalSelectedId] = useState<\n ChatIdentifier | undefined\n >();\n const isControlled = selectedTopicId !== undefined;\n const openTopicId = isControlled\n ? (selectedTopicId ?? undefined)\n : internalSelectedId;\n const openTopic =\n openTopicId === undefined\n ? undefined\n : topics.find((topic) => sameChatIdentifier(topic.id, openTopicId));\n const unnamed = isTopicUnnamed ?? defaultIsTopicUnnamed;\n\n const select = (topicId: ChatIdentifier | undefined, topic?: TTopic) => {\n if (!isControlled) {\n setInternalSelectedId(topicId);\n }\n onTopicSelected?.(\n topicId,\n topic ??\n (topicId === undefined\n ? undefined\n : topics.find((candidate) =>\n sameChatIdentifier(candidate.id, topicId),\n )),\n );\n };\n\n const startTopic = async () => {\n const topicId = await onStartTopic?.();\n if (topicId !== undefined) {\n select(topicId);\n }\n };\n\n const openMessages =\n openTopicId === undefined\n ? []\n : messages.filter((message) =>\n sameChatIdentifier(message.topicId, openTopicId),\n );\n\n const send = (body: string, mentions: ChatMention[]) => {\n if (openTopicId === undefined) return;\n onSendMessage(openTopicId, body, mentions);\n if (\n openTopic &&\n shouldRequestTopicName(unnamed(openTopic), openMessages.length)\n ) {\n onRequestTopicName?.(openTopic, body);\n }\n };\n\n const title =\n openTopicId === undefined\n ? (labels?.topics ?? 'Topics')\n : openTopic && !unnamed(openTopic)\n ? openTopic.name\n : (labels?.unnamedTopic ?? 'New topic');\n const titleIsPending =\n openTopicId !== undefined && (!openTopic || unnamed(openTopic));\n\n const panel = (\n <>\n <header\n {...pt?.header}\n className={classNames(\n 'cratis-chat-sidebar__header',\n pt?.header?.className,\n )}\n data-cratis-part='header'\n >\n {openTopicId !== undefined && (\n <button\n {...pt?.back}\n type='button'\n className={classNames(\n 'cratis-chat-sidebar__back',\n pt?.back?.className,\n )}\n data-cratis-part='back'\n title={labels?.back ?? 'Back to topics'}\n aria-label={labels?.back ?? 'Back to topics'}\n onClick={() => select(undefined)}\n >\n <BackIcon />\n </button>\n )}\n <h2\n {...pt?.title}\n className={classNames(\n 'cratis-chat-sidebar__title',\n titleIsPending\n ? 'cratis-chat-sidebar__title--pending'\n : undefined,\n pt?.title?.className,\n )}\n data-cratis-part='title'\n >\n {title}\n </h2>\n <button\n {...pt?.close}\n type='button'\n className={classNames(\n 'cratis-chat-sidebar__close',\n pt?.close?.className,\n )}\n data-cratis-part='close'\n aria-label={labels?.close ?? 'Close chat'}\n onClick={onClose}\n >\n <span aria-hidden='true'>{icon('close', '×')}</span>\n </button>\n </header>\n <div\n {...pt?.content}\n className={classNames(\n 'cratis-chat-sidebar__content',\n pt?.content?.className,\n )}\n data-cratis-part='content'\n >\n {openTopicId === undefined ? (\n <ChatTopicList<TTopic>\n topics={topics}\n topicActions={topicActions}\n status={topicsStatus}\n onOpen={(topic) => select(topic.id, topic)}\n onStart={\n onStartTopic\n ? () => {\n void startTopic();\n }\n : undefined\n }\n authorOf={conversation.authorOf}\n renderAvatar={conversation.renderAvatar}\n isTopicUnnamed={isTopicUnnamed}\n buildAvatarUrl={conversation.buildAvatarUrl}\n labels={{\n unnamedTopic: labels?.unnamedTopic,\n ...labels?.topicList,\n }}\n />\n ) : (\n <ChatConversation<TMessage>\n {...conversation}\n messages={openMessages}\n status={messagesStatus}\n onSendMessage={send}\n labels={labels?.conversation}\n />\n )}\n </div>\n </>\n );\n\n return modal ? (\n <ModalOverlay\n {...pt?.backdrop}\n isOpen={open}\n onOpenChange={(openState) => {\n if (!openState) onClose();\n }}\n isDismissable={modal}\n isKeyboardDismissDisabled={!modal}\n className={classNames(\n 'cratis-chat-sidebar__backdrop',\n pt?.backdrop?.className,\n )}\n data-cratis-part='backdrop'\n data-modal={modal}\n data-open={open || undefined}\n data-selected={openTopicId !== undefined || undefined}\n >\n <Modal\n {...pt?.root}\n className={classNames(\n 'cratis-chat-sidebar',\n pt?.root?.className,\n className,\n )}\n style={{ width, ...pt?.root?.style }}\n data-cratis-part='root'\n data-position={position}\n data-open={open || undefined}\n data-selected={openTopicId !== undefined || undefined}\n >\n {panel}\n </Modal>\n </ModalOverlay>\n ) : (\n // Non-modal: a chat lives *next to* the work, so the background must stay visible and\n // interactive. React Aria's Modal blocks its backdrop by design (its dismissal props only\n // gate how it closes, never whether it intercepts), so the default is a plain portaled\n // layer with no dismissal behavior at all — only the close/back affordances dismiss it.\n nonModalPhase !== 'closed' &&\n portalContainer &&\n createPortal(\n <div\n {...pt?.backdrop}\n className={classNames(\n 'cratis-chat-sidebar__backdrop',\n pt?.backdrop?.className,\n )}\n data-cratis-part='backdrop'\n data-modal={false}\n data-open={open || undefined}\n data-selected={openTopicId !== undefined || undefined}\n data-entering={nonModalPhase === 'entering' || undefined}\n data-exiting={nonModalPhase === 'exiting' || undefined}\n >\n <div\n {...pt?.root}\n ref={nonModalPanel}\n className={classNames(\n 'cratis-chat-sidebar',\n pt?.root?.className,\n className,\n )}\n style={{ width, ...pt?.root?.style }}\n data-cratis-part='root'\n data-position={position}\n data-open={open || undefined}\n data-selected={openTopicId !== undefined || undefined}\n data-entering={nonModalPhase === 'entering' || undefined}\n data-exiting={nonModalPhase === 'exiting' || undefined}\n onAnimationEnd={(event) => {\n pt?.root?.onAnimationEnd?.(event);\n if (event.target !== event.currentTarget) return;\n setNonModalPhase((phase) =>\n phase === 'entering' ? 'open' : phase === 'exiting' ? 'closed' : phase,\n );\n }}\n >\n {panel}\n </div>\n </div>,\n portalContainer,\n )\n );\n};\n"],"names":["_jsxs","_jsx","isTopicUnnamed","defaultIsTopicUnnamed","_Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatSidebar.js","sources":["../../../Chat/ChatSidebar.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useEffect, useRef, useState } from 'react';\nimport type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport { Modal, ModalOverlay } from 'react-aria-components';\nimport type { ChatConversationLabels, ChatConversationProps } from './ChatConversation';\nimport { ChatConversation } from './ChatConversation';\nimport { sameChatIdentifier, type ChatIdentifier } from './ChatIdentifier';\nimport type { ChatMention } from './ChatMention';\nimport type { ChatMessage } from './ChatMessage';\nimport type { ChatTopic } from './ChatTopic';\nimport type { ChatTopicAction } from './ChatTopicAction';\nimport type { ChatStatus } from './ChatStatus';\nimport type { ChatTopicListLabels } from './ChatTopicList';\nimport { ChatTopicList } from './ChatTopicList';\nimport { isTopicUnnamed as defaultIsTopicUnnamed } from './isTopicUnnamed';\nimport { shouldRequestTopicName } from './shouldRequestTopicName';\nimport { useCratisIcon } from '../configuration/useCratisIcon';\nimport { unstable_useOverlayEnvironment } from '../renderer/RendererContext';\nimport type { ExactPartKeys } from '../types/ExactPartKeys';\nimport type { PartsOf } from '../types/parts';\n\ntype ChatSidebarPartAttributes<TElement> = HTMLAttributes<TElement> & {\n [attribute: `data-${string}`]: string | number | boolean | undefined;\n};\n\ntype ChatSidebarButtonAttributes = Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n 'onClick' | 'type'\n>;\n\n/** Stable Cratis-owned parts for styling a {@link ChatSidebar}. */\nexport interface ChatSidebarParts {\n /** Viewport overlay layer the sidebar portals into — only paints/blocks when {@link ChatSidebarProps.modal} is true. */\n backdrop?: ChatSidebarPartAttributes<HTMLDivElement>;\n /** The sidebar panel itself. */\n root?: ChatSidebarPartAttributes<HTMLDivElement>;\n /** Header containing the back affordance, title, and close button. */\n header?: ChatSidebarPartAttributes<HTMLElement>;\n /** The topic list title, or the open topic's name. */\n title?: ChatSidebarPartAttributes<HTMLHeadingElement>;\n /** Back-to-topics button, present only while a topic is open. */\n back?: ChatSidebarButtonAttributes;\n /** Close button. */\n close?: ChatSidebarButtonAttributes;\n /** The topic list / conversation content region. */\n content?: ChatSidebarPartAttributes<HTMLDivElement>;\n}\n\nconst chatSidebarPartsMatchManifest: ExactPartKeys<\n ChatSidebarParts,\n PartsOf<'ChatSidebar'>\n> = true;\nvoid chatSidebarPartsMatchManifest;\n\n/** Overrides for every label the {@link ChatSidebar} and its children render. Any field left\n * unset falls back to a literal English default. */\nexport interface ChatSidebarLabels {\n /** The sidebar's title while the topic list is showing. Defaults to `'Topics'`. */\n topics?: string;\n\n /** Placeholder title for a topic that has not been named yet. Defaults to `'New topic'`. */\n unnamedTopic?: string;\n\n /** Tooltip/accessible label for the back-to-topics button. Defaults to `'Back to topics'`. */\n back?: string;\n\n /** Accessible label for the close button. Defaults to `'Close chat'`. */\n close?: string;\n\n /** Labels forwarded to the {@link ChatTopicList}. */\n topicList?: ChatTopicListLabels;\n\n /** Labels forwarded to the {@link ChatConversation}. */\n conversation?: ChatConversationLabels;\n}\n\n/**\n * Props for {@link ChatSidebar}.\n * @typeParam TMessage The host's message type — anything extending {@link ChatMessage}.\n * @typeParam TTopic The host's topic type — anything extending {@link ChatTopic}.\n */\nexport interface ChatSidebarProps<\n TMessage extends ChatMessage = ChatMessage,\n TTopic extends ChatTopic = ChatTopic,\n> extends Omit<\n ChatConversationProps<TMessage>,\n 'messages' | 'onSendMessage' | 'labels' | 'className' | 'status'\n> {\n /**\n * Renders host content in the header, between the title and the close button, for example a\n * rename control for the open topic. Nothing is rendered when it returns null or undefined.\n * @param openTopic The open topic, or undefined while the topic list is shown.\n * @returns What to render in the header.\n */\n renderHeaderActions?: (openTopic: TTopic | undefined) => ReactNode;\n\n /** Whether the sidebar is open. */\n open: boolean;\n\n /** Invoked when the sidebar asks to close — the host owns {@link open}, so closing means the host flips it. */\n onClose: () => void;\n\n /**\n * The topics, in any order. Hand this the array a live (observable) query delivers and the\n * sidebar re-renders as it changes.\n */\n topics: TTopic[];\n\n /**\n * The messages — either the whole chat or just the open topic's; the sidebar shows those of\n * the open topic by matching on `topicId` either way. Hand this the array a live\n * (observable) query delivers and the conversation re-renders as it changes.\n */\n messages: TMessage[];\n\n /** The host's actions to offer on each topic where they are available. The inherited `actions` prop applies only to messages. */\n topicActions?: ChatTopicAction<TTopic>[];\n\n /** Topic-list query display state. Defaults to ready when omitted. */\n topicsStatus?: ChatStatus;\n\n /** Conversation query display state. Defaults to ready when omitted. */\n messagesStatus?: ChatStatus;\n\n /**\n * The open topic, for hosts that own the selection themselves: an identifier opens that\n * topic's conversation, `null` shows the topic list, and leaving the prop unset lets the\n * sidebar keep the selection internally.\n */\n selectedTopicId?: ChatIdentifier | null;\n\n /**\n * Invoked when the open topic changes — a topic was opened (or just started), or the person\n * went back to the list.\n * @param topicId The identifier of the topic now open, or undefined for the list.\n * @param topic The topic itself, when it is known in {@link topics}.\n */\n onTopicSelected?: (topicId: ChatIdentifier | undefined, topic?: TTopic) => void;\n\n /**\n * Invoked when a new topic is asked for. Creating the topic is the host's business; answer\n * with its identifier (directly or through a promise) and the sidebar opens it, ready for\n * the first message. Omit to leave the new-topic affordance off.\n * @returns The identifier of the topic that was started, when the host created one.\n */\n onStartTopic?: () => ChatIdentifier | undefined | Promise<ChatIdentifier | undefined>;\n\n /**\n * Invoked when a message is sent, with the topic it belongs to, the trimmed body and who it\n * mentions. Everything else about persisting the message is the host's business.\n * @param topicId The identifier of the topic the message was sent in.\n * @param body The message text, mentions written into it as plain `@Name`.\n * @param mentions Who the body mentions.\n */\n onSendMessage: (\n topicId: ChatIdentifier,\n body: string,\n mentions: ChatMention[],\n ) => void;\n\n /**\n * Invoked when the very first message is sent in a topic that has no name yet — the host\n * asks its LLM (or whatever it prefers) for a good name and supplies it through the topic\n * data; until then the topic renders its pending placeholder. Not invoked again for later\n * messages, and never for a topic that already has a name.\n * @param topic The topic that needs a name.\n * @param firstMessageBody The message to derive the name from.\n */\n onRequestTopicName?: (topic: TTopic, firstMessageBody: string) => void;\n\n /**\n * Decides whether a topic counts as unnamed — see {@link ChatTopicListProps.isTopicUnnamed}.\n * @param topic The topic to check.\n * @returns True when the topic has no usable name yet.\n */\n isTopicUnnamed?: (topic: TTopic) => boolean;\n\n /** Which edge the sidebar opens from. Defaults to `'right'`. */\n position?: 'left' | 'right';\n\n /** The sidebar's width, any CSS length. Defaults to `'24rem'`. */\n width?: string;\n\n /**\n * Whether the sidebar is modal — a backdrop behind it, and dismissing by Escape or clicking\n * outside. Defaults to false: a chat lives *next to* the work, so it should not block it —\n * the background stays visible and interactive, and only the close/back affordances dismiss it.\n */\n modal?: boolean;\n\n /** Overrides for every label rendered. Unset fields fall back to literal English defaults. */\n labels?: ChatSidebarLabels;\n\n /** Additional class name for the sidebar's panel element. */\n className?: string;\n\n /** Cratis-owned per-part attributes. */\n pt?: ChatSidebarParts;\n}\n\nconst classNames = (...values: Array<string | undefined>) =>\n values.filter(Boolean).join(' ');\n\nconst BackIcon = () => (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2.5'\n aria-hidden='true'\n >\n <line x1='19' y1='12' x2='5' y2='12' />\n <polyline points='12 19 5 12 12 5' />\n </svg>\n);\n\n/**\n * A topic-based chat in a sidebar next to the view, built on Components' own overlay primitives\n * (React Aria internally, for accessible dismissal and focus handling). It opens on the topic\n * list; picking a topic (or starting a new one) slides into that conversation, with a back\n * affordance to the list. Everything about *data* is the host's: topics and messages come in as\n * arrays (hand it live query data and it stays current), sends and new topics go out as\n * callbacks, and when the first message lands in an unnamed topic the sidebar asks the host for\n * a name through {@link ChatSidebarProps.onRequestTopicName | onRequestTopicName} — rendering a\n * pending placeholder until the name arrives.\n */\nexport const ChatSidebar = <\n TMessage extends ChatMessage = ChatMessage,\n TTopic extends ChatTopic = ChatTopic,\n>({\n open,\n onClose,\n topics,\n messages,\n topicActions,\n renderHeaderActions,\n topicsStatus,\n messagesStatus,\n selectedTopicId,\n onTopicSelected,\n onStartTopic,\n onSendMessage,\n onRequestTopicName,\n isTopicUnnamed,\n position = 'right',\n width = '24rem',\n modal = false,\n labels,\n className,\n pt,\n ...conversation\n}: ChatSidebarProps<TMessage, TTopic>) => {\n const icon = useCratisIcon();\n const overlayEnvironment = unstable_useOverlayEnvironment();\n const [mounted, setMounted] = useState(false);\n const nonModalPanel = useRef<HTMLDivElement>(null);\n const [nonModalPhase, setNonModalPhase] = useState<\n 'closed' | 'entering' | 'open' | 'exiting'\n >('closed');\n\n useEffect(() => setMounted(true), []);\n useEffect(() => {\n setNonModalPhase((phase) =>\n open ? 'entering' : phase === 'closed' ? 'closed' : 'exiting',\n );\n }, [open]);\n const portalContainer = !modal && mounted && nonModalPhase !== 'closed'\n ? overlayEnvironment.getContainer()\n : null;\n useEffect(() => {\n if (nonModalPhase !== 'entering' && nonModalPhase !== 'exiting') return;\n if (!modal && (\n window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ||\n nonModalPanel.current?.getAnimations?.().length === 0\n )) {\n setNonModalPhase(nonModalPhase === 'entering' ? 'open' : 'closed');\n return;\n }\n const timeout = setTimeout(() => {\n setNonModalPhase(nonModalPhase === 'entering' ? 'open' : 'closed');\n }, 250);\n return () => clearTimeout(timeout);\n }, [modal, nonModalPhase, portalContainer]);\n\n const [internalSelectedId, setInternalSelectedId] = useState<\n ChatIdentifier | undefined\n >();\n const isControlled = selectedTopicId !== undefined;\n const openTopicId = isControlled\n ? (selectedTopicId ?? undefined)\n : internalSelectedId;\n const openTopic =\n openTopicId === undefined\n ? undefined\n : topics.find((topic) => sameChatIdentifier(topic.id, openTopicId));\n const unnamed = isTopicUnnamed ?? defaultIsTopicUnnamed;\n\n const select = (topicId: ChatIdentifier | undefined, topic?: TTopic) => {\n if (!isControlled) {\n setInternalSelectedId(topicId);\n }\n onTopicSelected?.(\n topicId,\n topic ??\n (topicId === undefined\n ? undefined\n : topics.find((candidate) =>\n sameChatIdentifier(candidate.id, topicId),\n )),\n );\n };\n\n const startTopic = async () => {\n const topicId = await onStartTopic?.();\n if (topicId !== undefined) {\n select(topicId);\n }\n };\n\n const openMessages =\n openTopicId === undefined\n ? []\n : messages.filter((message) =>\n sameChatIdentifier(message.topicId, openTopicId),\n );\n\n const send = (body: string, mentions: ChatMention[]) => {\n if (openTopicId === undefined) return;\n onSendMessage(openTopicId, body, mentions);\n if (\n openTopic &&\n shouldRequestTopicName(unnamed(openTopic), openMessages.length)\n ) {\n onRequestTopicName?.(openTopic, body);\n }\n };\n\n const title =\n openTopicId === undefined\n ? (labels?.topics ?? 'Topics')\n : openTopic && !unnamed(openTopic)\n ? openTopic.name\n : (labels?.unnamedTopic ?? 'New topic');\n const titleIsPending =\n openTopicId !== undefined && (!openTopic || unnamed(openTopic));\n\n const panel = (\n <>\n <header\n {...pt?.header}\n className={classNames(\n 'cratis-chat-sidebar__header',\n pt?.header?.className,\n )}\n data-cratis-part='header'\n >\n {openTopicId !== undefined && (\n <button\n {...pt?.back}\n type='button'\n className={classNames(\n 'cratis-chat-sidebar__back',\n pt?.back?.className,\n )}\n data-cratis-part='back'\n title={labels?.back ?? 'Back to topics'}\n aria-label={labels?.back ?? 'Back to topics'}\n onClick={() => select(undefined)}\n >\n <BackIcon />\n </button>\n )}\n <h2\n {...pt?.title}\n className={classNames(\n 'cratis-chat-sidebar__title',\n titleIsPending\n ? 'cratis-chat-sidebar__title--pending'\n : undefined,\n pt?.title?.className,\n )}\n data-cratis-part='title'\n >\n {title}\n </h2>\n {renderHeaderActions?.(openTopic)}\n <button\n {...pt?.close}\n type='button'\n className={classNames(\n 'cratis-chat-sidebar__close',\n pt?.close?.className,\n )}\n data-cratis-part='close'\n aria-label={labels?.close ?? 'Close chat'}\n onClick={onClose}\n >\n <span aria-hidden='true'>{icon('close', '×')}</span>\n </button>\n </header>\n <div\n {...pt?.content}\n className={classNames(\n 'cratis-chat-sidebar__content',\n pt?.content?.className,\n )}\n data-cratis-part='content'\n >\n {openTopicId === undefined ? (\n <ChatTopicList<TTopic>\n topics={topics}\n topicActions={topicActions}\n status={topicsStatus}\n onOpen={(topic) => select(topic.id, topic)}\n onStart={\n onStartTopic\n ? () => {\n void startTopic();\n }\n : undefined\n }\n authorOf={conversation.authorOf}\n renderAvatar={conversation.renderAvatar}\n isTopicUnnamed={isTopicUnnamed}\n buildAvatarUrl={conversation.buildAvatarUrl}\n labels={{\n unnamedTopic: labels?.unnamedTopic,\n ...labels?.topicList,\n }}\n />\n ) : (\n <ChatConversation<TMessage>\n {...conversation}\n messages={openMessages}\n status={messagesStatus}\n onSendMessage={send}\n labels={labels?.conversation}\n />\n )}\n </div>\n </>\n );\n\n return modal ? (\n <ModalOverlay\n {...pt?.backdrop}\n isOpen={open}\n onOpenChange={(openState) => {\n if (!openState) onClose();\n }}\n isDismissable={modal}\n isKeyboardDismissDisabled={!modal}\n className={classNames(\n 'cratis-chat-sidebar__backdrop',\n pt?.backdrop?.className,\n )}\n data-cratis-part='backdrop'\n data-modal={modal}\n data-open={open || undefined}\n data-selected={openTopicId !== undefined || undefined}\n >\n <Modal\n {...pt?.root}\n className={classNames(\n 'cratis-chat-sidebar',\n pt?.root?.className,\n className,\n )}\n style={{ width, ...pt?.root?.style }}\n data-cratis-part='root'\n data-position={position}\n data-open={open || undefined}\n data-selected={openTopicId !== undefined || undefined}\n >\n {panel}\n </Modal>\n </ModalOverlay>\n ) : (\n // Non-modal: a chat lives *next to* the work, so the background must stay visible and\n // interactive. React Aria's Modal blocks its backdrop by design (its dismissal props only\n // gate how it closes, never whether it intercepts), so the default is a plain portaled\n // layer with no dismissal behavior at all — only the close/back affordances dismiss it.\n nonModalPhase !== 'closed' &&\n portalContainer &&\n createPortal(\n <div\n {...pt?.backdrop}\n className={classNames(\n 'cratis-chat-sidebar__backdrop',\n pt?.backdrop?.className,\n )}\n data-cratis-part='backdrop'\n data-modal={false}\n data-open={open || undefined}\n data-selected={openTopicId !== undefined || undefined}\n data-entering={nonModalPhase === 'entering' || undefined}\n data-exiting={nonModalPhase === 'exiting' || undefined}\n >\n <div\n {...pt?.root}\n ref={nonModalPanel}\n className={classNames(\n 'cratis-chat-sidebar',\n pt?.root?.className,\n className,\n )}\n style={{ width, ...pt?.root?.style }}\n data-cratis-part='root'\n data-position={position}\n data-open={open || undefined}\n data-selected={openTopicId !== undefined || undefined}\n data-entering={nonModalPhase === 'entering' || undefined}\n data-exiting={nonModalPhase === 'exiting' || undefined}\n onAnimationEnd={(event) => {\n pt?.root?.onAnimationEnd?.(event);\n if (event.target !== event.currentTarget) return;\n setNonModalPhase((phase) =>\n phase === 'entering' ? 'open' : phase === 'exiting' ? 'closed' : phase,\n );\n }}\n >\n {panel}\n </div>\n </div>,\n portalContainer,\n )\n );\n};\n"],"names":["_jsxs","_jsx","isTopicUnnamed","defaultIsTopicUnnamed","_Fragment"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionMenubar.d.ts","sourceRoot":"","sources":["../../../Common/ActionMenubar.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,cAAc,EAAmB,MAAM,aAAU,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"ActionMenubar.d.ts","sourceRoot":"","sources":["../../../Common/ActionMenubar.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,cAAc,EAAmB,MAAM,aAAU,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAoB,CAAC;AAItD,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC3B,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,SAAS,CAAC;CAClD;AAED,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IAC/B,0EAA0E;IAC1E,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,4CAA4C;IAC5C,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,EAAE,CAAC,EAAE,WAAW,CAAC;IACjB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAkCD,2DAA2D;AAC3D,eAAO,MAAM,aAAa,kEAMvB,kBAAkB,gCAmBpB,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { Fragment } from 'react';
|
|
|
3
3
|
import { Button } from './Button.js';
|
|
4
4
|
import { ToolbarFocusMode } from './ToolbarFocusMode.js';
|
|
5
5
|
import { useToolbarKeyboardNavigation } from './useToolbarKeyboardNavigation.js';
|
|
6
|
+
import { ToolbarRovingContext, useRovingTool } from './ToolbarRovingContext.js';
|
|
6
7
|
|
|
7
8
|
const buttonToneForSeverity = {
|
|
8
9
|
secondary: 'neutral',
|
|
@@ -13,13 +14,17 @@ const buttonToneForSeverity = {
|
|
|
13
14
|
danger: 'critical',
|
|
14
15
|
contrast: 'neutral',
|
|
15
16
|
};
|
|
17
|
+
const ActionMenubarButton = ({ item, pt }) => {
|
|
18
|
+
const rovingTool = useRovingTool(pt?.root?.tabIndex, pt?.root?.onFocus);
|
|
19
|
+
return (jsx(Button, { variant: 'ghost', tone: item.severity ? buttonToneForSeverity[item.severity] : undefined, onClick: item.command, disabled: item.disabled, className: item.className, icon: item.icon, label: item.label, pt: pt, ref: rovingTool.ref, tabIndex: rovingTool.tabIndex, onFocus: rovingTool.onFocus }));
|
|
20
|
+
};
|
|
16
21
|
const ActionMenubar = ({ model, focusMode = ToolbarFocusMode.Arrows, className, pt, 'aria-label': ariaLabel, }) => {
|
|
17
|
-
const { rootRef, onKeyDown } = useToolbarKeyboardNavigation('horizontal', focusMode);
|
|
18
|
-
return jsx("div", { ref: rootRef, onKeyDown: onKeyDown, role: 'toolbar', className: ['cratis-action-menubar', className].filter(Boolean).join(' '), "data-cratis-part": 'root', "aria-label": ariaLabel, children: model.map((item, index) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
const { rootRef, onKeyDown, roving } = useToolbarKeyboardNavigation('horizontal', focusMode);
|
|
23
|
+
return jsx("div", { ref: rootRef, onKeyDown: onKeyDown, role: 'toolbar', className: ['cratis-action-menubar', className].filter(Boolean).join(' '), "data-cratis-part": 'root', "aria-label": ariaLabel, children: jsx(ToolbarRovingContext.Provider, { value: roving, children: model.map((item, index) => {
|
|
24
|
+
if (item.template)
|
|
25
|
+
return jsx(Fragment, { children: item.template(item) }, index);
|
|
26
|
+
return jsx(ActionMenubarButton, { item: item, pt: pt }, index);
|
|
27
|
+
}) }) });
|
|
23
28
|
};
|
|
24
29
|
|
|
25
30
|
export { ActionMenubar };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionMenubar.js","sources":["../../../Common/ActionMenubar.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { Fragment, type ReactNode } from 'react';\nimport { Button, type ButtonParts, type ButtonSeverity, type ButtonTone } from './Button';\nimport { ToolbarFocusMode } from './ToolbarFocusMode';\nimport { useToolbarKeyboardNavigation } from './useToolbarKeyboardNavigation';\n\n/** A single action in an {@link ActionMenubar}. */\nexport interface ActionMenuItem {\n /** The visible label. */\n label?: string;\n /** An icon element rendered before the label. */\n icon?: ReactNode;\n /** Invoked when the item is activated. */\n command?: () => void;\n /** When true, the item is greyed out and not clickable. */\n disabled?: boolean;\n /** Extra class name for the item. */\n className?: string;\n /** Severity styling for the action. */\n severity?: ButtonSeverity;\n /** Fully custom render for this item. */\n template?: (item: ActionMenuItem) => ReactNode;\n}\n\n/** Props for {@link ActionMenubar}. */\nexport interface ActionMenubarProps {\n /** Keyboard focus behavior (default: {@link ToolbarFocusMode.Arrows}). */\n focusMode?: ToolbarFocusMode;\n /** Actions to render from left to right. */\n model: ActionMenuItem[];\n /** Extra class name for the toolbar container. */\n className?: string;\n /** Accessible label for the toolbar. */\n 'aria-label'?: string;\n /** Cratis-owned per-part attributes applied to each action button. */\n pt?: ButtonParts;\n /**\n * @deprecated Cratis parts always merge. Remove this renderer-era option.\n */\n ptOptions?: object;\n /**\n * @deprecated Components always uses consumer-owned CSS. Customize through `pt` and CSS instead.\n */\n unstyled?: boolean;\n}\n\nconst buttonToneForSeverity: Record<ButtonSeverity, ButtonTone> = {\n secondary: 'neutral',\n info: 'accent',\n help: 'accent',\n success: 'positive',\n warn: 'caution',\n danger: 'critical',\n contrast: 'neutral',\n};\n\n/** A horizontal, accessible toolbar of command actions. */\nexport const ActionMenubar = ({\n model,\n focusMode = ToolbarFocusMode.Arrows,\n className,\n pt,\n 'aria-label': ariaLabel,\n}: ActionMenubarProps) => {\n const { rootRef, onKeyDown } = useToolbarKeyboardNavigation('horizontal', focusMode);\n return <div\n ref={rootRef}\n onKeyDown={onKeyDown}\n role='toolbar'\n className={['cratis-action-menubar', className].filter(Boolean).join(' ')}\n data-cratis-part='root'\n aria-label={ariaLabel}\n >\n {model.map((item, index) => {\n if (item.template)\n return <Fragment key={index}>{item.template(item)}</Fragment>;\n\n return
|
|
1
|
+
{"version":3,"file":"ActionMenubar.js","sources":["../../../Common/ActionMenubar.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { Fragment, type ReactNode } from 'react';\nimport { Button, type ButtonParts, type ButtonSeverity, type ButtonTone } from './Button';\nimport { ToolbarFocusMode } from './ToolbarFocusMode';\nimport { useToolbarKeyboardNavigation } from './useToolbarKeyboardNavigation';\nimport { ToolbarRovingContext, useRovingTool } from './ToolbarRovingContext';\n\n/** A single action in an {@link ActionMenubar}. */\nexport interface ActionMenuItem {\n /** The visible label. */\n label?: string;\n /** An icon element rendered before the label. */\n icon?: ReactNode;\n /** Invoked when the item is activated. */\n command?: () => void;\n /** When true, the item is greyed out and not clickable. */\n disabled?: boolean;\n /** Extra class name for the item. */\n className?: string;\n /** Severity styling for the action. */\n severity?: ButtonSeverity;\n /** Fully custom render for this item. */\n template?: (item: ActionMenuItem) => ReactNode;\n}\n\n/** Props for {@link ActionMenubar}. */\nexport interface ActionMenubarProps {\n /** Keyboard focus behavior (default: {@link ToolbarFocusMode.Arrows}). */\n focusMode?: ToolbarFocusMode;\n /** Actions to render from left to right. */\n model: ActionMenuItem[];\n /** Extra class name for the toolbar container. */\n className?: string;\n /** Accessible label for the toolbar. */\n 'aria-label'?: string;\n /** Cratis-owned per-part attributes applied to each action button. */\n pt?: ButtonParts;\n /**\n * @deprecated Cratis parts always merge. Remove this renderer-era option.\n */\n ptOptions?: object;\n /**\n * @deprecated Components always uses consumer-owned CSS. Customize through `pt` and CSS instead.\n */\n unstyled?: boolean;\n}\n\nconst buttonToneForSeverity: Record<ButtonSeverity, ButtonTone> = {\n secondary: 'neutral',\n info: 'accent',\n help: 'accent',\n success: 'positive',\n warn: 'caution',\n danger: 'critical',\n contrast: 'neutral',\n};\n\n/** One Components-owned action button, which takes part in the menubar's single Tab stop. */\nconst ActionMenubarButton = ({ item, pt }: { item: ActionMenuItem; pt: ActionMenubarProps['pt'] }) => {\n // The consumer's pt.root tab index and focus handler are composed, because props passed\n // directly to Button take precedence over pt.root.\n const rovingTool = useRovingTool<HTMLButtonElement>(pt?.root?.tabIndex, pt?.root?.onFocus);\n return (\n <Button\n variant='ghost'\n tone={item.severity ? buttonToneForSeverity[item.severity] : undefined}\n onClick={item.command}\n disabled={item.disabled}\n className={item.className}\n icon={item.icon}\n label={item.label}\n pt={pt}\n ref={rovingTool.ref}\n tabIndex={rovingTool.tabIndex}\n onFocus={rovingTool.onFocus}\n />\n );\n};\n\n/** A horizontal, accessible toolbar of command actions. */\nexport const ActionMenubar = ({\n model,\n focusMode = ToolbarFocusMode.Arrows,\n className,\n pt,\n 'aria-label': ariaLabel,\n}: ActionMenubarProps) => {\n const { rootRef, onKeyDown, roving } = useToolbarKeyboardNavigation('horizontal', focusMode);\n return <div\n ref={rootRef}\n onKeyDown={onKeyDown}\n role='toolbar'\n className={['cratis-action-menubar', className].filter(Boolean).join(' ')}\n data-cratis-part='root'\n aria-label={ariaLabel}\n >\n <ToolbarRovingContext.Provider value={roving}>\n {model.map((item, index) => {\n if (item.template)\n return <Fragment key={index}>{item.template(item)}</Fragment>;\n\n return <ActionMenubarButton key={index} item={item} pt={pt} />;\n })}\n </ToolbarRovingContext.Provider>\n </div>;\n};\n"],"names":["_jsx"],"mappings":";;;;;;;AAiDA,MAAM,qBAAqB,GAAuC;AAC9D,IAAA,SAAS,EAAE,SAAS;AACpB,IAAA,IAAI,EAAE,QAAQ;AACd,IAAA,IAAI,EAAE,QAAQ;AACd,IAAA,OAAO,EAAE,UAAU;AACnB,IAAA,IAAI,EAAE,SAAS;AACf,IAAA,MAAM,EAAE,UAAU;AAClB,IAAA,QAAQ,EAAE,SAAS;CACtB;AAGD,MAAM,mBAAmB,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,EAA0D,KAAI;AAGjG,IAAA,MAAM,UAAU,GAAG,aAAa,CAAoB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC;AAC1F,IAAA,QACIA,GAAA,CAAC,MAAM,EAAA,EACH,OAAO,EAAC,OAAO,EACf,IAAI,EAAE,IAAI,CAAC,QAAQ,GAAG,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,SAAS,EACtE,OAAO,EAAE,IAAI,CAAC,OAAO,EACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,SAAS,EAAE,IAAI,CAAC,SAAS,EACzB,IAAI,EAAE,IAAI,CAAC,IAAI,EACf,KAAK,EAAE,IAAI,CAAC,KAAK,EACjB,EAAE,EAAE,EAAE,EACN,GAAG,EAAE,UAAU,CAAC,GAAG,EACnB,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAC7B,OAAO,EAAE,UAAU,CAAC,OAAO,EAAA,CAC7B;AAEV,CAAC;MAGY,aAAa,GAAG,CAAC,EAC1B,KAAK,EACL,SAAS,GAAG,gBAAgB,CAAC,MAAM,EACnC,SAAS,EACT,EAAE,EACF,YAAY,EAAE,SAAS,GACN,KAAI;AACrB,IAAA,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,4BAA4B,CAAC,YAAY,EAAE,SAAS,CAAC;IAC5F,OAAOA,GAAA,CAAA,KAAA,EAAA,EACH,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,SAAS,EACpB,IAAI,EAAC,SAAS,EACd,SAAS,EAAE,CAAC,uBAAuB,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAA,kBAAA,EACxD,MAAM,EAAA,YAAA,EACX,SAAS,EAAA,QAAA,EAErBA,GAAA,CAAC,oBAAoB,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,MAAM,EAAA,QAAA,EAC3C,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,KAAI;gBACvB,IAAI,IAAI,CAAC,QAAQ;AACb,oBAAA,OAAOA,GAAA,CAAC,QAAQ,EAAA,EAAA,QAAA,EAAc,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAA,EAA3B,KAAK,CAAkC;AAEjE,gBAAA,OAAOA,GAAA,CAAC,mBAAmB,EAAA,EAAa,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAA,EAAzB,KAAK,CAAwB;YAClE,CAAC,CAAC,EAAA,CAC8B,EAAA,CAC9B;AACV;;"}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
/** Keyboard focus behavior for Toolbar and ActionMenubar.
|
|
1
|
+
/** Keyboard focus behavior for Toolbar and ActionMenubar. */
|
|
2
2
|
export declare enum ToolbarFocusMode {
|
|
3
3
|
/** Navigate with arrows and Home/End while retaining every tool's Tab stop (default). */
|
|
4
4
|
Arrows = "arrows",
|
|
5
|
+
/**
|
|
6
|
+
* Navigate with arrows and Home/End, with one Tab stop for the toolbar's own tools that
|
|
7
|
+
* returns to the last focused tool (the WAI-ARIA toolbar pattern). Controls you add yourself,
|
|
8
|
+
* widgets such as inputs and sliders, tools rendered outside the toolbar's own markup (for example
|
|
9
|
+
* through a portal), and tools with an explicit tab index keep their own Tab stop. An explicit
|
|
10
|
+
* tab index of -1 removes the tool from both Tab and arrow-key navigation.
|
|
11
|
+
*/
|
|
12
|
+
SingleTabStop = "singleTabStop",
|
|
5
13
|
/** Keep native focus behavior with no toolbar keyboard handling. */
|
|
6
14
|
None = "none"
|
|
7
15
|
}
|