@wallarm-org/design-system 0.22.0-rc-feature-WDS-74-attribute.2 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CodeSnippet/CodeSnippetActions.js +1 -1
- package/dist/components/CodeSnippet/CodeSnippetCode.js +21 -16
- package/dist/components/CodeSnippet/CodeSnippetContent.js +10 -10
- package/dist/components/CodeSnippet/CodeSnippetContext.d.ts +8 -0
- package/dist/components/CodeSnippet/CodeSnippetLineNumbers.js +10 -7
- package/dist/components/CodeSnippet/CodeSnippetRoot.d.ts +4 -1
- package/dist/components/CodeSnippet/CodeSnippetRoot.js +54 -3
- package/dist/components/CodeSnippet/InlineCodeSnippet.js +1 -1
- package/dist/components/CodeSnippet/index.d.ts +2 -0
- package/dist/components/CodeSnippet/index.js +2 -1
- package/dist/components/CodeSnippet/internal/CodeSnippetHighlights.js +7 -8
- package/dist/components/CodeSnippet/internal/ColorStickColumn.d.ts +2 -2
- package/dist/components/CodeSnippet/internal/ColorStickColumn.js +8 -8
- package/dist/components/CodeSnippet/internal/FoldColumn.d.ts +3 -0
- package/dist/components/CodeSnippet/internal/FoldColumn.js +42 -0
- package/dist/components/CodeSnippet/internal/FoldSummaryLine.d.ts +8 -0
- package/dist/components/CodeSnippet/internal/FoldSummaryLine.js +20 -0
- package/dist/components/CodeSnippet/internal/FoldToggle.d.ts +7 -0
- package/dist/components/CodeSnippet/internal/FoldToggle.js +19 -0
- package/dist/components/CodeSnippet/internal/PrefixColumn.d.ts +2 -2
- package/dist/components/CodeSnippet/internal/PrefixColumn.js +8 -8
- package/dist/components/CodeSnippet/internal/ShowMoreButton.js +2 -2
- package/dist/components/CodeSnippet/lib/foldUtils.d.ts +28 -0
- package/dist/components/CodeSnippet/lib/foldUtils.js +80 -0
- package/dist/components/CodeSnippet/lib/httpFolds.d.ts +27 -0
- package/dist/components/CodeSnippet/lib/httpFolds.js +36 -0
- package/dist/components/Flex/Flex.d.ts +1 -1
- package/dist/components/SegmentedControl/SegmentedControlSeparator.d.ts +1 -1
- package/dist/components/Separator/Separator.d.ts +1 -1
- package/dist/components/Skeleton/Skeleton.d.ts +1 -1
- package/dist/components/SplitButton/SplitButton.d.ts +6 -0
- package/dist/components/SplitButton/SplitButton.js +18 -0
- package/dist/components/SplitButton/classes.d.ts +1 -0
- package/dist/components/SplitButton/classes.js +7 -0
- package/dist/components/SplitButton/index.d.ts +1 -0
- package/dist/components/SplitButton/index.js +2 -0
- package/dist/components/Stack/Stack.d.ts +1 -1
- package/dist/hooks/useCopyTooltip.js +5 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/metadata/components.json +658 -1251
- package/dist/theme/index.css +1 -0
- package/dist/theme/semantic.css +32 -25
- package/dist/theme/utilities/code-snippet-bg.css +8 -0
- package/package.json +1 -1
- package/dist/components/Attribute/Attribute.d.ts +0 -9
- package/dist/components/Attribute/Attribute.js +0 -33
- package/dist/components/Attribute/AttributeLabel.d.ts +0 -15
- package/dist/components/Attribute/AttributeLabel.js +0 -58
- package/dist/components/Attribute/AttributeValue.d.ts +0 -6
- package/dist/components/Attribute/AttributeValue.js +0 -25
- package/dist/components/Attribute/index.d.ts +0 -3
- package/dist/components/Attribute/index.js +0 -4
|
@@ -7,7 +7,7 @@ const CodeSnippetActions = ({ className, ref, ...props })=>{
|
|
|
7
7
|
ref: ref,
|
|
8
8
|
"data-slot": "code-snippet-actions",
|
|
9
9
|
"data-testid": testId,
|
|
10
|
-
className: cn('flex items-center gap-4 ml-auto px-6 rounded-tr-6', className),
|
|
10
|
+
className: cn('flex items-center gap-4 ml-auto px-6 rounded-tr-6 code-snippet-bg', className),
|
|
11
11
|
...props
|
|
12
12
|
});
|
|
13
13
|
};
|
|
@@ -1,57 +1,62 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useTestId } from "../../utils/testId.js";
|
|
3
|
-
import { MIN_HIDDEN_LINES_THRESHOLD } from "./CodeSnippetContext.js";
|
|
4
3
|
import { useCodeSnippet } from "./hooks/index.js";
|
|
5
4
|
import { CodeContent, CodeLine, TokenizedCodeLine } from "./internal/index.js";
|
|
5
|
+
import { FoldSummaryLine } from "./internal/FoldSummaryLine.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,
|
|
10
|
+
const { code, tokens, isLoading, wrapLines, lines, size, inlineGutter, showLineNumbers, visibleDisplayItems, toggleFold } = useCodeSnippet();
|
|
11
11
|
const lineHeightClass = SIZE_LINE_HEIGHT_CLASSES[size];
|
|
12
|
-
const
|
|
13
|
-
|
|
12
|
+
const renderFoldSummary = (item)=>/*#__PURE__*/ jsx(FoldSummaryLine, {
|
|
13
|
+
fold: item.fold,
|
|
14
|
+
lineCount: item.lineCount,
|
|
15
|
+
lineHeightClass: lineHeightClass,
|
|
16
|
+
onToggle: ()=>toggleFold(item.fold.id)
|
|
17
|
+
}, `fold-${item.fold.id}`);
|
|
14
18
|
if (isLoading || !tokens) {
|
|
15
19
|
const codeLines = code.split('\n');
|
|
16
|
-
const visibleLines = shouldClip ? codeLines.slice(0, maxLines) : codeLines;
|
|
17
20
|
return /*#__PURE__*/ jsx(CodeContent, {
|
|
18
21
|
wrapLines: wrapLines,
|
|
19
22
|
className: className,
|
|
20
23
|
...props,
|
|
21
24
|
"data-testid": testId,
|
|
22
|
-
children:
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
+
children: visibleDisplayItems.map((item)=>{
|
|
26
|
+
if ('fold-summary' === item.type) return renderFoldSummary(item);
|
|
27
|
+
const line = codeLines[item.index] ?? '';
|
|
28
|
+
const lineConfig = lines.get(item.lineNumber);
|
|
25
29
|
const ranges = lineConfig?.ranges;
|
|
26
30
|
const hasRanges = ranges && ranges.length > 0;
|
|
27
31
|
return /*#__PURE__*/ jsx(CodeLine, {
|
|
28
32
|
lineConfig: lineConfig,
|
|
29
33
|
lineHeightClass: lineHeightClass,
|
|
30
34
|
showInlineGutter: inlineGutter,
|
|
31
|
-
lineNumber: showLineNumbers ? lineNumber : void 0,
|
|
35
|
+
lineNumber: showLineNumbers ? item.lineNumber : void 0,
|
|
32
36
|
children: hasRanges ? splitTextByRanges(line, ranges, lineConfig?.color).map((segment, i)=>/*#__PURE__*/ jsx("span", {
|
|
33
37
|
className: segment.rangeColor,
|
|
34
38
|
children: segment.content
|
|
35
39
|
}, i)) : line
|
|
36
|
-
}, lineNumber);
|
|
40
|
+
}, item.lineNumber);
|
|
37
41
|
})
|
|
38
42
|
});
|
|
39
43
|
}
|
|
40
|
-
const visibleTokens = shouldClip ? tokens.slice(0, maxLines) : tokens;
|
|
41
44
|
return /*#__PURE__*/ jsx(CodeContent, {
|
|
42
45
|
wrapLines: wrapLines,
|
|
43
46
|
className: className,
|
|
44
47
|
...props,
|
|
45
48
|
"data-testid": testId,
|
|
46
|
-
children:
|
|
47
|
-
|
|
49
|
+
children: visibleDisplayItems.map((item)=>{
|
|
50
|
+
if ('fold-summary' === item.type) return renderFoldSummary(item);
|
|
51
|
+
const lineTokens = tokens[item.index];
|
|
52
|
+
if (!lineTokens) return null;
|
|
48
53
|
return /*#__PURE__*/ jsx(TokenizedCodeLine, {
|
|
49
54
|
tokens: lineTokens,
|
|
50
|
-
lineConfig: lines.get(lineNumber),
|
|
55
|
+
lineConfig: lines.get(item.lineNumber),
|
|
51
56
|
lineHeightClass: lineHeightClass,
|
|
52
57
|
showInlineGutter: inlineGutter,
|
|
53
|
-
lineNumber: showLineNumbers ? lineNumber : void 0
|
|
54
|
-
}, lineNumber);
|
|
58
|
+
lineNumber: showLineNumbers ? item.lineNumber : void 0
|
|
59
|
+
}, item.lineNumber);
|
|
55
60
|
})
|
|
56
61
|
});
|
|
57
62
|
};
|
|
@@ -7,21 +7,22 @@ import { CodeSnippetContext } from "./CodeSnippetContext.js";
|
|
|
7
7
|
import { useCodeSnippet } from "./hooks/index.js";
|
|
8
8
|
import { CodeSnippetHighlights } from "./internal/CodeSnippetHighlights.js";
|
|
9
9
|
import { ColorStickColumn } from "./internal/ColorStickColumn.js";
|
|
10
|
+
import { FoldColumn } from "./internal/FoldColumn.js";
|
|
10
11
|
import { PrefixColumn } from "./internal/PrefixColumn.js";
|
|
11
12
|
import { SIZE_LINE_HEIGHT_CLASSES } from "./lib/lineStyles.js";
|
|
12
13
|
const CodeSnippetContent = ({ className, children, nativeScroll = false, ...props })=>{
|
|
13
14
|
const testId = useTestId('content');
|
|
14
15
|
const context = useCodeSnippet();
|
|
15
|
-
const { wrapLines, lines,
|
|
16
|
+
const { wrapLines, lines, visibleDisplayItems, folds, size } = context;
|
|
17
|
+
const hasFolds = folds.length > 0;
|
|
16
18
|
const hasHighlights = Array.from(lines.values()).some((config)=>null != config.color);
|
|
17
19
|
const hasAnyPrefix = Array.from(lines.values()).some((config)=>null != config.prefix);
|
|
18
|
-
const lineCount = totalLines;
|
|
19
20
|
const lineHeightClass = SIZE_LINE_HEIGHT_CLASSES[size];
|
|
20
21
|
const childrenArray = Children.toArray(children);
|
|
21
22
|
const lineNumbersElement = childrenArray.find((child)=>/*#__PURE__*/ isValidElement(child) && child.type?.displayName === 'CodeSnippetLineNumbers');
|
|
22
23
|
const otherChildren = childrenArray.filter((child)=>!/*#__PURE__*/ isValidElement(child) || child.type?.displayName !== 'CodeSnippetLineNumbers');
|
|
23
24
|
const hasLineNumbers = Boolean(lineNumbersElement);
|
|
24
|
-
const needsGutterElements = hasHighlights || hasLineNumbers || hasAnyPrefix;
|
|
25
|
+
const needsGutterElements = hasHighlights || hasLineNumbers || hasAnyPrefix || hasFolds;
|
|
25
26
|
const useInlineGutter = wrapLines && needsGutterElements;
|
|
26
27
|
const hasGutter = !wrapLines && needsGutterElements;
|
|
27
28
|
const overriddenContext = useMemo(()=>({
|
|
@@ -39,18 +40,17 @@ const CodeSnippetContent = ({ className, children, nativeScroll = false, ...prop
|
|
|
39
40
|
!wrapLines && hasHighlights && /*#__PURE__*/ jsx(CodeSnippetHighlights, {}),
|
|
40
41
|
hasGutter && /*#__PURE__*/ jsxs("div", {
|
|
41
42
|
"data-slot": "code-snippet-gutter",
|
|
42
|
-
className: "sticky left-0 z-20 flex shrink-0 py-8 -my-8 mr-8
|
|
43
|
+
className: "sticky left-0 z-20 flex shrink-0 py-8 -my-8 mr-8 code-snippet-bg [contain:paint]",
|
|
43
44
|
children: [
|
|
44
45
|
hasHighlights && /*#__PURE__*/ jsx(ColorStickColumn, {
|
|
45
|
-
|
|
46
|
-
startingLineNumber: startingLineNumber,
|
|
46
|
+
visibleDisplayItems: visibleDisplayItems,
|
|
47
47
|
lines: lines,
|
|
48
48
|
lineHeightClass: lineHeightClass
|
|
49
49
|
}),
|
|
50
50
|
lineNumbersElement,
|
|
51
|
+
hasFolds && /*#__PURE__*/ jsx(FoldColumn, {}),
|
|
51
52
|
hasAnyPrefix && /*#__PURE__*/ jsx(PrefixColumn, {
|
|
52
|
-
|
|
53
|
-
startingLineNumber: startingLineNumber,
|
|
53
|
+
visibleDisplayItems: visibleDisplayItems,
|
|
54
54
|
lines: lines,
|
|
55
55
|
lineHeightClass: lineHeightClass
|
|
56
56
|
})
|
|
@@ -68,14 +68,14 @@ const CodeSnippetContent = ({ className, children, nativeScroll = false, ...prop
|
|
|
68
68
|
if (nativeScroll) return /*#__PURE__*/ jsx("div", {
|
|
69
69
|
"data-slot": "code-snippet-content",
|
|
70
70
|
"data-testid": testId,
|
|
71
|
-
className: cn('min-h-0', wrapLines ? 'overflow-y-auto overflow-x-hidden' : 'overflow-auto', className),
|
|
71
|
+
className: cn('min-h-0 overscroll-none', wrapLines ? 'overflow-y-auto overflow-x-hidden' : 'overflow-auto', className),
|
|
72
72
|
...props,
|
|
73
73
|
children: innerContent
|
|
74
74
|
});
|
|
75
75
|
return /*#__PURE__*/ jsx("div", {
|
|
76
76
|
"data-slot": "code-snippet-content",
|
|
77
77
|
"data-testid": testId,
|
|
78
|
-
className: cn('min-h-0', className),
|
|
78
|
+
className: cn('min-h-0 [&_[data-part=viewport]]:overscroll-none', className),
|
|
79
79
|
...props,
|
|
80
80
|
children: /*#__PURE__*/ jsxs(ScrollArea, {
|
|
81
81
|
children: [
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CSSProperties, ReactNode } from 'react';
|
|
2
2
|
import type { SyntaxAdapter, Token } from './adapters/types';
|
|
3
|
+
import type { DisplayItem, FoldRegion } from './lib/foldUtils';
|
|
3
4
|
export type CodeSnippetSize = 'sm' | 'md' | 'lg';
|
|
4
5
|
export type LineColor = 'danger' | 'warning' | 'info' | 'success' | 'brand' | 'ai' | 'neutral';
|
|
5
6
|
export type LineTextStyle = 'regular' | 'medium' | 'italic';
|
|
@@ -39,6 +40,13 @@ export type CodeSnippetContextValue<TLanguage extends string = string> = {
|
|
|
39
40
|
showLineNumbers: boolean;
|
|
40
41
|
lines: Map<number, LineConfig>;
|
|
41
42
|
totalLines: number;
|
|
43
|
+
/** All display items (post-fold, pre-clip). Used for ShowMore counting. */
|
|
44
|
+
displayItems: DisplayItem[];
|
|
45
|
+
/** Display items clipped to maxLines. All rendering components should iterate this. */
|
|
46
|
+
visibleDisplayItems: DisplayItem[];
|
|
47
|
+
folds: FoldRegion[];
|
|
48
|
+
collapsedFolds: Set<string>;
|
|
49
|
+
toggleFold: (foldId: string) => void;
|
|
42
50
|
isExpanded: boolean;
|
|
43
51
|
maxLines: number;
|
|
44
52
|
isFullscreen: boolean;
|
|
@@ -5,7 +5,7 @@ import { useCodeSnippet } from "./hooks/index.js";
|
|
|
5
5
|
import { LINE_COLOR_STYLES, SIZE_LINE_HEIGHT_CLASSES } from "./lib/lineStyles.js";
|
|
6
6
|
const CodeSnippetLineNumbers = ({ className, ...props })=>{
|
|
7
7
|
const testId = useTestId('line-numbers');
|
|
8
|
-
const { tokens,
|
|
8
|
+
const { tokens, visibleDisplayItems, lines, size } = useCodeSnippet();
|
|
9
9
|
const lineHeightClass = SIZE_LINE_HEIGHT_CLASSES[size];
|
|
10
10
|
if (!tokens) return null;
|
|
11
11
|
return /*#__PURE__*/ jsx("div", {
|
|
@@ -13,14 +13,17 @@ const CodeSnippetLineNumbers = ({ className, ...props })=>{
|
|
|
13
13
|
"data-testid": testId,
|
|
14
14
|
className: cn('flex flex-col text-text-secondary select-none text-right', className),
|
|
15
15
|
...props,
|
|
16
|
-
children:
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
children: visibleDisplayItems.map((item)=>{
|
|
17
|
+
if ('fold-summary' === item.type) return /*#__PURE__*/ jsx("span", {
|
|
18
|
+
className: cn(lineHeightClass, 'px-8'),
|
|
19
|
+
children: item.fold.startLine
|
|
20
|
+
}, `fold-${item.fold.id}`);
|
|
21
|
+
const lineConfig = lines.get(item.lineNumber);
|
|
19
22
|
const colorStyles = lineConfig?.color ? LINE_COLOR_STYLES[lineConfig.color] : void 0;
|
|
20
23
|
return /*#__PURE__*/ jsx("span", {
|
|
21
|
-
className: cn(lineHeightClass, 'px-8
|
|
22
|
-
children: lineNumber
|
|
23
|
-
}, lineNumber);
|
|
24
|
+
className: cn(lineHeightClass, 'px-8', colorStyles?.text, colorStyles?.bg),
|
|
25
|
+
children: item.lineNumber
|
|
26
|
+
}, item.lineNumber);
|
|
24
27
|
})
|
|
25
28
|
});
|
|
26
29
|
};
|
|
@@ -2,6 +2,7 @@ import type { HTMLAttributes, ReactNode, Ref } from 'react';
|
|
|
2
2
|
import { type VariantProps } from 'class-variance-authority';
|
|
3
3
|
import { type TestableProps } from '../../utils/testId';
|
|
4
4
|
import { type LineConfig } from './CodeSnippetContext';
|
|
5
|
+
import { type FoldRegion } from './lib/foldUtils';
|
|
5
6
|
declare const codeSnippetRootVariants: (props?: ({
|
|
6
7
|
size?: "sm" | "md" | "lg" | null | undefined;
|
|
7
8
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
@@ -21,13 +22,15 @@ export type CodeSnippetRootProps<TLanguage extends string = string> = CodeSnippe
|
|
|
21
22
|
wrapLines?: boolean;
|
|
22
23
|
/** Max lines before collapsing (for ShowMore) */
|
|
23
24
|
maxLines?: number;
|
|
25
|
+
/** Foldable regions that can be collapsed/expanded */
|
|
26
|
+
folds?: FoldRegion[];
|
|
24
27
|
/** Callback when code is copied */
|
|
25
28
|
onCopy?: (code: string) => void;
|
|
26
29
|
/** Child components */
|
|
27
30
|
children?: ReactNode;
|
|
28
31
|
};
|
|
29
32
|
export declare const CodeSnippetRoot: {
|
|
30
|
-
<TLanguage extends string = string>({ code, language, size, lines, startingLineNumber, wrapLines: initialWrapLines, maxLines, onCopy, className, children, "data-testid": testId, ...props }: CodeSnippetRootProps<TLanguage>): import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
<TLanguage extends string = string>({ code, language, size, lines, startingLineNumber, wrapLines: initialWrapLines, maxLines, folds: foldsProp, onCopy, className, children, "data-testid": testId, ...props }: CodeSnippetRootProps<TLanguage>): import("react/jsx-runtime").JSX.Element;
|
|
31
34
|
displayName: string;
|
|
32
35
|
};
|
|
33
36
|
export {};
|
|
@@ -5,10 +5,11 @@ import { cva } from "class-variance-authority";
|
|
|
5
5
|
import { cn } from "../../utils/cn.js";
|
|
6
6
|
import { TestIdProvider } from "../../utils/testId.js";
|
|
7
7
|
import { plainAdapter } from "./adapters/plain.js";
|
|
8
|
-
import { CodeSnippetContext } from "./CodeSnippetContext.js";
|
|
8
|
+
import { CodeSnippetContext, MIN_HIDDEN_LINES_THRESHOLD } from "./CodeSnippetContext.js";
|
|
9
9
|
import { useAdapter } from "./hooks/index.js";
|
|
10
10
|
import { ShowMoreButton } from "./internal/ShowMoreButton.js";
|
|
11
|
-
|
|
11
|
+
import { buildDisplayItems, validateFolds } from "./lib/foldUtils.js";
|
|
12
|
+
const codeSnippetRootVariants = cva("relative code-snippet-bg rounded-6 font-mono text-syntax-no-syntax overflow-hidden flex flex-col [&::selection]:bg-[var(--color-syntax-highlight-selected-highlight)] [&::selection]:text-[var(--color-syntax-highlight-selected-code)] [&_*::selection]:bg-[var(--color-syntax-highlight-selected-highlight)] [&_*::selection]:text-[var(--color-syntax-highlight-selected-code)] [&>[data-slot=code-snippet-actions]]:absolute [&>[data-slot=code-snippet-actions]]:right-0 [&>[data-slot=code-snippet-actions]]:top-0 [&>[data-slot=code-snippet-actions]]:z-30 [&>[data-slot=code-snippet-actions]]:p-6 [&>[data-slot=code-snippet-actions]]:rounded-br-6 [&>[data-slot=code-snippet-actions]]:rounded-tl-6", {
|
|
12
13
|
variants: {
|
|
13
14
|
size: {
|
|
14
15
|
sm: 'text-xs leading-sm',
|
|
@@ -20,7 +21,8 @@ const codeSnippetRootVariants = cva("relative bg-component-code-snippet-bg round
|
|
|
20
21
|
size: 'sm'
|
|
21
22
|
}
|
|
22
23
|
});
|
|
23
|
-
const
|
|
24
|
+
const EMPTY_LINES = {};
|
|
25
|
+
const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = EMPTY_LINES, startingLineNumber = 1, wrapLines: initialWrapLines = false, maxLines = 0, folds: foldsProp, onCopy, className, children, 'data-testid': testId, ...props })=>{
|
|
24
26
|
const adapterContext = useAdapter();
|
|
25
27
|
const [wrapLines, setWrapLines] = useState(initialWrapLines);
|
|
26
28
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
@@ -79,6 +81,45 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = {}, sta
|
|
|
79
81
|
onCopy
|
|
80
82
|
]);
|
|
81
83
|
const totalLines = tokens?.length ?? code.split('\n').length;
|
|
84
|
+
const validatedFolds = useMemo(()=>foldsProp ? validateFolds(foldsProp, totalLines, startingLineNumber) : [], [
|
|
85
|
+
foldsProp,
|
|
86
|
+
totalLines,
|
|
87
|
+
startingLineNumber
|
|
88
|
+
]);
|
|
89
|
+
const computeCollapsedFolds = useCallback((folds)=>{
|
|
90
|
+
if (!folds) return new Set();
|
|
91
|
+
return new Set(folds.filter((f)=>false !== f.defaultCollapsed).map((f)=>f.id));
|
|
92
|
+
}, []);
|
|
93
|
+
const [collapsedFolds, setCollapsedFolds] = useState(()=>computeCollapsedFolds(foldsProp));
|
|
94
|
+
useEffect(()=>{
|
|
95
|
+
setCollapsedFolds(computeCollapsedFolds(foldsProp));
|
|
96
|
+
}, [
|
|
97
|
+
foldsProp,
|
|
98
|
+
computeCollapsedFolds
|
|
99
|
+
]);
|
|
100
|
+
const toggleFold = useCallback((foldId)=>{
|
|
101
|
+
setCollapsedFolds((prev)=>{
|
|
102
|
+
const next = new Set(prev);
|
|
103
|
+
if (next.has(foldId)) next.delete(foldId);
|
|
104
|
+
else next.add(foldId);
|
|
105
|
+
return next;
|
|
106
|
+
});
|
|
107
|
+
}, []);
|
|
108
|
+
const displayItems = useMemo(()=>buildDisplayItems(totalLines, validatedFolds, collapsedFolds, startingLineNumber), [
|
|
109
|
+
totalLines,
|
|
110
|
+
validatedFolds,
|
|
111
|
+
collapsedFolds,
|
|
112
|
+
startingLineNumber
|
|
113
|
+
]);
|
|
114
|
+
const visibleDisplayItems = useMemo(()=>{
|
|
115
|
+
const hiddenRows = displayItems.length - maxLines;
|
|
116
|
+
const shouldClip = maxLines > 0 && !isExpanded && hiddenRows >= MIN_HIDDEN_LINES_THRESHOLD;
|
|
117
|
+
return shouldClip ? displayItems.slice(0, maxLines) : displayItems;
|
|
118
|
+
}, [
|
|
119
|
+
displayItems,
|
|
120
|
+
maxLines,
|
|
121
|
+
isExpanded
|
|
122
|
+
]);
|
|
82
123
|
const contextValue = useMemo(()=>({
|
|
83
124
|
code,
|
|
84
125
|
language,
|
|
@@ -94,6 +135,11 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = {}, sta
|
|
|
94
135
|
v
|
|
95
136
|
])),
|
|
96
137
|
totalLines,
|
|
138
|
+
displayItems,
|
|
139
|
+
visibleDisplayItems,
|
|
140
|
+
folds: validatedFolds,
|
|
141
|
+
collapsedFolds,
|
|
142
|
+
toggleFold,
|
|
97
143
|
isExpanded,
|
|
98
144
|
maxLines,
|
|
99
145
|
isFullscreen,
|
|
@@ -112,6 +158,11 @@ const CodeSnippetRoot = ({ code, language = 'text', size = 'sm', lines = {}, sta
|
|
|
112
158
|
startingLineNumber,
|
|
113
159
|
lines,
|
|
114
160
|
totalLines,
|
|
161
|
+
displayItems,
|
|
162
|
+
visibleDisplayItems,
|
|
163
|
+
validatedFolds,
|
|
164
|
+
collapsedFolds,
|
|
165
|
+
toggleFold,
|
|
115
166
|
isExpanded,
|
|
116
167
|
maxLines,
|
|
117
168
|
isFullscreen,
|
|
@@ -4,7 +4,7 @@ import { cva } from "class-variance-authority";
|
|
|
4
4
|
import { useCopyTooltip } from "../../hooks/index.js";
|
|
5
5
|
import { cn } from "../../utils/cn.js";
|
|
6
6
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../Tooltip/index.js";
|
|
7
|
-
const inlineCodeSnippetVariants = cva("inline-flex items-center
|
|
7
|
+
const inlineCodeSnippetVariants = cva("inline-flex items-center code-snippet-bg rounded-6 font-mono font-normal text-syntax-no-syntax", {
|
|
8
8
|
variants: {
|
|
9
9
|
size: {
|
|
10
10
|
sm: 'text-xs px-4 py-2',
|
|
@@ -16,3 +16,5 @@ export { CodeSnippetTitle, type CodeSnippetTitleProps } from './CodeSnippetTitle
|
|
|
16
16
|
export { CodeSnippetWrapButton, type CodeSnippetWrapButtonProps } from './CodeSnippetWrapButton';
|
|
17
17
|
export { useAdapter, useCodeSnippet } from './hooks';
|
|
18
18
|
export { InlineCodeSnippet, type InlineCodeSnippetProps } from './InlineCodeSnippet';
|
|
19
|
+
export { type FoldRegion } from './lib/foldUtils';
|
|
20
|
+
export { getHttpFolds, HTTP_FOLD_ID, type HttpFoldOptions, type HttpFoldSectionOptions, } from './lib/httpFolds';
|
|
@@ -14,4 +14,5 @@ import { CodeSnippetTitle } from "./CodeSnippetTitle.js";
|
|
|
14
14
|
import { CodeSnippetWrapButton } from "./CodeSnippetWrapButton.js";
|
|
15
15
|
import { useAdapter, useCodeSnippet } from "./hooks/index.js";
|
|
16
16
|
import { InlineCodeSnippet } from "./InlineCodeSnippet.js";
|
|
17
|
-
|
|
17
|
+
import { HTTP_FOLD_ID, getHttpFolds } from "./lib/httpFolds.js";
|
|
18
|
+
export { CodeSnippetActions, CodeSnippetAdapterProvider, CodeSnippetCode, CodeSnippetContent, CodeSnippetCopyButton, CodeSnippetFullscreenButton, CodeSnippetHeader, CodeSnippetLineNumbers, CodeSnippetRoot, CodeSnippetTab, CodeSnippetTabs, CodeSnippetTitle, CodeSnippetWrapButton, HTTP_FOLD_ID, InlineCodeSnippet, getHttpFolds, loadHighlightJsAdapter, loadPrismAdapter, loadShikiAdapter, plainAdapter, useAdapter, useCodeSnippet };
|
|
@@ -3,22 +3,21 @@ import { cn } from "../../../utils/cn.js";
|
|
|
3
3
|
import { useCodeSnippet } from "../hooks/index.js";
|
|
4
4
|
import { LINE_COLOR_STYLES, SIZE_LINE_HEIGHT_CLASSES } from "../lib/lineStyles.js";
|
|
5
5
|
const CodeSnippetHighlights = ()=>{
|
|
6
|
-
const {
|
|
7
|
-
const lineCount = totalLines;
|
|
6
|
+
const { visibleDisplayItems, lines, size } = useCodeSnippet();
|
|
8
7
|
const lineHeightClass = SIZE_LINE_HEIGHT_CLASSES[size];
|
|
9
8
|
return /*#__PURE__*/ jsx("div", {
|
|
10
9
|
"data-slot": "code-snippet-highlights",
|
|
11
10
|
className: "absolute inset-0 z-0 flex flex-col py-8 pointer-events-none",
|
|
12
11
|
"aria-hidden": "true",
|
|
13
|
-
children:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const lineConfig = lines.get(lineNumber);
|
|
12
|
+
children: visibleDisplayItems.map((item)=>{
|
|
13
|
+
if ('fold-summary' === item.type) return /*#__PURE__*/ jsx("div", {
|
|
14
|
+
className: lineHeightClass
|
|
15
|
+
}, `fold-${item.fold.id}`);
|
|
16
|
+
const lineConfig = lines.get(item.lineNumber);
|
|
18
17
|
const colorStyles = lineConfig?.color ? LINE_COLOR_STYLES[lineConfig.color] : null;
|
|
19
18
|
return /*#__PURE__*/ jsx("div", {
|
|
20
19
|
className: cn(lineHeightClass, colorStyles?.bg)
|
|
21
|
-
}, lineNumber);
|
|
20
|
+
}, item.lineNumber);
|
|
22
21
|
})
|
|
23
22
|
});
|
|
24
23
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { FC } from 'react';
|
|
2
2
|
import type { LineConfig } from '../CodeSnippetContext';
|
|
3
|
+
import type { DisplayItem } from '../lib/foldUtils';
|
|
3
4
|
/** Color stick column component - renders color indicators for each line */
|
|
4
5
|
export declare const ColorStickColumn: FC<{
|
|
5
|
-
|
|
6
|
-
startingLineNumber: number;
|
|
6
|
+
visibleDisplayItems: DisplayItem[];
|
|
7
7
|
lines: Map<number, LineConfig>;
|
|
8
8
|
lineHeightClass: string;
|
|
9
9
|
}>;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../../../utils/cn.js";
|
|
3
3
|
import { LINE_COLOR_STYLES } from "../lib/lineStyles.js";
|
|
4
|
-
const ColorStickColumn = ({
|
|
4
|
+
const ColorStickColumn = ({ visibleDisplayItems, lines, lineHeightClass })=>/*#__PURE__*/ jsx("div", {
|
|
5
5
|
className: "flex flex-col",
|
|
6
6
|
"data-slot": "code-snippet-color-stick",
|
|
7
|
-
children:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const lineConfig = lines.get(lineNumber);
|
|
7
|
+
children: visibleDisplayItems.map((item)=>{
|
|
8
|
+
if ('fold-summary' === item.type) return /*#__PURE__*/ jsx("span", {
|
|
9
|
+
className: cn(lineHeightClass, 'border-l-2 pl-12 border-transparent')
|
|
10
|
+
}, `fold-${item.fold.id}`);
|
|
11
|
+
const lineConfig = lines.get(item.lineNumber);
|
|
12
12
|
const colorStyles = lineConfig?.color ? LINE_COLOR_STYLES[lineConfig.color] : void 0;
|
|
13
13
|
return /*#__PURE__*/ jsx("span", {
|
|
14
|
-
className: cn(lineHeightClass, 'border-l-2 pl-12',
|
|
15
|
-
}, lineNumber);
|
|
14
|
+
className: cn(lineHeightClass, 'border-l-2 pl-12', colorStyles?.border ?? 'border-transparent', colorStyles?.bg)
|
|
15
|
+
}, item.lineNumber);
|
|
16
16
|
})
|
|
17
17
|
});
|
|
18
18
|
export { ColorStickColumn };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { cn } from "../../../utils/cn.js";
|
|
4
|
+
import { useCodeSnippet } from "../hooks/index.js";
|
|
5
|
+
import { LINE_COLOR_STYLES, SIZE_LINE_HEIGHT_CLASSES } from "../lib/lineStyles.js";
|
|
6
|
+
import { FoldToggle } from "./FoldToggle.js";
|
|
7
|
+
const FoldColumn = ()=>{
|
|
8
|
+
const { visibleDisplayItems, folds, collapsedFolds, toggleFold, size, lines } = useCodeSnippet();
|
|
9
|
+
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
|
+
return /*#__PURE__*/ jsx("div", {
|
|
17
|
+
className: "flex flex-col select-none",
|
|
18
|
+
"data-slot": "code-snippet-fold",
|
|
19
|
+
children: visibleDisplayItems.map((item)=>{
|
|
20
|
+
if ('fold-summary' === item.type) return /*#__PURE__*/ jsx("span", {
|
|
21
|
+
className: cn(lineHeightClass, 'flex h-lh items-center justify-center px-4'),
|
|
22
|
+
children: /*#__PURE__*/ jsx(FoldToggle, {
|
|
23
|
+
fold: item.fold,
|
|
24
|
+
isCollapsed: true,
|
|
25
|
+
onToggle: ()=>toggleFold(item.fold.id)
|
|
26
|
+
})
|
|
27
|
+
}, `fold-${item.fold.id}`);
|
|
28
|
+
const fold = foldByStartLine.get(item.lineNumber);
|
|
29
|
+
const lineConfig = lines.get(item.lineNumber);
|
|
30
|
+
const colorStyles = lineConfig?.color ? LINE_COLOR_STYLES[lineConfig.color] : void 0;
|
|
31
|
+
return /*#__PURE__*/ jsx("span", {
|
|
32
|
+
className: cn(lineHeightClass, 'flex h-lh items-center justify-center px-4', colorStyles?.bg),
|
|
33
|
+
children: fold && !collapsedFolds.has(fold.id) ? /*#__PURE__*/ jsx(FoldToggle, {
|
|
34
|
+
fold: fold,
|
|
35
|
+
isCollapsed: false,
|
|
36
|
+
onToggle: ()=>toggleFold(fold.id)
|
|
37
|
+
}) : null
|
|
38
|
+
}, item.lineNumber);
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
export { FoldColumn };
|
|
@@ -0,0 +1,20 @@
|
|
|
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 };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDown } from "../../../icons/ChevronDown.js";
|
|
3
|
+
import { ChevronRight } from "../../../icons/ChevronRight.js";
|
|
4
|
+
import { getFoldSummaryLabel } from "../lib/foldUtils.js";
|
|
5
|
+
const FoldToggle = ({ fold, isCollapsed, onToggle })=>{
|
|
6
|
+
const lineCount = fold.endLine - fold.startLine + 1;
|
|
7
|
+
const label = getFoldSummaryLabel(fold, lineCount);
|
|
8
|
+
const ariaLabel = isCollapsed ? `Expand ${label}` : `Collapse ${label}`;
|
|
9
|
+
return /*#__PURE__*/ jsx("button", {
|
|
10
|
+
type: "button",
|
|
11
|
+
className: "flex items-center justify-center w-16 h-16 rounded-2 text-text-secondary hover:text-text-primary hover:bg-bg-secondary-hover transition-colors cursor-pointer",
|
|
12
|
+
"aria-expanded": !isCollapsed,
|
|
13
|
+
"aria-label": ariaLabel,
|
|
14
|
+
onClick: onToggle,
|
|
15
|
+
children: isCollapsed ? /*#__PURE__*/ jsx(ChevronRight, {}) : /*#__PURE__*/ jsx(ChevronDown, {})
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
FoldToggle.displayName = 'FoldToggle';
|
|
19
|
+
export { FoldToggle };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { FC } from 'react';
|
|
2
2
|
import type { LineConfig } from '../CodeSnippetContext';
|
|
3
|
+
import type { DisplayItem } from '../lib/foldUtils';
|
|
3
4
|
/** Prefix column component - renders all line prefixes in a separate column */
|
|
4
5
|
export declare const PrefixColumn: FC<{
|
|
5
|
-
|
|
6
|
-
startingLineNumber: number;
|
|
6
|
+
visibleDisplayItems: DisplayItem[];
|
|
7
7
|
lines: Map<number, LineConfig>;
|
|
8
8
|
lineHeightClass: string;
|
|
9
9
|
}>;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../../../utils/cn.js";
|
|
3
3
|
import { LINE_COLOR_STYLES } from "../lib/lineStyles.js";
|
|
4
|
-
const PrefixColumn = ({
|
|
4
|
+
const PrefixColumn = ({ visibleDisplayItems, lines, lineHeightClass })=>/*#__PURE__*/ jsx("div", {
|
|
5
5
|
className: "flex flex-col select-none",
|
|
6
6
|
"data-slot": "code-snippet-prefix",
|
|
7
|
-
children:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const lineConfig = lines.get(lineNumber);
|
|
7
|
+
children: visibleDisplayItems.map((item)=>{
|
|
8
|
+
if ('fold-summary' === item.type) return /*#__PURE__*/ jsx("span", {
|
|
9
|
+
className: cn(lineHeightClass, 'flex h-lh items-center justify-center px-8')
|
|
10
|
+
}, `fold-${item.fold.id}`);
|
|
11
|
+
const lineConfig = lines.get(item.lineNumber);
|
|
12
12
|
const colorStyles = lineConfig?.color ? LINE_COLOR_STYLES[lineConfig.color] : void 0;
|
|
13
13
|
return /*#__PURE__*/ jsx("span", {
|
|
14
|
-
className: cn(lineHeightClass, 'flex h-lh items-center justify-center px-8
|
|
14
|
+
className: cn(lineHeightClass, 'flex h-lh items-center justify-center px-8', colorStyles?.text, colorStyles?.bg),
|
|
15
15
|
children: lineConfig?.prefix
|
|
16
|
-
}, lineNumber);
|
|
16
|
+
}, item.lineNumber);
|
|
17
17
|
})
|
|
18
18
|
});
|
|
19
19
|
export { PrefixColumn };
|
|
@@ -5,8 +5,8 @@ import { Button } from "../../Button/index.js";
|
|
|
5
5
|
import { MIN_HIDDEN_LINES_THRESHOLD } from "../CodeSnippetContext.js";
|
|
6
6
|
import { useCodeSnippet } from "../hooks/index.js";
|
|
7
7
|
const ShowMoreButton = ()=>{
|
|
8
|
-
const {
|
|
9
|
-
const hiddenLines =
|
|
8
|
+
const { displayItems, maxLines, isExpanded, setIsExpanded } = useCodeSnippet();
|
|
9
|
+
const hiddenLines = displayItems.length - maxLines;
|
|
10
10
|
if (maxLines <= 0 || hiddenLines < MIN_HIDDEN_LINES_THRESHOLD) return null;
|
|
11
11
|
return /*#__PURE__*/ jsx("div", {
|
|
12
12
|
"data-slot": "code-snippet-show-more",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type FoldRegion = {
|
|
2
|
+
/** Unique identifier for this fold region */
|
|
3
|
+
id: string;
|
|
4
|
+
/** Inclusive start line number (1-based) */
|
|
5
|
+
startLine: number;
|
|
6
|
+
/** Inclusive end line number */
|
|
7
|
+
endLine: number;
|
|
8
|
+
/** Optional label for the collapsed summary. When omitted, auto-generated from line count. */
|
|
9
|
+
label?: string;
|
|
10
|
+
/** Whether the fold starts collapsed. Default: true */
|
|
11
|
+
defaultCollapsed?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export type DisplayItem = {
|
|
14
|
+
type: 'line';
|
|
15
|
+
index: number;
|
|
16
|
+
lineNumber: number;
|
|
17
|
+
} | {
|
|
18
|
+
type: 'fold-summary';
|
|
19
|
+
fold: FoldRegion;
|
|
20
|
+
lineCount: number;
|
|
21
|
+
};
|
|
22
|
+
export declare function getFoldSummaryLabel(fold: FoldRegion, lineCount: number): string;
|
|
23
|
+
/**
|
|
24
|
+
* Validates fold regions and returns a clean, sorted list.
|
|
25
|
+
* Dev: logs warnings for invalid folds. Prod: silently filters them out.
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateFolds(folds: FoldRegion[], totalLines: number, startingLineNumber?: number): FoldRegion[];
|
|
28
|
+
export declare function buildDisplayItems(totalLines: number, folds: FoldRegion[], collapsedFolds: Set<string>, startingLineNumber: number): DisplayItem[];
|