@redocly/theme-experimental 0.0.0-beta-20240215154132
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/LICENSE +1 -0
- package/README.md +1 -0
- package/markdoc/Cards/Card.stories.tsx +37 -0
- package/markdoc/Cards/Card.tsx +55 -0
- package/markdoc/Cards/CardsBlock.tsx +10 -0
- package/markdoc/Cards/__tests__/Card.test.tsx +63 -0
- package/markdoc/Cards/__tests__/CardsBlock.test.tsx +12 -0
- package/markdoc/Cards/index.ts +2 -0
- package/markdoc/CodeStep/CodeStep.tsx +67 -0
- package/markdoc/components.tsx +2 -0
- package/markdoc/schema.ts +23 -0
- package/package.json +93 -0
- package/src/layouts/CodeGuide/CodeDemo.tsx +53 -0
- package/src/layouts/CodeGuide/CodeGuide.tsx +47 -0
- package/src/mocks/CodeGuide/index.tsx +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(c) Copyright 2023 Redocly Inc, all rights reserved.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
For more details, contact team@redocly.com.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { Card } from './Card';
|
|
6
|
+
|
|
7
|
+
type Story = StoryObj<typeof Card>;
|
|
8
|
+
|
|
9
|
+
const meta: Meta<typeof Card> = {
|
|
10
|
+
title: 'Components/Card',
|
|
11
|
+
component: Card,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
title: 'Card Links',
|
|
19
|
+
links: {
|
|
20
|
+
type: 'group' as const,
|
|
21
|
+
label: 'Card Links',
|
|
22
|
+
items: [
|
|
23
|
+
{
|
|
24
|
+
type: 'link' as const,
|
|
25
|
+
link: '#1',
|
|
26
|
+
label: 'Link 1',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'link' as const,
|
|
30
|
+
link: '#2',
|
|
31
|
+
label: 'Link 2',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
render: (args) => <Card {...args} />,
|
|
37
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Link } from '@redocly/theme/components/Link/Link';
|
|
4
|
+
import { H3 } from '@redocly/theme/components/Typography/H3';
|
|
5
|
+
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
6
|
+
|
|
7
|
+
import type { ResolvedNavItem } from '@redocly/theme/core/types';
|
|
8
|
+
|
|
9
|
+
export interface CardProps {
|
|
10
|
+
title?: string;
|
|
11
|
+
icon?: string;
|
|
12
|
+
links: ResolvedNavItem;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function Card(props: CardProps): JSX.Element {
|
|
17
|
+
const { useTranslate } = useThemeHooks();
|
|
18
|
+
const { translate } = useTranslate();
|
|
19
|
+
return (
|
|
20
|
+
<CardWrapper data-component-name="Cards/Card" className={props.className}>
|
|
21
|
+
{props.icon && <img src={props?.icon} alt={props?.title} />}
|
|
22
|
+
{props.title && <H3>{props.title}</H3>}
|
|
23
|
+
{props.links.items && (
|
|
24
|
+
<CardLinksList>
|
|
25
|
+
{props.links.items.map((item) => (
|
|
26
|
+
<li key={item.label}>
|
|
27
|
+
<Link to={item.link as string}>
|
|
28
|
+
{translate(item.labelTranslationKey, item.label)}
|
|
29
|
+
</Link>
|
|
30
|
+
</li>
|
|
31
|
+
))}
|
|
32
|
+
</CardLinksList>
|
|
33
|
+
)}
|
|
34
|
+
</CardWrapper>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const CardWrapper = styled.div`
|
|
39
|
+
border-radius: 10px;
|
|
40
|
+
box-shadow: 0 10px 30px 0 rgba(35, 35, 35, 0.1);
|
|
41
|
+
padding: 20px;
|
|
42
|
+
margin: 0 20px 20px 0;
|
|
43
|
+
min-width: 25%;
|
|
44
|
+
font-family: var(--font-family-base);
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const CardLinksList = styled.ul`
|
|
48
|
+
list-style-type: none;
|
|
49
|
+
margin: 0;
|
|
50
|
+
padding: 0;
|
|
51
|
+
|
|
52
|
+
li {
|
|
53
|
+
margin-bottom: 10px;
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const CardsBlock = styled.div.attrs<{ className?: string }>(({ className }) => ({
|
|
4
|
+
'data-component-name': 'Cards/CardsBlock',
|
|
5
|
+
className,
|
|
6
|
+
}))`
|
|
7
|
+
display: flex;
|
|
8
|
+
padding: 20px 0;
|
|
9
|
+
justify-content: space-between;
|
|
10
|
+
`;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Card } from '../Card';
|
|
4
|
+
import { renderWithRouter } from '../../../../../tests/utils/';
|
|
5
|
+
|
|
6
|
+
jest.mock('@redocly/theme/hooks/useThemeHooks');
|
|
7
|
+
|
|
8
|
+
const links = {
|
|
9
|
+
type: 'group' as const,
|
|
10
|
+
label: 'Card Links',
|
|
11
|
+
items: [
|
|
12
|
+
{
|
|
13
|
+
type: 'link' as const,
|
|
14
|
+
link: '#1',
|
|
15
|
+
label: 'Link 1',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
type: 'link' as const,
|
|
19
|
+
link: '#2',
|
|
20
|
+
label: 'Link 2',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
describe('Card', () => {
|
|
26
|
+
const props = {
|
|
27
|
+
title: 'Example Card',
|
|
28
|
+
icon: 'example.png',
|
|
29
|
+
links,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
it('renders', () => {
|
|
33
|
+
const { container } = renderWithRouter(<Card title="Card Links" links={links} />);
|
|
34
|
+
|
|
35
|
+
expect(container.firstChild).toMatchSnapshot();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('renders the title when provided', () => {
|
|
39
|
+
const screen = renderWithRouter(<Card {...props} />);
|
|
40
|
+
expect(screen.getByText('Example Card')).toBeInTheDocument();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('renders the icon when provided', () => {
|
|
44
|
+
const screen = renderWithRouter(<Card {...props} />);
|
|
45
|
+
expect(screen.getByAltText('Example Card')).toHaveAttribute('src', 'example.png');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('renders the links when provided', () => {
|
|
49
|
+
const screen = renderWithRouter(<Card {...props} />);
|
|
50
|
+
expect(screen.getByText('Link 1')).toHaveAttribute('href', '/#1');
|
|
51
|
+
expect(screen.getByText('Link 2')).toHaveAttribute('href', '/#2');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('does not render the title when not provided', () => {
|
|
55
|
+
const { container } = renderWithRouter(<Card links={props.links} />);
|
|
56
|
+
expect(container.querySelector('h3')).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('does not render the icon when not provided', () => {
|
|
60
|
+
const { container } = renderWithRouter(<Card title={props.title} links={props.links} />);
|
|
61
|
+
expect(container.querySelector('img')).toBeNull();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import { CardsBlock } from '../CardsBlock';
|
|
5
|
+
|
|
6
|
+
describe('CardsBlock', () => {
|
|
7
|
+
it('renders', () => {
|
|
8
|
+
const { container } = render(<CardsBlock></CardsBlock>);
|
|
9
|
+
|
|
10
|
+
expect(container.firstChild).toMatchSnapshot();
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React, { useRef, useContext, useEffect, useCallback } from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { PropsWithChildren } from 'react';
|
|
5
|
+
|
|
6
|
+
import { CodeGuideContext, useCodeStepHandlers } from '@portal/CodeGuide';
|
|
7
|
+
|
|
8
|
+
interface CodeStepProps {
|
|
9
|
+
bindTo: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function CodeStep({ bindTo, children }: PropsWithChildren<CodeStepProps>) {
|
|
13
|
+
const context = useContext(CodeGuideContext);
|
|
14
|
+
|
|
15
|
+
const node = useRef<HTMLDivElement | null>(null);
|
|
16
|
+
|
|
17
|
+
const isSectionActive = context.activeDocsSection === bindTo;
|
|
18
|
+
const [file, lines] = bindTo.split('#');
|
|
19
|
+
|
|
20
|
+
const { onScroll, activateSection } = useCodeStepHandlers({
|
|
21
|
+
nodeRef: node,
|
|
22
|
+
bindTo,
|
|
23
|
+
context: context,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const scrollListener = useCallback(() => {
|
|
27
|
+
onScroll(context.codeBlock);
|
|
28
|
+
}, [context.codeBlock, onScroll]);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
window.addEventListener('scroll', scrollListener);
|
|
32
|
+
|
|
33
|
+
return () => {
|
|
34
|
+
window.removeEventListener('scroll', scrollListener);
|
|
35
|
+
};
|
|
36
|
+
}, [context.codeBlock, scrollListener]);
|
|
37
|
+
|
|
38
|
+
const setRef = (instance?: HTMLDivElement | null) => {
|
|
39
|
+
if (instance) {
|
|
40
|
+
node.current = instance;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Wrapper
|
|
46
|
+
onClick={() => activateSection(file, lines, context.codeBlock)}
|
|
47
|
+
active={isSectionActive}
|
|
48
|
+
ref={setRef}
|
|
49
|
+
>
|
|
50
|
+
{children}
|
|
51
|
+
</Wrapper>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const Wrapper = styled.div<{ active: boolean }>`
|
|
56
|
+
padding: 0 32px 16px;
|
|
57
|
+
box-sizing: border-box;
|
|
58
|
+
border-left: 3px solid;
|
|
59
|
+
border-left-color: ${({ active }) => (active ? 'var(--color-primary-base)' : 'transparent')};
|
|
60
|
+
background-color: ${({ active }) => (active ? 'var(--bg-color-raised)' : 'transparent')};
|
|
61
|
+
border-top: 1px solid #cbccd1;
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
|
|
64
|
+
&:hover {
|
|
65
|
+
border-left-color: ${({ active }) => (active ? 'var(--color-primary-base)' : '#cbccd1')};
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Schema } from '@markdoc/markdoc';
|
|
2
|
+
|
|
3
|
+
export const codeStep: Schema = {
|
|
4
|
+
attributes: {
|
|
5
|
+
bindTo: { type: String, required: false },
|
|
6
|
+
},
|
|
7
|
+
render: 'CodeStep',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const card: Schema = {
|
|
11
|
+
attributes: {
|
|
12
|
+
title: { type: String, required: false },
|
|
13
|
+
icon: { type: String, required: false },
|
|
14
|
+
links: { type: String },
|
|
15
|
+
},
|
|
16
|
+
selfClosing: true,
|
|
17
|
+
render: 'Card',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const cardsBlock: Schema = {
|
|
21
|
+
attributes: {},
|
|
22
|
+
render: 'CardsBlock',
|
|
23
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@redocly/theme-experimental",
|
|
3
|
+
"version": "0.0.0-beta-20240215154132",
|
|
4
|
+
"description": "Experimental UI components lib",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"theme-experimental",
|
|
7
|
+
"redocly"
|
|
8
|
+
],
|
|
9
|
+
"author": "team@redocly.com",
|
|
10
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
11
|
+
"exports": {
|
|
12
|
+
"./package.json": "./package.json",
|
|
13
|
+
"./src/": "./src/"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"lodash.throttle": "^4.1.1",
|
|
17
|
+
"prismjs": "^1.28.0",
|
|
18
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
19
|
+
"react-dom": "^17.0.0 || ^18.0.0",
|
|
20
|
+
"react-router-dom": "^6.21.1",
|
|
21
|
+
"styled-components": "^4.1.1 || ^5.3.11",
|
|
22
|
+
"styled-system": "^5.1.5",
|
|
23
|
+
"@markdoc/markdoc": "0.4.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@storybook/addon-actions": "7.6.4",
|
|
27
|
+
"@storybook/addon-essentials": "7.6.4",
|
|
28
|
+
"@storybook/addon-interactions": "7.6.4",
|
|
29
|
+
"@storybook/addon-links": "7.6.4",
|
|
30
|
+
"@storybook/addon-viewport": "7.6.4",
|
|
31
|
+
"@storybook/addons": "7.6.4",
|
|
32
|
+
"@storybook/core-common": "7.6.4",
|
|
33
|
+
"@storybook/node-logger": "7.6.4",
|
|
34
|
+
"@storybook/react": "7.6.4",
|
|
35
|
+
"@storybook/react-webpack5": "7.6.4",
|
|
36
|
+
"@storybook/testing-library": "0.2.2",
|
|
37
|
+
"@storybook/theming": "7.6.4",
|
|
38
|
+
"@testing-library/jest-dom": "6.1.5",
|
|
39
|
+
"@testing-library/react": "14.1.2",
|
|
40
|
+
"@testing-library/user-event": "14.5.1",
|
|
41
|
+
"@types/highlight-words-core": "1.2.3",
|
|
42
|
+
"@types/jest": "29.5.11",
|
|
43
|
+
"@types/jest-when": "3.5.5",
|
|
44
|
+
"@types/lodash.throttle": "4.1.9",
|
|
45
|
+
"@types/node": "18.19.3",
|
|
46
|
+
"@types/prismjs": "1.26.3",
|
|
47
|
+
"@types/react": "18.2.45",
|
|
48
|
+
"@types/react-dom": "18.2.17",
|
|
49
|
+
"@types/styled-components": "5.1.34",
|
|
50
|
+
"@types/styled-system": "5.1.22",
|
|
51
|
+
"@typescript-eslint/eslint-plugin": "5.55.0",
|
|
52
|
+
"@typescript-eslint/parser": "5.55.0",
|
|
53
|
+
"chromatic": "6.17.2",
|
|
54
|
+
"concurrently": "7.6.0",
|
|
55
|
+
"jest": "29.5.0",
|
|
56
|
+
"jest-environment-jsdom": "29.5.0",
|
|
57
|
+
"jest-styled-components": "7.2.0",
|
|
58
|
+
"jest-when": "3.6.0",
|
|
59
|
+
"json-schema-to-ts": "2.7.2",
|
|
60
|
+
"lodash.throttle": "4.1.1",
|
|
61
|
+
"npm-run-all": "4.1.5",
|
|
62
|
+
"react-refresh": "0.14.0",
|
|
63
|
+
"react-router-dom": "6.21.1",
|
|
64
|
+
"storybook": "7.6.4",
|
|
65
|
+
"storybook-addon-pseudo-states": "2.1.2",
|
|
66
|
+
"storybook-design-token": "3.0.0-beta.6",
|
|
67
|
+
"styled-components": "5.3.11",
|
|
68
|
+
"styled-system": "5.1.5",
|
|
69
|
+
"ts-jest": "29.1.1",
|
|
70
|
+
"ts-node": "10.9.1",
|
|
71
|
+
"ts-node-dev": "2.0.0",
|
|
72
|
+
"tsc-alias": "1.8.3",
|
|
73
|
+
"tsconfig-paths": "4.2.0",
|
|
74
|
+
"tsconfig-paths-webpack-plugin": "3.5.2",
|
|
75
|
+
"typescript": "5.2.2",
|
|
76
|
+
"webpack": "5.88.2"
|
|
77
|
+
},
|
|
78
|
+
"dependencies": {
|
|
79
|
+
"@redocly/theme": "^0.0.0-beta-20240215154132"
|
|
80
|
+
},
|
|
81
|
+
"scripts": {
|
|
82
|
+
"start": "npm-run-all --parallel storybook storybook:tokens:watch",
|
|
83
|
+
"watch": "tsc -p tsconfig.build.json && (concurrently \"tsc -w -p tsconfig.build.json\" \"tsc-alias -w -p tsconfig.build.json\")",
|
|
84
|
+
"ts:check": "tsc --noEmit --skipLibCheck",
|
|
85
|
+
"clean": "rm -rf lib",
|
|
86
|
+
"compile": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
87
|
+
"build": "npm run clean && npm run compile",
|
|
88
|
+
"storybook": "storybook dev -p 6007",
|
|
89
|
+
"storybook:build": "npm run storybook:tokens && storybook build",
|
|
90
|
+
"storybook:tokens": "ts-node scripts/generate-css-tokens.ts",
|
|
91
|
+
"storybook:tokens:watch": "ts-node-dev --respawn scripts/generate-css-tokens.ts"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { CodeBlock } from '@redocly/theme/components/CodeBlock/CodeBlock';
|
|
4
|
+
|
|
5
|
+
import { CodeGuideContext, useCodeGuideHighlightLines } from '@portal/CodeGuide';
|
|
6
|
+
|
|
7
|
+
export function CodeDemo() {
|
|
8
|
+
const { highlightedLines, activeCodeTab, setCurrentCodeSample, setCodeNode, codeGuideFiles } =
|
|
9
|
+
React.useContext(CodeGuideContext);
|
|
10
|
+
|
|
11
|
+
useCodeGuideHighlightLines(highlightedLines);
|
|
12
|
+
|
|
13
|
+
const codeBlockRef = React.useRef<HTMLPreElement | null>(null);
|
|
14
|
+
|
|
15
|
+
const setRef = (instance?: HTMLPreElement | null) => {
|
|
16
|
+
if (instance) {
|
|
17
|
+
codeBlockRef.current = instance;
|
|
18
|
+
setCodeNode(codeBlockRef);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const activeCodeExample = codeGuideFiles.find(({ name }) => activeCodeTab === name);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<CodeDemoWrapper>
|
|
26
|
+
<CodeBlock
|
|
27
|
+
source={activeCodeExample?.content}
|
|
28
|
+
lang={activeCodeExample?.lang}
|
|
29
|
+
withLineNumbers
|
|
30
|
+
header={{
|
|
31
|
+
controls: {
|
|
32
|
+
copy: {},
|
|
33
|
+
},
|
|
34
|
+
}}
|
|
35
|
+
codeBlockRef={setRef}
|
|
36
|
+
codeBlockMaxHeight="75vh"
|
|
37
|
+
hideCodeColors={true}
|
|
38
|
+
tabs={{
|
|
39
|
+
files: codeGuideFiles,
|
|
40
|
+
activeTabName: activeCodeTab,
|
|
41
|
+
handleTabSwitch: setCurrentCodeSample,
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
</CodeDemoWrapper>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const CodeDemoWrapper = styled.div`
|
|
49
|
+
background-color: var(--samples-panel-controls-background-color);
|
|
50
|
+
border-radius: 0 var(--global-border-radius);
|
|
51
|
+
padding: 32px 0;
|
|
52
|
+
min-height: 85vh;
|
|
53
|
+
`;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Markdown } from '@redocly/theme/components/Markdown/Markdown';
|
|
4
|
+
|
|
5
|
+
import { CodeGuideProvider } from '@portal/CodeGuide';
|
|
6
|
+
|
|
7
|
+
import { CodeDemo } from './CodeDemo';
|
|
8
|
+
|
|
9
|
+
export default function CodeGuide(props: React.PropsWithChildren<any>) {
|
|
10
|
+
const {
|
|
11
|
+
pageProps: { codeGuideFiles },
|
|
12
|
+
children,
|
|
13
|
+
} = props;
|
|
14
|
+
|
|
15
|
+
const initialState = {
|
|
16
|
+
codeGuideFiles,
|
|
17
|
+
highlightedLines: '',
|
|
18
|
+
activeCodeTab: codeGuideFiles?.[0]?.name || '',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<CodeGuideProvider initialState={initialState}>
|
|
23
|
+
<Wrapper data-component-name="layouts/CodeGuide">
|
|
24
|
+
<Markdown>{children}</Markdown>
|
|
25
|
+
<div>
|
|
26
|
+
<StickyPosition>
|
|
27
|
+
<CodeDemo />
|
|
28
|
+
</StickyPosition>
|
|
29
|
+
</div>
|
|
30
|
+
</Wrapper>
|
|
31
|
+
</CodeGuideProvider>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const Wrapper = styled.div`
|
|
36
|
+
padding-left: 24px;
|
|
37
|
+
display: grid;
|
|
38
|
+
grid-template-columns: 40% 55%;
|
|
39
|
+
grid-column-gap: 45px;
|
|
40
|
+
height: 100%;
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const StickyPosition = styled.div`
|
|
44
|
+
position: sticky;
|
|
45
|
+
top: 50px;
|
|
46
|
+
left: 0;
|
|
47
|
+
`;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export const CodeGuideContext = React.createContext({
|
|
4
|
+
highlightedLines: '',
|
|
5
|
+
activeCodeTab: '',
|
|
6
|
+
activeDocsSection: '',
|
|
7
|
+
codeBlock: undefined,
|
|
8
|
+
codeGuideFiles: [] as CodeGuideFile[],
|
|
9
|
+
updateHighlightedLines: (_lines: string) => {},
|
|
10
|
+
setCurrentCodeSample: (_fileName: string) => {},
|
|
11
|
+
setActiveDocumentationSection: (_sectionId: string) => {},
|
|
12
|
+
setCodeNode: (_node: React.MutableRefObject<HTMLPreElement | null>) => {},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export type CodeGuideFile = { name: string; lang?: string; content: string };
|
|
16
|
+
|
|
17
|
+
export interface CodeGuideProviderProps {
|
|
18
|
+
initialState: {
|
|
19
|
+
codeGuideFiles: CodeGuideFile[];
|
|
20
|
+
highlightedLines: string;
|
|
21
|
+
activeCodeTab: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const CodeGuideProvider = ({
|
|
26
|
+
children,
|
|
27
|
+
initialState,
|
|
28
|
+
}: React.PropsWithChildren<CodeGuideProviderProps>) => {
|
|
29
|
+
const contextValue = {
|
|
30
|
+
highlightedLines: initialState.highlightedLines,
|
|
31
|
+
activeCodeTab: initialState.activeCodeTab,
|
|
32
|
+
activeDocsSection: '',
|
|
33
|
+
codeBlock: undefined,
|
|
34
|
+
codeGuideFiles: [] as CodeGuideFile[],
|
|
35
|
+
updateHighlightedLines: (_lines: string) => {},
|
|
36
|
+
setCurrentCodeSample: (_fileName: string) => {},
|
|
37
|
+
setActiveDocumentationSection: (_sectionId: string) => {},
|
|
38
|
+
setCodeNode: (_node: React.MutableRefObject<HTMLPreElement | null>) => {},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return <CodeGuideContext.Provider value={contextValue}>{children}</CodeGuideContext.Provider>;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// @ts-ignore
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
46
|
+
export function useCodeGuideHighlightLines(highlightedLines: string) {}
|
|
47
|
+
|
|
48
|
+
type CodeStepHandlersProps = {
|
|
49
|
+
nodeRef: React.RefObject<HTMLDivElement>;
|
|
50
|
+
bindTo: string;
|
|
51
|
+
context: typeof CodeGuideContext.Provider.prototype.value;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
56
|
+
export function useCodeStepHandlers({ nodeRef, bindTo, context }: CodeStepHandlersProps): {
|
|
57
|
+
onScroll: (codeBlock: HTMLPreElement | undefined) => void;
|
|
58
|
+
activateSection: (file: string, lines: string, codeBlock: HTMLPreElement | undefined) => void;
|
|
59
|
+
} {
|
|
60
|
+
const onScroll = (codeBlock: HTMLPreElement | undefined) => {
|
|
61
|
+
if (nodeRef.current) {
|
|
62
|
+
const offsetTop = nodeRef.current.getBoundingClientRect().top;
|
|
63
|
+
if (offsetTop < 300 && offsetTop > 150) {
|
|
64
|
+
activateSection('', '', codeBlock);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
71
|
+
const activateSection = (file: string, lines: string, codeBlock: HTMLPreElement | undefined) => {
|
|
72
|
+
codeBlock?.scrollTo(0, 0);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
onScroll,
|
|
77
|
+
activateSection,
|
|
78
|
+
};
|
|
79
|
+
}
|