Skip to content

Commit 536b078

Browse files
committed
Better navigate buttons
1 parent a0ee07b commit 536b078

File tree

3 files changed

+101
-23
lines changed

3 files changed

+101
-23
lines changed

cli/src/components/ask-user/components/progress-indicator.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,48 @@
11
/**
22
* Progress indicator component showing question completion status
33
* Displays: ● = current, ○ = not answered, ✓ = answered
4+
* Dots are clickable to navigate between questions
45
*/
56

67
import React from 'react'
78
import { useTheme } from '../../../hooks/use-theme'
9+
import { Button } from '../../button'
810
import { SYMBOLS } from '../constants'
911

1012
export interface ProgressIndicatorProps {
1113
currentIndex: number
1214
answeredStates: boolean[]
1315
allAnswered: boolean
16+
onNavigate?: (index: number) => void
1417
}
1518

1619
export const ProgressIndicator: React.FC<ProgressIndicatorProps> = ({
1720
currentIndex,
1821
answeredStates,
1922
allAnswered,
23+
onNavigate,
2024
}) => {
2125
const theme = useTheme()
2226

2327
return (
2428
<box style={{ flexDirection: 'row', gap: 1, marginTop: 0 }}>
2529
{answeredStates.map((isAnswered, idx) => {
2630
const isCurrent = idx === currentIndex
31+
const symbol = isAnswered ? SYMBOLS.COMPLETED : isCurrent ? SYMBOLS.CURRENT : SYMBOLS.UNSELECTED
32+
const color = isAnswered
33+
? theme.primary
34+
: isCurrent
35+
? theme.foreground
36+
: theme.muted
37+
2738
return (
28-
<text
39+
<Button
2940
key={idx}
30-
style={{
31-
fg: isAnswered
32-
? theme.primary
33-
: isCurrent
34-
? theme.foreground
35-
: theme.muted,
36-
}}
41+
onClick={() => onNavigate?.(idx)}
42+
style={{ padding: 0 }}
3743
>
38-
{isAnswered ? SYMBOLS.COMPLETED : isCurrent ? SYMBOLS.CURRENT : SYMBOLS.UNSELECTED}
39-
</text>
44+
<text style={{ fg: color }}>{symbol}</text>
45+
</Button>
4046
)
4147
})}
4248
{allAnswered && (

cli/src/components/ask-user/components/question-header.tsx

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
*/
44

55
import React from 'react'
6+
import { TextAttributes } from '@opentui/core'
67
import { useTheme } from '../../../hooks/use-theme'
8+
import { Button } from '../../button'
9+
import { BORDER_CHARS } from '../../../utils/ui-constants'
710
import { ProgressIndicator } from './progress-indicator'
811
import { SkipButton } from './skip-button'
912

@@ -13,6 +16,9 @@ export interface QuestionHeaderProps {
1316
answeredStates: boolean[]
1417
allAnswered: boolean
1518
onSkip?: () => void
19+
onNavigate?: (index: number) => void
20+
onPrev?: () => void
21+
onNext?: () => void
1622
skipButtonFocused?: boolean
1723
skipButtonHovered?: boolean
1824
onSkipMouseOver?: () => void
@@ -26,13 +32,18 @@ export const QuestionHeader: React.FC<QuestionHeaderProps> = ({
2632
answeredStates,
2733
allAnswered,
2834
onSkip,
35+
onNavigate,
36+
onPrev,
37+
onNext,
2938
skipButtonFocused = false,
3039
skipButtonHovered = false,
3140
onSkipMouseOver,
3241
onSkipMouseOut,
3342
hasRoomForInlineButtons,
3443
}) => {
3544
const theme = useTheme()
45+
const isFirstQuestion = currentIndex === 0
46+
const isLastQuestion = currentIndex === totalQuestions - 1
3647

3748
return (
3849
<box
@@ -49,29 +60,72 @@ export const QuestionHeader: React.FC<QuestionHeaderProps> = ({
4960
<text style={{ fg: theme.secondary }}>
5061
Question {currentIndex + 1} of {totalQuestions}
5162
</text>
52-
{totalQuestions > 1 && (
53-
<text style={{ fg: theme.muted }}> (← → to navigate)</text>
54-
)}
5563
</box>
5664
{totalQuestions > 1 && (
5765
<ProgressIndicator
5866
currentIndex={currentIndex}
5967
answeredStates={answeredStates}
6068
allAnswered={allAnswered}
69+
onNavigate={onNavigate}
6170
/>
6271
)}
6372
</box>
6473

65-
{/* Right side: Skip button (if room) */}
66-
{hasRoomForInlineButtons && onSkip && (
67-
<box style={{ flexDirection: 'row', gap: 2 }}>
68-
<SkipButton
69-
onClick={onSkip}
70-
isFocused={skipButtonFocused}
71-
isHovered={skipButtonHovered}
72-
onMouseOver={onSkipMouseOver}
73-
onMouseOut={onSkipMouseOut}
74-
/>
74+
{/* Right side: Navigation and Skip buttons (if room) */}
75+
{hasRoomForInlineButtons && (
76+
<box style={{ flexDirection: 'row', gap: 1 }}>
77+
{/* Navigation buttons (only show for multi-question forms) */}
78+
{totalQuestions > 1 && (
79+
<>
80+
<Button
81+
onClick={onPrev}
82+
style={{
83+
borderStyle: 'single',
84+
borderColor: isFirstQuestion ? theme.muted : theme.secondary,
85+
customBorderChars: BORDER_CHARS,
86+
paddingLeft: 1,
87+
paddingRight: 1,
88+
}}
89+
>
90+
<text
91+
style={{
92+
fg: isFirstQuestion ? theme.muted : theme.foreground,
93+
attributes: isFirstQuestion ? undefined : TextAttributes.BOLD,
94+
}}
95+
>
96+
97+
</text>
98+
</Button>
99+
<Button
100+
onClick={onNext}
101+
style={{
102+
borderStyle: 'single',
103+
borderColor: isLastQuestion ? theme.muted : theme.secondary,
104+
customBorderChars: BORDER_CHARS,
105+
paddingLeft: 1,
106+
paddingRight: 1,
107+
}}
108+
>
109+
<text
110+
style={{
111+
fg: isLastQuestion ? theme.muted : theme.foreground,
112+
attributes: isLastQuestion ? undefined : TextAttributes.BOLD,
113+
}}
114+
>
115+
116+
</text>
117+
</Button>
118+
</>
119+
)}
120+
{onSkip && (
121+
<SkipButton
122+
onClick={onSkip}
123+
isFocused={skipButtonFocused}
124+
isHovered={skipButtonHovered}
125+
onMouseOver={onSkipMouseOver}
126+
onMouseOut={onSkipMouseOut}
127+
/>
128+
)}
75129
</box>
76130
)}
77131
</box>

cli/src/components/ask-user/index.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,24 @@ export const MultipleChoiceForm: React.FC<MultipleChoiceFormProps> = ({
127127
answeredStates={answeredStates}
128128
allAnswered={allAnswered}
129129
onSkip={onSkip}
130+
onNavigate={(newIndex) => {
131+
setCurrentQuestionIndex(newIndex)
132+
focusActions.resetToQuestion(newIndex)
133+
}}
134+
onPrev={() => {
135+
if (!isFirstQuestion) {
136+
const newIndex = currentQuestionIndex - 1
137+
setCurrentQuestionIndex(newIndex)
138+
focusActions.resetToQuestion(newIndex)
139+
}
140+
}}
141+
onNext={() => {
142+
if (!isLastQuestion) {
143+
const newIndex = currentQuestionIndex + 1
144+
setCurrentQuestionIndex(newIndex)
145+
focusActions.resetToQuestion(newIndex)
146+
}
147+
}}
130148
skipButtonFocused={isSkipFocused}
131149
skipButtonHovered={isSkipHovered}
132150
onSkipMouseOver={() => {

0 commit comments

Comments
 (0)