blixify-ui-web-mcp 0.1.2 → 0.1.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.
Files changed (25) hide show
  1. package/lib/components/chart/ganttChart/README.md +96 -110
  2. package/lib/components/chart/ganttChart/ganttChart.stories.tsx +132 -116
  3. package/lib/components/chart/heatmapChart/README.md +93 -82
  4. package/lib/components/chart/heatmapChart/heatmapChart.stories.tsx +118 -74
  5. package/lib/components/chat/chatAnchor/README.md +61 -79
  6. package/lib/components/chat/chatBase/README.md +166 -199
  7. package/lib/components/chat/chatBase/chatBase.stories.tsx +243 -265
  8. package/lib/components/chat/chatDashboard/README.md +87 -129
  9. package/lib/components/chat/chatDashboard/chatDashboard.stories.tsx +84 -107
  10. package/lib/components/data/dataGraph/README.md +135 -1
  11. package/lib/components/data/dataGraph/dataGraph.stories.tsx +86 -67
  12. package/lib/components/data/dataReference/README.md +141 -43
  13. package/lib/components/data/dataReference/dataReference.stories.tsx +107 -35
  14. package/lib/components/data/dataWrapper/README.md +2 -1
  15. package/lib/components/display/table/README.md +1 -1
  16. package/lib/components/navigation/bars/README.md +90 -17
  17. package/lib/components/navigation/bars/bars.stories.tsx +125 -51
  18. package/lib/components/navigation/breadcrumb/README.md +40 -20
  19. package/lib/components/navigation/breadcrumb/breadcrumb.stories.tsx +30 -4
  20. package/lib/components/navigation/header/README.md +67 -108
  21. package/lib/components/navigation/header/header.stories.tsx +143 -39
  22. package/lib/components/navigation/leftNav/README.md +121 -66
  23. package/lib/components/navigation/leftNav/leftNav.stories.tsx +176 -83
  24. package/lib/components/navigation/tabs/tabs.stories.tsx +53 -29
  25. package/package.json +1 -1
@@ -1,12 +1,12 @@
1
1
  import { TrashIcon } from "@heroicons/react/24/outline";
2
2
  import { storiesOf } from "@storybook/react";
3
3
  import moment from "moment";
4
- import React, { useEffect, useState } from "react";
4
+ import React, { useState } from "react";
5
5
  import { v4 as uuidv4 } from "uuid";
6
6
  import { ChatBase, ChatBaseAction, Conversation } from ".";
7
7
  import Logo from "../../../assets/logo.png";
8
+ import { StorySection } from "../../storyUtils";
8
9
  import { TextButton } from "../../action/textButton";
9
- import { Loading } from "../../display/loading";
10
10
  import README from "./README.md";
11
11
 
12
12
  const stories = storiesOf("Chat", module);
@@ -17,290 +17,268 @@ stories.addParameters({
17
17
  },
18
18
  });
19
19
 
