prime-ui-kit 0.11.1 → 0.13.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/README.md +4 -1
- package/dist/chunks/{chunk-WDTSHOTD.js → chunk-E6SQBSDF.js} +140 -30
- package/dist/chunks/{chunk-CUH6DJ57.js → chunk-Z3WKKAS6.js} +6572 -5024
- package/dist/chunks/{chunk-GKX3QAUC.js → chunk-ZSJMNA46.js} +14 -5
- package/dist/color-picker.js +2 -2
- package/dist/components/chat/Chat.d.ts +11 -12
- package/dist/components/chat/chatContext.d.ts +2 -0
- package/dist/components/chat-list/ChatList.d.ts +191 -0
- package/dist/components/chat-list/formatChatTime.d.ts +6 -0
- package/dist/components/command-menu/CommandMenu.d.ts +10 -5
- package/dist/components/composer/Composer.d.ts +3 -10
- package/dist/components/data-table/DataTable.d.ts +16 -2
- package/dist/components/data-table/Row.d.ts +23 -7
- package/dist/components/data-table/StateRows.d.ts +5 -1
- package/dist/components/data-table/reorder.d.ts +3 -1
- package/dist/components/data-table/rows.d.ts +20 -4
- package/dist/components/file-upload/FileUpload.d.ts +7 -0
- package/dist/components/gantt/Gantt.d.ts +19 -1
- package/dist/components/gantt/GanttBar.d.ts +4 -1
- package/dist/components/gantt/GanttRow.d.ts +8 -1
- package/dist/components/index.d.ts +11 -7
- package/dist/components/kanban/Kanban.d.ts +17 -2
- package/dist/components/list/List.d.ts +120 -0
- package/dist/components/message/Message.d.ts +13 -0
- package/dist/components/message/MessageParts.d.ts +2 -0
- package/dist/components/modal/DialogParts.d.ts +5 -1
- package/dist/components/select/Select.d.ts +11 -5
- package/dist/components/select/SelectItem.d.ts +19 -0
- package/dist/components/select/selectContext.d.ts +4 -0
- package/dist/components/sparkline/Sparkline.d.ts +14 -3
- package/dist/components/textarea/Textarea.d.ts +11 -3
- package/dist/hooks/useUploadQueue.d.ts +5 -1
- package/dist/icons/registry.d.ts +2 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +10 -2
- package/dist/internal/VisuallyHidden.d.ts +6 -1
- package/dist/internal/attachmentTile.d.ts +5 -1
- package/dist/internal/richText.d.ts +7 -2
- package/dist/internal/textFormatting.d.ts +52 -0
- package/dist/internal/textFormattingToolbar.d.ts +26 -0
- package/dist/skill/api-contract.md +1 -1
- package/dist/skill/cheatsheet.md +13 -1
- package/dist/skill/choosing.md +9 -4
- package/dist/skill/components.md +7 -5
- package/dist/skill/composition.md +9 -1
- package/dist/skill/layouts.md +4 -1
- package/dist/skill/patterns/app-shell.module.css +18 -0
- package/dist/skill/patterns/app-shell.tsx +301 -0
- package/dist/skill/patterns/chat.module.css +0 -25
- package/dist/skill/patterns/chat.tsx +72 -29
- package/dist/skill/patterns/kanban-board.module.css +28 -0
- package/dist/skill/patterns/kanban-board.tsx +612 -0
- package/dist/skill/patterns/media-library.module.css +66 -0
- package/dist/skill/patterns/media-library.tsx +638 -0
- package/dist/skill/patterns/schedule.module.css +22 -0
- package/dist/skill/patterns/schedule.tsx +722 -0
- package/dist/skill/reference/components/chat/COMPONENT.md +13 -4
- package/dist/skill/reference/components/chat/examples/states.tsx +100 -0
- package/dist/skill/reference/components/chat-list/COMPONENT.md +256 -0
- package/dist/skill/reference/components/chat-list/examples/controlled.tsx +103 -0
- package/dist/skill/reference/components/chat-list/examples/examples.module.css +66 -0
- package/dist/skill/reference/components/chat-list/examples/live.tsx +133 -0
- package/dist/skill/reference/components/chat-list/examples/long-list.tsx +87 -0
- package/dist/skill/reference/components/chat-list/examples/narrow.tsx +88 -0
- package/dist/skill/reference/components/chat-list/examples/overview.tsx +194 -0
- package/dist/skill/reference/components/chat-list/examples/search.tsx +71 -0
- package/dist/skill/reference/components/chat-list/examples/sizes.tsx +61 -0
- package/dist/skill/reference/components/chat-list/examples/states.tsx +107 -0
- package/dist/skill/reference/components/chat-list/examples/structure.tsx +135 -0
- package/dist/skill/reference/components/command-menu/COMPONENT.md +15 -11
- package/dist/skill/reference/components/command-menu/examples/server-search.tsx +11 -9
- package/dist/skill/reference/components/composer/COMPONENT.md +8 -7
- package/dist/skill/reference/components/composer/examples/formatting.tsx +1 -1
- package/dist/skill/reference/components/confirm-dialog/COMPONENT.md +1 -0
- package/dist/skill/reference/components/data-table/COMPONENT.md +21 -6
- package/dist/skill/reference/components/data-table/examples/groups.tsx +2 -1
- package/dist/skill/reference/components/data-table/examples/trend.tsx +85 -0
- package/dist/skill/reference/components/drawer/COMPONENT.md +7 -6
- package/dist/skill/reference/components/drawer/examples/client-card.tsx +50 -0
- package/dist/skill/reference/components/fieldset/COMPONENT.md +1 -0
- package/dist/skill/reference/components/file-upload/COMPONENT.md +12 -8
- package/dist/skill/reference/components/file-upload/examples/overview.tsx +14 -3
- package/dist/skill/reference/components/gantt/COMPONENT.md +15 -5
- package/dist/skill/reference/components/gantt/examples/row-link.tsx +96 -0
- package/dist/skill/reference/components/kanban/COMPONENT.md +12 -4
- package/dist/skill/reference/components/kanban/examples/item-link.tsx +92 -0
- package/dist/skill/reference/components/list/COMPONENT.md +195 -0
- package/dist/skill/reference/components/list/examples/as-link.tsx +37 -0
- package/dist/skill/reference/components/list/examples/examples.module.css +18 -0
- package/dist/skill/reference/components/list/examples/overflow.tsx +46 -0
- package/dist/skill/reference/components/list/examples/overview.tsx +63 -0
- package/dist/skill/reference/components/list/examples/reorder.tsx +48 -0
- package/dist/skill/reference/components/list/examples/sizes.tsx +35 -0
- package/dist/skill/reference/components/list/examples/states.tsx +27 -0
- package/dist/skill/reference/components/list/examples/with-icon.tsx +60 -0
- package/dist/skill/reference/components/message/COMPONENT.md +18 -2
- package/dist/skill/reference/components/message/examples/bot-actions.tsx +51 -0
- package/dist/skill/reference/components/modal/COMPONENT.md +6 -4
- package/dist/skill/reference/components/page-content/COMPONENT.md +2 -2
- package/dist/skill/reference/components/page-content/examples/examples.module.css +5 -12
- package/dist/skill/reference/components/page-content/examples/list-detail.tsx +13 -18
- package/dist/skill/reference/components/select/COMPONENT.md +38 -7
- package/dist/skill/reference/components/select/examples/create-action.tsx +107 -0
- package/dist/skill/reference/components/select/examples/saving.tsx +47 -0
- package/dist/skill/reference/components/sheet/COMPONENT.md +4 -4
- package/dist/skill/reference/components/sparkline/COMPONENT.md +16 -2
- package/dist/skill/reference/components/sparkline/examples/compact.tsx +41 -0
- package/dist/skill/reference/components/sparkline/examples/examples.module.css +12 -0
- package/dist/skill/reference/components/textarea/COMPONENT.md +22 -2
- package/dist/skill/reference/components/textarea/examples/formatting.tsx +20 -0
- package/dist/skill/reference/components/visually-hidden/COMPONENT.md +6 -3
- package/dist/skill/reference/components/visually-hidden/examples/overview.tsx +2 -2
- package/package.json +2 -2
- package/src/components/chat/COMPONENT.md +13 -4
- package/src/components/chat/examples/states.tsx +100 -0
- package/src/components/chat-list/COMPONENT.md +256 -0
- package/src/components/chat-list/examples/controlled.tsx +103 -0
- package/src/components/chat-list/examples/examples.module.css +66 -0
- package/src/components/chat-list/examples/live.tsx +133 -0
- package/src/components/chat-list/examples/long-list.tsx +87 -0
- package/src/components/chat-list/examples/narrow.tsx +88 -0
- package/src/components/chat-list/examples/overview.tsx +194 -0
- package/src/components/chat-list/examples/search.tsx +71 -0
- package/src/components/chat-list/examples/sizes.tsx +61 -0
- package/src/components/chat-list/examples/states.tsx +107 -0
- package/src/components/chat-list/examples/structure.tsx +135 -0
- package/src/components/command-menu/COMPONENT.md +15 -11
- package/src/components/command-menu/examples/server-search.tsx +11 -9
- package/src/components/composer/COMPONENT.md +8 -7
- package/src/components/composer/examples/formatting.tsx +1 -1
- package/src/components/confirm-dialog/COMPONENT.md +1 -0
- package/src/components/data-table/COMPONENT.md +21 -6
- package/src/components/data-table/examples/groups.tsx +2 -1
- package/src/components/data-table/examples/trend.tsx +85 -0
- package/src/components/drawer/COMPONENT.md +7 -6
- package/src/components/drawer/examples/client-card.tsx +50 -0
- package/src/components/fieldset/COMPONENT.md +1 -0
- package/src/components/file-upload/COMPONENT.md +12 -8
- package/src/components/file-upload/examples/overview.tsx +14 -3
- package/src/components/gantt/COMPONENT.md +15 -5
- package/src/components/gantt/examples/row-link.tsx +96 -0
- package/src/components/kanban/COMPONENT.md +12 -4
- package/src/components/kanban/examples/item-link.tsx +92 -0
- package/src/components/list/COMPONENT.md +195 -0
- package/src/components/list/examples/as-link.tsx +37 -0
- package/src/components/list/examples/examples.module.css +18 -0
- package/src/components/list/examples/overflow.tsx +46 -0
- package/src/components/list/examples/overview.tsx +63 -0
- package/src/components/list/examples/reorder.tsx +48 -0
- package/src/components/list/examples/sizes.tsx +35 -0
- package/src/components/list/examples/states.tsx +27 -0
- package/src/components/list/examples/with-icon.tsx +60 -0
- package/src/components/message/COMPONENT.md +18 -2
- package/src/components/message/examples/bot-actions.tsx +51 -0
- package/src/components/modal/COMPONENT.md +6 -4
- package/src/components/page-content/COMPONENT.md +2 -2
- package/src/components/page-content/examples/examples.module.css +5 -12
- package/src/components/page-content/examples/list-detail.tsx +13 -18
- package/src/components/select/COMPONENT.md +38 -7
- package/src/components/select/examples/create-action.tsx +107 -0
- package/src/components/select/examples/saving.tsx +47 -0
- package/src/components/sheet/COMPONENT.md +4 -4
- package/src/components/sparkline/COMPONENT.md +16 -2
- package/src/components/sparkline/examples/compact.tsx +41 -0
- package/src/components/sparkline/examples/examples.module.css +12 -0
- package/src/components/textarea/COMPONENT.md +22 -2
- package/src/components/textarea/examples/formatting.tsx +20 -0
- package/src/components/visually-hidden/COMPONENT.md +6 -3
- package/src/components/visually-hidden/examples/overview.tsx +2 -2
- package/dist/components/composer/format.d.ts +0 -39
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** At phone width long names and previews truncate while the time and counts stay whole; a pane squeezed under 240px keeps only the names and the counts — `ChatList.Root`, `search`. */
|
|
2
|
+
import { Avatar, Card, ChatList, hueOf } from "prime-ui-kit";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import styles from "./examples.module.css";
|
|
6
|
+
|
|
7
|
+
const NOW = new Date(2026, 9, 14, 15, 0);
|
|
8
|
+
const at = (day: number, hour: number, minute: number) => new Date(2026, 9, day, hour, minute);
|
|
9
|
+
|
|
10
|
+
const DIALOGS = [
|
|
11
|
+
{
|
|
12
|
+
id: "sever",
|
|
13
|
+
name: "АО «Северсталь-Сервис», отдел закупок",
|
|
14
|
+
initials: "СС",
|
|
15
|
+
preview: "Пришлите, пожалуйста, обновлённую спецификацию с учётом новых цен",
|
|
16
|
+
at: at(14, 14, 32),
|
|
17
|
+
unread: 128,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "vector",
|
|
21
|
+
name: "ООО «Вектор»",
|
|
22
|
+
initials: "ВК",
|
|
23
|
+
preview: "Не можем выгрузить счёт INV-2048 в 1С, ошибка после экспорта",
|
|
24
|
+
at: at(14, 9, 5),
|
|
25
|
+
unread: 0,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "kozlov",
|
|
29
|
+
name: "ИП Козлов Андрей Викторович",
|
|
30
|
+
initials: "ИК",
|
|
31
|
+
preview: "Подключите, пожалуйста, ЭДО",
|
|
32
|
+
at: at(11, 11, 40),
|
|
33
|
+
unread: 1,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: "leto",
|
|
37
|
+
name: "ООО «Лето»",
|
|
38
|
+
initials: "ЛТ",
|
|
39
|
+
preview: "Можно перенести доставку на среду?",
|
|
40
|
+
at: at(2, 9, 15),
|
|
41
|
+
unread: 0,
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
type Dialog = (typeof DIALOGS)[number];
|
|
46
|
+
|
|
47
|
+
const renderDialog = (item: Dialog) => (
|
|
48
|
+
<ChatList.Item time={item.at} unread={item.unread}>
|
|
49
|
+
<ChatList.ItemIcon>
|
|
50
|
+
<Avatar.Root color={hueOf(item.id)}>
|
|
51
|
+
<Avatar.Fallback>{item.initials}</Avatar.Fallback>
|
|
52
|
+
</Avatar.Root>
|
|
53
|
+
</ChatList.ItemIcon>
|
|
54
|
+
<ChatList.ItemText>{item.name}</ChatList.ItemText>
|
|
55
|
+
<ChatList.ItemDescription>{item.preview}</ChatList.ItemDescription>
|
|
56
|
+
</ChatList.Item>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
export default function ChatListNarrowExample() {
|
|
60
|
+
const [query, setQuery] = React.useState("");
|
|
61
|
+
const needle = query.trim().toLowerCase();
|
|
62
|
+
const found = DIALOGS.filter((item) => item.name.toLowerCase().includes(needle));
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<>
|
|
66
|
+
<Card.Root className={styles.phone}>
|
|
67
|
+
<ChatList.Root
|
|
68
|
+
items={found}
|
|
69
|
+
getKey={(item) => item.id}
|
|
70
|
+
defaultValue="vector"
|
|
71
|
+
search={query}
|
|
72
|
+
onSearchChange={setQuery}
|
|
73
|
+
now={NOW}
|
|
74
|
+
renderItem={renderDialog}
|
|
75
|
+
/>
|
|
76
|
+
</Card.Root>
|
|
77
|
+
<Card.Root className={styles.rail}>
|
|
78
|
+
<ChatList.Root
|
|
79
|
+
items={DIALOGS}
|
|
80
|
+
getKey={(item) => item.id}
|
|
81
|
+
defaultValue="vector"
|
|
82
|
+
now={NOW}
|
|
83
|
+
renderItem={renderDialog}
|
|
84
|
+
/>
|
|
85
|
+
</Card.Root>
|
|
86
|
+
</>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/** A support inbox: pinned dialogs first, the search, unread and muted counts, the typing line, a draft, delivery ticks and a row menu that pins, mutes, marks read and deletes after a confirm — `ChatList.Root`, `items`, `renderItem`, `value`, `onSearchChange`, `ChatList.Item`, `ChatList.ItemMenu`. */
|
|
2
|
+
import { Avatar, Card, ChatList, ConfirmDialog, Dropdown, hueOf, Icon } from "prime-ui-kit";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import styles from "./examples.module.css";
|
|
6
|
+
|
|
7
|
+
type Dialog = {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
initials: string;
|
|
11
|
+
preview: string;
|
|
12
|
+
author?: string;
|
|
13
|
+
at: Date;
|
|
14
|
+
unread: number;
|
|
15
|
+
online?: boolean;
|
|
16
|
+
pinned?: boolean;
|
|
17
|
+
muted?: boolean;
|
|
18
|
+
mention?: boolean;
|
|
19
|
+
typing?: boolean;
|
|
20
|
+
draft?: string;
|
|
21
|
+
status?: "sent" | "read";
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const NOW = new Date(2026, 9, 14, 15, 0);
|
|
25
|
+
const at = (day: number, hour: number, minute: number) => new Date(2026, 9, day, hour, minute);
|
|
26
|
+
|
|
27
|
+
const DIALOGS: Dialog[] = [
|
|
28
|
+
{
|
|
29
|
+
id: "vector",
|
|
30
|
+
name: "ООО «Вектор»",
|
|
31
|
+
initials: "ВК",
|
|
32
|
+
preview: "Не можем выгрузить счёт INV-2048 в 1С",
|
|
33
|
+
at: at(14, 14, 32),
|
|
34
|
+
unread: 2,
|
|
35
|
+
online: true,
|
|
36
|
+
pinned: true,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "granit",
|
|
40
|
+
name: "ООО «Гранит»",
|
|
41
|
+
initials: "ГР",
|
|
42
|
+
preview: "Акт сверки за сентябрь подписан",
|
|
43
|
+
author: "Вы",
|
|
44
|
+
at: at(14, 12, 5),
|
|
45
|
+
unread: 0,
|
|
46
|
+
pinned: true,
|
|
47
|
+
status: "read",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "kozlov",
|
|
51
|
+
name: "ИП Козлов",
|
|
52
|
+
initials: "ИК",
|
|
53
|
+
preview: "Подключите, пожалуйста, ЭДО",
|
|
54
|
+
at: at(14, 11, 40),
|
|
55
|
+
unread: 1,
|
|
56
|
+
typing: true,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "sever",
|
|
60
|
+
name: "Отдел продаж «Север»",
|
|
61
|
+
initials: "ОС",
|
|
62
|
+
preview: "@Анна, проверь скидку для «Лето»",
|
|
63
|
+
author: "Дмитрий",
|
|
64
|
+
at: at(13, 18, 20),
|
|
65
|
+
unread: 14,
|
|
66
|
+
muted: true,
|
|
67
|
+
mention: true,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "leto",
|
|
71
|
+
name: "ООО «Лето»",
|
|
72
|
+
initials: "ЛТ",
|
|
73
|
+
preview: "Можно перенести доставку на среду?",
|
|
74
|
+
at: at(12, 9, 15),
|
|
75
|
+
unread: 0,
|
|
76
|
+
draft: "Да, среда подходит, водитель",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: "tehnosnab",
|
|
80
|
+
name: "ТехноСнаб",
|
|
81
|
+
initials: "ТС",
|
|
82
|
+
preview: "Отправили договор на подпись",
|
|
83
|
+
author: "Вы",
|
|
84
|
+
at: at(3, 16, 0),
|
|
85
|
+
unread: 0,
|
|
86
|
+
status: "sent",
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
/** Pinned first, then by the last message. */
|
|
91
|
+
const ordered = (dialogs: Dialog[]) =>
|
|
92
|
+
[...dialogs].sort(
|
|
93
|
+
(a, b) => Number(b.pinned ?? false) - Number(a.pinned ?? false) || +b.at - +a.at,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
export default function ChatListOverviewExample() {
|
|
97
|
+
const [dialogs, setDialogs] = React.useState(DIALOGS);
|
|
98
|
+
const [dialogId, setDialogId] = React.useState<string | null>("vector");
|
|
99
|
+
const [query, setQuery] = React.useState("");
|
|
100
|
+
const [deleting, setDeleting] = React.useState<Dialog | null>(null);
|
|
101
|
+
|
|
102
|
+
const patch = (id: string, next: Partial<Dialog>) =>
|
|
103
|
+
setDialogs((current) => current.map((item) => (item.id === id ? { ...item, ...next } : item)));
|
|
104
|
+
|
|
105
|
+
const needle = query.trim().toLowerCase();
|
|
106
|
+
const shown = ordered(dialogs).filter(
|
|
107
|
+
(item) =>
|
|
108
|
+
!needle ||
|
|
109
|
+
item.name.toLowerCase().includes(needle) ||
|
|
110
|
+
item.preview.toLowerCase().includes(needle),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<Card.Root className={styles.pane}>
|
|
115
|
+
<ChatList.Root
|
|
116
|
+
items={shown}
|
|
117
|
+
getKey={(item) => item.id}
|
|
118
|
+
value={dialogId}
|
|
119
|
+
onValueChange={(id) => {
|
|
120
|
+
setDialogId(id);
|
|
121
|
+
patch(id, { unread: 0, mention: false });
|
|
122
|
+
}}
|
|
123
|
+
search={query}
|
|
124
|
+
onSearchChange={setQuery}
|
|
125
|
+
now={NOW}
|
|
126
|
+
renderItem={(item) => (
|
|
127
|
+
<ChatList.Item
|
|
128
|
+
time={item.at}
|
|
129
|
+
status={item.status}
|
|
130
|
+
unread={item.unread}
|
|
131
|
+
mention={item.mention}
|
|
132
|
+
muted={item.muted}
|
|
133
|
+
pinned={item.pinned}
|
|
134
|
+
typing={item.typing}
|
|
135
|
+
draft={item.draft}
|
|
136
|
+
>
|
|
137
|
+
<ChatList.ItemIcon>
|
|
138
|
+
<Avatar.Root color={hueOf(item.id)}>
|
|
139
|
+
<Avatar.Fallback>{item.initials}</Avatar.Fallback>
|
|
140
|
+
{item.online ? <Avatar.Status status="online" /> : null}
|
|
141
|
+
</Avatar.Root>
|
|
142
|
+
</ChatList.ItemIcon>
|
|
143
|
+
<ChatList.ItemText>{item.name}</ChatList.ItemText>
|
|
144
|
+
<ChatList.ItemDescription author={item.author}>{item.preview}</ChatList.ItemDescription>
|
|
145
|
+
<ChatList.ItemMenu>
|
|
146
|
+
<Dropdown.Item onSelect={() => patch(item.id, { pinned: !item.pinned })}>
|
|
147
|
+
<Dropdown.ItemIcon>
|
|
148
|
+
<Icon name="action.pin" />
|
|
149
|
+
</Dropdown.ItemIcon>
|
|
150
|
+
{item.pinned ? "Открепить" : "Закрепить"}
|
|
151
|
+
</Dropdown.Item>
|
|
152
|
+
<Dropdown.Item onSelect={() => patch(item.id, { muted: !item.muted })}>
|
|
153
|
+
<Dropdown.ItemIcon>
|
|
154
|
+
<Icon name={item.muted ? "status.sound" : "status.muted"} />
|
|
155
|
+
</Dropdown.ItemIcon>
|
|
156
|
+
{item.muted ? "Включить звук" : "Без звука"}
|
|
157
|
+
</Dropdown.Item>
|
|
158
|
+
<Dropdown.Item
|
|
159
|
+
disabled={item.unread === 0}
|
|
160
|
+
onSelect={() => patch(item.id, { unread: 0, mention: false })}
|
|
161
|
+
>
|
|
162
|
+
<Dropdown.ItemIcon>
|
|
163
|
+
<Icon name="status.read" />
|
|
164
|
+
</Dropdown.ItemIcon>
|
|
165
|
+
Отметить прочитанным
|
|
166
|
+
</Dropdown.Item>
|
|
167
|
+
<Dropdown.Separator />
|
|
168
|
+
<Dropdown.Item tone="danger" onSelect={() => setDeleting(item)}>
|
|
169
|
+
<Dropdown.ItemIcon>
|
|
170
|
+
<Icon name="action.delete" />
|
|
171
|
+
</Dropdown.ItemIcon>
|
|
172
|
+
Удалить
|
|
173
|
+
</Dropdown.Item>
|
|
174
|
+
</ChatList.ItemMenu>
|
|
175
|
+
</ChatList.Item>
|
|
176
|
+
)}
|
|
177
|
+
/>
|
|
178
|
+
<ConfirmDialog.Root
|
|
179
|
+
open={deleting !== null}
|
|
180
|
+
onOpenChange={(open) => {
|
|
181
|
+
if (!open) setDeleting(null);
|
|
182
|
+
}}
|
|
183
|
+
title={`Удалить диалог с ${deleting?.name ?? ""}?`}
|
|
184
|
+
description="История переписки удалится у вас; клиент свою копию сохранит."
|
|
185
|
+
confirmLabel="Удалить диалог"
|
|
186
|
+
onConfirm={() => {
|
|
187
|
+
const id = deleting?.id;
|
|
188
|
+
setDialogs((current) => current.filter((item) => item.id !== id));
|
|
189
|
+
if (id === dialogId) setDialogId(null);
|
|
190
|
+
}}
|
|
191
|
+
/>
|
|
192
|
+
</Card.Root>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** The search field filters dialogs by name and by the last message, marks the match in both, says how many were found and shows a quiet state when none match — `defaultSearch`, `onSearchChange`. */
|
|
2
|
+
import { Avatar, Card, ChatList, hueOf } from "prime-ui-kit";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import styles from "./examples.module.css";
|
|
6
|
+
|
|
7
|
+
const NOW = new Date(2026, 9, 14, 15, 0);
|
|
8
|
+
const at = (day: number, hour: number, minute: number) => new Date(2026, 9, day, hour, minute);
|
|
9
|
+
|
|
10
|
+
const DIALOGS = [
|
|
11
|
+
{
|
|
12
|
+
id: "vector",
|
|
13
|
+
name: "ООО «Вектор»",
|
|
14
|
+
initials: "ВК",
|
|
15
|
+
preview: "Счёт INV-2048 не выгружается в 1С",
|
|
16
|
+
at: at(14, 14, 32),
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "granit",
|
|
20
|
+
name: "ООО «Гранит»",
|
|
21
|
+
initials: "ГР",
|
|
22
|
+
preview: "Пришлите счёт на предоплату",
|
|
23
|
+
at: at(14, 12, 5),
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "kozlov",
|
|
27
|
+
name: "ИП Козлов",
|
|
28
|
+
initials: "ИК",
|
|
29
|
+
preview: "Подключите, пожалуйста, ЭДО",
|
|
30
|
+
at: at(13, 11, 40),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "schetovod",
|
|
34
|
+
name: "Счетовод — интеграция",
|
|
35
|
+
initials: "СИ",
|
|
36
|
+
preview: "Токен обновлён",
|
|
37
|
+
at: at(10, 9, 0),
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
export default function ChatListSearchExample() {
|
|
42
|
+
const [query, setQuery] = React.useState("счёт");
|
|
43
|
+
const needle = query.trim().toLowerCase();
|
|
44
|
+
const found = DIALOGS.filter(
|
|
45
|
+
(item) =>
|
|
46
|
+
item.name.toLowerCase().includes(needle) || item.preview.toLowerCase().includes(needle),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Card.Root className={styles.short}>
|
|
51
|
+
<ChatList.Root
|
|
52
|
+
items={found}
|
|
53
|
+
getKey={(item) => item.id}
|
|
54
|
+
defaultSearch="счёт"
|
|
55
|
+
onSearchChange={setQuery}
|
|
56
|
+
now={NOW}
|
|
57
|
+
renderItem={(item) => (
|
|
58
|
+
<ChatList.Item time={item.at}>
|
|
59
|
+
<ChatList.ItemIcon>
|
|
60
|
+
<Avatar.Root color={hueOf(item.id)}>
|
|
61
|
+
<Avatar.Fallback>{item.initials}</Avatar.Fallback>
|
|
62
|
+
</Avatar.Root>
|
|
63
|
+
</ChatList.ItemIcon>
|
|
64
|
+
<ChatList.ItemText>{item.name}</ChatList.ItemText>
|
|
65
|
+
<ChatList.ItemDescription>{item.preview}</ChatList.ItemDescription>
|
|
66
|
+
</ChatList.Item>
|
|
67
|
+
)}
|
|
68
|
+
/>
|
|
69
|
+
</Card.Root>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** Every size tier: row height, text, avatar, glyphs and the search field follow the tier — `size`. */
|
|
2
|
+
import { Avatar, Card, ChatList, hueOf, Typography } from "prime-ui-kit";
|
|
3
|
+
|
|
4
|
+
import styles from "./examples.module.css";
|
|
5
|
+
|
|
6
|
+
const SIZES = ["xs", "s", "m", "l", "xl"] as const;
|
|
7
|
+
|
|
8
|
+
const NOW = new Date(2026, 9, 14, 15, 0);
|
|
9
|
+
|
|
10
|
+
const DIALOGS = [
|
|
11
|
+
{
|
|
12
|
+
id: "vector",
|
|
13
|
+
name: "ООО «Вектор»",
|
|
14
|
+
initials: "ВК",
|
|
15
|
+
preview: "Не можем выгрузить счёт в 1С",
|
|
16
|
+
at: new Date(2026, 9, 14, 14, 32),
|
|
17
|
+
unread: 2,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "granit",
|
|
21
|
+
name: "ООО «Гранит»",
|
|
22
|
+
initials: "ГР",
|
|
23
|
+
preview: "Акт сверки подписан",
|
|
24
|
+
at: new Date(2026, 9, 13, 12, 5),
|
|
25
|
+
unread: 0,
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export default function ChatListSizesExample() {
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
{SIZES.map((size) => (
|
|
33
|
+
<div key={size} className={styles.tier}>
|
|
34
|
+
<Typography as="span" variant="caption" tone="muted">
|
|
35
|
+
{size}
|
|
36
|
+
</Typography>
|
|
37
|
+
<Card.Root>
|
|
38
|
+
<ChatList.Root
|
|
39
|
+
size={size}
|
|
40
|
+
items={DIALOGS}
|
|
41
|
+
getKey={(item) => item.id}
|
|
42
|
+
defaultValue="vector"
|
|
43
|
+
now={NOW}
|
|
44
|
+
renderItem={(item) => (
|
|
45
|
+
<ChatList.Item time={item.at} unread={item.unread}>
|
|
46
|
+
<ChatList.ItemIcon>
|
|
47
|
+
<Avatar.Root color={hueOf(item.id)}>
|
|
48
|
+
<Avatar.Fallback>{item.initials}</Avatar.Fallback>
|
|
49
|
+
</Avatar.Root>
|
|
50
|
+
</ChatList.ItemIcon>
|
|
51
|
+
<ChatList.ItemText>{item.name}</ChatList.ItemText>
|
|
52
|
+
<ChatList.ItemDescription>{item.preview}</ChatList.ItemDescription>
|
|
53
|
+
</ChatList.Item>
|
|
54
|
+
)}
|
|
55
|
+
/>
|
|
56
|
+
</Card.Root>
|
|
57
|
+
</div>
|
|
58
|
+
))}
|
|
59
|
+
</>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/** Loading and empty: placeholder rows in the rows' geometry cross-fade into the dialogs; a new workspace without dialogs shows its own empty state with an action — `loading`, `empty`. */
|
|
2
|
+
import { Avatar, Button, Card, ChatList, EmptyPage, hueOf, Icon } from "prime-ui-kit";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import styles from "./examples.module.css";
|
|
6
|
+
|
|
7
|
+
const NOW = new Date(2026, 9, 14, 15, 0);
|
|
8
|
+
|
|
9
|
+
const DIALOGS = [
|
|
10
|
+
{
|
|
11
|
+
id: "vector",
|
|
12
|
+
name: "ООО «Вектор»",
|
|
13
|
+
initials: "ВК",
|
|
14
|
+
preview: "Не можем выгрузить счёт в 1С",
|
|
15
|
+
at: new Date(2026, 9, 14, 14, 32),
|
|
16
|
+
unread: 2,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "granit",
|
|
20
|
+
name: "ООО «Гранит»",
|
|
21
|
+
initials: "ГР",
|
|
22
|
+
preview: "Акт сверки подписан",
|
|
23
|
+
at: new Date(2026, 9, 14, 12, 5),
|
|
24
|
+
unread: 0,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "kozlov",
|
|
28
|
+
name: "ИП Козлов",
|
|
29
|
+
initials: "ИК",
|
|
30
|
+
preview: "Подключите, пожалуйста, ЭДО",
|
|
31
|
+
at: new Date(2026, 9, 13, 11, 40),
|
|
32
|
+
unread: 1,
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const LOAD_MS = 1400;
|
|
37
|
+
|
|
38
|
+
type Dialog = (typeof DIALOGS)[number];
|
|
39
|
+
|
|
40
|
+
const renderDialog = (item: Dialog) => (
|
|
41
|
+
<ChatList.Item time={item.at} unread={item.unread}>
|
|
42
|
+
<ChatList.ItemIcon>
|
|
43
|
+
<Avatar.Root color={hueOf(item.id)}>
|
|
44
|
+
<Avatar.Fallback>{item.initials}</Avatar.Fallback>
|
|
45
|
+
</Avatar.Root>
|
|
46
|
+
</ChatList.ItemIcon>
|
|
47
|
+
<ChatList.ItemText>{item.name}</ChatList.ItemText>
|
|
48
|
+
<ChatList.ItemDescription>{item.preview}</ChatList.ItemDescription>
|
|
49
|
+
</ChatList.Item>
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
export default function ChatListStatesExample() {
|
|
53
|
+
const [loading, setLoading] = React.useState(true);
|
|
54
|
+
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
if (!loading) return;
|
|
57
|
+
const timer = window.setTimeout(() => setLoading(false), LOAD_MS);
|
|
58
|
+
return () => window.clearTimeout(timer);
|
|
59
|
+
}, [loading]);
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<>
|
|
63
|
+
<div className={styles.stage}>
|
|
64
|
+
<div className={styles.bar}>
|
|
65
|
+
<Button.Root
|
|
66
|
+
variant="soft"
|
|
67
|
+
tone="neutral"
|
|
68
|
+
loading={loading}
|
|
69
|
+
onClick={() => setLoading(true)}
|
|
70
|
+
>
|
|
71
|
+
Обновить
|
|
72
|
+
</Button.Root>
|
|
73
|
+
</div>
|
|
74
|
+
<Card.Root className={styles.short}>
|
|
75
|
+
<ChatList.Root
|
|
76
|
+
items={loading ? [] : DIALOGS}
|
|
77
|
+
getKey={(item) => item.id}
|
|
78
|
+
loading={loading}
|
|
79
|
+
now={NOW}
|
|
80
|
+
renderItem={renderDialog}
|
|
81
|
+
/>
|
|
82
|
+
</Card.Root>
|
|
83
|
+
</div>
|
|
84
|
+
<Card.Root className={styles.short}>
|
|
85
|
+
<ChatList.Root
|
|
86
|
+
items={[] as Dialog[]}
|
|
87
|
+
getKey={(item) => item.id}
|
|
88
|
+
renderItem={renderDialog}
|
|
89
|
+
empty={
|
|
90
|
+
<EmptyPage.Root layout="compact">
|
|
91
|
+
<EmptyPage.Icon>
|
|
92
|
+
<Icon name="object.message" />
|
|
93
|
+
</EmptyPage.Icon>
|
|
94
|
+
<EmptyPage.Title>Диалогов пока нет</EmptyPage.Title>
|
|
95
|
+
<EmptyPage.Description>
|
|
96
|
+
Клиенты появятся здесь, когда напишут в чат на сайте.
|
|
97
|
+
</EmptyPage.Description>
|
|
98
|
+
<EmptyPage.Actions>
|
|
99
|
+
<Button.Root variant="soft">Настроить виджет</Button.Root>
|
|
100
|
+
</EmptyPage.Actions>
|
|
101
|
+
</EmptyPage.Root>
|
|
102
|
+
}
|
|
103
|
+
/>
|
|
104
|
+
</Card.Root>
|
|
105
|
+
</>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/** Every piece of a row: a photo or initials with presence, a glyph beside the name, the author of the last message, media words with a length, a member typing in a group, ticks from sending to failed — `ChatList.ItemIcon`, `ChatList.ItemDescription`, `author`, `kind`, `duration`, `typing`, `status`. */
|
|
2
|
+
import { Avatar, Card, ChatList, hueOf, Icon, type MessageStatus } from "prime-ui-kit";
|
|
3
|
+
|
|
4
|
+
import styles from "./examples.module.css";
|
|
5
|
+
|
|
6
|
+
type Dialog = {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
initials: string;
|
|
10
|
+
photo?: string;
|
|
11
|
+
online?: boolean;
|
|
12
|
+
verified?: boolean;
|
|
13
|
+
group?: boolean;
|
|
14
|
+
at: Date;
|
|
15
|
+
author?: string;
|
|
16
|
+
kind?: "photo" | "voice" | "videoNote" | "file" | "location";
|
|
17
|
+
duration?: number;
|
|
18
|
+
text?: string;
|
|
19
|
+
status?: MessageStatus;
|
|
20
|
+
typing?: string;
|
|
21
|
+
unread?: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const NOW = new Date(2026, 9, 14, 15, 0);
|
|
25
|
+
const at = (day: number, hour: number, minute: number) => new Date(2026, 9, day, hour, minute);
|
|
26
|
+
|
|
27
|
+
const DIALOGS: Dialog[] = [
|
|
28
|
+
{
|
|
29
|
+
id: "irina",
|
|
30
|
+
name: "Ирина Белова",
|
|
31
|
+
initials: "ИБ",
|
|
32
|
+
photo: "https://images.unsplash.com/photo-1544005313-94ddf0286df2?w=128&h=128&fit=crop",
|
|
33
|
+
online: true,
|
|
34
|
+
at: at(14, 14, 40),
|
|
35
|
+
kind: "photo",
|
|
36
|
+
text: "Вот так выглядит ошибка",
|
|
37
|
+
unread: 1,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "prime-bot",
|
|
41
|
+
name: "Prime Бот",
|
|
42
|
+
initials: "ПБ",
|
|
43
|
+
verified: true,
|
|
44
|
+
at: at(14, 13, 10),
|
|
45
|
+
kind: "file",
|
|
46
|
+
text: "Счёт INV-2051.pdf",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "orlov",
|
|
50
|
+
name: "Дмитрий Орлов",
|
|
51
|
+
initials: "ДО",
|
|
52
|
+
at: at(14, 11, 2),
|
|
53
|
+
author: "Вы",
|
|
54
|
+
kind: "voice",
|
|
55
|
+
duration: 42,
|
|
56
|
+
status: "sending",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "warehouse",
|
|
60
|
+
name: "Склад «Центральный»",
|
|
61
|
+
initials: "СЦ",
|
|
62
|
+
group: true,
|
|
63
|
+
at: at(13, 18, 30),
|
|
64
|
+
author: "Олег",
|
|
65
|
+
kind: "location",
|
|
66
|
+
text: "Рампа 2",
|
|
67
|
+
typing: "Анна",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "lena",
|
|
71
|
+
name: "Елена Смирнова",
|
|
72
|
+
initials: "ЕС",
|
|
73
|
+
at: at(12, 9, 0),
|
|
74
|
+
author: "Вы",
|
|
75
|
+
kind: "videoNote",
|
|
76
|
+
duration: 6,
|
|
77
|
+
status: "read",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "leto",
|
|
81
|
+
name: "ООО «Лето»",
|
|
82
|
+
initials: "ЛТ",
|
|
83
|
+
at: at(1, 16, 45),
|
|
84
|
+
author: "Вы",
|
|
85
|
+
text: "Отправил акт повторно",
|
|
86
|
+
status: "failed",
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
export default function ChatListStructureExample() {
|
|
91
|
+
return (
|
|
92
|
+
<Card.Root className={styles.pane}>
|
|
93
|
+
<ChatList.Root
|
|
94
|
+
items={DIALOGS}
|
|
95
|
+
getKey={(item) => item.id}
|
|
96
|
+
defaultValue="irina"
|
|
97
|
+
now={NOW}
|
|
98
|
+
renderItem={(item) => (
|
|
99
|
+
<ChatList.Item
|
|
100
|
+
time={item.at}
|
|
101
|
+
status={item.status}
|
|
102
|
+
unread={item.unread}
|
|
103
|
+
typing={item.typing}
|
|
104
|
+
>
|
|
105
|
+
<ChatList.ItemIcon>
|
|
106
|
+
<Avatar.Root color={hueOf(item.id)}>
|
|
107
|
+
{item.photo ? <Avatar.Image src={item.photo} alt="" /> : null}
|
|
108
|
+
<Avatar.Fallback>{item.initials}</Avatar.Fallback>
|
|
109
|
+
{item.online ? <Avatar.Status status="online" /> : null}
|
|
110
|
+
</Avatar.Root>
|
|
111
|
+
</ChatList.ItemIcon>
|
|
112
|
+
<ChatList.ItemText>{item.name}</ChatList.ItemText>
|
|
113
|
+
{item.verified ? (
|
|
114
|
+
<ChatList.ItemIcon>
|
|
115
|
+
<Icon name="status.success" tone="accent" label="Официальный аккаунт" />
|
|
116
|
+
</ChatList.ItemIcon>
|
|
117
|
+
) : null}
|
|
118
|
+
{item.group ? (
|
|
119
|
+
<ChatList.ItemIcon>
|
|
120
|
+
<Icon name="object.users" label="Группа" />
|
|
121
|
+
</ChatList.ItemIcon>
|
|
122
|
+
) : null}
|
|
123
|
+
<ChatList.ItemDescription
|
|
124
|
+
author={item.author}
|
|
125
|
+
kind={item.kind}
|
|
126
|
+
duration={item.duration}
|
|
127
|
+
>
|
|
128
|
+
{item.text}
|
|
129
|
+
</ChatList.ItemDescription>
|
|
130
|
+
</ChatList.Item>
|
|
131
|
+
)}
|
|
132
|
+
/>
|
|
133
|
+
</Card.Root>
|
|
134
|
+
);
|
|
135
|
+
}
|