@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.
@@ -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 { FoldSummaryLine } from "./internal/FoldSummaryLine.js";
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 renderFoldSummary = (item)=>/*#__PURE__*/ jsx(FoldSummaryLine, {
13
- fold: item.fold,
14
- lineCount: item.lineCount,
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
- onToggle: ()=>toggleFold(item.fold.id)
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 { isSupported, copied, tooltipOpen, onTooltipOpenChange, handleCopy } = useCopyTooltip({
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 navigator.clipboard.writeText(code);
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)=>false !== f.defaultCollapsed).map((f)=>f.id));
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 { isSupported, copied, tooltipOpen, onTooltipOpenChange, handleCopy } = useCopyTooltip({
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: isCopyable
37
+ copyable
39
38
  }), className),
40
39
  ...otherProps,
41
40
  onClick: handleClick,
42
41
  children: code
43
42
  });
44
- if (!isCopyable) return codeElement;
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
- const CodeLine = ({ lineConfig, lineHeightClass, showInlineGutter = false, lineNumber, children })=>{
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, folds, collapsedFolds, toggleFold, size, lines } = useCodeSnippet();
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: true */
10
+ /** Whether the fold starts collapsed. Default: false */
11
11
  defaultCollapsed?: boolean;
12
12
  };
13
13
  export type DisplayItem = {
@@ -3,7 +3,6 @@ interface UseCopyTooltipOptions {
3
3
  enabled?: boolean;
4
4
  }
5
5
  interface UseCopyTooltipReturn {
6
- isSupported: boolean;
7
6
  copied: boolean;
8
7
  tooltipOpen: boolean;
9
8
  onTooltipOpenChange: (open: boolean) => void;
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
- const isClipboardSupported = "u" > typeof navigator && void 0 !== navigator.clipboard && 'function' == typeof navigator.clipboard.writeText;
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 || !isClipboardSupported) return;
21
- navigator.clipboard.writeText(text);
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.1",
3
- "generatedAt": "2026-04-13T08:49:49.893Z",
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, {\n headers: { defaultCollapsed: false },\n body: { defaultCollapsed: false },\n })}\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)",
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,2 @@
1
+ /** Copy text to clipboard using the Clipboard API with execCommand fallback. */
2
+ export declare function copyText(text: string): Promise<void>;
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@wallarm-org/design-system",
3
- "version": "0.23.2",
3
+ "version": "0.23.3",
4
4
  "description": "Core design system library with React components and Storybook documentation",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -1,8 +0,0 @@
1
- import type { FC } from 'react';
2
- import type { FoldRegion } from '../lib/foldUtils';
3
- export declare const FoldSummaryLine: FC<{
4
- fold: FoldRegion;
5
- lineCount: number;
6
- lineHeightClass: string;
7
- onToggle: () => void;
8
- }>;
@@ -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 };