gatsby-theme-carbon 4.3.66 → 4.4.0
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/gatsby-config.mjs +2 -0
- package/package.json +1 -1
- package/src/components/AnchorLinks/AnchorLinks.module.scss +11 -0
- package/src/components/ArticleCard/ArticleCard.js +1 -1
- package/src/components/Code/Code.js +85 -42
- package/src/components/Code/Code.module.scss +38 -0
- package/src/components/GlobalSearch/GlobalSearch.module.scss +1 -1
- package/src/components/Tabs/Tabs.js +17 -15
- package/src/components/Tabs/Tabs.module.scss +4 -0
- package/src/components/markdown/Markdown.module.scss +8 -0
- package/src/util/hooks/useMetadata.js +1 -0
package/gatsby-config.mjs
CHANGED
|
@@ -51,6 +51,7 @@ export default (themeOptions) => {
|
|
|
51
51
|
gatsbyPluginSharpOptions = {},
|
|
52
52
|
isServiceWorkerEnabled = false,
|
|
53
53
|
isSwitcherEnabled = true,
|
|
54
|
+
isCodeWrapEnabled = false,
|
|
54
55
|
} = themeOptions;
|
|
55
56
|
|
|
56
57
|
const theme = { ...defaultTheme, ...themeOption };
|
|
@@ -101,6 +102,7 @@ export default (themeOptions) => {
|
|
|
101
102
|
interiorTheme: theme.interior,
|
|
102
103
|
isServiceWorkerEnabled,
|
|
103
104
|
isSwitcherEnabled,
|
|
105
|
+
isCodeWrapEnabled,
|
|
104
106
|
title: 'Gatsby Theme Carbon',
|
|
105
107
|
description:
|
|
106
108
|
'Add a description by supplying it to siteMetadata in your gatsby-config.js file.',
|
package/package.json
CHANGED
|
@@ -32,6 +32,17 @@
|
|
|
32
32
|
color: currentColor;
|
|
33
33
|
cursor: pointer;
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
&:focus-visible {
|
|
37
|
+
outline: 2px solid $focus;
|
|
38
|
+
outline-offset: 2px;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Keep each link (and its focus ring) inside a single CSS column fragment
|
|
43
|
+
.multiple-columns li,
|
|
44
|
+
.multiple-columns .link {
|
|
45
|
+
break-inside: avoid;
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
.list--small .link {
|
|
@@ -155,7 +155,7 @@ ArticleCard.propTypes = {
|
|
|
155
155
|
readTime: PropTypes.string,
|
|
156
156
|
|
|
157
157
|
/**
|
|
158
|
-
* Action icon, default is blank, options are
|
|
158
|
+
* Action icon, default is blank, options are launch, arrowRight, download, disabled, email
|
|
159
159
|
*/
|
|
160
160
|
actionIcon: PropTypes.string,
|
|
161
161
|
|
|
@@ -14,10 +14,60 @@ import Sidebar from './Sidebar';
|
|
|
14
14
|
|
|
15
15
|
import useMetadata from '../../util/hooks/useMetadata';
|
|
16
16
|
|
|
17
|
+
export const parseCodeMetaData = (metaData) => {
|
|
18
|
+
// metaData string is of format: path=/directory/file.mdx src=https://gatsby.carbondesignsystem.com showAll wrap
|
|
19
|
+
const result = { path: '', src: '', showAll: false, wrap: null };
|
|
20
|
+
if (!metaData) {
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
const metaDataObject = metaData.split(' ').reduce((obj, item) => {
|
|
24
|
+
if (!item) {
|
|
25
|
+
return obj;
|
|
26
|
+
}
|
|
27
|
+
const [key, value] = item.split('=');
|
|
28
|
+
|
|
29
|
+
// Checking for boolean values coming as string or not coming like in showAll prop
|
|
30
|
+
if (value === 'true' || !value) {
|
|
31
|
+
obj[key] = true;
|
|
32
|
+
} else if (value === 'false') {
|
|
33
|
+
obj[key] = false;
|
|
34
|
+
} else {
|
|
35
|
+
// else setting what is passed after =
|
|
36
|
+
obj[key] = value;
|
|
37
|
+
}
|
|
38
|
+
return obj;
|
|
39
|
+
}, {});
|
|
40
|
+
|
|
41
|
+
if (metaDataObject.path) {
|
|
42
|
+
result.path = metaDataObject.path;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (metaDataObject.src) {
|
|
46
|
+
result.src = metaDataObject.src;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (metaDataObject.showAll) {
|
|
50
|
+
result.showAll = true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (metaDataObject.wrap === true || metaDataObject.wrap === 'true') {
|
|
54
|
+
result.wrap = true;
|
|
55
|
+
} else if (metaDataObject.wrap === false || metaDataObject.wrap === 'false') {
|
|
56
|
+
result.wrap = false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return result;
|
|
60
|
+
};
|
|
61
|
+
|
|
17
62
|
const Code = ({ children, className: classNameProp = '', metaData }) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const [
|
|
63
|
+
// Initialize from metaData so SSR/first paint already reflects
|
|
64
|
+
// path/src/showAll/wrap (useEffect below keeps them in sync on change).
|
|
65
|
+
const [path, setPath] = useState(() => parseCodeMetaData(metaData).path);
|
|
66
|
+
const [src, setSrc] = useState(() => parseCodeMetaData(metaData).src);
|
|
67
|
+
const [showAll, setShowAll] = useState(
|
|
68
|
+
() => parseCodeMetaData(metaData).showAll
|
|
69
|
+
);
|
|
70
|
+
const [wrap, setWrap] = useState(() => parseCodeMetaData(metaData).wrap);
|
|
21
71
|
const [hasMoreThanNineLines, setHasMoreThanNineLines] = useState(false);
|
|
22
72
|
const [shouldShowMore, setShouldShowMore] = useState(false);
|
|
23
73
|
const [isInlineCode, setIsInlineCode] = useState(false);
|
|
@@ -29,38 +79,15 @@ const Code = ({ children, className: classNameProp = '', metaData }) => {
|
|
|
29
79
|
}, [classNameProp]);
|
|
30
80
|
|
|
31
81
|
useEffect(() => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// Checking for boolean values coming as string or not coming like in showAll prop
|
|
38
|
-
if (value === 'true' || !value) {
|
|
39
|
-
obj[key] = true;
|
|
40
|
-
} else if (value === 'false') {
|
|
41
|
-
obj[key] = false;
|
|
42
|
-
} else {
|
|
43
|
-
// else setting what is passed after =
|
|
44
|
-
obj[key] = value;
|
|
45
|
-
}
|
|
46
|
-
return obj;
|
|
47
|
-
}, {});
|
|
48
|
-
|
|
49
|
-
if (metaDataObject.path) {
|
|
50
|
-
setPath(metaDataObject.path);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (metaDataObject.src) {
|
|
54
|
-
setSrc(metaDataObject.src);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (metaDataObject.showAll) {
|
|
58
|
-
setShowAll(true);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
82
|
+
const parsed = parseCodeMetaData(metaData);
|
|
83
|
+
setPath(parsed.path);
|
|
84
|
+
setSrc(parsed.src);
|
|
85
|
+
setShowAll(parsed.showAll);
|
|
86
|
+
setWrap(parsed.wrap);
|
|
61
87
|
}, [metaData]);
|
|
62
88
|
|
|
63
|
-
const { interiorTheme } = useMetadata();
|
|
89
|
+
const { interiorTheme, isCodeWrapEnabled } = useMetadata();
|
|
90
|
+
const shouldWrap = wrap !== null ? wrap : Boolean(isCodeWrapEnabled);
|
|
64
91
|
const language = classNameProp.replace(/language-/, '').replace('mdx', 'jsx');
|
|
65
92
|
|
|
66
93
|
const removeTrailingEmptyLine = (lines) => {
|
|
@@ -90,6 +117,7 @@ const Code = ({ children, className: classNameProp = '', metaData }) => {
|
|
|
90
117
|
if (isInlineCode) {
|
|
91
118
|
return <code>{children}</code>;
|
|
92
119
|
}
|
|
120
|
+
const showToggleButton = hasMoreThanNineLines && !showAll;
|
|
93
121
|
return (
|
|
94
122
|
<Row className={cx(styles.row)}>
|
|
95
123
|
<PathRow src={src} path={path}>
|
|
@@ -100,20 +128,33 @@ const Code = ({ children, className: classNameProp = '', metaData }) => {
|
|
|
100
128
|
language={language}
|
|
101
129
|
theme={getTheme(interiorTheme)}>
|
|
102
130
|
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
|
103
|
-
<div
|
|
131
|
+
<div
|
|
132
|
+
className={cx(styles.container, {
|
|
133
|
+
[styles.hasButton]: showToggleButton,
|
|
134
|
+
[styles.wrapContainer]: shouldWrap,
|
|
135
|
+
})}>
|
|
104
136
|
<pre
|
|
105
137
|
className={cx(styles.highlight, {
|
|
106
138
|
[styles.sideBarMinHeight]: !path && src,
|
|
139
|
+
[styles.wrap]: shouldWrap,
|
|
107
140
|
[className]: className,
|
|
108
141
|
})}
|
|
109
142
|
style={style}>
|
|
110
|
-
{getLines(tokens).map((line, i) =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
143
|
+
{getLines(tokens).map((line, i) => {
|
|
144
|
+
const lineProps = getLineProps({ line, key: i });
|
|
145
|
+
return (
|
|
146
|
+
<div
|
|
147
|
+
{...lineProps}
|
|
148
|
+
key={i}
|
|
149
|
+
className={cx(lineProps.className, {
|
|
150
|
+
[styles.wrappedLine]: shouldWrap,
|
|
151
|
+
})}>
|
|
152
|
+
{line.map((token, key) => (
|
|
153
|
+
<span {...getTokenProps({ token, key })} key={key} />
|
|
154
|
+
))}
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
})}
|
|
117
158
|
</pre>
|
|
118
159
|
<Sidebar path={path} src={src}>
|
|
119
160
|
{children}
|
|
@@ -121,12 +162,14 @@ const Code = ({ children, className: classNameProp = '', metaData }) => {
|
|
|
121
162
|
</div>
|
|
122
163
|
)}
|
|
123
164
|
</Highlight>
|
|
124
|
-
{
|
|
165
|
+
{showToggleButton && (
|
|
125
166
|
<button
|
|
126
167
|
className={cx(styles.showMoreButton, {
|
|
127
168
|
[styles.dark]: interiorTheme === 'dark',
|
|
128
169
|
})}
|
|
129
170
|
onClick={() => setShouldShowMore(!shouldShowMore)}
|
|
171
|
+
aria-expanded={shouldShowMore}
|
|
172
|
+
aria-label={shouldShowMore ? 'Show less code' : 'Show more code'}
|
|
130
173
|
type="button">
|
|
131
174
|
{shouldShowMore ? (
|
|
132
175
|
<>
|
|
@@ -67,6 +67,44 @@ li .row {
|
|
|
67
67
|
text-wrap: wrap;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
// Reserve room for the absolutely positioned Show more/less button so it
|
|
71
|
+
// doesn't cover code in default and nested-list contexts (see #868).
|
|
72
|
+
.has-button .highlight {
|
|
73
|
+
padding-bottom: $spacing-10;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Opt-in soft wrap via ```lang wrap. Breaks long log lines/URLs instead of
|
|
77
|
+
// forcing horizontal scroll, while keeping copy content unchanged.
|
|
78
|
+
.highlight.wrap {
|
|
79
|
+
white-space: pre-wrap;
|
|
80
|
+
overflow-wrap: anywhere;
|
|
81
|
+
overflow-x: hidden;
|
|
82
|
+
// Reserve room for the copy/src buttons so wrapped line endings never
|
|
83
|
+
// render underneath the sidebar (see #868).
|
|
84
|
+
padding-right: 3rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.wrapped-line {
|
|
88
|
+
white-space: pre-wrap;
|
|
89
|
+
overflow-wrap: anywhere;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// In wrap mode nothing scrolls under the sidebar, so drop the fade gradient
|
|
93
|
+
// (it would obscure wrapped line endings) and shrink to the button column.
|
|
94
|
+
.wrap-container .sidebar {
|
|
95
|
+
background: transparent;
|
|
96
|
+
padding-left: 8px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.wrap-container .sidebar.dark {
|
|
100
|
+
background: transparent;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Don't force extra scroll width when wrapping; keep it for scroll mode only.
|
|
104
|
+
.highlight.wrap :global(.token:last-of-type) {
|
|
105
|
+
margin-right: 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
70
108
|
.sideBarMinHeight {
|
|
71
109
|
min-height: 96px;
|
|
72
110
|
}
|
|
@@ -141,21 +141,23 @@ export const Tabs = (props) => {
|
|
|
141
141
|
[setActiveTab, activeTab, tabList.current]
|
|
142
142
|
);
|
|
143
143
|
return (
|
|
144
|
-
<
|
|
145
|
-
{
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
144
|
+
<div className={cx(styles.tabs, props.className)}>
|
|
145
|
+
<TabContext.Provider value={value}>
|
|
146
|
+
{isMobile ? (
|
|
147
|
+
<Select _id={id}>{props.children}</Select>
|
|
148
|
+
) : (
|
|
149
|
+
<TabList _id={id}>{props.children}</TabList>
|
|
150
|
+
)}
|
|
151
|
+
{React.Children.map(props.children, (child, index) => {
|
|
152
|
+
if (elementIsNullOrString(child)) return child;
|
|
153
|
+
return React.cloneElement(child, {
|
|
154
|
+
_id: `${id}__${index}`,
|
|
155
|
+
active: activeTab === index,
|
|
156
|
+
index,
|
|
157
|
+
});
|
|
158
|
+
})}
|
|
159
|
+
</TabContext.Provider>
|
|
160
|
+
</div>
|
|
159
161
|
);
|
|
160
162
|
};
|
|
161
163
|
|
|
@@ -193,3 +193,11 @@ img {
|
|
|
193
193
|
span:has(:global(.gatsby-resp-image-wrapper)) {
|
|
194
194
|
display: block;
|
|
195
195
|
}
|
|
196
|
+
|
|
197
|
+
// Linked images ([](href)) inherit paragraph text padding.
|
|
198
|
+
// Reset it so the image aligns flush like a standalone image.
|
|
199
|
+
.paragraph > a:global(.gatsby-resp-image-link) {
|
|
200
|
+
display: block;
|
|
201
|
+
margin: 0;
|
|
202
|
+
padding: 0;
|
|
203
|
+
}
|