@lobehub/ui 5.32.4 → 5.32.6
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 +15 -294
- package/es/Markdown/SyntaxMarkdown/StreamdownRender.mjs.map +1 -1
- package/es/Markdown/type.d.mts +2 -3
- package/es/base-ui/ContextMenu/renderItems.mjs +24 -1
- package/es/base-ui/ContextMenu/renderItems.mjs.map +1 -1
- package/es/base-ui/Input/style.d.mts +2 -2
- package/es/base-ui/Segmented/Segmented.mjs +4 -0
- package/es/base-ui/Segmented/Segmented.mjs.map +1 -1
- package/es/base-ui/Segmented/style.mjs +4 -0
- package/es/base-ui/Segmented/style.mjs.map +1 -1
- package/es/base-ui/Tabs/style.mjs +12 -0
- package/es/base-ui/Tabs/style.mjs.map +1 -1
- package/es/base-ui/Tooltip/style.mjs +104 -20
- package/es/base-ui/Tooltip/style.mjs.map +1 -1
- package/es/hooks/useMarkdown/useMarkdownContent.mjs +4 -16
- package/es/hooks/useMarkdown/useMarkdownContent.mjs.map +1 -1
- package/es/hooks/useMarkdown/utils.mjs +1 -1
- package/es/hooks/useMarkdown/utils.mjs.map +1 -1
- package/es/index.d.mts +1 -1
- package/es/index.mjs +1 -1
- package/package.json +2 -88
- package/es/Markdown/SyntaxMarkdown/CachedMarkdown.mjs +0 -83
- package/es/Markdown/SyntaxMarkdown/CachedMarkdown.mjs.map +0 -1
- package/es/Markdown/SyntaxMarkdown/fenceState.mjs +0 -42
- package/es/Markdown/SyntaxMarkdown/fenceState.mjs.map +0 -1
- package/es/Markdown/SyntaxMarkdown/streamAnimationMeta.mjs +0 -14
- package/es/Markdown/SyntaxMarkdown/streamAnimationMeta.mjs.map +0 -1
- package/es/Markdown/SyntaxMarkdown/style.mjs +0 -28
- package/es/Markdown/SyntaxMarkdown/style.mjs.map +0 -1
- package/es/Markdown/SyntaxMarkdown/useSmoothStreamContent.mjs +0 -314
- package/es/Markdown/SyntaxMarkdown/useSmoothStreamContent.mjs.map +0 -1
- package/es/Markdown/SyntaxMarkdown/useStreamQueue.mjs +0 -88
- package/es/Markdown/SyntaxMarkdown/useStreamQueue.mjs.map +0 -1
- package/es/Markdown/plugins/rehypeStreamAnimated.d.mts +0 -33
- package/es/Markdown/plugins/rehypeStreamAnimated.mjs +0 -115
- package/es/Markdown/plugins/rehypeStreamAnimated.mjs.map +0 -1
- package/es/Markdown/streamProfiler/StreamdownProfilerProvider.mjs +0 -19
- package/es/Markdown/streamProfiler/StreamdownProfilerProvider.mjs.map +0 -1
- package/es/hooks/useMarkdown/latex.mjs +0 -294
- package/es/hooks/useMarkdown/latex.mjs.map +0 -1
- package/es/node_modules/.pnpm/@types_hast@3.0.5/node_modules/@types/hast/index.d.mts +0 -869
- package/es/utils/getNow.mjs +0 -8
- package/es/utils/getNow.mjs.map +0 -1
|
@@ -1,307 +1,28 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { isDeepEqual } from "../../utils/isDeepEqual.mjs";
|
|
3
|
-
import { useStableValue } from "../../hooks/useStableValue.mjs";
|
|
4
2
|
import { useMarkdownContext } from "../components/MarkdownProvider.mjs";
|
|
5
3
|
import { useMarkdownComponents } from "../../hooks/useMarkdown/useMarkdownComponents.mjs";
|
|
6
4
|
import { useMarkdownContent } from "../../hooks/useMarkdown/useMarkdownContent.mjs";
|
|
7
5
|
import { useMarkdownRehypePlugins } from "../../hooks/useMarkdown/useMarkdownRehypePlugins.mjs";
|
|
8
6
|
import { useMarkdownRemarkPlugins } from "../../hooks/useMarkdown/useMarkdownRemarkPlugins.mjs";
|
|
9
|
-
import {
|
|
10
|
-
import { rehypeStreamAnimated } from "../plugins/rehypeStreamAnimated.mjs";
|
|
11
|
-
import { useStreamdownProfiler } from "../streamProfiler/StreamdownProfilerProvider.mjs";
|
|
12
|
-
import { CachedMarkdown } from "./CachedMarkdown.mjs";
|
|
13
|
-
import { resolveBlockAnimationMeta } from "./streamAnimationMeta.mjs";
|
|
14
|
-
import { styles } from "./style.mjs";
|
|
15
|
-
import { countChars, useSmoothStreamContent } from "./useSmoothStreamContent.mjs";
|
|
16
|
-
import { useStreamQueue } from "./useStreamQueue.mjs";
|
|
17
|
-
import { Profiler, createElement, memo, useCallback, useEffect, useId, useMemo, useRef } from "react";
|
|
7
|
+
import { memo } from "react";
|
|
18
8
|
import { jsx } from "react/jsx-runtime";
|
|
19
|
-
import {
|
|
20
|
-
import remend from "remend";
|
|
9
|
+
import { Streamdown } from "@lobehub/streamdown";
|
|
21
10
|
//#region src/Markdown/SyntaxMarkdown/StreamdownRender.tsx
|
|
22
|
-
const isSamePlugin = (prevPlugin, nextPlugin) => {
|
|
23
|
-
const prevTuple = Array.isArray(prevPlugin) ? prevPlugin : [prevPlugin];
|
|
24
|
-
const nextTuple = Array.isArray(nextPlugin) ? nextPlugin : [nextPlugin];
|
|
25
|
-
if (prevTuple.length !== nextTuple.length) return false;
|
|
26
|
-
if (prevTuple[0] !== nextTuple[0]) return false;
|
|
27
|
-
return isDeepEqual(prevTuple.slice(1), nextTuple.slice(1));
|
|
28
|
-
};
|
|
29
|
-
const isSamePlugins = (prevPlugins, nextPlugins) => {
|
|
30
|
-
if (prevPlugins === nextPlugins) return true;
|
|
31
|
-
if (!prevPlugins || !nextPlugins) return !prevPlugins && !nextPlugins;
|
|
32
|
-
if (prevPlugins.length !== nextPlugins.length) return false;
|
|
33
|
-
for (let i = 0; i < prevPlugins.length; i++) if (!isSamePlugin(prevPlugins[i], nextPlugins[i])) return false;
|
|
34
|
-
return true;
|
|
35
|
-
};
|
|
36
|
-
const useStablePlugins = (plugins) => {
|
|
37
|
-
const stableRef = useRef(plugins);
|
|
38
|
-
if (!isSamePlugins(stableRef.current, plugins)) stableRef.current = plugins;
|
|
39
|
-
return stableRef.current;
|
|
40
|
-
};
|
|
41
|
-
const StreamdownBlock = memo(({ children, ...rest }) => {
|
|
42
|
-
return /* @__PURE__ */ jsx(CachedMarkdown, {
|
|
43
|
-
...rest,
|
|
44
|
-
children
|
|
45
|
-
});
|
|
46
|
-
}, (prevProps, nextProps) => prevProps.children === nextProps.children && prevProps.components === nextProps.components && isSamePlugins(prevProps.rehypePlugins, nextProps.rehypePlugins) && isSamePlugins(prevProps.remarkPlugins, nextProps.remarkPlugins));
|
|
47
|
-
StreamdownBlock.displayName = "StreamdownBlock";
|
|
48
|
-
const MIN_STREAM_CHAR_PACE_MS = 2;
|
|
49
|
-
const MAX_REVEAL_GAP_MS = 160;
|
|
50
|
-
const updateBlockAnimation = ({ blocks, charDelay, getBlockState, pluginsCache, renderNow, revealClock, runtimes }) => {
|
|
51
|
-
const blockAnimationMeta = /* @__PURE__ */ new Map();
|
|
52
|
-
const alive = /* @__PURE__ */ new Set();
|
|
53
|
-
let revealedNewChars = false;
|
|
54
|
-
for (const [index, block] of blocks.entries()) {
|
|
55
|
-
alive.add(block.startOffset);
|
|
56
|
-
const state = getBlockState(index);
|
|
57
|
-
if (state === "queued") continue;
|
|
58
|
-
let runtime = runtimes.get(block.startOffset);
|
|
59
|
-
if (!runtime) {
|
|
60
|
-
runtime = {
|
|
61
|
-
births: [],
|
|
62
|
-
charCount: 0,
|
|
63
|
-
rawLength: -1,
|
|
64
|
-
settled: false,
|
|
65
|
-
styles: []
|
|
66
|
-
};
|
|
67
|
-
runtimes.set(block.startOffset, runtime);
|
|
68
|
-
}
|
|
69
|
-
if (runtime.rawLength !== block.content.length) {
|
|
70
|
-
runtime.charCount = countChars(block.content);
|
|
71
|
-
runtime.rawLength = block.content.length;
|
|
72
|
-
}
|
|
73
|
-
const blockCharCount = runtime.charCount;
|
|
74
|
-
const births = runtime.births;
|
|
75
|
-
if (births.length > blockCharCount) {
|
|
76
|
-
births.length = blockCharCount;
|
|
77
|
-
runtime.styles.length = blockCharCount;
|
|
78
|
-
}
|
|
79
|
-
if (births.length < blockCharCount) {
|
|
80
|
-
const newChars = blockCharCount - births.length;
|
|
81
|
-
let pace = charDelay;
|
|
82
|
-
let cap = renderNow + 180;
|
|
83
|
-
if (state === "streaming") {
|
|
84
|
-
revealedNewChars = true;
|
|
85
|
-
const gapMs = Math.min(Math.max(renderNow - revealClock.lastTs, 16), MAX_REVEAL_GAP_MS);
|
|
86
|
-
pace = Math.min(charDelay, Math.max(gapMs / newChars, MIN_STREAM_CHAR_PACE_MS));
|
|
87
|
-
cap = renderNow + gapMs + 180;
|
|
88
|
-
}
|
|
89
|
-
for (let i = births.length; i < blockCharCount; i++) {
|
|
90
|
-
const chained = (i > 0 ? births[i - 1] : renderNow - pace) + pace;
|
|
91
|
-
births.push(Math.min(cap, Math.max(chained, renderNow)));
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
let meta;
|
|
95
|
-
if (runtime.settled) meta = {
|
|
96
|
-
charDelay: runtime.charDelay ?? charDelay,
|
|
97
|
-
settled: true
|
|
98
|
-
};
|
|
99
|
-
else {
|
|
100
|
-
const lastBirthTs = births.length > 0 ? births.at(-1) ?? renderNow : renderNow;
|
|
101
|
-
meta = resolveBlockAnimationMeta({
|
|
102
|
-
currentCharDelay: charDelay,
|
|
103
|
-
fadeDuration: 180,
|
|
104
|
-
lastElapsedMs: renderNow - lastBirthTs,
|
|
105
|
-
previousCharDelay: runtime.charDelay,
|
|
106
|
-
state
|
|
107
|
-
});
|
|
108
|
-
runtime.settled = meta.settled;
|
|
109
|
-
}
|
|
110
|
-
runtime.charDelay = meta.charDelay;
|
|
111
|
-
blockAnimationMeta.set(block.startOffset, meta);
|
|
112
|
-
}
|
|
113
|
-
if (revealedNewChars) revealClock.lastTs = renderNow;
|
|
114
|
-
for (const key of runtimes.keys()) if (!alive.has(key)) {
|
|
115
|
-
runtimes.delete(key);
|
|
116
|
-
pluginsCache.delete(key);
|
|
117
|
-
}
|
|
118
|
-
return blockAnimationMeta;
|
|
119
|
-
};
|
|
120
|
-
const StreamdownBlocks = memo(({ content: smoothedContent, markdownOptions: rest }) => {
|
|
121
|
-
const { streamAnimationGranularity = "char" } = useMarkdownContext();
|
|
122
|
-
const profiler = useStreamdownProfiler();
|
|
123
|
-
const components = useMarkdownComponents();
|
|
124
|
-
const baseRehypePlugins = useStablePlugins(useMarkdownRehypePlugins());
|
|
125
|
-
const remarkPlugins = useStablePlugins(useMarkdownRemarkPlugins());
|
|
126
|
-
const generatedId = useId();
|
|
127
|
-
const processedContentResult = useMemo(() => {
|
|
128
|
-
const start = profiler ? getNow() : 0;
|
|
129
|
-
const value = remend(smoothedContent);
|
|
130
|
-
return {
|
|
131
|
-
durationMs: profiler ? getNow() - start : 0,
|
|
132
|
-
value
|
|
133
|
-
};
|
|
134
|
-
}, [profiler, smoothedContent]);
|
|
135
|
-
const processedContent = processedContentResult.value;
|
|
136
|
-
const blocksResult = useMemo(() => {
|
|
137
|
-
const start = profiler ? getNow() : 0;
|
|
138
|
-
const tokens = marked.lexer(processedContent);
|
|
139
|
-
let offset = 0;
|
|
140
|
-
const value = tokens.map((token) => {
|
|
141
|
-
const block = {
|
|
142
|
-
content: token.raw,
|
|
143
|
-
startOffset: offset
|
|
144
|
-
};
|
|
145
|
-
offset += token.raw.length;
|
|
146
|
-
return block;
|
|
147
|
-
});
|
|
148
|
-
return {
|
|
149
|
-
durationMs: profiler ? getNow() - start : 0,
|
|
150
|
-
value
|
|
151
|
-
};
|
|
152
|
-
}, [processedContent, profiler]);
|
|
153
|
-
const blocks = blocksResult.value;
|
|
154
|
-
const { getBlockState, charDelay } = useStreamQueue(blocks);
|
|
155
|
-
const blockRuntimesRef = useRef(/* @__PURE__ */ new Map());
|
|
156
|
-
const blockPluginsRef = useRef(/* @__PURE__ */ new Map());
|
|
157
|
-
const revealClockRef = useRef({ lastTs: 0 });
|
|
158
|
-
const renderNow = getNow();
|
|
159
|
-
const animationStart = profiler ? getNow() : 0;
|
|
160
|
-
const blockAnimationMeta = updateBlockAnimation({
|
|
161
|
-
blocks,
|
|
162
|
-
charDelay,
|
|
163
|
-
getBlockState,
|
|
164
|
-
pluginsCache: blockPluginsRef.current,
|
|
165
|
-
renderNow,
|
|
166
|
-
revealClock: revealClockRef.current,
|
|
167
|
-
runtimes: blockRuntimesRef.current
|
|
168
|
-
});
|
|
169
|
-
const blockAnimationDurationMs = profiler ? getNow() - animationStart : 0;
|
|
170
|
-
useEffect(() => {
|
|
171
|
-
if (!profiler) return;
|
|
172
|
-
profiler.recordCalculation({
|
|
173
|
-
durationMs: processedContentResult.durationMs,
|
|
174
|
-
name: "content-normalize",
|
|
175
|
-
textLength: processedContent.length
|
|
176
|
-
});
|
|
177
|
-
}, [
|
|
178
|
-
processedContent.length,
|
|
179
|
-
processedContentResult.durationMs,
|
|
180
|
-
profiler
|
|
181
|
-
]);
|
|
182
|
-
useEffect(() => {
|
|
183
|
-
if (!profiler) return;
|
|
184
|
-
profiler.recordCalculation({
|
|
185
|
-
durationMs: blocksResult.durationMs,
|
|
186
|
-
itemCount: blocks.length,
|
|
187
|
-
name: "block-lex",
|
|
188
|
-
textLength: processedContent.length
|
|
189
|
-
});
|
|
190
|
-
}, [
|
|
191
|
-
blocks.length,
|
|
192
|
-
blocksResult.durationMs,
|
|
193
|
-
processedContent.length,
|
|
194
|
-
profiler
|
|
195
|
-
]);
|
|
196
|
-
useEffect(() => {
|
|
197
|
-
if (!profiler) return;
|
|
198
|
-
profiler.recordCalculation({
|
|
199
|
-
durationMs: blockAnimationDurationMs,
|
|
200
|
-
itemCount: blocks.length,
|
|
201
|
-
name: "block-births",
|
|
202
|
-
textLength: processedContent.length
|
|
203
|
-
});
|
|
204
|
-
}, [
|
|
205
|
-
blockAnimationDurationMs,
|
|
206
|
-
blocks.length,
|
|
207
|
-
processedContent.length,
|
|
208
|
-
profiler
|
|
209
|
-
]);
|
|
210
|
-
const resolveBlockPlugins = (startOffset, settled) => {
|
|
211
|
-
if (settled) return baseRehypePlugins;
|
|
212
|
-
const cache = blockPluginsRef.current;
|
|
213
|
-
const entry = cache.get(startOffset);
|
|
214
|
-
if (entry && entry.base === baseRehypePlugins && entry.granularity === streamAnimationGranularity) return entry.value;
|
|
215
|
-
const runtime = blockRuntimesRef.current.get(startOffset);
|
|
216
|
-
const value = [...baseRehypePlugins, [rehypeStreamAnimated, {
|
|
217
|
-
fadeDuration: 180,
|
|
218
|
-
granularity: streamAnimationGranularity,
|
|
219
|
-
runtime
|
|
220
|
-
}]];
|
|
221
|
-
cache.set(startOffset, {
|
|
222
|
-
base: baseRehypePlugins,
|
|
223
|
-
granularity: streamAnimationGranularity,
|
|
224
|
-
value
|
|
225
|
-
});
|
|
226
|
-
return value;
|
|
227
|
-
};
|
|
228
|
-
const handleRootRender = useCallback((_, phase, actualDuration, baseDuration) => {
|
|
229
|
-
profiler?.recordRootCommit({
|
|
230
|
-
actualDuration,
|
|
231
|
-
baseDuration,
|
|
232
|
-
blockCount: blocks.length,
|
|
233
|
-
phase,
|
|
234
|
-
textLength: processedContent.length
|
|
235
|
-
});
|
|
236
|
-
}, [
|
|
237
|
-
blocks.length,
|
|
238
|
-
processedContent.length,
|
|
239
|
-
profiler
|
|
240
|
-
]);
|
|
241
|
-
const handleBlockRender = useCallback((id, phase, actualDuration, baseDuration) => {
|
|
242
|
-
if (!profiler) return;
|
|
243
|
-
const [, indexText, offsetText] = id.split(":");
|
|
244
|
-
const blockIndex = Number(indexText);
|
|
245
|
-
if (!Number.isFinite(blockIndex)) return;
|
|
246
|
-
const block = blocks[blockIndex];
|
|
247
|
-
if (!block) return;
|
|
248
|
-
profiler.recordBlockCommit({
|
|
249
|
-
actualDuration,
|
|
250
|
-
baseDuration,
|
|
251
|
-
blockChars: countChars(block.content),
|
|
252
|
-
blockIndex,
|
|
253
|
-
blockKey: offsetText ?? String(block.startOffset),
|
|
254
|
-
phase,
|
|
255
|
-
state: getBlockState(blockIndex)
|
|
256
|
-
});
|
|
257
|
-
}, [
|
|
258
|
-
blocks,
|
|
259
|
-
getBlockState,
|
|
260
|
-
profiler
|
|
261
|
-
]);
|
|
262
|
-
const content = /* @__PURE__ */ jsx("div", {
|
|
263
|
-
className: styles.animated,
|
|
264
|
-
children: blocks.map((block, index) => {
|
|
265
|
-
const animationMeta = blockAnimationMeta.get(block.startOffset);
|
|
266
|
-
if (!animationMeta) return null;
|
|
267
|
-
const plugins = resolveBlockPlugins(block.startOffset, animationMeta.settled);
|
|
268
|
-
const key = `${generatedId}-${block.startOffset}`;
|
|
269
|
-
if (!profiler) return /* @__PURE__ */ createElement(StreamdownBlock, {
|
|
270
|
-
...rest,
|
|
271
|
-
components,
|
|
272
|
-
key,
|
|
273
|
-
rehypePlugins: plugins,
|
|
274
|
-
remarkPlugins
|
|
275
|
-
}, block.content);
|
|
276
|
-
return /* @__PURE__ */ jsx(Profiler, {
|
|
277
|
-
id: `streamdown-block:${index}:${block.startOffset}`,
|
|
278
|
-
onRender: handleBlockRender,
|
|
279
|
-
children: /* @__PURE__ */ jsx(StreamdownBlock, {
|
|
280
|
-
...rest,
|
|
281
|
-
components,
|
|
282
|
-
rehypePlugins: plugins,
|
|
283
|
-
remarkPlugins,
|
|
284
|
-
children: block.content
|
|
285
|
-
})
|
|
286
|
-
}, key);
|
|
287
|
-
})
|
|
288
|
-
});
|
|
289
|
-
if (!profiler) return content;
|
|
290
|
-
return /* @__PURE__ */ jsx(Profiler, {
|
|
291
|
-
id: "streamdown-root",
|
|
292
|
-
onRender: handleRootRender,
|
|
293
|
-
children: content
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
StreamdownBlocks.displayName = "StreamdownBlocks";
|
|
297
11
|
const StreamdownRender = memo(({ children, ...rest }) => {
|
|
298
|
-
const { streamSmoothingPreset = "balanced" } = useMarkdownContext();
|
|
12
|
+
const { enableLatex = true, streamAnimationGranularity = "char", streamSmoothingPreset = "balanced" } = useMarkdownContext();
|
|
299
13
|
const escapedContent = useMarkdownContent(children || "");
|
|
300
|
-
const
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
14
|
+
const components = useMarkdownComponents();
|
|
15
|
+
const rehypePlugins = useMarkdownRehypePlugins();
|
|
16
|
+
const remarkPlugins = useMarkdownRemarkPlugins();
|
|
17
|
+
return /* @__PURE__ */ jsx(Streamdown, {
|
|
18
|
+
...rest,
|
|
19
|
+
components,
|
|
20
|
+
content: typeof escapedContent === "string" ? escapedContent : "",
|
|
21
|
+
granularity: streamAnimationGranularity,
|
|
22
|
+
latexGuard: enableLatex,
|
|
23
|
+
rehypePlugins,
|
|
24
|
+
remarkPlugins,
|
|
25
|
+
smoothing: streamSmoothingPreset
|
|
305
26
|
});
|
|
306
27
|
});
|
|
307
28
|
StreamdownRender.displayName = "StreamdownRender";
|
|
@@ -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 { 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 { useStableValue } from '@/hooks/useStableValue';\nimport { useMarkdownContext } from '@/Markdown/components/MarkdownProvider';\nimport {\n rehypeStreamAnimated,\n type StreamAnimatedRuntime,\n} from '@/Markdown/plugins/rehypeStreamAnimated';\nimport { useStreamdownProfiler } from '@/Markdown/streamProfiler';\nimport { type StreamAnimationGranularity } from '@/Markdown/type';\nimport { getNow } from '@/utils/getNow';\nimport { isDeepEqual } from '@/utils/isDeepEqual';\n\nimport { CachedMarkdown } from './CachedMarkdown';\nimport { type BlockAnimationMeta, resolveBlockAnimationMeta } from './streamAnimationMeta';\nimport { STREAM_FADE_DURATION, styles } from './style';\nimport { countChars, useSmoothStreamContent } from './useSmoothStreamContent';\nimport { type BlockInfo, type BlockState, useStreamQueue } from './useStreamQueue';\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 isDeepEqual(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 <CachedMarkdown {...rest}>{children}</CachedMarkdown>;\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\ninterface BlockRuntime extends StreamAnimatedRuntime {\n charCount: number;\n charDelay?: number;\n rawLength: number;\n settled: boolean;\n}\n\ninterface BlockPluginsCacheEntry {\n base: PluggableList;\n granularity: StreamAnimationGranularity;\n value: PluggableList;\n}\n\ninterface UpdateBlockAnimationArgs {\n blocks: BlockInfo[];\n charDelay: number;\n getBlockState: (index: number) => BlockState;\n pluginsCache: Map<number, BlockPluginsCacheEntry>;\n renderNow: number;\n revealClock: { lastTs: number };\n runtimes: Map<number, BlockRuntime>;\n}\n\nconst MIN_STREAM_CHAR_PACE_MS = 2;\nconst MAX_REVEAL_GAP_MS = 160;\n\n// Runs in the render phase: extends each visible block's birth timeline in\n// place and resolves its animation meta in one pass. Mutations are\n// idempotent for a given block content/length, so discarded or StrictMode\n// double renders re-derive the same state.\nconst updateBlockAnimation = ({\n blocks,\n charDelay,\n getBlockState,\n pluginsCache,\n renderNow,\n revealClock,\n runtimes,\n}: UpdateBlockAnimationArgs): Map<number, BlockAnimationMeta> => {\n const blockAnimationMeta = new Map<number, BlockAnimationMeta>();\n const alive = new Set<number>();\n let revealedNewChars = false;\n\n for (const [index, block] of blocks.entries()) {\n alive.add(block.startOffset);\n\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 const state = getBlockState(index);\n if (state === 'queued') continue;\n\n let runtime = runtimes.get(block.startOffset);\n if (!runtime) {\n runtime = { births: [], charCount: 0, rawLength: -1, settled: false, styles: [] };\n runtimes.set(block.startOffset, runtime);\n }\n\n if (runtime.rawLength !== block.content.length) {\n runtime.charCount = countChars(block.content);\n runtime.rawLength = block.content.length;\n }\n\n const blockCharCount = runtime.charCount;\n const births = runtime.births;\n\n if (births.length > blockCharCount) {\n // Block content shrunk (stream restart or upstream rewrite).\n births.length = blockCharCount;\n runtime.styles.length = blockCharCount;\n }\n\n if (births.length < blockCharCount) {\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 //\n // The streaming tail paces its stagger from the observed reveal-commit\n // gap instead of the queue's fixed charDelay: a commit's chars are\n // spread to land exactly until the next commit arrives, so the flow\n // stays per-char continuous no matter how far apart the throttled\n // commits are.\n const newChars = blockCharCount - births.length;\n let pace = charDelay;\n let cap = renderNow + STREAM_FADE_DURATION;\n if (state === 'streaming') {\n revealedNewChars = true;\n const gapMs = Math.min(Math.max(renderNow - revealClock.lastTs, 16), MAX_REVEAL_GAP_MS);\n pace = Math.min(charDelay, Math.max(gapMs / newChars, MIN_STREAM_CHAR_PACE_MS));\n cap = renderNow + gapMs + STREAM_FADE_DURATION;\n }\n for (let i = births.length; i < blockCharCount; i++) {\n const prevBirth = i > 0 ? births[i - 1] : renderNow - pace;\n const chained = prevBirth + pace;\n births.push(Math.min(cap, Math.max(chained, renderNow)));\n }\n }\n\n let meta: BlockAnimationMeta;\n if (runtime.settled) {\n // Settled is monotone: a revealed block's births are frozen, so once\n // its last fade completed it stays settled until the runtime is\n // pruned by a stream restart.\n meta = { charDelay: runtime.charDelay ?? charDelay, settled: true };\n } else {\n const lastBirthTs = births.length > 0 ? (births.at(-1) ?? renderNow) : renderNow;\n meta = resolveBlockAnimationMeta({\n currentCharDelay: charDelay,\n fadeDuration: STREAM_FADE_DURATION,\n lastElapsedMs: renderNow - lastBirthTs,\n previousCharDelay: runtime.charDelay,\n state,\n });\n runtime.settled = meta.settled;\n }\n runtime.charDelay = meta.charDelay;\n\n blockAnimationMeta.set(block.startOffset, meta);\n }\n\n if (revealedNewChars) {\n revealClock.lastTs = renderNow;\n }\n\n for (const key of runtimes.keys()) {\n if (!alive.has(key)) {\n runtimes.delete(key);\n pluginsCache.delete(key);\n }\n }\n\n return blockAnimationMeta;\n};\n\ninterface StreamdownBlocksProps {\n content: string;\n markdownOptions: Omit<Options, 'children'>;\n}\n\nconst StreamdownBlocks = memo<StreamdownBlocksProps>(\n ({ content: smoothedContent, markdownOptions: rest }) => {\n const { streamAnimationGranularity = 'char' } = useMarkdownContext();\n const profiler = useStreamdownProfiler();\n const components = useMarkdownComponents();\n const baseRehypePlugins = useStablePlugins(useMarkdownRehypePlugins());\n const remarkPlugins = useStablePlugins(useMarkdownRemarkPlugins());\n const generatedId = useId();\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 blockRuntimesRef = useRef<Map<number, BlockRuntime>>(new Map());\n const blockPluginsRef = useRef<Map<number, BlockPluginsCacheEntry>>(new Map());\n const revealClockRef = useRef<{ lastTs: number }>({ lastTs: 0 });\n\n const renderNow = getNow();\n\n const animationStart = profiler ? getNow() : 0;\n const blockAnimationMeta = updateBlockAnimation({\n blocks,\n charDelay,\n getBlockState,\n pluginsCache: blockPluginsRef.current,\n renderNow,\n revealClock: revealClockRef.current,\n runtimes: blockRuntimesRef.current,\n });\n const blockAnimationDurationMs = profiler ? getNow() - animationStart : 0;\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: blockAnimationDurationMs,\n itemCount: blocks.length,\n name: 'block-births',\n textLength: processedContent.length,\n });\n }, [blockAnimationDurationMs, blocks.length, processedContent.length, profiler]);\n\n const resolveBlockPlugins = (startOffset: number, settled: boolean): PluggableList => {\n if (settled) return baseRehypePlugins;\n\n const cache = blockPluginsRef.current;\n const entry = cache.get(startOffset);\n if (\n entry &&\n entry.base === baseRehypePlugins &&\n entry.granularity === streamAnimationGranularity\n ) {\n return entry.value;\n }\n\n const runtime = blockRuntimesRef.current.get(startOffset);\n const value: PluggableList = [\n ...baseRehypePlugins,\n [\n rehypeStreamAnimated,\n {\n fadeDuration: STREAM_FADE_DURATION,\n granularity: streamAnimationGranularity,\n runtime,\n },\n ],\n ];\n cache.set(startOffset, {\n base: baseRehypePlugins,\n granularity: streamAnimationGranularity,\n value,\n });\n return value;\n };\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 animationMeta = blockAnimationMeta.get(block.startOffset);\n if (!animationMeta) return null;\n\n const plugins = resolveBlockPlugins(block.startOffset, animationMeta.settled);\n const key = `${generatedId}-${block.startOffset}`;\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 <StreamdownBlock\n {...rest}\n components={components}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\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);\n\nStreamdownBlocks.displayName = 'StreamdownBlocks';\n\n// The outer component absorbs the upstream per-chunk prop churn: every\n// streamed chunk re-renders it, but it only feeds the smoother and renders\n// a memoized child keyed on the smoother's output — so the expensive block\n// pipeline runs per reveal commit, not per chunk AND per commit.\nexport const StreamdownRender = memo<Options>(({ children, ...rest }) => {\n const { streamSmoothingPreset = 'balanced' } = useMarkdownContext();\n const escapedContent = useMarkdownContent(children || '');\n const smoothedContent = useSmoothStreamContent(\n typeof escapedContent === 'string' ? escapedContent : '',\n { preset: streamSmoothingPreset },\n );\n const markdownOptions = useStableValue(rest);\n\n return <StreamdownBlocks content={smoothedContent} markdownOptions={markdownOptions} />;\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 { Streamdown } from '@lobehub/streamdown';\nimport { memo } from 'react';\nimport { type Options } from 'react-markdown';\n\nimport {\n useMarkdownComponents,\n useMarkdownContent,\n useMarkdownRehypePlugins,\n useMarkdownRemarkPlugins,\n} from '@/hooks/useMarkdown';\nimport { useMarkdownContext } from '@/Markdown/components/MarkdownProvider';\n\nexport const StreamdownRender = memo<Options>(({ children, ...rest }) => {\n const {\n enableLatex = true,\n streamAnimationGranularity = 'char',\n streamSmoothingPreset = 'balanced',\n } = useMarkdownContext();\n const escapedContent = useMarkdownContent(children || '');\n const components = useMarkdownComponents();\n const rehypePlugins = useMarkdownRehypePlugins();\n const remarkPlugins = useMarkdownRemarkPlugins();\n\n return (\n <Streamdown\n {...rest}\n components={components}\n content={typeof escapedContent === 'string' ? escapedContent : ''}\n granularity={streamAnimationGranularity}\n latexGuard={enableLatex}\n rehypePlugins={rehypePlugins}\n remarkPlugins={remarkPlugins}\n smoothing={streamSmoothingPreset}\n />\n );\n});\n\nStreamdownRender.displayName = 'StreamdownRender';\n\nexport default StreamdownRender;\n"],"mappings":";;;;;;;;;;AAcA,MAAa,mBAAmB,MAAe,EAAE,UAAU,GAAG,WAAW;CACvE,MAAM,EACJ,cAAc,MACd,6BAA6B,QAC7B,wBAAwB,eACtB,mBAAmB;CACvB,MAAM,iBAAiB,mBAAmB,YAAY,EAAE;CACxD,MAAM,aAAa,sBAAsB;CACzC,MAAM,gBAAgB,yBAAyB;CAC/C,MAAM,gBAAgB,yBAAyB;CAE/C,OACE,oBAAC,YAAD;EACE,GAAI;EACQ;EACZ,SAAS,OAAO,mBAAmB,WAAW,iBAAiB;EAC/D,aAAa;EACb,YAAY;EACG;EACA;EACf,WAAW;CACZ,CAAA;AAEL,CAAC;AAED,iBAAiB,cAAc"}
|
package/es/Markdown/type.d.mts
CHANGED
|
@@ -12,6 +12,7 @@ import { VideoProps } from "../mdx/mdxComponents/Video.mjs";
|
|
|
12
12
|
import "../mdx/index.mjs";
|
|
13
13
|
import { CSSProperties, ElementType, FC, ReactNode, Ref } from "react";
|
|
14
14
|
import { AnchorProps } from "antd";
|
|
15
|
+
import { StreamAnimationGranularity, StreamSmoothingPreset } from "@lobehub/streamdown";
|
|
15
16
|
import { Components, Options } from "react-markdown";
|
|
16
17
|
import { Pluggable } from "unified";
|
|
17
18
|
//#region src/Markdown/type.d.ts
|
|
@@ -23,8 +24,6 @@ interface TypographyProps extends DivProps {
|
|
|
23
24
|
marginMultiple?: number;
|
|
24
25
|
ref?: Ref<HTMLDivElement>;
|
|
25
26
|
}
|
|
26
|
-
type StreamSmoothingPreset = 'realtime' | 'balanced' | 'silky';
|
|
27
|
-
type StreamAnimationGranularity = 'char' | 'word';
|
|
28
27
|
interface SyntaxMarkdownProps {
|
|
29
28
|
allowHtml?: boolean;
|
|
30
29
|
allowHtmlList?: ElementType[];
|
|
@@ -69,5 +68,5 @@ interface MarkdownProps extends SyntaxMarkdownProps, Omit<TypographyProps, 'chil
|
|
|
69
68
|
style?: CSSProperties;
|
|
70
69
|
}
|
|
71
70
|
//#endregion
|
|
72
|
-
export { MarkdownProps, StreamAnimationGranularity, StreamSmoothingPreset, SyntaxMarkdownProps, TypographyProps };
|
|
71
|
+
export { MarkdownProps, type StreamAnimationGranularity, type StreamSmoothingPreset, SyntaxMarkdownProps, TypographyProps };
|
|
73
72
|
//# sourceMappingURL=type.d.mts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Icon from "../../Icon/Icon.mjs";
|
|
2
2
|
import { preventDefaultAndStopPropagation } from "../../utils/dom.mjs";
|
|
3
3
|
import Switch from "../Switch/Switch.mjs";
|
|
4
|
+
import { useLayerZIndex } from "../zIndex/useLayerZIndex.mjs";
|
|
4
5
|
import { styles } from "../DropdownMenu/sharedStyle.mjs";
|
|
5
6
|
import { SubmenuArrowIcon } from "../SubmenuArrowIcon.mjs";
|
|
6
7
|
import { getItemKey, getItemLabel, hasAnyIcon, hasCheckboxAndIcon, renderIcon } from "../../Menu/renderUtils.mjs";
|
|
@@ -12,6 +13,28 @@ import { cx } from "antd-style";
|
|
|
12
13
|
import { Check } from "lucide-react";
|
|
13
14
|
import { ContextMenu } from "@base-ui/react/context-menu";
|
|
14
15
|
//#region src/base-ui/ContextMenu/renderItems.tsx
|
|
16
|
+
/**
|
|
17
|
+
* Submenu positioners are portaled outside the root menu, so they must allocate
|
|
18
|
+
* their own layer z-index (same pattern as DropdownMenu) rather than relying on
|
|
19
|
+
* the fixed CSS fallback that can fall behind after repeated root openings (#608).
|
|
20
|
+
*/
|
|
21
|
+
const ContextMenuSubmenuPositioner = memo(({ style, ...rest }) => {
|
|
22
|
+
const explicitZIndex = typeof style !== "function" && style?.zIndex != null && typeof style.zIndex === "number" ? style.zIndex : void 0;
|
|
23
|
+
const { zIndex, ref: zRef } = useLayerZIndex("floating", explicitZIndex);
|
|
24
|
+
const resolvedStyle = typeof style === "function" ? (state) => ({
|
|
25
|
+
zIndex,
|
|
26
|
+
...style(state)
|
|
27
|
+
}) : {
|
|
28
|
+
zIndex,
|
|
29
|
+
...style
|
|
30
|
+
};
|
|
31
|
+
return /* @__PURE__ */ jsx(ContextMenu.Positioner, {
|
|
32
|
+
...rest,
|
|
33
|
+
ref: zRef,
|
|
34
|
+
style: resolvedStyle
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
ContextMenuSubmenuPositioner.displayName = "ContextMenuSubmenuPositioner";
|
|
15
38
|
const EmptyMenuItem = memo(() => {
|
|
16
39
|
const { t } = useTranslation(common_default);
|
|
17
40
|
return /* @__PURE__ */ jsx(ContextMenu.Item, {
|
|
@@ -211,7 +234,7 @@ const renderContextMenuItems = (items, keyPath = [], options) => {
|
|
|
211
234
|
reserveIconSpace,
|
|
212
235
|
submenu: true
|
|
213
236
|
})
|
|
214
|
-
}), /* @__PURE__ */ jsx(ContextMenu.Portal, { children: /* @__PURE__ */ jsx(
|
|
237
|
+
}), /* @__PURE__ */ jsx(ContextMenu.Portal, { children: /* @__PURE__ */ jsx(ContextMenuSubmenuPositioner, {
|
|
215
238
|
alignOffset: -4,
|
|
216
239
|
className: styles.positioner,
|
|
217
240
|
"data-submenu": "",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderItems.mjs","names":["common"],"sources":["../../../src/base-ui/ContextMenu/renderItems.tsx"],"sourcesContent":["import { ContextMenu } from '@base-ui/react/context-menu';\nimport { cx } from 'antd-style';\nimport { Check } from 'lucide-react';\nimport { type MenuInfo } from 'rc-menu/es/interface';\nimport {\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n} from 'react';\nimport { memo, useCallback, useState } from 'react';\n\nimport { styles } from '@/base-ui/DropdownMenu/sharedStyle';\nimport { SubmenuArrowIcon } from '@/base-ui/SubmenuArrowIcon';\nimport Switch from '@/base-ui/Switch';\nimport common from '@/i18n/resources/en/common';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport Icon from '@/Icon';\nimport {\n type BaseMenuItemGroupType,\n type BaseSubMenuType,\n getItemKey,\n getItemLabel,\n hasAnyIcon,\n hasCheckboxAndIcon,\n type MenuDividerType,\n type MenuItemType,\n renderIcon,\n type RenderItemContentOptions,\n type RenderOptions,\n} from '@/Menu';\nimport { preventDefaultAndStopPropagation } from '@/utils/dom';\n\nimport {\n type ContextMenuCheckboxItem,\n type ContextMenuItem,\n type ContextMenuSwitchItem,\n} from './type';\n\nexport type { IconAlign, IconSpaceMode } from '@/Menu';\n\nconst EmptyMenuItem = memo(() => {\n const { t } = useTranslation(common);\n return (\n <ContextMenu.Item disabled className={cx(styles.item, styles.empty)}>\n <div className={styles.itemContent}>\n <span className={styles.label}>{t('common.empty')}</span>\n </div>\n </ContextMenu.Item>\n );\n});\n\nEmptyMenuItem.displayName = 'EmptyMenuItem';\n\ninterface ContextMenuSwitchItemInternalProps {\n checked?: boolean;\n children: ReactNode;\n closeOnClick?: boolean;\n danger?: boolean;\n defaultChecked?: boolean;\n disabled?: boolean;\n label?: string;\n onCheckedChange?: (checked: boolean) => void;\n}\n\nconst ContextMenuSwitchItemInternal = ({\n checked: checkedProp,\n children,\n closeOnClick = false,\n danger,\n defaultChecked,\n disabled,\n label,\n onCheckedChange,\n}: ContextMenuSwitchItemInternalProps) => {\n const [internalChecked, setInternalChecked] = useState(defaultChecked ?? false);\n const isControlled = checkedProp !== undefined;\n const checked = isControlled ? checkedProp : internalChecked;\n\n const handleCheckedChange = useCallback(\n (newChecked: boolean) => {\n if (!isControlled) {\n setInternalChecked(newChecked);\n }\n onCheckedChange?.(newChecked);\n },\n [isControlled, onCheckedChange],\n );\n\n return (\n <ContextMenu.Item\n className={cx(styles.item, danger && styles.danger)}\n closeOnClick={closeOnClick}\n disabled={disabled}\n label={label}\n onClick={(e) => {\n e.preventDefault();\n if (!disabled) {\n handleCheckedChange(!checked);\n }\n }}\n >\n {children}\n <span\n style={{ display: 'inline-flex', marginInlineStart: 16 }}\n onFocus={(e) => e.stopPropagation()}\n >\n <Switch\n checked={checked}\n disabled={disabled}\n size=\"small\"\n tabIndex={-1}\n onChange={handleCheckedChange}\n onClick={(_, e) => e.stopPropagation()}\n />\n </span>\n </ContextMenu.Item>\n );\n};\n\nconst renderItemContent = (\n item: MenuItemType | BaseSubMenuType | ContextMenuCheckboxItem | ContextMenuSwitchItem,\n options?: RenderItemContentOptions,\n iconNode?: ReactNode,\n) => {\n const label = getItemLabel(item);\n const desc = 'desc' in item ? item.desc : undefined;\n const extra = 'extra' in item ? item.extra : undefined;\n const indicatorOnRight = options?.indicatorOnRight;\n const alignStart = Boolean(desc) && options?.iconAlign === 'start';\n const hasCustomIcon = iconNode !== undefined && !indicatorOnRight;\n const hasIcon = hasCustomIcon ? Boolean(iconNode) : Boolean(item.icon);\n const shouldRenderIcon = hasCustomIcon\n ? Boolean(options?.reserveIconSpace || iconNode)\n : Boolean(hasIcon || options?.reserveIconSpace);\n\n const labelNode = desc ? (\n <div className={styles.labelGroup}>\n <span className={styles.label}>{label}</span>\n <span className={styles.desc}>{desc}</span>\n </div>\n ) : (\n <span className={styles.label}>{label}</span>\n );\n\n return (\n <div className={cx(styles.itemContent, alignStart && styles.itemContentAlignStart)}>\n {shouldRenderIcon ? (\n <span\n aria-hidden={!hasIcon}\n className={cx(styles.icon, alignStart && styles.iconAlignStart)}\n >\n {hasCustomIcon ? iconNode : hasIcon ? renderIcon(item.icon, 'small') : null}\n </span>\n ) : null}\n {labelNode}\n {extra ? <span className={styles.extra}>{extra}</span> : null}\n {indicatorOnRight && iconNode ? iconNode : null}\n {options?.submenu ? (\n <span className={styles.submenuArrow}>\n <SubmenuArrowIcon />\n </span>\n ) : null}\n </div>\n );\n};\n\nconst invokeItemClick = (\n item: MenuItemType,\n keyPath: string[],\n event: ReactMouseEvent<HTMLElement> | ReactKeyboardEvent<HTMLElement>,\n) => {\n if (!item.onClick) return;\n const key = item.key ?? keyPath.at(-1) ?? '';\n const info: MenuInfo = {\n domEvent: event,\n item: event.currentTarget as MenuInfo['item'],\n key: String(key),\n keyPath,\n };\n item.onClick(info);\n};\n\nexport const renderContextMenuItems = (\n items: ContextMenuItem[],\n keyPath: string[] = [],\n options?: RenderOptions,\n): ReactNode[] => {\n const iconAlign = options?.iconAlign;\n const iconSpaceMode = options?.iconSpaceMode ?? 'global';\n const reserveIconSpace =\n options?.reserveIconSpace ?? hasAnyIcon(items, iconSpaceMode === 'global');\n const indicatorOnRight = options?.indicatorOnRight ?? hasCheckboxAndIcon(items);\n\n return items.map((item, index) => {\n if (!item) return null;\n\n const fallbackKey = `${keyPath.join('-') || 'root'}-${index}`;\n const itemKey = getItemKey(item, fallbackKey);\n const nextKeyPath = [...keyPath, String(itemKey)];\n\n if ((item as ContextMenuCheckboxItem).type === 'checkbox') {\n const checkboxItem = item as ContextMenuCheckboxItem;\n const label = getItemLabel(checkboxItem);\n const labelText = typeof label === 'string' ? label : undefined;\n const isDanger = Boolean(checkboxItem.danger);\n const indicator = (\n <ContextMenu.CheckboxItemIndicator>\n <Icon icon={Check} size={'small'} />\n </ContextMenu.CheckboxItemIndicator>\n );\n\n return (\n <ContextMenu.CheckboxItem\n checked={checkboxItem.checked}\n className={cx(styles.item, isDanger && styles.danger)}\n closeOnClick={checkboxItem.closeOnClick}\n defaultChecked={checkboxItem.defaultChecked}\n disabled={checkboxItem.disabled}\n key={itemKey}\n label={labelText}\n onCheckedChange={(checked) => checkboxItem.onCheckedChange?.(checked)}\n >\n {renderItemContent(\n checkboxItem,\n { iconAlign, indicatorOnRight, reserveIconSpace },\n indicator,\n )}\n </ContextMenu.CheckboxItem>\n );\n }\n\n if ((item as ContextMenuSwitchItem).type === 'switch') {\n const switchItem = item as ContextMenuSwitchItem;\n const label = getItemLabel(switchItem);\n const labelText = typeof label === 'string' ? label : undefined;\n const isDanger = Boolean(switchItem.danger);\n\n return (\n <ContextMenuSwitchItemInternal\n checked={switchItem.checked}\n closeOnClick={switchItem.closeOnClick}\n danger={isDanger}\n defaultChecked={switchItem.defaultChecked}\n disabled={switchItem.disabled}\n key={itemKey}\n label={labelText}\n onCheckedChange={switchItem.onCheckedChange}\n >\n {renderItemContent(switchItem, { iconAlign, reserveIconSpace })}\n </ContextMenuSwitchItemInternal>\n );\n }\n\n if ((item as MenuDividerType).type === 'divider') {\n return <ContextMenu.Separator className={styles.separator} key={itemKey} />;\n }\n\n if ((item as BaseMenuItemGroupType).type === 'group') {\n const group = item as BaseMenuItemGroupType;\n const groupReserveIconSpace =\n iconSpaceMode === 'group'\n ? group.children\n ? hasAnyIcon(group.children)\n : false\n : reserveIconSpace;\n const groupIndicatorOnRight = group.children ? hasCheckboxAndIcon(group.children) : false;\n return (\n <ContextMenu.Group key={itemKey}>\n {group.label ? (\n <ContextMenu.GroupLabel className={styles.groupLabel}>\n {group.label}\n </ContextMenu.GroupLabel>\n ) : null}\n {group.children\n ? renderContextMenuItems(group.children, nextKeyPath, {\n iconAlign,\n iconSpaceMode,\n indicatorOnRight: groupIndicatorOnRight,\n reserveIconSpace: groupReserveIconSpace,\n })\n : null}\n </ContextMenu.Group>\n );\n }\n\n if (\n (item as BaseSubMenuType).type === 'submenu' ||\n ('children' in item && (item as BaseSubMenuType).children)\n ) {\n const submenu = item as BaseSubMenuType;\n const label = getItemLabel(submenu);\n const labelText = typeof label === 'string' ? label : undefined;\n const isDanger = 'danger' in submenu && Boolean(submenu.danger);\n const submenuHasSlots = submenu.header != null || submenu.footer != null;\n const submenuBody =\n submenu.children && submenu.children.length > 0 ? (\n renderContextMenuItems(submenu.children, nextKeyPath, {\n iconAlign,\n iconSpaceMode,\n })\n ) : (\n <EmptyMenuItem />\n );\n\n return (\n <ContextMenu.SubmenuRoot\n defaultOpen={submenu.defaultOpen}\n key={itemKey}\n open={submenu.open}\n onOpenChange={submenu.onOpenChange}\n >\n <ContextMenu.SubmenuTrigger\n {...submenu.triggerProps}\n className={cx(styles.item, isDanger && styles.danger)}\n closeDelay={submenu.closeDelay}\n delay={submenu.delay}\n disabled={submenu.disabled}\n label={labelText}\n openOnHover={submenu.openOnHover}\n onClick={submenu.onClick}\n >\n {renderItemContent(submenu, {\n iconAlign,\n reserveIconSpace,\n submenu: true,\n })}\n </ContextMenu.SubmenuTrigger>\n <ContextMenu.Portal>\n <ContextMenu.Positioner\n alignOffset={-4}\n className={styles.positioner}\n data-submenu=\"\"\n sideOffset={-1}\n onContextMenu={preventDefaultAndStopPropagation}\n >\n <ContextMenu.Popup\n className={submenuHasSlots ? cx(styles.popup, styles.popupWithSlots) : styles.popup}\n >\n {submenu.header == null ? null : (\n <div className={styles.header}>{submenu.header}</div>\n )}\n {submenuHasSlots ? (\n <div className={styles.slotViewport}>{submenuBody}</div>\n ) : (\n submenuBody\n )}\n {submenu.footer == null ? null : (\n <div className={styles.footer}>{submenu.footer}</div>\n )}\n </ContextMenu.Popup>\n </ContextMenu.Positioner>\n </ContextMenu.Portal>\n </ContextMenu.SubmenuRoot>\n );\n }\n\n const menuItem = item as MenuItemType;\n const label = getItemLabel(menuItem);\n const labelText = typeof label === 'string' ? label : undefined;\n const isDanger = 'danger' in menuItem && Boolean(menuItem.danger);\n\n return (\n <ContextMenu.Item\n className={cx(styles.item, isDanger && styles.danger)}\n closeOnClick={menuItem.closeOnClick}\n disabled={menuItem.disabled}\n key={itemKey}\n label={labelText}\n onClick={(event) => invokeItemClick(menuItem, nextKeyPath, event)}\n >\n {renderItemContent(menuItem, { iconAlign, reserveIconSpace })}\n </ContextMenu.Item>\n );\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"renderItems.mjs","names":["common"],"sources":["../../../src/base-ui/ContextMenu/renderItems.tsx"],"sourcesContent":["import { ContextMenu } from '@base-ui/react/context-menu';\nimport { cx } from 'antd-style';\nimport { Check } from 'lucide-react';\nimport { type MenuInfo } from 'rc-menu/es/interface';\nimport {\n type ComponentProps,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n} from 'react';\nimport { memo, useCallback, useState } from 'react';\n\nimport { styles } from '@/base-ui/DropdownMenu/sharedStyle';\nimport { SubmenuArrowIcon } from '@/base-ui/SubmenuArrowIcon';\nimport Switch from '@/base-ui/Switch';\nimport common from '@/i18n/resources/en/common';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport Icon from '@/Icon';\nimport {\n type BaseMenuItemGroupType,\n type BaseSubMenuType,\n getItemKey,\n getItemLabel,\n hasAnyIcon,\n hasCheckboxAndIcon,\n type MenuDividerType,\n type MenuItemType,\n renderIcon,\n type RenderItemContentOptions,\n type RenderOptions,\n} from '@/Menu';\nimport { preventDefaultAndStopPropagation } from '@/utils/dom';\n\nimport { useLayerZIndex } from '../zIndex';\nimport {\n type ContextMenuCheckboxItem,\n type ContextMenuItem,\n type ContextMenuSwitchItem,\n} from './type';\n\ntype ContextMenuPositionerProps = ComponentProps<typeof ContextMenu.Positioner>;\n\n/**\n * Submenu positioners are portaled outside the root menu, so they must allocate\n * their own layer z-index (same pattern as DropdownMenu) rather than relying on\n * the fixed CSS fallback that can fall behind after repeated root openings (#608).\n */\nconst ContextMenuSubmenuPositioner = memo(({ style, ...rest }: ContextMenuPositionerProps) => {\n const explicitZIndex =\n typeof style !== 'function' && style?.zIndex != null && typeof style.zIndex === 'number'\n ? style.zIndex\n : undefined;\n const { zIndex, ref: zRef } = useLayerZIndex<HTMLDivElement>('floating', explicitZIndex);\n\n const resolvedStyle =\n typeof style === 'function'\n ? (state: any) => ({ zIndex, ...style(state) })\n : { zIndex, ...style };\n\n return <ContextMenu.Positioner {...rest} ref={zRef as any} style={resolvedStyle} />;\n});\n\nContextMenuSubmenuPositioner.displayName = 'ContextMenuSubmenuPositioner';\n\nexport type { IconAlign, IconSpaceMode } from '@/Menu';\n\nconst EmptyMenuItem = memo(() => {\n const { t } = useTranslation(common);\n return (\n <ContextMenu.Item disabled className={cx(styles.item, styles.empty)}>\n <div className={styles.itemContent}>\n <span className={styles.label}>{t('common.empty')}</span>\n </div>\n </ContextMenu.Item>\n );\n});\n\nEmptyMenuItem.displayName = 'EmptyMenuItem';\n\ninterface ContextMenuSwitchItemInternalProps {\n checked?: boolean;\n children: ReactNode;\n closeOnClick?: boolean;\n danger?: boolean;\n defaultChecked?: boolean;\n disabled?: boolean;\n label?: string;\n onCheckedChange?: (checked: boolean) => void;\n}\n\nconst ContextMenuSwitchItemInternal = ({\n checked: checkedProp,\n children,\n closeOnClick = false,\n danger,\n defaultChecked,\n disabled,\n label,\n onCheckedChange,\n}: ContextMenuSwitchItemInternalProps) => {\n const [internalChecked, setInternalChecked] = useState(defaultChecked ?? false);\n const isControlled = checkedProp !== undefined;\n const checked = isControlled ? checkedProp : internalChecked;\n\n const handleCheckedChange = useCallback(\n (newChecked: boolean) => {\n if (!isControlled) {\n setInternalChecked(newChecked);\n }\n onCheckedChange?.(newChecked);\n },\n [isControlled, onCheckedChange],\n );\n\n return (\n <ContextMenu.Item\n className={cx(styles.item, danger && styles.danger)}\n closeOnClick={closeOnClick}\n disabled={disabled}\n label={label}\n onClick={(e) => {\n e.preventDefault();\n if (!disabled) {\n handleCheckedChange(!checked);\n }\n }}\n >\n {children}\n <span\n style={{ display: 'inline-flex', marginInlineStart: 16 }}\n onFocus={(e) => e.stopPropagation()}\n >\n <Switch\n checked={checked}\n disabled={disabled}\n size=\"small\"\n tabIndex={-1}\n onChange={handleCheckedChange}\n onClick={(_, e) => e.stopPropagation()}\n />\n </span>\n </ContextMenu.Item>\n );\n};\n\nconst renderItemContent = (\n item: MenuItemType | BaseSubMenuType | ContextMenuCheckboxItem | ContextMenuSwitchItem,\n options?: RenderItemContentOptions,\n iconNode?: ReactNode,\n) => {\n const label = getItemLabel(item);\n const desc = 'desc' in item ? item.desc : undefined;\n const extra = 'extra' in item ? item.extra : undefined;\n const indicatorOnRight = options?.indicatorOnRight;\n const alignStart = Boolean(desc) && options?.iconAlign === 'start';\n const hasCustomIcon = iconNode !== undefined && !indicatorOnRight;\n const hasIcon = hasCustomIcon ? Boolean(iconNode) : Boolean(item.icon);\n const shouldRenderIcon = hasCustomIcon\n ? Boolean(options?.reserveIconSpace || iconNode)\n : Boolean(hasIcon || options?.reserveIconSpace);\n\n const labelNode = desc ? (\n <div className={styles.labelGroup}>\n <span className={styles.label}>{label}</span>\n <span className={styles.desc}>{desc}</span>\n </div>\n ) : (\n <span className={styles.label}>{label}</span>\n );\n\n return (\n <div className={cx(styles.itemContent, alignStart && styles.itemContentAlignStart)}>\n {shouldRenderIcon ? (\n <span\n aria-hidden={!hasIcon}\n className={cx(styles.icon, alignStart && styles.iconAlignStart)}\n >\n {hasCustomIcon ? iconNode : hasIcon ? renderIcon(item.icon, 'small') : null}\n </span>\n ) : null}\n {labelNode}\n {extra ? <span className={styles.extra}>{extra}</span> : null}\n {indicatorOnRight && iconNode ? iconNode : null}\n {options?.submenu ? (\n <span className={styles.submenuArrow}>\n <SubmenuArrowIcon />\n </span>\n ) : null}\n </div>\n );\n};\n\nconst invokeItemClick = (\n item: MenuItemType,\n keyPath: string[],\n event: ReactMouseEvent<HTMLElement> | ReactKeyboardEvent<HTMLElement>,\n) => {\n if (!item.onClick) return;\n const key = item.key ?? keyPath.at(-1) ?? '';\n const info: MenuInfo = {\n domEvent: event,\n item: event.currentTarget as MenuInfo['item'],\n key: String(key),\n keyPath,\n };\n item.onClick(info);\n};\n\nexport const renderContextMenuItems = (\n items: ContextMenuItem[],\n keyPath: string[] = [],\n options?: RenderOptions,\n): ReactNode[] => {\n const iconAlign = options?.iconAlign;\n const iconSpaceMode = options?.iconSpaceMode ?? 'global';\n const reserveIconSpace =\n options?.reserveIconSpace ?? hasAnyIcon(items, iconSpaceMode === 'global');\n const indicatorOnRight = options?.indicatorOnRight ?? hasCheckboxAndIcon(items);\n\n return items.map((item, index) => {\n if (!item) return null;\n\n const fallbackKey = `${keyPath.join('-') || 'root'}-${index}`;\n const itemKey = getItemKey(item, fallbackKey);\n const nextKeyPath = [...keyPath, String(itemKey)];\n\n if ((item as ContextMenuCheckboxItem).type === 'checkbox') {\n const checkboxItem = item as ContextMenuCheckboxItem;\n const label = getItemLabel(checkboxItem);\n const labelText = typeof label === 'string' ? label : undefined;\n const isDanger = Boolean(checkboxItem.danger);\n const indicator = (\n <ContextMenu.CheckboxItemIndicator>\n <Icon icon={Check} size={'small'} />\n </ContextMenu.CheckboxItemIndicator>\n );\n\n return (\n <ContextMenu.CheckboxItem\n checked={checkboxItem.checked}\n className={cx(styles.item, isDanger && styles.danger)}\n closeOnClick={checkboxItem.closeOnClick}\n defaultChecked={checkboxItem.defaultChecked}\n disabled={checkboxItem.disabled}\n key={itemKey}\n label={labelText}\n onCheckedChange={(checked) => checkboxItem.onCheckedChange?.(checked)}\n >\n {renderItemContent(\n checkboxItem,\n { iconAlign, indicatorOnRight, reserveIconSpace },\n indicator,\n )}\n </ContextMenu.CheckboxItem>\n );\n }\n\n if ((item as ContextMenuSwitchItem).type === 'switch') {\n const switchItem = item as ContextMenuSwitchItem;\n const label = getItemLabel(switchItem);\n const labelText = typeof label === 'string' ? label : undefined;\n const isDanger = Boolean(switchItem.danger);\n\n return (\n <ContextMenuSwitchItemInternal\n checked={switchItem.checked}\n closeOnClick={switchItem.closeOnClick}\n danger={isDanger}\n defaultChecked={switchItem.defaultChecked}\n disabled={switchItem.disabled}\n key={itemKey}\n label={labelText}\n onCheckedChange={switchItem.onCheckedChange}\n >\n {renderItemContent(switchItem, { iconAlign, reserveIconSpace })}\n </ContextMenuSwitchItemInternal>\n );\n }\n\n if ((item as MenuDividerType).type === 'divider') {\n return <ContextMenu.Separator className={styles.separator} key={itemKey} />;\n }\n\n if ((item as BaseMenuItemGroupType).type === 'group') {\n const group = item as BaseMenuItemGroupType;\n const groupReserveIconSpace =\n iconSpaceMode === 'group'\n ? group.children\n ? hasAnyIcon(group.children)\n : false\n : reserveIconSpace;\n const groupIndicatorOnRight = group.children ? hasCheckboxAndIcon(group.children) : false;\n return (\n <ContextMenu.Group key={itemKey}>\n {group.label ? (\n <ContextMenu.GroupLabel className={styles.groupLabel}>\n {group.label}\n </ContextMenu.GroupLabel>\n ) : null}\n {group.children\n ? renderContextMenuItems(group.children, nextKeyPath, {\n iconAlign,\n iconSpaceMode,\n indicatorOnRight: groupIndicatorOnRight,\n reserveIconSpace: groupReserveIconSpace,\n })\n : null}\n </ContextMenu.Group>\n );\n }\n\n if (\n (item as BaseSubMenuType).type === 'submenu' ||\n ('children' in item && (item as BaseSubMenuType).children)\n ) {\n const submenu = item as BaseSubMenuType;\n const label = getItemLabel(submenu);\n const labelText = typeof label === 'string' ? label : undefined;\n const isDanger = 'danger' in submenu && Boolean(submenu.danger);\n const submenuHasSlots = submenu.header != null || submenu.footer != null;\n const submenuBody =\n submenu.children && submenu.children.length > 0 ? (\n renderContextMenuItems(submenu.children, nextKeyPath, {\n iconAlign,\n iconSpaceMode,\n })\n ) : (\n <EmptyMenuItem />\n );\n\n return (\n <ContextMenu.SubmenuRoot\n defaultOpen={submenu.defaultOpen}\n key={itemKey}\n open={submenu.open}\n onOpenChange={submenu.onOpenChange}\n >\n <ContextMenu.SubmenuTrigger\n {...submenu.triggerProps}\n className={cx(styles.item, isDanger && styles.danger)}\n closeDelay={submenu.closeDelay}\n delay={submenu.delay}\n disabled={submenu.disabled}\n label={labelText}\n openOnHover={submenu.openOnHover}\n onClick={submenu.onClick}\n >\n {renderItemContent(submenu, {\n iconAlign,\n reserveIconSpace,\n submenu: true,\n })}\n </ContextMenu.SubmenuTrigger>\n <ContextMenu.Portal>\n <ContextMenuSubmenuPositioner\n alignOffset={-4}\n className={styles.positioner}\n data-submenu=\"\"\n sideOffset={-1}\n onContextMenu={preventDefaultAndStopPropagation}\n >\n <ContextMenu.Popup\n className={submenuHasSlots ? cx(styles.popup, styles.popupWithSlots) : styles.popup}\n >\n {submenu.header == null ? null : (\n <div className={styles.header}>{submenu.header}</div>\n )}\n {submenuHasSlots ? (\n <div className={styles.slotViewport}>{submenuBody}</div>\n ) : (\n submenuBody\n )}\n {submenu.footer == null ? null : (\n <div className={styles.footer}>{submenu.footer}</div>\n )}\n </ContextMenu.Popup>\n </ContextMenuSubmenuPositioner>\n </ContextMenu.Portal>\n </ContextMenu.SubmenuRoot>\n );\n }\n\n const menuItem = item as MenuItemType;\n const label = getItemLabel(menuItem);\n const labelText = typeof label === 'string' ? label : undefined;\n const isDanger = 'danger' in menuItem && Boolean(menuItem.danger);\n\n return (\n <ContextMenu.Item\n className={cx(styles.item, isDanger && styles.danger)}\n closeOnClick={menuItem.closeOnClick}\n disabled={menuItem.disabled}\n key={itemKey}\n label={labelText}\n onClick={(event) => invokeItemClick(menuItem, nextKeyPath, event)}\n >\n {renderItemContent(menuItem, { iconAlign, reserveIconSpace })}\n </ContextMenu.Item>\n );\n });\n};\n"],"mappings":"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"}
|
|
@@ -23,8 +23,8 @@ declare const styles: {
|
|
|
23
23
|
};
|
|
24
24
|
declare const rootVariants: (props?: ({
|
|
25
25
|
shadow?: boolean | null | undefined;
|
|
26
|
-
size?: "
|
|
27
|
-
variant?: "filled" | "
|
|
26
|
+
size?: "large" | "middle" | "small" | null | undefined;
|
|
27
|
+
variant?: "filled" | "borderless" | "outlined" | null | undefined;
|
|
28
28
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
29
29
|
//#endregion
|
|
30
30
|
export { rootVariants, styles };
|
|
@@ -29,12 +29,16 @@ const Segmented = ({ block = false, className, classNames, defaultValue, disable
|
|
|
29
29
|
if (!list) return;
|
|
30
30
|
const active = list.querySelector("[data-segmented-item][data-pressed]");
|
|
31
31
|
if (!active) {
|
|
32
|
+
list.style.setProperty("--active-item-left", "0px");
|
|
33
|
+
list.style.setProperty("--active-item-top", "0px");
|
|
34
|
+
list.style.setProperty("--active-item-right", "0px");
|
|
32
35
|
list.style.setProperty("--active-item-width", "0px");
|
|
33
36
|
list.style.setProperty("--active-item-height", "0px");
|
|
34
37
|
return;
|
|
35
38
|
}
|
|
36
39
|
list.style.setProperty("--active-item-left", `${active.offsetLeft}px`);
|
|
37
40
|
list.style.setProperty("--active-item-top", `${active.offsetTop}px`);
|
|
41
|
+
list.style.setProperty("--active-item-right", `${list.clientWidth - active.offsetLeft - active.offsetWidth}px`);
|
|
38
42
|
list.style.setProperty("--active-item-width", `${active.offsetWidth}px`);
|
|
39
43
|
list.style.setProperty("--active-item-height", `${active.offsetHeight}px`);
|
|
40
44
|
}, []);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Segmented.mjs","names":["useControlledState"],"sources":["../../../src/base-ui/Segmented/Segmented.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\nimport {\n type CSSProperties,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n} from 'react';\nimport useControlledState from 'use-merge-value';\n\nimport {\n SegmentedIndicator,\n SegmentedItem,\n SegmentedItemIcon,\n SegmentedItemLabel,\n SegmentedRoot,\n} from './atoms';\nimport type { SegmentedOption, SegmentedProps } from './type';\n\nconst normalizeOption = <Value extends string>(\n option: SegmentedOption<Value> | Value,\n): SegmentedOption<Value> =>\n typeof option === 'string' ? { label: option, value: option } : option;\n\nconst Segmented = <Value extends string = string>({\n block = false,\n className,\n classNames,\n defaultValue,\n disabled = false,\n glass = false,\n id,\n name,\n onChange,\n options,\n ref,\n shadow = false,\n size = 'middle',\n style,\n styles: customStyles,\n value,\n variant = 'filled',\n vertical = false,\n}: SegmentedProps<Value>) => {\n const [innerValue, setInnerValue] = useControlledState<Value | undefined>(defaultValue, {\n defaultValue,\n onChange: (next) => {\n if (next != null) onChange?.(next);\n },\n value,\n });\n\n const listRef = useRef<HTMLDivElement | null>(null);\n\n const mergedRef = useCallback(\n (node: HTMLDivElement | null) => {\n listRef.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.RefObject<HTMLDivElement | null>).current = node;\n },\n [ref],\n );\n\n const normalizedOptions = useMemo(\n () => (options ?? []).map((o) => normalizeOption<Value>(o)),\n [options],\n );\n\n const updateIndicator = useCallback(() => {\n const list = listRef.current;\n if (!list) return;\n const active = list.querySelector<HTMLElement>('[data-segmented-item][data-pressed]');\n if (!active) {\n list.style.setProperty('--active-item-width', '0px');\n list.style.setProperty('--active-item-height', '0px');\n return;\n }\n list.style.setProperty('--active-item-left', `${active.offsetLeft}px`);\n list.style.setProperty('--active-item-top', `${active.offsetTop}px`);\n list.style.setProperty('--active-item-width', `${active.offsetWidth}px`);\n list.style.setProperty('--active-item-height', `${active.offsetHeight}px`);\n }, []);\n\n useLayoutEffect(() => {\n updateIndicator();\n }, [innerValue, normalizedOptions, vertical, size, block, updateIndicator]);\n\n useEffect(() => {\n const list = listRef.current;\n if (!list || typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(() => updateIndicator());\n ro.observe(list);\n return () => ro.disconnect();\n }, [updateIndicator]);\n\n const groupValue = useMemo<Value[]>(() => (innerValue != null ? [innerValue] : []), [innerValue]);\n\n const rootStyle: CSSProperties = { ...style, ...customStyles?.root };\n\n return (\n <SegmentedRoot<Value>\n block={block}\n className={cx(classNames?.root, className)}\n disabled={disabled}\n glass={glass}\n id={id}\n orientation={vertical ? 'vertical' : 'horizontal'}\n ref={mergedRef}\n shadow={shadow}\n style={rootStyle}\n value={groupValue}\n variant={variant}\n onValueChange={(next) => {\n const picked = next[0];\n if (picked != null) setInnerValue(picked);\n }}\n >\n <SegmentedIndicator className={classNames?.indicator} style={customStyles?.indicator} />\n {normalizedOptions.map((opt) => (\n <SegmentedItem<Value>\n aria-label={typeof opt.label === 'string' ? opt.label : undefined}\n block={block}\n className={cx(classNames?.item, opt.className)}\n data-segmented-item=\"\"\n disabled={disabled || opt.disabled}\n key={opt.value}\n name={name}\n size={size}\n style={customStyles?.item}\n title={opt.title}\n value={opt.value}\n >\n {opt.icon != null && (\n <SegmentedItemIcon className={classNames?.itemIcon} style={customStyles?.itemIcon}>\n {opt.icon}\n </SegmentedItemIcon>\n )}\n {opt.label != null && (\n <SegmentedItemLabel className={classNames?.itemLabel} style={customStyles?.itemLabel}>\n {opt.label}\n </SegmentedItemLabel>\n )}\n </SegmentedItem>\n ))}\n </SegmentedRoot>\n );\n};\n\nSegmented.displayName = 'Segmented';\n\nexport default Segmented;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Segmented.mjs","names":["useControlledState"],"sources":["../../../src/base-ui/Segmented/Segmented.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\nimport {\n type CSSProperties,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n} from 'react';\nimport useControlledState from 'use-merge-value';\n\nimport {\n SegmentedIndicator,\n SegmentedItem,\n SegmentedItemIcon,\n SegmentedItemLabel,\n SegmentedRoot,\n} from './atoms';\nimport type { SegmentedOption, SegmentedProps } from './type';\n\nconst normalizeOption = <Value extends string>(\n option: SegmentedOption<Value> | Value,\n): SegmentedOption<Value> =>\n typeof option === 'string' ? { label: option, value: option } : option;\n\nconst Segmented = <Value extends string = string>({\n block = false,\n className,\n classNames,\n defaultValue,\n disabled = false,\n glass = false,\n id,\n name,\n onChange,\n options,\n ref,\n shadow = false,\n size = 'middle',\n style,\n styles: customStyles,\n value,\n variant = 'filled',\n vertical = false,\n}: SegmentedProps<Value>) => {\n const [innerValue, setInnerValue] = useControlledState<Value | undefined>(defaultValue, {\n defaultValue,\n onChange: (next) => {\n if (next != null) onChange?.(next);\n },\n value,\n });\n\n const listRef = useRef<HTMLDivElement | null>(null);\n\n const mergedRef = useCallback(\n (node: HTMLDivElement | null) => {\n listRef.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.RefObject<HTMLDivElement | null>).current = node;\n },\n [ref],\n );\n\n const normalizedOptions = useMemo(\n () => (options ?? []).map((o) => normalizeOption<Value>(o)),\n [options],\n );\n\n const updateIndicator = useCallback(() => {\n const list = listRef.current;\n if (!list) return;\n const active = list.querySelector<HTMLElement>('[data-segmented-item][data-pressed]');\n if (!active) {\n list.style.setProperty('--active-item-left', '0px');\n list.style.setProperty('--active-item-top', '0px');\n list.style.setProperty('--active-item-right', '0px');\n list.style.setProperty('--active-item-width', '0px');\n list.style.setProperty('--active-item-height', '0px');\n return;\n }\n list.style.setProperty('--active-item-left', `${active.offsetLeft}px`);\n list.style.setProperty('--active-item-top', `${active.offsetTop}px`);\n list.style.setProperty(\n '--active-item-right',\n `${list.clientWidth - active.offsetLeft - active.offsetWidth}px`,\n );\n list.style.setProperty('--active-item-width', `${active.offsetWidth}px`);\n list.style.setProperty('--active-item-height', `${active.offsetHeight}px`);\n }, []);\n\n useLayoutEffect(() => {\n updateIndicator();\n }, [innerValue, normalizedOptions, vertical, size, block, updateIndicator]);\n\n useEffect(() => {\n const list = listRef.current;\n if (!list || typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(() => updateIndicator());\n ro.observe(list);\n return () => ro.disconnect();\n }, [updateIndicator]);\n\n const groupValue = useMemo<Value[]>(() => (innerValue != null ? [innerValue] : []), [innerValue]);\n\n const rootStyle: CSSProperties = { ...style, ...customStyles?.root };\n\n return (\n <SegmentedRoot<Value>\n block={block}\n className={cx(classNames?.root, className)}\n disabled={disabled}\n glass={glass}\n id={id}\n orientation={vertical ? 'vertical' : 'horizontal'}\n ref={mergedRef}\n shadow={shadow}\n style={rootStyle}\n value={groupValue}\n variant={variant}\n onValueChange={(next) => {\n const picked = next[0];\n if (picked != null) setInnerValue(picked);\n }}\n >\n <SegmentedIndicator className={classNames?.indicator} style={customStyles?.indicator} />\n {normalizedOptions.map((opt) => (\n <SegmentedItem<Value>\n aria-label={typeof opt.label === 'string' ? opt.label : undefined}\n block={block}\n className={cx(classNames?.item, opt.className)}\n data-segmented-item=\"\"\n disabled={disabled || opt.disabled}\n key={opt.value}\n name={name}\n size={size}\n style={customStyles?.item}\n title={opt.title}\n value={opt.value}\n >\n {opt.icon != null && (\n <SegmentedItemIcon className={classNames?.itemIcon} style={customStyles?.itemIcon}>\n {opt.icon}\n </SegmentedItemIcon>\n )}\n {opt.label != null && (\n <SegmentedItemLabel className={classNames?.itemLabel} style={customStyles?.itemLabel}>\n {opt.label}\n </SegmentedItemLabel>\n )}\n </SegmentedItem>\n ))}\n </SegmentedRoot>\n );\n};\n\nSegmented.displayName = 'Segmented';\n\nexport default Segmented;\n"],"mappings":"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"}
|
|
@@ -22,6 +22,10 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
|
22
22
|
transition-duration: 240ms;
|
|
23
23
|
transition-property: inset-inline-start, inset-block-start, width, height;
|
|
24
24
|
|
|
25
|
+
[data-orientation='horizontal'] &:dir(rtl) {
|
|
26
|
+
inset-inline-start: var(--active-item-right);
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
@media (prefers-reduced-motion: reduce) {
|
|
26
30
|
transition-duration: 0s;
|
|
27
31
|
}
|