20
+ const botUser = {
21
+ id: "bot",
22
+ name: "Bot",
23
+ imageUrl:
24
+ "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
25
+ };
26
+ const currentUser = { id: "me", name: "You" };
27
+
28
+ const seedMessages = [
29
+ {
30
+ sender: botUser,
31
+ messageBody: "Hi! How can I help you? 🤖",
32
+ createdAt: moment("2023-11-03 12:00").toDate(),
33
+ },
34
+ {
35
+ sender: currentUser,
36
+ messageBody: "My computer keeps crashing.",
37
+ createdAt: moment("2023-11-03 12:01").toDate(),
38
+ },
39
+ {
40
+ sender: botUser,
41
+ messageBody:
42
+ "**Quick Tips**\n1. Restart your machine.\n2. Check for updates.\n3. Free up disk space.",
43
+ createdAt: moment("2023-11-03 12:02").toDate(),
44
+ },
45
+ {
46
+ sender: currentUser,
47
+ messageBody: "Thanks, I'll try that!",
48
+ createdAt: moment("2023-11-03 12:03").toDate(),
49
+ },
50
+ ];
51
+
52
+ const makeConversation = (messages = seedMessages): Conversation => ({
53
+ id: "1",
54
+ members: [currentUser, botUser],
55
+ messageList: messages,
56
+ });
57
+
20
58
  stories.add("Chat Base", () => {
21
- const botUser = {
22
- id: "1",
23
- name: "Bot",
24
- imageUrl:
25
- "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
26
- };
27
- const currentUser = { id: "2", name: "Guest" };
59
+ // --- Default ---
60
+ const [msg1, setMsg1] = useState("");
61
+ const [conv1, setConv1] = useState(makeConversation());
28
62
 
29
- const [message, setMessage] = useState("");
30
- const [messageLimit] = useState(20);
63
+ // --- Custom header/footer ---
64
+ const [msg2, setMsg2] = useState("");
65
+ const [conv2, setConv2] = useState(makeConversation());
66
+ const messageLimit = 8;
67
+
68
+ // --- Search ---
69
+ const [msg3, setMsg3] = useState("");
31
70
  const [search, setSearch] = useState("");
32
- const [filteredConversation, setFilteredConversation] =
33
- useState<Conversation | null>();
71
+ const [conv3, setConv3] = useState(makeConversation());
72
+ const [filteredConv, setFilteredConv] = useState<Conversation | null>(null);
73
+
74
+ // --- File upload ---
75
+ const [msg4, setMsg4] = useState("");
76
+ const [conv4, setConv4] = useState(makeConversation());
34
77
  const [file, setFile] = useState<File | any>(undefined);
35
- const [loading, setLoading] = useState(false);
36
78
 
37
- const [conversation, setConversation] = useState<Conversation>({
38
- id: "1",
39
- members: [currentUser, botUser],
40
- messageList: [
41
- {
42
- sender: botUser,
43
- messageBody: "Hi! How can I help you? 🤖",
44
- createdAt: moment(
45
- "Fri Nov 03 2023 12:21:14 GMT+0800 (Malaysia Time)"
46
- ).toDate(),
47
- },
48
- {
49
- sender: currentUser,
50
- messageBody: "Yo, my computer's acting up.",
51
- createdAt: moment(
52
- "Fri Nov 03 2023 13:21:14 GMT+0800 (Malaysia Time)"
53
- ).toDate(),
54
- },
55
- {
56
- sender: currentUser,
57
- messageBody:
58
- "It keeps saying 'Error 404: Coffee not found' when I start it up.",
59
- createdAt: moment(
60
- "Fri Nov 03 2023 14:21:14 GMT+0800 (Malaysia Time)"
61
- ).toDate(),
62
- },
63
- {
64
- sender: botUser,
65
- messageBody:
66
- "Sounds like a caffeine emergency!\nHave you tried offering it coffee?",
67
- createdAt: moment(
68
- "Fri Nov 03 2023 15:21:14 GMT+0800 (Malaysia Time)"
69
- ).toDate(),
70
- },
71
- {
72
- sender: currentUser,
73
- messageBody: "Seriously?",
74
- createdAt: moment(
75
- "Fri Nov 03 2023 16:21:14 GMT+0800 (Malaysia Time)"
76
- ).toDate(),
77
- },
78
- {
79
- sender: botUser,
80
- messageBody:
81
- "Well, computers do like a jolt of energy, but don't spill, that could be 'mocha-nical' trouble!",
82
- createdAt: moment(
83
- "Fri Nov 03 2023 17:21:14 GMT+0800 (Malaysia Time)"
84
- ).toDate(),
85
- },
86
- {
87
- sender: currentUser,
88
- messageBody: "Cringe. I'll give it a shot.Thanks.",
89
- createdAt: moment(
90
- "Fri Nov 03 2023 18:21:14 GMT+0800 (Malaysia Time)"
91
- ).toDate(),
92
- },
93
- {
94
- sender: botUser,
95
- messageBody:
96
- "**Quick Tips for Productivity**\n1. Set timers.\n2. Prioritize tasks.\n3. Take breaks.\n\n**Healthy Habits**\n 1. Stay hydrated.\n2. Eat well.\n3. Exercise daily.",
97
- createdAt: moment(
98
- "Fri Nov 03 2023 19:21:14 GMT+0800 (Malaysia Time)"
99
- ).toDate(),
100
- },
101
- {
102
- sender: botUser,
103
- messageBody: "Hey check out my dog!",
104
- createdAt: moment(
105
- "Fri Nov 03 2023 19:21:10 GMT+0800 (Malaysia Time)"
106
- ).toDate(),
107
- },
108
- {
109
- sender: botUser,
110
- messageBody: {
111
- fileName:
112
- "https://player.vimeo.com/external/364708250.sd.mp4?s=7382fdedda817a193bcffb6ff74624fa245ee666&profile_id=164&oauth2_token_id=57447761",
113
- token: "A",
114
- type: "video/mp4",
115
- },
116
- createdAt: moment(
117
- "Fri Nov 03 2023 19:21:14 GMT+0800 (Malaysia Time)"
118
- ).toDate(),
119
- },
120
- {
121
- sender: currentUser,
122
- messageBody: {
123
- fileName:
124
- "https://images.pexels.com/photos/3726314/pexels-photo-3726314.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
125
- token: "B",
126
- type: "image/png",
127
- },
128
- createdAt: moment(
129
- "Fri Nov 03 2023 19:21:18 GMT+0800 (Malaysia Time)"
130
- ).toDate(),
131
- },
132
- {
133
- sender: currentUser,
134
- messageBody: "Here's mine! He says hello!",
135
- createdAt: moment(
136
- "Fri Nov 03 2023 19:22:14 GMT+0800 (Malaysia Time)"
137
- ).toDate(),
138
- },
139
- ],
140
- });
79
+ // --- Loading reply ---
80
+ const [msg5, setMsg5] = useState("");
81
+ const [conv5, setConv5] = useState(makeConversation());
82
+ const [isLoading, setIsLoading] = useState(false);
141
83
 
142
- const handleOnSendMessage = (conversation: Conversation) => {
143
- setConversation(conversation);
144
- setMessage("");
145
- setFile(undefined);
146
- };
84
+ const sendMessage =
85
+ (
86
+ conv: Conversation,
87
+ setConv: (c: Conversation) => void,
88
+ setMsg: (s: string) => void
89
+ ) =>
90
+ (updated: Conversation) => {
91
+ setConv(updated);
92
+ setMsg("");
93
+ };
147
94
 
148
- const handleOnSearch = (searchMessage: string) => {
149
- if (searchMessage.length === 0) {
95
+ const handleSearch = (searchMessage: string) => {
96
+ if (!searchMessage) {
150
97
  setSearch("");
151
- setFilteredConversation(null);
152
- } else {
153
- const dupMessageList = conversation.messageList.filter((eachMessage) => {
154
- if (typeof eachMessage.messageBody === "string") {
155
- return eachMessage.messageBody
156
- .toLowerCase()
157
- .includes(searchMessage.toLowerCase());
158
- }
159
- return null;
160
- });
161
- const dupConversation = {
162
- id: conversation.id,
163
- members: [...conversation.members],
164
- messageList: [...dupMessageList],
165
- };
166
- setFilteredConversation(dupConversation);
167
- }
168
- };
169
-
170
- const handleOnClearMessage = () => {
171
- setFilteredConversation(null);
172
- setConversation({
173
- id: "1",
174
- members: [currentUser, botUser],
175
- messageList: [],
176
- });
177
- };
178
-
179
- const handleOnChangeUpload = (e: any) => {
180
- if (e && e.target.files.length > 0) {
181
- setFile(e.target.files[0]);
98
+ setFilteredConv(null);
182
99
  } else {
183
- setFile(undefined);
184
- }
185
- };
186
-
187
- const handleOnSendUpload = (conversation: Conversation) => {
188
- if (file && conversation) {
189
- setLoading(true);
190
- const dupConversation = {
191
- id: conversation.id,
192
- members: [...conversation.members],
193
- messageList: [...conversation.messageList],
194
- };
195
- const fileUrl = URL.createObjectURL(file);
196
- const fileObj = {
197
- fileName: fileUrl,
198
- token: uuidv4(),
199
- type: file.type,
200
- };
201
- dupConversation.messageList.push({
202
- sender: currentUser,
203
- messageBody: fileObj,
204
- createdAt: moment().toDate(),
100
+ setFilteredConv({
101
+ ...conv3,
102
+ messageList: conv3.messageList.filter(
103
+ (m) =>
104
+ typeof m.messageBody === "string" &&
105
+ m.messageBody.toLowerCase().includes(searchMessage.toLowerCase())
106
+ ),
205
107
  });
206
- setTimeout(() => {
207
- setConversation(dupConversation);
208
- setMessage("");
209
- setFile(undefined);
210
- setLoading(false);
211
- }, 5000);
212
108
  }
213
109
  };
214
110
 
215
- const headerElement = (
216
- <div className="p-2 bg-primary-500 rounded-t-md">
217
- <img src={Logo} alt="" className="h-12" />
218
- </div>
219
- );
220
-
221
- const renderFooterElement = () => {
222
- if (conversation.messageList.length >= messageLimit)
223
- return (
224
- <div className="p-2">
225
- <div className="p-2 text-xs border rounded-md shadow-md bg-primary-50 border-primary-300">
226
- <p>
227
- You have reached the message limit of: {messageLimit}
228
- <br />
229
- Clear previous messages to send a new one.
230
- </p>
231
- <TextButton
232
- text="Clear now"
233
- onClick={handleOnClearMessage}
234
- className="mt-2"
235
- />
236
- </div>
237
- </div>
238
- );
239
- else return undefined;
111
+ const handleSendWithReply = (updated: Conversation) => {
112
+ setConv5(updated);
113
+ setMsg5("");
114
+ setIsLoading(true);
115
+ setTimeout(() => {
116
+ setConv5((prev) => ({
117
+ ...prev,
118
+ messageList: [
119
+ ...prev.messageList,
120
+ {
121
+ sender: botUser,
122
+ messageBody: "Got it! Let me look into that for you.",
123
+ createdAt: moment().toDate(),
124
+ },
125
+ ],
126
+ }));
127
+ setIsLoading(false);
128
+ }, 2000);
240
129
  };
241
- const footerElement = renderFooterElement();
242
130
 
243
- const additionalAction: ChatBaseAction[] = [
131
+ const clearAction = (
132
+ setConv: (c: Conversation) => void
133
+ ): ChatBaseAction[] => [
244
134
  {
245
135
  text: "Clear",
246
- onClick: handleOnClearMessage,
247
- icon: <TrashIcon className="w-3 h-3" aria-hidden="true" />,
136
+ onClick: () => setConv(makeConversation([])),
137
+ icon: <TrashIcon className="w-3 h-3" />,
248
138
  },
249
139
  ];
250
140
 
251
- const loadingElement = (
252
- <div className="w-full h-full flex flex-col justify-center items-center">
253
- <Loading />
254
- <p className="mt-2">Uploading File</p>
255
- </div>
256
- );
141
+ return (
142
+ <>
143
+ <StorySection
144
+ title="Default"
145
+ description="Basic chat with send and receive. Sent messages appear on the right; received messages on the left with the sender's avatar. Received messages support markdown rendering."
146
+ >
147
+ <ChatBase
148
+ open
149
+ value={msg1}
150
+ currentUser={currentUser}
151
+ conversation={conv1}
152
+ onSendMessage={sendMessage(conv1, setConv1, setMsg1)}
153
+ onChangeMessage={(e) => setMsg1(e.target.value)}
154
+ additionalAction={clearAction(setConv1)}
155
+ sizeClassName="h-[500px] w-96"
156
+ disableMobileResize
157
+ />
158
+ </StorySection>
257
159
 
258
- useEffect(() => {
259
- setTimeout(() => {
260
- const dupConversation: Conversation = {
261
- id: conversation.id,
262
- members: [...conversation.members],
263
- messageList: [...conversation.messageList],
264
- };
265
- dupConversation.messageList.push({
266
- sender: botUser,
267
- messageBody: "Hello to you too",
268
- createdAt: moment().toDate(),
269
- });
270
- setConversation(dupConversation);
271
- }, 2000);
272
- }, []);
160
+ <StorySection
161
+ title="Custom Header & Footer"
162
+ description="headerElement replaces the default blue header bar. footerElement renders above the message input — useful for limit warnings, disclaimers, or action prompts."
163
+ >
164
+ <ChatBase
165
+ open
166
+ value={msg2}
167
+ currentUser={currentUser}
168
+ conversation={conv2}
169
+ onSendMessage={sendMessage(conv2, setConv2, setMsg2)}
170
+ onChangeMessage={(e) => setMsg2(e.target.value)}
171
+ headerElement={
172
+ <div className="p-3 bg-primary-500 rounded-t-md flex items-center">
173
+ <img src={Logo} alt="Logo" className="h-8" />
174
+ <span className="ml-3 text-white text-sm font-medium">
175
+ Support Chat
176
+ </span>
177
+ </div>
178
+ }
179
+ footerElement={
180
+ conv2.messageList.length >= messageLimit ? (
181
+ <div className="p-2">
182
+ <div className="p-2 text-xs border rounded-md bg-primary-50 border-primary-300">
183
+ Message limit reached ({messageLimit}).{" "}
184
+ <TextButton
185
+ text="Clear"
186
+ onClick={() => setConv2(makeConversation([]))}
187
+ className="inline"
188
+ />
189
+ </div>
190
+ </div>
191
+ ) : undefined
192
+ }
193
+ disableMessageInput={conv2.messageList.length >= messageLimit}
194
+ sizeClassName="h-[500px] w-96"
195
+ disableMobileResize
196
+ />
197
+ </StorySection>
273
198
 
274
- return (
275
- <ChatBase
276
- open
277
- value={message}
278
- currentUser={currentUser}
279
- conversation={filteredConversation ?? conversation}
280
- fileUpload={{
281
- allowFileUpload: true,
282
- file: file,
283
- acceptFileType: ".jpg,.jpeg,.png,.svg,.tiff,.mp4,.webm,.ogg",
284
- onChangeUpload: (e) => handleOnChangeUpload(e),
285
- onSendUpload: (conversation) => {
286
- handleOnSendUpload(conversation);
287
- },
288
- }}
289
- headerElement={headerElement}
290
- footerElement={footerElement}
291
- bodyElement={loading ? loadingElement : undefined}
292
- additionalAction={additionalAction}
293
- searchMessage={search}
294
- onChangeSearch={(e) => setSearch(e.target.value)}
295
- onSendMessage={handleOnSendMessage}
296
- onChangeMessage={(e) => setMessage(e.target.value)}
297
- onSearch={handleOnSearch}
298
- className="w-full border border-gray-200 shadow-lg sm:w-96"
299
- disableMobileResize
300
- disableMessageInput={
301
- conversation.messageList.length >= messageLimit || (file ? true : false)
302
- }
303
- onHiddenChatText={true}
304
- />
199
+ <StorySection
200
+ title="Message Search"
201
+ description="onSearch filters the conversation to matching messages. The search box is opened via the ⋮ actions menu. onChangeSearch updates the search input value."
202
+ >
203
+ <ChatBase
204
+ open
205
+ value={msg3}
206
+ currentUser={currentUser}
207
+ conversation={filteredConv ?? conv3}
208
+ searchMessage={search}
209
+ onSendMessage={sendMessage(conv3, setConv3, setMsg3)}
210
+ onChangeMessage={(e) => setMsg3(e.target.value)}
211
+ onSearch={handleSearch}
212
+ onChangeSearch={(e) => setSearch(e.target.value)}
213
+ additionalAction={clearAction(setConv3)}
214
+ sizeClassName="h-[500px] w-96"
215
+ disableMobileResize
216
+ />
217
+ </StorySection>
218
+
219
+ <StorySection
220
+ title="File Upload"
221
+ description="fileUpload.allowFileUpload=true shows a paperclip button. The selected file is previewed before sending. onSendUpload is called with the current conversation when the send button is clicked."
222
+ >
223
+ <ChatBase
224
+ open
225
+ value={msg4}
226
+ currentUser={currentUser}
227
+ conversation={conv4}
228
+ fileUpload={{
229
+ allowFileUpload: true,
230
+ file,
231
+ acceptFileType: ".jpg,.jpeg,.png,.mp4,.webm",
232
+ onChangeUpload: (e) => {
233
+ if (e?.target?.files?.length > 0) setFile(e.target.files[0]);
234
+ else setFile(undefined);
235
+ },
236
+ onSendUpload: (conversation) => {
237
+ if (file) {
238
+ const fileUrl = URL.createObjectURL(file);
239
+ setConv4({
240
+ ...conversation,
241
+ messageList: [
242
+ ...conversation.messageList,
243
+ {
244
+ sender: currentUser,
245
+ messageBody: {
246
+ fileName: fileUrl,
247
+ token: uuidv4(),
248
+ type: file.type,
249
+ },
250
+ createdAt: moment().toDate(),
251
+ },
252
+ ],
253
+ });
254
+ setFile(undefined);
255
+ setMsg4("");
256
+ }
257
+ },
258
+ }}
259
+ onSendMessage={sendMessage(conv4, setConv4, setMsg4)}
260
+ onChangeMessage={(e) => setMsg4(e.target.value)}
261
+ sizeClassName="h-[500px] w-96"
262
+ disableMobileResize
263
+ />
264
+ </StorySection>
265
+
266
+ <StorySection
267
+ title="Loading Reply"
268
+ description="isLoadingReply=true shows an animated typing indicator above the input and disables the message input while waiting for a response. Send a message to see the effect."
269
+ >
270
+ <ChatBase
271
+ open
272
+ value={msg5}
273
+ currentUser={currentUser}
274
+ conversation={conv5}
275
+ isLoadingReply={isLoading}
276
+ onSendMessage={handleSendWithReply}
277
+ onChangeMessage={(e) => setMsg5(e.target.value)}
278
+ sizeClassName="h-[500px] w-96"
279
+ disableMobileResize
280
+ />
281
+ </StorySection>
282
+ </>
305
283
  );
306
284
  });