@xyd-js/atlas 0.1.0-build.173

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.
Files changed (68) hide show
  1. package/.babelrc +6 -0
  2. package/.storybook/index.css +6 -0
  3. package/.storybook/main.ts +19 -0
  4. package/.storybook/preview.ts +17 -0
  5. package/.storybook/public/fonts/fustat-ext-500.woff2 +0 -0
  6. package/.storybook/public/fonts/fustat-ext-600.woff2 +0 -0
  7. package/.storybook/public/fonts/fustat-ext-700.woff2 +0 -0
  8. package/.storybook/public/fonts/fustat-regular.woff2 +0 -0
  9. package/CHANGELOG.md +1984 -0
  10. package/LICENSE +21 -0
  11. package/README.md +3 -0
  12. package/declarations.d.ts +4 -0
  13. package/dist/VideoGuide-C0K9fFar-Dk2lkn4r.js +4 -0
  14. package/dist/VideoGuide-C0K9fFar-Dk2lkn4r.js.map +1 -0
  15. package/dist/VideoGuide-CJYkuLst-Dk2lkn4r.js +4 -0
  16. package/dist/VideoGuide-CJYkuLst-Dk2lkn4r.js.map +1 -0
  17. package/dist/index.css +48 -0
  18. package/dist/index.d.ts +38 -0
  19. package/dist/index.js +2 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/styles.css +104 -0
  22. package/dist/tokens.css +60 -0
  23. package/dist/xydPlugin.d.ts +5 -0
  24. package/dist/xydPlugin.js +2 -0
  25. package/dist/xydPlugin.js.map +1 -0
  26. package/index.ts +2 -0
  27. package/package.json +68 -0
  28. package/packages/xyd-plugin/SidebarItem.tsx +27 -0
  29. package/packages/xyd-plugin/index.ts +20 -0
  30. package/postcss.config.cjs +5 -0
  31. package/rollup.config.js +120 -0
  32. package/src/components/ApiRef/ApiRefItem/ApiRefItem.styles.tsx +110 -0
  33. package/src/components/ApiRef/ApiRefItem/ApiRefItem.tsx +557 -0
  34. package/src/components/ApiRef/ApiRefItem/index.ts +7 -0
  35. package/src/components/ApiRef/ApiRefProperties/ApiRefProperties.styles.tsx +202 -0
  36. package/src/components/ApiRef/ApiRefProperties/ApiRefProperties.tsx +665 -0
  37. package/src/components/ApiRef/ApiRefProperties/index.ts +7 -0
  38. package/src/components/ApiRef/ApiRefSamples/ApiRefSamples.styles.tsx +28 -0
  39. package/src/components/ApiRef/ApiRefSamples/ApiRefSamples.tsx +69 -0
  40. package/src/components/ApiRef/ApiRefSamples/index.ts +7 -0
  41. package/src/components/ApiRef/index.ts +5 -0
  42. package/src/components/Atlas/Atlas.styles.tsx +5 -0
  43. package/src/components/Atlas/Atlas.tsx +43 -0
  44. package/src/components/Atlas/AtlasContext.tsx +47 -0
  45. package/src/components/Atlas/AtlasDecorator.styles.ts +22 -0
  46. package/src/components/Atlas/AtlasDecorator.tsx +15 -0
  47. package/src/components/Atlas/AtlasLazy/AtlasLazy.styles.tsx +9 -0
  48. package/src/components/Atlas/AtlasLazy/AtlasLazy.tsx +42 -0
  49. package/src/components/Atlas/AtlasLazy/hooks.ts +29 -0
  50. package/src/components/Atlas/AtlasLazy/index.ts +7 -0
  51. package/src/components/Atlas/AtlasPrimary.tsx +21 -0
  52. package/src/components/Atlas/AtlasSecondary.tsx +148 -0
  53. package/src/components/Atlas/index.ts +7 -0
  54. package/src/components/Atlas/types.ts +11 -0
  55. package/src/components/Code/CodeSampleButtons/CodeSampleButtons.styles.tsx +58 -0
  56. package/src/components/Code/CodeSampleButtons/CodeSampleButtons.tsx +97 -0
  57. package/src/components/Code/CodeSampleButtons/index.ts +7 -0
  58. package/src/components/Code/index.ts +2 -0
  59. package/src/components/Icon/index.tsx +386 -0
  60. package/src/docs/AtlasExample/AtlasExample.stories.tsx +47 -0
  61. package/src/docs/AtlasExample/todo-app.uniform.json +625 -0
  62. package/src/docs/AtlasExample/uniform-to-references.ts +101 -0
  63. package/src/styles/styles.css +104 -0
  64. package/src/styles/tokens.css +60 -0
  65. package/src/utils/mdx.ts +2 -0
  66. package/tsconfig.json +51 -0
  67. package/types.d.ts +22 -0
  68. package/vite.config.ts +25 -0
