|
1 | 1 | import React from "react"
|
2 |
| -import { EditorFrame } from "./editor-frame" |
3 |
| -import { InnerTerminal } from "@code-hike/mini-terminal" |
4 |
| -import { Code } from "./code" |
5 |
| -import { |
6 |
| - useBackwardTransitions, |
7 |
| - useForwardTransitions, |
8 |
| -} from "./steps" |
9 |
| -import { Classes } from "@code-hike/classer" |
10 |
| -import { EditorTransition } from "./editor-transition" |
11 |
| -// import "./theme.css" |
| 2 | +import { MiniEditorTween } from "./mini-editor-tween" |
| 3 | +import { EditorStep } from "./use-snapshots" |
| 4 | +import { CodeProps } from "./code" |
| 5 | +import { EditorFrameProps } from "./editor-frame" |
12 | 6 |
|
13 |
| -export { MiniEditorHike } |
| 7 | +export { MiniEditorHike, MiniEditorHikeProps } |
14 | 8 |
|
15 |
| -type MiniEditorStep = { |
16 |
| - code?: string |
17 |
| - focus?: string |
18 |
| - lang?: string |
19 |
| - file?: string |
20 |
| - tabs?: string[] |
21 |
| - terminal?: string |
22 |
| -} |
23 |
| - |
24 |
| -export type MiniEditorHikeProps = { |
25 |
| - progress?: number |
26 |
| - backward?: boolean |
27 |
| - code?: string |
28 |
| - focus?: string |
29 |
| - lang?: string |
30 |
| - file?: string |
31 |
| - tabs?: string[] |
32 |
| - steps?: MiniEditorStep[] |
33 |
| - height?: number |
34 |
| - minColumns?: number |
35 |
| - minZoom?: number |
36 |
| - maxZoom?: number |
37 |
| - button?: React.ReactNode |
38 |
| - horizontalCenter?: boolean |
39 |
| - classes?: Classes |
40 |
| -} & React.PropsWithoutRef<JSX.IntrinsicElements["div"]> |
41 |
| - |
42 |
| -function MiniEditorHike(props: MiniEditorHikeProps) { |
43 |
| - const { |
44 |
| - progress = 0, |
45 |
| - backward = false, |
46 |
| - code, |
47 |
| - focus, |
48 |
| - lang, |
49 |
| - file, |
50 |
| - steps: ogSteps, |
51 |
| - tabs: ogTabs, |
52 |
| - minColumns = 50, |
53 |
| - minZoom = 0.2, |
54 |
| - maxZoom = 1, |
55 |
| - height, |
56 |
| - horizontalCenter = false, |
57 |
| - ...rest |
58 |
| - } = props |
59 |
| - const t = progress % 1 |
60 |
| - const prevStep = ogSteps![0] |
61 |
| - const nextStep = ogSteps![1] || ogSteps![0] |
62 |
| - |
63 |
| - const { steps, files } = useSteps(ogSteps, { |
64 |
| - code, |
65 |
| - focus, |
66 |
| - lang, |
67 |
| - file, |
68 |
| - tabs: ogTabs, |
69 |
| - }) |
70 |
| - |
71 |
| - const prev = { |
72 |
| - files: [{ name: "A.js", code: "a", lang: "js" }], |
73 |
| - northPanel: { |
74 |
| - tabs: ["A.js"], |
75 |
| - active: "A.js", |
76 |
| - heightRatio: 0.5, |
77 |
| - }, |
78 |
| - } |
79 |
| - const next = { |
80 |
| - files: [{ name: "A.js", code: "b", lang: "js" }], |
81 |
| - northPanel: { |
82 |
| - tabs: ["A.js"], |
83 |
| - active: "A.js", |
84 |
| - heightRatio: 0.5, |
85 |
| - }, |
86 |
| - } |
87 |
| - console.log({ t, prev, next }) |
88 |
| - |
89 |
| - return null |
90 |
| - // <EditorTransition |
91 |
| - // t={t} |
92 |
| - // prev={prev} |
93 |
| - // next={next} |
94 |
| - // backward={backward} |
95 |
| - // /> |
96 |
| -} |
97 |
| - |
98 |
| -// function MiniEditorHikeOld(props: MiniEditorHikeProps) { |
99 |
| -// const { |
100 |
| -// progress = 0, |
101 |
| -// backward = false, |
102 |
| -// code, |
103 |
| -// focus, |
104 |
| -// lang, |
105 |
| -// file, |
106 |
| -// steps: ogSteps, |
107 |
| -// tabs: ogTabs, |
108 |
| -// minColumns = 50, |
109 |
| -// minZoom = 0.2, |
110 |
| -// maxZoom = 1, |
111 |
| -// height, |
112 |
| -// horizontalCenter = false, |
113 |
| -// ...rest |
114 |
| -// } = props |
115 |
| -// const { steps, files, stepsByFile } = useSteps(ogSteps, { |
116 |
| -// code, |
117 |
| -// focus, |
118 |
| -// lang, |
119 |
| -// file, |
120 |
| -// tabs: ogTabs, |
121 |
| -// }) |
122 |
| - |
123 |
| -// const activeStepIndex = backward |
124 |
| -// ? Math.floor(progress) |
125 |
| -// : Math.ceil(progress) |
126 |
| -// const activeStep = steps[activeStepIndex] |
127 |
| -// const activeFile = (activeStep && activeStep.file) || "" |
128 |
| - |
129 |
| -// const activeSteps = stepsByFile[activeFile] || [] |
130 |
| - |
131 |
| -// const tabs = activeStep.tabs || files |
132 |
| - |
133 |
| -// const terminalHeight = getTerminalHeight(steps, progress) |
134 |
| - |
135 |
| -// const terminalSteps = steps.map(s => ({ |
136 |
| -// text: (s && s.terminal) || "", |
137 |
| -// })) |
138 |
| - |
139 |
| -// const contentSteps = useStepsWithDefaults( |
140 |
| -// { code, focus, lang, file }, |
141 |
| -// ogSteps || [] |
142 |
| -// ) |
143 |
| - |
144 |
| -// return ( |
145 |
| -// <EditorFrame |
146 |
| -// files={tabs} |
147 |
| -// active={activeFile} |
148 |
| -// terminalPanel={ |
149 |
| -// <TerminalPanel height={terminalHeight}> |
150 |
| -// <InnerTerminal |
151 |
| -// steps={terminalSteps} |
152 |
| -// progress={progress} |
153 |
| -// /> |
154 |
| -// </TerminalPanel> |
155 |
| -// } |
156 |
| -// height={height} |
157 |
| -// {...rest} |
158 |
| -// > |
159 |
| -// {activeSteps.length > 0 && ( |
160 |
| -// <EditorContent |
161 |
| -// key={activeFile} |
162 |
| -// backward={backward} |
163 |
| -// progress={progress} |
164 |
| -// steps={contentSteps} |
165 |
| -// parentHeight={height} |
166 |
| -// minColumns={minColumns} |
167 |
| -// minZoom={minZoom} |
168 |
| -// maxZoom={maxZoom} |
169 |
| -// horizontalCenter={horizontalCenter} |
170 |
| -// /> |
171 |
| -// )} |
172 |
| -// </EditorFrame> |
173 |
| -// ) |
174 |
| -// } |
175 |
| - |
176 |
| -function useStepsWithDefaults( |
177 |
| - defaults: MiniEditorStep, |
178 |
| - steps: MiniEditorStep[] |
179 |
| -): ContentStep[] { |
180 |
| - const files = [ |
181 |
| - ...new Set( |
182 |
| - steps.map(s => coalesce(s.file, defaults.file, "")) |
183 |
| - ), |
184 |
| - ] |
185 |
| - return steps.map(step => { |
186 |
| - return { |
187 |
| - code: coalesce(step.code, defaults.code, ""), |
188 |
| - file: coalesce(step.file, defaults.file, ""), |
189 |
| - focus: coalesce(step.focus, defaults.focus, ""), |
190 |
| - lang: coalesce( |
191 |
| - step.lang, |
192 |
| - defaults.lang, |
193 |
| - "javascript" |
194 |
| - ), |
195 |
| - tabs: coalesce(step.tabs, defaults.tabs, files), |
196 |
| - terminal: step.terminal || defaults.terminal, |
197 |
| - } |
198 |
| - }) |
199 |
| -} |
200 |
| - |
201 |
| -function coalesce<T>( |
202 |
| - a: T | null | undefined, |
203 |
| - b: T | null | undefined, |
204 |
| - c: T |
205 |
| -): T { |
206 |
| - return a != null ? a : b != null ? b : c |
207 |
| -} |
208 |
| - |
209 |
| -type ContentStep = { |
210 |
| - code: string |
211 |
| - focus: string |
212 |
| - lang: string |
213 |
| - file: string |
214 |
| - tabs: string[] |
215 |
| - terminal?: string |
216 |
| -} |
217 |
| - |
218 |
| -type ContentProps = { |
| 9 | +type MiniEditorHikeProps = { |
| 10 | + steps: EditorStep[] |
219 | 11 | progress: number
|
220 | 12 | backward: boolean
|
221 |
| - steps: ContentStep[] |
222 |
| - parentHeight?: number |
223 |
| - minColumns: number |
224 |
| - minZoom: number |
225 |
| - maxZoom: number |
226 |
| - horizontalCenter: boolean |
227 |
| -} |
| 13 | + frameProps: Partial<EditorFrameProps> |
| 14 | + codeProps: Partial<CodeProps> |
| 15 | +} |
| 16 | + |
| 17 | +function MiniEditorHike({ |
| 18 | + steps = [], |
| 19 | + progress = 0, |
| 20 | + backward = false, |
| 21 | + frameProps, |
| 22 | + codeProps, |
| 23 | +}: MiniEditorHikeProps) { |
| 24 | + const prevIndex = clamp( |
| 25 | + Math.floor(progress), |
| 26 | + 0, |
| 27 | + steps.length - 1 |
| 28 | + ) |
| 29 | + const nextIndex = clamp( |
| 30 | + prevIndex + 1, |
| 31 | + 0, |
| 32 | + steps.length - 1 |
| 33 | + ) |
228 | 34 |
|
229 |
| -function EditorContent({ |
230 |
| - progress, |
231 |
| - backward, |
232 |
| - steps, |
233 |
| - parentHeight, |
234 |
| - minColumns, |
235 |
| - minZoom, |
236 |
| - maxZoom, |
237 |
| - horizontalCenter, |
238 |
| -}: ContentProps) { |
239 |
| - const fwdTransitions = useForwardTransitions(steps) |
240 |
| - const bwdTransitions = useBackwardTransitions(steps) |
| 35 | + const prev = steps[prevIndex] |
| 36 | + const next = steps[nextIndex] |
241 | 37 |
|
242 |
| - const transitionIndex = Math.ceil(progress) |
243 |
| - const { |
244 |
| - prevCode, |
245 |
| - nextCode, |
246 |
| - prevFocus, |
247 |
| - nextFocus, |
248 |
| - lang, |
249 |
| - } = backward |
250 |
| - ? bwdTransitions[transitionIndex] |
251 |
| - : fwdTransitions[transitionIndex] |
| 38 | + const t = clamp(progress - prevIndex, 0, steps.length - 1) |
252 | 39 |
|
253 | 40 | return (
|
254 |
| - <Code |
255 |
| - prevCode={prevCode || nextCode!} |
256 |
| - nextCode={nextCode || prevCode!} |
257 |
| - prevFocus={prevFocus} |
258 |
| - nextFocus={nextFocus} |
259 |
| - language={lang} |
260 |
| - progress={progress - transitionIndex + 1} |
261 |
| - parentHeight={parentHeight} |
262 |
| - minColumns={minColumns} |
263 |
| - minZoom={minZoom} |
264 |
| - maxZoom={maxZoom} |
265 |
| - horizontalCenter={horizontalCenter} |
| 41 | + <MiniEditorTween |
| 42 | + frameProps={frameProps} |
| 43 | + codeProps={codeProps} |
| 44 | + prev={prev} |
| 45 | + next={next} |
| 46 | + backward={backward} |
| 47 | + t={t} |
266 | 48 | />
|
267 | 49 | )
|
268 | 50 | }
|
269 | 51 |
|
270 |
| -function useSteps( |
271 |
| - ogSteps: MiniEditorStep[] | undefined, |
272 |
| - { code = "", focus, lang, file, tabs }: MiniEditorStep |
273 |
| -) { |
274 |
| - return React.useMemo(() => { |
275 |
| - const steps = ogSteps?.map(s => ({ |
276 |
| - code, |
277 |
| - focus, |
278 |
| - lang, |
279 |
| - file, |
280 |
| - tabs, |
281 |
| - ...s, |
282 |
| - })) || [{ code, focus, lang, file, tabs }] |
283 |
| - |
284 |
| - const files = [ |
285 |
| - ...new Set( |
286 |
| - steps |
287 |
| - .map((s: any) => s.file) |
288 |
| - .filter((f: any) => f != null) |
289 |
| - ), |
290 |
| - ] |
291 |
| - |
292 |
| - const stepsByFile: Record<string, MiniEditorStep[]> = {} |
293 |
| - steps.forEach(s => { |
294 |
| - if (s.file == null) return |
295 |
| - if (!stepsByFile[s.file]) { |
296 |
| - stepsByFile[s.file] = [] |
297 |
| - } |
298 |
| - stepsByFile[s.file].push(s) |
299 |
| - }) |
300 |
| - |
301 |
| - return { steps, files, stepsByFile } |
302 |
| - }, [ogSteps, code, focus, lang, file, tabs]) |
303 |
| -} |
304 |
| - |
305 |
| -const MAX_HEIGHT = 150 |
306 |
| -function getTerminalHeight(steps: any, progress: number) { |
307 |
| - if (!steps.length) { |
308 |
| - return 0 |
309 |
| - } |
310 |
| - |
311 |
| - const prevIndex = Math.floor(progress) |
312 |
| - const nextIndex = Math.ceil(progress) |
313 |
| - const prevTerminal = |
314 |
| - steps[prevIndex] && steps[prevIndex].terminal |
315 |
| - const nextTerminal = steps[nextIndex].terminal |
316 |
| - |
317 |
| - if (!prevTerminal && !nextTerminal) return 0 |
318 |
| - |
319 |
| - if (!prevTerminal && nextTerminal) |
320 |
| - return MAX_HEIGHT * Math.min((progress % 1) * 4, 1) |
321 |
| - if (prevTerminal && !nextTerminal) |
322 |
| - return MAX_HEIGHT * Math.max(1 - (progress % 1) * 4, 0) |
323 |
| - |
324 |
| - return MAX_HEIGHT |
| 52 | +function clamp(a: number, min: number, max: number) { |
| 53 | + return Math.max(Math.min(a, max), min) |
325 | 54 | }
|
0 commit comments