1
1
import { css } from "@emotion/css" ;
2
2
import { Pre } from "codehike/code" ;
3
- import { UsageSnippet } from "../models/components/usagesnippet.js" ;
4
- import { NonEmptyArray } from "../types/custom.js" ;
3
+ import { useMemo } from "react" ;
4
+ import {
5
+ GetCodeSamplesRequest ,
6
+ MethodPaths ,
7
+ } from "../models/operations/getcodesamples.js" ;
8
+ import {
9
+ useHighlightedCodeSamples ,
10
+ useSafeSpeakeasyCodeSamplesContext ,
11
+ useSelectedSnippet ,
12
+ } from "./hooks.js" ;
13
+ import { LanguageSelector } from "./language-selector.js" ;
14
+ import { lineNumbers } from "./line-numbers.js" ;
15
+ import {
16
+ LanguageSelectorSkeleton ,
17
+ LoadingSkeleton ,
18
+ TitleSkeleton ,
19
+ } from "./skeleton.js" ;
20
+ import {
21
+ cssVarKey ,
22
+ getCssVars ,
23
+ githubColorVars ,
24
+ useSystemColorMode ,
25
+ } from "./styles.js" ;
5
26
import { CodeSampleTitleComponent } from "./titles.js" ;
6
- import { prettyLanguageName } from "./utils.js" ;
7
- import { cssVarKey , getCssVars } from "./styles.js" ;
8
- import { useCodeHighlighting , useSelectedSnippet } from "./hooks.js" ;
27
+ import { tokenTransitions } from "./token-transitions.js" ;
9
28
10
29
const classes = {
11
30
root : css ( {
@@ -24,87 +43,100 @@ const classes = {
24
43
} ) ,
25
44
selector : css ( { } ) ,
26
45
codeContainer : css ( {
46
+ position : "relative" ,
27
47
paddingInline : "0.75rem" ,
48
+ overflowX : "scroll" ,
49
+ } ) ,
50
+ pre : css ( {
51
+ position : "relative" ,
28
52
} ) ,
29
53
} ;
30
54
55
+ type OperationId = string ;
56
+
31
57
export type CodePlaygroundProps = {
32
- snippets : NonEmptyArray < UsageSnippet > ;
33
- theme ?: "dark" | "light" ;
58
+ theme ?: "system" | "dark" | "light" ;
34
59
className ?: string | undefined ;
35
60
title ?: CodeSampleTitleComponent ;
61
+ operation : MethodPaths | OperationId ;
36
62
} ;
37
63
38
64
export function CodePlayground ( {
39
- snippets,
40
- theme = "light" ,
65
+ theme = "system" ,
41
66
className,
42
67
title,
68
+ operation,
43
69
} : CodePlaygroundProps ) {
44
70
const TitleComponent = title ;
45
71
46
- const { selectedSnippet, selectedLang, setSelectedLang } =
47
- useSelectedSnippet ( snippets ) ;
72
+ const systemColorMode = useSystemColorMode ( ) ;
48
73
49
- //const [selectedLang, setSelectedLang] = useState<string>(
50
- // snippets[0].language,
51
- //);
52
- //
53
- //const selectedSnippet: UsageSnippet = useMemo(() => {
54
- // const snippet = snippets.find((s) => s.language === selectedLang);
55
- // if (!snippet)
56
- // throw Error(`The selected language ${selectedLang} does not exist`);
57
- // return snippet;
58
- //}, [selectedLang]);
59
- //
74
+ const codeTheme = useMemo ( ( ) => {
75
+ if ( theme === "system" ) return githubColorVars [ systemColorMode ] ;
76
+ return githubColorVars [ theme ] ;
77
+ } , [ theme , systemColorMode ] ) ;
60
78
61
- const highlighted = useCodeHighlighting (
62
- selectedSnippet . code ,
63
- selectedLang ,
64
- theme ,
65
- ) ;
79
+ const request : GetCodeSamplesRequest = useMemo ( ( ) => {
80
+ if ( typeof operation === "string" ) return { operationIds : [ operation ] } ;
81
+ return { methoPaths : [ operation ] } ;
82
+ } , [ operation ] ) ;
83
+
84
+ const client = useSafeSpeakeasyCodeSamplesContext ( ) ;
85
+ const { status, data, error } = useHighlightedCodeSamples ( client , request ) ;
86
+
87
+ const { selectedSnippet, selectedLang, setSelectedLang } =
88
+ useSelectedSnippet ( data ) ;
66
89
67
- //const [highlighted, setHighlighted] = useState<HighlightedCode | null>(null);
68
- //
69
- //const updateHighlighted = useCallback(
70
- // async (code: string, language: string, theme: CodeHikeTheme) => {
71
- // const highlighted = await highlightCode(code, language, theme);
72
- // setHighlighted(highlighted);
73
- // },
74
- // [],
75
- //);
90
+ const longestCodeHeight = useMemo ( ( ) => {
91
+ const largestLines = Math . max (
92
+ ...Object . values ( data ?? [ ] )
93
+ . filter ( ( snippet ) => snippet . code !== undefined )
94
+ . map ( ( code ) => code . code ! . split ( "\n" ) . length ) ,
95
+ ) ;
76
96
77
- //useEffect(() => {
78
- // const chTheme = theme === "dark" ? "github-dark" : "github-light";
79
- // updateHighlighted(selectedSnippet.code, selectedLang, chTheme);
80
- //}, [selectedSnippet, selectedLang, theme]);
81
- //
82
- //useEffect(() => {
83
- // const chTheme = theme === "dark" ? "github-dark" : "github-light";
84
- // updateHighlighted(selectedSnippet.code, selectedLang, chTheme);
85
- //}, [selectedLang, theme]);
97
+ const lineHeight = 23 ;
98
+ const padding = 12 ;
99
+ return largestLines * lineHeight + padding * 2 ;
100
+ } , [ data ] ) ;
86
101
87
102
return (
88
103
< div
89
- style = { { ...( getCssVars ( theme ) as React . CSSProperties ) } }
104
+ style = { {
105
+ ...codeTheme ,
106
+ ...( getCssVars (
107
+ theme === "system" ? systemColorMode : theme ,
108
+ ) as React . CSSProperties ) ,
109
+ } }
90
110
className = { `${ classes . root } ${ className } ` }
91
111
>
92
112
< div className = { classes . heading } >
93
- { TitleComponent ? < TitleComponent { ...selectedSnippet } /> : < div > </ div > }
94
- < select
95
- value = { selectedLang }
96
- onChange = { ( e ) => setSelectedLang ( e . target . value ) }
97
- className = { classes . selector }
98
- >
99
- { snippets . map ( ( { language } , index ) => (
100
- < option key = { index } value = { language } >
101
- { prettyLanguageName ( language ) }
102
- </ option >
103
- ) ) }
104
- </ select >
113
+ { status === "loading" && error === undefined ? (
114
+ < TitleSkeleton />
115
+ ) : TitleComponent && selectedSnippet ? (
116
+ < TitleComponent { ...selectedSnippet ! . raw } />
117
+ ) : null }
118
+ { status === "loading" && error === undefined ? (
119
+ < LanguageSelectorSkeleton />
120
+ ) : (
121
+ < LanguageSelector
122
+ value = { selectedLang }
123
+ onChange = { setSelectedLang }
124
+ snippets = { data ?? [ ] }
125
+ className = { classes . selector }
126
+ />
127
+ ) }
105
128
</ div >
106
129
< div className = { classes . codeContainer } >
107
- { highlighted && < Pre code = { highlighted } > </ Pre > }
130
+ { status === "loading" ? (
131
+ < LoadingSkeleton />
132
+ ) : selectedSnippet ? (
133
+ < Pre
134
+ className = { classes . pre }
135
+ style = { { height : longestCodeHeight } }
136
+ handlers = { [ lineNumbers , tokenTransitions ] }
137
+ code = { selectedSnippet }
138
+ />
139
+ ) : null }
108
140
</ div >
109
141
</ div >
110
142
) ;
0 commit comments