@@ -0,0 +1,69 @@
1
+ import React, { useState, useMemo } from "react";
2
+ import { UXNode } from "openux-js";
3
+
4
+ import { ExampleRoot } from "@xyd-js/uniform";
5
+ import { CodeSample, type CodeThemeBlockProps } from "@xyd-js/components/coder";
6
+
7
+ import { CodeExampleButtons } from "@/components/Code";
8
+ import { useSyntaxHighlight } from "@/components/Atlas/AtlasContext";
9
+
10
+ import * as cn from "./ApiRefSamples.styles";
11
+
12
+ export interface ApiRefSamplesProps {
13
+ examples: ExampleRoot
14
+ }
15
+
16
+ export function ApiRefSamples({ examples }: ApiRefSamplesProps) {
17
+ const syntaxHighlight = useSyntaxHighlight()
18
+ const [activeExampleIndices, setActiveExampleIndices] = useState<Record<number, number>>({})
19
+
20
+ const handleExampleChange = (groupIndex: number, exampleIndex: number) => {
21
+ setActiveExampleIndices(prev => ({
22
+ ...prev,
23
+ [groupIndex]: exampleIndex
24
+ }))
25
+ }
26
+
27
+ return <atlas-apiref-samples className={cn.ApiRefSamplesContainerHost}>
28
+ {
29
+ examples.groups?.map(({ description, examples: example }, i) => {
30
+ const activeExampleIndex = activeExampleIndices[i] || 0
31
+ const activeExample = example[activeExampleIndex]
32
+
33
+ const codeblocks = activeExample?.codeblock?.tabs?.map(tab => ({
34
+ value: String(tab.code || ""),
35
+ lang: String(tab.language || ""),
36
+ meta: String(tab.context || ""),
37
+ highlighted: tab.highlighted
38
+ } as CodeThemeBlockProps)) || []
39
+
40
+ return <UXNode
41
+ name="APIRefSample"
42
+ props={activeExample}
43
+ >
44
+ <div key={i} className={cn.ApiRefSamplesGroupHost}>
45
+ {
46
+ example?.length > 1
47
+ ? <CodeExampleButtons
48
+ activeExample={activeExample}
49
+ examples={example}
50
+ onClick={(ex) => {
51
+ const index = example.findIndex(e => e === ex)
52
+ handleExampleChange(i, index)
53
+ }}
54
+ />
55
+ : null
56
+ }
57
+ <CodeSample
58
+ name={String(i)}
59
+ description={description || ""}
60
+ codeblocks={codeblocks}
61
+ theme={syntaxHighlight || undefined}
62
+ // controlByMeta
63
+ />
64
+ </div>
65
+ </UXNode>
66
+ })
67
+ }
68
+ </atlas-apiref-samples>
69
+ }
@@ -0,0 +1,7 @@
1
+ export {
2
+ ApiRefSamples
3
+ } from "./ApiRefSamples"
4
+
5
+ export type {
6
+ ApiRefSamplesProps
7
+ } from "./ApiRefSamples"
@@ -0,0 +1,5 @@
1
+ export * from "./ApiRefItem";
2
+
3
+ export * from "./ApiRefProperties";
4
+
5
+ export * from "./ApiRefSamples";
@@ -0,0 +1,5 @@
1
+ import {css} from "@linaria/core";
2
+
3
+ export const AtlasHost = css`
4
+ width: 100%;
5
+ `;
@@ -0,0 +1,43 @@
1
+ import React from "react";
2
+
3
+ import { MDXCommonAtlasProps } from "./types";
4
+ import { AtlasDecorator } from "./AtlasDecorator";
5
+ import { AtlasPrimary } from "./AtlasPrimary";
6
+ import { AtlasSecondary } from "./AtlasSecondary";
7
+
8
+ import * as cn from "./Atlas.styles";
9
+
10
+ interface AtlasProps<T> extends MDXCommonAtlasProps<T> {
11
+ kind: "secondary" | "primary" | undefined | null
12
+ }
13
+
14
+ export function Atlas<T>(props: AtlasProps<T>) {
15
+ let AtlasComponent: React.FC<MDXCommonAtlasProps<T>>;
16
+
17
+ if (props.kind === "secondary") {
18
+ AtlasComponent = AtlasSecondary;
19
+ } else {
20
+ AtlasComponent = AtlasPrimary;
21
+ }
22
+
23
+ let references = props.references
24
+ {
25
+ // TODO: find better solution - if we pass from md then its string
26
+ if (references && typeof references === "string") { // TODO: DO IT BETTER
27
+ try {
28
+ references = JSON.parse(references)
29
+ } catch (error) {
30
+ console.error("Error parsing references", error)
31
+ }
32
+ }
33
+ }
34
+
35
+ return <AtlasDecorator>
36
+ <div className={cn.AtlasHost}>
37
+ <AtlasComponent
38
+ references={references}
39
+ apiRefItemKind={props.apiRefItemKind}
40
+ />
41
+ </div>
42
+ </AtlasDecorator>
43
+ }
@@ -0,0 +1,47 @@
1
+ import { createContext, useContext } from "react";
2
+
3
+ import { type Theme } from "@code-hike/lighter";
4
+
5
+ export interface VariantToggleConfig {
6
+ key: string;
7
+ defaultValue: string;
8
+ }
9
+
10
+ export const AtlasContext = createContext<{
11
+ syntaxHighlight: Theme | null,
12
+ baseMatch?: string,
13
+ variantToggles?: VariantToggleConfig[], // Array of toggle configurations
14
+ Link?: any
15
+ }>({
16
+ syntaxHighlight: null
17
+ })
18
+
19
+ export function useSyntaxHighlight() {
20
+ const context = useContext(AtlasContext)
21
+
22
+ if (!context) {
23
+ throw new Error("useSyntaxHighlight must be used within a AtlasContext")
24
+ }
25
+
26
+ return context.syntaxHighlight
27
+ }
28
+
29
+ export function useBaseMatch() {
30
+ const context = useContext(AtlasContext)
31
+
32
+ if (!context) {
33
+ throw new Error("useBaseMatch must be used within a AtlasContext")
34
+ }
35
+
36
+ return context.baseMatch
37
+ }
38
+
39
+ export function useVariantToggles() {
40
+ const context = useContext(AtlasContext)
41
+
42
+ if (!context) {
43
+ throw new Error("useVariantToggles must be used within a AtlasContext")
44
+ }
45
+
46
+ return context.variantToggles || []
47
+ }
@@ -0,0 +1,22 @@
1
+ import { css } from "@linaria/core";
2
+
3
+ export default {
4
+ AtlasDecoratorHost: css`
5
+ @layer templates {
6
+ atlas-apiref-proptype {
7
+ font-size: var(--xyd-font-size-xsmall);
8
+ line-height: var(--xyd-line-height-xsmall);
9
+ }
10
+
11
+ atlas-apiref-item-showcase {
12
+ font-size: var(--xyd-font-size-small);
13
+ line-height: var(--xyd-line-height-medium);
14
+
15
+ p {
16
+ font-size: var(--xyd-font-size-small);
17
+ line-height: var(--xyd-line-height-medium);
18
+ }
19
+ }
20
+ }
21
+ `
22
+ }
@@ -0,0 +1,15 @@
1
+ import React from 'react'
2
+
3
+ import cn from "./AtlasDecorator.styles";
4
+
5
+ interface AtlasDecoratorProps {
6
+ children: React.ReactNode
7
+ }
8
+
9
+ export function AtlasDecorator({ children }: AtlasDecoratorProps) {
10
+ return <atlas-decorator
11
+ className={cn.AtlasDecoratorHost}
12
+ >
13
+ {children}
14
+ </atlas-decorator>
15
+ }
@@ -0,0 +1,9 @@
1
+ import {css} from "@linaria/core";
2
+
3
+ export const AtlasLazyItemHost = css`
4
+ padding: 16px 0;
5
+ `;
6
+
7
+ export const AtlasLazyItemFirst = css`
8
+ padding: 4px 0;
9
+ `;
@@ -0,0 +1,42 @@
1
+ import React, {useEffect, useRef} from "react";
2
+
3
+ import {Reference} from "@xyd-js/uniform";
4
+
5
+ import {ApiRefItem} from "@/components/ApiRef";
6
+
7
+ import * as cn from "./AtlasLazy.styles";
8
+
9
+ export interface AtlasLazyProps {
10
+ references: Reference[]
11
+ urlPrefix: string
12
+ slug: string,
13
+ onLoaded?: () => void
14
+ }
15
+
16
+ export function AtlasLazy(props: AtlasLazyProps) {
17
+ return props.references?.map((reference: any, i: number) => <>
18
+ <div
19
+ key={i}
20
+ // TODO: slug should be passed from reference or somrthing
21
+ // ref={`api-reference/${reference.title}` === slug ? targetRef : null} // Attach ref to the 30th item
22
+ className={`${cn.AtlasLazyItemHost} ${i === 0 && cn.AtlasLazyItemFirst}`}
23
+ // TODO: slug prefix props
24
+ data-slug={`${props.urlPrefix}/${reference.canonical?.title}`}
25
+ >
26
+ <ItemWrapper
27
+ reference={reference}
28
+ onLoad={i === props.references.length - 1 ? props.onLoaded : null}
29
+ />
30
+ </div>
31
+ </>)
32
+ }
33
+
34
+ function ItemWrapper({reference, onLoad}) {
35
+ useEffect(() => {
36
+ onLoad && onLoad()
37
+ }, []);
38
+
39
+ return <>
40
+ <ApiRefItem reference={reference}/>
41
+ </>
42
+ }
@@ -0,0 +1,29 @@
1
+ import {useEffect} from "react";
2
+
3
+ function getScrollPosition() {
4
+ return sessionStorage.getItem('scrollPosition');
5
+ }
6
+
7
+ export function useScrollRestoration() {
8
+ useEffect(() => {
9
+ const saveScrollPosition = () => {
10
+ sessionStorage.setItem('scrollPosition', window.scrollY.toString());
11
+ };
12
+
13
+ window.addEventListener('beforeunload', saveScrollPosition);
14
+
15
+ return () => {
16
+ window.removeEventListener('beforeunload', saveScrollPosition);
17
+ };
18
+ }, []);
19
+
20
+ return [
21
+ () => getScrollPosition(),
22
+ () => {
23
+ const scrollPosition = getScrollPosition();
24
+ if (scrollPosition) {
25
+ window.scrollTo(0, parseInt(scrollPosition, 10));
26
+ }
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,7 @@
1
+ export {
2
+ AtlasLazy,
3
+ } from "./AtlasLazy";
4
+
5
+ export type {
6
+ AtlasLazyProps
7
+ } from "./AtlasLazy";
@@ -0,0 +1,21 @@
1
+ import React, {} from "react";
2
+
3
+ import {ApiRefItem} from "@/components/ApiRef";
4
+
5
+ import {MDXCommonAtlasProps} from "./types";
6
+
7
+ export function AtlasPrimary<T>(props: MDXCommonAtlasProps<T>) {
8
+ return <>
9
+ {
10
+ props.references?.map((reference, i) =>
11
+ <ApiRefItem
12
+ key={i}
13
+ reference={{
14
+ ...reference
15
+ }}
16
+ kind={props.apiRefItemKind || undefined}
17
+ />
18
+ )
19
+ }
20
+ </>
21
+ }
@@ -0,0 +1,148 @@
1
+ import * as React from "react"
2
+
3
+ import {type Theme} from "@code-hike/lighter";
4
+
5
+ import {
6
+ Heading,
7
+ Table,
8
+ Details,
9
+ Code,
10
+ } from "@xyd-js/components/writer";
11
+ import {CodeSample} from "@xyd-js/components/coder";
12
+
13
+ import {MDXCommonAtlasProps} from "@/components/Atlas/types";
14
+ import {useSyntaxHighlight} from "./AtlasContext";
15
+ import {IconQuote} from "@/components/Icon";
16
+
17
+ // TODO: interface should be imported from somewhere
18
+ interface CodeSourceContext {
19
+ fileName: string;
20
+ fileFullPath: string;
21
+ sourcecode: string;
22
+ }
23
+
24
+ const MAX_REFERENCES = 2;
25
+
26
+ interface ReferenceItemProps {
27
+ reference: any;
28
+ index: number;
29
+ syntaxHighlight: Theme | null;
30
+ }
31
+
32
+
33
+ export function AtlasSecondary<T>({references}: MDXCommonAtlasProps<T>) {
34
+ const syntaxHighlight = useSyntaxHighlight()
35
+
36
+ if (!references) return null;
37
+
38
+ const initialReferences = references.slice(0, MAX_REFERENCES);
39
+ const remainingReferences = references.slice(MAX_REFERENCES);
40
+
41
+ const showMoreText = remainingReferences.length > 0 ?
42
+ `Show more (${remainingReferences.length}) reference${remainingReferences.length === 1 ? '' : 's'}` :
43
+ '';
44
+
45
+ return (
46
+ <>
47
+ {initialReferences.map((reference, i) => (
48
+ <$ReferenceItem
49
+ key={i}
50
+ reference={reference}
51
+ index={i}
52
+ syntaxHighlight={syntaxHighlight}
53
+ />
54
+ ))}
55
+
56
+ {remainingReferences.length > 0 && (
57
+ <Details
58
+ label={showMoreText}
59
+ >
60
+ {remainingReferences.map((reference, i) => (
61
+ <$ReferenceItem
62
+ key={i + MAX_REFERENCES}
63
+ reference={reference}
64
+ index={i + MAX_REFERENCES}
65
+ syntaxHighlight={syntaxHighlight}
66
+ />
67
+ ))}
68
+ </Details>
69
+ )}
70
+ </>
71
+ )
72
+ }
73
+
74
+ function $ReferenceItem({reference, index, syntaxHighlight}: ReferenceItemProps) {
75
+ return (
76
+ <React.Fragment key={index}>
77
+ <Heading size={3}>
78
+ {reference.title}
79
+ </Heading>
80
+
81
+ {/*<p>*/}
82
+ {/* {uniformChild(reference.description)}*/}
83
+ {/*</p>*/}
84
+
85
+ {
86
+ reference.context?.fileName && <Details
87
+ label=""
88
+ kind="tertiary"
89
+ title={<>
90
+ Source code in <Code>{reference.context.fileFullPath}</Code>
91
+ </>}
92
+ icon={<IconQuote/>}>
93
+ <CodeSample
94
+ name={reference.context.fileName}
95
+ description={reference.context.sourcecode.description}
96
+ theme={syntaxHighlight || undefined}
97
+ codeblocks={[
98
+ {
99
+ lang: reference.context.sourcecode.lang,
100
+ meta: "",
101
+ value: reference.context.sourcecode.code
102
+ }
103
+ ]}
104
+ />
105
+ {/* {parseChild(reference.context.sourcecode)} */}
106
+ </Details>
107
+ }
108
+
109
+ {
110
+ reference.definitions.map((definition: any, index: number) => {
111
+ return (
112
+ <React.Fragment key={index}>
113
+ <Heading size={4}>
114
+ {definition.title}
115
+ </Heading>
116
+ <Table>
117
+ <Table.Head>
118
+ <Table.Tr>
119
+ <Table.Th>Name</Table.Th>
120
+ <Table.Th>Type</Table.Th>
121
+ <Table.Th>Description</Table.Th>
122
+ </Table.Tr>
123
+ </Table.Head>
124
+ <Table.Body>
125
+ {definition.properties?.map((property: any, propIndex: number) => (
126
+ <Table.Tr key={propIndex}>
127
+ <Table.Td>
128
+ <Code>{property.name}</Code>
129
+ </Table.Td>
130
+ <Table.Td>
131
+ <Code>{property.type}</Code>
132
+ </Table.Td>
133
+ <Table.Td muted>
134
+ {property.description}
135
+ </Table.Td>
136
+ </Table.Tr>
137
+ ))}
138
+ </Table.Body>
139
+ </Table>
140
+ </React.Fragment>
141
+ )
142
+ })
143
+ }
144
+
145
+ <br/>
146
+ </React.Fragment>
147
+ )
148
+ }
@@ -0,0 +1,7 @@
1
+ export { AtlasLazy } from "./AtlasLazy"
2
+
3
+ export { Atlas } from "./Atlas"
4
+
5
+ export { AtlasContext, type VariantToggleConfig } from "./AtlasContext"
6
+
7
+ export type { AtlasProps } from "./types"
@@ -0,0 +1,11 @@
1
+ import type { Reference } from "@xyd-js/uniform";
2
+
3
+ // TODO unify MDXCommonAtlasProps and AtlasProps
4
+ export interface MDXCommonAtlasProps<T> {
5
+ references: Reference<T>[] | []
6
+ apiRefItemKind?: "secondary"
7
+ }
8
+
9
+ export interface AtlasProps {
10
+ references: Reference<any>[]
11
+ }
@@ -0,0 +1,58 @@
1
+ import {css} from "@linaria/core";
2
+
3
+ export const CodeSampleButtonsHost = css`
4
+ position: relative;
5
+ max-width: 100%;
6
+ `;
7
+
8
+ export const CodeSampleButtonsContainer = css`
9
+ display: inline-flex;
10
+ width: 100%;
11
+ align-items: center;
12
+ border-radius: 8px;
13
+ background-color: var(--XydAtlas-Component-Code-SampleButtons__color-containerBackground);
14
+ `;
15
+
16
+ export const CodeSampleButtonsArrowHost = css`
17
+ padding: 8px;
18
+ background-color: var(--white, #ffffff);
19
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
20
+ `;
21
+
22
+ export const CodeSampleButtonsArrowIcon = css`
23
+ width: 16px;
24
+ height: 16px;
25
+ `;
26
+
27
+ export const CodeSampleButtonsScrollerHost = css`
28
+ overflow-x: auto;
29
+ flex-grow: 1;
30
+ font-weight: var(--xyd-font-weight-semibold);
31
+ `;
32
+
33
+ export const CodeSampleButtonsScrollerContainer = css`
34
+ display: inline-flex;
35
+ gap: 4px;
36
+ padding: 4px;
37
+ margin-left: 4px;
38
+ `;
39
+
40
+ export const CodeSampleButtonsButtonHost = css`
41
+ padding: 4px 16px;
42
+ border-radius: 0.375rem;
43
+ white-space: nowrap;
44
+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
45
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
46
+ transition-duration: 300ms;
47
+ color: var(--XydAtlas-Component-Code-SampleButtons__color);
48
+
49
+ &:hover {
50
+ color: var(--XydAtlas-Component-Code-SampleButtons__color--active);
51
+ }
52
+ `;
53
+
54
+ export const CodeSampleButtonsButtonActive = css`
55
+ color: var(--XydAtlas-Component-Code-SampleButtons__color--active);
56
+ background-color: var(--XydAtlas-Component-Code-SampleButtons__color-background--active);
57
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
58
+ `;
@@ -0,0 +1,97 @@
1
+ import React, {useState, useRef, useEffect} from 'react'
2
+ import {ChevronLeft, ChevronRight} from "lucide-react"
3
+
4
+ import {Example} from "@xyd-js/uniform";
5
+
6
+ import * as cn from "./CodeSampleButtons.styles";
7
+
8
+ export interface CodeExampleButtonsProps {
9
+ examples: Example[]
10
+ activeExample: Example | null
11
+ onClick: (example: Example) => void
12
+ }
13
+
14
+ export function CodeExampleButtons({examples, activeExample, onClick}: CodeExampleButtonsProps) {
15
+ const [showLeftArrow, setShowLeftArrow] = useState(false)
16
+ const [showRightArrow, setShowRightArrow] = useState(false)
17
+ const scrollContainerRef = useRef<HTMLDivElement>(null)
18
+
19
+ const handleScroll = () => {
20
+ if (scrollContainerRef.current) {
21
+ const {scrollLeft, scrollWidth, clientWidth} = scrollContainerRef.current
22
+ setShowLeftArrow(scrollLeft > 0)
23
+ setShowRightArrow(scrollLeft < scrollWidth - clientWidth)
24
+ }
25
+ }
26
+
27
+ useEffect(() => {
28
+ handleScroll()
29
+ window.addEventListener('resize', handleScroll)
30
+ return () => window.removeEventListener('resize', handleScroll)
31
+ }, [])
32
+
33
+ const scroll = (direction: 'left' | 'right') => {
34
+ if (scrollContainerRef.current) {
35
+ const scrollAmount = direction === 'left' ? -200 : 200
36
+ scrollContainerRef.current.scrollBy({left: scrollAmount, behavior: 'smooth'})
37
+ }
38
+ }
39
+
40
+ return (
41
+ <div className={cn.CodeSampleButtonsHost}>
42
+ <div className={cn.CodeSampleButtonsContainer}>
43
+ {showLeftArrow && (
44
+ <button
45
+ onClick={() => scroll('left')}
46
+ className={cn.CodeSampleButtonsArrowHost}
47
+ >
48
+ <ChevronLeft className={cn.CodeSampleButtonsArrowIcon}/>
49
+ </button>
50
+ )}
51
+ <div
52
+ ref={scrollContainerRef}
53
+ onScroll={handleScroll}
54
+ className={cn.CodeSampleButtonsScrollerHost}
55
+ >
56
+ <div className={cn.CodeSampleButtonsScrollerContainer}>
57
+ {examples?.map((example) => (
58
+ <SampleButton
59
+ key={example.codeblock.title}
60
+ onClick={() => onClick(example)}
61
+ example={example}
62
+ activeExample={activeExample}
63
+ >
64
+ {(example.codeblock.title || null)}
65
+ </SampleButton>
66
+ ))}
67
+ </div>
68
+ </div>
69
+ {showRightArrow && (
70
+ <button
71
+ onClick={() => scroll('right')}
72
+ className={cn.CodeSampleButtonsArrowHost}
73
+ >
74
+ <ChevronRight className={cn.CodeSampleButtonsArrowIcon}/>
75
+ </button>
76
+ )}
77
+ </div>
78
+ </div>
79
+ )
80
+ }
81
+
82
+ function SampleButton({onClick, children, activeExample, example}: {
83
+ onClick: () => void,
84
+ children: React.ReactNode,
85
+ example: Example,
86
+ activeExample: Example | null,
87
+ }) {
88
+ const markExampleAsActive = (activeExample?.description && activeExample?.description === example?.description) ||
89
+ (activeExample?.codeblock?.title && activeExample?.codeblock?.title === example.codeblock.title)
90
+
91
+ return <button
92
+ onClick={onClick}
93
+ className={`${cn.CodeSampleButtonsButtonHost} ${markExampleAsActive && cn.CodeSampleButtonsButtonActive}`}
94
+ >
95
+ {children}
96
+ </button>
97
+ }
@@ -0,0 +1,7 @@
1
+ export {
2
+ CodeExampleButtons,
3
+ } from "./CodeSampleButtons"
4
+
5
+ export type {
6
+ CodeExampleButtonsProps
7
+ } from "./CodeSampleButtons"
@@ -0,0 +1,2 @@
1
+ export * from "./CodeSampleButtons";
2
+