@stream-mdx/react 0.4.0 → 1.0.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/CHANGELOG.md +385 -4
- package/README.md +141 -16
- package/dist/blocks-renderer-DAOBFBfi.d.ts +29 -0
- package/dist/blocks-renderer-DQEL1kJW.d.cts +29 -0
- package/dist/components/bottom-stick-scroll-area.cjs +268 -0
- package/dist/components/bottom-stick-scroll-area.d.cts +36 -0
- package/dist/components/bottom-stick-scroll-area.d.ts +36 -0
- package/dist/components/bottom-stick-scroll-area.mjs +249 -0
- package/dist/components/diff-renderer.cjs +86 -0
- package/dist/components/diff-renderer.d.cts +11 -0
- package/dist/components/diff-renderer.d.ts +11 -0
- package/dist/components/diff-renderer.mjs +66 -0
- package/dist/components/index.cjs +2879 -1295
- package/dist/components/index.d.cts +9 -2
- package/dist/components/index.d.ts +9 -2
- package/dist/components/index.mjs +2842 -1277
- package/dist/components/katex-math.cjs +220 -0
- package/dist/components/katex-math.d.cts +10 -0
- package/dist/components/katex-math.d.ts +10 -0
- package/dist/components/katex-math.mjs +188 -0
- package/dist/contexts/math-tracker.cjs +240 -521
- package/dist/contexts/math-tracker.mjs +235 -513
- package/dist/index-Cmne1yUK.d.cts +723 -0
- package/dist/index-D7dyyu3q.d.ts +723 -0
- package/dist/index.cjs +13180 -5640
- package/dist/index.d.cts +72 -40
- package/dist/index.d.ts +72 -40
- package/dist/index.mjs +12587 -5038
- package/dist/math/display-wrapper.cjs +7 -15
- package/dist/math/display-wrapper.d.cts +2 -2
- package/dist/math/display-wrapper.d.ts +2 -2
- package/dist/math/display-wrapper.mjs +2 -6
- package/dist/node-views-Bpc6Gkgl.d.cts +13 -0
- package/dist/node-views-DpnYdaAS.d.ts +13 -0
- package/dist/renderer/hooks.cjs +35 -20
- package/dist/renderer/hooks.d.cts +22 -1
- package/dist/renderer/hooks.d.ts +22 -1
- package/dist/renderer/hooks.mjs +27 -12
- package/dist/renderer/patch-batching.cjs +7 -8
- package/dist/renderer/patch-batching.d.cts +1 -1
- package/dist/renderer/patch-batching.d.ts +1 -1
- package/dist/renderer/patch-batching.mjs +2 -1
- package/dist/renderer/patch-coalescing.cjs +5 -8
- package/dist/renderer/patch-commit-scheduler.cjs +160 -326
- package/dist/renderer/patch-commit-scheduler.d.cts +28 -4
- package/dist/renderer/patch-commit-scheduler.d.ts +28 -4
- package/dist/renderer/patch-commit-scheduler.mjs +157 -318
- package/dist/renderer/store.cjs +3847 -1099
- package/dist/renderer/store.d.cts +111 -3
- package/dist/renderer/store.d.ts +111 -3
- package/dist/renderer/store.mjs +3847 -1085
- package/dist/renderer/virtualized-code.cjs +415 -71
- package/dist/renderer/virtualized-code.d.cts +24 -23
- package/dist/renderer/virtualized-code.d.ts +24 -23
- package/dist/renderer/virtualized-code.mjs +409 -62
- package/dist/renderer.cjs +10939 -3974
- package/dist/renderer.d.cts +100 -29
- package/dist/renderer.d.ts +100 -29
- package/dist/renderer.mjs +10711 -3697
- package/dist/server-regions-CPLHGli0.d.cts +32 -0
- package/dist/server-regions-mUhyoKi7.d.ts +32 -0
- package/dist/server.cjs +10286 -3365
- package/dist/server.d.cts +17 -247
- package/dist/server.d.ts +17 -247
- package/dist/server.mjs +10565 -3619
- package/dist/{streaming-markdown-DOc2uLiP.d.ts → streaming-markdown-yELbJxyH.d.ts} +51 -55
- package/dist/{streaming-markdown-D-JcByd9.d.cts → streaming-markdown-zVwFuaJG.d.cts} +51 -55
- package/dist/streaming-markdown.cjs +11828 -4879
- package/dist/streaming-markdown.d.cts +9 -2
- package/dist/streaming-markdown.d.ts +9 -2
- package/dist/streaming-markdown.mjs +11820 -4821
- package/dist/trusted-mdx/index.cjs +833 -0
- package/dist/trusted-mdx/index.d.cts +11 -0
- package/dist/trusted-mdx/index.d.ts +11 -0
- package/dist/trusted-mdx/index.mjs +807 -0
- package/dist/trusted-mdx/native-children-slot.cjs +45 -0
- package/dist/trusted-mdx/native-children-slot.d.cts +11 -0
- package/dist/trusted-mdx/native-children-slot.d.ts +11 -0
- package/dist/trusted-mdx/native-children-slot.mjs +24 -0
- package/dist/trusted-mdx/server.cjs +752 -0
- package/dist/trusted-mdx/server.d.cts +24 -0
- package/dist/trusted-mdx/server.d.ts +24 -0
- package/dist/trusted-mdx/server.mjs +724 -0
- package/dist/trusted-mdx-contract-ElKpkDI5.d.cts +113 -0
- package/dist/trusted-mdx-contract-ElKpkDI5.d.ts +113 -0
- package/dist/utils/inline-html.cjs +246 -147
- package/dist/utils/inline-html.d.cts +39 -6
- package/dist/utils/inline-html.d.ts +39 -6
- package/dist/utils/inline-html.mjs +237 -134
- package/package.json +52 -18
- package/shadcn/README.md +28 -0
- package/shadcn/bottom-stick-scroll-area.tsx +358 -0
- package/dist/index-D2MYusAM.d.cts +0 -248
- package/dist/index-D2MYusAM.d.ts +0 -248
- package/dist/mdx-client.cjs +0 -408
- package/dist/mdx-client.d.cts +0 -113
- package/dist/mdx-client.d.ts +0 -113
- package/dist/mdx-client.mjs +0 -369
- package/dist/mdx-coordinator.cjs +0 -563
- package/dist/mdx-coordinator.d.cts +0 -21
- package/dist/mdx-coordinator.d.ts +0 -21
- package/dist/mdx-coordinator.mjs +0 -529
- package/dist/renderer/node-views.cjs +0 -1642
- package/dist/renderer/node-views.d.cts +0 -12
- package/dist/renderer/node-views.d.ts +0 -12
- package/dist/renderer/node-views.mjs +0 -1616
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# `@stream-mdx/react`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@stream-mdx/react` is the renderer package for StreamMDX. It owns the React component surface, the renderer store, patch scheduling, server rendering helpers, and the packaged bottom-stick scroll component used by the docs/demo surfaces.
|
|
4
4
|
|
|
5
|
-
Most apps should install `stream-mdx` instead
|
|
5
|
+
**Most apps should install [`stream-mdx`](../stream-mdx/README.md) instead.** Use this package directly when you want the React layer without the convenience wrapper.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -10,19 +10,33 @@ Most apps should install `stream-mdx` instead of this package directly. Use `@st
|
|
|
10
10
|
npm install @stream-mdx/react @stream-mdx/worker
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Peer dependencies:
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
| Package | Range |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `react` | `>=18.2.0` |
|
|
18
|
+
| `react-dom` | `>=18.2.0` |
|
|
19
|
+
|
|
20
|
+
## Primary Exports
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
| Export | Purpose |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| `@stream-mdx/react` | Main React surface including `<StreamingMarkdown />` |
|
|
25
|
+
| `@stream-mdx/react/server` | Server/static render helpers |
|
|
26
|
+
| `@stream-mdx/react/components` | Shared UI component exports |
|
|
27
|
+
| `@stream-mdx/react/components/bottom-stick-scroll-area` | Packaged sticky-bottom scroll container |
|
|
28
|
+
| `@stream-mdx/react/renderer` | Lower-level renderer exports |
|
|
29
|
+
| `@stream-mdx/react/renderer/patch-commit-scheduler` | Scheduler implementation surface |
|
|
30
|
+
| `@stream-mdx/react/renderer/store` | Renderer store surface |
|
|
31
|
+
| `@stream-mdx/react/trusted-mdx` | Explicit trusted-MDX engine factory and pure contract types |
|
|
32
|
+
|
|
33
|
+
## Quickstart
|
|
18
34
|
|
|
19
35
|
```bash
|
|
20
36
|
mkdir -p public/workers
|
|
21
37
|
cp node_modules/@stream-mdx/worker/dist/hosted/markdown-worker.js public/workers/markdown-worker.js
|
|
22
38
|
```
|
|
23
39
|
|
|
24
|
-
Render streaming markdown from a client component:
|
|
25
|
-
|
|
26
40
|
```tsx
|
|
27
41
|
"use client";
|
|
28
42
|
|
|
@@ -33,20 +47,131 @@ export function Demo({ text }: { text: string }) {
|
|
|
33
47
|
<StreamingMarkdown
|
|
34
48
|
text={text}
|
|
35
49
|
worker="/workers/markdown-worker.js"
|
|
36
|
-
features={{ html: true, tables: true, math: true,
|
|
37
|
-
mdxCompileMode="worker"
|
|
50
|
+
features={{ html: true, tables: true, math: true, footnotes: true }}
|
|
38
51
|
/>
|
|
39
52
|
);
|
|
40
53
|
}
|
|
41
54
|
```
|
|
42
55
|
|
|
43
|
-
|
|
56
|
+
`engine` defaults to `"auto"` and bootstraps up to 4,096 accepted UTF-16 source units with the existing inline engine while worker setup and grammar prewarm are pending. Further source is deferred beyond that limit. `"worker"` requires a Worker; `"main"` runs parsing on the browser thread without a bootstrap cap. Engine setup runs after mounting, not during SSR. Use compiled snapshots and the server entry for server markup.
|
|
57
|
+
|
|
58
|
+
### Rendering customization
|
|
59
|
+
|
|
60
|
+
`components` replaces the removed renderer maps:
|
|
61
|
+
|
|
62
|
+
- `inlineComponents` → `components["inline:<kind>"]` or `components["extension:<name>"]`
|
|
63
|
+
- `htmlElements` → `components["html:<tag>"]`
|
|
64
|
+
- `tableElements` → `components.table` wrappers or headless table views with `useTableBlock` / `useTableRows`
|
|
65
|
+
- `codeRenderers` → `components.code` wrappers or headless code views; wrappers receive `metadata.language`, and `slots.actions` replaces native code actions. Mermaid diagrams use `engines.mermaid` from `createMermaidEngine()`
|
|
66
|
+
- `mathRenderer` → presentation becomes a math wrapper/view in `components["inline:math"]`; TeX rendering uses `engines.math` (KaTeX remains the default)
|
|
67
|
+
- `mdxComponents` → restricted `contentComponents` registrations, or trusted `contentPolicy="trusted-mdx"` with `engines.mdx`
|
|
68
|
+
|
|
69
|
+
`features.math: false` disables math parsing. See the [public API](../../docs/PUBLIC_API.md) for HTML policy, restricted/trusted MDX, inert artifact fences, and optional finalized-block unified routing.
|
|
70
|
+
|
|
71
|
+
`colorScheme` accepts `"light"`, `"dark"`, or `"system"` and sets the native root's `data-color-scheme`. System mode follows `prefers-color-scheme` with CSS only; when unset, a `.dark` ancestor keeps the existing class-based behavior. Native roots carry `data-stream-mdx` and `not-prose`, so enclosing typography-plugin selectors do not restyle native output and the optional Tailwind theme scopes native styles to that root.
|
|
72
|
+
|
|
73
|
+
## Typical Uses
|
|
74
|
+
|
|
75
|
+
| Use case | API |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| Browser/client rendering | `<StreamingMarkdown />` |
|
|
78
|
+
| SSR / SSG / static export rendering | `MarkdownBlocksRenderer` from `@stream-mdx/react/server` |
|
|
79
|
+
| Rich streaming/chat container | `BottomStickScrollArea` |
|
|
80
|
+
| Advanced renderer internals | `renderer/store`, `renderer/patch-commit-scheduler` |
|
|
81
|
+
|
|
82
|
+
### Server-side block rendering
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import { ComponentRegistry, MarkdownBlocksRenderer } from "@stream-mdx/react/server";
|
|
86
|
+
|
|
87
|
+
return <MarkdownBlocksRenderer blocks={blocks} componentRegistry={new ComponentRegistry()} />;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Default math stays as authored TeX source on the server and on the client's first render. KaTeX loads after commit and replaces that source after mount, so cold and preloaded clients hydrate from the same markup.
|
|
91
|
+
|
|
92
|
+
### Bottom-stick scroll area
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { BottomStickScrollArea } from "@stream-mdx/react/components/bottom-stick-scroll-area";
|
|
96
|
+
|
|
97
|
+
<BottomStickScrollArea className="h-[32rem]">{children}</BottomStickScrollArea>;
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Behavior notes:
|
|
101
|
+
|
|
102
|
+
- sticks to bottom while content appends
|
|
103
|
+
- detaches on upward user scroll
|
|
104
|
+
- supports smooth return-to-bottom behavior
|
|
105
|
+
- honors `prefers-reduced-motion: reduce` with instant jumps and no jump-chrome transition
|
|
106
|
+
- interrupts an active smooth return on wheel, touch, or scroll-key input without preventing native behavior
|
|
107
|
+
- exposes debug hooks used by deterministic checks in the repo
|
|
108
|
+
|
|
109
|
+
### Table row virtualization
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import { StreamingMarkdown } from "@stream-mdx/react";
|
|
113
|
+
|
|
114
|
+
<StreamingMarkdown
|
|
115
|
+
text={content}
|
|
116
|
+
tableVirtualization={{
|
|
117
|
+
enabled: true,
|
|
118
|
+
minRows: 40,
|
|
119
|
+
estimatedRowHeight: 44,
|
|
120
|
+
overscan: 8,
|
|
121
|
+
maxHeight: "600px",
|
|
122
|
+
}}
|
|
123
|
+
/>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Table virtualization is disabled by default. Only finalized tables meeting `minRows` virtualize after mounting in a browser and after the surrounding stream ends. SSR, unfinished tables, and active streams render every row.
|
|
127
|
+
|
|
128
|
+
Virtualized tables retain native table elements, a sticky header, measured variable row heights, and `aria-hidden` spacer rows. The last row remains reachable by scrolling.
|
|
129
|
+
When detached from the bottom, row measurements preserve the visible row and its pixel offset, including content partly covered by the sticky header. Scrolling chooses a new anchor; bottom attachment still follows measured tail growth.
|
|
130
|
+
|
|
131
|
+
### Code viewport and printing
|
|
132
|
+
|
|
133
|
+
Large native code blocks use a bounded reader viewport. The **Show full content** control mounts all authored lines while retaining the existing code wrapper and mounted native rows; toggling back restores the bounded window and reader anchor.
|
|
134
|
+
|
|
135
|
+
`StreamingMarkdown` accepts `codeVirtualization?: Partial<VirtualizedCodeConfig>` to configure native code viewport presentation:
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
<StreamingMarkdown text={content} codeVirtualization={{ followTail: true }} />
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`followTail` defaults to `false`, preserving the reader position when code is appended. Set it to `true` to opt in to following appended rows while attached at the physical bottom (within 2px). An upward scroll of more than 1px detaches; returning within 2px reattaches. Changing this presentation config does not reset authored source or replace the native viewport; turning `followTail` off preserves the existing reader anchor and scroll position on a subsequent append.
|
|
142
|
+
|
|
143
|
+
When a browser dispatches `beforeprint`, an eligible virtualized native code block temporarily mounts its full source and removes viewport clipping and height limits. `afterprint` restores the prior reader mode, scroll anchor, and native text selection without changing the manual full-content control.
|
|
144
|
+
|
|
145
|
+
Print preparation mounts the whole block in the DOM, so print-time memory and layout work scale with the source length. Custom code renderers and server-rendered output control their own print behavior.
|
|
146
|
+
|
|
147
|
+
Native whole-code copy/download uses cold authored source, preserving CR/LF and trailing blank lines rather than normalized display text. The test DOM exercises copy with an active selection and a Clipboard API stub; no physical browser or OS clipboard proof is claimed. Download shares the source action, but direct Blob browser proof remains pending. Native copied-status feedback cleanup prevents delayed updates after unmount.
|
|
148
|
+
Each eligible virtualized table also exposes a "Show full content" toggle. It mounts every row only on request; toggling it again restores the bounded viewport without replacing the native table or changing the reader's scroll position.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
### Table controls
|
|
152
|
+
|
|
153
|
+
Table controls are off by default. Enable Markdown copy or download for tables with `controls`:
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
<StreamingMarkdown
|
|
157
|
+
text={content}
|
|
158
|
+
features={{ tables: true }}
|
|
159
|
+
controls={{ table: { copy: ["markdown"], download: ["markdown"] } }}
|
|
160
|
+
/>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
For native tables, Markdown copy and download use the table's authored source slice. They preserve spacing, escaped pipes, inline Markdown, and original line endings, and exclude the final row's line ending and any following document content. TSV and CSV remain cell-value transforms, not source-preserving Markdown.
|
|
164
|
+
|
|
165
|
+
## When To Use This Package Directly
|
|
44
166
|
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
- Full docs site: https://kmccleary3301.github.io/stream-mdx/
|
|
167
|
+
- You are shipping a library that wants `@stream-mdx/react` explicitly in `peerDependencies` or `dependencies`.
|
|
168
|
+
- You need server rendering helpers without the convenience wrapper import path.
|
|
169
|
+
- You are working on renderer internals and want the lower-level exports directly.
|
|
49
170
|
|
|
50
|
-
##
|
|
171
|
+
## Documentation
|
|
51
172
|
|
|
52
|
-
-
|
|
173
|
+
- [`../../docs/PUBLIC_API.md`](../../docs/PUBLIC_API.md)
|
|
174
|
+
- [`../../docs/REACT_INTEGRATION_GUIDE.md`](../../docs/REACT_INTEGRATION_GUIDE.md)
|
|
175
|
+
- [`../../docs/SECURITY_MODEL.md`](../../docs/SECURITY_MODEL.md)
|
|
176
|
+
- [`../../docs/STREAMING_CORRECTNESS_CONTRACT.md`](../../docs/STREAMING_CORRECTNESS_CONTRACT.md)
|
|
177
|
+
- Docs site: <https://stream-mdx.vercel.app/docs>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Block, UrlTransform } from '@stream-mdx/core';
|
|
2
|
+
import React__default from 'react';
|
|
3
|
+
import { l as ElementFilterOptions, d as ComponentRegistry, _ as NodePresentations, b as ControlsConfig, c as ControlsComponentProps, w as AnimatedProp, C as ContentComponentsMap, $ as MermaidEngine } from './index-D7dyyu3q.js';
|
|
4
|
+
import { RendererStore } from './renderer/store.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React component for rendering markdown blocks
|
|
8
|
+
*/
|
|
9
|
+
interface MarkdownBlocksRendererProps extends ElementFilterOptions {
|
|
10
|
+
blocks: ReadonlyArray<Block>;
|
|
11
|
+
componentRegistry?: ComponentRegistry;
|
|
12
|
+
className?: string;
|
|
13
|
+
style?: React__default.CSSProperties;
|
|
14
|
+
colorScheme?: "light" | "dark" | "system";
|
|
15
|
+
store?: RendererStore;
|
|
16
|
+
components?: NodePresentations;
|
|
17
|
+
urlTransform?: UrlTransform;
|
|
18
|
+
controls?: ControlsConfig;
|
|
19
|
+
controlsComponent?: React__default.ComponentType<ControlsComponentProps>;
|
|
20
|
+
animated?: AnimatedProp;
|
|
21
|
+
statusMessage?: string;
|
|
22
|
+
contentComponents?: ContentComponentsMap;
|
|
23
|
+
engines?: {
|
|
24
|
+
mermaid?: MermaidEngine;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
declare const MarkdownBlocksRenderer: React__default.NamedExoticComponent<MarkdownBlocksRendererProps>;
|
|
28
|
+
|
|
29
|
+
export { MarkdownBlocksRenderer as M, type MarkdownBlocksRendererProps as a };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Block, UrlTransform } from '@stream-mdx/core';
|
|
2
|
+
import React__default from 'react';
|
|
3
|
+
import { l as ElementFilterOptions, d as ComponentRegistry, _ as NodePresentations, b as ControlsConfig, c as ControlsComponentProps, w as AnimatedProp, C as ContentComponentsMap, $ as MermaidEngine } from './index-Cmne1yUK.cjs';
|
|
4
|
+
import { RendererStore } from './renderer/store.cjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React component for rendering markdown blocks
|
|
8
|
+
*/
|
|
9
|
+
interface MarkdownBlocksRendererProps extends ElementFilterOptions {
|
|
10
|
+
blocks: ReadonlyArray<Block>;
|
|
11
|
+
componentRegistry?: ComponentRegistry;
|
|
12
|
+
className?: string;
|
|
13
|
+
style?: React__default.CSSProperties;
|
|
14
|
+
colorScheme?: "light" | "dark" | "system";
|
|
15
|
+
store?: RendererStore;
|
|
16
|
+
components?: NodePresentations;
|
|
17
|
+
urlTransform?: UrlTransform;
|
|
18
|
+
controls?: ControlsConfig;
|
|
19
|
+
controlsComponent?: React__default.ComponentType<ControlsComponentProps>;
|
|
20
|
+
animated?: AnimatedProp;
|
|
21
|
+
statusMessage?: string;
|
|
22
|
+
contentComponents?: ContentComponentsMap;
|
|
23
|
+
engines?: {
|
|
24
|
+
mermaid?: MermaidEngine;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
declare const MarkdownBlocksRenderer: React__default.NamedExoticComponent<MarkdownBlocksRendererProps>;
|
|
28
|
+
|
|
29
|
+
export { MarkdownBlocksRenderer as M, type MarkdownBlocksRendererProps as a };
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: !0 });
|
|
11
|
+
}, __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from == "object" || typeof from == "function")
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
!__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: !0 }), mod);
|
|
18
|
+
|
|
19
|
+
// src/components/bottom-stick-scroll-area.tsx
|
|
20
|
+
var bottom_stick_scroll_area_exports = {};
|
|
21
|
+
__export(bottom_stick_scroll_area_exports, {
|
|
22
|
+
BottomStickScrollArea: () => BottomStickScrollArea
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(bottom_stick_scroll_area_exports);
|
|
25
|
+
var import_react = require("react"), import_jsx_runtime = require("react/jsx-runtime"), EPS_BOTTOM_PX = 2, EPS_OVERFLOW_PX = 1, DETACH_SCROLL_DELTA_PX = 1, RETURN_MIN_MS = 180, RETURN_MAX_MS = 520, RETURN_PX_PER_MS = 2.2, RETURN_SETTLE_FRAMES = 2;
|
|
26
|
+
function clamp01(value) {
|
|
27
|
+
return value < 0 ? 0 : value > 1 ? 1 : value;
|
|
28
|
+
}
|
|
29
|
+
function easeOutCubic(value) {
|
|
30
|
+
return 1 - (1 - value) ** 3;
|
|
31
|
+
}
|
|
32
|
+
function joinClasses(...parts) {
|
|
33
|
+
return parts.filter(Boolean).join(" ");
|
|
34
|
+
}
|
|
35
|
+
function measure(viewport) {
|
|
36
|
+
let scrollTop = viewport.scrollTop, scrollHeight = viewport.scrollHeight, clientHeight = viewport.clientHeight, maxScrollTop = Math.max(0, scrollHeight - clientHeight), distanceToBottom = Math.max(0, maxScrollTop - scrollTop), isOverflowing = scrollHeight > clientHeight + EPS_OVERFLOW_PX;
|
|
37
|
+
return { scrollTop, scrollHeight, clientHeight, maxScrollTop, distanceToBottom, isOverflowing };
|
|
38
|
+
}
|
|
39
|
+
function isEditableTarget(target) {
|
|
40
|
+
if (!(target instanceof Element)) return !1;
|
|
41
|
+
if (target.closest("input, textarea, select, [role='textbox']")) return !0;
|
|
42
|
+
let contentEditable = target.closest("[contenteditable]");
|
|
43
|
+
return contentEditable !== null && contentEditable.getAttribute("contenteditable")?.toLowerCase() !== "false";
|
|
44
|
+
}
|
|
45
|
+
function isScrollKeyIntent(event) {
|
|
46
|
+
if (event.defaultPrevented || isEditableTarget(event.target)) return !1;
|
|
47
|
+
switch (event.key) {
|
|
48
|
+
case "ArrowDown":
|
|
49
|
+
case "ArrowUp":
|
|
50
|
+
case "PageDown":
|
|
51
|
+
case "PageUp":
|
|
52
|
+
case "Home":
|
|
53
|
+
case "End":
|
|
54
|
+
return !0;
|
|
55
|
+
case " ":
|
|
56
|
+
case "Spacebar":
|
|
57
|
+
return event.target instanceof Element ? event.target.closest(
|
|
58
|
+
"button, input, summary, [role='button'], [role='checkbox'], [role='switch'], [role='radio'], [role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']"
|
|
59
|
+
) === null : !0;
|
|
60
|
+
default:
|
|
61
|
+
return !1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function DefaultJumpButton({ canJump, jumpToBottom }) {
|
|
65
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
66
|
+
"button",
|
|
67
|
+
{
|
|
68
|
+
type: "button",
|
|
69
|
+
onClick: jumpToBottom,
|
|
70
|
+
"aria-label": "Scroll to bottom",
|
|
71
|
+
style: {
|
|
72
|
+
pointerEvents: canJump ? "auto" : "none",
|
|
73
|
+
borderRadius: 9999,
|
|
74
|
+
border: "1px solid color-mix(in srgb, currentColor 20%, transparent)",
|
|
75
|
+
width: 40,
|
|
76
|
+
height: 40,
|
|
77
|
+
display: "grid",
|
|
78
|
+
placeItems: "center",
|
|
79
|
+
boxShadow: "0 8px 20px rgba(0,0,0,0.18)",
|
|
80
|
+
background: "color-mix(in srgb, var(--background, #fff) 92%, transparent)",
|
|
81
|
+
color: "var(--foreground, #111)",
|
|
82
|
+
cursor: canJump ? "pointer" : "default"
|
|
83
|
+
},
|
|
84
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": !0, children: "\u2193" })
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
function BottomStickScrollArea({
|
|
89
|
+
children,
|
|
90
|
+
className,
|
|
91
|
+
viewportClassName,
|
|
92
|
+
contentClassName,
|
|
93
|
+
showJumpToBottom = !0,
|
|
94
|
+
onDebugStateChange,
|
|
95
|
+
onModeChange,
|
|
96
|
+
debugDomAttributes = !1,
|
|
97
|
+
renderJumpToBottom,
|
|
98
|
+
jumpContainerClassName
|
|
99
|
+
}) {
|
|
100
|
+
let viewportRef = (0, import_react.useRef)(null), contentRef = (0, import_react.useRef)(null), modeRef = (0, import_react.useRef)("STICKY_INSTANT"), [mode, setMode] = (0, import_react.useState)("STICKY_INSTANT"), [isOverflowing, setIsOverflowing] = (0, import_react.useState)(!1), [distanceToBottom, setDistanceToBottom] = (0, import_react.useState)(0), [prefersReducedMotion, setPrefersReducedMotion] = (0, import_react.useState)(!1), prefersReducedMotionRef = (0, import_react.useRef)(!1), prevScrollTopRef = (0, import_react.useRef)(0), lastProgrammaticWriteRef = (0, import_react.useRef)(null), programmaticWriteCountRef = (0, import_react.useRef)(0), pendingStickRafRef = (0, import_react.useRef)(null), stickRequestedRef = (0, import_react.useRef)(!1), returnRafRef = (0, import_react.useRef)(null), returnAnimRef = (0, import_react.useRef)(null), setModeBoth = (0, import_react.useCallback)(
|
|
101
|
+
(next) => {
|
|
102
|
+
modeRef.current = next, setMode(next), onModeChange?.(next);
|
|
103
|
+
},
|
|
104
|
+
[onModeChange]
|
|
105
|
+
), updateDebug = (0, import_react.useCallback)(() => {
|
|
106
|
+
let viewport = viewportRef.current;
|
|
107
|
+
if (!viewport) return;
|
|
108
|
+
let m = measure(viewport);
|
|
109
|
+
debugDomAttributes && (viewport.dataset.stickyMode = modeRef.current, viewport.dataset.distanceToBottom = String(m.distanceToBottom), viewport.dataset.isOverflowing = m.isOverflowing ? "1" : "0", viewport.dataset.programmaticWrites = String(programmaticWriteCountRef.current)), onDebugStateChange?.({
|
|
110
|
+
mode: modeRef.current,
|
|
111
|
+
isOverflowing: m.isOverflowing,
|
|
112
|
+
distanceToBottom: m.distanceToBottom,
|
|
113
|
+
scrollTop: m.scrollTop,
|
|
114
|
+
maxScrollTop: m.maxScrollTop,
|
|
115
|
+
scrollHeight: m.scrollHeight,
|
|
116
|
+
clientHeight: m.clientHeight,
|
|
117
|
+
programmaticWrites: programmaticWriteCountRef.current
|
|
118
|
+
}), setDistanceToBottom(
|
|
119
|
+
(previous) => Math.abs(previous - m.distanceToBottom) <= 0.1 ? previous : m.distanceToBottom
|
|
120
|
+
);
|
|
121
|
+
}, [debugDomAttributes, onDebugStateChange]), writeScrollTop = (0, import_react.useCallback)((nextTop) => {
|
|
122
|
+
let viewport = viewportRef.current;
|
|
123
|
+
viewport && (viewport.scrollTop = nextTop, programmaticWriteCountRef.current += 1, lastProgrammaticWriteRef.current = { at: performance.now(), top: nextTop });
|
|
124
|
+
}, []), cancelPendingStick = (0, import_react.useCallback)(() => {
|
|
125
|
+
pendingStickRafRef.current !== null && (cancelAnimationFrame(pendingStickRafRef.current), pendingStickRafRef.current = null), stickRequestedRef.current = !1;
|
|
126
|
+
}, []), cancelReturn = (0, import_react.useCallback)(() => {
|
|
127
|
+
returnRafRef.current !== null && (cancelAnimationFrame(returnRafRef.current), returnRafRef.current = null), returnAnimRef.current = null;
|
|
128
|
+
}, []), handleReducedMotionChange = (0, import_react.useCallback)(
|
|
129
|
+
(matches) => {
|
|
130
|
+
if (prefersReducedMotionRef.current !== matches && (prefersReducedMotionRef.current = matches, setPrefersReducedMotion(matches)), !matches || modeRef.current !== "RETURNING_SMOOTH") return;
|
|
131
|
+
cancelPendingStick(), cancelReturn(), setModeBoth("STICKY_INSTANT");
|
|
132
|
+
let viewport = viewportRef.current;
|
|
133
|
+
viewport && (writeScrollTop(measure(viewport).maxScrollTop), prevScrollTopRef.current = viewport.scrollTop), updateDebug();
|
|
134
|
+
},
|
|
135
|
+
[cancelPendingStick, cancelReturn, setModeBoth, updateDebug, writeScrollTop]
|
|
136
|
+
), reducedMotionChangeHandlerRef = (0, import_react.useRef)(handleReducedMotionChange);
|
|
137
|
+
reducedMotionChangeHandlerRef.current = handleReducedMotionChange;
|
|
138
|
+
let interruptReturnForUserIntent = (0, import_react.useCallback)(() => {
|
|
139
|
+
modeRef.current === "RETURNING_SMOOTH" && (cancelPendingStick(), cancelReturn(), setModeBoth("DETACHED"), updateDebug());
|
|
140
|
+
}, [cancelPendingStick, cancelReturn, setModeBoth, updateDebug]), onViewportKeyDown = (0, import_react.useCallback)(
|
|
141
|
+
(event) => {
|
|
142
|
+
isScrollKeyIntent(event) && interruptReturnForUserIntent();
|
|
143
|
+
},
|
|
144
|
+
[interruptReturnForUserIntent]
|
|
145
|
+
), stickToBottomInstant = (0, import_react.useCallback)(() => {
|
|
146
|
+
let viewport = viewportRef.current;
|
|
147
|
+
viewport && (stickRequestedRef.current = !0, pendingStickRafRef.current === null && (pendingStickRafRef.current = requestAnimationFrame(() => {
|
|
148
|
+
if (pendingStickRafRef.current = null, !stickRequestedRef.current || (stickRequestedRef.current = !1, modeRef.current !== "STICKY_INSTANT")) return;
|
|
149
|
+
let m = measure(viewport);
|
|
150
|
+
writeScrollTop(m.maxScrollTop), updateDebug();
|
|
151
|
+
})));
|
|
152
|
+
}, [updateDebug, writeScrollTop]), startReturnToBottomSmooth = (0, import_react.useCallback)(() => {
|
|
153
|
+
let viewport = viewportRef.current;
|
|
154
|
+
if (!viewport) return;
|
|
155
|
+
if (prefersReducedMotionRef.current) {
|
|
156
|
+
cancelPendingStick(), cancelReturn(), setModeBoth("STICKY_INSTANT"), writeScrollTop(measure(viewport).maxScrollTop), prevScrollTopRef.current = viewport.scrollTop, updateDebug();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
cancelPendingStick(), cancelReturn();
|
|
160
|
+
let initial = measure(viewport);
|
|
161
|
+
if (initial.distanceToBottom <= EPS_BOTTOM_PX) {
|
|
162
|
+
setModeBoth("STICKY_INSTANT"), stickToBottomInstant(), updateDebug();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
let durationMs = Math.max(
|
|
166
|
+
RETURN_MIN_MS,
|
|
167
|
+
Math.min(RETURN_MAX_MS, initial.distanceToBottom / RETURN_PX_PER_MS)
|
|
168
|
+
);
|
|
169
|
+
returnAnimRef.current = {
|
|
170
|
+
startTime: performance.now(),
|
|
171
|
+
startTop: initial.scrollTop,
|
|
172
|
+
durationMs,
|
|
173
|
+
settleFrames: 0
|
|
174
|
+
}, setModeBoth("RETURNING_SMOOTH");
|
|
175
|
+
let tick = (now) => {
|
|
176
|
+
let viewportCurrent = viewportRef.current, anim = returnAnimRef.current;
|
|
177
|
+
if (!viewportCurrent || !anim || modeRef.current !== "RETURNING_SMOOTH") {
|
|
178
|
+
cancelReturn();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
let before = measure(viewportCurrent), progress = clamp01((now - anim.startTime) / anim.durationMs), eased = easeOutCubic(progress), targetBottom = before.maxScrollTop, nextTop = anim.startTop + (targetBottom - anim.startTop) * eased;
|
|
182
|
+
nextTop < before.scrollTop && (nextTop = before.scrollTop), writeScrollTop(nextTop), measure(viewportCurrent).distanceToBottom <= EPS_BOTTOM_PX ? anim.settleFrames += 1 : anim.settleFrames = 0;
|
|
183
|
+
let doneByTime = progress >= 1, doneBySettle = anim.settleFrames >= RETURN_SETTLE_FRAMES && progress >= 0.6;
|
|
184
|
+
if (doneByTime && doneBySettle) {
|
|
185
|
+
cancelReturn(), setModeBoth("STICKY_INSTANT"), stickToBottomInstant(), updateDebug();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
updateDebug(), returnRafRef.current = requestAnimationFrame(tick);
|
|
189
|
+
};
|
|
190
|
+
returnRafRef.current = requestAnimationFrame(tick);
|
|
191
|
+
}, [cancelPendingStick, cancelReturn, setModeBoth, stickToBottomInstant, updateDebug, writeScrollTop]), onViewportScroll = (0, import_react.useCallback)(
|
|
192
|
+
(event) => {
|
|
193
|
+
let viewport = event.currentTarget, m = measure(viewport);
|
|
194
|
+
setIsOverflowing((previous) => previous === m.isOverflowing ? previous : m.isOverflowing);
|
|
195
|
+
let previousTop = prevScrollTopRef.current, delta = m.scrollTop - previousTop, lastWrite = lastProgrammaticWriteRef.current, isLikelyOwnWrite = lastWrite !== null && performance.now() - lastWrite.at < 60 && lastWrite !== null && Math.abs(m.scrollTop - lastWrite.top) <= 2;
|
|
196
|
+
modeRef.current === "RETURNING_SMOOTH" ? !isLikelyOwnWrite && delta < -DETACH_SCROLL_DELTA_PX && (cancelReturn(), setModeBoth("DETACHED")) : modeRef.current === "STICKY_INSTANT" ? !isLikelyOwnWrite && delta < -DETACH_SCROLL_DELTA_PX && m.isOverflowing && (cancelPendingStick(), setModeBoth("DETACHED")) : modeRef.current === "DETACHED" && m.distanceToBottom <= EPS_BOTTOM_PX && (setModeBoth("STICKY_INSTANT"), stickToBottomInstant()), prevScrollTopRef.current = m.scrollTop, updateDebug();
|
|
197
|
+
},
|
|
198
|
+
[cancelPendingStick, cancelReturn, setModeBoth, stickToBottomInstant, updateDebug]
|
|
199
|
+
);
|
|
200
|
+
(0, import_react.useEffect)(() => {
|
|
201
|
+
let viewport = viewportRef.current, content = contentRef.current;
|
|
202
|
+
if (!viewport || !content) return;
|
|
203
|
+
let observer = new ResizeObserver(() => {
|
|
204
|
+
let m = measure(viewport);
|
|
205
|
+
setIsOverflowing((previous) => previous === m.isOverflowing ? previous : m.isOverflowing), modeRef.current === "STICKY_INSTANT" && stickToBottomInstant(), updateDebug();
|
|
206
|
+
});
|
|
207
|
+
return observer.observe(content), observer.observe(viewport), () => observer.disconnect();
|
|
208
|
+
}, [stickToBottomInstant, updateDebug]), (0, import_react.useLayoutEffect)(() => {
|
|
209
|
+
let viewport = viewportRef.current;
|
|
210
|
+
if (!viewport) return;
|
|
211
|
+
setModeBoth("STICKY_INSTANT");
|
|
212
|
+
let initial = measure(viewport);
|
|
213
|
+
writeScrollTop(initial.maxScrollTop), prevScrollTopRef.current = viewport.scrollTop, setIsOverflowing(initial.isOverflowing), setDistanceToBottom(initial.distanceToBottom), updateDebug();
|
|
214
|
+
}, [setModeBoth, updateDebug, writeScrollTop]), (0, import_react.useLayoutEffect)(() => {
|
|
215
|
+
if (typeof window > "u" || typeof window.matchMedia != "function") return;
|
|
216
|
+
let mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)"), onChange = () => reducedMotionChangeHandlerRef.current(mediaQuery.matches);
|
|
217
|
+
return onChange(), typeof mediaQuery.addEventListener == "function" ? (mediaQuery.addEventListener("change", onChange), () => mediaQuery.removeEventListener("change", onChange)) : (mediaQuery.addListener(onChange), () => mediaQuery.removeListener(onChange));
|
|
218
|
+
}, []), (0, import_react.useEffect)(() => () => {
|
|
219
|
+
cancelPendingStick(), cancelReturn();
|
|
220
|
+
}, [cancelPendingStick, cancelReturn]);
|
|
221
|
+
let canJump = isOverflowing && mode === "DETACHED", jumpButton = renderJumpToBottom?.({
|
|
222
|
+
mode,
|
|
223
|
+
canJump,
|
|
224
|
+
isOverflowing,
|
|
225
|
+
distanceToBottom,
|
|
226
|
+
jumpToBottom: startReturnToBottomSmooth
|
|
227
|
+
}) ?? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DefaultJumpButton, { canJump, jumpToBottom: startReturnToBottomSmooth }), jumpContainerClassNameBase = "pointer-events-none absolute z-20", jumpContainerStyle = {
|
|
228
|
+
position: "absolute",
|
|
229
|
+
zIndex: 20,
|
|
230
|
+
left: 0,
|
|
231
|
+
right: 0,
|
|
232
|
+
bottom: 12,
|
|
233
|
+
display: "flex",
|
|
234
|
+
justifyContent: "center",
|
|
235
|
+
opacity: canJump ? 1 : 0,
|
|
236
|
+
transform: prefersReducedMotion ? "none" : canJump ? "translateY(0px)" : "translateY(4px)",
|
|
237
|
+
transition: prefersReducedMotion ? "none" : "opacity 150ms ease, transform 150ms ease",
|
|
238
|
+
pointerEvents: "none"
|
|
239
|
+
};
|
|
240
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: joinClasses("relative h-full w-full overflow-hidden", className), children: [
|
|
241
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
242
|
+
"div",
|
|
243
|
+
{
|
|
244
|
+
ref: viewportRef,
|
|
245
|
+
className: joinClasses("h-full w-full overflow-auto", viewportClassName),
|
|
246
|
+
onScroll: onViewportScroll,
|
|
247
|
+
onWheel: interruptReturnForUserIntent,
|
|
248
|
+
onTouchStart: interruptReturnForUserIntent,
|
|
249
|
+
onKeyDown: onViewportKeyDown,
|
|
250
|
+
"data-testid": "sticky-scroll-viewport",
|
|
251
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: contentRef, className: joinClasses("flex min-h-full w-full flex-col", contentClassName), children })
|
|
252
|
+
}
|
|
253
|
+
),
|
|
254
|
+
showJumpToBottom ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
255
|
+
"div",
|
|
256
|
+
{
|
|
257
|
+
className: joinClasses(jumpContainerClassNameBase, jumpContainerClassName),
|
|
258
|
+
style: jumpContainerStyle,
|
|
259
|
+
"aria-hidden": !canJump,
|
|
260
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: canJump ? "pointer-events-auto" : "pointer-events-none", children: jumpButton })
|
|
261
|
+
}
|
|
262
|
+
) : null
|
|
263
|
+
] });
|
|
264
|
+
}
|
|
265
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
266
|
+
0 && (module.exports = {
|
|
267
|
+
BottomStickScrollArea
|
|
268
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type BottomStickMode = "STICKY_INSTANT" | "DETACHED" | "RETURNING_SMOOTH";
|
|
5
|
+
type BottomStickDebugState = {
|
|
6
|
+
mode: BottomStickMode;
|
|
7
|
+
isOverflowing: boolean;
|
|
8
|
+
distanceToBottom: number;
|
|
9
|
+
scrollTop: number;
|
|
10
|
+
maxScrollTop: number;
|
|
11
|
+
scrollHeight: number;
|
|
12
|
+
clientHeight: number;
|
|
13
|
+
programmaticWrites: number;
|
|
14
|
+
};
|
|
15
|
+
type BottomStickJumpButtonRenderState = {
|
|
16
|
+
mode: BottomStickMode;
|
|
17
|
+
canJump: boolean;
|
|
18
|
+
isOverflowing: boolean;
|
|
19
|
+
distanceToBottom: number;
|
|
20
|
+
jumpToBottom: () => void;
|
|
21
|
+
};
|
|
22
|
+
type BottomStickScrollAreaProps = {
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
className?: string;
|
|
25
|
+
viewportClassName?: string;
|
|
26
|
+
contentClassName?: string;
|
|
27
|
+
showJumpToBottom?: boolean;
|
|
28
|
+
onDebugStateChange?: (state: BottomStickDebugState) => void;
|
|
29
|
+
onModeChange?: (mode: BottomStickMode) => void;
|
|
30
|
+
debugDomAttributes?: boolean;
|
|
31
|
+
renderJumpToBottom?: (state: BottomStickJumpButtonRenderState) => ReactNode;
|
|
32
|
+
jumpContainerClassName?: string;
|
|
33
|
+
};
|
|
34
|
+
declare function BottomStickScrollArea({ children, className, viewportClassName, contentClassName, showJumpToBottom, onDebugStateChange, onModeChange, debugDomAttributes, renderJumpToBottom, jumpContainerClassName, }: BottomStickScrollAreaProps): react_jsx_runtime.JSX.Element;
|
|
35
|
+
|
|
36
|
+
export { type BottomStickDebugState, type BottomStickJumpButtonRenderState, type BottomStickMode, BottomStickScrollArea, type BottomStickScrollAreaProps };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type BottomStickMode = "STICKY_INSTANT" | "DETACHED" | "RETURNING_SMOOTH";
|
|
5
|
+
type BottomStickDebugState = {
|
|
6
|
+
mode: BottomStickMode;
|
|
7
|
+
isOverflowing: boolean;
|
|
8
|
+
distanceToBottom: number;
|
|
9
|
+
scrollTop: number;
|
|
10
|
+
maxScrollTop: number;
|
|
11
|
+
scrollHeight: number;
|
|
12
|
+
clientHeight: number;
|
|
13
|
+
programmaticWrites: number;
|
|
14
|
+
};
|
|
15
|
+
type BottomStickJumpButtonRenderState = {
|
|
16
|
+
mode: BottomStickMode;
|
|
17
|
+
canJump: boolean;
|
|
18
|
+
isOverflowing: boolean;
|
|
19
|
+
distanceToBottom: number;
|
|
20
|
+
jumpToBottom: () => void;
|
|
21
|
+
};
|
|
22
|
+
type BottomStickScrollAreaProps = {
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
className?: string;
|
|
25
|
+
viewportClassName?: string;
|
|
26
|
+
contentClassName?: string;
|
|
27
|
+
showJumpToBottom?: boolean;
|
|
28
|
+
onDebugStateChange?: (state: BottomStickDebugState) => void;
|
|
29
|
+
onModeChange?: (mode: BottomStickMode) => void;
|
|
30
|
+
debugDomAttributes?: boolean;
|
|
31
|
+
renderJumpToBottom?: (state: BottomStickJumpButtonRenderState) => ReactNode;
|
|
32
|
+
jumpContainerClassName?: string;
|
|
33
|
+
};
|
|
34
|
+
declare function BottomStickScrollArea({ children, className, viewportClassName, contentClassName, showJumpToBottom, onDebugStateChange, onModeChange, debugDomAttributes, renderJumpToBottom, jumpContainerClassName, }: BottomStickScrollAreaProps): react_jsx_runtime.JSX.Element;
|
|
35
|
+
|
|
36
|
+
export { type BottomStickDebugState, type BottomStickJumpButtonRenderState, type BottomStickMode, BottomStickScrollArea, type BottomStickScrollAreaProps };
|