@gravity-ui/aikit 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/molecules/Suggestions/Suggestions.js +1 -1
- package/dist/components/organisms/Header/Header.js +2 -2
- package/dist/components/organisms/Header/types.d.ts +1 -0
- package/dist/components/organisms/Header/useHeader.d.ts +1 -0
- package/dist/components/organisms/Header/useHeader.js +2 -1
- package/dist/components/pages/ChatContainer/ChatContainer.js +27 -14
- package/dist/components/pages/ChatContainer/i18n/en.json +1 -1
- package/dist/components/pages/ChatContainer/i18n/index.d.ts +4 -4
- package/dist/components/pages/ChatContainer/i18n/ru.json +1 -1
- package/dist/components/pages/ChatContainer/types.d.ts +7 -0
- package/dist/components/templates/History/History.d.ts +2 -0
- package/dist/components/templates/History/History.js +2 -2
- package/dist/components/templates/History/History.scss +2 -1
- package/dist/components/templates/History/HistoryList.d.ts +2 -0
- package/dist/components/templates/History/HistoryList.js +9 -3
- package/dist/components/templates/History/i18n/en.json +1 -0
- package/dist/components/templates/History/i18n/index.d.ts +4 -4
- package/dist/components/templates/History/i18n/ru.json +1 -0
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@ export function Suggestions(props) {
|
|
|
18
18
|
onClick(item.title, item.id);
|
|
19
19
|
};
|
|
20
20
|
const renderButton = (item, index) => {
|
|
21
|
-
return (_jsx(ActionButton, { tooltipTitle: item.title, size: "m", view: item.view || 'outlined', onClick: () => handleClick(item), className: b('button', { layout }), width: "auto", children: _jsxs("div", { className: b('button-content', {
|
|
21
|
+
return (_jsx(ActionButton, { tooltipTitle: wrapText ? undefined : item.title, size: "m", view: item.view || 'outlined', onClick: () => handleClick(item), className: b('button', { layout }), width: "auto", children: _jsxs("div", { className: b('button-content', {
|
|
22
22
|
layout,
|
|
23
23
|
'text-align': item.icon ? undefined : textAlign,
|
|
24
24
|
}), children: [item.icon === 'left' && (_jsx("div", { className: b('button-icon'), children: _jsx(Icon, { data: ChevronLeft, size: 16 }) })), _jsx(Text, { as: "div", className: b(wrapText ? 'button-text-wrap' : 'button-text'), children: item.title }), item.icon === 'right' && (_jsx("div", { className: b('button-icon'), children: _jsx(Icon, { data: ChevronRight, size: 16 }) }))] }) }, item.id || index));
|
|
@@ -29,7 +29,7 @@ const FOLDING_ICONS = {
|
|
|
29
29
|
* @returns Header component
|
|
30
30
|
*/
|
|
31
31
|
export function Header(props) {
|
|
32
|
-
const { title, preview, icon, baseActions, additionalActions, titlePosition, withIcon, className, historyButtonRef, } = useHeader(props);
|
|
32
|
+
const { title, preview, icon, baseActions, additionalActions, titlePosition, withIcon, showTitle = true, className, historyButtonRef, } = useHeader(props);
|
|
33
33
|
// Render base action
|
|
34
34
|
const renderBaseAction = (action) => {
|
|
35
35
|
let IconComponent = ACTION_ICONS[action.id];
|
|
@@ -62,5 +62,5 @@ export function Header(props) {
|
|
|
62
62
|
// Determine class for title positioning
|
|
63
63
|
const titlePositionClass = b('title-container', { position: titlePosition });
|
|
64
64
|
const iconElement = icon ? (_jsx("div", { className: b('icon'), children: icon })) : (_jsx(Icon, { data: Sparkles, size: 16 }));
|
|
65
|
-
return (_jsxs("div", { className: b('', className), children: [withIcon && iconElement, _jsxs("div", { className: titlePositionClass, children: [title && (_jsx(Text, { as: "div", variant: "subheader-2", className: b('title'), children: title })), preview && _jsx("div", { className: b('preview'), children: preview })] }), _jsxs(ButtonGroup, { children: [additionalActions.map((action, index) => renderAdditionalAction(action, index)), baseActions.map((action) => renderBaseAction(action))] })] }));
|
|
65
|
+
return (_jsxs("div", { className: b('', className), children: [withIcon && iconElement, showTitle && (_jsxs("div", { className: titlePositionClass, children: [title && (_jsx(Text, { as: "div", variant: "subheader-2", className: b('title'), children: title })), preview && _jsx("div", { className: b('preview'), children: preview })] })), _jsxs(ButtonGroup, { children: [additionalActions.map((action, index) => renderAdditionalAction(action, index)), baseActions.map((action) => renderBaseAction(action))] })] }));
|
|
66
66
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { HeaderAction } from './types';
|
|
3
3
|
export function useHeader(props) {
|
|
4
|
-
const { icon, title, preview, baseActions = [], handleNewChat, handleHistoryToggle, handleClose, handleFolding, foldingState = 'opened', additionalActions = [], titlePosition = 'left', withIcon = true, className, historyButtonRef, } = props;
|
|
4
|
+
const { icon, title, preview, baseActions = [], handleNewChat, handleHistoryToggle, handleClose, handleFolding, foldingState = 'opened', additionalActions = [], titlePosition = 'left', withIcon = true, showTitle = true, className, historyButtonRef, } = props;
|
|
5
5
|
// Build base actions
|
|
6
6
|
const baseActionsList = useMemo(() => {
|
|
7
7
|
const actions = [];
|
|
@@ -66,6 +66,7 @@ export function useHeader(props) {
|
|
|
66
66
|
additionalActions: additionalActionsList,
|
|
67
67
|
titlePosition,
|
|
68
68
|
withIcon,
|
|
69
|
+
showTitle,
|
|
69
70
|
className,
|
|
70
71
|
historyButtonRef,
|
|
71
72
|
};
|
|
@@ -19,7 +19,7 @@ const b = block('chat-container');
|
|
|
19
19
|
*/
|
|
20
20
|
export function ChatContainer(props) {
|
|
21
21
|
var _a, _b;
|
|
22
|
-
const { chats = [], messages = [], onSendMessage, onDeleteChat, onCancel, onRetry, status = 'ready', error = null, showActionsOnHover = false, contextItems = [], transformOptions, messageListConfig, headerProps = {}, contentProps = {}, emptyContainerProps = {}, promptInputProps = {}, historyProps = {}, welcomeConfig, i18nConfig = {}, className, headerClassName, contentClassName, footerClassName, qa, } = props;
|
|
22
|
+
const { chats = [], messages = [], onSendMessage, onDeleteChat, onCancel, onRetry, status = 'ready', error = null, showActionsOnHover = false, contextItems = [], transformOptions, messageListConfig, headerProps = {}, contentProps = {}, emptyContainerProps = {}, promptInputProps = {}, disclaimerProps = {}, historyProps = {}, welcomeConfig, i18nConfig = {}, hideTitleOnEmptyChat = false, className, headerClassName, contentClassName, footerClassName, qa, } = props;
|
|
23
23
|
const hookState = useChatContainer(props);
|
|
24
24
|
// Collect i18n texts with overrides
|
|
25
25
|
const headerTitle = useMemo(() => {
|
|
@@ -29,9 +29,24 @@ export function ChatContainer(props) {
|
|
|
29
29
|
((_b = hookState.activeChat) === null || _b === void 0 ? void 0 : _b.name) ||
|
|
30
30
|
i18n('header-default-title');
|
|
31
31
|
}, [(_a = i18nConfig.header) === null || _a === void 0 ? void 0 : _a.defaultTitle, headerProps.title, (_b = hookState.activeChat) === null || _b === void 0 ? void 0 : _b.name]);
|
|
32
|
+
// Determine if chat is empty
|
|
33
|
+
const isChatEmpty = hookState.chatContentView === 'empty';
|
|
34
|
+
// Calculate showTitle based on hideTitleOnEmptyChat option
|
|
35
|
+
const showTitle = useMemo(() => {
|
|
36
|
+
// If explicit showTitle is provided in headerProps, use it
|
|
37
|
+
if (headerProps.showTitle !== undefined) {
|
|
38
|
+
return headerProps.showTitle;
|
|
39
|
+
}
|
|
40
|
+
// If hideTitleOnEmptyChat is enabled, show title only when chat has messages
|
|
41
|
+
if (hideTitleOnEmptyChat && isChatEmpty) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
}, [hideTitleOnEmptyChat, isChatEmpty, headerProps.showTitle]);
|
|
32
46
|
// Build props for Header
|
|
33
|
-
const finalHeaderProps = useMemo(() => (Object.assign(Object.assign({}, headerProps), { title: headerTitle, baseActions: hookState.baseActions, handleNewChat: hookState.handleNewChat, handleHistoryToggle: hookState.handleHistoryToggle, handleClose: hookState.handleClose, historyButtonRef: hookState.historyButtonRef })), [
|
|
47
|
+
const finalHeaderProps = useMemo(() => (Object.assign(Object.assign({}, headerProps), { title: headerTitle, showTitle, baseActions: hookState.baseActions, handleNewChat: hookState.handleNewChat, handleHistoryToggle: hookState.handleHistoryToggle, handleClose: hookState.handleClose, historyButtonRef: hookState.historyButtonRef })), [
|
|
34
48
|
headerTitle,
|
|
49
|
+
showTitle,
|
|
35
50
|
hookState.baseActions,
|
|
36
51
|
hookState.handleNewChat,
|
|
37
52
|
hookState.handleHistoryToggle,
|
|
@@ -44,9 +59,7 @@ export function ChatContainer(props) {
|
|
|
44
59
|
var _a, _b, _c, _d;
|
|
45
60
|
return Object.assign(Object.assign({}, emptyContainerProps), { image: welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.image, title: (welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.title) || ((_a = i18nConfig.emptyState) === null || _a === void 0 ? void 0 : _a.title) || i18n('empty-state-title'), description: (welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.description) ||
|
|
46
61
|
((_b = i18nConfig.emptyState) === null || _b === void 0 ? void 0 : _b.description) ||
|
|
47
|
-
i18n('empty-state-description'), suggestionTitle: (welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.suggestionTitle) ||
|
|
48
|
-
((_c = i18nConfig.emptyState) === null || _c === void 0 ? void 0 : _c.suggestionsTitle) ||
|
|
49
|
-
i18n('empty-state-suggestions-title'), suggestions: welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.suggestions, alignment: welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.alignment, layout: welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.layout, wrapText: welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.wrapText, showMore: welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.showMore, showMoreText: (welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.showMoreText) ||
|
|
62
|
+
i18n('empty-state-description'), suggestionTitle: (welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.suggestionTitle) || ((_c = i18nConfig.emptyState) === null || _c === void 0 ? void 0 : _c.suggestionsTitle), suggestions: welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.suggestions, alignment: welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.alignment, layout: welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.layout, wrapText: welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.wrapText, showMore: welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.showMore, showMoreText: (welcomeConfig === null || welcomeConfig === void 0 ? void 0 : welcomeConfig.showMoreText) ||
|
|
50
63
|
((_d = i18nConfig.emptyState) === null || _d === void 0 ? void 0 : _d.showMoreText) ||
|
|
51
64
|
i18n('empty-state-show-more'), onSuggestionClick: async (clickedTitle) => {
|
|
52
65
|
await onSendMessage({ content: clickedTitle });
|
|
@@ -74,21 +87,21 @@ export function ChatContainer(props) {
|
|
|
74
87
|
promptInputProps,
|
|
75
88
|
]);
|
|
76
89
|
// Build props for Disclaimer
|
|
77
|
-
const
|
|
90
|
+
const finalDisclaimerProps = useMemo(() => {
|
|
78
91
|
var _a;
|
|
79
92
|
const disclaimerText = ((_a = i18nConfig.disclaimer) === null || _a === void 0 ? void 0 : _a.text) || i18n('disclaimer-text');
|
|
80
|
-
return {
|
|
81
|
-
|
|
82
|
-
};
|
|
83
|
-
}, [i18nConfig.disclaimer]);
|
|
93
|
+
return Object.assign(Object.assign({}, disclaimerProps), { text: disclaimerProps.text || disclaimerText });
|
|
94
|
+
}, [i18nConfig.disclaimer, disclaimerProps]);
|
|
84
95
|
// Build props for ChatContent
|
|
85
96
|
const finalContentProps = useMemo(() => (Object.assign(Object.assign({}, contentProps), { view: hookState.chatContentView, emptyContainerProps: finalEmptyContainerProps, messageListProps })), [hookState.chatContentView, finalEmptyContainerProps, messageListProps, contentProps]);
|
|
86
97
|
// Build props for History
|
|
87
98
|
const finalHistoryProps = useMemo(() => {
|
|
88
|
-
var _a;
|
|
99
|
+
var _a, _b;
|
|
89
100
|
return (Object.assign(Object.assign({}, historyProps), { chats, selectedChat: hookState.activeChat, onSelectChat: hookState.handleSelectChat, onDeleteChat, open: hookState.isHistoryOpen, onOpenChange: hookState.handleHistoryOpenChange, anchorElement: hookState.historyButtonRef.current, emptyPlaceholder: ((_a = i18nConfig.history) === null || _a === void 0 ? void 0 : _a.emptyPlaceholder) ||
|
|
90
101
|
historyProps.emptyPlaceholder ||
|
|
91
|
-
i18n('history-empty')
|
|
102
|
+
i18n('history-empty'), emptyFilteredPlaceholder: ((_b = i18nConfig.history) === null || _b === void 0 ? void 0 : _b.emptyFilteredPlaceholder) ||
|
|
103
|
+
historyProps.emptyFilteredPlaceholder ||
|
|
104
|
+
i18n('history-empty-filtered') }));
|
|
92
105
|
}, [
|
|
93
106
|
chats,
|
|
94
107
|
hookState.activeChat,
|
|
@@ -100,6 +113,6 @@ export function ChatContainer(props) {
|
|
|
100
113
|
i18nConfig.history,
|
|
101
114
|
historyProps,
|
|
102
115
|
]);
|
|
103
|
-
const showFooter = finalPromptInputProps ||
|
|
104
|
-
return (_jsxs("div", { className: b(null, className), "data-qa": qa, children: [_jsx("div", { className: b('header', headerClassName), children: _jsx(Header, Object.assign({}, finalHeaderProps)) }), _jsx("div", { className: b('content', { view: hookState.chatContentView }, contentClassName), children: _jsx(ChatContent, Object.assign({}, finalContentProps)) }), showFooter && (_jsxs("div", { className: b('footer', { view: hookState.chatContentView }, footerClassName), children: [finalPromptInputProps && _jsx(PromptInput, Object.assign({}, finalPromptInputProps)),
|
|
116
|
+
const showFooter = finalPromptInputProps || finalDisclaimerProps;
|
|
117
|
+
return (_jsxs("div", { className: b(null, className), "data-qa": qa, children: [_jsx("div", { className: b('header', headerClassName), children: _jsx(Header, Object.assign({}, finalHeaderProps)) }), _jsx("div", { className: b('content', { view: hookState.chatContentView }, contentClassName), children: _jsx(ChatContent, Object.assign({}, finalContentProps)) }), showFooter && (_jsxs("div", { className: b('footer', { view: hookState.chatContentView }, footerClassName), children: [finalPromptInputProps && _jsx(PromptInput, Object.assign({}, finalPromptInputProps)), finalDisclaimerProps && _jsx(Disclaimer, Object.assign({}, finalDisclaimerProps))] })), _jsx(History, Object.assign({}, finalHistoryProps))] }));
|
|
105
118
|
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"header-default-title": "AI Chat",
|
|
3
3
|
"empty-state-title": "Welcome to AI Chat",
|
|
4
4
|
"empty-state-description": "Start a conversation by typing a message below or selecting a suggestion.",
|
|
5
|
-
"empty-state-suggestions-title": "Try asking:",
|
|
6
5
|
"empty-state-show-more": "Show more examples",
|
|
7
6
|
"prompt-placeholder": "Type your message...",
|
|
8
7
|
"history-empty": "No chat history yet",
|
|
8
|
+
"history-empty-filtered": "No chats match your search",
|
|
9
9
|
"disclaimer-text": "AI can make mistakes. Check important info."
|
|
10
10
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export declare const i18n: ((key: "header-default-title" | "empty-state-title" | "empty-state-description" | "empty-state-
|
|
1
|
+
export declare const i18n: ((key: "header-default-title" | "empty-state-title" | "empty-state-description" | "empty-state-show-more" | "prompt-placeholder" | "history-empty" | "history-empty-filtered" | "disclaimer-text", params?: import("@gravity-ui/i18n").Params) => string) & {
|
|
2
2
|
Translation: import("react").ComponentType<{
|
|
3
3
|
children: (props: {
|
|
4
|
-
t: (key: "header-default-title" | "empty-state-title" | "empty-state-description" | "empty-state-
|
|
4
|
+
t: (key: "header-default-title" | "empty-state-title" | "empty-state-description" | "empty-state-show-more" | "prompt-placeholder" | "history-empty" | "history-empty-filtered" | "disclaimer-text", params?: import("@gravity-ui/i18n").Params) => string;
|
|
5
5
|
}) => React.ReactNode;
|
|
6
6
|
}>;
|
|
7
7
|
useTranslation: () => {
|
|
8
|
-
t: (key: "header-default-title" | "empty-state-title" | "empty-state-description" | "empty-state-
|
|
8
|
+
t: (key: "header-default-title" | "empty-state-title" | "empty-state-description" | "empty-state-show-more" | "prompt-placeholder" | "history-empty" | "history-empty-filtered" | "disclaimer-text", params?: import("@gravity-ui/i18n").Params) => string;
|
|
9
9
|
};
|
|
10
10
|
keysetData: {
|
|
11
|
-
"g-aikit-ChatContainer": Record<"header-default-title" | "empty-state-title" | "empty-state-description" | "empty-state-
|
|
11
|
+
"g-aikit-ChatContainer": Record<"header-default-title" | "empty-state-title" | "empty-state-description" | "empty-state-show-more" | "prompt-placeholder" | "history-empty" | "history-empty-filtered" | "disclaimer-text", import("@gravity-ui/i18n").KeyData>;
|
|
12
12
|
};
|
|
13
13
|
};
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"header-default-title": "AI Чат",
|
|
3
3
|
"empty-state-title": "Добро пожаловать в AI Чат",
|
|
4
4
|
"empty-state-description": "Начните разговор, введя сообщение ниже или выбрав подсказку.",
|
|
5
|
-
"empty-state-suggestions-title": "Попробуйте спросить:",
|
|
6
5
|
"empty-state-show-more": "Показать больше примеров",
|
|
7
6
|
"prompt-placeholder": "Введите ваше сообщение...",
|
|
8
7
|
"history-empty": "История чатов пока пуста",
|
|
8
|
+
"history-empty-filtered": "Ничего не найдено по вашему запросу",
|
|
9
9
|
"disclaimer-text": "ИИ может ошибаться. Проверяйте важную информацию."
|
|
10
10
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { OptionsType } from '@diplodoc/transform/lib/typings';
|
|
2
2
|
import { MessageListProps } from 'src/components/organisms/MessageList';
|
|
3
3
|
import type { ChatStatus, ChatType, TChatMessage, TSubmitData } from '../../../types';
|
|
4
|
+
import type { DisclaimerProps } from '../../atoms/Disclaimer';
|
|
4
5
|
import type { ContextItemConfig } from '../../molecules/PromptInputHeader';
|
|
5
6
|
import type { SuggestionsItem } from '../../molecules/Suggestions';
|
|
6
7
|
import type { HeaderProps } from '../../organisms/Header';
|
|
@@ -50,6 +51,8 @@ export interface ChatContainerI18nConfig {
|
|
|
50
51
|
history?: {
|
|
51
52
|
/** Empty state placeholder */
|
|
52
53
|
emptyPlaceholder?: string;
|
|
54
|
+
/** Empty filtered state placeholder (when search returns no results) */
|
|
55
|
+
emptyFilteredPlaceholder?: string;
|
|
53
56
|
/** Search placeholder */
|
|
54
57
|
searchPlaceholder?: string;
|
|
55
58
|
};
|
|
@@ -134,6 +137,8 @@ export interface ChatContainerProps {
|
|
|
134
137
|
emptyContainerProps?: Partial<EmptyContainerProps>;
|
|
135
138
|
/** Props override for PromptInput component */
|
|
136
139
|
promptInputProps?: Partial<Omit<PromptInputProps, 'onSend' | 'onCancel'>>;
|
|
140
|
+
/** Props override for Disclaimer component */
|
|
141
|
+
disclaimerProps?: Partial<DisclaimerProps>;
|
|
137
142
|
/** Props override for History component */
|
|
138
143
|
historyProps?: Partial<Omit<HistoryProps, 'chats' | 'selectedChat' | 'onSelectChat' | 'onDeleteChat' | 'anchorElement'>>;
|
|
139
144
|
/** Welcome screen configuration for empty state */
|
|
@@ -146,6 +151,8 @@ export interface ChatContainerProps {
|
|
|
146
151
|
showNewChat?: boolean;
|
|
147
152
|
/** Show close button */
|
|
148
153
|
showClose?: boolean;
|
|
154
|
+
/** Hide header title when chat is empty */
|
|
155
|
+
hideTitleOnEmptyChat?: boolean;
|
|
149
156
|
/** Additional CSS class */
|
|
150
157
|
className?: string;
|
|
151
158
|
/** Additional CSS class for header section */
|
|
@@ -26,6 +26,8 @@ export interface HistoryProps extends QAProps, DOMProps {
|
|
|
26
26
|
showActions?: boolean;
|
|
27
27
|
/** Empty state placeholder */
|
|
28
28
|
emptyPlaceholder?: React.ReactNode;
|
|
29
|
+
/** Empty filtered state placeholder (when search returns no results) */
|
|
30
|
+
emptyFilteredPlaceholder?: React.ReactNode;
|
|
29
31
|
/** Additional CSS class */
|
|
30
32
|
className?: string;
|
|
31
33
|
/** Custom filter function for search */
|
|
@@ -8,9 +8,9 @@ import { HistoryList } from './HistoryList';
|
|
|
8
8
|
* @returns React component
|
|
9
9
|
*/
|
|
10
10
|
export function History(props) {
|
|
11
|
-
const { chats, selectedChat, onSelectChat, onDeleteChat, onLoadMore, hasMore = false, searchable = true, groupBy = 'date', showActions = true, emptyPlaceholder, className, qa, style, filterFunction, loading = false, open = false, onOpenChange, anchorElement, } = props;
|
|
11
|
+
const { chats, selectedChat, onSelectChat, onDeleteChat, onLoadMore, hasMore = false, searchable = true, groupBy = 'date', showActions = true, emptyPlaceholder, emptyFilteredPlaceholder, className, qa, style, filterFunction, loading = false, open = false, onOpenChange, anchorElement, } = props;
|
|
12
12
|
const handleChatClick = () => {
|
|
13
13
|
onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(false);
|
|
14
14
|
};
|
|
15
|
-
return (_jsx(Popup, { anchorElement: anchorElement, placement: "bottom-end", open: open, onOpenChange: onOpenChange, children: _jsx(HistoryList, { chats: chats, selectedChat: selectedChat, onSelectChat: onSelectChat, onDeleteChat: onDeleteChat, onLoadMore: onLoadMore, hasMore: hasMore, searchable: searchable, groupBy: groupBy, showActions: showActions, emptyPlaceholder: emptyPlaceholder, className: className, qa: qa, style: style, filterFunction: filterFunction, loading: loading, onChatClick: handleChatClick }) }));
|
|
15
|
+
return (_jsx(Popup, { anchorElement: anchorElement, placement: "bottom-end", open: open, onOpenChange: onOpenChange, children: _jsx(HistoryList, { chats: chats, selectedChat: selectedChat, onSelectChat: onSelectChat, onDeleteChat: onDeleteChat, onLoadMore: onLoadMore, hasMore: hasMore, searchable: searchable, groupBy: groupBy, showActions: showActions, emptyPlaceholder: emptyPlaceholder, emptyFilteredPlaceholder: emptyFilteredPlaceholder, className: className, qa: qa, style: style, filterFunction: filterFunction, loading: loading, onChatClick: handleChatClick }) }));
|
|
16
16
|
}
|
|
@@ -27,7 +27,7 @@ $block: '.#{variables.$ns}history';
|
|
|
27
27
|
|
|
28
28
|
&__filter {
|
|
29
29
|
display: flex;
|
|
30
|
-
padding: spacing(2);
|
|
30
|
+
padding: spacing(2) spacing(2) 0 spacing(2);
|
|
31
31
|
align-items: center;
|
|
32
32
|
gap: spacing(2);
|
|
33
33
|
align-self: stretch;
|
|
@@ -44,6 +44,7 @@ $block: '.#{variables.$ns}history';
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
&__chat-item {
|
|
47
|
+
cursor: pointer;
|
|
47
48
|
display: flex;
|
|
48
49
|
padding: spacing(2) spacing(4);
|
|
49
50
|
align-items: center;
|
|
@@ -27,6 +27,8 @@ export interface HistoryListProps extends QAProps, DOMProps {
|
|
|
27
27
|
showActions?: boolean;
|
|
28
28
|
/** Empty state placeholder */
|
|
29
29
|
emptyPlaceholder?: React.ReactNode;
|
|
30
|
+
/** Empty filtered state placeholder (when search returns no results) */
|
|
31
|
+
emptyFilteredPlaceholder?: React.ReactNode;
|
|
30
32
|
/** Additional CSS class */
|
|
31
33
|
className?: string;
|
|
32
34
|
/** Custom filter function for search */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from 'react';
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
3
|
import { Button, List } from '@gravity-ui/uikit';
|
|
4
4
|
import { defaultChatFilter, groupChatsByDate } from '../../../utils/chatUtils';
|
|
5
5
|
import { block } from '../../../utils/cn';
|
|
@@ -16,7 +16,8 @@ const b = block('history');
|
|
|
16
16
|
* @returns React component
|
|
17
17
|
*/
|
|
18
18
|
export function HistoryList(props) {
|
|
19
|
-
const { chats, selectedChat, onSelectChat, onDeleteChat, onLoadMore, hasMore = false, searchable = true, groupBy = 'date', showActions = true, emptyPlaceholder, className, qa, style, filterFunction = defaultChatFilter, loading = false, onChatClick, } = props;
|
|
19
|
+
const { chats, selectedChat, onSelectChat, onDeleteChat, onLoadMore, hasMore = false, searchable = true, groupBy = 'date', showActions = true, emptyPlaceholder, emptyFilteredPlaceholder, className, qa, style, filterFunction = defaultChatFilter, loading = false, onChatClick, } = props;
|
|
20
|
+
const [filteredItemCount, setFilteredItemCount] = useState(null);
|
|
20
21
|
// Group chats if needed
|
|
21
22
|
const groupedChats = useMemo(() => {
|
|
22
23
|
if (groupBy === 'none') {
|
|
@@ -84,5 +85,10 @@ export function HistoryList(props) {
|
|
|
84
85
|
return (_jsx(ChatItem, { chat: item, showActions: showActions, onChatClick: handleChatClick, onDeleteClick: onDeleteChat ? handleDeleteClick : undefined }, item.id));
|
|
85
86
|
};
|
|
86
87
|
const emptyState = emptyPlaceholder || _jsx("div", { className: b('empty'), children: i18n('empty-state') });
|
|
87
|
-
|
|
88
|
+
const emptyFilteredState = emptyFilteredPlaceholder || (_jsx("div", { className: b('empty'), children: i18n('empty-filtered-state') }));
|
|
89
|
+
// Determine which empty placeholder to show
|
|
90
|
+
const finalEmptyPlaceholder = filteredItemCount === null || filteredItemCount === listItems.length
|
|
91
|
+
? emptyState
|
|
92
|
+
: emptyFilteredState;
|
|
93
|
+
return (_jsxs("div", { className: b('container', className), "data-qa": qa, style: style, children: [_jsx("div", { className: b('list-wrapper'), children: loading ? (_jsx("div", { className: b('loader-wrapper'), children: _jsx(Loader, { view: "loading" }) })) : (_jsx(List, { size: "m", items: listItems, renderItem: renderItem, virtualized: false, filterable: searchable, filterItem: wrappedFilterFunction, filterPlaceholder: i18n('search-placeholder'), filterClassName: b('filter'), emptyPlaceholder: finalEmptyPlaceholder, selectedItemIndex: selectedItemIndex, itemsClassName: b('list'), itemClassName: b('list-item'), onFilterEnd: (data) => setFilteredItemCount(data.items.length) })) }), hasMore && onLoadMore && (_jsx(Button, { view: "flat-action", size: "m", width: "max", onClick: onLoadMore, children: i18n('action-load-more') }))] }));
|
|
88
94
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export declare const i18n: ((key: "action-load-more" | "search-placeholder" | "empty-state" | "tooltip-history" | "tooltip-delete", params?: import("@gravity-ui/i18n").Params) => string) & {
|
|
1
|
+
export declare const i18n: ((key: "action-load-more" | "search-placeholder" | "empty-state" | "empty-filtered-state" | "tooltip-history" | "tooltip-delete", params?: import("@gravity-ui/i18n").Params) => string) & {
|
|
2
2
|
Translation: import("react").ComponentType<{
|
|
3
3
|
children: (props: {
|
|
4
|
-
t: (key: "action-load-more" | "search-placeholder" | "empty-state" | "tooltip-history" | "tooltip-delete", params?: import("@gravity-ui/i18n").Params) => string;
|
|
4
|
+
t: (key: "action-load-more" | "search-placeholder" | "empty-state" | "empty-filtered-state" | "tooltip-history" | "tooltip-delete", params?: import("@gravity-ui/i18n").Params) => string;
|
|
5
5
|
}) => React.ReactNode;
|
|
6
6
|
}>;
|
|
7
7
|
useTranslation: () => {
|
|
8
|
-
t: (key: "action-load-more" | "search-placeholder" | "empty-state" | "tooltip-history" | "tooltip-delete", params?: import("@gravity-ui/i18n").Params) => string;
|
|
8
|
+
t: (key: "action-load-more" | "search-placeholder" | "empty-state" | "empty-filtered-state" | "tooltip-history" | "tooltip-delete", params?: import("@gravity-ui/i18n").Params) => string;
|
|
9
9
|
};
|
|
10
10
|
keysetData: {
|
|
11
|
-
"g-aikit-History": Record<"action-load-more" | "search-placeholder" | "empty-state" | "tooltip-history" | "tooltip-delete", import("@gravity-ui/i18n").KeyData>;
|
|
11
|
+
"g-aikit-History": Record<"action-load-more" | "search-placeholder" | "empty-state" | "empty-filtered-state" | "tooltip-history" | "tooltip-delete", import("@gravity-ui/i18n").KeyData>;
|
|
12
12
|
};
|
|
13
13
|
};
|