Skip to content

Commit d682585

Browse files
committed
Add alternate prompt type toggling to the header dropdown
- Implement alternation between previous and current prompt type. Use 'sync' codicon. Place the button to the left of 'prompt type' dropdown. - when the user has never changed prompt type, the alternate button should not be shown
1 parent f67e9b0 commit d682585

8 files changed

Lines changed: 159 additions & 19 deletions

File tree

‎apps/editor/src/views/panel/frontend/Main/MainView/components/Header/Header.tsx‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import { useRef, useCallback } from 'react'
22
import { MODE, Mode } from '@/views/panel/types/main-view-mode'
33
import { use_is_narrow_viewport, use_is_mac } from '@shared/hooks'
44
import { ApiPromptType, WebPromptType } from '@shared/types/prompt-types'
5-
import { InlineDropdown as UiInlineDropdown } from '@ui/components/editor/panel/InlineDropdown'
5+
import { PromptTypeDropdown as UiPromptTypeDropdown } from '@ui/components/editor/panel/PromptTypeDropdown'
66
import { IconButton as UiIconButton } from '@ui/components/editor/common/IconButton'
77
import styles from './Header.module.scss'
88
import {
99
api_prompt_type_labels,
1010
web_prompt_type_labels
1111
} from '../../prompt-type-labels'
1212
import { use_keyboard_shortcuts } from './hooks/use-keyboard-shortcuts'
13+
import { use_alternate_prompt_type } from './hooks/use-alternate-prompt-type'
1314
import { use_translation } from '../../../../i18n/use-translation'
1415

