Skip to content

Commit a80f7a2

Browse files
authored
Merge pull request #86 from code-hike/clickable-tabs
Make tabs clickable
2 parents f8d6535 + f0a55af commit a80f7a2

File tree

9 files changed

+164
-66
lines changed

9 files changed

+164
-66
lines changed

packages/mdx/src/client/code.tsx

+51-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,26 @@ import { CodeSpring } from "@code-hike/smooth-code"
33
import {
44
EditorSpring,
55
EditorProps,
6+
EditorStep,
67
} from "@code-hike/mini-editor"
78

89
export function Code(props: EditorProps) {
10+
const [step, setStep] = React.useState(props)
11+
12+
function onTabClick(filename: string) {
13+
const newStep = updateEditorStep(step, filename, null)
14+
setStep({ ...step, ...newStep })
15+
}
16+
17+
return <InnerCode {...step} onTabClick={onTabClick} />
18+
}
19+
20+
export function InnerCode({
21+
onTabClick,
22+
...props
23+
}: EditorProps & {
24+
onTabClick?: (filename: string) => void
25+
}) {
926
if (
1027
!props.southPanel &&
1128
props.files.length === 1 &&
@@ -19,6 +36,39 @@ export function Code(props: EditorProps) {
1936
/>
2037
)
2138
} else {
22-
return <EditorSpring {...props} />
39+
const frameProps = {
40+
...props?.frameProps,
41+
onTabClick,
42+
}
43+
return (
44+
<EditorSpring {...props} frameProps={frameProps} />
45+
)
46+
}
47+
}
48+
49+
export function updateEditorStep(
50+
step: EditorStep,
51+
filename: string | undefined,
52+
focus: string | null
53+
): EditorStep {
54+
const name = filename || step.northPanel.active
55+
const newFiles = step.files.map((file: any) =>
56+
file.name === name
57+
? {
58+
...file,
59+
focus: focus === null ? file.focus : focus,
60+
}
61+
: file
62+
)
63+
64+
let northPanel = { ...step.northPanel }
65+
let southPanel = step.southPanel && {
66+
...step.southPanel,
67+
}
68+
if (step.northPanel.tabs.includes(name)) {
69+
northPanel.active = name
70+
} else if (southPanel) {
71+
southPanel.active = name
2372
}
73+
return { files: newFiles, northPanel, southPanel }
2474
}

packages/mdx/src/client/scrollycoding.tsx

