@lobehub/ui 5.8.0 → 5.9.1
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/es/Markdown/SyntaxMarkdown/StreamdownRender.mjs +39 -44
- package/es/Markdown/SyntaxMarkdown/StreamdownRender.mjs.map +1 -1
- package/es/Markdown/SyntaxMarkdown/streamAnimationMeta.mjs +3 -7
- package/es/Markdown/SyntaxMarkdown/streamAnimationMeta.mjs.map +1 -1
- package/es/Markdown/plugins/rehypeStreamAnimated.d.mts +2 -3
- package/es/Markdown/plugins/rehypeStreamAnimated.mjs +10 -12
- package/es/Markdown/plugins/rehypeStreamAnimated.mjs.map +1 -1
- package/es/base-ui/FloatingSheet/FloatingSheet.d.mts +27 -0
- package/es/base-ui/FloatingSheet/FloatingSheet.mjs +197 -0
- package/es/base-ui/FloatingSheet/FloatingSheet.mjs.map +1 -0
- package/es/base-ui/FloatingSheet/FloatingSheetHeader.mjs +26 -0
- package/es/base-ui/FloatingSheet/FloatingSheetHeader.mjs.map +1 -0
- package/es/base-ui/FloatingSheet/helpers.mjs +30 -0
- package/es/base-ui/FloatingSheet/helpers.mjs.map +1 -0
- package/es/base-ui/FloatingSheet/index.d.mts +2 -0
- package/es/base-ui/FloatingSheet/style.mjs +94 -0
- package/es/base-ui/FloatingSheet/style.mjs.map +1 -0
- package/es/base-ui/FloatingSheet/type.d.mts +26 -0
- package/es/base-ui/FloatingSheet/useSheetDrag.mjs +51 -0
- package/es/base-ui/FloatingSheet/useSheetDrag.mjs.map +1 -0
- package/es/base-ui/FloatingSheet/useSnapPoints.mjs +69 -0
- package/es/base-ui/FloatingSheet/useSnapPoints.mjs.map +1 -0
- package/es/base-ui/index.d.mts +3 -1
- package/es/base-ui/index.mjs +2 -1
- package/package.json +1 -1
|
@@ -103,41 +103,43 @@ const StreamdownRender = memo(({ children, ...rest }) => {
|
|
|
103
103
|
}, [processedContent, profiler]);
|
|
104
104
|
const blocks = blocksResult.value;
|
|
105
105
|
const { getBlockState, charDelay } = useStreamQueue(blocks);
|
|
106
|
-
const prevBlockCharCountRef = useRef(/* @__PURE__ */ new Map());
|
|
107
106
|
const blockCharDelayRef = useRef(/* @__PURE__ */ new Map());
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const frameDt = lastRenderTsRef.current === null ? 0 : Math.max(0, Math.min(renderTs - lastRenderTsRef.current, 120));
|
|
112
|
-
const timelineResult = useMemo(() => {
|
|
107
|
+
const blockBirthsRef = useRef(/* @__PURE__ */ new Map());
|
|
108
|
+
const renderNow = getNow();
|
|
109
|
+
const birthsResult = useMemo(() => {
|
|
113
110
|
const start = profiler ? getNow() : 0;
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
|
|
111
|
+
const nextBirths = /* @__PURE__ */ new Map();
|
|
112
|
+
const prevBirths = blockBirthsRef.current;
|
|
113
|
+
for (const [index, block] of blocks.entries()) {
|
|
114
|
+
if (getBlockState(index) === "queued") continue;
|
|
118
115
|
const blockCharCount = countChars(block.content);
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
116
|
+
const prev = prevBirths.get(block.startOffset);
|
|
117
|
+
let arr;
|
|
118
|
+
if (prev && prev.length === blockCharCount) arr = prev;
|
|
119
|
+
else if (prev && prev.length > blockCharCount) arr = prev.slice(0, blockCharCount);
|
|
120
|
+
else {
|
|
121
|
+
arr = prev ? prev.slice() : [];
|
|
122
|
+
const startIdx = arr.length;
|
|
123
|
+
const cap = renderNow + STREAM_FADE_DURATION;
|
|
124
|
+
for (let i = startIdx; i < blockCharCount; i++) {
|
|
125
|
+
const chained = (i > 0 ? arr[i - 1] : renderNow - charDelay) + charDelay;
|
|
126
|
+
arr.push(Math.min(cap, Math.max(chained, renderNow)));
|
|
127
|
+
}
|
|
125
128
|
}
|
|
126
|
-
|
|
127
|
-
const minElapsed = Math.max(0, latestCharStart - charDelay * 2);
|
|
128
|
-
next.set(block.startOffset, Math.max(elapsedByTime, minElapsed));
|
|
129
|
+
nextBirths.set(block.startOffset, arr);
|
|
129
130
|
}
|
|
130
131
|
return {
|
|
131
132
|
durationMs: profiler ? getNow() - start : 0,
|
|
132
|
-
value:
|
|
133
|
+
value: nextBirths
|
|
133
134
|
};
|
|
134
135
|
}, [
|
|
135
136
|
blocks,
|
|
136
137
|
charDelay,
|
|
137
|
-
|
|
138
|
-
profiler
|
|
138
|
+
getBlockState,
|
|
139
|
+
profiler,
|
|
140
|
+
renderNow
|
|
139
141
|
]);
|
|
140
|
-
const
|
|
142
|
+
const birthsForRender = birthsResult.value;
|
|
141
143
|
useEffect(() => {
|
|
142
144
|
if (!profiler) return;
|
|
143
145
|
profiler.recordCalculation({
|
|
@@ -167,30 +169,29 @@ const StreamdownRender = memo(({ children, ...rest }) => {
|
|
|
167
169
|
useEffect(() => {
|
|
168
170
|
if (!profiler) return;
|
|
169
171
|
profiler.recordCalculation({
|
|
170
|
-
durationMs:
|
|
172
|
+
durationMs: birthsResult.durationMs,
|
|
171
173
|
itemCount: blocks.length,
|
|
172
|
-
name: "block-
|
|
174
|
+
name: "block-births",
|
|
173
175
|
textLength: processedContent.length
|
|
174
176
|
});
|
|
175
177
|
}, [
|
|
178
|
+
birthsResult.durationMs,
|
|
176
179
|
blocks.length,
|
|
177
180
|
processedContent.length,
|
|
178
|
-
profiler
|
|
179
|
-
timelineResult.durationMs
|
|
181
|
+
profiler
|
|
180
182
|
]);
|
|
181
183
|
const blockAnimationMetaResult = useMemo(() => {
|
|
182
184
|
const nextBlockCharDelay = /* @__PURE__ */ new Map();
|
|
183
185
|
const blockAnimationMeta = /* @__PURE__ */ new Map();
|
|
184
186
|
for (const [index, block] of blocks.entries()) {
|
|
185
187
|
const state = getBlockState(index);
|
|
186
|
-
const
|
|
188
|
+
const births = birthsForRender.get(block.startOffset);
|
|
187
189
|
const animationMeta = resolveBlockAnimationMeta({
|
|
188
|
-
blockCharCount: countChars(block.content),
|
|
189
190
|
currentCharDelay: charDelay,
|
|
190
191
|
fadeDuration: STREAM_FADE_DURATION,
|
|
192
|
+
lastElapsedMs: renderNow - (births && births.length > 0 ? births.at(-1) ?? renderNow : renderNow),
|
|
191
193
|
previousCharDelay: blockCharDelayRef.current.get(block.startOffset),
|
|
192
|
-
state
|
|
193
|
-
timelineElapsedMs
|
|
194
|
+
state
|
|
194
195
|
});
|
|
195
196
|
nextBlockCharDelay.set(block.startOffset, animationMeta.charDelay);
|
|
196
197
|
blockAnimationMeta.set(block.startOffset, animationMeta);
|
|
@@ -200,23 +201,16 @@ const StreamdownRender = memo(({ children, ...rest }) => {
|
|
|
200
201
|
blockCharDelay: nextBlockCharDelay
|
|
201
202
|
};
|
|
202
203
|
}, [
|
|
204
|
+
birthsForRender,
|
|
203
205
|
blocks,
|
|
204
206
|
charDelay,
|
|
205
207
|
getBlockState,
|
|
206
|
-
|
|
208
|
+
renderNow
|
|
207
209
|
]);
|
|
208
210
|
useEffect(() => {
|
|
209
|
-
const nextCharCount = /* @__PURE__ */ new Map();
|
|
210
|
-
for (const block of blocks) nextCharCount.set(block.startOffset, countChars(block.content));
|
|
211
211
|
blockCharDelayRef.current = blockAnimationMetaResult.blockCharDelay;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
lastRenderTsRef.current = getNow();
|
|
215
|
-
}, [
|
|
216
|
-
blockAnimationMetaResult.blockCharDelay,
|
|
217
|
-
blocks,
|
|
218
|
-
timelineForRender
|
|
219
|
-
]);
|
|
212
|
+
blockBirthsRef.current = birthsForRender;
|
|
213
|
+
}, [birthsForRender, blockAnimationMetaResult.blockCharDelay]);
|
|
220
214
|
const handleRootRender = useCallback((_, phase, actualDuration, baseDuration) => {
|
|
221
215
|
profiler?.recordRootCommit({
|
|
222
216
|
actualDuration,
|
|
@@ -257,10 +251,11 @@ const StreamdownRender = memo(({ children, ...rest }) => {
|
|
|
257
251
|
if (getBlockState(index) === "queued") return null;
|
|
258
252
|
const animationMeta = blockAnimationMetaResult.blockAnimationMeta.get(block.startOffset);
|
|
259
253
|
if (!animationMeta) return null;
|
|
254
|
+
const births = birthsForRender.get(block.startOffset);
|
|
260
255
|
const plugins = animationMeta.settled ? [...baseRehypePlugins, REVEALED_STREAM_PLUGIN] : [...baseRehypePlugins, [rehypeStreamAnimated, {
|
|
261
|
-
|
|
256
|
+
births,
|
|
262
257
|
fadeDuration: STREAM_FADE_DURATION,
|
|
263
|
-
|
|
258
|
+
nowMs: renderNow
|
|
264
259
|
}]];
|
|
265
260
|
const key = `${generatedId}-${block.startOffset}`;
|
|
266
261
|
const blockNode = /* @__PURE__ */ jsx(StreamdownBlock, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StreamdownRender.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/StreamdownRender.tsx"],"sourcesContent":["'use client';\n\nimport { marked } from 'marked';\nimport {\n memo,\n Profiler,\n type ProfilerOnRenderCallback,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n} from 'react';\nimport Markdown, { type Options } from 'react-markdown';\nimport remend from 'remend';\nimport type { Pluggable, PluggableList } from 'unified';\n\nimport {\n useMarkdownComponents,\n useMarkdownContent,\n useMarkdownRehypePlugins,\n useMarkdownRemarkPlugins,\n} from '@/hooks/useMarkdown';\nimport { useMarkdownContext } from '@/Markdown/components/MarkdownProvider';\nimport { rehypeStreamAnimated } from '@/Markdown/plugins/rehypeStreamAnimated';\nimport { useStreamdownProfiler } from '@/Markdown/streamProfiler';\n\nimport { resolveBlockAnimationMeta } from './streamAnimationMeta';\nimport { styles } from './style';\nimport { useSmoothStreamContent } from './useSmoothStreamContent';\nimport { type BlockInfo, useStreamQueue } from './useStreamQueue';\n\nconst STREAM_FADE_DURATION = 280;\nconst REVEALED_STREAM_PLUGIN: Pluggable = [rehypeStreamAnimated, { revealed: true }];\n\nfunction countChars(text: string): number {\n return [...text].length;\n}\n\nfunction getNow(): number {\n return typeof performance === 'undefined' ? Date.now() : performance.now();\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isDeepEqualValue = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b)) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqualValue(a[i], b[i])) return false;\n }\n return true;\n }\n\n if (!isRecord(a) || !isRecord(b)) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!isDeepEqualValue(a[key], b[key])) return false;\n }\n\n return true;\n};\n\nconst isSamePlugin = (prevPlugin: Pluggable, nextPlugin: Pluggable): boolean => {\n const prevTuple = Array.isArray(prevPlugin) ? prevPlugin : [prevPlugin];\n const nextTuple = Array.isArray(nextPlugin) ? nextPlugin : [nextPlugin];\n\n if (prevTuple.length !== nextTuple.length) return false;\n if (prevTuple[0] !== nextTuple[0]) return false;\n\n return isDeepEqualValue(prevTuple.slice(1), nextTuple.slice(1));\n};\n\nconst isSamePlugins = (\n prevPlugins?: PluggableList | null,\n nextPlugins?: PluggableList | null,\n): boolean => {\n if (prevPlugins === nextPlugins) return true;\n if (!prevPlugins || !nextPlugins) return !prevPlugins && !nextPlugins;\n if (prevPlugins.length !== nextPlugins.length) return false;\n\n for (let i = 0; i < prevPlugins.length; i++) {\n if (!isSamePlugin(prevPlugins[i], nextPlugins[i])) return false;\n }\n\n return true;\n};\n\nconst useStablePlugins = (plugins: PluggableList): PluggableList => {\n const stableRef = useRef<PluggableList>(plugins);\n\n if (!isSamePlugins(stableRef.current, plugins)) {\n stableRef.current = plugins;\n }\n\n return stableRef.current;\n};\n\nconst StreamdownBlock = memo<Options>(\n ({ children, ...rest }) => {\n return <Markdown {...rest}>{children}</Markdown>;\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.components === nextProps.components &&\n isSamePlugins(prevProps.rehypePlugins, nextProps.rehypePlugins) &&\n isSamePlugins(prevProps.remarkPlugins, nextProps.remarkPlugins),\n);\n\nStreamdownBlock.displayName = 'StreamdownBlock';\n\nexport const StreamdownRender = memo<Options>(({ children, ...rest }) => {\n const { streamSmoothingPreset = 'balanced' } = useMarkdownContext();\n const profiler = useStreamdownProfiler();\n const escapedContent = useMarkdownContent(children || '');\n const components = useMarkdownComponents();\n const baseRehypePlugins = useStablePlugins(useMarkdownRehypePlugins());\n const remarkPlugins = useStablePlugins(useMarkdownRemarkPlugins());\n const generatedId = useId();\n const smoothedContent = useSmoothStreamContent(\n typeof escapedContent === 'string' ? escapedContent : '',\n { preset: streamSmoothingPreset },\n );\n\n const processedContentResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const value = remend(smoothedContent);\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [profiler, smoothedContent]);\n const processedContent = processedContentResult.value;\n\n const blocksResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const tokens = marked.lexer(processedContent);\n let offset = 0;\n\n const value = tokens.map((token) => {\n const block = { content: token.raw, startOffset: offset };\n offset += token.raw.length;\n return block;\n });\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [processedContent, profiler]);\n const blocks: BlockInfo[] = blocksResult.value;\n\n const { getBlockState, charDelay } = useStreamQueue(blocks);\n const prevBlockCharCountRef = useRef<Map<number, number>>(new Map());\n const blockCharDelayRef = useRef<Map<number, number>>(new Map());\n const blockTimelineRef = useRef<Map<number, number>>(new Map());\n const lastRenderTsRef = useRef<number | null>(null);\n\n const renderTs = getNow();\n const frameDt =\n lastRenderTsRef.current === null\n ? 0\n : Math.max(0, Math.min(renderTs - lastRenderTsRef.current, 120));\n\n const timelineResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const next = new Map<number, number>();\n const prevTimeline = blockTimelineRef.current;\n const prevCharCounts = prevBlockCharCountRef.current;\n\n for (const block of blocks) {\n const blockCharCount = countChars(block.content);\n const prevCharCount = prevCharCounts.get(block.startOffset) ?? 0;\n const prevElapsed = prevTimeline.get(block.startOffset);\n const latestCharStart = Math.max(0, (blockCharCount - 1) * charDelay);\n\n if (prevElapsed === undefined || blockCharCount < prevCharCount) {\n next.set(block.startOffset, latestCharStart);\n continue;\n }\n\n const elapsedByTime = prevElapsed + frameDt;\n // Avoid huge hidden backlog when stream updates in bursts.\n const minElapsed = Math.max(0, latestCharStart - charDelay * 2);\n next.set(block.startOffset, Math.max(elapsedByTime, minElapsed));\n }\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value: next,\n };\n }, [blocks, charDelay, frameDt, profiler]);\n const timelineForRender = timelineResult.value;\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: processedContentResult.durationMs,\n name: 'content-normalize',\n textLength: processedContent.length,\n });\n }, [processedContent.length, processedContentResult.durationMs, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: blocksResult.durationMs,\n itemCount: blocks.length,\n name: 'block-lex',\n textLength: processedContent.length,\n });\n }, [blocks.length, blocksResult.durationMs, processedContent.length, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: timelineResult.durationMs,\n itemCount: blocks.length,\n name: 'block-timeline',\n textLength: processedContent.length,\n });\n }, [blocks.length, processedContent.length, profiler, timelineResult.durationMs]);\n\n const blockAnimationMetaResult = useMemo(() => {\n const nextBlockCharDelay = new Map<number, number>();\n const blockAnimationMeta = new Map<number, ReturnType<typeof resolveBlockAnimationMeta>>();\n\n for (const [index, block] of blocks.entries()) {\n const state = getBlockState(index);\n const timelineElapsedMs = timelineForRender.get(block.startOffset) ?? 0;\n const animationMeta = resolveBlockAnimationMeta({\n blockCharCount: countChars(block.content),\n currentCharDelay: charDelay,\n fadeDuration: STREAM_FADE_DURATION,\n previousCharDelay: blockCharDelayRef.current.get(block.startOffset),\n state,\n timelineElapsedMs,\n });\n\n nextBlockCharDelay.set(block.startOffset, animationMeta.charDelay);\n blockAnimationMeta.set(block.startOffset, animationMeta);\n }\n\n return {\n blockAnimationMeta,\n blockCharDelay: nextBlockCharDelay,\n };\n }, [blocks, charDelay, getBlockState, timelineForRender]);\n\n useEffect(() => {\n const nextCharCount = new Map<number, number>();\n for (const block of blocks) {\n nextCharCount.set(block.startOffset, countChars(block.content));\n }\n blockCharDelayRef.current = blockAnimationMetaResult.blockCharDelay;\n prevBlockCharCountRef.current = nextCharCount;\n blockTimelineRef.current = timelineForRender;\n lastRenderTsRef.current = getNow();\n }, [blockAnimationMetaResult.blockCharDelay, blocks, timelineForRender]);\n\n const handleRootRender = useCallback<ProfilerOnRenderCallback>(\n (_, phase, actualDuration, baseDuration) => {\n profiler?.recordRootCommit({\n actualDuration,\n baseDuration,\n blockCount: blocks.length,\n phase,\n textLength: processedContent.length,\n });\n },\n [blocks.length, processedContent.length, profiler],\n );\n\n const handleBlockRender = useCallback<ProfilerOnRenderCallback>(\n (id, phase, actualDuration, baseDuration) => {\n if (!profiler) return;\n\n const [, indexText, offsetText] = id.split(':');\n const blockIndex = Number(indexText);\n\n if (!Number.isFinite(blockIndex)) return;\n\n const block = blocks[blockIndex];\n if (!block) return;\n\n profiler.recordBlockCommit({\n actualDuration,\n baseDuration,\n blockChars: countChars(block.content),\n blockIndex,\n blockKey: offsetText ?? String(block.startOffset),\n phase,\n state: getBlockState(blockIndex),\n });\n },\n [blocks, getBlockState, profiler],\n );\n\n const content = (\n <div className={styles.animated}>\n {blocks.map((block, index) => {\n const state = getBlockState(index);\n if (state === 'queued') return null;\n const animationMeta = blockAnimationMetaResult.blockAnimationMeta.get(block.startOffset);\n if (!animationMeta) return null;\n\n const plugins: Pluggable[] = animationMeta.settled\n ? [...baseRehypePlugins, REVEALED_STREAM_PLUGIN]\n : [\n ...baseRehypePlugins,\n [\n rehypeStreamAnimated,\n {\n charDelay: animationMeta.charDelay,\n fadeDuration: STREAM_FADE_DURATION,\n timelineElapsedMs: animationMeta.timelineElapsedMs,\n },\n ],\n ];\n\n const key = `${generatedId}-${block.startOffset}`;\n const blockNode = (\n <StreamdownBlock\n {...rest}\n components={components}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n\n if (!profiler) {\n return (\n <StreamdownBlock\n {...rest}\n components={components}\n key={key}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n }\n\n return (\n <Profiler\n id={`streamdown-block:${index}:${block.startOffset}`}\n key={key}\n onRender={handleBlockRender}\n >\n {blockNode}\n </Profiler>\n );\n })}\n </div>\n );\n\n if (!profiler) return content;\n\n return (\n <Profiler id={'streamdown-root'} onRender={handleRootRender}>\n {content}\n </Profiler>\n );\n});\n\nStreamdownRender.displayName = 'StreamdownRender';\n\nexport default StreamdownRender;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"StreamdownRender.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/StreamdownRender.tsx"],"sourcesContent":["'use client';\n\nimport { marked } from 'marked';\nimport {\n memo,\n Profiler,\n type ProfilerOnRenderCallback,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n} from 'react';\nimport Markdown, { type Options } from 'react-markdown';\nimport remend from 'remend';\nimport type { Pluggable, PluggableList } from 'unified';\n\nimport {\n useMarkdownComponents,\n useMarkdownContent,\n useMarkdownRehypePlugins,\n useMarkdownRemarkPlugins,\n} from '@/hooks/useMarkdown';\nimport { useMarkdownContext } from '@/Markdown/components/MarkdownProvider';\nimport { rehypeStreamAnimated } from '@/Markdown/plugins/rehypeStreamAnimated';\nimport { useStreamdownProfiler } from '@/Markdown/streamProfiler';\n\nimport { resolveBlockAnimationMeta } from './streamAnimationMeta';\nimport { styles } from './style';\nimport { useSmoothStreamContent } from './useSmoothStreamContent';\nimport { type BlockInfo, useStreamQueue } from './useStreamQueue';\n\nconst STREAM_FADE_DURATION = 280;\nconst REVEALED_STREAM_PLUGIN: Pluggable = [rehypeStreamAnimated, { revealed: true }];\n\nfunction countChars(text: string): number {\n return [...text].length;\n}\n\nfunction getNow(): number {\n return typeof performance === 'undefined' ? Date.now() : performance.now();\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isDeepEqualValue = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b)) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqualValue(a[i], b[i])) return false;\n }\n return true;\n }\n\n if (!isRecord(a) || !isRecord(b)) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!isDeepEqualValue(a[key], b[key])) return false;\n }\n\n return true;\n};\n\nconst isSamePlugin = (prevPlugin: Pluggable, nextPlugin: Pluggable): boolean => {\n const prevTuple = Array.isArray(prevPlugin) ? prevPlugin : [prevPlugin];\n const nextTuple = Array.isArray(nextPlugin) ? nextPlugin : [nextPlugin];\n\n if (prevTuple.length !== nextTuple.length) return false;\n if (prevTuple[0] !== nextTuple[0]) return false;\n\n return isDeepEqualValue(prevTuple.slice(1), nextTuple.slice(1));\n};\n\nconst isSamePlugins = (\n prevPlugins?: PluggableList | null,\n nextPlugins?: PluggableList | null,\n): boolean => {\n if (prevPlugins === nextPlugins) return true;\n if (!prevPlugins || !nextPlugins) return !prevPlugins && !nextPlugins;\n if (prevPlugins.length !== nextPlugins.length) return false;\n\n for (let i = 0; i < prevPlugins.length; i++) {\n if (!isSamePlugin(prevPlugins[i], nextPlugins[i])) return false;\n }\n\n return true;\n};\n\nconst useStablePlugins = (plugins: PluggableList): PluggableList => {\n const stableRef = useRef<PluggableList>(plugins);\n\n if (!isSamePlugins(stableRef.current, plugins)) {\n stableRef.current = plugins;\n }\n\n return stableRef.current;\n};\n\nconst StreamdownBlock = memo<Options>(\n ({ children, ...rest }) => {\n return <Markdown {...rest}>{children}</Markdown>;\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.components === nextProps.components &&\n isSamePlugins(prevProps.rehypePlugins, nextProps.rehypePlugins) &&\n isSamePlugins(prevProps.remarkPlugins, nextProps.remarkPlugins),\n);\n\nStreamdownBlock.displayName = 'StreamdownBlock';\n\nexport const StreamdownRender = memo<Options>(({ children, ...rest }) => {\n const { streamSmoothingPreset = 'balanced' } = useMarkdownContext();\n const profiler = useStreamdownProfiler();\n const escapedContent = useMarkdownContent(children || '');\n const components = useMarkdownComponents();\n const baseRehypePlugins = useStablePlugins(useMarkdownRehypePlugins());\n const remarkPlugins = useStablePlugins(useMarkdownRemarkPlugins());\n const generatedId = useId();\n const smoothedContent = useSmoothStreamContent(\n typeof escapedContent === 'string' ? escapedContent : '',\n { preset: streamSmoothingPreset },\n );\n\n const processedContentResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const value = remend(smoothedContent);\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [profiler, smoothedContent]);\n const processedContent = processedContentResult.value;\n\n const blocksResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const tokens = marked.lexer(processedContent);\n let offset = 0;\n\n const value = tokens.map((token) => {\n const block = { content: token.raw, startOffset: offset };\n offset += token.raw.length;\n return block;\n });\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [processedContent, profiler]);\n const blocks: BlockInfo[] = blocksResult.value;\n\n const { getBlockState, charDelay } = useStreamQueue(blocks);\n const blockCharDelayRef = useRef<Map<number, number>>(new Map());\n const blockBirthsRef = useRef<Map<number, number[]>>(new Map());\n\n const renderNow = getNow();\n\n const birthsResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const nextBirths = new Map<number, number[]>();\n const prevBirths = blockBirthsRef.current;\n\n for (const [index, block] of blocks.entries()) {\n const state = getBlockState(index);\n // Queued blocks are not rendered. Defer birth assignment so that\n // when the block later transitions to animating/streaming, its\n // chars start fading from that moment instead of having already\n // \"aged out\" of the fade window.\n if (state === 'queued') continue;\n\n const blockCharCount = countChars(block.content);\n const prev = prevBirths.get(block.startOffset);\n let arr: number[];\n\n if (prev && prev.length === blockCharCount) {\n arr = prev;\n } else if (prev && prev.length > blockCharCount) {\n // Block content shrunk (stream restart or upstream rewrite).\n arr = prev.slice(0, blockCharCount);\n } else {\n arr = prev ? prev.slice() : [];\n const startIdx = arr.length;\n // Chain each new char monotonically after the previous one so fades\n // never race out of order. Cap how far the fade queue can run ahead\n // of renderNow to prevent stream-faster-than-fade producing seconds\n // of invisible backlog at the tail.\n const cap = renderNow + STREAM_FADE_DURATION;\n for (let i = startIdx; i < blockCharCount; i++) {\n const prevBirth = i > 0 ? (arr[i - 1] as number) : renderNow - charDelay;\n const chained = prevBirth + charDelay;\n arr.push(Math.min(cap, Math.max(chained, renderNow)));\n }\n }\n\n nextBirths.set(block.startOffset, arr);\n }\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value: nextBirths,\n };\n }, [blocks, charDelay, getBlockState, profiler, renderNow]);\n const birthsForRender = birthsResult.value;\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: processedContentResult.durationMs,\n name: 'content-normalize',\n textLength: processedContent.length,\n });\n }, [processedContent.length, processedContentResult.durationMs, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: blocksResult.durationMs,\n itemCount: blocks.length,\n name: 'block-lex',\n textLength: processedContent.length,\n });\n }, [blocks.length, blocksResult.durationMs, processedContent.length, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: birthsResult.durationMs,\n itemCount: blocks.length,\n name: 'block-births',\n textLength: processedContent.length,\n });\n }, [birthsResult.durationMs, blocks.length, processedContent.length, profiler]);\n\n const blockAnimationMetaResult = useMemo(() => {\n const nextBlockCharDelay = new Map<number, number>();\n const blockAnimationMeta = new Map<number, ReturnType<typeof resolveBlockAnimationMeta>>();\n\n for (const [index, block] of blocks.entries()) {\n const state = getBlockState(index);\n const births = birthsForRender.get(block.startOffset);\n const lastBirthTs = births && births.length > 0 ? (births.at(-1) ?? renderNow) : renderNow;\n const lastElapsedMs = renderNow - lastBirthTs;\n const animationMeta = resolveBlockAnimationMeta({\n currentCharDelay: charDelay,\n fadeDuration: STREAM_FADE_DURATION,\n lastElapsedMs,\n previousCharDelay: blockCharDelayRef.current.get(block.startOffset),\n state,\n });\n\n nextBlockCharDelay.set(block.startOffset, animationMeta.charDelay);\n blockAnimationMeta.set(block.startOffset, animationMeta);\n }\n\n return {\n blockAnimationMeta,\n blockCharDelay: nextBlockCharDelay,\n };\n }, [birthsForRender, blocks, charDelay, getBlockState, renderNow]);\n\n useEffect(() => {\n blockCharDelayRef.current = blockAnimationMetaResult.blockCharDelay;\n blockBirthsRef.current = birthsForRender;\n }, [birthsForRender, blockAnimationMetaResult.blockCharDelay]);\n\n const handleRootRender = useCallback<ProfilerOnRenderCallback>(\n (_, phase, actualDuration, baseDuration) => {\n profiler?.recordRootCommit({\n actualDuration,\n baseDuration,\n blockCount: blocks.length,\n phase,\n textLength: processedContent.length,\n });\n },\n [blocks.length, processedContent.length, profiler],\n );\n\n const handleBlockRender = useCallback<ProfilerOnRenderCallback>(\n (id, phase, actualDuration, baseDuration) => {\n if (!profiler) return;\n\n const [, indexText, offsetText] = id.split(':');\n const blockIndex = Number(indexText);\n\n if (!Number.isFinite(blockIndex)) return;\n\n const block = blocks[blockIndex];\n if (!block) return;\n\n profiler.recordBlockCommit({\n actualDuration,\n baseDuration,\n blockChars: countChars(block.content),\n blockIndex,\n blockKey: offsetText ?? String(block.startOffset),\n phase,\n state: getBlockState(blockIndex),\n });\n },\n [blocks, getBlockState, profiler],\n );\n\n const content = (\n <div className={styles.animated}>\n {blocks.map((block, index) => {\n const state = getBlockState(index);\n if (state === 'queued') return null;\n const animationMeta = blockAnimationMetaResult.blockAnimationMeta.get(block.startOffset);\n if (!animationMeta) return null;\n\n const births = birthsForRender.get(block.startOffset);\n const plugins: Pluggable[] = animationMeta.settled\n ? [...baseRehypePlugins, REVEALED_STREAM_PLUGIN]\n : [\n ...baseRehypePlugins,\n [\n rehypeStreamAnimated,\n {\n births,\n fadeDuration: STREAM_FADE_DURATION,\n nowMs: renderNow,\n },\n ],\n ];\n\n const key = `${generatedId}-${block.startOffset}`;\n const blockNode = (\n <StreamdownBlock\n {...rest}\n components={components}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n\n if (!profiler) {\n return (\n <StreamdownBlock\n {...rest}\n components={components}\n key={key}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n }\n\n return (\n <Profiler\n id={`streamdown-block:${index}:${block.startOffset}`}\n key={key}\n onRender={handleBlockRender}\n >\n {blockNode}\n </Profiler>\n );\n })}\n </div>\n );\n\n if (!profiler) return content;\n\n return (\n <Profiler id={'streamdown-root'} onRender={handleRootRender}>\n {content}\n </Profiler>\n );\n});\n\nStreamdownRender.displayName = 'StreamdownRender';\n\nexport default StreamdownRender;\n"],"mappings":"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"}
|
|
@@ -2,14 +2,10 @@
|
|
|
2
2
|
const isActiveBlock = (state) => {
|
|
3
3
|
return state === "animating" || state === "streaming";
|
|
4
4
|
};
|
|
5
|
-
const resolveBlockAnimationMeta = ({
|
|
6
|
-
const charDelay = isActiveBlock(state) ? currentCharDelay : previousCharDelay ?? currentCharDelay;
|
|
7
|
-
const latestCharStart = Math.max(0, (blockCharCount - 1) * charDelay);
|
|
8
|
-
const settled = state === "revealed" && timelineElapsedMs >= latestCharStart + fadeDuration;
|
|
5
|
+
const resolveBlockAnimationMeta = ({ currentCharDelay, fadeDuration, lastElapsedMs, previousCharDelay, state }) => {
|
|
9
6
|
return {
|
|
10
|
-
charDelay,
|
|
11
|
-
settled
|
|
12
|
-
timelineElapsedMs: settled ? latestCharStart + fadeDuration : timelineElapsedMs
|
|
7
|
+
charDelay: isActiveBlock(state) ? currentCharDelay : previousCharDelay ?? currentCharDelay,
|
|
8
|
+
settled: state === "revealed" && lastElapsedMs >= fadeDuration
|
|
13
9
|
};
|
|
14
10
|
};
|
|
15
11
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamAnimationMeta.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/streamAnimationMeta.ts"],"sourcesContent":["import { type BlockState } from './useStreamQueue';\n\nexport interface ResolveBlockAnimationMetaOptions {\n
|
|
1
|
+
{"version":3,"file":"streamAnimationMeta.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/streamAnimationMeta.ts"],"sourcesContent":["import { type BlockState } from './useStreamQueue';\n\nexport interface ResolveBlockAnimationMetaOptions {\n currentCharDelay: number;\n fadeDuration: number;\n lastElapsedMs: number;\n previousCharDelay?: number;\n state: BlockState;\n}\n\nexport interface BlockAnimationMeta {\n charDelay: number;\n settled: boolean;\n}\n\nconst isActiveBlock = (state: BlockState) => {\n return state === 'animating' || state === 'streaming';\n};\n\nexport const resolveBlockAnimationMeta = ({\n currentCharDelay,\n fadeDuration,\n lastElapsedMs,\n previousCharDelay,\n state,\n}: ResolveBlockAnimationMetaOptions): BlockAnimationMeta => {\n const charDelay = isActiveBlock(state)\n ? currentCharDelay\n : (previousCharDelay ?? currentCharDelay);\n const settled = state === 'revealed' && lastElapsedMs >= fadeDuration;\n\n return {\n charDelay,\n settled,\n };\n};\n"],"mappings":";AAeA,MAAM,iBAAiB,UAAsB;AAC3C,QAAO,UAAU,eAAe,UAAU;;AAG5C,MAAa,6BAA6B,EACxC,kBACA,cACA,eACA,mBACA,YAC0D;AAM1D,QAAO;EACL,WANgB,cAAc,MAAM,GAClC,mBACC,qBAAqB;EAKxB,SAJc,UAAU,cAAc,iBAAiB;EAKxD"}
|
|
@@ -2,11 +2,10 @@ import { Root } from "../../node_modules/@types/hast/index.mjs";
|
|
|
2
2
|
|
|
3
3
|
//#region src/Markdown/plugins/rehypeStreamAnimated.d.ts
|
|
4
4
|
interface StreamAnimatedOptions {
|
|
5
|
-
|
|
6
|
-
charDelay?: number;
|
|
5
|
+
births?: number[];
|
|
7
6
|
fadeDuration?: number;
|
|
7
|
+
nowMs?: number;
|
|
8
8
|
revealed?: boolean;
|
|
9
|
-
timelineElapsedMs?: number;
|
|
10
9
|
}
|
|
11
10
|
declare const rehypeStreamAnimated: (options?: StreamAnimatedOptions) => (tree: Root) => void;
|
|
12
11
|
//#endregion
|
|
@@ -23,8 +23,8 @@ function hasClass(node, cls) {
|
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
25
|
const rehypeStreamAnimated = (options = {}) => {
|
|
26
|
-
const {
|
|
27
|
-
const
|
|
26
|
+
const { births, fadeDuration = 150, nowMs, revealed = false } = options;
|
|
27
|
+
const hasBirths = !revealed && Array.isArray(births) && typeof nowMs === "number";
|
|
28
28
|
return (tree) => {
|
|
29
29
|
let globalCharIndex = 0;
|
|
30
30
|
const shouldSkip = (node) => {
|
|
@@ -33,19 +33,17 @@ const rehypeStreamAnimated = (options = {}) => {
|
|
|
33
33
|
const wrapText = (node) => {
|
|
34
34
|
const newChildren = [];
|
|
35
35
|
for (const child of node.children) if (child.type === "text") for (const char of child.value) {
|
|
36
|
-
const relativeIndex = globalCharIndex - baseCharCount;
|
|
37
36
|
let className = "stream-char";
|
|
38
37
|
let delay;
|
|
39
38
|
if (revealed) className = "stream-char stream-char-revealed";
|
|
40
|
-
else if (
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
43
|
-
else
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
else delay = -elapsed;
|
|
39
|
+
else if (hasBirths) {
|
|
40
|
+
const birthTs = births[globalCharIndex];
|
|
41
|
+
if (birthTs === void 0) className = "stream-char stream-char-revealed";
|
|
42
|
+
else {
|
|
43
|
+
const elapsed = nowMs - birthTs;
|
|
44
|
+
if (elapsed >= fadeDuration) className = "stream-char stream-char-revealed";
|
|
45
|
+
else delay = -elapsed;
|
|
46
|
+
}
|
|
49
47
|
}
|
|
50
48
|
const properties = { className };
|
|
51
49
|
if (delay !== void 0 && delay !== 0) properties.style = `animation-delay:${delay}ms`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rehypeStreamAnimated.mjs","names":[],"sources":["../../../src/Markdown/plugins/rehypeStreamAnimated.ts"],"sourcesContent":["import { type Element, type ElementContent, type Root } from 'hast';\nimport { type BuildVisitor } from 'unist-util-visit';\nimport { visit } from 'unist-util-visit';\n\nexport interface StreamAnimatedOptions {\n
|
|
1
|
+
{"version":3,"file":"rehypeStreamAnimated.mjs","names":[],"sources":["../../../src/Markdown/plugins/rehypeStreamAnimated.ts"],"sourcesContent":["import { type Element, type ElementContent, type Root } from 'hast';\nimport { type BuildVisitor } from 'unist-util-visit';\nimport { visit } from 'unist-util-visit';\n\nexport interface StreamAnimatedOptions {\n births?: number[];\n fadeDuration?: number;\n nowMs?: number;\n revealed?: boolean;\n}\n\nconst BLOCK_TAGS = new Set(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li']);\nconst SKIP_TAGS = new Set(['pre', 'code', 'table', 'svg']);\n\nfunction hasClass(node: Element, cls: string): boolean {\n const cn = node.properties?.className;\n if (Array.isArray(cn)) return cn.some((c) => String(c).includes(cls));\n if (typeof cn === 'string') return cn.includes(cls);\n return false;\n}\n\nexport const rehypeStreamAnimated = (options: StreamAnimatedOptions = {}) => {\n const { births, fadeDuration = 150, nowMs, revealed = false } = options;\n const hasBirths = !revealed && Array.isArray(births) && typeof nowMs === 'number';\n\n return (tree: Root) => {\n let globalCharIndex = 0;\n\n const shouldSkip = (node: Element): boolean => {\n return SKIP_TAGS.has(node.tagName) || hasClass(node, 'katex');\n };\n\n const wrapText = (node: Element) => {\n const newChildren: ElementContent[] = [];\n for (const child of node.children) {\n if (child.type === 'text') {\n for (const char of child.value) {\n let className = 'stream-char';\n let delay: number | undefined;\n\n if (revealed) {\n className = 'stream-char stream-char-revealed';\n } else if (hasBirths) {\n const birthTs = births![globalCharIndex];\n if (birthTs === undefined) {\n className = 'stream-char stream-char-revealed';\n } else {\n const elapsed = (nowMs as number) - birthTs;\n if (elapsed >= fadeDuration) {\n className = 'stream-char stream-char-revealed';\n } else {\n // Negative delay = already elapsed ms into the fade.\n // Positive delay = not started yet (char born in the future,\n // i.e. staggered within the same commit).\n delay = -elapsed;\n }\n }\n }\n\n const properties: Record<string, any> = { className };\n if (delay !== undefined && delay !== 0) {\n properties.style = `animation-delay:${delay}ms`;\n }\n newChildren.push({\n children: [{ type: 'text', value: char }],\n properties,\n tagName: 'span',\n type: 'element',\n });\n globalCharIndex++;\n }\n } else if (child.type === 'element') {\n if (!shouldSkip(child)) {\n wrapText(child);\n }\n newChildren.push(child);\n } else {\n newChildren.push(child);\n }\n }\n node.children = newChildren;\n };\n\n visit(tree, 'element', ((node: Element) => {\n if (shouldSkip(node)) return 'skip';\n if (BLOCK_TAGS.has(node.tagName)) {\n wrapText(node);\n return 'skip';\n }\n }) as BuildVisitor<Root, 'element'>);\n };\n};\n"],"mappings":";;AAWA,MAAM,aAAa,IAAI,IAAI;CAAC;CAAK;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAK,CAAC;AAC3E,MAAM,YAAY,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAS;CAAM,CAAC;AAE1D,SAAS,SAAS,MAAe,KAAsB;CACrD,MAAM,KAAK,KAAK,YAAY;AAC5B,KAAI,MAAM,QAAQ,GAAG,CAAE,QAAO,GAAG,MAAM,MAAM,OAAO,EAAE,CAAC,SAAS,IAAI,CAAC;AACrE,KAAI,OAAO,OAAO,SAAU,QAAO,GAAG,SAAS,IAAI;AACnD,QAAO;;AAGT,MAAa,wBAAwB,UAAiC,EAAE,KAAK;CAC3E,MAAM,EAAE,QAAQ,eAAe,KAAK,OAAO,WAAW,UAAU;CAChE,MAAM,YAAY,CAAC,YAAY,MAAM,QAAQ,OAAO,IAAI,OAAO,UAAU;AAEzE,SAAQ,SAAe;EACrB,IAAI,kBAAkB;EAEtB,MAAM,cAAc,SAA2B;AAC7C,UAAO,UAAU,IAAI,KAAK,QAAQ,IAAI,SAAS,MAAM,QAAQ;;EAG/D,MAAM,YAAY,SAAkB;GAClC,MAAM,cAAgC,EAAE;AACxC,QAAK,MAAM,SAAS,KAAK,SACvB,KAAI,MAAM,SAAS,OACjB,MAAK,MAAM,QAAQ,MAAM,OAAO;IAC9B,IAAI,YAAY;IAChB,IAAI;AAEJ,QAAI,SACF,aAAY;aACH,WAAW;KACpB,MAAM,UAAU,OAAQ;AACxB,SAAI,YAAY,KAAA,EACd,aAAY;UACP;MACL,MAAM,UAAW,QAAmB;AACpC,UAAI,WAAW,aACb,aAAY;UAKZ,SAAQ,CAAC;;;IAKf,MAAM,aAAkC,EAAE,WAAW;AACrD,QAAI,UAAU,KAAA,KAAa,UAAU,EACnC,YAAW,QAAQ,mBAAmB,MAAM;AAE9C,gBAAY,KAAK;KACf,UAAU,CAAC;MAAE,MAAM;MAAQ,OAAO;MAAM,CAAC;KACzC;KACA,SAAS;KACT,MAAM;KACP,CAAC;AACF;;YAEO,MAAM,SAAS,WAAW;AACnC,QAAI,CAAC,WAAW,MAAM,CACpB,UAAS,MAAM;AAEjB,gBAAY,KAAK,MAAM;SAEvB,aAAY,KAAK,MAAM;AAG3B,QAAK,WAAW;;AAGlB,QAAM,MAAM,aAAa,SAAkB;AACzC,OAAI,WAAW,KAAK,CAAE,QAAO;AAC7B,OAAI,WAAW,IAAI,KAAK,QAAQ,EAAE;AAChC,aAAS,KAAK;AACd,WAAO;;KAEyB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { FloatingSheetProps } from "./type.mjs";
|
|
2
|
+
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/base-ui/FloatingSheet/FloatingSheet.d.ts
|
|
5
|
+
declare function FloatingSheet({
|
|
6
|
+
open: openProp,
|
|
7
|
+
onOpenChange,
|
|
8
|
+
defaultOpen,
|
|
9
|
+
snapPoints: snapPointsProp,
|
|
10
|
+
activeSnapPoint: activeSnapPointProp,
|
|
11
|
+
onSnapPointChange,
|
|
12
|
+
minHeight: minHeightProp,
|
|
13
|
+
maxHeight: maxHeightProp,
|
|
14
|
+
restingHeight: restingHeightProp,
|
|
15
|
+
mode,
|
|
16
|
+
variant,
|
|
17
|
+
width,
|
|
18
|
+
title,
|
|
19
|
+
headerActions,
|
|
20
|
+
dismissible,
|
|
21
|
+
closeThreshold,
|
|
22
|
+
children,
|
|
23
|
+
className
|
|
24
|
+
}: FloatingSheetProps): _$react_jsx_runtime0.JSX.Element;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { FloatingSheet };
|
|
27
|
+
//# sourceMappingURL=FloatingSheet.d.mts.map
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { styles } from "./style.mjs";
|
|
2
|
+
import { FloatingSheetHeader } from "./FloatingSheetHeader.mjs";
|
|
3
|
+
import { clamp, dampenValue, resolveSize } from "./helpers.mjs";
|
|
4
|
+
import { useSheetDrag } from "./useSheetDrag.mjs";
|
|
5
|
+
import { useSnapPoints } from "./useSnapPoints.mjs";
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { cx } from "antd-style";
|
|
9
|
+
//#region src/base-ui/FloatingSheet/FloatingSheet.tsx
|
|
10
|
+
const ANIMATION_MS = 300;
|
|
11
|
+
function FloatingSheet({ open: openProp, onOpenChange, defaultOpen = false, snapPoints: snapPointsProp, activeSnapPoint: activeSnapPointProp, onSnapPointChange, minHeight: minHeightProp = 200, maxHeight: maxHeightProp = .8, restingHeight: restingHeightProp, mode = "overlay", variant = "elevated", width = "100%", title, headerActions, dismissible = true, closeThreshold = .25, children, className }) {
|
|
12
|
+
const s = styles;
|
|
13
|
+
const isControlled = openProp !== void 0;
|
|
14
|
+
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
15
|
+
const isOpen = isControlled ? openProp : internalOpen;
|
|
16
|
+
const setOpen = useCallback((value) => {
|
|
17
|
+
if (!isControlled) setInternalOpen(value);
|
|
18
|
+
onOpenChange?.(value);
|
|
19
|
+
}, [isControlled, onOpenChange]);
|
|
20
|
+
const containerRef = useRef(null);
|
|
21
|
+
const sheetRef = useRef(null);
|
|
22
|
+
const [containerHeight, setContainerHeight] = useState(0);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const parent = sheetRef.current?.parentElement;
|
|
25
|
+
if (!parent) return;
|
|
26
|
+
containerRef.current = parent;
|
|
27
|
+
const observer = new ResizeObserver((entries) => {
|
|
28
|
+
for (const entry of entries) setContainerHeight(entry.contentRect.height);
|
|
29
|
+
});
|
|
30
|
+
observer.observe(parent);
|
|
31
|
+
setContainerHeight(parent.getBoundingClientRect().height);
|
|
32
|
+
return () => observer.disconnect();
|
|
33
|
+
}, []);
|
|
34
|
+
const minHeightPx = useMemo(() => resolveSize(minHeightProp, containerHeight), [minHeightProp, containerHeight]);
|
|
35
|
+
const maxHeightPx = useMemo(() => resolveSize(maxHeightProp, containerHeight), [maxHeightProp, containerHeight]);
|
|
36
|
+
const restingHeightPx = useMemo(() => restingHeightProp !== void 0 ? clamp(resolveSize(restingHeightProp, containerHeight), minHeightPx, maxHeightPx) : minHeightPx, [
|
|
37
|
+
restingHeightProp,
|
|
38
|
+
containerHeight,
|
|
39
|
+
minHeightPx,
|
|
40
|
+
maxHeightPx
|
|
41
|
+
]);
|
|
42
|
+
const hasSnapPoints = !!snapPointsProp && snapPointsProp.length > 0;
|
|
43
|
+
const { snapPointHeights, findActiveIndex, getSnapRelease } = useSnapPoints({
|
|
44
|
+
closeThreshold,
|
|
45
|
+
containerHeight,
|
|
46
|
+
containerRef,
|
|
47
|
+
maxHeightPx,
|
|
48
|
+
minHeightPx,
|
|
49
|
+
snapPoints: snapPointsProp ?? []
|
|
50
|
+
});
|
|
51
|
+
const restingHeight = useMemo(() => {
|
|
52
|
+
if (!containerHeight) return 0;
|
|
53
|
+
if (hasSnapPoints && activeSnapPointProp !== void 0) return clamp(resolveSize(activeSnapPointProp, containerHeight), minHeightPx, maxHeightPx);
|
|
54
|
+
if (hasSnapPoints && snapPointHeights.length > 0) return snapPointHeights[0];
|
|
55
|
+
return restingHeightPx;
|
|
56
|
+
}, [
|
|
57
|
+
containerHeight,
|
|
58
|
+
hasSnapPoints,
|
|
59
|
+
activeSnapPointProp,
|
|
60
|
+
snapPointHeights,
|
|
61
|
+
minHeightPx,
|
|
62
|
+
maxHeightPx,
|
|
63
|
+
restingHeightPx
|
|
64
|
+
]);
|
|
65
|
+
const [height, setHeight] = useState(isOpen ? restingHeight : 0);
|
|
66
|
+
const [isAnimating, setIsAnimating] = useState(false);
|
|
67
|
+
const [isClosing, setIsClosing] = useState(false);
|
|
68
|
+
const heightBeforeDrag = useRef(0);
|
|
69
|
+
const prevOpenRef = useRef(isOpen);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const wasOpen = prevOpenRef.current;
|
|
72
|
+
prevOpenRef.current = isOpen;
|
|
73
|
+
if (isOpen && !wasOpen) {
|
|
74
|
+
setIsClosing(false);
|
|
75
|
+
setIsAnimating(true);
|
|
76
|
+
setHeight(restingHeight);
|
|
77
|
+
const timer = setTimeout(() => setIsAnimating(false), ANIMATION_MS);
|
|
78
|
+
return () => clearTimeout(timer);
|
|
79
|
+
}
|
|
80
|
+
if (!isOpen && wasOpen) {
|
|
81
|
+
setIsClosing(true);
|
|
82
|
+
setIsAnimating(true);
|
|
83
|
+
setHeight(0);
|
|
84
|
+
const timer = setTimeout(() => {
|
|
85
|
+
setIsAnimating(false);
|
|
86
|
+
setIsClosing(false);
|
|
87
|
+
}, ANIMATION_MS);
|
|
88
|
+
return () => clearTimeout(timer);
|
|
89
|
+
}
|
|
90
|
+
}, [isOpen]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (isOpen && !isDragging) setHeight(restingHeight);
|
|
93
|
+
}, [restingHeight]);
|
|
94
|
+
const onDragChange = useCallback((draggedDistance) => {
|
|
95
|
+
const newHeight = heightBeforeDrag.current + draggedDistance;
|
|
96
|
+
if (hasSnapPoints) {
|
|
97
|
+
const highest = snapPointHeights.at(-1) ?? maxHeightPx;
|
|
98
|
+
const lowest = snapPointHeights[0] ?? minHeightPx;
|
|
99
|
+
if (newHeight > highest) setHeight(highest + dampenValue(newHeight - highest));
|
|
100
|
+
else if (newHeight < lowest) {
|
|
101
|
+
const undershoot = lowest - newHeight;
|
|
102
|
+
setHeight(Math.max(0, lowest - dampenValue(undershoot)));
|
|
103
|
+
} else setHeight(newHeight);
|
|
104
|
+
} else setHeight(clamp(newHeight, 0, maxHeightPx));
|
|
105
|
+
}, [
|
|
106
|
+
hasSnapPoints,
|
|
107
|
+
snapPointHeights,
|
|
108
|
+
maxHeightPx,
|
|
109
|
+
minHeightPx
|
|
110
|
+
]);
|
|
111
|
+
const onDragEnd = useCallback((draggedDistance, velocity) => {
|
|
112
|
+
setIsAnimating(true);
|
|
113
|
+
const currentHeight = heightBeforeDrag.current + draggedDistance;
|
|
114
|
+
if (hasSnapPoints) {
|
|
115
|
+
const result = getSnapRelease({
|
|
116
|
+
activeIndex: findActiveIndex(heightBeforeDrag.current),
|
|
117
|
+
currentHeight,
|
|
118
|
+
dismissible,
|
|
119
|
+
draggedDistance,
|
|
120
|
+
velocity
|
|
121
|
+
});
|
|
122
|
+
if (result.type === "dismiss") {
|
|
123
|
+
setIsClosing(true);
|
|
124
|
+
setHeight(0);
|
|
125
|
+
const timer = setTimeout(() => {
|
|
126
|
+
setOpen(false);
|
|
127
|
+
setIsAnimating(false);
|
|
128
|
+
setIsClosing(false);
|
|
129
|
+
}, ANIMATION_MS);
|
|
130
|
+
return () => clearTimeout(timer);
|
|
131
|
+
}
|
|
132
|
+
setHeight(result.height);
|
|
133
|
+
const originalSnapValue = snapPointsProp?.find((sp) => resolveSize(sp, containerHeight) === result.height || clamp(resolveSize(sp, containerHeight), minHeightPx, maxHeightPx) === result.height);
|
|
134
|
+
if (originalSnapValue !== void 0) onSnapPointChange?.(originalSnapValue);
|
|
135
|
+
} else {
|
|
136
|
+
if (dismissible && currentHeight < minHeightPx * closeThreshold) {
|
|
137
|
+
setIsClosing(true);
|
|
138
|
+
setHeight(0);
|
|
139
|
+
const timer = setTimeout(() => {
|
|
140
|
+
setOpen(false);
|
|
141
|
+
setIsAnimating(false);
|
|
142
|
+
setIsClosing(false);
|
|
143
|
+
}, ANIMATION_MS);
|
|
144
|
+
return () => clearTimeout(timer);
|
|
145
|
+
}
|
|
146
|
+
setHeight(clamp(currentHeight, minHeightPx, maxHeightPx));
|
|
147
|
+
}
|
|
148
|
+
setTimeout(() => setIsAnimating(false), ANIMATION_MS);
|
|
149
|
+
}, [
|
|
150
|
+
hasSnapPoints,
|
|
151
|
+
findActiveIndex,
|
|
152
|
+
getSnapRelease,
|
|
153
|
+
dismissible,
|
|
154
|
+
snapPointsProp,
|
|
155
|
+
containerHeight,
|
|
156
|
+
minHeightPx,
|
|
157
|
+
maxHeightPx,
|
|
158
|
+
closeThreshold,
|
|
159
|
+
setOpen,
|
|
160
|
+
onSnapPointChange
|
|
161
|
+
]);
|
|
162
|
+
const { isDragging, handleProps } = useSheetDrag({
|
|
163
|
+
enabled: isOpen ?? false,
|
|
164
|
+
onDragChange,
|
|
165
|
+
onDragEnd
|
|
166
|
+
});
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (isDragging) heightBeforeDrag.current = height;
|
|
169
|
+
}, [isDragging]);
|
|
170
|
+
const isVisible = isOpen || isClosing || height > 0;
|
|
171
|
+
const shouldAnimate = !isDragging && isAnimating;
|
|
172
|
+
const inlineOverflowUp = mode === "inline" && isVisible ? Math.max(0, height - restingHeightPx) : 0;
|
|
173
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
174
|
+
"data-floating-sheet": "",
|
|
175
|
+
"data-state": isOpen ? "open" : "closed",
|
|
176
|
+
ref: sheetRef,
|
|
177
|
+
className: cx(s.root, variant === "embedded" ? s.embedded : s.elevated, mode === "overlay" ? s.overlay : s.inline, mode === "overlay" ? s.overlayRadius : s.inlineRadius, shouldAnimate && s.transition, !isVisible && s.hidden, className),
|
|
178
|
+
style: {
|
|
179
|
+
height: isVisible ? height : 0,
|
|
180
|
+
marginTop: inlineOverflowUp ? -inlineOverflowUp : void 0,
|
|
181
|
+
width
|
|
182
|
+
},
|
|
183
|
+
children: [/* @__PURE__ */ jsx(FloatingSheetHeader, {
|
|
184
|
+
handleProps,
|
|
185
|
+
headerActions,
|
|
186
|
+
isDragging,
|
|
187
|
+
title
|
|
188
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
189
|
+
className: s.content,
|
|
190
|
+
children
|
|
191
|
+
})]
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
195
|
+
export { FloatingSheet };
|
|
196
|
+
|
|
197
|
+
//# sourceMappingURL=FloatingSheet.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloatingSheet.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/FloatingSheet.tsx"],"sourcesContent":["import { cx } from 'antd-style';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { FloatingSheetHeader } from './FloatingSheetHeader';\nimport { clamp, dampenValue, resolveSize } from './helpers';\nimport { styles } from './style';\nimport type { FloatingSheetProps } from './type';\nimport { useSheetDrag } from './useSheetDrag';\nimport { useSnapPoints } from './useSnapPoints';\n\nconst ANIMATION_MS = 300;\n\nexport function FloatingSheet({\n open: openProp,\n onOpenChange,\n defaultOpen = false,\n snapPoints: snapPointsProp,\n activeSnapPoint: activeSnapPointProp,\n onSnapPointChange,\n minHeight: minHeightProp = 200,\n maxHeight: maxHeightProp = 0.8,\n restingHeight: restingHeightProp,\n mode = 'overlay',\n variant = 'elevated',\n width = '100%',\n title,\n headerActions,\n dismissible = true,\n closeThreshold = 0.25,\n children,\n className,\n}: FloatingSheetProps) {\n const s = styles;\n\n // Controlled / uncontrolled open state\n const isControlled = openProp !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const isOpen = isControlled ? openProp : internalOpen;\n\n const setOpen = useCallback(\n (value: boolean) => {\n if (!isControlled) setInternalOpen(value);\n onOpenChange?.(value);\n },\n [isControlled, onOpenChange],\n );\n\n // Container measurement via ResizeObserver\n const containerRef = useRef<HTMLElement | null>(null);\n const sheetRef = useRef<HTMLDivElement>(null);\n const [containerHeight, setContainerHeight] = useState(0);\n\n useEffect(() => {\n const parent = sheetRef.current?.parentElement;\n if (!parent) return;\n containerRef.current = parent;\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n setContainerHeight(entry.contentRect.height);\n }\n });\n observer.observe(parent);\n setContainerHeight(parent.getBoundingClientRect().height);\n\n return () => observer.disconnect();\n }, []);\n\n // Resolve min/max to px\n const minHeightPx = useMemo(\n () => resolveSize(minHeightProp, containerHeight),\n [minHeightProp, containerHeight],\n );\n const maxHeightPx = useMemo(\n () => resolveSize(maxHeightProp, containerHeight),\n [maxHeightProp, containerHeight],\n );\n const restingHeightPx = useMemo(\n () =>\n restingHeightProp !== undefined\n ? clamp(resolveSize(restingHeightProp, containerHeight), minHeightPx, maxHeightPx)\n : minHeightPx,\n [restingHeightProp, containerHeight, minHeightPx, maxHeightPx],\n );\n\n // Snap points\n const hasSnapPoints = !!snapPointsProp && snapPointsProp.length > 0;\n const { snapPointHeights, findActiveIndex, getSnapRelease } = useSnapPoints({\n closeThreshold,\n containerHeight,\n containerRef,\n maxHeightPx,\n minHeightPx,\n snapPoints: snapPointsProp ?? [],\n });\n\n // Compute the \"resting\" height for the current open + snap state\n const restingHeight = useMemo(() => {\n if (!containerHeight) return 0;\n if (hasSnapPoints && activeSnapPointProp !== undefined) {\n const resolved = resolveSize(activeSnapPointProp, containerHeight);\n return clamp(resolved, minHeightPx, maxHeightPx);\n }\n if (hasSnapPoints && snapPointHeights.length > 0) {\n return snapPointHeights[0];\n }\n return restingHeightPx;\n }, [\n containerHeight,\n hasSnapPoints,\n activeSnapPointProp,\n snapPointHeights,\n minHeightPx,\n maxHeightPx,\n restingHeightPx,\n ]);\n\n const [height, setHeight] = useState(isOpen ? restingHeight : 0);\n const [isAnimating, setIsAnimating] = useState(false);\n // Keeps sheet visible during close animation (height → 0)\n const [isClosing, setIsClosing] = useState(false);\n const heightBeforeDrag = useRef(0);\n const prevOpenRef = useRef(isOpen);\n\n // Handle open/close transitions\n useEffect(() => {\n const wasOpen = prevOpenRef.current;\n prevOpenRef.current = isOpen;\n\n if (isOpen && !wasOpen) {\n // Opening: animate from 0 → resting height\n setIsClosing(false);\n setIsAnimating(true);\n setHeight(restingHeight);\n const timer = setTimeout(() => setIsAnimating(false), ANIMATION_MS);\n return () => clearTimeout(timer);\n }\n\n if (!isOpen && wasOpen) {\n // Closing: animate from current height → 0, then hide\n setIsClosing(true);\n setIsAnimating(true);\n setHeight(0);\n const timer = setTimeout(() => {\n setIsAnimating(false);\n setIsClosing(false);\n }, ANIMATION_MS);\n return () => clearTimeout(timer);\n }\n }, [isOpen]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Sync height when resting height changes (container resize, snap point change)\n useEffect(() => {\n if (isOpen && !isDragging) {\n setHeight(restingHeight);\n }\n }, [restingHeight]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Drag handlers\n const onDragChange = useCallback(\n (draggedDistance: number) => {\n const newHeight = heightBeforeDrag.current + draggedDistance;\n\n if (hasSnapPoints) {\n const highest = snapPointHeights.at(-1) ?? maxHeightPx;\n const lowest = snapPointHeights[0] ?? minHeightPx;\n\n if (newHeight > highest) {\n const overshoot = newHeight - highest;\n setHeight(highest + dampenValue(overshoot));\n } else if (newHeight < lowest) {\n const undershoot = lowest - newHeight;\n setHeight(Math.max(0, lowest - dampenValue(undershoot)));\n } else {\n setHeight(newHeight);\n }\n } else {\n setHeight(clamp(newHeight, 0, maxHeightPx));\n }\n },\n [hasSnapPoints, snapPointHeights, maxHeightPx, minHeightPx],\n );\n\n const onDragEnd = useCallback(\n (draggedDistance: number, velocity: number) => {\n setIsAnimating(true);\n const currentHeight = heightBeforeDrag.current + draggedDistance;\n\n if (hasSnapPoints) {\n const activeIndex = findActiveIndex(heightBeforeDrag.current);\n const result = getSnapRelease({\n activeIndex,\n currentHeight,\n dismissible,\n draggedDistance,\n velocity,\n });\n\n if (result.type === 'dismiss') {\n setIsClosing(true);\n setHeight(0);\n const timer = setTimeout(() => {\n setOpen(false);\n setIsAnimating(false);\n setIsClosing(false);\n }, ANIMATION_MS);\n return () => clearTimeout(timer);\n }\n\n setHeight(result.height);\n const originalSnapValue = snapPointsProp?.find(\n (sp) =>\n resolveSize(sp, containerHeight) === result.height ||\n clamp(resolveSize(sp, containerHeight), minHeightPx, maxHeightPx) === result.height,\n );\n if (originalSnapValue !== undefined) {\n onSnapPointChange?.(originalSnapValue);\n }\n } else {\n if (dismissible && currentHeight < minHeightPx * closeThreshold) {\n setIsClosing(true);\n setHeight(0);\n const timer = setTimeout(() => {\n setOpen(false);\n setIsAnimating(false);\n setIsClosing(false);\n }, ANIMATION_MS);\n return () => clearTimeout(timer);\n }\n setHeight(clamp(currentHeight, minHeightPx, maxHeightPx));\n }\n\n setTimeout(() => setIsAnimating(false), ANIMATION_MS);\n },\n [\n hasSnapPoints,\n findActiveIndex,\n getSnapRelease,\n dismissible,\n snapPointsProp,\n containerHeight,\n minHeightPx,\n maxHeightPx,\n closeThreshold,\n setOpen,\n onSnapPointChange,\n ],\n );\n\n const { isDragging, handleProps } = useSheetDrag({\n enabled: isOpen ?? false,\n onDragChange,\n onDragEnd,\n });\n\n // Record height at drag start\n useEffect(() => {\n if (isDragging) {\n heightBeforeDrag.current = height;\n }\n }, [isDragging]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const isVisible = isOpen || isClosing || height > 0;\n const shouldAnimate = !isDragging && isAnimating;\n const inlineOverflowUp =\n mode === 'inline' && isVisible ? Math.max(0, height - restingHeightPx) : 0;\n\n return (\n <div\n data-floating-sheet=\"\"\n data-state={isOpen ? 'open' : 'closed'}\n ref={sheetRef}\n className={cx(\n s.root,\n variant === 'embedded' ? s.embedded : s.elevated,\n mode === 'overlay' ? s.overlay : s.inline,\n mode === 'overlay' ? s.overlayRadius : s.inlineRadius,\n shouldAnimate && s.transition,\n !isVisible && s.hidden,\n className,\n )}\n style={{\n height: isVisible ? height : 0,\n marginTop: inlineOverflowUp ? -inlineOverflowUp : undefined,\n width,\n }}\n >\n <FloatingSheetHeader\n handleProps={handleProps}\n headerActions={headerActions}\n isDragging={isDragging}\n title={title}\n />\n <div className={s.content}>{children}</div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAUA,MAAM,eAAe;AAErB,SAAgB,cAAc,EAC5B,MAAM,UACN,cACA,cAAc,OACd,YAAY,gBACZ,iBAAiB,qBACjB,mBACA,WAAW,gBAAgB,KAC3B,WAAW,gBAAgB,IAC3B,eAAe,mBACf,OAAO,WACP,UAAU,YACV,QAAQ,QACR,OACA,eACA,cAAc,MACd,iBAAiB,KACjB,UACA,aACqB;CACrB,MAAM,IAAI;CAGV,MAAM,eAAe,aAAa,KAAA;CAClC,MAAM,CAAC,cAAc,mBAAmB,SAAS,YAAY;CAC7D,MAAM,SAAS,eAAe,WAAW;CAEzC,MAAM,UAAU,aACb,UAAmB;AAClB,MAAI,CAAC,aAAc,iBAAgB,MAAM;AACzC,iBAAe,MAAM;IAEvB,CAAC,cAAc,aAAa,CAC7B;CAGD,MAAM,eAAe,OAA2B,KAAK;CACrD,MAAM,WAAW,OAAuB,KAAK;CAC7C,MAAM,CAAC,iBAAiB,sBAAsB,SAAS,EAAE;AAEzD,iBAAgB;EACd,MAAM,SAAS,SAAS,SAAS;AACjC,MAAI,CAAC,OAAQ;AACb,eAAa,UAAU;EAEvB,MAAM,WAAW,IAAI,gBAAgB,YAAY;AAC/C,QAAK,MAAM,SAAS,QAClB,oBAAmB,MAAM,YAAY,OAAO;IAE9C;AACF,WAAS,QAAQ,OAAO;AACxB,qBAAmB,OAAO,uBAAuB,CAAC,OAAO;AAEzD,eAAa,SAAS,YAAY;IACjC,EAAE,CAAC;CAGN,MAAM,cAAc,cACZ,YAAY,eAAe,gBAAgB,EACjD,CAAC,eAAe,gBAAgB,CACjC;CACD,MAAM,cAAc,cACZ,YAAY,eAAe,gBAAgB,EACjD,CAAC,eAAe,gBAAgB,CACjC;CACD,MAAM,kBAAkB,cAEpB,sBAAsB,KAAA,IAClB,MAAM,YAAY,mBAAmB,gBAAgB,EAAE,aAAa,YAAY,GAChF,aACN;EAAC;EAAmB;EAAiB;EAAa;EAAY,CAC/D;CAGD,MAAM,gBAAgB,CAAC,CAAC,kBAAkB,eAAe,SAAS;CAClE,MAAM,EAAE,kBAAkB,iBAAiB,mBAAmB,cAAc;EAC1E;EACA;EACA;EACA;EACA;EACA,YAAY,kBAAkB,EAAE;EACjC,CAAC;CAGF,MAAM,gBAAgB,cAAc;AAClC,MAAI,CAAC,gBAAiB,QAAO;AAC7B,MAAI,iBAAiB,wBAAwB,KAAA,EAE3C,QAAO,MADU,YAAY,qBAAqB,gBAAgB,EAC3C,aAAa,YAAY;AAElD,MAAI,iBAAiB,iBAAiB,SAAS,EAC7C,QAAO,iBAAiB;AAE1B,SAAO;IACN;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CAEF,MAAM,CAAC,QAAQ,aAAa,SAAS,SAAS,gBAAgB,EAAE;CAChE,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CAErD,MAAM,CAAC,WAAW,gBAAgB,SAAS,MAAM;CACjD,MAAM,mBAAmB,OAAO,EAAE;CAClC,MAAM,cAAc,OAAO,OAAO;AAGlC,iBAAgB;EACd,MAAM,UAAU,YAAY;AAC5B,cAAY,UAAU;AAEtB,MAAI,UAAU,CAAC,SAAS;AAEtB,gBAAa,MAAM;AACnB,kBAAe,KAAK;AACpB,aAAU,cAAc;GACxB,MAAM,QAAQ,iBAAiB,eAAe,MAAM,EAAE,aAAa;AACnE,gBAAa,aAAa,MAAM;;AAGlC,MAAI,CAAC,UAAU,SAAS;AAEtB,gBAAa,KAAK;AAClB,kBAAe,KAAK;AACpB,aAAU,EAAE;GACZ,MAAM,QAAQ,iBAAiB;AAC7B,mBAAe,MAAM;AACrB,iBAAa,MAAM;MAClB,aAAa;AAChB,gBAAa,aAAa,MAAM;;IAEjC,CAAC,OAAO,CAAC;AAGZ,iBAAgB;AACd,MAAI,UAAU,CAAC,WACb,WAAU,cAAc;IAEzB,CAAC,cAAc,CAAC;CAGnB,MAAM,eAAe,aAClB,oBAA4B;EAC3B,MAAM,YAAY,iBAAiB,UAAU;AAE7C,MAAI,eAAe;GACjB,MAAM,UAAU,iBAAiB,GAAG,GAAG,IAAI;GAC3C,MAAM,SAAS,iBAAiB,MAAM;AAEtC,OAAI,YAAY,QAEd,WAAU,UAAU,YADF,YAAY,QACY,CAAC;YAClC,YAAY,QAAQ;IAC7B,MAAM,aAAa,SAAS;AAC5B,cAAU,KAAK,IAAI,GAAG,SAAS,YAAY,WAAW,CAAC,CAAC;SAExD,WAAU,UAAU;QAGtB,WAAU,MAAM,WAAW,GAAG,YAAY,CAAC;IAG/C;EAAC;EAAe;EAAkB;EAAa;EAAY,CAC5D;CAED,MAAM,YAAY,aACf,iBAAyB,aAAqB;AAC7C,iBAAe,KAAK;EACpB,MAAM,gBAAgB,iBAAiB,UAAU;AAEjD,MAAI,eAAe;GAEjB,MAAM,SAAS,eAAe;IAC5B,aAFkB,gBAAgB,iBAAiB,QAAQ;IAG3D;IACA;IACA;IACA;IACD,CAAC;AAEF,OAAI,OAAO,SAAS,WAAW;AAC7B,iBAAa,KAAK;AAClB,cAAU,EAAE;IACZ,MAAM,QAAQ,iBAAiB;AAC7B,aAAQ,MAAM;AACd,oBAAe,MAAM;AACrB,kBAAa,MAAM;OAClB,aAAa;AAChB,iBAAa,aAAa,MAAM;;AAGlC,aAAU,OAAO,OAAO;GACxB,MAAM,oBAAoB,gBAAgB,MACvC,OACC,YAAY,IAAI,gBAAgB,KAAK,OAAO,UAC5C,MAAM,YAAY,IAAI,gBAAgB,EAAE,aAAa,YAAY,KAAK,OAAO,OAChF;AACD,OAAI,sBAAsB,KAAA,EACxB,qBAAoB,kBAAkB;SAEnC;AACL,OAAI,eAAe,gBAAgB,cAAc,gBAAgB;AAC/D,iBAAa,KAAK;AAClB,cAAU,EAAE;IACZ,MAAM,QAAQ,iBAAiB;AAC7B,aAAQ,MAAM;AACd,oBAAe,MAAM;AACrB,kBAAa,MAAM;OAClB,aAAa;AAChB,iBAAa,aAAa,MAAM;;AAElC,aAAU,MAAM,eAAe,aAAa,YAAY,CAAC;;AAG3D,mBAAiB,eAAe,MAAM,EAAE,aAAa;IAEvD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;CAED,MAAM,EAAE,YAAY,gBAAgB,aAAa;EAC/C,SAAS,UAAU;EACnB;EACA;EACD,CAAC;AAGF,iBAAgB;AACd,MAAI,WACF,kBAAiB,UAAU;IAE5B,CAAC,WAAW,CAAC;CAEhB,MAAM,YAAY,UAAU,aAAa,SAAS;CAClD,MAAM,gBAAgB,CAAC,cAAc;CACrC,MAAM,mBACJ,SAAS,YAAY,YAAY,KAAK,IAAI,GAAG,SAAS,gBAAgB,GAAG;AAE3E,QACE,qBAAC,OAAD;EACE,uBAAoB;EACpB,cAAY,SAAS,SAAS;EAC9B,KAAK;EACL,WAAW,GACT,EAAE,MACF,YAAY,aAAa,EAAE,WAAW,EAAE,UACxC,SAAS,YAAY,EAAE,UAAU,EAAE,QACnC,SAAS,YAAY,EAAE,gBAAgB,EAAE,cACzC,iBAAiB,EAAE,YACnB,CAAC,aAAa,EAAE,QAChB,UACD;EACD,OAAO;GACL,QAAQ,YAAY,SAAS;GAC7B,WAAW,mBAAmB,CAAC,mBAAmB,KAAA;GAClD;GACD;YAjBH,CAmBE,oBAAC,qBAAD;GACe;GACE;GACH;GACL;GACP,CAAA,EACF,oBAAC,OAAD;GAAK,WAAW,EAAE;GAAU;GAAe,CAAA,CACvC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { styles } from "./style.mjs";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cx } from "antd-style";
|
|
4
|
+
//#region src/base-ui/FloatingSheet/FloatingSheetHeader.tsx
|
|
5
|
+
function FloatingSheetHeader({ title, headerActions, isDragging, handleProps }) {
|
|
6
|
+
const s = styles;
|
|
7
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
8
|
+
className: cx(s.header, isDragging && s.headerDragging),
|
|
9
|
+
...handleProps,
|
|
10
|
+
children: [/* @__PURE__ */ jsx("div", { className: s.handle }), /* @__PURE__ */ jsxs("div", {
|
|
11
|
+
className: s.headerContent,
|
|
12
|
+
children: [title && /* @__PURE__ */ jsx("div", {
|
|
13
|
+
className: s.headerTitle,
|
|
14
|
+
children: title
|
|
15
|
+
}), headerActions && /* @__PURE__ */ jsx("div", {
|
|
16
|
+
className: s.headerActions,
|
|
17
|
+
"data-no-drag": "",
|
|
18
|
+
children: headerActions
|
|
19
|
+
})]
|
|
20
|
+
})]
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { FloatingSheetHeader };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=FloatingSheetHeader.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloatingSheetHeader.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/FloatingSheetHeader.tsx"],"sourcesContent":["import { cx } from 'antd-style';\nimport { type ReactNode } from 'react';\n\nimport { styles } from './style';\n\ninterface FloatingSheetHeaderProps {\n handleProps: {\n onMouseDown: (e: React.MouseEvent<HTMLDivElement>) => void;\n };\n headerActions?: ReactNode;\n isDragging: boolean;\n title?: ReactNode;\n}\n\nexport function FloatingSheetHeader({\n title,\n headerActions,\n isDragging,\n handleProps,\n}: FloatingSheetHeaderProps) {\n const s = styles;\n\n return (\n <div className={cx(s.header, isDragging && s.headerDragging)} {...handleProps}>\n <div className={s.handle} />\n <div className={s.headerContent}>\n {title && <div className={s.headerTitle}>{title}</div>}\n {headerActions && (\n <div className={s.headerActions} data-no-drag=\"\">\n {headerActions}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AAcA,SAAgB,oBAAoB,EAClC,OACA,eACA,YACA,eAC2B;CAC3B,MAAM,IAAI;AAEV,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,EAAE,QAAQ,cAAc,EAAE,eAAe;EAAE,GAAI;YAAlE,CACE,oBAAC,OAAD,EAAK,WAAW,EAAE,QAAU,CAAA,EAC5B,qBAAC,OAAD;GAAK,WAAW,EAAE;aAAlB,CACG,SAAS,oBAAC,OAAD;IAAK,WAAW,EAAE;cAAc;IAAY,CAAA,EACrD,iBACC,oBAAC,OAAD;IAAK,WAAW,EAAE;IAAe,gBAAa;cAC3C;IACG,CAAA,CAEJ;KACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/base-ui/FloatingSheet/helpers.ts
|
|
2
|
+
/**
|
|
3
|
+
* Clamp a value between min and max.
|
|
4
|
+
*/
|
|
5
|
+
function clamp(value, min, max) {
|
|
6
|
+
return Math.min(Math.max(value, min), max);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Rubber-band damping for drag resistance beyond snap bounds.
|
|
10
|
+
* Returns 0 when v <= 0, near 1:1 for small v, then grows logarithmically.
|
|
11
|
+
*/
|
|
12
|
+
function dampenValue(v) {
|
|
13
|
+
if (v <= 0) return 0;
|
|
14
|
+
return 8 * Math.log1p(v / 8);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a size value to pixels.
|
|
18
|
+
* 0 < v <= 1 → fraction of containerHeight.
|
|
19
|
+
* v > 1 → pixel value.
|
|
20
|
+
* v === 0 → 0.
|
|
21
|
+
*/
|
|
22
|
+
function resolveSize(value, containerHeight) {
|
|
23
|
+
if (value === 0) return 0;
|
|
24
|
+
if (value > 0 && value <= 1) return value * containerHeight;
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { clamp, dampenValue, resolveSize };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=helpers.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/helpers.ts"],"sourcesContent":["/**\n * Clamp a value between min and max.\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Rubber-band damping for drag resistance beyond snap bounds.\n * Returns 0 when v <= 0, near 1:1 for small v, then grows logarithmically.\n */\nexport function dampenValue(v: number): number {\n if (v <= 0) return 0;\n return 8 * Math.log1p(v / 8);\n}\n\n/**\n * Resolve a size value to pixels.\n * 0 < v <= 1 → fraction of containerHeight.\n * v > 1 → pixel value.\n * v === 0 → 0.\n */\nexport function resolveSize(value: number, containerHeight: number): number {\n if (value === 0) return 0;\n if (value > 0 && value <= 1) return value * containerHeight;\n return value;\n}\n"],"mappings":";;;;AAGA,SAAgB,MAAM,OAAe,KAAa,KAAqB;AACrE,QAAO,KAAK,IAAI,KAAK,IAAI,OAAO,IAAI,EAAE,IAAI;;;;;;AAO5C,SAAgB,YAAY,GAAmB;AAC7C,KAAI,KAAK,EAAG,QAAO;AACnB,QAAO,IAAI,KAAK,MAAM,IAAI,EAAE;;;;;;;;AAS9B,SAAgB,YAAY,OAAe,iBAAiC;AAC1E,KAAI,UAAU,EAAG,QAAO;AACxB,KAAI,QAAQ,KAAK,SAAS,EAAG,QAAO,QAAQ;AAC5C,QAAO"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createStaticStyles } from "antd-style";
|
|
2
|
+
//#region src/base-ui/FloatingSheet/style.ts
|
|
3
|
+
const transitionDuration = "0.3s";
|
|
4
|
+
const transitionEasing = "cubic-bezier(0.32, 0.72, 0, 1)";
|
|
5
|
+
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
6
|
+
root: css`
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
background: ${cssVar.colorBgContainer};
|
|
11
|
+
`,
|
|
12
|
+
overlayRadius: css`
|
|
13
|
+
border-radius: 12px 12px 0 0;
|
|
14
|
+
`,
|
|
15
|
+
inlineRadius: css`
|
|
16
|
+
border-radius: 12px;
|
|
17
|
+
`,
|
|
18
|
+
elevated: css`
|
|
19
|
+
box-shadow: ${cssVar.boxShadowSecondary};
|
|
20
|
+
`,
|
|
21
|
+
embedded: css`
|
|
22
|
+
border: 1px solid ${cssVar.colorBorderSecondary};
|
|
23
|
+
box-shadow: none;
|
|
24
|
+
`,
|
|
25
|
+
overlay: css`
|
|
26
|
+
position: absolute;
|
|
27
|
+
z-index: 10;
|
|
28
|
+
inset-block-end: 0;
|
|
29
|
+
inset-inline: 0;
|
|
30
|
+
`,
|
|
31
|
+
inline: css`
|
|
32
|
+
position: relative;
|
|
33
|
+
z-index: 1;
|
|
34
|
+
flex-shrink: 0;
|
|
35
|
+
`,
|
|
36
|
+
transition: css`
|
|
37
|
+
transition:
|
|
38
|
+
height ${transitionDuration} ${transitionEasing},
|
|
39
|
+
margin-block-start ${transitionDuration} ${transitionEasing};
|
|
40
|
+
`,
|
|
41
|
+
hidden: css`
|
|
42
|
+
visibility: hidden;
|
|
43
|
+
`,
|
|
44
|
+
header: css`
|
|
45
|
+
cursor: grab;
|
|
46
|
+
user-select: none;
|
|
47
|
+
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
flex-shrink: 0;
|
|
51
|
+
align-items: center;
|
|
52
|
+
|
|
53
|
+
padding-block: 8px 4px;
|
|
54
|
+
padding-inline: 16px;
|
|
55
|
+
`,
|
|
56
|
+
headerDragging: css`
|
|
57
|
+
cursor: grabbing;
|
|
58
|
+
`,
|
|
59
|
+
handle: css`
|
|
60
|
+
width: 32px;
|
|
61
|
+
height: 4px;
|
|
62
|
+
margin-block-end: 8px;
|
|
63
|
+
border-radius: 2px;
|
|
64
|
+
|
|
65
|
+
background: ${cssVar.colorBorderSecondary};
|
|
66
|
+
`,
|
|
67
|
+
headerContent: css`
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: space-between;
|
|
71
|
+
|
|
72
|
+
width: 100%;
|
|
73
|
+
min-height: 24px;
|
|
74
|
+
`,
|
|
75
|
+
headerTitle: css`
|
|
76
|
+
flex: 1;
|
|
77
|
+
min-width: 0;
|
|
78
|
+
`,
|
|
79
|
+
headerActions: css`
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-shrink: 0;
|
|
82
|
+
gap: 4px;
|
|
83
|
+
align-items: center;
|
|
84
|
+
`,
|
|
85
|
+
content: css`
|
|
86
|
+
overflow: auto;
|
|
87
|
+
flex: 1;
|
|
88
|
+
min-height: 0;
|
|
89
|
+
`
|
|
90
|
+
}));
|
|
91
|
+
//#endregion
|
|
92
|
+
export { styles };
|
|
93
|
+
|
|
94
|
+
//# sourceMappingURL=style.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nconst transitionDuration = '0.3s';\nconst transitionEasing = 'cubic-bezier(0.32, 0.72, 0, 1)';\n\nexport const styles = createStaticStyles(({ css, cssVar }) => ({\n root: css`\n overflow: hidden;\n display: flex;\n flex-direction: column;\n background: ${cssVar.colorBgContainer};\n `,\n overlayRadius: css`\n border-radius: 12px 12px 0 0;\n `,\n inlineRadius: css`\n border-radius: 12px;\n `,\n elevated: css`\n box-shadow: ${cssVar.boxShadowSecondary};\n `,\n embedded: css`\n border: 1px solid ${cssVar.colorBorderSecondary};\n box-shadow: none;\n `,\n overlay: css`\n position: absolute;\n z-index: 10;\n inset-block-end: 0;\n inset-inline: 0;\n `,\n inline: css`\n position: relative;\n z-index: 1;\n flex-shrink: 0;\n `,\n transition: css`\n transition:\n height ${transitionDuration} ${transitionEasing},\n margin-block-start ${transitionDuration} ${transitionEasing};\n `,\n hidden: css`\n visibility: hidden;\n `,\n header: css`\n cursor: grab;\n user-select: none;\n\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n align-items: center;\n\n padding-block: 8px 4px;\n padding-inline: 16px;\n `,\n headerDragging: css`\n cursor: grabbing;\n `,\n handle: css`\n width: 32px;\n height: 4px;\n margin-block-end: 8px;\n border-radius: 2px;\n\n background: ${cssVar.colorBorderSecondary};\n `,\n headerContent: css`\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n width: 100%;\n min-height: 24px;\n `,\n headerTitle: css`\n flex: 1;\n min-width: 0;\n `,\n headerActions: css`\n display: flex;\n flex-shrink: 0;\n gap: 4px;\n align-items: center;\n `,\n content: css`\n overflow: auto;\n flex: 1;\n min-height: 0;\n `,\n}));\n"],"mappings":";;AAEA,MAAM,qBAAqB;AAC3B,MAAM,mBAAmB;AAEzB,MAAa,SAAS,oBAAoB,EAAE,KAAK,cAAc;CAC7D,MAAM,GAAG;;;;kBAIO,OAAO,iBAAiB;;CAExC,eAAe,GAAG;;;CAGlB,cAAc,GAAG;;;CAGjB,UAAU,GAAG;kBACG,OAAO,mBAAmB;;CAE1C,UAAU,GAAG;wBACS,OAAO,qBAAqB;;;CAGlD,SAAS,GAAG;;;;;;CAMZ,QAAQ,GAAG;;;;;CAKX,YAAY,GAAG;;eAEF,mBAAmB,GAAG,iBAAiB;2BAC3B,mBAAmB,GAAG,iBAAiB;;CAEhE,QAAQ,GAAG;;;CAGX,QAAQ,GAAG;;;;;;;;;;;;CAYX,gBAAgB,GAAG;;;CAGnB,QAAQ,GAAG;;;;;;kBAMK,OAAO,qBAAqB;;CAE5C,eAAe,GAAG;;;;;;;;CAQlB,aAAa,GAAG;;;;CAIhB,eAAe,GAAG;;;;;;CAMlB,SAAS,GAAG;;;;;CAKb,EAAE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/base-ui/FloatingSheet/type.d.ts
|
|
4
|
+
interface FloatingSheetProps {
|
|
5
|
+
activeSnapPoint?: number;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
closeThreshold?: number;
|
|
9
|
+
defaultOpen?: boolean;
|
|
10
|
+
dismissible?: boolean;
|
|
11
|
+
headerActions?: ReactNode;
|
|
12
|
+
maxHeight?: number;
|
|
13
|
+
minHeight?: number;
|
|
14
|
+
mode?: 'overlay' | 'inline';
|
|
15
|
+
onOpenChange?: (open: boolean) => void;
|
|
16
|
+
onSnapPointChange?: (snapPoint: number) => void;
|
|
17
|
+
open?: boolean;
|
|
18
|
+
restingHeight?: number;
|
|
19
|
+
snapPoints?: number[];
|
|
20
|
+
title?: ReactNode;
|
|
21
|
+
variant?: 'elevated' | 'embedded';
|
|
22
|
+
width?: number | string;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { FloatingSheetProps };
|
|
26
|
+
//# sourceMappingURL=type.d.mts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
//#region src/base-ui/FloatingSheet/useSheetDrag.ts
|
|
3
|
+
function useSheetDrag({ onDragChange, onDragEnd, enabled }) {
|
|
4
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
5
|
+
const startY = useRef(0);
|
|
6
|
+
const startTime = useRef(0);
|
|
7
|
+
const draggingRef = useRef(false);
|
|
8
|
+
const onDragChangeRef = useRef(onDragChange);
|
|
9
|
+
const onDragEndRef = useRef(onDragEnd);
|
|
10
|
+
onDragChangeRef.current = onDragChange;
|
|
11
|
+
onDragEndRef.current = onDragEnd;
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!draggingRef.current) return;
|
|
14
|
+
const onMouseMove = (e) => {
|
|
15
|
+
e.preventDefault();
|
|
16
|
+
const draggedDistance = startY.current - e.clientY;
|
|
17
|
+
onDragChangeRef.current(draggedDistance);
|
|
18
|
+
};
|
|
19
|
+
const onMouseUp = (e) => {
|
|
20
|
+
draggingRef.current = false;
|
|
21
|
+
setIsDragging(false);
|
|
22
|
+
const draggedDistance = startY.current - e.clientY;
|
|
23
|
+
const elapsed = Date.now() - startTime.current;
|
|
24
|
+
const velocity = elapsed > 0 ? Math.abs(draggedDistance) / elapsed : 0;
|
|
25
|
+
onDragEndRef.current(draggedDistance, velocity);
|
|
26
|
+
};
|
|
27
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
28
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
29
|
+
return () => {
|
|
30
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
31
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
32
|
+
};
|
|
33
|
+
}, [isDragging]);
|
|
34
|
+
return {
|
|
35
|
+
isDragging,
|
|
36
|
+
handleProps: { onMouseDown: useCallback((event) => {
|
|
37
|
+
if (!enabled) return;
|
|
38
|
+
if (event.button !== 0) return;
|
|
39
|
+
if (event.target.closest?.("[data-no-drag]")) return;
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
startY.current = event.clientY;
|
|
42
|
+
startTime.current = Date.now();
|
|
43
|
+
draggingRef.current = true;
|
|
44
|
+
setIsDragging(true);
|
|
45
|
+
}, [enabled]) }
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { useSheetDrag };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=useSheetDrag.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSheetDrag.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/useSheetDrag.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface UseSheetDragOptions {\n enabled: boolean;\n onDragChange: (draggedDistance: number) => void;\n onDragEnd: (draggedDistance: number, velocity: number) => void;\n}\n\nexport function useSheetDrag({ onDragChange, onDragEnd, enabled }: UseSheetDragOptions) {\n const [isDragging, setIsDragging] = useState(false);\n const startY = useRef(0);\n const startTime = useRef(0);\n const draggingRef = useRef(false);\n\n // Store latest callbacks in refs to avoid stale closures in document listeners\n const onDragChangeRef = useRef(onDragChange);\n const onDragEndRef = useRef(onDragEnd);\n onDragChangeRef.current = onDragChange;\n onDragEndRef.current = onDragEnd;\n\n useEffect(() => {\n if (!draggingRef.current) return;\n\n const onMouseMove = (e: MouseEvent) => {\n e.preventDefault();\n const draggedDistance = startY.current - e.clientY;\n onDragChangeRef.current(draggedDistance);\n };\n\n const onMouseUp = (e: MouseEvent) => {\n draggingRef.current = false;\n setIsDragging(false);\n\n const draggedDistance = startY.current - e.clientY;\n const elapsed = Date.now() - startTime.current;\n const velocity = elapsed > 0 ? Math.abs(draggedDistance) / elapsed : 0;\n onDragEndRef.current(draggedDistance, velocity);\n };\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n return () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n }, [isDragging]);\n\n const onMouseDown = useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (!enabled) return;\n if (event.button !== 0) return; // left click only\n\n const target = event.target as HTMLElement;\n if (target.closest?.('[data-no-drag]')) return;\n\n event.preventDefault(); // prevent text selection during drag\n\n startY.current = event.clientY;\n startTime.current = Date.now();\n draggingRef.current = true;\n setIsDragging(true);\n },\n [enabled],\n );\n\n return {\n isDragging,\n handleProps: {\n onMouseDown,\n },\n };\n}\n"],"mappings":";;AAQA,SAAgB,aAAa,EAAE,cAAc,WAAW,WAAgC;CACtF,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,SAAS,OAAO,EAAE;CACxB,MAAM,YAAY,OAAO,EAAE;CAC3B,MAAM,cAAc,OAAO,MAAM;CAGjC,MAAM,kBAAkB,OAAO,aAAa;CAC5C,MAAM,eAAe,OAAO,UAAU;AACtC,iBAAgB,UAAU;AAC1B,cAAa,UAAU;AAEvB,iBAAgB;AACd,MAAI,CAAC,YAAY,QAAS;EAE1B,MAAM,eAAe,MAAkB;AACrC,KAAE,gBAAgB;GAClB,MAAM,kBAAkB,OAAO,UAAU,EAAE;AAC3C,mBAAgB,QAAQ,gBAAgB;;EAG1C,MAAM,aAAa,MAAkB;AACnC,eAAY,UAAU;AACtB,iBAAc,MAAM;GAEpB,MAAM,kBAAkB,OAAO,UAAU,EAAE;GAC3C,MAAM,UAAU,KAAK,KAAK,GAAG,UAAU;GACvC,MAAM,WAAW,UAAU,IAAI,KAAK,IAAI,gBAAgB,GAAG,UAAU;AACrE,gBAAa,QAAQ,iBAAiB,SAAS;;AAGjD,WAAS,iBAAiB,aAAa,YAAY;AACnD,WAAS,iBAAiB,WAAW,UAAU;AAC/C,eAAa;AACX,YAAS,oBAAoB,aAAa,YAAY;AACtD,YAAS,oBAAoB,WAAW,UAAU;;IAEnD,CAAC,WAAW,CAAC;AAoBhB,QAAO;EACL;EACA,aAAa,EACX,aArBgB,aACjB,UAA4C;AAC3C,OAAI,CAAC,QAAS;AACd,OAAI,MAAM,WAAW,EAAG;AAGxB,OADe,MAAM,OACV,UAAU,iBAAiB,CAAE;AAExC,SAAM,gBAAgB;AAEtB,UAAO,UAAU,MAAM;AACvB,aAAU,UAAU,KAAK,KAAK;AAC9B,eAAY,UAAU;AACtB,iBAAc,KAAK;KAErB,CAAC,QAAQ,CACV,EAME;EACF"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { clamp, resolveSize } from "./helpers.mjs";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
//#region src/base-ui/FloatingSheet/useSnapPoints.ts
|
|
4
|
+
const VELOCITY_THRESHOLD = .4;
|
|
5
|
+
const DRAG_DISTANCE_RATIO = .4;
|
|
6
|
+
function useSnapPoints({ closeThreshold, snapPoints, containerHeight, minHeightPx, maxHeightPx }) {
|
|
7
|
+
const snapPointHeights = useMemo(() => {
|
|
8
|
+
if (!containerHeight) return [];
|
|
9
|
+
const resolved = snapPoints.map((sp) => clamp(resolveSize(sp, containerHeight), minHeightPx, maxHeightPx)).sort((a, b) => a - b);
|
|
10
|
+
return [...new Set(resolved)];
|
|
11
|
+
}, [
|
|
12
|
+
snapPoints,
|
|
13
|
+
containerHeight,
|
|
14
|
+
minHeightPx,
|
|
15
|
+
maxHeightPx
|
|
16
|
+
]);
|
|
17
|
+
function findClosestSnapPoint(height) {
|
|
18
|
+
if (snapPointHeights.length === 0) return clamp(height, minHeightPx, maxHeightPx);
|
|
19
|
+
return snapPointHeights.reduce((prev, curr) => Math.abs(curr - height) < Math.abs(prev - height) ? curr : prev);
|
|
20
|
+
}
|
|
21
|
+
function findActiveIndex(height) {
|
|
22
|
+
const closest = findClosestSnapPoint(height);
|
|
23
|
+
return snapPointHeights.indexOf(closest);
|
|
24
|
+
}
|
|
25
|
+
function getSnapRelease({ currentHeight, activeIndex, draggedDistance, velocity, dismissible }) {
|
|
26
|
+
const isFirst = activeIndex === 0;
|
|
27
|
+
const isLast = activeIndex === snapPointHeights.length - 1;
|
|
28
|
+
const isDraggingUp = draggedDistance > 0;
|
|
29
|
+
const highestSnapPoint = snapPointHeights.at(-1) ?? maxHeightPx;
|
|
30
|
+
const lowestSnapPoint = snapPointHeights[0] ?? minHeightPx;
|
|
31
|
+
const nextHigherSnapPoint = snapPointHeights[Math.min(activeIndex + 1, snapPointHeights.length - 1)] ?? highestSnapPoint;
|
|
32
|
+
const nextLowerSnapPoint = snapPointHeights[Math.max(activeIndex - 1, 0)] ?? lowestSnapPoint;
|
|
33
|
+
const sheetHeight = snapPointHeights[activeIndex] ?? currentHeight;
|
|
34
|
+
if (velocity > VELOCITY_THRESHOLD && Math.abs(draggedDistance) < sheetHeight * DRAG_DISTANCE_RATIO) if (isDraggingUp) {
|
|
35
|
+
if (isLast) return {
|
|
36
|
+
type: "snap",
|
|
37
|
+
height: highestSnapPoint
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
type: "snap",
|
|
41
|
+
height: nextHigherSnapPoint
|
|
42
|
+
};
|
|
43
|
+
} else {
|
|
44
|
+
if (isFirst) return dismissible ? { type: "dismiss" } : {
|
|
45
|
+
type: "snap",
|
|
46
|
+
height: lowestSnapPoint
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
type: "snap",
|
|
50
|
+
height: nextLowerSnapPoint
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (dismissible && isFirst && !isDraggingUp && currentHeight < lowestSnapPoint * closeThreshold) return { type: "dismiss" };
|
|
54
|
+
return {
|
|
55
|
+
type: "snap",
|
|
56
|
+
height: findClosestSnapPoint(currentHeight)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
snapPointHeights,
|
|
61
|
+
findClosestSnapPoint,
|
|
62
|
+
findActiveIndex,
|
|
63
|
+
getSnapRelease
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { useSnapPoints };
|
|
68
|
+
|
|
69
|
+
//# sourceMappingURL=useSnapPoints.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSnapPoints.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/useSnapPoints.ts"],"sourcesContent":["import { type RefObject, useMemo } from 'react';\n\nimport { clamp, resolveSize } from './helpers';\n\nconst VELOCITY_THRESHOLD = 0.4;\nconst DRAG_DISTANCE_RATIO = 0.4;\n\ninterface UseSnapPointsOptions {\n closeThreshold: number;\n containerHeight: number;\n containerRef: RefObject<HTMLElement | null>;\n maxHeightPx: number;\n minHeightPx: number;\n snapPoints: number[];\n}\n\ninterface SnapReleaseParams {\n activeIndex: number;\n currentHeight: number;\n dismissible: boolean;\n draggedDistance: number; // positive = upward (growing), negative = downward (shrinking)\n velocity: number;\n}\n\ntype SnapReleaseResult = { type: 'snap'; height: number } | { type: 'dismiss' };\n\nexport function useSnapPoints({\n closeThreshold,\n snapPoints,\n containerHeight,\n minHeightPx,\n maxHeightPx,\n}: UseSnapPointsOptions) {\n const snapPointHeights = useMemo(() => {\n if (!containerHeight) return [];\n\n const resolved = snapPoints\n .map((sp) => clamp(resolveSize(sp, containerHeight), minHeightPx, maxHeightPx))\n .sort((a, b) => a - b);\n\n // Remove duplicates\n return [...new Set(resolved)];\n }, [snapPoints, containerHeight, minHeightPx, maxHeightPx]);\n\n function findClosestSnapPoint(height: number): number {\n if (snapPointHeights.length === 0) return clamp(height, minHeightPx, maxHeightPx);\n\n return snapPointHeights.reduce((prev, curr) =>\n Math.abs(curr - height) < Math.abs(prev - height) ? curr : prev,\n );\n }\n\n function findActiveIndex(height: number): number {\n const closest = findClosestSnapPoint(height);\n return snapPointHeights.indexOf(closest);\n }\n\n function getSnapRelease({\n currentHeight,\n activeIndex,\n draggedDistance,\n velocity,\n dismissible,\n }: SnapReleaseParams): SnapReleaseResult {\n const isFirst = activeIndex === 0;\n const isLast = activeIndex === snapPointHeights.length - 1;\n const isDraggingUp = draggedDistance > 0;\n const highestSnapPoint = snapPointHeights.at(-1) ?? maxHeightPx;\n const lowestSnapPoint = snapPointHeights[0] ?? minHeightPx;\n const nextHigherSnapPoint =\n snapPointHeights[Math.min(activeIndex + 1, snapPointHeights.length - 1)] ?? highestSnapPoint;\n const nextLowerSnapPoint = snapPointHeights[Math.max(activeIndex - 1, 0)] ?? lowestSnapPoint;\n const sheetHeight = snapPointHeights[activeIndex] ?? currentHeight;\n\n // High velocity handling\n if (\n velocity > VELOCITY_THRESHOLD &&\n Math.abs(draggedDistance) < sheetHeight * DRAG_DISTANCE_RATIO\n ) {\n if (isDraggingUp) {\n // Fling upward: go to next higher snap, cap at highest\n if (isLast) return { type: 'snap', height: highestSnapPoint };\n\n return { type: 'snap', height: nextHigherSnapPoint };\n } else {\n // Fling downward: go to next lower snap, or dismiss if at lowest\n if (isFirst) {\n return dismissible ? { type: 'dismiss' } : { type: 'snap', height: lowestSnapPoint };\n }\n\n return { type: 'snap', height: nextLowerSnapPoint };\n }\n }\n\n if (\n dismissible &&\n isFirst &&\n !isDraggingUp &&\n currentHeight < lowestSnapPoint * closeThreshold\n ) {\n return { type: 'dismiss' };\n }\n\n // Low velocity: snap to closest\n const closest = findClosestSnapPoint(currentHeight);\n return { type: 'snap', height: closest };\n }\n\n return {\n snapPointHeights,\n findClosestSnapPoint,\n findActiveIndex,\n getSnapRelease,\n };\n}\n"],"mappings":";;;AAIA,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAqB5B,SAAgB,cAAc,EAC5B,gBACA,YACA,iBACA,aACA,eACuB;CACvB,MAAM,mBAAmB,cAAc;AACrC,MAAI,CAAC,gBAAiB,QAAO,EAAE;EAE/B,MAAM,WAAW,WACd,KAAK,OAAO,MAAM,YAAY,IAAI,gBAAgB,EAAE,aAAa,YAAY,CAAC,CAC9E,MAAM,GAAG,MAAM,IAAI,EAAE;AAGxB,SAAO,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC;IAC5B;EAAC;EAAY;EAAiB;EAAa;EAAY,CAAC;CAE3D,SAAS,qBAAqB,QAAwB;AACpD,MAAI,iBAAiB,WAAW,EAAG,QAAO,MAAM,QAAQ,aAAa,YAAY;AAEjF,SAAO,iBAAiB,QAAQ,MAAM,SACpC,KAAK,IAAI,OAAO,OAAO,GAAG,KAAK,IAAI,OAAO,OAAO,GAAG,OAAO,KAC5D;;CAGH,SAAS,gBAAgB,QAAwB;EAC/C,MAAM,UAAU,qBAAqB,OAAO;AAC5C,SAAO,iBAAiB,QAAQ,QAAQ;;CAG1C,SAAS,eAAe,EACtB,eACA,aACA,iBACA,UACA,eACuC;EACvC,MAAM,UAAU,gBAAgB;EAChC,MAAM,SAAS,gBAAgB,iBAAiB,SAAS;EACzD,MAAM,eAAe,kBAAkB;EACvC,MAAM,mBAAmB,iBAAiB,GAAG,GAAG,IAAI;EACpD,MAAM,kBAAkB,iBAAiB,MAAM;EAC/C,MAAM,sBACJ,iBAAiB,KAAK,IAAI,cAAc,GAAG,iBAAiB,SAAS,EAAE,KAAK;EAC9E,MAAM,qBAAqB,iBAAiB,KAAK,IAAI,cAAc,GAAG,EAAE,KAAK;EAC7E,MAAM,cAAc,iBAAiB,gBAAgB;AAGrD,MACE,WAAW,sBACX,KAAK,IAAI,gBAAgB,GAAG,cAAc,oBAE1C,KAAI,cAAc;AAEhB,OAAI,OAAQ,QAAO;IAAE,MAAM;IAAQ,QAAQ;IAAkB;AAE7D,UAAO;IAAE,MAAM;IAAQ,QAAQ;IAAqB;SAC/C;AAEL,OAAI,QACF,QAAO,cAAc,EAAE,MAAM,WAAW,GAAG;IAAE,MAAM;IAAQ,QAAQ;IAAiB;AAGtF,UAAO;IAAE,MAAM;IAAQ,QAAQ;IAAoB;;AAIvD,MACE,eACA,WACA,CAAC,gBACD,gBAAgB,kBAAkB,eAElC,QAAO,EAAE,MAAM,WAAW;AAK5B,SAAO;GAAE,MAAM;GAAQ,QADP,qBAAqB,cAAc;GACX;;AAG1C,QAAO;EACL;EACA;EACA;EACA;EACD"}
|
package/es/base-ui/index.d.mts
CHANGED
|
@@ -19,6 +19,8 @@ import { ScrollAreaProps } from "./ScrollArea/type.mjs";
|
|
|
19
19
|
import { ScrollArea } from "./ScrollArea/ScrollArea.mjs";
|
|
20
20
|
import { ToastAPI, ToastInstance, ToastOptions, ToastPosition, ToastPromiseOptions, ToastProps, ToastType } from "./Toast/type.mjs";
|
|
21
21
|
import { ToastHost, ToastHostProps, toast, useToast } from "./Toast/imperative.mjs";
|
|
22
|
+
import { FloatingSheetProps } from "./FloatingSheet/type.mjs";
|
|
23
|
+
import { FloatingSheet } from "./FloatingSheet/FloatingSheet.mjs";
|
|
22
24
|
import { ModalBackdrop, ModalBackdropProps, ModalClose, ModalCloseProps, ModalContent, ModalContentProps, ModalDescription, ModalDescriptionProps, ModalFooter, ModalFooterProps, ModalHeader, ModalHeaderProps, ModalPopup, ModalPopupProps, ModalPortal, ModalPortalProps, ModalRoot, ModalRootProps, ModalTitle, ModalTitleProps, ModalTrigger, ModalTriggerProps, ModalViewport, ModalViewportProps, useModalActions, useModalOpen } from "./Modal/atoms.mjs";
|
|
23
25
|
import { backdropTransition, modalMotionConfig } from "./Modal/constants.mjs";
|
|
24
26
|
import { BaseModalProps, ImperativeModalProps, ModalComponentProps, ModalConfirmConfig, ModalContextValue, ModalInstance } from "./Modal/type.mjs";
|
|
@@ -32,4 +34,4 @@ import { SwitchChangeEventHandler, SwitchClassNames, SwitchClickEventHandler, Sw
|
|
|
32
34
|
import { styles } from "./Switch/style.mjs";
|
|
33
35
|
import { SwitchIcon, SwitchRoot, SwitchThumb, useSwitchContext } from "./Switch/atoms.mjs";
|
|
34
36
|
import { Switch } from "./Switch/Switch.mjs";
|
|
35
|
-
export { BaseModalProps, type ContextMenuCheckboxItem, ContextMenuHost, type ContextMenuItem, ContextMenuTrigger, DropdownItem, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuCheckboxItemIndicator, DropdownMenuCheckboxItemPrimitive, DropdownMenuCheckboxItemProps, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuGroupLabelProps, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemContentProps, DropdownMenuItemDesc, DropdownMenuItemDescProps, DropdownMenuItemExtra, DropdownMenuItemExtraProps, DropdownMenuItemIcon, DropdownMenuItemIconProps, DropdownMenuItemLabel, DropdownMenuItemLabelGroup, DropdownMenuItemLabelGroupProps, DropdownMenuItemLabelProps, DropdownMenuItemProps, DropdownMenuPlacement, DropdownMenuPopup, DropdownMenuPopupProps, DropdownMenuPortal, DropdownMenuPortalProps, DropdownMenuPositioner, DropdownMenuPositionerProps, DropdownMenuProps, DropdownMenuRoot, DropdownMenuSeparator, DropdownMenuSeparatorProps, DropdownMenuSubmenuArrow, DropdownMenuSubmenuArrowProps, DropdownMenuSubmenuRoot, DropdownMenuSubmenuTrigger, DropdownMenuSubmenuTriggerProps, DropdownMenuSwitchItem, DropdownMenuSwitchItemProps, DropdownMenuSwitchItem$1 as DropdownMenuSwitchItemType, DropdownMenuTrigger, DropdownMenuTriggerProps, IconSpaceMode, ImperativeModalProps, Modal, ModalBackdrop, ModalBackdropProps, ModalClose, ModalCloseProps, ModalComponentProps, ModalConfirmConfig, ModalContent, ModalContentProps, ModalContext, ModalContextValue, ModalDescription, ModalDescriptionProps, ModalFooter, ModalFooterProps, ModalHeader, ModalHeaderProps, ModalHost, ModalHostProps, ModalInstance, ModalPopup, ModalPopupProps, ModalPortal, ModalPortalProps, ModalRoot, ModalRootProps, ModalSystem, ModalTitle, ModalTitleProps, ModalTrigger, ModalTriggerProps, ModalViewport, ModalViewportProps, PopoverArrow, PopoverArrowAtomProps, PopoverArrowIcon, PopoverBackdrop, PopoverBackdropProps, PopoverContextValue, PopoverGroup, PopoverPlacement, PopoverPopup, PopoverPopupAtomProps, PopoverPopupProps, PopoverPortal, PopoverPortalAtomProps, PopoverPortalProps, PopoverPositioner, PopoverPositionerAtomProps, PopoverPositionerProps, PopoverProps, PopoverProvider, PopoverRoot, PopoverTrigger, PopoverTriggerComponentProps, PopoverTriggerElement, PopoverTriggerElementProps, PopoverViewport, PopoverViewportAtomProps, ScrollArea, ScrollAreaContent, ScrollAreaContentProps, ScrollAreaCorner, ScrollAreaCornerProps, ScrollAreaProps, ScrollAreaRoot, ScrollAreaRootProps, ScrollAreaScrollbar, ScrollAreaScrollbarProps, ScrollAreaThumb, ScrollAreaThumbProps, ScrollAreaViewport, ScrollAreaViewportProps, Select, SelectArrow, SelectArrowProps, SelectBackdrop, SelectBehaviorVariant, SelectClassNames, SelectGroup, SelectGroupLabel, SelectGroupLabelProps, SelectGroupProps, SelectIcon, SelectIconProps, SelectIndicatorVariant, SelectItem, SelectItemIndicator, SelectItemIndicatorProps, SelectItemProps, SelectItemText, SelectItemTextProps, SelectList, SelectListProps, SelectOption, SelectOptionGroup, SelectOptions, SelectPopup, SelectPopupProps, SelectPortal, SelectPortalProps, SelectPositioner, SelectPositionerProps, SelectProps, SelectRoot, SelectScrollDownArrow, SelectScrollDownArrowProps, SelectScrollUpArrow, SelectScrollUpArrowProps, SelectSeparator, SelectSize, SelectTrigger, SelectTriggerProps, SelectValue, SelectValueProps, SelectVariant, Switch, SwitchChangeEventHandler, SwitchClassNames, SwitchClickEventHandler, SwitchContextType, SwitchIcon, SwitchIconPosition, SwitchIconProps, SwitchProps, SwitchRoot, SwitchRootProps, SwitchSize, SwitchStyles, SwitchThumb, SwitchThumbProps, type ToastAPI, ToastHost, type ToastHostProps, type ToastInstance, type ToastOptions, type ToastPosition, type ToastPromiseOptions, type ToastProps, type ToastType, backdropTransition, closeContextMenu, confirmModal, createModal, createModalSystem, modalMotionConfig, parseTrigger, renderDropdownMenuItems, showContextMenu, styles as switchStyles, toast, updateContextMenuItems, useModalActions, useModalContext, useModalOpen, usePopoverContext, usePopoverPortalContainer, useSwitchContext, useToast };
|
|
37
|
+
export { BaseModalProps, type ContextMenuCheckboxItem, ContextMenuHost, type ContextMenuItem, ContextMenuTrigger, DropdownItem, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuCheckboxItemIndicator, DropdownMenuCheckboxItemPrimitive, DropdownMenuCheckboxItemProps, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuGroupLabelProps, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemContentProps, DropdownMenuItemDesc, DropdownMenuItemDescProps, DropdownMenuItemExtra, DropdownMenuItemExtraProps, DropdownMenuItemIcon, DropdownMenuItemIconProps, DropdownMenuItemLabel, DropdownMenuItemLabelGroup, DropdownMenuItemLabelGroupProps, DropdownMenuItemLabelProps, DropdownMenuItemProps, DropdownMenuPlacement, DropdownMenuPopup, DropdownMenuPopupProps, DropdownMenuPortal, DropdownMenuPortalProps, DropdownMenuPositioner, DropdownMenuPositionerProps, DropdownMenuProps, DropdownMenuRoot, DropdownMenuSeparator, DropdownMenuSeparatorProps, DropdownMenuSubmenuArrow, DropdownMenuSubmenuArrowProps, DropdownMenuSubmenuRoot, DropdownMenuSubmenuTrigger, DropdownMenuSubmenuTriggerProps, DropdownMenuSwitchItem, DropdownMenuSwitchItemProps, DropdownMenuSwitchItem$1 as DropdownMenuSwitchItemType, DropdownMenuTrigger, DropdownMenuTriggerProps, FloatingSheet, FloatingSheetProps, IconSpaceMode, ImperativeModalProps, Modal, ModalBackdrop, ModalBackdropProps, ModalClose, ModalCloseProps, ModalComponentProps, ModalConfirmConfig, ModalContent, ModalContentProps, ModalContext, ModalContextValue, ModalDescription, ModalDescriptionProps, ModalFooter, ModalFooterProps, ModalHeader, ModalHeaderProps, ModalHost, ModalHostProps, ModalInstance, ModalPopup, ModalPopupProps, ModalPortal, ModalPortalProps, ModalRoot, ModalRootProps, ModalSystem, ModalTitle, ModalTitleProps, ModalTrigger, ModalTriggerProps, ModalViewport, ModalViewportProps, PopoverArrow, PopoverArrowAtomProps, PopoverArrowIcon, PopoverBackdrop, PopoverBackdropProps, PopoverContextValue, PopoverGroup, PopoverPlacement, PopoverPopup, PopoverPopupAtomProps, PopoverPopupProps, PopoverPortal, PopoverPortalAtomProps, PopoverPortalProps, PopoverPositioner, PopoverPositionerAtomProps, PopoverPositionerProps, PopoverProps, PopoverProvider, PopoverRoot, PopoverTrigger, PopoverTriggerComponentProps, PopoverTriggerElement, PopoverTriggerElementProps, PopoverViewport, PopoverViewportAtomProps, ScrollArea, ScrollAreaContent, ScrollAreaContentProps, ScrollAreaCorner, ScrollAreaCornerProps, ScrollAreaProps, ScrollAreaRoot, ScrollAreaRootProps, ScrollAreaScrollbar, ScrollAreaScrollbarProps, ScrollAreaThumb, ScrollAreaThumbProps, ScrollAreaViewport, ScrollAreaViewportProps, Select, SelectArrow, SelectArrowProps, SelectBackdrop, SelectBehaviorVariant, SelectClassNames, SelectGroup, SelectGroupLabel, SelectGroupLabelProps, SelectGroupProps, SelectIcon, SelectIconProps, SelectIndicatorVariant, SelectItem, SelectItemIndicator, SelectItemIndicatorProps, SelectItemProps, SelectItemText, SelectItemTextProps, SelectList, SelectListProps, SelectOption, SelectOptionGroup, SelectOptions, SelectPopup, SelectPopupProps, SelectPortal, SelectPortalProps, SelectPositioner, SelectPositionerProps, SelectProps, SelectRoot, SelectScrollDownArrow, SelectScrollDownArrowProps, SelectScrollUpArrow, SelectScrollUpArrowProps, SelectSeparator, SelectSize, SelectTrigger, SelectTriggerProps, SelectValue, SelectValueProps, SelectVariant, Switch, SwitchChangeEventHandler, SwitchClassNames, SwitchClickEventHandler, SwitchContextType, SwitchIcon, SwitchIconPosition, SwitchIconProps, SwitchProps, SwitchRoot, SwitchRootProps, SwitchSize, SwitchStyles, SwitchThumb, SwitchThumbProps, type ToastAPI, ToastHost, type ToastHostProps, type ToastInstance, type ToastOptions, type ToastPosition, type ToastPromiseOptions, type ToastProps, type ToastType, backdropTransition, closeContextMenu, confirmModal, createModal, createModalSystem, modalMotionConfig, parseTrigger, renderDropdownMenuItems, showContextMenu, styles as switchStyles, toast, updateContextMenuItems, useModalActions, useModalContext, useModalOpen, usePopoverContext, usePopoverPortalContainer, useSwitchContext, useToast };
|
package/es/base-ui/index.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import PopoverGroup from "./Popover/PopoverGroup.mjs";
|
|
|
13
13
|
import { ScrollAreaContent, ScrollAreaCorner, ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport } from "./ScrollArea/atoms.mjs";
|
|
14
14
|
import { ScrollArea } from "./ScrollArea/ScrollArea.mjs";
|
|
15
15
|
import { ToastHost, toast, useToast } from "./Toast/imperative.mjs";
|
|
16
|
+
import { FloatingSheet } from "./FloatingSheet/FloatingSheet.mjs";
|
|
16
17
|
import { backdropTransition, modalMotionConfig } from "./Modal/constants.mjs";
|
|
17
18
|
import { ModalBackdrop, ModalClose, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalPopup, ModalPortal, ModalRoot, ModalTitle, ModalTrigger, ModalViewport, useModalActions, useModalOpen } from "./Modal/atoms.mjs";
|
|
18
19
|
import { ModalContext, useModalContext } from "./Modal/context.mjs";
|
|
@@ -23,4 +24,4 @@ import Select from "./Select/Select.mjs";
|
|
|
23
24
|
import { styles } from "./Switch/style.mjs";
|
|
24
25
|
import { SwitchIcon, SwitchRoot, SwitchThumb, useSwitchContext } from "./Switch/atoms.mjs";
|
|
25
26
|
import Switch from "./Switch/Switch.mjs";
|
|
26
|
-
export { ContextMenuHost, ContextMenuTrigger, DropdownMenu, DropdownMenuCheckboxItemIndicator, DropdownMenuCheckboxItemPrimitive, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemDesc, DropdownMenuItemExtra, DropdownMenuItemIcon, DropdownMenuItemLabel, DropdownMenuItemLabelGroup, DropdownMenuPopup, DropdownMenuPortal, DropdownMenuPositioner, DropdownMenuRoot, DropdownMenuSeparator, DropdownMenuSubmenuArrow, DropdownMenuSubmenuRoot, DropdownMenuSubmenuTrigger, DropdownMenuSwitchItem, DropdownMenuTrigger, Modal, ModalBackdrop, ModalClose, ModalContent, ModalContext, ModalDescription, ModalFooter, ModalHeader, ModalHost, ModalPopup, ModalPortal, ModalRoot, ModalTitle, ModalTrigger, ModalViewport, PopoverArrow, PopoverArrowIcon, PopoverBackdrop, PopoverGroup, PopoverPopup, PopoverPortal, PopoverPositioner, PopoverProvider, PopoverRoot, PopoverTriggerElement, PopoverViewport, ScrollArea, ScrollAreaContent, ScrollAreaCorner, ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport, Select, SelectArrow, SelectBackdrop, SelectGroup, SelectGroupLabel, SelectIcon, SelectItem, SelectItemIndicator, SelectItemText, SelectList, SelectPopup, SelectPortal, SelectPositioner, SelectRoot, SelectScrollDownArrow, SelectScrollUpArrow, SelectSeparator, SelectTrigger, SelectValue, Switch, SwitchIcon, SwitchRoot, SwitchThumb, ToastHost, backdropTransition, closeContextMenu, confirmModal, createModal, createModalSystem, modalMotionConfig, parseTrigger, renderDropdownMenuItems, showContextMenu, styles as switchStyles, toast, updateContextMenuItems, useModalActions, useModalContext, useModalOpen, usePopoverContext, usePopoverPortalContainer, useSwitchContext, useToast };
|
|
27
|
+
export { ContextMenuHost, ContextMenuTrigger, DropdownMenu, DropdownMenuCheckboxItemIndicator, DropdownMenuCheckboxItemPrimitive, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemDesc, DropdownMenuItemExtra, DropdownMenuItemIcon, DropdownMenuItemLabel, DropdownMenuItemLabelGroup, DropdownMenuPopup, DropdownMenuPortal, DropdownMenuPositioner, DropdownMenuRoot, DropdownMenuSeparator, DropdownMenuSubmenuArrow, DropdownMenuSubmenuRoot, DropdownMenuSubmenuTrigger, DropdownMenuSwitchItem, DropdownMenuTrigger, FloatingSheet, Modal, ModalBackdrop, ModalClose, ModalContent, ModalContext, ModalDescription, ModalFooter, ModalHeader, ModalHost, ModalPopup, ModalPortal, ModalRoot, ModalTitle, ModalTrigger, ModalViewport, PopoverArrow, PopoverArrowIcon, PopoverBackdrop, PopoverGroup, PopoverPopup, PopoverPortal, PopoverPositioner, PopoverProvider, PopoverRoot, PopoverTriggerElement, PopoverViewport, ScrollArea, ScrollAreaContent, ScrollAreaCorner, ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport, Select, SelectArrow, SelectBackdrop, SelectGroup, SelectGroupLabel, SelectIcon, SelectItem, SelectItemIndicator, SelectItemText, SelectList, SelectPopup, SelectPortal, SelectPositioner, SelectRoot, SelectScrollDownArrow, SelectScrollUpArrow, SelectSeparator, SelectTrigger, SelectValue, Switch, SwitchIcon, SwitchRoot, SwitchThumb, ToastHost, backdropTransition, closeContextMenu, confirmModal, createModal, createModalSystem, modalMotionConfig, parseTrigger, renderDropdownMenuItems, showContextMenu, styles as switchStyles, toast, updateContextMenuItems, useModalActions, useModalContext, useModalOpen, usePopoverContext, usePopoverPortalContainer, useSwitchContext, useToast };
|