1516
type Props = {
@@ -33,6 +34,14 @@ export const Header: React.FC<Props> = (props) => {
3334
const header_left_ref = useRef<HTMLDivElement>(null)
3435
const dropdown_container_ref = useRef<HTMLDivElement>(null)
3536

37+
const { handle_alternate_click, has_alternate } = use_alternate_prompt_type({
38+
mode: props.mode,
39+
web_prompt_type: props.web_prompt_type,
40+
api_prompt_type: props.api_prompt_type,
41+
on_web_prompt_type_change: props.on_web_prompt_type_change,
42+
on_api_prompt_type_change: props.on_api_prompt_type_change
43+
})
44+
3645
const handle_heading_click = useCallback(() => {
3746
if (props.mode == MODE.WEB) {
3847
props.on_mode_change(MODE.API)
@@ -76,7 +85,7 @@ export const Header: React.FC<Props> = (props) => {
7685
ref={dropdown_container_ref}
7786
>
7887
{props.mode == MODE.WEB && (
79-
<UiInlineDropdown
88+
<UiPromptTypeDropdown
8089
options={Object.entries(web_prompt_type_labels).map(
8190
([value, label]) => ({
8291
value: value as WebPromptType,
@@ -86,6 +95,9 @@ export const Header: React.FC<Props> = (props) => {
8695
)}
8796
selected_value={props.web_prompt_type}
8897
on_change={props.on_web_prompt_type_change}
98+
on_alternate_click={
99+
has_alternate ? handle_alternate_click : undefined
100+
}
89101
menu_max_height={MENU_MAX_HEIGHT}
90102
info={t('header.prompt-type')}
91103
title={
@@ -97,7 +109,7 @@ export const Header: React.FC<Props> = (props) => {
97109
/>
98110
)}
99111
{props.mode == MODE.API && (
100-
<UiInlineDropdown
112+
<UiPromptTypeDropdown
101113
options={Object.entries(api_prompt_type_labels).map(
102114
([value, label]) => ({
103115
value: value as ApiPromptType,
@@ -107,6 +119,9 @@ export const Header: React.FC<Props> = (props) => {
107119
)}
108120
selected_value={props.api_prompt_type}
109121
on_change={props.on_api_prompt_type_change}
122+
on_alternate_click={
123+
has_alternate ? handle_alternate_click : undefined
124+
}
110125
menu_max_height={MENU_MAX_HEIGHT}
111126
info={is_narrow_viewport ? undefined : t('header.prompt-type')}
112127
title={
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { useRef, useCallback } from 'react'
2+
3+
import { MODE, Mode } from '@/views/panel/types/main-view-mode'
4+
import { ApiPromptType, WebPromptType } from '@shared/types/prompt-types'
5+
6+
type UseAlternatePromptTypeParams = {
7+
mode: Mode
8+
web_prompt_type: WebPromptType
9+
api_prompt_type: ApiPromptType
10+
on_web_prompt_type_change: (prompt_type: WebPromptType) => void
11+
on_api_prompt_type_change: (prompt_type: ApiPromptType) => void
12+
}
13+
14+
export const use_alternate_prompt_type = ({
15+
mode,
16+
web_prompt_type,
17+
api_prompt_type,
18+
on_web_prompt_type_change,
19+
on_api_prompt_type_change
20+
}: UseAlternatePromptTypeParams) => {
21+
const prev_web_prompt_type_ref = useRef<WebPromptType>()
22+
const current_web_prompt_type_ref = useRef<WebPromptType>(web_prompt_type)
23+
24+
if (web_prompt_type !== current_web_prompt_type_ref.current) {
25+
prev_web_prompt_type_ref.current = current_web_prompt_type_ref.current
26+
current_web_prompt_type_ref.current = web_prompt_type
27+
}
28+
29+
const prev_api_prompt_type_ref = useRef<ApiPromptType>()
30+
const current_api_prompt_type_ref = useRef<ApiPromptType>(api_prompt_type)
31+
32+
if (api_prompt_type !== current_api_prompt_type_ref.current) {
33+
prev_api_prompt_type_ref.current = current_api_prompt_type_ref.current
34+
current_api_prompt_type_ref.current = api_prompt_type
35+
}
36+
37+
const has_alternate =
38+
mode == MODE.WEB
39+
? !!prev_web_prompt_type_ref.current
40+
: !!prev_api_prompt_type_ref.current
41+
42+
const handle_alternate_click = useCallback(() => {
43+
if (mode == MODE.WEB) {
44+
if (prev_web_prompt_type_ref.current) {
45+
on_web_prompt_type_change(prev_web_prompt_type_ref.current)
46+
}
47+
} else {
48+
if (prev_api_prompt_type_ref.current) {
49+
on_api_prompt_type_change(prev_api_prompt_type_ref.current)
50+
}
51+
}
52+
}, [mode, on_web_prompt_type_change, on_api_prompt_type_change])
53+
54+
return { handle_alternate_click, has_alternate }
55+
}

‎packages/ui/src/components/editor/common/DropdownMenu/DropdownMenu.module.scss‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
display: flex;
3434
justify-content: space-between;
3535
align-items: center;
36-
gap: 12px;
36+
gap: 4px;
3737
padding: 0 6px;
3838
height: 24px;
3939
cursor: pointer;

‎packages/ui/src/components/editor/panel/InlineDropdown/index.ts‎

Lines changed: 0 additions & 2 deletions
This file was deleted.

packages/ui/src/components/editor/panel/InlineDropdown/InlineDropdown.module.scss renamed to packages/ui/src/components/editor/panel/PromptTypeDropdown/PromptTypeDropdown.module.scss

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@
77
.button {
88
display: flex;
99
align-items: center;
10-
gap: 3px;
11-
padding: 0 5px 0 7px;
10+
padding-right: 5px;
1211
height: 24px;
1312
border: 1px solid var(--cwc-border-color-dimmed);
1413
border-radius: 4px;
1514
cursor: pointer;
1615
color: var(--vscode-foreground);
1716
width: 100%;
17+
background: transparent;
18+
user-select: none;
19+
box-sizing: border-box;
1820

1921
&:hover {
2022
border-color: var(--cwc-border-color);
@@ -24,12 +26,38 @@
2426
border-color: var(--cwc-border-color);
2527
}
2628

29+
&__alternate {
30+
display: flex;
31+
align-items: center;
32+
justify-content: center;
33+
width: 24px;
34+
height: 100%;
35+
cursor: pointer;
36+
text-decoration: none;
37+
border-right: 1px solid var(--cwc-border-color);
38+
flex-shrink: 0;
39+
40+
> span {
41+
font-size: 15px !important;
42+
}
43+
44+
&:focus {
45+
outline: none;
46+
}
47+
48+
&:hover {
49+
background-color: var(--vscode-toolbar-hoverBackground);
50+
text-decoration: none;
51+
}
52+
}
53+
2754
&__label {
2855
overflow: hidden;
2956
text-overflow: ellipsis;
3057
white-space: nowrap;
3158
flex-grow: 1;
3259
text-align: left;
60+
padding-left: 7px;
3361
}
3462

3563
&__icon {

packages/ui/src/components/editor/panel/InlineDropdown/InlineDropdown.stories.tsx renamed to packages/ui/src/components/editor/panel/PromptTypeDropdown/PromptTypeDropdown.stories.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { InlineDropdown } from './InlineDropdown'
1+
import { PromptTypeDropdown } from './PromptTypeDropdown'
22
import { useState } from 'react'
33

44
export default {
5-
component: InlineDropdown
5+
component: PromptTypeDropdown
66
}
77

88
export const Default = () => {
@@ -14,7 +14,7 @@ export const Default = () => {
1414

1515
return (
1616
<div style={{ width: '200px' }}>
17-
<InlineDropdown
17+
<PromptTypeDropdown
1818
options={options}
1919
selected_value={selected}
2020
on_change={set_selected}
@@ -32,7 +32,7 @@ export const WithInfo = () => {
3232

3333
return (
3434
<div style={{ width: '200px' }}>
35-
<InlineDropdown
35+
<PromptTypeDropdown
3636
options={options}
3737
selected_value={selected}
3838
on_change={set_selected}
@@ -51,11 +51,30 @@ export const LongList = () => {
5151

5252
return (
5353
<div style={{ width: '200px' }}>
54-
<InlineDropdown
54+
<PromptTypeDropdown
5555
options={options}
5656
selected_value={selected}
5757
on_change={set_selected}
5858
/>
5959
</div>
6060
)
6161
}
62+
63+
export const WithAlternateClick = () => {
64+
const options = [
65+
{ value: 'option1', label: 'Option 1' },
66+
{ value: 'option2', label: 'Option 2' }
67+
]
68+
const [selected, set_selected] = useState<string>('option1')
69+
70+
return (
71+
<div style={{ width: '200px' }}>
72+
<PromptTypeDropdown
73+
options={options}
74+
selected_value={selected}
75+
on_change={set_selected}
76+
on_alternate_click={() => alert('Alternate clicked')}
77+
/>
78+
</div>
79+
)
80+
}

packages/ui/src/components/editor/panel/InlineDropdown/InlineDropdown.tsx renamed to packages/ui/src/components/editor/panel/PromptTypeDropdown/PromptTypeDropdown.tsx

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { useState, useRef, useEffect, useCallback } from 'react'
2-
import styles from './InlineDropdown.module.scss'
2+
import styles from './PromptTypeDropdown.module.scss'
33
import cn from 'classnames'
44
import { DropdownMenu } from '../../common/DropdownMenu'
55
import { use_click_outside } from '../../../../hooks/use-click-outside'
66

7-
export namespace InlineDropdown {
7+
export namespace PromptTypeDropdown {
88
export type Option<T extends string> = {
99
value: T
1010
label: string
@@ -15,6 +15,7 @@ export namespace InlineDropdown {
1515
options: Option<T>[]
1616
selected_value: T
1717
on_change: (value: T) => void
18+
on_alternate_click?: () => void
1819
max_width?: number
1920
menu_max_width?: number | string
2021
menu_max_height?: number | string
@@ -24,8 +25,8 @@ export namespace InlineDropdown {
2425
}
2526
}
2627

27-
export const InlineDropdown = <T extends string>(
28-
props: InlineDropdown.Props<T>
28+
export const PromptTypeDropdown = <T extends string>(
29+
props: PromptTypeDropdown.Props<T>
2930
) => {
3031
const [is_open, set_is_open] = useState(false)
3132
const [just_opened, set_just_opened] = useState(false)
@@ -99,12 +100,35 @@ export const InlineDropdown = <T extends string>(
99100
className={cn(styles.container, { [styles['button--open']]: is_open })}
100101
ref={container_ref}
101102
>
102-
<button
103+
<div
103104
className={cn(styles.button, { [styles['button--open']]: is_open })}
104105
onClick={handle_toggle}
106+
onKeyDown={(e) => {
107+
if (e.key === 'Enter' || e.key === ' ') {
108+
e.preventDefault()
109+
handle_toggle()
110+
}
111+
}}
112+
role="button"
113+
tabIndex={0}
105114
style={{ maxWidth: props.max_width }}
106115
title={props.title}
107116
>
117+
{props.on_alternate_click && (
118+
<button
119+
type="button"
120+
onClick={(e) => {
121+
e.stopPropagation()
122+
if (props.on_alternate_click) {
123+
props.on_alternate_click()
124+
}
125+
}}
126+
className={styles.button__alternate}
127+
title="Toggle previous prompt type"
128+
>
129+
<span className={cn('codicon', 'codicon-sync')} />
130+
</button>
131+
)}
108132
<span className={styles.button__label}>
109133
{selected_option ? selected_option.label : 'Select an option'}
110134
</span>
@@ -121,7 +145,7 @@ export const InlineDropdown = <T extends string>(
121145
)}
122146
/>
123147
)}
124-
</button>
148+
</div>
125149

126150
<DropdownMenu
127151
anchor_ref={container_ref}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './PromptTypeDropdown'

0 commit comments

Comments
 (0)