@chayns-components/code-highlighter 5.5.3 → 5.5.8
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/AGENTS.md +37 -0
- package/lib/cjs/components/code-highlighter/CodeHighlighter.js +39 -23
- package/lib/cjs/components/code-highlighter/CodeHighlighter.js.map +1 -1
- package/lib/cjs/components/code-highlighter/CodeHighlighter.styles.js +9 -7
- package/lib/cjs/components/code-highlighter/CodeHighlighter.styles.js.map +1 -1
- package/lib/cjs/components/code-highlighter/CodeHighlighter.test.js +78 -0
- package/lib/cjs/components/code-highlighter/CodeHighlighter.test.js.map +1 -0
- package/lib/cjs/components/code-highlighter/copy-to-clipboard/CopyToClipboard.js +77 -24
- package/lib/cjs/components/code-highlighter/copy-to-clipboard/CopyToClipboard.js.map +1 -1
- package/lib/cjs/components/code-highlighter/copy-to-clipboard/CopyToClipboard.styles.js +86 -2
- package/lib/cjs/components/code-highlighter/copy-to-clipboard/CopyToClipboard.styles.js.map +1 -1
- package/lib/cjs/constants/textStrings.js +12 -0
- package/lib/cjs/constants/textStrings.js.map +1 -1
- package/lib/esm/components/code-highlighter/CodeHighlighter.js +40 -24
- package/lib/esm/components/code-highlighter/CodeHighlighter.js.map +1 -1
- package/lib/esm/components/code-highlighter/CodeHighlighter.styles.js +9 -7
- package/lib/esm/components/code-highlighter/CodeHighlighter.styles.js.map +1 -1
- package/lib/esm/components/code-highlighter/CodeHighlighter.test.js +74 -0
- package/lib/esm/components/code-highlighter/CodeHighlighter.test.js.map +1 -0
- package/lib/esm/components/code-highlighter/copy-to-clipboard/CopyToClipboard.js +77 -26
- package/lib/esm/components/code-highlighter/copy-to-clipboard/CopyToClipboard.js.map +1 -1
- package/lib/esm/components/code-highlighter/copy-to-clipboard/CopyToClipboard.styles.js +85 -1
- package/lib/esm/components/code-highlighter/copy-to-clipboard/CopyToClipboard.styles.js.map +1 -1
- package/lib/esm/constants/textStrings.js +12 -0
- package/lib/esm/constants/textStrings.js.map +1 -1
- package/lib/types/components/code-highlighter/copy-to-clipboard/CopyToClipboard.styles.d.ts +21 -1
- package/lib/types/constants/textStrings.d.ts +12 -0
- package/package.json +11 -6
|
@@ -3,13 +3,97 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.StyledCopyToClipboardText = exports.StyledCopyToClipboard = void 0;
|
|
6
|
+
exports.StyledCopyToClipboardText = exports.StyledCopyToClipboardButton = exports.StyledCopyToClipboardActionGroup = exports.StyledCopyToClipboard = void 0;
|
|
7
7
|
var _styledComponents = _interopRequireDefault(require("styled-components"));
|
|
8
8
|
var _codeHighlighter = require("../../../types/codeHighlighter");
|
|
9
9
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
10
|
const StyledCopyToClipboard = exports.StyledCopyToClipboard = _styledComponents.default.div`
|
|
11
|
+
position: sticky;
|
|
12
|
+
top: var(--code-highlighter-action-inset);
|
|
13
|
+
z-index: 1;
|
|
11
14
|
display: flex;
|
|
12
|
-
|
|
15
|
+
justify-content: flex-end;
|
|
16
|
+
height: calc(var(--code-highlighter-action-size) + var(--code-highlighter-action-inset));
|
|
17
|
+
margin-top: calc(
|
|
18
|
+
(var(--code-highlighter-action-size) + var(--code-highlighter-action-inset)) * -1
|
|
19
|
+
);
|
|
20
|
+
padding-right: var(--code-highlighter-action-inset);
|
|
21
|
+
`;
|
|
22
|
+
const getActionGroupBorder = ({
|
|
23
|
+
$codeTheme,
|
|
24
|
+
$isSticky
|
|
25
|
+
}) => {
|
|
26
|
+
if (!$isSticky) {
|
|
27
|
+
return 'transparent';
|
|
28
|
+
}
|
|
29
|
+
return $codeTheme === _codeHighlighter.CodeHighlighterTheme.Dark ? '#5a6474' : '#d4d4d4';
|
|
30
|
+
};
|
|
31
|
+
const getActionGroupBackground = ({
|
|
32
|
+
$codeTheme,
|
|
33
|
+
$isSticky
|
|
34
|
+
}) => {
|
|
35
|
+
if (!$isSticky) {
|
|
36
|
+
return 'transparent';
|
|
37
|
+
}
|
|
38
|
+
return $codeTheme === _codeHighlighter.CodeHighlighterTheme.Dark ? '#343a46' : '#fafafa';
|
|
39
|
+
};
|
|
40
|
+
const getButtonHoverBackground = ({
|
|
41
|
+
$codeTheme
|
|
42
|
+
}) => $codeTheme === _codeHighlighter.CodeHighlighterTheme.Dark ? '#454c59' : 'rgba(128, 128, 128, 0.2)';
|
|
43
|
+
const getButtonActiveBackground = ({
|
|
44
|
+
$codeTheme
|
|
45
|
+
}) => $codeTheme === _codeHighlighter.CodeHighlighterTheme.Dark ? '#545d6d' : 'rgba(128, 128, 128, 0.3)';
|
|
46
|
+
const getButtonHoverBorder = ({
|
|
47
|
+
$codeTheme
|
|
48
|
+
}) => $codeTheme === _codeHighlighter.CodeHighlighterTheme.Dark ? '#6a7485' : '#c2c2c2';
|
|
49
|
+
const getButtonShadow = ({
|
|
50
|
+
$isSticky
|
|
51
|
+
}) => $isSticky ? '0 2px 8px rgba(0, 0, 0, 0.28)' : 'none';
|
|
52
|
+
const getButtonHoverShadow = ({
|
|
53
|
+
$isSticky
|
|
54
|
+
}) => {
|
|
55
|
+
const insetShadow = 'inset 0 0 0 1px rgba(255, 255, 255, 0.08)';
|
|
56
|
+
return $isSticky ? `0 2px 8px rgba(0, 0, 0, 0.32), ${insetShadow}` : insetShadow;
|
|
57
|
+
};
|
|
58
|
+
const StyledCopyToClipboardActionGroup = exports.StyledCopyToClipboardActionGroup = _styledComponents.default.div`
|
|
59
|
+
display: flex;
|
|
60
|
+
gap: 4px;
|
|
61
|
+
height: var(--code-highlighter-action-size);
|
|
62
|
+
`;
|
|
63
|
+
const StyledCopyToClipboardButton = exports.StyledCopyToClipboardButton = _styledComponents.default.button`
|
|
64
|
+
box-sizing: border-box;
|
|
65
|
+
border: 1px solid ${getActionGroupBorder};
|
|
66
|
+
border-radius: 4px;
|
|
67
|
+
width: var(--code-highlighter-action-size);
|
|
68
|
+
height: var(--code-highlighter-action-size);
|
|
69
|
+
padding: 0;
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
background-color: ${getActionGroupBackground};
|
|
72
|
+
color: ${({
|
|
73
|
+
$codeTheme
|
|
74
|
+
}) => $codeTheme === _codeHighlighter.CodeHighlighterTheme.Dark ? '#f4f6f8' : '#999999'};
|
|
75
|
+
box-shadow: ${getButtonShadow};
|
|
76
|
+
transition:
|
|
77
|
+
background-color 0.15s ease,
|
|
78
|
+
border-color 0.15s ease,
|
|
79
|
+
box-shadow 0.15s ease,
|
|
80
|
+
transform 0.15s ease;
|
|
81
|
+
|
|
82
|
+
&:hover {
|
|
83
|
+
border-color: ${getButtonHoverBorder};
|
|
84
|
+
background-color: ${getButtonHoverBackground};
|
|
85
|
+
box-shadow: ${getButtonHoverShadow};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
&:active {
|
|
89
|
+
background-color: ${getButtonActiveBackground};
|
|
90
|
+
transform: scale(0.94);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
&:focus-visible {
|
|
94
|
+
outline: 2px solid currentColor;
|
|
95
|
+
outline-offset: 2px;
|
|
96
|
+
}
|
|
13
97
|
`;
|
|
14
98
|
const StyledCopyToClipboardText = exports.StyledCopyToClipboardText = _styledComponents.default.span`
|
|
15
99
|
color: ${({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopyToClipboard.styles.js","names":["_styledComponents","_interopRequireDefault","require","_codeHighlighter","e","__esModule","default","StyledCopyToClipboard","exports","styled","div","
|
|
1
|
+
{"version":3,"file":"CopyToClipboard.styles.js","names":["_styledComponents","_interopRequireDefault","require","_codeHighlighter","e","__esModule","default","StyledCopyToClipboard","exports","styled","div","getActionGroupBorder","$codeTheme","$isSticky","CodeHighlighterTheme","Dark","getActionGroupBackground","getButtonHoverBackground","getButtonActiveBackground","getButtonHoverBorder","getButtonShadow","getButtonHoverShadow","insetShadow","StyledCopyToClipboardActionGroup","StyledCopyToClipboardButton","button","StyledCopyToClipboardText","span"],"sources":["../../../../../src/components/code-highlighter/copy-to-clipboard/CopyToClipboard.styles.ts"],"sourcesContent":["import type { WithTheme } from '@chayns-components/core';\nimport styled from 'styled-components';\nimport { CodeHighlighterTheme } from '../../../types/codeHighlighter';\n\ntype StyledCopyToClipboardProps = WithTheme<{\n $codeTheme: CodeHighlighterTheme;\n}>;\n\nexport const StyledCopyToClipboard = styled.div<StyledCopyToClipboardProps>`\n position: sticky;\n top: var(--code-highlighter-action-inset);\n z-index: 1;\n display: flex;\n justify-content: flex-end;\n height: calc(var(--code-highlighter-action-size) + var(--code-highlighter-action-inset));\n margin-top: calc(\n (var(--code-highlighter-action-size) + var(--code-highlighter-action-inset)) * -1\n );\n padding-right: var(--code-highlighter-action-inset);\n`;\n\ntype StyledCopyToClipboardButtonProps = WithTheme<{\n $codeTheme: CodeHighlighterTheme;\n $isSticky: boolean;\n}>;\n\nconst getActionGroupBorder = ({ $codeTheme, $isSticky }: StyledCopyToClipboardButtonProps) => {\n if (!$isSticky) {\n return 'transparent';\n }\n\n return $codeTheme === CodeHighlighterTheme.Dark ? '#5a6474' : '#d4d4d4';\n};\n\nconst getActionGroupBackground = ({ $codeTheme, $isSticky }: StyledCopyToClipboardButtonProps) => {\n if (!$isSticky) {\n return 'transparent';\n }\n\n return $codeTheme === CodeHighlighterTheme.Dark ? '#343a46' : '#fafafa';\n};\n\nconst getButtonHoverBackground = ({ $codeTheme }: StyledCopyToClipboardButtonProps) =>\n $codeTheme === CodeHighlighterTheme.Dark ? '#454c59' : 'rgba(128, 128, 128, 0.2)';\n\nconst getButtonActiveBackground = ({ $codeTheme }: StyledCopyToClipboardButtonProps) =>\n $codeTheme === CodeHighlighterTheme.Dark ? '#545d6d' : 'rgba(128, 128, 128, 0.3)';\n\nconst getButtonHoverBorder = ({ $codeTheme }: StyledCopyToClipboardButtonProps) =>\n $codeTheme === CodeHighlighterTheme.Dark ? '#6a7485' : '#c2c2c2';\n\nconst getButtonShadow = ({ $isSticky }: StyledCopyToClipboardButtonProps) =>\n $isSticky ? '0 2px 8px rgba(0, 0, 0, 0.28)' : 'none';\n\nconst getButtonHoverShadow = ({ $isSticky }: StyledCopyToClipboardButtonProps) => {\n const insetShadow = 'inset 0 0 0 1px rgba(255, 255, 255, 0.08)';\n\n return $isSticky ? `0 2px 8px rgba(0, 0, 0, 0.32), ${insetShadow}` : insetShadow;\n};\n\nexport const StyledCopyToClipboardActionGroup = styled.div`\n display: flex;\n gap: 4px;\n height: var(--code-highlighter-action-size);\n`;\n\nexport const StyledCopyToClipboardButton = styled.button<StyledCopyToClipboardButtonProps>`\n box-sizing: border-box;\n border: 1px solid ${getActionGroupBorder};\n border-radius: 4px;\n width: var(--code-highlighter-action-size);\n height: var(--code-highlighter-action-size);\n padding: 0;\n cursor: pointer;\n background-color: ${getActionGroupBackground};\n color: ${({ $codeTheme }) =>\n $codeTheme === CodeHighlighterTheme.Dark ? '#f4f6f8' : '#999999'};\n box-shadow: ${getButtonShadow};\n transition:\n background-color 0.15s ease,\n border-color 0.15s ease,\n box-shadow 0.15s ease,\n transform 0.15s ease;\n\n &:hover {\n border-color: ${getButtonHoverBorder};\n background-color: ${getButtonHoverBackground};\n box-shadow: ${getButtonHoverShadow};\n }\n\n &:active {\n background-color: ${getButtonActiveBackground};\n transform: scale(0.94);\n }\n\n &:focus-visible {\n outline: 2px solid currentColor;\n outline-offset: 2px;\n }\n`;\n\ntype StyledCopyToClipboardTextProps = WithTheme<{\n $codeTheme: CodeHighlighterTheme;\n}>;\nexport const StyledCopyToClipboardText = styled.span<StyledCopyToClipboardTextProps>`\n color: ${({ $codeTheme }) =>\n $codeTheme === CodeHighlighterTheme.Dark ? '#e5e5e5' : '#999999'};\n font-size: small;\n margin-left: 4px;\n`;\n"],"mappings":";;;;;;AACA,IAAAA,iBAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,gBAAA,GAAAD,OAAA;AAAsE,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAM/D,MAAMG,qBAAqB,GAAAC,OAAA,CAAAD,qBAAA,GAAGE,yBAAM,CAACC,GAA+B;AAC3E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AAOD,MAAMC,oBAAoB,GAAGA,CAAC;EAAEC,UAAU;EAAEC;AAA4C,CAAC,KAAK;EAC1F,IAAI,CAACA,SAAS,EAAE;IACZ,OAAO,aAAa;EACxB;EAEA,OAAOD,UAAU,KAAKE,qCAAoB,CAACC,IAAI,GAAG,SAAS,GAAG,SAAS;AAC3E,CAAC;AAED,MAAMC,wBAAwB,GAAGA,CAAC;EAAEJ,UAAU;EAAEC;AAA4C,CAAC,KAAK;EAC9F,IAAI,CAACA,SAAS,EAAE;IACZ,OAAO,aAAa;EACxB;EAEA,OAAOD,UAAU,KAAKE,qCAAoB,CAACC,IAAI,GAAG,SAAS,GAAG,SAAS;AAC3E,CAAC;AAED,MAAME,wBAAwB,GAAGA,CAAC;EAAEL;AAA6C,CAAC,KAC9EA,UAAU,KAAKE,qCAAoB,CAACC,IAAI,GAAG,SAAS,GAAG,0BAA0B;AAErF,MAAMG,yBAAyB,GAAGA,CAAC;EAAEN;AAA6C,CAAC,KAC/EA,UAAU,KAAKE,qCAAoB,CAACC,IAAI,GAAG,SAAS,GAAG,0BAA0B;AAErF,MAAMI,oBAAoB,GAAGA,CAAC;EAAEP;AAA6C,CAAC,KAC1EA,UAAU,KAAKE,qCAAoB,CAACC,IAAI,GAAG,SAAS,GAAG,SAAS;AAEpE,MAAMK,eAAe,GAAGA,CAAC;EAAEP;AAA4C,CAAC,KACpEA,SAAS,GAAG,+BAA+B,GAAG,MAAM;AAExD,MAAMQ,oBAAoB,GAAGA,CAAC;EAAER;AAA4C,CAAC,KAAK;EAC9E,MAAMS,WAAW,GAAG,2CAA2C;EAE/D,OAAOT,SAAS,GAAG,kCAAkCS,WAAW,EAAE,GAAGA,WAAW;AACpF,CAAC;AAEM,MAAMC,gCAAgC,GAAAf,OAAA,CAAAe,gCAAA,GAAGd,yBAAM,CAACC,GAAG;AAC1D;AACA;AACA;AACA,CAAC;AAEM,MAAMc,2BAA2B,GAAAhB,OAAA,CAAAgB,2BAAA,GAAGf,yBAAM,CAACgB,MAAwC;AAC1F;AACA,wBAAwBd,oBAAoB;AAC5C;AACA;AACA;AACA;AACA;AACA,wBAAwBK,wBAAwB;AAChD,aAAa,CAAC;EAAEJ;AAAW,CAAC,KACpBA,UAAU,KAAKE,qCAAoB,CAACC,IAAI,GAAG,SAAS,GAAG,SAAS;AACxE,kBAAkBK,eAAe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,wBAAwBD,oBAAoB;AAC5C,4BAA4BF,wBAAwB;AACpD,sBAAsBI,oBAAoB;AAC1C;AACA;AACA;AACA,4BAA4BH,yBAAyB;AACrD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AAKM,MAAMQ,yBAAyB,GAAAlB,OAAA,CAAAkB,yBAAA,GAAGjB,yBAAM,CAACkB,IAAoC;AACpF,aAAa,CAAC;EAAEf;AAAW,CAAC,KACpBA,UAAU,KAAKE,qCAAoB,CAACC,IAAI,GAAG,SAAS,GAAG,SAAS;AACxE;AACA;AACA,CAAC","ignoreList":[]}
|
|
@@ -8,9 +8,21 @@ var _default = exports.default = {
|
|
|
8
8
|
components: {
|
|
9
9
|
codeHighlighter: {
|
|
10
10
|
copyToClipboard: {
|
|
11
|
+
copy: {
|
|
12
|
+
stringName: 'txt_chayns_components_code_highlighter_components_codeHighlighter_copyToClipboard_copy',
|
|
13
|
+
fallback: 'Kopieren'
|
|
14
|
+
},
|
|
11
15
|
copied: {
|
|
12
16
|
stringName: 'txt_chayns_components_code_highlighter_components_codeHighlighter_copyToClipboard_copied',
|
|
13
17
|
fallback: 'Kopiert!'
|
|
18
|
+
},
|
|
19
|
+
copyFailed: {
|
|
20
|
+
stringName: 'txt_chayns_components_code_highlighter_components_codeHighlighter_copyToClipboard_copyFailed',
|
|
21
|
+
fallback: 'Kopieren fehlgeschlagen'
|
|
22
|
+
},
|
|
23
|
+
share: {
|
|
24
|
+
stringName: 'txt_chayns_components_code_highlighter_components_codeHighlighter_copyToClipboard_share',
|
|
25
|
+
fallback: 'Teilen'
|
|
14
26
|
}
|
|
15
27
|
}
|
|
16
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textStrings.js","names":["components","codeHighlighter","copyToClipboard","
|
|
1
|
+
{"version":3,"file":"textStrings.js","names":["components","codeHighlighter","copyToClipboard","copy","stringName","fallback","copied","copyFailed","share"],"sources":["../../../src/constants/textStrings.ts"],"sourcesContent":["export default {\n components: {\n codeHighlighter: {\n copyToClipboard: {\n copy: {\n stringName:\n 'txt_chayns_components_code_highlighter_components_codeHighlighter_copyToClipboard_copy',\n fallback: 'Kopieren',\n },\n copied: {\n stringName:\n 'txt_chayns_components_code_highlighter_components_codeHighlighter_copyToClipboard_copied',\n fallback: 'Kopiert!',\n },\n copyFailed: {\n stringName:\n 'txt_chayns_components_code_highlighter_components_codeHighlighter_copyToClipboard_copyFailed',\n fallback: 'Kopieren fehlgeschlagen',\n },\n share: {\n stringName:\n 'txt_chayns_components_code_highlighter_components_codeHighlighter_copyToClipboard_share',\n fallback: 'Teilen',\n },\n },\n },\n },\n} as const;\n"],"mappings":";;;;;;iCAAe;EACXA,UAAU,EAAE;IACRC,eAAe,EAAE;MACbC,eAAe,EAAE;QACbC,IAAI,EAAE;UACFC,UAAU,EACN,wFAAwF;UAC5FC,QAAQ,EAAE;QACd,CAAC;QACDC,MAAM,EAAE;UACJF,UAAU,EACN,0FAA0F;UAC9FC,QAAQ,EAAE;QACd,CAAC;QACDE,UAAU,EAAE;UACRH,UAAU,EACN,8FAA8F;UAClGC,QAAQ,EAAE;QACd,CAAC;QACDG,KAAK,EAAE;UACHJ,UAAU,EACN,yFAAyF;UAC7FC,QAAQ,EAAE;QACd;MACJ;IACJ;EACJ;AACJ,CAAC","ignoreList":[]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { format } from 'prettier/standalone';
|
|
2
2
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
4
4
|
import { oneDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
|
5
|
+
import { useColorScheme } from '@chayns-components/core';
|
|
5
6
|
import { CodeHighlighterTheme } from '../../types/codeHighlighter';
|
|
6
7
|
import { formatLanguage, getParserForLanguage } from '../../utils/codeHighlighter';
|
|
7
8
|
import { StyledCodeHighlighter, StyledCodeHighlighterFileName, StyledCodeHighlighterHeader } from './CodeHighlighter.styles';
|
|
@@ -16,7 +17,7 @@ const addScrollbarClassToPre = ({
|
|
|
16
17
|
preElement.classList.add(className);
|
|
17
18
|
};
|
|
18
19
|
const CodeHighlighter = ({
|
|
19
|
-
theme
|
|
20
|
+
theme,
|
|
20
21
|
code,
|
|
21
22
|
copyButtonText,
|
|
22
23
|
language,
|
|
@@ -26,8 +27,11 @@ const CodeHighlighter = ({
|
|
|
26
27
|
shouldShowLineNumbers = false,
|
|
27
28
|
shouldWrapLines
|
|
28
29
|
}) => {
|
|
30
|
+
const colorScheme = useColorScheme();
|
|
29
31
|
const [width, setWidth] = useState(0);
|
|
32
|
+
const [formattedCode, setFormattedCode] = useState(code);
|
|
30
33
|
const ref = useRef(null);
|
|
34
|
+
const resolvedTheme = theme ?? (colorScheme?.theme.colorMode === 'dark' ? CodeHighlighterTheme.Dark : CodeHighlighterTheme.Light);
|
|
31
35
|
useEffect(() => {
|
|
32
36
|
addScrollbarClassToPre({
|
|
33
37
|
root: ref.current,
|
|
@@ -86,21 +90,32 @@ const CodeHighlighter = ({
|
|
|
86
90
|
const lineNumberStyle = useMemo(() => ({
|
|
87
91
|
WebkitUserSelect: 'none'
|
|
88
92
|
}), []);
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return code;
|
|
100
|
-
});
|
|
93
|
+
const syntaxHighlighterStyle = useMemo(() => ({
|
|
94
|
+
padding: '0 15px 15px'
|
|
95
|
+
}), []);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
let isCurrent = true;
|
|
98
|
+
if (!shouldFormatCode) {
|
|
99
|
+
setFormattedCode(code);
|
|
100
|
+
return () => {
|
|
101
|
+
isCurrent = false;
|
|
102
|
+
};
|
|
101
103
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
void getParserForLanguage(language).then(config => {
|
|
105
|
+
if (!config || !isCurrent) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
setFormattedCode(format(code, config));
|
|
110
|
+
} catch (error) {
|
|
111
|
+
onFormatError?.(error);
|
|
112
|
+
setFormattedCode(code);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
return () => {
|
|
116
|
+
isCurrent = false;
|
|
117
|
+
};
|
|
118
|
+
}, [code, language, onFormatError, shouldFormatCode]);
|
|
104
119
|
useEffect(() => {
|
|
105
120
|
const elements = document.getElementsByClassName('linenumber');
|
|
106
121
|
Array.from(elements).forEach(element => {
|
|
@@ -113,25 +128,26 @@ const CodeHighlighter = ({
|
|
|
113
128
|
}, []);
|
|
114
129
|
return useMemo(() => /*#__PURE__*/React.createElement(StyledCodeHighlighter, {
|
|
115
130
|
$shouldWrapLines: shouldWrapLines,
|
|
116
|
-
$codeTheme:
|
|
131
|
+
$codeTheme: resolvedTheme,
|
|
117
132
|
ref: ref
|
|
118
133
|
}, /*#__PURE__*/React.createElement(StyledCodeHighlighterHeader, {
|
|
119
|
-
$codeTheme:
|
|
134
|
+
$codeTheme: resolvedTheme
|
|
120
135
|
}, /*#__PURE__*/React.createElement(StyledCodeHighlighterFileName, {
|
|
121
|
-
$codeTheme:
|
|
122
|
-
}, /*#__PURE__*/React.createElement("tw-ignore", null, formatLanguage(language))), /*#__PURE__*/React.createElement(CopyToClipboard, {
|
|
136
|
+
$codeTheme: resolvedTheme
|
|
137
|
+
}, /*#__PURE__*/React.createElement("tw-ignore", null, formatLanguage(language)))), /*#__PURE__*/React.createElement(CopyToClipboard, {
|
|
123
138
|
text: code,
|
|
124
|
-
theme:
|
|
139
|
+
theme: resolvedTheme,
|
|
125
140
|
copyButtonText: copyButtonText
|
|
126
|
-
})
|
|
141
|
+
}), /*#__PURE__*/React.createElement(SyntaxHighlighter, {
|
|
142
|
+
customStyle: syntaxHighlighterStyle,
|
|
127
143
|
language: language ?? '',
|
|
128
144
|
lineNumberStyle: lineNumberStyle,
|
|
129
145
|
showLineNumbers: shouldShowLineNumbers,
|
|
130
|
-
style:
|
|
146
|
+
style: resolvedTheme === CodeHighlighterTheme.Dark ? oneDark : oneLight,
|
|
131
147
|
wrapLines: true,
|
|
132
148
|
wrapLongLines: shouldWrapLines,
|
|
133
149
|
lineProps: lineWrapper
|
|
134
|
-
}, formattedCode)), [shouldWrapLines,
|
|
150
|
+
}, formattedCode)), [shouldWrapLines, resolvedTheme, language, code, copyButtonText, lineNumberStyle, syntaxHighlighterStyle, shouldShowLineNumbers, lineWrapper, formattedCode]);
|
|
135
151
|
};
|
|
136
152
|
CodeHighlighter.displayName = 'CodeHighlighter';
|
|
137
153
|
export default CodeHighlighter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeHighlighter.js","names":["format","React","useCallback","useEffect","useMemo","useRef","useState","PrismAsyncLight","SyntaxHighlighter","oneDark","oneLight","CodeHighlighterTheme","formatLanguage","getParserForLanguage","StyledCodeHighlighter","StyledCodeHighlighterFileName","StyledCodeHighlighterHeader","CopyToClipboard","addScrollbarClassToPre","root","className","preElement","querySelector","classList","add","CodeHighlighter","theme","Dark","code","copyButtonText","language","highlightedLines","shouldFormatCode","onFormatError","shouldShowLineNumbers","shouldWrapLines","width","setWidth","ref","current","handleResize","children","Array","from","find","tagName","toLowerCase","scrollWidth","window","addEventListener","removeEventListener","lineWrapper","lineNumber","style","backgroundColor","display","borderRadius","added","includes","removed","marked","lineNumberStyle","WebkitUserSelect","formattedCode","then","config","error","elements","document","getElementsByClassName","forEach","element","wrapper","createElement","firstChild","appendChild","$shouldWrapLines","$codeTheme","text","showLineNumbers","wrapLines","wrapLongLines","lineProps","displayName"],"sources":["../../../../src/components/code-highlighter/CodeHighlighter.tsx"],"sourcesContent":["import { format } from 'prettier/standalone';\nimport React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { oneDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism';\nimport {\n CodeHighlighterLanguage,\n CodeHighlighterTheme,\n HighlightedLines,\n} from '../../types/codeHighlighter';\nimport { formatLanguage, getParserForLanguage } from '../../utils/codeHighlighter';\nimport {\n StyledCodeHighlighter,\n StyledCodeHighlighterFileName,\n StyledCodeHighlighterHeader,\n} from './CodeHighlighter.styles';\nimport CopyToClipboard from './copy-to-clipboard/CopyToClipboard';\n\ntype AddScrollbarClassOptions = {\n root: HTMLDivElement | null;\n className: string;\n};\n\nconst addScrollbarClassToPre = ({ root, className }: AddScrollbarClassOptions) => {\n if (!root) return;\n\n const preElement = root.querySelector('pre');\n if (!preElement) return;\n\n preElement.classList.add(className);\n};\n\nexport type CodeHighlighterProps = {\n /**\n * The code that should be displayed.\n */\n code: string;\n /**\n * The text that should be displayed after the copy button.\n * If not set, just the button is displayed without text.\n */\n copyButtonText?: string;\n /**\n * The lines of code that should be highlighted.\n * Following lines can be highlighted: added, removed and just marked.\n */\n highlightedLines?: HighlightedLines;\n /**\n * The language of the displayed code.\n */\n language: CodeHighlighterLanguage;\n /**\n * Function to be executed when the formatting of the code fails.\n */\n onFormatError?: (error: unknown) => void;\n /**\n * Whether the code should be formatted with prettier.\n */\n shouldFormatCode?: boolean;\n /**\n * Whether the line numbers should be displayed.\n */\n shouldShowLineNumbers?: boolean;\n /**\n * Whether long lines should be wrapped.\n */\n shouldWrapLines?: boolean;\n /**\n * The theme of the code block. Decide between dark and light.\n */\n theme?: CodeHighlighterTheme;\n};\n\nconst CodeHighlighter: FC<CodeHighlighterProps> = ({\n theme = CodeHighlighterTheme.Dark,\n code,\n copyButtonText,\n language,\n highlightedLines,\n shouldFormatCode = false,\n onFormatError,\n shouldShowLineNumbers = false,\n shouldWrapLines,\n}) => {\n const [width, setWidth] = useState(0);\n\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n addScrollbarClassToPre({ root: ref.current, className: 'chayns-scrollbar' });\n }, []);\n\n useEffect(() => {\n const handleResize = () => {\n if (ref.current) {\n const { children } = ref.current;\n\n const preElement = Array.from(children).find(\n ({ tagName }) => tagName.toLowerCase() === 'pre',\n );\n\n if (preElement) {\n setWidth(preElement.scrollWidth);\n }\n }\n };\n\n handleResize();\n\n window.addEventListener('resize', handleResize);\n\n return () => {\n window.removeEventListener('resize', handleResize);\n };\n }, []);\n\n // function to style highlighted code\n const lineWrapper = useCallback(\n (lineNumber: number) => {\n let style = {\n backgroundColor: 'none',\n display: 'block',\n borderRadius: '2px',\n width: width - 15,\n };\n\n if (highlightedLines?.added && highlightedLines.added.includes(lineNumber)) {\n style = { ...style, backgroundColor: '#2EF29930' };\n } else if (highlightedLines?.removed && highlightedLines.removed.includes(lineNumber)) {\n style = { ...style, backgroundColor: '#F22E5B30' };\n } else if (highlightedLines?.marked && highlightedLines.marked.includes(lineNumber)) {\n style = { ...style, backgroundColor: '#cccccc40' };\n }\n\n return { style };\n },\n [highlightedLines, width],\n );\n\n const lineNumberStyle = useMemo(\n () => ({\n WebkitUserSelect: 'none' as const,\n }),\n [],\n );\n\n const formattedCode = useMemo(() => {\n if (language) {\n void getParserForLanguage(language).then((config) => {\n if (shouldFormatCode && config) {\n try {\n return format(code, config) as unknown as string;\n } catch (error) {\n if (typeof onFormatError !== 'undefined') onFormatError(error);\n }\n }\n\n return code;\n });\n }\n\n return code;\n }, [code, language, shouldFormatCode, onFormatError]);\n\n useEffect(() => {\n const elements = document.getElementsByClassName('linenumber');\n\n Array.from(elements).forEach((element) => {\n const wrapper = document.createElement('tw-ignore');\n\n while (element.firstChild) {\n wrapper.appendChild(element.firstChild);\n }\n\n element.appendChild(wrapper);\n });\n }, []);\n\n return useMemo(\n () => (\n <StyledCodeHighlighter $shouldWrapLines={shouldWrapLines} $codeTheme={theme} ref={ref}>\n <StyledCodeHighlighterHeader $codeTheme={theme}>\n <StyledCodeHighlighterFileName $codeTheme={theme}>\n {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}\n {/* @ts-ignore */}\n <tw-ignore>{formatLanguage(language)}</tw-ignore>\n </StyledCodeHighlighterFileName>\n <CopyToClipboard text={code} theme={theme} copyButtonText={copyButtonText} />\n </StyledCodeHighlighterHeader>\n <SyntaxHighlighter\n language={language ?? ''}\n lineNumberStyle={lineNumberStyle}\n showLineNumbers={shouldShowLineNumbers}\n style={theme === CodeHighlighterTheme.Dark ? oneDark : oneLight}\n wrapLines\n wrapLongLines={shouldWrapLines}\n lineProps={lineWrapper}\n >\n {formattedCode}\n </SyntaxHighlighter>\n </StyledCodeHighlighter>\n ),\n [\n shouldWrapLines,\n theme,\n language,\n code,\n copyButtonText,\n lineNumberStyle,\n shouldShowLineNumbers,\n lineWrapper,\n formattedCode,\n ],\n );\n};\n\nCodeHighlighter.displayName = 'CodeHighlighter';\n\nexport default CodeHighlighter;\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"CodeHighlighter.js","names":["format","React","useCallback","useEffect","useMemo","useRef","useState","Prism","SyntaxHighlighter","oneDark","oneLight","useColorScheme","CodeHighlighterTheme","formatLanguage","getParserForLanguage","StyledCodeHighlighter","StyledCodeHighlighterFileName","StyledCodeHighlighterHeader","CopyToClipboard","addScrollbarClassToPre","root","className","preElement","querySelector","classList","add","CodeHighlighter","theme","code","copyButtonText","language","highlightedLines","shouldFormatCode","onFormatError","shouldShowLineNumbers","shouldWrapLines","colorScheme","width","setWidth","formattedCode","setFormattedCode","ref","resolvedTheme","colorMode","Dark","Light","current","handleResize","children","Array","from","find","tagName","toLowerCase","scrollWidth","window","addEventListener","removeEventListener","lineWrapper","lineNumber","style","backgroundColor","display","borderRadius","added","includes","removed","marked","lineNumberStyle","WebkitUserSelect","syntaxHighlighterStyle","padding","isCurrent","then","config","error","elements","document","getElementsByClassName","forEach","element","wrapper","createElement","firstChild","appendChild","$shouldWrapLines","$codeTheme","text","customStyle","showLineNumbers","wrapLines","wrapLongLines","lineProps","displayName"],"sources":["../../../../src/components/code-highlighter/CodeHighlighter.tsx"],"sourcesContent":["import { format } from 'prettier/standalone';\nimport React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { oneDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism';\nimport { useColorScheme } from '@chayns-components/core';\nimport {\n CodeHighlighterLanguage,\n CodeHighlighterTheme,\n HighlightedLines,\n} from '../../types/codeHighlighter';\nimport { formatLanguage, getParserForLanguage } from '../../utils/codeHighlighter';\nimport {\n StyledCodeHighlighter,\n StyledCodeHighlighterFileName,\n StyledCodeHighlighterHeader,\n} from './CodeHighlighter.styles';\nimport CopyToClipboard from './copy-to-clipboard/CopyToClipboard';\n\ntype AddScrollbarClassOptions = {\n root: HTMLDivElement | null;\n className: string;\n};\n\nconst addScrollbarClassToPre = ({ root, className }: AddScrollbarClassOptions) => {\n if (!root) return;\n\n const preElement = root.querySelector('pre');\n if (!preElement) return;\n\n preElement.classList.add(className);\n};\n\nexport type CodeHighlighterProps = {\n /**\n * The code that should be displayed.\n */\n code: string;\n /**\n * The text that should be displayed after the copy button.\n * If not set, just the button is displayed without text.\n */\n copyButtonText?: string;\n /**\n * The lines of code that should be highlighted.\n * Following lines can be highlighted: added, removed and just marked.\n */\n highlightedLines?: HighlightedLines;\n /**\n * The language of the displayed code.\n */\n language: CodeHighlighterLanguage;\n /**\n * Function to be executed when the formatting of the code fails.\n */\n onFormatError?: (error: unknown) => void;\n /**\n * Whether the code should be formatted with prettier.\n */\n shouldFormatCode?: boolean;\n /**\n * Whether the line numbers should be displayed.\n */\n shouldShowLineNumbers?: boolean;\n /**\n * Whether long lines should be wrapped.\n */\n shouldWrapLines?: boolean;\n /**\n * The theme of the code block. Decide between dark and light.\n */\n theme?: CodeHighlighterTheme;\n};\n\nconst CodeHighlighter: FC<CodeHighlighterProps> = ({\n theme,\n code,\n copyButtonText,\n language,\n highlightedLines,\n shouldFormatCode = false,\n onFormatError,\n shouldShowLineNumbers = false,\n shouldWrapLines,\n}) => {\n const colorScheme = useColorScheme();\n\n const [width, setWidth] = useState(0);\n const [formattedCode, setFormattedCode] = useState(code);\n\n const ref = useRef<HTMLDivElement>(null);\n\n const resolvedTheme =\n theme ??\n (colorScheme?.theme.colorMode === 'dark'\n ? CodeHighlighterTheme.Dark\n : CodeHighlighterTheme.Light);\n\n useEffect(() => {\n addScrollbarClassToPre({ root: ref.current, className: 'chayns-scrollbar' });\n }, []);\n\n useEffect(() => {\n const handleResize = () => {\n if (ref.current) {\n const { children } = ref.current;\n\n const preElement = Array.from(children).find(\n ({ tagName }) => tagName.toLowerCase() === 'pre',\n );\n\n if (preElement) {\n setWidth(preElement.scrollWidth);\n }\n }\n };\n\n handleResize();\n\n window.addEventListener('resize', handleResize);\n\n return () => {\n window.removeEventListener('resize', handleResize);\n };\n }, []);\n\n // function to style highlighted code\n const lineWrapper = useCallback(\n (lineNumber: number) => {\n let style = {\n backgroundColor: 'none',\n display: 'block',\n borderRadius: '2px',\n width: width - 15,\n };\n\n if (highlightedLines?.added && highlightedLines.added.includes(lineNumber)) {\n style = { ...style, backgroundColor: '#2EF29930' };\n } else if (highlightedLines?.removed && highlightedLines.removed.includes(lineNumber)) {\n style = { ...style, backgroundColor: '#F22E5B30' };\n } else if (highlightedLines?.marked && highlightedLines.marked.includes(lineNumber)) {\n style = { ...style, backgroundColor: '#cccccc40' };\n }\n\n return { style };\n },\n [highlightedLines, width],\n );\n\n const lineNumberStyle = useMemo(\n () => ({\n WebkitUserSelect: 'none' as const,\n }),\n [],\n );\n\n const syntaxHighlighterStyle = useMemo(\n () => ({\n padding: '0 15px 15px',\n }),\n [],\n );\n\n useEffect(() => {\n let isCurrent = true;\n\n if (!shouldFormatCode) {\n setFormattedCode(code);\n\n return () => {\n isCurrent = false;\n };\n }\n\n void getParserForLanguage(language).then((config) => {\n if (!config || !isCurrent) {\n return;\n }\n\n try {\n setFormattedCode(format(code, config) as unknown as string);\n } catch (error) {\n onFormatError?.(error);\n setFormattedCode(code);\n }\n });\n\n return () => {\n isCurrent = false;\n };\n }, [code, language, onFormatError, shouldFormatCode]);\n\n useEffect(() => {\n const elements = document.getElementsByClassName('linenumber');\n\n Array.from(elements).forEach((element) => {\n const wrapper = document.createElement('tw-ignore');\n\n while (element.firstChild) {\n wrapper.appendChild(element.firstChild);\n }\n\n element.appendChild(wrapper);\n });\n }, []);\n\n return useMemo(\n () => (\n <StyledCodeHighlighter\n $shouldWrapLines={shouldWrapLines}\n $codeTheme={resolvedTheme}\n ref={ref}\n >\n <StyledCodeHighlighterHeader $codeTheme={resolvedTheme}>\n <StyledCodeHighlighterFileName $codeTheme={resolvedTheme}>\n {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}\n {/* @ts-ignore */}\n <tw-ignore>{formatLanguage(language)}</tw-ignore>\n </StyledCodeHighlighterFileName>\n </StyledCodeHighlighterHeader>\n <CopyToClipboard\n text={code}\n theme={resolvedTheme}\n copyButtonText={copyButtonText}\n />\n <SyntaxHighlighter\n customStyle={syntaxHighlighterStyle}\n language={language ?? ''}\n lineNumberStyle={lineNumberStyle}\n showLineNumbers={shouldShowLineNumbers}\n style={resolvedTheme === CodeHighlighterTheme.Dark ? oneDark : oneLight}\n wrapLines\n wrapLongLines={shouldWrapLines}\n lineProps={lineWrapper}\n >\n {formattedCode}\n </SyntaxHighlighter>\n </StyledCodeHighlighter>\n ),\n [\n shouldWrapLines,\n resolvedTheme,\n language,\n code,\n copyButtonText,\n lineNumberStyle,\n syntaxHighlighterStyle,\n shouldShowLineNumbers,\n lineWrapper,\n formattedCode,\n ],\n );\n};\n\nCodeHighlighter.displayName = 'CodeHighlighter';\n\nexport default CodeHighlighter;\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -6,12 +6,17 @@ export const StyledCodeHighlighter = styled.div`
|
|
|
6
6
|
$codeTheme
|
|
7
7
|
}) => $codeTheme === CodeHighlighterTheme.Dark ? '#282c34' : '#fafafa'};
|
|
8
8
|
border-radius: 8px;
|
|
9
|
+
--code-highlighter-action-size: 32px;
|
|
10
|
+
--code-highlighter-action-inset: 8px;
|
|
9
11
|
padding-bottom: 6px;
|
|
12
|
+
padding-top: calc(
|
|
13
|
+
var(--code-highlighter-action-size) + var(--code-highlighter-action-inset) * 2
|
|
14
|
+
);
|
|
10
15
|
|
|
11
16
|
pre {
|
|
12
17
|
margin: 0 !important;
|
|
13
18
|
overflow: auto;
|
|
14
|
-
padding: 15px;
|
|
19
|
+
padding: 0 15px 15px;
|
|
15
20
|
line-height: 1.5;
|
|
16
21
|
|
|
17
22
|
code {
|
|
@@ -29,12 +34,9 @@ export const StyledCodeHighlighter = styled.div`
|
|
|
29
34
|
export const StyledCodeHighlighterHeader = styled.div`
|
|
30
35
|
display: flex;
|
|
31
36
|
align-items: center;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
$codeTheme
|
|
36
|
-
}) => $codeTheme === CodeHighlighterTheme.Dark ? '#e5e5e5' : '#999999'};
|
|
37
|
-
padding: 4px 12px;
|
|
37
|
+
padding: 0 12px;
|
|
38
|
+
height: 0;
|
|
39
|
+
transform: translateY(-26px);
|
|
38
40
|
`;
|
|
39
41
|
export const StyledCodeHighlighterFileName = styled.span`
|
|
40
42
|
color: ${({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeHighlighter.styles.js","names":["styled","CodeHighlighterTheme","StyledCodeHighlighter","div","$codeTheme","Dark","$shouldWrapLines","StyledCodeHighlighterHeader","StyledCodeHighlighterFileName","span"],"sources":["../../../../src/components/code-highlighter/CodeHighlighter.styles.ts"],"sourcesContent":["import type { WithTheme } from '@chayns-components/core';\nimport styled from 'styled-components';\nimport { CodeHighlighterTheme } from '../../types/codeHighlighter';\n\ntype StyledCodeHighlighterProps = WithTheme<{\n $codeTheme: CodeHighlighterTheme;\n $shouldWrapLines?: boolean;\n}>;\n\nexport const StyledCodeHighlighter = styled.div<StyledCodeHighlighterProps>`\n margin: 4px 0;\n background-color: ${({ $codeTheme }) =>\n $codeTheme === CodeHighlighterTheme.Dark ? '#282c34' : '#fafafa'};\n border-radius: 8px;\n padding-bottom: 6px;\n\n pre {\n margin: 0 !important;\n overflow: auto;\n padding: 15px;\n line-height: 1.5;\n\n code {\n white-space: ${({ $shouldWrapLines }) =>\n $shouldWrapLines ? 'pre-wrap' : 'pre'} !important;\n }\n }\n\n // Fixes display of tables in code highlighter for markdown.\n .language-markdown .token.table {\n display: inline;\n }\n`;\n\ntype StyledCodeHighlighterHeaderProps = WithTheme<{\n $codeTheme: CodeHighlighterTheme;\n}>;\n\nexport const StyledCodeHighlighterHeader = styled.div<StyledCodeHighlighterHeaderProps>`\n display: flex;\n align-items: center;\n
|
|
1
|
+
{"version":3,"file":"CodeHighlighter.styles.js","names":["styled","CodeHighlighterTheme","StyledCodeHighlighter","div","$codeTheme","Dark","$shouldWrapLines","StyledCodeHighlighterHeader","StyledCodeHighlighterFileName","span"],"sources":["../../../../src/components/code-highlighter/CodeHighlighter.styles.ts"],"sourcesContent":["import type { WithTheme } from '@chayns-components/core';\nimport styled from 'styled-components';\nimport { CodeHighlighterTheme } from '../../types/codeHighlighter';\n\ntype StyledCodeHighlighterProps = WithTheme<{\n $codeTheme: CodeHighlighterTheme;\n $shouldWrapLines?: boolean;\n}>;\n\nexport const StyledCodeHighlighter = styled.div<StyledCodeHighlighterProps>`\n margin: 4px 0;\n background-color: ${({ $codeTheme }) =>\n $codeTheme === CodeHighlighterTheme.Dark ? '#282c34' : '#fafafa'};\n border-radius: 8px;\n --code-highlighter-action-size: 32px;\n --code-highlighter-action-inset: 8px;\n padding-bottom: 6px;\n padding-top: calc(\n var(--code-highlighter-action-size) + var(--code-highlighter-action-inset) * 2\n );\n\n pre {\n margin: 0 !important;\n overflow: auto;\n padding: 0 15px 15px;\n line-height: 1.5;\n\n code {\n white-space: ${({ $shouldWrapLines }) =>\n $shouldWrapLines ? 'pre-wrap' : 'pre'} !important;\n }\n }\n\n // Fixes display of tables in code highlighter for markdown.\n .language-markdown .token.table {\n display: inline;\n }\n`;\n\ntype StyledCodeHighlighterHeaderProps = WithTheme<{\n $codeTheme: CodeHighlighterTheme;\n}>;\n\nexport const StyledCodeHighlighterHeader = styled.div<StyledCodeHighlighterHeaderProps>`\n display: flex;\n align-items: center;\n padding: 0 12px;\n height: 0;\n transform: translateY(-26px);\n`;\n\ntype StyledCodeHighlighterFileNameProps = WithTheme<{\n $codeTheme: CodeHighlighterTheme;\n}>;\n\nexport const StyledCodeHighlighterFileName = styled.span<StyledCodeHighlighterFileNameProps>`\n color: ${({ $codeTheme }) =>\n $codeTheme === CodeHighlighterTheme.Dark ? '#e5e5e5' : '#999999'};\n`;\n"],"mappings":"AACA,OAAOA,MAAM,MAAM,mBAAmB;AACtC,SAASC,oBAAoB,QAAQ,6BAA6B;AAOlE,OAAO,MAAMC,qBAAqB,GAAGF,MAAM,CAACG,GAA+B;AAC3E;AACA,wBAAwB,CAAC;EAAEC;AAAW,CAAC,KAC/BA,UAAU,KAAKH,oBAAoB,CAACI,IAAI,GAAG,SAAS,GAAG,SAAS;AACxE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,2BAA2B,CAAC;EAAEC;AAAiB,CAAC,KAChCA,gBAAgB,GAAG,UAAU,GAAG,KAAK;AACrD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AAMD,OAAO,MAAMC,2BAA2B,GAAGP,MAAM,CAACG,GAAqC;AACvF;AACA;AACA;AACA;AACA;AACA,CAAC;AAMD,OAAO,MAAMK,6BAA6B,GAAGR,MAAM,CAACS,IAAwC;AAC5F,aAAa,CAAC;EAAEL;AAAW,CAAC,KACpBA,UAAU,KAAKH,oBAAoB,CAACI,IAAI,GAAG,SAAS,GAAG,SAAS;AACxE,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
2
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { CodeHighlighterTheme } from '../../types/codeHighlighter';
|
|
6
|
+
import CodeHighlighter from './CodeHighlighter';
|
|
7
|
+
vi.mock('@chayns-components/core', () => ({
|
|
8
|
+
Icon: () => /*#__PURE__*/React.createElement("span", null),
|
|
9
|
+
SharingContextMenu: ({
|
|
10
|
+
children
|
|
11
|
+
}) => children,
|
|
12
|
+
useColorScheme: () => undefined
|
|
13
|
+
}));
|
|
14
|
+
vi.mock('@chayns-components/textstring', () => ({
|
|
15
|
+
ttsToITextString: value => value,
|
|
16
|
+
useTextstringValue: ({
|
|
17
|
+
textstring
|
|
18
|
+
}) => textstring.fallback
|
|
19
|
+
}));
|
|
20
|
+
vi.mock('chayns-api', () => ({
|
|
21
|
+
DialogType: {
|
|
22
|
+
TOAST: 1
|
|
23
|
+
},
|
|
24
|
+
ToastType: {
|
|
25
|
+
ERROR: 4,
|
|
26
|
+
SUCCESS: 2
|
|
27
|
+
},
|
|
28
|
+
createDialog: vi.fn(() => ({
|
|
29
|
+
open: vi.fn()
|
|
30
|
+
}))
|
|
31
|
+
}));
|
|
32
|
+
describe('CodeHighlighter', () => {
|
|
33
|
+
it('keeps the copy operation and renders sticky actions', () => {
|
|
34
|
+
const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();
|
|
35
|
+
const {
|
|
36
|
+
container
|
|
37
|
+
} = render(/*#__PURE__*/React.createElement(CodeHighlighter, {
|
|
38
|
+
code: "const value = true;",
|
|
39
|
+
copyButtonText: "Copy code",
|
|
40
|
+
language: "typescript",
|
|
41
|
+
shouldShowLineNumbers: true,
|
|
42
|
+
theme: CodeHighlighterTheme.Dark
|
|
43
|
+
}));
|
|
44
|
+
fireEvent.click(screen.getByRole('button', {
|
|
45
|
+
name: 'Copy code'
|
|
46
|
+
}));
|
|
47
|
+
expect(writeText).toHaveBeenCalledWith('const value = true;');
|
|
48
|
+
expect(container.querySelector('pre')).toHaveStyle({
|
|
49
|
+
overflow: 'auto',
|
|
50
|
+
padding: '0px 15px 15px'
|
|
51
|
+
});
|
|
52
|
+
expect(container.firstChild).toHaveStyle({
|
|
53
|
+
backgroundColor: 'rgb(40, 44, 52)'
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
it('formats code before highlighting when requested', async () => {
|
|
57
|
+
const {
|
|
58
|
+
container
|
|
59
|
+
} = render(/*#__PURE__*/React.createElement(CodeHighlighter, {
|
|
60
|
+
code: "const value={label:'Ada'};",
|
|
61
|
+
language: "typescript",
|
|
62
|
+
shouldFormatCode: true,
|
|
63
|
+
theme: CodeHighlighterTheme.Light
|
|
64
|
+
}));
|
|
65
|
+
await waitFor(() => {
|
|
66
|
+
expect(container.querySelector('pre')).toHaveTextContent("const value = { label: 'Ada' };");
|
|
67
|
+
});
|
|
68
|
+
expect(container.querySelector('pre span')).toBeInTheDocument();
|
|
69
|
+
expect(container.firstChild).toHaveStyle({
|
|
70
|
+
backgroundColor: 'rgb(250, 250, 250)'
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=CodeHighlighter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodeHighlighter.test.js","names":["fireEvent","render","screen","waitFor","React","describe","expect","it","vi","CodeHighlighterTheme","CodeHighlighter","mock","Icon","createElement","SharingContextMenu","children","useColorScheme","undefined","ttsToITextString","value","useTextstringValue","textstring","fallback","DialogType","TOAST","ToastType","ERROR","SUCCESS","createDialog","fn","open","writeText","spyOn","navigator","clipboard","mockResolvedValue","container","code","copyButtonText","language","shouldShowLineNumbers","theme","Dark","click","getByRole","name","toHaveBeenCalledWith","querySelector","toHaveStyle","overflow","padding","firstChild","backgroundColor","shouldFormatCode","Light","toHaveTextContent","toBeInTheDocument"],"sources":["../../../../src/components/code-highlighter/CodeHighlighter.test.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-call */\nimport { fireEvent, render, screen, waitFor } from '@testing-library/react';\nimport React from 'react';\nimport { describe, expect, it, vi } from 'vitest';\nimport { CodeHighlighterTheme } from '../../types/codeHighlighter';\nimport CodeHighlighter from './CodeHighlighter';\n\nvi.mock('@chayns-components/core', () => ({\n Icon: () => <span />,\n SharingContextMenu: ({ children }: { children: React.ReactNode }) => children,\n useColorScheme: () => undefined,\n}));\n\nvi.mock('@chayns-components/textstring', () => ({\n ttsToITextString: (value: unknown) => value,\n useTextstringValue: ({ textstring }: { textstring: { fallback: string } }) =>\n textstring.fallback,\n}));\n\nvi.mock('chayns-api', () => ({\n DialogType: { TOAST: 1 },\n ToastType: { ERROR: 4, SUCCESS: 2 },\n createDialog: vi.fn(() => ({ open: vi.fn() })),\n}));\n\ndescribe('CodeHighlighter', () => {\n it('keeps the copy operation and renders sticky actions', () => {\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n const { container } = render(\n <CodeHighlighter\n code=\"const value = true;\"\n copyButtonText=\"Copy code\"\n language=\"typescript\"\n shouldShowLineNumbers\n theme={CodeHighlighterTheme.Dark}\n />,\n );\n\n fireEvent.click(screen.getByRole('button', { name: 'Copy code' }));\n\n expect(writeText).toHaveBeenCalledWith('const value = true;');\n expect(container.querySelector('pre')).toHaveStyle({\n overflow: 'auto',\n padding: '0px 15px 15px',\n });\n expect(container.firstChild).toHaveStyle({ backgroundColor: 'rgb(40, 44, 52)' });\n });\n\n it('formats code before highlighting when requested', async () => {\n const { container } = render(\n <CodeHighlighter\n code=\"const value={label:'Ada'};\"\n language=\"typescript\"\n shouldFormatCode\n theme={CodeHighlighterTheme.Light}\n />,\n );\n\n await waitFor(() => {\n expect(container.querySelector('pre')).toHaveTextContent(\n \"const value = { label: 'Ada' };\",\n );\n });\n expect(container.querySelector('pre span')).toBeInTheDocument();\n expect(container.firstChild).toHaveStyle({ backgroundColor: 'rgb(250, 250, 250)' });\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,38 +1,89 @@
|
|
|
1
|
-
import { Icon,
|
|
2
|
-
import
|
|
1
|
+
import { Icon, SharingContextMenu } from '@chayns-components/core';
|
|
2
|
+
import { ttsToITextString, useTextstringValue } from '@chayns-components/textstring';
|
|
3
|
+
import { createDialog, DialogType, ToastType } from 'chayns-api';
|
|
4
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
5
|
import { CodeHighlighterTheme } from '../../../types/codeHighlighter';
|
|
4
|
-
import { StyledCopyToClipboard,
|
|
5
|
-
import { Textstring, TextstringProvider, ttsToITextString } from '@chayns-components/textstring';
|
|
6
|
+
import { StyledCopyToClipboard, StyledCopyToClipboardActionGroup, StyledCopyToClipboardButton } from './CopyToClipboard.styles';
|
|
6
7
|
import textStrings from '../../../constants/textStrings';
|
|
8
|
+
const STICKY_ACTION_OFFSET = 8;
|
|
7
9
|
const CopyToClipboard = ({
|
|
8
10
|
copyButtonText,
|
|
9
11
|
text,
|
|
10
12
|
theme
|
|
11
13
|
}) => {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
const [isActionGroupSticky, setIsActionGroupSticky] = useState(false);
|
|
15
|
+
const actionGroupRef = useRef(null);
|
|
16
|
+
const defaultCopyText = useTextstringValue({
|
|
17
|
+
textstring: ttsToITextString(textStrings.components.codeHighlighter.copyToClipboard.copy)
|
|
18
|
+
});
|
|
19
|
+
const copiedText = useTextstringValue({
|
|
20
|
+
textstring: ttsToITextString(textStrings.components.codeHighlighter.copyToClipboard.copied)
|
|
21
|
+
});
|
|
22
|
+
const copyFailedText = useTextstringValue({
|
|
23
|
+
textstring: ttsToITextString(textStrings.components.codeHighlighter.copyToClipboard.copyFailed)
|
|
24
|
+
});
|
|
25
|
+
const shareText = useTextstringValue({
|
|
26
|
+
textstring: ttsToITextString(textStrings.components.codeHighlighter.copyToClipboard.share)
|
|
27
|
+
});
|
|
28
|
+
const copyText = copyButtonText ?? defaultCopyText;
|
|
29
|
+
const iconColor = theme === CodeHighlighterTheme.Dark ? '#f4f6f8' : '#5f6368';
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const updateStickyState = () => {
|
|
32
|
+
const actionGroupTop = actionGroupRef.current?.getBoundingClientRect().top;
|
|
33
|
+
setIsActionGroupSticky(actionGroupTop !== undefined && actionGroupTop <= STICKY_ACTION_OFFSET);
|
|
34
|
+
};
|
|
35
|
+
updateStickyState();
|
|
36
|
+
document.addEventListener('scroll', updateStickyState, true);
|
|
37
|
+
window.addEventListener('resize', updateStickyState);
|
|
38
|
+
return () => {
|
|
39
|
+
document.removeEventListener('scroll', updateStickyState, true);
|
|
40
|
+
window.removeEventListener('resize', updateStickyState);
|
|
41
|
+
};
|
|
42
|
+
}, []);
|
|
43
|
+
const handleCopy = useCallback(async () => {
|
|
44
|
+
try {
|
|
45
|
+
await navigator.clipboard.writeText(text);
|
|
46
|
+
void createDialog({
|
|
47
|
+
showCloseIcon: true,
|
|
48
|
+
text: copiedText,
|
|
49
|
+
toastType: ToastType.SUCCESS,
|
|
50
|
+
type: DialogType.TOAST
|
|
51
|
+
}).open();
|
|
52
|
+
} catch {
|
|
53
|
+
void createDialog({
|
|
54
|
+
showCloseIcon: true,
|
|
55
|
+
text: copyFailedText,
|
|
56
|
+
toastType: ToastType.ERROR,
|
|
57
|
+
type: DialogType.TOAST
|
|
58
|
+
}).open();
|
|
21
59
|
}
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
60
|
+
}, [copiedText, copyFailedText, text]);
|
|
61
|
+
return /*#__PURE__*/React.createElement(StyledCopyToClipboard, {
|
|
62
|
+
$codeTheme: theme,
|
|
63
|
+
ref: actionGroupRef
|
|
64
|
+
}, /*#__PURE__*/React.createElement(StyledCopyToClipboardActionGroup, null, /*#__PURE__*/React.createElement(StyledCopyToClipboardButton, {
|
|
65
|
+
$codeTheme: theme,
|
|
66
|
+
$isSticky: isActionGroupSticky,
|
|
67
|
+
"aria-label": copyText,
|
|
68
|
+
onClick: () => {
|
|
69
|
+
void handleCopy();
|
|
70
|
+
},
|
|
71
|
+
type: "button"
|
|
30
72
|
}, /*#__PURE__*/React.createElement(Icon, {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}),
|
|
34
|
-
|
|
35
|
-
|
|
73
|
+
color: iconColor,
|
|
74
|
+
icons: ['fa-light fa-copy']
|
|
75
|
+
})), /*#__PURE__*/React.createElement(SharingContextMenu, {
|
|
76
|
+
link: text,
|
|
77
|
+
shouldUseDefaultTriggerStyles: false
|
|
78
|
+
}, /*#__PURE__*/React.createElement(StyledCopyToClipboardButton, {
|
|
79
|
+
$codeTheme: theme,
|
|
80
|
+
$isSticky: isActionGroupSticky,
|
|
81
|
+
"aria-label": shareText,
|
|
82
|
+
type: "button"
|
|
83
|
+
}, /*#__PURE__*/React.createElement(Icon, {
|
|
84
|
+
color: iconColor,
|
|
85
|
+
icons: ['fa fa-share-nodes']
|
|
86
|
+
})))));
|
|
36
87
|
};
|
|
37
88
|
CopyToClipboard.displayName = 'CopyToClipboard';
|
|
38
89
|
export default CopyToClipboard;
|