+25-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
EditorProps,
44
EditorStep,
55
} from "@code-hike/mini-editor"
6-
import { Code } from "./code"
6+
import { InnerCode, updateEditorStep } from "./code"
77
import {
88
Scroller,
99
Step as ScrollerStep,
@@ -25,12 +25,26 @@ export function Scrollycoding({
2525
}) {
2626
const stepsChildren = React.Children.toArray(children)
2727

28-
const [stepIndex, setIndex] = React.useState(start)
29-
const tab = editorSteps[stepIndex]
28+
const [state, setState] = React.useState({
29+
stepIndex: start,
30+
step: editorSteps[start],
31+
})
32+
33+
const tab = state.step
3034

3135
function onStepChange(index: number) {
32-
setIndex(index)
36+
setState({ stepIndex: index, step: editorSteps[index] })
37+
}
38+
39+
function onTabClick(filename: string) {
40+
const newStep = updateEditorStep(
41+
state.step,
42+
filename,
43+
null
44+
)
45+
setState({ ...state, step: newStep })
3346
}
47+
3448
return (
3549
<section
3650
className={`ch-scrollycoding ${
@@ -44,10 +58,10 @@ export function Scrollycoding({
4458
as="div"
4559
key={i}
4660
index={i}
47-
onClick={() => setIndex(i)}
61+
onClick={() => onStepChange(i)}
4862
className="ch-scrollycoding-step-content"
4963
data-selected={
50-
i === stepIndex ? "true" : undefined
64+
i === state.stepIndex ? "true" : undefined
5165
}
5266
>
5367
{children}
@@ -56,7 +70,11 @@ export function Scrollycoding({
5670
</Scroller>
5771
</div>
5872
<div className="ch-scrollycoding-sticker">
59-
<Code {...(tab as any)} codeConfig={codeConfig} />
73+
<InnerCode
74+
{...(tab as any)}
75+
codeConfig={codeConfig}
76+
onTabClick={onTabClick}
77+
/>
6078
{presetConfig && (
6179
<Preview
6280
className="ch-scrollycoding-preview"

packages/mdx/src/client/section.tsx

+14-23
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React from "react"
22
import { EditorProps } from "@code-hike/mini-editor"
3-
import { Code } from "./code"
3+
import { InnerCode, updateEditorStep } from "./code"
44

55
const SectionContext = React.createContext<{
66
props: EditorProps
77
selectedId?: string
88
setFocus: (x: {
99
fileName?: string
10-
focus: string
10+
focus: string | null
1111
id: string
1212
}) => void
1313
resetFocus: () => void
@@ -33,30 +33,14 @@ export function Section({
3333
id,
3434
}: {
3535
fileName?: string
36-
focus: string
36+
focus: string | null
3737
id: string
3838
}) => {
39-
const name = fileName || state.northPanel.active
40-
41-
const newFiles = state.files.map((file: any) =>
42-
file.name === name ? { ...file, focus } : file
43-
)
44-
45-
let northPanel = { ...state.northPanel }
46-
let southPanel = state.southPanel && {
47-
...state.northPanel,
48-
}
49-
if (state.northPanel.tabs.includes(name)) {
50-
northPanel.active = name
51-
} else if (southPanel) {
52-
southPanel.active = name
53-
}
39+
const newStep = updateEditorStep(state, fileName, focus)
5440

5541
setState({
5642
...state,
57-
files: newFiles,
58-
northPanel,
59-
southPanel,
43+
...newStep,
6044
selectedId: id,
6145
})
6246
}
@@ -78,8 +62,15 @@ export function Section({
7862
}
7963

8064
export function SectionCode() {
81-
const { props } = React.useContext(SectionContext)
82-
return <Code {...props} />
65+
const { props, setFocus } = React.useContext(
66+
SectionContext
67+
)
68+
69+
const onTabClick = (filename: string) => {
70+
setFocus({ fileName: filename, focus: null, id: "" })
71+
}
72+
73+
return <InnerCode {...props} onTabClick={onTabClick} />
8374
}
8475

8576
export function SectionLink({

packages/mdx/src/client/spotlight.tsx

+27-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
EditorProps,
44
EditorStep,
55
} from "@code-hike/mini-editor"
6-
import { Code } from "./code"
6+
import { InnerCode, updateEditorStep } from "./code"
77
import { Preview, PresetConfig } from "./preview"
88

99
export function Spotlight({
@@ -21,8 +21,20 @@ export function Spotlight({
2121
}) {
2222
const stepsChildren = React.Children.toArray(children)
2323

24-
const [stepIndex, setIndex] = React.useState(start)
25-
const tab = editorSteps[stepIndex]
24+
const [state, setState] = React.useState({
25+
stepIndex: start,
26+
step: editorSteps[start],
27+
})
28+
const tab = state.step
29+
30+
function onTabClick(filename: string) {
31+
const newStep = updateEditorStep(
32+
state.step,
33+
filename,
34+
null
35+
)
36+
setState({ ...state, step: newStep })
37+
}
2638

2739
const headerElement = stepsChildren[0] as React.ReactElement
2840

@@ -40,10 +52,15 @@ export function Spotlight({
4052
i === 0 ? null : (
4153
<div
4254
key={i}
43-
onClick={() => setIndex(i)}
55+
onClick={() =>
56+
setState({
57+
stepIndex: i,
58+
step: editorSteps[i],
59+
})
60+
}
4461
className="ch-spotlight-tab"
4562
data-selected={
46-
i === stepIndex ? "true" : undefined
63+
i === state.stepIndex ? "true" : undefined
4764
}
4865
>
4966
{children}
@@ -52,7 +69,11 @@ export function Spotlight({
5269
)}
5370
</div>
5471
<div className="ch-spotlight-sticker">
55-
<Code {...(tab as any)} codeConfig={codeConfig} />
72+
<InnerCode
73+
{...(tab as any)}
74+
codeConfig={codeConfig}
75+
onTabClick={onTabClick}
76+
/>
5677
{presetConfig && (
5778
<Preview
5879
className="ch-spotlight-preview"

packages/mini-editor/src/editor-frame.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type EditorFrameProps = {
3636
height?: number
3737
button?: React.ReactNode
3838
classes?: Classes
39+
onTabClick?: (filename: string) => void
3940
} & React.PropsWithoutRef<JSX.IntrinsicElements["div"]>
4041

4142
export const EditorFrame = React.forwardRef<
@@ -51,6 +52,7 @@ export const EditorFrame = React.forwardRef<
5152
button,
5253
theme,
5354
className,
55+
onTabClick,
5456
...rest
5557
},
5658
ref
@@ -76,6 +78,7 @@ export const EditorFrame = React.forwardRef<
7678
button={button}
7779
panel="north"
7880
theme={theme}
81+
onTabClick={onTabClick}
7982
/>
8083
}
8184
{...rest}
@@ -105,6 +108,7 @@ export const EditorFrame = React.forwardRef<
105108
topBorder={true}
106109
panel="south"
107110
theme={theme}
111+
onTabClick={onTabClick}
108112
/>
109113
</div>
110114
<div
@@ -130,6 +134,7 @@ type TabsContainerProps = {
130134
topBorder?: boolean
131135
panel: "north" | "south"
132136
theme: EditorTheme
137+
onTabClick?: (filename: string) => void
133138
}
134139
function TabsContainer({
135140
tabs,
@@ -138,6 +143,7 @@ function TabsContainer({
138143
topBorder,
139144
panel,
140145
theme,
146+
onTabClick,
141147
}: TabsContainerProps) {
142148
const c = useClasser("ch-editor-tab")
143149
return (
@@ -189,6 +195,7 @@ function TabsContainer({
189195
: ColorName.InactiveTabBackground
190196
),
191197
}}
198+
onClick={onTabClick && (() => onTabClick(title))}
192199
>
193200
<div>{title}</div>
194201
</div>

packages/playground/content/scrollycoding-demo.mdx

+10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Lorem ipsum dolor sit amet, consectetur adipiscing something about points, sed d
1616
1717
Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus.
1818

19+
<CH.Code>
20+
1921
```js app.js focus=3:10
2022
const { lorem, ipsum } = dolor({
2123
sit: {
@@ -53,6 +55,14 @@ const { lorem, ipsum } = dolor({
5355
})
5456
```
5557

58+
```css styles.css
59+
.lorem {
60+
color: red;
61+
}
62+
```
63+
64+
</CH.Code>
65+
5666
---
5767

5868
## Step 2

0 commit comments

Comments
 (0)