@mantine/code-highlight 7.0.0-alpha.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/README.md +21 -0
- package/cjs/CodeHighlight.js +126 -0
- package/cjs/CodeHighlight.js.map +1 -0
- package/cjs/CodeHighlight.module.css.js +8 -0
- package/cjs/CodeHighlight.module.css.js.map +1 -0
- package/cjs/CodeHighlight.theme.module.css.js +8 -0
- package/cjs/CodeHighlight.theme.module.css.js.map +1 -0
- package/cjs/CodeHighlightTabs.js +196 -0
- package/cjs/CodeHighlightTabs.js.map +1 -0
- package/cjs/CopyIcon.js +70 -0
- package/cjs/CopyIcon.js.map +1 -0
- package/cjs/ExpandIcon.js +83 -0
- package/cjs/ExpandIcon.js.map +1 -0
- package/cjs/FileIcon.js +22 -0
- package/cjs/FileIcon.js.map +1 -0
- package/cjs/InlineCodeHighlight.js +78 -0
- package/cjs/InlineCodeHighlight.js.map +1 -0
- package/cjs/index.css +326 -0
- package/cjs/index.js +14 -0
- package/cjs/index.js.map +1 -0
- package/cjs/use-highlight.js +27 -0
- package/cjs/use-highlight.js.map +1 -0
- package/esm/CodeHighlight.js +117 -0
- package/esm/CodeHighlight.js.map +1 -0
- package/esm/CodeHighlight.module.css.js +4 -0
- package/esm/CodeHighlight.module.css.js.map +1 -0
- package/esm/CodeHighlight.theme.module.css.js +4 -0
- package/esm/CodeHighlight.theme.module.css.js.map +1 -0
- package/esm/CodeHighlightTabs.js +186 -0
- package/esm/CodeHighlightTabs.js.map +1 -0
- package/esm/CopyIcon.js +62 -0
- package/esm/CopyIcon.js.map +1 -0
- package/esm/ExpandIcon.js +75 -0
- package/esm/ExpandIcon.js.map +1 -0
- package/esm/FileIcon.js +14 -0
- package/esm/FileIcon.js.map +1 -0
- package/esm/InlineCodeHighlight.js +68 -0
- package/esm/InlineCodeHighlight.js.map +1 -0
- package/esm/index.css +326 -0
- package/esm/index.js +4 -0
- package/esm/index.js.map +1 -0
- package/esm/use-highlight.js +19 -0
- package/esm/use-highlight.js.map +1 -0
- package/lib/CodeHighlight.d.ts +29 -0
- package/lib/CodeHighlightTabs.d.ts +55 -0
- package/lib/CopyIcon.d.ts +9 -0
- package/lib/ExpandIcon.d.ts +6 -0
- package/lib/FileIcon.d.ts +8 -0
- package/lib/InlineCodeHighlight.d.ts +19 -0
- package/lib/index.d.ts +6 -0
- package/lib/use-highlight.d.ts +15 -0
- package/package.json +42 -0
- package/src/CodeHighlight.module.css +153 -0
- package/src/CodeHighlight.story.tsx +218 -0
- package/src/CodeHighlight.test.tsx +30 -0
- package/src/CodeHighlight.theme.module.css +95 -0
- package/src/CodeHighlight.tsx +123 -0
- package/src/CodeHighlightTabs.test.tsx +39 -0
- package/src/CodeHighlightTabs.tsx +252 -0
- package/src/CopyIcon.tsx +37 -0
- package/src/ExpandIcon.tsx +42 -0
- package/src/FileIcon.tsx +19 -0
- package/src/InlineCodeHighlight.test.tsx +25 -0
- package/src/InlineCodeHighlight.tsx +72 -0
- package/src/index.ts +23 -0
- package/src/use-highlight.ts +30 -0
- package/styles.css +326 -0
- package/tsconfig.build.json +23 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import hljs from 'highlight.js';
|
|
3
|
+
import cx from 'clsx';
|
|
4
|
+
import { useUncontrolled } from '@mantine/hooks';
|
|
5
|
+
import {
|
|
6
|
+
Box,
|
|
7
|
+
BoxProps,
|
|
8
|
+
StylesApiProps,
|
|
9
|
+
factory,
|
|
10
|
+
ElementProps,
|
|
11
|
+
useProps,
|
|
12
|
+
useStyles,
|
|
13
|
+
CopyButton,
|
|
14
|
+
Tooltip,
|
|
15
|
+
ActionIcon,
|
|
16
|
+
UnstyledButton,
|
|
17
|
+
ScrollArea,
|
|
18
|
+
createVarsResolver,
|
|
19
|
+
rem,
|
|
20
|
+
Factory,
|
|
21
|
+
} from '@mantine/core';
|
|
22
|
+
import { CopyIcon } from './CopyIcon';
|
|
23
|
+
import { FileIcon } from './FileIcon';
|
|
24
|
+
import { ExpandIcon } from './ExpandIcon';
|
|
25
|
+
import _classes from './CodeHighlight.module.css';
|
|
26
|
+
import themeClasses from './CodeHighlight.theme.module.css';
|
|
27
|
+
|
|
28
|
+
const classes = { ..._classes, root: cx(_classes.root, themeClasses.theme) };
|
|
29
|
+
|
|
30
|
+
export type CodeHighlightTabsStylesNames =
|
|
31
|
+
| 'root'
|
|
32
|
+
| 'code'
|
|
33
|
+
| 'codeWrapper'
|
|
34
|
+
| 'showCodeButton'
|
|
35
|
+
| 'pre'
|
|
36
|
+
| 'controls'
|
|
37
|
+
| 'control'
|
|
38
|
+
| 'header'
|
|
39
|
+
| 'file'
|
|
40
|
+
| 'files';
|
|
41
|
+
|
|
42
|
+
export type CodeHighlightTabsVariant = string;
|
|
43
|
+
export type CodeHighlightTabsCssVariables = {
|
|
44
|
+
root: '--ch-max-collapsed-height';
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export interface CodeHighlightTabsCode {
|
|
48
|
+
language?: string;
|
|
49
|
+
code: string;
|
|
50
|
+
fileName?: string;
|
|
51
|
+
icon?: React.ReactNode;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface CodeHighlightTabsProps
|
|
55
|
+
extends BoxProps,
|
|
56
|
+
StylesApiProps<CodeHighlightTabsFactory>,
|
|
57
|
+
ElementProps<'div'> {
|
|
58
|
+
/** Code to highlight with meta data (file name and icon) */
|
|
59
|
+
code: CodeHighlightTabsCode | CodeHighlightTabsCode[];
|
|
60
|
+
|
|
61
|
+
/** Default active tab index */
|
|
62
|
+
defaultActiveTab?: number;
|
|
63
|
+
|
|
64
|
+
/** Index of controlled active tab state */
|
|
65
|
+
activeTab?: number;
|
|
66
|
+
|
|
67
|
+
/** Called when tab changes */
|
|
68
|
+
onTabChange?(tab: number): void;
|
|
69
|
+
|
|
70
|
+
/** Determines whether header with file names and copy button should be rendered, `true` by default */
|
|
71
|
+
withHeader?: boolean;
|
|
72
|
+
|
|
73
|
+
/** Copy tooltip label, `'Copy code'` by default */
|
|
74
|
+
copyLabel?: string;
|
|
75
|
+
|
|
76
|
+
/** Copied tooltip label, `'Copied'` by default */
|
|
77
|
+
copiedLabel?: string;
|
|
78
|
+
|
|
79
|
+
/** Function that returns icon based on file name */
|
|
80
|
+
getFileIcon?(fileName: string): React.ReactNode;
|
|
81
|
+
|
|
82
|
+
/** `max-height` of code in collapsed state, `'6rem'` by default */
|
|
83
|
+
maxCollapsedHeight?: React.CSSProperties['maxHeight'];
|
|
84
|
+
|
|
85
|
+
/** Controlled expanded state */
|
|
86
|
+
expanded?: boolean;
|
|
87
|
+
|
|
88
|
+
/** Uncontrolled expanded state initial value */
|
|
89
|
+
defaultExpanded?: boolean;
|
|
90
|
+
|
|
91
|
+
/** Called when expanded state changes */
|
|
92
|
+
onExpandedChange?(expanded: boolean): void;
|
|
93
|
+
|
|
94
|
+
/** Expand button label and tooltip, `'Expand code'` by default */
|
|
95
|
+
expandCodeLabel?: string;
|
|
96
|
+
|
|
97
|
+
/** Collapse button label and tooltip, `'Collapse code'` by default */
|
|
98
|
+
collapseCodeLabel?: string;
|
|
99
|
+
|
|
100
|
+
/** Determines whether to show the expand button, `false` by default */
|
|
101
|
+
withExpandButton?: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type CodeHighlightTabsFactory = Factory<{
|
|
105
|
+
props: CodeHighlightTabsProps;
|
|
106
|
+
ref: HTMLDivElement;
|
|
107
|
+
stylesNames: CodeHighlightTabsStylesNames;
|
|
108
|
+
}>;
|
|
109
|
+
|
|
110
|
+
const defaultProps: Partial<CodeHighlightTabsProps> = {
|
|
111
|
+
withHeader: true,
|
|
112
|
+
copyLabel: 'Copy code',
|
|
113
|
+
copiedLabel: 'Copied',
|
|
114
|
+
maxCollapsedHeight: '8rem',
|
|
115
|
+
expandCodeLabel: 'Expand code',
|
|
116
|
+
collapseCodeLabel: 'Collapse code',
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const varsResolver = createVarsResolver<CodeHighlightTabsFactory>((_, { maxCollapsedHeight }) => ({
|
|
120
|
+
root: { '--ch-max-collapsed-height': rem(maxCollapsedHeight) },
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
export const CodeHighlightTabs = factory<CodeHighlightTabsFactory>((_props, ref) => {
|
|
124
|
+
const props = useProps('CodeHighlightTabs', defaultProps, _props);
|
|
125
|
+
const {
|
|
126
|
+
classNames,
|
|
127
|
+
className,
|
|
128
|
+
style,
|
|
129
|
+
styles,
|
|
130
|
+
unstyled,
|
|
131
|
+
vars,
|
|
132
|
+
children,
|
|
133
|
+
code,
|
|
134
|
+
defaultActiveTab,
|
|
135
|
+
activeTab,
|
|
136
|
+
onTabChange,
|
|
137
|
+
withHeader,
|
|
138
|
+
copiedLabel,
|
|
139
|
+
copyLabel,
|
|
140
|
+
getFileIcon,
|
|
141
|
+
maxCollapsedHeight,
|
|
142
|
+
expanded,
|
|
143
|
+
defaultExpanded,
|
|
144
|
+
onExpandedChange,
|
|
145
|
+
expandCodeLabel,
|
|
146
|
+
collapseCodeLabel,
|
|
147
|
+
withExpandButton,
|
|
148
|
+
...others
|
|
149
|
+
} = props;
|
|
150
|
+
|
|
151
|
+
const getStyles = useStyles<CodeHighlightTabsFactory>({
|
|
152
|
+
name: 'CodeHighlightTabs',
|
|
153
|
+
props,
|
|
154
|
+
classes,
|
|
155
|
+
className,
|
|
156
|
+
style,
|
|
157
|
+
classNames,
|
|
158
|
+
styles,
|
|
159
|
+
unstyled,
|
|
160
|
+
vars,
|
|
161
|
+
varsResolver,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const [value, setValue] = useUncontrolled({
|
|
165
|
+
defaultValue: defaultActiveTab,
|
|
166
|
+
value: activeTab,
|
|
167
|
+
finalValue: 0,
|
|
168
|
+
onChange: onTabChange,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const [_expanded, setExpanded] = useUncontrolled({
|
|
172
|
+
defaultValue: defaultExpanded,
|
|
173
|
+
value: expanded,
|
|
174
|
+
finalValue: true,
|
|
175
|
+
onChange: onExpandedChange,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const nodes = Array.isArray(code) ? code : [code];
|
|
179
|
+
const currentCode = nodes[value];
|
|
180
|
+
|
|
181
|
+
const highlighted = hljs.highlight(currentCode.code.trim(), {
|
|
182
|
+
language: currentCode.language!,
|
|
183
|
+
}).value;
|
|
184
|
+
|
|
185
|
+
const files = nodes.map((node, index) => (
|
|
186
|
+
<UnstyledButton
|
|
187
|
+
{...getStyles('file')}
|
|
188
|
+
key={node.fileName}
|
|
189
|
+
mod={{ active: index === value }}
|
|
190
|
+
onClick={() => setValue(index)}
|
|
191
|
+
>
|
|
192
|
+
<FileIcon fileIcon={node.icon} getFileIcon={getFileIcon} fileName={node.fileName} />
|
|
193
|
+
<span>{node.fileName}</span>
|
|
194
|
+
</UnstyledButton>
|
|
195
|
+
));
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<Box {...getStyles('root')} ref={ref} {...others} dir="ltr">
|
|
199
|
+
{withHeader && (
|
|
200
|
+
<div {...getStyles('header')}>
|
|
201
|
+
<ScrollArea type="never" dir="ltr" offsetScrollbars={false}>
|
|
202
|
+
<div {...getStyles('files')}>{files}</div>
|
|
203
|
+
</ScrollArea>
|
|
204
|
+
<div {...getStyles('controls')}>
|
|
205
|
+
{withExpandButton && (
|
|
206
|
+
<Tooltip
|
|
207
|
+
label={_expanded ? collapseCodeLabel : expandCodeLabel}
|
|
208
|
+
fz="sm"
|
|
209
|
+
position="left"
|
|
210
|
+
>
|
|
211
|
+
<ActionIcon
|
|
212
|
+
onClick={() => setExpanded(!_expanded)}
|
|
213
|
+
variant="none"
|
|
214
|
+
{...getStyles('control')}
|
|
215
|
+
>
|
|
216
|
+
<ExpandIcon expanded={_expanded} />
|
|
217
|
+
</ActionIcon>
|
|
218
|
+
</Tooltip>
|
|
219
|
+
)}
|
|
220
|
+
|
|
221
|
+
<CopyButton value={currentCode.code.trim()}>
|
|
222
|
+
{({ copied, copy }) => (
|
|
223
|
+
<Tooltip label={copied ? copiedLabel : copyLabel} fz="sm" position="left">
|
|
224
|
+
<ActionIcon onClick={copy} variant="none" {...getStyles('control')}>
|
|
225
|
+
<CopyIcon copied={copied} />
|
|
226
|
+
</ActionIcon>
|
|
227
|
+
</Tooltip>
|
|
228
|
+
)}
|
|
229
|
+
</CopyButton>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
)}
|
|
233
|
+
|
|
234
|
+
<ScrollArea type="auto" dir="ltr" offsetScrollbars={false}>
|
|
235
|
+
<Box {...getStyles('codeWrapper')} mod={{ expanded: _expanded }}>
|
|
236
|
+
<pre {...getStyles('pre')}>
|
|
237
|
+
<code {...getStyles('code')} dangerouslySetInnerHTML={{ __html: highlighted }} />
|
|
238
|
+
</pre>
|
|
239
|
+
</Box>
|
|
240
|
+
</ScrollArea>
|
|
241
|
+
<UnstyledButton
|
|
242
|
+
{...getStyles('showCodeButton')}
|
|
243
|
+
mod={{ hidden: _expanded }}
|
|
244
|
+
onClick={() => setExpanded(true)}
|
|
245
|
+
>
|
|
246
|
+
Expand code
|
|
247
|
+
</UnstyledButton>
|
|
248
|
+
</Box>
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
CodeHighlightTabs.displayName = '@mantine/core/CodeHighlightTabs';
|
package/src/CopyIcon.tsx
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { rem } from '@mantine/core';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
interface CopyIconProps extends React.ComponentPropsWithoutRef<'svg'> {
|
|
5
|
+
copied: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function CopyIcon({ copied, style, ...others }: CopyIconProps) {
|
|
9
|
+
return (
|
|
10
|
+
<svg
|
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
+
style={{ width: rem(18), height: rem(18), ...style }}
|
|
13
|
+
viewBox="0 0 24 24"
|
|
14
|
+
strokeWidth="2"
|
|
15
|
+
stroke="currentColor"
|
|
16
|
+
fill="none"
|
|
17
|
+
strokeLinecap="round"
|
|
18
|
+
strokeLinejoin="round"
|
|
19
|
+
{...others}
|
|
20
|
+
>
|
|
21
|
+
{copied ? (
|
|
22
|
+
<>
|
|
23
|
+
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
24
|
+
<path d="M5 12l5 5l10 -10"></path>
|
|
25
|
+
</>
|
|
26
|
+
) : (
|
|
27
|
+
<>
|
|
28
|
+
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
29
|
+
<path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path>
|
|
30
|
+
<path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path>
|
|
31
|
+
</>
|
|
32
|
+
)}
|
|
33
|
+
</svg>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
CopyIcon.displayName = '@mantine/code-highlight/CopyIcon';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { rem } from '@mantine/core';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
interface ExpandIconProps extends React.ComponentPropsWithoutRef<'svg'> {
|
|
5
|
+
expanded: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function ExpandIcon({ expanded, style, ...others }: ExpandIconProps) {
|
|
9
|
+
return (
|
|
10
|
+
<svg
|
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
+
style={{ width: rem(18), height: rem(18), ...style }}
|
|
13
|
+
viewBox="0 0 24 24"
|
|
14
|
+
strokeWidth="2"
|
|
15
|
+
stroke="currentColor"
|
|
16
|
+
fill="none"
|
|
17
|
+
strokeLinecap="round"
|
|
18
|
+
strokeLinejoin="round"
|
|
19
|
+
{...others}
|
|
20
|
+
>
|
|
21
|
+
{expanded ? (
|
|
22
|
+
<>
|
|
23
|
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
24
|
+
<path d="M12 13v-8l-3 3m6 0l-3 -3" />
|
|
25
|
+
<path d="M9 17l1 0" />
|
|
26
|
+
<path d="M14 17l1 0" />
|
|
27
|
+
<path d="M19 17l1 0" />
|
|
28
|
+
<path d="M4 17l1 0" />
|
|
29
|
+
</>
|
|
30
|
+
) : (
|
|
31
|
+
<>
|
|
32
|
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
33
|
+
<path d="M12 11v8l3 -3m-6 0l3 3" />
|
|
34
|
+
<path d="M9 7l1 0" />
|
|
35
|
+
<path d="M14 7l1 0" />
|
|
36
|
+
<path d="M19 7l1 0" />
|
|
37
|
+
<path d="M4 7l1 0" />
|
|
38
|
+
</>
|
|
39
|
+
)}
|
|
40
|
+
</svg>
|
|
41
|
+
);
|
|
42
|
+
}
|
package/src/FileIcon.tsx
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface FileIconProps {
|
|
4
|
+
fileName: string | undefined;
|
|
5
|
+
getFileIcon?: ((fileName: string) => React.ReactNode) | undefined;
|
|
6
|
+
fileIcon: React.ReactNode | undefined;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function FileIcon({ fileIcon, fileName, getFileIcon }: FileIconProps) {
|
|
10
|
+
if (fileIcon) {
|
|
11
|
+
return <>{fileIcon}</>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (getFileIcon && fileName) {
|
|
15
|
+
return <>{getFileIcon(fileName)}</>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { tests } from '@mantine/tests';
|
|
2
|
+
import {
|
|
3
|
+
InlineCodeHighlight,
|
|
4
|
+
InlineCodeHighlightProps,
|
|
5
|
+
InlineCodeHighlightStylesNames,
|
|
6
|
+
} from './InlineCodeHighlight';
|
|
7
|
+
|
|
8
|
+
const defaultProps: InlineCodeHighlightProps = {
|
|
9
|
+
code: 'test-code',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
describe('@mantine/code-highlight/InlineCodeHighlight', () => {
|
|
13
|
+
tests.itSupportsSystemProps<InlineCodeHighlightProps, InlineCodeHighlightStylesNames>({
|
|
14
|
+
component: InlineCodeHighlight,
|
|
15
|
+
props: defaultProps,
|
|
16
|
+
polymorphic: true,
|
|
17
|
+
styleProps: true,
|
|
18
|
+
extend: true,
|
|
19
|
+
variant: true,
|
|
20
|
+
size: true,
|
|
21
|
+
refType: HTMLElement,
|
|
22
|
+
displayName: '@mantine/core/InlineCodeHighlight',
|
|
23
|
+
stylesApiSelectors: ['code'],
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import cx from 'clsx';
|
|
3
|
+
import {
|
|
4
|
+
Box,
|
|
5
|
+
BoxProps,
|
|
6
|
+
StylesApiProps,
|
|
7
|
+
factory,
|
|
8
|
+
ElementProps,
|
|
9
|
+
useProps,
|
|
10
|
+
useStyles,
|
|
11
|
+
Factory,
|
|
12
|
+
} from '@mantine/core';
|
|
13
|
+
import hljs from 'highlight.js';
|
|
14
|
+
import _classes from './CodeHighlight.module.css';
|
|
15
|
+
import themeClasses from './CodeHighlight.theme.module.css';
|
|
16
|
+
|
|
17
|
+
const classes = { ..._classes, code: cx(_classes.code, themeClasses.theme) };
|
|
18
|
+
|
|
19
|
+
export type InlineCodeHighlightStylesNames = 'code';
|
|
20
|
+
export type InlineCodeHighlightVariant = string;
|
|
21
|
+
|
|
22
|
+
export interface InlineCodeHighlightProps
|
|
23
|
+
extends BoxProps,
|
|
24
|
+
StylesApiProps<InlineCodeHighlightFactory>,
|
|
25
|
+
ElementProps<'div'> {
|
|
26
|
+
/** Code to highlight */
|
|
27
|
+
code: string;
|
|
28
|
+
|
|
29
|
+
/** Code language, `'tsx'` by default */
|
|
30
|
+
language?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type InlineCodeHighlightFactory = Factory<{
|
|
34
|
+
props: InlineCodeHighlightProps;
|
|
35
|
+
ref: HTMLElement;
|
|
36
|
+
stylesNames: InlineCodeHighlightStylesNames;
|
|
37
|
+
}>;
|
|
38
|
+
|
|
39
|
+
const defaultProps: Partial<InlineCodeHighlightProps> = {
|
|
40
|
+
language: 'tsx',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const InlineCodeHighlight = factory<InlineCodeHighlightFactory>((_props, ref) => {
|
|
44
|
+
const props = useProps('InlineCodeHighlight', defaultProps, _props);
|
|
45
|
+
const { classNames, className, style, styles, unstyled, vars, code, language, ...others } = props;
|
|
46
|
+
|
|
47
|
+
const getStyles = useStyles<InlineCodeHighlightFactory>({
|
|
48
|
+
name: 'InlineCodeHighlight',
|
|
49
|
+
props,
|
|
50
|
+
classes,
|
|
51
|
+
className,
|
|
52
|
+
style,
|
|
53
|
+
classNames,
|
|
54
|
+
styles,
|
|
55
|
+
unstyled,
|
|
56
|
+
rootSelector: 'code',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const highlighted = hljs.highlight(code.trim(), { language: language! }).value;
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Box
|
|
63
|
+
{...getStyles('code')}
|
|
64
|
+
component="code"
|
|
65
|
+
ref={ref}
|
|
66
|
+
{...others}
|
|
67
|
+
dangerouslySetInnerHTML={{ __html: highlighted }}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
InlineCodeHighlight.displayName = '@mantine/core/InlineCodeHighlight';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { CodeHighlightTabs } from './CodeHighlightTabs';
|
|
2
|
+
export { CodeHighlight } from './CodeHighlight';
|
|
3
|
+
export { InlineCodeHighlight } from './InlineCodeHighlight';
|
|
4
|
+
export type {
|
|
5
|
+
CodeHighlightTabsFactory,
|
|
6
|
+
CodeHighlightTabsStylesNames,
|
|
7
|
+
CodeHighlightTabsProps,
|
|
8
|
+
CodeHighlightTabsCode,
|
|
9
|
+
CodeHighlightTabsVariant,
|
|
10
|
+
CodeHighlightTabsCssVariables,
|
|
11
|
+
} from './CodeHighlightTabs';
|
|
12
|
+
export type {
|
|
13
|
+
CodeHighlightFactory,
|
|
14
|
+
CodeHighlightProps,
|
|
15
|
+
CodeHighlightStylesNames,
|
|
16
|
+
CodeHighlightVariant,
|
|
17
|
+
} from './CodeHighlight';
|
|
18
|
+
export type {
|
|
19
|
+
InlineCodeHighlightFactory,
|
|
20
|
+
InlineCodeHighlightProps,
|
|
21
|
+
InlineCodeHighlightStylesNames,
|
|
22
|
+
InlineCodeHighlightVariant,
|
|
23
|
+
} from './InlineCodeHighlight';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import hljs from 'highlight.js';
|
|
3
|
+
|
|
4
|
+
interface UseHighlightInput {
|
|
5
|
+
code: string;
|
|
6
|
+
language: string;
|
|
7
|
+
highlightOnClient: boolean | undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function useHighlight({ code, language, highlightOnClient }: UseHighlightInput) {
|
|
11
|
+
const getHighlightedCode = () => hljs.highlight(code.trim(), { language: language! }).value;
|
|
12
|
+
const [highlighted, setHighlighted] = useState(!highlightOnClient);
|
|
13
|
+
const [highlightedCode, setHighlightedCode] = useState(
|
|
14
|
+
highlightOnClient ? code : getHighlightedCode()
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const getCodeProps = () =>
|
|
18
|
+
highlighted
|
|
19
|
+
? { dangerouslySetInnerHTML: { __html: highlightedCode } }
|
|
20
|
+
: { children: code.trim() };
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (highlightOnClient) {
|
|
24
|
+
setHighlightedCode(getHighlightedCode());
|
|
25
|
+
setHighlighted(true);
|
|
26
|
+
}
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
return getCodeProps;
|
|
30
|
+
}
|