UNPKG

5.25 kBJavaScriptView Raw
1const _excluded = ["properties"];
2
3function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
4
5import React from 'react';
6import { useParameter } from '@storybook/api';
7import { styled } from '@storybook/theming';
8import { Link } from '@storybook/router';
9import { SyntaxHighlighter } from '@storybook/components'; // @ts-expect-error Typedefs don't currently expose `createElement` even though it exists
10
11import { createElement as createSyntaxHighlighterElement } from 'react-syntax-highlighter';
12const StyledStoryLink = styled(Link)(({
13 theme
14}) => ({
15 display: 'block',
16 textDecoration: 'none',
17 borderRadius: theme.appBorderRadius,
18 color: 'inherit',
19 '&:hover': {
20 background: theme.background.hoverable
21 }
22}));
23const SelectedStoryHighlight = styled.div(({
24 theme
25}) => ({
26 background: theme.background.hoverable,
27 borderRadius: theme.appBorderRadius
28}));
29const StyledSyntaxHighlighter = styled(SyntaxHighlighter)(({
30 theme
31}) => ({
32 fontSize: theme.typography.size.s2 - 1
33}));
34
35const areLocationsEqual = (a, b) => a.startLoc.line === b.startLoc.line && a.startLoc.col === b.startLoc.col && a.endLoc.line === b.endLoc.line && a.endLoc.col === b.endLoc.col;
36
37export const StoryPanel = ({
38 api
39}) => {
40 const story = api.getCurrentStoryData();
41 const selectedStoryRef = React.useRef(null);
42 const {
43 source,
44 locationsMap
45 } = useParameter('storySource', {
46 source: 'loading source...'
47 });
48 const currentLocation = locationsMap ? locationsMap[Object.keys(locationsMap).find(key => {
49 const sourceLoaderId = key.split('--');
50 return story.id.endsWith(sourceLoaderId[sourceLoaderId.length - 1]);
51 })] : undefined;
52 React.useEffect(() => {
53 if (selectedStoryRef.current) {
54 selectedStoryRef.current.scrollIntoView();
55 }
56 }, [selectedStoryRef.current]);
57
58 const createPart = ({
59 rows,
60 stylesheet,
61 useInlineStyles
62 }) => rows.map((node, i) => createSyntaxHighlighterElement({
63 node,
64 stylesheet,
65 useInlineStyles,
66 key: `code-segment${i}`
67 }));
68
69 const createStoryPart = ({
70 rows,
71 stylesheet,
72 useInlineStyles,
73 location,
74 id,
75 refId
76 }) => {
77 const first = location.startLoc.line - 1;
78 const last = location.endLoc.line;
79 const storyRows = rows.slice(first, last);
80 const storySource = createPart({
81 rows: storyRows,
82 stylesheet,
83 useInlineStyles
84 });
85 const storyKey = `${first}-${last}`;
86
87 if (currentLocation && areLocationsEqual(location, currentLocation)) {
88 return /*#__PURE__*/React.createElement(SelectedStoryHighlight, {
89 key: storyKey,
90 ref: selectedStoryRef
91 }, storySource);
92 }
93
94 return /*#__PURE__*/React.createElement(StyledStoryLink, {
95 to: refId ? `/story/${refId}_${id}` : `/story/${id}`,
96 key: storyKey
97 }, storySource);
98 };
99
100 const createParts = ({
101 rows,
102 stylesheet,
103 useInlineStyles
104 }) => {
105 const parts = [];
106 let lastRow = 0;
107 Object.keys(locationsMap).forEach(key => {
108 const location = locationsMap[key];
109 const first = location.startLoc.line - 1;
110 const last = location.endLoc.line;
111 const {
112 kind,
113 refId
114 } = story; // source loader ids are different from story id
115
116 const sourceIdParts = key.split('--');
117 const id = api.storyId(kind, sourceIdParts[sourceIdParts.length - 1]);
118 const start = createPart({
119 rows: rows.slice(lastRow, first),
120 stylesheet,
121 useInlineStyles
122 });
123 const storyPart = createStoryPart({
124 rows,
125 stylesheet,
126 useInlineStyles,
127 location,
128 id,
129 refId
130 });
131 parts.push(start);
132 parts.push(storyPart);
133 lastRow = last;
134 });
135 const lastPart = createPart({
136 rows: rows.slice(lastRow),
137 stylesheet,
138 useInlineStyles
139 });
140 parts.push(lastPart);
141 return parts;
142 };
143
144 const lineRenderer = ({
145 rows,
146 stylesheet,
147 useInlineStyles
148 }) => {
149 // because of the usage of lineRenderer, all lines will be wrapped in a span
150 // these spans will receive all classes on them for some reason
151 // which makes colours cascade incorrectly
152 // this removed that list of classnames
153 const myrows = rows.map(_ref => {
154 let rest = _objectWithoutPropertiesLoose(_ref, _excluded);
155
156 return Object.assign({}, rest, {
157 properties: {
158 className: []
159 }
160 });
161 });
162
163 if (!locationsMap || !Object.keys(locationsMap).length) {
164 return createPart({
165 rows: myrows,
166 stylesheet,
167 useInlineStyles
168 });
169 }
170
171 const parts = createParts({
172 rows: myrows,
173 stylesheet,
174 useInlineStyles
175 });
176 return /*#__PURE__*/React.createElement("span", null, parts);
177 };
178
179 return story ? /*#__PURE__*/React.createElement(StyledSyntaxHighlighter, {
180 language: "jsx",
181 showLineNumbers: true,
182 renderer: lineRenderer,
183 format: false,
184 copyable: false,
185 padded: true
186 }, source) : null;
187};
\No newline at end of file