@wallarm-org/design-system 0.23.2 → 0.23.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CodeSnippet/CodeSnippetCode.js +38 -7
- package/dist/components/CodeSnippet/CodeSnippetContent.js +1 -1
- package/dist/components/CodeSnippet/CodeSnippetContext.d.ts +2 -0
- package/dist/components/CodeSnippet/CodeSnippetCopyButton.js +1 -2
- package/dist/components/CodeSnippet/CodeSnippetRoot.js +11 -2
- package/dist/components/CodeSnippet/InlineCodeSnippet.js +3 -4
- package/dist/components/CodeSnippet/internal/CodeLine.d.ts +9 -0
- package/dist/components/CodeSnippet/internal/CodeLine.js +13 -2
- package/dist/components/CodeSnippet/internal/FoldColumn.js +1 -8
- package/dist/components/CodeSnippet/internal/TokenizedCodeLine.d.ts +9 -0
- package/dist/components/CodeSnippet/internal/TokenizedCodeLine.js +9 -1
- package/dist/components/CodeSnippet/lib/foldUtils.d.ts +1 -1
- package/dist/hooks/useCopyTooltip.d.ts +0 -1
- package/dist/hooks/useCopyTooltip.js +3 -4
- package/dist/metadata/components.json +5 -5
- package/dist/utils/copyText.d.ts +2 -0
- package/dist/utils/copyText.js +27 -0
- package/package.json +1 -1
- package/dist/components/CodeSnippet/internal/FoldSummaryLine.d.ts +0 -8
- package/dist/components/CodeSnippet/internal/FoldSummaryLine.js +0 -20
|
@@ -2,19 +2,48 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useTestId } from "../../utils/testId.js";
|
|
3
3
|
import { useCodeSnippet } from "./hooks/index.js";
|
|
4
4
|
import { CodeContent, CodeLine, TokenizedCodeLine } from "./internal/index.js";
|
|
5
|
-
import {
|
|
5
|
+
import { getFoldSummaryLabel } from "./lib/foldUtils.js";
|
|
6
6
|
import { SIZE_LINE_HEIGHT_CLASSES } from "./lib/lineStyles.js";
|
|
7
7
|
import { splitTextByRanges } from "./lib/lineUtils.js";
|
|
8
8
|
const CodeSnippetCode = ({ className, ...props })=>{
|
|
9
9
|
const testId = useTestId('code');
|
|
10
|
-
const { code, tokens, isLoading, wrapLines, lines, size, inlineGutter, showLineNumbers, visibleDisplayItems, toggleFold } = useCodeSnippet();
|
|
10
|
+
const { code, tokens, isLoading, wrapLines, lines, size, inlineGutter, showLineNumbers, visibleDisplayItems, folds, foldByStartLine, collapsedFolds, toggleFold } = useCodeSnippet();
|
|
11
11
|
const lineHeightClass = SIZE_LINE_HEIGHT_CLASSES[size];
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const hasFolds = folds.length > 0;
|
|
13
|
+
const getFoldProps = (lineNumber)=>{
|
|
14
|
+
if (!inlineGutter || !hasFolds) return {};
|
|
15
|
+
const fold = foldByStartLine.get(lineNumber);
|
|
16
|
+
return {
|
|
17
|
+
fold,
|
|
18
|
+
isFoldCollapsed: fold ? collapsedFolds.has(fold.id) : void 0,
|
|
19
|
+
onFoldToggle: fold ? ()=>toggleFold(fold.id) : void 0,
|
|
20
|
+
hasFolds: true
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
const renderFoldSummary = (item)=>{
|
|
24
|
+
const label = getFoldSummaryLabel(item.fold, item.lineCount);
|
|
25
|
+
return /*#__PURE__*/ jsx(CodeLine, {
|
|
26
|
+
lineConfig: void 0,
|
|
15
27
|
lineHeightClass: lineHeightClass,
|
|
16
|
-
|
|
28
|
+
showInlineGutter: inlineGutter,
|
|
29
|
+
lineNumber: showLineNumbers ? item.fold.startLine : void 0,
|
|
30
|
+
fold: inlineGutter ? item.fold : void 0,
|
|
31
|
+
isFoldCollapsed: inlineGutter || void 0,
|
|
32
|
+
onFoldToggle: inlineGutter ? ()=>toggleFold(item.fold.id) : void 0,
|
|
33
|
+
hasFolds: inlineGutter && hasFolds,
|
|
34
|
+
children: /*#__PURE__*/ jsx("button", {
|
|
35
|
+
type: "button",
|
|
36
|
+
className: "inline-flex items-center cursor-pointer select-none",
|
|
37
|
+
"aria-expanded": false,
|
|
38
|
+
"aria-label": `Collapsed region: ${label}, ${item.lineCount} lines`,
|
|
39
|
+
onClick: ()=>toggleFold(item.fold.id),
|
|
40
|
+
children: /*#__PURE__*/ jsx("span", {
|
|
41
|
+
className: "inline-flex items-center rounded-2 border border-border-secondary px-6 text-xs leading-sm text-text-secondary hover:text-text-primary transition-colors",
|
|
42
|
+
children: label
|
|
43
|
+
})
|
|
44
|
+
})
|
|
17
45
|
}, `fold-${item.fold.id}`);
|
|
46
|
+
};
|
|
18
47
|
if (isLoading || !tokens) {
|
|
19
48
|
const codeLines = code.split('\n');
|
|
20
49
|
return /*#__PURE__*/ jsx(CodeContent, {
|
|
@@ -33,6 +62,7 @@ const CodeSnippetCode = ({ className, ...props })=>{
|
|
|
33
62
|
lineHeightClass: lineHeightClass,
|
|
34
63
|
showInlineGutter: inlineGutter,
|
|
35
64
|
lineNumber: showLineNumbers ? item.lineNumber : void 0,
|
|
65
|
+
...getFoldProps(item.lineNumber),
|
|
36
66
|
children: hasRanges ? splitTextByRanges(line, ranges, lineConfig?.color).map((segment, i)=>/*#__PURE__*/ jsx("span", {
|
|
37
67
|
className: segment.rangeColor,
|
|
38
68
|
children: segment.content
|
|
@@ -55,7 +85,8 @@ const CodeSnippetCode = ({ className, ...props })=>{
|
|
|
55
85
|
lineConfig: lines.get(item.lineNumber),
|
|
56
86
|
lineHeightClass: lineHeightClass,
|
|
57
87
|
showInlineGutter: inlineGutter,
|
|
58
|
-
lineNumber: showLineNumbers ? item.lineNumber : void 0
|
|
88
|
+
lineNumber: showLineNumbers ? item.lineNumber : void 0,
|
|
89
|
+
...getFoldProps(item.lineNumber)
|
|
59
90
|
}, item.lineNumber);
|
|
60
91
|
})
|
|
61
92
|
});
|
|
@@ -75,7 +75,7 @@ const CodeSnippetContent = ({ className, children, nativeScroll = false, ...prop
|
|
|
75
75
|
return /*#__PURE__*/ jsx("div", {
|
|
76
76
|
"data-slot": "code-snippet-content",
|
|
77
77
|
"data-testid": testId,
|
|
78
|
-
className: cn('min-h-0 [&_[data-part=viewport]]:overscroll-none', className),
|
|
78
|
+
className: cn('min-h-0', '[&_[data-part=viewport]]:overscroll-none', '[&_[data-part=viewport]:not([data-overflow-x]):not([data-overflow-y])]:!overflow-clip', className),
|
|
79
79
|
...props,
|
|
80
80
|
children: /*#__PURE__*/ jsxs(ScrollArea, {
|
|
81
81
|
children: [
|
|
@@ -45,6 +45,8 @@ export type CodeSnippetContextValue<TLanguage extends string = string> = {
|
|
|
45
45
|
/** Display items clipped to maxLines. All rendering components should iterate this. */
|
|
46
46
|
visibleDisplayItems: DisplayItem[];
|
|
47
47
|
folds: FoldRegion[];
|
|
48
|
+
/** Fold regions indexed by their start line for O(1) lookup */
|
|
49
|
+
foldByStartLine: Map<number, FoldRegion>;
|
|
48
50
|
collapsedFolds: Set<string>;
|
|
49
51
|
toggleFold: (foldId: string) => void;
|
|
50
52
|
isExpanded: boolean;
|
|
@@ -8,14 +8,13 @@ import { useCodeSnippet } from "./hooks/index.js";
|
|
|
8
8
|
const CodeSnippetCopyButton = ({ onClick, ref, ...props })=>{
|
|
9
9
|
const testId = useTestId('copy-button');
|
|
10
10
|
const { code } = useCodeSnippet();
|
|
11
|
-
const {
|
|
11
|
+
const { copied, tooltipOpen, onTooltipOpenChange, handleCopy } = useCopyTooltip({
|
|
12
12
|
text: code
|
|
13
13
|
});
|
|
14
14
|
const handleClick = (event)=>{
|
|
15
15
|
handleCopy(event);
|
|
16
16
|
onClick?.(event);
|
|
17
17
|
};
|
|
18
|
-
if (!isSupported) return null;
|
|
19
18
|
return /*#__PURE__*/ jsxs(Tooltip, {
|
|
20
19
|
open: tooltipOpen,
|
|
21
20
|
onOpenChange: onTooltipOpenChange,
|
|
@@ -3,6 +3,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
|
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
import { cn } from "../../utils/cn.js";
|
|
6
|
+
import { copyText } from "../../utils/copyText.js";
|
|
6
7
|
import { TestIdProvider } from "../../utils/testId.js";
|
|
7
8
|
import { plainAdapter } from "./adapters/plain.js";
|
|
8
9
|
import { CodeSnippetContext, MIN_HIDDEN_LINES_THRESHOLD } from "./CodeSnippetContext.js";
|
|
@@ -74,7 +75,7 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_L
|
|
|
74
75
|
isFullscreen
|
|
75
76
|
]);
|
|
76
77
|
const copyToClipboard = useCallback(async ()=>{
|
|
77
|
-
await
|
|
78
|
+
await copyText(code);
|
|
78
79
|
onCopy?.(code);
|
|
79
80
|
}, [
|
|
80
81
|
code,
|
|
@@ -88,7 +89,7 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_L
|
|
|
88
89
|
]);
|
|
89
90
|
const computeCollapsedFolds = useCallback((folds)=>{
|
|
90
91
|
if (!folds) return new Set();
|
|
91
|
-
return new Set(folds.filter((f)=>
|
|
92
|
+
return new Set(folds.filter((f)=>true === f.defaultCollapsed).map((f)=>f.id));
|
|
92
93
|
}, []);
|
|
93
94
|
const [collapsedFolds, setCollapsedFolds] = useState(()=>computeCollapsedFolds(foldsProp));
|
|
94
95
|
useEffect(()=>{
|
|
@@ -105,6 +106,12 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_L
|
|
|
105
106
|
return next;
|
|
106
107
|
});
|
|
107
108
|
}, []);
|
|
109
|
+
const foldByStartLine = useMemo(()=>new Map(validatedFolds.map((f)=>[
|
|
110
|
+
f.startLine,
|
|
111
|
+
f
|
|
112
|
+
])), [
|
|
113
|
+
validatedFolds
|
|
114
|
+
]);
|
|
108
115
|
const displayItems = useMemo(()=>buildDisplayItems(totalLines, validatedFolds, collapsedFolds, startingLineNumber), [
|
|
109
116
|
totalLines,
|
|
110
117
|
validatedFolds,
|
|
@@ -138,6 +145,7 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_L
|
|
|
138
145
|
displayItems,
|
|
139
146
|
visibleDisplayItems,
|
|
140
147
|
folds: validatedFolds,
|
|
148
|
+
foldByStartLine,
|
|
141
149
|
collapsedFolds,
|
|
142
150
|
toggleFold,
|
|
143
151
|
isExpanded,
|
|
@@ -161,6 +169,7 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_L
|
|
|
161
169
|
displayItems,
|
|
162
170
|
visibleDisplayItems,
|
|
163
171
|
validatedFolds,
|
|
172
|
+
foldByStartLine,
|
|
164
173
|
collapsedFolds,
|
|
165
174
|
toggleFold,
|
|
166
175
|
isExpanded,
|
|
@@ -20,12 +20,11 @@ const inlineCodeSnippetVariants = cva("inline-flex items-center code-snippet-bg
|
|
|
20
20
|
});
|
|
21
21
|
const InlineCodeSnippet = (props)=>{
|
|
22
22
|
const { code, size = 'inherit', asChild = false, copyable = true, className, onClick, ref, ...otherProps } = props;
|
|
23
|
-
const {
|
|
23
|
+
const { copied, tooltipOpen, onTooltipOpenChange, handleCopy } = useCopyTooltip({
|
|
24
24
|
text: code,
|
|
25
25
|
enabled: copyable
|
|
26
26
|
});
|
|
27
27
|
const Comp = asChild ? Slot : 'code';
|
|
28
|
-
const isCopyable = copyable && isSupported;
|
|
29
28
|
const handleClick = (event)=>{
|
|
30
29
|
handleCopy(event);
|
|
31
30
|
onClick?.(event);
|
|
@@ -35,13 +34,13 @@ const InlineCodeSnippet = (props)=>{
|
|
|
35
34
|
"data-slot": "inline-code-snippet",
|
|
36
35
|
className: cn(inlineCodeSnippetVariants({
|
|
37
36
|
size,
|
|
38
|
-
copyable
|
|
37
|
+
copyable
|
|
39
38
|
}), className),
|
|
40
39
|
...otherProps,
|
|
41
40
|
onClick: handleClick,
|
|
42
41
|
children: code
|
|
43
42
|
});
|
|
44
|
-
if (!
|
|
43
|
+
if (!copyable) return codeElement;
|
|
45
44
|
return /*#__PURE__*/ jsxs(Tooltip, {
|
|
46
45
|
open: tooltipOpen,
|
|
47
46
|
onOpenChange: onTooltipOpenChange,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FC, ReactNode } from 'react';
|
|
2
2
|
import type { LineConfig } from '../CodeSnippetContext';
|
|
3
|
+
import type { FoldRegion } from '../lib/foldUtils';
|
|
3
4
|
export type CodeLineProps = {
|
|
4
5
|
lineConfig: LineConfig | undefined;
|
|
5
6
|
lineHeightClass: string;
|
|
@@ -7,6 +8,14 @@ export type CodeLineProps = {
|
|
|
7
8
|
showInlineGutter?: boolean;
|
|
8
9
|
/** Line number to display (only shown when showInlineGutter is true) */
|
|
9
10
|
lineNumber?: number;
|
|
11
|
+
/** Fold region starting at this line (only shown when showInlineGutter is true) */
|
|
12
|
+
fold?: FoldRegion;
|
|
13
|
+
/** Whether the fold is collapsed */
|
|
14
|
+
isFoldCollapsed?: boolean;
|
|
15
|
+
/** Callback to toggle the fold */
|
|
16
|
+
onFoldToggle?: () => void;
|
|
17
|
+
/** Whether any folds exist (used to render consistent-width spacer) */
|
|
18
|
+
hasFolds?: boolean;
|
|
10
19
|
children: ReactNode;
|
|
11
20
|
};
|
|
12
21
|
/** Renders a single line of code with styling */
|
|
@@ -2,7 +2,8 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { cn } from "../../../utils/cn.js";
|
|
3
3
|
import { LINE_COLOR_STYLES } from "../lib/lineStyles.js";
|
|
4
4
|
import { getLineTextStyles } from "../lib/lineUtils.js";
|
|
5
|
-
|
|
5
|
+
import { FoldToggle } from "./FoldToggle.js";
|
|
6
|
+
const CodeLine = ({ lineConfig, lineHeightClass, showInlineGutter = false, lineNumber, fold, isFoldCollapsed, onFoldToggle, hasFolds = false, children })=>{
|
|
6
7
|
const { colorClass, textStyleClass, className: lineClassName, style } = getLineTextStyles(lineConfig);
|
|
7
8
|
const colorStyles = lineConfig?.color ? LINE_COLOR_STYLES[lineConfig.color] : void 0;
|
|
8
9
|
return /*#__PURE__*/ jsxs("div", {
|
|
@@ -12,12 +13,22 @@ const CodeLine = ({ lineConfig, lineHeightClass, showInlineGutter = false, lineN
|
|
|
12
13
|
showInlineGutter && /*#__PURE__*/ jsxs(Fragment, {
|
|
13
14
|
children: [
|
|
14
15
|
/*#__PURE__*/ jsx("span", {
|
|
15
|
-
className: cn('shrink-0 self-stretch border-l-2 pl-12', colorStyles?.border ?? 'border-transparent')
|
|
16
|
+
className: cn('shrink-0 self-stretch border-l-2', !hasFolds && 'pl-12', colorStyles?.border ?? 'border-transparent')
|
|
16
17
|
}),
|
|
17
18
|
void 0 !== lineNumber && /*#__PURE__*/ jsx("span", {
|
|
18
19
|
className: cn('shrink-0 select-none text-right text-text-secondary px-8', colorStyles?.text),
|
|
19
20
|
children: lineNumber
|
|
20
21
|
}),
|
|
22
|
+
hasFolds && /*#__PURE__*/ jsx("span", {
|
|
23
|
+
className: "shrink-0 flex items-center justify-center px-4",
|
|
24
|
+
children: fold && onFoldToggle ? /*#__PURE__*/ jsx(FoldToggle, {
|
|
25
|
+
fold: fold,
|
|
26
|
+
isCollapsed: isFoldCollapsed ?? false,
|
|
27
|
+
onToggle: onFoldToggle
|
|
28
|
+
}) : /*#__PURE__*/ jsx("span", {
|
|
29
|
+
className: "w-16 h-16"
|
|
30
|
+
})
|
|
31
|
+
}),
|
|
21
32
|
lineConfig?.prefix !== void 0 && /*#__PURE__*/ jsx("span", {
|
|
22
33
|
className: cn('shrink-0 select-none px-8 text-center', colorStyles?.text),
|
|
23
34
|
children: lineConfig.prefix
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from "react";
|
|
3
2
|
import { cn } from "../../../utils/cn.js";
|
|
4
3
|
import { useCodeSnippet } from "../hooks/index.js";
|
|
5
4
|
import { LINE_COLOR_STYLES, SIZE_LINE_HEIGHT_CLASSES } from "../lib/lineStyles.js";
|
|
6
5
|
import { FoldToggle } from "./FoldToggle.js";
|
|
7
6
|
const FoldColumn = ()=>{
|
|
8
|
-
const { visibleDisplayItems,
|
|
7
|
+
const { visibleDisplayItems, foldByStartLine, collapsedFolds, toggleFold, size, lines } = useCodeSnippet();
|
|
9
8
|
const lineHeightClass = SIZE_LINE_HEIGHT_CLASSES[size];
|
|
10
|
-
const foldByStartLine = useMemo(()=>new Map(folds.map((f)=>[
|
|
11
|
-
f.startLine,
|
|
12
|
-
f
|
|
13
|
-
])), [
|
|
14
|
-
folds
|
|
15
|
-
]);
|
|
16
9
|
return /*#__PURE__*/ jsx("div", {
|
|
17
10
|
className: "flex flex-col select-none",
|
|
18
11
|
"data-slot": "code-snippet-fold",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { FC } from 'react';
|
|
2
2
|
import type { Token } from '../adapters/types';
|
|
3
3
|
import type { LineConfig } from '../CodeSnippetContext';
|
|
4
|
+
import type { FoldRegion } from '../lib/foldUtils';
|
|
4
5
|
export type TokenizedCodeLineProps = {
|
|
5
6
|
tokens: Token[];
|
|
6
7
|
lineConfig: LineConfig | undefined;
|
|
@@ -9,6 +10,14 @@ export type TokenizedCodeLineProps = {
|
|
|
9
10
|
showInlineGutter?: boolean;
|
|
10
11
|
/** Line number to display (only shown when showInlineGutter is true) */
|
|
11
12
|
lineNumber?: number;
|
|
13
|
+
/** Fold region starting at this line */
|
|
14
|
+
fold?: FoldRegion;
|
|
15
|
+
/** Whether the fold is collapsed */
|
|
16
|
+
isFoldCollapsed?: boolean;
|
|
17
|
+
/** Callback to toggle the fold */
|
|
18
|
+
onFoldToggle?: () => void;
|
|
19
|
+
/** Whether any folds exist */
|
|
20
|
+
hasFolds?: boolean;
|
|
12
21
|
};
|
|
13
22
|
/** Renders a line of tokenized code with syntax highlighting */
|
|
14
23
|
export declare const TokenizedCodeLine: FC<TokenizedCodeLineProps>;
|
|
@@ -2,7 +2,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { getLineTextStyles, splitTokensByRanges } from "../lib/lineUtils.js";
|
|
3
3
|
import { CodeLine } from "./CodeLine.js";
|
|
4
4
|
import { CodeToken } from "./CodeToken.js";
|
|
5
|
-
const TokenizedCodeLine = ({ tokens, lineConfig, lineHeightClass, showInlineGutter, lineNumber })=>{
|
|
5
|
+
const TokenizedCodeLine = ({ tokens, lineConfig, lineHeightClass, showInlineGutter, lineNumber, fold, isFoldCollapsed, onFoldToggle, hasFolds })=>{
|
|
6
6
|
const { colorClass } = getLineTextStyles(lineConfig);
|
|
7
7
|
const ranges = lineConfig?.ranges;
|
|
8
8
|
const hasRanges = ranges && ranges.length > 0;
|
|
@@ -13,6 +13,10 @@ const TokenizedCodeLine = ({ tokens, lineConfig, lineHeightClass, showInlineGutt
|
|
|
13
13
|
lineHeightClass: lineHeightClass,
|
|
14
14
|
showInlineGutter: showInlineGutter,
|
|
15
15
|
lineNumber: lineNumber,
|
|
16
|
+
fold: fold,
|
|
17
|
+
isFoldCollapsed: isFoldCollapsed,
|
|
18
|
+
onFoldToggle: onFoldToggle,
|
|
19
|
+
hasFolds: hasFolds,
|
|
16
20
|
children: enrichedTokens.map((token, i)=>/*#__PURE__*/ jsx(CodeToken, {
|
|
17
21
|
token: token,
|
|
18
22
|
colorClass: colorClass,
|
|
@@ -25,6 +29,10 @@ const TokenizedCodeLine = ({ tokens, lineConfig, lineHeightClass, showInlineGutt
|
|
|
25
29
|
lineHeightClass: lineHeightClass,
|
|
26
30
|
showInlineGutter: showInlineGutter,
|
|
27
31
|
lineNumber: lineNumber,
|
|
32
|
+
fold: fold,
|
|
33
|
+
isFoldCollapsed: isFoldCollapsed,
|
|
34
|
+
onFoldToggle: onFoldToggle,
|
|
35
|
+
hasFolds: hasFolds,
|
|
28
36
|
children: tokens.map((token, i)=>/*#__PURE__*/ jsx(CodeToken, {
|
|
29
37
|
token: token,
|
|
30
38
|
colorClass: colorClass
|
|
@@ -7,7 +7,7 @@ export type FoldRegion = {
|
|
|
7
7
|
endLine: number;
|
|
8
8
|
/** Optional label for the collapsed summary. When omitted, auto-generated from line count. */
|
|
9
9
|
label?: string;
|
|
10
|
-
/** Whether the fold starts collapsed. Default:
|
|
10
|
+
/** Whether the fold starts collapsed. Default: false */
|
|
11
11
|
defaultCollapsed?: boolean;
|
|
12
12
|
};
|
|
13
13
|
export type DisplayItem = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
-
|
|
2
|
+
import { copyText } from "../utils/copyText.js";
|
|
3
3
|
function useCopyTooltip({ text, enabled = true }) {
|
|
4
4
|
const [hovering, setHovering] = useState(false);
|
|
5
5
|
const [copied, setCopied] = useState(false);
|
|
@@ -17,8 +17,8 @@ function useCopyTooltip({ text, enabled = true }) {
|
|
|
17
17
|
]);
|
|
18
18
|
const handleCopy = useCallback((event)=>{
|
|
19
19
|
event.stopPropagation();
|
|
20
|
-
if (!enabled
|
|
21
|
-
|
|
20
|
+
if (!enabled) return;
|
|
21
|
+
copyText(text);
|
|
22
22
|
setCopied(true);
|
|
23
23
|
setKeepOpen(true);
|
|
24
24
|
clearTimer();
|
|
@@ -55,7 +55,6 @@ function useCopyTooltip({ text, enabled = true }) {
|
|
|
55
55
|
clearTimer
|
|
56
56
|
]);
|
|
57
57
|
return {
|
|
58
|
-
isSupported: isClipboardSupported,
|
|
59
58
|
copied,
|
|
60
59
|
tooltipOpen,
|
|
61
60
|
onTooltipOpenChange,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.23.
|
|
3
|
-
"generatedAt": "2026-04-
|
|
2
|
+
"version": "0.23.2",
|
|
3
|
+
"generatedAt": "2026-04-13T13:36:55.041Z",
|
|
4
4
|
"components": [
|
|
5
5
|
{
|
|
6
6
|
"name": "Alert",
|
|
@@ -12516,8 +12516,8 @@
|
|
|
12516
12516
|
},
|
|
12517
12517
|
{
|
|
12518
12518
|
"name": "LineWrapping",
|
|
12519
|
-
"code": "() => {\n const longCode = `const veryLongVariableName = \"This is a very long string that will demonstrate line wrapping behavior when the content exceeds the container width\";\nconsole.log(veryLongVariableName);`;\n\n return (\n <VStack gap={16}>\n <VStack align='start' gap={4}>\n <span className='text-xs text-text-secondary font-medium'>Without wrapping</span>\n <div style={{ maxWidth: '600px' }}>\n <CodeSnippetRoot code={longCode} language='text'>\n <CodeSnippetContent>\n <CodeSnippetLineNumbers />\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n </VStack>\n\n <VStack align='start' gap={4}>\n <span className='text-xs text-text-secondary font-medium'>With wrapping</span>\n <div style={{ maxWidth: '600px' }}>\n <CodeSnippetRoot code={longCode} language='text' wrapLines>\n <CodeSnippetContent>\n <CodeSnippetLineNumbers />\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n </VStack>\n <VStack align='start' gap={4}>\n <span className='text-xs text-text-secondary font-medium'>\n Without wrapping with annotations\n </span>\n <div style={{ maxWidth: '600px' }}>\n <CodeSnippetRoot\n code={longCode}\n language='text'\n lines={{\n 1: { color: 'danger', prefix: '-' },\n 2: { color: 'success', prefix: '+' },\n }}\n >\n <CodeSnippetContent>\n <CodeSnippetLineNumbers />\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n </VStack>\n <VStack align='start' gap={4}>\n <span className='text-xs text-text-secondary font-medium'>Wrapping with annotations</span>\n <div style={{ maxWidth: '600px' }}>\n <CodeSnippetRoot\n code={longCode}\n language='text'\n wrapLines\n lines={{\n 1: { color: 'danger', prefix: '-' },\n 2: { color: 'success', prefix: '+' },\n }}\n >\n <CodeSnippetContent>\n <CodeSnippetLineNumbers />\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n </VStack>\n </VStack>\n );\n}",
|
|
12520
|
-
"description": "Line wrapping enabled for long lines."
|
|
12519
|
+
"code": "() => {\n const longCode = `const veryLongVariableName = \"This is a very long string that will demonstrate line wrapping behavior when the content exceeds the container width\";\nconsole.log(veryLongVariableName);`;\n\n const httpCode = `GET /api/v2/users?page=1&limit=20&filter=active&sort=name&order=asc&include=profile,settings HTTP/1.1\nHost: api.wallarm.com\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0\nContent-Type: application/json\nAccept: application/json\n\n{\n \"filter\": { \"status\": \"active\", \"role\": \"admin\" }\n}`;\n\n return (\n <VStack gap={16}>\n <VStack align='start' gap={4}>\n <span className='text-xs text-text-secondary font-medium'>Without wrapping</span>\n <div style={{ maxWidth: '600px' }}>\n <CodeSnippetRoot code={longCode} language='text'>\n <CodeSnippetContent>\n <CodeSnippetLineNumbers />\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n </VStack>\n\n <VStack align='start' gap={4}>\n <span className='text-xs text-text-secondary font-medium'>With wrapping</span>\n <div style={{ maxWidth: '600px' }}>\n <CodeSnippetRoot code={longCode} language='text' wrapLines>\n <CodeSnippetContent>\n <CodeSnippetLineNumbers />\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n </VStack>\n <VStack align='start' gap={4}>\n <span className='text-xs text-text-secondary font-medium'>\n Without wrapping with annotations\n </span>\n <div style={{ maxWidth: '600px' }}>\n <CodeSnippetRoot\n code={longCode}\n language='text'\n lines={{\n 1: { color: 'danger', prefix: '-' },\n 2: { color: 'success', prefix: '+' },\n }}\n >\n <CodeSnippetContent>\n <CodeSnippetLineNumbers />\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n </VStack>\n <VStack align='start' gap={4}>\n <span className='text-xs text-text-secondary font-medium'>Wrapping with annotations</span>\n <div style={{ maxWidth: '600px' }}>\n <CodeSnippetRoot\n code={longCode}\n language='text'\n wrapLines\n lines={{\n 1: { color: 'danger', prefix: '-' },\n 2: { color: 'success', prefix: '+' },\n }}\n >\n <CodeSnippetContent>\n <CodeSnippetLineNumbers />\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n </VStack>\n <VStack align='start' gap={4}>\n <span className='text-xs text-text-secondary font-medium'>Folds without wrapping</span>\n <div style={{ maxWidth: '600px' }}>\n <CodeSnippetRoot\n code={httpCode}\n language='text'\n folds={getHttpFolds(httpCode, { headers: { defaultCollapsed: true } })}\n >\n <CodeSnippetContent>\n <CodeSnippetLineNumbers />\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n </VStack>\n <VStack align='start' gap={4}>\n <span className='text-xs text-text-secondary font-medium'>Folds with wrapping</span>\n <div style={{ maxWidth: '600px' }}>\n <CodeSnippetRoot\n code={httpCode}\n language='text'\n wrapLines\n folds={getHttpFolds(httpCode, { headers: { defaultCollapsed: true } })}\n >\n <CodeSnippetContent>\n <CodeSnippetLineNumbers />\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n </div>\n </VStack>\n </VStack>\n );\n}",
|
|
12520
|
+
"description": "Line wrapping enabled for long lines.\nIncludes examples with annotations and folds in wrap mode."
|
|
12521
12521
|
},
|
|
12522
12522
|
{
|
|
12523
12523
|
"name": "WithBothScrolls",
|
|
@@ -12591,7 +12591,7 @@
|
|
|
12591
12591
|
},
|
|
12592
12592
|
{
|
|
12593
12593
|
"name": "WithFoldingExpanded",
|
|
12594
|
-
"code": "() => (\n <CodeSnippetRoot\n code={foldableRequestCode}\n language='text'\n folds={getHttpFolds(foldableRequestCode
|
|
12594
|
+
"code": "() => (\n <CodeSnippetRoot\n code={foldableRequestCode}\n language='text'\n folds={getHttpFolds(foldableRequestCode)}\n lines={{\n 3: { color: 'warning' },\n 4: { color: 'danger', ranges: [{ start: 14, end: 42, color: 'danger' }] },\n 10: { color: 'info' },\n 11: { color: 'info' },\n }}\n >\n <CodeSnippetContent>\n <CodeSnippetLineNumbers />\n <CodeSnippetCode />\n </CodeSnippetContent>\n </CodeSnippetRoot>\n)",
|
|
12595
12595
|
"description": "Folds combined with line highlights — colors and decorations\non folded lines are hidden when collapsed, visible when expanded."
|
|
12596
12596
|
},
|
|
12597
12597
|
{
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const isClipboardApiSupported = "u" > typeof navigator && void 0 !== navigator.clipboard && 'function' == typeof navigator.clipboard.writeText;
|
|
2
|
+
function fallbackCopy(text) {
|
|
3
|
+
const textarea = document.createElement('textarea');
|
|
4
|
+
textarea.value = text;
|
|
5
|
+
textarea.style.position = 'fixed';
|
|
6
|
+
textarea.style.left = '-9999px';
|
|
7
|
+
textarea.setAttribute('aria-hidden', 'true');
|
|
8
|
+
textarea.tabIndex = -1;
|
|
9
|
+
textarea.readOnly = true;
|
|
10
|
+
document.body.appendChild(textarea);
|
|
11
|
+
textarea.select();
|
|
12
|
+
try {
|
|
13
|
+
return document.execCommand('copy');
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
} finally{
|
|
17
|
+
document.body.removeChild(textarea);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function copyText(text) {
|
|
21
|
+
if (isClipboardApiSupported) try {
|
|
22
|
+
await navigator.clipboard.writeText(text);
|
|
23
|
+
return;
|
|
24
|
+
} catch {}
|
|
25
|
+
fallbackCopy(text);
|
|
26
|
+
}
|
|
27
|
+
export { copyText };
|
package/package.json
CHANGED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from "../../../utils/cn.js";
|
|
3
|
-
import { getFoldSummaryLabel } from "../lib/foldUtils.js";
|
|
4
|
-
const FoldSummaryLine = ({ fold, lineCount, lineHeightClass, onToggle })=>{
|
|
5
|
-
const label = getFoldSummaryLabel(fold, lineCount);
|
|
6
|
-
const ariaLabel = `Collapsed region: ${label}, ${lineCount} lines`;
|
|
7
|
-
return /*#__PURE__*/ jsx("button", {
|
|
8
|
-
type: "button",
|
|
9
|
-
className: cn(lineHeightClass, 'flex w-full items-center gap-4 px-4 text-text-secondary hover:text-text-primary hover:bg-bg-secondary-hover transition-colors cursor-pointer select-none text-left'),
|
|
10
|
-
"aria-expanded": false,
|
|
11
|
-
"aria-label": ariaLabel,
|
|
12
|
-
onClick: onToggle,
|
|
13
|
-
children: /*#__PURE__*/ jsx("span", {
|
|
14
|
-
className: "inline-flex items-center rounded-2 border border-border-secondary px-6 text-xs leading-sm",
|
|
15
|
-
children: label
|
|
16
|
-
})
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
|
-
FoldSummaryLine.displayName = 'FoldSummaryLine';
|
|
20
|
-
export { FoldSummaryLine };
|