@galangel/react-scroll-magic 1.0.1 β 1.0.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/README.md +321 -41
- package/dist/Scroll/story/ai-chat/AIChatDemo.d.ts +3 -0
- package/dist/Scroll/story/ai-chat/components/AgentStepHeader.d.ts +13 -0
- package/dist/Scroll/story/ai-chat/components/ChatHeader.d.ts +7 -0
- package/dist/Scroll/story/ai-chat/components/ChatInput.d.ts +8 -0
- package/dist/Scroll/story/ai-chat/components/ChatStyles.d.ts +3 -0
- package/dist/Scroll/story/ai-chat/components/EmptyState.d.ts +3 -0
- package/dist/Scroll/story/ai-chat/components/OutputLineItem.d.ts +9 -0
- package/dist/Scroll/story/ai-chat/components/QuestionBubble.d.ts +8 -0
- package/dist/Scroll/story/ai-chat/components/ReasoningHeader.d.ts +13 -0
- package/dist/Scroll/story/ai-chat/components/SolutionHeader.d.ts +11 -0
- package/dist/Scroll/story/ai-chat/components/index.d.ts +9 -0
- package/dist/Scroll/story/ai-chat/hooks/index.d.ts +2 -0
- package/dist/Scroll/story/ai-chat/hooks/use-auto-scroll.d.ts +13 -0
- package/dist/Scroll/story/ai-chat/hooks/use-chat-messages.d.ts +9 -0
- package/dist/Scroll/story/ai-chat/index.d.ts +5 -0
- package/dist/Scroll/story/ai-chat/index.stories.d.ts +7 -0
- package/dist/Scroll/story/ai-chat/types/index.d.ts +22 -0
- package/dist/Scroll/story/ai-chat/utils/convert-to-items.d.ts +4 -0
- package/dist/Scroll/story/ai-chat/utils/generate-output-line.d.ts +3 -0
- package/dist/Scroll/story/ai-chat/utils/generate-question.d.ts +1 -0
- package/dist/Scroll/story/ai-chat/utils/index.d.ts +4 -0
- package/dist/Scroll/story/ai-chat/utils/step-config.d.ts +3 -0
- package/dist/cjs/index.js +1 -1
- package/dist/es/index.js +51 -51
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,23 +1,46 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://img.shields.io/badge/πͺ-React_Scroll_Magic-667eea?style=for-the-badge&labelColor=764ba2" alt="React Scroll Magic" />
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<h1 align="center">πͺ React Scroll Magic</h1>
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Create magical scroll experiences with nested sticky headers, collapsible sections, and smooth animations.</strong>
|
|
9
|
+
</p>
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@galangel/react-scroll-magic"><img src="https://img.shields.io/npm/v/@galangel/react-scroll-magic.svg?style=flat-square&color=667eea" alt="npm version" /></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/@galangel/react-scroll-magic"><img src="https://img.shields.io/npm/dm/@galangel/react-scroll-magic.svg?style=flat-square&color=764ba2" alt="npm downloads" /></a>
|
|
14
|
+
<a href="https://github.com/galangel/react-scroll-magic/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg?style=flat-square" alt="license" /></a>
|
|
15
|
+
<img src="https://img.shields.io/badge/TypeScript-Ready-3178c6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript" />
|
|
16
|
+
<img src="https://img.shields.io/badge/React-18+-61dafb?style=flat-square&logo=react&logoColor=white" alt="React 18+" />
|
|
17
|
+
</p>
|
|
8
18
|
|
|
9
|
-
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="https://galangel.github.io/react-scroll-magic/">π Live Demo & Documentation</a>
|
|
21
|
+
</p>
|
|
10
22
|
|
|
11
|
-
|
|
12
|
-
- scroll to header by click
|
|
13
|
-
- typescript
|
|
23
|
+
---
|
|
14
24
|
|
|
15
|
-
##
|
|
25
|
+
## β¨ Key Features
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
| Feature | Description |
|
|
28
|
+
| ------------------------- | --------------------------------------------------------------------------- |
|
|
29
|
+
| π **Sticky Headers** | Headers stick to top as you scroll, with support for nested sticky behavior |
|
|
30
|
+
| π― **Nested Structure** | Create deeply nested hierarchies with items inside items |
|
|
31
|
+
| π¦ **Collapse/Expand** | Each section with nested content can be collapsed or expanded |
|
|
32
|
+
| βΎοΈ **Infinite Scrolling** | Built-in support for loading more items when reaching the bottom |
|
|
33
|
+
| π¨ **Fully Customizable** | Complete control over rendering via render props |
|
|
34
|
+
| π€ **TypeScript Ready** | Fully typed with comprehensive interfaces |
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## π Quick Start
|
|
39
|
+
|
|
40
|
+
### Installation
|
|
18
41
|
|
|
19
42
|
```bash
|
|
20
|
-
npm
|
|
43
|
+
npm install @galangel/react-scroll-magic
|
|
21
44
|
```
|
|
22
45
|
|
|
23
46
|
or
|
|
@@ -26,52 +49,309 @@ or
|
|
|
26
49
|
yarn add @galangel/react-scroll-magic
|
|
27
50
|
```
|
|
28
51
|
|
|
29
|
-
|
|
52
|
+
### Basic Usage
|
|
53
|
+
|
|
54
|
+
```jsx
|
|
55
|
+
import { Scroll } from '@galangel/react-scroll-magic';
|
|
56
|
+
|
|
57
|
+
const items = [
|
|
58
|
+
{
|
|
59
|
+
id: 'section-1',
|
|
60
|
+
render: ({ collapse }) => (
|
|
61
|
+
<div style={{ padding: '10px', backgroundColor: '#f0f0f0' }}>
|
|
62
|
+
Header 1
|
|
63
|
+
{collapse && (
|
|
64
|
+
<button onClick={collapse.isOpen ? collapse.close : collapse.open}>{collapse.isOpen ? 'βΌ' : 'βΆ'}</button>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
),
|
|
68
|
+
nestedItems: [
|
|
69
|
+
{ render: () => <div style={{ padding: '10px' }}>Item 1.1</div> },
|
|
70
|
+
{ render: () => <div style={{ padding: '10px' }}>Item 1.2</div> },
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'section-2',
|
|
75
|
+
render: () => <div style={{ padding: '10px' }}>Simple Item</div>,
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
function App() {
|
|
80
|
+
return (
|
|
81
|
+
<div style={{ height: '400px', width: '100%' }}>
|
|
82
|
+
<Scroll items={items} headerBehavior="push" scrollBehavior="smooth" />
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## π API Reference
|
|
91
|
+
|
|
92
|
+
### Scroll Component Props
|
|
93
|
+
|
|
94
|
+
| Prop | Type | Default | Description |
|
|
95
|
+
| ---------------- | --------------------------------- | ---------- | -------------------------------------------------------------------------------------- |
|
|
96
|
+
| `items` | `Items` | Required | Array of items to render. Each item has a `render` function and optional `nestedItems` |
|
|
97
|
+
| `stickTo` | `'top' \| 'bottom' \| 'all'` | `'all'` | Where headers should stick when scrolling |
|
|
98
|
+
| `scrollBehavior` | `'auto' \| 'instant' \| 'smooth'` | `'smooth'` | CSS scroll-behavior when clicking headers |
|
|
99
|
+
| `headerBehavior` | `'stick' \| 'push' \| 'none'` | `'none'` | How headers behave when scrolling |
|
|
100
|
+
| `loading` | `Loading` | Optional | Configuration for infinite scrolling |
|
|
101
|
+
|
|
102
|
+
### Item Structure
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
interface Item {
|
|
106
|
+
id?: string; // Optional unique identifier
|
|
107
|
+
render: (props: {
|
|
108
|
+
// Render function for the item
|
|
109
|
+
collapse?: {
|
|
110
|
+
isOpen: boolean; // Current collapse state
|
|
111
|
+
open: () => void; // Function to expand
|
|
112
|
+
close: () => void; // Function to collapse
|
|
113
|
+
};
|
|
114
|
+
}) => JSX.Element;
|
|
115
|
+
nestedItems?: Item[]; // Optional nested items (makes this a header)
|
|
116
|
+
}
|
|
117
|
+
```
|
|
30
118
|
|
|
31
|
-
|
|
119
|
+
### Loading Type Definition
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
interface Loading {
|
|
123
|
+
onBottomReached?: () => Promise<void>; // Callback when user scrolls to bottom
|
|
124
|
+
render?: (isLoading: boolean) => JSX.Element; // Custom loading indicator renderer
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## βΎοΈ Infinite Scrolling Example
|
|
32
131
|
|
|
33
132
|
```jsx
|
|
34
|
-
import React from 'react';
|
|
35
|
-
import {
|
|
133
|
+
import React, { useState } from 'react';
|
|
134
|
+
import { Scroll } from '@galangel/react-scroll-magic';
|
|
135
|
+
|
|
136
|
+
const InfiniteScrollExample = () => {
|
|
137
|
+
const [items, setItems] = useState([
|
|
138
|
+
{ render: () => <div>Initial Item 1</div> },
|
|
139
|
+
{ render: () => <div>Initial Item 2</div> },
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
const loadMoreItems = async () => {
|
|
143
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
144
|
+
|
|
145
|
+
const newItems = Array.from({ length: 10 }, (_, i) => ({
|
|
146
|
+
render: () => <div>New Item {items.length + i + 1}</div>,
|
|
147
|
+
}));
|
|
148
|
+
|
|
149
|
+
setItems((prev) => [...prev, ...newItems]);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const loading = {
|
|
153
|
+
onBottomReached: loadMoreItems,
|
|
154
|
+
render: (isLoading) => (
|
|
155
|
+
<div style={{ textAlign: 'center', padding: '20px' }}>{isLoading ? 'Loading...' : 'Load more'}</div>
|
|
156
|
+
),
|
|
157
|
+
};
|
|
36
158
|
|
|
37
|
-
const App = () => {
|
|
38
159
|
return (
|
|
39
|
-
<
|
|
40
|
-
items={
|
|
41
|
-
|
|
42
|
-
/* array of items */
|
|
43
|
-
]
|
|
44
|
-
}
|
|
45
|
-
{...props}
|
|
46
|
-
/>
|
|
160
|
+
<div style={{ height: '400px', width: '100%' }}>
|
|
161
|
+
<Scroll items={items} loading={loading} headerBehavior="none" />
|
|
162
|
+
</div>
|
|
47
163
|
);
|
|
48
164
|
};
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## π¨ Styling & CSS Classes
|
|
170
|
+
|
|
171
|
+
The component uses semantic CSS classes that you can target for custom styling. Here's a complete reference:
|
|
172
|
+
|
|
173
|
+
### CSS Class Reference
|
|
174
|
+
|
|
175
|
+
| Class Name | Element | Description |
|
|
176
|
+
| ------------------------- | ------- | ----------------------------------------------- |
|
|
177
|
+
| `.scroll-list` | `<ul>` | Main scroll container element |
|
|
178
|
+
| `.scroll-item` | `<li>` | Regular list item (items without `nestedItems`) |
|
|
179
|
+
| `.scroll-header` | `<li>` | Header item (items with `nestedItems`) |
|
|
180
|
+
| `.scroll-header.stick` | `<li>` | Header with `headerBehavior="stick"` |
|
|
181
|
+
| `.scroll-header.push` | `<li>` | Header with `headerBehavior="push"` |
|
|
182
|
+
| `.scroll-header.none` | `<li>` | Header with `headerBehavior="none"` |
|
|
183
|
+
| `.scroll-loading` | `<li>` | Loading indicator container |
|
|
184
|
+
| `.scroll-loading.loading` | `<li>` | Loading indicator when actively loading |
|
|
185
|
+
|
|
186
|
+
### Styling Examples
|
|
49
187
|
|
|
50
|
-
|
|
188
|
+
```css
|
|
189
|
+
/* Main scroll container */
|
|
190
|
+
.scroll-list {
|
|
191
|
+
list-style: none;
|
|
192
|
+
margin: 0;
|
|
193
|
+
padding: 0;
|
|
194
|
+
height: 100%;
|
|
195
|
+
overflow-y: auto;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* All items (headers and regular items) */
|
|
199
|
+
.scroll-item,
|
|
200
|
+
.scroll-header {
|
|
201
|
+
width: 100%;
|
|
202
|
+
box-sizing: border-box;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* Regular items */
|
|
206
|
+
.scroll-item {
|
|
207
|
+
padding: 12px 16px;
|
|
208
|
+
background-color: #fff;
|
|
209
|
+
border-bottom: 1px solid #eee;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Header items - base styles */
|
|
213
|
+
.scroll-header {
|
|
214
|
+
padding: 16px 20px;
|
|
215
|
+
background-color: #f5f5f5;
|
|
216
|
+
font-weight: 600;
|
|
217
|
+
cursor: pointer;
|
|
218
|
+
border-bottom: 1px solid #ddd;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Sticky header behavior */
|
|
222
|
+
.scroll-header.stick {
|
|
223
|
+
position: sticky;
|
|
224
|
+
/* top/bottom values are set dynamically by the component */
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* Push header behavior */
|
|
228
|
+
.scroll-header.push {
|
|
229
|
+
position: sticky;
|
|
230
|
+
/* top value is set dynamically by the component */
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* Header hover effect */
|
|
234
|
+
.scroll-header:hover {
|
|
235
|
+
background-color: #e8e8e8;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Loading indicator */
|
|
239
|
+
.scroll-loading {
|
|
240
|
+
display: none;
|
|
241
|
+
padding: 20px;
|
|
242
|
+
text-align: center;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.scroll-loading.loading {
|
|
246
|
+
display: block;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* Loading spinner animation */
|
|
250
|
+
.scroll-loading.loading::after {
|
|
251
|
+
content: '';
|
|
252
|
+
display: inline-block;
|
|
253
|
+
width: 20px;
|
|
254
|
+
height: 20px;
|
|
255
|
+
border: 2px solid #ccc;
|
|
256
|
+
border-top-color: #667eea;
|
|
257
|
+
border-radius: 50%;
|
|
258
|
+
animation: spin 1s linear infinite;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@keyframes spin {
|
|
262
|
+
to {
|
|
263
|
+
transform: rotate(360deg);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Dark Theme Example
|
|
269
|
+
|
|
270
|
+
```css
|
|
271
|
+
/* Dark theme styling */
|
|
272
|
+
.scroll-list {
|
|
273
|
+
background-color: #1a202c;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.scroll-item {
|
|
277
|
+
background-color: #2d3748;
|
|
278
|
+
color: #e2e8f0;
|
|
279
|
+
border-bottom: 1px solid #4a5568;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.scroll-header {
|
|
283
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
284
|
+
color: #fff;
|
|
285
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.scroll-header:hover {
|
|
289
|
+
filter: brightness(1.1);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.scroll-loading.loading::after {
|
|
293
|
+
border-color: #4a5568;
|
|
294
|
+
border-top-color: #667eea;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Important Notes
|
|
299
|
+
|
|
300
|
+
> β οΈ **Container Height Required**: The scroll container must have a defined height for scrolling to work properly.
|
|
301
|
+
|
|
302
|
+
```jsx
|
|
303
|
+
<div style={{ height: '400px' }}>
|
|
304
|
+
{' '}
|
|
305
|
+
{/* or height: '100vh' */}
|
|
306
|
+
<Scroll items={items} />
|
|
307
|
+
</div>
|
|
51
308
|
```
|
|
52
309
|
|
|
53
|
-
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## π‘ Tips & Best Practices
|
|
313
|
+
|
|
314
|
+
| Tip | Description |
|
|
315
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
316
|
+
| π― **Use Unique IDs** | Assign unique `id` properties to items for better performance and scroll-to functionality |
|
|
317
|
+
| π **Stop Propagation** | When adding click handlers inside items (like collapse buttons), use `e.stopPropagation()` to prevent scroll-to behavior |
|
|
318
|
+
| π **Set Container Height** | The Scroll component needs a container with a defined height (`height: 100vh` or fixed pixels) |
|
|
319
|
+
| π¨ **headerBehavior: "push"** | The "push" mode creates a natural feel where headers push each other out of view |
|
|
54
320
|
|
|
55
|
-
|
|
321
|
+
---
|
|
56
322
|
|
|
57
|
-
|
|
58
|
-
| ------------------------- | ------------------------------------- | --------------------------------------------------------------- |
|
|
59
|
-
| `stickTo` | `top`\|`bottom`\|`all` | how headers should stick |
|
|
60
|
-
| `scrollBehavior` | `scrollBehavior CSS property` | how the scrolling should behave when clicking a header |
|
|
61
|
-
| `headerBehavior` | `stick`\|`push`\|`none` | how the headers behave when scrolling |
|
|
62
|
-
| `items` | `items array` | nested structure of items |
|
|
63
|
-
| `loading` | `Loading` | object containing loading state and optional callbacks |
|
|
64
|
-
| `loading.onBottomReached` | `() => Promise<void>` | Optional callback function triggered when the bottom is reached |
|
|
65
|
-
| `loading.render` | `(isLoading: boolean) => JSX.Element` | Optional render function for custom loading indicator |
|
|
323
|
+
## π€ Real World Example: AI Chat
|
|
66
324
|
|
|
67
|
-
|
|
325
|
+
Check out the [AI Chat demo](https://galangel.github.io/react-scroll-magic/?path=/story/examples-ai-chat--ai-chat) showcasing a complex real-world use case with:
|
|
68
326
|
|
|
69
|
-
|
|
327
|
+
- π¬ **Question β Response Flow**: Messages with nested reasoning steps
|
|
328
|
+
- π§ **Collapsible Reasoning**: Auto-collapse when complete
|
|
329
|
+
- π **Deep Nesting**: Four levels of nesting working seamlessly
|
|
70
330
|
|
|
71
|
-
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## π License
|
|
334
|
+
|
|
335
|
+
This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for details.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## π€ Contributing
|
|
72
340
|
|
|
73
341
|
Contributions are welcome! Please read the [CONTRIBUTING](CONTRIBUTING.md) guidelines before submitting a pull request.
|
|
74
342
|
|
|
75
|
-
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## π¬ Contact
|
|
346
|
+
|
|
347
|
+
For any questions or feedback, please [open an issue](https://github.com/galangel/react-scroll-magic/issues) on GitHub.
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
<p align="center">
|
|
352
|
+
Made with πͺ by <a href="https://github.com/galangel">@galangel</a>
|
|
353
|
+
</p>
|
|
76
354
|
|
|
77
|
-
|
|
355
|
+
<p align="center">
|
|
356
|
+
<a href="https://www.buymeacoffee.com/galangel"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50" /></a>
|
|
357
|
+
</p>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { AgentStep } from '../types';
|
|
3
|
+
|
|
4
|
+
interface AgentStepHeaderProps {
|
|
5
|
+
step: AgentStep;
|
|
6
|
+
collapse?: {
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
open: () => void;
|
|
9
|
+
close: () => void;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export declare const AgentStepHeader: React.FC<AgentStepHeaderProps>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
|
|
3
|
+
interface ReasoningHeaderProps {
|
|
4
|
+
messageId: string;
|
|
5
|
+
messageIsComplete?: boolean;
|
|
6
|
+
collapse?: {
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
open: () => void;
|
|
9
|
+
close: () => void;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export declare const ReasoningHeader: React.FC<ReasoningHeaderProps>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { QuestionBubble } from './QuestionBubble';
|
|
2
|
+
export { AgentStepHeader } from './AgentStepHeader';
|
|
3
|
+
export { ReasoningHeader } from './ReasoningHeader';
|
|
4
|
+
export { SolutionHeader } from './SolutionHeader';
|
|
5
|
+
export { OutputLineItem } from './OutputLineItem';
|
|
6
|
+
export { ChatHeader } from './ChatHeader';
|
|
7
|
+
export { ChatInput } from './ChatInput';
|
|
8
|
+
export { EmptyState } from './EmptyState';
|
|
9
|
+
export { ChatStyles } from './ChatStyles';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface UseAutoScrollOptions {
|
|
2
|
+
threshold?: number;
|
|
3
|
+
scrollIdleDelay?: number;
|
|
4
|
+
hasContent?: boolean;
|
|
5
|
+
}
|
|
6
|
+
interface UseAutoScrollReturn {
|
|
7
|
+
scrollContainerRef: React.RefObject<HTMLDivElement>;
|
|
8
|
+
shouldAutoScroll: () => boolean;
|
|
9
|
+
scrollToBottom: () => void;
|
|
10
|
+
forceAutoScrollOn: () => void;
|
|
11
|
+
}
|
|
12
|
+
export declare const useAutoScroll: ({ threshold, scrollIdleDelay, hasContent, }?: UseAutoScrollOptions) => UseAutoScrollReturn;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface OutputLine {
|
|
2
|
+
id: string;
|
|
3
|
+
text: string;
|
|
4
|
+
}
|
|
5
|
+
export interface AgentStep {
|
|
6
|
+
id: string;
|
|
7
|
+
type: 'thinking' | 'searching' | 'analyzing' | 'solution';
|
|
8
|
+
title: string;
|
|
9
|
+
outputs: OutputLine[];
|
|
10
|
+
isComplete: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ChatMessage {
|
|
13
|
+
id: string;
|
|
14
|
+
question: string;
|
|
15
|
+
steps: AgentStep[];
|
|
16
|
+
isComplete: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface StepConfig {
|
|
19
|
+
icon: string;
|
|
20
|
+
title: string;
|
|
21
|
+
bgColor: string;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const generateQuestion: () => string;
|
package/dist/cjs/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("react/jsx-runtime"),l=require("react"),w=(e=[])=>e.slice(0,e.length-1).map((u,h)=>e.slice(0,h+1)),k=l.createContext({getTopHeadersTotalHeight:()=>0,getBottomHeadersTotalHeight:()=>0,scrollToView:()=>{},setListRef:()=>{},stickTo:"all",scrollBehavior:"smooth",headerBehavior:"none",addHeader:()=>{},headerCollaspeOpen:()=>{},headerCollaspeClose:()=>{},collapsedPaths:[]}),z=()=>l.useContext(k),F=({children:e,stickTo:t="all",scrollBehavior:g="smooth",headerBehavior:u="none"})=>{const[h,p]=l.useState(null),[i,r]=l.useState([]),a=l.useRef({}),b=s=>{const n=a.current[s.join("-")],c=n.nextElementSibling;if(h&&c){const C=window.getComputedStyle(c),d=n.getBoundingClientRect().height,o=f(s),S=parseFloat(C.marginTop)+parseFloat(C.marginBottom)+parseFloat(C.paddingTop)+parseFloat(C.paddingBottom)+parseFloat(C.borderTopWidth)+parseFloat(C.borderBottomWidth),j=Math.ceil(t==="bottom"?c.offsetTop-d:c.offsetTop-o-d+S);h.scrollTo({top:j,behavior:g})}},x=(s,n)=>{a.current[n.join("-")]=s},f=(s=[])=>{const n=w(s),c=n.map(o=>o.join("-"));if(u==="stick"){const o=n.reduce((H,y)=>{const P=y[y.length-1],O=Array.from({length:P},(I,L)=>[...y.slice(0,y.length-1),L]).map(I=>I.join("-"));return H.push(...O),H},[]);c.push(...o);const S=s[s.length-1],j=Array.from({length:S},(H,y)=>[...s.slice(0,s.length-1),y]).map(H=>H.join("-"));c.push(...j)}return c.map(o=>a.current[o]).reduce((o,S)=>{var H;return o+((H=S==null?void 0:S.getBoundingClientRect)==null?void 0:H.call(S).height)||0},0)},T=(s=[])=>{const n=s[s.length-1];return Object.entries(a.current).reduce((d,[o,S])=>{const j=o.split("-").map(Number);return j.length===1&&j[0]>n&&d.push(S),d},[]).reduce((d,o)=>{var j;return d+((j=o==null?void 0:o.getBoundingClientRect)==null?void 0:j.call(o).height)||0},0)},R=s=>{const n=s.join("-");i.includes(n)&&r(c=>c.filter(C=>C!==n))},B=s=>{const n=s.join("-");i.includes(n)||r(c=>[...c,n])};return m.jsx(k.Provider,{value:{addHeader:x,getTopHeadersTotalHeight:f,getBottomHeadersTotalHeight:T,stickTo:t,scrollToView:b,setListRef:p,scrollBehavior:g,headerBehavior:u,headerCollaspeOpen:R,headerCollaspeClose:B,collapsedPaths:i},children:e})},M=({path:e,itemId:t,itemRender:g})=>{const{getTopHeadersTotalHeight:u,getBottomHeadersTotalHeight:h,scrollToView:p,stickTo:i,headerBehavior:r,addHeader:a,headerCollaspeClose:b,headerCollaspeOpen:x,collapsedPaths:f}=z(),T=l.useRef(null),R=()=>{p(e)};l.useEffect(()=>{if(T.current){const d=T.current;a(d,e)}},[T]);const B=l.useMemo(()=>{if(r==="stick"){let d="auto",o="auto";return d=i==="top"||i==="all"?u(e):"auto",o=i==="bottom"||i==="all"?h(e):"auto",{top:d,bottom:o,zIndex:100-e.length}}else if(r==="push")return{top:u(e),zIndex:100-e.length}},[r,e,i]),s=l.useCallback(()=>x(e),[x,e]),n=l.useCallback(()=>b(e),[b,e]),c=l.useMemo(()=>f.includes(e.join("-")),[f]),C=l.useMemo(()=>g({collapse:{open:s,close:n,isOpen:!c}}),[g,c,s,n]);return m.jsx("li",{id:t,onClick:R,className:`scroll-header ${r} `,style:{...B},"aria-label":"Scroll Header",role:"heading","aria-level":e.length,ref:T,children:C})},N=({itemRender:e,itemId:t})=>m.jsx("li",{id:t,role:"listitem","aria-label":"Scroll Item",className:"scroll-item ",children:e({})}),E={display:"none"},v=({headerBehavior:e,items:t,path:g=[],collapsedPaths:u})=>{const h=e==="push"?"section":l.Fragment;return m.jsx("section",{children:t.map((p,i)=>{var a;const r=[...g,i];if((a=p.nestedItems)!=null&&a.length){const x=u.includes(r.join("-"))?E:void 0;return m.jsxs(h,{children:[m.jsx(M,{path:r,itemRender:p.render,itemId:p.id}),m.jsx("section",{style:x,children:v({items:p.nestedItems,headerBehavior:e,path:r,collapsedPaths:u})})]},r.join("-"))}else return m.jsx(N,{itemRender:p.render,itemId:p.id},r.join("-"))})})},V=({loading:e})=>e?m.jsx("li",{role:"listitem","aria-label":"Scroll Loading",className:`scroll-loading ${e?"loading":""}`}):null,W=({items:e,loading:t})=>{const{setListRef:g,headerBehavior:u,collapsedPaths:h,scrollBehavior:p}=z(),[i,r]=l.useState(!1),a=l.useRef(null);l.useEffect(()=>{a.current&&g(a.current)},[a]);const b=l.useCallback(x=>{if(e.length===0||!(t!=null&&t.onBottomReached))return;const f=x.target;Math.floor(f.scrollHeight-f.scrollTop)<f.clientHeight+10&&(f.dataset.loading||(f.dataset.loading="true",r(!0),t.onBottomReached().finally(()=>{f.dataset.loading="",r(!1)})))},[e.length===0,t==null?void 0:t.onBottomReached]);return m.jsxs("ul",{ref:a,className:"scroll-list",onScroll:t!=null&&t.onBottomReached?b:void 0,style:{scrollBehavior:p},children:[v({items:e,headerBehavior:u,collapsedPaths:h}),t!=null&&t.render?t.render(i):m.jsx(V,{loading:i})]})},_=({stickTo:e,scrollBehavior:t,headerBehavior:g,loading:u,items:h})=>m.jsx(F,{headerBehavior:g,stickTo:e,scrollBehavior:t,children:m.jsx(W,{loading:u,items:h})});exports.Scroll=_;
|
package/dist/es/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { jsx as C, jsxs as
|
|
2
|
-
import B, { useState as P, useRef as k, useEffect as
|
|
3
|
-
const W = (e = []) => e.slice(0, e.length - 1).map((d,
|
|
1
|
+
import { jsx as C, jsxs as L } from "react/jsx-runtime";
|
|
2
|
+
import B, { useState as P, useRef as k, useEffect as O, useMemo as j, useCallback as z } from "react";
|
|
3
|
+
const W = (e = []) => e.slice(0, e.length - 1).map((d, m) => e.slice(0, m + 1)), w = B.createContext({
|
|
4
4
|
getTopHeadersTotalHeight: () => 0,
|
|
5
5
|
getBottomHeadersTotalHeight: () => 0,
|
|
6
6
|
scrollToView: () => {
|
|
@@ -17,40 +17,40 @@ const W = (e = []) => e.slice(0, e.length - 1).map((d, h) => e.slice(0, h + 1)),
|
|
|
17
17
|
headerCollaspeClose: () => {
|
|
18
18
|
},
|
|
19
19
|
collapsedPaths: []
|
|
20
|
-
}), F = () => B.useContext(
|
|
20
|
+
}), F = () => B.useContext(w), _ = ({
|
|
21
21
|
children: e,
|
|
22
22
|
stickTo: t = "all",
|
|
23
23
|
scrollBehavior: u = "smooth",
|
|
24
24
|
headerBehavior: d = "none"
|
|
25
25
|
}) => {
|
|
26
|
-
const [
|
|
26
|
+
const [m, g] = P(null), [c, l] = P([]), i = k({}), b = (o) => {
|
|
27
27
|
const n = i.current[o.join("-")], r = n.nextElementSibling;
|
|
28
|
-
if (
|
|
29
|
-
const p = window.getComputedStyle(r), a = n.getBoundingClientRect().height, s =
|
|
30
|
-
|
|
28
|
+
if (m && r) {
|
|
29
|
+
const p = window.getComputedStyle(r), a = n.getBoundingClientRect().height, s = h(o), f = parseFloat(p.marginTop) + parseFloat(p.marginBottom) + parseFloat(p.paddingTop) + parseFloat(p.paddingBottom) + parseFloat(p.borderTopWidth) + parseFloat(p.borderBottomWidth), H = Math.ceil(t === "bottom" ? r.offsetTop - a : r.offsetTop - s - a + f);
|
|
30
|
+
m.scrollTo({ top: H, behavior: u });
|
|
31
31
|
}
|
|
32
|
-
},
|
|
32
|
+
}, S = (o, n) => {
|
|
33
33
|
i.current[n.join("-")] = o;
|
|
34
|
-
},
|
|
34
|
+
}, h = (o = []) => {
|
|
35
35
|
const n = W(o), r = n.map((s) => s.join("-"));
|
|
36
36
|
if (d === "stick") {
|
|
37
|
-
const s = n.reduce((
|
|
37
|
+
const s = n.reduce((T, y) => {
|
|
38
38
|
const M = y[y.length - 1], E = Array.from({ length: M }, (v, V) => [
|
|
39
39
|
...y.slice(0, y.length - 1),
|
|
40
40
|
V
|
|
41
41
|
]).map((v) => v.join("-"));
|
|
42
|
-
return
|
|
42
|
+
return T.push(...E), T;
|
|
43
43
|
}, []);
|
|
44
44
|
r.push(...s);
|
|
45
|
-
const f = o[o.length - 1], H = Array.from({ length: f }, (
|
|
45
|
+
const f = o[o.length - 1], H = Array.from({ length: f }, (T, y) => [
|
|
46
46
|
...o.slice(0, o.length - 1),
|
|
47
47
|
y
|
|
48
|
-
]).map((
|
|
48
|
+
]).map((T) => T.join("-"));
|
|
49
49
|
r.push(...H);
|
|
50
50
|
}
|
|
51
51
|
return r.map((s) => i.current[s]).reduce((s, f) => {
|
|
52
|
-
var
|
|
53
|
-
return s + ((
|
|
52
|
+
var T;
|
|
53
|
+
return s + ((T = f == null ? void 0 : f.getBoundingClientRect) == null ? void 0 : T.call(f).height) || 0;
|
|
54
54
|
}, 0);
|
|
55
55
|
}, x = (o = []) => {
|
|
56
56
|
const n = o[o.length - 1];
|
|
@@ -69,15 +69,15 @@ const W = (e = []) => e.slice(0, e.length - 1).map((d, h) => e.slice(0, h + 1)),
|
|
|
69
69
|
c.includes(n) || l((r) => [...r, n]);
|
|
70
70
|
};
|
|
71
71
|
return /* @__PURE__ */ C(
|
|
72
|
-
|
|
72
|
+
w.Provider,
|
|
73
73
|
{
|
|
74
74
|
value: {
|
|
75
|
-
addHeader:
|
|
76
|
-
getTopHeadersTotalHeight:
|
|
75
|
+
addHeader: S,
|
|
76
|
+
getTopHeadersTotalHeight: h,
|
|
77
77
|
getBottomHeadersTotalHeight: x,
|
|
78
78
|
stickTo: t,
|
|
79
|
-
scrollToView:
|
|
80
|
-
setListRef:
|
|
79
|
+
scrollToView: b,
|
|
80
|
+
setListRef: g,
|
|
81
81
|
scrollBehavior: u,
|
|
82
82
|
headerBehavior: d,
|
|
83
83
|
headerCollaspeOpen: I,
|
|
@@ -90,18 +90,18 @@ const W = (e = []) => e.slice(0, e.length - 1).map((d, h) => e.slice(0, h + 1)),
|
|
|
90
90
|
}, A = ({ path: e, itemId: t, itemRender: u }) => {
|
|
91
91
|
const {
|
|
92
92
|
getTopHeadersTotalHeight: d,
|
|
93
|
-
getBottomHeadersTotalHeight:
|
|
94
|
-
scrollToView:
|
|
93
|
+
getBottomHeadersTotalHeight: m,
|
|
94
|
+
scrollToView: g,
|
|
95
95
|
stickTo: c,
|
|
96
96
|
headerBehavior: l,
|
|
97
97
|
addHeader: i,
|
|
98
|
-
headerCollaspeClose:
|
|
99
|
-
headerCollaspeOpen:
|
|
100
|
-
collapsedPaths:
|
|
98
|
+
headerCollaspeClose: b,
|
|
99
|
+
headerCollaspeOpen: S,
|
|
100
|
+
collapsedPaths: h
|
|
101
101
|
} = F(), x = k(null), I = () => {
|
|
102
|
-
|
|
102
|
+
g(e);
|
|
103
103
|
};
|
|
104
|
-
|
|
104
|
+
O(() => {
|
|
105
105
|
if (x.current) {
|
|
106
106
|
const a = x.current;
|
|
107
107
|
i(a, e);
|
|
@@ -110,10 +110,10 @@ const W = (e = []) => e.slice(0, e.length - 1).map((d, h) => e.slice(0, h + 1)),
|
|
|
110
110
|
const R = j(() => {
|
|
111
111
|
if (l === "stick") {
|
|
112
112
|
let a = "auto", s = "auto";
|
|
113
|
-
return a = c === "top" || c === "all" ? d(e) : "auto", s = c === "bottom" || c === "all" ?
|
|
113
|
+
return a = c === "top" || c === "all" ? d(e) : "auto", s = c === "bottom" || c === "all" ? m(e) : "auto", { top: a, bottom: s, zIndex: 100 - e.length };
|
|
114
114
|
} else if (l === "push")
|
|
115
115
|
return { top: d(e), zIndex: 100 - e.length };
|
|
116
|
-
}, [l, e, c]), o = z(() =>
|
|
116
|
+
}, [l, e, c]), o = z(() => S(e), [S, e]), n = z(() => b(e), [b, e]), r = j(() => h.includes(e.join("-")), [h]), p = j(
|
|
117
117
|
() => u({
|
|
118
118
|
collapse: {
|
|
119
119
|
open: o,
|
|
@@ -137,49 +137,49 @@ const W = (e = []) => e.slice(0, e.length - 1).map((d, h) => e.slice(0, h + 1)),
|
|
|
137
137
|
children: p
|
|
138
138
|
}
|
|
139
139
|
);
|
|
140
|
-
}, $ = ({ itemRender: e, itemId: t }) => /* @__PURE__ */ C("li", { id: t, role: "listitem", "aria-label": "Scroll Item", className: "scroll-item ", children: e({}) }), q = {
|
|
141
|
-
const
|
|
142
|
-
return /* @__PURE__ */ C("section", { children: t.map((
|
|
140
|
+
}, $ = ({ itemRender: e, itemId: t }) => /* @__PURE__ */ C("li", { id: t, role: "listitem", "aria-label": "Scroll Item", className: "scroll-item ", children: e({}) }), q = { display: "none" }, N = ({ headerBehavior: e, items: t, path: u = [], collapsedPaths: d }) => {
|
|
141
|
+
const m = e === "push" ? "section" : B.Fragment;
|
|
142
|
+
return /* @__PURE__ */ C("section", { children: t.map((g, c) => {
|
|
143
143
|
var i;
|
|
144
144
|
const l = [...u, c];
|
|
145
|
-
if ((i =
|
|
146
|
-
const S = d.includes(l.join("-")) ? q :
|
|
147
|
-
return /* @__PURE__ */
|
|
148
|
-
/* @__PURE__ */ C(A, { path: l, itemRender:
|
|
149
|
-
/* @__PURE__ */ C("section", { style: S, children: N({ items:
|
|
145
|
+
if ((i = g.nestedItems) != null && i.length) {
|
|
146
|
+
const S = d.includes(l.join("-")) ? q : void 0;
|
|
147
|
+
return /* @__PURE__ */ L(m, { children: [
|
|
148
|
+
/* @__PURE__ */ C(A, { path: l, itemRender: g.render, itemId: g.id }),
|
|
149
|
+
/* @__PURE__ */ C("section", { style: S, children: N({ items: g.nestedItems, headerBehavior: e, path: l, collapsedPaths: d }) })
|
|
150
150
|
] }, l.join("-"));
|
|
151
151
|
} else
|
|
152
|
-
return /* @__PURE__ */ C($, { itemRender:
|
|
152
|
+
return /* @__PURE__ */ C($, { itemRender: g.render, itemId: g.id }, l.join("-"));
|
|
153
153
|
}) });
|
|
154
154
|
}, D = ({ loading: e }) => e ? /* @__PURE__ */ C("li", { role: "listitem", "aria-label": "Scroll Loading", className: `scroll-loading ${e ? "loading" : ""}` }) : null, G = ({ items: e, loading: t }) => {
|
|
155
|
-
const { setListRef: u, headerBehavior: d, collapsedPaths:
|
|
156
|
-
|
|
155
|
+
const { setListRef: u, headerBehavior: d, collapsedPaths: m, scrollBehavior: g } = F(), [c, l] = B.useState(!1), i = k(null);
|
|
156
|
+
O(() => {
|
|
157
157
|
i.current && u(i.current);
|
|
158
158
|
}, [i]);
|
|
159
|
-
const
|
|
160
|
-
(
|
|
159
|
+
const b = z(
|
|
160
|
+
(S) => {
|
|
161
161
|
if (e.length === 0 || !(t != null && t.onBottomReached)) return;
|
|
162
|
-
const
|
|
163
|
-
Math.floor(
|
|
164
|
-
|
|
162
|
+
const h = S.target;
|
|
163
|
+
Math.floor(h.scrollHeight - h.scrollTop) < h.clientHeight + 10 && (h.dataset.loading || (h.dataset.loading = "true", l(!0), t.onBottomReached().finally(() => {
|
|
164
|
+
h.dataset.loading = "", l(!1);
|
|
165
165
|
})));
|
|
166
166
|
},
|
|
167
167
|
[e.length === 0, t == null ? void 0 : t.onBottomReached]
|
|
168
168
|
);
|
|
169
|
-
return /* @__PURE__ */
|
|
169
|
+
return /* @__PURE__ */ L(
|
|
170
170
|
"ul",
|
|
171
171
|
{
|
|
172
172
|
ref: i,
|
|
173
173
|
className: "scroll-list",
|
|
174
|
-
onScroll: t != null && t.onBottomReached ?
|
|
175
|
-
style: { scrollBehavior:
|
|
174
|
+
onScroll: t != null && t.onBottomReached ? b : void 0,
|
|
175
|
+
style: { scrollBehavior: g },
|
|
176
176
|
children: [
|
|
177
|
-
N({ items: e, headerBehavior: d, collapsedPaths:
|
|
177
|
+
N({ items: e, headerBehavior: d, collapsedPaths: m }),
|
|
178
178
|
t != null && t.render ? t.render(c) : /* @__PURE__ */ C(D, { loading: c })
|
|
179
179
|
]
|
|
180
180
|
}
|
|
181
181
|
);
|
|
182
|
-
}, Q = ({ stickTo: e, scrollBehavior: t, headerBehavior: u, loading: d, items:
|
|
182
|
+
}, Q = ({ stickTo: e, scrollBehavior: t, headerBehavior: u, loading: d, items: m }) => /* @__PURE__ */ C(_, { headerBehavior: u, stickTo: e, scrollBehavior: t, children: /* @__PURE__ */ C(G, { loading: d, items: m }) });
|
|
183
183
|
export {
|
|
184
184
|
Q as Scroll
|
|
185
185
|
};
|